1 /*******************************************************************
2  *
3  *    DESCRIPTION: 	consbind.cpp
4  *
5  *		Ability to kind keystrokes to strings so they appear in the
6  *	console when you hit the key.  Initial implementation went through
7  * the WM_KEYDOWN hook so that we get debouncing and typematic action
8  * for free; subsequently added an implementation based on the KeyboadInput[]
9  * array.
10  *
11  *    AUTHOR: David Malcolm
12  *
13  *    HISTORY:  Created 6/4/98
14  *
15  *******************************************************************/
16 
17 /* Includes ********************************************************/
18 #include <ctype.h>
19 
20 #include "3dc.h"
21 #include "consbind.hpp"
22 
23 	#if KeyBindingUses_KEY_ID
24 		#include "iofocus.h"
25 		#include "scstring.hpp"
26 		#include "strtab.hpp"
27 	#endif
28 
29 
30 	#define UseLocalAssert Yes
31 	#include "ourasert.h"
32 #include "avp_menus.h"
33 
34 /* Version settings ************************************************/
35 
36 /* Constants *******************************************************/
37 	#if KeyBindingUses_WM_KEYDOWN
38 		#define MAX_VALUE_BINDABLE_KEY (VK_DIVIDE)
39 	#endif
40 
41 	#if KeyBindingUses_KEY_ID
42 		#define MAX_VALUE_BINDABLE_KEY (MAX_NUMBER_OF_INPUT_KEYS)
43 	#endif
44 /* Macros **********************************************************/
45 
46 /* Imported function prototypes ************************************/
47 
48 /* Imported data ***************************************************/
49 #ifdef __cplusplus
50 	extern "C"
51 	{
52 #endif
53 		extern unsigned char KeyboardInput[];
54 		extern unsigned char DebouncedKeyboardInput[];
55 
56 #ifdef __cplusplus
57 	};
58 #endif
59 
60 
61 
62 /* Exported globals ************************************************/
63 
64 /* Internal type definitions ***************************************/
65 typedef enum TEXTSTRING_ID TextID;
66 
67 /* Internal function prototypes ************************************/
68 
69 /* Internal globals ************************************************/
70 
71 /* Exported function definitions ***********************************/
72 // class KeyBinding
73 // public:
74 
75 // static
76 void
ParseBindCommand(ProjChar * pProjCh_ToParse)77 KeyBinding :: ParseBindCommand
78 (
79 	ProjChar* pProjCh_ToParse
80 )
81 {
82 	GLOBALASSERT(pProjCh_ToParse);
83 
84 	BindableKey theKey;
85 	ProjChar* pProjCh_FollowingTheKey;
86 
87 	if
88 	(
89 		KeyBinding :: ParseBindCommand
90 		(
91 			theKey, // BindableKey& theKey_Out,
92 			&pProjCh_FollowingTheKey, // ProjChar** ppProjCh_Out,
93 				// returns where in the input string to continue processing
94 
95 			pProjCh_ToParse // ProjChar* pProjCh_In
96 		)
97 	)
98 	{
99 		SCString* pSCString_ToBind = new SCString(pProjCh_FollowingTheKey);
100 
101 		// Create the KeyBinding object:
102 		KeyBinding* pNewBinding;
103 		pNewBinding = new KeyBinding
104 		(
105 			theKey,
106 			pSCString_ToBind
107 		);
108 
109 		// Feedback:
110 		{
111 			SCString* pSCString_1 = new SCString("BOUND \"");
112 				// LOCALISEME
113 
114 			SCString* pSCString_2 = new SCString("\" TO ");
115 
116 			SCString* pSCString_3 = MakeStringForKey
117 			(
118 				theKey
119 			);
120 
121 			SCString* pSCString_Feedback = new SCString
122 			(
123 				pSCString_1,
124 				pSCString_ToBind,
125 				pSCString_2,
126 				pSCString_3
127 			);
128 
129 			pSCString_Feedback -> SendToScreen();
130 
131 			pSCString_Feedback -> R_Release();
132 			pSCString_3 -> R_Release();
133 			pSCString_2 -> R_Release();
134 			pSCString_1 -> R_Release();
135 		}
136 
137 		pSCString_ToBind -> R_Release();
138 	}
139 	else
140 	{
141 		// Can't recognise which key is to be bound to;
142 		// provide an error message
143 			// UNWRITTEN
144 	}
145 }
146 
147 // static
148 void
ParseUnbindCommand(ProjChar * pProjCh_ToParse)149 KeyBinding :: ParseUnbindCommand
150 (
151 	ProjChar* pProjCh_ToParse
152 )
153 {
154 	GLOBALASSERT(pProjCh_ToParse);
155 
156 	// Iterate through leading whitespace:
157 	{
158 		while
159 		(
160 			*pProjCh_ToParse
161 		)
162 		{
163 			// LOCALISEME:
164 			if (!isspace(*pProjCh_ToParse))
165 			{
166 				break;
167 			}
168 			pProjCh_ToParse++;
169 		}
170 	}
171 
172 	// Scan through the string, trying to find matches against strings for keys
173 	// We will use the longest match:
174 	{
175 		OurBool bGotMatch = No;
176 		unsigned int LongestMatch = 0;
177 		BindableKey theKey_ToUnbind = (BindableKey)0;
178 
179 		for (int i=0;i<MAX_VALUE_BINDABLE_KEY; i++)
180 		{
181 			BindableKey theKey = (BindableKey)i;
182 
183 			SCString* pSCString_TestKey = MakeStringForKey(theKey);
184 
185 			unsigned int LengthOfTestString = pSCString_TestKey -> GetNumChars();
186 
187 			if (LengthOfTestString > 0)
188 			{
189 				if
190 				(
191 					0 == _strnicmp
192 					(
193 						pSCString_TestKey -> pProjCh(),
194 						pProjCh_ToParse,
195 						LengthOfTestString
196 					)
197 						// LOCALISEME
198 				)
199 				{
200 					// Then we have a match; see if it's longer than
201 					// what's come before...
202 					if (LengthOfTestString>LongestMatch)
203 					{
204 						LongestMatch = LengthOfTestString;
205 
206 						theKey_ToUnbind = theKey;
207 						bGotMatch = Yes;
208 
209 					}
210 				}
211 			}
212 
213 			pSCString_TestKey -> R_Release();
214 		}
215 
216 		if (bGotMatch)
217 		{
218 			// Then we must get rid of all bindings with this as their key:
219 			for
220 			(
221 				LIF<KeyBinding*> oi(&List_pKeyBindings);
222 				!oi . done();
223 			)
224 			{
225 				GLOBALASSERT(oi());
226 				if ( oi() -> theKey == theKey_ToUnbind )
227 				{
228 					oi . delete_current();
229 				}
230 				else
231 				{
232 					oi . next();
233 				}
234 			}
235 		}
236 	}
237 }
238 
239 #if 0
240 // static
241 void
242 KeyBinding :: AttemptToBind
243 (
244 	SCString* pSCString_Key, // description of key
245 	SCString* pSCString_ToBind // string to be bound
246 )
247 {
248 	/* PRECONDITION */
249 	{
250 		GLOBALASSERT( pSCString_Key );
251 		GLOBALASSERT( pSCString_ToBind );
252 	}
253 
254 	/* CODE */
255 	{
256 		BindableKey theBindableKey_ToUse;
257 
258 		if
259 		(
260 			!bGetKeyForString
261 			(
262 				theBindableKey_ToUse, // BindableKey& theKey_Out,
263 				pSCString_Key -> pProjCh()
264 			)
265 		)
266 		{
267 			// Can't recognise which key is to be bound to;
268 			// provide an error message
269 			ErrorDontRecogniseKey(pSCString_Key);
270 
271 			return;
272 		}
273 
274 		// Create the KeyBinding object:
275 		new KeyBinding
276 		(
277 			theBindableKey_ToUse,
278 			pSCString_ToBind
279 		);
280 	}
281 }
282 
283 // static
284 void
285 KeyBinding :: AttemptToUnbind
286 (
287 	SCString* pSCString_Key // description of key
288 )
289 {
290 	/* PRECONDITION */
291 	{
292 		GLOBALASSERT( pSCString_Key );
293 	}
294 
295 	/* CODE */
296 	{
297 		BindableKey theBindableKey_ToUse;
298 
299 		if
300 		(
301 			!bGetKeyForString
302 			(
303 				theBindableKey_ToUse, // BindableKey& theKey_Out,
304 				pSCString_Key -> pProjCh()
305 			)
306 		)
307 		{
308 			// Can't recognise which key is to be bound to;
309 			// provide an error message
310 			ErrorDontRecogniseKey(pSCString_Key);
311 
312 			return;
313 		}
314 
315 		// Find and remove any bindings to that key; note the number:
316 
317 		// Provide feedback:
318 	}
319 }
320 #endif
321 
322 
323 // static
324 void
UnbindAll(void)325 KeyBinding :: UnbindAll(void)
326 {
327 	SCString* pSCString_Feedback = new SCString("DESTROYING ALL KEY BINDINGS");
328 		// LOCALISEME
329 
330 	pSCString_Feedback -> SendToScreen();
331 
332 	pSCString_Feedback -> R_Release();
333 
334 	while
335 	(
336 		List_pKeyBindings . size() > 0
337 	)
338 	{
339 		delete List_pKeyBindings . first_entry();
340 			// The destructor for the KeyBinding will remove
341 			// it from the list and hence the list will shrink.
342 	}
343 }
344 
345 // static
346 void
ListAllBindings(void)347 KeyBinding :: ListAllBindings(void)
348 {
349 	SCString* pSCString_Feedback = new SCString("LIST OF ALL KEY BINDINGS:");
350 		// LOCALISEME
351 
352 	pSCString_Feedback -> SendToScreen();
353 
354 	pSCString_Feedback -> R_Release();
355 
356 	for
357 	(
358 		CLIF<KeyBinding*> oi(&List_pKeyBindings);
359 		!oi . done();
360 		oi . next()
361 	)
362 	{
363 		GLOBALASSERT(oi());
364 		oi() -> ListThis();
365 	}
366 }
367 
368 // static
WriteToConfigFile(char * Filename)369 void KeyBinding :: WriteToConfigFile(char* Filename)
370 {
371 	// overwrites the file with a batch file that'll
372 	// restore current bindings
373 
374 	GLOBALASSERT(Filename);
375 
376 	FILE* pFile = OpenGameFile(Filename, FILEMODE_WRITEONLY, FILETYPE_CONFIG);
377 
378 	if (!pFile)
379 	{
380 		return;
381 			// and don't destroy the bindings, since we won't
382 			// restore them next time into game
383 	}
384 
385 	fprintf(pFile,"#This file generated by AVP\n");
386 		// LOCALISEME
387 
388 	for
389 	(
390 		LIF<KeyBinding*> oi(&List_pKeyBindings);
391 		!oi.done();
392 		oi.next()
393 	)
394 	{
395 		SCString* pSCString_Key = MakeStringForKey
396 		(
397 			oi() -> theKey
398 		);
399 
400 		fprintf
401 		(
402 			pFile,
403 			"BIND %s %s\n",
404 			pSCString_Key -> pProjCh(),
405 			oi() -> pSCString_ToOutput -> pProjCh()
406 		);
407 
408 		pSCString_Key->R_Release();
409 	}
410 
411 	fclose(pFile);
412 
413 	// Destroy all the current bindings so we don't get a duplicate
414 	// set next time the batch file fires:
415 	{
416 		while
417 		(
418 			List_pKeyBindings . size() > 0
419 		)
420 		{
421 			delete List_pKeyBindings . first_entry();
422 				// The destructor for the KeyBinding will remove
423 				// it from the list and hence the list will shrink.
424 		}
425 	}
426 }
427 
428 
429 #if KeyBindingUses_WM_KEYDOWN
430 // static
431 void
Process_WM_KEYDOWN(WPARAM wParam)432 KeyBinding :: Process_WM_KEYDOWN
433 (
434 	WPARAM wParam
435 )
436 {
437 
438 	// Iterate through the list, finding matches.
439 	// We must process the matching objects later, in case they are bound to things
440 	// which modify the list.  So we built a list of pending SCString*'s.
441 	// (so BIND X UNBIND X won't kill the program.  I hope)
442 
443 	// The list of pending SCStrings has been made static to the class in an attempt to
444 	// optimise this function
445 
446 	// Ensure it starts off empty (which it would if it was a local):
447 	GLOBALASSERT( 0 == PendingList . NumEntries() );
448 
449 	for
450 	(
451 		LIF<KeyBinding*>oi(&List_pKeyBindings);
452 		!oi.done();
453 		oi.next()
454 	)
455 	{
456 		// Note that the BindableKey type is type-equal to WPARAM if this function
457 		// exists:
458 		if
459 		(
460 			oi() -> theKey == wParam
461 		)
462 		{
463 			// Add _the_string_ to the pending list (with a reference)
464 			// The reference is added by the RefList template, and this ensures
465 			// the string stays alive whatever that pesky user does:
466 			GLOBALASSERT( oi() -> pSCString_ToOutput );
467 			PendingList . AddToEnd
468 			(
469 				*( oi() -> pSCString_ToOutput )
470 			);
471 
472 			if (bEcho)
473 			{
474 				oi() -> pSCString_ToOutput -> SendToScreen();
475 			}
476 		}
477 	}
478 
479 	// Iterate through the pending list, destructively reading the
480 	// "references" from the front:
481 	{
482 		SCString* pSCString;
483 
484 		// The assignment in this boolean expression is deliberate:
485 		while
486 		(
487 			NULL != (pSCString = PendingList . GetYourFirst())
488 		)
489 		{
490 			pSCString -> ProcessAnyCheatCodes();
491 			pSCString -> R_Release();
492 		}
493 	}
494 
495 	// Ensure the pending list finishes off empty
496 	// (since we're pretending it's a local variable):
497 	GLOBALASSERT( 0 == PendingList . NumEntries() );
498 
499 }
500 #endif
501 
502 #if KeyBindingUses_KEY_ID
503 // static
504 void
Maintain(void)505 KeyBinding :: Maintain(void)
506 {
507 	// Only process if we're in a running-around type of mode
508 	// rather than typing at the console:
509 	if
510 	(
511 		IOFOCUS_AcceptControls()
512 	)
513 	{
514 		// Iterate through the list, finding matches.
515 		// We must process the matching objects later, in case they are bound to things
516 		// which modify the list.  So we built a list of pending SCString*'s.
517 		// (so BIND X UNBIND X won't kill the program.  I hope)
518 
519 		// The list of pending SCStrings has been made static to the class in an attempt to
520 		// optimise this function
521 
522 		// Ensure it starts off empty (which it would if it was a local):
523 		GLOBALASSERT( 0 == PendingList . NumEntries() );
524 
525 		for
526 		(
527 			LIF<KeyBinding*>oi(&List_pKeyBindings);
528 			!oi.done();
529 			oi.next()
530 		)
531 		{
532 			// Note that the BindableKey type is type-equal to enum KEY_ID if this function
533 			// exists:
534 			if
535 			(
536 				DebouncedKeyboardInput[ oi() -> theKey ]
537 			)
538 			{
539 				// Add _the_string_ to the pending list (with a reference)
540 				// The reference is added by the RefList template, and this ensures
541 				// the string stays alive whatever that pesky user does:
542 				GLOBALASSERT( oi() -> pSCString_ToOutput );
543 				PendingList . AddToEnd
544 				(
545 					*( oi() -> pSCString_ToOutput )
546 				);
547 
548 				if (bEcho)
549 				{
550 					oi() -> pSCString_ToOutput -> SendToScreen();
551 				}
552 			}
553 		}
554 
555 		// Iterate through the pending list, destructively reading the
556 		// "references" from the front:
557 		{
558 			SCString* pSCString;
559 
560 			// The assignment in this boolean expression is deliberate:
561 			while
562 			(
563 				NULL != (pSCString = PendingList . GetYourFirst())
564 			)
565 			{
566 				pSCString -> ProcessAnyCheatCodes();
567 				pSCString -> R_Release();
568 			}
569 		}
570 
571 		// Ensure the pending list finishes off empty
572 		// (since we're pretending it's a local variable):
573 		GLOBALASSERT( 0 == PendingList . NumEntries() );
574 	}
575 }
576 #endif
577 
578 
579 // private:
580 // Private ctor/dtor; to be called only by static fns of the class:
KeyBinding(BindableKey theKey_ToUse,SCString * pSCString_ToBind)581 KeyBinding :: KeyBinding
582 (
583 	BindableKey theKey_ToUse,
584 	SCString* pSCString_ToBind
585 ) : theKey(theKey_ToUse),
586 	pSCString_ToOutput(pSCString_ToBind)
587 {
588 	GLOBALASSERT(pSCString_ToOutput);
589 
590 	pSCString_ToOutput -> R_AddRef();
591 
592 	List_pKeyBindings . add_entry(this);
593 }
594 
~KeyBinding()595 KeyBinding :: ~KeyBinding()
596 {
597 	List_pKeyBindings . delete_entry(this);
598 
599 	pSCString_ToOutput -> R_Release();
600 }
601 
602 #if 0
603 // static
604 OurBool
605 KeyBinding :: bGetKeyForString
606 (
607 	BindableKey& theKey_Out,
608 	const ProjChar* const pProjCh_In
609 )
610 {
611 	#if KeyBindingUses_WM_KEYDOWN
612 	{
613 		// takes an input string and tries to figure out
614 		// what the corresponding WPARAM would be...
615 		// Returns truth if it manages to get a sensible value
616 		// into the output area.
617 
618 		GLOBALASSERT( pProjCh_In );
619 
620 		#if 1
621 		theKey_Out = pProjCh_In[0];
622 		return Yes;
623 			// LOCALISEME
624 		#else
625 
626 		return No;
627 			// for now
628 		#endif
629 	}
630 	#endif
631 
632 	#if KeyBindingUses_KEY_ID
633 	{
634 		theKey_Out = KEY_NUMPADDEL;
635 		return Yes;
636 			// for now
637 	}
638 	#endif
639 }
640 #endif
641 
642 // static
643 void
ErrorDontRecogniseKey(SCString * pSCString_Key)644 KeyBinding :: ErrorDontRecogniseKey( SCString* pSCString_Key )
645 {
646 	GLOBALASSERT( pSCString_Key );
647 
648 	SCString* pSCString_1 = new SCString("UNRECOGNISED KEY: \"");
649 	SCString* pSCString_2 = new SCString("\"");
650 		// LOCALISEME
651 
652 	SCString* pSCString_Feedback = new SCString
653 	(
654 		pSCString_1,
655 		pSCString_Key,
656 		pSCString_2
657 	);
658 
659 	pSCString_Feedback -> SendToScreen();
660 
661 	pSCString_Feedback -> R_Release();
662 	pSCString_2 -> R_Release();
663 	pSCString_1 -> R_Release();
664 }
665 
666 void
ListThis(void) const667 KeyBinding :: ListThis(void) const
668 {
669 	// used by ListAllBindings()
670 	SCString* pSCString_1 = MakeStringForKey(theKey);
671 	SCString* pSCString_2 = new SCString(" -> \"");
672 		// LOCALISEME
673 	SCString* pSCString_3 = new SCString("\"");
674 
675 	SCString* pSCString_Feedback = new SCString
676 	(
677 		pSCString_1,
678 		pSCString_2,
679 		pSCString_ToOutput,
680 		pSCString_3
681 	);
682 
683 	pSCString_Feedback -> SendToScreen();
684 
685 	pSCString_Feedback -> R_Release();
686 	pSCString_3 -> R_Release();
687 	pSCString_2 -> R_Release();
688 	pSCString_1 -> R_Release();
689 
690 }
691 
692 
GetKeyLabel(int inPhysicalKey,TextID & outTextID)693 static int GetKeyLabel(int inPhysicalKey, TextID& outTextID)
694 {
695 	// takes a physical method key and attempts to find a text
696 	// string to use for it, returning whether it does.
697 	// If it fails, output area is untouched
698 	if (inPhysicalKey>=KEY_LEFT && inPhysicalKey<=KEY_MOUSEWHEELDOWN)
699 	{
700 		outTextID = (enum TEXTSTRING_ID) (TEXTSTRING_KEYS_LEFT + (inPhysicalKey-KEY_LEFT));
701 		return Yes;
702 	}
703 	else return No;
704 
705 	#if 0
706 	switch (inPhysicalKey)
707 	{
708 		case KEY_UP: outTextID = TEXTSTRING_KEYS_UP; return Yes;
709 		case KEY_DOWN: outTextID = TEXTSTRING_KEYS_DOWN; return Yes;
710 		case KEY_LEFT: outTextID = TEXTSTRING_KEYS_LEFT; return Yes;
711 		case KEY_RIGHT: outTextID = TEXTSTRING_KEYS_RIGHT; return Yes;
712 		case KEY_CR: outTextID = TEXTSTRING_KEYS_RETURN; return Yes;
713 		case KEY_TAB: outTextID = TEXTSTRING_KEYS_TAB; return Yes;
714 		case KEY_INS: outTextID = TEXTSTRING_KEYS_INSERT; return Yes;
715 		case KEY_DEL: outTextID = TEXTSTRING_KEYS_DELETE; return Yes;
716 		case KEY_END: outTextID = TEXTSTRING_KEYS_END; return Yes;
717 		case KEY_HOME: outTextID = TEXTSTRING_KEYS_HOME; return Yes;
718 		case KEY_PAGEUP: outTextID = TEXTSTRING_KEYS_PGUP; return Yes;
719 		case KEY_PAGEDOWN: outTextID = TEXTSTRING_KEYS_PGDOWN; return Yes;
720 		case KEY_BACKSPACE: outTextID = TEXTSTRING_KEYS_BACKSP; return Yes;
721 		case KEY_COMMA: outTextID = TEXTSTRING_KEYS_COMMA; return Yes;
722 		case KEY_FSTOP: outTextID = TEXTSTRING_KEYS_PERIOD; return Yes;
723 		case KEY_SPACE: outTextID = TEXTSTRING_KEYS_SPACE; return Yes;
724 		case KEY_LMOUSE: outTextID = TEXTSTRING_KEYS_LMOUSE; return Yes;
725 		case KEY_RMOUSE: outTextID = TEXTSTRING_KEYS_RMOUSE; return Yes;
726 		case KEY_LEFTALT: outTextID = TEXTSTRING_KEYS_LALT; return Yes;
727 		case KEY_RIGHTALT: outTextID = TEXTSTRING_KEYS_RALT; return Yes;
728 		case KEY_LEFTCTRL: outTextID = TEXTSTRING_KEYS_LCTRL; return Yes;
729 		case KEY_RIGHTCTRL: outTextID = TEXTSTRING_KEYS_RCTRL; return Yes;
730 		case KEY_LEFTSHIFT: outTextID = TEXTSTRING_KEYS_LSHIFT; return Yes;
731 		case KEY_RIGHTSHIFT: outTextID = TEXTSTRING_KEYS_RSHIFT; return Yes;
732 		case KEY_CAPS: outTextID = TEXTSTRING_KEYS_CAPS; return Yes;
733 		case KEY_NUMLOCK: outTextID = TEXTSTRING_KEYS_NUMLOCK; return Yes;
734 		case KEY_SCROLLOK: outTextID = TEXTSTRING_KEYS_SCRLOCK; return Yes;
735 		case KEY_NUMPAD0: outTextID = TEXTSTRING_KEYS_PAD0; return Yes;
736 		case KEY_NUMPAD1: outTextID = TEXTSTRING_KEYS_PAD1; return Yes;
737 		case KEY_NUMPAD2: outTextID = TEXTSTRING_KEYS_PAD2; return Yes;
738 		case KEY_NUMPAD3: outTextID = TEXTSTRING_KEYS_PAD3; return Yes;
739 		case KEY_NUMPAD4: outTextID = TEXTSTRING_KEYS_PAD4; return Yes;
740 		case KEY_NUMPAD5: outTextID = TEXTSTRING_KEYS_PAD5; return Yes;
741 		case KEY_NUMPAD6: outTextID = TEXTSTRING_KEYS_PAD6; return Yes;
742 		case KEY_NUMPAD7: outTextID = TEXTSTRING_KEYS_PAD7; return Yes;
743 		case KEY_NUMPAD8: outTextID = TEXTSTRING_KEYS_PAD8; return Yes;
744 		case KEY_NUMPAD9: outTextID = TEXTSTRING_KEYS_PAD9; return Yes;
745 		case KEY_NUMPADSUB: outTextID = TEXTSTRING_KEYS_PADSUB; return Yes;
746 		case KEY_NUMPADADD: outTextID = TEXTSTRING_KEYS_PADADD; return Yes;
747 		case KEY_NUMPADDEL: outTextID = TEXTSTRING_KEYS_PADDEL; return Yes;
748 		default: return No;
749 	}
750 	#endif
751 }
752 
GetMethodString(BindableKey inPhysicalKey)753 static SCString* GetMethodString(BindableKey inPhysicalKey)
754 {
755 	TextID theTextID;
756 
757 	if
758 	(
759 		GetKeyLabel
760 		(
761 			inPhysicalKey,
762 			theTextID // TextID& outTextID
763 		)
764 	)
765 	{
766 		return &StringTable :: GetSCString(theTextID);
767 	}
768 	else
769 	{
770 		ProjChar theProjChar[2];
771 
772 		if (inPhysicalKey >= KEY_A && inPhysicalKey <= KEY_Z)
773 		{
774 			theProjChar[0] = ProjChar(int(inPhysicalKey) - KEY_A + 'A');
775 		}
776 		else if (inPhysicalKey >= KEY_0 && inPhysicalKey <= KEY_9)
777 		{
778 			theProjChar[0] = ProjChar(int(inPhysicalKey) - KEY_0 + '0');
779 		}
780 		else
781 		{
782 			theProjChar[0] = 0;
783 		}
784 
785 		theProjChar[1] = 0;
786 
787 		return new SCString(theProjChar);
788 	}
789 }
790 
791 // static
792 SCString*
MakeStringForKey(BindableKey theKey)793 KeyBinding :: MakeStringForKey
794 (
795 	BindableKey theKey
796 )
797 {
798 	#if KeyBindingUses_WM_KEYDOWN
799 	{
800 		ProjChar tempProjCh[2];
801 
802 		tempProjCh[0] = (ProjChar)theKey;
803 		tempProjCh[1] = 0;
804 			// LOCALISEME
805 
806 
807 		return new SCString(&tempProjCh[0]);
808 	}
809 	#endif
810 
811 	#if KeyBindingUses_KEY_ID
812 	{
813 		return GetMethodString(theKey);
814 	}
815 	#endif
816 }
817 
818 // static
ParseBindCommand(BindableKey & theKey_Out,ProjChar ** ppProjCh_Out,ProjChar * pProjCh_In)819 OurBool KeyBinding :: ParseBindCommand
820 (
821 	BindableKey& theKey_Out,
822 	ProjChar** ppProjCh_Out,
823 		// returns where in the input string to continue processing
824 
825 	ProjChar* pProjCh_In
826 )
827 {
828 	// returns Yes if it understands the binding and fills out the output
829 
830 	GLOBALASSERT( ppProjCh_Out );
831 	GLOBALASSERT( pProjCh_In );
832 
833 	// Iterate through leading whitespace:
834 	{
835 		while
836 		(
837 			*pProjCh_In
838 		)
839 		{
840 			// LOCALISEME:
841 			if (!isspace(*pProjCh_In))
842 			{
843 				break;
844 			}
845 			pProjCh_In++;
846 		}
847 	}
848 
849 	// Scan through the string, trying to find matches against strings for keys
850 	// We will use the longest match:
851 	{
852 		OurBool bGotMatch = No;
853 		unsigned int LongestMatch = 0;
854 
855 		for (int i=0;i<MAX_VALUE_BINDABLE_KEY; i++)
856 		{
857 			BindableKey theKey = (BindableKey)i;
858 
859 			SCString* pSCString_TestKey = MakeStringForKey(theKey);
860 
861 			unsigned int LengthOfTestString = pSCString_TestKey -> GetNumChars();
862 
863 			if (LengthOfTestString > 0)
864 			{
865 				if
866 				(
867 					0 == _strnicmp
868 					(
869 						pSCString_TestKey -> pProjCh(),
870 						pProjCh_In,
871 						LengthOfTestString
872 					)
873 						// LOCALISEME
874 				)
875 				{
876 					// Then we have a match; see if it's longer than
877 					// what's come before...
878 					if (LengthOfTestString>LongestMatch)
879 					{
880 						LongestMatch = LengthOfTestString;
881 
882 						theKey_Out = theKey;
883 						*ppProjCh_Out = pProjCh_In + LengthOfTestString;
884 							// Continue processing after the string
885 						bGotMatch = Yes;
886 
887 					}
888 				}
889 			}
890 
891 			pSCString_TestKey -> R_Release();
892 		}
893 
894 		if (bGotMatch)
895 		{
896 			// Strip away whitespace following the key string:
897 			{
898 				while (**ppProjCh_Out)
899 				{
900 					// LOCALISEME:
901 					if (!isspace(**ppProjCh_Out))
902 					{
903 						break;
904 					}
905 					(*ppProjCh_Out)++;
906 				}
907 			}
908 		}
909 
910 		return bGotMatch;
911 	}
912 
913 }
914 
915 
916 
917 // private:
918 // Maintain a static list of all of objects of the class:
919 // static
920 List<KeyBinding*> KeyBinding :: List_pKeyBindings;
921 
922 // static
923 RefList<SCString> KeyBinding :: PendingList;
924 
925 // public:
926 // static
927 int KeyBinding :: bEcho = No;
928 
CONSBIND_WriteKeyBindingsToConfigFile(void)929 void CONSBIND_WriteKeyBindingsToConfigFile(void)
930 {
931 	#if !(PREDATOR_DEMO|MARINE_DEMO||ALIEN_DEMO||DEATHMATCH_DEMO)
932 	KeyBinding :: WriteToConfigFile("config.cfg");
933 	#endif
934 }
935 
936 
937 /* Internal function definitions ***********************************/
938