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