LaunchAPPL: make minivmac backend work with the mac version

This commit is contained in:
Wolfgang Thaller 2017-10-03 22:26:24 +02:00
parent 5808ab19f6
commit 39112683a3
3 changed files with 136 additions and 19 deletions

View File

@ -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

View File

@ -73,6 +73,10 @@ int Launcher::ChildProcess(string program, vector<string> args, int timeout)
{
execvp(argv[0], const_cast<char* const *> (argv.data()));
perror("exec failed");
std::cerr << "Tried to execute: " << program;
for(auto a : args)
std::cerr << " " << a;
std::cerr << std::endl;
_exit(1);
}

View File

@ -9,6 +9,12 @@ extern "C" {
#include <fstream>
#include <boost/filesystem/fstream.hpp>
#ifdef __APPLE__
#define ResType MacResType
#include <CoreFoundation/CoreFoundation.h>
#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<std::string>() );
vmacPath = fs::absolute( options["minivmac-path"].as<std::string>(), 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<std::string>(), 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<char> 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>() );
std::string minivmacpath = options["minivmac-path"].as<std::string>();
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<std::string>(),"directory containing vMac.ROM")
("minivmac-path", po::value<std::string>()->default_value("./minivmac"),"relative path to minivmac")
("minivmac-rom", po::value<std::string>()->default_value("./vMac.ROM"),"minivmac ROM file")
("system-image", po::value<std::string>(),"path to disk image with system")
("autoquit-image", po::value<std::string>(),"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;
}