/*
**	Command & Conquer Red Alert(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/>.
*/

/* $Header: /CounterStrike/PROFILE.CPP 1     3/03/97 10:25a Joe_bostic $ */
/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Command & Conquer                                            *
 *                                                                                             *
 *                    File Name : PROFILE.CPP                                                  *
 *                                                                                             *
 *                   Programmer : Joe L. Bostic                                                *
 *                                                                                             *
 *                   Start Date : September 10, 1993                                           *
 *                                                                                             *
 *                  Last Update : September 10, 1993   [JLB]                                   *
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   WWGetPrivateProfileInt -- Fetches integer value from INI.                                 *
 *   WWGetPrivateProfileString -- Fetch string from INI.                                       *
 *   WWWritePrivateProfileInt -- Write a profile int to the profile data block.                *
 *   WWWritePrivateProfileString -- Write a string to the profile data block.                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include	"function.h"


static char * WriteBinBuffer = NULL;
static int WriteBinBufferLen = 0;
static int WriteBinBufferPos = 0;
static int WriteBinBufferMax = 0;
static char * ReadBinBuffer = NULL;
static int ReadBinBufferLen = 0;
static int ReadBinBufferPos = 0;
static int ReadBinBufferMax = 0;


/***************************************************************************
 * Read_Private_Config_Struct -- Fetches override integer value.           *
 *                                                                         *
 * INPUT:                                                                  *
 * OUTPUT:                                                                 *
 * WARNINGS:                                                               *
 * HISTORY:                                                                *
 *   08/05/1992 JLB : Created.                                             *
 *=========================================================================*/
bool Read_Private_Config_Struct(FileClass & file, NewConfigType * config)
{
	INIClass ini;
	ini.Load(file);

	config->DigitCard 	= ini.Get_Hex("Sound", "Card", 0);
	config->IRQ 			= ini.Get_Int("Sound", "IRQ", 0);
	config->DMA 			= ini.Get_Int("Sound", "DMA", 0);
	config->Port 			= ini.Get_Hex("Sound", "Port", 0);
	config->BitsPerSample= ini.Get_Int("Sound", "BitsPerSample", 0);
	config->Channels 		= ini.Get_Int("Sound", "Channels", 0);
	config->Reverse      = ini.Get_Int("Sound", "Reverse", 0);
	config->Speed        = ini.Get_Int("Sound", "Speed", 0);
	ini.Get_String("Language", "Language", NULL, config->Language, sizeof(config->Language));


//	config->DigitCard 	= WWGetPrivateProfileHex("Sound", "Card", 			 profile);
//	config->IRQ 			= WWGetPrivateProfileInt("Sound", "IRQ", 			 0,profile);
//	config->DMA 			= WWGetPrivateProfileInt("Sound", "DMA", 			 0,profile);
//	config->Port 			= WWGetPrivateProfileHex("Sound", "Port", 			 profile);
//	config->BitsPerSample= WWGetPrivateProfileInt("Sound", "BitsPerSample",0,profile);
//	config->Channels 		= WWGetPrivateProfileInt("Sound", "Channels",		 0,profile);
//	config->Reverse      = WWGetPrivateProfileInt("Sound", "Reverse", 		 0,profile);
//	config->Speed        = WWGetPrivateProfileInt("Sound", "Speed", 		 0,profile);
//	WWGetPrivateProfileString("Language", "Language", NULL, config->Language, 3, profile);

	return((config->DigitCard == 0) && (config->IRQ == 0) && (config->DMA == 0));
}


/***************************************************************************
 * Get_Private_Profile_Hex -- Fetches override integer value.              *
 *                                                                         *
 * INPUT:                                                                  *
 * OUTPUT:                                                                 *
 * WARNINGS:                                                               *
 * HISTORY:                                                                *
 *   08/05/1992 MML : Created.                                             *
 *=========================================================================*/
unsigned WWGetPrivateProfileHex (char const * section, char const * entry, char * profile)
{
	char	buffer[16];					// Integer staging buffer.
	unsigned card;

	memset (buffer, '0', sizeof(buffer));		// MAX_ENTRY_SIZE = 15
	buffer[sizeof(buffer)-1] = '\0';

	WWGetPrivateProfileString(section, entry, "0", buffer, sizeof(buffer), profile);

	if (strlen (buffer) > 0) {
		sscanf (buffer, "%x", &card);
	} else {
		card = 0;
	}

	return(card);
}


