LOADDLG.CPP 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /*
  2. ** Command & Conquer Red Alert(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: /CounterStrike/LOADDLG.CPP 1 3/03/97 10:25a 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 : LOADDLG.CPP *
  26. * *
  27. * Programmer : Maria Legg, Joe Bostic, Bill Randolph *
  28. * *
  29. * Start Date : March 19, 1995 *
  30. * *
  31. * Last Update : June 25, 1995 [JLB] *
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * LoadOptionsClass::LoadOptionsClass -- class constructor *
  36. * LoadOptionsClass::~LoadOptionsClass -- class destructor *
  37. * LoadOptionsClass::Process -- main processing routine *
  38. * LoadOptionsClass::Clear_List -- clears the list box & Files arrays *
  39. * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays *
  40. * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays *
  41. * LoadOptionsClass::Compare -- for qsort *
  42. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  43. #include "function.h"
  44. #include <io.h> // for unlink
  45. /***********************************************************************************************
  46. * LoadOptionsClass::LoadOptionsClass -- class constructor *
  47. * *
  48. * INPUT: *
  49. * style style for this load/save dialog (LOAD/SAVE/DELETE) *
  50. * *
  51. * OUTPUT: *
  52. * none. *
  53. * *
  54. * WARNINGS: *
  55. * none. *
  56. * *
  57. * HISTORY: *
  58. * 02/14/1995 BR : Created. *
  59. *=============================================================================================*/
  60. LoadOptionsClass::LoadOptionsClass(LoadStyleType style)
  61. {
  62. Style = style;
  63. Files.Clear();
  64. }
  65. /***********************************************************************************************
  66. * LoadOptionsClass::~LoadOptionsClass -- class destructor *
  67. * *
  68. * INPUT: *
  69. * none. *
  70. * *
  71. * OUTPUT: *
  72. * none. *
  73. * *
  74. * WARNINGS: *
  75. * none. *
  76. * *
  77. * HISTORY: *
  78. * 02/14/1995 BR : Created. *
  79. *=============================================================================================*/
  80. LoadOptionsClass::~LoadOptionsClass()
  81. {
  82. for (int i = 0; i < Files.Count(); i++) {
  83. delete Files[i];
  84. }
  85. Files.Clear();
  86. }
  87. /***********************************************************************************************
  88. * LoadOptionsClass::Process -- main processing routine *
  89. * *
  90. * INPUT: *
  91. * none. *
  92. * *
  93. * OUTPUT: *
  94. * false = User cancelled, true = operation completed *
  95. * *
  96. * WARNINGS: *
  97. * none. *
  98. * *
  99. * HISTORY: *
  100. * 02/14/1995 BR : Created. *
  101. *=============================================================================================*/
  102. int LoadOptionsClass::Process(void)
  103. {
  104. /*
  105. ** Dialog & button dimensions
  106. */
  107. int d_dialog_w = 250 * RESFACTOR; // dialog width
  108. int d_dialog_h = 156 * RESFACTOR; // dialog height
  109. int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); // centered x-coord
  110. int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); // centered y-coord
  111. int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center
  112. int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text
  113. int d_margin = 7 * RESFACTOR; // margin width/height
  114. int x_margin = 16 * RESFACTOR; // margin width/height
  115. int d_list_w = d_dialog_w - (x_margin * 2);
  116. int d_list_h = 104 * RESFACTOR;
  117. int d_list_x = d_dialog_x + x_margin;
  118. int d_list_y = d_dialog_y + d_margin + d_txt8_h + d_margin;
  119. int d_edit_w = d_dialog_w - (x_margin * 2);
  120. int d_edit_h = 13 * RESFACTOR;
  121. int d_edit_x = d_dialog_x + x_margin;
  122. int d_edit_y = d_list_y + d_list_h - (30 * RESFACTOR) + d_margin + d_txt8_h;
  123. #if (GERMAN | FRENCH)
  124. int d_button_w = 50 * RESFACTOR;
  125. #else
  126. int d_button_w = 40 * RESFACTOR;
  127. #endif
  128. int d_button_h = 13 * RESFACTOR;
  129. int d_button_x = d_dialog_cx - d_button_w - d_margin;
  130. int d_button_y = d_dialog_y + d_dialog_h - d_button_h - d_margin;
  131. #if defined(GERMAN) || defined(FRENCH)
  132. int d_cancel_w = 60 * RESFACTOR;//BG:40
  133. #else
  134. int d_cancel_w = 40 * RESFACTOR;
  135. #endif
  136. int d_cancel_h = 13 * RESFACTOR;
  137. int d_cancel_x = d_dialog_cx + d_margin;
  138. int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin;
  139. /*
  140. ** Button enumerations
  141. */
  142. enum {
  143. BUTTON_LOAD = 100,
  144. BUTTON_SAVE,
  145. BUTTON_DELETE,
  146. BUTTON_CANCEL,
  147. BUTTON_LIST,
  148. BUTTON_EDIT,
  149. };
  150. /*
  151. ** Redraw values: in order from "top" to "bottom" layer of the dialog
  152. */
  153. typedef enum {
  154. REDRAW_NONE = 0,
  155. REDRAW_BUTTONS,
  156. REDRAW_BACKGROUND,
  157. REDRAW_ALL = REDRAW_BACKGROUND
  158. } RedrawType;
  159. /*
  160. ** Dialog variables
  161. */
  162. bool cancel = false; // true = user cancels
  163. int list_ht = d_list_h; // adjusted list box height
  164. /*
  165. ** Other Variables
  166. */
  167. int btn_txt; // text on the 'OK' button
  168. int btn_id; // ID of 'OK' button
  169. int caption; // dialog caption
  170. int game_idx = 0; // index of game to save/load/etc
  171. int game_num = 0; // file number of game to load/save/etc
  172. char game_descr[DESCRIP_MAX] = {0}; // save-game description
  173. char fname[_MAX_NAME+_MAX_EXT]; // for generating filename to delete
  174. int rc; // return code
  175. /*
  176. ** Buttons
  177. */
  178. ControlClass * commands = NULL; // the button list
  179. switch (Style) {
  180. case LOAD:
  181. btn_txt = TXT_LOAD_BUTTON;
  182. btn_id = BUTTON_LOAD;
  183. caption = TXT_LOAD_MISSION;
  184. break;
  185. case SAVE:
  186. btn_txt = TXT_SAVE_BUTTON;
  187. btn_id = BUTTON_SAVE;
  188. caption = TXT_SAVE_MISSION;
  189. list_ht -= 30;
  190. break;
  191. default:
  192. btn_txt = TXT_DELETE_BUTTON;
  193. btn_id = BUTTON_DELETE;
  194. caption = TXT_DELETE_MISSION;
  195. break;
  196. }
  197. TextButtonClass button (btn_id, btn_txt, TPF_BUTTON, d_button_x, d_button_y, d_button_w);
  198. TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w);
  199. ListClass listbtn (BUTTON_LIST, d_list_x, d_list_y, d_list_w, list_ht,
  200. TPF_6PT_GRAD | TPF_NOSHADOW,
  201. MFCD::Retrieve("BTN-UP.SHP"),
  202. MFCD::Retrieve("BTN-DN.SHP"));
  203. EditClass editbtn (BUTTON_EDIT, game_descr, sizeof(game_descr)-4, TPF_6PT_GRAD|TPF_NOSHADOW, d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC);
  204. /*
  205. ** Initialize.
  206. */
  207. Set_Logic_Page(SeenBuff);
  208. Fill_List(&listbtn);
  209. /*
  210. ** Do nothing if list is empty.
  211. */
  212. if ((Style == LOAD || Style == WWDELETE) && listbtn.Count()==0) {
  213. Clear_List(&listbtn);
  214. WWMessageBox().Process(TXT_NO_SAVES);
  215. return(false);
  216. }
  217. /*
  218. ** Create the button list.
  219. */
  220. commands = &button;
  221. cancelbtn.Add_Tail(*commands);
  222. listbtn.Add_Tail(*commands);
  223. if (Style == SAVE) {
  224. editbtn.Add_Tail(*commands);
  225. editbtn.Set_Focus();
  226. }
  227. /*
  228. ** Main Processing Loop.
  229. */
  230. Keyboard->Clear();
  231. bool firsttime = true;
  232. bool display = true;
  233. bool process = true;
  234. while (process) {
  235. /*
  236. ** Invoke game callback.
  237. */
  238. if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) {
  239. Call_Back();
  240. } else {
  241. if (Main_Loop()) {
  242. process = false;
  243. cancel = true;
  244. }
  245. }
  246. #ifdef WIN32
  247. /*
  248. ** If we have just received input focus again after running in the background then
  249. ** we need to redraw.
  250. */
  251. if (AllSurfaces.SurfacesRestored) {
  252. AllSurfaces.SurfacesRestored=FALSE;
  253. display = true;
  254. }
  255. #endif
  256. /*
  257. ** Refresh display if needed.
  258. */
  259. if (display) {
  260. /*
  261. ** Display the dialog box.
  262. */
  263. Hide_Mouse();
  264. if (display) {
  265. Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h);
  266. Draw_Caption(caption, d_dialog_x, d_dialog_y, d_dialog_w);
  267. if (Style == SAVE) {
  268. Fancy_Text_Print(TXT_MISSION_DESCRIPTION, d_dialog_cx,
  269. d_edit_y - d_txt8_h, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER);
  270. }
  271. }
  272. /*
  273. ** Redraw the buttons.
  274. */
  275. if (display) {
  276. commands->Flag_List_To_Redraw();
  277. }
  278. Show_Mouse();
  279. display = false;
  280. }
  281. /*
  282. ** Get user input.
  283. */
  284. KeyNumType input = commands->Input();
  285. /*
  286. ** The first time through the processing loop, set the edit
  287. ** gadget to have the focus if this is the save dialog. The
  288. ** focus must be set here since the gadget list has changed
  289. ** and this change will cause any previous focus setting to be
  290. ** cleared by the input processing routine.
  291. */
  292. if (firsttime && Style == SAVE) {
  293. firsttime = false;
  294. editbtn.Set_Focus();
  295. editbtn.Flag_To_Redraw();
  296. }
  297. /*
  298. ** If the <RETURN> key was pressed, then default to the appropriate
  299. ** action button according to the style of this dialog box.
  300. */
  301. if (input == KN_RETURN || input == (BUTTON_EDIT|KN_BUTTON)) {
  302. ToggleClass * toggle = NULL;
  303. switch (Style) {
  304. case SAVE:
  305. input = (KeyNumType)(BUTTON_SAVE|KN_BUTTON);
  306. cancelbtn.Turn_Off();
  307. // cancelbtn.IsOn = false;
  308. toggle = (ToggleClass*)commands->Extract_Gadget(BUTTON_SAVE);
  309. if (toggle != NULL) {
  310. toggle->Turn_On();
  311. // toggle->IsOn = true;
  312. toggle->IsPressed = true;
  313. }
  314. break;
  315. case LOAD:
  316. input = (KeyNumType)(BUTTON_LOAD|KN_BUTTON);
  317. // cancelbtn.IsOn = false;
  318. cancelbtn.Turn_Off();
  319. toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_LOAD);
  320. if (toggle != NULL) {
  321. toggle->IsOn = true;
  322. toggle->IsPressed = true;
  323. }
  324. break;
  325. case WWDELETE:
  326. input = (KeyNumType)(BUTTON_DELETE|KN_BUTTON);
  327. // cancelbtn.IsOn = false;
  328. cancelbtn.Turn_Off();
  329. toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE);
  330. if (toggle != NULL) {
  331. toggle->IsOn = true;
  332. toggle->IsPressed = true;
  333. }
  334. break;
  335. }
  336. Hide_Mouse();
  337. commands->Draw_All(true);
  338. Show_Mouse();
  339. }
  340. /*
  341. ** Process input.
  342. */
  343. switch (input) {
  344. /*
  345. ** Load: if load fails, present a message, and stay in the dialog
  346. ** to allow the user to try another game
  347. */
  348. case (BUTTON_LOAD | KN_BUTTON):
  349. game_idx = listbtn.Current_Index();
  350. game_num = Files[game_idx]->Num;
  351. if (Files[game_idx]->Valid) {
  352. /*
  353. ** Start a timer before we load the game
  354. */
  355. CDTimerClass<SystemTimerClass> timer;
  356. // timer.Start();
  357. timer = TICKS_PER_SECOND*4;
  358. WWMessageBox().Process(TXT_LOADING, TXT_NONE);
  359. Theme.Fade_Out();
  360. rc = Load_Game(game_num);
  361. /*
  362. ** Make sure the message says on the screen at least 1 second
  363. */
  364. while (timer > 0) {
  365. Call_Back();
  366. }
  367. Keyboard->Clear();
  368. if (!rc) {
  369. WWMessageBox().Process(TXT_ERROR_LOADING_GAME);
  370. } else {
  371. Speak(VOX_LOAD1);
  372. while (Is_Speaking()) {
  373. Call_Back();
  374. }
  375. Hide_Mouse();
  376. SeenPage.Clear();
  377. GamePalette.Set();
  378. // Set_Palette(GamePalette);
  379. Show_Mouse();
  380. process = false;
  381. }
  382. } else {
  383. WWMessageBox().Process(TXT_OBSOLETE_SAVEGAME);
  384. }
  385. break;
  386. /*
  387. ** Save: Save the game & exit the dialog
  388. */
  389. case (BUTTON_EDIT | KN_BUTTON):
  390. case (BUTTON_SAVE | KN_BUTTON):
  391. if (!strlen(game_descr)) {
  392. WWMessageBox().Process(TXT_MUSTENTER_DESCRIPTION);
  393. firsttime = true;
  394. display = true;
  395. break;
  396. }
  397. game_idx = listbtn.Current_Index();
  398. if (Disk_Space_Available() < SAVE_GAME_DISK_SPACE && game_idx == 0) {
  399. WWMessageBox().Process(TXT_SPACE_CANT_SAVE);
  400. firsttime = true;
  401. display = true;
  402. break;
  403. }
  404. game_num = Files[game_idx]->Num;
  405. if (!Save_Game(game_num, game_descr)) {
  406. WWMessageBox().Process(TXT_ERROR_SAVING_GAME);
  407. } else {
  408. Speak(VOX_SAVE1);
  409. while (Is_Speaking()) {
  410. Call_Back();
  411. }
  412. CDTimerClass<SystemTimerClass> timer;
  413. // timer.Start();
  414. timer = TICKS_PER_SECOND*4;
  415. WWMessageBox().Process(TXT_GAME_WAS_SAVED, TXT_NONE, TXT_NONE);
  416. /*
  417. ** Delay to let the user read the message
  418. */
  419. while (timer > 0) {
  420. Call_Back();
  421. }
  422. Keyboard->Clear();
  423. }
  424. process = false;
  425. break;
  426. /*
  427. ** Delete: delete the file & stay in the dialog, to allow the user
  428. ** to delete multiple files.
  429. */
  430. case (BUTTON_DELETE | KN_BUTTON):
  431. game_idx = listbtn.Current_Index();
  432. game_num = Files[game_idx]->Num;
  433. if (WWMessageBox().Process(TXT_DELETE_FILE_QUERY, TXT_YES, TXT_NO)==0) {
  434. sprintf(fname, "SAVEGAME.%03d", game_num);
  435. unlink(fname);
  436. Clear_List(&listbtn);
  437. Fill_List(&listbtn);
  438. if (listbtn.Count() == 0) {
  439. process = false;
  440. } else {
  441. ToggleClass * toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE);
  442. if (toggle != NULL) {
  443. // toggle->IsOn = false;
  444. toggle->Turn_Off();
  445. toggle->IsPressed = false;
  446. toggle->Flag_To_Redraw();
  447. }
  448. }
  449. }
  450. display = true;
  451. break;
  452. /*
  453. ** If the user clicks on the list, see if the there is a new current
  454. ** item; if so, and if we're in SAVE mode, copy the list item into
  455. ** the save-game description field.
  456. */
  457. case (BUTTON_LIST | KN_BUTTON):
  458. if (Style != SAVE) {
  459. break;
  460. }
  461. if (listbtn.Count() && listbtn.Current_Index() != game_idx) {
  462. game_idx = listbtn.Current_Index();
  463. /*
  464. ** Copy the game's description, UNLESS it's the empty slot; if
  465. ** it is, set the edit buffer to empty.
  466. */
  467. if (game_idx != 0) {
  468. strcpy(game_descr, listbtn.Get_Item(game_idx));
  469. /*
  470. ** Strip any leading parenthesis off of the description.
  471. */
  472. if (game_descr[0] == '(') {
  473. char * ptr = strchr(game_descr, ')');
  474. if (ptr != NULL) {
  475. strcpy(game_descr, ptr+1);
  476. strtrim(game_descr);
  477. }
  478. }
  479. } else {
  480. game_descr[0] = 0;
  481. }
  482. editbtn.Set_Text(game_descr, 40);
  483. }
  484. break;
  485. /*
  486. ** ESC/Cancel: break
  487. */
  488. case (KN_ESC):
  489. case (BUTTON_CANCEL | KN_BUTTON):
  490. cancel = true;
  491. process = false;
  492. break;
  493. default:
  494. break;
  495. }
  496. }
  497. Clear_List(&listbtn);
  498. if (cancel) return(false);
  499. return(true);
  500. }
  501. /***********************************************************************************************
  502. * LoadOptionsClass::Clear_List -- clears the list box & Files arrays *
  503. * *
  504. * This step is essential, because it frees all the strings allocated for list items. *
  505. * *
  506. * INPUT: *
  507. * none. *
  508. * *
  509. * OUTPUT: *
  510. * none. *
  511. * *
  512. * WARNINGS: *
  513. * none. *
  514. * *
  515. * HISTORY: *
  516. * 02/14/1995 BR : Created. *
  517. *=============================================================================================*/
  518. void LoadOptionsClass::Clear_List(ListClass * list)
  519. {
  520. /*
  521. ** For every item in the list, free its buffer & remove it from the list.
  522. */
  523. int j = list->Count();
  524. for (int i = 0; i < j; i++) {
  525. list->Remove_Item(list->Get_Item(0));
  526. }
  527. /*
  528. ** Clear the array of game numbers
  529. */
  530. for (i = 0; i < Files.Count(); i++) {
  531. delete Files[i];
  532. }
  533. Files.Clear();
  534. }
  535. /***********************************************************************************************
  536. * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays *
  537. * *
  538. * INPUT: *
  539. * none. *
  540. * *
  541. * OUTPUT: *
  542. * none. *
  543. * *
  544. * WARNINGS: *
  545. * none. *
  546. * *
  547. * HISTORY: *
  548. * 02/14/1995 BR : Created. *
  549. * 06/25/1995 JLB : Shows which saved games are "(old)". *
  550. *=============================================================================================*/
  551. void LoadOptionsClass::Fill_List(ListClass * list)
  552. {
  553. FileEntryClass * fdata; // for adding entries to 'Files'
  554. char descr[DESCRIP_MAX+32];
  555. unsigned scenario; // scenario #
  556. HousesType house; // house
  557. struct find_t ff; // for _dos_findfirst
  558. int id;
  559. /*
  560. ** Make sure the list is empty
  561. */
  562. Clear_List(list);
  563. /*
  564. ** Add the Empty Slot entry
  565. */
  566. if (Style == SAVE) {
  567. fdata = new FileEntryClass;
  568. strcpy(fdata->Descr, Text_String(TXT_EMPTY_SLOT));
  569. fdata->DateTime = 0xffffffff; // will always be first
  570. Files.Add(fdata);
  571. }
  572. /*
  573. ** Find all savegame files
  574. */
  575. int rc = _dos_findfirst("SAVEGAME.*", _A_NORMAL, &ff);
  576. while (!rc) {
  577. if (stricmp(ff.name, NET_SAVE_FILE_NAME) != 0) {
  578. /*
  579. ** Extract the game ID from the filename
  580. */
  581. id = Num_From_Ext(ff.name);
  582. /*
  583. ** get the game's info; if success, add it to the list
  584. */
  585. bool ok = Get_Savefile_Info(id, descr, &scenario, &house);
  586. fdata = new FileEntryClass;
  587. fdata->Descr[0] = '\0';
  588. if (!ok) {
  589. strcpy(fdata->Descr, Text_String(TXT_OLD_GAME));
  590. } else {
  591. if (house == HOUSE_USSR || house == HOUSE_UKRAINE) {
  592. #ifdef WIN32
  593. sprintf(fdata->Descr, "(%s) ", Text_String(TXT_SOVIET));
  594. #else
  595. sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_SOVIET));
  596. #endif
  597. } else {
  598. #ifdef WIN32
  599. sprintf(fdata->Descr, "(%s) ", Text_String(TXT_ALLIES));
  600. #else
  601. sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_ALLIES));
  602. #endif
  603. }
  604. }
  605. strncat(fdata->Descr, descr, (sizeof(fdata->Descr)-strlen(fdata->Descr))-1);
  606. fdata->Valid = ok;
  607. fdata->Scenario = scenario;
  608. fdata->House = house;
  609. fdata->Num = id;
  610. fdata->DateTime = (((unsigned long)ff.wr_date) << 16) | (unsigned long)ff.wr_time;
  611. Files.Add(fdata);
  612. }
  613. /*
  614. ** Find the next file
  615. */
  616. rc = _dos_findnext(&ff);
  617. }
  618. /*
  619. ** If saving a game, determine a unique file ID for the empty slot
  620. */
  621. if (Style == SAVE) {
  622. /*
  623. ** Find an un-used number to associate with the Empty Slot by looking in
  624. ** GameNum for each number from 0 to 'N', where 'N' is the # of entries
  625. ** in the list; if any number isn't found, use that number; otherwise,
  626. ** use 'N + 1'.
  627. */
  628. for (int i = 0; i < Files.Count(); i++) { // i = the # we're searching for
  629. id = -1; // mark as 'not found'
  630. for (int j = 0; j < Files.Count(); j++) { // loop through all game ID's
  631. if (Files[j]->Num==i) { // if found, mark as found
  632. id = j;
  633. break;
  634. }
  635. }
  636. if (id == -1) break; // if ID not found, use this one
  637. }
  638. Files[0]->Num = i; // set the empty slot's ID
  639. }
  640. /*
  641. ** Now sort the list in order of Date/Time (newest first, oldest last)
  642. */
  643. qsort((void *)(&Files[0]), Files.Count(), sizeof(class FileEntryClass *), LoadOptionsClass::Compare);
  644. /*
  645. ** Now add every file's name to the list box
  646. */
  647. for (int i = 0; i < Files.Count(); i++) {
  648. list->Add_Item(Files[i]->Descr);
  649. }
  650. }
  651. /***********************************************************************************************
  652. * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays *
  653. * *
  654. * INPUT: *
  655. * fname filename to parse *
  656. * *
  657. * OUTPUT: *
  658. * File number for this name. *
  659. * *
  660. * WARNINGS: *
  661. * none. *
  662. * *
  663. * HISTORY: *
  664. * 02/14/1995 BR : Created. *
  665. *=============================================================================================*/
  666. int LoadOptionsClass::Num_From_Ext(char * fname)
  667. {
  668. char ext[_MAX_EXT];
  669. _splitpath(fname, NULL, NULL, NULL, ext);
  670. int num = atoi(ext + 1); // skip the '.'
  671. return(num);
  672. }
  673. /***********************************************************************************************
  674. * LoadOptionsClass::Compare -- for qsort *
  675. * *
  676. * INPUT: *
  677. * p1,p2 ptrs to elements to compare *
  678. * *
  679. * OUTPUT: *
  680. * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) *
  681. * *
  682. * WARNINGS: *
  683. * none. *
  684. * *
  685. * HISTORY: *
  686. * 02/14/1995 BR : Created. *
  687. *=============================================================================================*/
  688. int LoadOptionsClass::Compare(const void * p1, const void * p2)
  689. {
  690. class FileEntryClass * fe1, * fe2;
  691. fe1 = *((class FileEntryClass **)p1);
  692. fe2 = *((class FileEntryClass **)p2);
  693. if (fe1->DateTime > fe2->DateTime) return(-1);
  694. if (fe1->DateTime < fe2->DateTime) return(1);
  695. return(0);
  696. }