summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99/gromport/cartridges.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ti99/gromport/cartridges.cpp')
-rw-r--r--src/devices/bus/ti99/gromport/cartridges.cpp403
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