/***********************************************************************************************
 * WWGetPrivateProfileInt -- Fetches integer value.                                            *
 *                                                                                             *
 * INPUT:                                                                                      *
 *      section      section to read from                                                      *
 *                                                                                             *
 *      entry         name of entry to read                                                    *
 *                                                                                             *
 *      def         default value, if entry isn't found                                        *
 *                                                                                             *
 *      profile      buffer containing INI data                                                *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *      integer requested                                                                      *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *      none.                                                                                  *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/05/1992 JLB : Created.                                                                 *
 *=============================================================================================*/
int WWGetPrivateProfileInt(char const * section, char const * entry, int def, char * profile)
{
	char	buffer[16];			// Integer staging buffer.

	/*
	**	Store the default in the buffer.
	*/
	sprintf(buffer, "%d", def);

	/*
	**	Get the buffer; use itself as the default.
	*/
	WWGetPrivateProfileString(section, entry, buffer, buffer, sizeof(buffer)-1, profile);

	/*
	**	Convert to int & return.
	*/
	return(atoi(buffer));
}


/***********************************************************************************************
 * WWWritePrivateProfileInt -- Write a profile int to the profile data block.                  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *      section      section name to write to                                                  *
 *                                                                                             *
 *      entry         name of entry to write; if NULL, the entire section is deleted           *
 *                                                                                             *
 *      value         value to write                                                           *
 *                                                                                             *
 *      profile      INI buffer                                                                *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *      true = success, false = failure                                                        *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *      none.                                                                                  *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/07/1992 JLB : Created.                                                                 *
 *=============================================================================================*/
bool WWWritePrivateProfileInt(char const * section, char const * entry, int value, char * profile)
{
	char	buffer[250];			// Working section buffer.

	/*
	**	Just return if nothing to do.
	*/
	if (!profile || !section) {
		return(true);
	}

	/*
	**	Generate string to save.
	*/
	sprintf(buffer, "%d", value);

	/*
	**	Save the string.
	*/
	return(WWWritePrivateProfileString(section, entry, buffer, profile));
}


/***********************************************************************************************
 * WWGetPrivateProfileString -- Fetch game override string.                                    *
 *                                                                                             *
 * INPUT:                                                                                      *
 *      section      section name to read from                                                 *
 *                                                                                             *
 *      entry         name of entry to read; if NULL, all entry names are returned             *
 *                                                                                             *
 *      def         default string to use if not found; can be NULL                            *
 *                                                                                             *
 *      retbuffer   buffer to store result in                                                  *
 *                                                                                             *
 *      retlen      max length of return buffer                                                *
 *                                                                                             *
 *      profile      INI buffer                                                                *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *      ptr to entry found in INI buf; NULL if not found                                       *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *      On the PC, the "\n" (10) is translated to "\r\n" (13,10) when it's written             *
 *      to disk. This routine must take this into consideration, by searching                  *
 *      for \n when scanning backward, and for \r when scanning forward.                       *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/05/1992 JLB : Created.                                                                 *
 *=============================================================================================*/
