Update to v094r18 release.

byuu says:

Okay yeah, lots of SNES coprocessor games were horribly broken. They
should be fixed now with the below changes:

Old syntax:

    auto programROM = root["rom[0]/name"].text();
    auto dataROM = root["rom[1]/name"].text();
    load_memory(root["ram[0]"]);

New syntax:

    auto rom = root.find("rom");
    auto ram = root.find("ram");
    auto programROM = rom(0)["name"].text();
    auto dataROM = rom(1)["name"].text();
    load_memory(ram(0));

Since I'm now relying on the XShm driver, which is multi-threaded, I'm
now compiling higan with -fopenmp. On FreeBSD, this requires linking
with -Wl,-rpath=/usr/local/lib -Wl,-rpath=/usr/local/lib/gcc49 to get
the right version of GOMP.

This gives a pretty nice speed boost for XShm, I go from around 101fps
to 111fps at 4x scale on the accuracy profile. The combination of
inlining the accuracy-PPU and parallelizing the XShm renderer about
evenly compensates now for the ~20% CPU overclock I gave up a while ago.

The WIP also has some other niceties from the newer version of nall.
Most noticeably, cheat code database searching is now instantaneous. No
more 3-second stall.
This commit is contained in:
Tim Allen 2015-05-16 17:37:13 +10:00
parent 39ca8a2fab
commit fc8eba133d
14 changed files with 263 additions and 218 deletions

View File

@ -12,8 +12,8 @@ target := tomoko
# console := true
# compiler
flags += -I. -O3
link +=
flags += -I. -O3 -fopenmp
link += -fopenmp
objects := libco
# profile-guided optimization mode
@ -39,15 +39,20 @@ ifeq ($(platform),windows)
link += -mwindows
endif
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
link += -Wl,-enable-auto-import
link += -Wl,-enable-runtime-pseudo-reloc
else ifeq ($(platform),macosx)
flags += -march=native
else ifeq ($(platform),linux)
flags += -march=native
link += -Wl,-export-dynamic -lX11 -lXext -ldl
link += -Wl,-export-dynamic
link += -lX11 -lXext -ldl
else ifeq ($(platform),bsd)
flags += -march=native
link += -Wl,-export-dynamic -lX11 -lXext
link += -Wl,-rpath=/usr/local/lib
link += -Wl,-rpath=/usr/local/lib/gcc49
link += -Wl,-export-dynamic
link += -lX11 -lXext
else
$(error unsupported platform.)
endif

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "higan";
static const char Version[] = "094.17";
static const char Version[] = "094.18";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/";

View File

