dlgsavegame.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. /*
  2. ** Command & Conquer Renegade(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. /***********************************************************************************************
  19. *** 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 ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Combat *
  23. * *
  24. * $Archive:: /Commando/Code/Commando/dlgsavegame.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 12/18/01 5:23p $*
  29. * *
  30. * $Revision:: 16 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "dlgsavegame.h"
  36. #include "listctrl.h"
  37. #include "dialogresource.h"
  38. #include "gamedata.h"
  39. #include "gamemode.h"
  40. #include "gameinitmgr.h"
  41. #include "savegame.h"
  42. #include "ffactory.h"
  43. #include "commandosaveload.h"
  44. #include "translatedb.h"
  45. #include "string_ids.h"
  46. #include "editctrl.h"
  47. ////////////////////////////////////////////////////////////////
  48. // Local constants
  49. ////////////////////////////////////////////////////////////////
  50. static enum
  51. {
  52. MBEVENT_OVERWRITE_PROMPT = 1,
  53. MBEVENT_DELETE_PROMPT,
  54. };
  55. static enum
  56. {
  57. COL_DATE = 0,
  58. COL_TIME,
  59. COL_NAME
  60. };
  61. ////////////////////////////////////////////////////////////////
  62. //
  63. // On_Init_Dialog
  64. //
  65. ////////////////////////////////////////////////////////////////
  66. void
  67. SaveGameMenuClass::On_Init_Dialog (void)
  68. {
  69. //
  70. // Get a pointer to the list control
  71. //
  72. ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_LOAD_GAME_LIST_CTRL);
  73. if (list_ctrl != NULL) {
  74. //
  75. // Configure the columns
  76. //
  77. list_ctrl->Add_Column (TRANSLATE (IDS_MENU_TIME), 0.25F, Vector3 (1, 1, 1));
  78. list_ctrl->Add_Column (TRANSLATE (IDS_MENU_DATE), 0.25F, Vector3 (1, 1, 1));
  79. list_ctrl->Add_Column (TRANSLATE (IDS_MENU_TEXT179), 0.5F, Vector3 (1, 1, 1));
  80. }
  81. //
  82. // Limit the amount of text you can type into the edit control
  83. //
  84. EditCtrlClass *edit_ctrl = (EditCtrlClass *)Get_Dlg_Item (IDC_FILENAME_EDIT);
  85. if (edit_ctrl) {
  86. edit_ctrl->Set_Text_Limit (64);
  87. }
  88. //
  89. // Populate the list
  90. //
  91. Reload_List (NULL);
  92. Update_Text_Field ();
  93. Update_Button_State ();
  94. MenuDialogClass::On_Init_Dialog ();
  95. return ;
  96. }
  97. ////////////////////////////////////////////////////////////////
  98. //
  99. // On_ListCtrl_Column_Click
  100. //
  101. ////////////////////////////////////////////////////////////////
  102. void
  103. SaveGameMenuClass::On_ListCtrl_Column_Click
  104. (
  105. ListCtrlClass *list_ctrl,
  106. int ctrl_id,
  107. int col_index
  108. )
  109. {
  110. if (ctrl_id == IDC_LOAD_GAME_LIST_CTRL) {
  111. //
  112. // Determine
  113. //
  114. if (col_index == CurrSortCol) {
  115. IsSortAscending = !IsSortAscending;
  116. } else {
  117. CurrSortCol = col_index;
  118. IsSortAscending = true;
  119. }
  120. //
  121. // Sort the list by the column that was clicked
  122. //
  123. list_ctrl->Sort (LoadListSortCallback, MAKELONG (CurrSortCol, IsSortAscending));
  124. //
  125. // Update the sort marker
  126. //
  127. ListCtrlClass::SORT_TYPE type = IsSortAscending ? ListCtrlClass::SORT_ASCENDING : ListCtrlClass::SORT_DESCENDING;
  128. list_ctrl->Set_Sort_Designator (CurrSortCol, type);
  129. }
  130. return ;
  131. }
  132. ////////////////////////////////////////////////////////////////
  133. //
  134. // On_ListCtrl_Delete_Entry
  135. //
  136. ////////////////////////////////////////////////////////////////
  137. void
  138. SaveGameMenuClass::On_ListCtrl_Delete_Entry
  139. (
  140. ListCtrlClass *list_ctrl,
  141. int ctrl_id,
  142. int item_index
  143. )
  144. {
  145. if (ctrl_id == IDC_LOAD_GAME_LIST_CTRL) {
  146. //
  147. // Remove the data we associated with this entry
  148. //
  149. FILETIME *file_time = (FILETIME *)list_ctrl->Get_Entry_Data (item_index, 0);
  150. StringClass *filename = (StringClass *)list_ctrl->Get_Entry_Data (item_index, 2);
  151. list_ctrl->Set_Entry_Data (item_index, 0, 0);
  152. list_ctrl->Set_Entry_Data (item_index, 2, 0);
  153. if (file_time != NULL) {
  154. delete file_time;
  155. }
  156. if (filename != NULL) {
  157. delete filename;
  158. }
  159. }
  160. return ;
  161. }
  162. ////////////////////////////////////////////////////////////////
  163. //
  164. // LoadListSortCallback
  165. //
  166. ////////////////////////////////////////////////////////////////
  167. int CALLBACK
  168. SaveGameMenuClass::LoadListSortCallback (ListCtrlClass *list_ctrl, int item_index1, int item_index2, uint32 user_param)
  169. {
  170. int retval = 0;
  171. //
  172. // Get the sorting params
  173. //
  174. int sort_col_index = LOWORD (user_param);
  175. BOOL sort_ascending = HIWORD (user_param);
  176. if (list_ctrl->Get_Entry_Data (item_index1, 0) == NULL) {
  177. retval = -1;
  178. } else if (list_ctrl->Get_Entry_Data (item_index2, 0) == NULL) {
  179. retval = 1;
  180. } else {
  181. if (sort_col_index == 0 || sort_col_index == 1) {
  182. //
  183. // Sort by time
  184. //
  185. FILETIME *file_time1 = (FILETIME *)list_ctrl->Get_Entry_Data (item_index1, 0);
  186. FILETIME *file_time2 = (FILETIME *)list_ctrl->Get_Entry_Data (item_index2, 0);
  187. retval = ::CompareFileTime (file_time1, file_time2);
  188. } else {
  189. //
  190. // Sort by name
  191. //
  192. const WCHAR *name1 = list_ctrl->Get_Entry_Text (item_index1, 2);
  193. const WCHAR *name2 = list_ctrl->Get_Entry_Text (item_index2, 2);
  194. retval = ::wcsicmp (name1, name2);
  195. }
  196. //
  197. // Invert the return value if we are sorting descendingly
  198. //
  199. if (sort_ascending == false) {
  200. retval = -retval;
  201. }
  202. }
  203. return retval;
  204. }
  205. ////////////////////////////////////////////////////////////////
  206. //
  207. // On_Command
  208. //
  209. ////////////////////////////////////////////////////////////////
  210. void
  211. SaveGameMenuClass::On_Command (int ctrl_id, int message_id, DWORD param)
  212. {
  213. switch (ctrl_id)
  214. {
  215. case IDC_SAVE_GAME_BUTTON:
  216. Save_Game (true);
  217. break;
  218. case IDC_DELETE_GAME_BUTTON:
  219. Delete_Game (true);
  220. break;
  221. }
  222. MenuDialogClass::On_Command (ctrl_id, message_id, param);
  223. return ;
  224. }
  225. ////////////////////////////////////////////////////////////////
  226. //
  227. // Save_Game
  228. //
  229. ////////////////////////////////////////////////////////////////
  230. void
  231. SaveGameMenuClass::Save_Game (bool prompt)
  232. {
  233. //
  234. // Get a pointer to the list control
  235. //
  236. ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_LOAD_GAME_LIST_CTRL);
  237. if (list_ctrl == NULL) {
  238. return ;
  239. }
  240. //
  241. // Don't save if the save button was disabled
  242. //
  243. if (Is_Dlg_Item_Enabled (IDC_SAVE_GAME_BUTTON) == false) {
  244. return ;
  245. }
  246. //
  247. // Get the currently selected entry
  248. //
  249. int item_index = list_ctrl->Get_Curr_Sel ();
  250. if (item_index != -1) {
  251. //
  252. // Determine what filename to save under
  253. //
  254. StringClass full_path;
  255. bool save_file = true;
  256. if (list_ctrl->Get_Entry_Data (item_index, 0) == NULL) {
  257. Get_Unique_Save_Filename (full_path);
  258. } else {
  259. StringClass filename = ((StringClass *)list_ctrl->Get_Entry_Data (item_index, 2))->Peek_Buffer ();
  260. //
  261. // Build a full filename
  262. //
  263. full_path = "save\\";
  264. full_path += filename;
  265. //
  266. // Display a message to the user asking if they really want to do this...
  267. //
  268. if (prompt) {
  269. DlgMsgBox::DoDialog (IDS_MENU_SAVE_OVERWRITE_TITLE, IDS_MENU_SAVE_OVERWRITE_MSG,
  270. DlgMsgBox::YesNo, this, MBEVENT_OVERWRITE_PROMPT);
  271. save_file = false;
  272. }
  273. }
  274. if (save_file) {
  275. if (Check_HD_Space ()) {
  276. //
  277. // Save the game
  278. //
  279. SaveGameManager::Set_Description (Get_Dlg_Item_Text (IDC_FILENAME_EDIT));
  280. SaveGameManager::Save_Game( full_path, &_CommandoSaveLoad, NULL );
  281. //
  282. // Quit out of the dialog
  283. //
  284. End_Dialog ();
  285. }
  286. }
  287. }
  288. return ;
  289. }
  290. ////////////////////////////////////////////////////////////////
  291. //
  292. // On_ListCtrl_DblClk
  293. //
  294. ////////////////////////////////////////////////////////////////
  295. void
  296. SaveGameMenuClass::On_ListCtrl_DblClk
  297. (
  298. ListCtrlClass *list_ctrl,
  299. int ctrl_id,
  300. int item_index
  301. )
  302. {
  303. return ;
  304. }
  305. ////////////////////////////////////////////////////////////////
  306. //
  307. // Get_Unique_Save_Filename
  308. //
  309. ////////////////////////////////////////////////////////////////
  310. void
  311. SaveGameMenuClass::Get_Unique_Save_Filename (StringClass &filename)
  312. {
  313. int slot = 1;
  314. bool done = false;
  315. while (!done) {
  316. filename.Format ("save\\savegame%.2d.sav", slot ++);
  317. //
  318. // Check to see if this file exists
  319. //
  320. FileClass *file= _TheWritingFileFactory->Get_File (filename);
  321. if ( file ) {
  322. file->Open ();
  323. done = !file->Is_Available ();
  324. _TheWritingFileFactory->Return_File (file);
  325. }
  326. }
  327. return ;
  328. }
  329. ////////////////////////////////////////////////////////////////
  330. //
  331. // Update_Text_Field
  332. //
  333. ////////////////////////////////////////////////////////////////
  334. void
  335. SaveGameMenuClass::Update_Text_Field (void)
  336. {
  337. //
  338. // Get a pointer to the list control
  339. //
  340. ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_LOAD_GAME_LIST_CTRL);
  341. if (list_ctrl == NULL) {
  342. return ;
  343. }
  344. //
  345. // Get the currently selected entry
  346. //
  347. int curr_sel = list_ctrl->Get_Curr_Sel ();
  348. if (curr_sel != -1) {
  349. //
  350. // Update the text field with the name of this save game
  351. //
  352. if (list_ctrl->Get_Entry_Data (curr_sel, 0) != NULL) {
  353. Set_Dlg_Item_Text (IDC_FILENAME_EDIT, list_ctrl->Get_Entry_Text (curr_sel, 2));
  354. } else {
  355. Set_Dlg_Item_Text (IDC_FILENAME_EDIT, L"");
  356. }
  357. } else {
  358. Set_Dlg_Item_Text (IDC_FILENAME_EDIT, L"");
  359. }
  360. return ;
  361. }
  362. ////////////////////////////////////////////////////////////////
  363. //
  364. // On_ListCtrl_Sel_Change
  365. //
  366. ////////////////////////////////////////////////////////////////
  367. void
  368. SaveGameMenuClass::On_ListCtrl_Sel_Change
  369. (
  370. ListCtrlClass *list_ctrl,
  371. int ctrl_id,
  372. int old_index,
  373. int new_index
  374. )
  375. {
  376. Update_Text_Field ();
  377. Update_Button_State ();
  378. return ;
  379. }
  380. ////////////////////////////////////////////////////////////////
  381. //
  382. // Delete_Game
  383. //
  384. ////////////////////////////////////////////////////////////////
  385. void
  386. SaveGameMenuClass::Delete_Game (bool prompt)
  387. {
  388. //
  389. // Get a pointer to the list control
  390. //
  391. ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_LOAD_GAME_LIST_CTRL);
  392. if (list_ctrl == NULL) {
  393. return ;
  394. }
  395. //
  396. // Get the currently selected entry
  397. //
  398. int item_index = list_ctrl->Get_Curr_Sel ();
  399. if (item_index != -1) {
  400. //
  401. // Determine what filename this entry refers to
  402. //
  403. if (list_ctrl->Get_Entry_Data (item_index, 0) != NULL) {
  404. StringClass filename = ((StringClass *)list_ctrl->Get_Entry_Data (item_index, 2))->Peek_Buffer ();
  405. if (prompt) {
  406. //
  407. // Build a confirmation message to display to the user
  408. //
  409. WideStringClass message;
  410. message.Format (TRANSLATE (IDS_MENU_DELETE_SAVE_MSG),
  411. list_ctrl->Get_Entry_Text (item_index, COL_NAME));
  412. //
  413. // Display a message to the user asking if they really want to do this...
  414. //
  415. DlgMsgBox::DoDialog (TRANSLATE (IDS_MENU_DELETE_SAVE_TITLE), message, DlgMsgBox::YesNo,
  416. this, MBEVENT_DELETE_PROMPT);
  417. } else {
  418. //
  419. // Build a full path from which to delete the file
  420. //
  421. StringClass full_path = "data\\save\\";
  422. full_path += filename;
  423. //
  424. // Delete the file and remove its entry from the list
  425. //
  426. if (::DeleteFile (full_path) != 0) {
  427. list_ctrl->Delete_Entry (item_index);
  428. Update_Text_Field ();
  429. Update_Button_State ();
  430. }
  431. }
  432. }
  433. }
  434. return ;
  435. }
  436. ////////////////////////////////////////////////////////////////
  437. //
  438. // Reload_List
  439. //
  440. ////////////////////////////////////////////////////////////////
  441. void
  442. SaveGameMenuClass::Reload_List (const char *current_filename)
  443. {
  444. //
  445. // Get a pointer to the list control
  446. //
  447. ListCtrlClass *list_ctrl = (ListCtrlClass *)Get_Dlg_Item (IDC_LOAD_GAME_LIST_CTRL);
  448. if (list_ctrl == NULL) {
  449. return ;
  450. }
  451. //
  452. // Start fresh
  453. //
  454. list_ctrl->Delete_All_Entries ();
  455. //
  456. // Create an empty slot entry
  457. //
  458. int item_index = list_ctrl->Insert_Entry (0, L"");
  459. if (item_index >= 0) {
  460. list_ctrl->Set_Entry_Text (item_index, 2, TRANSLATE (IDS_MENU_EMPTY_SLOT));
  461. list_ctrl->Set_Curr_Sel (item_index);
  462. }
  463. WIN32_FIND_DATA find_info = { 0 };
  464. BOOL keep_going = TRUE;
  465. HANDLE file_find = NULL;
  466. //
  467. // Build a list of all the saved games we know about
  468. //
  469. int index = 1;
  470. for (file_find = ::FindFirstFile ("data\\save\\*.sav", &find_info);
  471. (file_find != INVALID_HANDLE_VALUE) && keep_going;
  472. keep_going = ::FindNextFile (file_find, &find_info))
  473. {
  474. //
  475. // Get the user description and map name from the file
  476. //
  477. WideStringClass description;
  478. WideStringClass map_name;
  479. SaveGameManager::Peek_Description (find_info.cFileName, description, map_name);
  480. //
  481. // Get the time this file was last written
  482. //
  483. SYSTEMTIME system_time = { 0 };
  484. FILETIME local_time = { 0 };
  485. ::FileTimeToLocalFileTime (&find_info.ftLastWriteTime, &local_time);
  486. ::FileTimeToSystemTime (&local_time, &system_time);
  487. //
  488. // Build the time and date strings
  489. //
  490. WideStringClass time_string;
  491. WideStringClass date_string;
  492. time_string.Format (L"%d:%02d:%02d", system_time.wHour, system_time.wMinute, system_time.wSecond);
  493. date_string.Format (L"%d/%d/%d", system_time.wMonth, system_time.wDay, system_time.wYear);
  494. //
  495. // Add this entry to the list control
  496. //
  497. int item_index = list_ctrl->Insert_Entry (index ++, time_string);
  498. if (item_index >= 0) {
  499. list_ctrl->Set_Entry_Text (item_index, 1, date_string);
  500. list_ctrl->Set_Entry_Text (item_index, 2, description);
  501. list_ctrl->Set_Entry_Data (item_index, 0, (uint32)new FILETIME(local_time));
  502. list_ctrl->Set_Entry_Data (item_index, 2, (uint32)new StringClass(find_info.cFileName));
  503. //
  504. // Select this entry if its the default
  505. //
  506. if ( current_filename != NULL &&
  507. ::lstrcmpi (current_filename, find_info.cFileName) == 0)
  508. {
  509. list_ctrl->Set_Curr_Sel (item_index);
  510. }
  511. }
  512. }
  513. if (file_find != INVALID_HANDLE_VALUE) {
  514. ::FindClose (file_find);
  515. }
  516. //
  517. // Sort the list
  518. //
  519. list_ctrl->Sort (LoadListSortCallback, MAKELONG (CurrSortCol, IsSortAscending));
  520. //
  521. // Update the sort marker
  522. //
  523. ListCtrlClass::SORT_TYPE type = IsSortAscending ? ListCtrlClass::SORT_ASCENDING : ListCtrlClass::SORT_DESCENDING;
  524. list_ctrl->Set_Sort_Designator (CurrSortCol, type);
  525. return ;
  526. }
  527. ////////////////////////////////////////////////////////////////
  528. //
  529. // HandleNotification
  530. //
  531. ////////////////////////////////////////////////////////////////
  532. void
  533. SaveGameMenuClass::HandleNotification (DlgMsgBoxEvent &event)
  534. {
  535. if (event.Get_User_Data () == MBEVENT_OVERWRITE_PROMPT) {
  536. //
  537. // The user has confirmed the overwrite, so save the game
  538. //
  539. if (event.Event () == DlgMsgBoxEvent::Yes) {
  540. Save_Game (false);
  541. }
  542. } else if (event.Get_User_Data () == MBEVENT_DELETE_PROMPT) {
  543. //
  544. // The user has confirmed the delete, so delete the file
  545. //
  546. if (event.Event () == DlgMsgBoxEvent::Yes) {
  547. Delete_Game (false);
  548. }
  549. }
  550. return ;
  551. }
  552. ////////////////////////////////////////////////////////////////
  553. //
  554. // Update_Button_State
  555. //
  556. ////////////////////////////////////////////////////////////////
  557. void
  558. SaveGameMenuClass::Update_Button_State (void)
  559. {
  560. WideStringClass text = Get_Dlg_Item_Text (IDC_FILENAME_EDIT);
  561. bool enable = (text.Get_Length () > 0);
  562. //
  563. // Enable/disable the save game button as necessary
  564. //
  565. Enable_Dlg_Item (IDC_SAVE_GAME_BUTTON, enable);
  566. return ;
  567. }
  568. ////////////////////////////////////////////////////////////////
  569. //
  570. // On_EditCtrl_Change
  571. //
  572. ////////////////////////////////////////////////////////////////
  573. void
  574. SaveGameMenuClass::On_EditCtrl_Change (EditCtrlClass *edit_ctrl, int ctrl_id)
  575. {
  576. if (ctrl_id == IDC_FILENAME_EDIT) {
  577. Update_Button_State ();
  578. }
  579. return ;
  580. }
  581. ////////////////////////////////////////////////////////////////
  582. //
  583. // On_EditCtrl_Enter_Pressed
  584. //
  585. ////////////////////////////////////////////////////////////////
  586. void
  587. SaveGameMenuClass::On_EditCtrl_Enter_Pressed (EditCtrlClass *edit_ctrl, int ctrl_id)
  588. {
  589. if (ctrl_id == IDC_FILENAME_EDIT) {
  590. Save_Game (true);
  591. }
  592. return ;
  593. }
  594. ////////////////////////////////////////////////////////////////
  595. //
  596. // Check_HD_Space
  597. //
  598. ////////////////////////////////////////////////////////////////
  599. bool
  600. SaveGameMenuClass::Check_HD_Space (void)
  601. {
  602. bool retval = true;
  603. ULARGE_INTEGER freebytecount; // Free bytes on disk available to caller (caller may not have access to entire disk).
  604. ULARGE_INTEGER totalbytecount; // Total bytes on disk.
  605. StringClass kernelpathname;
  606. __int64 diskspace;
  607. int (__stdcall *getfreediskspaceex) (LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
  608. // Get the free disk space on the drive.
  609. // NOTE IML: For Win'95, must query for support for GetDiskFreeSpaceEx before using it - otherwise use GetDiskFreeSpace().
  610. GetSystemDirectory (kernelpathname.Get_Buffer (_MAX_PATH), _MAX_PATH);
  611. kernelpathname += "\\";
  612. kernelpathname += "Kernel32.dll";
  613. getfreediskspaceex = (int (_stdcall*) (LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER)) GetProcAddress (GetModuleHandle (kernelpathname.Peek_Buffer()), "GetDiskFreeSpaceExA");
  614. if (getfreediskspaceex != NULL) {
  615. if (!getfreediskspaceex (NULL, &freebytecount, &totalbytecount, NULL)) return (false);
  616. // Convert to a 64-bit integer.
  617. diskspace = freebytecount.QuadPart;
  618. } else {
  619. DWORD sectorspercluster, bytespersector, freeclustercount, totalclustercount;
  620. // The Ex version is not available. Use the Win'95 version.
  621. // QUESTION: SDK docs say that values returned by this function are erroneous if partition > 2Gb.
  622. // Does that mean that the partition is guaranteed to be <= 2Gb if Ex is not available?
  623. if (!GetDiskFreeSpace (NULL, &sectorspercluster, &bytespersector, &freeclustercount, &totalclustercount)) return (false);
  624. diskspace = sectorspercluster * bytespersector * freeclustercount;
  625. }
  626. //
  627. // Is there at least 2 megs of disk space available?
  628. //
  629. const __int64 TWO_MEGS = (1024 * 1024 * 2);
  630. if (diskspace < TWO_MEGS) {
  631. //
  632. // Let the user know that they can't save because of lack of disk space
  633. //
  634. DlgMsgBox::DoDialog (IDS_MENU_SAVE_NO_DISK_SPACE_TITLE, IDS_MENU_SAVE_NO_DISK_SPACE_MSG);
  635. retval = false;
  636. }
  637. return retval;
  638. }