// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code 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. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /* $Header: F:\projects\c&c\vcs\code\profile.cpv 2.18 16 Oct 1995 16:51:14 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. * * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * * WWGetPrivateProfileString -- Fetch string from INI. * * WWWritePrivateProfileString -- Write a string to the profile data block. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" /*************************************************************************** * Read_Private_Config_Struct -- Fetches override integer value. * * * * INPUT: * * OUTPUT: * * WARNINGS: * * HISTORY: * * 08/05/1992 JLB : Created. * *=========================================================================*/ bool Read_Private_Config_Struct (char *profile, NewConfigType *config) { 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 JLB : Created. * *=========================================================================*/ unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile) { // This buffer was overrun at the end due to the forced termination at MAX_ENTRY_SIZE below char buffer[MAX_ENTRY_SIZE + 1]; // Integer staging buffer. unsigned card; memset (buffer, '0', MAX_ENTRY_SIZE); // MAX_ENTRY_SIZE = 15 buffer[MAX_ENTRY_SIZE] = '\0'; WWGetPrivateProfileString(section, entry, "0", buffer, MAX_ENTRY_SIZE, 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, 15, 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 *profile) { char * workptr, // Working pointer into profile block. * altworkptr; // Alternate work pointer. char sec[50]; // Working section buffer. char *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 /* ** Fill in the default value just in case the entry could not be found. */ if (retbuffer) { 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 = 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(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(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(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(retval); } /*********************************************************************************************** * WWWritePrivateProfileString -- 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 *offset; char *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(offset,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(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(offset+strlen(buffer), offset, strlen(offset)+1); /* ** Copy the entry into the INI buffer. */ memcpy(offset, buffer, strlen(buffer)); } return(true); }