char * WWGetPrivateProfileString(char const * section, char const * entry, char const * def, char * retbuffer, int retlen, char const * profile)
{
	char const * workptr;		// Working pointer into profile block.
	char * altworkptr;	// Alternate work pointer.
	char	sec[50];			// Working section buffer.
	char	const * retval;			// Start of section or entry pointer.
	char	* next;			// Pointer to start of next section (or EOF).
	char	c,c2;				// Working character values.
	int	len;				// Working substring length value.
	int	entrylen;		// Byte length of specified entry.
	char * orig_retbuf;	// original retbuffer ptr

//	if (!retlen) return(NULL);

	/*
	**	Fill in the default value just in case the entry could not be found.
	*/
	if (retbuffer) {
		retbuffer[0] = '\0';
		if (retlen > 1 || retlen == 0) retbuffer[1] = '\0';
		if (def) {
			strncpy(retbuffer, def, retlen);
		}
		retbuffer[retlen-1] = '\0';
		orig_retbuf = retbuffer;
	}

	/*
	**	Make sure a profile string was passed in
	*/
	if (!profile || !section) {
		return(retbuffer);
	}

	/*
	**	Build section string to match file image.
	*/
	sprintf(sec, "[%s]", section);	// sec = section name including []'s
	strupr(sec);
	len = strlen(sec);					// len = section name length, incl []'s

	/*
	**	Scan for a matching section
	*/
	retval = profile;
	workptr = profile;
	for (;;) {

		/*
		**	'workptr' = start of next section
		*/
		workptr = strchr(workptr, '[');

		/*
		**	If the end has been reached without finding the desired section
		**	then abort with a failure flag.
		*/
		if (!workptr) {
			return(NULL);
		}

		/*
		**	'c' = character just before the '['
		*/
		if (workptr==profile) {
			c = '\n';
		} else {
			c = *(workptr-1);
		}

		/*
		**	If this is the section name & the character before is a newline,
		**	process this section
		*/
		if (memicmp(workptr, sec, len) == 0 && (c == '\n')) {

			/*
			**	Skip work pointer to start of first valid entry.
			*/
			workptr += len;
			while (isspace(*workptr)) {
				workptr++;
			}

			/*
			**	If the section name is empty, we will have stepped onto the start
			**	of the next section name; inserting new entries here will leave
			**	a blank line between this section's name & 1st entry. So, check
			**	for 2 newlines in a row & step backward.
			*/
			if (workptr - profile > 4) {
				if ( *(workptr-1)=='\n' && *(workptr-3)=='\n')
					workptr -= 2;
			}

			/*
			**	'next = end of section or end of file.
			*/
			next = strchr(workptr, '[');
			for (;;) {
				if (next) {

					c = *(next-1);

					/*
					**	If character before '[' is newline, this is the start of the
					**	next section
					*/
					if (c == '\n') {
						if (*(next-1)=='\n' && *(next-3)=='\n') {
							next -= 2;
						}
						break;
					}

					/*
					**	This bracket was in the section; keep looking
					*/
					next = strchr(next+1, '[');
				} else {

					/*
					**	No bracket found; set 'next' to the end of the file
					*/
					next = (char*)workptr + strlen(workptr)-1;
					break;
				}
			}

			/*
			**	If a specific entry was specified then return with the associated
			**	string.
			*/
			if (entry) {
				retval = workptr;
				entrylen = strlen(entry);

				for (;;) {
					/*
					** Search for the 1st character of the entry
					*/
					workptr = strchr(workptr, *entry);

					/*
					**	If the end of the file has been reached or we have spilled
					**	into the next section, then abort
					*/
					if (!workptr || workptr >= next) {
						return(NULL);
					}

					/*
					**	'c' = character before possible entry; must be a newline
					**	'c2' = character after possible entry; must be '=' or space
					*/
					c = *(workptr-1);
					c2 = *(workptr+entrylen);

					/*
					**	Entry found; extract it
					*/
					if (memicmp(workptr, entry, entrylen) == 0 && (c == '\n') &&
						(c2 == '=' || isspace(c2))) {
						retval = workptr;
						workptr += entrylen;							// skip entry name
						workptr = strchr(workptr, '=');			// find '='

						/*
						** 'altworkptr' = next newline; \r is used here since we're
						** scanning forward!
						*/
						if (workptr) {
							altworkptr = strchr(workptr, '\r');	// find next newline
						}

						/*
						**	Return if there was no '=', or if the newline is before
						**	the next '='
						*/
						if (workptr == NULL || altworkptr < workptr) {
							return((char *)retval);
						}

						/*
						**	Skip any white space after the '=' and before the first
						**	valid character of the parameter.
						*/
						workptr++;										// Skip the '='.
						while (isspace(*workptr)) {

							/*
							**	Just return if there's no entry past the '='.
							*/
							if (workptr >= altworkptr)
								return((char*)retval);

							workptr++;									// Skip the whitespace
						}

						/*
						**	Copy the entry into the return buffer.
						*/
						len = (int)(altworkptr - workptr);
						if (len > retlen-1) {
							len = retlen-1;
						}

						if (retbuffer) {
							memcpy(retbuffer, workptr, len);
							*(retbuffer + len) = '\0';		// Insert trailing null.
							strtrim(retbuffer);
						}
						return((char*)retval);
					}

					/*
					**	Entry was not found; go to the next one
					*/
					workptr++;
				}
			} else {

				/*
				**	No entry was specified, so build a list of all entries.
				**	'workptr' is at 1st entry after section name
				**	'next' is next bracket, or end of file
				*/
				retval = workptr;

				if (retbuffer) {

					/*
					**	Keep accumulating the identifier strings in the retbuffer.
					*/
					while (workptr && workptr < next) {
						altworkptr = strchr(workptr, '=');		// find '='

						if (altworkptr && altworkptr < next) {
							int	length;								// Length of ID string.

							length = (int)(altworkptr - workptr);

							/*
							**	Make sure we don't write past the end of the retbuffer;
							**	add '3' for the 3 NULL's at the end
							*/
							if (retbuffer - orig_retbuf + length + 3 < retlen) {
								memcpy(retbuffer, workptr, length);	// copy entry name
								*(retbuffer+length) = '\0';			// NULL-terminate it
								strtrim(retbuffer);						// trim spaces
								retbuffer += strlen(retbuffer)+1;	// next pos in dest buf
							} else {
								break;
							}

							/*
							**	Advance the work pointer to the start of the next line
							**	by skipping the end of line character.
							*/
							workptr = strchr(altworkptr, '\n');
							if (!workptr) {
								break;
							}
							workptr++;
						} else {

							/*
							**	If no '=', break out of loop
							*/
							break;
						}
					}

					/*
					**	Final trailing terminator. Make double sure the double
					**	trailing null is added.
					*/
					*retbuffer++ = '\0';
					*retbuffer++ = '\0';
				}
				break;
			}
		} else {

			/*
			**	Section name not found; go to the next bracket & try again
			**	Advance past '[' and keep scanning.
			*/
			workptr++;
		}
	}

	return((char*)retval);
}


