#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "jni.h" #include "log.h" #include "utils.h" #include "JavaLauncher.h" #include "external/fishhook/fishhook.h" #import "customcontrols/CustomControlsUtils.h" #import "LauncherPreferences.h" // PojavLancher: fixme: are these wrong? #define FULL_VERSION "1.8.0-internal" #define DOT_VERSION "1.8" #if CONFIG_RELEASE == 1 # define CONFIG_TYPE "release" #else # define CONFIG_TYPE "debug" #endif #ifndef CONFIG_COMMIT # define CONFIG_COMMIT unspecified #endif static char java_libs_path[2048]; static char args_path[2048]; static char env_path[2048]; static char log_path[2048]; extern char **environ; static const char* const_progname = "java"; static const char* const_launcher = "openjdk"; static const char** const_jargs = NULL; static const char** const_appclasspath = NULL; static const jboolean const_javaw = JNI_FALSE; static const jboolean const_cpwildcard = JNI_TRUE; static const jint const_ergo_class = 0; // DEFAULT_POLICY typedef jint JLI_Launch_func(int argc, char ** argv, /* main argc, argc */ int jargc, const char** jargv, /* java args */ int appclassc, const char** appclassv, /* app classpath */ const char* fullversion, /* full version defined */ const char* dotversion, /* dot version defined */ const char* pname, /* program name */ const char* lname, /* launcher name */ jboolean javaargs, /* JAVA_ARGS */ jboolean cpwildcard, /* classpath wildcard*/ jboolean javaw, /* windows-only javaw */ jint ergo /* ergonomics class policy */ ); int main(int argc, char * argv[]); static int (*orig_dladdr)(const void* addr, Dl_info* info); static void* (*orig_mmap)(void *addr, size_t len, int prot, int flags, int fd, off_t offset); static char* (*orig_realpath)(const char *restrict path, char *restrict resolved_path); static void (*orig_sys_icache_invalidate)(void *start, size_t len); static int margc = 0; static char* margv[1000]; static int pfd[2]; static int log_fd; static pthread_t logger; static BOOL filteredSessionID; const char *javaHome; const char *renderer; const char *allocmem; const char *multidir; NSString *javaHome_pre; NSString *renderer_pre; NSString *allocmem_pre; NSString *multidir_pre; int hooked_dladdr(const void* addr, Dl_info* info) { int retVal = orig_dladdr(addr, info); if (addr == main) { NSLog(@"hooked dladdr"); info->dli_fname = getenv("JAVA_EXT_EXECNAME"); NSLog(@"name = %s", info->dli_fname); } return retVal; } void *hooked_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) { NSLog(@"mmap(%p, %ld, %d, %d, %d, %lld)", addr, len, prot, flags, fd, offset); if (flags & MAP_JIT) { NSLog(@"'-> Found JIT mmap"); //flags &= ~MAP_JIT; } // raise(SIGINT); return orig_mmap(addr, len, prot, flags, fd, offset); } char *hooked_realpath(const char *restrict path, char *restrict resolved_path) { NSLog(@"hooked realpath %s", path); if (!strncmp(javaHome, path, strlen(javaHome))) { strcpy(resolved_path, path); return resolved_path; } else { return orig_realpath(path, resolved_path); } } void hooked_sys_icache_invalidate(void *start, size_t len) { // mprotect(start, 16384, PROT_EXEC | PROT_READ); // NSLog(@"mprotect errno %d", errno); orig_sys_icache_invalidate(start, len); } static void *logger_thread() { ssize_t rsize; char buf[2048]; while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) { // Filter out Session ID here int index; if (!filteredSessionID) { char *sessionStr = strstr(buf, "(Session ID is "); if (sessionStr) { char *censorStr = "(Session ID is )\n\0"; strcpy(sessionStr, censorStr); rsize = strlen(buf); filteredSessionID = true; } } write(log_fd, buf, rsize); } close(log_fd); return NULL; } void init_hookFunctions() { // if (!started && strncmp(argv[0], "/Applications", 13)) { // Jailed only: hook some functions for symlinked JRE home dir int retval = rebind_symbols((struct rebinding[4]){ //{"dlopen", hooked_dlopen, (void *)&orig_dlopen}, {"dladdr", hooked_dladdr, (void *)&orig_dladdr}, {"mmap", hooked_mmap, (void *)&orig_mmap}, {"realpath", hooked_realpath, (void *)&orig_realpath}, {"sys_icache_invalidate", hooked_sys_icache_invalidate, (void *)&orig_sys_icache_invalidate} }, 4); NSLog(@"hook retval = %d", retval); } } void init_loadCustomEnv() { FILE *envFile = fopen(env_path, "r"); debug("[Pre-init] Reading custom environment variables (custom_env.txt), opened=%d\n", envFile != NULL); if (envFile) { char *line = NULL; size_t len = 0; ssize_t read; while ((read = getline(&line, &len, envFile)) != -1) { if (line[0] == '#' || line[0] == '\n') continue; if (line[read-1] == '\n') { line[read-1] = '\0'; } if (strchr(line, '=') != NULL) { debug("[Pre-init] Added custom env: %s", line); setenv(strtok(line, "="), strtok(NULL, "="), 1); } else { debug("[Pre-init] Warning: skipped empty value custom env: %s", line); } } fclose(envFile); } } void init_loadCustomJvmFlags() { NSString *jvmargs = getPreference(@"java_args"); BOOL isFirstArg = YES; for (NSString *jvmarg in [jvmargs componentsSeparatedByString:@" -"]) { if ([jvmarg length] == 0) continue; if (isFirstArg) { isFirstArg = NO; margv[margc] = (char *) [jvmarg UTF8String]; } else { margv[margc] = (char *) [[@"-" stringByAppendingString:jvmarg] UTF8String]; } NSLog(@"[Pre-init] Added custom JVM flag: %s", margv[margc]); ++margc; } } void init_migrateToPlist(char* prefKey, char* filename) { // NSString *readmeStr = @"#README - this file has been merged into launcher_preferences.plist"; NSError *error; NSString *str, *path_str; // overrideargs.txt path_str = [NSString stringWithFormat:@"%s/%s", getenv("POJAV_HOME"), filename]; str = [NSString stringWithContentsOfFile:path_str encoding:NSUTF8StringEncoding error:&error]; if (error == nil && ![str hasPrefix:@"#README"]) { setPreference(@(prefKey), str); [@"#README - this file has been merged into launcher_preferences.plist" writeToFile:path_str atomically:YES encoding:NSUTF8StringEncoding error:nil]; } } void init_logDeviceAndVer (char *argument) { struct utsname systemInfo; uname(&systemInfo); // Hardware + software const char *deviceHardware = systemInfo.machine; const char *deviceSoftware = [[[UIDevice currentDevice] systemVersion] cStringUsingEncoding:NSUTF8StringEncoding]; // Jailbreak const char *deviceJailbreak; if (strncmp(argument, "/Applications", 13) == 0) { if ([[NSFileManager defaultManager] fileExistsAtPath:@"/taurine"]) { deviceJailbreak = "Taurine"; } else if ([[NSFileManager defaultManager] fileExistsAtPath:@"/odyssey"]) { deviceJailbreak = "Odyssey"; } else if ([[NSFileManager defaultManager] fileExistsAtPath:@"/chimera"]) { if ([[NSFileManager defaultManager] fileExistsAtPath:@"/.procursus_strapped"]) { deviceJailbreak = "Chimera"; } else { deviceJailbreak = "Chimera <1.4"; } } else { if ([[NSFileManager defaultManager] fileExistsAtPath:@"/private/etc/apt/undecimus"]) { deviceJailbreak = "unc0ver"; } else if ([[NSFileManager defaultManager] fileExistsAtPath:@"/binpack/lib/dyld"]) { if ([[NSFileManager defaultManager] fileExistsAtPath:@"/.procursus_strapped"]) { deviceJailbreak = "odysseyra1n"; } else { deviceJailbreak = "checkra1n"; } } } debug("[Pre-Init] %s with iOS %s (%s)", deviceHardware, deviceSoftware, deviceJailbreak); } else { debug("[Pre-Init] %s with iOS %s", deviceHardware, deviceSoftware); } // PojavLauncher version debug("[Pre-Init] PojavLauncher version: %s - %s", CONFIG_TYPE, CONFIG_COMMIT); setenv("POJAV_DETECTEDHW", deviceHardware, 1); setenv("POJAV_DETECTEDSW", deviceSoftware, 1); if (strncmp(argument, "/Applications", 13) == 0) { setenv("POJAV_DETECTEDJB", deviceJailbreak, 1); } } int launchJVM(int argc, char *argv[]) { char *homeDir; if (!started) { setenv("BUNDLE_PATH", dirname(argv[0]), 1); // Are we running on a jailbroken environment? if (strncmp(argv[0], "/Applications", 13) == 0) { setenv("HOME", "/var/mobile", 1); homeDir = "/var/mobile/Documents/.pojavlauncher"; } else { char pojavHome[2048]; sprintf(pojavHome, "%s/Documents", getenv("HOME")); homeDir = (char *) pojavHome; init_hookFunctions(); } setenv("POJAV_HOME", homeDir, 1); } else { homeDir = getenv("POJAV_HOME"); } init_loadCustomEnv(); loadPreferences(); init_migrateToPlist("selected_version", "config_ver.txt"); init_migrateToPlist("java_args", "overrideargs.txt"); sprintf((char*) env_path, "%s/custom_env.txt", homeDir); sprintf((char*) log_path, "%s/latestlog.txt", homeDir); sprintf((char*) java_libs_path, "%s/libs", getenv("BUNDLE_PATH")); mkdir(dirname(log_path), 755); if (!started) { debug("[Pre-init] Staring logging STDIO as jrelog:V\n"); // Redirect stdio to latestlog.txt char newname[2048]; sprintf(newname, "%s/latestlog.old.txt", homeDir); rename(log_path, newname); FILE* logFile = fopen(log_path, "w"); if (!logFile) { debug("[Pre-init] Error: failed to open %s: %s", log_path, strerror(errno)); assert(0 && "Failed to open latestlog.txt. Check oslog for more details."); } log_fd = fileno(logFile); setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered setvbuf(stderr, 0, _IONBF, 0); // make stderr unbuffered /* create the pipe and redirect stdout and stderr */ pipe(pfd); dup2(pfd[1], 1); dup2(pfd[1], 2); /* spawn the logging thread */ if(pthread_create(&logger, 0, logger_thread, 0) == -1) { char *fail_str = "Failed to start logging!"; write(log_fd, fail_str, strlen(fail_str)); close(log_fd); } pthread_detach(logger); } init_logDeviceAndVer(argv[0]); debug("[Pre-init] Beginning JVM launch\n"); char javaAwtPath[4096]; // I accidentally patched a bit wrong, the value should be a path containing libawt_xawt.dylib, but here is libawt.dylib path (no need to exist) sprintf(javaAwtPath, "%s/Frameworks/libawt.dylib", getenv("BUNDLE_PATH")); setenv("JAVA_AWT_PATH", javaAwtPath, 1); // setenv("LIBGL_FB", "2", 1); setenv("LIBGL_MIPMAP", "3", 1); // Fix white color on banner and sheep, since GL4ES 1.1.5 setenv("LIBGL_NORMALIZE", "1", 1); // Disable overloaded functions hack for Minecraft 1.17+ setenv("LIBGL_NOINTOVLHACK", "1", 1); // Override OpenGL version to 4.1 for Zink setenv("MESA_GL_VERSION_OVERRIDE", "4.1", 1); javaHome_pre = getPreference(@"java_home"); javaHome = [javaHome_pre cStringUsingEncoding:NSUTF8StringEncoding]; if ([javaHome_pre length] == 0) { if (strncmp(argv[0], "/Applications", 13) == 0) { if (0 != access("/usr/lib/jvm/java-8-openjdk/", F_OK)) { debug("[Pre-init] Java 8 wasn't found on your device. Install Java 8 for more compatibility and the mod installer."); javaHome_pre = @"/usr/lib/jvm/java-16-openjdk"; javaHome = [javaHome_pre cStringUsingEncoding:NSUTF8StringEncoding]; setPreference(@"java_home", javaHome_pre); } else { javaHome_pre = @"/usr/lib/jvm/java-8-openjdk"; javaHome = [javaHome_pre cStringUsingEncoding:NSUTF8StringEncoding]; setPreference(@"java_home", javaHome_pre); } } else { javaHome = calloc(1, 2048); sprintf((char *)javaHome, "%s/jre", homeDir); } debug("[Pre-init] JAVA_HOME environment variable was not set. Defaulting to %s for future use.\n", javaHome); } else { if (0 == [[NSFileManager defaultManager] fileExistsAtPath:javaHome_pre]) { debug("[Pre-Init] Failed to locate %s. Restoring default value for JAVA_HOME.", javaHome); if (0 != access("/usr/lib/jvm/java-8-openjdk/", F_OK)) { debug("[Pre-init] Java 8 wasn't found on your device. Install Java 8 for more compatibility and the mod installer."); javaHome_pre = @"/usr/lib/jvm/java-16-openjdk"; javaHome = [javaHome_pre cStringUsingEncoding:NSUTF8StringEncoding]; setPreference(@"java_home", javaHome_pre); } else { javaHome_pre = @"/usr/lib/jvm/java-8-openjdk"; javaHome = [javaHome_pre cStringUsingEncoding:NSUTF8StringEncoding]; setPreference(@"java_home", javaHome_pre); } } else { debug("[Pre-Init] Restored preference: JAVA_HOME is set to %s\n", javaHome); } } setenv("JAVA_HOME", javaHome, 1); if (!started && strncmp(argv[0], "/Applications", 13)) { char src[2048], dst[2048]; // Symlink frameworks -> dylibs on jailed environment mkdir(javaHome, 755); // Symlink the skeleton part of JRE sprintf((char *)src, "%s/jre/man", getenv("BUNDLE_PATH")); sprintf((char *)dst, "%s/man", javaHome); symlink(src, dst); sprintf((char *)src, "%s/jre/lib", getenv("BUNDLE_PATH")); sprintf((char *)dst, "%s/lib", javaHome); mkdir(dst, 755); DIR *d; struct dirent *dir; d = opendir(src); assert(d); int i = 0; while ((dir = readdir(d)) != NULL) { // Skip "." and ".." if (i < 2) { i++; continue; } else if (!strncmp(dir->d_name, "jli", 3)) { sprintf((char *)dst, "%s/lib/jli", javaHome); mkdir(dst, 755); // libjli.dylib sprintf((char *)src, "%s/Frameworks/libjli.dylib.framework/libjli.dylib", getenv("BUNDLE_PATH")); sprintf((char *)dst, "%s/lib/jli/libjli.dylib", javaHome); symlink(src, dst); } else if (!strncmp(dir->d_name, "server", 6)) { sprintf((char *)dst, "%s/lib/server", javaHome); mkdir(dst, 755); // libjsig.dylib sprintf((char *)src, "%s/Frameworks/libjsig.dylib.framework/libjsig.dylib", getenv("BUNDLE_PATH")); sprintf((char *)dst, "%s/lib/server/libjsig.dylib", javaHome); symlink(src, dst); // libjvm.dylib sprintf((char *)src, "%s/Frameworks/libjvm.dylib.framework/libjvm.dylib", getenv("BUNDLE_PATH")); sprintf((char *)dst, "%s/lib/server/libjvm.dylib", javaHome); symlink(src, dst); // Xusage.txt sprintf((char *)src, "%s/jre/lib/server/Xusage.txt", getenv("BUNDLE_PATH")); sprintf((char *)dst, "%s/lib/server/Xusage.txt", javaHome); symlink(src, dst); } else { sprintf((char *)src, "%s/jre/lib/%s", getenv("BUNDLE_PATH"), dir->d_name); sprintf((char *)dst, "%s/lib/%s", javaHome, dir->d_name); symlink(src, dst); } } closedir(d); // Symlink dylibs sprintf((char *)src, "%s/Frameworks", getenv("BUNDLE_PATH")); d = opendir(src); assert(d); i = 0; while ((dir = readdir(d)) != NULL) { // Skip "." and ".." if (i < 2) { i++; continue; } else if (!strncmp(dir->d_name, "lib", 3) && strlen(dir->d_name) > 12) { char *dylibName = strdup(dir->d_name); dylibName[strlen(dylibName) - 10] = '\0'; sprintf((char *)src, "%s/Frameworks/%s/%s", getenv("BUNDLE_PATH"), dir->d_name, dylibName); if (!strncmp(dir->d_name, "libjvm.dylib", 12)) { sprintf((char *)dst, "%s/lib/server/%s", javaHome, dylibName); } else { sprintf((char *)dst, "%s/lib/%s", javaHome, dylibName); } symlink(src, dst); dylibName[strlen(dylibName) - 11] = '.'; free(dylibName); } } closedir(d); } renderer_pre = getPreference(@"renderer"); renderer = [renderer_pre cStringUsingEncoding:NSUTF8StringEncoding]; if ([renderer_pre length] == 0) { renderer_pre = @"libgl4es_114.dylib"; setPreference(@"renderer", renderer_pre); renderer = [renderer_pre cStringUsingEncoding:NSUTF8StringEncoding]; setenv("RENDERER", renderer, 1); debug("[Pre-init] RENDERER environment variable was not set. Defaulting to %s for future use.\n", renderer); } else { debug("[Pre-Init] Restored preference: RENDERER is set to %s\n", renderer); } allocmem_pre = [getPreference(@"allocated_memory") stringValue]; allocmem = [allocmem_pre cStringUsingEncoding:NSUTF8StringEncoding]; char *controlPath = calloc(1, 2048); sprintf(controlPath, "%s/controlmap", homeDir); mkdir(controlPath, S_IRWXU | S_IRWXG | S_IRWXO); setenv("POJAV_PATH_CONTROL", controlPath, 1); free(controlPath); generateAndSaveDefaultControl(); char classpath[10000]; // "/Applications/PojavLauncher.app/libs/launcher.jar:/Applications/PojavLauncher.app/libs/ExagearApacheCommons.jar:/Applications/PojavLauncher.app/libs/gson-2.8.6.jar:/Applications/PojavLauncher.app/libs/jsr305.jar:/Applications/PojavLauncher.app/libs/lwjgl3-minecraft.jar"; // Generate classpath DIR *d; struct dirent *dir; d = opendir(java_libs_path); int cplen = 0; if (d) { // cplen += sprintf(classpath + cplen, "-Xbootclasspath/a:"); while ((dir = readdir(d)) != NULL) { cplen += sprintf(classpath + cplen, "%s/%s:", java_libs_path, dir->d_name); } closedir(d); } debug("[Pre-init] Classpath generated: %s", classpath); multidir_pre = getPreference(@"game_directory"); multidir = [multidir_pre cStringUsingEncoding:NSUTF8StringEncoding]; if ([multidir_pre length] == 0) { multidir_pre = @"default"; multidir = [multidir_pre cStringUsingEncoding:NSUTF8StringEncoding]; setPreference(@"game_directory", multidir_pre); debug("[Pre-init] MULTI_DIR environment variable was not set. Defaulting to %s for future use.\n", multidir); } else { multidir = [multidir_pre cStringUsingEncoding:NSUTF8StringEncoding]; debug("[Pre-init] Restored preference: MULTI_DIR is set to %s\n", multidir); } char *multidir_char = calloc(1, 2048); char *librarySym = calloc(1, 2048); snprintf(multidir_char, 2048, "%s/instances/%s", getenv("POJAV_HOME"), multidir); snprintf(librarySym, 2048, "%s/Library/Application Support/minecraft", getenv("POJAV_HOME")); remove(librarySym); symlink(multidir_char, librarySym); setenv("POJAV_GAME_DIR", librarySym, 1); char *oldGameDir = calloc(1, 2048); snprintf(oldGameDir, 2048, "%s/Documents/minecraft", getenv("HOME")); if (0 == access(oldGameDir, F_OK)) { rename(oldGameDir, multidir_char); debug("[Pre-Init] Migrated old minecraft folder to new location."); } char *oldLibraryDir = calloc(1, 2048); snprintf(oldLibraryDir, 2048, "%s/Documents/Library", getenv("HOME")); if (0 == access(oldLibraryDir, F_OK)) { remove(oldLibraryDir); } // Check if JVM restarts if (!started) { char *frameworkPath = calloc(1, 2048); char *javaPath = calloc(1, 2048); char *userDir = calloc(1, 2048); char *userHome = calloc(1, 2048); char *memMin = calloc(1, 2048); char *memMax = calloc(1, 2048); snprintf(frameworkPath, 2048, "-Djava.library.path=%s/Frameworks:%s/Frameworks/libOSMesa.8.dylib.framework", getenv("BUNDLE_PATH"), getenv("BUNDLE_PATH")); snprintf(javaPath, 2048, "%s/bin/java", javaHome); snprintf(userDir, 2048, "-Duser.dir=%s", getenv("POJAV_GAME_DIR")); snprintf(userHome, 2048, "-Duser.home=%s", getenv("POJAV_HOME")); snprintf(memMin, 2048, "-Xms%sM", allocmem); snprintf(memMax, 2048, "-Xmx%sM", allocmem); NSLog(@"[Pre-init] Java executable path: %s", javaPath); setenv("JAVA_EXT_EXECNAME", javaPath, 1); chdir(librarySym); margv[margc++] = javaPath; margv[margc++] = "-XstartOnFirstThread"; margv[margc++] = "-Djava.system.class.loader=net.kdt.pojavlaunch.PojavClassLoader"; margv[margc++] = memMin; margv[margc++] = memMax; margv[margc++] = frameworkPath; margv[margc++] = userDir; margv[margc++] = userHome; margv[margc++] = "-Dorg.lwjgl.system.allocator=system"; } else { setenv("RENDERER", renderer, 1); debug("[Pre-init] RENDERER has been set to %s", getenv("RENDERER")); } // Load java char libjlipath8[2048]; // java 8 char libjlipath16[2048]; // java 16+ (?) sprintf(libjlipath8, "%s/lib/jli/libjli.dylib", javaHome); sprintf(libjlipath16, "%s/lib/libjli.dylib", javaHome); void* libjli = dlopen(libjlipath16, RTLD_LAZY | RTLD_GLOBAL); if (!libjli) { debug("[Init] Can't load %s, trying %s", libjlipath16, libjlipath8); libjli = dlopen(libjlipath8, RTLD_LAZY | RTLD_GLOBAL); if (!libjli) { debug("[Init] JLI lib = NULL: %s", dlerror()); return -1; } if (!started) { // Setup Caciocavallo margv[margc++] = "-Djava.awt.headless=false"; margv[margc++] = "-Dcacio.font.fontmanager=sun.awt.X11FontManager"; margv[margc++] = "-Dcacio.font.fontscaler=sun.font.FreetypeFontScaler"; margv[margc++] = "-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel"; margv[margc++] = "-Dawt.toolkit=net.java.openjdk.cacio.ctc.CTCToolkit"; margv[margc++] = "-Djava.awt.graphicsenv=net.java.openjdk.cacio.ctc.CTCGraphicsEnvironment"; // Generate Caciocavallo bootclasspath char cacio_libs_path[2048]; char cacio_classpath[8192]; sprintf((char*) cacio_libs_path, "%s/libs_caciocavallo", getenv("BUNDLE_PATH")); cplen = sprintf(cacio_classpath, "-Xbootclasspath/p"); d = opendir(cacio_libs_path); if (d) { while ((dir = readdir(d)) != NULL) { cplen += sprintf(cacio_classpath + cplen, ":%s/%s", cacio_libs_path, dir->d_name); } closedir(d); } margv[margc++] = cacio_classpath; } } if (!started) { init_loadCustomJvmFlags(); } debug("[Init] Found JLI lib"); if (!started) { margv[margc++] = "-cp"; margv[margc++] = classpath; margv[margc++] = "net.kdt.pojavlaunch.PLaunchApp"; for (int i = 0; i < argc; i++) { margv[margc++] = argv[i]; } } JLI_Launch_func *pJLI_Launch = (JLI_Launch_func *)dlsym(libjli, "JLI_Launch"); if (NULL == pJLI_Launch) { debug("[Init] JLI_Launch = NULL"); return -2; } // Free unused char arrays free(librarySym); free(multidir_char); free(oldGameDir); free(oldLibraryDir); debug("[Init] Calling JLI_Launch"); /* for (int i = 0; i < margc; i++) { debug("arg[%d] = %s", i, margv[i]); } */ int targc = started ? argc : margc; char **targv = started ? argv : margv; if (!started) { started = true; } /* debug: for (int i = 0; i < targc; i++) { debug("Arg=%s", targv[i]); } */ return pJLI_Launch(targc, targv, 0, NULL, // sizeof(const_jargs) / sizeof(char *), const_jargs, 0, NULL, // sizeof(const_appclasspath) / sizeof(char *), const_appclasspath, FULL_VERSION, DOT_VERSION, const_progname, // (const_progname != NULL) ? const_progname : *margv, const_launcher, // (const_launcher != NULL) ? const_launcher : *margv, (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE, const_cpwildcard, const_javaw, const_ergo_class); }