From 7271dd5248419c214274e7ed23950b8ae332dcb8 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 16 Nov 2010 14:33:47 +1300 Subject: [PATCH] List installed mods in the sidebar. Changes utility mod info syntax. --- OpenRA.Launcher.Mac/Controller.h | 1 - OpenRA.Launcher.Mac/Controller.m | 30 ++-- .../English.lproj/MainMenu.xib | 20 +-- OpenRA.Launcher.Mac/GameInstall.h | 19 +-- OpenRA.Launcher.Mac/GameInstall.m | 128 ++++++++++++++++-- OpenRA.Launcher.Mac/ModEntry.h | 8 +- OpenRA.Launcher.Mac/ModEntry.m | 54 ++++++-- OpenRA.Utility/Command.cs | 2 +- mods/cnc/mod.yaml | 3 +- 9 files changed, 199 insertions(+), 66 deletions(-) diff --git a/OpenRA.Launcher.Mac/Controller.h b/OpenRA.Launcher.Mac/Controller.h index b7ecca9b7d..f3ac59463c 100644 --- a/OpenRA.Launcher.Mac/Controller.h +++ b/OpenRA.Launcher.Mac/Controller.h @@ -16,6 +16,5 @@ GameInstall *game; IBOutlet NSOutlineView *outlineView; } -- (ModEntry *)modTree; - (IBAction)launchGame:(id)sender; @end diff --git a/OpenRA.Launcher.Mac/Controller.m b/OpenRA.Launcher.Mac/Controller.m index 10a17ece8f..9ae22ee67f 100644 --- a/OpenRA.Launcher.Mac/Controller.m +++ b/OpenRA.Launcher.Mac/Controller.m @@ -15,8 +15,9 @@ - (void) awakeFromNib { + game = [[GameInstall alloc] initWithPath:@"/Users/paul/src/OpenRA"]; sidebarItems = [[ModEntry headerWithTitle:@""] retain]; - [sidebarItems addChild:[self modTree]]; + [sidebarItems addChild:[game modTree]]; NSTableColumn *col = [outlineView tableColumnWithIdentifier:@"mods"]; ImageAndTextCell *imageAndTextCell = [[[ImageAndTextCell alloc] init] autorelease]; @@ -25,8 +26,6 @@ [outlineView reloadData]; [outlineView expandItem:[outlineView itemAtRow:1] expandChildren:YES]; [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO]; - - game = [[GameInstall alloc] initWithPath:@"/Users/paul/src/OpenRA"]; } - (void) dealloc @@ -35,24 +34,6 @@ [super dealloc]; } -- (ModEntry *)modTree -{ - // Create root item - ModEntry *rootItem = [ModEntry headerWithTitle:@"MODS"]; - - NSString* imageName = [[NSBundle mainBundle] pathForResource:@"OpenRA" ofType:@"icns"]; - NSImage* imageObj = [[NSImage alloc] initWithContentsOfFile:imageName]; - - NSDictionary *foo = [NSDictionary dictionaryWithObjectsAndKeys: - @"Test mod", @"Title", - @"Foobar", @"Author", - imageObj, @"Icon", - nil]; - [imageObj release]; - [rootItem addChild:[ModEntry modWithFields:foo]]; - return rootItem; -} - #pragma mark Sidebar Datasource and Delegate - (int)outlineView:(NSOutlineView *)anOutlineView numberOfChildrenOfItem:(id)item { @@ -100,7 +81,12 @@ objectValueForTableColumn:(NSTableColumn *)tableColumn - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item; { // don't allow headers to be selected - return ![item isHeader]; + if ([item isHeader]) + return NO; + + // TODO: Display the webpage + + return YES; } - (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item diff --git a/OpenRA.Launcher.Mac/English.lproj/MainMenu.xib b/OpenRA.Launcher.Mac/English.lproj/MainMenu.xib index 77f762be9e..89a3fbc3e5 100644 --- a/OpenRA.Launcher.Mac/English.lproj/MainMenu.xib +++ b/OpenRA.Launcher.Mac/English.lproj/MainMenu.xib @@ -13,7 +13,7 @@ YES - + YES @@ -295,7 +295,7 @@ 256 - {144, 468} + {188, 468} YES @@ -307,7 +307,7 @@ YES mods - 141 + 185 16 1000 @@ -398,7 +398,7 @@ 14 - {{1, 1}, {144, 468}} + {{1, 1}, {188, 468}} @@ -425,7 +425,7 @@ 0.99295777082443237 - {146, 470} + {190, 470} 562 @@ -435,7 +435,7 @@ QSAAAEEgAABBoAAAQaAAAA - {145, 469} + {189, 469} 2 NSView @@ -447,8 +447,8 @@ YES - 268 - {{383, 15}, {111, 27}} + 289 + {{339, 19}, {111, 27}} YES @@ -466,7 +466,7 @@ - {{146, 0}, {513, 469}} + {{190, 0}, {469, 469}} 2 NSView @@ -1089,7 +1089,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABDnQAAw6CAAA + P4AAAL+AAABDv4AAwiAAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin diff --git a/OpenRA.Launcher.Mac/GameInstall.h b/OpenRA.Launcher.Mac/GameInstall.h index aec58557b5..efc22a5558 100644 --- a/OpenRA.Launcher.Mac/GameInstall.h +++ b/OpenRA.Launcher.Mac/GameInstall.h @@ -1,18 +1,21 @@ -// -// GameInstall.h -// OpenRA -// -// Created by Paul Chote on 15/11/10. -// Copyright 2010 __MyCompanyName__. All rights reserved. -// +/* + * Copyright 2007-2010 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see LICENSE. + */ #import - +@class ModEntry; @interface GameInstall : NSObject { NSString *gamePath; + NSMutableString *utilityBuffer; } -(id)initWithPath:(NSString *)path; +- (ModEntry *)modTree; -(void)launchGame; +- (void)runUtilityApp:(NSString *)arg handleOutput:(id)obj withMethod:(SEL)sel; @end diff --git a/OpenRA.Launcher.Mac/GameInstall.m b/OpenRA.Launcher.Mac/GameInstall.m index a41607e31c..a3ee9e5401 100644 --- a/OpenRA.Launcher.Mac/GameInstall.m +++ b/OpenRA.Launcher.Mac/GameInstall.m @@ -1,13 +1,13 @@ -// -// GameInstall.m -// OpenRA -// -// Created by Paul Chote on 15/11/10. -// Copyright 2010 __MyCompanyName__. All rights reserved. -// +/* + * Copyright 2007-2010 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see LICENSE. + */ #import "GameInstall.h" - +#import "ModEntry.h" @implementation GameInstall @@ -16,13 +16,98 @@ self = [super init]; if (self != nil) { - NSLog(@"creating game at path %@",path); - gamePath = path; } return self; } +- (void)dealloc +{ + [utilityBuffer release]; + [super dealloc]; +} + +-(void)clearBuffer +{ + [utilityBuffer release]; + utilityBuffer = [[NSMutableString stringWithString:@""] retain]; +} + +- (void)bufferData:(NSString *)string +{ + if (string == nil) return; + [utilityBuffer appendString:string]; +} + +- (NSArray *)installedMods +{ + [self clearBuffer]; + [self runUtilityApp:@"-l" handleOutput:self withMethod:@selector(bufferData:)]; + id mods = [[NSString stringWithString:utilityBuffer] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + return [mods componentsSeparatedByString:@"\n"]; +} + +- (NSArray *)infoForMods:(NSArray *)mods +{ + [self clearBuffer]; + [self runUtilityApp:[NSString stringWithFormat:@"-i=%@",[mods componentsJoinedByString:@","]] handleOutput:self withMethod:@selector(bufferData:)]; + NSArray *lines = [utilityBuffer componentsSeparatedByString:@"\n"]; + + NSMutableArray *ret = [NSMutableArray array]; + NSMutableDictionary *fields = nil; + NSString *current = nil; + for (id l in lines) + { + id line = [l stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (line == nil || [line length] == 0) + continue; + + id kv = [line componentsSeparatedByString:@":"]; + id key = [kv objectAtIndex:0]; + id value = [kv objectAtIndex:1]; + + if ([key isEqualToString:@"Error"]) + { + NSLog(@"Error: %@",value); + continue; + } + + if ([key isEqualToString:@"Mod"]) + { + // Commit prev mod + if (current != nil) + [ret addObject:[ModEntry modWithId:current fields:fields]]; + NSLog(@"Parsing mod %@",value); + current = value; + fields = [NSMutableDictionary dictionary]; + } + + if (fields != nil) + [fields setObject:[value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] + forKey:key]; + } + return ret; +} + +- (ModEntry *)modTree +{ + // Get info for all installed mods + id modnames = [self installedMods]; + NSArray *allMods = [self infoForMods:modnames]; + + ModEntry *rootItem = [ModEntry headerWithTitle:@"MODS"]; + for (id aMod in allMods) + { + if ([aMod standalone]) + { + [rootItem addChild:aMod]; + [aMod buildChildTree:allMods]; + } + } + + return rootItem; +} + -(void)launchGame { // Use LaunchServices because neither NSTask or NSWorkspace support Info.plist _and_ arguments pre-10.6 @@ -49,4 +134,27 @@ if (err == noErr) SetFrontProcess(&psn); } + +- (void)runUtilityApp:(NSString *)arg handleOutput:(id)obj withMethod:(SEL)sel +{ + NSTask *aTask = [[NSTask alloc] init]; + NSPipe *aPipe = [NSPipe pipe]; + NSFileHandle *readHandle = [aPipe fileHandleForReading]; + + NSMutableArray *taskArgs = [NSMutableArray arrayWithObject:@"OpenRA.Utility.exe"]; + [taskArgs addObject:arg]; + + [aTask setCurrentDirectoryPath:gamePath]; + [aTask setLaunchPath:@"/Library/Frameworks/Mono.framework/Commands/mono"]; + [aTask setArguments:taskArgs]; + [aTask setStandardOutput:aPipe]; + [aTask launch]; + + NSData *inData = nil; + while ((inData = [readHandle availableData]) && [inData length]) + [obj performSelector:sel withObject:[NSString stringWithUTF8String:[inData bytes]]]; + + [aTask release]; +} + @end diff --git a/OpenRA.Launcher.Mac/ModEntry.h b/OpenRA.Launcher.Mac/ModEntry.h index 38dad235ab..e78d35dc1f 100644 --- a/OpenRA.Launcher.Mac/ModEntry.h +++ b/OpenRA.Launcher.Mac/ModEntry.h @@ -10,6 +10,7 @@ @interface ModEntry : NSObject { BOOL isHeader; + NSString *mod; NSString *title; NSString *version; NSString *author; @@ -20,6 +21,7 @@ } @property (readonly) BOOL isHeader; +@property (readonly) NSString *mod; @property (readonly) NSString *title; @property (readonly) NSString *version; @property (readonly) NSString *author; @@ -30,8 +32,10 @@ @property (readonly) NSImage* icon; + (id)headerWithTitle:(NSString *)aTitle; -+ (id)modWithFields:(id)fields; -- (id)initWithFields:(NSDictionary *)fields isHeader:(BOOL)header; ++ (id)errorWithTitle:(NSString *)aTitle; ++ (id)modWithId:(NSString *)mid fields:(id)fields; +- (id)initWithId:(NSString *)mod fields:(NSDictionary *)fields isHeader:(BOOL)header; - (void)addChild:(id)child; +- (void)buildChildTree:(NSArray *)allMods; - (id)icon; @end diff --git a/OpenRA.Launcher.Mac/ModEntry.m b/OpenRA.Launcher.Mac/ModEntry.m index a8225ebe91..57e30e040c 100644 --- a/OpenRA.Launcher.Mac/ModEntry.m +++ b/OpenRA.Launcher.Mac/ModEntry.m @@ -10,6 +10,7 @@ @implementation ModEntry +@synthesize mod; @synthesize isHeader; @synthesize title; @synthesize version; @@ -22,31 +23,44 @@ + (id)headerWithTitle:(NSString *)aTitle { - id newObject = [[self alloc] initWithFields:[NSDictionary dictionaryWithObject:aTitle forKey:@"Title"] isHeader:YES]; + id newObject = [[self alloc] initWithId:@"title" fields:[NSDictionary dictionaryWithObject:aTitle forKey:@"Title"] isHeader:YES]; [newObject autorelease]; return newObject; } -+ (id)modWithFields:(id)fields ++ (id)errorWithTitle:(NSString *)aTitle { - id newObject = [[self alloc] initWithFields:fields isHeader:NO]; + id newObject = [[self alloc] initWithId:@"error" fields:[NSDictionary dictionaryWithObject:aTitle forKey:@"Title"] isHeader:NO]; [newObject autorelease]; return newObject; } -- (id)initWithFields:(NSDictionary *)fields isHeader:(BOOL)header ++ (id)modWithId:(NSString *)mod fields:(id)fields +{ + id newObject = [[self alloc] initWithId:mod fields:fields isHeader:NO]; + [newObject autorelease]; + return newObject; +} + +- (id)initWithId:(NSString *)anId fields:(NSDictionary *)fields isHeader:(BOOL)header { self = [super init]; if (self) { + mod = anId; isHeader = header; - title = [fields objectForKey:@"Title"]; - version = [fields objectForKey:@"Version"]; - author = [fields objectForKey:@"Author"]; - description = [fields objectForKey:@"Description"]; - requires = [fields objectForKey:@"Requires"]; - standalone = ([fields objectForKey:@"Standalone"] == @"True"); - icon = [[fields objectForKey:@"Icon"] retain]; + title = [[fields objectForKey:@"Title"] retain]; + version = [[fields objectForKey:@"Version"] retain]; + author = [[fields objectForKey:@"Author"] retain]; + description = [[fields objectForKey:@"Description"] retain]; + requires = [[fields objectForKey:@"Requires"] retain]; + standalone = ([[fields objectForKey:@"Standalone"] isEqualToString:@"True"]); + + if (!isHeader) + { + NSString* imageName = [[NSBundle mainBundle] pathForResource:@"OpenRA" ofType:@"icns"]; + icon = [[NSImage alloc] initWithContentsOfFile:imageName]; + } children = [[NSMutableArray alloc] init]; } return self; @@ -54,11 +68,29 @@ - (void)addChild:(ModEntry *)child { + NSLog(@"Adding child %@ to %@",[child mod], mod); [children addObject:child]; } +- (void)buildChildTree:(NSArray *)allMods +{ + for (id aMod in allMods) + { + if (![[aMod requires] isEqualToString:mod]) + continue; + + [self addChild:aMod]; + [aMod buildChildTree:allMods]; + } +} + - (void) dealloc { + [title release]; title = nil; + [version release]; version = nil; + [author release]; author = nil; + [description release]; description = nil; + [requires release]; requires = nil; [icon release]; icon = nil; [super dealloc]; } diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs index 11ee54238d..7faf5bfe21 100644 --- a/OpenRA.Utility/Command.cs +++ b/OpenRA.Utility/Command.cs @@ -42,7 +42,7 @@ namespace OpenRA.Utility return; } - Console.WriteLine("{0}:", m); + Console.WriteLine("Mod:{0}", m); Console.WriteLine(" Title: {0}", mod.Title); Console.WriteLine(" Version: {0}", mod.Version); Console.WriteLine(" Author: {0}", mod.Author); diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 9edd27eb3d..d8c97437f5 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -2,7 +2,8 @@ Metadata: Title: C&C Description: OpenRA Reimagining of the classic game Version: a0001 - + Author: The OpenRA Developers + Standalone: true Folders: . mods/cnc