(Big tangle of changes that all happened as I was looking into the ROM

loader rewrite, which is still in progress....)

Replaced mamedriv.c with a new driver list mechanism that is generated
by the build tools. The emulator core now expects the presence of a
file called src/$(TARGET)/$(SUBTARGET).lst which is just a raw list of
driver names, one per line. C and C++ comments are still permitted.
This file is parsed by a new build tool makelist which extracts the
driver names, sorts them, and generates a file called drivlist.c, which
is consumed by the core. [Aaron Giles]

Added new osdcore function osd_malloc_array() which is identical to
osd_malloc() but obviously hints that the underlying allocation is for
an array. Updated all callers to use the appropriate form. Modified the
Windows allocator to only use guard pages for array-style allocations,
allowing us to enable them once again in debug builds. [Aaron Giles]

Created new static class driver_list to wrap accesses to the list of
available drivers. Improved speed of driver lookups by relying on the
presorting done by makelist. [Aaron Giles]

Created helper class driver_enumerator as a helper for iterating through 
the list of drivers. This class supports basic filtering and iteration,
and also serves as a temporary cache of machine_configs. [Aaron Giles]

Created cli_frontend object to wrap all the CLI handling code in
clifront.c. Updated/simplified all the code to take advantage of the
driver_enumerator. [Aaron Giles]

Created media_auditor object to wrap all the auditing functions in
audit.c. Updated all users to the new interface. Note that the new
auditing mechanism is slightly out of sync with the romload code in
terms of finding ROMs owned by devices, so it may mis-report some
issues until the new ROM loading code is in. [Aaron Giles]

Added concept of a per-device searchpath. For most devices, their 
searchpath is just the short name of the device. For driver_devices, the
searchpath is driver[;parent[;bios]]. This searchpath will eventually be
used by the rom loader to find ROMs. For now it is used by the media
auditor only. [Aaron Giles]

Created info_xml_creator object to wrap all the info generation functions
in info.c. Converted the file to C++ and cleaned up the input processing
code. [Aaron Giles]

(not for whatsnew ... Known issues: auditing of CHDs appears busted, and 
debug builds report unfreed memory if you use the built-in game picker)
This commit is contained in:
Aaron Giles 2011-04-13 20:31:00 +00:00
parent f99e1a5da6
commit 00d745ca77
67 changed files with 15978 additions and 16007 deletions

8
.gitattributes vendored
View File

@ -10,6 +10,7 @@ hash/softwarelist.dtd svneol=native#text/plain
src/build/build.mak svneol=native#text/plain
src/build/file2str.c svneol=native#text/plain
src/build/makedep.c svneol=native#text/plain
src/build/makelist.c svneol=native#text/plain
src/build/png2bdc.c svneol=native#text/plain
src/build/verinfo.c svneol=native#text/plain
src/emu/addrmap.c svneol=native#text/plain
@ -708,6 +709,7 @@ src/emu/layout/hoffff20.lay svneol=native#text/plain
src/emu/layout/horizont.lay svneol=native#text/plain
src/emu/layout/lcd.lay svneol=native#text/plain
src/emu/layout/lcd_rot.lay svneol=native#text/plain
src/emu/layout/pinball.lay svneol=native#text/plain
src/emu/layout/snap.lay svneol=native#text/plain
src/emu/layout/triphsxs.lay svneol=native#text/plain
src/emu/layout/vertical.lay svneol=native#text/plain
@ -3441,7 +3443,6 @@ src/mame/layout/pe_poker.lay svneol=native#text/plain
src/mame/layout/pe_schip.lay svneol=native#text/plain
src/mame/layout/pe_slots.lay svneol=native#text/plain
src/mame/layout/peplus.lay svneol=native#text/plain
src/mame/layout/pinball.lay svneol=native#text/plain
src/mame/layout/pirpok2.lay svneol=native#text/plain
src/mame/layout/pmpoker.lay svneol=native#text/plain
src/mame/layout/pmroulet.lay -text svneol=native#plain/text
@ -3721,9 +3722,9 @@ src/mame/machine/znsec.c svneol=native#text/plain
src/mame/machine/znsec.h svneol=native#text/plain
src/mame/machine/zs01.c svneol=native#text/plain
src/mame/machine/zs01.h svneol=native#text/plain
src/mame/mame.lst svneol=native#text/plain
src/mame/mame.mak svneol=native#text/plain
src/mame/mamedriv.c svneol=native#text/plain
src/mame/tiny.c svneol=native#text/plain
src/mame/tiny.lst svneol=native#text/plain
src/mame/tiny.mak svneol=native#text/plain
src/mame/video/1942.c svneol=native#text/plain
src/mame/video/1943.c svneol=native#text/plain
@ -4607,7 +4608,6 @@ src/osd/windows/strconv.h svneol=native#text/plain
src/osd/windows/vconv.c svneol=native#text/plain
src/osd/windows/video.c svneol=native#text/plain
src/osd/windows/video.h svneol=native#text/plain
src/osd/windows/winalloc.c svneol=native#text/plain
src/osd/windows/winclip.c svneol=native#text/plain
src/osd/windows/windir.c svneol=native#text/plain
src/osd/windows/window.c svneol=native#text/plain

View File

@ -577,6 +577,8 @@ LIBOCORE = $(OBJ)/libocore.a
LIBOSD = $(OBJ)/libosd.a
VERSIONOBJ = $(OBJ)/version.o
DRIVLISTSRC = $(OBJ)/drivlist.c
DRIVLISTOBJ = $(OBJ)/drivlist.o
@ -722,7 +724,7 @@ ifndef EXECUTABLE_DEFINED
# always recompile the version string
$(VERSIONOBJ): $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(ZLIB) $(SOFTFLOAT) $(LIBOCORE) $(RESFILE)
$(EMULATOR): $(VERSIONOBJ) $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBDASM) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(SOFTFLOAT) $(ZLIB) $(LIBOCORE) $(RESFILE)
$(EMULATOR): $(VERSIONOBJ) $(DRIVLISTOBJ) $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBDASM) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(SOFTFLOAT) $(ZLIB) $(LIBOCORE) $(RESFILE)
@echo Linking $@...
$(LD) $(LDFLAGS) $(LDFLAGSEMULATOR) $^ $(LIBS) -o $@
ifeq ($(TARGETOS),win32)
@ -760,6 +762,14 @@ $(OBJ)/%.fh: $(SRC)/%.png $(PNG2BDC_TARGET) $(FILE2STR_TARGET)
@$(PNG2BDC) $< $(OBJ)/temp.bdc
@$(FILE2STR) $(OBJ)/temp.bdc $@ font_$(basename $(notdir $<)) UINT8
$(DRIVLISTOBJ): $(DRIVLISTSRC)
@echo Compiling $<...
$(CC) $(CDEFS) $(CFLAGS) -c $< -o $@
$(DRIVLISTSRC): $(SRC)/$(TARGET)/$(SUBTARGET).lst $(MAKELIST_TARGET)
@echo Building driver list $<...
@$(MAKELIST) $< >$@
$(OBJ)/%.a:
@echo Archiving $@...
$(RM) $@

View File

@ -20,17 +20,20 @@ OBJDIRS += \
FILE2STR_TARGET = $(BUILDOUT)/file2str$(BUILD_EXE)
MAKEDEP_TARGET = $(BUILDOUT)/makedep$(BUILD_EXE)
MAKELIST_TARGET = $(BUILDOUT)/makelist$(BUILD_EXE)
PNG2BDC_TARGET = $(BUILDOUT)/png2bdc$(BUILD_EXE)
VERINFO_TARGET = $(BUILDOUT)/verinfo$(BUILD_EXE)
ifeq ($(TARGETOS),win32)
FILE2STR = $(subst /,\,$(FILE2STR_TARGET))
MAKEDEP = $(subst /,\,$(MAKEDEP_TARGET))
MAKELIST = $(subst /,\,$(MAKELIST_TARGET))
PNG2BDC = $(subst /,\,$(PNG2BDC_TARGET))
VERINFO = $(subst /,\,$(VERINFO_TARGET))
else
FILE2STR = $(FILE2STR_TARGET)
MAKEDEP = $(MAKEDEP_TARGET)
MAKELIST = $(MAKELIST_TARGET)
PNG2BDC = $(PNG2BDC_TARGET)
VERINFO = $(VERINFO_TARGET)
endif
@ -39,6 +42,7 @@ ifneq ($(CROSS_BUILD),1)
BUILD += \
$(FILE2STR_TARGET) \
$(MAKEDEP_TARGET) \
$(MAKELIST_TARGET) \
$(PNG2BDC_TARGET) \
$(VERINFO_TARGET) \
@ -70,6 +74,19 @@ $(MAKEDEP_TARGET): $(MAKEDEPOBJS) $(LIBUTIL) $(LIBOCORE) $(ZLIB)
#-------------------------------------------------
# makelist
#-------------------------------------------------
MAKELISTOBJS = \
$(BUILDOBJ)/makelist.o \
$(MAKELIST_TARGET): $(MAKELISTOBJS) $(LIBUTIL) $(LIBOCORE) $(ZLIB)
@echo Linking $@...
$(LD) $(LDFLAGS) $^ $(LIBS) -o $@
#-------------------------------------------------
# png2bdc
#-------------------------------------------------

203
src/build/makelist.c Normal file
View File

