1
0

增加自定义画质设置

This commit is contained in:
chinosk 2024-05-26 21:34:18 +08:00
parent acac544183
commit 53cb596845
No known key found for this signature in database
GPG Key ID: 00610B08C1BF7BE9
15 changed files with 785 additions and 293 deletions

View File

@ -433,6 +433,48 @@ namespace GakumasLocal::HookMain {
return VLDOF_IsActive_Orig(_this); return VLDOF_IsActive_Orig(_this);
} }
DEFINE_HOOK(void, CampusQualityManager_set_TargetFrameRate, (void* _this, float value)) {
// Log::InfoFmt("CampusQualityManager_set_TargetFrameRate: %f", value);
const auto configFps = Config::targetFrameRate;
CampusQualityManager_set_TargetFrameRate_Orig(_this, configFps == 0 ? value : (float)configFps);
}
DEFINE_HOOK(void, CampusQualityManager_ApplySetting, (void* _this, int qualitySettingsLevel, int maxBufferPixel, float renderScale, int volumeIndex)) {
if (Config::targetFrameRate != 0) {
CampusQualityManager_set_TargetFrameRate_Orig(_this, Config::targetFrameRate);
}
if (Config::useCustomeGraphicSettings) {
static auto SetReflectionQuality = Il2cppUtils::GetMethod("campus-submodule.Runtime.dll", "Campus.Common",
"CampusQualityManager", "SetReflectionQuality");
static auto SetLODQuality = Il2cppUtils::GetMethod("campus-submodule.Runtime.dll", "Campus.Common",
"CampusQualityManager", "SetLODQuality");
static auto Enum_GetValues = Il2cppUtils::GetMethod("mscorlib.dll", "System", "Enum", "GetValues");
static auto QualityLevel_klass = Il2cppUtils::GetClass("campus-submodule.Runtime.dll", "", "QualityLevel");
static auto values = Enum_GetValues->Invoke<UnityResolve::UnityType::Array<int>*>(QualityLevel_klass->GetType())->ToVector();
if (values.empty()) {
values = {0x0, 0xa, 0x14, 0x1e, 0x28, 0x64};
}
if (Config::lodQualityLevel >= values.size()) Config::lodQualityLevel = values.size() - 1;
if (Config::reflectionQualityLevel >= values.size()) Config::reflectionQualityLevel = values.size() - 1;
SetLODQuality->Invoke<void>(_this, values[Config::lodQualityLevel]);
SetReflectionQuality->Invoke<void>(_this, values[Config::reflectionQualityLevel]);
qualitySettingsLevel = Config::qualitySettingsLevel;
maxBufferPixel = Config::maxBufferPixel;
renderScale = Config::renderScale;
volumeIndex = Config::volumeIndex;
Log::ShowToastFmt("ApplySetting\nqualityLevel: %d, maxBufferPixel: %d\nenderScale: %f, volumeIndex: %d\nLODQualityLv: %d, ReflectionLv: %d",
qualitySettingsLevel, maxBufferPixel, renderScale, volumeIndex, Config::lodQualityLevel, Config::reflectionQualityLevel);
}
CampusQualityManager_ApplySetting_Orig(_this, qualitySettingsLevel, maxBufferPixel, renderScale, volumeIndex);
}
void StartInjectFunctions() { void StartInjectFunctions() {
const auto hookInstaller = Plugin::GetInstance().GetHookInstaller(); const auto hookInstaller = Plugin::GetInstance().GetHookInstaller();
UnityResolve::Init(xdl_open(hookInstaller->m_il2cppLibraryPath.c_str(), RTLD_NOW), UnityResolve::Mode::Il2Cpp); UnityResolve::Init(xdl_open(hookInstaller->m_il2cppLibraryPath.c_str(), RTLD_NOW), UnityResolve::Mode::Il2Cpp);
@ -488,6 +530,14 @@ namespace GakumasLocal::HookMain {
Il2cppUtils::GetMethodPointer("Unity.RenderPipelines.Universal.Runtime.dll", "VL.Rendering", Il2cppUtils::GetMethodPointer("Unity.RenderPipelines.Universal.Runtime.dll", "VL.Rendering",
"VLDOF", "IsActive")); "VLDOF", "IsActive"));
ADD_HOOK(CampusQualityManager_ApplySetting,
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
"CampusQualityManager", "ApplySetting"));
ADD_HOOK(CampusQualityManager_set_TargetFrameRate,
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
"CampusQualityManager", "set_TargetFrameRate"));
ADD_HOOK(Internal_LogException, Il2cppUtils::il2cpp_resolve_icall( ADD_HOOK(Internal_LogException, Il2cppUtils::il2cpp_resolve_icall(
"UnityEngine.DebugLogHandler::Internal_LogException(System.Exception,UnityEngine.Object)")); "UnityEngine.DebugLogHandler::Internal_LogException(System.Exception,UnityEngine.Object)"));
ADD_HOOK(Internal_Log, Il2cppUtils::il2cpp_resolve_icall( ADD_HOOK(Internal_Log, Il2cppUtils::il2cpp_resolve_icall(

View File

@ -1,30 +1,26 @@
#include "Log.h" #include "Log.h"
#include <android/log.h> #include <android/log.h>
#include <Misc.h>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <thread>
std::string format(const char* fmt, ...) { extern JavaVM* g_javaVM;
va_list args; extern jclass g_gakumasHookMainClass;
va_start(args, fmt); extern jmethodID showToastMethodId;
va_list args_copy;
va_copy(args_copy, args);
// 计算格式化后的字符串长度 #define GetParamStringResult(name)\
int size = vsnprintf(nullptr, 0, fmt, args_copy) + 1; // 加上额外的终止符空间 va_list args;\
va_end(args_copy); va_start(args, fmt);\
va_list args_copy;\
// 动态分配缓冲区 va_copy(args_copy, args);\
char* buffer = new char[size]; int size = vsnprintf(nullptr, 0, fmt, args_copy) + 1;\
va_end(args_copy);\
// 格式化字符串 char* buffer = new char[size];\
vsnprintf(buffer, size, fmt, args); vsnprintf(buffer, size, fmt, args);\
va_end(args);\
va_end(args); std::string name(buffer);\
delete[] buffer
std::string result(buffer);
delete[] buffer; // 释放缓冲区
return result;
}
namespace GakumasLocal::Log { namespace GakumasLocal::Log {
@ -33,26 +29,7 @@ namespace GakumasLocal::Log {
} }
void LogFmt(int prio, const char* fmt, ...) { void LogFmt(int prio, const char* fmt, ...) {
va_list args; GetParamStringResult(result);
va_start(args, fmt);
va_list args_copy;
va_copy(args_copy, args);
// 计算格式化后的字符串长度
int size = vsnprintf(nullptr, 0, fmt, args_copy) + 1; // 加上额外的终止符空间
va_end(args_copy);
// 动态分配缓冲区
char* buffer = new char[size];
// 格式化字符串
vsnprintf(buffer, size, fmt, args);
va_end(args);
std::string result(buffer);
delete[] buffer; // 释放缓冲区
Log(prio, result.c_str()); Log(prio, result.c_str());
} }
@ -61,26 +38,7 @@ namespace GakumasLocal::Log {
} }
void InfoFmt(const char* fmt, ...) { void InfoFmt(const char* fmt, ...) {
va_list args; GetParamStringResult(result);
va_start(args, fmt);
va_list args_copy;
va_copy(args_copy, args);
// 计算格式化后的字符串长度
int size = vsnprintf(nullptr, 0, fmt, args_copy) + 1; // 加上额外的终止符空间
va_end(args_copy);
// 动态分配缓冲区
char* buffer = new char[size];
// 格式化字符串
vsnprintf(buffer, size, fmt, args);
va_end(args);
std::string result(buffer);
delete[] buffer; // 释放缓冲区
Info(result.c_str()); Info(result.c_str());
} }
@ -89,26 +47,7 @@ namespace GakumasLocal::Log {
} }
void ErrorFmt(const char* fmt, ...) { void ErrorFmt(const char* fmt, ...) {
va_list args; GetParamStringResult(result);
va_start(args, fmt);
va_list args_copy;
va_copy(args_copy, args);
// 计算格式化后的字符串长度
int size = vsnprintf(nullptr, 0, fmt, args_copy) + 1; // 加上额外的终止符空间
va_end(args_copy);
// 动态分配缓冲区
char* buffer = new char[size];
// 格式化字符串
vsnprintf(buffer, size, fmt, args);
va_end(args);
std::string result(buffer);
delete[] buffer; // 释放缓冲区
Error(result.c_str()); Error(result.c_str());
} }
@ -117,50 +56,44 @@ namespace GakumasLocal::Log {
} }
void DebugFmt(const char* fmt, ...) { void DebugFmt(const char* fmt, ...) {
va_list args; GetParamStringResult(result);
va_start(args, fmt);
va_list args_copy;
va_copy(args_copy, args);
// 计算格式化后的字符串长度
int size = vsnprintf(nullptr, 0, fmt, args_copy) + 1; // 加上额外的终止符空间
va_end(args_copy);
// 动态分配缓冲区
char* buffer = new char[size];
// 格式化字符串
vsnprintf(buffer, size, fmt, args);
va_end(args);
std::string result(buffer);
delete[] buffer; // 释放缓冲区
Debug(result.c_str()); Debug(result.c_str());
} }
void LogUnityLog(int prio, const char* fmt, ...) { void LogUnityLog(int prio, const char* fmt, ...) {
va_list args; GetParamStringResult(result);
va_start(args, fmt);
va_list args_copy;
va_copy(args_copy, args);
// 计算格式化后的字符串长度
int size = vsnprintf(nullptr, 0, fmt, args_copy) + 1; // 加上额外的终止符空间
va_end(args_copy);
// 动态分配缓冲区
char* buffer = new char[size];
// 格式化字符串
vsnprintf(buffer, size, fmt, args);
va_end(args);
std::string result(buffer);
delete[] buffer; // 释放缓冲区
__android_log_write(prio, "GakumasLog", result.c_str()); __android_log_write(prio, "GakumasLog", result.c_str());
} }
void ShowToast(const std::string& text) {
DebugFmt("Toast: %s", text.c_str());
std::thread([text](){
auto env = Misc::GetJNIEnv();
if (!env) {
return;
}
g_javaVM->AttachCurrentThread(&env, nullptr);
jclass& kotlinClass = g_gakumasHookMainClass;
if (!kotlinClass) {
g_javaVM->DetachCurrentThread();
return;
}
jmethodID& methodId = showToastMethodId;
if (!methodId) {
g_javaVM->DetachCurrentThread();
return;
}
jstring param = env->NewStringUTF(text.c_str());
env->CallStaticVoidMethod(kotlinClass, methodId, param);
g_javaVM->DetachCurrentThread();
}).detach();
}
void ShowToastFmt(const char* fmt, ...) {
GetParamStringResult(result);
ShowToast(result);
}
} }

