1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // Nestopia - NES/Famicom emulator written in C++
4 //
5 // Copyright (C) 2003-2008 Martin Freij
6 //
7 // This file is part of Nestopia.
8 //
9 // Nestopia is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Nestopia is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Nestopia; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 ////////////////////////////////////////////////////////////////////////////////////////
24 
25 #include "NstResourceString.hpp"
26 #include "NstApplicationInstance.hpp"
27 #include "NstWindowParam.hpp"
28 #include "NstWindowUser.hpp"
29 #include "NstDialogPaths.hpp"
30 #include "NstDialogBrowse.hpp"
31 
32 namespace Nestopia
33 {
34 	namespace Window
35 	{
36 		NST_COMPILE_ASSERT
37 		(
38 			IDC_PATHS_BATTERY     - IDC_PATHS_IMAGE == IDC_PATHS_BATTERY_BROWSE     - IDC_PATHS_IMAGE_BROWSE &&
39 			IDC_PATHS_NST         - IDC_PATHS_IMAGE == IDC_PATHS_NST_BROWSE         - IDC_PATHS_IMAGE_BROWSE &&
40 			IDC_PATHS_SAMPLES     - IDC_PATHS_IMAGE == IDC_PATHS_SAMPLES_BROWSE     - IDC_PATHS_IMAGE_BROWSE &&
41 			IDC_PATHS_CHEATS      - IDC_PATHS_IMAGE == IDC_PATHS_CHEATS_BROWSE      - IDC_PATHS_IMAGE_BROWSE &&
42 			IDC_PATHS_PATCHES     - IDC_PATHS_IMAGE == IDC_PATHS_PATCHES_BROWSE     - IDC_PATHS_IMAGE_BROWSE &&
43 			IDC_PATHS_SCREENSHOTS - IDC_PATHS_IMAGE == IDC_PATHS_SCREENSHOTS_BROWSE - IDC_PATHS_IMAGE_BROWSE
44 		);
45 
46 		struct Paths::Lut
47 		{
48 			struct A
49 			{
50 				ushort type;
51 				ushort dlg;
52 				wcstring def;
53 			};
54 
55 			struct B
56 			{
57 				ushort type;
58 				ushort dlg;
59 			};
60 
61 			struct C
62 			{
63 				ushort type;
64 				ushort dlg;
65 				wcstring cfg;
66 			};
67 
68 			static const A dirs[NUM_DIRS];
69 			static const B flags[NUM_FLAGS];
70 			static const C screenShots[NUM_SCREENSHOTS];
71 		};
72 
73 		const Paths::Lut::A Paths::Lut::dirs[NUM_DIRS] =
74 		{
75 			{ DIR_IMAGE,      IDC_PATHS_IMAGE,       L""              },
76 			{ DIR_SAVE,       IDC_PATHS_BATTERY,     L"save\\"        },
77 			{ DIR_STATE,      IDC_PATHS_NST,         L"states\\"      },
78 			{ DIR_SAMPLES,    IDC_PATHS_SAMPLES,     L"samples\\"     },
79 			{ DIR_CHEATS,     IDC_PATHS_CHEATS,      L"cheats\\"      },
80 			{ DIR_PATCHES,    IDC_PATHS_PATCHES,     L"patches\\"     },
81 			{ DIR_SCREENSHOT, IDC_PATHS_SCREENSHOTS, L"screenshots\\" }
82 		};
83 
84 		const Paths::Lut::B Paths::Lut::flags[NUM_FLAGS] =
85 		{
86 			{ USE_LAST_IMAGE_DIR,      IDC_PATHS_IMAGE_LAST                },
87 			{ READONLY_CARTRIDGE,      IDC_PATHS_BATTERY_PROTECT           },
88 			{ AUTO_IMPORT_STATE_SLOTS, IDC_PATHS_NST_AUTO_IMPORT           },
89 			{ AUTO_EXPORT_STATE_SLOTS, IDC_PATHS_NST_AUTO_EXPORT           },
90 			{ CHEATS_AUTO_LOAD,        IDC_PATHS_CHEATS_AUTO_LOAD          },
91 			{ CHEATS_AUTO_SAVE,        IDC_PATHS_CHEATS_AUTO_SAVE          },
92 			{ PATCH_AUTO_APPLY,        IDC_PATHS_PATCHES_AUTO_APPLY        },
93 			{ PATCH_BYPASS_VALIDATION, IDC_PATHS_PATCHES_BYPASS_VALIDATION },
94 			{ COMPRESS_STATES,         IDC_PATHS_NST_COMPRESS              }
95 		};
96 
97 		const Paths::Lut::C Paths::Lut::screenShots[NUM_SCREENSHOTS] =
98 		{
99 			{ SCREENSHOT_PNG,  IDC_PATHS_SCREENSHOTS_PNG,  L"png" },
100 			{ SCREENSHOT_JPEG, IDC_PATHS_SCREENSHOTS_JPEG, L"jpg" },
101 			{ SCREENSHOT_BMP,  IDC_PATHS_SCREENSHOTS_BMP,  L"bmp" }
102 		};
103 
104 		struct Paths::Handlers
105 		{
106 			static const MsgHandler::Entry<Paths> messages[];
107 			static const MsgHandler::Entry<Paths> commands[];
108 		};
109 
110 		const MsgHandler::Entry<Paths> Paths::Handlers::messages[] =
111 		{
112 			{ WM_INITDIALOG, &Paths::OnInitDialog }
113 		};
114 
115 		const MsgHandler::Entry<Paths> Paths::Handlers::commands[] =
116 		{
117 			{ IDC_PATHS_IMAGE_BROWSE,       &Paths::OnCmdBrowse      },
118 			{ IDC_PATHS_BATTERY_BROWSE,     &Paths::OnCmdBrowse      },
119 			{ IDC_PATHS_NST_BROWSE,         &Paths::OnCmdBrowse      },
120 			{ IDC_PATHS_SAMPLES_BROWSE,     &Paths::OnCmdBrowse      },
121 			{ IDC_PATHS_CHEATS_BROWSE,      &Paths::OnCmdBrowse      },
122 			{ IDC_PATHS_PATCHES_BROWSE,     &Paths::OnCmdBrowse      },
123 			{ IDC_PATHS_SCREENSHOTS_BROWSE, &Paths::OnCmdBrowse      },
124 			{ IDC_PATHS_IMAGE_LAST,         &Paths::OnCmdLastVisited },
125 			{ IDC_PATHS_DEFAULT,            &Paths::OnCmdDefault     },
126 			{ IDOK,                         &Paths::OnCmdOk          }
127 		};
128 
Flags()129 		inline Paths::Settings::Flags::Flags()
130 		: Collection::BitSet
131 		(
132 			1U << USE_LAST_IMAGE_DIR      |
133 			1U << AUTO_IMPORT_STATE_SLOTS |
134 			1U << AUTO_EXPORT_STATE_SLOTS |
135 			1U << COMPRESS_STATES
136 		)
137 		{}
138 
Settings()139 		Paths::Settings::Settings()
140 		: screenShotFormat(SCREENSHOT_PNG) {}
141 
Paths(const Configuration & cfg)142 		Paths::Paths(const Configuration& cfg)
143 		: dialog(IDD_PATHS,this,Handlers::messages,Handlers::commands)
144 		{
145 			Configuration::ConstSection paths( cfg["paths"] );
146 
147 			{
148 				Configuration::ConstSection images( paths["images"] );
149 
150 				settings.dirs[DIR_IMAGE] = images["directory"].Str();
151 
152 				if (images["use-recent-directory"].Yes())
153 				{
154 					settings.flags[USE_LAST_IMAGE_DIR] = true;
155 				}
156 				else if (images["use-recent-directory"].No())
157 				{
158 					settings.flags[USE_LAST_IMAGE_DIR] = false;
159 				}
160 			}
161 
162 			{
163 				Configuration::ConstSection saves( paths["saves"] );
164 
165 				settings.dirs[DIR_SAVE] = saves["directory"].Str();
166 
167 				if (saves["force-read-only"].Yes())
168 				{
169 					settings.flags[READONLY_CARTRIDGE] = true;
170 				}
171 				else if (saves["force-read-only"].No())
172 				{
173 					settings.flags[READONLY_CARTRIDGE] = false;
174 				}
175 			}
176 
177 			{
178 				Configuration::ConstSection states( paths["states"] );
179 
180 				settings.dirs[DIR_STATE] = states["directory"].Str();
181 
182 				if (states["use-compression"].Yes())
183 				{
184 					settings.flags[COMPRESS_STATES] = true;
185 				}
186 				else if (states["use-compression"].No())
187 				{
188 					settings.flags[COMPRESS_STATES] = false;
189 				}
190 
191 				if (states["auto-import"].Yes())
192 				{
193 					settings.flags[AUTO_IMPORT_STATE_SLOTS] = true;
194 				}
195 				else if (states["auto-import"].No())
196 				{
197 					settings.flags[AUTO_IMPORT_STATE_SLOTS] = false;
198 				}
199 
200 				if (states["auto-export"].Yes())
201 				{
202 					settings.flags[AUTO_EXPORT_STATE_SLOTS] = true;
203 				}
204 				else if (states["auto-export"].No())
205 				{
206 					settings.flags[AUTO_EXPORT_STATE_SLOTS] = false;
207 				}
208 			}
209 
210 			{
211 				Configuration::ConstSection samples( paths["samples"] );
212 
213 				settings.dirs[DIR_SAMPLES] = samples["directory"].Str();
214 			}
215 
216 			{
217 				Configuration::ConstSection patches( paths["cheats"] );
218 
219 				settings.dirs[DIR_CHEATS] = patches["directory"].Str();
220 
221 				if (patches["auto-load"].Yes())
222 				{
223 					settings.flags[CHEATS_AUTO_LOAD] = true;
224 				}
225 				else if (patches["auto-load"].No())
226 				{
227 					settings.flags[CHEATS_AUTO_LOAD] = false;
228 				}
229 
230 				if (patches["auto-save"].Yes())
231 				{
232 					settings.flags[CHEATS_AUTO_SAVE] = true;
233 				}
234 				else if (patches["auto-save"].No())
235 				{
236 					settings.flags[CHEATS_AUTO_SAVE] = false;
237 				}
238 			}
239 
240 			{
241 				Configuration::ConstSection patches( paths["patches"] );
242 
243 				settings.dirs[DIR_PATCHES] = patches["directory"].Str();
244 
245 				if (patches["auto-apply"].Yes())
246 				{
247 					settings.flags[PATCH_AUTO_APPLY] = true;
248 				}
249 				else if (patches["auto-apply"].No())
250 				{
251 					settings.flags[PATCH_AUTO_APPLY] = false;
252 				}
253 
254 				if (patches["bypass-validation"].Yes())
255 				{
256 					settings.flags[PATCH_BYPASS_VALIDATION] = true;
257 				}
258 				else if (patches["bypass-validation"].No())
259 				{
260 					settings.flags[PATCH_BYPASS_VALIDATION] = false;
261 				}
262 			}
263 
264 			{
265 				Configuration::ConstSection screenshots( paths["screenshots"] );
266 
267 				settings.dirs[DIR_SCREENSHOT] = screenshots["directory"].Str();
268 
269 				const GenericString format( screenshots["format"].Str() );
270 
271 				if (format.Length())
272 				{
273 					for (uint i=0; i < NUM_SCREENSHOTS; ++i)
274 					{
275 						if (format == Lut::screenShots[i].cfg)
276 						{
277 							settings.screenShotFormat = static_cast<ScreenShotFormat>(Lut::screenShots[i].type);
278 							break;
279 						}
280 					}
281 				}
282 			}
283 
284 			for (uint i=0; i < NUM_DIRS; ++i)
285 				UpdateDirectory( i );
286 		}
287 
~Paths()288 		Paths::~Paths()
289 		{
290 		}
291 
Save(Configuration & cfg) const292 		void Paths::Save(Configuration& cfg) const
293 		{
294 			Configuration::Section paths( cfg["paths"] );
295 
296 			{
297 				Configuration::Section images( paths["images"] );
298 
299 				images[ "directory"            ].Str() = settings.dirs[DIR_IMAGE];
300 				images[ "use-recent-directory" ].YesNo() = settings.flags[USE_LAST_IMAGE_DIR];
301 			}
302 
303 			{
304 				Configuration::Section saves( paths["saves"] );
305 
306 				saves[ "directory"       ].Str() = settings.dirs[DIR_SAVE];
307 				saves[ "force-read-only" ].YesNo() = settings.flags[READONLY_CARTRIDGE];
308 			}
309 
310 			{
311 				Configuration::Section states( paths["states"] );
312 
313 				states[ "directory"       ].Str() = settings.dirs[DIR_STATE];
314 				states[ "use-compression" ].YesNo() = settings.flags[COMPRESS_STATES];
315 				states[ "auto-import"     ].YesNo() = settings.flags[AUTO_IMPORT_STATE_SLOTS];
316 				states[ "auto-export"     ].YesNo() = settings.flags[AUTO_EXPORT_STATE_SLOTS];
317 			}
318 
319 			{
320 				Configuration::Section samples( paths["samples"] );
321 
322 				samples[ "directory" ].Str() = settings.dirs[DIR_SAMPLES];
323 			}
324 
325 			{
326 				Configuration::Section patches( paths["cheats"] );
327 
328 				patches[ "directory" ].Str() = settings.dirs[DIR_CHEATS];
329 				patches[ "auto-load" ].YesNo() = settings.flags[CHEATS_AUTO_LOAD];
330 				patches[ "auto-save" ].YesNo() = settings.flags[CHEATS_AUTO_SAVE];
331 			}
332 
333 			{
334 				Configuration::Section patches( paths["patches"] );
335 
336 				patches[ "directory"  ].Str() = settings.dirs[DIR_PATCHES];
337 				patches[ "auto-apply" ].YesNo() = settings.flags[PATCH_AUTO_APPLY];
338 				patches[ "bypass-validation" ].YesNo() = settings.flags[PATCH_BYPASS_VALIDATION];
339 			}
340 
341 			{
342 				Configuration::Section screenshots( paths["screenshots"] );
343 
344 				screenshots[ "directory" ].Str() = settings.dirs[DIR_SCREENSHOT];
345 				screenshots[ "format"    ].Str() = Lut::screenShots[settings.screenShotFormat].cfg;
346 			}
347 		}
348 
GetScreenShotExtension() const349 		const GenericString Paths::GetScreenShotExtension() const
350 		{
351 			switch (settings.screenShotFormat)
352 			{
353 				case SCREENSHOT_JPEG: return L"jpg";
354 				case SCREENSHOT_BMP:  return L"bmp";
355 				default:              return L"png";
356 			}
357 		}
358 
GetDirectory(Type type) const359 		const Path Paths::GetDirectory(Type type) const
360 		{
361 			return Application::Instance::GetFullPath( settings.dirs[type] );
362 		}
363 
UpdateDirectory(const uint i)364 		void Paths::UpdateDirectory(const uint i)
365 		{
366 			Path def( Application::Instance::GetExePath(Lut::dirs[i].def) );
367 			Path& dir = settings.dirs[Lut::dirs[i].type];
368 			bool useDef;
369 
370 			if (dir.Length())
371 			{
372 				dir.MakePretty( true );
373 				useDef = (dir == def);
374 			}
375 			else
376 			{
377 				dir = def;
378 				useDef = true;
379 			}
380 
381 			def = Application::Instance::GetFullPath( dir );
382 
383 			if (::GetFileAttributes( def.Ptr() ) == INVALID_FILE_ATTRIBUTES)
384 			{
385 				if (useDef || User::Confirm( Resource::String(IDS_FILE_ASK_CREATE_DIR).Invoke(def) ))
386 				{
387 					if (!::CreateDirectory( def.Ptr(), NULL ))
388 					{
389 						if (useDef)
390 							dir = Application::Instance::GetExePath().Directory();
391 						else
392 							User::Fail( IDS_FILE_ERR_CREATE_DIR );
393 					}
394 				}
395 			}
396 		}
397 
Update(const bool reset) const398 		void Paths::Update(const bool reset) const
399 		{
400 			for (uint i=0; i < NUM_DIRS; ++i)
401 				dialog.Edit( Lut::dirs[i].dlg ) << (reset ? Application::Instance::GetExePath(Lut::dirs[i].def).Ptr() : settings.dirs[Lut::dirs[i].type].Ptr());
402 
403 			Settings::Flags flags;
404 
405 			if (!reset)
406 				flags = settings.flags;
407 
408 			for (uint i=0; i < NUM_FLAGS; ++i)
409 				dialog.CheckBox( Lut::flags[i].dlg ).Check( flags[Lut::flags[i].type] );
410 
411 			ScreenShotFormat screenShotFormat = SCREENSHOT_PNG;
412 
413 			if (!reset)
414 				screenShotFormat = settings.screenShotFormat;
415 
416 			for (uint i=0; i < NUM_SCREENSHOTS; ++i)
417 				dialog.RadioButton( Lut::screenShots[i].dlg ).Check( Lut::screenShots[i].type == screenShotFormat );
418 
419 			UpdateLastVisited();
420 		}
421 
UpdateLastVisited() const422 		void Paths::UpdateLastVisited() const
423 		{
424 			bool unchecked = dialog.CheckBox( IDC_PATHS_IMAGE_LAST ).Unchecked();
425 			dialog.Control( IDC_PATHS_IMAGE ).Enable( unchecked );
426 			dialog.Control( IDC_PATHS_IMAGE_BROWSE ).Enable( unchecked );
427 		}
428 
OnInitDialog(Param &)429 		ibool Paths::OnInitDialog(Param&)
430 		{
431 			Update( false );
432 			return true;
433 		}
434 
OnCmdDefault(Param & param)435 		ibool Paths::OnCmdDefault(Param& param)
436 		{
437 			if (param.Button().Clicked())
438 				Update( true );
439 
440 			return true;
441 		}
442 
OnCmdBrowse(Param & param)443 		ibool Paths::OnCmdBrowse(Param& param)
444 		{
445 			if (param.Button().Clicked())
446 			{
447 				const uint id = IDC_PATHS_IMAGE + (param.Button().GetId() - IDC_PATHS_IMAGE_BROWSE);
448 				dialog.Edit( id ).Try() << Browser::SelectDirectory().Ptr();
449 			}
450 
451 			return true;
452 		}
453 
OnCmdLastVisited(Param & param)454 		ibool Paths::OnCmdLastVisited(Param& param)
455 		{
456 			if (param.Button().Clicked())
457 				UpdateLastVisited();
458 
459 			return true;
460 		}
461 
OnCmdOk(Param & param)462 		ibool Paths::OnCmdOk(Param& param)
463 		{
464 			if (param.Button().Clicked())
465 			{
466 				for (uint i=0; i < NUM_DIRS; ++i)
467 				{
468 					dialog.Edit( Lut::dirs[i].dlg ) >> settings.dirs[Lut::dirs[i].type];
469 					UpdateDirectory( i );
470 				}
471 
472 				for (uint i=0; i < NUM_FLAGS; ++i)
473 					settings.flags[Lut::flags[i].type] = dialog.CheckBox( Lut::flags[i].dlg ).Checked();
474 
475 				for (uint i=0; i < NUM_SCREENSHOTS; ++i)
476 				{
477 					if (dialog.RadioButton( Lut::screenShots[i].dlg ).Checked())
478 					{
479 						settings.screenShotFormat = static_cast<ScreenShotFormat>(Lut::screenShots[i].type);
480 						break;
481 					}
482 				}
483 
484 				dialog.Close();
485 			}
486 
487 			return true;
488 		}
489 	}
490 }
491