mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
UI: Allow remote paths to be pinned.
Currently, the listing request is synchronous, but it works fine otherwise.
This commit is contained in:
parent
3e12734b80
commit
2d7ce0afa3
6 changed files with 126 additions and 81 deletions
|
@ -1141,8 +1141,9 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
|||
vPinnedPaths.clear();
|
||||
for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) {
|
||||
// Unpin paths that are deleted automatically.
|
||||
if (File::Exists(it->second)) {
|
||||
vPinnedPaths.push_back(File::ResolvePath(it->second));
|
||||
const std::string &path = it->second;
|
||||
if (startsWith(path, "http://") || startsWith(path, "https://") || File::Exists(path)) {
|
||||
vPinnedPaths.push_back(File::ResolvePath(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -577,7 +577,7 @@ void GameBrowser::Refresh() {
|
|||
|
||||
if (!isGame && !isSaveData) {
|
||||
if (allowBrowsing_) {
|
||||
dirButtons.push_back(new DirButton(fileInfo[i].name, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)));
|
||||
dirButtons.push_back(new DirButton(fileInfo[i].fullName, fileInfo[i].name, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)));
|
||||
}
|
||||
} else {
|
||||
gameButtons.push_back(new GameButton(fileInfo[i].fullName, *gridStyle_, new UI::LinearLayoutParams(*gridStyle_ == true ? UI::WRAP_CONTENT : UI::FILL_PARENT, UI::WRAP_CONTENT)));
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "base/timeutil.h"
|
||||
#include "file/path.h"
|
||||
#include "i18n/i18n.h"
|
||||
#include "json/json_reader.h"
|
||||
#include "net/http_client.h"
|
||||
|
@ -146,84 +147,21 @@ static bool FindServer(std::string &resultHost, int &resultPort) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool LoadRemoteGameList(const std::string &url, bool *cancel, std::vector<std::string> &games) {
|
||||
http::Client http;
|
||||
Buffer result;
|
||||
int code = 500;
|
||||
std::vector<std::string> responseHeaders;
|
||||
|
||||
Url baseURL(url);
|
||||
if (!baseURL.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start by requesting the list of games from the server.
|
||||
if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) {
|
||||
if (http.Connect(2, 20.0, cancel)) {
|
||||
code = http.GET(baseURL.Resource().c_str(), &result, responseHeaders);
|
||||
http.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
if (code != 200 || (cancel && *cancel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string listing;
|
||||
std::vector<std::string> items;
|
||||
result.TakeAll(&listing);
|
||||
|
||||
std::string contentType;
|
||||
for (const std::string &header : responseHeaders) {
|
||||
if (startsWithNoCase(header, "Content-Type:")) {
|
||||
contentType = header.substr(strlen("Content-Type:"));
|
||||
// Strip any whitespace (TODO: maybe move this to stringutil?)
|
||||
contentType.erase(0, contentType.find_first_not_of(" \t\r\n"));
|
||||
contentType.erase(contentType.find_last_not_of(" \t\r\n") + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Technically, "TExt/hTml ; chaRSet = Utf8" should pass, but "text/htmlese" should not.
|
||||
// But unlikely that'll be an issue.
|
||||
bool parseHtml = startsWithNoCase(contentType, "text/html");
|
||||
bool parseText = startsWithNoCase(contentType, "text/plain");
|
||||
|
||||
if (parseText) {
|
||||
// Plain text format - easy.
|
||||
SplitString(listing, '\n', items);
|
||||
} else if (parseHtml) {
|
||||
// Try to extract from an automatic webserver directory listing...
|
||||
GetQuotedStrings(listing, items);
|
||||
} else {
|
||||
ERROR_LOG(FILESYS, "Unsupported Content-Type: %s", contentType.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (std::string item : items) {
|
||||
// Apply some workarounds.
|
||||
if (item.empty())
|
||||
continue;
|
||||
|
||||
if (item.back() == '\r')
|
||||
item.pop_back();
|
||||
|
||||
if (!RemoteISOFileSupported(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
games.push_back(baseURL.Relative(item).ToString());
|
||||
}
|
||||
|
||||
return !games.empty();
|
||||
}
|
||||
|
||||
static bool LoadGameList(const std::string &host, int port, std::vector<std::string> &games) {
|
||||
std::string subdir = RemoteSubdir();
|
||||
|
||||
char temp[1024];
|
||||
snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s", host.c_str(), port, subdir.c_str());
|
||||
|
||||
LoadRemoteGameList(temp, &scanCancelled, games);
|
||||
PathBrowser browser(temp);
|
||||
std::vector<FileInfo> files;
|
||||
browser.GetListing(files, "iso:cso:pbp:elf:prx:ppdmp:", &scanCancelled);
|
||||
if (scanCancelled) {
|
||||
return false;
|
||||
}
|
||||
for (auto &file : files) {
|
||||
games.push_back(file.fullName);
|
||||
}
|
||||
|
||||
// Save for next time unless manual is true
|
||||
if (!games.empty() && !g_Config.bRemoteISOManual) {
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include "UI/MiscScreens.h"
|
||||
#include "UI/MainScreen.h"
|
||||
|
||||
bool LoadRemoteGameList(const std::string &url, bool *cancel, std::vector<std::string> &games);
|
||||
|
||||
class RemoteISOScreen : public UIScreenWithBackground {
|
||||
public:
|
||||
RemoteISOScreen();
|
||||
|
|
|
@ -1,4 +1,107 @@
|
|||
#include <algorithm>
|
||||
#include <set>
|
||||
#include "base/stringutil.h"
|
||||
#include "file/path.h"
|
||||
#include "net/http_client.h"
|
||||
#include "net/url.h"
|
||||
|
||||
bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<FileInfo> &files, const char *filter) {
|
||||
http::Client http;
|
||||
Buffer result;
|
||||
int code = 500;
|
||||
std::vector<std::string> responseHeaders;
|
||||
|
||||
Url baseURL(url);
|
||||
if (!baseURL.Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start by requesting the list of files from the server.
|
||||
if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) {
|
||||
if (http.Connect(2, 20.0, cancel)) {
|
||||
code = http.GET(baseURL.Resource().c_str(), &result, responseHeaders);
|
||||
http.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
if (code != 200 || (cancel && *cancel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string listing;
|
||||
std::vector<std::string> items;
|
||||
result.TakeAll(&listing);
|
||||
|
||||
std::string contentType;
|
||||
for (const std::string &header : responseHeaders) {
|
||||
if (startsWithNoCase(header, "Content-Type:")) {
|
||||
contentType = header.substr(strlen("Content-Type:"));
|
||||
// Strip any whitespace (TODO: maybe move this to stringutil?)
|
||||
contentType.erase(0, contentType.find_first_not_of(" \t\r\n"));
|
||||
contentType.erase(contentType.find_last_not_of(" \t\r\n") + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Technically, "TExt/hTml ; chaRSet = Utf8" should pass, but "text/htmlese" should not.
|
||||
// But unlikely that'll be an issue.
|
||||
bool parseHtml = startsWithNoCase(contentType, "text/html");
|
||||
bool parseText = startsWithNoCase(contentType, "text/plain");
|
||||
|
||||
if (parseText) {
|
||||
// Plain text format - easy.
|
||||
SplitString(listing, '\n', items);
|
||||
} else if (parseHtml) {
|
||||
// Try to extract from an automatic webserver directory listing...
|
||||
GetQuotedStrings(listing, items);
|
||||
} else {
|
||||
ELOG("Unsupported Content-Type: %s", contentType.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<std::string> filters;
|
||||
if (filter) {
|
||||
std::string tmp;
|
||||
while (*filter) {
|
||||
if (*filter == ':') {
|
||||
filters.insert(std::move(tmp));
|
||||
} else {
|
||||
tmp.push_back(*filter);
|
||||
}
|
||||
filter++;
|
||||
}
|
||||
if (!tmp.empty())
|
||||
filters.insert(std::move(tmp));
|
||||
}
|
||||
|
||||
for (std::string item : items) {
|
||||
// Apply some workarounds.
|
||||
if (item.empty())
|
||||
continue;
|
||||
if (item.back() == '\r')
|
||||
item.pop_back();
|
||||
|
||||
FileInfo info;
|
||||
info.name = item;
|
||||
info.fullName = baseURL.Relative(item).ToString();
|
||||
info.isDirectory = endsWith(item, "/");
|
||||
info.exists = true;
|
||||
info.size = 0;
|
||||
info.isWritable = false;
|
||||
if (!info.isDirectory) {
|
||||
std::string ext = getFileExtension(info.fullName);
|
||||
if (filter) {
|
||||
if (filters.find(ext) == filters.end())
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
files.push_back(info);
|
||||
}
|
||||
|
||||
|
||||
std::sort(files.begin(), files.end());
|
||||
return !files.empty();
|
||||
}
|
||||
|
||||
// Normalize slashes.
|
||||
void PathBrowser::SetPath(const std::string &path) {
|
||||
|
@ -14,7 +117,7 @@ void PathBrowser::SetPath(const std::string &path) {
|
|||
path_ += "/";
|
||||
}
|
||||
|
||||
void PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter) {
|
||||
bool PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter, bool *cancel) {
|
||||
#ifdef _WIN32
|
||||
if (path_ == "/") {
|
||||
// Special path that means root of file system.
|
||||
|
@ -34,7 +137,12 @@ void PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter
|
|||
}
|
||||
#endif
|
||||
|
||||
getFilesInDir(path_.c_str(), &fileInfo, filter);
|
||||
if (startsWith(path_, "http://") || startsWith(path_, "https://")) {
|
||||
return LoadRemoteFileList(path_, cancel, fileInfo, filter);
|
||||
} else {
|
||||
getFilesInDir(path_.c_str(), &fileInfo, filter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support paths like "../../hello"
|
||||
|
@ -52,7 +160,7 @@ void PathBrowser::Navigate(const std::string &path) {
|
|||
path_ = path_.substr(0, slash + 1);
|
||||
}
|
||||
} else {
|
||||
if (path[1] == ':' && path_ == "/")
|
||||
if (path.size() > 2 && path[1] == ':' && path_ == "/")
|
||||
path_ = path;
|
||||
else
|
||||
path_ = path_ + path;
|
||||
|
|
|
@ -16,7 +16,7 @@ public:
|
|||
PathBrowser(std::string path) { SetPath(path); }
|
||||
|
||||
void SetPath(const std::string &path);
|
||||
void GetListing(std::vector<FileInfo> &fileInfo, const char *filter = 0);
|
||||
bool GetListing(std::vector<FileInfo> &fileInfo, const char *filter = nullptr, bool *cancel = nullptr);
|
||||
void Navigate(const std::string &path);
|
||||
|
||||
std::string GetPath() const {
|
||||
|
|
Loading…
Add table
Reference in a new issue