mirror of
https://github.com/autc04/Retro68.git
synced 2025-01-06 14:31:00 +00:00
14f5ad3728
When copying files from the system disk image to the new disk image, preserve all attributes (creation date, modification date, bundle bit, locked bit, etc.), except clear the inited bit so that the Finder chooses new icon positions, otherwise (under System 6 at least) the icons pile up on top of each other. Preserving the bundle bit in particular tells the Finder to look in the file for icon information when building the desktop database, without which the files all show generic icons in the Finder.
599 lines
18 KiB
C++
599 lines
18 KiB
C++
#include "MiniVMac.h"
|
|
#include "Launcher.h"
|
|
#include "Utilities.h"
|
|
#include "BinaryIO.h"
|
|
#include "ResourceFile.h"
|
|
|
|
extern "C" {
|
|
#include "hfs.h"
|
|
#include <arpa/inet.h>
|
|
#define HAVE_MKTIME
|
|
#ifdef HAVE_MKTIME
|
|
#include <time.h>
|
|
#endif
|
|
}
|
|
|
|
#include <iostream>
|
|
#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;
|
|
|
|
namespace po = boost::program_options;
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
/* Adapted from http://sebastien.kirche.free.fr/python_stuff/MacOS-aliases.txt */
|
|
typedef struct
|
|
{
|
|
uint16_t type; /* type of data */
|
|
uint16_t size; /* length of variable-length data */
|
|
char data[]; /* actual data */
|
|
} VarData;
|
|
|
|
#pragma pack(push,1)
|
|
typedef struct
|
|
{
|
|
/* Type Code: alis */
|
|
char userType[4] = {0, 0, 0, 0}; /* for application use, can be zeros */
|
|
uint16_t size; /* alias record size, including variable-length data */
|
|
int16_t version = htons(2); /* alias version, current 2 */
|
|
int16_t type = htons(0); /* file = 0, directory = 1 */
|
|
char volumeNameSize; /* length of volume name */
|
|
char volumeName[27]; /* volume name padded with null bytes */
|
|
uint32_t volumeCreationDate; /* volume creation date, seconds since 1904 */
|
|
uint16_t volumeSig = htons(0x4244); /* volume signature MFS = RW, HFS = BD */
|
|
int16_t volumeType = htons(5); /* [HD] = 0, Foreign = 1, Floppy: 400K, 800K, 1400K = 2-4, OtherEjectable = 5 */
|
|
int32_t parentDirID; /* parent directory ID, 0 for relative searches */
|
|
char fileNameSize; /* length of file or directory name */
|
|
char fileName[63]; /* file or directory name padded with null bytes */
|
|
int32_t fileNum; /* file number or directory ID */
|
|
uint32_t fileCreationDate; /* file or directory creation date, seconds since 1904 */
|
|
char typeCode[4]; /* file type */
|
|
char creatorCode[4]; /* file's creator */
|
|
int16_t nlvlFrom = htons(0); /* next level up from alias, used in relative searches */
|
|
int16_t nlvlTo = htons(0); /* next level down to target, ditto */
|
|
uint32_t volumeAttr = htonl(0); /* various flags (locked, ejectable), see link above */
|
|
int16_t volumeFSID = htons(0); /* file system ID for the volume, 0 for MFS, HFS */
|
|
int16_t unused = htons(0);
|
|
uint32_t unused1 = htonl(0);
|
|
uint32_t unused2 = htonl(0);
|
|
VarData vdata[1] = { /* variable-length data, see link above */
|
|
{htons(0xFFFF), htons(0)} /* end of variable-length data marker, length 0 */
|
|
};
|
|
} AliasData;
|
|
#pragma pack(pop)
|
|
|
|
|
|
/* Begin code copied from libhfs/data.c */
|
|
|
|
# define TIMEDIFF 2082844800UL
|
|
|
|
static
|
|
time_t tzdiff = -1;
|
|
|
|
/*
|
|
* NAME: calctzdiff()
|
|
* DESCRIPTION: calculate the timezone difference between local time and UTC
|
|
*/
|
|
static
|
|
void calctzdiff(void)
|
|
{
|
|
# ifdef HAVE_MKTIME
|
|
|
|
time_t t;
|
|
int isdst;
|
|
struct tm tm;
|
|
const struct tm *tmp;
|
|
|
|
time(&t);
|
|
isdst = localtime(&t)->tm_isdst;
|
|
|
|
tmp = gmtime(&t);
|
|
if (tmp)
|
|
{
|
|
tm = *tmp;
|
|
tm.tm_isdst = isdst;
|
|
|
|
tzdiff = t - mktime(&tm);
|
|
}
|
|
else
|
|
tzdiff = 0;
|
|
|
|
# else
|
|
|
|
tzdiff = 0;
|
|
|
|
# endif
|
|
}
|
|
|
|
/*
|
|
* NAME: data->mtime()
|
|
* DESCRIPTION: convert local time to MacOS time
|
|
*/
|
|
unsigned long d_mtime(time_t ltime)
|
|
{
|
|
if (tzdiff == -1)
|
|
calctzdiff();
|
|
|
|
return (unsigned long) (ltime + tzdiff) + TIMEDIFF;
|
|
}
|
|
|
|
/* End code copied from libhfs/data.c */
|
|
|
|
|
|
class MiniVMacLauncher : public Launcher
|
|
{
|
|
fs::path imagePath;
|
|
fs::path systemImage;
|
|
fs::path vmacDir;
|
|
fs::path vmacPath;
|
|
#ifdef __APPLE__
|
|
bool vmacIsAppBundle;
|
|
#endif
|
|
|
|
hfsvol *sysvol;
|
|
hfsvol *vol;
|
|
std::unique_ptr<Resources> systemRes;
|
|
|
|
void CopySystemFile(const std::string& fn, bool required);
|
|
void ReadSystemResources(const std::string& systemFileName);
|
|
uint16_t GetSystemVersion();
|
|
std::string FindFolder(const std::string& folderType);
|
|
void MakeAlias(const std::string& dest, const std::string& src);
|
|
fs::path ConvertImage(const fs::path& path);
|
|
public:
|
|
MiniVMacLauncher(po::variables_map& options);
|
|
virtual ~MiniVMacLauncher();
|
|
|
|
virtual bool Go(int timeout = 0);
|
|
virtual void DumpOutput();
|
|
};
|
|
|
|
|
|
/*
|
|
* Recursive directory copy from https://stackoverflow.com/a/39146566
|
|
*/
|
|
#ifdef __APPLE__
|
|
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})
|
|
{
|
|
auto path = dirEnt.path();
|
|
auto relativePathStr = path.string().substr(sourceDir.string().size());
|
|
auto dst = destinationDir / relativePathStr;
|
|
|
|
if(fs::is_directory(path))
|
|
fs::create_directory(dst);
|
|
else
|
|
fs::copy_file(path, dst);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
fs::path MiniVMacLauncher::ConvertImage(const fs::path& path)
|
|
{
|
|
fs::ifstream in(path);
|
|
|
|
in.seekg(0x40);
|
|
uint32_t diskCopyLength = longword(in);
|
|
|
|
in.seekg(0x52);
|
|
uint16_t diskCopySig = word(in);
|
|
|
|
//in.seekg(1024);
|
|
//uint16_t rawHFSSig = word(in);
|
|
|
|
in.seekg(0x54 + 1024);
|
|
uint16_t diskCopyHFSSig = word(in);
|
|
|
|
in.seekg(0, std::ios::end);
|
|
uint32_t actualSize = in.tellg();
|
|
|
|
if(diskCopySig == 0x0100 && actualSize == diskCopyLength + 0x54
|
|
&& diskCopyLength % 512 == 0 && diskCopyHFSSig == 0x4244)
|
|
{
|
|
auto outPath = tempDir / fs::unique_path();
|
|
|
|
fs::ofstream out(outPath);
|
|
|
|
in.seekg(0x54);
|
|
|
|
char buf[4096];
|
|
|
|
while(in.read(buf, sizeof(buf)), in.gcount() > 0)
|
|
out.write(buf, in.gcount());
|
|
|
|
return outPath;
|
|
}
|
|
else
|
|
return path;
|
|
}
|
|
|
|
|
|
MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options)
|
|
: Launcher(options),
|
|
sysvol(NULL), vol(NULL)
|
|
{
|
|
vmacDir = fs::absolute( options["minivmac-dir"].as<std::string>() );
|
|
vmacPath = fs::absolute( options["minivmac-path"].as<std::string>(), vmacDir );
|
|
|
|
fs::path dataDir;
|
|
#ifdef __APPLE__
|
|
vmacIsAppBundle = vmacPath.extension().string() == ".app";
|
|
if(vmacIsAppBundle)
|
|
{
|
|
dataDir = tempDir / "minivmac.app" / "Contents" / "mnvm_dat";
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
dataDir = tempDir;
|
|
}
|
|
imagePath = dataDir / "disk1.dsk";
|
|
|
|
systemImage = fs::absolute(options["system-image"].as<std::string>(), vmacDir);
|
|
systemImage = ConvertImage(systemImage);
|
|
|
|
std::vector<unsigned char> bootblock1(1024);
|
|
fs::ifstream(systemImage).read((char*) bootblock1.data(), 1024);
|
|
|
|
if(bootblock1[0] != 'L' || bootblock1[1] != 'K' || bootblock1[0xA] > 15)
|
|
throw std::runtime_error("Not a bootable Mac disk image: " + systemImage.string());
|
|
|
|
sysvol = hfs_mount(systemImage.string().c_str(),0, HFS_MODE_RDONLY);
|
|
assert(sysvol);
|
|
hfsvolent ent;
|
|
hfs_vstat(sysvol, &ent);
|
|
hfs_setcwd(sysvol, ent.blessed);
|
|
|
|
string systemFileName(bootblock1.begin() + 0xB, bootblock1.begin() + 0xB + bootblock1[0xA]);
|
|
ReadSystemResources(systemFileName);
|
|
uint16_t sysver = GetSystemVersion();
|
|
|
|
bool usesAutQuit7 = (sysver >= 0x700);
|
|
std::string optionsKey = usesAutQuit7 ? "autquit7-image" : "autoquit-image";
|
|
if(options.count(optionsKey) == 0)
|
|
{
|
|
std::ostringstream str;
|
|
str << "'" << optionsKey << "' not configured for Mini vMac and System version " << (sysver >> 8);
|
|
throw std::runtime_error(str.str());
|
|
}
|
|
|
|
fs::path autoquitImage = fs::absolute(options[optionsKey].as<std::string>(), vmacDir);
|
|
autoquitImage = ConvertImage(autoquitImage);
|
|
|
|
/*
|
|
Copy over the entire Mini vMac program.
|
|
Mini vMac looks for ROM (vMac.ROM) and disk images (disk1.dsk)
|
|
in the directory next to its binary or in the case of the Mac
|
|
version in the mnvm_dat directory in the Contents directory in
|
|
the app bundle.
|
|
The Mac version also ignores command line arguments.
|
|
Having our own copy in our temp directory is just simpler.
|
|
It is five times smaller than System 6, so this really does not
|
|
matter.
|
|
*/
|
|
fs::path vmacCopy;
|
|
#ifdef __APPLE__
|
|
if(vmacIsAppBundle)
|
|
{
|
|
vmacCopy = tempDir / "minivmac.app";
|
|
copyDirectoryRecursively(vmacPath, vmacCopy);
|
|
vmacPath = vmacCopy;
|
|
boost::filesystem::create_directories(dataDir);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
vmacCopy = tempDir / "minivmac";
|
|
fs::copy(vmacPath, vmacCopy);
|
|
vmacPath = vmacCopy;
|
|
}
|
|
|
|
int size = 5000*1024;
|
|
|
|
fs::ofstream(imagePath, std::ios::binary | std::ios::trunc).seekp(size-1).put(0);
|
|
hfs_format(imagePath.string().c_str(), 0, 0, "SysAndApp", 0, NULL);
|
|
|
|
if(!usesAutQuit7)
|
|
{
|
|
std::string finderName = std::string("AutoQuit");
|
|
bootblock1[0x1A] = finderName.size();
|
|
memcpy(&bootblock1[0x1B], finderName.c_str(), finderName.size());
|
|
bootblock1[0x5A] = 3;
|
|
memcpy(&bootblock1[0x5B],"App", 3);
|
|
}
|
|
fs::fstream(imagePath, std::ios::in | std::ios::out | std::ios::binary)
|
|
.write((const char*) bootblock1.data(), 1024);
|
|
|
|
|
|
vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDWR);
|
|
assert(vol);
|
|
|
|
hfs_vstat(vol, &ent);
|
|
ent.blessed = hfs_getcwd(vol);
|
|
hfs_vsetattr(vol, &ent);
|
|
|
|
|
|
|
|
CopySystemFile(systemFileName, true);
|
|
string debuggerFileName(bootblock1.begin() + 0x2B, bootblock1.begin() + 0x2B + bootblock1[0x2A]);
|
|
CopySystemFile(debuggerFileName, false);
|
|
|
|
if (usesAutQuit7)
|
|
{
|
|
string finderFileName(bootblock1.begin() + 0x1B, bootblock1.begin() + 0x1B + bootblock1[0x1A]);
|
|
CopySystemFile(finderFileName, true);
|
|
CopySystemFile("System 7.5 Update", false);
|
|
std::string extensionsFolderName = FindFolder("extn");
|
|
if(hfs_chdir(sysvol, extensionsFolderName.c_str()) != -1)
|
|
{
|
|
hfs_mkdir(vol, extensionsFolderName.c_str());
|
|
if(hfs_chdir(vol, extensionsFolderName.c_str()) != -1)
|
|
{
|
|
CopySystemFile("Appearance Extension", false);
|
|
CopySystemFile("System 7 Tuner", false);
|
|
CopySystemFile("System Update", false);
|
|
hfs_chdir(vol, "::");
|
|
}
|
|
hfs_chdir(sysvol, "::");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CopySystemFile("32-Bit QuickDraw", false);
|
|
CopySystemFile("TrueType\xaa 1.0", false);
|
|
}
|
|
|
|
{
|
|
std::ostringstream rsrcOut;
|
|
app.resources.writeFork(rsrcOut);
|
|
std::string rsrc = rsrcOut.str();
|
|
std::string& data = app.data;
|
|
|
|
hfsfile *file = hfs_create(vol, "App","APPL","????");
|
|
hfs_setfork(file, 0);
|
|
hfs_write(file, data.data(), data.size());
|
|
hfs_setfork(file, 1);
|
|
hfs_write(file, rsrc.data(), rsrc.size());
|
|
hfs_close(file);
|
|
}
|
|
|
|
hfs_umount(sysvol);
|
|
sysvol = hfs_mount(autoquitImage.string().c_str(),0, HFS_MODE_RDONLY);
|
|
if(!sysvol)
|
|
throw std::runtime_error("Cannot open disk image: " + autoquitImage.string());
|
|
assert(sysvol);
|
|
if (usesAutQuit7)
|
|
{
|
|
CopySystemFile("AutQuit7", true);
|
|
MakeAlias("AutQuit7 alias", "AutQuit7");
|
|
std::string startupItemsFolderName = FindFolder("strt");
|
|
hfs_mkdir(vol, startupItemsFolderName.c_str());
|
|
hfs_rename(vol, "AutQuit7 alias", startupItemsFolderName.c_str());
|
|
}
|
|
else
|
|
{
|
|
CopySystemFile("AutoQuit", true);
|
|
}
|
|
|
|
{
|
|
hfsfile *file = hfs_create(vol, "out", "TEXT", "MPS ");
|
|
hfs_close(file);
|
|
}
|
|
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,
|
|
dataDir / 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 version of Mini vMac that is not emulating
|
|
// a Macintosh Plus and has named the ROM file accordingly.
|
|
|
|
// To be on the safe side, provide both the user-specified name and
|
|
// the standard vMac.ROM.
|
|
|
|
fs::create_symlink(
|
|
romFile,
|
|
dataDir / "vMac.ROM" );
|
|
}
|
|
}
|
|
|
|
MiniVMacLauncher::~MiniVMacLauncher()
|
|
{
|
|
if(sysvol)
|
|
hfs_umount(sysvol);
|
|
if(vol)
|
|
hfs_umount(vol);
|
|
|
|
}
|
|
|
|
void MiniVMacLauncher::CopySystemFile(const std::string &fn, bool required)
|
|
{
|
|
hfsdirent fileent;
|
|
if(hfs_stat(sysvol, fn.c_str(), &fileent) < 0)
|
|
{
|
|
if(required)
|
|
throw std::runtime_error(string("File ") + fn + " not found in disk image");
|
|
else
|
|
return;
|
|
}
|
|
hfsfile *in = hfs_open(sysvol, fn.c_str());
|
|
hfsfile *out = hfs_create(vol, fn.c_str(), fileent.u.file.type,fileent.u.file.creator);
|
|
|
|
std::vector<uint8_t> buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize));
|
|
hfs_setfork(in, 0);
|
|
hfs_setfork(out, 0);
|
|
hfs_read(in, buffer.data(), fileent.u.file.dsize);
|
|
hfs_write(out, buffer.data(), fileent.u.file.dsize);
|
|
hfs_setfork(in, 1);
|
|
hfs_setfork(out, 1);
|
|
hfs_read(in, buffer.data(), fileent.u.file.rsize);
|
|
hfs_write(out, buffer.data(), fileent.u.file.rsize);
|
|
fileent.fdflags &= ~HFS_FNDR_HASBEENINITED;
|
|
hfs_fsetattr(out, &fileent);
|
|
hfs_close(in);
|
|
hfs_close(out);
|
|
}
|
|
|
|
void MiniVMacLauncher::MakeAlias(const std::string& dest, const std::string& src)
|
|
{
|
|
hfsdirent ent;
|
|
hfsvolent vent;
|
|
|
|
hfs_stat(vol, src.c_str(), &ent);
|
|
hfs_vstat(vol, &vent);
|
|
|
|
AliasData alias;
|
|
alias.size = htons(sizeof(AliasData));
|
|
alias.volumeNameSize = strlen(vent.name);
|
|
memcpy(&(alias.volumeName), vent.name, alias.volumeNameSize);
|
|
memset(&(alias.volumeName[0]) + alias.volumeNameSize, 0, 27 - alias.volumeNameSize);
|
|
alias.volumeCreationDate = htonl(d_mtime(vent.crdate));
|
|
alias.parentDirID = htonl(ent.parid);
|
|
alias.fileNameSize = strlen(ent.name);
|
|
memcpy(&(alias.fileName), ent.name, alias.fileNameSize);
|
|
memset(&(alias.fileName[0]) + alias.fileNameSize, 0, 63 - alias.fileNameSize);
|
|
alias.fileNum = htonl(ent.cnid);
|
|
alias.fileCreationDate = htonl(d_mtime(ent.crdate));
|
|
memcpy(&(alias.typeCode), ent.u.file.type, 4);
|
|
memcpy(&(alias.creatorCode), ent.u.file.creator, 4);
|
|
|
|
std::ostringstream roalias;
|
|
Resources res;
|
|
res.addResource(Resource("alis", 0, std::string((char*)&alias, sizeof(AliasData))));
|
|
res.writeFork(roalias);
|
|
std::string ralias = roalias.str();
|
|
|
|
hfsfile *falias = hfs_create(vol, dest.c_str(), "adrp", ent.u.file.creator);
|
|
hfs_setfork(falias, 1);
|
|
hfs_write(falias, ralias.data(), ralias.size());
|
|
hfs_fstat(falias, &ent);
|
|
ent.fdflags |= HFS_FNDR_ISALIAS;
|
|
hfs_fsetattr(falias, &ent);
|
|
hfs_close(falias);
|
|
}
|
|
|
|
|
|
void MiniVMacLauncher::ReadSystemResources(const std::string& systemFileName)
|
|
{
|
|
hfsdirent fileent;
|
|
hfs_stat(sysvol, systemFileName.c_str(), &fileent);
|
|
hfsfile* system = hfs_open(sysvol, systemFileName.c_str());
|
|
std::vector<uint8_t> buffer(fileent.u.file.rsize);
|
|
hfs_setfork(system, 1);
|
|
hfs_read(system, buffer.data(), fileent.u.file.rsize);
|
|
hfs_close(system);
|
|
std::istringstream systemResStream(std::string((char*)buffer.data(), buffer.size()));
|
|
systemRes = std::make_unique<Resources>(systemResStream);
|
|
}
|
|
|
|
|
|
uint16_t MiniVMacLauncher::GetSystemVersion()
|
|
{
|
|
Resource vers = systemRes->resources[ResRef('vers', 1)];
|
|
return (uint16_t)vers.getData()[0] << 8 | vers.getData()[1];
|
|
}
|
|
|
|
|
|
std::string MiniVMacLauncher::FindFolder(const std::string& folderType)
|
|
{
|
|
Resource fld = systemRes->resources[ResRef('fld#', 0)];
|
|
size_t i = 0;
|
|
while (i < fld.getData().size())
|
|
{
|
|
unsigned char len = fld.getData()[i + 7];
|
|
if (fld.getData().substr(i, 4) == folderType)
|
|
return fld.getData().substr(i + 8, len);
|
|
i += 8 + len + len % 2;
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
|
|
bool MiniVMacLauncher::Go(int timeout)
|
|
{
|
|
fs::current_path(tempDir);
|
|
#ifdef __APPLE__
|
|
if(vmacIsAppBundle)
|
|
return ChildProcess("open", {"-nWa", vmacPath.string()}, timeout) == 0;
|
|
#endif
|
|
return ChildProcess(vmacPath.string(), {}, timeout) == 0;
|
|
}
|
|
|
|
void MiniVMacLauncher::DumpOutput()
|
|
{
|
|
vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDONLY);
|
|
hfsdirent fileent;
|
|
int statres = hfs_stat(vol, "out", &fileent);
|
|
if(statres)
|
|
return;
|
|
|
|
hfsfile *out = hfs_open(vol, "out");
|
|
if(!out)
|
|
return;
|
|
std::vector<char> buffer(fileent.u.file.dsize);
|
|
hfs_setfork(out, 0);
|
|
hfs_read(out, buffer.data(), fileent.u.file.dsize);
|
|
hfs_close(out);
|
|
std::cout << string(buffer.begin(), buffer.end());
|
|
hfs_umount(vol); vol = NULL;
|
|
}
|
|
|
|
|
|
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")
|
|
("autquit7-image", po::value<std::string>(),"path to autquit7 disk image, available from the minivmac web site")
|
|
;
|
|
}
|
|
|
|
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") + options.count("autquit7-image") > 0;
|
|
}
|
|
|
|
std::unique_ptr<Launcher> MiniVMac::MakeLauncher(variables_map &options)
|
|
{
|
|
return std::unique_ptr<Launcher>(new MiniVMacLauncher(options));
|
|
}
|