Merge pull request #9370 from fireballwayne/remoteiso

Remoteiso
This commit is contained in:
Henrik Rydgård 2017-03-12 12:39:45 +01:00 committed by GitHub
commit cfb93a707c
9 changed files with 155 additions and 17 deletions

View File

@ -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),

View File

@ -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.

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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;

View File

@ -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.

View File

@ -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()) {

View File

@ -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);

View File

@ -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) {