From 7af658f0897c6d6ad1f67b9d7a9bc60955c029a0 Mon Sep 17 00:00:00 2001
From: Nadia Holmquist Pedersen <nadia@nhp.sh>
Date: Wed, 4 Dec 2019 22:46:33 +0100
Subject: Add a UNIX_PORTABLE build option, turning it off makes a build of
 melonDS suitable for systemwide installation.

---
 src/NDSCart.cpp              |  2 +-
 src/Platform.h               |  8 +++++-
 src/libui_sdl/CMakeLists.txt |  8 ++++++
 src/libui_sdl/Platform.cpp   | 64 ++++++++++++++++++++++++++++++++++++++++++++
 src/libui_sdl/main.cpp       | 32 +++++++++++++++++-----
 5 files changed, 106 insertions(+), 8 deletions(-)

(limited to 'src')

diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index 0ecd304..5654a7d 100644
--- a/src/NDSCart.cpp
+++ b/src/NDSCart.cpp
@@ -815,7 +815,7 @@ bool ReadROMParams(u32 gamecode, u32* params)
     // [gamecode] [ROM size] [save type] [reserved]
     // list must be sorted by gamecode
 
-    FILE* f = Platform::OpenLocalFile("romlist.bin", "rb");
+    FILE* f = Platform::OpenDataFile("romlist.bin");
     if (!f) return false;
 
     fseek(f, 0, SEEK_END);
diff --git a/src/Platform.h b/src/Platform.h
index ca6971e..dfe83d0 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -32,15 +32,21 @@ void StopEmu();
 //     can be optionally restricted to only opening a file that already exists.
 // * OpenLocalFile():
 //     opens files local to the emulator (melonDS.ini, BIOS, firmware, ...)
-//     checks, by order of priority:
+//     For Windows builds, or portable UNIX builds it checks, by order of priority:
 //     * current working directory
 //     * emulator directory (essentially where the melonDS executable is) if supported
 //     * any platform-specific application data directories
 //     in create mode, if the file doesn't exist, it will be created in the emulator
 //     directory if supported, or in the current directory otherwise
+//     For regular UNIX builds, the user's configuration directory is always used.
+// * OpenDataFile():
+//     Opens a file that was installed alongside melonDS on UNIX systems in /usr/share, etc.
+//     Looks in the user's data directory first, then the system's.
+//     If on Windows or a portable UNIX build, this simply calls OpenLocalFile().
 
 FILE* OpenFile(const char* path, const char* mode, bool mustexist=false);
 FILE* OpenLocalFile(const char* path, const char* mode);
+FILE* OpenDataFile(const char* path);
 
 inline bool FileExists(const char* name)
 {
diff --git a/src/libui_sdl/CMakeLists.txt b/src/libui_sdl/CMakeLists.txt
index 64206bf..afd38e4 100644
--- a/src/libui_sdl/CMakeLists.txt
+++ b/src/libui_sdl/CMakeLists.txt
@@ -31,6 +31,11 @@ target_link_libraries(melonDS
 	core ${SDL2_LIBRARIES} libui)
 
 if (UNIX)
+	option(UNIX_PORTABLE "Make a portable build that looks for its configuration in the current directory" ON)
+	if (UNIX_PORTABLE)
+		add_definitions(-DUNIX_PORTABLE)
+	endif()
+
 	find_package(PkgConfig REQUIRED)
 	pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
 	pkg_check_modules(SDL2 REQUIRED sdl2)
@@ -61,4 +66,7 @@ elseif (WIN32)
 	target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi)
 endif ()
 
+install(FILES ../../melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
+install(FILES ../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps)
+install(FILES ../../romlist.bin DESTINATION ${CMAKE_INSTALL_PREFIX}/share/melonds)
 install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
diff --git a/src/libui_sdl/Platform.cpp b/src/libui_sdl/Platform.cpp
index 94b3791..5cbf344 100644
--- a/src/libui_sdl/Platform.cpp
+++ b/src/libui_sdl/Platform.cpp
@@ -135,6 +135,63 @@ FILE* OpenFile(const char* path, const char* mode, bool mustexist)
     return ret;
 }
 
