/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*********************************************************************************************** 
 ***                            Confidential - Westwood Studios                              *** 
 *********************************************************************************************** 
 *                                                                                             * 
 *                 Project Name : Installer                                                    * 
 *                                                                                             * 
 *                     $Archive:: /Commando/Code/Installer/SerialDialog.cpp $* 
 *                                                                                             * 
 *                      $Author:: Ian_l                   $* 
 *                                                                                             * 
 *                     $Modtime:: 1/15/02 11:09a                $* 
 *                                                                                             * 
 *                    $Revision:: 10                    $* 
 *                                                                                             * 
 *---------------------------------------------------------------------------------------------* 
 * Functions:                                                                                  * 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

// Includes.
#include "SerialDialog.h"
#include "CodeControl.h"
#include "EditCtrl.h"
#include "Installer.h"
#include "Resource.h"
#include "Translator.h"


// Defines.
#define EDIT_CONTROL_COUNT	4


// Forward declarations.
unsigned int Check_Sum (char *str);


// Static data.
static const int _EditControlIds		 [EDIT_CONTROL_COUNT] = {IDC_SERIAL_EDIT1,
																				 IDC_SERIAL_EDIT2,
																				 IDC_SERIAL_EDIT3,
																				 IDC_SERIAL_EDIT4};

static const int _EditControlLengths [EDIT_CONTROL_COUNT] = {6, 6, 6, 4};

static char *_SerialNumberLowRange;
static char *_SerialNumberHighRange;


