From 39112683a3eb7cad5f1170fb51e20dd09b82a790 Mon Sep 17 00:00:00 2001 From: Wolfgang Thaller Date: Tue, 3 Oct 2017 22:26:24 +0200 Subject: [PATCH] LaunchAPPL: make minivmac backend work with the mac version --- LaunchAPPL/LaunchAPPL.cfg.example | 26 ++++--- LaunchAPPL/Launcher.cc | 4 + LaunchAPPL/MiniVMac.cc | 125 ++++++++++++++++++++++++++++-- 3 files changed, 136 insertions(+), 19 deletions(-) diff --git a/LaunchAPPL/LaunchAPPL.cfg.example b/LaunchAPPL/LaunchAPPL.cfg.example index 5bb7e0780a..4da1ff0d01 100644 --- a/LaunchAPPL/LaunchAPPL.cfg.example +++ b/LaunchAPPL/LaunchAPPL.cfg.example @@ -6,7 +6,7 @@ # ########### Classic Environment # If you are on a PowerPC Mac running Tiger (10.4), - # uncomment the following to use the Classic Environment: + # uncomment the following to use the Classic Environment: # emulator = classic @@ -14,7 +14,7 @@ # ########### Carbon on Mac OS X (native PowerPC or Rosetta) # If you are on any Mac running Snow Leopard (10.6) or earlier - # and you are developing Carbon applications, use the following: + # and you are developing Carbon applications, use the following: # emulator = carbon @@ -22,21 +22,25 @@ # ########### Mini vMac (old 68K Macs) # To use Mini vMac with LaunchAPPL, you need to supply the ROM file, - # a system disk image, and a download of autoquit from the minivmac web - # site, currently at - # http://www.gryphel.com/c/minivmac/extras/autoquit/index.html - # LaunchAPPL does not currently support MultiFinder or System 7. + # a system disk image, and a download of autoquit from the minivmac web + # site, currently at + # http://www.gryphel.com/c/minivmac/extras/autoquit/index.html + # LaunchAPPL does not currently support MultiFinder or System 7. # Fill in the information below and uncomment the lines: # emulator = minivmac - # The directory containing vMac.ROM - # All other paths relevant to Mini vMac are relative to this directory. + # All minivmac related paths are specified relative to minivmac-dir: # minivmac-dir = /path/to/directory/with/vMac.ROM/ # First, we need Mini vMac itself: # minivmac-path = ./Mini vMac + # On Macs, specify the path of the application bundle, not the executable inside it: +# minivmac-path = ./Mini vMac.app + + # A ROM file: +# minivmac-rom = ./vMac.ROM # Next, a system disk image (System 6 or earlier) # system-image = ./System.dsk @@ -51,7 +55,7 @@ # emulator = executor # If Executor is in your PATH and the SystemFolder environment variable - # is already set up, nothing else is required. + # is already set up, nothing else is required. # in case it's somewhere else: # executor-path = /usr/local/bin/executor @@ -60,8 +64,8 @@ # executor-system-folder = /path/to/ExecutorVolume/System Folder # Pass more options to Executor. - # Note that each "word" needs to be specified separately: -# executor-option = -size # emulated screen size + # Note that each "word" needs to be specified separately: +# executor-option = -size # emulated screen size # executor-option = 1600x1200 # executor-option = -appearance # uncommenting these two lines diff --git a/LaunchAPPL/Launcher.cc b/LaunchAPPL/Launcher.cc index f5b568f2ec..3f21a2ec47 100644 --- a/LaunchAPPL/Launcher.cc +++ b/LaunchAPPL/Launcher.cc @@ -73,6 +73,10 @@ int Launcher::ChildProcess(string program, vector args, int timeout) { execvp(argv[0], const_cast (argv.data())); perror("exec failed"); + std::cerr << "Tried to execute: " << program; + for(auto a : args) + std::cerr << " " << a; + std::cerr << std::endl; _exit(1); } diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index 611148f3d7..6a564f6f85 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -9,6 +9,12 @@ extern "C" { #include #include + +#ifdef __APPLE__ +#define ResType MacResType +#include +#endif + namespace fs = boost::filesystem; using std::string; using std::vector; @@ -36,11 +42,37 @@ public: }; +/* + * Recursive directory copy from https://stackoverflow.com/a/39146566 + */ +static void copyDirectoryRecursively(const fs::path& sourceDir, const fs::path& destinationDir) +{ + if (!fs::exists(sourceDir) || !fs::is_directory(sourceDir)) + { + throw std::runtime_error("Source directory " + sourceDir.string() + " does not exist or is not a directory"); + } + if (fs::exists(destinationDir)) + { + throw std::runtime_error("Destination directory " + destinationDir.string() + " already exists"); + } + if (!fs::create_directory(destinationDir)) + { + throw std::runtime_error("Cannot create destination directory " + destinationDir.string()); + } + + for (const auto& dirEnt : fs::recursive_directory_iterator{sourceDir}) + { + const auto& path = dirEnt.path(); + auto relativePathStr = path.lexically_relative(sourceDir); + fs::copy(path, destinationDir / relativePathStr); + } +} + MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) : Launcher(options), sysvol(NULL), vol(NULL) { - imagePath = tempDir / "image.dsk"; + imagePath = tempDir / "disk1.dsk"; vmacDir = fs::absolute( options["minivmac-dir"].as() ); vmacPath = fs::absolute( options["minivmac-path"].as(), vmacDir ); @@ -119,6 +151,86 @@ MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) hfs_umount(sysvol); sysvol = NULL; hfs_umount(vol); vol = NULL; + + fs::path romFile = fs::absolute( options["minivmac-rom"].as(), vmacDir ); + + fs::create_symlink( + romFile, + tempDir / romFile.filename() ); + + if(romFile.filename() != "vMac.ROM") + { + // If the ROM file is not named vMac.ROM, this might be for two different + // reasons. + // 1. The user didn't bother to rename it to the correct "vMac.ROM" + // 2. The user is using a MacII version of Mini vMac and has named the + // ROM file MacII.ROM on purpose. + + // To be on the safe side, provide both the user-specified name and + // the standard vMac.ROM. + + fs::create_symlink( + romFile, + tempDir / romFile.filename() ); + } + +#ifdef __APPLE__ + /* + A special case for the Mac. + + The Mac build of Mini vMac does not look for files (vMac.ROM, disk1.dsk) + in the current directory, but rather in the parent directory + of the .app bundle. + + Also, it ignores command line arguments. + + So we just copy the entire application bundle over to our temporary + directory. It is five times smaller than System 6, so this really does not + matter. + */ + if(vmacPath.extension().string() == ".app") + { + fs::path appPath = tempDir / "minivmac.app"; + + copyDirectoryRecursively( vmacPath, appPath ); + + // The following 30 lines of code should rather be written as: + // vmacPath = appPath / "Contents" / "MacOS" / Bundle(appPath).getExecutablePath(); + // But this is CoreFoundation, so it's a tiny little bit more verbose: + + CFStringRef appPathCF + = CFStringCreateWithCString( + kCFAllocatorDefault, appPath.string().c_str(), kCFStringEncodingUTF8); + CFURLRef bundleURL = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, appPathCF, kCFURLPOSIXPathStyle, true); + + CFBundleRef bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL ); + + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + CFStringRef executablePath = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle); + + if(const char *ptr = CFStringGetCStringPtr(executablePath, kCFURLPOSIXPathStyle)) + { + vmacPath = string(ptr); + } + else + { + vector buffer( + CFStringGetMaximumSizeForEncoding( + CFStringGetLength(executablePath), kCFStringEncodingUTF8) + 1); + CFStringGetCString(executablePath, buffer.data(), buffer.size(), kCFStringEncodingUTF8); + vmacPath = string(buffer.data()); + } + vmacPath = appPath / "Contents" / "MacOS" / vmacPath; + + CFRelease(appPathCF); + CFRelease(bundleURL); + CFRelease(bundle); + CFRelease(executableURL); + CFRelease(executablePath); + } +#endif } MiniVMacLauncher::~MiniVMacLauncher() @@ -159,17 +271,12 @@ void MiniVMacLauncher::CopySystemFile(const std::string &fn, bool required) bool MiniVMacLauncher::Go(int timeout) { - fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as() ); - std::string minivmacpath = options["minivmac-path"].as(); - - fs::current_path(minivmacdir); - - return ChildProcess(minivmacpath, { imagePath.string() }, timeout) == 0; + fs::current_path(tempDir); + return ChildProcess(vmacPath.string(), {}, timeout) == 0; } void MiniVMacLauncher::DumpOutput() { - sleep(1); vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDONLY); hfsdirent fileent; int statres = hfs_stat(vol, "out", &fileent); @@ -191,6 +298,7 @@ void MiniVMac::GetOptions(options_description &desc) desc.add_options() ("minivmac-dir", po::value(),"directory containing vMac.ROM") ("minivmac-path", po::value()->default_value("./minivmac"),"relative path to minivmac") + ("minivmac-rom", po::value()->default_value("./vMac.ROM"),"minivmac ROM file") ("system-image", po::value(),"path to disk image with system") ("autoquit-image", po::value(),"path to autoquit disk image, available from the minivmac web site") ; @@ -200,6 +308,7 @@ bool MiniVMac::CheckOptions(variables_map &options) { return options.count("minivmac-path") != 0 && options.count("minivmac-dir") != 0 + && options.count("minivmac-rom") != 0 && options.count("system-image") != 0 && options.count("autoquit-image") != 0; }