diff --git a/Emulator/Base/Defaults.cpp b/Emulator/Base/Defaults.cpp index 1bd2dc022..b4ed82453 100644 --- a/Emulator/Base/Defaults.cpp +++ b/Emulator/Base/Defaults.cpp @@ -165,6 +165,11 @@ Defaults::Defaults() setFallback(OPT_REC_ASPECT_X, 768); setFallback(OPT_REC_ASPECT_Y, 702); + setFallback(OPT_SRV_PORT, 8081, { SERVER_RSH }); + setFallback(OPT_SRV_PROTOCOL, SRVPROT_DEFAULT, { SERVER_RSH }); + setFallback(OPT_SRV_AUTORUN, false, { SERVER_RSH }); + setFallback(OPT_SRV_VERBOSE, true, { SERVER_RSH }); + setFallback("BASIC_PATH", ""); setFallback("CHAR_PATH", ""); setFallback("KERNAL_PATH", ""); diff --git a/Emulator/Base/ErrorTypes.h b/Emulator/Base/ErrorTypes.h index 12a255c9b..e7594567a 100644 --- a/Emulator/Base/ErrorTypes.h +++ b/Emulator/Base/ErrorTypes.h @@ -88,6 +88,22 @@ enum_long(ERROR_CODE) ERROR_CRT_TOO_MANY_PACKETS, ///< CRT file contains too many Rom packets ERROR_CRT_CORRUPTED_PACKET, ///< CRT file contains a corrupted Rom package + // Remote servers + ERROR_SOCK_CANT_CREATE, + ERROR_SOCK_CANT_CONNECT, + ERROR_SOCK_CANT_BIND, + ERROR_SOCK_CANT_LISTEN, + ERROR_SOCK_CANT_ACCEPT, + ERROR_SOCK_CANT_RECEIVE, + ERROR_SOCK_CANT_SEND, + ERROR_SOCK_DISCONNECTED, + ERROR_SERVER_PORT_IN_USE, + ERROR_SERVER_ON, + ERROR_SERVER_OFF, + ERROR_SERVER_RUNNING, + ERROR_SERVER_NOT_RUNNING, + ERROR_SERVER_NO_CLIENT, + // File systems ERROR_FS_UNSUPPORTED, ///< Unsupported file system ERROR_FS_WRONG_CAPACITY, ///< Wrong file system capacity @@ -171,6 +187,21 @@ struct ErrorCodeEnum : util::Reflection { case ERROR_CRT_TOO_MANY_PACKETS: return "CRT_TOO_MANY_PACKETS"; case ERROR_CRT_CORRUPTED_PACKET: return "CRT_CORRUPTED_PACKET"; + case ERROR_SOCK_CANT_CREATE: return "SOCK_CANT_CREATE"; + case ERROR_SOCK_CANT_CONNECT: return "SOCK_CANT_CONNECT"; + case ERROR_SOCK_CANT_BIND: return "SOCK_CANT_BIND"; + case ERROR_SOCK_CANT_LISTEN: return "SOCK_CANT_LISTEN"; + case ERROR_SOCK_CANT_ACCEPT: return "SOCK_CANT_ACCEPT"; + case ERROR_SOCK_CANT_RECEIVE: return "SOCK_CANT_RECEIVE"; + case ERROR_SOCK_CANT_SEND: return "SOCK_CANT_SEND"; + case ERROR_SOCK_DISCONNECTED: return "SOCK_DISCONNECTED"; + case ERROR_SERVER_PORT_IN_USE: return "SERVER_PORT_IN_USE"; + case ERROR_SERVER_ON: return "SERVER_ON"; + case ERROR_SERVER_OFF: return "SERVER_OFF"; + case ERROR_SERVER_RUNNING: return "SERVER_RUNNING"; + case ERROR_SERVER_NOT_RUNNING: return "SERVER_NOT_RUNNING"; + case ERROR_SERVER_NO_CLIENT: return "SERVER_NO_CLIENT"; + case ERROR_FS_UNSUPPORTED: return "FS_UNSUPPORTED"; case ERROR_FS_WRONG_CAPACITY: return "FS_WRONG_CAPACITY"; case ERROR_FS_CORRUPTED: return "FS_CORRUPTED"; diff --git a/Emulator/Base/MsgQueueTypes.h b/Emulator/Base/MsgQueueTypes.h index c0876c379..9cfb40128 100644 --- a/Emulator/Base/MsgQueueTypes.h +++ b/Emulator/Base/MsgQueueTypes.h @@ -110,7 +110,12 @@ enum_long(MSG_TYPE) MSG_ALARM, ///< A user-set alarm event has fired MSG_RS232, ///< RS232 activity (DEPRECATED) MSG_RS232_IN, ///< RS232 adapter has received data - MSG_RS232_OUT ///< RS232 adapter has sent data + MSG_RS232_OUT, ///< RS232 adapter has sent data + + // Remote server + MSG_SRV_STATE, + MSG_SRV_RECEIVE, + MSG_SRV_SEND }; typedef MSG_TYPE MsgType; @@ -118,7 +123,7 @@ typedef MSG_TYPE MsgType; struct MsgTypeEnum : util::Reflection { static constexpr long minVal = 0; - static constexpr long maxVal = MSG_ALARM; + static constexpr long maxVal = MSG_SRV_SEND; static bool isValid(auto value) { return value >= minVal && value <= maxVal; } static const char *prefix() { return "MSG"; } @@ -198,6 +203,10 @@ struct MsgTypeEnum : util::Reflection { case MSG_RS232: return "RS232"; case MSG_RS232_IN: return "RS232_IN"; case MSG_RS232_OUT: return "RS232_OUT"; + + case MSG_SRV_STATE: return "SRV_STATE"; + case MSG_SRV_RECEIVE: return "SRV_RECEIVE"; + case MSG_SRV_SEND: return "SRV_SEND"; } return "???"; } diff --git a/Emulator/Base/Option.cpp b/Emulator/Base/Option.cpp index 81795b807..5a379e0a9 100644 --- a/Emulator/Base/Option.cpp +++ b/Emulator/Base/Option.cpp @@ -163,6 +163,11 @@ OptionParser::create(Option opt, i64 arg) case OPT_REC_ASPECT_X: return numParser(); case OPT_REC_ASPECT_Y: return numParser(); + case OPT_SRV_PORT: return numParser(); + case OPT_SRV_PROTOCOL: return enumParser.template operator()(); + case OPT_SRV_AUTORUN: return boolParser(); + case OPT_SRV_VERBOSE: return boolParser(); + default: fatalError; } diff --git a/Emulator/Base/OptionTypes.h b/Emulator/Base/OptionTypes.h index e3373ab38..da4cadac0 100644 --- a/Emulator/Base/OptionTypes.h +++ b/Emulator/Base/OptionTypes.h @@ -181,6 +181,12 @@ enum_long(OPT) OPT_REC_ASPECT_X, ///< Numerator of the video's aspect ratio OPT_REC_ASPECT_Y, ///< Denumerator of the video's aspect ratio + // Remote servers + OPT_SRV_PORT, + OPT_SRV_PROTOCOL, + OPT_SRV_AUTORUN, + OPT_SRV_VERBOSE, + OPT_COUNT }; typedef OPT Option; @@ -332,6 +338,11 @@ struct OptionEnum : util::Reflection { case OPT_REC_ASPECT_X: return "REC.ASPECT_X"; case OPT_REC_ASPECT_Y: return "REC.ASPECT_Y"; + case OPT_SRV_PORT: return "SRV.PORT"; + case OPT_SRV_PROTOCOL: return "SRV.PROTOCOL"; + case OPT_SRV_AUTORUN: return "SRV.AUTORUN"; + case OPT_SRV_VERBOSE: return "SRV.VERBOSE"; + case OPT_COUNT: return "???"; } return "???"; @@ -476,6 +487,11 @@ struct OptionEnum : util::Reflection { case OPT_REC_ASPECT_X: return "Numerator of the video's aspect ratio"; case OPT_REC_ASPECT_Y: return "Denumerator of the video's aspect ratio"; + case OPT_SRV_PORT: return "Server port"; + case OPT_SRV_PROTOCOL: return "Server protocol"; + case OPT_SRV_AUTORUN: return "Auto run"; + case OPT_SRV_VERBOSE: return "Verbose mode"; + case OPT_COUNT: return "???"; } return "???"; diff --git a/Emulator/Base/SubComponent.cpp b/Emulator/Base/SubComponent.cpp index 52f5ea97a..9ead97e0e 100644 --- a/Emulator/Base/SubComponent.cpp +++ b/Emulator/Base/SubComponent.cpp @@ -41,6 +41,7 @@ parCable(ref.parCable), powerSupply(ref.supply), recorder(ref.recorder), regressionTester(ref.regressionTester), +remoteManager(ref.remoteManager), retroShell(ref.retroShell), sidBridge(ref.sidBridge), sid0(ref.sidBridge.sid[0]), diff --git a/Emulator/Base/SubComponent.h b/Emulator/Base/SubComponent.h index 406179a38..36fd93a9c 100644 --- a/Emulator/Base/SubComponent.h +++ b/Emulator/Base/SubComponent.h @@ -43,6 +43,7 @@ class References { class PowerPort &powerSupply; class Recorder &recorder; class RegressionTester ®ressionTester; + class RemoteManager &remoteManager; class RetroShell &retroShell; class SIDBridge &sidBridge; class SID& sid0; diff --git a/Emulator/Components/C64.cpp b/Emulator/Components/C64.cpp index a94d53a4c..e6c83f4be 100644 --- a/Emulator/Components/C64.cpp +++ b/Emulator/Components/C64.cpp @@ -182,6 +182,16 @@ C64::eventName(EventSlot slot, EventID id) } break; + case SLOT_SRV: + + switch (id) { + + case EVENT_NONE: return "none"; + case SRV_LAUNCH_DAEMON: return "SRV_LAUNCH_DAEMON"; + default: return "*** INVALID ***"; + } + break; + case SLOT_ALA: switch (id) { @@ -302,6 +312,7 @@ C64::operator << (SerResetter &worker) // Schedule initial events scheduleAbs(cpu.clock, CIA_EXECUTE); scheduleAbs(cpu.clock, CIA_EXECUTE); + scheduleRel(C64::sec(0.5), SRV_LAUNCH_DAEMON); if (insEvent) scheduleRel (0, insEvent); scheduleNextSNPEvent(); @@ -860,6 +871,9 @@ C64::processEvents(Cycle cycle) if (isDue(cycle)) { keyboard.processKeyEvent(eventid[SLOT_KEY]); } + if (isDue(cycle)) { + remoteManager.serviceServerEvent(); + } if (isDue(cycle)) { processAlarmEvent(); } @@ -1528,6 +1542,8 @@ C64::getDebugVariable(DebugFlag flag) case FLAG_REC_DEBUG: return REC_DEBUG; case FLAG_REU_DEBUG: return REU_DEBUG; + case FLAG_SCK_DEBUG: return SCK_DEBUG; + case FLAG_SRV_DEBUG: return SRV_DEBUG; case FLAG_FORCE_ROM_MISSING: return FORCE_ROM_MISSING; case FLAG_FORCE_MEGA64_MISMATCH: return FORCE_MEGA64_MISMATCH; @@ -1615,6 +1631,8 @@ C64::setDebugVariable(DebugFlag flag, int val) case FLAG_REC_DEBUG: REC_DEBUG = val; break; case FLAG_REU_DEBUG: REU_DEBUG = val; break; + case FLAG_SCK_DEBUG: SCK_DEBUG = val; break; + case FLAG_SRV_DEBUG: SRV_DEBUG = val; break; case FLAG_FORCE_ROM_MISSING: FORCE_ROM_MISSING = val; break; case FLAG_FORCE_MEGA64_MISMATCH: FORCE_MEGA64_MISMATCH = val; break; diff --git a/Emulator/Components/C64.h b/Emulator/Components/C64.h index e81141071..e5ca644f6 100644 --- a/Emulator/Components/C64.h +++ b/Emulator/Components/C64.h @@ -28,8 +28,6 @@ #include "CIA.h" #include "CPU.h" #include "Recorder.h" -#include "RegressionTester.h" -#include "RetroShell.h" // Ports #include "AudioPort.h" @@ -64,6 +62,13 @@ #include "CRTFile.h" #include "FileSystem.h" +// Misc +#include "Host.h" +#include "RegressionTester.h" +#include "RemoteManager.h" +#include "RetroShell.h" +#include "RshServer.h" + namespace vc64 { // @@ -156,6 +161,7 @@ class C64 final : public CoreComponent, public Inspectable { // Misc RetroShell retroShell = RetroShell(*this); + RemoteManager remoteManager = RemoteManager(*this); Debugger debugger = Debugger(*this); RegressionTester regressionTester = RegressionTester(*this); Recorder recorder = Recorder(*this); diff --git a/Emulator/Components/C64Types.h b/Emulator/Components/C64Types.h index 624f483a8..1573597f0 100644 --- a/Emulator/Components/C64Types.h +++ b/Emulator/Components/C64Types.h @@ -112,6 +112,7 @@ enum_long(SLOT) SLOT_SNP, // Snapshots SLOT_RSH, // Retro Shell SLOT_KEY, // Auto-typing + SLOT_SRV, // Remote server manager SLOT_ALA, // Alarms (set by the GUI) SLOT_INS, // Handles periodic calls to inspect() @@ -143,6 +144,7 @@ struct EventSlotEnum : util::Reflection case SLOT_SNP: return "SNP"; case SLOT_RSH: return "RSH"; case SLOT_KEY: return "KEY"; + case SLOT_SRV: return "SRV"; case SLOT_ALA: return "ALA"; case SLOT_INS: return "INS"; @@ -223,6 +225,10 @@ enum_i8(EventID) KEY_AUTO_TYPE = 1, KEY_EVENT_COUNT, + // Remote server manager + SRV_LAUNCH_DAEMON = 1, + SRV_EVENT_COUNT, + // Alarm event slot ALA_TRIGGER = 1, ALA_EVENT_COUNT, diff --git a/Emulator/Components/EmulatorTypes.h b/Emulator/Components/EmulatorTypes.h index 807c455c2..374dab36d 100644 --- a/Emulator/Components/EmulatorTypes.h +++ b/Emulator/Components/EmulatorTypes.h @@ -93,6 +93,8 @@ enum_long(DEBUG_FLAG) // Other components FLAG_REC_DEBUG, ///< Debug the screen recorder FLAG_REU_DEBUG, ///< Debug the REU memory expansion + FLAG_SCK_DEBUG, ///< Debug the socket interface + FLAG_SRV_DEBUG, ///< Debug the remote servers //! Forced error condition FLAG_FORCE_ROM_MISSING, @@ -186,6 +188,8 @@ struct DebugFlagEnum : util::Reflection // Other components case FLAG_REC_DEBUG: return "REC_DEBUG"; case FLAG_REU_DEBUG: return "REU_DEBUG"; + case FLAG_SCK_DEBUG: return "SCK_DEBUG"; + case FLAG_SRV_DEBUG: return "SRV_DEBUG"; // Forced error conditions case FLAG_FORCE_ROM_MISSING: return "FORCE_ROM_MISSING"; @@ -273,6 +277,8 @@ struct DebugFlagEnum : util::Reflection // Other components case FLAG_REC_DEBUG: return "Screen recorder"; case FLAG_REU_DEBUG: return "REU memory expansion"; + case FLAG_SCK_DEBUG: return "Sockets"; + case FLAG_SRV_DEBUG: return "Remote servers"; // Forced error conditions case FLAG_FORCE_ROM_MISSING: return ""; diff --git a/Emulator/Misc/RemoteServers/CMakeLists.txt b/Emulator/Misc/RemoteServers/CMakeLists.txt new file mode 100644 index 000000000..87dedeb9d --- /dev/null +++ b/Emulator/Misc/RemoteServers/CMakeLists.txt @@ -0,0 +1,11 @@ +target_include_directories(vc64Core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +target_sources(vc64Core PRIVATE + +RemoteManager.cpp +RemoteServer.cpp +SerServer.cpp +RshServer.cpp +Socket.cpp + +) diff --git a/Emulator/Misc/RemoteServers/RemoteManager.cpp b/Emulator/Misc/RemoteServers/RemoteManager.cpp new file mode 100644 index 000000000..76bb5ea80 --- /dev/null +++ b/Emulator/Misc/RemoteServers/RemoteManager.cpp @@ -0,0 +1,109 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#include "config.h" +#include "RemoteManager.h" +#include "IOUtils.h" +#include "C64.h" + +namespace vc64 { + +RemoteManager::RemoteManager(C64& ref) : SubComponent(ref) +{ + subComponents = std::vector { + + &rshServer + }; +} + +void +RemoteManager::_dump(Category category, std::ostream& os) const +{ + using namespace util; + + if (category == Category::State) { + + os << "Remote server status: " << std::endl << std::endl; + + for (auto server : servers) { + + auto name = server->objectName(); + auto port = server->config.port; + + os << tab(string(name)); + + if (server->isOff()) { + os << "Off" << std::endl; + } else { + os << "Port " << dec(port); + os << " (" << SrvStateEnum::key(server->state) << ")" << std::endl; + } + } + } +} + +void +RemoteManager::cacheInfo(RemoteManagerInfo &result) const +{ + info.numLaunching = numLaunching(); + info.numListening = numListening(); + info.numConnected = numConnected(); + info.numErroneous = numErroneous(); +} + +isize +RemoteManager::numLaunching() const +{ + isize result = 0; + for (auto &s : servers) if (s->isStarting()) result++; + return result; +} + +isize +RemoteManager::numListening() const +{ + isize result = 0; + for (auto &s : servers) if (s->isListening()) result++; + return result; +} + +isize +RemoteManager::numConnected() const +{ + isize result = 0; + for (auto &s : servers) if (s->isConnected()) result++; + return result; +} + +isize +RemoteManager::numErroneous() const +{ + isize result = 0; + for (auto &s : servers) if (s->isErroneous()) result++; + return result; +} + +void +RemoteManager::serviceServerEvent() +{ + assert(c64.eventid[SLOT_SRV] == SRV_LAUNCH_DAEMON); + + // Run the launch daemon + if (rshServer.config.autoRun) { + rshServer.shouldRun() ? rshServer._start() : rshServer._stop(); + } + + // Schedule next event + c64.scheduleInc (C64::sec(0.5), SRV_LAUNCH_DAEMON); +} + +} diff --git a/Emulator/Misc/RemoteServers/RemoteManager.h b/Emulator/Misc/RemoteServers/RemoteManager.h new file mode 100644 index 000000000..fd093830a --- /dev/null +++ b/Emulator/Misc/RemoteServers/RemoteManager.h @@ -0,0 +1,121 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#pragma once + +#include "SubComponent.h" +#include "RemoteManagerTypes.h" +#include "RshServer.h" + +namespace vc64 { + +class RemoteManager : public SubComponent, public Inspectable { + + Descriptions descriptions = {{ + + .name = "RemoteManager", + .description = "Remote Manager", + .shell = "server" + }}; + + ConfigOptions options = { + + }; + +public: + + // The remote servers + RshServer rshServer = RshServer(c64); + + // Convenience wrapper + std::vector servers = { &rshServer }; + + + // + // Initializing + // + +public: + + RemoteManager(C64& ref); + + RemoteManager& operator= (const RemoteManager& other) { + + CLONE(rshServer) + + return *this; + } + + + // + // Methods from CoreObject + // + +protected: + + void _dump(Category category, std::ostream& os) const override; + + + // + // Methods from CoreComponent + // + +private: + + template void serialize(T& worker) { } SERIALIZERS(serialize); + +public: + + const Descriptions &getDescriptions() const override { return descriptions; } + + + // + // Methods from Configurable + // + +public: + + const ConfigOptions &getOptions() const override { return options; } + + + // + // Methods from Inspectable + // + +public: + + void cacheInfo(RemoteManagerInfo &result) const override; + + + // + // Managing connections + // + +public: + + // Returns the number of servers being in a certain state + isize numLaunching() const; + isize numListening() const; + isize numConnected() const; + isize numErroneous() const; + + + // + // Servicing events + // + +public: + + void serviceServerEvent(); +}; + +} diff --git a/Emulator/Misc/RemoteServers/RemoteManagerTypes.h b/Emulator/Misc/RemoteServers/RemoteManagerTypes.h new file mode 100644 index 000000000..0eb9ffac9 --- /dev/null +++ b/Emulator/Misc/RemoteServers/RemoteManagerTypes.h @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#pragma once + +#include "Reflection.h" + +namespace vc64 { + +// +// Enumerations +// + +enum_long(SERVER_TYPE) +{ + SERVER_RSH +}; +typedef SERVER_TYPE ServerType; + +#ifdef __cplusplus +struct ServerTypeEnum : util::Reflection +{ + static constexpr long minVal = 0; + static constexpr long maxVal = SERVER_RSH; + static bool isValid(auto val) { return val >= minVal && val <= maxVal; } + + static const char *prefix() { return "SERVER"; } + static const char *_key(long value) + { + switch (value) { + + case SERVER_RSH: return "RSH"; + } + return "???"; + } +}; +#endif + + +// +// Structures +// + +typedef struct +{ + isize numLaunching; + isize numListening; + isize numConnected; + isize numErroneous; +} +RemoteManagerInfo; + +} diff --git a/Emulator/Misc/RemoteServers/RemoteServer.cpp b/Emulator/Misc/RemoteServers/RemoteServer.cpp new file mode 100644 index 000000000..39e829160 --- /dev/null +++ b/Emulator/Misc/RemoteServers/RemoteServer.cpp @@ -0,0 +1,383 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#include "config.h" +#include "RemoteServer.h" +#include "Emulator.h" +#include "CPU.h" +#include "IOUtils.h" +#include "Memory.h" +#include "MemUtils.h" +#include "MsgQueue.h" +#include "RetroShell.h" + +namespace vc64 { + +RemoteServer::RemoteServer(C64 &ref) : SubComponent(ref) +{ + +} + +void +RemoteServer::shutDownServer() +{ + debug(SRV_DEBUG, "Shutting down\n"); + try { stop(); } catch(...) { } +} + +void +RemoteServer::_dump(Category category, std::ostream& os) const +{ + using namespace util; + + if (category == Category::Config) { + + dumpConfig(os); + } + + if (category == Category::State) { + + os << tab("State"); + os << SrvStateEnum::key(state) << std::endl; + os << tab("Received packets"); + os << dec(numReceived) << std::endl; + os << tab("Transmitted packets"); + os << dec(numSent) << std::endl; + } +} + +void +RemoteServer::_powerOff() +{ + shutDownServer(); +} + +void +RemoteServer::_didLoad() +{ + // Stop the server (will be restarted by the launch daemon in auto-run mode) + stop(); +} + +i64 +RemoteServer::getOption(Option option) const +{ + switch (option) { + + case OPT_SRV_PORT: return config.port; + case OPT_SRV_PROTOCOL: return config.protocol; + case OPT_SRV_AUTORUN: return config.autoRun; + case OPT_SRV_VERBOSE: return config.verbose; + + default: + fatalError; + } +} + +void +RemoteServer::checkOption(Option opt, i64 value) +{ + switch (opt) { + + case OPT_SRV_PORT: + case OPT_SRV_PROTOCOL: + case OPT_SRV_AUTORUN: + case OPT_SRV_VERBOSE: + + return; + + default: + throw(ERROR_OPT_UNSUPPORTED); + } +} + +void +RemoteServer::setOption(Option option, i64 value) +{ + switch (option) { + + case OPT_SRV_PORT: + + if (config.port != (u16)value) { + + if (isOff()) { + + config.port = (u16)value; + + } else { + + stop(); + config.port = (u16)value; + start(); + } + } + return; + + case OPT_SRV_PROTOCOL: + + config.protocol = (ServerProtocol)value; + return; + + case OPT_SRV_AUTORUN: + + config.autoRun = (bool)value; + return; + + case OPT_SRV_VERBOSE: + + config.verbose = (bool)value; + return; + + default: + fatalError; + } +} + +void +RemoteServer::_start() +{ + if (isOff()) { + + debug(SRV_DEBUG, "Starting server...\n"); + switchState(SRV_STATE_STARTING); + + // Make sure we continue with a terminated server thread + if (serverThread.joinable()) serverThread.join(); + + // Spawn a new thread + serverThread = std::thread(&RemoteServer::main, this); + } +} + +void +RemoteServer::_stop() +{ + if (!isOff()) { + + debug(SRV_DEBUG, "Stopping server...\n"); + switchState(SRV_STATE_STOPPING); + + // Interrupt the server thread + _disconnect(); + + // Wait until the server thread has terminated + if (serverThread.joinable()) serverThread.join(); + + switchState(SRV_STATE_OFF); + } +} + +void +RemoteServer::_disconnect() +{ + debug(SRV_DEBUG, "Disconnecting...\n"); + + // Trigger an exception inside the server thread + connection.close(); + listener.close(); +} + +void +RemoteServer::switchState(SrvState newState) +{ + auto oldState = state; + + if (oldState != newState) { + + debug(SRV_DEBUG, "Switching state: %s -> %s\n", + SrvStateEnum::key(state), SrvStateEnum::key(newState)); + + // Switch state + state = newState; + + // Call the delegation method + didSwitch(oldState, newState); + + // Inform the GUI + msgQueue.put(MSG_SRV_STATE, newState); + } +} + +string +RemoteServer::receive() +{ + string packet; + + if (isConnected()) { + + packet = doReceive(); + msgQueue.put(MSG_SRV_RECEIVE, ++numReceived); + } + + return packet; +} + +void +RemoteServer::send(const string &packet) +{ + if (isConnected()) { + + doSend(packet); + msgQueue.put(MSG_SRV_SEND, ++numSent); + } +} + +void +RemoteServer::send(char payload) +{ + send(string(1, payload)); +} + +void +RemoteServer::send(int payload) +{ + send(std::to_string(payload)); +} + +void +RemoteServer::send(long payload) +{ + send(std::to_string(payload)); +} + + +void +RemoteServer::send(std::stringstream &payload) +{ + string line; + while(std::getline(payload, line)) { + send(line + "\n"); + } +} + +void +RemoteServer::process(const string &payload) +{ + doProcess(payload); +} + +void +RemoteServer::main() +{ + try { + + mainLoop(); + + } catch (std::exception &err) { + + debug(SRV_DEBUG, "Server thread interrupted\n"); + handleError(err.what()); + } +} + +void +RemoteServer::mainLoop() +{ + switchState(SRV_STATE_LISTENING); + + while (isListening()) { + + try { + + try { + + // Try to be a client by connecting to an existing server + connection.connect(config.port); + debug(SRV_DEBUG, "Acting as a client\n"); + + } catch (...) { + + // If there is no existing server, be the server + debug(SRV_DEBUG, "Acting as a server\n"); + + // Create a port listener + listener.bind(config.port); + listener.listen(); + + // Wait for a client to connect + connection = listener.accept(); + } + + // Handle the session + sessionLoop(); + + // Close the port listener + listener.close(); + + } catch (std::exception &err) { + + debug(SRV_DEBUG, "Main loop interrupted\n"); + + // Handle error if we haven't been interrupted purposely + if (!isStopping()) handleError(err.what()); + } + } + + switchState(SRV_STATE_OFF); +} + +void +RemoteServer::sessionLoop() +{ + switchState(SRV_STATE_CONNECTED); + + numReceived = 0; + numSent = 0; + + try { + + // Receive and process packets + while (1) { process(receive()); } + + } catch (std::exception &err) { + + debug(SRV_DEBUG, "Session loop interrupted\n"); + + // Handle error if we haven't been interrupted purposely + if (!isStopping()) { + + handleError(err.what()); + switchState(SRV_STATE_LISTENING); + } + } + + numReceived = 0; + numSent = 0; + + connection.close(); +} + +void +RemoteServer::handleError(const char *description) +{ + switchState(SRV_STATE_ERROR); + retroShell << "Server Error: " << string(description) << '\n'; +} + +void +RemoteServer::didSwitch(SrvState from, SrvState to) +{ + if (from == SRV_STATE_STARTING && to == SRV_STATE_LISTENING) { + didStart(); + } + if (to == SRV_STATE_OFF) { + didStop(); + } + if (to == SRV_STATE_CONNECTED) { + didConnect(); + } + if (from == SRV_STATE_CONNECTED) { + didDisconnect(); + } +} + +} diff --git a/Emulator/Misc/RemoteServers/RemoteServer.h b/Emulator/Misc/RemoteServers/RemoteServer.h new file mode 100644 index 000000000..d8e79f97b --- /dev/null +++ b/Emulator/Misc/RemoteServers/RemoteServer.h @@ -0,0 +1,243 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#pragma once + +#include "RemoteServerTypes.h" +#include "SubComponent.h" +#include "Socket.h" +#include "Thread.h" +#include + +namespace vc64 { + +class RemoteServer : public SubComponent { + + friend class RemoteManager; + + Descriptions descriptions = {{ + + .name = "RemoteServer", + .description = "Remote Server", + .shell = "" + }}; + + ConfigOptions options = { + + OPT_SRV_PORT, + OPT_SRV_PROTOCOL, + OPT_SRV_AUTORUN, + OPT_SRV_VERBOSE + }; + +protected: + + // Current configuration + ServerConfig config = {}; + + // Sockets + Socket listener; + Socket connection; + + // The server thread + std::thread serverThread; + + // The current server state + SrvState state = SRV_STATE_OFF; + + // The number of sent and received packets + isize numSent = 0; + isize numReceived = 0; + + + // + // Initializing + // + +public: + + RemoteServer(C64& ref); + ~RemoteServer() { shutDownServer(); } + void shutDownServer(); + + RemoteServer& operator= (const RemoteServer& other) { + + CLONE(config) + + return *this; + } + + + // + // Methods from CoreObject + // + +protected: + + void _dump(Category category, std::ostream& os) const override; + +public: + + const Descriptions &getDescriptions() const override { return descriptions; } + + + // + // Methods from CoreComponent + // + +private: + + void _powerOff() override; + + template + void serialize(T& worker) + { + if (isResetter(worker)) return; + + worker + + << config.port + << config.protocol + << config.verbose; + + } SERIALIZERS(serialize); + + void _didLoad() override; + + + // + // Methods from Configurable + // + +public: + + const ServerConfig &getConfig() const { return config; } + const ConfigOptions &getOptions() const override { return options; } + i64 getOption(Option option) const override; + void checkOption(Option opt, i64 value) override; + void setOption(Option option, i64 value) override; + + + // + // Examining state + // + +public: + + bool isOff() const { return state == SRV_STATE_OFF; } + bool isStarting() const { return state == SRV_STATE_STARTING; } + bool isListening() const { return state == SRV_STATE_LISTENING; } + bool isConnected() const { return state == SRV_STATE_CONNECTED; } + bool isStopping() const { return state == SRV_STATE_STOPPING; } + bool isErroneous() const { return state == SRV_STATE_ERROR; } + + + // + // Starting and stopping the server + // + +public: + + // Launch the remote server + void start() throws { SUSPENDED _start(); } + + // Shuts down the remote server + void stop() throws { SUSPENDED _stop(); } + + // Disconnects the client + void disconnect() throws { SUSPENDED _disconnect(); } + +protected: + + // Called from disconnect(), start() and stop() + void _start() throws; + void _stop() throws; + void _disconnect() throws; + + // Switches the internal state + void switchState(SrvState newState); + +private: + + // Used by the launch daemon to determine if actions should be taken + virtual bool shouldRun() { return true; } + + + // + // Running the server + // + +private: + + // The main thread function + void main(); + + // Inner loops (called from main) + void mainLoop() throws; + void sessionLoop(); + + + // + // Transmitting and processing packets + // + +public: + + // Receives or packet + string receive() throws; + + // Sends a packet + void send(const string &payload) throws; + void send(char payload) throws; + void send(int payload) throws; + void send(long payload) throws; + void send(std::stringstream &payload) throws; + + // Operator overloads + RemoteServer &operator<<(char payload) { send(payload); return *this; } + RemoteServer &operator<<(const string &payload) { send(payload); return *this; } + RemoteServer &operator<<(int payload) { send(payload); return *this; } + RemoteServer &operator<<(long payload) { send(payload); return *this; } + RemoteServer &operator<<(std::stringstream &payload) { send(payload); return *this; } + + // Processes a package + void process(const string &payload) throws; + +private: + + // Reports an error to the GUI + void handleError(const char *description); + + + // + // Subclass specific implementations + // + +private: + + virtual string doReceive() throws = 0; + virtual void doSend(const string &payload) throws = 0; + virtual void doProcess(const string &payload) throws = 0; + + + // + // Delegation methods + // + + void didSwitch(SrvState from, SrvState to); + virtual void didStart() { }; + virtual void didStop() { }; + virtual void didConnect() { }; + virtual void didDisconnect() { }; +}; + +} diff --git a/Emulator/Misc/RemoteServers/RemoteServerTypes.h b/Emulator/Misc/RemoteServers/RemoteServerTypes.h new file mode 100644 index 000000000..d2df300bb --- /dev/null +++ b/Emulator/Misc/RemoteServers/RemoteServerTypes.h @@ -0,0 +1,102 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#pragma once + +#include "Reflection.h" + +namespace vc64 { + +// +// Enumerations +// + +enum_long(SRV_STATE) +{ + SRV_STATE_OFF, // The server is inactive + SRV_STATE_STARTING, // The server is starting up + SRV_STATE_LISTENING, // The server is waiting for a client to connect + SRV_STATE_CONNECTED, // The server is connected to a client + SRV_STATE_STOPPING, // The server is shutting down + SRV_STATE_ERROR // The server is in an error state +}; +typedef SRV_STATE SrvState; + +#ifdef __cplusplus +struct SrvStateEnum : util::Reflection +{ + static constexpr long minVal = 0; + static constexpr long maxVal = SRV_STATE_ERROR; + + static const char *prefix() { return "SRV"; } + static const char *_key(long value) + { + switch (value) { + + case SRV_STATE_OFF: return "OFF"; + case SRV_STATE_STARTING: return "STARTING"; + case SRV_STATE_LISTENING: return "LISTENING"; + case SRV_STATE_CONNECTED: return "CONNECTED"; + case SRV_STATE_STOPPING: return "STOPPING"; + case SRV_STATE_ERROR: return "ERROR"; + } + return "???"; + } +}; +#endif + +enum_long(SRVPROT) +{ + SRVPROT_DEFAULT +}; +typedef SRVPROT ServerProtocol; + +#ifdef __cplusplus +struct ServerProtocolEnum : util::Reflection +{ + static constexpr long minVal = 0; + static constexpr long maxVal = SRVPROT_DEFAULT; + static bool isValid(auto val) { return val >= minVal && val <= maxVal; } + + static const char *prefix() { return "SRVPROT"; } + static const char *_key(long value) + { + switch (value) { + + case SRVPROT_DEFAULT: return "DEFAULT"; + } + return "???"; + } +}; +#endif + +// +// Structures +// + +typedef struct +{ + // The socket port number of this server + u16 port; + + // Indicates special operation modes (if not DEFAULT) + ServerProtocol protocol; + + // If true, the lauch manager starts and stops the server automatically + bool autoRun; + + // If true, transmitted packets are shown in RetroShell + bool verbose; +} +ServerConfig; + +} diff --git a/Emulator/Misc/RemoteServers/RshServer.cpp b/Emulator/Misc/RemoteServers/RshServer.cpp new file mode 100644 index 000000000..12cd16868 --- /dev/null +++ b/Emulator/Misc/RemoteServers/RshServer.cpp @@ -0,0 +1,103 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#include "config.h" +#include "RshServer.h" +#include "C64.h" +#include "RetroShell.h" +#include "StringUtils.h" + +namespace vc64 { + +void +RshServer::_dump(Category category, std::ostream& os) const +{ + using namespace util; + + RemoteServer::_dump(category, os); +} + +void +RshServer::didStart() +{ + if (config.verbose) { + + *this << "Remote server is listening at port " << config.port << "\n"; + } +} + +void +RshServer::didConnect() +{ + if (config.verbose) { + + try { + + printf("FIXME\n"); + assert(false); + // retroShell.asyncExec("welcome"); + + } catch (...) { }; + } +} + +string +RshServer::doReceive() +{ + string payload = connection.recv(); + + // Remove LF and CR (if present) + payload = util::rtrim(payload, "\n\r"); + + // Ask the client to delete the input (will be replicated by RetroShell) + connection.send("\033[A\33[2K\r"); + + return payload; +} + +void +RshServer::doSend(const string &payload) +{ + string mapped; + + for (auto c : payload) { + + switch (c) { + + case '\r': + + mapped += "\33[2K\r"; + break; + + case '\n': + + mapped += "\n"; + break; + + default: + + if (isprint(c)) mapped += c; + break; + } + } + + connection.send(mapped); +} + +void +RshServer::doProcess(const string &payload) +{ + retroShell.press(payload); + retroShell.press('\n'); +} + +} diff --git a/Emulator/Misc/RemoteServers/RshServer.h b/Emulator/Misc/RemoteServers/RshServer.h new file mode 100644 index 000000000..cdf423528 --- /dev/null +++ b/Emulator/Misc/RemoteServers/RshServer.h @@ -0,0 +1,63 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#pragma once + +#include "RemoteServer.h" + +namespace vc64 { + +class RshServer : public RemoteServer { + + Descriptions descriptions = {{ + + .name = "RshServer", + .description = "Remote Shell Server", + .shell = "server rshell" + }}; + +public: + + using RemoteServer::RemoteServer; + + RshServer& operator= (const RshServer& other) { + + RemoteServer::operator = (other); + return *this; + } + + + // + // Methods from CoreObject + // + +private: + + void _dump(Category category, std::ostream& os) const override; + +public: + + const Descriptions &getDescriptions() const override { return descriptions; } + + + // + // Methods from RemoteServer + // + + string doReceive() throws override; + void doProcess(const string &packet) throws override; + void doSend(const string &packet)throws override; + void didStart() override; + void didConnect() override; +}; + +} diff --git a/Emulator/Misc/RemoteServers/Socket.cpp b/Emulator/Misc/RemoteServers/Socket.cpp new file mode 100644 index 000000000..12533db9e --- /dev/null +++ b/Emulator/Misc/RemoteServers/Socket.cpp @@ -0,0 +1,197 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#include "config.h" +#include "Socket.h" +#include "MemUtils.h" + +namespace vc64 { + +Socket::Socket() : socket(INVALID_SOCKET) +{ + debug(SCK_DEBUG, "Socket constructor\n"); +} + +Socket::Socket(SOCKET id) : socket(id) +{ + debug(SCK_DEBUG, "Wrapping socket %lld\n", (i64)id); +} + +Socket::Socket(Socket&& other) +{ + socket = other.socket; + other.socket = INVALID_SOCKET; +} + +Socket& Socket::operator=(Socket&& other) +{ + assert(socket != other.socket); + + if (socket != other.socket) { + + close(); + socket = other.socket; + other.socket = INVALID_SOCKET; + } + return *this; +} + +Socket::~Socket() +{ + debug(SCK_DEBUG, "Socket destructor\n"); + + if (socket != INVALID_SOCKET) { + close(); + } +} + +void Socket::create() +{ +#ifdef _WIN32 + static struct WSAInit { + + WSAInit() { + + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData)) + throw Error(ERROR_SOCK_CANT_CREATE); + } + ~WSAInit() { + + WSACleanup(); + } + } wsaInit; +#endif + + if (socket == INVALID_SOCKET) { + + // Create a new socket + socket = ::socket(AF_INET, SOCK_STREAM, 0); + if (socket == INVALID_SOCKET) { + throw Error(ERROR_SOCK_CANT_CREATE); + } + + // Set options + int opt = 1; + auto success = setsockopt(socket, + SOL_SOCKET, + SO_REUSEADDR, + (const char *)&opt, + sizeof(opt)); + if (success < 0) { + throw Error(ERROR_SOCK_CANT_CREATE); + } + + debug(SCK_DEBUG, "Created new socket %lld\n", (i64)socket); + } +} + +void +Socket::connect(u16 port) +{ + // Create the socket if it is uninitialized + create(); + + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = util::bigEndian(port); + + if (::connect(socket, (struct sockaddr *)&address, sizeof(address)) < 0) { + throw Error(ERROR_SOCK_CANT_CONNECT); + } +} + +void +Socket::bind(u16 port) +{ + // Create the socket if it is uninitialized + create(); + + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = util::bigEndian(port); + + if (::bind(socket, (struct sockaddr *)&address, sizeof(address)) < 0) { + throw Error(ERROR_SOCK_CANT_BIND); + } +} + +void +Socket::listen() +{ + if (::listen(socket, 3) < 0) { + throw Error(ERROR_SOCK_CANT_LISTEN); + } +} + +Socket +Socket::accept() +{ + struct sockaddr_in address; + auto addrlen = (socklen_t)sizeof(struct sockaddr_in); + auto s = ::accept(socket, (struct sockaddr *)&address, &addrlen); + + if (s == INVALID_SOCKET) { + throw Error(ERROR_SOCK_CANT_ACCEPT); + } + + return Socket(s); +} + +std::string +Socket::recv() +{ + char buffer[BUFFER_SIZE + 1] = {}; + if (auto n = ::recv(socket, buffer, BUFFER_SIZE, 0); n > 0) { + + // Convert the buffer to a string + string result = string(buffer, n); + return result; + } + + throw Error(ERROR_SOCK_CANT_RECEIVE); +} + +void +Socket::send(u8 value) +{ + if (::send(socket, (const char *)&value, 1, 0) < 1) { + throw Error(ERROR_SOCK_CANT_SEND); + } +} + +void +Socket::send(const string &s) +{ + if (::send(socket, s.c_str(), (int)s.length(), 0) < 0) { + throw Error(ERROR_SOCK_CANT_SEND); + } +} + +void +Socket::close() +{ + if (socket != INVALID_SOCKET) { + + debug(SCK_DEBUG, "Closing socket %lld\n", (i64)socket); +#ifdef _WIN32 + closesocket(socket); +#else + ::close(socket); +#endif + socket = INVALID_SOCKET; + } +} + +} diff --git a/Emulator/Misc/RemoteServers/Socket.h b/Emulator/Misc/RemoteServers/Socket.h new file mode 100644 index 000000000..678927e4f --- /dev/null +++ b/Emulator/Misc/RemoteServers/Socket.h @@ -0,0 +1,100 @@ +// ----------------------------------------------------------------------------- +// This file is part of VirtualC64 +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// This FILE is dual-licensed. You are free to choose between: +// +// - The GNU General Public License v3 (or any later version) +// - The Mozilla Public License v2 +// +// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0 +// ----------------------------------------------------------------------------- + +#pragma once + +#include "CoreObject.h" + +#ifdef _WIN32 + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include + +#else + +#include +#include +#include + +namespace vc64 { typedef int SOCKET; } +#define INVALID_SOCKET -1 + +#endif + +namespace vc64 { + +class Socket : public CoreObject { + + SOCKET socket; + +public: + + // Size of the communication buffer + static constexpr isize BUFFER_SIZE = 512; + + + // + // Initializing + // + +public: + + Socket(); + Socket(SOCKET id); + Socket(const Socket& other) = delete; + Socket& operator=(const Socket& other) = delete; + Socket(Socket&& other); + Socket& operator=(Socket&& other); + ~Socket(); + + void create(); + + + // + // Methods from CoreObject + // + +private: + + const char *objectName() const override { return "Socket"; } + void _dump(Category category, std::ostream& os) const override { }; + + + // + // Establishing and terminating a connection + // + +public: + + void connect(u16 port) throws; + void bind(u16 port) throws; + void listen(); + Socket accept(); + void close(); + + + // + // Transfering data + // + +public: + + string recv(); + void send(u8 value); + void send(char c) { send((u8)c); } + void send(const string &s); +}; + +} diff --git a/Emulator/VirtualC64Types.h b/Emulator/VirtualC64Types.h index 45397dcff..17fa9dee6 100644 --- a/Emulator/VirtualC64Types.h +++ b/Emulator/VirtualC64Types.h @@ -40,6 +40,8 @@ #include "ParCableTypes.h" #include "PowerPortTypes.h" #include "RecorderTypes.h" +#include "RemoteManagerTypes.h" +#include "RemoteServerTypes.h" #include "RetroShellTypes.h" #include "SIDTypes.h" #include "ThreadTypes.h" diff --git a/Emulator/config.cpp b/Emulator/config.cpp index 40272b72f..4393bf98f 100644 --- a/Emulator/config.cpp +++ b/Emulator/config.cpp @@ -80,6 +80,8 @@ debugflag USR_DEBUG = 0; // Other components debugflag REC_DEBUG = 0; debugflag REU_DEBUG = 0; +debugflag SCK_DEBUG = 0; +debugflag SRV_DEBUG = 0; // diff --git a/Emulator/config.h b/Emulator/config.h index 912323bda..7a7cb1217 100644 --- a/Emulator/config.h +++ b/Emulator/config.h @@ -158,6 +158,8 @@ extern debugflag USR_DEBUG; // Other components extern debugflag REC_DEBUG; extern debugflag REU_DEBUG; +extern debugflag SCK_DEBUG; +extern debugflag SRV_DEBUG; // diff --git a/Proxy/TypeExtensions.swift b/Proxy/TypeExtensions.swift index 978b2fdea..7bad5539a 100644 --- a/Proxy/TypeExtensions.swift +++ b/Proxy/TypeExtensions.swift @@ -31,6 +31,7 @@ extension vc64.EventSlot: CustomStringConvertible { case ._SNP: return "Snapshots" case ._RSH: return "Retro Shell" case ._KEY: return "Auto Typing" + case ._SRV: return "Server Daemon" case ._ALA: return "Alarms" case ._INS: return "Inspector" diff --git a/VirtualC64.xcodeproj/project.pbxproj b/VirtualC64.xcodeproj/project.pbxproj index 7cdd71e1b..765d14f26 100644 --- a/VirtualC64.xcodeproj/project.pbxproj +++ b/VirtualC64.xcodeproj/project.pbxproj @@ -391,6 +391,15 @@ 50A22A2F2618845800C9FB1E /* TextureRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A22A2E2618845800C9FB1E /* TextureRect.swift */; }; 50A519411B8DA230006771E2 /* drive_click.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 50A519401B8DA230006771E2 /* drive_click.aiff */; }; 50A519431B8DA360006771E2 /* drive_snatch_uae.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 50A519421B8DA360006771E2 /* drive_snatch_uae.aiff */; }; + 50A5B7CE2C74E44C00D6DDAD /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 50A5B7C22C74E44C00D6DDAD /* CMakeLists.txt */; }; + 50A5B7CF2C74E44C00D6DDAD /* RemoteManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7C32C74E44C00D6DDAD /* RemoteManager.cpp */; }; + 50A5B7D02C74E44C00D6DDAD /* RemoteManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7C32C74E44C00D6DDAD /* RemoteManager.cpp */; }; + 50A5B7D12C74E44C00D6DDAD /* RemoteServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7C62C74E44C00D6DDAD /* RemoteServer.cpp */; }; + 50A5B7D22C74E44C00D6DDAD /* RemoteServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7C62C74E44C00D6DDAD /* RemoteServer.cpp */; }; + 50A5B7D32C74E44C00D6DDAD /* RshServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7C92C74E44C00D6DDAD /* RshServer.cpp */; }; + 50A5B7D42C74E44C00D6DDAD /* RshServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7C92C74E44C00D6DDAD /* RshServer.cpp */; }; + 50A5B7D52C74E44C00D6DDAD /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7CB2C74E44C00D6DDAD /* Socket.cpp */; }; + 50A5B7D62C74E44C00D6DDAD /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A5B7CB2C74E44C00D6DDAD /* Socket.cpp */; }; 50A9A088250DE90900723D32 /* PageFox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50A9A086250DE90900723D32 /* PageFox.cpp */; }; 50AA3EC12616F62500C96EDB /* MemUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50AA3EC02616F62500C96EDB /* MemUtils.cpp */; }; 50AA48B2292F8EC30084401D /* Peddle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50AA48B0292F8EC30084401D /* Peddle.cpp */; }; @@ -952,6 +961,17 @@ 50A2D7AE24AF944500671F38 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; 50A519401B8DA230006771E2 /* drive_click.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = drive_click.aiff; sourceTree = ""; }; 50A519421B8DA360006771E2 /* drive_snatch_uae.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = drive_snatch_uae.aiff; sourceTree = ""; }; + 50A5B7C22C74E44C00D6DDAD /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 50A5B7C32C74E44C00D6DDAD /* RemoteManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RemoteManager.cpp; sourceTree = ""; }; + 50A5B7C42C74E44C00D6DDAD /* RemoteManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteManager.h; sourceTree = ""; }; + 50A5B7C52C74E44C00D6DDAD /* RemoteManagerTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteManagerTypes.h; sourceTree = ""; }; + 50A5B7C62C74E44C00D6DDAD /* RemoteServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RemoteServer.cpp; sourceTree = ""; }; + 50A5B7C72C74E44C00D6DDAD /* RemoteServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteServer.h; sourceTree = ""; }; + 50A5B7C82C74E44C00D6DDAD /* RemoteServerTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteServerTypes.h; sourceTree = ""; }; + 50A5B7C92C74E44C00D6DDAD /* RshServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RshServer.cpp; sourceTree = ""; }; + 50A5B7CA2C74E44C00D6DDAD /* RshServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RshServer.h; sourceTree = ""; }; + 50A5B7CB2C74E44C00D6DDAD /* Socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Socket.cpp; sourceTree = ""; }; + 50A5B7CC2C74E44C00D6DDAD /* Socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Socket.h; sourceTree = ""; }; 50A9A086250DE90900723D32 /* PageFox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PageFox.cpp; sourceTree = ""; }; 50A9A087250DE90900723D32 /* PageFox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PageFox.h; sourceTree = ""; }; 50AA3EBF2616F60800C96EDB /* MemUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MemUtils.h; sourceTree = ""; }; @@ -2042,6 +2062,24 @@ path = Base; sourceTree = ""; }; + 50A5B7CD2C74E44C00D6DDAD /* RemoteServers */ = { + isa = PBXGroup; + children = ( + 50A5B7C22C74E44C00D6DDAD /* CMakeLists.txt */, + 50A5B7C52C74E44C00D6DDAD /* RemoteManagerTypes.h */, + 50A5B7C42C74E44C00D6DDAD /* RemoteManager.h */, + 50A5B7C32C74E44C00D6DDAD /* RemoteManager.cpp */, + 50A5B7C82C74E44C00D6DDAD /* RemoteServerTypes.h */, + 50A5B7C72C74E44C00D6DDAD /* RemoteServer.h */, + 50A5B7C62C74E44C00D6DDAD /* RemoteServer.cpp */, + 50A5B7CA2C74E44C00D6DDAD /* RshServer.h */, + 50A5B7C92C74E44C00D6DDAD /* RshServer.cpp */, + 50A5B7CC2C74E44C00D6DDAD /* Socket.h */, + 50A5B7CB2C74E44C00D6DDAD /* Socket.cpp */, + ); + path = RemoteServers; + sourceTree = ""; + }; 50AA48B5292F8F350084401D /* Peddle */ = { isa = PBXGroup; children = ( @@ -2198,6 +2236,7 @@ children = ( 50EF22322815927D00440C4D /* CMakeLists.txt */, 5095C8192BA08AF500A83825 /* Debugger */, + 50A5B7CD2C74E44C00D6DDAD /* RemoteServers */, 5036E0AD261AEF000048E66A /* RetroShell */, 50EF22302815922300440C4D /* RegressionTester */, 50CD36912816817500E40BFC /* Recorder */, @@ -2296,6 +2335,7 @@ 503C1DD124FC10F400D91141 /* SnapshotViewer.xib in Resources */, 50A0B48D24C1CAEB00FF0B0B /* Preferences.xib in Resources */, 50D3092624C71AD300B92563 /* Monitor.xib in Resources */, + 50A5B7CE2C74E44C00D6DDAD /* CMakeLists.txt in Resources */, 500F7CC6281EAB64003F1A7C /* DiskCreator.xib in Resources */, 501E2C1225B2DDED00B9C9C0 /* AppIcon.icns in Resources */, 5098C57D2658D4DC00E05EFC /* VideoExporter.xib in Resources */, @@ -2455,6 +2495,7 @@ 50BD641E2C04C05A007CD347 /* Heatmap.cpp in Sources */, 50A1948E2BDA6F490046849E /* EmulatorBase.cpp in Sources */, 50726F5D2961C9AC0031F2F5 /* VICII.cpp in Sources */, + 50A5B7D22C74E44C00D6DDAD /* RemoteServer.cpp in Sources */, 508C1E572C0B20760097473C /* ExpansionPortBase.cpp in Sources */, 50726F652961C9B30031F2F5 /* NamedPipe.cpp in Sources */, 50726F8C2961CA0D0031F2F5 /* Reu.cpp in Sources */, @@ -2462,6 +2503,7 @@ 50726F492961C9940031F2F5 /* MsgQueue.cpp in Sources */, 500634632C0EF1A400D60D96 /* DatasetteBase.cpp in Sources */, 50CCC8022C117ACF0047ED17 /* MediaFile.cpp in Sources */, + 50A5B7D02C74E44C00D6DDAD /* RemoteManager.cpp in Sources */, 50726F862961C9F10031F2F5 /* FlashRom.cpp in Sources */, 50726F782961C9DC0031F2F5 /* PIA.cpp in Sources */, 50BB3E922B7A232800B2D87B /* Inspectable.cpp in Sources */, @@ -2507,6 +2549,7 @@ 508C1E5E2C0B31320097473C /* DmaDebuggerBase.cpp in Sources */, 50726F4A2961C9940031F2F5 /* CPU.cpp in Sources */, 50BD641A2C0373F9007CD347 /* PowerPortBase.cpp in Sources */, + 50A5B7D42C74E44C00D6DDAD /* RshServer.cpp in Sources */, 50726FA22961CA0D0031F2F5 /* WarpSpeed.cpp in Sources */, 50726F472961C9940031F2F5 /* SubComponent.cpp in Sources */, 50726F702961C9C60031F2F5 /* pot.cc in Sources */, @@ -2520,6 +2563,7 @@ 50726F962961CA0D0031F2F5 /* Westermann.cpp in Sources */, 50726F6B2961C9C60031F2F5 /* extfilt.cc in Sources */, 50726F9C2961CA0D0031F2F5 /* Kcs.cpp in Sources */, + 50A5B7D62C74E44C00D6DDAD /* Socket.cpp in Sources */, 506F30432B74C1C70083EAEA /* Wakeable.cpp in Sources */, 50A194852BDA5CD40046849E /* TODBase.cpp in Sources */, 508C1E6E2C0C58FF0097473C /* Paddle.cpp in Sources */, @@ -2565,6 +2609,7 @@ 5052A7C125A0AC0700E62EDD /* Errors.swift in Sources */, 50AE19392632B787005A5898 /* Script.cpp in Sources */, 5044A03B2657A7A800C551C6 /* Recorder.cpp in Sources */, + 50A5B7CF2C74E44C00D6DDAD /* RemoteManager.cpp in Sources */, 5085D89D25B848940043B15C /* Joystick.cpp in Sources */, 500E2D662824EC140015C46F /* CartridgeInspector.swift in Sources */, 500E34172615C40D006A38DA /* Parser.cpp in Sources */, @@ -2673,6 +2718,7 @@ 50ED5BF812DD99C000596417 /* Speedometer.swift in Sources */, 504C43AB24AF29AC00E69CAE /* CIA.cpp in Sources */, 5058A922281D10B100C223C9 /* VolumeInspector.swift in Sources */, + 50A5B7D32C74E44C00D6DDAD /* RshServer.cpp in Sources */, 504C436024AF29AC00E69CAE /* Mach5.cpp in Sources */, 50891C012652380A0004AC1B /* VICIISprites.cpp in Sources */, 50DF719425021FCA00953E2E /* Keycap.swift in Sources */, @@ -2745,6 +2791,7 @@ 504C436724AF29AC00E69CAE /* MagicDesk.cpp in Sources */, 50033829266E47B400D5456E /* CompatibilityConf.swift in Sources */, 50B485FD24FA1D3200844133 /* iCarousel.m in Sources */, + 50A5B7D52C74E44C00D6DDAD /* Socket.cpp in Sources */, 50BE4B7024E7FC21008F39C9 /* MTLDevice.swift in Sources */, 5055D5B726158ABC005D3DA6 /* Checksum.cpp in Sources */, 50AA48B2292F8EC30084401D /* Peddle.cpp in Sources */, @@ -2784,6 +2831,7 @@ 50300AEE258B2C5400D261E3 /* FSDirEntry.cpp in Sources */, 507A49A024C8D66A00DBF984 /* GuardTableView.swift in Sources */, 5016F02829574FDF00F2063B /* Reu.cpp in Sources */, + 50A5B7D12C74E44C00D6DDAD /* RemoteServer.cpp in Sources */, 50C72DE61BC7F42100F1863B /* Shaders.metal in Sources */, 5077AC682B5ACC5100EFAC85 /* config.cpp in Sources */, 5036E0B7261AEF000048E66A /* Interpreter.cpp in Sources */,