diff --git a/AutomatedTests/CMakeLists.txt b/AutomatedTests/CMakeLists.txt index 4378cddb18..f50aeba184 100644 --- a/AutomatedTests/CMakeLists.txt +++ b/AutomatedTests/CMakeLists.txt @@ -1,20 +1,27 @@ enable_testing() +set(RETRO68_TEST_CONFIG "-eminivmac" "-t4" "--minivmac-dir=/home/wolfgang/Emulators" + "--system-image=/home/wolfgang/Emulators/baresystem.dsk" + "--autoquit-image=/home/wolfgang/Emulators/vmac extras/autoquit-1.1.1.dsk") + + add_application(ReallyEmpty ReallyEmpty.c) -add_test(NAME ReallyEmpty COMMAND LaunchAPPL -e ReallyEmpty.bin) +if(CMAKE_SYSTEM_NAME MATCHES Retro68) + set_target_properties(ReallyEmpty PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-single") +endif() +add_test(NAME ReallyEmpty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} ReallyEmpty.bin) add_application(Empty Empty.c) -add_test(NAME Empty COMMAND LaunchAPPL -e Empty.bin) +add_test(NAME Empty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Empty.bin) add_application(File File.c) add_test(NAME File - COMMAND LaunchAPPL -e File.bin --logfile=out -t5 + COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} File.bin ) set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK") -add_application(Timeout Timeout.c) -add_test(NAME Timeout - COMMAND sh -c "! LaunchAPPL -e Timeout.bin --logfile=out -t2" - ) -set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One;Two" - FAIL_REGULAR_EXPRESSION "Three") + add_application(Log Log.c) + add_test(NAME Log + COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Log.bin + ) + set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree") diff --git a/AutomatedTests/Log.c b/AutomatedTests/Log.c index ec6624054c..8c216dde56 100644 --- a/AutomatedTests/Log.c +++ b/AutomatedTests/Log.c @@ -4,8 +4,6 @@ int main() { TEST_LOG_SIZED("One",3); TEST_LOG_SIZED("Two",3); - for(;;) - ; TEST_LOG_SIZED("Three",5); return 0; } diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 14f268d3fd..4dfa3d1e50 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -1,6 +1,13 @@ find_package(Boost COMPONENTS filesystem program_options) -add_executable(LaunchAPPL LaunchAPPL.cc bootblock.c) +add_executable(LaunchAPPL + LaunchAPPL.cc bootblock.c + + LaunchMethod.h LaunchMethod.cc + Launcher.h Launcher.cc + + Executor.h Executor.cc + MiniVMac.h MiniVMac.cc) target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR}) target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES}) diff --git a/LaunchAPPL/Executor.cc b/LaunchAPPL/Executor.cc new file mode 100644 index 0000000000..8c9b7d4cc2 --- /dev/null +++ b/LaunchAPPL/Executor.cc @@ -0,0 +1,48 @@ +#include "Executor.h" +#include "Launcher.h" + +namespace po = boost::program_options; + +class ExecutorLauncher : public Launcher +{ +public: + ExecutorLauncher(po::variables_map& options); + virtual ~ExecutorLauncher(); + + virtual bool Go(int timeout = 0); + +}; + +ExecutorLauncher::ExecutorLauncher(po::variables_map &options) + : Launcher(options, ResourceFile::Format::percent_appledouble) +{ + +} + +ExecutorLauncher::~ExecutorLauncher() +{ + +} + +bool ExecutorLauncher::Go(int timeout) +{ + return ChildProcess(options["executor-path"].as(), { appPath.string() }, timeout) == 0; +} + + +void Executor::GetOptions(options_description &desc) +{ + desc.add_options() + ("executor-path", po::value()->default_value("executor"),"path to executor") + ; +} + +bool Executor::CheckOptions(variables_map &options) +{ + return options.count("executor-path") != 0; +} + +std::unique_ptr Executor::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new ExecutorLauncher(options)); +} diff --git a/LaunchAPPL/Executor.h b/LaunchAPPL/Executor.h new file mode 100644 index 0000000000..57dc584416 --- /dev/null +++ b/LaunchAPPL/Executor.h @@ -0,0 +1,16 @@ +#ifndef EXECUTOR_H +#define EXECUTOR_H + +#include "LaunchMethod.h" + +class Executor : public LaunchMethod +{ +public: + virtual std::string GetName() { return "executor"; } + virtual void GetOptions(options_description& desc); + virtual bool CheckOptions(variables_map& options); + + virtual std::unique_ptr MakeLauncher(variables_map& options); +}; + +#endif // EXECUTOR_H diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index e9288c78b7..4befeda29d 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -5,18 +5,11 @@ #include #include -#include -#include -#include - - -#include "ResourceFork.h" -#include "ResourceFile.h" - -extern "C" { -#include "hfs.h" -} +#include "LaunchMethod.h" +#include "Launcher.h" +#include "Executor.h" +#include "MiniVMac.h" namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -34,105 +27,20 @@ static void usage() } -int ChildProcess(string program, vector args) -{ - std::vector argv; - argv.push_back(program.c_str()); - for(string& s : args) - argv.push_back(s.c_str()); - argv.push_back(NULL); - - int timeout = options.count("timeout") ? options["timeout"].as() : 0; - - pid_t pid = fork(); - if(pid < 0) - { - perror("unable to fork"); - return 1; - } - else if(pid == 0) - { - pid_t worker_pid = timeout ? fork() : 0; - if(worker_pid < 0) - { - perror("unable to fork"); - _exit(1); - } - if(worker_pid == 0) - { - execvp(argv[0], const_cast (argv.data())); - perror("exec failed"); - _exit(1); - } - - pid_t timeout_pid = fork(); - if(timeout_pid < 0) - { - perror("unable to fork"); - _exit(1); - } - if(timeout_pid == 0) - { - sleep(timeout); - _exit(0); - } - int wstatus; - pid_t exited_pid = wait(&wstatus); - if(exited_pid == worker_pid) - { - kill(timeout_pid, SIGKILL); - wait(NULL); - if(!WIFEXITED(wstatus)) - { - return 1; - } - else - { - int exitcode = WEXITSTATUS(wstatus); - _exit(exitcode); - } - } - else - { - kill(worker_pid, SIGKILL); - wait(NULL); - _exit(1); - } - } - else - { - int wstatus; - int result = 0; - do - { - result = waitpid(pid, &wstatus, 0); - } while(result == -1 && errno == EINTR); - - if(!WIFEXITED(wstatus)) - { - return 1; - } - else - { - int exitcode = WEXITSTATUS(wstatus); - return exitcode; - } - } - -} int main(int argc, char *argv[]) { + std::vector methods = { + new Executor(), new MiniVMac() + }; desc.add_options() ("help,h", "show this help message") - ("executor,e", "run using executor") - ("minivmac,m", "run using executor") - - ("executor-path", po::value()->default_value("executor"),"path to executor") - ("minivmac-path", po::value()->default_value("minivmac"),"path to minivmac") - ("minivmac-dir", po::value()->default_value("."),"directory containing vMac.ROM") - ("system-image", po::value(),"path to disk image with system") + ("emulator,e", po::value(), "what emulator/environment to use") + ; + for(LaunchMethod *lm : methods) + lm->GetOptions(desc); + desc.add_options() ("timeout,t", po::value(),"abort after timeout") ("timeout-ok","timeout counts as success") ("logfile", po::value(), "read log file") @@ -142,7 +50,6 @@ int main(int argc, char *argv[]) ("application,a", po::value(), "application" ) ; alldesc.add(desc).add(hidden); - try { auto parsed = po::command_line_parser(argc, argv) @@ -162,198 +69,41 @@ int main(int argc, char *argv[]) po::notify(options); - if(options.count("help") || !options.count("application")) + if(options.count("help") || !options.count("application") || !options.count("emulator")) { usage(); return 0; } - ResourceFile app(options["application"].as()); - if(!app.read()) + LaunchMethod *method = NULL; + for(LaunchMethod *lm : methods) { - std::cerr << "Could not read application file.\n"; + if(lm->GetName() == options["emulator"].as()) + { + method = lm; + break; + } + } + if(!method) + { + std::cerr << "ERROR: unknown emulator/environment.\n"; return 1; } - - - if(options.count("executor")) + if(!method->CheckOptions(options)) { - fs::path tempDir = fs::unique_path(); - fs::create_directories(tempDir); - - fs::path appPath = tempDir / "Application"; - - app.assign(appPath.string(), ResourceFile::Format::percent_appledouble); - if(!app.write()) - { - std::cerr << "Could not write application file.\n"; - return 1; - } - - if(options.count("logfile")) - { - fs::ofstream out(tempDir/options["logfile"].as()); - } - - int result = ChildProcess(options["executor-path"].as(), { appPath.string() }); - - if(options.count("logfile")) - { - fs::ifstream in(tempDir/options["logfile"].as()); - std::cout << in.rdbuf(); - } - - fs::remove_all(tempDir); - - return result; - } - if(options.count("minivmac")) - { - assert(options.count("system-image")); - fs::path tempDir = fs::unique_path(); - fs::path path = tempDir / "image.dsk"; - fs::create_directories(tempDir); - - hfsvol *sysvol = hfs_mount(options["system-image"].as().c_str(), - 0, HFS_MODE_RDONLY); - - int size = 5000*1024; - fs::ofstream(path, std::ios::binary | std::ios::trunc).seekp(size-1).put(0); - hfs_format(path.string().c_str(), 0, 0, "SysAndApp", 0, NULL); - hfsvol *vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDWR); - - hfsvolent ent; - hfs_vstat(sysvol, &ent); - - hfs_setcwd(sysvol, ent.blessed); - - - - - { - const char *fn = "System"; - hfsdirent fileent; - hfs_stat(sysvol, fn, &fileent); - hfsfile *in = hfs_open(sysvol, fn); - hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); - - std::vector 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); - hfs_close(in); - hfs_close(out); - } - { - const char *fn = "Finder"; - hfsdirent fileent; - hfs_stat(sysvol, fn, &fileent); - hfsfile *in = hfs_open(sysvol, fn); - hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); - - std::vector 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); - hfs_close(in); - hfs_close(out); - } - { - const char *fn = "MacsBug"; - hfsdirent fileent; - hfs_stat(sysvol, fn, &fileent); - hfsfile *in = hfs_open(sysvol, fn); - hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator); - - std::vector 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); - hfs_close(in); - hfs_close(out); - } - - { - 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); - } - - { - hfsfile *out = hfs_create(vol, "out", "TEXT", "????"); - hfs_close(out); - } - - hfs_vstat(vol, &ent); - ent.blessed = hfs_getcwd(vol); - std::cout << "blessed: " << ent.blessed << std::endl; - hfs_vsetattr(vol, &ent); - - hfs_umount(vol); - hfs_umount(sysvol); - - extern unsigned char bootblock[1024]; - std::vector bootblock1(bootblock, bootblock+1024); - std::fstream out(path.string(), std::ios::in | std::ios::out | std::ios::binary); - - bootblock1[0x5A] = 3; - bootblock1[0x5B] = 'A'; - bootblock1[0x5C] = 'p'; - bootblock1[0x5D] = 'p'; - - out.write((const char*) bootblock1.data(), 1024); - - path = fs::absolute(path); - - fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as() ); - fs::path minivmacpath = fs::absolute( minivmacdir / options["minivmac-path"].as() ); - - fs::current_path(minivmacdir); - - int result = ChildProcess(minivmacpath.string(), { path.string() }); - - std::cerr << "volume at: " << path.string() << std::endl; - vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDONLY); - { - hfsfile *out = hfs_open(vol, "out"); - if(!out) - return 1; - hfsdirent fileent; - int statres = hfs_stat(vol, "out", &fileent); - std::cerr << "stat: " << statres << "\n"; - std::cerr << "out: " << fileent.u.file.dsize << " bytes\n"; - std::vector 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); + std::cerr << "Missing configuration.\n"; + return 1; } - return 0; + std::unique_ptr launcher = method->MakeLauncher(options); + + int timeout = options.count("timeout") ? options["timeout"].as() : 0; + + bool result = launcher->Go(timeout); + + launcher->DumpOutput(); + + + return result ? 0 : 1; } diff --git a/LaunchAPPL/LaunchMethod.cc b/LaunchAPPL/LaunchMethod.cc new file mode 100644 index 0000000000..34560d31db --- /dev/null +++ b/LaunchAPPL/LaunchMethod.cc @@ -0,0 +1,20 @@ +#include "LaunchMethod.h" + +LaunchMethod::LaunchMethod() +{ + +} + +LaunchMethod::~LaunchMethod() +{ + +} + +void LaunchMethod::GetOptions(boost::program_options::options_description &desc) +{ +} + +bool LaunchMethod::CheckOptions(boost::program_options::variables_map &options) +{ + return true; +} diff --git a/LaunchAPPL/LaunchMethod.h b/LaunchAPPL/LaunchMethod.h new file mode 100644 index 0000000000..ab208db8e0 --- /dev/null +++ b/LaunchAPPL/LaunchMethod.h @@ -0,0 +1,28 @@ +#ifndef LAUNCHMETHOD_H +#define LAUNCHMETHOD_H + +#include +#include +#include + +#include + +class Launcher; + +class LaunchMethod +{ +public: + typedef boost::program_options::options_description options_description; + typedef boost::program_options::variables_map variables_map; + + LaunchMethod(); + virtual ~LaunchMethod(); + + virtual std::string GetName() = 0; + virtual void GetOptions(options_description& desc); + virtual bool CheckOptions(variables_map& options); + + virtual std::unique_ptr MakeLauncher(variables_map& options) = 0; +}; + +#endif // LAUNCHMETHOD_H diff --git a/LaunchAPPL/Launcher.cc b/LaunchAPPL/Launcher.cc new file mode 100644 index 0000000000..41038f7278 --- /dev/null +++ b/LaunchAPPL/Launcher.cc @@ -0,0 +1,132 @@ +#include "Launcher.h" +#include +#include +#include +#include +#include + + +namespace fs = boost::filesystem; +using std::string; +using std::vector; + +Launcher::Launcher(boost::program_options::variables_map &options) + : options(options) +{ + app.assign(options["application"].as()); + if(!app.read()) + throw std::runtime_error("Could not load application file."); + + tempDir = fs::absolute(fs::unique_path()); + fs::create_directories(tempDir); + + appPath = tempDir / "Application"; + outPath = tempDir / "out"; + + fs::ofstream out(outPath); +} + +Launcher::Launcher(boost::program_options::variables_map &options, ResourceFile::Format f) + : Launcher(options) +{ + app.assign(appPath.string(), f); + app.write(); +} + +void Launcher::DumpOutput() +{ + fs::ifstream in(outPath); + std::cout << in.rdbuf(); +} + +Launcher::~Launcher() +{ + fs::remove_all(tempDir); +} + + + +int Launcher::ChildProcess(string program, vector args, int timeout) +{ + std::vector argv; + argv.push_back(program.c_str()); + for(string& s : args) + argv.push_back(s.c_str()); + argv.push_back(NULL); + + pid_t pid = fork(); + if(pid < 0) + { + perror("unable to fork"); + return 1; + } + else if(pid == 0) + { + pid_t worker_pid = timeout ? fork() : 0; + if(worker_pid < 0) + { + perror("unable to fork"); + _exit(1); + } + if(worker_pid == 0) + { + execvp(argv[0], const_cast (argv.data())); + perror("exec failed"); + _exit(1); + } + + pid_t timeout_pid = fork(); + if(timeout_pid < 0) + { + perror("unable to fork"); + _exit(1); + } + if(timeout_pid == 0) + { + sleep(timeout); + _exit(0); + } + int wstatus; + pid_t exited_pid = wait(&wstatus); + if(exited_pid == worker_pid) + { + kill(timeout_pid, SIGKILL); + wait(NULL); + if(!WIFEXITED(wstatus)) + { + return 1; + } + else + { + int exitcode = WEXITSTATUS(wstatus); + _exit(exitcode); + } + } + else + { + kill(worker_pid, SIGKILL); + wait(NULL); + _exit(1); + } + } + else + { + int wstatus; + int result = 0; + do + { + result = waitpid(pid, &wstatus, 0); + } while(result == -1 && errno == EINTR); + + if(!WIFEXITED(wstatus)) + { + return 1; + } + else + { + int exitcode = WEXITSTATUS(wstatus); + return exitcode; + } + } + +} diff --git a/LaunchAPPL/Launcher.h b/LaunchAPPL/Launcher.h new file mode 100644 index 0000000000..e6bf703725 --- /dev/null +++ b/LaunchAPPL/Launcher.h @@ -0,0 +1,31 @@ +#ifndef LAUNCHER_H +#define LAUNCHER_H + +#include +#include "ResourceFile.h" +#include + +class Launcher +{ +protected: + boost::program_options::variables_map& options; + + ResourceFile app; + boost::filesystem::path tempDir, appPath, outPath; + bool keepTempFiles; + + int ChildProcess(std::string program, std::vector args, int timeout); +public: + Launcher(boost::program_options::variables_map& options); + Launcher(boost::program_options::variables_map& options, ResourceFile::Format f); + + void KeepTempFiles() { keepTempFiles = true; } + + virtual bool Go(int timeout = 0) = 0; + + virtual void DumpOutput(); + + virtual ~Launcher(); +}; + +#endif // LAUNCHER_H diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc new file mode 100644 index 0000000000..4f8f686b67 --- /dev/null +++ b/LaunchAPPL/MiniVMac.cc @@ -0,0 +1,195 @@ +#include "MiniVMac.h" +#include "Launcher.h" + +extern "C" { +#include "hfs.h" +} + +#include + +namespace fs = boost::filesystem; +using std::string; +using std::vector; + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +class MiniVMacLauncher : public Launcher +{ + fs::path imagePath; + fs::path systemImage; + + hfsvol *sysvol; + hfsvol *vol; + + void CopySystemFile(const char* fn, const char *dstfn = NULL); +public: + MiniVMacLauncher(po::variables_map& options); + virtual ~MiniVMacLauncher(); + + virtual bool Go(int timeout = 0); + virtual void DumpOutput(); +}; + + +MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options) + : Launcher(options, ResourceFile::Format::percent_appledouble), + sysvol(NULL), vol(NULL) +{ + imagePath = fs::absolute(tempDir / "image.dsk"); + systemImage = fs::absolute(options["system-image"].as()); + fs::path autoquitImage = fs::absolute(options["autoquit-image"].as()); + + sysvol = hfs_mount(systemImage.string().c_str(),0, HFS_MODE_RDONLY); + assert(sysvol); + hfsvolent ent; + hfs_vstat(sysvol, &ent); + hfs_setcwd(sysvol, ent.blessed); + + + 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); + + { + extern unsigned char bootblock[1024]; + std::vector bootblock1(bootblock, bootblock+1024); + + bootblock1[0x1A] = 8; + memcpy(&bootblock1[0x1B],"AutoQuit", 8); + bootblock1[0x5A] = 3; + memcpy(&bootblock1[0x5B],"App", 3); + + std::fstream(imagePath.string(), 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("System"); + CopySystemFile("Finder"); + CopySystemFile("MacsBug"); + + { + 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); + assert(sysvol); + CopySystemFile("AutoQuit"); + + { + hfsfile *file = hfs_create(vol, "out", "TEXT", "MPS "); + hfs_close(file); + } + + hfs_umount(sysvol); sysvol = NULL; + hfs_umount(vol); vol = NULL; +} + +MiniVMacLauncher::~MiniVMacLauncher() +{ + if(sysvol) + hfs_umount(sysvol); + if(vol) + hfs_umount(vol); + +} + +void MiniVMacLauncher::CopySystemFile(const char *fn, const char *dstfn) +{ + if(!dstfn) + dstfn = fn; + hfsdirent fileent; + if(hfs_stat(sysvol, fn, &fileent) < 0) + return; + hfsfile *in = hfs_open(sysvol, fn); + hfsfile *out = hfs_create(vol, dstfn, fileent.u.file.type,fileent.u.file.creator); + + std::vector 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); + hfs_close(in); + hfs_close(out); +} + + +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; +} + +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); + std::cerr << "stat: " << statres << "\n"; + std::cerr << "out: " << fileent.u.file.dsize << " bytes\n"; + + hfsfile *out = hfs_open(vol, "out"); + if(!out) + return; + std::vector 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(),"directory containing vMac.ROM") + ("minivmac-path", po::value()->default_value("./minivmac"),"relative path to minivmac") + ("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") + ; +} + +bool MiniVMac::CheckOptions(variables_map &options) +{ + return options.count("minivmac-path") != 0 + && options.count("minivmac-dir") != 0 + && options.count("system-image") != 0 + && options.count("autoquit-image") != 0; +} + +std::unique_ptr MiniVMac::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new MiniVMacLauncher(options)); +} diff --git a/LaunchAPPL/MiniVMac.h b/LaunchAPPL/MiniVMac.h new file mode 100644 index 0000000000..0f83aa4ad8 --- /dev/null +++ b/LaunchAPPL/MiniVMac.h @@ -0,0 +1,16 @@ +#ifndef MINIVMAC_H +#define MINIVMAC_H + +#include "LaunchMethod.h" + +class MiniVMac : public LaunchMethod +{ +public: + virtual std::string GetName() { return "minivmac"; } + virtual void GetOptions(options_description& desc); + virtual bool CheckOptions(variables_map& options); + + virtual std::unique_ptr MakeLauncher(variables_map& options); +}; + +#endif // MINIVMAC_H diff --git a/LaunchAPPL/bootblock b/LaunchAPPL/bootblock new file mode 100644 index 0000000000..40d64c6862 Binary files /dev/null and b/LaunchAPPL/bootblock differ