Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/PojavLauncher_iOS
Path: blob/main/Natives/JavaLauncher.m
589 views
1
#include <dirent.h>
2
#include <dlfcn.h>
3
#include <errno.h>
4
#include <libgen.h>
5
#include <spawn.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <string.h>
9
#include <sys/stat.h>
10
#include <unistd.h>
11
12
#include "utils.h"
13
14
#import "ios_uikit_bridge.h"
15
#import "JavaLauncher.h"
16
#import "LauncherPreferences.h"
17
#import "PLProfiles.h"
18
19
#define fm NSFileManager.defaultManager
20
21
extern char **environ;
22
23
void init_loadDefaultEnv() {
24
/* Define default env */
25
26
// Silent Caciocavallo NPE error in locating Android-only lib
27
setenv("LD_LIBRARY_PATH", "", 1);
28
29
// Ignore mipmap for performance(?) seems does not affect iOS
30
//setenv("LIBGL_MIPMAP", "3", 1);
31
32
// Disable overloaded functions hack for Minecraft 1.17+
33
setenv("LIBGL_NOINTOVLHACK", "1", 1);
34
35
// Fix white color on banner and sheep, since GL4ES 1.1.5
36
setenv("LIBGL_NORMALIZE", "1", 1);
37
38
// Override OpenGL version to 4.1 for Zink
39
setenv("MESA_GL_VERSION_OVERRIDE", "4.1", 1);
40
41
// Runs JVM in a separate thread
42
setenv("HACK_IGNORE_START_ON_FIRST_THREAD", "1", 1);
43
}
44
45
void init_loadCustomEnv() {
46
NSString *envvars = getPrefObject(@"java.env_variables");
47
if (envvars == nil) return;
48
NSLog(@"[JavaLauncher] Reading custom environment variables");
49
for (NSString *line in [envvars componentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceCharacterSet]) {
50
if (![line containsString:@"="]) {
51
NSLog(@"[JavaLauncher] Warning: skipped empty value custom env variable: %@", line);
52
continue;
53
}
54
NSRange range = [line rangeOfString:@"="];
55
NSString *key = [line substringToIndex:range.location];
56
NSString *value = [line substringFromIndex:range.location+range.length];
57
setenv(key.UTF8String, value.UTF8String, 1);
58
NSLog(@"[JavaLauncher] Added custom env variable: %@", line);
59
}
60
}
61
62
void init_loadCustomJvmFlags(int* argc, const char** argv) {
63
NSString *jvmargs = [PLProfiles resolveKeyForCurrentProfile:@"javaArgs"];
64
if (jvmargs == nil) return;
65
// Make the separator happy
66
jvmargs = [jvmargs stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
67
jvmargs = [@" " stringByAppendingString:jvmargs];
68
69
NSLog(@"[JavaLauncher] Reading custom JVM flags");
70
NSArray *argsToPurge = @[@"Xms", @"Xmx", @"d32", @"d64"];
71
for (NSString *arg in [jvmargs componentsSeparatedByString:@" -"]) {
72
NSString *jvmarg = [arg stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
73
if (jvmarg.length == 0) continue;
74
BOOL ignore = NO;
75
for (NSString *argToPurge in argsToPurge) {
76
if ([jvmarg hasPrefix:argToPurge]) {
77
NSLog(@"[JavaLauncher] Ignored JVM flag: -%@", jvmarg);
78
ignore = YES;
79
break;
80
}
81
}
82
if (ignore) continue;
83
84
++*argc;
85
argv[*argc] = [@"-" stringByAppendingString:jvmarg].UTF8String;
86
87
NSLog(@"[JavaLauncher] Added custom JVM flag: %s", argv[*argc]);
88
}
89
}
90
91
int launchJVM(NSString *username, id launchTarget, int width, int height, int minVersion) {
92
NSLog(@"[JavaLauncher] Beginning JVM launch");
93
94
if (NSBundle.mainBundle.infoDictionary[@"LCDataUUID"]) {
95
NSDebugLog(@"[JavaLauncher] Running in LiveContainer, skipping dyld patch");
96
} else {
97
// Activate Library Validation bypass for external runtime and dylibs (JNA, etc)
98
init_bypassDyldLibValidation();
99
}
100
101
102
init_loadDefaultEnv();
103
init_loadCustomEnv();
104
105
BOOL launchJar = NO;
106
NSString *gameDir;
107
NSString *defaultJRETag;
108
if ([launchTarget isKindOfClass:NSDictionary.class]) {
109
// Get preferred Java version from current profile
110
int preferredJavaVersion = [PLProfiles resolveKeyForCurrentProfile:@"javaVersion"].intValue;
111
if (preferredJavaVersion > 0) {
112
if (minVersion > preferredJavaVersion) {
113
NSLog(@"[JavaLauncher] Profile's preferred Java version (%d) does not meet the minimum version (%d), dropping request", preferredJavaVersion, minVersion);
114
} else {
115
NSDebugLog(@"[PLProfiles] Applying javaVersion");
116
minVersion = preferredJavaVersion;
117
}
118
}
119
if (minVersion <= 8) {
120
defaultJRETag = @"1_16_5_older";
121
} else {
122
defaultJRETag = @"1_17_newer";
123
}
124
125
// Setup POJAV_RENDERER
126
NSString *renderer = [PLProfiles resolveKeyForCurrentProfile:@"renderer"];
127
NSLog(@"[JavaLauncher] RENDERER is set to %@\n", renderer);
128
setenv("POJAV_RENDERER", renderer.UTF8String, 1);
129
// Setup gameDir
130
gameDir = [NSString stringWithFormat:@"%s/instances/%@/%@",
131
getenv("POJAV_HOME"), getPrefObject(@"general.game_directory"),
132
[PLProfiles resolveKeyForCurrentProfile:@"gameDir"]]
133
.stringByStandardizingPath;
134
} else {
135
defaultJRETag = @"execute_jar";
136
gameDir = @(getenv("POJAV_GAME_DIR"));
137
launchJar = YES;
138
}
139
NSLog(@"[JavaLauncher] Looking for Java %d or later", minVersion);
140
NSString *javaHome = getSelectedJavaHome(defaultJRETag, minVersion);
141
142
if (javaHome == nil) {
143
UIKit_returnToSplitView();
144
BOOL isExecuteJar = [defaultJRETag isEqualToString:@"execute_jar"];
145
showDialog(localize(@"Error", nil), [NSString stringWithFormat:localize(@"java.error.missing_runtime", nil),
146
isExecuteJar ? [launchTarget lastPathComponent] : PLProfiles.current.selectedProfile[@"lastVersionId"], minVersion]);
147
return 1;
148
} else if ([javaHome hasPrefix:@(getenv("POJAV_HOME"))]) {
149
// Symlink libawt_xawt.dylib
150
NSString *dest = [NSString stringWithFormat:@"%@/lib/libawt_xawt.dylib", javaHome];
151
NSString *source = [NSString stringWithFormat:@"%@/Frameworks/libawt_xawt.dylib", NSBundle.mainBundle.bundlePath];
152
NSError *error;
153
[fm createSymbolicLinkAtPath:dest withDestinationPath:source error:&error];
154
if (error) {
155
NSLog(@"[JavaLauncher] Symlink libawt_xawt.dylib failed: %@", error.localizedDescription);
156
}
157
}
158
159
setenv("JAVA_HOME", javaHome.UTF8String, 1);
160
NSLog(@"[JavaLauncher] JAVA_HOME has been set to %@", javaHome);
161
162
int allocmem;
163
if (getPrefBool(@"java.auto_ram")) {
164
CGFloat autoRatio = getEntitlementValue(@"com.apple.private.memorystatus") ? 0.4 : 0.25;
165
allocmem = roundf((NSProcessInfo.processInfo.physicalMemory / 1048576) * autoRatio);
166
} else {
167
allocmem = getPrefInt(@"java.allocated_memory");
168
}
169
NSLog(@"[JavaLauncher] Max RAM allocation is set to %d MB", allocmem);
170
171
int margc = -1;
172
const char *margv[1000];
173
174
margv[++margc] = [NSString stringWithFormat:@"%@/bin/java", javaHome].UTF8String;
175
margv[++margc] = "-XstartOnFirstThread";
176
if (!launchJar) {
177
margv[++margc] = "-Djava.system.class.loader=net.kdt.pojavlaunch.PojavClassLoader";
178
}
179
margv[++margc] = "-Xms128M";
180
margv[++margc] = [NSString stringWithFormat:@"-Xmx%dM", allocmem].UTF8String;
181
margv[++margc] = [NSString stringWithFormat:@"-Djava.library.path=%@/Frameworks", NSBundle.mainBundle.bundlePath].UTF8String;
182
margv[++margc] = [NSString stringWithFormat:@"-Duser.dir=%@", gameDir].UTF8String;
183
margv[++margc] = [NSString stringWithFormat:@"-Duser.home=%s", getenv("POJAV_HOME")].UTF8String;
184
margv[++margc] = [NSString stringWithFormat:@"-Duser.timezone=%@", NSTimeZone.localTimeZone.name].UTF8String;
185
margv[++margc] = [NSString stringWithFormat:@"-DUIScreen.maximumFramesPerSecond=%d", (int)UIScreen.mainScreen.maximumFramesPerSecond].UTF8String;
186
margv[++margc] = "-Dorg.lwjgl.glfw.checkThread0=false";
187
margv[++margc] = "-Dorg.lwjgl.system.allocator=system";
188
//margv[++margc] = "-Dorg.lwjgl.util.NoChecks=true";
189
margv[++margc] = "-Dlog4j2.formatMsgNoLookups=true";
190
191
// Preset OpenGL libname
192
const char *glLibName = getenv("POJAV_RENDERER");
193
if (glLibName) {
194
if (!strcmp(glLibName, "auto")) {
195
// workaround only applies to 1.20.2+
196
glLibName = RENDERER_NAME_MTL_ANGLE;
197
}
198
margv[++margc] = [NSString stringWithFormat:@"-Dorg.lwjgl.opengl.libname=%s", glLibName].UTF8String;
199
}
200
201
NSString *librariesPath = [NSString stringWithFormat:@"%@/libs", NSBundle.mainBundle.bundlePath];
202
margv[++margc] = [NSString stringWithFormat:@"-javaagent:%@/patchjna_agent.jar=", librariesPath].UTF8String;
203
if(getPrefBool(@"general.cosmetica")) {
204
margv[++margc] = [NSString stringWithFormat:@"-javaagent:%@/arc_dns_injector.jar=23.95.137.176", librariesPath].UTF8String;
205
}
206
207
// Workaround random stack guard allocation crashes
208
margv[++margc] = "-XX:+UnlockExperimentalVMOptions";
209
margv[++margc] = "-XX:+DisablePrimordialThreadGuardPages";
210
211
// Disable Forge 1.16.x early progress window
212
margv[++margc] = "-Dfml.earlyprogresswindow=false";
213
214
// Load java
215
NSString *libjlipath8 = [NSString stringWithFormat:@"%@/lib/jli/libjli.dylib", javaHome]; // java 8
216
NSString *libjlipath11 = [NSString stringWithFormat:@"%@/lib/libjli.dylib", javaHome]; // java 11+
217
BOOL isJava8 = [fm fileExistsAtPath:libjlipath8];
218
setenv("INTERNAL_JLI_PATH", (isJava8 ? libjlipath8 : libjlipath11).UTF8String, 1);
219
void* libjli = dlopen(getenv("INTERNAL_JLI_PATH"), RTLD_GLOBAL);
220
221
if (!libjli) {
222
const char *error = dlerror();
223
NSLog(@"[Init] JLI lib = NULL: %s", error);
224
UIKit_returnToSplitView();
225
showDialog(localize(@"Error", nil), @(error));
226
return 1;
227
}
228
229
// Setup Caciocavallo
230
margv[++margc] = "-Djava.awt.headless=false";
231
margv[++margc] = "-Dcacio.font.fontmanager=sun.awt.X11FontManager";
232
margv[++margc] = "-Dcacio.font.fontscaler=sun.font.FreetypeFontScaler";
233
margv[++margc] = [NSString stringWithFormat:@"-Dcacio.managed.screensize=%dx%d", width, height].UTF8String;
234
margv[++margc] = "-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel";
235
if (isJava8) {
236
// Setup Caciocavallo
237
margv[++margc] = "-Dawt.toolkit=net.java.openjdk.cacio.ctc.CTCToolkit";
238
margv[++margc] = "-Djava.awt.graphicsenv=net.java.openjdk.cacio.ctc.CTCGraphicsEnvironment";
239
} else {
240
// Required by Cosmetica to inject DNS
241
margv[++margc] = "--add-opens=java.base/java.net=ALL-UNNAMED";
242
243
// Setup Caciocavallo
244
margv[++margc] = "-Dawt.toolkit=com.github.caciocavallosilano.cacio.ctc.CTCToolkit";
245
margv[++margc] = "-Djava.awt.graphicsenv=com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment";
246
247
// Required by Caciocavallo17 to access internal API
248
margv[++margc] = "--add-exports=java.desktop/java.awt=ALL-UNNAMED";
249
margv[++margc] = "--add-exports=java.desktop/java.awt.peer=ALL-UNNAMED";
250
margv[++margc] = "--add-exports=java.desktop/sun.awt.image=ALL-UNNAMED";
251
margv[++margc] = "--add-exports=java.desktop/sun.java2d=ALL-UNNAMED";
252
margv[++margc] = "--add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED";
253
margv[++margc] = "--add-exports=java.desktop/sun.awt=ALL-UNNAMED";
254
margv[++margc] = "--add-exports=java.desktop/sun.awt.event=ALL-UNNAMED";
255
margv[++margc] = "--add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED";
256
margv[++margc] = "--add-exports=java.desktop/sun.font=ALL-UNNAMED";
257
margv[++margc] = "--add-exports=java.base/sun.security.action=ALL-UNNAMED";
258
margv[++margc] = "--add-opens=java.base/java.util=ALL-UNNAMED";
259
margv[++margc] = "--add-opens=java.desktop/java.awt=ALL-UNNAMED";
260
margv[++margc] = "--add-opens=java.desktop/sun.font=ALL-UNNAMED";
261
margv[++margc] = "--add-opens=java.desktop/sun.java2d=ALL-UNNAMED";
262
margv[++margc] = "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED";
263
264
// TODO: workaround, will be removed once the startup part works without PLaunchApp
265
margv[++margc] = "--add-exports=cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED";
266
}
267
268
// Add Caciocavallo bootclasspath
269
NSString *cacio_classpath = [NSString stringWithFormat:@"-Xbootclasspath/%s", isJava8 ? "p" : "a"];
270
NSString *cacio_libs_path = [NSString stringWithFormat:@"%@/libs_caciocavallo%s", NSBundle.mainBundle.bundlePath, isJava8 ? "" : "17"];
271
NSArray *files = [fm contentsOfDirectoryAtPath:cacio_libs_path error:nil];
272
for(NSString *file in files) {
273
if ([file hasSuffix:@".jar"]) {
274
cacio_classpath = [NSString stringWithFormat:@"%@:%@/%@", cacio_classpath, cacio_libs_path, file];
275
}
276
}
277
margv[++margc] = cacio_classpath.UTF8String;
278
279
if (!getEntitlementValue(@"com.apple.developer.kernel.extended-virtual-addressing")) {
280
// In jailed environment, where extended virtual addressing entitlement isn't
281
// present (for free dev account), allocating compressed space fails.
282
// FIXME: does extended VA allow allocating compressed class space?
283
margv[++margc] = "-XX:-UseCompressedClassPointers";
284
}
285
286
if ([launchTarget isKindOfClass:NSDictionary.class]) {
287
for (NSString *arg in launchTarget[@"arguments"][@"jvm_processed"]) {
288
margv[++margc] = arg.UTF8String;
289
}
290
}
291
292
init_loadCustomJvmFlags(&margc, (const char **)margv);
293
NSLog(@"[Init] Found JLI lib");
294
295
NSString *classpath = [NSString stringWithFormat:@"%@/*", librariesPath];
296
if (launchJar) {
297
classpath = [classpath stringByAppendingFormat:@":%@", launchTarget];
298
}
299
margv[++margc] = "-cp";
300
margv[++margc] = classpath.UTF8String;
301
margv[++margc] = "net.kdt.pojavlaunch.PojavLauncher";
302
303
if (launchJar) {
304
margv[++margc] = "-jar";
305
} else {
306
margv[++margc] = username.UTF8String;
307
}
308
309
if ([launchTarget isKindOfClass:NSDictionary.class]) {
310
margv[++margc] = [launchTarget[@"id"] UTF8String];
311
} else {
312
margv[++margc] = [launchTarget UTF8String];
313
}
314
//margv[++margc] = "ghidra.GhidraRun";
315
316
pJLI_Launch = (JLI_Launch_func *)dlsym(libjli, "JLI_Launch");
317
318
if (NULL == pJLI_Launch) {
319
NSLog(@"[Init] JLI_Launch = NULL");
320
return -2;
321
}
322
323
NSLog(@"[Init] Calling JLI_Launch");
324
325
// Cr4shed known issue: exit after crash dump,
326
// reset signal handler so that JVM can catch them
327
signal(SIGSEGV, SIG_DFL);
328
signal(SIGPIPE, SIG_DFL);
329
signal(SIGBUS, SIG_DFL);
330
signal(SIGILL, SIG_DFL);
331
signal(SIGFPE, SIG_DFL);
332
333
// Free split VC
334
tmpRootVC = nil;
335
336
return pJLI_Launch(++margc, margv,
337
0, NULL, // sizeof(const_jargs) / sizeof(char *), const_jargs,
338
0, NULL, // sizeof(const_appclasspath) / sizeof(char *), const_appclasspath,
339
// These values are ignored in Java 17, so keep it anyways
340
"1.8.0-internal",
341
"1.8",
342
343
"java", "openjdk",
344
/* (const_jargs != NULL) ? JNI_TRUE : */ JNI_FALSE,
345
JNI_TRUE, JNI_FALSE, JNI_TRUE);
346
}
347
348