diff options
Diffstat (limited to 'src/emu/rendlay.cpp')
-rw-r--r-- | src/emu/rendlay.cpp | 1255 |
1 files changed, 909 insertions, 346 deletions
diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index e245aebf2dd..d07c07704a6 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -15,12 +15,21 @@ #include "rendfont.h" #include "rendlay.h" #include "rendutil.h" +#include "vecstream.h" #include "xmlfile.h" #include <ctype.h> +#include <algorithm> +#include <cstddef> +#include <cstdio> +#include <cstring> +#include <iomanip> +#include <locale> #include <sstream> #include <stdexcept> +#include <tuple> #include <type_traits> +#include <utility> @@ -69,6 +78,8 @@ enum LINE_CAP_END = 2 }; +std::locale const f_portable_locale("C"); + //************************************************************************** @@ -129,253 +140,727 @@ inline void render_bounds_transform(render_bounds &bounds, render_bounds const & class layout_syntax_error : public std::invalid_argument { using std::invalid_argument::invalid_argument; }; class layout_reference_error : public std::out_of_range { using std::out_of_range::out_of_range; }; +} // anonymous namespace -//************************************************************************** -// SHARED PARSING HELPERS -//************************************************************************** +namespace emu { namespace render { namespace detail { -//------------------------------------------------- -// get_variable_value - compute the value of -// a variable in an XML attribute -//------------------------------------------------- - -int get_variable_value(running_machine &machine, const char *string, char **outputptr) +class layout_environment { - char temp[100]; - - // screen 0 parameters - int scrnum = 0; - for (const screen_device &device : screen_device_iterator(machine.root_device())) - { - // native X aspect factor - sprintf(temp, "~scr%dnativexaspect~", scrnum); - if (!strncmp(string, temp, strlen(temp))) +private: + class entry + { + public: + entry(std::string &&name, std::string &&t) + : m_name(std::move(name)) + , m_text(std::move(t)) + , m_text_valid(true) + { } + entry(std::string &&name, s64 i) + : m_name(std::move(name)) + , m_int(i) + , m_int_valid(true) + { } + entry(std::string &&name, double f) + : m_name(std::move(name)) + , m_float(f) + , m_float_valid(true) + { } + entry(std::string &&name, std::string &&t, s64 i, int s) + : m_name(std::move(name)) + , m_text(std::move(t)) + , m_int_increment(i) + , m_shift(s) + , m_text_valid(true) + , m_incrementing(true) + { } + entry(std::string &&name, std::string &&t, double i, int s) + : m_name(std::move(name)) + , m_text(std::move(t)) + , m_float_increment(i) + , m_shift(s) + , m_text_valid(true) + , m_incrementing(true) + { } + entry(entry &&) = default; + entry &operator=(entry &&) = default; + + void set(std::string &&t) { - int num = device.visible_area().width(); - int den = device.visible_area().height(); - reduce_fraction(num, den); - *outputptr += sprintf(*outputptr, "%d", num); - return strlen(temp); + m_text = std::move(t); + m_text_valid = true; + m_int_valid = false; + m_float_valid = false; } - - // native Y aspect factor - sprintf(temp, "~scr%dnativeyaspect~", scrnum); - if (!strncmp(string, temp, strlen(temp))) + void set(s64 i) { - int num = device.visible_area().width(); - int den = device.visible_area().height(); - reduce_fraction(num, den); - *outputptr += sprintf(*outputptr, "%d", den); - return strlen(temp); + m_int = i; + m_text_valid = false; + m_int_valid = true; + m_float_valid = false; } - - // native width - sprintf(temp, "~scr%dwidth~", scrnum); - if (!strncmp(string, temp, strlen(temp))) + void set(double f) { - *outputptr += sprintf(*outputptr, "%d", device.visible_area().width()); - return strlen(temp); + m_float = f; + m_text_valid = false; + m_int_valid = false; + m_float_valid = true; } - // native height - sprintf(temp, "~scr%dheight~", scrnum); - if (!strncmp(string, temp, strlen(temp))) + std::string const &name() const { return m_name; } + bool is_incrementing() const { return m_incrementing; } + + std::string const &get_text() { - *outputptr += sprintf(*outputptr, "%d", device.visible_area().height()); - return strlen(temp); + if (!m_text_valid) + { + if (m_float_valid) + { + m_text = std::to_string(m_float); + m_text_valid = true; + } + else if (m_int_valid) + { + m_text = std::to_string(m_int); + m_text_valid = true; + } + } + return m_text; } - // keep count - scrnum++; - } + void increment() + { + if (is_incrementing()) + { + // apply increment + if (m_float_increment) + { + if (m_int_valid && !m_float_valid) + { + m_float = m_int; + m_float_valid = true; + } + if (m_text_valid && !m_float_valid) + { + std::istringstream stream(m_text); + stream.imbue(f_portable_locale); + m_text.c_str(); + if (m_text[0] == '$') + { + stream.get(); + u64 uvalue; + stream >> std::hex >> uvalue; + m_float = uvalue; + } + else if ((m_text[0] == '0') && ((m_text[1] == 'x') || (m_text[1] == 'X'))) + { + stream.get(); + stream.get(); + u64 uvalue; + stream >> std::hex >> uvalue; + m_float = uvalue; + } + else if (m_text[0] == '#') + { + stream.get(); + stream >> m_int; + m_float = m_int; + } + else + { + stream >> m_float; + } + m_float_valid = bool(stream); + } + m_float += m_float_increment; + m_int_valid = m_text_valid = false; + } + else + { + if (m_text_valid && !m_int_valid && !m_float_valid) + { + std::istringstream stream(m_text); + stream.imbue(f_portable_locale); + m_text.c_str(); + if (m_text[0] == '$') + { + stream.get(); + u64 uvalue; + stream >> std::hex >> uvalue; + m_int = s64(uvalue); + m_int_valid = bool(stream); + } + else if ((m_text[0] == '0') && ((m_text[1] == 'x') || (m_text[1] == 'X'))) + { + stream.get(); + stream.get(); + u64 uvalue; + stream >> std::hex >> uvalue; + m_int = s64(uvalue); + m_int_valid = bool(stream); + } + else if (m_text[0] == '#') + { + stream.get(); + stream >> m_int; + m_int_valid = bool(stream); + } + else if (m_text.find_first_of(".eE") != std::string::npos) + { + stream >> m_float; + m_float_valid = bool(stream); + } + else + { + stream >> m_int; + m_int_valid = bool(stream); + } + } - // default: copy the first character and continue - **outputptr = *string; - *outputptr += 1; - return 1; -} + if (m_float_valid) + { + m_float += m_int_increment; + m_int_valid = m_text_valid = false; + } + else + { + m_int += m_int_increment; + m_float_valid = m_text_valid = false; + } + } + // apply shift + if (m_shift) + { + if (m_float_valid && !m_int_valid) + { + m_int = s64(m_float); + m_int_valid = true; + } + if (m_text_valid && !m_int_valid) + { + std::istringstream stream(m_text); + stream.imbue(f_portable_locale); + m_text.c_str(); + if (m_text[0] == '$') + { + stream.get(); + u64 uvalue; + stream >> std::hex >> uvalue; + m_int = s64(uvalue); + } + else if ((m_text[0] == '0') && ((m_text[1] == 'x') || (m_text[1] == 'X'))) + { + stream.get(); + stream.get(); + u64 uvalue; + stream >> std::hex >> uvalue; + m_int = s64(uvalue); + } + else + { + if (m_text[0] == '#') + stream.get(); + stream >> m_int; + } + m_int_valid = bool(stream); + } + if (0 > m_shift) + m_int >>= -m_shift; + else + m_int <<= m_shift; + m_text_valid = m_float_valid = false; + } + } + } -//------------------------------------------------- -// xml_get_attribute_string_with_subst - analog -// to xml_get_attribute_string but with variable -// substitution -//------------------------------------------------- + static bool name_less(entry const &lhs, entry const &rhs) { return lhs.name() < rhs.name(); } + + private: + std::string m_name; + std::string m_text; + s64 m_int = 0, m_int_increment = 0; + double m_float = 0.0, m_float_increment = 0.0; + int m_shift = 0; + bool m_text_valid = false; + bool m_int_valid = false; + bool m_float_valid = false; + bool m_incrementing = false; + }; + + using entry_vector = std::vector<entry>; + + template <typename T, typename U> + void try_insert(T &&name, U &&value) + { + entry_vector::iterator const pos( + std::lower_bound( + m_entries.begin(), + m_entries.end(), + name, + [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; })); + if ((m_entries.end() == pos) || (pos->name() != name)) + m_entries.emplace(pos, std::forward<T>(name), std::forward<U>(value)); + } + + template <typename T, typename U> + void set(T &&name, U &&value) + { + entry_vector::iterator const pos( + std::lower_bound( + m_entries.begin(), + m_entries.end(), + name, + [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; })); + if ((m_entries.end() == pos) || (pos->name() != name)) + m_entries.emplace(pos, std::forward<T>(name), std::forward<U>(value)); + else + pos->set(std::forward<U>(value)); + } -const char *xml_get_attribute_string_with_subst(running_machine &machine, util::xml::data_node const &node, const char *attribute, const char *defvalue) -{ - const char *str = node.get_attribute_string(attribute, nullptr); - static char buffer[1000]; + void cache_device_entries() + { + if (!m_next && !m_cached) + { + try_insert("devicetag", device().tag()); + try_insert("devicebasetag", device().basetag()); + try_insert("devicename", device().name()); + try_insert("deviceshortname", device().shortname()); + util::ovectorstream tmp; + unsigned i(0U); + for (screen_device const &screen : screen_device_iterator(machine().root_device())) + { + s64 const w(screen.visible_area().width()), h(screen.visible_area().height()); + s64 xaspect(w), yaspect(h); + reduce_fraction(xaspect, yaspect); + + tmp.seekp(0); + util::stream_format(tmp, "scr%unativexaspect", i); + tmp.put('\0'); + try_insert(&tmp.vec()[0], xaspect); + + tmp.seekp(0); + util::stream_format(tmp, "scr%unativeyaspect", i); + tmp.put('\0'); + try_insert(&tmp.vec()[0], yaspect); + + tmp.seekp(0); + util::stream_format(tmp, "scr%uwidth", i); + tmp.put('\0'); + try_insert(&tmp.vec()[0], w); + + tmp.seekp(0); + util::stream_format(tmp, "scr%uheight", i); + tmp.put('\0'); + try_insert(&tmp.vec()[0], h); + + ++i; + } + m_cached = true; + } + } - // if nothing, just return the default - if (str == nullptr) - return defvalue; + entry *find_entry(char const *begin, char const *end) + { + cache_device_entries(); + entry_vector::iterator const pos( + std::lower_bound( + m_entries.begin(), + m_entries.end(), + std::make_pair(begin, end - begin), + [] (entry const &lhs, std::pair<char const *, std::ptrdiff_t> const &rhs) + { return 0 > std::strncmp(lhs.name().c_str(), rhs.first, rhs.second); })); + if ((m_entries.end() != pos) && (pos->name().length() == (end - begin)) && !std::strncmp(pos->name().c_str(), begin, end - begin)) + return &*pos; + else + return m_next ? m_next->find_entry(begin, end) : nullptr; + } - // if no tildes, don't worry - if (strchr(str, '~') == nullptr) - return str; + template <typename... T> + std::tuple<char const *, char const *, bool> get_variable_text(T &&... args) + { + entry *const found(find_entry(std::forward<T>(args)...)); + if (found) + { + std::string const &text(found->get_text()); + char const *const begin(text.c_str()); + return std::make_tuple(begin, begin + text.length(), true); + } + else + { + return std::make_tuple(nullptr, nullptr, false); + } + } - // make a copy of the string, doing substitutions along the way - const char *s; - char *d; - for (s = str, d = buffer; *s != 0; ) + std::pair<char const *, char const *> expand(char const *begin, char const *end) { - // if not a variable, just copy - if (*s != '~') - *d++ = *s++; + // search for candidate variable references + char const *start(begin); + char const *pos(std::find_if(start, end, is_variable_start)); + while (pos != end) + { + char const *const term(std::find_if(pos + 1, end, [] (char ch) { return !is_variable_char(ch); })); + if ((term == end) || !is_variable_end(*term)) + { + // not a valid variable name - keep searching + pos = std::find_if(term, end, is_variable_start); + } + else + { + // looks like a variable reference - try to look it up + std::tuple<char const *, char const *, bool> const text(get_variable_text(pos + 1, term)); + if (std::get<2>(text)) + { + // variable found + if (begin == start) + m_buffer.seekp(0); + m_buffer.write(start, pos - start); + m_buffer.write(std::get<0>(text), std::get<1>(text) - std::get<0>(text)); + start = term + 1; + pos = std::find_if(start, end, is_variable_start); + } + else + { + // variable not found - move on + pos = std::find_if(pos + 1, end, is_variable_start); + } + } + } - // extract the variable + // short-circuit the case where no substitutions were made + if (start == begin) + { + return std::make_pair(begin, end); + } else - s += get_variable_value(machine, s, &d); + { + m_buffer.write(start, pos - start); + m_buffer.put('\0'); + std::vector<char> const &vec(m_buffer.vec()); + if (vec.empty()) + return std::make_pair(nullptr, nullptr); + else + return std::make_pair(&vec[0], &vec[0] + vec.size() - 1); + } } - *d = 0; - return buffer; -} - -//------------------------------------------------- -// xml_get_attribute_int_with_subst - analog -// to xml_get_attribute_int but with variable -// substitution -//------------------------------------------------- + std::pair<char const *, char const *> expand(char const *str) + { + return expand(str, str + strlen(str)); + } -int xml_get_attribute_int_with_subst(running_machine &machine, util::xml::data_node const &node, const char *attribute, int defvalue) -{ - const char *string = xml_get_attribute_string_with_subst(machine, node, attribute, nullptr); - int value; - unsigned int uvalue; - - if (string == nullptr) - return defvalue; - if (string[0] == '$') - return (sscanf(&string[1], "%X", &uvalue) == 1) ? uvalue : defvalue; - if (string[0] == '0' && string[1] == 'x') - return (sscanf(&string[2], "%X", &uvalue) == 1) ? uvalue : defvalue; - if (string[0] == '#') - return (sscanf(&string[1], "%d", &value) == 1) ? value : defvalue; - return (sscanf(&string[0], "%d", &value) == 1) ? value : defvalue; -} + std::string parameter_name(util::xml::data_node const &node) + { + char const *const attrib(node.get_attribute_string("name", nullptr)); + if (!attrib) + throw layout_syntax_error("parameter lacks name attribute"); + std::pair<char const *, char const *> const expanded(expand(attrib)); + return std::string(expanded.first, expanded.second); + } + static constexpr bool is_variable_start(char ch) + { + return '~' == ch; + } + static constexpr bool is_variable_end(char ch) + { + return '~' == ch; + } + static constexpr bool is_variable_char(char ch) + { + return (('0' <= ch) && ('9' >= ch)) || (('A' <= ch) && ('Z' >= ch)) || (('a' <= ch) && ('z' >= ch)) || ('_' == ch); + } -//------------------------------------------------- -// xml_get_attribute_float_with_subst - analog -// to xml_get_attribute_float but with variable -// substitution -//------------------------------------------------- + entry_vector m_entries; + util::ovectorstream m_buffer; + device_t &m_device; + layout_environment *const m_next = nullptr; + bool m_cached = false; -float xml_get_attribute_float_with_subst(running_machine &machine, util::xml::data_node const &node, const char *attribute, float defvalue) -{ - const char *string = xml_get_attribute_string_with_subst(machine, node, attribute, nullptr); - float value; +public: + explicit layout_environment(device_t &device) : m_device(device) { } + explicit layout_environment(layout_environment &next) : m_device(next.m_device), m_next(&next) { } + layout_environment(layout_environment const &) = delete; - if (string == nullptr || sscanf(string, "%f", &value) != 1) - return defvalue; - return value; -} + device_t &device() { return m_device; } + running_machine &machine() { return device().machine(); } + bool is_root_device() { return &device() == &machine().root_device(); } + void set_parameter(std::string &&name, std::string &&value) + { + set(std::move(name), std::move(value)); + } -//------------------------------------------------- -// parse_bounds - parse a bounds XML node -//------------------------------------------------- + void set_parameter(std::string &&name, s64 value) + { + set(std::move(name), value); + } -void parse_bounds(running_machine &machine, util::xml::data_node const *boundsnode, render_bounds &bounds) -{ - // skip if nothing - if (boundsnode == nullptr) + void set_parameter(std::string &&name, double value) { - bounds.x0 = bounds.y0 = 0.0f; - bounds.x1 = bounds.y1 = 1.0f; - return; + set(std::move(name), value); } - // parse out the data - if (boundsnode->has_attribute("left")) + void set_parameter(util::xml::data_node const &node) { - // left/right/top/bottom format - bounds.x0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "left", 0.0f); - bounds.x1 = xml_get_attribute_float_with_subst(machine, *boundsnode, "right", 1.0f); - bounds.y0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "top", 0.0f); - bounds.y1 = xml_get_attribute_float_with_subst(machine, *boundsnode, "bottom", 1.0f); + // do basic validation + std::string name(parameter_name(node)); + if (node.has_attribute("start") || node.has_attribute("increment") || node.has_attribute("lshift") || node.has_attribute("rshift")) + throw layout_syntax_error("start/increment/lshift/rshift attributes are only allowed for repeat parameters"); + char const *const value(node.get_attribute_string("value", nullptr)); + if (!value) + throw layout_syntax_error("parameter lacks value attribute"); + + // expand value and stash + std::pair<char const *, char const *> const expanded(expand(value)); + set(std::move(name), std::string(expanded.first, expanded.second)); } - else if (boundsnode->has_attribute("x")) + + void set_repeat_parameter(util::xml::data_node const &node, bool init) { - // x/y/width/height format - bounds.x0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "x", 0.0f); - bounds.x1 = bounds.x0 + xml_get_attribute_float_with_subst(machine, *boundsnode, "width", 1.0f); - bounds.y0 = xml_get_attribute_float_with_subst(machine, *boundsnode, "y", 0.0f); - bounds.y1 = bounds.y0 + xml_get_attribute_float_with_subst(machine, *boundsnode, "height", 1.0f); + // two types are allowed here - static value, and start/increment/lshift/rshift + std::string name(parameter_name(node)); + char const *const start(node.get_attribute_string("start", nullptr)); + if (start) + { + // simple validity checks + if (node.has_attribute("value")) + throw layout_syntax_error("start attribute may not be used in combination with value attribute"); + int const lshift(node.has_attribute("lshift") ? get_attribute_int(node, "lshift", -1) : 0); + int const rshift(node.has_attribute("rshift") ? get_attribute_int(node, "rshift", -1) : 0); + if ((0 > lshift) || (0 > rshift)) + throw layout_syntax_error("lshift/rshift attributes must be non-negative integers"); + + // increment is more complex - it may be an integer or a floating-point number + s64 intincrement(0); + double floatincrement(0); + char const *const increment(node.get_attribute_string("increment", nullptr)); + if (increment) + { + std::pair<char const *, char const *> const expanded(expand(increment)); + unsigned const hexprefix((expanded.first[0] == '$') ? 1U : ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X'))) ? 2U : 0U); + unsigned const decprefix((expanded.first[0] == '#') ? 1U : 0U); + bool const floatchars(std::find_if(expanded.first, expanded.second, [] (char ch) { return ('.' == ch) || ('e' == ch) || ('E' == ch); }) != expanded.second); + std::istringstream stream(std::string(expanded.first + hexprefix + decprefix, expanded.second)); + stream.imbue(f_portable_locale); + if (!hexprefix && !decprefix && floatchars) + { + stream >> floatincrement; + } + else if (hexprefix) + { + u64 uvalue; + stream >> std::hex >> uvalue; + intincrement = s64(uvalue); + } + else + { + stream >> intincrement; + } + + // reject obviously bad stuff + if (!stream) + throw layout_syntax_error("increment attribute must be a number"); + } + + // don't allow incrementing parameters to be redefined + if (init) + { + entry_vector::iterator const pos( + std::lower_bound( + m_entries.begin(), + m_entries.end(), + name, + [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; })); + if ((m_entries.end() != pos) && (pos->name() == name)) + throw layout_syntax_error("incrementing parameters must be defined exactly once per scope"); + + std::pair<char const *, char const *> const expanded(expand(start)); + if (floatincrement) + m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second), floatincrement, lshift - rshift); + else + m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second), intincrement, lshift - rshift); + } + } + else if (node.has_attribute("increment") || node.has_attribute("lshift") || node.has_attribute("rshift")) + { + throw layout_syntax_error("increment/lshift/rshift attributes require start attribute"); + } + else + { + char const *const value(node.get_attribute_string("value", nullptr)); + if (!value) + throw layout_syntax_error("parameter lacks value attribute"); + std::pair<char const *, char const *> const expanded(expand(value)); + entry_vector::iterator const pos( + std::lower_bound( + m_entries.begin(), + m_entries.end(), + name, + [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; })); + if ((m_entries.end() == pos) || (pos->name() != name)) + m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second)); + else if (pos->is_incrementing()) + throw layout_syntax_error("incrementing parameters must be defined exactly once per scope"); + else + pos->set(std::string(expanded.first, expanded.second)); + } } - else + + void increment_parameters() { - throw layout_syntax_error("bounds element requires either left or x attribute"); + for (entry &e : m_entries) + e.increment(); } - // check for errors - if ((bounds.x0 > bounds.x1) || (bounds.y0 > bounds.y1)) - throw layout_syntax_error(util::string_format("illegal bounds (%f-%f)-(%f-%f)", bounds.x0, bounds.x1, bounds.y0, bounds.y1)); -} + char const *get_attribute_string(util::xml::data_node const &node, char const *name, char const *defvalue) + { + char const *const attrib(node.get_attribute_string(name, nullptr)); + return attrib ? expand(attrib).first : defvalue; + } + int get_attribute_int(util::xml::data_node const &node, const char *name, int defvalue) + { + char const *const attrib(node.get_attribute_string(name, nullptr)); + if (!attrib) + return defvalue; -//------------------------------------------------- -// parse_color - parse a color XML node -//------------------------------------------------- + // similar to what XML nodes do + std::pair<char const *, char const *> const expanded(expand(attrib)); + std::istringstream stream; + stream.imbue(f_portable_locale); + int result; + if (expanded.first[0] == '$') + { + stream.str(std::string(expanded.first + 1, expanded.second)); + unsigned uvalue; + stream >> std::hex >> uvalue; + result = int(uvalue); + } + else if ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X'))) + { + stream.str(std::string(expanded.first + 2, expanded.second)); + unsigned uvalue; + stream >> std::hex >> uvalue; + result = int(uvalue); + } + else if (expanded.first[0] == '#') + { + stream.str(std::string(expanded.first + 1, expanded.second)); + stream >> result; + } + else + { + stream.str(std::string(expanded.first, expanded.second)); + stream >> result; + } -void parse_color(running_machine &machine, util::xml::data_node const *colornode, render_color &color) -{ - // skip if nothing - if (colornode == nullptr) - { - color.r = color.g = color.b = color.a = 1.0f; - return; + return stream ? result : defvalue; } - // parse out the data - color.r = xml_get_attribute_float_with_subst(machine, *colornode, "red", 1.0); - color.g = xml_get_attribute_float_with_subst(machine, *colornode, "green", 1.0); - color.b = xml_get_attribute_float_with_subst(machine, *colornode, "blue", 1.0); - color.a = xml_get_attribute_float_with_subst(machine, *colornode, "alpha", 1.0); + float get_attribute_float(util::xml::data_node const &node, char const *name, float defvalue) + { + char const *const attrib(node.get_attribute_string(name, nullptr)); + if (!attrib) + return defvalue; - // check for errors - if ((color.r < 0.0f) || (color.r > 1.0f) || (color.g < 0.0f) || (color.g > 1.0f) || - (color.b < 0.0f) || (color.b > 1.0f) || (color.a < 0.0f) || (color.a > 1.0f)) - throw layout_syntax_error(util::string_format("illegal RGBA color %f,%f,%f,%f", color.r, color.g, color.b, color.a)); -} + // similar to what XML nodes do + std::pair<char const *, char const *> const expanded(expand(attrib)); + std::istringstream stream(std::string(expanded.first, expanded.second)); + stream.imbue(f_portable_locale); + float result; + return (stream >> result) ? result : defvalue; + } + void parse_bounds(util::xml::data_node const *node, render_bounds &result) + { + // default to unit rectangle + if (!node) + { + result.x0 = result.y0 = 0.0F; + result.x1 = result.y1 = 1.0F; + } + else + { + // parse attributes + if (node->has_attribute("left")) + { + // left/right/top/bottom format + result.x0 = get_attribute_float(*node, "left", 0.0F); + result.x1 = get_attribute_float(*node, "right", 1.0F); + result.y0 = get_attribute_float(*node, "top", 0.0F); + result.y1 = get_attribute_float(*node, "bottom", 1.0F); + } + else if (node->has_attribute("x")) + { + // x/y/width/height format + result.x0 = get_attribute_float(*node, "x", 0.0F); + result.x1 = result.x0 + get_attribute_float(*node, "width", 1.0F); + result.y0 = get_attribute_float(*node, "y", 0.0F); + result.y1 = result.y0 + get_attribute_float(*node, "height", 1.0F); + } + else + { + throw layout_syntax_error("bounds element requires either left or x attribute"); + } -//------------------------------------------------- -// parse_orientation - parse an orientation XML -// node -//------------------------------------------------- + // check for errors + if ((result.x0 > result.x1) || (result.y0 > result.y1)) + throw layout_syntax_error(util::string_format("illegal bounds (%f-%f)-(%f-%f)", result.x0, result.x1, result.y0, result.y1)); + } + } -void parse_orientation(running_machine &machine, util::xml::data_node const *orientnode, int &orientation) -{ - // skip if nothing - if (orientnode == nullptr) + void parse_color(util::xml::data_node const *node, render_color &result) { - orientation = ROT0; - return; + // default to white + if (!node) + { + result.r = result.g = result.b = result.a = 1.0F; + } + else + { + // parse attributes + result.r = get_attribute_float(*node, "red", 1.0F); + result.g = get_attribute_float(*node, "green", 1.0F); + result.b = get_attribute_float(*node, "blue", 1.0F); + result.a = get_attribute_float(*node, "alpha", 1.0F); + + // check for errors + if ((0.0F > (std::min)({ result.r, result.g, result.b, result.a })) || (1.0F < (std::max)({ result.r, result.g, result.b, result.a }))) + throw layout_syntax_error(util::string_format("illegal RGBA color %f,%f,%f,%f", result.r, result.g, result.b, result.a)); + } } - // parse out the data - int rotate = xml_get_attribute_int_with_subst(machine, *orientnode, "rotate", 0); - switch (rotate) + void parse_orientation(util::xml::data_node const *node, int &result) { - case 0: orientation = ROT0; break; - case 90: orientation = ROT90; break; - case 180: orientation = ROT180; break; - case 270: orientation = ROT270; break; - default: throw layout_syntax_error(util::string_format("invalid rotate attribute %d", rotate)); + // default to no transform + if (!node) + { + result = ROT0; + } + else + { + // parse attributes + int const rotate(get_attribute_int(*node, "rotate", 0)); + switch (rotate) + { + case 0: result = ROT0; break; + case 90: result = ROT90; break; + case 180: result = ROT180; break; + case 270: result = ROT270; break; + default: throw layout_syntax_error(util::string_format("invalid rotate attribute %d", rotate)); + } + if (!std::strcmp("yes", get_attribute_string(*node, "swapxy", "no"))) + result ^= ORIENTATION_SWAP_XY; + if (!std::strcmp("yes", get_attribute_string(*node, "flipx", "no"))) + result ^= ORIENTATION_FLIP_X; + if (!std::strcmp("yes", get_attribute_string(*node, "flipy", "no"))) + result ^= ORIENTATION_FLIP_Y; + } } - if (strcmp("yes", xml_get_attribute_string_with_subst(machine, *orientnode, "swapxy", "no")) == 0) - orientation ^= ORIENTATION_SWAP_XY; - if (strcmp("yes", xml_get_attribute_string_with_subst(machine, *orientnode, "flipx", "no")) == 0) - orientation ^= ORIENTATION_FLIP_X; - if (strcmp("yes", xml_get_attribute_string_with_subst(machine, *orientnode, "flipy", "no")) == 0) - orientation ^= ORIENTATION_FLIP_Y; -} +}; -} // anonymous namespace +} } } // namespace emu::render::detail @@ -413,13 +898,13 @@ layout_element::make_component_map const layout_element::s_make_component{ // layout_element - constructor //------------------------------------------------- -layout_element::layout_element(running_machine &machine, util::xml::data_node const &elemnode, const char *dirname) - : m_machine(machine) +layout_element::layout_element(environment &env, util::xml::data_node const &elemnode, const char *dirname) + : m_machine(env.machine()) , m_defstate(0) , m_maxstate(0) { // get the default state - m_defstate = xml_get_attribute_int_with_subst(machine, elemnode, "defstate", -1); + m_defstate = env.get_attribute_int(elemnode, "defstate", -1); // parse components in order bool first = true; @@ -431,7 +916,7 @@ layout_element::layout_element(running_machine &machine, util::xml::data_node co throw layout_syntax_error(util::string_format("unknown element component %s", compnode->get_name())); // insert the new component into the list - component const &newcomp(**m_complist.emplace(m_complist.end(), make_func->second(machine, *compnode, dirname))); + component const &newcomp(**m_complist.emplace(m_complist.end(), make_func->second(env, *compnode, dirname))); // accumulate bounds if (first) @@ -480,9 +965,8 @@ layout_element::~layout_element() // layout_group - constructor //------------------------------------------------- -layout_group::layout_group(running_machine &machine, util::xml::data_node const &groupnode) - : m_machine(machine) - , m_groupnode(groupnode) +layout_group::layout_group(util::xml::data_node const &groupnode) + : m_groupnode(groupnode) , m_bounds{ 0.0f, 0.0f, 0.0f, 0.0f } , m_bounds_resolved(false) { @@ -530,16 +1014,21 @@ render_bounds layout_group::make_transform(render_bounds const &dest, render_bou // nested groups into consideration //------------------------------------------------- -void layout_group::resolve_bounds(group_map &groupmap) +void layout_group::set_bounds_unresolved() +{ + m_bounds_resolved = false; +} + +void layout_group::resolve_bounds(environment &env, group_map &groupmap) { if (!m_bounds_resolved) { std::vector<layout_group const *> seen; - resolve_bounds(groupmap, seen); + resolve_bounds(env, groupmap, seen); } } -void layout_group::resolve_bounds(group_map &groupmap, std::vector<layout_group const *> &seen) +void layout_group::resolve_bounds(environment &env, group_map &groupmap, std::vector<layout_group const *> &seen) { if (seen.end() != std::find(seen.begin(), seen.end(), this)) { @@ -554,59 +1043,87 @@ void layout_group::resolve_bounds(group_map &groupmap, std::vector<layout_group seen.push_back(this); if (!m_bounds_resolved) { - util::xml::data_node const *const boundsnode(m_groupnode.get_child("bounds")); - if (boundsnode) + environment local(env); + resolve_bounds(local, m_groupnode, groupmap, seen, false, true); + } +} + +void layout_group::resolve_bounds( + environment &env, + util::xml::data_node const &parentnode, + group_map &groupmap, + std::vector<layout_group const *> &seen, + bool repeat, + bool init) +{ + bool envaltered(false); + bool unresolved(true); + for (util::xml::data_node const *itemnode = parentnode.get_first_child(); !m_bounds_resolved && itemnode; itemnode = itemnode->get_next_sibling()) + { + if (!strcmp(itemnode->get_name(), "bounds")) { // use explicit bounds - parse_bounds(m_machine, boundsnode, m_bounds); + env.parse_bounds(itemnode, m_bounds); + m_bounds_resolved = true; } - else + else if (!strcmp(itemnode->get_name(), "param")) { - // otherwise build from items - for (util::xml::data_node const *itemnode = m_groupnode.get_first_child(); itemnode; itemnode = itemnode->get_next_sibling()) + envaltered = true; + if (!unresolved) { - if (!strcmp(itemnode->get_name(), "backdrop") || - !strcmp(itemnode->get_name(), "screen") || - !strcmp(itemnode->get_name(), "overlay") || - !strcmp(itemnode->get_name(), "bezel") || - !strcmp(itemnode->get_name(), "cpanel") || - !strcmp(itemnode->get_name(), "marquee")) - { - render_bounds itembounds; - parse_bounds(m_machine, itemnode->get_child("bounds"), itembounds); - union_render_bounds(m_bounds, itembounds); - } - else if (!strcmp(itemnode->get_name(), "group")) - { - char const *ref(xml_get_attribute_string_with_subst(m_machine, *itemnode, "ref", nullptr)); - if (!ref) - throw layout_syntax_error("nested group must have ref attribute"); + unresolved = true; + for (group_map::value_type &group : groupmap) + group.second.set_bounds_unresolved(); + } + if (!repeat) + env.set_parameter(*itemnode); + else + env.set_repeat_parameter(*itemnode, init); + } + else if (!strcmp(itemnode->get_name(), "backdrop") || + !strcmp(itemnode->get_name(), "screen") || + !strcmp(itemnode->get_name(), "overlay") || + !strcmp(itemnode->get_name(), "bezel") || + !strcmp(itemnode->get_name(), "cpanel") || + !strcmp(itemnode->get_name(), "marquee")) + { + render_bounds itembounds; + env.parse_bounds(itemnode->get_child("bounds"), itembounds); + union_render_bounds(m_bounds, itembounds); + } + else if (!strcmp(itemnode->get_name(), "group")) + { + char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr)); + if (!ref) + throw layout_syntax_error("nested group must have ref attribute"); - group_map::iterator const found(groupmap.find(ref)); - if (groupmap.end() == found) - throw layout_syntax_error(util::string_format("unable to find group %s", ref)); + group_map::iterator const found(groupmap.find(ref)); + if (groupmap.end() == found) + throw layout_syntax_error(util::string_format("unable to find group %s", ref)); - found->second.resolve_bounds(groupmap, seen); - util::xml::data_node const *const itemboundsnode(itemnode->get_child("bounds")); - if (itemboundsnode) - { - render_bounds itembounds; - parse_bounds(m_machine, itemboundsnode, itembounds); - union_render_bounds(m_bounds, itembounds); - } - else - { - union_render_bounds(m_bounds, found->second.m_bounds); - } - } - else if (strcmp(itemnode->get_name(), "bounds")) - { - throw layout_syntax_error(util::string_format("unknown group element %s", itemnode->get_name())); - } + environment local(env); + found->second.resolve_bounds(local, groupmap, seen); + union_render_bounds(m_bounds, found->second.m_bounds); + } + else if (!strcmp(itemnode->get_name(), "repeat")) + { + int const count(env.get_attribute_int(*itemnode, "count", -1)); + if (0 >= count) + throw layout_syntax_error("repeat must have positive integer count attribute"); + environment local(env); + for (int i = 0; !m_bounds_resolved && (count > i); ++i) + { + resolve_bounds(local, *itemnode, groupmap, seen, true, !i); + local.increment_parameters(); } } - m_bounds_resolved = true; + else + { + throw layout_syntax_error(util::string_format("unknown group element %s", itemnode->get_name())); + } } + if (!repeat) + m_bounds_resolved = true; } @@ -663,15 +1180,15 @@ class layout_element::image_component : public component { public: // construction/destruction - image_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + image_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) , m_hasalpha(false) { if (dirname != nullptr) m_dirname = dirname; - m_imagefile = xml_get_attribute_string_with_subst(machine, compnode, "file", ""); - m_alphafile = xml_get_attribute_string_with_subst(machine, compnode, "alphafile", ""); - m_file = std::make_unique<emu_file>(machine.options().art_path(), OPEN_FLAG_READ); + m_imagefile = env.get_attribute_string(compnode, "file", ""); + m_alphafile = env.get_attribute_string(compnode, "alphafile", ""); + m_file = std::make_unique<emu_file>(env.machine().options().art_path(), OPEN_FLAG_READ); } protected: @@ -745,8 +1262,8 @@ class layout_element::rect_component : public component { public: // construction/destruction - rect_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + rect_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -791,8 +1308,8 @@ class layout_element::disk_component : public component { public: // construction/destruction - disk_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + disk_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -852,11 +1369,11 @@ class layout_element::text_component : public component { public: // construction/destruction - text_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + text_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { - m_string = xml_get_attribute_string_with_subst(machine, compnode, "string", ""); - m_textalign = xml_get_attribute_int_with_subst(machine, compnode, "align", 0); + m_string = env.get_attribute_string(compnode, "string", ""); + m_textalign = env.get_attribute_int(compnode, "align", 0); } protected: @@ -880,8 +1397,8 @@ class layout_element::led7seg_component : public component { public: // construction/destruction - led7seg_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + led7seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -942,8 +1459,8 @@ class layout_element::led8seg_gts1_component : public component { public: // construction/destruction - led8seg_gts1_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + led8seg_gts1_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -1010,8 +1527,8 @@ class layout_element::led14seg_component : public component { public: // construction/destruction - led14seg_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + led14seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -1122,8 +1639,8 @@ class layout_element::led16seg_component : public component { public: // construction/destruction - led16seg_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + led16seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -1244,8 +1761,8 @@ class layout_element::led14segsc_component : public component { public: // construction/destruction - led14segsc_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + led14segsc_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -1365,8 +1882,8 @@ class layout_element::led16segsc_component : public component { public: // construction/destruction - led16segsc_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + led16segsc_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { } @@ -1496,8 +2013,8 @@ class layout_element::dotmatrix_component : public component { public: // construction/destruction - dotmatrix_component(int dots, running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + dotmatrix_component(int dots, environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) , m_dots(dots) { } @@ -1537,11 +2054,11 @@ class layout_element::simplecounter_component : public component { public: // construction/destruction - simplecounter_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) - , m_digits(xml_get_attribute_int_with_subst(machine, compnode, "digits", 2)) - , m_textalign(xml_get_attribute_int_with_subst(machine, compnode, "align", 0)) - , m_maxstate(xml_get_attribute_int_with_subst(machine, compnode, "maxstate", 999)) + simplecounter_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) + , m_digits(env.get_attribute_int(compnode, "digits", 2)) + , m_textalign(env.get_attribute_int(compnode, "align", 0)) + , m_maxstate(env.get_attribute_int(compnode, "maxstate", 999)) { } @@ -1572,13 +2089,13 @@ class layout_element::reel_component : public component public: // construction/destruction - reel_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) - : component(machine, compnode, dirname) + reel_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + : component(env, compnode, dirname) { for (auto & elem : m_hasalpha) elem = false; - std::string symbollist = xml_get_attribute_string_with_subst(machine, compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"); + std::string symbollist = env.get_attribute_string(compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"); // split out position names from string and figure out our number of symbols int location; @@ -1608,7 +2125,7 @@ public: m_imagefile[i] = m_imagefile[i].substr(location+1, m_imagefile[i].length()-(location-1)); //m_alphafile[i] = - m_file[i] = std::make_unique<emu_file>(machine.options().art_path(), OPEN_FLAG_READ); + m_file[i] = std::make_unique<emu_file>(env.machine().options().art_path(), OPEN_FLAG_READ); } else { @@ -1618,10 +2135,10 @@ public: } } - m_stateoffset = xml_get_attribute_int_with_subst(machine, compnode, "stateoffset", 0); - m_numsymbolsvisible = xml_get_attribute_int_with_subst(machine, compnode, "numsymbolsvisible", 3); - m_reelreversed = xml_get_attribute_int_with_subst(machine, compnode, "reelreversed", 0); - m_beltreel = xml_get_attribute_int_with_subst(machine, compnode, "beltreel", 0); + m_stateoffset = env.get_attribute_int(compnode, "stateoffset", 0); + m_numsymbolsvisible = env.get_attribute_int(compnode, "numsymbolsvisible", 3); + m_reelreversed = env.get_attribute_int(compnode, "reelreversed", 0); + m_beltreel = env.get_attribute_int(compnode, "beltreel", 0); } protected: @@ -1631,7 +2148,7 @@ protected: { if (m_beltreel) { - draw_beltreel(machine,dest,bounds,state); + draw_beltreel(machine, dest, bounds, state); return; } @@ -1996,9 +2513,9 @@ private: //------------------------------------------------- template <typename T> -layout_element::component::ptr layout_element::make_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) +layout_element::component::ptr layout_element::make_component(environment &env, util::xml::data_node const &compnode, const char *dirname) { - return std::make_unique<T>(machine, compnode, dirname); + return std::make_unique<T>(env, compnode, dirname); } @@ -2008,9 +2525,9 @@ layout_element::component::ptr layout_element::make_component(running_machine &m //------------------------------------------------- template <int D> -layout_element::component::ptr layout_element::make_dotmatrix_component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) +layout_element::component::ptr layout_element::make_dotmatrix_component(environment &env, util::xml::data_node const &compnode, const char *dirname) { - return std::make_unique<dotmatrix_component>(D, machine, compnode, dirname); + return std::make_unique<dotmatrix_component>(D, env, compnode, dirname); } @@ -2071,13 +2588,13 @@ layout_element::texture &layout_element::texture::operator=(texture &&that) // component - constructor //------------------------------------------------- -layout_element::component::component(running_machine &machine, util::xml::data_node const &compnode, const char *dirname) +layout_element::component::component(environment &env, util::xml::data_node const &compnode, const char *dirname) : m_state(0) { // fetch common data - m_state = xml_get_attribute_int_with_subst(machine, compnode, "state", -1); - parse_bounds(machine, compnode.get_child("bounds"), m_bounds); - parse_color(machine, compnode.get_child("color"), m_color); + m_state = env.get_attribute_int(compnode, "state", -1); + env.parse_bounds(compnode.get_child("bounds"), m_bounds); + env.parse_color(compnode.get_child("color"), m_color); } @@ -2389,25 +2906,21 @@ void layout_element::component::apply_skew(bitmap_argb32 &dest, int skewwidth) //------------------------------------------------- layout_view::layout_view( - device_t &device, + environment &env, util::xml::data_node const &viewnode, element_map &elemmap, - group_map const &groupmap) - : m_name(make_name(device, viewnode)) + group_map &groupmap) + : m_name(make_name(env, viewnode)) , m_aspect(1.0f) , m_scraspect(1.0f) { - // if we have a bounds item, load it - util::xml::data_node const *const boundsnode = viewnode.get_child("bounds"); m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0; - if (boundsnode) - parse_bounds(device.machine(), boundsnode, m_expbounds); - - // load items - add_items(device, viewnode, elemmap, groupmap, render_bounds{ 0.0f, 0.0f, 1.0f, 1.0f }); - - // recompute the data for the view based on a default layer config + environment local(env); + local.set_parameter("viewname", std::string(m_name)); + add_items(local, viewnode, elemmap, groupmap, render_bounds{ 0.0f, 0.0f, 1.0f, 1.0f }, true, false, true); recompute(render_layer_config()); + for (group_map::value_type &group : groupmap) + group.second.set_bounds_unresolved(); } @@ -2561,76 +3074,125 @@ void layout_view::resolve_tags() //------------------------------------------------- void layout_view::add_items( - device_t &device, + environment &env, util::xml::data_node const &parentnode, element_map &elemmap, - group_map const &groupmap, - render_bounds const &transform) -{ + group_map &groupmap, + render_bounds const &transform, + bool root, + bool repeat, + bool init) +{ + bool envaltered(false); + bool unresolved(true); for (util::xml::data_node const *itemnode = parentnode.get_first_child(); itemnode; itemnode = itemnode->get_next_sibling()) { - if (!strcmp(itemnode->get_name(), "backdrop")) + if (!strcmp(itemnode->get_name(), "bounds")) + { + // set explicit bounds + if (root) + env.parse_bounds(itemnode, m_expbounds); + } + else if (!strcmp(itemnode->get_name(), "param")) { - m_backdrop_list.emplace_back(device, *itemnode, elemmap, transform); + envaltered = true; + if (!unresolved) + { + unresolved = true; + for (group_map::value_type &group : groupmap) + group.second.set_bounds_unresolved(); + } + if (!repeat) + env.set_parameter(*itemnode); + else + env.set_repeat_parameter(*itemnode, init); + } + else if (!strcmp(itemnode->get_name(), "backdrop")) + { + m_backdrop_list.emplace_back(env, *itemnode, elemmap, transform); } else if (!strcmp(itemnode->get_name(), "screen")) { - m_screen_list.emplace_back(device, *itemnode, elemmap, transform); + m_screen_list.emplace_back(env, *itemnode, elemmap, transform); } else if (!strcmp(itemnode->get_name(), "overlay")) { - m_overlay_list.emplace_back(device, *itemnode, elemmap, transform); + m_overlay_list.emplace_back(env, *itemnode, elemmap, transform); } else if (!strcmp(itemnode->get_name(), "bezel")) { - m_bezel_list.emplace_back(device, *itemnode, elemmap, transform); + m_bezel_list.emplace_back(env, *itemnode, elemmap, transform); } else if (!strcmp(itemnode->get_name(), "cpanel")) { - m_cpanel_list.emplace_back(device, *itemnode, elemmap, transform); + m_cpanel_list.emplace_back(env, *itemnode, elemmap, transform); } else if (!strcmp(itemnode->get_name(), "marquee")) { - m_marquee_list.emplace_back(device, *itemnode, elemmap, transform); + m_marquee_list.emplace_back(env, *itemnode, elemmap, transform); } else if (!strcmp(itemnode->get_name(), "group")) { - char const *ref(xml_get_attribute_string_with_subst(device.machine(), *itemnode, "ref", nullptr)); + char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr)); if (!ref) throw layout_syntax_error("nested group must have ref attribute"); - group_map::const_iterator const found(groupmap.find(ref)); + group_map::iterator const found(groupmap.find(ref)); if (groupmap.end() == found) throw layout_syntax_error(util::string_format("unable to find group %s", ref)); + unresolved = false; + found->second.resolve_bounds(env, groupmap); render_bounds grouptrans(transform); util::xml::data_node const *const itemboundsnode(itemnode->get_child("bounds")); if (itemboundsnode) { render_bounds itembounds; - parse_bounds(device.machine(), itemboundsnode, itembounds); + env.parse_bounds(itemboundsnode, itembounds); grouptrans = found->second.make_transform(itembounds, transform); } - add_items(device, found->second.get_groupnode(), elemmap, groupmap, grouptrans); + environment local(env); + add_items(local, found->second.get_groupnode(), elemmap, groupmap, grouptrans, false, false, true); } - else if (strcmp(itemnode->get_name(), "bounds")) + else if (!strcmp(itemnode->get_name(), "repeat")) + { + int const count(env.get_attribute_int(*itemnode, "count", -1)); + if (0 >= count) + throw layout_syntax_error("repeat must have positive integer count attribute"); + environment local(env); + for (int i = 0; count > i; ++i) + { + add_items(local, *itemnode, elemmap, groupmap, transform, false, true, !i); + local.increment_parameters(); + } + } + else { throw layout_syntax_error(util::string_format("unknown view item %s", itemnode->get_name())); } } + + if (envaltered && !unresolved) + { + for (group_map::value_type &group : groupmap) + group.second.set_bounds_unresolved(); + } } -std::string layout_view::make_name(device_t &device, util::xml::data_node const &viewnode) +std::string layout_view::make_name(environment &env, util::xml::data_node const &viewnode) { - char const *const name(xml_get_attribute_string_with_subst(device.machine(), viewnode, "name", "")); - if (&device == &device.machine().root_device()) + char const *const name(env.get_attribute_string(viewnode, "name", nullptr)); + if (!name) + throw layout_syntax_error("view must have name attribute"); + + if (env.is_root_device()) { return name; } else { - char const *tag(device.tag()); + char const *tag(env.device().tag()); if (':' == *tag) ++tag; return util::string_format("%s %s", tag, name); @@ -2648,22 +3210,21 @@ std::string layout_view::make_name(device_t &device, util::xml::data_node const //------------------------------------------------- layout_view::item::item( - device_t &device, + environment &env, util::xml::data_node const &itemnode, element_map &elemmap, render_bounds const &transform) : m_element(nullptr) - , m_output(device, xml_get_attribute_string_with_subst(device.machine(), itemnode, "name", "")) - , m_have_output(xml_get_attribute_string_with_subst(device.machine(), itemnode, "name", "")[0]) - , m_input_tag(xml_get_attribute_string_with_subst(device.machine(), itemnode, "inputtag", "")) + , m_output(env.device(), env.get_attribute_string(itemnode, "name", "")) + , m_have_output(env.get_attribute_string(itemnode, "name", "")[0]) + , m_input_tag(env.get_attribute_string(itemnode, "inputtag", "")) , m_input_port(nullptr) , m_input_mask(0) , m_screen(nullptr) , m_orientation(ROT0) { // find the associated element - running_machine &machine(device.machine()); - char const *const name = xml_get_attribute_string_with_subst(machine, itemnode, "element", nullptr); + char const *const name(env.get_attribute_string(itemnode, "element", nullptr)); if (name) { // search the list of elements for a match, error if not found @@ -2679,24 +3240,24 @@ layout_view::item::item( m_output.resolve(); // fetch common data - int index = xml_get_attribute_int_with_subst(machine, itemnode, "index", -1); + int index = env.get_attribute_int(itemnode, "index", -1); if (index != -1) - m_screen = screen_device_iterator(machine.root_device()).byindex(index); - m_input_mask = xml_get_attribute_int_with_subst(machine, itemnode, "inputmask", 0); + m_screen = screen_device_iterator(env.machine().root_device()).byindex(index); + m_input_mask = env.get_attribute_int(itemnode, "inputmask", 0); if (m_have_output && m_element) m_output = m_element->default_state(); - parse_bounds(machine, itemnode.get_child("bounds"), m_rawbounds); + env.parse_bounds(itemnode.get_child("bounds"), m_rawbounds); render_bounds_transform(m_rawbounds, transform); - parse_color(machine, itemnode.get_child("color"), m_color); - parse_orientation(machine, itemnode.get_child("orientation"), m_orientation); + env.parse_color(itemnode.get_child("color"), m_color); + env.parse_orientation(itemnode.get_child("orientation"), m_orientation); // sanity checks if (strcmp(itemnode.get_name(), "screen") == 0) { if (itemnode.has_attribute("tag")) { - char const *const tag(xml_get_attribute_string_with_subst(machine, itemnode, "tag", "")); - m_screen = dynamic_cast<screen_device *>(device.subdevice(tag)); + char const *const tag(env.get_attribute_string(itemnode, "tag", "")); + m_screen = dynamic_cast<screen_device *>(env.device().subdevice(tag)); if (!m_screen) throw layout_reference_error(util::string_format("invalid screen tag '%d'", tag)); } @@ -2711,7 +3272,7 @@ layout_view::item::item( } if (has_input()) - m_input_port = device.ioport(m_input_tag.c_str()); + m_input_port = env.device().ioport(m_input_tag.c_str()); } @@ -2790,7 +3351,7 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, : m_elemmap() , m_viewlist() { - running_machine &machine(device.machine()); + emu::render::detail::layout_environment env(device); try { // find the layout node @@ -2803,13 +3364,17 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, if (version != LAYOUT_VERSION) throw layout_syntax_error(util::string_format("unsupported version %d", version)); + // parse all the parameters + for (util::xml::data_node const *elemnode = mamelayoutnode->get_child("param") ; elemnode; elemnode = elemnode->get_next_sibling("param")) + env.set_parameter(*elemnode); + // parse all the elements for (util::xml::data_node const *elemnode = mamelayoutnode->get_child("element"); elemnode; elemnode = elemnode->get_next_sibling("element")) { - char const *const name(xml_get_attribute_string_with_subst(machine, *elemnode, "name", nullptr)); + char const *const name(env.get_attribute_string(*elemnode, "name", nullptr)); if (!name) throw layout_syntax_error("element lacks name attribute"); - if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(machine, *elemnode, dirname)).second) + if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(env, *elemnode, dirname)).second) throw layout_syntax_error(util::string_format("duplicate element name %s", name)); } @@ -2817,14 +3382,12 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, group_map groupmap; for (util::xml::data_node const *groupnode = mamelayoutnode->get_child("group"); groupnode; groupnode = groupnode->get_next_sibling("group")) { - char const *const name(xml_get_attribute_string_with_subst(machine, *groupnode, "name", nullptr)); + char const *const name(env.get_attribute_string(*groupnode, "name", nullptr)); if (!name) throw layout_syntax_error("group lacks name attribute"); - if (!groupmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(machine, *groupnode)).second) + if (!groupmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(*groupnode)).second) throw layout_syntax_error(util::string_format("duplicate group name %s", name)); } - for (group_map::value_type &group : groupmap) - group.second.resolve_bounds(groupmap); // parse all the views for (util::xml::data_node const *viewnode = mamelayoutnode->get_child("view"); viewnode != nullptr; viewnode = viewnode->get_next_sibling("view")) @@ -2835,11 +3398,11 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, // if the error is allowed to propagate, the entire layout is dropped so you can't select the useful view try { - m_viewlist.emplace_back(device, *viewnode, m_elemmap, groupmap); + m_viewlist.emplace_back(env, *viewnode, m_elemmap, groupmap); } catch (layout_reference_error const &err) { - osd_printf_warning("Error instantiating layout view %s: %s\n", xml_get_attribute_string_with_subst(machine, *viewnode, "name", ""), err.what()); + osd_printf_warning("Error instantiating layout view %s: %s\n", env.get_attribute_string(*viewnode, "name", ""), err.what()); } } } |