mirror of
https://github.com/holub/mame
synced 2025-05-10 16:21:42 +03:00

Use standard uint64_t, uint32_t, uint16_t or uint8_t instead of UINT64, UINT32, UINT16 or UINT8 also use standard int64_t, int32_t, int16_t or int8_t instead of INT64, INT32, INT16 or INT8
882 lines
25 KiB
C++
882 lines
25 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Wilbert Pol
|
|
/***************************************************************************
|
|
|
|
softlist.cpp
|
|
|
|
Software list construction helpers.
|
|
|
|
***************************************************************************/
|
|
|
|
#include <regex>
|
|
|
|
#include "softlist.h"
|
|
#include "hash.h"
|
|
#include "expat.h"
|
|
|
|
|
|
//**************************************************************************
|
|
// STATIC VARIABLES
|
|
//**************************************************************************
|
|
|
|
static std::regex s_potenial_softlist_regex("\\w+(\\:\\w+)*");
|
|
|
|
|
|
//**************************************************************************
|
|
// FEATURE LIST ITEM
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// feature_list_item - constructor
|
|
//-------------------------------------------------
|
|
|
|
feature_list_item::feature_list_item(const std::string &name, const std::string &value)
|
|
: m_next(nullptr),
|
|
m_name(name),
|
|
m_value(value)
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// feature_list_item - constructor
|
|
//-------------------------------------------------
|
|
|
|
feature_list_item::feature_list_item(std::string &&name, std::string &&value)
|
|
: m_next(nullptr),
|
|
m_name(std::move(name)),
|
|
m_value(std::move(value))
|
|
{
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
// SOFTWARE PART
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// software_part - constructor
|
|
//-------------------------------------------------
|
|
|
|
software_part::software_part(software_info &info, std::string &&name, std::string &&interface)
|
|
: m_next(nullptr),
|
|
m_info(info),
|
|
m_name(std::move(name)),
|
|
m_interface(std::move(interface))
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// feature - return the value of the given
|
|
// feature, if specified
|
|
//-------------------------------------------------
|
|
|
|
const char *software_part::feature(const std::string &feature_name) const
|
|
{
|
|
// scan the feature list for an entry matching feature_name and return the value
|
|
for (const feature_list_item &feature : m_featurelist)
|
|
if (feature.name() == feature_name)
|
|
return feature.value().c_str();
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// matches_interface - determine if we match
|
|
// an interface in the provided list
|
|
//-------------------------------------------------
|
|
|
|
bool software_part::matches_interface(const char *interface_list) const
|
|
{
|
|
// if we have no interface, then we match by default
|
|
if (m_interface.empty())
|
|
return true;
|
|
|
|
// copy the comma-delimited interface list and ensure it ends with a final comma
|
|
std::string interfaces = std::string(interface_list).append(",");
|
|
|
|
// then add a comma to the end of our interface and return true if we find it in the list string
|
|
std::string our_interface = std::string(m_interface).append(",");
|
|
return (interfaces.find(our_interface) != -1);
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
// SOFTWARE INFO
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// software_info - constructor
|
|
//-------------------------------------------------
|
|
|
|
software_info::software_info(std::string &&name, std::string &&parent, const std::string &supported)
|
|
: m_supported(SOFTWARE_SUPPORTED_YES),
|
|
m_shortname(std::move(name)),
|
|
m_parentname(std::move(parent))
|
|
{
|
|
// handle the supported flag if provided
|
|
if (supported == "partial")
|
|
m_supported = SOFTWARE_SUPPORTED_PARTIAL;
|
|
else if (supported == "no")
|
|
m_supported = SOFTWARE_SUPPORTED_NO;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// find_part - find a part by name with an
|
|
// optional interface match
|
|
//-------------------------------------------------
|
|
|
|
const software_part *software_info::find_part(const std::string &part_name, const char *interface) const
|
|
{
|
|
// look for the part by name and match against the interface if provided
|
|
auto iter = std::find_if(
|
|
m_partdata.begin(),
|
|
m_partdata.end(),
|
|
[&](const software_part &part)
|
|
{
|
|
// try to match the part_name (or all parts if part_name is empty), and then try
|
|
// to match the interface (or all interfaces if interface is nullptr)
|
|
return (part_name.empty() || part_name == part.name())
|
|
&& (interface == nullptr || part.matches_interface(interface));
|
|
});
|
|
|
|
return iter != m_partdata.end()
|
|
? &*iter
|
|
: nullptr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// has_multiple_parts - return true if we have
|
|
// more than one part matching the given
|
|
// interface
|
|
//-------------------------------------------------
|
|
|
|
bool software_info::has_multiple_parts(const char *interface) const
|
|
{
|
|
int count = 0;
|
|
|
|
// increment the count for each match and stop if we hit more than 1
|
|
for (const software_part &part : m_partdata)
|
|
if (part.matches_interface(interface))
|
|
if (++count > 1)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
// SOFTWARE LIST PARSER
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// softlist_parser - constructor
|
|
//-------------------------------------------------
|
|
|
|
softlist_parser::softlist_parser(util::core_file &file, const std::string &filename, std::string &description, std::list<software_info> &infolist, std::ostringstream &errors)
|
|
: m_file(file),
|
|
m_filename(filename),
|
|
m_infolist(infolist),
|
|
m_errors(errors),
|
|
m_done(false),
|
|
m_description(description),
|
|
m_data_accum_expected(false),
|
|
m_current_info(nullptr),
|
|
m_current_part(nullptr),
|
|
m_pos(POS_ROOT)
|
|
{
|
|
// create the parser
|
|
m_parser = XML_ParserCreate_MM(nullptr, nullptr, nullptr);
|
|
if (m_parser == nullptr)
|
|
throw std::bad_alloc();
|
|
|
|
// set the handlers
|
|
XML_SetUserData(m_parser, this);
|
|
XML_SetElementHandler(m_parser, &softlist_parser::start_handler, &softlist_parser::end_handler);
|
|
XML_SetCharacterDataHandler(m_parser, &softlist_parser::data_handler);
|
|
|
|
// parse the file contents
|
|
m_file.seek(0, SEEK_SET);
|
|
char buffer[1024];
|
|
while (!m_done)
|
|
{
|
|
uint32_t length = m_file.read(buffer, sizeof(buffer));
|
|
m_done = m_file.eof();
|
|
if (XML_Parse(m_parser, buffer, length, m_done) == XML_STATUS_ERROR)
|
|
{
|
|
parse_error("%s", parser_error());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// free the parser
|
|
XML_ParserFree(m_parser);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// line
|
|
//-------------------------------------------------
|
|
|
|
int softlist_parser::line() const
|
|
{
|
|
return XML_GetCurrentLineNumber(m_parser);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// column
|
|
//-------------------------------------------------
|
|
|
|
int softlist_parser::column() const
|
|
{
|
|
return XML_GetCurrentColumnNumber(m_parser);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parser_error
|
|
//-------------------------------------------------
|
|
|
|
const char *softlist_parser::parser_error() const
|
|
{
|
|
return XML_ErrorString(XML_GetErrorCode(m_parser));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_error - append a parsing error with
|
|
// filename, line and column information
|
|
//-------------------------------------------------
|
|
|
|
template <typename Format, typename... Params>
|
|
inline void softlist_parser::parse_error(Format &&fmt, Params &&... args)
|
|
{
|
|
// always start with filename(line.column):
|
|
util::stream_format(m_errors, "%s(%d.%d): ", m_filename, line(), column());
|
|
|
|
// append the remainder of the string
|
|
util::stream_format(m_errors, std::forward<Format>(fmt), std::forward<Params>(args)...);
|
|
|
|
// append a newline at the end
|
|
m_errors.put('\n');
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_attributes - helper to parse a set of
|
|
// attributes into a list of strings
|
|
//-------------------------------------------------
|
|
|
|
template <typename T>
|
|
std::vector<std::string> softlist_parser::parse_attributes(const char **attributes, const T &attrlist)
|
|
{
|
|
std::vector<std::string> outlist(std::distance(std::begin(attrlist), std::end(attrlist)));
|
|
|
|
// iterate over attribute/value pairs
|
|
for( ; attributes[0]; attributes += 2)
|
|
{
|
|
auto iter = std::begin(attrlist);
|
|
|
|
// look for a match among the attributes provided
|
|
for (std::size_t index = 0; iter != std::end(attrlist); ++index, ++iter)
|
|
{
|
|
if (strcmp(attributes[0], *iter) == 0)
|
|
{
|
|
// if found, set the corresponding output entry to the value
|
|
outlist[index] = attributes[1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if not found, report an unknown attribute
|
|
if (iter == std::end(attrlist))
|
|
unknown_attribute(attributes[0]);
|
|
}
|
|
|
|
return outlist;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_name_and_value - helper to parse "name"
|
|
// and "value" attribute pairs (allowing the
|
|
// latter to be defined as an empty string)
|
|
//-------------------------------------------------
|
|
|
|
bool softlist_parser::parse_name_and_value(const char **attributes, std::string &name, std::string &value)
|
|
{
|
|
bool found_value = false;
|
|
|
|
// iterate over attribute/value pairs
|
|
for( ; attributes[0]; attributes += 2)
|
|
{
|
|
// if found, set the corresponding output entry to the value
|
|
if (strcmp(attributes[0], "name") == 0)
|
|
{
|
|
name = attributes[1];
|
|
}
|
|
|
|
else if (strcmp(attributes[0], "value") == 0)
|
|
{
|
|
value = attributes[1];
|
|
found_value = true;
|
|
}
|
|
|
|
// if not found, report an unknown attribute
|
|
else
|
|
unknown_attribute(attributes[0]);
|
|
}
|
|
|
|
return !name.empty() && found_value;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// add_rom_entry - append a new ROM entry to the
|
|
// current part's list
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::add_rom_entry(std::string &&name, std::string &&hashdata, uint32_t offset, uint32_t length, uint32_t flags)
|
|
{
|
|
// get the current part
|
|
if (m_current_part == nullptr)
|
|
{
|
|
parse_error("ROM entry added in invalid context");
|
|
return;
|
|
}
|
|
|
|
// make sure we don't add duplicate regions
|
|
if (!name.empty() && (flags & ROMENTRY_TYPEMASK) == ROMENTRYTYPE_REGION)
|
|
{
|
|
for (auto &elem : m_current_part->m_romdata)
|
|
if (elem.name() == name)
|
|
parse_error("Duplicated dataarea %s in software %s", name, infoname());
|
|
}
|
|
|
|
m_current_part->m_romdata.emplace_back(std::move(name), std::move(hashdata), offset, length, flags);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// start_handler - expat handler for tag start
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::start_handler(void *data, const char *tagname, const char **attributes)
|
|
{
|
|
// switch off the current state
|
|
softlist_parser *state = reinterpret_cast<softlist_parser *>(data);
|
|
switch (state->m_pos)
|
|
{
|
|
case POS_ROOT:
|
|
state->parse_root_start(tagname, attributes);
|
|
break;
|
|
|
|
case POS_MAIN:
|
|
state->parse_main_start(tagname, attributes);
|
|
break;
|
|
|
|
case POS_SOFT:
|
|
state->parse_soft_start(tagname, attributes);
|
|
break;
|
|
|
|
case POS_PART:
|
|
state->parse_part_start(tagname, attributes);
|
|
break;
|
|
|
|
case POS_DATA:
|
|
state->parse_data_start(tagname, attributes);
|
|
break;
|
|
}
|
|
|
|
// increment the state since this is a tag start
|
|
state->m_pos = parse_position(state->m_pos + 1);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// end_handler - handle end-of-tag post-processing
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::end_handler(void *data, const char *name)
|
|
{
|
|
// reset the text destination and bump the position down
|
|
softlist_parser *state = reinterpret_cast<softlist_parser *>(data);
|
|
state->m_pos = parse_position(state->m_pos - 1);
|
|
|
|
// switch off of the new position
|
|
switch (state->m_pos)
|
|
{
|
|
case POS_ROOT:
|
|
break;
|
|
|
|
case POS_MAIN:
|
|
state->m_current_info = nullptr;
|
|
break;
|
|
|
|
case POS_SOFT:
|
|
state->parse_soft_end(name);
|
|
state->m_current_part = nullptr;
|
|
break;
|
|
|
|
case POS_PART:
|
|
break;
|
|
|
|
case POS_DATA:
|
|
break;
|
|
}
|
|
|
|
// stop accumulating
|
|
state->m_data_accum_expected = false;
|
|
state->m_data_accum.clear();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// data_handler - expat data handler
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::data_handler(void *data, const char *s, int len)
|
|
{
|
|
softlist_parser *state = reinterpret_cast<softlist_parser *>(data);
|
|
|
|
// if we have an std::string to accumulate data in, do it
|
|
if (state->m_data_accum_expected)
|
|
state->m_data_accum.append(s, len);
|
|
|
|
// otherwise, report an error if the data is non-blank
|
|
else
|
|
for (int i = 0; i < len; i++)
|
|
if (!isspace(s[i]))
|
|
{
|
|
state->parse_error("Unexpected content");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_root_start - handle tag start at the root
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::parse_root_start(const char *tagname, const char **attributes)
|
|
{
|
|
// <softwarelist name='' description=''>
|
|
if (strcmp(tagname, "softwarelist") == 0)
|
|
{
|
|
static const char *attrnames[] = { "name", "description" };
|
|
const auto attrvalues = parse_attributes(attributes, attrnames);
|
|
|
|
if (!attrvalues[1].empty())
|
|
m_description = attrvalues[1];
|
|
}
|
|
else
|
|
unknown_tag(tagname);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_main_start - handle tag start within
|
|
// a softwarelist tag
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::parse_main_start(const char *tagname, const char **attributes)
|
|
{
|
|
// <software name='' cloneof='' supported=''>
|
|
if (strcmp(tagname, "software") == 0)
|
|
{
|
|
static const char *attrnames[] = { "name", "cloneof", "supported" };
|
|
auto attrvalues = parse_attributes(attributes, attrnames);
|
|
|
|
if (!attrvalues[0].empty())
|
|
{
|
|
m_infolist.emplace_back(std::move(attrvalues[0]), std::move(attrvalues[1]), attrvalues[2].c_str());
|
|
m_current_info = &m_infolist.back();
|
|
}
|
|
else
|
|
parse_error("No name defined for item");
|
|
}
|
|
else
|
|
unknown_tag(tagname);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_main_start - handle tag start within
|
|
// a software tag
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::parse_soft_start(const char *tagname, const char **attributes)
|
|
{
|
|
// get the current info; error if none
|
|
if (m_current_info == nullptr)
|
|
{
|
|
parse_error("Tag %s found outside of software context", tagname);
|
|
return;
|
|
}
|
|
|
|
// <description>
|
|
if (strcmp(tagname, "description") == 0)
|
|
m_data_accum_expected = true;
|
|
|
|
// <year>
|
|
else if (strcmp(tagname, "year") == 0)
|
|
m_data_accum_expected = true;
|
|
|
|
// <publisher>
|
|
else if (strcmp(tagname, "publisher") == 0)
|
|
m_data_accum_expected = true;
|
|
|
|
// <info name='' value=''>
|
|
else if (strcmp(tagname, "info") == 0)
|
|
{
|
|
std::string infoname, infovalue;
|
|
|
|
if (parse_name_and_value(attributes, infoname, infovalue))
|
|
m_current_info->m_other_info.emplace_back(std::move(infoname), std::move(infovalue));
|
|
else
|
|
parse_error("Incomplete other_info definition");
|
|
}
|
|
|
|
// <sharedfeat name='' value=''>
|
|
else if (strcmp(tagname, "sharedfeat") == 0)
|
|
{
|
|
std::string featname, featvalue;
|
|
|
|
if (parse_name_and_value(attributes, featname, featvalue))
|
|
m_current_info->m_shared_info.emplace_back(std::move(featname), std::move(featvalue));
|
|
else
|
|
parse_error("Incomplete sharedfeat definition");
|
|
}
|
|
|
|
// <part name='' interface=''>
|
|
else if (strcmp(tagname, "part" ) == 0)
|
|
{
|
|
static const char *attrnames[] = { "name", "interface" };
|
|
auto attrvalues = parse_attributes(attributes, attrnames);
|
|
|
|
if (!attrvalues[0].empty() && !attrvalues[1].empty())
|
|
{
|
|
m_current_info->m_partdata.emplace_back(*m_current_info, std::move(attrvalues[0]), std::move(attrvalues[1]));
|
|
m_current_part = &m_current_info->m_partdata.back();
|
|
}
|
|
else
|
|
parse_error("Incomplete part definition");
|
|
}
|
|
else
|
|
unknown_tag(tagname);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_part_start - handle tag start within
|
|
// a part tag
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::parse_part_start(const char *tagname, const char **attributes)
|
|
{
|
|
// get the current part; error if none
|
|
if (m_current_part == nullptr)
|
|
{
|
|
parse_error("Tag %s found outside of part context", tagname);
|
|
return;
|
|
}
|
|
|
|
// <dataarea name='' size=''>
|
|
if (strcmp(tagname, "dataarea") == 0)
|
|
{
|
|
static const char *attrnames[] = { "name", "size", "width", "endianness" };
|
|
auto attrvalues = parse_attributes(attributes, attrnames);
|
|
|
|
if (!attrvalues[0].empty() && !attrvalues[1].empty())
|
|
{
|
|
// handle region attributes
|
|
const std::string &width = attrvalues[2];
|
|
const std::string &endianness = attrvalues[3];
|
|
uint32_t regionflags = ROMENTRYTYPE_REGION;
|
|
|
|
if (!width.empty())
|
|
{
|
|
if (width == "8")
|
|
regionflags |= ROMREGION_8BIT;
|
|
else if (width == "16")
|
|
regionflags |= ROMREGION_16BIT;
|
|
else if (width == "32")
|
|
regionflags |= ROMREGION_32BIT;
|
|
else if (width == "64")
|
|
regionflags |= ROMREGION_64BIT;
|
|
else
|
|
parse_error("Invalid dataarea width");
|
|
}
|
|
if (!endianness.empty())
|
|
{
|
|
if (endianness == "little")
|
|
regionflags |= ROMREGION_LE;
|
|
else if (endianness == "big")
|
|
regionflags |= ROMREGION_BE;
|
|
else
|
|
parse_error("Invalid dataarea endianness");
|
|
}
|
|
|
|
add_rom_entry(std::move(attrvalues[0]), "", 0, strtol(attrvalues[1].c_str(), nullptr, 0), regionflags);
|
|
}
|
|
else
|
|
parse_error("Incomplete dataarea definition");
|
|
}
|
|
|
|
// <diskarea name=''>
|
|
else if (strcmp(tagname, "diskarea") == 0)
|
|
{
|
|
static const char *attrnames[] = { "name" };
|
|
auto attrvalues = parse_attributes(attributes, attrnames);
|
|
|
|
if (!attrvalues[0].empty())
|
|
add_rom_entry(std::move(attrvalues[0]), "", 0, 1, ROMENTRYTYPE_REGION | ROMREGION_DATATYPEDISK);
|
|
else
|
|
parse_error("Incomplete diskarea definition");
|
|
}
|
|
|
|
// <feature name='' value=''>
|
|
else if (strcmp(tagname, "feature") == 0)
|
|
{
|
|
std::string featname, featvalue;
|
|
|
|
if (parse_name_and_value(attributes, featname, featvalue))
|
|
m_current_part->m_featurelist.emplace_back(std::move(featname), std::move(featvalue));
|
|
else
|
|
parse_error("Incomplete feature definition");
|
|
}
|
|
|
|
// <dipswitch>
|
|
else if (strcmp(tagname, "dipswitch") == 0)
|
|
;
|
|
else
|
|
unknown_tag(tagname);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_data_start - handle tag start within a
|
|
// dataarea or diskarea tag
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::parse_data_start(const char *tagname, const char **attributes)
|
|
{
|
|
// get the current part; error if none
|
|
if (m_current_part == nullptr)
|
|
{
|
|
parse_error("Tag %s found outside of part context", tagname);
|
|
return;
|
|
}
|
|
|
|
// <rom name='' size='' crc='' sha1='' offset='' value='' status='' loadflag=''>
|
|
if (strcmp(tagname, "rom") == 0)
|
|
{
|
|
static const char *attrnames[] = { "name", "size", "crc", "sha1", "offset", "value", "status", "loadflag" };
|
|
auto attrvalues = parse_attributes(attributes, attrnames);
|
|
|
|
std::string &name = attrvalues[0];
|
|
const std::string &sizestr = attrvalues[1];
|
|
const std::string &crc = attrvalues[2];
|
|
const std::string &sha1 = attrvalues[3];
|
|
const std::string &offsetstr = attrvalues[4];
|
|
std::string &value = attrvalues[5];
|
|
const std::string &status = attrvalues[6];
|
|
const std::string &loadflag = attrvalues[7];
|
|
if (!sizestr.empty() && !offsetstr.empty())
|
|
{
|
|
uint32_t length = strtol(sizestr.c_str(), nullptr, 0);
|
|
uint32_t offset = strtol(offsetstr.c_str(), nullptr, 0);
|
|
|
|
if (loadflag == "reload")
|
|
add_rom_entry("", "", offset, length, ROMENTRYTYPE_RELOAD | ROM_INHERITFLAGS);
|
|
else if (loadflag == "reload_plain")
|
|
add_rom_entry("", "", offset, length, ROMENTRYTYPE_RELOAD);
|
|
else if (loadflag == "continue")
|
|
add_rom_entry("", "", offset, length, ROMENTRYTYPE_CONTINUE | ROM_INHERITFLAGS);
|
|
else if (loadflag == "fill")
|
|
add_rom_entry("", std::move(value), offset, length, ROMENTRYTYPE_FILL);
|
|
else if (!name.empty())
|
|
{
|
|
bool baddump = (status == "baddump");
|
|
bool nodump = (status == "nodump");
|
|
|
|
std::string hashdata;
|
|
if (nodump)
|
|
{
|
|
hashdata = string_format("%s", NO_DUMP);
|
|
if (!crc.empty() && !sha1.empty())
|
|
parse_error("No need for hash definition");
|
|
}
|
|
else
|
|
{
|
|
if (!crc.empty() && !sha1.empty())
|
|
hashdata = string_format("%c%s%c%s%s", util::hash_collection::HASH_CRC, crc, util::hash_collection::HASH_SHA1, sha1, (baddump ? BAD_DUMP : ""));
|
|
else
|
|
parse_error("Incomplete rom hash definition");
|
|
}
|
|
|
|
// Handle loadflag attribute
|
|
int romflags = 0;
|
|
if (loadflag == "load16_word_swap")
|
|
romflags = ROM_GROUPWORD | ROM_REVERSE;
|
|
else if (loadflag == "load16_byte")
|
|
romflags = ROM_SKIP(1);
|
|
else if (loadflag == "load32_word_swap")
|
|
romflags = ROM_GROUPWORD | ROM_REVERSE | ROM_SKIP(2);
|
|
else if (loadflag == "load32_word")
|
|
romflags = ROM_GROUPWORD | ROM_SKIP(2);
|
|
else if (loadflag == "load32_byte")
|
|
romflags = ROM_SKIP(3);
|
|
|
|
add_rom_entry(std::move(name), std::move(hashdata), offset, length, ROMENTRYTYPE_ROM | romflags);
|
|
}
|
|
else
|
|
parse_error("Rom name missing");
|
|
}
|
|
else if (!sizestr.empty() && !loadflag.empty() && loadflag == "ignore")
|
|
{
|
|
uint32_t length = strtol(sizestr.c_str(), nullptr, 0);
|
|
add_rom_entry("", "", 0, length, ROMENTRYTYPE_IGNORE | ROM_INHERITFLAGS);
|
|
}
|
|
else
|
|
parse_error("Incomplete rom definition");
|
|
}
|
|
|
|
// <rom name='' sha1='' status='' writeable=''>
|
|
else if (strcmp(tagname, "disk") == 0)
|
|
{
|
|
static const char *attrnames[] = { "name", "sha1", "status", "writeable" };
|
|
auto attrvalues = parse_attributes(attributes, attrnames);
|
|
|
|
std::string &name = attrvalues[0];
|
|
const std::string &sha1 = attrvalues[1];
|
|
const std::string &status = attrvalues[2];
|
|
const std::string &writeablestr = attrvalues[3];
|
|
if (!name.empty() && !sha1.empty())
|
|
{
|
|
const bool baddump = (status == "baddump");
|
|
const bool nodump = (status == "nodump" );
|
|
const bool writeable = (writeablestr == "yes");
|
|
std::string hashdata = string_format("%c%s%s", util::hash_collection::HASH_SHA1, sha1, (nodump ? NO_DUMP : (baddump ? BAD_DUMP : "")));
|
|
|
|
add_rom_entry(std::move(name), std::move(hashdata), 0, 0, ROMENTRYTYPE_ROM | (writeable ? DISK_READWRITE : DISK_READONLY));
|
|
}
|
|
else if (status.empty() || (status == "nodump")) // a no_dump chd is not an incomplete entry
|
|
parse_error("Incomplete disk definition");
|
|
}
|
|
|
|
// <dipvalue>
|
|
else if (strcmp(tagname, "dipvalue") == 0)
|
|
;
|
|
else
|
|
unknown_tag(tagname);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// parse_soft_end - handle end-of-tag post-
|
|
// processing within the <software> tag
|
|
//-------------------------------------------------
|
|
|
|
void softlist_parser::parse_soft_end(const char *tagname)
|
|
{
|
|
assert(m_current_info != nullptr);
|
|
|
|
// <description>
|
|
if (strcmp(tagname, "description") == 0)
|
|
m_current_info->m_longname = m_data_accum;
|
|
|
|
// <year>
|
|
else if (strcmp(tagname, "year") == 0)
|
|
m_current_info->m_year = m_data_accum;
|
|
|
|
// <publisher>
|
|
else if (strcmp(tagname, "publisher") == 0)
|
|
m_current_info->m_publisher = m_data_accum;
|
|
|
|
// </part>
|
|
else if (strcmp(tagname, "part") == 0)
|
|
{
|
|
// get the last part
|
|
assert(m_current_part != nullptr);
|
|
if (m_current_part == nullptr)
|
|
return;
|
|
|
|
// was any dataarea/rom information encountered? if so, add a terminator
|
|
if (!m_current_part->m_romdata.empty())
|
|
add_rom_entry("", "", 0, 0, ROMENTRYTYPE_END);
|
|
|
|
// get the info; if present, copy shared data (we assume name/value strings live
|
|
// in the string pool and don't need to be reallocated)
|
|
if (m_current_info != nullptr)
|
|
for (const feature_list_item &item : m_current_info->shared_info())
|
|
m_current_part->m_featurelist.emplace_back(item.name(), item.value());
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// software_name_parse - helper that splits a
|
|
// software identifier (software_list:software:part)
|
|
// string into separate software_list, software, and part
|
|
// strings.
|
|
//
|
|
// str1:str2:str3 => swlist_name - str1, swname - str2, swpart - str3
|
|
// str1:str2 => swlist_name - nullptr, swname - str1, swpart - str2
|
|
// str1 => swlist_name - nullptr, swname - str1, swpart - nullptr
|
|
//
|
|
// Notice however that we could also have been
|
|
// passed a string swlist_name:swname, and thus
|
|
// some special check has to be performed in this
|
|
// case.
|
|
//-------------------------------------------------
|
|
|
|
bool software_name_parse(const std::string &identifier, std::string *list_name, std::string *software_name, std::string *part_name)
|
|
{
|
|
// first, sanity check the arguments
|
|
if (!std::regex_match(identifier, s_potenial_softlist_regex))
|
|
return false;
|
|
|
|
// reset all output parameters (if specified of course)
|
|
if (list_name != nullptr)
|
|
list_name->clear();
|
|
if (software_name != nullptr)
|
|
software_name->clear();
|
|
if (part_name != nullptr)
|
|
part_name->clear();
|
|
|
|
// if no colon, this is the swname by itself
|
|
auto split1 = identifier.find_first_of(':');
|
|
if (split1 == std::string::npos)
|
|
{
|
|
if (software_name != nullptr)
|
|
*software_name = identifier;
|
|
return true;
|
|
}
|
|
|
|
// if one colon, it is the swname and swpart alone
|
|
auto split2 = identifier.find_first_of(':', split1 + 1);
|
|
if (split2 == std::string::npos)
|
|
{
|
|
if (software_name != nullptr)
|
|
*software_name = identifier.substr(0, split1);
|
|
if (part_name != nullptr)
|
|
*part_name = identifier.substr(split1 + 1);
|
|
return true;
|
|
}
|
|
|
|
// if two colons present, split into 3 parts
|
|
if (list_name != nullptr)
|
|
*list_name = identifier.substr(0, split1);
|
|
if (software_name != nullptr)
|
|
*software_name = identifier.substr(split1 + 1, split2 - (split1 + 1));
|
|
if (part_name != nullptr)
|
|
*part_name = identifier.substr(split2 + 1);
|
|
return true;
|
|
}
|