@ -94,10 +94,10 @@ Board::Board(Markup::Node& document) {
auto crom = cartridge["chr/rom"];
auto cram = cartridge["chr/ram"];
prgrom.size = prom["size"].text().numeral();
prgram.size = pram["size"].text().numeral();
chrrom.size = crom["size"].text().numeral();
chrram.size = cram["size"].text().numeral();
prgrom.size = prom["size"].decimal();
prgram.size = pram["size"].decimal();
chrrom.size = crom["size"].decimal();
chrram.size = cram["size"].decimal();
if(prgrom.size) prgrom.data = new uint8[prgrom.size]();
if(prgram.size) prgram.data = new uint8[prgram.size]();

View File

@ -74,9 +74,9 @@ void Cartridge::load(System::Revision revision) {
//Super Game Boy core loads memory from Super Famicom core
if(revision != System::Revision::SuperGameBoy) {
if(rom["name"]) interface->loadRequest(ID::ROM, rom["name"].text());
if(ram["name"]) interface->loadRequest(ID::RAM, ram["name"].text());
if(ram["name"]) memory.append({ID::RAM, ram["name"].text()});
if(auto name = rom["name"].text()) interface->loadRequest(ID::ROM, name);
if(auto name = ram["name"].text()) interface->loadRequest(ID::RAM, name);
if(auto name = ram["name"].text()) memory.append({ID::RAM, name});
}
information.romsize = rom["size"].decimal();

View File

@ -1,152 +0,0 @@
#ifndef NALL_COMPOSITOR_HPP
#define NALL_COMPOSITOR_HPP
#include <nall/intrinsics.hpp>
namespace nall {
struct compositor {
inline static bool enabled();
inline static bool enable(bool status);
#if defined(PLATFORM_XORG)
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
inline static Compositor detect();
inline static bool enabled_metacity();
inline static bool enable_metacity(bool status);
inline static bool enabled_xfwm4();
inline static bool enable_xfwm4(bool status);
#endif
};
#if defined(PLATFORM_XORG)
//Metacity
bool compositor::enabled_metacity() {
FILE* fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
if(!fp) return false;
char buffer[512];
if(!fgets(buffer, sizeof buffer, fp)) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable_metacity(bool status) {
FILE* fp;
if(status) {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
} else {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
}
if(!fp) return false;
pclose(fp);
return true;
}
//Xfwm4
bool compositor::enabled_xfwm4() {
FILE* fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
if(!fp) return false;
char buffer[512];
if(!fgets(buffer, sizeof buffer, fp)) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable_xfwm4(bool status) {
FILE* fp;
if(status) {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
} else {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
}
if(!fp) return false;
pclose(fp);
return true;
}
//General
compositor::Compositor compositor::detect() {
Compositor result = Compositor::Unknown;
FILE* fp;
char buffer[512];
fp = popen("pidof metacity", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
pclose(fp);
fp = popen("pidof xfwm4", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
pclose(fp);
return result;
}
bool compositor::enabled() {
switch(detect()) {
case Compositor::Metacity: return enabled_metacity();
case Compositor::Xfwm4: return enabled_xfwm4();
default: return false;
}
}
bool compositor::enable(bool status) {
switch(detect()) {
case Compositor::Metacity: return enable_metacity(status);
case Compositor::Xfwm4: return enable_xfwm4(status);
default: return false;
}
}
#elif defined(PLATFORM_WINDOWS)
bool compositor::enabled() {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == nullptr) module = LoadLibraryW(L"dwmapi");
if(module == nullptr) return false;
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
if(pDwmIsCompositionEnabled == nullptr) return false;
BOOL result;
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
return result;
}
bool compositor::enable(bool status) {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == nullptr) module = LoadLibraryW(L"dwmapi");
if(module == nullptr) return false;
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
if(pDwmEnableComposition == nullptr) return false;
if(pDwmEnableComposition(status) != S_OK) return false;
return true;
}
#else
bool compositor::enabled() {
return false;
}
bool compositor::enable(bool) {
return false;
}
#endif
}
#endif

159
nall/database/sqlite3.hpp Normal file
View File

@ -0,0 +1,159 @@
#ifndef NALL_DATABASE_SQLITE3_HPP
#define NALL_DATABASE_SQLITE3_HPP
/* SQLite3 C++ RAII wrapper for nall
*
* Note on code below: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects
*/
#include <sqlite3.h>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall { namespace Database {
struct SQLite3 {
struct Statement {
using type = Statement;
Statement(sqlite3_stmt* stmt) : stmt(stmt) {}
Statement(const Statement& source) { operator=(source); }
Statement(Statement&& source) { operator=(move(source)); }
auto operator=(const Statement& source) -> type& { stmt = source.stmt, stepped = source.stepped, response = source.response; return *this; }
auto operator=(Statement&& source) -> type& { operator=(source); source.stmt = nullptr, source.stepped = false, source.response = SQLITE_OK; return *this; }
explicit operator bool() { return !finished(); }
auto bind(unsigned column, nullptr_t) -> type& { sqlite3_bind_null(stmt, 1 + column); return *this; }
auto bind(unsigned column, int32_t value) -> type& { sqlite3_bind_int(stmt, 1 + column, value); return *this; }
auto bind(unsigned column, uint32_t value) -> type& { sqlite3_bind_int(stmt, 1 + column, value); return *this; }
auto bind(unsigned column, int64_t value) -> type& { sqlite3_bind_int64(stmt, 1 + column, value); return *this; }
auto bind(unsigned column, uint64_t value) -> type& { sqlite3_bind_int64(stmt, 1 + column, value); return *this; }
auto bind(unsigned column, double value) -> type& { sqlite3_bind_double(stmt, 1 + column, value); return *this; }
auto bind(unsigned column, const string& value) -> type& { sqlite3_bind_text(stmt, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
auto bind(unsigned column, const vector<uint8_t>& value) -> type& { sqlite3_bind_blob(stmt, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
auto integer(unsigned column) -> int64_t { return sqlite3_column_int64(handle(), column); }
auto decimal(unsigned column) -> uint64_t { return sqlite3_column_int64(handle(), column); }
auto real(unsigned column) -> double { return sqlite3_column_double(handle(), column); }
auto text(unsigned column) -> string {
string result;
if(auto text = sqlite3_column_text(handle(), column)) {
result.resize(sqlite3_column_bytes(handle(), column));
memory::copy(result.pointer(), text, result.size());
}
return result;
}
auto data(unsigned column) -> vector<uint8_t> {
vector<uint8_t> result;
if(auto data = sqlite3_column_blob(handle(), column)) {
result.resize(sqlite3_column_bytes(handle(), column));
memory::copy(result.data(), data, result.size());
}
return result;
}
auto integer() -> int64_t { return integer(column++); }
auto decimal() -> uint64_t { return decimal(column++); }
auto real() -> double { return real(column++); }
auto text() -> string { return text(column++); }
auto data() -> vector<uint8_t> { return data(column++); }
protected:
auto step() -> bool { stepped = true; response = sqlite3_step(stmt); return response == SQLITE_ROW; }
auto handle() -> sqlite3_stmt* { if(!stepped) step(); return stmt; }
auto finished() -> bool { handle(); return response != SQLITE_ROW; }
auto finalize() -> void { handle(); sqlite3_finalize(stmt); stmt = nullptr; }
private:
sqlite3_stmt* stmt = nullptr;
bool stepped = false;
int response = SQLITE_OK;
unsigned column = 0; //this value is not copied/moved via operator=
};
struct Query : Statement {
Query(sqlite3_stmt* stmt) : Statement(stmt) {}
Query(const Query& source) : Statement(source) {}
Query(Query&& source) : Statement(move(source)) {}
~Query() { finalize(); }
auto operator=(const Query& source) -> Query& { return Statement::operator=(source), *this; }
auto operator=(Query&& source) -> Query& { return Statement::operator=(move(source)), *this; }
explicit operator bool() { return !finished(); }
using Statement::step;
using Statement::finalize;
struct Iterator {
auto operator*() -> Statement { return Statement(query); }
auto operator!=(const Iterator& source) const -> bool { return finished != source.finished; }
auto operator++() -> Iterator& { finished = !query.step(); return *this; }
Iterator(Query& query, bool finished) : query(query), finished(finished) {}
protected:
Query& query;
bool finished = false;
};
auto begin() -> Iterator { return Iterator(*this, finished()); }
auto end() -> Iterator { return Iterator(*this, true); }
};
SQLite3() = default;
SQLite3(const string& filename) { open(filename); }
~SQLite3() { close(); }
explicit operator bool() const { return database; }
auto open(const string& filename) -> bool {
close();
sqlite3_open(filename, &database);
return database;
}
auto close() -> void {
sqlite3_close(database);
database = nullptr;
}
template<typename... P> auto execute(const string& statement, P&&... p) -> Query {
if(!database) return {nullptr};
sqlite3_stmt* stmt = nullptr;
sqlite3_prepare_v2(database, statement.data(), statement.size(), &stmt, nullptr);
if(!stmt) {
if(debug) print("[sqlite3_prepare_v2] ", sqlite3_errmsg(database), "\n");
return {nullptr};
}
Query query{stmt};
_bind(query, 0, forward<P>(p)...);
return query;
}
auto lastInsertID() const -> uint64_t {
return database ? sqlite3_last_insert_rowid(database) : 0;
}
auto setDebug(bool debug = true) -> void {
this->debug = debug;
}
protected:
auto _bind(Query&, unsigned) -> void {}
template<typename T, typename... P> auto _bind(Query& query, unsigned column, const T& value, P&&... p) -> void {
query.bind(column, value);
_bind(query, column + 1, forward<P>(p)...);
}
bool debug = false;
sqlite3* database = nullptr;
};
}}
#endif

View File

@ -6,10 +6,10 @@
#include <nall/string.hpp>
#include <nall/hash/crc32.hpp>
namespace nall {
namespace nall { namespace Encode {
struct zip {
zip(const string& filename) {
struct ZIP {
ZIP(const string& filename) {
fp.open(filename, file::mode::write);
time_t currentTime = time(nullptr);
tm* info = localtime(&currentTime);
@ -40,7 +40,7 @@ struct zip {
fp.write(data, size); //file data
}
~zip() {
~ZIP() {
//central directory
unsigned baseOffset = fp.offset();
for(auto& entry : directory) {
@ -90,6 +90,6 @@ protected:
vector<entry_t> directory;
};
}
}}
#endif

View File

@ -21,7 +21,7 @@
#include <nall/bit.hpp>
#include <nall/bitvector.hpp>
#include <nall/bmp.hpp>
//#include <nall/config.hpp>
#include <nall/config.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
@ -55,7 +55,6 @@
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/zip.hpp>
#include <nall/decode/bmp.hpp>
#include <nall/decode/gzip.hpp>
#include <nall/decode/inflate.hpp>

View File

@ -27,6 +27,11 @@
#include <stdint.h>
#endif
#if defined(__SIZEOF_INT128__)
using int128_t = signed __int128;
using uint128_t = unsigned __int128;
#endif
namespace nall {
static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" );

View File

@ -211,8 +211,6 @@ public:
auto operator> (const char* s) const -> bool { return strcmp(data(), s) > 0; }
auto operator>=(const char* s) const -> bool { return strcmp(data(), s) >= 0; }
auto operator+=(const string& s) -> type& { return append(s); }
string(const string& source) : string() { operator=(source); }
string(string&& source) : string() { operator=(std::move(source)); }

View File

@ -14,26 +14,26 @@ using SharedNode = shared_pointer<ManagedNode>;
struct ManagedNode : Markup::ManagedNode {
protected:
//test to verify if a valid character for a node name
bool valid(char p) const { //A-Z, a-z, 0-9, -.
auto valid(char p) const -> bool { //A-Z, a-z, 0-9, -.
return p - 'A' < 26u || p - 'a' < 26u || p - '0' < 10u || p - '-' < 2u;
}
//determine indentation level, without incrementing pointer
unsigned readDepth(const char* p) {
auto readDepth(const char* p) -> unsigned {
unsigned depth = 0;
while(p[depth] == '\t' || p[depth] == ' ') depth++;
return depth;
}
//determine indentation level
unsigned parseDepth(const char*& p) {
auto parseDepth(const char*& p) -> unsigned {
unsigned depth = readDepth(p);
p += depth;
return depth;
}
//read name
void parseName(const char*& p) {
auto parseName(const char*& p) -> void {
unsigned length = 0;
while(valid(p[length])) length++;
if(length == 0) throw "Invalid node name";
@ -41,7 +41,7 @@ protected:
p += length;
}
void parseData(const char*& p) {
auto parseData(const char*& p) -> void {
if(*p == '=' && *(p + 1) == '\"') {
unsigned length = 2;
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
@ -63,7 +63,7 @@ protected:
}
//read all attributes for a node
void parseAttributes(const char*& p) {
auto parseAttributes(const char*& p) -> void {
while(*p && *p != '\n') {
if(*p != ' ') throw "Invalid node name";
while(*p == ' ') p++; //skip excess spaces
@ -81,7 +81,7 @@ protected:
}
//read a node and all of its child nodes
void parseNode(const lstring& text, unsigned& y) {
auto parseNode(const lstring& text, unsigned& y) -> void {
const char* p = text[y++];
_metadata = parseDepth(p);
parseName(p);
@ -106,22 +106,32 @@ protected:
}
//read top-level nodes
void parse(const string& document) {
lstring text = string{document}.replace("\r", "").split("\n");
//remove empty lines and comment lines
for(unsigned y = 0; y < text.size();) {
unsigned x = 0;
auto parse(string document) -> void {
//in order to simplify the parsing logic; we do an initial pass to normalize the data
//the below code will turn '\r\n' into '\n'; skip empty lines; and skip comment lines
char* p = document.pointer(), *output = p;
while(*p) {
char* origin = p;
bool empty = true;
while(x < text[y].size()) {
if(text[y][x] == ' ' || text[y][x] == '\t') { x++; continue; }
empty = (text[y][x + 0] == '/' && text[y][x + 1] == '/');
while(*p) {
//scan for first non-whitespace character. if it's a line feed or comment; skip the line
if(p[0] == ' ' || p[0] == '\t') { p++; continue; }
empty = p[0] == '\r' || p[0] == '\n' || (p[0] == '/' && p[1] == '/');
break;
}
if(empty) text.remove(y);
else y++;
}
while(*p) {
if(p[0] == '\r') p[0] = '\n'; //turns '\r\n' into '\n\n' (second '\n' will be skipped)
if(*p++ == '\n') break; //include '\n' in the output to be copied
}
if(empty) continue;
memory::move(output, origin, p - origin);
output += p - origin;
}
document.resize(document.size() - (p - output)).rtrim("\n");
if(document.size() == 0) return; //empty document
auto text = document.split("\n");
unsigned y = 0;
while(y < text.size()) {
SharedNode node(new ManagedNode);

View File

@ -75,7 +75,7 @@ auto configpath() -> string {
// c:/users/username/appdata/local/
auto localpath() -> string {
#if defined(PLATFORM_WINDOWS)
whcar_t path[PATH_MAX] = L"";
wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
string result = (const char*)utf8_t(path);
result.transform("\\", "/");

View File

@ -124,9 +124,9 @@ void Cartridge::load_super_game_boy() {
GameBoy::cartridge.information.markup = information.markup.gameBoy;
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy);
if(rom["name"]) interface->loadRequest(ID::SuperGameBoyROM, rom["name"].text());
if(ram["name"]) interface->loadRequest(ID::SuperGameBoyRAM, ram["name"].text());
if(ram["name"]) memory.append({ID::SuperGameBoyRAM, ram["name"].text()});
if(auto name = rom["name"].text()) interface->loadRequest(ID::SuperGameBoyROM, name);
if(auto name = ram["name"].text()) interface->loadRequest(ID::SuperGameBoyRAM, name);
if(auto name = ram["name"].text()) memory.append({ID::SuperGameBoyRAM, name});
}
void Cartridge::load_satellaview() {

View File

@ -147,7 +147,7 @@ void Cartridge::parse_markup_sufamiturbo(Markup::Node root, bool slot) {
}
for(auto node : root.find("map")) {
SufamiTurboCartridge &cart = (slot == 0 ? sufamiturboA : sufamiturboB);
SufamiTurboCartridge& cart = (slot == 0 ? sufamiturboA : sufamiturboB);
if(node["id"].text() == "rom") {
if(cart.rom.size() == 0) continue;
@ -234,9 +234,12 @@ void Cartridge::parse_markup_sa1(Markup::Node root) {
if(!root) return;
has_sa1 = true;
parse_markup_memory(sa1.rom, root["rom"], ID::SA1ROM, false);
parse_markup_memory(sa1.bwram, root["ram"].at(0), ID::SA1BWRAM, true);
parse_markup_memory(sa1.iram, root["ram"].at(1), ID::SA1IRAM, true);
auto rom = root.find("rom");
auto ram = root.find("ram");
parse_markup_memory(sa1.rom, rom(0), ID::SA1ROM, false);
parse_markup_memory(sa1.bwram, ram(0), ID::SA1BWRAM, true);
parse_markup_memory(sa1.iram, ram(1), ID::SA1IRAM, true);
for(auto node : root.find("map")) {
if(node["id"].text() == "io") {
@ -270,8 +273,11 @@ void Cartridge::parse_markup_superfx(Markup::Node root) {
if(!root) return;
has_superfx = true;
parse_markup_memory(superfx.rom, root["rom"], ID::SuperFXROM, false);
parse_markup_memory(superfx.ram, root["ram"], ID::SuperFXRAM, true);
auto rom = root.find("rom");
auto ram = root.find("ram");
parse_markup_memory(superfx.rom, rom(0), ID::SuperFXROM, false);
parse_markup_memory(superfx.ram, ram(0), ID::SuperFXRAM, true);
for(auto node : root.find("map")) {
if(node["id"].text() == "io") {
@ -300,9 +306,12 @@ void Cartridge::parse_markup_armdsp(Markup::Node root) {
if(!root) return;
has_armdsp = true;
string programROMName = root["rom"].at(0)["name"].text();
string dataROMName = root["rom"].at(1)["name"].text();
string dataRAMName = root["ram/name"].text();
auto rom = root.find("rom");
auto ram = root.find("ram");
string programROMName = rom(0)["name"].text();
string dataROMName = rom(1)["name"].text();
string dataRAMName = ram(0)["name"].text();
interface->loadRequest(ID::ArmDSPPROM, programROMName);
interface->loadRequest(ID::ArmDSPDROM, dataROMName);
@ -324,8 +333,11 @@ void Cartridge::parse_markup_hitachidsp(Markup::Node root, unsigned roms) {
if(!root) return;
has_hitachidsp = true;
parse_markup_memory(hitachidsp.rom, root["rom"].at(0), ID::HitachiDSPROM, false);
parse_markup_memory(hitachidsp.ram, root["ram"].at(1), ID::HitachiDSPRAM, true);
auto rom = root.find("rom");
auto ram = root.find("ram");
parse_markup_memory(hitachidsp.rom, rom(0), ID::HitachiDSPROM, false);
parse_markup_memory(hitachidsp.ram, ram(0), ID::HitachiDSPRAM, true);
for(auto& word : hitachidsp.dataROM) word = 0x000000;
for(auto& word : hitachidsp.dataRAM) word = 0x00;
@ -334,8 +346,8 @@ void Cartridge::parse_markup_hitachidsp(Markup::Node root, unsigned roms) {
if(hitachidsp.Frequency == 0) hitachidsp.frequency = 20000000;
hitachidsp.Roms = roms;
string dataROMName = root["rom"].at(1)["name"].text();
string dataRAMName = root["ram"].at(1)["name"].text();
string dataROMName = rom(1)["name"].text();
string dataRAMName = ram(1)["name"].text();
interface->loadRequest(ID::HitachiDSPDROM, dataROMName);
if(dataRAMName.empty() == false) {
@ -380,9 +392,12 @@ void Cartridge::parse_markup_necdsp(Markup::Node root) {
: root["model"].text() == "uPD96050" ? NECDSP::Revision::uPD96050
: NECDSP::Revision::uPD7725;
string programROMName = root["rom"].at(0)["name"].text();
string dataROMName = root["rom"].at(1)["name"].text();
string dataRAMName = root["ram/name"].text();
auto rom = root.find("rom");
auto ram = root.find("ram");
string programROMName = rom(0)["name"].text();
string dataROMName = rom(1)["name"].text();
string dataRAMName = ram(0)["name"].text();
if(necdsp.revision == NECDSP::Revision::uPD7725) {
interface->loadRequest(ID::Nec7725DSPPROM, programROMName);
@ -456,9 +471,12 @@ void Cartridge::parse_markup_spc7110(Markup::Node root) {
if(!root) return;
has_spc7110 = true;
parse_markup_memory(spc7110.prom, root["rom"].at(0), ID::SPC7110PROM, false);
parse_markup_memory(spc7110.drom, root["rom"].at(1), ID::SPC7110DROM, false);
parse_markup_memory(spc7110.ram, root["ram"], ID::SPC7110RAM, true);
auto rom = root.find("rom");
auto ram = root.find("ram");
parse_markup_memory(spc7110.prom, rom(0), ID::SPC7110PROM, false);
parse_markup_memory(spc7110.drom, rom(1), ID::SPC7110DROM, false);
parse_markup_memory(spc7110.ram, ram(0), ID::SPC7110RAM, true);
for(auto node : root.find("map")) {
if(node["id"].text() == "io") {
@ -485,8 +503,11 @@ void Cartridge::parse_markup_sdd1(Markup::Node root) {
if(!root) return;
has_sdd1 = true;
parse_markup_memory(sdd1.rom, root["rom"], ID::SDD1ROM, false);
parse_markup_memory(sdd1.ram, root["ram"], ID::SDD1RAM, true);
auto rom = root.find("rom");
auto ram = root.find("ram");
parse_markup_memory(sdd1.rom, rom(0), ID::SDD1ROM, false);
parse_markup_memory(sdd1.ram, ram(0), ID::SDD1RAM, true);
for(auto node : root.find("map")) {
if(node["id"].text() == "io") {