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