[gnome-games/wip/exalm/n64: 31/31] nintendo-64: Handle controller paks



commit 587103cad923cebc058dfe00cc383cb7287e3a45
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Sun Feb 2 15:42:21 2020 +0500

    nintendo-64: Handle controller paks
    
    Store paks in the savestate and add a switcher menu, similarly to
    Nintendo DS.

 plugins/nintendo-64/data/meson.build               |   6 +
 plugins/nintendo-64/data/nintendo-64.gresource.xml |   7 ++
 .../nintendo-64/data/ui/nintendo-64-pak-player.ui  |  39 ++++++
 .../data/ui/nintendo-64-pak-switcher.ui            |  50 ++++++++
 plugins/nintendo-64/src/meson.build                |   8 +-
 .../nintendo-64/src/nintendo-64-pak-player.vala    |  43 +++++++
 .../nintendo-64/src/nintendo-64-pak-switcher.vala  |  81 +++++++++++++
 plugins/nintendo-64/src/nintendo-64-pak.vala       |  40 +++++++
 plugins/nintendo-64/src/nintendo-64-platform.vala  |  11 ++
 plugins/nintendo-64/src/nintendo-64-plugin.vala    |   4 +-
 plugins/nintendo-64/src/nintendo-64-runner.vala    | 131 +++++++++++++++++++++
 plugins/nintendo-64/src/nintendo-64-savestate.vala |  33 ++++++
 12 files changed, 450 insertions(+), 3 deletions(-)
---
diff --git a/plugins/nintendo-64/data/meson.build b/plugins/nintendo-64/data/meson.build
index 0d4b6c49..063fe570 100644
--- a/plugins/nintendo-64/data/meson.build
+++ b/plugins/nintendo-64/data/meson.build
@@ -1 +1,7 @@
 install_data (plugin_name + '.plugin', install_dir: plugins_dir)
