[gnome-games/wip/aplazas/playstation-disc-image: 16/16] Parse PlayStation disc images properly



commit d67b01a6b78c886acdae2baee68bbe78834ba024
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Fri Dec 23 06:10:22 2016 +0100

    Parse PlayStation disc images properly

 plugins/playstation/src/Makefile.am              |    1 +
 plugins/playstation/src/playstation-disc-image.c |  650 ++++++++++++++++++++++
 plugins/playstation/src/playstation-header.vala  |  104 +---
 3 files changed, 677 insertions(+), 78 deletions(-)
---
diff --git a/plugins/playstation/src/Makefile.am b/plugins/playstation/src/Makefile.am
index 1b0067e..ca69157 100644
--- a/plugins/playstation/src/Makefile.am
+++ b/plugins/playstation/src/Makefile.am
@@ -36,6 +36,7 @@ libgames_playstation_plugin_la_DEPENDENCIES = \
 
 libgames_playstation_plugin_la_SOURCES = \
        $(BUILT_SOURCES) \
+       playstation-disc-image.c \
        playstation-error.vala \
        playstation-game-factory.vala \
        playstation-header.vala \
diff --git a/plugins/playstation/src/playstation-disc-image.c 
b/plugins/playstation/src/playstation-disc-image.c
new file mode 100644
index 0000000..f974bf5
--- /dev/null
+++ b/plugins/playstation/src/playstation-disc-image.c
@@ -0,0 +1,650 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <gio/gio.h>
+
+////////////////////////////////////////////////////////////////////////
+// GamesDiscImageTime
+////////////////////////////////////////////////////////////////////////
+
+#define GAMES_DISC_IMAGE_FRAMES_PER_SECOND 75
+
+typedef struct {
+  guint8 minute;
+  guint8 second;
+  guint8 frame;
+} GamesDiscImageTime;
+
+static void
+games_disc_image_time_set_minute_second_frame (GamesDiscImageTime *time,
+                                               guint8              minute,
+                                               guint8              second,
+                                               guint8              frame)
+{
+  time->minute = minute;
+  time->second = second;
+  time->frame = frame;
+}
+
+static void
+games_disc_image_time_get_minute_second_frame (const GamesDiscImageTime *time,
+                                               guint8                   *minute,
+                                               guint8                   *second,
+                                               guint8                   *frame)
+{
+  *minute = time->minute;
+  *second = time->second;
+  *frame = time->frame;
+}
+
+static void
+games_disc_image_time_set_from_time_reference (GamesDiscImageTime *time,
+                                               guint8             *time_reference)
+{
+  gint32 block; // The value of the clock containing the target time
+  int minute, second, frame;
+
+  block = GINT32_FROM_LE (*((gint32 *) time_reference));
+
+  block += 2 * GAMES_DISC_IMAGE_FRAMES_PER_SECOND;
+  minute = block / (60 * GAMES_DISC_IMAGE_FRAMES_PER_SECOND);
+  block = block - minute * (60 * GAMES_DISC_IMAGE_FRAMES_PER_SECOND);
+  second = block / GAMES_DISC_IMAGE_FRAMES_PER_SECOND;
+  frame = block - second * GAMES_DISC_IMAGE_FRAMES_PER_SECOND;
+
+  games_disc_image_time_set_minute_second_frame (time, minute, second, frame);
+}
+
+static gint
+games_disc_image_time_get_sector (const GamesDiscImageTime *time)
+{
+  guint8 minute, second, frame;
+  games_disc_image_time_get_minute_second_frame (time, &minute, &second, &frame);
+
+  return (minute * 60 + second - 2) * GAMES_DISC_IMAGE_FRAMES_PER_SECOND + frame;
+}
+
+static void
+games_disc_image_time_increment (GamesDiscImageTime *time)
+{
+  guint8 minute, second, frame;
+  games_disc_image_time_get_minute_second_frame (time, &minute, &second, &frame);
+
+  frame++;
+  if (frame == GAMES_DISC_IMAGE_FRAMES_PER_SECOND) {
+    frame = 0;
+    second++;
+    if (second == 60) {
+      second = 0;
+      minute++;
+    }
+  }
+
+  games_disc_image_time_set_minute_second_frame (time, minute, second, frame);
+}
+
+////////////////////////////////////////////////////////////////////////
+// GamesDiscFrame
+////////////////////////////////////////////////////////////////////////
+
+#define GAMES_DISC_IMAGE_FRAME_SIZE           2352
+#define GAMES_DISC_IMAGE_FRAME_HEADER_SIZE    12
+
+typedef struct _GamesDiscFrameMode1 GamesDiscFrameMode1;
+struct _GamesDiscFrameMode1 {
+  guint8 synchronization[12];
+  guint8 header[12];
+  guint8 content[2048];
+  guint8 error_correction_code[280];
+};
+
+typedef struct _GamesDiscFrameMode2 GamesDiscFrameMode2;
+struct _GamesDiscFrameMode2 {
+  guint8 synchronization[12];
+  guint8 content[2340];
+};
+
+typedef union _GamesDiscFrame GamesDiscFrame;
+union _GamesDiscFrame {
+  GamesDiscFrameMode1 mode1;
+  GamesDiscFrameMode2 mode2;
+};
+
+////////////////////////////////////////////////////////////////////////
+// GamesDiscFileInfo
+////////////////////////////////////////////////////////////////////////
+
+typedef struct _GamesDiscFileInfo GamesDiscFileInfo;
+struct _GamesDiscFileInfo {
+  guint8 length;
+  guint8 ext_attr_length;
+  guint8 extent[8];
+  guint8 size[8];
+  guint8 date[7];
+  guint8 flags;
+  guint8 file_unit_size;
+  guint8 interleave;
+  guint8 volume_sequence_number[4];
+  guint8 name_length;
+};
+
+typedef gboolean (*GamesDiscFileInfoForeachCallback) (GamesDiscFileInfo *file_info, gpointer user_data);
+
+static gboolean
+games_disc_file_info_is_directory (GamesDiscFileInfo *file_info)
+{
+  g_return_val_if_fail (file_info != NULL, FALSE);
+
+  return file_info->flags & 0x2;
+}
+
+static gboolean
+games_disc_file_info_is_valid (const GamesDiscFileInfo *file_info)
+{
+  // FIXME Magic number, I have no idea what it is but it works.
+  const gsize MAGIC_SIZE = 47;
+
+  g_return_val_if_fail (file_info != NULL, FALSE);
+
+  return file_info->length >= MAGIC_SIZE + file_info->name_length;
+}
+
+static gchar *
+games_disc_file_info_access_name (GamesDiscFileInfo *file_info)
+{
+  g_return_val_if_fail (file_info != NULL, NULL);
+
+  return (gchar *) file_info + sizeof (GamesDiscFileInfo);
+}
+
+static gchar *
+games_disc_file_info_get_name (GamesDiscFileInfo *file_info)
+{
+  g_return_val_if_fail (file_info != NULL, NULL);
+
+  return g_strndup (games_disc_file_info_access_name (file_info), file_info->name_length);
+}
+
+static GamesDiscFileInfo *
+games_disc_file_info_get_next (const GamesDiscFileInfo *file_info)
+{
+  g_return_val_if_fail (file_info != NULL, NULL);
+
+  if (!games_disc_file_info_is_valid (file_info))
+    return NULL;
+
+  return (GamesDiscFileInfo *) ((gpointer) file_info + file_info->length);
+}
+
+static void
+games_disc_file_info_foreach_file (GamesDiscFileInfo                *file_info,
+                                   gsize                             size,
+                                   GamesDiscFileInfoForeachCallback  callback,
+                                   gpointer                          user_data)
+{
+  GamesDiscFileInfo *current;
+  GamesDiscFileInfo *next;
+
+  g_return_if_fail (file_info != NULL);
+
+  for (current = file_info; current != NULL && games_disc_file_info_is_valid (current); current = 
games_disc_file_info_get_next (current)) {
+    // The file info should never go beyond the end of the buffer.
+    if ((gpointer) current - (gpointer) file_info + sizeof (GamesDiscFileInfo) >= size ||
+        (gpointer) current - (gpointer) file_info + current->length >= size)
+      break;
+
+    if (!callback (current, user_data))
+      break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// GamesDisc
+////////////////////////////////////////////////////////////////////////
+
+struct _GamesDiscImage {
+  FILE *file_handle;
+  GFileInputStream *input_stream;
+};
+
+typedef struct _GamesDiscImage GamesDiscImage;
+
+static void
+games_disc_image_open (GamesDiscImage  *disc,
+                       const char      *filename,
+                       GError         **error)
+{
+  GFile *file;
+  GError *tmp_error = NULL;
+
+  file = g_file_new_for_path (filename);
+  g_clear_object (&disc->input_stream);
+  disc->input_stream = g_file_read (file, NULL, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+    g_object_unref(file);
+
+    return;
+  }
+
+  g_object_unref(file);
+}
+
+static void
+games_disc_image_dispose (GamesDiscImage *disc)
+{
+  g_clear_object (&disc->input_stream);
+}
+
+gboolean
+games_disc_image_read_frame (GamesDiscImage            *disc,
+                             const GamesDiscImageTime  *time,
+                             GamesDiscFrame            *frame,
+                             GCancellable              *cancellable,
+                             GError                   **error)
+{
+  gssize read;
+  gsize offset;
+  GError *tmp_error = NULL;
+
+  g_return_val_if_fail (disc != NULL, FALSE);
+  g_return_val_if_fail (time != NULL, FALSE);
+  g_return_val_if_fail (frame != NULL, FALSE);
+
+  // FIXME Check the multiplication doesn't overflow.
+  offset = games_disc_image_time_get_sector (time) * sizeof (GamesDiscFrame);
+  g_seekable_seek (G_SEEKABLE (disc->input_stream),
+                   offset, G_SEEK_SET,
+                   cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  read = g_input_stream_read (G_INPUT_STREAM (disc->input_stream),
+                              frame, sizeof (GamesDiscFrame),
+                              cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  return read == sizeof (GamesDiscFrame);
+}
+
+gboolean
+games_disc_image_read_directory (GamesDiscImage      *disc,
+                                 GamesDiscImageTime  *time,
+                                 guint8              *dst,
+                                 GCancellable        *cancellable,
+                                 GError             **error)
+{
+  gssize read;
+  gint sector;
+  GError *tmp_error = NULL;
+
+  sector = games_disc_image_time_get_sector(time);
+  g_seekable_seek (G_SEEKABLE (disc->input_stream),
+                   sector * GAMES_DISC_IMAGE_FRAME_SIZE + GAMES_DISC_IMAGE_FRAME_HEADER_SIZE + 12,
+                   G_SEEK_SET, cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  read = g_input_stream_read (G_INPUT_STREAM (disc->input_stream),
+                              dst, 2048,
+                              cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  if (read == -1)
+    return FALSE;
+
+  games_disc_image_time_increment (time);
+
+  sector = games_disc_image_time_get_sector(time);
+  g_seekable_seek (G_SEEKABLE (disc->input_stream),
+                   sector * GAMES_DISC_IMAGE_FRAME_SIZE + GAMES_DISC_IMAGE_FRAME_HEADER_SIZE + 12,
+                   G_SEEK_SET, cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  read = g_input_stream_read (G_INPUT_STREAM (disc->input_stream),
+                              dst + 2048, 2048,
+                              cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  if (read == -1)
+    return FALSE;
+
+  return TRUE;
+}
+
+typedef struct {
+  const gchar        *filename;
+  GamesDiscImageTime *time;
+  gboolean            is_dir;
+  gboolean            found;
+} GetFileData;
+
+static gboolean
+get_file_co (GamesDiscFileInfo *file_info,
+             gpointer           user_data)
+{
+  GetFileData *data = (GetFileData *) user_data;
+
+  if (games_disc_file_info_is_directory (file_info)) {
+    if (g_ascii_strncasecmp (games_disc_file_info_access_name (file_info), data->filename, 
file_info->name_length) == 0) {
+      if (data->filename[file_info->name_length] != '\\')
+        return TRUE;
+
+      data->filename += file_info->name_length + 1;
+
+      games_disc_image_time_set_from_time_reference (data->time, file_info->extent);
+      data->is_dir = TRUE;
+      data->found = TRUE;
+
+      return FALSE;
+    }
+  }
+  else {
+    if (g_ascii_strncasecmp (games_disc_file_info_access_name (file_info), data->filename, strlen 
(data->filename)) == 0) {
+      games_disc_image_time_set_from_time_reference (data->time, file_info->extent);
+      data->is_dir = FALSE;
+      data->found = TRUE;
+
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+static gboolean
+games_disc_image_get_file (GamesDiscImage      *disc,
+                           GamesDiscFileInfo   *file_info,
+                           const gchar         *filename,
+                           GamesDiscImageTime  *time,
+                           GCancellable        *cancellable,
+                           GError             **error)
+{
+  guint8 ddir[4096];
+  GetFileData data = { 0 };
+  gboolean success;
+  GError *tmp_error = NULL;
+
+  g_return_val_if_fail (filename != NULL, FALSE);
+
+  data.filename = filename;
+  data.time = time;
+  data.is_dir = TRUE;
+  data.found = FALSE;
+
+  while (data.is_dir) {
+    data.filename = filename;
+    data.time = time;
+    data.is_dir = FALSE;
+    data.found = FALSE;
+
+    games_disc_file_info_foreach_file (file_info, 4096, get_file_co, &data);
+
+    if (data.found && data.is_dir) {
+      success = games_disc_image_read_directory (disc, time, ddir, cancellable, &tmp_error);
+      if (tmp_error != NULL) {
+        g_propagate_error (error, tmp_error);
+
+        return FALSE;
+      }
+
+      if (!success)
+        return FALSE;
+
+      file_info = (GamesDiscFileInfo *) ddir;
+
+      break; // Parse the sub directory.
+    }
+  }
+
+  return data.found;
+}
+
+////////////////////////////////////////////////////////////////////////
+// PlayStation
+////////////////////////////////////////////////////////////////////////
+
+#define SYSTEM_CNF "SYSTEM.CNF;1"
+#define PSX_EXE "PSX.EXE;1"
+
+typedef struct _PlayStationGamesDiscInfo {
+  gchar *label;
+  gchar *exe;
+} PlayStationGamesDiscInfo;
+
+gboolean
+games_disc_image_get_playstation_info (GamesDiscImage            *disc,
+                                       PlayStationGamesDiscInfo  *games_disc_image_info,
+                                       GCancellable              *cancellable,
+                                       GError                   **error)
+{
+  gchar label_buffer[33] = "";
+
+  GamesDiscFileInfo *dir;
+  GamesDiscImageTime time;
+  guchar mdir[4096];
+  gchar exe_buffer[256];
+  gint i, len, c;
+  gchar *ptr;
+  GamesDiscFrame frame;
+  gboolean success;
+  GError *tmp_error = NULL;
+
+  games_disc_image_time_set_minute_second_frame (&time, 0, 2, 0x10);
+  success = games_disc_image_read_frame (disc, &time, &frame, cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  if (!success)
+    return FALSE;
+
+  memset (label_buffer, 0, sizeof (label_buffer));
+  memset (exe_buffer, 0, sizeof (exe_buffer));
+
+  strncpy (label_buffer, (const char *) frame.mode2.content + 52, 32);
+
+  // Skip head and sub, and go to the root directory record
+  dir = (GamesDiscFileInfo *) (frame.mode1.content + 156);
+
+  games_disc_image_time_set_from_time_reference (&time, dir->extent);
+
+  success = games_disc_image_read_directory (disc, &time, mdir, cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  if (!success)
+    return FALSE;
+
+  // Look for ststem configuration.
+
+  success = games_disc_image_get_file (disc, (GamesDiscFileInfo *) mdir, SYSTEM_CNF, &time, cancellable, 
&tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  if (success) {
+    if (!games_disc_image_read_frame (disc, &time, &frame, cancellable, &tmp_error))
+      return FALSE;
+
+    if (tmp_error != NULL) {
+      g_propagate_error (error, tmp_error);
+
+      return FALSE;
+    }
+
+    // Look of "BOOT = cdrom:\\"
+
+    sscanf ((char *) frame.mode1.content, "BOOT = cdrom:\\%255s", exe_buffer);
+    success = games_disc_image_get_file (disc, (GamesDiscFileInfo *) mdir, exe_buffer, &time, cancellable, 
&tmp_error);
+    if (tmp_error != NULL) {
+      g_propagate_error (error, tmp_error);
+
+      return FALSE;
+    }
+
+    if (success) {
+      if (games_disc_image_info != NULL) {
+        games_disc_image_info->label = strndup (label_buffer, sizeof (label_buffer));
+        games_disc_image_info->exe = strndup (exe_buffer, sizeof (exe_buffer));
+      }
+
+      return TRUE;
+    }
+
+    // Look of "BOOT = cdrom:"
+
+    sscanf ((char *) frame.mode1.content, "BOOT = cdrom:%255s", exe_buffer);
+    success = games_disc_image_get_file (disc, (GamesDiscFileInfo *) mdir, exe_buffer, &time, cancellable, 
&tmp_error);
+    if (tmp_error != NULL) {
+      g_propagate_error (error, tmp_error);
+
+      return FALSE;
+    }
+
+    if (success) {
+      if (games_disc_image_info != NULL) {
+        games_disc_image_info->label = strndup (label_buffer, sizeof (label_buffer));
+        games_disc_image_info->exe = strndup (exe_buffer, sizeof (exe_buffer));
+      }
+
+      return TRUE;
+    }
+
+    // Look of "cdrom:"
+
+    ptr = strstr((gchar *) frame.mode1.content, "cdrom:"); // Possibly the executable is in some subdir.
+    if (ptr == NULL)
+      return FALSE;
+
+    // Skip "cdrom:".
+    ptr += 6;
+
+    // Skip slashes.
+    while (*ptr == '\\' || *ptr == '/')
+      ptr++;
+
+    strncpy (exe_buffer, ptr, 255);
+    exe_buffer[255] = '\0';
+    ptr = exe_buffer;
+
+    // Keep only the first line.
+    while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n')
+      ptr++;
+    *ptr = '\0';
+
+    success = games_disc_image_get_file (disc, (GamesDiscFileInfo *) mdir, exe_buffer, &time, cancellable, 
&tmp_error);
+    if (tmp_error != NULL) {
+      g_propagate_error (error, tmp_error);
+
+      return FALSE;
+    }
+
+    if (success) {
+      if (games_disc_image_info != NULL) {
+        games_disc_image_info->label = strndup (label_buffer, sizeof (label_buffer));
+        games_disc_image_info->exe = strndup (exe_buffer, sizeof (exe_buffer));
+      }
+
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  // Look for the default executable.
+
+  success = games_disc_image_get_file (disc, (GamesDiscFileInfo *) mdir, PSX_EXE, &time, cancellable, 
&tmp_error);
+  if (tmp_error != NULL) {
+    g_propagate_error (error, tmp_error);
+
+    return FALSE;
+  }
+
+  if (success) {
+    if (games_disc_image_info != NULL) {
+      games_disc_image_info->label = strndup (label_buffer, sizeof (label_buffer));
+      games_disc_image_info->exe = strndup (PSX_EXE, sizeof (PSX_EXE));
+    }
+
+    return TRUE;
+  }
+
+  // SYSTEM.CNF and PSX.EXE not found.
+
+  return FALSE;
+}
+
+gboolean
+get_playstation_info (const gchar   *image_filename,
+                      gchar        **label,
+                      gchar        **exe,
+                      GCancellable  *cancellable,
+                      GError       **error)
+{
+  GamesDiscImage disc = { 0 };
+  gboolean success;
+  GError *tmp_error = NULL;
+
+  games_disc_image_open (&disc, image_filename, &tmp_error);
+  if (tmp_error != NULL) {
+    g_debug ("%s", tmp_error->message);
+    g_error_free (tmp_error);
+    games_disc_image_dispose (&disc);
+
+    return FALSE;
+  }
+
+  PlayStationGamesDiscInfo games_disc_image_info = { 0 };
+  success = games_disc_image_get_playstation_info (&disc, &games_disc_image_info, cancellable, &tmp_error);
+  if (tmp_error != NULL) {
+    g_debug ("%s", tmp_error->message);
+    g_error_free (tmp_error);
+    games_disc_image_dispose (&disc);
+
+    return FALSE;
+  }
+
+  games_disc_image_dispose (&disc);
+
+  if (success) {
+    *label = games_disc_image_info.label;
+    *exe = games_disc_image_info.exe;
+
+    return TRUE;
+  }
+
+  return FALSE;
+}
diff --git a/plugins/playstation/src/playstation-header.vala b/plugins/playstation/src/playstation-header.vala
index 19d1275..b63c447 100644
--- a/plugins/playstation/src/playstation-header.vala
+++ b/plugins/playstation/src/playstation-header.vala
@@ -1,24 +1,7 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 private class Games.PlayStationHeader : Object {
-       private const size_t[] HEADER_OFFSETS = {
-               0x85D2, // .bin.ecm
-               0x9320, // .bin
-               0x9360, // .iso 
-       };
-       private const size_t HEADER_TITLE_OFFSET = 0x20;
-       private const string HEADER_MAGIC_VALUE = "PLAYSTATION";
-
-       private const size_t[] BOOT_OFFSETS = {
-               0xBE64, // .bin.ecm
-               0xD368, // .bin
-               0xD3A8, // .iso
-               0xA9F98, // .bin
-       };
-       private const string BOOT_MAGIC_VALUE = "BOOT";
-
        // The ID prefixes must always be in uppercase.
-       private const string[] IDS = { "SLUS", "SCUS", "SLES", "SCES", "SLPS", "SLPM", "SCPS" };
        private const size_t DISC_ID_SIZE = 10;
 
        private static Regex disc_id_regex;
@@ -38,83 +21,45 @@ private class Games.PlayStationHeader : Object {
                if (_disc_id != null)
                        return;
 
-               _disc_id = get_id_from_boot ();
+               string label;
+               string exe;
+               if (!get_playstation_info (file.get_path (), out label, out exe))
+                       throw new PlayStationError.INVALID_HEADER (_("Not a PlayStation disc: ā€œ%sā€."), 
file.get_uri ());
+
+               _disc_id = parse_id_from_exe (exe);
                if (_disc_id != null)
                        return;
 
-               _disc_id = search_id_in_header ();
+               _disc_id = parse_id_from_label (label);
                if (_disc_id != null)
                        return;
 
                throw new PlayStationError.INVALID_HEADER (_("Invalid PlayStation header: disc ID not found 
in ā€œ%sā€."), file.get_uri ());
        }
 
-       private string? search_id_in_header () throws Error {
-               var offset = get_header_offset ();
-               if (offset == null)
-                       return null;
-
-               var stream = new StringInputStream (file);
-               var header = stream.read_string_for_size (offset + HEADER_TITLE_OFFSET, DISC_ID_SIZE);
-
-               var raw_id = header.up ();
-               raw_id = raw_id.replace ("_", "-");
-
-               foreach (var id in IDS) {
-                       if (!(id in header))
-                               continue;
-
-                       if (is_a_disc_id (raw_id))
-                               return raw_id;
-               }
-
-               return null;
-       }
-
-       private size_t? get_header_offset () throws Error {
-               var stream = new StringInputStream (file);
+       private string? parse_id_from_exe (string exe) throws Error {
+               var disc_id = exe.strip ();
+               disc_id = disc_id.split (";")[0];
+               disc_id = disc_id.replace ("_", "-");
+               disc_id = disc_id.replace (".", "");
+               disc_id = disc_id.up ();
 
-               foreach (var offset in HEADER_OFFSETS)
-                       if (stream.has_string (offset, HEADER_MAGIC_VALUE))
-                               return offset;
-
-               return null;
-       }
-
-       private string? get_id_from_boot () throws Error {
-               var offset = get_boot_offset ();
-               if (offset == null)
+               if (!is_a_disc_id (disc_id))
                        return null;
 
-               var stream = new StringInputStream (file);
-               var header = stream.read_string (offset);
-               header = header.up ();
-
-               foreach (var id in IDS) {
-                       if (!(id in header))
-                               continue;
-
-                       var raw_id = header.split (id)[1];
-                       raw_id = raw_id.split (";")[0];
-                       raw_id = raw_id.replace ("_", "-");
-                       raw_id = raw_id.replace (".", "");
-                       raw_id = (id + raw_id).up ();
-
-                       if (is_a_disc_id (raw_id))
-                               return raw_id;
-               }
-
-               return null;
+               return disc_id;
        }
 
-       private size_t? get_boot_offset () throws Error {
-               var stream = new StringInputStream (file);
+       private string? parse_id_from_label (string label) throws Error {
+               var disc_id = label.strip ();
+               disc_id = disc_id.replace ("_", "-");
+               disc_id = disc_id.strip ();
+               disc_id = disc_id.up ();
 
-               foreach (var offset in BOOT_OFFSETS)
-                       if (stream.has_string (offset, BOOT_MAGIC_VALUE))
-                               return offset;
+               if (!is_a_disc_id (disc_id))
+                       return null;
 
-               return null;
+               return disc_id;
        }
 
        private static bool is_a_disc_id (string disc_id) {
@@ -123,4 +68,7 @@ private class Games.PlayStationHeader : Object {
 
                return disc_id_regex.match (disc_id);
        }
+
+       [CCode (cname = "get_playstation_info")]
+       private static extern bool get_playstation_info (string filename, out string label, out string exe) 
throws Error;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]