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