@ -0,0 +1,203 @@
/***************************************************************************
makelist.c
Create and sort the driver list.
****************************************************************************
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.
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "corefile.h"
#define MAX_DRIVERS 65536
static const char *drivlist[MAX_DRIVERS];
//-------------------------------------------------
// driver_sort_callback - compare two items in
// a string array
//-------------------------------------------------
int sort_callback(const void *elem1, const void *elem2)
{
const char **item1 = (const char **)elem1;
const char **item2 = (const char **)elem2;
return strcmp(*item1, *item2);
}
//-------------------------------------------------
// main - primary entry point
//-------------------------------------------------
int main(int argc, char *argv[])
{
// needs at least 1 argument
if (argc < 2)
{
fprintf(stderr,
"Usage:\n"
" makelist <source.lst>\n"
);
return 0;
}
// extract arguments
const char *srcfile = argv[1];
// read source file
void *buffer;
UINT32 length;
file_error filerr = core_fload(srcfile, &buffer, &length);
if (filerr != FILERR_NONE)
{
fprintf(stderr, "Unable to read source file '%s'\n", srcfile);
return 1;
}
// rip through it to find all drivers
int drivcount = 0;
char *srcptr = (char *)buffer;
char *endptr = srcptr + length;
int linenum = 1;
bool in_comment = false;
while (srcptr < endptr)
{
char c = *srcptr++;
// count newlines
if (c == 13 || c == 10)
{
if (c == 13 && *srcptr == 10)
srcptr++;
linenum++;
continue;
}
// skip any spaces
if (isspace(c))
continue;
// look for end of C comment
if (in_comment && c == '*' && *srcptr == '/')
{
srcptr++;
in_comment = false;
continue;
}
// skip anything else inside a C comment
if (in_comment)
continue;
// look for start of C comment
if (c == '/' && *srcptr == '*')
{
srcptr++;
in_comment = true;
continue;
}
// if we hit a C++ comment, scan to the end of line
if (c == '/' && *srcptr == '/')
{
while (srcptr < endptr && *srcptr != 13 && *srcptr != 10)
srcptr++;
continue;
}
// extract the driver name
char drivname[32];
drivname[0] = 0;
srcptr--;
for (int pos = 0; srcptr < endptr && pos < ARRAY_LENGTH(drivname) - 1 && !isspace(*srcptr); pos++)
{
drivname[pos] = *srcptr++;
drivname[pos+1] = 0;
}
// verify the name as valid
for (char *drivch = drivname; *drivch != 0; drivch++)
{
if ((*drivch >= 'a' && *drivch <= 'z') || (*drivch >= '0' && *drivch <= '9') || *drivch == '_')
continue;
fprintf(stderr, "%s:%d - Invalid character '%c' in driver \"%s\"\n", srcfile, linenum, *drivch, drivname);
return 1;
}
// add it to the list
char *name = (char *)malloc(strlen(drivname) + 1);
strcpy(name, drivname);
drivlist[drivcount++] = name;
}
// add a reference to the ___empty driver
drivlist[drivcount++] = "___empty";
// output a count
if (drivcount == 0)
{
fprintf(stderr, "No drivers found\n");
return 1;
}
fprintf(stderr, "%d drivers found\n", drivcount);
// sort the list
qsort(drivlist, drivcount, sizeof(*drivlist), sort_callback);
// start with a header
printf("#include \"emu.h\"\n\n");
// output the list of externs first
for (int index = 0; index < drivcount; index++)
printf("GAME_EXTERN(%s);\n", drivlist[index]);
printf("\n");
// then output the array
printf("const game_driver * const driver_list::s_drivers_sorted[%d] =\n", drivcount);
printf("{\n");
for (int index = 0; index < drivcount; index++)
printf("\t&GAME_NAME(%s)%s\n", drivlist[index], (index == drivcount - 1) ? "" : ",");
printf("};\n");
printf("\n");
// also output a global count
printf("int driver_list::s_driver_count = %d;\n", drivcount);
return 0;
}

View File

@ -4,8 +4,36 @@
ROM set auditing functions.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
****************************************************************************
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.
***************************************************************************/
@ -17,510 +45,419 @@
#include "sound/samples.h"
//**************************************************************************
// CORE FUNCTIONS
//**************************************************************************
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
//-------------------------------------------------
// media_auditor - constructor
//-------------------------------------------------
static void audit_one_rom(emu_options &options, const rom_entry *rom, const char *regiontag, const game_driver *gamedrv, const char *validation, audit_record *record);
static void audit_one_disk(emu_options &options, const rom_entry *rom, const game_driver *gamedrv, const char *validation, audit_record *record);
static int rom_used_by_parent(emu_options &options, const game_driver *gamedrv, const hash_collection &romhashes, const game_driver **parent);
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
set_status - shortcut for setting status and
substatus values
-------------------------------------------------*/
INLINE void set_status(audit_record *record, UINT8 status, UINT8 substatus)
media_auditor::media_auditor(const driver_enumerator &enumerator)
: m_enumerator(enumerator),
m_validation(AUDIT_VALIDATE_FULL),
m_searchpath(NULL)
{
record->status = status;
record->substatus = substatus;
}
//-------------------------------------------------
// audit_media - audit the media described by the
// currently-enumerated driver
//-------------------------------------------------
/***************************************************************************
CORE FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
audit_images - validate the ROM and disk
images for a game
-------------------------------------------------*/
int audit_images(emu_options &options, const game_driver *gamedrv, const char *validation, audit_record **audit)
media_auditor::summary media_auditor::audit_media(const char *validation)
{
machine_config config(*gamedrv, options);
const rom_entry *region, *rom;
const rom_source *source;
audit_record *record;
int anyfound = FALSE;
int anyrequired = FALSE;
int allshared = TRUE;
int records;
// start fresh
m_record_list.reset();
/* determine the number of records we will generate */
records = 0;
bool source_is_gamedrv = true;
for (source = rom_first_source(config); source != NULL; source = rom_next_source(*source))
// store validation for later
m_validation = validation;
// iterate over ROM sources and regions
bool anyfound = false;
bool anyrequired = false;
for (const rom_source *source = rom_first_source(m_enumerator.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))
if (ROMREGION_ISROMDATA(region) || ROMREGION_ISDISKDATA(region))
{
if (source_is_gamedrv && !ROM_ISOPTIONAL(rom))
{
hash_collection hashes(ROM_GETHASHDATA(rom));
if (!hashes.flag(hash_collection::FLAG_NO_DUMP))
{
anyrequired = TRUE;
if (allshared && !rom_used_by_parent(options, gamedrv, hashes, NULL))
allshared = FALSE;
}
}
records++;
}
// determine the search path for this source and iterate through the regions
m_searchpath = source->searchpath();
// also determine if this is the driver's specific ROMs or not
bool source_is_gamedrv = (dynamic_cast<const driver_device_config_base *>(source) != NULL);
// now iterate over regions and ROMs within
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; rom = rom_next_file(rom))
{
hash_collection hashes(ROM_GETHASHDATA(rom));
source_is_gamedrv = false;
// if a dump exists, then at least one entry is required
if (!hashes.flag(hash_collection::FLAG_NO_DUMP))
anyrequired = true;
// audit a file
audit_record *record = NULL;
if (ROMREGION_ISROMDATA(region))
record = audit_one_rom(rom);
// audit a disk
else if (ROMREGION_ISDISKDATA(region))
record = audit_one_disk(rom);
// skip if no record
if (record == NULL)
continue;
// if we got a record back,
if (record->status() != audit_record::STATUS_NOT_FOUND && source_is_gamedrv && also_used_by_parent(hashes) == -1)
anyfound = true;
}
}
if (records > 0)
{
/* allocate memory for the records */
*audit = global_alloc_array_clear(audit_record, records);
record = *audit;
// if we found nothing, we don't have the set at all
if (!anyfound && anyrequired)
m_record_list.reset();
// return a summary
return summarize();
}
/* iterate over ROM sources and regions */
bool source_is_gamedrv = true;
for (source = rom_first_source(config); source != NULL; source = rom_next_source(*source))
//-------------------------------------------------
// audit_samples - validate the samples for the
// currently-enumerated driver
//-------------------------------------------------
media_auditor::summary media_auditor::audit_samples()
{
// start fresh
m_record_list.reset();
// iterate over sample entries
for (const device_config *devconfig = m_enumerator.config().first_device(); devconfig != NULL; devconfig = devconfig->next())
if (devconfig->type() == SAMPLES)
{
for (region = rom_first_region(*source); region != NULL; region = rom_next_region(region))
const samples_interface *intf = reinterpret_cast<const samples_interface *>(devconfig->static_config());
if (intf->samplenames != NULL)
{
const char *regiontag = ROMREGION_ISLOADBYNAME(region) ? ROM_GETNAME(region) : NULL;
for (rom = rom_first_file(region); rom; rom = rom_next_file(rom))
// by default we just search using the driver name
astring searchpath(m_enumerator.driver().name);
// iterate over samples in this entry
for (int sampnum = 0; intf->samplenames[sampnum] != NULL; sampnum++)
{
/* audit a file */
if (ROMREGION_ISROMDATA(region))
{
audit_one_rom(options, rom, regiontag, gamedrv, validation, record);
}
/* audit a disk */
else if (ROMREGION_ISDISKDATA(region))
{
audit_one_disk(options, rom, gamedrv, validation, record);
}
else
// starred entries indicate an additional searchpath
if (intf->samplenames[sampnum][0] == '*')
{
searchpath.cat(";").cat(&intf->samplenames[sampnum][1]);
continue;
}
if (source_is_gamedrv && record->status != AUDIT_STATUS_NOT_FOUND && (allshared || !rom_used_by_parent(options, gamedrv, record->exphashes, NULL)))
anyfound = TRUE;
record++;
// create a new record
audit_record &record = m_record_list.append(*global_alloc(audit_record(intf->samplenames[sampnum], audit_record::MEDIA_SAMPLE)));
// look for the files
emu_file file(m_enumerator.options().sample_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD);
path_iterator path(searchpath);
astring curpath;
while (path.next(curpath, intf->samplenames[sampnum]))
{
// attempt to access the file
file_error filerr = file.open(curpath);
if (filerr == FILERR_NONE)
record.set_status(audit_record::STATUS_GOOD, audit_record::SUBSTATUS_GOOD);
else
record.set_status(audit_record::STATUS_NOT_FOUND, audit_record::SUBSTATUS_NOT_FOUND);
}
}
}
source_is_gamedrv = false;
}
}
/* if we found nothing, we don't have the set at all */
if (!anyfound && anyrequired)
{
global_free(*audit);
*audit = NULL;
records = 0;
}
return records;
// return a summary
return summarize();
}
/*-------------------------------------------------
audit_samples - validate the samples for a
game
-------------------------------------------------*/
//-------------------------------------------------
// summary - generate a summary, with an optional
// string format
//-------------------------------------------------
int audit_samples(emu_options &options, const game_driver *gamedrv, audit_record **audit)
media_auditor::summary media_auditor::summarize(astring *string)
{
machine_config config(*gamedrv, options);
audit_record *record;
int records = 0;
int sampnum;
/* count the number of sample records attached to this driver */
const device_config_sound_interface *sound = NULL;
for (bool gotone = config.m_devicelist.first(sound); gotone; gotone = sound->next(sound))
if (sound->devconfig().type() == SAMPLES)
{
const samples_interface *intf = (const samples_interface *)sound->devconfig().static_config();
if (intf->samplenames != NULL)
{
/* iterate over samples in this entry */
for (sampnum = 0; intf->samplenames[sampnum] != NULL; sampnum++)
if (intf->samplenames[sampnum][0] != '*')
records++;
}
}
/* if no records, just quit now */
if (records == 0)
goto skip;
/* allocate memory for the records */
*audit = global_alloc_array_clear(audit_record, records);
record = *audit;
/* now iterate over sample entries */
for (bool gotone = config.m_devicelist.first(sound); gotone; gotone = sound->next(sound))
if (sound->devconfig().type() == SAMPLES)
{
const samples_interface *intf = (const samples_interface *)sound->devconfig().static_config();
const char *sharedname = NULL;
if (intf->samplenames != NULL)
{
/* iterate over samples in this entry */
for (sampnum = 0; intf->samplenames[sampnum] != NULL; sampnum++)
if (intf->samplenames[sampnum][0] == '*')
sharedname = &intf->samplenames[sampnum][1];
else
{
/* attempt to access the file from the game driver name */
emu_file file(options.sample_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD);
file_error filerr = file.open(gamedrv->name, PATH_SEPARATOR, intf->samplenames[sampnum]);
/* attempt to access the file from the shared driver name */
if (filerr != FILERR_NONE && sharedname != NULL)
filerr = file.open(sharedname, PATH_SEPARATOR, intf->samplenames[sampnum]);
/* fill in the record */
record->type = AUDIT_FILE_SAMPLE;
record->name = intf->samplenames[sampnum];
if (filerr == FILERR_NONE)
set_status(record++, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD);
else
set_status(record++, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND);
}
}
}
skip:
return records;
}
/*-------------------------------------------------
audit_summary - output a summary given a
list of audit records
-------------------------------------------------*/
int audit_summary(const game_driver *gamedrv, int count, const audit_record *records, int output)
{
int overall_status = CORRECT;
int recnum;
/* no count AND no records means not found */
if (count == 0 && records == NULL)
// no count AND no records means not found
if (m_record_list.count() == 0)
return NOTFOUND;
/* loop over records */
for (recnum = 0; recnum < count; recnum++)
// loop over records
summary overall_status = CORRECT;
for (audit_record *record = m_record_list.first(); record != NULL; record = record->next())
{
const audit_record *record = &records[recnum];
int best_new_status = INCORRECT;
summary best_new_status = INCORRECT;
/* skip anything that's fine */
if (record->substatus == SUBSTATUS_GOOD)
// skip anything that's fine
if (record->substatus() == audit_record::SUBSTATUS_GOOD)
continue;
/* output the game name, file name, and length (if applicable) */
if (output)
// output the game name, file name, and length (if applicable)
if (string != NULL)
{
mame_printf_info("%-8s: %s", gamedrv->name, record->name);
if (record->explength > 0)
mame_printf_info(" (%d bytes)", record->explength);
mame_printf_info(" - ");
string->catprintf("%-12s: %s", m_enumerator.driver().name, record->name());
if (record->expected_length() > 0)
string->catprintf(" (%d bytes)", record->expected_length());
string->catprintf(" - ");
}
/* use the substatus for finer details */
switch (record->substatus)
// use the substatus for finer details
switch (record->substatus())
{
case SUBSTATUS_GOOD_NEEDS_REDUMP:
if (output) mame_printf_info("NEEDS REDUMP\n");
case audit_record::SUBSTATUS_GOOD_NEEDS_REDUMP:
if (string != NULL) string->catprintf("NEEDS REDUMP\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_FOUND_NODUMP:
if (output) mame_printf_info("NO GOOD DUMP KNOWN\n");
case audit_record::SUBSTATUS_FOUND_NODUMP:
if (string != NULL) string->catprintf("NO GOOD DUMP KNOWN\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_FOUND_BAD_CHECKSUM:
if (output)
case audit_record::SUBSTATUS_FOUND_BAD_CHECKSUM:
if (string != NULL)
{
astring tempstr;
mame_printf_info("INCORRECT CHECKSUM:\n");
mame_printf_info("EXPECTED: %s\n", record->exphashes.macro_string(tempstr));
mame_printf_info(" FOUND: %s\n", record->hashes.macro_string(tempstr));
string->catprintf("INCORRECT CHECKSUM:\n");
string->catprintf("EXPECTED: %s\n", record->expected_hashes().macro_string(tempstr));
string->catprintf(" FOUND: %s\n", record->actual_hashes().macro_string(tempstr));
}
break;
case SUBSTATUS_FOUND_WRONG_LENGTH:
if (output) mame_printf_info("INCORRECT LENGTH: %d bytes\n", record->length);
case audit_record::SUBSTATUS_FOUND_WRONG_LENGTH:
if (string != NULL) string->catprintf("INCORRECT LENGTH: %d bytes\n", record->actual_length());
break;
case SUBSTATUS_NOT_FOUND:
if (output) mame_printf_info("NOT FOUND\n");
case audit_record::SUBSTATUS_NOT_FOUND:
if (string != NULL) string->catprintf("NOT FOUND\n");
break;
case SUBSTATUS_NOT_FOUND_NODUMP:
if (output) mame_printf_info("NOT FOUND - NO GOOD DUMP KNOWN\n");
case audit_record::SUBSTATUS_NOT_FOUND_NODUMP:
if (string != NULL) string->catprintf("NOT FOUND - NO GOOD DUMP KNOWN\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_NOT_FOUND_OPTIONAL:
if (output) mame_printf_info("NOT FOUND BUT OPTIONAL\n");
case audit_record::SUBSTATUS_NOT_FOUND_OPTIONAL:
if (string != NULL) string->catprintf("NOT FOUND BUT OPTIONAL\n");
best_new_status = BEST_AVAILABLE;
break;
case SUBSTATUS_NOT_FOUND_PARENT:
if (output) mame_printf_info("NOT FOUND (shared with parent)\n");
case audit_record::SUBSTATUS_NOT_FOUND_PARENT:
if (string != NULL) string->catprintf("NOT FOUND (shared with parent)\n");
break;
case SUBSTATUS_NOT_FOUND_BIOS:
if (output) mame_printf_info("NOT FOUND (BIOS)\n");
case audit_record::SUBSTATUS_NOT_FOUND_BIOS:
if (string != NULL) string->catprintf("NOT FOUND (BIOS)\n");
break;
default:
assert(false);
}
/* downgrade the overall status if necessary */
// downgrade the overall status if necessary
overall_status = MAX(overall_status, best_new_status);
}
return overall_status;
}
//-------------------------------------------------
// audit_one_rom - validate a single ROM entry
//-------------------------------------------------
/***************************************************************************
UTILITIES
***************************************************************************/
/*-------------------------------------------------
audit_one_rom - validate a single ROM entry
-------------------------------------------------*/
static void audit_one_rom(emu_options &options, const rom_entry *rom, const char *regiontag, const game_driver *gamedrv, const char *validation, audit_record *record)
audit_record *media_auditor::audit_one_rom(const rom_entry *rom)
{
const game_driver *drv;
// allocate and append a new record
audit_record &record = m_record_list.append(*global_alloc(audit_record(*rom, audit_record::MEDIA_ROM)));
// see if we have a CRC and extract it if so
UINT32 crc = 0;
bool has_crc = record.expected_hashes().crc(crc);
/* fill in the record basics */
record->type = AUDIT_FILE_ROM;
record->name = ROM_GETNAME(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 */
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))
// find the file and checksum it, getting the file length along the way
emu_file file(m_enumerator.options().media_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD);
path_iterator path(m_searchpath);
astring curpath;
while (path.next(curpath, record.name()))
{
emu_file file(options.media_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD);
/* open the file if we can */
// open the file if we can
file_error filerr;
if (has_crc)
filerr = file.open(drv->name, PATH_SEPARATOR, ROM_GETNAME(rom), crc);
filerr = file.open(curpath, crc);
else
filerr = file.open(drv->name, PATH_SEPARATOR, ROM_GETNAME(rom));
filerr = file.open(curpath);
// if it worked, get the actual length and hashes, then stop
if (filerr == FILERR_NONE)
{
record->hashes = file.hashes(validation);
record->length = (UINT32)file.size();
record.set_actual(file.hashes(m_validation), file.size());
break;
}
}
/* if not found, check the region as a backup */
if (record->length == 0 && regiontag != NULL)
{
emu_file file(options.media_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD);
/* open the file if we can */
file_error filerr;
if (has_crc)
filerr = file.open(regiontag, PATH_SEPARATOR, ROM_GETNAME(rom), crc);
else
filerr = file.open(regiontag, PATH_SEPARATOR, ROM_GETNAME(rom));
if (filerr == FILERR_NONE)
{
record->hashes = file.hashes(validation);
record->length = (UINT32)file.size();
}
}
/* if we failed to find the file, set the appropriate status */
if (record->length == 0)
{
const game_driver *parent;
/* no good dump */
if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_NODUMP);
/* optional ROM */
else if (ROM_ISOPTIONAL(rom))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_OPTIONAL);
/* not found and used by parent */
else if (rom_used_by_parent(options, 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 */
else
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND);
}
/* if we did find the file, do additional verification */
else
{
/* length mismatch */
if (record->explength != record->length)
set_status(record, AUDIT_STATUS_FOUND_INVALID, SUBSTATUS_FOUND_WRONG_LENGTH);
/* found but needs a dump */
else if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_FOUND_NODUMP);
/* incorrect hash */
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 (record->exphashes.flag(hash_collection::FLAG_BAD_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD_NEEDS_REDUMP);
/* just plain old good */
else
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD);
}
// compute the final status
compute_status(record, rom, record.actual_length() != 0);
return &record;
}
/*-------------------------------------------------
audit_one_disk - validate a single disk entry
-------------------------------------------------*/
//-------------------------------------------------
// audit_one_disk - validate a single disk entry
//-------------------------------------------------
static void audit_one_disk(emu_options &options, const rom_entry *rom, const game_driver *gamedrv, const char *validation, audit_record *record)
audit_record *media_auditor::audit_one_disk(const rom_entry *rom)
{
// allocate and append a new record
audit_record &record = m_record_list.append(*global_alloc(audit_record(*rom, audit_record::MEDIA_DISK)));
// open the disk
emu_file *source_file;
chd_file *source;
chd_error err;
/* fill in the record basics */
record->type = AUDIT_FILE_DISK;
record->name = ROM_GETNAME(rom);
record->exphashes.from_internal_string(ROM_GETHASHDATA(rom));
/* open the disk */
err = open_disk_image(options, gamedrv, rom, &source_file, &source, NULL);
/* if we failed, report the error */
if (err != CHDERR_NONE)
{
/* out of memory */
if (err == CHDERR_OUT_OF_MEMORY)
set_status(record, AUDIT_STATUS_ERROR, SUBSTATUS_ERROR);
/* not found but it's not good anyway */
else if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_NODUMP);
/* not found but optional */
else if (DISK_ISOPTIONAL(rom))
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND_OPTIONAL);
/* not found at all */
else
set_status(record, AUDIT_STATUS_NOT_FOUND, SUBSTATUS_NOT_FOUND);
}
/* if we succeeded, validate it */
else
chd_error err = open_disk_image(m_enumerator.options(), &m_enumerator.driver(), rom, &source_file, &source, NULL);
// if we succeeded, get the hashes
if (err == CHDERR_NONE)
{
static const UINT8 nullhash[20] = { 0 };
chd_header header = *chd_get_header(source);
hash_collection hashes;
/* if there's an MD5 or SHA1 hash, add them to the output hash */
// if there's an MD5 or SHA1 hash, add them to the output hash
if (memcmp(nullhash, header.md5, sizeof(header.md5)) != 0)
record->hashes.add_from_buffer(hash_collection::HASH_MD5, header.md5, sizeof(header.md5));
hashes.add_from_buffer(hash_collection::HASH_MD5, header.md5, sizeof(header.md5));
if (memcmp(nullhash, header.sha1, sizeof(header.sha1)) != 0)
record->hashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1));
/* found but needs a dump */
if (record->exphashes.flag(hash_collection::FLAG_NO_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_FOUND_NODUMP);
/* incorrect hash */
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 (record->exphashes.flag(hash_collection::FLAG_BAD_DUMP))
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD_NEEDS_REDUMP);
/* just plain good */
else
set_status(record, AUDIT_STATUS_GOOD, SUBSTATUS_GOOD);
hashes.add_from_buffer(hash_collection::HASH_SHA1, header.sha1, sizeof(header.sha1));
// update the actual values
record.set_actual(hashes);
// close the file and release the source
chd_close(source);
global_free(source_file);
}
// compute the final status
compute_status(record, rom, record.actual_length() != 0);
return &record;
}
/*-------------------------------------------------
rom_used_by_parent - determine if a given
ROM is also used by the parent
-------------------------------------------------*/
//-------------------------------------------------
// compute_status - compute a detailed status
// based on the information we have
//-------------------------------------------------
static int rom_used_by_parent(emu_options &options, const game_driver *gamedrv, const hash_collection &romhashes, const game_driver **parent)
void media_auditor::compute_status(audit_record &record, const rom_entry *rom, bool found)
{
const game_driver *drv;
/* iterate up the parent chain */
for (drv = driver_get_clone(gamedrv); drv != NULL; drv = driver_get_clone(drv))
// if not found, provide more details
if (!found)
{
machine_config config(*drv, options);
const rom_entry *region;
const rom_entry *rom;
int parent;
// no good dump
if (record.expected_hashes().flag(hash_collection::FLAG_NO_DUMP))
record.set_status(audit_record::STATUS_NOT_FOUND, audit_record::SUBSTATUS_NOT_FOUND_NODUMP);
/* see if the parent has the same ROM or not */
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))
// optional ROM
else if (ROM_ISOPTIONAL(rom))
record.set_status(audit_record::STATUS_NOT_FOUND, audit_record::SUBSTATUS_NOT_FOUND_OPTIONAL);
// not found and used by parent
else if ((parent = also_used_by_parent(record.expected_hashes())) != -1)
{
if (m_enumerator.driver(parent).flags & GAME_IS_BIOS_ROOT)
record.set_status(audit_record::STATUS_NOT_FOUND, audit_record::SUBSTATUS_NOT_FOUND_BIOS);
else
record.set_status(audit_record::STATUS_NOT_FOUND, audit_record::SUBSTATUS_NOT_FOUND_PARENT);
}
// just plain old not found
else
record.set_status(audit_record::STATUS_NOT_FOUND, audit_record::SUBSTATUS_NOT_FOUND);
}
// if found, provide more details
else
{
// length mismatch
if (record.expected_length() != record.actual_length())
record.set_status(audit_record::STATUS_FOUND_INVALID, audit_record::SUBSTATUS_FOUND_WRONG_LENGTH);
// found but needs a dump
else if (record.expected_hashes().flag(hash_collection::FLAG_NO_DUMP))
record.set_status(audit_record::STATUS_GOOD, audit_record::SUBSTATUS_FOUND_NODUMP);
// incorrect hash
else if (record.expected_hashes() != record.actual_hashes())
record.set_status(audit_record::STATUS_FOUND_INVALID, audit_record::SUBSTATUS_FOUND_BAD_CHECKSUM);
// correct hash but needs a redump
else if (record.expected_hashes().flag(hash_collection::FLAG_BAD_DUMP))
record.set_status(audit_record::STATUS_GOOD, audit_record::SUBSTATUS_GOOD_NEEDS_REDUMP);
// just plain old good
else
record.set_status(audit_record::STATUS_GOOD, audit_record::SUBSTATUS_GOOD);
}
}
//-------------------------------------------------
// also_used_by_parent - return the index in the
// enumerator of the parent who also owns a media
// entry with the same hashes
//-------------------------------------------------
int media_auditor::also_used_by_parent(const hash_collection &romhashes)
{
// iterate up the parent chain
for (int drvindex = m_enumerator.find(m_enumerator.driver().parent); drvindex != -1; drvindex = m_enumerator.find(m_enumerator.driver(drvindex).parent))
// see if the parent has the same ROM or not
for (const rom_source *source = rom_first_source(m_enumerator.config(drvindex)); source != NULL; source = rom_next_source(*source))
for (const rom_entry *region = rom_first_region(*source); region; region = rom_next_region(region))
for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom))
{
hash_collection hashes(ROM_GETHASHDATA(rom));
if (!hashes.flag(hash_collection::FLAG_NO_DUMP) && hashes == romhashes)
{
if (parent != NULL)
*parent = drv;
return TRUE;
}
return drvindex;
}
}
return FALSE;
// nope, return -1
return -1;
}
//-------------------------------------------------
// audit_record - constructor
//-------------------------------------------------
audit_record::audit_record(const rom_entry &media, media_type type)
: m_next(NULL),
m_type(type),
m_status(STATUS_ERROR),
m_substatus(SUBSTATUS_ERROR),
m_name(ROM_GETNAME(&media)),
m_explength(rom_file_size(&media)),
m_length(0)
{
m_exphashes.from_internal_string(ROM_GETHASHDATA(&media));
}
audit_record::audit_record(const char *name, media_type type)
: m_next(NULL),
m_type(type),
m_status(STATUS_ERROR),
m_substatus(SUBSTATUS_ERROR),
m_name(name),
m_explength(0),
m_length(0)
{
}

