From 46464d27224b57d623b840061ea417e535db8bda Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2020 09:02:10 -0800 Subject: [PATCH 1/9] 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 04ae730c1..d1ad8a5b7 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 721d26d16..2e032ae2a 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 7c2953b2d..ca26ad7a4 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 457874450..680a48c62 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 4508a1d3d..84d09875b 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 866f4c074..24718559e 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 2/9] 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 71fea8b4a..771750339 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 9a73338de..5b2b93eab 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 3/9] 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 5b2b93eab..0a70a9d38 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 4/9] 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 7fcc42015..1fdb54948 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 6c808552d..f9a94151a 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 5/9] 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 0a70a9d38..29386bb99 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 6/9] 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 f36cbcec8..5ccd0ddd5 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 ae317198d..e8c39d0a6 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 29386bb99..39938b3fc 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 da5b83ee5..8231d8c31 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 24718559e..5ceb76d84 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 ed8d7cf91..72c6620c6 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 7/9] 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 d7e5fa807..f269ecbe0 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 8/9] 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 86afd47b5..7fccb89a5 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 9/9] 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 54de138d8..3be621bb0 100644 --- a/Windows/PPSSPP.manifest +++ b/Windows/PPSSPP.manifest @@ -32,7 +32,8 @@ - True + true + true