/***********************************************************************************************
 * SerialDialogClass::On_Init_Dialog --																		  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void SerialDialogClass::On_Init_Dialog (void)
{
	if (!_Installer.Beta_Test()) {
		#if USE_QA_SERIAL_NOS
		_SerialNumberLowRange  = "0661";
		_SerialNumberHighRange = "0661";
		#else
		_SerialNumberLowRange  = "0559";
		_SerialNumberHighRange = "0660";
		#endif
	} else {
		_SerialNumberLowRange  = "0663";
		_SerialNumberHighRange = "0663";
	}

	#if NDEBUG
	#if USE_QA_SERIAL_NOS
	Set_Dlg_Item_Text (IDC_SERIAL_EDIT1, WideStringClass (_SerialNumberLowRange));
	#endif
	#else
	// Initialize with a valid serial number.
	if (!_Installer.Beta_Test()) {
		#if USE_QA_SERIAL_NOS
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT1, WideStringClass ("066146"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT2, WideStringClass ("321463"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT3, WideStringClass ("028258"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT4, WideStringClass ("8591"));
		#else
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT1, WideStringClass ("065906"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT2, WideStringClass ("044792"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT3, WideStringClass ("767144"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT4, WideStringClass ("4915"));
		#endif
	} else {
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT1, WideStringClass ("066390"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT2, WideStringClass ("274825"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT3, WideStringClass ("508134"));
		Set_Dlg_Item_Text (IDC_SERIAL_EDIT4, WideStringClass ("6328"));
	}
	#endif

	InstallMenuDialogClass::On_Init_Dialog();
}


/***********************************************************************************************
 * SerialDialogClass::On_Unicode_Char --																		  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void SerialDialogClass::On_Unicode_Char (uint16 unicode)
{

	// Scan the edit controls for input focus...
	for (unsigned i = 0; i < EDIT_CONTROL_COUNT; i++) {

		EditCtrlClass *editcontrol = Get_Dlg_Item (_EditControlIds [i])->As_EditCtrlClass();

		// Does this control have focus?
		if (editcontrol->Has_Focus()) {

			int textlength = wcslen (editcontrol->Get_Text());

			// Is this key one of the edit keys?
			switch (unicode) {

				case VK_BACK:
					if (textlength == 0) {
						if (i > 0) {
						 
							EditCtrlClass *preveditcontrol = Get_Dlg_Item (_EditControlIds [i - 1])->As_EditCtrlClass();
							preveditcontrol->Set_Focus();
							preveditcontrol->Set_Sel (-1, -1);
						}
					
					} else {
						InstallMenuDialogClass::On_Unicode_Char (unicode);
					}
					break;

				case VK_DELETE:
				case VK_HOME:
				case VK_END:
				case VK_LEFT:
				case VK_RIGHT:
				case VK_RETURN:
				case VK_UP:
				case VK_DOWN:
				case VK_TAB:

					// Process the key.
					InstallMenuDialogClass::On_Unicode_Char (unicode);
					break;

				default:
					
					// Is it a numeric key?
					if ((unicode >= L'0') && (unicode <= L'9')) {

						if (textlength < _EditControlLengths [i]) {
							
							// Process the key.
							InstallMenuDialogClass::On_Unicode_Char (unicode);
							textlength = wcslen (editcontrol->Get_Text());

							// If the edit control is now full and the caret is at the end...
							if ((textlength == _EditControlLengths [i]) && (textlength == editcontrol->Get_Caret_Pos())) {

								// If the next edit control is empty switch focus to it.
								if (i < EDIT_CONTROL_COUNT - 1) {

									EditCtrlClass *nexteditcontrol = Get_Dlg_Item (_EditControlIds [i + 1])->As_EditCtrlClass();

									if (wcslen (nexteditcontrol->Get_Text()) == 0) {
										nexteditcontrol->Set_Focus();
									}
								}
							}

						} else {

	 						if ((textlength == _EditControlLengths [i]) && (textlength == editcontrol->Get_Caret_Pos())) {
							
								// If the next edit control is empty switch focus to it and process the key.
								if (i < EDIT_CONTROL_COUNT - 1) {

									EditCtrlClass *nexteditcontrol = Get_Dlg_Item (_EditControlIds [i + 1])->As_EditCtrlClass();

									if (wcslen (nexteditcontrol->Get_Text()) == 0) {
										nexteditcontrol->Set_Focus();
										InstallMenuDialogClass::On_Unicode_Char (unicode);
									}
								}
							}
						}
					}
			}
			break;
		}
	}
}


/***********************************************************************************************
 * SerialDialogClass::On_Command --																				  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void SerialDialogClass::On_Command (int ctrl_id, int message_id, DWORD param)
{
	bool valid = true;

	switch (ctrl_id) {

		case IDOK:
		{
			StringClass serialnumber;

			valid = Get_Serial_Number (serialnumber);
			if (valid) {

				#if NDEBUG
				StringClass lowrange  (_SerialNumberLowRange);
				StringClass highrange (_SerialNumberHighRange);
			
				// Limit range of serial number.
				valid = !(((_strnicmp (serialnumber, lowrange, 4) < 0) || (_strnicmp (serialnumber, highrange, 4) > 0)));
				#endif

				if (valid) {

					unsigned i, v;
					
					// Scan the string for all zeros.
					v = 0;
					for (i = 0; i < (unsigned) serialnumber.Get_Length(); i++) {
						v += *(serialnumber.Peek_Buffer() + i) - '0';
					}
					valid = (v > 0);

					if (valid) {
	
						const unsigned offset = 18;

						StringClass checksumstring (serialnumber);
						unsigned		checksum;

						// Apply checksum test.
						*(checksumstring.Peek_Buffer() + offset) = '\0';
						checksum = Check_Sum (checksumstring.Peek_Buffer());
						valid = (checksum == (uint32) atol (serialnumber.Peek_Buffer() + offset));
					}
				}
			}
			break;
		}

		default:
			// Do nothing.
			break;
	}
	
	if (valid) {
		InstallMenuDialogClass::On_Command (ctrl_id, message_id, param);
	} else {
		MessageBoxClass::Do_Dialog (TxWideStringClass (IDS_WARNING), TxWideStringClass (IDS_INCORRECT_SERIAL), MessageBoxClass::MESSAGE_BOX_TYPE_OK, this);
	}
}


/***********************************************************************************************
 * SerialDialogClass::Get_Serial_Number --																	  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool SerialDialogClass::Get_Serial_Number (StringClass &serialnumber)
{
	bool valid = false;

	for (unsigned i = 0; i < EDIT_CONTROL_COUNT; i++) {

		EditCtrlClass *editcontrol = Get_Dlg_Item (_EditControlIds [i])->As_EditCtrlClass();

		// Extract the serial number.
		WideStringClass widedigits (editcontrol->Get_Text());
		StringClass		 digits;		

		// Ensure that the text contains correct no. of characters.
		// NOTE: Non-numeric characters have already been filtered out, so no need to check for this.
		valid = (widedigits.Get_Length() == _EditControlLengths [i]);
		if (!valid) {
			break;
		}
		widedigits.Convert_To (digits);
		serialnumber += digits;
	}

	return (valid);
}


/***********************************************************************************************
 * SerialDialogClass::CheckSum -- Generate a checksum number between 0...9999 from a null		  *
 *											 terminated string of numbers.										  *
 *																															  *
 * INPUT:		none.																									  *
 *																															  *
 * OUTPUT:		none.																									  *
 *																															  *
 * WARNINGS:																											  *
 *																															  *
 *		The checksum is divided into 2 parts.  The first is computed by grabbing 7 digits at a   *
 *		grabbing 7 digits at a time and summing them mod 97. The second part is computed by		  *
 *		grabbing 5 digits at a time and The second part is computed by grabbing 5 digits at a	  *
 *		time and	summing them mod 93.																				  *
 *																															  *
 *		Note that it's important that 97 & 93 are prime (or at least relatively prime...) and	  *
 *		less than 100.	7 & 5 were chosen because they can stay out of phase for the length of	  *
 *		the input string.																								  *
 *																															  *
 *		The two components together make the checksum. This will catch	all errors of 1 & 2		  *
 *		digits and many other errors, but the proof is left as an exercise to the reader.		  *
 *																															  *
 *		Note, if you try and checksum ammounts over 30 chars the above	guarantees will fail.	  *
 *		(That's the number of chars that the skip	& offset values will stay out of phase).		  *														  *
 *																															  *
 *		Note: The checksum of all zeros will be 0 so you need to special case the check to not	  *
 *		allow this (since it's a pretty obvious thing for a user to type all 0's to try and		  *
 *		cheat the system).																							  *
 *																															  *
 * HISTORY:																												  *
 *		10/30/1995	MML : Created.																					  *
 *=============================================================================================*/
