[gnome-video-arcade] Make game play back work again with recent MAME releases.
- From: Matthew Barnes <mbarnes src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-video-arcade] Make game play back work again with recent MAME releases.
- Date: Mon, 13 Apr 2009 11:14:15 -0400 (EDT)
commit c658f0f6fc7415b5a14f623e6a066c9ca0e36931
Author: Matthew Barnes <mbarnes redhat com>
Date: Mon Apr 13 11:13:05 2009 -0400
Make game play back work again with recent MAME releases.
2009-04-13 Matthew Barnes <mbarnes redhat com>
* configure.ac:
Post-release version bump.
* src/Makefile.am:
Add gva-input-file.[ch].
* src/gva-error.h:
Add GVA_ERROR_FORMAT for file format errors.
* src/gva-input-file.c:
* src/gva-input-file.h:
New GvaInputFile class for parsing input file headers.
Knows about all header formats dating back to MAME 0.112.
* src/gva-mame-common.c (gva_mame_get_input_files):
Return a list of GvaInputFiles instead of a hash table of
filenames and ROM names.
* src/gva-play-back.c (play_back_add_input_file):
Take a GvaInputFile instead of a filename and ROM name.
* src/gva-play-back.c (gva_play_back_show):
Adapt to new gva_mame_get_input_files() signature.
* src/gva-util.c (gva_get_debug_flags):
* src/gva-util.h (GvaDebugFlags):
Add GVA_DEBUG_INP flag, which prints input file details.
---
.gitignore | 1 +
ChangeLog | 30 +++
NEWS | 13 +
README | 4 +-
configure.ac | 2 +-
po/.gitignore | 2 +
src/Makefile.am | 2 +
src/gva-error.h | 3 +
src/gva-input-file.c | 592 +++++++++++++++++++++++++++++++++++++++++++++++++
src/gva-input-file.h | 89 ++++++++
src/gva-mame-common.c | 45 +---
src/gva-mame.h | 2 +-
src/gva-play-back.c | 53 +++--
src/gva-ui.c | 4 +-
src/gva-util.c | 3 +-
src/gva-util.h | 5 +-
16 files changed, 786 insertions(+), 64 deletions(-)
diff --git a/.gitignore b/.gitignore
index af7af70..82a293b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ depcomp
gnome-doc-utils.make
gtk-doc.make
install-sh
+intltool-*.in
libtool
ltmain.sh
missing
diff --git a/ChangeLog b/ChangeLog
index c4f7b49..bf571ab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+2009-04-13 Matthew Barnes <mbarnes redhat com>
+
+ * configure.ac:
+ Post-release version bump.
+
+ * src/Makefile.am:
+ Add gva-input-file.[ch].
+
+ * src/gva-error.h:
+ Add GVA_ERROR_FORMAT for file format errors.
+
+ * src/gva-input-file.c:
+ * src/gva-input-file.h:
+ New GvaInputFile class for parsing input file headers.
+ Knows about all header formats dating back to MAME 0.112.
+
+ * src/gva-mame-common.c (gva_mame_get_input_files):
+ Return a list of GvaInputFiles instead of a hash table of
+ filenames and ROM names.
+
+ * src/gva-play-back.c (play_back_add_input_file):
+ Take a GvaInputFile instead of a filename and ROM name.
+
+ * src/gva-play-back.c (gva_play_back_show):
+ Adapt to new gva_mame_get_input_files() signature.
+
+ * src/gva-util.c (gva_get_debug_flags):
+ * src/gva-util.h (GvaDebugFlags):
+ Add GVA_DEBUG_INP flag, which prints input file details.
+
2009-04-11 Matthew Barnes <mbarnes redhat com>
* src/gva-process.c (process_stdout_ready):
diff --git a/NEWS b/NEWS
index e1b1187..bbfcc88 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+GNOME Video Arcade 0.6.7
+========================
+
+ Released April ??, 2009
+
+ What's New
+ ----------
+ * Make game play back work again with recent MAME releases.
+ * GNOME Video Arcade now works on OpenBSD. Huge thanks to
+ Pierre Riteau and Antoine Jacoutot for their assistance.
+ * Set the GVA_DEBUG environment variable to "inp" to show
+ input file header information.
+
GNOME Video Arcade 0.6.6
========================
diff --git a/README b/README
index 2abd42a..419fe38 100644
--- a/README
+++ b/README
@@ -127,11 +127,11 @@ including GNOME Video Arcade. This section supplements the INSTALL
file with information specific to GNOME Video Arcade.
These instructions are written specifically for GNOME Video Arcade
-version 0.6.5 and may change in forthcoming releases.
+version 0.6.7 and may change in forthcoming releases.
The standard installation procedure looks like this:
- $ cd /path/to/gnome-video-arcade-0.6.5
+ $ cd /path/to/gnome-video-arcade-0.6.7
$ ./configure
$ make
$ su -c "make install"
diff --git a/configure.ac b/configure.ac
index 3606686..7d89b17 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
AC_PREREQ([2.54])
-m4_define([gva_version], 0.6.6)
+m4_define([gva_version], 0.6.7)
AC_INIT([GNOME Video Arcade], [gva_version],
[mbarnes redhat com], gnome-video-arcade)
diff --git a/po/.gitignore b/po/.gitignore
index 68c54d2..98ea2e1 100644
--- a/po/.gitignore
+++ b/po/.gitignore
@@ -1,2 +1,4 @@
POTFILES
*.gmo
+.intltool-merge-cache
+Makefile.in.in
diff --git a/src/Makefile.am b/src/Makefile.am
index 139381a..2126b7a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,8 @@ gnome_video_arcade_SOURCES = \
gva-game-store.h \
gva-history.c \
gva-history.h \
+ gva-input-file.c \
+ gva-input-file.h \
gva-link-button.c \
gva-link-button.h \
gva-main.c \
diff --git a/src/gva-error.h b/src/gva-error.h
index bf09444..2207630 100644
--- a/src/gva-error.h
+++ b/src/gva-error.h
@@ -55,6 +55,8 @@ G_BEGIN_DECLS
* GvaError:
* @GVA_ERROR_CONFIG:
* Configuration error.
+ * @GVA_ERROR_FORMAT:
+ * File format error.
* @GVA_ERROR_MAME:
* Error from a MAME process.
* @GVA_ERROR_QUERY:
@@ -68,6 +70,7 @@ G_BEGIN_DECLS
typedef enum
{
GVA_ERROR_CONFIG,
+ GVA_ERROR_FORMAT,
GVA_ERROR_MAME,
GVA_ERROR_QUERY,
GVA_ERROR_SYSTEM
diff --git a/src/gva-input-file.c b/src/gva-input-file.c
new file mode 100644
index 0000000..4b521e7
--- /dev/null
+++ b/src/gva-input-file.c
@@ -0,0 +1,592 @@
+/* Copyright 2007-2009 Matthew Barnes
+ *
+ * This file is part of GNOME Video Arcade.
+ *
+ * GNOME Video Arcade is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * GNOME Video Arcade is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* A brief history of MAME's INP format.
+ *
+ * ??? - 0.112
+ * - Unknown. No INP headers?
+ *
+ * 0.113 - 0.115
+ * - Simple headers introduced.
+ *
+ * 0.116 - 0.124
+ * - Added support for playback of "extended" INP files that are commonly
+ * found on compete sites. [David Haywood]
+ *
+ * 0.125 - 0.128
+ * - Versioned headers introduced (INP version 2.0).
+ * - Dropped support for simple and extended formats.
+ * - Rewrote INP recording from scratch, since all old INPs are broken
+ * anyways. Header now includes timestamp, which overrides the default
+ * time base for MAME's system time. ... [Aaron Giles]
+ *
+ * 0.129 - 0.130 (present)
+ * - INP version 3.0.
+ * - To fix 02688 (DIP switch settings are not being stored in INP files),
+ * old INP files had to be broken. Since they were already broken, the
+ * corefile module was enhanced to support streaming compression, and the
+ * new INP files are output and processed compressed. [Aaron Giles]
+ */
+
+#include "gva-input-file.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "gva-db.h"
+#include "gva-error.h"
+#include "gva-mame.h"
+#include "gva-util.h"
+
+#define GVA_INPUT_FILE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), GVA_TYPE_INPUT_FILE, GvaInputFilePrivate))
+
+typedef struct _GvaInpHeaderSimple GvaInpHeaderSimple;
+typedef struct _GvaInpHeaderExtended GvaInpHeaderExtended;
+typedef struct _GvaInpHeaderVersioned GvaInpHeaderVersioned;
+
+enum {
+ PROP_0,
+ PROP_FILENAME,
+ PROP_FORMAT,
+ PROP_GAME,
+ PROP_ORIGIN,
+ PROP_TIMESTAMP
+};
+
+struct _GvaInputFilePrivate
+{
+ gchar *filename;
+ gchar *format;
+ gchar *game;
+ gchar *origin;
+ time_t timestamp;
+};
+
+/* Simple Header */
+struct _GvaInpHeaderSimple
+{
+ gchar rom_name[9]; /* ROM name */
+ gint8 major_version; /* MAME major version */
+ gint8 minor_version; /* MAME minor version */
+ gint8 beta_version; /* MAME beta version */
+ gchar reserved[20]; /* unused padding */
+};
+
+/* Extended Header */
+struct _GvaInpHeaderExtended
+{
+ gchar header[7]; /* "XINP\0\0\0" */
+ gchar rom_name[9]; /* ROM name */
+ gchar origin[32]; /* MAME version string */
+ guint32 timestamp; /* approx start time */
+ gchar reserved[32]; /* unused padding */
+};
+
+/* Versioned Header */
+struct _GvaInpHeaderVersioned
+{
+ gchar header[8]; /* "MAMEINP\0" */
+ gint64 timestamp; /* approx start time */
+ gint16 major_version; /* INP format major version */
+ gint16 minor_version; /* INP format minor version */
+ gchar rom_name[12]; /* ROM name */
+ gchar origin[32]; /* MAME version string */
+};
+
+static gpointer parent_class = NULL;
+
+static void
+input_file_dump_header (GvaInputFile *input_file)
+{
+ const gchar *filename;
+ const gchar *format;
+ const gchar *game;
+ const gchar *origin;
+ gchar *inpname;
+ time_t timestamp;
+
+ filename = gva_input_file_get_filename (input_file);
+ format = gva_input_file_get_format (input_file);
+ game = gva_input_file_get_game (input_file);
+ origin = gva_input_file_get_origin (input_file);
+ timestamp = gva_input_file_get_timestamp (input_file);
+
+ inpname = g_strdelimit (g_path_get_basename (filename), ".", '\0');
+
+ g_debug ("Input file: %s", inpname);
+ g_debug ("Game: %s", game);
+ g_debug ("%s", format);
+ g_debug ("Created %s", ctime (×tamp));
+ g_debug ("Recorded using %s", origin);
+ g_debug ("--");
+
+ g_free (inpname);
+}
+
+static gboolean
+input_file_read_simple (GvaInputFile *input_file,
+ GMappedFile *mapped_file)
+{
+ GvaInpHeaderSimple *header;
+ const gchar *contents;
+ const gchar *filename;
+ struct stat st;
+ GString *buffer;
+ gsize length;
+
+ filename = gva_input_file_get_filename (input_file);
+
+ contents = g_mapped_file_get_contents (mapped_file);
+ length = g_mapped_file_get_length (mapped_file);
+
+ if (length < sizeof (GvaInpHeaderSimple))
+ return FALSE;
+
+ header = (GvaInpHeaderSimple *) contents;
+
+ input_file->priv->game = g_strndup (header->rom_name, 9);
+
+ buffer = g_string_sized_new (64);
+ g_string_append_printf (
+ buffer, "MAME %d.%d",
+ header->major_version,
+ header->minor_version);
+ if (header->beta_version > 0)
+ g_string_append_printf (
+ buffer, "b%d", header->beta_version);
+ input_file->priv->origin = g_string_free (buffer, FALSE);
+
+ if (g_stat (filename, &st) == 0)
+ input_file->priv->timestamp = st.st_mtime;
+
+ input_file->priv->format = g_strdup ("INP simple format");
+
+ if (gva_get_debug_flags () & GVA_DEBUG_INP)
+ input_file_dump_header (input_file);
+
+ return TRUE;
+}
+
+static gboolean
+input_file_read_extended (GvaInputFile *input_file,
+ GMappedFile *mapped_file)
+{
+ GvaInpHeaderExtended *header;
+ const gchar *contents;
+ gsize length;
+
+ contents = g_mapped_file_get_contents (mapped_file);
+ length = g_mapped_file_get_length (mapped_file);
+
+ if (length < sizeof (GvaInpHeaderExtended))
+ return FALSE;
+
+ if (memcmp (contents, "XINP\0\0\0", 7) != 0)
+ return FALSE;
+
+ header = (GvaInpHeaderExtended *) contents;
+
+ input_file->priv->game = g_strndup (header->rom_name, 9);
+ input_file->priv->origin = g_strndup (header->origin, 32);
+ input_file->priv->timestamp = GINT32_FROM_LE (header->timestamp);
+
+ input_file->priv->format = g_strdup ("INP extended format");
+
+ if (gva_get_debug_flags () & GVA_DEBUG_INP)
+ input_file_dump_header (input_file);
+
+ return TRUE;
+}
+
+static gboolean
+input_file_read_versioned (GvaInputFile *input_file,
+ GMappedFile *mapped_file)
+{
+ GvaInpHeaderVersioned *header;
+ const gchar *contents;
+ gsize length;
+
+ contents = g_mapped_file_get_contents (mapped_file);
+ length = g_mapped_file_get_length (mapped_file);
+
+ if (length < sizeof (GvaInpHeaderVersioned))
+ return FALSE;
+
+ if (memcmp (contents, "MAMEINP\0", 8) != 0)
+ return FALSE;
+
+ header = (GvaInpHeaderVersioned *) contents;
+
+ input_file->priv->game = g_strndup (header->rom_name, 12);
+ input_file->priv->origin = g_strndup (header->origin, 32);
+ input_file->priv->timestamp = GINT64_FROM_LE (header->timestamp);
+
+ input_file->priv->format = g_strdup_printf (
+ "INP version %d.%d",
+ GINT16_FROM_LE (header->major_version),
+ GINT16_FROM_LE (header->minor_version));
+
+ if (gva_get_debug_flags () & GVA_DEBUG_INP)
+ input_file_dump_header (input_file);
+
+ return TRUE;
+}
+
+static void
+input_file_set_filename (GvaInputFile *input_file,
+ const gchar *filename)
+{
+ g_return_if_fail (input_file->priv->filename == NULL);
+
+ input_file->priv->filename = g_strdup (filename);
+}
+
+static void
+input_file_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_FILENAME:
+ input_file_set_filename (
+ GVA_INPUT_FILE (object),
+ g_value_get_string (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+input_file_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_FILENAME:
+ g_value_set_string (
+ value, gva_input_file_get_filename (
+ GVA_INPUT_FILE (object)));
+ return;
+
+ case PROP_FORMAT:
+ g_value_set_string (
+ value, gva_input_file_get_format (
+ GVA_INPUT_FILE (object)));
+ return;
+
+ case PROP_GAME:
+ g_value_set_string (
+ value, gva_input_file_get_game (
+ GVA_INPUT_FILE (object)));
+ return;
+
+ case PROP_ORIGIN:
+ g_value_set_string (
+ value, gva_input_file_get_origin (
+ GVA_INPUT_FILE (object)));
+ return;
+
+ case PROP_TIMESTAMP:
+ g_value_set_int64 (
+ value, gva_input_file_get_timestamp (
+ GVA_INPUT_FILE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+input_file_finalize (GObject *object)
+{
+ GvaInputFilePrivate *priv;
+
+ priv = GVA_INPUT_FILE_GET_PRIVATE (object);
+
+ g_free (priv->filename);
+ g_free (priv->format);
+ g_free (priv->game);
+ g_free (priv->origin);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+input_file_class_init (GvaInputFileClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (GvaInputFilePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = input_file_set_property;
+ object_class->get_property = input_file_get_property;
+ object_class->finalize = input_file_finalize;
+
+ /**
+ * GvaInputFile:filename:
+ *
+ * The name of the input file.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_FILENAME,
+ g_param_spec_string (
+ "filename",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GvaInputFile:format:
+ *
+ * The format of the input file.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_FORMAT,
+ g_param_spec_string (
+ "format",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READABLE));
+
+ /**
+ * GvaInputFile:game:
+ *
+ * The corresponding ROM name for the input file.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_GAME,
+ g_param_spec_string (
+ "game",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READABLE));
+
+ /**
+ * GvaInputFile:origin:
+ *
+ * The version of MAME that recorded the input file.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_ORIGIN,
+ g_param_spec_string (
+ "origin",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READABLE));
+
+ /**
+ * GvaInputFile:timestamp:
+ *
+ * The creation timestamp for the input file.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_TIMESTAMP,
+ g_param_spec_int64 (
+ "timestamp",
+ NULL,
+ NULL,
+ G_MININT64,
+ G_MAXINT64,
+ 0,
+ G_PARAM_READABLE));
+}
+
+static void
+input_file_init (GvaInputFile *input_file)
+{
+ input_file->priv = GVA_INPUT_FILE_GET_PRIVATE (input_file);
+}
+
+GType
+gva_input_file_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0))
+ {
+ static const GTypeInfo type_info =
+ {
+ sizeof (GvaInputFileClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) input_file_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GvaInputFile),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) input_file_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ G_TYPE_OBJECT, "GvaInputFile", &type_info, 0);
+ }
+
+ return type;
+}
+
+GvaInputFile *
+gva_input_file_new (const gchar *filename)
+{
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ return g_object_new (GVA_TYPE_INPUT_FILE, "filename", filename, NULL);
+}
+
+gboolean
+gva_input_file_read (GvaInputFile *input_file,
+ GError **error)
+{
+ GMappedFile *mapped_file = NULL;
+ const gchar *filename;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (GVA_IS_INPUT_FILE (input_file), FALSE);
+
+ if (input_file->priv->format != NULL)
+ goto exit;
+
+ filename = gva_input_file_get_filename (input_file);
+
+ mapped_file = g_mapped_file_new (filename, FALSE, error);
+ if (mapped_file == NULL)
+ return FALSE;
+
+ input_file->priv->filename = g_strdup (filename);
+
+ /* Check for a versioned INP header. */
+ if (input_file_read_versioned (input_file, mapped_file))
+ goto exit;
+
+ /* Check for an extended INP header. */
+ if (input_file_read_extended (input_file, mapped_file))
+ goto exit;
+
+ /* Check for a simple INP header. */
+ if (input_file_read_simple (input_file, mapped_file))
+ goto exit;
+
+ g_set_error (
+ error, GVA_ERROR, GVA_ERROR_FORMAT,
+ _("Invalid or unsupported INP file format"));
+
+ success = FALSE;
+
+exit:
+ if (success)
+ {
+ g_object_freeze_notify (G_OBJECT (input_file));
+ g_object_notify (G_OBJECT (input_file), "format");
+ g_object_notify (G_OBJECT (input_file), "game");
+ g_object_notify (G_OBJECT (input_file), "origin");
+ g_object_notify (G_OBJECT (input_file), "timestamp");
+ g_object_thaw_notify (G_OBJECT (input_file));
+ }
+
+ if (mapped_file != NULL)
+ g_mapped_file_free (mapped_file);
+
+ return success;
+}
+
+GvaProcess *
+gva_input_file_play_back (GvaInputFile *input_file,
+ GError **error)
+{
+ GvaProcess *process;
+ const gchar *filename;
+ const gchar *game;
+ gchar *inpname;
+
+ g_return_val_if_fail (GVA_IS_INPUT_FILE (input_file), NULL);
+
+ if (!gva_input_file_read (input_file, error))
+ return NULL;
+
+ filename = gva_input_file_get_filename (input_file);
+ game = gva_input_file_get_game (input_file);
+
+ inpname = g_strdelimit (g_path_get_basename (filename), ".", '\0');
+ process = gva_mame_playback_game (game, inpname, error);
+ g_free (inpname);
+
+ return process;
+}
+
+const gchar *
+gva_input_file_get_filename (GvaInputFile *input_file)
+{
+ g_return_val_if_fail (GVA_IS_INPUT_FILE (input_file), NULL);
+
+ return input_file->priv->filename;
+}
+
+const gchar *
+gva_input_file_get_format (GvaInputFile *input_file)
+{
+ g_return_val_if_fail (GVA_IS_INPUT_FILE (input_file), NULL);
+
+ if (input_file->priv->format == NULL)
+ return NULL;
+
+ return input_file->priv->format;
+}
+
+const gchar *
+gva_input_file_get_game (GvaInputFile *input_file)
+{
+ g_return_val_if_fail (GVA_IS_INPUT_FILE (input_file), NULL);
+
+ return input_file->priv->game;
+}
+
+const gchar *
+gva_input_file_get_origin (GvaInputFile *input_file)
+{
+ g_return_val_if_fail (GVA_IS_INPUT_FILE (input_file), NULL);
+
+ return input_file->priv->origin;
+}
+
+time_t
+gva_input_file_get_timestamp (GvaInputFile *input_file)
+{
+ g_return_val_if_fail (GVA_IS_INPUT_FILE (input_file), 0);
+
+ return input_file->priv->timestamp;
+}
diff --git a/src/gva-input-file.h b/src/gva-input-file.h
new file mode 100644
index 0000000..fe7e640
--- /dev/null
+++ b/src/gva-input-file.h
@@ -0,0 +1,89 @@
+/* Copyright 2007-2009 Matthew Barnes
+ *
+ * This file is part of GNOME Video Arcade.
+ *
+ * GNOME Video Arcade is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * GNOME Video Arcade is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: gva-input-file
+ * @short_description: A stable interface for MAME INP files
+ *
+ * A #GvaInputFile provides metadata about a MAME INP file. It handles
+ * both current and historical INP formats.
+ **/
+
+#ifndef GVA_INPUT_FILE_H
+#define GVA_INPUT_FILE_H
+
+#include "gva-common.h"
+#include "gva-process.h"
+
+/* Standard GObject macros */
+#define GVA_TYPE_INPUT_FILE \
+ (gva_input_file_get_type ())
+#define GVA_INPUT_FILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), GVA_TYPE_INPUT_FILE, GvaInputFile))
+#define GVA_INPUT_FILE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), GVA_TYPE_INPUT_FILE, GvaInputFileClass))
+#define GVA_IS_INPUT_FILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), GVA_TYPE_INPUT_FILE))
+#define GVA_IS_INPUT_FILE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), GVA_TYPE_INPUT_FILE))
+#define GVA_INPUT_FILE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), GVA_TYPE_INPUT_FILE, GvaInputFileClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GvaInputFile GvaInputFile;
+typedef struct _GvaInputFileClass GvaInputFileClass;
+typedef struct _GvaInputFilePrivate GvaInputFilePrivate;
+
+/**
+ * GvaInputFile:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _GvaInputFile
+{
+ GObject parent;
+ GvaInputFilePrivate *priv;
+};
+
+struct _GvaInputFileClass
+{
+ GObjectClass parent_class;
+};
+
+GType gva_input_file_get_type (void);
+GvaInputFile * gva_input_file_new (const gchar *filename);
+gboolean gva_input_file_read (GvaInputFile *input_file,
+ GError **error);
+GvaProcess * gva_input_file_play_back (GvaInputFile *input_file,
+ GError **error);
+const gchar * gva_input_file_get_filename (GvaInputFile *input_file);
+const gchar * gva_input_file_get_format (GvaInputFile *input_file);
+const gchar * gva_input_file_get_game (GvaInputFile *input_file);
+const gchar * gva_input_file_get_origin (GvaInputFile *input_file);
+time_t gva_input_file_get_timestamp (GvaInputFile *input_file);
+
+G_END_DECLS
+
+#endif /* GVA_INPUT_FILE_H */
diff --git a/src/gva-mame-common.c b/src/gva-mame-common.c
index 7feaa39..649cdd7 100644
--- a/src/gva-mame-common.c
+++ b/src/gva-mame-common.c
@@ -27,6 +27,7 @@
#endif
#include "gva-error.h"
+#include "gva-input-file.h"
#include "gva-mame-process.h"
#include "gva-mute-button.h"
#include "gva-preferences.h"
@@ -291,18 +292,18 @@ gva_mame_get_search_paths (const gchar *config_key,
* gva_mame_get_input_files:
* @error: return location for a @GError, or %NULL
*
- * Returns a #GHashTable of input files for playback. The keys of the
- * #GHashTable are absolute filenames and the values are the corresponding
- * game name. If an error occurs, it returns %NULL and sets @error.
+ * Returns a list of #GvaInputFile instances corresponding to files in the
+ * input directory. If an error occurs, it returns %NULL and sets @error.
*
- * Returns: a #GHashTable of input files, or %NULL
+ * Returns: a list of #GvaInputFile instances, or %NULL
**/
-GHashTable *
+GList *
gva_mame_get_input_files (GError **error)
{
GHashTable *hash_table;
const gchar *basename;
const gchar *directory;
+ GList *list = NULL;
GDir *dir;
directory = gva_mame_get_input_directory (error);
@@ -319,41 +320,17 @@ gva_mame_get_input_files (GError **error)
while ((basename = g_dir_read_name (dir)) != NULL)
{
gchar *filename;
- GIOChannel *channel;
- GError *local_error = NULL;
+ GvaInputFile *input_file;
filename = g_build_filename (directory, basename, NULL);
-
- channel = g_io_channel_new_file (filename, "r", &local_error);
-
- if (channel != NULL)
- {
- gchar name[16];
- GIOStatus status;
-
- status = g_io_channel_read_chars (
- channel, name, sizeof (name),
- NULL, &local_error);
- if (status == G_IO_STATUS_NORMAL)
- g_hash_table_insert (
- hash_table, filename,
- g_strdup (name));
- g_io_channel_unref (channel);
- }
-
- if (local_error != NULL)
- {
- g_free (filename);
- g_hash_table_destroy (hash_table);
- g_propagate_error (error, local_error);
- hash_table = NULL;
- break;
- }
+ input_file = gva_input_file_new (filename);
+ list = g_list_prepend (list, input_file);
+ g_free (filename);
}
g_dir_close (dir);
- return hash_table;
+ return g_list_reverse (list);
}
/**
diff --git a/src/gva-mame.h b/src/gva-mame.h
index 8dbf9b2..6c523a2 100644
--- a/src/gva-mame.h
+++ b/src/gva-mame.h
@@ -55,7 +55,7 @@ gchar * gva_mame_get_config_value (const gchar *config_key,
gboolean gva_mame_has_config_value (const gchar *config_key);
gchar ** gva_mame_get_search_paths (const gchar *config_key,
GError **error);
-GHashTable * gva_mame_get_input_files (GError **error);
+GList * gva_mame_get_input_files (GError **error);
GvaProcess * gva_mame_list_xml (GError **error);
gchar * gva_mame_verify_roms (const gchar *name,
GError **error);
diff --git a/src/gva-play-back.c b/src/gva-play-back.c
index ada1057..6ed3b03 100644
--- a/src/gva-play-back.c
+++ b/src/gva-play-back.c
@@ -28,6 +28,7 @@
#include "gva-db.h"
#include "gva-error.h"
#include "gva-game-store.h"
+#include "gva-input-file.h"
#include "gva-mame.h"
#include "gva-time.h"
#include "gva-tree-view.h"
@@ -113,33 +114,42 @@ play_back_selection_changed_cb (GtkTreeSelection *tree_selection)
}
static void
-play_back_add_input_file (const gchar *inpfile,
- const gchar *name,
+play_back_add_input_file (GvaInputFile *input_file,
GvaGameStore *game_store)
{
GtkTreeIter iter;
+ const gchar *filename;
+ const gchar *game;
gchar *comment = NULL;
gchar **result = NULL;
gchar *inpname;
gchar *sql;
gint rows;
struct stat st;
- time_t *time;
+ time_t timestamp;
GError *error = NULL;
- if (g_stat (inpfile, &st) != 0)
+ if (!gva_input_file_read (input_file, &error))
{
- g_warning ("%s: %s", inpfile, g_strerror (errno));
+ gva_error_handle (&error);
return;
}
- time = &st.st_ctime;
+ game = gva_input_file_get_game (input_file);
+ filename = gva_input_file_get_filename (input_file);
+ timestamp = gva_input_file_get_timestamp (input_file);
+
+ if (g_stat (filename, &st) != 0)
+ {
+ g_warning ("%s: %s", filename, g_strerror (errno));
+ return;
+ }
/* Try to fetch the comment from the database. */
sql = g_strdup_printf (
"SELECT comment FROM playback WHERE "
"name == '%s' AND inode == %" G_GINT64_FORMAT,
- name, (gint64) st.st_ino);
+ game, (gint64) st.st_ino);
if (gva_db_get_table (sql, &result, &rows, NULL, &error))
{
if (rows > 0)
@@ -154,7 +164,7 @@ play_back_add_input_file (const gchar *inpfile,
{
sql = g_strdup_printf (
"SELECT description FROM available "
- "WHERE name == '%s'", name);
+ "WHERE name == '%s'", game);
if (gva_db_get_table (sql, &result, &rows, NULL, &error))
{
if (rows > 0)
@@ -168,7 +178,7 @@ play_back_add_input_file (const gchar *inpfile,
/* The game may not be available anymore. */
if (comment == NULL)
{
- g_warning ("%s: Game '%s' not found", inpfile, name);
+ g_warning ("%s: Game '%s' not found", filename, game);
return;
}
@@ -176,14 +186,14 @@ play_back_add_input_file (const gchar *inpfile,
gtk_tree_store_set (
GTK_TREE_STORE (game_store), &iter,
- GVA_GAME_STORE_COLUMN_NAME, name,
+ GVA_GAME_STORE_COLUMN_NAME, game,
GVA_GAME_STORE_COLUMN_COMMENT, comment,
GVA_GAME_STORE_COLUMN_INODE, (gint64) st.st_ino,
- GVA_GAME_STORE_COLUMN_INPFILE, inpfile,
- GVA_GAME_STORE_COLUMN_TIME, time,
+ GVA_GAME_STORE_COLUMN_INPFILE, filename,
+ GVA_GAME_STORE_COLUMN_TIME, ×tamp,
-1);
- inpname = g_strdelimit (g_path_get_basename (inpfile), ".", '\0');
+ inpname = g_strdelimit (g_path_get_basename (filename), ".", '\0');
gva_game_store_index_insert (game_store, inpname, &iter);
g_free (inpname);
@@ -239,7 +249,7 @@ gva_play_back_init (void)
void
gva_play_back_show (const gchar *inpname)
{
- GHashTable *hash_table;
+ GList *list;
GvaGameStore *game_store;
GtkTreeView *view;
GError *error = NULL;
@@ -247,17 +257,14 @@ gva_play_back_show (const gchar *inpname)
view = GTK_TREE_VIEW (GVA_WIDGET_PLAY_BACK_TREE_VIEW);
game_store = GVA_GAME_STORE (gtk_tree_view_get_model (view));
- hash_table = gva_mame_get_input_files (&error);
+ gva_game_store_clear (game_store);
+
+ list = gva_mame_get_input_files (&error);
gva_error_handle (&error);
- if (hash_table != NULL)
- {
- gva_game_store_clear (game_store);
- g_hash_table_foreach (
- hash_table, (GHFunc) play_back_add_input_file,
- game_store);
- g_hash_table_destroy (hash_table);
- }
+ g_list_foreach (list, (GFunc) play_back_add_input_file, game_store);
+ g_list_foreach (list, (GFunc) g_object_unref, NULL);
+ g_list_free (list);
if (inpname != NULL)
{
diff --git a/src/gva-ui.c b/src/gva-ui.c
index 6f06b99..d877d36 100644
--- a/src/gva-ui.c
+++ b/src/gva-ui.c
@@ -64,7 +64,9 @@ static const gchar *license =
"along with this program. If not, see <http://www.gnu.org/licenses/>.";
static void
-record_game_exited (GvaProcess *process, gint status, gchar *inpname)
+record_game_exited (GvaProcess *process,
+ gint status,
+ gchar *inpname)
{
if (process->error == NULL)
gva_play_back_show (inpname);
diff --git a/src/gva-util.c b/src/gva-util.c
index 23add53..f5bfa1d 100644
--- a/src/gva-util.c
+++ b/src/gva-util.c
@@ -165,7 +165,8 @@ gva_get_debug_flags (void)
{
{ "mame", GVA_DEBUG_MAME },
{ "sql", GVA_DEBUG_SQL },
- { "io", GVA_DEBUG_IO }
+ { "io", GVA_DEBUG_IO },
+ { "inp", GVA_DEBUG_INP }
};
const gchar *env = g_getenv ("GVA_DEBUG");
diff --git a/src/gva-util.h b/src/gva-util.h
index 4df3c5a..e1004d0 100644
--- a/src/gva-util.h
+++ b/src/gva-util.h
@@ -41,6 +41,8 @@ G_BEGIN_DECLS
* Print SQL commands to the game database.
* @GVA_DEBUG_IO:
* Print all communication between GVA and MAME.
+ * @GVA_DEBUG_INP:
+ * Print information about input files.
*
* These flags indicate which types of debugging messages will be triggered
* at runtime. Debugging messages can be triggered by setting the GVA_DEBUG
@@ -51,7 +53,8 @@ typedef enum
GVA_DEBUG_NONE = 0,
GVA_DEBUG_MAME = 1 << 0,
GVA_DEBUG_SQL = 1 << 1,
- GVA_DEBUG_IO = 1 << 2
+ GVA_DEBUG_IO = 1 << 2,
+ GVA_DEBUG_INP = 1 << 3
} GvaDebugFlags;
gchar * gva_choose_inpname (const gchar *game);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]