[gnome-games] dreamcast: Support .gdi files



commit b82945159a271ba4c413b10f7d24764954afc592
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Tue Nov 3 09:27:38 2020 +0100

    dreamcast: Support .gdi files
    
    Fixes https://gitlab.gnome.org/GNOME/gnome-games/-/issues/48

 data/org.gnome.Games.desktop.in.in            |  2 +-
 plugins/dreamcast/src/dreamcast-error.vala    |  2 +
 plugins/dreamcast/src/dreamcast-header.vala   | 17 ++++++---
 plugins/dreamcast/src/dreamcast-plugin.vala   | 44 ++++++++++++++++++++--
 plugins/dreamcast/src/{gdi => }/gdi.vala      | 53 +++++++++++++++++----------
 plugins/dreamcast/src/gdi/gdi-error.vala      | 10 -----
 plugins/dreamcast/src/gdi/gdi-track-node.vala | 10 -----
 plugins/dreamcast/src/meson.build             |  5 +--
 8 files changed, 89 insertions(+), 54 deletions(-)
---
diff --git a/data/org.gnome.Games.desktop.in.in b/data/org.gnome.Games.desktop.in.in
index 14d62d37..779e780a 100644
--- a/data/org.gnome.Games.desktop.in.in
+++ b/data/org.gnome.Games.desktop.in.in
@@ -13,4 +13,4 @@ Type=Application
 StartupNotify=true
 DBusActivatable=true
 Categories=GNOME;GTK;Player;Game;
-MimeType=application/vnd.nintendo.snes.rom;application/x-amiga-disk-format;application/x-atari-2600-rom;application/x-atari-7800-rom;application/x-atari-lynx-rom;application/x-cue;application/x-discjuggler-cd-image;application/x-doom-wad;application/x-fds-disk;application/x-gameboy-color-rom;application/x-gameboy-rom;application/x-gamecube-rom;application/x-gamegear-rom;application/x-gba-rom;application/x-genesis-32x-rom;application/x-genesis-rom;application/x-love-game;application/x-mame-rom;application/x-ms-dos-executable;application/x-n64-rom;application/x-neo-geo-pocket-color-rom;application/x-neo-geo-pocket-rom;application/x-nes-rom;application/x-nintendo-ds-rom;application/x-pc-engine-rom;application/x-playstation-rom;application/x-saturn-rom;application/x-sega-cd-rom;application/x-sega-pico-rom;application/x-sg1000-rom;application/x-sms-rom;application/x-virtual-boy-rom;application/x-wii-rom;application/x-wii-wad;application/x-wonderswan-color-rom;application/x-wonderswan-rom
 ;application/zip;
+MimeType=application/vnd.nintendo.snes.rom;application/x-amiga-disk-format;application/x-atari-2600-rom;application/x-atari-7800-rom;application/x-atari-lynx-rom;application/x-cue;application/x-discjuggler-cd-image;application/x-doom-wad;application/x-fds-disk;application/x-gameboy-color-rom;application/x-gameboy-rom;application/x-gamecube-rom;application/x-gamegear-rom;application/x-gba-rom;application/x-gd-rom-cue;application/x-genesis-32x-rom;application/x-genesis-rom;application/x-love-game;application/x-mame-rom;application/x-ms-dos-executable;application/x-n64-rom;application/x-neo-geo-pocket-color-rom;application/x-neo-geo-pocket-rom;application/x-nes-rom;application/x-nintendo-ds-rom;application/x-pc-engine-rom;application/x-playstation-rom;application/x-saturn-rom;application/x-sega-cd-rom;application/x-sega-pico-rom;application/x-sg1000-rom;application/x-sms-rom;application/x-virtual-boy-rom;application/x-wii-rom;application/x-wii-wad;application/x-wonderswan-color-rom;app
 lication/x-wonderswan-rom;application/zip;
diff --git a/plugins/dreamcast/src/dreamcast-error.vala b/plugins/dreamcast/src/dreamcast-error.vala
index 2a521659..1bb2dd8c 100644
--- a/plugins/dreamcast/src/dreamcast-error.vala
+++ b/plugins/dreamcast/src/dreamcast-error.vala
@@ -1,5 +1,7 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 errordomain Games.DreamcastError {
+       INVALID_GDI,
+       INVALID_FILE_TYPE,
        INVALID_HEADER,
 }
