config.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. /*
  2. * Human-readable config file management for PicoDrive
  3. * (c) notaz, 2008
  4. */
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #ifdef UIQ3
  9. #include <unistd.h>
  10. #endif
  11. #include "config.h"
  12. #include "lprintf.h"
  13. static char *mystrip(char *str);
  14. #ifndef _MSC_VER
  15. #include "menu.h"
  16. #include "emu.h"
  17. #include <Pico/Pico.h>
  18. extern menu_entry opt_entries[];
  19. extern menu_entry opt2_entries[];
  20. extern menu_entry cdopt_entries[];
  21. extern menu_entry ctrlopt_entries[];
  22. extern const int opt_entry_count;
  23. extern const int opt2_entry_count;
  24. extern const int cdopt_entry_count;
  25. extern const int ctrlopt_entry_count;
  26. #ifdef PSP
  27. extern menu_entry opt3_entries[];
  28. extern const int opt3_entry_count;
  29. #endif
  30. static menu_entry *cfg_opts[] =
  31. {
  32. opt_entries,
  33. opt2_entries,
  34. cdopt_entries,
  35. ctrlopt_entries,
  36. #ifdef PSP
  37. opt3_entries,
  38. #endif
  39. };
  40. static const int *cfg_opt_counts[] =
  41. {
  42. &opt_entry_count,
  43. &opt2_entry_count,
  44. &cdopt_entry_count,
  45. &ctrlopt_entry_count,
  46. #ifdef PSP
  47. &opt3_entry_count,
  48. #endif
  49. };
  50. #define NL "\r\n"
  51. static int seek_sect(FILE *f, const char *section)
  52. {
  53. char line[128], *tmp;
  54. int len;
  55. len = strlen(section);
  56. // seek to the section needed
  57. while (!feof(f))
  58. {
  59. tmp = fgets(line, sizeof(line), f);
  60. if (tmp == NULL) break;
  61. if (line[0] != '[') continue; // not section start
  62. if (strncmp(line + 1, section, len) == 0 && line[len+1] == ']')
  63. return 1; // found it
  64. }
  65. return 0;
  66. }
  67. static void custom_write(FILE *f, const menu_entry *me, int no_def)
  68. {
  69. char *str, str24[24];
  70. switch (me->id)
  71. {
  72. case MA_OPT_RENDERER:
  73. if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
  74. !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
  75. if (PicoOpt&POPT_ALT_RENDERER)
  76. str =
  77. #ifndef PSP
  78. "8bit "
  79. #endif
  80. "fast";
  81. else if (currentConfig.EmuOpt&0x80)
  82. str =
  83. #ifndef PSP
  84. "16bit "
  85. #endif
  86. "accurate";
  87. else
  88. str = "8bit accurate";
  89. fprintf(f, "Renderer = %s", str);
  90. break;
  91. case MA_OPT_SCALING:
  92. if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
  93. #ifdef __GP2X__
  94. switch (currentConfig.scaling) {
  95. default: str = "OFF"; break;
  96. case 1: str = "hw horizontal"; break;
  97. case 2: str = "hw horiz. + vert."; break;
  98. case 3: str = "sw horizontal"; break;
  99. }
  100. fprintf(f, "Scaling = %s", str);
  101. #endif
  102. break;
  103. case MA_OPT_FRAMESKIP:
  104. if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
  105. if (currentConfig.Frameskip < 0)
  106. strcpy(str24, "Auto");
  107. else sprintf(str24, "%i", currentConfig.Frameskip);
  108. fprintf(f, "Frameskip = %s", str24);
  109. break;
  110. case MA_OPT_SOUND_QUALITY:
  111. if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
  112. defaultConfig.s_PsndRate == PsndRate) return;
  113. str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
  114. fprintf(f, "Sound Quality = %i %s", PsndRate, str);
  115. break;
  116. case MA_OPT_REGION:
  117. if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
  118. defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
  119. strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
  120. fprintf(f, "Region = %s", mystrip(str24));
  121. break;
  122. case MA_OPT_CONFIRM_STATES:
  123. if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
  124. switch ((currentConfig.EmuOpt >> 9) & 5) {
  125. default: str = "OFF"; break;
  126. case 1: str = "writes"; break;
  127. case 4: str = "loads"; break;
  128. case 5: str = "both"; break;
  129. }
  130. fprintf(f, "Confirm savestate = %s", str);
  131. break;
  132. case MA_OPT_CPU_CLOCKS:
  133. if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
  134. #ifdef __GP2X__
  135. fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
  136. #elif defined(PSP)
  137. fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
  138. #endif
  139. break;
  140. case MA_OPT2_GAMMA:
  141. if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
  142. fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
  143. break;
  144. case MA_OPT2_SQUIDGEHACK:
  145. if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
  146. fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
  147. break;
  148. case MA_CDOPT_READAHEAD:
  149. if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
  150. sprintf(str24, "%i", PicoCDBuffers * 2);
  151. fprintf(f, "ReadAhead buffer = %s", str24);
  152. break;
  153. /* PSP */
  154. case MA_OPT3_SCALE:
  155. if (no_def && defaultConfig.scale == currentConfig.scale) return;
  156. fprintf(f, "Scale factor = %.2f", currentConfig.scale);
  157. break;
  158. case MA_OPT3_HSCALE32:
  159. if (no_def && defaultConfig.hscale32 == currentConfig.hscale32) return;
  160. fprintf(f, "Hor. scale (for low res. games) = %.2f", currentConfig.hscale32);
  161. break;
  162. case MA_OPT3_HSCALE40:
  163. if (no_def && defaultConfig.hscale40 == currentConfig.hscale40) return;
  164. fprintf(f, "Hor. scale (for hi res. games) = %.2f", currentConfig.hscale40);
  165. break;
  166. case MA_OPT3_FILTERING:
  167. if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
  168. fprintf(f, "Bilinear filtering = %i", currentConfig.scaling);
  169. break;
  170. case MA_OPT3_GAMMAA:
  171. if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
  172. fprintf(f, "Gamma adjustment = %i", currentConfig.gamma);
  173. break;
  174. case MA_OPT3_BLACKLVL:
  175. if (no_def && defaultConfig.gamma2 == currentConfig.gamma2) return;
  176. fprintf(f, "Black level = %i", currentConfig.gamma2);
  177. break;
  178. case MA_OPT3_VSYNC:
  179. if (no_def && (defaultConfig.EmuOpt&0x12000) == (currentConfig.gamma2&0x12000)) return;
  180. strcpy(str24, "never");
  181. if (currentConfig.EmuOpt & 0x2000)
  182. strcpy(str24, (currentConfig.EmuOpt & 0x10000) ? "sometimes" : "always");
  183. fprintf(f, "Wait for vsync = %s", str24);
  184. break;
  185. default:
  186. lprintf("unhandled custom_write: %i\n", me->id);
  187. return;
  188. }
  189. fprintf(f, NL);
  190. }
  191. #if PLAT_HAVE_JOY
  192. static const char *joyKeyNames[32] =
  193. {
  194. "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4",
  195. "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12",
  196. "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20",
  197. "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28"
  198. };
  199. #endif
  200. static void keys_write(FILE *fn, const char *bind_str, const int binds[32],
  201. const int def_binds[32], const char * const names[32], int key_count, int no_defaults)
  202. {
  203. int t, i;
  204. char act[48], name[32];
  205. for (t = 0; t < key_count; t++)
  206. {
  207. act[0] = act[31] = 0;
  208. if (no_defaults && binds[t] == def_binds[t])
  209. continue;
  210. #ifdef __GP2X__
  211. if (strcmp(names[t], "SELECT") == 0) continue;
  212. #endif
  213. if (t >= 32 || names[t] == NULL || strcmp(names[t], "???") == 0) {
  214. if ((t >= '0' && t <= '9') || (t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z'))
  215. sprintf(name, "%c", t);
  216. else
  217. sprintf(name, "\\x%02x", t);
  218. }
  219. else
  220. strcpy(name, names[t]);
  221. if (binds[t] == 0 && def_binds[t] != 0) {
  222. fprintf(fn, "%s %s =" NL, bind_str, name); // no binds
  223. continue;
  224. }
  225. for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
  226. if (me_ctrl_actions[i].mask & binds[t]) {
  227. strncpy(act, me_ctrl_actions[i].name, 31);
  228. fprintf(fn, "%s %s = player%i %s" NL, bind_str, name,
  229. ((binds[t]>>16)&1)+1, mystrip(act));
  230. }
  231. }
  232. for (i = 0; emuctrl_actions[i].name != NULL; i++) {
  233. if (emuctrl_actions[i].mask & binds[t]) {
  234. strncpy(act, emuctrl_actions[i].name, 31);
  235. fprintf(fn, "%s %s = %s" NL, bind_str, name, mystrip(act));
  236. }
  237. }
  238. }
  239. }
  240. static int default_var(const menu_entry *me)
  241. {
  242. switch (me->id)
  243. {
  244. case MA_OPT_ACC_TIMING:
  245. case MA_OPT_ACC_SPRITES:
  246. case MA_OPT_ARM940_SOUND:
  247. case MA_OPT_6BUTTON_PAD:
  248. case MA_OPT2_ENABLE_Z80:
  249. case MA_OPT2_ENABLE_YM2612:
  250. case MA_OPT2_ENABLE_SN76496:
  251. case MA_OPT2_SVP_DYNAREC:
  252. case MA_CDOPT_CDDA:
  253. case MA_CDOPT_PCM:
  254. case MA_CDOPT_SAVERAM:
  255. case MA_CDOPT_SCALEROT_CHIP:
  256. case MA_CDOPT_BETTER_SYNC:
  257. return defaultConfig.s_PicoOpt;
  258. case MA_OPT_SHOW_FPS:
  259. case MA_OPT_ENABLE_SOUND:
  260. case MA_OPT_SRAM_STATES:
  261. case MA_OPT2_A_SN_GAMMA:
  262. case MA_OPT2_VSYNC:
  263. case MA_OPT2_GZIP_STATES:
  264. case MA_OPT2_NO_LAST_ROM:
  265. case MA_OPT2_RAMTIMINGS:
  266. case MA_CDOPT_LEDS:
  267. return defaultConfig.EmuOpt;
  268. case MA_CTRL_TURBO_RATE: return defaultConfig.turbo_rate;
  269. case MA_OPT_SCALING: return defaultConfig.scaling;
  270. case MA_OPT_ROTATION: return defaultConfig.rotation;
  271. case MA_OPT_SAVE_SLOT:
  272. default:
  273. return 0;
  274. }
  275. }
  276. int config_writesect(const char *fname, const char *section)
  277. {
  278. FILE *fo = NULL, *fn = NULL; // old and new
  279. int no_defaults = 0; // avoid saving defaults
  280. menu_entry *me;
  281. int t, i, tlen, ret;
  282. char line[128], *tmp;
  283. if (section != NULL)
  284. {
  285. no_defaults = 1;
  286. fo = fopen(fname, "r");
  287. if (fo == NULL) {
  288. fn = fopen(fname, "w");
  289. goto write;
  290. }
  291. ret = seek_sect(fo, section);
  292. if (!ret) {
  293. // sect not found, we can simply append
  294. fclose(fo); fo = NULL;
  295. fn = fopen(fname, "a");
  296. goto write;
  297. }
  298. // use 2 files..
  299. fclose(fo);
  300. rename(fname, "tmp.cfg");
  301. fo = fopen("tmp.cfg", "r");
  302. fn = fopen(fname, "w");
  303. if (fo == NULL || fn == NULL) goto write;
  304. // copy everything until sect
  305. tlen = strlen(section);
  306. while (!feof(fo))
  307. {
  308. tmp = fgets(line, sizeof(line), fo);
  309. if (tmp == NULL) break;
  310. if (line[0] == '[' && strncmp(line + 1, section, tlen) == 0 && line[tlen+1] == ']')
  311. break;
  312. fputs(line, fn);
  313. }
  314. // now skip to next sect
  315. while (!feof(fo))
  316. {
  317. tmp = fgets(line, sizeof(line), fo);
  318. if (tmp == NULL) break;
  319. if (line[0] == '[') {
  320. fseek(fo, -strlen(line), SEEK_CUR);
  321. break;
  322. }
  323. }
  324. if (feof(fo))
  325. {
  326. fclose(fo); fo = NULL;
  327. remove("tmp.cfg");
  328. }
  329. }
  330. else
  331. {
  332. fn = fopen(fname, "w");
  333. }
  334. write:
  335. if (fn == NULL) {
  336. if (fo) fclose(fo);
  337. return -1;
  338. }
  339. if (section != NULL)
  340. fprintf(fn, "[%s]" NL, section);
  341. for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]); t++)
  342. {
  343. me = cfg_opts[t];
  344. tlen = *(cfg_opt_counts[t]);
  345. for (i = 0; i < tlen; i++, me++)
  346. {
  347. if (!me->need_to_save) continue;
  348. if ((me->beh != MB_ONOFF && me->beh != MB_RANGE) || me->name == NULL)
  349. custom_write(fn, me, no_defaults);
  350. else if (me->beh == MB_ONOFF) {
  351. if (!no_defaults || ((*(int *)me->var ^ default_var(me)) & me->mask))
  352. fprintf(fn, "%s = %i" NL, me->name, (*(int *)me->var & me->mask) ? 1 : 0);
  353. } else if (me->beh == MB_RANGE) {
  354. if (!no_defaults || (*(int *)me->var ^ default_var(me)))
  355. fprintf(fn, "%s = %i" NL, me->name, *(int *)me->var);
  356. }
  357. }
  358. }
  359. // save key config
  360. keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, PLAT_MAX_KEYS, no_defaults);
  361. #if PLAT_HAVE_JOY
  362. keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 32, 1);
  363. keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 32, 1);
  364. keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 32, 1);
  365. keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 32, 1);
  366. #endif
  367. #ifndef PSP
  368. if (section == NULL)
  369. fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
  370. #endif
  371. fprintf(fn, NL);
  372. if (fo != NULL)
  373. {
  374. // copy whatever is left
  375. while (!feof(fo))
  376. {
  377. tmp = fgets(line, sizeof(line), fo);
  378. if (tmp == NULL) break;
  379. fputs(line, fn);
  380. }
  381. fclose(fo);
  382. remove("tmp.cfg");
  383. }
  384. fclose(fn);
  385. return 0;
  386. }
  387. int config_writelrom(const char *fname)
  388. {
  389. char line[128], *tmp, *optr = NULL;
  390. char *old_data = NULL;
  391. int size;
  392. FILE *f;
  393. if (strlen(loadedRomFName) == 0) return -1;
  394. f = fopen(fname, "r");
  395. if (f != NULL)
  396. {
  397. fseek(f, 0, SEEK_END);
  398. size = ftell(f);
  399. fseek(f, 0, SEEK_SET);
  400. old_data = malloc(size + size/8);
  401. if (old_data != NULL)
  402. {
  403. optr = old_data;
  404. while (!feof(f))
  405. {
  406. tmp = fgets(line, sizeof(line), f);
  407. if (tmp == NULL) break;
  408. mystrip(line);
  409. if (strncasecmp(line, "LastUsedROM", 11) == 0)
  410. continue;
  411. sprintf(optr, "%s", line);
  412. optr += strlen(optr);
  413. }
  414. }
  415. fclose(f);
  416. }
  417. f = fopen(fname, "w");
  418. if (f == NULL) return -1;
  419. if (old_data != NULL) {
  420. fwrite(old_data, 1, optr - old_data, f);
  421. free(old_data);
  422. }
  423. fprintf(f, "LastUsedROM = %s" NL, loadedRomFName);
  424. fclose(f);
  425. return 0;
  426. }
  427. /* --------------------------------------------------------------------------*/
  428. int config_readlrom(const char *fname)
  429. {
  430. char line[128], *tmp;
  431. int i, len, ret = -1;
  432. FILE *f;
  433. f = fopen(fname, "r");
  434. if (f == NULL) return -1;
  435. // seek to the section needed
  436. while (!feof(f))
  437. {
  438. tmp = fgets(line, sizeof(line), f);
  439. if (tmp == NULL) break;
  440. if (strncasecmp(line, "LastUsedROM", 11) != 0) continue;
  441. len = strlen(line);
  442. for (i = 0; i < len; i++)
  443. if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
  444. tmp = strchr(line, '=');
  445. if (tmp == NULL) break;
  446. tmp++;
  447. mystrip(tmp);
  448. len = sizeof(loadedRomFName);
  449. strncpy(loadedRomFName, tmp, len);
  450. loadedRomFName[len-1] = 0;
  451. ret = 0;
  452. break;
  453. }
  454. fclose(f);
  455. return ret;
  456. }
  457. static int custom_read(menu_entry *me, const char *var, const char *val)
  458. {
  459. char *tmp;
  460. int tmpi;
  461. switch (me->id)
  462. {
  463. case MA_OPT_RENDERER:
  464. if (strcasecmp(var, "Renderer") != 0) return 0;
  465. if (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
  466. PicoOpt |= POPT_ALT_RENDERER;
  467. }
  468. else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
  469. PicoOpt &= ~POPT_ALT_RENDERER;
  470. currentConfig.EmuOpt |= 0x80;
  471. }
  472. else if (strcasecmp(val, "8bit accurate") == 0) {
  473. PicoOpt &= ~POPT_ALT_RENDERER;
  474. currentConfig.EmuOpt &= ~0x80;
  475. }
  476. else
  477. return 0;
  478. return 1;
  479. case MA_OPT_SCALING:
  480. #ifdef __GP2X__
  481. if (strcasecmp(var, "Scaling") != 0) return 0;
  482. if (strcasecmp(val, "OFF") == 0) {
  483. currentConfig.scaling = 0;
  484. } else if (strcasecmp(val, "hw horizontal") == 0) {
  485. currentConfig.scaling = 1;
  486. } else if (strcasecmp(val, "hw horiz. + vert.") == 0) {
  487. currentConfig.scaling = 2;
  488. } else if (strcasecmp(val, "sw horizontal") == 0) {
  489. currentConfig.scaling = 3;
  490. } else
  491. return 0;
  492. return 1;
  493. #else
  494. return 0;
  495. #endif
  496. case MA_OPT_FRAMESKIP:
  497. if (strcasecmp(var, "Frameskip") != 0) return 0;
  498. if (strcasecmp(val, "Auto") == 0)
  499. currentConfig.Frameskip = -1;
  500. else currentConfig.Frameskip = atoi(val);
  501. return 1;
  502. case MA_OPT_SOUND_QUALITY:
  503. if (strcasecmp(var, "Sound Quality") != 0) return 0;
  504. PsndRate = strtoul(val, &tmp, 10);
  505. if (PsndRate < 8000 || PsndRate > 44100)
  506. PsndRate = 22050;
  507. while (*tmp == ' ') tmp++;
  508. if (strcasecmp(tmp, "stereo") == 0) {
  509. PicoOpt |= POPT_EN_STEREO;
  510. } else if (strcasecmp(tmp, "mono") == 0) {
  511. PicoOpt &= ~POPT_EN_STEREO;
  512. } else
  513. return 0;
  514. return 1;
  515. case MA_OPT_REGION:
  516. if (strcasecmp(var, "Region") != 0) return 0;
  517. if (strncasecmp(val, "Auto: ", 6) == 0)
  518. {
  519. const char *p = val + 5, *end = val + strlen(val);
  520. int i;
  521. PicoRegionOverride = PicoAutoRgnOrder = 0;
  522. for (i = 0; p < end && i < 3; i++)
  523. {
  524. while (*p == ' ') p++;
  525. if (p[0] == 'J' && p[1] == 'P') {
  526. PicoAutoRgnOrder |= 1 << (i*4);
  527. } else if (p[0] == 'U' && p[1] == 'S') {
  528. PicoAutoRgnOrder |= 4 << (i*4);
  529. } else if (p[0] == 'E' && p[1] == 'U') {
  530. PicoAutoRgnOrder |= 8 << (i*4);
  531. }
  532. while (*p != ' ' && *p != 0) p++;
  533. if (*p == 0) break;
  534. }
  535. }
  536. else if (strcasecmp(val, "Auto") == 0) {
  537. PicoRegionOverride = 0;
  538. } else if (strcasecmp(val, "Japan NTSC") == 0) {
  539. PicoRegionOverride = 1;
  540. } else if (strcasecmp(val, "Japan PAL") == 0) {
  541. PicoRegionOverride = 2;
  542. } else if (strcasecmp(val, "USA") == 0) {
  543. PicoRegionOverride = 4;
  544. } else if (strcasecmp(val, "Europe") == 0) {
  545. PicoRegionOverride = 8;
  546. } else
  547. return 0;
  548. return 1;
  549. case MA_OPT_CONFIRM_STATES:
  550. if (strcasecmp(var, "Confirm savestate") != 0) return 0;
  551. if (strcasecmp(val, "OFF") == 0) {
  552. currentConfig.EmuOpt &= ~(5<<9);
  553. } else if (strcasecmp(val, "writes") == 0) {
  554. currentConfig.EmuOpt &= ~(5<<9);
  555. currentConfig.EmuOpt |= 1<<9;
  556. } else if (strcasecmp(val, "loads") == 0) {
  557. currentConfig.EmuOpt &= ~(5<<9);
  558. currentConfig.EmuOpt |= 4<<9;
  559. } else if (strcasecmp(val, "both") == 0) {
  560. currentConfig.EmuOpt &= ~(5<<9);
  561. currentConfig.EmuOpt |= 5<<9;
  562. } else
  563. return 0;
  564. return 1;
  565. case MA_OPT_CPU_CLOCKS:
  566. #ifdef __GP2X__
  567. if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
  568. #elif defined(PSP)
  569. if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
  570. #endif
  571. currentConfig.CPUclock = atoi(val);
  572. return 1;
  573. case MA_OPT2_GAMMA:
  574. if (strcasecmp(var, "Gamma correction") != 0) return 0;
  575. currentConfig.gamma = (int) (atof(val) * 100.0);
  576. return 1;
  577. case MA_OPT2_SQUIDGEHACK:
  578. if (strcasecmp(var, "Squidgehack") != 0) return 0;
  579. tmpi = atoi(val);
  580. if (tmpi) *(int *)me->var |= me->mask;
  581. else *(int *)me->var &= ~me->mask;
  582. return 1;
  583. case MA_CDOPT_READAHEAD:
  584. if (strcasecmp(var, "ReadAhead buffer") != 0) return 0;
  585. PicoCDBuffers = atoi(val) / 2;
  586. return 1;
  587. /* PSP */
  588. case MA_OPT3_SCALE:
  589. if (strcasecmp(var, "Scale factor") != 0) return 0;
  590. currentConfig.scale = atof(val);
  591. return 1;
  592. case MA_OPT3_HSCALE32:
  593. if (strcasecmp(var, "Hor. scale (for low res. games)") != 0) return 0;
  594. currentConfig.hscale32 = atof(val);
  595. return 1;
  596. case MA_OPT3_HSCALE40:
  597. if (strcasecmp(var, "Hor. scale (for hi res. games)") != 0) return 0;
  598. currentConfig.hscale40 = atof(val);
  599. return 1;
  600. case MA_OPT3_FILTERING:
  601. if (strcasecmp(var, "Bilinear filtering") != 0) return 0;
  602. currentConfig.scaling = atoi(val);
  603. return 1;
  604. case MA_OPT3_GAMMAA:
  605. if (strcasecmp(var, "Gamma adjustment") != 0) return 0;
  606. currentConfig.gamma = atoi(val);
  607. return 1;
  608. case MA_OPT3_BLACKLVL:
  609. if (strcasecmp(var, "Black level") != 0) return 0;
  610. currentConfig.gamma2 = atoi(val);
  611. return 1;
  612. case MA_OPT3_VSYNC:
  613. if (strcasecmp(var, "Wait for vsync") != 0) return 0;
  614. if (strcasecmp(val, "never") == 0) {
  615. currentConfig.EmuOpt &= ~0x12000;
  616. } else if (strcasecmp(val, "sometimes") == 0) {
  617. currentConfig.EmuOpt |= 0x12000;
  618. } else if (strcasecmp(val, "always") == 0) {
  619. currentConfig.EmuOpt &= ~0x12000;
  620. currentConfig.EmuOpt |= 0x02000;
  621. } else
  622. return 0;
  623. return 1;
  624. default:
  625. lprintf("unhandled custom_read: %i\n", me->id);
  626. return 0;
  627. }
  628. }
  629. static unsigned int keys_encountered = 0;
  630. static void keys_parse(const char *var, const char *val, int binds[32],
  631. const char * const names[32], int max_keys)
  632. {
  633. int t, i;
  634. unsigned int player;
  635. for (t = 0; t < 32; t++)
  636. {
  637. if (names[t] && strcmp(names[t], var) == 0) break;
  638. }
  639. if (t == 32)
  640. {
  641. int len = strlen(var);
  642. if (len == 1) t = var[0];
  643. else if (len >= 4 && var[0] == '\\' && var[1] == 'x') {
  644. char *p;
  645. t = (int)strtoul(var + 2, &p, 16);
  646. if (*p != 0) t = max_keys; // parse failed
  647. }
  648. else
  649. t = max_keys; // invalid
  650. }
  651. if (t < 0 || t >= max_keys) {
  652. lprintf("unhandled bind \"%s\"\n", var);
  653. return;
  654. }
  655. if (binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) { // hack
  656. binds[t] = 0;
  657. keys_encountered |= 1<<t;
  658. }
  659. if (val[0] == 0)
  660. return;
  661. if (strncasecmp(val, "player", 6) == 0)
  662. {
  663. player = atoi(val + 6) - 1;
  664. if (player > 1) goto fail;
  665. for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
  666. if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0) {
  667. binds[t] |= me_ctrl_actions[i].mask | (player<<16);
  668. return;
  669. }
  670. }
  671. }
  672. for (i = 0; emuctrl_actions[i].name != NULL; i++) {
  673. if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
  674. binds[t] |= emuctrl_actions[i].mask;
  675. return;
  676. }
  677. }
  678. fail:
  679. lprintf("unhandled action \"%s\"\n", val);
  680. return;
  681. }
  682. #define try_joy_parse(num) { \
  683. if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \
  684. keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames, 32); \
  685. return; \
  686. } \
  687. }
  688. static void parse(const char *var, const char *val)
  689. {
  690. menu_entry *me;
  691. int t, i, tlen, tmp, ret = 0;
  692. if (strcasecmp(var, "LastUsedROM") == 0)
  693. return; /* handled elsewhere */
  694. if (strcasecmp(var, "Sound Volume") == 0) {
  695. currentConfig.volume = atoi(val);
  696. return;
  697. }
  698. // key binds
  699. if (strncasecmp(var, "bind ", 5) == 0) {
  700. keys_parse(var + 5, val, currentConfig.KeyBinds, keyNames, PLAT_MAX_KEYS);
  701. return;
  702. }
  703. #if PLAT_HAVE_JOY
  704. try_joy_parse(0)
  705. try_joy_parse(1)
  706. try_joy_parse(2)
  707. try_joy_parse(3)
  708. #endif
  709. for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]) && ret == 0; t++)
  710. {
  711. me = cfg_opts[t];
  712. tlen = *(cfg_opt_counts[t]);
  713. for (i = 0; i < tlen && ret == 0; i++, me++)
  714. {
  715. if (!me->need_to_save) continue;
  716. if (me->name != NULL) {
  717. if (strcasecmp(var, me->name) != 0) continue; // surely not this one
  718. if (me->beh == MB_ONOFF) {
  719. tmp = atoi(val);
  720. if (tmp) *(int *)me->var |= me->mask;
  721. else *(int *)me->var &= ~me->mask;
  722. return;
  723. } else if (me->beh == MB_RANGE) {
  724. tmp = atoi(val);
  725. if (tmp < me->min) tmp = me->min;
  726. if (tmp > me->max) tmp = me->max;
  727. *(int *)me->var = tmp;
  728. return;
  729. }
  730. }
  731. ret = custom_read(me, var, val);
  732. }
  733. }
  734. if (!ret) lprintf("config_readsect: unhandled var: \"%s\"\n", var);
  735. }
  736. int config_havesect(const char *fname, const char *section)
  737. {
  738. FILE *f;
  739. int ret;
  740. f = fopen(fname, "r");
  741. if (f == NULL) return 0;
  742. ret = seek_sect(f, section);
  743. fclose(f);
  744. return ret;
  745. }
  746. int config_readsect(const char *fname, const char *section)
  747. {
  748. char line[128], *var, *val;
  749. FILE *f;
  750. int ret;
  751. f = fopen(fname, "r");
  752. if (f == NULL) return -1;
  753. if (section != NULL)
  754. {
  755. ret = seek_sect(f, section);
  756. if (!ret) {
  757. lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
  758. fclose(f);
  759. return -1;
  760. }
  761. }
  762. keys_encountered = 0;
  763. while (!feof(f))
  764. {
  765. ret = config_get_var_val(f, line, sizeof(line), &var, &val);
  766. if (ret == 0) break;
  767. if (ret == -1) continue;
  768. parse(var, val);
  769. }
  770. fclose(f);
  771. return 0;
  772. }
  773. #endif // _MSC_VER
  774. static char *mystrip(char *str)
  775. {
  776. int i, len;
  777. len = strlen(str);
  778. for (i = 0; i < len; i++)
  779. if (str[i] != ' ') break;
  780. if (i > 0) memmove(str, str + i, len - i + 1);
  781. len = strlen(str);
  782. for (i = len - 1; i >= 0; i--)
  783. if (str[i] != ' ') break;
  784. str[i+1] = 0;
  785. return str;
  786. }
  787. /* returns:
  788. * 0 - EOF, end
  789. * 1 - parsed ok
  790. * -1 - failed to parse line
  791. */
  792. int config_get_var_val(void *file, char *line, int lsize, char **rvar, char **rval)
  793. {
  794. char *var, *val, *tmp;
  795. FILE *f = file;
  796. int len, i;
  797. tmp = fgets(line, lsize, f);
  798. if (tmp == NULL) return 0;
  799. if (line[0] == '[') return 0; // other section
  800. // strip comments, linefeed, spaces..
  801. len = strlen(line);
  802. for (i = 0; i < len; i++)
  803. if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
  804. mystrip(line);
  805. len = strlen(line);
  806. if (len <= 0) return -1;;
  807. // get var and val
  808. for (i = 0; i < len; i++)
  809. if (line[i] == '=') break;
  810. if (i >= len || strchr(&line[i+1], '=') != NULL) {
  811. lprintf("config_readsect: can't parse: %s\n", line);
  812. return -1;
  813. }
  814. line[i] = 0;
  815. var = line;
  816. val = &line[i+1];
  817. mystrip(var);
  818. mystrip(val);
  819. #ifndef _MSC_VER
  820. if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
  821. lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);
  822. return -1;;
  823. }
  824. #endif
  825. *rvar = var;
  826. *rval = val;
  827. return 1;
  828. }