2022-11-28 13:03:30 -05:00
|
|
|
|
|
|
|
#include <Geode/DefaultInclude.hpp>
|
2022-07-30 12:24:03 -04:00
|
|
|
|
|
|
|
#ifdef GEODE_IS_WINDOWS
|
|
|
|
|
2023-03-10 14:33:24 -05:00
|
|
|
using namespace geode::prelude;
|
2023-05-01 09:06:06 -04:00
|
|
|
#include <Geode/loader/Dirs.hpp>
|
2022-11-28 12:09:39 -05:00
|
|
|
#include "nfdwin.hpp"
|
2023-02-08 08:42:34 -05:00
|
|
|
#include <ghc/fs_fwd.hpp>
|
2022-11-28 12:09:39 -05:00
|
|
|
#include <Windows.h>
|
|
|
|
#include <iostream>
|
2023-05-06 11:31:51 -04:00
|
|
|
#include <ShlObj.h>
|
2022-11-28 12:09:39 -05:00
|
|
|
#include <shlwapi.h>
|
|
|
|
#include <shobjidl.h>
|
|
|
|
#include <sstream>
|
|
|
|
#include <Geode/utils/web.hpp>
|
2023-03-16 20:42:30 -04:00
|
|
|
#include <Geode/utils/cocos.hpp>
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2023-06-10 08:49:55 -04:00
|
|
|
#include <filesystem>
|
|
|
|
|
2022-07-30 12:24:03 -04:00
|
|
|
bool utils::clipboard::write(std::string const& data) {
|
2022-10-30 14:56:36 -04:00
|
|
|
if (!OpenClipboard(nullptr)) return false;
|
2022-07-30 12:24:03 -04:00
|
|
|
if (!EmptyClipboard()) {
|
|
|
|
CloseClipboard();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, data.size() + 1);
|
|
|
|
|
2022-10-30 14:56:36 -04:00
|
|
|
if (!hg) {
|
|
|
|
CloseClipboard();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto dest = GlobalLock(hg);
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-10-30 14:56:36 -04:00
|
|
|
if (!dest) {
|
|
|
|
CloseClipboard();
|
|
|
|
return false;
|
|
|
|
}
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-10-30 14:56:36 -04:00
|
|
|
memcpy(dest, data.c_str(), data.size() + 1);
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-10-30 14:56:36 -04:00
|
|
|
GlobalUnlock(hg);
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-10-30 14:56:36 -04:00
|
|
|
SetClipboardData(CF_TEXT, hg);
|
|
|
|
CloseClipboard();
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-10-30 14:56:36 -04:00
|
|
|
GlobalFree(hg);
|
2022-07-30 12:24:03 -04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string utils::clipboard::read() {
|
2022-10-30 14:56:36 -04:00
|
|
|
if (!OpenClipboard(nullptr)) return "";
|
|
|
|
|
2022-07-30 12:24:03 -04:00
|
|
|
HANDLE hData = GetClipboardData(CF_TEXT);
|
|
|
|
if (hData == nullptr) {
|
|
|
|
CloseClipboard();
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2022-10-30 14:56:36 -04:00
|
|
|
char* pszText = static_cast<char*>(GlobalLock(hData));
|
2022-07-30 12:24:03 -04:00
|
|
|
if (pszText == nullptr) {
|
|
|
|
CloseClipboard();
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string text(pszText);
|
|
|
|
|
|
|
|
GlobalUnlock(hData);
|
|
|
|
CloseClipboard();
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
2022-09-26 06:53:40 -04:00
|
|
|
bool utils::file::openFolder(ghc::filesystem::path const& path) {
|
2022-10-30 14:56:36 -04:00
|
|
|
ShellExecuteA(NULL, "open", path.string().c_str(), NULL, NULL, SW_SHOWDEFAULT);
|
|
|
|
return true;
|
2022-07-30 12:24:03 -04:00
|
|
|
}
|
|
|
|
|
2022-09-26 06:53:40 -04:00
|
|
|
Result<ghc::filesystem::path> utils::file::pickFile(
|
2022-10-30 14:56:36 -04:00
|
|
|
file::PickMode mode, file::FilePickOptions const& options
|
2022-09-26 06:53:40 -04:00
|
|
|
) {
|
|
|
|
#define TURN_INTO_NFDMODE(mode) \
|
|
|
|
case file::PickMode::mode: nfdMode = NFDMode::mode; break;
|
|
|
|
|
|
|
|
NFDMode nfdMode;
|
|
|
|
switch (mode) {
|
|
|
|
TURN_INTO_NFDMODE(OpenFile);
|
|
|
|
TURN_INTO_NFDMODE(SaveFile);
|
|
|
|
TURN_INTO_NFDMODE(OpenFolder);
|
2022-11-28 10:42:19 -05:00
|
|
|
default: return Err<std::string>("Unknown open mode");
|
2022-09-26 06:53:40 -04:00
|
|
|
}
|
|
|
|
ghc::filesystem::path path;
|
2022-11-28 10:42:19 -05:00
|
|
|
GEODE_UNWRAP(nfdPick(nfdMode, options, &path));
|
2022-09-26 06:53:40 -04:00
|
|
|
return Ok(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
Result<std::vector<ghc::filesystem::path>> utils::file::pickFiles(
|
|
|
|
file::FilePickOptions const& options
|
|
|
|
) {
|
|
|
|
std::vector<ghc::filesystem::path> paths;
|
2022-11-28 10:42:19 -05:00
|
|
|
GEODE_UNWRAP(nfdPick(NFDMode::OpenFolder, options, &paths));
|
2022-09-26 06:53:40 -04:00
|
|
|
return Ok(paths);
|
|
|
|
}
|
|
|
|
|
|
|
|
void utils::web::openLinkInBrowser(std::string const& url) {
|
2022-07-30 12:24:03 -04:00
|
|
|
ShellExecuteA(0, 0, url.c_str(), 0, 0, SW_SHOW);
|
|
|
|
}
|
|
|
|
|
2023-03-16 20:42:30 -04:00
|
|
|
CCPoint cocos::getMousePos() {
|
|
|
|
auto* director = CCDirector::get();
|
|
|
|
auto* gl = director->getOpenGLView();
|
|
|
|
auto winSize = director->getWinSize();
|
|
|
|
auto frameSize = gl->getFrameSize();
|
|
|
|
auto mouse = gl->getMousePosition() / frameSize;
|
|
|
|
return ccp(mouse.x, 1.f - mouse.y) * winSize;
|
|
|
|
}
|
|
|
|
|
2023-05-01 09:06:06 -04:00
|
|
|
ghc::filesystem::path dirs::getGameDir() {
|
2023-05-01 10:18:35 -04:00
|
|
|
// only fetch the path once, since ofc it'll never change
|
|
|
|
// throughout the execution
|
|
|
|
static const auto path = [] {
|
|
|
|
std::array<WCHAR, MAX_PATH> buffer;
|
|
|
|
GetModuleFileNameW(NULL, buffer.data(), MAX_PATH);
|
2023-05-01 07:47:25 -04:00
|
|
|
|
2023-05-01 10:18:35 -04:00
|
|
|
const ghc::filesystem::path path(buffer.data());
|
2023-06-10 08:49:55 -04:00
|
|
|
return std::filesystem::weakly_canonical(path.parent_path().wstring()).wstring();
|
2023-05-01 10:18:35 -04:00
|
|
|
}();
|
|
|
|
|
|
|
|
return path;
|
2023-05-01 05:11:32 -04:00
|
|
|
}
|
|
|
|
|
2023-05-06 11:31:51 -04:00
|
|
|
ghc::filesystem::path dirs::getSaveDir() {
|
|
|
|
// only fetch the path once, since ofc it'll never change
|
|
|
|
// throughout the execution
|
|
|
|
static const auto path = [] {
|
|
|
|
std::array<WCHAR, MAX_PATH + 1> buffer;
|
|
|
|
GetModuleFileNameW(NULL, buffer.data(), MAX_PATH + 1);
|
|
|
|
|
|
|
|
auto executablePath = ghc::filesystem::path(buffer.data());
|
|
|
|
auto executableName = executablePath.filename().wstring();
|
|
|
|
executableName = executableName.substr(0, executableName.find_last_of(L"."));
|
|
|
|
|
|
|
|
if (SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, buffer.data()) >= 0) {
|
|
|
|
auto appdataPath = ghc::filesystem::path(buffer.data());
|
|
|
|
auto savePath = appdataPath / executableName;
|
|
|
|
|
|
|
|
if (SHCreateDirectoryExW(NULL, savePath.wstring().c_str(), NULL) >= 0) {
|
2023-06-10 08:49:55 -04:00
|
|
|
return std::filesystem::weakly_canonical(savePath.wstring()).wstring();
|
2023-05-06 11:31:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-10 08:49:55 -04:00
|
|
|
return std::filesystem::weakly_canonical(executablePath.parent_path().wstring()).wstring();
|
2023-05-06 11:31:51 -04:00
|
|
|
}();
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2023-09-16 10:22:30 -04:00
|
|
|
ghc::filesystem::path dirs::getModRuntimeDir() {
|
|
|
|
return dirs::getGeodeDir() / "unzipped";
|
|
|
|
}
|
|
|
|
|
2023-09-11 09:36:35 -04:00
|
|
|
void geode::utils::game::exit() {
|
|
|
|
if (CCApplication::sharedApplication() &&
|
|
|
|
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
|
|
|
log::error("Cannot exit in PlayLayer or LevelEditorLayer!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CCApplication::sharedApplication())
|
|
|
|
// please forgive me..
|
|
|
|
// manually set the closed flag
|
|
|
|
// TODO: actually call glfwSetWindowShouldClose
|
|
|
|
*reinterpret_cast<bool*>(reinterpret_cast<uintptr_t>(CCEGLView::sharedOpenGLView()->getWindow()) + 0xa) = true;
|
|
|
|
else
|
|
|
|
std::exit(0);
|
|
|
|
}
|
|
|
|
|
2023-08-08 14:59:13 -04:00
|
|
|
void geode::utils::game::restart() {
|
|
|
|
if (CCApplication::sharedApplication() &&
|
|
|
|
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
|
|
|
log::error("Cannot restart in PlayLayer or LevelEditorLayer!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto workingDir = dirs::getGameDir();
|
|
|
|
|
|
|
|
wchar_t buffer[MAX_PATH];
|
|
|
|
GetModuleFileNameW(nullptr, buffer, MAX_PATH);
|
|
|
|
const auto gdName = ghc::filesystem::path(buffer).filename().string();
|
|
|
|
|
|
|
|
// launch updater
|
|
|
|
const auto updaterPath = (workingDir / "GeodeUpdater.exe").string();
|
|
|
|
ShellExecuteA(nullptr, "open", updaterPath.c_str(), gdName.c_str(), workingDir.string().c_str(), false);
|
|
|
|
|
2023-09-11 09:36:35 -04:00
|
|
|
exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void geode::utils::game::launchLoaderUninstaller(bool deleteSaveData) {
|
|
|
|
const auto workingDir = dirs::getGameDir();
|
|
|
|
|
|
|
|
if (!exists((workingDir / "GeodeUninstaller.exe"))) {
|
|
|
|
log::error("Uninstaller not found! Not launching.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string params;
|
|
|
|
if (deleteSaveData) {
|
|
|
|
params = "\"/DATA=" + dirs::getSaveDir().string() + "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
// launch uninstaller
|
|
|
|
const auto uninstallerPath = (workingDir / "GeodeUninstaller.exe").string();
|
|
|
|
ShellExecuteA(nullptr, "open", uninstallerPath.c_str(), params.c_str(), workingDir.string().c_str(), false);
|
2023-08-08 14:59:13 -04:00
|
|
|
}
|
|
|
|
|
2023-08-18 03:51:00 -04:00
|
|
|
Result<> geode::hook::addObjcMethod(std::string const& className, std::string const& selectorName, void* imp) {
|
|
|
|
return Err("Wrong platform");
|
|
|
|
}
|
|
|
|
Result<void*> geode::hook::getObjcMethodImp(std::string const& className, std::string const& selectorName) {
|
|
|
|
return Err("Wrong platform");
|
|
|
|
}
|
|
|
|
|
2022-07-30 12:24:03 -04:00
|
|
|
#endif
|