+
+nintendo_64_resources = gnome.compile_resources (
+  'nintendo-64',
+  'nintendo-64.gresource.xml',
+  c_name: 'resources'
+)
diff --git a/plugins/nintendo-64/data/nintendo-64.gresource.xml 
b/plugins/nintendo-64/data/nintendo-64.gresource.xml
new file mode 100644
index 00000000..3bc5831d
--- /dev/null
+++ b/plugins/nintendo-64/data/nintendo-64.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/Games/plugins/nintendo-64">
+    <file>ui/nintendo-64-pak-player.ui</file>
+    <file>ui/nintendo-64-pak-switcher.ui</file>
+  </gresource>
+</gresources>
diff --git a/plugins/nintendo-64/data/ui/nintendo-64-pak-player.ui 
b/plugins/nintendo-64/data/ui/nintendo-64-pak-player.ui
new file mode 100644
index 00000000..c9bea10c
--- /dev/null
+++ b/plugins/nintendo-64/data/ui/nintendo-64-pak-player.ui
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <template class="GamesNintendo64PakPlayer" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkLabel" id="title">
+        <property name="visible" bind-source="GamesNintendo64PakPlayer" bind-property="show-title" 
bind-flags="sync-create|bidirectional"/>
+        <property name="xalign">0</property>
+        <property name="margin">5</property>
+        <attributes>
+          <attribute name="weight" value="bold"/>
+          <attribute name="scale" value="0.8"/>
+        </attributes>
+        <style>
+          <class name="dim-label"/>
+        </style>
+      </object>
+    </child>
+    <child>
+      <object class="GtkModelButton" id="memory_btn">
+        <property name="visible">True</property>
+        <property name="role">radio</property>
+        <property name="text" translatable="true">Controller Pak</property>
+        <signal name="clicked" handler="memory_btn_clicked"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkModelButton" id="rumble_btn">
+        <property name="visible">True</property>
+        <property name="role">radio</property>
+        <property name="text" translatable="true">Rumble Pak</property>
+        <property name="sensitive" bind-source="GamesNintendo64PakPlayer" bind-property="supports-rumble" 
bind-flags="sync-create"/>
+        <signal name="clicked" handler="rumble_btn_clicked"/>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/plugins/nintendo-64/data/ui/nintendo-64-pak-switcher.ui 
b/plugins/nintendo-64/data/ui/nintendo-64-pak-switcher.ui
new file mode 100644
index 00000000..53a48360
--- /dev/null
+++ b/plugins/nintendo-64/data/ui/nintendo-64-pak-switcher.ui
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <template class="GamesNintendo64PakSwitcher" parent="GtkBin">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkMenuButton" id="menu_button">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="popover">pak_popover</property>
+        <signal name="notify::active" handler="on_menu_state_changed"/>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="a11y-display-discs">
+            <property name="accessible-name" translatable="yes">Controller Expansion</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="icon-name">input-gaming-symbolic</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="icon-name">pan-down-symbolic</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkPopover" id="pak_popover">
+    <property name="visible">False</property>
+    <signal name="show" handler="update_ui"/>
+    <child>
+      <object class="GtkBox" id="controllers_box">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="margin">12</property>
+        <property name="spacing">6</property>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/nintendo-64/src/meson.build b/plugins/nintendo-64/src/meson.build
index 39b5bd24..d1c90409 100644
--- a/plugins/nintendo-64/src/meson.build
+++ b/plugins/nintendo-64/src/meson.build
@@ -1,5 +1,11 @@
 vala_sources = [
+  'nintendo-64-pak.vala',
+  'nintendo-64-pak-player.vala',
+  'nintendo-64-pak-switcher.vala',
+  'nintendo-64-platform.vala',
   'nintendo-64-plugin.vala',
+  'nintendo-64-runner.vala',
+  'nintendo-64-savestate.vala',
 ]
 
 c_args = [
@@ -8,7 +14,7 @@ c_args = [
 
 shared_module (
   'games-' + plugin_name + '-plugin',
-  vala_sources,
+  vala_sources + nintendo_64_resources,
   dependencies: gnome_games_dep,
   c_args: c_args,
   install: true,
diff --git a/plugins/nintendo-64/src/nintendo-64-pak-player.vala 
b/plugins/nintendo-64/src/nintendo-64-pak-player.vala
new file mode 100644
index 00000000..87ebd791
--- /dev/null
+++ b/plugins/nintendo-64/src/nintendo-64-pak-player.vala
@@ -0,0 +1,43 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/plugins/nintendo-64/ui/nintendo-64-pak-player.ui")]
+private class Games.Nintendo64PakPlayer : Gtk.Box {
+       [GtkChild]
+       private Gtk.Label title;
+       [GtkChild]
+       private Gtk.ModelButton memory_btn;
+       [GtkChild]
+       private Gtk.ModelButton rumble_btn;
+
+       public uint player { get; construct; }
+       public bool supports_rumble { get; construct; }
+       public bool show_title { get; set; default = true; }
+
+       private Nintendo64Pak _pak;
+       public Nintendo64Pak pak {
+               get { return _pak; }
+               set {
+                       _pak = value;
+                       memory_btn.active = (pak == Nintendo64Pak.MEMORY);
+                       rumble_btn.active = (pak == Nintendo64Pak.RUMBLE);
+               }
+       }
+
+       public Nintendo64PakPlayer (uint player, bool supports_rumble) {
+               Object (player: player, supports_rumble: supports_rumble);
+       }
+
+       construct {
+               title.label = _("Player %u").printf (player);
+       }
+
+       [GtkCallback]
+       public void memory_btn_clicked () {
+               pak = Nintendo64Pak.MEMORY;
+       }
+
+       [GtkCallback]
+       public void rumble_btn_clicked () {
+               pak = Nintendo64Pak.RUMBLE;
+       }
+}
diff --git a/plugins/nintendo-64/src/nintendo-64-pak-switcher.vala 
b/plugins/nintendo-64/src/nintendo-64-pak-switcher.vala
new file mode 100644
index 00000000..09230b8d
--- /dev/null
+++ b/plugins/nintendo-64/src/nintendo-64-pak-switcher.vala
@@ -0,0 +1,81 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/plugins/nintendo-64/ui/nintendo-64-pak-switcher.ui")]
+private class Games.Nintendo64PakSwitcher : Gtk.Bin, HeaderBarWidget {
+       [GtkChild]
+       private Gtk.MenuButton menu_button;
+       [GtkChild]
+       private Gtk.Box controllers_box;
+
+       public Nintendo64Runner runner { get; construct; }
+
+       public Nintendo64Pak pak1 { get; set; }
+       public Nintendo64Pak pak2 { get; set; }
+       public Nintendo64Pak pak3 { get; set; }
+       public Nintendo64Pak pak4 { get; set; }
+
+       private bool is_menu_open;
+       public bool block_autohide {
+               get { return is_menu_open; }
+       }
+
+       public override void constructed () {
+               update_ui ();
+
+               bind_property ("pak1", runner,
+                              "pak1", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+               bind_property ("pak2", runner,
+                              "pak2", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+               bind_property ("pak3", runner,
+                              "pak3", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+               bind_property ("pak4", runner,
+                              "pak4", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+
+               base.constructed ();
+       }
+
+       public Nintendo64PakSwitcher (Nintendo64Runner runner) {
+               Object (runner: runner);
+       }
+
+       [GtkCallback]
+       private void on_menu_state_changed () {
+               is_menu_open = menu_button.active;
+               notify_property ("block-autohide");
+       }
+
+       [GtkCallback]
+       private void update_ui () {
+               foreach (var row in controllers_box.get_children ())
+                       controllers_box.remove (row);
+
+               var core = runner.get_core ();
+               var iterator = core.iterate_controllers ();
+
+               Nintendo64PakPlayer first_widget = null;
+               uint total_players = 0;
+
+               uint port;
+               unowned Retro.Controller controller;
+               while (iterator.next (out port, out controller)) {
+                       if (total_players > 3)
+                               break;
+
+                       total_players++;
+
+                       bool supports_rumble = controller.get_supports_rumble ();
+                       var widget = new Nintendo64PakPlayer (total_players, supports_rumble);
+
+                       if (first_widget == null)
+                               first_widget = widget;
+
+                       bind_property (@"pak$total_players", widget, "pak",
+                           BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+
+                       controllers_box.add (widget);
+               }
+
+               if (total_players == 1)
+                       first_widget.show_title = false;
+       }
+}
diff --git a/plugins/nintendo-64/src/nintendo-64-pak.vala b/plugins/nintendo-64/src/nintendo-64-pak.vala
new file mode 100644
index 00000000..fa251341
--- /dev/null
+++ b/plugins/nintendo-64/src/nintendo-64-pak.vala
@@ -0,0 +1,40 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+public enum Games.Nintendo64Pak {
+       MEMORY,
+       RUMBLE,
+       NONE;
+
+       public string get_value () {
+               switch (this) {
+               case MEMORY:
+                       return "memory";
+
+               case RUMBLE:
+                       return "rumble";
+
+               case NONE:
+                       return "none";
+
+               default:
+                       assert_not_reached ();
+               }
+       }
+
+       public static Nintendo64Pak? from_value (string value) {
+               switch (value) {
+               case "memory":
+                       return MEMORY;
+
+               case "rumble":
+                       return RUMBLE;
+
+               case "none":
+                       return NONE;
+
+               default:
+                       warning ("Unknown screen layout: %s\n", value);
+                       return null;
+               }
+       }
+}
diff --git a/plugins/nintendo-64/src/nintendo-64-platform.vala 
b/plugins/nintendo-64/src/nintendo-64-platform.vala
new file mode 100644
index 00000000..64355fb1
--- /dev/null
+++ b/plugins/nintendo-64/src/nintendo-64-platform.vala
@@ -0,0 +1,11 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+public class Games.Nintendo64Platform : RetroPlatform {
+       public Nintendo64Platform (string id, string name, string[] mime_types, string prefix) {
+               base (id, name, mime_types, prefix);
+       }
+
+       public override Type get_savestate_type () {
+               return typeof (Nintendo64Savestate);
+       }
+}
diff --git a/plugins/nintendo-64/src/nintendo-64-plugin.vala b/plugins/nintendo-64/src/nintendo-64-plugin.vala
index a06551c7..5e286acb 100644
--- a/plugins/nintendo-64/src/nintendo-64-plugin.vala
+++ b/plugins/nintendo-64/src/nintendo-64-plugin.vala
@@ -9,7 +9,7 @@ private class Games.Nintendo64Plugin : Object, Plugin {
        private static RetroPlatform platform;
 
        static construct {
-               platform = new RetroPlatform (PLATFORM_ID, PLATFORM_NAME, { MIME_TYPE }, PLATFORM_UID_PREFIX);
+               platform = new Nintendo64Platform (PLATFORM_ID, PLATFORM_NAME, { MIME_TYPE }, 
PLATFORM_UID_PREFIX);
        }
 
        public Platform[] get_platforms () {
@@ -58,7 +58,7 @@ private class Games.Nintendo64Plugin : Object, Plugin {
                builder.uri = game.get_uri ();
                builder.uid = game.get_uid ();
                builder.title = game.name;
-               return builder.to_runner ();
+               return builder.to_runner (typeof (Nintendo64Runner));
        }
 }
 
diff --git a/plugins/nintendo-64/src/nintendo-64-runner.vala b/plugins/nintendo-64/src/nintendo-64-runner.vala
new file mode 100644
index 00000000..c20462f4
--- /dev/null
+++ b/plugins/nintendo-64/src/nintendo-64-runner.vala
@@ -0,0 +1,131 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.Nintendo64Runner : RetroRunner {
+       private const string MUPEN64PLUS_PAK_OPTION = "mupen64plus-pak%u";
+       private const string PARALLEL_N64_PAK_OPTION = "parallel-n64-pak%u";
+
+       private Nintendo64Pak pak[4];
+
+       public Nintendo64Pak pak1 {
+               get { return pak[0]; }
+               set {
+                       pak[0] = value;
+                       update_paks ();
+               }
+       }
+
+       public Nintendo64Pak pak2 {
+               get { return pak[1]; }
+               set {
+                       pak[1] = value;
+                       update_paks ();
+               }
+       }
+
+       public Nintendo64Pak pak3 {
+               get { return pak[2]; }
+               set {
+                       pak[2] = value;
+                       update_paks ();
+               }
+       }
+
+       public Nintendo64Pak pak4 {
+               get { return pak[3]; }
+               set {
+                       pak[3] = value;
+                       update_paks ();
+               }
+       }
+
+       private bool has_pak_options (string prefix) {
+               var core = get_core ();
+
+               for (int i = 1; i <= 4; i++)
+                       if (!core.has_option (prefix.printf (i)))
+                               return false;
+
+               return true;
+       }
+
+       private string? get_option_prefix () {
+               if (has_pak_options (MUPEN64PLUS_PAK_OPTION))
+                       return MUPEN64PLUS_PAK_OPTION;
+
+               if (has_pak_options (PARALLEL_N64_PAK_OPTION))
+                       return PARALLEL_N64_PAK_OPTION;
+
+               return null;
+       }
+
+       public override HeaderBarWidget? get_extra_widget () {
+               if (get_option_prefix () == null)
+                       return null;
+
+               return new Nintendo64PakSwitcher (this);
+       }
+
+       private void update_paks () {
+               var prefix = get_option_prefix ();
+
+               if (prefix == null)
+                       return;
+
+               var core = get_core ();
+               for (int i = 0; i < 4; i++) {
+                       var option = core.get_option (prefix.printf (i + 1));
+
+                       try {
+                               option.set_value (pak[i].get_value ());
+                       }
+                       catch (Error e) {
+                               critical ("Couldn't set pak %u to %s: %s", i + 1, pak[i].get_value (), 
e.message);
+                       }
+               }
+       }
+
+       protected override void save_savestate_metadata (Savestate savestate) throws Error {
+               assert (savestate is Nintendo64Savestate);
+
+               var platform_savestate = savestate as Nintendo64Savestate;
+               platform_savestate.pak1 = pak1;
+               platform_savestate.pak2 = pak2;
+               platform_savestate.pak3 = pak3;
+               platform_savestate.pak4 = pak4;
+
+               base.save_savestate_metadata (savestate);
+       }
+
+       protected override void load_savestate_metadata (Savestate savestate) throws Error {
+               base.load_savestate_metadata (savestate);
+
+               assert (savestate is Nintendo64Savestate);
+
+               var platform_savestate = savestate as Nintendo64Savestate;
+               pak1 = platform_savestate.pak1;
+               pak2 = platform_savestate.pak2;
+               pak3 = platform_savestate.pak3;
+               pak4 = platform_savestate.pak4;
+       }
+
+       protected override void reset_metadata (Savestate? last_savestate) throws Error {
+               base.reset_metadata (last_savestate);
+
+               if (last_savestate == null) {
+                       pak1 = Nintendo64Pak.MEMORY;
+                       pak2 = Nintendo64Pak.MEMORY;
+                       pak3 = Nintendo64Pak.MEMORY;
+                       pak4 = Nintendo64Pak.MEMORY;
+
+                       return;
+               }
+
+               assert (last_savestate is Nintendo64Savestate);
+
+               var platform_savestate = last_savestate as Nintendo64Savestate;
+               pak1 = platform_savestate.pak1;
+               pak2 = platform_savestate.pak2;
+               pak3 = platform_savestate.pak3;
+               pak4 = platform_savestate.pak4;
+       }
+}
diff --git a/plugins/nintendo-64/src/nintendo-64-savestate.vala 
b/plugins/nintendo-64/src/nintendo-64-savestate.vala
new file mode 100644
index 00000000..b0ab21a9
--- /dev/null
+++ b/plugins/nintendo-64/src/nintendo-64-savestate.vala
@@ -0,0 +1,33 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+public class Games.Nintendo64Savestate : Savestate {
+       public Nintendo64Pak pak1 { get; set; }
+       public Nintendo64Pak pak2 { get; set; }
+       public Nintendo64Pak pak3 { get; set; }
+       public Nintendo64Pak pak4 { get; set; }
+
+       protected override void load_metadata (KeyFile keyfile) throws KeyFileError {
+               base.load_metadata (keyfile);
+
+               var value = keyfile.get_string ("Nintendo 64", "Pak 1");
+               pak1 = Nintendo64Pak.from_value (value);
+
+               value = keyfile.get_string ("Nintendo 64", "Pak 2");
+               pak2 = Nintendo64Pak.from_value (value);
+
+               value = keyfile.get_string ("Nintendo 64", "Pak 3");
+               pak3 = Nintendo64Pak.from_value (value);
+
+               value = keyfile.get_string ("Nintendo 64", "Pak 4");
+               pak4 = Nintendo64Pak.from_value (value);
+       }
+
+       protected override void save_metadata (KeyFile keyfile) {
+               base.save_metadata (keyfile);
+
+               keyfile.set_string ("Nintendo 64", "Pak 1", pak1.get_value ());
+               keyfile.set_string ("Nintendo 64", "Pak 2", pak2.get_value ());
+               keyfile.set_string ("Nintendo 64", "Pak 3", pak3.get_value ());
+               keyfile.set_string ("Nintendo 64", "Pak 4", pak4.get_value ());
+       }
+}


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