Compare commits
No commits in common. "main" and "main" have entirely different histories.
|
@ -15,8 +15,8 @@ android {
|
||||||
applicationId "io.github.chinosk.gakumas.localify"
|
applicationId "io.github.chinosk.gakumas.localify"
|
||||||
minSdk 29
|
minSdk 29
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 12
|
versionCode 11
|
||||||
versionName "v3.0.4"
|
versionName "v2.1.0"
|
||||||
buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
|
buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
#include "../platformDefine.hpp"
|
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
#define KEY_W 51
|
#define KEY_W 51
|
||||||
#define KEY_S 47
|
#define KEY_S 47
|
||||||
#define KEY_A 29
|
#define KEY_A 29
|
||||||
|
@ -27,32 +24,6 @@
|
||||||
|
|
||||||
#define WM_KEYDOWN 0
|
#define WM_KEYDOWN 0
|
||||||
#define WM_KEYUP 1
|
#define WM_KEYUP 1
|
||||||
#else
|
|
||||||
#define KEY_W 'W'
|
|
||||||
#define KEY_S 'S'
|
|
||||||
#define KEY_A 'A'
|
|
||||||
#define KEY_D 'D'
|
|
||||||
#define KEY_R 'R'
|
|
||||||
#define KEY_Q 'Q'
|
|
||||||
#define KEY_E 'E'
|
|
||||||
#define KEY_F 'F'
|
|
||||||
#define KEY_I 'I'
|
|
||||||
#define KEY_K 'K'
|
|
||||||
#define KEY_J 'J'
|
|
||||||
#define KEY_L 'L'
|
|
||||||
#define KEY_V 'V'
|
|
||||||
#define KEY_UP 38
|
|
||||||
#define KEY_DOWN 40
|
|
||||||
#define KEY_LEFT 37
|
|
||||||
#define KEY_RIGHT 39
|
|
||||||
#define KEY_CTRL 17
|
|
||||||
#define KEY_SHIFT 16
|
|
||||||
#define KEY_ALT 18
|
|
||||||
#define KEY_SPACE 32
|
|
||||||
|
|
||||||
#define KEY_ADD 187
|
|
||||||
#define KEY_SUB 189
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BTN_A 96
|
#define BTN_A 96
|
||||||
#define BTN_B 97
|
#define BTN_B 97
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <android/log.h>
|
||||||
#include "Hook.h"
|
#include "Hook.h"
|
||||||
#include "Plugin.h"
|
#include "Plugin.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
@ -8,21 +9,14 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include "camera/camera.hpp"
|
#include "camera/camera.hpp"
|
||||||
#include "config/Config.hpp"
|
#include "config/Config.hpp"
|
||||||
// #include <jni.h>
|
#include "shadowhook.h"
|
||||||
|
#include <jni.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "../platformDefine.hpp"
|
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
#include "../windowsPlatform.hpp"
|
|
||||||
#include "cpprest/details/http_helpers.h"
|
|
||||||
#include "../resourceUpdate/resourceUpdate.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
std::unordered_set<void*> hookedStubs{};
|
std::unordered_set<void*> hookedStubs{};
|
||||||
extern std::filesystem::path gakumasLocalPath;
|
|
||||||
|
|
||||||
#define DEFINE_HOOK(returnType, name, params) \
|
#define DEFINE_HOOK(returnType, name, params) \
|
||||||
using name##_Type = returnType(*) params; \
|
using name##_Type = returnType(*) params; \
|
||||||
|
@ -30,7 +24,26 @@ extern std::filesystem::path gakumasLocalPath;
|
||||||
name##_Type name##_Orig = nullptr; \
|
name##_Type name##_Orig = nullptr; \
|
||||||
returnType name##_Hook params
|
returnType name##_Hook params
|
||||||
|
|
||||||
/*
|
|
||||||
|
#define ADD_HOOK(name, addr) \
|
||||||
|
name##_Addr = reinterpret_cast<name##_Type>(addr); \
|
||||||
|
if (addr) { \
|
||||||
|
auto stub = hookInstaller->InstallHook(reinterpret_cast<void*>(addr), \
|
||||||
|
reinterpret_cast<void*>(name##_Hook), \
|
||||||
|
reinterpret_cast<void**>(&name##_Orig)); \
|
||||||
|
if (stub == NULL) { \
|
||||||
|
int error_num = shadowhook_get_errno(); \
|
||||||
|
const char *error_msg = shadowhook_to_errmsg(error_num); \
|
||||||
|
Log::ErrorFmt("ADD_HOOK: %s at %p failed: %s", #name, addr, error_msg); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
hookedStubs.emplace(stub); \
|
||||||
|
GakumasLocal::Log::InfoFmt("ADD_HOOK: %s at %p", #name, addr); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
else GakumasLocal::Log::ErrorFmt("Hook failed: %s is NULL", #name, addr); \
|
||||||
|
if (Config::lazyInit) UnityResolveProgress::classProgress.current++
|
||||||
|
|
||||||
void UnHookAll() {
|
void UnHookAll() {
|
||||||
for (const auto i: hookedStubs) {
|
for (const auto i: hookedStubs) {
|
||||||
int result = shadowhook_unhook(i);
|
int result = shadowhook_unhook(i);
|
||||||
|
@ -41,7 +54,7 @@ void UnHookAll() {
|
||||||
GakumasLocal::Log::ErrorFmt("unhook failed: %d - %s", error_num, error_msg);
|
GakumasLocal::Log::ErrorFmt("unhook failed: %d - %s", error_num, error_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
namespace GakumasLocal::HookMain {
|
namespace GakumasLocal::HookMain {
|
||||||
using Il2cppString = UnityResolve::UnityType::String;
|
using Il2cppString = UnityResolve::UnityType::String;
|
||||||
|
@ -99,18 +112,6 @@ namespace GakumasLocal::HookMain {
|
||||||
return GetResolution->Invoke<Il2cppUtils::Resolution_t>();
|
return GetResolution->Invoke<Il2cppUtils::Resolution_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Il2cppString* ToJsonStr(void* object) {
|
|
||||||
static Il2cppString* (*toJsonStr)(void*) = nullptr;
|
|
||||||
if (!toJsonStr) {
|
|
||||||
toJsonStr = reinterpret_cast<Il2cppString * (*)(void*)>(Il2cppUtils::GetMethodPointer("Newtonsoft.Json.dll", "Newtonsoft.Json",
|
|
||||||
"JsonConvert", "SerializeObject", { "*" }));
|
|
||||||
}
|
|
||||||
if (!toJsonStr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return toJsonStr(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_HOOK(void, Unity_set_fieldOfView, (UnityResolve::UnityType::Camera* self, float value)) {
|
DEFINE_HOOK(void, Unity_set_fieldOfView, (UnityResolve::UnityType::Camera* self, float value)) {
|
||||||
if (Config::enableFreeCamera) {
|
if (Config::enableFreeCamera) {
|
||||||
if (self == mainCameraCache) {
|
if (self == mainCameraCache) {
|
||||||
|
@ -223,16 +224,6 @@ namespace GakumasLocal::HookMain {
|
||||||
return Unity_set_position_Injected_Orig(self, data);
|
return Unity_set_position_Injected_Orig(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
DEFINE_HOOK(void*, InternalSetOrientationAsync, (void* retstr, void* self, int type, void* c, void* tc, void* mtd)) {
|
|
||||||
switch (Config::gameOrientation) {
|
|
||||||
case 1: type = 0x2; break; // FixedPortrait
|
|
||||||
case 2: type = 0x3; break; // FixedLandscape
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
return InternalSetOrientationAsync_Orig(retstr, self, type, c, tc, mtd);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
DEFINE_HOOK(void*, InternalSetOrientationAsync, (void* self, int type, void* c, void* tc, void* mtd)) {
|
DEFINE_HOOK(void*, InternalSetOrientationAsync, (void* self, int type, void* c, void* tc, void* mtd)) {
|
||||||
switch (Config::gameOrientation) {
|
switch (Config::gameOrientation) {
|
||||||
case 1: type = 0x2; break; // FixedPortrait
|
case 1: type = 0x2; break; // FixedPortrait
|
||||||
|
@ -241,7 +232,6 @@ namespace GakumasLocal::HookMain {
|
||||||
}
|
}
|
||||||
return InternalSetOrientationAsync_Orig(self, type, c, tc, mtd);
|
return InternalSetOrientationAsync_Orig(self, type, c, tc, mtd);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
DEFINE_HOOK(void, EndCameraRendering, (void* ctx, void* camera, void* method)) {
|
DEFINE_HOOK(void, EndCameraRendering, (void* ctx, void* camera, void* method)) {
|
||||||
EndCameraRendering_Orig(ctx, camera, method);
|
EndCameraRendering_Orig(ctx, camera, method);
|
||||||
|
@ -310,129 +300,9 @@ namespace GakumasLocal::HookMain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
struct TransparentStringHash : std::hash<std::wstring>, std::hash<std::wstring_view>
|
|
||||||
{
|
|
||||||
using is_transparent = void;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::unordered_set<std::wstring, TransparentStringHash, std::equal_to<void>> AssetPathsType;
|
|
||||||
std::map<std::string, AssetPathsType> CustomAssetBundleAssetPaths;
|
|
||||||
std::unordered_map<std::string, uint32_t> CustomAssetBundleHandleMap{};
|
|
||||||
std::list<std::string> g_extra_assetbundle_paths{};
|
|
||||||
|
|
||||||
void LoadExtraAssetBundle() {
|
|
||||||
using Il2CppString = UnityResolve::UnityType::String;
|
|
||||||
|
|
||||||
if (g_extra_assetbundle_paths.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// CustomAssetBundleHandleMap.clear();
|
|
||||||
// CustomAssetBundleAssetPaths.clear();
|
|
||||||
// assert(!ExtraAssetBundleHandle && ExtraAssetBundleAssetPaths.empty());
|
|
||||||
|
|
||||||
static auto AssetBundle_GetAllAssetNames = reinterpret_cast<void* (*)(void*)>(
|
|
||||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.AssetBundle::GetAllAssetNames()")
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const auto& i : g_extra_assetbundle_paths) {
|
|
||||||
if (CustomAssetBundleHandleMap.contains(i)) continue;
|
|
||||||
|
|
||||||
const auto extraAssetBundle = WinHooks::LoadAssetBundle(i);
|
|
||||||
if (extraAssetBundle)
|
|
||||||
{
|
|
||||||
const auto allAssetPaths = AssetBundle_GetAllAssetNames(extraAssetBundle);
|
|
||||||
AssetPathsType assetPath{};
|
|
||||||
Il2cppUtils::iterate_IEnumerable<Il2CppString*>(allAssetPaths, [&assetPath](Il2CppString* path)
|
|
||||||
{
|
|
||||||
// ExtraAssetBundleAssetPaths.emplace(path->start_char);
|
|
||||||
// printf("Asset loaded: %ls\n", path->start_char);
|
|
||||||
assetPath.emplace(path->start_char);
|
|
||||||
});
|
|
||||||
CustomAssetBundleAssetPaths.emplace(i, assetPath);
|
|
||||||
CustomAssetBundleHandleMap.emplace(i, UnityResolve::Invoke<uint32_t>("il2cpp_gchandle_new", extraAssetBundle, false));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::ErrorFmt("Cannot load asset bundle: %s\n", i.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GetBundleHandleByAssetName(std::wstring assetName) {
|
|
||||||
for (const auto& i : CustomAssetBundleAssetPaths) {
|
|
||||||
for (const auto& m : i.second) {
|
|
||||||
if (std::equal(m.begin(), m.end(), assetName.begin(), assetName.end(),
|
|
||||||
[](wchar_t c1, wchar_t c2) {
|
|
||||||
return std::tolower(c1, std::locale()) == std::tolower(c2, std::locale());
|
|
||||||
})) {
|
|
||||||
return CustomAssetBundleHandleMap.at(i.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GetBundleHandleByAssetName(std::string assetName) {
|
|
||||||
return GetBundleHandleByAssetName(utility::conversions::to_string_t(assetName));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ReplaceFontHandle;
|
|
||||||
|
|
||||||
void* GetReplaceFont() {
|
|
||||||
static auto FontClass = Il2cppUtils::GetClass("UnityEngine.TextRenderingModule.dll", "UnityEngine", "Font");
|
|
||||||
static auto Font_Type = UnityResolve::Invoke<Il2cppUtils::Il2CppReflectionType*>("il2cpp_type_get_object",
|
|
||||||
UnityResolve::Invoke<void*>("il2cpp_class_get_type", FontClass->address));
|
|
||||||
|
|
||||||
using Il2CppString = UnityResolve::UnityType::String;
|
|
||||||
const auto fontPath = "assets/fonts/gkamszhfontmix.otf";
|
|
||||||
|
|
||||||
void* replaceFont{};
|
|
||||||
const auto& bundleHandle = GetBundleHandleByAssetName(fontPath);
|
|
||||||
if (bundleHandle)
|
|
||||||
{
|
|
||||||
if (ReplaceFontHandle)
|
|
||||||
{
|
|
||||||
replaceFont = UnityResolve::Invoke<void*>("il2cpp_gchandle_get_target", ReplaceFontHandle);
|
|
||||||
// 加载场景时会被 Resources.UnloadUnusedAssets 干掉,且不受 DontDestroyOnLoad 影响,暂且判断是否存活,并在必要的时候重新加载
|
|
||||||
// TODO: 考虑挂载到 GameObject 上
|
|
||||||
// AssetBundle 不会被干掉
|
|
||||||
if (IsNativeObjectAlive(replaceFont))
|
|
||||||
{
|
|
||||||
return replaceFont;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UnityResolve::Invoke<void>("il2cpp_gchandle_free", std::exchange(ReplaceFontHandle, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto extraAssetBundle = UnityResolve::Invoke<void*>("il2cpp_gchandle_get_target", bundleHandle);
|
|
||||||
static auto AssetBundle_LoadAsset = reinterpret_cast<void* (*)(void* _this, Il2CppString* name, Il2cppUtils::Il2CppReflectionType* type)>(
|
|
||||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.AssetBundle::LoadAsset_Internal(System.String,System.Type)")
|
|
||||||
);;
|
|
||||||
|
|
||||||
replaceFont = AssetBundle_LoadAsset(extraAssetBundle, Il2cppString::New(fontPath), Font_Type);
|
|
||||||
if (replaceFont)
|
|
||||||
{
|
|
||||||
ReplaceFontHandle = UnityResolve::Invoke<uint32_t>("il2cpp_gchandle_new", replaceFont, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::Error("Cannot load asset font\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::Error("Cannot find asset font\n");
|
|
||||||
}
|
|
||||||
return replaceFont;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void* fontCache = nullptr;
|
void* fontCache = nullptr;
|
||||||
void* GetReplaceFont() {
|
void* GetReplaceFont() {
|
||||||
static auto fontName = Local::GetBasePath() / "local-files" / "gkamsZHFontMIX.otf";
|
static std::string fontName = Local::GetBasePath() / "local-files" / "gkamsZHFontMIX.otf";
|
||||||
if (!std::filesystem::exists(fontName)) {
|
if (!std::filesystem::exists(fontName)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -453,11 +323,10 @@ namespace GakumasLocal::HookMain {
|
||||||
const auto newFont = Font_klass->New<void*>();
|
const auto newFont = Font_klass->New<void*>();
|
||||||
Font_ctor->Invoke<void>(newFont);
|
Font_ctor->Invoke<void>(newFont);
|
||||||
|
|
||||||
CreateFontFromPath(newFont, Il2cppString::New(fontName.string()));
|
CreateFontFromPath(newFont, Il2cppString::New(fontName));
|
||||||
fontCache = newFont;
|
fontCache = newFont;
|
||||||
return newFont;
|
return newFont;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
std::unordered_set<void*> updatedFontPtrs{};
|
std::unordered_set<void*> updatedFontPtrs{};
|
||||||
void UpdateFont(void* TMP_Textself) {
|
void UpdateFont(void* TMP_Textself) {
|
||||||
|
@ -481,7 +350,6 @@ namespace GakumasLocal::HookMain {
|
||||||
|
|
||||||
auto newFont = GetReplaceFont();
|
auto newFont = GetReplaceFont();
|
||||||
if (!newFont) return;
|
if (!newFont) return;
|
||||||
|
|
||||||
auto fontAsset = get_font->Invoke<void*>(TMP_Textself);
|
auto fontAsset = get_font->Invoke<void*>(TMP_Textself);
|
||||||
if (fontAsset) {
|
if (fontAsset) {
|
||||||
set_sourceFontFile->Invoke<void>(fontAsset, newFont);
|
set_sourceFontFile->Invoke<void>(fontAsset, newFont);
|
||||||
|
@ -491,9 +359,6 @@ namespace GakumasLocal::HookMain {
|
||||||
}
|
}
|
||||||
if (updatedFontPtrs.size() > 200) updatedFontPtrs.clear();
|
if (updatedFontPtrs.size() > 200) updatedFontPtrs.clear();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
Log::Error("UpdateFont: fontAsset is null.");
|
|
||||||
}
|
|
||||||
set_font->Invoke<void>(TMP_Textself, fontAsset);
|
set_font->Invoke<void>(TMP_Textself, fontAsset);
|
||||||
|
|
||||||
// auto fontMaterial = get_material->Invoke<void*>(fontAsset);
|
// auto fontMaterial = get_material->Invoke<void*>(fontAsset);
|
||||||
|
@ -526,26 +391,6 @@ namespace GakumasLocal::HookMain {
|
||||||
UpdateFont(self);
|
UpdateFont(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_HOOK(void, TMP_Text_SetText_2, (void* self, Il2cppString* sourceText, bool syncTextInputBox, void* mtd)) {
|
|
||||||
if (!sourceText) {
|
|
||||||
return TMP_Text_SetText_2_Orig(self, sourceText, syncTextInputBox, mtd);
|
|
||||||
}
|
|
||||||
const std::string origText = sourceText->ToString();
|
|
||||||
std::string transText;
|
|
||||||
if (Local::GetGenericText(origText, &transText)) {
|
|
||||||
const auto newText = UnityResolve::UnityType::String::New(transText);
|
|
||||||
UpdateFont(self);
|
|
||||||
return TMP_Text_SetText_2_Orig(self, newText, syncTextInputBox, mtd);
|
|
||||||
}
|
|
||||||
if (Config::textTest) {
|
|
||||||
TMP_Text_SetText_2_Orig(self, UnityResolve::UnityType::String::New("[TS]" + sourceText->ToString()), syncTextInputBox, mtd);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
TMP_Text_SetText_2_Orig(self, sourceText, syncTextInputBox, mtd);
|
|
||||||
}
|
|
||||||
UpdateFont(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_HOOK(void, TextMeshProUGUI_Awake, (void* self, void* method)) {
|
DEFINE_HOOK(void, TextMeshProUGUI_Awake, (void* self, void* method)) {
|
||||||
// Log::InfoFmt("TextMeshProUGUI_Awake at %p, self at %p", TextMeshProUGUI_Awake_Orig, self);
|
// Log::InfoFmt("TextMeshProUGUI_Awake at %p, self at %p", TextMeshProUGUI_Awake_Orig, self);
|
||||||
|
|
||||||
|
@ -714,27 +559,15 @@ namespace GakumasLocal::HookMain {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
DEFINE_HOOK(void, PictureBookLiveThumbnailView_SetDataAsync, (void* self, void* liveData, bool isReleased, bool isUnlocked, bool isNew, bool hasLiveSkin, void* ct, void* mtd)) {
|
||||||
DEFINE_HOOK(void, PictureBookLiveThumbnailView_SetDataAsync, (void* retstr, void* self, void* liveData, Il2cppString* characterId, bool isReleased, bool isUnlocked, bool isNew, bool hasLiveSkin, void* ct, void* mtd)) {
|
|
||||||
// Log::DebugFmt("PictureBookLiveThumbnailView_SetDataAsync: isReleased: %d, isUnlocked: %d, isNew: %d, hasLiveSkin: %d", isReleased, isUnlocked, isNew, hasLiveSkin);
|
// Log::DebugFmt("PictureBookLiveThumbnailView_SetDataAsync: isReleased: %d, isUnlocked: %d, isNew: %d, hasLiveSkin: %d", isReleased, isUnlocked, isNew, hasLiveSkin);
|
||||||
if (Config::dbgMode && Config::unlockAllLive) {
|
if (Config::dbgMode && Config::unlockAllLive) {
|
||||||
isUnlocked = true;
|
isUnlocked = true;
|
||||||
isReleased = true;
|
isReleased = true;
|
||||||
hasLiveSkin = true;
|
hasLiveSkin = true;
|
||||||
}
|
}
|
||||||
PictureBookLiveThumbnailView_SetDataAsync_Orig(retstr, self, liveData, characterId, isReleased, isUnlocked, isNew, hasLiveSkin, ct, mtd);
|
PictureBookLiveThumbnailView_SetDataAsync_Orig(self, liveData, isReleased, isUnlocked, isNew, hasLiveSkin, ct, mtd);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
DEFINE_HOOK(void, PictureBookLiveThumbnailView_SetDataAsync, (void* self, void* liveData, Il2cppString* characterId, bool isReleased, bool isUnlocked, bool isNew, bool hasLiveSkin, void* ct, void* mtd)) {
|
|
||||||
// Log::DebugFmt("PictureBookLiveThumbnailView_SetDataAsync: isReleased: %d, isUnlocked: %d, isNew: %d, hasLiveSkin: %d", isReleased, isUnlocked, isNew, hasLiveSkin);
|
|
||||||
if (Config::dbgMode && Config::unlockAllLive) {
|
|
||||||
isUnlocked = true;
|
|
||||||
isReleased = true;
|
|
||||||
hasLiveSkin = true;
|
|
||||||
}
|
|
||||||
PictureBookLiveThumbnailView_SetDataAsync_Orig(self, liveData, characterId, isReleased, isUnlocked, isNew, hasLiveSkin, ct, mtd);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum class GetIdolIdType {
|
enum class GetIdolIdType {
|
||||||
MusicId,
|
MusicId,
|
||||||
|
@ -877,34 +710,6 @@ namespace GakumasLocal::HookMain {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* getCompletedUniTask() {
|
|
||||||
static auto unitask_klass = Il2cppUtils::GetClass("UniTask.dll", "Cysharp.Threading.Tasks", "UniTask");
|
|
||||||
static auto CompletedTask_field = unitask_klass->Get<UnityResolve::Field>("CompletedTask");
|
|
||||||
auto ret = UnityResolve::Invoke<void*>("il2cpp_object_new", unitask_klass->address);
|
|
||||||
UnityResolve::Invoke<void>("il2cpp_field_static_get_value", CompletedTask_field->address, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
// 绕过切歌时的等待以及网络请求
|
|
||||||
DEFINE_HOOK(void*, Produce_ViewPictureBookLiveAsync, (void* retstr, Il2cppString* musicId, Il2cppString* characterId,
|
|
||||||
void* ct, void* callOption, void* errorHandlerIl, Il2cppString* requestIdForResponseCache, void* mtd)) {
|
|
||||||
|
|
||||||
// Log::DebugFmt("Produce_ViewPictureBookLiveAsync: %s - %s", musicId->ToString().c_str(), characterId->ToString().c_str());
|
|
||||||
if (Config::unlockAllLive) return getCompletedUniTask();
|
|
||||||
return Produce_ViewPictureBookLiveAsync_Orig(retstr, musicId, characterId, ct, callOption, errorHandlerIl, requestIdForResponseCache, mtd);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
DEFINE_HOOK(void*, Produce_ViewPictureBookLiveAsync, (Il2cppString* musicId, Il2cppString* characterId,
|
|
||||||
void* ct, void* callOption, void* errorHandlerIl, Il2cppString* requestIdForResponseCache, void* mtd)) {
|
|
||||||
|
|
||||||
// Log::DebugFmt("Produce_ViewPictureBookLiveAsync: %s - %s", musicId->ToString().c_str(), characterId->ToString().c_str());
|
|
||||||
if (Config::unlockAllLive) return getCompletedUniTask();
|
|
||||||
return Produce_ViewPictureBookLiveAsync_Orig(musicId, characterId, ct, callOption, errorHandlerIl, requestIdForResponseCache, mtd);
|
|
||||||
}
|
|
||||||
#endif // GKMS_WINDOWS
|
|
||||||
|
|
||||||
|
|
||||||
void* PictureBookWindowPresenter_instance = nullptr;
|
void* PictureBookWindowPresenter_instance = nullptr;
|
||||||
std::string PictureBookWindowPresenter_charaId;
|
std::string PictureBookWindowPresenter_charaId;
|
||||||
DEFINE_HOOK(void*, PictureBookWindowPresenter_GetLiveMusics, (void* self, Il2cppString* charaId, void* mtd)) {
|
DEFINE_HOOK(void*, PictureBookWindowPresenter_GetLiveMusics, (void* self, Il2cppString* charaId, void* mtd)) {
|
||||||
|
@ -917,59 +722,27 @@ namespace GakumasLocal::HookMain {
|
||||||
static auto PictureBookWindowPresenter_klass = Il2cppUtils::GetClass("Assembly-CSharp.dll", "Campus.OutGame",
|
static auto PictureBookWindowPresenter_klass = Il2cppUtils::GetClass("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookWindowPresenter");
|
"PictureBookWindowPresenter");
|
||||||
static auto existsMusicIds_field = PictureBookWindowPresenter_klass->Get<UnityResolve::Field>("_existsMusicIds");
|
static auto existsMusicIds_field = PictureBookWindowPresenter_klass->Get<UnityResolve::Field>("_existsMusicIds");
|
||||||
// auto existsMusicIds = Il2cppUtils::ClassGetFieldValue<UnityResolve::UnityType::List<Il2cppString*>*>(self, existsMusicIds_field);
|
auto existsMusicIds = Il2cppUtils::ClassGetFieldValue<UnityResolve::UnityType::List<Il2cppString*>*>(self, existsMusicIds_field);
|
||||||
auto existsMusicIds = Il2cppUtils::ClassGetFieldValue<UnityResolve::UnityType::Dictionary<Il2cppString*, UnityResolve::UnityType::List<Il2cppString*>>*>(self, existsMusicIds_field);
|
|
||||||
|
|
||||||
if (!existsMusicIds) {
|
if (!existsMusicIds) {
|
||||||
static auto Dict_List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
|
|
||||||
"System.Collections.Generic.Dictionary`2[System.String, System.Collections.Generic.List`1[System.String]]");
|
|
||||||
static auto List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
|
static auto List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
|
||||||
"System.Collections.Generic.List`1[System.String]");
|
"System.Collections.Generic.List`1[System.String]");
|
||||||
static auto List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(List_String_klass, ".ctor", 0);
|
static auto List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(List_String_klass, ".ctor", 0);
|
||||||
static auto List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(List_String_ctor_mtd->methodPointer);
|
static auto List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(List_String_ctor_mtd->methodPointer);
|
||||||
|
|
||||||
auto fullIds = GetIdolMusicIdAll();
|
|
||||||
|
|
||||||
static auto Dict_List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(Dict_List_String_klass, ".ctor", 0);
|
|
||||||
static auto Dict_List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(Dict_List_String_ctor_mtd->methodPointer);
|
|
||||||
|
|
||||||
auto newDict = UnityResolve::Invoke<void*>("il2cpp_object_new", Dict_List_String_klass);
|
|
||||||
Dict_List_String_ctor(newDict, Dict_List_String_ctor_mtd);
|
|
||||||
Il2cppUtils::Tools::CSDictEditor<Il2cppString*, void*> newDictEditor(newDict, Dict_List_String_klass);
|
|
||||||
|
|
||||||
// auto fullIds = GetIdolMusicIdAll();
|
|
||||||
|
|
||||||
for (auto& i : fullIds) {
|
|
||||||
// Log::DebugFmt("GetLiveMusics - Add: %s", i.c_str()); // eg. music-all-amao-001, music-char-hski-001
|
|
||||||
//newListEditor.Add(Il2cppString::New(i));
|
|
||||||
auto newList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
|
auto newList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
|
||||||
List_String_ctor(newList, List_String_ctor_mtd);
|
List_String_ctor(newList, List_String_ctor_mtd);
|
||||||
newDictEditor.Add(Il2cppString::New(i), newList);
|
Il2cppUtils::Tools::CSListEditor<Il2cppString*> newListEditor(newList);
|
||||||
|
|
||||||
|
auto fullIds = GetIdolMusicIdAll();
|
||||||
|
|
||||||
|
for (auto& i : fullIds) {
|
||||||
|
// Log::DebugFmt("GetLiveMusics - Add: %s", i.c_str());
|
||||||
|
newListEditor.Add(Il2cppString::New(i));
|
||||||
}
|
}
|
||||||
Il2cppUtils::ClassSetFieldValue(self, existsMusicIds_field, newDict);
|
Il2cppUtils::ClassSetFieldValue(self, existsMusicIds_field, newList);
|
||||||
existsMusicIds = reinterpret_cast<decltype(existsMusicIds)>(newDict);
|
|
||||||
// Log::DebugFmt("GetLiveMusics - set end: %d", fullIds.size());
|
// Log::DebugFmt("GetLiveMusics - set end: %d", fullIds.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Il2cppUtils::Tools::CSDictEditor<Il2cppString*, void*> dicCheckEditor(existsMusicIds, Dict_List_String_klass);
|
|
||||||
for (auto& i : fullIds) {
|
|
||||||
auto currKeyStr = Il2cppString::New(i);
|
|
||||||
void* currList;
|
|
||||||
if (dicCheckEditor.ContainsKey(currKeyStr)) {
|
|
||||||
currList = dicCheckEditor.get_Item(currKeyStr);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
currList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
|
|
||||||
List_String_ctor(currList, List_String_ctor_mtd);
|
|
||||||
}
|
|
||||||
Il2cppUtils::Tools::CSListEditor<Il2cppString*> currListEditor(currList);
|
|
||||||
if (!currListEditor.Contains(charaId)) {
|
|
||||||
currListEditor.Add(charaId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PictureBookWindowPresenter_GetLiveMusics_Orig(self, charaId, mtd);
|
return PictureBookWindowPresenter_GetLiveMusics_Orig(self, charaId, mtd);
|
||||||
|
@ -978,7 +751,7 @@ namespace GakumasLocal::HookMain {
|
||||||
DEFINE_HOOK(void, PictureBookLiveSelectScreenModel_ctor, (void* self, void* transitionParam, UnityResolve::UnityType::List<void*>* musics, void* mtd)) {
|
DEFINE_HOOK(void, PictureBookLiveSelectScreenModel_ctor, (void* self, void* transitionParam, UnityResolve::UnityType::List<void*>* musics, void* mtd)) {
|
||||||
// Log::DebugFmt("PictureBookLiveSelectScreenModel_ctor");
|
// Log::DebugFmt("PictureBookLiveSelectScreenModel_ctor");
|
||||||
|
|
||||||
if (Config::dbgMode && Config::unlockAllLive) {
|
if (Config::unlockAllLive) {
|
||||||
static auto GetLiveMusics = Il2cppUtils::GetMethod("Assembly-CSharp.dll", "Campus.OutGame",
|
static auto GetLiveMusics = Il2cppUtils::GetMethod("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookWindowPresenter", "GetLiveMusics");
|
"PictureBookWindowPresenter", "GetLiveMusics");
|
||||||
if (PictureBookWindowPresenter_instance && !PictureBookWindowPresenter_charaId.empty()) {
|
if (PictureBookWindowPresenter_instance && !PictureBookWindowPresenter_charaId.empty()) {
|
||||||
|
@ -992,16 +765,17 @@ namespace GakumasLocal::HookMain {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needRestoreHides = false;
|
bool needRestoreHides = false;
|
||||||
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_MoveLiveScene, (void* self, void* produceLive, bool isPlayCharacterFocusCamera, void* mtd)) {
|
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_MoveLiveScene, (void* self, void* produceLive,
|
||||||
|
Il2cppString* characterId, Il2cppString* idolCardId, Il2cppString* costumeId, Il2cppString* costumeHeadId, void* mtd)) {
|
||||||
needRestoreHides = false;
|
needRestoreHides = false;
|
||||||
// Log::InfoFmt("MoveLiveScene: characterId: %s, idolCardId: %s, costumeId: %s, costumeHeadId: %s,",
|
Log::InfoFmt("MoveLiveScene: characterId: %s, idolCardId: %s, costumeId: %s, costumeHeadId: %s,",
|
||||||
// characterId->ToString().c_str(), idolCardId->ToString().c_str(), costumeId->ToString().c_str(), costumeHeadId->ToString().c_str());
|
characterId->ToString().c_str(), idolCardId->ToString().c_str(), costumeId->ToString().c_str(), costumeHeadId->ToString().c_str());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
characterId: hski, costumeId: hski-cstm-0002, costumeHeadId: costume_head_hski-cstm-0002,
|
characterId: hski, costumeId: hski-cstm-0002, costumeHeadId: costume_head_hski-cstm-0002,
|
||||||
characterId: shro, costumeId: shro-cstm-0006, costumeHeadId: costume_head_shro-cstm-0006,
|
characterId: shro, costumeId: shro-cstm-0006, costumeHeadId: costume_head_shro-cstm-0006,
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
if (Config::dbgMode && Config::enableLiveCustomeDress) {
|
if (Config::dbgMode && Config::enableLiveCustomeDress) {
|
||||||
// 修改 LiveFixedData_GetCharacter 可以更改 Loading 角色和演唱者名字,而不变更实际登台人
|
// 修改 LiveFixedData_GetCharacter 可以更改 Loading 角色和演唱者名字,而不变更实际登台人
|
||||||
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId,
|
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId,
|
||||||
|
@ -1009,18 +783,11 @@ namespace GakumasLocal::HookMain {
|
||||||
Config::liveCustomeHeadId.empty() ? costumeHeadId : Il2cppString::New(Config::liveCustomeHeadId),
|
Config::liveCustomeHeadId.empty() ? costumeHeadId : Il2cppString::New(Config::liveCustomeHeadId),
|
||||||
mtd);
|
mtd);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
// return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId, costumeId, costumeHeadId, mtd);
|
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId, costumeId, costumeHeadId, mtd);
|
||||||
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, isPlayCharacterFocusCamera, mtd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::string lastMusicId;
|
// std::string lastMusicId;
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_OnSelectMusic, (void* retstr, void* self, void* itemModel, void* ct, void* mtd)) {
|
|
||||||
// if (!itemModel) return nullptr;
|
|
||||||
return PictureBookLiveSelectScreenPresenter_OnSelectMusic_Orig(retstr, self, itemModel, ct, mtd);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
DEFINE_HOOK(void, PictureBookLiveSelectScreenPresenter_OnSelectMusic, (void* self, void* itemModel, void* ct, void* mtd)) {
|
DEFINE_HOOK(void, PictureBookLiveSelectScreenPresenter_OnSelectMusic, (void* self, void* itemModel, void* ct, void* mtd)) {
|
||||||
/* // 修改角色后,Live 结束返回时, itemModel 为 null
|
/* // 修改角色后,Live 结束返回时, itemModel 为 null
|
||||||
Log::DebugFmt("OnSelectMusic itemModel at %p", itemModel);
|
Log::DebugFmt("OnSelectMusic itemModel at %p", itemModel);
|
||||||
|
@ -1054,7 +821,6 @@ namespace GakumasLocal::HookMain {
|
||||||
if (!itemModel) return;
|
if (!itemModel) return;
|
||||||
return PictureBookLiveSelectScreenPresenter_OnSelectMusic_Orig(self, itemModel, ct, mtd);
|
return PictureBookLiveSelectScreenPresenter_OnSelectMusic_Orig(self, itemModel, ct, mtd);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
DEFINE_HOOK(bool, VLDOF_IsActive, (void* self)) {
|
DEFINE_HOOK(bool, VLDOF_IsActive, (void* self)) {
|
||||||
if (Config::enableFreeCamera) return false;
|
if (Config::enableFreeCamera) return false;
|
||||||
|
@ -1306,14 +1072,10 @@ namespace GakumasLocal::HookMain {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (processedIOS) {
|
if (processedIOS) {
|
||||||
Log::DebugFmt("Restore API");
|
Log::DebugFmt("Restore API to Android");
|
||||||
static auto ApiBase_klass = Il2cppUtils::get_class_from_instance(self);
|
static auto ApiBase_klass = Il2cppUtils::get_class_from_instance(self);
|
||||||
static auto platform_field = UnityResolve::Invoke<Il2cppUtils::FieldInfo*>("il2cpp_class_get_field_from_name", ApiBase_klass, "_platform");
|
static auto platform_field = UnityResolve::Invoke<Il2cppUtils::FieldInfo*>("il2cpp_class_get_field_from_name", ApiBase_klass, "_platform");
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
Il2cppUtils::ClassSetFieldValue(self, platform_field, Il2cppString::New("dmm"));
|
|
||||||
#else
|
|
||||||
Il2cppUtils::ClassSetFieldValue(self, platform_field, Il2cppString::New("Android"));
|
Il2cppUtils::ClassSetFieldValue(self, platform_field, Il2cppString::New("Android"));
|
||||||
#endif
|
|
||||||
processedIOS = nullptr;
|
processedIOS = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1332,70 +1094,6 @@ namespace GakumasLocal::HookMain {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
// DMM Only
|
|
||||||
DEFINE_HOOK(void*, WindowHandle_SetWindowLong, (int32_t nIndex, intptr_t dwNewLong, void* mtd)) {
|
|
||||||
if (GakumasLocal::Config::dmmUnlockSize) {
|
|
||||||
// Log::DebugFmt("WindowHandle_SetWindowLong: %d, %p\n", nIndex, dwNewLong);
|
|
||||||
|
|
||||||
if (nIndex == GWLP_WNDPROC) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return WindowHandle_SetWindowLong_Orig(nIndex, dwNewLong, mtd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DMM Only
|
|
||||||
void SetResolution(int width, int height, bool fullscreen) {
|
|
||||||
static auto Screen_SetResolution = reinterpret_cast<void (*)(UINT, UINT, UINT, void*)>(
|
|
||||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::SetResolution_Injected(System.Int32,System.Int32,UnityEngine.FullScreenMode,UnityEngine.RefreshRate&)"));
|
|
||||||
|
|
||||||
int64_t v8[3];
|
|
||||||
v8[0] = 0x100000000LL;
|
|
||||||
Screen_SetResolution(width, height, 2 * !fullscreen + 1, v8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DMM Only
|
|
||||||
DEFINE_HOOK(void, WindowManager_ApplyOrientationSettings, (int orientation, void* method)) {
|
|
||||||
if (!GakumasLocal::Config::dmmUnlockSize) return WindowManager_ApplyOrientationSettings_Orig(orientation, method);
|
|
||||||
|
|
||||||
static auto get_Height = reinterpret_cast<int (*)()>(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_height()"));
|
|
||||||
static auto get_Width = reinterpret_cast<int (*)()>(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_width()"));
|
|
||||||
|
|
||||||
static auto lastWidth = -1;
|
|
||||||
static auto lastHeight = -1;
|
|
||||||
|
|
||||||
const auto currWidth = get_Width();
|
|
||||||
const auto currHeight = get_Height();
|
|
||||||
|
|
||||||
if (lastWidth == -1) {
|
|
||||||
lastWidth = currWidth;
|
|
||||||
lastHeight = currHeight;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool lastIsPortrait = lastWidth < lastHeight;
|
|
||||||
const bool currIsPortrait = currWidth < currHeight;
|
|
||||||
if (lastIsPortrait == currIsPortrait) {
|
|
||||||
lastWidth = currWidth;
|
|
||||||
lastHeight = currHeight;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetResolution(lastWidth, lastHeight, false);
|
|
||||||
lastWidth = currWidth;
|
|
||||||
lastHeight = currHeight;
|
|
||||||
|
|
||||||
Log::DebugFmt("WindowManager_ApplyOrientationSettings: %d (%d, %d)\n", orientation, get_Width(), get_Height());
|
|
||||||
}
|
|
||||||
|
|
||||||
// DMM Only
|
|
||||||
DEFINE_HOOK(void, AspectRatioHandler_NudgeWindow, (void* method)) {
|
|
||||||
if (!GakumasLocal::Config::dmmUnlockSize) return AspectRatioHandler_NudgeWindow_Orig(method);
|
|
||||||
// printf("AspectRatioHandler_NudgeWindow\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void UpdateSwingBreastBonesData(void* initializeData) {
|
void UpdateSwingBreastBonesData(void* initializeData) {
|
||||||
if (!Config::enableBreastParam) return;
|
if (!Config::enableBreastParam) return;
|
||||||
|
@ -1566,18 +1264,8 @@ namespace GakumasLocal::HookMain {
|
||||||
|
|
||||||
void StartInjectFunctions() {
|
void StartInjectFunctions() {
|
||||||
const auto hookInstaller = Plugin::GetInstance().GetHookInstaller();
|
const auto hookInstaller = Plugin::GetInstance().GetHookInstaller();
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
auto il2cpp_module = GetModuleHandle("GameAssembly.dll");
|
|
||||||
if (!il2cpp_module) {
|
|
||||||
Log::ErrorFmt("GameAssembly.dll not loaded.");
|
|
||||||
}
|
|
||||||
UnityResolve::Init(il2cpp_module, UnityResolve::Mode::Il2Cpp, Config::lazyInit);
|
|
||||||
GakumasLocal::WinHooks::Keyboard::InstallWndProcHook();
|
|
||||||
#else
|
|
||||||
UnityResolve::Init(xdl_open(hookInstaller->m_il2cppLibraryPath.c_str(), RTLD_NOW),
|
UnityResolve::Init(xdl_open(hookInstaller->m_il2cppLibraryPath.c_str(), RTLD_NOW),
|
||||||
UnityResolve::Mode::Il2Cpp, Config::lazyInit);
|
UnityResolve::Mode::Il2Cpp, Config::lazyInit);
|
||||||
#endif
|
|
||||||
|
|
||||||
ADD_HOOK(AssetBundle_LoadAssetAsync, Il2cppUtils::il2cpp_resolve_icall(
|
ADD_HOOK(AssetBundle_LoadAssetAsync, Il2cppUtils::il2cpp_resolve_icall(
|
||||||
"UnityEngine.AssetBundle::LoadAssetAsync_Internal(System.String,System.Type)"));
|
"UnityEngine.AssetBundle::LoadAssetAsync_Internal(System.String,System.Type)"));
|
||||||
|
@ -1600,9 +1288,6 @@ namespace GakumasLocal::HookMain {
|
||||||
ADD_HOOK(TMP_Text_PopulateTextBackingArray, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
ADD_HOOK(TMP_Text_PopulateTextBackingArray, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
||||||
"TMP_Text", "PopulateTextBackingArray",
|
"TMP_Text", "PopulateTextBackingArray",
|
||||||
{"System.String", "System.Int32", "System.Int32"}));
|
{"System.String", "System.Int32", "System.Int32"}));
|
||||||
ADD_HOOK(TMP_Text_SetText_2, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
|
||||||
"TMP_Text", "SetText",
|
|
||||||
{ "System.String", "System.Boolean" }));
|
|
||||||
|
|
||||||
ADD_HOOK(TextField_set_value, Il2cppUtils::GetMethodPointer("UnityEngine.UIElementsModule.dll", "UnityEngine.UIElements",
|
ADD_HOOK(TextField_set_value, Il2cppUtils::GetMethodPointer("UnityEngine.UIElementsModule.dll", "UnityEngine.UIElements",
|
||||||
"TextField", "set_value"));
|
"TextField", "set_value"));
|
||||||
|
@ -1688,18 +1373,12 @@ namespace GakumasLocal::HookMain {
|
||||||
ADD_HOOK(UserCostumeCollection_FindBy, UserCostumeCollection_FindBy_mtd->methodPointer);
|
ADD_HOOK(UserCostumeCollection_FindBy, UserCostumeCollection_FindBy_mtd->methodPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 双端
|
|
||||||
ADD_HOOK(PictureBookLiveThumbnailView_SetDataAsync,
|
ADD_HOOK(PictureBookLiveThumbnailView_SetDataAsync,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame.PictureBook",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame.PictureBook",
|
||||||
"PictureBookLiveThumbnailView", "SetDataAsync", {"*", "*", "*", "*", "*"}));
|
"PictureBookLiveThumbnailView", "SetDataAsync", {"*", "*", "*", "*", "*"}));
|
||||||
|
|
||||||
ADD_HOOK(PictureBookWindowPresenter_GetLiveMusics,
|
ADD_HOOK(PictureBookWindowPresenter_GetLiveMusics,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookWindowPresenter", "GetLiveMusics"));
|
"PictureBookWindowPresenter", "GetLiveMusics"));
|
||||||
|
|
||||||
ADD_HOOK(Produce_ViewPictureBookLiveAsync,
|
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "",
|
|
||||||
"Produce", "ViewPictureBookLiveAsync"));
|
|
||||||
ADD_HOOK(PictureBookLiveSelectScreenModel_ctor,
|
ADD_HOOK(PictureBookLiveSelectScreenModel_ctor,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookLiveSelectScreenModel", ".ctor"));
|
"PictureBookLiveSelectScreenModel", ".ctor"));
|
||||||
|
@ -1707,8 +1386,6 @@ namespace GakumasLocal::HookMain {
|
||||||
ADD_HOOK(PictureBookLiveSelectScreenPresenter_MoveLiveScene,
|
ADD_HOOK(PictureBookLiveSelectScreenPresenter_MoveLiveScene,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookLiveSelectScreenPresenter", "MoveLiveScene"));
|
"PictureBookLiveSelectScreenPresenter", "MoveLiveScene"));
|
||||||
|
|
||||||
// 双端
|
|
||||||
ADD_HOOK(PictureBookLiveSelectScreenPresenter_OnSelectMusic,
|
ADD_HOOK(PictureBookLiveSelectScreenPresenter_OnSelectMusic,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookLiveSelectScreenPresenter", "OnSelectMusicAsync"));
|
"PictureBookLiveSelectScreenPresenter", "OnSelectMusicAsync"));
|
||||||
|
@ -1778,7 +1455,6 @@ namespace GakumasLocal::HookMain {
|
||||||
ADD_HOOK(Internal_Log, Il2cppUtils::il2cpp_resolve_icall(
|
ADD_HOOK(Internal_Log, Il2cppUtils::il2cpp_resolve_icall(
|
||||||
"UnityEngine.DebugLogHandler::Internal_Log(UnityEngine.LogType,UnityEngine.LogOption,System.String,UnityEngine.Object)"));
|
"UnityEngine.DebugLogHandler::Internal_Log(UnityEngine.LogType,UnityEngine.LogOption,System.String,UnityEngine.Object)"));
|
||||||
|
|
||||||
// 双端
|
|
||||||
ADD_HOOK(InternalSetOrientationAsync,
|
ADD_HOOK(InternalSetOrientationAsync,
|
||||||
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
|
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
|
||||||
"ScreenOrientationControllerBase", "InternalSetOrientationAsync"));
|
"ScreenOrientationControllerBase", "InternalSetOrientationAsync"));
|
||||||
|
@ -1795,48 +1471,11 @@ namespace GakumasLocal::HookMain {
|
||||||
"UnityEngine.Application::set_targetFrameRate(System.Int32)"));
|
"UnityEngine.Application::set_targetFrameRate(System.Int32)"));
|
||||||
ADD_HOOK(EndCameraRendering, Il2cppUtils::GetMethodPointer("UnityEngine.CoreModule.dll", "UnityEngine.Rendering",
|
ADD_HOOK(EndCameraRendering, Il2cppUtils::GetMethodPointer("UnityEngine.CoreModule.dll", "UnityEngine.Rendering",
|
||||||
"RenderPipeline", "EndCameraRendering"));
|
"RenderPipeline", "EndCameraRendering"));
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
ADD_HOOK(WindowHandle_SetWindowLong, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
|
||||||
"WindowHandle", "SetWindowLong"));
|
|
||||||
//ADD_HOOK(WindowHandle_SetWindowLong32, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
|
||||||
// "WindowHandle", "SetWindowLong32"));
|
|
||||||
//ADD_HOOK(WindowHandle_SetWindowLongPtr64, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
|
||||||
// "WindowHandle", "SetWindowLongPtr64"));
|
|
||||||
//ADD_HOOK(WindowSizeUtility_RestoreWindowSize, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
|
||||||
// "WindowSizeUtility", "RestoreWindowSize"));
|
|
||||||
ADD_HOOK(WindowManager_ApplyOrientationSettings, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
|
||||||
"WindowManager", "ApplyOrientationSettings"));
|
|
||||||
ADD_HOOK(AspectRatioHandler_NudgeWindow, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
|
||||||
"AspectRatioHandler", "NudgeWindow"));
|
|
||||||
//ADD_HOOK(AspectRatioHandler_WindowProc, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
|
||||||
// "AspectRatioHandler", "WindowProc"));
|
|
||||||
|
|
||||||
if (GakumasLocal::Config::dmmUnlockSize) {
|
|
||||||
std::thread([]() {
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
|
||||||
auto hWnd = FindWindowW(L"UnityWndClass", L"gakumas");
|
|
||||||
// 添加可调整大小的边框和最大化按钮
|
|
||||||
LONG style = GetWindowLong(hWnd, GWL_STYLE);
|
|
||||||
style |= WS_THICKFRAME | WS_MAXIMIZEBOX;
|
|
||||||
SetWindowLong(hWnd, GWL_STYLE, style);
|
|
||||||
}).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
g_extra_assetbundle_paths.push_back((gakumasLocalPath / "local-files/gakumasassets").string());
|
|
||||||
LoadExtraAssetBundle();
|
|
||||||
GkmsResourceUpdate::CheckUpdateFromAPI(false);
|
|
||||||
#endif // GKMS_WINDOWS
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// 77 2640 5000
|
// 77 2640 5000
|
||||||
|
|
||||||
DEFINE_HOOK(int, il2cpp_init, (const char* domain_name)) {
|
DEFINE_HOOK(int, il2cpp_init, (const char* domain_name)) {
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
const auto ret = il2cpp_init_Orig(domain_name);
|
const auto ret = il2cpp_init_Orig(domain_name);
|
||||||
#else
|
|
||||||
const auto ret = 0;
|
|
||||||
#endif
|
|
||||||
// InjectFunctions();
|
// InjectFunctions();
|
||||||
|
|
||||||
Log::Info("Waiting for config...");
|
Log::Info("Waiting for config...");
|
||||||
|
@ -1885,13 +1524,8 @@ namespace GakumasLocal::Hook {
|
||||||
|
|
||||||
Log::Info("Installing hook");
|
Log::Info("Installing hook");
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
ADD_HOOK(HookMain::il2cpp_init,
|
ADD_HOOK(HookMain::il2cpp_init,
|
||||||
Plugin::GetInstance().GetHookInstaller()->LookupSymbol("il2cpp_init"));
|
Plugin::GetInstance().GetHookInstaller()->LookupSymbol("il2cpp_init"));
|
||||||
#else
|
|
||||||
HookMain::il2cpp_init_Hook(nullptr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
Log::Info("Hook installed");
|
Log::Info("Hook installed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,22 +308,6 @@ namespace Il2cppUtils {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = void*>
|
|
||||||
static void iterate_IEnumerable(const void* obj, std::invocable<T> auto&& receiver)
|
|
||||||
{
|
|
||||||
const auto klass = get_class_from_instance(obj);
|
|
||||||
const auto getEnumeratorMethod = reinterpret_cast<void* (*)(const void*)>(il2cpp_class_get_method_from_name(klass, "GetEnumerator", 0)->methodPointer);
|
|
||||||
const auto enumerator = getEnumeratorMethod(obj);
|
|
||||||
const auto enumeratorClass = get_class_from_instance(enumerator);
|
|
||||||
const auto getCurrentMethod = reinterpret_cast<T(*)(void*)>(il2cpp_class_get_method_from_name(enumeratorClass, "get_Current", 0)->methodPointer);
|
|
||||||
const auto moveNextMethod = reinterpret_cast<bool(*)(void*)>(il2cpp_class_get_method_from_name(enumeratorClass, "MoveNext", 0)->methodPointer);
|
|
||||||
|
|
||||||
while (moveNextMethod(enumerator))
|
|
||||||
{
|
|
||||||
static_cast<decltype(receiver)>(receiver)(getCurrentMethod(enumerator));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Tools {
|
namespace Tools {
|
||||||
|
|
||||||
template <typename T = void*>
|
template <typename T = void*>
|
||||||
|
@ -337,23 +321,17 @@ namespace Il2cppUtils {
|
||||||
lst_get_Item_method = il2cpp_class_get_method_from_name(list_klass, "get_Item", 1);
|
lst_get_Item_method = il2cpp_class_get_method_from_name(list_klass, "get_Item", 1);
|
||||||
lst_set_Item_method = il2cpp_class_get_method_from_name(list_klass, "set_Item", 2);
|
lst_set_Item_method = il2cpp_class_get_method_from_name(list_klass, "set_Item", 2);
|
||||||
lst_Add_method = il2cpp_class_get_method_from_name(list_klass, "Add", 1);
|
lst_Add_method = il2cpp_class_get_method_from_name(list_klass, "Add", 1);
|
||||||
lst_Contains_method = il2cpp_class_get_method_from_name(list_klass, "Contains", 1);
|
|
||||||
|
|
||||||
lst_get_Count = reinterpret_cast<lst_get_Count_t>(lst_get_Count_method->methodPointer);
|
lst_get_Count = reinterpret_cast<lst_get_Count_t>(lst_get_Count_method->methodPointer);
|
||||||
lst_get_Item = reinterpret_cast<lst_get_Item_t>(lst_get_Item_method->methodPointer);
|
lst_get_Item = reinterpret_cast<lst_get_Item_t>(lst_get_Item_method->methodPointer);
|
||||||
lst_set_Item = reinterpret_cast<lst_set_Item_t>(lst_set_Item_method->methodPointer);
|
lst_set_Item = reinterpret_cast<lst_set_Item_t>(lst_set_Item_method->methodPointer);
|
||||||
lst_Add = reinterpret_cast<lst_Add_t>(lst_Add_method->methodPointer);
|
lst_Add = reinterpret_cast<lst_Add_t>(lst_Add_method->methodPointer);
|
||||||
lst_Contains = reinterpret_cast<lst_Contains_t>(lst_Contains_method->methodPointer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Add(T value) {
|
void Add(T value) {
|
||||||
lst_Add(lst, value, lst_Add_method);
|
lst_Add(lst, value, lst_Add_method);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Contains(T value) {
|
|
||||||
return lst_Contains(lst, value, lst_Contains_method);
|
|
||||||
}
|
|
||||||
|
|
||||||
T get_Item(int index) {
|
T get_Item(int index) {
|
||||||
return lst_get_Item(lst, index, lst_get_Item_method);
|
return lst_get_Item(lst, index, lst_get_Item_method);
|
||||||
}
|
}
|
||||||
|
@ -407,86 +385,16 @@ namespace Il2cppUtils {
|
||||||
typedef void(*lst_Add_t)(void*, T, void* mtd);
|
typedef void(*lst_Add_t)(void*, T, void* mtd);
|
||||||
typedef void(*lst_set_Item_t)(void*, int, T, void* mtd);
|
typedef void(*lst_set_Item_t)(void*, int, T, void* mtd);
|
||||||
typedef int(*lst_get_Count_t)(void*, void* mtd);
|
typedef int(*lst_get_Count_t)(void*, void* mtd);
|
||||||
typedef bool(*lst_Contains_t)(void*, T, void* mtd);
|
|
||||||
|
|
||||||
MethodInfo* lst_get_Item_method;
|
MethodInfo* lst_get_Item_method;
|
||||||
MethodInfo* lst_Add_method;
|
MethodInfo* lst_Add_method;
|
||||||
MethodInfo* lst_get_Count_method;
|
MethodInfo* lst_get_Count_method;
|
||||||
MethodInfo* lst_set_Item_method;
|
MethodInfo* lst_set_Item_method;
|
||||||
MethodInfo* lst_Contains_method;
|
|
||||||
|
|
||||||
lst_get_Item_t lst_get_Item;
|
lst_get_Item_t lst_get_Item;
|
||||||
lst_set_Item_t lst_set_Item;
|
lst_set_Item_t lst_set_Item;
|
||||||
lst_Add_t lst_Add;
|
lst_Add_t lst_Add;
|
||||||
lst_get_Count_t lst_get_Count;
|
lst_get_Count_t lst_get_Count;
|
||||||
lst_Contains_t lst_Contains;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename KT = void*, typename VT = void*>
|
|
||||||
class CSDictEditor {
|
|
||||||
public:
|
|
||||||
// @param dict: Dictionary instance.
|
|
||||||
// @param dictTypeStr: Reflection type. eg: "System.Collections.Generic.Dictionary`2[System.Int32, System.Int32]"
|
|
||||||
CSDictEditor(void* dict, const char* dictTypeStr) {
|
|
||||||
dic_klass = Il2cppUtils::get_system_class_from_reflection_type_str(dictTypeStr);
|
|
||||||
initDict(dict);
|
|
||||||
}
|
|
||||||
|
|
||||||
CSDictEditor(void* dict) {
|
|
||||||
dic_klass = get_class_from_instance(dict);
|
|
||||||
initDict(dict);
|
|
||||||
}
|
|
||||||
|
|
||||||
CSDictEditor(void* dict, void* dicClass) {
|
|
||||||
dic_klass = dicClass;
|
|
||||||
initDict(dict);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Add(KT key, VT value) {
|
|
||||||
dic_Add(dict, key, value, Add_method);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ContainsKey(KT key) {
|
|
||||||
return dic_containsKey(dict, key, ContainsKey_method);
|
|
||||||
}
|
|
||||||
|
|
||||||
VT get_Item(KT key) {
|
|
||||||
return dic_get_Item(dict, key, get_Item_method);
|
|
||||||
}
|
|
||||||
|
|
||||||
VT operator[] (KT key) {
|
|
||||||
return get_Item(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* dict;
|
|
||||||
void* dic_klass;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initDict(void* dict) {
|
|
||||||
// dic_klass = dicClass;
|
|
||||||
this->dict = dict;
|
|
||||||
|
|
||||||
get_Item_method = il2cpp_class_get_method_from_name(dic_klass, "get_Item", 1);
|
|
||||||
Add_method = il2cpp_class_get_method_from_name(dic_klass, "Add", 2);
|
|
||||||
ContainsKey_method = il2cpp_class_get_method_from_name(dic_klass, "ContainsKey", 1);
|
|
||||||
|
|
||||||
dic_get_Item = (dic_get_Item_t)get_Item_method->methodPointer;
|
|
||||||
dic_Add = (dic_Add_t)Add_method->methodPointer;
|
|
||||||
dic_containsKey = (dic_containsKey_t)ContainsKey_method->methodPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef VT(*dic_get_Item_t)(void*, KT, void* mtd);
|
|
||||||
typedef VT(*dic_Add_t)(void*, KT, VT, void* mtd);
|
|
||||||
typedef VT(*dic_containsKey_t)(void*, KT, void* mtd);
|
|
||||||
|
|
||||||
CSDictEditor();
|
|
||||||
MethodInfo* get_Item_method;
|
|
||||||
MethodInfo* Add_method;
|
|
||||||
MethodInfo* ContainsKey_method;
|
|
||||||
dic_get_Item_t dic_get_Item;
|
|
||||||
dic_Add_t dic_Add;
|
|
||||||
dic_containsKey_t dic_containsKey;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
#include "BaseDefine.h"
|
#include "BaseDefine.h"
|
||||||
#include "string_parser/StringParser.hpp"
|
#include "string_parser/StringParser.hpp"
|
||||||
|
|
||||||
// #include "cpprest/details/http_helpers.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace GakumasLocal::Local {
|
namespace GakumasLocal::Local {
|
||||||
std::unordered_map<std::string, std::string> i18nData{};
|
std::unordered_map<std::string, std::string> i18nData{};
|
||||||
|
@ -92,7 +90,7 @@ namespace GakumasLocal::Local {
|
||||||
}
|
}
|
||||||
std::ifstream file(filePath);
|
std::ifstream file(filePath);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
Log::ErrorFmt("Load %s failed.\n", filePath.string().c_str());
|
Log::ErrorFmt("Load %s failed.\n", filePath.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
@ -114,7 +112,7 @@ namespace GakumasLocal::Local {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
Log::ErrorFmt("Load %s failed: %s\n", filePath.string().c_str(), e.what());
|
Log::ErrorFmt("Load %s failed: %s\n", filePath.c_str(), e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +249,7 @@ namespace GakumasLocal::Local {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetSplitTagsTranslation(const std::string& origText, std::string* newText, std::vector<std::string>& unTransResultRet) {
|
bool GetSplitTagsTranslation(const std::string& origText, std::string* newText, std::vector<std::string>& unTransResultRet) {
|
||||||
if (!origText.contains('<')) return false;
|
if (!origText.contains(L'<')) return false;
|
||||||
const auto splitResult = SplitByTags(origText);
|
const auto splitResult = SplitByTags(origText);
|
||||||
if (splitResult.empty()) return false;
|
if (splitResult.empty()) return false;
|
||||||
|
|
||||||
|
@ -291,18 +289,10 @@ namespace GakumasLocal::Local {
|
||||||
|
|
||||||
std::u16string currentWaitingReplaceText;
|
std::u16string currentWaitingReplaceText;
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
#define checkCurrentWaitingReplaceTextAndClear() \
|
|
||||||
if (!currentWaitingReplaceText.empty()) { \
|
|
||||||
auto trimmed = trim(Misc::ToUTF8(currentWaitingReplaceText)); \
|
|
||||||
waitingReplaceTexts.push_back(trimmed); \
|
|
||||||
currentWaitingReplaceText.clear(); }
|
|
||||||
#else
|
|
||||||
#define checkCurrentWaitingReplaceTextAndClear() \
|
#define checkCurrentWaitingReplaceTextAndClear() \
|
||||||
if (!currentWaitingReplaceText.empty()) { \
|
if (!currentWaitingReplaceText.empty()) { \
|
||||||
waitingReplaceTexts.push_back(Misc::ToUTF8(currentWaitingReplaceText)); \
|
waitingReplaceTexts.push_back(Misc::ToUTF8(currentWaitingReplaceText)); \
|
||||||
currentWaitingReplaceText.clear(); }
|
currentWaitingReplaceText.clear(); }
|
||||||
#endif
|
|
||||||
|
|
||||||
for (char16_t currChar : origText) {
|
for (char16_t currChar : origText) {
|
||||||
if (currChar == u'<') {
|
if (currChar == u'<') {
|
||||||
|
@ -457,7 +447,7 @@ namespace GakumasLocal::Local {
|
||||||
const auto targetFilePath = basePath / "local-files" / "resource" / name;
|
const auto targetFilePath = basePath / "local-files" / "resource" / name;
|
||||||
// Log::DebugFmt("GetResourceText: %s", targetFilePath.c_str());
|
// Log::DebugFmt("GetResourceText: %s", targetFilePath.c_str());
|
||||||
if (exists(targetFilePath)) {
|
if (exists(targetFilePath)) {
|
||||||
auto readStr = readFileToString(targetFilePath.string());
|
auto readStr = readFileToString(targetFilePath);
|
||||||
*ret = readStr;
|
*ret = readStr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Misc.hpp"
|
#include <android/log.h>
|
||||||
|
#include <Misc.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <cstdarg>
|
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
extern JavaVM* g_javaVM;
|
extern JavaVM* g_javaVM;
|
||||||
extern jclass g_gakumasHookMainClass;
|
extern jclass g_gakumasHookMainClass;
|
||||||
extern jmethodID showToastMethodId;
|
extern jmethodID showToastMethodId;
|
||||||
#endif // GKMS_WINDOWS
|
|
||||||
|
|
||||||
|
|
||||||
#define GetParamStringResult(name)\
|
#define GetParamStringResult(name)\
|
||||||
va_list args;\
|
va_list args;\
|
||||||
|
@ -80,7 +75,6 @@ namespace GakumasLocal::Log {
|
||||||
__android_log_write(prio, "GakumasLog", result.c_str());
|
__android_log_write(prio, "GakumasLog", result.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void ShowToastJNI(const char* text) {
|
void ShowToastJNI(const char* text) {
|
||||||
DebugFmt("Toast: %s", text);
|
DebugFmt("Toast: %s", text);
|
||||||
|
|
||||||
|
@ -105,19 +99,15 @@ namespace GakumasLocal::Log {
|
||||||
|
|
||||||
g_javaVM->DetachCurrentThread();
|
g_javaVM->DetachCurrentThread();
|
||||||
}).detach();
|
}).detach();
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
|
||||||
void ShowToast(const std::string& text) {
|
void ShowToast(const std::string& text) {
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
showingToasts.push(text);
|
showingToasts.push(text);
|
||||||
#else
|
|
||||||
InfoFmt("Toast: %s", text.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowToast(const char* text) {
|
void ShowToast(const char* text) {
|
||||||
// DebugFmt("Toast: %s", text);
|
DebugFmt("Toast: %s", text);
|
||||||
return ShowToast(std::string(text));
|
return ShowToast(std::string(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +125,6 @@ namespace GakumasLocal::Log {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
void ToastLoop(JNIEnv *env, jclass clazz) {
|
void ToastLoop(JNIEnv *env, jclass clazz) {
|
||||||
const auto toastString = GetQueuedToast();
|
const auto toastString = GetQueuedToast();
|
||||||
if (toastString.empty()) return;
|
if (toastString.empty()) return;
|
||||||
|
@ -151,6 +140,4 @@ namespace GakumasLocal::Log {
|
||||||
_showToastMethodId = env->GetStaticMethodID(clazz, "showToast", "(Ljava/lang/String;)V");
|
_showToastMethodId = env->GetStaticMethodID(clazz, "showToast", "(Ljava/lang/String;)V");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
#ifndef GAKUMAS_LOCALIFY_LOG_H
|
#ifndef GAKUMAS_LOCALIFY_LOG_H
|
||||||
#define GAKUMAS_LOCALIFY_LOG_H
|
#define GAKUMAS_LOCALIFY_LOG_H
|
||||||
|
|
||||||
#include "../platformDefine.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace GakumasLocal::Log {
|
namespace GakumasLocal::Log {
|
||||||
std::string StringFormat(const char* fmt, ...);
|
std::string StringFormat(const char* fmt, ...);
|
||||||
|
@ -24,9 +18,7 @@ namespace GakumasLocal::Log {
|
||||||
void ShowToast(const char* text);
|
void ShowToast(const char* text);
|
||||||
void ShowToastFmt(const char* fmt, ...);
|
void ShowToastFmt(const char* fmt, ...);
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
void ToastLoop(JNIEnv *env, jclass clazz);
|
void ToastLoop(JNIEnv *env, jclass clazz);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //GAKUMAS_LOCALIFY_LOG_H
|
#endif //GAKUMAS_LOCALIFY_LOG_H
|
||||||
|
|
|
@ -542,7 +542,6 @@ namespace GakumasLocal::MasterLocal {
|
||||||
}
|
}
|
||||||
if (!isFailed) break;
|
if (!isFailed) break;
|
||||||
NextLoop:
|
NextLoop:
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFailed) return false;
|
if (isFailed) return false;
|
||||||
|
@ -694,7 +693,6 @@ namespace GakumasLocal::MasterLocal {
|
||||||
} break;
|
} break;
|
||||||
case JsonValueType::JVT_String: {
|
case JsonValueType::JVT_String: {
|
||||||
auto readValue = fc.ReadStringField(mainPk);
|
auto readValue = fc.ReadStringField(mainPk);
|
||||||
if (!readValue) return;
|
|
||||||
baseDataKey.append(readValue->ToString());
|
baseDataKey.append(readValue->ToString());
|
||||||
baseDataKey.push_back('|');
|
baseDataKey.push_back('|');
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -0,0 +1,786 @@
|
||||||
|
#include "MasterLocal.h"
|
||||||
|
#include "Local.h"
|
||||||
|
#include "Il2cppUtils.hpp"
|
||||||
|
#include "config/Config.hpp"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
#include <regex>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace GakumasLocal::MasterLocal {
|
||||||
|
using Il2cppString = UnityResolve::UnityType::String;
|
||||||
|
|
||||||
|
enum class JsonValueType {
|
||||||
|
JVT_String,
|
||||||
|
JVT_Int,
|
||||||
|
JVT_Object,
|
||||||
|
JVT_ArrayObject,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PKItem {
|
||||||
|
std::string topLevel;
|
||||||
|
std::string subField;
|
||||||
|
JsonValueType topLevelType;
|
||||||
|
JsonValueType subFieldType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TableInfo {
|
||||||
|
std::vector<PKItem> pkItems;
|
||||||
|
std::unordered_map<std::string, nlohmann::json> dataMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, TableInfo> g_loadedData;
|
||||||
|
static std::unordered_map<std::string, Il2cppUtils::MethodInfo*> fieldSetCache;
|
||||||
|
static std::unordered_map<std::string, Il2cppUtils::MethodInfo*> fieldGetCache;
|
||||||
|
|
||||||
|
class FieldController {
|
||||||
|
void* self;
|
||||||
|
std::string self_klass_name;
|
||||||
|
|
||||||
|
static std::string capitalizeFirstLetter(const std::string& input) {
|
||||||
|
if (input.empty()) return input;
|
||||||
|
std::string result = input;
|
||||||
|
result[0] = static_cast<char>(std::toupper(result[0]));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Il2cppUtils::MethodInfo* GetGetSetMethodFromCache(const std::string& fieldName, int argsCount,
|
||||||
|
std::unordered_map<std::string, Il2cppUtils::MethodInfo*>& fromCache, const std::string& prefix = "set_") {
|
||||||
|
const std::string methodName = prefix + capitalizeFirstLetter(fieldName);
|
||||||
|
const std::string searchName = self_klass_name + "." + methodName;
|
||||||
|
|
||||||
|
if (auto it = fromCache.find(searchName); it != fromCache.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
auto set_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(
|
||||||
|
self_klass,
|
||||||
|
methodName.c_str(),
|
||||||
|
argsCount
|
||||||
|
);
|
||||||
|
fromCache.emplace(searchName, set_mtd);
|
||||||
|
return set_mtd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Il2cppUtils::Il2CppClassHead* self_klass;
|
||||||
|
|
||||||
|
explicit FieldController(void* from) {
|
||||||
|
self = from;
|
||||||
|
self_klass = Il2cppUtils::get_class_from_instance(self);
|
||||||
|
if (self_klass) {
|
||||||
|
self_klass_name = self_klass->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T ReadField(const std::string& fieldName) {
|
||||||
|
auto get_mtd = GetGetSetMethodFromCache(fieldName, 0, fieldGetCache, "get_");
|
||||||
|
if (get_mtd) {
|
||||||
|
return reinterpret_cast<T (*)(void*, void*)>(get_mtd->methodPointer)(self, get_mtd);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto field = UnityResolve::Invoke<Il2cppUtils::FieldInfo*>(
|
||||||
|
"il2cpp_class_get_field_from_name",
|
||||||
|
self_klass,
|
||||||
|
(fieldName + '_').c_str()
|
||||||
|
);
|
||||||
|
if (!field) {
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
return Il2cppUtils::ClassGetFieldValue<T>(self, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void SetField(const std::string& fieldName, T value) {
|
||||||
|
auto set_mtd = GetGetSetMethodFromCache(fieldName, 1, fieldSetCache, "set_");
|
||||||
|
if (set_mtd) {
|
||||||
|
reinterpret_cast<void (*)(void*, T, void*)>(
|
||||||
|
set_mtd->methodPointer
|
||||||
|
)(self, value, set_mtd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto field = UnityResolve::Invoke<Il2cppUtils::FieldInfo*>(
|
||||||
|
"il2cpp_class_get_field_from_name",
|
||||||
|
self_klass,
|
||||||
|
(fieldName + '_').c_str()
|
||||||
|
);
|
||||||
|
if (!field) return;
|
||||||
|
Il2cppUtils::ClassSetFieldValue(self, field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadIntField(const std::string& fieldName) {
|
||||||
|
return ReadField<int>(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Il2cppString* ReadStringField(const std::string& fieldName) {
|
||||||
|
auto get_mtd = GetGetSetMethodFromCache(fieldName, 0, fieldGetCache, "get_");
|
||||||
|
if (!get_mtd) {
|
||||||
|
return ReadField<Il2cppString*>(fieldName);
|
||||||
|
}
|
||||||
|
auto returnClass = UnityResolve::Invoke<Il2cppUtils::Il2CppClassHead*>(
|
||||||
|
"il2cpp_class_from_type",
|
||||||
|
UnityResolve::Invoke<void*>("il2cpp_method_get_return_type", get_mtd)
|
||||||
|
);
|
||||||
|
if (!returnClass) {
|
||||||
|
return reinterpret_cast<Il2cppString* (*)(void*, void*)>(
|
||||||
|
get_mtd->methodPointer
|
||||||
|
)(self, get_mtd);
|
||||||
|
}
|
||||||
|
auto isEnum = UnityResolve::Invoke<bool>("il2cpp_class_is_enum", returnClass);
|
||||||
|
if (!isEnum) {
|
||||||
|
return reinterpret_cast<Il2cppString* (*)(void*, void*)>(
|
||||||
|
get_mtd->methodPointer
|
||||||
|
)(self, get_mtd);
|
||||||
|
}
|
||||||
|
auto enumMap = Il2cppUtils::EnumToValueMap(returnClass, true);
|
||||||
|
auto enumValue = reinterpret_cast<int (*)(void*, void*)>(
|
||||||
|
get_mtd->methodPointer
|
||||||
|
)(self, get_mtd);
|
||||||
|
if (auto it = enumMap.find(enumValue); it != enumMap.end()) {
|
||||||
|
return Il2cppString::New(it->second);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStringField(const std::string& fieldName, const std::string& value) {
|
||||||
|
auto newString = Il2cppString::New(value);
|
||||||
|
SetField(fieldName, newString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStringListField(const std::string& fieldName, const std::vector<std::string>& data) {
|
||||||
|
static auto List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
|
||||||
|
"System.Collections.Generic.List`1[System.String]"
|
||||||
|
);
|
||||||
|
static auto List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(
|
||||||
|
List_String_klass, ".ctor", 0
|
||||||
|
);
|
||||||
|
static auto List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(
|
||||||
|
List_String_ctor_mtd->methodPointer
|
||||||
|
);
|
||||||
|
|
||||||
|
auto newList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
|
||||||
|
List_String_ctor(newList, List_String_ctor_mtd);
|
||||||
|
|
||||||
|
Il2cppUtils::Tools::CSListEditor<Il2cppString*> newListEditor(newList);
|
||||||
|
for (auto& s : data) {
|
||||||
|
newListEditor.Add(Il2cppString::New(s));
|
||||||
|
}
|
||||||
|
SetField(fieldName, newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ReadObjectField(const std::string& fieldName) {
|
||||||
|
return ReadField<void*>(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ReadObjectListField(const std::string& fieldName) {
|
||||||
|
return ReadField<void*>(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FieldController CreateSubFieldController(void* subObj) {
|
||||||
|
return FieldController(subObj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================
|
||||||
|
// 帮助函数:判断 JSON 字段类型
|
||||||
|
//==============================================================
|
||||||
|
JsonValueType checkJsonValueType(const nlohmann::json& j) {
|
||||||
|
if (j.is_string()) return JsonValueType::JVT_String;
|
||||||
|
if (j.is_number_integer()) return JsonValueType::JVT_Int;
|
||||||
|
if (j.is_object()) return JsonValueType::JVT_Object;
|
||||||
|
if (j.is_array()) {
|
||||||
|
if (!j.empty() && j.begin()->is_object()) {
|
||||||
|
return JsonValueType::JVT_ArrayObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JsonValueType::JVT_String;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================
|
||||||
|
// 解析 pkName => PKItem
|
||||||
|
//==============================================================
|
||||||
|
PKItem parsePK(const nlohmann::json& row, const std::string& pkStr) {
|
||||||
|
auto pos = pkStr.find('.');
|
||||||
|
PKItem item;
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
item.topLevel = pkStr;
|
||||||
|
item.subField = "";
|
||||||
|
if (!row.contains(pkStr)) {
|
||||||
|
item.topLevelType = JsonValueType::JVT_String;
|
||||||
|
} else {
|
||||||
|
item.topLevelType = checkJsonValueType(row[pkStr]);
|
||||||
|
}
|
||||||
|
item.subFieldType = JsonValueType::JVT_String;
|
||||||
|
} else {
|
||||||
|
item.topLevel = pkStr.substr(0, pos);
|
||||||
|
item.subField = pkStr.substr(pos + 1);
|
||||||
|
if (!row.contains(item.topLevel)) {
|
||||||
|
item.topLevelType = JsonValueType::JVT_Object;
|
||||||
|
} else {
|
||||||
|
auto& jTop = row[item.topLevel];
|
||||||
|
auto t = checkJsonValueType(jTop);
|
||||||
|
if (t == JsonValueType::JVT_Object) {
|
||||||
|
item.topLevelType = JsonValueType::JVT_Object;
|
||||||
|
} else if (t == JsonValueType::JVT_ArrayObject) {
|
||||||
|
item.topLevelType = JsonValueType::JVT_ArrayObject;
|
||||||
|
} else {
|
||||||
|
item.topLevelType = JsonValueType::JVT_Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.subFieldType = JsonValueType::JVT_String;
|
||||||
|
if (row.contains(item.topLevel)) {
|
||||||
|
auto& jTop = row[item.topLevel];
|
||||||
|
if (jTop.is_object()) {
|
||||||
|
if (jTop.contains(item.subField)) {
|
||||||
|
item.subFieldType = checkJsonValueType(jTop[item.subField]);
|
||||||
|
}
|
||||||
|
} else if (jTop.is_array() && !jTop.empty()) {
|
||||||
|
auto& firstElem = *jTop.begin();
|
||||||
|
if (firstElem.is_object() && firstElem.contains(item.subField)) {
|
||||||
|
item.subFieldType = checkJsonValueType(firstElem[item.subField]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PKItem> parseAllPKItems(const nlohmann::json& row, const std::vector<std::string>& pkNames) {
|
||||||
|
std::vector<PKItem> result;
|
||||||
|
result.reserve(pkNames.size());
|
||||||
|
for (auto& pk : pkNames) {
|
||||||
|
auto item = parsePK(row, pk);
|
||||||
|
result.push_back(item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================
|
||||||
|
// 将 jval 拼接到 uniqueKey
|
||||||
|
//==============================================================
|
||||||
|
inline void appendPKValue(std::string& uniqueKey, const nlohmann::json& jval, bool& isFirst) {
|
||||||
|
if (!isFirst) uniqueKey += "|";
|
||||||
|
if (jval.is_string()) {
|
||||||
|
uniqueKey += jval.get<std::string>();
|
||||||
|
} else if (jval.is_number_integer()) {
|
||||||
|
uniqueKey += std::to_string(jval.get<int>());
|
||||||
|
}
|
||||||
|
isFirst = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================
|
||||||
|
// 读取文件 => 解析 => 加载 dataMap
|
||||||
|
//==============================================================
|
||||||
|
std::string ReadFileToString(const std::filesystem::path& path) {
|
||||||
|
std::ifstream ifs(path, std::ios::binary);
|
||||||
|
if (!ifs) return {};
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << ifs.rdbuf();
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断 row 里,与 pkNames/主键相关的字段(若是数组)是否为空
|
||||||
|
bool hasEmptyArrayForPk(const nlohmann::json& row, const std::vector<std::string>& pkNames) {
|
||||||
|
// 如果行为空,直接返回 false(或 true,看你需求)
|
||||||
|
if (row.is_null() || !row.is_object()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& pk : pkNames) {
|
||||||
|
// 先看该行是否包含此顶层字段
|
||||||
|
auto dotPos = pk.find('.');
|
||||||
|
std::string topLevel = (dotPos == std::string::npos) ? pk : pk.substr(0, dotPos);
|
||||||
|
|
||||||
|
if (!row.contains(topLevel)) {
|
||||||
|
// 没有这个字段就略过
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 pk 中含 '.', 说明可能是 array<object> 类型
|
||||||
|
// 这里仅检查 "顶层字段是否是空数组"
|
||||||
|
// 若需要更深层的判断,需扩展
|
||||||
|
const auto& jTop = row[topLevel];
|
||||||
|
if (jTop.is_array()) {
|
||||||
|
// 一旦发现是空数组,就返回 true
|
||||||
|
if (jTop.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 pkItems 构造一个 skipSet,里面包含 "topLevel" 和 "topLevel.subField"
|
||||||
|
// 或者只包含 subField, 看你具体需求
|
||||||
|
static std::unordered_set<std::string> buildSkipFields(const std::vector<PKItem>& pkItems) {
|
||||||
|
std::unordered_set<std::string> skipSet;
|
||||||
|
for (auto& pk : pkItems) {
|
||||||
|
if (pk.subField.empty()) {
|
||||||
|
// e.g. "id"
|
||||||
|
skipSet.insert(pk.topLevel);
|
||||||
|
} else {
|
||||||
|
// e.g. "descriptions.type" => 既要跳过 "type" 又要跳过 "descriptions"?
|
||||||
|
// 具体看你业务需要:
|
||||||
|
// skipSet.insert(pk.topLevel); // 可能不需要
|
||||||
|
skipSet.insert(pk.subField); // "type"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return skipSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归枚举 JSON 值里的字符串并插入到 localSet
|
||||||
|
void collectLocalizableStrings_impl(
|
||||||
|
const nlohmann::json& node,
|
||||||
|
const std::unordered_set<std::string>& skipSet,
|
||||||
|
std::unordered_set<std::string>& localSet
|
||||||
|
) {
|
||||||
|
if (node.is_string()) {
|
||||||
|
// node本身就是string => 这时无法知道key名,但一般情况下我们是key->value对?
|
||||||
|
// 这里仅当外层调用传入一个object时可取到key
|
||||||
|
// 先写成仅object字段时处理
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (node.is_object()) {
|
||||||
|
// 枚举键值
|
||||||
|
for (auto it = node.begin(); it != node.end(); ++it) {
|
||||||
|
auto& key = it.key();
|
||||||
|
auto& val = it.value();
|
||||||
|
// 如果key在skipSet里,则跳过
|
||||||
|
if (skipSet.count(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 否则看val的类型
|
||||||
|
if (val.is_string()) {
|
||||||
|
// 收集
|
||||||
|
localSet.insert(val.get<std::string>());
|
||||||
|
// Log::DebugFmt("localSet.insert: %s", val.get<std::string>().c_str());
|
||||||
|
} else if (val.is_object() || val.is_array()) {
|
||||||
|
// 递归下去
|
||||||
|
collectLocalizableStrings_impl(val, skipSet, localSet);
|
||||||
|
}
|
||||||
|
// 其他类型 (int/bool/float) 不做本地化
|
||||||
|
}
|
||||||
|
} else if (node.is_array()) {
|
||||||
|
// 枚举数组元素
|
||||||
|
for (auto& element : node) {
|
||||||
|
if (element.is_string()) {
|
||||||
|
localSet.insert(element.get<std::string>());
|
||||||
|
// Log::DebugFmt("localSet.insert: %s", element.get<std::string>().c_str());
|
||||||
|
} else if (element.is_object() || element.is_array()) {
|
||||||
|
collectLocalizableStrings_impl(element, skipSet, localSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对外接口:根据 row + pkItems,把所有非主键字段的字符串插到 localSet
|
||||||
|
void collectLocalizableStrings(const nlohmann::json& row, const std::vector<PKItem>& pkItems, std::unordered_set<std::string>& localSet) {
|
||||||
|
// 先构建一个 skipSet,表示"主键字段"要跳过
|
||||||
|
auto skipSet = buildSkipFields(pkItems);
|
||||||
|
// 然后递归遍历
|
||||||
|
collectLocalizableStrings_impl(row, skipSet, localSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LoadData() {
|
||||||
|
g_loadedData.clear();
|
||||||
|
static auto masterDir = Local::GetBasePath() / "local-files" / "masterTrans";
|
||||||
|
if (!std::filesystem::is_directory(masterDir)) {
|
||||||
|
Log::ErrorFmt("LoadData: not found: %s", masterDir.string().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFirstIteration = true;
|
||||||
|
for (auto& p : std::filesystem::directory_iterator(masterDir)) {
|
||||||
|
if (isFirstIteration) {
|
||||||
|
auto totalFileCount = std::distance(
|
||||||
|
std::filesystem::directory_iterator(masterDir),
|
||||||
|
std::filesystem::directory_iterator{}
|
||||||
|
);
|
||||||
|
UnityResolveProgress::classProgress.total = totalFileCount <= 0 ? 1 : totalFileCount;
|
||||||
|
isFirstIteration = false;
|
||||||
|
}
|
||||||
|
UnityResolveProgress::classProgress.current++;
|
||||||
|
|
||||||
|
if (!p.is_regular_file()) continue;
|
||||||
|
const auto& path = p.path();
|
||||||
|
if (path.extension() != ".json") continue;
|
||||||
|
|
||||||
|
std::string tableName = path.stem().string();
|
||||||
|
auto fileContent = ReadFileToString(path);
|
||||||
|
if (fileContent.empty()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto j = nlohmann::json::parse(fileContent);
|
||||||
|
if (!j.contains("rules") || !j["rules"].contains("primaryKeys")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::vector<std::string> pkNames;
|
||||||
|
for (auto& x : j["rules"]["primaryKeys"]) {
|
||||||
|
pkNames.push_back(x.get<std::string>());
|
||||||
|
}
|
||||||
|
if (!j.contains("data") || !j["data"].is_array()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TableInfo tableInfo;
|
||||||
|
if (!j["data"].empty()) {
|
||||||
|
for (auto & currRow : j["data"]) {
|
||||||
|
if (!hasEmptyArrayForPk(currRow, pkNames)) {
|
||||||
|
tableInfo.pkItems = parseAllPKItems(currRow, pkNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// auto& firstRow = j["data"][0];
|
||||||
|
// tableInfo.pkItems = parseAllPKItems(firstRow, pkNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================
|
||||||
|
// 构建 dataMap, 支持 array + index
|
||||||
|
//==============================================================
|
||||||
|
for (auto& row : j["data"]) {
|
||||||
|
std::string uniqueKey;
|
||||||
|
bool firstKey = true;
|
||||||
|
bool failed = false;
|
||||||
|
|
||||||
|
for (auto& pkItem : tableInfo.pkItems) {
|
||||||
|
if (!row.contains(pkItem.topLevel)) {
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto& jTop = row[pkItem.topLevel];
|
||||||
|
|
||||||
|
// 无子字段 => 直接处理
|
||||||
|
if (pkItem.subField.empty()) {
|
||||||
|
if (jTop.is_string() || jTop.is_number_integer()) {
|
||||||
|
appendPKValue(uniqueKey, jTop, firstKey);
|
||||||
|
} else {
|
||||||
|
failed = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 若是 array<object> + subField,就遍历数组每个下标 + subField => 并将 index + value 拼进 uniqueKey
|
||||||
|
if (pkItem.topLevelType == JsonValueType::JVT_ArrayObject) {
|
||||||
|
if (!jTop.is_array()) { failed = true; break; }
|
||||||
|
// 遍历数组所有元素
|
||||||
|
for (int i = 0; i < (int)jTop.size(); i++) {
|
||||||
|
auto& elem = jTop[i];
|
||||||
|
if (!elem.is_object()) { failed = true; break; }
|
||||||
|
if (!elem.contains(pkItem.subField)) { failed = true; break; }
|
||||||
|
auto& subVal = elem[pkItem.subField];
|
||||||
|
// 只支持 string/int
|
||||||
|
if (!subVal.is_string() && !subVal.is_number_integer()) {
|
||||||
|
failed = true; break;
|
||||||
|
}
|
||||||
|
// 拼上索引 + 值
|
||||||
|
// e.g. "|0:xxx|1:yyy"...
|
||||||
|
if (!firstKey) uniqueKey += "|";
|
||||||
|
uniqueKey += std::to_string(i);
|
||||||
|
uniqueKey += ":";
|
||||||
|
if (subVal.is_string()) {
|
||||||
|
uniqueKey += subVal.get<std::string>();
|
||||||
|
} else {
|
||||||
|
uniqueKey += std::to_string(subVal.get<int>());
|
||||||
|
}
|
||||||
|
firstKey = false;
|
||||||
|
}
|
||||||
|
if (failed) break;
|
||||||
|
}
|
||||||
|
else if (pkItem.topLevelType == JsonValueType::JVT_Object) {
|
||||||
|
if (!jTop.is_object()) {
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!jTop.contains(pkItem.subField)) { failed = true; break; }
|
||||||
|
auto& subVal = jTop[pkItem.subField];
|
||||||
|
if (subVal.is_string() || subVal.is_number_integer()) {
|
||||||
|
appendPKValue(uniqueKey, subVal, firstKey);
|
||||||
|
} else {
|
||||||
|
failed = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failed) break;
|
||||||
|
}
|
||||||
|
if (!failed && !uniqueKey.empty()) {
|
||||||
|
tableInfo.dataMap[uniqueKey] = row;
|
||||||
|
collectLocalizableStrings(row, tableInfo.pkItems, Local::translatedText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log::DebugFmt("Load table: %s, %d, %d", tableName.c_str(), tableInfo.pkItems.size(), tableInfo.dataMap.size());
|
||||||
|
g_loadedData[tableName] = std::move(tableInfo);
|
||||||
|
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
Log::ErrorFmt("MasterLocal::LoadData: parse error in '%s': %s",
|
||||||
|
path.string().c_str(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================
|
||||||
|
// 在 C# 对象里,根据 pkItems 构造 uniqueKey
|
||||||
|
// 同样要支持 array<object> + index
|
||||||
|
//==============================================================
|
||||||
|
bool buildUniqueKeyFromCSharp(FieldController& fc, const TableInfo& tableInfo, std::string& outKey) {
|
||||||
|
outKey.clear();
|
||||||
|
bool firstKey = true;
|
||||||
|
|
||||||
|
for (auto& pk : tableInfo.pkItems) {
|
||||||
|
if (pk.subField.empty()) {
|
||||||
|
// 顶层无子字段
|
||||||
|
if (pk.topLevelType == JsonValueType::JVT_String) {
|
||||||
|
auto sptr = fc.ReadStringField(pk.topLevel);
|
||||||
|
if (!sptr) return false;
|
||||||
|
if (!firstKey) outKey += "|";
|
||||||
|
outKey += sptr->ToString();
|
||||||
|
firstKey = false;
|
||||||
|
} else if (pk.topLevelType == JsonValueType::JVT_Int) {
|
||||||
|
int ival = fc.ReadIntField(pk.topLevel);
|
||||||
|
if (!firstKey) outKey += "|";
|
||||||
|
outKey += std::to_string(ival);
|
||||||
|
firstKey = false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// subField
|
||||||
|
if (pk.topLevelType == JsonValueType::JVT_ArrayObject) {
|
||||||
|
// => c# 里 readObjectListField
|
||||||
|
void* listPtr = fc.ReadObjectListField(pk.topLevel);
|
||||||
|
if (!listPtr) return false;
|
||||||
|
Il2cppUtils::Tools::CSListEditor<void*> listEdit(listPtr);
|
||||||
|
int arrCount = listEdit.get_Count();
|
||||||
|
|
||||||
|
// 遍历每个 index
|
||||||
|
for (int i = 0; i < arrCount; i++) {
|
||||||
|
auto elemPtr = listEdit.get_Item(i);
|
||||||
|
if (!elemPtr) return false;
|
||||||
|
FieldController subFC = FieldController::CreateSubFieldController(elemPtr);
|
||||||
|
|
||||||
|
// 只支持 string/int
|
||||||
|
if (pk.subFieldType == JsonValueType::JVT_String) {
|
||||||
|
auto sptr = subFC.ReadStringField(pk.subField);
|
||||||
|
if (!sptr) return false;
|
||||||
|
if (!firstKey) outKey += "|";
|
||||||
|
// "|i:xxx"
|
||||||
|
outKey += std::to_string(i);
|
||||||
|
outKey += ":";
|
||||||
|
outKey += sptr->ToString();
|
||||||
|
firstKey = false;
|
||||||
|
}
|
||||||
|
else if (pk.subFieldType == JsonValueType::JVT_Int) {
|
||||||
|
int ival = subFC.ReadIntField(pk.subField);
|
||||||
|
if (!firstKey) outKey += "|";
|
||||||
|
outKey += std::to_string(i);
|
||||||
|
outKey += ":";
|
||||||
|
outKey += std::to_string(ival);
|
||||||
|
firstKey = false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pk.topLevelType == JsonValueType::JVT_Object) {
|
||||||
|
void* subObj = fc.ReadObjectField(pk.topLevel);
|
||||||
|
if (!subObj) return false;
|
||||||
|
FieldController subFC = FieldController::CreateSubFieldController(subObj);
|
||||||
|
|
||||||
|
if (pk.subFieldType == JsonValueType::JVT_String) {
|
||||||
|
auto sptr = subFC.ReadStringField(pk.subField);
|
||||||
|
if (!sptr) return false;
|
||||||
|
if (!firstKey) outKey += "|";
|
||||||
|
outKey += sptr->ToString();
|
||||||
|
firstKey = false;
|
||||||
|
}
|
||||||
|
else if (pk.subFieldType == JsonValueType::JVT_Int) {
|
||||||
|
int ival = subFC.ReadIntField(pk.subField);
|
||||||
|
if (!firstKey) outKey += "|";
|
||||||
|
outKey += std::to_string(ival);
|
||||||
|
firstKey = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !outKey.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 声明
|
||||||
|
void localizeJsonToCsharp(FieldController& fc, const nlohmann::json& jdata, const std::unordered_set<std::string>& skipKeySet);
|
||||||
|
void localizeArrayOfObject(FieldController& fc, const std::string& fieldName, const nlohmann::json& arrVal, const std::unordered_set<std::string>& skipKeySet);
|
||||||
|
void localizeObject(FieldController& fc, const std::string& fieldName, const nlohmann::json& objVal, const std::unordered_set<std::string>& skipKeySet);
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
// 对 array<object> 做一层递归 —— 需要带着 skipKeySet
|
||||||
|
//====================================================================
|
||||||
|
void localizeArrayOfObject(FieldController& fc, const std::string& fieldName, const nlohmann::json& arrVal, const std::unordered_set<std::string>& skipKeySet) {
|
||||||
|
void* listPtr = fc.ReadObjectListField(fieldName);
|
||||||
|
if (!listPtr) return;
|
||||||
|
Il2cppUtils::Tools::CSListEditor<void*> listEdit(listPtr);
|
||||||
|
int cmin = std::min<int>(listEdit.get_Count(), (int)arrVal.size());
|
||||||
|
for (int i = 0; i < cmin; i++) {
|
||||||
|
auto elemPtr = listEdit.get_Item(i);
|
||||||
|
if (!elemPtr) continue;
|
||||||
|
FieldController subFC = FieldController::CreateSubFieldController(elemPtr);
|
||||||
|
localizeJsonToCsharp(subFC, arrVal[i], skipKeySet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
// 对单个 object 做一层递归 —— 需要带着 skipKeySet
|
||||||
|
//====================================================================
|
||||||
|
void localizeObject(FieldController& fc, const std::string& fieldName, const nlohmann::json& objVal, const std::unordered_set<std::string>& skipKeySet) {
|
||||||
|
void* subObj = fc.ReadObjectField(fieldName);
|
||||||
|
if (!subObj) return;
|
||||||
|
FieldController subFC = FieldController::CreateSubFieldController(subObj);
|
||||||
|
localizeJsonToCsharp(subFC, objVal, skipKeySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
// 仅一层本地化: string, string[], object, object[],带 skipKeySet
|
||||||
|
//====================================================================
|
||||||
|
void localizeJsonToCsharp(FieldController& fc, const nlohmann::json& jdata, const std::unordered_set<std::string>& skipKeySet) {
|
||||||
|
if (!jdata.is_object()) return;
|
||||||
|
for (auto it = jdata.begin(); it != jdata.end(); ++it) {
|
||||||
|
const std::string& key = it.key();
|
||||||
|
// 如果 key 在 skipKeySet 里,则跳过本地化
|
||||||
|
if (skipKeySet.count(key)) {
|
||||||
|
// Debug输出可以留意一下
|
||||||
|
// Log::DebugFmt("skip field: %s", key.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& val = it.value();
|
||||||
|
if (val.is_string()) {
|
||||||
|
// 打印一下做验证
|
||||||
|
auto origStr = fc.ReadStringField(key);
|
||||||
|
auto newStr = val.get<std::string>();
|
||||||
|
if (origStr) {
|
||||||
|
std::string oldVal = origStr->ToString();
|
||||||
|
// Log::DebugFmt("SetStringField key: %s, oldVal: %s -> newVal: %s", key.c_str(), oldVal.c_str(), newStr.c_str());
|
||||||
|
if (((oldVal == "\n") || (oldVal == "\r\n")) && newStr.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fc.SetStringField(key, val.get<std::string>());
|
||||||
|
}
|
||||||
|
else if (val.is_array()) {
|
||||||
|
if (!val.empty() && val.begin()->is_string()) {
|
||||||
|
bool allStr = true;
|
||||||
|
std::vector<std::string> strArray;
|
||||||
|
for (auto& x : val) {
|
||||||
|
if (!x.is_string()) { allStr = false; break; }
|
||||||
|
strArray.push_back(x.get<std::string>());
|
||||||
|
}
|
||||||
|
if (allStr) {
|
||||||
|
// Log::DebugFmt("SetStringListField in %s, key: %s", fc.self_klass->name, key.c_str());
|
||||||
|
fc.SetStringListField(key, strArray);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// array<object>
|
||||||
|
if (!val.empty() && val.begin()->is_object()) {
|
||||||
|
localizeArrayOfObject(fc, key, val, skipKeySet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (val.is_object()) {
|
||||||
|
localizeObject(fc, key, val, skipKeySet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
// 真正处理单个C#对象
|
||||||
|
//====================================================================
|
||||||
|
void LocalizeMasterItem(FieldController& fc, const std::string& tableName) {
|
||||||
|
auto it = g_loadedData.find(tableName);
|
||||||
|
if (it == g_loadedData.end()) return;
|
||||||
|
// Log::DebugFmt("LocalizeMasterItem: %s", tableName.c_str());
|
||||||
|
auto& tableInfo = it->second;
|
||||||
|
if (tableInfo.dataMap.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string uniqueKey;
|
||||||
|
if (!buildUniqueKeyFromCSharp(fc, tableInfo, uniqueKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto itRow = tableInfo.dataMap.find(uniqueKey);
|
||||||
|
if (itRow == tableInfo.dataMap.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& rowData = itRow->second;
|
||||||
|
|
||||||
|
//=====================================================
|
||||||
|
// 把「有子字段」的 pkItem 也加入 skipKeySet,但用它的 `subField` 部分
|
||||||
|
//=====================================================
|
||||||
|
std::unordered_set<std::string> skipKeySet;
|
||||||
|
for (auto& pk : tableInfo.pkItems) {
|
||||||
|
if (pk.subField.empty()) {
|
||||||
|
// 若没有子字段,说明 topLevel 本身是主键
|
||||||
|
skipKeySet.insert(pk.topLevel);
|
||||||
|
} else {
|
||||||
|
// 如果有子字段,说明这个子字段才是 PK
|
||||||
|
// e.g. produceDescriptions.examEffectType => skipKeySet.insert("examEffectType");
|
||||||
|
skipKeySet.insert(pk.subField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 然后带着 skipKeySet 去做本地化
|
||||||
|
localizeJsonToCsharp(fc, rowData, skipKeySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalizeMasterTables(const std::string& tableName, UnityResolve::UnityType::List<void*>* result) {
|
||||||
|
if (!result) return;
|
||||||
|
Il2cppUtils::Tools::CSListEditor resultList(result);
|
||||||
|
if (resultList.get_Count() <= 0) return;
|
||||||
|
|
||||||
|
for (auto i : resultList) {
|
||||||
|
if (!i) continue;
|
||||||
|
FieldController fc(i);
|
||||||
|
LocalizeMasterItem(fc, tableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalizeMaster(const std::string& sql, UnityResolve::UnityType::List<void*>* result) {
|
||||||
|
static const std::regex tableNameRegex(R"(\bFROM\s+(?:`([^`]+)`|(\S+)))");
|
||||||
|
std::smatch match;
|
||||||
|
if (std::regex_search(sql, match, tableNameRegex)) {
|
||||||
|
std::string tableName = match[1].matched ? match[1].str() : match[2].str();
|
||||||
|
LocalizeMasterTables(tableName, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalizeMaster(const std::string& sql, void* result) {
|
||||||
|
if (!Config::useMasterTrans) return;
|
||||||
|
LocalizeMaster(sql, reinterpret_cast<UnityResolve::UnityType::List<void*>*>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalizeMaster(void* result, const std::string& tableName) {
|
||||||
|
if (!Config::useMasterTrans) return;
|
||||||
|
LocalizeMasterTables(tableName, reinterpret_cast<UnityResolve::UnityType::List<void*>*>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalizeMasterItem(void* item, const std::string& tableName) {
|
||||||
|
if (!Config::useMasterTrans) return;
|
||||||
|
FieldController fc(item);
|
||||||
|
LocalizeMasterItem(fc, tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GakumasLocal::MasterLocal
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef GAKUMAS_LOCALIFY_MASTERLOCAL_H
|
||||||
|
#define GAKUMAS_LOCALIFY_MASTERLOCAL_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace GakumasLocal::MasterLocal {
|
||||||
|
void LoadData();
|
||||||
|
void LocalizeMaster(const std::string& sql, void* result);
|
||||||
|
void LocalizeMaster(void* result, const std::string& tableName);
|
||||||
|
void LocalizeMasterItem(void* item, const std::string& tableName);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif //GAKUMAS_LOCALIFY_MASTERLOCAL_H
|
|
@ -2,36 +2,14 @@
|
||||||
|
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
#include <jni.h>
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
extern JavaVM* g_javaVM;
|
extern JavaVM* g_javaVM;
|
||||||
#else
|
|
||||||
#include "cpprest/details/http_helpers.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace GakumasLocal::Misc {
|
namespace GakumasLocal::Misc {
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
std::string ToUTF8(const std::wstring_view& str) {
|
|
||||||
return utility::conversions::to_utf8string(str.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::u16string ToUTF16(const std::string_view& str) {
|
|
||||||
std::string input(str);
|
|
||||||
std::wstring wstr = utility::conversions::utf8_to_utf16(input);
|
|
||||||
return std::u16string(wstr.begin(), wstr.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ToUTF8(const std::u16string_view& str) {
|
|
||||||
std::u16string u16(str);
|
|
||||||
std::wstring wstr(u16.begin(), u16.end());
|
|
||||||
return utility::conversions::utf16_to_utf8(wstr);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
std::u16string ToUTF16(const std::string_view& str) {
|
std::u16string ToUTF16(const std::string_view& str) {
|
||||||
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.from_bytes(str.data(), str.data() + str.size());
|
return utf16conv.from_bytes(str.data(), str.data() + str.size());
|
||||||
|
@ -41,9 +19,7 @@ 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());
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
JNIEnv* GetJNIEnv() {
|
JNIEnv* GetJNIEnv() {
|
||||||
if (!g_javaVM) return nullptr;
|
if (!g_javaVM) return nullptr;
|
||||||
JNIEnv* env = nullptr;
|
JNIEnv* env = nullptr;
|
||||||
|
@ -55,7 +31,6 @@ namespace GakumasLocal::Misc {
|
||||||
}
|
}
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
CSEnum::CSEnum(const std::string& name, const int value) {
|
CSEnum::CSEnum(const std::string& name, const int value) {
|
||||||
this->Add(name, value);
|
this->Add(name, value);
|
||||||
|
|
|
@ -2,16 +2,11 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <jni.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "../platformDefine.hpp"
|
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
#include <jni.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace GakumasLocal {
|
namespace GakumasLocal {
|
||||||
using OpaqueFunctionPointer = void (*)();
|
using OpaqueFunctionPointer = void (*)();
|
||||||
|
@ -19,13 +14,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);
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
std::string ToUTF8(const std::wstring_view& str);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
JNIEnv* GetJNIEnv();
|
JNIEnv* GetJNIEnv();
|
||||||
#endif
|
|
||||||
|
|
||||||
class CSEnum {
|
class CSEnum {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -4,13 +4,7 @@
|
||||||
#include "Misc.hpp"
|
#include "Misc.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "../platformDefine.hpp"
|
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#endif // !GKMS_WINDOWS
|
|
||||||
|
|
||||||
|
|
||||||
namespace GakumasLocal {
|
namespace GakumasLocal {
|
||||||
struct HookInstaller
|
struct HookInstaller
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
#include "baseCamera.hpp"
|
#include "baseCamera.hpp"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "../../platformDefine.hpp"
|
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
#include <corecrt_math_defines.h>
|
|
||||||
#endif // GKMS_WINDOWS
|
|
||||||
|
|
||||||
|
|
||||||
namespace BaseCamera {
|
namespace BaseCamera {
|
||||||
using Vector3_t = UnityResolve::UnityType::Vector3;
|
using Vector3_t = UnityResolve::UnityType::Vector3;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../../deps/UnityResolve/UnityResolve.hpp"
|
#include "../deps/UnityResolve/UnityResolve.hpp"
|
||||||
|
|
||||||
enum LonMoveHState {
|
enum LonMoveHState {
|
||||||
LonMoveLeftAndRight,
|
LonMoveLeftAndRight,
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
#include "baseCamera.hpp"
|
#include "baseCamera.hpp"
|
||||||
#include "camera.hpp"
|
#include "camera.hpp"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "../Misc.hpp"
|
#include "Misc.hpp"
|
||||||
#include "../BaseDefine.h"
|
#include "../BaseDefine.h"
|
||||||
#include "../../platformDefine.hpp"
|
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
|
||||||
#include <corecrt_math_defines.h>
|
|
||||||
#endif // GKMS_WINDOWS
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace GKCamera {
|
namespace GKCamera {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "baseCamera.hpp"
|
#include "baseCamera.hpp"
|
||||||
#include "../../deps/Joystick/JoystickEvent.h"
|
#include "Joystick/JoystickEvent.h"
|
||||||
|
|
||||||
namespace GKCamera {
|
namespace GKCamera {
|
||||||
enum class CameraMode {
|
enum class CameraMode {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
#include "../Log.h"
|
#include "../Log.h"
|
||||||
#include <thread>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
namespace GakumasLocal::Config {
|
namespace GakumasLocal::Config {
|
||||||
bool isConfigInit = false;
|
bool isConfigInit = false;
|
||||||
|
@ -54,8 +52,6 @@ namespace GakumasLocal::Config {
|
||||||
float bLimitZx = 1.0f;
|
float bLimitZx = 1.0f;
|
||||||
float bLimitZy = 1.0f;
|
float bLimitZy = 1.0f;
|
||||||
|
|
||||||
bool dmmUnlockSize = false;
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -104,74 +100,11 @@ namespace GakumasLocal::Config {
|
||||||
GetConfigItem(bLimitYy);
|
GetConfigItem(bLimitYy);
|
||||||
GetConfigItem(bLimitZx);
|
GetConfigItem(bLimitZx);
|
||||||
GetConfigItem(bLimitZy);
|
GetConfigItem(bLimitZy);
|
||||||
GetConfigItem(dmmUnlockSize);
|
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
Log::ErrorFmt("LoadConfig error: %s", e.what());
|
Log::ErrorFmt("LoadConfig error: %s", e.what());
|
||||||
}
|
}
|
||||||
isConfigInit = true;
|
isConfigInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveConfig(const std::string& configPath) {
|
|
||||||
try {
|
|
||||||
nlohmann::json config;
|
|
||||||
|
|
||||||
#define SetConfigItem(name) config[#name] = name
|
|
||||||
|
|
||||||
SetConfigItem(dbgMode);
|
|
||||||
SetConfigItem(enabled);
|
|
||||||
SetConfigItem(lazyInit);
|
|
||||||
SetConfigItem(replaceFont);
|
|
||||||
SetConfigItem(forceExportResource);
|
|
||||||
SetConfigItem(gameOrientation);
|
|
||||||
SetConfigItem(textTest);
|
|
||||||
SetConfigItem(useMasterTrans);
|
|
||||||
SetConfigItem(dumpText);
|
|
||||||
SetConfigItem(targetFrameRate);
|
|
||||||
SetConfigItem(enableFreeCamera);
|
|
||||||
SetConfigItem(unlockAllLive);
|
|
||||||
SetConfigItem(unlockAllLiveCostume);
|
|
||||||
SetConfigItem(enableLiveCustomeDress);
|
|
||||||
SetConfigItem(liveCustomeHeadId);
|
|
||||||
SetConfigItem(liveCustomeCostumeId);
|
|
||||||
SetConfigItem(loginAsIOS);
|
|
||||||
SetConfigItem(useCustomeGraphicSettings);
|
|
||||||
SetConfigItem(renderScale);
|
|
||||||
SetConfigItem(qualitySettingsLevel);
|
|
||||||
SetConfigItem(volumeIndex);
|
|
||||||
SetConfigItem(maxBufferPixel);
|
|
||||||
SetConfigItem(reflectionQualityLevel);
|
|
||||||
SetConfigItem(lodQualityLevel);
|
|
||||||
SetConfigItem(enableBreastParam);
|
|
||||||
SetConfigItem(bDamping);
|
|
||||||
SetConfigItem(bStiffness);
|
|
||||||
SetConfigItem(bSpring);
|
|
||||||
SetConfigItem(bPendulum);
|
|
||||||
SetConfigItem(bPendulumRange);
|
|
||||||
SetConfigItem(bAverage);
|
|
||||||
SetConfigItem(bRootWeight);
|
|
||||||
SetConfigItem(bUseArmCorrection);
|
|
||||||
SetConfigItem(bUseScale);
|
|
||||||
SetConfigItem(bScale);
|
|
||||||
SetConfigItem(bUseLimit);
|
|
||||||
SetConfigItem(bLimitXx);
|
|
||||||
SetConfigItem(bLimitXy);
|
|
||||||
SetConfigItem(bLimitYx);
|
|
||||||
SetConfigItem(bLimitYy);
|
|
||||||
SetConfigItem(bLimitZx);
|
|
||||||
SetConfigItem(bLimitZy);
|
|
||||||
SetConfigItem(dmmUnlockSize);
|
|
||||||
|
|
||||||
std::ofstream out(configPath);
|
|
||||||
if (!out) {
|
|
||||||
Log::ErrorFmt("SaveConfig error: Cannot open file: %s", configPath.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
out << config.dump(4);
|
|
||||||
Log::Info("SaveConfig success");
|
|
||||||
}
|
|
||||||
catch (std::exception& e) {
|
|
||||||
Log::ErrorFmt("SaveConfig error: %s", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,8 +51,5 @@ namespace GakumasLocal::Config {
|
||||||
extern float bLimitZx;
|
extern float bLimitZx;
|
||||||
extern float bLimitZy;
|
extern float bLimitZy;
|
||||||
|
|
||||||
extern bool dmmUnlockSize;
|
|
||||||
|
|
||||||
void LoadConfig(const std::string& configStr);
|
void LoadConfig(const std::string& configStr);
|
||||||
void SaveConfig(const std::string& configPath);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
#include "shadowhook.h"
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
#define ADD_HOOK(name, addr) \
|
|
||||||
name##_Addr = reinterpret_cast<name##_Type>(addr); \
|
|
||||||
if (addr) { \
|
|
||||||
auto stub = hookInstaller->InstallHook(reinterpret_cast<void*>(addr), \
|
|
||||||
reinterpret_cast<void*>(name##_Hook), \
|
|
||||||
reinterpret_cast<void**>(&name##_Orig)); \
|
|
||||||
if (stub == NULL) { \
|
|
||||||
int error_num = shadowhook_get_errno(); \
|
|
||||||
const char *error_msg = shadowhook_to_errmsg(error_num); \
|
|
||||||
Log::ErrorFmt("ADD_HOOK: %s at %p failed: %s", #name, addr, error_msg); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
hookedStubs.emplace(stub); \
|
|
||||||
GakumasLocal::Log::InfoFmt("ADD_HOOK: %s at %p", #name, addr); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
else GakumasLocal::Log::ErrorFmt("Hook failed: %s is NULL", #name, addr); \
|
|
||||||
if (Config::lazyInit) UnityResolveProgress::classProgress.current++
|
|
|
@ -365,8 +365,6 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
|
||||||
checked = config.value.unlockAllLiveCostume) {
|
checked = config.value.unlockAllLiveCostume) {
|
||||||
v -> context?.onUnlockAllLiveCostumeChanged(v)
|
v -> context?.onUnlockAllLiveCostumeChanged(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
thickness = 1.dp,
|
thickness = 1.dp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
|
||||||
|
@ -391,7 +389,7 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
|
||||||
value = config.value.liveCustomeCostumeId,
|
value = config.value.liveCustomeCostumeId,
|
||||||
onValueChange = { c -> context?.onLiveCustomeCostumeIdChanged(c, 0, 0, 0)},
|
onValueChange = { c -> context?.onLiveCustomeCostumeIdChanged(c, 0, 0, 0)},
|
||||||
label = { Text(stringResource(R.string.live_custome_dress_id)) }
|
label = { Text(stringResource(R.string.live_custome_dress_id)) }
|
||||||
)*/
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,8 +314,8 @@ fun HomePage(modifier: Modifier = Modifier,
|
||||||
fontSize = 14f,
|
fontSize = 14f,
|
||||||
value = programConfig.value.useAPIAssetsURL,
|
value = programConfig.value.useAPIAssetsURL,
|
||||||
onValueChange = { c -> context?.onPUseAPIAssetsURLChanged(c, 0, 0, 0)},
|
onValueChange = { c -> context?.onPUseAPIAssetsURLChanged(c, 0, 0, 0)},
|
||||||
label = { Text(stringResource(R.string.api_addr)) }
|
label = { Text(stringResource(R.string.api_addr)) },
|
||||||
)
|
keyboardOptions = keyboardOptionsNumber)
|
||||||
|
|
||||||
if (downloadAble) {
|
if (downloadAble) {
|
||||||
GakuButton(modifier = modifier
|
GakuButton(modifier = modifier
|
||||||
|
@ -411,8 +411,8 @@ fun HomePage(modifier: Modifier = Modifier,
|
||||||
fontSize = 14f,
|
fontSize = 14f,
|
||||||
value = programConfig.value.transRemoteZipUrl,
|
value = programConfig.value.transRemoteZipUrl,
|
||||||
onValueChange = { c -> context?.onPTransRemoteZipUrlChanged(c, 0, 0, 0)},
|
onValueChange = { c -> context?.onPTransRemoteZipUrlChanged(c, 0, 0, 0)},
|
||||||
label = { Text(stringResource(id = R.string.resource_url)) }
|
label = { Text(stringResource(id = R.string.resource_url)) },
|
||||||
)
|
keyboardOptions = keyboardOptionsNumber)
|
||||||
|
|
||||||
if (downloadAble) {
|
if (downloadAble) {
|
||||||
GakuButton(modifier = modifier
|
GakuButton(modifier = modifier
|
||||||
|
|
|
@ -1,32 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
<resources>
|
||||||
<string name="abc_action_bar_home_description">ホームに戻る</string>
|
|
||||||
<string name="abc_action_bar_up_description">前に戻る</string>
|
|
||||||
<string name="abc_action_menu_overflow_description">その他のオプション</string>
|
|
||||||
<string name="abc_action_mode_done">完了</string>
|
|
||||||
<string name="abc_activity_chooser_view_see_all">すべて表示</string>
|
|
||||||
<string name="abc_activitychooserview_choose_application">アプリの選択</string>
|
|
||||||
<string name="abc_capital_off">OFF</string>
|
|
||||||
<string name="abc_capital_on">ON</string>
|
|
||||||
<string name="abc_menu_alt_shortcut_label">Alt+</string>
|
|
||||||
<string name="abc_menu_ctrl_shortcut_label">Ctrl+</string>
|
|
||||||
<string name="abc_menu_delete_shortcut_label">Delete</string>
|
|
||||||
<string name="abc_menu_enter_shortcut_label">Enter</string>
|
|
||||||
<string name="abc_menu_function_shortcut_label">Function+</string>
|
|
||||||
<string name="abc_menu_meta_shortcut_label">Meta+</string>
|
|
||||||
<string name="abc_menu_shift_shortcut_label">Shift+</string>
|
|
||||||
<string name="abc_menu_space_shortcut_label">Space</string>
|
|
||||||
<string name="abc_menu_sym_shortcut_label">Sym+</string>
|
|
||||||
<string name="abc_prepend_shortcut_label">Menu+</string>
|
|
||||||
<string name="abc_search_hint">検索…</string>
|
|
||||||
<string name="abc_searchview_description_clear">検索キーワードを削除</string>
|
|
||||||
<string name="abc_searchview_description_query">検索キーワード</string>
|
|
||||||
<string name="abc_searchview_description_search">検索</string>
|
|
||||||
<string name="abc_searchview_description_submit">検索キーワードを送信</string>
|
|
||||||
<string name="abc_searchview_description_voice">音声検索</string>
|
|
||||||
<string name="abc_shareactionprovider_share_with">共有</string>
|
|
||||||
<string name="abc_shareactionprovider_share_with_application">%sと共有</string>
|
|
||||||
<string name="abc_toolbar_collapse_description">折りたたむ</string>
|
|
||||||
<string name="about">情報</string>
|
<string name="about">情報</string>
|
||||||
<string name="about_about_p1">このプラグインは完全に無料で提供されます。このプラグインで料金を支払ってしまった場合は、販売者に報告をしてください。</string>
|
<string name="about_about_p1">このプラグインは完全に無料で提供されます。このプラグインで料金を支払ってしまった場合は、販売者に報告をしてください。</string>
|
||||||
<string name="about_about_p2">プラグインの QQ グループ: 975854705</string>
|
<string name="about_about_p2">プラグインの QQ グループ: 975854705</string>
|
||||||
|
@ -36,10 +8,8 @@
|
||||||
<string name="about_warn_p2">外部プラグインは関連する TOS に違反するため、自己責任でご使用ください。</string>
|
<string name="about_warn_p2">外部プラグインは関連する TOS に違反するため、自己責任でご使用ください。</string>
|
||||||
<string name="about_warn_title">警告</string>
|
<string name="about_warn_title">警告</string>
|
||||||
<string name="advanced_settings">高度な設定</string>
|
<string name="advanced_settings">高度な設定</string>
|
||||||
<string name="androidx_startup">androidx.startup</string>
|
<string name="api_addr">APIアドレス (GitHub の最新リリース API)</string>
|
||||||
<string name="api_addr">API アドレス (GitHub 最新リリース API)</string>
|
|
||||||
<string name="app_name">Gakumas Localify</string>
|
<string name="app_name">Gakumas Localify</string>
|
||||||
<string name="appbar_scrolling_view_behavior">com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior</string>
|
|
||||||
<string name="average">平均</string>
|
<string name="average">平均</string>
|
||||||
<string name="axisx_x">X 軸.x</string>
|
<string name="axisx_x">X 軸.x</string>
|
||||||
<string name="axisx_y">X 軸.y</string>
|
<string name="axisx_y">X 軸.y</string>
|
||||||
|
@ -48,28 +18,15 @@
|
||||||
<string name="axisz_x">Z 軸.x</string>
|
<string name="axisz_x">Z 軸.x</string>
|
||||||
<string name="axisz_y">Z 軸.y</string>
|
<string name="axisz_y">Z 軸.y</string>
|
||||||
<string name="basic_settings">基本設定</string>
|
<string name="basic_settings">基本設定</string>
|
||||||
<string name="bottom_sheet_behavior">com.google.android.material.bottomsheet.BottomSheetBehavior</string>
|
|
||||||
<string name="bottomsheet_action_collapse">ボトムシートを閉じる</string>
|
|
||||||
<string name="bottomsheet_action_expand">ボトムシートを開く</string>
|
|
||||||
<string name="bottomsheet_action_expand_halfway">下半分を展開</string>
|
|
||||||
<string name="bottomsheet_drag_handle_clicked">ハンドルをダブルタップしてドラッグ</string>
|
|
||||||
<string name="bottomsheet_drag_handle_content_description">ドラッグハンドル</string>
|
|
||||||
<string name="breast_param">胸のパラメーター</string>
|
<string name="breast_param">胸のパラメーター</string>
|
||||||
<string name="breast_scale">胸の大きさ</string>
|
<string name="breast_scale">胸の大きさ</string>
|
||||||
<string name="call_notification_answer_action">応答</string>
|
|
||||||
<string name="call_notification_answer_video_action">動画</string>
|
|
||||||
<string name="call_notification_decline_action">拒否</string>
|
|
||||||
<string name="call_notification_hang_up_action">通話終了</string>
|
|
||||||
<string name="call_notification_incoming_text">着信</string>
|
|
||||||
<string name="call_notification_ongoing_text">通話中</string>
|
|
||||||
<string name="call_notification_screening_text">着信をスクリーニング中</string>
|
|
||||||
<string name="camera_settings">カメラ設定</string>
|
<string name="camera_settings">カメラ設定</string>
|
||||||
<string name="cancel">キャンセル</string>
|
<string name="cancel">キャンセル</string>
|
||||||
<string name="character_counter_content_description">%1$d の %2$d に入力された文字</string>
|
<string name="character_counter_content_description">%1$d の %2$d に入力された文字</string>
|
||||||
<string name="character_counter_overflowed_content_description">文字制限が %2$d 文字中、 %1$d 文字を超えています</string>
|
<string name="character_counter_overflowed_content_description">文字制限が %2$d 文字中、 %1$d 文字を超えています</string>
|
||||||
<string name="character_counter_pattern">%1$d/%2$d</string>
|
<string name="character_counter_pattern">%1$d/%2$d</string>
|
||||||
<string name="check_built_in_resource">内蔵アセットの更新を確認</string>
|
<string name="check_built_in_resource">内蔵アセットのアップデートを確認</string>
|
||||||
<string name="check_resource_from_api">リソースの更新を API から確認</string>
|
<string name="check_resource_from_api">リソースアップデートを API から確認</string>
|
||||||
<string name="check_update">確認</string>
|
<string name="check_update">確認</string>
|
||||||
<string name="clear_text_end_icon_content_description">テキストを消去</string>
|
<string name="clear_text_end_icon_content_description">テキストを消去</string>
|
||||||
<string name="close_drawer">ナビゲーションメニューを閉じる</string>
|
<string name="close_drawer">ナビゲーションメニューを閉じる</string>
|
||||||
|
@ -80,7 +37,7 @@
|
||||||
<string name="default_assets_check_api">https://api.github.com/repos/NatsumeLS/Gakumas-Translation-Data-EN/releases/latest</string>
|
<string name="default_assets_check_api">https://api.github.com/repos/NatsumeLS/Gakumas-Translation-Data-EN/releases/latest</string>
|
||||||
<string name="default_error_message">入力が無効です</string>
|
<string name="default_error_message">入力が無効です</string>
|
||||||
<string name="default_popup_window_title">ポップアップウィンドウ</string>
|
<string name="default_popup_window_title">ポップアップウィンドウ</string>
|
||||||
<string name="del_remote_after_update">キャッシュファイルを更新後に削除</string>
|
<string name="del_remote_after_update">キャッシュファイルをアップデート後に削除</string>
|
||||||
<string name="delete_plugin_resource">プラグインリソースを削除</string>
|
<string name="delete_plugin_resource">プラグインリソースを削除</string>
|
||||||
<string name="download">ダウンロード</string>
|
<string name="download">ダウンロード</string>
|
||||||
<string name="downloaded_resource_version">ダウンロードされたバージョン</string>
|
<string name="downloaded_resource_version">ダウンロードされたバージョン</string>
|
||||||
|
@ -92,19 +49,17 @@
|
||||||
<string name="error_icon_content_description">エラー</string>
|
<string name="error_icon_content_description">エラー</string>
|
||||||
<string name="export_text">テキストをエクスポート</string>
|
<string name="export_text">テキストをエクスポート</string>
|
||||||
<string name="exposed_dropdown_menu_content_description">ドロップダウンメニューを表示</string>
|
<string name="exposed_dropdown_menu_content_description">ドロップダウンメニューを表示</string>
|
||||||
<string name="fab_transformation_scrim_behavior">com.google.android.material.transformation.FabTransformationScrimBehavior</string>
|
<string name="force_export_resource">リソースのアップデートを強制する</string>
|
||||||
<string name="fab_transformation_sheet_behavior">com.google.android.material.transformation.FabTransformationSheetBehavior</string>
|
<string name="login_as_ios">iOSとしてログイン</string>
|
||||||
<string name="force_export_resource">リソースの更新を強制する</string>
|
|
||||||
<string name="gakumas_localify">Gakumas Localify</string>
|
<string name="gakumas_localify">Gakumas Localify</string>
|
||||||
<string name="game_patch">ゲームパッチ</string>
|
<string name="game_patch">ゲームパッチ</string>
|
||||||
<string name="graphic_settings">グラフィック設定</string>
|
<string name="graphic_settings">グラフィック設定</string>
|
||||||
<string name="hide_bottom_view_on_scroll_behavior">com.google.android.material.behavior.HideBottomViewOnScrollBehavior</string>
|
|
||||||
<string name="hign">高</string>
|
<string name="hign">高</string>
|
||||||
<string name="home">ホーム</string>
|
<string name="home">ホーム</string>
|
||||||
<string name="home_shizuku_warning">一部の機能が使用できません</string>
|
<string name="home_shizuku_warning">一部の機能が使用できません</string>
|
||||||
<string name="icon_content_description">ダイアログアイコン</string>
|
<string name="icon_content_description">ダイアログアイコン</string>
|
||||||
<string name="in_progress">実行中</string>
|
<string name="in_progress">実行中</string>
|
||||||
<string name="indeterminate">部分的に確認済み</string>
|
<string name="indeterminate">部分的にチェック済み</string>
|
||||||
<string name="install">インストール</string>
|
<string name="install">インストール</string>
|
||||||
<string name="installing">インストール中</string>
|
<string name="installing">インストール中</string>
|
||||||
<string name="invalid_zip_file">無効なファイル</string>
|
<string name="invalid_zip_file">無効なファイル</string>
|
||||||
|
@ -115,168 +70,9 @@
|
||||||
<string name="liveUseCustomeDress">ライブのキャラクターをカスタム</string>
|
<string name="liveUseCustomeDress">ライブのキャラクターをカスタム</string>
|
||||||
<string name="live_costume_head_id">ライブのカスタムヘッド ID (例: costume_head_hski-cstm-0002)</string>
|
<string name="live_costume_head_id">ライブのカスタムヘッド ID (例: costume_head_hski-cstm-0002)</string>
|
||||||
<string name="live_custome_dress_id">ライブ衣装のカスタム ID (例: hski-cstm-0002)</string>
|
<string name="live_custome_dress_id">ライブ衣装のカスタム ID (例: hski-cstm-0002)</string>
|
||||||
<string name="login_as_ios">iOS としてログイン</string>
|
|
||||||
<string name="low">低</string>
|
<string name="low">低</string>
|
||||||
<string name="m3_exceed_max_badge_text_suffix">%1$s%2$s</string>
|
|
||||||
<string name="m3_ref_typeface_brand_medium">sans-serif-medium</string>
|
|
||||||
<string name="m3_ref_typeface_brand_regular">sans-serif</string>
|
|
||||||
<string name="m3_ref_typeface_plain_medium">sans-serif-medium</string>
|
|
||||||
<string name="m3_ref_typeface_plain_regular">sans-serif</string>
|
|
||||||
<string name="m3_sys_motion_easing_emphasized">path(M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_emphasized_accelerate">cubic-bezier(0.3, 0, 0.8, 0.2)</string>
|
|
||||||
<string name="m3_sys_motion_easing_emphasized_decelerate">cubic-bezier(0.1, 0.7, 0.1, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_emphasized_path_data">M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1</string>
|
|
||||||
<string name="m3_sys_motion_easing_legacy">cubic-bezier(0.4, 0, 0.2, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_legacy_accelerate">cubic-bezier(0.4, 0, 1, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_legacy_decelerate">cubic-bezier(0, 0, 0.2, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_linear">cubic-bezier(0, 0, 1, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_standard">cubic-bezier(0.2, 0, 0, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_standard_accelerate">cubic-bezier(0.3, 0, 1, 1)</string>
|
|
||||||
<string name="m3_sys_motion_easing_standard_decelerate">cubic-bezier(0, 0, 0, 1)</string>
|
|
||||||
<string name="m3c_bottom_sheet_collapse_description">ボトムシートを折りたたみます</string>
|
|
||||||
<string name="m3c_bottom_sheet_dismiss_description">ボトムシートを閉じます</string>
|
|
||||||
<string name="m3c_bottom_sheet_drag_handle_description">ドラッグハンドル</string>
|
|
||||||
<string name="m3c_bottom_sheet_expand_description">ボトムシートを開きます</string>
|
|
||||||
<string name="m3c_bottom_sheet_pane_title">ボトムシート</string>
|
|
||||||
<string name="m3c_date_input_headline">入力された日付</string>
|
|
||||||
<string name="m3c_date_input_headline_description">入力された日付: %1$s</string>
|
|
||||||
<string name="m3c_date_input_invalid_for_pattern">想定パターンと一致しない日付: %1$s</string>
|
|
||||||
<string name="m3c_date_input_invalid_not_allowed">許可されない日付: %1$s</string>
|
|
||||||
<string name="m3c_date_input_invalid_year_range">想定される年の範囲(%1$s~%2$s)から日付が外れています</string>
|
|
||||||
<string name="m3c_date_input_label">日付</string>
|
|
||||||
<string name="m3c_date_input_no_input_description">なし</string>
|
|
||||||
<string name="m3c_date_input_title">日付の選択</string>
|
|
||||||
<string name="m3c_date_picker_headline">選択した日付</string>
|
|
||||||
<string name="m3c_date_picker_headline_description">現在の選択: %1$s</string>
|
|
||||||
<string name="m3c_date_picker_navigate_to_year_description">年に移動 %1$s</string>
|
|
||||||
<string name="m3c_date_picker_no_selection_description">なし</string>
|
|
||||||
<string name="m3c_date_picker_scroll_to_earlier_years">これより前の年を表示するにはスクロールしてください</string>
|
|
||||||
<string name="m3c_date_picker_scroll_to_later_years">これより後の年を表示するにはスクロールしてください</string>
|
|
||||||
<string name="m3c_date_picker_switch_to_calendar_mode">カレンダー入力モードに切り替え</string>
|
|
||||||
<string name="m3c_date_picker_switch_to_day_selection">スワイプして年を選択するか、タップして日付の選択に戻ります</string>
|
|
||||||
<string name="m3c_date_picker_switch_to_input_mode">テキスト入力モードに切り替え</string>
|
|
||||||
<string name="m3c_date_picker_switch_to_next_month">翌月に変更</string>
|
|
||||||
<string name="m3c_date_picker_switch_to_previous_month">前月に変更</string>
|
|
||||||
<string name="m3c_date_picker_switch_to_year_selection">年の選択に切り替え</string>
|
|
||||||
<string name="m3c_date_picker_title">日付の選択</string>
|
|
||||||
<string name="m3c_date_picker_today_description">今日</string>
|
|
||||||
<string name="m3c_date_picker_year_picker_pane_title">年の選択ツールの表示</string>
|
|
||||||
<string name="m3c_date_range_input_invalid_range_input">入力された期間は無効です</string>
|
|
||||||
<string name="m3c_date_range_input_title">日付の入力</string>
|
|
||||||
<string name="m3c_date_range_picker_day_in_range">範囲内</string>
|
|
||||||
<string name="m3c_date_range_picker_end_headline">終了日</string>
|
|
||||||
<string name="m3c_date_range_picker_scroll_to_next_month">翌月を表示するにはスクロールしてください</string>
|
|
||||||
<string name="m3c_date_range_picker_scroll_to_previous_month">前月を表示するにはスクロールしてください</string>
|
|
||||||
<string name="m3c_date_range_picker_start_headline">開始日</string>
|
|
||||||
<string name="m3c_date_range_picker_title">日付の選択</string>
|
|
||||||
<string name="m3c_dialog">ダイアログ</string>
|
|
||||||
<string name="m3c_dropdown_menu_collapsed">閉じています</string>
|
|
||||||
<string name="m3c_dropdown_menu_expanded">開いています</string>
|
|
||||||
<string name="m3c_search_bar_search">検索</string>
|
|
||||||
<string name="m3c_snackbar_dismiss">閉じる</string>
|
|
||||||
<string name="m3c_suggestions_available">検索候補は次のとおりです</string>
|
|
||||||
<string name="m3c_time_picker_am">AM</string>
|
|
||||||
<string name="m3c_time_picker_hour">時間</string>
|
|
||||||
<string name="m3c_time_picker_hour_24h_suffix">%1$d 時間</string>
|
|
||||||
<string name="m3c_time_picker_hour_selection">時刻を選択</string>
|
|
||||||
<string name="m3c_time_picker_hour_suffix">"%1$d 時"</string>
|
|
||||||
<string name="m3c_time_picker_hour_text_field">(時間単位)</string>
|
|
||||||
<string name="m3c_time_picker_minute">分</string>
|
|
||||||
<string name="m3c_time_picker_minute_selection">分を選択</string>
|
|
||||||
<string name="m3c_time_picker_minute_suffix">%1$d 分</string>
|
|
||||||
<string name="m3c_time_picker_minute_text_field">(分単位)</string>
|
|
||||||
<string name="m3c_time_picker_period_toggle_description">午前または午後を選択</string>
|
|
||||||
<string name="m3c_time_picker_pm">PM</string>
|
|
||||||
<string name="m3c_tooltip_long_press_label">ツールチップを表示</string>
|
|
||||||
<string name="m3c_tooltip_pane_description">ツールチップ</string>
|
|
||||||
<string name="material_clock_display_divider">:</string>
|
|
||||||
<string name="material_clock_toggle_content_description">午前または午後を選択</string>
|
|
||||||
<string name="material_hour_24h_suffix">%1$s 時間</string>
|
|
||||||
<string name="material_hour_selection">時刻を選択してください</string>
|
|
||||||
<string name="material_hour_suffix">"%1$s時"</string>
|
|
||||||
<string name="material_minute_selection">分を選択</string>
|
|
||||||
<string name="material_minute_suffix">%1$s分</string>
|
|
||||||
<string name="material_motion_easing_accelerated">cubic-bezier(0.4, 0.0, 1.0, 1.0)</string>
|
|
||||||
<string name="material_motion_easing_decelerated">cubic-bezier(0.0, 0.0, 0.2, 1.0)</string>
|
|
||||||
<string name="material_motion_easing_emphasized">path(M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1)</string>
|
|
||||||
<string name="material_motion_easing_linear">cubic-bezier(0.0, 0.0, 1.0, 1.0)</string>
|
|
||||||
<string name="material_motion_easing_standard">cubic-bezier(0.4, 0.0, 0.2, 1.0)</string>
|
|
||||||
<string name="material_slider_range_end">Range end</string>
|
|
||||||
<string name="material_slider_range_start">Range start</string>
|
|
||||||
<string name="material_slider_value">Value</string>
|
|
||||||
<string name="material_timepicker_am">AM</string>
|
|
||||||
<string name="material_timepicker_clock_mode_description">時刻を時計で入力するモードに切り替えます。</string>
|
|
||||||
<string name="material_timepicker_hour">時間</string>
|
|
||||||
<string name="material_timepicker_minute">分</string>
|
|
||||||
<string name="material_timepicker_pm">PM</string>
|
|
||||||
<string name="material_timepicker_select_time">時間を選択</string>
|
|
||||||
<string name="material_timepicker_text_input_mode_description">時刻をテキストで入力するモードに切り替えます。</string>
|
|
||||||
<string name="max_high">ウルトラ</string>
|
<string name="max_high">ウルトラ</string>
|
||||||
<string name="middle">中</string>
|
<string name="middle">中</string>
|
||||||
<string name="mtrl_badge_numberless_content_description">新しい通知</string>
|
|
||||||
<string name="mtrl_checkbox_button_icon_path_checked">M14,18.2 11.4,15.6 10,17 14,21 22,13 20.6,11.6z</string>
|
|
||||||
<string name="mtrl_checkbox_button_icon_path_group_name">icon</string>
|
|
||||||
<string name="mtrl_checkbox_button_icon_path_indeterminate">M13.4,15 11,15 11,17 13.4,17 21,17 21,15z</string>
|
|
||||||
<string name="mtrl_checkbox_button_icon_path_name">icon path</string>
|
|
||||||
<string name="mtrl_checkbox_button_path_checked">M23,7H9C7.9,7,7,7.9,7,9v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9C25,7.9,24.1,7,23,7z</string>
|
|
||||||
<string name="mtrl_checkbox_button_path_group_name">button</string>
|
|
||||||
<string name="mtrl_checkbox_button_path_name">button path</string>
|
|
||||||
<string name="mtrl_checkbox_button_path_unchecked">M23,7H9C7.9,7,7,7.9,7,9v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9C25,7.9,24.1,7,23,7z M23,23H9V9h14V23z</string>
|
|
||||||
<string name="mtrl_checkbox_state_description_checked">オン</string>
|
|
||||||
<string name="mtrl_checkbox_state_description_indeterminate">一部オン</string>
|
|
||||||
<string name="mtrl_checkbox_state_description_unchecked">オフ</string>
|
|
||||||
<string name="mtrl_chip_close_icon_content_description">%1$s を削除します</string>
|
|
||||||
<string name="mtrl_exceed_max_badge_number_content_description">%1$d 件以上の新しい通知</string>
|
|
||||||
<string name="mtrl_exceed_max_badge_number_suffix">%1$d%2$s</string>
|
|
||||||
<string name="mtrl_picker_a11y_next_month">翌月に変更</string>
|
|
||||||
<string name="mtrl_picker_a11y_prev_month">前月に変更</string>
|
|
||||||
<string name="mtrl_picker_announce_current_range_selection">開始日の選択: %1$s – 終了日の選択: %2$s</string>
|
|
||||||
<string name="mtrl_picker_announce_current_selection">現在の選択: %1$s</string>
|
|
||||||
<string name="mtrl_picker_announce_current_selection_none">なし</string>
|
|
||||||
<string name="mtrl_picker_cancel">キャンセル</string>
|
|
||||||
<string name="mtrl_picker_confirm">OK</string>
|
|
||||||
<string name="mtrl_picker_date_header_selected">%1$s</string>
|
|
||||||
<string name="mtrl_picker_date_header_title">日付を選択してください</string>
|
|
||||||
<string name="mtrl_picker_date_header_unselected">選択した日付</string>
|
|
||||||
<string name="mtrl_picker_day_of_week_column_header">%1$s</string>
|
|
||||||
<string name="mtrl_picker_end_date_description">終了日 %1$s</string>
|
|
||||||
<string name="mtrl_picker_invalid_format">形式が無効です。</string>
|
|
||||||
<string name="mtrl_picker_invalid_format_example">例: %1$s</string>
|
|
||||||
<string name="mtrl_picker_invalid_format_use">使用: %1$s</string>
|
|
||||||
<string name="mtrl_picker_invalid_range">範囲が無効です。</string>
|
|
||||||
<string name="mtrl_picker_navigate_to_current_year_description">現在の年(%1$d)に移動</string>
|
|
||||||
<string name="mtrl_picker_navigate_to_year_description">%1$d 年に移動</string>
|
|
||||||
<string name="mtrl_picker_out_of_range">範囲外: %1$s</string>
|
|
||||||
<string name="mtrl_picker_range_header_only_end_selected">開始日~%1$s</string>
|
|
||||||
<string name="mtrl_picker_range_header_only_start_selected">%1$s~終了日</string>
|
|
||||||
<string name="mtrl_picker_range_header_selected">%1$s~%2$s</string>
|
|
||||||
<string name="mtrl_picker_range_header_title">期間を選択してください</string>
|
|
||||||
<string name="mtrl_picker_range_header_unselected">開始日~終了日</string>
|
|
||||||
<string name="mtrl_picker_save">保存</string>
|
|
||||||
<string name="mtrl_picker_start_date_description">開始日 %1$s</string>
|
|
||||||
<string name="mtrl_picker_text_input_date_hint">日付</string>
|
|
||||||
<string name="mtrl_picker_text_input_date_range_end_hint">終了日</string>
|
|
||||||
<string name="mtrl_picker_text_input_date_range_start_hint">開始日</string>
|
|
||||||
<string name="mtrl_picker_text_input_day_abbr">d</string>
|
|
||||||
<string name="mtrl_picker_text_input_month_abbr">m</string>
|
|
||||||
<string name="mtrl_picker_text_input_year_abbr">y</string>
|
|
||||||
<string name="mtrl_picker_today_description">今日(%1$s)</string>
|
|
||||||
<string name="mtrl_picker_toggle_to_calendar_input_mode">カレンダー入力モードに切り替え</string>
|
|
||||||
<string name="mtrl_picker_toggle_to_day_selection">タップするとカレンダー表示に切り替わります</string>
|
|
||||||
<string name="mtrl_picker_toggle_to_text_input_mode">テキスト入力モードに切り替え</string>
|
|
||||||
<string name="mtrl_picker_toggle_to_year_selection">タップすると年表示に切り替わります</string>
|
|
||||||
<string name="mtrl_switch_thumb_group_name">circle_group</string>
|
|
||||||
<string name="mtrl_switch_thumb_path_checked">M4,16 A12,12 0 0,1 16,4 H16 A12,12 0 0,1 16,28 H16 A12,12 0 0,1 4,16</string>
|
|
||||||
<string name="mtrl_switch_thumb_path_morphing">M0,16 A11,11 0 0,1 11,5 H21 A11,11 0 0,1 21,27 H11 A11,11 0 0,1 0,16</string>
|
|
||||||
<string name="mtrl_switch_thumb_path_name">circle</string>
|
|
||||||
<string name="mtrl_switch_thumb_path_pressed">M2,16 A14,14 0 0,1 16,2 H16 A14,14 0 0,1 16,30 H16 A14,14 0 0,1 2,16</string>
|
|
||||||
<string name="mtrl_switch_thumb_path_unchecked">M8,16 A8,8 0 0,1 16,8 H16 A8,8 0 0,1 16,24 H16 A8,8 0 0,1 8,16</string>
|
|
||||||
<string name="mtrl_switch_track_decoration_path">M1,16 A15,15 0 0,1 16,1 H36 A15,15 0 0,1 36,31 H16 A15,15 0 0,1 1,16</string>
|
|
||||||
<string name="mtrl_switch_track_path">M0,16 A16,16 0 0,1 16,0 H36 A16,16 0 0,1 36,32 H16 A16,16 0 0,1 0,16</string>
|
|
||||||
<string name="mtrl_timepicker_cancel">キャンセル</string>
|
|
||||||
<string name="mtrl_timepicker_confirm">OK</string>
|
|
||||||
<string name="navigation_menu">ナビゲーションメニュー</string>
|
|
||||||
<string name="not_selected">未選択</string>
|
|
||||||
<string name="off">OFF</string>
|
<string name="off">OFF</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="on">ON</string>
|
<string name="on">ON</string>
|
||||||
|
@ -288,21 +84,12 @@
|
||||||
<string name="patch_debuggable">デバッグを可能にする</string>
|
<string name="patch_debuggable">デバッグを可能にする</string>
|
||||||
<string name="patch_finished">パッチが完了しました。インストールをしますか?</string>
|
<string name="patch_finished">パッチが完了しました。インストールをしますか?</string>
|
||||||
<string name="patch_integrated">統合</string>
|
<string name="patch_integrated">統合</string>
|
||||||
<string name="patch_integrated_desc">"モジュールを埋め込んだ状態なアプリでパッチを当てます。
|
<string name="patch_integrated_desc">"モジュールを埋め込んだ状態なアプリでパッチを当てます。\nパッチを適用したアプリは LSPatch Manager なしで実行できますが、動的に管理はできません。\n統合パッチが適用されたアプリは、LSPatch Manager がインストールされていないデバイスでも使用が可能です。"</string>
|
||||||
パッチを適用したアプリは LSPatch Manager なしで実行できますが、動的に管理はできません。
|
|
||||||
統合パッチが適用されたアプリは、LSPatch Manager がインストールされていないデバイスでも使用が可能です。"</string>
|
|
||||||
<string name="patch_local">ローカル</string>
|
<string name="patch_local">ローカル</string>
|
||||||
<string name="patch_local_desc">"モジュールを埋め込まずにアプリにパッチを当てます。
|
<string name="patch_local_desc">"モジュールを埋め込まずにアプリにパッチを当てます。\nXposed スコープは再パッチなしで動的に変更が可能です。\nローカルでのパッチを当てたアプリは、ローカルのデバイスでのみ実行可能です。"</string>
|
||||||
Xposed スコープは再パッチなしで動的に変更が可能です。
|
|
||||||
ローカルでのパッチを当てたアプリは、ローカルのデバイスでのみ実行可能です。"</string>
|
|
||||||
<string name="patch_mode">パッチモード</string>
|
<string name="patch_mode">パッチモード</string>
|
||||||
<string name="patch_uninstall_confirm">アンインストールをしてもよろしいですか?</string>
|
<string name="patch_uninstall_confirm">アンインストールをしてもよろしいですか?</string>
|
||||||
<string name="patch_uninstall_text">"署名が異なるため、パッチをインストールする前に元となるアプリをアンインストールする必要があります。
|
<string name="patch_uninstall_text">"署名が異なるため、パッチをインストールする前に元となるアプリをアンインストールする必要があります。\n個人データのバックアップを設定済みであることを確認してください。"</string>
|
||||||
個人データのバックアップを設定済みであることを確認してください。"</string>
|
|
||||||
<string name="path_password_eye">M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z</string>
|
|
||||||
<string name="path_password_eye_mask_strike_through">M2,4.27 L19.73,22 L22.27,19.46 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
|
|
||||||
<string name="path_password_eye_mask_visible">M2,4.27 L2,4.27 L4.54,1.73 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
|
|
||||||
<string name="path_password_strike_through">M3.27,4.27 L19.74,20.74</string>
|
|
||||||
<string name="pendulum">揺れ</string>
|
<string name="pendulum">揺れ</string>
|
||||||
<string name="pendulumrange">揺れの範囲</string>
|
<string name="pendulumrange">揺れの範囲</string>
|
||||||
<string name="plugin_code">プラグインのコード</string>
|
<string name="plugin_code">プラグインのコード</string>
|
||||||
|
@ -314,37 +101,26 @@ Xposed スコープは再パッチなしで動的に変更が可能です。
|
||||||
<string name="reserve_patched">パッチ済みの APK を予約する</string>
|
<string name="reserve_patched">パッチ済みの APK を予約する</string>
|
||||||
<string name="resource_settings">リソース設定</string>
|
<string name="resource_settings">リソース設定</string>
|
||||||
<string name="resource_url">リソース URL</string>
|
<string name="resource_url">リソース URL</string>
|
||||||
<string name="rootweight">ルートウエイト</string>
|
<string name="rootweight">ルートウェイト</string>
|
||||||
<string name="search_menu_title">検索</string>
|
|
||||||
<string name="searchbar_scrolling_view_behavior">com.google.android.material.search.SearchBar$ScrollingViewBehavior</string>
|
|
||||||
<string name="searchview_clear_text_content_description">テキストを消去</string>
|
|
||||||
<string name="searchview_navigation_content_description">戻る</string>
|
|
||||||
<string name="selected">選択済み</string>
|
<string name="selected">選択済み</string>
|
||||||
<string name="setFpsTitle">最大 FPS (0 はオリジナルの設定を使用します)</string>
|
<string name="setFpsTitle">最大 FPS (0 はオリジナルの設定を使用します)</string>
|
||||||
<string name="shizuku_available">Shizuku サービスが有効です</string>
|
<string name="shizuku_available">Shizuku サービスが有効です</string>
|
||||||
<string name="shizuku_unavailable">Shizuku サービスが接続されていません</string>
|
<string name="shizuku_unavailable">Shizuku サービスが接続されていません</string>
|
||||||
<string name="side_sheet_accessibility_pane_title">サイドシート</string>
|
|
||||||
<string name="side_sheet_behavior">com.google.android.material.sidesheet.SideSheetBehavior</string>
|
|
||||||
<string name="spring">跳ね</string>
|
<string name="spring">跳ね</string>
|
||||||
<string name="start_game">ゲーム開始 / ホットリロードの設定</string>
|
<string name="start_game">ゲーム開始 / ホットリロードの設定</string>
|
||||||
<string name="status_bar_notification_info_overflow">999+</string>
|
|
||||||
<string name="stiffness">剛性</string>
|
<string name="stiffness">剛性</string>
|
||||||
<string name="support_file_types">"対応ファイル:
|
<string name="support_file_types">"対応ファイル:\n単一または複数選択: apk\n単一選択: apks、xapk、zip"</string>
|
||||||
単一または複数選択: apk
|
|
||||||
単一選択: apks、xapk、zip"</string>
|
|
||||||
<string name="switch_role">切り替え</string>
|
<string name="switch_role">切り替え</string>
|
||||||
<string name="tab">タブ</string>
|
<string name="tab">タブ</string>
|
||||||
<string name="template_percent">%1$d パーセント。</string>
|
<string name="template_percent">%1$d パーセント。</string>
|
||||||
<string name="test_mode_live">テストモード - ライブ</string>
|
<string name="test_mode_live">テストモード - ライブ</string>
|
||||||
<string name="text_hook_test_mode">テキストフックテストモード</string>
|
<string name="text_hook_test_mode">テキストフックテストモード</string>
|
||||||
<string name="tooltip_description">ツールチップ</string>
|
<string name="useMasterDBTrans">MasterDB をローカライズする</string>
|
||||||
<string name="tooltip_label">ツールチップを表示</string>
|
|
||||||
<string name="translation_repository">翻訳のリポジトリ</string>
|
<string name="translation_repository">翻訳のリポジトリ</string>
|
||||||
<string name="translation_resource_update">翻訳リソースを更新</string>
|
<string name="translation_resource_update">翻訳リソースをアップデート</string>
|
||||||
<string name="unlockAllLive">すべてのライブを開放</string>
|
<string name="unlockAllLive">すべてのライブを開放</string>
|
||||||
<string name="unlockAllLiveCostume">すべてのライブ衣装を開放</string>
|
<string name="unlockAllLiveCostume">すべてのライブ衣装を開放</string>
|
||||||
<string name="useCustomeGraphicSettings">カスタムグラフィック設定を使用する</string>
|
<string name="useCustomeGraphicSettings">カスタムグラフィック設定を使用する</string>
|
||||||
<string name="useMasterDBTrans">MasterDB のローカライズを有効化</string>
|
|
||||||
<string name="use_remote_zip_resource">リモート ZIP リソースを使用する</string>
|
<string name="use_remote_zip_resource">リモート ZIP リソースを使用する</string>
|
||||||
<string name="usearmcorrection">Arm コレクションを使用する</string>
|
<string name="usearmcorrection">Arm コレクションを使用する</string>
|
||||||
<string name="uselimit_0_1">リミットレンジの倍率 (0 は無制限)</string>
|
<string name="uselimit_0_1">リミットレンジの倍率 (0 は無制限)</string>
|
||||||
|
|
Loading…
Reference in New Issue