Path: blob/main/Natives/JavaLauncher.m
589 views
#include <dirent.h>1#include <dlfcn.h>2#include <errno.h>3#include <libgen.h>4#include <spawn.h>5#include <stdio.h>6#include <stdlib.h>7#include <string.h>8#include <sys/stat.h>9#include <unistd.h>1011#include "utils.h"1213#import "ios_uikit_bridge.h"14#import "JavaLauncher.h"15#import "LauncherPreferences.h"16#import "PLProfiles.h"1718#define fm NSFileManager.defaultManager1920extern char **environ;2122void init_loadDefaultEnv() {23/* Define default env */2425// Silent Caciocavallo NPE error in locating Android-only lib26setenv("LD_LIBRARY_PATH", "", 1);2728// Ignore mipmap for performance(?) seems does not affect iOS29//setenv("LIBGL_MIPMAP", "3", 1);3031// Disable overloaded functions hack for Minecraft 1.17+32setenv("LIBGL_NOINTOVLHACK", "1", 1);3334// Fix white color on banner and sheep, since GL4ES 1.1.535setenv("LIBGL_NORMALIZE", "1", 1);3637// Override OpenGL version to 4.1 for Zink38setenv("MESA_GL_VERSION_OVERRIDE", "4.1", 1);3940// Runs JVM in a separate thread41setenv("HACK_IGNORE_START_ON_FIRST_THREAD", "1", 1);42}4344void init_loadCustomEnv() {45NSString *envvars = getPrefObject(@"java.env_variables");46if (envvars == nil) return;47NSLog(@"[JavaLauncher] Reading custom environment variables");48for (NSString *line in [envvars componentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceCharacterSet]) {49if (![line containsString:@"="]) {50NSLog(@"[JavaLauncher] Warning: skipped empty value custom env variable: %@", line);51continue;52}53NSRange range = [line rangeOfString:@"="];54NSString *key = [line substringToIndex:range.location];55NSString *value = [line substringFromIndex:range.location+range.length];56setenv(key.UTF8String, value.UTF8String, 1);57NSLog(@"[JavaLauncher] Added custom env variable: %@", line);58}59}6061void init_loadCustomJvmFlags(int* argc, const char** argv) {62NSString *jvmargs = [PLProfiles resolveKeyForCurrentProfile:@"javaArgs"];63if (jvmargs == nil) return;64// Make the separator happy65jvmargs = [jvmargs stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];66jvmargs = [@" " stringByAppendingString:jvmargs];6768NSLog(@"[JavaLauncher] Reading custom JVM flags");69NSArray *argsToPurge = @[@"Xms", @"Xmx", @"d32", @"d64"];70for (NSString *arg in [jvmargs componentsSeparatedByString:@" -"]) {71NSString *jvmarg = [arg stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];72if (jvmarg.length == 0) continue;73BOOL ignore = NO;74for (NSString *argToPurge in argsToPurge) {75if ([jvmarg hasPrefix:argToPurge]) {76NSLog(@"[JavaLauncher] Ignored JVM flag: -%@", jvmarg);77ignore = YES;78break;79}80}81if (ignore) continue;8283++*argc;84argv[*argc] = [@"-" stringByAppendingString:jvmarg].UTF8String;8586NSLog(@"[JavaLauncher] Added custom JVM flag: %s", argv[*argc]);87}88}8990int launchJVM(NSString *username, id launchTarget, int width, int height, int minVersion) {91NSLog(@"[JavaLauncher] Beginning JVM launch");9293if (NSBundle.mainBundle.infoDictionary[@"LCDataUUID"]) {94NSDebugLog(@"[JavaLauncher] Running in LiveContainer, skipping dyld patch");95} else {96// Activate Library Validation bypass for external runtime and dylibs (JNA, etc)97init_bypassDyldLibValidation();98}99100101init_loadDefaultEnv();102init_loadCustomEnv();103104BOOL launchJar = NO;105NSString *gameDir;106NSString *defaultJRETag;107if ([launchTarget isKindOfClass:NSDictionary.class]) {108// Get preferred Java version from current profile109int preferredJavaVersion = [PLProfiles resolveKeyForCurrentProfile:@"javaVersion"].intValue;110if (preferredJavaVersion > 0) {111if (minVersion > preferredJavaVersion) {112NSLog(@"[JavaLauncher] Profile's preferred Java version (%d) does not meet the minimum version (%d), dropping request", preferredJavaVersion, minVersion);113} else {114NSDebugLog(@"[PLProfiles] Applying javaVersion");115minVersion = preferredJavaVersion;116}117}118if (minVersion <= 8) {119defaultJRETag = @"1_16_5_older";120} else {121defaultJRETag = @"1_17_newer";122}123124// Setup POJAV_RENDERER125NSString *renderer = [PLProfiles resolveKeyForCurrentProfile:@"renderer"];126NSLog(@"[JavaLauncher] RENDERER is set to %@\n", renderer);127setenv("POJAV_RENDERER", renderer.UTF8String, 1);128// Setup gameDir129gameDir = [NSString stringWithFormat:@"%s/instances/%@/%@",130getenv("POJAV_HOME"), getPrefObject(@"general.game_directory"),131[PLProfiles resolveKeyForCurrentProfile:@"gameDir"]]132.stringByStandardizingPath;133} else {134defaultJRETag = @"execute_jar";135gameDir = @(getenv("POJAV_GAME_DIR"));136launchJar = YES;137}138NSLog(@"[JavaLauncher] Looking for Java %d or later", minVersion);139NSString *javaHome = getSelectedJavaHome(defaultJRETag, minVersion);140141if (javaHome == nil) {142UIKit_returnToSplitView();143BOOL isExecuteJar = [defaultJRETag isEqualToString:@"execute_jar"];144showDialog(localize(@"Error", nil), [NSString stringWithFormat:localize(@"java.error.missing_runtime", nil),145isExecuteJar ? [launchTarget lastPathComponent] : PLProfiles.current.selectedProfile[@"lastVersionId"], minVersion]);146return 1;147} else if ([javaHome hasPrefix:@(getenv("POJAV_HOME"))]) {148// Symlink libawt_xawt.dylib149NSString *dest = [NSString stringWithFormat:@"%@/lib/libawt_xawt.dylib", javaHome];150NSString *source = [NSString stringWithFormat:@"%@/Frameworks/libawt_xawt.dylib", NSBundle.mainBundle.bundlePath];151NSError *error;152[fm createSymbolicLinkAtPath:dest withDestinationPath:source error:&error];153if (error) {154NSLog(@"[JavaLauncher] Symlink libawt_xawt.dylib failed: %@", error.localizedDescription);155}156}157158setenv("JAVA_HOME", javaHome.UTF8String, 1);159NSLog(@"[JavaLauncher] JAVA_HOME has been set to %@", javaHome);160161int allocmem;162if (getPrefBool(@"java.auto_ram")) {163CGFloat autoRatio = getEntitlementValue(@"com.apple.private.memorystatus") ? 0.4 : 0.25;164allocmem = roundf((NSProcessInfo.processInfo.physicalMemory / 1048576) * autoRatio);165} else {166allocmem = getPrefInt(@"java.allocated_memory");167}168NSLog(@"[JavaLauncher] Max RAM allocation is set to %d MB", allocmem);169170int margc = -1;171const char *margv[1000];172173margv[++margc] = [NSString stringWithFormat:@"%@/bin/java", javaHome].UTF8String;174margv[++margc] = "-XstartOnFirstThread";175if (!launchJar) {176margv[++margc] = "-Djava.system.class.loader=net.kdt.pojavlaunch.PojavClassLoader";177}178margv[++margc] = "-Xms128M";179margv[++margc] = [NSString stringWithFormat:@"-Xmx%dM", allocmem].UTF8String;180margv[++margc] = [NSString stringWithFormat:@"-Djava.library.path=%@/Frameworks", NSBundle.mainBundle.bundlePath].UTF8String;181margv[++margc] = [NSString stringWithFormat:@"-Duser.dir=%@", gameDir].UTF8String;182margv[++margc] = [NSString stringWithFormat:@"-Duser.home=%s", getenv("POJAV_HOME")].UTF8String;183margv[++margc] = [NSString stringWithFormat:@"-Duser.timezone=%@", NSTimeZone.localTimeZone.name].UTF8String;184margv[++margc] = [NSString stringWithFormat:@"-DUIScreen.maximumFramesPerSecond=%d", (int)UIScreen.mainScreen.maximumFramesPerSecond].UTF8String;185margv[++margc] = "-Dorg.lwjgl.glfw.checkThread0=false";186margv[++margc] = "-Dorg.lwjgl.system.allocator=system";187//margv[++margc] = "-Dorg.lwjgl.util.NoChecks=true";188margv[++margc] = "-Dlog4j2.formatMsgNoLookups=true";189190// Preset OpenGL libname191const char *glLibName = getenv("POJAV_RENDERER");192if (glLibName) {193if (!strcmp(glLibName, "auto")) {194// workaround only applies to 1.20.2+195glLibName = RENDERER_NAME_MTL_ANGLE;196}197margv[++margc] = [NSString stringWithFormat:@"-Dorg.lwjgl.opengl.libname=%s", glLibName].UTF8String;198}199200NSString *librariesPath = [NSString stringWithFormat:@"%@/libs", NSBundle.mainBundle.bundlePath];201margv[++margc] = [NSString stringWithFormat:@"-javaagent:%@/patchjna_agent.jar=", librariesPath].UTF8String;202if(getPrefBool(@"general.cosmetica")) {203margv[++margc] = [NSString stringWithFormat:@"-javaagent:%@/arc_dns_injector.jar=23.95.137.176", librariesPath].UTF8String;204}205206// Workaround random stack guard allocation crashes207margv[++margc] = "-XX:+UnlockExperimentalVMOptions";208margv[++margc] = "-XX:+DisablePrimordialThreadGuardPages";209210// Disable Forge 1.16.x early progress window211margv[++margc] = "-Dfml.earlyprogresswindow=false";212213// Load java214NSString *libjlipath8 = [NSString stringWithFormat:@"%@/lib/jli/libjli.dylib", javaHome]; // java 8215NSString *libjlipath11 = [NSString stringWithFormat:@"%@/lib/libjli.dylib", javaHome]; // java 11+216BOOL isJava8 = [fm fileExistsAtPath:libjlipath8];217setenv("INTERNAL_JLI_PATH", (isJava8 ? libjlipath8 : libjlipath11).UTF8String, 1);218void* libjli = dlopen(getenv("INTERNAL_JLI_PATH"), RTLD_GLOBAL);219220if (!libjli) {221const char *error = dlerror();222NSLog(@"[Init] JLI lib = NULL: %s", error);223UIKit_returnToSplitView();224showDialog(localize(@"Error", nil), @(error));225return 1;226}227228// Setup Caciocavallo229margv[++margc] = "-Djava.awt.headless=false";230margv[++margc] = "-Dcacio.font.fontmanager=sun.awt.X11FontManager";231margv[++margc] = "-Dcacio.font.fontscaler=sun.font.FreetypeFontScaler";232margv[++margc] = [NSString stringWithFormat:@"-Dcacio.managed.screensize=%dx%d", width, height].UTF8String;233margv[++margc] = "-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel";234if (isJava8) {235// Setup Caciocavallo236margv[++margc] = "-Dawt.toolkit=net.java.openjdk.cacio.ctc.CTCToolkit";237margv[++margc] = "-Djava.awt.graphicsenv=net.java.openjdk.cacio.ctc.CTCGraphicsEnvironment";238} else {239// Required by Cosmetica to inject DNS240margv[++margc] = "--add-opens=java.base/java.net=ALL-UNNAMED";241242// Setup Caciocavallo243margv[++margc] = "-Dawt.toolkit=com.github.caciocavallosilano.cacio.ctc.CTCToolkit";244margv[++margc] = "-Djava.awt.graphicsenv=com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment";245246// Required by Caciocavallo17 to access internal API247margv[++margc] = "--add-exports=java.desktop/java.awt=ALL-UNNAMED";248margv[++margc] = "--add-exports=java.desktop/java.awt.peer=ALL-UNNAMED";249margv[++margc] = "--add-exports=java.desktop/sun.awt.image=ALL-UNNAMED";250margv[++margc] = "--add-exports=java.desktop/sun.java2d=ALL-UNNAMED";251margv[++margc] = "--add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED";252margv[++margc] = "--add-exports=java.desktop/sun.awt=ALL-UNNAMED";253margv[++margc] = "--add-exports=java.desktop/sun.awt.event=ALL-UNNAMED";254margv[++margc] = "--add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED";255margv[++margc] = "--add-exports=java.desktop/sun.font=ALL-UNNAMED";256margv[++margc] = "--add-exports=java.base/sun.security.action=ALL-UNNAMED";257margv[++margc] = "--add-opens=java.base/java.util=ALL-UNNAMED";258margv[++margc] = "--add-opens=java.desktop/java.awt=ALL-UNNAMED";259margv[++margc] = "--add-opens=java.desktop/sun.font=ALL-UNNAMED";260margv[++margc] = "--add-opens=java.desktop/sun.java2d=ALL-UNNAMED";261margv[++margc] = "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED";262263// TODO: workaround, will be removed once the startup part works without PLaunchApp264margv[++margc] = "--add-exports=cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED";265}266267// Add Caciocavallo bootclasspath268NSString *cacio_classpath = [NSString stringWithFormat:@"-Xbootclasspath/%s", isJava8 ? "p" : "a"];269NSString *cacio_libs_path = [NSString stringWithFormat:@"%@/libs_caciocavallo%s", NSBundle.mainBundle.bundlePath, isJava8 ? "" : "17"];270NSArray *files = [fm contentsOfDirectoryAtPath:cacio_libs_path error:nil];271for(NSString *file in files) {272if ([file hasSuffix:@".jar"]) {273cacio_classpath = [NSString stringWithFormat:@"%@:%@/%@", cacio_classpath, cacio_libs_path, file];274}275}276margv[++margc] = cacio_classpath.UTF8String;277278if (!getEntitlementValue(@"com.apple.developer.kernel.extended-virtual-addressing")) {279// In jailed environment, where extended virtual addressing entitlement isn't280// present (for free dev account), allocating compressed space fails.281// FIXME: does extended VA allow allocating compressed class space?282margv[++margc] = "-XX:-UseCompressedClassPointers";283}284285if ([launchTarget isKindOfClass:NSDictionary.class]) {286for (NSString *arg in launchTarget[@"arguments"][@"jvm_processed"]) {287margv[++margc] = arg.UTF8String;288}289}290291init_loadCustomJvmFlags(&margc, (const char **)margv);292NSLog(@"[Init] Found JLI lib");293294NSString *classpath = [NSString stringWithFormat:@"%@/*", librariesPath];295if (launchJar) {296classpath = [classpath stringByAppendingFormat:@":%@", launchTarget];297}298margv[++margc] = "-cp";299margv[++margc] = classpath.UTF8String;300margv[++margc] = "net.kdt.pojavlaunch.PojavLauncher";301302if (launchJar) {303margv[++margc] = "-jar";304} else {305margv[++margc] = username.UTF8String;306}307308if ([launchTarget isKindOfClass:NSDictionary.class]) {309margv[++margc] = [launchTarget[@"id"] UTF8String];310} else {311margv[++margc] = [launchTarget UTF8String];312}313//margv[++margc] = "ghidra.GhidraRun";314315pJLI_Launch = (JLI_Launch_func *)dlsym(libjli, "JLI_Launch");316317if (NULL == pJLI_Launch) {318NSLog(@"[Init] JLI_Launch = NULL");319return -2;320}321322NSLog(@"[Init] Calling JLI_Launch");323324// Cr4shed known issue: exit after crash dump,325// reset signal handler so that JVM can catch them326signal(SIGSEGV, SIG_DFL);327signal(SIGPIPE, SIG_DFL);328signal(SIGBUS, SIG_DFL);329signal(SIGILL, SIG_DFL);330signal(SIGFPE, SIG_DFL);331332// Free split VC333tmpRootVC = nil;334335return pJLI_Launch(++margc, margv,3360, NULL, // sizeof(const_jargs) / sizeof(char *), const_jargs,3370, NULL, // sizeof(const_appclasspath) / sizeof(char *), const_appclasspath,338// These values are ignored in Java 17, so keep it anyways339"1.8.0-internal",340"1.8",341342"java", "openjdk",343/* (const_jargs != NULL) ? JNI_TRUE : */ JNI_FALSE,344JNI_TRUE, JNI_FALSE, JNI_TRUE);345}346347348