diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7efddc6b3b..3b95915f1b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1605,6 +1605,8 @@ list(APPEND NativeAppSource
UI/SystemInfoScreen.cpp
UI/Store.h
UI/Store.cpp
+ UI/UploadScreen.h
+ UI/UploadScreen.cpp
UI/CwCheatScreen.h
UI/CwCheatScreen.cpp
UI/InstallZipScreen.h
diff --git a/Common/File/FileDescriptor.cpp b/Common/File/FileDescriptor.cpp
index 0da3b9e09f..61fa4a73e8 100644
--- a/Common/File/FileDescriptor.cpp
+++ b/Common/File/FileDescriptor.cpp
@@ -75,7 +75,8 @@ std::string GetLocalIP(int sock) {
} server_addr;
memset(&server_addr, 0, sizeof(server_addr));
socklen_t len = sizeof(server_addr);
- if (getsockname(sock, (struct sockaddr *)&server_addr, &len) == 0) {
+ int retval = getsockname(sock, (struct sockaddr *)&server_addr, &len);
+ if (retval == 0) {
char temp[64]{};
// We clear the port below for WSAAddressToStringA.
@@ -94,17 +95,24 @@ std::string GetLocalIP(int sock) {
wchar_t wtemp[sizeof(temp)];
DWORD len = (DWORD)sizeof(temp);
// Windows XP doesn't support inet_ntop.
- if (WSAAddressToStringW((struct sockaddr *)&server_addr, sizeof(server_addr), nullptr, wtemp, &len) == 0) {
+ HRESULT result = WSAAddressToStringW((struct sockaddr *)&server_addr, sizeof(server_addr), nullptr, wtemp, &len);
+ if (result == 0) {
return ConvertWStringToUTF8(wtemp);
+ } else {
+ return "";
}
#else
const char *result = inet_ntop(server_addr.sa.sa_family, addr, temp, sizeof(temp));
if (result) {
return result;
+ } else {
+ return "";
}
#endif
+ } else {
+ WARN_LOG(Log::IO, "GetLocalIP: getsockname failed with error %d (%s)", retval, strerror(retval));
+ return "";
}
- return "";
}
} // fd_util
diff --git a/Common/Net/HTTPClient.cpp b/Common/Net/HTTPClient.cpp
index e17565f2c0..a9aa042bb6 100644
--- a/Common/Net/HTTPClient.cpp
+++ b/Common/Net/HTTPClient.cpp
@@ -47,6 +47,10 @@ const char *DNSTypeAsString(DNSType type) {
}
}
+std::string Connection::GetLocalIpAsString() const {
+ return fd_util::GetLocalIP(this->sock());
+}
+
bool Connection::Resolve(const char *host, int port, DNSType type) {
if ((intptr_t)sock_ != -1) {
ERROR_LOG(Log::IO, "Resolve: Already have a socket");
diff --git a/Common/Net/HTTPClient.h b/Common/Net/HTTPClient.h
index c8a147db56..2ecc1d1d16 100644
--- a/Common/Net/HTTPClient.h
+++ b/Common/Net/HTTPClient.h
@@ -30,6 +30,8 @@ public:
// Only to be used for bring-up and debugging.
uintptr_t sock() const { return sock_; }
+ std::string GetLocalIpAsString() const;
+
protected:
// Store the remote host here, so we can send it along through HTTP/1.1 requests.
// TODO: Move to http::client?
diff --git a/Common/Net/HTTPServer.cpp b/Common/Net/HTTPServer.cpp
index 34bc87a7a0..c69aae0125 100644
--- a/Common/Net/HTTPServer.cpp
+++ b/Common/Net/HTTPServer.cpp
@@ -66,7 +66,7 @@ ServerRequest::ServerRequest(int fd)
header_.ParseHeaders(in_);
if (header_.ok) {
- VERBOSE_LOG(Log::IO, "The request carried with it %i bytes", (int)header_.content_length);
+ VERBOSE_LOG(Log::HTTP, "The request carried with it %i bytes", (int)header_.content_length);
} else {
Close();
}
@@ -76,11 +76,11 @@ ServerRequest::~ServerRequest() {
Close();
if (!in_->Empty()) {
- ERROR_LOG(Log::IO, "Input not empty - invalid request?");
+ ERROR_LOG(Log::HTTP, "Input not empty - invalid request?");
}
delete in_;
if (!out_->Empty()) {
- WARN_LOG(Log::IO, "Output not empty - connection abort? (%s) (%d bytes)", this->header_.resource, (int)out_->BytesRemaining());
+ WARN_LOG(Log::HTTP, "Output not empty - connection abort? (%s) (%d bytes)", this->header_.resource, (int)out_->BytesRemaining());
}
delete out_;
}
@@ -170,8 +170,8 @@ bool Server::Listen(int port, const char *reason, net::DNSType type) {
}
bool Server::Listen4(int port, const char *reason) {
- listener_ = socket(AF_INET, SOCK_STREAM, 0);
- if (listener_ < 0)
+ listenerSock_ = socket(AF_INET, SOCK_STREAM, 0);
+ if (listenerSock_ < 0)
return false;
struct sockaddr_in server_addr;
@@ -182,42 +182,43 @@ bool Server::Listen4(int port, const char *reason) {
int opt = 1;
// Enable re-binding to avoid the pain when restarting the server quickly.
- setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
+ setsockopt(listenerSock_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
- if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
+ if (bind(listenerSock_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
#if PPSSPP_PLATFORM(WINDOWS)
int err = WSAGetLastError();
#else
int err = errno;
#endif
- ERROR_LOG(Log::IO, "%s: Failed to bind to port %d, error=%d - Bailing (ipv4)", reason, port, err);
- closesocket(listener_);
+ ERROR_LOG(Log::HTTP, "%s: Failed to bind to port %d, error=%d - Bailing (ipv4)", reason, port, err);
+ closesocket(listenerSock_);
return false;
}
- fd_util::SetNonBlocking(listener_, true);
+ fd_util::SetNonBlocking(listenerSock_, true);
// 1024 is the max number of queued requests.
- if (listen(listener_, 1024) < 0) {
- closesocket(listener_);
+ if (listen(listenerSock_, 1024) < 0) {
+ closesocket(listenerSock_);
return false;
}
socklen_t len = sizeof(server_addr);
- if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) {
+ if (getsockname(listenerSock_, (struct sockaddr *)&server_addr, &len) == 0) {
port = ntohs(server_addr.sin_port);
}
- INFO_LOG(Log::IO, "HTTP server started on port %d: %s", port, reason);
- port_ = port;
+ localAddress_ = fd_util::GetLocalIP(listenerSock_);
+ INFO_LOG(Log::HTTP, "HTTP IPv4 server started on port %d: %s (ip: %s)", port, reason, localAddress_.c_str());
+ port_ = port;
return true;
}
bool Server::Listen6(int port, bool ipv6_only, const char *reason) {
#if !PPSSPP_PLATFORM(SWITCH)
- listener_ = socket(AF_INET6, SOCK_STREAM, 0);
- if (listener_ < 0)
+ listenerSock_ = socket(AF_INET6, SOCK_STREAM, 0);
+ if (listenerSock_ < 0)
return false;
struct sockaddr_in6 server_addr;
@@ -228,54 +229,55 @@ bool Server::Listen6(int port, bool ipv6_only, const char *reason) {
int opt = 1;
// Enable re-binding to avoid the pain when restarting the server quickly.
- setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
+ setsockopt(listenerSock_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
// Enable listening on IPv6 and IPv4?
opt = ipv6_only ? 1 : 0;
- setsockopt(listener_, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt));
+ setsockopt(listenerSock_, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt));
- if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
+ if (bind(listenerSock_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
#if PPSSPP_PLATFORM(WINDOWS)
int err = WSAGetLastError();
#else
int err = errno;
#endif
- ERROR_LOG(Log::IO, "%s: Failed to bind to port %d, error=%d - Bailing (ipv6)", reason, port, err);
- closesocket(listener_);
+ ERROR_LOG(Log::HTTP, "%s: Failed to bind to port %d, error=%d - Bailing (ipv6)", reason, port, err);
+ closesocket(listenerSock_);
return false;
}
- fd_util::SetNonBlocking(listener_, true);
+ fd_util::SetNonBlocking(listenerSock_, true);
// 1024 is the max number of queued requests.
- if (listen(listener_, 1024) < 0) {
- closesocket(listener_);
+ if (listen(listenerSock_, 1024) < 0) {
+ closesocket(listenerSock_);
return false;
}
socklen_t len = sizeof(server_addr);
- if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) {
+ if (getsockname(listenerSock_, (struct sockaddr *)&server_addr, &len) == 0) {
port = ntohs(server_addr.sin6_port);
}
- INFO_LOG(Log::IO, "HTTP server started on port %d: %s", port, reason);
+ localAddress_ = fd_util::GetLocalIP(listenerSock_);
+ INFO_LOG(Log::HTTP, "HTTP IPv6 server started on port %d: %s (ip: %s)", port, reason, localAddress_.c_str());
port_ = port;
-
return true;
#else
+ ERROR_LOG(Log::HTTP, "IPv6 not supported on this platform.");
return false;
#endif
}
bool Server::RunSlice(double timeout) {
- if (listener_ < 0 || port_ == 0) {
+ if (listenerSock_ < 0 || port_ == 0) {
return false;
}
if (timeout <= 0.0) {
timeout = 86400.0;
}
- if (!fd_util::WaitUntilReady(listener_, timeout, false)) {
+ if (!fd_util::WaitUntilReady(listenerSock_, timeout, false)) {
return false;
}
@@ -287,13 +289,13 @@ bool Server::RunSlice(double timeout) {
#endif
} client_addr;
socklen_t client_addr_size = sizeof(client_addr);
- int conn_fd = accept(listener_, &client_addr.sa, &client_addr_size);
+ int conn_fd = accept(listenerSock_, &client_addr.sa, &client_addr_size);
if (conn_fd >= 0) {
executor_->Run(std::bind(&Server::HandleConnection, this, conn_fd));
return true;
}
else {
- ERROR_LOG(Log::IO, "socket accept failed: %i", conn_fd);
+ ERROR_LOG(Log::HTTP, "socket accept failed: %i", conn_fd);
return false;
}
}
@@ -312,13 +314,13 @@ bool Server::Run(int port) {
}
void Server::Stop() {
- closesocket(listener_);
+ closesocket(listenerSock_);
}
void Server::HandleConnection(int conn_fd) {
ServerRequest request(conn_fd);
if (!request.IsOK()) {
- WARN_LOG(Log::IO, "Bad request, ignoring.");
+ WARN_LOG(Log::HTTP, "Bad request, ignoring.");
return;
}
HandleRequest(request);
@@ -350,7 +352,7 @@ void Server::HandleRequestDefault(const ServerRequest &request) {
}
void Server::Handle404(const ServerRequest &request) {
- INFO_LOG(Log::IO, "No handler for '%.*s', falling back to 404.", (int)request.resource().size(), request.resource().data());
+ INFO_LOG(Log::HTTP, "No handler for '%.*s', falling back to 404.", (int)request.resource().size(), request.resource().data());
const char *payload = "
404 not found\r\n";
request.WriteHttpResponseHeader("1.0", 404, strlen(payload));
request.Out()->Push(payload);
diff --git a/Common/Net/HTTPServer.h b/Common/Net/HTTPServer.h
index 4f117069ae..bc2b2bc6e7 100644
--- a/Common/Net/HTTPServer.h
+++ b/Common/Net/HTTPServer.h
@@ -96,9 +96,15 @@ public:
// if they don't recognize the url.
virtual void HandleRequest(const ServerRequest &request);
- int Port() {
+ int ListenerSocket() const {
+ return listenerSock_;
+ }
+ int Port() const {
return port_;
}
+ const std::string &LocalAddress() const {
+ return localAddress_;
+ }
private:
bool Listen6(int port, bool ipv6_only, const char *reason);
@@ -113,8 +119,9 @@ private:
void HandleListing(const ServerRequest &request);
void Handle404(const ServerRequest &request);
- int listener_;
+ int listenerSock_;
int port_ = 0;
+ std::string localAddress_;
UrlHandlerMap handlers_;
UrlHandlerFunc fallback_;
diff --git a/Common/Net/Resolve.cpp b/Common/Net/Resolve.cpp
index eb3727136d..23d3bf989d 100644
--- a/Common/Net/Resolve.cpp
+++ b/Common/Net/Resolve.cpp
@@ -113,7 +113,7 @@ void DNSResolveFree(addrinfo *res)
freeaddrinfo(res);
}
-bool GetIPList(std::vector &IP4s) {
+bool GetLocalIP4List(std::vector &IP4s) {
char ipstr[INET6_ADDRSTRLEN]; // We use IPv6 length since it's longer than IPv4
// getifaddrs first appeared in glibc 2.3, On Android officially supported since __ANDROID_API__ >= 24
#if defined(_IFADDRS_H_) || (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) || (__ANDROID_API__ >= 24)
@@ -145,7 +145,7 @@ bool GetIPList(std::vector &IP4s) {
return true;
}
#elif defined(SIOCGIFCONF) // Better detection on Linux/UNIX/MacOS/some Android
- INFO_LOG(Log::sceNet, "GetIPList from SIOCGIFCONF");
+ INFO_LOG(Log::IO, "GetIPList from SIOCGIFCONF");
static struct ifreq ifreqs[32];
struct ifconf ifc{};
ifc.ifc_req = ifreqs;
@@ -153,13 +153,13 @@ bool GetIPList(std::vector &IP4s) {
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
- ERROR_LOG(Log::sceNet, "GetIPList failed to create socket (result = %i, errno = %i)", sd, socket_errno);
+ ERROR_LOG(Log::IO, "GetIPList failed to create socket (result = %i, errno = %i)", sd, socket_errno);
return false;
}
int r = ioctl(sd, SIOCGIFCONF, (char*)&ifc);
if (r != 0) {
- ERROR_LOG(Log::sceNet, "GetIPList failed ioctl/SIOCGIFCONF (result = %i, errno = %i)", r, socket_errno);
+ ERROR_LOG(Log::IO, "GetIPList failed ioctl/SIOCGIFCONF (result = %i, errno = %i)", r, socket_errno);
return false;
}
@@ -175,7 +175,7 @@ bool GetIPList(std::vector &IP4s) {
r = ioctl(sd, SIOCGIFADDR, item);
if (r != 0)
{
- ERROR_LOG(Log::sceNet, "GetIPList failed ioctl/SIOCGIFADDR (i = %i, result = %i, errno = %i)", i, r, socket_errno);
+ ERROR_LOG(Log::IO, "GetIPList failed ioctl/SIOCGIFADDR (i = %i, result = %i, errno = %i)", i, r, socket_errno);
}
if (ifreqs[i].ifr_addr.sa_family == AF_INET) {
@@ -194,8 +194,8 @@ bool GetIPList(std::vector &IP4s) {
close(sd);
return true;
-#else // Fallback to POSIX/Cross-platform way but may not works well on Linux (ie. only shows 127.0.0.1)
- INFO_LOG(Log::sceNet, "GetIPList from Fallback");
+#else // Fallback to POSIX/Cross-platform way but may not work well on Linux (ie. only shows 127.0.0.1)
+ DEBUG_LOG(Log::IO, "GetIPList from fallback method");
struct addrinfo hints, * res, * p;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
diff --git a/Common/Net/Resolve.h b/Common/Net/Resolve.h
index 5c7958d654..09c2ab168c 100644
--- a/Common/Net/Resolve.h
+++ b/Common/Net/Resolve.h
@@ -20,7 +20,7 @@ enum class DNSType {
bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type = DNSType::ANY);
void DNSResolveFree(addrinfo *res);
-bool GetIPList(std::vector& IP4s);
+bool GetLocalIP4List(std::vector& IP4s);
int inet_pton(int af, const char* src, void* dst);
diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp
index 710479c98e..0fde0fe922 100644
--- a/Core/WebServer.cpp
+++ b/Core/WebServer.cpp
@@ -73,7 +73,7 @@ static ServerStatus RetrieveStatus() {
}
// This reports the local IP address to report.ppsspp.org, which can then
-// relay that address to a mobile device searching for the server.
+// relay that address to a mobile device on the same wifi/LAN searching for the server.
static bool RegisterServer(int port) {
bool success = false;
http::Client http(nullptr);
@@ -86,7 +86,7 @@ static bool RegisterServer(int port) {
char resource4[1024]{};
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) {
if (http.Connect()) {
- std::string ip = fd_util::GetLocalIP(http.sock());
+ std::string ip = http.GetLocalIpAsString();
snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port);
if (http.GET(http::RequestParams(resource4), &theVoid, &progress) > 0)
@@ -111,7 +111,7 @@ static bool RegisterServer(int port) {
// Currently, we're not using keepalive, so gotta reconnect...
if (http.Connect(timeout)) {
char resource6[1024] = {};
- std::string ip = fd_util::GetLocalIP(http.sock());
+ std::string ip = http.GetLocalIpAsString();
snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port);
if (http.GET(http::RequestParams(resource6), &theVoid, &progress) > 0)
@@ -388,6 +388,7 @@ static void HandleFallback(const http::ServerRequest &request) {
static void ForwardDebuggerRequest(const http::ServerRequest &request) {
SetCurrentThreadName("ForwardDebuggerRequest");
+ // Hm, is this needed?
AndroidJNIThreadContext jniContext;
if (serverFlags & (int)WebServerFlags::DEBUGGER) {
@@ -408,7 +409,26 @@ static void ForwardDebuggerRequest(const http::ServerRequest &request) {
}
}
-static void ExecuteWebServer() {
+static void HandleUploadUI(const http::ServerRequest &request) {
+ // Read the file from VFS.
+ AndroidJNIThreadContext jniContext;
+ request.WriteHttpResponseHeader("1.0", 200, -1, "text/html");
+ request.Out()->Push(
+ "PPSSPP Remote ISO Upload"
+ "Upload ISO File
"
+ ""
+ "");
+}
+
+static void HandleUploadPost(const http::ServerRequest &request) {
+ AndroidJNIThreadContext jniContext;
+
+}
+
+static void WebServerThread() {
SetCurrentThreadName("HTTPServer");
AndroidJNIThreadContext context; // Destructor detaches.
@@ -418,6 +438,8 @@ static void ExecuteWebServer() {
// This lists all the (current) recent ISOs. It also handles the debugger, which is very ugly.
http->SetFallbackHandler(&HandleFallback);
http->RegisterHandler("/debugger", &ForwardDebuggerRequest);
+ http->RegisterHandler("/upload", &HandleUploadUI);
+ http->RegisterHandler("/upload_post", &HandleUploadPost);
if (!http->Listen(g_Config.iRemoteISOPort, "debugger-webserver")) {
if (!http->Listen(0, "debugger-webserver")) {
@@ -426,11 +448,14 @@ static void ExecuteWebServer() {
return;
}
}
+
UpdateStatus(ServerStatus::RUNNING);
g_Config.iRemoteISOPort = http->Port();
RegisterServer(http->Port());
double lastRegister = time_now_d();
+
+ INFO_LOG(Log::HTTP, "Entering web server loop. Listening on port %d", g_Config.iRemoteISOPort);
while (RetrieveStatus() == ServerStatus::RUNNING) {
constexpr double webServerSliceSeconds = 0.2f;
http->RunSlice(webServerSliceSeconds);
@@ -440,6 +465,7 @@ static void ExecuteWebServer() {
lastRegister = now;
}
}
+ INFO_LOG(Log::HTTP, "Leaving web server loop.");
http->Stop();
StopAllDebuggers();
@@ -448,11 +474,13 @@ static void ExecuteWebServer() {
UpdateStatus(ServerStatus::FINISHED);
}
+// Only adds flags.
bool StartWebServer(WebServerFlags flags) {
std::lock_guard guard(serverStatusLock);
switch (serverStatus) {
case ServerStatus::RUNNING:
if ((serverFlags & (int)flags) == (int)flags) {
+ // Already running with these flags.
return false;
}
serverFlags |= (int)flags;
@@ -465,7 +493,7 @@ bool StartWebServer(WebServerFlags flags) {
case ServerStatus::STOPPED:
serverStatus = ServerStatus::STARTING;
serverFlags = (int)flags;
- serverThread = std::thread(&ExecuteWebServer);
+ serverThread = std::thread(&WebServerThread);
return true;
default:
@@ -473,6 +501,7 @@ bool StartWebServer(WebServerFlags flags) {
}
}
+// Only removes flags.
bool StopWebServer(WebServerFlags flags) {
std::lock_guard guard(serverStatusLock);
if (serverStatus != ServerStatus::RUNNING) {
@@ -510,3 +539,7 @@ void ShutdownWebServer() {
bool WebServerRunning(WebServerFlags flags) {
return RetrieveStatus() == ServerStatus::RUNNING && (serverFlags & (int)flags) != 0;
}
+
+int WebServerPort() {
+ return g_Config.iRemoteISOPort;
+}
diff --git a/Core/WebServer.h b/Core/WebServer.h
index cd0d2cc3ec..26d2f64c71 100644
--- a/Core/WebServer.h
+++ b/Core/WebServer.h
@@ -23,8 +23,9 @@ enum class WebServerFlags {
NONE = 0,
DISCS = 1,
DEBUGGER = 2,
+ FILE_UPLOAD = 4,
- ALL = 1 | 2,
+ ALL = 1 | 2 | 4,
};
ENUM_CLASS_BITOPS(WebServerFlags);
@@ -36,3 +37,4 @@ bool WebServerRunning(WebServerFlags flags);
void ShutdownWebServer();
bool RemoteISOFileSupported(const std::string &filename);
+int WebServerPort();
diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp
index 19c5737f72..956916330e 100644
--- a/UI/DevScreens.cpp
+++ b/UI/DevScreens.cpp
@@ -496,6 +496,9 @@ void ShaderViewScreen::CreateViews() {
LinearLayout *topbar = new LinearLayout(ORIENT_HORIZONTAL);
topbar->Add(new Choice(ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams()))->OnClick.Handle(this, &UIScreen::OnBack);
+ topbar->Add(new Choice(ImageID("I_FILE_COPY"), new LinearLayoutParams()))->OnClick.Add([this](UI::EventParams &e) {
+ System_CopyStringToClipboard(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC));
+ });
topbar->Add(new TextView(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC), FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT, false));
layout->Add(topbar);
diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp
index fbb440029b..dc79bd3d4c 100644
--- a/UI/GameSettingsScreen.cpp
+++ b/UI/GameSettingsScreen.cpp
@@ -1874,7 +1874,7 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) {
}
// Add non-editable items
listIP.push_back("localhost");
- net::GetIPList(listIP);
+ net::GetLocalIP4List(listIP);
ipRows_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
ScrollView* scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp
index 39d155c46d..e4d0da1bac 100644
--- a/UI/MainScreen.cpp
+++ b/UI/MainScreen.cpp
@@ -62,6 +62,7 @@
#include "UI/DisplayLayoutScreen.h"
#include "UI/SavedataScreen.h"
#include "UI/Store.h"
+#include "UI/UploadScreen.h"
#include "UI/InstallZipScreen.h"
#include "Core/Config.h"
#include "Core/Loaders.h"
@@ -815,6 +816,12 @@ void GameBrowser::Refresh() {
topBar->Add(new Choice(mm->T("PPSSPP Homebrew Store"), new UI::LinearLayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::OnHomebrewStore);
}
+ if (browseFlags_ & BrowseFlags::UPLOAD_BUTTON) {
+ topBar->Add(new Choice(ImageID("I_FOLDER_UPLOAD"), new UI::LinearLayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Add([this](UI::EventParams &e) {
+ screenManager_->push(new UploadScreen());
+ });
+ }
+
ChoiceStrip *layoutChoice = topBar->Add(new ChoiceStrip(ORIENT_HORIZONTAL));
layoutChoice->AddChoice(ImageID("I_GRID"));
layoutChoice->AddChoice(ImageID("I_LINES"));
diff --git a/UI/MainScreen.h b/UI/MainScreen.h
index 40da0b1945..358c60fca2 100644
--- a/UI/MainScreen.h
+++ b/UI/MainScreen.h
@@ -38,7 +38,8 @@ enum class BrowseFlags {
ARCHIVES = 4,
PIN = 8,
HOMEBREW_STORE = 16,
- STANDARD = 1 | 2 | 4 | 8,
+ UPLOAD_BUTTON = 32,
+ STANDARD = 1 | 2 | 4 | 8 | 32,
};
ENUM_CLASS_BITOPS(BrowseFlags);
diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj
index 42ad2df168..551778b5e7 100644
--- a/UI/UI.vcxproj
+++ b/UI/UI.vcxproj
@@ -75,6 +75,7 @@
+
@@ -124,6 +125,7 @@
+
diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters
index 3d8693dd36..ecbd604bcb 100644
--- a/UI/UI.vcxproj.filters
+++ b/UI/UI.vcxproj.filters
@@ -129,6 +129,9 @@
ImDebugger
+
+ Screens
+
@@ -258,6 +261,9 @@
ImDebugger
+
+ Screens
+
diff --git a/UI/UIAtlas.cpp b/UI/UIAtlas.cpp
index 5da10ea751..673a084939 100644
--- a/UI/UIAtlas.cpp
+++ b/UI/UIAtlas.cpp
@@ -138,6 +138,9 @@ static const ImageMeta imageIDs[] = {
{"I_SETTINGS_DISPLAY", false},
{"I_NAVIGATE_BACK", false},
{"I_NAVIGATE_FORWARD", false},
+ {"I_FOLDER_UPLOAD", false},
+ {"I_FILE", false},
+ {"I_FILE_COPY", false},
};
static std::string PNGNameFromID(std::string_view id) {
diff --git a/UI/UploadScreen.cpp b/UI/UploadScreen.cpp
new file mode 100644
index 0000000000..581e1ebe45
--- /dev/null
+++ b/UI/UploadScreen.cpp
@@ -0,0 +1,52 @@
+#include "Common/Net/Resolve.h"
+#include "Common/System/Request.h"
+#include "UI/UploadScreen.h"
+#include "Common/StringUtils.h"
+#include "Core/WebServer.h"
+
+UploadScreen::UploadScreen() {
+ net::GetLocalIP4List(localIPs_);
+
+ StartWebServer(WebServerFlags::FILE_UPLOAD);
+}
+
+UploadScreen::~UploadScreen() {
+ StopWebServer(WebServerFlags::FILE_UPLOAD);
+}
+
+void UploadScreen::CreateViews() {
+ using namespace UI;
+ auto co = GetI18NCategory(I18NCat::NETWORKING);
+ root_ = new LinearLayout(ORIENT_VERTICAL);
+ LinearLayout *topBar = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
+ topBar->Add(new Choice(ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams()))->OnClick.Handle(this, &UIScreen::OnBack);
+
+ root_->Add(topBar);
+
+ if (prevRunning_) {
+ root_->Add(new TextView(co->T("On your other device, connect to the same network, then go to this URL:")));
+ for (const auto &ip : localIPs_) {
+ std::string url = StringFromFormat("http://%s:%d/upload", ip.c_str(), WebServerPort());
+ root_->Add(new TextView(url));
+ root_->Add(new Choice(ImageID("I_FILE_COPY")))->OnClick.Add([url](UI::EventParams &) {
+ System_CopyStringToClipboard(url);
+ });
+ }
+ } else {
+ root_->Add(new TextView(co->T("Connecting...")));
+ }
+
+ //infoText->SetTextAlignment(TEXT_ALIGN_CENTER);
+ //verticalLayout->AddView(infoText);
+ //AddStandardBack(verticalLayout);
+ //root_->AddView(verticalLayout);
+}
+
+void UploadScreen::update() {
+ UIScreen::update();
+ bool running = WebServerRunning(WebServerFlags::FILE_UPLOAD);
+ if (prevRunning_ != running) {
+ prevRunning_ = running;
+ RecreateViews();
+ }
+}
diff --git a/UI/UploadScreen.h b/UI/UploadScreen.h
new file mode 100644
index 0000000000..4f4d0bb1cd
--- /dev/null
+++ b/UI/UploadScreen.h
@@ -0,0 +1,47 @@
+#pragma once
+
+// Copyright (c) 2013- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#include
+#include
+#include
+
+#include "Common/UI/UIScreen.h"
+#include "Common/UI/ViewGroup.h"
+#include "UI/MiscScreens.h"
+
+// Upload screen: Shows the user an ip address to go to in a web browser on the same network,
+// in order to upload game files to the current directory.
+
+class UploadScreen : public UIDialogScreenWithBackground {
+public:
+ UploadScreen();
+ ~UploadScreen();
+ void CreateViews() override;
+
+protected:
+ void update() override;
+
+ const char *tag() const override { return "Upload"; }
+
+private:
+ bool prevRunning_ = false;
+ std::vector localIPs_;
+};
diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj
index 4d14b3fa83..a4f785d754 100644
--- a/UWP/UI_UWP/UI_UWP.vcxproj
+++ b/UWP/UI_UWP/UI_UWP.vcxproj
@@ -132,6 +132,7 @@
+
@@ -184,6 +185,7 @@
+
Create
diff --git a/UWP/UI_UWP/UI_UWP.vcxproj.filters b/UWP/UI_UWP/UI_UWP.vcxproj.filters
index f064528ae2..addb46f19c 100644
--- a/UWP/UI_UWP/UI_UWP.vcxproj.filters
+++ b/UWP/UI_UWP/UI_UWP.vcxproj.filters
@@ -124,6 +124,9 @@
Screens
+
+ Screens
+
@@ -249,6 +252,9 @@
Screens
+
+ Screens
+
diff --git a/Windows/main.cpp b/Windows/main.cpp
index cf5ab99e4d..db8050dcea 100644
--- a/Windows/main.cpp
+++ b/Windows/main.cpp
@@ -433,6 +433,8 @@ bool System_GetPropertyBool(SystemProperty prop) {
#else
return false;
#endif
+ case SYSPROP_HAS_ACCELEROMETER:
+ return true; // for debugging
default:
return false;
}
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 519895951a..23c1f27f77 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -922,6 +922,7 @@ LOCAL_SRC_FILES := \
$(SRC)/UI/JoystickHistoryView.cpp \
$(SRC)/UI/GameInfoCache.cpp \
$(SRC)/UI/GameScreen.cpp \
+ $(SRC)/UI/UploadScreen.cpp \
$(SRC)/UI/ControlMappingScreen.cpp \
$(SRC)/UI/GameSettingsScreen.cpp \
$(SRC)/UI/DeveloperToolsScreen.cpp \
diff --git a/assets/ui_images/images.svg b/assets/ui_images/images.svg
index 9675533ad5..1c0450333c 100644
--- a/assets/ui_images/images.svg
+++ b/assets/ui_images/images.svg
@@ -24,15 +24,15 @@
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
- inkscape:zoom="2.8284272"
- inkscape:cx="322.97101"
- inkscape:cy="372.46849"
+ inkscape:zoom="8.0000002"
+ inkscape:cx="297.18749"
+ inkscape:cy="545.18749"
inkscape:window-width="3379"
inkscape:window-height="1941"
inkscape:window-x="-9"
inkscape:window-y="0"
inkscape:window-maximized="0"
- inkscape:current-layer="svg1"
+ inkscape:current-layer="layer1"
showgrid="false" />
-