1 /*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6
7 // all windows-specific command line and config file parsing/saving/loading
8 // this stuff was moved out of wsnes.cpp, to keep things a little tidier
9
10 // note:
11 // if you want to force all users of a new version to have a
12 // particular setting reset to its given default,
13 // change the name string of that setting (in WinRegisterConfigItems)
14 // to something a little different...
15 // but if it's not in a windows-specific category, make sure you change its name elsewhere too
16
17 #include "../port.h"
18 #include "../snes9x.h"
19 #include "wsnes9x.h"
20 #include "wlanguage.h"
21 #include "../display.h"
22 #include "../conffile.h"
23 #include "../spc7110.h"
24 #include "../gfx.h"
25 #include "../snapshot.h"
26 #ifdef NETPLAY_SUPPORT
27 #include "../netplay.h"
28 extern SNPServer NPServer;
29 #endif
30 #include <assert.h>
31
32 static void WinDeleteRegistryEntries ();
33 void WinSetDefaultValues ();
34 void WinDeleteRecentGamesList ();
35
36 HANDLE configMutex = NULL;
37
38 extern TCHAR multiRomA[MAX_PATH]; // lazy, should put in sGUI and add init to {0} somewhere
39 extern TCHAR multiRomB[MAX_PATH];
40
S9xParseArg(char ** argv,int & i,int argc)41 void S9xParseArg (char **argv, int &i, int argc)
42 {
43 if (strcasecmp (argv [i], "-restore") == 0)
44 {
45 WinDeleteRegistryEntries ();
46 WinSetDefaultValues ();
47 }
48 else if (strcasecmp (argv[i], "-hidemenu") == 0)
49 {
50 GUI.HideMenu = true;
51 }
52 else if (strcasecmp (argv[i], "-fullscreen") == 0)
53 {
54 GUI.FullScreen = true;
55 }
56 else if (!strcasecmp(argv[i], "-cartb"))
57 {
58 Settings.Multi = TRUE; // only used to signal winmain
59 if (i + 1 < argc)
60 {
61 lstrcpyn(multiRomB, _tFromChar(argv[++i]), MAX_PATH);
62 }
63 }
64 }
65
WinSetDefaultValues()66 void WinSetDefaultValues ()
67 {
68 // TODO: delete the parts that are already covered by the default values in WinRegisterConfigItems
69
70 char temp[4];
71 strcpy(temp,"C:\\");
72
73 GUI.ControllerOption = SNES_JOYPAD;
74 GUI.ValidControllerOptions = 0xFFFF;
75 GUI.IgnoreNextMouseMove = false;
76
77 GUI.DoubleBuffered = false;
78 GUI.FullScreen = false;
79 GUI.Stretch = false;
80 GUI.FlipCounter = 0;
81 GUI.NumFlipFrames = 1;
82 Settings.BilinearFilter = false;
83 GUI.LockDirectories = false;
84 GUI.window_maximized = false;
85 GUI.EmulatedFullscreen = false;
86
87 WinDeleteRecentGamesList ();
88
89 GUI.SoundChannelEnable=255;
90
91 // Tracing options
92 Settings.TraceDMA = false;
93 Settings.TraceHDMA = false;
94 Settings.TraceVRAM = false;
95 Settings.TraceUnknownRegisters = false;
96 Settings.TraceDSP = false;
97
98 // ROM timing options (see also H_Max above)
99 Settings.PAL = false;
100 Settings.FrameTimePAL = 20000;
101 Settings.FrameTimeNTSC = 16667;
102 Settings.FrameTime = 16667;
103
104 // CPU options
105 Settings.Paused = false;
106
107 Settings.SupportHiRes = true;
108
109 #ifdef NETPLAY_SUPPORT
110 Settings.Port = 1996;
111 NetPlay.MaxFrameSkip = 10;
112 NetPlay.MaxBehindFrameCount = 10;
113 NPServer.SyncByReset = true;
114 NPServer.SendROMImageOnConnect = false;
115 #endif
116
117 Settings.TakeScreenshot=false;
118
119 GUI.Language=0;
120 GUI.AllowSoundSync = true;
121 }
122
123
try_save(const char * fname,ConfigFile & conf)124 static bool try_save(const char *fname, ConfigFile &conf){
125 STREAM fp;
126 if((fp=OPEN_STREAM(fname, "w"))!=NULL){
127 fprintf(stdout, "Saving config file %s\n", fname);
128 CLOSE_STREAM(fp);
129 conf.SaveTo(fname);
130 return true;
131 }
132 return false;
133 }
134
S9xSaveConfigFile(ConfigFile & conf)135 static bool S9xSaveConfigFile(ConfigFile &conf){
136
137 configMutex = CreateMutex(NULL, FALSE, TEXT("Snes9xConfigMutex"));
138 int times = 0;
139 DWORD waitVal = WAIT_TIMEOUT;
140 while(waitVal == WAIT_TIMEOUT && ++times <= 150) // wait at most 15 seconds
141 waitVal = WaitForSingleObject(configMutex, 100);
142
143 // save over the .conf file if it already exists, otherwise save over the .cfg file
144 std::string fname;
145 fname=S9xGetDirectory(DEFAULT_DIR);
146 fname+=SLASH_STR S9X_CONF_FILE_NAME;
147
148 // ensure previous config file is not lost if we crash while writing the new one
149 std::string ftemp;
150 {
151 CopyFileA(fname.c_str(), (fname + ".autobak").c_str(), FALSE);
152
153 ftemp=S9xGetDirectory(DEFAULT_DIR);
154 ftemp+=SLASH_STR "config_error";
155 FILE* tempfile = fopen(ftemp.c_str(), "wb");
156 if(tempfile) fclose(tempfile);
157 }
158
159 bool ret = try_save(fname.c_str(), conf);
160
161 remove(ftemp.c_str());
162 remove((fname + ".autobak").c_str());
163
164 ReleaseMutex(configMutex);
165 CloseHandle(configMutex);
166
167 return ret;
168 }
169
170
WinDeleteRegistryEntries()171 static void WinDeleteRegistryEntries ()
172 {
173 // WinDeleteRegKey (HKEY_CURRENT_USER, S9X_REG_KEY_BASE);
174 }
175
SkipSpaces(char * p)176 static inline char *SkipSpaces (char *p)
177 {
178 while (*p && isspace (*p))
179 p++;
180
181 return (p);
182 }
183
WinParseCommandLineAndLoadConfigFile(TCHAR * line)184 const TCHAR* WinParseCommandLineAndLoadConfigFile (TCHAR *line)
185 {
186 // Break the command line up into an array of string pointers, each pointer
187 // points at a separate word or character sequence enclosed in quotes.
188
189 int count = 0;
190 static TCHAR return_filename[MAX_PATH];
191
192 #ifdef UNICODE
193 // split params into argv
194 TCHAR **params = CommandLineToArgvW(line, &count);
195
196 // convert all parameters to utf8
197 char **parameters = new char*[count];
198 for(int i = 0; i < count; i++) {
199 int requiredChars = WideCharToMultiByte(CP_UTF8, 0, params[i], -1, NULL, 0, NULL, NULL);
200 parameters[i] = new char[requiredChars];
201 WideCharToMultiByte(CP_UTF8, 0, params[i], -1, parameters[i], requiredChars, NULL, NULL);
202 }
203 LocalFree(params);
204 #else
205 #define MAX_PARAMETERS 100
206 char *p = line;
207 char *parameters[MAX_PARAMETERS];
208 while (count < MAX_PARAMETERS && *p)
209 {
210 p = SkipSpaces (p);
211 if (*p == '"')
212 {
213 p++;
214 parameters [count++] = p;
215 while (*p && *p != '"')
216 p++;
217 *p++ = 0;
218 }
219 else
220 if (*p == '\'')
221 {
222 p++;
223 parameters [count++] = p;
224 while (*p && *p != '\'')
225 p++;
226 *p++ = 0;
227 }
228 else
229 {
230 parameters [count++] = p;
231 while (*p && !isspace (*p))
232 p++;
233 if (!*p)
234 break;
235 *p++ = 0;
236 }
237 }
238
239 #endif
240 configMutex = CreateMutex(NULL, FALSE, TEXT("Snes9xConfigMutex"));
241 int times = 0;
242 DWORD waitVal = WAIT_TIMEOUT;
243 while(waitVal == WAIT_TIMEOUT && ++times <= 150) // wait at most 15 seconds
244 waitVal = WaitForSingleObject(configMutex, 100);
245
246 // ensure previous config file is not lost if we crashed while writing a new one
247 {
248 std::string ftemp;
249 ftemp=S9xGetDirectory(DEFAULT_DIR);
250 ftemp+=SLASH_STR "config_error";
251 FILE* tempfile = fopen(ftemp.c_str(), "rb");
252 if(tempfile)
253 {
254 fclose(tempfile);
255
256 std::string fname;
257 for(int i=0; i<2; i++)
258 {
259 fname=S9xGetDirectory(DEFAULT_DIR);
260 if(i == 0) fname+=SLASH_STR "snes9x.conf";
261 else if(i == 1) fname+=SLASH_STR "snes9x.cfg";
262
263 tempfile = fopen((fname + ".autobak").c_str(), "rb");
264 if(tempfile)
265 {
266 fclose(tempfile);
267 MoveFileExA((fname + ".autobak").c_str(), fname.c_str(), MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH);
268 }
269 }
270 remove(ftemp.c_str());
271 }
272 }
273
274 S9xLoadConfigFiles(parameters, count);
275
276 ReleaseMutex(configMutex);
277 CloseHandle(configMutex);
278
279 const char* rf = S9xParseArgs (parameters, count);
280
281 if(rf) // save rom_filename as TCHAR if available
282 lstrcpy(return_filename, _tFromChar(rf));
283
284 #ifdef UNICODE
285 // free parameters
286 for(int i = 0; i < count; i++) {
287 delete [] parameters[i];
288 }
289 delete [] parameters;
290 #endif
291
292 return rf ? return_filename : NULL;
293 }
294
295 #define S(x) GAMEDEVICE_VK_##x
296 #define SO(x) GAMEDEVICE_VK_OEM_##x
297 static const char* keyToString [256] =
298 {
299 "Unassigned","LMB","RMB","Break","MMB","XMB1","XMB2","0x07",
300 S(BACK),S(TAB),"0x0A","0x0B",S(CLEAR),S(RETURN),"0x0E","0x0F",
301 S(SHIFT),S(CONTROL),S(MENU),S(PAUSE),S(CAPITAL),"Kana","0x16","Junja",
302 "Final","Kanji","0x1A",S(ESCAPE),"Convert","NonConvert","Accept","ModeChange",
303 S(SPACE),S(PRIOR),S(NEXT),S(END),S(HOME),S(LEFT),S(UP),S(RIGHT),
304 S(DOWN),S(SELECT),S(PRINT),S(EXECUTE),S(SNAPSHOT),S(INSERT),S(DELETE),S(HELP),
305 "0","1","2","3","4","5","6","7",
306 "8","9","0x3A","0x3B","0x3C","0x3D","0x3E","0x3F",
307 "0x40","A","B","C","D","E","F","G",
308 "H","I","J","K","L","M","N","O",
309 "P","Q","R","S","T","U","V","W",
310 "X","Y","Z",S(LWIN),S(RWIN),S(APPS),"0x5E","Sleep",
311 "Pad0","Pad1","Pad2","Pad3","Pad4","Pad5","Pad6","Pad7",
312 "Pad8","Pad9",S(MULTIPLY),S(ADD),S(SEPARATOR),S(SUBTRACT),S(DECIMAL),S(DIVIDE),
313 S(F1),S(F2),S(F3),S(F4),S(F5),S(F6),S(F7),S(F8),
314 S(F9),S(F10),S(F11),S(F12),"F13","F14","F15","F16",
315 "F17","F18","F19","F20","F21","F22","F23","F24",
316 "0x88","0x89","0x8A","0x8B","0x8C","0x8D","0x8E","0x8F",
317 S(NUMLOCK),S(SCROLL),"PadEqual","Masshou","Touroku","Loya","Roya","0x97",
318 "0x98","0x99","0x9A","0x9B","0x9C","0x9D","0x9E","0x9F",
319 S(LSHIFT),S(RSHIFT),S(LCONTROL),S(RCONTROL),S(LMENU),S(RMENU),"BrowserBack","BrowserForward",
320 "BrowserRefresh","BrowserStop","BrowserSearch","BrowserFavorites","BrowserHome","VolumeMute","VolumeDown","VolumeUp",
321 "MediaNextTrack","MediaPrevTrack","MediaStop","MediaPlayPause","LaunchMail","MediaSelect","LaunchApp1","LaunchApp2",
322 "0xB8","0xB9",";","+",",",SO(MINUS),".",SO(2),
323 SO(3),"0xC1","0xC2","0xC3","0xC4","0xC5","0xC6","0xC7",
324 "0xC8","0xC9","0xCA","0xCB","0xCC","0xCD","0xCE","0xCF",
325 "0xD0","0xD1","0xD2","0xD3","0xD4","0xD5","0xD6","0xD7",
326 "0xD8","0xD9","0xDA",SO(4),"Backslash",SO(6),"'","OEM8",
327 "0xE0","AX","<>","ICOHelp","ICO00","Process","ICOClear","Packet",
328 "0xE8","Reset","Jump","PA1_2","PA2","PA3","WSCTRL","CUSEL",
329 "Attention2","Finish","Copy","Auto","ENLW","Backtab","Attention","CRSEL",
330 "EXSEL","EREOF","Play","Zoom","NoName","PA1","Clear2","0xFF"
331 };
332 static const char* keyToAlternateString [256] =
333 {
334 "none","LeftClick","RightClick","Cancel","MiddleClick","","","","Back","","","","","Return","","",
335 "","","Menu","","","","","","","","","Escape","","","","",
336 "","PageUp","PageDown","","","","","","","","PrintScreen","","","Ins","Del","",
337 "","","","","","","","","","","","","","","","",
338 "","","","","","","","","","","","","","","","",
339 "","","","","","","","","","","","","","","","",
340 "","","","","","","","","","","","","","","","",
341 "","","","","","","","","","","","","","","","",
342 "","","","","","","","","","","","","","","","",
343 "NumLock","ScrollLock","","","","","","","","","","","","","","",
344 "","","","","","","","","","","","","","","","",
345 "","","","","","","","","","",SO(1),SO(PLUS),SO(COMMA),"Minus",SO(PERIOD),"?",
346 "","","","","","","","","","","","","","","","",
347 "","","","oem_pa1","","","","","","","","LBracket","|","RBracket",SO(7),"",
348 "ATTN2","","","","","","","","","","","","","","ATTN","",
349 "","","","","","","","","","","","","","","",""
350 };
351 #undef S
352 #undef SO
353
354
355
356
357 // We will maintain our own list of items to put in the config file,
358 // so that each config item has only one point of change across both saving and loading
359 // and to allow us to manage custom types of config items, such as virtual key codes represented as strings
360
361 enum ConfigItemType {
362 CIT_BOOL, CIT_INT, CIT_UINT, CIT_STRING, CIT_INVBOOL, CIT_BOOLONOFF, CIT_INVBOOLONOFF, CIT_VKEY, CIT_VKEYMOD
363 };
364
365 struct ConfigItem
366 {
367 const char* name;
368 void* addr;
369 int size;
370 void* def;
371 const char* comment;
372 ConfigItemType type;
373
ConfigItemConfigItem374 ConfigItem(const char* nname, void* naddr, int nsize, void* ndef, const char* ncomment, ConfigItemType ntype) {
375 addr = naddr; name = nname; size = nsize; def = ndef; comment = ncomment; type = ntype;
376 }
377
GetConfigItem378 void Get(ConfigFile& conf) {
379 switch(type)
380 {
381 case CIT_BOOL:
382 case CIT_BOOLONOFF:
383 if(size == 1) *(uint8 *)addr = (uint8) conf.GetBool(name, def!=0);
384 if(size == 2) *(uint16*)addr = (uint16)conf.GetBool(name, def!=0);
385 if(size == 4) *(uint32*)addr = (uint32)conf.GetBool(name, def!=0);
386 if(size == 8) *(uint64*)addr = (uint64)conf.GetBool(name, def!=0);
387 break;
388 case CIT_INT:
389 if(size == 1) *(uint8 *)addr = (uint8) conf.GetInt(name, PtrToInt(def));
390 if(size == 2) *(uint16*)addr = (uint16)conf.GetInt(name, PtrToInt(def));
391 if(size == 4) *(uint32*)addr = (uint32)conf.GetInt(name, PtrToInt(def));
392 if(size == 8) *(uint64*)addr = (uint64)conf.GetInt(name, PtrToInt(def));
393 break;
394 case CIT_UINT:
395 if(size == 1) *(uint8 *)addr = (uint8) conf.GetUInt(name, PtrToUint(def));
396 if(size == 2) *(uint16*)addr = (uint16)conf.GetUInt(name, PtrToUint(def));
397 if(size == 4) *(uint32*)addr = (uint32)conf.GetUInt(name, PtrToUint(def));
398 if(size == 8) *(uint64*)addr = (uint64)conf.GetUInt(name, PtrToUint(def));
399 break;
400 case CIT_STRING:
401 lstrcpyn((TCHAR*)addr, _tFromChar(conf.GetString(name, reinterpret_cast<const char*>(def))), size-1);
402 ((TCHAR*)addr)[size-1] = TEXT('\0');
403 break;
404 case CIT_INVBOOL:
405 case CIT_INVBOOLONOFF:
406 if(size == 1) *(uint8 *)addr = (uint8) !conf.GetBool(name, def!=0);
407 if(size == 2) *(uint16*)addr = (uint16)!conf.GetBool(name, def!=0);
408 if(size == 4) *(uint32*)addr = (uint32)!conf.GetBool(name, def!=0);
409 if(size == 8) *(uint64*)addr = (uint64)!conf.GetBool(name, def!=0);
410 break;
411 case CIT_VKEY:
412 {
413 uint16 keyNum = (uint16)conf.GetUInt(name, PtrToUint(def));
414 const char* keyStr = conf.GetString(name);
415 if(keyStr)
416 {
417 for(int i=0;i<512;i++)
418 {
419 if(i<256) // keys
420 {
421 if(!strcasecmp(keyStr,keyToString[i]) ||
422 (*keyToAlternateString[i] && !strcasecmp(keyStr,keyToAlternateString[i])))
423 {
424 keyNum = i;
425 break;
426 }
427 }
428 else // joystick:
429 {
430 char temp [128];
431 extern void TranslateKey(WORD keyz,char *out);
432 TranslateKey(0x8000|(i-256),temp);
433 if(strlen(keyStr)>3 && !strcasecmp(keyStr+3,temp+3))
434 {
435 for(int j = 0 ; j < 16 ; j++)
436 {
437 if(keyStr[2]-'0' == j || keyStr[2]-'a' == j-10)
438 {
439 keyNum = 0x8000|(i-256)|(j<<8);
440 i = 512;
441 break;
442 }
443 }
444 }
445 }
446 }
447 }
448 if(size == 1) *(uint8 *)addr = (uint8) keyNum;
449 if(size == 2) *(uint16*)addr = (uint16)keyNum;
450 if(size == 4) *(uint32*)addr = (uint32)keyNum;
451 if(size == 8) *(uint64*)addr = (uint64)keyNum;
452 }
453 break;
454 case CIT_VKEYMOD:
455 {
456 uint16 modNum = 0;
457 const char* modStr = conf.GetString(name);
458 if(modStr) {
459 if(strstr(modStr, "ft") || strstr(modStr, "FT")) modNum |= CUSTKEY_SHIFT_MASK;
460 if(strstr(modStr, "tr") || strstr(modStr, "TR")) modNum |= CUSTKEY_CTRL_MASK;
461 if(strstr(modStr, "lt") || strstr(modStr, "LT")) modNum |= CUSTKEY_ALT_MASK;
462 }
463 if(!modNum && (!modStr || strcasecmp(modStr, "none")))
464 modNum = conf.GetUInt(name, PtrToUint(def));
465 if(size == 1) *(uint8 *)addr = (uint8) modNum;
466 if(size == 2) *(uint16*)addr = (uint16)modNum;
467 if(size == 4) *(uint32*)addr = (uint32)modNum;
468 if(size == 8) *(uint64*)addr = (uint64)modNum;
469 }
470 break;
471 }
472
473 // if it had a comment, override our own with it
474 const char* newComment = conf.GetComment(name);
475 if(newComment && *newComment)
476 comment = newComment;
477 }
478
SetConfigItem479 void Set(ConfigFile& conf) {
480 switch(type)
481 {
482 case CIT_BOOL:
483 if(size == 1) conf.SetBool(name, 0!=(*(uint8 *)addr), "TRUE","FALSE", comment);
484 if(size == 2) conf.SetBool(name, 0!=(*(uint16*)addr), "TRUE","FALSE", comment);
485 if(size == 4) conf.SetBool(name, 0!=(*(uint32*)addr), "TRUE","FALSE", comment);
486 if(size == 8) conf.SetBool(name, 0!=(*(uint64*)addr), "TRUE","FALSE", comment);
487 break;
488 case CIT_BOOLONOFF:
489 if(size == 1) conf.SetBool(name, 0!=(*(uint8 *)addr), "ON","OFF", comment);
490 if(size == 2) conf.SetBool(name, 0!=(*(uint16*)addr), "ON","OFF", comment);
491 if(size == 4) conf.SetBool(name, 0!=(*(uint32*)addr), "ON","OFF", comment);
492 if(size == 8) conf.SetBool(name, 0!=(*(uint64*)addr), "ON","OFF", comment);
493 break;
494 case CIT_INT:
495 if(size == 1) conf.SetInt(name, (int32)(*(uint8 *)addr), comment);
496 if(size == 2) conf.SetInt(name, (int32)(*(uint16*)addr), comment);
497 if(size == 4) conf.SetInt(name, (int32)(*(uint32*)addr), comment);
498 if(size == 8) conf.SetInt(name, (int32)(*(uint64*)addr), comment);
499 break;
500 case CIT_UINT:
501 if(size == 1) conf.SetUInt(name, (uint32)(*(uint8 *)addr), 10, comment);
502 if(size == 2) conf.SetUInt(name, (uint32)(*(uint16*)addr), 10, comment);
503 if(size == 4) conf.SetUInt(name, (uint32)(*(uint32*)addr), 10, comment);
504 if(size == 8) conf.SetUInt(name, (uint32)(*(uint64*)addr), 10, comment);
505 break;
506 case CIT_STRING:
507 if((TCHAR*)addr)
508 conf.SetString(name, std::string(_tToChar((TCHAR*)addr)), comment);
509 break;
510 case CIT_INVBOOL:
511 if(size == 1) conf.SetBool(name, 0==(*(uint8 *)addr), "TRUE","FALSE", comment);
512 if(size == 2) conf.SetBool(name, 0==(*(uint16*)addr), "TRUE","FALSE", comment);
513 if(size == 4) conf.SetBool(name, 0==(*(uint32*)addr), "TRUE","FALSE", comment);
514 if(size == 8) conf.SetBool(name, 0==(*(uint64*)addr), "TRUE","FALSE", comment);
515 break;
516 case CIT_INVBOOLONOFF:
517 if(size == 1) conf.SetBool(name, 0==(*(uint8 *)addr), "ON","OFF", comment);
518 if(size == 2) conf.SetBool(name, 0==(*(uint16*)addr), "ON","OFF", comment);
519 if(size == 4) conf.SetBool(name, 0==(*(uint32*)addr), "ON","OFF", comment);
520 if(size == 8) conf.SetBool(name, 0==(*(uint64*)addr), "ON","OFF", comment);
521 break;
522 case CIT_VKEY:
523 {
524 uint16 keyNum = 0;
525 if(size == 1) keyNum = (uint8)(*(uint8 *)addr);
526 if(size == 2) keyNum = (uint16)(*(uint16*)addr);
527 if(size == 4) keyNum = (uint16)(*(uint32*)addr);
528 if(size == 8) keyNum = (uint16)(*(uint64*)addr);
529 if(keyNum < 256) conf.SetString(name, keyToString[keyNum], comment);
530 else if(keyNum & 0x8000) {
531 char temp [128];
532 extern void TranslateKey(WORD keyz,char *out);
533 TranslateKey(keyNum,temp);
534 conf.SetString(name, temp, comment);
535 }
536 else conf.SetUInt(name, keyNum, 16, comment);
537 }
538 break;
539 case CIT_VKEYMOD:
540 {
541 uint16 modNum = 0;
542 if(size == 1) modNum = (uint8)(*(uint8 *)addr);
543 if(size == 2) modNum = (uint16)(*(uint16*)addr);
544 if(size == 4) modNum = (uint16)(*(uint32*)addr);
545 if(size == 8) modNum = (uint16)(*(uint64*)addr);
546 std::string modStr;
547 if(modNum & CUSTKEY_CTRL_MASK) modStr += "Ctrl ";
548 if(modNum & CUSTKEY_ALT_MASK) modStr += "Alt ";
549 if(modNum & CUSTKEY_SHIFT_MASK) modStr += "Shift ";
550 if(!(modNum & (CUSTKEY_CTRL_MASK|CUSTKEY_ALT_MASK|CUSTKEY_SHIFT_MASK))) modStr = "none";
551 else modStr.erase(modStr.length()-1);
552 conf.SetString(name, modStr, comment);
553 }
554 break;
555 }
556 }
557 };
558
559 std::vector<ConfigItem> configItems;
560 // var must be a persistent variable. In the case of strings, it must point to a writeable character array.
561 #define AddItemC(name, var, def, comment, type) configItems.push_back(ConfigItem((const char*)(CATEGORY "::" name), (void*)(&var), sizeof(var), (void*)(pint)def, (const char*)comment, (ConfigItemType)type))
562 #define AddItem(name, var, def, type) AddItemC(name,var,def,"",type)
563 #define AddUInt(name, var, def) AddItem(name,var,def,CIT_UINT)
564 #define AddInt(name, var, def) AddItem(name,var,def,CIT_INT)
565 #define AddBool(name, var, def) AddItem(name,var,def,CIT_BOOL)
566 #define AddBool2(name, var, def) AddItem(name,var,def,CIT_BOOLONOFF)
567 #define AddInvBool(name, var, def) AddItem(name,var,def,CIT_INVBOOL)
568 #define AddInvBool2(name, var, def) AddItem(name,var,def,CIT_INVBOOLONOFF)
569 #define AddVKey(name, var, def) AddItem(name,var,def,CIT_VKEY)
570 #define AddVKMod(name, var, def) AddItem(name,var,def,CIT_VKEYMOD)
571 #define AddUIntC(name, var, def, comment) AddItemC(name,var,def,comment,CIT_UINT)
572 #define AddIntC(name, var, def, comment) AddItemC(name,var,def,comment,CIT_INT)
573 #define AddBoolC(name, var, def, comment) AddItemC(name,var,def,comment,CIT_BOOL)
574 #define AddBool2C(name, var, def, comment) AddItemC(name,var,def,comment,CIT_BOOLONOFF)
575 #define AddInvBoolC(name, var, def, comment) AddItemC(name,var,def,comment,CIT_INVBOOL)
576 #define AddInvBool2C(name, var, def, comment) AddItemC(name,var,def,comment,CIT_INVBOOLONOFF)
577 #define AddStringC(name, var, buflen, def, comment) configItems.push_back(ConfigItem((const char*)(CATEGORY "::" name), (void*)var, buflen, (void*)(pint)def, (const char*)comment, CIT_STRING))
578 #define AddString(name, var, buflen, def) AddStringC(name, var, buflen, def, "")
579
580 static char filterString [1024], filterString2 [1024], snapVerString [256];
581 static bool niceAlignment, showComments, readOnlyConfig;
582 static int configSort;
583
WinPreSave(ConfigFile & conf)584 void WinPreSave(ConfigFile& conf)
585 {
586 strcpy(filterString, "output filter: ");
587 for(int i=0;i<NUM_FILTERS;i++)
588 {
589 static char temp [256];
590 sprintf(temp, "%d=%s%c ", i, GetFilterName((RenderFilter)i), i!=NUM_FILTERS-1?',':' ');
591 strcat(filterString, temp);
592 }
593 strcpy(filterString2, "hi-res output filter: ");
594 for(int i=0;i<NUM_FILTERS;i++)
595 {
596 if(GetFilterHiResSupport((RenderFilter)i))
597 {
598 static char temp [256];
599 sprintf(temp, "%d=%s%c ", i, GetFilterName((RenderFilter)i), i!=NUM_FILTERS-1?',':' ');
600 strcat(filterString2, temp);
601 }
602 }
603 sprintf(snapVerString, "Snapshot save version. Must be between 1 and %d (inclusive)", SNAPSHOT_VERSION);
604
605 // GetWindowRect (GUI.hWnd, &GUI.window_size);
606 GUI.window_size.right -= GUI.window_size.left;
607 GUI.window_size.bottom -= GUI.window_size.top;
608
609 int extra_width = 2*(GetSystemMetrics(SM_CXBORDER) +
610 GetSystemMetrics(SM_CXDLGFRAME));
611 GUI.window_size.right -= extra_width;
612
613 int extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
614 GetSystemMetrics(SM_CYDLGFRAME)) +
615 GetSystemMetrics(SM_CYCAPTION) +
616 GetSystemMetrics(SM_CYMENU) +
617 (GUI.HideMenu ? 0 : (
618 (GUI.window_size.right <= 392 ? GetSystemMetrics(SM_CYMENU) : 0) + // HACK: accounts for menu wrapping (when width is small)
619 (GUI.window_size.right <= 208 ? GetSystemMetrics(SM_CYMENU) : 0) +
620 (GUI.window_size.right <= 148 ? GetSystemMetrics(SM_CYMENU) : 0)));
621 GUI.window_size.bottom -= extra_height;
622
623 if(GUI.window_size.bottom < 10) GUI.window_size.bottom = 10;
624 if(GUI.window_size.right < 10) GUI.window_size.right = 10;
625
626 GUI.customRomDlgSettings.window_size.right -= GUI.customRomDlgSettings.window_size.left;
627 GUI.customRomDlgSettings.window_size.bottom -= GUI.customRomDlgSettings.window_size.top;
628
629 conf.DeleteKey("Sound::Mono");
630 if(configSort == 2)
631 conf.ClearLines();
632 }
WinPostSave(ConfigFile & conf)633 void WinPostSave(ConfigFile& conf)
634 {
635 int extra_width = 2*(GetSystemMetrics(SM_CXBORDER) +
636 GetSystemMetrics(SM_CXDLGFRAME));
637 int extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
638 GetSystemMetrics(SM_CYDLGFRAME)) +
639 GetSystemMetrics(SM_CYCAPTION) +
640 (GUI.HideMenu ? 0 : (GetSystemMetrics(SM_CYMENU) +
641 (GUI.window_size.right <= 392 ? GetSystemMetrics(SM_CYMENU) : 0) + // HACK: accounts for menu wrapping (when width is small)
642 (GUI.window_size.right <= 208 ? GetSystemMetrics(SM_CYMENU) : 0) +
643 (GUI.window_size.right <= 148 ? GetSystemMetrics(SM_CYMENU) : 0)));
644 GUI.window_size.right += GUI.window_size.left;
645 GUI.window_size.bottom += GUI.window_size.top;
646 GUI.window_size.right += extra_width;
647 GUI.window_size.bottom += extra_height;
648
649 GUI.customRomDlgSettings.window_size.right += GUI.customRomDlgSettings.window_size.left;
650 GUI.customRomDlgSettings.window_size.bottom += GUI.customRomDlgSettings.window_size.top;
651 }
WinPostLoad(ConfigFile & conf)652 void WinPostLoad(ConfigFile& conf)
653 {
654 int i;
655 if(Settings.DisplayPressedKeys) Settings.DisplayPressedKeys = 2;
656 for(i=0;i<8;i++) Joypad[i+8].Enabled = Joypad[i].Enabled;
657 if(GUI.MaxRecentGames < 1) GUI.MaxRecentGames = 1;
658 if(GUI.MaxRecentGames > MAX_RECENT_GAMES_LIST_SIZE) GUI.MaxRecentGames = MAX_RECENT_GAMES_LIST_SIZE;
659 if(GUI.rewindGranularity==0) GUI.rewindGranularity = 1;
660 bool gap = false;
661 for(i=0;i<MAX_RECENT_GAMES_LIST_SIZE;i++) // remove gaps in recent games list
662 {
663 if(!*GUI.RecentGames[i])
664 gap = true;
665 else if(gap)
666 {
667 memmove(GUI.RecentGames[i-1], GUI.RecentGames[i], MAX_PATH * sizeof(TCHAR));
668 *GUI.RecentGames[i] = TEXT('\0');
669 gap = false;
670 i = -1;
671 }
672 }
673
674 if (Settings.MaxSpriteTilesPerLine != 34 && Settings.MaxSpriteTilesPerLine != 128)
675 Settings.MaxSpriteTilesPerLine = 34;
676
677 switch (Settings.OverclockMode)
678 {
679 default:
680 case 0:
681 Settings.OneClockCycle = 6;
682 Settings.OneSlowClockCycle = 8;
683 Settings.TwoClockCycles = 12;
684 break;
685 case 1:
686 Settings.OneClockCycle = 6;
687 Settings.OneSlowClockCycle = 6;
688 Settings.TwoClockCycles = 12;
689 break;
690 case 2:
691 Settings.OneClockCycle = 4;
692 Settings.OneSlowClockCycle = 5;
693 Settings.TwoClockCycles = 8;
694 break;
695 case 3:
696 Settings.OneClockCycle = 3;
697 Settings.OneSlowClockCycle = 4;
698 Settings.TwoClockCycles = 6;
699 break;
700 }
701
702 ConfigFile::SetNiceAlignment(niceAlignment);
703 ConfigFile::SetShowComments(showComments);
704 ConfigFile::SetAlphaSort(configSort==2);
705 ConfigFile::SetTimeSort(configSort==1);
706
707 WinPostSave(conf);
708 }
709
WinPreLoad(ConfigFile & conf)710 void WinPreLoad(ConfigFile& conf)
711 {
712 if(conf.Exists("Config::Sort"))
713 {
714 if(conf.GetUInt("Config::Sort") == 1)
715 {
716 configSort = 1;
717 ConfigFile::SetAlphaSort(configSort==2);
718 ConfigFile::SetTimeSort(configSort==1);
719 conf.ClearLines();
720 }
721 }
722 else
723 {
724 conf.DeleteKey("Config::Sort");
725 }
726 }
727
728
729 // and now for the important part,
730 // which determines what goes into and comes out of the config file:
731 // add all of the things we want to save and load to a list
732 // (but don't actually save or load anything yet)
WinRegisterConfigItems()733 void WinRegisterConfigItems()
734 {
735 #define CATEGORY "Config"
736 AddBool2C("NiceAlignment", niceAlignment, true, "on to line up the =, :, and # in each section of this config file");
737 AddBool2C("Comments", showComments, true, "on to keep comments such as this in this config file. To update/refresh all comments, set this to false and run Snes9x, then set it to true and run Snes9x again.");
738 AddUIntC("Sort", configSort, 1, "ordering within sections: 0=allow reordering, 1=force default order, 2=sort alphabetically");
739 AddBoolC("Lock", readOnlyConfig, false, "if true, prevents Snes9x from editing this configuration file (or making it read-only while it is running)");
740 #undef CATEGORY
741 #define CATEGORY "Display"
742 AddBool2("Transparency", Settings.Transparency, true);
743 AddBoolC("MessagesInImage", Settings.AutoDisplayMessages, false, "true to draw text inside the SNES image (will get into AVIs, screenshots, and filters)");
744 AddBool2C("FrameRate", Settings.DisplayFrameRate, false, "on to display the framerate (will be inaccurate if AutoMaxSkipFrames is too small)");
745 AddBoolC("DisplayInput", Settings.DisplayPressedKeys, false, "true to show which buttons are pressed");
746 AddBoolC("DisplayFrameCount", Settings.DisplayMovieFrame, true, "true to show the frame count when a movie is playing");
747 #undef CATEGORY
748 #define CATEGORY "Display\\Win"
749 AddUIntC("OutputMethod", GUI.outputMethod, 1, "0=DirectDraw, 1=Direct3D, 2=OpenGL");
750 AddUIntC("FilterType", GUI.Scale, 0, filterString);
751 AddUIntC("FilterHiRes", GUI.ScaleHiRes, 0, filterString2);
752 AddBoolC("BlendHiRes", GUI.BlendHiRes, true, "true to horizontally blend Hi-Res images (better transparency effect on filters that do not account for this)");
753 AddBoolC("NTSCScanlines", GUI.NTSCScanlines, true, "true to use scanlines with Blargg's NTSC filters");
754 AddBoolC("ShaderEnabled", GUI.shaderEnabled, false, "true to use pixel shader (if supported by output method)");
755 AddStringC("Direct3D:D3DShader", GUI.D3DshaderFileName, MAX_PATH, "", "shader filename for Direct3D mode (HLSL effect file or CG shader");
756 AddStringC("OpenGL:OGLShader", GUI.OGLshaderFileName, MAX_PATH, "", "shader filename for OpenGL mode (bsnes-style XML shader or CG shader)");
757 AddBoolC("OpenGL:DisablePBOs", GUI.OGLdisablePBOs, false, "do not use PBOs in OpenGL mode, even if the video card supports them");
758 AddBoolC("ExtendHeight", GUI.HeightExtend, false, "true to display an extra 15 pixels at the bottom, which few games use. Also increases AVI output size from 256x224 to 256x240.");
759 AddBoolC("AlwaysCenterImage", GUI.AlwaysCenterImage,false, "true to center the image even if larger than window");
760 AddIntC("Window:Width", GUI.window_size.right, 512, "256=1x, 512=2x, 768=3x, 1024=4x, etc. (usually)");
761 AddIntC("Window:Height", GUI.window_size.bottom, 448, "224=1x, 448=2x, 672=3x, 896=4x, etc. (usually)");
762 AddIntC("Window:Left", GUI.window_size.left, 0, "in pixels from left edge of screen");
763 AddIntC("Window:Top", GUI.window_size.top, 0, "in pixels from top edge of screen");
764 AddBool("Window:Maximized", GUI.window_maximized, false);
765 AddIntC("CustomRomDialog:Width", GUI.customRomDlgSettings.window_size.right, 660, "");
766 AddIntC("CustomRomDialog:Height", GUI.customRomDlgSettings.window_size.bottom, 400, "");
767 AddIntC("CustomRomDialog:Left", GUI.customRomDlgSettings.window_size.left, 50, "in pixels from left edge of screen");
768 AddIntC("CustomRomDialog:Top", GUI.customRomDlgSettings.window_size.top, 50, "in pixels from top edge of screen");
769 AddBool("CustomRomDialog:Maximized", GUI.customRomDlgSettings.window_maximized, false);
770 AddIntC("CustomRomDialog:FolderPaneWidth", GUI.customRomDlgSettings.folderPaneWidth, 230, "");
771 AddIntC("CustomRomDialog:DescColumnWidth", GUI.customRomDlgSettings.columnDescription, 112, "");
772 AddIntC("CustomRomDialog:FilenameColumnWidth", GUI.customRomDlgSettings.columnFilename, 196, "");
773 AddIntC("CustomRomDialog:SizeColumnWidth", GUI.customRomDlgSettings.columnSize, 67, "");
774 AddBoolC("Stretch:Enabled", GUI.Stretch, true, "true to stretch the game image to fill the window or screen");
775 AddBoolC("Stretch:MaintainAspectRatio", GUI.AspectRatio, true, "prevents stretching from changing the aspect ratio");
776 AddBoolC("Stretch:IntegerScaling", GUI.IntegerScaling, false, "scales image height to exact integer multiples");
777 AddUIntC("Stretch:AspectRatioBaseWidth", GUI.AspectWidth, 256, "base width for aspect ratio calculation (AR=AspectRatioBaseWidth/224), default is 256 - set to 299 for 4:3 aspect ratio");
778 AddBoolC("Stretch:BilinearFilter", Settings.BilinearFilter, true, "allows bilinear filtering of stretching. Depending on your video card and the window size, this may result in a lower framerate.");
779 AddBoolC("Stretch:LocalVidMem", GUI.LocalVidMem, true, "determines the location of video memory in DirectDraw mode. May increase or decrease rendering performance, depending on your setup and which filter and stretching options are active.");
780 AddBool("Fullscreen:Enabled", GUI.FullScreen, false);
781 AddUInt("Fullscreen:Width", GUI.FullscreenMode.width, 640);
782 AddUInt("Fullscreen:Height", GUI.FullscreenMode.height, 480);
783 AddUInt("Fullscreen:Depth", GUI.FullscreenMode.depth, 16);
784 AddUInt("Fullscreen:RefreshRate", GUI.FullscreenMode.rate, 60);
785 AddBool("Fullscreen:DoubleBuffered", GUI.DoubleBuffered, false);
786 AddBoolC("Fullscreen:EmulateFullscreen", GUI.EmulateFullscreen, true,"true makes snes9x create a window that spans the entire screen when going fullscreen");
787 AddBoolC("HideMenu", GUI.HideMenu, false, "true to auto-hide the menu bar on startup.");
788 AddBoolC("Vsync", GUI.Vsync, false, "true to enable Vsync");
789 AddBoolC("ReduceInputLag", GUI.ReduceInputLag, false, "true to reduce input lag by hard synchronization");
790 AddBoolC("DWMSync", GUI.DWMSync, false, "sync to DWM compositor if it is running");
791 AddBoolC("FilterMessageFont", GUI.filterMessagFont, true, "true to filter message font with EPX on 2x/3x scales if MessagesInImage is false)");
792 #undef CATEGORY
793 #define CATEGORY "Settings"
794 AddUIntC("FrameSkip", Settings.SkipFrames, AUTO_FRAMERATE, "200=automatic (limits at 50/60 fps), 0=none, 1=skip every other, ...");
795 AddUIntC("AutoMaxSkipFramesAtOnce", Settings.AutoMaxSkipFrames, 0, "most frames to skip at once to maintain speed in automatic mode, don't set to more than 1 or 2 frames because the skipping algorithm isn't very smart");
796 AddUIntC("TurboFrameSkip", Settings.TurboSkipFrames, 15, "how many frames to skip when in fast-forward mode");
797 AddUInt("AutoSaveDelay", Settings.AutoSaveDelay, 30);
798 AddBool("BlockInvalidVRAMAccess", Settings.BlockInvalidVRAMAccessMaster, true);
799 AddBool2C("SnapshotScreenshots", Settings.SnapshotScreenshots, true, "on to save the screenshot in each snapshot, for loading-when-paused display");
800 AddBoolC("MovieTruncateAtEnd", Settings.MovieTruncate, true, "true to truncate any leftover data in the movie file after the current frame when recording stops");
801 AddBoolC("MovieNotifyIgnored", Settings.MovieNotifyIgnored, false, "true to display \"(ignored)\" in the frame counter when recording when the last frame of input was not used by the SNES (such as lag or loading frames)");
802 AddBool("DisplayWatchedAddresses", Settings.DisplayWatchedAddresses, true);
803 AddBool2C("WrongMovieStateProtection", Settings.WrongMovieStateProtection, true, "off to allow states to be loaded for recording from a different movie than they were made in");
804 AddUIntC("MessageDisplayTime", Settings.InitialInfoStringTimeout, 120, "display length of messages, in frames. set to 0 to disable all message text");
805 #undef CATEGORY
806 #define CATEGORY "Settings\\Win"
807 AddUIntC("RewindBufferSize", GUI.rewindBufferSize, 0, "rewind buffer size in MB - 0 disables rewind support");
808 AddUIntC("RewindGranularity", GUI.rewindGranularity, 1, "rewind granularity - rewind takes a snapshot each x frames");
809 AddBoolC("PauseWhenInactive", GUI.InactivePause, TRUE, "true to pause Snes9x when it is not the active window");
810 AddBoolC("CustomRomOpenDialog", GUI.CustomRomOpen, false, "false to use standard Windows open dialog for the ROM open dialog");
811 AddBoolC("AVIHiRes", GUI.AVIHiRes, false, "true to record AVI in Hi-Res scale");
812 AddBoolC("ConfirmSaveLoad", GUI.ConfirmSaveLoad, false, "true to ask for confirmation when saving/loading");
813 // AddUIntC("Language", GUI.Language, 0, "0=English, 1=Nederlands"); // NYI
814 AddBoolC("FrameAdvanceSkipsNonInput", GUI.FASkipsNonInput, false, "causes frame advance to fast-forward past frames where the game is definitely not checking input, such as during lag or loading time. EXPERIMENTAL");
815 AddBool("MovieDefaultClearSRAM", GUI.MovieClearSRAM, false);
816 AddBool("MovieDefaultStartFromReset", GUI.MovieStartFromReset, false);
817 AddBool("MovieDefaultReadOnly", GUI.MovieReadOnly, true);
818 AddUInt("CurrentSaveSlot", GUI.CurrentSaveSlot, 0);
819 AddUIntC("MaxRecentGames", GUI.MaxRecentGames, 14, "max recent games to show in the recent games menu (must be <= 32)");
820 #undef CATEGORY
821 #define CATEGORY "Settings\\Win\\Files"
822 AddStringC("Dir:Roms", GUI.RomDir, _MAX_PATH, ".\\Roms", "directory where the Open ROM dialog will start");
823 AddStringC("Dir:Screenshots", GUI.ScreensDir, _MAX_PATH, ".\\Screenshots", "directory where screenshots will be saved");
824 AddStringC("Dir:Movies", GUI.MovieDir, _MAX_PATH, ".\\Movies", "the default directory for recorded movie (.smv) files");
825 AddStringC("Dir:SPCs", GUI.SPCDir, _MAX_PATH, ".\\SPCs", "directory where SPCs will be saved");
826 AddStringC("Dir:Savestates", GUI.FreezeFileDir, _MAX_PATH, ".\\Saves", "directory where savestates will be created and loaded from");
827 AddStringC("Dir:SRAM", GUI.SRAMFileDir, _MAX_PATH, ".\\Saves", "directory where battery saves will be created and loaded from");
828 AddStringC("Dir:Cheats", GUI.CheatDir, _MAX_PATH, ".\\Cheats", "directory in which cheats (.cht files) will be looked for");
829 AddStringC("Dir:Patches", GUI.PatchDir, _MAX_PATH, ".\\Patches", "directory in which ROM patches (.ips/.bps/.ups files) will be looked for");
830 AddStringC("Dir:Bios", GUI.BiosDir, _MAX_PATH, ".\\BIOS", "directory where BIOS files (such as \"BS-X.bios\") will be located");
831 AddStringC("Dir:SatData", GUI.SatDir, _MAX_PATH, ".\\SatData", "directory where Satellaview Signal Data files will be located");
832 AddBoolC("Dir:Lock", GUI.LockDirectories, false, "true to prevent Snes9x from changing configured directories when you browse to a new location");
833 #define ADD(n) AddString("Rom:RecentGame" #n, GUI.RecentGames[n-1], MAX_PATH, "")
834 ADD(1); ADD(2); ADD(3); ADD(4); ADD(5); ADD(6); ADD(7); ADD(8);
835 ADD(9); ADD(10); ADD(11); ADD(12); ADD(13); ADD(14); ADD(15); ADD(16);
836 ADD(17); ADD(18); ADD(19); ADD(20); ADD(21); ADD(22); ADD(23); ADD(24);
837 ADD(25); ADD(26); ADD(27); ADD(28); ADD(29); ADD(30); ADD(31); ADD(32);
838 assert(MAX_RECENT_GAMES_LIST_SIZE == 32);
839 #undef ADD
840 AddString("Rom:MultiCartA", multiRomA, _MAX_PATH, "");
841 AddString("Rom:MultiCartB", multiRomB, _MAX_PATH, "");
842 #undef CATEGORY
843 #define CATEGORY "Sound"
844 AddIntC("Sync", Settings.SoundSync, 1, "1 to sync emulation to sound output, 0 to disable.");
845 AddUIntC("Rate", Settings.SoundPlaybackRate, 48000, "sound playback quality, in Hz");
846 AddUIntC("InputRate", Settings.SoundInputRate, 31950, "for each 'Input rate' samples generated by the SNES, 'Playback rate' samples will produced. If you experience crackling you can try to lower this setting.");
847 AddBoolC("Mute", GUI.Mute, false, "true to mute sound output (does not disable the sound CPU)");
848 AddBool("DynamicRateControl", Settings.DynamicRateControl, false);
849 AddBool("AutomaticInputRate", GUI.AutomaticInputRate, true);
850 AddIntC("InterpolationMethod", Settings.InterpolationMethod, 2, "0 = None, 1 = Linear, 2 = Gaussian (accurate), 3 = Cubic, 4 = Sinc");
851 #undef CATEGORY
852 #define CATEGORY "Sound\\Win"
853 AddUIntC("SoundDriver", GUI.SoundDriver, 4, "4=XAudio2 (recommended), 8=WaveOut");
854 AddUIntC("BufferSize", GUI.SoundBufferSize, 64, "sound buffer size in ms - determines the internal and output sound buffer sizes. actual mixing is done every SoundBufferSize/4 samples");
855 AddBoolC("MuteFrameAdvance", GUI.FAMute, false, "true to prevent Snes9x from outputting sound when the Frame Advance command is in use");
856 AddUIntC("VolumeRegular", GUI.VolumeRegular, 100, "volume during regular play (percentage between 0 and 100)");
857 AddUIntC("VolumeTurbo", GUI.VolumeTurbo, 100, "volume during turbo mode (percentage between 0 and 100)");
858 AddStringC("OutputDevice", GUI.AudioDevice, MAX_AUDIO_NAME_LENGTH, "Default", "Name of the output audio device (substring matching, XAudio2 only atm), set to 'Default' for default audio device");
859 #undef CATEGORY
860 #define CATEGORY "Controls"
861 AddBoolC("AllowLeftRight", Settings.UpAndDown, false, "true to allow left+right and up+down");
862 #undef CATEGORY
863 #define CATEGORY "ROM"
864 AddBoolC("Cheat", Settings.ApplyCheats, true, "true to allow enabled cheats to be applied");
865 AddInvBoolC("Patch", Settings.NoPatch, true, "true to allow IPS/UPS patches to be applied (\"soft patching\")");
866 AddBoolC("IgnorePatchChecksum", Settings.IgnorePatchChecksum, false, "true to allow BPS patches to be applied even if the checksum fails");
867 AddBoolC("BS", Settings.BS, false, "Broadcast Satellaview emulation");
868 #undef CATEGORY
869 #ifdef NETPLAY_SUPPORT
870 #define CATEGORY "Netplay"
871 AddUInt("Port", Settings.Port, NP_DEFAULT_PORT);
872 AddString("Server", Settings.ServerName, 128, Settings.ServerName);
873 AddBool2("SyncByReset", NPServer.SyncByReset, true);
874 AddBool2("SendROMImageOnConnect", NPServer.SendROMImageOnConnect, false);
875 AddUInt("MaxFrameSkip", NetPlay.MaxFrameSkip, 10);
876 AddUInt("MaxBehindFrameCount", NetPlay.MaxBehindFrameCount, 10);
877 AddBoolC("UseJoypad1", GUI.NetplayUseJoypad1, true, "if false, player 2 has to use their joypad #2 controls, etc.");
878 #define ADD(n,d) AddString("RecentHost" #n, GUI.RecentHostNames[n-1], MAX_PATH, d)
879 ADD(1,"localhost"); ADD(2,""); ADD(3,""); ADD(4,""); ADD(5,""); ADD(6,""); ADD(7,""); ADD(8,"");
880 ADD(9,""); ADD(10,""); ADD(11,""); ADD(12,""); ADD(13,""); ADD(14,""); ADD(15,""); ADD(16,"");
881 assert(MAX_RECENT_HOSTS_LIST_SIZE == 16);
882 #undef ADD
883 #undef CATEGORY
884 #endif
885 #define CATEGORY "Controls\\Win"
886 #define ADD(n,x) AddVKey("Joypad" #n ":" #x, Joypad[n-1].x, Joypad[n-1].x)
887 #define ADDN(n,x,n2) AddVKey("Joypad" #n ":" #n2, Joypad[n-1].x, Joypad[n-1].x)
888 #define ADDB(n,x) AddBool("Joypad" #n ":" #x, Joypad[n-1].x, Joypad[n-1].x)
889 #define ADD2(n) ADDB(n,Enabled); ADD(n,Up); ADD(n,Down); ADD(n,Left); ADD(n,Right); ADD(n,A); ADD(n,B); ADD(n,Y); ADD(n,X); ADD(n,L); ADD(n,R); ADD(n,Start); ADD(n,Select); ADDN(n,Left_Up,Left+Up); ADDN(n,Right_Up,Right+Up); ADDN(n,Right_Down,Right+Down); ADDN(n,Left_Down,Left+Down)
890 #define ADDT(n,x) AddVKey("Joypad" #n "Turbo:" #x, Joypad[n-1+8].x, Joypad[n-1+8].x)
891 #define ADDTN(n,x,n2) AddVKey("Joypad" #n "Turbo:" #n2, Joypad[n-1+8].x, Joypad[n-1+8].x)
892 #define ADDT2(n) ADDTN(n,Down,AutoFire); ADDTN(n,Left,AutoHold); ADDTN(n,Up,TempTurbo); ADDTN(n,Right,ClearAll)
893 #define ADDT3(n) ADDT(n,A); ADDT(n,B); ADDT(n,Y); ADDT(n,X); ADDT(n,L); ADDT(n,R); ADDT(n,Start); ADDT(n,Select)
894 #define ADD2T2(n) ADD2(n); ADDT2(n)
895 // NOTE: for now, the windows port doesn't actually allow 8 different controllers to be set configured, only 5
896 ADD2T2(1); ADD2T2(2); ADD2T2(3); ADD2T2(4); ADD2T2(5); ADD2T2(6); ADD2T2(7); ADD2T2(8);
897 ADDT3(1); ADDT3(2); ADDT3(3); ADDT3(4); ADDT3(5); ADDT3(6); ADDT3(7); ADDT3(8);
898 #undef ADD
899 #undef ADD2
900 #undef ADDN
901 #undef ADDB
902 #undef ADDT
903 #undef ADDT2
904 #undef ADDT3
905 #undef ADDTN
906 #undef ADD2T2
907 AddBool2C("Input:Background", GUI.BackgroundInput, false, "on to detect game keypresses and hotkeys while window is inactive, if PauseWhenInactive = FALSE.");
908 AddBool2C("Input:BackgroundKeyHotkeys", GUI.BackgroundKeyHotkeys, true, "on to also detect keyboard hotkeys when backgroundinput is active");
909 #undef CATEGORY
910 #define CATEGORY "Controls\\Win\\Hotkeys"
911 AddBool2C("Handler:Joystick", GUI.JoystickHotkeys, true, "on to detect game controller buttons assigned to hotkeys. May impact performance.");
912 #define ADD(x) AddVKey("Key:" #x , CustomKeys.x.key, CustomKeys.x.key); AddVKMod("Mods:" #x, CustomKeys.x.modifiers, CustomKeys.x.modifiers)
913 #define ADDN(x,n2) AddVKey("Key:" #n2, CustomKeys.x.key, CustomKeys.x.key); AddVKMod("Mods:" #n2, CustomKeys.x.modifiers, CustomKeys.x.modifiers)
914 ADD(SpeedUp); ADD(SpeedDown); ADD(Pause); ADD(FrameAdvance);
915 ADD(SkipUp); ADD(SkipDown); ADD(ScopeTurbo); ADD(ScopePause);
916 ADD(FrameCount); ADD(ReadOnly); ADD(FastForward); ADD(FastForwardToggle); ADD(ShowPressed);
917 ADDN(Save[0],SaveSlot0); ADDN(Save[1],SaveSlot1); ADDN(Save[2],SaveSlot2); ADDN(Save[3],SaveSlot3); ADDN(Save[4],SaveSlot4); ADDN(Save[5],SaveSlot5); ADDN(Save[6],SaveSlot6); ADDN(Save[7],SaveSlot7); ADDN(Save[8],SaveSlot8); ADDN(Save[9],SaveSlot9);
918 ADDN(Load[0],LoadSlot0); ADDN(Load[1],LoadSlot1); ADDN(Load[2],LoadSlot2); ADDN(Load[3],LoadSlot3); ADDN(Load[4],LoadSlot4); ADDN(Load[5],LoadSlot5); ADDN(Load[6],LoadSlot6); ADDN(Load[7],LoadSlot7); ADDN(Load[8],LoadSlot8); ADDN(Load[9],LoadSlot9);
919 ADDN(SelectSave[0],SelectSlot0); ADDN(SelectSave[1],SelectSlot1); ADDN(SelectSave[2],SelectSlot2); ADDN(SelectSave[3],SelectSlot3); ADDN(SelectSave[4],SelectSlot4); ADDN(SelectSave[5],SelectSlot5); ADDN(SelectSave[6],SelectSlot6); ADDN(SelectSave[7],SelectSlot7); ADDN(SelectSave[8],SelectSlot8); ADDN(SelectSave[9],SelectSlot9);
920 ADD(SaveScreenShot); ADD(SlotPlus); ADD(SlotMinus); ADD(SlotSave); ADD(SlotLoad);
921 ADD(BGL1); ADD(BGL2); ADD(BGL3); ADD(BGL4); ADD(BGL5);
922 ADD(ClippingWindows); /*ADD(BGLHack);*/ ADD(Transparency); /*ADD(HDMA)*/; /*ADD(GLCube);*/
923 /*ADD(InterpMode7);*/ ADD(JoypadSwap); ADD(SwitchControllers); ADD(ResetGame); ADD(ToggleCheats);
924 ADD(TurboA); ADD(TurboB); ADD(TurboY); ADD(TurboX); ADD(TurboL); ADD(TurboR); ADD(TurboStart); ADD(TurboSelect); ADD(TurboUp); ADD(TurboDown); ADD(TurboLeft); ADD(TurboRight);
925 ADD(QuitS9X);ADD(Rewind);
926 ADD(SaveFileSelect); ADD(LoadFileSelect);
927 ADD(Mute);
928 #undef ADD
929 #undef ADDN
930 #undef CATEGORY
931 #define CATEGORY "Hack"
932 AddUIntC("CPUOverclockMode", Settings.OverclockMode, 0, "CPU Overclock: 0=none, 1=min, 2=medium, 3=max");
933 AddUIntC("MaxSpriteTilesPerLine", Settings.MaxSpriteTilesPerLine, 34, "Max sprite tiles rendered per line. Default = 34, Unlimited ~= 128");
934 AddUIntC("SuperFXClockMultiplier", Settings.SuperFXClockMultiplier, 100, "SuperFX speed, in percent (default 100)");
935 AddBoolC("SeparateEchoBuffer", Settings.SeparateEchoBuffer, false, "Separate echo buffer from APU ram. For old hacks only.");
936 #undef CATEGORY
937 }
938
939
940
941
942 // These let us give the config file read-only status to other applications while snes9x is running,
943 // to prevent the situation where the user saves a modified .cfg file while snes9x is running
944 // only to have the changes overwritten by snes9x upon closing snes9x.
945 static HANDLE locked_file = NULL;
WinLockConfigFile()946 void WinLockConfigFile ()
947 {
948 if(readOnlyConfig) return; // if user has lock on file, don't let Snes9x lock it
949
950 static std::string fname;
951 fname=S9xGetDirectory(DEFAULT_DIR);
952 fname+=SLASH_STR "snes9x.conf";
953 STREAM fp;
954 if((fp=OPEN_STREAM(fname.c_str(), "r"))!=NULL){
955 CLOSE_STREAM(fp);
956 locked_file=CreateFileA(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
957 } else {
958 fname=S9xGetDirectory(DEFAULT_DIR);
959 fname+=SLASH_STR "snes9x.cfg";
960 if((fp=OPEN_STREAM(fname.c_str(), "r"))!=NULL){
961 CLOSE_STREAM(fp);
962 locked_file=CreateFileA(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
963 }
964 }
965 }
WinUnlockConfigFile()966 void WinUnlockConfigFile ()
967 {
968 if(locked_file){
969 CloseHandle(locked_file);
970 locked_file=NULL;
971 }
972 }
973
974
975 /*****************************************************************************/
976 /* WinSave/WinLoad - Save/Load the settings to/from the registry */
977 /*****************************************************************************/
978
979
980
981 static ConfigFile loaded_config_file;
982
WinLoadConfigFile(ConfigFile & conf)983 void WinLoadConfigFile(ConfigFile& conf)
984 {
985 WinPreLoad(conf);
986
987 WinSetDefaultValues();
988
989 for(unsigned int i = 0 ; i < configItems.size() ; i++)
990 configItems[i].Get(conf);
991
992 loaded_config_file = conf;
993
994 WinPostLoad(conf);
995 }
996
WinSaveConfigFile()997 void WinSaveConfigFile()
998 {
999 if(readOnlyConfig) return; // if user has lock on file, don't let Snes9x overwrite it
1000
1001 ConfigFile& conf = loaded_config_file;
1002 conf.ClearUnused();
1003
1004 WinPreSave(conf);
1005
1006 for(unsigned int i = 0 ; i < configItems.size() ; i++)
1007 configItems[i].Set(conf);
1008
1009 bool wasLocked = locked_file!=NULL;
1010 if(wasLocked) WinUnlockConfigFile();
1011
1012 S9xSaveConfigFile(conf);
1013
1014 if(wasLocked) WinLockConfigFile();
1015
1016 WinPostSave(conf);
1017 }
1018
1019
S9xParsePortConfig(ConfigFile & conf,int pass)1020 void S9xParsePortConfig(ConfigFile &conf, int pass)
1021 {
1022 WinLoadConfigFile(conf);
1023 return;
1024 }
1025
WinCleanupConfigData()1026 void WinCleanupConfigData()
1027 {
1028 configItems.clear();
1029 }
1030