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