+#if !defined(UNIX_PORTABLE) && !defined(__WIN32__)
+
+FILE* OpenLocalFile(const char* path, const char* mode)
+{
+    std::string fullpath;
+    if (path[0] == '/')
+    {
+        // If it's an absolute path, just open that.
+        fullpath = std::string(path);
+    }
+    else
+    {
+        // Check user configuration directory
+        std::string confpath = std::string(g_get_user_config_dir()) + "/melonds/";
+        g_mkdir_with_parents(confpath.c_str(), 0755);
+        fullpath = confpath + path;
+    }
+
+    return OpenFile(fullpath.c_str(), mode, mode[0] != 'w');
+}
+
+FILE* OpenDataFile(const char* path)
+{
+    const char* melondir = "melonds";
+    const char* const* sys_dirs = g_get_system_data_dirs();
+    const char* user_dir = g_get_user_data_dir();
+
+    // First check the user's data directory
+    char* fullpath = g_build_path("/", user_dir, melondir, path, NULL);
+    if (access(fullpath, R_OK) == 0)
+    {
+        FILE* f = fopen(fullpath, "r");
+        g_free(fullpath);
+        return f;
+    }
+    free(fullpath);
+
+    // Then check the system data directories
+    for (size_t i = 0; sys_dirs[i] != NULL; i++)
+    {
+        const char* dir = sys_dirs[i];
+        char* fullpath = g_build_path("/", dir, melondir, path, NULL);
+
+        if (access(fullpath, R_OK) == 0)
+        {
+            FILE* f = fopen(fullpath, "r");
+            g_free(fullpath);
+            return f;
+        }
+        free(fullpath);
+    }
+    
+    return NULL;
+}
+
+#else
+
 FILE* OpenLocalFile(const char* path, const char* mode)
 {
     bool relpath = false;
@@ -257,6 +314,13 @@ FILE* OpenLocalFile(const char* path, const char* mode)
     return NULL;
 }
 
+FILE* OpenDataFile(const char* path)
+{
+	return OpenLocalFile(path, "r");
+}
+
+#endif
+
 
 void* Thread_Create(void (*func)())
 {
diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp
index d6aa460..40b7079 100644
--- a/src/libui_sdl/main.cpp
+++ b/src/libui_sdl/main.cpp
@@ -21,6 +21,10 @@
 #include <stdio.h>
 #include <string.h>
 
+#ifndef __WIN32__
+#include <glib.h>
+#endif
+
 #include <SDL2/SDL.h>
 #include "libui/ui.h"
 
@@ -2589,6 +2593,7 @@ int main(int argc, char** argv)
     printf("melonDS " MELONDS_VERSION "\n");
     printf(MELONDS_URL "\n");
 
+#if defined(__WIN32__) || defined(UNIX_PORTABLE)
     if (argc > 0 && strlen(argv[0]) > 0)
     {
         int len = strlen(argv[0]);
@@ -2615,6 +2620,13 @@ int main(int argc, char** argv)
         EmuDirectory = new char[2];
         strcpy(EmuDirectory, ".");
     }
+#else
+	const char* confdir = g_get_user_config_dir();
+	const char* confname = "/melonds";
+	EmuDirectory = new char[strlen(confdir) + strlen(confname) + 1];
+	strcat(EmuDirectory, confdir);
+	strcat(EmuDirectory, confname);
+#endif
 
     // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
     SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
@@ -2650,15 +2662,23 @@ int main(int argc, char** argv)
         !Platform::LocalFileExists("bios9.bin") ||
         !Platform::LocalFileExists("firmware.bin"))
     {
-        uiMsgBoxError(
-            NULL,
-            "BIOS/Firmware not found",
+#if defined(__WIN32__) || defined(UNIX_PORTABLE)
+		const char* locationName = "the directory you run melonDS from";
+#else
+		char* locationName = EmuDirectory;
+#endif
+		char msgboxtext[512];
+		sprintf(msgboxtext, 
             "One or more of the following required files don't exist or couldn't be accessed:\n\n"
             "bios7.bin -- ARM7 BIOS\n"
             "bios9.bin -- ARM9 BIOS\n"
             "firmware.bin -- firmware image\n\n"
-            "Dump the files from your DS and place them in the directory you run melonDS from.\n"
-            "Make sure that the files can be accessed.");
+            "Dump the files from your DS and place them in %s.\n"
+            "Make sure that the files can be accessed.",
+			locationName
+		);
+
+        uiMsgBoxError(NULL, "BIOS/Firmware not found", msgboxtext);
 
         uiUninit();
         SDL_Quit();
@@ -2704,7 +2724,7 @@ int main(int argc, char** argv)
         }
     }
     {
-        FILE* f = Platform::OpenLocalFile("romlist.bin", "rb");
+        FILE* f = Platform::OpenDataFile("romlist.bin");
         if (f)
         {
             u32 data;
-- 
cgit v1.2.3