Support launching an arbitrary mod. Hook up package detection for cnc.
This commit is contained in:
@@ -16,10 +16,14 @@
|
||||
{
|
||||
SidebarEntry *sidebarItems;
|
||||
GameInstall *game;
|
||||
NSDictionary *allMods;
|
||||
IBOutlet NSOutlineView *outlineView;
|
||||
IBOutlet WebView *webView;
|
||||
}
|
||||
- (void)launchGame;
|
||||
@property(readonly) NSDictionary *allMods;
|
||||
|
||||
- (void)launchMod:(NSString *)mod;
|
||||
- (void)populateModInfo;
|
||||
- (SidebarEntry *)sidebarModsTree;
|
||||
- (SidebarEntry *)sidebarOtherTree;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "JSBridge.h"
|
||||
|
||||
@implementation Controller
|
||||
@synthesize allMods;
|
||||
|
||||
- (void) awakeFromNib
|
||||
{
|
||||
@@ -25,6 +26,7 @@
|
||||
[col setDataCell:imageAndTextCell];
|
||||
|
||||
sidebarItems = [[SidebarEntry headerWithTitle:@""] retain];
|
||||
[self populateModInfo];
|
||||
id modsRoot = [self sidebarModsTree];
|
||||
[sidebarItems addChild:modsRoot];
|
||||
id otherRoot = [self sidebarOtherTree];
|
||||
@@ -45,21 +47,25 @@
|
||||
[outlineView expandItem:otherRoot expandChildren:YES];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
- (void)dealloc
|
||||
{
|
||||
[sidebarItems release]; sidebarItems = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (SidebarEntry *)sidebarModsTree
|
||||
- (void)populateModInfo
|
||||
{
|
||||
// Get info for all installed mods
|
||||
id modnames = [game installedMods];
|
||||
NSArray *allMods = [game infoForMods:modnames];
|
||||
|
||||
[allMods autorelease];
|
||||
allMods = [[game infoForMods:[game installedMods]] retain];
|
||||
}
|
||||
|
||||
- (SidebarEntry *)sidebarModsTree
|
||||
{
|
||||
SidebarEntry *rootItem = [SidebarEntry headerWithTitle:@"MODS"];
|
||||
for (id aMod in allMods)
|
||||
for (id key in allMods)
|
||||
{
|
||||
id aMod = [allMods objectForKey:key];
|
||||
if ([aMod standalone])
|
||||
{
|
||||
id child = [SidebarEntry entryWithMod:aMod allMods:allMods baseURL:[[game gameURL] URLByAppendingPathComponent:@"mods"]];
|
||||
@@ -79,9 +85,9 @@
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
- (void)launchGame
|
||||
- (void)launchMod:(NSString *)mod
|
||||
{
|
||||
[game launchGame];
|
||||
[game launchMod:mod];
|
||||
}
|
||||
|
||||
#pragma mark Sidebar Datasource and Delegate
|
||||
@@ -156,4 +162,8 @@ objectValueForTableColumn:(NSTableColumn *)tableColumn
|
||||
[windowObject setValue:[JSBridge sharedInstance] forKey:@"external"];
|
||||
}
|
||||
|
||||
- (void)webView:(WebView *)webView addMessageToConsole:(NSDictionary *)dictionary
|
||||
{
|
||||
NSLog(@"%@",dictionary);
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
@property(readonly) NSURL *gameURL;
|
||||
|
||||
-(id)initWithURL:(NSURL *)path;
|
||||
-(void)launchGame;
|
||||
-(void)launchMod:(NSString *)mod;
|
||||
- (NSString *)runUtilityQuery:(NSString *)arg;
|
||||
- (void)runUtilityQuery:(NSString *)arg handleOutput:(id)obj withMethod:(SEL)sel;
|
||||
- (NSArray *)installedMods;
|
||||
- (NSArray *)infoForMods:(NSArray *)mods;
|
||||
- (NSDictionary *)infoForMods:(NSArray *)mods;
|
||||
@end
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
return [mods componentsSeparatedByString:@"\n"];
|
||||
}
|
||||
|
||||
- (NSArray *)infoForMods:(NSArray *)mods
|
||||
- (NSDictionary *)infoForMods:(NSArray *)mods
|
||||
{
|
||||
id query = [NSString stringWithFormat:@"-i=%@",[mods componentsJoinedByString:@","]];
|
||||
NSArray *lines = [[self runUtilityQuery:query] componentsSeparatedByString:@"\n"];
|
||||
|
||||
NSMutableArray *ret = [NSMutableArray array];
|
||||
NSMutableDictionary *ret = [NSMutableDictionary dictionary];
|
||||
NSMutableDictionary *fields = nil;
|
||||
NSString *current = nil;
|
||||
for (id l in lines)
|
||||
@@ -68,7 +68,7 @@
|
||||
if (current != nil)
|
||||
{
|
||||
id url = [gameURL URLByAppendingPathComponent:[NSString stringWithFormat:@"mods/%@",current]];
|
||||
[ret addObject:[Mod modWithId:current fields:fields baseURL:url]];
|
||||
[ret setObject:[Mod modWithId:current fields:fields baseURL:url] forKey:current];
|
||||
}
|
||||
NSLog(@"Parsing mod %@",value);
|
||||
current = value;
|
||||
@@ -82,17 +82,17 @@
|
||||
if (current != nil)
|
||||
{
|
||||
id url = [gameURL URLByAppendingPathComponent:[NSString stringWithFormat:@"mods/%@",current]];
|
||||
[ret addObject:[Mod modWithId:current fields:fields baseURL:url]];
|
||||
[ret setObject:[Mod modWithId:current fields:fields baseURL:url] forKey:current];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
-(void)launchGame
|
||||
-(void)launchMod:(NSString *)mod
|
||||
{
|
||||
// Use LaunchServices because neither NSTask or NSWorkspace support Info.plist _and_ arguments pre-10.6
|
||||
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"OpenRA.app/Contents/MacOS/OpenRA"];
|
||||
NSArray *args = [NSArray arrayWithObjects:[gameURL absoluteString], @"mono", @"--debug", @"OpenRA.Game.exe", @"Game.Mods=ra",nil];
|
||||
NSArray *args = [NSArray arrayWithObjects:[gameURL absoluteString], @"mono", @"--debug", @"OpenRA.Game.exe", [NSString stringWithFormat:@"Game.Mods=%@",mod],nil];
|
||||
|
||||
FSRef appRef;
|
||||
CFURLGetFSRef((CFURLRef)[NSURL URLWithString:path], &appRef);
|
||||
|
||||
@@ -38,7 +38,7 @@ static JSBridge *SharedInstance;
|
||||
if (self != nil)
|
||||
{
|
||||
methods = [[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"launchCurrentMod", NSStringFromSelector(@selector(launchCurrentMod)),
|
||||
@"launchMod", NSStringFromSelector(@selector(launchMod:)),
|
||||
@"log", NSStringFromSelector(@selector(log:)),
|
||||
@"fileExistsInMod", NSStringFromSelector(@selector(fileExists:inMod:)),
|
||||
nil] retain];
|
||||
@@ -59,10 +59,17 @@ static JSBridge *SharedInstance;
|
||||
|
||||
#pragma mark JS methods
|
||||
|
||||
- (void)launchCurrentMod
|
||||
- (BOOL)launchMod:(NSString *)aMod
|
||||
{
|
||||
NSLog(@"launchcurrent");
|
||||
[controller launchGame];
|
||||
id mod = [[controller allMods] objectForKey:aMod];
|
||||
if (mod == nil)
|
||||
{
|
||||
NSLog(@"Invalid or unknown mod: %@", aMod);
|
||||
return NO;
|
||||
}
|
||||
|
||||
[controller launchMod:aMod];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)log:(NSString *)message
|
||||
@@ -72,8 +79,19 @@ static JSBridge *SharedInstance;
|
||||
|
||||
- (BOOL)fileExists:(NSString *)aFile inMod:(NSString *)aMod
|
||||
{
|
||||
NSLog(@"File %@ exists in mod %@",aFile, aMod);
|
||||
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 = [[[mod baseURL] absoluteString]
|
||||
stringByAppendingPathComponent:[aFile stringByReplacingOccurrencesOfString:@"../"
|
||||
withString:@""]];
|
||||
|
||||
return [[NSFileManager defaultManager] fileExistsAtPath:path];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,10 +24,9 @@
|
||||
@property (readonly) NSString *author;
|
||||
@property (readonly) NSString *description;
|
||||
@property (readonly) NSString *requires;
|
||||
@property (readonly) NSURL *baseURL;
|
||||
@property (readonly) BOOL standalone;
|
||||
|
||||
+ (id)modWithId:(NSString *)mid fields:(id)fields baseURL:(NSURL *)url;
|
||||
- (id)initWithId:(NSString *)anId fields:(NSDictionary *)fields baseURL:(NSURL *)url;
|
||||
|
||||
- (NSURL *)pageURL;
|
||||
@end
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
@synthesize description;
|
||||
@synthesize requires;
|
||||
@synthesize standalone;
|
||||
@synthesize baseURL;
|
||||
|
||||
+ (id)modWithId:(NSString *)mod fields:(id)fields baseURL:(NSURL *)url
|
||||
{
|
||||
@@ -54,9 +55,4 @@
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSURL *)pageURL
|
||||
{
|
||||
return [baseURL URLByAppendingPathComponent:@"mod.html"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
+ (id)headerWithTitle:(NSString *)aTitle;
|
||||
+ (id)entryWithTitle:(NSString *)aTitle url:(NSURL *)aURL icon:(id)anIcon;
|
||||
+ (id)entryWithMod:(Mod *)baseMod allMods:(NSArray *)allMods baseURL:(NSURL *)aURL;
|
||||
+ (id)entryWithMod:(Mod *)baseMod allMods:(NSDictionary *)allMods baseURL:(NSURL *)aURL;
|
||||
- (id)initWithTitle:(NSString *)aTitle url:(NSURL *)aURL icon:(id)anIcon isHeader:(BOOL)aHeader;
|
||||
- (void)addChild:(id)child;
|
||||
- (NSURL *)url;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
return newObject;
|
||||
}
|
||||
|
||||
+ (id)entryWithMod:(Mod *)baseMod allMods:(NSArray *)allMods baseURL:(NSURL *)baseURL
|
||||
+ (id)entryWithMod:(Mod *)baseMod allMods:(NSDictionary *)allMods baseURL:(NSURL *)baseURL
|
||||
{
|
||||
// TODO: Get the mod icon from the Mod
|
||||
// Temporary hack until mods define an icon
|
||||
@@ -40,8 +40,9 @@
|
||||
|
||||
id ret = [SidebarEntry entryWithTitle:[baseMod title] url:url icon:icon];
|
||||
|
||||
for (id aMod in allMods)
|
||||
for (id key in allMods)
|
||||
{
|
||||
id aMod = [allMods objectForKey:key];
|
||||
if (![[aMod requires] isEqualToString:[baseMod mod]])
|
||||
continue;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
padding: 20px 0px;
|
||||
width:100%;
|
||||
height:100%;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
div#header
|
||||
@@ -21,6 +22,7 @@
|
||||
margin-top: 30px;
|
||||
height: 66px;
|
||||
text-align:center;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
div.bar
|
||||
@@ -40,16 +42,21 @@
|
||||
div#content
|
||||
{
|
||||
text-align: justify;
|
||||
width:50%;
|
||||
padding-left:50px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
div#buttons
|
||||
div.buttons
|
||||
{
|
||||
position:absolute;
|
||||
bottom:0px;
|
||||
padding:20px;
|
||||
}
|
||||
|
||||
div.desc
|
||||
{
|
||||
font-size:0.75em;
|
||||
}
|
||||
|
||||
.button
|
||||
{
|
||||
@@ -57,7 +64,7 @@
|
||||
border-radius: 10px;
|
||||
margin:10px;
|
||||
height:40px;
|
||||
width:150px;
|
||||
width:170px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
font-size:1em;
|
||||
@@ -75,8 +82,42 @@
|
||||
}
|
||||
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
// Check which packages are installed.
|
||||
// Returns 1 if basic files are installed
|
||||
// Returns 2 if basic files plus music are installed
|
||||
function packagesInstalled()
|
||||
{
|
||||
if (window.external.fileExistsInMod('packages/conquer.mix','cnc') != 1)
|
||||
return 0;
|
||||
|
||||
return (window.external.fileExistsInMod('packages/scores.mix','cnc') == 1) ? 2 : 1;
|
||||
}
|
||||
|
||||
function play()
|
||||
{
|
||||
window.external.launchMod("cnc");
|
||||
}
|
||||
|
||||
function installFromCD()
|
||||
{
|
||||
window.external.log("installFromCD()");
|
||||
}
|
||||
|
||||
function installFromWeb()
|
||||
{
|
||||
window.external.log("installFromWeb()");
|
||||
}
|
||||
|
||||
function onLoad()
|
||||
{
|
||||
document.getElementById("buttons-install").style.display = (packagesInstalled() == 0) ? "" : "none";
|
||||
document.getElementById("buttons-upgrade").style.display = (packagesInstalled() == 1) ? "" : "none";
|
||||
document.getElementById("buttons-play").style.display = (packagesInstalled() == 2) ? "" : "none";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="onLoad();">
|
||||
<div id="header" class="bar">
|
||||
<h1>Command & Conquer</h1>
|
||||
</div>
|
||||
@@ -93,9 +134,23 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="buttons">
|
||||
<input type="button" class="button" onclick="window.external.launchCurrentMod();" value="Play" />
|
||||
<input type="button" class="button" onclick="window.external.fileExistsInMod('foo.mix','ra');"value="Debug" />
|
||||
<div id="buttons-install" class="buttons">
|
||||
<div class="desc">The original game data is required before you can play this mod.<br />
|
||||
Installing from web will install the minimal files required to play.<br />
|
||||
Installing from CD will also install the music and movie files for an improved game experience.
|
||||
</div>
|
||||
<input type="button" class="button" onclick="installFromWeb();" value="Install from Web" />
|
||||
<input type="button" class="button" onclick="installFromCD();" value="Install from CD" />
|
||||
</div>
|
||||
<div id="buttons-upgrade" class="buttons" style="display:none">
|
||||
<div class="desc">
|
||||
Upgrade from CD to install the original music and movie files for an improved game experience.
|
||||
</div>
|
||||
<input type="button" class="button" onclick="play();" value="Play" />
|
||||
<input type="button" class="button" onclick="installFromCD();" value="Upgrade from CD" />
|
||||
</div>
|
||||
<div id="buttons-play" class="buttons" style="display:none">
|
||||
<input type="button" class="button" onclick="play();" value="Play" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Dune 2000</h1>
|
||||
<input type="button" class="button" onclick="window.external.launchCurrentMod();" value="Play" />
|
||||
<input type="button" class="button" onclick="window.external.launchMod('d2k')" value="Play" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Default Mod</h1>
|
||||
<input type="button" class="button" onclick="window.external.launchCurrentMod();" value="Play" />
|
||||
<input type="button" class="button" onclick="window.external.launchMod('default')" value="Play" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<body>
|
||||
<h1>Example Mod</h1>
|
||||
<h2>Demonstrates how to add a new unit by adding a soviet supply truck.</h2>
|
||||
<input type="button" class="button" onclick="window.external.launchCurrentMod();" value="Play" />
|
||||
<input type="button" class="button" onclick="window.external.launchMod('example')" value="Play" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Red Alert</h1>
|
||||
<input type="button" class="button" onclick="window.external.launchCurrentMod();" value="Play" />
|
||||
<input type="button" class="button" onclick="window.external.launchMod('ra')" value="Play" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<body>
|
||||
<h1>RA Performance Tests</h1>
|
||||
<h2>Adds special performance test maps to ra.</h2>
|
||||
<input type="button" class="button" onclick="window.external.launchCurrentMod();" value="Play" />
|
||||
<input type="button" class="button" onclick="window.external.launchMod('ra_perf')" value="Play" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user