summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/imagedev/multcart.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/imagedev/multcart.c')
-rw-r--r--src/emu/imagedev/multcart.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/src/emu/imagedev/multcart.c b/src/emu/imagedev/multcart.c
new file mode 100644
index 00000000000..1c0eb9f3d41
--- /dev/null
+++ b/src/emu/imagedev/multcart.c
@@ -0,0 +1,607 @@
+/*********************************************************************
+
+ multcart.c
+
+ Multi-cartridge handling code
+
+*********************************************************************/
+
+#include "multcart.h"
+#include "pool.h"
+#include "unzip.h"
+#include "corestr.h"
+#include "xmlfile.h"
+#include "emu.h"
+#include "hash.h"
+#include "machine/ram.h"
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+struct _multicart_private
+{
+ object_pool * pool;
+ zip_file * zip;
+};
+
+
+typedef struct _multicart_load_state multicart_load_state;
+struct _multicart_load_state
+{
+ multicart_t * multicart;
+ zip_file * zip;
+ xml_data_node * layout_xml;
+ xml_data_node * resources_node;
+ xml_data_node * pcb_node;
+ multicart_resource * resources;
+ multicart_socket * sockets;
+};
+
+static const char error_text[14][30] =
+{
+ "no error",
+ "not a multicart",
+ "module definition corrupt",
+ "out of memory",
+ "xml format error",
+ "invalid file reference",
+ "zip file error",
+ "missing ram length",
+ "invalid ram specification",
+ "unknown resource type",
+ "invalid resource reference",
+ "invalid file format",
+ "missing layout",
+ "no pcb or resource found"
+};
+
+const char *multicart_error_text(multicart_open_error error)
+{
+ return error_text[(int)error];
+}
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ find_file
+-------------------------------------------------*/
+
+static const zip_file_header *find_file(zip_file *zip, const char *filename)
+{
+ const zip_file_header *header;
+ for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip))
+ {
+ if (!core_stricmp(header->filename, filename))
+ return header;
+ }
+ return NULL;
+}
+
+static const zip_file_header *find_file_crc(zip_file *zip, const char *filename, UINT32 crc)
+{
+ const zip_file_header *header;
+ for (header = zip_file_first_file(zip); header != NULL; header = zip_file_next_file(zip))
+ {
+ // if the CRC and name both match, we're good
+ // if the CRC matches and the name doesn't, we're still good
+ if ((!core_stricmp(header->filename, filename)) && (header->crc == crc))
+ {
+ return header;
+ }
+ else if (header->crc == crc)
+ {
+ return header;
+ }
+ }
+ return NULL;
+}
+
+/*-------------------------------------------------
+ find_pcb_and_resource_nodes
+-------------------------------------------------*/
+
+static int find_pcb_and_resource_nodes(xml_data_node *layout_xml,
+ xml_data_node **pcb_node, xml_data_node **resource_node)
+{
+ xml_data_node *romset_node;
+ xml_data_node *configuration_node;
+
+ *pcb_node = NULL;
+ *resource_node = NULL;
+
+ romset_node = xml_get_sibling(layout_xml->child, "romset");
+ if (romset_node == NULL)
+ return FALSE;
+
+ configuration_node = xml_get_sibling(romset_node->child, "configuration");
+ if (configuration_node == NULL)
+ return FALSE;
+
+ *pcb_node = xml_get_sibling(configuration_node->child, "pcb");
+ if (*pcb_node == NULL)
+ return FALSE;
+
+ *resource_node = xml_get_sibling(romset_node->child, "resources");
+ if (*resource_node == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ get_resource_type
+-------------------------------------------------*/
+
+static multicart_resource_type get_resource_type(const char *s)
+{
+ multicart_resource_type result;
+ if (!strcmp(s, "rom"))
+ result = MULTICART_RESOURCE_TYPE_ROM;
+ else if (!strcmp(s, "ram"))
+ result = MULTICART_RESOURCE_TYPE_RAM;
+ else
+ result = MULTICART_RESOURCE_TYPE_INVALID;
+ return result;
+}
+
+
+/*-------------------------------------------------
+ load_rom_resource
+-------------------------------------------------*/
+
+static multicart_open_error load_rom_resource(multicart_load_state *state, xml_data_node *resource_node,
+ multicart_resource *resource)
+{
+ const char *file, *crcstr, *sha1;
+ const zip_file_header *header;
+ zip_error ziperr;
+ UINT32 crc;
+
+ /* locate the 'file' attribute */
+ file = xml_get_attribute_string(resource_node, "file", NULL);
+ if (file == NULL)
+ return MCERR_XML_ERROR;
+
+ if (!(crcstr = xml_get_attribute_string(resource_node, "crc", NULL)))
+ {
+ /* locate the file in the ZIP file */
+ header = find_file(state->zip, file);
+ }
+ else /* CRC tag is present, use it */
+ {
+ crc = strtoul(crcstr, NULL, 16);
+ header = find_file_crc(state->zip, file, crc);
+ }
+ if (header == NULL)
+ return MCERR_INVALID_FILE_REF;
+ resource->length = header->uncompressed_length;
+
+ /* allocate bytes for this resource */
+ resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length);
+ if (resource->ptr == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* and decompress it */
+ ziperr = zip_file_decompress(state->zip, resource->ptr, resource->length);
+ if (ziperr != ZIPERR_NONE)
+ return MCERR_ZIP_ERROR;
+
+ /* check SHA1 now */
+ if ((sha1 = xml_get_attribute_string(resource_node, "sha1", NULL)))
+ {
+ char calc_sha[256];
+
+ memset(calc_sha, 0, sizeof(calc_sha));
+ hash_compute(&calc_sha[0], (const unsigned char*)resource->ptr, resource->length, HASH_SHA1);
+
+ if ((strncmp(sha1, &calc_sha[2], 20)))
+ {
+ return MCERR_INVALID_FILE_REF;
+ }
+ }
+
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_ram_resource
+-------------------------------------------------*/
+
+static multicart_open_error load_ram_resource(multicart_load_state *state, xml_data_node *resource_node,
+ multicart_resource *resource)
+{
+ const char *length_string;
+ const char *ram_type;
+ const char *ram_filename;
+
+ astring *ram_pathname;
+
+ /* locate the 'length' attribute */
+ length_string = xml_get_attribute_string(resource_node, "length", NULL);
+ if (length_string == NULL)
+ return MCERR_MISSING_RAM_LENGTH;
+
+ /* ...and parse it */
+ resource->length = ram_parse_string(length_string);
+ if (resource->length <= 0)
+ return MCERR_INVALID_RAM_SPEC;
+
+ /* allocate bytes for this resource */
+ resource->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length);
+ if (resource->ptr == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* Is this a persistent RAM resource? Then try to load it. */
+ ram_type = xml_get_attribute_string(resource_node, "type", NULL);
+ if (ram_type != NULL)
+ {
+ if (strcmp(ram_type, "persistent")==0)
+ {
+ astring tmp;
+
+ /* Get the file name. */
+ ram_filename = xml_get_attribute_string(resource_node, "file", NULL);
+ if (ram_filename==NULL)
+ return MCERR_XML_ERROR;
+
+ ram_pathname = astring_assemble_3(&tmp, state->multicart->gamedrv_name, PATH_SEPARATOR, ram_filename);
+
+ /* Save the file name so that we can write the contents on unloading.
+ If the RAM resource has no filename, we know that it was volatile only. */
+ resource->filename = pool_strdup_lib(state->multicart->data->pool, astring_c(ram_pathname));
+
+ if (resource->filename == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ image_battery_load_by_name(resource->filename, resource->ptr, resource->length, 0x00);
+ }
+ /* else this type is volatile, in which case we just have
+ a memory expansion */
+ }
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_resource
+-------------------------------------------------*/
+
+static multicart_open_error load_resource(multicart_load_state *state, xml_data_node *resource_node,
+ multicart_resource_type resource_type)
+{
+ const char *id;
+ multicart_open_error err;
+ multicart_resource *resource;
+ multicart_resource **next_resource;
+
+ /* get the 'id' attribute; error if not present */
+ id = xml_get_attribute_string(resource_node, "id", NULL);
+ if (id == NULL)
+ return MCERR_XML_ERROR;
+
+ /* allocate memory for the resource */
+ resource = (multicart_resource *)pool_malloc_lib(state->multicart->data->pool, sizeof(*resource));
+ if (resource == NULL)
+ return MCERR_OUT_OF_MEMORY;
+ memset(resource, 0, sizeof(*resource));
+ resource->type = resource_type;
+
+ /* copy id */
+ resource->id = pool_strdup_lib(state->multicart->data->pool, id);
+ if (resource->id == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ switch(resource->type)
+ {
+ case MULTICART_RESOURCE_TYPE_ROM:
+ err = load_rom_resource(state, resource_node, resource);
+ if (err != MCERR_NONE)
+ return err;
+ break;
+
+ case MULTICART_RESOURCE_TYPE_RAM:
+ err = load_ram_resource(state, resource_node, resource);
+ if (err != MCERR_NONE)
+ return err;
+ break;
+
+ default:
+ return MCERR_UNKNOWN_RESOURCE_TYPE;
+ }
+
+ /* append the resource */
+ for (next_resource = &state->resources; *next_resource; next_resource = &(*next_resource)->next)
+ ;
+ *next_resource = resource;
+
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_all_resources
+-------------------------------------------------*/
+
+static multicart_open_error load_all_resources(multicart_load_state *state)
+{
+ multicart_open_error err;
+ xml_data_node *resource_node;
+ multicart_resource_type resource_type;
+
+ for (resource_node = state->resources_node->child; resource_node != NULL; resource_node = resource_node->next)
+ {
+ resource_type = get_resource_type(resource_node->name);
+ if (resource_type != MULTICART_RESOURCE_TYPE_INVALID)
+ {
+ err = load_resource(state, resource_node, resource_type);
+ if (err != MCERR_NONE)
+ return err;
+ }
+ }
+
+ state->multicart->resources = state->resources;
+ return MCERR_NONE;
+}
+
+/*-------------------------------------------------
+ save_ram_resources. This is important for persistent RAM. All
+ resources were allocated within the memory pool of this device and will
+ be freed on multicart_close.
+-------------------------------------------------*/
+
+static multicart_open_error save_ram_resources(multicart_t *cart)
+{
+ const multicart_resource *resource;
+
+ for (resource = cart->resources; resource != NULL; resource = resource->next)
+ {
+ if ((resource->type == MULTICART_RESOURCE_TYPE_RAM) && (resource->filename != NULL))
+ {
+ image_battery_save_by_name(resource->filename, resource->ptr, resource->length);
+ }
+ }
+ return MCERR_NONE;
+}
+
+/*-------------------------------------------------
+ load_socket
+-------------------------------------------------*/
+
+static multicart_open_error load_socket(multicart_load_state *state, xml_data_node *socket_node)
+{
+ const char *id;
+ const char *uses;
+ const multicart_resource *resource;
+ multicart_socket *socket;
+ multicart_socket **next_socket;
+
+ /* get the 'id' and 'uses' attributes; error if not present */
+ id = xml_get_attribute_string(socket_node, "id", NULL);
+ uses = xml_get_attribute_string(socket_node, "uses", NULL);
+ if ((id == NULL) || (uses == NULL))
+ return MCERR_XML_ERROR;
+
+ /* find the resource */
+ for (resource = state->multicart->resources; resource != NULL; resource = resource->next)
+ {
+ if (!strcmp(uses, resource->id))
+ break;
+ }
+ if (resource == NULL)
+ return MCERR_INVALID_RESOURCE_REF;
+
+ /* create the socket */
+ socket = (multicart_socket *)pool_malloc_lib(state->multicart->data->pool, sizeof(*socket));
+ if (socket == NULL)
+ return MCERR_OUT_OF_MEMORY;
+ memset(socket, 0, sizeof(*socket));
+ socket->resource = resource;
+ socket->ptr = resource->ptr;
+
+ /* copy id */
+ socket->id = pool_strdup_lib(state->multicart->data->pool, id);
+ if (socket->id == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* which pointer should I use? */
+ if (resource->ptr != NULL)
+ {
+ /* use the resource's ptr */
+ socket->ptr = resource->ptr;
+ }
+ else
+ {
+ /* allocate bytes for this socket */
+ socket->ptr = pool_malloc_lib(state->multicart->data->pool, resource->length);
+ if (socket->ptr == NULL)
+ return MCERR_OUT_OF_MEMORY;
+
+ /* ...and clear it */
+ memset(socket->ptr, 0xCD, resource->length);
+ }
+
+ /* append the resource */
+ for (next_socket = &state->sockets; *next_socket; next_socket = &(*next_socket)->next)
+ ;
+ *next_socket = socket;
+
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ load_all_sockets
+-------------------------------------------------*/
+
+static multicart_open_error load_all_sockets(multicart_load_state *state)
+{
+ multicart_open_error err;
+ xml_data_node *socket_node;
+
+ for (socket_node = xml_get_sibling(state->pcb_node->child, "socket"); socket_node != NULL;
+ socket_node = xml_get_sibling(socket_node->next, "socket"))
+ {
+ err = load_socket(state, socket_node);
+ if (err != MCERR_NONE)
+ return err;
+ }
+
+ state->multicart->sockets = state->sockets;
+ return MCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ multicart_open - opens a multicart
+-------------------------------------------------*/
+
+multicart_open_error multicart_open(const char *filename, const char *gamedrv, multicart_load_flags load_flags, multicart_t **cart)
+{
+ multicart_open_error err;
+ zip_error ziperr;
+ object_pool *pool;
+ multicart_load_state state = {0, };
+ const zip_file_header *header;
+ const char *pcb_type;
+ char *layout_text = NULL;
+
+ /* allocate an object pool */
+ pool = pool_alloc_lib(NULL);
+ if (pool == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* allocate the multicart */
+ state.multicart = (multicart_t*)pool_malloc_lib(pool, sizeof(*state.multicart));
+ if (state.multicart == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+ memset(state.multicart, 0, sizeof(*state.multicart));
+
+ /* allocate the multicart's private data */
+ state.multicart->data = (multicart_private*)pool_malloc_lib(pool, sizeof(*state.multicart->data));
+ if (state.multicart->data == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+ memset(state.multicart->data, 0, sizeof(*state.multicart->data));
+ state.multicart->data->pool = pool;
+ pool = NULL;
+
+ /* open the ZIP file */
+ ziperr = zip_file_open(filename, &state.zip);
+ if (ziperr != ZIPERR_NONE)
+ {
+ err = MCERR_NOT_MULTICART;
+ goto done;
+ }
+
+ /* find the layout.xml file */
+ header = find_file(state.zip, "layout.xml");
+ if (header == NULL)
+ {
+ err = MCERR_MISSING_LAYOUT;
+ goto done;
+ }
+
+ /* reserve space for the layout text */
+ layout_text = (char*)malloc(header->uncompressed_length + 1);
+ if (layout_text == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* uncompress the layout text */
+ ziperr = zip_file_decompress(state.zip, layout_text, header->uncompressed_length);
+ if (ziperr != ZIPERR_NONE)
+ {
+ err = MCERR_ZIP_ERROR;
+ goto done;
+ }
+ layout_text[header->uncompressed_length] = '\0';
+
+ /* parse the layout text */
+ state.layout_xml = xml_string_read(layout_text, NULL);
+ if (state.layout_xml == NULL)
+ {
+ err = MCERR_XML_ERROR;
+ goto done;
+ }
+
+ /* locate the PCB node */
+ if (!find_pcb_and_resource_nodes(state.layout_xml, &state.pcb_node, &state.resources_node))
+ {
+ err = MCERR_NO_PCB_OR_RESOURCES;
+ goto done;
+ }
+
+ /* get the PCB resource_type */
+ pcb_type = xml_get_attribute_string(state.pcb_node, "type", "");
+ state.multicart->pcb_type = pool_strdup_lib(state.multicart->data->pool, pcb_type);
+ if (state.multicart->pcb_type == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ state.multicart->gamedrv_name = pool_strdup_lib(state.multicart->data->pool, gamedrv);
+ if (state.multicart->gamedrv_name == NULL)
+ {
+ err = MCERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ /* do we have to load resources? */
+ if (load_flags & MULTICART_FLAGS_LOAD_RESOURCES)
+ {
+ err = load_all_resources(&state);
+ if (err != MCERR_NONE)
+ goto done;
+
+ err = load_all_sockets(&state);
+ if (err != MCERR_NONE)
+ goto done;
+ }
+
+ err = MCERR_NONE;
+
+done:
+ if (pool != NULL)
+ pool_free_lib(pool);
+ if (state.zip != NULL)
+ zip_file_close(state.zip);
+ if (layout_text != NULL)
+ free(layout_text);
+ if (state.layout_xml != NULL)
+ xml_file_free(state.layout_xml);
+
+ if ((err != MCERR_NONE) && (state.multicart != NULL))
+ {
+ multicart_close(state.multicart);
+ state.multicart = NULL;
+ }
+ *cart = state.multicart;
+ return err;
+}
+
+
+/*-------------------------------------------------
+ multicart_close - closes a multicart
+-------------------------------------------------*/
+
+void multicart_close(multicart_t *cart)
+{
+ save_ram_resources(cart);
+ pool_free_lib(cart->data->pool);
+}