From e36f894f666e8e477c94be91391c6e4b98a81841 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 5 Oct 2019 08:37:18 -0700 Subject: [PATCH 01/71] HLE: Replace Motorstorm pixel read func. Helps with some lighting, see #12363. --- Core/HLE/ReplaceTables.cpp | 10 ++++++++++ Core/MIPS/MIPSAnalyst.cpp | 1 + 2 files changed, 11 insertions(+) diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index e64adef5fa..a1d9e25e5c 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -1230,6 +1230,15 @@ static int Hook_starocean_clear_framebuf_after() { return 0; } +static int Hook_motorstorm_pixel_read() { + u32 fb_address = Memory::Read_U32(currentMIPS->r[MIPS_REG_A0] + 0x18); + u32 fb_height = Memory::Read_U16(currentMIPS->r[MIPS_REG_A0] + 0x26); + u32 fb_stride = Memory::Read_U16(currentMIPS->r[MIPS_REG_A0] + 0x28); + gpu->PerformMemoryDownload(fb_address, fb_height * fb_stride); + CBreakPoints::ExecMemCheck(fb_address, true, fb_height * fb_stride, currentMIPS->pc); + return 0; +} + #define JITFUNC(f) (&MIPSComp::MIPSFrontendInterface::f) // Can either replace with C functions or functions emitted in Asm/ArmAsm. @@ -1341,6 +1350,7 @@ static const ReplacementTableEntry entries[] = { { "marvelalliance1_copy", &Hook_marvelalliance1_copy_after, 0, REPFLAG_HOOKENTER, 0x69c }, { "starocean_clear_framebuf", &Hook_starocean_clear_framebuf_before, 0, REPFLAG_HOOKENTER, 0 }, { "starocean_clear_framebuf", &Hook_starocean_clear_framebuf_after, 0, REPFLAG_HOOKEXIT, 0 }, + { "motorstorm_pixel_read", &Hook_motorstorm_pixel_read, 0, REPFLAG_HOOKENTER, 0 }, {} }; diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 7a3459f808..9b951848be 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -175,6 +175,7 @@ static const HardHashTableEntry hardcodedHashes[] = { { 0x3024e961d1811dea, 396, "fmod", }, { 0x3050bfd0e729dfbf, 220, "atvoffroadfuryblazintrails_download_frame", }, // ATV Offroad Fury Blazin' Trails (US) { 0x30c9c4f420573eb6, 540, "expf", }, + { 0x311779b4db21dbf3, 124, "motorstorm_pixel_read" }, // Motorstorm Arctic Edge (US) { 0x317afeb882ff324a, 212, "memcpy", }, // Mimana (US) { 0x31ea2e192f5095a1, 52, "vector_add_t", }, { 0x31f523ef18898e0e, 420, "logf", }, From 46464d27224b57d623b840061ea417e535db8bda Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 09:02:10 -0800 Subject: [PATCH 02/71] Windows: Resolve module/full paths for long paths. --- Common/FileUtil.cpp | 23 +++++++++++++++++------ Core/PSPLoaders.cpp | 14 +++++++++++--- Windows/W32Util/Misc.cpp | 31 ++++++++++++++++++++++++++----- Windows/W32Util/Misc.h | 1 + Windows/WindowsHost.cpp | 13 ++++++++++--- Windows/main.cpp | 12 ++++++------ 6 files changed, 71 insertions(+), 23 deletions(-) diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 04ae730c1c..d1ad8a5b72 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -792,12 +792,23 @@ const std::string &GetExeDirectory() if (ExePath.empty()) { #ifdef _WIN32 - TCHAR program_path[4096] = {0}; - GetModuleFileName(NULL, program_path, ARRAY_SIZE(program_path) - 1); - program_path[ARRAY_SIZE(program_path) - 1] = '\0'; - TCHAR *last_slash = _tcsrchr(program_path, '\\'); - if (last_slash != NULL) - *(last_slash + 1) = '\0'; +#ifdef UNICODE + std::wstring program_path; +#else + std::string program_path; +#endif + size_t sz; + do { + program_path.resize(program_path.size() + MAX_PATH); + // On failure, this will return the same value as passed in, but success will always be one lower. + sz = GetModuleFileName(nullptr, &program_path[0], (DWORD)program_path.size()); + } while (sz >= program_path.size()); + + TCHAR *last_slash = _tcsrchr(&program_path[0], '\\'); + if (last_slash != nullptr) + program_path.resize(last_slash - &program_path[0] + 1); + else + program_path.resize(sz); #ifdef UNICODE ExePath = ConvertWStringToUTF8(program_path); #else diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 721d26d16e..2e032ae2a3 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -284,10 +284,18 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { static std::string NormalizePath(const std::string &path) { #ifdef _WIN32 - wchar_t buf[512] = {0}; std::wstring wpath = ConvertUTF8ToWString(path); - if (GetFullPathName(wpath.c_str(), (int)ARRAY_SIZE(buf) - 1, buf, NULL) == 0) - return ""; + std::wstring buf; + buf.resize(512); + size_t sz = GetFullPathName(wpath.c_str(), (DWORD)buf.size(), &buf[0], nullptr); + if (sz != 0 && sz < buf.size()) { + buf.resize(sz); + } else if (sz > buf.size()) { + buf.resize(sz); + sz = GetFullPathName(wpath.c_str(), (DWORD)buf.size(), &buf[0], nullptr); + // This should truncate off the null terminator. + buf.resize(sz); + } return ConvertWStringToUTF8(buf); #else char buf[PATH_MAX + 1]; diff --git a/Windows/W32Util/Misc.cpp b/Windows/W32Util/Misc.cpp index 7c2953b2d6..ca26ad7a4b 100644 --- a/Windows/W32Util/Misc.cpp +++ b/Windows/W32Util/Misc.cpp @@ -131,15 +131,36 @@ namespace W32Util return cmdline; } + void GetSelfExecuteParams(std::wstring &workingDirectory, std::wstring &moduleFilename) { + workingDirectory.resize(MAX_PATH); + size_t sz = GetCurrentDirectoryW((DWORD)workingDirectory.size(), &workingDirectory[0]); + if (sz != 0 && sz < workingDirectory.size()) { + // This means success, so now we can remove the null terminator. + workingDirectory.resize(sz); + } else if (sz > workingDirectory.size()) { + // If insufficient, sz will include the null terminator, so we remove after. + workingDirectory.resize(sz); + sz = GetCurrentDirectoryW((DWORD)sz, &workingDirectory[0]); + workingDirectory.resize(sz); + } + + moduleFilename.clear(); + do { + moduleFilename.resize(moduleFilename.size() + MAX_PATH); + // On failure, this will return the same value as passed in, but success will always be one lower. + sz = GetModuleFileName(GetModuleHandle(nullptr), &moduleFilename[0], (DWORD)moduleFilename.size()); + } while (sz >= moduleFilename.size()); + moduleFilename.resize(sz); + } + void ExitAndRestart() { // This preserves arguments (for example, config file) and working directory. + std::wstring workingDirectory; + std::wstring moduleFilename; + GetSelfExecuteParams(workingDirectory, moduleFilename); - wchar_t moduleFilename[MAX_PATH]; - wchar_t workingDirectory[MAX_PATH]; - GetCurrentDirectoryW(MAX_PATH, workingDirectory); const wchar_t *cmdline = RemoveExecutableFromCommandLine(GetCommandLineW()); - GetModuleFileName(GetModuleHandle(NULL), moduleFilename, MAX_PATH); - ShellExecute(NULL, NULL, moduleFilename, cmdline, workingDirectory, SW_SHOW); + ShellExecute(nullptr, nullptr, moduleFilename.c_str(), cmdline, workingDirectory.c_str(), SW_SHOW); ExitProcess(0); } diff --git a/Windows/W32Util/Misc.h b/Windows/W32Util/Misc.h index 457874450e..680a48c62e 100644 --- a/Windows/W32Util/Misc.h +++ b/Windows/W32Util/Misc.h @@ -12,6 +12,7 @@ namespace W32Util BOOL CopyTextToClipboard(HWND hwnd, const std::wstring &wtext); void MakeTopMost(HWND hwnd, bool topMost); void ExitAndRestart(); + void GetSelfExecuteParams(std::wstring &workingDirectory, std::wstring &moduleFilename); } struct GenericListViewColumn diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index 4508a1d3d3..84d09875bd 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -353,6 +353,7 @@ bool WindowsHost::CreateDesktopShortcut(std::string argumentPath, std::string ga // Get the desktop folder + // TODO: Not long path safe. wchar_t *pathbuf = new wchar_t[MAX_PATH + gameTitle.size() + 100]; SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, pathbuf); @@ -370,10 +371,16 @@ bool WindowsHost::CreateDesktopShortcut(std::string argumentPath, std::string ga wcscat(pathbuf, L"\\"); wcscat(pathbuf, ConvertUTF8ToWString(gameTitle).c_str()); - wchar_t module[MAX_PATH]; - GetModuleFileName(NULL, module, MAX_PATH); + std::wstring moduleFilename; + size_t sz; + do { + moduleFilename.resize(moduleFilename.size() + MAX_PATH); + // On failure, this will return the same value as passed in, but success will always be one lower. + sz = GetModuleFileName(nullptr, &moduleFilename[0], (DWORD)moduleFilename.size()); + } while (sz >= moduleFilename.size()); + moduleFilename.resize(sz); - CreateLink(module, ConvertUTF8ToWString(argumentPath).c_str(), pathbuf, ConvertUTF8ToWString(gameTitle).c_str()); + CreateLink(moduleFilename.c_str(), ConvertUTF8ToWString(argumentPath).c_str(), pathbuf, ConvertUTF8ToWString(gameTitle).c_str()); delete [] pathbuf; return false; diff --git a/Windows/main.cpp b/Windows/main.cpp index 866f4c0746..24718559ed 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -374,17 +374,17 @@ 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); + std::wstring workingDirectory; + std::wstring moduleFilename; + W32Util::GetSelfExecuteParams(workingDirectory, moduleFilename); + 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.lpFile = moduleFilename.c_str(); info.lpParameters = cmdline; - info.lpDirectory = workingDirectory; + info.lpDirectory = workingDirectory.c_str(); info.nShow = SW_HIDE; if (ShellExecuteEx(&info) != TRUE) { return false; From 97ac18ceee473f6ea8932aaf7f1de1f824bf8215 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 09:34:20 -0800 Subject: [PATCH 03/71] Windows: Make open/save dialogs long path aware. At least, in most cases. If the filename is super long, it'll open twice, but we try to avoid. --- Windows/Debugger/CtrlDisAsmView.cpp | 51 ++++++--------- Windows/W32Util/ShellUtil.cpp | 96 ++++++++++++++++------------- 2 files changed, 72 insertions(+), 75 deletions(-) diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index 71fea8b4ac..7717503399 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -4,6 +4,7 @@ #include "Core/MemMap.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Windows/W32Util/Misc.h" +#include "Windows/W32Util/ShellUtil.h" #include "Windows/MainWindow.h" #include "Windows/InputBox.h" @@ -1323,47 +1324,31 @@ std::string CtrlDisAsmView::disassembleRange(u32 start, u32 size) return result; } -void CtrlDisAsmView::disassembleToFile() -{ - wchar_t fileName[MAX_PATH]; - u32 size; - +void CtrlDisAsmView::disassembleToFile() { // get size - if (executeExpressionWindow(wnd,debugger,size) == false) return; - if (size == 0 || size > 10*1024*1024) - { + u32 size; + if (executeExpressionWindow(wnd,debugger,size) == false) + return; + if (size == 0 || size > 10*1024*1024) { MessageBox(wnd,L"Invalid size!",L"Error",MB_OK); return; } - // get file name - OPENFILENAME ofn; - ZeroMemory( &ofn , sizeof( ofn)); - ofn.lStructSize = sizeof ( ofn ); - ofn.hwndOwner = NULL ; - ofn.lpstrFile = fileName ; - ofn.lpstrFile[0] = '\0'; - ofn.nMaxFile = sizeof( fileName ); - ofn.lpstrFilter = L"All Files\0*.*\0\0"; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = NULL ; - ofn.nMaxFileTitle = 0 ; - ofn.lpstrInitialDir = NULL ; - ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_OVERWRITEPROMPT; + std::string filename; + if (W32Util::BrowseForFileName(false, nullptr, L"Save Disassembly As...", nullptr, L"All Files\0*.*\0\0", nullptr, filename)) { + std::wstring fileName = ConvertUTF8ToWString(filename); + FILE *output = _wfopen(fileName.c_str(), L"wb"); + if (output == nullptr) { + MessageBox(wnd, L"Could not open file!", L"Error", MB_OK); + return; + } - if (GetSaveFileName(&ofn) == false) return; + std::string disassembly = disassembleRange(curAddress, size); + fprintf(output, "%s", disassembly.c_str()); - FILE* output = _wfopen(fileName, L"wb"); - if (output == NULL) { - MessageBox(wnd,L"Could not open file!",L"Error",MB_OK); - return; + fclose(output); + MessageBox(wnd, L"Finished!", L"Done", MB_OK); } - - std::string disassembly = disassembleRange(curAddress,size); - fprintf(output,"%s",disassembly.c_str()); - - fclose(output); - MessageBox(wnd,L"Finished!",L"Done",MB_OK); } void CtrlDisAsmView::getOpcodeText(u32 address, char* dest, int bufsize) diff --git a/Windows/W32Util/ShellUtil.cpp b/Windows/W32Util/ShellUtil.cpp index 9a73338de2..5b2b93eab8 100644 --- a/Windows/W32Util/ShellUtil.cpp +++ b/Windows/W32Util/ShellUtil.cpp @@ -9,10 +9,10 @@ #include "util/text/utf8.h" #include "ShellUtil.h" -#include "CommDlg.h" #include #include +#include namespace W32Util { @@ -44,80 +44,92 @@ namespace W32Util //--------------------------------------------------------------------------------------------------- // function WinBrowseForFileName //--------------------------------------------------------------------------------------------------- - bool BrowseForFileName (bool _bLoad, HWND _hParent, const wchar_t *_pTitle, - const wchar_t *_pInitialFolder,const wchar_t *_pFilter,const wchar_t *_pExtension, - std::string& _strFileName) - { - wchar_t szFile [MAX_PATH+1] = {0}; - wchar_t szFileTitle [MAX_PATH+1] = {0}; + bool BrowseForFileName(bool _bLoad, HWND _hParent, const wchar_t *_pTitle, + const wchar_t *_pInitialFolder, const wchar_t *_pFilter, const wchar_t *_pExtension, + std::string &_strFileName) { + // Let's hope this is large enough, don't want to trigger the dialog twice... + std::wstring filenameBuffer(32768 * 10, '\0'); OPENFILENAME ofn{ sizeof(OPENFILENAME) }; + auto resetFileBuffer = [&] { + ofn.nMaxFile = (DWORD)filenameBuffer.size(); + ofn.lpstrFile = &filenameBuffer[0]; + if (!_strFileName.empty()) + wcsncpy(ofn.lpstrFile, ConvertUTF8ToWString(_strFileName).c_str(), filenameBuffer.size() - 1); + }; + + resetFileBuffer(); ofn.lpstrInitialDir = _pInitialFolder; ofn.lpstrFilter = _pFilter; - ofn.nMaxFile = ARRAY_SIZE(szFile); - ofn.lpstrFile = szFile; - ofn.lpstrFileTitle = szFileTitle; - ofn.nMaxFileTitle = ARRAY_SIZE(szFileTitle); + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; ofn.lpstrDefExt = _pExtension; ofn.hwndOwner = _hParent; ofn.Flags = OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_HIDEREADONLY; - if (!_strFileName.empty()) - wcsncpy(ofn.lpstrFile, ConvertUTF8ToWString(_strFileName).c_str(), MAX_PATH); + int success = _bLoad ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn); + if (success == 0 && CommDlgExtendedError() == FNERR_BUFFERTOOSMALL) { + size_t sz = *(unsigned short *)&filenameBuffer[0]; + // Documentation is unclear if this is WCHARs to CHARs. + filenameBuffer.resize(filenameBuffer.size() + sz * 2); + resetFileBuffer(); + success = _bLoad ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn); + } - if (((_bLoad) ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn))) - { + if (success) { _strFileName = ConvertWStringToUTF8(ofn.lpstrFile); return true; } - else - return false; + return false; } std::vector BrowseForFileNameMultiSelect(bool _bLoad, HWND _hParent, const wchar_t *_pTitle, - const wchar_t *_pInitialFolder,const wchar_t *_pFilter,const wchar_t *_pExtension) - { - wchar_t szFile [MAX_PATH+1+2048*2] = {0}; - wchar_t szFileTitle [MAX_PATH+1] = {0}; + const wchar_t *_pInitialFolder, const wchar_t *_pFilter, const wchar_t *_pExtension) { + // Let's hope this is large enough, don't want to trigger the dialog twice... + std::wstring filenameBuffer(32768 * 10, '\0'); OPENFILENAME ofn{ sizeof(OPENFILENAME) }; + auto resetFileBuffer = [&] { + ofn.nMaxFile = (DWORD)filenameBuffer.size(); + ofn.lpstrFile = &filenameBuffer[0]; + }; + + resetFileBuffer(); ofn.lpstrInitialDir = _pInitialFolder; ofn.lpstrFilter = _pFilter; - ofn.nMaxFile = ARRAY_SIZE(szFile); - ofn.lpstrFile = szFile; - ofn.lpstrFileTitle = szFileTitle; - ofn.nMaxFileTitle = ARRAY_SIZE(szFileTitle); + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; ofn.lpstrDefExt = _pExtension; ofn.hwndOwner = _hParent; ofn.Flags = OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT; std::vector files; + int success = _bLoad ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn); + if (success == 0 && CommDlgExtendedError() == FNERR_BUFFERTOOSMALL) { + size_t sz = *(unsigned short *)&filenameBuffer[0]; + // Documentation is unclear if this is WCHARs to CHARs. + filenameBuffer.resize(filenameBuffer.size() + sz * 2); + resetFileBuffer(); + success = _bLoad ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn); + } - if (((_bLoad) ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn))) - { + if (success) { std::string directory = ConvertWStringToUTF8(ofn.lpstrFile); wchar_t *temp = ofn.lpstrFile; - wchar_t *oldtemp = temp; - temp += wcslen(temp)+1; - if (*temp==0) - { + temp += wcslen(temp) + 1; + if (*temp == 0) { //we only got one file - files.push_back(ConvertWStringToUTF8(oldtemp)); - } - else - { - while (*temp) - { - files.push_back(directory+"\\"+ConvertWStringToUTF8(temp)); - temp += wcslen(temp)+1; + files.push_back(directory); + } else { + while (*temp) { + files.push_back(directory + "\\" + ConvertWStringToUTF8(temp)); + temp += wcslen(temp) + 1; } } - return files; } - else - return std::vector(); // empty vector; + return files; } AsyncBrowseDialog::AsyncBrowseDialog(HWND parent, UINT completeMsg, std::wstring title) From c222ff0bf718ee727bf4af985d8ceb728473aa80 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 09:35:00 -0800 Subject: [PATCH 04/71] Windows: Allow read only files in load dialog. --- Windows/W32Util/ShellUtil.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Windows/W32Util/ShellUtil.cpp b/Windows/W32Util/ShellUtil.cpp index 5b2b93eab8..0a70a9d38b 100644 --- a/Windows/W32Util/ShellUtil.cpp +++ b/Windows/W32Util/ShellUtil.cpp @@ -66,7 +66,9 @@ namespace W32Util ofn.nMaxFileTitle = 0; ofn.lpstrDefExt = _pExtension; ofn.hwndOwner = _hParent; - ofn.Flags = OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_HIDEREADONLY; + ofn.Flags = OFN_NOCHANGEDIR | OFN_EXPLORER; + if (!_bLoad) + ofn.Flags |= OFN_HIDEREADONLY; int success = _bLoad ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn); if (success == 0 && CommDlgExtendedError() == FNERR_BUFFERTOOSMALL) { @@ -103,7 +105,9 @@ namespace W32Util ofn.nMaxFileTitle = 0; ofn.lpstrDefExt = _pExtension; ofn.hwndOwner = _hParent; - ofn.Flags = OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT; + ofn.Flags = OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_ALLOWMULTISELECT; + if (!_bLoad) + ofn.Flags |= OFN_HIDEREADONLY; std::vector files; int success = _bLoad ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn); From 6706fef58a7716933b7b9013fe166f922a80611b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 10:32:48 -0800 Subject: [PATCH 05/71] Windows: Make memory dump long path friendly. --- Windows/Debugger/DumpMemoryWindow.cpp | 52 ++++++++++++++++----------- Windows/Debugger/DumpMemoryWindow.h | 8 +++-- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Windows/Debugger/DumpMemoryWindow.cpp b/Windows/Debugger/DumpMemoryWindow.cpp index 7fcc420157..1fdb549486 100644 --- a/Windows/Debugger/DumpMemoryWindow.cpp +++ b/Windows/Debugger/DumpMemoryWindow.cpp @@ -1,11 +1,28 @@ -#include "DumpMemoryWindow.h" -#include "../resource.h" -#include -#include "Core/MemMap.h" -#include "Windows/W32Util/ShellUtil.h" +#include +#include +#include "util/text/utf8.h" #include "Core/Core.h" +#include "Core/MemMap.h" +#include "Windows/Debugger/DumpMemoryWindow.h" +#include "Windows/resource.h" +#include "Windows/W32Util/ShellUtil.h" DumpMemoryWindow* DumpMemoryWindow::bp; + +void DumpMemoryWindow::HandleBrowseClick(HWND hwnd) { + std::wstring buffer; + HWND filenameWnd = GetDlgItem(hwnd, IDC_DUMP_FILENAME); + buffer.resize(GetWindowTextLengthW(filenameWnd) + 1); + GetWindowTextW(filenameWnd, &buffer[0], (int)buffer.size()); + std::string fn = ConvertWStringToUTF8(buffer); + + bool result = W32Util::BrowseForFileName(false, hwnd, L"Select filename", NULL, NULL, NULL, fn); + if (result) { + filenameChosen_ = true; + buffer = ConvertUTF8ToWString(fn); + SetWindowTextW(filenameWnd, buffer.c_str()); + } +} INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { @@ -53,16 +70,7 @@ INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, switch (HIWORD(wParam)) { case BN_CLICKED: - char str[MAX_PATH]; - GetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_FILENAME),str,MAX_PATH); - std::string fn = str; - - bool result = W32Util::BrowseForFileName(false, hwnd, L"Select filename", NULL,NULL,NULL,fn); - if (result) - { - bp->filenameChosen = true; - SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_FILENAME),fn.c_str()); - } + bp->HandleBrowseClick(hwnd); break; } break; @@ -73,10 +81,10 @@ INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, if (!PSP_IsInited()) break; - FILE* output = fopen(bp->fileName,"wb"); + FILE *output = _wfopen(bp->fileName_.c_str(), L"wb"); if (output == NULL) { char errorMessage[2048]; - snprintf(errorMessage, sizeof(errorMessage), "Could not open file \"%s\".",bp->fileName); + snprintf(errorMessage, sizeof(errorMessage), "Could not open file \"%S\".", bp->fileName_.c_str()); MessageBoxA(hwnd,errorMessage,"Error",MB_OK); break; } @@ -140,8 +148,10 @@ bool DumpMemoryWindow::fetchDialogData(HWND hwnd) } // get filename - GetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_FILENAME),fileName,MAX_PATH); - if (strlen(fileName) == 0) return false; + fileName_.resize(GetWindowTextLengthW(GetDlgItem(hwnd, IDC_DUMP_FILENAME)) + 1); + GetWindowTextW(GetDlgItem(hwnd, IDC_DUMP_FILENAME), &fileName_[0], (int)fileName_.size()); + if (fileName_.size() == 0) + return false; // now check if data makes sense... bool invalidSize = false; @@ -190,7 +200,7 @@ void DumpMemoryWindow::changeMode(HWND hwnd, Mode newMode) EnableWindow(GetDlgItem(hwnd,IDC_DUMP_STARTADDRESS),TRUE); EnableWindow(GetDlgItem(hwnd,IDC_DUMP_SIZE),TRUE); - if (filenameChosen == false) + if (filenameChosen_ == false) SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_FILENAME),"Custom.dump"); } else { u32 start, size; @@ -223,7 +233,7 @@ void DumpMemoryWindow::changeMode(HWND hwnd, Mode newMode) SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_SIZE),buffer); EnableWindow(GetDlgItem(hwnd,IDC_DUMP_SIZE),FALSE); - if (filenameChosen == false) + if (filenameChosen_ == false) SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_FILENAME),defaultFileName); } } diff --git a/Windows/Debugger/DumpMemoryWindow.h b/Windows/Debugger/DumpMemoryWindow.h index 6c808552d3..f9a94151a6 100644 --- a/Windows/Debugger/DumpMemoryWindow.h +++ b/Windows/Debugger/DumpMemoryWindow.h @@ -9,21 +9,23 @@ class DumpMemoryWindow HWND parentHwnd; DebugInterface* cpu; - bool filenameChosen; + bool filenameChosen_; Mode selectedMode; u32 start; u32 size; - char fileName[MAX_PATH]; + std::wstring fileName_; static DumpMemoryWindow* bp; void changeMode(HWND hwnd, Mode newMode); bool fetchDialogData(HWND hwnd); + void HandleBrowseClick(HWND hwnd); + public: DumpMemoryWindow(HWND parent, DebugInterface* cpu): cpu(cpu) { parentHwnd = parent; - filenameChosen = false; + filenameChosen_ = false; selectedMode = MODE_RAM; }; From 9ff95edb8922148058c27cdff3b7642ada050747 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 10:33:19 -0800 Subject: [PATCH 06/71] Windows: Allow longer paths in browse for folder. --- Windows/W32Util/ShellUtil.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/Windows/W32Util/ShellUtil.cpp b/Windows/W32Util/ShellUtil.cpp index 0a70a9d38b..29386bb993 100644 --- a/Windows/W32Util/ShellUtil.cpp +++ b/Windows/W32Util/ShellUtil.cpp @@ -31,14 +31,30 @@ namespace W32Util //info.pszDisplayName auto idList = SHBrowseForFolder(&info); + HMODULE shell32 = GetModuleHandle(L"shell32.dll"); + typedef BOOL (WINAPI *SHGetPathFromIDListEx_f)(PCIDLIST_ABSOLUTE pidl, PWSTR pszPath, DWORD cchPath, GPFIDL_FLAGS uOpts); + SHGetPathFromIDListEx_f SHGetPathFromIDListEx_= (SHGetPathFromIDListEx_f)GetProcAddress(shell32, "SHGetPathFromIDListEx"); + + std::string result; + if (SHGetPathFromIDListEx_) { + std::wstring temp; + do { + // Assume it's failing if it goes on too long. + if (temp.size() > 32768 * 10) { + temp.clear(); + break; + } + temp.resize(temp.size() + MAX_PATH); + } while (SHGetPathFromIDListEx_(idList, &temp[0], (DWORD)temp.size(), GPFIDL_DEFAULT) == 0); + result = ConvertWStringToUTF8(temp); + } else { + wchar_t temp[MAX_PATH]{}; + SHGetPathFromIDList(idList, temp); + result = ConvertWStringToUTF8(temp); + } - wchar_t temp[MAX_PATH]; - SHGetPathFromIDList(idList, temp); CoTaskMemFree(idList); - if (wcslen(temp)) - return ConvertWStringToUTF8(temp); - else - return ""; + return result; } //--------------------------------------------------------------------------------------------------- From 5b5ded058c92c51ef59b34d7b6cd3d2652b15475 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 10:57:23 -0800 Subject: [PATCH 07/71] Windows: Support long My Documents paths. --- Core/System.cpp | 11 +++++++---- UI/GameSettingsScreen.cpp | 24 +++++++++--------------- Windows/W32Util/ShellUtil.cpp | 24 +++++++++++++++++++++++- Windows/W32Util/ShellUtil.h | 4 +++- Windows/main.cpp | 1 + headless/Headless.cpp | 1 + 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/Core/System.cpp b/Core/System.cpp index f36cbcec83..5ccd0ddd55 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -23,6 +23,9 @@ #include #include #include +#if !PPSSPP_PLATFORM(UWP) +#include "Windows/W32Util/ShellUtil.h" +#endif #endif #include @@ -560,14 +563,14 @@ void InitSysDirectories() { // We set g_Config.memStickDirectory outside. #else - wchar_t myDocumentsPath[MAX_PATH]; - const HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, myDocumentsPath); - const std::string myDocsPath = ConvertWStringToUTF8(myDocumentsPath) + "/PPSSPP/"; + // Caller sets this to the Documents folder. + const std::string rootMyDocsPath = g_Config.internalDataDirectory; + const std::string myDocsPath = rootMyDocsPath + "/PPSSPP/"; const std::string installedFile = path + "installed.txt"; const bool installed = File::Exists(installedFile); // If installed.txt exists(and we can determine the Documents directory) - if (installed && (result == S_OK)) { + if (installed && rootMyDocsPath.size() > 0) { #if defined(_WIN32) && defined(__MINGW32__) std::ifstream inputFile(installedFile); #else diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index ae317198d6..e8c39d0a6c 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -743,13 +743,12 @@ void GameSettingsScreen::CreateViews() { SavePathInOtherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Save path in installed.txt", "Save path in installed.txt"))); SavePathInOtherChoice->SetEnabled(false); SavePathInOtherChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathOther); - wchar_t myDocumentsPath[MAX_PATH]; - const HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, myDocumentsPath); + const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0; const std::string PPSSPPpath = File::GetExeDirectory(); const std::string installedFile = PPSSPPpath + "installed.txt"; installed_ = File::Exists(installedFile); otherinstalled_ = false; - if (!installed_ && result == S_OK) { + if (!installed_ && myDocsExists) { if (File::CreateEmptyFile(PPSSPPpath + "installedTEMP.txt")) { // Disable the setting whether cannot create & delete file if (!(File::Delete(PPSSPPpath + "installedTEMP.txt"))) @@ -759,7 +758,7 @@ void GameSettingsScreen::CreateViews() { } else SavePathInMyDocumentChoice->SetEnabled(false); } else { - if (installed_ && (result == S_OK)) { + if (installed_ && myDocsExists) { #ifdef _MSC_VER std::ifstream inputFile(ConvertUTF8ToWString(installedFile)); #else @@ -779,8 +778,9 @@ void GameSettingsScreen::CreateViews() { } } inputFile.close(); - } else if (result != S_OK) + } else if (!myDocsExists) { SavePathInMyDocumentChoice->SetEnabled(false); + } } #endif @@ -925,26 +925,20 @@ UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) { File::Delete(PPSSPPpath + "installed.txt"); File::CreateEmptyFile(PPSSPPpath + "installed.txt"); otherinstalled_ = false; - wchar_t myDocumentsPath[MAX_PATH]; - const HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, myDocumentsPath); - const std::string myDocsPath = ConvertWStringToUTF8(myDocumentsPath) + "/PPSSPP/"; + const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; g_Config.memStickDirectory = myDocsPath; - } - else if (installed_) { + } else if (installed_) { File::Delete(PPSSPPpath + "installed.txt"); installed_ = false; g_Config.memStickDirectory = PPSSPPpath + "memstick/"; - } - else { + } else { std::ofstream myfile; myfile.open(PPSSPPpath + "installed.txt"); if (myfile.is_open()){ myfile.close(); } - wchar_t myDocumentsPath[MAX_PATH]; - const HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, myDocumentsPath); - const std::string myDocsPath = ConvertWStringToUTF8(myDocumentsPath) + "/PPSSPP/"; + const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; g_Config.memStickDirectory = myDocsPath; installed_ = true; } diff --git a/Windows/W32Util/ShellUtil.cpp b/Windows/W32Util/ShellUtil.cpp index 29386bb993..39938b3fc0 100644 --- a/Windows/W32Util/ShellUtil.cpp +++ b/Windows/W32Util/ShellUtil.cpp @@ -33,7 +33,7 @@ namespace W32Util auto idList = SHBrowseForFolder(&info); HMODULE shell32 = GetModuleHandle(L"shell32.dll"); typedef BOOL (WINAPI *SHGetPathFromIDListEx_f)(PCIDLIST_ABSOLUTE pidl, PWSTR pszPath, DWORD cchPath, GPFIDL_FLAGS uOpts); - SHGetPathFromIDListEx_f SHGetPathFromIDListEx_= (SHGetPathFromIDListEx_f)GetProcAddress(shell32, "SHGetPathFromIDListEx"); + SHGetPathFromIDListEx_f SHGetPathFromIDListEx_ = (SHGetPathFromIDListEx_f)GetProcAddress(shell32, "SHGetPathFromIDListEx"); std::string result; if (SHGetPathFromIDListEx_) { @@ -152,6 +152,28 @@ namespace W32Util return files; } + std::string UserDocumentsPath() { + std::string result; + HMODULE shell32 = GetModuleHandle(L"shell32.dll"); + typedef HRESULT(WINAPI *SHGetKnownFolderPath_f)(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath); + SHGetKnownFolderPath_f SHGetKnownFolderPath_ = (SHGetKnownFolderPath_f)GetProcAddress(shell32, "SHGetKnownFolderPath"); + if (SHGetKnownFolderPath_) { + PWSTR path = nullptr; + if (SHGetKnownFolderPath_(FOLDERID_Documents, 0, nullptr, &path) == S_OK) { + result = ConvertWStringToUTF8(path); + } + if (path) + CoTaskMemFree(path); + } else { + wchar_t path[MAX_PATH]; + if (SHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, path) == S_OK) { + result = ConvertWStringToUTF8(path); + } + } + + return result; + } + AsyncBrowseDialog::AsyncBrowseDialog(HWND parent, UINT completeMsg, std::wstring title) : type_(DIR), parent_(parent), completeMsg_(completeMsg), title_(title), complete_(false), result_(false) { thread_ = new std::thread(std::bind(&AsyncBrowseDialog::Execute, this)); diff --git a/Windows/W32Util/ShellUtil.h b/Windows/W32Util/ShellUtil.h index da5b83ee52..8231d8c31c 100644 --- a/Windows/W32Util/ShellUtil.h +++ b/Windows/W32Util/ShellUtil.h @@ -14,6 +14,8 @@ namespace W32Util std::vector BrowseForFileNameMultiSelect(bool _bLoad, HWND _hParent, const wchar_t*_pTitle, const wchar_t*_pInitialFolder,const wchar_t*_pFilter,const wchar_t*_pExtension); + std::string UserDocumentsPath(); + struct AsyncBrowseDialog { public: enum Type { @@ -49,4 +51,4 @@ namespace W32Util bool result_; std::string filename_; }; -} \ No newline at end of file +} diff --git a/Windows/main.cpp b/Windows/main.cpp index 24718559ed..5ceb76d849 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -493,6 +493,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // On Win32 it makes more sense to initialize the system directories here // because the next place it was called was in the EmuThread, and it's too late by then. + g_Config.internalDataDirectory = W32Util::UserDocumentsPath(); InitSysDirectories(); // Load config up here, because those changes below would be overwritten diff --git a/headless/Headless.cpp b/headless/Headless.cpp index ed8d7cf917..72c6620c6c 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -379,6 +379,7 @@ int main(int argc, const char* argv[]) g_Config.iAudioLatency = 1; #ifdef _WIN32 + g_Config.internalDataDirectory = ""; InitSysDirectories(); #endif From 61e481841969e4dfedd8f20618752ce345ff648f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 11:04:33 -0800 Subject: [PATCH 08/71] Windows: Allow long path for sys directory. Unlikely, though. --- ext/native/thin3d/d3dx9_loader.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/d3dx9_loader.cpp b/ext/native/thin3d/d3dx9_loader.cpp index d7e5fa807f..f269ecbe04 100644 --- a/ext/native/thin3d/d3dx9_loader.cpp +++ b/ext/native/thin3d/d3dx9_loader.cpp @@ -52,15 +52,18 @@ bool checkDllExistsW(const WCHAR* fname, bool bIncludeExeDir) { return ::GetFileAttributesW(fname) != DWORD(-1); } -bool checkExistsDllW(const WCHAR* sDllFilename) { - std::wstring sfullpath; - WCHAR bufsysdir[MAX_PATH]; - - if (!::GetSystemDirectoryW(bufsysdir, MAX_PATH)) { +bool checkExistsDllW(const WCHAR *sDllFilename) { + std::wstring sfullpath(MAX_PATH, '\0'); + size_t sz = ::GetSystemDirectoryW(&sfullpath[0], (UINT)sfullpath.size()); + if (sz >= sfullpath.size()) { + sfullpath.resize(sz); + sz = ::GetSystemDirectoryW(&sfullpath[0], (UINT)sfullpath.size()); + } + sfullpath.resize(sz); + if (sz == 0) { throw (std::runtime_error("system error")); } - sfullpath = bufsysdir; sfullpath += L'\\'; sfullpath += sDllFilename; From dc26fb8ef735749c952ec5e0d1aa2cbf3aed8ab8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 11:06:12 -0800 Subject: [PATCH 09/71] Windows: Make TEMP dir detection long path aware. --- UI/MainScreen.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 86afd47b5e..7fccb89a58 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -96,8 +96,14 @@ static bool IsTempPath(const std::string &str) { // Normalize slashes. item = ReplaceAll(str, "/", "\\"); - wchar_t tempPath[MAX_PATH]; - GetTempPath(MAX_PATH, tempPath); + std::wstring tempPath(MAX_PATH, '\0'); + size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]); + if (sz >= tempPath.size()) { + tempPath.resize(sz); + sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]); + } + // Need to resize off the null terminator either way. + tempPath.resize(sz); if (testPath(ConvertWStringToUTF8(tempPath))) return true; #endif From 9b745d473071dbe02908ad3197b69fb777597e4f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 11:15:34 -0800 Subject: [PATCH 10/71] Windows: Mark PPSSPP as long path aware. Fixes #12503, but must be enabled. --- Windows/PPSSPP.manifest | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Windows/PPSSPP.manifest b/Windows/PPSSPP.manifest index 54de138d8b..3be621bb0a 100644 --- a/Windows/PPSSPP.manifest +++ b/Windows/PPSSPP.manifest @@ -32,7 +32,8 @@ - True + true + true From 91f491a0a849f4216b87a1d51e3a9ce81a6efcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s?= Date: Sun, 12 Jan 2020 10:11:41 +0100 Subject: [PATCH 11/71] Upload new Gold icon for Windows Includes 256x256 size. --- Windows/ppsspp_gold.ico | Bin 82726 -> 108422 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Windows/ppsspp_gold.ico b/Windows/ppsspp_gold.ico index 999b4fa0ac16ba442d33c2e68c20fd3ebd8f1cfb..36e3c7bd642a1ed9825e0b803617c6d458452058 100644 GIT binary patch delta 26046 zcmX_n1ymJZ)bHF&UHZ~UUP2nAL+Szog0z50r<63(F!$0Wpduh0N=hl+rJ%H=bazVk z$Nzory;*D4nRC|K=bV|n&(7cEhhv2gV%q=!1i%8|@c&u_V223+`TzhR5dYgRydQ75 zALHl$zx{^*@QMNeP^kaybHE?~k>owW{q-NcmNfu~DnI~^`~E%Y{mB0r|Im&tP#G?b z`Jt9l^*bS!Dg6H7)d%Se*!RB_k`HU`1R?d1?^;N4NOA@-1QtRlQ%fiVd4L5*X=x}E z;?dyU&qAp3^oh=Yll^zYuVhma(; zMBgO8=%{cyHqBajPB=}0leqc}e{DOPMrsw&q(-W^z^^{Ro+O(?2e*wYfs(PEJsy>B zyha{3eJrI-l9C?lVCGr+(HsU!l_~yFG?jntVrgA zJ026E*>TI4dC()L5xZ+6{6##(AH7T#3bcclx*nAj|3oX#g#7>#H*%E1=Al3@v_gq^ zVo#tQJmpGc5T_YAN-reGQd@q&289cnAd9GQ0E1TtcOo&w|7~1abHR$H1th8a)Y=Vh zpdS``{?+1iP5H@z?Y$Ok?4=9!d02c=)DEo(#{$MD6`@%i=bJ=+%S8v*mvA*scjbQH zaL&-S5qc_)e<_qE3htv=2ud7t2uj4ORf5rKf4+qfqnr52mu|EwTH#?gKh0yK`J0#s zi#Df%yg@5Q%kPUc=4jOY)Ef+Fc95;ysv~F;6GogKgBys)Kg(yC+LrU4G;w!;Rw4*5 zU;32E0j|mFKnXJd%-`+}?+N5&x#32~rJ{aIQRPQTq%xsr#B zw!y|j<#yj6xGBavN?+8GU)n6Vn0od%#axyJyteo!LDLxiuXb(CL5GG`XDLU=A8ChO z+_AWtiFBi-xFo{eq;V5)SKFeT0TlUb)vLMk64#%9<1P76a1`v=1VX5uNs)KN#t76j!A3xO=6pp3`(FP)Zv_X4G>%bZ(hG{jDGQiD&!T>nHGr}RD9NHdMV2Vf~c6ZMe=)7n2fQ+#)CA3(VisBx~Efgir#4Iw#TAX-=D-YNZin#=4UgTA-(kn@;D6ZYiv>bwT_}I>H$j~0)^>SC=yUr11 zOFWcb5|%jxsYw;ItZzIcP)A%xRrd2D`p|m-dVsEyj5)a*4B*}~spKW!WmY--E0&oce$ukhX z(kz5yVen<`G*!<3-2(gNnX$poj;`9nA2`9TWyYhzU2;^x9)u`ts5-6!;e@U2z>&tJ zao_?M9lELI`8%@~*_j_!=4Zzffxdq{7&IxJf%=8WM19mMkpU$#{@cBpE*p!-s*$2& zG+MDymf!G~I{%i(G1o`4ttxoE3 z=s!3&AH;(QGlf2tS;2lfJ1geNQ_ zh`5)C0V4{20Koqq^F=1$boeuo@8SFyX6zH9`4jgy^0gmvDrw}z?P>wKK0J7;H&;d~ zLuMc`f>gAWVy&WGVXyT=*J567b97^0PKWa0y-9;snZr2!Jt;U1}T{Zp(09H_#?z z;$&mSMFfQmKQ-y=qDjK4=vFpPGW^aML&wb4b^F{|!LEMT)G_4ff#FN@cn@`n;n)t`kT--7-p82K7yMe6+^E5cuq@Zh&ET}t&0ot;#E^fu z|3Q=O(QrotV2GtEC>JX`=-6sWKU;!1=5cK%f1Q;(2q<#$vruoaxuKn&hfu&^>Ke|H!Hg^$(WjHh} zz@aCkeZRR1BM9&`5({WE3F2XU{DeU4Dq(9L#vup7evCi{@Ll<4N$t!A`-rqOWHUTq z39|GYGT1$7*y7lm&QOkQ%r~x+(-B-Oq)!<)R@}@F9luxN={Htn?vJ1RxzZ<$_?Cx@ zA0s6$FqYPtHivG$Q!)R6gJD?|$CQc**UpkxANK_YN-;u7hr!y97<0c+Gx-?EI!c%m zL>`<*jsJKM66?oCoFIWHv-=a$;8^ zieiVH3AB8kq*H;cq_stZ&6h5~H*}y8b?^ z+vW5Hdhs^w2fruJmcM$1b$w{^RbTHM{E&f-Hyydnq;E`Q$vrH~P#lF<&%3q$JcHJp z&t&_#_Fdx+2D3WSnjKz@)gkE_hdkXJZ!_h=|MU;~1Y$)*Lx}S=puMJ2)H_gnHQy%i!YT@_Nxf9u-KWxjucV7A@gr`Y$NAro)x@oF?sJt3(w*5wf}=xhh3|<~jYPE0 z9=@K^XcGWj=gRS=qYQBb{%B5}*}j9j=F;kLyvfmOjQqunLzx8fG7mvf%_tn|z=LY2 zY?B$r`leZ5{iTBQG)&|V-WMkfW+R#4nR6j(+lbP6T|@=0olz-%p#LFUBzONJV%x8~ zgQVFWg3?}*_TmgTy@MvT7n8q+Ac?Wi)Htp`C|=M#nQ=7AxrpgJ!Oy8FRj{6>(lUJU zQzpAdt}yzu*f3alXGnZ(ak}w0(G(l_I7k?kL=$IVOQN74#AC*d4Vjx0gbR_zjsipU+KG zv0}8X4c@)v(g({J_HWmu)K#sf+?jp+Tlfw}u_rG#^+D7Q4wUvJPfU%$A`;U}{#mCl zKi+hfY=>jyAZ|rRFfV?#as%Xd1~^@zq@N%>)v{lHesYrWOkmh_0UJig z#U}*~+nB^fLW#`@@qyFAgU;-Cx_V(t_{>hRWu*2a&qE;6Jq1pR!uVPv-dKLaJ;gc) z7H-pM@RfD82oErshkz?%@4Z&xgOi>?)Whfmp8ONJ`HX>PgY;Rv1aR}rER1uso^9{~ z8-Ia{*B^Y*35m`~^E`(2N7Y;D$VW7>-l$g`PJ3?Vf{(c{Us`A18)Vf3GCAa@L#8Bq znm^&kzmxzJb`{U)^S~QN;7_B^n3_Jyq%9O=vVrp~)&l0`6~MT&0zdrcza0pG+$J?Y znn!1tZT{yYdL{svr&ojB24rGwPz-#)j~v#8%C6*@p?Qo$bR z#c@^`hP?zTk_{lg^Bel4knbsm93hAp)%HDchBhd0PtHmV_rRFTO2z_0c|c%DJpeCG zlksi=UaHzo3G689x24Tn+8T7Mc`_#O;3FIvP*xir`}G`}?HC64=9l3Igaa5INBS`! z-$CtZZBFywDI(!Sj1fir^LSNAM0?@=DI1KfQ*E#A--G}|h={=PuaJe;pCH-7vYgCA zq{k~E`R9#TK%>@G+Dj0!rCCye7>W4e8dDt$(P9gB-=xiLpjRP5Hxt61rn0!1dZm?E zzXX1L3>E>lF!n7K9B4OFPevE{S2`b`_H#Y2*!bh zBCZq+-ZKCJ6@o^2&d4<%z-w0}C9wk!aUVIQ;exqCLQf>Y=a2=+fiDrvOg4CU?9t-V z3D|A_bnfx)sqfO)FKL)i0fy|k2t@`QBGn6sI-2e9eR}A6YvF3_&&-zWBhb&6``$K135EwC_4$e0Y}v-`YxVWM0f>4ltylu%=OJMV zsw1EQQ7veAAL){%BSPh#k>NEb>*EdRPrmzw=d19WTs=1&6S0qXFEj`Yn3ODjO#Cb{p2~9x6{1?`UduQ^ZnKW7$8YaR#hvxa5J$q( z4b}E~JMYi`PPz$!yA?g^>2ml8Ju{ag(Flj)9Z1L55!C%RMfgmnPt#SK7nmfJLSZ8> zf*wxETYRmIq5im~ppVpQqD>$IIM8RBR95HvnVqD^v8wHVVgC9ux6Q3eb1EYOrEOuV zX(-i93qht0ov;*0SPGFj!|#Xq~jijK$JLoI_c9;1KJ-n2 zE(98&k^TdCg^nu3z|e6Q0-RghwfX)zx75N(gR#h|i@UZZm{$TXu%PELaLnj!evkqw z^qkh_bS+$=R?5M7(zbO$CjT>3Fj?A$C~OtF1I-b`hv^iHS}ykdQ%kL=o`4{;Z9Me* zz^llf!ul0XDJOobYK_!G6z)s!>md04V_LFaJS}^wH|PS^>g}3o5SwYihe~`H!}Pz< zGdmroNR(T@wqC_peMs$l9pnmD!WT^}und3%ESS_knWQXVct;{q4F_np=XlSUGtMg! z_-a5<>iCG}oD4ud%efi(@=FI;`NgdjuwkHf_E_6~S^Jk;%2U8CzE^V9GI%a1r9L9| zdx#*U_4=ooHvsVcNrRH=dXNWo|_-vo4Wm+;Pm$!YLR>1l@&@1a)cj;{xxrsE}m^(ctT8_ z6&OOTacn&!LvI}Y3uyVt2g|Af5YOOt#}@w`1-%E+AbHQ`;(jU1XajG7B}X*gox=Vi z37~VFKUxb(eIXn;)Mq8E#jNu{XMWgWyXUwO=^wNy4)BUBhFf20T z2NMHb?J3Y~yxDXG3kcGS4h2Oz;hI}SiHQIkAA=vlfA9N~<*LGjoq9Hf(zXx4fsFDT zuCRj=wvNGmU-E8=C+RKRduX?sLWzB>M}BRXIDYafP}5^;Oce8fQ9H$~F>@aj)rW** zpmzxkbeGgy#T8n1EBtYGLh|qY3ZoNGu2=-Uj8Et?UXX^a7_)~rF;l`ljPU|Z{h^X7qykbABbA6TZOQE+i0b0i4)hF^An zRL{e|vsn#e8wVZVEs`OA7NzXwuVAMqeYIVISQ?=%aHAikwCGgGxx-)o4PJ9s28{#l%|jzzf17ysr1TSu&8 z-lx$x8~^SFToN962oSdPO{3kQOc6tdwFE60+=5{Nr*%vbMkEfiNh7xUuiKBnJJK|= z(AL>&vDs7qGa}<|59*BO`bTZ=#O$&R?fwF&Zi^|KPX-`RaRnevszt;AJH~fN>D1br z*o1Oyf5k@-^_W+eHee~5Uq9;*W~!NUqaFsXy`k1#`k`KLoGR1`4c#+3))R%ui83tz zBtj9+){)&O6a3mWF(Ip;G%KHsP`%k>*P}S6oyfDfV(xjDIs1ZtvH>f4W$@SJ#k%oz zWe$a07Z`D!KXX)zzo3~C+>QH71|3nmuHd@oKbD-b&6puuf=*o2Xnv*OG>Ez!$ z=gIjB%G9KrT@}GC@D7?OoO1JOYToYh`_+Kr{OylA_T=qv{x~SN(?G8rn`P`3$qx-Y zMnDBE&_+$dK_4J#>VM;yP6e}4f1z2%MRM*{_QY{kkVB$kx6vUqVA_4p@4AyqO4L>Y zr1?I2O8|g}!#*i%3A5ckI6IlQSI6?bxvk9Uhl+ZQud(=_wS50BLq>GDDnHtKLN6oj z$@7`{5gYtZbvBnC5%Wwg@~};6tCC=+?^+^lL`0o?ersN zuuuL?@MaeEzDsQ3%AO?gT{p)!=lrJ}#J24Trr#&U)2KoP;g(Y3@ZUf#+B-mhPcN2!+NF0fOnN>Q6F~Yo{N)P^7RF;s)pzGTF})yYeN1= zD6QKRq=||WchcUXfUR`|vz^`kx}-~^!=XcS*G$&(XhQG&I6=U5t)9QQBtNw{l*DK=(ndlFodNF$EgkmE#BbNr~$3(NsN8~Czb#@?jibFPAW z{~ikz`Ku{mE6~4fYWGxtkhfTN zD_SL^5)%%ED zG7>KohFp+c>2Y)RaZzACv+)h3oe%fSkG$c=i14IK!l!%5 zq%MR3;$p5d4~4dA+9msqM*5sk#R;O|(d?Ta>W`M+6cBb72%k88sfcf&X!7T0F5M8&l12D$|f(CIB`ExXEJfmXPMkK&~z*cINgtY z%;&u|SP~DfLYWI?4n)&iiik?FcNwj2C(2CH?&}$&YxfuRWd9YJ2K`^gM+N@Jr`ols zN9owVCE;@dIG$Hm6~344G)Ru2*r$aeyu?E@`9Z=56SQTb23D`MQ>5_#9ozs7YjF02 zy*+TwBE@j!hj3%rY{5=ZjrJn!_U4EDjMv}ilcA$u$_v|tMX`eII}`4%Mb2;9NYlqn z1{Eu)+FtULCvHdPxXfx}agL^s_|DxiB%7S@tUag#8~4jcM_=>u^7370d^SH4n-4g; zz-3P#^7}i?;kUuuXGP8_c6jlJ#T>lP>IU2z^ptKRCIX<1-$?X7{pL_+34_}?@>2xR z6T?_vPV4ymOT8h?=2`!ZksyuOk$Cq8*qE+0UkGh4^pxc1omvhg@EfAc&u^dK=*+W~ z4PQ@wjimmiLK8I(JhLH`3gTqT3WV>uM^ex55~H;yn=vfT5?OZU(KX>Sq#(6p+QH_% zT2}Uaheg)k|CH4o_v02(M7~eQXS>cMUZWPOAnH5rcwTq8nPoQmRx&2W0TmFEC5ETl zqx{(dQb)fZqWUIjrF%RP&un|Z%S}vXsBVI|Nnh;Z(0f~qrA4bSoM&Y07X#eq`?!*T zh3so+=+)ql&=#yhPgvoKFQ}^%+{{d;reERt_tlK&=xj>-=sULuv%&wE#pmlOlH+)~ z6+^R)mdqdQql%xn&mOtdU)!f&Zw9=6hPbs%ef1-nLXm=kqSf|YsWK_=pGS1sHQCPN zw~KGf73@ul@TH>6pr^y$s7I1`>He^)r}(<_4i){H>xp5L+&-upwnDJ@{WzgP6Q9o* z(?r<~$7O(f8M&HQWrZ#4_ABGy+&ngO!%X~-xy}Ro%Ey8;{@YrCa!r>rJW~A2od&;O z74Otl8HI~wg)dHA<9YkjQMa-?Ti?>m_;&=JM?`HhCT}&`4d+HLc1Fx|jHn*5{y!`M zha}PZQ|lDcuOT3*b7+$XQ3tJA2-}rqd z7Vw^26l>TES?KuQRzLmwF#`thv=It4gni|94QX~0QX=4d`|i!h*xX!p@u1l6Xk3lv zvyAbR+>GLIdJ)1p$3Odra={|mY<(a8CRyxM;KF5_@NGeLhG<9XFc0s3Wk&#C**GeO z^?t4|Uy z@eBx+$A67olyhj!LyW`ew`Wj_T|Y0 zG^_RI(+&?JrPix~&fOoTbboer38TQORHe_YLAjYX2mVv;*0t-8ntap5Z&%1PQ=Cfb zVwam(kPM&4UiE$Hy%kUGOguNIiePyNqvI{-@^Y%Y zd+;;OuTRz65u*)+=(VkMLyH0#+F*4Vsi4CVpUW#7Sn9d`z5Au6(oyT&J{%F>T?&op z0Mn&KN$+}8{1>A_&q}A`K7ABZ1r^t%K_b9xCCq8_sO%-PW`1PIqhS68I@e5#;Sp`r zRJKxTHm4lF(SvNI&Ujo5!xEGTvB95AOU&TKLh^z&$g8_Q0Y5owA7!PPf3cys-#HBY zjhSk%z;wMy8oT|XP3St3<)Il{7OAvu96_P{N8k{2-c1H=W>kk=2Y!SqepW%usvXK~g2UlQvcXGX!U zx@6JZkAcxSs)STP4;m_u#D%7Q#*p`6M_i%W46~hh7tHW6MWNC2@2|ZWkPueIXII8X zH~+xPYll5WYzD+ODqDBOl&9COA77kzFwCE$y(benu024x-q`I>2<|TB6p`NN89nyi zr-ieAou7-8)t!snFybucsFav!#SvrIms!yr> z{t_25A*u*rUvZ#mAZmz`p5rv!S+y}3X9y-QsDitJi5XoiLP>YjkEx=A!Dg&J6=lU) z1@f4$ezOwj+iu%G4Lu-}zlWN@1}rC~Z|=QCRHq~moCizah$Hp37Bj&QS>S*^YG4X8 z@|e;TlVzl1z|m+rWpzicx_&#zn$MGv6C`USd!XJhx!(^4^*jpvax0JfNz$2LOqJYD zMR`v=rk7pQ#5`DDJ8D0vHKNR@QpH|AAj!5f|Ya70MaHj%djLtJTAUl5f^lIebBrSz0BE~AI@V?Yw|q7>VyfH zYNBvh?t8Q{NJKw)p^9G@m8diK`Mp}eJKQM<9E=$+658|OJWvQ8e8hf!Q~AcNpR;U9 zh{&x;h4T$*=NX(ybAEl_A6i(dTfWX-yEWhc+RsFiYoql!ZQB#nfp+hcw`Iuu5G;z5 zGk89Rf-4!^9_d2M7dv2+HOS0QvlBKE{YPEtZ7C2N+~Fw;Xjvr z3D$a_`XqOj+Bq6HZsaJFz8W(RvOVa8AhmxrR8PAep~WIvp220T!c2k*jKZlHIFIi- zFHR++n|F}ba=<0ja4a%rdyrVE>hM-iwId9+lH#g{&-N0v%9{p;~8IM4pL$$=)g)Cm51wipNg z?zM_|UVE7MofMpHzvZU%IAZ#iVa`IHlm;V`tps?v-u+jM~XlD7u|^+JKa}wqBf!Da@|p|y4(;1!7cB-tr*p(X?7`g^cJB>>yUUu zmU$0JfL4`4w_4xg`Os!#5-3+-Ap9=0+7yCWc2n`5ZKXCT$V$wF!qj>(D#)|pwTyvl zXb#@PrJk9d$0WhcfT%a4_&;p0^Q9JKSPy$H35RWMS5m?(6jkfXBp&@=eYW`ZmQ}af zO6;u!#^7)z8FwB2qsLR@|4zjgZE~z1@O$9Dj{^sA%kOMT1-IKOEpxhm5o#VY$#E2W z7na{$wui@)m@NnmAIEcsu@p?sPGqFsiP}^Fp-?r{(B12U8x0H(LI9+|e`7pEZugBb zbrf%`l_XIM^*2vDsaOM6Qwr2HK{1Qp^fUa$ecG?>j3BVX`18sD7H=&F(e}e7`)T@Dnd)?m~11c4UfG)P}^! z)|=qUS(hd3dtl7Q^I%FdrOj=075mmWaqos7(ZMskQ` z+3Y-3gfXWSPZnjt?(c$|% zxPvf&Mt2LyO;^XmQiwja?&_4dU%%UQP@u}aw740 zPj6_hJUfv45eOh$$H?A#5I5U*zoe~kECymj7k#6$Yt za2MLCc-P=zs|I{en4DU)bhOoyjjz<=__f!x*5NYa2kLZ^>YBl0gx*tpy8J7^uH^$m zByGI1EPOQ|U=SNoVh&5c2$B0#C|gJn%;2w11_bkK{XUnX0!KV1sBZTQYATMB$<$dvxJBows=)haoD zajK7^n@5DYp={3RX&iS!&^)5GD0 zwe^{Xa;m9snzF*}YgmsO;K#d9Irc|$jHA%tvz{jmf^*y-Fo5hKy@~ zrhfH+j`uXh3IBpOzB5->DpmTOvV3SnME&->iT*Q2h#DWV59+f;#xo2&&<)KmTDUJ8 zB+?OFP2PoQ1<}A(bGS&!e?2KIf+c3;rrq}C$X+kxo1Op?a}e)aWaKIIxhaVHynwYd zPqlvM`wTyx8u|BA+NN^2n+SN+xQaH*ePv!+7_R=ihx;v~wiJbgFk$}_=uaM07%dzYUa5NW zg5}$4U=Nz!JWdc6_JN7$F)T85)*^lq{YQ{`37T`3&38fLJ%8|E-5C6>)5qb)qlm6c875|B=oC;Loq*9N2J&3a%g+5U z5)%QwL|j)tRi>#UB!QuHb#?(|O<`}F@9cFo*iv&B|ICyj&@x!WoYj*eWT=4jHf62u zCOF79V8vHc`Q+Q$l_W3ezAZsUwa8H)aNE4LdpF5F z9OyH-;2Z)EQy>c(>CxB!nCb8VhiN7F9HHetz5_Q_HATo&G^CQCY6CIbO6-J#ZH7Pj zTauxQ6w{DuBkbNVE{H>#%p;16hqAv+FYm%wRY_n_X|FagZGUl~Zgb~bWgd=1(5~LcXUv-K#RIv7OJhj%MW_=8X;?&hB0f^YDe|EH{w36TwExNywvrqP6fG zxQ9QYL3(&m?Ptl73;?UxfRa%+X;e7Tms)xL#_N{SY9uugBFD3P_Rt#JF9Sv|fHwX5 z=n3?TjYJ{n0WUPbbl}F$31-0kS%1P)798TaNELG&(YL-ijg)NN8KXoty+F3(i)#Ir z)7Ar`h(A7rJ?^Ep?>TyXzR?~RQ}(!;N6J6P#m&U(i8{-1QCpThO&yi&T#NJA+JZ7p z>|n2lWT*flku}IWH{1IO&9_j;Q8UN)#9bnEtWAVqS8I)x;}FdQU*I4Npm>}Z7WMw1 z?sbgmb@zL{9%clGiDI^wP_8t48&TWKE4DlO!4EQ(yfCf~nMINV#gTSn1)v^xLCUYG z_7COTt0d30Awe|0Zh?i^B99)!lF7^9O%gzQ3!`5X|gP& zt?V;bauwhXRGfSdMyi!UplSU?Y5l`;2cN+OEFCg8<8+XyL#iBX$s_rz81b+HPw5}O z`|ff{(1SMvXX|{1Bd1lxf3$vP5ID%Y{wzxaLgkpeLhWUAElVR3|JaI0E@>iw`p@h8 zZW8&{_wE+K8mRN3*Mn&FDru!s;j!iPeW08zd<$(^afJ_HF(_ zc<%9#E**N-dpxNDMxxF^69a3W18eU}?utAKLN~4TkN1yK&2qE&^HD9{5D`0lLPHD` z@~O7j?ZkL-1KVavQ64_FIDH2ybYJb$zWGm+szyAnN{mpPG3Kipk!+i*8giFW-8NI_ z)MtNW%IUf(f?CacV`ytd!-VP~pFnYdHMp%SM~YrHRF-{Ht~W@_7UUm%nsBZjsrMw1_I)#ma8u-<{Hhl?k-k$G zs`I;Ba6+7Y$Lf|?zV*4MSMp)QfsX0Kd8ik8gjRqb@YFwy`c({6{umy8V$C$ckK`Hg z`04*$1PP;lm>`mr1gMdVf{IKht^Ths_Vx9zrW4W^3z!s-lF0LOU*_jzx?j(yzBRjR zIx>sU;Cr*;@06z!%WCZ=38kMHP#ej?-JN`?RYXQpjkY6!7d9(Ycs!6tm$8l*7}8nM zX8D{%Fj~yz@^KTjCE^r1>a#dmG*r#*XUE;Tss-T%WLe4W+FbsN0);nxo$c z@|fqFsTBEJrrduoD|dGTTop==O)hZFZiwhN+bX^0_;Jsiz>B@a2k&F`t}$B zihpT91Po&B(~^Bmns&bz#EEucYzM@io3~oE^OR;KIx6 zNtg7@iu8LB*6t@lU1#9!PG6muU$J?2>lge9Woo;5a9Pk$=~3%lFG=#;;jK=Ww^y_y zeP+LC_AvRV#^X)a=}?b5A^i<+!IbZ^kqZ!AT*J?}4(8mtcS=u0o5P--(E2eccX*Z( ztFJ?lJd1OYU&_*f5$EwY0y47LL~>_2gdgVo_3N88NcZjXTD+}Giv&-OXvW_xy{7`J z*?3>H=sI)U2QsbVy6f?J{^<1_Uc$zzTNq>vX5LtSUt=byO zyVIX-9;AeB4&($YVDhDi?5k0jFXQ*IV~5q5nIcI(7%-k0(k{n&}Ka+9jn@m`EpXWlCPp?Os> zGf0SNFZFw^qB$`sA=JtonL>PUfv`j~C@9zs6xiDlHN6mCMbjqa>$r+G0syk*_Hz4u zC$a4HndsCvM$OMU5(bZ0s(ySKy+ifniy@o>8H-zKhIBLf$5^$LB~re(I^QacgoLDm zlqDiIMXKQ( zOP<5FhPRu8myg>$6YRuVj((_;c4wuRk*M@2mGtLs>x2j<%V2ZkHGAgznmVk!Q za0!jSGt#k~;$+=i>UFUPG8qve;yAcCITy#vO#Nt?uC=mcCu!ymiVMWg7sCu^R=V?q zlf@vX0^m>#3TVL6x}h6t=z4$X*~RFFWGjo6aqoM<*0!}Z{%LtI^W;UyJfl)T!K8tW z*-wOUU;P3`V-LMNseFWFOWrbAU7Y)>bG*h#DeH2N>gfPNxCD{-p4D?w;xMK)!|T=R z`8P-LxO(?REFE{ocS;F0CzU37(z(?_=_LwF;&1`tCmcX%0G!E;y~ms6t>^4bRxG&o z_Ri^rL(cNz*HxpvASA@1^zwlF*2CzzV^`1lGu%Q($DPWZPN6|gZckJ7 zKi2;qEaP)nWp+qieX70o`qR@MC@mX!YVrp0_TSSNUkpB4>jj=HQ*gg{Q5z7{*7Z-P zwpah%Pz0 zLRI4*A46t$gEMena=`Ynp3Hi)%cB|AT5yz26sAYMd?cQWKi|?6Kn+j=u@5XQ$uZN@ zWK;Ns=KJY0oxv9sAQhU?w;&T>6@k7n10?UPR2m)}D$^&_Gm|aGi;cW}b(KW##HgEo z5bvKbGdxjvW0eu+Jil41yd5^KCTD zvt5_xDjr;q=dnM<0$)}-cPF|J*&ZwV^!97(K|gl^ zz0c6+mOH^h@G3SqQ)5yXF*-?_>NX|9D09-eEI-k6ZQBL(CO;67NWYj|}CI9F*|y(!mS;Tzc~Mwsw)L6kXWJo*pKl^2~*Y);%-u z?J|i+gJ%3OC0D1H<_rJ*7_OM;vo^TeE)SXvIEtDG5Wqi#$iBlI$$~7f$8ove9;`{2 z9ZAuu{DB8$FfCcXG->hCn?3nx+j=R~S4;4n4c9jg*R1(EZG9zf+jS2%JmKBUAtzA^ z=m7z&Cd?tjcye#D7?05-18+5JvoVWNM{%Pu!Ql>mri69Usk@5V;@HY1%dZM9DD)f~ zFXQLaFaJewi_%8QvK+Zs--IPINI7q~XfOyxXju+^?*D#LuY!lvB6sS&6TOgf}i*M0xuIDX_vhIQKH*b8e zf>6n$df^;+acOyd#g8Dk6){tvnc>wCWWDu_ZRza#ErAQ2!BtTGp>#xsEb|Lcj$MK@ z>}OVc!trehBTvIo>eXE}M$vN}dr{WK?4XMQP1CxKrOM7{k8(ZdIq5%hIAj-z)HS_Q zcGMdy-hGx8xZGWwmGt^_6>$*NT7nBsB+%h8P}$8q0L+z`4|)soGu;omuGvR;VXDHB ztQFliJG0D__3+<`5nD7x`^~nXN=NlMbIcX}Y3+Py$IGxQyk6PH>%Mmi(WYwa``(5_ zD!C8gcQSg4%Qf_+Aa%fbY_0W6{RHC=2=?~}Nxeixb<5rnv)UVP)c(?9$xy$q9TThO zEuEV(KH1fzEoSgPY<%N{u1E}PDV;1Ht)7{9R19<4xepSCQ8Lsp+c2qWHf4>@M9QogyhEAPtftASEIl zt03Lovmhc!r!*+tNOvP5(hZW*-AL{J_WQigJAcjd%$=Eg&%Ni&=X35kXRdKxtUGmN z)NB^KuJ8?z))r+M_{^G@c#I0Xj!z&;f41(MWqvK4EI1B6Ew1BjE$}R?wB5MLe7TIY zIEe?5Ew)xz5WQ*=&!OHgqQm*A1)2u9%|bw|KG*En=@9NR9?{P`hW`&HfbM;9jKeWB zs$E)G;+Q>g+9}v0Kh0_SwAEW*X>5kNn@vC>pK5=)?Y6!Vy>nlPypy`^n@c+g0c_9k ztU-r#*Aj_if%ex+Wla?qf>EOhx=Z9$M1|5*Zbs9I8mC(z^fWN1DvL0o9CN&|51Pg#T@gq zvcFk8!ws1!!%`N8fSSX?qrgi?iv2Py@ME$JDg2$z=+H}FI>vvVGLmo4{!{sCE|V$Zdfj(4pJUP#2H*Xk>U$S+!CKN(hJOzR2pK_?+ng>bWrt z@&a}KtU*p+m~eiOi4S?O;RW81kAJ!q1n#QjFr(jRo1vJ3tjudf7KABO59qDq`LDa& zG)9A6nCsUZ|8^Mqf8BBlw3r5a`0xK(G5v;x!5a&8SRWG0a&mi0J_C2wBG@DM-Ei~s z7R;R}-r$QgVCmUc+L;*EmDKNk^xD)Ly7p0ciT76H#UC4+TK~s{*(xnwHWXX75;XWB z_2fa9R!1qV%lU=6{u#ryAy~(8X^x~n7&8x&b}BQ-^WdtVP?_voZTA;ofdlUm;j_C+ zVrI9-%Jwapqv*X~gRNH^r`}V^Q(8v4g>CM;JlI1_6AmW_pT9X8wtYty2=w2-_k@b( z6lh8PXoV7;_+WO|Q87N*&f1G^ZENflZ{wb!z#EjLO}l$3>R^XX>pA;T(r@1$uoQ_g``e+_yu_A0Mh zNLxz1te0ltl8kQOo_f87)HaEkTy6he?J$BmIq%X#`tBObD_^>l#1o%h9#xl4D*BAJ zzREe2TNJrR!q?lVmNLPaA>x2~^5O=9A!T_YTo)J+V!&_W|K}BmP0*&YV(`||5n4UM zWZ071?y$3aAz8X~+iE+Ip3O5KCs$+DGi=4OH(qBu;kjyiBnYIiV@(AFT+*2~G=dUt z2`U%N+HNs|{Vl7oInAtGCgmmT_3HA=IZlF56*hALX! z?_q`!D%x7}Sw85=SnF)Adqr!$*Xv)^jXM_BR6+b^V%N^r=#bI*IQQtG2S7^J2(g4h zrGV8~8--J|fc-K%>@Qo{L<&2SD!NALhS0(K>H7avpl+9+f5fQWHo$}#J6>Zzepd43 z^nq*85g(vj@}Twf?}QVL_Fr_(p`4ntaH*S1fcQ3tWUXL{E+rS6_W>g{QH?uVwa{1id(5qMW!yld0Aaz z$-Jix1|gDj#j|B zJGCo07D_a`Q*0Pl)@_so2c|pPkz?HlSYKmo6w-%6ySuv^`%qggnyrs=kKG(Q1y~Ki zq%WVU|6iNMs#%x#+N;WEsrA!@=2ek2n?Ju)2A+^pQ6Nr_e2fg`NgXn6RIGZw z=5y|3eEV_ANM>)rHanN>Zi)HVE$xpbXy)AYcB}1Jv*()#pG0{@#bNAFx~SRRNk7vU zV5QyvB3|XAPxMP6TBpMk-+s2ovI!rw(!XpxbutX~c@3+Jtsf2@Z>=zzmKdo~!Pa0r z_n7GRJCZ@IhckXqvI?!Un_O37#-YKGfV<|AU*fFQY+EFld^WB>@!j86b$=1h1y228 z=a-jHFNwZV*ZHeZjq*o!zH- zp9>wia+%Ab+~{VT>(l&xCPVRk7mJdkZE6CX=VN7}Sam*0OJBM0yQbqkt;c_-qw7HE zW7!NgjLJtXephOein$M+vCO!?9|!pzrM@jTyx0uH)Yi#~f;#?rF2K0+v^73-YPmFI z(Y{K>Uvl`O za^>0BLmlE54DKFFiBEKv(S;L&=J9hgbDMLDIS2T;-0yFu2Z?A_T*J?-P*r^P@&zfl z?@Qe0)GSP6u9mMJ0IX0sd&vTj$!R^d7#3(g-6f*Ckra*q(<^wJS!P>4G>z|ZqJH*q;x?5R#mMlhR+el-(XNjl@`n<$90fUbGU}{|MJmV90 z`*q1-dLO8cRjvN=ZC0s4%6{87e(8hIekL~vqc`%CdbK>Q^1P=mNYCuuJTr0_(h{55 zx9p=AByk%w`_eV_gZ^0OHw z@2c+a;_90}%GHldFFwUkvi-{q9}5)lvh%(E6>hwD^}*di^L&wFbKIzEiwWZa6AY)X z`O+$SmM^V9Rm*7R$D)}Prlho>wF-w8Op#f?Nw=MOGtediuOkpy*^41o0cSitPM>Fhx0pbMxia8Sn;@2;jH@889sc1pUw5>AH$%2pwaaMhe@P!Z;gp z4nCR5M)E&V4PNNunww}!Kv&4QYK1){tm-p>6Ro6;0cGyH08g`sFlRA-=kXAev zL=BY{`ANuA>lzxbZC;u_>IFIN)g;Ki>fB^IsTzf91Lwe3Y^J~+(juz6L~qb4eaaae zSxjeq)8^EbfcKA=uI3<5yzgwVH9p!IT^y(DNhW*-#4DqQ#gWPWA{Lh-AfpMQZ^0nT z)(Pphtym_jWQY^|`TJasAWA{M#e7T?U&O(A2{)&K6BDkRP_k?dMWn6m5*PY9h@26b z{1B;!QJ2F+?1=YOL#@1P;&1id_TPd(n84M1d9C=J(iqG5LdVy zv%9<xgIyz?d6n-+XDTjB(VSD?L*=^EK=D+mzBN~*^y8?!wxX#B~Wnr z=htg>$RQiiP)ohu#Ye(Wat@i}&I#-{t)R;zLL%Vmrwq8Cwl`CUjM#c=b-4@Fy_@3>gmzj_Q4A|-jv9d_XRByNvcS02dIZBQU3 zP((e+JARL2=ZHDus@~^kGz5n7J$Qx-x1rTCBa~IgUh&C_Sib$r+g!IpV27Y*9HK1e ziJ38-1TcWX55kgNb!FHyNIsm3gEt?M)oR);;Q4JOeSp^!CqopZqg5zO-K^pd~AqSU!XPQK3%0iOf-|=~+rV(}2@pKGo#fC@iOr4Tc zwkr+4kSO5bkz|7jcq>o(;cXrWSqp!7fBKQbYZv`)J!&3EOoV=bDhT#!Lq@7!WU*f5P!leM5T2VD<)d6tA z>Mg1!CXJ)nkyZIq;y{8Jdap3>?` zDCLt)qN=kT(OFv0@BHc*S*ZEL3V5c_PK4qM3MicL*NdOu;`w4rluL{C<)d%A<|?P|S_YU~{bo!=4Wj9p zRK`Hg92YJifeMoZI8}&g7re{IK8$qcS@_1}*dVX*?+bzb)Dh)ikZ@hlQ0Q4%Q$UxU z$gH-F%V}jh@-9Xzb$4XpH&)LJl>J050qh~wrIE;cOaOPV>LbfVGai6fJ7ho)0(JSt>p;xae?2DW0w8<|QFTWs z^YALYv+=Tv*g340K+r;XSeW77lf5zTXkaKGCKt4HRd!HC!eL@^q%d(e3edaU zp+pNmEB4(+X5S`Vh6&L83e7_dGldcHzJM9?^y_Fq|24LvYv3z6Y&&s*)pk46rVKcwwMj3;g~{Adrsj71SKC&y;u+02;DJpE=SXBlP= zYZU46Rgoi+C$TYGadht zh57?8{&|^~=zRKGFhy;YgwH^m)iPa=+gC-}-9fd8=d7_~KkwbXq6W+YF77?kcaXMx zpzm&?h&aY4Su#@+g^1JdpaEgXl4d4qQi~L9(a!+TnCkzcd z8Si+2!w+7I7EKO&!ZA8-ku=MgY&_Gh)rz6((vTl3T8!BOp}U>Spe$U^*0r^j=y-`? z#tTBtLk8tK-ctq;zf`;2R=q3x_D9)(w+GP$jnv;bKf&r8=<+YF?uxjmkf{An#8RBc z`|1>C`|boZM{on#m+h}tjM4KdMmb)AHpp9*aT6615sYI6{M7@;wfM*WPwMaBXo_*Lit96 zb3#cG(|B^@gD-|JTD|&1-^;u*U@wNVx>(NBoe*SPHG|preUgWIEuXfue4MWO>Z8UD zZ6#0<1J5q*PI~_m2eABdQS*B~_UjL4-cR?9v7z2l=GY+jqGzTpSe+G?)Lff0Y`}+3 zr@QgOqy2x`*_kQwtH1OTF+TGt@%C${Q(LHK-!BW`%O zjkJpnYm9(O*vVXAyUOLomdBoiM#nZU>1 zZw;68Z?nh){+c!JAq!2ryL*|-qkEo0ixNmUVX^XY7L_$aH*wA39tirV0;N{;itwJ? z5_k75LZwn^sOdu7i&d)zA8$nnA~+uRrfYVV@q;9Lwwt4^EbHmQ>FQWjSqM$ zVPzc!b@-i5a&{2f5=dem)YPoDFd2hc)zzLakvb&}*T?r$MPs&<^KUob71vtT5k4EB zE#B0rZWn5FoP~7L^A$C%%!Vk((S&uKH0Q?a_^}hxrvS0>Z%McaLnrtS932FXdb^r7OB?4YxR5nw9PvQ zen-r-1){!Rd5jbu`gpPHp(-y69zSp0IGN9b{E=nuyIVP!=N%Yg54=Siw9wU8OqtY^ zoevOP?1&@e%5i7mQJZj|pxPYTSNbp&bY}H&{6XbGY?9)_^v@&vcI61mUzlvKc3vKG z6R^5F_NYQp>_A#5HIidoK{sMmw>WMMweT ze7G6Wk?E@v3|XQOvNm6g&SUl`!4$3%(@$5$b~U6TW+3$rrW2SetlPPr?wYZ`Uig*jmf3|+kZkt3fJZkT(IYzt zod%g3xQH@ixL$J$^%?h0>~JJFa;f|BME>hjLTfl)Y&uDemD$yVd4k)>-OZH(E|0t8 zK+Ji5|W4$TvKb)-ft-| zN)deC{?da{roWNnhJ$R2Z& zE12DMW}ng9%?vvjSolARf?bg%{PVQ|fX-C<75jN14TI$G2E3tE#2r4*!p425OS3R$ zC8DK?fBLKpk9^XUH6RpC4#-?4o8wJw$p5S?uej4oi#zpZLpZ^Puc*Xt;x`e3LiOf| zw{5Oi$y_GtrE+q;Us69RZANEFmmX7R z_e9T@YSw=(zQX%a+eR?<@cegoc!F!W#>J;1j;gw6IsZi8r+ z4N27LGWzylT6O?rd&zawCe;vwIg{Z;HwPwG>e&*7EkcQHuM=qe&Ynnd`UqpmmVXq& ziTgAKDB4jdV5OM7{bLlM?`>MH0f<17Q!c1v7x$+~xc1Q(E{<`Z_0x@KO zl4_X-O^wKBEyntF!%4Pxt6o8KFh(a=!652?s#i_b82`O^poZ!zY!W(~#F(C;BBvVS zv7DS4Z7L-c`wS~@I*}An>vzQT=;LVspP~JemiO8{(!vy*GaJkQCZWN@ zMiC%jL-#k4=qIpFW0o2&M}R*(zZ5Ig!f4u}?W`v+ks8*pzgliKlZH0eH%DlpZJr@9 z<>AYtNv9IB5j8vAC#pdv31KX^rd*U*19|StwoU>Ec@h8{B1s-P-f<``#Y_&yk5fTS zJc^d=pm4p^$0Q^czb0WRQFoUI4P`>X&v4Q5=;e~-gEl_d)%>xxBX^I}IaMXq;?ij< z_Us<~Sg2jWh*^_`}_zrXng z^FOYLa?gjW0_qN!La;(h`8Sg=o<~)<#gkSum$Rq1%sTDNVdxZX+#G|p4#Nk`UL8V) zS-@ux1nt&b5nI(>BT-_;X9bPj2927z$p3qgI`r%n0AU7izw7ZUlZ|B_vd>Z#!zKmj z&G-fr!BlDs!Q-Qw!5D{z51$c}y1_&wnwGuFsY$^;exBcq>Q?{ZK|MEYWDOV)_q$fh zr4*P?5`iLNd|WgroRih*U=G{pGFYIEw-S|x=rChA8ZVow0j0y3=0<>kO@hCp8Sw=b+dy*a)yw<#N+UPaG9G!b*h zq7Y>ShtO7;<=awI4Cg5yuVhT8J+xE3jXbyqa~f{yiyRcJZ(F{eD>BIML_*!B@5>46z1l zH`_(ec58u7oKAwyv}b2+j&dv0wg;R_cPWzhuCFsNtg1IpJ-fzmm*#cr^z}3o-V0t` zEldAHGZ8UyeCDW+x)-J2oYCiv7Ih}W@YY(dga$*k}!hxD1mF^I7+2Qa+$)5o)G6>8=3bPDrj{_|{^6z3StVV|W# znxo~KqnIsA|1o#Q&iwskuK8XmsQw#PSz$!dSD3RwwFu;iS86m`uKFz&W9nC0B$X8H0~_SJvm1#TDB?(L91FGe2>z(l(vjYmO5I( zU7E&vGIVC4mbkuc%qbuyPVRa|sL^!A=)5uk&w`oiKDz+D)D2;=z)vK+ z>;)JDP%i;XYY1xmlDjxeCp#nW2V2`1cC{i?xM&nQFem#v0F6S=ZcgHmFS2(SRak}I zQ6MTUxE%+gr;@R>p!z=lDR*Ya`d-sXv*&%fotIoBX4h#b=J(+--1a`)P`fN6{y|pq zX!a3}5hW>wN2XUn3etxPm4w$it(WgWKvT!`V)MB+A;NHC7wAS4F)F6T`a>$sz|D{) z=|r-(vdLWrM(MjD<1{vWB$0mX1qz2~0&v)W>6J~BPQcEM(aN~Z4Uv58osS_~zJrs~ z$ow-N9jEdpA+Do%F87NyPrv|OSTld*jWj=l_ylS_$oy{zi2;Y`3`}Ya2n5-<8Lc6i zWz+mr>JZ&5EOfx1v)EY6^wc5PwYR=~B^$62hD_X>mzAmzuVh&d%w2zA6j8 z#+X-?@@tCC(n8;)U!G$H=A4Qh|NI_Nc=RvfB1)1(zSo&YSn6+t?{M_*W)SJxaSMVI zqPZ~p;$MGEhI(K(@(h@gQ-E$1QLGt>#hTc3GQICVv61f%7ym{hIX}X>Labcb*Pm@P z?IV#f%k)xSHX=c%YZ+Pcnvc(qCtD_P5NV%@VqvkY1MgVVcJ1826tAgwcit@0hZp^S zsQO`9%M1*M;M!bnF3qb7%5AdAzGhXXo>%DJ=IDlEoa*5n8W2D@$={PH=(UoLbRI|E z_VZ`CGX?I+lhpZKYV=8<4JoZGaN1ebV09L?F#}P{?=wId{}Fmd;ojll`};o!!B#6W zI9xCSv(*r(0YWr|0b7r0NF<2u5-K+uCD{(w|@%-h!DKWbu@c0up&Pz0j7 z6;ty8Q~dpE)in7o4K$eX2m>~|I3OgNgvQZIe|P@ItEF%+Ro$Lwsbg22j2#14Ec714Bat zgs;KD$nejKfk6YP2dETi0z@20F#wHac*+dKlQ-5WZ=7b!!U0qP6L8ur%Ni%Zp@I;J to6PIPvH7K_wJJL!MA`O2E=C8&?FD9xs~D&6RAuC8R Date: Sun, 12 Jan 2020 11:52:49 +0100 Subject: [PATCH 12/71] Snap resize --- UI/TouchControlLayoutScreen.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 65ebb654cf..4483678f89 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -260,9 +260,16 @@ bool TouchControlLayoutScreen::touch(const TouchInput &touch) { float diffX = (touch.x - startX_); float diffY = -(touch.y - startY_); + // Snap to grid + if (g_Config.bTouchSnapToGrid) { + diffX -= (int)(touch.x - startX_) % (g_Config.iTouchSnapGridSize/2); + diffY += (int)(touch.y - startY_) % (g_Config.iTouchSnapGridSize/2); + } + float movementScale = 0.02f; float newScale = startScale_ + diffY * movementScale; float newSpacing = startSpacing_ + diffX * movementScale; + if (newScale > 3.0f) newScale = 3.0f; if (newScale < 0.5f) newScale = 0.5f; if (newSpacing > 3.0f) newSpacing = 3.0f; From f8632b9142ee96cf512f33118e7831ed0ebe6037 Mon Sep 17 00:00:00 2001 From: iota97 Date: Sun, 12 Jan 2020 11:54:06 +0100 Subject: [PATCH 13/71] Remove extra newline --- UI/TouchControlLayoutScreen.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 4483678f89..d0d6886879 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -265,11 +265,9 @@ bool TouchControlLayoutScreen::touch(const TouchInput &touch) { diffX -= (int)(touch.x - startX_) % (g_Config.iTouchSnapGridSize/2); diffY += (int)(touch.y - startY_) % (g_Config.iTouchSnapGridSize/2); } - float movementScale = 0.02f; float newScale = startScale_ + diffY * movementScale; float newSpacing = startSpacing_ + diffX * movementScale; - if (newScale > 3.0f) newScale = 3.0f; if (newScale < 0.5f) newScale = 0.5f; if (newSpacing > 3.0f) newSpacing = 3.0f; From 53ba31aef8641b3d15a1afaca9752c1c15c46437 Mon Sep 17 00:00:00 2001 From: iota97 Date: Sun, 12 Jan 2020 14:28:51 +0100 Subject: [PATCH 14/71] Avoid divison by 0 --- UI/TouchControlLayoutScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index d0d6886879..8137cd6f23 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -340,7 +340,7 @@ void TouchControlLayoutScreen::CreateViews() { // ->OnChange.Handle(this, &GameSettingsScreen::OnChangeControlScaling); CheckBox *snap = new CheckBox(&g_Config.bTouchSnapToGrid, di->T("Snap"), "", new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 228)); - PopupSliderChoice *gridSize = new PopupSliderChoice(&g_Config.iTouchSnapGridSize, 1, 256, di->T("Grid"), screenManager(), "", new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 158)); + PopupSliderChoice *gridSize = new PopupSliderChoice(&g_Config.iTouchSnapGridSize, 2, 256, di->T("Grid"), screenManager(), "", new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 158)); gridSize->SetEnabledPtr(&g_Config.bTouchSnapToGrid); mode_ = new ChoiceStrip(ORIENT_VERTICAL, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 140 + 158 + 64 + 10)); From a881d78d64ff76ac04a89df3bdb24ee1cfe276a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jan 2020 23:54:01 +0100 Subject: [PATCH 15/71] Warning fix --- Core/HLE/sceKernelModule.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index f166e9310f..ec6c9f96cd 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1064,7 +1064,6 @@ static bool KernelImportModuleFuncs(Module *module, u32 *firstImportStubAddr, bo static int gzipDecompress(u8 *OutBuffer, int OutBufferLength, u8 *InBuffer) { int err; - uLong crc; z_stream stream; u8 *outBufferPtr; From e2845363e91eb394a6aa3ef56b5b2cc447abc7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 Jan 2020 18:41:46 +0100 Subject: [PATCH 16/71] Async I/O: Don't delay on close. Fixes #12549 (MGS:PW crash). Also sneak in a small change in logging. --- Core/ELF/PBPReader.cpp | 2 +- Core/HLE/sceIo.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/ELF/PBPReader.cpp b/Core/ELF/PBPReader.cpp index 76fdbb36e4..41653bfcba 100644 --- a/Core/ELF/PBPReader.cpp +++ b/Core/ELF/PBPReader.cpp @@ -37,7 +37,7 @@ PBPReader::PBPReader(FileLoader *fileLoader) : file_(nullptr), header_(), isELF_ } if (memcmp(header_.magic, "\0PBP", 4) != 0) { if (memcmp(header_.magic, "\nFLE", 4) != 0) { - DEBUG_LOG(LOADER, "%s: File actually an ELF, not a PBP", fileLoader->Path().c_str()); + VERBOSE_LOG(LOADER, "%s: File actually an ELF, not a PBP", fileLoader->Path().c_str()); isELF_ = true; } else { ERROR_LOG(LOADER, "Magic number in %s indicated no PBP: %s", fileLoader->Path().c_str(), header_.magic); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index e0378732c4..7f2ea0afa9 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -2719,8 +2719,8 @@ static int IoAsyncFinish(int id) { case IoAsyncOp::CLOSE: f->asyncResult = 0; - // TODO: Rough estimate. - us = 100; + // CLOSE shouldn't have a delay. See #12549. + us = 0; DEBUG_LOG(SCEIO, "ASYNC %lli = sceIoCloseAsync(%d)", f->asyncResult, id); break; From 034a257baf768f94999a02da62e26a2bfe9a22b7 Mon Sep 17 00:00:00 2001 From: shenweip <1037567878@qq.com> Date: Mon, 13 Jan 2020 15:06:02 +0800 Subject: [PATCH 17/71] Add camera support for windows. --- CMakeLists.txt | 5 +- Core/Config.cpp | 1 + Core/Config.h | 1 + Core/HLE/sceUsbCam.cpp | 36 +- UI/GameSettingsScreen.cpp | 13 + UI/NativeApp.cpp | 14 + UI/UI.vcxproj | 12 +- Windows/BufferLock.h | 123 ++++++ Windows/CaptureDevice.cpp | 695 +++++++++++++++++++++++++++++++++ Windows/CaptureDevice.h | 218 +++++++++++ Windows/PPSSPP.vcxproj | 27 +- Windows/PPSSPP.vcxproj.filters | 9 + 12 files changed, 1134 insertions(+), 20 deletions(-) create mode 100644 Windows/BufferLock.h create mode 100644 Windows/CaptureDevice.cpp create mode 100644 Windows/CaptureDevice.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5eab3c7033..df14ac85a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1864,6 +1864,9 @@ set(WindowsFiles Windows/GEDebugger/GEDebugger.h Windows/GEDebugger/TabDisplayLists.h Windows/GEDebugger/TabVertices.h + Windows/BufferLock.h + Windows/CaptureDevice.cpp + Windows/CaptureDevice.h Windows/DinputDevice.cpp Windows/DinputDevice.h Windows/DSoundStream.cpp @@ -1917,7 +1920,7 @@ set(WindowsFiles list(APPEND LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT}) if(WIN32) - list(APPEND LinkCommon kernel32 user32 gdi32 shell32 comctl32 dsound xinput d3d9 winmm dinput8 ole32 winspool ksuser) + list(APPEND LinkCommon kernel32 user32 gdi32 shell32 comctl32 dsound xinput d3d9 winmm dinput8 ole32 winspool ksuser mf mfplat mfreadwrite mfuuid shlwapi) #setup_target_project(${TargetBin} Windows) list(APPEND NativeAppSource ${WindowsFiles}) endif() diff --git a/Core/Config.cpp b/Core/Config.cpp index 1fc75131f0..8ae8b6f78f 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -706,6 +706,7 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", true, false), #ifdef _WIN32 ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), + ConfigSetting("WinCameraDevice", &g_Config.sWinCameraDevice, "", true, false), #endif ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false), ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true), diff --git a/Core/Config.h b/Core/Config.h index 2fe185fcd7..3252164101 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -128,6 +128,7 @@ public: // If not set, will use the "best" device. std::string sVulkanDevice; std::string sD3D11Device; // Windows only + std::string sWinCameraDevice; // Windows only bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 7667cc335f..5d0bce11cb 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -25,6 +25,12 @@ #include "Core/HLE/sceUsbCam.h" #include "Core/MemMapHelpers.h" +#ifdef _WIN32 +#include "Windows/CaptureDevice.h" +#undef min +#endif + + PspUsbCamSetupMicParam micParam; PspUsbCamSetupVideoParam videoParam; @@ -32,6 +38,7 @@ unsigned int videoBufferLength = 0; unsigned int nextVideoFrame = 0; uint8_t *videoBuffer; std::mutex videoBufferMutex; +bool isShutDown = false; enum { VIDEO_BUFFER_SIZE = 40 * 1000, @@ -39,9 +46,17 @@ enum { void __UsbCamInit() { videoBuffer = new uint8_t[VIDEO_BUFFER_SIZE]; + isShutDown = false; } void __UsbCamShutdown() { +#ifdef _WIN32 + if (winCamera) { + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); + } +#endif + isShutDown = true; + delete[] videoBuffer; videoBuffer = nullptr; } @@ -97,13 +112,31 @@ static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { static int sceUsbCamStartVideo() { INFO_LOG(HLE, "UNIMPL sceUsbCamStartVideo"); +#ifdef _WIN32 + if (winCamera) { + if (winCamera->isShutDown()) { + delete winCamera; + winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); + } + + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::START, nullptr }); + } + +#else System_SendMessage("camera_command", "startVideo"); +#endif return 0; } static int sceUsbCamStopVideo() { INFO_LOG(HLE, "UNIMPL sceUsbCamStopVideo"); +#ifdef _WIN32 + if (winCamera) + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); +#else System_SendMessage("camera_command", "stopVideo"); +#endif return 0; } @@ -114,7 +147,6 @@ static int sceUsbCamAutoImageReverseSW(int rev) { static int sceUsbCamReadVideoFrameBlocking(u32 bufAddr, u32 size) { std::lock_guard lock(videoBufferMutex); - u32 transferSize = std::min(videoBufferLength, size); if (Memory::IsValidRange(bufAddr, size)) { Memory::Memcpy(bufAddr, videoBuffer, transferSize); @@ -203,6 +235,8 @@ void Register_sceUsbCam() void Camera::pushCameraImage(long long length, unsigned char* image) { std::lock_guard lock(videoBufferMutex); + if (isShutDown) + return; memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); if (length > VIDEO_BUFFER_SIZE) { videoBufferLength = 0; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index ae317198d6..be60930581 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -70,6 +70,11 @@ #include "Windows/W32Util/ShellUtil.h" #endif +#ifdef _WIN32 +#include "Windows/CaptureDevice.h" +#endif + + GameSettingsScreen::GameSettingsScreen(std::string gamePath, std::string gameID, bool editThenRestore) : UIDialogScreenWithGameBackground(gamePath), gameID_(gameID), enableReports_(false), editThenRestore_(editThenRestore) { lastVertical_ = UseVerticalLayout(); @@ -262,6 +267,14 @@ void GameSettingsScreen::CreateViews() { softwareGPU->SetEnabled(false); } +#ifdef _WIN32 + if (winCamera && winCamera->getDeviceList().size() >= 1) { + graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); + PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sWinCameraDevice, gr->T("Camera Device"), winCamera->getDeviceList(), nullptr, screenManager())); + } +#endif + + graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), gr->GetName(), screenManager())); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c04ecd7bc4..a855348f35 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -37,6 +37,7 @@ #if defined(_WIN32) #include "Windows/WindowsAudio.h" #include "Windows/MainWindow.h" +#include "Windows/CaptureDevice.h" #endif #include "base/display.h" @@ -67,6 +68,7 @@ #include "Common/LogManager.h" #include "Common/MemArena.h" #include "Common/GraphicsContext.h" +#include "Common/OSVersion.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/Core.h" @@ -855,6 +857,13 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) { #endif #endif +#ifdef _WIN32 + if (IsVistaOrHigher()) { + winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); + } +#endif + g_gameInfoCache = new GameInfoCache(); if (gpu) @@ -879,6 +888,11 @@ void NativeShutdownGraphics() { winAudioBackend = nullptr; #endif +#ifdef _WIN32 + delete winCamera; + winCamera = nullptr; +#endif + ShutdownWebServer(); UIBackgroundShutdown(); diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index 769c127261..ea06201cf5 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -207,7 +207,7 @@ Level3 _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext true false false @@ -231,7 +231,7 @@ Level3 _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_64=1;WIN32;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext true false false @@ -281,7 +281,7 @@ Level3 _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext true false false @@ -310,7 +310,7 @@ true true USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext false Size true @@ -339,7 +339,7 @@ true true USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_64=1;WIN32;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext false true false @@ -401,7 +401,7 @@ true true USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext false true false diff --git a/Windows/BufferLock.h b/Windows/BufferLock.h new file mode 100644 index 0000000000..a19b8c965d --- /dev/null +++ b/Windows/BufferLock.h @@ -0,0 +1,123 @@ +////////////////////////////////////////////////////////////////////////// +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +////////////////////////////////////////////////////////////////////////// + + + +#pragma once + + +//------------------------------------------------------------------- +// VideoBufferLock class +// +// Locks a video buffer that might or might not support IMF2DBuffer. +// +//------------------------------------------------------------------- + +class VideoBufferLock +{ +public: + VideoBufferLock(IMFMediaBuffer *pBuffer) : m_p2DBuffer(NULL), m_bLocked(FALSE) + { + m_pBuffer = pBuffer; + m_pBuffer->AddRef(); + + // Query for the 2-D buffer interface. OK if this fails. + (void)m_pBuffer->QueryInterface(IID_PPV_ARGS(&m_p2DBuffer)); + } + + ~VideoBufferLock() + { + UnlockBuffer(); + SafeRelease(&m_pBuffer); + SafeRelease(&m_p2DBuffer); + } + + //------------------------------------------------------------------- + // LockBuffer + // + // Locks the buffer. Returns a pointer to scan line 0 and returns the stride. + // + // The caller must provide the default stride as an input parameter, in case + // the buffer does not expose IMF2DBuffer. You can calculate the default stride + // from the media type. + //------------------------------------------------------------------- + + HRESULT LockBuffer( + LONG lDefaultStride, // Minimum stride (with no padding). + DWORD dwHeightInPixels, // Height of the image, in pixels. + BYTE **ppbScanLine0, // Receives a pointer to the start of scan line 0. + LONG *plStride // Receives the actual stride. + ) + { + HRESULT hr = S_OK; + + // Use the 2-D version if available. + if (m_p2DBuffer) + { + hr = m_p2DBuffer->Lock2D(ppbScanLine0, plStride); + } + else + { + // Use non-2D version. + BYTE *pData = NULL; + + hr = m_pBuffer->Lock(&pData, NULL, NULL); + if (SUCCEEDED(hr)) + { + *plStride = lDefaultStride; + if (lDefaultStride < 0) + { + // Bottom-up orientation. Return a pointer to the start of the + // last row *in memory* which is the top row of the image. + *ppbScanLine0 = pData + abs(lDefaultStride) * (dwHeightInPixels - 1); + } + else + { + // Top-down orientation. Return a pointer to the start of the + // buffer. + *ppbScanLine0 = pData; + } + } + } + + m_bLocked = (SUCCEEDED(hr)); + + return hr; + } + + //------------------------------------------------------------------- + // UnlockBuffer + // + // Unlocks the buffer. Called automatically by the destructor. + //------------------------------------------------------------------- + + void UnlockBuffer() + { + if (m_bLocked) + { + if (m_p2DBuffer) + { + (void)m_p2DBuffer->Unlock2D(); + } + else + { + (void)m_pBuffer->Unlock(); + } + m_bLocked = FALSE; + } + } + +private: + IMFMediaBuffer *m_pBuffer; + IMF2DBuffer *m_p2DBuffer; + + BOOL m_bLocked; +}; diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp new file mode 100644 index 0000000000..45d55a2a23 --- /dev/null +++ b/Windows/CaptureDevice.cpp @@ -0,0 +1,695 @@ +// Copyright (c) 2020- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include "thread/threadutil.h" +#include "CaptureDevice.h" +#include "BufferLock.h" +#include "ext/jpge/jpge.h" +#include "Core/HLE/sceUsbCam.h" +#include "Core/Config.h" + +WindowsCaptureDevice *winCamera; + +// TODO: Add more formats, but need some tests. +VideoFormatTransform g_VideoFormats[] = +{ + { MFVideoFormat_RGB32, AV_PIX_FMT_RGBA }, + { MFVideoFormat_RGB24, AV_PIX_FMT_RGB24 }, + { MFVideoFormat_YUY2, AV_PIX_FMT_YUYV422 }, + { MFVideoFormat_NV12, AV_PIX_FMT_NV12 } +}; + +const int g_cVideoFormats = 4; + + +MediaParam defaultVideoParam = { 480, 272, 0, MFVideoFormat_RGB24 }; +MediaParam defaultAudioParam = { 44100, 2, 0, MFAudioFormat_PCM }; + +HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride); + +ReaderCallback::ReaderCallback(WindowsCaptureDevice *device): img_convert_ctx(nullptr){ + this->device = device; +} + +ReaderCallback::~ReaderCallback() { + sws_freeContext(img_convert_ctx); +} + +HRESULT ReaderCallback::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = + { + QITABENT(ReaderCallback, IMFSourceReaderCallback), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +HRESULT ReaderCallback::OnReadSample( + HRESULT hrStatus, + DWORD dwStreamIndex, + DWORD dwStreamFlags, + LONGLONG llTimestamp, + IMFSample *pSample) { + HRESULT hr = S_OK; + IMFMediaBuffer *pBuffer = nullptr; + LONG lStride = 0; + std::lock_guard lock(device->sdMutex); + if (device->isShutDown()) + return hr; + + if (FAILED(hrStatus)) + hr = hrStatus; + + if (SUCCEEDED(hr)) { + if (pSample) { + hr = pSample->GetBufferByIndex(0, &pBuffer); + } + } + if (SUCCEEDED(hr)) { + switch (device->type) { + case CAPTUREDEVIDE_TYPE::VIDEO: { + BYTE *pbScanline0 = nullptr; + VideoBufferLock *videoBuffer = nullptr; + int imgJpegSize = device->imgJpegSize; + unsigned char* invertedSrcImg = nullptr; + LONG srcPadding = 0; + + UINT32 srcW = device->deviceParam.width; + UINT32 srcH = device->deviceParam.height; + UINT32 dstW = device->targetMediaParam.width; + UINT32 dstH = device->targetMediaParam.height; + GUID srcMFVideoFormat = device->deviceParam.videoFormat; + + // pSample can be null, in this case ReadSample still should be called to request next frame. + if (pSample) { + videoBuffer = new VideoBufferLock(pBuffer); + hr = videoBuffer->LockBuffer(device->deviceParam.default_stride, device->deviceParam.height, &pbScanline0, &lStride); + + if (lStride > 0) + srcPadding = lStride - device->deviceParam.default_stride; + else + srcPadding = device->deviceParam.default_stride - lStride; + + if (SUCCEEDED(hr)) { + // Convert image to RGB24 + if (lStride > 0) { + imgConvert( + device->imageRGB, dstW, dstH, device->imgRGBLineSizes, + pbScanline0, srcW, srcH, srcMFVideoFormat, srcPadding); + } + else { + // If stride < 0, the pointer to the first row of source image is the last row in memory,should invert it in memory. + invertedSrcImg = (unsigned char*)av_malloc(av_image_get_buffer_size(getAVVideoFormatbyMFVideoFormat(srcMFVideoFormat), srcW, srcH, 1)); + imgInvert(invertedSrcImg, pbScanline0, srcW, srcH, device->deviceParam.videoFormat, lStride); + // We alloc a inverted image with no padding, set padding to zero. + srcPadding = 0; + imgConvert( + device->imageRGB, dstW, dstH, device->imgRGBLineSizes, + invertedSrcImg, srcW, srcH, srcMFVideoFormat, srcPadding); + av_free(invertedSrcImg); + } + + // Compress image to jpeg from RGB24. + jpge::compress_image_to_jpeg_file_in_memory( + device->imageJpeg, imgJpegSize, + dstW, + dstH, + 3, + device->imageRGB); + } + Camera::pushCameraImage(imgJpegSize, device->imageJpeg); + } + // Request the next frame. + if (SUCCEEDED(hr)) { + hr = device->m_pReader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + 0, + nullptr, + nullptr, + nullptr, + nullptr + ); + } + + delete videoBuffer; + break; + } + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } + } + + SafeRelease(&pBuffer); + return hr; +} + +AVPixelFormat ReaderCallback::getAVVideoFormatbyMFVideoFormat(const GUID &MFVideoFormat) { + for (int i = 0; i < g_cVideoFormats; i++) { + if (MFVideoFormat == g_VideoFormats[i].MFVideoFormat) + return g_VideoFormats[i].AVVideoFormat; + } + return AV_PIX_FMT_RGB24; +} + +void ReaderCallback::imgConvert( + unsigned char *dst, unsigned int &dstW, unsigned int &dstH, int dstLineSizes[4], + unsigned char *src, const unsigned int &srcW, const unsigned int &srcH, const GUID &srcFormat, + const int &srcPadding) { + int srcLineSizes[4] = { 0, 0, 0, 0 }; + unsigned char *pSrc[4]; + unsigned char *pDst[4]; + + AVPixelFormat srcAvFormat = getAVVideoFormatbyMFVideoFormat(srcFormat); + + av_image_fill_linesizes(srcLineSizes, srcAvFormat, srcW); + + // Is this correct? + if (srcPadding != 0) { + for (int i = 0; i < 4; i++) { + if (srcLineSizes[i] != 0) + srcLineSizes[i] += srcPadding; + } + } + + av_image_fill_pointers(pSrc, srcAvFormat, srcH, src, srcLineSizes); + av_image_fill_pointers(pDst, AV_PIX_FMT_RGB24, dstH, dst, dstLineSizes); + + + + if (img_convert_ctx == nullptr) { + img_convert_ctx = sws_getContext( + srcW, + srcH, + srcAvFormat, + dstW, + dstH, + AV_PIX_FMT_RGB24, + SWS_BICUBIC, + nullptr, + nullptr, + nullptr + ); + } + + if (img_convert_ctx) { + sws_scale(img_convert_ctx, + (const uint8_t *const *)pSrc, + srcLineSizes, + 0, + srcH, + (uint8_t *const *)pDst, + dstLineSizes + ); + } +} + +void ReaderCallback::imgInvert(unsigned char *dst, unsigned char *src, const int &srcW, const int &srcH, const GUID &srcFormat, const int &srcStride) { + AVPixelFormat srcAvFormat = getAVVideoFormatbyMFVideoFormat(srcFormat); + int dstLineSizes[4] = { 0, 0, 0, 0 }; + + av_image_fill_linesizes(dstLineSizes, srcAvFormat, srcW); + + if(srcFormat == MFVideoFormat_RGB32) + imgInvertRGBA(dst, dstLineSizes[0], src, srcStride, srcH); + else if(srcFormat == MFVideoFormat_RGB24) + imgInvertRGB(dst, dstLineSizes[0], src, srcStride, srcH); + else if (srcFormat == MFVideoFormat_YUY2) + imgInvertYUY2(dst, dstLineSizes[0], src, srcStride, srcH); + else if (srcFormat == MFVideoFormat_NV12) + imgInvertNV12(dst, dstLineSizes[0], src, srcStride, srcH);; +} + +void ReaderCallback::imgInvertRGBA(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + MFCopyImage(dst, dstStride, src, srcStride, dstStride, h); +} + +void ReaderCallback::imgInvertRGB(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + for (int y = 0; y < h; y++) { + for (int srcx = dstStride - 1, dstx = 0; dstx < dstStride; srcx--, dstx++) { + dst[dstx] = src[srcx]; + } + dst += dstStride; + src += srcStride; + } +} + +void ReaderCallback::imgInvertYUY2(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + for (int y = 0; y < h; y++) { + for (int srcx = dstStride - 1, dstx = 0; dstx < dstStride; srcx--, dstx++) { + dst[dstx] = src[srcx]; + } + dst += dstStride; + src += srcStride; + } +} + +void ReaderCallback::imgInvertNV12(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + unsigned char *dstY = dst; + unsigned char *dstU = dst + dstStride * h; + unsigned char *srcY = src; + unsigned char *srcV = src + srcStride * h; + + unsigned char *srcY1 = srcY; + unsigned char *srcY2 = srcY1 + srcStride; + unsigned char *dstY1 = dstY; + unsigned char *dstY2 = dstY1 + dstStride; + + bool isodd = h % 2 != 0; + + for (int y = 0; y < (isodd ? h - 1 : h); y += 2) { + for (int srcx = dstStride - 1, dstx = 0; dstx < dstStride; srcx--, dstx++) { + dstY1[dstx] = srcY1[srcx]; + dstY2[dstx] = srcY2[srcx]; + dstU[dstx] = srcV[srcx]; + } + dstY += dstStride * 2; + srcY1 += srcStride * 2; + srcY2 += srcStride *2; + srcV += srcStride; + dstU += dstStride; + } +} + +WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) : + type(type), + m_pCallback(nullptr), + m_pSource(nullptr), + m_pReader(nullptr), + error(CAPTUREDEVIDE_ERROR_NO_ERROR), + errorMessage(""), + state(CAPTUREDEVIDE_STATE::UNINITIALIZED) { + + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + targetMediaParam = defaultVideoParam; + imageRGB = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, targetMediaParam.width, targetMediaParam.height, 1)); + av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width); + imgJpegSize = av_image_get_buffer_size(AV_PIX_FMT_YUVJ411P, targetMediaParam.width, targetMediaParam.height, 1); + imageJpeg = (unsigned char*)av_malloc(imgJpegSize); + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + targetMediaParam = defaultAudioParam; + imageRGB = nullptr; + imageJpeg = nullptr; + break; + } + + std::thread t(&WindowsCaptureDevice::messageHandler, this); + t.detach(); +} + +WindowsCaptureDevice::~WindowsCaptureDevice() { + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + av_free(imageRGB); + av_free(imageJpeg); + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } +} + +bool WindowsCaptureDevice::init() { + HRESULT hr = S_OK; + param = { 0 }; + IMFAttributes *pAttributes = nullptr; + + hr = MFCreateAttributes(&pAttributes, 1); + if (SUCCEEDED(hr)) { + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + hr = pAttributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID + ); + + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + hr = pAttributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID + ); + + break; + default: + setError(CAPTUREDEVIDE_ERROR_UNKNOWN_TYPE, "Unknown device type"); + return false; + } + } + + if (SUCCEEDED(hr)) + hr = MFEnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); + + if (FAILED(hr)) { + setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot enumerate devices"); + SafeRelease(&pAttributes); + return false; + } + + SafeRelease(&pAttributes); + updateState(CAPTUREDEVIDE_STATE::STOPPED); + return true; +} + +bool WindowsCaptureDevice::start() { + HRESULT hr = S_OK; + IMFAttributes *pAttributes = nullptr; + IMFMediaType *pType = nullptr; + UINT32 selection = 0; + UINT32 count = 0; + std::vector deviceList = getDeviceList(); + + if (deviceList.size() < 1) { + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Has no device"); + return false; + } + + m_pCallback = new ReaderCallback(this); + + switch (state) { + case CAPTUREDEVIDE_STATE::STOPPED: + for (auto &name : deviceList) { + if (name == g_Config.sWinCameraDevice) { + selection = count; + break; + } + ++count; + } + setSelction(selection); + hr = param.ppDevices[param.selection]->ActivateObject( + __uuidof(IMFMediaSource), + (void**)&m_pSource); + + if (SUCCEEDED(hr)) + hr = MFCreateAttributes(&pAttributes, 2); + + // Use async mode + if (SUCCEEDED(hr)) + hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, m_pCallback); + + if (SUCCEEDED(hr)) + hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE); + + if (SUCCEEDED(hr)) { + hr = MFCreateSourceReaderFromMediaSource( + m_pSource, + pAttributes, + &m_pReader + ); + } + + if (!m_pReader) + hr = -1; + + if (SUCCEEDED(hr)) { + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + hr = m_pReader->GetNativeMediaType( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + (DWORD)0xFFFFFFFF,//MF_SOURCE_READER_CURRENT_TYPE_INDEX + &pType + ); + if (SUCCEEDED(hr)) + hr = setDeviceParam(pType); + + // Request the first frame, in asnyc mode, OnReadSample will be called when ReadSample completed. + if (SUCCEEDED(hr)) { + hr = m_pReader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + 0, + nullptr, + nullptr, + nullptr, + nullptr + ); + } + + break; + + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } + } + + if (FAILED(hr)) { + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Cannot start"); + if(m_pSource) + m_pSource->Shutdown(); + SafeRelease(&m_pSource); + SafeRelease(&pAttributes); + SafeRelease(&pType); + SafeRelease(&m_pReader); + return false; + } + + SafeRelease(&pAttributes); + SafeRelease(&pType); + updateState(CAPTUREDEVIDE_STATE::STARTED); + break; + case CAPTUREDEVIDE_STATE::LOST: + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Device has lost"); + return false; + case CAPTUREDEVIDE_STATE::STARTED: + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Device has started"); + return false; + case CAPTUREDEVIDE_STATE::UNINITIALIZED: + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Device doesn't initialize"); + return false; + default: + break; + } + return true; +} + +bool WindowsCaptureDevice::stop() { + if (state == CAPTUREDEVIDE_STATE::STOPPED) + return true; + if (m_pSource) + m_pSource->Stop(); + + updateState(CAPTUREDEVIDE_STATE::STOPPED); + + return true; +}; + +std::vector WindowsCaptureDevice::getDeviceList(int *pActuallCount) { + HRESULT hr = S_OK; + UINT32 count = 0; + LPWSTR pwstrName = nullptr; + char *cstrName = nullptr; + std::string strName; + DWORD dwMinSize = 0; + std::vector deviceList; + + for (; count < param.count; count++) { + hr = param.ppDevices[count]->GetAllocatedString( + MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, + &pwstrName, + nullptr + ); + + if (SUCCEEDED(hr)) { + // Get the size needed first + dwMinSize = WideCharToMultiByte(CP_OEMCP, NULL, pwstrName, -1, nullptr, 0, nullptr, FALSE); + if (dwMinSize == 0) + hr = -1; + } + if (SUCCEEDED(hr)) { + cstrName = new char[dwMinSize]; + WideCharToMultiByte(CP_OEMCP, NULL, pwstrName, -1, cstrName, dwMinSize, NULL, FALSE); + strName = cstrName; + delete[] cstrName; + + deviceList.push_back(strName); + } + + if (FAILED(hr)) { + setError(CAPTUREDEVIDE_ERROR_GETNAMES_FAILED, "Error occurred,gotten " + std::to_string((int)count) + " device names"); + if(pActuallCount) + *pActuallCount = count; + return deviceList; + } + } + if (pActuallCount) + *pActuallCount = count + 1; + return deviceList; +} + +HRESULT WindowsCaptureDevice::setDeviceParam(IMFMediaType *pType) { + HRESULT hr = S_OK; + GUID subtype = { 0 }; + bool getFormat = false; + + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); + if (FAILED(hr)) + break; + + for (int i = 0; i < g_cVideoFormats; i++) { + if (subtype == g_VideoFormats[i].MFVideoFormat) { + deviceParam.videoFormat = subtype; + getFormat = true; + break; + } + } + + if (!getFormat) { + for (int i = 0; i < g_cVideoFormats; i++) { + hr = pType->SetGUID(MF_MT_SUBTYPE, g_VideoFormats[i].MFVideoFormat); + if (FAILED(hr)) + continue; + + hr = m_pReader->SetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + NULL, + pType + ); + + if (SUCCEEDED(hr)) { + deviceParam.videoFormat = g_VideoFormats[i].MFVideoFormat; + getFormat = true; + break; + } + } + } + if (SUCCEEDED(hr)) + hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &deviceParam.width, &deviceParam.height); + + if (SUCCEEDED(hr)) + hr = GetDefaultStride(pType, &deviceParam.default_stride); + + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } + + return hr; +} + +void WindowsCaptureDevice::sendMessage(CAPTUREDEVIDE_MESSAGE message) { + // Must be unique lock + std::unique_lock lock(mutex); + messageQueue.push(message); + lock.unlock(); + cond.notify_one(); +} + +CAPTUREDEVIDE_MESSAGE WindowsCaptureDevice::getMessage() { + // Must be unique lock + std::unique_lock lock(mutex); + CAPTUREDEVIDE_MESSAGE message; + cond.wait(lock, [this]() { return !messageQueue.empty(); }); + message = messageQueue.front(); + messageQueue.pop(); + lock.unlock(); + + return message; +} + +void WindowsCaptureDevice::messageHandler() { + CoInitializeEx(NULL, COINIT_MULTITHREADED); + MFStartup(MF_VERSION); + CAPTUREDEVIDE_MESSAGE message; + + if (type == CAPTUREDEVIDE_TYPE::VIDEO) { + setCurrentThreadName("Camera"); + } + + while ((message = getMessage()).command != CAPTUREDEVIDE_COMMAND::SHUTDOWN) { + switch (message.command) { + case CAPTUREDEVIDE_COMMAND::INITIALIZE: + init(); + break; + case CAPTUREDEVIDE_COMMAND::START: + start(); + break; + case CAPTUREDEVIDE_COMMAND::STOP: + stop(); + break; + case CAPTUREDEVIDE_COMMAND::UPDATE_STATE: + updateState((*(CAPTUREDEVIDE_STATE *)message.opacity)); + break; + } + } + + if (state != CAPTUREDEVIDE_STATE::STOPPED) + stop(); + + std::lock_guard lock(sdMutex); + SafeRelease(&m_pSource); + SafeRelease(&m_pReader); + delete m_pCallback; + + MFShutdown(); + CoUninitialize(); + + updateState(CAPTUREDEVIDE_STATE::SHUTDOWN); +} + +//----------------------------------------------------------------------------- +// GetDefaultStride +// +// Gets the default stride for a video frame, assuming no extra padding bytes. +// +//----------------------------------------------------------------------------- + +HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride) +{ + LONG lStride = 0; + + // Try to get the default stride from the media type. + HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride); + if (FAILED(hr)) + { + // Attribute not set. Try to calculate the default stride. + GUID subtype = GUID_NULL; + + UINT32 width = 0; + UINT32 height = 0; + + // Get the subtype and the image size. + hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); + if (SUCCEEDED(hr)) + { + hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height); + } + if (SUCCEEDED(hr)) + { + hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride); + } + + // Set the attribute for later reference. + if (SUCCEEDED(hr)) + { + (void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride)); + } + } + + if (SUCCEEDED(hr)) + { + *plStride = lStride; + } + return hr; +} diff --git a/Windows/CaptureDevice.h b/Windows/CaptureDevice.h new file mode 100644 index 0000000000..ea76a971b5 --- /dev/null +++ b/Windows/CaptureDevice.h @@ -0,0 +1,218 @@ +// Copyright (c) 2020- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#include "libavformat/avformat.h" +#include "libswscale/swscale.h" +#include "libavutil/imgutils.h" +} +#endif // __cplusplus + +struct VideoFormatTransform { + GUID MFVideoFormat; + AVPixelFormat AVVideoFormat; +}; + +enum class CAPTUREDEVIDE_TYPE { + VIDEO, + AUDIO +}; + +enum class CAPTUREDEVIDE_STATE { + UNINITIALIZED, + LOST, + STOPPED, + STARTED, + SHUTDOWN +}; + +enum class CAPTUREDEVIDE_COMMAND { + INITIALIZE, + START, + STOP, + SHUTDOWN, + UPDATE_STATE +}; + +enum CAPTUREDEVIDE_ERROR { + CAPTUREDEVIDE_ERROR_NO_ERROR, + CAPTUREDEVIDE_ERROR_UNKNOWN_TYPE = 0x80000001, + CAPTUREDEVIDE_ERROR_INIT_FAILED, + CAPTUREDEVIDE_ERROR_START_FAILED, + CAPTUREDEVIDE_ERROR_STOP_FAILED, + CAPTUREDEVIDE_ERROR_GETNAMES_FAILED +}; + +struct CAPTUREDEVIDE_MESSAGE{ + CAPTUREDEVIDE_COMMAND command; + void *opacity; +}; + +struct ChooseDeviceParam { + IMFActivate **ppDevices; + UINT32 count; + UINT32 selection; +}; + +union MediaParam { + struct { + UINT32 width; + UINT32 height; + LONG default_stride; + GUID videoFormat; + }; + struct { + UINT32 sampleRate; + UINT32 channels; + LONG padding; + GUID audioFomat; + }; +}; + +template void SafeRelease(T **ppT) { + if (*ppT) { + (*ppT)->Release(); + *ppT = nullptr; + } +} + +class WindowsCaptureDevice; + +class ReaderCallback : public IMFSourceReaderCallback { +public: + ReaderCallback(WindowsCaptureDevice *device); + ~ReaderCallback(); + + // IUnknown methods. + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef() { return 0; } // Unused, just define. + STDMETHODIMP_(ULONG) Release() { return 0; } // Unused, just define. + + // IMFSourceReaderCallback methods. + STDMETHODIMP OnReadSample( + HRESULT hrStatus, + DWORD dwStreamIndex, + DWORD dwStreamFlags, + LONGLONG llTimestamp, + IMFSample *pSample // Can be null,even if hrStatus is success. + ); + + STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) { return S_OK; } + STDMETHODIMP OnFlush(DWORD) { return S_OK; } + + AVPixelFormat getAVVideoFormatbyMFVideoFormat(const GUID &MFVideoFormat); + + /* + * Always convet the image to RGB24 + * @param dst/src pointer to destination/source image + * @param dstW/srcW, dstH/srcH destination/source image's width and height in pixels + * @param dstLineSizes get the linesize of each plane by av_image_fill_linesizes() + * @param srcFormat MF_MT_SUBTYPE attribute of source image + * @param srcPadding should be setted to non-zero if source image has padding + */ + void imgConvert( + unsigned char *dst, unsigned int &dstW, unsigned int &dstH, int dstLineSizes[4], + unsigned char *src, const unsigned int &srcW, const unsigned int &srcH, const GUID &srcFormat, + const int &srcPadding); + + // Flip image start and end in memory, it is neccessary if stride of source image is a negative value + // Might need some tests in different machine. + void imgInvert(unsigned char *dst, unsigned char *src, const int &srcW, const int &srcH, const GUID &srcFormat, const int &srcStride); + void imgInvertRGBA(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + void imgInvertRGB(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + void imgInvertYUY2(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + void imgInvertNV12(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + + +protected: + WindowsCaptureDevice *device; + SwsContext *img_convert_ctx; +}; + +class WindowsCaptureDevice { +public: + WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type); + ~WindowsCaptureDevice(); + + bool init(); + bool start(); + bool stop(); + + CAPTUREDEVIDE_ERROR getError() const { return error; } + std::string getErrorMessage() const { return errorMessage; } + int getDeviceCounts() const { return param.count; } + // Get a list contained friendly device name. + std::vector getDeviceList(int *pActuallCount = nullptr); + + void setError(const CAPTUREDEVIDE_ERROR &newError, const std::string &newErrorMessage) { error = newError; errorMessage = newErrorMessage; } + void setSelction(const UINT32 &selection) { param.selection = selection; } + void updateState(const CAPTUREDEVIDE_STATE &newState) { state = newState; } + HRESULT setDeviceParam(IMFMediaType *pType); + + bool isShutDown() const { return state == CAPTUREDEVIDE_STATE::SHUTDOWN; } + + void sendMessage(CAPTUREDEVIDE_MESSAGE message); + CAPTUREDEVIDE_MESSAGE getMessage(); + + friend class ReaderCallback; + +protected: +// Handle message here. + void messageHandler(); + + CAPTUREDEVIDE_TYPE type; + MediaParam deviceParam; + MediaParam targetMediaParam; + CAPTUREDEVIDE_STATE state; + ChooseDeviceParam param; + + CAPTUREDEVIDE_ERROR error; + std::string errorMessage; + +// MF interface. + ReaderCallback *m_pCallback; + IMFSourceReader *m_pReader; + IMFMediaSource *m_pSource; + +// Message loop. + std::mutex mutex; + std::condition_variable cond; + std::queue messageQueue; + +// For the shutdown event safety. + std::mutex sdMutex; + +// Camera only + unsigned char *imageRGB; + int imgRGBLineSizes[4]; + unsigned char *imageJpeg; + int imgJpegSize; +}; + +extern WindowsCaptureDevice *winCamera; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 9eed29a578..212518524e 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -238,7 +238,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h;Common/DbgNew.h MultiThreadedDebug StreamingSIMDExtensions2 @@ -249,7 +249,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) true Windows MachineX86 @@ -275,7 +275,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h;Common/DbgNew.h MultiThreadedDebug false @@ -287,7 +287,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) true $(OutDir)$(ProjectName).pdb true @@ -342,7 +342,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h MultiThreadedDebug false @@ -354,7 +354,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) true $(OutDir)$(ProjectName).pdb true @@ -382,7 +382,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h stdafx.h true @@ -390,7 +390,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) true @@ -431,14 +431,14 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h true false $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) true Windows @@ -514,14 +514,14 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h true false $(EXTERNAL_COMPILE_OPTIONS) - oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) true Windows @@ -680,6 +680,7 @@ true true + @@ -871,6 +872,8 @@ true true + + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index c640c5642e..d51c53a9f5 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -191,6 +191,9 @@ Windows\System + + Windows\Input + @@ -354,6 +357,12 @@ Windows\System + + Windows\Input + + + Windows\Input + From ff7bf318aff52ce5db5dfc501aba7e57ec42d036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 13 Jan 2020 15:36:11 +0100 Subject: [PATCH 18/71] Update lang submodule --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index 6189eb455e..3e05ae374e 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 6189eb455e4b184ede558f7354e6ba31b485fd58 +Subproject commit 3e05ae374e9b8baab899d6f10141af870828c643 From c2e857775617e75306bcb98969dd06ee7d0feacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 13 Jan 2020 23:28:18 +0100 Subject: [PATCH 19/71] Loader: If the module range is bad, don't get stuck trying to scan for functions. Better crash immediately (although that's also not ideal). --- Core/HLE/sceKernelModule.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index ec6c9f96cd..87eeab4b15 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1334,16 +1334,21 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd if (scan && codeSections.empty()) { u32 scanStart = module->textStart; u32 scanEnd = module->textEnd; - // Skip the exports and imports sections, they're not code. - if (scanEnd >= std::min(modinfo->libent, modinfo->libstub)) { - insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::min(modinfo->libent, modinfo->libstub) - 4, insertSymbols); - scanStart = std::min(modinfo->libentend, modinfo->libstubend); + + if (Memory::IsValidRange(scanStart, scanEnd - scanStart)) { + // Skip the exports and imports sections, they're not code. + if (scanEnd >= std::min(modinfo->libent, modinfo->libstub)) { + insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::min(modinfo->libent, modinfo->libstub) - 4, insertSymbols); + scanStart = std::min(modinfo->libentend, modinfo->libstubend); + } + if (scanEnd >= std::max(modinfo->libent, modinfo->libstub)) { + insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::max(modinfo->libent, modinfo->libstub) - 4, insertSymbols); + scanStart = std::max(modinfo->libentend, modinfo->libstubend); + } + insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, scanEnd, insertSymbols); + } else { + ERROR_LOG(LOADER, "Bad text scan range %08x-%08x", scanStart, scanEnd); } - if (scanEnd >= std::max(modinfo->libent, modinfo->libstub)) { - insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::max(modinfo->libent, modinfo->libstub) - 4, insertSymbols); - scanStart = std::max(modinfo->libentend, modinfo->libstubend); - } - insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, scanEnd, insertSymbols); } if (scan) { From 5c4a1e00fc1c30ea2a13cd011f46dd1efff9057b Mon Sep 17 00:00:00 2001 From: shenweip <1037567878@qq.com> Date: Tue, 14 Jan 2020 08:19:44 +0800 Subject: [PATCH 20/71] BuildFix --- UI/NativeApp.cpp | 6 ++++-- Windows/CaptureDevice.cpp | 7 +++++-- headless/Headless.vcxproj | 13 +++++++------ headless/Headless.vcxproj.filters | 3 +++ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index a855348f35..7de044f077 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -889,8 +889,10 @@ void NativeShutdownGraphics() { #endif #ifdef _WIN32 - delete winCamera; - winCamera = nullptr; + if (winCamera) { + delete winCamera; + winCamera = nullptr; + } #endif ShutdownWebServer(); diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index 45d55a2a23..d05f4bba4d 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -20,6 +20,7 @@ #include "CaptureDevice.h" #include "BufferLock.h" #include "ext/jpge/jpge.h" +#include "CommonTypes.h" #include "Core/HLE/sceUsbCam.h" #include "Core/Config.h" @@ -112,8 +113,7 @@ HRESULT ReaderCallback::OnReadSample( imgConvert( device->imageRGB, dstW, dstH, device->imgRGBLineSizes, pbScanline0, srcW, srcH, srcMFVideoFormat, srcPadding); - } - else { + } else { // If stride < 0, the pointer to the first row of source image is the last row in memory,should invert it in memory. invertedSrcImg = (unsigned char*)av_malloc(av_image_get_buffer_size(getAVVideoFormatbyMFVideoFormat(srcMFVideoFormat), srcW, srcH, 1)); imgInvert(invertedSrcImg, pbScanline0, srcW, srcH, device->deviceParam.videoFormat, lStride); @@ -523,6 +523,8 @@ std::vector WindowsCaptureDevice::getDeviceList(int *pActuallCount) deviceList.push_back(strName); } + CoTaskMemFree(pwstrName); + if (FAILED(hr)) { setError(CAPTUREDEVIDE_ERROR_GETNAMES_FAILED, "Error occurred,gotten " + std::to_string((int)count) + " device names"); if(pActuallCount) @@ -640,6 +642,7 @@ void WindowsCaptureDevice::messageHandler() { std::lock_guard lock(sdMutex); SafeRelease(&m_pSource); SafeRelease(&m_pReader); + CoTaskMemFree(param.ppDevices); delete m_pCallback; MFShutdown(); diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index dd42cbe4fc..94c6d72c8a 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -301,7 +301,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS - ../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native false StreamingSIMDExtensions2 Precise @@ -316,7 +316,7 @@ true true true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) 0x00400000 false true @@ -336,7 +336,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native false NotSet Precise @@ -353,7 +353,7 @@ true true true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) 0x00400000 false true @@ -407,7 +407,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native false NotSet Precise @@ -424,7 +424,7 @@ true true true - shell32.lib;advapi32.lib;gdi32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;shell32.lib;advapi32.lib;gdi32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) @@ -441,6 +441,7 @@ true true + diff --git a/headless/Headless.vcxproj.filters b/headless/Headless.vcxproj.filters index 962db7ee84..f084554e71 100644 --- a/headless/Headless.vcxproj.filters +++ b/headless/Headless.vcxproj.filters @@ -26,6 +26,9 @@ Other Platforms + + Windows + From d1e354ce049558b4ad049ed611052a2fe8a0891d Mon Sep 17 00:00:00 2001 From: shenweip <1037567878@qq.com> Date: Tue, 14 Jan 2020 17:15:11 +0800 Subject: [PATCH 21/71] Improve compatibility. Don't implemente on vista and UWP,they don't have a full support for this. --- Common/OSVersion.cpp | 19 ++++++++ Common/OSVersion.h | 1 + Core/Config.cpp | 2 + Core/HLE/sceUsbCam.cpp | 9 ++-- UI/GameSettingsScreen.cpp | 6 +-- UI/NativeApp.cpp | 9 ++-- Windows/CaptureDevice.cpp | 91 ++++++++++++++++++++++++++++++++++++--- 7 files changed, 120 insertions(+), 17 deletions(-) diff --git a/Common/OSVersion.cpp b/Common/OSVersion.cpp index f66c441365..adc2f1d310 100644 --- a/Common/OSVersion.cpp +++ b/Common/OSVersion.cpp @@ -95,6 +95,25 @@ bool IsVistaOrHigher() { #endif } +bool IsWin7OrHigher() { +#if PPSSPP_PLATFORM(UWP) + return true; +#else + OSVERSIONINFOEX osvi; + DWORDLONG dwlConditionMask = 0; + int op = VER_GREATER_EQUAL; + ZeroMemory(&osvi, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = 6; // Win7 is 6.1 + osvi.dwMinorVersion = 1; + + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op); + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op); + + return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask) != FALSE; +#endif +} + std::string GetWindowsVersion() { const bool IsWindowsXPSP2 = DoesVersionMatchWindows(5, 1, 2, 0, false); const bool IsWindowsXPSP3 = DoesVersionMatchWindows(5, 1, 3, 0, false); diff --git a/Common/OSVersion.h b/Common/OSVersion.h index 1f441e412c..262cba05ac 100644 --- a/Common/OSVersion.h +++ b/Common/OSVersion.h @@ -5,6 +5,7 @@ #ifdef _WIN32 bool IsVistaOrHigher(); +bool IsWin7OrHigher(); bool DoesVersionMatchWindows(uint32_t major, uint32_t minor, uint32_t spMajor, uint32_t spMinor, bool acceptGreater); std::string GetWindowsVersion(); std::string GetWindowsSystemArchitecture(); diff --git a/Core/Config.cpp b/Core/Config.cpp index 8ae8b6f78f..3dd37d49c7 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -706,6 +706,8 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", true, false), #ifdef _WIN32 ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), +#endif +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) ConfigSetting("WinCameraDevice", &g_Config.sWinCameraDevice, "", true, false), #endif ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false), diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 5d0bce11cb..3510985130 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -25,7 +25,7 @@ #include "Core/HLE/sceUsbCam.h" #include "Core/MemMapHelpers.h" -#ifdef _WIN32 +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) #include "Windows/CaptureDevice.h" #undef min #endif @@ -50,7 +50,7 @@ void __UsbCamInit() { } void __UsbCamShutdown() { -#ifdef _WIN32 +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); } @@ -112,7 +112,7 @@ static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { static int sceUsbCamStartVideo() { INFO_LOG(HLE, "UNIMPL sceUsbCamStartVideo"); -#ifdef _WIN32 +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { if (winCamera->isShutDown()) { delete winCamera; @@ -131,7 +131,7 @@ static int sceUsbCamStartVideo() { static int sceUsbCamStopVideo() { INFO_LOG(HLE, "UNIMPL sceUsbCamStopVideo"); -#ifdef _WIN32 +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); #else @@ -155,6 +155,7 @@ static int sceUsbCamReadVideoFrameBlocking(u32 bufAddr, u32 size) { } static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) { + ERROR_LOG(HLE, "%s", winCamera->getErrorMessage().c_str()); std::lock_guard lock(videoBufferMutex); u32 transferSize = std::min(videoBufferLength, size); if (Memory::IsValidRange(bufAddr, size)) { diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index be60930581..ab5975c65e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -68,13 +68,9 @@ #include "Windows/MainWindow.h" #include #include "Windows/W32Util/ShellUtil.h" -#endif - -#ifdef _WIN32 #include "Windows/CaptureDevice.h" #endif - GameSettingsScreen::GameSettingsScreen(std::string gamePath, std::string gameID, bool editThenRestore) : UIDialogScreenWithGameBackground(gamePath), gameID_(gameID), enableReports_(false), editThenRestore_(editThenRestore) { lastVertical_ = UseVerticalLayout(); @@ -267,7 +263,7 @@ void GameSettingsScreen::CreateViews() { softwareGPU->SetEnabled(false); } -#ifdef _WIN32 +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera && winCamera->getDeviceList().size() >= 1) { graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sWinCameraDevice, gr->T("Camera Device"), winCamera->getDeviceList(), nullptr, screenManager())); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 7de044f077..ff4531beb4 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -37,6 +37,9 @@ #if defined(_WIN32) #include "Windows/WindowsAudio.h" #include "Windows/MainWindow.h" +#endif + +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) #include "Windows/CaptureDevice.h" #endif @@ -857,8 +860,8 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) { #endif #endif -#ifdef _WIN32 - if (IsVistaOrHigher()) { +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (IsWin7OrHigher()) { winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); } @@ -888,7 +891,7 @@ void NativeShutdownGraphics() { winAudioBackend = nullptr; #endif -#ifdef _WIN32 +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { delete winCamera; winCamera = nullptr; diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index d05f4bba4d..ef0907f19e 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -24,6 +24,66 @@ #include "Core/HLE/sceUsbCam.h" #include "Core/Config.h" +namespace MFAPI { + HINSTANCE Mflib; + HINSTANCE Mfplatlib; + HINSTANCE Mfreadwritelib; + + typedef HRESULT(WINAPI *MFEnumDeviceSourcesFunc)(IMFAttributes *, IMFActivate ***, UINT32 *); + typedef HRESULT(WINAPI *MFGetStrideForBitmapInfoHeaderFunc)(DWORD, DWORD, LONG *); + typedef HRESULT(WINAPI *MFCreateSourceReaderFromMediaSourceFunc)(IMFMediaSource *, IMFAttributes *, IMFSourceReader **); + typedef HRESULT(WINAPI *MFCopyImageFunc)(BYTE *, LONG, const BYTE *, LONG, DWORD, DWORD); + + MFEnumDeviceSourcesFunc EnumDeviceSources; + MFGetStrideForBitmapInfoHeaderFunc GetStrideForBitmapInfoHeader; + MFCreateSourceReaderFromMediaSourceFunc CreateSourceReaderFromMediaSource; + MFCopyImageFunc CopyImage; +} + +using namespace MFAPI; + +bool RegisterCMPTMFApis(){ + //For the compatibility,these funcs don't be supported on vista. + Mflib = LoadLibrary(L"Mf.dll"); + Mfplatlib = LoadLibrary(L"Mfplat.dll"); + Mfreadwritelib = LoadLibrary(L"Mfreadwrite.dll"); + if (!Mflib || !Mfplatlib || !Mfreadwritelib) + return false; + + EnumDeviceSources = (MFEnumDeviceSourcesFunc)GetProcAddress(Mflib, "MFEnumDeviceSources"); + GetStrideForBitmapInfoHeader = (MFGetStrideForBitmapInfoHeaderFunc)GetProcAddress(Mfplatlib, "MFGetStrideForBitmapInfoHeader"); + MFAPI::CopyImage = (MFCopyImageFunc)GetProcAddress(Mfplatlib, "MFCopyImage"); + CreateSourceReaderFromMediaSource = (MFCreateSourceReaderFromMediaSourceFunc)GetProcAddress(Mfreadwritelib, "MFCreateSourceReaderFromMediaSource"); + if (!EnumDeviceSources || !GetStrideForBitmapInfoHeader || !CreateSourceReaderFromMediaSource || !MFAPI::CopyImage) + return false; + + return true; +} + +bool unRegisterCMPTMFApis() { + if (Mflib) { + FreeLibrary(Mflib); + Mflib = nullptr; + } + + if (Mfplatlib) { + FreeLibrary(Mfplatlib); + Mfplatlib = nullptr; + } + + if (Mfreadwritelib) { + FreeLibrary(Mfreadwritelib); + Mfreadwritelib = nullptr; + } + + EnumDeviceSources = nullptr; + GetStrideForBitmapInfoHeader = nullptr; + CreateSourceReaderFromMediaSource = nullptr; + MFAPI::CopyImage = nullptr; + + return true; +} + WindowsCaptureDevice *winCamera; // TODO: Add more formats, but need some tests. @@ -237,7 +297,7 @@ void ReaderCallback::imgInvert(unsigned char *dst, unsigned char *src, const int } void ReaderCallback::imgInvertRGBA(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { - MFCopyImage(dst, dstStride, src, srcStride, dstStride, h); + MFAPI::CopyImage(dst, dstStride, src, srcStride, dstStride, h); } void ReaderCallback::imgInvertRGB(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { @@ -333,6 +393,11 @@ bool WindowsCaptureDevice::init() { param = { 0 }; IMFAttributes *pAttributes = nullptr; + if (!RegisterCMPTMFApis()) { + setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot register devices"); + return false; + } + hr = MFCreateAttributes(&pAttributes, 1); if (SUCCEEDED(hr)) { switch (type) { @@ -357,7 +422,7 @@ bool WindowsCaptureDevice::init() { } if (SUCCEEDED(hr)) - hr = MFEnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); + hr = EnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); if (FAILED(hr)) { setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot enumerate devices"); @@ -410,7 +475,7 @@ bool WindowsCaptureDevice::start() { hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE); if (SUCCEEDED(hr)) { - hr = MFCreateSourceReaderFromMediaSource( + hr = CreateSourceReaderFromMediaSource( m_pSource, pAttributes, &m_pReader @@ -423,13 +488,28 @@ bool WindowsCaptureDevice::start() { if (SUCCEEDED(hr)) { switch (type) { case CAPTUREDEVIDE_TYPE::VIDEO: + for (DWORD i = 0; ; i++) { + hr = m_pReader->GetNativeMediaType( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + i, + &pType + ); + + if (FAILED(hr)) { break; } + + hr = setDeviceParam(pType); + + if (SUCCEEDED(hr)) + break; + } + /* hr = m_pReader->GetNativeMediaType( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, (DWORD)0xFFFFFFFF,//MF_SOURCE_READER_CURRENT_TYPE_INDEX &pType ); if (SUCCEEDED(hr)) - hr = setDeviceParam(pType); + hr = setDeviceParam(pType);*/ // Don't support on Win7 // Request the first frame, in asnyc mode, OnReadSample will be called when ReadSample completed. if (SUCCEEDED(hr)) { @@ -644,6 +724,7 @@ void WindowsCaptureDevice::messageHandler() { SafeRelease(&m_pReader); CoTaskMemFree(param.ppDevices); delete m_pCallback; + unRegisterCMPTMFApis(); MFShutdown(); CoUninitialize(); @@ -680,7 +761,7 @@ HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride) } if (SUCCEEDED(hr)) { - hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride); + hr = GetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride); } // Set the attribute for later reference. From f39aa47e9bae4dc48f15ab6f8d1bea5a62c4efd6 Mon Sep 17 00:00:00 2001 From: shenweip <1037567878@qq.com> Date: Tue, 14 Jan 2020 17:30:15 +0800 Subject: [PATCH 22/71] Oops --- Core/HLE/sceUsbCam.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 3510985130..50f1147b05 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -155,7 +155,6 @@ static int sceUsbCamReadVideoFrameBlocking(u32 bufAddr, u32 size) { } static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) { - ERROR_LOG(HLE, "%s", winCamera->getErrorMessage().c_str()); std::lock_guard lock(videoBufferMutex); u32 transferSize = std::min(videoBufferLength, size); if (Memory::IsValidRange(bufAddr, size)) { From 9becb4b72ba11323640285443d17f60318b721f0 Mon Sep 17 00:00:00 2001 From: shenweip <1037567878@qq.com> Date: Tue, 14 Jan 2020 18:12:28 +0800 Subject: [PATCH 23/71] BuildFix2 --- unittest/UnitTests.vcxproj | 13 +++++++------ unittest/UnitTests.vcxproj.filters | 10 +++++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index e9975c81d7..5e3c5629a5 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -264,7 +264,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib false Size true @@ -277,7 +277,7 @@ true true true - Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ..\ffmpeg\Windows\x86\lib @@ -291,7 +291,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib false true false @@ -306,7 +306,7 @@ true true true - Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ..\ffmpeg\Windows\x86_64\lib @@ -349,7 +349,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib false true false @@ -364,7 +364,7 @@ true true true - Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ..\ffmpeg\Windows\arm\lib @@ -376,6 +376,7 @@ true true + true diff --git a/unittest/UnitTests.vcxproj.filters b/unittest/UnitTests.vcxproj.filters index a92e8e080c..fd685e86b1 100644 --- a/unittest/UnitTests.vcxproj.filters +++ b/unittest/UnitTests.vcxproj.filters @@ -8,10 +8,18 @@ + + Windows + - + + + {584f25d4-e7a1-461f-ba21-7588497a83f1} + + + \ No newline at end of file From 4395da7c7e11351d75c83f242d5d3f677f22bfed Mon Sep 17 00:00:00 2001 From: iota97 Date: Tue, 14 Jan 2020 19:18:27 +0100 Subject: [PATCH 24/71] Render grid --- UI/TouchControlLayoutScreen.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 65ebb654cf..99ce023414 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -213,6 +213,30 @@ private: float &spacing_; }; +class SnapGrid : public UI::View { +public: + SnapGrid(int leftMargin, int rightMargin, int topMargin, int bottomMargin, u32 color) { + x1 = leftMargin; + x2 = rightMargin; + y1 = topMargin; + y2 = bottomMargin; + col = color; + } + + void Draw(UIContext &dc) override { + if (g_Config.bTouchSnapToGrid) { + for (int x = x1; x < x2; x += g_Config.iTouchSnapGridSize) + dc.Draw()->vLine(x, y1, y2, col); + for (int y = y1; y < y2; y += g_Config.iTouchSnapGridSize) + dc.Draw()->hLine(x1, y, x2, col); + } + } + +private: + int x1, x2, y1, y2; + u32 col; +}; + TouchControlLayoutScreen::TouchControlLayoutScreen() { pickedControl_ = 0; }; @@ -455,6 +479,7 @@ void TouchControlLayoutScreen::CreateViews() { for (size_t i = 0; i < controls_.size(); i++) { root_->Add(controls_[i]); } + root_->Add(new SnapGrid(leftColumnWidth+10, bounds.w, 0, bounds.h, 0x7FFFFFFF)); } // return the control which was picked up by the touchEvent. If a control From f2bfaeb8740d2aba548138c4d9b77932199cc7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 14 Jan 2020 22:31:37 +0100 Subject: [PATCH 25/71] Add another game controller mapping. Fixes #10679 --- assets/gamecontrollerdb.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index cbc06396c4..76ba6498a5 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -78,6 +78,7 @@ d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,righ 5e04000000000000ea02000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4, 81170000000000007e05000000000000,Sega Saturn,x:b0,a:b2,b:b4,y:b6,start:b13,dpleft:b15,dpdown:b16,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,lefttrigger:b10,rightshoulder:b9,righttrigger:a4,righttrigger:b11,leftx:a0,lefty:a2,platform:Mac OS X, bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X, +03000000632500002305000000010000,Redragon Saturn,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X, # Linux 050000004c0500006802000000000000,Sony PLAYSTATION(R)3 Controller,platform:Linux,a:b14,b:b13,x:b15,y:b12,back:b0,guide:b16,start:b3,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpdown:b6,dpleft:b7,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a12,righttrigger:a13, From b69cdea29ea516a5b767ada32fc97ddee220bcf7 Mon Sep 17 00:00:00 2001 From: iota97 Date: Wed, 15 Jan 2020 18:48:43 +0100 Subject: [PATCH 26/71] Fix line rendering in texmode --- UI/TouchControlLayoutScreen.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 99ce023414..1acafbe3a1 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -225,10 +225,13 @@ public: void Draw(UIContext &dc) override { if (g_Config.bTouchSnapToGrid) { + dc.BeginNoTex(); for (int x = x1; x < x2; x += g_Config.iTouchSnapGridSize) dc.Draw()->vLine(x, y1, y2, col); for (int y = y1; y < y2; y += g_Config.iTouchSnapGridSize) dc.Draw()->hLine(x1, y, x2, col); + dc.Flush(); + dc.Begin(); } } @@ -352,6 +355,7 @@ void TouchControlLayoutScreen::CreateViews() { root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + root_->Add(new SnapGrid(leftColumnWidth+10, bounds.w, 0, bounds.h, 0x3FFFFFFF)); Choice *reset = new Choice(di->T("Reset"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 84)); Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 10)); Choice *visibility = new Choice(co->T("Visibility"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 298)); @@ -479,7 +483,6 @@ void TouchControlLayoutScreen::CreateViews() { for (size_t i = 0; i < controls_.size(); i++) { root_->Add(controls_[i]); } - root_->Add(new SnapGrid(leftColumnWidth+10, bounds.w, 0, bounds.h, 0x7FFFFFFF)); } // return the control which was picked up by the touchEvent. If a control From 1332e3c1ccb12dff2116632e6f2c07fc22ba889a Mon Sep 17 00:00:00 2001 From: iota97 Date: Thu, 16 Jan 2020 06:16:46 +0100 Subject: [PATCH 27/71] Flush other UI element draw before changing to noTex --- UI/TouchControlLayoutScreen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 1acafbe3a1..649014a6fd 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -225,6 +225,7 @@ public: void Draw(UIContext &dc) override { if (g_Config.bTouchSnapToGrid) { + dc.Flush(); dc.BeginNoTex(); for (int x = x1; x < x2; x += g_Config.iTouchSnapGridSize) dc.Draw()->vLine(x, y1, y2, col); @@ -355,7 +356,6 @@ void TouchControlLayoutScreen::CreateViews() { root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); - root_->Add(new SnapGrid(leftColumnWidth+10, bounds.w, 0, bounds.h, 0x3FFFFFFF)); Choice *reset = new Choice(di->T("Reset"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 84)); Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 10)); Choice *visibility = new Choice(co->T("Visibility"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 298)); @@ -483,6 +483,8 @@ void TouchControlLayoutScreen::CreateViews() { for (size_t i = 0; i < controls_.size(); i++) { root_->Add(controls_[i]); } + + root_->Add(new SnapGrid(leftColumnWidth+10, bounds.w, 0, bounds.h, 0x3FFFFFFF)); } // return the control which was picked up by the touchEvent. If a control From d79abaa9c5ef0100d24cb4c4a05e2a9387556993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Jan 2020 21:58:11 +0100 Subject: [PATCH 28/71] Add DEFLATE and MD5 to HLE module blacklist. Replaces #12576, fixes #12570 And possibly #12571 --- Core/HLE/sceKernelModule.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 87eeab4b15..8485136e7a 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -93,6 +93,7 @@ static const char * const lieAboutSuccessModules[] = { "disc0:/PSP_GAME/SYSDIR/UPDATE/EBOOT.BIN", }; +// Modules to not load. TODO: Look into loosening this a little (say sceFont). static const char * const blacklistedModules[] = { "sceATRAC3plus_Library", "sceFont_Library", @@ -109,6 +110,8 @@ static const char * const blacklistedModules[] = { "sceNetResolver_Library", "sceNet_Library", "sceSsl_Module", + "sceDEFLATE_Library", + "sceMD5_Library", }; struct VarSymbolImport { From 09e32223c2e75a4b3ceb0be5a5b48274f6ac3df2 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Thu, 9 Jan 2020 15:57:09 +0200 Subject: [PATCH 29/71] Add camera support for linux (V4L2) --- CMakeLists.txt | 2 + Core/Config.cpp | 4 +- Core/Config.h | 2 +- Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/HLE/sceUsb.cpp | 14 +- Core/HLE/sceUsbCam.cpp | 216 ++++++++---- Core/HLE/sceUsbCam.h | 85 ++++- Core/HW/Camera.cpp | 314 ++++++++++++++++++ Core/HW/Camera.h | 66 ++++ UI/GameSettingsScreen.cpp | 10 +- Windows/CaptureDevice.cpp | 6 +- .../src/org/ppsspp/ppsspp/NativeActivity.java | 6 - 13 files changed, 648 insertions(+), 85 deletions(-) create mode 100644 Core/HW/Camera.cpp create mode 100644 Core/HW/Camera.h diff --git a/CMakeLists.txt b/CMakeLists.txt index df14ac85a6..176a4500e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1668,6 +1668,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HW/SimpleAudioDec.h Core/HW/AsyncIOManager.cpp Core/HW/AsyncIOManager.h + Core/HW/Camera.cpp + Core/HW/Camera.h Core/HW/MediaEngine.cpp Core/HW/MediaEngine.h Core/HW/MpegDemux.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index 3dd37d49c7..0e7a27f655 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -707,8 +707,8 @@ static ConfigSetting graphicsSettings[] = { #ifdef _WIN32 ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), #endif -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - ConfigSetting("WinCameraDevice", &g_Config.sWinCameraDevice, "", true, false), +#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX) + ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", true, false), #endif ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false), ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true), diff --git a/Core/Config.h b/Core/Config.h index 3252164101..88be654ea3 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -128,7 +128,7 @@ public: // If not set, will use the "best" device. std::string sVulkanDevice; std::string sD3D11Device; // Windows only - std::string sWinCameraDevice; // Windows only + std::string sCameraDevice; bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 22d258ecad..021cbfb153 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -375,6 +375,7 @@ + @@ -903,6 +904,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 7f096c1b50..d1257aa716 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -740,6 +740,9 @@ HLE\Libraries + + HW + @@ -1373,6 +1376,9 @@ HLE\Libraries + + HW + diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index 361f55f26b..cd2e5e5e16 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -99,6 +99,16 @@ static int sceUsbDeactivate(u32 pid) { return 0; } +static int sceUsbWaitState(int state, int waitMode, u32 timeoutAddr) { + ERROR_LOG(HLE, "UNIMPL sceUsbWaitStat(%i, %i, %08x)", state, waitMode, timeoutAddr); + return sceUsbGetState(); +} + +static int sceUsbWaitStateCB(int state, int waitMode, u32 timeoutAddr) { + ERROR_LOG(HLE, "UNIMPL sceUsbWaitStateCB(%i, %i, %08x)", state, waitMode, timeoutAddr); + return 0; +} + const HLEFunction sceUsb[] = { {0XAE5DE6AF, &WrapI_CUU, "sceUsbStart", 'i', "sxx"}, @@ -108,8 +118,8 @@ const HLEFunction sceUsb[] = {0X112CC951, nullptr, "sceUsbGetDrvState", '?', "" }, {0X586DB82C, &WrapI_U, "sceUsbActivate", 'i', "x" }, {0XC572A9C8, &WrapI_U, "sceUsbDeactivate", 'i', "x" }, - {0X5BE0E002, nullptr, "sceUsbWaitState", '?', "" }, - {0X616F2B61, nullptr, "sceUsbWaitStateCB", '?', "" }, + {0X5BE0E002, &WrapI_IIU, "sceUsbWaitState", '?', "xxx"}, + {0X616F2B61, &WrapI_IIU, "sceUsbWaitStateCB", '?', "xxx"}, {0X1C360735, nullptr, "sceUsbWaitCancel", '?', "" }, }; diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 50f1147b05..da850ce8f9 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -19,55 +19,95 @@ #include #include "base/NativeApp.h" +#include "ppsspp_config.h" #include "Common/ChunkFile.h" #include "Core/HLE/HLE.h" -#include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceUsbCam.h" +#include "Core/HW/Camera.h" #include "Core/MemMapHelpers.h" #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#ifndef NOMINMAX +#define NOMINMAX +#endif #include "Windows/CaptureDevice.h" -#undef min #endif - -PspUsbCamSetupMicParam micParam; -PspUsbCamSetupVideoParam videoParam; +Camera::Config *config; unsigned int videoBufferLength = 0; unsigned int nextVideoFrame = 0; uint8_t *videoBuffer; std::mutex videoBufferMutex; -bool isShutDown = false; enum { VIDEO_BUFFER_SIZE = 40 * 1000, }; void __UsbCamInit() { - videoBuffer = new uint8_t[VIDEO_BUFFER_SIZE]; - isShutDown = false; + config = new Camera::Config; + config->mode = Camera::Mode::Unused; + config->type = Camera::ConfigType::CfNone; + videoBuffer = new uint8_t[VIDEO_BUFFER_SIZE]; +} + +void __UsbCamDoState(PointerWrap &p) { + auto s = p.Section("sceUsbCam", 0, 1); + if (!s) { + return; + } + + p.Do(*config); + if (config->mode == Camera::Mode::Video) { // stillImage? TBD + Camera::startCapture(); + } } void __UsbCamShutdown() { -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - if (winCamera) { - winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); - } -#endif - isShutDown = true; - + Camera::stopCapture(); delete[] videoBuffer; videoBuffer = nullptr; + delete[] config; + config = nullptr; } // TODO: Technically, we should store the videoBuffer into the savestate, if this // module has been initialized. +static int getCameraResolution(Camera::ConfigType type, int *width, int *height) { + if (type == Camera::ConfigType::CfStill || type == Camera::ConfigType::CfVideo) { + switch(config->stillParam.resolution) { + case 0: *width = 160; *height = 120; return 0; + case 1: *width = 176; *height = 144; return 0; + case 2: *width = 320; *height = 240; return 0; + case 3: *width = 352; *height = 288; return 0; + case 4: *width = 640; *height = 480; return 0; + case 5: *width =1024; *height = 768; return 0; + case 6: *width =1280; *height = 960; return 0; + case 7: *width = 480; *height = 272; return 0; + case 8: *width = 360; *height = 272; return 0; + } + } else if (type == Camera::ConfigType::CfStillEx || type == Camera::ConfigType::CfVideoEx) { + switch(config->stillExParam.resolution) { + case 0: *width = 160; *height = 120; return 0; + case 1: *width = 176; *height = 144; return 0; + case 2: *width = 320; *height = 240; return 0; + case 3: *width = 352; *height = 288; return 0; + case 4: *width = 360; *height = 272; return 0; + case 5: *width = 480; *height = 272; return 0; + case 6: *width = 640; *height = 480; return 0; + case 7: *width =1024; *height = 768; return 0; + case 8: *width =1280; *height = 960; return 0; + } + } + *width = 0; *height = 0; return 1; +} + + static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupMic"); - if (Memory::IsValidRange(paramAddr, sizeof(micParam))) { - Memory::ReadStruct(paramAddr, &micParam); + if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) { + Memory::ReadStruct(paramAddr, &config->micParam); } return 0; } @@ -89,59 +129,37 @@ static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) { Memory::Write_U8(i & 0xFF, bufAddr + i); } } - hleEatMicro(1000000 / micParam.frequency * (size / 2)); + hleEatMicro(1000000 / config->micParam.frequency * (size / 2)); return size; } static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo"); - if (Memory::IsValidRange(paramAddr, sizeof(videoParam))) { - Memory::ReadStruct(paramAddr, &videoParam); + if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoParam))) { + Memory::ReadStruct(paramAddr, &config->videoParam); } + config->type = Camera::ConfigType::CfVideo; + return 0; +} - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - size: %d", videoParam.size); - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - resolution: %d", videoParam.resolution); - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - framesize: %d", videoParam.framesize); - - std::lock_guard lock(videoBufferMutex); - videoBufferLength = sizeof(sceUsbCamDummyImage); - memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); - memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage)); +static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) { + if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoExParam))) { + Memory::ReadStruct(paramAddr, &config->videoExParam); + } + config->type = Camera::ConfigType::CfVideoEx; return 0; } static int sceUsbCamStartVideo() { - INFO_LOG(HLE, "UNIMPL sceUsbCamStartVideo"); -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - if (winCamera) { - if (winCamera->isShutDown()) { - delete winCamera; - winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); - winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); - } - - winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::START, nullptr }); - } - -#else - System_SendMessage("camera_command", "startVideo"); -#endif + std::lock_guard lock(videoBufferMutex); + videoBufferLength = sizeof(sceUsbCamDummyImage); + memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); + memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage)); + Camera::startCapture(); return 0; } static int sceUsbCamStopVideo() { - INFO_LOG(HLE, "UNIMPL sceUsbCamStopVideo"); -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - if (winCamera) - winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); -#else - System_SendMessage("camera_command", "stopVideo"); -#endif - return 0; -} - -static int sceUsbCamAutoImageReverseSW(int rev) { - INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW"); + Camera::stopCapture(); return 0; } @@ -169,6 +187,29 @@ static int sceUsbCamPollReadVideoFrameEnd() { return nextVideoFrame; } +static int sceUsbCamSetupStill(u32 paramAddr) { + INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStill"); + if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillParam))) { + Memory::ReadStruct(paramAddr, &config->stillParam); + } + config->type = Camera::ConfigType::CfStill; + return 0; +} + +static int sceUsbCamSetupStillEx(u32 paramAddr) { + INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStillEx"); + if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillExParam))) { + Memory::ReadStruct(paramAddr, &config->stillExParam); + } + config->type = Camera::ConfigType::CfStillEx; + return 0; +} + +static int sceUsbCamAutoImageReverseSW(int rev) { + INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW"); + return 0; +} + const HLEFunction sceUsbCam[] = { { 0X03ED7A82, &WrapI_UUI, "sceUsbCamSetupMic", 'i', "xxi" }, @@ -183,7 +224,7 @@ const HLEFunction sceUsbCam[] = { 0X08AEE98A, nullptr, "sceUsbCamSetMicGain", '?', "" }, { 0X17F7B2FB, &WrapI_UUI, "sceUsbCamSetupVideo", 'i', "xxi" }, - { 0XCFE9E999, nullptr, "sceUsbCamSetupVideoEx", '?', "" }, + { 0XCFE9E999, &WrapI_UUI, "sceUsbCamSetupVideoEx", 'i', "xxi" }, { 0X574A8C3F, &WrapI_V, "sceUsbCamStartVideo", 'i', "" }, { 0X6CF32CB9, &WrapI_V, "sceUsbCamStopVideo", 'i', "" }, { 0X7DAC0C71, &WrapI_UU, "sceUsbCamReadVideoFrameBlocking", 'i', "xx" }, @@ -192,8 +233,8 @@ const HLEFunction sceUsbCam[] = { 0X41E73E95, &WrapI_V, "sceUsbCamPollReadVideoFrameEnd", 'i', "" }, { 0XDF9D0C92, nullptr, "sceUsbCamGetReadVideoFrameSize", '?', "" }, - { 0X3F0CF289, nullptr, "sceUsbCamSetupStill", '?', "" }, - { 0X0A41A298, nullptr, "sceUsbCamSetupStillEx", '?', "" }, + { 0X3F0CF289, &WrapI_U, "sceUsbCamSetupStill", 'i', "x" }, + { 0X0A41A298, &WrapI_U, "sceUsbCamSetupStillEx", 'i', "x" }, { 0X61BE5CAC, nullptr, "sceUsbCamStillInputBlocking", '?', "" }, { 0XFB0A6C5D, nullptr, "sceUsbCamStillInput", '?', "" }, { 0X7563AFA1, nullptr, "sceUsbCamStillWaitInputEnd", '?', "" }, @@ -233,10 +274,65 @@ void Register_sceUsbCam() RegisterModule("sceUsbCam", ARRAY_SIZE(sceUsbCam), sceUsbCam); } +std::vector Camera::getDeviceList() { + #if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + return __v4l_getDeviceList(); + #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera) { + return winCamera->getDeviceList(); + } + #endif + return std::vector(); +} + +int Camera::startCapture() { + int width, height; + getCameraResolution(config->type, &width, &height); + INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height); + + config->mode = Camera::Mode::Video; + #if PPSSPP_PLATFORM(ANDROID) + System_SendMessage("camera_command", "startVideo"); + #elif PPSSPP_PLATFORM(LINUX) + __v4l_startCapture(width, height); + #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera) { + if (winCamera->isShutDown()) { + delete winCamera; + winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); + } + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::START, nullptr }); + } + #else + ERROR_LOG(HLE, "%s not implemented", __FUNCTION__); + #endif + return 0; +} + +int Camera::stopCapture() { + INFO_LOG(HLE, "%s", __FUNCTION__); + #if PPSSPP_PLATFORM(ANDROID) + System_SendMessage("camera_command", "stopVideo"); + #elif PPSSPP_PLATFORM(LINUX) + __v4l_stopCapture(); + #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera) { + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); + } + #else + ERROR_LOG(HLE, "%s not implemented", __FUNCTION__); + #endif + config->mode = Camera::Mode::Unused; + return 0; +} + void Camera::pushCameraImage(long long length, unsigned char* image) { std::lock_guard lock(videoBufferMutex); - if (isShutDown) + if (!videoBuffer) { return; + } memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); if (length > VIDEO_BUFFER_SIZE) { videoBufferLength = 0; diff --git a/Core/HLE/sceUsbCam.h b/Core/HLE/sceUsbCam.h index ebb5388db6..3d6bfbcf3f 100644 --- a/Core/HLE/sceUsbCam.h +++ b/Core/HLE/sceUsbCam.h @@ -17,16 +17,18 @@ #pragma once +#include +#include +#include +#include "Core/HLE/FunctionWrappers.h" + void Register_sceUsbCam(); void __UsbCamInit(); +void __UsbCamDoState(PointerWrap &p); void __UsbCamShutdown(); -namespace Camera { - void pushCameraImage(long long length, unsigned char *image); -} - -typedef struct PspUsbCamSetupMicParam { +typedef struct { int size; u32 unk; int gain; @@ -34,7 +36,30 @@ typedef struct PspUsbCamSetupMicParam { int frequency; } PspUsbCamSetupMicParam; -typedef struct PspUsbCamSetupVideoParam { +typedef struct { + int size; + int resolution; + int jpegsize; + int reverseflags; + int delay; + int complevel; +} PspUsbCamSetupStillParam; + +typedef struct { + int size; + u32 unk; + int resolution; + int jpegsize; + int complevel; + u32 unk2; + u32 unk3; + int flip; + int mirror; + int delay; + u32 unk4[5]; +} PspUsbCamSetupStillExParam; + +typedef struct { int size; int resolution; int framerate; @@ -49,6 +74,54 @@ typedef struct PspUsbCamSetupVideoParam { int evlevel; } PspUsbCamSetupVideoParam; +typedef struct { + int size; + u32 unk; + int resolution; + int framerate; + u32 unk2; + u32 unk3; + int wb; + int saturation; + int brightness; + int contrast; + int sharpness; + u32 unk4; + u32 unk5; + u32 unk6[3]; + int effectmode; + u32 unk7; + u32 unk8; + u32 unk9; + u32 unk10; + u32 unk11; + int framesize; + u32 unk12; + int evlevel; +} PspUsbCamSetupVideoExParam; + +namespace Camera { + enum Mode { Unused, Still, Video }; + enum ConfigType { CfNone, CfStill, CfStillEx, CfVideo, CfVideoEx }; + + typedef struct { + Mode mode; + ConfigType type; + PspUsbCamSetupMicParam micParam; + union { + PspUsbCamSetupStillParam stillParam; + PspUsbCamSetupStillExParam stillExParam; + PspUsbCamSetupVideoParam videoParam; + PspUsbCamSetupVideoExParam videoExParam; + }; + } Config; + + std::vector getDeviceList(); + int startCapture(); + int stopCapture(); + void pushCameraImage(long long length, unsigned char *image); +} + static const unsigned char sceUsbCamDummyImage[] = { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x68, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, diff --git a/Core/HW/Camera.cpp b/Core/HW/Camera.cpp new file mode 100644 index 0000000000..2260f452e8 --- /dev/null +++ b/Core/HW/Camera.cpp @@ -0,0 +1,314 @@ +// Copyright (c) 2020- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Camera.h" + +#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + +std::vector __v4l_getDeviceList() { + std::vector deviceList; + for (int i = 0; i < 64; i++) { + char path[256]; + snprintf(path, sizeof(path), "/dev/video%d", i); + if (access(path, F_OK) < 0) { + break; + } + int fd = -1; + if((fd = open(path, O_RDONLY)) < 0) { + ERROR_LOG(HLE, "Cannot open '%s'; errno=%d(%s)", path, errno, strerror(errno)); + continue; + } + struct v4l2_capability video_cap; + if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) < 0) { + ERROR_LOG(HLE, "VIDIOC_QUERYCAP"); + goto cont; + } else { + char device[256]; + snprintf(device, sizeof(device), "%d:%s", i, video_cap.card); + deviceList.push_back(device); + } +cont: + close(fd); + fd = -1; + } + return deviceList; +} + +void convert_frame(unsigned char *inData, AVPixelFormat inFormat, unsigned char **outData, int *outLen) { + // resize + uint8_t *src[4] = {0}; + uint8_t *dst[4] = {0}; + int srcStride[4], dstStride[4]; + + unsigned char *rgbData = (unsigned char*)malloc(v4l_ideal_width * v4l_ideal_height * 3); + + av_image_fill_linesizes(srcStride, inFormat, v4l_hw_width); + av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, v4l_ideal_width); + + av_image_fill_pointers(src, inFormat, v4l_hw_height, inData, srcStride); + av_image_fill_pointers(dst, AV_PIX_FMT_RGB24, v4l_ideal_height, rgbData, dstStride); + + sws_scale(sws_context, + src, srcStride, 0, v4l_height_fixed_aspect, + dst, dstStride); + + // compress jpeg + *outLen = v4l_ideal_width * v4l_ideal_height * 3; + *outData = (unsigned char*)malloc(*outLen); + + jpge::params params; + params.m_quality = 60; + params.m_subsampling = jpge::H2V2; + params.m_two_pass_flag = false; + jpge::compress_image_to_jpeg_file_in_memory( + *outData, *outLen, v4l_ideal_width, v4l_ideal_height, 3, rgbData, params); + free(rgbData); +} + +void *v4l_loop(void *data) { + setCurrentThreadName("v4l_loop"); + while (v4l_fd >= 0) { + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + if (ioctl(v4l_fd, VIDIOC_DQBUF, &buf) == -1) { + ERROR_LOG(HLE, "VIDIOC_DQBUF; errno=%d(%s)", errno, strerror(errno)); + switch (errno) { + case EAGAIN: + continue; + default: + return NULL; + } + } + + unsigned char *jpegData; + int jpegLen; + + if (v4l_format == V4L2_PIX_FMT_YUYV) { + convert_frame((unsigned char*)v4l_buffer, AV_PIX_FMT_YUYV422, &jpegData, &jpegLen); + } else if (v4l_format == V4L2_PIX_FMT_MJPEG) { + // decompress jpeg + int width, height, req_comps; + unsigned char *rgbData = jpgd::decompress_jpeg_image_from_memory( + (unsigned char*)v4l_buffer, buf.bytesused, &width, &height, &req_comps, 3); + + convert_frame(rgbData, AV_PIX_FMT_RGB24, &jpegData, &jpegLen); + free(rgbData); + } + + Camera::pushCameraImage(jpegLen, jpegData); + free(jpegData); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) { + ERROR_LOG(HLE, "VIDIOC_QBUF"); + return NULL; + } + } +} + +int __v4l_startCapture(int ideal_width, int ideal_height) { + if (v4l_fd >= 0) { + __v4l_stopCapture(); + } + v4l_ideal_width = ideal_width; + v4l_ideal_height = ideal_height; + + int dev_index = 0; + char dev_name[64]; + sscanf(g_Config.sCameraDevice.c_str(), "%d:", &dev_index); + snprintf(dev_name, sizeof(dev_name), "/dev/video%d", dev_index); + + if ((v4l_fd = open(dev_name, O_RDWR)) == -1) { + ERROR_LOG(HLE, "Cannot open '%s'; errno=%d(%s)", dev_name, errno, strerror(errno)); + return -1; + } + + struct v4l2_capability cap; + memset(&cap, 0, sizeof(cap)); + if (ioctl(v4l_fd, VIDIOC_QUERYCAP, &cap) == -1) { + ERROR_LOG(HLE, "VIDIOC_QUERYCAP"); + return -1; + } + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + ERROR_LOG(HLE, "V4L2_CAP_VIDEO_CAPTURE"); + return -1; + } + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + ERROR_LOG(HLE, "V4L2_CAP_STREAMING"); + return -1; + } + + struct v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.pixelformat = 0; + + // select a pixel format + struct v4l2_fmtdesc desc; + memset(&desc, 0, sizeof(desc)); + desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + while (ioctl(v4l_fd, VIDIOC_ENUM_FMT, &desc) == 0) { + desc.index++; + INFO_LOG(HLE, "V4L2: pixel format supported: %s", desc.description); + if (fmt.fmt.pix.pixelformat != 0) { + continue; + } else if (desc.pixelformat == V4L2_PIX_FMT_YUYV + || desc.pixelformat == V4L2_PIX_FMT_MJPEG) { + INFO_LOG(HLE, "V4L2: %s selected", desc.description); + fmt.fmt.pix.pixelformat = desc.pixelformat; + v4l_format = desc.pixelformat; + } + } + if (fmt.fmt.pix.pixelformat == 0) { + ERROR_LOG(HLE, "V4L2: No supported format found"); + return -1; + } + + // select a frame size + fmt.fmt.pix.width = 0; + fmt.fmt.pix.height = 0; + struct v4l2_frmsizeenum frmsize; + memset(&frmsize, 0, sizeof(frmsize)); + frmsize.pixel_format = fmt.fmt.pix.pixelformat; + while (ioctl(v4l_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { + frmsize.index++; + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + INFO_LOG(HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height); + if (fmt.fmt.pix.width != 0 && fmt.fmt.pix.height != 0) { + continue; + } else if (frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height) { + fmt.fmt.pix.width = frmsize.discrete.width; + fmt.fmt.pix.height = frmsize.discrete.height; + } + } + } + + if (fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0) { + fmt.fmt.pix.width = ideal_width; + fmt.fmt.pix.height = ideal_height; + } + INFO_LOG(HLE, "V4L2: asking for %dx%d", fmt.fmt.pix.width, fmt.fmt.pix.height); + if (ioctl(v4l_fd, VIDIOC_S_FMT, &fmt) == -1) { + ERROR_LOG(HLE, "VIDIOC_S_FMT"); + return -1; + } + v4l_hw_width = fmt.fmt.pix.width; + v4l_hw_height = fmt.fmt.pix.height; + INFO_LOG(HLE, "V4L2: will receive %dx%d", v4l_hw_width, v4l_hw_height); + v4l_height_fixed_aspect = v4l_hw_width * ideal_height / ideal_width; + INFO_LOG(HLE, "V4L2: will use %dx%d", v4l_hw_width, v4l_height_fixed_aspect); + + // create a converter context + if (v4l_format == V4L2_PIX_FMT_YUYV) { + sws_context = sws_getContext( + v4l_hw_width, v4l_height_fixed_aspect, AV_PIX_FMT_YUYV422, + ideal_width, ideal_height, AV_PIX_FMT_RGB24, + SWS_BICUBIC, NULL, NULL, NULL); + } else if (v4l_format == V4L2_PIX_FMT_MJPEG) { + sws_context = sws_getContext( + v4l_hw_width, v4l_height_fixed_aspect, AV_PIX_FMT_RGB24, + ideal_width, ideal_height, AV_PIX_FMT_RGB24, + SWS_BICUBIC, NULL, NULL, NULL); + } + + + struct v4l2_requestbuffers req; + memset(&req, 0, sizeof(req)); + req.count = 1; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + if (ioctl(v4l_fd, VIDIOC_REQBUFS, &req) == -1) { + ERROR_LOG(HLE, "VIDIOC_REQBUFS"); + return -1; + } + + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = 0; + if (ioctl(v4l_fd, VIDIOC_QUERYBUF, &buf) == -1) { + ERROR_LOG(HLE, "VIDIOC_QUERYBUF"); + return -1; + } + + v4l_length = buf.length; + v4l_buffer = mmap(NULL, + v4l_length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + v4l_fd, buf.m.offset); + if (v4l_buffer == MAP_FAILED) { + ERROR_LOG(HLE, "MAP_FAILED"); + return -1; + } + + memset(&buf, 0, sizeof(buf)); + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.type = type; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = 0; + if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) { + ERROR_LOG(HLE, "VIDIOC_QBUF"); + return -1; + } + + if (ioctl(v4l_fd, VIDIOC_STREAMON, &type) == -1) { + ERROR_LOG(HLE, "VIDIOC_STREAMON"); + return -1; + } + + pthread_create(&v4l_thread, NULL, v4l_loop, NULL); + + return 0; +} + +int __v4l_stopCapture() { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (v4l_fd < 0) { + goto exit; + } + + if (ioctl(v4l_fd, VIDIOC_STREAMOFF, &type) == -1) { + ERROR_LOG(HLE, "VIDIOC_STREAMOFF"); + goto exit; + } + + if (munmap(v4l_buffer, v4l_length) == -1) { + ERROR_LOG(HLE, "munmap"); + goto exit; + } + + if (close(v4l_fd) == -1) { + ERROR_LOG(HLE, "close"); + goto exit; + } + + v4l_fd = -1; + //pthread_join(v4l_thread, NULL); + +exit: + v4l_fd = -1; + return 0; +} + +#endif // PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) diff --git a/Core/HW/Camera.h b/Core/HW/Camera.h new file mode 100644 index 0000000000..fb082c10cd --- /dev/null +++ b/Core/HW/Camera.h @@ -0,0 +1,66 @@ +// Copyright (c) 2020- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "ppsspp_config.h" +#include "Core/Config.h" +#include "Log.h" + +#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "thread/threadutil.h" +#include "Core/HLE/sceUsbCam.h" + +#include +#include "ext/jpge/jpgd.h" +#include "ext/jpge/jpge.h" + +extern "C" { +#include "libswscale/swscale.h" +#include "libavutil/imgutils.h" +} + + static int v4l_fd = -1; + static uint32_t v4l_format; + static int v4l_hw_width; + static int v4l_hw_height; + static int v4l_height_fixed_aspect; + static int v4l_ideal_width; + static int v4l_ideal_height; + + static pthread_t v4l_thread; + static void *v4l_buffer; + static int v4l_length; + static struct SwsContext *sws_context; + + std::vector __v4l_getDeviceList(); + int __v4l_startCapture(int width, int height); + int __v4l_stopCapture(); +#endif diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 71c468467e..90130177a2 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -58,6 +58,7 @@ #include "Core/Reporting.h" #include "Core/TextureReplacer.h" #include "Core/WebServer.h" +#include "Core/HLE/sceUsbCam.h" #include "GPU/Common/PostShader.h" #include "android/jni/TestRunner.h" #include "GPU/GPUInterface.h" @@ -68,7 +69,6 @@ #include "Windows/MainWindow.h" #include #include "Windows/W32Util/ShellUtil.h" -#include "Windows/CaptureDevice.h" #endif GameSettingsScreen::GameSettingsScreen(std::string gamePath, std::string gameID, bool editThenRestore) @@ -263,14 +263,14 @@ void GameSettingsScreen::CreateViews() { softwareGPU->SetEnabled(false); } -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - if (winCamera && winCamera->getDeviceList().size() >= 1) { +#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX) + std::vector cameraList = Camera::getDeviceList(); + if (cameraList.size() >= 1) { graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); - PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sWinCameraDevice, gr->T("Camera Device"), winCamera->getDeviceList(), nullptr, screenManager())); + PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, nullptr, screenManager())); } #endif - graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), gr->GetName(), screenManager())); diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index ef0907f19e..429f4270ee 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -283,7 +283,7 @@ void ReaderCallback::imgConvert( void ReaderCallback::imgInvert(unsigned char *dst, unsigned char *src, const int &srcW, const int &srcH, const GUID &srcFormat, const int &srcStride) { AVPixelFormat srcAvFormat = getAVVideoFormatbyMFVideoFormat(srcFormat); int dstLineSizes[4] = { 0, 0, 0, 0 }; - + av_image_fill_linesizes(dstLineSizes, srcAvFormat, srcW); if(srcFormat == MFVideoFormat_RGB32) @@ -453,7 +453,7 @@ bool WindowsCaptureDevice::start() { switch (state) { case CAPTUREDEVIDE_STATE::STOPPED: for (auto &name : deviceList) { - if (name == g_Config.sWinCameraDevice) { + if (name == g_Config.sCameraDevice) { selection = count; break; } @@ -510,7 +510,7 @@ bool WindowsCaptureDevice::start() { ); if (SUCCEEDED(hr)) hr = setDeviceParam(pType);*/ // Don't support on Win7 - + // Request the first frame, in asnyc mode, OnReadSample will be called when ReadSample completed. if (SUCCEEDED(hr)) { hr = m_pReader->ReadSample( diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 5676afee23..251a0f5ddc 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -1409,12 +1409,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Only keep the screen bright ingame. window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - } else if (command.equals("event")) { - if (params.equals("exitgame")) { - if (mCameraHelper != null) { - mCameraHelper.stopCamera(); - } - } } return false; } From 24a8077616ff5eecea7146b213aed0eac74dad1f Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Fri, 17 Jan 2020 15:53:56 +0200 Subject: [PATCH 30/71] SDL app icon --- SDL/SDLMain.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 90db5d4195..ccaf57ab0b 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -24,6 +24,7 @@ SDLJoystick *joystick = NULL; #include "base/logging.h" #include "base/timeutil.h" #include "ext/glslang/glslang/Public/ShaderLang.h" +#include "image/png_load.h" #include "input/input_state.h" #include "input/keycodes.h" #include "net/resolve.h" @@ -629,6 +630,20 @@ int main(int argc, char *argv[]) { SDL_SetWindowTitle(window, (app_name_nice + " " + PPSSPP_GIT_VERSION).c_str()); + char iconPath[PATH_MAX]; + snprintf(iconPath, PATH_MAX, "%sassets/icon_regular_72.png", SDL_GetBasePath() ? SDL_GetBasePath() : ""); + int width = 0, height = 0; + unsigned char *imageData; + if (pngLoad(iconPath, &width, &height, &imageData, false) == 1) { + SDL_Surface *surface = SDL_CreateRGBSurface(0, width, height, 32, + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + memcpy(surface->pixels, imageData, width*height*4); + SDL_SetWindowIcon(window, surface); + SDL_FreeSurface(surface); + free(imageData); + imageData = NULL; + } + // Since we render from the main thread, there's nothing done here, but we call it to avoid confusion. if (!graphicsContext->InitFromRenderThread(&error_message)) { printf("Init from thread error: '%s'\n", error_message.c_str()); From dd1de41db7bbd8eaeda5f696127ccededa375d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Jan 2020 18:43:29 +0100 Subject: [PATCH 31/71] Revert "Android EGL config: Always require 8888, stencil if version >= ICS" This reverts commit 015c314fb18fe002b74a161a75c3e71e296117e8. Helps #11922 --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 251a0f5ddc..480b06bb9c 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -557,10 +557,13 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mGLSurfaceView.setEGLConfigChooser(new NativeEGLConfigChooser()); } else { // Tried to mess around with config choosers (NativeEGLConfigChooser) here but fail completely on Xperia Play. - // On the other hand, I think from ICS we should be safe to at least require 8888 and stencil... - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !Build.MANUFACTURER.equals("Amazon")) { - mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 8); - } + + // Then I tried to require 8888/16/8 but that backfired too, does not work on Mali 450 which is + // used in popular TVs and boxes like Mi Box. So we'll just get what we get, I guess... + + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !Build.MANUFACTURER.equals("Amazon")) { + // mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 8); + // } } mGLSurfaceView.setRenderer(nativeRenderer); setContentView(mGLSurfaceView); From 7cd60549dd1bbd2db61964cd8e31ff13a5897ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 19 Jan 2020 17:34:21 +0800 Subject: [PATCH 32/71] Initial support for vulkan on macOS ( MoltenVK ) --- CMakeLists.txt | 13 +++++++------ Common/Vulkan/VulkanContext.cpp | 19 +++++++++++++++++-- Common/Vulkan/VulkanContext.h | 3 +++ Common/Vulkan/VulkanLoader.cpp | 11 +++++++++++ Common/Vulkan/VulkanLoader.h | 4 ++++ SDL/SDLCocoaMetalLayer.h | 5 +++++ SDL/SDLCocoaMetalLayer.mm | 16 ++++++++++++++++ SDL/SDLGLGraphicsContext.h | 2 -- SDL/SDLMain.cpp | 8 +++++--- SDL/SDLVulkanGraphicsContext.cpp | 10 ++++++++-- SDL/SDLVulkanGraphicsContext.h | 2 -- 11 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 SDL/SDLCocoaMetalLayer.h create mode 100644 SDL/SDLCocoaMetalLayer.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 176a4500e9..573235b787 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,8 +84,8 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Android") set(ANDROID ON) endif() -# We only support Vulkan on Unix, Android and Windows. -if(ANDROID OR WIN32 OR (UNIX AND NOT APPLE AND NOT ARM_NO_VULKAN)) +# We only support Vulkan on Unix, macOS (by MoltenVK), Android and Windows. +if(ANDROID OR WIN32 OR (UNIX AND NOT ARM_NO_VULKAN)) set(VULKAN ON) endif() @@ -188,6 +188,7 @@ include(FindThreads) if(APPLE) find_library(COCOA_LIBRARY Cocoa) + find_library(QUARTZ_CORE_LIBRARY QuartzCore) endif() include_directories("${CMAKE_SOURCE_DIR}") @@ -828,8 +829,8 @@ elseif(TARGET SDL2::SDL2) ) set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2) if(APPLE) - set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm) - set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY}) + set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm SDL/SDLCocoaMetalLayer.h SDL/SDLCocoaMetalLayer.mm) + set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY}) elseif(USING_EGL) set(nativeExtraLibs ${nativeExtraLibs} pthread) endif() @@ -1955,7 +1956,7 @@ if(HEADLESS) headless/SDLHeadlessHost.cpp headless/SDLHeadlessHost.h ) - target_link_libraries(PPSSPPHeadless ${COCOA_LIBRARY} ${LinkCommon}) + target_link_libraries(PPSSPPHeadless ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${LinkCommon}) setup_target_project(PPSSPPHeadless headless) endif() @@ -1970,7 +1971,7 @@ if(UNITTEST) Core/MIPS/ARM/ArmRegCache.cpp Core/MIPS/ARM/ArmRegCacheFPU.cpp ) - target_link_libraries(unitTest ${COCOA_LIBRARY} ${LinkCommon} Common) + target_link_libraries(unitTest ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${LinkCommon} Common) setup_target_project(unitTest unittest) endif() diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index be07dd66c6..cb4dc0b33a 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -125,6 +125,11 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) { instance_extensions_enabled_.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); } #endif +#if defined(VK_USE_PLATFORM_METAL_EXT) + if (IsInstanceExtensionAvailable(VK_EXT_METAL_SURFACE_EXTENSION_NAME)) { + instance_extensions_enabled_.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); + } +#endif #endif if (flags_ & VULKAN_FLAG_VALIDATE) { @@ -760,6 +765,16 @@ VkResult VulkanContext::ReinitSurface() { return vkCreateAndroidSurfaceKHR(instance_, &android, nullptr, &surface_); } #endif +#if defined(VK_USE_PLATFORM_METAL_EXT) + case WINDOWSYSTEM_METAL_EXT: + { + VkMetalSurfaceCreateInfoEXT metal{ VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT }; + metal.flags = 0; + metal.pLayer = winsysData1_; + metal.pNext = winsysData2_; + return vkCreateMetalSurfaceEXT(instance_, &metal, nullptr, &surface_); + } +#endif #if defined(VK_USE_PLATFORM_XLIB_KHR) case WINDOWSYSTEM_XLIB: { @@ -968,10 +983,10 @@ bool VulkanContext::InitSwapchain() { // Application must settle for fewer images than desired: desiredNumberOfSwapChainImages = surfCapabilities_.maxImageCount; } - + // We mostly follow the practices from // https://arm-software.github.io/vulkan_best_practice_for_mobile_developers/samples/surface_rotation/surface_rotation_tutorial.html - // + // VkSurfaceTransformFlagBitsKHR preTransform; std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms); std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform); diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 63fa5108e7..fccc3dc98e 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -34,6 +34,9 @@ enum WindowSystem { #ifdef __ANDROID__ WINDOWSYSTEM_ANDROID, #endif +#ifdef VK_USE_PLATFORM_METAL_EXT + WINDOWSYSTEM_METAL_EXT, +#endif #ifdef VK_USE_PLATFORM_XLIB_KHR WINDOWSYSTEM_XLIB, #endif diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index 93aff18cec..a523ab4968 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -176,6 +176,9 @@ PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR; #elif defined(_WIN32) PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; #endif +#if defined(VK_USE_PLATFORM_METAL_EXT) +PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; +#endif #if defined(VK_USE_PLATFORM_XLIB_KHR) PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; #endif @@ -236,10 +239,14 @@ static const char *device_name_blacklist[] = { }; static const char *so_names[] = { +#if defined(__APPLE__) + "libMoltenVK.dylib", +#else "libvulkan.so", #if !defined(__ANDROID__) "libvulkan.so.1", #endif +#endif }; void VulkanSetAvailable(bool available) { @@ -310,6 +317,8 @@ bool VulkanMayBeAvailable() { const char * const platformSurfaceExtension = VK_KHR_WIN32_SURFACE_EXTENSION_NAME; #elif defined(__ANDROID__) const char *platformSurfaceExtension = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; +#elif defined(VK_USE_PLATFORM_METAL_EXT) + const char * const platformSurfaceExtension = VK_EXT_METAL_SURFACE_EXTENSION_NAME; #else const char *platformSurfaceExtension = 0; #endif @@ -515,6 +524,8 @@ void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanDeviceExtensio LOAD_INSTANCE_FUNC(instance, vkCreateWin32SurfaceKHR); #elif defined(__ANDROID__) LOAD_INSTANCE_FUNC(instance, vkCreateAndroidSurfaceKHR); +#elif defined(VK_USE_PLATFORM_METAL_EXT) + LOAD_INSTANCE_FUNC(instance, vkCreateMetalSurfaceEXT); #endif #if defined(VK_USE_PLATFORM_XLIB_KHR) LOAD_INSTANCE_FUNC(instance, vkCreateXlibSurfaceKHR); diff --git a/Common/Vulkan/VulkanLoader.h b/Common/Vulkan/VulkanLoader.h index 3d1ccae86b..99b560cfa6 100644 --- a/Common/Vulkan/VulkanLoader.h +++ b/Common/Vulkan/VulkanLoader.h @@ -25,6 +25,8 @@ #ifndef NOMINMAX #define NOMINMAX #endif +#elif defined(__APPLE__) +#define VK_USE_PLATFORM_METAL_EXT #endif #define VK_NO_PROTOTYPES @@ -173,6 +175,8 @@ extern PFN_vkCmdExecuteCommands vkCmdExecuteCommands; extern PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR; #elif defined(_WIN32) extern PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; +#elif defined(VK_USE_PLATFORM_METAL_EXT) +extern PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; #endif #if defined(VK_USE_PLATFORM_XLIB_KHR) extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; diff --git a/SDL/SDLCocoaMetalLayer.h b/SDL/SDLCocoaMetalLayer.h new file mode 100644 index 0000000000..28c7849e56 --- /dev/null +++ b/SDL/SDLCocoaMetalLayer.h @@ -0,0 +1,5 @@ +#pragma once + +extern "C" { +void *makeWindowMetalCompatible(void *window); +} diff --git a/SDL/SDLCocoaMetalLayer.mm b/SDL/SDLCocoaMetalLayer.mm new file mode 100644 index 0000000000..ce1b9cd206 --- /dev/null +++ b/SDL/SDLCocoaMetalLayer.mm @@ -0,0 +1,16 @@ +#import +#import + +#include "base/logging.h" +#include "SDLCocoaMetalLayer.h" + +void *makeWindowMetalCompatible(void *window) { + NSView *view = ((NSWindow *)window).contentView; + assert([view isKindOfClass:[NSView class]]); + + if (![view.layer isKindOfClass:[CAMetalLayer class]]) + { + [view setLayer:[CAMetalLayer layer]]; + } + return view.layer; +} diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h index 651f7a534b..666aa72774 100644 --- a/SDL/SDLGLGraphicsContext.h +++ b/SDL/SDLGLGraphicsContext.h @@ -1,6 +1,4 @@ -#if !defined(__APPLE__) #include "SDL_syswm.h" -#endif #include "SDL.h" #include "thin3d/GLRenderManager.h" diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 90db5d4195..ee12c75442 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -35,9 +35,7 @@ SDLJoystick *joystick = NULL; #include "thread/threadutil.h" #include "math.h" -#if !defined(__APPLE__) #include "SDL_syswm.h" -#endif #if defined(VK_USE_PLATFORM_XLIB_KHR) #include @@ -66,6 +64,7 @@ static int g_QuitRequested = 0; static int g_DesktopWidth = 0; static int g_DesktopHeight = 0; +static int g_RefreshRate = 60000; int getDisplayNumber(void) { int displayNumber = 0; @@ -308,13 +307,15 @@ int System_GetPropertyInt(SystemProperty prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return 44100; case SYSPROP_DISPLAY_REFRESH_RATE: - return 60000; + return g_RefreshRate; case SYSPROP_DEVICE_TYPE: #if defined(MOBILE_DEVICE) return DEVICE_TYPE_MOBILE; #else return DEVICE_TYPE_DESKTOP; #endif + case SYSPROP_DISPLAY_COUNT: + return SDL_GetNumVideoDisplays(); default: return -1; } @@ -509,6 +510,7 @@ int main(int argc, char *argv[]) { } g_DesktopWidth = displayMode.w; g_DesktopHeight = displayMode.h; + g_RefreshRate = (int)(displayMode.refresh_rate * 1000); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp index 3cf55a78c5..29d68ffc04 100644 --- a/SDL/SDLVulkanGraphicsContext.cpp +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -8,6 +8,9 @@ #include "Core/System.h" #include "SDLVulkanGraphicsContext.h" +#if defined(VK_USE_PLATFORM_METAL_EXT) +#include "SDLCocoaMetalLayer.h" +#endif bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message) { window = SDL_CreateWindow("Initializing Vulkan...", x, y, pixel_xres, pixel_yres, mode); @@ -50,7 +53,6 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, return false; } -#if !defined(__APPLE__) SDL_SysWMinfo sys_info{}; SDL_VERSION(&sys_info.version); //Set SDL version if (!SDL_GetWindowWMInfo(window, &sys_info)) { @@ -71,13 +73,17 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, case SDL_SYSWM_WAYLAND: vulkan_->InitSurface(WINDOWSYSTEM_WAYLAND, (void*)sys_info.info.wl.display, (void *)sys_info.info.wl.surface); break; +#endif +#if defined(VK_USE_PLATFORM_METAL_EXT) + case SDL_SYSWM_COCOA: + vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(sys_info.info.cocoa.window), nullptr); + break; #endif default: fprintf(stderr, "Vulkan subsystem %d not supported\n", sys_info.subsystem); exit(1); break; } -#endif if (!vulkan_->InitObjects()) { *error_message = vulkan_->InitError(); diff --git a/SDL/SDLVulkanGraphicsContext.h b/SDL/SDLVulkanGraphicsContext.h index 5a29e6c3df..6ecfecfe1e 100644 --- a/SDL/SDLVulkanGraphicsContext.h +++ b/SDL/SDLVulkanGraphicsContext.h @@ -1,7 +1,5 @@ #include "SDL.h" -#if !defined(__APPLE__) #include "SDL_syswm.h" -#endif #include "Common/GraphicsContext.h" #include "Common/Vulkan/VulkanContext.h" From a34d35d70595623f101e82a09781b3ecea8e3063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 20 Jan 2020 14:43:08 +0800 Subject: [PATCH 33/71] Add support for iOS ( completely untested ) --- SDL/SDLCocoaMetalLayer.mm | 21 +++++++++++++++++++++ SDL/SDLVulkanGraphicsContext.cpp | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/SDL/SDLCocoaMetalLayer.mm b/SDL/SDLCocoaMetalLayer.mm index ce1b9cd206..d0df82716b 100644 --- a/SDL/SDLCocoaMetalLayer.mm +++ b/SDL/SDLCocoaMetalLayer.mm @@ -1,10 +1,16 @@ +#if defined(PPSSPP_PLATFORM_MAC) #import +#else +#import +#endif #import #include "base/logging.h" #include "SDLCocoaMetalLayer.h" void *makeWindowMetalCompatible(void *window) { + // https://github.com/KhronosGroup/MoltenVK/issues/78#issuecomment-371118536 +#if defined(PPSSPP_PLATFORM_MAC) NSView *view = ((NSWindow *)window).contentView; assert([view isKindOfClass:[NSView class]]); @@ -13,4 +19,19 @@ void *makeWindowMetalCompatible(void *window) { [view setLayer:[CAMetalLayer layer]]; } return view.layer; +#else + UIView *view = (UIView *)window; + assert([view isKindOfClass:[UIView class]]); + + CAMetalLayer *metalLayer = [CAMetalLayer new]; + + CGSize viewSize = view.frame.size; + metalLayer.frame = view.frame; + metalLayer.opaque = true; + metalLayer.framebufferOnly = true; + metalLayer.drawableSize = viewSize; + metalLayer.pixelFormat = (MTLPixelFormat)80;//BGRA8Unorm==80 + [view.layer addSublayer:metalLayer]; + return metalLayer; +#endif } diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp index 29d68ffc04..02123b105d 100644 --- a/SDL/SDLVulkanGraphicsContext.cpp +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -75,9 +75,15 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, break; #endif #if defined(VK_USE_PLATFORM_METAL_EXT) +#if defined(PPSSPP_PLATFORM_MAC) case SDL_SYSWM_COCOA: vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(sys_info.info.cocoa.window), nullptr); break; +#else + case SDL_SYSWM_UIKIT: + vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(sys_info.info.uikit.window), nullptr); + break; +#endif #endif default: fprintf(stderr, "Vulkan subsystem %d not supported\n", sys_info.subsystem); From 81b42df7c243fddc488d372054743d4931ebbd90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 20 Jan 2020 22:54:28 +0100 Subject: [PATCH 34/71] D3D11: Fix mistake in UI vertex shader for non-textured geometry. Fixes #12587. --- ext/native/thin3d/thin3d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 340157708d..2d3fdcc2f8 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -262,7 +262,7 @@ static const std::vector vsCol = { "};\n" "VS_OUTPUT main(VS_INPUT input) {\n" " VS_OUTPUT output;\n" - " output.Position = mul(float4(input.Position, 1.0), WorldViewProj);\n" + " output.Position = mul(WorldViewProj, float4(input.Position, 1.0));\n" " output.Color0 = input.Color0;\n" " return output;\n" "}\n" From 97c81ac5a2313651b1ab4e54d3d3a97f85e40344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 21 Jan 2020 20:57:26 +0100 Subject: [PATCH 35/71] Partial workaround for #12588. Cameras are now gone from settings though after playing one game... --- Windows/CaptureDevice.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index 429f4270ee..13883d6dfe 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -483,7 +483,7 @@ bool WindowsCaptureDevice::start() { } if (!m_pReader) - hr = -1; + hr = E_FAIL; if (SUCCEEDED(hr)) { switch (type) { @@ -730,6 +730,9 @@ void WindowsCaptureDevice::messageHandler() { CoUninitialize(); updateState(CAPTUREDEVIDE_STATE::SHUTDOWN); + + // Make sure we don't try to loop through the devices later in getDeviceList()... + param = {}; } //----------------------------------------------------------------------------- From 3f585782f161eb22d212c01bc9f5b303fc0ef5d4 Mon Sep 17 00:00:00 2001 From: shenweip <1037567878@qq.com> Date: Wed, 22 Jan 2020 21:13:50 +0800 Subject: [PATCH 36/71] Don't shut down camera when exiting games,also re-enumerating devices if received WM_DEVICECHANGE. --- Core/HLE/sceUsbCam.cpp | 1 - UI/NativeApp.cpp | 2 + Windows/CaptureDevice.cpp | 114 ++++++++++++++++++++++++++------------ Windows/CaptureDevice.h | 9 ++- Windows/MainWindow.cpp | 2 + 5 files changed, 90 insertions(+), 38 deletions(-) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index da850ce8f9..6690c90d50 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -319,7 +319,6 @@ int Camera::stopCapture() { #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); - winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); } #else ERROR_LOG(HLE, "%s not implemented", __FUNCTION__); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index ff4531beb4..32c98d75b7 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -893,6 +893,8 @@ void NativeShutdownGraphics() { #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); + while (!winCamera->isShutDown()) {};// Wait for shutting down. delete winCamera; winCamera = nullptr; } diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index 13883d6dfe..fa750e0bf5 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -24,6 +24,8 @@ #include "Core/HLE/sceUsbCam.h" #include "Core/Config.h" +bool isDeviceChanged = false; + namespace MFAPI { HINSTANCE Mflib; HINSTANCE Mfplatlib; @@ -355,6 +357,7 @@ WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) : error(CAPTUREDEVIDE_ERROR_NO_ERROR), errorMessage(""), state(CAPTUREDEVIDE_STATE::UNINITIALIZED) { + param = { 0 }; switch (type) { case CAPTUREDEVIDE_TYPE::VIDEO: @@ -387,50 +390,26 @@ WindowsCaptureDevice::~WindowsCaptureDevice() { break; } } +void WindowsCaptureDevice::CheckDevices() { + isDeviceChanged = true; +} bool WindowsCaptureDevice::init() { HRESULT hr = S_OK; - param = { 0 }; - IMFAttributes *pAttributes = nullptr; if (!RegisterCMPTMFApis()) { setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot register devices"); return false; } - - hr = MFCreateAttributes(&pAttributes, 1); - if (SUCCEEDED(hr)) { - switch (type) { - case CAPTUREDEVIDE_TYPE::VIDEO: - hr = pAttributes->SetGUID( - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID - ); - - break; - case CAPTUREDEVIDE_TYPE::AUDIO: - hr = pAttributes->SetGUID( - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID - ); - - break; - default: - setError(CAPTUREDEVIDE_ERROR_UNKNOWN_TYPE, "Unknown device type"); - return false; - } - } - - if (SUCCEEDED(hr)) - hr = EnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); + std::unique_lock lock(paramMutex); + hr = enumDevices(); + lock.unlock(); if (FAILED(hr)) { setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot enumerate devices"); - SafeRelease(&pAttributes); return false; } - SafeRelease(&pAttributes); updateState(CAPTUREDEVIDE_STATE::STOPPED); return true; } @@ -441,7 +420,16 @@ bool WindowsCaptureDevice::start() { IMFMediaType *pType = nullptr; UINT32 selection = 0; UINT32 count = 0; - std::vector deviceList = getDeviceList(); + + // Release old sources first(if any). + SafeRelease(&m_pSource); + SafeRelease(&m_pReader); + if (m_pCallback) { + delete m_pCallback; + m_pCallback = nullptr; + } + // Need to re-enumerate the list,because old sources were released. + std::vector deviceList = getDeviceList(true); if (deviceList.size() < 1) { setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Has no device"); @@ -511,7 +499,7 @@ bool WindowsCaptureDevice::start() { if (SUCCEEDED(hr)) hr = setDeviceParam(pType);*/ // Don't support on Win7 - // Request the first frame, in asnyc mode, OnReadSample will be called when ReadSample completed. + // Request the first frame, in async mode, OnReadSample will be called when ReadSample completed. if (SUCCEEDED(hr)) { hr = m_pReader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, @@ -572,7 +560,7 @@ bool WindowsCaptureDevice::stop() { return true; }; -std::vector WindowsCaptureDevice::getDeviceList(int *pActuallCount) { +std::vector WindowsCaptureDevice::getDeviceList(bool forceEnum, int *pActuallCount) { HRESULT hr = S_OK; UINT32 count = 0; LPWSTR pwstrName = nullptr; @@ -581,6 +569,23 @@ std::vector WindowsCaptureDevice::getDeviceList(int *pActuallCount) DWORD dwMinSize = 0; std::vector deviceList; + if (isDeviceChanged || forceEnum) { + std::unique_lock lock(paramMutex); + for (DWORD i = 0; i < param.count; i++) { + SafeRelease(¶m.ppDevices[i]); + } + CoTaskMemFree(param.ppDevices); // Null pointer is okay. + + hr = enumDevices(); + + lock.unlock(); + + if (SUCCEEDED(hr)) + isDeviceChanged = false; + else + return deviceList; + } + for (; count < param.count; count++) { hr = param.ppDevices[count]->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, @@ -722,17 +727,54 @@ void WindowsCaptureDevice::messageHandler() { std::lock_guard lock(sdMutex); SafeRelease(&m_pSource); SafeRelease(&m_pReader); - CoTaskMemFree(param.ppDevices); delete m_pCallback; unRegisterCMPTMFApis(); + std::unique_lock lock2(paramMutex); + for (DWORD i = 0; i < param.count; i++) { + SafeRelease(¶m.ppDevices[i]); + } + CoTaskMemFree(param.ppDevices); // Null pointer is okay. + lock2.unlock(); + MFShutdown(); CoUninitialize(); updateState(CAPTUREDEVIDE_STATE::SHUTDOWN); +} - // Make sure we don't try to loop through the devices later in getDeviceList()... - param = {}; +HRESULT WindowsCaptureDevice::enumDevices() { + HRESULT hr = S_OK; + IMFAttributes *pAttributes = nullptr; + + hr = MFCreateAttributes(&pAttributes, 1); + if (SUCCEEDED(hr)) { + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + hr = pAttributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID + ); + + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + hr = pAttributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID + ); + + break; + default: + setError(CAPTUREDEVIDE_ERROR_UNKNOWN_TYPE, "Unknown device type"); + return E_FAIL; + } + } + if (SUCCEEDED(hr)) { + hr = EnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); + } + + SafeRelease(&pAttributes); + return hr; } //----------------------------------------------------------------------------- diff --git a/Windows/CaptureDevice.h b/Windows/CaptureDevice.h index ea76a971b5..06bb86c376 100644 --- a/Windows/CaptureDevice.h +++ b/Windows/CaptureDevice.h @@ -160,6 +160,8 @@ public: WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type); ~WindowsCaptureDevice(); + static void CheckDevices(); + bool init(); bool start(); bool stop(); @@ -168,7 +170,7 @@ public: std::string getErrorMessage() const { return errorMessage; } int getDeviceCounts() const { return param.count; } // Get a list contained friendly device name. - std::vector getDeviceList(int *pActuallCount = nullptr); + std::vector getDeviceList(bool forceEnum = false, int *pActuallCount = nullptr); void setError(const CAPTUREDEVIDE_ERROR &newError, const std::string &newErrorMessage) { error = newError; errorMessage = newErrorMessage; } void setSelction(const UINT32 &selection) { param.selection = selection; } @@ -180,6 +182,8 @@ public: void sendMessage(CAPTUREDEVIDE_MESSAGE message); CAPTUREDEVIDE_MESSAGE getMessage(); + HRESULT enumDevices(); + friend class ReaderCallback; protected: @@ -208,6 +212,9 @@ protected: // For the shutdown event safety. std::mutex sdMutex; +// Param updating synchronously. + std::mutex paramMutex; + // Camera only unsigned char *imageRGB; int imgRGBLineSizes[4]; diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index fe66c287c8..2462568dba 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -72,6 +72,7 @@ #include "Windows/W32Util/ShellUtil.h" #include "Windows/W32Util/Misc.h" #include "Windows/RawInput.h" +#include "Windows/CaptureDevice.h" #include "Windows/TouchInputHandler.h" #include "Windows/MainWindowMenu.h" #include "GPU/GPUInterface.h" @@ -848,6 +849,7 @@ namespace MainWindow #ifndef _M_ARM DinputDevice::CheckDevices(); #endif + WindowsCaptureDevice::CheckDevices(); return DefWindowProc(hWnd, message, wParam, lParam); case WM_VERYSLEEPY_MSG: From 5fbf2d7713734bacf8645f50bcd6740e53f4e0cd Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Thu, 9 Jan 2020 15:57:09 +0200 Subject: [PATCH 37/71] Add camera/location support for iOS Add front camera support for Android Allow to change the active camera in-game --- CMakeLists.txt | 9 +- Core/Config.cpp | 2 - Core/HLE/sceKernel.cpp | 1 + Core/HLE/sceUsbCam.cpp | 33 +++- Core/HLE/sceUsbCam.h | 1 + Core/HLE/sceUsbGps.cpp | 31 ++-- Core/HLE/sceUsbGps.h | 1 + Core/HW/Camera.cpp | 7 +- UI/GameSettingsScreen.cpp | 8 +- UI/GameSettingsScreen.h | 1 + UI/NativeApp.cpp | 2 +- android/jni/app-android.cpp | 36 ++++- .../src/org/ppsspp/ppsspp/CameraHelper.java | 33 +++- .../src/org/ppsspp/ppsspp/LocationHelper.java | 3 +- android/src/org/ppsspp/ppsspp/NativeApp.java | 6 +- ext/native/base/NativeApp.h | 4 +- ios/CameraHelper.h | 17 +++ ios/CameraHelper.mm | 144 ++++++++++++++++++ ios/LocationHelper.h | 18 +++ ios/LocationHelper.mm | 34 +++++ ios/PPSSPP-Info.plist | 6 + ios/ViewController.h | 9 +- ios/ViewController.mm | 35 +++++ ios/main.mm | 12 ++ 24 files changed, 409 insertions(+), 44 deletions(-) create mode 100644 ios/CameraHelper.h create mode 100644 ios/CameraHelper.mm create mode 100644 ios/LocationHelper.h create mode 100644 ios/LocationHelper.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 573235b787..e724ec6939 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -745,6 +745,10 @@ elseif(IOS) ios/ViewController.h ios/iOSCoreAudio.mm ios/iOSCoreAudio.h + ios/CameraHelper.mm + ios/CameraHelper.h + ios/LocationHelper.mm + ios/LocationHelper.h ios/PPSSPPUIApplication.h ios/PPSSPPUIApplication.mm ios/SmartKeyboardMap.cpp @@ -755,7 +759,7 @@ elseif(IOS) ios/iCade/iCadeReaderView.m ios/iCade/iCadeState.h ) - set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation") + set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreVideo -framework CoreMedia" ) if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework") set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController") endif() @@ -770,7 +774,8 @@ elseif(IOS) set_source_files_properties(ios/PPSSPPUIApplication.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(ios/iCade/iCadeReaderView.m PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(ios/main.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) - + set_source_files_properties(ios/CameraHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) + set_source_files_properties(ios/LocationHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set(TargetBin PPSSPP) elseif(USING_QT_UI) diff --git a/Core/Config.cpp b/Core/Config.cpp index 0e7a27f655..4458042234 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -707,9 +707,7 @@ static ConfigSetting graphicsSettings[] = { #ifdef _WIN32 ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), #endif -#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX) ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", true, false), -#endif ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false), ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true), ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true), diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index c375c59a04..2effbb5a26 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -169,6 +169,7 @@ void __KernelShutdown() kernelObjects.Clear(); __UsbCamShutdown(); + __UsbGpsShutdown(); __AudioCodecShutdown(); __VideoPmpShutdown(); diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index da850ce8f9..fdcf4d07c0 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -205,8 +205,18 @@ static int sceUsbCamSetupStillEx(u32 paramAddr) { return 0; } -static int sceUsbCamAutoImageReverseSW(int rev) { - INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW"); +static int sceUsbCamAutoImageReverseSW(int on) { + INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW: %d", on); + return 0; +} + +static int sceUsbCamGetLensDirection() { + INFO_LOG(HLE, "UNIMPL sceUsbCamGetLensDirection"); + return 0; +} + +static int sceUsbCamSetReverseMode(int reverseflags) { + INFO_LOG(HLE, "UNIMPL sceUsbCamSetReverseMode %d", reverseflags); return 0; } @@ -244,7 +254,7 @@ const HLEFunction sceUsbCam[] = { 0XF93C4669, &WrapI_I, "sceUsbCamAutoImageReverseSW", 'i', "i" }, { 0X11A1F128, nullptr, "sceUsbCamGetAutoImageReverseState", '?', "" }, - { 0X4C34F553, nullptr, "sceUsbCamGetLensDirection", '?', "" }, + { 0X4C34F553, &WrapI_V, "sceUsbCamGetLensDirection", 'i', "" }, { 0X383E9FA8, nullptr, "sceUsbCamGetSaturation", '?', "" }, { 0X6E205974, nullptr, "sceUsbCamSetSaturation", '?', "" }, @@ -259,7 +269,7 @@ const HLEFunction sceUsbCam[] = { 0X2BCD50C0, nullptr, "sceUsbCamGetEvLevel", '?', "" }, { 0X1D686870, nullptr, "sceUsbCamSetEvLevel", '?', "" }, { 0XD5279339, nullptr, "sceUsbCamGetReverseMode", '?', "" }, - { 0X951BEDF5, nullptr, "sceUsbCamSetReverseMode", '?', "" }, + { 0X951BEDF5, &WrapI_I, "sceUsbCamSetReverseMode", 'i', "i" }, { 0X9E8AAF8D, nullptr, "sceUsbCamGetZoom", '?', "" }, { 0XC484901F, nullptr, "sceUsbCamSetZoom", '?', "" }, { 0XAA7D94BA, nullptr, "sceUsbCamGetAntiFlicker", '?', "" }, @@ -275,7 +285,9 @@ void Register_sceUsbCam() } std::vector Camera::getDeviceList() { - #if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) + return __cameraGetDeviceList(); + #elif PPSSPP_PLATFORM(LINUX) return __v4l_getDeviceList(); #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { @@ -291,7 +303,7 @@ int Camera::startCapture() { INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height); config->mode = Camera::Mode::Video; - #if PPSSPP_PLATFORM(ANDROID) + #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) System_SendMessage("camera_command", "startVideo"); #elif PPSSPP_PLATFORM(LINUX) __v4l_startCapture(width, height); @@ -312,7 +324,7 @@ int Camera::startCapture() { int Camera::stopCapture() { INFO_LOG(HLE, "%s", __FUNCTION__); - #if PPSSPP_PLATFORM(ANDROID) + #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) System_SendMessage("camera_command", "stopVideo"); #elif PPSSPP_PLATFORM(LINUX) __v4l_stopCapture(); @@ -328,6 +340,13 @@ int Camera::stopCapture() { return 0; } +void Camera::onCameraDeviceChange() { + if (config != nullptr && config->mode == Camera::Mode::Video) { + stopCapture(); + startCapture(); + } +} + void Camera::pushCameraImage(long long length, unsigned char* image) { std::lock_guard lock(videoBufferMutex); if (!videoBuffer) { diff --git a/Core/HLE/sceUsbCam.h b/Core/HLE/sceUsbCam.h index 3d6bfbcf3f..ad1ebde215 100644 --- a/Core/HLE/sceUsbCam.h +++ b/Core/HLE/sceUsbCam.h @@ -117,6 +117,7 @@ namespace Camera { } Config; std::vector getDeviceList(); + void onCameraDeviceChange(); int startCapture(); int stopCapture(); void pushCameraImage(long long length, unsigned char *image); diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index 4213e7abd1..6a04812309 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -43,6 +43,15 @@ void __UsbGpsDoState(PointerWrap &p) { p.Do(gpsStatus); } +void __UsbGpsShutdown() { + gpsStatus = GPS_STATE_OFF; + System_SendMessage("gps_command", "close"); +}; + +static int sceUsbGpsGetInitDataLocation(u32 addr) { + return 0; +} + static int sceUsbGpsGetState(u32 stateAddr) { if (Memory::IsValidAddress(stateAddr)) { Memory::Write_U32(gpsStatus, stateAddr); @@ -77,17 +86,17 @@ static int sceUsbGpsGetData(u32 gpsDataAddr, u32 satDataAddr) { const HLEFunction sceUsbGps[] = { - {0X268F95CA, nullptr, "sceUsbGpsSetInitDataLocation", '?', "" }, - {0X31F95CDE, nullptr, "sceUsbGpsGetPowerSaveMode", '?', "" }, - {0X54D26AA4, nullptr, "sceUsbGpsGetInitDataLocation", '?', "" }, - {0X63D1F89D, nullptr, "sceUsbGpsResetInitialPosition", '?', "" }, - {0X69E4AAA8, nullptr, "sceUsbGpsSaveInitData", '?', "" }, - {0X6EED4811, &WrapI_V, "sceUsbGpsClose", 'i', "" }, - {0X7C16AC3A, &WrapI_U, "sceUsbGpsGetState", 'i', "x"}, - {0X934EC2B2, &WrapI_UU, "sceUsbGpsGetData", 'i', "xx" }, - {0X9D8F99E8, nullptr, "sceUsbGpsSetPowerSaveMode", '?', "" }, - {0X9F267D34, &WrapI_V, "sceUsbGpsOpen", 'i', "" }, - {0XA259CD67, nullptr, "sceUsbGpsReset", '?', "" }, + {0X268F95CA, nullptr, "sceUsbGpsSetInitDataLocation", '?', "" }, + {0X31F95CDE, nullptr, "sceUsbGpsGetPowerSaveMode", '?', "" }, + {0X54D26AA4, &WrapI_U, "sceUsbGpsGetInitDataLocation", 'i', "x" }, + {0X63D1F89D, nullptr, "sceUsbGpsResetInitialPosition", '?', "" }, + {0X69E4AAA8, nullptr, "sceUsbGpsSaveInitData", '?', "" }, + {0X6EED4811, &WrapI_V, "sceUsbGpsClose", 'i', "" }, + {0X7C16AC3A, &WrapI_U, "sceUsbGpsGetState", 'i', "x"}, + {0X934EC2B2, &WrapI_UU, "sceUsbGpsGetData", 'i', "xx" }, + {0X9D8F99E8, nullptr, "sceUsbGpsSetPowerSaveMode", '?', "" }, + {0X9F267D34, &WrapI_V, "sceUsbGpsOpen", 'i', "" }, + {0XA259CD67, nullptr, "sceUsbGpsReset", '?', "" }, }; void Register_sceUsbGps() diff --git a/Core/HLE/sceUsbGps.h b/Core/HLE/sceUsbGps.h index c9f1b7e8f0..fa7059b146 100644 --- a/Core/HLE/sceUsbGps.h +++ b/Core/HLE/sceUsbGps.h @@ -21,6 +21,7 @@ void Register_sceUsbGps(); void __UsbGpsInit(); void __UsbGpsDoState(PointerWrap &p); +void __UsbGpsShutdown(); #pragma pack(push, 1) diff --git a/Core/HW/Camera.cpp b/Core/HW/Camera.cpp index 2260f452e8..c03da48bd4 100644 --- a/Core/HW/Camera.cpp +++ b/Core/HW/Camera.cpp @@ -192,9 +192,10 @@ int __v4l_startCapture(int ideal_width, int ideal_height) { frmsize.index++; if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { INFO_LOG(HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height); - if (fmt.fmt.pix.width != 0 && fmt.fmt.pix.height != 0) { - continue; - } else if (frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height) { + if (frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height + && fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0 + || frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height + && frmsize.discrete.width < fmt.fmt.pix.width && frmsize.discrete.height < fmt.fmt.pix.height) { fmt.fmt.pix.width = frmsize.discrete.width; fmt.fmt.pix.height = frmsize.discrete.height; } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 90130177a2..f1bda2a044 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -263,13 +263,12 @@ void GameSettingsScreen::CreateViews() { softwareGPU->SetEnabled(false); } -#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX) std::vector cameraList = Camera::getDeviceList(); if (cameraList.size() >= 1) { graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, nullptr, screenManager())); + cameraChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCameraDeviceChange); } -#endif graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; @@ -1200,6 +1199,11 @@ UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) { + Camera::onCameraDeviceChange(); + return UI::EVENT_DONE; +} + UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { I18NCategory *a = GetI18NCategory("Audio"); if (g_Config.sAudioDevice == a->T("Auto")) { diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index e4b6232e5e..3b9cae93e8 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -97,6 +97,7 @@ private: UI::EventReturn OnRenderingMode(UI::EventParams &e); UI::EventReturn OnRenderingBackend(UI::EventParams &e); UI::EventReturn OnRenderingDevice(UI::EventParams &e); + UI::EventReturn OnCameraDeviceChange(UI::EventParams& e); UI::EventReturn OnAudioDevice(UI::EventParams &e); UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); #if PPSSPP_PLATFORM(ANDROID) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index ff4531beb4..43f237534b 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1358,7 +1358,7 @@ void NativeShutdown() { // I think we handle most globals correctly or correct-enough now. } -void PushNewGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time) { +void SetGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time) { GPS::setGpsData(latitude, longitude, altitude, speed, bearing, time); } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 1f9655b3a7..a464418405 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -921,12 +921,40 @@ extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferHei return desiredBackbufferSizeY; } -extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushNewGpsData(JNIEnv *, jclass, - jfloat latitude, jfloat longitude, jfloat altitude, jfloat speed, jfloat bearing, jlong time) { - PushNewGpsData(latitude, longitude, altitude, speed, bearing, time); + +std::vector __cameraGetDeviceList() { + jclass cameraClass = findClass("org/ppsspp/ppsspp/CameraHelper"); + jmethodID deviceListMethod = getEnv()->GetStaticMethodID(cameraClass, "getDeviceList", "()Ljava/util/ArrayList;"); + jobject deviceListObject = getEnv()->CallStaticObjectMethod(cameraClass, deviceListMethod); + jclass arrayListClass = getEnv()->FindClass("java/util/ArrayList"); + jmethodID arrayListSize = getEnv()->GetMethodID(arrayListClass, "size", "()I"); + jmethodID arrayListGet = getEnv()->GetMethodID(arrayListClass, "get", "(I)Ljava/lang/Object;"); + + jint arrayListObjectLen = getEnv()->CallIntMethod(deviceListObject, arrayListSize); + std::vector deviceListVector; + + for (int i=0; i < arrayListObjectLen; i++) { + jstring dev = static_cast(getEnv()->CallObjectMethod(deviceListObject, arrayListGet, i)); + const char* cdev = getEnv()->GetStringUTFChars(dev, nullptr); + deviceListVector.push_back(cdev); + getEnv()->ReleaseStringUTFChars(dev, cdev); + getEnv()->DeleteLocalRef(dev); + } + return deviceListVector; } -extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImage(JNIEnv *env, jclass, +extern "C" jint Java_org_ppsspp_ppsspp_NativeApp_getSelectedCamera(JNIEnv *, jclass) { + int cameraId = 0; + sscanf(g_Config.sCameraDevice.c_str(), "%d:", &cameraId); + return cameraId; +} + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setGpsDataAndroid(JNIEnv *, jclass, + jfloat latitude, jfloat longitude, jfloat altitude, jfloat speed, jfloat bearing, jlong time) { + SetGpsData(latitude, longitude, altitude, speed, bearing, time); +} + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImageAndroid(JNIEnv *env, jclass, jbyteArray image) { if (image != NULL) { diff --git a/android/src/org/ppsspp/ppsspp/CameraHelper.java b/android/src/org/ppsspp/ppsspp/CameraHelper.java index 9979b81660..bd18be1dc0 100644 --- a/android/src/org/ppsspp/ppsspp/CameraHelper.java +++ b/android/src/org/ppsspp/ppsspp/CameraHelper.java @@ -15,6 +15,7 @@ import android.view.WindowManager; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.List; @TargetApi(23) @@ -24,6 +25,7 @@ class CameraHelper { private Display mDisplay; private Camera mCamera = null; private boolean mIsCameraRunning = false; + private int mCameraFacing = 0; private int mCameraOrientation = 0; private Camera.Size mPreviewSize = null; private long mLastFrameTime = 0; @@ -38,7 +40,11 @@ class CameraHelper { case Surface.ROTATION_180: displayDegrees = 180; break; case Surface.ROTATION_270: displayDegrees = 270; break; } - return (mCameraOrientation - displayDegrees + 360) % 360; + if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + return (mCameraOrientation + displayDegrees) % 360; + } else { + return (mCameraOrientation - displayDegrees + 360) % 360; + } } static byte[] rotateNV21(final byte[] input, final int inWidth, final int inHeight, @@ -117,7 +123,7 @@ class CameraHelper { // convert to Jpeg Rect crop = new Rect(0, 0, targetW, targetH); yuvImage.compressToJpeg(crop, 80, baos); - NativeApp.pushCameraImage(baos.toByteArray()); + NativeApp.pushCameraImageAndroid(baos.toByteArray()); try { baos.close(); } catch (IOException e) { @@ -132,14 +138,29 @@ class CameraHelper { mSurfaceTexture = new SurfaceTexture(10); } + static ArrayList getDeviceList() { + ArrayList deviceList = new ArrayList<>(); + int nrCam = Camera.getNumberOfCameras(); + for (int index = 0; index < nrCam; index++) { + Camera.CameraInfo info = new Camera.CameraInfo(); + Camera.getCameraInfo(index, info); + String devName = index + ":" + (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK ? "Back Camera" : "Front Camera"); + deviceList.add(devName); + } + return deviceList; + } + void startCamera() { - Log.d(TAG, "startCamera"); try { - Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); - Camera.getCameraInfo(0, info); + int cameraId = NativeApp.getSelectedCamera(); + Log.d(TAG, "startCamera: " + cameraId); + + Camera.CameraInfo info = new Camera.CameraInfo(); + Camera.getCameraInfo(cameraId, info); + mCameraFacing = info.facing; mCameraOrientation = info.orientation; - mCamera = Camera.open(); + mCamera = Camera.open(cameraId); Camera.Parameters param = mCamera.getParameters(); // Set preview size diff --git a/android/src/org/ppsspp/ppsspp/LocationHelper.java b/android/src/org/ppsspp/ppsspp/LocationHelper.java index a71bcaeb19..73e9350f07 100644 --- a/android/src/org/ppsspp/ppsspp/LocationHelper.java +++ b/android/src/org/ppsspp/ppsspp/LocationHelper.java @@ -50,12 +50,13 @@ class LocationHelper implements LocationListener { public void onLocationChanged(Location location) { float latitude = (float) location.getLatitude(); float longitude = (float) location.getLongitude(); + // Android altitude is in meters above the WGS 84 reference ellipsoid float altitude = (float) location.getAltitude(); float speed = location.getSpeed(); float bearing = location.getBearing(); long time = location.getTime() / 1000; // ms to s !! - NativeApp.pushNewGpsData(latitude, longitude, altitude, speed, bearing, time); + NativeApp.setGpsDataAndroid(latitude, longitude, altitude, speed, bearing, time); } @Override diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 140f3edd59..5db8abdc6d 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -51,7 +51,7 @@ public class NativeApp { public static native String queryConfig(String queryName); - public static native void pushNewGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long time); - - public static native void pushCameraImage(byte[] image); + public static native int getSelectedCamera(); + public static native void setGpsDataAndroid(float latitude, float longitude, float altitude, float speed, float bearing, long time); + public static native void pushCameraImageAndroid(byte[] image); } diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index 623332d37e..d32353ccde 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -1,6 +1,7 @@ #pragma once #include +#include // The Native App API. // @@ -174,5 +175,6 @@ std::string System_GetProperty(SystemProperty prop); int System_GetPropertyInt(SystemProperty prop); bool System_GetPropertyBool(SystemProperty prop); -void PushNewGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time); +std::vector __cameraGetDeviceList(); +void SetGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time); void PushCameraImage(long long length, unsigned char* image); diff --git a/ios/CameraHelper.h b/ios/CameraHelper.h new file mode 100644 index 0000000000..8c7c2b5f2f --- /dev/null +++ b/ios/CameraHelper.h @@ -0,0 +1,17 @@ +#import +#import + +@protocol CameraFrameDelegate +@required +- (void) PushCameraImageIOS:(long long)len buffer:(unsigned char*)data; +@end + +@interface CameraHelper : NSObject + +@property (nonatomic, strong) id delegate; + +- (int) checkPermission; +- (void) startVideo; +- (void) stopVideo; + +@end diff --git a/ios/CameraHelper.mm b/ios/CameraHelper.mm new file mode 100644 index 0000000000..7a2f6c87b6 --- /dev/null +++ b/ios/CameraHelper.mm @@ -0,0 +1,144 @@ +#include +#include +#include "Core/Config.h" +#import "CameraHelper.h" +#import + +@interface CameraHelper() { + AVCaptureSession *captureSession; + AVCaptureVideoPreviewLayer *previewLayer; +} +@end + +@implementation CameraHelper + +std::vector __cameraGetDeviceList() { + std::vector deviceList; + for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + deviceList.push_back([device.localizedName UTF8String]); + } + return deviceList; +} + +NSString *getSelectedCamera() { + NSString *selectedCamera = [NSString stringWithCString:g_Config.sCameraDevice.c_str() encoding:[NSString defaultCStringEncoding]]; + return selectedCamera; +} + +-(int) checkPermission { + AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; + NSLog(@"CameraHelper::checkPermission %ld", (long)status); + + switch (status) { + case AVAuthorizationStatusNotDetermined: { + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { + if (granted) { + NSLog(@"camera permission granted"); + dispatch_async(dispatch_get_main_queue(), ^{ + [self startVideo]; + }); + } else { + NSLog(@"camera permission denied"); + } + }]; + return 1; + } + case AVAuthorizationStatusRestricted: + case AVAuthorizationStatusDenied: { + NSLog(@"camera permission denied"); + return 1; + } + case AVAuthorizationStatusAuthorized: { + return 0; + } + } +} + +-(void) startVideo { + NSLog(@"CameraHelper::startVideo"); + if ([self checkPermission]) { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + NSError *error = nil; + + captureSession = [[AVCaptureSession alloc] init]; + captureSession.sessionPreset = AVCaptureSessionPresetMedium; + + AVCaptureDeviceInput *videoInput = nil; + NSString *selectedCamera = getSelectedCamera(); + for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + if ([device.localizedName isEqualToString:selectedCamera]) { + videoInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; + } + } + if (videoInput == nil || error) { + NSLog(@"selectedCamera error; try default device"); + + AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if (videoDevice == nil) { + NSLog(@"videoDevice error"); + return; + } + videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error]; + if (videoInput == nil) { + NSLog(@"videoInput error"); + return; + } + } + [captureSession addInput:videoInput]; + + AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init]; + videoOutput.videoSettings = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (id)kCVPixelBufferPixelFormatTypeKey]; + + [captureSession addOutput:videoOutput]; + + dispatch_queue_t queue = dispatch_queue_create("cameraQueue", NULL); + [videoOutput setSampleBufferDelegate:self queue:queue]; + + previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession]; + previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + + [previewLayer setFrame:CGRectMake(0, 0, 480, 272)]; + + [captureSession startRunning]; + }); +} + +-(void) stopVideo { + dispatch_async(dispatch_get_main_queue(), ^{ + [captureSession stopRunning]; + }); +} + +- (void) captureOutput:(AVCaptureOutput *)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection { + CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer]; + UIImage *theImage = [UIImage imageWithCGImage: cgImage]; + CGImageRelease(cgImage); + NSData *imageData = UIImageJPEGRepresentation(theImage, 0.6); + + [self.delegate PushCameraImageIOS:imageData.length buffer:(unsigned char*)imageData.bytes]; +} + +- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer { + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer,0); + void* baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); + size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + size_t width = CVPixelBufferGetWidth(imageBuffer); + size_t height = CVPixelBufferGetHeight(imageBuffer); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); + CGImageRef newImage = CGBitmapContextCreateImage(newContext); + CGContextRelease(newContext); + + CGColorSpaceRelease(colorSpace); + CVPixelBufferUnlockBaseAddress(imageBuffer,0); + return newImage; +} + +@end diff --git a/ios/LocationHelper.h b/ios/LocationHelper.h new file mode 100644 index 0000000000..f04a8ddb99 --- /dev/null +++ b/ios/LocationHelper.h @@ -0,0 +1,18 @@ +#import +#import + +@protocol LocationHandlerDelegate +@required +- (void) SetGpsDataIOS:(CLLocation*)newLocation; +@end + +@interface LocationHelper : NSObject { + CLLocationManager *locationManager; +} + +@property(nonatomic,strong) id delegate; + +- (void) startLocationUpdates; +- (void) stopLocationUpdates; + +@end diff --git a/ios/LocationHelper.mm b/ios/LocationHelper.mm new file mode 100644 index 0000000000..237a9e12fe --- /dev/null +++ b/ios/LocationHelper.mm @@ -0,0 +1,34 @@ +#import "LocationHelper.h" + +@interface LocationHelper() +@end + +@implementation LocationHelper + +-(id) init { + NSLog(@"LocationHelper::init"); + locationManager = [[CLLocationManager alloc] init]; + [locationManager setDelegate:self]; + return self; +} + +-(void) startLocationUpdates { + NSLog(@"LocationHelper::startLocationUpdates"); + if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { + [locationManager requestWhenInUseAuthorization]; + } + [locationManager startUpdatingLocation]; +} + +-(void) stopLocationUpdates { + NSLog(@"LocationHelper::stopLocationUpdates"); + [locationManager stopUpdatingLocation]; +} + +- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { + for (id location in locations) { + [self.delegate SetGpsDataIOS:location]; + } +} + +@end diff --git a/ios/PPSSPP-Info.plist b/ios/PPSSPP-Info.plist index 8569dc54cd..74234337c8 100644 --- a/ios/PPSSPP-Info.plist +++ b/ios/PPSSPP-Info.plist @@ -4,6 +4,12 @@ UISupportsDocumentBrowser + NSLocationWhenInUseUsageDescription + Your location may be used to emulate Go!Explore, a GPS accessory + NSCameraUsageDescription + Your camera may be used to emulate Go!Cam, a camera accessory + NSMicrophoneUsageDescription + Your microphone may be used to emulate Go!Cam/Talkman, a microphone accessory CFBundleDevelopmentRegion en CFBundleDisplayName diff --git a/ios/ViewController.h b/ios/ViewController.h index 4eedd89e7c..7f93faaebb 100644 --- a/ios/ViewController.h +++ b/ios/ViewController.h @@ -6,11 +6,18 @@ #import #endif #import "iCade/iCadeReaderView.h" +#import "CameraHelper.h" +#import "LocationHelper.h" -@interface ViewController : GLKViewController +@interface ViewController : GLKViewController - (void)shutdown; @end extern __unsafe_unretained ViewController* sharedViewController; +void startVideo(); +void stopVideo(); +void startLocation(); +void stopLocation(); diff --git a/ios/ViewController.mm b/ios/ViewController.mm index ad334f0d8d..5eaea61c3a 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -84,6 +84,8 @@ static bool threadStopped = false; __unsafe_unretained ViewController* sharedViewController; static GraphicsContext *graphicsContext; +static CameraHelper *cameraHelper; +static LocationHelper *locationHelper; @interface ViewController () { std::map iCadeToKeyMap; @@ -202,6 +204,12 @@ static GraphicsContext *graphicsContext; volume.delegate = self; [self.view addSubview:volume]; [self.view bringSubviewToFront:volume]; + + cameraHelper = [[CameraHelper alloc] init]; + [cameraHelper setDelegate:self]; + + locationHelper = [[LocationHelper alloc] init]; + [locationHelper setDelegate:self]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NativeInitGraphics(graphicsContext); @@ -667,6 +675,33 @@ static GraphicsContext *graphicsContext; } #endif +void startVideo() { + [cameraHelper startVideo]; +} + +void stopVideo() { + [cameraHelper stopVideo]; +} + +-(void) PushCameraImageIOS:(long long)len buffer:(unsigned char*)data { + PushCameraImage(len, data); +} + +void startLocation() { + [locationHelper startLocationUpdates]; +} + +void stopLocation() { + [locationHelper stopLocationUpdates]; +} + +-(void) SetGpsDataIOS:(CLLocation *)newLocation { + NSLog(@"SetGpsDataIOS: speed: %f", newLocation.speed); // m/s + SetGpsData(newLocation.coordinate.latitude, newLocation.coordinate.longitude, + newLocation.altitude, 0 /* speed */, 0 /* bearing */, + (long long)newLocation.timestamp.timeIntervalSince1970); +} + @end void OpenDirectory(const char *path) { diff --git a/ios/main.mm b/ios/main.mm index cad442c87b..dfa1548296 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -112,6 +112,18 @@ void System_SendMessage(const char *command, const char *parameter) { // [sharedViewController shutdown]; // exit(0); // }); + } else if (!strcmp(command, "camera_command")) { + if (!strcmp(parameter, "startVideo")) { + startVideo(); + } else if (!strcmp(parameter, "stopVideo")) { + stopVideo(); + } + } else if (!strcmp(command, "gps_command")) { + if (!strcmp(parameter, "open")) { + startLocation(); + } else if (!strcmp(parameter, "close")) { + stopLocation(); + } } } From c824fabd539d8a13285f029069b94b3604246e0b Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Fri, 24 Jan 2020 19:39:37 +0200 Subject: [PATCH 38/71] Update macOS system name --- Qt/QtMain.cpp | 2 +- SDL/SDLMain.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 9aa200641a..3c9641c1bd 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -108,7 +108,7 @@ std::string System_GetProperty(SystemProperty prop) { #elif defined(_WIN32) return "Qt:Windows"; #elif defined(Q_OS_MAC) - return "Qt:Mac"; + return "Qt:macOS"; #else return "Qt"; #endif diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index ca560fd44b..e8bc75fc4d 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -257,7 +257,7 @@ std::string System_GetProperty(SystemProperty prop) { #elif __linux__ return "SDL:Linux"; #elif __APPLE__ - return "SDL:OSX"; + return "SDL:macOS"; #else return "SDL:"; #endif From 66ff2d8a4af06bb3e990d0838e44cca5c5944ba1 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sat, 25 Jan 2020 17:59:53 +0000 Subject: [PATCH 39/71] Common/Hashmaps.h: add missing include On gcc-10 the build fails as: ``` [ 51%] Building CXX object CMakeFiles/Core.dir/GPU/Null/NullGpu.cpp.o In file included from ../GPU/Common/DrawEngineCommon.h:24, from ../GPU/Null/NullGpu.cpp:18: ../Common/Hashmaps.h:11:7: error: 'uint32_t' does not name a type; did you mean 'u_int32_t'? 11 | const uint32_t hashmapSeed = 0x23B58532; | ^~~~~~~~ | u_int32_t ``` This happens due to improved transitive include hygiene on gcc-10 headers. The change includes to pull in 'uint32_t' typedef. Signed-off-by: Sergei Trofimovich --- Common/Hashmaps.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Common/Hashmaps.h b/Common/Hashmaps.h index f0a6fe4709..e5e36be02e 100644 --- a/Common/Hashmaps.h +++ b/Common/Hashmaps.h @@ -1,5 +1,6 @@ #pragma once +#include /* uint32_t */ #include #include From 2b57d2365f8ce59b3b0ade74696e33545d42d874 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Sat, 25 Jan 2020 22:34:50 +0200 Subject: [PATCH 40/71] Android satellites info --- Core/HLE/sceUsbGps.cpp | 13 +++++- Core/HLE/sceUsbGps.h | 3 ++ UI/NativeApp.cpp | 8 ---- android/jni/app-android.cpp | 11 ++++- .../src/org/ppsspp/ppsspp/LocationHelper.java | 44 ++++++++++++++++++- android/src/org/ppsspp/ppsspp/NativeApp.java | 1 + ext/native/base/NativeApp.h | 2 - ios/ViewController.mm | 6 ++- 8 files changed, 70 insertions(+), 18 deletions(-) diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index 6a04812309..c5c7f3d9d6 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -120,7 +120,7 @@ void GPS::init() { gpsData.bearing = 35.0f; satData.satellites_in_view = 6; - for (int i = 0; i < satData.satellites_in_view; i++) { + for (unsigned char i = 0; i < satData.satellites_in_view; i++) { satData.satInfo[i].id = i + 1; // 1 .. 32 satData.satInfo[i].elevation = i * 10; satData.satInfo[i].azimuth = i * 50; @@ -131,7 +131,7 @@ void GPS::init() { void GPS::setGpsTime(time_t *time) { struct tm *gpsTime; - gpsTime = localtime(time); + gpsTime = gmtime(time); gpsData.year = (short)(gpsTime->tm_year + 1900); gpsData.month = (short)(gpsTime->tm_mon + 1); @@ -151,6 +151,15 @@ void GPS::setGpsData(float latitude, float longitude, float altitude, float spee gpsData.bearing = bearing; } +void GPS::setSatInfo(short index, unsigned char id, unsigned char elevation, short azimuth, unsigned char snr, unsigned char good) { + satData.satInfo[index].id = id; + satData.satInfo[index].elevation = elevation; + satData.satInfo[index].azimuth = azimuth; + satData.satInfo[index].snr = snr; + satData.satInfo[index].good = good; + satData.satellites_in_view = index + 1; +} + GpsData* GPS::getGpsData() { return &gpsData; } diff --git a/Core/HLE/sceUsbGps.h b/Core/HLE/sceUsbGps.h index fa7059b146..a36f237918 100644 --- a/Core/HLE/sceUsbGps.h +++ b/Core/HLE/sceUsbGps.h @@ -17,6 +17,8 @@ #pragma once +#include "Core/HLE/FunctionWrappers.h" + void Register_sceUsbGps(); void __UsbGpsInit(); @@ -64,6 +66,7 @@ namespace GPS { void init(); void setGpsTime(time_t *time); void setGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time); + void setSatInfo(short index, unsigned char id, unsigned char elevation, short azimuth, unsigned char snr, unsigned char good); GpsData *getGpsData(); SatData *getSatData(); } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 43f237534b..2992291052 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1357,11 +1357,3 @@ void NativeShutdown() { // Previously we did exit() here on Android but that makes it hard to do things like restart on backend change. // I think we handle most globals correctly or correct-enough now. } - -void SetGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time) { - GPS::setGpsData(latitude, longitude, altitude, speed, bearing, time); -} - -void PushCameraImage(long long length, unsigned char* image) { - Camera::pushCameraImage(length, image); -} diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index a464418405..78802e0689 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -46,6 +46,8 @@ #include "Core/ConfigValues.h" #include "Core/Loaders.h" #include "Core/System.h" +#include "Core/HLE/sceUsbCam.h" +#include "Core/HLE/sceUsbGps.h" #include "Common/CPUDetect.h" #include "Common/Log.h" #include "UI/GameInfoCache.h" @@ -951,7 +953,12 @@ extern "C" jint Java_org_ppsspp_ppsspp_NativeApp_getSelectedCamera(JNIEnv *, jcl extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setGpsDataAndroid(JNIEnv *, jclass, jfloat latitude, jfloat longitude, jfloat altitude, jfloat speed, jfloat bearing, jlong time) { - SetGpsData(latitude, longitude, altitude, speed, bearing, time); + GPS::setGpsData(latitude, longitude, altitude, speed, bearing, time); +} + +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setSatInfoAndroid(JNIEnv *, jclass, + jshort index, jshort id, jshort elevation, jshort azimuth, jshort snr, jshort good) { + GPS::setSatInfo(index, id, elevation, azimuth, snr, good); } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImageAndroid(JNIEnv *env, jclass, @@ -960,7 +967,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImageAndroid( if (image != NULL) { jlong size = env->GetArrayLength(image); jbyte* buffer = env->GetByteArrayElements(image, NULL); - PushCameraImage(size, (unsigned char *)buffer); + Camera::pushCameraImage(size, (unsigned char *)buffer); env->ReleaseByteArrayElements(image, buffer, JNI_ABORT); } } diff --git a/android/src/org/ppsspp/ppsspp/LocationHelper.java b/android/src/org/ppsspp/ppsspp/LocationHelper.java index 73e9350f07..b4ccef1ef3 100644 --- a/android/src/org/ppsspp/ppsspp/LocationHelper.java +++ b/android/src/org/ppsspp/ppsspp/LocationHelper.java @@ -1,14 +1,19 @@ package org.ppsspp.ppsspp; + import android.content.Context; +import android.location.GpsSatellite; +import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.util.Log; -class LocationHelper implements LocationListener { - private static final String TAG = "LocationHelper"; +import java.util.Iterator; + +class LocationHelper implements LocationListener, GpsStatus.Listener { + private static final String TAG = LocationHelper.class.getSimpleName(); private LocationManager mLocationManager; private boolean mLocationEnable; @@ -27,6 +32,7 @@ class LocationHelper implements LocationListener { isNetworkEnabled = mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this); mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, this); + mLocationManager.addGpsStatusListener(this); mLocationEnable = true; } catch (SecurityException e) { Log.e(TAG, "Cannot start location updates: " + e.toString()); @@ -70,4 +76,38 @@ class LocationHelper implements LocationListener { @Override public void onProviderDisabled(String provider) { } + + + @Override + public void onGpsStatusChanged(int i) { + switch (i) { + case GpsStatus.GPS_EVENT_STARTED: + case GpsStatus.GPS_EVENT_STOPPED: + case GpsStatus.GPS_EVENT_FIRST_FIX: + break; + case GpsStatus.GPS_EVENT_SATELLITE_STATUS: { + try { + GpsStatus gpsStatus = mLocationManager.getGpsStatus(null); + Iterable satellites = gpsStatus.getSatellites(); + + short index = 0; + for (Iterator iterator = satellites.iterator(); iterator.hasNext(); ) { + GpsSatellite satellite = iterator.next(); + if (satellite.getPrn() > 37) { + continue; + } + NativeApp.setSatInfoAndroid(index, (short) satellite.getPrn(), (short) satellite.getElevation(), + (short) satellite.getAzimuth(), (short) satellite.getSnr(), satellite.usedInFix() ? (short) 1 : (short) 0); + index++; + if (index == 24) { + break; + } + } + } catch (SecurityException e) { + Log.e(TAG, e.toString()); + } + break; + } + } + } } diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 5db8abdc6d..2af23dc867 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -53,5 +53,6 @@ public class NativeApp { public static native int getSelectedCamera(); public static native void setGpsDataAndroid(float latitude, float longitude, float altitude, float speed, float bearing, long time); + public static native void setSatInfoAndroid(short index, short id, short elevation, short azimuth, short snr, short good); public static native void pushCameraImageAndroid(byte[] image); } diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index d32353ccde..da2fba06ba 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -176,5 +176,3 @@ int System_GetPropertyInt(SystemProperty prop); bool System_GetPropertyBool(SystemProperty prop); std::vector __cameraGetDeviceList(); -void SetGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time); -void PushCameraImage(long long length, unsigned char* image); diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 5eaea61c3a..b114aed731 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -27,6 +27,8 @@ #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/System.h" +#include "Core/HLE/sceUsbCam.h" +#include "Core/HLE/sceUsbGps.h" #include "Common/GraphicsContext.h" #include @@ -684,7 +686,7 @@ void stopVideo() { } -(void) PushCameraImageIOS:(long long)len buffer:(unsigned char*)data { - PushCameraImage(len, data); + Camera::pushCameraImage(len, data); } void startLocation() { @@ -697,7 +699,7 @@ void stopLocation() { -(void) SetGpsDataIOS:(CLLocation *)newLocation { NSLog(@"SetGpsDataIOS: speed: %f", newLocation.speed); // m/s - SetGpsData(newLocation.coordinate.latitude, newLocation.coordinate.longitude, + GPS::setGpsData(newLocation.coordinate.latitude, newLocation.coordinate.longitude, newLocation.altitude, 0 /* speed */, 0 /* bearing */, (long long)newLocation.timestamp.timeIntervalSince1970); } From 63196897e45f875b027c4e6e97e89a078bfaf743 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Sun, 26 Jan 2020 10:04:12 +0200 Subject: [PATCH 41/71] Do not stop camera if unnecessary --- Core/HLE/sceUsbCam.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index fdcf4d07c0..bf6b0281be 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -64,7 +64,9 @@ void __UsbCamDoState(PointerWrap &p) { } void __UsbCamShutdown() { - Camera::stopCapture(); + if (config->mode == Camera::Mode::Video) { // stillImage? TBD + Camera::stopCapture(); + } delete[] videoBuffer; videoBuffer = nullptr; delete[] config; From 83277680dab92cf7de6b1906c4b2904c45e7efa9 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Sun, 26 Jan 2020 12:30:46 +0200 Subject: [PATCH 42/71] Location: add HDOP; change speed to km/h; change altitude from geoid to sea level The speed is now close to the value obtained on original hardware but the altitude on original hardawre is too imprecise to tell if we're good --- Core/HLE/sceUsbGps.cpp | 3 +- Core/HLE/sceUsbGps.h | 2 +- android/jni/app-android.cpp | 4 +- .../src/org/ppsspp/ppsspp/LocationHelper.java | 42 ++++++++++++++++--- android/src/org/ppsspp/ppsspp/NativeApp.java | 2 +- ios/ViewController.mm | 10 +++-- 6 files changed, 48 insertions(+), 15 deletions(-) diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index c5c7f3d9d6..05b16e5b3d 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -141,9 +141,10 @@ void GPS::setGpsTime(time_t *time) { gpsData.second = (short)gpsTime->tm_sec; } -void GPS::setGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long gpsTime) { +void GPS::setGpsData(long long gpsTime, float hdop, float latitude, float longitude, float altitude, float speed, float bearing) { setGpsTime((time_t*)&gpsTime); + gpsData.hdop = hdop; gpsData.latitude = latitude; gpsData.longitude = longitude; gpsData.altitude = altitude; diff --git a/Core/HLE/sceUsbGps.h b/Core/HLE/sceUsbGps.h index a36f237918..24c2f5ccd0 100644 --- a/Core/HLE/sceUsbGps.h +++ b/Core/HLE/sceUsbGps.h @@ -65,7 +65,7 @@ typedef struct { namespace GPS { void init(); void setGpsTime(time_t *time); - void setGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time); + void setGpsData(long long time, float hdop, float latitude, float longitude, float altitude, float speed, float bearing); void setSatInfo(short index, unsigned char id, unsigned char elevation, short azimuth, unsigned char snr, unsigned char good); GpsData *getGpsData(); SatData *getSatData(); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 78802e0689..dd8058df43 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -952,8 +952,8 @@ extern "C" jint Java_org_ppsspp_ppsspp_NativeApp_getSelectedCamera(JNIEnv *, jcl } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setGpsDataAndroid(JNIEnv *, jclass, - jfloat latitude, jfloat longitude, jfloat altitude, jfloat speed, jfloat bearing, jlong time) { - GPS::setGpsData(latitude, longitude, altitude, speed, bearing, time); + jlong time, jfloat hdop, jfloat latitude, jfloat longitude, jfloat altitude, jfloat speed, jfloat bearing) { + GPS::setGpsData(time, hdop, latitude, longitude, altitude, speed, bearing); } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setSatInfoAndroid(JNIEnv *, jclass, diff --git a/android/src/org/ppsspp/ppsspp/LocationHelper.java b/android/src/org/ppsspp/ppsspp/LocationHelper.java index b4ccef1ef3..7a02ca3700 100644 --- a/android/src/org/ppsspp/ppsspp/LocationHelper.java +++ b/android/src/org/ppsspp/ppsspp/LocationHelper.java @@ -12,10 +12,12 @@ import android.util.Log; import java.util.Iterator; -class LocationHelper implements LocationListener, GpsStatus.Listener { +class LocationHelper implements LocationListener, GpsStatus.Listener, GpsStatus.NmeaListener { private static final String TAG = LocationHelper.class.getSimpleName(); private LocationManager mLocationManager; private boolean mLocationEnable; + private float mAltitudeAboveSeaLevel = 0f; + private float mHdop = 0f; LocationHelper(Context context) { mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); @@ -33,6 +35,7 @@ class LocationHelper implements LocationListener, GpsStatus.Listener { mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this); mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, this); mLocationManager.addGpsStatusListener(this); + mLocationManager.addNmeaListener(this); mLocationEnable = true; } catch (SecurityException e) { Log.e(TAG, "Cannot start location updates: " + e.toString()); @@ -52,17 +55,20 @@ class LocationHelper implements LocationListener, GpsStatus.Listener { } } + + /* + * LocationListener + */ + @Override public void onLocationChanged(Location location) { + long time = location.getTime() / 1000; // ms to s !! float latitude = (float) location.getLatitude(); float longitude = (float) location.getLongitude(); - // Android altitude is in meters above the WGS 84 reference ellipsoid - float altitude = (float) location.getAltitude(); - float speed = location.getSpeed(); + float speed = location.getSpeed() * 3.6f; // m/s to km/h !! float bearing = location.getBearing(); - long time = location.getTime() / 1000; // ms to s !! - NativeApp.setGpsDataAndroid(latitude, longitude, altitude, speed, bearing, time); + NativeApp.setGpsDataAndroid(time, mHdop, latitude, longitude, mAltitudeAboveSeaLevel, speed, bearing); } @Override @@ -78,6 +84,10 @@ class LocationHelper implements LocationListener, GpsStatus.Listener { } + /* + * GpsStatus.Listener + */ + @Override public void onGpsStatusChanged(int i) { switch (i) { @@ -110,4 +120,24 @@ class LocationHelper implements LocationListener, GpsStatus.Listener { } } } + + + /* + * GpsStatus.Listener + */ + + @Override + public void onNmeaReceived(long timestamp, String nmea) { + String[] tokens = nmea.split(","); + Log.e(TAG, nmea); + if (tokens.length < 10 || !tokens[0].equals("$GPGGA")) { + return; + } + if (!tokens[8].isEmpty()) { + mHdop = Float.valueOf(tokens[8]); + } + if (!tokens[9].isEmpty()) { + mAltitudeAboveSeaLevel = Float.valueOf(tokens[9]); + } + } } diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 2af23dc867..fec8c5c002 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -52,7 +52,7 @@ public class NativeApp { public static native String queryConfig(String queryName); public static native int getSelectedCamera(); - public static native void setGpsDataAndroid(float latitude, float longitude, float altitude, float speed, float bearing, long time); + public static native void setGpsDataAndroid(long time, float hdop, float latitude, float longitude, float altitude, float speed, float bearing); public static native void setSatInfoAndroid(short index, short id, short elevation, short azimuth, short snr, short good); public static native void pushCameraImageAndroid(byte[] image); } diff --git a/ios/ViewController.mm b/ios/ViewController.mm index b114aed731..7c45c27a11 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -698,10 +698,12 @@ void stopLocation() { } -(void) SetGpsDataIOS:(CLLocation *)newLocation { - NSLog(@"SetGpsDataIOS: speed: %f", newLocation.speed); // m/s - GPS::setGpsData(newLocation.coordinate.latitude, newLocation.coordinate.longitude, - newLocation.altitude, 0 /* speed */, 0 /* bearing */, - (long long)newLocation.timestamp.timeIntervalSince1970); + GPS::setGpsData((long long)newLocation.timestamp.timeIntervalSince1970, + newLocation.horizontalAccuracy/5.0, + newLocation.coordinate.latitude, newLocation.coordinate.longitude, + newLocation.altitude, + MAX(newLocation.speed * 3.6, 0.0), /* m/s to km/h */ + 0 /* bearing */); } @end From e4f9f3b120f5fd1686ce13471f392e818e6af970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 26 Jan 2020 15:28:54 +0100 Subject: [PATCH 43/71] DrawEngineDX9: Rename lookup table, cleanup --- GPU/Directx9/DrawEngineDX9.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 9d02936e71..954aa32d07 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -43,17 +43,16 @@ namespace DX9 { -const D3DPRIMITIVETYPE glprim[8] = { +static const D3DPRIMITIVETYPE d3d_prim[8] = { D3DPT_POINTLIST, D3DPT_LINELIST, D3DPT_LINESTRIP, D3DPT_TRIANGLELIST, D3DPT_TRIANGLESTRIP, D3DPT_TRIANGLEFAN, - D3DPT_TRIANGLELIST, // With OpenGL ES we have to expand sprites into triangles, tripling the data instead of doubling. sigh. OpenGL ES, Y U NO SUPPORT GL_QUADS? + D3DPT_TRIANGLELIST, }; -// hrydgard's quick guesses - TODO verify static const int D3DPRIMITIVEVERTEXCOUNT[8][2] = { {0, 0}, // invalid {1, 0}, // 1 = D3DPT_POINTLIST, @@ -499,9 +498,9 @@ rotateVBO: device_->SetVertexDeclaration(pHardwareVertexDecl); if (vb_ == NULL) { if (useElements) { - device_->DrawIndexedPrimitiveUP(glprim[prim], 0, maxIndex + 1, D3DPrimCount(glprim[prim], vertexCount), decIndex, D3DFMT_INDEX16, decoded, dec_->GetDecVtxFmt().stride); + device_->DrawIndexedPrimitiveUP(d3d_prim[prim], 0, maxIndex + 1, D3DPrimCount(d3d_prim[prim], vertexCount), decIndex, D3DFMT_INDEX16, decoded, dec_->GetDecVtxFmt().stride); } else { - device_->DrawPrimitiveUP(glprim[prim], D3DPrimCount(glprim[prim], vertexCount), decoded, dec_->GetDecVtxFmt().stride); + device_->DrawPrimitiveUP(d3d_prim[prim], D3DPrimCount(d3d_prim[prim], vertexCount), decoded, dec_->GetDecVtxFmt().stride); } } else { device_->SetStreamSource(0, vb_, 0, dec_->GetDecVtxFmt().stride); @@ -509,9 +508,9 @@ rotateVBO: if (useElements) { device_->SetIndices(ib_); - device_->DrawIndexedPrimitive(glprim[prim], 0, 0, maxIndex + 1, 0, D3DPrimCount(glprim[prim], vertexCount)); + device_->DrawIndexedPrimitive(d3d_prim[prim], 0, 0, maxIndex + 1, 0, D3DPrimCount(d3d_prim[prim], vertexCount)); } else { - device_->DrawPrimitive(glprim[prim], 0, D3DPrimCount(glprim[prim], vertexCount)); + device_->DrawPrimitive(d3d_prim[prim], 0, D3DPrimCount(d3d_prim[prim], vertexCount)); } } } @@ -568,9 +567,9 @@ rotateVBO: device_->SetVertexDeclaration(transformedVertexDecl_); if (drawIndexed) { - device_->DrawIndexedPrimitiveUP(glprim[prim], 0, maxIndex, D3DPrimCount(glprim[prim], numTrans), inds, D3DFMT_INDEX16, drawBuffer, sizeof(TransformedVertex)); + device_->DrawIndexedPrimitiveUP(d3d_prim[prim], 0, maxIndex, D3DPrimCount(d3d_prim[prim], numTrans), inds, D3DFMT_INDEX16, drawBuffer, sizeof(TransformedVertex)); } else { - device_->DrawPrimitiveUP(glprim[prim], D3DPrimCount(glprim[prim], numTrans), drawBuffer, sizeof(TransformedVertex)); + device_->DrawPrimitiveUP(d3d_prim[prim], D3DPrimCount(d3d_prim[prim], numTrans), drawBuffer, sizeof(TransformedVertex)); } } else if (result.action == SW_CLEAR) { u32 clearColor = result.color; From 3aa575daef1f269c6ee33036cba55524c159f487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 26 Jan 2020 15:29:45 +0100 Subject: [PATCH 44/71] OpenGL: Call ApplyDrawState before SoftwareTransform --- GPU/GLES/DrawEngineGLES.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index a934a25790..2f272def84 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -562,15 +562,18 @@ rotateVBO: if (vertexCount > 0x10000 / 3) vertexCount = 0x10000 / 3; #endif + + if (textureNeedsApply) + textureCache_->ApplyTexture(); + + // Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state. + ApplyDrawState(prim); + SoftwareTransform( prim, vertexCount, dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(), maxIndex, drawBuffer, numTrans, drawIndexed, ¶ms, &result); - if (textureNeedsApply) - textureCache_->ApplyTexture(); - - ApplyDrawState(prim); ApplyDrawStateLate(result.setStencil, result.stencilValue); LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim); From 92a1395cd01e53cf0939aaa872136ceac01aa7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 26 Jan 2020 15:30:20 +0100 Subject: [PATCH 45/71] Apply viewport before SoftwareTransform so flipping can be checked --- GPU/Vulkan/DrawEngineVulkan.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index b5ca090dc4..9a3ee27ed3 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -895,6 +895,14 @@ void DrawEngineVulkan::DoFlush() { params.allowSeparateAlphaClear = false; params.provokeFlatFirst = true; + // We need to update the viewport early because it's checked for flipping in SoftwareTransform. + // We don't have a "DrawStateEarly" in vulkan, so... + // TODO: Probably should eventually refactor this and feed the vp size into SoftwareTransform directly (Unknown's idea). + if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { + gstate_c.vpWidth = gstate.getViewportXScale() * 2.0f; + gstate_c.vpHeight = gstate.getViewportYScale() * 2.0f; + } + int maxIndex = indexGen.MaxIndex(); SoftwareTransform( prim, indexGen.VertexCount(), From 118e927b86a0bfb9bbab8fe123120592c11f68cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 26 Jan 2020 15:31:07 +0100 Subject: [PATCH 46/71] GPU: Only GL uses a flipped buffer in non-buffered, reflect that in the SW transform code. --- GPU/Common/SoftwareTransformCommon.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 21d45951f7..2949d8e335 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -62,7 +62,7 @@ static void SwapUVs(TransformedVertex &a, TransformedVertex &b) { // Note: 0 is BR and 2 is TL. -static void RotateUV(TransformedVertex v[4], float flippedMatrix[16], float ySign) { +static void RotateUV(TransformedVertex v[4], float flippedMatrix[16], bool flippedY) { // Transform these two coordinates to figure out whether they're flipped or not. Vec4f tl; Vec3ByMatrix44(tl.AsArray(), v[2].pos, flippedMatrix); @@ -70,6 +70,8 @@ static void RotateUV(TransformedVertex v[4], float flippedMatrix[16], float ySig Vec4f br; Vec3ByMatrix44(br.AsArray(), v[0].pos, flippedMatrix); + float ySign = flippedY ? -1.0 : 1.0; + const float invtlw = 1.0f / tl.w; const float invbrw = 1.0f / br.w; const float x1 = tl.x * invtlw; @@ -153,6 +155,7 @@ static int ColorIndexOffset(int prim, GEShadeMode shadeMode, bool clearMode) { return 0; } +// NOTE: The viewport must be up to date! void SoftwareTransform( int prim, int vertexCount, u32 vertType, u16 *&inds, int indexType, const DecVtxFormat &decVtxFormat, int &maxIndex, TransformedVertex *&drawBuffer, int &numTrans, bool &drawIndexed, const SoftwareTransformParams *params, SoftwareTransformResult *result) { @@ -161,7 +164,6 @@ void SoftwareTransform( TextureCacheCommon *texCache = params->texCache; TransformedVertex *transformed = params->transformed; TransformedVertex *transformedExpanded = params->transformedExpanded; - float ySign = 1.0f; bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0; bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled(); @@ -515,20 +517,24 @@ void SoftwareTransform( numTrans = 0; drawIndexed = false; + bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; + + bool flippedY = g_Config.iGPUBackend == (int)GPUBackend::OPENGL && !useBufferedRendering; + if (prim != GE_PRIM_RECTANGLES) { // We can simply draw the unexpanded buffer. numTrans = vertexCount; drawIndexed = true; } else { - bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; - if (useBufferedRendering) - ySign = -ySign; + // Pretty bad hackery where we re-do the transform (in RotateUV) to see if the vertices are flipped in screen space. + // Since we've already got API-specific assumptions (Y direction, etc) baked into the projMatrix (which we arguably shouldn't), + // this gets nasty and very hard to understand. float flippedMatrix[16]; if (!throughmode) { memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); - const bool invertedY = useBufferedRendering ? (gstate_c.vpHeight < 0) : (gstate_c.vpHeight > 0); + const bool invertedY = flippedY ? (gstate_c.vpHeight < 0) : (gstate_c.vpHeight > 0); if (invertedY) { flippedMatrix[1] = -flippedMatrix[1]; flippedMatrix[5] = -flippedMatrix[5]; @@ -584,7 +590,7 @@ void SoftwareTransform( if (throughmode) RotateUVThrough(trans); else - RotateUV(trans, flippedMatrix, ySign); + RotateUV(trans, flippedMatrix, flippedY); // Triangle: BR-TR-TL indsOut[0] = i * 2 + 0; From 41a6b97023fd0242c7c4a28f37f056055643a39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 26 Jan 2020 16:34:46 +0100 Subject: [PATCH 47/71] DX9: Simplify some expressions --- GPU/Directx9/ShaderManagerDX9.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 22424d703a..f923fa82a8 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -243,14 +243,18 @@ void ShaderManagerDX9::VSSetMatrix(int creg, const float* pMatrix) { static void ConvertProjMatrixToD3D(Matrix4x4 &in, bool invertedX, bool invertedY) { // Half pixel offset hack float xoff = 1.0f / gstate_c.curRTRenderWidth; - xoff = gstate_c.vpXOffset + (invertedX ? xoff : -xoff); - float yoff = -1.0f / gstate_c.curRTRenderHeight; - yoff = gstate_c.vpYOffset + (invertedY ? yoff : -yoff); + if (invertedX) { + xoff = -gstate_c.vpXOffset - xoff; + } else { + xoff = gstate_c.vpXOffset - xoff; + } - if (invertedX) - xoff = -xoff; - if (invertedY) - yoff = -yoff; + float yoff = -1.0f / gstate_c.curRTRenderHeight; + if (invertedY) { + yoff = gstate_c.vpYOffset - yoff; + } else { + yoff = -gstate_c.vpYOffset - yoff; + } const Vec3 trans(xoff, yoff, gstate_c.vpZOffset * 0.5f + 0.5f); const Vec3 scale(gstate_c.vpWidthScale, gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f); From 931dff6125445561b74c23156c93cf5c6f9ca50d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 26 Jan 2020 10:18:41 -0800 Subject: [PATCH 48/71] Core: Future proof CSO support a bit. For CSO versions >= 2, respect the header size field and uncompressed frame size behavior. This will allow more options for future files, like adding a field for the CRC or otherwise. --- Core/FileSystems/BlockDevices.cpp | 40 +++++++++++++------------------ Core/FileSystems/BlockDevices.h | 1 + 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index f5ac8f9264..0826cafed6 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -134,18 +134,11 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader) CISO_H hdr; size_t readSize = fileLoader->ReadAt(0, sizeof(CISO_H), 1, &hdr); - if (readSize != 1 || memcmp(hdr.magic, "CISO", 4) != 0) - { + if (readSize != 1 || memcmp(hdr.magic, "CISO", 4) != 0) { WARN_LOG(LOADER, "Invalid CSO!"); } - else - { - VERBOSE_LOG(LOADER, "Valid CSO!"); - } - if (hdr.ver > 1) - { - ERROR_LOG(LOADER, "CSO version too high!"); - //ARGH! + if (hdr.ver > 1) { + WARN_LOG(LOADER, "CSO version too high!"); } frameSize = hdr.block_size; @@ -174,10 +167,11 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader) zlibBufferFrame = numFrames; const u32 indexSize = numFrames + 1; + const size_t headerEnd = hdr.ver > 1 ? (size_t)hdr.header_size : sizeof(hdr); #if COMMON_LITTLE_ENDIAN index = new u32[indexSize]; - if (fileLoader->ReadAt(sizeof(hdr), sizeof(u32), indexSize, index) != indexSize) { + if (fileLoader->ReadAt(headerEnd, sizeof(u32), indexSize, index) != indexSize) { NotifyReadError(); memset(index, 0, indexSize * sizeof(u32)); } @@ -185,7 +179,7 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader) index = new u32[indexSize]; u32_le *indexTemp = new u32_le[indexSize]; - if (fileLoader->ReadAt(sizeof(hdr), sizeof(u32), indexSize, indexTemp) != indexSize) { + if (fileLoader->ReadAt(headerEnd, sizeof(u32), indexSize, indexTemp) != indexSize) { NotifyReadError(); memset(indexTemp, 0, indexSize * sizeof(u32_le)); } @@ -196,6 +190,8 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader) delete[] indexTemp; #endif + ver_ = hdr.ver; + // Double check that the CSO is not truncated. In most cases, this will be the exact size. u64 fileSize = fileLoader->FileSize(); u64 lastIndexPos = index[indexSize - 1] & 0x7FFFFFFF; @@ -216,8 +212,7 @@ CISOFileBlockDevice::~CISOFileBlockDevice() bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached) { FileLoader::Flags flags = uncached ? FileLoader::Flags::HINT_UNCACHED : FileLoader::Flags::NONE; - if ((u32)blockNumber >= numBlocks) - { + if ((u32)blockNumber >= numBlocks) { memset(outPtr, 0, GetBlockSize()); return false; } @@ -233,20 +228,19 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached) const size_t compressedReadSize = (size_t)(compressedReadEnd - compressedReadPos); const u32 compressedOffset = (blockNumber & ((1 << blockShift) - 1)) * GetBlockSize(); - const int plain = idx & 0x80000000; - if (plain) - { + bool plain = (idx & 0x80000000) != 0; + if (ver_ >= 2) { + // CSO v2+ requires blocks be uncompressed if large enough to be. High bit means other things. + plain = compressedReadSize >= frameSize; + } + if (plain) { int readSize = (u32)fileLoader_->ReadAt(compressedReadPos + compressedOffset, 1, GetBlockSize(), outPtr, flags); if (readSize < GetBlockSize()) memset(outPtr + readSize, 0, GetBlockSize() - readSize); - } - else if (zlibBufferFrame == frameNumber) - { + } else if (zlibBufferFrame == frameNumber) { // We already have it. Just apply the offset and copy. memcpy(outPtr, zlibBuffer + compressedOffset, GetBlockSize()); - } - else - { + } else { const u32 readSize = (u32)fileLoader_->ReadAt(compressedReadPos, 1, compressedReadSize, readBuffer, flags); z.zalloc = Z_NULL; diff --git a/Core/FileSystems/BlockDevices.h b/Core/FileSystems/BlockDevices.h index e3b9dc280b..6e17aaa4ea 100644 --- a/Core/FileSystems/BlockDevices.h +++ b/Core/FileSystems/BlockDevices.h @@ -72,6 +72,7 @@ private: u32 frameSize; u32 numBlocks; u32 numFrames; + int ver_; }; From 50d7d64eaa021c8b076d397468b0294a4dca1663 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Sun, 26 Jan 2020 23:28:41 +0200 Subject: [PATCH 49/71] Add new listeners in addition to those present which are deprecated --- .../src/org/ppsspp/ppsspp/LocationHelper.java | 96 +++++++++++++++---- 1 file changed, 78 insertions(+), 18 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/LocationHelper.java b/android/src/org/ppsspp/ppsspp/LocationHelper.java index 7a02ca3700..f5582964df 100644 --- a/android/src/org/ppsspp/ppsspp/LocationHelper.java +++ b/android/src/org/ppsspp/ppsspp/LocationHelper.java @@ -1,21 +1,28 @@ package org.ppsspp.ppsspp; - +import android.annotation.TargetApi; import android.content.Context; +import android.location.GnssStatus; import android.location.GpsSatellite; import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; +import android.location.OnNmeaMessageListener; +import android.os.Build; import android.os.Bundle; import android.util.Log; import java.util.Iterator; -class LocationHelper implements LocationListener, GpsStatus.Listener, GpsStatus.NmeaListener { +class LocationHelper implements LocationListener { private static final String TAG = LocationHelper.class.getSimpleName(); private LocationManager mLocationManager; private boolean mLocationEnable; + private GpsStatus.Listener mGpsStatusListener; + private GnssStatus.Callback mGnssStatusCallback; + private OnNmeaMessageListener mNmeaMessageListener; + private GpsStatus.NmeaListener mNmeaListener; private float mAltitudeAboveSeaLevel = 0f; private float mHdop = 0f; @@ -34,8 +41,37 @@ class LocationHelper implements LocationListener, GpsStatus.Listener, GpsStatus. isNetworkEnabled = mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this); mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, this); - mLocationManager.addGpsStatusListener(this); - mLocationManager.addNmeaListener(this); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mGnssStatusCallback = new GnssStatus.Callback() { + @Override + public void onSatelliteStatusChanged(GnssStatus status) { + onSatelliteStatus(status); + } + }; + mLocationManager.registerGnssStatusCallback(mGnssStatusCallback); + mNmeaMessageListener = new OnNmeaMessageListener() { + @Override + public void onNmeaMessage(String message, long timestamp) { + onNmea(message); + } + }; + mLocationManager.addNmeaListener(mNmeaMessageListener); + } else { + mGpsStatusListener = new GpsStatus.Listener() { + @Override + public void onGpsStatusChanged(int event) { + onGpsStatus(event); + } + }; + mLocationManager.addGpsStatusListener(mGpsStatusListener); + mNmeaListener = new GpsStatus.NmeaListener() { + @Override + public void onNmeaReceived(long timestamp, String nmea) { + onNmea(nmea); + } + }; + mLocationManager.addNmeaListener(mNmeaListener); + } mLocationEnable = true; } catch (SecurityException e) { Log.e(TAG, "Cannot start location updates: " + e.toString()); @@ -49,6 +85,25 @@ class LocationHelper implements LocationListener, GpsStatus.Listener, GpsStatus. void stopLocationUpdates() { Log.d(TAG, "stopLocationUpdates"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (mGnssStatusCallback != null) { + mLocationManager.unregisterGnssStatusCallback(mGnssStatusCallback); + mGnssStatusCallback = null; + } + if (mNmeaMessageListener != null) { + mLocationManager.removeNmeaListener(mNmeaMessageListener); + mNmeaMessageListener = null; + } + } else { + if (mGpsStatusListener != null) { + mLocationManager.removeGpsStatusListener(mGpsStatusListener); + mGpsStatusListener = null; + } + if (mNmeaListener != null) { + mLocationManager.removeNmeaListener(mNmeaListener); + mNmeaListener = null; + } + } if (mLocationEnable) { mLocationEnable = false; mLocationManager.removeUpdates(this); @@ -84,13 +139,25 @@ class LocationHelper implements LocationListener, GpsStatus.Listener, GpsStatus. } - /* - * GpsStatus.Listener - */ - @Override - public void onGpsStatusChanged(int i) { - switch (i) { + @TargetApi(Build.VERSION_CODES.N) + private void onSatelliteStatus(GnssStatus status) { + short index = 0; + for (short i = 0; i < status.getSatelliteCount(); i++) { + if (status.getConstellationType(i) != GnssStatus.CONSTELLATION_GPS) { + continue; + } + NativeApp.setSatInfoAndroid(index, (short) status.getSvid(i), (short) status.getElevationDegrees(i), + (short) status.getAzimuthDegrees(i), (short) status.getCn0DbHz(i), status.usedInFix(i) ? (short) 1 : (short) 0); + index++; + if (index == 24) { + break; + } + } + } + + private void onGpsStatus(int event) { + switch (event) { case GpsStatus.GPS_EVENT_STARTED: case GpsStatus.GPS_EVENT_STOPPED: case GpsStatus.GPS_EVENT_FIRST_FIX: @@ -121,15 +188,8 @@ class LocationHelper implements LocationListener, GpsStatus.Listener, GpsStatus. } } - - /* - * GpsStatus.Listener - */ - - @Override - public void onNmeaReceived(long timestamp, String nmea) { + private void onNmea(String nmea) { String[] tokens = nmea.split(","); - Log.e(TAG, nmea); if (tokens.length < 10 || !tokens[0].equals("$GPGGA")) { return; } From f42e6a984f3d3a10b6a70429942b42afa033e039 Mon Sep 17 00:00:00 2001 From: iota97 Date: Tue, 28 Jan 2020 08:52:18 +0100 Subject: [PATCH 50/71] Rapid fire touch control --- Core/Config.cpp | 2 ++ Core/Config.h | 1 + Core/HLE/sceCtrl.cpp | 5 +++++ Core/HLE/sceCtrl.h | 1 + UI/EmuScreen.cpp | 10 +++++++--- UI/GamepadEmu.cpp | 19 +++++++++++++++++++ UI/GamepadEmu.h | 9 +++++++++ UI/TouchControlLayoutScreen.cpp | 6 ++++++ UI/TouchControlVisibilityScreen.cpp | 1 + 9 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 4458042234..4b914e608c 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -881,6 +881,7 @@ static ConfigSetting controlSettings[] = { ConfigSetting("fcombo4X", "fcombo4Y", "comboKeyScale4", "ShowComboKey4", &g_Config.touchCombo4, defaultTouchPosHide, true, true), ConfigSetting("Speed1KeyX", "Speed1KeyY", "Speed1KeyScale", "ShowSpeed1Key", &g_Config.touchSpeed1Key, defaultTouchPosHide, true, true), ConfigSetting("Speed2KeyX", "Speed2KeyY", "Speed2KeyScale", "ShowSpeed2Key", &g_Config.touchSpeed2Key, defaultTouchPosHide, true, true), + ConfigSetting("RapidFireKeyX", "RapidFireKeyY", "RapidFireKeyScale", "ShowRapidFireKey", &g_Config.touchRapidFireKey, defaultTouchPosHide, true, true), #ifdef _WIN32 ConfigSetting("DInputAnalogDeadzone", &g_Config.fDInputAnalogDeadzone, 0.1f, true, true), @@ -1599,6 +1600,7 @@ void Config::ResetControlLayout() { reset(g_Config.touchCombo4); reset(g_Config.touchSpeed1Key); reset(g_Config.touchSpeed2Key); + reset(g_Config.touchRapidFireKey); } void Config::GetReportingInfo(UrlEncoder &data) { diff --git a/Core/Config.h b/Core/Config.h index 88be654ea3..e60e0224ef 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -308,6 +308,7 @@ public: ConfigTouchPos touchCombo4; ConfigTouchPos touchSpeed1Key; ConfigTouchPos touchSpeed2Key; + ConfigTouchPos touchRapidFireKey; // Controls Visibility bool bShowTouchControls; diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 6074b1601f..79888ebea5 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -200,6 +200,11 @@ void __CtrlSetRapidFire(bool state) emuRapidFire = state; } +bool __CtrlGetRapidFire() +{ + return emuRapidFire; +} + static int __CtrlReadSingleBuffer(PSPPointer data, bool negative) { if (data.IsValid()) diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index db7c09b582..0c2d757dde 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -79,6 +79,7 @@ void __CtrlSetAnalogY(float value, int stick = CTRL_STICK_LEFT); // Call this to enable rapid-fire. This will cause buttons other than arrows to alternate. void __CtrlSetRapidFire(bool state); +bool __CtrlGetRapidFire(); // For use by internal UI like MsgDialog u32 __CtrlPeekButtons(); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 6c062d6a52..d72ca4ee7f 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -628,6 +628,9 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { osm.Show(sc->T("replaceTextures_false", "Textures no longer are being replaced"), 2.0); NativeMessageReceived("gpu_clearCache", ""); break; + case VIRTKEY_RAPID_FIRE: + __CtrlSetRapidFire(true); + break; } } @@ -677,6 +680,10 @@ void EmuScreen::onVKeyUp(int virtualKeyCode) { setVKeyAnalogY(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); break; + case VIRTKEY_RAPID_FIRE: + __CtrlSetRapidFire(false); + break; + default: break; } @@ -1080,9 +1087,6 @@ void EmuScreen::update() { if (invalid_) return; - // Virtual keys. - __CtrlSetRapidFire(virtKeys[VIRTKEY_RAPID_FIRE - VIRTKEY_FIRST]); - // This is here to support the iOS on screen back button. if (pauseTrigger_) { pauseTrigger_ = false; diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 9a4fc1ceea..126cfa28c6 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -157,6 +157,19 @@ void FPSLimitButton::Touch(const TouchInput &input) { } } +void RapidFireButton::Touch(const TouchInput &input) { + bool lastDown = pointerDownMask_ != 0; + MultiTouchButton::Touch(input); + bool down = pointerDownMask_ != 0; + if (down && !lastDown) { + __CtrlSetRapidFire(!__CtrlGetRapidFire()); + } +} + +bool RapidFireButton::IsDown() { + return __CtrlGetRapidFire(); +} + bool FPSLimitButton::IsDown() { return PSP_CoreParameter().fpsLimit == limit_; } @@ -510,6 +523,7 @@ void InitPadLayout(float xres, float yres, float globalScale) { initTouchPos(g_Config.touchSpeed1Key, unthrottle_key_X, unthrottle_key_Y - 60 * scale); initTouchPos(g_Config.touchSpeed2Key, unthrottle_key_X + bottom_key_spacing * scale, unthrottle_key_Y - 60 * scale); + initTouchPos(g_Config.touchRapidFireKey, unthrottle_key_X + 2*bottom_key_spacing * scale, unthrottle_key_Y - 60 * scale); // L and R------------------------------------------------------------ // Put them above the analog stick / above the buttons to the right. @@ -633,6 +647,11 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { }); } + if (g_Config.touchRapidFireKey.show) { + auto rapidFire = root->Add(new RapidFireButton(rectImage, I_RECT, I_ARROW, g_Config.touchRapidFireKey.scale, buttonLayoutParams(g_Config.touchRapidFireKey))); + rapidFire->SetAngle(90.0f, 180.0f); + } + FPSLimitButton *speed1 = addFPSLimitButton(FPSLimit::CUSTOM1, rectImage, I_RECT, I_ARROW, g_Config.touchSpeed1Key); if (speed1) speed1->SetAngle(170.0f, 180.0f); diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h index ec52ac575b..70ad2fd56b 100644 --- a/UI/GamepadEmu.h +++ b/UI/GamepadEmu.h @@ -97,6 +97,15 @@ private: FPSLimit limit_; }; +class RapidFireButton : public MultiTouchButton { +public: + RapidFireButton(int bgImg, int bgDownImg, int img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(bgImg, bgDownImg, img, scale, layoutParams) { + } + void Touch(const TouchInput &input) override; + bool IsDown() override; +}; + class PSPButton : public MultiTouchButton { public: PSPButton(int pspButtonBit, int bgImg, int bgDownImg, int img, float scale, UI::LayoutParams *layoutParams) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 6c7917f79a..1e54323dcf 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -453,6 +453,12 @@ void TouchControlLayoutScreen::CreateViews() { controls_.push_back(speed2); } + if (g_Config.touchRapidFireKey.show) { + DragDropButton *rapidFire = new DragDropButton(g_Config.touchRapidFireKey, rectImage, I_ARROW); + rapidFire->SetAngle(90.0f, 180.0f); + controls_.push_back(rapidFire); + } + if (g_Config.touchLKey.show) { controls_.push_back(new DragDropButton(g_Config.touchLKey, shoulderImage, I_L)); } diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 1c7f23af4e..b7320816c9 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -89,6 +89,7 @@ void TouchControlVisibilityScreen::CreateViews() { toggles_.push_back({ "Combo4", &g_Config.touchCombo4.show, I_5 }); toggles_.push_back({ "Alt speed 1", &g_Config.touchSpeed1Key.show, -1 }); toggles_.push_back({ "Alt speed 2", &g_Config.touchSpeed2Key.show, -1 }); + toggles_.push_back({ "Rapid Fire", &g_Config.touchRapidFireKey.show, -1 }); I18NCategory *mc = GetI18NCategory("MappableControls"); From b738c751e72fc45128c928799186c0811d9c1494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 2 Feb 2020 15:29:04 +0100 Subject: [PATCH 51/71] compat.ini: Workaround for timing problem in Hot Wheels, see #12604. --- assets/compat.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index 7bba442d39..a998d7e174 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -594,6 +594,10 @@ ULUS10433 = true ULES01376 = true ULUS10466 = true +# Hot Wheels - Ultimate Racing (issue #12604, this is a workaround) +ULUS10239 = true +ULES00828 = true + [JitInvalidationHack] # This is an absolutely awful hack that somehow prevents issues when clearing the JIT, # if the game has copied code with EmuHack opcodes or something. Hopefully will be able From f5148527919574c2e2ff74c21eeb31a0cbea57e6 Mon Sep 17 00:00:00 2001 From: Filippo Scognamiglio Date: Mon, 3 Feb 2020 20:57:34 +0100 Subject: [PATCH 52/71] Use ASharedMemory api instead of direct access to /dev/ashmem which is deprecated. --- Common/MemArenaAndroid.cpp | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/Common/MemArenaAndroid.cpp b/Common/MemArenaAndroid.cpp index 31eda9d3d0..4a9242b2df 100644 --- a/Common/MemArenaAndroid.cpp +++ b/Common/MemArenaAndroid.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include // Hopefully this ABI will never change... @@ -39,11 +41,41 @@ bool MemArena::NeedsProbing() { return false; } +static int get_sdk_version() { + char sdk[PROP_VALUE_MAX] = {0}; + if (__system_property_get("ro.build.version.sdk", sdk) != 0) { + return atoi(sdk); + } + return -1; +} + // ashmem_create_region - creates a new ashmem region and returns the file // descriptor, or <0 on error +// This function is defined in much later version of the ndk, so we can only access it via dlopen(). // `name' is an optional label to give the region (visible in /proc/pid/maps) // `size' is the size of the region, in page-aligned bytes static int ashmem_create_region(const char *name, size_t size) { + static void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); + using type_ASharedMemory_create = int(*)(const char *name, size_t size); + static type_ASharedMemory_create function_create = nullptr; + + if (handle != nullptr) { + function_create = + reinterpret_cast(dlsym(handle, "ASharedMemory_create")); + } + + if (function_create != nullptr) { + return function_create(name, size); + } else { + return -1; + } +} + +// legacy_ashmem_create_region - creates a new ashmem region and returns the file +// descriptor, or <0 on error +// `name' is an optional label to give the region (visible in /proc/pid/maps) +// `size' is the size of the region, in page-aligned bytes +static int legacy_ashmem_create_region(const char *name, size_t size) { int fd = open(ASHMEM_DEVICE, O_RDWR); if (fd < 0) return fd; @@ -76,7 +108,14 @@ size_t MemArena::roundup(size_t x) { void MemArena::GrabLowMemSpace(size_t size) { // Use ashmem so we don't have to allocate a file on disk! - fd = ashmem_create_region("PPSSPP_RAM", size); + const char* name = "PPSSPP_RAM"; + + // Since version 26 Android provides a new api for accessing SharedMemory. + if (get_sdk_version() >= 26) { + fd = ashmem_create_region(name, size); + } else { + fd = legacy_ashmem_create_region(name, size); + } // Note that it appears that ashmem is pinned by default, so no need to pin. if (fd < 0) { ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno)); From c00ce91a2a77c756c5cc6256246446777b7726cb Mon Sep 17 00:00:00 2001 From: Filippo Scognamiglio Date: Mon, 3 Feb 2020 22:10:47 +0100 Subject: [PATCH 53/71] Use System_GetPropertyInt(SYSPROP_SYSTEMVERSION to retrieve version. --- Common/MemArenaAndroid.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Common/MemArenaAndroid.cpp b/Common/MemArenaAndroid.cpp index 4a9242b2df..1a533b080c 100644 --- a/Common/MemArenaAndroid.cpp +++ b/Common/MemArenaAndroid.cpp @@ -20,6 +20,7 @@ #ifdef __ANDROID__ #include "base/logging.h" +#include "base/NativeApp.h" #include "MemoryUtil.h" #include "MemArena.h" #include "StringUtils.h" @@ -41,14 +42,6 @@ bool MemArena::NeedsProbing() { return false; } -static int get_sdk_version() { - char sdk[PROP_VALUE_MAX] = {0}; - if (__system_property_get("ro.build.version.sdk", sdk) != 0) { - return atoi(sdk); - } - return -1; -} - // ashmem_create_region - creates a new ashmem region and returns the file // descriptor, or <0 on error // This function is defined in much later version of the ndk, so we can only access it via dlopen(). @@ -111,7 +104,7 @@ void MemArena::GrabLowMemSpace(size_t size) { const char* name = "PPSSPP_RAM"; // Since version 26 Android provides a new api for accessing SharedMemory. - if (get_sdk_version() >= 26) { + if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 26) { fd = ashmem_create_region(name, size); } else { fd = legacy_ashmem_create_region(name, size); From 58f6426167bffa6d66cd5d9d490b340c6aee5512 Mon Sep 17 00:00:00 2001 From: Filippo Scognamiglio Date: Mon, 3 Feb 2020 22:17:27 +0100 Subject: [PATCH 54/71] Remove unused import. --- Common/MemArenaAndroid.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Common/MemArenaAndroid.cpp b/Common/MemArenaAndroid.cpp index 1a533b080c..a0b8614f19 100644 --- a/Common/MemArenaAndroid.cpp +++ b/Common/MemArenaAndroid.cpp @@ -32,7 +32,6 @@ #include #include #include -#include // Hopefully this ABI will never change... From 0a2aa2c3af0b8744af6d49437c1b258735028c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 5 Jan 2020 15:46:27 +0800 Subject: [PATCH 55/71] Qt/macOS: enable HiDPI ( retina display ) support --- Core/Core.cpp | 7 ++++++- Qt/QtMain.cpp | 34 +++++++++++++++++++--------------- Qt/mainwindow.cpp | 20 +++++++++++++++----- ext/native/base/NativeApp.h | 2 ++ 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 93c6eaf8d8..8d8ca89cd9 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -150,7 +150,12 @@ static bool IsWindowSmall(int pixelWidth, int pixelHeight) { // TODO: Feels like this belongs elsewhere. bool UpdateScreenScale(int width, int height) { bool smallWindow; -#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) +#if defined(USING_QT_UI) + g_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI); + float g_logical_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_LOGICAL_DPI); + g_dpi_scale_x = g_logical_dpi / g_dpi; + g_dpi_scale_y = g_logical_dpi / g_dpi; +#elif PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) g_dpi = (float)System_GetPropertyInt(SYSPROP_DISPLAY_DPI); g_dpi_scale_x = 96.0f / g_dpi; g_dpi_scale_y = 96.0f / g_dpi; diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 3c9641c1bd..83043c150e 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -159,11 +159,24 @@ int System_GetPropertyInt(SystemProperty prop) { #else return DEVICE_TYPE_DESKTOP; #endif + case SYSPROP_DISPLAY_COUNT: + return QApplication::screens().size(); default: return -1; } } +int System_GetPropertyFloat(SystemProperty prop) { + switch (prop) { + case SYSPROP_DISPLAY_LOGICAL_DPI: + return QApplication::primaryScreen()->logicalDotsPerInch(); + case SYSPROP_DISPLAY_DPI: + return QApplication::primaryScreen()->physicalDotsPerInch(); + default: + return System_GetPropertyInt(prop); + } +} + bool System_GetPropertyBool(SystemProperty prop) { switch (prop) { case SYSPROP_HAS_BACK_BUTTON: @@ -229,16 +242,6 @@ void LaunchBrowser(const char *url) QDesktopServices::openUrl(QUrl(url)); } -float CalculateDPIScale() -{ - // Sane default rather than check DPI -#if defined(USING_GLES2) - return 1.2f; -#else - return 1.0f; -#endif -} - static int mainInternal(QApplication &a) { #ifdef MOBILE_DEVICE emugl = new MainUI(); @@ -628,19 +631,21 @@ int main(int argc, char *argv[]) QGLFormat::setDefaultFormat(format); QApplication a(argc, argv); - QSize res = QApplication::desktop()->screenGeometry().size(); + QScreen* screen = a.primaryScreen(); + QSizeF res = screen->physicalSize(); if (res.width() < res.height()) res.transpose(); pixel_xres = res.width(); pixel_yres = res.height(); - g_dpi_scale_x = CalculateDPIScale(); - g_dpi_scale_y = CalculateDPIScale(); + + g_dpi_scale_x = screen->logicalDotsPerInchX() / screen->physicalDotsPerInchX(); + g_dpi_scale_y = screen->logicalDotsPerInchY() / screen->physicalDotsPerInchY(); g_dpi_scale_real_x = g_dpi_scale_x; g_dpi_scale_real_y = g_dpi_scale_y; dp_xres = (int)(pixel_xres * g_dpi_scale_x); dp_yres = (int)(pixel_yres * g_dpi_scale_y); - refreshRate = (int)(a.primaryScreen()->refreshRate() * 1000); + refreshRate = (int)(screen->refreshRate() * 1000); std::string savegame_dir = "."; std::string external_dir = "."; @@ -669,4 +674,3 @@ int main(int argc, char *argv[]) glslang::FinalizeProcess(); return ret; } - diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 4b4a6aaaf7..8b59c683ea 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -24,7 +24,7 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : { QDesktopWidget *desktop = QApplication::desktop(); int screenNum = QProcessEnvironment::systemEnvironment().value("SDL_VIDEO_FULLSCREEN_HEAD", "0").toInt(); - + // Move window to the center of selected screen QRect rect = desktop->screenGeometry(screenNum); move((rect.width()-frameGeometry().width()) / 4, (rect.height()-frameGeometry().height()) / 4); @@ -295,19 +295,22 @@ void MainWindow::consoleAct() void MainWindow::raiseTopMost() { setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); - raise(); + raise(); activateWindow(); } void MainWindow::SetFullScreen(bool fullscreen) { if (fullscreen) { +#if !PPSSPP_PLATFORM(MAC) menuBar()->hide(); - + emugl->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + // TODO: Shouldn't this be physicalSize()? emugl->resizeGL(emugl->size().width(), emugl->size().height()); // TODO: Won't showFullScreen do this for us? setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); +#endif showFullScreen(); InitPadLayout(dp_xres, dp_yres); @@ -315,8 +318,10 @@ void MainWindow::SetFullScreen(bool fullscreen) { if (GetUIState() == UISTATE_INGAME && !g_Config.bShowTouchControls) QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); } else { +#if !PPSSPP_PLATFORM(MAC) menuBar()->show(); updateMenus(); +#endif showNormal(); SetWindowScale(-1); @@ -346,7 +351,7 @@ void MainWindow::forumAct() QDesktopServices::openUrl(QUrl("https://forums.ppsspp.org/")); } -void MainWindow::gitAct() +void MainWindow::gitAct() { QDesktopServices::openUrl(QUrl("https://github.com/hrydgard/ppsspp/")); } @@ -394,9 +399,14 @@ void MainWindow::SetWindowScale(int zoom) { g_Config.iWindowWidth = width; g_Config.iWindowHeight = height; +#if !PPSSPP_PLATFORM(MAC) emugl->setFixedSize(g_Config.iWindowWidth, g_Config.iWindowHeight); + // TODO: Shouldn't this be scaled size? emugl->resizeGL(g_Config.iWindowWidth, g_Config.iWindowHeight); setFixedSize(sizeHint()); +#else + resize(g_Config.iWindowWidth, g_Config.iWindowHeight); +#endif } void MainWindow::SetGameTitle(QString text) @@ -585,7 +595,7 @@ void MainWindow::createMenus() } } langMenu->addActions(langGroup->actions()); - + // Help MenuTree* helpMenu = new MenuTree(this, menuBar(), QT_TR_NOOP("&Help")); helpMenu->add(new MenuAction(this, SLOT(websiteAct()), QT_TR_NOOP("Official &website"), QKeySequence::HelpContents)); diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index da2fba06ba..188bcbaa52 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -149,6 +149,7 @@ enum SystemProperty { SYSPROP_DISPLAY_XRES, SYSPROP_DISPLAY_YRES, SYSPROP_DISPLAY_REFRESH_RATE, // returns 1000*the refresh rate in Hz as it can be non-integer + SYSPROP_DISPLAY_LOGICAL_DPI, SYSPROP_DISPLAY_DPI, SYSPROP_DISPLAY_COUNT, SYSPROP_MOGA_VERSION, @@ -173,6 +174,7 @@ enum SystemProperty { std::string System_GetProperty(SystemProperty prop); int System_GetPropertyInt(SystemProperty prop); +int System_GetPropertyFloat(SystemProperty prop); bool System_GetPropertyBool(SystemProperty prop); std::vector __cameraGetDeviceList(); From 55bb58e13edbfeafec2b32342503740cf01a3f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 6 Jan 2020 01:04:07 +0800 Subject: [PATCH 56/71] NativeApp: add System_GetPropertyFloat all the places --- Core/Core.cpp | 2 +- Core/HLE/sceDisplay.cpp | 11 +++++------ Core/HW/StereoResampler.cpp | 2 +- Qt/QtMain.cpp | 12 ++++++------ SDL/SDLMain.cpp | 15 +++++++++++---- UWP/PPSSPP_UWPMain.cpp | 17 ++++++++++++----- Windows/MainWindow.cpp | 20 ++++++++++---------- Windows/main.cpp | 29 ++++++++++++++++++----------- android/jni/app-android.cpp | 11 +++++++++-- ext/native/base/NativeApp.h | 4 ++-- headless/Headless.cpp | 7 +++++-- ios/main.mm | 27 +++++++++++++++++---------- libretro/libretro.cpp | 17 ++++++++++++----- unittest/UnitTest.cpp | 5 ++++- 14 files changed, 113 insertions(+), 66 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 8d8ca89cd9..3f66794576 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -156,7 +156,7 @@ bool UpdateScreenScale(int width, int height) { g_dpi_scale_x = g_logical_dpi / g_dpi; g_dpi_scale_y = g_logical_dpi / g_dpi; #elif PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) - g_dpi = (float)System_GetPropertyInt(SYSPROP_DISPLAY_DPI); + g_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI); g_dpi_scale_x = 96.0f / g_dpi; g_dpi_scale_y = 96.0f / g_dpi; #else diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index d1a2de1110..3e6b526a8f 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -435,8 +435,7 @@ static bool IsRunningSlow() { best = std::max(fpsHistory[index], best); } - // Note that SYSPROP_DISPLAY_REFRESH_RATE is multiplied by 1000. - return best < System_GetPropertyInt(SYSPROP_DISPLAY_REFRESH_RATE) * (1.0 / 1001.0); + return best < System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE) * 0.999; } return false; @@ -510,12 +509,12 @@ static void DoFrameDropLogging(float scaledTimestep) { static int CalculateFrameSkip() { int frameSkipNum; - if (g_Config.iFrameSkipType == 1) { + if (g_Config.iFrameSkipType == 1) { // Calculate the frames to skip dynamically using the set percentage of the current fps - frameSkipNum = ceil( flips * (static_cast(g_Config.iFrameSkip) / 100.00) ); - } else { + frameSkipNum = ceil( flips * (static_cast(g_Config.iFrameSkip) / 100.00) ); + } else { // Use the set number of frames to skip - frameSkipNum = g_Config.iFrameSkip; + frameSkipNum = g_Config.iFrameSkip; } return frameSkipNum; } diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 8bf58c3f4c..3894e6d771 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -71,7 +71,7 @@ StereoResampler::StereoResampler() // Some Android devices are v-synced to non-60Hz framerates. We simply timestretch audio to fit. // TODO: should only do this if auto frameskip is off? - float refresh = System_GetPropertyInt(SYSPROP_DISPLAY_REFRESH_RATE) / 1000.0f; + float refresh = System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE); // If framerate is "close"... if (refresh != 60.0f && refresh > 50.0f && refresh < 70.0f) { diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 83043c150e..f117d3a762 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -42,7 +42,7 @@ #include MainUI *emugl = nullptr; -static int refreshRate = 60000; +static float refreshRate = 60.f; static int browseFileEvent = -1; static int browseFolderEvent = -1; @@ -145,8 +145,6 @@ int System_GetPropertyInt(SystemProperty prop) { switch (prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return 44100; - case SYSPROP_DISPLAY_REFRESH_RATE: - return refreshRate; case SYSPROP_DEVICE_TYPE: #if defined(__ANDROID__) return DEVICE_TYPE_MOBILE; @@ -166,14 +164,16 @@ int System_GetPropertyInt(SystemProperty prop) { } } -int System_GetPropertyFloat(SystemProperty prop) { +float System_GetPropertyFloat(SystemProperty prop) { switch (prop) { + case SYSPROP_DISPLAY_REFRESH_RATE: + return refreshRate; case SYSPROP_DISPLAY_LOGICAL_DPI: return QApplication::primaryScreen()->logicalDotsPerInch(); case SYSPROP_DISPLAY_DPI: return QApplication::primaryScreen()->physicalDotsPerInch(); default: - return System_GetPropertyInt(prop); + return -1; } } @@ -645,7 +645,7 @@ int main(int argc, char *argv[]) dp_xres = (int)(pixel_xres * g_dpi_scale_x); dp_yres = (int)(pixel_yres * g_dpi_scale_y); - refreshRate = (int)(screen->refreshRate() * 1000); + refreshRate = screen->refreshRate(); std::string savegame_dir = "."; std::string external_dir = "."; diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index e8bc75fc4d..548d588ea5 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -65,7 +65,7 @@ static int g_QuitRequested = 0; static int g_DesktopWidth = 0; static int g_DesktopHeight = 0; -static int g_RefreshRate = 60000; +static float g_RefreshRate = 60.f; int getDisplayNumber(void) { int displayNumber = 0; @@ -307,8 +307,6 @@ int System_GetPropertyInt(SystemProperty prop) { switch (prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return 44100; - case SYSPROP_DISPLAY_REFRESH_RATE: - return g_RefreshRate; case SYSPROP_DEVICE_TYPE: #if defined(MOBILE_DEVICE) return DEVICE_TYPE_MOBILE; @@ -322,6 +320,15 @@ int System_GetPropertyInt(SystemProperty prop) { } } +float System_GetPropertyFloat(SystemProperty prop) { + switch (prop) { + case SYSPROP_DISPLAY_REFRESH_RATE: + return g_RefreshRate; + default: + return -1; + } +} + bool System_GetPropertyBool(SystemProperty prop) { switch (prop) { case SYSPROP_HAS_BACK_BUTTON: @@ -511,7 +518,7 @@ int main(int argc, char *argv[]) { } g_DesktopWidth = displayMode.w; g_DesktopHeight = displayMode.h; - g_RefreshRate = (int)(displayMode.refresh_rate * 1000); + g_RefreshRate = displayMode.refresh_rate; SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 1f77adf425..35739b4dc8 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -96,7 +96,7 @@ PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptrGetSampleRate() : -1; - case SYSPROP_DISPLAY_REFRESH_RATE: - return 60000; case SYSPROP_DEVICE_TYPE: { auto ver = Windows::System::Profile::AnalyticsInfo::VersionInfo; @@ -375,6 +373,15 @@ int System_GetPropertyInt(SystemProperty prop) { } } +float System_GetPropertyFloat(SystemProperty prop) { + switch (prop) { + case SYSPROP_DISPLAY_REFRESH_RATE: + return 60.f; + default: + return -1; + } +} + bool VulkanMayBeAvailable() { return false; } @@ -477,7 +484,7 @@ bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &default std::string GetCPUBrandString() { Platform::String^ cpu_id = nullptr; Platform::String^ cpu_name = nullptr; - + // GUID_DEVICE_PROCESSOR: {97FADB10-4E33-40AE-359C-8BEF029DBDD0} Platform::String^ if_filter = L"System.Devices.InterfaceClassGuid:=\"{97FADB10-4E33-40AE-359C-8BEF029DBDD0}\""; diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 2462568dba..3d835c9ea0 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -154,7 +154,7 @@ namespace MainWindow // Register classes - Main Window WNDCLASSEX wcex; memset(&wcex, 0, sizeof(wcex)); - wcex.cbSize = sizeof(WNDCLASSEX); + wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = 0; // Show in taskbar wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.hInstance = hInstance; @@ -228,7 +228,7 @@ namespace MainWindow if (++g_Config.iInternalResolution > RESOLUTION_MAX) g_Config.iInternalResolution = 0; } - + // Taking auto-texture scaling into account if (g_Config.iTexScalingLevel == TEXSCALING_AUTO) setTexScalingMultiplier(0); @@ -329,7 +329,7 @@ namespace MainWindow dwStyle &= ~WS_POPUP; // Re-add caption and border styles. dwStyle |= WS_OVERLAPPEDWINDOW; - + // Put back the menu bar. ::SetMenu(hWnd, menu); } else { @@ -437,7 +437,7 @@ namespace MainWindow bool portrait = g_Config.IsPortrait(); // We want to adjust for DPI but still get an integer pixel scaling ratio. - double dpi_scale = 96.0 / System_GetPropertyInt(SYSPROP_DISPLAY_DPI); + double dpi_scale = 96.0 / System_GetPropertyFloat(SYSPROP_DISPLAY_DPI); int scale = (int)ceil(2.0 / dpi_scale); GetWindowSizeAtResolution(scale * (portrait ? 272 : 480), scale * (portrait ? 480 : 272), &windowWidth, &windowHeight); @@ -547,7 +547,7 @@ namespace MainWindow if (disasmWindow[0]) delete disasmWindow[0]; disasmWindow[0] = 0; - + #if PPSSPP_API(ANY_GL) DialogManager::RemoveDlg(geDebuggerWindow); if (geDebuggerWindow) @@ -682,7 +682,7 @@ namespace MainWindow } return 0; } - + LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: @@ -691,7 +691,7 @@ namespace MainWindow RemoveMenu(GetMenu(hWnd), ID_OPTIONS_DIRECT3D11, MF_BYCOMMAND); } break; - + case WM_GETMINMAXINFO: { MINMAXINFO *minmax = reinterpret_cast(lParam); @@ -975,10 +975,10 @@ namespace MainWindow case WM_SYSCOMMAND: { switch (wParam) { - case SC_SCREENSAVE: + case SC_SCREENSAVE: return 0; case SC_MONITORPOWER: - return 0; + return 0; } return DefWindowProc(hWnd, message, wParam, lParam); } @@ -988,7 +988,7 @@ namespace MainWindow } return 0; } - + void Redraw() { InvalidateRect(hwndDisplay,0,0); } diff --git a/Windows/main.cpp b/Windows/main.cpp index 5ceb76d849..facf559935 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -126,7 +126,7 @@ std::string GetVideoCardDriverVersion() { } IWbemLocator *pIWbemLocator = NULL; - hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, + hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID *)&pIWbemLocator); if (FAILED(hr)) { CoUninitialize(); @@ -143,9 +143,9 @@ std::string GetVideoCardDriverVersion() { return retvalue; } - hr = CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, + hr = CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL,EOAC_DEFAULT); - + BSTR bstrWQL = SysAllocString(L"WQL"); BSTR bstrPath = SysAllocString(L"select * from Win32_VideoController"); IEnumWbemClassObject* pEnum; @@ -232,12 +232,8 @@ int System_GetPropertyInt(SystemProperty prop) { switch (prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return winAudioBackend ? winAudioBackend->GetSampleRate() : -1; - case SYSPROP_DISPLAY_REFRESH_RATE: - return 60000; case SYSPROP_DEVICE_TYPE: return DEVICE_TYPE_DESKTOP; - case SYSPROP_DISPLAY_DPI: - return ScreenDPI(); case SYSPROP_DISPLAY_COUNT: return GetSystemMetrics(SM_CMONITORS); default: @@ -245,6 +241,17 @@ int System_GetPropertyInt(SystemProperty prop) { } } +float System_GetPropertyFloat(SystemProperty prop) { + switch (prop) { + case SYSPROP_DISPLAY_REFRESH_RATE: + return 60.f; + case SYSPROP_DISPLAY_DPI: + return (float)ScreenDPI(); + default: + return -1; + } +} + bool System_GetPropertyBool(SystemProperty prop) { switch (prop) { case SYSPROP_HAS_FILE_BROWSER: @@ -491,7 +498,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin LogManager::Init(); - // On Win32 it makes more sense to initialize the system directories here + // On Win32 it makes more sense to initialize the system directories here // because the next place it was called was in the EmuThread, and it's too late by then. g_Config.internalDataDirectory = W32Util::UserDocumentsPath(); InitSysDirectories(); @@ -589,7 +596,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // - The -l switch is expected to show the log console, REGARDLESS of config settings. // - It should be possible to log to a file without showing the console. LogManager::GetInstance()->GetConsoleListener()->Init(showLog, 150, 120, "PPSSPP Debug Console"); - + if (debugLogLevel) LogManager::GetInstance()->SetAllLogLevels(LogTypes::LDEBUG); @@ -603,7 +610,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin HWND hwndMain = MainWindow::GetHWND(); HWND hwndDisplay = MainWindow::GetDisplayHWND(); - + //initialize custom controls CtrlDisAsmView::init(); CtrlMemView::init(); @@ -636,7 +643,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin { //hack to enable/disable menu command accelerate keys MainWindow::UpdateCommands(); - + //hack to make it possible to get to main window from floating windows with Esc if (msg.hwnd != hwndMain && msg.wParam == VK_ESCAPE) BringWindowToTop(hwndMain); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index dd8058df43..0ceace3732 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -303,8 +303,15 @@ int System_GetPropertyInt(SystemProperty prop) { return optimalSampleRate; case SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER: return optimalFramesPerBuffer; + default: + return -1; + } +} + +float System_GetPropertyFloat(SystemProperty prop) { + switch (prop) { case SYSPROP_DISPLAY_REFRESH_RATE: - return (int)(display_hz * 1000.0); + return display_hz; default: return -1; } @@ -682,7 +689,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, hasSetThreadName = true; setCurrentThreadName("AndroidRender"); } - + if (useCPUThread) { // This is the "GPU thread". if (graphicsContext) diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index 188bcbaa52..f1f348acc7 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -148,7 +148,7 @@ enum SystemProperty { SYSPROP_SYSTEMVERSION, SYSPROP_DISPLAY_XRES, SYSPROP_DISPLAY_YRES, - SYSPROP_DISPLAY_REFRESH_RATE, // returns 1000*the refresh rate in Hz as it can be non-integer + SYSPROP_DISPLAY_REFRESH_RATE, SYSPROP_DISPLAY_LOGICAL_DPI, SYSPROP_DISPLAY_DPI, SYSPROP_DISPLAY_COUNT, @@ -174,7 +174,7 @@ enum SystemProperty { std::string System_GetProperty(SystemProperty prop); int System_GetPropertyInt(SystemProperty prop); -int System_GetPropertyFloat(SystemProperty prop); +float System_GetPropertyFloat(SystemProperty prop); bool System_GetPropertyBool(SystemProperty prop); std::vector __cameraGetDeviceList(); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 72c6620c6c..b92f36d6ba 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -74,6 +74,9 @@ std::string System_GetProperty(SystemProperty prop) { return ""; } int System_GetPropertyInt(SystemProperty prop) { return -1; } +float System_GetPropertyFloat(SystemProperty prop) { + return -1; +} bool System_GetPropertyBool(SystemProperty prop) { return false; } @@ -225,7 +228,7 @@ int main(int argc, const char* argv[]) const char *stateToLoad = 0; GPUCore gpuCore = GPUCORE_NULL; CPUCore cpuCore = CPUCore::JIT; - + std::vector testFilenames; const char *mountIso = 0; const char *mountRoot = 0; @@ -321,7 +324,7 @@ int main(int argc, const char* argv[]) LogManager::Init(); LogManager *logman = LogManager::GetInstance(); - + PrintfLogger *printfLogger = new PrintfLogger(); for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) { diff --git a/ios/main.mm b/ios/main.mm index dfa1548296..eee92a0cad 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -80,8 +80,6 @@ int System_GetPropertyInt(SystemProperty prop) { switch (prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return 44100; - case SYSPROP_DISPLAY_REFRESH_RATE: - return 60000; case SYSPROP_DEVICE_TYPE: return DEVICE_TYPE_MOBILE; default: @@ -89,6 +87,15 @@ int System_GetPropertyInt(SystemProperty prop) { } } +float System_GetPropertyFloat(SystemProperty prop) { + switch (prop) { + case SYSPROP_DISPLAY_REFRESH_RATE: + return 60.f; + default: + return -1; + } +} + bool System_GetPropertyBool(SystemProperty prop) { switch (prop) { case SYSPROP_HAS_BACK_BUTTON: @@ -139,7 +146,7 @@ BOOL SupportsTaptic() { return NO; } - + // http://www.mikitamanko.com/blog/2017/01/29/haptic-feedback-with-uifeedbackgenerator/ // use private API against UIDevice to determine the haptic stepping // 2 - iPhone 7 or above, full taptic feedback @@ -150,7 +157,7 @@ BOOL SupportsTaptic() } void Vibrate(int mode) { - + if(SupportsTaptic()) { PPSSPPUIApplication* app = (PPSSPPUIApplication*)[UIApplication sharedApplication]; @@ -165,10 +172,10 @@ void Vibrate(int mode) { { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; NSArray *pattern = @[@YES, @30, @NO, @2]; - + dictionary[@"VibePattern"] = pattern; dictionary[@"Intensity"] = @2; - + AudioServicesPlaySystemSoundWithVibration(kSystemSoundID_Vibrate, nil, dictionary); } } @@ -180,15 +187,15 @@ int main(int argc, char *argv[]) fprintf(stderr, "Unable to cleanly obtain CS_DEBUGGED - probably not jailbroken. Attempting old method.\n"); ptrace(PTRACE_TRACEME, 0, 0, 0); } - + PROFILE_INIT(); - + @autoreleasepool { NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/assets/"]; - + NativeInit(argc, (const char**)argv, documentsPath.UTF8String, bundlePath.UTF8String, NULL); - + return UIApplicationMain(argc, argv, NSStringFromClass([PPSSPPUIApplication class]), NSStringFromClass([AppDelegate class])); } } diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 9335063608..11ddce7d24 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -322,10 +322,10 @@ void retro_init(void) { g_Config.bEnableSound = true; g_Config.bAudioResampler = false; g_Config.iCwCheatRefreshRate = 60; - + g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE; g_Config.iPSPModel = PSP_MODEL_SLIM; - + LogManager::Init(); host = new LibretroHost; @@ -730,7 +730,7 @@ bool retro_serialize(void *data, size_t size) { if (useEmuThread) { EmuThreadPause(); // Does nothing if already paused } - + SaveState::SaveStart state; assert(CChunkFileReader::MeasurePtr(state) <= size); bool retVal = CChunkFileReader::SavePtr((u8 *)data, state) == CChunkFileReader::ERROR_NONE; @@ -739,7 +739,7 @@ bool retro_serialize(void *data, size_t size) { EmuThreadStart(); sleep_ms(4); } - + return retVal; } @@ -786,8 +786,15 @@ int System_GetPropertyInt(SystemProperty prop) { switch (prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return SAMPLERATE; + default: + return -1; + } +} + +float System_GetPropertyFloat(SystemProperty prop) { + switch (prop) { case SYSPROP_DISPLAY_REFRESH_RATE: - return 60000; + return 60.f; default: return -1; } diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 8e0d47f122..e2b94af96a 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -55,6 +55,9 @@ std::string System_GetProperty(SystemProperty prop) { return ""; } int System_GetPropertyInt(SystemProperty prop) { return -1; } +float System_GetPropertyFloat(SystemProperty prop) { + return -1; +} bool System_GetPropertyBool(SystemProperty prop) { return false; } @@ -157,7 +160,7 @@ void fcs(float angle, float &sinout, float &cosout) { int phasein = angle * (1 << BITSPERQUARTER); // Modulo phase into quarter, convert to float 0..1 float modphase = (phasein & ((1<> BITSPERQUARTER; // Recognize quarter if (!quarter) { From ace88bf4735348e0de243b3f3ac4fd3fab74aae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 4 Feb 2020 11:55:54 +0800 Subject: [PATCH 57/71] Modify Info.plist automatically --- CMakeLists.txt | 2 ++ Qt/macbundle.sh | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 Qt/macbundle.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index e724ec6939..b92314f113 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2013,6 +2013,8 @@ if(TargetBin) add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${SHADER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource}) if(TARGET SDL2::SDL2 AND NOT USING_QT_UI) add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/SDL/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPSDL.app") + elseif(USING_QT_UI) + add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/Qt/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPQt.app") endif() endif() elseif(WIN32) diff --git a/Qt/macbundle.sh b/Qt/macbundle.sh new file mode 100644 index 0000000000..3272de6657 --- /dev/null +++ b/Qt/macbundle.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +PPSSPP="${1}" +PPSSPPQt="${PPSSPP}/Contents/MacOS/PPSSPPQt" + +if [ ! -f "${PPSSPPQt}" ]; then + echo "No such file: ${PPSSPPQt}!" + exit 0 +fi + +plutil -replace NSPrincipalClass -string NSApplication ${PPSSPP}/Contents/Info.plist +plutil -replace NSHighResolutionCapable -bool YES ${PPSSPP}/Contents/Info.plist + +# TODO: install SDL and Qt frameworks From 427e40f46e4059d78a83f004743074495702b5af Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Wed, 5 Feb 2020 11:00:08 -0600 Subject: [PATCH 58/71] libretro: android: Do not build in the standalone UI --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b92314f113..3a979a0499 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -718,7 +718,7 @@ if(ANDROID) Common/GL/GLInterfaceBase.h ) - set(nativeExtra ${nativeExtra} + set(NativeAppSource ${NativeAppSource} ext/native/base/NativeApp.h android/jni/app-android.cpp android/jni/AndroidEGLContext.cpp @@ -885,7 +885,10 @@ if(ANDROID) elseif(ARM64) set(NativeAppSource ${NativeAppSource} android/jni/Arm64EmitterTest.cpp) endif() - set(nativeExtra ${nativeExtra} ${NativeAppSource}) + + if (NOT LIBRETRO) + set(nativeExtra ${nativeExtra} ${NativeAppSource}) + endif() endif() set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp From 3f7c6467e8fc42b2630e87f43c1bbca044a1f105 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Wed, 5 Feb 2020 11:51:47 -0600 Subject: [PATCH 59/71] libretro: android: ios: Add stub camera functionality --- libretro/libretro.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 11ddce7d24..45185748ca 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -806,3 +806,7 @@ void NativeUpdate() {} void NativeRender(GraphicsContext *graphicsContext) {} void NativeResized() {} bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultvalue, std::wstring &outvalue) { return false; } + +#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) +std::vector __cameraGetDeviceList() { return std::vector(); } +#endif From a4ba5d485933b5c11ae4a3f4953de23a322d08e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 7 Feb 2020 00:09:42 +0100 Subject: [PATCH 60/71] HttpClient/Buffer: Just some tweaks/sanity checks After a mysterious crash report by Azuka on Discord. --- ext/native/base/buffer.cpp | 18 ++++++++++++------ ext/native/net/http_client.cpp | 5 +++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/ext/native/base/buffer.cpp b/ext/native/base/buffer.cpp index 8e48848319..68cc412dd3 100644 --- a/ext/native/base/buffer.cpp +++ b/ext/native/base/buffer.cpp @@ -26,9 +26,13 @@ Buffer::Buffer() { } Buffer::~Buffer() { } char *Buffer::Append(ssize_t length) { - size_t old_size = data_.size(); - data_.resize(old_size + length); - return &data_[0] + old_size; + if (length > 0) { + size_t old_size = data_.size(); + data_.resize(old_size + length); + return &data_[0] + old_size; + } else { + return nullptr; + } } void Buffer::Append(const std::string &str) { @@ -44,8 +48,10 @@ void Buffer::Append(const char *str) { void Buffer::Append(const Buffer &other) { size_t len = other.size(); - char *dest = Append(len); - memcpy(dest, &other.data_[0], len); + if (len > 0) { + char *dest = Append(len); + memcpy(dest, &other.data_[0], len); + } } void Buffer::AppendValue(int value) { @@ -175,7 +181,7 @@ bool Buffer::ReadAll(int fd, int hintSize) { } else if (hintSize >= 1024 * 16) { buf.resize(hintSize / 16); } else { - buf.resize(1024); + buf.resize(4096); } while (true) { diff --git a/ext/native/net/http_client.cpp b/ext/native/net/http_client.cpp index 05bb656830..91fec283a8 100644 --- a/ext/native/net/http_client.cpp +++ b/ext/native/net/http_client.cpp @@ -383,6 +383,11 @@ int Client::ReadResponseEntity(Buffer *readbuf, const std::vector & } } + if (contentLength < 0) { + // Just sanity checking... + contentLength = 0; + } + if (!contentLength && progress) { // Content length is unknown. // Set progress to 1% so it looks like something is happening... From 5009698cc09e01ff2c84d9ed632bcfa162cdcb37 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 26 Jan 2020 10:43:18 -0800 Subject: [PATCH 61/71] Core: Use a shared_ptr for i18n categories. This does not make them thread safe, but it reduces the chances of a crash a bit (see #12594.) --- Core/CwCheat.cpp | 2 +- Core/Dialog/PSPDialog.cpp | 2 +- Core/Dialog/PSPMsgDialog.cpp | 4 +- Core/Dialog/PSPNetconfDialog.cpp | 6 +-- Core/Dialog/PSPOskDialog.cpp | 2 +- Core/Dialog/PSPSaveDialog.cpp | 8 +-- Core/Dialog/SavedataParam.cpp | 8 +-- Core/FileSystems/BlockDevices.cpp | 2 +- Core/FileSystems/DirectoryFileSystem.cpp | 6 +-- Core/HLE/proAdhoc.cpp | 2 +- Core/HLE/sceDisplay.cpp | 2 +- Core/SaveState.cpp | 10 ++-- Core/TextureReplacer.cpp | 2 +- Core/Util/GameManager.cpp | 6 +-- GPU/Common/FramebufferCommon.cpp | 2 +- GPU/Directx9/GPU_DX9.cpp | 2 +- GPU/Directx9/ShaderManagerDX9.cpp | 2 +- GPU/GLES/GPU_GLES.cpp | 2 +- GPU/GLES/ShaderManagerGLES.cpp | 2 +- GPU/GLES/TextureCacheGLES.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- Qt/QtMain.cpp | 2 +- UI/ComboKeyMappingScreen.cpp | 6 +-- UI/ControlMappingScreen.cpp | 26 ++++----- UI/ControlMappingScreen.h | 7 +-- UI/CwCheatScreen.cpp | 4 +- UI/DevScreens.cpp | 38 ++++++------- UI/DevScreens.h | 3 +- UI/DiscordIntegration.cpp | 4 +- UI/DisplayLayoutScreen.cpp | 6 +-- UI/EmuScreen.cpp | 26 ++++----- UI/GPUDriverTestScreen.cpp | 4 +- UI/GameScreen.cpp | 24 ++++----- UI/GameSettingsScreen.cpp | 68 ++++++++++++------------ UI/InstallZipScreen.cpp | 6 +-- UI/MainScreen.cpp | 10 ++-- UI/MiscScreens.cpp | 20 +++---- UI/NativeApp.cpp | 8 +-- UI/PauseScreen.cpp | 19 +++---- UI/RemoteISOScreen.cpp | 16 +++--- UI/ReportScreen.cpp | 22 ++++---- UI/SavedataScreen.cpp | 12 ++--- UI/Store.cpp | 8 +-- UI/TiltAnalogSettingsScreen.cpp | 4 +- UI/TouchControlLayoutScreen.cpp | 6 +-- UI/TouchControlVisibilityScreen.cpp | 6 +-- Windows/EmuThread.cpp | 4 +- Windows/GPU/D3D11Context.cpp | 2 +- Windows/GPU/WindowsGLContext.cpp | 2 +- Windows/MainWindowMenu.cpp | 14 ++--- Windows/main.cpp | 2 +- ext/native/i18n/i18n.cpp | 12 ++--- ext/native/i18n/i18n.h | 9 ++-- ext/native/ui/ui_screen.cpp | 8 +-- 54 files changed, 244 insertions(+), 240 deletions(-) diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 8c62c42e3a..886b0acbb1 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -340,7 +340,7 @@ void CWCheatEngine::CreateCheatFile() { fclose(f); } if (!File::Exists(activeCheatFile)) { - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Unable to create cheat file, disk may be full")); } } diff --git a/Core/Dialog/PSPDialog.cpp b/Core/Dialog/PSPDialog.cpp index b6f00f86a3..64b5d6197d 100644 --- a/Core/Dialog/PSPDialog.cpp +++ b/Core/Dialog/PSPDialog.cpp @@ -202,7 +202,7 @@ void PSPDialog::DisplayButtons(int flags, const char *caption) truncate_cpy(safeCaption, caption); } - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); float x1 = 183.5f, x2 = 261.5f; if (GetCommonParam()->buttonSwap == 1) { x1 = 261.5f; diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index ca9e283f3e..7ef30d7691 100755 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -156,7 +156,7 @@ void PSPMsgDialog::DisplayMessage(std::string text, bool hasYesNo, bool hasOK) if (hasYesNo) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); const char *choiceText; u32 yesColor, noColor; float x, w; @@ -193,7 +193,7 @@ void PSPMsgDialog::DisplayMessage(std::string text, bool hasYesNo, bool hasOK) } if (hasOK) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); float x, w; x = 240.0f; w = 15.0f; diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index 25c9d42baf..a4f4df8083 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -62,14 +62,14 @@ void PSPNetconfDialog::DrawBanner() { // TODO: Draw a hexagon icon PPGeDrawImage(10, 6, 12.0f, 12.0f, 1, 10, 1, 10, 10, 10, CalcFadedColor(0xFFFFFFFF)); - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); PPGeDrawText(di->T("Network Connection"), 30, 11, PPGE_ALIGN_VCENTER, 0.6f, CalcFadedColor(0xFFFFFFFF)); } int PSPNetconfDialog::Update(int animSpeed) { UpdateButtons(); - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *err = GetI18NCategory("Error"); + auto di = GetI18NCategory("Dialog"); + auto err = GetI18NCategory("Error"); const float WRAP_WIDTH = 254.0f; const int confirmBtnImage = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? I_CROSS : I_CIRCLE; const int confirmBtn = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CROSS : CTRL_CIRCLE; diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index cf09c4db42..b1b7cde8af 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -917,7 +917,7 @@ int PSPOskDialog::Update(int animSpeed) { PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363)); RenderKeyboard(); - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); PPGeDrawImage(I_SQUARE, 365, 222, 16, 16, 0, CalcFadedColor(0xFFFFFFFF)); PPGeDrawText(di->T("Space"), 390, 222, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index d078382897..e0a27decaf 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -280,7 +280,7 @@ const std::string PSPSaveDialog::GetSelectedSaveDirName() const void PSPSaveDialog::DisplayBanner(int which) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); PPGeDrawRect(0, 0, 480, 23, CalcFadedColor(0x65636358)); const char *title; switch (which) @@ -394,7 +394,7 @@ void PSPSaveDialog::DisplaySaveDataInfo1() { std::lock_guard guard(paramLock); if (param.GetFileInfo(currentSelectedSave).size == 0) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); PPGeDrawText(di->T("NEW DATA"), 180, 136, PPGE_ALIGN_VCENTER, 0.6f, CalcFadedColor(0xFFFFFFFF)); } else { char title[512]; @@ -539,7 +539,7 @@ void PSPSaveDialog::DisplayMessage(std::string text, bool hasYesNo) float h2 = h * (float)n / 2.0f; if (hasYesNo) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); const char *choiceText; u32 yesColor, noColor; float x, w; @@ -621,7 +621,7 @@ int PSPSaveDialog::Update(int animSpeed) cancelButtonFlag = CTRL_CIRCLE; } - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); switch (display) { diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 646a6bd20c..c014d0ca3f 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -392,7 +392,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD if (!pspFileSystem.GetFileInfo(dirPath).exists) { if (!pspFileSystem.MkDir(dirPath)) { - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Unable to write savedata, disk may be full")); } } @@ -422,7 +422,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD } if (EncryptData(decryptMode, cryptedData, &cryptedSize, &aligned_len, cryptedHash, (hasKey ? param->key : 0)) != 0) { - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Save encryption failed. This save won't work on real PSP"), 6.0f); ERROR_LOG(SCEUTILITY,"Save encryption failed. This save won't work on real PSP"); delete[] cryptedData; @@ -689,7 +689,7 @@ void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, co // Don't notify the user if we're not going to upgrade the save. if (!g_Config.bEncryptSave) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); host->NotifyUserMessage(di->T("When you save, it will load on a PSP, but not an older PPSSPP"), 6.0f); host->NotifyUserMessage(di->T("Old savedata detected"), 6.0f); } @@ -701,7 +701,7 @@ void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, co } if (g_Config.bSavedataUpgrade) { decryptMode = prevCryptMode; - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); host->NotifyUserMessage(di->T("When you save, it will not work on outdated PSP Firmware anymore"), 6.0f); host->NotifyUserMessage(di->T("Old savedata detected"), 6.0f); } diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index 0826cafed6..0131abdeb1 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -64,7 +64,7 @@ u32 BlockDevice::CalculateCRC() { } void BlockDevice::NotifyReadError() { - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); if (!reportedError_) { host->NotifyUserMessage(err->T("Game disc read error - ISO corrupt"), 6.0f); reportedError_ = true; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index e6e97546e2..972c1c5d03 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -251,7 +251,7 @@ bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileNam if (w32err == ERROR_DISK_FULL || w32err == ERROR_NOT_ENOUGH_QUOTA) { // This is returned when the disk is full. - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Disk full while writing data")); error = SCE_KERNEL_ERROR_ERRNO_NO_PERM; } else if (!success) { @@ -315,7 +315,7 @@ bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileNam } } else if (errno == ENOSPC) { // This is returned when the disk is full. - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Disk full while writing data")); error = SCE_KERNEL_ERROR_ERRNO_NO_PERM; } else { @@ -383,7 +383,7 @@ size_t DirectoryFileHandle::Write(const u8* pointer, s64 size) if (diskFull) { ERROR_LOG(FILESYS, "Disk full"); - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Disk full while writing data")); // We only return an error when the disk is actually full. // When writing this would cause the disk to be full, so it wasn't written, we return 0. diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 6299c1d2b8..6e92b7305a 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1457,7 +1457,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ int sent = send(metasocket, (char*)&packet, sizeof(packet), 0); changeBlockingMode(metasocket, 1); // Change to non-blocking if (sent > 0) { - I18NCategory *n = GetI18NCategory("Networking"); + auto n = GetI18NCategory("Networking"); host->NotifyUserMessage(n->T("Network Initialized"), 1.0); return 0; } diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 3e6b526a8f..4644241f0b 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -720,7 +720,7 @@ void __DisplayFlip(int cyclesLate) { static bool hasNotifiedSlow = false; if (!g_Config.bHideSlowWarnings && !hasNotifiedSlow && PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && IsRunningSlow()) { #ifndef _DEBUG - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); if (g_Config.bSoftwareRendering) { host->NotifyUserMessage(err->T("Running slow: Try turning off Software Rendering"), 6.0f, 0xFF30D0D0); } else { diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 40e93d8ce5..198113e3a4 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -381,7 +381,7 @@ namespace SaveState return StringFromFormat("%s (%c)", title.c_str(), slotChar); } if (detectSlot(UNDO_STATE_EXTENSION)) { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); // Allow the number to be positioned where it makes sense. std::string undo = sy->T("undo %c"); return title + " (" + StringFromFormat(undo.c_str(), slotChar) + ")"; @@ -402,7 +402,7 @@ namespace SaveState } // The file can't be loaded - let's note that. - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); return File::GetFilename(filename) + " " + sy->T("(broken)"); } @@ -437,7 +437,7 @@ namespace SaveState if (!fn.empty()) { Load(fn, callback, cbUserData); } else { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); if (callback) callback(Status::FAILURE, sy->T("Failed to load state. Error in the file system."), cbUserData); } @@ -494,7 +494,7 @@ namespace SaveState SaveScreenshot(shot, Callback(), 0); Save(fn + ".tmp", renameCallback, cbUserData); } else { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); if (callback) callback(Status::FAILURE, sy->T("Failed to save state. Error in the file system."), cbUserData); } @@ -729,7 +729,7 @@ namespace SaveState std::string reason; std::string title; - I18NCategory *sc = GetI18NCategory("Screen"); + auto sc = GetI18NCategory("Screen"); const char *i18nLoadFailure = sc->T("Load savestate failed", ""); const char *i18nSaveFailure = sc->T("Save State Failed", ""); if (strlen(i18nLoadFailure) == 0) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 6087c87aeb..d7c08f7ff8 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -168,7 +168,7 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, bool isOverride) { } if (filenameWarning) { - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("textures.ini filenames may not be cross-platform"), 6.0f); } diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index d731de0476..1e23a5fa79 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -261,7 +261,7 @@ bool GameManager::InstallGame(const std::string &url, const std::string &fileNam return InstallRawISO(fileName, shortFilename, deleteAfter); } - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); installInProgress_ = true; std::string pspGame = GetSysDirectory(DIRECTORY_GAME); @@ -312,7 +312,7 @@ bool GameManager::InstallGame(const std::string &url, const std::string &fileNam } bool GameManager::DetectTexturePackDest(struct zip *z, int iniIndex, std::string *dest) { - I18NCategory *iz = GetI18NCategory("InstallZip"); + auto iz = GetI18NCategory("InstallZip"); struct zip_stat zstat; zip_stat_index(z, iniIndex, 0, &zstat); @@ -491,7 +491,7 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, size_t allBytes = 0; size_t bytesCopied = 0; - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); auto fileAllowed = [&](const char *fn) { if (!allowRoot && strchr(fn, '/') == 0) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 4addac91d1..ea63dcd180 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1906,7 +1906,7 @@ void FramebufferManagerCommon::UpdateFramebufUsage(VirtualFramebuffer *vfb) { } void FramebufferManagerCommon::ShowScreenResolution() { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); std::ostringstream messageStream; messageStream << gr->T("Internal Resolution") << ": "; diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 2a2d3ecda0..f54bd4e071 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -101,7 +101,7 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw) // Disable hardware tessellation bacause DX9 is still unsupported. g_Config.bHardwareTessellation = false; ERROR_LOG(G3D, "Hardware Tessellation is unsupported, falling back to software tessellation"); - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); host->NotifyUserMessage(gr->T("Turn off Hardware Tessellation - unsupported"), 2.5f, 0xFF3030FF); } } diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index f923fa82a8..6075cdba51 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -588,7 +588,7 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { vs = new VSShader(device_, VSID, codeBuffer_, useHWTransform); if (vs->Failed()) { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); ERROR_LOG(G3D, "Shader compilation failed, falling back to software transform"); if (!g_Config.bHideSlowWarnings) { host->NotifyUserMessage(gr->T("hardware transform error - falling back to software"), 2.5f, 0xFF3030FF); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 967aaf6709..97b0ecf178 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -112,7 +112,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) if (!gstate_c.SupportsAll(GPU_SUPPORTS_VERTEX_TEXTURE_FETCH | GPU_SUPPORTS_TEXTURE_FLOAT) || !hasTexelFetch) { g_Config.bHardwareTessellation = false; ERROR_LOG(G3D, "Hardware Tessellation is unsupported, falling back to software tessellation"); - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); host->NotifyUserMessage(gr->T("Turn off Hardware Tessellation - unsupported"), 2.5f, 0xFF3030FF); } } diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 2629743f7e..e57c125d10 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -678,7 +678,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID * // Vertex shader not in cache. Let's compile it. vs = CompileVertexShader(*VSID); if (vs->Failed()) { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); ERROR_LOG(G3D, "Shader compilation failed, falling back to software transform"); if (!g_Config.bHideSlowWarnings) { host->NotifyUserMessage(gr->T("hardware transform error - falling back to software"), 2.5f, 0xFF3030FF); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index c326123a5a..50e8ea9b6c 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -219,7 +219,7 @@ void TextureCacheGLES::StartFrame() { lowMemoryMode_ = true; decimationCounter_ = 0; - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); if (standardScaleFactor_ > 1) { host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f); } else { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 1de9ba2c9e..45cd6a0140 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1122,7 +1122,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { // TODO: We should stall the GPU here and wipe things out of memory. // As is, it will almost definitely fail the second time, but next frame it may recover. - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); if (scaleFactor > 1) { host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f); } else { diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index f117d3a762..60675b127e 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -471,7 +471,7 @@ bool MainUI::event(QEvent *e) { } break; } else if (e->type() == browseFolderEvent) { - I18NCategory *mm = GetI18NCategory("MainMenu"); + auto 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()); diff --git a/UI/ComboKeyMappingScreen.cpp b/UI/ComboKeyMappingScreen.cpp index 7860a49710..aa7d4fae63 100644 --- a/UI/ComboKeyMappingScreen.cpp +++ b/UI/ComboKeyMappingScreen.cpp @@ -33,13 +33,13 @@ void Combo_keyScreen::CreateViews() { using namespace UI; - I18NCategory *co = GetI18NCategory("Controls"); + auto co = GetI18NCategory("Controls"); root_ = new LinearLayout(ORIENT_VERTICAL); root_->Add(new ItemHeader(co->T("Combo Key Setting"))); LinearLayout *root__ = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0)); root_->Add(root__); LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(120, FILL_PARENT)); - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); static const int comboKeyImages[5] = { I_1, I_2, I_3, I_4, I_5, @@ -113,7 +113,7 @@ void Combo_keyScreen::CreateViews() { std::map::iterator imageFinder; - I18NCategory *mc = GetI18NCategory("MappableControls"); + auto mc = GetI18NCategory("MappableControls"); for (auto i = keyToggles.begin(); i != keyToggles.end(); ++i) { LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 6575d45cc9..e9649eace1 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -88,7 +88,7 @@ void ControlMapper::Update() { void ControlMapper::Refresh() { bool hasFocus = UI::GetFocusedView() == this; Clear(); - I18NCategory *mc = GetI18NCategory("MappableControls"); + auto mc = GetI18NCategory("MappableControls"); std::map keyImages; keyImages["Circle"] = I_CIRCLE; @@ -186,28 +186,28 @@ void ControlMapper::MappedCallback(KeyDef kdf) { UI::EventReturn ControlMapper::OnReplace(UI::EventParams ¶ms) { actionIndex_ = atoi(params.v->Tag().c_str()); action_ = REPLACEONE; - I18NCategory *km = GetI18NCategory("KeyMapping"); + auto km = GetI18NCategory("KeyMapping"); scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km)); return UI::EVENT_DONE; } UI::EventReturn ControlMapper::OnReplaceAll(UI::EventParams ¶ms) { action_ = REPLACEALL; - I18NCategory *km = GetI18NCategory("KeyMapping"); + auto km = GetI18NCategory("KeyMapping"); scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km)); return UI::EVENT_DONE; } UI::EventReturn ControlMapper::OnAdd(UI::EventParams ¶ms) { action_ = ADD; - I18NCategory *km = GetI18NCategory("KeyMapping"); + auto km = GetI18NCategory("KeyMapping"); scrm_->push(new KeyMappingNewKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km)); return UI::EVENT_DONE; } UI::EventReturn ControlMapper::OnAddMouse(UI::EventParams ¶ms) { action_ = ADD; g_Config.bMapMouse = true; - I18NCategory *km = GetI18NCategory("KeyMapping"); + auto km = GetI18NCategory("KeyMapping"); scrm_->push(new KeyMappingNewMouseKeyDialog(pspKey_, true, std::bind(&ControlMapper::MappedCallback, this, std::placeholders::_1), km)); return UI::EVENT_DONE; } @@ -224,7 +224,7 @@ void ControlMappingScreen::CreateViews() { using namespace UI; mappers_.clear(); - I18NCategory *km = GetI18NCategory("KeyMapping"); + auto km = GetI18NCategory("KeyMapping"); root_ = new LinearLayout(ORIENT_HORIZONTAL); @@ -275,7 +275,7 @@ UI::EventReturn ControlMappingScreen::OnAutoConfigure(UI::EventParams ¶ms) { for (auto s = seenPads.begin(), end = seenPads.end(); s != end; ++s) { items.push_back(*s); } - I18NCategory *km = GetI18NCategory("KeyMapping"); + auto km = GetI18NCategory("KeyMapping"); ListPopupScreen *autoConfList = new ListPopupScreen(km->T("Autoconfigure for device"), items, -1); if (params.v) autoConfList->SetPopupOrigin(params.v); @@ -306,8 +306,8 @@ void ControlMappingScreen::KeyMapped(int pspkey) { // Notification to let us re void KeyMappingNewKeyDialog::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; - I18NCategory *km = GetI18NCategory("KeyMapping"); - I18NCategory *mc = GetI18NCategory("MappableControls"); + auto km = GetI18NCategory("KeyMapping"); + auto mc = GetI18NCategory("MappableControls"); std::string pspButtonName = KeyMap::GetPspButtonName(this->pspBtn_); @@ -334,7 +334,7 @@ bool KeyMappingNewKeyDialog::key(const KeyInput &key) { void KeyMappingNewMouseKeyDialog::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; - I18NCategory *km = GetI18NCategory("KeyMapping"); + auto km = GetI18NCategory("KeyMapping"); parent->Add(new TextView(std::string(km->T("You can press ESC to cancel.")), new LinearLayoutParams(Margins(10, 0)))); } @@ -544,7 +544,7 @@ bool AnalogTestScreen::axis(const AxisInput &axis) { void AnalogTestScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); root_ = new LinearLayout(ORIENT_VERTICAL); @@ -627,8 +627,8 @@ bool TouchTestScreen::touch(const TouchInput &touch) { void TouchTestScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *gr = GetI18NCategory("Graphics"); + auto di = GetI18NCategory("Dialog"); + auto gr = GetI18NCategory("Graphics"); root_ = new LinearLayout(ORIENT_VERTICAL); LinearLayout *theTwo = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f)); root_->Add(theTwo); diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 77f5c90df5..130596ee05 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -18,8 +18,9 @@ #pragma once #include -#include +#include #include +#include #include "i18n/i18n.h" #include "ui/view.h" @@ -51,7 +52,7 @@ private: class KeyMappingNewKeyDialog : public PopupScreen { public: - explicit KeyMappingNewKeyDialog(int btn, bool replace, std::function callback, I18NCategory *i18n) + explicit KeyMappingNewKeyDialog(int btn, bool replace, std::function callback, std::shared_ptr i18n) : PopupScreen(i18n->T("Map Key"), "Cancel", ""), callback_(callback), mapped_(false) { pspBtn_ = btn; } @@ -74,7 +75,7 @@ private: class KeyMappingNewMouseKeyDialog : public PopupScreen { public: - explicit KeyMappingNewMouseKeyDialog(int btn, bool replace, std::function callback, I18NCategory *i18n) + explicit KeyMappingNewMouseKeyDialog(int btn, bool replace, std::function callback, std::shared_ptr i18n) : PopupScreen(i18n->T("Map Mouse"), "", ""), callback_(callback), mapped_(false) { pspBtn_ = btn; } diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 7c6de132f1..095ec55fb6 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -79,8 +79,8 @@ void CwCheatScreen::CreateCodeList() { void CwCheatScreen::CreateViews() { using namespace UI; - I18NCategory *cw = GetI18NCategory("CwCheats"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto cw = GetI18NCategory("CwCheats"); + auto di = GetI18NCategory("Dialog"); CreateCodeList(); g_Config.bReloadCheats = true; root_ = new LinearLayout(ORIENT_HORIZONTAL); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 684a293a43..372671d39d 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -66,8 +66,8 @@ static const char *logLevelList[] = { void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; - I18NCategory *dev = GetI18NCategory("Developer"); - I18NCategory *sy = GetI18NCategory("System"); + auto dev = GetI18NCategory("Developer"); + auto sy = GetI18NCategory("System"); ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); LinearLayout *items = new LinearLayout(ORIENT_VERTICAL); @@ -189,7 +189,7 @@ void LogScreen::update() { void LogScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); LinearLayout *outer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); root_ = outer; @@ -223,8 +223,8 @@ UI::EventReturn LogScreen::OnSubmit(UI::EventParams &e) { void LogConfigScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *dev = GetI18NCategory("Developer"); + auto di = GetI18NCategory("Dialog"); + auto dev = GetI18NCategory("Developer"); root_ = new ScrollView(ORIENT_VERTICAL); @@ -294,7 +294,7 @@ UI::EventReturn LogConfigScreen::OnLogLevelChange(UI::EventParams &e) { } UI::EventReturn LogConfigScreen::OnLogLevel(UI::EventParams &e) { - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); auto logLevelScreen = new LogLevelScreen(dev->T("Log Level")); logLevelScreen->OnChoice.Handle(this, &LogConfigScreen::OnLogLevelChange); @@ -364,8 +364,8 @@ static const JitDisableFlag jitDisableFlags[] = { void JitDebugScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *dev = GetI18NCategory("Developer"); + auto di = GetI18NCategory("Dialog"); + auto dev = GetI18NCategory("Developer"); root_ = new ScrollView(ORIENT_VERTICAL); @@ -417,9 +417,9 @@ void SystemInfoScreen::CreateViews() { using namespace UI; // NOTE: Do not translate this section. It will change a lot and will be impossible to keep up. - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *si = GetI18NCategory("SysInfo"); - I18NCategory *gr = GetI18NCategory("Graphics"); + auto di = GetI18NCategory("Dialog"); + auto si = GetI18NCategory("SysInfo"); + auto gr = GetI18NCategory("Graphics"); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f)); @@ -674,7 +674,7 @@ void SystemInfoScreen::CreateViews() { void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); addrView_ = new TextView(dev->T("Enter address"), ALIGN_HCENTER, false); parent->Add(addrView_); @@ -728,7 +728,7 @@ void AddressPromptScreen::BackspaceDigit() { } void AddressPromptScreen::UpdatePreviewDigits() { - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); if (addr_ != 0) { char temp[32]; @@ -761,8 +761,8 @@ bool AddressPromptScreen::key(const KeyInput &key) { // Three panes: Block chooser, MIPS view, ARM/x86 view void JitCompareScreen::CreateViews() { - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *dev = GetI18NCategory("Developer"); + auto di = GetI18NCategory("Dialog"); + auto dev = GetI18NCategory("Developer"); using namespace UI; @@ -807,7 +807,7 @@ void JitCompareScreen::UpdateDisasm() { using namespace UI; - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface(); @@ -898,7 +898,7 @@ UI::EventReturn JitCompareScreen::OnShowStats(UI::EventParams &e) { UI::EventReturn JitCompareScreen::OnSelectBlock(UI::EventParams &e) { - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); auto addressPrompt = new AddressPromptScreen(dev->T("Block address")); addressPrompt->OnChoice.Handle(this, &JitCompareScreen::OnBlockAddress); @@ -1042,7 +1042,7 @@ struct { DebugShaderType type; const char *name; } shaderTypes[] = { void ShaderListScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL); root_ = layout; @@ -1071,7 +1071,7 @@ UI::EventReturn ShaderListScreen::OnShaderClick(UI::EventParams &e) { void ShaderViewScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL); root_ = layout; diff --git a/UI/DevScreens.h b/UI/DevScreens.h index a6c480bc66..8b98b022c6 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -31,7 +32,7 @@ class DevMenu : public PopupScreen { public: - DevMenu(I18NCategory *i18n) : PopupScreen(i18n->T("Dev Tools")) {} + DevMenu(std::shared_ptr i18n) : PopupScreen(i18n->T("Dev Tools")) {} void CreatePopupContents(UI::ViewGroup *parent) override; void dialogFinished(const Screen *dialog, DialogResult result) override; diff --git a/UI/DiscordIntegration.cpp b/UI/DiscordIntegration.cpp index 5c49340c7e..b872b21e01 100644 --- a/UI/DiscordIntegration.cpp +++ b/UI/DiscordIntegration.cpp @@ -101,7 +101,7 @@ void Discord::SetPresenceGame(const char *gameTitle) { } #ifdef ENABLE_DISCORD - I18NCategory *sc = GetI18NCategory("Screen"); + auto sc = GetI18NCategory("Screen"); DiscordRichPresence discordPresence{}; discordPresence.state = gameTitle; @@ -127,7 +127,7 @@ void Discord::SetPresenceMenu() { } #ifdef ENABLE_DISCORD - I18NCategory *sc = GetI18NCategory("Screen"); + auto sc = GetI18NCategory("Screen"); DiscordRichPresence discordPresence{}; discordPresence.state = sc->T("In menu"); diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index 3f5f14ef13..6a5cedbff5 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -231,9 +231,9 @@ void DisplayLayoutScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *gr = GetI18NCategory("Graphics"); - I18NCategory *co = GetI18NCategory("Controls"); + auto di = GetI18NCategory("Dialog"); + auto gr = GetI18NCategory("Graphics"); + auto co = GetI18NCategory("Controls"); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index d72ca4ee7f..7bbf0863df 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -95,7 +95,7 @@ extern bool g_TakeScreenshot; static void __EmuScreenVblank() { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); if (frameStep_ && lastNumFlips != gpuStats.numFlips) { @@ -192,7 +192,7 @@ void EmuScreen::bootGame(const std::string &filename) { if (!bootAllowStorage(filename)) return; - I18NCategory *sc = GetI18NCategory("Screen"); + auto sc = GetI18NCategory("Screen"); invalid_ = true; @@ -267,17 +267,17 @@ void EmuScreen::bootGame(const std::string &filename) { } if (PSP_CoreParameter().compat.flags().RequireBufferedRendering && g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); host->NotifyUserMessage(gr->T("BufferedRenderingRequired", "Warning: This game requires Rendering Mode to be set to Buffered."), 15.0f); } if (PSP_CoreParameter().compat.flags().RequireBlockTransfer && g_Config.bBlockTransferGPU == false) { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); host->NotifyUserMessage(gr->T("BlockTransferRequired", "Warning: This game requires Simulate Block Transfer Mode to be set to On."), 15.0f); } if (PSP_CoreParameter().compat.flags().RequireDefaultCPUClock && g_Config.iLockedCPUSpeed != 0) { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); host->NotifyUserMessage(gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 15.0f); } @@ -293,7 +293,7 @@ void EmuScreen::bootComplete() { NOTICE_LOG(BOOT, "Loading %s...", PSP_CoreParameter().fileToStart.c_str()); autoLoad(); - I18NCategory *sc = GetI18NCategory("Screen"); + auto sc = GetI18NCategory("Screen"); #ifndef MOBILE_DEVICE if (g_Config.bFirstRun) { @@ -318,7 +318,7 @@ void EmuScreen::bootComplete() { #endif if (Core_GetPowerSaving()) { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); #ifdef __ANDROID__ osm.Show(sy->T("WARNING: Android battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving"); #else @@ -474,7 +474,7 @@ bool EmuScreen::touch(const TouchInput &touch) { } void EmuScreen::onVKeyDown(int virtualKeyCode) { - I18NCategory *sc = GetI18NCategory("Screen"); + auto sc = GetI18NCategory("Screen"); switch (virtualKeyCode) { case VIRTKEY_UNTHROTTLE: @@ -635,7 +635,7 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { } void EmuScreen::onVKeyUp(int virtualKeyCode) { - I18NCategory *sc = GetI18NCategory("Screen"); + auto sc = GetI18NCategory("Screen"); switch (virtualKeyCode) { case VIRTKEY_UNTHROTTLE: @@ -959,8 +959,8 @@ protected: void EmuScreen::CreateViews() { using namespace UI; - I18NCategory *sc = GetI18NCategory("Screen"); - I18NCategory *dev = GetI18NCategory("Developer"); + auto sc = GetI18NCategory("Screen"); + auto dev = GetI18NCategory("Developer"); const Bounds &bounds = screenManager()->getUIContext()->GetBounds(); InitPadLayout(bounds.w, bounds.h); @@ -1030,7 +1030,7 @@ void EmuScreen::CreateViews() { } UI::EventReturn EmuScreen::OnDevTools(UI::EventParams ¶ms) { - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); DevMenu *devMenu = new DevMenu(dev); if (params.v) devMenu->SetPopupOrigin(params.v); @@ -1072,7 +1072,7 @@ void EmuScreen::update() { quit_ = true; return; } - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); std::string errLoadingFile = gamePath_ + "\n"; errLoadingFile.append(err->T("Error loading file", "Could not load game")); errLoadingFile.append(" "); diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index bd9f0aa7ab..f9bda115a2 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -78,8 +78,8 @@ GPUDriverTestScreen::~GPUDriverTestScreen() { void GPUDriverTestScreen::CreateViews() { // Don't bother with views for now. using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *cr = GetI18NCategory("PSPCredits"); + auto di = GetI18NCategory("Dialog"); + auto cr = GetI18NCategory("PSPCredits"); AnchorLayout *anchor = new AnchorLayout(); root_ = anchor; diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 556579e64e..e31f6d7892 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -50,9 +50,9 @@ void GameScreen::CreateViews() { if (info && !info->id.empty()) saveDirs = info->GetSaveDataDirectories(); // Get's very heavy, let's not do it in update() - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ga = GetI18NCategory("Game"); - I18NCategory *pa = GetI18NCategory("Pause"); + auto di = GetI18NCategory("Dialog"); + auto ga = GetI18NCategory("Game"); + auto pa = GetI18NCategory("Pause"); // Information in the top left. // Back button to the bottom left. @@ -176,8 +176,8 @@ void GameScreen::CallbackDeleteConfig(bool yes) { UI::EventReturn GameScreen::OnDeleteConfig(UI::EventParams &e) { - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ga = GetI18NCategory("Game"); + auto di = GetI18NCategory("Dialog"); + auto ga = GetI18NCategory("Game"); screenManager()->push( new PromptScreen(di->T("DeleteConfirmGameConfig", "Do you really want to delete the settings for this game?"), ga->T("ConfirmDelete"), di->T("Cancel"), std::bind(&GameScreen::CallbackDeleteConfig, this, std::placeholders::_1))); @@ -188,7 +188,7 @@ UI::EventReturn GameScreen::OnDeleteConfig(UI::EventParams &e) void GameScreen::render() { UIScreen::render(); - I18NCategory *ga = GetI18NCategory("Game"); + auto ga = GetI18NCategory("Game"); Draw::DrawContext *thin3d = screenManager()->getDrawContext(); @@ -277,8 +277,8 @@ UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) { } UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) { - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ga = GetI18NCategory("Game"); + auto di = GetI18NCategory("Dialog"); + auto ga = GetI18NCategory("Game"); std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); if (info) { // Check that there's any savedata to delete @@ -303,8 +303,8 @@ void GameScreen::CallbackDeleteSaveData(bool yes) { } UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) { - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ga = GetI18NCategory("Game"); + auto di = GetI18NCategory("Dialog"); + auto ga = GetI18NCategory("Game"); std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); if (info) { screenManager()->push( @@ -380,7 +380,7 @@ SetBackgroundPopupScreen::SetBackgroundPopupScreen(const std::string &title, con } void SetBackgroundPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { - I18NCategory *ga = GetI18NCategory("Game"); + auto ga = GetI18NCategory("Game"); parent->Add(new UI::TextView(ga->T("One moment please..."), ALIGN_LEFT | ALIGN_VCENTER, false, new UI::LinearLayoutParams(UI::Margins(10, 0, 10, 10)))); } @@ -415,7 +415,7 @@ void SetBackgroundPopupScreen::update() { } UI::EventReturn GameScreen::OnSetBackground(UI::EventParams &e) { - I18NCategory *ga = GetI18NCategory("Game"); + auto ga = GetI18NCategory("Game"); // This popup is used to prevent any race condition: // g_gameInfoCache may take time to load the data, and a crash could happen if they exit before then. SetBackgroundPopupScreen *pop = new SetBackgroundPopupScreen(ga->T("Setting Background"), gamePath_); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index f1bda2a044..a6fae59fa4 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -110,7 +110,7 @@ bool DoesBackendSupportHWTess() { } static std::string PostShaderTranslateName(const char *value) { - I18NCategory *ps = GetI18NCategory("PostShaders"); + auto ps = GetI18NCategory("PostShaders"); const ShaderInfo *info = GetPostShaderInfo(value); if (info) { return ps->T(value, info ? info->name.c_str() : value); @@ -149,16 +149,16 @@ void GameSettingsScreen::CreateViews() { // Scrolling action menu to the right. using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *gr = GetI18NCategory("Graphics"); - I18NCategory *co = GetI18NCategory("Controls"); - I18NCategory *a = GetI18NCategory("Audio"); - I18NCategory *sa = GetI18NCategory("Savedata"); - I18NCategory *sy = GetI18NCategory("System"); - I18NCategory *n = GetI18NCategory("Networking"); - I18NCategory *ms = GetI18NCategory("MainSettings"); - I18NCategory *dev = GetI18NCategory("Developer"); - I18NCategory *ri = GetI18NCategory("RemoteISO"); + auto di = GetI18NCategory("Dialog"); + auto gr = GetI18NCategory("Graphics"); + auto co = GetI18NCategory("Controls"); + auto a = GetI18NCategory("Audio"); + auto sa = GetI18NCategory("Savedata"); + auto sy = GetI18NCategory("System"); + auto n = GetI18NCategory("Networking"); + auto ms = GetI18NCategory("MainSettings"); + auto dev = GetI18NCategory("Developer"); + auto ri = GetI18NCategory("RemoteISO"); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); @@ -291,7 +291,7 @@ void GameSettingsScreen::CreateViews() { graphicsSettings->Add(new ItemHeader(gr->T("Features"))); // Hide postprocess option on unsupported backends to avoid confusion. if (GetGPUBackend() != GPUBackend::DIRECT3D9) { - I18NCategory *ps = GetI18NCategory("PostShaders"); + auto ps = GetI18NCategory("PostShaders"); postProcChoice_ = graphicsSettings->Add(new ChoiceWithValueDisplay(&g_Config.sPostShaderName, gr->T("Postprocessing Shader"), &PostShaderTranslateName)); postProcChoice_->OnClick.Handle(this, &GameSettingsScreen::OnPostProcShader); postProcEnable_ = !g_Config.bSoftwareRendering && (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE); @@ -871,7 +871,7 @@ void RecreateActivity() { System_SendMessage("recreate", ""); ILOG("Got back from recreate"); } else { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); System_SendMessage("toast", gr->T("Must Restart", "You must restart PPSSPP for this change to take effect")); } } @@ -918,7 +918,7 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { #if PPSSPP_PLATFORM(ANDROID) UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); System_SendMessage("inputbox", (std::string(sy->T("Memory Stick Folder")) + ":" + g_Config.memStickDirectory).c_str()); return UI::EVENT_DONE; } @@ -956,7 +956,7 @@ UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnSavePathOther(UI::EventParams &e) { const std::string PPSSPPpath = File::GetExeDirectory(); if (otherinstalled_) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder")); if (folder.size()) { g_Config.memStickDirectory = folder; @@ -1085,8 +1085,8 @@ void GameSettingsScreen::onFinish(DialogResult result) { void GameSettingsScreen::sendMessage(const char *message, const char *value) { UIDialogScreenWithGameBackground::sendMessage(message, value); - I18NCategory *sy = GetI18NCategory("System"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto sy = GetI18NCategory("System"); + auto di = GetI18NCategory("Dialog"); if (!strcmp(message, "inputbox_completed")) { std::vector inputboxValue; @@ -1121,7 +1121,7 @@ void GameSettingsScreen::sendMessage(const char *message, const char *value) { #if PPSSPP_PLATFORM(ANDROID) void GameSettingsScreen::CallbackMemstickFolder(bool yes) { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); if (yes) { std::string memstickDirFile = g_Config.internalDataDirectory + "/memstick_dir.txt"; @@ -1177,7 +1177,7 @@ void GameSettingsScreen::CallbackRenderingDevice(bool yes) { } UI::EventReturn GameSettingsScreen::OnRenderingBackend(UI::EventParams &e) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); // It only makes sense to show the restart prompt if the backend was actually changed. if (g_Config.iGPUBackend != (int)GetGPUBackend()) { @@ -1188,7 +1188,7 @@ UI::EventReturn GameSettingsScreen::OnRenderingBackend(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); // It only makes sense to show the restart prompt if the device was actually changed. std::string *deviceNameSetting = GPUDeviceNameSetting(); @@ -1205,7 +1205,7 @@ UI::EventReturn GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) { } UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { - I18NCategory *a = GetI18NCategory("Audio"); + auto a = GetI18NCategory("Audio"); if (g_Config.sAudioDevice == a->T("Auto")) { g_Config.sAudioDevice.clear(); } @@ -1231,7 +1231,7 @@ UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); #if defined(__ANDROID__) System_SendMessage("inputbox", ("IP:" + g_Config.proAdhocServer).c_str()); @@ -1254,7 +1254,7 @@ UI::EventReturn GameSettingsScreen::OnComboKey(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnLanguage(UI::EventParams &e) { - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); auto langScreen = new NewLanguageScreen(dev->T("Language")); langScreen->OnChoice.Handle(this, &GameSettingsScreen::OnLanguageChange); if (e.v) @@ -1273,7 +1273,7 @@ UI::EventReturn GameSettingsScreen::OnLanguageChange(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnPostProcShader(UI::EventParams &e) { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); auto procScreen = new PostProcScreen(gr->T("Postprocessing Shader")); procScreen->OnChoice.Handle(this, &GameSettingsScreen::OnPostProcShaderChange); if (e.v) @@ -1337,11 +1337,11 @@ void DeveloperToolsScreen::CreateViews() { settingsScroll->SetTag("DevToolsSettings"); root_->Add(settingsScroll); - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *dev = GetI18NCategory("Developer"); - I18NCategory *gr = GetI18NCategory("Graphics"); - I18NCategory *a = GetI18NCategory("Audio"); - I18NCategory *sy = GetI18NCategory("System"); + auto di = GetI18NCategory("Dialog"); + auto dev = GetI18NCategory("Developer"); + auto gr = GetI18NCategory("Graphics"); + auto a = GetI18NCategory("Audio"); + auto sy = GetI18NCategory("System"); AddStandardBack(root_); @@ -1413,8 +1413,8 @@ void GameSettingsScreen::CallbackRestoreDefaults(bool yes) { } UI::EventReturn GameSettingsScreen::OnRestoreDefaultSettings(UI::EventParams &e) { - I18NCategory *dev = GetI18NCategory("Developer"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto dev = GetI18NCategory("Developer"); + auto di = GetI18NCategory("Dialog"); if (g_Config.bGameSpecific) { screenManager()->push( @@ -1506,9 +1506,9 @@ void DeveloperToolsScreen::update() { void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; - I18NCategory *sy = GetI18NCategory("System"); - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *n = GetI18NCategory("Networking"); + auto sy = GetI18NCategory("System"); + auto di = GetI18NCategory("Dialog"); + auto n = GetI18NCategory("Networking"); LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10))); diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index 1af1e85dc7..a815e01b7a 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -34,8 +34,8 @@ void InstallZipScreen::CreateViews() { FileInfo fileInfo; bool success = getFileInfo(zipPath_.c_str(), &fileInfo); - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *iz = GetI18NCategory("InstallZip"); + auto di = GetI18NCategory("Dialog"); + auto iz = GetI18NCategory("InstallZip"); Margins actionMenuMargins(0, 100, 15, 0); @@ -106,7 +106,7 @@ UI::EventReturn InstallZipScreen::OnInstall(UI::EventParams ¶ms) { } void InstallZipScreen::update() { - I18NCategory *iz = GetI18NCategory("InstallZip"); + auto iz = GetI18NCategory("InstallZip"); using namespace UI; if (g_GameManager.GetState() != GameManagerState::IDLE) { diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 7fccb89a58..535fed6b3b 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -523,7 +523,7 @@ void GameBrowser::Refresh() { Clear(); Add(new Spacer(1.0f)); - I18NCategory *mm = GetI18NCategory("MainMenu"); + auto mm = GetI18NCategory("MainMenu"); // No topbar on recent screen if (DisplayTopBar()) { @@ -777,7 +777,7 @@ void MainScreen::CreateViews() { bool vertical = UseVerticalLayout(); - I18NCategory *mm = GetI18NCategory("MainMenu"); + auto mm = GetI18NCategory("MainMenu"); Margins actionMenuMargins(0, 10, 10, 0); @@ -954,7 +954,7 @@ void MainScreen::CreateViews() { root_->SetDefaultFocusView(tabHolder_); } - I18NCategory *u = GetI18NCategory("Upgrade"); + auto u = GetI18NCategory("Upgrade"); upgradeBar_ = 0; if (!g_Config.upgradeMessage.empty()) { @@ -1255,8 +1255,8 @@ void MainScreen::dialogFinished(const Screen *dialog, DialogResult result) { void UmdReplaceScreen::CreateViews() { using namespace UI; Margins actionMenuMargins(0, 100, 15, 0); - I18NCategory *mm = GetI18NCategory("MainMenu"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto mm = GetI18NCategory("MainMenu"); + auto di = GetI18NCategory("Dialog"); TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0)); leftColumn->SetTag("UmdReplace"); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index e132d44649..106be32e5b 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -183,7 +183,7 @@ void HandleCommonMessages(const char *message, const char *value, ScreenManager UpdateUIState(UISTATE_MENU); manager->push(new GameSettingsScreen("")); } else if (!strcmp(message, "language screen") && isActiveScreen) { - I18NCategory *dev = GetI18NCategory("Developer"); + auto dev = GetI18NCategory("Developer"); auto langScreen = new NewLanguageScreen(dev->T("Language")); langScreen->OnChoice.Add([](UI::EventParams &) { NativeMessageReceived("recreateviews", ""); @@ -247,7 +247,7 @@ void UIDialogScreenWithBackground::DrawBackground(UIContext &dc) { void UIDialogScreenWithBackground::AddStandardBack(UI::ViewGroup *parent) { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); parent->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, 64, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); } @@ -257,7 +257,7 @@ void UIDialogScreenWithBackground::sendMessage(const char *message, const char * PromptScreen::PromptScreen(std::string message, std::string yesButtonText, std::string noButtonText, std::function callback) : message_(message), callback_(callback) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); yesButtonText_ = di->T(yesButtonText.c_str()); noButtonText_ = di->T(noButtonText.c_str()); } @@ -303,7 +303,7 @@ void PromptScreen::TriggerFinish(DialogResult result) { } PostProcScreen::PostProcScreen(const std::string &title) : ListPopupScreen(title) { - I18NCategory *ps = GetI18NCategory("PostShaders"); + auto ps = GetI18NCategory("PostShaders"); ReloadAllPostShaderInfo(); shaders_ = GetAllPostShaderInfo(); std::vector items; @@ -487,8 +487,8 @@ void LogoScreen::render() { ::DrawBackground(dc, alpha); - I18NCategory *cr = GetI18NCategory("PSPCredits"); - I18NCategory *gr = GetI18NCategory("Graphics"); + auto cr = GetI18NCategory("PSPCredits"); + auto gr = GetI18NCategory("Graphics"); char temp[256]; // Manually formatting UTF-8 is fun. \xXX doesn't work everywhere. snprintf(temp, sizeof(temp), "%s Henrik Rydg%c%crd", cr->T("created", "Created by"), 0xC3, 0xA5); @@ -518,8 +518,8 @@ void LogoScreen::render() { void CreditsScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *cr = GetI18NCategory("PSPCredits"); + auto di = GetI18NCategory("Dialog"); + auto cr = GetI18NCategory("PSPCredits"); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); Button *back = root_->Add(new Button(di->T("Back"), new AnchorLayoutParams(260, 64, NONE, NONE, 10, 10, false))); @@ -589,7 +589,7 @@ UI::EventReturn CreditsScreen::OnDiscord(UI::EventParams &e) { } UI::EventReturn CreditsScreen::OnShare(UI::EventParams &e) { - I18NCategory *cr = GetI18NCategory("PSPCredits"); + auto cr = GetI18NCategory("PSPCredits"); System_SendMessage("sharetext", cr->T("CheckOutPPSSPP", "Check out PPSSPP, the awesome PSP emulator: http://www.ppsspp.org/")); return UI::EVENT_DONE; } @@ -608,7 +608,7 @@ void CreditsScreen::update() { void CreditsScreen::render() { UIScreen::render(); - I18NCategory *cr = GetI18NCategory("PSPCredits"); + auto cr = GetI18NCategory("PSPCredits"); std::string specialthanksMaxim = "Maxim "; specialthanksMaxim += cr->T("specialthanksMaxim", "for his amazing Atrac3+ decoder work"); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 9447474edb..10407bf07b 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -661,7 +661,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } - I18NCategory *des = GetI18NCategory("DesktopUI"); + auto des = GetI18NCategory("DesktopUI"); // Note to translators: do not translate this/add this to PPSSPP-lang's files. // It's intended to be custom for every user. // Only add it to your own personal copies of PPSSPP. @@ -956,7 +956,7 @@ void TakeScreenshot() { if (success) { osm.Show(filename); } else { - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); osm.Show(err->T("Could not save screenshot file")); } } @@ -1105,7 +1105,7 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { UIBackgroundInit(*uiContext); } if (msg == "savestate_displayslot") { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); std::string msg = StringFromFormat("%s: %d", sy->T("Savestate Slot"), SaveState::GetCurrentSlot() + 1); // Show for the same duration as the preview. osm.Show(msg, 2.0f, 0xFFFFFF, -1, true, "savestate_slot"); @@ -1119,7 +1119,7 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { } if (msg == "core_powerSaving") { if (value != "false") { - I18NCategory *sy = GetI18NCategory("System"); + auto sy = GetI18NCategory("System"); #ifdef __ANDROID__ osm.Show(sy->T("WARNING: Android battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving"); #else diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 2d17643cdd..59769a01c7 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "i18n/i18n.h" #include "gfx_es2/draw_buffer.h" #include "ui/view.h" @@ -155,7 +156,7 @@ void AsyncImageFileView::Draw(UIContext &dc) { class ScreenshotViewScreen : public PopupScreen { public: - ScreenshotViewScreen(std::string filename, std::string title, int slot, I18NCategory *i18n) + ScreenshotViewScreen(std::string filename, std::string title, int slot, std::shared_ptr i18n) : PopupScreen(title, i18n->T("Load State"), "Back"), filename_(filename), slot_(slot) {} // PopupScreen will translate Back on its own int GetSlot() const { @@ -232,7 +233,7 @@ SaveSlotView::SaveSlotView(const std::string &gameFilename, int slot, UI::Layout AsyncImageFileView *fv = Add(new AsyncImageFileView(screenshotFilename_, IS_DEFAULT, wq, new UI::LayoutParams(82 * 2, 47 * 2))); fv->SetOverlayText(StringFromFormat("%d", slot_ + 1)); - I18NCategory *pa = GetI18NCategory("Pause"); + auto pa = GetI18NCategory("Pause"); LinearLayout *buttons = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); buttons->SetSpacing(2.0); @@ -321,8 +322,8 @@ void GamePauseScreen::CreateViews() { using namespace UI; Margins scrollMargins(0, 20, 0, 0); Margins actionMenuMargins(0, 20, 15, 0); - I18NCategory *gr = GetI18NCategory("Graphics"); - I18NCategory *pa = GetI18NCategory("Pause"); + auto gr = GetI18NCategory("Graphics"); + auto pa = GetI18NCategory("Pause"); root_ = new LinearLayout(ORIENT_HORIZONTAL); @@ -377,12 +378,12 @@ void GamePauseScreen::CreateViews() { // TODO, also might be nice to show overall compat rating here? // Based on their platform or even cpu/gpu/config. Would add an API for it. if (Reporting::IsSupported() && g_paramSFO.GetValueString("DISC_ID").size()) { - I18NCategory *rp = GetI18NCategory("Reporting"); + auto rp = GetI18NCategory("Reporting"); rightColumnItems->Add(new Choice(rp->T("ReportButton", "Report Feedback")))->OnClick.Handle(this, &GamePauseScreen::OnReportFeedback); } rightColumnItems->Add(new Spacer(25.0)); if (g_Config.bPauseMenuExitsEmulator) { - I18NCategory *mm = GetI18NCategory("MainMenu"); + auto mm = GetI18NCategory("MainMenu"); rightColumnItems->Add(new Choice(mm->T("Exit")))->OnClick.Handle(this, &GamePauseScreen::OnExitToMenu); } else { rightColumnItems->Add(new Choice(pa->T("Exit to menu")))->OnClick.Handle(this, &GamePauseScreen::OnExitToMenu); @@ -421,7 +422,7 @@ UI::EventReturn GamePauseScreen::OnScreenshotClicked(UI::EventParams &e) { if (SaveState::HasSaveInSlot(gamePath_, slot)) { std::string fn = v->GetScreenshotFilename(); std::string title = v->GetScreenshotTitle(); - I18NCategory *pa = GetI18NCategory("Pause"); + auto pa = GetI18NCategory("Pause"); Screen *screen = new ScreenshotViewScreen(fn, title, v->GetSlot(), pa); screenManager()->push(screen); } @@ -487,8 +488,8 @@ UI::EventReturn GamePauseScreen::OnCreateConfig(UI::EventParams &e) UI::EventReturn GamePauseScreen::OnDeleteConfig(UI::EventParams &e) { - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ga = GetI18NCategory("Game"); + auto di = GetI18NCategory("Dialog"); + auto ga = GetI18NCategory("Game"); screenManager()->push( new PromptScreen(di->T("DeleteConfirmGameConfig", "Do you really want to delete the settings for this game?"), ga->T("ConfirmDelete"), di->T("Cancel"), std::bind(&GamePauseScreen::CallbackDeleteConfig, this, std::placeholders::_1))); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 2b34cfc440..1bc941570e 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -181,8 +181,8 @@ void RemoteISOScreen::update() { } void RemoteISOScreen::CreateViews() { - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ri = GetI18NCategory("RemoteISO"); + auto di = GetI18NCategory("Dialog"); + auto ri = GetI18NCategory("RemoteISO"); Margins actionMenuMargins(0, 20, 15, 0); Margins contentMargins(0, 20, 5, 5); @@ -276,8 +276,8 @@ RemoteISOConnectScreen::~RemoteISOConnectScreen() { } void RemoteISOConnectScreen::CreateViews() { - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ri = GetI18NCategory("RemoteISO"); + auto di = GetI18NCategory("Dialog"); + auto ri = GetI18NCategory("RemoteISO"); Margins actionMenuMargins(0, 20, 15, 0); Margins contentMargins(0, 20, 5, 5); @@ -300,7 +300,7 @@ void RemoteISOConnectScreen::CreateViews() { } void RemoteISOConnectScreen::update() { - I18NCategory *ri = GetI18NCategory("RemoteISO"); + auto ri = GetI18NCategory("RemoteISO"); UIScreenWithBackground::update(); @@ -412,8 +412,8 @@ RemoteISOBrowseScreen::RemoteISOBrowseScreen(const std::string &url, const std:: void RemoteISOBrowseScreen::CreateViews() { bool vertical = UseVerticalLayout(); - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *ri = GetI18NCategory("RemoteISO"); + auto di = GetI18NCategory("Dialog"); + auto ri = GetI18NCategory("RemoteISO"); Margins actionMenuMargins(0, 10, 10, 0); @@ -478,7 +478,7 @@ void RemoteISOSettingsScreen::update() { } void RemoteISOSettingsScreen::CreateViews() { - I18NCategory *ri = GetI18NCategory("RemoteISO"); + auto ri = GetI18NCategory("RemoteISO"); ViewGroup *remoteisoSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); remoteisoSettingsScroll->SetTag("RemoteISOSettings"); diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index 43c50959ed..577fb40e57 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -66,7 +66,7 @@ RatingChoice::RatingChoice(const char *captionKey, int *value, LayoutParams *lay : LinearLayout(ORIENT_VERTICAL, layoutParams), value_(value) { SetSpacing(0.0f); - I18NCategory *rp = GetI18NCategory("Reporting"); + auto rp = GetI18NCategory("Reporting"); group_ = new LinearLayout(ORIENT_HORIZONTAL); Add(new TextView(rp->T(captionKey), FLAG_WRAP_TEXT, false))->SetShadow(true); Add(group_); @@ -98,7 +98,7 @@ RatingChoice *RatingChoice::SetEnabledPtr(bool *ptr) { } void RatingChoice::SetupChoices() { - I18NCategory *rp = GetI18NCategory("Reporting"); + auto rp = GetI18NCategory("Reporting"); AddChoice(0, rp->T("Bad")); AddChoice(1, rp->T("OK")); AddChoice(2, rp->T("Great")); @@ -146,7 +146,7 @@ CompatRatingChoice::CompatRatingChoice(const char *captionKey, int *value, Layou } void CompatRatingChoice::SetupChoices() { - I18NCategory *rp = GetI18NCategory("Reporting"); + auto rp = GetI18NCategory("Reporting"); group_->Clear(); AddChoice(0, rp->T("Perfect")); AddChoice(1, rp->T("Plays")); @@ -219,9 +219,9 @@ EventReturn ReportScreen::HandleReportingChange(EventParams &e) { } void ReportScreen::CreateViews() { - I18NCategory *rp = GetI18NCategory("Reporting"); - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *sy = GetI18NCategory("System"); + auto rp = GetI18NCategory("Reporting"); + auto di = GetI18NCategory("Dialog"); + auto sy = GetI18NCategory("System"); Margins actionMenuMargins(0, 20, 15, 0); Margins contentMargins(0, 20, 5, 5); @@ -301,7 +301,7 @@ void ReportScreen::UpdateSubmit() { } void ReportScreen::UpdateOverallDescription() { - I18NCategory *rp = GetI18NCategory("Reporting"); + auto rp = GetI18NCategory("Reporting"); const char *desc; uint32_t c = 0xFFFFFFFF; switch (overall_) { @@ -351,8 +351,8 @@ ReportFinishScreen::ReportFinishScreen(const std::string &gamePath, ReportingOve } void ReportFinishScreen::CreateViews() { - I18NCategory *rp = GetI18NCategory("Reporting"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto rp = GetI18NCategory("Reporting"); + auto di = GetI18NCategory("Dialog"); Margins actionMenuMargins(0, 20, 15, 0); Margins contentMargins(0, 20, 5, 5); @@ -386,7 +386,7 @@ void ReportFinishScreen::CreateViews() { } void ReportFinishScreen::update() { - I18NCategory *rp = GetI18NCategory("Reporting"); + auto rp = GetI18NCategory("Reporting"); if (!setStatus_) { Reporting::ReportStatus status = Reporting::GetStatus(); @@ -412,7 +412,7 @@ void ReportFinishScreen::update() { } void ReportFinishScreen::ShowSuggestions() { - I18NCategory *rp = GetI18NCategory("Reporting"); + auto rp = GetI18NCategory("Reporting"); auto suggestions = Reporting::CompatibilitySuggestions(); if (score_ == ReportingOverallScore::PERFECT || score_ == ReportingOverallScore::PLAYABLE) { diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 05e17353de..a89fc0d90e 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -80,7 +80,7 @@ public: LinearLayout *toprow = new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); content->Add(toprow); - I18NCategory *sa = GetI18NCategory("Savedata"); + auto sa = GetI18NCategory("Savedata"); if (ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { std::string savedata_detail = ginfo->paramSFO.GetValueString("SAVEDATA_DETAIL"); std::string savedata_title = ginfo->paramSFO.GetValueString("SAVEDATA_TITLE"); @@ -108,7 +108,7 @@ public: content->Add(new TextView(GetFileDateAsString(savePath_), 0, true, new LinearLayoutParams(Margins(10, 5))))->SetTextColor(textStyle.fgColor); } - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); LinearLayout *buttons = new LinearLayout(ORIENT_HORIZONTAL); buttons->Add(new Button(di->T("Back"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &UIScreen::OnBack); buttons->Add(new Button(di->T("Delete"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &SavedataPopupScreen::OnDeleteButtonClick); @@ -394,8 +394,8 @@ void SavedataBrowser::Refresh() { Clear(); Add(new Spacer(1.0f)); - I18NCategory *mm = GetI18NCategory("MainMenu"); - I18NCategory *sa = GetI18NCategory("Savedata"); + auto mm = GetI18NCategory("MainMenu"); + auto sa = GetI18NCategory("Savedata"); SortedLinearLayout *gl = new SortedLinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); gl->SetSpacing(4.0f); @@ -458,8 +458,8 @@ SavedataScreen::~SavedataScreen() { void SavedataScreen::CreateViews() { using namespace UI; - I18NCategory *sa = GetI18NCategory("Savedata"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto sa = GetI18NCategory("Savedata"); + auto di = GetI18NCategory("Dialog"); std::string savedata_dir = GetSysDirectory(DIRECTORY_SAVEDATA); std::string savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE); diff --git a/UI/Store.cpp b/UI/Store.cpp index 254843afa3..23f02fd42f 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -240,8 +240,8 @@ void ProductView::CreateViews() { Add(new TextView(entry_.name)); Add(new TextView(entry_.author)); - I18NCategory *st = GetI18NCategory("Store"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto st = GetI18NCategory("Store"); + auto di = GetI18NCategory("Dialog"); wasInstalled_ = IsGameInstalled(); if (!wasInstalled_) { installButton_ = Add(new Button(st->T("Install"))); @@ -431,8 +431,8 @@ void StoreScreen::CreateViews() { root_ = new LinearLayout(ORIENT_VERTICAL); - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *st = GetI18NCategory("Store"); + auto di = GetI18NCategory("Dialog"); + auto st = GetI18NCategory("Store"); // Top bar LinearLayout *topBar = root_->Add(new LinearLayout(ORIENT_HORIZONTAL)); diff --git a/UI/TiltAnalogSettingsScreen.cpp b/UI/TiltAnalogSettingsScreen.cpp index 2e7d4add46..fd427dbf29 100644 --- a/UI/TiltAnalogSettingsScreen.cpp +++ b/UI/TiltAnalogSettingsScreen.cpp @@ -23,8 +23,8 @@ void TiltAnalogSettingsScreen::CreateViews() { using namespace UI; - I18NCategory *co = GetI18NCategory("Controls"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto co = GetI18NCategory("Controls"); + auto di = GetI18NCategory("Dialog"); root_ = new ScrollView(ORIENT_VERTICAL); root_->SetTag("TiltAnalogSettings"); diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 1e54323dcf..0e8f12618a 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -356,8 +356,8 @@ void TouchControlLayoutScreen::CreateViews() { using namespace UI; - I18NCategory *co = GetI18NCategory("Controls"); - I18NCategory *di = GetI18NCategory("Dialog"); + auto co = GetI18NCategory("Controls"); + auto di = GetI18NCategory("Dialog"); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); @@ -395,7 +395,7 @@ void TouchControlLayoutScreen::CreateViews() { // serves no other purpose. AnchorLayout *controlsHolder = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); - I18NCategory *ms = GetI18NCategory("MainSettings"); + auto ms = GetI18NCategory("MainSettings"); //tabHolder->AddTab(ms->T("Controls"), controlsHolder); diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index b7320816c9..1ba3972128 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -42,8 +42,8 @@ private: void TouchControlVisibilityScreen::CreateViews() { using namespace UI; - I18NCategory *di = GetI18NCategory("Dialog"); - I18NCategory *co = GetI18NCategory("Controls"); + auto di = GetI18NCategory("Dialog"); + auto co = GetI18NCategory("Controls"); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); @@ -91,7 +91,7 @@ void TouchControlVisibilityScreen::CreateViews() { toggles_.push_back({ "Alt speed 2", &g_Config.touchSpeed2Key.show, -1 }); toggles_.push_back({ "Rapid Fire", &g_Config.touchRapidFireKey.show, -1 }); - I18NCategory *mc = GetI18NCategory("MappableControls"); + auto mc = GetI18NCategory("MappableControls"); for (auto toggle : toggles_) { LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 3e310bf7cb..2d5c6d06f4 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -135,7 +135,7 @@ void MainThreadFunc() { if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { Reporting::ReportMessage("Graphics init error: %s", "ALL"); - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); const char *defaultErrorAll = "Failed initializing any graphics. Try upgrading your graphics drivers."; const char *genericError = err->T("GenericAllGraphicsError", defaultErrorAll); std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); @@ -163,7 +163,7 @@ void MainThreadFunc() { W32Util::ExitAndRestart(); } - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); Reporting::ReportMessage("Graphics init error: %s", error_string.c_str()); const char *defaultErrorVulkan = "Failed initializing graphics. Try upgrading your graphics drivers.\n\nWould you like to try switching to OpenGL?\n\nError message:"; diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index fef1438fb6..c8911e5144 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -142,7 +142,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { if (FAILED(hr)) { const char *defaultError = "Your GPU does not appear to support Direct3D 11.\n\nWould you like to try again using Direct3D 9 instead?"; - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); std::wstring error; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index 97540ec629..0c8c3bd685 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -236,7 +236,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { // GL_VERSION GL_VENDOR GL_RENDERER // "1.4.0 - Build 8.14.10.2364" "intel" intel Pineview Platform - I18NCategory *err = GetI18NCategory("Error"); + auto err = GetI18NCategory("Error"); std::string glVersion = (const char *)glGetString(GL_VERSION); std::string glRenderer = (const char *)glGetString(GL_RENDERER); diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index a53ecdaad5..225e3ce681 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -125,7 +125,7 @@ namespace MainWindow { } void CreateHelpMenu(HMENU menu) { - I18NCategory *des = GetI18NCategory("DesktopUI"); + auto des = GetI18NCategory("DesktopUI"); const std::wstring visitMainWebsite = ConvertUTF8ToWString(des->T("www.ppsspp.org")); const std::wstring visitForum = ConvertUTF8ToWString(des->T("PPSSPP Forums")); @@ -171,7 +171,7 @@ namespace MainWindow { return false; } - I18NCategory *ps = GetI18NCategory("PostShaders"); + auto ps = GetI18NCategory("PostShaders"); HMENU shaderMenu = GetSubmenuById(menu, ID_OPTIONS_SHADER_MENU); EmptySubMenu(shaderMenu); @@ -202,7 +202,7 @@ namespace MainWindow { } static void TranslateMenuItem(const HMENU hMenu, const int menuID, const std::wstring& accelerator = L"", const char *key = nullptr) { - I18NCategory *des = GetI18NCategory("DesktopUI"); + auto des = GetI18NCategory("DesktopUI"); std::wstring translated; if (key == nullptr || !strcmp(key, "")) { @@ -474,7 +474,7 @@ namespace MainWindow { } static void setRenderingMode(int mode) { - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); g_Config.iRenderingMode = mode; switch (g_Config.iRenderingMode) { @@ -499,7 +499,7 @@ namespace MainWindow { g_Config.iFrameSkip = FRAMESKIP_OFF; } - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); std::ostringstream messageStream; messageStream << gr->T("Frame Skipping") << ":" << " "; @@ -519,7 +519,7 @@ namespace MainWindow { g_Config.iFrameSkipType = 0; } - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); std::ostringstream messageStream; messageStream << gr->T("Frame Skipping Type") << ":" << " "; @@ -553,7 +553,7 @@ namespace MainWindow { void MainWindowMenu_Process(HWND hWnd, WPARAM wParam) { std::string fn; - I18NCategory *gr = GetI18NCategory("Graphics"); + auto gr = GetI18NCategory("Graphics"); int wmId = LOWORD(wParam); int wmEvent = HIWORD(wParam); diff --git a/Windows/main.cpp b/Windows/main.cpp index facf559935..7acdd73713 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -297,7 +297,7 @@ 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"); + auto mm = GetI18NCategory("MainMenu"); std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), mm->T("Choose folder")); if (folder.size()) NativeMessageReceived("browse_folderSelect", folder.c_str()); diff --git a/ext/native/i18n/i18n.cpp b/ext/native/i18n/i18n.cpp index 974610efd4..958f162cae 100644 --- a/ext/native/i18n/i18n.cpp +++ b/ext/native/i18n/i18n.cpp @@ -15,7 +15,7 @@ std::string I18NRepo::LanguageID() { void I18NRepo::Clear() { for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) { - delete iter->second; + iter->second.reset(); } cats_.clear(); } @@ -52,14 +52,14 @@ void I18NCategory::SetMap(const std::map &m) { } } -I18NCategory *I18NRepo::GetCategory(const char *category) { +std::shared_ptr I18NRepo::GetCategory(const char *category) { auto iter = cats_.find(category); if (iter != cats_.end()) { return iter->second; } else { I18NCategory *c = new I18NCategory(this, category); - cats_[category] = c; - return c; + cats_[category].reset(c); + return cats_[category]; } } @@ -96,7 +96,7 @@ bool I18NRepo::LoadIni(const std::string &languageID, const std::string &overrid for (auto iter = sections.begin(); iter != sections.end(); ++iter) { if (iter->name() != "") { - cats_[iter->name()] = LoadSection(&(*iter), iter->name().c_str()); + cats_[iter->name()].reset(LoadSection(&(*iter), iter->name().c_str())); } } @@ -124,7 +124,7 @@ void I18NRepo::SaveIni(const std::string &languageID) { ini.Save(GetIniPath(languageID)); } -void I18NRepo::SaveSection(IniFile &ini, IniFile::Section *section, I18NCategory *cat) { +void I18NRepo::SaveSection(IniFile &ini, IniFile::Section *section, std::shared_ptr cat) { const std::map &missed = cat->Missed(); for (auto iter = missed.begin(); iter != missed.end(); ++iter) { diff --git a/ext/native/i18n/i18n.h b/ext/native/i18n/i18n.h index ede7f1e8a6..5a527d6646 100644 --- a/ext/native/i18n/i18n.h +++ b/ext/native/i18n/i18n.h @@ -9,6 +9,7 @@ // As usual, everything is UTF-8. Nothing else allowed. #include +#include #include #include @@ -76,7 +77,7 @@ public: std::string LanguageID(); - I18NCategory *GetCategory(const char *categoryName); + std::shared_ptr GetCategory(const char *categoryName); bool HasCategory(const char *categoryName) const { return cats_.find(categoryName) != cats_.end(); } @@ -86,9 +87,9 @@ private: std::string GetIniPath(const std::string &languageID) const; void Clear(); I18NCategory *LoadSection(const IniFile::Section *section, const char *name); - void SaveSection(IniFile &ini, IniFile::Section *section, I18NCategory *cat); + void SaveSection(IniFile &ini, IniFile::Section *section, std::shared_ptr cat); - std::map cats_; + std::map> cats_; std::string languageID_; DISALLOW_COPY_AND_ASSIGN(I18NRepo); @@ -98,7 +99,7 @@ extern I18NRepo i18nrepo; // These are simply talking to the one global instance of I18NRepo. -inline I18NCategory *GetI18NCategory(const char *categoryName) { +inline std::shared_ptr GetI18NCategory(const char *categoryName) { if (!categoryName) return nullptr; return i18nrepo.GetCategory(categoryName); diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index b2662ba385..9ed9aa2b38 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -233,7 +233,7 @@ UI::EventReturn UIScreen::OnCancel(UI::EventParams &e) { PopupScreen::PopupScreen(std::string title, std::string button1, std::string button2) : box_(0), defaultButton_(nullptr), title_(title) { - I18NCategory *di = GetI18NCategory("Dialog"); + auto di = GetI18NCategory("Dialog"); if (!button1.empty()) button1_ = di->T(button1.c_str()); if (!button2.empty()) @@ -427,7 +427,7 @@ std::string ChopTitle(const std::string &title) { UI::EventReturn PopupMultiChoice::HandleClick(UI::EventParams &e) { restoreFocus_ = HasFocus(); - I18NCategory *category = category_ ? GetI18NCategory(category_) : nullptr; + auto category = category_ ? GetI18NCategory(category_) : nullptr; std::vector choices; for (int i = 0; i < numChoices_; i++) { @@ -450,7 +450,7 @@ void PopupMultiChoice::Update() { void PopupMultiChoice::UpdateText() { if (!choices_) return; - I18NCategory *category = GetI18NCategory(category_); + auto category = GetI18NCategory(category_); // Clamp the value to be safe. if (*value_ < minVal_ || *value_ > minVal_ + numChoices_ - 1) { valueText_ = "(invalid choice)"; // Shouldn't happen. Should be no need to translate this. @@ -865,7 +865,7 @@ void ChoiceWithValueDisplay::Draw(UIContext &dc) { int paddingX = 12; dc.SetFontStyle(dc.theme->uiFont); - I18NCategory *category = GetI18NCategory(category_); + auto category = GetI18NCategory(category_); std::ostringstream valueText; if (translateCallback_ && sValue_) { valueText << translateCallback_(sValue_->c_str()); From 58b04ef77baeb5ce47aef25a69c32d7b45e4925f Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Sat, 25 Jan 2020 10:06:00 +0200 Subject: [PATCH 62/71] QT Api for Camera (Linux/macOS) Allow the use of different resolutions (win/android/iOS) --- CMakeLists.txt | 9 +- Core/HLE/sceUsbCam.cpp | 54 +- Core/HLE/sceUsbCam.h | 854 ------------------ Core/HW/Camera.cpp | 236 +++-- Core/HW/Camera.h | 50 +- Qt/QtMain.cpp | 23 + Qt/QtMain.h | 15 + Windows/CaptureDevice.cpp | 14 +- Windows/CaptureDevice.h | 2 +- android/jni/Android.mk | 1 + .../src/org/ppsspp/ppsspp/CameraHelper.java | 19 +- .../src/org/ppsspp/ppsspp/NativeActivity.java | 11 +- ios/CameraHelper.h | 2 +- ios/CameraHelper.mm | 26 +- ios/ViewController.h | 1 + ios/ViewController.mm | 6 +- ios/main.mm | 5 +- 17 files changed, 354 insertions(+), 974 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a979a0499..eb89454173 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -780,10 +780,7 @@ elseif(IOS) set(TargetBin PPSSPP) elseif(USING_QT_UI) set(CMAKE_AUTOMOC ON) - find_package(Qt5 COMPONENTS OpenGL Gui Core) - if(NOT SDL2_FOUND) - find_package(Qt5 COMPONENTS Multimedia) - endif(NOT SDL2_FOUND) + find_package(Qt5 COMPONENTS OpenGL Gui Core Multimedia) list(APPEND NativeAppSource Qt/QtMain.cpp Qt/QtMain.h @@ -797,7 +794,7 @@ elseif(USING_QT_UI) add_definitions(-DQT_OPENGL_ES -DQT_OPENGL_ES_2) endif() include_directories(${CMAKE_CURRENT_BINARY_DIR} Qt) - set(nativeExtraLibs ${nativeExtraLibs} Qt5::OpenGL Qt5::Gui Qt5::Core) + set(nativeExtraLibs ${nativeExtraLibs} Qt5::OpenGL Qt5::Gui Qt5::Core Qt5::Multimedia) set(TargetBin PPSSPPQt) # Enable SDL if found @@ -808,8 +805,6 @@ elseif(USING_QT_UI) SDL/SDLJoystick.cpp ) set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2) - else(SDL2_FOUND) - set(nativeExtraLibs ${nativeExtraLibs} Qt5::Multimedia) endif() elseif(WIN32) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 3c4596e6cf..c2ab2979fa 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -59,6 +59,7 @@ void __UsbCamDoState(PointerWrap &p) { p.Do(*config); if (config->mode == Camera::Mode::Video) { // stillImage? TBD + Camera::stopCapture(); Camera::startCapture(); } } @@ -153,9 +154,21 @@ static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) { static int sceUsbCamStartVideo() { std::lock_guard lock(videoBufferMutex); - videoBufferLength = sizeof(sceUsbCamDummyImage); + + int width, height; + getCameraResolution(config->type, &width, &height); + + unsigned char* jpegData = nullptr; + int jpegLen = 0; + __cameraDummyImage(width, height, &jpegData, &jpegLen); + videoBufferLength = jpegLen; memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); - memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage)); + if (jpegData) { + memcpy(videoBuffer, jpegData, jpegLen); + free(jpegData); + jpegData = nullptr; + } + Camera::startCapture(); return 0; } @@ -287,14 +300,16 @@ void Register_sceUsbCam() } std::vector Camera::getDeviceList() { - #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) - return __cameraGetDeviceList(); - #elif PPSSPP_PLATFORM(LINUX) - return __v4l_getDeviceList(); - #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { return winCamera->getDeviceList(); } + #elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) + return __cameraGetDeviceList(); + #elif defined(USING_QT_UI) // Qt:macOS / Qt:Linux + return __qt_getDeviceList(); + #elif PPSSPP_PLATFORM(LINUX) // SDL:Linux + return __v4l_getDeviceList(); #endif return std::vector(); } @@ -305,19 +320,22 @@ int Camera::startCapture() { INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height); config->mode = Camera::Mode::Video; - #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) - System_SendMessage("camera_command", "startVideo"); - #elif PPSSPP_PLATFORM(LINUX) - __v4l_startCapture(width, height); - #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { if (winCamera->isShutDown()) { delete winCamera; winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); } - winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::START, nullptr }); + void* resolution = static_cast(new std::vector({ width, height })); + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::START, resolution }); } + #elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI) + char command[40] = {0}; + snprintf(command, sizeof(command), "startVideo_%dx%d", width, height); + System_SendMessage("camera_command", command); + #elif PPSSPP_PLATFORM(LINUX) + __v4l_startCapture(width, height); #else ERROR_LOG(HLE, "%s not implemented", __FUNCTION__); #endif @@ -326,14 +344,14 @@ int Camera::startCapture() { int Camera::stopCapture() { INFO_LOG(HLE, "%s", __FUNCTION__); - #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) - System_SendMessage("camera_command", "stopVideo"); - #elif PPSSPP_PLATFORM(LINUX) - __v4l_stopCapture(); - #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); } + #elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI) + System_SendMessage("camera_command", "stopVideo"); + #elif PPSSPP_PLATFORM(LINUX) + __v4l_stopCapture(); #else ERROR_LOG(HLE, "%s not implemented", __FUNCTION__); #endif diff --git a/Core/HLE/sceUsbCam.h b/Core/HLE/sceUsbCam.h index ad1ebde215..4d4e4f0831 100644 --- a/Core/HLE/sceUsbCam.h +++ b/Core/HLE/sceUsbCam.h @@ -122,857 +122,3 @@ namespace Camera { int stopCapture(); void pushCameraImage(long long length, unsigned char *image); } - -static const unsigned char sceUsbCamDummyImage[] = { - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, - 0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x68, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, - 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x3E, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, - 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x70, 0x61, 0x69, 0x6E, - 0x74, 0x2E, 0x6E, 0x65, 0x74, 0x20, 0x34, 0x2E, 0x30, 0x2E, 0x31, 0x37, 0x00, 0x00, 0xFF, 0xDB, - 0x00, 0x43, 0x00, 0x04, 0x02, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, - 0x04, 0x05, 0x09, 0x06, 0x05, 0x05, 0x05, 0x05, 0x0B, 0x08, 0x08, 0x06, 0x09, 0x0D, 0x0B, 0x0D, - 0x0D, 0x0D, 0x0B, 0x0C, 0x0C, 0x0E, 0x10, 0x14, 0x11, 0x0E, 0x0F, 0x13, 0x0F, 0x0C, 0x0C, 0x12, - 0x18, 0x12, 0x13, 0x15, 0x16, 0x17, 0x17, 0x17, 0x0E, 0x11, 0x19, 0x1B, 0x19, 0x16, 0x1A, 0x14, - 0x16, 0x17, 0x16, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x0A, 0x06, - 0x06, 0x0A, 0x16, 0x0F, 0x0C, 0x0F, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x01, 0x10, 0x01, - 0xE0, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, - 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, - 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, - 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, - 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, - 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, - 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, - 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, - 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, - 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, - 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, - 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0xF4, 0xEA, 0x28, 0xA2, 0xBF, 0x62, 0x3F, - 0x6C, 0x0A, 0x2A, 0x1B, 0x89, 0xC4, 0x67, 0x68, 0x19, 0x35, 0x1F, 0xDA, 0x9B, 0xFB, 0x83, 0xF3, - 0xAF, 0x96, 0xC7, 0xF1, 0x9E, 0x49, 0x81, 0xC4, 0x4B, 0x0F, 0x5A, 0xB7, 0xBF, 0x1D, 0xEC, 0x9B, - 0xB7, 0x95, 0xD2, 0xB1, 0xF9, 0xBE, 0x77, 0xE2, 0xCF, 0x08, 0x64, 0xB8, 0xE9, 0xE0, 0x71, 0x78, - 0xAF, 0xDE, 0xC3, 0x49, 0x28, 0xC6, 0x52, 0xB3, 0xEC, 0xDA, 0x4D, 0x5F, 0xBA, 0xBE, 0x9D, 0x4B, - 0x54, 0x55, 0x5F, 0xB5, 0x37, 0xF7, 0x07, 0xE7, 0x40, 0xBB, 0x3D, 0xD3, 0xF5, 0xAE, 0x48, 0xF8, - 0x81, 0xC3, 0xCD, 0xDB, 0xDB, 0x35, 0xFF, 0x00, 0x6E, 0xCB, 0xFC, 0x8F, 0x32, 0x9F, 0x8E, 0x1C, - 0x0B, 0x29, 0x24, 0xF1, 0x6D, 0x7A, 0xD3, 0xA9, 0xFF, 0x00, 0xC8, 0x96, 0xA8, 0xA8, 0xE1, 0x95, - 0x24, 0xE9, 0xD7, 0xD0, 0xD4, 0x95, 0xF5, 0x78, 0x4C, 0x66, 0x1F, 0x19, 0x45, 0x56, 0xC3, 0xCD, - 0x4A, 0x0F, 0xAA, 0x3F, 0x4B, 0xCA, 0xF3, 0x6C, 0x0E, 0x6B, 0x85, 0x8E, 0x2F, 0x03, 0x55, 0x54, - 0xA7, 0x2D, 0x9C, 0x5D, 0xD7, 0xFC, 0x07, 0xDD, 0x3D, 0x57, 0x50, 0xA2, 0x8A, 0x2B, 0xA4, 0xF4, - 0x02, 0x8A, 0x2A, 0xB4, 0xB7, 0x24, 0x36, 0x10, 0x0C, 0x0F, 0x5A, 0xF1, 0xF3, 0x9C, 0xFB, 0x03, - 0x93, 0xD2, 0x8D, 0x4C, 0x5C, 0xAC, 0xA4, 0xEC, 0x92, 0x57, 0x6F, 0xFE, 0x18, 0xF9, 0x4E, 0x2C, - 0xE3, 0x4C, 0x9B, 0x85, 0x70, 0xD0, 0xC4, 0x66, 0x95, 0x1C, 0x54, 0xDD, 0xA2, 0x92, 0x6D, 0xB6, - 0xB7, 0xB2, 0x5D, 0x17, 0x57, 0xB6, 0xDD, 0xD1, 0x66, 0x8A, 0xA7, 0xF6, 0xA9, 0x3D, 0x17, 0xF2, - 0xA3, 0xED, 0x52, 0x7A, 0x2F, 0xE5, 0x5F, 0x37, 0xFF, 0x00, 0x11, 0x1F, 0x21, 0xFE, 0x69, 0x7F, - 0xE0, 0x27, 0xC0, 0x7F, 0xC4, 0x7C, 0xE0, 0xBF, 0xE7, 0xA9, 0xFF, 0x00, 0x80, 0x3F, 0xF3, 0x2E, - 0x51, 0x55, 0xE3, 0xBA, 0x07, 0x87, 0x5C, 0x7B, 0x8A, 0x9D, 0x48, 0x65, 0xC8, 0x39, 0x15, 0xF4, - 0x99, 0x56, 0x7D, 0x96, 0xE6, 0xB1, 0x6F, 0x09, 0x55, 0x49, 0xAD, 0xD6, 0xCD, 0x7C, 0x9E, 0xBF, - 0x3D, 0x8F, 0xD0, 0x38, 0x6B, 0x8D, 0xB2, 0x0E, 0x25, 0x83, 0x96, 0x57, 0x89, 0x53, 0x92, 0xD5, - 0xC7, 0x69, 0x2F, 0x58, 0xBB, 0x3B, 0x79, 0xED, 0xE6, 0x2D, 0x14, 0x51, 0x5E, 0xC1, 0xF5, 0x41, - 0x45, 0x14, 0x50, 0x01, 0x45, 0x41, 0x2D, 0xCA, 0xAB, 0x61, 0x46, 0xEA, 0x67, 0xDA, 0x9B, 0xFB, - 0x83, 0xF3, 0xAF, 0x93, 0xC5, 0x71, 0xC6, 0x43, 0x86, 0xAA, 0xE9, 0x4E, 0xBD, 0xDA, 0xDE, 0xC9, - 0xB5, 0xF7, 0xA5, 0x6F, 0xB8, 0xFC, 0xC7, 0x32, 0xF1, 0x8B, 0x82, 0xB2, 0xFC, 0x4C, 0xB0, 0xD5, - 0x71, 0x97, 0x94, 0x5D, 0x9F, 0x2C, 0x67, 0x24, 0x9F, 0xF8, 0x92, 0x69, 0xFC, 0x9B, 0x2D, 0x51, - 0x55, 0xD6, 0xE8, 0x7F, 0x12, 0xE3, 0xE9, 0x53, 0x47, 0x22, 0x3F, 0xDD, 0x6F, 0xC2, 0xBD, 0x0C, - 0xBB, 0x89, 0x32, 0x8C, 0xC5, 0xA8, 0xE1, 0xAB, 0xA7, 0x2E, 0xCF, 0x47, 0xF7, 0x3B, 0x33, 0xDD, - 0xC8, 0x7C, 0x40, 0xE1, 0x7C, 0xFA, 0x4A, 0x9E, 0x5F, 0x8C, 0x8C, 0xA6, 0xFE, 0xCB, 0xBC, 0x64, - 0xFD, 0x23, 0x24, 0x9B, 0xF9, 0x26, 0x3A, 0x8A, 0x28, 0xAF, 0x70, 0xFB, 0x10, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA8, 0xE4, 0x99, 0x11, 0xB0, 0x4F, 0x3E, 0xD5, 0x25, 0x67, 0xCD, - 0xFE, 0xB9, 0xB3, 0xEA, 0x6B, 0xE2, 0xF8, 0xDB, 0x88, 0xB1, 0x59, 0x2E, 0x12, 0x9C, 0xF0, 0xD1, - 0x5C, 0xD3, 0x76, 0xBB, 0xD5, 0x2B, 0x2B, 0xFD, 0xE7, 0xE4, 0x7E, 0x2F, 0x71, 0xDE, 0x63, 0xC2, - 0x39, 0x5D, 0x0A, 0xB9, 0x7C, 0x22, 0xEA, 0x55, 0x93, 0x8D, 0xE4, 0xAE, 0x92, 0x4A, 0xFB, 0x5D, - 0x6A, 0xFA, 0x7C, 0xCB, 0x5F, 0x6A, 0x8F, 0xD1, 0xBF, 0x2A, 0x55, 0xB8, 0x89, 0xB8, 0xCE, 0x3E, - 0xB5, 0x51, 0x51, 0xD8, 0x65, 0x54, 0x9F, 0xA0, 0xA4, 0x65, 0x65, 0xFB, 0xC0, 0x8F, 0xAD, 0x7E, - 0x7B, 0x4F, 0xC4, 0x0E, 0x23, 0xA5, 0x15, 0x5A, 0xA5, 0x34, 0xE1, 0xE7, 0x06, 0x93, 0xF9, 0xA6, - 0x7E, 0x19, 0x47, 0xC7, 0x0E, 0x3D, 0xC3, 0xC2, 0x38, 0xBA, 0xF4, 0x23, 0x2A, 0x2F, 0xAB, 0xA5, - 0x25, 0x17, 0xE9, 0x24, 0xD7, 0xE6, 0x68, 0xF5, 0xE9, 0x45, 0x67, 0xC7, 0x23, 0xA7, 0xDD, 0x6F, - 0xC2, 0xAC, 0xDB, 0xDC, 0x07, 0x3B, 0x5F, 0x83, 0x5F, 0x77, 0x92, 0x71, 0xFE, 0x59, 0x98, 0xCE, - 0x34, 0x6A, 0xA7, 0x4A, 0xA3, 0xEF, 0xF0, 0xB7, 0xD9, 0x4B, 0xFC, 0xD2, 0x3F, 0x67, 0xE0, 0xFF, - 0x00, 0x1B, 0xF8, 0x7B, 0x3E, 0xAB, 0x4F, 0x09, 0x89, 0x4F, 0x0F, 0x5E, 0x5A, 0x5A, 0x56, 0x70, - 0x6F, 0xB2, 0x9F, 0x9F, 0x4E, 0x65, 0x1E, 0xDA, 0xB2, 0x7A, 0x28, 0xA2, 0xBE, 0xEC, 0xFD, 0xA0, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0xA5, 0x79, 0xFF, 0x00, 0x1F, 0x0D, 0x49, 0x0C, 0x4D, 0x26, 0x76, 0x91, 0xC7, 0xAD, 0x3A, 0xFB, - 0xFD, 0x70, 0xFA, 0x53, 0xB4, 0xF3, 0xF3, 0x30, 0xF5, 0x15, 0xFC, 0xFA, 0xF2, 0xBC, 0x36, 0x2B, - 0x8C, 0xAA, 0x60, 0xF1, 0x5F, 0x04, 0xE7, 0x3E, 0xB6, 0xDD, 0x39, 0x2F, 0xC6, 0xC7, 0xF0, 0xDC, - 0xB8, 0x77, 0x01, 0x99, 0x78, 0xB1, 0x88, 0xCA, 0xB3, 0x2B, 0xBA, 0x55, 0x6B, 0x55, 0xBD, 0x9D, - 0x9F, 0xBC, 0xA5, 0x38, 0xD9, 0xFA, 0xD9, 0x79, 0x89, 0xF6, 0x59, 0x3D, 0x56, 0x9A, 0xD6, 0xF2, - 0x2A, 0xE7, 0x83, 0xF4, 0xAB, 0xB4, 0x31, 0x01, 0x72, 0x4E, 0x05, 0x7E, 0x83, 0x5F, 0xC3, 0x9C, - 0x8B, 0xD9, 0x4B, 0x59, 0x47, 0x4D, 0xF9, 0xB6, 0xF3, 0xD5, 0x58, 0xFD, 0xCB, 0x19, 0xE0, 0x1F, - 0x05, 0xFD, 0x5E, 0x6D, 0x3A, 0x94, 0xEC, 0x9F, 0xBD, 0xCF, 0xF0, 0xF9, 0xBB, 0xAB, 0x59, 0x75, - 0xB9, 0x9B, 0x92, 0x0E, 0x45, 0x5E, 0xB6, 0x72, 0xF0, 0x82, 0x7A, 0xD5, 0x39, 0x08, 0x32, 0x31, - 0x1D, 0xCD, 0x5A, 0xB1, 0xFF, 0x00, 0x51, 0xF8, 0xD7, 0xC7, 0x78, 0x73, 0x5A, 0xA5, 0x2C, 0xEA, - 0xAE, 0x1A, 0x9C, 0xEF, 0x4D, 0xC6, 0x5E, 0x8E, 0xCD, 0x59, 0xFF, 0x00, 0x5D, 0xCF, 0xCA, 0x3C, - 0x03, 0xC5, 0x57, 0xC3, 0x71, 0x76, 0x27, 0x01, 0x46, 0xAB, 0x95, 0x07, 0x09, 0xDF, 0xB4, 0xB9, - 0x65, 0x15, 0x19, 0x5B, 0xE7, 0xF7, 0x32, 0x6A, 0x28, 0xA2, 0xBF, 0x72, 0x3F, 0xB2, 0x42, 0xB3, - 0x4D, 0x69, 0x56, 0x6D, 0x7E, 0x43, 0xE2, 0xB6, 0xD8, 0x3F, 0xFB, 0x7F, 0xFF, 0x00, 0x6C, 0x3F, - 0x96, 0x7E, 0x92, 0xDF, 0x0E, 0x57, 0xFF, 0x00, 0x71, 0xBF, 0xF7, 0x19, 0x3C, 0x56, 0xFB, 0xE3, - 0x0D, 0xBF, 0x19, 0xF6, 0xA5, 0xFB, 0x21, 0xEC, 0xFF, 0x00, 0xA5, 0x4D, 0x6B, 0xFF, 0x00, 0x1E, - 0xEB, 0xF4, 0xA9, 0x2B, 0xDE, 0xCA, 0xF8, 0x1F, 0x20, 0xC4, 0x65, 0xD4, 0x2A, 0xD4, 0xA1, 0xEF, - 0x4A, 0x11, 0x6D, 0xF3, 0x4B, 0x76, 0x93, 0x6F, 0xE2, 0xB1, 0xF6, 0x9C, 0x39, 0xE0, 0xEF, 0x04, - 0xE3, 0xB2, 0x2C, 0x1E, 0x26, 0xBE, 0x0D, 0xBA, 0x95, 0x29, 0x42, 0x52, 0x7E, 0xD2, 0xA2, 0xF7, - 0xA5, 0x04, 0xDB, 0xB7, 0x3D, 0xB7, 0x7D, 0xAD, 0xE4, 0x50, 0x96, 0x37, 0x8F, 0xEF, 0x0F, 0xC6, - 0x96, 0xDE, 0x53, 0x1B, 0xFF, 0x00, 0xB3, 0xDC, 0x55, 0xD7, 0x50, 0xCA, 0x54, 0xF7, 0xAC, 0xF6, - 0x1B, 0x58, 0x8F, 0x43, 0x5F, 0x03, 0xC4, 0xD9, 0x0D, 0x5E, 0x16, 0xC7, 0x51, 0xC5, 0xE0, 0x6A, - 0x3E, 0x56, 0xF4, 0xBE, 0xE9, 0xAD, 0xD3, 0xEE, 0x9A, 0xFC, 0x2E, 0x99, 0xF8, 0xA7, 0x88, 0x5C, - 0x17, 0x88, 0xF0, 0xE3, 0x38, 0xC2, 0xE6, 0x99, 0x35, 0x79, 0x7B, 0x39, 0x36, 0xE2, 0xDF, 0xC5, - 0x19, 0x46, 0xD7, 0x8B, 0x6A, 0xDC, 0xD1, 0x69, 0xF6, 0xD5, 0x5D, 0x3E, 0xEF, 0x46, 0x8A, 0x8E, - 0xD4, 0xE6, 0xDD, 0x73, 0x52, 0x57, 0xEE, 0x59, 0x7E, 0x2D, 0x63, 0x30, 0x74, 0xB1, 0x29, 0x59, - 0x4E, 0x2A, 0x5F, 0x7A, 0xB9, 0xFD, 0x93, 0x91, 0xE6, 0x71, 0xCD, 0x72, 0xAC, 0x36, 0x61, 0x15, - 0x65, 0x56, 0x11, 0x9D, 0xBB, 0x73, 0x45, 0x3B, 0x7C, 0xAE, 0x15, 0x5E, 0xF2, 0x5C, 0x7E, 0xED, - 0x4F, 0xD6, 0xAC, 0x31, 0xC2, 0xE4, 0xF6, 0xAC, 0xE6, 0x25, 0x98, 0x93, 0xDE, 0xBE, 0x1F, 0xC4, - 0x4C, 0xF6, 0xAE, 0x03, 0x05, 0x0C, 0x2D, 0x07, 0x69, 0x55, 0xBD, 0xDF, 0x55, 0x15, 0xBF, 0xDF, - 0x7B, 0x7A, 0x5C, 0xFC, 0x6F, 0xC7, 0x8E, 0x33, 0xC4, 0xE4, 0xB9, 0x45, 0x2C, 0xB7, 0x07, 0x2E, - 0x5A, 0x98, 0x9E, 0x6B, 0xB5, 0xBA, 0x84, 0x6D, 0x7B, 0x76, 0x72, 0x6D, 0x2B, 0xF6, 0xE6, 0x05, - 0x52, 0xCD, 0xB5, 0x47, 0x35, 0x2F, 0xD9, 0x64, 0xF5, 0x5F, 0xCE, 0xA6, 0xB3, 0x8C, 0x2C, 0x7B, - 0xBB, 0xB5, 0x4D, 0x5E, 0x4F, 0x0E, 0x78, 0x79, 0x83, 0xAD, 0x80, 0x85, 0x7C, 0xC2, 0xEE, 0x73, - 0xD5, 0x24, 0xED, 0x64, 0xF6, 0xF9, 0xF5, 0x7D, 0xB6, 0x3E, 0x67, 0x80, 0xBC, 0x09, 0xCA, 0xB1, - 0x59, 0x2D, 0x3C, 0x6E, 0x7B, 0xCC, 0xEA, 0xD5, 0x4A, 0x4A, 0x31, 0x97, 0x2A, 0x8C, 0x5A, 0xBA, - 0x4F, 0xBC, 0x9A, 0xD5, 0xF6, 0xDB, 0xA3, 0x6E, 0x8C, 0x90, 0x48, 0xBD, 0x57, 0x3F, 0x4A, 0x8C, - 0x12, 0x0E, 0x47, 0x15, 0xA5, 0x50, 0xDC, 0x40, 0xAF, 0xC8, 0xE1, 0xBF, 0x9D, 0x73, 0xE7, 0x5E, - 0x1A, 0xBA, 0x50, 0x75, 0xB2, 0xBA, 0x8D, 0xB5, 0xAF, 0x2C, 0xB7, 0xF9, 0x4B, 0x4D, 0x7D, 0x7E, - 0xF3, 0x8B, 0x8B, 0xBE, 0x8F, 0xB3, 0xC2, 0xD1, 0x78, 0xBE, 0x1C, 0xAD, 0x29, 0x4E, 0x3A, 0xFB, - 0x39, 0xB5, 0x77, 0xFE, 0x09, 0xAB, 0x6B, 0xD9, 0x35, 0xFF, 0x00, 0x6F, 0x5C, 0x6D, 0xBD, 0xC6, - 0x7E, 0x57, 0xEB, 0xD8, 0xD5, 0x8A, 0xA4, 0xB6, 0xF2, 0x6E, 0xC1, 0x18, 0xF7, 0xAB, 0xB5, 0xF5, - 0x5C, 0x0F, 0x8C, 0xCE, 0x2B, 0x61, 0x27, 0x4B, 0x32, 0x83, 0x5C, 0x96, 0x51, 0x72, 0x4D, 0x37, - 0xDE, 0xF7, 0xDE, 0xDA, 0x6A, 0x7E, 0x93, 0xE0, 0xEE, 0x6B, 0xC5, 0x78, 0xBC, 0xB2, 0xAE, 0x1F, - 0x88, 0x28, 0xCA, 0x2E, 0x93, 0x51, 0x84, 0xA6, 0x9C, 0x67, 0x25, 0xAD, 0xD3, 0xBE, 0xAF, 0x97, - 0x4F, 0x7B, 0xAD, 0xF5, 0x6D, 0xAB, 0x85, 0x14, 0x51, 0x5F, 0x70, 0x7E, 0xC4, 0x23, 0x30, 0x55, - 0xDC, 0x7A, 0x0A, 0xAE, 0xD7, 0x47, 0x3C, 0x27, 0xE6, 0x68, 0xBF, 0x63, 0xF2, 0xA7, 0xE2, 0x6A, - 0x08, 0x91, 0xA4, 0x6D, 0xAB, 0x5F, 0x91, 0xF1, 0x6F, 0x15, 0xE6, 0x8B, 0x36, 0x59, 0x66, 0x56, - 0xEC, 0xD5, 0x93, 0xB2, 0x4D, 0xCA, 0x4F, 0x5B, 0x6B, 0x7D, 0x17, 0xE7, 0x73, 0xF9, 0x7B, 0xC4, - 0xFF, 0x00, 0x12, 0xF8, 0x92, 0x3C, 0x4C, 0xB8, 0x7B, 0x86, 0xE4, 0xE3, 0x28, 0x59, 0x3E, 0x54, - 0xA5, 0x29, 0xCE, 0x49, 0x3B, 0x2B, 0xA7, 0x64, 0x93, 0x4B, 0xA6, 0xB7, 0xBE, 0x84, 0xDF, 0x6A, - 0x6F, 0xEE, 0x0A, 0x72, 0xDD, 0x0F, 0xE2, 0x4F, 0xC8, 0xD3, 0x3E, 0xCB, 0x27, 0xAA, 0xD3, 0x1A, - 0x09, 0x57, 0xF8, 0x73, 0xF4, 0xAF, 0x25, 0xE6, 0x7C, 0x77, 0x84, 0xFD, 0xE5, 0x48, 0xCD, 0xAF, - 0xF0, 0x27, 0xF9, 0x2B, 0x9F, 0x2F, 0x2E, 0x22, 0xF1, 0x9F, 0x2C, 0x5E, 0xDE, 0xB5, 0x3A, 0xB2, - 0x8A, 0xDE, 0xF4, 0xA3, 0x25, 0xF3, 0xE5, 0x8B, 0x6B, 0xD7, 0x42, 0xD4, 0x73, 0x46, 0xFD, 0x1B, - 0xF0, 0x35, 0x25, 0x66, 0x90, 0x47, 0x04, 0x54, 0x91, 0xCC, 0xE9, 0xD0, 0xE4, 0x7A, 0x1A, 0xF5, - 0x32, 0xBF, 0x13, 0xA4, 0xA4, 0xA9, 0xE6, 0x54, 0x6D, 0xFD, 0xE8, 0xFE, 0xB1, 0x7F, 0xA3, 0xF9, - 0x1F, 0x47, 0xC3, 0x9F, 0x48, 0x99, 0xC6, 0x6A, 0x86, 0x7F, 0x85, 0xB3, 0xBD, 0x9C, 0xE9, 0xDF, - 0x4F, 0x58, 0x49, 0xFD, 0xF6, 0x97, 0xC8, 0xBD, 0x45, 0x47, 0x0C, 0xCB, 0x27, 0x4E, 0x0F, 0xA5, - 0x49, 0x5F, 0xAA, 0x60, 0xB1, 0xD8, 0x6C, 0x6D, 0x15, 0x5F, 0x0D, 0x35, 0x28, 0x3E, 0xAB, 0xFA, - 0xD1, 0xF9, 0x1F, 0xD2, 0x99, 0x4E, 0x71, 0x97, 0xE7, 0x18, 0x48, 0xE3, 0x32, 0xFA, 0xAA, 0xA5, - 0x29, 0x6C, 0xD7, 0xE4, 0xFA, 0xA6, 0xBA, 0xA7, 0x66, 0xBA, 0x85, 0x14, 0x51, 0x5D, 0x47, 0xA4, - 0x14, 0x51, 0x51, 0xC9, 0x34, 0x69, 0xC1, 0x3C, 0xFA, 0x0A, 0xE5, 0xC5, 0xE3, 0x70, 0xD8, 0x3A, - 0x5E, 0xD7, 0x13, 0x51, 0x42, 0x3D, 0xDB, 0xB1, 0xE7, 0x66, 0x99, 0xBE, 0x5F, 0x95, 0x61, 0xDE, - 0x27, 0x1F, 0x5A, 0x34, 0xA9, 0xAE, 0xB2, 0x69, 0x2B, 0xF6, 0xD7, 0x77, 0xE4, 0xB5, 0x24, 0xA2, - 0xAB, 0x35, 0xD0, 0xFE, 0x14, 0xFC, 0xCD, 0x33, 0xED, 0x52, 0x7A, 0x2F, 0xE5, 0x5F, 0x29, 0x88, - 0xF1, 0x03, 0x20, 0xA3, 0x2B, 0x2A, 0xAE, 0x5E, 0x91, 0x7F, 0xAD, 0x8F, 0xCC, 0xF1, 0xDE, 0x38, - 0xF0, 0x46, 0x16, 0x7C, 0xB1, 0xC4, 0x4A, 0xA3, 0xFE, 0xE4, 0x24, 0xD7, 0xDE, 0xD4, 0x53, 0xF9, - 0x5C, 0xB9, 0x45, 0x53, 0xFB, 0x54, 0x9E, 0x8B, 0xF9, 0x51, 0xF6, 0xA9, 0x3D, 0x17, 0xF2, 0xAE, - 0x75, 0xE2, 0x46, 0x44, 0xDE, 0xF2, 0xFF, 0x00, 0xC0, 0x7F, 0xE0, 0x9C, 0x31, 0xF1, 0xFB, 0x83, - 0x1B, 0x49, 0xCA, 0xA2, 0xFF, 0x00, 0xB7, 0x3F, 0xE0, 0x97, 0x28, 0xA6, 0x43, 0x22, 0xC8, 0xB9, - 0x1D, 0x7B, 0x8A, 0x7D, 0x7D, 0xAE, 0x17, 0x17, 0x43, 0x17, 0x42, 0x35, 0xE8, 0x4B, 0x9A, 0x12, - 0xD5, 0x34, 0x7E, 0xBB, 0x96, 0xE6, 0x78, 0x3C, 0xCF, 0x07, 0x4F, 0x19, 0x82, 0xA8, 0xA7, 0x4A, - 0x6A, 0xEA, 0x4B, 0x66, 0xBF, 0xAD, 0x1A, 0x7A, 0xA7, 0xA3, 0xD4, 0x2A, 0x8D, 0xD7, 0x17, 0x0D, - 0xF5, 0xAB, 0xD5, 0x4E, 0xF8, 0x62, 0x6C, 0xFA, 0x8A, 0xF8, 0x3F, 0x13, 0x30, 0xEE, 0xA6, 0x4D, - 0x1A, 0x8B, 0xEC, 0x4D, 0x3F, 0x93, 0x4D, 0x7E, 0x6D, 0x1F, 0x8A, 0xFD, 0x21, 0xB0, 0x32, 0xAF, - 0xC2, 0x94, 0xEB, 0xC7, 0xFE, 0x5D, 0x55, 0x8B, 0x7E, 0x8D, 0x4A, 0x3F, 0x9B, 0x44, 0xBA, 0x79, - 0xFD, 0xDB, 0x0F, 0x7A, 0x99, 0x94, 0x32, 0xE1, 0x86, 0x6A, 0xB6, 0x9E, 0x7F, 0x78, 0xCB, 0xEA, - 0x2A, 0xD5, 0x7A, 0x7C, 0x13, 0x56, 0x9E, 0x2F, 0x87, 0x68, 0xC6, 0x69, 0x34, 0x93, 0x8B, 0x5D, - 0x34, 0x6F, 0xF4, 0xB1, 0xF4, 0x5E, 0x0F, 0xE2, 0x68, 0x66, 0x7C, 0x07, 0x84, 0xA7, 0x56, 0x2A, - 0x4A, 0x2A, 0x50, 0x92, 0x6A, 0xEB, 0x49, 0x3D, 0x1A, 0x7B, 0xDE, 0x36, 0xFB, 0xCA, 0xF2, 0x5A, - 0x8D, 0xB9, 0x42, 0x73, 0xE8, 0x6A, 0xAF, 0x4A, 0xD2, 0xAA, 0x97, 0xCA, 0x04, 0x81, 0x87, 0xF1, - 0x75, 0xAF, 0x90, 0xE3, 0xCE, 0x11, 0xC1, 0xE1, 0x70, 0x9F, 0xDA, 0x38, 0x28, 0xA8, 0x72, 0xB5, - 0xCC, 0x96, 0xCE, 0xED, 0x24, 0xD7, 0x6B, 0x37, 0xB2, 0xFD, 0x0F, 0xCB, 0x3C, 0x68, 0xF0, 0xBF, - 0x2B, 0xCB, 0xB2, 0xCF, 0xED, 0xDC, 0xA2, 0x9A, 0xA2, 0xA9, 0xB4, 0xAA, 0x41, 0x5E, 0xCD, 0x4A, - 0x49, 0x29, 0x45, 0x6C, 0x9A, 0x6D, 0x26, 0x96, 0x8D, 0x3B, 0xE9, 0x6D, 0x66, 0xB5, 0x93, 0xCC, - 0x4C, 0x1F, 0xBC, 0xB5, 0x2D, 0x53, 0xB1, 0x3F, 0xBE, 0x23, 0xDA, 0xAE, 0x57, 0xDA, 0x70, 0x56, - 0x65, 0x5B, 0x30, 0xC9, 0x69, 0xD4, 0xAE, 0xEF, 0x35, 0x78, 0xB7, 0xDE, 0xDB, 0x3F, 0xBA, 0xC7, - 0xEB, 0x9E, 0x10, 0xF1, 0x06, 0x2F, 0x3C, 0xE1, 0x1C, 0x3E, 0x23, 0x19, 0x2E, 0x6A, 0x90, 0x72, - 0x83, 0x93, 0xDE, 0x5C, 0xAF, 0x46, 0xFC, 0xED, 0x6B, 0xBE, 0xAF, 0x50, 0xA2, 0x8A, 0x2B, 0xEB, - 0x0F, 0xD3, 0x42, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x0A, 0xFA, - 0x80, 0xF9, 0x55, 0xBD, 0xF1, 0x50, 0xDA, 0x1C, 0x5C, 0x2D, 0x59, 0xBC, 0x5D, 0xD0, 0x9F, 0x6E, - 0x6A, 0x9A, 0x92, 0xAC, 0x08, 0xEC, 0x6B, 0xF0, 0xCE, 0x34, 0xBE, 0x5B, 0xC5, 0x54, 0xB1, 0xB6, - 0xD1, 0xF2, 0x4F, 0xD7, 0x95, 0xD9, 0xFE, 0x47, 0xF1, 0xAF, 0x8B, 0x49, 0xF0, 0xFF, 0x00, 0x89, - 0x38, 0x6C, 0xDF, 0x97, 0xDD, 0x6E, 0x95, 0x5D, 0x3A, 0xF2, 0x35, 0x19, 0x2F, 0x5B, 0x47, 0xF1, - 0x2F, 0xC8, 0xEB, 0x1A, 0xE5, 0x8D, 0x53, 0x9A, 0x56, 0x90, 0xF3, 0xC0, 0xF4, 0xA6, 0xC8, 0xEC, - 0xED, 0x96, 0x35, 0x25, 0xBC, 0x06, 0x4E, 0x4F, 0x0B, 0xFC, 0xEB, 0x3C, 0xE3, 0x88, 0xB3, 0x3E, - 0x29, 0xC5, 0xFD, 0x43, 0x2E, 0x8B, 0x54, 0x9F, 0x4E, 0xFE, 0x73, 0x7D, 0x17, 0x96, 0xDE, 0xAE, - 0xC6, 0x1C, 0x55, 0xC7, 0x9C, 0x45, 0xE2, 0x3E, 0x66, 0xB2, 0x4C, 0x86, 0x9C, 0xA3, 0x87, 0x7F, - 0x67, 0x67, 0x24, 0xBE, 0xD5, 0x47, 0xB2, 0x8A, 0xFE, 0x5B, 0xD9, 0x79, 0xBB, 0x0C, 0x86, 0x36, - 0x91, 0xB0, 0x3A, 0x77, 0x35, 0x7A, 0x35, 0x08, 0xA1, 0x47, 0x6A, 0x15, 0x42, 0xAE, 0x14, 0x60, - 0x52, 0xD7, 0xE9, 0x3C, 0x2B, 0xC2, 0x94, 0x32, 0x3A, 0x4E, 0x4D, 0xF3, 0x56, 0x97, 0xC4, 0xFA, - 0x7A, 0x2F, 0x2F, 0xCF, 0xF0, 0x3F, 0xA0, 0x7C, 0x35, 0xF0, 0xCF, 0x07, 0xC1, 0xB8, 0x69, 0x4E, - 0x52, 0xF6, 0x98, 0xAA, 0x8A, 0xD3, 0x9F, 0x44, 0xB7, 0xE5, 0x8F, 0x95, 0xF7, 0x6F, 0x59, 0x3D, - 0x5D, 0xB4, 0x48, 0xA2, 0x8A, 0x2B, 0xEB, 0x8F, 0xD4, 0x02, 0xB3, 0x6B, 0x4A, 0xB3, 0x6B, 0xF2, - 0x1F, 0x15, 0xB6, 0xC1, 0xFF, 0x00, 0xDB, 0xFF, 0x00, 0xFB, 0x61, 0xFC, 0xB3, 0xF4, 0x96, 0xDB, - 0x2B, 0xFF, 0x00, 0xB8, 0xDF, 0xFB, 0x88, 0xBD, 0x6B, 0xFF, 0x00, 0x1E, 0xEB, 0xF4, 0xA7, 0x96, - 0x03, 0xA9, 0x15, 0x9F, 0xB9, 0xB6, 0xE3, 0x71, 0xC7, 0xA6, 0x69, 0x2B, 0x97, 0x0D, 0xE2, 0x62, - 0xC2, 0xE0, 0xE9, 0x61, 0xE9, 0xE1, 0xAE, 0xE1, 0x18, 0xC6, 0xEE, 0x56, 0xD9, 0x25, 0xB5, 0x9F, - 0xE6, 0x79, 0xD9, 0x7F, 0xD2, 0x12, 0x39, 0x6E, 0x55, 0x86, 0xC0, 0xE1, 0xF2, 0xFE, 0x67, 0x4A, - 0x9C, 0x21, 0xCD, 0x2A, 0x96, 0x4D, 0xC6, 0x29, 0x37, 0x65, 0x17, 0xDB, 0xB9, 0x76, 0x4B, 0x88, - 0xD7, 0xA1, 0xDC, 0x7D, 0xAA, 0x9B, 0x1D, 0xCC, 0x4F, 0xA9, 0xA1, 0x41, 0x63, 0x80, 0x09, 0xFA, - 0x54, 0xF0, 0xDB, 0x13, 0xCC, 0x9F, 0x95, 0x78, 0xB8, 0xAC, 0x5E, 0x7D, 0xC6, 0x35, 0xE1, 0x4E, - 0x14, 0xED, 0x08, 0xBD, 0x2C, 0xAD, 0x15, 0xE6, 0xE4, 0xFA, 0xFF, 0x00, 0x49, 0x1F, 0x25, 0x99, - 0x66, 0x7C, 0x69, 0xE2, 0xBE, 0x36, 0x95, 0x0A, 0x58, 0x74, 0xA8, 0xC1, 0xBB, 0x72, 0xA6, 0xA9, - 0xC2, 0xFB, 0xB9, 0x4D, 0xDE, 0xEE, 0xDD, 0x2F, 0xFE, 0x18, 0xDC, 0x9A, 0xCC, 0x11, 0x6E, 0xB9, - 0xA9, 0x28, 0xE9, 0xC5, 0x15, 0xFB, 0xB6, 0x5D, 0x84, 0x58, 0x3C, 0x1D, 0x2C, 0x32, 0x77, 0xE4, - 0x8A, 0x8D, 0xFB, 0xD9, 0x58, 0xFE, 0xCE, 0xC8, 0x72, 0xB5, 0x94, 0xE5, 0x38, 0x6C, 0xBD, 0x4B, - 0x9B, 0xD8, 0xC2, 0x30, 0xBF, 0x7E, 0x58, 0xA5, 0x7F, 0x9D, 0x86, 0xCD, 0xCC, 0x4C, 0x3D, 0x8D, - 0x67, 0xD6, 0x95, 0x54, 0xBA, 0x80, 0xAB, 0x6E, 0x51, 0x95, 0xFE, 0x55, 0xF9, 0xCF, 0x89, 0x59, - 0x2E, 0x2B, 0x15, 0x4E, 0x96, 0x32, 0x84, 0x79, 0x94, 0x13, 0x52, 0x4B, 0x74, 0x9F, 0x5F, 0x4E, - 0xFF, 0x00, 0xF0, 0xE7, 0xE0, 0xBF, 0x48, 0x1E, 0x11, 0xCC, 0x73, 0x2A, 0x18, 0x6C, 0xDB, 0x05, - 0x07, 0x38, 0xD1, 0x52, 0x8C, 0xD2, 0xD5, 0xA4, 0xDA, 0x6A, 0x56, 0xEA, 0x96, 0xBC, 0xDD, 0xB4, - 0x7B, 0x5D, 0xA9, 0xED, 0xE4, 0x56, 0x8C, 0x00, 0x79, 0x03, 0xA5, 0x49, 0x59, 0xBD, 0x2A, 0x45, - 0x9E, 0x55, 0xFE, 0x2C, 0xFD, 0x6B, 0x93, 0x27, 0xF1, 0x32, 0x8C, 0x28, 0xC6, 0x96, 0x3A, 0x8B, - 0x4D, 0x24, 0xAF, 0x1D, 0x6F, 0x6F, 0x27, 0x6B, 0x7D, 0xEC, 0xF3, 0x38, 0x57, 0xE9, 0x0D, 0x84, - 0xA3, 0x85, 0xA7, 0x85, 0xCE, 0x70, 0xB2, 0x4E, 0x09, 0x47, 0x9E, 0x9D, 0x9A, 0x76, 0x56, 0xBB, - 0x8C, 0x9A, 0xB7, 0x9D, 0x9B, 0xF4, 0x2F, 0x51, 0x55, 0x56, 0xE9, 0x87, 0xDE, 0x50, 0x7E, 0x95, - 0x2A, 0x5C, 0x46, 0xDD, 0x4E, 0xDF, 0xAD, 0x7D, 0x9E, 0x03, 0x8D, 0x32, 0x3C, 0x6D, 0x94, 0x6B, - 0xA8, 0xBE, 0xD2, 0xF7, 0x7F, 0x17, 0xA7, 0xE2, 0x7E, 0xB5, 0x92, 0xF8, 0xB9, 0xC1, 0xB9, 0xBB, - 0x51, 0xA7, 0x8C, 0x54, 0xE4, 0xFA, 0x54, 0xF7, 0x3F, 0x19, 0x7B, 0xAF, 0xE4, 0xC9, 0x68, 0xA4, - 0x52, 0x18, 0x64, 0x1C, 0xFD, 0x29, 0x6B, 0xEA, 0x23, 0x38, 0xCE, 0x2A, 0x51, 0x77, 0x4C, 0xFD, - 0x1A, 0x9D, 0x48, 0x55, 0x82, 0x9D, 0x39, 0x27, 0x17, 0xB3, 0x5A, 0xA7, 0xE8, 0xC2, 0x8A, 0x28, - 0xAA, 0x2C, 0xA7, 0x7D, 0xFE, 0xB8, 0x7F, 0xBB, 0x4E, 0xD3, 0xFE, 0xF3, 0x7E, 0x14, 0x97, 0xE0, - 0xF9, 0xA0, 0xFB, 0x53, 0x2D, 0xA5, 0xF2, 0xD8, 0xE4, 0x64, 0x1E, 0xB5, 0xF8, 0x3D, 0x4C, 0x5D, - 0x0C, 0x0F, 0x1C, 0x4E, 0xBE, 0x25, 0xF2, 0xC1, 0x4D, 0xDD, 0xF6, 0xBC, 0x6C, 0x9F, 0xE2, 0x8F, - 0xE2, 0xBA, 0xF9, 0xA6, 0x0F, 0x26, 0xF1, 0x8E, 0xA6, 0x37, 0x30, 0x97, 0x25, 0x28, 0xD5, 0x95, - 0xDB, 0x4F, 0x4E, 0x6A, 0x6D, 0x45, 0xFA, 0x5D, 0xA7, 0x7E, 0xDA, 0x97, 0xA8, 0xA4, 0x56, 0x0C, - 0xB9, 0x53, 0x91, 0x4B, 0x5F, 0xBA, 0xD3, 0xA9, 0x0A, 0x90, 0x53, 0x83, 0xBA, 0x7A, 0xA6, 0xB6, - 0x67, 0xF6, 0x7D, 0x0A, 0xF4, 0xAB, 0xD2, 0x8D, 0x5A, 0x32, 0x52, 0x84, 0x95, 0xD3, 0x4E, 0xE9, - 0xA7, 0xB3, 0x4D, 0x6E, 0x86, 0xC8, 0x8A, 0xFF, 0x00, 0x79, 0x73, 0x55, 0xE6, 0xB6, 0x23, 0x98, - 0xF9, 0x1E, 0x95, 0x6A, 0x8A, 0xF0, 0xF3, 0x9E, 0x19, 0xCB, 0x73, 0x6A, 0x6D, 0x62, 0x29, 0xA5, - 0x3F, 0xE6, 0x5A, 0x49, 0x7C, 0xFA, 0xFA, 0x3B, 0xA3, 0xE3, 0x78, 0xB3, 0xC3, 0xDE, 0x1F, 0xE2, - 0x7A, 0x32, 0x8E, 0x36, 0x82, 0x55, 0x5E, 0xD5, 0x22, 0x92, 0x9A, 0x7E, 0xBD, 0x57, 0x94, 0xAE, - 0x8C, 0xDE, 0x41, 0xAB, 0xB6, 0x8E, 0x5E, 0x2C, 0x9E, 0xA0, 0xE2, 0x96, 0x58, 0x51, 0xDB, 0x24, - 0x60, 0xFB, 0x53, 0xA3, 0x50, 0x8B, 0xB5, 0x45, 0x7C, 0xA7, 0x0A, 0x70, 0x96, 0x67, 0x92, 0xE6, - 0x75, 0x27, 0x2A, 0xA9, 0xD0, 0x69, 0xAD, 0x3E, 0xD7, 0x6B, 0xAE, 0x8D, 0x7F, 0xC0, 0x47, 0xE6, - 0x9E, 0x19, 0xF8, 0x61, 0xC4, 0x3C, 0x23, 0xC4, 0x55, 0xAA, 0xD4, 0xC4, 0x46, 0x58, 0x47, 0x16, - 0xB4, 0x6F, 0xDF, 0x7A, 0x72, 0xB7, 0x0D, 0xA2, 0xE3, 0xDE, 0xEF, 0xB2, 0x6D, 0x36, 0x3A, 0x8A, - 0x28, 0xAF, 0xD2, 0x8F, 0xE8, 0x20, 0xAC, 0xE6, 0x24, 0xB1, 0x26, 0xB4, 0x6B, 0x36, 0xBF, 0x22, - 0xF1, 0x56, 0x4F, 0x97, 0x08, 0xBA, 0x7B, 0xFF, 0x00, 0xFB, 0x69, 0xFC, 0xB9, 0xF4, 0x96, 0x9C, - 0x95, 0x3C, 0xAE, 0x37, 0xD1, 0xBA, 0xBF, 0x87, 0xB3, 0xFF, 0x00, 0x36, 0x49, 0x1C, 0x32, 0x38, - 0xC8, 0x1C, 0x1E, 0xE4, 0xD4, 0x8B, 0x6A, 0xDD, 0xDC, 0x7E, 0x02, 0xA6, 0xB5, 0xFF, 0x00, 0x8F, - 0x75, 0xFA, 0x54, 0x95, 0xE9, 0xE4, 0x9C, 0x03, 0x92, 0xD4, 0xC1, 0x51, 0xC4, 0x57, 0x8C, 0xA7, - 0x29, 0xC6, 0x2D, 0xDE, 0x4D, 0x2B, 0xB4, 0x9E, 0x9C, 0xB6, 0xFC, 0xCF, 0xA2, 0xE0, 0xFF, 0x00, - 0x04, 0x78, 0x46, 0xBE, 0x51, 0x85, 0xC6, 0xE3, 0x21, 0x3A, 0xB3, 0xAB, 0x4E, 0x13, 0x69, 0xCD, - 0xA4, 0x9C, 0xA2, 0x9B, 0xB7, 0x27, 0x2B, 0xB6, 0xBA, 0x5D, 0xB2, 0xB7, 0xD9, 0x3F, 0xE9, 0xA7, - 0xE9, 0x4C, 0x9E, 0x03, 0x1A, 0xEE, 0xCE, 0xE1, 0xDF, 0x8A, 0xB9, 0x48, 0xEA, 0x19, 0x4A, 0x9E, - 0xF5, 0xDD, 0x98, 0x78, 0x7D, 0x92, 0xD4, 0xC2, 0x54, 0x8E, 0x1A, 0x97, 0x2D, 0x4B, 0x3E, 0x57, - 0xCD, 0x27, 0xAF, 0x4D, 0xDB, 0x5E, 0xA7, 0xAF, 0x9E, 0x78, 0x1B, 0xC2, 0x35, 0xB2, 0xCA, 0xF0, - 0xCB, 0xB0, 0xDE, 0xCE, 0xBB, 0x8B, 0xE4, 0x97, 0x3D, 0x47, 0x69, 0x74, 0xBA, 0x94, 0x9A, 0xB3, - 0x7A, 0x3D, 0x36, 0x7A, 0x6A, 0x51, 0x81, 0xF6, 0x48, 0x1B, 0xB7, 0x7A, 0xBF, 0x59, 0xA4, 0x60, - 0xE0, 0xD5, 0xEB, 0x53, 0x98, 0x14, 0xD7, 0xCD, 0xF8, 0x63, 0x99, 0x54, 0x55, 0x2B, 0xE5, 0xD3, - 0xD9, 0x7B, 0xCB, 0xCB, 0x54, 0xA4, 0xBF, 0x14, 0x7C, 0x07, 0xD1, 0xD7, 0x88, 0x2B, 0xC6, 0xBE, - 0x2F, 0x21, 0xAD, 0xB2, 0x5E, 0xD2, 0x3E, 0x4D, 0x35, 0x19, 0xAF, 0x9D, 0xE2, 0xFD, 0x53, 0xEE, - 0x49, 0x55, 0xF5, 0x01, 0xC2, 0x9A, 0xB1, 0x50, 0x6A, 0x1F, 0xEA, 0xD7, 0xEB, 0x5F, 0x6D, 0xC6, - 0xD4, 0xD5, 0x4E, 0x1F, 0xC4, 0xA7, 0xD9, 0x3F, 0xBA, 0x49, 0x9F, 0xB0, 0x78, 0xBF, 0x42, 0x35, - 0xB8, 0x1B, 0x30, 0x8C, 0xBA, 0x46, 0x2F, 0xE7, 0x19, 0xC5, 0xAF, 0xC8, 0x86, 0xCC, 0xE2, 0xE0, - 0x7B, 0xD5, 0xDA, 0xCF, 0x8D, 0x8A, 0x38, 0x61, 0xD4, 0x53, 0xDA, 0xE2, 0x53, 0xDF, 0x1F, 0x41, - 0x5F, 0x9C, 0xF0, 0x7F, 0x18, 0xE0, 0x32, 0x6C, 0xAE, 0x74, 0x31, 0x2A, 0x4E, 0x7C, 0xCD, 0xA4, - 0x97, 0x46, 0xA3, 0xD5, 0xB5, 0xD6, 0xE7, 0xE0, 0x9E, 0x15, 0xF8, 0xAD, 0x92, 0xF0, 0x9F, 0x0E, - 0x55, 0xC1, 0xE3, 0xD4, 0xE7, 0x53, 0xDA, 0x4A, 0x51, 0x8C, 0x52, 0x7E, 0xEB, 0x8C, 0x16, 0xED, - 0xA4, 0xB5, 0x4C, 0xB6, 0xEE, 0xA8, 0x32, 0xC7, 0x15, 0x4A, 0xE2, 0x43, 0x24, 0x99, 0xED, 0xD8, - 0x53, 0x09, 0x24, 0xE4, 0x9A, 0x92, 0x08, 0x5A, 0x46, 0xF4, 0x5E, 0xE6, 0xB8, 0xB3, 0x9E, 0x26, - 0xCC, 0xB8, 0xA6, 0xA4, 0x30, 0x18, 0x4A, 0x5C, 0xB0, 0x6F, 0xE1, 0x5A, 0xB7, 0xE7, 0x27, 0xA5, - 0x92, 0xDF, 0xB2, 0xDD, 0xF4, 0x3C, 0x7E, 0x2C, 0xF1, 0x0F, 0x3F, 0xF1, 0x22, 0xBD, 0x2C, 0x97, - 0x2C, 0xC3, 0x38, 0x53, 0x94, 0xAF, 0xC8, 0x9F, 0x33, 0x93, 0x5B, 0x39, 0xCA, 0xC9, 0x28, 0xC7, - 0x7E, 0x89, 0x6E, 0xDB, 0xB2, 0xB4, 0x96, 0x0A, 0x77, 0x17, 0xED, 0x8C, 0x55, 0xAA, 0x45, 0x50, - 0xAB, 0xB4, 0x0E, 0x05, 0x2D, 0x7E, 0xC3, 0xC3, 0x99, 0x32, 0xC9, 0xF2, 0xDA, 0x78, 0x4B, 0xDE, - 0x4B, 0x56, 0xFC, 0xDE, 0xF6, 0xF2, 0xE8, 0x8F, 0xEA, 0xAE, 0x01, 0xE1, 0x38, 0xF0, 0xAE, 0x41, - 0x47, 0x2C, 0xE7, 0xE6, 0x9A, 0xBB, 0x93, 0xE8, 0xE5, 0x2D, 0x5D, 0xBC, 0x96, 0xCB, 0xC9, 0x5C, - 0x28, 0xA2, 0x8A, 0xF7, 0x0F, 0xB2, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, - 0xA2, 0x80, 0x1B, 0x20, 0x26, 0x36, 0x03, 0xB8, 0xAC, 0xF2, 0x08, 0x38, 0x35, 0xA5, 0x54, 0x6E, - 0x86, 0x2E, 0x1A, 0xBF, 0x27, 0xF1, 0x4B, 0x00, 0x9D, 0x0A, 0x18, 0xDB, 0xEA, 0x9F, 0x27, 0xDE, - 0x9B, 0xFD, 0x0F, 0xE6, 0x4F, 0xA4, 0x7E, 0x4B, 0x19, 0x60, 0xB0, 0x79, 0xB2, 0x6E, 0xF1, 0x93, - 0xA6, 0xD7, 0x4B, 0x49, 0x39, 0x27, 0xFF, 0x00, 0x92, 0xBF, 0xBC, 0x7D, 0xAC, 0x2A, 0xE3, 0x7B, - 0x1C, 0x8C, 0xF4, 0xAB, 0x7D, 0x3A, 0x55, 0x7D, 0x3C, 0xFE, 0xED, 0x87, 0xBD, 0x58, 0xAF, 0xA8, - 0xE0, 0x6C, 0x1E, 0x12, 0x8E, 0x4B, 0x46, 0xB5, 0x08, 0x25, 0x29, 0xAB, 0xC9, 0xF5, 0x6D, 0x36, - 0xB7, 0xFD, 0x36, 0x47, 0xE8, 0xDE, 0x0D, 0xE5, 0x59, 0x5E, 0x17, 0x84, 0x70, 0xB8, 0xAC, 0x1D, - 0x25, 0x19, 0xD5, 0x8D, 0xE7, 0x2E, 0xB2, 0x92, 0x6D, 0x3B, 0xB7, 0xAD, 0x93, 0x4E, 0xCB, 0x65, - 0xD1, 0x05, 0x14, 0x51, 0x5F, 0x60, 0x7E, 0xAA, 0x14, 0x51, 0x45, 0x00, 0x15, 0x9B, 0x5A, 0x55, - 0x9B, 0x5F, 0x90, 0xF8, 0xAD, 0xB6, 0x0F, 0xFE, 0xDF, 0xFF, 0x00, 0xDB, 0x0F, 0xE5, 0x9F, 0xA4, - 0xB6, 0xD9, 0x5F, 0xFD, 0xC6, 0xFF, 0x00, 0xDC, 0x65, 0xA8, 0x20, 0x8D, 0xA3, 0x56, 0x39, 0xC9, - 0xF7, 0xA9, 0x16, 0x18, 0x87, 0xF0, 0x0F, 0xC6, 0x8B, 0x5F, 0xF8, 0xF7, 0x5F, 0xA5, 0x49, 0x5F, - 0x6B, 0x90, 0xE4, 0x79, 0x5C, 0x72, 0xEC, 0x3D, 0x5F, 0xAB, 0xC3, 0x99, 0xC2, 0x2D, 0xBE, 0x54, - 0xDD, 0xF9, 0x56, 0xB7, 0xB1, 0xFA, 0xEF, 0x05, 0xF0, 0x6F, 0x0D, 0xC3, 0x22, 0xC0, 0xE2, 0x56, - 0x02, 0x97, 0xB4, 0x95, 0x2A, 0x72, 0x72, 0x70, 0x8B, 0x6D, 0xB8, 0x26, 0xDD, 0xDA, 0xBD, 0xDB, - 0x11, 0x40, 0x5E, 0x80, 0x0F, 0xA5, 0x2D, 0x14, 0x57, 0xD3, 0xC2, 0x11, 0x84, 0x54, 0x62, 0xAC, - 0x8F, 0xD1, 0x69, 0x52, 0xA7, 0x4A, 0x0A, 0x14, 0xE2, 0x94, 0x57, 0x44, 0xAC, 0x82, 0x8A, 0x28, - 0xAA, 0x34, 0x0A, 0x28, 0xA2, 0x80, 0x19, 0x24, 0x51, 0xBF, 0xDE, 0x5F, 0xC4, 0x54, 0x4D, 0x6A, - 0x3F, 0x85, 0xCF, 0xE2, 0x2A, 0xC5, 0x15, 0xE0, 0xE3, 0xF8, 0x63, 0x27, 0xC7, 0xC9, 0xCB, 0x11, - 0x87, 0x8B, 0x93, 0xEA, 0xB4, 0x7F, 0x7C, 0x6C, 0xCF, 0x8B, 0xCE, 0xFC, 0x3B, 0xE1, 0x4C, 0xEE, - 0xA3, 0xA9, 0x8E, 0xC0, 0xC2, 0x53, 0x7B, 0xC9, 0x5E, 0x12, 0x7E, 0xB2, 0x83, 0x8B, 0x7F, 0x36, - 0x53, 0x7B, 0x69, 0x07, 0x4C, 0x35, 0x44, 0xCA, 0xCB, 0xF7, 0x94, 0x8A, 0xD1, 0xA4, 0x60, 0x0A, - 0xE0, 0x8E, 0x2B, 0xE4, 0x33, 0x1F, 0x0C, 0x72, 0xFA, 0xA9, 0xCB, 0x07, 0x52, 0x54, 0xDF, 0x67, - 0xEF, 0x2F, 0xF3, 0xFC, 0x59, 0xF9, 0x66, 0x7D, 0xF4, 0x77, 0xC8, 0xF1, 0x2A, 0x53, 0xCA, 0xAB, - 0xCE, 0x8C, 0xBA, 0x29, 0x7B, 0xF0, 0xF2, 0x5A, 0xDA, 0x4B, 0xD7, 0x99, 0xFA, 0x14, 0x61, 0x91, - 0xA3, 0x6C, 0x8F, 0xC4, 0x55, 0xE5, 0x60, 0xCA, 0x18, 0x74, 0x35, 0x42, 0x64, 0xD9, 0x21, 0x5A, - 0xB1, 0x60, 0xD9, 0x8C, 0xAF, 0xA1, 0xAF, 0x17, 0xC3, 0xFC, 0xD3, 0x13, 0x82, 0xCC, 0xAA, 0x64, - 0xD8, 0x97, 0xA7, 0xBD, 0x65, 0xDA, 0x51, 0xDE, 0xDE, 0x4D, 0x27, 0xF3, 0x47, 0xC8, 0x78, 0x1F, - 0xC4, 0x79, 0x8E, 0x51, 0xC4, 0x15, 0xB8, 0x53, 0x1E, 0xDF, 0x2B, 0x73, 0x4A, 0x2D, 0xDF, 0x92, - 0xA4, 0x2F, 0xCC, 0x97, 0x94, 0x92, 0x77, 0xF3, 0x49, 0xAD, 0xD9, 0x62, 0x8A, 0x28, 0xAF, 0xD9, - 0xCF, 0xEB, 0x61, 0xB2, 0xA2, 0xC8, 0xB8, 0x6A, 0xA7, 0x3C, 0x4D, 0x19, 0xE7, 0x91, 0xD8, 0xD5, - 0xEA, 0x46, 0x01, 0x97, 0x04, 0x64, 0x57, 0xC9, 0x71, 0x37, 0x08, 0xE0, 0xF3, 0xAA, 0x6E, 0x7F, - 0x0D, 0x64, 0xB4, 0x97, 0xE9, 0x2E, 0xEB, 0xF1, 0x5D, 0x3B, 0x1F, 0x98, 0x78, 0x85, 0xE1, 0x7E, - 0x55, 0xC5, 0xD4, 0x5D, 0x5B, 0x7B, 0x3C, 0x5A, 0x56, 0x8D, 0x45, 0xD6, 0xDB, 0x29, 0xAF, 0xB4, - 0xBC, 0xF7, 0x5D, 0x3B, 0x3A, 0x30, 0xC8, 0xD1, 0xB6, 0x47, 0xE2, 0x2A, 0xE4, 0x32, 0xAC, 0x8B, - 0xC7, 0x5E, 0xE2, 0xAB, 0xDC, 0x5B, 0x95, 0xF9, 0x93, 0x95, 0xF4, 0xF4, 0xA8, 0x55, 0x8A, 0xB6, - 0x54, 0xE0, 0xD7, 0xE6, 0x59, 0x6E, 0x77, 0x9B, 0xF0, 0x96, 0x2F, 0xEA, 0x78, 0xD8, 0x37, 0x4B, - 0xB7, 0x97, 0x78, 0x3F, 0xD3, 0x6E, 0xF6, 0x67, 0xF3, 0xBE, 0x41, 0xC5, 0xFC, 0x53, 0xE1, 0x7E, - 0x67, 0xFD, 0x93, 0x9B, 0xD3, 0x73, 0xC3, 0x5F, 0xE0, 0x6E, 0xEA, 0xDF, 0xCD, 0x4A, 0x5B, 0x79, - 0xDB, 0x66, 0xF4, 0x6A, 0x32, 0xD5, 0x68, 0xD1, 0x50, 0xC1, 0x70, 0xAD, 0x85, 0x6E, 0x1B, 0xF9, - 0xD4, 0xD5, 0xFB, 0x4E, 0x59, 0x9A, 0xE0, 0xF3, 0x3A, 0x0A, 0xBE, 0x16, 0x6A, 0x51, 0xFC, 0x57, - 0x93, 0x5D, 0x19, 0xFD, 0x73, 0xC3, 0xBC, 0x4B, 0x95, 0x71, 0x0E, 0x09, 0x63, 0x32, 0xDA, 0xCA, - 0x70, 0x7B, 0xF7, 0x8B, 0xED, 0x25, 0xBA, 0x7E, 0xBF, 0x2D, 0x02, 0x8A, 0x28, 0xAF, 0x44, 0xF7, - 0x82, 0x8A, 0x28, 0xA0, 0x02, 0xB3, 0x4F, 0x5A, 0xD2, 0xAC, 0xF9, 0x86, 0x26, 0x61, 0xEF, 0x5F, - 0x93, 0xF8, 0xA9, 0x45, 0xBA, 0x18, 0x5A, 0xBD, 0x9C, 0x97, 0xDE, 0x93, 0xFD, 0x0F, 0xE6, 0x4F, - 0xA4, 0xA6, 0x15, 0xCB, 0x05, 0x97, 0x62, 0x7A, 0x46, 0x55, 0x23, 0xFF, 0x00, 0x81, 0x28, 0xBF, - 0xFD, 0xB4, 0xB9, 0x6B, 0xFF, 0x00, 0x1E, 0xEB, 0x52, 0x55, 0x04, 0x96, 0x45, 0x5D, 0xAA, 0xD8, - 0x1F, 0x4A, 0x5F, 0x3E, 0x5F, 0xEF, 0xFE, 0x82, 0xAB, 0x2C, 0xF1, 0x1F, 0x2B, 0xC3, 0x60, 0x68, - 0xD0, 0xA9, 0x4E, 0x7C, 0xD0, 0x8C, 0x62, 0xEC, 0xA3, 0x6B, 0xA4, 0x96, 0x9E, 0xF1, 0xA7, 0x0E, - 0xF8, 0xFB, 0xC3, 0x99, 0x76, 0x4D, 0x84, 0xC1, 0x62, 0x30, 0xF5, 0x9C, 0xE9, 0x53, 0x84, 0x1F, - 0x2C, 0x61, 0x6B, 0xC6, 0x29, 0x3B, 0x5E, 0xA2, 0x76, 0xD3, 0x4B, 0xA4, 0x5E, 0xA8, 0x66, 0xB8, - 0x44, 0xE0, 0x7C, 0xCD, 0x55, 0x5A, 0x59, 0x1B, 0xAB, 0x9A, 0x45, 0x05, 0x8E, 0x00, 0xC9, 0xAE, - 0x0C, 0xD7, 0xC4, 0xBA, 0xD8, 0x88, 0xFB, 0x1C, 0xB2, 0x93, 0x8C, 0x9E, 0x97, 0x7A, 0xBF, 0x94, - 0x55, 0xD5, 0xFE, 0xFF, 0x00, 0x43, 0xC5, 0xE2, 0x5F, 0xA4, 0x26, 0x2F, 0x1B, 0x4F, 0xEA, 0xBC, - 0x3D, 0x86, 0x70, 0x9C, 0xB4, 0xE7, 0x9D, 0xA5, 0x2D, 0x76, 0xE5, 0x82, 0xBA, 0xBF, 0xAB, 0x97, - 0xA0, 0x31, 0x25, 0x89, 0x3D, 0xCD, 0x5D, 0xB5, 0x04, 0x40, 0xA0, 0xD4, 0x70, 0x5B, 0x63, 0xE6, - 0x93, 0xF2, 0xAB, 0x15, 0xEB, 0x70, 0x0F, 0x0C, 0xE3, 0x70, 0x35, 0x67, 0x98, 0x63, 0x57, 0x2C, - 0xA6, 0xAC, 0xA2, 0xF7, 0xD5, 0xA6, 0xDB, 0xED, 0xB6, 0xDB, 0xF7, 0x3E, 0x9B, 0xC1, 0x3F, 0x0F, - 0x33, 0x7C, 0x9F, 0x13, 0x5B, 0x3D, 0xCD, 0xD3, 0x85, 0x4A, 0xB1, 0x71, 0x8C, 0x1F, 0xC5, 0x69, - 0x35, 0x27, 0x29, 0x76, 0x6D, 0xA5, 0x64, 0xF5, 0xDE, 0xF6, 0x0A, 0x83, 0x50, 0xFF, 0x00, 0x56, - 0xBF, 0x5A, 0x9E, 0xA0, 0xD4, 0x3F, 0xD5, 0xAF, 0xD6, 0xBE, 0xA7, 0x8C, 0xBF, 0xE4, 0x41, 0x8A, - 0xFF, 0x00, 0x0F, 0xEA, 0x8F, 0xD2, 0x3C, 0x58, 0xFF, 0x00, 0x92, 0x23, 0x31, 0xFF, 0x00, 0x07, - 0xFE, 0xDC, 0x8A, 0xD1, 0xA9, 0x77, 0x0A, 0x3B, 0xD4, 0xEB, 0x6B, 0xFD, 0xE7, 0xFC, 0x85, 0x45, - 0x69, 0xFF, 0x00, 0x1F, 0x0B, 0xFE, 0x7B, 0x55, 0xEA, 0xFC, 0xFB, 0x80, 0xB8, 0x67, 0x2B, 0xCC, - 0xF0, 0x13, 0xC4, 0xE3, 0x29, 0xF3, 0x49, 0x4D, 0xA5, 0xAB, 0x4A, 0xC9, 0x45, 0xEC, 0x9A, 0xEE, - 0x7E, 0x1B, 0xE0, 0xA7, 0x87, 0x7C, 0x39, 0xC4, 0x59, 0x35, 0x5C, 0xC3, 0x34, 0xA1, 0xED, 0x2A, - 0x46, 0xAB, 0x8A, 0xF7, 0xA4, 0x95, 0x94, 0x61, 0x25, 0xA4, 0x5A, 0xBE, 0xAD, 0xEE, 0x47, 0x1C, - 0x11, 0xAF, 0xF0, 0xE7, 0xEB, 0x52, 0x51, 0x45, 0x7E, 0xB9, 0x83, 0xC0, 0x61, 0x70, 0x70, 0xF6, - 0x78, 0x6A, 0x6A, 0x0B, 0xC9, 0x24, 0x7F, 0x51, 0x65, 0x79, 0x2E, 0x5B, 0x94, 0xD1, 0xF6, 0x39, - 0x7D, 0x08, 0xD2, 0x8F, 0x68, 0xC5, 0x2F, 0xBE, 0xDB, 0xFC, 0xC2, 0x8A, 0x28, 0xAE, 0xB3, 0xD3, - 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x0A, 0xA7, 0x7C, 0x31, 0x3F, 0xD4, 0x55, 0xCA, 0xA9, 0x7E, 0x3F, 0x7C, 0x0F, 0xB5, 0x7C, 0x17, - 0x89, 0x10, 0xE6, 0xC8, 0x9B, 0xB6, 0xD2, 0x8B, 0xFC, 0xD7, 0xEA, 0x7E, 0x29, 0xE3, 0xF5, 0x15, - 0x3E, 0x0C, 0x94, 0xAD, 0xF0, 0xD4, 0x83, 0xF4, 0xDD, 0x7E, 0xB6, 0x1D, 0xA7, 0xFF, 0x00, 0x1F, - 0xE1, 0x56, 0x6A, 0xAE, 0x9E, 0x7F, 0x78, 0xC3, 0xDA, 0xAD, 0x57, 0x57, 0x87, 0xF5, 0x14, 0xB8, - 0x7A, 0x82, 0x5D, 0x1C, 0x97, 0xFE, 0x4C, 0xDF, 0xEA, 0x7A, 0x3E, 0x07, 0xD7, 0x8D, 0x4E, 0x04, - 0xC2, 0x45, 0x3D, 0x62, 0xEA, 0x27, 0xEB, 0xED, 0x24, 0xFF, 0x00, 0x26, 0x82, 0x8A, 0x28, 0xAF, - 0xB3, 0x3F, 0x5A, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0xCD, 0xAD, 0x2A, 0xCD, 0xAF, 0xC8, 0x7C, 0x56, - 0xDB, 0x07, 0xFF, 0x00, 0x6F, 0xFF, 0x00, 0xED, 0x87, 0xF2, 0xCF, 0xD2, 0x5B, 0x6C, 0xAF, 0xFE, - 0xE3, 0x7F, 0xEE, 0x22, 0xF5, 0xAF, 0xFC, 0x7B, 0xAF, 0xD2, 0xA4, 0xA8, 0xED, 0x7F, 0xE3, 0xDD, - 0x7E, 0x95, 0x25, 0x7E, 0x95, 0x91, 0xFF, 0x00, 0xC8, 0xAB, 0x0D, 0xFF, 0x00, 0x5E, 0xE1, 0xFF, - 0x00, 0xA4, 0xA3, 0xFA, 0x0B, 0x83, 0x7F, 0xE4, 0x9A, 0xCB, 0xBF, 0xEB, 0xCD, 0x2F, 0xFD, 0x22, - 0x21, 0x45, 0x14, 0x57, 0xA8, 0x7D, 0x20, 0x54, 0x73, 0x4C, 0xB1, 0xF0, 0x79, 0x3E, 0x95, 0x25, - 0x56, 0xBE, 0x43, 0x9F, 0x30, 0x7E, 0x35, 0xF3, 0x9C, 0x55, 0x8F, 0xC7, 0x60, 0x32, 0xAA, 0x98, - 0x8C, 0x14, 0x6F, 0x38, 0xDB, 0xA5, 0xEC, 0xBA, 0xBB, 0x79, 0x7F, 0xC1, 0x3E, 0x0B, 0xC4, 0xBC, - 0xEB, 0x39, 0xC9, 0x78, 0x6A, 0xB6, 0x3B, 0x27, 0x82, 0x95, 0x58, 0x5A, 0xF7, 0x57, 0xE5, 0x8D, - 0xFD, 0xE9, 0x5B, 0xAD, 0xBF, 0x05, 0x77, 0xD0, 0x9A, 0x19, 0x16, 0x45, 0xCA, 0xFE, 0x22, 0x9F, - 0x59, 0xF1, 0xBB, 0x23, 0x6E, 0x53, 0x56, 0xA2, 0xB8, 0x46, 0xE0, 0xFC, 0xA7, 0xDE, 0xBC, 0x2E, - 0x17, 0xE3, 0xAC, 0x26, 0x61, 0x49, 0x51, 0xC7, 0x4D, 0x42, 0xB2, 0xEF, 0xA4, 0x65, 0xE9, 0xD9, - 0xF7, 0x5F, 0x77, 0x97, 0xC5, 0x78, 0x73, 0xE3, 0x36, 0x5B, 0x9E, 0x61, 0xA3, 0x85, 0xCE, 0x2A, - 0xC6, 0x8E, 0x2D, 0x69, 0x77, 0xEE, 0xC2, 0x7D, 0x9C, 0x5B, 0xD1, 0x3E, 0xF1, 0x7D, 0x7E, 0x1D, - 0x34, 0x53, 0x51, 0x46, 0x68, 0xCD, 0x7D, 0xFF, 0x00, 0x32, 0x67, 0xEE, 0x0A, 0x51, 0x6A, 0xE9, - 0x85, 0x14, 0x64, 0x7A, 0xD4, 0x32, 0x5C, 0x46, 0xBD, 0x3E, 0x63, 0xED, 0x5C, 0x18, 0xFC, 0xD7, - 0x03, 0x97, 0xD3, 0xE7, 0xC5, 0x55, 0x50, 0x5E, 0x6F, 0x57, 0xE8, 0xB7, 0x7F, 0x23, 0xC5, 0xCE, - 0xF8, 0x9B, 0x26, 0xC8, 0xE8, 0xFB, 0x6C, 0xCB, 0x13, 0x1A, 0x51, 0xE9, 0x77, 0xAB, 0xF4, 0x8E, - 0xEF, 0xE4, 0x99, 0x0D, 0xF7, 0xFA, 0xE1, 0xF4, 0xA7, 0x69, 0xFF, 0x00, 0x79, 0xBE, 0x95, 0x14, - 0xD2, 0x19, 0x1F, 0x71, 0x18, 0xA9, 0x74, 0xF1, 0xF3, 0x31, 0xAF, 0xC5, 0x72, 0x7C, 0x5D, 0x2C, - 0x77, 0x1B, 0x47, 0x11, 0x87, 0xD6, 0x12, 0x94, 0x9A, 0xD2, 0xDA, 0x72, 0xB3, 0xF9, 0x13, 0x85, - 0xB3, 0x3C, 0x36, 0x71, 0xE2, 0xFC, 0x31, 0xF8, 0x1B, 0xBA, 0x55, 0x2A, 0x4E, 0x49, 0xDA, 0xDA, - 0x7B, 0x39, 0x6B, 0x6E, 0x9F, 0x32, 0xD5, 0x14, 0x53, 0x65, 0x71, 0x1A, 0xEE, 0x6A, 0xFD, 0xD6, - 0xBD, 0x7A, 0x54, 0x29, 0x4A, 0xAD, 0x59, 0x72, 0xC6, 0x3A, 0xB6, 0xFA, 0x23, 0xFB, 0x3B, 0x19, - 0x8C, 0xC3, 0xE0, 0xB0, 0xF3, 0xC4, 0xE2, 0x66, 0xA1, 0x4E, 0x0A, 0xF2, 0x6F, 0x44, 0x92, 0xEA, - 0xC7, 0x51, 0x51, 0x43, 0x3A, 0x48, 0xD8, 0xE8, 0x7D, 0xEA, 0x5A, 0xC3, 0x03, 0x98, 0x61, 0x31, - 0xF4, 0x7D, 0xB6, 0x16, 0xA2, 0x9C, 0x76, 0xBA, 0x38, 0xB2, 0x5C, 0xF7, 0x2D, 0xCE, 0xF0, 0xAB, - 0x17, 0x96, 0xD6, 0x55, 0x69, 0xDE, 0xD7, 0x5D, 0xD7, 0x47, 0xD5, 0x3F, 0x26, 0x15, 0x05, 0xC5, - 0xBE, 0xEF, 0x99, 0x38, 0x3E, 0x95, 0x3D, 0x15, 0x86, 0x6D, 0x93, 0xE0, 0xF3, 0x5C, 0x3B, 0xC3, - 0xE2, 0xA1, 0x75, 0xD1, 0xF5, 0x4F, 0xBA, 0x7D, 0x3F, 0xAB, 0x9C, 0x7C, 0x4D, 0xC2, 0xB9, 0x57, - 0x12, 0xE0, 0x1E, 0x0B, 0x32, 0xA7, 0xCD, 0x1D, 0xD3, 0xDA, 0x51, 0x7D, 0xE2, 0xFA, 0x3F, 0xC1, - 0xEC, 0xD3, 0x46, 0x73, 0x02, 0xAD, 0x82, 0x30, 0x6A, 0x58, 0x2E, 0x19, 0x78, 0x7F, 0x99, 0x6A, - 0xCC, 0xB1, 0x24, 0x83, 0xE6, 0x1C, 0xFA, 0xD5, 0x49, 0xA2, 0x68, 0xCF, 0x3C, 0x8F, 0x5A, 0xFC, - 0x63, 0x31, 0xC8, 0x33, 0xAE, 0x14, 0xC4, 0x3C, 0x66, 0x0A, 0x6E, 0x54, 0xBB, 0xAE, 0xDD, 0xA7, - 0x1F, 0xD7, 0x6F, 0x46, 0x7F, 0x25, 0xE7, 0xDC, 0x13, 0xC5, 0xDE, 0x1A, 0x63, 0x9E, 0x6B, 0x93, - 0xD6, 0x73, 0xC3, 0xA7, 0xF1, 0x47, 0xB7, 0x6A, 0xB0, 0xDA, 0xDE, 0x7A, 0xC7, 0xAD, 0xE2, 0xEC, - 0x8B, 0xA8, 0xCA, 0xCB, 0x95, 0x39, 0xA5, 0xAA, 0x10, 0x48, 0x63, 0x7C, 0xF6, 0xEE, 0x2A, 0xF2, - 0x90, 0xCA, 0x08, 0xE8, 0x6B, 0xF4, 0xEE, 0x14, 0xE2, 0x8A, 0x59, 0xE6, 0x1D, 0xDD, 0x72, 0xD5, - 0x8F, 0xC5, 0x1F, 0xD5, 0x79, 0x3F, 0xC3, 0xEE, 0x3F, 0xA2, 0x7C, 0x33, 0xF1, 0x1F, 0x0D, 0xC6, - 0x58, 0x09, 0x39, 0x45, 0x43, 0x13, 0x4F, 0xE3, 0x82, 0xDA, 0xCF, 0x69, 0x46, 0xFF, 0x00, 0x65, - 0xFD, 0xE9, 0xE8, 0xFA, 0x36, 0xB4, 0x51, 0x45, 0x7D, 0x61, 0xFA, 0x68, 0x55, 0x1B, 0xBF, 0xF8, - 0xF8, 0x6A, 0xBD, 0x54, 0x6E, 0xFF, 0x00, 0xE3, 0xE1, 0xBF, 0xCF, 0x6A, 0xFC, 0xDB, 0xC5, 0x0F, - 0xF9, 0x14, 0x52, 0xFF, 0x00, 0xAF, 0x8B, 0xFF, 0x00, 0x49, 0x91, 0xFC, 0xFF, 0x00, 0xF4, 0x8C, - 0xFF, 0x00, 0x92, 0x5B, 0x0D, 0xFF, 0x00, 0x5F, 0xE3, 0xFF, 0x00, 0xA6, 0xEA, 0x0E, 0xB6, 0x85, - 0x64, 0x42, 0x49, 0x23, 0x9E, 0xD5, 0x2F, 0xD9, 0x53, 0xFB, 0xCD, 0x45, 0x87, 0xFA, 0xA3, 0xFE, - 0xF5, 0x4F, 0x5B, 0x70, 0xCF, 0x0B, 0xE4, 0xD8, 0xAC, 0x9F, 0x0F, 0x5A, 0xB6, 0x1D, 0x4A, 0x72, - 0x8E, 0xAF, 0x5D, 0x75, 0xF5, 0x3A, 0xFC, 0x3B, 0xF0, 0xE7, 0x84, 0xF3, 0x1E, 0x14, 0xC0, 0xE2, - 0xF1, 0x78, 0x18, 0x4E, 0xAC, 0xE1, 0x79, 0x49, 0xDE, 0xED, 0xDD, 0xF9, 0x90, 0xAD, 0xAC, 0x63, - 0xA9, 0x63, 0xF8, 0xD4, 0x91, 0xA2, 0xA7, 0x0A, 0xB8, 0xA7, 0x51, 0x5F, 0x5D, 0x81, 0xC8, 0xF2, - 0xCC, 0x0C, 0xB9, 0xB0, 0xD4, 0x23, 0x19, 0x77, 0x4B, 0x5F, 0xBF, 0x73, 0xF4, 0xFC, 0x9F, 0x83, - 0x78, 0x77, 0x26, 0x9F, 0xB4, 0xCB, 0xF0, 0x54, 0xE9, 0xCF, 0xF9, 0x94, 0x57, 0x37, 0xFE, 0x04, - 0xEE, 0xFF, 0x00, 0x10, 0xA2, 0x8A, 0x2B, 0xD5, 0x3E, 0x98, 0x2A, 0x0D, 0x43, 0xFD, 0x5A, 0xFD, - 0x6A, 0x7A, 0x83, 0x50, 0xFF, 0x00, 0x56, 0xBF, 0x5A, 0xF9, 0x9E, 0x32, 0xFF, 0x00, 0x91, 0x06, - 0x2B, 0xFC, 0x3F, 0xAA, 0x3F, 0x3C, 0xF1, 0x63, 0xFE, 0x48, 0x8C, 0xC7, 0xFC, 0x1F, 0xFB, 0x72, - 0x20, 0xB4, 0xFF, 0x00, 0x8F, 0x85, 0xFF, 0x00, 0x3D, 0xAA, 0xF5, 0x51, 0xB4, 0xFF, 0x00, 0x8F, - 0x85, 0xFF, 0x00, 0x3D, 0xAA, 0xF5, 0x7C, 0xEF, 0x85, 0xFF, 0x00, 0xF2, 0x27, 0xAB, 0xFF, 0x00, - 0x5F, 0x1F, 0xFE, 0x93, 0x03, 0xE1, 0x3E, 0x8E, 0x7F, 0xF2, 0x4A, 0xE2, 0x3F, 0xEB, 0xFC, 0xBF, - 0xF4, 0xDD, 0x30, 0xA2, 0x8A, 0x2B, 0xF4, 0x83, 0xF7, 0xE0, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, - 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xAA, 0xBA, - 0x80, 0xF9, 0x94, 0xD5, 0xAA, 0x86, 0xF5, 0x0B, 0x28, 0x23, 0xF8, 0x7B, 0x57, 0xCA, 0xF1, 0xB6, - 0x16, 0xA6, 0x27, 0x21, 0xAF, 0x0A, 0x71, 0x72, 0x96, 0x8E, 0xCB, 0xCA, 0x49, 0xBF, 0xC2, 0xE7, - 0xE6, 0xBE, 0x2F, 0xE5, 0xB5, 0xF3, 0x0E, 0x0A, 0xC6, 0xD1, 0xA1, 0x07, 0x39, 0xA5, 0x19, 0x24, - 0xB5, 0x7E, 0xEC, 0xE2, 0xDE, 0x9E, 0x49, 0x36, 0x43, 0x62, 0x71, 0x36, 0x3D, 0x45, 0x5C, 0xAA, - 0x96, 0x71, 0xB7, 0x9B, 0xB8, 0x8C, 0x01, 0xEB, 0x56, 0xEB, 0xCC, 0xF0, 0xE6, 0x95, 0x7A, 0x79, - 0x2B, 0x55, 0xA2, 0xD7, 0xBE, 0xED, 0x7E, 0xD6, 0x5F, 0xAD, 0xCF, 0x9D, 0xF0, 0x0F, 0x0F, 0x8D, - 0xA1, 0xC2, 0x2E, 0x38, 0xAA, 0x6E, 0x0B, 0xDA, 0xCD, 0xC6, 0xEA, 0xD7, 0x8D, 0xA3, 0xAF, 0xA7, - 0x37, 0x30, 0x51, 0x45, 0x15, 0xF7, 0xA7, 0xED, 0x81, 0x45, 0x14, 0x50, 0x01, 0x59, 0xB5, 0xA5, - 0x54, 0x9E, 0x09, 0x04, 0x98, 0x0A, 0x4F, 0xA1, 0xAF, 0xCB, 0x7C, 0x4C, 0xCB, 0xF1, 0x58, 0xAA, - 0x78, 0x59, 0xD0, 0xA6, 0xE4, 0xA2, 0xE4, 0x9D, 0x95, 0xF7, 0xE5, 0xB6, 0xDE, 0x8C, 0xFE, 0x6E, - 0xFA, 0x43, 0x64, 0x79, 0x96, 0x63, 0x47, 0x2E, 0xAB, 0x82, 0xA1, 0x2A, 0x8A, 0x0E, 0xA2, 0x7C, - 0xA9, 0xC9, 0xA7, 0x2E, 0x4E, 0x5D, 0x16, 0xBA, 0xF2, 0xB2, 0xCD, 0xAF, 0xFC, 0x7B, 0xAF, 0xD2, - 0xA4, 0xA6, 0xC2, 0xBB, 0x23, 0x55, 0xF4, 0x14, 0xEA, 0xFD, 0x0B, 0x29, 0xA3, 0x3A, 0x39, 0x7D, - 0x0A, 0x55, 0x15, 0xA5, 0x18, 0x45, 0x3F, 0x54, 0x92, 0x67, 0xEE, 0xBC, 0x33, 0x85, 0xAD, 0x84, - 0xC8, 0xB0, 0x58, 0x6A, 0xCA, 0xD3, 0x85, 0x2A, 0x71, 0x6B, 0xB3, 0x50, 0x49, 0xAF, 0xBC, 0x28, - 0xA2, 0x8A, 0xF4, 0x0F, 0x70, 0x28, 0xEB, 0xC1, 0xA2, 0x8A, 0x4D, 0x26, 0xAC, 0xC5, 0x28, 0xA9, - 0x26, 0x9A, 0xBA, 0x65, 0x6B, 0x8B, 0x6F, 0xE2, 0x8F, 0xF2, 0xAA, 0xEC, 0xAC, 0xA7, 0x0C, 0x31, - 0x5A, 0x34, 0x8C, 0xA1, 0xB8, 0x60, 0x0F, 0xD6, 0xBF, 0x39, 0xCE, 0xFC, 0x38, 0xC0, 0xE3, 0x27, - 0x2A, 0xD8, 0x39, 0x7B, 0x29, 0x3E, 0x96, 0xBC, 0x7E, 0xED, 0xD7, 0xCB, 0x4F, 0x23, 0xF0, 0x2E, - 0x2F, 0xF0, 0x0F, 0x27, 0xCD, 0x6B, 0x4F, 0x15, 0x95, 0x54, 0xFA, 0xBD, 0x49, 0x6B, 0xCB, 0x6B, - 0xD3, 0xBF, 0x92, 0xD1, 0xC6, 0xFE, 0x4D, 0xAE, 0xD1, 0x33, 0xA8, 0xCD, 0x5E, 0xF2, 0x62, 0xFE, - 0xE0, 0xA3, 0xC9, 0x8B, 0xFB, 0x82, 0xBE, 0x53, 0xFE, 0x21, 0x86, 0x6B, 0xD2, 0xB4, 0x3E, 0xF9, - 0x7F, 0xF2, 0x27, 0xE6, 0xAF, 0xE8, 0xE9, 0xC4, 0xC9, 0xE9, 0x8B, 0xA3, 0x6F, 0x59, 0xFF, 0x00, - 0xF2, 0x05, 0x1A, 0x55, 0x56, 0x63, 0x85, 0x19, 0xAB, 0xBE, 0x4C, 0x5F, 0xDC, 0x14, 0xF5, 0x01, - 0x46, 0x00, 0xC5, 0x75, 0xE0, 0xFC, 0x2E, 0xC4, 0xBA, 0x9F, 0xED, 0x78, 0x84, 0xA3, 0xFD, 0xDB, - 0xB7, 0xF8, 0xA5, 0x6F, 0xC4, 0xF4, 0xF2, 0xAF, 0xA3, 0x86, 0x61, 0x3C, 0x42, 0x79, 0xA6, 0x36, - 0x2A, 0x9F, 0xFD, 0x3B, 0x4E, 0x52, 0x7F, 0x39, 0x28, 0xA5, 0xEB, 0x67, 0xE8, 0x56, 0x8E, 0xD5, - 0x8F, 0x2E, 0x71, 0xEC, 0x2A, 0xC4, 0x6A, 0xA8, 0xB8, 0x51, 0x4E, 0xA2, 0xBF, 0x48, 0xC9, 0xB8, - 0x67, 0x2C, 0xCA, 0x15, 0xF0, 0xB4, 0xFD, 0xEE, 0xB2, 0x7A, 0xCB, 0xEF, 0xE9, 0xF2, 0xB1, 0xFB, - 0xFF, 0x00, 0x09, 0xF8, 0x7B, 0xC3, 0xDC, 0x2C, 0x9C, 0xB2, 0xEA, 0x3F, 0xBC, 0x6A, 0xCE, 0x72, - 0x7C, 0xD3, 0x6B, 0xD7, 0xA2, 0xEE, 0xA2, 0x92, 0xF2, 0x0A, 0x8A, 0xF1, 0x0B, 0xC3, 0xC7, 0xF0, - 0xF3, 0x52, 0xD1, 0x5E, 0x8E, 0x65, 0x81, 0xA7, 0x8F, 0xC1, 0xD4, 0xC2, 0xD4, 0xF8, 0x66, 0xAD, - 0xFF, 0x00, 0x07, 0xE4, 0x7B, 0xDC, 0x41, 0x92, 0xD0, 0xCE, 0xF2, 0xAC, 0x46, 0x5B, 0x88, 0x76, - 0x85, 0x58, 0xB8, 0xB6, 0xB7, 0x57, 0xD9, 0xAF, 0x47, 0xA9, 0x9C, 0xA4, 0x86, 0x04, 0x75, 0x15, - 0x7A, 0x19, 0x04, 0x89, 0x91, 0xD7, 0xB8, 0xAA, 0xF7, 0x50, 0x95, 0x6D, 0xEA, 0x3E, 0x53, 0xD7, - 0xDA, 0xA2, 0x8D, 0xD9, 0x1B, 0x72, 0x9A, 0xFC, 0x3F, 0x27, 0xCC, 0xF1, 0xBC, 0x1F, 0x9A, 0x4F, - 0x0B, 0x8B, 0x8B, 0x74, 0xE5, 0xBA, 0xEE, 0xBA, 0x4E, 0x3F, 0xD6, 0xBB, 0x3D, 0x56, 0x9F, 0xC7, - 0x3C, 0x2D, 0xC4, 0x39, 0xBF, 0x85, 0x7C, 0x47, 0x57, 0x2E, 0xCD, 0x29, 0xB7, 0x87, 0x9B, 0xF7, - 0x92, 0xEA, 0xBE, 0xCD, 0x5A, 0x7D, 0x1E, 0x9D, 0x3A, 0xAF, 0x75, 0xD9, 0xAD, 0x34, 0x28, 0xA8, - 0x05, 0xD2, 0x63, 0x95, 0x6A, 0x3E, 0xD4, 0x9F, 0xDD, 0x6A, 0xFD, 0x65, 0x71, 0x8E, 0x40, 0xD5, - 0xFE, 0xB5, 0x1F, 0xC7, 0xFC, 0x8F, 0xE9, 0xC8, 0xF8, 0xAD, 0xC1, 0x32, 0x8A, 0x97, 0xF6, 0x8C, - 0x35, 0xF5, 0xFC, 0xAC, 0x4F, 0x4C, 0x98, 0xA0, 0x8C, 0xEF, 0xE9, 0x50, 0x3D, 0xD1, 0xFE, 0x15, - 0xFC, 0x4D, 0x40, 0xEC, 0xCE, 0xD9, 0x63, 0x9A, 0xF9, 0x8C, 0xFB, 0xC4, 0x5C, 0xBA, 0x9D, 0x09, - 0x52, 0xC1, 0x2F, 0x6B, 0x26, 0xAD, 0xAA, 0x6A, 0x3F, 0x3B, 0xD9, 0xBF, 0x4D, 0xBC, 0xCF, 0xCE, - 0xF8, 0xD3, 0xC7, 0x8C, 0x82, 0x86, 0x0E, 0x78, 0x6C, 0x9E, 0x3F, 0x59, 0xA9, 0x34, 0xD5, 0xE5, - 0x16, 0xA9, 0xA4, 0xF4, 0x77, 0x52, 0x49, 0xCB, 0xD1, 0x2B, 0x3F, 0xE6, 0x10, 0xE3, 0x3C, 0x55, - 0xDB, 0x3C, 0xFD, 0x9C, 0x66, 0xAB, 0xC1, 0x0B, 0x49, 0xC9, 0xE1, 0x7D, 0x6A, 0xE2, 0x80, 0xAB, - 0x81, 0xD0, 0x57, 0x97, 0xE1, 0xBE, 0x47, 0x8C, 0xA7, 0x89, 0x9E, 0x63, 0x56, 0x3C, 0xB0, 0x71, - 0xB4, 0x7A, 0x73, 0x5D, 0xA7, 0x74, 0xBB, 0x2B, 0x7C, 0xFA, 0x1F, 0x37, 0xE0, 0x0F, 0x07, 0xE6, - 0xB4, 0x31, 0xF5, 0x73, 0xEC, 0x55, 0x37, 0x4E, 0x8C, 0xA0, 0xE3, 0x04, 0xD5, 0xB9, 0xF9, 0x9A, - 0x77, 0x4B, 0xF9, 0x55, 0xB4, 0x7D, 0x6E, 0xAD, 0xB0, 0xB4, 0x51, 0x45, 0x7E, 0xC0, 0x7F, 0x55, - 0x05, 0x52, 0xBC, 0x18, 0xB8, 0x6A, 0xBB, 0x54, 0xEF, 0x7F, 0xD7, 0x9F, 0xA5, 0x7E, 0x77, 0xE2, - 0x6C, 0x14, 0xB2, 0x58, 0x3E, 0xD5, 0x17, 0xE5, 0x23, 0xF0, 0x7F, 0xA4, 0x3D, 0x25, 0x3E, 0x12, - 0xA5, 0x27, 0xF6, 0x6B, 0x41, 0xFF, 0x00, 0xE4, 0xB3, 0x5F, 0xA9, 0x26, 0x9E, 0x7E, 0x56, 0x5F, - 0x7C, 0xD5, 0x8A, 0xAD, 0xA7, 0xFF, 0x00, 0x17, 0xE1, 0x56, 0x6B, 0xD7, 0xE0, 0x5A, 0x92, 0x9F, - 0x0F, 0x61, 0x9C, 0xBF, 0xBC, 0xBE, 0xE9, 0x49, 0x1F, 0x53, 0xE0, 0xD5, 0x69, 0xD5, 0xE0, 0x4C, - 0x04, 0xA6, 0xF5, 0x4A, 0x6B, 0xE4, 0xAA, 0x4D, 0x2F, 0xC1, 0x05, 0x14, 0x51, 0x5F, 0x5A, 0x7E, - 0x9E, 0x14, 0x51, 0x45, 0x00, 0x15, 0x06, 0xA1, 0xFE, 0xA9, 0x7E, 0xB5, 0x3D, 0x36, 0x44, 0x12, - 0x2E, 0xD6, 0xAF, 0x1F, 0x88, 0x30, 0x15, 0x73, 0x0C, 0xAE, 0xBE, 0x16, 0x97, 0xC5, 0x25, 0xA5, - 0xFB, 0xEE, 0x7C, 0xAF, 0x1C, 0x64, 0xB8, 0x8C, 0xF3, 0x86, 0xF1, 0x99, 0x76, 0x19, 0xA5, 0x52, - 0xA4, 0x2D, 0x1B, 0xED, 0x7B, 0xA6, 0x97, 0xCE, 0xD6, 0x29, 0xDA, 0x7F, 0xC7, 0xC2, 0xD5, 0xEA, - 0x8A, 0x18, 0x16, 0x36, 0xDD, 0x9C, 0x9A, 0x96, 0xBC, 0x6E, 0x08, 0xC9, 0x71, 0x59, 0x46, 0x59, - 0x2A, 0x38, 0xA4, 0x94, 0xE5, 0x27, 0x2B, 0x27, 0x7B, 0x68, 0x97, 0xE8, 0x7C, 0x9F, 0x83, 0xDC, - 0x23, 0x99, 0x70, 0xBF, 0x0F, 0x4F, 0x09, 0x99, 0x24, 0xAA, 0xCE, 0xA4, 0xA7, 0x64, 0xD3, 0xB2, - 0x71, 0x8C, 0x52, 0x6D, 0x69, 0x7F, 0x76, 0xFA, 0x77, 0x0A, 0x28, 0xA2, 0xBE, 0xC4, 0xFD, 0x58, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x2A, 0xB5, 0xC5, 0xB9, 0xDD, 0xBA, 0x3F, 0xCA, - 0xAC, 0xD1, 0x5E, 0x3E, 0x75, 0x91, 0x60, 0x73, 0x8A, 0x1E, 0xC7, 0x15, 0x1D, 0xB6, 0x6B, 0x46, - 0xBD, 0x1F, 0xE9, 0xB1, 0xF2, 0x9C, 0x5D, 0xC1, 0x79, 0x3F, 0x15, 0xE0, 0x96, 0x17, 0x32, 0x85, - 0xED, 0xAC, 0x64, 0xB4, 0x94, 0x5F, 0xF7, 0x5E, 0xBB, 0xF5, 0x4D, 0x34, 0xFA, 0xAD, 0x11, 0x47, - 0xC9, 0x97, 0xFB, 0x86, 0x8F, 0x26, 0x5F, 0xEE, 0x1A, 0xBD, 0x45, 0x7C, 0x67, 0xFC, 0x42, 0xFC, - 0xAB, 0xFE, 0x7F, 0x54, 0xFB, 0xE3, 0xFF, 0x00, 0xC8, 0x9F, 0x93, 0x7F, 0xC4, 0xB8, 0xF0, 0xD7, - 0xFD, 0x05, 0x56, 0xFB, 0xE1, 0xFF, 0x00, 0xC8, 0x14, 0xD6, 0xDA, 0x53, 0xD4, 0x01, 0xF5, 0x35, - 0x34, 0x56, 0xC8, 0xBC, 0xB7, 0xCC, 0x6A, 0x6A, 0x2B, 0xD9, 0xCB, 0x78, 0x0F, 0x24, 0xC0, 0xCD, - 0x54, 0xE4, 0x73, 0x92, 0xDB, 0x9D, 0xDF, 0xF0, 0x49, 0x2F, 0xBD, 0x1F, 0x57, 0xC3, 0xFE, 0x0A, - 0xF0, 0x86, 0x4D, 0x5A, 0x35, 0xD5, 0x17, 0x5A, 0x6B, 0x67, 0x55, 0xF3, 0x25, 0xFF, 0x00, 0x6E, - 0xA5, 0x18, 0xFD, 0xE9, 0x85, 0x14, 0x51, 0x5F, 0x64, 0x7E, 0xB2, 0x95, 0x95, 0x90, 0x51, 0x45, - 0x14, 0x00, 0x55, 0x5B, 0xE4, 0x6F, 0x33, 0x70, 0x1C, 0x62, 0xAD, 0x51, 0x5E, 0x27, 0x10, 0x64, - 0x94, 0xB3, 0x9C, 0x0B, 0xC2, 0xD4, 0x93, 0x8E, 0xA9, 0xA6, 0xBA, 0x35, 0xE4, 0x7C, 0x7F, 0x1C, - 0x70, 0x7E, 0x1B, 0x8B, 0x72, 0x79, 0x65, 0xB5, 0xEA, 0x38, 0x6A, 0xA4, 0xA4, 0x95, 0xEC, 0xD7, - 0x97, 0x55, 0x66, 0xD6, 0xE8, 0x82, 0xC5, 0x19, 0x55, 0x89, 0x18, 0xCF, 0x4A, 0x9E, 0x8A, 0x2B, - 0xA3, 0x27, 0xCA, 0xE9, 0xE5, 0x78, 0x0A, 0x78, 0x3A, 0x6E, 0xEA, 0x1D, 0x5F, 0x5B, 0xB6, 0xDF, - 0xE2, 0xCE, 0xFE, 0x14, 0xE1, 0xDC, 0x3F, 0x0E, 0x64, 0xB4, 0x32, 0xAC, 0x3C, 0x9C, 0xA3, 0x4D, - 0x3D, 0x5E, 0xED, 0xB6, 0xE4, 0xDF, 0xCD, 0xB7, 0xA7, 0x40, 0xA2, 0x8A, 0x2B, 0xD3, 0x3E, 0x84, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8C, 0xE3, 0xAD, 0x26, 0xD2, 0x57, 0x62, 0x94, 0x94, 0x55, 0xDB, 0xD0, 0x28, 0xA2, - 0x8A, 0x63, 0xDC, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, - 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x98, 0xD3, 0x44, - 0x0E, 0x0B, 0x8A, 0xE6, 0xC4, 0xE3, 0x30, 0xD8, 0x58, 0xA9, 0x62, 0x2A, 0x46, 0x09, 0xFF, 0x00, - 0x33, 0x4B, 0xF3, 0x3C, 0xFC, 0xC3, 0x36, 0xCB, 0xF2, 0xE8, 0x29, 0xE3, 0xAB, 0xC2, 0x92, 0x7B, - 0x39, 0xC9, 0x45, 0x3F, 0x4B, 0xB4, 0x3E, 0x8A, 0x62, 0xCB, 0x1B, 0x1C, 0x2B, 0x0C, 0xD3, 0xEA, - 0xB0, 0xF8, 0xAC, 0x3E, 0x26, 0x1C, 0xF4, 0x26, 0xA7, 0x1E, 0xE9, 0xA6, 0xBF, 0x02, 0xF0, 0x39, - 0x96, 0x0B, 0x30, 0xA5, 0xED, 0x70, 0x75, 0xA3, 0x52, 0x1B, 0x5E, 0x32, 0x52, 0x57, 0xF5, 0x4D, - 0xA0, 0xA2, 0x8A, 0x2B, 0x73, 0xB4, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xAA, 0x6D, 0x71, 0x27, 0x99, - 0x90, 0x78, 0xF4, 0xAB, 0x50, 0xB8, 0x78, 0xC3, 0x0A, 0xF9, 0xAC, 0x97, 0x8A, 0xF2, 0xEC, 0xDF, - 0x11, 0x53, 0x0F, 0x87, 0x6F, 0x9A, 0x3D, 0xD5, 0xAE, 0xB6, 0xBA, 0xFE, 0x93, 0x3F, 0x3D, 0xE1, - 0x1F, 0x13, 0x32, 0x1E, 0x29, 0xC7, 0x57, 0xC1, 0x60, 0x1C, 0x95, 0x4A, 0x7A, 0xFB, 0xCA, 0xDC, - 0xD1, 0x4E, 0xDC, 0xD1, 0xD5, 0xE9, 0x7B, 0x6F, 0x67, 0xAA, 0xD0, 0x75, 0x14, 0x51, 0x5F, 0x4A, - 0x7E, 0x84, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x52, 0x48, 0xC1, 0x14, 0xB1, 0xED, 0x54, 0x66, 0x95, 0xA4, 0x6C, 0x93, 0xC7, - 0x61, 0x5F, 0x29, 0xC4, 0xDC, 0x59, 0x85, 0xC8, 0xA1, 0x18, 0xCA, 0x3C, 0xF5, 0x25, 0xB4, 0x53, - 0xB6, 0x9D, 0xDB, 0xD6, 0xCB, 0xE5, 0xA9, 0xF9, 0xA7, 0x88, 0x5E, 0x27, 0x65, 0xBC, 0x1B, 0x4E, - 0x10, 0xA9, 0x0F, 0x6B, 0x5E, 0x7A, 0xC6, 0x9A, 0x76, 0xD3, 0xF9, 0xA4, 0xEC, 0xEC, 0xBA, 0x2D, - 0x1B, 0x6F, 0x65, 0xA3, 0x6A, 0xFE, 0x68, 0xAC, 0xDA, 0x55, 0x62, 0x3A, 0x12, 0x3E, 0x95, 0xF1, - 0xB0, 0xF1, 0x56, 0x2E, 0x4B, 0x9F, 0x09, 0x65, 0xE5, 0x3B, 0xFF, 0x00, 0xED, 0xA8, 0xFC, 0x9A, - 0x8F, 0xD2, 0x5A, 0x0E, 0x69, 0x55, 0xCA, 0xDA, 0x8F, 0x5B, 0x55, 0xBB, 0xFB, 0x9D, 0x35, 0xF9, - 0x9A, 0x34, 0x55, 0x48, 0x6E, 0x59, 0x78, 0x7F, 0x98, 0x55, 0x98, 0xDD, 0x5D, 0x72, 0xA7, 0x35, - 0xF7, 0x79, 0x1F, 0x14, 0x65, 0xB9, 0xC4, 0x7F, 0xD9, 0xE7, 0x69, 0xF5, 0x8B, 0xD2, 0x5F, 0xF0, - 0x7D, 0x55, 0xCF, 0xDA, 0x78, 0x3F, 0xC4, 0x6E, 0x1F, 0xE2, 0xB8, 0x5B, 0x03, 0x56, 0xD5, 0x56, - 0xF4, 0xE5, 0xA4, 0xD7, 0xCA, 0xED, 0x49, 0x79, 0xC5, 0xBF, 0x3B, 0x0E, 0xA0, 0x90, 0x06, 0x4D, - 0x15, 0x5A, 0xF9, 0xCE, 0x42, 0x0F, 0xA9, 0xAE, 0x8C, 0xFF, 0x00, 0x39, 0xA7, 0x93, 0xE5, 0xF3, - 0xC5, 0xCD, 0x5D, 0xAD, 0x12, 0xEE, 0xDE, 0xCB, 0xFC, 0xFC, 0x8E, 0xEE, 0x37, 0xE2, 0xBA, 0x1C, - 0x2D, 0x91, 0xD5, 0xCC, 0xEA, 0xC7, 0x99, 0xC6, 0xCA, 0x31, 0xDB, 0x9A, 0x4F, 0x44, 0xAF, 0xD1, - 0x75, 0x7E, 0x49, 0x8B, 0x2D, 0xC8, 0x07, 0x08, 0x33, 0xEE, 0x6A, 0x3F, 0xB5, 0x49, 0xE8, 0xBF, - 0x95, 0x47, 0x1A, 0xB3, 0xB6, 0x14, 0x54, 0xDF, 0x65, 0x7F, 0xEF, 0x2D, 0x7E, 0x3F, 0x47, 0x38, - 0xE3, 0x1C, 0xE6, 0x52, 0xC4, 0x61, 0x1C, 0xB9, 0x2F, 0xF6, 0x52, 0x51, 0x5E, 0x4A, 0xFB, 0xFD, - 0xED, 0x9F, 0xCA, 0xF8, 0x5E, 0x2B, 0xF1, 0x5F, 0x8B, 0x27, 0x53, 0x1D, 0x95, 0xB9, 0xFB, 0x2B, - 0xDA, 0xD4, 0xD4, 0x63, 0x05, 0xE4, 0x9C, 0xB7, 0xB7, 0x5D, 0x5B, 0xEE, 0x20, 0xBA, 0x7E, 0xEA, - 0xB5, 0x3C, 0x33, 0x2C, 0x9C, 0x74, 0x6F, 0x4A, 0xAF, 0x25, 0xBB, 0xAA, 0x96, 0xC8, 0x38, 0xA8, - 0x94, 0x95, 0x60, 0x47, 0x51, 0x5A, 0xD1, 0xE2, 0x9E, 0x24, 0xC8, 0xF1, 0x90, 0x8E, 0x69, 0x77, - 0x07, 0xBA, 0x95, 0xB6, 0xEF, 0x16, 0xBA, 0xAF, 0x5B, 0x77, 0x37, 0xC2, 0x78, 0x91, 0xC7, 0xFC, - 0x1D, 0x9B, 0x52, 0xA7, 0xC4, 0x6A, 0x53, 0xA7, 0x3D, 0x5C, 0x66, 0xA3, 0x77, 0x1B, 0xD9, 0xB8, - 0x49, 0x75, 0x5D, 0xAE, 0xD7, 0x74, 0x68, 0xD1, 0x48, 0xA7, 0x2A, 0x0F, 0xA8, 0xA5, 0xAF, 0xDC, - 0xE1, 0x38, 0xCE, 0x2A, 0x51, 0xD9, 0x9F, 0xD9, 0x54, 0xAA, 0xC2, 0xAD, 0x38, 0xD4, 0x83, 0xBC, - 0x5A, 0x4D, 0x7A, 0x30, 0xA2, 0xAA, 0xC9, 0x72, 0xDB, 0xFE, 0x50, 0x31, 0x53, 0x5B, 0xCA, 0x24, - 0x5F, 0x42, 0x3A, 0x8A, 0xF9, 0xBC, 0xB7, 0x8B, 0xF2, 0x8C, 0xC7, 0x18, 0xF0, 0x94, 0x2A, 0x7B, - 0xFD, 0x2E, 0xAC, 0xA5, 0x6E, 0xCF, 0xAF, 0xE0, 0x7E, 0x7F, 0x90, 0x78, 0xA5, 0xC2, 0xF9, 0xEE, - 0x69, 0x2C, 0xAF, 0x07, 0x59, 0xFB, 0x55, 0x7B, 0x5D, 0x59, 0x4E, 0xDB, 0xF2, 0x3E, 0xBD, 0xF5, - 0xB3, 0x6B, 0x54, 0x89, 0x2A, 0x95, 0xD4, 0x85, 0xE4, 0x38, 0x3F, 0x28, 0xE9, 0x53, 0x5E, 0x4B, - 0xB5, 0x76, 0x29, 0xE4, 0xF5, 0xAA, 0x95, 0xF0, 0x1E, 0x23, 0x71, 0x1C, 0x6A, 0x49, 0x65, 0x78, - 0x77, 0xA4, 0x5D, 0xE6, 0xD7, 0x7E, 0x91, 0xF9, 0x6E, 0xFC, 0xED, 0xD8, 0xFC, 0x47, 0xC7, 0xCE, - 0x3E, 0x86, 0x22, 0xA2, 0xE1, 0xCC, 0x0C, 0xF4, 0x83, 0xE6, 0xAA, 0xD3, 0xD1, 0xBB, 0x69, 0x0F, - 0x3B, 0x5E, 0xF2, 0xF3, 0xB2, 0xDD, 0x32, 0xD5, 0x8B, 0x12, 0xAC, 0xA4, 0xF4, 0xE9, 0x56, 0x2A, - 0xBE, 0x9F, 0xD1, 0xBF, 0x0A, 0x96, 0xE2, 0x4F, 0x2E, 0x3C, 0xF7, 0xED, 0x5F, 0x65, 0xC2, 0x98, - 0xF8, 0x61, 0xF8, 0x5E, 0x8E, 0x2B, 0x15, 0x3F, 0x76, 0x2A, 0x57, 0x6F, 0xB2, 0x93, 0x4B, 0xFC, - 0x91, 0xFA, 0xC7, 0x86, 0x79, 0xD5, 0x2C, 0x0F, 0x87, 0x38, 0x5C, 0xC7, 0x31, 0xAB, 0xEE, 0x53, - 0x84, 0xDC, 0xA4, 0xF5, 0xF7, 0x63, 0x39, 0xA4, 0xBC, 0xF4, 0x49, 0x25, 0xE8, 0x90, 0xE6, 0x65, - 0x5F, 0xBC, 0xC0, 0x7D, 0x4D, 0x27, 0x99, 0x1F, 0xF7, 0xD7, 0xF3, 0xAA, 0x0C, 0x4B, 0x36, 0x49, - 0xC9, 0xA4, 0xAF, 0x8C, 0xAD, 0xE2, 0x9D, 0x7F, 0x68, 0xFD, 0x96, 0x19, 0x72, 0xF4, 0xBB, 0x77, - 0xF9, 0xD8, 0xFC, 0x9B, 0x17, 0xF4, 0x92, 0xC6, 0x7B, 0x79, 0x7D, 0x5B, 0x01, 0x1F, 0x67, 0x7D, - 0x39, 0xA4, 0xF9, 0xAD, 0xE7, 0x65, 0x6B, 0xF9, 0x2D, 0xBB, 0xB3, 0x43, 0xCC, 0x8F, 0xFB, 0xEB, - 0xF9, 0xD2, 0xAB, 0x2B, 0x7D, 0xD6, 0x07, 0xE8, 0x6B, 0x3A, 0xA4, 0xB7, 0xDD, 0xE7, 0x2E, 0xDF, - 0x5A, 0xDF, 0x2F, 0xF1, 0x2F, 0x15, 0x88, 0xC5, 0xD3, 0xA3, 0x2C, 0x32, 0xB4, 0x9A, 0x5A, 0x37, - 0x7D, 0x7B, 0x1D, 0xB9, 0x1F, 0xD2, 0x13, 0x31, 0xC7, 0xE6, 0x54, 0x30, 0x95, 0x32, 0xF8, 0xB5, - 0x52, 0x4A, 0x3E, 0xEC, 0xA4, 0xE5, 0xAB, 0xB6, 0x89, 0xAD, 0x6D, 0xD8, 0xBD, 0x45, 0x14, 0xD9, - 0x18, 0x24, 0x65, 0x8F, 0x6A, 0xFD, 0x6E, 0xB5, 0x68, 0x51, 0xA5, 0x2A, 0xB5, 0x1D, 0xA3, 0x14, - 0xDB, 0x7D, 0x92, 0xD5, 0x9F, 0xD3, 0xD8, 0xAC, 0x55, 0x1C, 0x26, 0x1E, 0xA6, 0x22, 0xBC, 0xB9, - 0x61, 0x04, 0xE5, 0x27, 0xD9, 0x25, 0x76, 0xFE, 0x48, 0x73, 0x10, 0x39, 0x27, 0x15, 0x1B, 0x4F, - 0x10, 0xFE, 0x2C, 0xFD, 0x2A, 0x9C, 0x92, 0x33, 0xB6, 0x58, 0xFE, 0x14, 0x8A, 0x09, 0xE8, 0x09, - 0xFA, 0x57, 0xE3, 0xF9, 0x87, 0x89, 0xB8, 0x9A, 0x95, 0x5D, 0x3C, 0xBA, 0x8A, 0xB7, 0x47, 0x2B, - 0xB6, 0xFE, 0x4A, 0xD6, 0xFC, 0x4F, 0xE5, 0x7C, 0xF7, 0xE9, 0x0F, 0x98, 0x57, 0xC4, 0xBA, 0x19, - 0x0E, 0x11, 0x72, 0xDE, 0xC9, 0xCD, 0x39, 0x4A, 0x5E, 0x6A, 0x31, 0x6A, 0xDE, 0x97, 0x65, 0xBF, - 0xB4, 0xC5, 0xEF, 0xF9, 0x53, 0xE3, 0x95, 0x1C, 0xE1, 0x4F, 0x3E, 0x86, 0xA9, 0x79, 0x6F, 0xFD, - 0xC6, 0xFC, 0xA9, 0xAA, 0x48, 0x39, 0x15, 0xC7, 0x4B, 0xC4, 0x5C, 0xEF, 0x0F, 0x5A, 0x3F, 0x5C, - 0xA2, 0xB9, 0x3A, 0xAE, 0x57, 0x17, 0x6F, 0x26, 0xD9, 0xE4, 0xE1, 0xFC, 0x79, 0xE2, 0xFC, 0x06, - 0x2E, 0x9B, 0xCD, 0x70, 0xB1, 0xF6, 0x4D, 0xEA, 0xB9, 0x25, 0x09, 0x35, 0xD7, 0x95, 0xB7, 0xBA, - 0xF4, 0x68, 0xD2, 0xA2, 0x92, 0x33, 0xBA, 0x35, 0x3E, 0xA2, 0x96, 0xBF, 0x6C, 0xA5, 0x52, 0x35, - 0x69, 0xC6, 0xA4, 0x76, 0x6A, 0xFF, 0x00, 0x79, 0xFD, 0x81, 0x87, 0xAF, 0x0C, 0x45, 0x18, 0x56, - 0xA7, 0xF0, 0xC9, 0x26, 0xBD, 0x1A, 0xBA, 0x21, 0xBC, 0x72, 0xB1, 0x60, 0x7F, 0x11, 0xAA, 0x75, - 0x63, 0x50, 0x3F, 0x3A, 0x8F, 0x6A, 0x6D, 0x88, 0xCC, 0xD9, 0xF4, 0x15, 0xF8, 0x5F, 0x15, 0x46, - 0xA6, 0x73, 0xC5, 0x6B, 0x02, 0xA5, 0x64, 0x9C, 0x60, 0xBA, 0xDB, 0x4B, 0xB7, 0x6F, 0x9B, 0x3F, - 0x8C, 0x7C, 0x4A, 0xA7, 0x88, 0xE2, 0xCF, 0x13, 0x16, 0x4C, 0xAA, 0x72, 0xC5, 0x38, 0x52, 0x8B, - 0xDF, 0x95, 0x59, 0x4A, 0x6E, 0xD7, 0x57, 0x77, 0x6F, 0xAA, 0xBD, 0x96, 0xA4, 0x3D, 0x2A, 0xF5, - 0xAC, 0x9B, 0xE3, 0xE7, 0xA8, 0xE0, 0xD4, 0x17, 0xC9, 0x87, 0x0E, 0x3F, 0x8B, 0xAD, 0x25, 0x9B, - 0xED, 0x9B, 0x07, 0xA3, 0x71, 0x47, 0x0F, 0xD5, 0xAD, 0xC3, 0x5C, 0x4C, 0xF0, 0x15, 0xA5, 0xEE, - 0x49, 0xF2, 0xBE, 0xCE, 0xFF, 0x00, 0x04, 0xAD, 0xF7, 0x7A, 0x5D, 0x8F, 0x81, 0xB1, 0x18, 0xBF, - 0x0F, 0xBC, 0x42, 0x96, 0x4B, 0x8B, 0xA9, 0x7A, 0x53, 0x97, 0xB3, 0x93, 0xD9, 0x49, 0x4B, 0x5A, - 0x73, 0xB6, 0xB6, 0xD5, 0xAE, 0xAE, 0xC9, 0xC9, 0x5C, 0xB9, 0x45, 0x14, 0x57, 0xEF, 0x07, 0xF6, - 0x98, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, - 0x00, 0xC9, 0x25, 0x44, 0x38, 0x63, 0x4E, 0x52, 0x0A, 0xE4, 0x1A, 0xAD, 0x75, 0x13, 0x99, 0x37, - 0x01, 0x90, 0x6A, 0x6B, 0x74, 0x29, 0x10, 0x53, 0x5F, 0x25, 0x95, 0xE7, 0x19, 0xAE, 0x27, 0x39, - 0xAF, 0x84, 0xC4, 0x61, 0xF9, 0x68, 0xC2, 0xFC, 0xB2, 0xB3, 0xEF, 0xA6, 0xBB, 0x3B, 0xAD, 0x74, - 0xD8, 0xFC, 0xBF, 0x86, 0xF8, 0xAB, 0x89, 0x71, 0xFC, 0x57, 0x8D, 0xCB, 0x31, 0xD8, 0x1F, 0x67, - 0x85, 0xA7, 0x7E, 0x49, 0xDA, 0x4A, 0xF6, 0x69, 0x2D, 0x5E, 0x92, 0xE6, 0x5A, 0xE9, 0xB1, 0x5A, - 0xEA, 0x32, 0x92, 0x64, 0x7D, 0xD6, 0xA4, 0xB6, 0x94, 0xC6, 0xFC, 0xFD, 0xD3, 0xD6, 0xAE, 0x48, - 0xA1, 0xD0, 0xA9, 0xEF, 0x54, 0x18, 0x15, 0x62, 0xA7, 0xB1, 0xAF, 0xCD, 0xB8, 0xB3, 0x2A, 0xAD, - 0xC3, 0x79, 0xB5, 0x3C, 0xC7, 0x02, 0xED, 0x19, 0x3B, 0xAF, 0x27, 0xD6, 0x3E, 0x8F, 0xF2, 0xBA, - 0xE8, 0x7F, 0x3F, 0xF8, 0x99, 0xC3, 0x58, 0xAE, 0x01, 0xE2, 0x7A, 0x39, 0xF6, 0x4F, 0x2E, 0x5A, - 0x75, 0x24, 0xE5, 0x1F, 0xEE, 0xCF, 0x79, 0x41, 0xF7, 0x8C, 0x93, 0xD1, 0x76, 0x6E, 0x3D, 0x2E, - 0x68, 0xD1, 0x50, 0xD9, 0xB1, 0x68, 0x79, 0xEC, 0x71, 0x53, 0x57, 0xED, 0x19, 0x5E, 0x3A, 0x38, - 0xFC, 0x15, 0x2C, 0x54, 0x55, 0x94, 0xD2, 0x76, 0xED, 0xE5, 0xF2, 0x3F, 0xAD, 0xB8, 0x77, 0x39, - 0xA7, 0x9D, 0xE5, 0x18, 0x6C, 0xCA, 0x9A, 0xB2, 0xAB, 0x05, 0x2B, 0x76, 0x6D, 0x6A, 0xBE, 0x4E, - 0xE8, 0x28, 0xA2, 0x8A, 0xEE, 0x3D, 0xA0, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, - 0x8A, 0x28, 0x02, 0xA5, 0xF4, 0x9B, 0x9F, 0x60, 0xE8, 0xBD, 0x69, 0x96, 0xF1, 0xF9, 0x92, 0x63, - 0xB0, 0xEB, 0x4D, 0x9B, 0xFD, 0x73, 0x7D, 0x4D, 0x5A, 0xB1, 0x03, 0xC9, 0xCF, 0xBD, 0x7E, 0x05, - 0x97, 0xE1, 0xFF, 0x00, 0xD6, 0x1E, 0x2C, 0x9F, 0xD6, 0xDD, 0xE2, 0x9C, 0x9B, 0x5E, 0x51, 0xD1, - 0x47, 0xD3, 0x6B, 0xFC, 0xCF, 0xE2, 0x4C, 0x8F, 0x01, 0xFE, 0xBD, 0xF8, 0x9B, 0x5B, 0xFB, 0x4D, - 0xDE, 0x11, 0x9C, 0xE4, 0xE3, 0xDE, 0x14, 0xDD, 0xA3, 0x0F, 0x4D, 0x93, 0xEE, 0xAF, 0xD5, 0x8F, - 0xF2, 0xA3, 0xFE, 0xE2, 0xFE, 0x54, 0xD7, 0xB7, 0x8D, 0xBA, 0x0D, 0xBF, 0x4A, 0x96, 0x8A, 0xFD, - 0xA2, 0xB6, 0x47, 0x95, 0xD6, 0x83, 0x85, 0x4C, 0x34, 0x1A, 0xFF, 0x00, 0x0A, 0xFF, 0x00, 0x2D, - 0x0F, 0xEB, 0x6C, 0x57, 0x06, 0xF0, 0xDE, 0x2A, 0x8B, 0xA3, 0x5B, 0x01, 0x49, 0xC5, 0xFF, 0x00, - 0xD3, 0xB8, 0xAF, 0xB9, 0xA5, 0x75, 0xEA, 0xB5, 0x28, 0xCD, 0x0B, 0xC7, 0xEE, 0x3D, 0x45, 0x49, - 0x62, 0x1B, 0xCC, 0x27, 0xB6, 0x39, 0xAB, 0x54, 0x57, 0xC9, 0xE0, 0xFC, 0x3F, 0xC2, 0xE0, 0xB3, - 0x68, 0x63, 0xB0, 0xF5, 0x5A, 0x84, 0x5D, 0xF9, 0x77, 0xD7, 0xB7, 0x35, 0xF6, 0xF9, 0x5F, 0xCC, - 0xFC, 0xC7, 0x2B, 0xF0, 0x37, 0x2D, 0xCA, 0x78, 0x9A, 0x8E, 0x71, 0x81, 0xC4, 0xCA, 0x34, 0xA9, - 0xBE, 0x65, 0x4E, 0xD7, 0x77, 0xED, 0xCF, 0x7B, 0xF2, 0xF9, 0x34, 0xDD, 0xB4, 0xB8, 0x55, 0x3B, - 0xDF, 0xF5, 0xFF, 0x00, 0x85, 0x5C, 0xAA, 0x37, 0x2D, 0xBA, 0x66, 0x3F, 0x85, 0x65, 0xE2, 0x6D, - 0x68, 0x47, 0x28, 0x85, 0x36, 0xFD, 0xE9, 0x4D, 0x5B, 0xE4, 0x9D, 0xFF, 0x00, 0x33, 0x9B, 0xE9, - 0x11, 0x8B, 0xA3, 0x4F, 0x85, 0xA8, 0xD0, 0x93, 0xF7, 0xE7, 0x56, 0x2D, 0x2E, 0xEA, 0x31, 0x95, - 0xFE, 0xEB, 0xAF, 0xBC, 0x97, 0x4F, 0xFE, 0x2F, 0xC2, 0xA6, 0x69, 0x63, 0x5E, 0xAE, 0x2A, 0x86, - 0x4D, 0x15, 0xF1, 0x19, 0x4F, 0x1F, 0x57, 0xCA, 0xF2, 0xBA, 0x58, 0x1C, 0x3D, 0x04, 0xE5, 0x1B, - 0xFB, 0xCD, 0xBB, 0x6B, 0x26, 0xF6, 0x56, 0xEF, 0xDC, 0xFC, 0x7F, 0x86, 0x3C, 0x6C, 0xC6, 0x70, - 0xE7, 0x0E, 0x61, 0xF2, 0x7C, 0x0E, 0x0E, 0x32, 0x9D, 0x3E, 0x6F, 0x7E, 0x72, 0x6D, 0x3E, 0x69, - 0xCA, 0x5F, 0x0A, 0xB3, 0xEB, 0x6F, 0x88, 0xBA, 0xD3, 0xC4, 0x54, 0x8D, 0xDD, 0x7D, 0xAA, 0x95, - 0x18, 0x3E, 0x94, 0x57, 0x89, 0xC4, 0x5C, 0x49, 0x8C, 0xCE, 0xFD, 0x9C, 0xB1, 0x34, 0xE3, 0x17, - 0x0B, 0xD9, 0xC5, 0x35, 0xBD, 0xB7, 0xBB, 0x7D, 0x8F, 0x8F, 0xE3, 0xBE, 0x3F, 0xCD, 0x38, 0xBD, - 0xD0, 0x9E, 0x63, 0x42, 0x10, 0x74, 0xAE, 0x93, 0x82, 0x92, 0xBA, 0x95, 0xAE, 0x9F, 0x34, 0xA5, - 0xDB, 0x4F, 0x99, 0x7A, 0xCD, 0xB3, 0x00, 0xF6, 0xE2, 0xA4, 0xA8, 0x2C, 0x3F, 0xD5, 0x1F, 0xF7, - 0xAA, 0x7A, 0xFD, 0xEB, 0x85, 0xEB, 0x4A, 0xBE, 0x49, 0x85, 0x9C, 0xF7, 0xE4, 0x4B, 0xEE, 0xD3, - 0xF4, 0x3F, 0xB5, 0xFC, 0x39, 0xC5, 0xD4, 0xC6, 0x70, 0x86, 0x5D, 0x56, 0xAF, 0xC4, 0xE9, 0x45, - 0x7F, 0xE0, 0x2B, 0x96, 0xFF, 0x00, 0x3B, 0x19, 0xCC, 0x30, 0xC4, 0x7A, 0x1A, 0x58, 0xDC, 0xA3, - 0x6E, 0x14, 0xEB, 0xA1, 0x8B, 0x86, 0xFC, 0xEA, 0x3A, 0xFE, 0x71, 0xC6, 0x42, 0xA6, 0x5F, 0x98, - 0xD4, 0x85, 0x36, 0xE3, 0x2A, 0x73, 0x69, 0x3E, 0xD6, 0x7A, 0x1F, 0xC0, 0xB9, 0xAD, 0x1C, 0x46, - 0x45, 0x9F, 0xD7, 0xA5, 0x42, 0x4E, 0x13, 0xA1, 0x56, 0x4A, 0x2D, 0x6E, 0x9C, 0x24, 0xEC, 0xD7, - 0xDC, 0x2B, 0xB1, 0x66, 0x2C, 0x7B, 0xD0, 0xC0, 0xAF, 0x5E, 0xE3, 0x34, 0xB1, 0xAE, 0xE9, 0x15, - 0x7D, 0x4D, 0x4B, 0x7C, 0x31, 0x22, 0xFD, 0x2B, 0xAA, 0x96, 0x5B, 0x57, 0x15, 0x96, 0xE2, 0x73, - 0x3A, 0x8D, 0xBE, 0x59, 0x45, 0x7A, 0xB9, 0x37, 0x76, 0xFF, 0x00, 0x0F, 0xBC, 0xF4, 0xB0, 0xF9, - 0x06, 0x23, 0x31, 0xC8, 0x31, 0xFC, 0x43, 0x5A, 0x4D, 0xBA, 0x75, 0x20, 0xAF, 0xFC, 0xD2, 0x9B, - 0x6E, 0x4D, 0xFA, 0x7B, 0xBF, 0xF8, 0x10, 0xED, 0x3C, 0xFD, 0xE1, 0xF4, 0xA6, 0x5F, 0x36, 0x65, - 0xDB, 0xFD, 0xD1, 0x4B, 0x60, 0x7F, 0x78, 0xC3, 0xDA, 0x9B, 0x79, 0xFF, 0x00, 0x1F, 0x0D, 0x5F, - 0x5B, 0x8A, 0xC6, 0x4B, 0xFD, 0x44, 0xA1, 0x08, 0xBF, 0xF9, 0x78, 0xE2, 0xFE, 0xF9, 0x4B, 0xFC, - 0x8F, 0xD3, 0xF3, 0x2C, 0xD2, 0xA7, 0xFC, 0x41, 0x7C, 0x1D, 0x1A, 0x6E, 0xC9, 0xD7, 0x70, 0x97, - 0x9A, 0xE6, 0xA9, 0x51, 0x2F, 0xBF, 0x95, 0xFC, 0x85, 0xB3, 0x8C, 0x3C, 0x99, 0x3D, 0x16, 0xAD, - 0xED, 0x5F, 0xEE, 0x8F, 0xCA, 0xA9, 0xDB, 0xCA, 0x22, 0x56, 0xE3, 0x24, 0xD4, 0x9F, 0x6B, 0xFF, - 0x00, 0x63, 0xF5, 0xAF, 0x67, 0x84, 0xF3, 0xCE, 0x1C, 0xCB, 0x32, 0x9A, 0x74, 0xF1, 0x33, 0x4A, - 0xAC, 0xAE, 0xE5, 0xEE, 0xB9, 0x75, 0x69, 0x6A, 0xA2, 0xFA, 0x25, 0xA1, 0xF5, 0x7E, 0x18, 0xF1, - 0x97, 0x01, 0x70, 0xF7, 0x0C, 0xD0, 0xC3, 0xE6, 0x15, 0xA2, 0xB1, 0x13, 0x72, 0x94, 0xEF, 0x4E, - 0x73, 0x77, 0x72, 0x6A, 0x37, 0x71, 0x84, 0x97, 0xC2, 0x96, 0x97, 0xF3, 0xEA, 0x58, 0xDA, 0xBF, - 0xDD, 0x1F, 0x95, 0x00, 0x01, 0xD0, 0x0A, 0x8A, 0x1B, 0x80, 0xED, 0xB4, 0xAE, 0x0F, 0x6E, 0x6A, - 0x6A, 0xFD, 0x1B, 0x2B, 0xC6, 0xE5, 0xB9, 0x85, 0x2F, 0xAC, 0x60, 0x9A, 0x94, 0x76, 0xBA, 0x56, - 0x69, 0xFC, 0xD2, 0x68, 0xFD, 0xF7, 0x86, 0xF3, 0x7E, 0x1F, 0xCF, 0x30, 0xDF, 0x5E, 0xC9, 0xE5, - 0x19, 0xC1, 0x3B, 0x5D, 0x47, 0x95, 0xA7, 0xD9, 0xA6, 0x94, 0x97, 0xCD, 0x05, 0x45, 0x79, 0xFF, - 0x00, 0x1E, 0xED, 0xF8, 0x54, 0xB5, 0x15, 0xE7, 0xFC, 0x7B, 0xB7, 0xE1, 0x53, 0xC4, 0x3F, 0xF2, - 0x27, 0xC5, 0xFF, 0x00, 0xD7, 0xB9, 0xFF, 0x00, 0xE9, 0x2C, 0xCF, 0x8E, 0xBF, 0xE4, 0x94, 0xCC, - 0xFF, 0x00, 0xEB, 0xC5, 0x5F, 0xFD, 0x37, 0x22, 0x9A, 0xF2, 0xC0, 0x7B, 0xD6, 0x82, 0x80, 0x06, - 0x00, 0xC5, 0x67, 0x53, 0xB7, 0xBF, 0xF7, 0x9B, 0xF3, 0xAF, 0xC3, 0x78, 0x4B, 0x8A, 0x68, 0xE4, - 0x4A, 0xAF, 0x3D, 0x1E, 0x77, 0x3B, 0x6B, 0x74, 0xAD, 0x6B, 0xF9, 0x3E, 0xE7, 0xF1, 0xB7, 0x86, - 0x1E, 0x24, 0x61, 0x38, 0x2E, 0x38, 0x97, 0x57, 0x09, 0xED, 0x65, 0x57, 0x96, 0xCD, 0x34, 0x9A, - 0x4A, 0xF7, 0x57, 0x69, 0xEF, 0x7F, 0xC0, 0xB5, 0x75, 0x2E, 0xC5, 0xDA, 0x3E, 0xF1, 0xFD, 0x2A, - 0x9D, 0x1C, 0x9A, 0xB5, 0x69, 0x0E, 0x3E, 0x77, 0x1C, 0xF6, 0x15, 0xD1, 0x5A, 0xA6, 0x61, 0xC6, - 0x99, 0xBA, 0x50, 0x8F, 0x2C, 0x17, 0xCD, 0x42, 0x3D, 0x5B, 0x7D, 0x5B, 0xFC, 0x5E, 0x9B, 0x23, - 0xBB, 0x15, 0x88, 0xCF, 0x3C, 0x5C, 0xE2, 0x98, 0xC6, 0x94, 0x3D, 0x9D, 0x18, 0x2B, 0x2E, 0xB1, - 0xA5, 0x0E, 0xAD, 0xBB, 0x2B, 0xCA, 0x4F, 0xD2, 0xEE, 0xCB, 0x44, 0xAE, 0xA6, 0x8C, 0x62, 0x35, - 0x07, 0xB0, 0xA7, 0x51, 0x45, 0x7E, 0xFD, 0x46, 0x92, 0xA5, 0x4A, 0x34, 0xE3, 0xB4, 0x52, 0x5F, - 0x71, 0xFD, 0xBB, 0x85, 0xC3, 0xC7, 0x0D, 0x87, 0xA7, 0x42, 0x1B, 0x41, 0x24, 0xBD, 0x12, 0xB1, - 0x4E, 0xF8, 0xE6, 0x7F, 0xA0, 0xA7, 0x69, 0xFD, 0x58, 0xFD, 0x2A, 0x19, 0x49, 0x32, 0x31, 0x3D, - 0x73, 0x4D, 0xAF, 0xE7, 0x08, 0xE7, 0xCB, 0x0B, 0xC4, 0x95, 0x33, 0x4F, 0x67, 0xCC, 0x94, 0xE6, - 0xD4, 0x5B, 0xB6, 0xF7, 0x4B, 0x5B, 0x3D, 0xAF, 0xD8, 0xFE, 0x05, 0x8F, 0x1B, 0x47, 0x2E, 0xE3, - 0xFA, 0xDC, 0x46, 0xE8, 0x7B, 0x44, 0xAA, 0xD4, 0x6A, 0x0D, 0xF2, 0xBB, 0x35, 0x28, 0xAB, 0xBB, - 0x4A, 0xCD, 0x26, 0x9E, 0xCF, 0x54, 0x5E, 0xB8, 0x50, 0xF0, 0x91, 0xDF, 0xA8, 0xAA, 0x3D, 0x39, - 0x14, 0x51, 0x59, 0xF1, 0x37, 0x11, 0x43, 0x3B, 0xC4, 0xC3, 0x13, 0x1A, 0x3E, 0xCE, 0x51, 0x56, - 0x7E, 0xF5, 0xEF, 0xAD, 0xD7, 0x45, 0x6B, 0x6A, 0x73, 0xF8, 0x89, 0xC7, 0x74, 0xB8, 0xC3, 0x31, - 0xA3, 0x98, 0x43, 0x09, 0xEC, 0x2A, 0x42, 0x3C, 0xAE, 0xD3, 0xE6, 0xE6, 0xB3, 0xBC, 0x5F, 0xC3, - 0x1B, 0x35, 0x77, 0xDE, 0xFA, 0x76, 0x34, 0x22, 0x6D, 0xD1, 0xAB, 0x7A, 0x8A, 0x75, 0x43, 0x62, - 0x73, 0x0E, 0x3D, 0x0D, 0x4D, 0x5F, 0xD0, 0x39, 0x16, 0x36, 0x58, 0xEC, 0xAE, 0x86, 0x26, 0x5B, - 0xCA, 0x2A, 0xFE, 0xBD, 0x7F, 0x13, 0xFB, 0x93, 0x83, 0x33, 0x79, 0xE7, 0x1C, 0x3B, 0x82, 0xC7, - 0xD4, 0xF8, 0xAA, 0x53, 0x8B, 0x7F, 0xE2, 0xB5, 0x9F, 0xE2, 0x98, 0x51, 0x45, 0x15, 0xEB, 0x1F, - 0x4C, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x50, 0xC4, - 0x01, 0x92, 0x69, 0xAA, 0xEA, 0xDF, 0x74, 0xE6, 0xB2, 0x95, 0x7A, 0x51, 0xA8, 0xA9, 0x4A, 0x49, - 0x49, 0xEC, 0xAF, 0xAB, 0xF4, 0x47, 0x34, 0xF1, 0xB8, 0x5A, 0x75, 0xE3, 0x87, 0x9D, 0x48, 0xAA, - 0x92, 0xD5, 0x45, 0xB5, 0xCC, 0xFD, 0x16, 0xEC, 0x75, 0x52, 0xBC, 0x18, 0x9C, 0xFB, 0x8C, 0xD5, - 0xDA, 0xA5, 0x78, 0xDB, 0xA7, 0x3E, 0xDC, 0x57, 0xC0, 0x78, 0x98, 0xE9, 0x2C, 0x9A, 0x0A, 0x7F, - 0x17, 0x3A, 0xB7, 0xDC, 0xEF, 0xF8, 0x1F, 0x88, 0x7D, 0x21, 0xA5, 0x86, 0x5C, 0x27, 0x49, 0x54, - 0xF8, 0xDD, 0x58, 0xF2, 0xFA, 0xF2, 0xCA, 0xFF, 0x00, 0x2B, 0x5F, 0xF0, 0x24, 0xD3, 0xCF, 0xDE, - 0x15, 0x66, 0xAB, 0x69, 0xE3, 0xEF, 0x35, 0x59, 0xAF, 0x53, 0x80, 0xD4, 0xD7, 0x0F, 0x50, 0xE7, - 0xFE, 0xF5, 0xBD, 0x39, 0x99, 0xF4, 0x9E, 0x0A, 0xC6, 0xB2, 0xE0, 0x5C, 0x17, 0xB5, 0xFE, 0xFD, - 0xBD, 0x3D, 0xA4, 0xAD, 0xFF, 0x00, 0x03, 0xC8, 0x28, 0xA2, 0x8A, 0xFB, 0x03, 0xF5, 0x40, 0xA2, - 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x02, 0x8D, 0xD2, 0x95, 0x99, 0xB3, - 0xDC, 0xE4, 0x52, 0xDB, 0xCC, 0x63, 0xE3, 0x19, 0x15, 0x6E, 0x58, 0xD6, 0x45, 0xC3, 0x0F, 0xA1, - 0xAA, 0xCF, 0x6A, 0xE3, 0xEE, 0x90, 0xD5, 0xF8, 0xA6, 0x73, 0xC2, 0xF9, 0xDE, 0x53, 0x99, 0xCF, - 0x30, 0xCA, 0x6F, 0x28, 0xB6, 0xDA, 0xE5, 0xD5, 0xAB, 0xEE, 0x9C, 0x7A, 0xAF, 0x93, 0xF3, 0x3F, - 0x90, 0xF8, 0xB3, 0xC3, 0x8E, 0x30, 0xE1, 0x8E, 0x22, 0xAB, 0x9E, 0x70, 0xC2, 0x94, 0xE1, 0x39, - 0x4A, 0x49, 0xC2, 0xCE, 0x71, 0xE6, 0xD5, 0xC6, 0x50, 0x77, 0xE6, 0x57, 0xDA, 0xCA, 0x49, 0xAB, - 0x5E, 0xCC, 0x7F, 0xDA, 0x97, 0xFB, 0x86, 0x8F, 0xB5, 0x2F, 0xF7, 0x4D, 0x43, 0xF6, 0x79, 0x7F, - 0xBB, 0xFA, 0xD1, 0xE4, 0x4B, 0xFD, 0xDF, 0xD6, 0xB1, 0xFF, 0x00, 0x58, 0x78, 0xE7, 0xFE, 0x7C, - 0xCF, 0xFF, 0x00, 0x05, 0x7F, 0xF6, 0xA7, 0x2F, 0xFA, 0xF5, 0xE3, 0x1F, 0xFD, 0x02, 0xD5, 0xFF, - 0x00, 0xC2, 0x67, 0xFF, 0x00, 0xC8, 0x13, 0x7D, 0xA9, 0x3F, 0xBA, 0x6A, 0x75, 0x21, 0x97, 0x23, - 0xA1, 0xAA, 0x7F, 0x67, 0x97, 0xFB, 0xBF, 0xAD, 0x5B, 0x85, 0x76, 0x46, 0x17, 0xD2, 0xBE, 0xCF, - 0x83, 0xF3, 0x3E, 0x22, 0xC5, 0xD6, 0xA9, 0x1C, 0xD2, 0x93, 0x8C, 0x12, 0xD1, 0xB8, 0xF2, 0xBB, - 0xDF, 0x6E, 0x97, 0xD2, 0xFD, 0x0F, 0xD6, 0x3C, 0x2A, 0xE2, 0x2E, 0x3C, 0xCD, 0x31, 0x75, 0xE1, - 0xC4, 0x78, 0x69, 0x42, 0x92, 0x8D, 0xE3, 0x29, 0x53, 0xF6, 0x6F, 0x9A, 0xEB, 0x44, 0xAC, 0xAE, - 0x9A, 0xBB, 0x6E, 0xDA, 0x59, 0x6B, 0xA8, 0xEA, 0xCF, 0x9B, 0xFD, 0x73, 0x7D, 0x4D, 0x68, 0x55, - 0x39, 0x20, 0x93, 0xCC, 0x38, 0x5C, 0xE4, 0xF5, 0xAE, 0x3F, 0x12, 0xB0, 0x58, 0xBC, 0x56, 0x12, - 0x84, 0x70, 0xF4, 0xDC, 0xED, 0x27, 0x7B, 0x2B, 0xDB, 0x4D, 0x36, 0x3C, 0xBF, 0xA4, 0x0E, 0x53, - 0x9A, 0x66, 0x59, 0x66, 0x0A, 0x18, 0x1C, 0x3C, 0xAA, 0xA5, 0x39, 0x5F, 0x95, 0x39, 0x35, 0x75, - 0x65, 0xA2, 0xD7, 0x5D, 0x75, 0xD8, 0x75, 0xAC, 0x28, 0xF1, 0xEE, 0x61, 0x9E, 0x6A, 0xC2, 0xA2, - 0xAF, 0xDD, 0x50, 0x3F, 0x0A, 0x48, 0x53, 0x64, 0x61, 0x69, 0xF5, 0xF4, 0xDC, 0x3B, 0x91, 0xE1, - 0xB0, 0x18, 0x0A, 0x09, 0xD1, 0x8C, 0x6A, 0xA8, 0xAE, 0x67, 0x65, 0x7B, 0xDB, 0x5B, 0xBD, 0xCF, - 0xD1, 0x38, 0x0B, 0x83, 0xB2, 0xFC, 0x93, 0x24, 0xC2, 0x29, 0xE1, 0x21, 0x0C, 0x4A, 0x84, 0x79, - 0xE5, 0xCB, 0x1E, 0x6E, 0x6B, 0x7B, 0xD7, 0x95, 0xAE, 0xF5, 0xF3, 0x0A, 0xA3, 0x75, 0xC5, 0xC3, - 0x7D, 0x6A, 0xF5, 0x32, 0x48, 0x91, 0xCE, 0x58, 0x57, 0x37, 0x18, 0xF0, 0xFD, 0x7C, 0xEB, 0x01, - 0x1A, 0x34, 0x24, 0x94, 0xA3, 0x25, 0x2D, 0x76, 0x7A, 0x35, 0xFA, 0x9E, 0x6F, 0x8A, 0xDC, 0x0F, - 0x8C, 0xE2, 0xEC, 0x96, 0x9E, 0x13, 0x05, 0x38, 0xC6, 0xA4, 0x26, 0xA6, 0xB9, 0xAE, 0x93, 0xD1, - 0xC5, 0xAB, 0xA4, 0xED, 0xBD, 0xF6, 0xE8, 0x47, 0x61, 0xFE, 0xA4, 0xFF, 0x00, 0xBD, 0x53, 0xD2, - 0x28, 0x0A, 0xB8, 0x03, 0x02, 0x96, 0xBD, 0x9C, 0x97, 0x2F, 0x96, 0x5F, 0x97, 0x51, 0xC2, 0x49, - 0xDD, 0xC1, 0x24, 0xDF, 0x99, 0xF5, 0xBC, 0x23, 0x92, 0x4F, 0x22, 0xC8, 0x70, 0xB9, 0x65, 0x49, - 0x73, 0x4A, 0x94, 0x12, 0x6D, 0x6C, 0xDF, 0x5B, 0x79, 0x5F, 0x62, 0xAE, 0xA0, 0x31, 0x22, 0xB7, - 0xA8, 0xA8, 0xED, 0xD0, 0x48, 0xE5, 0x0F, 0xA5, 0x59, 0xBA, 0x88, 0xC8, 0xA3, 0x1D, 0x45, 0x36, - 0xD6, 0x16, 0x46, 0xDC, 0xDD, 0x7D, 0x2B, 0xF3, 0x3C, 0xCB, 0x85, 0xF1, 0x55, 0xF8, 0xBB, 0xDA, - 0x3A, 0x17, 0xC3, 0xCD, 0xA6, 0xDF, 0xD9, 0xB7, 0x2F, 0xBD, 0x77, 0xDE, 0xF7, 0xF3, 0x3F, 0x9E, - 0x33, 0xFF, 0x00, 0x0E, 0x33, 0x2C, 0x6F, 0x8A, 0x3E, 0xDD, 0xE1, 0x1C, 0xF0, 0x55, 0x65, 0x19, - 0x4A, 0x5F, 0x63, 0x95, 0xC1, 0x2A, 0x89, 0xBE, 0x8D, 0xBE, 0x6B, 0x2D, 0xF5, 0x4D, 0x0B, 0x0C, - 0x02, 0x36, 0xDD, 0x9C, 0x9E, 0xD4, 0xDD, 0x40, 0x7C, 0xAA, 0xD5, 0x62, 0x91, 0xD4, 0x3A, 0xED, - 0x61, 0xC5, 0x7D, 0xC6, 0x61, 0xC3, 0x18, 0x59, 0x64, 0xB5, 0x72, 0xCC, 0x14, 0x54, 0x14, 0xB5, - 0x5B, 0xFC, 0x49, 0xA6, 0x9B, 0x7A, 0xBE, 0x8B, 0xE4, 0x7E, 0xC9, 0x9D, 0xF8, 0x77, 0x96, 0xD4, - 0xE1, 0x0C, 0x4F, 0x0F, 0x65, 0x14, 0xE3, 0x46, 0x35, 0x35, 0x5B, 0xB5, 0xCE, 0x9A, 0x92, 0x72, - 0x6E, 0xF2, 0x77, 0x71, 0x49, 0xB7, 0x7B, 0x2F, 0x4B, 0x14, 0xEC, 0xC9, 0xFB, 0x40, 0xC5, 0x17, - 0x80, 0xFD, 0xA0, 0xD5, 0xA8, 0xA2, 0x48, 0xCE, 0x54, 0x73, 0xEF, 0x44, 0xD1, 0x2C, 0x8B, 0xCF, - 0x5E, 0xC6, 0xBE, 0x4B, 0xFD, 0x48, 0xC7, 0xFF, 0x00, 0xAB, 0x2F, 0x01, 0x29, 0xA7, 0x55, 0x4F, - 0xDA, 0x25, 0xD3, 0x6B, 0x72, 0xDF, 0xEF, 0x7D, 0xAF, 0xF7, 0x9F, 0x97, 0xBF, 0x07, 0xF3, 0xA5, - 0xE1, 0xEC, 0xB2, 0x49, 0xD4, 0x8B, 0xC4, 0xAA, 0xDE, 0xDA, 0x29, 0x3F, 0x77, 0xE1, 0xE5, 0xE4, - 0xE6, 0x69, 0x6A, 0xD5, 0xDD, 0xED, 0x6B, 0xBB, 0x3D, 0x35, 0x28, 0x0E, 0x4E, 0x33, 0x8A, 0x9B, - 0xEC, 0xD2, 0x1E, 0x41, 0x5F, 0xCE, 0xA3, 0x91, 0x19, 0x1B, 0x6B, 0x0A, 0x92, 0x19, 0xDA, 0x35, - 0xDB, 0x8D, 0xC3, 0xB7, 0x35, 0xF9, 0xE6, 0x4D, 0x4B, 0x29, 0xA5, 0x8A, 0x9E, 0x1B, 0x3B, 0x84, - 0xA3, 0x6E, 0xAA, 0xF7, 0x4D, 0x74, 0x69, 0x27, 0xF8, 0x7E, 0x5B, 0x7E, 0x13, 0xC2, 0x78, 0x6E, - 0x18, 0xC3, 0x66, 0x55, 0x70, 0x1C, 0x5F, 0x4A, 0x74, 0xD4, 0x6E, 0xB9, 0xA2, 0xE4, 0x9C, 0x24, - 0xB7, 0x8C, 0xA2, 0x93, 0x6D, 0x3E, 0x8D, 0x2B, 0xA7, 0xBD, 0xD3, 0xBA, 0x92, 0xDE, 0xDC, 0xA4, - 0x9B, 0x98, 0x8E, 0x3B, 0x0A, 0xB1, 0x54, 0x66, 0x95, 0xA4, 0x6C, 0xF4, 0x1D, 0x85, 0x58, 0xB2, - 0x66, 0x68, 0xB9, 0xEC, 0x78, 0xAF, 0xD3, 0x78, 0x3F, 0x3D, 0xCA, 0x16, 0x32, 0x59, 0x56, 0x5B, - 0x46, 0x4A, 0x1A, 0xC9, 0x4D, 0xBB, 0xF3, 0x35, 0xD5, 0xAE, 0x9E, 0x5F, 0x92, 0x3F, 0xA2, 0x3C, - 0x2B, 0xE3, 0x3E, 0x17, 0x8E, 0x6B, 0x53, 0x86, 0xB2, 0x0C, 0x2C, 0xE3, 0x45, 0xF3, 0x4D, 0x54, - 0x94, 0xAE, 0xE6, 0xD2, 0x57, 0x6D, 0x35, 0x78, 0xAB, 0x7C, 0x3A, 0xF6, 0xBA, 0x4D, 0x93, 0x54, - 0x57, 0x9F, 0xF1, 0xEE, 0xDF, 0x85, 0x4B, 0x51, 0x5E, 0x7F, 0xC7, 0xBB, 0x7E, 0x15, 0xF6, 0x7C, - 0x43, 0xFF, 0x00, 0x22, 0x7C, 0x5F, 0xFD, 0x7B, 0x9F, 0xFE, 0x92, 0xCF, 0xD6, 0xB8, 0xEB, 0xFE, - 0x49, 0x4C, 0xCF, 0xFE, 0xBC, 0x55, 0xFF, 0x00, 0xD3, 0x72, 0x29, 0x01, 0x93, 0x8A, 0x74, 0x8A, - 0xC8, 0xD8, 0x61, 0x8A, 0x44, 0xFB, 0xC3, 0xEB, 0x57, 0xE5, 0x45, 0x91, 0x70, 0xC3, 0xF1, 0xAF, - 0xC2, 0xB8, 0x6F, 0x85, 0x5E, 0x79, 0x83, 0xC4, 0x4E, 0x94, 0xED, 0x56, 0x0D, 0x5B, 0xB3, 0xBA, - 0x7A, 0x3E, 0xDB, 0x68, 0xCF, 0xE3, 0x1E, 0x00, 0xF0, 0xD9, 0xF1, 0x8E, 0x53, 0x8E, 0xAB, 0x87, - 0xAB, 0xC9, 0x88, 0xA2, 0xE3, 0xC8, 0x9F, 0xC3, 0x2B, 0xA9, 0x5D, 0x3E, 0xA9, 0xE8, 0xAC, 0xFA, - 0x75, 0x5D, 0xA9, 0xDB, 0xBA, 0xA4, 0x99, 0x65, 0xC8, 0xFE, 0x55, 0x78, 0x10, 0x46, 0x45, 0x67, - 0xCA, 0x86, 0x36, 0xDA, 0x6A, 0x4B, 0x79, 0x8A, 0x61, 0x4F, 0x2B, 0x5E, 0xE7, 0x07, 0xF1, 0x33, - 0xC9, 0x2B, 0xCF, 0x2C, 0xCC, 0x63, 0xCB, 0x1B, 0xEF, 0x6D, 0x63, 0x2F, 0x3E, 0xEB, 0xCF, 0x5B, - 0x7A, 0x1F, 0x65, 0xE1, 0x4F, 0x88, 0x4F, 0x83, 0xF1, 0x95, 0x78, 0x7B, 0x3D, 0xA7, 0xEC, 0xE9, - 0xF3, 0x35, 0x7E, 0x5F, 0x7A, 0x13, 0xBD, 0x9A, 0x95, 0xB5, 0x71, 0x7D, 0xF5, 0xB7, 0x4F, 0x75, - 0xE9, 0x72, 0x8A, 0x29, 0x18, 0xE1, 0x49, 0xF4, 0x15, 0xFB, 0x7C, 0xE4, 0xA3, 0x17, 0x26, 0xF4, - 0x47, 0xF6, 0x15, 0x5A, 0x91, 0xA7, 0x4E, 0x55, 0x24, 0xEC, 0x92, 0xBB, 0xF4, 0x45, 0x09, 0x0E, - 0x64, 0x63, 0xEA, 0x6A, 0xCD, 0xA4, 0x68, 0x61, 0x05, 0x94, 0x12, 0x7D, 0x45, 0x54, 0xAB, 0xD6, - 0xC3, 0x10, 0x2F, 0xD2, 0xBF, 0x0B, 0xF0, 0xEF, 0x0F, 0x4F, 0x1B, 0x9C, 0xD6, 0xAB, 0x5A, 0x0A, - 0x4B, 0x95, 0xBD, 0x55, 0xD5, 0xDC, 0x97, 0xFC, 0x13, 0xF8, 0xCF, 0xC0, 0x9C, 0x0D, 0x1C, 0xDB, - 0x8A, 0xF1, 0x78, 0x9C, 0x5D, 0x35, 0x52, 0x2A, 0x9C, 0x9F, 0xBC, 0x93, 0x5C, 0xD2, 0x9C, 0x7A, - 0x3E, 0xB6, 0xB8, 0xEF, 0x2A, 0x3F, 0xEE, 0x2F, 0xE5, 0x54, 0xEE, 0x80, 0x13, 0xB0, 0x03, 0x02, - 0xAF, 0x55, 0x1B, 0xCF, 0xF8, 0xF8, 0x6A, 0xFA, 0x6F, 0x12, 0xB0, 0x58, 0x7A, 0x39, 0x4D, 0x39, - 0x52, 0xA6, 0xA2, 0xFD, 0xA2, 0xD5, 0x24, 0xBE, 0xCC, 0xBB, 0x1F, 0xA1, 0x7D, 0x20, 0x72, 0x9C, - 0x06, 0x13, 0x86, 0x30, 0xF5, 0x30, 0xB8, 0x78, 0x41, 0xFB, 0x68, 0xA6, 0xE3, 0x14, 0x9D, 0xB9, - 0x2A, 0x75, 0x4B, 0x6B, 0xD8, 0x9E, 0xC3, 0xFD, 0x51, 0xFF, 0x00, 0x7A, 0xA7, 0xA8, 0x2C, 0x3F, - 0xD4, 0x9F, 0xF7, 0xAA, 0x7A, 0xFA, 0xCE, 0x10, 0x4D, 0x64, 0x38, 0x5B, 0xFF, 0x00, 0x2F, 0xEA, - 0xCF, 0xD3, 0xFC, 0x2C, 0x8B, 0x5C, 0x15, 0x97, 0x5D, 0x7F, 0xCB, 0xB5, 0xF9, 0xB0, 0xA2, 0x8A, - 0x2B, 0xE9, 0x0F, 0xBF, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x22, 0xBB, 0x56, 0x68, 0xB0, 0xBF, 0x95, 0x45, 0x66, 0x8E, 0x24, 0xDC, 0x46, 0x05, 0x5A, 0xA2, - 0xBE, 0x5F, 0x1B, 0xC2, 0xD8, 0x7C, 0x5E, 0x71, 0x4B, 0x34, 0x95, 0x46, 0xA5, 0x0B, 0x69, 0xD1, - 0xDB, 0x6F, 0x4F, 0x3E, 0xE7, 0xE7, 0x39, 0xC7, 0x86, 0xF8, 0x1C, 0xCF, 0x8A, 0xF0, 0xDC, 0x47, - 0x52, 0xB4, 0xD4, 0xE9, 0x72, 0xFB, 0xAB, 0x66, 0xE3, 0x76, 0xB5, 0xDD, 0x2E, 0xEB, 0xA8, 0x56, - 0x7C, 0x87, 0x74, 0x8C, 0x7D, 0x4D, 0x5A, 0xBA, 0x98, 0x22, 0xED, 0x1F, 0x78, 0xFE, 0x95, 0x4C, - 0x0C, 0x9C, 0x0A, 0xFC, 0xF7, 0xC4, 0xBC, 0xDA, 0x8E, 0x26, 0xBD, 0x2C, 0x05, 0x07, 0xCC, 0xE0, - 0xDB, 0x95, 0xBB, 0xBB, 0x24, 0xBD, 0x77, 0xBF, 0xA9, 0xF8, 0x5F, 0xD2, 0x0F, 0x8A, 0x30, 0xB9, - 0x86, 0x37, 0x0D, 0x92, 0xE0, 0xE7, 0xCF, 0x2A, 0x2E, 0x4E, 0x76, 0xD6, 0xD2, 0x76, 0x51, 0x8F, - 0xAA, 0x57, 0xBA, 0xE9, 0x74, 0xB7, 0xB9, 0x72, 0xC4, 0x62, 0x0C, 0xFA, 0x9A, 0x9A, 0x9B, 0x1A, - 0xED, 0x8D, 0x57, 0xD0, 0x53, 0xAB, 0xF5, 0x7C, 0x97, 0x06, 0xF0, 0x59, 0x6D, 0x0C, 0x3B, 0xDE, - 0x31, 0x49, 0xFA, 0xDB, 0x5F, 0xC4, 0xFE, 0x98, 0xE1, 0x2C, 0xAA, 0x59, 0x4F, 0x0F, 0xE0, 0xB0, - 0x12, 0x5E, 0xF5, 0x3A, 0x70, 0x4F, 0xFC, 0x56, 0x5C, 0xDF, 0x8D, 0xC2, 0x8A, 0x28, 0xAF, 0x4C, - 0xFA, 0x20, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, - 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, - 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, - 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x01, 0x93, 0x46, 0x24, 0x5C, 0x1E, 0xBD, 0x8D, 0x57, 0xFB, 0x2C, - 0x9E, 0xAB, 0x56, 0xE8, 0xAF, 0x99, 0xCD, 0xF8, 0x47, 0x29, 0xCD, 0x6B, 0x7B, 0x7C, 0x44, 0x1F, - 0x3F, 0x74, 0xED, 0x7F, 0x5E, 0xE7, 0xE7, 0x9C, 0x53, 0xE1, 0x77, 0x0C, 0xF1, 0x36, 0x2D, 0x63, - 0x71, 0xF4, 0x5A, 0xAB, 0x6B, 0x39, 0x46, 0x4E, 0x2D, 0xA5, 0xB5, 0xFA, 0x3B, 0x77, 0xB5, 0xED, - 0xA5, 0xEC, 0x91, 0x55, 0x6D, 0x5B, 0x77, 0xCC, 0xC3, 0x1E, 0xD5, 0x65, 0x54, 0x2A, 0xED, 0x03, - 0x81, 0x4B, 0x45, 0x74, 0x64, 0xDC, 0x37, 0x96, 0xE4, 0xFC, 0xCF, 0x09, 0x0B, 0x4A, 0x5B, 0xB6, - 0xEE, 0xED, 0xDB, 0xD0, 0xEF, 0xE1, 0x3F, 0x0F, 0xF8, 0x7F, 0x85, 0x79, 0xE5, 0x96, 0x52, 0xB4, - 0xE7, 0xA3, 0x94, 0x9F, 0x34, 0xAD, 0xDA, 0xEF, 0x65, 0xE4, 0xAD, 0x7E, 0xBB, 0x20, 0xA6, 0xCA, - 0xBB, 0xE3, 0x2B, 0xEB, 0x4E, 0xA2, 0xBD, 0x9A, 0xF4, 0x69, 0xD7, 0xA5, 0x2A, 0x35, 0x15, 0xE3, - 0x24, 0xD3, 0x5E, 0x4F, 0x46, 0x7D, 0x5E, 0x33, 0x07, 0x47, 0x1B, 0x86, 0xA9, 0x85, 0xAF, 0x1E, - 0x6A, 0x75, 0x22, 0xE3, 0x25, 0xDD, 0x49, 0x59, 0xAF, 0xB9, 0x95, 0x63, 0xB6, 0x71, 0x20, 0x2D, - 0x8C, 0x0A, 0xB5, 0x45, 0x15, 0xE4, 0xE4, 0x7C, 0x3F, 0x82, 0xC9, 0x69, 0x4A, 0x9E, 0x15, 0x3F, - 0x79, 0xDD, 0xB6, 0xEE, 0xFF, 0x00, 0xA4, 0x7C, 0xC7, 0x07, 0xF0, 0x3E, 0x51, 0xC2, 0x58, 0x6A, - 0x94, 0x32, 0xD5, 0x2B, 0x54, 0x77, 0x93, 0x93, 0xBB, 0x7D, 0x96, 0xCB, 0x45, 0xD3, 0x41, 0x93, - 0x46, 0x24, 0x5C, 0x1E, 0xBD, 0x8D, 0x43, 0x1D, 0xA9, 0x0F, 0x96, 0x61, 0x81, 0xE9, 0x56, 0x68, - 0xAC, 0xB3, 0x0E, 0x17, 0xCA, 0xB3, 0x0C, 0x5C, 0x71, 0x78, 0x8A, 0x57, 0x9A, 0xF3, 0x6A, 0xF6, - 0xDA, 0xEB, 0xAF, 0xF4, 0xB6, 0x39, 0xF3, 0xCF, 0x0E, 0x78, 0x6B, 0x3C, 0xCC, 0xE9, 0xE6, 0x78, - 0xEC, 0x3F, 0x35, 0x68, 0x5B, 0x5B, 0xB4, 0xA5, 0x6D, 0xB9, 0x92, 0xD2, 0x56, 0xF3, 0xE9, 0xA3, - 0xBA, 0xD0, 0x29, 0x24, 0x1B, 0x90, 0xAF, 0xA8, 0xA5, 0xA2, 0xBD, 0xDA, 0x94, 0xE3, 0x56, 0x9C, - 0xA9, 0xCB, 0x66, 0xAC, 0xFE, 0x67, 0xD9, 0x62, 0x30, 0xF4, 0xF1, 0x14, 0x67, 0x46, 0xA2, 0xBC, - 0x64, 0x9A, 0x7E, 0x8D, 0x59, 0x94, 0x96, 0xDE, 0x42, 0xF8, 0x23, 0x8F, 0x5A, 0xBA, 0x06, 0x06, - 0x05, 0x14, 0x57, 0xCF, 0xF0, 0xFF, 0x00, 0x0C, 0x60, 0xB2, 0x35, 0x53, 0xEA, 0xED, 0xB7, 0x3D, - 0xDB, 0xEC, 0xB6, 0x5D, 0x3B, 0x9F, 0x11, 0xC0, 0xFE, 0x1D, 0xE5, 0x1C, 0x1D, 0x1A, 0xFF, 0x00, - 0x50, 0x72, 0x94, 0xAA, 0xB5, 0x77, 0x26, 0x9B, 0xB2, 0xBD, 0xA2, 0xAC, 0x96, 0x8A, 0xEF, 0xD7, - 0xA8, 0x53, 0x64, 0x8D, 0x1F, 0xEF, 0x2E, 0x69, 0xD4, 0x57, 0xBB, 0x88, 0xC3, 0xD1, 0xC4, 0x53, - 0x74, 0xEB, 0x41, 0x4A, 0x2F, 0xA3, 0x57, 0x5F, 0x73, 0x3E, 0xCF, 0x1B, 0x81, 0xC2, 0x63, 0xA8, - 0x3C, 0x3E, 0x2E, 0x94, 0x6A, 0x53, 0x7B, 0xC6, 0x49, 0x49, 0x3F, 0x93, 0xBA, 0x11, 0x40, 0x55, - 0xC0, 0x18, 0x14, 0xB4, 0x51, 0x5A, 0x46, 0x31, 0x84, 0x54, 0x62, 0xAC, 0x91, 0xBD, 0x3A, 0x70, - 0xA5, 0x05, 0x4E, 0x9A, 0x4A, 0x2B, 0x44, 0x96, 0x89, 0x25, 0xD1, 0x20, 0xA2, 0x8A, 0x2A, 0x8B, - 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x28, 0xDC, 0x2B, 0x09, 0x9B, 0x20, 0xF2, 0x78, 0xA9, 0xAD, 0x61, 0xDB, 0xF3, 0xBF, 0x5E, 0xC3, - 0xD2, 0xAC, 0x51, 0x5F, 0x0D, 0x96, 0xF0, 0x26, 0x0B, 0x09, 0x99, 0xCF, 0x1F, 0x52, 0x6E, 0xA3, - 0x6D, 0xB8, 0xA6, 0xB6, 0x6D, 0xDE, 0xEF, 0x5D, 0x5A, 0xE9, 0xB7, 0x7D, 0xCF, 0xC6, 0xB8, 0x7F, - 0xC1, 0x8C, 0xA7, 0x2B, 0xE2, 0x2A, 0xB9, 0xD5, 0x7A, 0xAE, 0xBB, 0x72, 0x73, 0x84, 0x64, 0xB4, - 0x84, 0x9B, 0xBD, 0xDB, 0xBB, 0xE6, 0x6B, 0xA3, 0xB2, 0xB6, 0xF6, 0xBD, 0x9A, 0x28, 0xA2, 0x8A, - 0xFB, 0x93, 0xF6, 0x50, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x03, 0xFF, 0xD9 }; diff --git a/Core/HW/Camera.cpp b/Core/HW/Camera.cpp index c03da48bd4..3c233d8931 100644 --- a/Core/HW/Camera.cpp +++ b/Core/HW/Camera.cpp @@ -17,7 +17,179 @@ #include "Camera.h" -#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) +void convert_frame(int inw, int inh, unsigned char *inData, AVPixelFormat inFormat, + int outw, int outh, unsigned char **outData, int *outLen) { + + struct SwsContext *sws_context = sws_getContext( + inw, inh, inFormat, + outw, outh, AV_PIX_FMT_RGB24, + SWS_BICUBIC, NULL, NULL, NULL); + + // resize + uint8_t *src[4] = {0}; + uint8_t *dst[4] = {0}; + int srcStride[4], dstStride[4]; + + unsigned char *rgbData = (unsigned char*)malloc(outw * outh * 4); + + av_image_fill_linesizes(srcStride, inFormat, inw); + av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, outw); + + av_image_fill_pointers(src, inFormat, inh, inData, srcStride); + av_image_fill_pointers(dst, AV_PIX_FMT_RGB24, outh, rgbData, dstStride); + + sws_scale(sws_context, + src, srcStride, 0, inh, + dst, dstStride); + + // compress jpeg + *outLen = outw * outh * 2; + *outData = (unsigned char*)malloc(*outLen); + + jpge::params params; + params.m_quality = 60; + params.m_subsampling = jpge::H2V2; + params.m_two_pass_flag = false; + jpge::compress_image_to_jpeg_file_in_memory( + *outData, *outLen, outw, outh, 3, rgbData, params); + free(rgbData); +} + +void __cameraDummyImage(int width, int height, unsigned char** outData, int* outLen) { + unsigned char* rgbData = (unsigned char*)malloc(3 * width * height); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + rgbData[3 * (y * width + x) + 0] = x*255/width; + rgbData[3 * (y * width + x) + 1] = x*255/width; + rgbData[3 * (y * width + x) + 2] = y*255/height; + } + } + + *outLen = width * height * 2; + *outData = (unsigned char*)malloc(*outLen); + + jpge::params params; + params.m_quality = 60; + params.m_subsampling = jpge::H2V2; + params.m_two_pass_flag = false; + jpge::compress_image_to_jpeg_file_in_memory( + *outData, *outLen, width, height, 3, rgbData, params); + free(rgbData); +} + + +#if defined(USING_QT_UI) + +std::vector __qt_getDeviceList() { + std::vector deviceList; + const QList cameras = QCameraInfo::availableCameras(); + for (const QCameraInfo &cameraInfo : cameras) { + deviceList.push_back(cameraInfo.deviceName().toStdString() + + " (" + cameraInfo.description().toStdString() + ")"); + } + return deviceList; +} + +QList MyViewfinder::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const { + Q_UNUSED(handleType); + // Return the formats you will support + return QList() + << QVideoFrame::Format_RGB24 + << QVideoFrame::Format_YUYV + ; +} + +bool MyViewfinder::present(const QVideoFrame &frame) { + if (frame.isValid()) { + QVideoFrame cloneFrame(frame); + cloneFrame.map(QAbstractVideoBuffer::ReadOnly); + + unsigned char *jpegData = nullptr; + int jpegLen = 0; + + QVideoFrame::PixelFormat frameFormat = cloneFrame.pixelFormat(); + if (frameFormat == QVideoFrame::Format_RGB24) { + convert_frame(cloneFrame.size().width(), cloneFrame.size().height(), + (unsigned char*)cloneFrame.bits(), AV_PIX_FMT_RGB24, + qtc_ideal_width, qtc_ideal_height, &jpegData, &jpegLen); + + } else if (frameFormat == QVideoFrame::Format_YUYV) { + convert_frame(cloneFrame.size().width(), cloneFrame.size().height(), + (unsigned char*)cloneFrame.bits(), AV_PIX_FMT_YUYV422, + qtc_ideal_width, qtc_ideal_height, &jpegData, &jpegLen); + } + + if (jpegData) { + Camera::pushCameraImage(jpegLen, jpegData); + free(jpegData); + jpegData = nullptr; + } + + cloneFrame.unmap(); + return true; + } + return false; +} + +int __qt_startCapture(int width, int height) { + if (qt_camera != nullptr) { + ERROR_LOG(HLE, "camera already started"); + return -1; + } + + char selectedCamera[80]; + sscanf(g_Config.sCameraDevice.c_str(), "%80s ", &selectedCamera[0]); + + const QList availableCameras = QCameraInfo::availableCameras(); + if (availableCameras.size() < 1) { + delete qt_camera; + qt_camera = nullptr; + ERROR_LOG(HLE, "no camera found"); + return -1; + } + for (const QCameraInfo &cameraInfo : availableCameras) { + if (cameraInfo.deviceName() == selectedCamera) { + qt_camera = new QCamera(cameraInfo); + } + } + if (qt_camera == nullptr) { + qt_camera = new QCamera(); + if (qt_camera == nullptr) { + ERROR_LOG(HLE, "cannot open camera"); + return -1; + } + } + + qtc_ideal_width = width; + qtc_ideal_height = height; + + qt_viewfinder = new MyViewfinder; + + QCameraViewfinderSettings viewfinderSettings = qt_camera->viewfinderSettings(); + viewfinderSettings.setResolution(640, 480); + viewfinderSettings.setMinimumFrameRate(15.0); + viewfinderSettings.setMaximumFrameRate(15.0); + + qt_camera->setViewfinderSettings(viewfinderSettings); + qt_camera->setViewfinder(qt_viewfinder); + qt_camera->start(); + + return 0; +} + +int __qt_stopCapture() { + if (qt_camera != nullptr) { + qt_camera->stop(); + qt_camera->unload(); + delete qt_camera; + delete qt_viewfinder; + qt_camera = nullptr; + } + return 0; +} + +//endif defined(USING_QT_UI) +#elif PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) std::vector __v4l_getDeviceList() { std::vector deviceList; @@ -48,37 +220,6 @@ cont: return deviceList; } -void convert_frame(unsigned char *inData, AVPixelFormat inFormat, unsigned char **outData, int *outLen) { - // resize - uint8_t *src[4] = {0}; - uint8_t *dst[4] = {0}; - int srcStride[4], dstStride[4]; - - unsigned char *rgbData = (unsigned char*)malloc(v4l_ideal_width * v4l_ideal_height * 3); - - av_image_fill_linesizes(srcStride, inFormat, v4l_hw_width); - av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, v4l_ideal_width); - - av_image_fill_pointers(src, inFormat, v4l_hw_height, inData, srcStride); - av_image_fill_pointers(dst, AV_PIX_FMT_RGB24, v4l_ideal_height, rgbData, dstStride); - - sws_scale(sws_context, - src, srcStride, 0, v4l_height_fixed_aspect, - dst, dstStride); - - // compress jpeg - *outLen = v4l_ideal_width * v4l_ideal_height * 3; - *outData = (unsigned char*)malloc(*outLen); - - jpge::params params; - params.m_quality = 60; - params.m_subsampling = jpge::H2V2; - params.m_two_pass_flag = false; - jpge::compress_image_to_jpeg_file_in_memory( - *outData, *outLen, v4l_ideal_width, v4l_ideal_height, 3, rgbData, params); - free(rgbData); -} - void *v4l_loop(void *data) { setCurrentThreadName("v4l_loop"); while (v4l_fd >= 0) { @@ -97,23 +238,28 @@ void *v4l_loop(void *data) { } } - unsigned char *jpegData; - int jpegLen; + unsigned char *jpegData = nullptr; + int jpegLen = 0; if (v4l_format == V4L2_PIX_FMT_YUYV) { - convert_frame((unsigned char*)v4l_buffer, AV_PIX_FMT_YUYV422, &jpegData, &jpegLen); + convert_frame(v4l_hw_width, v4l_hw_height, (unsigned char*)v4l_buffer, AV_PIX_FMT_YUYV422, + v4l_ideal_width, v4l_ideal_height, &jpegData, &jpegLen); } else if (v4l_format == V4L2_PIX_FMT_MJPEG) { // decompress jpeg int width, height, req_comps; unsigned char *rgbData = jpgd::decompress_jpeg_image_from_memory( (unsigned char*)v4l_buffer, buf.bytesused, &width, &height, &req_comps, 3); - convert_frame(rgbData, AV_PIX_FMT_RGB24, &jpegData, &jpegLen); + convert_frame(v4l_hw_width, v4l_hw_height, (unsigned char*)v4l_buffer, AV_PIX_FMT_RGB24, + v4l_ideal_width, v4l_ideal_height, &jpegData, &jpegLen); free(rgbData); } - Camera::pushCameraImage(jpegLen, jpegData); - free(jpegData); + if (jpegData) { + Camera::pushCameraImage(jpegLen, jpegData); + free(jpegData); + jpegData = nullptr; + } buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; @@ -217,20 +363,6 @@ int __v4l_startCapture(int ideal_width, int ideal_height) { v4l_height_fixed_aspect = v4l_hw_width * ideal_height / ideal_width; INFO_LOG(HLE, "V4L2: will use %dx%d", v4l_hw_width, v4l_height_fixed_aspect); - // create a converter context - if (v4l_format == V4L2_PIX_FMT_YUYV) { - sws_context = sws_getContext( - v4l_hw_width, v4l_height_fixed_aspect, AV_PIX_FMT_YUYV422, - ideal_width, ideal_height, AV_PIX_FMT_RGB24, - SWS_BICUBIC, NULL, NULL, NULL); - } else if (v4l_format == V4L2_PIX_FMT_MJPEG) { - sws_context = sws_getContext( - v4l_hw_width, v4l_height_fixed_aspect, AV_PIX_FMT_RGB24, - ideal_width, ideal_height, AV_PIX_FMT_RGB24, - SWS_BICUBIC, NULL, NULL, NULL); - } - - struct v4l2_requestbuffers req; memset(&req, 0, sizeof(req)); req.count = 1; diff --git a/Core/HW/Camera.h b/Core/HW/Camera.h index fb082c10cd..9308e1e732 100644 --- a/Core/HW/Camera.h +++ b/Core/HW/Camera.h @@ -19,26 +19,9 @@ #include "ppsspp_config.h" #include "Core/Config.h" +#include "Core/HLE/sceUsbCam.h" #include "Log.h" -#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "thread/threadutil.h" -#include "Core/HLE/sceUsbCam.h" - -#include #include "ext/jpge/jpgd.h" #include "ext/jpge/jpge.h" @@ -47,6 +30,36 @@ extern "C" { #include "libavutil/imgutils.h" } +void __cameraDummyImage(int width, int height, unsigned char** outData, int* outLen); + +#if defined(USING_QT_UI) +#include +#include + + class MyViewfinder : public QAbstractVideoSurface { + Q_OBJECT + public: + QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + bool present(const QVideoFrame &frame); + }; + + static int qtc_ideal_width; + static int qtc_ideal_height; + static QCamera *qt_camera; + static QAbstractVideoSurface *qt_viewfinder; + + std::vector __qt_getDeviceList(); + int __qt_startCapture(int width, int height); + int __qt_stopCapture(); + +#elif PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) +#include +#include +#include +#include + +#include "thread/threadutil.h" + static int v4l_fd = -1; static uint32_t v4l_format; static int v4l_hw_width; @@ -58,7 +71,6 @@ extern "C" { static pthread_t v4l_thread; static void *v4l_buffer; static int v4l_length; - static struct SwsContext *sws_context; std::vector __v4l_getDeviceList(); int __v4l_startCapture(int width, int height); diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 60675b127e..7dd7469163 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -38,6 +38,7 @@ #include "util/text/utf8.h" #include "Core/Config.h" #include "Core/ConfigValues.h" +#include "Core/HW/Camera.h" #include @@ -45,6 +46,7 @@ MainUI *emugl = nullptr; static float refreshRate = 60.f; static int browseFileEvent = -1; static int browseFolderEvent = -1; +QTCamera *qtcamera = nullptr; #ifdef SDL static SDL_AudioDeviceID audioDev = 0; @@ -204,6 +206,14 @@ void System_SendMessage(const char *command, const char *parameter) { } else if (!strcmp(command, "graphics_restart")) { // Should find a way to properly restart the app. qApp->exit(0); + } else if (!strcmp(command, "camera_command")) { + if (!strncmp(parameter, "startVideo", 10)) { + int width = 0, height = 0; + sscanf(parameter, "startVideo_%dx%d", &width, &height); + emit(qtcamera->onStartCamera(width, height)); + } else if (!strcmp(parameter, "stopVideo")) { + emit(qtcamera->onStopCamera()); + } } else if (!strcmp(command, "setclipboardtext")) { QApplication::clipboard()->setText(parameter); #if defined(SDL) @@ -606,6 +616,14 @@ void MainAudio::timerEvent(QTimerEvent *) { #endif +void QTCamera::startCamera(int width, int height) { + __qt_startCapture(width, height); +} + +void QTCamera::stopCamera() { + __qt_stopCapture(); +} + #ifndef SDL Q_DECL_EXPORT #endif @@ -633,6 +651,7 @@ int main(int argc, char *argv[]) QApplication a(argc, argv); QScreen* screen = a.primaryScreen(); QSizeF res = screen->physicalSize(); + if (res.width() < res.height()) res.transpose(); pixel_xres = res.width(); @@ -647,6 +666,10 @@ int main(int argc, char *argv[]) refreshRate = screen->refreshRate(); + qtcamera = new QTCamera; + QObject::connect(qtcamera, SIGNAL(onStartCamera(int, int)), qtcamera, SLOT(startCamera(int, int))); + QObject::connect(qtcamera, SIGNAL(onStopCamera()), qtcamera, SLOT(stopCamera())); + std::string savegame_dir = "."; std::string external_dir = "."; #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) diff --git a/Qt/QtMain.h b/Qt/QtMain.h index bf351c84f7..7391a59f54 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -147,6 +147,21 @@ private: std::atomic emuThreadState; }; +class QTCamera : public QObject { + Q_OBJECT +public: + QTCamera() {} + ~QTCamera() {}; + +signals: + void onStartCamera(int width, int height); + void onStopCamera(); + +public slots: + void startCamera(int width, int height); + void stopCamera(); +}; + extern MainUI* emugl; #ifndef SDL diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index fa750e0bf5..d6c453c05d 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -99,8 +99,7 @@ VideoFormatTransform g_VideoFormats[] = const int g_cVideoFormats = 4; - -MediaParam defaultVideoParam = { 480, 272, 0, MFVideoFormat_RGB24 }; +MediaParam defaultVideoParam = { 640, 480, 0, MFVideoFormat_RGB24 }; MediaParam defaultAudioParam = { 44100, 2, 0, MFAudioFormat_PCM }; HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride); @@ -363,7 +362,6 @@ WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) : case CAPTUREDEVIDE_TYPE::VIDEO: targetMediaParam = defaultVideoParam; imageRGB = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, targetMediaParam.width, targetMediaParam.height, 1)); - av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width); imgJpegSize = av_image_get_buffer_size(AV_PIX_FMT_YUVJ411P, targetMediaParam.width, targetMediaParam.height, 1); imageJpeg = (unsigned char*)av_malloc(imgJpegSize); break; @@ -414,7 +412,7 @@ bool WindowsCaptureDevice::init() { return true; } -bool WindowsCaptureDevice::start() { +bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) { HRESULT hr = S_OK; IMFAttributes *pAttributes = nullptr; IMFMediaType *pType = nullptr; @@ -436,6 +434,10 @@ bool WindowsCaptureDevice::start() { return false; } + targetMediaParam.width = width; + targetMediaParam.height = height; + av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width); + m_pCallback = new ReaderCallback(this); switch (state) { @@ -699,6 +701,7 @@ void WindowsCaptureDevice::messageHandler() { CoInitializeEx(NULL, COINIT_MULTITHREADED); MFStartup(MF_VERSION); CAPTUREDEVIDE_MESSAGE message; + std::vector* resolution; if (type == CAPTUREDEVIDE_TYPE::VIDEO) { setCurrentThreadName("Camera"); @@ -710,7 +713,8 @@ void WindowsCaptureDevice::messageHandler() { init(); break; case CAPTUREDEVIDE_COMMAND::START: - start(); + resolution = static_cast*>(message.opacity); + start(resolution->at(0), resolution->at(1)); break; case CAPTUREDEVIDE_COMMAND::STOP: stop(); diff --git a/Windows/CaptureDevice.h b/Windows/CaptureDevice.h index 06bb86c376..fe82f02a93 100644 --- a/Windows/CaptureDevice.h +++ b/Windows/CaptureDevice.h @@ -163,7 +163,7 @@ public: static void CheckDevices(); bool init(); - bool start(); + bool start(UINT32 width, UINT32 height); bool stop(); CAPTUREDEVIDE_ERROR getError() const { return error; } diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 00b2c74ea9..da3f9312bb 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -275,6 +275,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/ELF/ParamSFO.cpp \ $(SRC)/Core/HW/SimpleAudioDec.cpp \ $(SRC)/Core/HW/AsyncIOManager.cpp \ + $(SRC)/Core/HW/Camera.cpp \ $(SRC)/Core/HW/MemoryStick.cpp \ $(SRC)/Core/HW/MpegDemux.cpp.arm \ $(SRC)/Core/HW/MediaEngine.cpp.arm \ diff --git a/android/src/org/ppsspp/ppsspp/CameraHelper.java b/android/src/org/ppsspp/ppsspp/CameraHelper.java index bd18be1dc0..2b46fe8393 100644 --- a/android/src/org/ppsspp/ppsspp/CameraHelper.java +++ b/android/src/org/ppsspp/ppsspp/CameraHelper.java @@ -23,6 +23,8 @@ import java.util.List; class CameraHelper { private static final String TAG = CameraHelper.class.getSimpleName(); private Display mDisplay; + private int mTargetWidth = 0; + private int mTargetHeight = 0; private Camera mCamera = null; private boolean mIsCameraRunning = false; private int mCameraFacing = 0; @@ -110,18 +112,14 @@ class CameraHelper { } mLastFrameTime = currentTime; - // the expected values arrives in sceUsbCamSetupVideo - int targetW = 480; - int targetH = 272; - int cameraRotation = getCameraRotation(); byte[] newPreviewData = rotateNV21(previewData, mPreviewSize.width, mPreviewSize.height, - targetW, targetH, cameraRotation); - YuvImage yuvImage = new YuvImage(newPreviewData, ImageFormat.NV21, targetW, targetH, null); + mTargetWidth, mTargetHeight, cameraRotation); + YuvImage yuvImage = new YuvImage(newPreviewData, ImageFormat.NV21, mTargetWidth, mTargetHeight, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // convert to Jpeg - Rect crop = new Rect(0, 0, targetW, targetH); + Rect crop = new Rect(0, 0, mTargetWidth, mTargetHeight); yuvImage.compressToJpeg(crop, 80, baos); NativeApp.pushCameraImageAndroid(baos.toByteArray()); try { @@ -150,10 +148,15 @@ class CameraHelper { return deviceList; } + void setCameraSize(int width, int height) { + mTargetWidth = width; + mTargetHeight = height; + } + void startCamera() { try { int cameraId = NativeApp.getSelectedCamera(); - Log.d(TAG, "startCamera: " + cameraId); + Log.d(TAG, "startCamera [id=" + cameraId + ", res=" + mTargetWidth + "x" + mTargetHeight + "]"); Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 480b06bb9c..700318133a 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -53,6 +53,8 @@ import java.io.File; import java.lang.reflect.Field; import java.util.List; import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public abstract class NativeActivity extends Activity implements SurfaceHolder.Callback { // Remember to loadLibrary your JNI .so in a static {} block @@ -1396,7 +1398,14 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mLocationHelper.stopLocationUpdates(); } } else if (command.equals("camera_command")) { - if (params.equals("startVideo")) { + if (params.startsWith("startVideo")) { + Pattern pattern = Pattern.compile("startVideo_(\\d+)x(\\d+)"); + Matcher matcher = pattern.matcher(params); + if (!matcher.matches()) + return false; + int width = Integer.parseInt(matcher.group(1)); + int height = Integer.parseInt(matcher.group(2)); + mCameraHelper.setCameraSize(width, height); if (mCameraHelper != null && !askForPermissions(permissionsForCamera, REQUEST_CODE_CAMERA_PERMISSION)) { mCameraHelper.startCamera(); } diff --git a/ios/CameraHelper.h b/ios/CameraHelper.h index 8c7c2b5f2f..45de67d5d9 100644 --- a/ios/CameraHelper.h +++ b/ios/CameraHelper.h @@ -10,7 +10,7 @@ @property (nonatomic, strong) id delegate; -- (int) checkPermission; +- (void) setCameraSize:(int)width h:(int)height; - (void) startVideo; - (void) stopVideo; diff --git a/ios/CameraHelper.mm b/ios/CameraHelper.mm index 7a2f6c87b6..ef6a7c305a 100644 --- a/ios/CameraHelper.mm +++ b/ios/CameraHelper.mm @@ -7,6 +7,8 @@ @interface CameraHelper() { AVCaptureSession *captureSession; AVCaptureVideoPreviewLayer *previewLayer; + int mWidth; + int mHeight; } @end @@ -54,6 +56,12 @@ NSString *getSelectedCamera() { } } +-(void) setCameraSize: (int)width h:(int)height { + NSLog(@"CameraHelper::setCameraSize %dx%d", width, height); + mWidth = width; + mHeight = height; +} + -(void) startVideo { NSLog(@"CameraHelper::startVideo"); if ([self checkPermission]) { @@ -100,7 +108,7 @@ NSString *getSelectedCamera() { previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession]; previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; - [previewLayer setFrame:CGRectMake(0, 0, 480, 272)]; + [previewLayer setFrame:CGRectMake(0, 0, mWidth, mHeight)]; [captureSession startRunning]; }); @@ -132,13 +140,19 @@ NSString *getSelectedCamera() { size_t height = CVPixelBufferGetHeight(imageBuffer); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); - CGImageRef newImage = CGBitmapContextCreateImage(newContext); - CGContextRelease(newContext); + CGContextRef inContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); + CGImageRef inImage = CGBitmapContextCreateImage(inContext); + CGContextRelease(inContext); + + CGRect outRect = CGRectMake(0, 0, width, height); + CGContextRef outContext = CGBitmapContextCreate(nil, mWidth, mHeight, 8, mWidth * 4, colorSpace, kCGImageAlphaPremultipliedFirst); + CGContextDrawImage(outContext, outRect, inImage); + CGImageRelease(inImage); + CGImageRef outImage = CGBitmapContextCreateImage(outContext); CGColorSpaceRelease(colorSpace); - CVPixelBufferUnlockBaseAddress(imageBuffer,0); - return newImage; + CVPixelBufferUnlockBaseAddress(imageBuffer, 0); + return outImage; } @end diff --git a/ios/ViewController.h b/ios/ViewController.h index 7f93faaebb..340ea79a79 100644 --- a/ios/ViewController.h +++ b/ios/ViewController.h @@ -17,6 +17,7 @@ @end extern __unsafe_unretained ViewController* sharedViewController; +void setCameraSize(int width, int height); void startVideo(); void stopVideo(); void startLocation(); diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 7c45c27a11..65549221ba 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -677,8 +677,12 @@ static LocationHelper *locationHelper; } #endif +void setCameraSize(int width, int height) { + [cameraHelper setCameraSize: width h:height]; +} + void startVideo() { - [cameraHelper startVideo]; + [cameraHelper startVideo]; } void stopVideo() { diff --git a/ios/main.mm b/ios/main.mm index eee92a0cad..107a804b6e 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -120,7 +120,10 @@ void System_SendMessage(const char *command, const char *parameter) { // exit(0); // }); } else if (!strcmp(command, "camera_command")) { - if (!strcmp(parameter, "startVideo")) { + if (!strncmp(parameter, "startVideo", 10)) { + int width = 0, height = 0; + sscanf(parameter, "startVideo_%dx%d", &width, &height); + setCameraSize(width, height); startVideo(); } else if (!strcmp(parameter, "stopVideo")) { stopVideo(); From 3c359cfd2951285c4ab4f2372c5d8983b633e935 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Tue, 4 Feb 2020 12:00:37 +0200 Subject: [PATCH 63/71] macOS permissions --- Qt/macbundle.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Qt/macbundle.sh b/Qt/macbundle.sh index 3272de6657..f906e0d77f 100644 --- a/Qt/macbundle.sh +++ b/Qt/macbundle.sh @@ -11,4 +11,8 @@ fi plutil -replace NSPrincipalClass -string NSApplication ${PPSSPP}/Contents/Info.plist plutil -replace NSHighResolutionCapable -bool YES ${PPSSPP}/Contents/Info.plist +plutil -replace NSLocationWhenInUseUsageDescription -string "Your location may be used to emulate Go!Explore, a GPS accessory" ${PPSSPP}/Contents/Info.plist +plutil -replace NSCameraUsageDescription -string "Your camera may be used to emulate Go!Cam, a camera accessory" ${PPSSPP}/Contents/Info.plist +plutil -replace NSMicrophoneUsageDescription -string "Your microphone may be used to emulate Go!Cam/Talkman, a microphone accessory" ${PPSSPP}/Contents/Info.plist + # TODO: install SDL and Qt frameworks From 90ff68e6c94d4972808afa378ee3bdb1875040e7 Mon Sep 17 00:00:00 2001 From: iota97 Date: Tue, 11 Feb 2020 08:19:30 +0100 Subject: [PATCH 64/71] Framegraph in devmenu --- Core/Config.cpp | 1 + Core/Config.h | 1 + Core/HLE/sceDisplay.cpp | 23 +++++++++++++++++++++++ Core/HLE/sceDisplay.h | 1 + UI/DevScreens.cpp | 1 + UI/EmuScreen.cpp | 19 +++++++++++++++++++ 6 files changed, 46 insertions(+) diff --git a/Core/Config.cpp b/Core/Config.cpp index 4b914e608c..d73c00276c 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -979,6 +979,7 @@ static ConfigSetting debuggerSettings[] = { ConfigSetting("ShowGpuProfile", &g_Config.bShowGpuProfile, false, false), ConfigSetting("SkipDeadbeefFilling", &g_Config.bSkipDeadbeefFilling, false), ConfigSetting("FuncHashMap", &g_Config.bFuncHashMap, false), + ConfigSetting("DrawFrameGraph", &g_Config.bDrawFrameGraph, false), ConfigSetting(false), }; diff --git a/Core/Config.h b/Core/Config.h index e60e0224ef..68f938be79 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -401,6 +401,7 @@ public: // Double edged sword: much easier debugging, but not accurate. bool bSkipDeadbeefFilling; bool bFuncHashMap; + bool bDrawFrameGraph; // Volatile development settings bool bShowFrameProfiler; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 4644241f0b..972e2e759f 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -153,6 +153,11 @@ static double fpsHistory[120]; static int fpsHistorySize = (int)ARRAY_SIZE(fpsHistory); static int fpsHistoryPos = 0; static int fpsHistoryValid = 0; +static double frameTimeHistory[600]; +static int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory); +static int frameTimeHistoryPos = 0; +static int frameTimeHistoryValid = 0; +static double lastFrameTimeHistory = 0.0; static double monitorFpsUntil = 0.0; static int lastNumFlips = 0; static float flips = 0.0f; @@ -235,6 +240,9 @@ void __DisplayInit() { lastNumFlips = 0; fpsHistoryValid = 0; fpsHistoryPos = 0; + frameTimeHistoryValid = 0; + frameTimeHistoryPos = 0; + lastFrameTimeHistory = 0.0; __KernelRegisterWaitTypeFuncs(WAITTYPE_VBLANK, __DisplayVblankBeginCallback, __DisplayVblankEndCallback); } @@ -463,6 +471,21 @@ static void CalculateFPS() { ++fpsHistoryValid; } } + + if (g_Config.bDrawFrameGraph) { + frameTimeHistory[frameTimeHistoryPos++] = now - lastFrameTimeHistory; + lastFrameTimeHistory = now; + frameTimeHistoryPos = frameTimeHistoryPos % frameTimeHistorySize; + if (frameTimeHistoryValid < frameTimeHistorySize) { + ++frameTimeHistoryValid; + } + } +} + +double* __DisplayGetFrameTimes(int *out_valid, int *out_pos) { + *out_valid = frameTimeHistoryValid; + *out_pos = frameTimeHistoryPos; + return frameTimeHistory; } void __DisplayGetDebugStats(char *stats, size_t bufsize) { diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index db6cbcd1e1..a19db5ff7f 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -40,6 +40,7 @@ void __DisplayGetDebugStats(char stats[], size_t bufsize); void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps); void __DisplayGetVPS(float *out_vps); void __DisplayGetAveragedFPS(float *out_vps, float *out_fps); +double* __DisplayGetFrameTimes(int *out_valid, int *out_pos); int __DisplayGetNumVblanks(); int __DisplayGetVCount(); int __DisplayGetFlipCount(); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 372671d39d..a38eb6cde3 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -89,6 +89,7 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { #ifdef USE_PROFILER items->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), "")); #endif + items->Add(new CheckBox(&g_Config.bDrawFrameGraph, dev->T("Draw Frametimes Graph"))); scroll->Add(items); parent->Add(scroll); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 7bbf0863df..6dc2768ce7 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1190,6 +1190,21 @@ static void DrawFPS(DrawBuffer *draw2d, const Bounds &bounds) { draw2d->SetFontScale(1.0f, 1.0f); } +static void DrawFrameTimes(UIContext *ctx) { + int valid, pos; + double *history = __DisplayGetFrameTimes(&valid, &pos); + + ctx->Flush(); + ctx->BeginNoTex(); + int bottom = ctx->GetBounds().y2(); + for (int i = 0; i < valid; ++i) { + ctx->Draw()->vLine(i, bottom, bottom - history[i]*10000, 0x7F3fFF3f); + } + ctx->Draw()->vLine(pos, bottom, bottom - 512, 0xFFffFFff); + ctx->Flush(); + ctx->Begin(); +} + void EmuScreen::preRender() { using namespace Draw; DrawContext *draw = screenManager()->getDrawContext(); @@ -1367,6 +1382,10 @@ void EmuScreen::renderUI() { DrawFPS(draw2d, ctx->GetBounds()); } + if (g_Config.bDrawFrameGraph && !invalid_) { + DrawFrameTimes(ctx); + } + #if !PPSSPP_PLATFORM(UWP) if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN && g_Config.bShowAllocatorDebug) { DrawAllocatorVis(ctx, gpu); From be2eed180194463906f5eea6ac49b2d3d0d1245a Mon Sep 17 00:00:00 2001 From: iota97 Date: Tue, 11 Feb 2020 10:28:49 +0100 Subject: [PATCH 65/71] Add some reference line --- UI/EmuScreen.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 6dc2768ce7..d63e991150 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1193,16 +1193,26 @@ static void DrawFPS(DrawBuffer *draw2d, const Bounds &bounds) { static void DrawFrameTimes(UIContext *ctx) { int valid, pos; double *history = __DisplayGetFrameTimes(&valid, &pos); + int scale = 7000; + int width = 600; ctx->Flush(); ctx->BeginNoTex(); int bottom = ctx->GetBounds().y2(); for (int i = 0; i < valid; ++i) { - ctx->Draw()->vLine(i, bottom, bottom - history[i]*10000, 0x7F3fFF3f); + ctx->Draw()->vLine(i, bottom, bottom - history[i]*scale, 0xFF3fFF3f); } - ctx->Draw()->vLine(pos, bottom, bottom - 512, 0xFFffFFff); + ctx->Draw()->vLine(pos, bottom, bottom - 512, 0xFFff3F3f); + + ctx->Draw()->hLine(0, bottom - 0.0333*scale, width, 0xFF3f3Fff); + ctx->Draw()->hLine(0, bottom - 0.0167*scale, width, 0xFF3f3Fff); + ctx->Flush(); ctx->Begin(); + ctx->Draw()->SetFontScale(0.5f, 0.5f); + ctx->Draw()->DrawText(UBUNTU24, "33.3ms", width, bottom - 0.0333*scale, 0xFF3f3Fff, ALIGN_BOTTOMLEFT | FLAG_DYNAMIC_ASCII); + ctx->Draw()->DrawText(UBUNTU24, "16.7ms", width, bottom - 0.0167*scale, 0xFF3f3Fff, ALIGN_BOTTOMLEFT | FLAG_DYNAMIC_ASCII); + ctx->Draw()->SetFontScale(1.0f, 1.0f); } void EmuScreen::preRender() { From b5f65c5643bd3c02119cddd816973a93a450e72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 11 Feb 2020 21:16:09 +0100 Subject: [PATCH 66/71] Fix linking issue in the UWP build (add camera files to project) --- UWP/CoreUWP/CoreUWP.vcxproj | 4 +++- UWP/CoreUWP/CoreUWP.vcxproj.filters | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index a26bcbac2c..49703f02f3 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -509,6 +509,7 @@ + @@ -719,6 +720,7 @@ + @@ -945,4 +947,4 @@ - + \ No newline at end of file diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index fe872876ad..649a1c4405 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -703,6 +703,9 @@ MIPS\ARM64 + + HW + @@ -1297,5 +1300,8 @@ MIPS\ARM64 + + HW + - + \ No newline at end of file From 06dc3dbd29d9f5f05b8c4dca56fa4018bca47b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 12 Feb 2020 09:24:25 +0100 Subject: [PATCH 67/71] UWP: Update "certificate thumbprint" to one that works --- UWP/UWP.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 35cb743325..693d7700b2 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -188,7 +188,7 @@ True False Never - B57919568D3F60B8D25EC6856DE94173682C548C + b5e422213fb26042db3327f1102c4f9b86c27b1c False 1 OnApplicationRun From e8f018179d98b33cd428c757d1fba5ddfbc913b5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 15 Feb 2020 17:40:15 -0800 Subject: [PATCH 68/71] UI: Correct refresh rate display. It's a float now, not a premultiplied int. --- UI/DevScreens.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index a38eb6cde3..651af59887 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -523,7 +523,7 @@ void SystemInfoScreen::CreateViews() { deviceSpecs->Add(new InfoItem(si->T("Native Resolution"), StringFromFormat("%dx%d", System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)))); - deviceSpecs->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat("%0.3f Hz", (float)System_GetPropertyInt(SYSPROP_DISPLAY_REFRESH_RATE) / 1000.0f))); + deviceSpecs->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat("%0.3f Hz", (float)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE)))); #endif #if 0 From 373ff6061f1c7eff8e585481ddd33bff8232e8bd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 15 Feb 2020 17:47:10 -0800 Subject: [PATCH 69/71] UI: Show refresh rate on more platforms. It's detected on SDL/Qt, so we should show it there. --- UI/DevScreens.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 651af59887..e39888d041 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -523,6 +523,10 @@ void SystemInfoScreen::CreateViews() { deviceSpecs->Add(new InfoItem(si->T("Native Resolution"), StringFromFormat("%dx%d", System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)))); +#endif + +#if !PPSSPP_PLATFORM(WINDOWS) + // Don't show on Windows, since it's always treated as 60 there. deviceSpecs->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat("%0.3f Hz", (float)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE)))); #endif From ce694da5ffee491185d116f7f32527e76da6964e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 17 Feb 2020 11:17:16 -0800 Subject: [PATCH 70/71] UI: Separate sleep time in frame time graph. It's useful to know how much of the frame time is active vs inactive. --- Core/HLE/sceDisplay.cpp | 27 +++++++++++++++++++++++---- Core/HLE/sceDisplay.h | 2 +- UI/EmuScreen.cpp | 7 +++++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 972e2e759f..578afae3c3 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -154,7 +154,8 @@ static int fpsHistorySize = (int)ARRAY_SIZE(fpsHistory); static int fpsHistoryPos = 0; static int fpsHistoryValid = 0; static double frameTimeHistory[600]; -static int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory); +static double frameSleepHistory[600]; +static const int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory); static int frameTimeHistoryPos = 0; static int frameTimeHistoryValid = 0; static double lastFrameTimeHistory = 0.0; @@ -479,12 +480,14 @@ static void CalculateFPS() { if (frameTimeHistoryValid < frameTimeHistorySize) { ++frameTimeHistoryValid; } + frameSleepHistory[frameTimeHistoryPos] = 0.0; } } -double* __DisplayGetFrameTimes(int *out_valid, int *out_pos) { +double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep) { *out_valid = frameTimeHistoryValid; *out_pos = frameTimeHistoryPos; + *out_sleep = frameSleepHistory; return frameTimeHistory; } @@ -638,7 +641,8 @@ static void DoFrameIdleTiming() { time_update(); - double dist = time_now_d() - lastFrameTime; + double before = time_now_d(); + double dist = before - lastFrameTime; // Ignore if the distance is just crazy. May mean wrap or pause. if (dist < 0.0 || dist >= 15 * timePerVblank) { return; @@ -656,7 +660,7 @@ static void DoFrameIdleTiming() { // This prevents fast forward during loading screens. // Give a little extra wiggle room in case the next vblank does more work. const double goal = lastFrameTime + (numVBlanksSinceFlip - 1) * scaledVblank - 0.001; - if (numVBlanksSinceFlip >= 2 && time_now_d() < goal) { + if (numVBlanksSinceFlip >= 2 && before < goal) { while (time_now_d() < goal) { #ifdef _WIN32 sleep_ms(1); @@ -666,6 +670,10 @@ static void DoFrameIdleTiming() { #endif time_update(); } + + if (g_Config.bDrawFrameGraph) { + frameSleepHistory[frameTimeHistoryPos] += time_now_d() - before; + } } } @@ -736,6 +744,7 @@ void __DisplayFlip(int cyclesLate) { postEffectRequiresFlip = shaderInfo->requires60fps; const bool fbDirty = gpu->FramebufferDirty(); if (fbDirty || noRecentFlip || postEffectRequiresFlip) { + int frameSleepPos = frameTimeHistoryPos; CalculateFPS(); // Let the user know if we're running slow, so they know to adjust settings. @@ -797,6 +806,11 @@ void __DisplayFlip(int cyclesLate) { CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0); numVBlanksSinceFlip = 0; + + if (g_Config.bDrawFrameGraph) { + // Track how long we sleep (whether vsync or sleep_ms.) + frameSleepHistory[frameSleepPos] += real_time_now() - lastFrameTimeHistory; + } } else { // Okay, there's no new frame to draw. But audio may be playing, so we need to time still. DoFrameIdleTiming(); @@ -843,6 +857,7 @@ void hleLagSync(u64 userdata, int cyclesLate) { const double goal = lastLagSync + (scale / 1000.0f); time_update(); + double before = time_now_d(); // Don't lag too long ever, if they leave it paused. while (time_now_d() < goal && goal < time_now_d() + 0.01) { #ifndef _WIN32 @@ -855,6 +870,10 @@ void hleLagSync(u64 userdata, int cyclesLate) { const int emuOver = (int)cyclesToUs(cyclesLate); const int over = (int)((time_now_d() - goal) * 1000000); ScheduleLagSync(over - emuOver); + + if (g_Config.bDrawFrameGraph) { + frameSleepHistory[frameTimeHistoryPos] += time_now_d() - before; + } } static u32 sceDisplayIsVblank() { diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index a19db5ff7f..47294c56d7 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -40,7 +40,7 @@ void __DisplayGetDebugStats(char stats[], size_t bufsize); void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps); void __DisplayGetVPS(float *out_vps); void __DisplayGetAveragedFPS(float *out_vps, float *out_fps); -double* __DisplayGetFrameTimes(int *out_valid, int *out_pos); +double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep); int __DisplayGetNumVblanks(); int __DisplayGetVCount(); int __DisplayGetFlipCount(); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index d63e991150..0fcbfbe5b6 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1192,7 +1192,8 @@ static void DrawFPS(DrawBuffer *draw2d, const Bounds &bounds) { static void DrawFrameTimes(UIContext *ctx) { int valid, pos; - double *history = __DisplayGetFrameTimes(&valid, &pos); + double *sleepHistory; + double *history = __DisplayGetFrameTimes(&valid, &pos, &sleepHistory); int scale = 7000; int width = 600; @@ -1200,7 +1201,9 @@ static void DrawFrameTimes(UIContext *ctx) { ctx->BeginNoTex(); int bottom = ctx->GetBounds().y2(); for (int i = 0; i < valid; ++i) { - ctx->Draw()->vLine(i, bottom, bottom - history[i]*scale, 0xFF3fFF3f); + double activeTime = history[i] - sleepHistory[i]; + ctx->Draw()->vLine(i, bottom, bottom - activeTime * scale, 0xFF3FFF3F); + ctx->Draw()->vLine(i, bottom - activeTime * scale, bottom - history[i] * scale, 0x7F3FFF3F); } ctx->Draw()->vLine(pos, bottom, bottom - 512, 0xFFff3F3f); From 1422c8d21bcde04f4375fab39ecddc969c7d2527 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Feb 2020 04:15:13 -0800 Subject: [PATCH 71/71] Kernel: Delay better in sceKernelReferThreadStatus. Sometimes this is called in loops, so correct timing for errors. --- Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelThread.cpp | 31 ++++++++++++------------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 2effbb5a26..ad571355ef 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -739,7 +739,7 @@ const HLEFunction ThreadManForUser[] = {0X3B183E26, &WrapI_I, "sceKernelGetThreadExitStatus", 'i', "i" }, {0X52089CA1, &WrapI_I, "sceKernelGetThreadStackFreeSize", 'i', "i" }, {0XFFC36A14, &WrapU_UU, "sceKernelReferThreadRunStatus", 'x', "xx" }, - {0X17C1684E, &WrapU_UU, "sceKernelReferThreadStatus", 'x', "xx" }, + {0X17C1684E, &WrapU_UU, "sceKernelReferThreadStatus", 'i', "xp" }, {0X2C34E053, &WrapI_I, "sceKernelReleaseWaitThread", 'i', "i" }, {0X75156E8F, &WrapI_I, "sceKernelResumeThread", 'i', "i" }, {0X3AD58B8C, &WrapU_V, "sceKernelSuspendDispatchThread", 'x', "", HLE_NOT_IN_INTERRUPT }, diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 5d9dd685fd..53b8895304 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1256,44 +1256,37 @@ u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr) u32 error; Thread *t = kernelObjects.Get(threadID, error); - if (!t) - { - ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferThreadStatus(%i, %08x): bad thread", error, threadID, statusPtr); - return error; + if (!t) { + hleEatCycles(700); + hleReSchedule("refer thread status"); + return hleLogError(SCEKERNEL, error, "bad thread"); } u32 wantedSize = Memory::Read_U32(statusPtr); - if (sceKernelGetCompiledSdkVersion() > 0x02060010) - { - if (wantedSize > THREADINFO_SIZE_AFTER_260) - { - ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferThreadStatus(%i, %08x): bad size %d", SCE_KERNEL_ERROR_ILLEGAL_SIZE, threadID, statusPtr, wantedSize); - return SCE_KERNEL_ERROR_ILLEGAL_SIZE; + if (sceKernelGetCompiledSdkVersion() > 0x02060010) { + if (wantedSize > THREADINFO_SIZE_AFTER_260) { + hleEatCycles(1200); + hleReSchedule("refer thread status"); + return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_SIZE, "bad size %d", wantedSize); } - VERBOSE_LOG(SCEKERNEL, "sceKernelReferThreadStatus(%i, %08x)", threadID, statusPtr); - t->nt.nativeSize = THREADINFO_SIZE_AFTER_260; if (wantedSize != 0) Memory::Memcpy(statusPtr, &t->nt, std::min(wantedSize, (u32)sizeof(t->nt))); // TODO: What is this value? Basic tests show 0... if (wantedSize > sizeof(t->nt)) Memory::Memset(statusPtr + sizeof(t->nt), 0, wantedSize - sizeof(t->nt)); - } - else - { - VERBOSE_LOG(SCEKERNEL, "sceKernelReferThreadStatus(%i, %08x)", threadID, statusPtr); - + } else { t->nt.nativeSize = THREADINFO_SIZE; u32 sz = std::min(THREADINFO_SIZE, wantedSize); if (sz != 0) Memory::Memcpy(statusPtr, &t->nt, sz); } - hleEatCycles(1220); + hleEatCycles(1400); hleReSchedule("refer thread status"); - return 0; + return hleLogSuccessVerboseI(SCEKERNEL, 0); } // Thanks JPCSP