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