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