mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-08 12:03:29 -04:00
Feat[renderer]: Add LTW renderer (#6477)
* Feat[launcher]: start introducing the Tinywrapper renderer * Feat[launcher]: add renderer, add compatibility checks * Chore[ltw]: update to latest * Style[code]: code cosmetic changes * Legal[ltw]: make LTW optional * Fix[ltw]: fix actions, add forgotten file * Workflow[ltw]: disallow forks, allow building on error
This commit is contained in:
parent
ed89b44d3b
commit
b28fc4a1f4
12
.github/workflows/android.yml
vendored
12
.github/workflows/android.yml
vendored
@ -26,6 +26,18 @@ jobs:
|
||||
distribution: 'temurin'
|
||||
java-version: '8'
|
||||
|
||||
- name: Get LTW
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.LTW_CLONER_SECRET}}
|
||||
repo: PojavLauncherTeam/BigTinyWrapper
|
||||
workflow: android.yml
|
||||
workflow_conclusion: success
|
||||
name: output-aar
|
||||
path: app_pojavlauncher/libs
|
||||
allow_forks: false
|
||||
|
||||
- name: Get JRE 8
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
|
@ -227,5 +227,5 @@ dependencies {
|
||||
|
||||
// implementation 'net.sourceforge.streamsupport:streamsupport-cfuture:1.7.0'
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
}
|
||||
|
@ -22,6 +22,11 @@ import android.database.Cursor;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorManager;
|
||||
import android.net.Uri;
|
||||
import android.opengl.EGL14;
|
||||
import android.opengl.EGLConfig;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.GLES30;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
@ -68,6 +73,7 @@ import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
import net.kdt.pojavlaunch.utils.FileUtils;
|
||||
import net.kdt.pojavlaunch.utils.JREUtils;
|
||||
import net.kdt.pojavlaunch.utils.JSONUtils;
|
||||
import net.kdt.pojavlaunch.utils.MCOptionUtils;
|
||||
import net.kdt.pojavlaunch.utils.OldVersionsUtils;
|
||||
import net.kdt.pojavlaunch.value.DependentLibrary;
|
||||
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
||||
@ -178,6 +184,97 @@ public final class Tools {
|
||||
NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimization mods based on Sodium can mitigate the render distance issue. Check if Sodium
|
||||
* or its derivative is currently installed to skip the render distance check.
|
||||
* @param gameDir current game directory
|
||||
* @return whether sodium or a sodium-based mod is installed
|
||||
*/
|
||||
private static boolean hasSodium(File gameDir) {
|
||||
File modsDir = new File(gameDir, "mods");
|
||||
File[] mods = modsDir.listFiles(file -> file.isFile() && file.getName().endsWith(".jar"));
|
||||
if(mods == null) return false;
|
||||
for(File file : mods) {
|
||||
String name = file.getName();
|
||||
if(name.contains("sodium") ||
|
||||
name.contains("embeddium") ||
|
||||
name.contains("rubidium")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize OpenGL and do checks to see if the GPU of the device is affected by the render
|
||||
* distance issue.
|
||||
|
||||
* Currently only checks whether the user has an Adreno GPU capable of OpenGL ES 3
|
||||
* and surfaceless rendering installed.
|
||||
|
||||
* This issue is caused by a very severe limit on the amount of GL buffer names that could be allocated
|
||||
* by the Adreno properietary GLES driver.
|
||||
|
||||
* @return whether the GPU is affected by the Large Thin Wrapper render distance issue on vanilla
|
||||
*/
|
||||
private static boolean affectedByRenderDistanceIssue() {
|
||||
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||
if(eglDisplay == EGL14.EGL_NO_DISPLAY || !EGL14.eglInitialize(eglDisplay, null, 0, null, 0)) return false;
|
||||
int[] egl_attributes = new int[] {
|
||||
EGL14.EGL_BLUE_SIZE, 8,
|
||||
EGL14.EGL_GREEN_SIZE, 8,
|
||||
EGL14.EGL_RED_SIZE, 8,
|
||||
EGL14.EGL_ALPHA_SIZE, 8,
|
||||
EGL14.EGL_DEPTH_SIZE, 24,
|
||||
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
|
||||
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
|
||||
EGL14.EGL_NONE
|
||||
};
|
||||
EGLConfig[] config = new EGLConfig[1];
|
||||
int[] num_configs = new int[]{0};
|
||||
if(!EGL14.eglChooseConfig(eglDisplay, egl_attributes, 0, config, 0, 1, num_configs, 0) || num_configs[0] == 0) {
|
||||
EGL14.eglTerminate(eglDisplay);
|
||||
Log.e("CheckVendor", "Failed to choose an EGL config");
|
||||
return false;
|
||||
}
|
||||
int[] egl_context_attributes = new int[] { EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE };
|
||||
EGLContext context = EGL14.eglCreateContext(eglDisplay, config[0], EGL14.EGL_NO_CONTEXT, egl_context_attributes, 0);
|
||||
if(context == EGL14.EGL_NO_CONTEXT) {
|
||||
Log.e("CheckVendor", "Failed to create a context");
|
||||
EGL14.eglTerminate(eglDisplay);
|
||||
return false;
|
||||
}
|
||||
if(!EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, context)) {
|
||||
Log.e("CheckVendor", "Failed to make context current");
|
||||
EGL14.eglDestroyContext(eglDisplay, context);
|
||||
EGL14.eglTerminate(eglDisplay);
|
||||
}
|
||||
boolean is_adreno = GLES30.glGetString(GLES30.GL_VENDOR).equals("Qualcomm") &&
|
||||
GLES30.glGetString(GLES30.GL_RENDERER).contains("Adreno");
|
||||
Log.e("CheckVendor", "Running Adreno graphics: "+is_adreno);
|
||||
EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
||||
EGL14.eglDestroyContext(eglDisplay, context);
|
||||
EGL14.eglTerminate(eglDisplay);
|
||||
return is_adreno;
|
||||
}
|
||||
|
||||
private static boolean checkRenderDistance(File gamedir) {
|
||||
if(!"opengles3_ltw".equals(Tools.LOCAL_RENDERER)) return false;
|
||||
if(!affectedByRenderDistanceIssue()) return false;
|
||||
if(hasSodium(gamedir)) return false;
|
||||
|
||||
int renderDistance;
|
||||
try {
|
||||
MCOptionUtils.load();
|
||||
String renderDistanceString = MCOptionUtils.get("renderDistance");
|
||||
renderDistance = Integer.parseInt(renderDistanceString);
|
||||
}catch (Exception e) {
|
||||
Log.e("Tools", "Failed to check render distance", e);
|
||||
renderDistance = 12; // Assume Minecraft's default render distance
|
||||
}
|
||||
// 7 is the render distance "magic number" above which MC creates too many buffers
|
||||
// for Adreno's OpenGL ES implementation
|
||||
return renderDistance > 7;
|
||||
}
|
||||
|
||||
public static void launchMinecraft(final AppCompatActivity activity, MinecraftAccount minecraftAccount,
|
||||
MinecraftProfile minecraftProfile, String versionId, int versionJavaRequirement) throws Throwable {
|
||||
int freeDeviceMemory = getFreeDeviceMemory(activity);
|
||||
@ -203,10 +300,27 @@ public final class Tools {
|
||||
// to start after the activity is shown again
|
||||
}
|
||||
}
|
||||
Runtime runtime = MultiRTUtils.forceReread(Tools.pickRuntime(minecraftProfile, versionJavaRequirement));
|
||||
JMinecraftVersionList.Version versionInfo = Tools.getVersionInfo(versionId);
|
||||
LauncherProfiles.load();
|
||||
File gamedir = Tools.getGameDirPath(minecraftProfile);
|
||||
if(checkRenderDistance(gamedir)) {
|
||||
LifecycleAwareAlertDialog.DialogCreator dialogCreator = ((alertDialog, dialogBuilder) ->
|
||||
dialogBuilder.setMessage(activity.getString(R.string.ltw_render_distance_warning_msg))
|
||||
.setPositiveButton(android.R.string.ok, (d, w)->{}));
|
||||
if(LifecycleAwareAlertDialog.haltOnDialog(activity.getLifecycle(), activity, dialogCreator)) {
|
||||
return;
|
||||
}
|
||||
// If the code goes here, it means that the user clicked "OK". Fix the render distance.
|
||||
try {
|
||||
MCOptionUtils.set("renderDistance", "7");
|
||||
MCOptionUtils.save();
|
||||
}catch (Exception e) {
|
||||
Log.e("Tools", "Failed to fix render distance setting", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Runtime runtime = MultiRTUtils.forceReread(Tools.pickRuntime(minecraftProfile, versionJavaRequirement));
|
||||
JMinecraftVersionList.Version versionInfo = Tools.getVersionInfo(versionId);
|
||||
|
||||
|
||||
// Pre-process specific files
|
||||
@ -1271,12 +1385,16 @@ public final class Tools {
|
||||
boolean deviceHasVulkan = checkVulkanSupport(context.getPackageManager());
|
||||
// Currently, only 32-bit x86 does not have the Zink binary
|
||||
boolean deviceHasZinkBinary = !(Architecture.is32BitsDevice() && Architecture.isx86Device());
|
||||
boolean deviceHasOpenGLES3 = JREUtils.getDetectedVersion() >= 3;
|
||||
// LTW is an optional proprietary dependency
|
||||
boolean appHasLtw = new File(Tools.NATIVE_LIB_DIR, "libltw.so").exists();
|
||||
List<String> rendererIds = new ArrayList<>(defaultRenderers.length);
|
||||
List<String> rendererNames = new ArrayList<>(defaultRendererNames.length);
|
||||
for(int i = 0; i < defaultRenderers.length; i++) {
|
||||
String rendererId = defaultRenderers[i];
|
||||
if(rendererId.contains("vulkan") && !deviceHasVulkan) continue;
|
||||
if(rendererId.contains("zink") && !deviceHasZinkBinary) continue;
|
||||
if(rendererId.contains("ltw") && (!deviceHasOpenGLES3 || !appHasLtw)) continue;
|
||||
rendererIds.add(rendererId);
|
||||
rendererNames.add(defaultRendererNames[i]);
|
||||
}
|
||||
|
@ -220,9 +220,9 @@ public class JREUtils {
|
||||
|
||||
if(LOCAL_RENDERER != null) {
|
||||
envMap.put("POJAV_RENDERER", LOCAL_RENDERER);
|
||||
if(LOCAL_RENDERER.equals("opengles3_desktopgl_angle_vulkan")) {
|
||||
if(LOCAL_RENDERER.equals("opengles3_ltw")) {
|
||||
envMap.put("LIBGL_ES", "3");
|
||||
envMap.put("POJAVEXEC_EGL","libEGL_angle.so"); // Use ANGLE EGL
|
||||
envMap.put("POJAVEXEC_EGL","libltw.so"); // Use ANGLE EGL
|
||||
}
|
||||
}
|
||||
if(LauncherPreferences.PREF_BIG_CORE_AFFINITY) envMap.put("POJAV_BIG_CORE_AFFINITY", "1");
|
||||
@ -464,7 +464,7 @@ public class JREUtils {
|
||||
case "opengles3":
|
||||
renderLibrary = "libgl4es_114.so"; break;
|
||||
case "vulkan_zink": renderLibrary = "libOSMesa.so"; break;
|
||||
case "opengles3_desktopgl_angle_vulkan" : renderLibrary = "libtinywrapper.so"; break;
|
||||
case "opengles3_ltw" : renderLibrary = "libltw.so"; break;
|
||||
default:
|
||||
Log.w("RENDER_LIBRARY", "No renderer selected, defaulting to opengles2");
|
||||
renderLibrary = "libgl4es_114.so";
|
||||
|
@ -8,18 +8,6 @@ HERE_PATH := $(LOCAL_PATH)
|
||||
|
||||
LOCAL_PATH := $(HERE_PATH)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := angle_gles2
|
||||
LOCAL_SRC_FILES := tinywrapper/angle-gles/$(TARGET_ARCH_ABI)/libGLESv2_angle.so
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := tinywrapper
|
||||
LOCAL_SHARED_LIBRARIES := angle_gles2
|
||||
LOCAL_SRC_FILES := tinywrapper/main.c tinywrapper/string_utils.c
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/tinywrapper
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,prefab/bytehook)
|
||||
LOCAL_PATH := $(HERE_PATH)
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,201 +0,0 @@
|
||||
//#import <Foundation/Foundation.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "GL/gl.h"
|
||||
#include "GLES3/gl32.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
#define LOOKUP_FUNC(func) \
|
||||
if (!gles_##func) { \
|
||||
gles_##func = dlsym(RTLD_NEXT, #func); \
|
||||
} if (!gles_##func) { \
|
||||
gles_##func = dlsym(RTLD_DEFAULT, #func); \
|
||||
}
|
||||
|
||||
int proxy_width, proxy_height, proxy_intformat, maxTextureSize;
|
||||
|
||||
void glBindFragDataLocationEXT(GLuint program, GLuint colorNumber, const char * name);
|
||||
|
||||
void(*gles_glGetTexLevelParameteriv)(GLenum target, GLint level, GLenum pname, GLint *params);
|
||||
void(*gles_glShaderSource)(GLuint shader, GLsizei count, const GLchar * const *string, const GLint *length);
|
||||
void(*gles_glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data);
|
||||
|
||||
void glBindFragDataLocation(GLuint program, GLuint colorNumber, const char * name) {
|
||||
glBindFragDataLocationEXT(program, colorNumber, name);
|
||||
}
|
||||
|
||||
void glClearDepth(GLdouble depth) {
|
||||
glClearDepthf(depth);
|
||||
}
|
||||
|
||||
void *glMapBuffer(GLenum target, GLenum access) {
|
||||
// Use: GL_EXT_map_buffer_range
|
||||
|
||||
GLenum access_range;
|
||||
GLint length;
|
||||
|
||||
switch (target) {
|
||||
// GL 4.2
|
||||
case GL_ATOMIC_COUNTER_BUFFER:
|
||||
|
||||
// GL 4.3
|
||||
case GL_DISPATCH_INDIRECT_BUFFER:
|
||||
case GL_SHADER_STORAGE_BUFFER :
|
||||
|
||||
// GL 4.4
|
||||
case GL_QUERY_BUFFER:
|
||||
printf("ERROR: glMapBuffer unsupported target=0x%x", target);
|
||||
break; // not supported for now
|
||||
|
||||
case GL_DRAW_INDIRECT_BUFFER:
|
||||
case GL_TEXTURE_BUFFER:
|
||||
printf("ERROR: glMapBuffer unimplemented target=0x%x", target);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (access) {
|
||||
case GL_READ_ONLY:
|
||||
access_range = GL_MAP_READ_BIT;
|
||||
break;
|
||||
|
||||
case GL_WRITE_ONLY:
|
||||
access_range = GL_MAP_WRITE_BIT;
|
||||
break;
|
||||
|
||||
case GL_READ_WRITE:
|
||||
access_range = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);
|
||||
return glMapBufferRange(target, 0, length, access_range);
|
||||
}
|
||||
|
||||
void glShaderSource(GLuint shader, GLsizei count, const GLchar * const *string, const GLint *length) {
|
||||
LOOKUP_FUNC(glShaderSource)
|
||||
|
||||
// DBG(printf("glShaderSource(%d, %d, %p, %p)\n", shader, count, string, length);)
|
||||
char *source = NULL;
|
||||
char *converted;
|
||||
|
||||
// get the size of the shader sources and than concatenate in a single string
|
||||
int l = 0;
|
||||
for (int i=0; i<count; i++) l+=(length && length[i] >= 0)?length[i]:strlen(string[i]);
|
||||
if (source) free(source);
|
||||
source = calloc(1, l+1);
|
||||
if(length) {
|
||||
for (int i=0; i<count; i++) {
|
||||
if(length[i] >= 0)
|
||||
strncat(source, string[i], length[i]);
|
||||
else
|
||||
strcat(source, string[i]);
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<count; i++)
|
||||
strcat(source, string[i]);
|
||||
}
|
||||
|
||||
char *source2 = strchr(source, '#');
|
||||
if (!source2) {
|
||||
source2 = source;
|
||||
}
|
||||
// are there #version?
|
||||
if (!strncmp(source2, "#version ", 9)) {
|
||||
converted = strdup(source2);
|
||||
if (converted[9] == '1') {
|
||||
if (converted[10] - '0' < 2) {
|
||||
// 100, 110 -> 120
|
||||
converted[10] = '2';
|
||||
} else if (converted[10] - '0' < 6) {
|
||||
// 130, 140, 150 -> 330
|
||||
converted[9] = converted[10] = '3';
|
||||
}
|
||||
}
|
||||
// remove "core", is it safe?
|
||||
if (!strncmp(&converted[13], "core", 4)) {
|
||||
strncpy(&converted[13], "\n//c", 4);
|
||||
}
|
||||
} else {
|
||||
converted = calloc(1, strlen(source) + 13);
|
||||
strcpy(converted, "#version 120\n");
|
||||
strcpy(&converted[13], strdup(source));
|
||||
}
|
||||
|
||||
int convertedLen = strlen(converted);
|
||||
|
||||
#ifdef __APPLE__
|
||||
// patch OptiFine 1.17.x
|
||||
if (FindString(converted, "\nuniform mat4 textureMatrix = mat4(1.0);")) {
|
||||
InplaceReplace(converted, &convertedLen, "\nuniform mat4 textureMatrix = mat4(1.0);", "\n#define textureMatrix mat4(1.0)");
|
||||
}
|
||||
#endif
|
||||
|
||||
// some needed exts
|
||||
const char* extensions =
|
||||
"#extension GL_EXT_blend_func_extended : enable\n"
|
||||
// For OptiFine (see patch above)
|
||||
"#extension GL_EXT_shader_non_constant_global_initializers : enable\n";
|
||||
converted = InplaceInsert(GetLine(converted, 1), extensions, converted, &convertedLen);
|
||||
|
||||
gles_glShaderSource(shader, 1, (const GLchar * const*)((converted)?(&converted):(&source)), NULL);
|
||||
|
||||
free(source);
|
||||
free(converted);
|
||||
}
|
||||
|
||||
int isProxyTexture(GLenum target) {
|
||||
switch (target) {
|
||||
case GL_PROXY_TEXTURE_1D:
|
||||
case GL_PROXY_TEXTURE_2D:
|
||||
case GL_PROXY_TEXTURE_3D:
|
||||
case GL_PROXY_TEXTURE_RECTANGLE_ARB:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inline nlevel(int size, int level) {
|
||||
if(size) {
|
||||
size>>=level;
|
||||
if(!size) size=1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params) {
|
||||
LOOKUP_FUNC(glGetTexLevelParameteriv)
|
||||
// NSLog("glGetTexLevelParameteriv(%x, %d, %x, %p)", target, level, pname, params);
|
||||
if (isProxyTexture(target)) {
|
||||
switch (pname) {
|
||||
case GL_TEXTURE_WIDTH:
|
||||
(*params) = nlevel(proxy_width,level);
|
||||
break;
|
||||
case GL_TEXTURE_HEIGHT:
|
||||
(*params) = nlevel(proxy_height,level);
|
||||
break;
|
||||
case GL_TEXTURE_INTERNAL_FORMAT:
|
||||
(*params) = proxy_intformat;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
gles_glGetTexLevelParameteriv(target, level, pname, params);
|
||||
}
|
||||
}
|
||||
|
||||
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data) {
|
||||
LOOKUP_FUNC(glTexImage2D)
|
||||
if (isProxyTexture(target)) {
|
||||
if (!maxTextureSize) {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
// maxTextureSize = 16384;
|
||||
// NSLog(@"Maximum texture size: %d", maxTextureSize);
|
||||
}
|
||||
proxy_width = ((width<<level)>maxTextureSize)?0:width;
|
||||
proxy_height = ((height<<level)>maxTextureSize)?0:height;
|
||||
proxy_intformat = internalformat;
|
||||
// swizzle_internalformat((GLenum *) &internalformat, format, type);
|
||||
} else {
|
||||
gles_glTexImage2D(target, level, internalformat, width, height, border, format, type, data);
|
||||
}
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "string_utils.h"
|
||||
|
||||
const char* AllSeparators = " \t\n\r.,;()[]{}-<>+*/%&\\\"'^$=!:?";
|
||||
|
||||
char* ResizeIfNeeded(char* pBuffer, int *size, int addsize);
|
||||
|
||||
char* InplaceReplace(char* pBuffer, int* size, const char* S, const char* D)
|
||||
{
|
||||
int lS = strlen(S), lD = strlen(D);
|
||||
pBuffer = ResizeIfNeeded(pBuffer, size, (lD-lS)*CountString(pBuffer, S));
|
||||
char* p = pBuffer;
|
||||
while((p = strstr(p, S)))
|
||||
{
|
||||
// found an occurence of S
|
||||
// check if good to replace, strchr also found '\0' :)
|
||||
if(strchr(AllSeparators, p[lS])!=NULL && (p==pBuffer || strchr(AllSeparators, p[-1])!=NULL)) {
|
||||
// move out rest of string
|
||||
memmove(p+lD, p+lS, strlen(p)-lS+1);
|
||||
// replace
|
||||
memcpy(p, D, strlen(D));
|
||||
// next
|
||||
p+=lD;
|
||||
} else p+=lS;
|
||||
}
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
char* InplaceInsert(char* pBuffer, const char* S, char* master, int* size)
|
||||
{
|
||||
char* m = ResizeIfNeeded(master, size, strlen(S));
|
||||
if(m!=master) {
|
||||
pBuffer += (m-master);
|
||||
master = m;
|
||||
}
|
||||
char* p = pBuffer;
|
||||
int lS = strlen(S), ll = strlen(pBuffer);
|
||||
memmove(p+lS, p, ll+1);
|
||||
memcpy(p, S, lS);
|
||||
|
||||
return master;
|
||||
}
|
||||
|
||||
char* GetLine(char* pBuffer, int num)
|
||||
{
|
||||
char *p = pBuffer;
|
||||
while(num-- && (p=strstr(p, "\n"))) p+=strlen("\n");
|
||||
return (p)?p:pBuffer;
|
||||
}
|
||||
|
||||
int CountLine(const char* pBuffer)
|
||||
{
|
||||
int n=0;
|
||||
const char* p = pBuffer;
|
||||
while((p=strstr(p, "\n"))) {
|
||||
p+=strlen("\n");
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int GetLineFor(const char* pBuffer, const char* S)
|
||||
{
|
||||
int n=0;
|
||||
const char* p = pBuffer;
|
||||
const char* end = FindString(pBuffer, S);
|
||||
if(!end)
|
||||
return 0;
|
||||
while((p=strstr(p, "\n"))) {
|
||||
p+=strlen("\n");
|
||||
n++;
|
||||
if(p>=end)
|
||||
return n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int CountString(const char* pBuffer, const char* S)
|
||||
{
|
||||
const char* p = pBuffer;
|
||||
int lS = strlen(S);
|
||||
int n = 0;
|
||||
while((p = strstr(p, S)))
|
||||
{
|
||||
// found an occurence of S
|
||||
// check if good to count, strchr also found '\0' :)
|
||||
if(strchr(AllSeparators, p[lS])!=NULL && (p==pBuffer || strchr(AllSeparators, p[-1])!=NULL))
|
||||
n++;
|
||||
p+=lS;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
const char* FindString(const char* pBuffer, const char* S)
|
||||
{
|
||||
const char* p = pBuffer;
|
||||
int lS = strlen(S);
|
||||
while((p = strstr(p, S)))
|
||||
{
|
||||
// found an occurence of S
|
||||
// check if good to count, strchr also found '\0' :)
|
||||
if(strchr(AllSeparators, p[lS])!=NULL && (p==pBuffer || strchr(AllSeparators, p[-1])!=NULL))
|
||||
return p;
|
||||
p+=lS;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* FindStringNC(char* pBuffer, const char* S)
|
||||
{
|
||||
char* p = pBuffer;
|
||||
int lS = strlen(S);
|
||||
while((p = strstr(p, S)))
|
||||
{
|
||||
// found an occurence of S
|
||||
// check if good to count, strchr also found '\0' :)
|
||||
if(strchr(AllSeparators, p[lS])!=NULL && (p==pBuffer || strchr(AllSeparators, p[-1])!=NULL))
|
||||
return p;
|
||||
p+=lS;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* ResizeIfNeeded(char* pBuffer, int *size, int addsize) {
|
||||
char* p = pBuffer;
|
||||
int newsize = strlen(pBuffer)+addsize+1;
|
||||
if (newsize>*size) {
|
||||
newsize += 100;
|
||||
p = (char*)realloc(pBuffer, newsize);
|
||||
*size=newsize;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
char* Append(char* pBuffer, int* size, const char* S) {
|
||||
char* p =pBuffer;
|
||||
p = ResizeIfNeeded(pBuffer, size, strlen(S));
|
||||
strcat(p, S);
|
||||
return p;
|
||||
}
|
||||
|
||||
int isBlank(char c) {
|
||||
switch(c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ':':
|
||||
case ',':
|
||||
case ';':
|
||||
case '/':
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
char* StrNext(char *pBuffer, const char* S) {
|
||||
if(!pBuffer) return NULL;
|
||||
char *p = strstr(pBuffer, S);
|
||||
return (p)?p:(p+strlen(S));
|
||||
}
|
||||
|
||||
char* NextStr(char* pBuffer) {
|
||||
if(!pBuffer) return NULL;
|
||||
while(isBlank(*pBuffer))
|
||||
++pBuffer;
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
char* NextBlank(char* pBuffer) {
|
||||
if(!pBuffer) return NULL;
|
||||
while(!isBlank(*pBuffer))
|
||||
++pBuffer;
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
char* NextLine(char* pBuffer) {
|
||||
if(!pBuffer) return NULL;
|
||||
while(*pBuffer && *pBuffer!='\n')
|
||||
++pBuffer;
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
const char* GetNextStr(char* pBuffer) {
|
||||
static char buff[100] = {0};
|
||||
buff[0] = '\0';
|
||||
if(!pBuffer) return NULL;
|
||||
char* p1 = NextStr(pBuffer);
|
||||
if(!p1) return buff;
|
||||
char* p2 = NextBlank(p1);
|
||||
if(!p2) return buff;
|
||||
int i=0;
|
||||
while(p1!=p2 && i<99)
|
||||
buff[i++] = *(p1++);
|
||||
buff[i] = '\0';
|
||||
return buff;
|
||||
}
|
||||
|
||||
int CountStringSimple(char* pBuffer, const char* S)
|
||||
{
|
||||
char* p = pBuffer;
|
||||
int lS = strlen(S);
|
||||
int n = 0;
|
||||
while((p = strstr(p, S)))
|
||||
{
|
||||
// found an occurence of S
|
||||
n++;
|
||||
p+=lS;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
char* InplaceReplaceSimple(char* pBuffer, int* size, const char* S, const char* D)
|
||||
{
|
||||
int lS = strlen(S), lD = strlen(D);
|
||||
pBuffer = ResizeIfNeeded(pBuffer, size, (lD-lS)*CountStringSimple(pBuffer, S));
|
||||
char* p = pBuffer;
|
||||
while((p = strstr(p, S)))
|
||||
{
|
||||
// found an occurence of S
|
||||
// move out rest of string
|
||||
memmove(p+lD, p+lS, strlen(p)-lS+1);
|
||||
// replace
|
||||
memcpy(p, D, strlen(D));
|
||||
// next
|
||||
p+=lD;
|
||||
}
|
||||
|
||||
return pBuffer;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#ifndef _GL4ES_STRING_UTILS_H_
|
||||
#define _GL4ES_STRING_UTILS_H_
|
||||
|
||||
extern const char* AllSeparators;
|
||||
|
||||
const char* FindString(const char* pBuffer, const char* S);
|
||||
char* FindStringNC(char* pBuffer, const char* S);
|
||||
int CountString(const char* pBuffer, const char* S);
|
||||
char* ResizeIfNeeded(char* pBuffer, int *size, int addsize);
|
||||
char* InplaceReplace(char* pBuffer, int* size, const char* S, const char* D);
|
||||
char* Append(char* pBuffer, int* size, const char* S);
|
||||
char* InplaceInsert(char* pBuffer, const char* S, char* master, int* size);
|
||||
char* GetLine(char* pBuffer, int num);
|
||||
int CountLine(const char* pBuffer);
|
||||
int GetLineFor(const char* pBuffer, const char* S); // get the line number for 1st occurent of S in pBuffer
|
||||
char* StrNext(char *pBuffer, const char* S); // mostly as strstr, but go after the substring if found
|
||||
//"blank" (space, tab, cr, lf,":", ",", ";", ".", "/")
|
||||
char* NextStr(char* pBuffer); // go to next non "blank"
|
||||
char* NextBlank(char* pBuffer); // go to next "blank"
|
||||
char* NextLine(char* pBuffer); // go to next new line (crlf not included)
|
||||
|
||||
const char* GetNextStr(char* pBuffer); // get a (static) copy of next str (until next separator), can be a simple number or separator also
|
||||
|
||||
// those function don't try to be smart with separators...
|
||||
int CountStringSimple(char* pBuffer, const char* S);
|
||||
char* InplaceReplaceSimple(char* pBuffer, int* size, const char* S, const char* D);
|
||||
|
||||
|
||||
#endif // _GL4ES_STRING_UTILS_H_
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,7 +3,7 @@
|
||||
<string-array name="renderer">
|
||||
<item name="1">@string/mcl_setting_renderer_gles2_4</item>
|
||||
<item name="2">@string/mcl_setting_renderer_vulkan_zink</item>
|
||||
<item name="3">@string/mcl_setting_renderer_angle</item>
|
||||
<item name="3">@string/mcl_setting_renderer_ltw</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="menu_customcontrol">
|
||||
@ -36,7 +36,7 @@
|
||||
<string-array name="renderer_values">
|
||||
<item>opengles2</item> <!-- gl4es_extra 1.1.4 with OpenGL ES 2/"3" -->
|
||||
<item>vulkan_zink</item> <!-- virglrenderer with OpenGL ES 3 -->
|
||||
<item>opengles3_desktopgl_angle_vulkan</item>
|
||||
<item>opengles3_ltw</item>
|
||||
</string-array>
|
||||
<string-array name="download_source_names">
|
||||
<item>@string/global_default</item>
|
||||
|
@ -66,7 +66,7 @@
|
||||
<string name="mcl_setting_category_renderer">Renderer</string>
|
||||
<string name="mcl_setting_renderer_gles2_4">Holy GL4ES - (all versions, fast)</string>
|
||||
<string name="mcl_setting_renderer_vulkan_zink">Zink (Vulkan) - (all versions, mid)</string>
|
||||
<string name="mcl_setting_renderer_angle">ANGLE (Vulkan) - (1.17+ only, mid)</string>
|
||||
<string name="mcl_setting_renderer_ltw">LTW (OpenGL ES 3) - 1.17+ only</string>
|
||||
<string name="mcl_setting_veroption_release">Release</string>
|
||||
<string name="mcl_setting_veroption_snapshot">Snapshot</string>
|
||||
<string name="mcl_setting_veroption_oldalpha">Old-alpha</string>
|
||||
@ -418,6 +418,7 @@
|
||||
<string name="preference_remap_controller_title">Change controller key bindings</string>
|
||||
<string name="preference_remap_controller_description">Allows you to modify the keyboard keys bound to each controller button</string>
|
||||
<string name="mcl_button_discord">Discord</string>
|
||||
<string name="ltw_render_distance_warning_msg">Your GPU is not capable of rendering above 7 render distance without Sodium or other similar mods. The render distance will be automatically reduced when you click "OK".</string>
|
||||
<string name="mcl_button_open_directory">Open game directory</string>
|
||||
<string name="discord_invite" translatable="false">https://discord.com/invite/aenk3EUvER</string>
|
||||
<string name="local_login_bad_username_title">Unsuitable username</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user