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 <cstring>
26 #include <new>
27 #include <algorithm>
28 #include "../NstMachine.hpp"
29 #include "../NstCartridge.hpp"
30 #include "../input/NstInpDevice.hpp"
31 #include "../input/NstInpAdapter.hpp"
32 #include "../input/NstInpPad.hpp"
33 #include "../input/NstInpZapper.hpp"
34 #include "../input/NstInpPaddle.hpp"
35 #include "../input/NstInpPowerPad.hpp"
36 #include "../input/NstInpPowerGlove.hpp"
37 #include "../input/NstInpMouse.hpp"
38 #include "../input/NstInpFamilyTrainer.hpp"
39 #include "../input/NstInpFamilyKeyboard.hpp"
40 #include "../input/NstInpSuborKeyboard.hpp"
41 #include "../input/NstInpDoremikkoKeyboard.hpp"
42 #include "../input/NstInpHoriTrack.hpp"
43 #include "../input/NstInpPachinko.hpp"
44 #include "../input/NstInpOekaKidsTablet.hpp"
45 #include "../input/NstInpKonamiHyperShot.hpp"
46 #include "../input/NstInpBandaiHyperShot.hpp"
47 #include "../input/NstInpCrazyClimber.hpp"
48 #include "../input/NstInpMahjong.hpp"
49 #include "../input/NstInpExcitingBoxing.hpp"
50 #include "../input/NstInpTopRider.hpp"
51 #include "../input/NstInpPokkunMoguraa.hpp"
52 #include "../input/NstInpPartyTap.hpp"
53 #include "../input/NstInpRob.hpp"
54 #include "../input/NstInpTurboFile.hpp"
55 #include "../input/NstInpBarcodeWorld.hpp"
56 
57 namespace Nes
58 {
59 	#ifdef NST_MSVC_OPTIMIZE
60 	#pragma optimize("s", on)
61 	#endif
62 
63 	namespace Api
64 	{
65 		Input::ControllerCaller Input::controllerCallback;
66 		Input::AdapterCaller Input::adapterCallback;
67 	}
68 
69 	namespace Core
70 	{
71 		namespace Input
72 		{
73 			Controllers::PollCaller2< Controllers::Pad               > Controllers::Pad::callback;
74 			Controllers::PollCaller1< Controllers::Zapper            > Controllers::Zapper::callback;
75 			Controllers::PollCaller1< Controllers::Paddle            > Controllers::Paddle::callback;
76 			Controllers::PollCaller1< Controllers::PowerPad          > Controllers::PowerPad::callback;
77 			Controllers::PollCaller1< Controllers::PowerGlove        > Controllers::PowerGlove::callback;
78 			Controllers::PollCaller1< Controllers::Mouse             > Controllers::Mouse::callback;
79 			Controllers::PollCaller1< Controllers::FamilyTrainer     > Controllers::FamilyTrainer::callback;
80 			Controllers::PollCaller3< Controllers::FamilyKeyboard    > Controllers::FamilyKeyboard::callback;
81 			Controllers::PollCaller3< Controllers::SuborKeyboard     > Controllers::SuborKeyboard::callback;
82 			Controllers::PollCaller3< Controllers::DoremikkoKeyboard > Controllers::DoremikkoKeyboard::callback;
83 			Controllers::PollCaller1< Controllers::HoriTrack         > Controllers::HoriTrack::callback;
84 			Controllers::PollCaller1< Controllers::Pachinko          > Controllers::Pachinko::callback;
85 			Controllers::PollCaller1< Controllers::VsSystem          > Controllers::VsSystem::callback;
86 			Controllers::PollCaller1< Controllers::OekaKidsTablet    > Controllers::OekaKidsTablet::callback;
87 			Controllers::PollCaller1< Controllers::KonamiHyperShot   > Controllers::KonamiHyperShot::callback;
88 			Controllers::PollCaller1< Controllers::BandaiHyperShot   > Controllers::BandaiHyperShot::callback;
89 			Controllers::PollCaller1< Controllers::CrazyClimber      > Controllers::CrazyClimber::callback;
90 			Controllers::PollCaller2< Controllers::Mahjong           > Controllers::Mahjong::callback;
91 			Controllers::PollCaller2< Controllers::ExcitingBoxing    > Controllers::ExcitingBoxing::callback;
92 			Controllers::PollCaller1< Controllers::TopRider          > Controllers::TopRider::callback;
93 			Controllers::PollCaller2< Controllers::PokkunMoguraa     > Controllers::PokkunMoguraa::callback;
94 			Controllers::PollCaller1< Controllers::PartyTap          > Controllers::PartyTap::callback;
95 			Controllers::PollCaller1< Controllers::KaraokeStudio     > Controllers::KaraokeStudio::callback;
96 
PowerPad()97 			Controllers::PowerPad::PowerPad() throw()
98 			{
99 				std::fill( sideA, sideA + NUM_SIDE_A_BUTTONS, false );
100 				std::fill( sideB, sideB + NUM_SIDE_B_BUTTONS, false );
101 			}
102 
PowerGlove()103 			Controllers::PowerGlove::PowerGlove() throw()
104 			{
105 				x = 0;
106 				y = 0;
107 				distance = 0;
108 				wrist = 0;
109 				gesture = GESTURE_OPEN;
110 			}
111 
FamilyTrainer()112 			Controllers::FamilyTrainer::FamilyTrainer() throw()
113 			{
114 				std::fill( sideA, sideA + NUM_SIDE_A_BUTTONS, false );
115 				std::fill( sideB, sideB + NUM_SIDE_B_BUTTONS, false );
116 			}
117 
FamilyKeyboard()118 			Controllers::FamilyKeyboard::FamilyKeyboard() throw()
119 			{
120 				std::memset( parts, 0x00, sizeof(parts) );
121 			}
122 
SuborKeyboard()123 			Controllers::SuborKeyboard::SuborKeyboard() throw()
124 			{
125 				std::memset( parts, 0x00, sizeof(parts) );
126 			}
127 
Controllers()128 			Controllers::Controllers() throw()
129 			{
130 			}
131 		}
132 	}
133 
134 	namespace Api
135 	{
ConnectController(const uint port,const Type type)136 		Result Input::ConnectController(const uint port,const Type type) throw()
137 		{
138 			Core::Input::Device* old = NULL;
139 
140 			switch (port)
141 			{
142 				case PORT_1:
143 				case PORT_2:
144 
145 					if (emulator.extPort->GetDevice( port ).GetType() == type)
146 					{
147 						return RESULT_NOP;
148 					}
149 					else switch (type)
150 					{
151 						case UNCONNECTED:
152 
153 							old = new (std::nothrow) Core::Input::Device( emulator.cpu );
154 							break;
155 
156 						case PAD1:
157 						case PAD2:
158 						case PAD3:
159 						case PAD4:
160 
161 							old = new (std::nothrow) Core::Input::Pad( emulator.cpu, uint(type) - PAD1 );
162 							break;
163 
164 						case ZAPPER:
165 
166 							old = new (std::nothrow) Core::Input::Zapper( emulator.cpu, emulator.ppu );
167 							break;
168 
169 						case PADDLE:
170 
171 							old = new (std::nothrow) Core::Input::Paddle( emulator.cpu, false );
172 							break;
173 
174 						case POWERPAD:
175 
176 							old = new (std::nothrow) Core::Input::PowerPad( emulator.cpu );
177 							break;
178 
179 						case POWERGLOVE:
180 
181 							old = new (std::nothrow) Core::Input::PowerGlove( emulator.cpu );
182 							break;
183 
184 						case MOUSE:
185 
186 							old = new (std::nothrow) Core::Input::Mouse( emulator.cpu );
187 							break;
188 
189 						case ROB:
190 
191 							old = new (std::nothrow) Core::Input::Rob( emulator.cpu, emulator.ppu );
192 							break;
193 
194 						default: return RESULT_ERR_INVALID_PARAM;
195 					}
196 
197 					if (old)
198 						old = &emulator.extPort->Connect( port, *old );
199 					else
200 						return RESULT_ERR_OUT_OF_MEMORY;
201 
202 					break;
203 
204 				case PORT_3:
205 				case PORT_4:
206 
207 					if (emulator.extPort->NumPorts() > 2)
208 					{
209 						if (emulator.extPort->GetDevice( port ).GetType() == type)
210 						{
211 							return RESULT_NOP;
212 						}
213 						else switch (type)
214 						{
215 							case UNCONNECTED:
216 
217 								if (emulator.extPort->GetDevice( port == PORT_3 ? PORT_4 : PORT_3 ).GetType() == UNCONNECTED)
218 								{
219 									Core::Input::Adapter* const adapter = new (std::nothrow) Core::Input::AdapterTwo
220 									(
221 										emulator.extPort->GetDevice(0),
222 										emulator.extPort->GetDevice(1),
223 										emulator.extPort->GetType()
224 									);
225 
226 									if (adapter == NULL)
227 										return RESULT_ERR_OUT_OF_MEMORY;
228 
229 									for (uint i=2; i < 4; ++i)
230 										delete &emulator.extPort->GetDevice(i);
231 
232 									delete emulator.extPort;
233 									emulator.extPort = adapter;
234 								}
235 								else if (NULL != (old = new (std::nothrow) Core::Input::Device( emulator.cpu )))
236 								{
237 									old = &emulator.extPort->Connect( port, *old );
238 								}
239 								else
240 								{
241 									return RESULT_ERR_OUT_OF_MEMORY;
242 								}
243 								break;
244 
245 							case PAD1:
246 							case PAD2:
247 							case PAD3:
248 							case PAD4:
249 
250 								if (NULL != (old = new (std::nothrow) Core::Input::Pad( emulator.cpu, uint(type) - PAD1 )))
251 								{
252 									old = &emulator.extPort->Connect( port, *old );
253 								}
254 								else
255 								{
256 									return RESULT_ERR_OUT_OF_MEMORY;
257 								}
258 								break;
259 
260 							default: return RESULT_ERR_INVALID_PARAM;
261 						}
262 					}
263 					else
264 					{
265 						switch (type)
266 						{
267 							case UNCONNECTED:
268 
269 								return RESULT_NOP;
270 
271 							case PAD1:
272 							case PAD2:
273 							case PAD3:
274 							case PAD4:
275 							{
276 								Core::Input::Device* const devices[2] =
277 								{
278 									new (std::nothrow) Core::Input::Device( emulator.cpu ),
279 									new (std::nothrow) Core::Input::Pad( emulator.cpu, uint(type) - PAD1 )
280 								};
281 
282 								Core::Input::Adapter* adapter;
283 
284 								if
285 								(
286 									devices[0] && devices[1] && NULL !=
287 									(
288 										adapter = new (std::nothrow) Core::Input::AdapterFour
289 										(
290 											emulator.extPort->GetDevice(0),
291 											emulator.extPort->GetDevice(1),
292 											*devices[port == PORT_3],
293 											*devices[port != PORT_3],
294 											emulator.extPort->GetType()
295 										)
296 									)
297 								)
298 								{
299 									delete emulator.extPort;
300 									emulator.extPort = adapter;
301 								}
302 								else
303 								{
304 									delete devices[0];
305 									delete devices[1];
306 
307 									return RESULT_ERR_OUT_OF_MEMORY;
308 								}
309 								break;
310 							}
311 
312 							default: return RESULT_ERR_INVALID_PARAM;
313 						}
314 					}
315 					break;
316 
317 				case EXPANSION_PORT:
318 
319 					if (emulator.expPort->GetType() == type)
320 					{
321 						return RESULT_NOP;
322 					}
323 					else switch (type)
324 					{
325 						case UNCONNECTED:       old = new (std::nothrow) Core::Input::Device( emulator.cpu );                        break;
326 						case PADDLE:            old = new (std::nothrow) Core::Input::Paddle( emulator.cpu, true );                  break;
327 						case FAMILYTRAINER:     old = new (std::nothrow) Core::Input::FamilyTrainer( emulator.cpu );                 break;
328 						case FAMILYKEYBOARD:    old = new (std::nothrow) Core::Input::FamilyKeyboard( emulator.cpu, true );          break;
329 						case SUBORKEYBOARD:     old = new (std::nothrow) Core::Input::SuborKeyboard( emulator.cpu );                 break;
330 						case DOREMIKKOKEYBOARD: old = new (std::nothrow) Core::Input::DoremikkoKeyboard( emulator.cpu );             break;
331 						case HORITRACK:         old = new (std::nothrow) Core::Input::HoriTrack( emulator.cpu );                     break;
332 						case PACHINKO:          old = new (std::nothrow) Core::Input::Pachinko( emulator.cpu );                      break;
333 						case OEKAKIDSTABLET:    old = new (std::nothrow) Core::Input::OekaKidsTablet( emulator.cpu );                break;
334 						case KONAMIHYPERSHOT:   old = new (std::nothrow) Core::Input::KonamiHyperShot( emulator.cpu );               break;
335 						case BANDAIHYPERSHOT:   old = new (std::nothrow) Core::Input::BandaiHyperShot( emulator.cpu, emulator.ppu ); break;
336 						case CRAZYCLIMBER:      old = new (std::nothrow) Core::Input::CrazyClimber( emulator.cpu );                  break;
337 						case MAHJONG:           old = new (std::nothrow) Core::Input::Mahjong( emulator.cpu );                       break;
338 						case EXCITINGBOXING:    old = new (std::nothrow) Core::Input::ExcitingBoxing( emulator.cpu );                break;
339 						case TOPRIDER:          old = new (std::nothrow) Core::Input::TopRider( emulator.cpu );                      break;
340 						case POKKUNMOGURAA:     old = new (std::nothrow) Core::Input::PokkunMoguraa( emulator.cpu );                 break;
341 						case PARTYTAP:          old = new (std::nothrow) Core::Input::PartyTap( emulator.cpu );                      break;
342 						case TURBOFILE:         old = new (std::nothrow) Core::Input::TurboFile( emulator.cpu );                     break;
343 						case BARCODEWORLD:      old = new (std::nothrow) Core::Input::BarcodeWorld( emulator.cpu );                  break;
344 
345 						default: return RESULT_ERR_INVALID_PARAM;
346 					}
347 
348 					if (old)
349 						std::swap( old, emulator.expPort );
350 					else
351 						return RESULT_ERR_OUT_OF_MEMORY;
352 
353 					break;
354 
355 				default: return RESULT_ERR_INVALID_PARAM;
356 			}
357 
358 			delete old;
359 			emulator.InitializeInputDevices();
360 			controllerCallback( port, type );
361 
362 			return RESULT_OK;
363 		}
364 
ConnectAdapter(Adapter adapter)365 		Result Input::ConnectAdapter(Adapter adapter) throw()
366 		{
367 			if (emulator.extPort->SetType( adapter ))
368 			{
369 				adapterCallback( adapter );
370 				return RESULT_OK;
371 			}
372 			else
373 			{
374 				return RESULT_NOP;
375 			}
376 		}
377 
AutoSelectController(uint port)378 		Result Input::AutoSelectController(uint port) throw()
379 		{
380 			if (port >= NUM_PORTS)
381 				return RESULT_ERR_INVALID_PARAM;
382 
383 			Type type;
384 
385 			if (emulator.image)
386 			{
387 				type = static_cast<Type>(emulator.image->GetDesiredController( port ));
388 			}
389 			else switch (port)
390 			{
391 				case PORT_1: type = PAD1;        break;
392 				case PORT_2: type = PAD2;        break;
393 				default:     type = UNCONNECTED; break;
394 			}
395 
396 			return ConnectController( port, type );
397 		}
398 
AutoSelectControllers()399 		Result Input::AutoSelectControllers() throw()
400 		{
401 			Result result = RESULT_NOP;
402 
403 			for (uint i=0; i < NUM_PORTS; ++i)
404 			{
405 				const Result tmp = AutoSelectController( i );
406 
407 				if (result > tmp)
408 					result = tmp;
409 			}
410 
411 			return result;
412 		}
413 
AutoSelectAdapter()414 		Result Input::AutoSelectAdapter() throw()
415 		{
416 			return ConnectAdapter( emulator.image ? static_cast<Adapter>(emulator.image->GetDesiredAdapter()) : ADAPTER_NES );
417 		}
418 
GetConnectedController(uint port) const419 		Input::Type Input::GetConnectedController(uint port) const throw()
420 		{
421 			if (port == EXPANSION_PORT)
422 				return emulator.expPort->GetType();
423 
424 			if (port < emulator.extPort->NumPorts())
425 				return emulator.extPort->GetDevice( port ).GetType();
426 
427 			return UNCONNECTED;
428 		}
429 
GetConnectedAdapter() const430 		Input::Adapter Input::GetConnectedAdapter() const throw()
431 		{
432 			return emulator.extPort->GetType();
433 		}
434 
IsControllerConnected(Type type) const435 		bool Input::IsControllerConnected(Type type) const throw()
436 		{
437 			if (emulator.expPort->GetType() == type)
438 				return true;
439 
440 			for (uint ports=emulator.extPort->NumPorts(), i=0; i < ports; ++i)
441 			{
442 				if (emulator.extPort->GetDevice(i).GetType() == type)
443 					return true;
444 			}
445 
446 			return false;
447 		}
448 	}
449 
450 	#ifdef NST_MSVC_OPTIMIZE
451 	#pragma optimize("", on)
452 	#endif
453 }
454