diff --git a/.github/workflows/build-launcher(cmake-gcc).yml b/.github/workflows/build-launcher(cmake-gcc).yml
new file mode 100644
index 0000000000..537bd2509f
--- /dev/null
+++ b/.github/workflows/build-launcher(cmake-gcc).yml
@@ -0,0 +1,60 @@
+name: Build HMCLauncher (Using CMake and gcc)
+
+on:
+ push:
+ paths:
+ - 'HMCLauncher/**'
+ - '.github/workflows/build-launcher(cmake-gcc).yml'
+ pull_request:
+ paths:
+ - 'HMCLauncher/**'
+ - '.github/workflows/build-launcher(cmake-gcc).yml'
+
+jobs:
+ build:
+ runs-on: windows-latest
+ defaults:
+ run:
+ shell: msys2 {0}
+ steps:
+ - name: 'Setup MSYS2'
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: mingw32
+ update: true
+ install: >-
+ mingw-w64-i686-toolchain
+ mingw-w64-i686-ninja
+ mingw-w64-i686-cmake
+ git
+ - name: 'Checkout'
+ uses: actions/checkout@v4
+ - name: Build HMCLauncher
+ run: |
+ cmake -S ./HMCLauncher/HMCL -B ./HMCLauncher/HMCL/build -DCMAKE_BUILD_TYPE=Release
+ cmake --build ./HMCLauncher/HMCL/build
+ - name: Copy HMCLauncher to assets
+ run: cp ./HMCLauncher/HMCL/build/HMCLauncher.exe ./HMCL/src/main/resources/assets/HMCLauncher.exe
+ - name: Set up JDK 11
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: '11'
+ java-package: 'jdk+fx'
+ - name: Build with Gradle
+ run: ./gradlew makeExecutables --no-daemon
+ env:
+ MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }}
+ MICROSOFT_AUTH_SECRET: ${{ secrets.MICROSOFT_AUTH_SECRET }}
+ CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
+ - name: Get short SHA
+ uses: benjlevesque/short-sha@v3.0
+ with:
+ length: 7
+ - name: Copy HMCLauncher to libs
+ run: cp ./HMCLauncher/HMCL/build/HMCLauncher.exe ./HMCL/build/libs/HMCLauncher.exe
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: HMCLauncher-${{ env.SHA }}
+ path: HMCL/build/libs/*.exe
\ No newline at end of file
diff --git a/HMCL/src/main/resources/assets/HMCLauncher.exe b/HMCL/src/main/resources/assets/HMCLauncher.exe
index 1278064551..48cedda62c 100644
Binary files a/HMCL/src/main/resources/assets/HMCLauncher.exe and b/HMCL/src/main/resources/assets/HMCLauncher.exe differ
diff --git a/HMCLauncher/HMCL/CMakeLists.txt b/HMCLauncher/HMCL/CMakeLists.txt
new file mode 100644
index 0000000000..b316635f95
--- /dev/null
+++ b/HMCLauncher/HMCL/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.25)
+project(HMCLauncher)
+if(MSVC)
+ add_compile_options(/utf-8 /D_UNICODE /W4)
+else()
+ add_compile_options(-municode -Wall -Wextra -Wpedantic)
+endif()
+if(MSVC)
+ add_link_options(/ENTRY:wWinMainCRTStartup)
+else()
+ add_link_options(-municode)
+endif()
+OPTION(ENABLE_MINGW_STATIC_LINK_LIBSTDCXX "Link the C++ standard library statically to the executable file(mingw only)." ON)
+if(ENABLE_MINGW_STATIC_LINK_LIBSTDCXX AND MINGW)
+ add_link_options(-static)
+endif()
+set(CMAKE_WIN32_EXECUTABLE ON)
+add_executable(HMCLauncher WIN32 HMCL.rc java.cpp main.cpp os.cpp install.cpp stdafx.cpp Version.cpp)
+target_link_libraries(HMCLauncher Version)
+target_link_libraries(HMCLauncher wininet)
+install(TARGETS HMCLauncher)
\ No newline at end of file
diff --git a/HMCLauncher/HMCL/HMCL.vcxproj b/HMCLauncher/HMCL/HMCL.vcxproj
index fe4858207d..d05ea073ec 100644
--- a/HMCLauncher/HMCL/HMCL.vcxproj
+++ b/HMCLauncher/HMCL/HMCL.vcxproj
@@ -96,7 +96,7 @@
Windows
true
- version.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;%(AdditionalDependencies)
+ version.lib;wininet.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;%(AdditionalDependencies)
@@ -134,7 +134,7 @@
true
true
true
- version.lib;%(AdditionalDependencies)
+ version.lib;wininet.lib;%(AdditionalDependencies)
@@ -160,6 +160,7 @@
+
@@ -170,6 +171,7 @@
+
diff --git a/HMCLauncher/HMCL/HMCL.vcxproj.filters b/HMCLauncher/HMCL/HMCL.vcxproj.filters
index fea247b7a7..18791dc943 100644
--- a/HMCLauncher/HMCL/HMCL.vcxproj.filters
+++ b/HMCLauncher/HMCL/HMCL.vcxproj.filters
@@ -33,6 +33,9 @@
头文件
+
+ 头文件
+
头文件
@@ -53,6 +56,9 @@
源文件
+
+ 源文件
+
源文件
diff --git a/HMCLauncher/HMCL/Version.h b/HMCLauncher/HMCL/Version.h
index 5236904020..e289dcb009 100644
--- a/HMCLauncher/HMCL/Version.h
+++ b/HMCLauncher/HMCL/Version.h
@@ -28,4 +28,10 @@ class Version {
if (ver[i] != other.ver[i]) return ver[i] < other.ver[i];
return true;
}
+
+ bool operator>=(const Version &other) const {
+ for (int i = 0; i < 4; ++i)
+ if (ver[i] != other.ver[i]) return ver[i] > other.ver[i];
+ return true;
+ }
};
diff --git a/HMCLauncher/HMCL/install.cpp b/HMCLauncher/HMCL/install.cpp
new file mode 100644
index 0000000000..55667139ef
--- /dev/null
+++ b/HMCLauncher/HMCL/install.cpp
@@ -0,0 +1,189 @@
+#include "stdafx.h"
+#include "install.h"
+
+int InstallHMCLJRE(const std::wstring &home, const std::wstring &domain,
+ const std::wstring &file) {
+ WIN32_FIND_DATA ffd;
+ std::wstring runtimeDirectory, jreDownloadedFile, jreDownloadedDirectory;
+
+ runtimeDirectory = home + L"runtime";
+ jreDownloadedFile = home + L".hmclauncher-jre-";
+ jreDownloadedFile.append(std::to_wstring(rand()));
+ jreDownloadedDirectory = jreDownloadedFile + L"";
+ jreDownloadedFile.append(L".zip");
+
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+
+ int err = 0;
+
+ err = DownloadHMCLJRE(jreDownloadedFile, domain, file);
+ if (err != 0) {
+ goto cleanup;
+ }
+
+ if (!CreateDirectory(jreDownloadedDirectory.c_str(), NULL)) {
+ err = GetLastError();
+ goto cleanup;
+ }
+
+ UncompressHMCLJRE(jreDownloadedFile, jreDownloadedDirectory);
+
+ hFind = FindFirstFile((jreDownloadedDirectory + L"\\*").c_str(), &ffd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ err = GetLastError();
+ goto cleanup;
+ }
+
+ {
+ std::wstring targetFile = std::wstring();
+ do {
+ std::wstring fileName = std::wstring(ffd.cFileName);
+ if (fileName.size() <= 2 && fileName[0] == L'.') {
+ continue;
+ }
+ if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (targetFile.size() != 0) {
+ err = HMCL_ERR_INVALID_JRE_PACK;
+ goto cleanup;
+ }
+
+ targetFile.append(fileName);
+ }
+ } while (FindNextFile(hFind, &ffd) != 0);
+ err = GetLastError();
+ if (err != ERROR_NO_MORE_FILES) {
+ goto cleanup;
+ }
+ err = 0;
+
+ if (targetFile.size() == 0) {
+ err = HMCL_ERR_INVALID_JRE_PACK;
+ goto cleanup;
+ }
+
+ if (!MoveFile((jreDownloadedDirectory + L'\\' + targetFile).c_str(), runtimeDirectory.c_str())) {
+ err = GetLastError();
+ goto cleanup;
+ }
+ }
+
+ cleanup:
+ if (hFind != INVALID_HANDLE_VALUE) {
+ CloseHandle(hFind);
+ }
+ RemoveDirectory(jreDownloadedDirectory.c_str());
+ DeleteFile(jreDownloadedFile.c_str());
+ return err;
+}
+
+int DownloadHMCLJRE(std::wstring &jreDownloadedFile, const std::wstring &domain,
+ const std::wstring &file) {
+ int err = 0;
+ HINTERNET hInternet = NULL, hConnection = NULL, hRequest = NULL;
+ byte buffer[4096];
+ HANDLE fd = NULL;
+
+ {
+ hInternet = InternetOpen(L"HMCLauncher", INTERNET_OPEN_TYPE_PRECONFIG, NULL,
+ NULL, 0);
+ if (hInternet == NULL) {
+ err = GetLastError();
+ goto cleanup;
+ }
+ hConnection =
+ InternetConnect(hInternet, domain.c_str(), INTERNET_DEFAULT_HTTPS_PORT,
+ NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
+ if (hConnection == NULL) {
+ err = GetLastError();
+ goto cleanup;
+ }
+
+ const wchar_t *ACCEPTS[] = {L"application/zip", NULL};
+ hRequest = HttpOpenRequest(hConnection, L"GET", file.c_str(), NULL, NULL,
+ ACCEPTS, INTERNET_FLAG_SECURE, 0);
+ if (hRequest == NULL) {
+ err = GetLastError();
+ goto cleanup;
+ }
+
+ if (!HttpSendRequest(hRequest, NULL, 0, NULL, 0)) {
+ err = GetLastError();
+ goto cleanup;
+ }
+
+ {
+ DWORD receivedCount, unused;
+
+ fd = CreateFile(jreDownloadedFile.c_str(), GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (fd == INVALID_HANDLE_VALUE) {
+ err = GetLastError();
+ goto cleanup;
+ }
+
+ while (InternetReadFile(hRequest, buffer, 4096, &receivedCount) &&
+ receivedCount) {
+ if (!WriteFile(fd, buffer, receivedCount, &unused, NULL)) {
+ err = GetLastError();
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+cleanup:
+ if (hRequest != NULL) {
+ InternetCloseHandle(hRequest);
+ }
+ if (hConnection != NULL) {
+ InternetCloseHandle(hConnection);
+ }
+ if (hInternet != NULL) {
+ InternetCloseHandle(hInternet);
+ }
+ if (fd != NULL) {
+ CloseHandle(fd);
+ }
+ return err;
+}
+
+int UncompressHMCLJRE(std::wstring jreDownloadedFile, std::wstring jreDownloadedDirectory) {
+ std::wstring command = L"tar.exe -xf \"" + jreDownloadedFile + L"\"";
+ int err = 0;
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ si.cb = sizeof(si);
+ ZeroMemory(&si, sizeof(si));
+ ZeroMemory(&pi, sizeof(pi));
+ if (!CreateProcess(NULL, &command[0], NULL, NULL, false,
+ NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, jreDownloadedDirectory.c_str(), &si,
+ &pi)) {
+ return GetLastError();
+ }
+
+ while (true) {
+ DWORD exitCode;
+ if (!GetExitCodeProcess(pi.hProcess, &exitCode)) {
+ err = GetLastError();
+ goto cleanup;
+ }
+ if (exitCode != STILL_ACTIVE) {
+ if (exitCode != 0) {
+ err = exitCode;
+ goto cleanup;
+ }
+ break;
+ }
+ }
+
+ cleanup:
+ CloseHandle(si.hStdInput);
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdError);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ return err;
+}
\ No newline at end of file
diff --git a/HMCLauncher/HMCL/install.h b/HMCLauncher/HMCL/install.h
new file mode 100644
index 0000000000..7c904309f6
--- /dev/null
+++ b/HMCLauncher/HMCL/install.h
@@ -0,0 +1,11 @@
+#include "stdafx.h"
+
+#define HMCL_ERR_INVALID_JRE_PACK -129
+
+int InstallHMCLJRE(const std::wstring &home, const std::wstring &domain,
+ const std::wstring &file);
+
+int DownloadHMCLJRE(std::wstring &target, const std::wstring &domain,
+ const std::wstring &file);
+
+int UncompressHMCLJRE(std::wstring jreDownloadedFile, std::wstring jreDownloadedDirectory);
\ No newline at end of file
diff --git a/HMCLauncher/HMCL/java.cpp b/HMCLauncher/HMCL/java.cpp
index 067c12fb2a..bd3df3d07a 100644
--- a/HMCLauncher/HMCL/java.cpp
+++ b/HMCLauncher/HMCL/java.cpp
@@ -5,14 +5,110 @@
const Version JAVA_8(L"1.8"), JAVA_11(L"11");
-const LPCWSTR JDK_NEW = L"SOFTWARE\\JavaSoft\\JDK";
-const LPCWSTR JRE_NEW = L"SOFTWARE\\JavaSoft\\JRE";
-const LPCWSTR JDK_OLD = L"SOFTWARE\\JavaSoft\\Java Development Kit";
-const LPCWSTR JRE_OLD = L"SOFTWARE\\JavaSoft\\Java Runtime Environment";
+const LPCWSTR JDK_HKEYS[] = {L"SOFTWARE\\JavaSoft\\JDK",
+ L"SOFTWARE\\JavaSoft\\JRE",
+ L"SOFTWARE\\JavaSoft\\Java Development Kit",
+ L"SOFTWARE\\JavaSoft\\Java Runtime Environment"};
-bool oldJavaFound = false;
+const LPCWSTR VENDOR_DIRS[] = {L"Java", L"Microsoft", L"BellSoft",
+ L"Zulu", L"Eclipse Foundation", L"AdoptOpenJDK",
+ L"Semeru"};
-bool FindJavaByRegistryKey(HKEY rootKey, LPCWSTR subKey, std::wstring& path) {
+const LPCWSTR PROGRAM_DIRS[] = {L"ProgramFiles", L"ProgramFiles(x86)",
+ L"ProgramW6432"};
+
+/* Here we find the java from Environment Variable and Registry, which is fast,
+ * and store the result to 'path'. */
+void ScanJava(SYSTEM_INFO& systemInfo, std::wstring& result, int& status) {
+ status = JAVA_STATUS_NOT_FOUND;
+
+ // If 'HMCL_JAVA_HOME' is settled, this value MUST be used without any check as it can be used.
+ if (ERROR_SUCCESS == MyGetEnvironmentVariable(L"HMCL_JAVA_HOME", result)) {
+ MyPathNormalize(result);
+ status = JAVA_STATUS_BEST;
+ return;
+ }
+
+ {
+ std::wstring buffer;
+ if (SUCCEEDED(MySHGetFolderPath(CSIDL_APPDATA, buffer)) ||
+ SUCCEEDED(MySHGetFolderPath(CSIDL_PROFILE, buffer))) {
+ MyPathNormalize(buffer);
+ MyPathAppend(buffer, L".hmcl\\runtime");
+ MyPathAddBackslash(buffer);
+
+ if (DeterminJavaHome(buffer, result, status) &&
+ status != JAVA_STATUS_NOT_FOUND) {
+ return;
+ }
+ }
+ }
+
+ std::wstring currentResult;
+ if (ERROR_SUCCESS == MyGetEnvironmentVariable(L"JAVA_HOME", currentResult)) {
+ MyPathNormalize(result);
+ if (DeterminJavaHome(currentResult, result, status) &&
+ status == JAVA_STATUS_BEST) {
+ return;
+ }
+ }
+
+ for (LPCWSTR hkey : JDK_HKEYS) {
+ ScanJavaRegistry(HKEY_LOCAL_MACHINE, hkey, result, status);
+ if (status == JAVA_STATUS_BEST) {
+ return;
+ }
+ }
+
+ std::wstring envPath;
+ if (ERROR_SUCCESS == MyGetEnvironmentVariable(L"PATH", envPath)) {
+ int length = envPath.size();
+ int lastI = 0;
+ for (int i = 0; i < length; i++) {
+ if (envPath[i] == L';') {
+ int partL = i - lastI;
+ if (partL > 0) {
+ std::wstring part = envPath.substr(lastI, partL);
+ MyPathNormalize(part);
+ MyPathAddBackslash(part);
+ int partL2 = part.size();
+ if (part[partL2 - 5] == L'\\' && part[partL2 - 4] == L'b' &&
+ part[partL2 - 3] == L'i' && part[partL2 - 2] == L'n' &&
+ part[partL2 - 1] == L'\\') {
+ part.resize(partL2 - 5);
+ if (DeterminJavaHome(part, result, status) &&
+ status == JAVA_STATUS_BEST) {
+ return;
+ }
+ }
+ }
+
+ lastI = i + 1;
+ }
+ }
+ }
+
+ std::wstring root;
+ for (LPCWSTR program : PROGRAM_DIRS) {
+ if (ERROR_SUCCESS != MyGetEnvironmentVariable(program, root)) {
+ continue;
+ }
+
+ MyPathNormalize(root);
+ for (LPCWSTR vender : VENDOR_DIRS) {
+ std::wstring currentRoot = root + L"";
+ MyPathAppend(currentRoot, vender);
+ MyPathAddBackslash(currentRoot);
+ ScanJavaFolder(currentRoot, result, status);
+ if (status == JAVA_STATUS_BEST) {
+ return;
+ }
+ }
+ }
+}
+
+void ScanJavaRegistry(HKEY rootKey, LPCWSTR subKey, std::wstring& path,
+ int& status) {
WCHAR javaVer[MAX_KEY_LENGTH]; // buffer for subkey name, special for
// JavaVersion
DWORD cbName; // size of name string
@@ -25,9 +121,10 @@ bool FindJavaByRegistryKey(HKEY rootKey, LPCWSTR subKey, std::wstring& path) {
HKEY hKey;
if (ERROR_SUCCESS !=
- (result =
- RegOpenKeyEx(rootKey, subKey, 0, KEY_WOW64_64KEY | KEY_READ, &hKey)))
- return false;
+ (result = RegOpenKeyEx(rootKey, subKey, 0, KEY_WOW64_64KEY | KEY_READ,
+ &hKey))) {
+ return;
+ }
RegQueryInfoKey(hKey, // key handle
NULL, // buffer for class name
@@ -42,9 +139,10 @@ bool FindJavaByRegistryKey(HKEY rootKey, LPCWSTR subKey, std::wstring& path) {
NULL, // security descriptor
NULL); // last write time
- if (!cSubKeys) return false;
+ if (!cSubKeys) {
+ return;
+ }
- bool flag = false;
for (DWORD i = 0; i < cSubKeys; ++i) {
cbName = MAX_KEY_LENGTH;
if (ERROR_SUCCESS != (result = RegEnumKeyEx(hKey, i, javaVer, &cbName, NULL,
@@ -55,30 +153,60 @@ bool FindJavaByRegistryKey(HKEY rootKey, LPCWSTR subKey, std::wstring& path) {
if (ERROR_SUCCESS != RegOpenKeyEx(hKey, javaVer, 0, KEY_READ, &javaKey))
continue;
- if (ERROR_SUCCESS == MyRegQueryValue(javaKey, L"JavaHome", REG_SZ, path)) {
- if (Version(javaVer) < JAVA_8)
- oldJavaFound = true;
- else
- flag = true;
- }
+ std::wstring currentPath;
+ if (ERROR_SUCCESS ==
+ MyRegQueryValue(javaKey, L"JavaHome", REG_SZ, currentPath)) {
+ MyPathNormalize(currentPath);
+ Version v = Version(javaVer);
- if (flag) break;
+ if (status < JAVA_STATUS_BEST && v >= JAVA_11) {
+ path = currentPath;
+ status = JAVA_STATUS_BEST;
+ break;
+ } else if (status < JAVA_STATUS_USABLE && v >= JAVA_8) {
+ path = currentPath;
+ status = JAVA_STATUS_USABLE;
+ }
+ }
}
RegCloseKey(hKey);
-
- return flag;
}
-bool FindJavaInRegistry(std::wstring& path) {
- return FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JDK_NEW, path) ||
- FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JRE_NEW, path) ||
- FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JDK_OLD, path) ||
- FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JRE_OLD, path);
-}
+void ScanJavaFolder(std::wstring root, std::wstring& result, int& status) {
+ WIN32_FIND_DATA data;
+ HANDLE hFind =
+ FindFirstFile((root + L"*").c_str(), &data); // Search all subdirectory
-bool FindJava(std::wstring& path) {
- return ERROR_SUCCESS == MyGetEnvironmentVariable(L"HMCL_JAVA_HOME", path) ||
- ERROR_SUCCESS == MyGetEnvironmentVariable(L"JAVA_HOME", path) ||
- FindJavaInRegistry(path);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (DeterminJavaHome(root + data.cFileName, result, status) &&
+ status == JAVA_STATUS_BEST) {
+ goto done;
+ }
+ } while (FindNextFile(hFind, &data));
+
+ done:
+ FindClose(hFind);
+ }
}
+
+boolean DeterminJavaHome(std::wstring target, std::wstring& result,
+ int& status) {
+ Version version(L"");
+ std::wstring currentRoot = target + L"";
+ MyPathAppend(currentRoot, std::wstring(L"bin\\javaw.exe"));
+
+ if (!MyGetFileVersionInfo(currentRoot, version)) return false;
+
+ if (status < JAVA_STATUS_BEST && version >= JAVA_11) {
+ result = target;
+ status = JAVA_STATUS_BEST;
+ return true;
+ } else if (status < JAVA_STATUS_USABLE && version >= JAVA_8) {
+ result = target;
+ status = JAVA_STATUS_USABLE;
+ return true;
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/HMCLauncher/HMCL/java.h b/HMCLauncher/HMCL/java.h
index eb0a289f97..2985061cfb 100644
--- a/HMCLauncher/HMCL/java.h
+++ b/HMCLauncher/HMCL/java.h
@@ -2,8 +2,18 @@
#include
#include
-// Find Java installation in system registry
-bool FindJavaInRegistry(std::wstring &path);
+// These increasing values represent the priority of the Java.
+// Be careful while changing their values!
+const int JAVA_STATUS_NOT_FOUND = 0;
+const int JAVA_STATUS_USABLE = 1; // Java 8 - 11 (Exclude)
+const int JAVA_STATUS_BEST = 2; // Java 11 (Include) - ++
-// Find Java Installation in registry and environment variable
-bool FindJava(std::wstring &path);
+void ScanJava(SYSTEM_INFO& systemInfo, std::wstring& result, int& status);
+
+void ScanJavaRegistry(HKEY rootKey, LPCWSTR subKey, std::wstring& path,
+ int& status);
+
+void ScanJavaFolder(std::wstring root, std::wstring& result, int& status);
+
+boolean DeterminJavaHome(std::wstring target, std::wstring& result,
+ int& status);
\ No newline at end of file
diff --git a/HMCLauncher/HMCL/main.cpp b/HMCLauncher/HMCL/main.cpp
index 78bdc48f47..fd1f9e1cba 100644
--- a/HMCLauncher/HMCL/main.cpp
+++ b/HMCLauncher/HMCL/main.cpp
@@ -3,60 +3,58 @@
#include "os.h"
#include "java.h"
#include "lang.h"
+#include "install.h"
#include
Version J8(TEXT("8"));
extern "C" {
+#ifdef _MSC_VER
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
+#else
+__attribute__ ((dllexport)) DWORD NvOptimusEnablement = 0x00000001;
+__attribute__ ((dllexport)) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
+#endif
}
-LPCWSTR VENDOR_DIRS[] = {
- L"Java", L"Microsoft", L"BellSoft", L"Zulu", L"Eclipse Foundation", L"AdoptOpenJDK", L"Semeru"
-};
+void LaunchHMCLDirect(const std::wstring &javaPath, const std::wstring &workdir,
+ const std::wstring &jarPath,
+ const std::wstring &jvmOptions) {
+ HANDLE hProcess = MyCreateProcess(L"\"" + javaPath + L"\" " + jvmOptions + L" -jar \"" +
+ jarPath + L"\"", workdir);
+ if (hProcess == INVALID_HANDLE_VALUE) {
+ return;
+ }
-void RawLaunchJVM(const std::wstring &javaPath, const std::wstring &workdir,
- const std::wstring &jarPath, const std::wstring &jvmOptions) {
- if (MyCreateProcess(L"\"" + javaPath + L"\" " + jvmOptions + L" -jar \"" + jarPath + L"\"", workdir))
+ Sleep(5000);
+ DWORD exitCode;
+ if (!GetExitCodeProcess(hProcess, &exitCode)) {
+ GetLastError();
+ CloseHandle(hProcess);
+ return;
+ }
+ CloseHandle(hProcess);
+ if (exitCode == STILL_ACTIVE || exitCode == 0) {
exit(EXIT_SUCCESS);
+ }
}
-void LaunchJVM(const std::wstring &javaPath, const std::wstring &workdir,
- const std::wstring &jarPath, const std::wstring &jvmOptions) {
+void LaunchHMCL(const std::wstring &javaPath, const std::wstring &workdir,
+ const std::wstring &jarPath, const std::wstring &jvmOptions) {
Version javaVersion(L"");
if (!MyGetFileVersionInfo(javaPath, javaVersion)) return;
if (J8 <= javaVersion) {
- RawLaunchJVM(javaPath, workdir, jarPath, jvmOptions);
- }
-}
-
-void FindJavaInDirAndLaunchJVM(const std::wstring &baseDir, const std::wstring &workdir,
- const std::wstring &jarPath, const std::wstring &jvmOptions) {
- std::wstring pattern = baseDir + L"*";
-
- WIN32_FIND_DATA data;
- HANDLE hFind = FindFirstFile(pattern.c_str(), &data); // Search all subdirectory
-
- if (hFind != INVALID_HANDLE_VALUE) {
- do {
- std::wstring javaw = baseDir + data.cFileName + std::wstring(L"\\bin\\javaw.exe");
- if (FindFirstFileExists(javaw.c_str(), 0)) {
- LaunchJVM(javaw, workdir, jarPath, jvmOptions);
- }
- } while (FindNextFile(hFind, &data));
- FindClose(hFind);
+ LaunchHMCLDirect(javaPath, workdir, jarPath, jvmOptions);
}
}
-void OpenHelpPage() {
- ShellExecute(0, 0, L"https://docs.hmcl.net/help.html", 0, 0, SW_SHOW);
-}
-
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
- std::wstring path, exeName, jvmOptions;
+ srand(GetTickCount64());
+
+ std::wstring exeName, jvmOptions;
// Since Jar file is appended to this executable, we should first get the
// location of JAR file.
@@ -69,88 +67,65 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
exeName = exeName.substr(last_slash + 1);
}
- if (ERROR_SUCCESS != MyGetEnvironmentVariable(L"HMCL_JAVA_OPTS", jvmOptions)) {
- jvmOptions = L"-XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=15"; // Default Options
+ if (ERROR_SUCCESS !=
+ MyGetEnvironmentVariable(L"HMCL_JAVA_OPTS", jvmOptions)) {
+ jvmOptions =
+ L"-XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=15"; // Default Options
}
- bool useChinese = GetUserDefaultUILanguage() == 2052; // zh-CN
+ bool useChinese = GetUserDefaultUILanguage() == 2052; // zh-CN
SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo);
// TODO: check whether the bundled JRE is valid.
- // First try the Java packaged together.
- bool isX64 = (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);
- bool isARM64 = (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64);
+
+ // First, try the Java packaged together.
+ bool isX64 =
+ (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);
+ bool isARM64 =
+ (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64);
if (isARM64) {
- RawLaunchJVM(L"jre-arm64\\bin\\javaw.exe", workdir, exeName, jvmOptions);
+ LaunchHMCLDirect(L"jre-arm64\\bin\\java.exe", workdir, exeName,
+ jvmOptions);
}
if (isX64) {
- RawLaunchJVM(L"jre-x64\\bin\\javaw.exe", workdir, exeName, jvmOptions);
+ LaunchHMCLDirect(L"jre-x64\\bin\\java.exe", workdir, exeName, jvmOptions);
}
- RawLaunchJVM(L"jre-x86\\bin\\javaw.exe", workdir, exeName, jvmOptions);
+ LaunchHMCLDirect(L"jre-x86\\bin\\java.exe", workdir, exeName, jvmOptions);
- if (FindJava(path)) LaunchJVM(path + L"\\bin\\javaw.exe", workdir, exeName, jvmOptions);
+ // Next, try the Java installed on this computer.
+ std::wstring path;
+ int status;
- std::wstring programFiles;
+ ScanJava(systemInfo, path, status);
- // Or we try to search Java in C:\Program Files
- if (!SUCCEEDED(MySHGetFolderPath(CSIDL_PROGRAM_FILES, programFiles))) programFiles = L"C:\\Program Files\\";
- for (LPCWSTR vendorDir : VENDOR_DIRS) {
- std::wstring dir = programFiles;
- MyPathAppend(dir, vendorDir);
- MyPathAddBackslash(dir);
-
- FindJavaInDirAndLaunchJVM(dir, workdir, exeName, jvmOptions);
- }
-
- // Consider C:\Program Files (x86)
- if (!SUCCEEDED(MySHGetFolderPath(CSIDL_PROGRAM_FILESX86, programFiles))) programFiles = L"C:\\Program Files (x86)\\";
- for (LPCWSTR vendorDir : VENDOR_DIRS) {
- std::wstring dir = programFiles;
- MyPathAppend(dir, vendorDir);
- MyPathAddBackslash(dir);
-
- FindJavaInDirAndLaunchJVM(dir, workdir, exeName, jvmOptions);
+ if (status != JAVA_STATUS_NOT_FOUND) {
+ MyPathAppend(path, std::wstring(L"bin\\java.exe"));
+ LaunchHMCL(path, workdir, exeName, jvmOptions);
}
// Try java in PATH
- RawLaunchJVM(L"javaw", workdir, exeName, jvmOptions);
-
- std::wstring hmclJavaDir;
- {
- std::wstring buffer;
- if (SUCCEEDED(MySHGetFolderPath(CSIDL_APPDATA, buffer)) || SUCCEEDED(MySHGetFolderPath(CSIDL_PROFILE, buffer))) {
- MyPathAppend(buffer, L".hmcl");
- MyPathAppend(buffer, L"java");
- if (isARM64) {
- MyPathAppend(buffer, L"windows-arm64");
- } else if (isX64) {
- MyPathAppend(buffer, L"windows-x86_64");
- } else {
- MyPathAppend(buffer, L"windows-x86");
- }
- MyPathAddBackslash(buffer);
- hmclJavaDir = buffer;
+ LaunchHMCLDirect(L"java", workdir, exeName, jvmOptions);
+
+ std::wstring home;
+ if (SUCCEEDED(MySHGetFolderPath(CSIDL_APPDATA, home)) ||
+ SUCCEEDED(MySHGetFolderPath(CSIDL_PROFILE, home))) {
+ MyPathNormalize(home);
+ MyPathAppend(home, L".hmcl\\");
+
+ std::wstring file = L"";
+ if (isARM64) {
+ file = L"/java/11.0.24+9/bellsoft-jre11.0.24+9-windows-aarch64-full.zip";
+ } else if (isX64) {
+ file = L"/java/11.0.24+9/bellsoft-jre11.0.24+9-windows-i586-full.zip";
+ } else {
+ file = L"/java/11.0.24+9/bellsoft-jre11.0.24+9-windows-amd64-full.zip";
}
- }
+ InstallHMCLJRE(home, L"download.bell-sw.com", file);
- if (!hmclJavaDir.empty()) {
- FindJavaInDirAndLaunchJVM(hmclJavaDir, workdir, exeName, jvmOptions);
+ LaunchHMCLDirect(home + L"runtime\\bin\\java.exe", workdir, exeName, jvmOptions);
}
- LPCWSTR downloadLink;
-
- if (isARM64) {
- downloadLink = L"https://docs.hmcl.net/downloads/windows/arm64.html";
- } if (isX64) {
- downloadLink = L"https://docs.hmcl.net/downloads/windows/x86_64.html";
- } else {
- downloadLink = L"https://docs.hmcl.net/downloads/windows/x86.html";
- }
-
- if (IDOK == MessageBox(NULL, useChinese ? ERROR_PROMPT_ZH : ERROR_PROMPT, useChinese ? ERROR_TITLE_ZH : ERROR_TITLE, MB_ICONWARNING | MB_OKCANCEL)) {
- ShellExecute(0, 0, downloadLink, 0, 0, SW_SHOW);
- }
- return 1;
+ return 0;
}
diff --git a/HMCLauncher/HMCL/main.h b/HMCLauncher/HMCL/main.h
index d00d47e788..49ab1a0c81 100644
--- a/HMCLauncher/HMCL/main.h
+++ b/HMCLauncher/HMCL/main.h
@@ -1,3 +1,3 @@
#pragma once
-#include "resource.h"
+#include "resource.h"
\ No newline at end of file
diff --git a/HMCLauncher/HMCL/os.cpp b/HMCLauncher/HMCL/os.cpp
index d632b422c8..2bcab02f79 100644
--- a/HMCLauncher/HMCL/os.cpp
+++ b/HMCLauncher/HMCL/os.cpp
@@ -30,21 +30,29 @@ LSTATUS MyGetModuleFileName(HMODULE hModule, std::wstring &out) {
}
LSTATUS MyGetEnvironmentVariable(LPCWSTR name, std::wstring &out) {
- DWORD res, size = MAX_PATH;
out = std::wstring();
- out.resize(size);
- while ((res = GetEnvironmentVariable(name, &out[0], size)) == size) {
- out.resize(size += MAX_PATH);
- }
- if (res == 0)
- return GetLastError();
- else {
- out.resize(size - MAX_PATH + res);
- return ERROR_SUCCESS;
+ int size = MAX_PATH;
+ while (true) {
+ out.resize(size);
+ DWORD res = GetEnvironmentVariable(name, &out[0], size);
+ if (res == 0) {
+ return GetLastError();
+ } else if (res == size) {
+ // This should NOT happen.
+ // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable
+ return ERROR_INVALID_FUNCTION;
+ } else {
+ out.resize(res);
+ if (res < size) {
+ return ERROR_SUCCESS;
+ }
+ // Allocate enough memory and try again.
+ size = res;
+ }
}
}
-bool MyCreateProcess(const std::wstring &command, const std::wstring &workdir) {
+HANDLE MyCreateProcess(const std::wstring &command, const std::wstring &workdir) {
std::wstring writable_command = command;
STARTUPINFO si;
PROCESS_INFORMATION pi;
@@ -52,14 +60,17 @@ bool MyCreateProcess(const std::wstring &command, const std::wstring &workdir) {
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
- if (workdir.empty()) {
- return CreateProcess(NULL, &writable_command[0], NULL, NULL, false,
- NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
- } else {
- return CreateProcess(NULL, &writable_command[0], NULL, NULL, false,
- NORMAL_PRIORITY_CLASS, NULL, workdir.c_str(), &si,
- &pi);
+ if (!CreateProcess(NULL, &writable_command[0], NULL, NULL, false,
+ NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, workdir.empty() ? NULL : workdir.c_str(), &si, &pi)) {
+ return INVALID_HANDLE_VALUE;
}
+
+ CloseHandle(si.hStdInput);
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdError);
+ CloseHandle(pi.hThread);
+
+ return pi.hProcess;
}
bool FindFirstFileExists(LPCWSTR lpPath, DWORD dwFilter) {
@@ -111,6 +122,15 @@ HRESULT MySHGetFolderPath(int csidl, std::wstring &out) {
return res;
}
+void MyPathNormalize(std::wstring &path) {
+ int size = path.size();
+ for (int i = 0; i < size; i++) {
+ if (path[i] == L'/') {
+ path[i] = L'\\';
+ }
+ }
+}
+
void MyPathAppend(std::wstring &filePath, const std::wstring &more) {
if (filePath.back() != L'\\') {
filePath += L'\\';
@@ -125,7 +145,8 @@ void MyPathAddBackslash(std::wstring &filePath) {
}
}
-LSTATUS MyGetTempFile(const std::wstring &prefixString, const std::wstring &ext, std::wstring &out) {
+LSTATUS MyGetTempFile(const std::wstring &prefixString, const std::wstring &ext,
+ std::wstring &out) {
out.resize(MAX_PATH);
DWORD res = GetTempPath(MAX_PATH, &out[0]);
if (res == 0) {
@@ -140,7 +161,7 @@ LSTATUS MyGetTempFile(const std::wstring &prefixString, const std::wstring &ext,
WCHAR buffer[MAX_PATH];
int n = StringFromGUID2(guid, buffer, MAX_PATH);
if (n == 0) {
- return CO_E_PATHTOOLONG;
+ return CO_E_PATHTOOLONG;
}
MyPathAddBackslash(out);
@@ -152,7 +173,8 @@ LSTATUS MyGetTempFile(const std::wstring &prefixString, const std::wstring &ext,
return ERROR_SUCCESS;
}
-void MyAppendPathToCommandLine(std::wstring &commandLine, const std::wstring &path) {
+void MyAppendPathToCommandLine(std::wstring &commandLine,
+ const std::wstring &path) {
commandLine += L'"';
for (size_t i = 0; i < path.size(); i++) {
WCHAR ch = path[i];
diff --git a/HMCLauncher/HMCL/os.h b/HMCLauncher/HMCL/os.h
index d0c2fcd63e..f664ac9f0b 100644
--- a/HMCLauncher/HMCL/os.h
+++ b/HMCLauncher/HMCL/os.h
@@ -25,14 +25,16 @@ LSTATUS MyGetModuleFileName(HMODULE hModule, std::wstring &out);
LSTATUS MyGetEnvironmentVariable(LPCWSTR name, std::wstring &out);
// Create process by invoking CreateProcess, only pass command.
-bool MyCreateProcess(const std::wstring &command, const std::wstring &workdir);
+HANDLE MyCreateProcess(const std::wstring &command, const std::wstring &workdir);
// Check if file lpPath exists.
bool FindFirstFileExists(LPCWSTR lpPath, DWORD dwFilter);
+HRESULT MySHGetFolderPath(int csidl, std::wstring &out);
+
bool MyGetFileVersionInfo(const std::wstring &filePath, Version &version);
-HRESULT MySHGetFolderPath(int csidl, std::wstring &out);
+void MyPathNormalize(std::wstring& path);
void MyPathAppend(std::wstring &filePath, const std::wstring &more);
diff --git a/HMCLauncher/HMCL/stdafx.h b/HMCLauncher/HMCL/stdafx.h
index b7aace52a3..05c9439e66 100644
--- a/HMCLauncher/HMCL/stdafx.h
+++ b/HMCLauncher/HMCL/stdafx.h
@@ -3,5 +3,6 @@
#include "targetver.h"
#include
+#include
#include
-#include
+#include
\ No newline at end of file