1 /* RetroArch - A frontend for libretro.
2  * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  * Copyright (C) 2011-2017 - Daniel De Matteis
4  *
5  * RetroArch is free software: you can redistribute it and/or modify it under the terms
6  * of the GNU General Public License as published by the Free Software Found-
7  * ation, either version 3 of the License, or (at your option) any later version.
8  *
9  * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  * PURPOSE. See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along with RetroArch.
14  * If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <stdint.h>
18 #include <boolean.h>
19 #include <stddef.h>
20 #include <string.h>
21 
22 #include <file/config_file.h>
23 #include <file/file_path.h>
24 #include <lists/dir_list.h>
25 #include <retro_miscellaneous.h>
26 #include <string/stdstring.h>
27 #include <compat/strl.h>
28 
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32 
33 #include "frontend_driver.h"
34 #include "../defaults.h"
35 #include "../verbosity.h"
36 #include "../file_path_special.h"
37 
38 struct defaults g_defaults;
39 
40 /*We need to set libretro to the first entry in the cores
41  * directory so that it will be saved to the config file
42  */
find_first_libretro_core(char * first_file,size_t size_of_first_file,const char * dir,const char * ext)43 static void find_first_libretro_core(char *first_file,
44    size_t size_of_first_file, const char *dir,
45    const char * ext)
46 {
47    size_t i;
48    bool                 ret = false;
49    struct string_list *list = dir_list_new(dir, ext, false, true, false, false);
50 
51    if (!list)
52    {
53       RARCH_ERR("Couldn't read directory."
54             " Cannot infer default libretro core.\n");
55       return;
56    }
57 
58    RARCH_LOG("Searching for valid libretro implementation in: \"%s\".\n",
59          dir);
60 
61    for (i = 0; i < list->size && !ret; i++)
62    {
63       char fname[PATH_MAX_LENGTH]           = {0};
64       char salamander_name[PATH_MAX_LENGTH] = {0};
65       const char *libretro_elem             = (const char*)list->elems[i].data;
66 
67       RARCH_LOG("Checking library: \"%s\".\n", libretro_elem);
68 
69       if (!libretro_elem)
70          continue;
71 
72       fill_pathname_base(fname, libretro_elem, sizeof(fname));
73 
74       if (!frontend_driver_get_salamander_basename(
75                salamander_name, sizeof(salamander_name)))
76          break;
77 
78       if (!strncmp(fname, salamander_name, sizeof(fname)))
79       {
80          if (list->size == (i + 1))
81          {
82             RARCH_WARN("Entry is RetroArch Salamander itself, "
83                   "but is last entry. No choice but to set it.\n");
84             strlcpy(first_file, fname, size_of_first_file);
85          }
86 
87          continue;
88       }
89 
90       strlcpy(first_file, fname, size_of_first_file);
91       RARCH_LOG("First found libretro core is: \"%s\".\n", first_file);
92       ret = true;
93    }
94 
95    dir_list_free(list);
96 }
97 
98 /* Last fallback - we'll need to start the first executable file
99  * we can find in the RetroArch cores directory.
100  */
find_and_set_first_file(char * s,size_t len,const char * ext)101 static void find_and_set_first_file(char *s, size_t len,
102       const char *ext)
103 {
104 
105    char first_file[PATH_MAX_LENGTH] = {0};
106    find_first_libretro_core(first_file, sizeof(first_file),
107          g_defaults.dirs[DEFAULT_DIR_CORE], ext);
108 
109    if (string_is_empty(first_file))
110    {
111       RARCH_ERR("Failed last fallback - RetroArch Salamander will exit.\n");
112       return;
113    }
114 
115    fill_pathname_join(s, g_defaults.dirs[DEFAULT_DIR_CORE], first_file, len);
116    RARCH_LOG("libretro_path now set to: %s.\n", s);
117 }
118 
salamander_init(char * s,size_t len)119 static void salamander_init(char *s, size_t len)
120 {
121    /* Normal executable loading path */
122    config_file_t *config         = NULL;
123    const char *rarch_config_path = g_defaults.path_config;
124    bool config_valid             = false;
125    char config_path[PATH_MAX_LENGTH];
126    char config_dir[PATH_MAX_LENGTH];
127 
128    config_path[0] = '\0';
129    config_dir[0]  = '\0';
130 
131    /* Get salamander config file path */
132    if (!string_is_empty(rarch_config_path))
133       fill_pathname_resolve_relative(config_path,
134             rarch_config_path,
135             FILE_PATH_SALAMANDER_CONFIG,
136             sizeof(config_path));
137    else
138       strcpy_literal(config_path, FILE_PATH_SALAMANDER_CONFIG);
139 
140    /* Ensure that config directory exists */
141    fill_pathname_parent_dir(config_dir, config_path, sizeof(config_dir));
142    if (!string_is_empty(config_dir) &&
143        !path_is_directory(config_dir))
144       path_mkdir(config_dir);
145 
146    /* Attempt to open config file */
147    config = config_file_new_from_path_to_string(config_path);
148 
149    if (config)
150    {
151       char libretro_path[PATH_MAX_LENGTH];
152 
153       libretro_path[0] = '\0';
154 
155       if (config_get_path(config, "libretro_path",
156             libretro_path, sizeof(libretro_path)) &&
157           !string_is_empty(libretro_path) &&
158           !string_is_equal(libretro_path, "builtin"))
159       {
160          strlcpy(s, libretro_path, len);
161          config_valid = true;
162       }
163 
164       config_file_free(config);
165       config = NULL;
166    }
167 
168    if (!config_valid)
169    {
170       char executable_name[PATH_MAX_LENGTH];
171 
172       executable_name[0] = '\0';
173 
174       /* No config file - search filesystem for
175        * first available core */
176       frontend_driver_get_core_extension(
177             executable_name, sizeof(executable_name));
178       find_and_set_first_file(s, len, executable_name);
179 
180       /* Save result to new config file */
181       if (!string_is_empty(s))
182       {
183          config = config_file_new_alloc();
184 
185          if (config)
186          {
187             config_set_path(config, "libretro_path", s);
188             config_file_write(config, config_path, false);
189             config_file_free(config);
190          }
191       }
192    }
193    else
194       RARCH_LOG("Start [%s] found in %s.\n", s,
195             FILE_PATH_SALAMANDER_CONFIG);
196 }
197 
198 #ifdef HAVE_MAIN
salamander_main(int argc,char * argv[])199 int salamander_main(int argc, char *argv[])
200 #else
201 int main(int argc, char *argv[])
202 #endif
203 {
204    char libretro_path[PATH_MAX_LENGTH] = {0};
205    void *args                          = NULL;
206    struct rarch_main_wrap *wrap_args   = NULL;
207    frontend_ctx_driver_t *frontend_ctx = (frontend_ctx_driver_t*)frontend_ctx_init_first();
208 
209    if (!frontend_ctx)
210       return 0;
211 
212    if (frontend_ctx && frontend_ctx->init)
213       frontend_ctx->init(args);
214 
215    if (frontend_ctx && frontend_ctx->environment_get)
216       frontend_ctx->environment_get(&argc, argv, args, wrap_args);
217 
218    salamander_init(libretro_path, sizeof(libretro_path));
219 
220    if (frontend_ctx && frontend_ctx->deinit)
221       frontend_ctx->deinit(args);
222 
223    if (frontend_ctx && frontend_ctx->exitspawn)
224       frontend_ctx->exitspawn(libretro_path, sizeof(libretro_path), NULL);
225 
226    return 1;
227 }
228