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 "NstObjectHeap.hpp" 27 #include "NstIoScreen.hpp" 28 #include "NstWindowUser.hpp" 29 #include "NstSystemKeyboard.hpp" 30 #include "NstManager.hpp" 31 #include "NstManagerFds.hpp" 32 #include "NstDialogFds.hpp" 33 34 namespace Nestopia 35 { 36 namespace Managers 37 { 38 NST_COMPILE_ASSERT 39 ( 40 IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 1 && 41 IDM_MACHINE_EXT_FDS_INSERT_DISK_2_SIDE_A == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 2 && 42 IDM_MACHINE_EXT_FDS_INSERT_DISK_2_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 3 && 43 IDM_MACHINE_EXT_FDS_INSERT_DISK_3_SIDE_A == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 4 && 44 IDM_MACHINE_EXT_FDS_INSERT_DISK_3_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 5 && 45 IDM_MACHINE_EXT_FDS_INSERT_DISK_4_SIDE_A == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 6 && 46 IDM_MACHINE_EXT_FDS_INSERT_DISK_4_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 7 && 47 IDM_MACHINE_EXT_FDS_INSERT_DISK_5_SIDE_A == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 8 && 48 IDM_MACHINE_EXT_FDS_INSERT_DISK_5_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 9 && 49 IDM_MACHINE_EXT_FDS_INSERT_DISK_6_SIDE_A == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 10 && 50 IDM_MACHINE_EXT_FDS_INSERT_DISK_6_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 11 && 51 IDM_MACHINE_EXT_FDS_INSERT_DISK_7_SIDE_A == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 12 && 52 IDM_MACHINE_EXT_FDS_INSERT_DISK_7_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 13 && 53 IDM_MACHINE_EXT_FDS_INSERT_DISK_8_SIDE_A == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 14 && 54 IDM_MACHINE_EXT_FDS_INSERT_DISK_8_SIDE_B == IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + 15 55 ); 56 57 struct Fds::Callbacks 58 { 59 NST_COMPILE_ASSERT( Nes::Fds::NUM_DRIVE_CALLBACKS == 3 ); 60 OnDiskAccessNumLockNestopia::Managers::Fds::Callbacks61 static void NST_CALLBACK OnDiskAccessNumLock(Nes::Fds::UserData,Nes::Fds::Motor motor) 62 { 63 System::Keyboard::ToggleIndicator( System::Keyboard::NUM_LOCK, motor ); 64 } 65 OnDiskAccessScrollLockNestopia::Managers::Fds::Callbacks66 static void NST_CALLBACK OnDiskAccessScrollLock(Nes::Fds::UserData,Nes::Fds::Motor motor) 67 { 68 System::Keyboard::ToggleIndicator( System::Keyboard::SCROLL_LOCK, motor ); 69 } 70 OnDiskAccessCapsLockNestopia::Managers::Fds::Callbacks71 static void NST_CALLBACK OnDiskAccessCapsLock(Nes::Fds::UserData,Nes::Fds::Motor motor) 72 { 73 System::Keyboard::ToggleIndicator( System::Keyboard::CAPS_LOCK, motor ); 74 } 75 OnDiskAccessScreenNestopia::Managers::Fds::Callbacks76 static void NST_CALLBACK OnDiskAccessScreen(Nes::Fds::UserData,Nes::Fds::Motor motor) 77 { 78 Io::Screen(15000) << (motor != Nes::Fds::MOTOR_OFF ? Resource::String(motor == Nes::Fds::MOTOR_READ ? IDS_SCREEN_FDS_READING : IDS_SCREEN_FDS_WRITING).Ptr() : L""); 79 } 80 }; 81 Fds(Emulator & e,const Configuration & cfg,Window::Menu & m,const Paths & paths)82 Fds::Fds(Emulator& e,const Configuration& cfg,Window::Menu& m,const Paths& paths) 83 : 84 Manager ( e, m, this, &Fds::OnEmuEvent ), 85 dialog ( new Window::Fds(e,cfg,paths) ) 86 { 87 static const Window::Menu::CmdHandler::Entry<Fds> commands[] = 88 { 89 { IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A, &Fds::OnCmdInsertDisk }, 90 { IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_B, &Fds::OnCmdInsertDisk }, 91 { IDM_MACHINE_EXT_FDS_INSERT_DISK_2_SIDE_A, &Fds::OnCmdInsertDisk }, 92 { IDM_MACHINE_EXT_FDS_INSERT_DISK_2_SIDE_B, &Fds::OnCmdInsertDisk }, 93 { IDM_MACHINE_EXT_FDS_INSERT_DISK_3_SIDE_A, &Fds::OnCmdInsertDisk }, 94 { IDM_MACHINE_EXT_FDS_INSERT_DISK_3_SIDE_B, &Fds::OnCmdInsertDisk }, 95 { IDM_MACHINE_EXT_FDS_INSERT_DISK_4_SIDE_A, &Fds::OnCmdInsertDisk }, 96 { IDM_MACHINE_EXT_FDS_INSERT_DISK_4_SIDE_B, &Fds::OnCmdInsertDisk }, 97 { IDM_MACHINE_EXT_FDS_INSERT_DISK_5_SIDE_A, &Fds::OnCmdInsertDisk }, 98 { IDM_MACHINE_EXT_FDS_INSERT_DISK_5_SIDE_B, &Fds::OnCmdInsertDisk }, 99 { IDM_MACHINE_EXT_FDS_INSERT_DISK_6_SIDE_A, &Fds::OnCmdInsertDisk }, 100 { IDM_MACHINE_EXT_FDS_INSERT_DISK_6_SIDE_B, &Fds::OnCmdInsertDisk }, 101 { IDM_MACHINE_EXT_FDS_INSERT_DISK_7_SIDE_A, &Fds::OnCmdInsertDisk }, 102 { IDM_MACHINE_EXT_FDS_INSERT_DISK_7_SIDE_B, &Fds::OnCmdInsertDisk }, 103 { IDM_MACHINE_EXT_FDS_INSERT_DISK_8_SIDE_A, &Fds::OnCmdInsertDisk }, 104 { IDM_MACHINE_EXT_FDS_INSERT_DISK_8_SIDE_B, &Fds::OnCmdInsertDisk }, 105 { IDM_MACHINE_EXT_FDS_CHANGE_SIDE, &Fds::OnCmdChangeSide }, 106 { IDM_MACHINE_EXT_FDS_EJECT_DISK, &Fds::OnCmdEjectDisk }, 107 { IDM_MACHINE_EXT_FDS_OPTIONS, &Fds::OnCmdOptions } 108 }; 109 110 menu.Commands().Add( this, commands ); 111 112 static const Window::Menu::PopupHandler::Entry<Fds> popups[] = 113 { 114 { Window::Menu::PopupHandler::Pos<IDM_POS_MACHINE,IDM_POS_MACHINE_EXT,IDM_POS_MACHINE_EXT_FDS>::ID, &Fds::OnMenuExtFds }, 115 { Window::Menu::PopupHandler::Pos<IDM_POS_MACHINE,IDM_POS_MACHINE_EXT,IDM_POS_MACHINE_EXT_FDS,IDM_POS_MACHINE_EXT_FDS_INSERT>::ID, &Fds::OnMenuExtFdsInsert } 116 }; 117 118 menu.Popups().Add( this, popups ); 119 120 UpdateSettings(); 121 UpdateMenuDisks(); 122 } 123 ~Fds()124 Fds::~Fds() 125 { 126 Nes::Fds::driveCallback.Unset(); 127 } 128 Save(Configuration & cfg) const129 void Fds::Save(Configuration& cfg) const 130 { 131 dialog->Save( cfg ); 132 } 133 UpdateSettings() const134 void Fds::UpdateSettings() const 135 { 136 const Window::Fds::Led led = dialog->GetLed(); 137 138 Nes::Fds::driveCallback.Set 139 ( 140 led == Window::Fds::LED_SCREEN ? Callbacks::OnDiskAccessScreen : 141 led == Window::Fds::LED_NUM_LOCK ? Callbacks::OnDiskAccessNumLock : 142 led == Window::Fds::LED_CAPS_LOCK ? Callbacks::OnDiskAccessCapsLock : 143 led == Window::Fds::LED_SCROLL_LOCK ? Callbacks::OnDiskAccessScrollLock : 144 NULL, 145 NULL 146 ); 147 } 148 UpdateMenuDisks() const149 void Fds::UpdateMenuDisks() const 150 { 151 const Window::Menu::Item subMenu 152 ( 153 menu[IDM_POS_MACHINE][IDM_POS_MACHINE_EXT][IDM_POS_MACHINE_EXT_FDS][IDM_POS_MACHINE_EXT_FDS_INSERT] 154 ); 155 156 subMenu.Enable( emulator.IsFds() ); 157 subMenu.Clear(); 158 159 if (uint numSides = Nes::Fds(emulator).GetNumSides()) 160 { 161 NST_VERIFY( numSides <= MAX_SIDES ); 162 163 if (numSides > MAX_SIDES) 164 numSides = MAX_SIDES; 165 166 for (uint i=0; i < numSides; ++i) 167 { 168 menu.Insert 169 ( 170 subMenu[i], 171 IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + i, 172 Resource::String( (i % 2) ? IDS_FDS_DISK_SIDE_B : IDS_FDS_DISK_SIDE_A ).Invoke( wchar_t('1' + i / 2) ) 173 ); 174 } 175 } 176 } 177 CanInsertDisk() const178 bool Fds::CanInsertDisk() const 179 { 180 return 181 ( 182 emulator.IsFds() && 183 !emulator.IsLocked() && 184 emulator.GetPlayer() == Emulator::MASTER 185 ); 186 } 187 CanEjectDisk() const188 bool Fds::CanEjectDisk() const 189 { 190 return 191 ( 192 Nes::Fds(emulator).IsAnyDiskInserted() && 193 !emulator.IsLocked() && 194 emulator.GetPlayer() == Emulator::MASTER 195 ); 196 } 197 CanChangeSide() const198 bool Fds::CanChangeSide() const 199 { 200 return 201 ( 202 Nes::Fds(emulator).CanChangeDiskSide() && 203 !emulator.IsLocked() && 204 emulator.GetPlayer() == Emulator::MASTER 205 ); 206 } 207 OnMenuExtFds(const Window::Menu::PopupHandler::Param & param)208 void Fds::OnMenuExtFds(const Window::Menu::PopupHandler::Param& param) 209 { 210 param.menu[ IDM_MACHINE_EXT_FDS_CHANGE_SIDE ].Enable( !param.show || CanChangeSide() ); 211 param.menu[ IDM_MACHINE_EXT_FDS_EJECT_DISK ].Enable( !param.show || CanEjectDisk() ); 212 } 213 OnMenuExtFdsInsert(const Window::Menu::PopupHandler::Param & param)214 void Fds::OnMenuExtFdsInsert(const Window::Menu::PopupHandler::Param& param) 215 { 216 if (uint numSides = Nes::Fds(emulator).GetNumSides()) 217 { 218 if (numSides > MAX_SIDES) 219 numSides = MAX_SIDES; 220 221 const bool checked = (param.show && Nes::Fds(emulator).IsAnyDiskInserted()); 222 223 param.menu[IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + (checked ? Nes::Fds(emulator).GetCurrentDisk() * 2 + Nes::Fds(emulator).GetCurrentDiskSide() : 0)].Check 224 ( 225 IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A, 226 IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + numSides, 227 checked 228 ); 229 230 const bool enabled = (!checked || CanInsertDisk()); 231 232 do 233 { 234 param.menu[IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A + --numSides].Enable( enabled ); 235 } 236 while (numSides); 237 } 238 } 239 OnCmdInsertDisk(uint disk)240 void Fds::OnCmdInsertDisk(uint disk) 241 { 242 if (CanInsertDisk()) 243 { 244 emulator.SendCommand( Emulator::COMMAND_DISK_INSERT, disk - IDM_MACHINE_EXT_FDS_INSERT_DISK_1_SIDE_A ); 245 Resume(); 246 } 247 } 248 OnCmdChangeSide(uint)249 void Fds::OnCmdChangeSide(uint) 250 { 251 if (CanChangeSide()) 252 { 253 emulator.SendCommand( Emulator::COMMAND_DISK_INSERT, Nes::Fds(emulator).GetCurrentDisk() * 2 + (Nes::Fds(emulator).GetCurrentDiskSide() == 0) ); 254 Resume(); 255 } 256 } 257 OnCmdEjectDisk(uint)258 void Fds::OnCmdEjectDisk(uint) 259 { 260 if (CanEjectDisk()) 261 { 262 emulator.SendCommand( Emulator::COMMAND_DISK_EJECT ); 263 Resume(); 264 } 265 } 266 OnCmdOptions(uint)267 void Fds::OnCmdOptions(uint) 268 { 269 dialog->Open(); 270 UpdateSettings(); 271 } 272 OnEmuEvent(const Emulator::Event event,const Emulator::Data data)273 void Fds::OnEmuEvent(const Emulator::Event event,const Emulator::Data data) 274 { 275 switch (event) 276 { 277 case Emulator::EVENT_DISK_INSERT: 278 case Emulator::EVENT_DISK_EJECT: 279 280 Io::Screen() << Resource::String( event == Emulator::EVENT_DISK_EJECT ? IDS_SCREEN_DISK_EJECTED : (data % 2U ? IDS_SCREEN_DISK_SIDE_B_INSERTED : IDS_SCREEN_DISK_SIDE_A_INSERTED) ).Invoke( wchar_t('1' + data / 2U) ); 281 break; 282 283 case Emulator::EVENT_LOAD: 284 case Emulator::EVENT_UNLOAD: 285 286 UpdateMenuDisks(); 287 288 if (emulator.IsFds()) 289 emulator.SendCommand( Emulator::COMMAND_DISK_INSERT ); 290 291 break; 292 293 case Emulator::EVENT_DISK_NONSTANDARD: 294 295 Window::User::Warn( IDS_FDS_NONSTANDARD ); 296 break; 297 298 case Emulator::EVENT_DISK_QUERY_BIOS: 299 300 dialog->QueryBiosFile(); 301 break; 302 303 case Emulator::EVENT_NETPLAY_MODE: 304 305 menu[IDM_MACHINE_EXT_FDS_OPTIONS].Enable( !data ); 306 break; 307 } 308 } 309 } 310 } 311