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