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