diff --git a/Common/Net/Sinks.cpp b/Common/Net/Sinks.cpp index 0c8e485e85..3183ead35c 100644 --- a/Common/Net/Sinks.cpp +++ b/Common/Net/Sinks.cpp @@ -59,7 +59,7 @@ std::pair InputSink::BufferParts() const { } size_t InputSink::ReadBinaryUntilTerminator(char *dest, size_t bufSize, std::string_view terminator, bool *didReadTerminator) { - Block(); + Fill(); auto [part1, part2] = BufferParts(); size_t offset = SplitSearch(terminator, part1, part2); diff --git a/Common/System/System.h b/Common/System/System.h index 084614d4de..ce3a2b779d 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -145,6 +145,7 @@ enum SystemProperty { SYSPROP_CLIPBOARD_TEXT, SYSPROP_GPUDRIVER_VERSION, SYSPROP_BUILD_VERSION, + SYSPROP_COMPUTER_NAME, // Separate SD cards or similar. // Need hacky solutions to get at this. diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index cce8e42172..ab5891dcc0 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -32,6 +32,7 @@ #include "Common/File/VFS/VFS.h" #include "Common/TimeUtil.h" #include "Common/StringUtils.h" +#include "Common/System/System.h" #include "Core/Util/RecentFiles.h" #include "Core/Config.h" #include "Core/Debugger/WebSocket.h" @@ -322,6 +323,45 @@ static void HandleListing(const http::ServerRequest &request) { } } +std::string RenderTemplate(std::string_view input, const std::map& values) { + std::string output; + output.reserve(input.size()); + + size_t i = 0; + while (i < input.size()) { + if (i + 3 < input.size() && input[i] == '{' && input[i + 1] == '{') { + size_t end = input.find("}}", i + 2); + if (end != std::string_view::npos) { + // Trim whitespace around key + size_t startKey = i + 2; + while (startKey < end && std::isspace(static_cast(input[startKey]))) + startKey++; + size_t endKey = end; + while (endKey > startKey && std::isspace(static_cast(input[endKey - 1]))) + endKey--; + + std::string key(input.substr(startKey, endKey - startKey)); + + auto it = values.find(key); + if (it != values.end()) { + output += it->second; + } else { + // Keep the original placeholder if key not found + output.append(input.substr(i, end + 2 - i)); + } + + i = end + 2; + continue; + } + } + + output.push_back(input[i]); + ++i; + } + + return output; +} + static bool ServeAssetFile(const http::ServerRequest &request) { // Skip the slash at the start of the resource path. std::string_view filename = request.resource().substr(1); @@ -330,20 +370,8 @@ static bool ServeAssetFile(const http::ServerRequest &request) { return false; } - size_t size; - // TODO: ReadFile should take a string_view. - uint8_t *data = g_VFS.ReadFile(std::string(filename).c_str(), &size); - if (!data) { - // Try appending index.html - data = g_VFS.ReadFile((std::string(filename) + "/index.html").c_str(), &size); - if (!data) { - return false; - } - INFO_LOG(Log::HTTP, "Redirected to /index.html"); - } - std::string ext = Path(filename).GetFileExtension(); - const char *mimeType = "text/plain"; + std::string_view mimeType = "text/plain"; if (ext == ".html") { mimeType = "text/html"; } else if (ext == ".ico") { @@ -358,10 +386,43 @@ static bool ServeAssetFile(const http::ServerRequest &request) { mimeType = "text/css"; } - request.WriteHttpResponseHeader("1.0", 200, size, mimeType); - request.Out()->Push((char *)data, size); + size_t size; + // TODO: ReadFile should take a string_view. + uint8_t *data = g_VFS.ReadFile(std::string(filename).c_str(), &size); + if (!data) { + // Try appending index.html + data = g_VFS.ReadFile((std::string(filename) + "/index.html").c_str(), &size); + mimeType = "text/html"; + if (!data) { + return false; + } + INFO_LOG(Log::HTTP, "Redirected to /index.html"); + } + std::string html = std::string((const char *)data, size); delete[] data; + + // This is a gross, gross hack to have here in ServeAssetFile, but oh well. + if (mimeType == "text/html") { + if (html.find("") != std::string::npos) { + std::string uploadPath = g_uploadPath.ToVisualString(); + std::string deviceName = System_GetProperty(SYSPROP_NAME); + std::string computerName = System_GetProperty(SYSPROP_COMPUTER_NAME); + if (!computerName.empty()) { + deviceName = computerName; + } + std::map values = { + {"upload_path", uploadPath}, + {"device_name", deviceName}, + }; + // Use templating to insert some information. + html = RenderTemplate(html, values); + } + } + + request.WriteHttpResponseHeader("1.0", 200, html.size(), std::string(mimeType).c_str()); + request.Out()->Push(html.data(), html.size()); + return true; } @@ -392,7 +453,7 @@ static void HandleFallback(const http::ServerRequest &request) { } if (serverFlags & WebServerFlags::FILE_UPLOAD) { - if (startsWith(request.resource(), "/upload/")) { + if (startsWith(request.resource(), "/upload")) { if (ServeAssetFile(request)) { return; } diff --git a/Windows/main.cpp b/Windows/main.cpp index db8050dcea..1240f65f98 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -115,6 +115,7 @@ static std::string langRegion; static std::string osName; static std::string osVersion; static std::string gpuDriverVersion; +static std::string computerName; static std::string restartArgs; @@ -253,6 +254,17 @@ std::string System_GetProperty(SystemProperty prop) { return PPSSPP_GIT_VERSION; case SYSPROP_USER_DOCUMENTS_DIR: return Path(W32Util::UserDocumentsPath()).ToString(); // this'll reverse the slashes. + case SYSPROP_COMPUTER_NAME: + if (computerName.empty()) { + wchar_t nameBuf[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD size = ARRAY_SIZE(nameBuf); + if (GetComputerNameW(nameBuf, &size)) { + computerName = ConvertWStringToUTF8(std::wstring(nameBuf, size)); + } else { + computerName = "(N/A)"; + } + } + return computerName; default: return ""; } diff --git a/assets/upload/index.html b/assets/upload/index.html index 0f0522ed6c..b17a35c7fa 100644 --- a/assets/upload/index.html +++ b/assets/upload/index.html @@ -1,208 +1,261 @@ + + - - File/Folder Uploader + + File Uploader - -

Upload Files or Folders

+

Upload files to {{ device_name }}

- - -
- - -
- +

To folder:

{{ upload_path }}

-
- - -
- +
+ + +
-
+
+ +
+ +
+ + +
+