frontend: support analog controller using nubs; some refactoring
[pcsx_rearmed.git] / frontend / main.c
1 /*
2  * (C) notaz, 2010-2011
3  *
4  * This work is licensed under the terms of the GNU GPLv2 or later.
5  * See the COPYING file in the top-level directory.
6  */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdarg.h>
11 #include <dlfcn.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <signal.h>
16
17 #include "main.h"
18 #include "plugin.h"
19 #include "plugin_lib.h"
20 #include "pcnt.h"
21 #include "menu.h"
22 #include "../libpcsxcore/misc.h"
23 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
24 #include "../plugins/cdrcimg/cdrcimg.h"
25 #include "common/plat.h"
26 #include "common/input.h"
27
28 int ready_to_go;
29 unsigned long gpuDisp;
30 char cfgfile_basename[MAXPATHLEN];
31 static char *(*real_getenv)(const char *name);
32
33 static void make_path(char *buf, size_t size, const char *dir, const char *fname)
34 {
35         if (fname)
36                 snprintf(buf, size, ".%s%s", dir, fname);
37         else
38                 snprintf(buf, size, ".%s", dir);
39 }
40 #define MAKE_PATH(buf, dir, fname) \
41         make_path(buf, sizeof(buf), dir, fname)
42
43 static void create_profile_dir(const char *directory) {
44         char path[MAXPATHLEN];
45
46         MAKE_PATH(path, directory, NULL);
47         mkdir(path, S_IRWXU | S_IRWXG);
48 }
49
50 static void CheckSubDir() {
51         // make sure that ~/.pcsx exists
52         create_profile_dir(PCSX_DOT_DIR);
53
54         create_profile_dir(BIOS_DIR);
55         create_profile_dir(MEMCARD_DIR);
56         create_profile_dir(STATES_DIR);
57         create_profile_dir(PLUGINS_DIR);
58         create_profile_dir(PLUGINS_CFG_DIR);
59         create_profile_dir(CHEATS_DIR);
60         create_profile_dir(PATCHES_DIR);
61         create_profile_dir(PCSX_DOT_DIR "cfg");
62 }
63
64 void set_cd_image(const char *fname)
65 {
66         const char *ext = NULL;
67         
68         if (fname != NULL)
69                 ext = strrchr(fname, '.');
70
71         if (ext && (
72             strcasecmp(ext, ".z") == 0 || strcasecmp(ext, ".bz") == 0 ||
73             strcasecmp(ext, ".znx") == 0 || strcasecmp(ext, ".pbp") == 0)) {
74                 SetIsoFile(NULL);
75                 cdrcimg_set_fname(fname);
76                 strcpy(Config.Cdr, "builtin_cdrcimg");
77         } else {
78                 SetIsoFile(fname);
79                 strcpy(Config.Cdr, "builtin_cdr");
80         }
81 }
82
83 static void set_default_paths(void)
84 {
85         MAKE_PATH(Config.Mcd1, MEMCARD_DIR, "card1.mcd");
86         MAKE_PATH(Config.Mcd2, MEMCARD_DIR, "card2.mcd");
87         strcpy(Config.BiosDir, "bios");
88
89         strcpy(Config.PluginsDir, "plugins");
90         strcpy(Config.Gpu, "builtin_gpu");
91         strcpy(Config.Spu, "builtin_spu");
92         strcpy(Config.Cdr, "builtin_cdr");
93         strcpy(Config.Pad1, "builtin_pad");
94         strcpy(Config.Pad2, "builtin_pad");
95         strcpy(Config.Net, "Disabled");
96         Config.PsxAuto = 1;
97
98         snprintf(Config.PatchesDir, sizeof(Config.PatchesDir), "." PATCHES_DIR);
99 }
100
101 int main(int argc, char *argv[])
102 {
103         void *tmp;
104
105         tmp = dlopen("/lib/libdl.so.2", RTLD_LAZY);
106         if (tmp == NULL)
107                 tmp = dlopen("/lib32/libdl.so.2", RTLD_LAZY);
108         if (tmp != NULL)
109                 real_getenv = dlsym(tmp, "getenv");
110         if (real_getenv == NULL) {
111                 fprintf(stderr, "%s\n", dlerror());
112                 return 1;
113         }
114         dlclose(tmp);
115
116         // what is the name of the config file?
117         // it may be redefined by -cfg on the command line
118         strcpy(cfgfile_basename, "pcsx.cfg");
119
120         emuLog = stdout;
121         SetIsoFile(NULL);
122
123         memset(&Config, 0, sizeof(Config));
124
125         CheckSubDir();
126         set_default_paths();
127         strcpy(Config.Bios, "HLE");
128
129 #ifdef MAEMO
130         extern int maemo_main(int argc, char **argv);
131         return maemo_main(argc, argv);
132 #else
133         char file[MAXPATHLEN] = "";
134         char path[MAXPATHLEN];
135         const char *cdfile = NULL;
136         int loadst = 0;
137         int i;
138
139         // read command line options
140         for (i = 1; i < argc; i++) {
141                      if (!strcmp(argv[i], "-psxout")) Config.PsxOut = 1;
142                 else if (!strcmp(argv[i], "-load")) loadst = atol(argv[++i]);
143                 else if (!strcmp(argv[i], "-cfg")) {
144                         if (i+1 >= argc) break;
145                         strncpy(cfgfile_basename, argv[++i], MAXPATHLEN-100);   /* TODO buffer overruns */
146                         printf("Using config file %s.\n", cfgfile_basename);
147                 }
148                 else if (!strcmp(argv[i], "-cdfile")) {
149                         char isofilename[MAXPATHLEN];
150
151                         if (i+1 >= argc) break;
152                         strncpy(isofilename, argv[++i], MAXPATHLEN);
153                         if (isofilename[0] != '/') {
154                                 getcwd(path, MAXPATHLEN);
155                                 if (strlen(path) + strlen(isofilename) + 1 < MAXPATHLEN) {
156                                         strcat(path, "/");
157                                         strcat(path, isofilename);
158                                         strcpy(isofilename, path);
159                                 } else
160                                         isofilename[0] = 0;
161                         }
162
163                         cdfile = isofilename;
164                 }
165                 else if (!strcmp(argv[i], "-h") ||
166                          !strcmp(argv[i], "-help") ||
167                          !strcmp(argv[i], "--help")) {
168                          printf(PACKAGE_NAME " " PACKAGE_VERSION "\n");
169                          printf("%s\n", _(
170                                                         " pcsx [options] [file]\n"
171                                                         "\toptions:\n"
172                                                         "\t-cdfile FILE\tRuns a CD image file\n"
173                                                         "\t-nogui\t\tDon't open the GTK GUI\n"
174                                                         "\t-cfg FILE\tLoads desired configuration file (default: ~/.pcsx/pcsx.cfg)\n"
175                                                         "\t-psxout\t\tEnable PSX output\n"
176                                                         "\t-load STATENUM\tLoads savestate STATENUM (1-5)\n"
177                                                         "\t-h -help\tDisplay this message\n"
178                                                         "\tfile\t\tLoads file\n"));
179                          return 0;
180                 } else {
181                         strncpy(file, argv[i], MAXPATHLEN);
182                         if (file[0] != '/') {
183                                 getcwd(path, MAXPATHLEN);
184                                 if (strlen(path) + strlen(file) + 1 < MAXPATHLEN) {
185                                         strcat(path, "/");
186                                         strcat(path, file);
187                                         strcpy(file, path);
188                                 } else
189                                         file[0] = 0;
190                         }
191                 }
192         }
193
194         if (cdfile)
195                 set_cd_image(cdfile);
196
197         if (SysInit() == -1)
198                 return 1;
199
200         // frontend stuff
201         in_init();
202         in_probe();
203         plat_init();
204         menu_init();
205
206         if (LoadPlugins() == -1) {
207                 // FIXME: this recovery doesn't work, just delete bad config and bail out
208                 // SysMessage("could not load plugins, retrying with defaults\n");
209                 set_default_paths();
210                 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "%s", cfgfile_basename);
211                 remove(path);
212                 SysMessage("Failed loading plugins!");
213                 return 1;
214         }
215         pcnt_hook_plugins();
216
217         if (OpenPlugins() == -1) {
218                 return 1;
219         }
220         plugin_call_rearmed_cbs();
221
222         CheckCdrom();
223         SysReset();
224
225         if (file[0] != '\0') {
226                 if (Load(file) != -1)
227                         ready_to_go = 1;
228         } else {
229                 if (cdfile) {
230                         if (LoadCdrom() == -1) {
231                                 ClosePlugins();
232                                 printf(_("Could not load CD-ROM!\n"));
233                                 return -1;
234                         }
235                         ready_to_go = 1;
236                 }
237         }
238
239         // If a state has been specified, then load that
240         if (loadst) {
241                 char state_filename[MAXPATHLEN];
242                 int ret = get_state_filename(state_filename, sizeof(state_filename), loadst - 1);
243                 if (ret == 0)
244                         ret = LoadState(state_filename);
245                 printf("%s state %s\n", ret ? "failed to load" : "loaded", state_filename);
246         }
247
248         if (ready_to_go)
249                 menu_prepare_emu();
250         else
251                 menu_loop();
252
253         pl_start_watchdog();
254
255         while (1)
256         {
257                 stop = 0;
258                 psxCpu->Execute();
259                 menu_loop();
260         }
261
262         return 0;
263 #endif
264 }
265
266 int SysInit() {
267         if (EmuInit() == -1) {
268                 printf("PSX emulator couldn't be initialized.\n");
269                 return -1;
270         }
271
272         LoadMcds(Config.Mcd1, Config.Mcd2);     /* TODO Do we need to have this here, or in the calling main() function?? */
273
274         if (Config.Debug) {
275                 StartDebugger();
276         }
277
278         return 0;
279 }
280
281 void SysRunGui() {
282         printf("SysRunGui\n");
283 }
284
285 void StartGui() {
286         printf("StartGui\n");
287 }
288
289 static void dummy_lace()
290 {
291 }
292
293 void SysReset() {
294         // rearmed hack: EmuReset() runs some code when real BIOS is used,
295         // but we usually do reset from menu while GPU is not open yet,
296         // so we need to prevent updateLace() call..
297         void *real_lace = GPU_updateLace;
298         GPU_updateLace = dummy_lace;
299
300         EmuReset();
301
302         // hmh core forgets this
303         CDR_stop();
304
305         GPU_updateLace = real_lace;
306 }
307
308 void SysClose() {
309         EmuShutdown();
310         ReleasePlugins();
311
312         StopDebugger();
313
314         if (emuLog != NULL) fclose(emuLog);
315 }
316
317 void SysUpdate() {
318 }
319
320 void OnFile_Exit() {
321         printf("OnFile_Exit\n");
322 #ifndef MAEMO
323         menu_finish();
324 #endif
325         plat_finish();
326         SysClose();
327         exit(0);
328 }
329
330 int get_state_filename(char *buf, int size, int i) {
331         char trimlabel[33];
332         int j;
333
334         strncpy(trimlabel, CdromLabel, 32);
335         trimlabel[32] = 0;
336         for (j = 31; j >= 0; j--)
337                 if (trimlabel[j] == ' ')
338                         trimlabel[j] = 0;
339                 else
340                         continue;
341
342         snprintf(buf, size, "." STATES_DIR "%.32s-%.9s.%3.3d",
343                 trimlabel, CdromId, i);
344
345         return 0;
346 }
347
348 void SysPrintf(const char *fmt, ...) {
349         va_list list;
350         char msg[512];
351
352         va_start(list, fmt);
353         vsprintf(msg, fmt, list);
354         va_end(list);
355
356         fprintf(emuLog, "%s", msg);
357 }
358
359 void SysMessage(const char *fmt, ...) {
360         va_list list;
361         char msg[512];
362
363         va_start(list, fmt);
364         vsprintf(msg, fmt, list);
365         va_end(list);
366
367         if (msg[strlen(msg) - 1] == '\n')
368                 msg[strlen(msg) - 1] = 0;
369
370         fprintf(stderr, "%s\n", msg);
371 }
372
373 static void SignalExit(int sig) {
374         ClosePlugins();
375         OnFile_Exit();
376 }
377
378 #define PARSEPATH(dst, src) \
379         ptr = src + strlen(src); \
380         while (*ptr != '\\' && ptr != src) ptr--; \
381         if (ptr != src) { \
382                 strcpy(dst, ptr+1); \
383         }
384
385 static int _OpenPlugins(void) {
386         int ret;
387
388         signal(SIGINT, SignalExit);
389         signal(SIGPIPE, SignalExit);
390
391         GPU_clearDynarec(clearDynarec);
392
393         ret = CDR_open();
394         if (ret < 0) { SysMessage(_("Error opening CD-ROM plugin!")); return -1; }
395         ret = SPU_open();
396         if (ret < 0) { SysMessage(_("Error opening SPU plugin!")); return -1; }
397         SPU_registerCallback(SPUirq);
398         // pcsx-rearmed: we handle gpu elsewhere
399         //ret = GPU_open(&gpuDisp, "PCSX", NULL);
400         //if (ret < 0) { SysMessage(_("Error opening GPU plugin!")); return -1; }
401         ret = PAD1_open(&gpuDisp);
402         if (ret < 0) { SysMessage(_("Error opening Controller 1 plugin!")); return -1; }
403         ret = PAD2_open(&gpuDisp);
404         if (ret < 0) { SysMessage(_("Error opening Controller 2 plugin!")); return -1; }
405
406         if (Config.UseNet && !NetOpened) {
407                 netInfo info;
408                 char path[MAXPATHLEN];
409                 char dotdir[MAXPATHLEN];
410
411                 MAKE_PATH(dotdir, "/.pcsx/plugins/", NULL);
412
413                 strcpy(info.EmuName, "PCSX " PACKAGE_VERSION);
414                 strncpy(info.CdromID, CdromId, 9);
415                 strncpy(info.CdromLabel, CdromLabel, 9);
416                 info.psxMem = psxM;
417                 info.GPU_showScreenPic = GPU_showScreenPic;
418                 info.GPU_displayText = GPU_displayText;
419                 info.GPU_showScreenPic = GPU_showScreenPic;
420                 info.PAD_setSensitive = PAD1_setSensitive;
421                 sprintf(path, "%s%s", Config.BiosDir, Config.Bios);
422                 strcpy(info.BIOSpath, path);
423                 strcpy(info.MCD1path, Config.Mcd1);
424                 strcpy(info.MCD2path, Config.Mcd2);
425                 sprintf(path, "%s%s", dotdir, Config.Gpu);
426                 strcpy(info.GPUpath, path);
427                 sprintf(path, "%s%s", dotdir, Config.Spu);
428                 strcpy(info.SPUpath, path);
429                 sprintf(path, "%s%s", dotdir, Config.Cdr);
430                 strcpy(info.CDRpath, path);
431                 NET_setInfo(&info);
432
433                 ret = NET_open(&gpuDisp);
434                 if (ret < 0) {
435                         if (ret == -2) {
436                                 // -2 is returned when something in the info
437                                 // changed and needs to be synced
438                                 char *ptr;
439
440                                 PARSEPATH(Config.Bios, info.BIOSpath);
441                                 PARSEPATH(Config.Gpu,  info.GPUpath);
442                                 PARSEPATH(Config.Spu,  info.SPUpath);
443                                 PARSEPATH(Config.Cdr,  info.CDRpath);
444
445                                 strcpy(Config.Mcd1, info.MCD1path);
446                                 strcpy(Config.Mcd2, info.MCD2path);
447                                 return -2;
448                         } else {
449                                 Config.UseNet = FALSE;
450                         }
451                 } else {
452                         if (NET_queryPlayer() == 1) {
453                                 if (SendPcsxInfo() == -1) Config.UseNet = FALSE;
454                         } else {
455                                 if (RecvPcsxInfo() == -1) Config.UseNet = FALSE;
456                         }
457                 }
458                 NetOpened = TRUE;
459         } else if (Config.UseNet) {
460                 NET_resume();
461         }
462
463         return 0;
464 }
465
466 int OpenPlugins() {
467         int ret;
468
469         while ((ret = _OpenPlugins()) == -2) {
470                 ReleasePlugins();
471                 LoadMcds(Config.Mcd1, Config.Mcd2);
472                 if (LoadPlugins() == -1) return -1;
473         }
474         return ret;
475 }
476
477 void ClosePlugins() {
478         int ret;
479
480         signal(SIGINT, SIG_DFL);
481         signal(SIGPIPE, SIG_DFL);
482         ret = CDR_close();
483         if (ret < 0) { SysMessage(_("Error closing CD-ROM plugin!")); return; }
484         ret = SPU_close();
485         if (ret < 0) { SysMessage(_("Error closing SPU plugin!")); return; }
486         ret = PAD1_close();
487         if (ret < 0) { SysMessage(_("Error closing Controller 1 Plugin!")); return; }
488         ret = PAD2_close();
489         if (ret < 0) { SysMessage(_("Error closing Controller 2 plugin!")); return; }
490         // pcsx-rearmed: we handle gpu elsewhere
491         //ret = GPU_close();
492         //if (ret < 0) { SysMessage(_("Error closing GPU plugin!")); return; }
493
494         if (Config.UseNet) {
495                 NET_pause();
496         }
497 }
498
499 #if 1
500 /* this is to avoid having to hack every plugin to stop using $HOME */
501 char *getenv(const char *name)
502 {
503         static char ret[8] = ".";
504
505         if (name && strcmp(name, "HOME") == 0 &&
506                         ((int)name >> 28) == 0) // HACK: let libs find home
507                 return ret;
508
509         return real_getenv(name);
510 }
511 #endif
512
513 /* we hook statically linked plugins here */
514 static const char *builtin_plugins[] = {
515         "builtin_gpu", "builtin_spu", "builtin_cdr", "builtin_pad",
516         "builtin_cdrcimg",
517 };
518
519 static const int builtin_plugin_ids[] = {
520         PLUGIN_GPU, PLUGIN_SPU, PLUGIN_CDR, PLUGIN_PAD,
521         PLUGIN_CDRCIMG,
522 };
523
524 void *SysLoadLibrary(const char *lib) {
525         const char *tmp = strrchr(lib, '/');
526         void *ret;
527         int i;
528
529         printf("plugin: %s\n", lib);
530
531         if (tmp != NULL) {
532                 tmp++;
533                 for (i = 0; i < ARRAY_SIZE(builtin_plugins); i++)
534                         if (strcmp(tmp, builtin_plugins[i]) == 0)
535                                 return (void *)(long)(PLUGIN_DL_BASE + builtin_plugin_ids[i]);
536         }
537
538 #if defined(__x86_64__) || defined(__i386__)
539         // convenience hack
540         char name[MAXPATHLEN];
541         snprintf(name, sizeof(name), "%s.x86", lib);
542         lib = name;
543 #endif
544
545         ret = dlopen(lib, RTLD_NOW);
546         if (ret == NULL)
547                 fprintf(stderr, "dlopen: %s\n", dlerror());
548         return ret;
549 }
550
551 void *SysLoadSym(void *lib, const char *sym) {
552         unsigned int plugid = (unsigned int)(long)lib;
553
554         if (PLUGIN_DL_BASE <= plugid && plugid < PLUGIN_DL_BASE + ARRAY_SIZE(builtin_plugins))
555                 return plugin_link(plugid - PLUGIN_DL_BASE, sym);
556
557         return dlsym(lib, sym);
558 }
559
560 const char *SysLibError() {
561         return dlerror();
562 }
563
564 void SysCloseLibrary(void *lib) {
565         unsigned int plugid = (unsigned int)(long)lib;
566
567         if (PLUGIN_DL_BASE <= plugid && plugid < PLUGIN_DL_BASE + ARRAY_SIZE(builtin_plugins))
568                 return;
569
570         dlclose(lib);
571 }
572