/***********************************************************************************************
 * WritePrivateProfileString -- Write a string to the profile data block.                      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *      section      section name to write to                                                  *
 *      entry         name of entry to write; if NULL, the section is deleted                  *
 *      string      string to write; if NULL, the entry is deleted                             *
 *      profile      INI buffer                                                                *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *      true = success, false = failure                                                        *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *      This function has to translate newlines into \r\n sequences.                           *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/07/1992 JLB : Created.                                                                 *
 *=============================================================================================*/
bool WWWritePrivateProfileString(char const * section, char const * entry, char const * string, char * profile)
{
	char	buffer[250];			// Working section buffer
	char const * offset;
	char const * next;						// ptr to next section
	char	c;							// Working character value

	/*
	**	Just return if nothing to do.
	*/
	if (!profile || !section) {
		return(true);
	}

	/*
	**	Try to find the section. WWGetPrivateProfileString with NULL entry name
	**	will return all entry names in the given buffer, truncated to the given
	**	buffer length. 'offset' will point to 1st entry in the section, NULL if
	**	section not found.
	*/
	offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile);

	/*
	**	If the section could not be found, then add it to the end. Don't add
	**	anything if a removal of an entry is requested (it is obviously already
	**	non-existent). Make sure two newlines precede the section name.
	*/
	if (!offset && entry) {
		sprintf(buffer, "\r\n[%s]\r\n", section);
		strcat(profile, buffer);
	}

	/*
	**	If the section is there and 'entry' is NULL, remove the entire section
	*/
	if (offset && !entry) {

		/*
		**	'next = end of section or end of file.
		*/
		next = strchr(offset, '[');
		for (;;) {
			if (next) {
				c = *(next-1);

				/*
				**	If character before '[' is newline, this is the start of the
				**	next section
				*/
				if (c == '\n') {
					if ( *(next-1)=='\n' && *(next-3)=='\n') {
						next -= 2;
					}
					break;
				}

				/*
				**	This bracket was in the section; keep looking
				*/
				next = strchr(next+1, '[');
			} else {

				/*
				**	No bracket found; set 'next' to the end of the file
				*/
				next = offset + strlen(offset);
				break;
			}
		}

		/*
		**	Remove the section
		*/
		strcpy((char*)offset, (char*)next);

		return(true);
	}

	/*
	**	Find the matching entry within the desired section. A NULL return buffer
	**	with 0 length will just return the offset of the found entry, NULL if
	**	entry not found.
	*/
	offset = WWGetPrivateProfileString(section, entry, NULL, NULL, 0, profile);

	/*
	**	Remove any existing entry
	*/
	if (offset) {
		int	eol;			// Working EOL offset.

		/*
		**	Get # characters up to newline; \n is used since we're after the end
		**	of this line
		*/
		eol = strcspn(offset, "\n");

		/*
		**	Erase the entry by strcpy'ing the entire INI file over this entry
		*/
		if (eol) {
			strcpy((char*)offset, offset + eol + 1);
		}
	} else {

		/*
		**	Entry doesn't exist, so point 'offset' to the 1st entry position in
		**	the section.
		*/
		offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile);
	}

	/*
	**	Add the desired entry.
	*/
	if (entry && string) {

		/*
		**	Generate entry string.
		*/
		sprintf(buffer, "%s=%s\r\n", entry, string);

		/*
		**	Make room for new entry.
		*/
		memmove((char*)offset+strlen(buffer), offset, strlen(offset)+1);

		/*
		**	Copy the entry into the INI buffer.
		*/
		memcpy((char*)offset, buffer, strlen(buffer));
	}

	return(true);
}


