diff --git a/client/CMT.cpp b/client/CMT.cpp index c67991618..cb02f612a 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -188,6 +188,13 @@ int main(int argc, char** argv) std::string executablePath = argv[0]; std::string workDir = executablePath.substr(0, executablePath.rfind('/')); chdir(workDir.c_str()); + + FILE* check = fopen("../Data/game_data_prepared", "r"); + if (check == NULL) { + system("open ./vcmibuilder.app"); + return 0; + } + fclose(check); #endif tlog0 << "Starting... " << std::endl; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a389728b3..68ffb11da 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -76,7 +76,7 @@ elseif(APPLE) mkdir -p ${BUNDLE_PATH}/Data/Mods/WoG && cp -r ${CMAKE_HOME_DIRECTORY}/config ${BUNDLE_PATH}/Data/config && cp -r ${CMAKE_HOME_DIRECTORY}/Mods/vcmi ${BUNDLE_PATH}/Data/Mods/vcmi && - cp ${CMAKE_HOME_DIRECTORY}/Mods/WoG/filesystem.json ${BUNDLE_PATH}/Data/Mods/WoG/filesystem.json) + cp ${CMAKE_HOME_DIRECTORY}/Mods/WoG/mod.json ${BUNDLE_PATH}/Data/Mods/WoG/mod.json) add_custom_command(TARGET vcmiclient POST_BUILD COMMAND ${MakeVCMIBundle}) else() add_executable(vcmiclient ${client_SRCS}) diff --git a/osx-vcmibuilder/innoextract b/osx-vcmibuilder/innoextract new file mode 100755 index 000000000..2a2b50dcf Binary files /dev/null and b/osx-vcmibuilder/innoextract differ diff --git a/osx-vcmibuilder/unshield b/osx-vcmibuilder/unshield new file mode 100755 index 000000000..124ff27fc Binary files /dev/null and b/osx-vcmibuilder/unshield differ diff --git a/osx-vcmibuilder/vcmibuilder.icns b/osx-vcmibuilder/vcmibuilder.icns new file mode 100644 index 000000000..a660e31c5 Binary files /dev/null and b/osx-vcmibuilder/vcmibuilder.icns differ diff --git a/osx-vcmibuilder/vcmibuilder.xcodeproj/project.pbxproj b/osx-vcmibuilder/vcmibuilder.xcodeproj/project.pbxproj new file mode 100644 index 000000000..adfa1df4d --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder.xcodeproj/project.pbxproj @@ -0,0 +1,307 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + A8467DFF16760A4E00A9F095 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8467DFE16760A4E00A9F095 /* Cocoa.framework */; }; + A8467E0916760A4E00A9F095 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A8467E0716760A4E00A9F095 /* InfoPlist.strings */; }; + A8467E0B16760A4E00A9F095 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A8467E0A16760A4E00A9F095 /* main.m */; }; + A8467E1216760A4E00A9F095 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A8467E1116760A4E00A9F095 /* AppDelegate.m */; }; + A8467E1516760A4E00A9F095 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A8467E1316760A4E00A9F095 /* MainMenu.xib */; }; + A866001516760C5B00DBFB19 /* vcmibuilder.icns in Resources */ = {isa = PBXBuildFile; fileRef = A866001416760C5B00DBFB19 /* vcmibuilder.icns */; }; + A8F36836167632DC0009832A /* innoextract in Resources */ = {isa = PBXBuildFile; fileRef = A8F36834167632DC0009832A /* innoextract */; }; + A8F36837167632DC0009832A /* unshield in Resources */ = {isa = PBXBuildFile; fileRef = A8F36835167632DC0009832A /* unshield */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + A8467DFA16760A4E00A9F095 /* vcmibuilder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vcmibuilder.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A8467DFE16760A4E00A9F095 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + A8467E0116760A4E00A9F095 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + A8467E0216760A4E00A9F095 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + A8467E0316760A4E00A9F095 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + A8467E0616760A4E00A9F095 /* vcmibuilder-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "vcmibuilder-Info.plist"; sourceTree = ""; }; + A8467E0816760A4E00A9F095 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + A8467E0A16760A4E00A9F095 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A8467E0C16760A4E00A9F095 /* vcmibuilder-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "vcmibuilder-Prefix.pch"; sourceTree = ""; }; + A8467E1016760A4E00A9F095 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A8467E1116760A4E00A9F095 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A8467E1416760A4E00A9F095 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; + A866001416760C5B00DBFB19 /* vcmibuilder.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = vcmibuilder.icns; sourceTree = ""; }; + A8F36834167632DC0009832A /* innoextract */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = innoextract; sourceTree = ""; }; + A8F36835167632DC0009832A /* unshield */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = unshield; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A8467DF716760A4E00A9F095 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A8467DFF16760A4E00A9F095 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A8467DEF16760A4E00A9F095 = { + isa = PBXGroup; + children = ( + A8F36834167632DC0009832A /* innoextract */, + A8F36835167632DC0009832A /* unshield */, + A866001416760C5B00DBFB19 /* vcmibuilder.icns */, + A8467E0416760A4E00A9F095 /* vcmibuilder */, + A8467DFD16760A4E00A9F095 /* Frameworks */, + A8467DFB16760A4E00A9F095 /* Products */, + ); + sourceTree = ""; + }; + A8467DFB16760A4E00A9F095 /* Products */ = { + isa = PBXGroup; + children = ( + A8467DFA16760A4E00A9F095 /* vcmibuilder.app */, + ); + name = Products; + sourceTree = ""; + }; + A8467DFD16760A4E00A9F095 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A8467DFE16760A4E00A9F095 /* Cocoa.framework */, + A8467E0016760A4E00A9F095 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + A8467E0016760A4E00A9F095 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + A8467E0116760A4E00A9F095 /* AppKit.framework */, + A8467E0216760A4E00A9F095 /* CoreData.framework */, + A8467E0316760A4E00A9F095 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + A8467E0416760A4E00A9F095 /* vcmibuilder */ = { + isa = PBXGroup; + children = ( + A8467E1016760A4E00A9F095 /* AppDelegate.h */, + A8467E1116760A4E00A9F095 /* AppDelegate.m */, + A8467E1316760A4E00A9F095 /* MainMenu.xib */, + A8467E0516760A4E00A9F095 /* Supporting Files */, + ); + path = vcmibuilder; + sourceTree = ""; + }; + A8467E0516760A4E00A9F095 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + A8467E0616760A4E00A9F095 /* vcmibuilder-Info.plist */, + A8467E0716760A4E00A9F095 /* InfoPlist.strings */, + A8467E0A16760A4E00A9F095 /* main.m */, + A8467E0C16760A4E00A9F095 /* vcmibuilder-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A8467DF916760A4E00A9F095 /* vcmibuilder */ = { + isa = PBXNativeTarget; + buildConfigurationList = A8467E1816760A4E00A9F095 /* Build configuration list for PBXNativeTarget "vcmibuilder" */; + buildPhases = ( + A8467DF616760A4E00A9F095 /* Sources */, + A8467DF716760A4E00A9F095 /* Frameworks */, + A8467DF816760A4E00A9F095 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = vcmibuilder; + productName = vcmibuilder; + productReference = A8467DFA16760A4E00A9F095 /* vcmibuilder.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A8467DF116760A4E00A9F095 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0450; + ORGANIZATIONNAME = "Alexey Petruchik"; + }; + buildConfigurationList = A8467DF416760A4E00A9F095 /* Build configuration list for PBXProject "vcmibuilder" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = A8467DEF16760A4E00A9F095; + productRefGroup = A8467DFB16760A4E00A9F095 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A8467DF916760A4E00A9F095 /* vcmibuilder */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A8467DF816760A4E00A9F095 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A8467E0916760A4E00A9F095 /* InfoPlist.strings in Resources */, + A8467E1516760A4E00A9F095 /* MainMenu.xib in Resources */, + A866001516760C5B00DBFB19 /* vcmibuilder.icns in Resources */, + A8F36836167632DC0009832A /* innoextract in Resources */, + A8F36837167632DC0009832A /* unshield in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A8467DF616760A4E00A9F095 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A8467E0B16760A4E00A9F095 /* main.m in Sources */, + A8467E1216760A4E00A9F095 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + A8467E0716760A4E00A9F095 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + A8467E0816760A4E00A9F095 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + A8467E1316760A4E00A9F095 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + A8467E1416760A4E00A9F095 /* en */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + A8467E1616760A4E00A9F095 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = NO; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + A8467E1716760A4E00A9F095 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = NO; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + SDKROOT = macosx; + }; + name = Release; + }; + A8467E1916760A4E00A9F095 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "vcmibuilder/vcmibuilder-Prefix.pch"; + INFOPLIST_FILE = "vcmibuilder/vcmibuilder-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + A8467E1A16760A4E00A9F095 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "vcmibuilder/vcmibuilder-Prefix.pch"; + INFOPLIST_FILE = "vcmibuilder/vcmibuilder-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A8467DF416760A4E00A9F095 /* Build configuration list for PBXProject "vcmibuilder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A8467E1616760A4E00A9F095 /* Debug */, + A8467E1716760A4E00A9F095 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A8467E1816760A4E00A9F095 /* Build configuration list for PBXNativeTarget "vcmibuilder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A8467E1916760A4E00A9F095 /* Debug */, + A8467E1A16760A4E00A9F095 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A8467DF116760A4E00A9F095 /* Project object */; +} diff --git a/osx-vcmibuilder/vcmibuilder/AppDelegate.h b/osx-vcmibuilder/vcmibuilder/AppDelegate.h new file mode 100644 index 000000000..9e7f77fb9 --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder/AppDelegate.h @@ -0,0 +1,48 @@ +#import + +@interface AppDelegate : NSObject +{ + NSString* outputDir; + NSString* tempDir; + NSString* dataDir; + + NSMutableArray* actions; + + int64_t bytesRecieved; + int64_t bytesExpected; + + BOOL installationCompleted; +} + +@property (strong) IBOutlet NSWindow *window; +@property (weak) IBOutlet NSButton *cd1Button; +@property (weak) IBOutlet NSTextField *cd1TextField; +@property (weak) IBOutlet NSButton *cd2Button; +@property (weak) IBOutlet NSTextField *cd2TextField; +@property (weak) IBOutlet NSProgressIndicator *progressIndicator; +@property (weak) IBOutlet NSTextField *progressLabel; +@property (weak) IBOutlet NSButton *installButton; +@property (weak) IBOutlet NSTextField *errorLabel; + +@property (strong) NSURLDownload* download; + +- (IBAction)selectCD1:(id)sender; +- (IBAction)selectCD2:(id)sender; +- (IBAction)install:(id)sender; + +- (void)selectFile:(NSArray*)fileTypes withTextField:(NSTextField*)textField; +- (void)showProgressText:(NSString*)text; +- (void)showErrorText:(NSString*)text; +- (void)nextAction; +- (int)runTask:(NSString*)executable withArgs:(NSArray*)args withWorkingDir:(NSString*)workingDir withPipe:(NSPipe*)pipe; + +- (void)downloadWogArchive; +- (void)unzipWogArchive; +- (void)extractGameData; +- (void)innoexctract; +- (NSString*)attachDiskImage:(NSString*)path; +- (void)unshield; +- (void)detachDiskImage:(NSString*)mountedPath; +- (void)extractionCompleted; + +@end diff --git a/osx-vcmibuilder/vcmibuilder/AppDelegate.m b/osx-vcmibuilder/vcmibuilder/AppDelegate.m new file mode 100644 index 000000000..4cd14f1f5 --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder/AppDelegate.m @@ -0,0 +1,310 @@ +#import "AppDelegate.h" + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification +{ + installationCompleted = NO; + outputDir = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/../../Data"]; + tempDir = NSTemporaryDirectory(); +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender +{ + return YES; +} + +- (void)download:(NSURLDownload*)download didReceiveResponse:(NSURLResponse*)response +{ + self->bytesRecieved = 0; + self->bytesExpected = [response expectedContentLength]; +} + +- (void)download:(NSURLDownload*)download didReceiveDataOfLength:(NSUInteger)length +{ + self->bytesRecieved += length; + [self showProgressText:[NSString stringWithFormat:@"Downloading WoG archive: %3.1f Mb / %3.1f Mb", + self->bytesRecieved / 1024.0f / 1024.0f, self->bytesExpected / 1024.0f / 1024.0f]]; +} + +- (void)download:(NSURLDownload*)download decideDestinationWithSuggestedFilename:(NSString*)filename +{ + [download setDestination:[tempDir stringByAppendingString:@"/wog.zip"] allowOverwrite:YES]; +} + +- (void)downloadDidFinish:(NSURLDownload*)download +{ + [self showProgressText:@"Downloading WoG archive: completed"]; + [self nextAction]; +} + +- (void)download:(NSURLDownload*)download didFailWithError:(NSError*)error +{ + [self showProgressText:@"Downloading WoG archive: failed"]; + [self showErrorText:[error localizedDescription]]; +} + +- (void)nextAction +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + SEL sel = NSSelectorFromString(actions[0]); + [actions removeObjectAtIndex:0]; + @try { + [self performSelector:sel]; + } + @catch (NSException* e) { + [self showErrorText:[e name]]; + } + }); +} + +- (int)runTask:(NSString*)executable withArgs:(NSArray*)args withWorkingDir:(NSString*)workingDir withPipe:(NSPipe*)pipe +{ + if (![executable hasPrefix:@"/usr/"]) { + executable = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:executable]; + } + + NSTask* task = [[NSTask alloc] init]; + [task setLaunchPath:executable]; + if (workingDir != nil) { + [task setCurrentDirectoryPath:workingDir]; + } + if (pipe != nil) { + [task setStandardOutput:pipe]; + } + [task setArguments:args]; + + [task launch]; + [task waitUntilExit]; + + return [task terminationStatus]; +} + +- (void)validateAction +{ + // Before starting anything run validations + if (![[NSFileManager defaultManager] fileExistsAtPath:[self.cd1TextField stringValue]]) { + return [self showErrorText:@"Please select existing file"]; + } + + // Show progress controls + [self.progressIndicator setHidden:NO]; + [self.progressIndicator startAnimation:self]; + [self showProgressText:@"Installing VCMI..."]; + + [self nextAction]; +} + +- (void)downloadWogArchive +{ + // First of all we need to download WoG archive + // Downloading should be done on main thread because of callbacks + dispatch_async(dispatch_get_main_queue(), ^{ + NSURL* url = [NSURL URLWithString:@"http://download.vcmi.eu/WoG/wog.zip"]; + self.download = [[NSURLDownload alloc] initWithRequest:[NSURLRequest requestWithURL:url] delegate:self]; + }); +} + +- (void)unzipWogArchive +{ + // Then we unzip downloaded WoG archive + [self showProgressText:@"Unzipping WoG archive"]; + if ([self runTask:@"/usr/bin/unzip" withArgs:@[@"-qo", [tempDir stringByAppendingString:@"/wog.zip"], @"-d", outputDir] withWorkingDir:nil withPipe:nil] != 0) { + return [self showErrorText:@"Failed to unzip WoG archive"]; + } + + [self nextAction]; +} + +- (void)extractGameData +{ + // Then we extract game data from provided iso files using unshield or from innosetup exe + if ([[self.cd1TextField stringValue] hasSuffix:@".exe"]) { + [self innoexctract]; + } else { + [self unshield]; + } + + [self nextAction]; +} + +- (void)innoexctract +{ + // Extraction via innoextact is pretty straightforward + [self showProgressText:@"Extracting game data using innoextract..."]; + if ([self runTask:@"/innoextract" withArgs:@[[self.cd1TextField stringValue]] withWorkingDir:tempDir withPipe:nil] != 0) { + [self showErrorText:@"Failed to exctract game data using innoextract"]; + } + + dataDir = [tempDir stringByAppendingString:@"/app"]; +} + +- (NSString*)attachDiskImage:(NSString*)path +{ + [self showProgressText:[NSString stringWithFormat:@"Mounting image \"%@\"", path]]; + + // Run hdiutil to mount specified disk image + NSPipe* pipe = [NSPipe pipe]; + if ([self runTask:@"/usr/bin/hdiutil" withArgs:@[@"attach", path] withWorkingDir:nil withPipe:pipe] != 0) { + [NSException raise:[NSString stringWithFormat:@"Failed to mount \"%@\"", path] format:nil]; + } + + // Capture hdiutil output to get mounted disk image filesystem path + NSFileHandle* file = [pipe fileHandleForReading]; + NSString* output = [[NSString alloc] initWithData:[file readDataToEndOfFile] encoding:NSUTF8StringEncoding]; + + NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"(/Volumes/.*)$" options:0 error:nil]; + NSTextCheckingResult* match = [regex firstMatchInString:output options:0 range:NSMakeRange(0, [output length])]; + + return [output substringWithRange:[match range]]; +} + +- (void)detachDiskImage:(NSString*)mountedPath +{ + if ([self runTask:@"/usr/bin/hdiutil" withArgs:@[@"detach", mountedPath] withWorkingDir:nil withPipe:nil] != 0) { + [NSException raise:[NSString stringWithFormat:@"Failed to unmount \"%@\"", mountedPath] format:nil]; + } +} + +- (void)unshield +{ + // In case of iso files we should mount them first + // If CD2 is not specified use the same path as for CD1 + NSString* cd1 = [self attachDiskImage:[self.cd1TextField stringValue]]; + NSString* cd2 = [[self.cd2TextField stringValue] isEqualToString:@""] ? cd1 : [self attachDiskImage:[self.cd2TextField stringValue]]; + + // Extract + [self showProgressText:@"Extracting game data using unshield..."]; + if ([self runTask:@"/unshield" withArgs:@[@"-d", tempDir, @"x", [cd1 stringByAppendingString:@"/_setup/data1.cab"]] withWorkingDir:tempDir withPipe:nil] != 0) { + return [self showErrorText:@"Failed to extract game data using unshield"]; + } + + dataDir = [tempDir stringByAppendingString:@"/Heroes3"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:dataDir]) { + // Some releases have "Program_Files" folder instead of "Heroes3" + dataDir = [tempDir stringByAppendingString:@"/Program_Files"]; + } + + // Unmount CD1. Unmount CD2 if needed + [self detachDiskImage:cd1]; + if (![cd1 isEqualToString:cd2]) { + [self detachDiskImage:cd2]; + } +} + +- (void)extractionCompleted +{ + // After game data is extracted we should move it to destination place + [self showProgressText:@"Moving items into place"]; + + NSFileManager* fileManager = [NSFileManager defaultManager]; + + [fileManager moveItemAtPath:[dataDir stringByAppendingString:@"/Data"] toPath:[outputDir stringByAppendingString:@"/Data"] error:nil]; + [fileManager moveItemAtPath:[dataDir stringByAppendingString:@"/Maps"] toPath:[outputDir stringByAppendingString:@"/Maps"] error:nil]; + + if ([fileManager fileExistsAtPath:[dataDir stringByAppendingString:@"/MP3"] isDirectory:nil]) { + [fileManager moveItemAtPath:[dataDir stringByAppendingString:@"/MP3"] toPath:[outputDir stringByAppendingString:@"/Mp3"] error:nil]; + } else { + [fileManager moveItemAtPath:[dataDir stringByAppendingString:@"/Mp3"] toPath:[outputDir stringByAppendingString:@"/Mp3"] error:nil]; + } + + // After everythin is complete we create marker file. VCMI will look for this file to exists on startup and + // will run this setup otherwise + system([[NSString stringWithFormat:@"touch %@/game_data_prepared", outputDir] UTF8String]); + + [self showProgressText:@"Installation complete"]; + [self.installButton setTitle:@"Run VCMI"]; + [self.progressIndicator stopAnimation:self]; + + [NSApp requestUserAttention:NSCriticalRequest]; + + // Hide all progress related controls + [self.progressIndicator setHidden:YES]; + [self.progressIndicator stopAnimation:self]; + [self.progressLabel setHidden:YES]; + + [self.installButton setEnabled:YES]; + installationCompleted = YES; +} + +- (void)selectFile:(NSArray*)fileTypes withTextField:(NSTextField*)textField +{ + NSOpenPanel* openPanel = [NSOpenPanel openPanel]; + [openPanel setCanChooseFiles:YES]; + [openPanel setAllowedFileTypes:fileTypes]; + [openPanel setAllowsMultipleSelection:NO]; + + if ([openPanel runModal] == NSOKButton) { + NSString* path = [[openPanel URL] path]; + [textField setStringValue:path]; + } +} + +- (IBAction)selectCD1:(id)sender +{ + [self selectFile:@[@"iso", @"exe"] withTextField:self.cd1TextField]; +} + +- (IBAction)selectCD2:(id)sender +{ + [self selectFile:@[@"iso"] withTextField:self.cd2TextField]; +} + +- (IBAction)install:(id)sender +{ + if (installationCompleted) { + // Run vcmi + system([[NSString stringWithFormat:@"open %@/../../..", [[NSBundle mainBundle] bundlePath]] UTF8String]); + [NSApp terminate: nil]; + } else { + // Run installation + [self.cd1Button setEnabled:NO]; + [self.cd2Button setEnabled:NO]; + [self.installButton setEnabled:NO]; + + actions = [NSMutableArray arrayWithObjects: + @"validateAction", + @"downloadWogArchive", + @"unzipWogArchive", + @"extractGameData", + @"extractionCompleted", + nil + ]; + + [self nextAction]; + } +} + +- (void)showProgressText:(NSString*)text +{ + // All GUI updates should be done on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + [self.progressLabel setHidden:NO]; + [self.progressLabel setStringValue:text]; + }); +} + +- (void)showErrorText:(NSString*)text +{ + // All GUI updates should be done on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + // Show error alert + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Error"]; + [alert setInformativeText:text]; + [alert beginSheetModalForWindow:self.window modalDelegate:nil didEndSelector:nil contextInfo:nil]; + + // Enable select file buttons again + [self.cd1Button setEnabled:YES]; + [self.cd2Button setEnabled:YES]; + [self.installButton setEnabled:YES]; + + // Hide all progress related controls + [self.progressIndicator setHidden:YES]; + [self.progressIndicator stopAnimation:self]; + + [self.progressLabel setHidden:YES]; + }); +} + +@end diff --git a/osx-vcmibuilder/vcmibuilder/en.lproj/InfoPlist.strings b/osx-vcmibuilder/vcmibuilder/en.lproj/InfoPlist.strings new file mode 100644 index 000000000..477b28ff8 --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/osx-vcmibuilder/vcmibuilder/en.lproj/MainMenu.xib b/osx-vcmibuilder/vcmibuilder/en.lproj/MainMenu.xib new file mode 100644 index 000000000..da95fd9ce --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder/en.lproj/MainMenu.xib @@ -0,0 +1,772 @@ + + + + 1070 + 11G63 + 2844 + 1138.51 + 569.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2844 + + + NSButton + NSButtonCell + NSCustomObject + NSMenu + NSMenuItem + NSProgressIndicator + NSTextField + NSTextFieldCell + NSView + NSWindowTemplate + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + + + vcmibuilder + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + vcmibuilder + + + + About vcmibuilder + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit vcmibuilder + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + _NSMainMenu + + + 7 + 2 + {{335, 390}, {572, 194}} + 1954021376 + VCMI Install + NSWindow + + + {572, 207} + + + 256 + + + + 12 + {{20, 83}, {483, 22}} + + + + _NS:9 + YES + + -2074083263 + 272631872 + + + LucidaGrande + 13 + 1044 + + CD1 ISO disk image or installer executable + _NS:9 + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + 3 + MAA + + + + + + + 12 + {{20, 51}, {483, 22}} + + + + _NS:9 + YES + + -2074083263 + 272631872 + + + CD2 ISO disk image (optional) + _NS:9 + + YES + + + + + + + 268 + {{17, 140}, {538, 34}} + + + + _NS:1535 + YES + + 68157504 + 272630784 + In order to complete VCMI installation you need original Heroes III: Shadow of Death
game data. Please select ISO disk image or installer executable and press Install. + + _NS:1535 + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + + + + + + 268 + {{505, 76}, {53, 32}} + + + + _NS:9 + YES + + 67108864 + 134217728 + ... + + _NS:9 + + -2038284288 + 129 + + + 200 + 25 + + + + + 268 + {{505, 44}, {53, 32}} + + + + _NS:9 + YES + + 67108864 + 134217728 + ... + + _NS:9 + + -2038284288 + 129 + + + 200 + 25 + + + + + 268 + {{461, 12}, {97, 32}} + + + + _NS:9 + YES + + 67108864 + 134217728 + Install + + _NS:9 + + -2038284288 + 129 + + + 200 + 25 + + + + + -2147483380 + {{20, 22}, {16, 16}} + + + + _NS:945 + 20746 + 100 + + + + -2147483380 + {{41, 22}, {419, 17}} + + + + _NS:1535 + YES + + 67108928 + 272631808 + Label + + _NS:1535 + + + + + + + {572, 194} + + + + + {{0, 0}, {1680, 1028}} + {572, 229} + {10000000000000, 10000000000000} + YES + + + AppDelegate + + + NSFontManager + + + + + + + terminate: + + + + 449 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + delegate + + + + 495 + + + + window + + + + 532 + + + + cd1Button + + + + 615 + + + + cd2Button + + + + 616 + + + + cd1TextField + + + + 617 + + + + cd2TextField + + + + 618 + + + + selectCD1: + + + + 619 + + + + selectCD2: + + + + 620 + + + + progressLabel + + + + 622 + + + + progressIndicator + + + + 623 + + + + installButton + + + + 624 + + + + install: + + + + 625 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + + + + + + 56 + + + + + + + + 57 + + + + + + + + + + 58 + + + + + 136 + + + + + 149 + + + + + 371 + + + + + + + + 372 + + + + + + + + + + + + + + + 420 + + + + + 494 + + + + + 536 + + + + + + + + 537 + + + + + 541 + + + + + + + + 542 + + + + + 549 + + + + + + + + 550 + + + + + 562 + + + + + + + + 563 + + + + + 569 + + + + + + + + 570 + + + + + 581 + + + + + + + + 582 + + + + + 587 + + + + + 593 + + + + + + + + 594 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin + {{380, 496}, {480, 360}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 730 + + + + + AppDelegate + NSObject + + id + id + id + + + + install: + id + + + selectCD1: + id + + + selectCD2: + id + + + + NSButton + NSTextField + NSButton + NSTextField + NSTextField + NSButton + NSProgressIndicator + NSTextField + NSWindow + + + + cd1Button + NSButton + + + cd1TextField + NSTextField + + + cd2Button + NSButton + + + cd2TextField + NSTextField + + + errorLabel + NSTextField + + + installButton + NSButton + + + progressIndicator + NSProgressIndicator + + + progressLabel + NSTextField + + + window + NSWindow + + + + IBProjectSource + ./Classes/AppDelegate.h + + + + + 0 + IBCocoaFramework + YES + 3 + + {11, 11} + {10, 3} + + + diff --git a/osx-vcmibuilder/vcmibuilder/main.m b/osx-vcmibuilder/vcmibuilder/main.m new file mode 100644 index 000000000..04d99dab9 --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder/main.m @@ -0,0 +1,6 @@ +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **)argv); +} diff --git a/osx-vcmibuilder/vcmibuilder/vcmibuilder-Info.plist b/osx-vcmibuilder/vcmibuilder/vcmibuilder-Info.plist new file mode 100644 index 000000000..0d2eff163 --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder/vcmibuilder-Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + vcmibuilder + CFBundleIdentifier + com.vcmi.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright © 2012 Alexey Petruchik. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/osx-vcmibuilder/vcmibuilder/vcmibuilder-Prefix.pch b/osx-vcmibuilder/vcmibuilder/vcmibuilder-Prefix.pch new file mode 100644 index 000000000..63c1a5e8b --- /dev/null +++ b/osx-vcmibuilder/vcmibuilder/vcmibuilder-Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'vcmibuilder' target in the 'vcmibuilder' project +// + +#ifdef __OBJC__ + #import +#endif