Oolite
Loading...
Searching...
No Matches
OOOXZManager.m
Go to the documentation of this file.
1/*
2
3OOOXZManager.m
4
5Responsible for installing and uninstalling OXZs
6
7Oolite
8Copyright (C) 2004-2013 Giles C Williams and contributors
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23MA 02110-1301, USA.
24
25*/
26
27#import "OOOXZManager.h"
28#import "OOPListParsing.h"
29#import "OOStringParsing.h"
30#import "ResourceManager.h"
31#import "OOCacheManager.h"
32#import "Universe.h"
33#import "GuiDisplayGen.h"
34#import "PlayerEntity.h"
35#import "PlayerEntitySound.h"
40#import "OOColor.h"
41#import "OOStringExpander.h"
42#import "MyOpenGLView.h"
43
44#import "unzip.h"
45
47
48/* The URL for the manifest.plist array. */
49/* switching (temporarily maybe) to oolite.space - Nikos 20230507 */
50/*static NSString * const kOOOXZDataURL = @"http://addons.oolite.org/api/1.0/overview";*/
51static NSString * const kOOOXZDataURL = @"https://addons.oolite.space/api/1.0/overview";
52/* The config parameter to use a non-default URL at runtime */
53static NSString * const kOOOXZDataConfig = @"oxz-index-url";
54/* The filename to store the downloaded manifest.plist array */
55static NSString * const kOOOXZManifestCache = @"Oolite-manifests.plist";
56/* The filename to temporarily store the downloaded OXZ. Has an OXZ extension since we might want to read its manifest.plist out of it; */
57static NSString * const kOOOXZTmpPath = @"Oolite-download.oxz";
58/* The filename to temporarily store the downloaded plists. */
59static NSString * const kOOOXZTmpPlistPath = @"Oolite-download.plist";
60
61/* Log file record types */
62static NSString * const kOOOXZErrorLog = @"oxz.manager.error";
63static NSString * const kOOOXZDebugLog = @"oxz.manager.debug";
64
65
66/* Filter components */
67static NSString * const kOOOXZFilterAll = @"*";
68static NSString * const kOOOXZFilterUpdates = @"u";
69static NSString * const kOOOXZFilterInstallable = @"i";
70static NSString * const kOOOXZFilterKeyword = @"k:";
71static NSString * const kOOOXZFilterAuthor = @"a:";
72static NSString * const kOOOXZFilterCategory = @"c:";
73static NSString * const kOOOXZFilterDays = @"d:";
74static NSString * const kOOOXZFilterTag = @"t:";
75
76
77typedef enum {
82 // for things to work, _ALREADY must be the first UNINSTALLABLE state
83 // and all the INSTALLABLE ones must be before all the UNINSTALLABLE ones
89
90
91enum {
116
117NSComparisonResult oxzSort(id m1, id m2, void *context);
118
120
121// protocol was only formalised in 10.7
122#if OOLITE_MAC_OS_X_10_7
123@interface OOOXZManager (OOPrivate) <NSURLConnectionDataDelegate>
124#else
125@interface OOOXZManager (NSURLConnectionDataDelegate)
126#endif
127
128- (NSString *) manifestPath;
129- (NSString *) downloadPath;
130- (NSString *) extractionBasePathForIdentifier:(NSString *)identifier andVersion:(NSString *)version;
131- (NSString *) dataURL;
132- (NSString *) humanSize:(NSUInteger)bytes;
133
134- (BOOL) ensureInstallPath;
135
136- (BOOL) beginDownload:(NSMutableURLRequest *)request;
137- (BOOL) processDownloadedManifests;
138- (BOOL) processDownloadedOXZ;
139
140- (OXZInstallableState) installableState:(NSDictionary *)manifest;
141- (OOColor *) colorForManifest:(NSDictionary *)manifest;
142- (NSString *) installStatusForManifest:(NSDictionary *)manifest;
143
144- (BOOL) validateFilter:(NSString *)input;
145
146- (void) setOXZList:(NSArray *)list;
147- (void) setFilteredList:(NSArray *)list;
148- (NSArray *) applyCurrentFilter:(NSArray *)list;
149
150- (void) setCurrentDownload:(NSURLConnection *)download withLabel:(NSString *)label;
151- (void) setProgressStatus:(NSString *)newStatus;
152
153- (BOOL) installOXZ:(NSUInteger)item;
154- (BOOL) updateAllOXZ;
155- (BOOL) removeOXZ:(NSUInteger)item;
156- (NSArray *) installOptions;
157- (NSArray *) removeOptions;
158
159- (NSString *) extractOXZ:(NSUInteger)item;
160
161/* Delegates for URL downloader */
162- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
163- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
164- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
165- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
166
167@end
168
169@interface OOOXZManager (OOFilterRules)
170- (BOOL) applyFilterByNoFilter:(NSDictionary *)manifest;
171- (BOOL) applyFilterByUpdateRequired:(NSDictionary *)manifest;
172- (BOOL) applyFilterByInstallable:(NSDictionary *)manifest;
173- (BOOL) applyFilterByKeyword:(NSDictionary *)manifest keyword:(NSString *)keyword;
174- (BOOL) applyFilterByAuthor:(NSDictionary *)manifest author:(NSString *)author;
175- (BOOL) applyFilterByDays:(NSDictionary *)manifest days:(NSString *)days;
176- (BOOL) applyFilterByTag:(NSDictionary *)manifest tag:(NSString *)tag;
177- (BOOL) applyFilterByCategory:(NSDictionary *)manifest category:(NSString *)category;
178
179@end
180
181
182
183@implementation OOOXZManager
184
185+ (OOOXZManager *)sharedManager
186{
187 // NOTE: assumes single-threaded first access.
188 if (sSingleton == nil) sSingleton = [[self alloc] init];
189 return sSingleton;
190}
191
192
193- (id) init
194{
195 self = [super init];
196 if (self != nil)
197 {
198 _downloadStatus = OXZ_DOWNLOAD_NONE;
199 // if the file has not been downloaded, this will be nil
200 [self setOXZList:OOArrayFromFile([self manifestPath])];
201 OOLog(kOOOXZDebugLog,@"Initialised with %@",_oxzList);
202 _interfaceState = OXZ_STATE_NODATA;
203 _currentFilter = @"*";
204
205 _interfaceShowingOXZDetail = NO;
206 _changesMade = NO;
207 _downloadAllDependencies = NO;
208 _dependencyStack = [[NSMutableSet alloc] initWithCapacity:8];
209 [self setProgressStatus:@""];
210 }
211 return self;
212}
213
214
215- (void)dealloc
216{
217 if (sSingleton == self) sSingleton = nil;
218
219 [self setCurrentDownload:nil withLabel:nil];
220 DESTROY(_oxzList);
221 DESTROY(_managedList);
222 DESTROY(_filteredList);
223
224 [super dealloc];
225}
226
227
228/* The install path for OXZs downloaded by
229 * Oolite. Library/ApplicationSupport seems to be the most appropriate
230 * location. */
231- (NSString *) installPath
232{
233 const char *managedAddOnsEnv = SDL_getenv("OO_MANAGEDADDONSDIR");
234
235 if (managedAddOnsEnv)
236 {
237 return [NSString stringWithUTF8String:managedAddOnsEnv];
238 }
239 else
240 {
241 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask,YES);
242 NSString *appPath = [paths objectAtIndex:0];
243 if (appPath != nil)
244 {
245 appPath = [appPath stringByAppendingPathComponent:@"Oolite"];
246 #if OOLITE_MAC_OS_X
247 appPath = [appPath stringByAppendingPathComponent:@"Managed AddOns"];
248 #else
249 /* GNUStep uses "ApplicationSupport" rather than "Application
250 * Support" so match convention by not putting a space in the
251 * path either */
252 appPath = [appPath stringByAppendingPathComponent:@"ManagedAddOns"];
253 #endif
254 return appPath;
255 }
256 }
257 return nil;
258}
259
260/* The extract path for OXZs . */
261- (NSString *) extractAddOnsPath
262{
263 const char *addOnsExtractEnv = SDL_getenv("OO_ADDONSEXTRACTDIR");
264
265 if (addOnsExtractEnv)
266 {
267 return [NSString stringWithUTF8String:addOnsExtractEnv];
268 }
269 else
270 {
271#if OOLITE_WINDOWS
272 #if OO_GAME_DATA_TO_USER_FOLDER
273 return [NSString stringWithFormat:@"%s\\Oolite\\AddOns", SDL_getenv("LOCALAPPDATA")];
274 #else
275 return @"../AddOns";
276 #endif
277#else
278 return [[NSHomeDirectory() stringByAppendingPathComponent:@".Oolite"] stringByAppendingPathComponent:@"AddOns"];
279#endif
280 }
281}
282
283/* Add additional AddOns paths */
284- (NSArray *) additionalAddOnsPaths
285{
286 const char *additionalAddOnsEnv = SDL_getenv("OO_ADDITIONALADDONSDIRS");
287
288 if (additionalAddOnsEnv) {
289 NSString *envStr = [NSString stringWithUTF8String:additionalAddOnsEnv];
290 return [envStr componentsSeparatedByString:@","];
291 }
292 return [NSArray array];
293}
294
295
296- (NSString *) extractionBasePathForIdentifier:(NSString *)identifier andVersion:(NSString *)version
297{
298 NSString *basePath = [[ResourceManager userRootPaths] lastObject];
299 NSString *rawMainDir = [NSString stringWithFormat:@"%@-%@.off",identifier,version];
300
301 NSCharacterSet *blacklist = [NSCharacterSet characterSetWithCharactersInString:@"'#%^&{}[]/~|\\?<,:\" "];
302 return [[[basePath stringByAppendingPathComponent:[[rawMainDir componentsSeparatedByCharactersInSet:blacklist] componentsJoinedByString:@""]] retain] autorelease];
303}
304
305
306- (BOOL) ensureInstallPath
307{
308 BOOL exists, directory;
309 NSFileManager *fmgr = [NSFileManager defaultManager];
310 NSString *path = [self installPath];
311
312 exists = [fmgr fileExistsAtPath:path isDirectory:&directory];
313
314 if (exists && !directory)
315 {
316 OOLog(kOOOXZErrorLog, @"Expected %@ to be a folder, but it is a file.", path);
317 return NO;
318 }
319 if (!exists)
320 {
321 if (![fmgr oo_createDirectoryAtPath:path attributes:nil])
322 {
323 OOLog(kOOOXZErrorLog, @"Could not create folder %@.", path);
324 return NO;
325 }
326 }
327
328 return YES;
329}
330
331
332- (NSString *) manifestPath
333{
334 return [[[OOCacheManager sharedCache] cacheDirectoryPathCreatingIfNecessary:YES] stringByAppendingPathComponent:kOOOXZManifestCache];
335}
336
337
338/* Download mechanism could destroy a correct file if it failed
339 * half-way and was downloaded on top of the old one. So this loads it
340 * off to the side a bit */
341- (NSString *) downloadPath
342{
343 if (_interfaceState == OXZ_STATE_UPDATING)
344 {
345 return [[[OOCacheManager sharedCache] cacheDirectoryPathCreatingIfNecessary:YES] stringByAppendingPathComponent:kOOOXZTmpPlistPath];
346 }
347 else
348 {
349 return [[[OOCacheManager sharedCache] cacheDirectoryPathCreatingIfNecessary:YES] stringByAppendingPathComponent:kOOOXZTmpPath];
350 }
351}
352
353
354- (NSString *) dataURL
355{
356 /* Not expected to be set in general, but might be useful for some users */
357 NSString *url = [[NSUserDefaults standardUserDefaults] stringForKey:kOOOXZDataConfig];
358 if (url != nil)
359 {
360 return url;
361 }
362 return kOOOXZDataURL;
363}
364
365
366- (NSString *) humanSize:(NSUInteger)bytes
367{
368 if (bytes == 0)
369 {
370 return DESC(@"oolite-oxzmanager-missing-field");
371 }
372 else if (bytes < 1024)
373 {
374 return @"<1 kB";
375 }
376 else if (bytes < 1024*1024)
377 {
378 return [NSString stringWithFormat:@"%llu kB",bytes>>10];
379 }
380 else
381 {
382 return [NSString stringWithFormat:@"%.2f MB",((float)(bytes>>10))/1024];
383 }
384}
385
386
387- (void) setOXZList:(NSArray *)list
388{
389 DESTROY(_oxzList);
390 if (list != nil)
391 {
392 _oxzList = [[list sortedArrayUsingFunction:oxzSort context:NULL] retain];
393 // needed for update to available versions
394 DESTROY(_managedList);
395 }
396}
397
398
399- (void) setFilteredList:(NSArray *)list
400{
401 DESTROY(_filteredList);
402 _filteredList = [list copy]; // copy retains
403}
404
405
406- (void) setFilter:(NSString *)filter
407{
408 DESTROY(_currentFilter);
409 _currentFilter = [[filter lowercaseString] copy]; // copy retains
410}
411
412
413- (NSArray *) applyCurrentFilter:(NSArray *)list
414{
415 SEL filterSelector = @selector(applyFilterByNoFilter:);
416 NSString *parameter = nil;
417 if ([_currentFilter isEqualToString:kOOOXZFilterUpdates])
418 {
419 filterSelector = @selector(applyFilterByUpdateRequired:);
420 }
421 else if ([_currentFilter isEqualToString:kOOOXZFilterInstallable])
422 {
423 filterSelector = @selector(applyFilterByInstallable:);
424 }
425 else if ([_currentFilter hasPrefix:kOOOXZFilterKeyword])
426 {
427 filterSelector = @selector(applyFilterByKeyword:keyword:);
428 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterKeyword length]];
429 }
430 else if ([_currentFilter hasPrefix:kOOOXZFilterAuthor])
431 {
432 filterSelector = @selector(applyFilterByAuthor:author:);
433 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterAuthor length]];
434 }
435 else if ([_currentFilter hasPrefix:kOOOXZFilterDays])
436 {
437 filterSelector = @selector(applyFilterByDays:days:);
438 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterDays length]];
439 }
440 else if ([_currentFilter hasPrefix:kOOOXZFilterTag])
441 {
442 filterSelector = @selector(applyFilterByTag:tag:);
443 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterTag length]];
444 }
445 else if ([_currentFilter hasPrefix:kOOOXZFilterCategory])
446 {
447 filterSelector = @selector(applyFilterByCategory:category:);
448 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterCategory length]];
449 }
450
451 NSMutableArray *filteredList = [NSMutableArray arrayWithCapacity:[list count]];
452 NSDictionary *manifest = nil;
453 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:filterSelector]];
454 [invocation setSelector:filterSelector];
455 [invocation setTarget:self];
456 if (parameter != nil)
457 {
458 [invocation setArgument:&parameter atIndex:3];
459 }
460
461 foreach (manifest, list)
462 {
463 [invocation setArgument:&manifest atIndex:2];
464 [invocation invoke];
465 BOOL filterAccepted = NO;
466 [invocation getReturnValue:&filterAccepted];
467 if (filterAccepted)
468 {
469 [filteredList addObject:manifest];
470 }
471 }
472 // any bad filter that gets this far is also treated as '*'
473 // so don't need to explicitly test for '*' or ''
474 return [[filteredList copy] autorelease];
475}
476
477
478/*** Start filters ***/
479- (BOOL) applyFilterByNoFilter:(NSDictionary *)manifest
480{
481 return YES;
482}
483
484
485- (BOOL) applyFilterByUpdateRequired:(NSDictionary *)manifest
486{
487 return ([self installableState:manifest] == OXZ_INSTALLABLE_UPDATE);
488}
489
490
491- (BOOL) applyFilterByInstallable:(NSDictionary *)manifest
492{
493 return ([self installableState:manifest] < OXZ_UNINSTALLABLE_ALREADY);
494}
495
496
497- (BOOL) applyFilterByKeyword:(NSDictionary *)manifest keyword:(NSString *)keyword
498{
499 NSString *parameter = nil;
500 NSArray *parameters = [NSArray arrayWithObjects:kOOManifestTitle,kOOManifestDescription,kOOManifestCategory,nil];
501
502 // trim any eventual leading whitespace from input string
503 keyword = [keyword stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
504
505 foreach (parameter,parameters)
506 {
507 if ([[manifest oo_stringForKey:parameter] rangeOfString:keyword options:NSCaseInsensitiveSearch].location != NSNotFound)
508 {
509 return YES;
510 }
511 }
512 // tags are slightly different
513 parameters = [manifest oo_arrayForKey:kOOManifestTags];
514 foreach (parameter,parameters)
515 {
516 if ([parameter rangeOfString:keyword options:NSCaseInsensitiveSearch].location != NSNotFound)
517 {
518 return YES;
519 }
520 }
521
522 return NO;
523}
524
525
526- (BOOL) applyFilterByAuthor:(NSDictionary *)manifest author:(NSString *)author
527{
528 // trim any eventual leading whitespace from input string
529 author = [author stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
530
531 NSString *mAuth = [manifest oo_stringForKey:kOOManifestAuthor];
532 return ([mAuth rangeOfString:author options:NSCaseInsensitiveSearch].location != NSNotFound);
533}
534
535
536- (BOOL) applyFilterByDays:(NSDictionary *)manifest days:(NSString *)days
537{
538 NSInteger i = [days integerValue];
539 if (i < 1)
540 {
541 return NO;
542 }
543 else
544 {
545 NSUInteger updated = [manifest oo_unsignedIntegerForKey:kOOManifestUploadDate];
546 NSUInteger now = (NSUInteger)[[NSDate date] timeIntervalSince1970];
547 return (updated + (86400 * i) > now);
548 }
549}
550
551
552- (BOOL) applyFilterByTag:(NSDictionary *)manifest tag:(NSString *)tag
553{
554 NSString *parameter = nil;
555 NSArray *parameters = [manifest oo_arrayForKey:kOOManifestTags];
556
557 // trim any eventual leading whitespace from input string
558 tag = [tag stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
559
560 foreach (parameter,parameters)
561 {
562 if ([parameter rangeOfString:tag options:NSCaseInsensitiveSearch].location != NSNotFound)
563 {
564 return YES;
565 }
566 }
567
568 return NO;
569}
570
571
572- (BOOL) applyFilterByCategory:(NSDictionary *)manifest category:(NSString *)category
573{
574 // trim any eventual leading whitespace from input string
575 category = [category stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
576
577 NSString *mCategory = [manifest oo_stringForKey:kOOManifestCategory];
578 return ([mCategory rangeOfString:category options:NSCaseInsensitiveSearch].location != NSNotFound);
579}
580
581
582/*** End filters ***/
583
584- (BOOL) validateFilter:(NSString *)input
585{
586 NSString *filter = [input lowercaseString];
587 if (([filter length] == 0) // empty is valid
588 || ([filter isEqualToString:kOOOXZFilterAll])
589 || ([filter isEqualToString:kOOOXZFilterUpdates])
590 || ([filter isEqualToString:kOOOXZFilterInstallable])
591 || ([filter hasPrefix:kOOOXZFilterKeyword] && [filter length] > [kOOOXZFilterKeyword length])
592 || ([filter hasPrefix:kOOOXZFilterAuthor] && [filter length] > [kOOOXZFilterAuthor length])
593 || ([filter hasPrefix:kOOOXZFilterDays] && [[filter substringFromIndex:[kOOOXZFilterDays length]] intValue] > 0)
594 || ([filter hasPrefix:kOOOXZFilterTag] && [filter length] > [kOOOXZFilterTag length])
595 || ([filter hasPrefix:kOOOXZFilterCategory] && [filter length] > [kOOOXZFilterCategory length])
596 )
597 {
598 return YES;
599 }
600
601 return NO;
602}
603
604
605- (void) setCurrentDownload:(NSURLConnection *)download withLabel:(NSString *)label
606{
607 if (_currentDownload != nil)
608 {
609 [_currentDownload cancel]; // releases via delegate
610 }
611 _currentDownload = [download retain];
612 DESTROY(_currentDownloadName);
613 _currentDownloadName = [label copy];
614}
615
616
617- (void) setProgressStatus:(NSString *)new
618{
619 DESTROY(_progressStatus);
620 _progressStatus = [new copy];
621}
622
623- (BOOL) updateManifests
624{
625 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self dataURL]]];
626 if (_downloadStatus != OXZ_DOWNLOAD_NONE)
627 {
628 return NO;
629 }
630 _downloadStatus = OXZ_DOWNLOAD_STARTED;
631 _interfaceState = OXZ_STATE_UPDATING;
632 [self setProgressStatus:@""];
633
634 return [self beginDownload:request];
635}
636
637
638- (BOOL) beginDownload:(NSMutableURLRequest *)request
639{
640 NSString *userAgent = [NSString stringWithFormat:@"Oolite/%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]];
641 [request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
642 [request setHTTPShouldHandleCookies:NO];
643 NSURLConnection *download = [[NSURLConnection alloc] initWithRequest:request delegate:self];
644 if (download)
645 {
646 _downloadProgress = 0;
647 _downloadExpected = 0;
648 NSString *label = DESC(@"oolite-oxzmanager-download-label-list");
649 if (_interfaceState != OXZ_STATE_UPDATING)
650 {
651 NSDictionary *expectedManifest = nil;
652 expectedManifest = [_filteredList objectAtIndex:_item];
653
654 label = [expectedManifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-download-label-oxz")];
655 }
656
657 [self setCurrentDownload:download withLabel:label]; // retains it
658 [download release];
659 OOLog(kOOOXZDebugLog,@"Download request received, using %@ and downloading to %@",[request URL],[self downloadPath]);
660 return YES;
661 }
662 else
663 {
664 OOLog(kOOOXZErrorLog,@"Unable to start downloading file at %@",[request URL]);
665 _downloadStatus = OXZ_DOWNLOAD_ERROR;
666 return NO;
667 }
668}
669
670
671- (BOOL) cancelUpdate
672{
673 if (!(_interfaceState == OXZ_STATE_UPDATING || _interfaceState == OXZ_STATE_INSTALLING) || _downloadStatus == OXZ_DOWNLOAD_NONE)
674 {
675 return NO;
676 }
677 OOLog(kOOOXZDebugLog, @"%@", @"Trying to cancel file download");
678 if (_currentDownload != nil)
679 {
680 [_currentDownload cancel];
681 }
682 else if (_downloadStatus == OXZ_DOWNLOAD_COMPLETE)
683 {
684 NSString *path = [self downloadPath];
685 [[NSFileManager defaultManager] oo_removeItemAtPath:path];
686 }
687 _downloadStatus = OXZ_DOWNLOAD_NONE;
688 if (_interfaceState == OXZ_STATE_INSTALLING)
689 {
690 _interfaceState = OXZ_STATE_PICK_INSTALL;
691 }
692 else
693 {
694 _interfaceState = OXZ_STATE_MAIN;
695 }
696 [self gui];
697 return YES;
698}
699
700
701- (NSArray *) manifests
702{
703 return _oxzList;
704}
705
706
707- (NSArray *) managedOXZs
708{
709 if (_managedList == nil)
710 {
711 // if this list is being reset, also reset the current install list
713 NSArray *managedOXZs = [[NSFileManager defaultManager] oo_directoryContentsAtPath:[self installPath]];
714 NSMutableArray *manifests = [NSMutableArray arrayWithCapacity:[managedOXZs count]];
715 NSString *filename = nil;
716 NSString *fullpath = nil;
717 NSDictionary *manifest = nil;
718 foreach (filename, managedOXZs)
719 {
720 fullpath = [[self installPath] stringByAppendingPathComponent:filename];
721 manifest = OODictionaryFromFile([fullpath stringByAppendingPathComponent:@"manifest.plist"]);
722 if (manifest != nil)
723 {
724 NSMutableDictionary *adjManifest = [NSMutableDictionary dictionaryWithDictionary:manifest];
725 [adjManifest setObject:fullpath forKey:kOOManifestFilePath];
726
727 NSDictionary *stored = nil;
728 /* The list is already sorted to put the latest
729 * versions first. This flag means that it stops
730 * checking the list for versions once it finds one
731 * that is plausibly installable */
732 BOOL foundInstallable = NO;
733 foreach (stored, _oxzList)
734 {
735 if ([[stored oo_stringForKey:kOOManifestIdentifier] isEqualToString:[manifest oo_stringForKey:kOOManifestIdentifier]])
736 {
737 if (foundInstallable == NO)
738 {
739 [adjManifest setObject:[stored oo_stringForKey:kOOManifestVersion] forKey:kOOManifestAvailableVersion];
740 [adjManifest setObject:[stored oo_stringForKey:kOOManifestDownloadURL] forKey:kOOManifestDownloadURL];
741 if ([ResourceManager checkVersionCompatibility:manifest forOXP:nil])
742 {
743 foundInstallable = YES;
744 }
745 }
746 }
747 }
748
749 [manifests addObject:adjManifest];
750 }
751 }
752 [manifests sortUsingFunction:oxzSort context:NULL];
753
754 _managedList = [manifests copy];
755 }
756 return _managedList;
757}
758
759
760- (BOOL) processDownloadedManifests
761{
762 if (_downloadStatus != OXZ_DOWNLOAD_COMPLETE)
763 {
764 return NO;
765 }
766 [self setOXZList:OOArrayFromFile([self downloadPath])];
767 if (_oxzList != nil)
768 {
769 [_oxzList writeToFile:[self manifestPath] atomically:YES];
770 // and clean up the temp file
771 [[NSFileManager defaultManager] oo_removeItemAtPath:[self downloadPath]];
772 // invalidate the managed list
773 DESTROY(_managedList);
774 _interfaceState = OXZ_STATE_TASKDONE;
775 [self gui];
776 return YES;
777 }
778 else
779 {
780 _downloadStatus = OXZ_DOWNLOAD_ERROR;
781 OOLog(kOOOXZErrorLog,@"Downloaded manifest was not a valid plist, has been left in %@",[self downloadPath]);
782 // revert to the old one
783 [self setOXZList:OOArrayFromFile([self manifestPath])];
784 _interfaceState = OXZ_STATE_TASKDONE;
785 [self gui];
786 return NO;
787 }
788}
789
790
791- (BOOL) processDownloadedOXZ
792{
793 if (_downloadStatus != OXZ_DOWNLOAD_COMPLETE)
794 {
795 return NO;
796 }
797
798 NSDictionary *downloadedManifest = OODictionaryFromFile([[self downloadPath] stringByAppendingPathComponent:@"manifest.plist"]);
799 if (downloadedManifest == nil)
800 {
801 _downloadStatus = OXZ_DOWNLOAD_ERROR;
802 OOLog(kOOOXZErrorLog,@"Downloaded OXZ does not contain a manifest.plist, has been left in %@",[self downloadPath]);
803 _interfaceState = OXZ_STATE_TASKDONE;
804 [self gui];
805 return NO;
806 }
807 NSDictionary *expectedManifest = nil;
808 expectedManifest = [_filteredList objectAtIndex:_item];
809
810 if (expectedManifest == nil ||
811 (![[downloadedManifest oo_stringForKey:kOOManifestIdentifier] isEqualToString:[expectedManifest oo_stringForKey:kOOManifestIdentifier]]) ||
812 (![[downloadedManifest oo_stringForKey:kOOManifestVersion] isEqualToString:[expectedManifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[expectedManifest oo_stringForKey:kOOManifestVersion]]])
813 )
814 {
815 _downloadStatus = OXZ_DOWNLOAD_ERROR;
816 OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ does not have the same identifer and version as expected. This might be due to your manifests list being out of date - try updating it.");
817 _interfaceState = OXZ_STATE_TASKDONE;
818 [self gui];
819 return NO;
820 }
821 // this appears to be the OXZ we expected
822 // filename is going to be identifier.oxz
823 NSString *filename = [[downloadedManifest oo_stringForKey:kOOManifestIdentifier] stringByAppendingString:@".oxz"];
824
825 if (![self ensureInstallPath])
826 {
827 _downloadStatus = OXZ_DOWNLOAD_ERROR;
828 OOLog(kOOOXZErrorLog, @"%@", @"Unable to create installation folder.");
829 _interfaceState = OXZ_STATE_TASKDONE;
830 [self gui];
831 return NO;
832 }
833
834 // delete filename if it exists from OXZ folder
835 NSString *destination = [[self installPath] stringByAppendingPathComponent:filename];
836 [[NSFileManager defaultManager] oo_removeItemAtPath:destination];
837
838 // move the temp file on to it
839 if (![[NSFileManager defaultManager] oo_moveItemAtPath:[self downloadPath] toPath:destination])
840 {
841 _downloadStatus = OXZ_DOWNLOAD_ERROR;
842 OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
843 _interfaceState = OXZ_STATE_TASKDONE;
844 [self gui];
845 return NO;
846 }
847 _changesMade = YES;
848 DESTROY(_managedList); // will need updating
849 // do this now to cope with circular dependencies on download
851
864 NSArray *requires = [downloadedManifest oo_arrayForKey:kOOManifestRequiresOXPs defaultValue:nil];
865 if (requires == nil)
866 {
867 // just in case the requirements are only specified in the online copy
868 requires = [expectedManifest oo_arrayForKey:kOOManifestRequiresOXPs defaultValue:nil];
869 }
870 NSDictionary *requirement = nil;
871 NSMutableString *progress = [NSMutableString stringWithCapacity:2048];
872 OOLog(kOOOXZDebugLog,@"Dependency stack has %llu elements",[_dependencyStack count]);
873
874 if ([_dependencyStack count] > 0)
875 {
876 // will remove as iterate, so create a temp copy to iterate over
877 NSSet *tempStack = [NSSet setWithSet:_dependencyStack];
878 foreach (requirement, tempStack)
879 {
880 OOLog(kOOOXZDebugLog,@"Dependency stack: checking %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
881 if (![ResourceManager manifest:downloadedManifest HasUnmetDependency:requirement logErrors:NO]
882 && requires != nil && [requires containsObject:requirement])
883 {
884 // it was unmet, but now it's met
885 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-now-has-@"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
886 [_dependencyStack removeObject:requirement];
887 OOLog(kOOOXZDebugLog, @"%@", @"Dependency stack: requirement met");
888 } else if ([[requirement oo_stringForKey:kOOManifestRelationIdentifier] isEqualToString:[downloadedManifest oo_stringForKey:kOOManifestIdentifier]]) {
889 // remove the requirement for the just downloaded OXP
890 [_dependencyStack removeObject:requirement];
891 }
892 }
893 }
894 if (requires != nil)
895 {
896 foreach (requirement, requires)
897 {
898 if ([ResourceManager manifest:downloadedManifest HasUnmetDependency:requirement logErrors:NO])
899 {
900 OOLog(kOOOXZDebugLog,@"Dependency stack: adding %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
901 [_dependencyStack addObject:requirement];
902 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-requires-@"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
903 }
904 }
905 }
906 if ([_dependencyStack count] > 0)
907 {
908 // get an object from the requirements list, and download it
909 // if it can be found
910 BOOL undownloadedRequirement = NO;
911 NSDictionary *availableDownload = nil;
912 BOOL foundDownload = NO;
913 NSUInteger index = 0;
914 NSString *needsIdentifier = nil;
915
916 do
917 {
918 undownloadedRequirement = YES;
919 requirement = [_dependencyStack anyObject];
920 OOLog(kOOOXZDebugLog,@"Dependency stack: next is %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
921
922 if (!_downloadAllDependencies)
923 {
924 [progress appendString:DESC(@"oolite-oxzmanager-progress-get-required")];
925 }
926 needsIdentifier = [requirement oo_stringForKey:kOOManifestRelationIdentifier];
927
928 foreach (availableDownload, _oxzList)
929 {
930 if ([[availableDownload oo_stringForKey:kOOManifestIdentifier] isEqualToString:needsIdentifier])
931 {
932 if ([ResourceManager matchVersions:requirement withVersion:[availableDownload oo_stringForKey:kOOManifestVersion]])
933 {
934 OOLog(kOOOXZDebugLog, @"%@", @"Dependency stack: found download for next item");
935 foundDownload = YES;
936 index = [_oxzList indexOfObject:availableDownload];
937 break;
938 }
939 }
940 }
941
942 if (foundDownload)
943 {
944 if ([self installableState:[_oxzList objectAtIndex:index]] == OXZ_UNINSTALLABLE_ALREADY)
945 {
946 OOLog(kOOOXZDebugLog,@"Dependency stack: %@ is downloaded but not yet loadable, removing from list.",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
947 // then this has already been downloaded, but
948 // can't be configured yet presumably because
949 // another dependency is still to be loaded
950 [_dependencyStack removeObject:requirement];
951 if ([_dependencyStack count] > 0)
952 {
953 // try again
954 undownloadedRequirement = NO;
955 }
956 else
957 {
958 // this case should probably never happen
959 // is handled below just in case
960 foundDownload = NO;
961 }
962 }
963 }
964 }
965 while (!undownloadedRequirement);
966
967 if (foundDownload)
968 {
969 // must clear filters entirely at this point
970 [self setFilteredList:_oxzList];
971 // then download that item
972 _downloadStatus = OXZ_DOWNLOAD_NONE;
973 if (_downloadAllDependencies)
974 {
975 OOLog(kOOOXZDebugLog,@"Dependency stack: installing %llu from list",index);
976 if (![self installOXZ:index]) {
977 // if a required dependency is somehow uninstallable
978 // e.g. required+maximum version don't match this Oolite
979 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-required-@-not-found"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
980 [self setProgressStatus:progress];
981 OOLog(kOOOXZErrorLog,@"OXZ dependency %@ could not be found for automatic download.",needsIdentifier);
982 _downloadStatus = OXZ_DOWNLOAD_ERROR;
983 OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
984 _interfaceState = OXZ_STATE_TASKDONE;
985 [self gui];
986 return NO;
987 }
988 }
989 else
990 {
991 _interfaceState = OXZ_STATE_DEPENDENCIES;
992 _item = index;
993 }
994 [self setProgressStatus:progress];
995 [self gui];
996 return YES;
997 }
998 // this is probably always the case, see above
999 else if ([_dependencyStack count] > 0)
1000 {
1001 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-required-@-not-found"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
1002 [self setProgressStatus:progress];
1003 OOLog(kOOOXZErrorLog,@"OXZ dependency %@ could not be found for automatic download.",needsIdentifier);
1004 _downloadStatus = OXZ_DOWNLOAD_ERROR;
1005 OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
1006 _interfaceState = OXZ_STATE_TASKDONE;
1007 [self gui];
1008 return NO;
1009 }
1010 }
1011
1012 [self setProgressStatus:@""];
1013 _interfaceState = OXZ_STATE_TASKDONE;
1014 [_dependencyStack removeAllObjects]; // just in case
1015 _downloadAllDependencies = NO;
1016 [self gui];
1017 return YES;
1018}
1019
1020
1021- (NSDictionary *) installedManifestForIdentifier:(NSString *)identifier
1022{
1023 NSArray *installed = [self managedOXZs];
1024 NSDictionary *manifest = nil;
1025 foreach (manifest,installed)
1026 {
1027 if ([[manifest oo_stringForKey:kOOManifestIdentifier] isEqualToString:identifier])
1028 {
1029 return manifest;
1030 }
1031 }
1032 return nil;
1033}
1034
1035
1036- (OXZInstallableState) installableState:(NSDictionary *)manifest
1037{
1038 NSString *title = [manifest oo_stringForKey:kOOManifestTitle defaultValue:nil];
1039 NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier defaultValue:nil];
1040 /* Check Oolite version */
1041 if (![ResourceManager checkVersionCompatibility:manifest forOXP:title])
1042 {
1044 }
1045 /* Check for current automated install */
1046 NSDictionary *installed = [self installedManifestForIdentifier:identifier];
1047 if (installed == nil)
1048 {
1049 // check for manual install
1050 installed = [ResourceManager manifestForIdentifier:identifier];
1051 }
1052
1053 if (installed != nil)
1054 {
1055 if (![[installed oo_stringForKey:kOOManifestFilePath] hasPrefix:[self installPath]])
1056 {
1057 // installed manually
1059 }
1060 if ([[installed oo_stringForKey:kOOManifestVersion] isEqualToString:[manifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[manifest oo_stringForKey:kOOManifestVersion]]]
1061 && [[NSFileManager defaultManager] fileExistsAtPath:[installed oo_stringForKey:kOOManifestFilePath]])
1062 {
1063 // installed this exact version already, and haven't
1064 // uninstalled it since entering the manager, and it's
1065 // still available
1067 }
1068 else if ([installed oo_stringForKey:kOOManifestAvailableVersion defaultValue:nil] == nil)
1069 {
1070 // installed, but no remote copy is indexed any more
1072 }
1073 }
1074 /* Check for dependencies being met */
1075 if ([ResourceManager manifestHasConflicts:manifest logErrors:NO])
1076 {
1078 }
1079 if (installed != nil)
1080 {
1081 NSString *availableVersion = [manifest oo_stringForKey:kOOManifestAvailableVersion];
1082 if (availableVersion == nil)
1083 {
1084 availableVersion = [manifest oo_stringForKey:kOOManifestVersion];
1085 }
1086 NSString *installedVersion = [installed oo_stringForKey:kOOManifestVersion];
1087 OOLog(@"version.debug",@"%@ mv:%@ mav:%@",identifier,installedVersion,availableVersion);
1088 if (CompareVersions(ComponentsFromVersionString(installedVersion),ComponentsFromVersionString(availableVersion)) == NSOrderedDescending)
1089 {
1090 // the installed copy is more recent than the server copy
1092 }
1094 }
1095 if ([ResourceManager manifestHasMissingDependencies:manifest logErrors:NO])
1096 {
1098 }
1099 return OXZ_INSTALLABLE_OKAY;
1100}
1101
1102
1103- (OOColor *) colorForManifest:(NSDictionary *)manifest
1104{
1105 switch ([self installableState:manifest])
1106 {
1108 return [OOColor yellowColor];
1110 return [OOColor cyanColor];
1112 return [OOColor orangeColor];
1114 return [OOColor brownColor];
1116 return [OOColor whiteColor];
1118 return [OOColor redColor];
1120 return [OOColor grayColor];
1122 return [OOColor blueColor];
1123 }
1124 return [OOColor yellowColor]; // never
1125}
1126
1127
1128- (NSString *) installStatusForManifest:(NSDictionary *)manifest
1129{
1130 switch ([self installableState:manifest])
1131 {
1133 return DESC(@"oolite-oxzmanager-installable-okay");
1135 return DESC(@"oolite-oxzmanager-installable-update");
1137 return DESC(@"oolite-oxzmanager-installable-depend");
1139 return DESC(@"oolite-oxzmanager-installable-conflicts");
1141 return DESC(@"oolite-oxzmanager-installable-already");
1143 return DESC(@"oolite-oxzmanager-installable-manual");
1145 return DESC(@"oolite-oxzmanager-installable-version");
1147 return DESC(@"oolite-oxzmanager-installable-noremote");
1148 }
1149 return nil; // never
1150}
1151
1152
1153
1154- (void) gui
1155{
1156 GuiDisplayGen *gui = [UNIVERSE gui];
1157 OOGUIRow startRow = OXZ_GUI_ROW_EXIT;
1158
1159#if OOLITE_WINDOWS
1160 /* unlock OXZs ahead of potential changes by making sure sound
1161 * files aren't being held open */
1163 [PLAYER destroySound];
1164#endif
1165
1166 [gui clearAndKeepBackground:YES];
1167 [gui setTitle:DESC(@"oolite-oxzmanager-title")];
1168
1169 /* This switch will give warnings unless all states are
1170 * covered. */
1171 switch (_interfaceState)
1172 {
1174 [gui setTitle:DESC(@"oolite-oxzmanager-title-setfilter")];
1175 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-currentfilter-is-@"),_currentFilter] forRow:OXZ_GUI_ROW_FILTERCURRENT align:GUI_ALIGN_LEFT];
1176 [gui addLongText:DESC(@"oolite-oxzmanager-filterhelp") startingAtRow:OXZ_GUI_ROW_FILTERHELP align:GUI_ALIGN_LEFT];
1177
1178
1179 return; // don't do normal row selection stuff
1180 case OXZ_STATE_NODATA:
1181 if (_oxzList == nil)
1182 {
1183 [gui addLongText:DESC(@"oolite-oxzmanager-firstrun") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
1184 [gui setText:DESC(@"oolite-oxzmanager-download-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
1185 [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
1186
1187 startRow = OXZ_GUI_ROW_UPDATE;
1188 }
1189 else
1190 {
1191 // update data
1192 [gui addLongText:DESC(@"oolite-oxzmanager-secondrun") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
1193 [gui setText:DESC(@"oolite-oxzmanager-download-noupdate") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
1194 [gui setKey:@"_MAIN" forRow:OXZ_GUI_ROW_PROCEED];
1195
1196 [gui setText:DESC(@"oolite-oxzmanager-update-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
1197 [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
1198
1199 startRow = OXZ_GUI_ROW_PROCEED;
1200 }
1201 break;
1203 [gui addLongText:DESC(@"oolite-oxzmanager-restart") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
1204 return; // yes, return, not break: controls are pointless here
1205 case OXZ_STATE_MAIN:
1206 [gui addLongText:DESC(@"oolite-oxzmanager-intro") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
1207 // fall through
1211 if (_interfaceState != OXZ_STATE_MAIN)
1212 {
1213 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-currentfilter-is-@-@"),OOExpand(@"[oolite_key_oxzmanager_setfilter]"),_currentFilter] forRow:OXZ_GUI_ROW_LISTFILTER align:GUI_ALIGN_LEFT];
1214 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTFILTER];
1215 }
1216
1217 [gui setText:DESC(@"oolite-oxzmanager-install") forRow:OXZ_GUI_ROW_INSTALL align:GUI_ALIGN_CENTER];
1218 [gui setKey:@"_INSTALL" forRow:OXZ_GUI_ROW_INSTALL];
1219 [gui setText:DESC(@"oolite-oxzmanager-installed") forRow:OXZ_GUI_ROW_INSTALLED align:GUI_ALIGN_CENTER];
1220 [gui setKey:@"_INSTALLED" forRow:OXZ_GUI_ROW_INSTALLED];
1221 [gui setText:DESC(@"oolite-oxzmanager-remove") forRow:OXZ_GUI_ROW_REMOVE align:GUI_ALIGN_CENTER];
1222 [gui setKey:@"_REMOVE" forRow:OXZ_GUI_ROW_REMOVE];
1223 [gui setText:DESC(@"oolite-oxzmanager-update-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
1224 [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
1225 [gui setText:DESC(@"oolite-oxzmanager-update-all") forRow:OXZ_GUI_ROW_UPDATE_ALL align:GUI_ALIGN_CENTER];
1226 [gui setKey:@"_UPDATE_ALL" forRow:OXZ_GUI_ROW_UPDATE_ALL];
1227
1228 startRow = OXZ_GUI_ROW_INSTALL;
1229 break;
1230 case OXZ_STATE_UPDATING:
1232 [gui setTitle:DESC(@"oolite-oxzmanager-title-downloading")];
1233
1234 if (_downloadStatus == OXZ_DOWNLOAD_ERROR)
1235 {
1236 [gui addLongText:OOExpandKey(@"oolite-oxzmanager-progress-error") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
1237 }
1238 else
1239 {
1240 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-progress-@-is-@-of-@"),_currentDownloadName,[self humanSize:_downloadProgress],[self humanSize:_downloadExpected]] startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
1241 }
1242 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+2 align:GUI_ALIGN_LEFT];
1243
1244 [gui setText:DESC(@"oolite-oxzmanager-cancel") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
1245 [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
1246 startRow = OXZ_GUI_ROW_UPDATE;
1247 break;
1249 [gui setTitle:DESC(@"oolite-oxzmanager-title-dependencies")];
1250
1251 [gui setText:DESC(@"oolite-oxzmanager-dependencies-decision") forRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
1252
1253 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+2 align:GUI_ALIGN_LEFT];
1254
1255 startRow = OXZ_GUI_ROW_INSTALLED;
1256 [gui setText:DESC(@"oolite-oxzmanager-dependencies-yes-all") forRow:OXZ_GUI_ROW_INSTALLED align:GUI_ALIGN_CENTER];
1257 [gui setKey:@"_PROCEED_ALL" forRow:OXZ_GUI_ROW_INSTALLED];
1258
1259 [gui setText:DESC(@"oolite-oxzmanager-dependencies-yes") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
1260 [gui setKey:@"_PROCEED" forRow:OXZ_GUI_ROW_PROCEED];
1261
1262 [gui setText:DESC(@"oolite-oxzmanager-dependencies-no") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
1263 [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
1264 break;
1265
1266 case OXZ_STATE_REMOVING:
1267 [gui addLongText:DESC(@"oolite-oxzmanager-removal-done") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
1268 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
1269 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
1270 startRow = OXZ_GUI_ROW_UPDATE;
1271 break;
1272 case OXZ_STATE_TASKDONE:
1273 if (_downloadStatus == OXZ_DOWNLOAD_COMPLETE)
1274 {
1275 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-progress-done-%u-%u"),[_oxzList count],[[self managedOXZs] count]] startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
1276 }
1277 else
1278 {
1279 [gui addLongText:OOExpandKey(@"oolite-oxzmanager-progress-error") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
1280 }
1281 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+4 align:GUI_ALIGN_LEFT];
1282
1283 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
1284 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
1285 startRow = OXZ_GUI_ROW_UPDATE;
1286 break;
1287 case OXZ_STATE_EXTRACT:
1288 {
1289 NSDictionary *manifest = [_filteredList oo_dictionaryAtIndex:_item];
1290 NSString *title = [manifest oo_stringForKey:kOOManifestTitle];
1291 NSString *version = [manifest oo_stringForKey:kOOManifestVersion];
1292 NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier];
1293 [gui setTitle:DESC(@"oolite-oxzmanager-title-extract")];
1294 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-title-@-version-@"),
1295 title,
1296 version]
1297 forRow:0 align:GUI_ALIGN_LEFT];
1298 [gui addLongText:DESC(@"oolite-oxzmanager-extract-info") startingAtRow:2 align:GUI_ALIGN_LEFT];
1299#ifdef NDEBUG
1300 [gui addLongText:DESC(@"oolite-oxzmanager-extract-releasebuild") startingAtRow:7 align:GUI_ALIGN_LEFT];
1301 [gui setColor:[OOColor orangeColor] forRow:7];
1302 [gui setColor:[OOColor orangeColor] forRow:8];
1303#endif
1304 NSString *path = [self extractionBasePathForIdentifier:identifier andVersion:version];
1305 if ([[NSFileManager defaultManager] fileExistsAtPath:path])
1306 {
1307 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-extract-@-already-exists"), path]
1308 startingAtRow:10 align:GUI_ALIGN_LEFT];
1309 startRow = OXZ_GUI_ROW_CANCEL;
1310 [gui setText:DESC(@"oolite-oxzmanager-extract-unavailable") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
1311 [gui setColor:[OOColor grayColor] forRow:OXZ_GUI_ROW_PROCEED];
1312 }
1313 else
1314 {
1315 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-extract-to-@"), path]
1316 startingAtRow:10 align:GUI_ALIGN_LEFT];
1317 startRow = OXZ_GUI_ROW_PROCEED;
1318 [gui setText:DESC(@"oolite-oxzmanager-extract-proceed") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
1319 [gui setKey:@"_PROCEED" forRow:OXZ_GUI_ROW_PROCEED];
1320
1321 }
1322 [gui setText:DESC(@"oolite-oxzmanager-extract-cancel") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
1323 [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
1324
1325 }
1326 break;
1328 [gui addLongText:_progressStatus startingAtRow:1 align:GUI_ALIGN_LEFT];
1329 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
1330 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
1331 startRow = OXZ_GUI_ROW_UPDATE;
1332 break;
1333
1334 }
1335
1336 if (_interfaceState == OXZ_STATE_PICK_INSTALL)
1337 {
1338 [gui setTitle:DESC(@"oolite-oxzmanager-title-install")];
1339 [self setFilteredList:[self applyCurrentFilter:_oxzList]];
1340 startRow = [self showInstallOptions];
1341 }
1342 else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
1343 {
1344 [gui setTitle:DESC(@"oolite-oxzmanager-title-installed")];
1345 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
1346 startRow = [self showInstallOptions];
1347 }
1348 else if (_interfaceState == OXZ_STATE_PICK_REMOVE)
1349 {
1350 [gui setTitle:DESC(@"oolite-oxzmanager-title-remove")];
1351 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
1352 startRow = [self showRemoveOptions];
1353 }
1354
1355
1356 if (_changesMade)
1357 {
1358 [gui setText:DESC(@"oolite-oxzmanager-exit-restart") forRow:OXZ_GUI_ROW_EXIT align:GUI_ALIGN_CENTER];
1359 }
1360 else
1361 {
1362 [gui setText:DESC(@"oolite-oxzmanager-exit") forRow:OXZ_GUI_ROW_EXIT align:GUI_ALIGN_CENTER];
1363 }
1364 [gui setKey:@"_EXIT" forRow:OXZ_GUI_ROW_EXIT];
1365 [gui setSelectableRange:NSMakeRange(startRow,2+(OXZ_GUI_ROW_EXIT-startRow))];
1366 if (startRow < OXZ_GUI_ROW_INSTALL)
1367 {
1368 [gui setSelectedRow:OXZ_GUI_ROW_INSTALL];
1369 }
1370 else if (_interfaceState == OXZ_STATE_NODATA)
1371 {
1372 [gui setSelectedRow:OXZ_GUI_ROW_UPDATE];
1373 }
1374 else
1375 {
1376 [gui setSelectedRow:startRow];
1377 }
1378
1379}
1380
1381
1382- (BOOL) isRestarting
1383{
1384 // for the restart
1385 if (EXPECT_NOT(_interfaceState == OXZ_STATE_RESTARTING))
1386 {
1387 // Rebuilds OXP search
1389 [UNIVERSE reinitAndShowDemo:YES];
1390 _changesMade = NO;
1391 _interfaceState = OXZ_STATE_MAIN;
1392 _downloadStatus = OXZ_DOWNLOAD_NONE; // clear error state
1393 return YES;
1394 }
1395 else
1396 {
1397 return NO;
1398 }
1399}
1400
1401
1402- (void) processSelection
1403{
1404 GuiDisplayGen *gui = [UNIVERSE gui];
1405 OOGUIRow selection = [gui selectedRow];
1406
1407 if (selection == OXZ_GUI_ROW_EXIT)
1408 {
1409 [self cancelUpdate]; // doesn't hurt if no update in progress
1410 [_dependencyStack removeAllObjects]; // cleanup
1411 _downloadAllDependencies = NO;
1412 _downloadStatus = OXZ_DOWNLOAD_NONE; // clear error state
1413 if (_changesMade)
1414 {
1415 _interfaceState = OXZ_STATE_RESTARTING;
1416 }
1417 else
1418 {
1419 [PLAYER setGuiToIntroFirstGo:YES];
1420 if (_oxzList != nil)
1421 {
1422 _interfaceState = OXZ_STATE_MAIN;
1423 }
1424 else
1425 {
1426 _interfaceState = OXZ_STATE_NODATA;
1427 }
1428 return;
1429 }
1430 }
1431 else if (selection == OXZ_GUI_ROW_UPDATE) // also == _CANCEL
1432 {
1433 if (_interfaceState == OXZ_STATE_REMOVING)
1434 {
1435 _interfaceState = OXZ_STATE_PICK_REMOVE;
1436 _downloadStatus = OXZ_DOWNLOAD_NONE;
1437 }
1438 else if (_interfaceState == OXZ_STATE_TASKDONE || _interfaceState == OXZ_STATE_DEPENDENCIES)
1439 {
1440 [_dependencyStack removeAllObjects];
1441 _downloadAllDependencies = NO;
1442 _interfaceState = OXZ_STATE_PICK_INSTALL;
1443 _downloadStatus = OXZ_DOWNLOAD_NONE;
1444 }
1445 else if (_interfaceState == OXZ_STATE_EXTRACTDONE)
1446 {
1447 [_dependencyStack removeAllObjects];
1448 _downloadAllDependencies = NO;
1449 _interfaceState = OXZ_STATE_PICK_INSTALLED;
1450 _downloadStatus = OXZ_DOWNLOAD_NONE;
1451 }
1452 else if (_interfaceState == OXZ_STATE_INSTALLING || _interfaceState == OXZ_STATE_UPDATING)
1453 {
1454 [self cancelUpdate]; // sets interface state and download status
1455 }
1456 else if (_interfaceState == OXZ_STATE_EXTRACT)
1457 {
1458 _interfaceState = OXZ_STATE_MAIN;
1459 }
1460 else
1461 {
1462 [self updateManifests];
1463 }
1464 }
1465 else if (selection == OXZ_GUI_ROW_INSTALL)
1466 {
1467 _interfaceState = OXZ_STATE_PICK_INSTALL;
1468 }
1469 else if (selection == OXZ_GUI_ROW_INSTALLED)
1470 {
1471 if (_interfaceState == OXZ_STATE_DEPENDENCIES) // also == _PROCEED_ALL
1472 {
1473 _downloadAllDependencies = YES;
1474 [self installOXZ:_item];
1475 }
1476 else
1477 {
1478 _interfaceState = OXZ_STATE_PICK_INSTALLED;
1479 }
1480 }
1481 else if (selection == OXZ_GUI_ROW_REMOVE) // also == _PROCEED
1482 {
1483 if (_interfaceState == OXZ_STATE_DEPENDENCIES)
1484 {
1485 [self installOXZ:_item];
1486 }
1487 else if (_interfaceState == OXZ_STATE_NODATA)
1488 {
1489 _interfaceState = OXZ_STATE_MAIN;
1490 }
1491 else if (_interfaceState == OXZ_STATE_EXTRACT)
1492 {
1493 [self setProgressStatus:[self extractOXZ:_item]];
1494 _interfaceState = OXZ_STATE_EXTRACTDONE;
1495 }
1496 else
1497 {
1498 _interfaceState = OXZ_STATE_PICK_REMOVE;
1499 }
1500 }
1501 else if (selection == OXZ_GUI_ROW_UPDATE_ALL)
1502 {
1503 OOLog(kOOOXZDebugLog, @"%@", @"Trying to update all managed OXPs");
1504 [self updateAllOXZ];
1505 }
1506 else if (selection == OXZ_GUI_ROW_LISTPREV)
1507 {
1508 [self processOptionsPrev];
1509 return;
1510 }
1511 else if (selection == OXZ_GUI_ROW_LISTNEXT)
1512 {
1513 [self processOptionsNext];
1514 return;
1515 }
1516 else
1517 {
1518 NSUInteger item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
1519 if (_interfaceState == OXZ_STATE_PICK_REMOVE)
1520 {
1521 [self removeOXZ:item];
1522 }
1523 else if (_interfaceState == OXZ_STATE_PICK_INSTALL)
1524 {
1525 OOLog(kOOOXZDebugLog, @"Trying to install index %lu", (unsigned long)item);
1526 [self installOXZ:item];
1527 }
1528 else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
1529 {
1530 OOLog(kOOOXZDebugLog, @"Trying to install index %lu", (unsigned long)item);
1531 [self installOXZ:item];
1532 }
1533
1534 }
1535
1536 [self gui]; // update GUI
1537}
1538
1539
1540- (BOOL) isAcceptingTextInput
1541{
1542 return (_interfaceState == OXZ_STATE_SETFILTER);
1543}
1544
1545
1546- (BOOL) isAcceptingGUIInput
1547{
1548 return !_interfaceShowingOXZDetail;
1549}
1550
1551
1552- (void) processTextInput:(NSString *)input
1553{
1554 if ([self validateFilter:input])
1555 {
1556 if ([input length] > 0)
1557 {
1558 [self setFilter:input];
1559 } // else keep previous filter
1560 _interfaceState = OXZ_STATE_PICK_INSTALL;
1561 [self gui];
1562 }
1563 // else nothing
1564}
1565
1566
1567- (void) refreshTextInput:(NSString *)input
1568{
1569 GuiDisplayGen *gui = [UNIVERSE gui];
1570 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-text-prompt-@"), input] forRow:OXZ_GUI_ROW_INPUT align:GUI_ALIGN_LEFT];
1571 if ([self validateFilter:input])
1572 {
1573 [gui setColor:[OOColor cyanColor] forRow:OXZ_GUI_ROW_INPUT];
1574 }
1575 else
1576 {
1577 [gui setColor:[OOColor orangeColor] forRow:OXZ_GUI_ROW_INPUT];
1578 }
1579}
1580
1581
1582- (void) processFilterKey
1583{
1584 if (_interfaceShowingOXZDetail)
1585 {
1586 _interfaceShowingOXZDetail = NO;
1587 }
1588 if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_MAIN)
1589 {
1590 _interfaceState = OXZ_STATE_SETFILTER;
1591 [[UNIVERSE gameView] resetTypedString];
1592 [self gui];
1593 }
1594 // else this key does nothing
1595}
1596
1597
1598- (void) processShowInfoKey
1599{
1600 if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE)
1601 {
1602 GuiDisplayGen *gui = [UNIVERSE gui];
1603
1604 if (_interfaceShowingOXZDetail)
1605 {
1606 _interfaceShowingOXZDetail = NO;
1607 [self gui]; // restore screen
1608 // reset list selection position
1609 [gui setSelectedRow:(_item - _offset + OXZ_GUI_ROW_LISTSTART)];
1610 // and do the GUI again with the correct positions
1611 [self showOptionsUpdate]; // restore screen
1612 }
1613 else
1614 {
1615 OOGUIRow selection = [gui selectedRow];
1616
1617 if (selection < OXZ_GUI_ROW_LISTSTART || selection >= OXZ_GUI_ROW_LISTSTART + OXZ_GUI_NUM_LISTROWS)
1618 {
1619 // not on an OXZ
1620 return;
1621 }
1622
1623
1624 _item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
1625
1626 NSDictionary *manifest = [_filteredList oo_dictionaryAtIndex:_item];
1627 _interfaceShowingOXZDetail = YES;
1628
1629 [gui clearAndKeepBackground:YES];
1630 [gui setTitle:DESC(@"oolite-oxzmanager-title-infopage")];
1631
1632// title, version
1633 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-title-@-version-@"),
1634 [manifest oo_stringForKey:kOOManifestTitle],
1635 [manifest oo_stringForKey:kOOManifestVersion]]
1636 forRow:0 align:GUI_ALIGN_LEFT];
1637
1638// author
1639 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-author-@"),
1640 [manifest oo_stringForKey:kOOManifestAuthor]]
1641 forRow:1 align:GUI_ALIGN_LEFT];
1642
1643// license
1644 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-license-@"),
1645 [manifest oo_stringForKey:kOOManifestLicense]]
1646 startingAtRow:2 align:GUI_ALIGN_LEFT];
1647// tags
1648
1649 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-tags-@"),[[manifest oo_arrayForKey:kOOManifestTags] componentsJoinedByString: @", "]]
1650 startingAtRow:4 align:GUI_ALIGN_LEFT];
1651// description
1652 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-description-@"),[manifest oo_stringForKey:kOOManifestDescription]]
1653 startingAtRow:7 align:GUI_ALIGN_LEFT];
1654
1655// infoURL
1656 NSString *infoURLString = [manifest oo_stringForKey:kOOManifestInformationURL];
1657 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-infourl-@"),
1658 infoURLString]
1659 forRow:25 align:GUI_ALIGN_LEFT];
1660 // copy url info text to clipboard automatically once we are in the oxz info page
1661 [[UNIVERSE gameView] stringToClipboard:infoURLString];
1662
1663// instructions
1664 [gui setText:OOExpand(DESC(@"oolite-oxzmanager-infopage-return")) forRow:27 align:GUI_ALIGN_CENTER];
1665 [gui setColor:[OOColor greenColor] forRow:27];
1666
1667 }
1668 }
1669}
1670
1671
1672- (void) processExtractKey
1673{
1674 // TODO: Extraction functionality - converts an installed OXZ to
1675 // an OXP in the main AddOns folder if it's safe to do so.
1676 if (!_interfaceShowingOXZDetail && (_interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE))
1677 {
1678 GuiDisplayGen *gui = [UNIVERSE gui];
1679 OOGUIRow selection = [gui selectedRow];
1680
1681 if (selection < OXZ_GUI_ROW_LISTSTART || selection >= OXZ_GUI_ROW_LISTSTART + OXZ_GUI_NUM_LISTROWS)
1682 {
1683 // not on an OXZ
1684 return;
1685 }
1686
1687 _item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
1688 _interfaceState = OXZ_STATE_EXTRACT;
1689 [self gui];
1690 }
1691}
1692
1693
1694- (BOOL) installOXZ:(NSUInteger)item
1695{
1696 NSArray *picklist = _filteredList;
1697
1698 if ([picklist count] <= item)
1699 {
1700 return NO;
1701 }
1702 NSDictionary *manifest = [picklist objectAtIndex:item];
1703 _item = item;
1704
1705 if ([self installableState:manifest] >= OXZ_UNINSTALLABLE_ALREADY)
1706 {
1707 OOLog(kOOOXZDebugLog,@"Cannot install %@",manifest);
1708 // can't be installed on this version of Oolite, or already is installed
1709 return NO;
1710 }
1711 NSString *url = [manifest objectForKey:kOOManifestDownloadURL];
1712 if (url == nil)
1713 {
1714 OOLog(kOOOXZErrorLog, @"%@", @"Manifest does not have a download URL - cannot install");
1715 return NO;
1716 }
1717 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
1718 if (_downloadStatus != OXZ_DOWNLOAD_NONE)
1719 {
1720 return NO;
1721 }
1722 _downloadStatus = OXZ_DOWNLOAD_STARTED;
1723 _interfaceState = OXZ_STATE_INSTALLING;
1724
1725 [self setProgressStatus:@""];
1726 return [self beginDownload:request];
1727}
1728
1729
1730- (BOOL) updateAllOXZ
1731{
1732 [_dependencyStack removeAllObjects];
1733 _downloadAllDependencies = YES;
1734 [self setFilteredList:_oxzList];
1735 NSDictionary *manifest = nil;
1736
1737 foreach (manifest,_oxzList)
1738 {
1739 if ([self installableState:manifest] == OXZ_INSTALLABLE_UPDATE)
1740 {
1741 OOLog(kOOOXZDebugLog, @"Queuing in for update: %@", manifest);
1742 [_dependencyStack addObject:manifest];
1743 }
1744 }
1745 NSDictionary *first = [_dependencyStack anyObject];
1746 NSString* identifier = [first oo_stringForKey:kOOManifestRelationIdentifier];
1747 NSUInteger item = NSUIntegerMax;
1748 NSDictionary *availableDownload = nil;
1749 foreach (availableDownload, _oxzList)
1750 {
1751 if ([[availableDownload oo_stringForKey:kOOManifestIdentifier] isEqualToString:identifier])
1752 {
1753 item = [_oxzList indexOfObject:availableDownload];
1754 break;
1755 }
1756 }
1757 return [self installOXZ:item];
1758}
1759
1760
1761- (NSArray *) installOptions
1762{
1763 NSUInteger start = _offset;
1764 if (start >= [_filteredList count])
1765 {
1766 start = 0;
1767 _offset = 0;
1768 }
1769 NSUInteger end = start + OXZ_GUI_NUM_LISTROWS;
1770 if (end > [_filteredList count])
1771 {
1772 end = [_filteredList count];
1773 }
1774 return [_filteredList subarrayWithRange:NSMakeRange(start,end-start)];
1775}
1776
1777
1778- (OOGUIRow) showInstallOptions
1779{
1780 // shows the current installation options page
1781 OOGUIRow startRow = OXZ_GUI_ROW_LISTPREV;
1782 NSArray *options = [self installOptions];
1783 NSUInteger optCount = [_filteredList count];
1784 GuiDisplayGen *gui = [UNIVERSE gui];
1785 OOGUITabSettings tab_stops;
1786 tab_stops[0] = 0;
1787 tab_stops[1] = 100;
1788 tab_stops[2] = 320;
1789 tab_stops[3] = 400;
1790 [gui setTabStops:tab_stops];
1791
1792
1793 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-heading-category"),
1794 DESC(@"oolite-oxzmanager-heading-title"),
1795 DESC(@"oolite-oxzmanager-heading-installed"),
1796 DESC(@"oolite-oxzmanager-heading-downloadable"),
1797 nil] forRow:OXZ_GUI_ROW_LISTHEAD];
1798
1799 if (_offset > 0)
1800 {
1801 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTPREV];
1802 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"",@"",@" <-- ", nil] forRow:OXZ_GUI_ROW_LISTPREV];
1803 [gui setKey:@"_BACK" forRow:OXZ_GUI_ROW_LISTPREV];
1804 }
1805 else
1806 {
1807 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
1808 {
1809 [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
1810 }
1811 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTPREV align:GUI_ALIGN_LEFT];
1812 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTPREV];
1813 }
1814 if (_offset + 10 < optCount)
1815 {
1816 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTNEXT];
1817 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"",@"",@" --> ", nil] forRow:OXZ_GUI_ROW_LISTNEXT];
1818 [gui setKey:@"_NEXT" forRow:OXZ_GUI_ROW_LISTNEXT];
1819 }
1820 else
1821 {
1822 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
1823 {
1824 [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
1825 }
1826 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTNEXT align:GUI_ALIGN_LEFT];
1827 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTNEXT];
1828 }
1829
1830 // clear any previous longtext
1831 for (NSUInteger i = OXZ_GUI_ROW_LISTSTATUS; i < OXZ_GUI_ROW_INSTALL-1; i++)
1832 {
1833 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
1834 [gui setKey:GUI_KEY_SKIP forRow:i];
1835 }
1836 // and any previous listed entries
1837 for (NSUInteger i = OXZ_GUI_ROW_LISTSTART; i < OXZ_GUI_ROW_LISTNEXT; i++)
1838 {
1839 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
1840 [gui setKey:GUI_KEY_SKIP forRow:i];
1841 }
1842
1844 NSDictionary *manifest = nil;
1845 BOOL oxzLineSelected = NO;
1846
1847 foreach (manifest, options)
1848 {
1849 NSDictionary *installed = [ResourceManager manifestForIdentifier:[manifest oo_stringForKey:kOOManifestIdentifier]];
1850 NSString *localPath = [[[self installPath] stringByAppendingPathComponent:[manifest oo_stringForKey:kOOManifestIdentifier]] stringByAppendingPathExtension:@"oxz"];
1851 if (installed == nil)
1852 {
1853 // check that there's not one just been downloaded
1854 installed = OODictionaryFromFile([localPath stringByAppendingPathComponent:@"manifest.plist"]);
1855 }
1856 else
1857 {
1858 // check for a more recent download
1859 if ([[NSFileManager defaultManager] fileExistsAtPath:localPath])
1860 {
1861
1862 installed = OODictionaryFromFile([localPath stringByAppendingPathComponent:@"manifest.plist"]);
1863 }
1864 else
1865 {
1866 // check if this was a managed OXZ which has been deleted
1867 if ([[installed oo_stringForKey:kOOManifestFilePath] hasPrefix:[self installPath]])
1868 {
1869 installed = nil;
1870 }
1871 }
1872 }
1873
1874 NSString *installedVersion = DESC(@"oolite-oxzmanager-version-none");
1875 if (installed != nil)
1876 {
1877 installedVersion = [installed oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-version-none")];
1878 }
1879
1880 /* If the filter is in use, the available_version key will
1881 * contain the version which can be downloaded. */
1882 [gui setArray:[NSArray arrayWithObjects:
1883 [manifest oo_stringForKey:kOOManifestCategory defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
1884 [manifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
1885 installedVersion,
1886 [manifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[manifest oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-version-none")]],
1887 nil] forRow:row];
1888
1889 [gui setKey:[manifest oo_stringForKey:kOOManifestIdentifier] forRow:row];
1890 /* yellow for installable, orange for dependency issues, grey and unselectable for version issues, white and unselectable for already installed (manually or otherwise) at the current version, red and unselectable for already installed manually at a different version. */
1891 [gui setColor:[self colorForManifest:manifest] forRow:row];
1892
1893 if (row == [gui selectedRow])
1894 {
1895 oxzLineSelected = YES;
1896
1897 [gui setText:[self installStatusForManifest:manifest] forRow:OXZ_GUI_ROW_LISTSTATUS];
1898 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTSTATUS];
1899
1900 [gui addLongText:[[[manifest oo_stringForKey:kOOManifestDescription] componentsSeparatedByString:@"\n"] oo_stringAtIndex:0] startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
1901
1902 NSString *infoUrl = [manifest oo_stringForKey:kOOManifestInformationURL];
1903 if (infoUrl != nil)
1904 {
1905 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-url"),infoUrl,nil] forRow:OXZ_GUI_ROW_LISTINFO1];
1906 }
1907 NSUInteger size = [manifest oo_unsignedIntForKey:kOOManifestFileSize defaultValue:0];
1908 NSString *updatedDesc = nil;
1909
1910 NSUInteger timestamp = [manifest oo_unsignedIntegerForKey:kOOManifestUploadDate defaultValue:0];
1911 if (timestamp > 0)
1912 {
1913 // list of installable OXZs
1914 NSDate *updated = [NSDate dateWithTimeIntervalSince1970:timestamp];
1915
1916 //keep only the first part of the date string description, which should be in YYYY-MM-DD format
1917 updatedDesc = [[[updated description] componentsSeparatedByString:@" "] oo_stringAtIndex:0];
1918
1919 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-size"),[self humanSize:size],DESC(@"oolite-oxzmanager-infoline-date"),updatedDesc,nil] forRow:OXZ_GUI_ROW_LISTINFO2];
1920 }
1921 else if (size > 0)
1922 {
1923 // list of installed/removable OXZs
1924 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-size"),[self humanSize:size],nil] forRow:OXZ_GUI_ROW_LISTINFO2];
1925 }
1926
1927
1928 }
1929
1930
1931 row++;
1932 }
1933
1934 if (!oxzLineSelected)
1935 {
1936 if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
1937 {
1938 // installeD
1939 [gui addLongText:OOExpand(DESC(@"oolite-oxzmanager-installed-nonepicked")) startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
1940 }
1941 else
1942 {
1943 // installeR
1944 [gui addLongText:OOExpand(DESC(@"oolite-oxzmanager-installer-nonepicked")) startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
1945 }
1946
1947 }
1948
1949
1950 return startRow;
1951}
1952
1953
1954- (BOOL) removeOXZ:(NSUInteger)item
1955{
1956 NSArray *remList = _filteredList;
1957 if ([remList count] <= item)
1958 {
1959 OOLog(kOOOXZDebugLog, @"Unable to remove item %lu as only %lu in list", (unsigned long)item, (unsigned long)[remList count]);
1960 return NO;
1961 }
1962 NSString *filename = [[remList objectAtIndex:item] oo_stringForKey:kOOManifestFilePath];
1963 if (filename == nil)
1964 {
1965 OOLog(kOOOXZDebugLog, @"Unable to remove item %lu as filename not found", (unsigned long)item);
1966 return NO;
1967 }
1968
1969 if (![[NSFileManager defaultManager] oo_removeItemAtPath:filename])
1970 {
1971 OOLog(kOOOXZErrorLog, @"Unable to remove file %@", filename);
1972 return NO;
1973 }
1974 _changesMade = YES;
1975 DESTROY(_managedList); // will need updating
1976 _interfaceState = OXZ_STATE_REMOVING;
1977 [self gui];
1978 return YES;
1979}
1980
1981
1982- (NSArray *) removeOptions
1983{
1984 NSArray *remList = _filteredList;
1985 if ([remList count] == 0)
1986 {
1987 return nil;
1988 }
1989 NSUInteger start = _offset;
1990 if (start >= [remList count])
1991 {
1992 start = 0;
1993 _offset = 0;
1994 }
1995 NSUInteger end = start + OXZ_GUI_NUM_LISTROWS;
1996 if (end > [remList count])
1997 {
1998 end = [remList count];
1999 }
2000 return [remList subarrayWithRange:NSMakeRange(start,end-start)];
2001}
2002
2003
2004- (OOGUIRow) showRemoveOptions
2005{
2006 // shows the current installation options page
2007 OOGUIRow startRow = OXZ_GUI_ROW_LISTPREV;
2008 NSArray *options = [self removeOptions];
2009 GuiDisplayGen *gui = [UNIVERSE gui];
2010 if (options == nil)
2011 {
2012 [gui addLongText:DESC(@"oolite-oxzmanager-nothing-removable") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
2013 return startRow;
2014 }
2015
2016 OOGUITabSettings tab_stops;
2017 tab_stops[0] = 0;
2018 tab_stops[1] = 100;
2019 tab_stops[2] = 400;
2020 [gui setTabStops:tab_stops];
2021
2022 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-heading-category"),
2023 DESC(@"oolite-oxzmanager-heading-title"),
2024 DESC(@"oolite-oxzmanager-heading-version"),
2025 nil] forRow:OXZ_GUI_ROW_LISTHEAD];
2026 if (_offset > 0)
2027 {
2028 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTPREV];
2029 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"",@" <-- ", nil] forRow:OXZ_GUI_ROW_LISTPREV];
2030 [gui setKey:@"_BACK" forRow:OXZ_GUI_ROW_LISTPREV];
2031 }
2032 else
2033 {
2034 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
2035 {
2036 [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
2037 }
2038 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTPREV align:GUI_ALIGN_LEFT];
2039 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTPREV];
2040 }
2041 if (_offset + OXZ_GUI_NUM_LISTROWS < [[self managedOXZs] count])
2042 {
2043 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTNEXT];
2044 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"",@" --> ", nil] forRow:OXZ_GUI_ROW_LISTNEXT];
2045 [gui setKey:@"_NEXT" forRow:OXZ_GUI_ROW_LISTNEXT];
2046 }
2047 else
2048 {
2049 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
2050 {
2051 [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
2052 }
2053 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTNEXT align:GUI_ALIGN_LEFT];
2054 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTNEXT];
2055 }
2056
2057 // clear any previous longtext
2058 for (NSUInteger i = OXZ_GUI_ROW_LISTDESC; i < OXZ_GUI_ROW_INSTALL-1; i++)
2059 {
2060 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
2061 [gui setKey:GUI_KEY_SKIP forRow:i];
2062 }
2063 // and any previous listed entries
2064 for (NSUInteger i = OXZ_GUI_ROW_LISTSTART; i < OXZ_GUI_ROW_LISTNEXT; i++)
2065 {
2066 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
2067 [gui setKey:GUI_KEY_SKIP forRow:i];
2068 }
2069
2070
2072 NSDictionary *manifest = nil;
2073 BOOL oxzSelected = NO;
2074
2075 foreach (manifest, options)
2076 {
2077
2078 [gui setArray:[NSArray arrayWithObjects:
2079 [manifest oo_stringForKey:kOOManifestCategory defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
2080 [manifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
2081 [manifest oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
2082 nil] forRow:row];
2083 NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier];
2084 [gui setKey:identifier forRow:row];
2085
2086 [gui setColor:[self colorForManifest:manifest] forRow:row];
2087
2088 if (row == [gui selectedRow])
2089 {
2090 [gui setText:[self installStatusForManifest:manifest] forRow:OXZ_GUI_ROW_LISTSTATUS];
2091 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTSTATUS];
2092
2093 [gui addLongText:[[[manifest oo_stringForKey:kOOManifestDescription] componentsSeparatedByString:@"\n"] oo_stringAtIndex:0] startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
2094
2095 oxzSelected = YES;
2096 }
2097 row++;
2098 }
2099
2100 if (!oxzSelected)
2101 {
2102 [gui addLongText:DESC(@"oolite-oxzmanager-remover-nonepicked") startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
2103 }
2104
2105 return startRow;
2106}
2107
2108
2109- (void) showOptionsUpdate
2110{
2111
2112 if (_interfaceState == OXZ_STATE_PICK_INSTALL)
2113 {
2114 [self setFilteredList:[self applyCurrentFilter:_oxzList]];
2115 [self showInstallOptions];
2116 }
2117 else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
2118 {
2119 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
2120 [self showInstallOptions];
2121 }
2122 else if (_interfaceState == OXZ_STATE_PICK_REMOVE)
2123 {
2124 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
2125 [self showRemoveOptions];
2126 }
2127 // else nothing necessary
2128}
2129
2130
2131- (void) showOptionsPrev
2132{
2133 GuiDisplayGen *gui = [UNIVERSE gui];
2134 if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_PICK_INSTALLED)
2135 {
2136 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
2137 {
2138 [self processSelection];
2139 }
2140 }
2141}
2142
2143
2144- (void) processOptionsPrev
2145{
2146 if (_offset < OXZ_GUI_NUM_LISTROWS)
2147 {
2148 _offset = 0;
2149 }
2150 else
2151 {
2152 _offset -= OXZ_GUI_NUM_LISTROWS;
2153 }
2154 [self showOptionsUpdate];
2155}
2156
2157
2158- (void) processOptionsNext
2159{
2160 if (_offset + OXZ_GUI_NUM_LISTROWS < [_filteredList count])
2161 {
2162 _offset += OXZ_GUI_NUM_LISTROWS;
2163 }
2164 [self showOptionsUpdate];
2165 return;
2166}
2167
2168
2169- (void) showOptionsNext
2170{
2171 GuiDisplayGen *gui = [UNIVERSE gui];
2172 if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_PICK_INSTALLED)
2173 {
2174 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
2175 {
2176 [self processSelection];
2177 }
2178 }
2179}
2180
2181
2182- (NSString *) extractOXZ:(NSUInteger)item
2183{
2184 NSFileManager *fmgr = [NSFileManager defaultManager];
2185 NSMutableString *extractionLog = [[NSMutableString alloc] init];
2186 NSDictionary *manifest = [_filteredList oo_dictionaryAtIndex:item];
2187 NSString *version = [manifest oo_stringForKey:kOOManifestVersion];
2188 NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier];
2189 NSString *path = [self extractionBasePathForIdentifier:identifier andVersion:version];
2190
2191 // OXZ errors should really never happen unless someone is messing
2192 // directly with the managed folder while Oolite is running, but
2193 // it's possible.
2194
2195 NSString *oxzfile = [manifest oo_stringForKey:kOOManifestFilePath];
2196 if (![fmgr fileExistsAtPath:oxzfile])
2197 {
2198 OOLog(kOOOXZErrorLog,@"OXZ %@ could not be found",oxzfile);
2199 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-no-original")];
2200 return [extractionLog autorelease];
2201 }
2202 const char* zipname = [oxzfile UTF8String];
2203 unzFile uf = NULL;
2204 uf = unzOpen64(zipname);
2205 if (uf == NULL)
2206 {
2207 OOLog(kOOOXZErrorLog,@"Could not open .oxz at %@ as zip file",path);
2208 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-bad-original")];
2209 return [extractionLog autorelease];
2210 }
2211
2212 if ([fmgr fileExistsAtPath:path])
2213 {
2214 OOLog(kOOOXZErrorLog,@"Path %@ already exists",path);
2215 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-exists")];
2216 unzClose(uf);
2217 return [extractionLog autorelease];
2218 }
2219 if (![fmgr oo_createDirectoryAtPath:path attributes:nil])
2220 {
2221 OOLog(kOOOXZErrorLog,@"Path %@ could not be created",path);
2222 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-unmakeable")];
2223 unzClose(uf);
2224 return [extractionLog autorelease];
2225 }
2226 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-created")];
2227 NSUInteger counter = 0;
2228 char rawComponentName[512];
2229 BOOL error = NO;
2230 unz_file_info64 file_info = {0};
2231 if (unzGoToFirstFile(uf) == UNZ_OK)
2232 {
2233 do
2234 {
2235 unzGetCurrentFileInfo64(uf, &file_info,
2236 rawComponentName, 512,
2237 NULL, 0,
2238 NULL, 0);
2239 NSString *componentName = [NSString stringWithUTF8String:rawComponentName];
2240 if ([componentName hasSuffix:@"/"])
2241 {
2242 // folder
2243 if (![fmgr oo_createDirectoryAtPath:[path stringByAppendingPathComponent:componentName] attributes:nil])
2244 {
2245 OOLog(kOOOXZErrorLog,@"Subpath %@ could not be created",componentName);
2246 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
2247 error = YES;
2248 break;
2249 }
2250 else
2251 {
2252 OOLog(kOOOXZDebugLog,@"Subpath %@ created OK",componentName);
2253 }
2254 }
2255 else
2256 {
2257 // file
2258 // usually folder must now exist, but just in case...
2259 NSString *folder = [[path stringByAppendingPathComponent:componentName] stringByDeletingLastPathComponent];
2260 if ([folder length] > 0 && ![fmgr fileExistsAtPath:folder] && ![fmgr oo_createDirectoryAtPath:folder attributes:nil])
2261 {
2262 OOLog(kOOOXZErrorLog,@"Subpath %@ could not be created",folder);
2263 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
2264 error = YES;
2265 break;
2266 }
2267
2268
2269 // This is less efficient in memory use than just
2270 // streaming out of the ZIP file onto disk
2271 // but it makes error handling easier
2272 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2273 NSData *tmp = [NSData oo_dataWithOXZFile:[oxzfile stringByAppendingPathComponent:componentName]];
2274 if (tmp == nil)
2275 {
2276 OOLog(kOOOXZErrorLog,@"Sub file %@ could not be extracted from the OXZ",componentName);
2277 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
2278 error = YES;
2279 [pool release];
2280 break;
2281 }
2282 else
2283 {
2284 if (![tmp writeToFile:[path stringByAppendingPathComponent:componentName] atomically:YES])
2285 {
2286 OOLog(kOOOXZErrorLog,@"Sub file %@ could not be created",componentName);
2287 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
2288 error = YES;
2289 [pool release];
2290 break;
2291 }
2292 else
2293 {
2294 ++counter;
2295 }
2296 }
2297 [pool release];
2298
2299 }
2300 }
2301 while (unzGoToNextFile(uf) == UNZ_OK);
2302 }
2303 unzClose(uf);
2304
2305 if (!error)
2306 {
2307 [extractionLog appendFormat:DESC(@"oolite-oxzmanager-extract-log-num-u-extracted"),counter];
2308 [extractionLog appendFormat:DESC(@"oolite-oxzmanager-extract-log-extracted-to-@"),path];
2309 }
2310
2311 return [extractionLog autorelease];
2312}
2313
2314
2315
2316
2317- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
2318{
2319 _downloadStatus = OXZ_DOWNLOAD_RECEIVING;
2320 OOLog(kOOOXZDebugLog, @"%@", @"Download receiving");
2321 _downloadExpected = [response expectedContentLength];
2322 _downloadProgress = 0;
2323 DESTROY(_fileWriter);
2324 [[NSFileManager defaultManager] createFileAtPath:[self downloadPath] contents:nil attributes:nil];
2325 _fileWriter = [[NSFileHandle fileHandleForWritingAtPath:[self downloadPath]] retain];
2326 if (_fileWriter == nil)
2327 {
2328 // file system is full or read-only or something
2329 OOLog(kOOOXZErrorLog, @"%@", @"Unable to create download file");
2330 [self cancelUpdate];
2331 }
2332}
2333
2334
2335- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
2336{
2337 OOLog(kOOOXZDebugLog,@"Downloaded %llu bytes",[data length]);
2338 [_fileWriter seekToEndOfFile];
2339 [_fileWriter writeData:data];
2340 _downloadProgress += [data length];
2341 [self gui]; // update GUI
2342#if OOLITE_WINDOWS
2343 /* Irritating fix to issue https://github.com/OoliteProject/oolite/issues/95
2344 *
2345 * The problem is that on MINGW, GNUStep makes all socket streams
2346 * blocking, which causes problems with the run loop. Calling this
2347 * method of the run loop forces it to execute all already
2348 * scheduled items with a time in the past, before any more items
2349 * are placed on it, which means that the main game update gets a
2350 * chance to run.
2351 *
2352 * This stops the interface freezing - and Oolite appearing to
2353 * have stopped responding to the OS - when downloading large
2354 * (>20Mb) OXZ files.
2355 *
2356 * CIM 6 July 2014
2357 */
2358 [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
2359#endif
2360}
2361
2362
2363- (void)connectionDidFinishLoading:(NSURLConnection *)connection
2364{
2365 _downloadStatus = OXZ_DOWNLOAD_COMPLETE;
2366 OOLog(kOOOXZDebugLog, @"%@", @"Download complete");
2367 [_fileWriter synchronizeFile];
2368 [_fileWriter closeFile];
2369 DESTROY(_fileWriter);
2370 DESTROY(_currentDownload);
2371 if (_interfaceState == OXZ_STATE_UPDATING)
2372 {
2373 if (![self processDownloadedManifests])
2374 {
2375 _downloadStatus = OXZ_DOWNLOAD_ERROR;
2376 }
2377 }
2378 else if (_interfaceState == OXZ_STATE_INSTALLING)
2379 {
2380 if (![self processDownloadedOXZ])
2381 {
2382 _downloadStatus = OXZ_DOWNLOAD_ERROR;
2383 }
2384 }
2385 else
2386 {
2387 OOLog(kOOOXZErrorLog,@"Error: download completed in unexpected state %d. This is an internal error - please report it.",_interfaceState);
2388 _downloadStatus = OXZ_DOWNLOAD_ERROR;
2389 }
2390}
2391
2392
2393- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
2394{
2395 _downloadStatus = OXZ_DOWNLOAD_ERROR;
2396 OOLog(kOOOXZErrorLog,@"Error downloading file: %@",[error description]);
2397 [_fileWriter closeFile];
2398 DESTROY(_fileWriter);
2399 DESTROY(_currentDownload);
2400}
2401
2402
2403
2404
2405@end
2406
2407/* Sort by category, then title, then version - and that should be unique */
2408NSComparisonResult oxzSort(id m1, id m2, void *context)
2409{
2410 NSComparisonResult result = [[m1 oo_stringForKey:kOOManifestCategory defaultValue:@"zz"] localizedCompare:[m2 oo_stringForKey:kOOManifestCategory defaultValue:@"zz"]];
2411 if (result == NSOrderedSame)
2412 {
2413 result = [[m1 oo_stringForKey:kOOManifestTitle defaultValue:@"zz"] localizedCompare:[m2 oo_stringForKey:kOOManifestTitle defaultValue:@"zz"]];
2414 if (result == NSOrderedSame)
2415 {
2416 result = [[m2 oo_stringForKey:kOOManifestVersion defaultValue:@"0"] localizedCompare:[m1 oo_stringForKey:kOOManifestVersion defaultValue:@"0"]];
2417 }
2418 }
2419 return result;
2420}
OOGUITabStop OOGUITabSettings[GUI_MAX_COLUMNS]
NSInteger OOGUIRow
#define DESTROY(x)
Definition OOCocoa.h:75
static OODebugMonitor * sSingleton
#define EXPECT_NOT(x)
#define OOLog(class, format,...)
Definition OOLogging.h:88
static NSString *const kOOManifestAvailableVersion
static NSString *const kOOManifestRelationIdentifier
static NSString *const kOOManifestIdentifier
static NSString *const kOOManifestVersion
static NSString *const kOOManifestFilePath
@ OXZ_STATE_UPDATING
@ OXZ_STATE_INSTALLING
@ OXZ_STATE_PICK_INSTALLED
@ OXZ_STATE_RESTARTING
@ OXZ_STATE_SETFILTER
@ OXZ_STATE_NODATA
@ OXZ_STATE_TASKDONE
@ OXZ_STATE_DEPENDENCIES
@ OXZ_STATE_REMOVING
@ OXZ_STATE_PICK_INSTALL
@ OXZ_STATE_PICK_REMOVE
@ OXZ_STATE_EXTRACTDONE
@ OXZ_STATE_MAIN
@ OXZ_STATE_EXTRACT
@ OXZ_DOWNLOAD_RECEIVING
@ OXZ_DOWNLOAD_COMPLETE
@ OXZ_DOWNLOAD_STARTED
@ OXZ_DOWNLOAD_NONE
@ OXZ_DOWNLOAD_ERROR
static NSString *const kOOOXZFilterDays
static NSString *const kOOOXZErrorLog
static NSString *const kOOOXZFilterTag
static NSString *const kOOOXZFilterKeyword
OXZInstallableState
@ OXZ_INSTALLABLE_UPDATE
@ OXZ_INSTALLABLE_DEPENDENCIES
@ OXZ_INSTALLABLE_CONFLICTS
@ OXZ_INSTALLABLE_OKAY
@ OXZ_UNINSTALLABLE_VERSION
@ OXZ_UNINSTALLABLE_ALREADY
@ OXZ_UNINSTALLABLE_NOREMOTE
@ OXZ_UNINSTALLABLE_MANUAL
static NSString *const kOOOXZManifestCache
static NSString *const kOOOXZFilterUpdates
static NSString *const kOOOXZFilterAll
static NSString *const kOOOXZFilterCategory
static NSString *const kOOOXZFilterInstallable
static NSString *const kOOOXZTmpPlistPath
static NSString *const kOOOXZFilterAuthor
NSComparisonResult oxzSort(id m1, id m2, void *context)
static NSString *const kOOOXZDataURL
static NSString *const kOOOXZTmpPath
static NSString *const kOOOXZDataConfig
static OOOXZManager * sSingleton
static NSString *const kOOOXZDebugLog
@ OXZ_GUI_ROW_CANCEL
@ OXZ_GUI_ROW_PROGRESS
@ OXZ_GUI_ROW_FILTERHELP
@ OXZ_GUI_ROW_FIRSTRUN
@ OXZ_GUI_NUM_LISTROWS
@ OXZ_GUI_ROW_LISTINFO2
@ OXZ_GUI_ROW_REMOVE
@ OXZ_GUI_ROW_LISTFILTER
@ OXZ_GUI_ROW_EXIT
@ OXZ_GUI_ROW_INSTALL
@ OXZ_GUI_ROW_FILTERCURRENT
@ OXZ_GUI_ROW_LISTSTATUS
@ OXZ_GUI_ROW_INSTALLED
@ OXZ_GUI_ROW_LISTINFO1
@ OXZ_GUI_ROW_LISTDESC
@ OXZ_GUI_ROW_INPUT
@ OXZ_GUI_ROW_UPDATE_ALL
@ OXZ_GUI_ROW_PROCEED
@ OXZ_GUI_ROW_LISTHEAD
@ OXZ_GUI_ROW_LISTSTART
@ OXZ_GUI_ROW_LISTPREV
@ OXZ_GUI_ROW_LISTNEXT
@ OXZ_GUI_ROW_UPDATE
NSDictionary * OODictionaryFromFile(NSString *path)
return self
unsigned count
return nil
NSArray * ComponentsFromVersionString(NSString *string)
NSComparisonResult CompareVersions(NSArray *version1, NSArray *version2)
#define DESC(key)
Definition Universe.h:848
BOOL setSelectedRow:(OOGUIRow row)
OOGUIRow addLongText:startingAtRow:align:(NSString *str,[startingAtRow] OOGUIRow row,[align] OOGUIAlignment alignment)
void setText:forRow:(NSString *str,[forRow] OOGUIRow row)
void setText:forRow:align:(NSString *str,[forRow] OOGUIRow row,[align] OOGUIAlignment alignment)
void clearAndKeepBackground:(BOOL keepBackground)
OOGUIRow selectedRow
void setSelectableRange:(NSRange range)
void setColor:forRow:(OOColor *color,[forRow] OOGUIRow row)
void setTitle:(NSString *str)
void setTabStops:(OOGUITabSettings stops)
void setArray:forRow:(NSArray *arr,[forRow] OOGUIRow row)
void setKey:forRow:(NSString *str,[forRow] OOGUIRow row)
NSString * cacheDirectoryPathCreatingIfNecessary:(BOOL create)
OOCacheManager * sharedCache()
OOColor * cyanColor()
Definition OOColor.m:286
OOColor * orangeColor()
Definition OOColor.m:304
OOColor * redColor()
Definition OOColor.m:268
OOColor * greenColor()
Definition OOColor.m:274
OOColor * blueColor()
Definition OOColor.m:280
OOColor * grayColor()
Definition OOColor.m:262
OOColor * whiteColor()
Definition OOColor.m:256
OOColor * brownColor()
Definition OOColor.m:316
OOColor * yellowColor()
Definition OOColor.m:292
void resetManifestKnowledgeForOXZManager()
NSDictionary * manifestForIdentifier:(NSString *identifier)
NSArray * userRootPaths()
voidpf void uLong size
Definition ioapi.h:134
const char * filename
Definition ioapi.h:133
int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)
Definition unzip.c:1130
int ZEXPORT unzGoToFirstFile(unzFile file)
Definition unzip.c:1184
unzFile ZEXPORT unzOpen64(const void *path)
Definition unzip.c:801
int ZEXPORT unzGoToNextFile(unzFile file)
Definition unzip.c:1205
int ZEXPORT unzClose(unzFile file)
Definition unzip.c:811
voidp unzFile
Definition unzip.h:70
#define UNZ_OK
Definition unzip.h:74