View File

@ -4,8 +4,36 @@
ROM, disk, and sample auditing functions.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
****************************************************************************
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.
***************************************************************************/
@ -18,84 +46,145 @@
/***************************************************************************
CONSTANTS
***************************************************************************/
//**************************************************************************
// CONSTANTS
//**************************************************************************
/* hashes to use for validation */
// hashes to use for validation
#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
{
CORRECT = 0,
BEST_AVAILABLE,
INCORRECT,
NOTFOUND
};
/* image types for audit_record.type */
enum
{
AUDIT_FILE_ROM = 0,
AUDIT_FILE_DISK,
AUDIT_FILE_SAMPLE
};
/* status values for audit_record.status */
enum
{
AUDIT_STATUS_GOOD = 0,
AUDIT_STATUS_FOUND_INVALID,
AUDIT_STATUS_NOT_FOUND,
AUDIT_STATUS_ERROR
};
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
/* substatus values for audit_record.substatus */
enum
// ======================> audit_record
// holds the result of auditing a single item
class audit_record
{
SUBSTATUS_GOOD = 0,
SUBSTATUS_GOOD_NEEDS_REDUMP,
SUBSTATUS_FOUND_NODUMP,
SUBSTATUS_FOUND_BAD_CHECKSUM,
SUBSTATUS_FOUND_WRONG_LENGTH,
SUBSTATUS_NOT_FOUND,
SUBSTATUS_NOT_FOUND_NODUMP,
SUBSTATUS_NOT_FOUND_OPTIONAL,
SUBSTATUS_NOT_FOUND_PARENT,
SUBSTATUS_NOT_FOUND_BIOS,
SUBSTATUS_ERROR = 100
friend class simple_list<audit_record>;
public:
// media types
enum media_type
{
MEDIA_ROM = 0,
MEDIA_DISK,
MEDIA_SAMPLE
};
// status values
enum audit_status
{
STATUS_GOOD = 0,
STATUS_FOUND_INVALID,
STATUS_NOT_FOUND,
STATUS_ERROR
};
// substatus values
enum audit_substatus
{
SUBSTATUS_GOOD = 0,
SUBSTATUS_GOOD_NEEDS_REDUMP,
SUBSTATUS_FOUND_NODUMP,
SUBSTATUS_FOUND_BAD_CHECKSUM,
SUBSTATUS_FOUND_WRONG_LENGTH,
SUBSTATUS_NOT_FOUND,
SUBSTATUS_NOT_FOUND_NODUMP,
SUBSTATUS_NOT_FOUND_OPTIONAL,
SUBSTATUS_NOT_FOUND_PARENT,
SUBSTATUS_NOT_FOUND_BIOS,
SUBSTATUS_ERROR = 100
};
// construction/destruction
audit_record(const rom_entry &media, media_type type);
audit_record(const char *name, media_type type);
// getters
audit_record *next() const { return m_next; }
media_type type() const { return m_type; }
audit_status status() const { return m_status; }
audit_substatus substatus() const { return m_substatus; }
const char *name() const { return m_name; }
UINT64 expected_length() const { return m_explength; }
UINT64 actual_length() const { return m_length; }
const hash_collection &expected_hashes() const { return m_exphashes; }
const hash_collection &actual_hashes() const { return m_hashes; }
// setters
void set_status(audit_status status, audit_substatus substatus)
{
m_status = status;
m_substatus = substatus;
}
void set_actual(const hash_collection &hashes, UINT64 length = 0)
{
m_hashes = hashes;
m_length = length;
}
private:
// internal state
audit_record * m_next;
media_type m_type; /* type of item that was audited */
audit_status m_status; /* status of audit on this item */
audit_substatus m_substatus; /* finer-detail status */
const char * m_name; /* name of item */
UINT64 m_explength; /* expected length of item */
UINT64 m_length; /* actual length of item */
hash_collection m_exphashes; /* expected hash data */
hash_collection m_hashes; /* actual hash information */
};
// ======================> media_auditor
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
typedef struct _audit_record audit_record;
struct _audit_record
// class which manages auditing of items
class media_auditor
{
UINT8 type; /* type of item that was audited */
UINT8 status; /* status of audit on this item */
UINT8 substatus; /* finer-detail status */
const char * name; /* name of item */
UINT32 explength; /* expected length of item */
UINT32 length; /* actual length of item */
hash_collection exphashes; /* expected hash data */
hash_collection hashes; /* actual hash information */
public:
// summary values
enum summary
{
CORRECT = 0,
NONE_NEEDED,
BEST_AVAILABLE,
INCORRECT,
NOTFOUND
};
// construction/destruction
media_auditor(const driver_enumerator &enumerator);
// getters
audit_record *first() const { return m_record_list.first(); }
int count() const { return m_record_list.count(); }
// audit operations
summary audit_media(const char *validation = AUDIT_VALIDATE_FULL);
summary audit_samples();
summary summarize(astring *output = NULL);
private:
// internal helpers
audit_record *audit_one_rom(const rom_entry *rom);
audit_record *audit_one_disk(const rom_entry *rom);
void compute_status(audit_record &record, const rom_entry *rom, bool found);
int also_used_by_parent(const hash_collection &romhashes);
// internal state
simple_list<audit_record> m_record_list;
const driver_enumerator & m_enumerator;
const char * m_validation;
const char * m_searchpath;
};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
int audit_images(emu_options &options, const game_driver *gamedrv, const char *validation, audit_record **audit);
int audit_samples(emu_options &options, const game_driver *gamedrv, audit_record **audit);
int audit_summary(const game_driver *gamedrv, int count, const audit_record *records, int output);
#endif /* __AUDIT_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -80,6 +80,7 @@
// TYPE DEFINITIONS
//**************************************************************************
// cli_options wraps the general emu options with CLI-specific additions
class cli_options : public emu_options
{
public:
@ -91,22 +92,75 @@ private:
};
// cli_frontend handles command-line processing and emulator execution
class cli_frontend
{
public:
// construction/destruction
cli_frontend(cli_options &options, osd_interface &osd);
~cli_frontend();
// execute based on the incoming argc/argv
int execute(int argc, char **argv);
// direct access to the command operations
void listxml(const char *gamename = "*");
void listfull(const char *gamename = "*");
void listsource(const char *gamename = "*");
void listclones(const char *gamename = "*");
void listbrothers(const char *gamename = "*");
void listcrc(const char *gamename = "*");
void listroms(const char *gamename = "*");
void listsamples(const char *gamename = "*");
void listdevices(const char *gamename = "*");
void listmedia(const char *gamename = "*");
void verifyroms(const char *gamename = "*");
void verifysamples(const char *gamename = "*");
void romident(const char *filename);
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
private:
// internal helpers
void execute_commands(const char *exename);
void display_help();
void display_suggestions(const char *gamename);
// internal state
cli_options & m_options;
osd_interface & m_osd;
int m_result;
};
// media_identifier class identifies media by hash via a search in
// the driver database
class media_identifier
{
public:
// construction/destruction
media_identifier(cli_options &options);
// getters
const char *result() const { return m_result; }
int total() const { return m_total; }
int matches() const { return m_matches; }
int nonroms() const { return m_nonroms; }
// operations
void reset() { m_total = m_matches = m_nonroms = 0; m_result.reset(); }
void identify(const char *name);
void identify_file(const char *name);
void identify_data(const char *name, const UINT8 *data, int length);
int find_by_hash(const hash_collection &hashes, int length);
private:
// internal state
driver_enumerator m_drivlist;
astring m_result;
int m_total;
int m_matches;
int m_nonroms;
};
int cli_execute(cli_options &options, osd_interface &osd, int argc, char **argv);
/* informational functions */
void cli_info_listxml(emu_options &options, const char *gamename);
void cli_info_listfull(emu_options &options, const char *gamename);
void cli_info_listsource(emu_options &options, const char *gamename);
void cli_info_listclones(emu_options &options, const char *gamename);
void cli_info_listbrothers(emu_options &options, const char *gamename);
void cli_info_listcrc(emu_options &options, const char *gamename);
void cli_info_listroms(emu_options &options, const char *gamename);
void cli_info_listsamples(emu_options &options, const char *gamename);
void cli_info_listdevices(emu_options &options, const char *gamename);
#endif /* __CLIFRONT_H__ */

View File

@ -235,13 +235,13 @@ static int config_load_xml(running_machine &machine, emu_file &file, int which_t
case CONFIG_TYPE_CONTROLLER:
{
const game_driver *clone_of;
int clone_of;
/* match on: default, game name, source file name, parent name, grandparent name */
if (strcmp(name, "default") != 0 &&
strcmp(name, machine.system().name) != 0 &&
strcmp(name, srcfile) != 0 &&
((clone_of = driver_get_clone(&machine.system())) == NULL || strcmp(name, clone_of->name) != 0) &&
(clone_of == NULL || ((clone_of = driver_get_clone(clone_of)) == NULL) || strcmp(name, clone_of->name) != 0))
((clone_of = driver_list::clone(machine.system())) == -1 || strcmp(name, driver_list::driver(clone_of).name) != 0) &&
(clone_of == -1 || ((clone_of = driver_list::clone(clone_of)) == -1) || strcmp(name, driver_list::driver(clone_of).name) != 0))
continue;
break;
}

View File

