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