diff --git a/Common/System/System.h b/Common/System/System.h index 081227589f..bd68a959f0 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -49,6 +49,7 @@ enum class LaunchUrlType { EMAIL_ADDRESS, LOCAL_FILE, LOCAL_FOLDER, // Shows the folder. Not supported on all systems of course. + AUTO, }; void System_Vibrate(int length_ms); diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index 09a4e7438c..ed2efab736 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -24,6 +24,9 @@ PopupScreen::PopupScreen(std::string_view title, std::string_view button1, std:: if (button1 == di->T("Delete") || button1 == di->T("Move to trash")) { button1Image_ = ImageID("I_TRASHCAN"); } + if (button1 == di->T("Back")) { + button1Image_ = ImageID("I_NAVIGATE_BACK"); + } alpha_ = 0.0f; // inherited ignoreInsets_ = true; // for layout purposes. diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index b2f3dac943..c05fc1c794 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -1085,6 +1085,10 @@ void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz } } +TextView *TextView::SetWordWrap() { + textAlign_ |= FLAG_WRAP_TEXT; return this; +} + void TextView::Draw(UIContext &dc) { uint32_t textColor = hasTextColor_ ? textColor_ : (popupStyle_ ? dc.GetTheme().popupStyle.fgColor : dc.GetTheme().infoStyle.fgColor); if (!(textColor & 0xFF000000)) diff --git a/Common/UI/View.h b/Common/UI/View.h index 8d41e81462..5064e02c57 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -1052,7 +1052,7 @@ public: std::string DescribeText() const override { return GetText(); } void SetSmall(bool small) { textSize_ = TextSize::Small; } void SetBig(bool big) { textSize_ = TextSize::Big; } - void SetTextSize(TextSize size) { textSize_ = size; } + TextView *SetTextSize(TextSize size) { textSize_ = size; return this; } void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; } void SetShadow(bool shadow) { shadow_ = shadow; } void SetFocusable(bool focusable) { focusable_ = focusable; } @@ -1060,6 +1060,7 @@ public: void SetBullet(bool bullet) { bullet_ = bullet; } void SetPadding(Margins padding) { pad_ = padding; } void SetAlign(int align) { textAlign_ = align; } + TextView *SetWordWrap(); bool CanBeFocused() const override { return focusable_; } diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 7482fa04fe..d1e44b2c7d 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -143,6 +143,7 @@ bool ParseServerListEntriesJSON(std::string_view json) { entry.discord = server.getStringOr("discord", ""); entry.host = server.getStringOr("host", ""); entry.web = server.getStringOr("web", ""); + entry.ip = server.getStringOr("ip", ""); entry.location = server.getStringOr("location", ""); entry.description = server.getStringOr("description", ""); entry.mode = equals(server.getStringOr("data_mode", ""), "AemuPostoffice") ? AdhocDataMode::AemuPostoffice : AdhocDataMode::P2P; diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index e7d0493798..17b093bdbd 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -107,6 +107,7 @@ struct AdhocServerListEntry { std::string name; std::string host; std::string web; + std::string ip; // could be used as a fallback if DNS resolution fails. Currently unused. std::string discord; std::string location; std::string description; diff --git a/README.md b/README.md index d12c217802..9e8293cc3a 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ What's new in 1.20 - Soundtrack is working again in Tony Hawk's Project 8 ([#20775]) - Fix crash in StormBasic homebrew apps ([#20715]) - Built-in songs in Beats work again ([#20662]) - - Fix adhoc in Syphon Filter games ([#20642]) + - Fix adhoc in Syphon Filter games ([#20642]) - Fix strange cutscene speedups in Power Stone 2 (Power Stone Collection) ([#2124]) - Work around game bug to help more custom songs play again in GTA LCS ([#20692]) diff --git a/UI/AdhocServerScreen.cpp b/UI/AdhocServerScreen.cpp index 0a6f5111ce..7d940f4364 100644 --- a/UI/AdhocServerScreen.cpp +++ b/UI/AdhocServerScreen.cpp @@ -5,6 +5,153 @@ #include "Common/StringUtils.h" #include "Core/HLE/sceNetAdhoc.h" +static UI::View *CreateLinkButton(std::string url) { + using namespace UI; + + // steal strings from all over the place + auto cr = GetI18NCategory(I18NCat::PSPCREDITS); + auto st = GetI18NCategory(I18NCat::STORE); + + ImageID icon = ImageID("I_LINK_OUT_QUESTION"); + std::string title; + + if (startsWith(url, "https://discord")) { + icon = ImageID("I_LOGO_DISCORD"); + title = cr->T("Discord"); + } else { + icon = ImageID("I_WEB_BROWSER"); + title = st->T("Website"); + } + + Choice *choice = new Choice(title, icon, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Margins(12, 0))); + choice->OnClick.Add([url](UI::EventParams &) { + System_LaunchUrl(LaunchUrlType::BROWSER_URL, url); + }); + return choice; +} + +// Later, this might also show games-in-progress. +// For now, it's just a simple metadata viewer. +class AdhocServerInfoScreen : public UI::PopupScreen { +public: + AdhocServerInfoScreen(const AdhocServerListEntry &entry) + : PopupScreen(entry.name, T(I18NCat::DIALOG, "Back")), entry_(entry) { + } // PopupScreen will translate Back on its own + + const char *tag() const override { return "AdhocServerInfo"; } + +protected: + bool FillVertical() const override { return false; } + UI::Size PopupWidth() const override { return 500; } + bool ShowButtons() const override { return true; } + + void CreatePopupContents(UI::ViewGroup *parent) override { + using namespace UI; + auto pa = GetI18NCategory(I18NCat::PAUSE); + auto di = GetI18NCategory(I18NCat::DIALOG); + auto ni = GetI18NCategory(I18NCat::NETWORKING); + + ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); + LinearLayout *content = new LinearLayout(ORIENT_VERTICAL); + Margins contentMargins(10, 0); + + content->Add(new InfoItem(entry_.host, "")); + if (!entry_.ip.empty()) { + content->Add(new InfoItem(entry_.ip, "")); + } + content->Add(new InfoItem(entry_.location, "")); + content->Add(new InfoItem(ni->T("Relay server mode"), entry_.mode == AdhocDataMode::AemuPostoffice ? di->T("Yes") : di->T("No"))); + TextView *desc = content->Add(new TextView(entry_.description, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(10)))); + desc->SetTextSize(TextSize::Small); + desc->SetWordWrap(); + if (!entry_.web.empty()) { + content->Add(CreateLinkButton(entry_.web)); + } + if (!entry_.discord.empty()) { + content->Add(CreateLinkButton(entry_.discord)); + } + scroll->Add(content); + parent->Add(scroll); + } +private: + AdhocServerListEntry entry_; +}; + +class AdhocServerRow : public UI::LinearLayout { +public: + AdhocServerRow(std::string *value, const AdhocServerListEntry &entry, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr); + + void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { + w = 500; h = 90; + } + + void Draw(UIContext &dc) override { + dc.FillRect(dc.GetTheme().itemStyle.background, bounds_); + if (*value_ == entry_.host) { + // TODO: Make this highlight themable + dc.FillRect(UI::Drawable(0x30FFFFFF), GetBounds()); + } + LinearLayout::Draw(dc); + } + + bool Touch(const TouchInput &input) override { + using namespace UI; + if (UI::LinearLayout::Touch(input)) { + return true; + } + if (input.flags & TouchInputFlags::DOWN) { + if (bounds_.Contains(input.x, input.y)) { + dragging_ = true; + return true; + } + } + if (dragging_ && (input.flags & TouchInputFlags::UP)) { + dragging_ = false; + if (!(input.flags & TouchInputFlags::CANCEL) && bounds_.Contains(input.x, input.y)) { + EventParams e; + e.v = this; + OnSelected.Trigger(e); + return true; + } + } + return false; + } + + UI::Event OnSelected; + +private: + bool dragging_ = false; + std::string *value_; + AdhocServerListEntry entry_; +}; + +AdhocServerRow::AdhocServerRow(std::string *value, const AdhocServerListEntry &entry, ScreenManager *screenManager, UI::LayoutParams *layoutParams) + : UI::LinearLayout(ORIENT_HORIZONTAL, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT, UI::Margins(5.0f, 0.0f))), value_(value), entry_(entry) { + using namespace UI; + + int number = 0; + + LinearLayout *lines = Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(Margins(5, 5)))); + lines->SetSpacing(0.0f); + lines->Add(new TextView(entry.name)); + lines->Add(new TextView(entry.host))->SetTextSize(TextSize::Small)->SetWordWrap(); + + Add(new Spacer(0.0f, new LinearLayoutParams(1.0f, Margins(0.0f, 5.0f)))); + + if (entry.mode == AdhocDataMode::AemuPostoffice) { + TextView *relay = Add(new TextView("Relay", new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity::G_VCENTER, Margins(10.0)))); + } + + if (!entry.description.empty()) { + Choice *infoButton = Add(new Choice(ImageID("I_INFO"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity::G_VCENTER, Margins(0, 0, 10, 0)))); + AdhocServerListEntry copy = entry; + infoButton->OnClick.Add([this, copy, screenManager](UI::EventParams &e) { + e.v = this; + screenManager->push(new AdhocServerInfoScreen(copy)); + }); + } +} + AdhocServerScreen::AdhocServerScreen(std::string *value, std::string_view title) : UI::PopupScreen(title, T(I18NCat::DIALOG, "OK"), T(I18NCat::DIALOG, "Cancel")), value_(value) { resolver_ = std::thread([](AdhocServerScreen *thiz) { @@ -37,7 +184,7 @@ void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) { auto listItems = AdhocGetServerList(); - PopupTextInputChoice *textInputChoice = parent->Add(new PopupTextInputChoice(GetRequesterToken(), &editValue_, n->T("Hostname"), "", 256, screenManager())); + PopupTextInputChoice *textInputChoice = parent->Add(new PopupTextInputChoice(GetRequesterToken(), &editValue_, n->T("Hostname"), "", 450, screenManager())); parent->Add(new Spacer(5.0f)); // editValue_ has the currently selected server. On closing the dialog, we copy that to settings. @@ -79,7 +226,7 @@ void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) { for (const auto &host : g_Config.vCustomAdhocServerListWithRelay) { // If the host is already in the public list, skip it. We don't want duplicates. - if (hostInEntries(host)) { + if (hostInEntries(host) || host.empty()) { continue; } AdhocServerListEntry entry; @@ -91,7 +238,7 @@ void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) { for (const auto &host : g_Config.vCustomAdhocServerList) { // If the host is already in the public list, skip it. We don't want duplicates. - if (hostInEntries(host)) { + if (hostInEntries(host) || host.empty()) { continue; } AdhocServerListEntry entry; @@ -106,9 +253,9 @@ void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) { innerView->SetSpacing(5.0f); auto AddButtonFromEntry = [this](UI::ViewGroup *parent, const AdhocServerListEntry &entry) { - // Filter out IP prefixed with "127." and "169.254." also "0." since they can be redundant or unusable - auto button = parent->Add(new Button(entry.host, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); - button->OnClick.Add([this](UI::EventParams &e) { + AdhocServerRow *row = new AdhocServerRow(&editValue_, entry, screenManager()); + parent->Add(row); + row->OnSelected.Add([this](UI::EventParams &e) { std::string value = e.v->Tag(); if (!value.empty()) { editValue_ = value; @@ -116,7 +263,7 @@ void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) { System_CopyStringToClipboard(value); } }); - button->SetTag(entry.host); + row->SetTag(entry.host); }; if (!customEntries.empty()) { diff --git a/UI/AdhocServerScreen.h b/UI/AdhocServerScreen.h index 9ac29dfba9..06ad80a145 100644 --- a/UI/AdhocServerScreen.h +++ b/UI/AdhocServerScreen.h @@ -22,6 +22,7 @@ public: protected: void OnCompleted(DialogResult result) override; bool CanComplete(DialogResult result) override; + virtual UI::Size PopupWidth() const { return 650; } void sendMessage(UIMessage message, const char *value) override; diff --git a/assets/compat.ini b/assets/compat.ini index 8c5c3887b3..5f98444cdb 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1424,6 +1424,12 @@ ULES00928 = true ULUS10312 = true ULKS46154 = true +# Gripshift (see #21367) +ULUS10040 = true +ULES00177 = true +ULKS46040 = true +ULJM05089 = true + [FramebufferAllowLargeVerticalOffset] # Tokimeki Memorial 4 (see #6379) NPJH50127 = true @@ -1438,7 +1444,8 @@ ULJM05224 = true NPJH50214 = true [AtracLoopHack] -#Atrac looped incorrectly see #7601 #13773 #11586 #10139 #12083 +# Atrac looped incorrectly see #7601 #13773 #11586 #10139 #12083 +# NOTE: This is no longer relevant after the Atrac3 rewrite, except for old savestates. #Coded Arms: Contagion ULUS10184 = true