@ -336,6 +336,12 @@ void *malloc_file_line(size_t size, const char *file, int line)
return osd_malloc(size);
}
void *malloc_array_file_line(size_t size, const char *file, int line)
{
// allocate the memory and fail if we can't
return osd_malloc_array(size);
}
void free_file_line( void *memory, const char *file, int line )
{
osd_free( memory );

View File

@ -93,7 +93,7 @@ text_buffer *text_buffer_alloc(UINT32 bytes, UINT32 lines)
return NULL;
/* allocate memory for the buffer itself */
text->buffer = (char *)osd_malloc(bytes);
text->buffer = (char *)osd_malloc_array(bytes);
if (!text->buffer)
{
osd_free(text);
@ -101,7 +101,7 @@ text_buffer *text_buffer_alloc(UINT32 bytes, UINT32 lines)
}
/* allocate memory for the lines array */
text->lineoffs = (INT32 *)osd_malloc(lines * sizeof(text->lineoffs[0]));
text->lineoffs = (INT32 *)osd_malloc_array(lines * sizeof(text->lineoffs[0]));
if (!text->lineoffs)
{
osd_free(text->buffer);

View File

@ -88,6 +88,7 @@ legacy_cpu_device_config::legacy_cpu_device_config(const machine_config &mconfig
// set the real name
m_name = get_legacy_config_string(DEVINFO_STR_NAME);
m_shortname = get_legacy_config_string(DEVINFO_STR_SHORTNAME);
m_searchpath = m_shortname;
}

View File

@ -307,6 +307,30 @@ device_config::device_config(const machine_config &mconfig, device_type type, co
}
device_config::device_config(const machine_config &mconfig, device_type type, const char *name, const char *shortname, const char *tag, const device_config *owner, UINT32 clock, UINT32 param)
: m_next(NULL),
m_owner(const_cast<device_config *>(owner)),
m_interface_list(NULL),
m_type(type),
m_clock(clock),
m_machine_config(mconfig),
m_static_config(NULL),
m_input_defaults(NULL),
m_name(name),
m_shortname(shortname),
m_searchpath(shortname),
m_tag(tag),
m_config_complete(false)
{
// derive the clock from our owner if requested
if ((m_clock & 0xff000000) == 0xff000000)
{
assert(m_owner != NULL);
m_clock = m_owner->m_clock * ((m_clock >> 12) & 0xfff) / ((m_clock >> 0) & 0xfff);
}
}
//-------------------------------------------------
// ~device_config - destructor
//-------------------------------------------------

View File

@ -262,6 +262,7 @@ class device_config
protected:
// construction/destruction
device_config(const machine_config &mconfig, device_type type, const char *name, const char *tag, const device_config *owner, UINT32 clock, UINT32 param = 0);
device_config(const machine_config &mconfig, device_type type, const char *name, const char *shortname, const char *tag, const device_config *owner, UINT32 clock, UINT32 param = 0);
virtual ~device_config();
public:
@ -289,6 +290,7 @@ public:
UINT32 clock() const { return m_clock; }
const char *name() const { return m_name; }
const char *shortname() const { return m_shortname; }
const char *searchpath() const { return m_searchpath; }
const char *tag() const { return m_tag; }
const void *static_config() const { return m_static_config; }
const machine_config &mconfig() const { return m_machine_config; }
@ -336,7 +338,9 @@ protected:
const input_device_default *m_input_defaults; // devices input ports default overrides
astring m_name; // name of the device
astring m_shortname; // short name of the device, used for potential romload
astring m_shortname; // short name of the device
astring m_searchpath; // search path, used for media loading
private:
astring m_tag; // tag for this instance
bool m_config_complete; // have we completed our configuration?

View File

@ -62,6 +62,7 @@ legacy_device_config_base::legacy_device_config_base(const machine_config &mconf
// set the proper name
m_name = get_legacy_config_string(DEVINFO_STR_NAME);
m_shortname = get_legacy_config_string(DEVINFO_STR_SHORTNAME);
m_searchpath = m_shortname;
}

View File

@ -126,7 +126,7 @@ enum
DEVINFO_STR_FIRST = 0x30000,
DEVINFO_STR_NAME = DEVINFO_STR_FIRST, // R/O: name of the device
DEVINFO_STR_SHORTNAME, // R/O: short name of device, used in case of romload
DEVINFO_STR_SHORTNAME, // R/O: search path of device, used for media loading
DEVINFO_STR_FAMILY, // R/O: family of the device
DEVINFO_STR_VERSION, // R/O: version of the device
DEVINFO_STR_SOURCE_FILE, // R/O: file containing the device implementation

View File

@ -383,7 +383,6 @@ bool device_image_interface::try_change_working_directory(const char *subdir)
void device_image_interface::setup_working_directory()
{
const game_driver *gamedrv;
char *dst = NULL;
osd_get_full_path(&dst,".");
@ -394,10 +393,10 @@ void device_image_interface::setup_working_directory()
if (try_change_working_directory("software"))
{
/* now down to a directory for this computer */
gamedrv = &device().machine().system();
while(gamedrv && !try_change_working_directory(gamedrv->name))
int gamedrv = driver_list::find(device().machine().system());
while(gamedrv != -1 && !try_change_working_directory(driver_list::driver(gamedrv).name))
{
gamedrv = driver_get_compatible(gamedrv);
gamedrv = driver_list::compatible_with(gamedrv);
}
}
osd_free(dst);

View File

@ -2,10 +2,38 @@
driver.c
Driver construction helpers.
Driver enumeration helpers.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
****************************************************************************
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.
***************************************************************************/
@ -14,219 +42,74 @@
/***************************************************************************
CONSTANTS
***************************************************************************/
//**************************************************************************
// DRIVER LIST
//**************************************************************************
#define DRIVER_LRU_SIZE 10
//-------------------------------------------------
// driver_list - constructor
//-------------------------------------------------
/***************************************************************************
GLOBAL VARIABLES
***************************************************************************/
static int driver_lru[DRIVER_LRU_SIZE];
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
static int penalty_compare(const char *source, const char *target);
/***************************************************************************
MISC FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
driver_get_name - return a pointer to a
driver given its name
-------------------------------------------------*/
const game_driver *driver_get_name(const char *name)
driver_list::driver_list()
{
int lurnum, drvnum;
/* scan the LRU list first */
for (lurnum = 0; lurnum < DRIVER_LRU_SIZE; lurnum++)
if (mame_stricmp(drivers[driver_lru[lurnum]]->name, name) == 0)
{
/* if not first, swap with the head */
if (lurnum != 0)
{
int temp = driver_lru[0];
driver_lru[0] = driver_lru[lurnum];
driver_lru[lurnum] = temp;
}
return drivers[driver_lru[0]];
}
/* scan for a match in the drivers -- slow! */
for (drvnum = 0; drivers[drvnum] != NULL; drvnum++)
if (mame_stricmp(drivers[drvnum]->name, name) == 0)
{
memmove((void *)&driver_lru[1], (void *)&driver_lru[0], sizeof(driver_lru[0]) * (DRIVER_LRU_SIZE - 1));
driver_lru[0] = drvnum;
return drivers[drvnum];
}
return NULL;
}
/*-------------------------------------------------
driver_get_clone - return a pointer to the
clone of a game driver.
-------------------------------------------------*/
//-------------------------------------------------
// find - find a driver by name
//-------------------------------------------------
const game_driver *driver_get_clone(const game_driver *driver)
int driver_list::find(const char *name)
{
/* if no clone, easy out */
if (driver->parent == NULL || (driver->parent[0] == '0' && driver->parent[1] == 0))
return NULL;
// if no name, bail
if (name == NULL)
return -1;
/* convert the name to a game_driver */
return driver_get_name(driver->parent);
// create a dummy item for comparison purposes
game_driver driver;
driver.name = name;
game_driver *driverptr = &driver;
// binary search to find it
const game_driver **result = reinterpret_cast<const game_driver **>(bsearch(&driverptr, s_drivers_sorted, s_driver_count, sizeof(*s_drivers_sorted), driver_sort_callback));
return (result == NULL) ? -1 : result - s_drivers_sorted;
}
/*-------------------------------------------------
driver_get_searchpath - return a search path
for a given driver
-------------------------------------------------*/
//-------------------------------------------------
// driver_sort_callback - compare two items in
// an array of game_driver pointers
//-------------------------------------------------
const char *driver_get_searchpath(const game_driver &driver, astring &string)
int driver_list::driver_sort_callback(const void *elem1, const void *elem2)
{
// create the search path consisting of gamedrv[;parent[;...]]
string = driver.name;
for (const game_driver *parent = driver_get_clone(&driver); parent != NULL; parent = driver_get_clone(parent))
string.cat(";").cat(parent->name);
return string;
const game_driver * const *item1 = reinterpret_cast<const game_driver * const *>(elem1);
const game_driver * const *item2 = reinterpret_cast<const game_driver * const *>(elem2);
return mame_stricmp((*item1)->name, (*item2)->name);
}
/*-------------------------------------------------
driver_list_get_approx_matches - find the best
n matches to a driver name.
-------------------------------------------------*/
//-------------------------------------------------
// penalty_compare - compare two strings for
// closeness and assign a score.
//-------------------------------------------------
void driver_list_get_approx_matches(const game_driver * const driverlist[], const char *name, int matches, const game_driver **list)
{
#undef rand
int matchnum, drvnum;
int *penalty;
/* if no name, pick random entries */
if (name == NULL || name[0] == 0)
{
const game_driver **templist;
int driver_count;
int shufnum;
/* allocate a temporary list */
templist = global_alloc_array(const game_driver *, driver_list_get_count(driverlist));
/* build up a list of valid entries */
for (drvnum = driver_count = 0; driverlist[drvnum] != NULL; drvnum++)
if ((driverlist[drvnum]->flags & GAME_NO_STANDALONE) == 0)
templist[driver_count++] = driverlist[drvnum];
/* seed the RNG first */
srand(osd_ticks());
/* shuffle */
for (shufnum = 0; shufnum < 4 * driver_count; shufnum++)
{
int item1 = rand() % driver_count;
int item2 = rand() % driver_count;
const game_driver *temp;
temp = templist[item1];
templist[item1] = templist[item2];
templist[item2] = temp;
}
/* copy out the first few entries */
for (matchnum = 0; matchnum < matches; matchnum++)
list[matchnum] = templist[matchnum % driver_count];
global_free(templist);
return;
}
/* allocate some temp memory */
penalty = global_alloc_array(int, matches);
/* initialize everyone's states */
for (matchnum = 0; matchnum < matches; matchnum++)
{
penalty[matchnum] = 9999;
list[matchnum] = NULL;
}
/* scan the entire drivers array */
for (drvnum = 0; driverlist[drvnum] != NULL; drvnum++)
{
int curpenalty, tmp;
/* skip things that can't run */
if ((driverlist[drvnum]->flags & GAME_NO_STANDALONE) != 0)
continue;
/* pick the best match between driver name and description */
curpenalty = penalty_compare(name, driverlist[drvnum]->description);
tmp = penalty_compare(name, driverlist[drvnum]->name);
curpenalty = MIN(curpenalty, tmp);
/* insert into the sorted table of matches */
for (matchnum = matches - 1; matchnum >= 0; matchnum--)
{
/* stop if we're worse than the current entry */
if (curpenalty >= penalty[matchnum])
break;
/* as lng as this isn't the last entry, bump this one down */
if (matchnum < matches - 1)
{
penalty[matchnum + 1] = penalty[matchnum];
list[matchnum + 1] = list[matchnum];
}
list[matchnum] = driverlist[drvnum];
penalty[matchnum] = curpenalty;
}
}
/* free our temp memory */
global_free(penalty);
}
/*-------------------------------------------------
penalty_compare - compare two strings for
closeness and assign a score.
-------------------------------------------------*/
static int penalty_compare(const char *source, const char *target)
int driver_list::penalty_compare(const char *source, const char *target)
{
int gaps = 1;
int last = TRUE;
bool last = true;
/* scan the strings */
// scan the strings
for ( ; *source && *target; target++)
{
/* do a case insensitive match */
int match = (tolower((UINT8)*source) == tolower((UINT8)*target));
// do a case insensitive match
bool match = (tolower((UINT8)*source) == tolower((UINT8)*target));
/* if we matched, advance the source */
// if we matched, advance the source
if (match)
source++;
/* if the match state changed, count gaps */
// if the match state changed, count gaps
if (match != last)
{
last = match;
@ -235,11 +118,11 @@ static int penalty_compare(const char *source, const char *target)
}
}
/* penalty if short string does not completely fit in */
// penalty if short string does not completely fit in
for ( ; *source; source++)
gaps++;
/* if we matched perfectly, gaps == 0 */
// if we matched perfectly, gaps == 0
if (gaps == 1 && *source == 0 && *target == 0)
gaps = 0;
@ -247,31 +130,244 @@ static int penalty_compare(const char *source, const char *target)
}
/*-------------------------------------------------
driver_list_get_count - returns the amount of
drivers
-------------------------------------------------*/
int driver_list_get_count(const game_driver * const driverlist[])
//**************************************************************************
// DRIVER ENUMERATOR
//**************************************************************************
//-------------------------------------------------
// driver_enumerator - constructor
//-------------------------------------------------
driver_enumerator::driver_enumerator(emu_options &options)
: m_current(-1),
m_filtered_count(0),
m_options(options),
m_included(global_alloc_array(UINT8, s_driver_count)),
m_config(global_alloc_array_clear(machine_config *, s_driver_count))
{
int count;
for (count = 0; driverlist[count] != NULL; count++) ;
return count;
include_all();
}
/*-------------------------------------------------
driver_get_compatible - return a pointer to the
compatible driver.
-------------------------------------------------*/
const game_driver *driver_get_compatible(const game_driver *drv)
driver_enumerator::driver_enumerator(emu_options &options, const char *string)
: m_current(-1),
m_filtered_count(0),
m_options(options),
m_included(global_alloc_array(UINT8, s_driver_count)),
m_config(global_alloc_array_clear(machine_config *, s_driver_count))
{
if (driver_get_clone(drv))
drv = driver_get_clone(drv);
else if (drv->compatible_with)
drv = driver_get_name(drv->compatible_with);
else
drv = NULL;
return drv;
filter(string);
}
driver_enumerator::driver_enumerator(emu_options &options, const game_driver &driver)
: m_current(-1),
m_filtered_count(0),
m_options(options),
m_included(global_alloc_array(UINT8, s_driver_count)),
m_config(global_alloc_array_clear(machine_config *, s_driver_count))
{
filter(driver);
}
//-------------------------------------------------
// ~driver_enumerator - destructor
//-------------------------------------------------
driver_enumerator::~driver_enumerator()
{
// free any configs
for (int index = 0; index < s_driver_count; index++)
global_free(m_config[index]);
// free the arrays
global_free(m_included);
global_free(m_config);
}
//-------------------------------------------------
// config - return a machine_config for the given
// driver, allocating on demand if needed
//-------------------------------------------------
machine_config &driver_enumerator::config(int index) const
{
assert(index >= 0 && index < s_driver_count);
if (m_config[index] == NULL)
m_config[index] = global_alloc(machine_config(*s_drivers_sorted[index], m_options));
return *m_config[index];
}
//-------------------------------------------------
// filter - filter the driver list against the
// given string
//-------------------------------------------------
int driver_enumerator::filter(const char *filterstring)
{
// reset the count
exclude_all();
// match name against each driver in the list
for (int index = 0; index < s_driver_count; index++)
if (matches(filterstring, s_drivers_sorted[index]->name))
include(index);
return m_filtered_count;
}
//-------------------------------------------------
// filter - filter the driver list against the
// given driver
//-------------------------------------------------
int driver_enumerator::filter(const game_driver &driver)
{
// reset the count
exclude_all();
// match name against each driver in the list
for (int index = 0; index < s_driver_count; index++)
if (s_drivers_sorted[index] == &driver)
include(index);
return m_filtered_count;
}
//-------------------------------------------------
// next - get the next driver matching the given
// filter
//-------------------------------------------------
bool driver_enumerator::next()
{
// always advance one
m_current++;
// if we have a filter, scan forward to the next match
while (m_current < s_driver_count)
{
if (m_included[m_current])
break;
m_current++;
}
// return true if we end up in range
return (m_current >= 0 && m_current < s_driver_count);
}
//-------------------------------------------------
// next_excluded - get the next driver that is
// not currently included in the list
//-------------------------------------------------
bool driver_enumerator::next_excluded()
{
// always advance one
m_current++;
// if we have a filter, scan forward to the next match
while (m_current < s_driver_count)
{
if (!m_included[m_current])
break;
m_current++;
}
// return true if we end up in range
return (m_current >= 0 && m_current < s_driver_count);
}
//-------------------------------------------------
// driver_sort_callback - compare two items in
// an array of game_driver pointers
//-------------------------------------------------
void driver_enumerator::find_approximate_matches(const char *string, int count, int *results)
{
#undef rand
// if no name, pick random entries
if (string == NULL || string[0] == 0)
{
// seed the RNG first
srand(osd_ticks());
// allocate a temporary list
int *templist = global_alloc_array(int, m_filtered_count);
int arrayindex = 0;
for (int index = 0; index < s_driver_count; index++)
if (m_included[index])
templist[arrayindex++] = index;
assert(arrayindex == m_filtered_count);
// shuffle
for (int shufnum = 0; shufnum < 4 * s_driver_count; shufnum++)
{
int item1 = rand() % m_filtered_count;
int item2 = rand() % m_filtered_count;
int temp = templist[item1];
templist[item1] = templist[item2];
templist[item2] = temp;
}
// copy out the first few entries
for (int matchnum = 0; matchnum < count; matchnum++)
results[matchnum] = templist[matchnum % m_filtered_count];
global_free(templist);
return;
}
// allocate memory to track the penalty value
int *penalty = global_alloc_array(int, count);
// initialize everyone's states
for (int matchnum = 0; matchnum < count; matchnum++)
{
penalty[matchnum] = 9999;
results[matchnum] = -1;
}
// scan the entire drivers array
for (int index = 0; index < s_driver_count; index++)
if (m_included[index])
{
// skip things that can't run
if ((s_drivers_sorted[index]->flags & GAME_NO_STANDALONE) != 0)
continue;
// pick the best match between driver name and description
int curpenalty = penalty_compare(string, s_drivers_sorted[index]->description);
int tmp = penalty_compare(string, s_drivers_sorted[index]->name);
curpenalty = MIN(curpenalty, tmp);
// insert into the sorted table of matches
for (int matchnum = count - 1; matchnum >= 0; matchnum--)
{
// stop if we're worse than the current entry
if (curpenalty >= penalty[matchnum])
break;
// as long as this isn't the last entry, bump this one down
if (matchnum < count - 1)
{
penalty[matchnum + 1] = penalty[matchnum];
results[matchnum + 1] = results[matchnum];
}
results[matchnum] = index;
penalty[matchnum] = curpenalty;
}
}
// free our temp memory
global_free(penalty);
}

View File

@ -2,10 +2,38 @@
driver.h
Definitions relating to game drivers.
Driver enumeration helpers.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
****************************************************************************
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.
***************************************************************************/
@ -81,6 +109,108 @@ struct game_driver
};
// driver_list is a purely static class that wraps the global driver list
class driver_list
{
DISABLE_COPYING(driver_list);
protected:
// construction/destruction
driver_list();
public:
// getters
static int total() { return s_driver_count; }
// any item by index
static const game_driver &driver(int index) { assert(index >= 0 && index < s_driver_count); return *s_drivers_sorted[index]; }
static int clone(int index) { return find(driver(index).parent); }
static int non_bios_clone(int index) { int result = find(driver(index).parent); return (result != -1 && (driver(result).flags & GAME_IS_BIOS_ROOT) == 0) ? result : -1; }
static int compatible_with(int index) { int result = clone(index); return (result != -1) ? result : find(driver(index).compatible_with); }
// any item by driver
static int clone(const game_driver &driver) { int index = find(driver); assert(index != -1); return clone(index); }
static int non_bios_clone(const game_driver &driver) { int index = find(driver); assert(index != -1); return non_bios_clone(index); }
static int compatible_with(const game_driver &driver) { int index = find(driver); assert(index != -1); return compatible_with(index); }
// general helpers
static int find(const char *name);
static int find(const game_driver &driver) { return find(driver.name); }
// static helpers
static bool matches(const char *wildstring, const char *string) { return (wildstring == NULL || mame_strwildcmp(wildstring, string) == 0); }
protected:
// internal helpers
static int driver_sort_callback(const void *elem1, const void *elem2);
static int penalty_compare(const char *source, const char *target);
// internal state
static int s_driver_count;
static const game_driver * const s_drivers_sorted[];
};
// driver_enumerator enables efficient iteration through the driver list
class driver_enumerator : public driver_list
{
DISABLE_COPYING(driver_enumerator);
public:
// construction/destruction
driver_enumerator(emu_options &options);
driver_enumerator(emu_options &options, const char *filter);
driver_enumerator(emu_options &options, const game_driver &filter);
~driver_enumerator();
// getters
int count() const { return m_filtered_count; }
int current() const { return m_current; }
emu_options &options() const { return m_options; }
// current item
const game_driver &driver() const { return driver_list::driver(m_current); }
machine_config &config() const { return config(m_current); }
int clone() { return driver_list::clone(m_current); }
int non_bios_clone() { return driver_list::non_bios_clone(m_current); }
int compatible_with() { return driver_list::compatible_with(m_current); }
void include() { include(m_current); }
void exclude() { exclude(m_current); }
// any item by index
bool included(int index) const { assert(index >= 0 && index < s_driver_count); return m_included[index]; }
bool excluded(int index) const { assert(index >= 0 && index < s_driver_count); return !m_included[index]; }
machine_config &config(int index) const;
void include(int index) { assert(index >= 0 && index < s_driver_count); if (!m_included[index]) { m_included[index] = true; m_filtered_count++; } }
void exclude(int index) { assert(index >= 0 && index < s_driver_count); if (m_included[index]) { m_included[index] = false; m_filtered_count--; } }
using driver_list::driver;
using driver_list::clone;
using driver_list::non_bios_clone;
using driver_list::compatible_with;
// filtering/iterating
int filter(const char *string = NULL);
int filter(const game_driver &driver);
void include_all() { memset(m_included, 1, sizeof(m_included[0]) * s_driver_count); m_filtered_count = s_driver_count; }
void exclude_all() { memset(m_included, 0, sizeof(m_included[0]) * s_driver_count); m_filtered_count = 0; }
void reset() { m_current = -1; }
bool next();
bool next_excluded();
// general helpers
void set_current(int index) { assert(index >= -1 && index <= s_driver_count); m_current = index; }
void find_approximate_matches(const char *string, int count, int *results);
private:
// internal state
int m_current;
int m_filtered_count;
emu_options & m_options;
UINT8 * m_included;
machine_config ** m_config;
};
/***************************************************************************
MACROS FOR BUILDING GAME DRIVERS
@ -176,24 +306,8 @@ extern const game_driver GAME_NAME(NAME) = \
GLOBAL VARIABLES
***************************************************************************/
extern const game_driver * const drivers[];
GAME_EXTERN(___empty);
GAME_EXTERN(empty);
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
const game_driver *driver_get_name(const char *name);
const game_driver *driver_get_clone(const game_driver *driver);
const game_driver *driver_get_compatible(const game_driver *drv);
const char *driver_get_searchpath(const game_driver &driver, astring &string);
void driver_list_get_approx_matches(const game_driver * const driverlist[], const char *name, int matches, const game_driver **list);
int driver_list_get_count(const game_driver * const driverlist[]);
#endif

View File

@ -20,7 +20,7 @@
*
*************************************/
static MACHINE_START( empty )
static MACHINE_START( ___empty )
{
/* force the UI to show the game select screen */
ui_menu_force_game_select(machine, &machine.render().ui_container());
@ -34,9 +34,9 @@ static MACHINE_START( empty )
*
*************************************/
static MACHINE_CONFIG_START( empty, driver_device )
static MACHINE_CONFIG_START( ___empty, driver_device )
MCFG_MACHINE_START(empty)
MCFG_MACHINE_START(___empty)
/* video hardware */
MCFG_SCREEN_ADD("screen", RASTER)
@ -54,8 +54,8 @@ MACHINE_CONFIG_END
*
*************************************/
ROM_START( empty )
ROM_REGION( 0x10, "user1", 0 )
ROM_START( ___empty )
ROM_REGION( 0x10, "user1", ROMREGION_ERASEFF )
ROM_END
@ -66,4 +66,4 @@ ROM_END
*
*************************************/
GAME( 2007, empty, 0, empty, 0, 0, ROT0, "MAME", "No Driver Loaded", 0 )
GAME( 2007, ___empty, 0, ___empty, 0, 0, ROT0, "MAME", "No Driver Loaded", GAME_NO_SOUND )

View File

@ -338,5 +338,6 @@ $(EMUOBJ)/rendlay.o: $(EMULAYOUT)/dualhovu.lh \
$(EMULAYOUT)/voffff20.lh \
$(EMULAYOUT)/lcd.lh \
$(EMULAYOUT)/lcd_rot.lh \
$(EMULAYOUT)/pinball.lh \
$(EMUOBJ)/video.o: $(EMULAYOUT)/snap.lh

View File

@ -92,6 +92,7 @@ public:
static UINT64 s_curid; // current ID
static osd_lock * s_lock; // lock for managing the list
static bool s_lock_alloc; // set to true temporarily during lock allocation
static bool s_tracking; // set to true when tracking is live
static memory_entry *s_hash[k_hash_prime];// hash table based on pointer
static memory_entry *s_freehead; // pointer to the head of the free list
@ -118,9 +119,10 @@ resource_pool global_resource_pool;
const zeromem_t zeromem = { };
// globals for memory_entry
UINT64 memory_entry::s_curid = 0;
UINT64 memory_entry::s_curid = 1;
osd_lock *memory_entry::s_lock = NULL;
bool memory_entry::s_lock_alloc = false;
bool memory_entry::s_tracking = false;
memory_entry *memory_entry::s_hash[memory_entry::k_hash_prime] = { NULL };
memory_entry *memory_entry::s_freehead = NULL;
@ -154,6 +156,31 @@ void *malloc_file_line(size_t size, const char *file, int line)
}
//-------------------------------------------------
// malloc_array_file_line - allocate memory with
// file and line number information, and a hint
// that this object is an array
//-------------------------------------------------
void *malloc_array_file_line(size_t size, const char *file, int line)
{
// allocate the memory and fail if we can't
void *result = osd_malloc_array(size);
if (result == NULL)
return NULL;
// add a new entry
memory_entry::allocate(size, result, file, line);
#ifdef MAME_DEBUG
// randomize the memory
rand_memory(result, size);
#endif
return result;
}
//-------------------------------------------------
// free_file_line - free memory with file
// and line number information
@ -189,7 +216,19 @@ void free_file_line(void *memory, const char *file, int line)
// memory
//-------------------------------------------------
void dump_unfreed_mem(void)
void track_memory(bool track)
{
memory_entry::s_tracking = track;
}
//-------------------------------------------------
// dump_unfreed_mem - called from the exit path
// of any code that wants to check for unfreed
// memory
//-------------------------------------------------
void dump_unfreed_mem()
{
#ifdef MAME_DEBUG
memory_entry::report_unfreed();
@ -446,7 +485,7 @@ memory_entry *memory_entry::allocate(size_t size, void *base, const char *file,
if (s_freehead == NULL)
{
// create a new chunk, and fail if we can't
memory_entry *entry = reinterpret_cast<memory_entry *>(osd_malloc(memory_block_alloc_chunk * sizeof(memory_entry)));
memory_entry *entry = reinterpret_cast<memory_entry *>(osd_malloc_array(memory_block_alloc_chunk * sizeof(memory_entry)));
if (entry == NULL)
{
release_lock();
@ -468,8 +507,8 @@ memory_entry *memory_entry::allocate(size_t size, void *base, const char *file,
// populate it
entry->m_size = size;
entry->m_base = base;
entry->m_file = file;
entry->m_line = line;
entry->m_file = s_tracking ? file : NULL;
entry->m_line = s_tracking ? line : 0;
entry->m_id = s_curid++;
if (LOG_ALLOCS)
fprintf(stderr, "#%06d, alloc %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line);
@ -561,5 +600,5 @@ void memory_entry::report_unfreed()
release_lock();
if (total > 0)
fprintf(stderr, "a total of %d bytes were not free()'d\n", total);
fprintf(stderr, "a total of %d bytes were not freed\n", total);
}

View File

@ -46,10 +46,26 @@
#include "osdcore.h"
//**************************************************************************
// DEBUGGING
//**************************************************************************
// set to 1 to track memory allocated by emualloc.h itself as well
#define TRACK_SELF_MEMORY (0)
//**************************************************************************
// MACROS
//**************************************************************************
// self-allocation helpers
#if TRACK_SELF_MEMORY
#define EMUALLOC_SELF_NEW new(__FILE__, __LINE__)
#else
#define EMUALLOC_SELF_NEW new
#endif
// pool allocation helpers
#define pool_alloc(_pool, _type) (_pool).add_object(new(__FILE__, __LINE__) _type)
#define pool_alloc_clear(_pool, _type) (_pool).add_object(new(__FILE__, __LINE__, zeromem) _type)
@ -72,11 +88,13 @@
// allocate memory with file and line number information
void *malloc_file_line(size_t size, const char *file, int line);
void *malloc_array_file_line(size_t size, const char *file, int line);
// free memory with file and line number information
void free_file_line(void *memory, const char *file, int line);
// called from the exit path of any code that wants to check for unfreed memory
void track_memory(bool track);
void dump_unfreed_mem();
@ -100,7 +118,7 @@ ATTR_FORCE_INLINE inline void *operator new(std::size_t size) throw (std::bad_al
ATTR_FORCE_INLINE inline void *operator new[](std::size_t size) throw (std::bad_alloc)
{
void *result = malloc_file_line(size, NULL, 0);
void *result = malloc_array_file_line(size, NULL, 0);
if (result == NULL)
throw std::bad_alloc();
return result;
@ -130,7 +148,7 @@ ATTR_FORCE_INLINE inline void *operator new(std::size_t size, const char *file,
ATTR_FORCE_INLINE inline void *operator new[](std::size_t size, const char *file, int line) throw (std::bad_alloc)
{
void *result = malloc_file_line(size, file, line);
void *result = malloc_array_file_line(size, file, line);
if (result == NULL)
throw std::bad_alloc();
return result;
@ -161,7 +179,7 @@ ATTR_FORCE_INLINE inline void *operator new(std::size_t size, const char *file,
ATTR_FORCE_INLINE inline void *operator new[](std::size_t size, const char *file, int line, const zeromem_t &) throw (std::bad_alloc)
{
void *result = malloc_file_line(size, file, line);
void *result = malloc_array_file_line(size, file, line);
if (result == NULL)
throw std::bad_alloc();
memset(result, 0, size);
@ -270,8 +288,8 @@ public:
bool contains(void *ptrstart, void *ptrend);
void clear();
template<class T> T *add_object(T* object) { add(*new(__FILE__, __LINE__) resource_pool_object<T>(object)); return object; }
template<class T> T *add_array(T* array, int count) { add(*new(__FILE__, __LINE__) resource_pool_array<T>(array, count)); return array; }
template<class T> T *add_object(T* object) { add(*EMUALLOC_SELF_NEW resource_pool_object<T>(object)); return object; }
template<class T> T *add_array(T* array, int count) { add(*EMUALLOC_SELF_NEW resource_pool_array<T>(array, count)); return array; }
private:
static const int k_hash_prime = 193;
@ -306,7 +324,7 @@ extern const zeromem_t zeromem;
#undef realloc
#undef free
#define malloc(x) malloc_file_line(x, __FILE__, __LINE__)
#define malloc(x) malloc_array_file_line(x, __FILE__, __LINE__)
#define calloc(x,y) __error_use_auto_alloc_clear_or_global_alloc_clear_instead__
#define realloc(x,y) __error_realloc_is_dangerous__
#define free(x) free_file_line(x, __FILE__, __LINE__)

View File

@ -349,12 +349,12 @@ void emu_options::parse_standard_inis(astring &error_string)
}
// then parse the grandparent, parent, and system-specific INIs
const game_driver *parent = driver_get_clone(cursystem);
const game_driver *gparent = (parent != NULL) ? driver_get_clone(parent) : NULL;
if (gparent != NULL)
parse_one_ini(gparent->name, OPTION_PRIORITY_GPARENT_INI, &error_string);
if (parent != NULL)
parse_one_ini(parent->name, OPTION_PRIORITY_PARENT_INI, &error_string);
int parent = driver_list::clone(*cursystem);
int gparent = (parent != -1) ? driver_list::clone(parent) : -1;
if (gparent != -1)
parse_one_ini(driver_list::driver(gparent).name, OPTION_PRIORITY_GPARENT_INI, &error_string);
if (parent != -1)
parse_one_ini(driver_list::driver(parent).name, OPTION_PRIORITY_PARENT_INI, &error_string);
parse_one_ini(cursystem->name, OPTION_PRIORITY_DRIVER_INI, &error_string);
}
@ -367,7 +367,8 @@ void emu_options::parse_standard_inis(astring &error_string)
const game_driver *emu_options::system() const
{
astring tempstr;
return driver_get_name(*core_filename_extract_base(&tempstr, system_name(), TRUE));
int index = driver_list::find(*core_filename_extract_base(&tempstr, system_name(), TRUE));
return (index != -1) ? &driver_list::driver(index) : NULL;
}

View File

@ -245,6 +245,37 @@ private:
};
// ======================> simple_list_wrapper
// a simple_list_wrapper wraps an existing object with a next pointer so it
// can live in a simple_list without requiring the object to have a next
// pointer
template<class T>
class simple_list_wrapper
{
public:
// construction/destruction
simple_list_wrapper(T *object)
: m_next(NULL),
m_object(object) { }
// operators
operator T *() { return m_object; }
operator T *() const { return m_object; }
T *operator *() { return m_object; }
T *operator *() const { return m_object; }
// getters
simple_list_wrapper *next() const { return m_next; }
T *object() const { return m_object; }
private:
// internal state
simple_list_wrapper * m_next;
T * m_object;
};
// ======================> fixed_allocator
// a fixed_allocator is a simple class that maintains a free pool of objects

View File

@ -593,7 +593,7 @@ bool hash_collection::remove(char type)
// crc - return the CRC hash if present
//-------------------------------------------------
bool hash_collection::crc(UINT32 &result)
bool hash_collection::crc(UINT32 &result) const
{
// attempt to find the CRC hash; if we fail, return false
hash_base *crchash = hash(HASH_CRC);
@ -601,7 +601,7 @@ bool hash_collection::crc(UINT32 &result)
return false;
// downcast to a hash_crc and convert to a UINT32
result = *downcast<hash_crc *>(crchash);
result = *downcast<const hash_crc *>(crchash);
return true;
}

View File

@ -161,7 +161,7 @@ public:
bool remove(char type);
// CRC-specific helpers
bool crc(UINT32 &result);
bool crc(UINT32 &result) const;
hash_base *add_crc(UINT32 crc);
// string conversion

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,36 @@
Dumps the MAME internal data as an XML file.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
****************************************************************************
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.
***************************************************************************/
@ -15,12 +43,47 @@
#define __INFO_H__
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
//**************************************************************************
// FUNCTION PROTOTYPES
//**************************************************************************
/* print the MAME database in XML format */
void print_mame_xml(FILE* out, const game_driver* const games[], const char *gamename, emu_options &options);
// helper class to putput
class info_xml_creator
{
public:
// construction/destruction
info_xml_creator(driver_enumerator &drivlist);
// output
void output(FILE *out);
private:
// internal helper
void output_one();
void output_sampleof();
void output_bios();
void output_rom();
void output_sample();
void output_chips();
void output_display();
void output_sound();
void output_input(const ioport_list &portlist);
void output_switches(const ioport_list &portlist, int type, const char *outertag, const char *innertag);
void output_adjusters(const ioport_list &portlist);
void output_categories(const ioport_list &portlist);
void output_driver();
void output_images();
void output_software_list();
void output_ramoptions();
const char *get_merge_name(const hash_collection &romhashes);
// internal state
FILE * m_output;
driver_enumerator & m_drivlist;
static const char s_dtd_string[];
};
#endif /* __INFO_H__ */

View File

@ -943,8 +943,17 @@ driver_device_config_base::driver_device_config_base(const machine_config &mconf
void driver_device_config_base::static_set_game(device_config *device, const game_driver *game)
{
downcast<driver_device_config_base *>(device)->m_system = game;
downcast<driver_device_config_base *>(device)->m_shortname = game->name;
driver_device_config_base *base = downcast<driver_device_config_base *>(device);
base->m_system = game;
// set the short name to the game's name
base->m_shortname = game->name;
// and set the search path to include all parents
base->m_searchpath = game->name;
for (int parent = driver_list::clone(*game); parent != -1; parent = driver_list::clone(parent))
base->m_searchpath.cat(";").cat(driver_list::driver(parent).name);
}

View File

@ -157,7 +157,7 @@ int mame_execute(emu_options &options, osd_interface &osd)
const game_driver *system = options.system();
if (system == NULL)
{
system = &GAME_NAME(empty);
system = &GAME_NAME(___empty);
if (firstgame)
started_empty = true;
}

View File

@ -1642,10 +1642,10 @@ void render_target::load_layout_files(const char *layoutfile, bool singlefile)
load_layout_file(NULL, m_manager.machine().config().m_default_layout);
// try to load another file based on the parent driver name
const game_driver *cloneof = driver_get_clone(&system);
if (cloneof != NULL)
if (!load_layout_file(cloneof->name, cloneof->name))
load_layout_file(cloneof->name, "default");
int cloneof = driver_list::clone(system);
if (cloneof != -1)
if (!load_layout_file(driver_list::driver(cloneof).name, driver_list::driver(cloneof).name))
load_layout_file(driver_list::driver(cloneof).name, "default");
// now do the built-in layouts for single-screen games
if (m_manager.machine().m_devicelist.count(SCREEN) == 1)

View File

@ -117,6 +117,10 @@
#include "lcd.lh"
#include "lcd_rot.lh"
// generic dummy pinball layout
#include "pinball.lh"
//**************************************************************************
// CONSTANTS

View File

@ -614,7 +614,6 @@ 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;
/* update status display */
display_loading_rom_message(romdata, ROM_GETNAME(romp));
@ -626,9 +625,8 @@ static int open_rom_file(rom_load_data *romdata, const char *regiontag, const ro
/* 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 = &romdata->machine().system(); romdata->file == NULL && drv != NULL; drv = driver_get_clone(drv))
if (drv->name != NULL && *drv->name != 0)
filerr = common_process_file(romdata->machine().options(), drv->name, has_crc, crc, romp, &romdata->file);
for (int drv = driver_list::find(romdata->machine().system()); romdata->file == NULL && drv != -1; drv = driver_list::clone(drv))
filerr = common_process_file(romdata->machine().options(), driver_list::driver(drv).name, has_crc, crc, romp, &romdata->file);
/* if the region is load by name, load the ROM from there */
if (romdata->file == NULL && regiontag != NULL)
@ -1007,7 +1005,6 @@ static void process_rom_entries(rom_load_data *romdata, const char *regiontag, c
chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, emu_file **image_file, chd_file **image_chd, const char *locationtag)
{
const game_driver *drv, *searchdrv;
const rom_entry *region, *rom;
const rom_source *source;
file_error filerr;
@ -1018,8 +1015,8 @@ chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, cons
/* 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))
filerr = common_process_file(options, searchdrv->name, ".chd", romp, image_file);
for (int searchdrv = driver_list::find(*gamedrv); searchdrv != -1 && filerr != FILERR_NONE; searchdrv = driver_list::clone(searchdrv))
filerr = common_process_file(options, driver_list::driver(searchdrv).name, ".chd", romp, image_file);
if (filerr != FILERR_NONE)
filerr = common_process_file(options, NULL, ".chd", romp, image_file);
@ -1114,9 +1111,9 @@ chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, cons
/* 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))
for (int drv = driver_list::find(*gamedrv); drv != -1; drv = driver_list::clone(drv))
{
machine_config config(*drv, options);
machine_config config(driver_list::driver(drv), options);
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))
if (ROMREGION_ISDISKDATA(region))
@ -1128,8 +1125,8 @@ chd_error open_disk_image(emu_options &options, const game_driver *gamedrv, cons
{
/* 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))
filerr = common_process_file(options, searchdrv->name, ".chd", rom, image_file);
for (int searchdrv = drv; searchdrv != -1 && filerr != FILERR_NONE; searchdrv = driver_list::clone(searchdrv))
filerr = common_process_file(options, driver_list::driver(searchdrv).name, ".chd", rom, image_file);
if (filerr != FILERR_NONE)
filerr = common_process_file(options, NULL, ".chd", rom, image_file);

View File

@ -107,13 +107,12 @@ ROM_END
//-------------------------------------------------
bsmt2000_device_config::bsmt2000_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock)
: device_config(mconfig, static_alloc_device_config, "BSMT2000", tag, owner, clock),
: device_config(mconfig, static_alloc_device_config, "BSMT2000", "bsmt2000", tag, owner, clock),
device_config_sound_interface(mconfig, *this),
device_config_memory_interface(mconfig, *this),
m_space_config("samples", ENDIANNESS_LITTLE, 8, 32, 0, NULL, *ADDRESS_MAP_NAME(bsmt2000)),
m_ready_callback(NULL)
{
m_shortname = "bsmt2000";
}

View File

@ -126,7 +126,7 @@ void wav_add_data_32(wav_file *wav, INT32 *data, int samples, int shift)
if (!wav) return;
/* allocate temp memory */
temp = (INT16 *)osd_malloc(samples * sizeof(temp[0]));
temp = (INT16 *)osd_malloc_array(samples * sizeof(temp[0]));
if (!temp)
return;
@ -154,7 +154,7 @@ void wav_add_data_16lr(wav_file *wav, INT16 *left, INT16 *right, int samples)
if (!wav) return;
/* allocate temp memory */
temp = (INT16 *)osd_malloc(samples * 2 * sizeof(temp[0]));
temp = (INT16 *)osd_malloc_array(samples * 2 * sizeof(temp[0]));
if (!temp)
return;
@ -179,7 +179,7 @@ void wav_add_data_32lr(wav_file *wav, INT32 *left, INT32 *right, int samples, in
if (!wav) return;
/* allocate temp memory */
temp = (INT16 *)osd_malloc(samples * 2 * sizeof(temp[0]));
temp = (INT16 *)osd_malloc_array(samples * 2 * sizeof(temp[0]));
if (!temp)
return;

View File

@ -280,7 +280,7 @@ int ui_display_startup_screens(running_machine &machine, int first_time, int sho
/* disable everything if we are using -str for 300 or fewer seconds, or if we're the empty driver,
or if we are debugging */
if (!first_time || (str > 0 && str < 60*5) || &machine.system() == &GAME_NAME(empty) || (machine.debug_flags & DEBUG_FLAG_ENABLED) != 0)
if (!first_time || (str > 0 && str < 60*5) || &machine.system() == &GAME_NAME(___empty) || (machine.debug_flags & DEBUG_FLAG_ENABLED) != 0)
show_gameinfo = show_warnings = show_disclaimer = FALSE;
/* initialize the on-screen display system */
@ -907,8 +907,6 @@ static astring &warnings_string(running_machine &machine, astring &string)
GAME_IMPERFECT_GRAPHICS | \
GAME_NO_COCKTAIL)
int i;
string.reset();
/* if no warnings, nothing to return */
@ -955,10 +953,6 @@ static astring &warnings_string(running_machine &machine, astring &string)
/* if there's a NOT WORKING, UNEMULATED PROTECTION or GAME MECHANICAL warning, make it stronger */
if (machine.system().flags & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION | GAME_MECHANICAL))
{
const game_driver *maindrv;
const game_driver *clone_of;
int foundworking;
/* add the strings for these warnings */
if (machine.system().flags & GAME_UNEMULATED_PROTECTION)
string.cat("The game has protection which isn't fully emulated.\n");
@ -970,25 +964,25 @@ static astring &warnings_string(running_machine &machine, astring &string)
"It is not possible to fully play this " GAMENOUN ".\n");
/* find the parent of this driver */
clone_of = driver_get_clone(&machine.system());
if (clone_of != NULL && !(clone_of->flags & GAME_IS_BIOS_ROOT))
driver_enumerator drivlist(machine.options());
int maindrv = drivlist.find(machine.system());
int clone_of = drivlist.non_bios_clone(maindrv);
if (clone_of != -1)
maindrv = clone_of;
else
maindrv = &machine.system();
/* scan the driver list for any working clones and add them */
foundworking = FALSE;
for (i = 0; drivers[i] != NULL; i++)
if (drivers[i] == maindrv || driver_get_clone(drivers[i]) == maindrv)
if ((drivers[i]->flags & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION | GAME_MECHANICAL)) == 0)
bool foundworking = false;
while (drivlist.next())
if (drivlist.current() == maindrv || drivlist.clone() == maindrv)
if ((drivlist.driver().flags & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION | GAME_MECHANICAL)) == 0)
{
/* this one works, add a header and display the name of the clone */
if (!foundworking)
string.cat("\n\nThere are working clones of this game: ");
else
string.cat(", ");
string.cat(drivers[i]->name);
foundworking = TRUE;
string.cat(drivlist.driver().name);
foundworking = true;
}
if (foundworking)

View File

@ -208,7 +208,7 @@ struct _select_game_state
UINT8 error;
UINT8 rerandomize;
char search[40];
const game_driver * matchlist[VISIBLE_GAMES_IN_LIST];
int matchlist[VISIBLE_GAMES_IN_LIST];
const game_driver * driverlist[1];
};
@ -245,6 +245,9 @@ static const char priortext[] = "Return to Prior Menu";
static const char backtext[] = "Return to " CAPSTARTGAMENOUN;
static const char exittext[] = "Exit";
// temporary hack until this is C++-ified
static driver_enumerator *drivlist;
/***************************************************************************
@ -299,7 +302,6 @@ static void menu_crosshair_populate(running_machine &machine, ui_menu *menu);
static void menu_quit_game(running_machine &machine, ui_menu *menu, void *parameter, void *state);
static void menu_select_game(running_machine &machine, ui_menu *menu, void *parameter, void *state);
static void menu_select_game_populate(running_machine &machine, ui_menu *menu, select_game_state *menustate);
static int CLIB_DECL menu_select_game_driver_compare(const void *elem1, const void *elem2);
static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state *menustate);
static void menu_select_game_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x, float y, float x2, float y2);
@ -3426,9 +3428,10 @@ static void menu_select_game(running_machine &machine, ui_menu *menu, void *para
/* if no state, allocate some */
if (state == NULL)
{
state = ui_menu_alloc_state(menu, sizeof(*menustate) + sizeof(menustate->driverlist) * driver_list_get_count(drivers), NULL);
state = ui_menu_alloc_state(menu, sizeof(*menustate) + sizeof(menustate->driverlist) * driver_list::total(), NULL);
if (parameter != NULL)
strcpy(((select_game_state *)state)->search, (const char *)parameter);
((select_game_state *)state)->matchlist[0] = -1;
}
menustate = (select_game_state *)state;
@ -3459,24 +3462,20 @@ static void menu_select_game(running_machine &machine, ui_menu *menu, void *para
/* anything else is a driver */
else
{
audit_record *audit;
int audit_records;
int audit_result;
// audit the game first to see if we're going to work
driver_enumerator enumerator(machine.options(), *driver);
enumerator.next();
media_auditor auditor(enumerator);
media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST);
/* audit the game first to see if we're going to work */
audit_records = audit_images(menu->machine().options(), driver, AUDIT_VALIDATE_FAST, &audit);
audit_result = audit_summary(driver, audit_records, audit, FALSE);
if (audit_records > 0)
global_free(audit);
/* if everything looks good, schedule the new driver */
if (audit_result == CORRECT || audit_result == BEST_AVAILABLE)
// if everything looks good, schedule the new driver
if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE)
{
machine.schedule_new_driver(*driver);
ui_menu_stack_reset(machine);
}
/* otherwise, display an error */
// otherwise, display an error
else
{
ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
@ -3551,18 +3550,19 @@ static void menu_select_game_populate(running_machine &machine, ui_menu *menu, s
}
/* otherwise, rebuild the match list */
if (menustate->search[0] != 0 || menustate->matchlist[0] == NULL || menustate->rerandomize)
driver_list_get_approx_matches(menustate->driverlist, menustate->search, matchcount, menustate->matchlist);
assert(drivlist != NULL);
if (menustate->search[0] != 0 || menustate->matchlist[0] == -1 || menustate->rerandomize)
drivlist->find_approximate_matches(menustate->search, matchcount, menustate->matchlist);
menustate->rerandomize = FALSE;
/* iterate over entries */
for (curitem = 0; curitem < matchcount; curitem++)
{
const game_driver *driver = menustate->matchlist[curitem];
if (driver != NULL)
int curmatch = menustate->matchlist[curitem];
if (curmatch != -1)
{
const game_driver *cloneof = driver_get_clone(driver);
ui_menu_item_append(menu, driver->name, driver->description, (cloneof == NULL || (cloneof->flags & GAME_IS_BIOS_ROOT)) ? 0 : MENU_FLAG_INVERT, (void *)driver);
int cloneof = drivlist->non_bios_clone(curmatch);
ui_menu_item_append(menu, drivlist->driver(curmatch).name, drivlist->driver(curmatch).description, (cloneof == -1) ? 0 : MENU_FLAG_INVERT, (void *)&drivlist->driver(curmatch));
}
}
@ -3578,24 +3578,6 @@ static void menu_select_game_populate(running_machine &machine, ui_menu *menu, s
}
/*-------------------------------------------------
menu_select_game_driver_compare - compare the
names of two drivers
-------------------------------------------------*/
static int CLIB_DECL menu_select_game_driver_compare(const void *elem1, const void *elem2)
{
const game_driver **driver1_ptr = (const game_driver **)elem1;
const game_driver **driver2_ptr = (const game_driver **)elem2;
const char *driver1 = (*driver1_ptr)->name;
const char *driver2 = (*driver2_ptr)->name;
while (*driver1 == *driver2 && *driver1 != 0)
driver1++, driver2++;
return *driver1 - *driver2;
}
/*-------------------------------------------------
menu_select_game_build_driver_list - build a
list of available drivers
@ -3603,17 +3585,11 @@ static int CLIB_DECL menu_select_game_driver_compare(const void *elem1, const vo
static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state *menustate)
{
int driver_count = driver_list_get_count(drivers);
int drivnum, listnum;
UINT8 *found;
/* create a sorted copy of the main driver list */
memcpy((void *)menustate->driverlist, drivers, driver_count * sizeof(menustate->driverlist[0]));
qsort((void *)menustate->driverlist, driver_count, sizeof(menustate->driverlist[0]), menu_select_game_driver_compare);
/* allocate a temporary array to track which ones we found */
found = (UINT8 *)ui_menu_pool_alloc(menu, (driver_count + 7) / 8);
memset(found, 0, (driver_count + 7) / 8);
// start with an empty list
// hack alert: use new directly here to avoid reporting this one-time static memory as unfreed
if (drivlist == NULL)
drivlist = new driver_enumerator(menu->machine().options());
drivlist->exclude_all();
/* open a path to the ROMs and find them in the array */
file_enumerator path(menu->machine().options().media_path());
@ -3622,9 +3598,6 @@ static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state
/* iterate while we get new objects */
while ((dir = path.next()) != NULL)
{
game_driver tempdriver;
game_driver *tempdriver_ptr;
const game_driver **found_driver;
char drivername[50];
char *dst = drivername;
const char *src;
@ -3634,23 +3607,16 @@ static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state
*dst++ = tolower((UINT8)*src);
*dst = 0;
/* find it in the array */
tempdriver.name = drivername;
tempdriver_ptr = &tempdriver;
found_driver = (const game_driver **)bsearch(&tempdriver_ptr, menustate->driverlist, driver_count, sizeof(*menustate->driverlist), menu_select_game_driver_compare);
/* if found, mark the corresponding entry in the array */
if (found_driver != NULL)
{
int index = found_driver - menustate->driverlist;
found[index / 8] |= 1 << (index % 8);
}
int drivnum = drivlist->find(drivername);
if (drivnum != -1)
drivlist->include(drivnum);
}
/* now build the final list */
for (drivnum = listnum = 0; drivnum < driver_count; drivnum++)
if (found[drivnum / 8] & (1 << (drivnum % 8)))
menustate->driverlist[listnum++] = menustate->driverlist[drivnum];
drivlist->reset();
int listnum = 0;
while (drivlist->next())
menustate->driverlist[listnum++] = &drivlist->driver();
/* NULL-terminate */
menustate->driverlist[listnum] = NULL;

View File

@ -314,12 +314,11 @@ static bool validate_inlines(void)
information
-------------------------------------------------*/
static bool validate_driver(const machine_config &config, game_driver_map &names, game_driver_map &descriptions)
static bool validate_driver(driver_enumerator &drivlist, game_driver_map &names, game_driver_map &descriptions)
{
const game_driver &driver = config.gamedrv();
const game_driver *clone_of;
const game_driver &driver = drivlist.driver();
const machine_config &config = drivlist.config();
const char *compatible_with;
const game_driver *other_drv;
bool error = FALSE, is_clone;
const char *s;
@ -343,27 +342,27 @@ static bool validate_driver(const machine_config &config, game_driver_map &names
/* determine the clone */
is_clone = (strcmp(driver.parent, "0") != 0);
clone_of = driver_get_clone(&driver);
if (clone_of && (clone_of->flags & GAME_IS_BIOS_ROOT))
int clone_of = drivlist.clone(driver);
if (clone_of != -1 && (drivlist.driver(clone_of).flags & GAME_IS_BIOS_ROOT))
is_clone = false;
/* if we have at least 100 drivers, validate the clone */
/* (100 is arbitrary, but tries to avoid tiny.mak dependencies) */
if (driver_list_get_count(drivers) > 100 && !clone_of && is_clone)
if (driver_list::total() > 100 && clone_of == -1 && is_clone)
{
mame_printf_error("%s: %s is a non-existant clone\n", driver.source_file, driver.parent);
error = true;
}
/* look for recursive cloning */
if (clone_of == &driver)
if (clone_of != -1 && &drivlist.driver(clone_of) == &driver)
{
mame_printf_error("%s: %s is set as a clone of itself\n", driver.source_file, driver.name);
error = true;
}
/* look for clones that are too deep */
if (clone_of != NULL && (clone_of = driver_get_clone(clone_of)) != NULL && (clone_of->flags & GAME_IS_BIOS_ROOT) == 0)
if (clone_of != -1 && (clone_of = drivlist.non_bios_clone(clone_of)) != -1)
{
mame_printf_error("%s: %s is a clone of a clone\n", driver.source_file, driver.name);
error = true;
@ -392,23 +391,23 @@ static bool validate_driver(const machine_config &config, game_driver_map &names
compatible_with = NULL;
/* check for this driver being compatible with a non-existant driver */
if ((compatible_with != NULL) && (driver_get_name(driver.compatible_with) == NULL))
if ((compatible_with != NULL) && (drivlist.find(driver.compatible_with) == -1))
{
mame_printf_error("%s: is compatible with %s, which is not in drivers[]\n", driver.name, driver.compatible_with);
error = true;
}
/* check for clone_of and compatible_with being specified at the same time */
if ((driver_get_clone(&driver) != NULL) && (compatible_with != NULL))
if ((drivlist.clone(driver) != -1) && (compatible_with != NULL))
{
mame_printf_error("%s: both compatible_with and clone_of are specified\n", driver.name);
error = true;
}
/* find any recursive dependencies on the current driver */
for (other_drv = driver_get_compatible(&driver); other_drv != NULL; other_drv = driver_get_compatible(other_drv))
for (int other_drv = drivlist.compatible_with(driver); other_drv != -1; other_drv = drivlist.compatible_with(other_drv))
{
if (&driver == other_drv)
if (&driver == &drivlist.driver(other_drv))
{
mame_printf_error("%s: recursive compatibility\n", driver.name);
error = true;
@ -432,9 +431,10 @@ static bool validate_driver(const machine_config &config, game_driver_map &names
validate_roms - validate ROM definitions
-------------------------------------------------*/
static bool validate_roms(const machine_config &config, region_array *rgninfo, game_driver_map &roms)
static bool validate_roms(driver_enumerator &drivlist, region_array *rgninfo, game_driver_map &roms)
{
const game_driver &driver = config.gamedrv();
const game_driver &driver = drivlist.driver();
const machine_config &config = drivlist.config();
int bios_flags = 0, last_bios = 0;
const char *last_rgnname = "???";
const char *last_name = "???";
@ -579,9 +579,10 @@ static bool validate_roms(const machine_config &config, region_array *rgninfo, g
configurations
-------------------------------------------------*/
static bool validate_display(const machine_config &config)
static bool validate_display(driver_enumerator &drivlist)
{
const game_driver &driver = config.gamedrv();
const game_driver &driver = drivlist.driver();
const machine_config &config = drivlist.config();
bool palette_modes = false;
bool error = false;
@ -605,9 +606,10 @@ static bool validate_display(const machine_config &config)
configuration
-------------------------------------------------*/
static bool validate_gfx(const machine_config &config, region_array *rgninfo)
static bool validate_gfx(driver_enumerator &drivlist, region_array *rgninfo)
{
const game_driver &driver = config.gamedrv();
const game_driver &driver = drivlist.driver();
const machine_config &config = drivlist.config();
bool error = false;
int gfxnum;
@ -939,12 +941,13 @@ static void validate_dip_settings(const input_field_config *field, const game_dr
validate_inputs - validate input configuration
-------------------------------------------------*/
static bool validate_inputs(const machine_config &config, int_map &defstr_map, ioport_list &portlist)
static bool validate_inputs(driver_enumerator &drivlist, int_map &defstr_map, ioport_list &portlist)
{
const input_port_config *scanport;
const input_port_config *port;
const input_field_config *field;
const game_driver &driver = config.gamedrv();
const game_driver &driver = drivlist.driver();
const machine_config &config = drivlist.config();
int empty_string_found = FALSE;
char errorbuf[1024];
bool error = false;
@ -1088,10 +1091,11 @@ static bool validate_inputs(const machine_config &config, int_map &defstr_map, i
checks
-------------------------------------------------*/
static bool validate_devices(const machine_config &config, const ioport_list &portlist, region_array *rgninfo)
static bool validate_devices(driver_enumerator &drivlist, const ioport_list &portlist, region_array *rgninfo)
{
bool error = false;
const game_driver &driver = config.gamedrv();
const game_driver &driver = drivlist.driver();
const machine_config &config = drivlist.config();
for (const device_config *devconfig = config.m_devicelist.first(); devconfig != NULL; devconfig = devconfig->next())
{
@ -1126,7 +1130,6 @@ static bool validate_devices(const machine_config &config, const ioport_list &po
void validate_drivers(emu_options &options, const game_driver *curdriver)
{
osd_ticks_t prep = 0;
osd_ticks_t expansion = 0;
osd_ticks_t driver_checks = 0;
osd_ticks_t rom_checks = 0;
osd_ticks_t gfx_checks = 0;
@ -1186,9 +1189,10 @@ void validate_drivers(emu_options &options, const game_driver *curdriver)
prep += get_profile_ticks();
/* iterate over all drivers */
for (int drivnum = 0; drivers[drivnum]; drivnum++)
driver_enumerator drivlist(options);
while (drivlist.next())
{
const game_driver &driver = *drivers[drivnum];
const game_driver &driver = drivlist.driver();
ioport_list portlist;
region_array rgninfo;
@ -1198,39 +1202,34 @@ void validate_drivers(emu_options &options, const game_driver *curdriver)
try
{
/* expand the machine driver */
expansion -= get_profile_ticks();
machine_config config(driver, options);
expansion += get_profile_ticks();
/* validate the driver entry */
driver_checks -= get_profile_ticks();
error = validate_driver(config, names, descriptions) || error;
error = validate_driver(drivlist, names, descriptions) || error;
driver_checks += get_profile_ticks();
/* validate the ROM information */
rom_checks -= get_profile_ticks();
error = validate_roms(config, &rgninfo, roms) || error;
error = validate_roms(drivlist, &rgninfo, roms) || error;
rom_checks += get_profile_ticks();
/* validate input ports */
input_checks -= get_profile_ticks();
error = validate_inputs(config, defstr, portlist) || error;
error = validate_inputs(drivlist, defstr, portlist) || error;
input_checks += get_profile_ticks();
/* validate the display */
display_checks -= get_profile_ticks();
error = validate_display(config) || error;
error = validate_display(drivlist) || error;
display_checks += get_profile_ticks();
/* validate the graphics decoding */
gfx_checks -= get_profile_ticks();
error = validate_gfx(config, &rgninfo) || error;
error = validate_gfx(drivlist, &rgninfo) || error;
gfx_checks += get_profile_ticks();
/* validate devices */
device_checks -= get_profile_ticks();
error = validate_devices(config, portlist, &rgninfo) || error;
error = validate_devices(drivlist, portlist, &rgninfo) || error;
device_checks += get_profile_ticks();
}
catch (emu_fatalerror &err)
@ -1241,7 +1240,6 @@ void validate_drivers(emu_options &options, const game_driver *curdriver)
#if (REPORT_TIMES)
mame_printf_info("Prep: %8dm\n", (int)(prep / 1000000));
mame_printf_info("Expansion: %8dm\n", (int)(expansion / 1000000));
mame_printf_info("Driver: %8dm\n", (int)(driver_checks / 1000000));
mame_printf_info("ROM: %8dm\n", (int)(rom_checks / 1000000));
mame_printf_info("CPU: %8dm\n", (int)(cpu_checks / 1000000));

View File

@ -318,6 +318,7 @@ public:
astring(const char *str1, const char *str2, const char *str3, const char *str4) { init().cpy(str1).cat(str2).cat(str3).cat(str4); }
astring(const char *str1, const char *str2, const char *str3, const char *str4, const char *str5) { init().cpy(str1).cat(str2).cat(str3).cat(str4).cat(str5); }
astring(const astring &string) { init().cpy(string); }
astring(const astring &string, int start, int count = -1) { init().cpysubstr(string, start, count); }
astring &operator=(const char *string) { return cpy(string); }
astring &operator=(const astring &string) { return cpy(string); }

View File

@ -141,7 +141,7 @@ char *core_strdup(const char *str)
char *cpy = NULL;
if (str != NULL)
{
cpy = (char *)osd_malloc(strlen(str) + 1);
cpy = (char *)osd_malloc_array(strlen(str) + 1);
if (cpy != NULL)
strcpy(cpy, str);
}

View File

@ -130,7 +130,7 @@ static osd_shared_mem *osd_sharedmem_alloc(const char *path, int create, size_t
if (create)
{
char *buf = (char *) osd_malloc(size);
char *buf = (char *) osd_malloc_array(size);
memset(buf,0, size);
fd = open(path, O_RDWR | O_CREAT, S_IRWXU);
@ -147,7 +147,7 @@ static osd_shared_mem *osd_sharedmem_alloc(const char *path, int create, size_t
}
os_shmem->creator = 0;
}
os_shmem->fn = (char *) osd_malloc(strlen(path)+1);
os_shmem->fn = (char *) osd_malloc_array(strlen(path)+1);
strcpy(os_shmem->fn, path);
assert(fd != -1);
@ -178,7 +178,7 @@ static osd_shared_mem *osd_sharedmem_alloc(const char *path, int create, size_t
os_shmem->creator = 0;
os_shmem->ptr = (void *) osd_malloc(size);
os_shmem->ptr = (void *) osd_malloc_array(size);
os_shmem->size = size;
return os_shmem;
}

View File

@ -1589,7 +1589,7 @@ static int megadrive_load_nonlist(device_image_interface &image)
else if ((rawROM[0x2080] == 'E') && (rawROM[0x2081] == 'A') &&
(rawROM[0x2082] == 'M' || rawROM[0x2082] == 'G'))
{
tmpROMnew = (unsigned char *)osd_malloc(length);
tmpROMnew = global_alloc_array(unsigned char, length);
secondhalf = &tmpROMnew[length >> 1];
if (!tmpROMnew)
@ -1605,7 +1605,7 @@ static int megadrive_load_nonlist(device_image_interface &image)
ROM[ptr] = secondhalf[ptr >> 1];
ROM[ptr + 1] = tmpROMnew[ptr >> 1];
}
free(tmpROMnew);
global_free(tmpROMnew);
#ifdef LSB_FIRST
for (ptr = 0; ptr < length; ptr += 2)

12225
src/mame/mame.lst Normal file

File diff suppressed because it is too large Load Diff

View File

@ -235,11 +235,6 @@ SOUNDS += MAS3507D
# the list of drivers
#-------------------------------------------------
ifeq ($(TARGET),mame)
DRVLIBS += \
$(MAMEOBJ)/mamedriv.o
endif
DRVLIBS += \
$(MAMEOBJ)/alba.a \
$(MAMEOBJ)/alliedl.a \

File diff suppressed because it is too large Load Diff

View File

@ -1,70 +0,0 @@
/******************************************************************************
tiny.c
mamedriv.c substitute file for "tiny" MAME builds.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
The list of used drivers. Drivers have to be included here to be recognized
by the executable.
To save some typing, we use a hack here. This file is recursively #included
twice, with different definitions of the DRIVER() macro. The first one
declares external references to the drivers; the second one builds an array
storing all the drivers.
******************************************************************************/
#include "emu.h"
#ifndef DRIVER_RECURSIVE
#define DRIVER_RECURSIVE
/* step 1: declare all external references */
#define DRIVER(NAME) GAME_EXTERN(NAME);
#include "tiny.c"
/* step 2: define the drivers[] array */
#undef DRIVER
#define DRIVER(NAME) &GAME_NAME(NAME),
const game_driver * const drivers[] =
{
#include "tiny.c"
0 /* end of array */
};
#else /* DRIVER_RECURSIVE */
DRIVER( robby ) /* (c) 1981 Bally Midway */
DRIVER( gridlee ) /* [1983 Videa] prototype - no copyright notice */
DRIVER( alienar ) /* (c) 1985 Duncan Brown */
DRIVER( carpolo ) /* (c) 1977 Exidy */
DRIVER( sidetrac ) /* (c) 1979 Exidy */
DRIVER( targ ) /* (c) 1980 Exidy */
DRIVER( spectar ) /* (c) 1980 Exidy */
DRIVER( teetert ) /* (c) 1982 Exidy */
DRIVER( hardhat ) /* (c) 1982 */
DRIVER( fax ) /* (c) 1983 */
DRIVER( fax2 ) /* (c) 1983 */
DRIVER( circus ) /* (c) 1977 Exidy */
DRIVER( robotbwl ) /* (c) 197? Exidy */
DRIVER( crash ) /* (c) 1979 Exidy */
DRIVER( ripcord ) /* (c) 1979 Exidy */
DRIVER( starfire ) /* (c) 1979 Exidy */
DRIVER( starfirea ) /* (c) 1979 Exidy */
DRIVER( fireone ) /* (c) 1979 Exidy */
DRIVER( starfir2 ) /* (c) 1979 Exidy */
DRIVER( victory ) /* (c) 1982 */
DRIVER( victorba ) /* (c) 1982 */
DRIVER( topgunnr ) /* (c) 1986 */
DRIVER( looping ) /* (c) 1982 Video Games GMBH */
DRIVER( supertnk ) /* (c) 1981 VIDEO GAMES GmbH, W.-GERMANY */
DRIVER( wrally ) /* (c) 1993 - Ref 930705 */
#endif /* DRIVER_RECURSIVE */

68
src/mame/tiny.lst Normal file
View File

@ -0,0 +1,68 @@
/******************************************************************************
tiny.lst
List of all enabled drivers in the system. This file is parsed by
makelist.exe, sorted, and output as C code describing the drivers.
****************************************************************************
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.
******************************************************************************/
robby // (c) 1981 Bally Midway
gridlee // [1983 Videa] prototype - no copyright notice
alienar // (c) 1985 Duncan Brown
carpolo // (c) 1977 Exidy
sidetrac // (c) 1979 Exidy
targ // (c) 1980 Exidy
spectar // (c) 1980 Exidy
teetert // (c) 1982 Exidy
hardhat // (c) 1982
fax // (c) 1983
fax2 // (c) 1983
circus // (c) 1977 Exidy
robotbwl // (c) 197? Exidy
crash // (c) 1979 Exidy
ripcord // (c) 1979 Exidy
starfire // (c) 1979 Exidy
starfirea // (c) 1979 Exidy
fireone // (c) 1979 Exidy
starfir2 // (c) 1979 Exidy
victory // (c) 1982
victorba // (c) 1982
topgunnr // (c) 1986
looping // (c) 1982 Video Games GMBH
supertnk // (c) 1981 VIDEO GAMES GmbH, W.-GERMANY
wrally // (c) 1993 - Ref 930705

View File

@ -71,7 +71,6 @@ SOUNDS += CEM3394
#-------------------------------------------------
DRVLIBS = \
$(MAMEOBJ)/tiny.o \
$(EMUDRIVERS)/emudummy.o \
$(MACHINE)/ticket.o \
$(DRIVERS)/carpolo.o $(MACHINE)/carpolo.o $(VIDEO)/carpolo.o \

View File

@ -723,7 +723,7 @@ void osd_work_item_release(osd_work_item *item);
***************************************************************************/
/*-----------------------------------------------------------------------------
osd_malloc: allocate memory that
osd_malloc: allocate memory
Parameters:
@ -741,6 +741,26 @@ void osd_work_item_release(osd_work_item *item);
void *osd_malloc(size_t size);
/*-----------------------------------------------------------------------------
osd_malloc_array: allocate memory, hinting tha this memory contains an
array
Parameters:
size - the number of bytes to allocate
Return value:
a pointer to the allocated memory
Notes:
This is just a hook to do OS-specific allocation trickery.
It can be safely written as a wrapper to malloc().
-----------------------------------------------------------------------------*/
void *osd_malloc_array(size_t size);
/*-----------------------------------------------------------------------------
osd_free: free memory allocated by osd_malloc

View File

@ -93,10 +93,12 @@ static INT32 keyboard_get_state(void *device_internal, void *item_internal);
int main(int argc, char *argv[])
{
// cli_execute does the heavy lifting; if we have osd-specific options, we
// would pass them as the third parameter here
// cli_frontend does the heavy lifting; if we have osd-specific options, we
// create a derivative of cli_options and add our own
cli_options options;
mini_osd_interface osd;
return cli_execute(argc, argv, osd, NULL);
cli_frontend frontend(options, osd);
return frontend.execute(argc, argv);
}
@ -135,7 +137,7 @@ void mini_osd_interface::init(running_machine &machine)
// initialize the input system by adding devices
// let's pretend like we have a keyboard device
keyboard_device = input_device_add(&machine, DEVICE_CLASS_KEYBOARD, "Keyboard", NULL);
keyboard_device = input_device_add(machine, DEVICE_CLASS_KEYBOARD, "Keyboard", NULL);
if (keyboard_device == NULL)
fatalerror("Error creating keyboard device");

View File

@ -44,7 +44,7 @@
//============================================================
// osd_alloc
// osd_malloc
//============================================================
void *osd_malloc(size_t size)
@ -53,6 +53,16 @@ void *osd_malloc(size_t size)
}
//============================================================
// osd_malloc_array
//============================================================
void *osd_malloc_array(size_t size)
{
return malloc(size);
}
//============================================================
// osd_free
//============================================================

View File

@ -964,7 +964,7 @@ static texture_info *texture_create(sdl_window_info *window, const render_texinf
if ( (texture->copyinfo->func != NULL) && (texture->sdl_access == SDL_TEXTUREACCESS_STATIC))
{
texture->pixels = osd_malloc(texture->setup.rotwidth * texture->setup.rotheight * texture->copyinfo->dst_bpp);
texture->pixels = osd_malloc_array(texture->setup.rotwidth * texture->setup.rotheight * texture->copyinfo->dst_bpp);
texture->pixels_own=TRUE;
}
/* add us to the texture list */

View File

@ -67,7 +67,7 @@ struct _osd_directory
static char *build_full_path(const char *path, const char *file)
{
char *ret = (char *) osd_malloc(strlen(path)+strlen(file)+2);
char *ret = (char *) osd_malloc_array(strlen(path)+strlen(file)+2);
char *p = ret;
strcpy(p, path);
@ -126,13 +126,13 @@ osd_directory *osd_opendir(const char *dirname)
dir->fd = NULL;
}
tmpstr = (char *) osd_malloc(strlen(dirname)+1);
tmpstr = (char *) osd_malloc_array(strlen(dirname)+1);
strcpy(tmpstr, dirname);
if (tmpstr[0] == '$')
{
char *envval;
envstr = (char *) osd_malloc(strlen(tmpstr)+1);
envstr = (char *) osd_malloc_array(strlen(tmpstr)+1);
strcpy(envstr, tmpstr);
@ -149,7 +149,7 @@ osd_directory *osd_opendir(const char *dirname)
{
j = strlen(envval) + strlen(tmpstr) + 1;
osd_free(tmpstr);
tmpstr = (char *) osd_malloc(j);
tmpstr = (char *) osd_malloc_array(j);
// start with the value of $HOME
strcpy(tmpstr, envval);

View File

@ -115,7 +115,7 @@ file_error osd_open(const char *path, UINT32 openflags, osd_file **file, UINT64
tmpstr = NULL;
// allocate a file object, plus space for the converted filename
*file = (osd_file *) osd_malloc(sizeof(**file) + sizeof(char) * strlen(path));
*file = (osd_file *) osd_malloc_array(sizeof(**file) + sizeof(char) * strlen(path));
if (*file == NULL)
{
filerr = FILERR_OUT_OF_MEMORY;
@ -160,14 +160,14 @@ file_error osd_open(const char *path, UINT32 openflags, osd_file **file, UINT64
goto error;
}
tmpstr = (char *) osd_malloc(strlen((*file)->filename)+1);
tmpstr = (char *) osd_malloc_array(strlen((*file)->filename)+1);
strcpy(tmpstr, (*file)->filename);
// does path start with an environment variable?
if (tmpstr[0] == '$')
{
char *envval;
envstr = (char *) osd_malloc(strlen(tmpstr)+1);
envstr = (char *) osd_malloc_array(strlen(tmpstr)+1);
strcpy(envstr, tmpstr);
@ -184,7 +184,7 @@ file_error osd_open(const char *path, UINT32 openflags, osd_file **file, UINT64
{
j = strlen(envval) + strlen(tmpstr) + 1;
osd_free(tmpstr);
tmpstr = (char *) osd_malloc(j);
tmpstr = (char *) osd_malloc_array(j);
// start with the value of $HOME
strcpy(tmpstr, envval);

View File

@ -348,7 +348,8 @@ int main(int argc, char *argv[])
{
sdl_osd_interface osd;
sdl_options options;
res = cli_execute(options, osd, argc, argv);
cli_frontend frontend(options, osd);
res = frontend.execute(argc, argv);
}
#ifdef MALLOC_DEBUG

View File

@ -176,6 +176,20 @@ void *osd_malloc(size_t size)
}
//============================================================
// osd_malloc_array
//============================================================
void *osd_malloc_array(size_t size)
{
#ifndef MALLOC_DEBUG
return malloc(size);
#else
#error "MALLOC_DEBUG not yet supported"
#endif
}
//============================================================
// osd_free
//============================================================
@ -277,7 +291,7 @@ char *osd_get_clipboard_text(void)
length = CFDataGetLength (data_ref);
range = CFRangeMake (0,length);
result = (char *)osd_malloc (length+1);
result = (char *)osd_malloc_array (length+1);
if (result != NULL)
{
CFDataGetBytes (data_ref, range, (unsigned char *)result);
@ -330,7 +344,7 @@ osd_directory_entry *osd_stat(const char *path)
// create an osd_directory_entry; be sure to make sure that the caller can
// free all resources by just freeing the resulting osd_directory_entry
result = (osd_directory_entry *) osd_malloc(sizeof(*result) + strlen(path) + 1);
result = (osd_directory_entry *) osd_malloc_array(sizeof(*result) + strlen(path) + 1);
strcpy(((char *) result) + sizeof(*result), path);
result->name = ((char *) result) + sizeof(*result);
result->type = S_ISDIR(st.st_mode) ? ENTTYPE_DIR : ENTTYPE_FILE;
@ -367,7 +381,7 @@ file_error osd_get_full_path(char **dst, const char *path)
}
else
{
*dst = (char *)osd_malloc(strlen(path_buffer)+strlen(path)+3);
*dst = (char *)osd_malloc_array(strlen(path_buffer)+strlen(path)+3);
// if it's already a full path, just pass it through
if (path[0] == '/')

View File

@ -198,6 +198,20 @@ void *osd_malloc(size_t size)
}
//============================================================
// osd_malloc_array
//============================================================
void *osd_malloc_array(size_t size)
{
#ifndef MALLOC_DEBUG
return malloc(size);
#else
#error "MALLOC_DEBUG not yet supported"
#endif
}
//============================================================
// osd_free
//============================================================
@ -258,7 +272,7 @@ osd_directory_entry *osd_stat(const char *path)
// create an osd_directory_entry; be sure to make sure that the caller can
// free all resources by just freeing the resulting osd_directory_entry
result = (osd_directory_entry *) osd_malloc(sizeof(*result) + strlen(path)
result = (osd_directory_entry *) osd_malloc_array(sizeof(*result) + strlen(path)
1);
strcpy(((char *) result) + sizeof(*result), path);
result->name = ((char *) result) + sizeof(*result);
@ -300,7 +314,7 @@ const char *osd_get_volume_name(int idx)
file_error osd_get_full_path(char **dst, const char *path)
{
*dst = (char *)osd_malloc(CCHMAXPATH + 1);
*dst = (char *)osd_malloc_array(CCHMAXPATH + 1);
if (*dst == NULL)
return FILERR_OUT_OF_MEMORY;

View File

@ -92,6 +92,20 @@ void *osd_malloc(size_t size)
}
//============================================================
// osd_malloc_array
//============================================================
void *osd_malloc_array(size_t size)
{
#ifndef MALLOC_DEBUG
return malloc(size);
#else
#error "MALLOC_DEBUG not yet supported"
#endif
}
//============================================================
// osd_free
//============================================================
@ -207,7 +221,7 @@ char *osd_get_clipboard_text(void)
/* return a copy & free original */
if (prop != NULL)
{
result = (char *) osd_malloc(strlen((char *)prop)+1);
result = (char *) osd_malloc_array(strlen((char *)prop)+1);
strcpy(result, (char *)prop);
}
else
@ -256,7 +270,7 @@ osd_directory_entry *osd_stat(const char *path)
// create an osd_directory_entry; be sure to make sure that the caller can
// free all resources by just freeing the resulting osd_directory_entry
result = (osd_directory_entry *) osd_malloc(sizeof(*result) + strlen(path) + 1);
result = (osd_directory_entry *) osd_malloc_array(sizeof(*result) + strlen(path) + 1);
strcpy(((char *) result) + sizeof(*result), path);
result->name = ((char *) result) + sizeof(*result);
result->type = S_ISDIR(st.st_mode) ? ENTTYPE_DIR : ENTTYPE_FILE;
@ -293,7 +307,7 @@ file_error osd_get_full_path(char **dst, const char *path)
}
else
{
*dst = (char *)osd_malloc(strlen(path_buffer)+strlen(path)+3);
*dst = (char *)osd_malloc_array(strlen(path_buffer)+strlen(path)+3);
// if it's already a full path, just pass it through
if (path[0] == '/')

View File

@ -180,41 +180,54 @@ void *osd_malloc(size_t size)
#ifndef MALLOC_DEBUG
return HeapAlloc(GetProcessHeap(), 0, size);
#else
// add in space for the base pointer
// add in space for the size
size += sizeof(size_t);
// small items just come from the heap
void *result;
if (size < GUARD_PAGE_THRESH)
result = HeapAlloc(GetProcessHeap(), 0, size);
// basic objects just come from the heap
void *result = HeapAlloc(GetProcessHeap(), 0, size);
// large items get guard pages
else
{
// round the size up to a page boundary
size_t rounded_size = ((size + sizeof(void *) + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
// reserve that much memory, plus two guard pages
void *page_base = VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (page_base == NULL)
return NULL;
// now allow access to everything but the first and last pages
page_base = VirtualAlloc(reinterpret_cast<UINT8 *>(page_base) + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE);
if (page_base == NULL)
return NULL;
// work backwards from the page base to get to the block base
result = GUARD_ALIGN_START ? page_base : (reinterpret_cast<UINT8 *>(page_base) + rounded_size - size);
}
// store the page_base at the start
// store the size and return and pointer to the data afterward
*reinterpret_cast<size_t *>(result) = size;
return reinterpret_cast<UINT8 *>(result) + sizeof(size_t);
#endif
}
//============================================================
// osd_malloc_array
//============================================================
void *osd_malloc_array(size_t size)
{
#ifndef MALLOC_DEBUG
return HeapAlloc(GetProcessHeap(), 0, size);
#else
// add in space for the size
size += sizeof(size_t);
// round the size up to a page boundary
size_t rounded_size = ((size + sizeof(void *) + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
// reserve that much memory, plus two guard pages
void *page_base = VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (page_base == NULL)
return NULL;
// now allow access to everything but the first and last pages
page_base = VirtualAlloc(reinterpret_cast<UINT8 *>(page_base) + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE);
if (page_base == NULL)
return NULL;
// work backwards from the page base to get to the block base
void *result = GUARD_ALIGN_START ? page_base : (reinterpret_cast<UINT8 *>(page_base) + rounded_size - size);
// store the size at the start with a flag indicating it has a guard page
*reinterpret_cast<size_t *>(result) = size | 0x80000000;
return reinterpret_cast<UINT8 *>(result) + sizeof(size_t);
#endif
}
//============================================================
// osd_free
//============================================================
@ -226,14 +239,14 @@ void osd_free(void *ptr)
#else
size_t size = reinterpret_cast<size_t *>(ptr)[-1];
// small items just get freed
if (size < GUARD_PAGE_THRESH)
// if no guard page, just free the pointer
if ((size & 0x80000000) == 0)
HeapFree(GetProcessHeap(), 0, reinterpret_cast<UINT8 *>(ptr) - sizeof(size_t));
// large items need more care
else
{
FPTR page_base = (reinterpret_cast<FPTR>(ptr) - sizeof(size_t)) & ~(PAGE_SIZE - 1);
ULONG_PTR page_base = (reinterpret_cast<ULONG_PTR>(ptr) - sizeof(size_t)) & ~(PAGE_SIZE - 1);
VirtualFree(reinterpret_cast<void *>(page_base - PAGE_SIZE), 0, MEM_RELEASE);
}
#endif
@ -262,7 +275,7 @@ int osd_setenv(const char *name, const char *value, int overwrite)
if (osd_getenv(name) != NULL)
return 0;
}
buf = (char *) osd_malloc(strlen(name)+strlen(value)+2);
buf = (char *) osd_malloc_array(strlen(name)+strlen(value)+2);
sprintf(buf, "%s=%s", name, value);
result = putenv(buf);
@ -371,7 +384,7 @@ CHAR *astring_from_utf8(const char *utf8string)
// convert UTF-16 to "ANSI code page" string
char_count = WideCharToMultiByte(CP_ACP, 0, wstring, -1, NULL, 0, NULL, NULL);
result = (CHAR *)osd_malloc(char_count * sizeof(*result));
result = (CHAR *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
WideCharToMultiByte(CP_ACP, 0, wstring, -1, result, char_count, NULL, NULL);
@ -389,7 +402,7 @@ WCHAR *wstring_from_utf8(const char *utf8string)
// convert MAME string (UTF-8) to UTF-16
char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0);
result = (WCHAR *)osd_malloc(char_count * sizeof(*result));
result = (WCHAR *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, result, char_count);

View File

@ -183,7 +183,7 @@ osd_work_queue *osd_work_queue_alloc(int flags)
queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
// allocate memory for thread array (+1 to count the calling thread)
queue->thread = (work_thread_info *)osd_malloc((queue->threads + 1) * sizeof(queue->thread[0]));
queue->thread = (work_thread_info *)osd_malloc_array((queue->threads + 1) * sizeof(queue->thread[0]));
if (queue->thread == NULL)
goto error;
memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0]));

View File

@ -38,7 +38,7 @@ char *utf8_from_astring(const CHAR *astring)
// convert UTF-16 to MAME string (UTF-8)
char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL);
result = (CHAR *)osd_malloc(char_count * sizeof(*result));
result = (CHAR *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL);
@ -56,7 +56,7 @@ char *utf8_from_wstring(const WCHAR *wstring)
// convert UTF-16 to MAME string (UTF-8)
char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL);
result = (char *)osd_malloc(char_count * sizeof(*result));
result = (char *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL);

View File

@ -65,7 +65,7 @@ CHAR *astring_from_utf8(const char *utf8string)
// convert UTF-16 to "ANSI code page" string
char_count = WideCharToMultiByte(CP_ACP, 0, wstring, -1, NULL, 0, NULL, NULL);
result = (CHAR *)osd_malloc(char_count * sizeof(*result));
result = (CHAR *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
WideCharToMultiByte(CP_ACP, 0, wstring, -1, result, char_count, NULL, NULL);
@ -90,7 +90,7 @@ char *utf8_from_astring(const CHAR *astring)
// convert UTF-16 to MAME string (UTF-8)
char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL);
result = (CHAR *)osd_malloc(char_count * sizeof(*result));
result = (CHAR *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL);
@ -109,7 +109,7 @@ WCHAR *wstring_from_utf8(const char *utf8string)
// convert MAME string (UTF-8) to UTF-16
char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0);
result = (WCHAR *)osd_malloc(char_count * sizeof(*result));
result = (WCHAR *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, result, char_count);
@ -128,7 +128,7 @@ char *utf8_from_wstring(const WCHAR *wstring)
// convert UTF-16 to MAME string (UTF-8)
char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL);
result = (char *)osd_malloc(char_count * sizeof(*result));
result = (char *)osd_malloc_array(char_count * sizeof(*result));
if (result != NULL)
WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL);

View File

@ -1,618 +0,0 @@
//============================================================
//
// winalloc.c - Win32 memory allocation routines
//
//============================================================
//
// 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
// DAMAGE (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.
//
//============================================================
// standard windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
// MAME headers
#include "osdcore.h"
// undefine any redefines we have in the prefix
#undef malloc
#undef calloc
#undef realloc
//============================================================
// CONSTANTS
//============================================================
#define OVERRIDE_STANDARD_CALLS (0)
#define PAGE_SIZE 4096
#define COOKIE_VAL 0x11335577
// set this to 1 to align memory blocks to the start of a page;
// otherwise, they are aligned to the end, thus catching array
// overruns
#define ALIGN_START 0
// set this to 1 to record all mallocs and frees in the logfile
#define LOG_CALLS 0
#if LOG_CALLS
#define LOG(x) do { if (LOG_CALLS) logerror x; } while (0)
void CLIB_DECL logerror(const char *text,...);
#else
#define LOG(x)
#endif
//============================================================
// TYPEDEFS
//============================================================
typedef struct _memory_entry memory_entry;
struct _memory_entry
{
memory_entry * next;
memory_entry * prev;
size_t size;
void * base;
const char * file;
int line;
int id;
};
//============================================================
// GLOBAL VARIABLES
//============================================================
int winalloc_in_main_code = FALSE;
//============================================================
// LOCAL VARIABLES
//============================================================
static memory_entry *alloc_list;
static memory_entry *free_list;
static int current_id;
static CRITICAL_SECTION memory_lock;
static UINT8 global_init_done = FALSE;
static UINT8 use_malloc_tracking = FALSE;
//============================================================
// PROTOTYPES
//============================================================
static memory_entry *allocate_entry(void);
static memory_entry *find_entry(void *pointer);
static void free_entry(memory_entry *entry);
static void global_init(void);
//============================================================
// INLINES
//============================================================
INLINE void global_init_if_not_done(void)
{
if (!global_init_done)
{
global_init_done = TRUE;
global_init();
}
}
INLINE void memory_lock_acquire(void)
{
EnterCriticalSection(&memory_lock);
}
INLINE void memory_lock_release(void)
{
LeaveCriticalSection(&memory_lock);
}
//============================================================
// IMPLEMENTATION
//============================================================
//============================================================
// malloc_file_line - debugging version of malloc which
// accepts filename and line number
//============================================================
void *malloc_file_line(size_t size, const char *file, int line)
{
UINT8 *block_base;
int id = current_id++;
// perform global intialization if not already done
global_init_if_not_done();
// only proceed if enabled
if (use_malloc_tracking)
{
UINT8 *page_base;
size_t rounded_size;
memory_entry *entry;
// round the size up to a page boundary
rounded_size = ((size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
// reserve that much memory, plus two guard pages
page_base = (UINT8 *)VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (page_base == NULL)
return NULL;
// now allow access to everything but the first and last pages
page_base = (UINT8 *)VirtualAlloc(page_base + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE);
if (page_base == NULL)
return NULL;
// work backwards from the page base to get to the block base
if (ALIGN_START)
block_base = page_base;
else
block_base = page_base + rounded_size - size;
// fill in the entry
entry = allocate_entry();
entry->size = size;
entry->base = block_base;
entry->file = file;
entry->line = line;
entry->id = id;
}
else
{
block_base = (UINT8 *)GlobalAlloc(GMEM_FIXED, size);
}
// logging
if (file != NULL)
LOG(("malloc #%06d size = %d (%s:%d)\n", id, size, file, line));
else
LOG(("malloc #%06d size = %d\n", id, size));
return block_base;
}
//============================================================
// malloc - override for the malloc() function
//============================================================
#if OVERRIDE_STANDARD_CALLS
void *CLIB_DECL malloc(size_t size)
{
return malloc_file_line(size, NULL, 0);
}
#endif
//============================================================
// calloc_file_line - debugging version of calloc which
// accepts filename and line number
//============================================================
void *calloc_file_line(size_t size, size_t count, const char *file, int line)
{
// first allocate the memory
void *memory = malloc_file_line(size * count, file, line);
if (memory == NULL)
return NULL;
// then memset it
memset(memory, 0, size * count);
return memory;
}
//============================================================
// calloc - override for the calloc() function
//============================================================
#if OVERRIDE_STANDARD_CALLS
void *CLIB_DECL calloc(size_t size, size_t count)
{
return calloc_file_line(size, count, NULL, 0);
}
#endif
//============================================================
// _calloc_crt - override for the _calloc_crt() function,
// which is called by beginthreadex
//============================================================
#if OVERRIDE_STANDARD_CALLS
void *CLIB_DECL _calloc_crt(size_t size, size_t count)
{
return calloc_file_line(size, count, NULL, 0);
}
#endif
//============================================================
// realloc_file_line - debugging version of realloc which
// accepts filename and line number
//============================================================
void *realloc_file_line(void *memory, size_t size, const char *file, int line)
{
void *newmemory = NULL;
// perform global intialization if not already done
global_init_if_not_done();
// only proceed if enabled
if (use_malloc_tracking)
{
// if size is non-zero, we need to reallocate memory
if (size != 0)
{
// allocate space for the new amount
newmemory = malloc_file_line(size, file, line);
if (newmemory == NULL)
return NULL;
// if we have an old pointer, copy it
if (memory != NULL)
{
memory_entry *entry = find_entry(memory);
if (entry == NULL)
{
if (winalloc_in_main_code)
{
fprintf(stderr, "Error: realloc a non-existant block (%s:%d)\n", file, line);
osd_break_into_debugger("Error: realloc a non-existant block\n");
}
}
else
memcpy(newmemory, memory, (size < entry->size) ? size : entry->size);
}
}
// if we have an original pointer, free it
if (memory != NULL)
free(memory);
}
else
{
if (memory != NULL)
newmemory = (void *) GlobalReAlloc(memory, size, GMEM_MOVEABLE);
else
newmemory = (void *) GlobalAlloc(GMEM_FIXED, size);
}
return newmemory;
}
//============================================================
// realloc - override for the realloc() function
//============================================================
#if OVERRIDE_STANDARD_CALLS
void *CLIB_DECL realloc(void *memory, size_t size)
{
return realloc_file_line(memory, size, NULL, 0);
}
#endif
//============================================================
// free_file_line - debugging version of free which
// accepts filename and line number
//============================================================
void CLIB_DECL free_file_line(void *memory, const char *file, int line)
{
memory_entry *entry;
// allow NULL frees
if (memory == NULL)
return;
// only proceed if enabled
if (use_malloc_tracking)
{
// error if no entry found
entry = find_entry(memory);
if (entry == NULL)
{
if (winalloc_in_main_code)
{
fprintf(stderr, "Error: free a non-existant block\n");
osd_break_into_debugger("Error: free a non-existant block");
}
return;
}
free_entry(entry);
// free the memory
VirtualFree((UINT8 *)memory - ((size_t)memory & (PAGE_SIZE-1)) - PAGE_SIZE, 0, MEM_RELEASE);
LOG(("free #%06d size = %d\n", entry->id, entry->size));
}
else
{
GlobalFree(memory);
}
}
//============================================================
// free - override for the free() function
//============================================================
#if OVERRIDE_STANDARD_CALLS
void CLIB_DECL free(void *memory)
{
free_file_line(memory, NULL, 0);
}
#endif
//============================================================
// _msize - internal MSVC routine that returns the size of
// a memory block
//============================================================
#if OVERRIDE_STANDARD_CALLS
size_t CLIB_DECL _msize(void *memory)
{
size_t result;
// only proceed if enabled
if (use_malloc_tracking)
{
memory_entry *entry = find_entry(memory);
if (entry == NULL)
{
if (winalloc_in_main_code)
{
fprintf(stderr, "Error: msize a non-existant block\n");
osd_break_into_debugger("Error: msize a non-existant block");
}
return 0;
}
result = entry->size;
}
else
{
result = GlobalSize(memory);
}
return result;
}
#endif
//============================================================
// check_unfreed_mem - called from the exit path of any
// code that wants to check for unfreed memory
//============================================================
void check_unfreed_mem(void)
{
memory_entry *entry;
int total = 0;
// only valid if we are tracking
if (use_malloc_tracking)
{
memory_lock_acquire();
// check for leaked memory
for (entry = alloc_list; entry; entry = entry->next)
if (entry->file != NULL)
{
if (total == 0)
fprintf(stderr, "--- memory leak warning ---\n");
total += entry->size;
fprintf(stderr, "allocation #%06d, %d bytes (%s:%d)\n", entry->id, entry->size, entry->file, (int)entry->line);
}
memory_lock_release();
if (total > 0)
fprintf(stderr, "a total of %d bytes were not free()'d\n", total);
}
}
//============================================================
// allocate_entry - allocate a new entry and link it into
// the list of allocated memory
//============================================================
static memory_entry *allocate_entry(void)
{
memory_entry *entry;
// always take the lock when allocating
memory_lock_acquire();
// if we're out of entries, allocate some more
if (free_list == NULL)
{
int entries_per_page = PAGE_SIZE / sizeof(memory_entry);
// allocate a new pages' worth of entry
entry = (memory_entry *)VirtualAlloc(NULL, PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (entry == NULL)
{
memory_lock_release();
fprintf(stderr, "Out of memory for malloc tracking!\n");
exit(1);
}
// add all the entries to the list
while (entries_per_page--)
{
entry->next = free_list;
free_list = entry;
entry++;
}
}
// grab a free list entry
entry = free_list;
free_list = free_list->next;
// add ourselves to the alloc list
entry->next = alloc_list;
if (entry->next)
entry->next->prev = entry;
entry->prev = NULL;
alloc_list = entry;
// release the lock when finished
memory_lock_release();
return entry;
}
//============================================================
// find_entry - find a memory_object entry in the list that
// contains the given pointer
//============================================================
static memory_entry *find_entry(void *pointer)
{
memory_entry *entry;
// scan the list looking for a matching base
if (pointer)
{
memory_lock_acquire();
for (entry = alloc_list; entry; entry = entry->next)
if (entry->base == pointer)
break;
memory_lock_release();
return entry;
}
return NULL;
}
//============================================================
// free_entry - free a memory_entry object
//============================================================
static void free_entry(memory_entry *entry)
{
memory_lock_acquire();
// remove ourselves from the alloc list
if (entry->prev)
entry->prev->next = entry->next;
else
alloc_list = entry->next;
if (entry->next)
entry->next->prev = entry->prev;
// add ourself to the free list
entry->next = free_list;
free_list = entry;
memory_lock_release();
}
//============================================================
// global_init - global initialization of memory variables
//============================================================
static void global_init(void)
{
TCHAR *envstring;
// create the memory lock
InitializeCriticalSection(&memory_lock);
// determine if we enabled by default
if (win_is_gui_application()) {
use_malloc_tracking = FALSE;
} else {
use_malloc_tracking = TRUE;
}
// now allow overrides by the environment
envstring = _tgetenv(_T("OSDDEBUGMALLOC"));
if (envstring != NULL)
use_malloc_tracking = (_ttoi(envstring) != 0);
#ifdef PTR64
// 64-bit builds also can allocate everything under 4GB, unless disabled
envstring = _tgetenv(_T("OSDDEBUG4GB"));
if (envstring == NULL || _ttoi(envstring) != 0)
{
INT8 allocshift;
// loop from 256MB down to 4k (page size)
for (allocshift = 8 + 20; allocshift >= 12; allocshift--)
{
// keep allocating address space at that size until we get something >4gb
while ((UINT64)VirtualAlloc(NULL, (UINT64)1 << allocshift, MEM_RESERVE, PAGE_NOACCESS) < ((UINT64)1 << 32)) ;
}
// loop from 64k down
for (allocshift = 6 + 10; allocshift >= 1; allocshift--)
{
// keep allocating memory until we get something >4gb
while ((UINT64)GlobalAlloc(GMEM_FIXED, (UINT64)1 << allocshift) < ((UINT64)1 << 32)) ;
}
}
#endif
}

View File

@ -418,7 +418,8 @@ int main(int argc, char *argv[])
{
windows_options options;
windows_osd_interface osd;
result = cli_execute(options, osd, argc, argv);
cli_frontend frontend(options, osd);
frontend.execute(argc, argv);
}
// free symbols

View File

@ -45,7 +45,7 @@
#include <tchar.h>
// MAME headers
#include "emucore.h"
#include "osdcore.h"
// MAMEOS headers
#include "winutf8.h"
@ -60,9 +60,6 @@
// presumed size of a page of memory
#define PAGE_SIZE 4096
// allocations this size and larger get guard pages
#define GUARD_PAGE_THRESH 32
// align allocations to start or end of the page?
#define GUARD_ALIGN_START 0
@ -85,41 +82,54 @@ void *osd_malloc(size_t size)
#ifndef MALLOC_DEBUG
return HeapAlloc(GetProcessHeap(), 0, size);
#else
// add in space for the base pointer
// add in space for the size
size += sizeof(size_t);
// small items just come from the heap
void *result;
if (size < GUARD_PAGE_THRESH)
result = HeapAlloc(GetProcessHeap(), 0, size);
// basic objects just come from the heap
void *result = HeapAlloc(GetProcessHeap(), 0, size);
// large items get guard pages
else
{
// round the size up to a page boundary
size_t rounded_size = ((size + sizeof(void *) + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
// reserve that much memory, plus two guard pages
void *page_base = VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (page_base == NULL)
return NULL;
// now allow access to everything but the first and last pages
page_base = VirtualAlloc(reinterpret_cast<UINT8 *>(page_base) + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE);
if (page_base == NULL)
return NULL;
// work backwards from the page base to get to the block base
result = GUARD_ALIGN_START ? page_base : (reinterpret_cast<UINT8 *>(page_base) + rounded_size - size);
}
// store the page_base at the start
// store the size and return and pointer to the data afterward
*reinterpret_cast<size_t *>(result) = size;
return reinterpret_cast<UINT8 *>(result) + sizeof(size_t);
#endif
}
//============================================================
// osd_malloc_array
//============================================================
void *osd_malloc_array(size_t size)
{
#ifndef MALLOC_DEBUG
return HeapAlloc(GetProcessHeap(), 0, size);
#else
// add in space for the size
size += sizeof(size_t);
// round the size up to a page boundary
size_t rounded_size = ((size + sizeof(void *) + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
// reserve that much memory, plus two guard pages
void *page_base = VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (page_base == NULL)
return NULL;
// now allow access to everything but the first and last pages
page_base = VirtualAlloc(reinterpret_cast<UINT8 *>(page_base) + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE);
if (page_base == NULL)
return NULL;
// work backwards from the page base to get to the block base
void *result = GUARD_ALIGN_START ? page_base : (reinterpret_cast<UINT8 *>(page_base) + rounded_size - size);
// store the size at the start with a flag indicating it has a guard page
*reinterpret_cast<size_t *>(result) = size | 0x80000000;
return reinterpret_cast<UINT8 *>(result) + sizeof(size_t);
#endif
}
//============================================================
// osd_free
//============================================================
@ -131,14 +141,14 @@ void osd_free(void *ptr)
#else
size_t size = reinterpret_cast<size_t *>(ptr)[-1];
// small items just get freed
if (size < GUARD_PAGE_THRESH)
// if no guard page, just free the pointer
if ((size & 0x80000000) == 0)
HeapFree(GetProcessHeap(), 0, reinterpret_cast<UINT8 *>(ptr) - sizeof(size_t));
// large items need more care
else
{
FPTR page_base = (reinterpret_cast<FPTR>(ptr) - sizeof(size_t)) & ~(PAGE_SIZE - 1);
ULONG_PTR page_base = (reinterpret_cast<ULONG_PTR>(ptr) - sizeof(size_t)) & ~(PAGE_SIZE - 1);
VirtualFree(reinterpret_cast<void *>(page_base - PAGE_SIZE), 0, MEM_RELEASE);
}
#endif