1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <SDL2/SDL.h>
6 
7 #if defined(_WIN32) || defined(WIN32)
8 #include <shlobj.h> //SHCreateDirectoryEx
9 #else
10 #include <sys/stat.h> // mkdir
11 #endif
12 
13 #include "resources/pathmanager.h"
14 #include "utils/log.h"
15 
16 // Files
17 static const char* logfile_name = "openomf.log";
18 static const char* configfile_name = "openomf.conf";
19 static const char* scorefile_name = "SCORES.DAT";
20 static const char* savegamedir_name = "save/";
21 static char errormessage[128];
22 
23 // Lists
24 static char* local_paths[NUMBER_OF_LOCAL_PATHS];
25 static char* resource_paths[NUMBER_OF_RESOURCES];
26 
27 // Build directory
local_path_build(int path_id,const char * path,const char * ext)28 static void local_path_build(int path_id, const char *path, const char *ext) {
29     int len = strlen(path) + strlen(ext) + 1;
30     local_paths[path_id] = realloc(local_paths[path_id], len);
31     sprintf(local_paths[path_id], "%s%s", path, ext);
32 }
resource_path_build(int path_id,const char * path,const char * ext)33 static void resource_path_build(int path_id, const char *path, const char *ext) {
34     int len = strlen(path) + strlen(ext) + 1;
35     resource_paths[path_id] = realloc(resource_paths[path_id], len);
36     sprintf(resource_paths[path_id], "%s%s", path, ext);
37 }
38 
str_ends_with_sep(const char * str)39 int str_ends_with_sep(const char *str) {
40     int pos = strlen(str) - 1;
41     return (str[pos] == '/' || str[pos] == '\\');
42 }
43 
44 // Makes sure resource file exists
pm_validate_resources()45 int pm_validate_resources() {
46     for(int i = 0; i < NUMBER_OF_RESOURCES; i++) {
47         const char *testfile = pm_get_resource_path(i);
48         if(access(testfile, F_OK) == -1) {
49             sprintf(errormessage, "Missing file %s.", testfile);
50             return 1;
51         }
52     }
53     return 0;
54 }
55 
pm_init()56 int pm_init() {
57     char *local_base_dir;
58     char *bin_base_dir;
59 
60     // Clear everything
61     errormessage[0] = 0;
62     memset(local_paths, 0, sizeof(local_paths));
63     memset(resource_paths, 0, sizeof(resource_paths));
64 
65     // Find local basedir
66     local_base_dir = pm_get_local_base_dir();
67     if(local_base_dir == NULL) {
68         goto error_0;
69     }
70 
71     // Other paths
72     local_path_build(LOG_PATH, local_base_dir, logfile_name);
73     local_path_build(CONFIG_PATH, local_base_dir, configfile_name);
74     local_path_build(SCORE_PATH, local_base_dir, scorefile_name);
75     local_path_build(SAVE_PATH, local_base_dir, savegamedir_name);
76 
77     // Set default base dirs for resources and plugins
78     int m_ok = 0;
79     if(pm_in_release_mode()) {
80         // where is the openomf binary, if this call fails we will look for resources in ./resources
81         bin_base_dir = SDL_GetBasePath();
82         if(bin_base_dir != NULL) {
83             if(!strcasecmp(SDL_GetPlatform(), "Windows")) {
84                 // on windows, the resources will be in ./resources, relative to the binary
85                 local_path_build(RESOURCE_PATH, bin_base_dir, "resources\\");
86                 local_path_build(PLUGIN_PATH, bin_base_dir, "plugins\\");
87                 m_ok = 1;
88             } else if(!strcasecmp(SDL_GetPlatform(), "FreeBSD")) {
89                 // on linux, the resources will be in ../share/games/openomf, relative to the binary
90                 // so if openomf is installed to /usr/local/bin,
91                 // the resources will be in /usr/local/share/games/openomf
92                 local_path_build(RESOURCE_PATH, "/usr/local", "/share/openomf/");
93                 local_path_build(PLUGIN_PATH, bin_base_dir, "../lib/openomf/");
94                 m_ok = 1;
95             } else if(!strcasecmp(SDL_GetPlatform(), "Mac OS X")) {
96                 // on OSX, GetBasePath returns the 'Resources' directory
97                 // if run from an app bundle, so we can use this as-is
98                 local_path_build(RESOURCE_PATH, bin_base_dir, "");
99                 local_path_build(PLUGIN_PATH, bin_base_dir, "plugins/");
100                 m_ok = 1;
101             }
102             // any other platform will look in ./resources
103             SDL_free(bin_base_dir);
104         }
105     }
106 
107     // Set default resource and plugins paths
108     if(!m_ok) {
109         if(!strcasecmp(SDL_GetPlatform(), "Windows")) {
110             local_path_build(RESOURCE_PATH, "resources\\", "");
111             local_path_build(PLUGIN_PATH, "plugins\\", "");
112         } else {
113             local_path_build(RESOURCE_PATH, "resources/", "");
114             local_path_build(PLUGIN_PATH, "plugins/", "");
115         }
116     }
117 
118     char *platform_sep = "/";
119     if(!strcasecmp(SDL_GetPlatform(), "Windows")) {
120         platform_sep = "\\";
121     }
122 
123     // check if we have overrides from the environment
124     char *resource_env = getenv("OPENOMF_RESOURCE_DIR");
125     if (resource_env) {
126         char *ext = str_ends_with_sep(resource_env) ? "" : platform_sep;
127         local_path_build(RESOURCE_PATH, resource_env, ext);
128     }
129 
130     char *plugin_env = getenv("OPENOMF_PLUGIN_DIR");
131     if (plugin_env) {
132         char *ext = str_ends_with_sep(plugin_env) ? "" : platform_sep;
133         local_path_build(PLUGIN_PATH, plugin_env, ext);
134     }
135 
136 
137     // Set resource paths
138     for(int i = 0; i < NUMBER_OF_RESOURCES; i++) {
139         resource_path_build(i,
140             pm_get_local_path(RESOURCE_PATH),
141             get_resource_file(i));
142     }
143 
144     // Check resources
145     if(pm_validate_resources() != 0) {
146         goto error_1;
147     }
148 
149     // All done
150     free(local_base_dir);
151     return 0;
152 
153 error_1:
154     pm_free();
155     free(local_base_dir);
156 error_0:
157     return 1;
158 }
159 
pm_log()160 void pm_log() {
161     // Debug info
162     for(unsigned int i = 0; i < NUMBER_OF_LOCAL_PATHS; i++) {
163         DEBUG("%s: %s",
164             pm_get_local_path_type_name(i),
165             pm_get_local_path(i));
166     }
167 }
168 
pm_free()169 void pm_free() {
170     for(int i = 0; i < NUMBER_OF_RESOURCES; i++) {
171         free(resource_paths[i]);
172     }
173     for(int i = 0; i < NUMBER_OF_LOCAL_PATHS; i++) {
174         free(local_paths[i]);
175     }
176 }
177 
pm_get_errormsg()178 const char* pm_get_errormsg() {
179     return errormessage;
180 }
181 
pm_in_debug_mode()182 int pm_in_debug_mode() {
183 #ifdef DEBUGMODE
184     return 1;
185 #else
186     return 0;
187 #endif
188 }
189 
pm_in_release_mode()190 int pm_in_release_mode() {
191     if(!pm_in_portable_mode() && !pm_in_debug_mode()) {
192         return 1;
193     }
194     return 0;
195 }
196 
pm_in_portable_mode()197 int pm_in_portable_mode() {
198     if(access(configfile_name, F_OK) != -1) {
199         return 1;
200     }
201     return 0;
202 }
203 
pm_get_local_base_dir()204 char* pm_get_local_base_dir() {
205     char *out = NULL;
206     if(pm_in_portable_mode()) {
207         out = malloc(1);
208         out[0] = 0;
209         return out;
210     }
211 
212     // Attempt to open up locally writable directory
213     char *sdl_path = SDL_GetPrefPath("openomfproject", "OpenOMF");
214     if(sdl_path == NULL) {
215         sprintf(errormessage, "Error getting config path: %s", SDL_GetError());
216         return NULL;
217     }
218     out = malloc(strlen(sdl_path)+1);
219     strcpy(out, sdl_path);
220     SDL_free(sdl_path);
221 
222     // Ensure the path exists before continuing on
223     // XXX shouldn't SDL_GetPrefPath automatically create the path if it doesn't exist?
224 #if defined(_WIN32) || defined(WIN32)
225     int sherr = SHCreateDirectoryEx(NULL, out, NULL);
226     if(sherr == ERROR_FILE_EXISTS) {
227         sprintf(errormessage, "Please delete this file and relaunch OpenOMF: %s", out);
228         return NULL;
229     } else if(sherr != ERROR_SUCCESS && sherr != ERROR_ALREADY_EXISTS) {
230         sprintf(errormessage, "Failed to create config path: %s", out);
231         return NULL;
232     }
233 #endif
234 
235     // All done
236     return out;
237 }
238 
pm_get_local_path(unsigned int local_id)239 const char* pm_get_local_path(unsigned int local_id) {
240     if(local_id >= NUMBER_OF_LOCAL_PATHS) {
241         return NULL;
242     }
243     return local_paths[local_id];
244 }
245 
pm_get_resource_path(unsigned int resource_id)246 const char* pm_get_resource_path(unsigned int resource_id) {
247     if(resource_id >= NUMBER_OF_RESOURCES) {
248         return NULL;
249     }
250     return resource_paths[resource_id];
251 }
252 
pm_get_local_path_type_name(unsigned int path_id)253 const char* pm_get_local_path_type_name(unsigned int path_id) {
254     switch(path_id) {
255         case RESOURCE_PATH: return "RESOURCE_PATH";
256         case PLUGIN_PATH: return "PLUGIN_PATH";
257         case CONFIG_PATH: return "CONFIG_PATH";
258         case LOG_PATH: return "LOG_PATH";
259         case SCORE_PATH: return "SCORE_PATH";
260         case SAVE_PATH: return "SAVE_PATH";
261     }
262     return "UNKNOWN";
263 }
264 
pm_create_dir(const char * dirname)265 int pm_create_dir(const char* dirname) {
266     #if defined(_WIN32) || defined(WIN32)
267     if(SHCreateDirectoryEx(NULL, dirname, NULL) != ERROR_SUCCESS) {
268         PERROR("Error while attempting to create directory '%s'.", dirname);
269         return 1;
270     }
271     #else
272     if(mkdir(dirname, 0755) != 0) {
273         PERROR("Error while attempting to create directory '%s'.", dirname);
274         return 1;
275     }
276     #endif
277     return 0;
278 }
279