PROFILE.CPP (24851B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\profile.cpv 2.18 16 Oct 1995 16:51:14 JOE_BOSTIC $ */ 17 /*********************************************************************************************** 18 *** 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 *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : PROFILE.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : September 10, 1993 * 28 * * 29 * Last Update : September 10, 1993 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * WWGetPrivateProfileInt -- Fetches integer value from INI. * 34 * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * 35 * WWGetPrivateProfileString -- Fetch string from INI. * 36 * WWWritePrivateProfileString -- Write a string to the profile data block. * 37 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 38 39 #include "function.h" 40 41 42 /*************************************************************************** 43 * Read_Private_Config_Struct -- Fetches override integer value. * 44 * * 45 * INPUT: * 46 * OUTPUT: * 47 * WARNINGS: * 48 * HISTORY: * 49 * 08/05/1992 JLB : Created. * 50 *=========================================================================*/ 51 bool Read_Private_Config_Struct (char *profile, NewConfigType *config) 52 { 53 config->DigitCard = WWGetPrivateProfileHex ("Sound", "Card", profile); 54 config->IRQ = WWGetPrivateProfileInt ("Sound", "IRQ", 0,profile); 55 config->DMA = WWGetPrivateProfileInt ("Sound", "DMA", 0,profile); 56 config->Port = WWGetPrivateProfileHex ("Sound", "Port", profile); 57 config->BitsPerSample= WWGetPrivateProfileInt ("Sound", "BitsPerSample",0,profile); 58 config->Channels = WWGetPrivateProfileInt ("Sound", "Channels", 0,profile); 59 config->Reverse = WWGetPrivateProfileInt ("Sound", "Reverse", 0,profile); 60 config->Speed = WWGetPrivateProfileInt ("Sound", "Speed", 0,profile); 61 WWGetPrivateProfileString ("Language", "Language", NULL, config->Language, 3, profile); 62 63 return((config->DigitCard == 0) && (config->IRQ == 0) && (config->DMA == 0)); 64 } 65 66 67 /*************************************************************************** 68 * Get_Private_Profile_Hex -- Fetches override integer value. * 69 * * 70 * INPUT: * 71 * OUTPUT: * 72 * WARNINGS: * 73 * HISTORY: * 74 * 08/05/1992 JLB : Created. * 75 *=========================================================================*/ 76 unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile) 77 { 78 // This buffer was overrun at the end due to the forced termination at MAX_ENTRY_SIZE below 79 char buffer[MAX_ENTRY_SIZE + 1]; // Integer staging buffer. 80 unsigned card; 81 82 memset (buffer, '0', MAX_ENTRY_SIZE); // MAX_ENTRY_SIZE = 15 83 buffer[MAX_ENTRY_SIZE] = '\0'; 84 85 WWGetPrivateProfileString(section, entry, "0", buffer, MAX_ENTRY_SIZE, profile); 86 87 if (strlen (buffer) > 0) { 88 sscanf (buffer, "%x", &card); 89 } else { 90 card = 0; 91 } 92 93 return(card); 94 } 95 96 97 /*********************************************************************************************** 98 * WWGetPrivateProfileInt -- Fetches integer value. * 99 * * 100 * INPUT: * 101 * section section to read from * 102 * * 103 * entry name of entry to read * 104 * * 105 * def default value, if entry isn't found * 106 * * 107 * profile buffer containing INI data * 108 * * 109 * OUTPUT: * 110 * integer requested * 111 * * 112 * WARNINGS: * 113 * none. * 114 * * 115 * HISTORY: * 116 * 08/05/1992 JLB : Created. * 117 *=============================================================================================*/ 118 int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile) 119 { 120 char buffer[16]; // Integer staging buffer. 121 122 /* 123 ** Store the default in the buffer. 124 */ 125 sprintf(buffer, "%d", def); 126 127 /* 128 ** Get the buffer; use itself as the default. 129 */ 130 WWGetPrivateProfileString(section, entry, buffer, buffer, 15, profile); 131 132 /* 133 ** Convert to int & return. 134 */ 135 return(atoi(buffer)); 136 } 137 138 139 /*********************************************************************************************** 140 * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * 141 * * 142 * INPUT: * 143 * section section name to write to * 144 * * 145 * entry name of entry to write; if NULL, the entire section is deleted * 146 * * 147 * value value to write * 148 * * 149 * profile INI buffer * 150 * * 151 * OUTPUT: * 152 * true = success, false = failure * 153 * * 154 * WARNINGS: * 155 * none. * 156 * * 157 * HISTORY: * 158 * 10/07/1992 JLB : Created. * 159 *=============================================================================================*/ 160 bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile) 161 { 162 char buffer[250]; // Working section buffer. 163 164 /* 165 ** Just return if nothing to do. 166 */ 167 if (!profile || !section) { 168 return(true); 169 } 170 171 /* 172 ** Generate string to save. 173 */ 174 sprintf(buffer, "%d", value); 175 176 /* 177 ** Save the string. 178 */ 179 return(WWWritePrivateProfileString(section, entry, buffer, profile)); 180 } 181 182 183 /*********************************************************************************************** 184 * WWGetPrivateProfileString -- Fetch game override string. * 185 * * 186 * INPUT: * 187 * section section name to read from * 188 * * 189 * entry name of entry to read; if NULL, all entry names are returned * 190 * * 191 * def default string to use if not found; can be NULL * 192 * * 193 * retbuffer buffer to store result in * 194 * * 195 * retlen max length of return buffer * 196 * * 197 * profile INI buffer * 198 * * 199 * OUTPUT: * 200 * ptr to entry found in INI buf; NULL if not found * 201 * * 202 * WARNINGS: * 203 * On the PC, the "\n" (10) is translated to "\r\n" (13,10) when it's written * 204 * to disk. This routine must take this into consideration, by searching * 205 * for \n when scanning backward, and for \r when scanning forward. * 206 * * 207 * HISTORY: * 208 * 08/05/1992 JLB : Created. * 209 *=============================================================================================*/ 210 char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char *profile) 211 { 212 char * workptr, // Working pointer into profile block. 213 * altworkptr; // Alternate work pointer. 214 char sec[50]; // Working section buffer. 215 char *retval; // Start of section or entry pointer. 216 char * next; // Pointer to start of next section (or EOF). 217 char c,c2; // Working character values. 218 int len; // Working substring length value. 219 int entrylen; // Byte length of specified entry. 220 char *orig_retbuf; // original retbuffer ptr 221 222 /* 223 ** Fill in the default value just in case the entry could not be found. 224 */ 225 if (retbuffer) { 226 if (def) { 227 strncpy(retbuffer, def, retlen); 228 } 229 retbuffer[retlen-1] = '\0'; 230 orig_retbuf = retbuffer; 231 } 232 233 /* 234 ** Make sure a profile string was passed in 235 */ 236 if (!profile || !section) { 237 return(retbuffer); 238 } 239 240 /* 241 ** Build section string to match file image. 242 */ 243 sprintf(sec, "[%s]", section); // sec = section name including []'s 244 strupr(sec); 245 len = strlen(sec); // len = section name length, incl []'s 246 247 /* 248 ** Scan for a matching section 249 */ 250 retval = profile; 251 workptr = profile; 252 for (;;) { 253 254 /* 255 ** 'workptr' = start of next section 256 */ 257 workptr = strchr(workptr, '['); 258 259 /* 260 ** If the end has been reached without finding the desired section 261 ** then abort with a failure flag. 262 */ 263 if (!workptr) { 264 return(NULL); 265 } 266 267 /* 268 ** 'c' = character just before the '[' 269 */ 270 if (workptr==profile) { 271 c = '\n'; 272 } else { 273 c = *(workptr-1); 274 } 275 276 /* 277 ** If this is the section name & the character before is a newline, 278 ** process this section 279 */ 280 if (memicmp(workptr, sec, len) == 0 && (c == '\n')) { 281 282 /* 283 ** Skip work pointer to start of first valid entry. 284 */ 285 workptr += len; 286 while (isspace(*workptr)) { 287 workptr++; 288 } 289 290 /* 291 ** If the section name is empty, we will have stepped onto the start 292 ** of the next section name; inserting new entries here will leave 293 ** a blank line between this section's name & 1st entry. So, check 294 ** for 2 newlines in a row & step backward. 295 */ 296 if (workptr - profile > 4) { 297 if ( *(workptr-1)=='\n' && *(workptr-3)=='\n') 298 workptr -= 2; 299 } 300 301 /* 302 ** 'next = end of section or end of file. 303 */ 304 next = strchr(workptr, '['); 305 for (;;) { 306 if (next) { 307 308 c = *(next-1); 309 310 /* 311 ** If character before '[' is newline, this is the start of the 312 ** next section 313 */ 314 if (c == '\n') { 315 if (*(next-1)=='\n' && *(next-3)=='\n') { 316 next -= 2; 317 } 318 break; 319 } 320 321 /* 322 ** This bracket was in the section; keep looking 323 */ 324 next = strchr(next+1, '['); 325 } else { 326 327 /* 328 ** No bracket found; set 'next' to the end of the file 329 */ 330 next = workptr + strlen(workptr)-1; 331 break; 332 } 333 } 334 335 /* 336 ** If a specific entry was specified then return with the associated 337 ** string. 338 */ 339 if (entry) { 340 retval = workptr; 341 entrylen = strlen(entry); 342 343 for (;;) { 344 /* 345 ** Search for the 1st character of the entry 346 */ 347 workptr = strchr(workptr, *entry); 348 349 /* 350 ** If the end of the file has been reached or we have spilled 351 ** into the next section, then abort 352 */ 353 if (!workptr || workptr >= next) { 354 return(NULL); 355 } 356 357 /* 358 ** 'c' = character before possible entry; must be a newline 359 ** 'c2' = character after possible entry; must be '=' or space 360 */ 361 c = *(workptr-1); 362 c2 = *(workptr+entrylen); 363 364 /* 365 ** Entry found; extract it 366 */ 367 if (memicmp(workptr, entry, entrylen) == 0 && (c == '\n') && 368 (c2 == '=' || isspace(c2))) { 369 retval = workptr; 370 workptr += entrylen; // skip entry name 371 workptr = strchr(workptr, '='); // find '=' 372 373 /* 374 ** 'altworkptr' = next newline; \r is used here since we're 375 ** scanning forward! 376 */ 377 if (workptr) { 378 altworkptr = strchr(workptr, '\r'); // find next newline 379 } 380 381 /* 382 ** Return if there was no '=', or if the newline is before 383 ** the next '=' 384 */ 385 if (workptr == NULL || altworkptr < workptr) { 386 return(retval); 387 } 388 389 /* 390 ** Skip any white space after the '=' and before the first 391 ** valid character of the parameter. 392 */ 393 workptr++; // Skip the '='. 394 while (isspace(*workptr)) { 395 396 /* 397 ** Just return if there's no entry past the '='. 398 */ 399 if (workptr >= altworkptr) 400 return(retval); 401 402 workptr++; // Skip the whitespace 403 } 404 405 /* 406 ** Copy the entry into the return buffer. 407 */ 408 len = (int)(altworkptr - workptr); 409 if (len > retlen-1) { 410 len = retlen-1; 411 } 412 413 if (retbuffer) { 414 memcpy(retbuffer, workptr, len); 415 *(retbuffer + len) = '\0'; // Insert trailing null. 416 strtrim(retbuffer); 417 } 418 return(retval); 419 } 420 421 /* 422 ** Entry was not found; go to the next one 423 */ 424 workptr++; 425 } 426 } else { 427 428 /* 429 ** No entry was specified, so build a list of all entries. 430 ** 'workptr' is at 1st entry after section name 431 ** 'next' is next bracket, or end of file 432 */ 433 retval = workptr; 434 435 if (retbuffer) { 436 437 /* 438 ** Keep accumulating the identifier strings in the retbuffer. 439 */ 440 while (workptr && workptr < next) { 441 altworkptr = strchr(workptr, '='); // find '=' 442 443 if (altworkptr && altworkptr < next) { 444 int length; // Length of ID string. 445 446 length = (int)(altworkptr - workptr); 447 448 /* 449 ** Make sure we don't write past the end of the retbuffer; 450 ** add '3' for the 3 NULL's at the end 451 */ 452 if (retbuffer - orig_retbuf + length + 3 < retlen) { 453 memcpy(retbuffer, workptr, length); // copy entry name 454 *(retbuffer+length) = '\0'; // NULL-terminate it 455 strtrim(retbuffer); // trim spaces 456 retbuffer += strlen(retbuffer)+1; // next pos in dest buf 457 } else { 458 break; 459 } 460 461 /* 462 ** Advance the work pointer to the start of the next line 463 ** by skipping the end of line character. 464 */ 465 workptr = strchr(altworkptr, '\n'); 466 if (!workptr) { 467 break; 468 } 469 workptr++; 470 } else { 471 472 /* 473 ** If no '=', break out of loop 474 */ 475 break; 476 } 477 } 478 479 /* 480 ** Final trailing terminator. Make double sure the double 481 ** trailing null is added. 482 */ 483 *retbuffer++ = '\0'; 484 *retbuffer++ = '\0'; 485 } 486 break; 487 } 488 } else { 489 490 /* 491 ** Section name not found; go to the next bracket & try again 492 ** Advance past '[' and keep scanning. 493 */ 494 workptr++; 495 } 496 } 497 498 return(retval); 499 } 500 501 502 /*********************************************************************************************** 503 * WWWritePrivateProfileString -- Write a string to the profile data block. * 504 * * 505 * INPUT: * 506 * section section name to write to * 507 * entry name of entry to write; if NULL, the section is deleted * 508 * string string to write; if NULL, the entry is deleted * 509 * profile INI buffer * 510 * * 511 * OUTPUT: * 512 * true = success, false = failure * 513 * * 514 * WARNINGS: * 515 * This function has to translate newlines into \r\n sequences. * 516 * * 517 * HISTORY: * 518 * 10/07/1992 JLB : Created. * 519 *=============================================================================================*/ 520 bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile) 521 { 522 char buffer[250]; // Working section buffer 523 char *offset; 524 char *next; // ptr to next section 525 char c; // Working character value 526 527 /* 528 ** Just return if nothing to do. 529 */ 530 if (!profile || !section) { 531 return(true); 532 } 533 534 /* 535 ** Try to find the section. WWGetPrivateProfileString with NULL entry name 536 ** will return all entry names in the given buffer, truncated to the given 537 ** buffer length. 'offset' will point to 1st entry in the section, NULL if 538 ** section not found. 539 */ 540 offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); 541 542 /* 543 ** If the section could not be found, then add it to the end. Don't add 544 ** anything if a removal of an entry is requested (it is obviously already 545 ** non-existent). Make sure two newlines precede the section name. 546 */ 547 if (!offset && entry) { 548 sprintf(buffer, "\r\n[%s]\r\n", section); 549 strcat(profile, buffer); 550 } 551 552 /* 553 ** If the section is there and 'entry' is NULL, remove the entire section 554 */ 555 if (offset && !entry) { 556 557 /* 558 ** 'next = end of section or end of file. 559 */ 560 next = strchr(offset, '['); 561 for (;;) { 562 if (next) { 563 c = *(next-1); 564 565 /* 566 ** If character before '[' is newline, this is the start of the 567 ** next section 568 */ 569 if (c == '\n') { 570 if ( *(next-1)=='\n' && *(next-3)=='\n') { 571 next -= 2; 572 } 573 break; 574 } 575 576 /* 577 ** This bracket was in the section; keep looking 578 */ 579 next = strchr(next+1, '['); 580 } else { 581 582 /* 583 ** No bracket found; set 'next' to the end of the file 584 */ 585 next = offset + strlen(offset); 586 break; 587 } 588 } 589 590 /* 591 ** Remove the section 592 */ 593 strcpy(offset,next); 594 595 return(true); 596 } 597 598 /* 599 ** Find the matching entry within the desired section. A NULL return buffer 600 ** with 0 length will just return the offset of the found entry, NULL if 601 ** entry not found. 602 */ 603 offset = WWGetPrivateProfileString(section, entry, NULL, NULL, 0, profile); 604 605 /* 606 ** Remove any existing entry 607 */ 608 if (offset) { 609 int eol; // Working EOL offset. 610 611 /* 612 ** Get # characters up to newline; \n is used since we're after the end 613 ** of this line 614 */ 615 eol = strcspn(offset, "\n"); 616 617 /* 618 ** Erase the entry by strcpy'ing the entire INI file over this entry 619 */ 620 if (eol) { 621 strcpy(offset, offset + eol + 1); 622 } 623 } else { 624 625 /* 626 ** Entry doesn't exist, so point 'offset' to the 1st entry position in 627 ** the section. 628 */ 629 offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); 630 } 631 632 /* 633 ** Add the desired entry. 634 */ 635 if (entry && string) { 636 637 /* 638 ** Generate entry string. 639 */ 640 sprintf(buffer, "%s=%s\r\n", entry, string); 641 642 /* 643 ** Make room for new entry. 644 */ 645 memmove(offset+strlen(buffer), offset, strlen(offset)+1); 646 647 /* 648 ** Copy the entry into the INI buffer. 649 */ 650 memcpy(offset, buffer, strlen(buffer)); 651 } 652 653 return(true); 654 }