PROFILE.CPP 24 KB

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