PROFILE.CPP 24 KB

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