View File

@ -10,6 +10,9 @@ namespace GakumasLocal::Log {
void ErrorFmt(const char* fmt, ...); void ErrorFmt(const char* fmt, ...);
void Debug(const char* msg); void Debug(const char* msg);
void DebugFmt(const char* fmt, ...); void DebugFmt(const char* fmt, ...);
void ShowToast(const char* text);
void ShowToastFmt(const char* fmt, ...);
} }
#endif //GAKUMAS_LOCALIFY_LOG_H #endif //GAKUMAS_LOCALIFY_LOG_H

View File

@ -2,6 +2,10 @@
#include <codecvt> #include <codecvt>
#include <locale> #include <locale>
#include <jni.h>
extern JavaVM* g_javaVM;
namespace GakumasLocal::Misc { namespace GakumasLocal::Misc {
@ -14,4 +18,17 @@ namespace GakumasLocal::Misc {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv; std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
return utf16conv.to_bytes(str.data(), str.data() + str.size()); return utf16conv.to_bytes(str.data(), str.data() + str.size());
} }
JNIEnv* GetJNIEnv() {
if (!g_javaVM) return nullptr;
JNIEnv* env = nullptr;
if (g_javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
int status = g_javaVM->AttachCurrentThread(&env, nullptr);
if (status < 0) {
return nullptr;
}
}
return env;
}
} // namespace UmaPyogin::Misc } // namespace UmaPyogin::Misc

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <jni.h>
namespace GakumasLocal { namespace GakumasLocal {
using OpaqueFunctionPointer = void (*)(); using OpaqueFunctionPointer = void (*)();
@ -10,6 +11,7 @@ namespace GakumasLocal {
namespace Misc { namespace Misc {
std::u16string ToUTF16(const std::string_view& str); std::u16string ToUTF16(const std::string_view& str);
std::string ToUTF8(const std::u16string_view& str); std::string ToUTF8(const std::u16string_view& str);
JNIEnv* GetJNIEnv();
} }
} }

View File

@ -4,6 +4,7 @@
#include "Misc.h" #include "Misc.h"
#include <string> #include <string>
#include <memory> #include <memory>
#include <jni.h>
namespace GakumasLocal { namespace GakumasLocal {
struct HookInstaller struct HookInstaller

View File

@ -14,6 +14,14 @@ namespace GakumasLocal::Config {
std::string liveCustomeHeadId = ""; std::string liveCustomeHeadId = "";
std::string liveCustomeCostumeId = ""; std::string liveCustomeCostumeId = "";
bool useCustomeGraphicSettings = false;
float renderScale = 0.77f;
int qualitySettingsLevel = 3;
int volumeIndex = 3;
int maxBufferPixel = 3384;
int reflectionQualityLevel = 4;
int lodQualityLevel = 4;
void LoadConfig(const std::string& configStr) { void LoadConfig(const std::string& configStr) {
try { try {
const auto config = nlohmann::json::parse(configStr); const auto config = nlohmann::json::parse(configStr);
@ -27,6 +35,13 @@ namespace GakumasLocal::Config {
GetConfigItem(enableLiveCustomeDress); GetConfigItem(enableLiveCustomeDress);
GetConfigItem(liveCustomeHeadId); GetConfigItem(liveCustomeHeadId);
GetConfigItem(liveCustomeCostumeId); GetConfigItem(liveCustomeCostumeId);
GetConfigItem(useCustomeGraphicSettings);
GetConfigItem(renderScale);
GetConfigItem(qualitySettingsLevel);
GetConfigItem(volumeIndex);
GetConfigItem(maxBufferPixel);
GetConfigItem(reflectionQualityLevel);
GetConfigItem(lodQualityLevel);
} }
catch (std::exception& e) { catch (std::exception& e) {

View File

@ -14,6 +14,15 @@ namespace GakumasLocal::Config {
extern std::string liveCustomeHeadId; extern std::string liveCustomeHeadId;
extern std::string liveCustomeCostumeId; extern std::string liveCustomeCostumeId;
extern bool useCustomeGraphicSettings;
extern float renderScale;
extern int qualitySettingsLevel;
extern int volumeIndex;
extern int maxBufferPixel;
extern int reflectionQualityLevel;
extern int lodQualityLevel;
void LoadConfig(const std::string& configStr); void LoadConfig(const std::string& configStr);
} }

View File

@ -9,6 +9,10 @@
#include "GakumasLocalify/camera/camera.hpp" #include "GakumasLocalify/camera/camera.hpp"
#include "GakumasLocalify/config/Config.hpp" #include "GakumasLocalify/config/Config.hpp"
JavaVM* g_javaVM = nullptr;
jclass g_gakumasHookMainClass = nullptr;
jmethodID showToastMethodId = nullptr;
namespace namespace
{ {
class AndroidHookInstaller : public GakumasLocal::HookInstaller class AndroidHookInstaller : public GakumasLocal::HookInstaller
@ -40,11 +44,20 @@ namespace
}; };
} }
extern "C"
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved) {
g_javaVM = vm;
return JNI_VERSION_1_6;
}
extern "C" extern "C"
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_io_github_chinosk_gakumas_localify_GakumasHookMain_initHook(JNIEnv *env, jclass clazz, jstring targetLibraryPath, Java_io_github_chinosk_gakumas_localify_GakumasHookMain_initHook(JNIEnv *env, jclass clazz, jstring targetLibraryPath,
jstring localizationFilesDir) { jstring localizationFilesDir) {
g_gakumasHookMainClass = clazz;
showToastMethodId = env->GetStaticMethodID(clazz, "showToast", "(Ljava/lang/String;)V");
const auto targetLibraryPathChars = env->GetStringUTFChars(targetLibraryPath, nullptr); const auto targetLibraryPathChars = env->GetStringUTFChars(targetLibraryPath, nullptr);
const std::string targetLibraryPathStr = targetLibraryPathChars; const std::string targetLibraryPathStr = targetLibraryPathChars;

View File

@ -22,12 +22,12 @@ import android.widget.Toast
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
import java.io.File import java.io.File
val TAG = "GakumasLocalify"
class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit { class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
private lateinit var modulePath: String private lateinit var modulePath: String
private var nativeLibLoadSuccess: Boolean private var nativeLibLoadSuccess: Boolean
private var alreadyInitialized = false private var alreadyInitialized = false
private val TAG = "GakumasLocalify"
private val targetPackageName = "com.bandainamcoent.idolmaster_gakuen" private val targetPackageName = "com.bandainamcoent.idolmaster_gakuen"
private val nativeLibName = "MarryKotone" private val nativeLibName = "MarryKotone"
@ -115,20 +115,6 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
}) })
} }
private fun showToast(message: String) {
val app = AndroidAppHelper.currentApplication()
val context = app?.applicationContext
if (context != null) {
val handler = Handler(Looper.getMainLooper())
handler.post {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}
else {
Log.e(TAG, "showToast: $message failed: applicationContext is null")
}
}
fun initGkmsConfig(activity: Activity) { fun initGkmsConfig(activity: Activity) {
val intent = activity.intent val intent = activity.intent
val gkmsData = intent.getStringExtra("gkmsData") val gkmsData = intent.getStringExtra("gkmsData")
@ -160,8 +146,22 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
external fun keyboardEvent(keyCode: Int, action: Int) external fun keyboardEvent(keyCode: Int, action: Int)
@JvmStatic @JvmStatic
external fun loadConfig(configJsonStr: String) external fun loadConfig(configJsonStr: String)
}
@JvmStatic
fun showToast(message: String) {
val app = AndroidAppHelper.currentApplication()
val context = app?.applicationContext
if (context != null) {
val handler = Handler(Looper.getMainLooper())
handler.post {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}
else {
Log.e(TAG, "showToast: $message failed: applicationContext is null")
}
}
}
init { init {
ShadowHook.init( ShadowHook.init(

View File

@ -7,6 +7,9 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.databinding.ObservableField
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonSyntaxException import com.google.gson.JsonSyntaxException
import io.github.chinosk.gakumas.localify.databinding.ActivityMainBinding import io.github.chinosk.gakumas.localify.databinding.ActivityMainBinding
@ -23,27 +26,33 @@ interface ConfigListener {
fun onLiveCustomeDressChanged(value: Boolean) fun onLiveCustomeDressChanged(value: Boolean)
fun onLiveCustomeHeadIdChanged(s: CharSequence, start: Int, before: Int, count: Int) fun onLiveCustomeHeadIdChanged(s: CharSequence, start: Int, before: Int, count: Int)
fun onLiveCustomeCostumeIdChanged(s: CharSequence, start: Int, before: Int, count: Int) fun onLiveCustomeCostumeIdChanged(s: CharSequence, start: Int, before: Int, count: Int)
fun onUseCustomeGraphicSettingsChanged(value: Boolean)
fun onRenderScaleChanged(s: CharSequence, start: Int, before: Int, count: Int)
fun onQualitySettingsLevelChanged(s: CharSequence, start: Int, before: Int, count: Int)
fun onVolumeIndexChanged(s: CharSequence, start: Int, before: Int, count: Int)
fun onMaxBufferPixelChanged(s: CharSequence, start: Int, before: Int, count: Int)
fun onChangePresetQuality(level: Int)
fun onReflectionQualityLevelChanged(s: CharSequence, start: Int, before: Int, count: Int)
fun onLodQualityLevelChanged(s: CharSequence, start: Int, before: Int, count: Int)
} }
class MainActivity : AppCompatActivity(), ConfigListener { class MainActivity : AppCompatActivity(), ConfigListener {
private lateinit var config: GakumasConfig private lateinit var binding: ActivityMainBinding
private val TAG = "GakumasLocalify" private val TAG = "GakumasLocalify"
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
loadConfig() loadConfig()
binding.listener = this
val requestData = intent.getStringExtra("gkmsData") val requestData = intent.getStringExtra("gkmsData")
if (requestData != null) { if (requestData != null) {
onClickStartGame() onClickStartGame()
finish() finish()
} }
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.config = config
binding.listener = this
} }
private fun showToast(message: String) { private fun showToast(message: String) {
@ -63,34 +72,33 @@ class MainActivity : AppCompatActivity(), ConfigListener {
private fun loadConfig() { private fun loadConfig() {
val configStr = getConfigContent() val configStr = getConfigContent()
val config = try { binding.config = try {
Gson().fromJson(configStr, GakumasConfig::class.java) Gson().fromJson(configStr, GakumasConfig::class.java)
} }
catch (e: JsonSyntaxException) { catch (e: JsonSyntaxException) {
showToast("配置文件异常,已重置: $e") showToast("配置文件异常,已重置: $e")
Gson().fromJson("{}", GakumasConfig::class.java) Gson().fromJson("{}", GakumasConfig::class.java)
} }
this.config = config
saveConfig() saveConfig()
} }
private fun saveConfig() { private fun saveConfig() {
val configFile = File(filesDir, "gkms-config.json") val configFile = File(filesDir, "gkms-config.json")
configFile.writeText(Gson().toJson(config)) configFile.writeText(Gson().toJson(binding.config!!))
} }
override fun onEnabledChanged(value: Boolean) { override fun onEnabledChanged(value: Boolean) {
config.enabled = value binding.config!!.enabled = value
saveConfig() saveConfig()
} }
override fun onEnableFreeCameraChanged(value: Boolean) { override fun onEnableFreeCameraChanged(value: Boolean) {
config.enableFreeCamera = value binding.config!!.enableFreeCamera = value
saveConfig() saveConfig()
} }
override fun onUnlockAllLiveChanged(value: Boolean) { override fun onUnlockAllLiveChanged(value: Boolean) {
config.unlockAllLive = value binding.config!!.unlockAllLive = value
saveConfig() saveConfig()
} }
@ -103,7 +111,7 @@ class MainActivity : AppCompatActivity(), ConfigListener {
} else { } else {
valueStr.toInt() valueStr.toInt()
} }
config.targetFrameRate = value binding.config!!.targetFrameRate = value
saveConfig() saveConfig()
} }
catch (e: Exception) { catch (e: Exception) {
@ -112,17 +120,62 @@ class MainActivity : AppCompatActivity(), ConfigListener {
} }
override fun onLiveCustomeDressChanged(value: Boolean) { override fun onLiveCustomeDressChanged(value: Boolean) {
config.enableLiveCustomeDress = value binding.config!!.enableLiveCustomeDress = value
saveConfig() saveConfig()
} }
override fun onLiveCustomeCostumeIdChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onLiveCustomeCostumeIdChanged(s: CharSequence, start: Int, before: Int, count: Int) {
config.liveCustomeCostumeId = s.toString() binding.config!!.liveCustomeCostumeId = s.toString()
saveConfig()
}
override fun onUseCustomeGraphicSettingsChanged(value: Boolean) {
binding.config!!.useCustomeGraphicSettings = value
saveConfig()
}
override fun onRenderScaleChanged(s: CharSequence, start: Int, before: Int, count: Int) {
binding.config!!.renderScale = try {
s.toString().toFloat()
}
catch (e: Exception) {
0.0f
}
saveConfig()
}
override fun onQualitySettingsLevelChanged(s: CharSequence, start: Int, before: Int, count: Int) {
binding.config!!.qualitySettingsLevel = try {
s.toString().toInt()
}
catch (e: Exception) {
0
}
saveConfig()
}
override fun onVolumeIndexChanged(s: CharSequence, start: Int, before: Int, count: Int) {
binding.config!!.volumeIndex = try {
s.toString().toInt()
}
catch (e: Exception) {
0
}
saveConfig()
}
override fun onMaxBufferPixelChanged(s: CharSequence, start: Int, before: Int, count: Int) {
binding.config!!.maxBufferPixel = try {
s.toString().toInt()
}
catch (e: Exception) {
0
}
saveConfig() saveConfig()
} }
override fun onLiveCustomeHeadIdChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onLiveCustomeHeadIdChanged(s: CharSequence, start: Int, before: Int, count: Int) {
config.liveCustomeHeadId = s.toString() binding.config!!.liveCustomeHeadId = s.toString()
saveConfig() saveConfig()
} }
@ -134,4 +187,78 @@ class MainActivity : AppCompatActivity(), ConfigListener {
} }
startActivity(intent) startActivity(intent)
} }
override fun onReflectionQualityLevelChanged(s: CharSequence, start: Int, before: Int, count: Int) {
binding.config!!.reflectionQualityLevel = try {
val value = s.toString().toInt()
if (value > 5) 5 else value
}
catch (e: Exception) {
0
}
saveConfig()
}
override fun onLodQualityLevelChanged(s: CharSequence, start: Int, before: Int, count: Int) {
binding.config!!.lodQualityLevel = try {
val value = s.toString().toInt()
if (value > 5) 5 else value
}
catch (e: Exception) {
0
}
saveConfig()
}
override fun onChangePresetQuality(level: Int) {
when (level) {
0 -> {
binding.config!!.renderScale = 0.5f
binding.config!!.qualitySettingsLevel = 1
binding.config!!.volumeIndex = 0
binding.config!!.maxBufferPixel = 1024
binding.config!!.lodQualityLevel = 1
binding.config!!.reflectionQualityLevel = 1
}
1 -> {
binding.config!!.renderScale = 0.59f
binding.config!!.qualitySettingsLevel = 1
binding.config!!.volumeIndex = 1
binding.config!!.maxBufferPixel = 1440
binding.config!!.lodQualityLevel = 2
binding.config!!.reflectionQualityLevel = 2
}
2 -> {
binding.config!!.renderScale = 0.67f
binding.config!!.qualitySettingsLevel = 2
binding.config!!.volumeIndex = 2
binding.config!!.maxBufferPixel = 2538
binding.config!!.lodQualityLevel = 3
binding.config!!.reflectionQualityLevel = 3
}
3 -> {
binding.config!!.renderScale = 0.77f
binding.config!!.qualitySettingsLevel = 3
binding.config!!.volumeIndex = 3
binding.config!!.maxBufferPixel = 3384
binding.config!!.lodQualityLevel = 4
binding.config!!.reflectionQualityLevel = 4
}
4 -> {
binding.config!!.renderScale = 1.0f
binding.config!!.qualitySettingsLevel = 5
binding.config!!.volumeIndex = 4
binding.config!!.maxBufferPixel = 8190
binding.config!!.lodQualityLevel = 5
binding.config!!.reflectionQualityLevel = 5
}
}
binding.config = binding.config
binding.notifyChange()
saveConfig()
}
private fun showTextInputLayoutHint(view: TextInputLayout) {
showToast(view.hint.toString())
}
} }

View File

@ -1,12 +1,21 @@
package io.github.chinosk.gakumas.localify.models package io.github.chinosk.gakumas.localify.models
data class GakumasConfig( import androidx.databinding.BaseObservable
data class GakumasConfig (
var enabled: Boolean = true, var enabled: Boolean = true,
var enableFreeCamera: Boolean = false, var enableFreeCamera: Boolean = false,
var targetFrameRate: Int = 0, var targetFrameRate: Int = 0,
var unlockAllLive: Boolean = false, var unlockAllLive: Boolean = false,
var enableLiveCustomeDress: Boolean = false, var enableLiveCustomeDress: Boolean = false,
var liveCustomeHeadId: String = "", var liveCustomeHeadId: String = "",
var liveCustomeCostumeId: String = "" var liveCustomeCostumeId: String = "",
)
var useCustomeGraphicSettings: Boolean = false,
var renderScale: Float = 0.77f,
var qualitySettingsLevel: Int = 3,
var volumeIndex: Int = 3,
var maxBufferPixel: Int = 3384,
var reflectionQualityLevel: Int = 4, // 0~5
var lodQualityLevel: Int = 4, // 0~5
)

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#ACACAC"/> <!-- 设置边框宽度和颜色 -->
</shape>

View File

@ -16,174 +16,477 @@
android:padding="6sp" android:padding="6sp"
tools:context=".MainActivity"> tools:context=".MainActivity">
<LinearLayout <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<TextView <LinearLayout
android:id="@+id/textViewTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="20sp"
android:text="@string/gakumas_localify" />
<Space
android:layout_width="match_parent"
android:layout_height="25sp" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:stretchColumns="0"> app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TableRow <TextView
android:id="@+id/textViewTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content"
android:text="@string/gakumas_localify"
android:textColor="@color/black"
android:textSize="20sp" />
<com.google.android.material.switchmaterial.SwitchMaterial <Space
android:id="@+id/SwitchEnablePlugin"
android:layout_width="match_parent"
android:layout_height="48dp"
android:checked="@={config.enabled}"
android:onCheckedChanged="@{(view, value) -> listener.onEnabledChanged(value)}"
android:text="@string/enable_plugin" />
</TableRow>
<TableRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="25sp" />
<com.google.android.material.textfield.TextInputLayout <TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:stretchColumns="0">
<TableRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="match_parent">
app:boxBackgroundColor="@android:color/transparent"
android:background="@android:color/transparent"
android:hint="@string/setFpsTitle" >
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/editTextTargetFps" android:id="@+id/SwitchEnablePlugin"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:ems="10" android:checked="@={config.enabled}"
android:inputType="numberSigned" android:onCheckedChanged="@{(view, value) -> listener.onEnabledChanged(value)}"
android:paddingStart="0dp" android:text="@string/enable_plugin" />
android:paddingEnd="0dp" </TableRow>
android:paddingBottom="0dp"
android:text="@={`` + config.targetFrameRate}"
android:onTextChanged="@{(s, st, b, a) -> listener.onTargetFpsChanged(s, st, b, a)}"/>
</com.google.android.material.textfield.TextInputLayout> <TableRow
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/SwitchEnableFreeCameraPlugin"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="match_parent">
android:checked="@={config.enableFreeCamera}"
android:onCheckedChanged="@{(view, value) -> listener.onEnableFreeCameraChanged(value)}"
android:text="@string/enable_free_camera" />
</TableRow>
<TableRow <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/SwitchUnlockAllLive"
android:layout_width="match_parent"
android:layout_height="48dp"
android:checked="@={config.unlockAllLive}"
android:onCheckedChanged="@{(view, value) -> listener.onUnlockAllLiveChanged(value)}"
android:text="@string/unlockAllLive" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/SwitchLiveUseCustomeDress"
android:layout_width="match_parent"
android:layout_height="48dp"
android:checked="@={config.enableLiveCustomeDress}"
android:onCheckedChanged="@{(view, value) -> listener.onLiveCustomeDressChanged(value)}"
android:text="@string/liveUseCustomeDress" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="48dp"
app:boxBackgroundColor="@android:color/transparent"
android:background="@android:color/transparent"
android:hint="@string/live_costume_head_id" >
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextLiveCustomeCharaId"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="48dp"
android:ems="10" android:background="@android:color/transparent"
android:paddingStart="0dp" android:hint="@string/setFpsTitle"
android:paddingEnd="0dp" app:boxBackgroundColor="@android:color/transparent">
android:paddingBottom="0dp"
android:text="@={config.liveCustomeHeadId}"
android:onTextChanged="@{(s, st, b, a) -> listener.onLiveCustomeHeadIdChanged(s, st, b, a)}"/>
</com.google.android.material.textfield.TextInputLayout> <com.google.android.material.textfield.TextInputEditText
</TableRow> android:id="@+id/editTextTargetFps"
android:layout_width="match_parent"
android:layout_height="48dp"
android:ems="10"
android:inputType="numberSigned"
android:onTextChanged="@{(s, st, b, a) -> listener.onTargetFpsChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={`` + config.targetFrameRate}" />
<TableRow </com.google.android.material.textfield.TextInputLayout>
android:layout_width="match_parent"
android:layout_height="match_parent"> </TableRow>
<com.google.android.material.textfield.TextInputLayout
<TableRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="match_parent">
app:boxBackgroundColor="@android:color/transparent"
android:background="@android:color/transparent"
android:hint="@string/live_custome_dress_id" >
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/editTextLiveCustomeCostumeId" android:id="@+id/SwitchEnableFreeCameraPlugin"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="48dp"
android:ems="10" android:checked="@={config.enableFreeCamera}"
android:paddingStart="0dp" android:onCheckedChanged="@{(view, value) -> listener.onEnableFreeCameraChanged(value)}"
android:paddingEnd="0dp" android:text="@string/enable_free_camera" />
android:paddingBottom="0dp" </TableRow>
android:text="@={config.liveCustomeCostumeId}"
android:onTextChanged="@{(s, st, b, a) -> listener.onLiveCustomeCostumeIdChanged(s, st, b, a)}"/>
</com.google.android.material.textfield.TextInputLayout>
</TableRow>
<TableRow
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.button.MaterialButton
android:id="@+id/StartGameButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="match_parent">
android:onClick="@{() -> listener.onClickStartGame()}"
android:text="@string/start_game" />
</TableRow>
</TableLayout> <com.google.android.material.switchmaterial.SwitchMaterial
</LinearLayout> android:id="@+id/SwitchUnlockAllLive"
android:layout_width="match_parent"
android:layout_height="48dp"
android:checked="@={config.unlockAllLive}"
android:onCheckedChanged="@{(view, value) -> listener.onUnlockAllLiveChanged(value)}"
android:text="@string/unlockAllLive" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/table_row_border">
<TableLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="10sp"
android:paddingRight="10sp"
android:paddingBottom="10sp"
android:stretchColumns="0">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/SwitchLiveUseCustomeDress"
android:layout_width="match_parent"
android:layout_height="48dp"
android:checked="@={config.enableLiveCustomeDress}"
android:onCheckedChanged="@{(view, value) -> listener.onLiveCustomeDressChanged(value)}"
android:text="@string/liveUseCustomeDress" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@android:color/transparent"
android:hint="@string/live_costume_head_id"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextLiveCustomeCharaId"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:onTextChanged="@{(s, st, b, a) -> listener.onLiveCustomeHeadIdChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={config.liveCustomeHeadId}" />
</com.google.android.material.textfield.TextInputLayout>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@android:color/transparent"
android:hint="@string/live_custome_dress_id"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextLiveCustomeCostumeId"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:onTextChanged="@{(s, st, b, a) -> listener.onLiveCustomeCostumeIdChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={config.liveCustomeCostumeId}" />
</com.google.android.material.textfield.TextInputLayout>
</TableRow>
</TableLayout>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="4sp"
android:background="@drawable/table_row_border">
<TableLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="10sp"
android:paddingRight="10sp"
android:paddingBottom="10sp"
android:stretchColumns="0">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/SwitchUseCustomeGraphicSettings"
android:layout_width="match_parent"
android:layout_height="48dp"
android:checked="@={config.useCustomeGraphicSettings}"
android:onCheckedChanged="@{(view, value) -> listener.onUseCustomeGraphicSettingsChanged(value)}"
android:text="@string/useCustomeGraphicSettings" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.button.MaterialButton
android:id="@+id/qualityRecordButton"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_margin="1sp"
android:layout_weight="1"
android:minWidth="1sp"
android:onClick="@{() -> listener.onChangePresetQuality(4)}"
android:text="极高" />
<com.google.android.material.button.MaterialButton
android:id="@+id/qualityVeryHighButton"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_margin="1sp"
android:layout_weight="1"
android:minWidth="1sp"
android:onClick="@{() -> listener.onChangePresetQuality(3)}"
android:text="超高" />
<com.google.android.material.button.MaterialButton
android:id="@+id/qualityHighButton"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_margin="1sp"
android:layout_weight="1"
android:minWidth="1sp"
android:onClick="@{() -> listener.onChangePresetQuality(2)}"
android:text="高" />
<com.google.android.material.button.MaterialButton
android:id="@+id/qualityMidButton"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_margin="1sp"
android:layout_weight="1"
android:minWidth="1sp"
android:onClick="@{() -> listener.onChangePresetQuality(1)}"
android:text="中" />
<com.google.android.material.button.MaterialButton
android:id="@+id/qualityLowButton"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_weight="1"
android:minWidth="1sp"
android:onClick="@{() -> listener.onChangePresetQuality(0)}"
android:text="低" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="2">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/editTextRenderScaleLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="@string/renderscale"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextRenderScale"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:hint="0.5/0.59/0.67/0.77/1.0"
android:inputType="numberDecimal"
android:onTextChanged="@{(s, st, b, a) -> listener.onRenderScaleChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={`` + config.renderScale}" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/editTextQualitySettingsLevelLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="QualityLevel (1/1/2/3/5)"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextQualitySettingsLevel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:hint="1/1/2/3/5"
android:inputType="number"
android:onTextChanged="@{(s, st, b, a) -> listener.onQualitySettingsLevelChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={`` + config.qualitySettingsLevel}" />
</com.google.android.material.textfield.TextInputLayout>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="2">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/editTextVolumeIndexLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="VolumeIndex (0/1/2/3/4)"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextVolumeIndex"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:hint="0/1/2/3/4"
android:inputType="number"
android:onTextChanged="@{(s, st, b, a) -> listener.onVolumeIndexChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={`` + config.volumeIndex}" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/editTextMaxBufferPixelLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="MaxBufferPixel (1024/1440/2538/3384/8190)"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextMaxBufferPixel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:hint="1024/1440/2538/3384/8190"
android:inputType="number"
android:onTextChanged="@{(s, st, b, a) -> listener.onMaxBufferPixelChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={`` + config.maxBufferPixel}" />
</com.google.android.material.textfield.TextInputLayout>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="2">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="ReflectionLevel (0~5)"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextReflectionQualityLevel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:hint="0/1/2/3/4/5"
android:inputType="number"
android:onTextChanged="@{(s, st, b, a) -> listener.onReflectionQualityLevelChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={`` + config.reflectionQualityLevel}" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@android:color/transparent"
android:hint="LOD Level (0~5)"
app:boxBackgroundColor="@android:color/transparent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextLodQualityLevel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:hint="0/1/2/3/4/5"
android:inputType="number"
android:onTextChanged="@{(s, st, b, a) -> listener.onLodQualityLevelChanged(s, st, b, a)}"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:text="@={`` + config.lodQualityLevel}" />
</com.google.android.material.textfield.TextInputLayout>
</TableRow>
</TableLayout>
</TableRow>
</TableLayout>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.button.MaterialButton
android:id="@+id/StartGameButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:onClick="@{() -> listener.onClickStartGame()}"
android:text="@string/start_game" />
</TableRow>
</TableLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View File

@ -9,4 +9,6 @@
<string name="liveUseCustomeDress">Live 使用自定义角色</string> <string name="liveUseCustomeDress">Live 使用自定义角色</string>
<string name="live_costume_head_id">Live 自定义头部 ID (例: costume_head_hski-cstm-0002)</string> <string name="live_costume_head_id">Live 自定义头部 ID (例: costume_head_hski-cstm-0002)</string>
<string name="live_custome_dress_id">Live 自定义服装 ID (例: hski-cstm-0002)</string> <string name="live_custome_dress_id">Live 自定义服装 ID (例: hski-cstm-0002)</string>
<string name="useCustomeGraphicSettings">使用自定义画质设置</string>
<string name="renderscale">RenderScale (0.5/0.59/0.67/0.77/1.0)</string>
</resources> </resources>