mirror of
https://github.com/libretro/ppsspp.git
synced 2025-02-25 19:30:53 +00:00
commit
cfb93a707c
@ -349,6 +349,8 @@ static ConfigSetting generalSettings[] = {
|
||||
ConfigSetting("RemoteISOPort", &g_Config.iRemoteISOPort, 0, true, false),
|
||||
ConfigSetting("LastRemoteISOServer", &g_Config.sLastRemoteISOServer, ""),
|
||||
ConfigSetting("LastRemoteISOPort", &g_Config.iLastRemoteISOPort, 0),
|
||||
ConfigSetting("RemoteISOManualConfig", &g_Config.bRemoteISOManual, false),
|
||||
ConfigSetting("RemoteISOSubdir", &g_Config.sRemoteISOSubdir, "/"),
|
||||
|
||||
#ifdef __ANDROID__
|
||||
ConfigSetting("ScreenRotation", &g_Config.iScreenRotation, 1),
|
||||
|
@ -144,6 +144,8 @@ public:
|
||||
int iRemoteISOPort;
|
||||
std::string sLastRemoteISOServer;
|
||||
int iLastRemoteISOPort;
|
||||
bool bRemoteISOManual;
|
||||
std::string sRemoteISOSubdir;
|
||||
bool bMemStickInserted;
|
||||
|
||||
int iScreenRotation; // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms.
|
||||
|
@ -198,6 +198,11 @@ static bool FindServer(std::string &resultHost, int &resultPort) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//don't scan if in manual mode
|
||||
if (g_Config.bRemoteISOManual) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start by requesting a list of recent local ips for this network.
|
||||
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT)) {
|
||||
if (http.Connect()) {
|
||||
@ -251,11 +256,23 @@ static bool LoadGameList(const std::string &host, int port, std::vector<std::str
|
||||
http::Client http;
|
||||
Buffer result;
|
||||
int code = 500;
|
||||
std::vector<std::string> responseHeaders;
|
||||
std::string subdir ="/";
|
||||
size_t offset;
|
||||
|
||||
// Start by requesting a list of recent local ips for this network.
|
||||
if (g_Config.bRemoteISOManual) {
|
||||
subdir = g_Config.sRemoteISOSubdir;
|
||||
offset=subdir.find_last_of("/");
|
||||
if (offset != subdir.length() - 1 && offset != subdir.npos) {
|
||||
//truncate everything after last /
|
||||
subdir.erase(offset + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Start by requesting the list of games from the server.
|
||||
if (http.Resolve(host.c_str(), port)) {
|
||||
if (http.Connect()) {
|
||||
code = http.GET("/", &result);
|
||||
code = http.GET(subdir.c_str(), &result,responseHeaders);
|
||||
http.Disconnect();
|
||||
}
|
||||
}
|
||||
@ -268,19 +285,53 @@ static bool LoadGameList(const std::string &host, int port, std::vector<std::str
|
||||
std::vector<std::string> items;
|
||||
result.TakeAll(&listing);
|
||||
|
||||
SplitString(listing, '\n', items);
|
||||
for (const std::string &item : items) {
|
||||
if (!endsWithNoCase(item, ".cso") && !endsWithNoCase(item, ".iso") && !endsWithNoCase(item, ".pbp")) {
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
|
||||
char temp[1024] = {};
|
||||
snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s", host.c_str(), port, item.c_str());
|
||||
games.push_back(temp);
|
||||
}
|
||||
|
||||
//save for next time
|
||||
if (!games.empty()){
|
||||
// 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) {
|
||||
//ppsspp server
|
||||
SplitString(listing, '\n', items);
|
||||
for (const std::string &item : items) {
|
||||
if (!endsWithNoCase(item, ".cso") && !endsWithNoCase(item, ".iso") && !endsWithNoCase(item, ".pbp")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char temp[1024] = {};
|
||||
snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s", host.c_str(), port, item.c_str());
|
||||
games.push_back(temp);
|
||||
}
|
||||
} else if (parseHtml) {
|
||||
//other webserver
|
||||
GetQuotedStrings(listing, items);
|
||||
for (const std::string &item : items) {
|
||||
|
||||
if (!endsWithNoCase(item, ".cso") && !endsWithNoCase(item, ".iso") && !endsWithNoCase(item, ".pbp")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char temp[1024] = {};
|
||||
snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s%s", host.c_str(), port, subdir.c_str(),item.c_str());
|
||||
games.push_back(temp);
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(FILESYS, "Unsupported Content-Type: %s", contentType.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//save for next time unless manual is true
|
||||
if (!games.empty() && !g_Config.bRemoteISOManual){
|
||||
g_Config.sLastRemoteISOServer = host;
|
||||
g_Config.iLastRemoteISOPort = port;
|
||||
}
|
||||
@ -322,8 +373,6 @@ void RemoteISOScreen::CreateViews() {
|
||||
leftColumnItems->Add(new TextView(sy->T("RemoteISODesc", "Games in your recent list will be shared"), new LinearLayoutParams(Margins(12, 5, 0, 5))));
|
||||
leftColumnItems->Add(new TextView(sy->T("RemoteISOWifi", "Note: Connect both devices to the same wifi"), new LinearLayoutParams(Margins(12, 5, 0, 5))));
|
||||
|
||||
// TODO: Could display server address for manual entry.
|
||||
|
||||
rightColumnItems->SetSpacing(0.0f);
|
||||
Choice *browseChoice = new Choice(sy->T("Browse Games"));
|
||||
rightColumnItems->Add(browseChoice)->OnClick.Handle(this, &RemoteISOScreen::HandleBrowse);
|
||||
@ -338,6 +387,8 @@ void RemoteISOScreen::CreateViews() {
|
||||
rightColumnItems->Add(new Choice(sy->T("Share Games (Server)")))->OnClick.Handle(this, &RemoteISOScreen::HandleStartServer);
|
||||
browseChoice->SetEnabled(true);
|
||||
}
|
||||
Choice *settingsChoice = new Choice(sy->T("Settings"));
|
||||
rightColumnItems->Add(settingsChoice)->OnClick.Handle(this, &RemoteISOScreen::HandleSettings);
|
||||
|
||||
rightColumnItems->Add(new Spacer(25.0));
|
||||
rightColumnItems->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
@ -383,6 +434,11 @@ UI::EventReturn RemoteISOScreen::HandleBrowse(UI::EventParams &e) {
|
||||
return EVENT_DONE;
|
||||
}
|
||||
|
||||
UI::EventReturn RemoteISOScreen::HandleSettings(UI::EventParams &e) {
|
||||
screenManager()->push(new RemoteISOSettingsScreen());
|
||||
return EVENT_DONE;
|
||||
}
|
||||
|
||||
RemoteISOConnectScreen::RemoteISOConnectScreen() : status_(ScanStatus::SCANNING), nextRetry_(0.0) {
|
||||
scanCancelled = false;
|
||||
|
||||
@ -582,3 +638,42 @@ void RemoteISOBrowseScreen::CreateViews() {
|
||||
|
||||
upgradeBar_ = 0;
|
||||
}
|
||||
|
||||
void RemoteISOSettingsScreen::CreateViews() {
|
||||
I18NCategory *di = GetI18NCategory("Dialog");
|
||||
I18NCategory *n = GetI18NCategory("Networking");
|
||||
I18NCategory *ms = GetI18NCategory("MainSettings");
|
||||
|
||||
ViewGroup *remoteisoSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
remoteisoSettingsScroll->SetTag("GameSettingsNetworking");
|
||||
LinearLayout *remoteisoSettings = new LinearLayout(ORIENT_VERTICAL);
|
||||
remoteisoSettings->SetSpacing(0);
|
||||
remoteisoSettingsScroll->Add(remoteisoSettings);
|
||||
|
||||
remoteisoSettings->Add(new ItemHeader(ms->T("Remote Disc Streaming")));
|
||||
remoteisoSettings->Add(new CheckBox(&g_Config.bRemoteISOManual, n->T("Manual Mode Client", "Manual Mode Client")));
|
||||
PopupTextInputChoice *remoteServer = remoteisoSettings->Add(new PopupTextInputChoice(&g_Config.sLastRemoteISOServer, n->T("Remote Server"), "", 255, screenManager()));
|
||||
remoteServer->SetEnabledPtr(&g_Config.bRemoteISOManual);
|
||||
PopupSliderChoice *remotePort = remoteisoSettings->Add(new PopupSliderChoice(&g_Config.iLastRemoteISOPort, 0, 65535, n->T("Remote Port", "Remote Port"), 100, screenManager()));
|
||||
remotePort->SetEnabledPtr(&g_Config.bRemoteISOManual);
|
||||
PopupTextInputChoice *remoteSubdir = remoteisoSettings->Add(new PopupTextInputChoice(&g_Config.sRemoteISOSubdir, n->T("Remote Subdirectory"), "", 255, screenManager()));
|
||||
remoteSubdir->SetEnabledPtr(&g_Config.bRemoteISOManual);
|
||||
remoteSubdir->OnChange.Handle(this, &RemoteISOSettingsScreen::OnChangeRemoteISOSubdir);
|
||||
remoteisoSettings->Add(new PopupSliderChoice(&g_Config.iRemoteISOPort, 0, 65535, n->T("Local Server Port", "Local Server Port"), 100, screenManager()));
|
||||
remoteisoSettings->Add(new Spacer(25.0));
|
||||
remoteisoSettings->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
|
||||
root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
root_->Add(remoteisoSettings);
|
||||
}
|
||||
|
||||
UI::EventReturn RemoteISOSettingsScreen::OnChangeRemoteISOSubdir(UI::EventParams &e) {
|
||||
//Conform to HTTP standards
|
||||
ReplaceAll(g_Config.sRemoteISOSubdir, " ", "%20");
|
||||
ReplaceAll(g_Config.sRemoteISOSubdir, "\\", "/");
|
||||
//Make sure it begins with /
|
||||
if (g_Config.sRemoteISOSubdir[0] != '/')
|
||||
g_Config.sRemoteISOSubdir = "/" + g_Config.sRemoteISOSubdir;
|
||||
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ protected:
|
||||
UI::EventReturn HandleStartServer(UI::EventParams &e);
|
||||
UI::EventReturn HandleStopServer(UI::EventParams &e);
|
||||
UI::EventReturn HandleBrowse(UI::EventParams &e);
|
||||
UI::EventReturn HandleSettings(UI::EventParams &e);
|
||||
|
||||
bool serverRunning_;
|
||||
bool serverStopping_;
|
||||
@ -83,3 +84,13 @@ protected:
|
||||
|
||||
std::vector<std::string> games_;
|
||||
};
|
||||
|
||||
class RemoteISOSettingsScreen : public UIScreenWithBackground {
|
||||
public:
|
||||
|
||||
protected:
|
||||
void CreateViews() override;
|
||||
|
||||
UI::EventReturn OnChangeRemoteISOSubdir(UI::EventParams &e);
|
||||
|
||||
};
|
@ -279,6 +279,26 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
|
||||
}
|
||||
}
|
||||
|
||||
void GetQuotedStrings(const std::string& str, std::vector<std::string>& output)
|
||||
{
|
||||
size_t next = 0;
|
||||
bool even = 0;
|
||||
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
|
||||
if (str[pos] == '\"' || str[pos] == '\'') {
|
||||
if (even) {
|
||||
//quoted text
|
||||
output.push_back(str.substr(next, pos - next));
|
||||
even = 0;
|
||||
} else {
|
||||
//non quoted text
|
||||
even = 1;
|
||||
}
|
||||
// Skip the delimiter itself.
|
||||
next = pos + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest)
|
||||
{
|
||||
size_t pos = 0;
|
||||
|
@ -113,6 +113,8 @@ static bool TryParse(const std::string &str, N *const output)
|
||||
}
|
||||
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output);
|
||||
|
||||
void GetQuotedStrings(const std::string& str, std::vector<std::string>& output);
|
||||
|
||||
std::string ReplaceAll(std::string input, const std::string& src, const std::string& dest);
|
||||
|
||||
// Compare two strings, ignore the difference between the ignorestr1 and the ignorestr2 in str1 and str2.
|
||||
|
@ -180,7 +180,7 @@ void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *prog
|
||||
}
|
||||
}
|
||||
|
||||
int Client::GET(const char *resource, Buffer *output, float *progress, bool *cancelled) {
|
||||
int Client::GET(const char *resource, Buffer *output, std::vector<std::string> &responseHeaders, float *progress, bool *cancelled) {
|
||||
const char *otherHeaders =
|
||||
"Accept: */*\r\n"
|
||||
"Accept-Encoding: gzip\r\n";
|
||||
@ -190,7 +190,6 @@ int Client::GET(const char *resource, Buffer *output, float *progress, bool *can
|
||||
}
|
||||
|
||||
Buffer readbuf;
|
||||
std::vector<std::string> responseHeaders;
|
||||
int code = ReadResponseHeaders(&readbuf, responseHeaders, progress);
|
||||
if (code < 0) {
|
||||
return code;
|
||||
@ -203,6 +202,12 @@ int Client::GET(const char *resource, Buffer *output, float *progress, bool *can
|
||||
return code;
|
||||
}
|
||||
|
||||
int Client::GET(const char *resource, Buffer *output, float *progress, bool *cancelled) {
|
||||
std::vector<std::string> responseHeaders;
|
||||
int code = GET(resource, output, responseHeaders, progress, cancelled);
|
||||
return code;
|
||||
}
|
||||
|
||||
int Client::POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, float *progress) {
|
||||
char otherHeaders[2048];
|
||||
if (mime.empty()) {
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
|
||||
// Return value is the HTTP return code. 200 means OK. < 0 means some local error.
|
||||
int GET(const char *resource, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
int GET(const char *resource, Buffer *output, std::vector<std::string> &responseHeaders, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
|
||||
// Return value is the HTTP return code.
|
||||
int POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, float *progress = nullptr);
|
||||
|
@ -86,7 +86,7 @@ void Request::WriteHttpResponseHeader(int status, int64_t size, const char *mime
|
||||
|
||||
net::OutputSink *buffer = Out();
|
||||
buffer->Printf("HTTP/1.0 %03d %s\r\n", status, statusStr);
|
||||
buffer->Push("Server: SuperDuperServer v0.1\r\n");
|
||||
buffer->Push("Server: PPSSPPServer v0.1\r\n");
|
||||
buffer->Printf("Content-Type: %s\r\n", mimeType ? mimeType : DEFAULT_MIME_TYPE);
|
||||
buffer->Push("Connection: close\r\n");
|
||||
if (size >= 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user