LCOV - code coverage report
Current view: top level - Core/Entities - PlayerEntityLoadSave.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 18 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             :  
       3             :  PlayerEntityLoadSave.m
       4             :  
       5             :  Oolite
       6             :  Copyright (C) 2004-2013 Giles C Williams and contributors
       7             :  
       8             :  This program is free software; you can redistribute it and/or
       9             :  modify it under the terms of the GNU General Public License
      10             :  as published by the Free Software Foundation; either version 2
      11             :  of the License, or (at your option) any later version.
      12             :  
      13             :  This program is distributed in the hope that it will be useful,
      14             :  but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :  GNU General Public License for more details.
      17             :  
      18             :  You should have received a copy of the GNU General Public License
      19             :  along with this program; if not, write to the Free Software
      20             :  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             :  MA 02110-1301, USA.
      22             :  
      23             :  */
      24             : 
      25             : #import "PlayerEntityLoadSave.h"
      26             : #import "PlayerEntityContracts.h"
      27             : #import "PlayerEntityControls.h"
      28             : #import "PlayerEntitySound.h"
      29             : 
      30             : #import "NSFileManagerOOExtensions.h"
      31             : #import "GameController.h"
      32             : #import "ResourceManager.h"
      33             : #import "OOStringExpander.h"
      34             : #import "PlayerEntityControls.h"
      35             : #import "ProxyPlayerEntity.h"
      36             : #import "ShipEntityAI.h"
      37             : #import "OOXMLExtensions.h"
      38             : #import "OOSound.h"
      39             : #import "OOColor.h"
      40             : #import "OOStringParsing.h"
      41             : #import "OOPListParsing.h"
      42             : #import "StationEntity.h"
      43             : #import "OOCollectionExtractors.h"
      44             : #import "OOConstToString.h"
      45             : #import "OOShipRegistry.h"
      46             : #import "OOTexture.h"
      47             : #import "NSStringOOExtensions.h"
      48             : #import "NSNumberOOExtensions.h"
      49             : #import "OOJavaScriptEngine.h"
      50             : 
      51             : 
      52             : // Name of modifier key used to issue commands. See also -isCommandModifierKeyDown.
      53             : #if OO_USE_CUSTOM_LOAD_SAVE
      54           0 : #define COMMAND_MODIFIER_KEY            "Ctrl"
      55             : #endif
      56             : 
      57             : 
      58             : static uint16_t PersonalityForCommanderDict(NSDictionary *dict);
      59             : 
      60             : 
      61             : #if OO_USE_CUSTOM_LOAD_SAVE
      62             : 
      63             : @interface MyOpenGLView (OOLoadSaveExtensions)
      64             : 
      65           0 : - (BOOL)isCommandModifierKeyDown;
      66             : 
      67             : @end
      68             : 
      69             : #endif
      70             : 
      71             : 
      72             : @interface PlayerEntity (OOLoadSavePrivate)
      73             : 
      74             : #if OOLITE_USE_APPKIT_LOAD_SAVE
      75             : 
      76           0 : - (BOOL) loadPlayerWithPanel;
      77           0 : - (void) savePlayerWithPanel;
      78             : 
      79             : #endif
      80             : 
      81             : #if OO_USE_CUSTOM_LOAD_SAVE
      82             : 
      83           0 : - (void) setGuiToLoadCommanderScreen;
      84           0 : - (void) setGuiToSaveCommanderScreen: (NSString *)cdrName;
      85           0 : - (void) setGuiToOverwriteScreen: (NSString *)cdrName;
      86           0 : - (void) lsCommanders: (GuiDisplayGen *)gui directory: (NSString*)directory pageNumber: (int)page highlightName: (NSString *)highlightName;
      87           0 : - (void) showCommanderShip: (int)cdrArrayIndex;
      88           0 : - (int) findIndexOfCommander: (NSString *)cdrName;
      89           0 : - (void) nativeSavePlayer: (NSString *)cdrName;
      90           0 : - (BOOL) existingNativeSave: (NSString *)cdrName;
      91             : 
      92             : #endif
      93             : 
      94           0 : - (void) writePlayerToPath:(NSString *)path;
      95             : 
      96             : @end
      97             : 
      98             : 
      99             : @implementation PlayerEntity (LoadSave)
     100             : 
     101             : - (BOOL)loadPlayer
     102             : {
     103             :         BOOL                            OK = YES;
     104             :         
     105             : #if OO_USE_APPKIT_LOAD_SAVE_ALWAYS
     106             :         OK = [self loadPlayerWithPanel];
     107             : #elif OOLITE_USE_APPKIT_LOAD_SAVE
     108             :         // OS X: use system open/save dialogs in windowed mode, custom interface in full-screen.
     109             :         if ([[UNIVERSE gameController] inFullScreenMode])
     110             :         {
     111             :                 [self setGuiToLoadCommanderScreen];
     112             :         }
     113             :         else
     114             :         {
     115             :                 OK = [self loadPlayerWithPanel];
     116             :         }
     117             : #else
     118             :         // Other platforms: use custom interface all the time.
     119             :         [self setGuiToLoadCommanderScreen];
     120             : #endif
     121             :         return OK;
     122             : }
     123             : 
     124             : 
     125             : - (void)savePlayer
     126             : {
     127             : #if OO_USE_APPKIT_LOAD_SAVE_ALWAYS
     128             :         [self savePlayerWithPanel];
     129             : #elif OOLITE_USE_APPKIT_LOAD_SAVE
     130             :         // OS X: use system open/save dialogs in windowed mode, custom interface in full-screen.
     131             :         if ([[UNIVERSE gameController] inFullScreenMode])
     132             :         {
     133             :                 [self setGuiToSaveCommanderScreen:self.lastsaveName];
     134             :         }
     135             :         else
     136             :         {
     137             :                 [self savePlayerWithPanel];
     138             :         }
     139             : #else
     140             :         // Other platforms: use custom interface all the time.
     141             :         [self setGuiToSaveCommanderScreen:[self lastsaveName]];
     142             : #endif
     143             : }
     144             : 
     145             : - (void) autosavePlayer
     146             : {
     147             :         NSString                *tmp_path = nil;
     148             :         NSString                *tmp_name = nil;
     149             :         NSString                *dir = [[UNIVERSE gameController] playerFileDirectory];
     150             :         
     151             :         tmp_name = [self lastsaveName];
     152             :         tmp_path = save_path;
     153             :         
     154             :         ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("AUTO_SAVE"));
     155             :         
     156             :         NSString *saveName = [self lastsaveName];
     157             :         NSString *autosaveSuffix = DESC(@"autosave-commander-suffix");
     158             :         
     159             :         if (![saveName hasSuffix:autosaveSuffix])
     160             :         {
     161             :                 saveName = [saveName stringByAppendingString:autosaveSuffix];
     162             :         }
     163             :         NSString *savePath = [dir stringByAppendingPathComponent:[saveName stringByAppendingString:@".oolite-save"]];
     164             :         
     165             :         [self setLastsaveName:saveName];
     166             :         
     167             :         @try
     168             :         {
     169             :                 [self writePlayerToPath:savePath];
     170             :         }
     171             :         @catch (id exception)
     172             :         {
     173             :                 // Suppress exceptions silently. Warning the user about failed autosaves would be pretty unhelpful.
     174             :         }
     175             :         
     176             :         if (tmp_path != nil)
     177             :         {
     178             :                 [save_path autorelease];
     179             :                 save_path = [tmp_path copy];
     180             :         }
     181             :         [self setLastsaveName:tmp_name];
     182             : }
     183             : 
     184             : 
     185             : - (void) quicksavePlayer
     186             : {
     187             :         MyOpenGLView    *gameView = [UNIVERSE gameView];
     188             :         NSString                *path = nil;
     189             :         
     190             :         path = save_path;
     191             :         if (!path)  path = [[gameView gameController] playerFileToLoad];
     192             :         if (!path)
     193             :         {
     194             :                 OOLog(@"quickSave.failed.noName", @"%@", @"ERROR no file name returned by [[gameView gameController] playerFileToLoad]");
     195             :                 [NSException raise:@"OoliteGameNotSavedException"
     196             :                                         format:@"ERROR no file name returned by [[gameView gameController] playerFileToLoad]"];
     197             :         }
     198             :         
     199             :         ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("QUICK_SAVE"));
     200             :         
     201             :         [self writePlayerToPath:path];
     202             :         [[UNIVERSE gameView] suppressKeysUntilKeyUp];
     203             :         [self setGuiToStatusScreen];
     204             : }
     205             : 
     206             : 
     207             : - (void) setGuiToScenarioScreen:(int)page
     208             : {
     209             :         NSArray *scenarios = [UNIVERSE scenarios];
     210             :         [UNIVERSE removeDemoShips];
     211             :         // GUI stuff
     212             :         {
     213             :                 GuiDisplayGen   *gui = [UNIVERSE gui];
     214             :                 OOGUIRow                start_row = GUI_ROW_SCENARIOS_START;
     215             :                 OOGUIRow                row = start_row;
     216             :                 BOOL                    guiChanged = (gui_screen != GUI_SCREEN_NEWGAME);
     217             : 
     218             :                 [gui clearAndKeepBackground:!guiChanged];
     219             :                 [gui setTitle:DESC(@"oolite-newgame-title")];
     220             : 
     221             :                 OOGUITabSettings tab_stops;
     222             :                 tab_stops[0] = 0;
     223             :                 tab_stops[1] = -480;
     224             :                 [gui setTabStops:tab_stops];
     225             : 
     226             :                 unsigned n_rows = GUI_MAX_ROWS_SCENARIOS;
     227             :                 NSUInteger i, count = [scenarios count];
     228             : 
     229             :                 NSDictionary *scenario = nil;
     230             : 
     231             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-scenario-exit"), @" <----- ", nil] forRow:start_row - 2];
     232             :                 [gui setColor:[OOColor redColor] forRow:start_row - 2];
     233             :                 [gui setKey:@"exit" forRow:start_row - 2];
     234             :                 
     235             : 
     236             :                 if (page > 0)
     237             :                 {
     238             :                         [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @" <-- ", nil] forRow:start_row - 1];
     239             :                         [gui setColor:[OOColor greenColor] forRow:start_row - 1];
     240             :                         [gui setKey:[NSString stringWithFormat:@"__page:%i",page-1] forRow:start_row - 1];
     241             :                 }
     242             : 
     243             :                 [self setShowDemoShips:NO];
     244             : 
     245             :                 for (i = page*n_rows ; i < count && row < start_row + n_rows ; i++)
     246             :                 {
     247             :                         scenario = [[UNIVERSE scenarios] objectAtIndex:i];
     248             :                         NSString *scenarioName = [NSString stringWithFormat:@" %@ ",[scenario oo_stringForKey:@"name"]];
     249             :                         [gui setText:OOExpand(scenarioName) forRow:row];
     250             :                         [gui setKey:[NSString stringWithFormat:@"Scenario:%lu", (unsigned long)i] forRow:row];
     251             :                         ++row;
     252             :                 }
     253             : 
     254             :                 if ((page+1) * n_rows < count)
     255             :                 {
     256             :                         [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @" --> ", nil] forRow:row];
     257             :                         [gui setColor:[OOColor greenColor] forRow:row];
     258             :                         [gui setKey:[NSString stringWithFormat:@"__page:%i",page+1] forRow:row];
     259             :                         ++row;
     260             :                 }
     261             :                 
     262             :                 [gui setSelectableRange:NSMakeRange(start_row - 2,3 + row - start_row)];
     263             :                 [gui setSelectedRow:start_row];
     264             :                 [self showScenarioDetails];
     265             : 
     266             :                 gui_screen = GUI_SCREEN_NEWGAME;
     267             :         
     268             :                 if (guiChanged)
     269             :                 {
     270             :                         [gui setBackgroundTextureKey:@"newgame"];
     271             :                         [gui setForegroundTextureKey:@"newgame_overlay"];
     272             :                 }
     273             :         }
     274             :         
     275             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
     276             : }
     277             : 
     278             : - (void) addScenarioModel:(NSString *)shipKey
     279             : {
     280             :         [self showShipModelWithKey:shipKey shipData:nil personality:0 factorX:1.2 factorY:0.8 factorZ:6.4 inContext:@"scenario"];
     281             : }
     282             : 
     283             : 
     284             : - (void) showScenarioDetails
     285             : {
     286             :         GuiDisplayGen* gui = [UNIVERSE gui];
     287             :         NSString* key = [gui selectedRowKey];
     288             :         [UNIVERSE removeDemoShips];
     289             : 
     290             :         if ([key hasPrefix:@"Scenario"])
     291             :         {
     292             :                 int item = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
     293             :                 NSDictionary *scenario = [[UNIVERSE scenarios] objectAtIndex:item];
     294             :                 [self setShowDemoShips:NO];
     295             :                 for (NSUInteger i=GUI_ROW_SCENARIOS_DETAIL;i<=27;i++)
     296             :                 {
     297             :                         [gui setText:@"" forRow:i];
     298             :                 }
     299             :                 if (scenario)
     300             :                 {
     301             :                         [gui addLongText:OOExpand([scenario oo_stringForKey:@"description"]) startingAtRow:GUI_ROW_SCENARIOS_DETAIL align:GUI_ALIGN_LEFT];
     302             :                         NSString *shipKey = [scenario oo_stringForKey:@"model"];
     303             :                         if (shipKey != nil)
     304             :                         {
     305             :                                 [self addScenarioModel:shipKey];
     306             :                                 [self setShowDemoShips:YES];
     307             :                         }
     308             :                 }
     309             : 
     310             :         }
     311             : }
     312             : 
     313             : 
     314             : - (BOOL) startScenario
     315             : {
     316             :         GuiDisplayGen* gui = [UNIVERSE gui];
     317             :         NSString* key = [gui selectedRowKey];
     318             : 
     319             :         if ([key isEqualToString:@"exit"])
     320             :         {
     321             :                 // intended to return to main menu
     322             :                 return NO; 
     323             :         }
     324             :         if ([key hasPrefix:@"__page"])
     325             :         {
     326             :                 int page = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
     327             :                 [self setGuiToScenarioScreen:page];
     328             :                 return YES;
     329             :         }
     330             :         int selection = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
     331             : 
     332             :         NSDictionary *scenario = [[UNIVERSE scenarios] objectAtIndex:selection];
     333             :         NSString *file = [scenario oo_stringForKey:@"file" defaultValue:nil];
     334             :         if (file == nil) 
     335             :         {
     336             :                 OOLog(@"scenario.init.error", @"%@", @"No file entry found for scenario");
     337             :                 return NO;
     338             :         }
     339             :         NSString *path = [ResourceManager pathForFileNamed:file inFolder:@"Scenarios"];
     340             :         if (path == nil)
     341             :         {
     342             :                 OOLog(@"scenario.init.error", @"Game file not found for scenario %@",file);
     343             :                 return NO;
     344             :         }
     345             :         BOOL result = [self loadPlayerFromFile:path asNew:YES];
     346             :         if (!result)
     347             :         {
     348             :                 return NO;
     349             :         }
     350             :         [scenarioKey release];
     351             :         scenarioKey = [[scenario oo_stringForKey:@"scenario" defaultValue:nil] retain];
     352             : 
     353             :         // don't drop the save game directory in
     354             :         return YES;
     355             : }
     356             : 
     357             : 
     358             : 
     359             : 
     360             : #if OO_USE_CUSTOM_LOAD_SAVE
     361             : 
     362             : - (NSString *)commanderSelector
     363             : {
     364             :         MyOpenGLView    *gameView = [UNIVERSE gameView];
     365             :         GuiDisplayGen   *gui = [UNIVERSE gui];
     366             :         NSString                *dir = [[UNIVERSE gameController] playerFileDirectory];
     367             :         
     368             :         int idx;
     369             :         if([self handleGUIUpDownArrowKeys])
     370             :         {
     371             :                 int guiSelectedRow=[gui selectedRow];
     372             :                 idx=(guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
     373             :                 if (guiSelectedRow != MOREROW && guiSelectedRow != BACKROW && guiSelectedRow != EXITROW)
     374             :                 {
     375             :                         [self showCommanderShip: idx];
     376             :                 }
     377             :                 else
     378             :                 {
     379             :                         [UNIVERSE removeDemoShips];
     380             :                         [gui setText:@"" forRow:CDRDESCROW align:GUI_ALIGN_LEFT];
     381             :                         [gui setText:@"" forRow:CDRDESCROW + 1 align:GUI_ALIGN_LEFT];
     382             :                         [gui setText:@"" forRow:CDRDESCROW + 2 align:GUI_ALIGN_LEFT];
     383             :                 }
     384             : 
     385             :         }
     386             :         else
     387             :         {
     388             :                 idx=([gui selectedRow] - STARTROW) + (currentPage * NUMROWS);
     389             :         }
     390             :         
     391             :         // handle page <-- and page --> keys
     392             :         if (([self checkKeyPress:n_key_gui_arrow_left] || [self checkKeyPress:n_key_gui_page_up]) && [[gui keyForRow:BACKROW] isEqual: GUI_KEY_OK])
     393             :         {
     394             :                 currentPage--;
     395             :                 [self playMenuPagePrevious];
     396             :                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     397             :                 [gameView suppressKeysUntilKeyUp];
     398             :         }
     399             :         if (([self checkKeyPress:n_key_gui_arrow_right] || [self checkKeyPress:n_key_gui_page_down]) && [[gui keyForRow:MOREROW] isEqual: GUI_KEY_OK])
     400             :         {
     401             :                 currentPage++;
     402             :                 [self playMenuPageNext];
     403             :                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     404             :                 [gameView suppressKeysUntilKeyUp];
     405             :         }
     406             :         
     407             :         // Enter pressed - find the commander name underneath.
     408             :         // ignore Ctrl for the moment - we check for it explicitly later
     409             :         if ([self checkKeyPress:n_key_gui_select ignore_ctrl:YES]||[gameView isDown:gvMouseDoubleClick])
     410             :         {
     411             :                 NSDictionary *cdr;
     412             :                 switch ([gui selectedRow])
     413             :                 {
     414             :                         case EXITROW:
     415             :                                 if ([self status] == STATUS_START_GAME)
     416             :                                 {
     417             :                                         [self setGuiToIntroFirstGo:YES];
     418             :                                         return nil;
     419             :                                 }
     420             :                                 break;
     421             :                         case BACKROW:
     422             :                                 currentPage--;
     423             :                                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     424             :                                 [gameView suppressKeysUntilKeyUp];
     425             :                                 break;
     426             :                         case MOREROW:
     427             :                                 currentPage++;
     428             :                                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     429             :                                 [gameView suppressKeysUntilKeyUp];
     430             :                                 break;
     431             :                         default:
     432             :                                 cdr=[cdrDetailArray objectAtIndex: idx];
     433             :                                 if ([cdr oo_boolForKey:@"isSavedGame"])
     434             :                                         return [cdr oo_stringForKey:@"saved_game_path"];
     435             :                                 else
     436             :                                 {
     437             :                                         if ([gameView isCommandModifierKeyDown]||[gameView isDown:gvMouseDoubleClick])
     438             :                                         {
     439             :                                                 // change directory to the selected path
     440             :                                                 NSString* newDir = [cdr oo_stringForKey:@"saved_game_path"];
     441             :                                                 [[UNIVERSE gameController] setPlayerFileDirectory: newDir];
     442             :                                                 dir = newDir;
     443             :                                                 currentPage = 0;
     444             :                                                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     445             :                                                 [gameView suppressKeysUntilKeyUp];
     446             :                                         }
     447             :                                 }
     448             :                 }
     449             :         }
     450             :         
     451             :         if([gameView isDown: 27]) // escape key
     452             :         {
     453             :                 [self setGuiToStatusScreen];
     454             :         }
     455             :         return nil;
     456             : }
     457             : 
     458             : 
     459             : - (void) saveCommanderInputHandler
     460             : {
     461             :         MyOpenGLView    *gameView = [UNIVERSE gameView];
     462             :         GuiDisplayGen   *gui = [UNIVERSE gui];
     463             :         NSString                *dir = [[UNIVERSE gameController] playerFileDirectory];
     464             :         
     465             :         if ([self handleGUIUpDownArrowKeys])
     466             :         {
     467             :                 int guiSelectedRow=[gui selectedRow];
     468             :                 int     idx = (guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
     469             :                 if (guiSelectedRow != MOREROW && guiSelectedRow != BACKROW)
     470             :                 {
     471             :                         [self showCommanderShip: idx];
     472             :                         if ([(NSDictionary *)[cdrDetailArray objectAtIndex:idx] oo_boolForKey:@"isSavedGame"])        // don't show things that aren't saved games
     473             :                                 commanderNameString = [[cdrDetailArray oo_dictionaryAtIndex:idx] oo_stringForKey:@"player_save_name" defaultValue:[[cdrDetailArray oo_dictionaryAtIndex:idx] oo_stringForKey:@"player_name"]];
     474             :                         else
     475             :                                 commanderNameString = [gameView typedString];
     476             :                 }
     477             :                 else
     478             :                 {
     479             :                         [UNIVERSE removeDemoShips];
     480             :                         [gui setText:@"" forRow:CDRDESCROW align:GUI_ALIGN_LEFT];
     481             :                         [gui setText:@"" forRow:CDRDESCROW + 1 align:GUI_ALIGN_LEFT];
     482             :                         [gui setText:@"" forRow:CDRDESCROW + 2 align:GUI_ALIGN_LEFT];
     483             :                 }
     484             :         }
     485             :         else
     486             :         {
     487             :                 commanderNameString = [gameView typedString];
     488             :         }
     489             :         
     490             :         [gameView setTypedString: commanderNameString];
     491             :         
     492             :         [gui setText:
     493             :                 [NSString stringWithFormat:DESC(@"savescreen-commander-name-@"), commanderNameString]
     494             :                   forRow: INPUTROW];
     495             :         [gui setColor:[OOColor cyanColor] forRow:INPUTROW];
     496             :         
     497             :         // handle page <-- and page --> keys, and on-screen buttons
     498             :         if (((([gameView isDown:gvMouseDoubleClick] || [self checkKeyPress:n_key_gui_select]) && [gui selectedRow] == BACKROW) || ([self checkKeyPress:n_key_gui_arrow_left] || [self checkKeyPress:n_key_gui_page_up]))
     499             :                                         && [[gui keyForRow:BACKROW] isEqual: GUI_KEY_OK])
     500             :         {
     501             :                 currentPage--;
     502             :                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     503             :                 [gameView suppressKeysUntilKeyUp];
     504             :         }
     505             :         //
     506             :         if (((([gameView isDown:gvMouseDoubleClick] || [self checkKeyPress:n_key_gui_select]) && [gui selectedRow] == MOREROW) || ([self checkKeyPress:n_key_gui_arrow_right] || [self checkKeyPress:n_key_gui_page_down]))
     507             :                                         && [[gui keyForRow:MOREROW] isEqual: GUI_KEY_OK])
     508             :         {
     509             :                 currentPage++;
     510             :                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     511             :                 [gameView suppressKeysUntilKeyUp];
     512             :         }
     513             :         
     514             :         // ignore Ctrl if pressed together with Enter for the moment - we check for it explicitly immediately after
     515             :         if(([self checkKeyPress:n_key_gui_select ignore_ctrl:YES]||[gameView isDown:gvMouseDoubleClick]) && [commanderNameString length])
     516             :         {
     517             :                 if ([gameView isCommandModifierKeyDown]||[gameView isDown:gvMouseDoubleClick])
     518             :                 {
     519             :                         int guiSelectedRow=[gui selectedRow];
     520             :                         int     idx = (guiSelectedRow - STARTROW) + (currentPage * NUMROWS);
     521             :                         NSDictionary* cdr = [cdrDetailArray objectAtIndex:idx];
     522             :                         
     523             :                         if (![cdr oo_boolForKey:@"isSavedGame"])      // don't open saved games
     524             :                         {
     525             :                                 // change directory to the selected path
     526             :                                 NSString* newDir = [cdr oo_stringForKey:@"saved_game_path"];
     527             :                                 [[UNIVERSE gameController] setPlayerFileDirectory: newDir];
     528             :                                 dir = newDir;
     529             :                                 currentPage = 0;
     530             :                                 [self lsCommanders: gui directory: dir  pageNumber: currentPage  highlightName: nil];
     531             :                                 [gameView suppressKeysUntilKeyUp];
     532             :                         }
     533             :                 }
     534             :                 else
     535             :                 {
     536             :                         pollControls = YES;
     537             :                         if ([self existingNativeSave: commanderNameString])
     538             :                         {
     539             :                                 [gameView suppressKeysUntilKeyUp];
     540             :                                 [self setGuiToOverwriteScreen: commanderNameString];
     541             :                         }
     542             :                         else
     543             :                         {
     544             :                                 [self nativeSavePlayer: commanderNameString];
     545             :                                 [[UNIVERSE gameView] suppressKeysUntilKeyUp];
     546             :                                 [self setGuiToStatusScreen];
     547             :                         }
     548             :                 }
     549             :         }
     550             :         
     551             :         if([gameView isDown: 27]) // escape key
     552             :         {
     553             :                 // get out of here
     554             :                 pollControls = YES;
     555             :                 [[UNIVERSE gameView] resetTypedString];
     556             :                 [self setGuiToStatusScreen];
     557             :         }
     558             : }
     559             : 
     560             : 
     561             : - (void) overwriteCommanderInputHandler
     562             : {
     563             :         MyOpenGLView    *gameView = [UNIVERSE gameView];
     564             :         GuiDisplayGen   *gui = [UNIVERSE gui];
     565             :         
     566             :         [self handleGUIUpDownArrowKeys];
     567             :         
     568             :         // Translation issue: we can't confidently use raw Y and N ascii as shortcuts. It's better to use the load-previous-commander keys.
     569             :         id valueYes = [[[UNIVERSE descriptions] oo_stringForKey:@"load-previous-commander-yes" defaultValue:@"y"] lowercaseString];
     570             :         id valueNo = [[[UNIVERSE descriptions] oo_stringForKey:@"load-previous-commander-no" defaultValue:@"n"] lowercaseString];
     571             :         unsigned char cYes, cNo;
     572             :         
     573             :         cYes = [valueYes characterAtIndex: 0] & 0x00ff;     // Use lower byte of unichar.
     574             :         cNo = [valueNo characterAtIndex: 0] & 0x00ff;       // Use lower byte of unichar.
     575             :         
     576             :         if (([self checkKeyPress:n_key_gui_select] && ([gui selectedRow] == SAVE_OVERWRITE_YES_ROW))||[gameView isDown:cYes]||[gameView isDown:cYes - 32])
     577             :         {
     578             :                 pollControls=YES;
     579             :                 [self nativeSavePlayer: commanderNameString];
     580             :                 [self playSaveOverwriteYes];
     581             :                 [[UNIVERSE gameView] suppressKeysUntilKeyUp];
     582             :                 [self setGuiToStatusScreen];
     583             :         }
     584             :         
     585             :         if (([self checkKeyPress:n_key_gui_select] && ([gui selectedRow] == SAVE_OVERWRITE_NO_ROW))||[gameView isDown:27]||[gameView isDown:cNo]||[gameView isDown:cNo - 32])
     586             :         {
     587             :                 // esc or NO was pressed - get out of here
     588             :                 pollControls=YES;
     589             :                 [self playSaveOverwriteNo];
     590             :                 [self setGuiToSaveCommanderScreen:@""];
     591             :         }
     592             : }
     593             : 
     594             : #endif
     595             : 
     596             : 
     597             : - (BOOL) loadPlayerFromFile:(NSString *)fileToOpen asNew:(BOOL)asNew
     598             : {
     599             :         /*      TODO: it would probably be better to load by creating a new
     600             :                 PlayerEntity, verifying that's OK, then replacing the global player.
     601             :                 
     602             :                 Actually, it'd be better to separate PlayerEntity into OOPlayer and
     603             :                 OOPlayerShipEntity. And then move most of OOPlayerShipEntity into
     604             :                 ShipEntity, and make NPC ships behave more like player ships.
     605             :                 -- Ahruman
     606             :         */
     607             :         
     608             :         BOOL                    loadedOK = YES;
     609             :         NSDictionary    *fileDic = nil;
     610             :         NSString                *fail_reason = nil;
     611             :         
     612             :         if (fileToOpen == nil)
     613             :         {
     614             :                 fail_reason = DESC(@"loadfailed-no-file-specified");
     615             :                 loadedOK = NO;
     616             :         }
     617             :         
     618             :         if (loadedOK)
     619             :         {
     620             :                 OOLog(@"load.progress", @"%@", @"Reading file");
     621             :                 fileDic = OODictionaryFromFile(fileToOpen);
     622             :                 if (fileDic == nil)
     623             :                 {
     624             :                         fail_reason = DESC(@"loadfailed-could-not-load-file");
     625             :                         loadedOK = NO;
     626             :                 }
     627             :         }
     628             : 
     629             :         if (loadedOK)
     630             :         {
     631             :                 OOLog(@"load.progress", @"%@", @"Restricting scenario");
     632             :                 NSString *scenarioRestrict = [fileDic oo_stringForKey:@"scenario_restriction" defaultValue:nil];
     633             :                 if (scenarioRestrict == nil)
     634             :                 {
     635             :                         // older save game - use the 'strict' key instead
     636             :                         BOOL strict = [fileDic oo_boolForKey:@"strict" defaultValue:NO];
     637             :                         if (strict)
     638             :                         {
     639             :                                 scenarioRestrict = SCENARIO_OXP_DEFINITION_NONE;
     640             :                         }
     641             :                         else
     642             :                         {
     643             :                                 scenarioRestrict = SCENARIO_OXP_DEFINITION_ALL;
     644             :                         }
     645             :                 }
     646             : 
     647             :                 if (![UNIVERSE setUseAddOns:scenarioRestrict fromSaveGame:YES forceReinit:YES]) 
     648             :                 {
     649             :                         fail_reason = DESC(@"loadfailed-saved-game-failed-to-load");
     650             :                         loadedOK = NO;
     651             :                 } 
     652             :         }
     653             :         
     654             : 
     655             :         if (loadedOK)
     656             :         {
     657             :                 OOLog(@"load.progress", @"%@", @"Creating player ship");
     658             :                 // Check that player ship exists
     659             :                 NSString                *shipKey = nil;
     660             :                 NSDictionary    *shipDict = nil;
     661             :                 
     662             :                 shipKey = [fileDic oo_stringForKey:@"ship_desc"];
     663             :                 shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
     664             :                 
     665             :                 if (shipDict == nil)
     666             :                 {
     667             :                         loadedOK = NO;
     668             :                         if (shipKey != nil)  fail_reason = [NSString stringWithFormat:DESC(@"loadfailed-could-not-find-ship-type-@-please-reinstall-the-appropriate-OXP"), shipKey];
     669             :                         else  fail_reason = DESC(@"loadfailed-invalid-saved-game-no-ship-specified");
     670             :                 }
     671             :         }
     672             :         
     673             :         if (loadedOK)
     674             :         {
     675             :                 OOLog(@"load.progress", @"%@", @"Initialising player entity");
     676             :                 if (![self setUpAndConfirmOK:YES saveGame:YES])
     677             :                 {
     678             :                         fail_reason = DESC(@"loadfailed-could-not-reset-javascript");
     679             :                         loadedOK = NO;
     680             :                 }
     681             :         }
     682             :         
     683             :         if (loadedOK)
     684             :         {
     685             :                 OOLog(@"load.progress", @"%@", @"Loading commander data");
     686             :                 if (![self setCommanderDataFromDictionary:fileDic])
     687             :                 {
     688             :                         // this could still be a reset js issue, if switching from strict / unrestricted
     689             :                         // TODO: use "could not reset js message" if that's the case.
     690             :                         fail_reason = DESC(@"loadfailed-could-not-set-up-player-ship");
     691             :                         loadedOK = NO;
     692             :                 }
     693             :         }
     694             :         
     695             :         if (loadedOK)
     696             :         {
     697             :                 OOLog(@"load.progress", @"%@", @"Recording save path");
     698             :                 if (!asNew)
     699             :                 {
     700             :                         [save_path autorelease];
     701             :                         save_path = [fileToOpen retain];
     702             :                 
     703             :                         [[[UNIVERSE gameView] gameController] setPlayerFileToLoad:fileToOpen];
     704             :                         [[[UNIVERSE gameView] gameController] setPlayerFileDirectory:fileToOpen];
     705             :                 }
     706             :         }
     707             :         else
     708             :         {
     709             :                 OOLog(@"load.failed", @"***** Failed to load saved game \"%@\": %@", [fileToOpen lastPathComponent], fail_reason ? fail_reason : (NSString *)@"unknown error");
     710             :                 [[UNIVERSE gameController] setPlayerFileToLoad:nil];
     711             :                 [UNIVERSE handleGameOver];
     712             :                 [UNIVERSE clearPreviousMessage];
     713             :                 [UNIVERSE addMessage:DESC(@"loadfailed-saved-game-failed-to-load") forCount: 9.0];
     714             :                 if (fail_reason != nil)  [UNIVERSE addMessage: fail_reason forCount: 9.0];
     715             :                 return NO;
     716             :         }
     717             :         
     718             :         OOLog(@"load.progress", @"%@", @"Creating system");
     719             :         [UNIVERSE setTimeAccelerationFactor:TIME_ACCELERATION_FACTOR_DEFAULT];
     720             :         [UNIVERSE setSystemTo:system_id];
     721             :         [UNIVERSE removeAllEntitiesExceptPlayer];
     722             :         [UNIVERSE setGalaxyTo: galaxy_number andReinit:YES]; // set overridden planet names on long range map
     723             :         [UNIVERSE setUpSpace];
     724             :         [UNIVERSE setAutoSaveNow:NO];
     725             :         
     726             :         OOLog(@"load.progress", @"%@", @"Resetting player flight variables");
     727             :         [self setDockedAtMainStation];
     728             :         StationEntity *dockedStation = [self dockedStation];
     729             :         
     730             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:NO];
     731             :         
     732             :         if (dockedStation)
     733             :         {
     734             :                 position = [dockedStation position];
     735             :                 [self setOrientation: kIdentityQuaternion];
     736             :                 v_forward = vector_forward_from_quaternion(orientation);
     737             :                 v_right = vector_right_from_quaternion(orientation);
     738             :                 v_up = vector_up_from_quaternion(orientation);
     739             :         }
     740             :         
     741             :         flightRoll = 0.0;
     742             :         flightPitch = 0.0;
     743             :         flightYaw = 0.0;
     744             :         flightSpeed = 0.0;
     745             :         
     746             :         [self setEntityPersonalityInt:PersonalityForCommanderDict(fileDic)];
     747             :         
     748             :         OOLog(@"load.progress", @"%@", @"Loading system market");
     749             :         // dockedStation is always the main station at this point;
     750             :         // "localMarket" save key always refers to the main station (system) market
     751             :         NSArray *market = [fileDic oo_arrayForKey:@"localMarket"];
     752             :         if (market != nil) 
     753             :         {
     754             :                 [dockedStation setLocalMarket:market];
     755             :         }
     756             :         else
     757             :         {
     758             :                 [dockedStation initialiseLocalMarket];
     759             :         }
     760             : 
     761             :         [self calculateCurrentCargo];
     762             :         
     763             :         OOLog(@"load.progress", @"%@", @"Setting scenario key");
     764             :         // set scenario key if the scenario allows saving and has one
     765             :         NSString *scenario = [fileDic oo_stringForKey:@"scenario_key" defaultValue:nil];
     766             :         DESTROY(scenarioKey);
     767             :         if (scenario != nil)
     768             :         {
     769             :                 scenarioKey = [scenario retain];
     770             :         }
     771             : 
     772             :         OOLog(@"load.progress", @"%@", @"Starting JS engine");
     773             :         // Remember the savegame target, run js startUp.
     774             :         [self completeSetUpAndSetTarget:NO];
     775             :         // run initial system population
     776             :         OOLog(@"load.progress", @"%@", @"Populating initial system");
     777             :         [UNIVERSE populateNormalSpace];
     778             : 
     779             :         // might as well start off with a collected JS environment
     780             :         [[OOJavaScriptEngine sharedEngine] garbageCollectionOpportunity:YES];
     781             :         
     782             :         // read saved position vector and primary role, check for an
     783             :         // appropriate station at those coordinates, if found, switch
     784             :         // docked station to that one.
     785             :         HPVector dockedPos = [fileDic oo_hpvectorForKey:@"docked_station_position"];
     786             :         NSString *dockedRole = [fileDic oo_stringForKey:@"docked_station_role" defaultValue:@""];
     787             :         StationEntity *saveStation = [UNIVERSE stationWithRole:dockedRole andPosition:dockedPos];
     788             :         if (saveStation != nil && [saveStation allowsSaving])
     789             :         {
     790             :                 [self setDockedStation:saveStation];
     791             :                 position = [saveStation position];
     792             :         }
     793             :         // and initialise markets for the secondary stations
     794             :         [UNIVERSE loadStationMarkets:[fileDic oo_arrayForKey:@"station_markets"]];
     795             : 
     796             :         OOLog(@"load.progress", @"%@", @"Completing JS startup");
     797             :         [self startUpComplete];
     798             : 
     799             :         [[UNIVERSE gameView] suppressKeysUntilKeyUp];
     800             :         gui_screen = GUI_SCREEN_LOAD; // force evaluation of new gui screen on startup
     801             :         [self setGuiToStatusScreen];
     802             :         if (loadedOK) [self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")];  // trigger missionScreenOpportunity immediately after loading
     803             :         OOLog(@"load.progress", @"%@", @"Loading complete");
     804             :         return loadedOK;
     805             : }
     806             : 
     807             : @end
     808             : 
     809             : 
     810             : @implementation PlayerEntity (OOLoadSavePrivate)
     811             : 
     812             : #if OOLITE_USE_APPKIT_LOAD_SAVE
     813             : 
     814             : - (BOOL)loadPlayerWithPanel
     815             : {
     816             :         NSOpenPanel *oPanel = [NSOpenPanel openPanel];
     817             :         
     818             :         oPanel.allowsMultipleSelection = NO;
     819             :         oPanel.allowedFileTypes = [NSArray arrayWithObject:@"oolite-save"];
     820             :         
     821             :         if ([oPanel runModal] == NSOKButton)
     822             :         {
     823             :                 NSURL *url = oPanel.URL;
     824             :                 if (url.isFileURL)
     825             :                 {
     826             :                         return [self loadPlayerFromFile:url.path asNew:NO];
     827             :                 }
     828             :         }
     829             :         
     830             :         return NO;
     831             : }
     832             : 
     833             : 
     834             : - (void) savePlayerWithPanel
     835             : {
     836             :         NSSavePanel *sPanel = [NSSavePanel savePanel];
     837             :         
     838             :         sPanel.allowedFileTypes = [NSArray arrayWithObject:@"oolite-save"];
     839             :         sPanel.canSelectHiddenExtension = YES;
     840             :         sPanel.nameFieldStringValue = self.lastsaveName;
     841             :         
     842             :         if ([sPanel runModal] == NSOKButton)
     843             :         {
     844             :                 NSURL *url = sPanel.URL;
     845             :                 NSAssert(url.isFileURL, @"Save panel with default configuration should not provide non-file URLs.");
     846             :                 
     847             :                 NSString *path = url.path;
     848             :                 NSString *newName = [path.lastPathComponent stringByDeletingPathExtension];
     849             :                 
     850             :                 ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("STANDARD_SAVE"));
     851             :                 
     852             :                 self.lastsaveName = newName;
     853             :                 [self writePlayerToPath:path];
     854             :         }
     855             :         [self setGuiToStatusScreen];
     856             : }
     857             : 
     858             : #endif
     859             : 
     860             : 
     861             : - (void) writePlayerToPath:(NSString *)path
     862             : {
     863             :         NSString                *errDesc = nil;
     864             :         NSDictionary    *dict = nil;
     865             :         BOOL                    didSave = NO;
     866             :         [[UNIVERSE gameView] resetTypedString];
     867             :         
     868             :         if (!path)
     869             :         {
     870             :                 OOLog(@"save.failed", @"***** SAVE ERROR: %s called with nil path.", __PRETTY_FUNCTION__);
     871             :                 return;
     872             :         }
     873             :         
     874             :         dict = [self commanderDataDictionary];
     875             :         if (dict == nil)  errDesc = @"could not construct commander data dictionary.";
     876             :         else  didSave = [dict writeOOXMLToFile:path atomically:YES errorDescription:&errDesc];
     877             :         if (didSave)
     878             :         {
     879             :                 [UNIVERSE clearPreviousMessage];        // allow this to be given time and again
     880             :                 [UNIVERSE addMessage:DESC(@"game-saved") forCount:2];
     881             :                 [save_path autorelease];
     882             :                 save_path = [path copy];
     883             :                 [[UNIVERSE gameController] setPlayerFileToLoad:save_path];
     884             :                 [[UNIVERSE gameController] setPlayerFileDirectory:save_path];
     885             :                 // no duplicated autosave immediately after a save.
     886             :                 [UNIVERSE setAutoSaveNow:NO];
     887             :         }
     888             :         else
     889             :         {
     890             :                 OOLog(@"save.failed", @"***** SAVE ERROR: %@", errDesc);
     891             :                 [NSException raise:@"OoliteException"
     892             :                                         format:@"Attempt to save game to file '%@' failed: %@", path, errDesc];
     893             :         }
     894             :         [[UNIVERSE gameView] suppressKeysUntilKeyUp];
     895             :         [self setGuiToStatusScreen];
     896             : }
     897             : 
     898             : 
     899             : - (void)nativeSavePlayer:(NSString *)cdrName
     900             : {
     901             :         NSString*       dir = [[UNIVERSE gameController] playerFileDirectory];
     902             :         NSString *savePath = [dir stringByAppendingPathComponent:[cdrName stringByAppendingPathExtension:@"oolite-save"]];
     903             :         
     904             :         ShipScriptEventNoCx(self, "playerWillSaveGame", OOJSSTR("STANDARD_SAVE"));
     905             :         
     906             :         [self setLastsaveName:cdrName];
     907             :         
     908             :         [self writePlayerToPath:savePath];
     909             : }
     910             : 
     911             : 
     912             : #if OO_USE_CUSTOM_LOAD_SAVE
     913             : 
     914             : - (void) setGuiToLoadCommanderScreen
     915             : {
     916             :         GuiDisplayGen *gui=[UNIVERSE gui];
     917             :         NSString*       dir = [[UNIVERSE gameController] playerFileDirectory];
     918             :         
     919             :         gui_screen = GUI_SCREEN_LOAD;
     920             :         
     921             :         [gui clear];
     922             :         [gui setTitle:DESC(@"loadscreen-title")];
     923             :         
     924             :         currentPage = 0;
     925             :         [self lsCommanders:gui directory:dir pageNumber: currentPage highlightName:nil];
     926             :         
     927             :         [gui setForegroundTextureKey:@"docked_overlay"];
     928             :         [gui setBackgroundTextureKey:@"load_save"];
     929             :         
     930             :         [[UNIVERSE gameView] suppressKeysUntilKeyUp];
     931             :         
     932             :         [self setShowDemoShips:YES];
     933             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
     934             : }
     935             : 
     936             : 
     937             : - (void) setGuiToSaveCommanderScreen:(NSString *)cdrName
     938             : {
     939             :         GuiDisplayGen *gui=[UNIVERSE gui];
     940             :         MyOpenGLView *gameView = [UNIVERSE gameView];
     941             :         NSString *dir = [[UNIVERSE gameController] playerFileDirectory];
     942             :         
     943             :         pollControls = NO;
     944             :         gui_screen = GUI_SCREEN_SAVE;
     945             :         
     946             :         [gui clear];
     947             :         [gui setTitle:DESC(@"savescreen-title")];
     948             :         
     949             :         currentPage = 0;
     950             :         [self lsCommanders:gui directory:dir pageNumber: currentPage highlightName:nil];
     951             :         
     952             :         [gui setText:DESC(@"savescreen-commander-name") forRow: INPUTROW];
     953             :         [gui setColor:[OOColor cyanColor] forRow:INPUTROW];
     954             :         [gui setShowTextCursor: YES];
     955             :         [gui setCurrentRow: INPUTROW];
     956             :         
     957             :         [gui setForegroundTextureKey:@"docked_overlay"];
     958             :         [gui setBackgroundTextureKey:@"load_save"];
     959             :         
     960             :         [gameView setTypedString:cdrName];
     961             :         [gameView suppressKeysUntilKeyUp];
     962             :         
     963             :         [self setShowDemoShips:YES];
     964             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
     965             : }
     966             : 
     967             : 
     968             : - (void) setGuiToOverwriteScreen:(NSString *)cdrName
     969             : {
     970             :         GuiDisplayGen *gui=[UNIVERSE gui];
     971             :         MyOpenGLView*   gameView = [UNIVERSE gameView];
     972             :         
     973             :         // Don't poll controls
     974             :         pollControls=NO;
     975             :         
     976             :         gui_screen = GUI_SCREEN_SAVE_OVERWRITE;
     977             :         
     978             :         [gui clear];
     979             :         [gui setTitle:[NSString stringWithFormat:DESC(@"overwrite-save-commander-@"), cdrName]];
     980             :         
     981             :         [gui setText:[NSString stringWithFormat:DESC(@"overwritescreen-commander-@-already-exists-overwrite-query"), cdrName]
     982             :                                                                 forRow:SAVE_OVERWRITE_WARN_ROW align: GUI_ALIGN_CENTER];
     983             :         
     984             :         [gui setText:DESC(@"overwritescreen-yes") forRow: SAVE_OVERWRITE_YES_ROW align: GUI_ALIGN_CENTER];
     985             :         [gui setKey:GUI_KEY_OK forRow: SAVE_OVERWRITE_YES_ROW];
     986             :         
     987             :         [gui setText:DESC(@"overwritescreen-no") forRow: SAVE_OVERWRITE_NO_ROW align: GUI_ALIGN_CENTER];
     988             :         [gui setKey:GUI_KEY_OK forRow: SAVE_OVERWRITE_NO_ROW];
     989             :         
     990             :         [gui setSelectableRange: NSMakeRange(SAVE_OVERWRITE_YES_ROW, 2)];
     991             :         [gui setSelectedRow: SAVE_OVERWRITE_NO_ROW];
     992             :         
     993             :         // We can only leave this screen by answering yes or no, or esc. Therefore
     994             :         // use a specific overlay, to allow visual reminders of the available options.
     995             :         [gui setForegroundTextureKey:@"overwrite_overlay"];
     996             :         [gui setBackgroundTextureKey:@"load_save"];
     997             :         
     998             :         [self setShowDemoShips:NO];
     999             :         [gameView setStringInput:gvStringInputNo];
    1000             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:NO];     // FIXME: should be YES, but was NO before introducing new mouse mode stuff. If set to YES, choices can be selected but not activated.
    1001             : }
    1002             : 
    1003           0 : NSComparisonResult sortCommanders(id cdr1, id cdr2, void *context)
    1004             : {
    1005             :         return [[cdr1 objectForKey:@"saved_game_path"] localizedCompare:[cdr2 objectForKey:@"saved_game_path"]];
    1006             : }
    1007             : 
    1008             : - (void) lsCommanders: (GuiDisplayGen *)gui
    1009             :                         directory: (NSString*) directory
    1010             :                    pageNumber: (int)page
    1011             :                 highlightName: (NSString *)highlightName
    1012             : {
    1013             :         NSFileManager *cdrFileManager=[NSFileManager defaultManager];
    1014             :         int rangeStart=STARTROW;
    1015             :         unsigned lastIndex;
    1016             :         unsigned i;
    1017             :         int row=STARTROW;
    1018             :         
    1019             :         // cdrArray defined in PlayerEntity.h
    1020             :         NSArray *cdrArray=[cdrFileManager commanderContentsOfPath: directory];
    1021             :         
    1022             :         // get commander details so a brief rundown of the commander's details may
    1023             :         // be displayed.
    1024             :         if (!cdrDetailArray)
    1025             :                 cdrDetailArray=[[NSMutableArray alloc] init];   // alloc retains this so the retain further on in the code was unnecessary
    1026             :         else
    1027             :                 [cdrDetailArray removeAllObjects];
    1028             :         
    1029             :         [cdrDetailArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
    1030             :                 @"YES", @"isParentFolder",
    1031             :                 [directory stringByDeletingLastPathComponent], @"saved_game_path", nil]];
    1032             :         
    1033             :         for(i = 0; i < [cdrArray count]; i++)
    1034             :         {
    1035             :                 NSString*       path = [cdrArray objectAtIndex:i];
    1036             :                 BOOL            exists, isDirectory = NO;
    1037             :                 
    1038             :                 exists = [cdrFileManager fileExistsAtPath:path isDirectory:&isDirectory];
    1039             :                 
    1040             :                 if (exists)
    1041             :                 {
    1042             :                         if (!isDirectory && [[[path pathExtension] lowercaseString] isEqualToString:@"oolite-save"])
    1043             :                         {
    1044             :                                 NSDictionary *cdr = OODictionaryFromFile(path);
    1045             :                                 if(cdr)
    1046             :                                 {
    1047             :                                         // okay use the same dictionary but add a 'saved_game_path' attribute
    1048             :                                         NSMutableDictionary* cdr1 = [NSMutableDictionary dictionaryWithDictionary:cdr];
    1049             :                                         [cdr1 setObject: @"YES" forKey:@"isSavedGame"];
    1050             :                                         [cdr1 setObject: path forKey:@"saved_game_path"];
    1051             :                                         [cdrDetailArray addObject: cdr1];
    1052             :                                 }
    1053             :                         }
    1054             :                         if (isDirectory && ![[path lastPathComponent] hasPrefix:@"."])
    1055             :                         {
    1056             :                                 [cdrDetailArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: @"YES", @"isFolder", path, @"saved_game_path", nil]];
    1057             :                         }
    1058             :                 }
    1059             :         }
    1060             :         
    1061             :         if(![cdrDetailArray count])
    1062             :         {
    1063             :                 // Empty directory; tell the user and exit immediately.
    1064             :                 [gui setText:DESC(@"loadsavescreen-no-commanders-found") forRow:STARTROW align:GUI_ALIGN_CENTER];
    1065             :                 return;
    1066             :         }
    1067             : 
    1068             :         [cdrDetailArray sortUsingFunction:sortCommanders context:NULL];
    1069             :         
    1070             :         // Do we need to highlight a name?
    1071             :         int highlightRowOnPage=STARTROW;
    1072             :         int highlightIdx=0;
    1073             :         if(highlightName)
    1074             :         {
    1075             :                 highlightIdx=[self findIndexOfCommander: highlightName];
    1076             :                 if(highlightIdx < 0)
    1077             :                 {
    1078             :                         OOLog(@"save.list.commanders.commanderNotFound", @"Commander %@ doesn't exist, very bad", highlightName);
    1079             :                         highlightIdx=0;
    1080             :                 }
    1081             :                 
    1082             :                 // figure out what page we need to be on
    1083             :                 page=highlightIdx/NUMROWS;
    1084             :                 highlightRowOnPage=highlightIdx % NUMROWS + STARTROW;
    1085             :         }
    1086             :         
    1087             :         // We now know for certain what page we're on - 
    1088             :         // set the first index of the first commander on this page.
    1089             :         unsigned firstIndex=page * NUMROWS;
    1090             :         
    1091             :         // Set up the GUI.
    1092             :         OOGUITabSettings tabStop;
    1093             :         tabStop[0]=0;
    1094             :         tabStop[1]=160;
    1095             :         tabStop[2]=270;
    1096             :         [gui setTabStops: tabStop];
    1097             :         
    1098             :         // clear text lines here
    1099             :         for (i = EXITROW ; i < ENDROW + 1; i++)
    1100             :         {
    1101             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    1102             :                 [gui setColor: [OOColor yellowColor] forRow: i];
    1103             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    1104             :         }
    1105             : 
    1106             :         [gui setColor: [OOColor greenColor] forRow: LABELROW];
    1107             :         [gui setArray: [NSArray arrayWithObjects: DESC(@"loadsavescreen-commander-name"), DESC(@"loadsavescreen-rating"), nil]
    1108             :                    forRow:LABELROW];
    1109             : 
    1110             :         if (page)
    1111             :         {
    1112             :                 [gui setColor:[OOColor greenColor] forRow:STARTROW-1];
    1113             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @" <-- ", nil]
    1114             :                            forRow:STARTROW-1];
    1115             :                 [gui setKey:GUI_KEY_OK forRow:STARTROW-1];
    1116             :                 rangeStart=STARTROW-1;
    1117             :         }
    1118             : 
    1119             :         if ([self status] == STATUS_START_GAME)
    1120             :         {
    1121             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-loadsave-exit"), @" <----- ", nil] forRow:EXITROW];
    1122             :                 [gui setColor:[OOColor redColor] forRow:EXITROW];
    1123             :                 [gui setKey:GUI_KEY_OK forRow:EXITROW];
    1124             :                 rangeStart = EXITROW;
    1125             :         }
    1126             : 
    1127             :         
    1128             :         if (firstIndex + NUMROWS >= [cdrDetailArray count])
    1129             :         {
    1130             :                 lastIndex=[cdrDetailArray count];
    1131             :                 [gui setSelectableRange: NSMakeRange(rangeStart, rangeStart + NUMROWS + 2)];
    1132             :         }
    1133             :         else
    1134             :         {
    1135             :                 lastIndex=(page * NUMROWS) + NUMROWS;
    1136             :                 [gui setColor:[OOColor greenColor] forRow:ENDROW];
    1137             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @" --> ", nil]
    1138             :                            forRow:ENDROW];
    1139             :                 [gui setKey:GUI_KEY_OK forRow:ENDROW];
    1140             :                 [gui setSelectableRange: NSMakeRange(rangeStart, MOREROW)];
    1141             :         }
    1142             :         
    1143             :         for (i=firstIndex; i < lastIndex; i++)
    1144             :         {
    1145             :                 NSDictionary *cdr=[cdrDetailArray objectAtIndex: i];
    1146             :                 if ([cdr oo_boolForKey:@"isSavedGame"])
    1147             :                 {
    1148             :                         NSString *ratingDesc = OODisplayRatingStringFromKillCount([cdr oo_unsignedIntForKey:@"ship_kills"]);
    1149             :                         [gui setArray:[NSArray arrayWithObjects:
    1150             :                                 [NSString stringWithFormat:@" %@ ",[cdr oo_stringForKey:@"player_save_name" defaultValue:[cdr oo_stringForKey:@"player_name"]]],
    1151             :                                 [NSString stringWithFormat:@" %@ ",ratingDesc],
    1152             :                                 nil]
    1153             :                                    forRow:row];
    1154             :                         if ([[self lastsaveName] isEqualToString:[cdr oo_stringForKey:@"player_save_name" defaultValue:[cdr oo_stringForKey:@"player_name"]]])
    1155             :                         {
    1156             :                                 highlightRowOnPage = row;
    1157             :                         }
    1158             :                         
    1159             :                         [gui setKey:GUI_KEY_OK forRow:row];
    1160             :                         row++;
    1161             :                 }
    1162             :                 if ([cdr oo_boolForKey:@"isParentFolder"])
    1163             :                 {
    1164             :                         [gui setArray:[NSArray arrayWithObjects:
    1165             :                                 [NSString stringWithFormat:@" (..) %@ ", [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]],
    1166             :                                 @"",
    1167             :                                 nil]
    1168             :                                    forRow:row];
    1169             :                         [gui setColor: [OOColor orangeColor] forRow: row];
    1170             :                         [gui setKey:GUI_KEY_OK forRow:row];
    1171             :                         row++;
    1172             :                 }
    1173             :                 if ([cdr oo_boolForKey:@"isFolder"])
    1174             :                 {
    1175             :                         [gui setArray:[NSArray arrayWithObjects:
    1176             :                                 [NSString stringWithFormat:@" >> %@ ", [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]],
    1177             :                                 @"",
    1178             :                                 nil]
    1179             :                                    forRow:row];
    1180             :                         [gui setColor: [OOColor orangeColor] forRow: row];
    1181             :                         [gui setKey:GUI_KEY_OK forRow:row];
    1182             :                         row++;
    1183             :                 }
    1184             :         }
    1185             :         [gui setSelectedRow: highlightRowOnPage];
    1186             :         highlightIdx = (highlightRowOnPage - STARTROW) + (currentPage * NUMROWS);
    1187             :         // show the first ship, this will be the selected row
    1188             :         [self showCommanderShip: highlightIdx];
    1189             : }
    1190             : 
    1191             : 
    1192             : // check for an existing saved game...
    1193             : - (BOOL) existingNativeSave: (NSString *)cdrName
    1194             : {
    1195             :         NSString*       dir = [[UNIVERSE gameController] playerFileDirectory];
    1196             :         
    1197             :         NSString *savePath=[dir stringByAppendingPathComponent:[cdrName stringByAppendingPathExtension:@"oolite-save"]];
    1198             :         return [[NSFileManager defaultManager] fileExistsAtPath:savePath];
    1199             : }
    1200             : 
    1201             : 
    1202             : // Get some brief details about the commander file.
    1203             : - (void) showCommanderShip:(int)cdrArrayIndex
    1204             : {
    1205             :         GuiDisplayGen *gui=[UNIVERSE gui];
    1206             :         [UNIVERSE removeDemoShips];
    1207             :         NSDictionary *cdr=[cdrDetailArray objectAtIndex: cdrArrayIndex];
    1208             :         
    1209             :         [gui setText:@"" forRow:CDRDESCROW align:GUI_ALIGN_LEFT];
    1210             :         [gui setText:@"" forRow:CDRDESCROW + 1 align:GUI_ALIGN_LEFT];
    1211             :         [gui setText:@"" forRow:CDRDESCROW + 2 align:GUI_ALIGN_LEFT];
    1212             :         
    1213             :         if ([cdr oo_boolForKey:@"isFolder"])
    1214             :         {
    1215             :                 NSString *folderDesc=[NSString stringWithFormat: DESC(@"loadsavescreen-hold-@-and-press-return-to-open-folder-@"), @COMMAND_MODIFIER_KEY, [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]];
    1216             :                 [gui setColor: [OOColor orangeColor] forRow: CDRDESCROW];
    1217             :                 [gui addLongText: folderDesc startingAtRow: CDRDESCROW align: GUI_ALIGN_LEFT];
    1218             :                 return;
    1219             :         }
    1220             :         
    1221             :         if ([cdr oo_boolForKey:@"isParentFolder"])
    1222             :         {
    1223             :                 NSString *folderDesc=[NSString stringWithFormat: DESC(@"loadsavescreen-hold-@-and-press-return-to-open-parent-folder-@"), @COMMAND_MODIFIER_KEY, [[cdr oo_stringForKey:@"saved_game_path"] lastPathComponent]];
    1224             :                 [gui setColor: [OOColor orangeColor] forRow: CDRDESCROW];
    1225             :                 [gui addLongText: folderDesc startingAtRow: CDRDESCROW align: GUI_ALIGN_LEFT];
    1226             :                 return;
    1227             :         }
    1228             :         [gui setColor:[gui colorFromSetting:nil defaultValue:nil] forRow: CDRDESCROW];
    1229             : 
    1230             :         if (![cdr oo_boolForKey:@"isSavedGame"])  return;     // don't show things that aren't saved games
    1231             :         
    1232             :         if ([self dockedStation] == nil)  [self setDockedAtMainStation];
    1233             :         
    1234             :         // Display the commander's ship.
    1235             :         NSString                        *shipDesc = [cdr oo_stringForKey:@"ship_desc"];
    1236             :         NSString                        *shipName = nil;
    1237             :         NSDictionary            *shipDict = nil;
    1238             :         NSString                        *rating = nil;
    1239             :         uint16_t                        personality = PersonalityForCommanderDict(cdr);
    1240             :         
    1241             :         shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipDesc];
    1242             :         if(shipDict != nil)
    1243             :         {
    1244             :                 NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:[shipDict count] + 1];
    1245             :                 [dict setDictionary:shipDict];
    1246             :                 id subEntStatus = [cdr objectForKey:@"subentities_status"];
    1247             :                 // don't add it to the dictionary if there's no subentities_status key
    1248             :                 if (subEntStatus != nil) [dict setObject:subEntStatus forKey:@"subentities_status"];
    1249             :                 [self showShipyardModel:shipDesc shipData:dict personality:personality];
    1250             :                 [dict release];
    1251             :                 shipName = [shipDict oo_stringForKey:@"display_name"];
    1252             :                 if (shipName == nil) shipName = [shipDict oo_stringForKey:KEY_NAME];
    1253             :         }
    1254             :         else
    1255             :         {
    1256             :                 [self showShipyardModel:@"oolite-unknown-ship" shipData:nil personality:personality];
    1257             :                 shipName = [cdr oo_stringForKey:@"ship_name" defaultValue:@"unknown"];
    1258             :                 if (![[UNIVERSE useAddOns] isEqualToString:SCENARIO_OXP_DEFINITION_ALL])
    1259             :                 {
    1260             :                         shipName = [shipName stringByAppendingString:@" - OXPs disabled or not installed"];
    1261             :                 }
    1262             :                 else
    1263             :                 {
    1264             :                         shipName = [shipName stringByAppendingString:@" - OXP not installed"];
    1265             :                 }
    1266             :         }
    1267             :         
    1268             :         // Make a short description of the commander
    1269             :         NSString *legalDesc = OODisplayStringFromLegalStatus([cdr oo_intForKey:@"legal_status"]);
    1270             :         
    1271             :         rating = KillCountToRatingAndKillString([cdr oo_unsignedIntForKey:@"ship_kills"]);
    1272             :         OOCreditsQuantity money = OODeciCreditsFromObject([cdr objectForKey:@"credits"]);
    1273             :         
    1274             :         // Nikos - Add some more information in the load game screen (current location, galaxy number and timestamp).
    1275             :         //-------------------------------------------------------------------------------------------------------------------------
    1276             :         
    1277             :         int                     galNumber;
    1278             :         NSString                *timeStamp  = nil;
    1279             :         NSString                *locationName = [cdr oo_stringForKey:@"current_system_name"];
    1280             : 
    1281             :         // If there is no key containing the name of the current system in
    1282             :         // the savefile, calculating what it should have been is going to
    1283             :         // be tricky now that system generation isn't seed based - but
    1284             :         // this implies a save game well over 5 years old.
    1285             :         if (locationName == nil)
    1286             :         {       
    1287             :                 // Leaving the location blank in this case is probably okay
    1288             :                 locationName = @"";
    1289             :         }
    1290             :         
    1291             :         galNumber = [cdr oo_intForKey:@"galaxy_number"] + 1;  // Galaxy numbering starts at 0.
    1292             : 
    1293             :         NSString *locationGov = @"";
    1294             :         NSString *locationEco = @"";
    1295             :         NSString *locationTL = [cdr objectForKey:@"current_system_techlevel"] ? [NSString stringWithFormat:@"%u", [cdr oo_unsignedIntForKey:@"current_system_techlevel"] + 1] : nil;
    1296             :         if (locationTL)
    1297             :         {
    1298             :                 locationGov = [NSString stringWithFormat:@"%c", [cdr oo_unsignedCharForKey:@"current_system_government"]];
    1299             :                 locationEco = [NSString stringWithFormat:@" %c", (7 - [cdr oo_unsignedCharForKey:@"current_system_economy"]) + 16];
    1300             :         }
    1301             :         else  locationTL = @"";
    1302             :         
    1303             :         timeStamp = ClockToString([cdr oo_doubleForKey:@"ship_clock" defaultValue:PLAYER_SHIP_CLOCK_START], NO);
    1304             :         
    1305             :         //-------------------------------------------------------------------------------------------------------------------------
    1306             :         
    1307             :         NSString                *cdrDesc = nil;
    1308             :         
    1309             :         cdrDesc = [NSString stringWithFormat:DESC(@"loadsavescreen-commander-@-rated-@-has-@-legal-status-@-ship-@-location-@-g-@-eco-@-gov-@-tl-@-timestamp-@"),
    1310             :                 [cdr oo_stringForKey:@"player_name"],
    1311             :                 rating,
    1312             :                 OOCredits(money),
    1313             :                 legalDesc,
    1314             :                 shipName,
    1315             :                 locationName,
    1316             :                 galNumber,
    1317             :                 locationEco,
    1318             :                 locationGov,
    1319             :                 locationTL,
    1320             :                 timeStamp];
    1321             :         
    1322             :         //-------------------------------------------------------------------------------------------------------------------------
    1323             :         
    1324             :         [gui addLongText:cdrDesc startingAtRow:CDRDESCROW align:GUI_ALIGN_LEFT];
    1325             :         
    1326             : }
    1327             : 
    1328             : 
    1329             : - (int) findIndexOfCommander: (NSString *)cdrName
    1330             : {
    1331             :         unsigned i;
    1332             :         for (i=0; i < [cdrDetailArray count]; i++)
    1333             :         {
    1334             :                 NSString *currentName = [[cdrDetailArray oo_dictionaryAtIndex: i] oo_stringForKey:@"player_save_name" defaultValue:[[cdrDetailArray oo_dictionaryAtIndex: i] oo_stringForKey:@"player_name"]];
    1335             :                 if([cdrName compare: currentName] == NSOrderedSame)
    1336             :                 {
    1337             :                         return i;
    1338             :                 }
    1339             :         }
    1340             :         
    1341             :         // not found!
    1342             :         return -1;
    1343             : }
    1344             : 
    1345             : #endif
    1346             : 
    1347             : @end
    1348             : 
    1349             : 
    1350             : #if OO_USE_CUSTOM_LOAD_SAVE
    1351             : 
    1352             : @implementation MyOpenGLView (OOLoadSaveExtensions)
    1353             : 
    1354             : - (BOOL)isCommandModifierKeyDown
    1355             : {
    1356             :         return [self isCtrlDown];
    1357             : }
    1358             : 
    1359             : @end
    1360             : 
    1361             : #endif
    1362             : 
    1363             : 
    1364           0 : static uint16_t PersonalityForCommanderDict(NSDictionary *dict)
    1365             : {
    1366             :         uint16_t personality = [dict oo_unsignedShortForKey:@"entity_personality" defaultValue:ENTITY_PERSONALITY_INVALID];
    1367             :         
    1368             :         if (personality == ENTITY_PERSONALITY_INVALID)
    1369             :         {
    1370             :                 // For pre-1.74 saved games, generate a default personality based on some hashes.
    1371             :                 personality = [[dict oo_stringForKey:@"ship_desc"] oo_hash] * [[dict oo_stringForKey:@"player_name"] oo_hash];
    1372             :         }
    1373             :         
    1374             :         return personality & ENTITY_PERSONALITY_MAX;
    1375             : }
    1376             : 
    1377             : 
    1378           0 : OOCreditsQuantity OODeciCreditsFromDouble(double doubleDeciCredits)
    1379             : {
    1380             :         /*      Clamp value to 0..kOOMaxCredits.
    1381             :                 The important bit here is that kOOMaxCredits can't be represented
    1382             :                 exactly as a double, and casting it rounds it up; casting this value
    1383             :                 back to an OOCreditsQuantity truncates it. Comparing value directly to
    1384             :                 kOOMaxCredits promotes kOOMaxCredits to a double, giving us this
    1385             :                 problem.
    1386             :                 nextafter(kOOMaxCredits, -1) gives us the highest non-truncated
    1387             :                 credits value that's representable as a double (namely,
    1388             :                 18 446 744 073 709 549 568 decicredits, or 2047 less than kOOMaxCredits).
    1389             :                 -- Ahruman 2011-02-27
    1390             :         */
    1391             :         if (doubleDeciCredits > 0)
    1392             :         {
    1393             :                 doubleDeciCredits = round(doubleDeciCredits);
    1394             :                 double threshold = nextafter(kOOMaxCredits, -1);
    1395             :                 
    1396             :                 if (doubleDeciCredits <= threshold)
    1397             :                 {
    1398             :                         return doubleDeciCredits;
    1399             :                 }
    1400             :                 else
    1401             :                 {
    1402             :                         return kOOMaxCredits;
    1403             :                 }
    1404             :         }
    1405             :         else
    1406             :         {
    1407             :                 return 0;
    1408             :         }
    1409             : }
    1410             : 
    1411             : 
    1412           0 : OOCreditsQuantity OODeciCreditsFromObject(id object)
    1413             : {
    1414             :         if ([object isKindOfClass:[NSNumber class]] && [object oo_isFloatingPointNumber])
    1415             :         {
    1416             :                 return OODeciCreditsFromDouble([object doubleValue]);
    1417             :         }
    1418             :         else
    1419             :         {
    1420             :                 return OOUnsignedLongLongFromObject(object, 0);
    1421             :         }
    1422             : }

Generated by: LCOV version 1.14