Zip extraction support.
This commit is contained in:
@@ -222,7 +222,7 @@ objectValueForTableColumn:(NSTableColumn *)tableColumn
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
||||||
{
|
{
|
||||||
if ([[tableColumn identifier] isEqualToString:@"mods"])
|
if ([[tableColumn identifier] isEqualToString:@"mods"])
|
||||||
{
|
{
|
||||||
if ([cell isKindOfClass:[ImageAndTextCell class]])
|
if ([cell isKindOfClass:[ImageAndTextCell class]])
|
||||||
|
|||||||
@@ -32,5 +32,5 @@
|
|||||||
- (id)initWithURL:(NSString *)aURL filename:(NSString *)aFilename key:(NSString *)aKey game:(GameInstall *)game;
|
- (id)initWithURL:(NSString *)aURL filename:(NSString *)aFilename key:(NSString *)aKey game:(GameInstall *)game;
|
||||||
- (BOOL)start;
|
- (BOOL)start;
|
||||||
- (BOOL)cancel;
|
- (BOOL)cancel;
|
||||||
|
- (BOOL)extractToPath:(NSString *)aPath;
|
||||||
@end
|
@end
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:filename])
|
if ([[NSFileManager defaultManager] fileExistsAtPath:filename])
|
||||||
{
|
{
|
||||||
status = @"COMPLETE";
|
status = @"DOWNLOADED";
|
||||||
bytesCompleted = bytesTotal = [[[NSFileManager defaultManager] attributesOfItemAtPath:filename error:NULL] fileSize];
|
bytesCompleted = bytesTotal = [[[NSFileManager defaultManager] attributesOfItemAtPath:filename error:NULL] fileSize];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -49,7 +49,35 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)utilityResponded:(NSNotification *)n
|
|
||||||
|
- (BOOL)start
|
||||||
|
{
|
||||||
|
status = @"DOWNLOADING";
|
||||||
|
task = [game runAsyncUtilityWithArg:[NSString stringWithFormat:@"--download-url=%@,%@",url,filename]
|
||||||
|
delegate:self
|
||||||
|
responseSelector:@selector(downloadResponded:)
|
||||||
|
terminatedSelector:@selector(utilityTerminated:)];
|
||||||
|
[task retain];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)cancel
|
||||||
|
{
|
||||||
|
status = @"ERROR";
|
||||||
|
error = @"Download Cancelled";
|
||||||
|
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:filename error:NULL];
|
||||||
|
bytesCompleted = bytesTotal = -1;
|
||||||
|
|
||||||
|
[[JSBridge sharedInstance] notifyDownloadProgress:self];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||||
|
name:NSFileHandleReadCompletionNotification
|
||||||
|
object:[[task standardOutput] fileHandleForReading]];
|
||||||
|
[task terminate];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)downloadResponded:(NSNotification *)n
|
||||||
{
|
{
|
||||||
NSData *data = [[n userInfo] valueForKey:NSFileHandleNotificationDataItem];
|
NSData *data = [[n userInfo] valueForKey:NSFileHandleNotificationDataItem];
|
||||||
NSString *response = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
|
NSString *response = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
|
||||||
@@ -80,7 +108,7 @@
|
|||||||
{
|
{
|
||||||
if ([message isEqualToString:@"Completed"])
|
if ([message isEqualToString:@"Completed"])
|
||||||
{
|
{
|
||||||
status = @"COMPLETE";
|
status = @"DOWNLOADED";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse download status info
|
// Parse download status info
|
||||||
@@ -99,36 +127,60 @@
|
|||||||
[[n object] readInBackgroundAndNotify];
|
[[n object] readInBackgroundAndNotify];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)start
|
- (BOOL)extractToPath:(NSString *)aPath
|
||||||
{
|
{
|
||||||
status = @"DOWNLOADING";
|
status = @"EXTRACTING";
|
||||||
task = [game runAsyncUtilityWithArg:[NSString stringWithFormat:@"--download-url=%@,%@",url,filename]
|
task = [game runAsyncUtilityWithArg:[NSString stringWithFormat:@"--extract-zip=%@,%@",filename,aPath]
|
||||||
delegate:self
|
delegate:self
|
||||||
responseSelector:@selector(utilityResponded:)
|
responseSelector:@selector(extractResponded:)
|
||||||
terminatedSelector:@selector(utilityTerminated:)];
|
terminatedSelector:@selector(utilityTerminated:)];
|
||||||
[task retain];
|
[task retain];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)cancel
|
- (void)extractResponded:(NSNotification *)n
|
||||||
{
|
{
|
||||||
status = @"ERROR";
|
NSData *data = [[n userInfo] valueForKey:NSFileHandleNotificationDataItem];
|
||||||
error = @"Download Cancelled";
|
NSString *response = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
|
||||||
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:filename error:NULL];
|
// Response can contain multiple lines, or no lines. Split into lines, and parse each in turn
|
||||||
bytesCompleted = bytesTotal = -1;
|
NSArray *lines = [response componentsSeparatedByString:@"\n"];
|
||||||
|
for (NSString *line in lines)
|
||||||
|
{
|
||||||
|
NSLog(@"%@",line);
|
||||||
|
NSRange separator = [line rangeOfString:@":"];
|
||||||
|
if (separator.location == NSNotFound)
|
||||||
|
continue; // We only care about messages of the form key: value
|
||||||
|
|
||||||
|
NSString *type = [line substringToIndex:separator.location];
|
||||||
|
NSString *message = [[line substringFromIndex:separator.location+1]
|
||||||
|
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||||
|
|
||||||
|
if ([type isEqualToString:@"Error"])
|
||||||
|
{
|
||||||
|
status = @"ERROR";
|
||||||
|
[error autorelease];
|
||||||
|
error = [message retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ([type isEqualToString:@"Status"])
|
||||||
|
{
|
||||||
|
if ([message isEqualToString:@"Completed"])
|
||||||
|
{
|
||||||
|
status = @"EXTRACTED";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[[JSBridge sharedInstance] notifyExtractProgress:self];
|
||||||
|
|
||||||
[[JSBridge sharedInstance] notifyDownloadProgress:self];
|
// Keep reading
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
if ([n object] != nil)
|
||||||
name:NSFileHandleReadCompletionNotification
|
[[n object] readInBackgroundAndNotify];
|
||||||
object:[[task standardOutput] fileHandleForReading]];
|
|
||||||
[task terminate];
|
|
||||||
return YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)utilityTerminated:(NSNotification *)n
|
- (void)utilityTerminated:(NSNotification *)n
|
||||||
{
|
{
|
||||||
NSLog(@"utility terminated");
|
NSLog(@"download terminated");
|
||||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||||
[nc removeObserver:self name:NSFileHandleReadCompletionNotification object:[[task standardOutput] fileHandleForReading]];
|
[nc removeObserver:self name:NSFileHandleReadCompletionNotification object:[[task standardOutput] fileHandleForReading]];
|
||||||
[nc removeObserver:self name:NSTaskDidTerminateNotification object:task];
|
[nc removeObserver:self name:NSTaskDidTerminateNotification object:task];
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ static JSBridge *SharedInstance;
|
|||||||
@"downloadError", NSStringFromSelector(@selector(downloadError:)),
|
@"downloadError", NSStringFromSelector(@selector(downloadError:)),
|
||||||
@"bytesCompleted", NSStringFromSelector(@selector(bytesCompleted:)),
|
@"bytesCompleted", NSStringFromSelector(@selector(bytesCompleted:)),
|
||||||
@"bytesTotal", NSStringFromSelector(@selector(bytesTotal:)),
|
@"bytesTotal", NSStringFromSelector(@selector(bytesTotal:)),
|
||||||
|
@"extractDownload", NSStringFromSelector(@selector(extractDownload:toPath:inMod:)),
|
||||||
nil] retain];
|
nil] retain];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -153,6 +154,40 @@ static JSBridge *SharedInstance;
|
|||||||
[NSString stringWithFormat:@"downloadProgressed('%@')",[download key]]];
|
[NSString stringWithFormat:@"downloadProgressed('%@')",[download key]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)notifyExtractProgress:(Download *)download
|
||||||
|
{
|
||||||
|
[[[controller webView] windowScriptObject] evaluateWebScript:
|
||||||
|
[NSString stringWithFormat:@"extractProgressed('%@')",[download key]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)extractDownload:(NSString *)key toPath:(NSString *)aFile inMod:(NSString *)aMod
|
||||||
|
{
|
||||||
|
Download *d = [controller downloadWithKey:key];
|
||||||
|
if (d == nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Unknown download");
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
if (![[d status] isEqualToString:@"DOWNLOADED"])
|
||||||
|
{
|
||||||
|
NSLog(@"Invalid download status");
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
id mod = [[controller allMods] objectForKey:aMod];
|
||||||
|
if (mod == nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Invalid or unknown mod: %@", aMod);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disallow traversing up the directory tree
|
||||||
|
id path = [aMod stringByAppendingPathComponent:[aFile stringByReplacingOccurrencesOfString:@"../"
|
||||||
|
withString:@""]];
|
||||||
|
|
||||||
|
[d extractToPath:path];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)log:(NSString *)message
|
- (void)log:(NSString *)message
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,7 +57,12 @@
|
|||||||
{
|
{
|
||||||
font-size:0.75em;
|
font-size:0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status
|
||||||
|
{
|
||||||
|
font-size:0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
.button
|
.button
|
||||||
{
|
{
|
||||||
border: solid 3px #650b03;
|
border: solid 3px #650b03;
|
||||||
@@ -113,7 +118,7 @@
|
|||||||
|
|
||||||
function extract()
|
function extract()
|
||||||
{
|
{
|
||||||
window.external.log("Todo: Extract package");
|
window.external.extractDownload("cnc-packages","packages","cnc");
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoad()
|
function onLoad()
|
||||||
@@ -140,24 +145,31 @@
|
|||||||
document.getElementById("download-available").style.display = "none";
|
document.getElementById("download-available").style.display = "none";
|
||||||
document.getElementById("download-downloading").style.display = "none";
|
document.getElementById("download-downloading").style.display = "none";
|
||||||
document.getElementById("download-extract").style.display = "none";
|
document.getElementById("download-extract").style.display = "none";
|
||||||
|
document.getElementById("download-extracting").style.display = "none";
|
||||||
document.getElementById("download-error").style.display = "none";
|
document.getElementById("download-error").style.display = "none";
|
||||||
// status can be NOT_REGISTERED, AVAILABLE, DOWNLOADING, COMPLETE, ERROR
|
|
||||||
|
// status can be NOT_REGISTERED, AVAILABLE, DOWNLOADING, DOWNLOADED, EXTRACTING, EXTRACTED, ERROR
|
||||||
var status = window.external.downloadStatus("cnc-packages");
|
var status = window.external.downloadStatus("cnc-packages");
|
||||||
|
|
||||||
// Download complete, offer button to extract it
|
if (status == "AVAILABLE")
|
||||||
if (status == "COMPLETE")
|
|
||||||
{
|
{
|
||||||
document.getElementById("download-extract").style.display = "";
|
document.getElementById("download-available").style.display = "";
|
||||||
}
|
}
|
||||||
// Show the download progress
|
|
||||||
else if (status == "DOWNLOADING")
|
else if (status == "DOWNLOADING")
|
||||||
{
|
{
|
||||||
document.getElementById("download-downloading").style.display = "";
|
document.getElementById("download-downloading").style.display = "";
|
||||||
}
|
}
|
||||||
// Show the download button
|
if (status == "DOWNLOADED")
|
||||||
else if (status == "AVAILABLE")
|
|
||||||
{
|
{
|
||||||
document.getElementById("download-available").style.display = "";
|
document.getElementById("download-extract").style.display = "";
|
||||||
|
}
|
||||||
|
else if (status == "EXTRACTING")
|
||||||
|
{
|
||||||
|
document.getElementById("download-extracting").style.display = "";
|
||||||
|
}
|
||||||
|
else if (status == "EXTRACTED")
|
||||||
|
{
|
||||||
|
refreshSections();
|
||||||
}
|
}
|
||||||
else if (status == "ERROR")
|
else if (status == "ERROR")
|
||||||
{
|
{
|
||||||
@@ -183,6 +195,16 @@
|
|||||||
}
|
}
|
||||||
refreshDownloadButtons();
|
refreshDownloadButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractProgressed(file)
|
||||||
|
{
|
||||||
|
if (file != "cnc-packages")
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Todo: show an extraction ticker?
|
||||||
|
|
||||||
|
refreshDownloadButtons();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="onLoad();">
|
<body onload="onLoad();">
|
||||||
@@ -213,12 +235,15 @@
|
|||||||
<input type="button" class="button" id="download-status" value="Initializing..." />
|
<input type="button" class="button" id="download-status" value="Initializing..." />
|
||||||
<input type="button" class="button" onclick="cancel();" value="Cancel" />
|
<input type="button" class="button" onclick="cancel();" value="Cancel" />
|
||||||
</div>
|
</div>
|
||||||
|
<div id="download-error" style="display:none">
|
||||||
|
<input type="button" class="button" onclick="download();" value="Retry" />
|
||||||
|
<span class="status" id="error-message"></span>
|
||||||
|
</div>
|
||||||
<div id="download-extract" style="display:none">
|
<div id="download-extract" style="display:none">
|
||||||
<input type="button" class="button" onclick="extract();" value="Install" />
|
<input type="button" class="button" onclick="extract();" value="Install" />
|
||||||
</div>
|
</div>
|
||||||
<div id="download-error" style="display:none">
|
<div id="download-extracting" style="display:none">
|
||||||
<input type="button" class="button" onclick="download();" value="Retry" />
|
<input type="button" class="button" value="Installing..." />
|
||||||
<span class="desc" id="error-message"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="buttons-upgrade" class="buttons" style="display:none">
|
<div id="buttons-upgrade" class="buttons" style="display:none">
|
||||||
|
|||||||
Reference in New Issue
Block a user