main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. #include <Windows.h>
  2. #include <d3d11.h>
  3. #pragma comment(lib, "d3d11.lib")
  4. #include <sstream>
  5. #include <iostream>
  6. #include <filesystem>
  7. #include <optional>
  8. #include <fstream>
  9. #include <commdlg.h>
  10. #include <tchar.h>
  11. #include "../minty/api/json/json.hpp"
  12. namespace fs = std::filesystem;
  13. std::string mobileMode = "use_mobile_platform -is_cloud 1 -platform_type CLOUD_THIRD_PARTY_MOBILE";
  14. bool InjectStandard(HANDLE hTarget, const char* dllpath) {
  15. LPVOID loadlib = GetProcAddress(GetModuleHandle(L"kernel32"), "LoadLibraryA");
  16. LPVOID dllPathAddr = VirtualAllocEx(hTarget, nullptr, strlen(dllpath) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  17. if (dllPathAddr == nullptr) {
  18. std::cout << "Failed allocating memory in the target process. GetLastError(): " << GetLastError() << "\n";
  19. return false;
  20. }
  21. if (!WriteProcessMemory(hTarget, dllPathAddr, dllpath, strlen(dllpath) + 1, nullptr)) {
  22. std::cout << "Failed writing to process. GetLastError(): " << GetLastError() << "\n";
  23. return false;
  24. }
  25. HANDLE hThread = CreateRemoteThread(hTarget, nullptr, NULL, (LPTHREAD_START_ROUTINE)loadlib, dllPathAddr, NULL, nullptr);
  26. if (hThread == nullptr) {
  27. std::cout << "Failed to create a thread in the target process. GetLastError(): " << GetLastError() << "\n";
  28. return false;
  29. }
  30. WaitForSingleObject(hThread, INFINITE);
  31. DWORD exit_code = 0;
  32. GetExitCodeThread(hThread, &exit_code);
  33. VirtualFreeEx(hTarget, dllPathAddr, 0, MEM_RELEASE);
  34. CloseHandle(hThread);
  35. if (exit_code == 0) {
  36. std::cout << "LoadLibrary failed with exit code 0.\n";
  37. return false;
  38. }
  39. return true;
  40. }
  41. std::optional<std::string> read_whole_file(const fs::path& file) {
  42. try {
  43. std::ifstream ifs(file);
  44. if (!ifs.is_open())
  45. return std::nullopt;
  46. ifs.exceptions(std::ios::failbit);
  47. std::stringstream buf;
  48. buf << ifs.rdbuf();
  49. return buf.str();
  50. }
  51. catch (const std::ios::failure&) {
  52. return std::nullopt;
  53. }
  54. }
  55. std::optional<fs::path> this_dir() {
  56. HMODULE mod = nullptr;
  57. TCHAR path[MAX_PATH]{};
  58. if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)&this_dir, &mod)) {
  59. printf("GetModuleHandleEx failed (%i)\n", GetLastError());
  60. return std::nullopt;
  61. }
  62. if (!GetModuleFileName(mod, path, MAX_PATH)) {
  63. printf("GetModuleFileName failed (%i)\n", GetLastError());
  64. return std::nullopt;
  65. }
  66. return fs::path(path).remove_filename();
  67. }
  68. void StartAndInject(std::string exe_path, std::filesystem::path dll_path, std::string startupArguments) {
  69. PROCESS_INFORMATION proc_info{};
  70. STARTUPINFOA startup_info{};
  71. std::string command_line = exe_path + " " + startupArguments;
  72. if (CreateProcessA(nullptr, const_cast<char*>(command_line.c_str()), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup_info, &proc_info)) {
  73. InjectStandard(proc_info.hProcess, dll_path.string().c_str());
  74. ResumeThread(proc_info.hThread);
  75. CloseHandle(proc_info.hThread);
  76. CloseHandle(proc_info.hProcess);
  77. }
  78. }
  79. // Data
  80. static ID3D11Device* g_pd3dDevice = nullptr;
  81. static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
  82. static IDXGISwapChain* g_pSwapChain = nullptr;
  83. static UINT g_ResizeWidth = 0, g_ResizeHeight = 0;
  84. static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
  85. // Forward declarations of helper functions
  86. bool CreateDeviceD3D(HWND hWnd);
  87. void CleanupDeviceD3D();
  88. void CreateRenderTarget();
  89. void CleanupRenderTarget();
  90. LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  91. int main() {
  92. nlohmann::json cfg;
  93. std::optional<fs::path> current_dir = this_dir();
  94. std::filesystem::path dll_path = current_dir.value() / "minty.dll";
  95. if (!fs::is_regular_file(dll_path)) {
  96. printf("minty.dll not found\n");
  97. system("pause");
  98. return 0;
  99. }
  100. std::string exe_path;
  101. std::string startupArguments = "";
  102. fs::path settings_path = fs::current_path() / "minty.json";
  103. std::ifstream settings_file(settings_path);
  104. if (!fs::exists(settings_path)) {
  105. std::ofstream settings_file(settings_path);
  106. if (settings_file.is_open()) {
  107. // Write the executable path to the settings file
  108. cfg["general"]["execPath"] = exe_path;
  109. cfg["functions"]["Settings"]["startupArguments"] = "";
  110. settings_file << cfg.dump(4) << std::endl;
  111. exe_path = cfg["general"]["execPath"];
  112. if (!fs::is_regular_file(exe_path)) {
  113. std::cout << "File path in minty.json invalid" << std::endl;
  114. std::cout << "Please select your Game Executable" << std::endl;
  115. OPENFILENAMEA ofn{};
  116. char szFile[260]{};
  117. ZeroMemory(&ofn, sizeof(ofn));
  118. ofn.lStructSize = sizeof(ofn);
  119. ofn.lpstrFile = szFile;
  120. ofn.lpstrFile[0] = '\0';
  121. ofn.hwndOwner = NULL;
  122. ofn.nMaxFile = sizeof(szFile);
  123. ofn.lpstrFilter = "Executable\0GenshinImpact.exe;YuanShen.exe";
  124. ofn.nFilterIndex = 1;
  125. ofn.lpstrTitle = "Select Genshin Impact game executable";
  126. ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
  127. if (GetOpenFileNameA(&ofn)) {
  128. std::string(exe_path) = ofn.lpstrFile;
  129. std::ofstream settings_file("minty.json", std::ios_base::out);
  130. if (settings_file.is_open()) {
  131. cfg["general"]["execPath"] = exe_path;
  132. settings_file << cfg.dump(4) << std::endl;
  133. settings_file.close();
  134. } else {
  135. std::cout << "Error: Unable to open settings file." << std::endl;
  136. return 1;
  137. }
  138. } else {
  139. std::cout << "Error: Unable to open file dialog." << std::endl;
  140. return 1;
  141. }
  142. exe_path = cfg["general"]["execPath"];
  143. startupArguments = cfg["functions"]["Settings"]["startupArguments"];
  144. if (cfg["functions"]["Settings"]["mobileMode"] == true)
  145. StartAndInject(exe_path, dll_path, mobileMode + " " + startupArguments);
  146. else
  147. StartAndInject(exe_path, dll_path, startupArguments);
  148. return 0;
  149. }
  150. } else {
  151. std::cout << "Error: Unable to create config file." << std::endl;
  152. }
  153. }
  154. settings_file >> cfg;
  155. if (!read_whole_file(settings_path))
  156. printf("Failed reading config\n");
  157. exe_path = cfg["general"]["execPath"];
  158. if (cfg["functions"]["Settings"].find("startupArguments") != cfg["functions"]["Settings"].end())
  159. startupArguments = cfg["functions"]["Settings"]["startupArguments"];
  160. if (!fs::is_regular_file(exe_path)) {
  161. std::cout << "File path in minty.json invalid" << std::endl;
  162. std::cout << "Please select your Game Executable" << std::endl;
  163. OPENFILENAMEA ofn{};
  164. char szFile[260]{};
  165. ZeroMemory(&ofn, sizeof(ofn));
  166. ofn.lStructSize = sizeof(ofn);
  167. ofn.lpstrFile = szFile;
  168. ofn.lpstrFile[0] = '\0';
  169. ofn.hwndOwner = NULL;
  170. ofn.nMaxFile = sizeof(szFile);
  171. ofn.lpstrFilter = "Executable\0GenshinImpact.exe;YuanShen.exe";
  172. ofn.nFilterIndex = 1;
  173. ofn.lpstrTitle = "Select Genshin Impact game executable";
  174. ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
  175. if (GetOpenFileNameA(&ofn)) {
  176. std::string(exe_path) = ofn.lpstrFile;
  177. std::ofstream settings_file("minty.json", std::ios_base::out);
  178. if (settings_file.is_open()) {
  179. cfg["general"]["execPath"] = exe_path;
  180. settings_file << cfg.dump(4) << std::endl;
  181. settings_file.close();
  182. } else {
  183. std::cout << "Error: Unable to open settings file." << std::endl;
  184. return 1;
  185. }
  186. } else {
  187. std::cout << "Error: Unable to open file dialog." << std::endl;
  188. return 1;
  189. }
  190. exe_path = cfg["general"]["execPath"];
  191. }
  192. if (cfg["functions"]["Settings"]["mobileMode"] == true)
  193. StartAndInject(exe_path, dll_path, mobileMode + " " + startupArguments);
  194. else
  195. StartAndInject(exe_path, dll_path, startupArguments);
  196. return 0;
  197. }
  198. // Helper functions
  199. bool CreateDeviceD3D(HWND hWnd) {
  200. // Setup swap chain
  201. DXGI_SWAP_CHAIN_DESC sd;
  202. ZeroMemory(&sd, sizeof(sd));
  203. sd.BufferCount = 2;
  204. sd.BufferDesc.Width = 0;
  205. sd.BufferDesc.Height = 0;
  206. sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  207. sd.BufferDesc.RefreshRate.Numerator = 60;
  208. sd.BufferDesc.RefreshRate.Denominator = 1;
  209. sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
  210. sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  211. sd.OutputWindow = hWnd;
  212. sd.SampleDesc.Count = 1;
  213. sd.SampleDesc.Quality = 0;
  214. sd.Windowed = TRUE;
  215. sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
  216. UINT createDeviceFlags = 0;
  217. //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
  218. D3D_FEATURE_LEVEL featureLevel;
  219. const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
  220. HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
  221. // Try high-performance WARP software driver if hardware is not available.
  222. if (res == DXGI_ERROR_UNSUPPORTED)
  223. res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
  224. if (res != S_OK)
  225. return false;
  226. CreateRenderTarget();
  227. return true;
  228. }
  229. void CleanupDeviceD3D() {
  230. CleanupRenderTarget();
  231. if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
  232. if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
  233. if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
  234. }
  235. void CreateRenderTarget() {
  236. ID3D11Texture2D* pBackBuffer;
  237. g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
  238. g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
  239. pBackBuffer->Release();
  240. }
  241. void CleanupRenderTarget() {
  242. if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
  243. }