char * Read_Bin_Buffer( void )
{
	return( ReadBinBuffer );
}


bool Read_Bin_Init( char * buffer, int length )
{
	ReadBinBuffer = buffer;
	ReadBinBufferLen = length;
	ReadBinBufferPos = 0;
	ReadBinBufferMax = 0;
	return( true );
}


int Read_Bin_Length( char * buffer )
{
	if (buffer != ReadBinBuffer) {
		return( -1 );
	} else {
		return( ReadBinBufferMax );
	}
}


bool Read_Bin_Num( void * num, int length, char * buffer )
{
	char * ptr;

	if (buffer != ReadBinBuffer || length <= 0 || length > 4 ||
		 (ReadBinBufferPos + length) >= ReadBinBufferLen) {
		return( false );
	} else {
		ptr = ReadBinBuffer + ReadBinBufferPos;
		memcpy( num, ptr, length );
		ReadBinBufferPos += length;

		if (ReadBinBufferPos > ReadBinBufferMax) {
			ReadBinBufferMax = ReadBinBufferPos;
		}

		return( true );
	}
}


int Read_Bin_Pos( char * buffer )
{
	if (buffer != ReadBinBuffer) {
		return( -1 );
	} else {
		return( ReadBinBufferPos );
	}
}


int Read_Bin_PosSet( unsigned int pos, char * buffer )
{
	if (buffer != ReadBinBuffer) {
		return( -1 );
	} else {
		ReadBinBufferPos = pos;
		return( ReadBinBufferPos );
	}
}


bool Read_Bin_String( char * string, char * buffer )
{
	char * ptr;
	unsigned char length;

	if (buffer != ReadBinBuffer ||
		 ReadBinBufferPos >= ReadBinBufferLen) {
		return( false );
	} else {
		ptr = ReadBinBuffer + ReadBinBufferPos;
		length = (unsigned char)*ptr++;
		if ( (ReadBinBufferPos + length + 2) <= ReadBinBufferLen) {
			memcpy( string, ptr, (unsigned int)(length + 1) );
			ReadBinBufferPos += (length + 2);

			if (ReadBinBufferPos > ReadBinBufferMax) {
				ReadBinBufferMax = ReadBinBufferPos;
			}

			return( true );
		} else {
			return( false );
		}
	}
}


char * Write_Bin_Buffer( void )
{
	return( WriteBinBuffer );
}


bool Write_Bin_Init( char * buffer, int length )
{
	WriteBinBuffer = buffer;
	WriteBinBufferLen = length;
	WriteBinBufferPos = 0;
	WriteBinBufferMax = 0;
	return( true );
}


int Write_Bin_Length( char * buffer )
{
	if (buffer != WriteBinBuffer) {
		return( -1 );
	} else {
		return( WriteBinBufferMax );
	}
}


bool Write_Bin_Num( void * num, int length, char * buffer )
{
	char * ptr;

	if (buffer != WriteBinBuffer || length <= 0 || length > 4 ||
		 (WriteBinBufferPos + length) > WriteBinBufferLen) {
		return( false );
	} else {
		ptr = WriteBinBuffer + WriteBinBufferPos;
		memcpy( ptr, num, length );
		WriteBinBufferPos += length;

		if (WriteBinBufferPos > WriteBinBufferMax) {
			WriteBinBufferMax = WriteBinBufferPos;
		}

		return( true );
	}
}


int Write_Bin_Pos( char * buffer )
{
	if (buffer != WriteBinBuffer) {
		return( -1 );
	} else {
		return( WriteBinBufferPos );
	}
}


int Write_Bin_PosSet( unsigned int pos, char * buffer )
{
	if (buffer != WriteBinBuffer) {
		return( -1 );
	} else {
		WriteBinBufferPos = pos;
		return( WriteBinBufferPos );
	}
}


bool Write_Bin_String( char * string, int length, char * buffer )
{
	char * ptr;

	if (buffer != WriteBinBuffer || length < 0 || length > 255 ||
		 (WriteBinBufferPos + length + 2) > WriteBinBufferLen) {
		return( false );
	} else {
		ptr = WriteBinBuffer + WriteBinBufferPos;
		*ptr++ = (char)length;
		memcpy( ptr, string, (length + 1) );
		WriteBinBufferPos += (length + 2);

		if (WriteBinBufferPos > WriteBinBufferMax) {
			WriteBinBufferMax = WriteBinBufferPos;
		}

		return( true );
	}
}