From 9e8341cf88d466bd3d5a095578f007c7cb34b61f Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 14 Feb 2019 09:53:48 +0100 Subject: [PATCH 01/24] Enable BlockTransferAllowCreateFB for Ys Seven as discussed in #11669 --- assets/compat.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index c8991bdc2c..9b28f448e7 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -346,6 +346,12 @@ NPJH50686 = true # Digimon Adventures (JP, and English patches...) ULJS00078 = true # MotoGP ULUS10153 = true # MotoGP UCES00373 = true # MotoGP +ULUS10551 = true # Ys Seven +ULJM05475 = true # Ys Seven JP +ULUS10549 = true # Ys Seven +NPEH00065 = true # Ys Seven +NPJH50350 = true # Ys Seven +ULJM08041 = true # Ys Seven [YugiohSaveFix] # The cause of Yu-gi-oh series 's bad save (cannot save) are load "save status" and use cwcheat, From af58577aff9cf67a39360bf1ddfd32043db2a5ae Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Feb 2019 06:54:56 -0800 Subject: [PATCH 02/24] GPU: Better typesafety for shader bits. See 128c0ad. --- GPU/Common/ShaderId.cpp | 12 +++--- GPU/Common/ShaderId.h | 76 ++++++++++++++++++++++++++-------- GPU/GLES/ShaderManagerGLES.cpp | 2 +- 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index f3a8e09228..21b4519af9 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -11,7 +11,7 @@ #include "GPU/Common/ShaderId.h" #include "GPU/Common/VertexDecoderCommon.h" -std::string VertexShaderDesc(const ShaderID &id) { +std::string VertexShaderDesc(const VShaderID &id) { std::stringstream desc; desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]); if (id.Bit(VS_BIT_IS_THROUGH)) desc << "THR "; @@ -60,7 +60,7 @@ std::string VertexShaderDesc(const ShaderID &id) { return desc.str(); } -void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform) { +void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform) { bool isModeThrough = (vertType & GE_VTYPE_THROUGH) != 0; bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear(); bool doTextureTransform = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; @@ -81,7 +81,7 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform) bool enableFog = gstate.isFogEnabled() && !isModeThrough && !gstate.isModeClear(); bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !isModeThrough; - ShaderID id; + VShaderID id; id.SetBit(VS_BIT_LMODE, lmode); id.SetBit(VS_BIT_IS_THROUGH, isModeThrough); id.SetBit(VS_BIT_ENABLE_FOG, enableFog); @@ -160,7 +160,7 @@ static bool MatrixNeedsProjection(const float m[12]) { return m[2] != 0.0f || m[5] != 0.0f || m[8] != 0.0f || m[11] != 1.0f; } -std::string FragmentShaderDesc(const ShaderID &id) { +std::string FragmentShaderDesc(const FShaderID &id) { std::stringstream desc; desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]); if (id.Bit(FS_BIT_CLEARMODE)) desc << "Clear "; @@ -226,8 +226,8 @@ std::string FragmentShaderDesc(const ShaderID &id) { // Here we must take all the bits of the gstate that determine what the fragment shader will // look like, and concatenate them together into an ID. -void ComputeFragmentShaderID(ShaderID *id_out, const Draw::Bugs &bugs) { - ShaderID id; +void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) { + FShaderID id; if (gstate.isModeClear()) { // We only need one clear shader, so let's ignore the rest of the bits. id.SetBit(FS_BIT_CLEARMODE); diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index ec57b0273f..e614d8f112 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -8,7 +8,7 @@ // TODO: There will be additional bits, indicating that groups of these will be // sent to the shader and processed there. This will cut down the number of shaders ("ubershader approach") // This is probably only really worth doing for lighting and bones. -enum { +enum VShaderBit : uint8_t { VS_BIT_LMODE = 0, VS_BIT_IS_THROUGH = 1, VS_BIT_ENABLE_FOG = 2, @@ -55,9 +55,12 @@ enum { // No more free }; +static inline VShaderBit operator +(VShaderBit bit, int i) { + return VShaderBit((int)bit + i); +} // Local -enum { +enum FShaderBit : uint8_t { FS_BIT_CLEARMODE = 0, FS_BIT_DO_TEXTURE = 1, FS_BIT_TEXFUNC = 2, // 3 bits @@ -91,6 +94,10 @@ enum { // 50+ are free. }; +static inline FShaderBit operator +(FShaderBit bit, int i) { + return FShaderBit((int)bit + i); +} + struct ShaderID { ShaderID() { clear(); @@ -126,6 +133,20 @@ struct ShaderID { bool operator != (const ShaderID &other) const { return !(*this == other); } + + uint32_t Word(int word) const { + return d[word]; + } + + void ToString(std::string *dest) const { + dest->resize(sizeof(d)); + memcpy(&(*dest)[0], d, sizeof(d)); + } + void FromString(std::string src) { + memcpy(d, &(src)[0], sizeof(d)); + } + +protected: bool Bit(int bit) const { return (d[bit >> 5] >> (bit & 31)) & 1; } @@ -134,9 +155,6 @@ struct ShaderID { const int mask = (1 << count) - 1; return (d[bit >> 5] >> (bit & 31)) & mask; } - uint32_t Word(int word) const { - return d[word]; - } void SetBit(int bit, bool value = true) { if (value) { d[bit >> 5] |= 1 << (bit & 31); @@ -148,14 +166,6 @@ struct ShaderID { d[bit >> 5] |= (value & mask) << (bit & 31); } } - - void ToString(std::string *dest) const { - dest->resize(sizeof(d)); - memcpy(&(*dest)[0], d, sizeof(d)); - } - void FromString(std::string src) { - memcpy(d, &(src)[0], sizeof(d)); - } }; struct VShaderID : ShaderID { @@ -165,6 +175,22 @@ struct VShaderID : ShaderID { explicit VShaderID(ShaderID &src) { memcpy(d, src.d, sizeof(d)); } + + bool Bit(VShaderBit bit) const { + return ShaderID::Bit((int)bit); + } + + int Bits(VShaderBit bit, int count) const { + return ShaderID::Bits((int)bit, count); + } + + void SetBit(VShaderBit bit, bool value = true) { + ShaderID::SetBit((int)bit, value); + } + + void SetBits(VShaderBit bit, int count, int value) { + ShaderID::SetBits((int)bit, count, value); + } }; struct FShaderID : ShaderID { @@ -174,6 +200,22 @@ struct FShaderID : ShaderID { explicit FShaderID(ShaderID &src) { memcpy(d, src.d, sizeof(d)); } + + bool Bit(FShaderBit bit) const { + return ShaderID::Bit((int)bit); + } + + int Bits(FShaderBit bit, int count) const { + return ShaderID::Bits((int)bit, count); + } + + void SetBit(FShaderBit bit, bool value = true) { + ShaderID::SetBit((int)bit, value); + } + + void SetBits(FShaderBit bit, int count, int value) { + ShaderID::SetBits((int)bit, count, value); + } }; namespace Draw { @@ -181,10 +223,10 @@ class Bugs; } -void ComputeVertexShaderID(ShaderID *id, uint32_t vertexType, bool useHWTransform); +void ComputeVertexShaderID(VShaderID *id, uint32_t vertexType, bool useHWTransform); // Generates a compact string that describes the shader. Useful in a list to get an overview // of the current flora of shaders. -std::string VertexShaderDesc(const ShaderID &id); +std::string VertexShaderDesc(const VShaderID &id); -void ComputeFragmentShaderID(ShaderID *id, const Draw::Bugs &bugs); -std::string FragmentShaderDesc(const ShaderID &id); +void ComputeFragmentShaderID(FShaderID *id, const Draw::Bugs &bugs); +std::string FragmentShaderDesc(const FShaderID &id); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 585a6cf459..ffafcaa362 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -762,7 +762,7 @@ std::string Shader::GetShaderString(DebugShaderStringType type, ShaderID id) con case SHADER_STRING_SOURCE_CODE: return source_; case SHADER_STRING_SHORT_DESC: - return isFragment_ ? FragmentShaderDesc(id) : VertexShaderDesc(id); + return isFragment_ ? FragmentShaderDesc(FShaderID(id)) : VertexShaderDesc(VShaderID(id)); default: return "N/A"; } From bc84571f0f98de4e146d055f55d3f9b706ec3f5d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Feb 2019 10:14:54 -0800 Subject: [PATCH 03/24] Windows: Detect Vulkan in separate process. This should avoid crashes with broken AMD drivers that can't initialize Vulkan properly. Instead, Vulkan will show up as unavailable. Disabled during debug to allow for debugging. --- Windows/main.cpp | 77 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/Windows/main.cpp b/Windows/main.cpp index 92e0310ea2..c3d3dfed72 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -21,6 +21,7 @@ #include "Common/CommonWindows.h" #include "Common/OSVersion.h" +#include "Common/Vulkan/VulkanLoader.h" #include #include @@ -350,6 +351,39 @@ static std::string GetDefaultLangRegion() { } } +static const int EXIT_CODE_VULKAN_WORKS = 42; + +static bool DetectVulkanInExternalProcess() { + wchar_t moduleFilename[MAX_PATH]; + wchar_t workingDirectory[MAX_PATH]; + GetCurrentDirectoryW(MAX_PATH, workingDirectory); + const wchar_t *cmdline = L"--vulkan-available-check"; + GetModuleFileName(GetModuleHandle(NULL), moduleFilename, MAX_PATH); + + SHELLEXECUTEINFO info{ sizeof(SHELLEXECUTEINFO) }; + info.fMask = SEE_MASK_NOCLOSEPROCESS; + info.lpFile = moduleFilename; + info.lpParameters = cmdline; + info.lpDirectory = workingDirectory; + info.nShow = SW_HIDE; + if (ShellExecuteEx(&info) != TRUE) { + return false; + } + if (info.hProcess == nullptr) { + return false; + } + + DWORD result = WaitForSingleObject(info.hProcess, 10000); + DWORD exitCode = 0; + if (result == WAIT_FAILED || GetExitCodeProcess(info.hProcess, &exitCode) == 0) { + CloseHandle(info.hProcess); + return false; + } + CloseHandle(info.hProcess); + + return exitCode == EXIT_CODE_VULKAN_WORKS; +} + std::vector GetWideCmdLine() { wchar_t **wargv; int wargc = -1; @@ -361,9 +395,7 @@ std::vector GetWideCmdLine() { return wideArgs; } -int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { - setCurrentThreadName("Main"); - +static void WinMainInit() { CoInitializeEx(NULL, COINIT_MULTITHREADED); net::Init(); // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times. @@ -384,6 +416,21 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // FMA3 support in the 2013 CRT is broken on Vista and Windows 7 RTM (fixed in SP1). Just disable it. _set_FMA3_enable(0); #endif +} + +static void WinMainCleanup() { + if (g_Config.bRestartRequired) { + W32Util::ExitAndRestart(); + } + + net::Shutdown(); + CoUninitialize(); +} + +int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { + setCurrentThreadName("Main"); + + WinMainInit(); #ifndef _DEBUG bool showLog = false; @@ -486,12 +533,28 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin g_Config.bSoftwareRendering = true; } } + + // This should only be called by DetectVulkanInExternalProcess(). + if (wideArgs[i] == L"--vulkan-available-check") { + // Just call it, this way it will crash here if it doesn't work. + // (this is an external process.) + bool result = VulkanMayBeAvailable(); + + LogManager::Shutdown(); + WinMainCleanup(); + return result ? EXIT_CODE_VULKAN_WORKS : EXIT_FAILURE; + } } } #ifdef _DEBUG g_Config.bEnableLogging = true; #endif +#ifndef _DEBUG + // See #11719 - too many Vulkan drivers crash on basic init. + VulkanSetAvailable(DetectVulkanInExternalProcess()); +#endif + if (iCmdShow == SW_MAXIMIZE) { // Consider this to mean --fullscreen. g_Config.bFullScreen = true; @@ -595,13 +658,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin timeEndPeriod(1); LogManager::Shutdown(); - - if (g_Config.bRestartRequired) { - W32Util::ExitAndRestart(); - } - - net::Shutdown(); - CoUninitialize(); + WinMainCleanup(); return 0; } From 9a5fbe5ea56ad7203c25b71253b4a14703ae8176 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Feb 2019 14:52:01 -0800 Subject: [PATCH 04/24] Savedata: Truncate detail if it's too long. We could add scrolling later, but this makes it more usable without ugly text overlap, at least. Fixes #11711. --- Core/Dialog/PSPMsgDialog.cpp | 4 +- Core/Dialog/PSPNetconfDialog.cpp | 2 +- Core/Dialog/PSPSaveDialog.cpp | 8 ++-- Core/Util/PPGeDraw.cpp | 77 ++++++++++++++++++++++++++------ Core/Util/PPGeDraw.h | 4 +- 5 files changed, 73 insertions(+), 22 deletions(-) diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index b50dee2a9f..302113b3b5 100755 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -207,8 +207,8 @@ void PSPMsgDialog::DisplayMessage(std::string text, bool hasYesNo, bool hasOK) ey = y2 + 25.0f; } - PPGeDrawTextWrapped(text.c_str(), 241.0f, y+2, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000)); - PPGeDrawTextWrapped(text.c_str(), 240.0f, y, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawTextWrapped(text.c_str(), 241.0f, y+2, WRAP_WIDTH, 0, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000)); + PPGeDrawTextWrapped(text.c_str(), 240.0f, y, WRAP_WIDTH, 0, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF)); sy = 125.0f - h2; PPGeDrawRect(40.0f, sy, 440.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawRect(40.0f, ey, 440.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF)); diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index d9f6ff1c54..25c9d42baf 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -81,7 +81,7 @@ int PSPNetconfDialog::Update(int animSpeed) { StartDraw(); DrawBanner(); PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363)); - PPGeDrawTextWrapped(err->T("PPSSPPDoesNotSupportInternet", "PPSSPP currently does not support connecting to the Internet for DLC, PSN, or game updates."), 241, 132, WRAP_WIDTH, PPGE_ALIGN_CENTER, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawTextWrapped(err->T("PPSSPPDoesNotSupportInternet", "PPSSPP currently does not support connecting to the Internet for DLC, PSN, or game updates."), 241, 132, WRAP_WIDTH, 0, PPGE_ALIGN_CENTER, 0.5f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawImage(confirmBtnImage, 195, 250, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); PPGeDrawText(di->T("OK"), 225, 252, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index 09b32dcc01..c06a436152 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -458,8 +458,8 @@ void PSPSaveDialog::DisplaySaveDataInfo1() PPGeDrawText(timeTxt.c_str(), 180, 137, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawText(saveTitleTxt.c_str(), 176, 162, PPGE_ALIGN_LEFT, 0.55f, CalcFadedColor(0x80000000)); PPGeDrawText(saveTitleTxt.c_str(), 175, 159, PPGE_ALIGN_LEFT, 0.55f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText(saveDetailTxt.c_str(), 176, 183, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0x80000000)); - PPGeDrawText(saveDetailTxt.c_str(), 175, 181, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawTextWrapped(saveDetailTxt.c_str(), 176, 183, 480 - 175, 250 - 183, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0x80000000)); + PPGeDrawTextWrapped(saveDetailTxt.c_str(), 175, 181, 480 - 175, 250 - 181, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); } } @@ -573,8 +573,8 @@ void PSPSaveDialog::DisplayMessage(std::string text, bool hasYesNo) yesnoChoice = 0; } } - PPGeDrawTextWrapped(text.c_str(), 335.0f, y+2, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000)); - PPGeDrawTextWrapped(text.c_str(), 334.0f, y, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawTextWrapped(text.c_str(), 335.0f, y+2, WRAP_WIDTH, 0, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000)); + PPGeDrawTextWrapped(text.c_str(), 334.0f, y, WRAP_WIDTH, 0, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF)); float sy = 122.0f - h2, ey = 150.0f + h2; PPGeDrawRect(202.0f, sy, 466.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawRect(202.0f, ey, 466.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF)); diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index cb091c837a..442ce268af 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -17,6 +17,7 @@ #include +#include "base/stringutil.h" #include "image/zim_load.h" #include "image/png_load.h" #include "util/text/utf8.h" @@ -377,7 +378,7 @@ static const AtlasChar *PPGeGetChar(const AtlasFont &atlasfont, unsigned int cva // Break a single text string into mutiple lines. static AtlasTextMetrics BreakLines(const char *text, const AtlasFont &atlasfont, float x, float y, - int align, float scale, int wrapType, float wrapWidth, bool dryRun) + int align, float scale, float lineHeightScale, int wrapType, float wrapWidth, bool dryRun) { y += atlasfont.ascend * scale; float sx = x, sy = y; @@ -399,7 +400,7 @@ static AtlasTextMetrics BreakLines(const char *text, const AtlasFont &atlasfont, int numLines = 1; float maxw = 0; - float lineHeight = atlasfont.height * scale; + float lineHeight = atlasfont.height * lineHeightScale; for (UTF8 utf(text); !utf.end(); ) { float lineWidth = 0; @@ -617,16 +618,16 @@ void PPGeMeasureText(float *w, float *h, int *n, const char *text, float scale, int WrapType, int wrapWidth) { const AtlasFont &atlasfont = *ppge_atlas.fonts[0]; - AtlasTextMetrics metrics = BreakLines(text, atlasfont, 0, 0, 0, scale, WrapType, wrapWidth, true); + AtlasTextMetrics metrics = BreakLines(text, atlasfont, 0, 0, 0, scale, scale, WrapType, wrapWidth, true); if (w) *w = metrics.maxWidth; if (h) *h = metrics.lineHeight; if (n) *n = metrics.numLines; } -void PPGePrepareText(const char *text, float x, float y, int align, float scale, int WrapType, int wrapWidth) +void PPGePrepareText(const char *text, float x, float y, int align, float scale, float lineHeightScale, int WrapType, int wrapWidth) { const AtlasFont &atlasfont = *ppge_atlas.fonts[0]; - char_lines_metrics = BreakLines(text, atlasfont, x, y, align, scale, WrapType, wrapWidth, false); + char_lines_metrics = BreakLines(text, atlasfont, x, y, align, scale, lineHeightScale, WrapType, wrapWidth, false); } void PPGeMeasureCurrentText(float *x, float *y, float *w, float *h, int *n) @@ -638,6 +639,13 @@ void PPGeMeasureCurrentText(float *x, float *y, float *w, float *h, int *n) if (n) *n = char_lines_metrics.numLines; } +static void PPGeResetCurrentText() { + char_one_line.clear(); + char_lines.clear(); + AtlasTextMetrics zeroBox = { 0 }; + char_lines_metrics = zeroBox; +} + // Draws some text using the one font we have. // Mostly rewritten. void PPGeDrawCurrentText(u32 color) @@ -661,21 +669,64 @@ void PPGeDrawCurrentText(u32 color) } EndVertexDataAndDraw(GE_PRIM_RECTANGLES); } - char_one_line.clear(); - char_lines.clear(); - AtlasTextMetrics zeroBox = { 0 }; - char_lines_metrics = zeroBox; + PPGeResetCurrentText(); } void PPGeDrawText(const char *text, float x, float y, int align, float scale, u32 color) { - PPGePrepareText(text, x, y, align, scale, PPGE_LINE_USE_ELLIPSIS); + PPGePrepareText(text, x, y, align, scale, scale, PPGE_LINE_USE_ELLIPSIS); PPGeDrawCurrentText(color); } -void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, int align, float scale, u32 color) -{ - PPGePrepareText(text, x, y, align, scale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth); +static std::string StripTrailingWhite(const std::string &s) { + size_t lastChar = s.find_last_not_of(" \t\r\n"); + if (lastChar != s.npos) { + return s.substr(0, lastChar + 1); + } + return s; +} + +static std::string CropLinesToCount(const std::string &s, int numLines) { + std::vector lines; + SplitString(s, '\n', lines); + if ((int)lines.size() <= numLines) { + return s; + } + + size_t len = 0; + for (int i = 0; i < numLines; ++i) { + len += lines[i].length() + 1; + } + + return s.substr(0, len); +} + +void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, float wrapHeight, int align, float scale, u32 color) { + std::string s = text; + if (wrapHeight != 0) { + s = StripTrailingWhite(s); + } + + PPGePrepareText(s.c_str(), x, y, align, scale, scale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth); + + int zoom = (PSP_CoreParameter().pixelHeight + 479) / 480; + float maxScaleDown = zoom == 1 ? 1.3f : 2.0f; + float actualHeight = char_lines_metrics.lineHeight * char_lines_metrics.numLines; + if (actualHeight > wrapHeight) { + if (actualHeight > wrapHeight * maxScaleDown) { + float maxLines = floor(wrapHeight * maxScaleDown / char_lines_metrics.lineHeight); + actualHeight = (maxLines + 1) * char_lines_metrics.lineHeight; + // Add an ellipsis if it's just too long to be readable. + // On a PSP, it does this without scaling it down. + s = StripTrailingWhite(CropLinesToCount(s, (int)maxLines)) + "\n..."; + } + + // Measure the text again after scaling down. + PPGeResetCurrentText(); + float reduced = scale * wrapHeight / actualHeight; + // Try to keep the font as large as possible, so reduce the line height some. + PPGePrepareText(s.c_str(), x, y, align, reduced * 1.15, reduced, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth); + } PPGeDrawCurrentText(color); } diff --git a/Core/Util/PPGeDraw.h b/Core/Util/PPGeDraw.h index 8283d99f76..df18dd0dbc 100644 --- a/Core/Util/PPGeDraw.h +++ b/Core/Util/PPGeDraw.h @@ -80,7 +80,7 @@ void PPGeMeasureText(float *w, float *h, int *n, const char *text, float scale, int WrapType = PPGE_LINE_NONE, int wrapWidth = 0); // Overwrite the current text lines buffer so it can be drawn later. -void PPGePrepareText(const char *text, float x, float y, int align, float scale, +void PPGePrepareText(const char *text, float x, float y, int align, float scale, float lineHeightScale, int WrapType = PPGE_LINE_NONE, int wrapWidth = 0); // Get the metrics of the bounding box of the currently stated text. @@ -95,7 +95,7 @@ void PPGeDrawCurrentText(u32 color = 0xFFFFFFFF); // Draws some text using the one font we have. // Clears the text buffer when done. void PPGeDrawText(const char *text, float x, float y, int align, float scale = 1.0f, u32 color = 0xFFFFFFFF); -void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, int align, float scale = 1.0f, u32 color = 0xFFFFFFFF); +void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, float wrapHeight, int align, float scale = 1.0f, u32 color = 0xFFFFFFFF); // Draws a "4-patch" for button-like things that can be resized. void PPGeDraw4Patch(int atlasImage, float x, float y, float w, float h, u32 color = 0xFFFFFFFF); From dfc75e8c34428a2b122276eec366dd65e996a591 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Feb 2019 04:53:30 -0800 Subject: [PATCH 05/24] SDL: Prevent mouse capture on macbook trackpad. --- SDL/SDLMain.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index db786c3f17..3a8583970f 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -682,6 +682,8 @@ int main(int argc, char *argv[]) { NativeKey(key); break; } +// This behavior doesn't feel right on a macbook with a touchpad. +#if !PPSSPP_PLATFORM(MAC) case SDL_FINGERMOTION: { SDL_GetWindowSize(window, &w, &h); @@ -744,6 +746,7 @@ int main(int argc, char *argv[]) { SDL_PushEvent(&touchEvent); break; } +#endif case SDL_MOUSEBUTTONDOWN: switch (event.button.button) { case SDL_BUTTON_LEFT: From 555a8db33503ee878bebe35ebb04150ec8b1aac2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Feb 2019 06:27:41 -0800 Subject: [PATCH 06/24] SDL: Force fullscreen desktop for USING_FBDEV. --- SDL/SDLMain.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index db786c3f17..56c457ffdd 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -435,7 +435,7 @@ int main(int argc, char *argv[]) { SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetSwapInterval(1); - // Is resolution is too low to run windowed + // Force fullscreen if the resolution is too low to run windowed. if (g_DesktopWidth < 480 * 2 && g_DesktopHeight < 272 * 2) { mode |= SDL_WINDOW_FULLSCREEN_DESKTOP; } @@ -443,6 +443,8 @@ int main(int argc, char *argv[]) { // If we're on mobile, don't try for windowed either. #if defined(MOBILE_DEVICE) mode |= SDL_WINDOW_FULLSCREEN; +#elif defined(USING_FBDEV) + mode |= SDL_WINDOW_FULLSCREEN_DESKTOP; #else mode |= SDL_WINDOW_RESIZABLE; #endif From 67884d60b3647559a4367715350b6e579b248874 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Feb 2019 07:30:40 -0800 Subject: [PATCH 07/24] Qt: Re-enable Load button to browse for file. --- Qt/QtMain.cpp | 29 ++++++++++++++++++++++++++--- UI/MainScreen.cpp | 13 +------------ 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 178f3f82c4..f7a0daf63a 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -6,13 +6,15 @@ // Currently supports: Android, Linux, Windows, Mac OSX #include -#include -#include #include #include +#include +#include +#include #include #include #include +#include #include "ext/glslang/glslang/Public/ShaderLang.h" @@ -39,6 +41,7 @@ MainUI *emugl = NULL; static int refreshRate = 60000; +static int browseFileEvent = -1; #ifdef SDL extern void mixaudio(void *userdata, Uint8 *stream, int len) { @@ -94,6 +97,8 @@ bool System_GetPropertyBool(SystemProperty prop) { switch (prop) { case SYSPROP_HAS_BACK_BUTTON: return true; + case SYSPROP_HAS_FILE_BROWSER: + return true; case SYSPROP_APP_GOLD: #ifdef GOLD return true; @@ -108,6 +113,8 @@ bool System_GetPropertyBool(SystemProperty prop) { void System_SendMessage(const char *command, const char *parameter) { if (!strcmp(command, "finish")) { qApp->exit(0); + } else if (!strcmp(command, "browse_file")) { + QCoreApplication::postEvent(emugl, new QEvent((QEvent::Type)browseFileEvent)); } } @@ -190,6 +197,9 @@ static int mainInternal(QApplication &a) { QScopedPointer audio(new MainAudio()); audio->run(); #endif + + browseFileEvent = QEvent::registerEventType(); + int retval = a.exec(); delete emugl; return retval; @@ -387,8 +397,21 @@ bool MainUI::event(QEvent *e) case QEvent::KeyRelease: NativeKey(KeyInput(DEVICE_ID_KEYBOARD, KeyMapRawQttoNative.find(((QKeyEvent*)e)->key())->second, KEY_UP)); break; + default: - return QWidget::event(e); + if (e->type() == browseFileEvent) { + QString fileName = QFileDialog::getOpenFileName(NULL, "Load ROM", g_Config.currentDirectory.c_str(), "PSP ROMs (*.iso *.cso *.pbp *.elf *.zip *.ppdmp)"); + if (QFile::exists(fileName)) { + QDir newPath; + g_Config.currentDirectory = newPath.filePath(fileName).toStdString(); + g_Config.Save(); + + NativeMessageReceived("boot", fileName.toStdString().c_str()); + } + break; + } else { + return QWidget::event(e); + } } e->accept(); return true; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 904bcfac7f..7d90d6763f 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -915,8 +915,7 @@ void MainScreen::CreateViews() { TextView *ver = rightColumnItems->Add(new TextView(versionString, new LinearLayoutParams(Margins(70, -6, 0, 0)))); ver->SetSmall(true); ver->SetClip(false); -// Temporarily disabled the load button for Qt. See #11721 -#if defined(_WIN32) // || defined(USING_QT_UI) +#if defined(_WIN32) || defined(USING_QT_UI) rightColumnItems->Add(new Choice(mm->T("Load","Load...")))->OnClick.Handle(this, &MainScreen::OnLoadFile); #endif rightColumnItems->Add(new Choice(mm->T("Game Settings", "Settings")))->OnClick.Handle(this, &MainScreen::OnGameSettings); @@ -1028,16 +1027,6 @@ bool MainScreen::UseVerticalLayout() const { } UI::EventReturn MainScreen::OnLoadFile(UI::EventParams &e) { -#if defined(USING_QT_UI) - QString fileName = QFileDialog::getOpenFileName(NULL, "Load ROM", g_Config.currentDirectory.c_str(), "PSP ROMs (*.iso *.cso *.pbp *.elf *.zip *.ppdmp)"); - if (QFile::exists(fileName)) { - QDir newPath; - g_Config.currentDirectory = newPath.filePath(fileName).toStdString(); - g_Config.Save(); - screenManager()->switchScreen(new EmuScreen(fileName.toStdString())); - } -#endif - if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { System_SendMessage("browse_file", ""); } From 8b40f1a8143d01563234f01ffdee50a4d34abd0c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Feb 2019 07:48:32 -0800 Subject: [PATCH 08/24] Qt: Correct Browse... handling for multithreaded. --- Qt/QtMain.cpp | 11 +++++++++++ UI/MainScreen.cpp | 43 ++++++++++++++++++++++--------------------- UI/MainScreen.h | 3 ++- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index f7a0daf63a..73b6f540ad 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -31,6 +31,7 @@ #endif #include "QtMain.h" #include "gfx_es2/gpu_features.h" +#include "i18n/i18n.h" #include "math/math_util.h" #include "thread/threadutil.h" #include "util/text/utf8.h" @@ -42,6 +43,7 @@ MainUI *emugl = NULL; static int refreshRate = 60000; static int browseFileEvent = -1; +static int browseFolderEvent = -1; #ifdef SDL extern void mixaudio(void *userdata, Uint8 *stream, int len) { @@ -115,6 +117,8 @@ void System_SendMessage(const char *command, const char *parameter) { qApp->exit(0); } else if (!strcmp(command, "browse_file")) { QCoreApplication::postEvent(emugl, new QEvent((QEvent::Type)browseFileEvent)); + } else if (!strcmp(command, "browse_folder")) { + QCoreApplication::postEvent(emugl, new QEvent((QEvent::Type)browseFolderEvent)); } } @@ -199,6 +203,7 @@ static int mainInternal(QApplication &a) { #endif browseFileEvent = QEvent::registerEventType(); + browseFolderEvent = QEvent::registerEventType(); int retval = a.exec(); delete emugl; @@ -409,6 +414,12 @@ bool MainUI::event(QEvent *e) NativeMessageReceived("boot", fileName.toStdString().c_str()); } break; + } else if (e->type() == browseFolderEvent) { + I18NCategory *mm = GetI18NCategory("MainMenu"); + QString fileName = QFileDialog::getExistingDirectory(nullptr, mm->T("Choose folder"), g_Config.currentDirectory.c_str()); + if (QDir(fileName).exists()) { + NativeMessageReceived("browse_folderSelect", fileName.toStdString().c_str()); + } } else { return QWidget::event(e); } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 7d90d6763f..1fe8038cb8 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -67,12 +67,6 @@ #include "android/android-ndk-profiler/prof.h" #endif -#ifdef USING_QT_UI -#include -#include -#include -#endif - #include bool MainScreen::showHomebrewTab = false; @@ -447,12 +441,18 @@ GameBrowser::GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, Refresh(); } -void GameBrowser::FocusGame(std::string gamePath) { +void GameBrowser::FocusGame(const std::string &gamePath) { focusGamePath_ = gamePath; Refresh(); focusGamePath_.clear(); } +void GameBrowser::SetPath(const std::string &path) { + path_.SetPath(path); + g_Config.currentDirectory = path_.GetPath(); + Refresh(); +} + UI::EventReturn GameBrowser::LayoutChange(UI::EventParams &e) { *gridStyle_ = e.a == 0 ? true : false; Refresh(); @@ -466,14 +466,9 @@ UI::EventReturn GameBrowser::LastClick(UI::EventParams &e) { UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) { #ifdef __ANDROID__ - path_.SetPath(g_Config.memStickDirectory); + SetPath(g_Config.memStickDirectory); #elif defined(USING_QT_UI) - I18NCategory *mm = GetI18NCategory("MainMenu"); - QString fileName = QFileDialog::getExistingDirectory(NULL, "Browse for Folder", g_Config.currentDirectory.c_str()); - if (QDir(fileName).exists()) - path_.SetPath(fileName.toStdString()); - else - return UI::EVENT_DONE; + System_SendMessage("browse_folder", ""); #elif defined(_WIN32) #if PPSSPP_PLATFORM(UWP) // TODO UWP @@ -482,14 +477,12 @@ UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) { std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), mm->T("Choose folder")); if (!folder.size()) return UI::EVENT_DONE; - path_.SetPath(folder); + SetPath(folder); #endif #else - path_.SetPath(getenv("HOME")); + SetPath(getenv("HOME")); #endif - g_Config.currentDirectory = path_.GetPath(); - Refresh(); return UI::EVENT_DONE; } @@ -742,7 +735,7 @@ UI::EventReturn GameBrowser::GameButtonHighlight(UI::EventParams &e) { } UI::EventReturn GameBrowser::NavigateClick(UI::EventParams &e) { - DirButton *button = static_cast(e.v); + DirButton *button = static_cast(e.v); std::string text = button->GetPath(); if (button->PathAbsolute()) { path_.SetPath(text); @@ -1004,8 +997,16 @@ void MainScreen::sendMessage(const char *message, const char *value) { // Always call the base class method first to handle the most common messages. UIScreenWithBackground::sendMessage(message, value); - if (!strcmp(message, "boot") && screenManager()->topScreen() == this) { - screenManager()->switchScreen(new EmuScreen(value)); + if (screenManager()->topScreen() == this) { + if (!strcmp(message, "boot")) { + screenManager()->switchScreen(new EmuScreen(value)); + } + if (!strcmp(message, "browse_folderSelect")) { + int tab = tabHolder_->GetCurrentTab(); + if (tab >= 0 && tab < (int)gameBrowsers_.size()) { + gameBrowsers_[tab]->SetPath(value); + } + } } if (!strcmp(message, "permission_granted") && !strcmp(value, "storage")) { RecreateViews(); diff --git a/UI/MainScreen.h b/UI/MainScreen.h index d4957e1d7a..93d21a2984 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -34,7 +34,8 @@ public: UI::Choice *HomebrewStoreButton() { return homebrewStoreButton_; } - void FocusGame(std::string gamePath); + void FocusGame(const std::string &gamePath); + void SetPath(const std::string &path); protected: virtual bool DisplayTopBar(); From d19e59dde7d8731d24c2b29184ca2f509e5df206 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Feb 2019 08:03:10 -0800 Subject: [PATCH 09/24] Windows: Browse for folder like Qt. Better to separate out the platform specific stuff. --- UI/MainScreen.cpp | 25 ++++++++++--------------- Windows/main.cpp | 9 ++++++++- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 1fe8038cb8..8f1fb0044d 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -58,8 +58,8 @@ #include "Core/HLE/sceUmd.h" #ifdef _WIN32 -#include "Windows/W32Util/ShellUtil.h" -#include "Windows/MainWindow.h" +// Unfortunate, for undef DrawText... +#include "Common/CommonWindows.h" #endif #ifdef ANDROID_NDK_PROFILER @@ -467,18 +467,13 @@ UI::EventReturn GameBrowser::LastClick(UI::EventParams &e) { UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) { #ifdef __ANDROID__ SetPath(g_Config.memStickDirectory); -#elif defined(USING_QT_UI) - System_SendMessage("browse_folder", ""); -#elif defined(_WIN32) -#if PPSSPP_PLATFORM(UWP) +#elif defined(USING_QT_UI) || defined(USING_WIN_UI) + if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { + System_SendMessage("browse_folder", ""); + } +#elif PPSSPP_PLATFORM(UWP) // TODO UWP -#else - I18NCategory *mm = GetI18NCategory("MainMenu"); - std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), mm->T("Choose folder")); - if (!folder.size()) - return UI::EVENT_DONE; - SetPath(folder); -#endif + SetPath(g_Config.memStickDirectory); #else SetPath(getenv("HOME")); #endif @@ -526,7 +521,7 @@ void GameBrowser::Refresh() { if (allowBrowsing_) { topBar->Add(new Spacer(2.0f)); topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); -#if defined(_WIN32) || defined(USING_QT_UI) +#if defined(USING_WIN_UI) || defined(USING_QT_UI) topBar->Add(new Choice(mm->T("Browse", "Browse..."), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); #else topBar->Add(new Choice(mm->T("Home"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); @@ -908,7 +903,7 @@ void MainScreen::CreateViews() { TextView *ver = rightColumnItems->Add(new TextView(versionString, new LinearLayoutParams(Margins(70, -6, 0, 0)))); ver->SetSmall(true); ver->SetClip(false); -#if defined(_WIN32) || defined(USING_QT_UI) +#if defined(USING_WIN_UI) || defined(USING_QT_UI) rightColumnItems->Add(new Choice(mm->T("Load","Load...")))->OnClick.Handle(this, &MainScreen::OnLoadFile); #endif rightColumnItems->Add(new Choice(mm->T("Game Settings", "Settings")))->OnClick.Handle(this, &MainScreen::OnGameSettings); diff --git a/Windows/main.cpp b/Windows/main.cpp index c3d3dfed72..5ed2a7331d 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -27,10 +27,11 @@ #include #include +#include "base/NativeApp.h" #include "base/display.h" #include "file/vfs.h" #include "file/zip_read.h" -#include "base/NativeApp.h" +#include "i18n/i18n.h" #include "profiler/profiler.h" #include "thread/threadutil.h" #include "util/text/utf8.h" @@ -58,6 +59,7 @@ #include "Windows/GEDebugger/GEDebugger.h" #include "Windows/W32Util/DialogManager.h" +#include "Windows/W32Util/ShellUtil.h" #include "Windows/Debugger/CtrlDisAsmView.h" #include "Windows/Debugger/CtrlMemView.h" @@ -274,6 +276,11 @@ void System_SendMessage(const char *command, const char *parameter) { } } else if (!strcmp(command, "browse_file")) { MainWindow::BrowseAndBoot(""); + } else if (!strcmp(command, "browse_folder")) { + I18NCategory *mm = GetI18NCategory("MainMenu"); + std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), mm->T("Choose folder")); + if (folder.size()) + NativeMessageReceived("browse_folderSelect", folder.c_str()); } else if (!strcmp(command, "bgImage_browse")) { MainWindow::BrowseBackground(); } else if (!strcmp(command, "toggle_fullscreen")) { From bb6aa99b9e0b419540fd1f1d76616b05c65eca5a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Feb 2019 08:10:14 -0800 Subject: [PATCH 10/24] Qt: Correct text bind on first draw of string. Previously, the first frame we drew a new string was using the UI texture, typically. --- ext/native/gfx_es2/draw_text_qt.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/native/gfx_es2/draw_text_qt.cpp b/ext/native/gfx_es2/draw_text_qt.cpp index a2fae32f39..a29b363397 100644 --- a/ext/native/gfx_es2/draw_text_qt.cpp +++ b/ext/native/gfx_es2/draw_text_qt.cpp @@ -81,8 +81,6 @@ void TextDrawerQt::DrawString(DrawBuffer &target, const char *str, float x, floa auto iter = cache_.find(entryHash); if (iter != cache_.end()) { entry = iter->second.get(); - entry->lastUsedFrame = frameCount_; - draw_->BindTexture(0, entry->texture); } else { QFont *font = fontMap_.find(fontHash_)->second; QFontMetrics fm(*font); @@ -104,7 +102,6 @@ void TextDrawerQt::DrawString(DrawBuffer &target, const char *str, float x, floa entry = new TextStringEntry(); entry->bmWidth = entry->width = image.width(); entry->bmHeight = entry->height = image.height(); - entry->lastUsedFrame = frameCount_; TextureDesc desc{}; desc.type = TextureType::LINEAR2D; @@ -126,8 +123,11 @@ void TextDrawerQt::DrawString(DrawBuffer &target, const char *str, float x, floa delete[] bitmapData; cache_[entryHash] = std::unique_ptr(entry); } + float w = entry->bmWidth * fontScaleX_; float h = entry->bmHeight * fontScaleY_; + entry->lastUsedFrame = frameCount_; + draw_->BindTexture(0, entry->texture); DrawBuffer::DoAlign(align, &x, &y, &w, &h); target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color); target.Flush(true); From 6bc596d3a791ac344d71cdd8b292e4057849b568 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Feb 2019 12:17:41 -0800 Subject: [PATCH 11/24] Windows: Hide accelerators when they are disabled. Currently, mapping something to load state / save state / etc. causes the default accelerators to become disabled. In this case, it can be confusing when the menu still shows them. Fixes #11395. --- Common/KeyMap.cpp | 3 +++ Common/KeyMap.h | 1 + UI/ControlMappingScreen.cpp | 4 ++++ Windows/MainWindowMenu.cpp | 13 +++++++++---- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Common/KeyMap.cpp b/Common/KeyMap.cpp index 092ca95144..2a05e4e434 100644 --- a/Common/KeyMap.cpp +++ b/Common/KeyMap.cpp @@ -44,6 +44,7 @@ struct DefMappingStruct { }; KeyMapping g_controllerMap; +int g_controllerMapGeneration = 0; std::set g_seenPads; bool g_swapped_keys = false; @@ -833,6 +834,7 @@ void SetKeyMapping(int btn, KeyDef key, bool replace) { } g_controllerMap[btn].push_back(key); } + g_controllerMapGeneration++; UpdateNativeMenuKeys(); } @@ -956,6 +958,7 @@ void AutoConfForPad(const std::string &name) { // Add a couple of convenient keyboard mappings by default, too. g_controllerMap[VIRTKEY_PAUSE].push_back(KeyDef(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE)); g_controllerMap[VIRTKEY_UNTHROTTLE].push_back(KeyDef(DEVICE_ID_KEYBOARD, NKCODE_TAB)); + g_controllerMapGeneration++; #endif } diff --git a/Common/KeyMap.h b/Common/KeyMap.h index 9f408ed7f6..a6020fc076 100644 --- a/Common/KeyMap.h +++ b/Common/KeyMap.h @@ -85,6 +85,7 @@ class IniFile; namespace KeyMap { extern KeyMapping g_controllerMap; + extern int g_controllerMapGeneration; // Key & Button names struct KeyMap_IntStrPair { diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 68448d059b..0612cad540 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -29,6 +29,7 @@ #include "ui/view.h" #include "ui/viewgroup.h" +#include "Core/Host.h" #include "Core/HLE/sceCtrl.h" #include "Core/System.h" #include "Common/KeyMap.h" @@ -79,6 +80,7 @@ void ControlMapper::Update() { if (refresh_) { refresh_ = false; Refresh(); + host->UpdateUI(); } } @@ -169,6 +171,7 @@ void ControlMapper::MappedCallback(KeyDef kdf) { break; case REPLACEONE: KeyMap::g_controllerMap[pspKey_][actionIndex_] = kdf; + KeyMap::g_controllerMapGeneration++; break; default: ; @@ -211,6 +214,7 @@ UI::EventReturn ControlMapper::OnAddMouse(UI::EventParams ¶ms) { UI::EventReturn ControlMapper::OnDelete(UI::EventParams ¶ms) { int index = atoi(params.v->Tag().c_str()); KeyMap::g_controllerMap[pspKey_].erase(KeyMap::g_controllerMap[pspKey_].begin() + index); + KeyMap::g_controllerMapGeneration++; refresh_ = true; return UI::EVENT_DONE; } diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 2bb5f9be4b..b0a87829a7 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -50,6 +50,7 @@ namespace MainWindow { static std::unordered_map initialMenuKeys; static std::vector availableShaders; static std::string menuLanguageID = ""; + static int menuKeymapGeneration = -1; static bool menuShaderInfoLoaded = false; std::vector menuShaderInfo; @@ -209,6 +210,10 @@ namespace MainWindow { } void DoTranslateMenus(HWND hWnd, HMENU menu) { + auto useDefHotkey = [](int virtkey) { + return KeyMap::g_controllerMap[virtkey].empty(); + }; + TranslateMenuItem(menu, ID_FILE_MENU); TranslateMenuItem(menu, ID_EMULATION_MENU); TranslateMenuItem(menu, ID_DEBUG_MENU); @@ -220,9 +225,9 @@ namespace MainWindow { TranslateMenuItem(menu, ID_FILE_LOAD_DIR); TranslateMenuItem(menu, ID_FILE_LOAD_MEMSTICK); TranslateMenuItem(menu, ID_FILE_MEMSTICK); - TranslateMenuItem(menu, ID_FILE_SAVESTATE_SLOT_MENU, L"\tF3"); - TranslateMenuItem(menu, ID_FILE_QUICKLOADSTATE, L"\tF4"); - TranslateMenuItem(menu, ID_FILE_QUICKSAVESTATE, L"\tF2"); + TranslateMenuItem(menu, ID_FILE_SAVESTATE_SLOT_MENU, useDefHotkey(VIRTKEY_NEXT_SLOT) ? L"\tF3" : L""); + TranslateMenuItem(menu, ID_FILE_QUICKLOADSTATE, useDefHotkey(VIRTKEY_LOAD_STATE) ? L"\tF4" : L""); + TranslateMenuItem(menu, ID_FILE_QUICKSAVESTATE, useDefHotkey(VIRTKEY_SAVE_STATE) ? L"\tF2" : L""); TranslateMenuItem(menu, ID_FILE_LOADSTATEFILE); TranslateMenuItem(menu, ID_FILE_SAVESTATEFILE); TranslateMenuItem(menu, ID_FILE_RECORD_MENU); @@ -326,7 +331,7 @@ namespace MainWindow { bool changed = false; const std::string curLanguageID = i18nrepo.LanguageID(); - if (curLanguageID != menuLanguageID) { + if (curLanguageID != menuLanguageID || menuKeymapGeneration != KeyMap::g_controllerMapGeneration) { DoTranslateMenus(hWnd, menu); menuLanguageID = curLanguageID; changed = true; From bba008f1626b67e4a55ebcd556d4a4f026abb0da Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 18 Feb 2019 05:00:28 -0800 Subject: [PATCH 12/24] Qt: Cleanup indentation. Also some NULL -> nullptr. No actual code changes. --- Qt/QtMain.cpp | 355 +++++++++++++++++++++++------------------------- SDL/SDLMain.cpp | 4 +- 2 files changed, 174 insertions(+), 185 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 73b6f540ad..32129d42dd 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -40,7 +40,7 @@ #include -MainUI *emugl = NULL; +MainUI *emugl = nullptr; static int refreshRate = 60000; static int browseFileEvent = -1; static int browseFolderEvent = -1; @@ -242,9 +242,8 @@ void MainUI::EmuThreadJoin() { emuThread = std::thread(); } -MainUI::MainUI(QWidget *parent): - QGLWidget(parent) -{ +MainUI::MainUI(QWidget *parent) + : QGLWidget(parent) { emuThreadState = (int)EmuThreadState::DISABLED; setAttribute(Qt::WA_AcceptTouchEvents); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) @@ -259,8 +258,7 @@ MainUI::MainUI(QWidget *parent): startTimer(16); } -MainUI::~MainUI() -{ +MainUI::~MainUI() { ILOG("MainUI::Destructor"); if (emuThreadState != (int)EmuThreadState::DISABLED) { ILOG("EmuThreadStop"); @@ -279,133 +277,128 @@ MainUI::~MainUI() graphicsContext = nullptr; } -QString MainUI::InputBoxGetQString(QString title, QString defaultValue) -{ - bool ok; - QString text = QInputDialog::getText(this, title, title, QLineEdit::Normal, defaultValue, &ok); - if (!ok) - text = QString(); - return text; +QString MainUI::InputBoxGetQString(QString title, QString defaultValue) { + bool ok; + QString text = QInputDialog::getText(this, title, title, QLineEdit::Normal, defaultValue, &ok); + if (!ok) + text = QString(); + return text; } -void MainUI::resizeGL(int w, int h) -{ - if (UpdateScreenScale(w, h)) { - NativeMessageReceived("gpu_resized", ""); - } - xscale = w / this->width(); - yscale = h / this->height(); +void MainUI::resizeGL(int w, int h) { + if (UpdateScreenScale(w, h)) { + NativeMessageReceived("gpu_resized", ""); + } + xscale = w / this->width(); + yscale = h / this->height(); - PSP_CoreParameter().pixelWidth = pixel_xres; - PSP_CoreParameter().pixelHeight = pixel_yres; + PSP_CoreParameter().pixelWidth = pixel_xres; + PSP_CoreParameter().pixelHeight = pixel_yres; } -void MainUI::timerEvent(QTimerEvent *) -{ - updateGL(); - emit newFrame(); +void MainUI::timerEvent(QTimerEvent *) { + updateGL(); + emit newFrame(); } -void MainUI::changeEvent(QEvent *e) -{ - QGLWidget::changeEvent(e); - if(e->type() == QEvent::WindowStateChange) - Core_NotifyWindowHidden(isMinimized()); +void MainUI::changeEvent(QEvent *e) { + QGLWidget::changeEvent(e); + if (e->type() == QEvent::WindowStateChange) + Core_NotifyWindowHidden(isMinimized()); } -bool MainUI::event(QEvent *e) -{ - TouchInput input; - QList touchPoints; - switch(e->type()) - { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - touchPoints = static_cast(e)->touchPoints(); - foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints) { - switch (touchPoint.state()) { - case Qt::TouchPointStationary: - break; - case Qt::TouchPointPressed: - case Qt::TouchPointReleased: - input.x = touchPoint.pos().x() * g_dpi_scale_x * xscale; - input.y = touchPoint.pos().y() * g_dpi_scale_y * yscale; - input.flags = (touchPoint.state() == Qt::TouchPointPressed) ? TOUCH_DOWN : TOUCH_UP; - input.id = touchPoint.id(); - NativeTouch(input); - break; - case Qt::TouchPointMoved: - input.x = touchPoint.pos().x() * g_dpi_scale_x * xscale; - input.y = touchPoint.pos().y() * g_dpi_scale_y * yscale; - input.flags = TOUCH_MOVE; - input.id = touchPoint.id(); - NativeTouch(input); - break; - default: - break; - } - } - break; - case QEvent::MouseButtonDblClick: - if (!g_Config.bShowTouchControls || GetUIState() != UISTATE_INGAME) - emit doubleClick(); - break; - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - input.x = ((QMouseEvent*)e)->pos().x() * g_dpi_scale_x * xscale; - input.y = ((QMouseEvent*)e)->pos().y() * g_dpi_scale_y * yscale; - input.flags = (e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP; - input.id = 0; - NativeTouch(input); - break; - case QEvent::MouseMove: - input.x = ((QMouseEvent*)e)->pos().x() * g_dpi_scale_x * xscale; - input.y = ((QMouseEvent*)e)->pos().y() * g_dpi_scale_y * yscale; - input.flags = TOUCH_MOVE; - input.id = 0; - NativeTouch(input); - break; - case QEvent::Wheel: - NativeKey(KeyInput(DEVICE_ID_MOUSE, ((QWheelEvent*)e)->delta()<0 ? NKCODE_EXT_MOUSEWHEEL_DOWN : NKCODE_EXT_MOUSEWHEEL_UP, KEY_DOWN)); - break; - case QEvent::KeyPress: - { - auto qtKeycode = ((QKeyEvent*)e)->key(); - auto iter = KeyMapRawQttoNative.find(qtKeycode); - int nativeKeycode = 0; - if (iter != KeyMapRawQttoNative.end()) { - nativeKeycode = iter->second; - NativeKey(KeyInput(DEVICE_ID_KEYBOARD, nativeKeycode, KEY_DOWN)); - } +bool MainUI::event(QEvent *e) { + TouchInput input; + QList touchPoints; - // Also get the unicode value. - QString text = ((QKeyEvent*)e)->text(); - std::string str = text.toStdString(); - // Now, we don't want CHAR events for non-printable characters. Not quite sure how we'll best - // do that, but here's one attempt.... - switch (nativeKeycode) { - case NKCODE_DEL: - case NKCODE_FORWARD_DEL: - case NKCODE_TAB: - break; - default: - if (str.size()) { - int pos = 0; - int code = u8_nextchar(str.c_str(), &pos); - NativeKey(KeyInput(DEVICE_ID_KEYBOARD, code, KEY_CHAR)); - } - break; - } + switch (e->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + touchPoints = static_cast(e)->touchPoints(); + foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints) { + switch (touchPoint.state()) { + case Qt::TouchPointStationary: + break; + case Qt::TouchPointPressed: + case Qt::TouchPointReleased: + input.x = touchPoint.pos().x() * g_dpi_scale_x * xscale; + input.y = touchPoint.pos().y() * g_dpi_scale_y * yscale; + input.flags = (touchPoint.state() == Qt::TouchPointPressed) ? TOUCH_DOWN : TOUCH_UP; + input.id = touchPoint.id(); + NativeTouch(input); + break; + case Qt::TouchPointMoved: + input.x = touchPoint.pos().x() * g_dpi_scale_x * xscale; + input.y = touchPoint.pos().y() * g_dpi_scale_y * yscale; + input.flags = TOUCH_MOVE; + input.id = touchPoint.id(); + NativeTouch(input); + break; + default: + break; + } + } + break; + case QEvent::MouseButtonDblClick: + if (!g_Config.bShowTouchControls || GetUIState() != UISTATE_INGAME) + emit doubleClick(); + break; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + input.x = ((QMouseEvent*)e)->pos().x() * g_dpi_scale_x * xscale; + input.y = ((QMouseEvent*)e)->pos().y() * g_dpi_scale_y * yscale; + input.flags = (e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP; + input.id = 0; + NativeTouch(input); + break; + case QEvent::MouseMove: + input.x = ((QMouseEvent*)e)->pos().x() * g_dpi_scale_x * xscale; + input.y = ((QMouseEvent*)e)->pos().y() * g_dpi_scale_y * yscale; + input.flags = TOUCH_MOVE; + input.id = 0; + NativeTouch(input); + break; + case QEvent::Wheel: + NativeKey(KeyInput(DEVICE_ID_MOUSE, ((QWheelEvent*)e)->delta()<0 ? NKCODE_EXT_MOUSEWHEEL_DOWN : NKCODE_EXT_MOUSEWHEEL_UP, KEY_DOWN)); + break; + case QEvent::KeyPress: + { + auto qtKeycode = ((QKeyEvent*)e)->key(); + auto iter = KeyMapRawQttoNative.find(qtKeycode); + int nativeKeycode = 0; + if (iter != KeyMapRawQttoNative.end()) { + nativeKeycode = iter->second; + NativeKey(KeyInput(DEVICE_ID_KEYBOARD, nativeKeycode, KEY_DOWN)); + } + + // Also get the unicode value. + QString text = ((QKeyEvent*)e)->text(); + std::string str = text.toStdString(); + // Now, we don't want CHAR events for non-printable characters. Not quite sure how we'll best + // do that, but here's one attempt.... + switch (nativeKeycode) { + case NKCODE_DEL: + case NKCODE_FORWARD_DEL: + case NKCODE_TAB: + break; + default: + if (str.size()) { + int pos = 0; + int code = u8_nextchar(str.c_str(), &pos); + NativeKey(KeyInput(DEVICE_ID_KEYBOARD, code, KEY_CHAR)); } - break; - case QEvent::KeyRelease: - NativeKey(KeyInput(DEVICE_ID_KEYBOARD, KeyMapRawQttoNative.find(((QKeyEvent*)e)->key())->second, KEY_UP)); - break; + break; + } + } + break; + case QEvent::KeyRelease: + NativeKey(KeyInput(DEVICE_ID_KEYBOARD, KeyMapRawQttoNative.find(((QKeyEvent*)e)->key())->second, KEY_UP)); + break; - default: + default: if (e->type() == browseFileEvent) { - QString fileName = QFileDialog::getOpenFileName(NULL, "Load ROM", g_Config.currentDirectory.c_str(), "PSP ROMs (*.iso *.cso *.pbp *.elf *.zip *.ppdmp)"); + QString fileName = QFileDialog::getOpenFileName(nullptr, "Load ROM", g_Config.currentDirectory.c_str(), "PSP ROMs (*.iso *.cso *.pbp *.elf *.zip *.ppdmp)"); if (QFile::exists(fileName)) { QDir newPath; g_Config.currentDirectory = newPath.filePath(fileName).toStdString(); @@ -421,11 +414,11 @@ bool MainUI::event(QEvent *e) NativeMessageReceived("browse_folderSelect", fileName.toStdString().c_str()); } } else { - return QWidget::event(e); + return QWidget::event(e); } - } - e->accept(); - return true; + } + e->accept(); + return true; } void MainUI::initializeGL() { @@ -459,9 +452,9 @@ void MainUI::initializeGL() { } void MainUI::paintGL() { - #ifdef SDL +#ifdef SDL SDL_PumpEvents(); - #endif +#endif updateAccelerometer(); if (emuThreadState == (int)EmuThreadState::DISABLED) { UpdateRunLoop(); @@ -471,28 +464,27 @@ void MainUI::paintGL() { } } -void MainUI::updateAccelerometer() -{ +void MainUI::updateAccelerometer() { #if defined(MOBILE_DEVICE) - // TODO: Toggle it depending on whether it is enabled - QAccelerometerReading *reading = acc->reading(); - if (reading) { - AxisInput axis; - axis.deviceId = DEVICE_ID_ACCELEROMETER; - axis.flags = 0; + // TODO: Toggle it depending on whether it is enabled + QAccelerometerReading *reading = acc->reading(); + if (reading) { + AxisInput axis; + axis.deviceId = DEVICE_ID_ACCELEROMETER; + axis.flags = 0; - axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; - axis.value = reading->x(); - NativeAxis(axis); + axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; + axis.value = reading->x(); + NativeAxis(axis); - axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Y; - axis.value = reading->y(); - NativeAxis(axis); + axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Y; + axis.value = reading->y(); + NativeAxis(axis); - axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Z; - axis.value = reading->z(); - NativeAxis(axis); - } + axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Z; + axis.value = reading->z(); + NativeAxis(axis); + } #endif } @@ -504,49 +496,46 @@ void MainUI::updateAccelerometer() #define AUDIO_SAMPLESIZE 16 #define AUDIO_BUFFERS 5 -MainAudio::~MainAudio() -{ - if (feed != NULL) { - killTimer(timer); - feed->close(); - } - if (output) { - output->stop(); - delete output; - } - if (mixbuf) - free(mixbuf); +MainAudio::~MainAudio() { + if (feed != nullptr) { + killTimer(timer); + feed->close(); + } + if (output) { + output->stop(); + delete output; + } + if (mixbuf) + free(mixbuf); } -void MainAudio::run() -{ - QAudioFormat fmt; - fmt.setSampleRate(AUDIO_FREQ); - fmt.setCodec("audio/pcm"); - fmt.setChannelCount(AUDIO_CHANNELS); - fmt.setSampleSize(AUDIO_SAMPLESIZE); - fmt.setByteOrder(QAudioFormat::LittleEndian); - fmt.setSampleType(QAudioFormat::SignedInt); - mixlen = sizeof(short)*AUDIO_BUFFERS*AUDIO_CHANNELS*AUDIO_SAMPLES; - mixbuf = (char*)malloc(mixlen); - output = new QAudioOutput(fmt); - output->setBufferSize(mixlen); - feed = output->start(); - if (feed != NULL) { - // buffering has already done in the internal mixed buffer - // use a small interval to copy mixed audio stream from - // internal buffer to audio output buffer as soon as possible - // use 1 instead of 0 to prevent CPU exhausting - timer = startTimer(1); - } +void MainAudio::run() { + QAudioFormat fmt; + fmt.setSampleRate(AUDIO_FREQ); + fmt.setCodec("audio/pcm"); + fmt.setChannelCount(AUDIO_CHANNELS); + fmt.setSampleSize(AUDIO_SAMPLESIZE); + fmt.setByteOrder(QAudioFormat::LittleEndian); + fmt.setSampleType(QAudioFormat::SignedInt); + mixlen = sizeof(short)*AUDIO_BUFFERS*AUDIO_CHANNELS*AUDIO_SAMPLES; + mixbuf = (char*)malloc(mixlen); + output = new QAudioOutput(fmt); + output->setBufferSize(mixlen); + feed = output->start(); + if (feed != nullptr) { + // buffering has already done in the internal mixed buffer + // use a small interval to copy mixed audio stream from + // internal buffer to audio output buffer as soon as possible + // use 1 instead of 0 to prevent CPU exhausting + timer = startTimer(1); + } } -void MainAudio::timerEvent(QTimerEvent *) -{ - memset(mixbuf, 0, mixlen); - size_t frames = NativeMix((short *)mixbuf, AUDIO_BUFFERS*AUDIO_SAMPLES); - if (frames > 0) - feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames); +void MainAudio::timerEvent(QTimerEvent *) { + memset(mixbuf, 0, mixlen); + size_t frames = NativeMix((short *)mixbuf, AUDIO_BUFFERS*AUDIO_SAMPLES); + if (frames > 0) + feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames); } #endif diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 0daf922b9b..1f390da5bd 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -442,11 +442,11 @@ int main(int argc, char *argv[]) { // If we're on mobile, don't try for windowed either. #if defined(MOBILE_DEVICE) - mode |= SDL_WINDOW_FULLSCREEN; + mode |= SDL_WINDOW_FULLSCREEN; #elif defined(USING_FBDEV) mode |= SDL_WINDOW_FULLSCREEN_DESKTOP; #else - mode |= SDL_WINDOW_RESIZABLE; + mode |= SDL_WINDOW_RESIZABLE; #endif if (mode & SDL_WINDOW_FULLSCREEN_DESKTOP) { From 556b17503b710a41bb74e03592a684f975ac05b4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 18 Feb 2019 05:26:57 -0800 Subject: [PATCH 13/24] SDL: Allow PauseWhenMinimized to work. Essentially fixes #6276, see also #10445 and its fix. --- SDL/SDLMain.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 1f390da5bd..3f07c9fb6a 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -602,6 +602,7 @@ int main(int argc, char *argv[]) { } graphicsContext->ThreadStart(); + bool windowHidden = false; while (true) { double startTime = time_now_d(); SDL_Event event, touchEvent; @@ -638,6 +639,19 @@ int main(int argc, char *argv[]) { break; } + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_HIDDEN: + windowHidden = true; + Core_NotifyWindowHidden(windowHidden); + break; + case SDL_WINDOWEVENT_EXPOSED: + case SDL_WINDOWEVENT_SHOWN: + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_RESTORED: + windowHidden = false; + Core_NotifyWindowHidden(windowHidden); + break; + default: break; } @@ -852,7 +866,8 @@ int main(int argc, char *argv[]) { // glsl_refresh(); // auto-reloads modified GLSL shaders once per second. } - if (emuThreadState != (int)EmuThreadState::DISABLED) { + bool renderThreadPaused = windowHidden && g_Config.bPauseWhenMinimized && emuThreadState != (int)EmuThreadState::DISABLED; + if (emuThreadState != (int)EmuThreadState::DISABLED && !renderThreadPaused) { if (!graphicsContext->ThreadFrame()) break; } @@ -864,7 +879,7 @@ int main(int argc, char *argv[]) { // Simple throttling to not burn the GPU in the menu. time_update(); - if (GetUIState() != UISTATE_INGAME || !PSP_IsInited()) { + if (GetUIState() != UISTATE_INGAME || !PSP_IsInited() || renderThreadPaused) { double diffTime = time_now_d() - startTime; int sleepTime = (int)(1000.0 / 60.0) - (int)(diffTime * 1000.0); if (sleepTime > 0) From a29fe3f46ed37c90cd372968de809621b3f54f51 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 18 Feb 2019 09:14:05 -0800 Subject: [PATCH 14/24] Core: Remove some now unused variables. From removed settings. --- Core/Config.cpp | 4 ---- UI/GameSettingsScreen.h | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 4c366e5a07..fc2026c9bc 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -505,10 +505,6 @@ static int DefaultZoomType() { return (int)SmallDisplayZoom::AUTO; } -static bool DefaultTimerHack() { - return false; -} - static int DefaultAndroidHwScale() { #ifdef __ANDROID__ if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19 || System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) { diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 78619558ac..718f98d70d 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -106,8 +106,7 @@ private: UI::EventReturn OnSavedataManager(UI::EventParams &e); UI::EventReturn OnSysInfo(UI::EventParams &e); - // Temporaries to convert bools to int settings - bool cap60FPS_; + // Temporaries to convert setting types. int iAlternateSpeedPercent1_; int iAlternateSpeedPercent2_; bool enableReports_; From 1e943549c2037abd2e520d0f09ec6982f8428f37 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 18 Feb 2019 09:15:15 -0800 Subject: [PATCH 15/24] Android: Allow using custom storage paths. This allows a user to keep their save data on an insertable card, or otherwise. Currently, the UI isn't great - errors are easy to miss, and the path must be typed manually. --- UI/GameSettingsScreen.cpp | 80 ++++++++++++++++++++++++++++++++++++++- UI/GameSettingsScreen.h | 13 ++++++- UI/NativeApp.cpp | 9 +++++ 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5159d96854..c04cc88602 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -736,7 +736,11 @@ void GameSettingsScreen::CreateViews() { #if defined(USING_WIN_UI) systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Enable Windows native keyboard", "Enable Windows native keyboard"))); #endif -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#if PPSSPP_PLATFORM(ANDROID) + auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&g_Config.memStickDirectory, sy->T("Change Memory Stick folder"), (const char *)nullptr)); + memstickPath->SetEnabled(!PSP_IsInited()); + memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnChangeMemStickDir); +#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) SavePathInMyDocumentChoice = systemSettings->Add(new CheckBox(&installed_, sy->T("Save path in My Documents", "Save path in My Documents"))); SavePathInMyDocumentChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathMydoc); SavePathInOtherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Save path in installed.txt", "Save path in installed.txt"))); @@ -905,7 +909,15 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { return UI::EVENT_DONE; } -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#if PPSSPP_PLATFORM(ANDROID) + +UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { + I18NCategory *sy = GetI18NCategory("System"); + System_SendMessage("inputbox", (std::string(sy->T("Memory Stick Folder")) + ":" + g_Config.memStickDirectory).c_str()); + return UI::EVENT_DONE; +} + +#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) { const std::string PPSSPPpath = File::GetExeDirectory(); @@ -1067,6 +1079,70 @@ void GameSettingsScreen::onFinish(DialogResult result) { NativeMessageReceived("gpu_clearCache", ""); } +void GameSettingsScreen::sendMessage(const char *message, const char *value) { + UIDialogScreenWithGameBackground::sendMessage(message, value); + + I18NCategory *sy = GetI18NCategory("System"); + I18NCategory *di = GetI18NCategory("Dialog"); + + if (!strcmp(message, "inputbox_completed")) { + std::vector inputboxValue; + SplitString(value, ':', inputboxValue); + +#if PPSSPP_PLATFORM(ANDROID) + if (inputboxValue.size() >= 2 && inputboxValue[0] == sy->T("Memory Stick Folder")) { + // Allow colons in the path. + std::string newPath = std::string(value).substr(inputboxValue[0].size() + 1); + size_t pos = newPath.find_last_not_of("/"); + // Gotta have at least something but a /, and also needs to start with a /. + if (newPath.empty() || pos == newPath.npos || newPath[0] != '/') { + settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr); + return; + } + if (pos != newPath.size() - 1) { + newPath = newPath.substr(0, pos + 1); + } + + pendingMemstickFolder_ = newPath; + std::string promptMessage = sy->T("ChangingMemstickPath", "Save games, save states, and other data will not be copied to this folder.\n\nChange the Memory Stick folder?"); + if (!File::Exists(newPath)) { + promptMessage = sy->T("ChangingMemstickPathNotExists", "That folder doesn't exist yet.\n\nSave games, save states, and other data will not be copied to this folder.\n\nCreate a new Memory Stick folder?"); + } + // Add the path for clarity and proper confirmation. + promptMessage += "\n\n" + newPath + "/"; + screenManager()->push(new PromptScreen(promptMessage, di->T("Yes"), di->T("No"), std::bind(&GameSettingsScreen::CallbackMemstickFolder, this, std::placeholders::_1))); + } +#endif + } +} + +#if PPSSPP_PLATFORM(ANDROID) +void GameSettingsScreen::CallbackMemstickFolder(bool yes) { + I18NCategory *sy = GetI18NCategory("System"); + + if (yes) { + std::string memstickDirFile = g_Config.internalDataDirectory + "/memstick_dir.txt"; + std::string testWriteFile = pendingMemstickFolder_ + "/.write_verify_file"; + + // Already, create away. + if (!File::Exists(pendingMemstickFolder_)) { + File::CreateFullPath(pendingMemstickFolder_); + } + if (!writeDataToFile(true, "1", 1, testWriteFile.c_str())) { + settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr); + return; + } + File::Delete(testWriteFile); + + writeDataToFile(true, pendingMemstickFolder_.c_str(), pendingMemstickFolder_.size(), memstickDirFile.c_str()); + // Save so the settings, at least, are transferred. + g_Config.memStickDirectory = pendingMemstickFolder_ + "/"; + g_Config.Save(); + screenManager()->RecreateAllViews(); + } +} +#endif + void GameSettingsScreen::CallbackRenderingBackend(bool yes) { // If the user ends up deciding not to restart, set the config back to the current backend // so it doesn't get switched by accident. diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 718f98d70d..a72ea24e1e 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -17,6 +17,7 @@ #pragma once +#include "ppsspp_config.h" #include "ui/ui_screen.h" #include "UI/MiscScreens.h" @@ -30,6 +31,7 @@ public: void update() override; void onFinish(DialogResult result) override; + void sendMessage(const char *message, const char *value) override; std::string tag() const override { return "settings"; } UI::Event OnRecentChanged; @@ -39,6 +41,9 @@ protected: void CallbackRestoreDefaults(bool yes); void CallbackRenderingBackend(bool yes); void CallbackRenderingDevice(bool yes); +#if PPSSPP_PLATFORM(ANDROID) + void CallbackMemstickFolder(bool yes); +#endif bool UseVerticalLayout() const; private: @@ -90,7 +95,9 @@ private: UI::EventReturn OnRenderingBackend(UI::EventParams &e); UI::EventReturn OnRenderingDevice(UI::EventParams &e); UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); -#ifdef _WIN32 +#if PPSSPP_PLATFORM(ANDROID) + UI::EventReturn OnChangeMemStickDir(UI::EventParams &e); +#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) UI::EventReturn OnSavePathMydoc(UI::EventParams &e); UI::EventReturn OnSavePathOther(UI::EventParams &e); #endif @@ -120,6 +127,10 @@ private: bool resolutionEnable_; bool bloomHackEnable_; bool tessHWEnable_; + +#if PPSSPP_PLATFORM(ANDROID) + std::string pendingMemstickFolder_; +#endif }; class SettingInfoMessage : public UI::LinearLayout { diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 7b7cee5158..baa72bb9eb 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -466,6 +466,15 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // most sense. g_Config.memStickDirectory = std::string(external_dir) + "/"; g_Config.flash0Directory = std::string(external_dir) + "/flash0/"; + + std::string memstickDirFile = g_Config.internalDataDirectory + "/memstick_dir.txt"; + if (File::Exists(memstickDirFile)) { + std::string memstickDir; + readFileToString(true, memstickDirFile.c_str(), memstickDir); + if (!memstickDir.empty() && File::Exists(memstickDir)) { + g_Config.memStickDirectory = memstickDir + "/"; + } + } #elif defined(IOS) g_Config.memStickDirectory = user_data_path; g_Config.flash0Directory = std::string(external_dir) + "/flash0/"; From 04829c3c43b778830a2d53f7d95d37039f9b3096 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 19 Feb 2019 11:06:33 +0100 Subject: [PATCH 16/24] Update pspautotests submodule, not that it matters, just for convenience. --- pspautotests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pspautotests b/pspautotests index 98b83d2cb0..32d3e0426a 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 98b83d2cb06e78c6e24f287b24a89a0ef87ac53b +Subproject commit 32d3e0426af6fccd67fe00fb4a345e6d2f3ea1a4 From 11be1e7a677010c632856568d2cc987ca30ee19a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 18 Feb 2019 19:24:11 -0800 Subject: [PATCH 17/24] Core: Fix lag sync on game start / after pause. Fixes #11189. It was often stuck thinking it was constantly running over, needed a reset on large delay. --- Core/HLE/sceDisplay.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 4b25735e31..57f510f278 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -180,6 +180,11 @@ void __DisplayFlip(int cyclesLate); static void ScheduleLagSync(int over = 0) { lagSyncScheduled = g_Config.bForceLagSync; if (lagSyncScheduled) { + // Reset over if it became too high, such as after pausing or initial loading. + // There's no real sense in it being more than 1/60th of a second. + if (over > 1000000 / 60) { + over = 0; + } CoreTiming::ScheduleEvent(usToCycles(1000 + over), lagSyncEvent, 0); lastLagSync = real_time_now(); } From 00247ba3e099bb05528b51528e4afafb02f4482c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 19 Feb 2019 23:45:08 +0100 Subject: [PATCH 18/24] Fix one instance of #11815. Hope there aren't a lot more missed GOW IDs. --- assets/compat.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/compat.ini b/assets/compat.ini index 9b28f448e7..d6e56e59ef 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -419,6 +419,7 @@ NPUG80325 = true NPEG00023 = true NPHG00028 = true NPJG00120 = true +UCET00844 = true # F1 2006 has extremely long loading times if we don't limit the framerate. UCES00238 = true From e9633447cdf813157121150728153b6943884864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 19 Feb 2019 23:48:40 +0100 Subject: [PATCH 19/24] Followup to last commit: Also add IDs for GOW demos. --- assets/compat.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index d6e56e59ef..cd67716096 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -408,6 +408,8 @@ NPEG00044 = true NPJG00120 = true UCJS10114 = true UCES01401 = true +# GOW : Ghost of Sparta Demo +NPEG90035 = true # GOW : Chains Of Olympus UCUS98653 = true UCUS98705 = true @@ -420,6 +422,8 @@ NPEG00023 = true NPHG00028 = true NPJG00120 = true UCET00844 = true +# GOW: Chains of Olympus Demo +UCED00971 = true # F1 2006 has extremely long loading times if we don't limit the framerate. UCES00238 = true From 7452c69ac401ddf56083539bb5e116b19dfaefd6 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 20 Feb 2019 09:39:02 +0100 Subject: [PATCH 20/24] A lot more GoW game IDs. Should probably go through compat.ini and look up more games in our reporting... --- assets/compat.ini | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/compat.ini b/assets/compat.ini index cd67716096..4195b23d73 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -400,30 +400,39 @@ UCJS10045 = true # The GOW games are very heavy and render as fast as they can. They benefit greatly from # capping the framerate at 60fps. +UCJS10114 = true +UCKS45084 = true # GOW : Ghost of Sparta UCUS98737 = true UCAS40323 = true NPHG00092 = true NPEG00044 = true +NPEG00045 = true NPJG00120 = true +NPUG80508 = true UCJS10114 = true UCES01401 = true +UCES01473 = true # GOW : Ghost of Sparta Demo NPEG90035 = true +NPUG70125 = true # GOW : Chains Of Olympus +UCAS40198 = true UCUS98653 = true -UCUS98705 = true UCES00842 = true ULJM05438 = true ULJM05348 = true UCKS45084 = true NPUG80325 = true NPEG00023 = true +NPHG00027 = true NPHG00028 = true -NPJG00120 = true +NPJH50170 = true UCET00844 = true # GOW: Chains of Olympus Demo +UCUS98705 = true UCED00971 = true +UCUS98713 = true # F1 2006 has extremely long loading times if we don't limit the framerate. UCES00238 = true From e226c0414ac42484a64f5880e8dd1afc77c2b141 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 20 Feb 2019 10:31:25 +0100 Subject: [PATCH 21/24] compat.ini: More IDs to lists of games requiring default CPU freq and buffered rendering. --- assets/compat.ini | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/assets/compat.ini b/assets/compat.ini index 4195b23d73..035416b30c 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -204,17 +204,39 @@ ULJM05255 = true ULJM05359 = true ULJM05885 = true NPJH50825 = true +# GOW : JP/Korean +UCJS10114 = true +UCKS45084 = true +# GOW : Ghost of Sparta +UCUS98737 = true +UCAS40323 = true +NPHG00092 = true +NPEG00044 = true +NPEG00045 = true +NPJG00120 = true +NPUG80508 = true +UCJS10114 = true +UCES01401 = true +UCES01473 = true +# GOW : Ghost of Sparta Demo +NPEG90035 = true +NPUG70125 = true # GOW : Chains Of Olympus +UCAS40198 = true UCUS98653 = true -UCUS98705 = true UCES00842 = true ULJM05438 = true ULJM05348 = true UCKS45084 = true NPUG80325 = true NPEG00023 = true +NPHG00027 = true NPHG00028 = true -NPJG00120 = true +NPJH50170 = true +UCET00844 = true +# GOW: Chains of Olympus Demo +UCUS98705 = true +UCED00971 = true # Daxter UCUS98618 = true UCES00044 = true @@ -263,6 +285,7 @@ ULES00740 = true # Chili Con Carnage ULUS10216 = true ULES00629 = true +# TODO: There are many more. [RequireBlockTransfer] # Warn the user that the game will have issue graphic, if simulate block transfer is not enabled. @@ -323,9 +346,15 @@ UCUS98737 = true UCAS40323 = true NPHG00092 = true NPEG00044 = true +NPEG00045 = true NPJG00120 = true +NPUG80508 = true UCJS10114 = true UCES01401 = true +UCES01473 = true +# GOW : Ghost of Sparta Demo +NPEG90035 = true +NPUG70125 = true [MGS2AcidHack] ULES00008 = true From 8863ceaa6a3e0ab76ebbdab776cd960bbb7ac7d5 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 20 Feb 2019 13:17:24 +0100 Subject: [PATCH 22/24] Another GOW game ID. See #11815 --- assets/compat.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index 035416b30c..8c0ebd5cab 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -219,6 +219,7 @@ UCJS10114 = true UCES01401 = true UCES01473 = true # GOW : Ghost of Sparta Demo +NPJG90095 = true NPEG90035 = true NPUG70125 = true # GOW : Chains Of Olympus @@ -353,6 +354,7 @@ UCJS10114 = true UCES01401 = true UCES01473 = true # GOW : Ghost of Sparta Demo +NPJG90095 = true NPEG90035 = true NPUG70125 = true @@ -445,6 +447,7 @@ UCES01473 = true # GOW : Ghost of Sparta Demo NPEG90035 = true NPUG70125 = true +NPJG90095 = true # GOW : Chains Of Olympus UCAS40198 = true UCUS98653 = true From 13c85b127d35648398001684045c7a45641fa517 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 20 Feb 2019 13:51:42 +0100 Subject: [PATCH 23/24] Add a large number of missing Game IDs to compat.ini. May help various Phantasy Star and SOCOM issues, in particular. Like #9118, #8492, #10749. #11814, #9802 --- assets/compat.ini | 106 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 17 deletions(-) diff --git a/assets/compat.ini b/assets/compat.ini index 8c0ebd5cab..6607fe3a8f 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -31,15 +31,31 @@ [VertexDepthRounding] # Phantasy Star Portable needs depth rounding to 16-bit precision for text to show up. # It's enough to do it at the vertex granularity. #3777 +# Phantasy Star Portable ULJM05309 = true ULUS10410 = true ULES01218 = true ULJM08023 = true ULES01218 = true -# Phantasy Star Portable 2 JP (missing text) +# Phantasy Star Portable 1 Demo +NPUH90023 = true +# Phantasy Star Portable 2 +ULES01439 = true +ULUS10529 = true ULJM05493 = true NPJH50043 = true ULJM08030 = true +NPUH90023 = true +ULJM91014 = true +NPJH90002 = true +ULJM05732 = true +NPJH50332 = true +# Phantasy Star Portable 2 JP Demo +ULJM91018 = true +NPJH90062 = true +# Phantasy Star Portable Infinity Demo +NPJH90157 = true # Infinity demo + # Puyo Puyo Fever 2 #3663 (layering) ULJM05058 = true # NBA 2K13 #6603 (menu glitches) @@ -71,47 +87,83 @@ ULJS00454 = true # Heroes Phantasia Limited Edition Disc requires pixel depth rounding. ULJS00455 = true # Phantasy Star games flickering -# Phantasy Star Portable 1 +# Phantasy Star Portable ULJM05309 = true ULUS10410 = true ULES01218 = true ULJM08023 = true ULES01218 = true -# Phantasy Star Portable 2 JP +# Phantasy Star Portable 1 Demo +NPUH90023 = true +# Phantasy Star Portable 2 +ULES01439 = true +ULUS10529 = true ULJM05493 = true NPJH50043 = true ULJM08030 = true -# Phantasy Star Portable 1 Demo NPUH90023 = true ULJM91014 = true NPJH90002 = true +ULJM05732 = true +NPJH50332 = true # Phantasy Star Portable 2 JP Demo ULJM91018 = true NPJH90062 = true +# Phantasy Star Portable Infinity Demo +NPJH90157 = true # Infinity demo [DepthRangeHack] # Phantasy Star Portable 2 and Infinity both use viewport depth outside [0, 1]. # This gets clamped in our current implementation, but attempts to fix it run into # Other bugs, so we've restored this hack for now. +# Phantasy Star Portable +ULJM05309 = true +ULUS10410 = true +ULES01218 = true +ULJM08023 = true +ULES01218 = true +# Phantasy Star Portable 1 Demo +NPUH90023 = true +# Phantasy Star Portable 2 +ULES01439 = true +ULUS10529 = true ULJM05493 = true NPJH50043 = true ULJM08030 = true -ULES01439 = true -ULUS10529 = true -ULJM91018 = true # Infinity demo disc? -NPJH90157 = true # Infinity demo +NPUH90023 = true +ULJM91014 = true +NPJH90002 = true ULJM05732 = true NPJH50332 = true +# Phantasy Star Portable 2 JP Demo +ULJM91018 = true +NPJH90062 = true +# Phantasy Star Portable Infinity Demo +NPJH90157 = true # Infinity demo [ClearToRAM] # SOCOM Navy Seals games require this. See issue #8973. -# Navy Seals -UCUS98615 = true -ULES00038 = true -UCKS45021 = true -# Fireteam Bravo 3 -UCJS10102 = true +# Navy Seals : Tactical Strike +UCES00855 = true +UCUS98649 = true NPJG00035 = true +NPJG90068 = true +UCJS10102 = true +# Tactical Strike demo +NPUG70003 = true +# Fireteam Bravo +UCKS45021 = true +UCUS98615 = true +UCES00038 = true +ULES00038 = true +# Fireteam Bravo 2 +UCES00543 = true +UCUS98645 = true +# Fireteam Bravo 2 Demo +UCUS98677 = true +UCUS98691 = true +# Fireteam Bravo 3 +NPHG00032 = true UCUS98716 = true UCES01242 = true @@ -143,10 +195,13 @@ UCES00373 = true # It makes sceGeDrawSync eat a lot of cycles which can affect timing in lots of games, # might be negative for others, but happens to fix games below. # Crash Tag Team Racing needs it to pass checking memory stick screen. -ULUS10044 = true ULES00168 = true +ULES00169 = true +ULES00170 = true +ULES00171 = true ULES00172 = true ULJM05036 = true +ULUS10044 = true # Gundam Battle Royale might need it to avoid crashes when certain Ace enemies shows up ULJS00083 = true ULKS46104 = true @@ -168,6 +223,7 @@ UCKS45033 = true UCJS18014 = true UCUS98700 = true NPJG00032 = true +UCJX90019 = true [FakeMipmapChange] # This hacks separates each mipmap to independent textures to display wrong-size mipmaps. @@ -238,6 +294,7 @@ UCET00844 = true # GOW: Chains of Olympus Demo UCUS98705 = true UCED00971 = true +UCUS98713 = true # Daxter UCUS98618 = true UCES00044 = true @@ -304,6 +361,8 @@ ULES01556 = true ULJM05170 = true ULJM08033 = true NPJH50373 = true +NPUH10191 = true +NPUH10197 = true # Grand Knights History need it to fix blackboxes on characters and flickering texture . See issues #2135 , #6099 ULJS00394 = true ULJS19068 = true @@ -333,6 +392,9 @@ ULUS10025 = true ULJM05228 = true NPJH50305 = true ULJM05049 = true +ULKS46027 = true +ULAS42019 = true + # Dominator ULUS10236 = true ULES00750 = true @@ -340,6 +402,7 @@ ULJM05242 = true ULJM05371 = true NPJH50304 = true ULES00703 = true +ULAS42095 = true [RequireDefaultCPUClock] # GOW : Ghost of Sparta @@ -360,6 +423,9 @@ NPUG70125 = true [MGS2AcidHack] ULES00008 = true +ULES00284 = true +ULJM05047 = true +ULKS46065 = true ULJM08001 = true ULJM05001 = true ULAS42007 = true @@ -371,6 +437,7 @@ ULES00622 = true # SR1 ULUS10195 = true # SR1 ULUS10323 = true # SR2 ULES00940 = true # SR2 +ULET00958 = true [BlockTransferAllowCreateFB] NPJH50686 = true # Digimon Adventures (JP, and English patches...) @@ -411,10 +478,11 @@ ULES01362 = true # Yu-Gi-Oh! 5D's Tag Force 5 ULUS10555 = true ULJM05734 = true +ULES01474 = true # Yu-Gi-Oh! 5D's Tag Force 6 -ULJM-05940 = true -NPJH-50794 = true +ULJM05940 = true +NPJH50794 = true # Yu-Gi-Oh! 5D's Tag Force ULJM05940 = true @@ -426,6 +494,8 @@ NPJH00142 = true # F1 2006 won't boot at all with our standard unrealistically fast timing. UCES00238 = true UCJS10045 = true +# F1 2005, japan only? +UCJS10019 = true [ForceMax60FPS] # The GOW games are very heavy and render as fast as they can. They benefit greatly from @@ -469,3 +539,5 @@ UCUS98713 = true # F1 2006 has extremely long loading times if we don't limit the framerate. UCES00238 = true UCJS10045 = true +# F1 2005, japan only? +UCJS10019 = true From b1146d8d9d8b7186d924d0a96f7fc9a77219d958 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 20 Feb 2019 17:04:30 -0800 Subject: [PATCH 24/24] PPGe: Correct text wrap without height. --- Core/Util/PPGeDraw.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 442ce268af..74dc3baa8d 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -703,7 +703,7 @@ static std::string CropLinesToCount(const std::string &s, int numLines) { void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, float wrapHeight, int align, float scale, u32 color) { std::string s = text; - if (wrapHeight != 0) { + if (wrapHeight != 0.0f) { s = StripTrailingWhite(s); } @@ -712,7 +712,7 @@ void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, fl int zoom = (PSP_CoreParameter().pixelHeight + 479) / 480; float maxScaleDown = zoom == 1 ? 1.3f : 2.0f; float actualHeight = char_lines_metrics.lineHeight * char_lines_metrics.numLines; - if (actualHeight > wrapHeight) { + if (wrapHeight != 0.0f && actualHeight > wrapHeight) { if (actualHeight > wrapHeight * maxScaleDown) { float maxLines = floor(wrapHeight * maxScaleDown / char_lines_metrics.lineHeight); actualHeight = (maxLines + 1) * char_lines_metrics.lineHeight;