diff options
Diffstat (limited to 'src/devices/bus/ti99/gromport/cartridges.cpp')
-rw-r--r-- | src/devices/bus/ti99/gromport/cartridges.cpp | 403 |
1 files changed, 84 insertions, 319 deletions
diff --git a/src/devices/bus/ti99/gromport/cartridges.cpp b/src/devices/bus/ti99/gromport/cartridges.cpp index 2f5a558d29b..78c0231c9a7 100644 --- a/src/devices/bus/ti99/gromport/cartridges.cpp +++ b/src/devices/bus/ti99/gromport/cartridges.cpp @@ -16,10 +16,7 @@ #include "cartridges.h" #include "corestr.h" -#include "ioprocs.h" -#include "unzip.h" -#include "xmlfile.h" - +#include "formats/rpk.h" #define LOG_WARN (1U<<1) // Warnings #define LOG_CONFIG (1U<<2) // Configuration @@ -63,21 +60,21 @@ enum PCB_PAGED7 }; -static const pcb_type pcbdefs[] = -{ - { PCB_STANDARD, "standard" }, - { PCB_PAGED12K, "paged12k" }, - { PCB_PAGED16K, "paged" }, - { PCB_MINIMEM, "minimem" }, - { PCB_SUPER, "super" }, - { PCB_MBX, "mbx" }, - { PCB_PAGED379I, "paged379i" }, - { PCB_PAGED378, "paged378" }, - { PCB_PAGED377, "paged377" }, - { PCB_PAGEDCRU, "pagedcru" }, - { PCB_GROMEMU, "gromemu" }, - { PCB_PAGED7, "paged7" }, - { 0, nullptr} +static char const *const pcbdefs[] = +{ + "standard", // PCB_STANDARD + "paged12k", // PCB_PAGED12K + "paged", // PCB_PAGED16K + "minimem", // PCB_MINIMEM + "super", // PCB_SUPER + "mbx", // PCB_MBX + "paged379i", // PCB_PAGED379I + "paged378", // PCB_PAGED378 + "paged377", // PCB_PAGED377 + "pagedcru", // PCB_PAGEDCRU + "gromemu", // PCB_GROMEMU + "paged7", // PCB_PAGED7 + nullptr }; static const pcb_type sw_pcbdefs[] = @@ -99,8 +96,7 @@ ti99_cartridge_device::ti99_cartridge_device(const machine_config &mconfig, cons m_pcbtype(0), m_slot(0), m_pcb(nullptr), - m_connector(nullptr), - m_rpk(nullptr) + m_connector(nullptr) { } @@ -257,23 +253,19 @@ image_init_result ti99_cartridge_device::call_load() i++; } while (sw_pcbdefs[i].id != 0); LOGMASKED(LOG_CONFIG, "Cartridge type is %s (%d)\n", pcb, m_pcbtype); - m_rpk = nullptr; + m_rpk.reset(); } else { - auto reader = std::make_unique<rpk_reader>(pcbdefs); - try + std::error_condition err = rpk_open(machine().options(), util::core_file_read(image_core_file()), machine().system().name, m_rpk); + if (err) { - m_rpk = reader->open(machine().options(), image_core_file(), machine().system().name); - m_pcbtype = m_rpk->get_type(); - } - catch (rpk_exception& err) - { - LOGMASKED(LOG_WARN, "Failed to load cartridge '%s': %s\n", basename(), err.to_string().c_str()); - m_rpk = nullptr; + LOGMASKED(LOG_WARN, "Failed to load cartridge '%s': %s\n", basename(), err.message().c_str()); + m_rpk.reset(); m_err = image_error::INVALIDIMAGE; return image_init_result::FAIL; } + m_pcbtype = m_rpk->get_type(); } switch (m_pcbtype) @@ -342,7 +334,7 @@ void ti99_cartridge_device::call_unload() if (m_rpk != nullptr) { m_rpk->close(); // will write NVRAM contents - delete m_rpk; + m_rpk.reset(); } else { @@ -1451,47 +1443,6 @@ void ti99_gromemu_cartridge::gromemuwrite(offs_t offset, uint8_t data) RPK loader - RPK format support - - A RPK file ("rompack") contains a collection of dump files and a layout - file that defines the kind of circuit board (PCB) used in the cartridge - and the mapping of dumps to sockets on the board. - -Example: - <?xml version="1.0" encoding="utf-8"?> - <romset> - <resources> - <rom id="gromimage" file="ed-assmg.bin" /> - </resources> - <configuration> - <pcb type="standard"> - <socket id="grom_socket" uses="gromimage"/> - </pcb> - </configuration> - </romset> - -DTD: - <!ELEMENT romset (resources, configuration)> - <!ELEMENT resources (rom|ram)+> - <!ELEMENT rom EMPTY> - <!ELEMENT ram EMPTY> - <!ELEMENT configuration (pcb)> - <!ELEMENT pcb (socket)+> - <!ELEMENT socket EMPTY> - <!ATTLIST romset version CDATA #IMPLIED> - <!ATTLIST rom id ID #REQUIRED - <!ATTLIST rom file CDATA #REQUIRED> - <!ATTLIST rom crc CDATA #IMPLIED> - <!ATTLIST rom sha1 CDATA #IMPLIED> - <!ATTLIST ram id ID #REQUIRED> - <!ATTLIST ram type (volatile|persistent) #IMPLIED> - <!ATTLIST ram store (internal|external) #IMPLIED> - <!ATTLIST ram file CDATA #IMPLIED> - <!ATTLIST ram length CDATA #REQUIRED> - <!ATTLIST pcb type CDATA #REQUIRED> - <!ATTLIST socket id ID #REQUIRED> - <!ATTLIST socket uses IDREF #REQUIRED> - ****************************************************************************/ #undef LOG_OUTPUT_FUNC @@ -1535,7 +1486,7 @@ int ti99_cartridge_device::rpk::get_resource_length(const char *socket_name) return socket->second->get_content_length(); } -void ti99_cartridge_device::rpk::add_socket(const char* id, std::unique_ptr<rpk_socket> newsock) +void ti99_cartridge_device::rpk::add_socket(const char* id, std::unique_ptr<ti99_rpk_socket> &&newsock) { m_sockets.emplace(id, std::move(newsock)); } @@ -1571,178 +1522,68 @@ void ti99_cartridge_device::rpk::close() not a network socket) ***************************************************************/ -ti99_cartridge_device::rpk_socket::rpk_socket(const char* id, int length, std::unique_ptr<uint8_t []> &&contents, std::string &&pathname) +ti99_cartridge_device::ti99_rpk_socket::ti99_rpk_socket(const char* id, int length, std::vector<uint8_t> &&contents, std::string &&pathname) : m_id(id), m_length(length), m_contents(std::move(contents)), m_pathname(std::move(pathname)) { } -ti99_cartridge_device::rpk_socket::rpk_socket(const char* id, int length, std::unique_ptr<uint8_t []> &&contents) - : rpk_socket(id, length, std::move(contents), "") -{ -} - -/* - Locate a file in the ZIP container -*/ -int ti99_cartridge_device::rpk_reader::find_file(util::archive_file &zip, const char *filename, uint32_t crc) +ti99_cartridge_device::ti99_rpk_socket::ti99_rpk_socket(const char* id, int length, std::vector<uint8_t> &&contents) + : ti99_rpk_socket(id, length, std::move(contents), "") { - for (int header = zip.first_file(); header >= 0; header = zip.next_file()) - { - // Ignore directories - if (!zip.current_is_directory()) - { - // We don't check for CRC == 0. - if (crc != 0) - { - // if the CRC and name both match, we're good - // if the CRC matches and the name doesn't, we're still good - if (zip.current_crc() == crc) - return header; - } - else - { - if (core_stricmp(zip.current_name().c_str(), filename) == 0) - { - return header; - } - } - } - } - return -1; } /* Load a rom resource and put it in a pcb socket instance. */ -std::unique_ptr<ti99_cartridge_device::rpk_socket> ti99_cartridge_device::rpk_reader::load_rom_resource(util::archive_file &zip, util::xml::data_node const* rom_resource_node, const char* socketname) +std::error_condition ti99_cartridge_device::rpk_load_rom_resource(const rpk_socket &socket, std::unique_ptr<ti99_rpk_socket> &result) { - // find the file attribute (required) - std::string const *const file = rom_resource_node->get_attribute_string_ptr("file"); - if (file == nullptr) throw rpk_exception(RPK_INVALID_LAYOUT, "<rom> must have a 'file' attribute"); + LOGMASKED(LOG_RPK, "[RPK handler] Loading ROM contents for socket '%s' from file %s\n", socket.id(), socket.filename()); - LOGMASKED(LOG_RPK, "[RPK handler] Loading ROM contents for socket '%s' from file %s\n", socketname, *file); - - // check for crc - std::string const *const crcstr = rom_resource_node->get_attribute_string_ptr("crc"); - int header; - if (crcstr==nullptr) - { - // no CRC, just find the file in the RPK - header = find_file(zip, file->c_str(), 0); - } - else - { - uint32_t crc = strtoul(crcstr->c_str(), nullptr, 16); - header = find_file(zip, file->c_str(), crc); - } - if (header < 0) throw rpk_exception(RPK_INVALID_FILE_REF, "File not found or CRC check failed"); - - int length = zip.current_uncompressed_length(); - - // Allocate storage - std::unique_ptr<uint8_t []> contents; - try { contents = make_unique_clear<uint8_t []>(length); } - catch (std::bad_alloc const &) { throw rpk_exception(RPK_OUT_OF_MEMORY); } - - // and unzip file from the zip file - std::error_condition const ziperr = zip.decompress(contents.get(), length); - if (ziperr) - { - if (ziperr == util::archive_file::error::UNSUPPORTED) throw rpk_exception(RPK_ZIP_UNSUPPORTED); - else throw rpk_exception(RPK_ZIP_ERROR); - } - - // check for sha1 - std::string const *const sha1 = rom_resource_node->get_attribute_string_ptr("sha1"); - if (sha1 != nullptr) - { - util::hash_collection actual_hashes; - actual_hashes.compute(contents.get(), length, util::hash_collection::HASH_TYPES_CRC_SHA1); - - util::hash_collection expected_hashes; - expected_hashes.add_from_string(util::hash_collection::HASH_SHA1, *sha1); - - if (actual_hashes != expected_hashes) throw rpk_exception(RPK_INVALID_FILE_REF, "SHA1 check failed"); - } + std::vector<uint8_t> contents; + std::error_condition err = socket.read_file(contents); + if (err) + return err; // Create a socket instance - return std::make_unique<rpk_socket>(socketname, length, std::move(contents)); + result = std::make_unique<ti99_rpk_socket>(socket.id().c_str(), contents.size(), std::move(contents)); + return std::error_condition(); } /* Load a ram resource and put it in a pcb socket instance. */ -std::unique_ptr<ti99_cartridge_device::rpk_socket> ti99_cartridge_device::rpk_reader::load_ram_resource(emu_options &options, util::xml::data_node const* ram_resource_node, const char* socketname, const char* system_name) +std::unique_ptr<ti99_cartridge_device::ti99_rpk_socket> ti99_cartridge_device::rpk_load_ram_resource(emu_options &options, const rpk_socket &socket, const char *system_name) { - // find the length attribute - std::string const *const length_string = ram_resource_node->get_attribute_string_ptr("length"); - if (length_string == nullptr) throw rpk_exception(RPK_MISSING_RAM_LENGTH); - - // parse it - unsigned int length; - char suffix = '\0'; - sscanf(length_string->c_str(), "%u%c", &length, &suffix); - switch(tolower(suffix)) - { - case 'k': // kilobytes - length *= 1024; - break; - - case 'm': - /* megabytes */ - length *= 1024*1024; - break; - - case '\0': - break; - - default: // failed - throw rpk_exception(RPK_INVALID_RAM_SPEC); - } - // Allocate memory for this resource - std::unique_ptr<uint8_t []> contents; - try { contents = make_unique_clear<uint8_t []>(length); } - catch (std::bad_alloc const &) { throw rpk_exception(RPK_OUT_OF_MEMORY); } + std::vector<uint8_t> contents; + contents.resize(socket.length()); + std::fill(contents.begin(), contents.end(), 0); - LOGMASKED(LOG_RPK, "[RPK handler] Allocating RAM buffer (%d bytes) for socket '%s'\n", length, socketname); + LOGMASKED(LOG_RPK, "[RPK handler] Allocating RAM buffer (%d bytes) for socket '%s'\n", socket.length(), socket.id()); // That's it for pure RAM. Now check whether the RAM is "persistent", i.e. NVRAM. // In that case we must load it from the NVRAM directory. // The file name is given in the RPK file; the subdirectory is the system name. - std::string const *const ram_type = ram_resource_node->get_attribute_string_ptr("type"); std::string ram_pname; - if (ram_type != nullptr) + if (socket.type() == rpk_socket::socket_type::PERSISTENT_RAM) { - if (*ram_type == "persistent") - { - // Get the file name (required if persistent) - std::string const *const ram_filename = ram_resource_node->get_attribute_string_ptr("file"); - if (ram_filename==nullptr) - throw rpk_exception(RPK_INVALID_RAM_SPEC, "<ram type='persistent'> must have a 'file' attribute"); - - ram_pname = std::string(system_name).append(PATH_SEPARATOR).append(*ram_filename); - // load, and fill rest with 00 - LOGMASKED(LOG_RPK, "[RPK handler] Loading NVRAM contents from '%s'\n", ram_pname.c_str()); - - // Load the NVRAM contents - if (!contents || (length <= 0)) - throw emu_fatalerror("ti99_cartridge_device::rpk_reader::load_ram_resource: Buffer is null or length is 0"); - - // try to open the battery file and read it if possible - emu_file file(options.nvram_directory(), OPEN_FLAG_READ); - std::error_condition const filerr = file.open(ram_pname); - int bytes_read = 0; - if (!filerr) - bytes_read = file.read(contents.get(), length); + ram_pname = std::string(system_name).append(PATH_SEPARATOR).append(socket.filename()); + // load, and fill rest with 00 + LOGMASKED(LOG_RPK, "[RPK handler] Loading NVRAM contents from '%s'\n", ram_pname); - // fill remaining bytes (if necessary) - std::fill_n(&contents[bytes_read], length - bytes_read, 0x00); - } + // try to open the battery file and read it if possible + emu_file file(options.nvram_directory(), OPEN_FLAG_READ); + std::error_condition const filerr = file.open(ram_pname); + int bytes_read = 0; + if (!filerr) + bytes_read = file.read(&contents[0], contents.size()); + + // fill remaining bytes (if necessary) + std::fill_n(&contents[bytes_read], contents.size() - bytes_read, 0x00); } // Create a socket instance - return std::make_unique<rpk_socket>(socketname, length, std::move(contents), std::move(ram_pname)); + return std::make_unique<ti99_rpk_socket>(socket.id().c_str(), socket.length(), std::move(contents), std::move(ram_pname)); } /*------------------------------------------------- @@ -1751,123 +1592,47 @@ std::unique_ptr<ti99_cartridge_device::rpk_socket> ti99_cartridge_device::rpk_re system_name - name of the driver (also just for NVRAM handling) -------------------------------------------------*/ -ti99_cartridge_device::rpk* ti99_cartridge_device::rpk_reader::open(emu_options &options, util::core_file &file, const char *system_name) +std::error_condition ti99_cartridge_device::rpk_open(emu_options &options, std::unique_ptr<util::random_read> &&stream, const char *system_name, std::unique_ptr<rpk> &result) { - auto newrpk = new rpk(options, system_name); - - try - { - // open the ZIP file - auto reader = util::core_file_read(file); - if (!reader) throw rpk_exception(RPK_OUT_OF_MEMORY); - - std::error_condition ziperr; - util::archive_file::ptr zipfile; - ziperr = util::archive_file::open_zip(std::move(reader), zipfile); - if (ziperr) throw rpk_exception(RPK_NOT_ZIP_FORMAT); - - // find the layout.xml file - if (find_file(*zipfile, "layout.xml", 0) < 0) throw rpk_exception(RPK_MISSING_LAYOUT); + std::unique_ptr<rpk> newrpk = std::make_unique<rpk>(options, system_name); - // reserve space for the layout file contents (+1 for the termination) - std::vector<char> layout_text(zipfile->current_uncompressed_length() + 1); + rpk_reader reader(pcbdefs, true); - // uncompress the layout text - ziperr = zipfile->decompress(&layout_text[0], zipfile->current_uncompressed_length()); - if (ziperr) - { - if (ziperr == util::archive_file::error::UNSUPPORTED) throw rpk_exception(RPK_ZIP_UNSUPPORTED); - else throw rpk_exception(RPK_ZIP_ERROR); - } + // open the RPK + rpk_file::ptr file; + std::error_condition err = reader.read(std::move(stream), file); + if (err) + return err; - layout_text[zipfile->current_uncompressed_length()] = '\0'; // Null-terminate + // specify the PCB + newrpk->m_type = file->pcb_type() + 1; + LOGMASKED(LOG_RPK, "[RPK handler] Cartridge says it has PCB type '%s'\n", pcbdefs[file->pcb_type()]); - // parse the layout text - util::xml::file::ptr const layout_xml = util::xml::file::string_read(&layout_text[0], nullptr); - if (!layout_xml) throw rpk_exception(RPK_XML_ERROR); - - // Now we work within the XML tree - - // romset is the root node - util::xml::data_node const *const romset_node = layout_xml->get_child("romset"); - if (!romset_node) throw rpk_exception(RPK_INVALID_LAYOUT, "document element must be <romset>"); - - // resources is a child of romset - util::xml::data_node const *const resources_node = romset_node->get_child("resources"); - if (!resources_node) throw rpk_exception(RPK_INVALID_LAYOUT, "<romset> must have a <resources> child"); - - // configuration is a child of romset; we're actually interested in ... - util::xml::data_node const *const configuration_node = romset_node->get_child("configuration"); - if (!configuration_node) throw rpk_exception(RPK_INVALID_LAYOUT, "<romset> must have a <configuration> child"); - - // ... pcb, which is a child of configuration - util::xml::data_node const *const pcb_node = configuration_node->get_child("pcb"); - if (!pcb_node) throw rpk_exception(RPK_INVALID_LAYOUT, "<configuration> must have a <pcb> child"); - - // We'll try to find the PCB type on the provided type list. - std::string const *const pcb_type = pcb_node->get_attribute_string_ptr("type"); - if (!pcb_type) throw rpk_exception(RPK_INVALID_LAYOUT, "<pcb> must have a 'type' attribute"); - LOGMASKED(LOG_RPK, "[RPK handler] Cartridge says it has PCB type '%s'\n", *pcb_type); + for (const rpk_socket &socket : file->sockets()) + { + std::unique_ptr<ti99_rpk_socket> ti99_socket; - int i=0; - do + switch (socket.type()) { - if (*pcb_type == m_types[i].name) - { - newrpk->m_type = m_types[i].id; - break; - } - i++; - } while (m_types[i].id != 0); + case rpk_socket::socket_type::ROM: + err = rpk_load_rom_resource(socket, ti99_socket); + if (err) + return err; + newrpk->add_socket(socket.id().c_str(), std::move(ti99_socket)); + break; - if (m_types[i].id==0) throw rpk_exception(RPK_UNKNOWN_PCB_TYPE); + case rpk_socket::socket_type::RAM: + case rpk_socket::socket_type::PERSISTENT_RAM: + newrpk->add_socket(socket.id().c_str(), rpk_load_ram_resource(options, socket, system_name)); + break; - // Find the sockets and load their respective resource - for (util::xml::data_node const *socket_node = pcb_node->get_first_child(); socket_node != nullptr; socket_node = socket_node->get_next_sibling()) - { - if (strcmp(socket_node->get_name(), "socket")!=0) throw rpk_exception(RPK_INVALID_LAYOUT, "<pcb> element has only <socket> children"); - std::string const *const id = socket_node->get_attribute_string_ptr("id"); - if (!id) throw rpk_exception(RPK_INVALID_LAYOUT, "<socket> must have an 'id' attribute"); - std::string const *const uses_name = socket_node->get_attribute_string_ptr("uses"); - if (!uses_name) throw rpk_exception(RPK_INVALID_LAYOUT, "<socket> must have a 'uses' attribute"); - - bool found = false; - // Locate the resource node - for (util::xml::data_node const *resource_node = resources_node->get_first_child(); resource_node != nullptr; resource_node = resource_node->get_next_sibling()) - { - std::string const *const resource_name = resource_node->get_attribute_string_ptr("id"); - if (!resource_name) throw rpk_exception(RPK_INVALID_LAYOUT, "resource node must have an 'id' attribute"); - - if (*resource_name == *uses_name) - { - // found it - if (strcmp(resource_node->get_name(), "rom")==0) - { - newrpk->add_socket(id->c_str(), load_rom_resource(*zipfile, resource_node, id->c_str())); - } - else - { - if (strcmp(resource_node->get_name(), "ram")==0) - { - newrpk->add_socket(id->c_str(), load_ram_resource(options, resource_node, id->c_str(), system_name)); - } - else throw rpk_exception(RPK_INVALID_LAYOUT, "resource node must be <rom> or <ram>"); - } - found = true; - } - } - if (!found) throw rpk_exception(RPK_INVALID_RESOURCE_REF, uses_name->c_str()); + default: + throw false; } } - catch (rpk_exception &) - { - newrpk->close(); - - // rethrow the exception - throw; - } - return newrpk; + result = std::move(newrpk); + return std::error_condition(); } } // end namespace bus::ti99::gromport |