unsigned int Check_Sum (char *str)
{
	unsigned int counter	= 0;
	unsigned int retval	= 0;
	unsigned int group	= 0;
	int	len	= strlen( str );
	int	mult	= 0;
	int	i;
	int	j;
 
	const int skipa	=  7;		// part A's skip value
	const int skipb	=  5;		// ... part B
	const int offsa	= -5;		// Everywhere the skips are in phase is a place where an 2nd error can occur and not be detected.

	//---------------------------------------------------------------------------
	// Part1
	//---------------------------------------------------------------------------
	counter = 0;
	for ( i = offsa; i < len; i += skipa ) {

		mult = 1;
		for ( j = 1; j < skipa; j++ ) {
			mult *= 10;
		}
 
		group = 0;
		for ( j = i; j < MIN( len, i+skipa ); j++ ) {

			if ( j >= 0 ) {
				group += ((int)( str[j] - '0' )) * mult;
			}
			mult /= 10;
		}
		counter += group;
		counter %= 97;
	}
	retval = counter * 100;
 
	//---------------------------------------------------------------------------
	// Part2
	//---------------------------------------------------------------------------
	counter = 0;
	for ( i = 0; i < len; i += skipb ) {

		mult = 1;
		for ( j = 1; j < skipb; j++ ) {
			mult *= 10;
		}
 
		group = 0;
		for ( j = i; j < MIN( len, i+skipb ); j++ ) {

			if ( j >= 0 ) {
				group += ((int)( str[j] - '0' )) * mult;
			}
         mult /= 10;
    }
    counter += group;
    counter %= 93;
  }
  retval += counter;
 
  return (retval);
}
