shithub: m8c

Download patch

ref: 44ab66d603d452ade672335f95c9c593929ab7cd
parent: 2317dec1d839ba7bd92f4072f180719e7ab39934
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Sun Dec 5 17:20:38 EST 2021

Feature/config file writing (#35)


--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -56,7 +56,7 @@
           cp /mingw64/bin/SDL2.dll .
           cp /mingw64/bin/libserialport-0.dll .
         fi
-        unix2dos README.md config.ini LICENSE AUDIOGUIDE.md
+        unix2dos README.md config.ini.sample LICENSE AUDIOGUIDE.md
     - name: 'Upload artifact (win32)'
       if: matrix.win == 'win32'
       uses: actions/upload-artifact@v2
@@ -72,7 +72,7 @@
           LICENSE
           README.md
           AUDIOGUIDE.md
-          config.ini
+          config.ini.sample
     - name: 'Upload artifact (win64)'
       if: matrix.win == 'win64'
       uses: actions/upload-artifact@v2
@@ -86,7 +86,7 @@
           LICENSE
           README.md
           AUDIOGUIDE.md
-          config.ini
+          config.ini.sample
 
   build-linux:
     runs-on: ubuntu-latest
@@ -115,4 +115,4 @@
             README.md
             m8c
             gamecontrollerdb.txt
-            config.ini
+            config.ini.sample
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@
 
 ### Start the program
 
-Connect the Teensy to your computer and start the program. It should automatically detect your device.
+Connect the M8 or Teensy (with headless firmware) to your computer and start the program. It should automatically detect your device.
 
 ```
 ./m8c
@@ -95,9 +95,14 @@
 
 ### Config
 
-The config function is currently a prototype, the config file must be named `config.ini` and must be present in the directory from which you're running the command.
+Keyboard and game controller bindings can be configured via `config.ini`.
 
-See the `config.ini` file to see the available options.
+If not found, the file will be created in one of these locations:
+Windows: C:\Users\<username>\AppData\Roaming\m8c\config.ini
+Linux: /home/<username>/.local/share/m8c/config.ini
+MacOS: /Users/<username>/Library/Application Support/m8c/config.ini
+
+See the `config.ini.sample` file to see the available options.
 
 -----------
 
--- a/config.c
+++ b/config.c
@@ -1,57 +1,136 @@
 // Copyright 2021 Jonne Kokkonen
 // Released under the MIT licence, https://opensource.org/licenses/MIT
 
-#include <SDL2/SDL.h>
 #include "config.h"
+#include <SDL2/SDL.h>
 
 config_params_s init_config() {
   config_params_s c;
 
-  c.filename        = "config.ini"; // default config file to load
-  c.init_fullscreen = 0;            // default fullscreen state at load
+  c.filename = "config.ini"; // default config file to load
 
-  c.key_up          = SDL_SCANCODE_UP;
-  c.key_left        = SDL_SCANCODE_LEFT;
-  c.key_down        = SDL_SCANCODE_DOWN;
-  c.key_right       = SDL_SCANCODE_RIGHT;
-  c.key_select      = SDL_SCANCODE_LSHIFT;
-  c.key_select_alt  = SDL_SCANCODE_A;
-  c.key_start       = SDL_SCANCODE_SPACE;
-  c.key_start_alt   = SDL_SCANCODE_S;
-  c.key_opt         = SDL_SCANCODE_LALT;
-  c.key_opt_alt     = SDL_SCANCODE_Z;
-  c.key_edit        = SDL_SCANCODE_LCTRL;
-  c.key_edit_alt    = SDL_SCANCODE_X;
-  c.key_delete      = SDL_SCANCODE_DELETE;
-  c.key_reset       = SDL_SCANCODE_R;
+  c.init_fullscreen = 0; // default fullscreen state at load
 
-  c.gamepad_up                    = SDL_CONTROLLER_BUTTON_DPAD_UP;
-  c.gamepad_left                  = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
-  c.gamepad_down                  = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
-  c.gamepad_right                 = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
-  c.gamepad_select                = SDL_CONTROLLER_BUTTON_BACK;
-  c.gamepad_start                 = SDL_CONTROLLER_BUTTON_START;
-  c.gamepad_opt                   = SDL_CONTROLLER_BUTTON_B;
-  c.gamepad_edit                  = SDL_CONTROLLER_BUTTON_A;
+  c.key_up = SDL_SCANCODE_UP;
+  c.key_left = SDL_SCANCODE_LEFT;
+  c.key_down = SDL_SCANCODE_DOWN;
+  c.key_right = SDL_SCANCODE_RIGHT;
+  c.key_select = SDL_SCANCODE_LSHIFT;
+  c.key_select_alt = SDL_SCANCODE_A;
+  c.key_start = SDL_SCANCODE_SPACE;
+  c.key_start_alt = SDL_SCANCODE_S;
+  c.key_opt = SDL_SCANCODE_LALT;
+  c.key_opt_alt = SDL_SCANCODE_Z;
+  c.key_edit = SDL_SCANCODE_LCTRL;
+  c.key_edit_alt = SDL_SCANCODE_X;
+  c.key_delete = SDL_SCANCODE_DELETE;
+  c.key_reset = SDL_SCANCODE_R;
+
+  c.gamepad_up = SDL_CONTROLLER_BUTTON_DPAD_UP;
+  c.gamepad_left = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
+  c.gamepad_down = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
+  c.gamepad_right = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
+  c.gamepad_select = SDL_CONTROLLER_BUTTON_BACK;
+  c.gamepad_start = SDL_CONTROLLER_BUTTON_START;
+  c.gamepad_opt = SDL_CONTROLLER_BUTTON_B;
+  c.gamepad_edit = SDL_CONTROLLER_BUTTON_A;
 
-  c.gamepad_analog_threshold      = 32767;
-  c.gamepad_analog_invert         = 0;
-  c.gamepad_analog_axis_updown    = SDL_CONTROLLER_AXIS_LEFTY;
+  c.gamepad_analog_threshold = 32766;
+  c.gamepad_analog_invert = 0;
+  c.gamepad_analog_axis_updown = SDL_CONTROLLER_AXIS_LEFTY;
   c.gamepad_analog_axis_leftright = SDL_CONTROLLER_AXIS_LEFTX;
-  c.gamepad_analog_axis_start     = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
-  c.gamepad_analog_axis_select    = SDL_CONTROLLER_AXIS_TRIGGERLEFT;
-  c.gamepad_analog_axis_opt       = SDL_CONTROLLER_AXIS_INVALID;
-  c.gamepad_analog_axis_edit      = SDL_CONTROLLER_AXIS_INVALID;
+  c.gamepad_analog_axis_start = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
+  c.gamepad_analog_axis_select = SDL_CONTROLLER_AXIS_TRIGGERLEFT;
+  c.gamepad_analog_axis_opt = SDL_CONTROLLER_AXIS_INVALID;
+  c.gamepad_analog_axis_edit = SDL_CONTROLLER_AXIS_INVALID;
 
-
   return c;
 }
 
-// Read config 
+// Write config to file
+void write_config(config_params_s *conf) {
+
+  // Open the default config file for writing
+  char config_path[1024] = {0};
+  sprintf(config_path, "%s%s", SDL_GetPrefPath("", "m8c"), conf->filename);
+  SDL_RWops *rw = SDL_RWFromFile(config_path, "w");
+
+  SDL_Log("Writing config file to %s", config_path);
+
+  // Entries for the config file
+  char ini_values[34][50];
+  sprintf(ini_values[0], "[graphics]\n");
+  sprintf(ini_values[1], "fullscreen=%s\n",
+          conf->init_fullscreen ? "true" : "false");
+  sprintf(ini_values[2], "[keyboard]\n");
+  sprintf(ini_values[3], "key_up=%d\n", conf->key_up);
+  sprintf(ini_values[4], "key_left=%d\n", conf->key_left);
+  sprintf(ini_values[5], "key_down=%d\n", conf->key_down);
+  sprintf(ini_values[6], "key_right=%d\n", conf->key_right);
+  sprintf(ini_values[7], "key_select=%d\n", conf->key_select);
+  sprintf(ini_values[8], "key_select_alt=%d\n", conf->key_select_alt);
+  sprintf(ini_values[9], "key_start=%d\n", conf->key_start);
+  sprintf(ini_values[10], "key_start_alt=%d\n", conf->key_start_alt);
+  sprintf(ini_values[11], "key_opt=%d\n", conf->key_opt);
+  sprintf(ini_values[12], "key_opt_alt=%d\n", conf->key_opt_alt);
+  sprintf(ini_values[13], "key_edit=%d\n", conf->key_edit);
+  sprintf(ini_values[14], "key_edit_alt=%d\n", conf->key_edit_alt);
+  sprintf(ini_values[15], "key_delete=%d\n", conf->key_delete);
+  sprintf(ini_values[16], "key_reset=%d\n", conf->key_reset);
+  sprintf(ini_values[17], "[gamepad]\n");
+  sprintf(ini_values[18], "gamepad_up=%d\n", conf->gamepad_up);
+  sprintf(ini_values[19], "gamepad_left=%d\n", conf->gamepad_left);
+  sprintf(ini_values[20], "gamepad_down=%d\n", conf->gamepad_down);
+  sprintf(ini_values[21], "gamepad_right=%d\n", conf->gamepad_right);
+  sprintf(ini_values[22], "gamepad_select=%d\n", conf->gamepad_select);
+  sprintf(ini_values[23], "gamepad_start=%d\n", conf->gamepad_start);
+  sprintf(ini_values[24], "gamepad_opt=%d\n", conf->gamepad_opt);
+  sprintf(ini_values[25], "gamepad_edit=%d\n", conf->gamepad_edit);
+  sprintf(ini_values[26], "gamepad_analog_threshold=%d\n",
+          conf->gamepad_analog_threshold);
+  sprintf(ini_values[27], "gamepad_analog_invert=%s\n",
+          conf->gamepad_analog_invert ? "true" : "false");
+  sprintf(ini_values[28], "gamepad_analog_axis_updown=%d\n",
+          conf->gamepad_analog_axis_updown);
+  sprintf(ini_values[29], "gamepad_analog_axis_leftright=%d\n",
+          conf->gamepad_analog_axis_leftright);
+  sprintf(ini_values[30], "gamepad_analog_axis_select=%d\n",
+          conf->gamepad_analog_axis_select);
+  sprintf(ini_values[31], "gamepad_analog_axis_start=%d\n",
+          conf->gamepad_analog_axis_start);
+  sprintf(ini_values[32], "gamepad_analog_axis_opt=%d\n",
+          conf->gamepad_analog_axis_opt);
+  sprintf(ini_values[33], "gamepad_analog_axis_edit=%d\n",
+          conf->gamepad_analog_axis_edit);
+
+  if (rw != NULL) {
+    // Write ini_values array to config file
+    for (int i = 0; i < 34; i++) {
+      size_t len = SDL_strlen(ini_values[i]);
+      if (SDL_RWwrite(rw, ini_values[i], 1, len) != len) {
+        SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM,
+                     "Couldn't write line into config file.");
+      } else {
+        SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Wrote to config: %s",
+                     ini_values[i]);
+      }
+    }
+    SDL_RWclose(rw);
+  } else {
+    SDL_Log("Couldn't write into config file.");
+  }
+}
+
+// Read config
 void read_config(config_params_s *conf) {
-  // Load the config and read the fullscreen setting from the graphics section
-  ini_t *ini = ini_load(conf->filename);
+
+  char config_path[1024] = {0};
+  sprintf(config_path, "%s%s", SDL_GetPrefPath("", "m8c"), conf->filename);
+  SDL_Log("Reading config %s", config_path);
+  ini_t *ini = ini_load(config_path);
   if (ini == NULL) {
+    SDL_Log("Could not load config.");
+    write_config(conf);
     return;
   }
 
@@ -65,87 +144,119 @@
 
 void read_graphics_config(ini_t *ini, config_params_s *conf) {
   const char *param = ini_get(ini, "graphics", "fullscreen");
-  // This obviously requires the parameter to be a lowercase true to enable fullscreen
-  if ( strcmp(param, "true") == 0 ) {
+  // This obviously requires the parameter to be a lowercase true to enable
+  // fullscreen
+  if (strcmp(param, "true") == 0) {
     conf->init_fullscreen = 1;
-  }
-  else conf->init_fullscreen = 0;
+  } else
+    conf->init_fullscreen = 0;
 }
 
 void read_key_config(ini_t *ini, config_params_s *conf) {
   // TODO: Some form of validation
 
-  const char *key_up          = ini_get(ini, "keyboard", "key_up");
-  const char *key_left        = ini_get(ini, "keyboard", "key_left");
-  const char *key_down        = ini_get(ini, "keyboard", "key_down");
-  const char *key_right       = ini_get(ini, "keyboard", "key_right");
-  const char *key_select      = ini_get(ini, "keyboard", "key_select");
-  const char *key_select_alt  = ini_get(ini, "keyboard", "key_select_alt");
-  const char *key_start       = ini_get(ini, "keyboard", "key_start");
-  const char *key_start_alt   = ini_get(ini, "keyboard", "key_start_alt");
-  const char *key_opt         = ini_get(ini, "keyboard", "key_opt");
-  const char *key_opt_alt     = ini_get(ini, "keyboard", "key_opt_alt");
-  const char *key_edit        = ini_get(ini, "keyboard", "key_edit");
-  const char *key_edit_alt    = ini_get(ini, "keyboard", "key_edit_alt");
-  const char *key_delete      = ini_get(ini, "keyboard", "key_delete");
-  const char *key_reset       = ini_get(ini, "keyboard", "key_reset");
+  const char *key_up = ini_get(ini, "keyboard", "key_up");
+  const char *key_left = ini_get(ini, "keyboard", "key_left");
+  const char *key_down = ini_get(ini, "keyboard", "key_down");
+  const char *key_right = ini_get(ini, "keyboard", "key_right");
+  const char *key_select = ini_get(ini, "keyboard", "key_select");
+  const char *key_select_alt = ini_get(ini, "keyboard", "key_select_alt");
+  const char *key_start = ini_get(ini, "keyboard", "key_start");
+  const char *key_start_alt = ini_get(ini, "keyboard", "key_start_alt");
+  const char *key_opt = ini_get(ini, "keyboard", "key_opt");
+  const char *key_opt_alt = ini_get(ini, "keyboard", "key_opt_alt");
+  const char *key_edit = ini_get(ini, "keyboard", "key_edit");
+  const char *key_edit_alt = ini_get(ini, "keyboard", "key_edit_alt");
+  const char *key_delete = ini_get(ini, "keyboard", "key_delete");
+  const char *key_reset = ini_get(ini, "keyboard", "key_reset");
 
-  conf->key_up          = atoi(key_up);
-  conf->key_left        = atoi(key_left);
-  conf->key_down        = atoi(key_down);
-  conf->key_right       = atoi(key_right);
-  conf->key_select      = atoi(key_select);
-  conf->key_select_alt  = atoi(key_select_alt);
-  conf->key_start       = atoi(key_start);
-  conf->key_start_alt   = atoi(key_start_alt);
-  conf->key_opt         = atoi(key_opt);
-  conf->key_opt_alt     = atoi(key_opt_alt);
-  conf->key_edit        = atoi(key_edit);
-  conf->key_edit_alt    = atoi(key_edit_alt);
-  conf->key_delete      = atoi(key_delete);
-  conf->key_reset       = atoi(key_reset);
+  if (key_up)
+    conf->key_up = SDL_atoi(key_up);
+  if (key_left)
+    conf->key_left = SDL_atoi(key_left);
+  if (key_down)
+    conf->key_down = SDL_atoi(key_down);
+  if (key_right)
+    conf->key_right = SDL_atoi(key_right);
+  if (key_select)
+    conf->key_select = SDL_atoi(key_select);
+  if (key_select_alt)
+    conf->key_select_alt = SDL_atoi(key_select_alt);
+  if (key_start)
+    conf->key_start = SDL_atoi(key_start);
+  if (key_start_alt)
+    conf->key_start_alt = SDL_atoi(key_start_alt);
+  if (key_opt)
+    conf->key_opt = SDL_atoi(key_opt);
+  if (key_opt_alt)
+    conf->key_opt_alt = SDL_atoi(key_opt_alt);
+  if (key_edit)
+    conf->key_edit = SDL_atoi(key_edit);
+  if (key_edit_alt)
+    conf->key_edit_alt = SDL_atoi(key_edit_alt);
+  if (key_delete)
+    conf->key_delete = SDL_atoi(key_delete);
+  if (key_reset)
+    conf->key_reset = SDL_atoi(key_reset);
 }
 
 void read_gamepad_config(ini_t *ini, config_params_s *conf) {
   // TODO: Some form of validation
 
-  const char *gamepad_up                    = ini_get(ini, "gamepad", "gamepad_up");
-  const char *gamepad_left                  = ini_get(ini, "gamepad", "gamepad_left");
-  const char *gamepad_down                  = ini_get(ini, "gamepad", "gamepad_down");
-  const char *gamepad_right                 = ini_get(ini, "gamepad", "gamepad_right");
-  const char *gamepad_select                = ini_get(ini, "gamepad", "gamepad_select");
-  const char *gamepad_start                 = ini_get(ini, "gamepad", "gamepad_start");
-  const char *gamepad_opt                   = ini_get(ini, "gamepad", "gamepad_opt");
-  const char *gamepad_edit                  = ini_get(ini, "gamepad", "gamepad_edit");
-  const char *gamepad_analog_threshold      = ini_get(ini, "gamepad", "gamepad_analog_threshold");
-  const char *gamepad_analog_invert         = ini_get(ini, "gamepad", "gamepad_analog_invert");
-  const char *gamepad_analog_axis_updown    = ini_get(ini, "gamepad", "gamepad_analog_axis_updown");
-  const char *gamepad_analog_axis_leftright = ini_get(ini, "gamepad", "gamepad_analog_axis_leftright");
-  const char *gamepad_analog_axis_select    = ini_get(ini, "gamepad", "gamepad_analog_axis_select");
-  const char *gamepad_analog_axis_start     = ini_get(ini, "gamepad", "gamepad_analog_axis_start");
-  const char *gamepad_analog_axis_opt       = ini_get(ini, "gamepad", "gamepad_analog_axis_opt");
-  const char *gamepad_analog_axis_edit      = ini_get(ini, "gamepad", "gamepad_analog_axis_edit");
+  const char *gamepad_up = ini_get(ini, "gamepad", "gamepad_up");
+  const char *gamepad_left = ini_get(ini, "gamepad", "gamepad_left");
+  const char *gamepad_down = ini_get(ini, "gamepad", "gamepad_down");
+  const char *gamepad_right = ini_get(ini, "gamepad", "gamepad_right");
+  const char *gamepad_select = ini_get(ini, "gamepad", "gamepad_select");
+  const char *gamepad_start = ini_get(ini, "gamepad", "gamepad_start");
+  const char *gamepad_opt = ini_get(ini, "gamepad", "gamepad_opt");
+  const char *gamepad_edit = ini_get(ini, "gamepad", "gamepad_edit");
+  const char *gamepad_analog_threshold =
+      ini_get(ini, "gamepad", "gamepad_analog_threshold");
+  const char *gamepad_analog_invert =
+      ini_get(ini, "gamepad", "gamepad_analog_invert");
+  const char *gamepad_analog_axis_updown =
+      ini_get(ini, "gamepad", "gamepad_analog_axis_updown");
+  const char *gamepad_analog_axis_leftright =
+      ini_get(ini, "gamepad", "gamepad_analog_axis_leftright");
+  const char *gamepad_analog_axis_select =
+      ini_get(ini, "gamepad", "gamepad_analog_axis_select");
+  const char *gamepad_analog_axis_start =
+      ini_get(ini, "gamepad", "gamepad_analog_axis_start");
+  const char *gamepad_analog_axis_opt =
+      ini_get(ini, "gamepad", "gamepad_analog_axis_opt");
+  const char *gamepad_analog_axis_edit =
+      ini_get(ini, "gamepad", "gamepad_analog_axis_edit");
 
-  conf->gamepad_up                    = atoi(gamepad_up);
-  conf->gamepad_left                  = atoi(gamepad_left);
-  conf->gamepad_down                  = atoi(gamepad_down);
-  conf->gamepad_right                 = atoi(gamepad_right);
-  conf->gamepad_select                = atoi(gamepad_select);
-  conf->gamepad_start                 = atoi(gamepad_start);
-  conf->gamepad_opt                   = atoi(gamepad_opt);
-  conf->gamepad_edit                  = atoi(gamepad_edit);
-  conf->gamepad_analog_threshold      = atoi(gamepad_analog_threshold);
+  if (gamepad_up)
+    conf->gamepad_up = SDL_atoi(gamepad_up);
+  if (gamepad_left)
+    conf->gamepad_left = SDL_atoi(gamepad_left);
+  if (gamepad_down)
+    conf->gamepad_down = SDL_atoi(gamepad_down);
+  if (gamepad_right)
+    conf->gamepad_right = SDL_atoi(gamepad_right);
+  if (gamepad_select)
+    conf->gamepad_select = SDL_atoi(gamepad_select);
+  if (gamepad_start)
+    conf->gamepad_start = SDL_atoi(gamepad_start);
+  if (gamepad_opt)
+    conf->gamepad_opt = SDL_atoi(gamepad_opt);
+  if (gamepad_edit)
+    conf->gamepad_edit = SDL_atoi(gamepad_edit);
+  if (gamepad_analog_threshold)
+    conf->gamepad_analog_threshold = SDL_atoi(gamepad_analog_threshold);
 
-  // This obviously requires the parameter to be a lowercase true to enable fullscreen
-  if ( strcmp(gamepad_analog_invert, "true") == 0 ) {
+  // This requires the parameter to be a lowercase true to enable fullscreen
+  if (strcmp(gamepad_analog_invert, "true") == 0)
     conf->gamepad_analog_invert = 1;
-  }
-  else conf->gamepad_analog_invert = 0;
+  else
+    conf->gamepad_analog_invert = 0;
 
-  conf->gamepad_analog_axis_updown    = atoi(gamepad_analog_axis_updown);
-  conf->gamepad_analog_axis_leftright = atoi(gamepad_analog_axis_leftright);
-  conf->gamepad_analog_axis_select    = atoi(gamepad_analog_axis_select);
-  conf->gamepad_analog_axis_start     = atoi(gamepad_analog_axis_start);
-  conf->gamepad_analog_axis_opt       = atoi(gamepad_analog_axis_opt);
-  conf->gamepad_analog_axis_edit      = atoi(gamepad_analog_axis_edit);  
+  if (gamepad_analog_axis_updown) conf->gamepad_analog_axis_updown = SDL_atoi(gamepad_analog_axis_updown);
+  if (gamepad_analog_axis_leftright) conf->gamepad_analog_axis_leftright = SDL_atoi(gamepad_analog_axis_leftright);
+  if (gamepad_analog_axis_select) conf->gamepad_analog_axis_select = SDL_atoi(gamepad_analog_axis_select);
+  if (gamepad_analog_axis_start) conf->gamepad_analog_axis_start = SDL_atoi(gamepad_analog_axis_start);
+  if (gamepad_analog_axis_opt) conf->gamepad_analog_axis_opt = SDL_atoi(gamepad_analog_axis_opt);
+  if (gamepad_analog_axis_edit) conf->gamepad_analog_axis_edit = SDL_atoi(gamepad_analog_axis_edit);
 }
\ No newline at end of file
--- a/config.ini
+++ /dev/null
@@ -1,45 +1,0 @@
-[graphics]
-; set this to true to have m8c start fullscreen
-fullscreen=false
-
-[keyboard]
-; these need to be the decimal value of the SDL scancodes.
-; a table exists here: https://github.com/libsdl-org/sdlwiki/blob/main/SDLScancodeLookup.mediawiki
-key_up=82
-key_left=80
-key_down=81
-key_right=79
-key_select=225
-key_select_alt=4
-key_start=44
-key_start_alt=22
-key_opt=226
-key_opt_alt=29
-key_edit=224
-key_edit_alt=27
-key_delete=76
-key_reset=21
-
-[gamepad]
-; these need to be the decimal value of the SDL Controller buttons.
-; a table exists here: https://wiki.libsdl.org/SDL_GameControllerButton
-gamepad_up=11
-gamepad_left=13
-gamepad_down=12
-gamepad_right=14
-gamepad_select=4
-gamepad_start=6
-gamepad_opt=1
-gamepad_edit=0
-
-gamepad_analog_threshold=32766 ;the threshold for analog sticks to trigger cursor movement (working values: 1-32766)
-gamepad_analog_invert=false ;NOT IMPLEMENTED YET: invert up/down and left/right axis (true/false)
-
-; these need to be the decimal value of the controller axis
-; you can use -1 if you do not wish to map the function to an analog axis
-gamepad_analog_axis_updown=1
-gamepad_analog_axis_leftright=0
-gamepad_analog_axis_start=5
-gamepad_analog_axis_select=4
-gamepad_analog_axis_opt=-1
-gamepad_analog_axis_edit=-1
--- /dev/null
+++ b/config.ini.sample
@@ -1,0 +1,45 @@
+[graphics]
+; set this to true to have m8c start fullscreen
+fullscreen=false
+
+[keyboard]
+; these need to be the decimal value of the SDL scancodes.
+; a table exists here: https://github.com/libsdl-org/sdlwiki/blob/main/SDLScancodeLookup.mediawiki
+key_up=82
+key_left=80
+key_down=81
+key_right=79
+key_select=225
+key_select_alt=4
+key_start=44
+key_start_alt=22
+key_opt=226
+key_opt_alt=29
+key_edit=224
+key_edit_alt=27
+key_delete=76
+key_reset=21
+
+[gamepad]
+; these need to be the decimal value of the SDL Controller buttons.
+; a table exists here: https://wiki.libsdl.org/SDL_GameControllerButton
+gamepad_up=11
+gamepad_left=13
+gamepad_down=12
+gamepad_right=14
+gamepad_select=4
+gamepad_start=6
+gamepad_opt=1
+gamepad_edit=0
+
+gamepad_analog_threshold=32766 ;the threshold for analog sticks to trigger cursor movement (working values: 1-32766)
+gamepad_analog_invert=false ;NOT IMPLEMENTED YET: invert up/down and left/right axis (true/false)
+
+; these need to be the decimal value of the controller axis
+; you can use -1 if you do not wish to map the function to an analog axis
+gamepad_analog_axis_updown=1
+gamepad_analog_axis_leftright=0
+gamepad_analog_axis_start=5
+gamepad_analog_axis_select=4
+gamepad_analog_axis_opt=-1
+gamepad_analog_axis_edit=-1
--- a/input.c
+++ b/input.c
@@ -46,7 +46,8 @@
 
   SDL_Log("Looking for game controllers\n");
   SDL_Delay(
-      1); // Some controllers like XBone wired need a little while to get ready
+      10); // Some controllers like XBone wired need a little while to get ready
+
   // Open all available game controllers
   for (int i = 0; i < num_joysticks; i++) {
     if (!SDL_IsGameController(i))
@@ -59,8 +60,23 @@
     controller_index++;
   }
 
-  // Read controller mapping database
-  SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
+  // Try to load the game controller database file
+  char db_filename[1024] = {0};
+  sprintf(db_filename, "%sgamecontrollerdb.txt", SDL_GetPrefPath("", "m8c"));
+  SDL_Log("Trying to open game controller database from %s", db_filename);
+  SDL_RWops *db_rw = SDL_RWFromFile(db_filename, "rb");
+
+  if (db_rw != NULL) {
+    int mappings = SDL_GameControllerAddMappingsFromRW(db_rw, 1);
+    if (mappings != -1)
+      SDL_Log("Found %d game controller mappings", mappings);
+    else
+      SDL_LogError(SDL_LOG_CATEGORY_INPUT,
+                   "Error loading game controller mappings.");
+  } else {
+    SDL_LogError(SDL_LOG_CATEGORY_INPUT,
+                 "Unable to open game controller database file.");
+  }
 
   return controller_index;
 }
--- a/render.c
+++ b/render.c
@@ -55,8 +55,10 @@
   inrenderer(rend);
   prepare_inline_font();
 
+  SDL_LogSetAllPriority(SDL_LOG_PRIORITY_INFO);
+
   // Uncomment this for debug level logging
-  //SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
+  // SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
 
   return 1;
 }
--