/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2015-2017 Baldur Karlsson * Copyright (c) 2014 Crytek * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include #include #include "api/replay/renderdoc_replay.h" #include "core/core.h" #include "os/os_specific.h" #include "replay/replay_controller.h" #include "serialise/serialiser.h" #include "serialise/string_utils.h" #include "replay_proxy.h" #include "socket_helpers.h" using std::pair; template <> string ToStrHelper::Get(const PathProperty &el) { return "<...>"; } template <> string ToStrHelper::Get(const CaptureOptions &el) { return "<...>"; } template <> void Serialiser::Serialise(const char *name, PathEntry &el) { ScopedContext scope(this, name, "DirectoryFile", 0, true); Serialise("filename", el.filename); Serialise("flags", el.flags); Serialise("lastmod", el.lastmod); Serialise("size", el.size); } template <> string ToStrHelper::Get(const EnvMod &el) { return "<...>"; } template <> string ToStrHelper::Get(const EnvSep &el) { return "<...>"; } template <> void Serialiser::Serialise(const char *name, EnvironmentModification &el) { ScopedContext scope(this, name, "EnvironmentModification", 0, true); Serialise("mod", el.mod); Serialise("sep", el.sep); Serialise("name", el.name); Serialise("value", el.value); } static const uint32_t RemoteServerProtocolVersion = 1; enum RemoteServerPacket { eRemoteServer_Noop, eRemoteServer_Handshake, eRemoteServer_VersionMismatch, eRemoteServer_Busy, eRemoteServer_Ping, eRemoteServer_RemoteDriverList, eRemoteServer_TakeOwnershipCapture, eRemoteServer_CopyCaptureToRemote, eRemoteServer_CopyCaptureFromRemote, eRemoteServer_OpenLog, eRemoteServer_LogOpenProgress, eRemoteServer_LogOpened, eRemoteServer_CloseLog, eRemoteServer_HomeDir, eRemoteServer_ListDir, eRemoteServer_ExecuteAndInject, eRemoteServer_ShutdownServer, eRemoteServer_RemoteServerCount, }; RDCCOMPILE_ASSERT((int)eRemoteServer_RemoteServerCount < (int)eReplayProxy_First, "Remote server and Replay Proxy packets overlap"); struct ProgressLoopData { Network::Socket *sock; float progress; bool killsignal; }; static void ProgressTicker(void *d) { ProgressLoopData *data = (ProgressLoopData *)d; Serialiser ser("", Serialiser::WRITING, false); while(!data->killsignal) { ser.Rewind(); ser.Serialise("", data->progress); if(!SendPacket(data->sock, eRemoteServer_LogOpenProgress, ser)) { SAFE_DELETE(data->sock); break; } Threading::Sleep(100); } } struct ClientThread { ClientThread() : socket(NULL), allowExecution(false), killThread(false), killServer(false), thread(0) { } Network::Socket *socket; bool allowExecution; bool killThread; bool killServer; Threading::ThreadHandle thread; }; static void InactiveRemoteClientThread(void *data) { ClientThread *threadData = (ClientThread *)data; uint32_t ip = threadData->socket->GetRemoteIP(); // this thread just handles receiving the handshake and sending a busy signal without blocking the // server thread RemoteServerPacket type = eRemoteServer_Noop; Serialiser *recvser = NULL; if(!RecvPacket(threadData->socket, type, &recvser) || type != eRemoteServer_Handshake) { RDCWARN("Didn't receive proper handshake"); SAFE_DELETE(threadData->socket); return; } uint32_t version = 0; recvser->Serialise("version", version); SAFE_DELETE(recvser); if(version != RemoteServerProtocolVersion) { RDCLOG("Connection using protocol %u, but we are running %u", version, RemoteServerProtocolVersion); SendPacket(threadData->socket, eRemoteServer_VersionMismatch); } else { SendPacket(threadData->socket, eRemoteServer_Busy); } SAFE_DELETE(threadData->socket); RDCLOG("Closed inactive connection from %u.%u.%u.%u.", Network::GetIPOctet(ip, 0), Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3)); } static void ActiveRemoteClientThread(void *data) { ClientThread *threadData = (ClientThread *)data; Network::Socket *&client = threadData->socket; uint32_t ip = client->GetRemoteIP(); RemoteServerPacket type = eRemoteServer_Noop; Serialiser *handshakeSer = NULL; if(!RecvPacket(threadData->socket, type, &handshakeSer) || type != eRemoteServer_Handshake) { RDCWARN("Didn't receive proper handshake"); SAFE_DELETE(client); return; } uint32_t version = 0; handshakeSer->Serialise("version", version); SAFE_DELETE(handshakeSer); if(version != RemoteServerProtocolVersion) { RDCLOG("Connection using protocol %u, but we are running %u", version, RemoteServerProtocolVersion); SendPacket(threadData->socket, eRemoteServer_VersionMismatch); SAFE_DELETE(client); return; } else { // handshake and continue SendPacket(threadData->socket, eRemoteServer_Handshake); } vector tempFiles; IRemoteDriver *driver = NULL; ReplayProxy *proxy = NULL; Serialiser sendSer("", Serialiser::WRITING, false); while(client) { if(client && !client->Connected()) break; if(threadData->killThread) break; RemoteServerPacket sendType = eRemoteServer_Noop; sendSer.Rewind(); Threading::Sleep(4); if(client->IsRecvDataWaiting()) { type = eRemoteServer_Noop; Serialiser *recvser = NULL; if(!RecvPacket(client, type, &recvser)) break; if(client == NULL) { SAFE_DELETE(recvser); continue; } else if(type == eRemoteServer_Ping) { sendType = eRemoteServer_Ping; } else if(type == eRemoteServer_RemoteDriverList) { map drivers = RenderDoc::Inst().GetRemoteDrivers(); sendType = eRemoteServer_RemoteDriverList; uint32_t count = (uint32_t)drivers.size(); sendSer.Serialise("", count); for(auto it = drivers.begin(); it != drivers.end(); ++it) { RDCDriver driverType = it->first; sendSer.Serialise("", driverType); sendSer.Serialise("", (*it).second); } } else if(type == eRemoteServer_HomeDir) { sendType = eRemoteServer_HomeDir; string home = FileIO::GetHomeFolderFilename(); sendSer.Serialise("", home); } else if(type == eRemoteServer_ListDir) { string path; recvser->Serialise("path", path); sendType = eRemoteServer_ListDir; std::vector files = FileIO::GetFilesInDirectory(path.c_str()); sendSer.Serialise("", files); } else if(type == eRemoteServer_CopyCaptureFromRemote) { string path; recvser->Serialise("path", path); if(!SendChunkedFile(client, eRemoteServer_CopyCaptureFromRemote, path.c_str(), sendSer, NULL)) { RDCERR("Network error sending file"); SAFE_DELETE(recvser); break; } sendSer.Rewind(); } else if(type == eRemoteServer_CopyCaptureToRemote) { string cap_file; string dummy, dummy2; FileIO::GetDefaultFiles("remotecopy", cap_file, dummy, dummy2); Serialiser *fileRecv = NULL; RDCLOG("Copying file to local path '%s'.", cap_file.c_str()); if(!RecvChunkedFile(client, type, cap_file.c_str(), fileRecv, NULL)) { FileIO::Delete(cap_file.c_str()); RDCERR("Network error receiving file"); SAFE_DELETE(fileRecv); SAFE_DELETE(recvser); break; } RDCLOG("File received."); tempFiles.push_back(cap_file); SAFE_DELETE(fileRecv); sendType = eRemoteServer_CopyCaptureToRemote; sendSer.Serialise("path", cap_file); } else if(type == eRemoteServer_TakeOwnershipCapture) { string cap_file; recvser->Serialise("filename", cap_file); RDCLOG("Taking ownership of '%s'.", cap_file.c_str()); tempFiles.push_back(cap_file); } else if(type == eRemoteServer_ShutdownServer) { RDCLOG("Requested to shut down."); threadData->killServer = true; threadData->killThread = true; sendType = eRemoteServer_ShutdownServer; } else if(type == eRemoteServer_OpenLog) { string cap_file; recvser->Serialise("filename", cap_file); RDCASSERT(driver == NULL && proxy == NULL); RDCDriver driverType = RDC_Unknown; string driverName = ""; uint64_t fileMachineIdent = 0; ReplayStatus status = RenderDoc::Inst().FillInitParams(cap_file.c_str(), driverType, driverName, fileMachineIdent, NULL); if(status != ReplayStatus::Succeeded) { RDCERR("Failed to open %s", cap_file.c_str()); } else if(RenderDoc::Inst().HasRemoteDriver(driverType)) { ProgressLoopData progressData; progressData.sock = client; progressData.killsignal = false; progressData.progress = 0.0f; RenderDoc::Inst().SetProgressPtr(&progressData.progress); Threading::ThreadHandle ticker = Threading::CreateThread(ProgressTicker, &progressData); status = RenderDoc::Inst().CreateRemoteDriver(driverType, cap_file.c_str(), &driver); if(status != ReplayStatus::Succeeded || driver == NULL) { RDCERR("Failed to create remote driver for driver type %d name %s", driverType, driverName.c_str()); } else { driver->ReadLogInitialisation(); RenderDoc::Inst().SetProgressPtr(NULL); progressData.killsignal = true; Threading::JoinThread(ticker); Threading::CloseThread(ticker); proxy = new ReplayProxy(client, driver); } } else { RDCERR("File needs driver for %s which isn't supported!", driverName.c_str()); status = ReplayStatus::APIUnsupported; } sendType = eRemoteServer_LogOpened; sendSer.Serialise("status", status); } else if(type == eRemoteServer_CloseLog) { if(driver) driver->Shutdown(); driver = NULL; SAFE_DELETE(proxy); } else if(type == eRemoteServer_ExecuteAndInject) { string app, workingDir, cmdLine, logfile; CaptureOptions opts; recvser->Serialise("app", app); recvser->Serialise("workingDir", workingDir); recvser->Serialise("cmdLine", cmdLine); recvser->Serialise("opts", opts); rdctype::array env; recvser->Serialise("env", env); uint32_t ident = uint32_t(ReplayStatus::NetworkIOFailed); if(threadData->allowExecution) { ident = Process::LaunchAndInjectIntoProcess(app.c_str(), workingDir.c_str(), cmdLine.c_str(), env, "", opts, false); } else { RDCWARN("Requested to execute program - disallowing based on configuration"); } sendType = eRemoteServer_ExecuteAndInject; sendSer.Serialise("ident", ident); } else if((int)type >= eReplayProxy_First && proxy) { bool ok = proxy->Tick(type, recvser); SAFE_DELETE(recvser); if(!ok) break; continue; } SAFE_DELETE(recvser); if(sendType != eRemoteServer_Noop && !SendPacket(client, sendType, sendSer)) { RDCERR("Network error sending supported driver list"); break; } continue; } } if(driver) driver->Shutdown(); SAFE_DELETE(proxy); for(size_t i = 0; i < tempFiles.size(); i++) { FileIO::Delete(tempFiles[i].c_str()); } RDCLOG("Closing active connection from %u.%u.%u.%u.", Network::GetIPOctet(ip, 0), Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3)); RDCLOG("Ready for new active connection..."); SAFE_DELETE(client); } void RenderDoc::BecomeRemoteServer(const char *listenhost, uint16_t port, volatile bool32 &killReplay) { Network::Socket *sock = Network::CreateServerSocket(listenhost, port, 1); if(sock == NULL) return; std::vector > listenRanges; bool allowExecution = true; FILE *f = FileIO::fopen(FileIO::GetAppFolderFilename("remoteserver.conf").c_str(), "r"); while(f && !FileIO::feof(f)) { string line = trim(FileIO::getline(f)); if(line == "") continue; // skip comments if(line[0] == '#') continue; if(line.substr(0, sizeof("whitelist") - 1) == "whitelist") { uint32_t ip = 0, mask = 0; // CIDR notation bool found = Network::ParseIPRangeCIDR(line.c_str() + sizeof("whitelist"), ip, mask); if(found) { listenRanges.push_back(std::make_pair(ip, mask)); continue; } else { RDCLOG("Couldn't parse IP range from: %s", line.c_str() + sizeof("whitelist")); } continue; } else if(line.substr(0, sizeof("noexec") - 1) == "noexec") { allowExecution = false; continue; } RDCLOG("Malformed line '%s'. See documentation for file format.", line.c_str()); } if(f) FileIO::fclose(f); if(listenRanges.empty()) { RDCLOG("No whitelist IP ranges configured - using default private IP ranges."); RDCLOG( "Create a config file remoteserver.conf in ~/.renderdoc or %%APPDATA%%/renderdoc to narrow " "this down or accept connections from more ranges."); listenRanges.push_back(std::make_pair(Network::MakeIP(10, 0, 0, 0), 0xff000000)); listenRanges.push_back(std::make_pair(Network::MakeIP(172, 16, 0, 0), 0xfff00000)); listenRanges.push_back(std::make_pair(Network::MakeIP(192, 168, 0, 0), 0xffff0000)); } RDCLOG("Allowing connections from:"); for(size_t i = 0; i < listenRanges.size(); i++) { uint32_t ip = listenRanges[i].first; uint32_t mask = listenRanges[i].second; RDCLOG("%u.%u.%u.%u / %u.%u.%u.%u", Network::GetIPOctet(ip, 0), Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3), Network::GetIPOctet(mask, 0), Network::GetIPOctet(mask, 1), Network::GetIPOctet(mask, 2), Network::GetIPOctet(mask, 3)); } if(allowExecution) RDCLOG("Allowing execution commands"); else RDCLOG("Blocking execution commands"); RDCLOG("Replay host ready for requests..."); ClientThread *activeClientData = NULL; std::vector inactives; while(!killReplay) { Network::Socket *client = sock->AcceptClient(false); if(activeClientData && activeClientData->killServer) break; // reap any dead inactive threads for(size_t i = 0; i < inactives.size(); i++) { if(inactives[i]->socket == NULL) { Threading::JoinThread(inactives[i]->thread); Threading::CloseThread(inactives[i]->thread); delete inactives[i]; inactives.erase(inactives.begin() + i); break; } } // reap our active connection possibly if(activeClientData && activeClientData->socket == NULL) { Threading::JoinThread(activeClientData->thread); Threading::CloseThread(activeClientData->thread); delete activeClientData; activeClientData = NULL; } if(client == NULL) { if(!sock->Connected()) { RDCERR("Error in accept - shutting down server"); SAFE_DELETE(sock); return; } Threading::Sleep(5); continue; } uint32_t ip = client->GetRemoteIP(); RDCLOG("Connection received from %u.%u.%u.%u.", Network::GetIPOctet(ip, 0), Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3)); bool valid = false; // always allow connections from localhost valid = Network::MatchIPMask(ip, Network::MakeIP(127, 0, 0, 1), ~0U); for(size_t i = 0; i < listenRanges.size(); i++) { if(Network::MatchIPMask(ip, listenRanges[i].first, listenRanges[i].second)) { valid = true; break; } } if(!valid) { RDCLOG("Doesn't match any listen range, closing connection."); SAFE_DELETE(client); continue; } if(activeClientData == NULL) { activeClientData = new ClientThread(); activeClientData->socket = client; activeClientData->allowExecution = allowExecution; activeClientData->thread = Threading::CreateThread(ActiveRemoteClientThread, activeClientData); RDCLOG("Making active connection"); } else { ClientThread *inactive = new ClientThread(); inactive->socket = client; inactive->allowExecution = false; inactive->thread = Threading::CreateThread(InactiveRemoteClientThread, inactive); inactives.push_back(inactive); RDCLOG("Refusing inactive connection"); } } if(activeClientData && activeClientData->socket != NULL) { activeClientData->killThread = true; Threading::JoinThread(activeClientData->thread); Threading::CloseThread(activeClientData->thread); delete activeClientData; } // shut down client threads for(size_t i = 0; i < inactives.size(); i++) { Threading::JoinThread(inactives[i]->thread); Threading::CloseThread(inactives[i]->thread); delete inactives[i]; } SAFE_DELETE(sock); } struct RemoteServer : public IRemoteServer { public: RemoteServer(Network::Socket *sock, const char *hostname) : m_Socket(sock), m_hostname(hostname) { map m = RenderDoc::Inst().GetReplayDrivers(); m_Proxies.reserve(m.size()); for(auto it = m.begin(); it != m.end(); ++it) m_Proxies.push_back(*it); } const string &hostname() const { return m_hostname; } virtual ~RemoteServer() { SAFE_DELETE(m_Socket); } void ShutdownConnection() { delete this; } void ShutdownServerAndConnection() { Serialiser sendData("", Serialiser::WRITING, false); Send(eRemoteServer_ShutdownServer, sendData); RemoteServerPacket type = eRemoteServer_Noop; vector payload; RecvPacket(m_Socket, type, payload); delete this; } bool Connected() { return m_Socket != NULL && m_Socket->Connected(); } bool Ping() { if(!Connected()) return false; Serialiser sendData("", Serialiser::WRITING, false); Send(eRemoteServer_Ping, sendData); RemoteServerPacket type = eRemoteServer_Noop; Serialiser *ser = NULL; Get(type, &ser); SAFE_DELETE(ser); return type == eRemoteServer_Ping; } rdctype::array LocalProxies() { rdctype::array out; create_array_uninit(out, m_Proxies.size()); size_t i = 0; for(auto it = m_Proxies.begin(); it != m_Proxies.end(); ++it, ++i) out[i] = it->second; return out; } rdctype::array RemoteSupportedReplays() { rdctype::array out; { Serialiser sendData("", Serialiser::WRITING, false); Send(eRemoteServer_RemoteDriverList, sendData); RemoteServerPacket type = eRemoteServer_RemoteDriverList; Serialiser *ser = NULL; Get(type, &ser); if(ser) { uint32_t count = 0; ser->Serialise("", count); create_array_uninit(out, count); for(uint32_t i = 0; i < count; i++) { RDCDriver driver = RDC_Unknown; string name = ""; ser->Serialise("", driver); ser->Serialise("", name); out[i] = name; } delete ser; } } return out; } rdctype::str GetHomeFolder() { if(Android::IsHostADB(m_hostname.c_str())) return "/"; rdctype::str ret; Serialiser sendData("", Serialiser::WRITING, false); Send(eRemoteServer_HomeDir, sendData); RemoteServerPacket type = eRemoteServer_HomeDir; Serialiser *ser = NULL; Get(type, &ser); if(ser) { string home; ser->Serialise("", home); ret = home; delete ser; } return ret; } rdctype::array ListFolder(const char *path) { rdctype::array ret; if(Android::IsHostADB(m_hostname.c_str())) { int index = 0; std::string deviceID; Android::extractDeviceIDAndIndex(m_hostname, index, deviceID); string adbStdout = Android::adbExecCommand(deviceID, "shell pm list packages -3").strStdout; using namespace std; istringstream stdoutStream(adbStdout); string line; vector packages; while(getline(stdoutStream, line)) { vector tokens; split(line, tokens, ':'); if(tokens.size() == 2 && tokens[0] == "package") { PathEntry package; package.filename = trim(tokens[1]); package.size = 0; package.lastmod = 0; package.flags = PathProperty::Executable; packages.push_back(package); } } create_array_uninit(ret, packages.size()); for(size_t i = 0; i < packages.size(); i++) ret[i] = packages[i]; return ret; } string folderPath = path; Serialiser sendData("", Serialiser::WRITING, false); sendData.Serialise("", folderPath); Send(eRemoteServer_ListDir, sendData); RemoteServerPacket type = eRemoteServer_ListDir; Serialiser *ser = NULL; Get(type, &ser); if(ser) { std::vector paths; ser->Serialise("", paths); ret = paths; delete ser; } else { create_array_uninit(ret, 1); ret.elems[0].filename = path; ret.elems[0].flags = PathProperty::ErrorUnknown; } return ret; } uint32_t ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, const rdctype::array &env, const CaptureOptions &opts) { const char *host = hostname().c_str(); if(Android::IsHostADB(host)) return Android::StartAndroidPackageForCapture(host, app); string appstr = app && app[0] ? app : ""; string workstr = workingDir && workingDir[0] ? workingDir : ""; string cmdstr = cmdLine && cmdLine[0] ? cmdLine : ""; Serialiser sendData("", Serialiser::WRITING, false); sendData.Serialise("app", appstr); sendData.Serialise("workingDir", workstr); sendData.Serialise("cmdLine", cmdstr); sendData.Serialise("opts", (CaptureOptions &)opts); sendData.Serialise("env", (rdctype::array &)env); Send(eRemoteServer_ExecuteAndInject, sendData); RemoteServerPacket type = eRemoteServer_ExecuteAndInject; Serialiser *ser = NULL; Get(type, &ser); uint32_t ident = 0; if(ser) ser->Serialise("ident", ident); SAFE_DELETE(ser); return ident; } void CopyCaptureFromRemote(const char *remotepath, const char *localpath, float *progress) { string path = remotepath; Serialiser sendData("", Serialiser::WRITING, false); sendData.Serialise("path", path); Send(eRemoteServer_CopyCaptureFromRemote, sendData); float dummy = 0.0f; if(progress == NULL) progress = &dummy; Serialiser *fileRecv = NULL; if(!RecvChunkedFile(m_Socket, eRemoteServer_CopyCaptureFromRemote, localpath, fileRecv, progress)) { SAFE_DELETE(fileRecv); RDCERR("Network error receiving file"); return; } SAFE_DELETE(fileRecv); } rdctype::str CopyCaptureToRemote(const char *filename, float *progress) { Serialiser sendData("", Serialiser::WRITING, false); Send(eRemoteServer_CopyCaptureToRemote, sendData); float dummy = 0.0f; if(progress == NULL) progress = &dummy; sendData.Rewind(); if(!SendChunkedFile(m_Socket, eRemoteServer_CopyCaptureToRemote, filename, sendData, progress)) { SAFE_DELETE(m_Socket); return ""; } RemoteServerPacket type = eRemoteServer_Noop; Serialiser *ser = NULL; Get(type, &ser); if(type == eRemoteServer_CopyCaptureToRemote && ser) { string remotepath; ser->Serialise("path", remotepath); return remotepath; } return ""; } void TakeOwnershipCapture(const char *filename) { string logfile = filename; Serialiser sendData("", Serialiser::WRITING, false); sendData.Serialise("logfile", logfile); Send(eRemoteServer_TakeOwnershipCapture, sendData); } rdctype::pair OpenCapture(uint32_t proxyid, const char *filename, float *progress) { rdctype::pair ret; ret.first = ReplayStatus::InternalError; ret.second = NULL; string logfile = filename; if(proxyid != ~0U && proxyid >= m_Proxies.size()) { RDCERR("Invalid proxy driver id %d specified for remote renderer", proxyid); ret.first = ReplayStatus::InternalError; return ret; } float dummy = 0.0f; if(progress == NULL) progress = &dummy; // if the proxy id is ~0U, then we just don't care so let RenderDoc pick the most // appropriate supported proxy for the current platform. RDCDriver proxydrivertype = proxyid == ~0U ? RDC_Unknown : m_Proxies[proxyid].first; Serialiser sendData("", Serialiser::WRITING, false); sendData.Serialise("logfile", logfile); Send(eRemoteServer_OpenLog, sendData); Serialiser *progressSer = NULL; RemoteServerPacket type = eRemoteServer_Noop; while(m_Socket) { Get(type, &progressSer); if(!m_Socket || progressSer == NULL || type != eRemoteServer_LogOpenProgress) break; progressSer->Serialise("", *progress); RDCLOG("% 3.0f%%...", (*progress) * 100.0f); SAFE_DELETE(progressSer); } if(!m_Socket || progressSer == NULL || type != eRemoteServer_LogOpened) { ret.first = ReplayStatus::NetworkIOFailed; return ret; } ReplayStatus status = ReplayStatus::Succeeded; progressSer->Serialise("status", status); SAFE_DELETE(progressSer); *progress = 1.0f; if(status != ReplayStatus::Succeeded) { ret.first = status; return ret; } RDCLOG("Log ready on replay host"); IReplayDriver *proxyDriver = NULL; status = RenderDoc::Inst().CreateReplayDriver(proxydrivertype, NULL, &proxyDriver); if(status != ReplayStatus::Succeeded || !proxyDriver) { if(proxyDriver) proxyDriver->Shutdown(); ret.first = status; return ret; } ReplayController *rend = new ReplayController(); ReplayProxy *proxy = new ReplayProxy(m_Socket, proxyDriver); status = rend->SetDevice(proxy); if(status != ReplayStatus::Succeeded) { SAFE_DELETE(rend); ret.first = status; return ret; } // ReplayController takes ownership of the ProxySerialiser (as IReplayDriver) // and it cleans itself up in Shutdown. ret.first = ReplayStatus::Succeeded; ret.second = rend; return ret; } void CloseCapture(IReplayController *rend) { Serialiser sendData("", Serialiser::WRITING, false); Send(eRemoteServer_CloseLog, sendData); rend->Shutdown(); } private: Network::Socket *m_Socket; string m_hostname; void Send(RemoteServerPacket type, const Serialiser &ser) { SendPacket(m_Socket, type, ser); } void Get(RemoteServerPacket &type, Serialiser **ser) { vector payload; if(!RecvPacket(m_Socket, type, payload)) { SAFE_DELETE(m_Socket); if(ser) *ser = NULL; return; } if(ser) *ser = new Serialiser(payload.size(), &payload[0], false); } vector > m_Proxies; }; extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_ShutdownConnection(IRemoteServer *remote) { remote->ShutdownConnection(); } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_ShutdownServerAndConnection(IRemoteServer *remote) { remote->ShutdownServerAndConnection(); } extern "C" RENDERDOC_API bool32 RENDERDOC_CC RemoteServer_Ping(IRemoteServer *remote) { return remote->Ping(); } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_LocalProxies(IRemoteServer *remote, rdctype::array *out) { *out = remote->LocalProxies(); } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_GetHomeFolder(IRemoteServer *remote, rdctype::str *home) { rdctype::str path = remote->GetHomeFolder(); if(home) *home = path; } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_ListFolder(IRemoteServer *remote, const char *path, rdctype::array *dirlist) { rdctype::array files = remote->ListFolder(path); if(dirlist) *dirlist = files; } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_RemoteSupportedReplays(IRemoteServer *remote, rdctype::array *out) { *out = remote->RemoteSupportedReplays(); } extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RemoteServer_ExecuteAndInject( IRemoteServer *remote, const char *app, const char *workingDir, const char *cmdLine, const rdctype::array &env, const CaptureOptions &opts) { return remote->ExecuteAndInject(app, workingDir, cmdLine, env, opts); } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_TakeOwnershipCapture(IRemoteServer *remote, const char *filename) { remote->TakeOwnershipCapture(filename); } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_CopyCaptureToRemote(IRemoteServer *remote, const char *filename, float *progress, rdctype::str *remotepath) { rdctype::str path = remote->CopyCaptureToRemote(filename, progress); if(remotepath) *remotepath = path; } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_CopyCaptureFromRemote(IRemoteServer *remote, const char *remotepath, const char *localpath, float *progress) { remote->CopyCaptureFromRemote(remotepath, localpath, progress); } extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC RemoteServer_OpenCapture(IRemoteServer *remote, uint32_t proxyid, const char *logfile, float *progress, IReplayController **rend) { auto ret = remote->OpenCapture(proxyid, logfile, progress); if(rend) *rend = ret.second; return ret.first; } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_CloseCapture(IRemoteServer *remote, IReplayController *rend) { return remote->CloseCapture(rend); } extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC RENDERDOC_CreateRemoteServerConnection(const char *host, uint32_t port, IRemoteServer **rend) { if(rend == NULL) return ReplayStatus::InternalError; string s = "localhost"; if(host != NULL && host[0] != '\0') s = host; if(port == 0) port = RENDERDOC_GetDefaultRemoteServerPort(); if(host != NULL && Android::IsHostADB(host)) { s = "127.0.0.1"; int index = 0; std::string deviceID; Android::extractDeviceIDAndIndex(host, index, deviceID); // each subsequent device gets a new range of ports. The deviceID isn't needed since we already // forwarded the ports to the right devices. if(port == RENDERDOC_GetDefaultRemoteServerPort()) port += RenderDoc_AndroidPortOffset * (index + 1); } Network::Socket *sock = NULL; if(s != "-") { sock = Network::CreateClientSocket(s.c_str(), (uint16_t)port, 750); if(sock == NULL) return ReplayStatus::NetworkIOFailed; } Serialiser sendData("", Serialiser::WRITING, false); uint32_t version = RemoteServerProtocolVersion; sendData.Serialise("version", version); SendPacket(sock, eRemoteServer_Handshake, sendData); RemoteServerPacket type = (RemoteServerPacket)RecvPacket(sock); if(type == eRemoteServer_Busy) { SAFE_DELETE(sock); return ReplayStatus::NetworkRemoteBusy; } if(type == eRemoteServer_VersionMismatch) { SAFE_DELETE(sock); return ReplayStatus::NetworkVersionMismatch; } if(type != eRemoteServer_Handshake) { RDCWARN("Didn't get proper handshake"); SAFE_DELETE(sock); return ReplayStatus::NetworkIOFailed; } *rend = new RemoteServer(sock, host); return ReplayStatus::Succeeded; }