Files
itgmania212121/Xcode/Patcher/Upgrader.m
T
2010-05-12 12:21:02 -05:00

365 lines
10 KiB
Objective-C

#import "Upgrader.h"
#import "CopyFiles.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
@implementation Upgrader
// Generate English strings using: genstring -o en.lproj Upgrader.m
static NSString *CHOOSE_FILE;
static NSString *PATCH_FILE;
static NSString *PATCH_COMPLETE;
static NSString *QUIT;
static NSString *ERROR;
static NSString *ERROR_MESSAGE;
static NSString *PATCH_FILES_MISSING;
static NSString *PATCHER_CORRUPTED;
static NSString *CORRUPT_TITLE;
static NSString *PATCHING;
static NSString *SEARCHING;
static NSString *SELECTION;
static NSString *WINDOW_TITLE;
static NSOpenPanel *panel = nil;
- (void)awakeFromNib
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"Identification" ofType:@"txt"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSString *stringData = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
CHOOSE_FILE = NSLocalizedString( @"Choose file to patch.", nil );
PATCH_FILE = NSLocalizedString( @"Patch file.", nil );
PATCH_COMPLETE = NSLocalizedString( @"Patch complete.", nil );
QUIT = NSLocalizedString( @"Quit", nil );
ERROR = NSLocalizedString( @"Error", nil );
ERROR_MESSAGE = NSLocalizedString( @"Patching failed:\n%@", nil );
PATCH_FILES_MISSING = NSLocalizedString( @"Patch files missing.", nil );
PATCHER_CORRUPTED = NSLocalizedString( @"This patcher has become corrupted.", nil );
CORRUPT_TITLE = NSLocalizedString( @"Corrupt Patcher", nil );
PATCHING = NSLocalizedString( @"Patching", nil );
SEARCHING = NSLocalizedString( @"Searching for %@...", nil );
SELECTION = NSLocalizedString( @"You have chosen to patch \"%@\" in the folder:\n%@", nil );
WINDOW_TITLE = NSLocalizedString( @"StepMania Patcher", nil );
if( !data )
{
NSRunCriticalAlertPanel( CORRUPT_TITLE, PATCHER_CORRUPTED, @"OK", nil, nil );
[NSApp terminate:self];
}
m_sName = nil;
m_sAppID = nil;
m_vVersions = nil;
NSArray *lines = [stringData componentsSeparatedByString:@"\n"];
NSEnumerator *iter = [lines objectEnumerator];
NSString *line;
NSMutableArray *versions = [NSMutableArray array];
while( (line = [iter nextObject]) )
{
line = [line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if( [line length] == 0 || [line characterAtIndex:0] == '#' )
continue;
if( m_sName == nil )
{
m_sName = [line retain];
}
else if( m_sAppID == nil )
{
m_sAppID = [line retain];
}
else if( [line isEqualToString:@"-ALL-"] )
{
versions = nil;
break;
}
else
{
[versions addObject:line];
}
}
if( !m_sName || !m_sAppID )
{
NSRunCriticalAlertPanel( CORRUPT_TITLE, PATCHER_CORRUPTED, @"OK", nil, nil );
[NSApp terminate:self];
}
if( versions && [versions count] )
m_vVersions = [versions retain];
// Set strings.
[m_StatusText setStringValue:[NSString stringWithFormat:SEARCHING, m_sName]];
[m_SelectionText setStringValue:@""];
[m_PatchingText setStringValue:PATCHING];
[m_Searching setUsesThreadedAnimation:YES];
[m_ProgressBar setUsesThreadedAnimation:YES];
[m_Window setTitle:WINDOW_TITLE];
[NSThread detachNewThreadSelector:@selector(findApp:) toTarget:self withObject:nil];
[m_Choose setEnabled:YES];
}
- (void) dealloc
{
[m_sName release];
[m_sAppID release];
[m_vVersions release];
[super dealloc];
}
- (IBAction) chooseFile:(id)sender
{
if( !panel )
{
panel = [[NSOpenPanel openPanel] retain];
[panel setCanChooseDirectories:YES];
[panel setAllowsMultipleSelection:NO];
[panel setDelegate:self];
[panel setDirectory:@"/Applications"];
}
if( [panel runModal] == NSFileHandlingPanelOKButton )
{
NSString *dir, *app;
[m_StatusText setStringValue:PATCH_FILE];
[m_Okay setEnabled:YES];
[m_sPath release];
m_sPath = [[panel filename] retain];
dir = [m_sPath stringByDeletingLastPathComponent];
app = [[m_sPath lastPathComponent] stringByDeletingPathExtension];
[m_SelectionText setStringValue:[NSString stringWithFormat:SELECTION, app, dir]];
[m_Okay setTitle:@"OK"];
[m_Okay setAction:@selector(upgrade:)];
[m_Okay setTarget:self];
}
}
- (IBAction) upgrade:(id)sender
{
[NSApp beginSheet:m_Panel
modalForWindow:[sender window]
modalDelegate:self
didEndSelector:@selector(sheetEnded:returnCode:contextInfo:)
contextInfo:NULL];
[NSThread detachNewThreadSelector:@selector(upgradeFile:) toTarget:self withObject:m_sPath];
}
- (void) findApp:(id)obj
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[m_Searching startAnimation:self];
// Find ITG
CFStringRef key = CFSTR( "ApplicationBundlePath" );
CFPropertyListRef list = CFPreferencesCopyAppValue( key, (CFStringRef)m_sAppID );
NSString *sPath = nil;
if( list && CFGetTypeID(list) != CFDictionaryGetTypeID() )
{
CFRelease( list );
list = NULL;
}
if( list )
{
CFTypeRef value;
CFDictionaryRef dict = (CFDictionaryRef)list;
if( m_vVersions )
{
NSEnumerator *iter = [m_vVersions objectEnumerator];
id version;
while( (version = [iter nextObject]) )
{
if( CFDictionaryGetValueIfPresent(dict, (CFStringRef)version, &value) &&
CFGetTypeID(value) == CFStringGetTypeID() &&
[self checkPath:(NSString *)value] )
{
sPath = (NSString *)CFRetain( value );
[sPath autorelease];
}
}
}
else
{
NSDictionary *dict = (NSDictionary *)list;
NSEnumerator *iter = [dict objectEnumerator];
id path;
while( (path = [iter nextObject]) )
{
if( ![path isKindOfClass:[NSString class]] || ![self checkPath:path] )
continue;
sPath = [[path retain] autorelease];
break;
}
}
CFRelease( list );
}
if( !sPath )
{
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
[ws findApplications];
sPath = [ws fullPathForApplication:m_sName];
if( ![self checkPath:sPath] )
sPath = nil;
}
[self performSelectorOnMainThread:@selector(foundApp:) withObject:sPath waitUntilDone:YES];
[m_Searching stopAnimation:self];
[pool release];
}
- (void) foundApp:(id)obj
{
NSString *path = (NSString *)obj;
if( path ) // found
{
NSString *dir = [path stringByDeletingLastPathComponent];
NSString *app = [[path lastPathComponent] stringByDeletingPathExtension];
[m_sPath release];
m_sPath = [path retain];
[m_Okay setEnabled:YES];
[m_StatusText setStringValue:PATCH_FILE];
[m_SelectionText setStringValue:[NSString stringWithFormat:SELECTION, app, dir]];
}
else
{
m_sPath = @"";
[m_StatusText setStringValue:CHOOSE_FILE];
}
[m_Choose setEnabled:YES];
[m_Choose setNeedsDisplay:YES];
[m_Okay setNeedsDisplay:YES];
[m_StatusText setNeedsDisplay:YES];
[m_SelectionText setNeedsDisplay:YES];
}
- (void) upgradeFile:(id)path
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSBundle *bundle = [NSBundle mainBundle];
NSString *patch = [bundle pathForResource:@"Contents" ofType:@"" inDirectory:@"Patch"];
NSArray *parentFiles = [bundle pathsForResourcesOfType:@"" inDirectory:@"Patch/ParentContents"];
if( patch && parentFiles)
{
[m_ProgressBar startAnimation:self];
if( NeedsPrivs(path) )
m_sError = CopyWithPrivs( patch, path );
else
m_sError = Copy( patch, path );
path = [path stringByDeletingLastPathComponent];
NSEnumerator *enumerator = [parentFiles objectEnumerator];
while( !m_sError && (patch = [enumerator nextObject]) )
{
if( NeedsPrivs(path) )
m_sError = CopyWithPrivs( patch, path );
else
m_sError = Copy( patch, path );
}
[m_ProgressBar stopAnimation:self];
}
else
{
m_sError = PATCH_FILES_MISSING;
}
[NSApp performSelectorOnMainThread:@selector(endSheet:) withObject:m_Panel waitUntilDone:YES];
[pool release];
}
- (void) sheetEnded:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
[m_Panel orderOut:self];
if( m_sError )
{
NSRunCriticalAlertPanel( ERROR, ERROR_MESSAGE, @"OK", nil, nil, m_sError );
return;
}
[m_StatusText setStringValue:PATCH_COMPLETE];
[m_Okay setTitle:QUIT];
[m_Okay setAction:@selector(terminate:)];
[m_Okay setTarget:NSApp];
}
- (BOOL) panel:(id)sender isValidFilename:(NSString *)filename
{
return [filename hasSuffix:@".app"] && [self checkPath:filename];
}
- (BOOL) panel:(id)sender shouldShowFilename:(NSString *)filename
{
if( [filename hasSuffix:@".app"] )
return [self checkPath:filename];
struct stat sb;
if( stat([filename UTF8String], &sb) )
return NO;
return (sb.st_mode & S_IFDIR) == S_IFDIR;
}
- (BOOL) checkPath:(NSString *)path
{
NSBundle *b = [NSBundle bundleWithPath:path];
if( !b )
return NO;
if( ![[b objectForInfoDictionaryKey:@"CFBundleIdentifier"] isEqualToString:m_sAppID] )
return NO;
if( m_vVersions == nil )
return YES;
NSString *bundleVersion = [b objectForInfoDictionaryKey:@"CFBundleVersion"];
NSEnumerator *iter = [m_vVersions objectEnumerator];
NSString *version;
while( (version = [iter nextObject]) )
if( [bundleVersion isEqualToString:version] )
return YES;
return NO;
}
@end
/*
* (c) 2006 Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/