diff --git a/plugins/dreamcast/src/dreamcast-header.vala b/plugins/dreamcast/src/dreamcast-header.vala
index 9f2a5516..c9004529 100644
--- a/plugins/dreamcast/src/dreamcast-header.vala
+++ b/plugins/dreamcast/src/dreamcast-header.vala
@@ -1,6 +1,7 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 private class Games.DreamcastHeader : Object {
+       private const size_t HEADER_OFFSET = 0x10;
        private const size_t HEADER_SIZE = 0x100;
 
        private const size_t MAGIC_OFFSET = 0x0;
@@ -43,17 +44,23 @@ private class Games.DreamcastHeader : Object {
                if (header_offset != null)
                        return header_offset;
 
+               if (lookup_header_offset (HEADER_OFFSET)) {
+                       header_offset = HEADER_OFFSET;
+
+                       return header_offset;
+               }
+
                var path = file.get_path ();
                var header_offsets = Grep.get_offsets (path, MAGIC_VALUE);
 
                foreach (var offset in header_offsets)
-                       if (lookup_header_offset (offset))
+                       if (lookup_header_offset (offset)) {
                                header_offset = offset;
 
-               if (header_offset == null)
-                       throw new DreamcastError.INVALID_HEADER ("The file doesn’t have a Dreamcast header.");
+                               return header_offset;
+                       }
 
-               return header_offset;
+               throw new DreamcastError.INVALID_HEADER ("The file doesn’t have a Dreamcast header.");
        }
 
        private bool lookup_header_offset (size_t offset) throws Error {
@@ -61,7 +68,7 @@ private class Games.DreamcastHeader : Object {
                if (!stream.has_string (offset + MAGIC_OFFSET, MAGIC_VALUE))
                        return false;
 
-               var header = stream.read_string_for_size (offset + MAGIC_OFFSET, HEADER_SIZE);
+               var header = stream.read_string_for_size (offset, HEADER_SIZE);
 
                return header.length == HEADER_SIZE && header.is_ascii ();
        }
diff --git a/plugins/dreamcast/src/dreamcast-plugin.vala b/plugins/dreamcast/src/dreamcast-plugin.vala
index 7f620922..c3aacae1 100644
--- a/plugins/dreamcast/src/dreamcast-plugin.vala
+++ b/plugins/dreamcast/src/dreamcast-plugin.vala
@@ -1,16 +1,18 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 private class Games.DreamcastPlugin : Object, Plugin {
+       private const string GDI_MIME_TYPE = "application/x-gd-rom-cue";
        private const string CDI_MIME_TYPE = "application/x-discjuggler-cd-image";
        private const string DREAMCAST_MIME_TYPE = "application/x-dreamcast-rom";
        private const string PLATFORM_ID = "Dreamcast";
        private const string PLATFORM_NAME = _("Dreamcast");
        private const string PLATFORM_UID_PREFIX = "dreamcast";
+       private const string[] MIME_TYPES = { GDI_MIME_TYPE, CDI_MIME_TYPE };
 
        private static RetroPlatform platform;
 
        static construct {
-               platform = new RetroPlatform (PLATFORM_ID, PLATFORM_NAME, { CDI_MIME_TYPE }, 
PLATFORM_UID_PREFIX);
+               platform = new RetroPlatform (PLATFORM_ID, PLATFORM_NAME, MIME_TYPES, PLATFORM_UID_PREFIX);
        }
 
        public Platform[] get_platforms () {
@@ -18,13 +20,14 @@ private class Games.DreamcastPlugin : Object, Plugin {
        }
 
        public string[] get_mime_types () {
-               return { CDI_MIME_TYPE };
+               return MIME_TYPES;
        }
 
        public UriGameFactory[] get_uri_game_factories () {
                var game_uri_adapter = new GenericGameUriAdapter (game_for_uri);
                var factory = new GenericUriGameFactory (game_uri_adapter);
-               factory.add_mime_type (CDI_MIME_TYPE);
+               foreach (var mime_type in MIME_TYPES)
+                       factory.add_mime_type (mime_type);
 
                return { factory };
        }
@@ -44,7 +47,26 @@ private class Games.DreamcastPlugin : Object, Plugin {
 
        private static Game game_for_uri (Uri uri) throws Error {
                var file = uri.to_file ();
-               var header = new DreamcastHeader (file);
+               var file_info = file.query_info (FileAttribute.STANDARD_CONTENT_TYPE, 
FileQueryInfoFlags.NONE);
+               var mime_type = file_info.get_content_type ();
+
+               File bin_file;
+               switch (mime_type) {
+               case GDI_MIME_TYPE:
+                       var gdi = new Gdi (file);
+                       gdi.parse ();
+                       bin_file = get_binary_file (gdi);
+
+                       break;
+               case CDI_MIME_TYPE:
+                       bin_file = file;
+
+                       break;
+               default:
+                       throw new DreamcastError.INVALID_FILE_TYPE ("Invalid file type: expected %s or %s but 
got %s for file %s.", GDI_MIME_TYPE, CDI_MIME_TYPE, mime_type, uri.to_string ());
+               }
+
+               var header = new DreamcastHeader (bin_file);
                header.check_validity ();
 
                var uid = new Uid (get_uid (header));
@@ -59,6 +81,20 @@ private class Games.DreamcastPlugin : Object, Plugin {
 
                return game;
        }
+
+       private static File get_binary_file (Gdi gdi) throws Error {
+               if (gdi.tracks_number == 0)
+                       throw new DreamcastError.INVALID_GDI ("The file “%s” doesn’t have a track.", 
gdi.file.get_uri ());
+
+               var track = gdi.get_track (0);
+               var file = track.file;
+
+               var file_info = file.query_info ("*", FileQueryInfoFlags.NONE);
+               if (file_info.get_content_type () != DREAMCAST_MIME_TYPE)
+                       throw new DreamcastError.INVALID_FILE_TYPE ("The file “%s” doesn’t have a valid 
Dreamcast binary file.", gdi.file.get_uri ());
+
+               return file;
+       }
 }
 
 [ModuleInit]
diff --git a/plugins/dreamcast/src/gdi/gdi.vala b/plugins/dreamcast/src/gdi.vala
similarity index 85%
rename from plugins/dreamcast/src/gdi/gdi.vala
rename to plugins/dreamcast/src/gdi.vala
index 04d3e004..80de9081 100644
--- a/plugins/dreamcast/src/gdi/gdi.vala
+++ b/plugins/dreamcast/src/gdi.vala
@@ -3,24 +3,26 @@
 public class Games.Gdi : Object {
        private const string NEW_LINE = "\n";
 
-       private File _file;
-       public File file {
-               get { return _file; }
-       }
+       public File file { get; construct; }
 
        public uint tracks_number {
-               get { return tracks.length; }
+               get {
+                       assert (parsed);
+
+                       return tracks.length;
+               }
        }
 
+       private bool parsed = false;
        private GdiTrackNode[] tracks;
 
-       public Gdi (File file) throws Error {
-               _file = file;
-
-               parse ();
+       public Gdi (File file) {
+               Object (file: file);
        }
 
        public GdiTrackNode get_track (uint i) throws Error {
+               assert (parsed);
+
                if (i >= tracks.length)
                        throw new GdiError.NOT_A_TRACK ("“%s” doesn’t have a track for index %u.", 
file.get_uri (), i);
 
@@ -71,11 +73,15 @@ public class Games.Gdi : Object {
                return tokens;
        }
 
-       private void parse () throws Error {
+       public void parse () throws Error {
+               assert (!parsed);
+
+               parsed = true;
+
                var tokens = tokenize ();
 
                size_t line = 1;
-               for (size_t i = 0 ; i < tokens.length ; line++)
+               for (size_t i = 0; i < tokens.length; line++)
                        // Each case must consume the line completely.
                        if (line == 1)
                                parse_track_count_line (ref tokens, ref i, line);
@@ -113,7 +119,7 @@ public class Games.Gdi : Object {
                if (track_number < 1 || track_number > 99)
                        throw new GdiError.INVALID_TRACK_NUMBER ("%s:%lu: Invalid track number %s, expected a 
number in the 1-99 range.", file.get_basename (), line, track_number_string);
 
-               return new GdiTrackNode (child_file, track_number);
+               return GdiTrackNode () { file = child_file, track_number = track_number };
        }
 
        private void skip_token (ref string[] tokens, ref size_t i, size_t line) throws GdiError {
@@ -123,8 +129,6 @@ public class Games.Gdi : Object {
                if (tokens[i] == NEW_LINE)
                        throw new GdiError.UNEXPECTED_EOL ("%s:%lu: Unexpected end of line, expected a 
token.", file.get_basename (), line);
 
-               warning ("Skipping token `%s`", tokens[i]);
-
                i++;
        }
 
@@ -135,12 +139,7 @@ public class Games.Gdi : Object {
                if (tokens[i] == NEW_LINE)
                        throw new GdiError.UNEXPECTED_EOL ("%s:%lu: Unexpected end of line, expected a 
token.", file.get_basename (), line);
 
-               warning ("Getting token `%s`", tokens[i]);
-
-               var token = tokens[i];
-               i++;
-
-               return token;
+               return tokens[i++];
        }
 
        private void is_end_of_line (ref string[] tokens, ref size_t i, size_t line) throws GdiError {
@@ -150,3 +149,17 @@ public class Games.Gdi : Object {
                i++;
        }
 }
+
+public struct Games.GdiTrackNode {
+       public File file;
+       public int track_number;
+}
+
+private errordomain Games.GdiError {
+       UNEXPECTED_TOKEN,
+       UNEXPECTED_EOL,
+       UNEXPECTED_EOF,
+       INVALID_TRACK_NUMBER,
+       INVALID_TRACK_MODE,
+       NOT_A_TRACK,
+}
diff --git a/plugins/dreamcast/src/meson.build b/plugins/dreamcast/src/meson.build
index f4b47ac1..90af8614 100644
--- a/plugins/dreamcast/src/meson.build
+++ b/plugins/dreamcast/src/meson.build
@@ -2,10 +2,7 @@ vala_sources = [
   'dreamcast-error.vala',
   'dreamcast-header.vala',
   'dreamcast-plugin.vala',
-
-  'gdi/gdi.vala',
-  'gdi/gdi-error.vala',
-  'gdi/gdi-track-node.vala',
+  'gdi.vala',
 ]
 
 c_args = [


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