添加贴图替换功能
This commit is contained in:
parent
3c4b16e03b
commit
4d731ba32b
|
|
@ -5,6 +5,9 @@
|
|||
"useRemoteAssets": true,
|
||||
"useAPIAssets": false,
|
||||
"useAPIAssetsURL": "https://uma.chinosk6.cn/api/gkms_trans_data",
|
||||
"useAPITextureAssets": false,
|
||||
"useAPITextureAssetsURL": "https://texture.gakumas.cn/api/gkms_texture_data",
|
||||
"delTextureRemoteAfterUpdate": true,
|
||||
"delRemoteAfterUpdate": false,
|
||||
"cleanLocalAssets": false,
|
||||
"p": false
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
"enabled": true,
|
||||
"lazyInit": true,
|
||||
"replaceFont": true,
|
||||
"replaceTexture": true,
|
||||
"textTest": false,
|
||||
"useMasterTrans": true,
|
||||
"dumpText": false,
|
||||
"dumpRuntimeTexture": false,
|
||||
"gameOrientation": 0,
|
||||
"forceExportResource": false,
|
||||
"enableFreeCamera": false,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@
|
|||
#include <thread>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <cctype>
|
||||
#include "../platformDefine.hpp"
|
||||
|
||||
#ifdef GKMS_WINDOWS
|
||||
|
|
@ -262,10 +267,27 @@ namespace GakumasLocal::HookMain {
|
|||
|
||||
std::unordered_map<void*, std::string> loadHistory{};
|
||||
|
||||
void* ReplaceTextureOrSpriteAsset(void* result, const std::string& assetName);
|
||||
void* ReplaceTextureOrSpriteByObjectName(void* result);
|
||||
void ReplaceAllAssetTextures(void* allAssets);
|
||||
void* ReplaceSpriteAssetByTextureName(void* sprite);
|
||||
void* ReplaceSpriteTexture(void* texture2D);
|
||||
void DumpTextureOrSpriteAsset(void* result);
|
||||
|
||||
DEFINE_HOOK(void*, AssetBundle_LoadAsset, (void* self, Il2cppString* name, void* type)) {
|
||||
auto result = AssetBundle_LoadAsset_Orig(self, name, type);
|
||||
if (name) {
|
||||
result = ReplaceTextureOrSpriteAsset(result, name->ToString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void*, AssetBundle_LoadAssetAsync, (void* self, Il2cppString* name, void* type)) {
|
||||
// Log::InfoFmt("AssetBundle_LoadAssetAsync: %s, type: %s", name->ToString().c_str());
|
||||
auto ret = AssetBundle_LoadAssetAsync_Orig(self, name, type);
|
||||
loadHistory.emplace(ret, name->ToString());
|
||||
if (ret && name) {
|
||||
loadHistory.emplace(ret, name->ToString());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -277,18 +299,62 @@ namespace GakumasLocal::HookMain {
|
|||
|
||||
// const auto assetClass = Il2cppUtils::get_class_from_instance(result);
|
||||
// Log::InfoFmt("AssetBundleRequest_GetResult: %s, type: %s", name.c_str(), static_cast<Il2CppClassHead*>(assetClass)->name);
|
||||
result = ReplaceTextureOrSpriteAsset(result, name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void*, AssetBundleRequest_get_asset, (void* self)) {
|
||||
std::string name;
|
||||
if (const auto iter = loadHistory.find(self); iter != loadHistory.end()) {
|
||||
name = iter->second;
|
||||
loadHistory.erase(iter);
|
||||
}
|
||||
|
||||
auto result = AssetBundleRequest_get_asset_Orig(self);
|
||||
if (!name.empty()) {
|
||||
result = ReplaceTextureOrSpriteAsset(result, name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void*, AssetBundleRequest_get_allAssets, (void* self)) {
|
||||
auto result = AssetBundleRequest_get_allAssets_Orig(self);
|
||||
ReplaceAllAssetTextures(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void*, Resources_Load, (Il2cppString* path, void* systemTypeInstance)) {
|
||||
auto ret = Resources_Load_Orig(path, systemTypeInstance);
|
||||
|
||||
// if (ret) Log::DebugFmt("Resources_Load: %s, type: %s", path->ToString().c_str(), Il2cppUtils::get_class_from_instance(ret)->name);
|
||||
if (path) {
|
||||
ret = ReplaceTextureOrSpriteAsset(ret, path->ToString());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void*, Sprite_get_texture, (void* self)) {
|
||||
return ReplaceSpriteTexture(Sprite_get_texture_Orig(self));
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void, Image_set_sprite, (void* self, void* sprite)) {
|
||||
Image_set_sprite_Orig(self, ReplaceSpriteAssetByTextureName(sprite));
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void, Image_set_overrideSprite, (void* self, void* sprite)) {
|
||||
Image_set_overrideSprite_Orig(self, ReplaceSpriteAssetByTextureName(sprite));
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void, CanvasRenderer_SetTexture, (void* self, void* texture)) {
|
||||
CanvasRenderer_SetTexture_Orig(self, ReplaceTextureOrSpriteByObjectName(texture));
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void, SpriteRenderer_set_sprite, (void* self, void* sprite)) {
|
||||
SpriteRenderer_set_sprite_Orig(self, ReplaceSpriteAssetByTextureName(sprite));
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void, I18nHelper_SetUpI18n, (void* self, Il2cppString* lang, Il2cppString* localizationText, int keyComparison)) {
|
||||
// Log::InfoFmt("SetUpI18n lang: %s, key: %d text: %s", lang->ToString().c_str(), keyComparison, localizationText->ToString().c_str());
|
||||
// TODO 此处为 dump 原文 csv
|
||||
|
|
@ -460,6 +526,775 @@ namespace GakumasLocal::HookMain {
|
|||
}
|
||||
#endif
|
||||
|
||||
Il2cppUtils::Il2CppClassHead* Texture2DClass = nullptr;
|
||||
Il2cppUtils::Il2CppClassHead* SpriteClass = nullptr;
|
||||
std::unordered_map<std::string, uint32_t> LoadedLocalTextureHandles{};
|
||||
std::unordered_set<std::string> AppliedLocalTextureKeys{};
|
||||
|
||||
Il2cppUtils::Il2CppClassHead* GetTexture2DClass() {
|
||||
if (!Texture2DClass) {
|
||||
const auto textureClass = Il2cppUtils::GetClass("UnityEngine.CoreModule.dll", "UnityEngine", "Texture2D");
|
||||
if (textureClass) {
|
||||
Texture2DClass = static_cast<Il2cppUtils::Il2CppClassHead*>(textureClass->address);
|
||||
}
|
||||
}
|
||||
return Texture2DClass;
|
||||
}
|
||||
|
||||
Il2cppUtils::Il2CppClassHead* GetSpriteClass() {
|
||||
if (!SpriteClass) {
|
||||
const auto spriteClass = Il2cppUtils::GetClass("UnityEngine.CoreModule.dll", "UnityEngine", "Sprite");
|
||||
if (spriteClass) {
|
||||
SpriteClass = static_cast<Il2cppUtils::Il2CppClassHead*>(spriteClass->address);
|
||||
}
|
||||
}
|
||||
return SpriteClass;
|
||||
}
|
||||
|
||||
bool IsTexture2D(void* obj) {
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
if (!obj || !textureClass) return false;
|
||||
|
||||
const auto objClass = Il2cppUtils::get_class_from_instance(obj);
|
||||
if (objClass == textureClass) return true;
|
||||
|
||||
return UnityResolve::Invoke<bool>("il2cpp_class_is_assignable_from", textureClass, objClass);
|
||||
}
|
||||
|
||||
bool IsSprite(void* obj) {
|
||||
const auto spriteClass = GetSpriteClass();
|
||||
if (!obj || !spriteClass) return false;
|
||||
|
||||
const auto objClass = Il2cppUtils::get_class_from_instance(obj);
|
||||
if (objClass == spriteClass) return true;
|
||||
|
||||
return UnityResolve::Invoke<bool>("il2cpp_class_is_assignable_from", spriteClass, objClass);
|
||||
}
|
||||
|
||||
Il2cppString* GetObjectName(void* obj) {
|
||||
if (!obj) return nullptr;
|
||||
|
||||
static auto Object_GetName = reinterpret_cast<Il2cppString * (*)(void*)>(
|
||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Object::GetName(UnityEngine.Object)"));
|
||||
return Object_GetName ? Object_GetName(obj) : nullptr;
|
||||
}
|
||||
|
||||
void SetDontUnloadUnusedAsset(void* obj) {
|
||||
if (!obj) return;
|
||||
|
||||
static auto Object_set_hideFlags = reinterpret_cast<void (*)(void*, int)>(
|
||||
Il2cppUtils::GetMethodPointer("UnityEngine.CoreModule.dll", "UnityEngine", "Object", "set_hideFlags"));
|
||||
if (Object_set_hideFlags) {
|
||||
Object_set_hideFlags(obj, 32);
|
||||
}
|
||||
}
|
||||
|
||||
void AddTexturePathCandidate(std::vector<std::filesystem::path>& candidates, const std::filesystem::path& path) {
|
||||
if (path.empty()) return;
|
||||
if (std::find(candidates.begin(), candidates.end(), path) == candidates.end()) {
|
||||
candidates.emplace_back(path);
|
||||
}
|
||||
if (!path.has_extension()) {
|
||||
auto pngPath = path;
|
||||
pngPath += ".png";
|
||||
if (std::find(candidates.begin(), candidates.end(), pngPath) == candidates.end()) {
|
||||
candidates.emplace_back(std::move(pngPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class TextureCategory {
|
||||
Image,
|
||||
Atlas,
|
||||
Others,
|
||||
};
|
||||
|
||||
std::string ToLowerAscii(std::string value) {
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
TextureCategory GetTextureCategory(const std::string& textureName) {
|
||||
const auto lowerName = ToLowerAscii(std::filesystem::path(textureName).filename().generic_string());
|
||||
if (lowerName.rfind("img", 0) == 0) {
|
||||
return TextureCategory::Image;
|
||||
}
|
||||
if (lowerName.rfind("sactx", 0) == 0) {
|
||||
return TextureCategory::Atlas;
|
||||
}
|
||||
return TextureCategory::Others;
|
||||
}
|
||||
|
||||
std::filesystem::path GetTextureCategoryDirName(TextureCategory category) {
|
||||
switch (category) {
|
||||
case TextureCategory::Image:
|
||||
return "image";
|
||||
case TextureCategory::Atlas:
|
||||
return "atlas";
|
||||
default:
|
||||
return "others";
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path GetTextureReplaceRoot() {
|
||||
return Local::GetBasePath() / "texture2d";
|
||||
}
|
||||
|
||||
std::filesystem::path GetTextureDumpRoot() {
|
||||
return Local::GetBasePath() / "dump-files" / "texture2d";
|
||||
}
|
||||
|
||||
std::filesystem::path GetTextureReplaceBase(const std::string& textureName) {
|
||||
return GetTextureReplaceRoot() / GetTextureCategoryDirName(GetTextureCategory(textureName));
|
||||
}
|
||||
|
||||
std::filesystem::path GetTextureDumpBase(const std::string& textureName) {
|
||||
return GetTextureDumpRoot() / GetTextureCategoryDirName(GetTextureCategory(textureName));
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& value, char delimiter) {
|
||||
std::vector<std::string> parts;
|
||||
size_t start = 0;
|
||||
while (start <= value.size()) {
|
||||
const auto end = value.find(delimiter, start);
|
||||
parts.emplace_back(value.substr(start, end == std::string::npos ? std::string::npos : end - start));
|
||||
if (end == std::string::npos) break;
|
||||
start = end + 1;
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
void AppendTextureCandidates(std::vector<std::filesystem::path>& target, std::vector<std::filesystem::path>&& source);
|
||||
std::string NormalizeLocalAssetKey(const std::filesystem::path& path);
|
||||
|
||||
bool IsHexHashPart(const std::string& value) {
|
||||
return value.size() == 8 && std::all_of(value.begin(), value.end(), [](unsigned char ch) {
|
||||
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
|
||||
});
|
||||
}
|
||||
|
||||
std::string GetPortableSactxTextureName(const std::string& objectName) {
|
||||
auto fileName = std::filesystem::path(objectName).filename().generic_string();
|
||||
if (fileName.ends_with(".png")) {
|
||||
fileName.resize(fileName.size() - 4);
|
||||
}
|
||||
|
||||
const auto parts = SplitString(fileName, '-');
|
||||
if (parts.size() < 5 || parts[0] != "sactx" || parts[2].find('x') == std::string::npos) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto atlasEnd = IsHexHashPart(parts.back()) ? parts.size() - 1 : parts.size();
|
||||
if (atlasEnd <= 4) return {};
|
||||
|
||||
std::string portableName = parts[0] + "-" + parts[1] + "-" + parts[2];
|
||||
for (size_t i = 4; i < atlasEnd; ++i) {
|
||||
portableName += "-" + parts[i];
|
||||
}
|
||||
return portableName;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::vector<std::filesystem::path>>> RecursiveTexturePathIndex{};
|
||||
|
||||
std::vector<std::filesystem::path> GetRecursiveTextureCandidates(const std::filesystem::path& basePath,
|
||||
const std::string& lookupName) {
|
||||
std::vector<std::filesystem::path> candidates;
|
||||
if (lookupName.empty() || !std::filesystem::exists(basePath)) return candidates;
|
||||
|
||||
const auto baseKey = NormalizeLocalAssetKey(basePath);
|
||||
auto& index = RecursiveTexturePathIndex[baseKey];
|
||||
if (index.empty()) {
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(basePath)) {
|
||||
if (!entry.is_regular_file()) continue;
|
||||
|
||||
const auto& path = entry.path();
|
||||
if (ToLowerAscii(path.extension().generic_string()) != ".png") continue;
|
||||
|
||||
const auto fileName = path.filename().generic_string();
|
||||
const auto stemName = path.stem().generic_string();
|
||||
index[fileName].emplace_back(path);
|
||||
if (stemName != fileName) {
|
||||
index[stemName].emplace_back(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto iter = index.find(lookupName); iter != index.end()) {
|
||||
candidates.insert(candidates.end(), iter->second.begin(), iter->second.end());
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> GetNamedTextureCandidates(const std::filesystem::path& assetName) {
|
||||
std::vector<std::filesystem::path> candidates;
|
||||
if (assetName.empty()) return candidates;
|
||||
|
||||
const auto basePath = GetTextureReplaceBase(assetName.filename().generic_string());
|
||||
AddTexturePathCandidate(candidates, basePath / assetName);
|
||||
if (assetName.has_parent_path()) {
|
||||
AddTexturePathCandidate(candidates, basePath / assetName.filename());
|
||||
}
|
||||
AppendTextureCandidates(candidates, GetRecursiveTextureCandidates(basePath, assetName.filename().generic_string()));
|
||||
|
||||
const auto portableAssetName = GetPortableSactxTextureName(assetName.filename().generic_string());
|
||||
if (!portableAssetName.empty()) {
|
||||
AddTexturePathCandidate(candidates, basePath / portableAssetName);
|
||||
AppendTextureCandidates(candidates, GetRecursiveTextureCandidates(basePath, portableAssetName));
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> GetSpriteTextureCandidates(const std::string& objectName) {
|
||||
std::vector<std::filesystem::path> candidates;
|
||||
if (objectName.empty()) return candidates;
|
||||
|
||||
auto safeObjectName = objectName;
|
||||
std::replace(safeObjectName.begin(), safeObjectName.end(), '|', '_');
|
||||
|
||||
const auto basePath = GetTextureReplaceBase(safeObjectName);
|
||||
AddTexturePathCandidate(candidates, basePath / objectName);
|
||||
if (safeObjectName != objectName) {
|
||||
AddTexturePathCandidate(candidates, basePath / safeObjectName);
|
||||
}
|
||||
AppendTextureCandidates(candidates, GetRecursiveTextureCandidates(basePath, safeObjectName));
|
||||
const auto portableObjectName = GetPortableSactxTextureName(safeObjectName);
|
||||
if (!portableObjectName.empty() && portableObjectName != objectName && portableObjectName != safeObjectName) {
|
||||
AddTexturePathCandidate(candidates, basePath / portableObjectName);
|
||||
AppendTextureCandidates(candidates, GetRecursiveTextureCandidates(basePath, portableObjectName));
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
void AppendTextureCandidates(std::vector<std::filesystem::path>& target, std::vector<std::filesystem::path>&& source) {
|
||||
target.insert(target.end(),
|
||||
std::make_move_iterator(source.begin()),
|
||||
std::make_move_iterator(source.end()));
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> GetSpriteAssetTextureCandidates(void* sprite, const std::string& assetName) {
|
||||
std::vector<std::filesystem::path> candidates;
|
||||
|
||||
const auto assetPath = std::filesystem::path(assetName);
|
||||
if (!assetName.empty()) {
|
||||
AppendTextureCandidates(candidates, GetSpriteTextureCandidates(assetPath.filename().generic_string()));
|
||||
}
|
||||
|
||||
if (sprite && Sprite_get_texture_Orig) {
|
||||
if (const auto texture = Sprite_get_texture_Orig(sprite)) {
|
||||
if (const auto textureName = GetObjectName(texture)) {
|
||||
AppendTextureCandidates(candidates, GetSpriteTextureCandidates(textureName->ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
std::string NormalizeLocalAssetKey(const std::filesystem::path& path) {
|
||||
auto key = path.lexically_normal().generic_string();
|
||||
std::replace(key.begin(), key.end(), '\\', '/');
|
||||
return key;
|
||||
}
|
||||
|
||||
std::string SanitizeDumpPathPart(std::string part) {
|
||||
constexpr std::string_view invalidChars = "<>:\"/\\|?*";
|
||||
if (part.empty() || part == "." || part == "..") return "_";
|
||||
|
||||
for (auto& ch : part) {
|
||||
if (static_cast<unsigned char>(ch) < 32 || invalidChars.find(ch) != std::string_view::npos) {
|
||||
ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
while (!part.empty() && (part.back() == '.' || part.back() == ' ')) {
|
||||
part.back() = '_';
|
||||
}
|
||||
return part.empty() ? "_" : part;
|
||||
}
|
||||
|
||||
std::filesystem::path SanitizeDumpSubPath(const std::filesystem::path& dumpSubDir) {
|
||||
std::filesystem::path safePath;
|
||||
for (const auto& part : dumpSubDir) {
|
||||
const auto partString = part.generic_string();
|
||||
if (partString.empty() || partString == "." || partString == ".."
|
||||
|| part == part.root_name() || part == part.root_directory()) {
|
||||
continue;
|
||||
}
|
||||
safePath /= SanitizeDumpPathPart(partString);
|
||||
}
|
||||
return safePath;
|
||||
}
|
||||
|
||||
bool DumpTexture2D(void* texture2D) {
|
||||
if (!IsTexture2D(texture2D)) return false;
|
||||
|
||||
const auto objectName = GetObjectName(texture2D);
|
||||
const auto textureName = objectName ? objectName->ToString() : std::string("texture");
|
||||
const auto dumpDir = GetTextureDumpBase(textureName);
|
||||
const auto dumpPath = dumpDir / (SanitizeDumpPathPart(textureName) + ".png");
|
||||
|
||||
if (std::filesystem::exists(dumpPath)) return true;
|
||||
|
||||
static auto Texture2D_get_width = [] {
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
const auto method = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, "get_width", 0) : nullptr;
|
||||
return method ? reinterpret_cast<int (*)(void*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto Texture2D_get_height = [] {
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
const auto method = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, "get_height", 0) : nullptr;
|
||||
return method ? reinterpret_cast<int (*)(void*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto Texture2D_ctor = [] {
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
const auto ctor = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, ".ctor", 2) : nullptr;
|
||||
return ctor ? reinterpret_cast<void (*)(void*, int, int)>(ctor->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto Texture2D_ReadPixels = [] {
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
const auto method = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, "ReadPixels", 3) : nullptr;
|
||||
return method ? reinterpret_cast<void (*)(void*, UnityResolve::UnityType::Rect, int, int)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto Texture2D_Apply = [] {
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
const auto method = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, "Apply", 0) : nullptr;
|
||||
return method ? reinterpret_cast<void (*)(void*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto RenderTexture_GetTemporary = [] {
|
||||
const auto renderTextureClass = Il2cppUtils::GetClass("UnityEngine.CoreModule.dll", "UnityEngine", "RenderTexture");
|
||||
const auto method = renderTextureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(renderTextureClass->address, "GetTemporary", 3) : nullptr;
|
||||
return method ? reinterpret_cast<void* (*)(int, int, int)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto RenderTexture_ReleaseTemporary = [] {
|
||||
const auto renderTextureClass = Il2cppUtils::GetClass("UnityEngine.CoreModule.dll", "UnityEngine", "RenderTexture");
|
||||
const auto method = renderTextureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(renderTextureClass->address, "ReleaseTemporary", 1) : nullptr;
|
||||
return method ? reinterpret_cast<void (*)(void*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto RenderTexture_get_active = [] {
|
||||
const auto renderTextureClass = Il2cppUtils::GetClass("UnityEngine.CoreModule.dll", "UnityEngine", "RenderTexture");
|
||||
const auto method = renderTextureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(renderTextureClass->address, "get_active", 0) : nullptr;
|
||||
return method ? reinterpret_cast<void* (*)()>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto RenderTexture_set_active = [] {
|
||||
const auto renderTextureClass = Il2cppUtils::GetClass("UnityEngine.CoreModule.dll", "UnityEngine", "RenderTexture");
|
||||
const auto method = renderTextureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(renderTextureClass->address, "set_active", 1) : nullptr;
|
||||
return method ? reinterpret_cast<void (*)(void*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto Graphics_Blit = [] {
|
||||
const auto graphicsClass = Il2cppUtils::GetClass("UnityEngine.CoreModule.dll", "UnityEngine", "Graphics");
|
||||
const auto method = graphicsClass ? Il2cppUtils::il2cpp_class_get_method_from_name(graphicsClass->address, "Blit", 2) : nullptr;
|
||||
return method ? reinterpret_cast<void (*)(void*, void*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto ImageConversion_EncodeToPNG = [] {
|
||||
using EncodeToPNGFn = void* (*)(void*);
|
||||
if (const auto icall = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.ImageConversion::EncodeToPNG(UnityEngine.Texture2D)")) {
|
||||
return reinterpret_cast<EncodeToPNGFn>(icall);
|
||||
}
|
||||
|
||||
for (const auto& assemblyName : {"UnityEngine.ImageConversionModule.dll", "UnityEngine.CoreModule.dll"}) {
|
||||
const auto assembly = UnityResolve::Get(assemblyName);
|
||||
const auto imageConversionClass = assembly ? assembly->Get("ImageConversion", "UnityEngine") : nullptr;
|
||||
const auto method = imageConversionClass
|
||||
? Il2cppUtils::il2cpp_class_get_method_from_name(imageConversionClass->address, "EncodeToPNG", 1)
|
||||
: nullptr;
|
||||
if (method) {
|
||||
return reinterpret_cast<EncodeToPNGFn>(method->methodPointer);
|
||||
}
|
||||
}
|
||||
return static_cast<EncodeToPNGFn>(nullptr);
|
||||
}();
|
||||
static auto File_WriteAllBytes = [] {
|
||||
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
||||
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "WriteAllBytes", 2) : nullptr;
|
||||
return method ? reinterpret_cast<void (*)(Il2cppString*, void*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
|
||||
if (!Texture2D_get_width || !Texture2D_get_height || !Texture2D_ctor || !Texture2D_ReadPixels
|
||||
|| !Texture2D_Apply || !RenderTexture_GetTemporary || !RenderTexture_ReleaseTemporary
|
||||
|| !RenderTexture_get_active || !RenderTexture_set_active || !Graphics_Blit
|
||||
|| !ImageConversion_EncodeToPNG || !File_WriteAllBytes) {
|
||||
Log::Error("DumpTexture2D failed: Unity texture dump API not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto width = Texture2D_get_width(texture2D);
|
||||
const auto height = Texture2D_get_height(texture2D);
|
||||
if (width <= 0 || height <= 0) return false;
|
||||
|
||||
void* renderTexture = nullptr;
|
||||
void* readableTexture = nullptr;
|
||||
void* previousActive = nullptr;
|
||||
const auto cleanup = [&] {
|
||||
if (RenderTexture_get_active && RenderTexture_set_active
|
||||
&& (previousActive || RenderTexture_get_active() == renderTexture)) {
|
||||
RenderTexture_set_active(previousActive);
|
||||
}
|
||||
if (renderTexture && RenderTexture_ReleaseTemporary) {
|
||||
RenderTexture_ReleaseTemporary(renderTexture);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
std::filesystem::create_directories(dumpDir);
|
||||
|
||||
renderTexture = RenderTexture_GetTemporary(width, height, 0);
|
||||
if (!renderTexture) {
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
Graphics_Blit(texture2D, renderTexture);
|
||||
previousActive = RenderTexture_get_active();
|
||||
RenderTexture_set_active(renderTexture);
|
||||
|
||||
readableTexture = UnityResolve::Invoke<void*>("il2cpp_object_new", GetTexture2DClass());
|
||||
if (!readableTexture) {
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
Texture2D_ctor(readableTexture, width, height);
|
||||
Texture2D_ReadPixels(readableTexture, UnityResolve::UnityType::Rect(0, 0, static_cast<float>(width), static_cast<float>(height)), 0, 0);
|
||||
Texture2D_Apply(readableTexture);
|
||||
|
||||
const auto pngBytes = ImageConversion_EncodeToPNG(readableTexture);
|
||||
if (!pngBytes) {
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
File_WriteAllBytes(Il2cppString::New(dumpPath.string()), pngBytes);
|
||||
Log::InfoFmt("Texture dumped: %s", dumpPath.string().c_str());
|
||||
cleanup();
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
cleanup();
|
||||
Log::ErrorFmt("DumpTexture2D failed: %s", ex.what());
|
||||
return false;
|
||||
}
|
||||
catch (...) {
|
||||
cleanup();
|
||||
Log::Error("DumpTexture2D failed: unknown error.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DumpTextureOrSpriteAsset(void* result) {
|
||||
if (!result) return;
|
||||
|
||||
if (IsTexture2D(result)) {
|
||||
DumpTexture2D(result);
|
||||
return;
|
||||
}
|
||||
if (IsSprite(result) && Sprite_get_texture_Orig) {
|
||||
if (const auto texture = Sprite_get_texture_Orig(result)) {
|
||||
DumpTexture2D(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* LoadLocalTexture2D(const std::filesystem::path& path) {
|
||||
if (!std::filesystem::is_regular_file(path)) return nullptr;
|
||||
|
||||
const auto cacheKey = NormalizeLocalAssetKey(path);
|
||||
if (const auto iter = LoadedLocalTextureHandles.find(cacheKey); iter != LoadedLocalTextureHandles.end()) {
|
||||
const auto cachedTexture = UnityResolve::Invoke<void*>("il2cpp_gchandle_get_target", iter->second);
|
||||
if (cachedTexture && IsNativeObjectAlive(cachedTexture)) {
|
||||
return cachedTexture;
|
||||
}
|
||||
|
||||
UnityResolve::Invoke<void>("il2cpp_gchandle_free", iter->second);
|
||||
LoadedLocalTextureHandles.erase(iter);
|
||||
}
|
||||
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
if (!textureClass) return nullptr;
|
||||
|
||||
static auto Texture2D_ctor = [] {
|
||||
const auto textureClass = GetTexture2DClass();
|
||||
const auto ctor = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, ".ctor", 2) : nullptr;
|
||||
return ctor ? reinterpret_cast<void (*)(void*, int, int)>(ctor->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto ImageConversion_LoadImage = [] {
|
||||
using LoadImageFn = bool (*)(void*, void*, bool);
|
||||
if (const auto icall = Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.ImageConversion::LoadImage(UnityEngine.Texture2D,System.Byte[],System.Boolean)")) {
|
||||
return reinterpret_cast<LoadImageFn>(icall);
|
||||
}
|
||||
|
||||
for (const auto& assemblyName : {"UnityEngine.ImageConversionModule.dll", "UnityEngine.CoreModule.dll"}) {
|
||||
const auto assembly = UnityResolve::Get(assemblyName);
|
||||
const auto imageConversionClass = assembly ? assembly->Get("ImageConversion", "UnityEngine") : nullptr;
|
||||
const auto method = imageConversionClass
|
||||
? Il2cppUtils::il2cpp_class_get_method_from_name(imageConversionClass->address, "LoadImage", 3)
|
||||
: nullptr;
|
||||
if (method) {
|
||||
return reinterpret_cast<LoadImageFn>(method->methodPointer);
|
||||
}
|
||||
}
|
||||
return static_cast<LoadImageFn>(nullptr);
|
||||
}();
|
||||
static auto File_ReadAllBytes = [] {
|
||||
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
||||
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
||||
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
|
||||
if (!Texture2D_ctor || !ImageConversion_LoadImage || !File_ReadAllBytes) {
|
||||
Log::Error("LoadLocalTexture2D failed: Unity Texture2D/ImageConversion/File API not found.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto fileBytes = File_ReadAllBytes(Il2cppString::New(path.string()));
|
||||
if (!fileBytes) return nullptr;
|
||||
|
||||
const auto texture = UnityResolve::Invoke<void*>("il2cpp_object_new", textureClass);
|
||||
Texture2D_ctor(texture, 2, 2);
|
||||
if (!ImageConversion_LoadImage(texture, fileBytes, false)) {
|
||||
Log::ErrorFmt("LoadLocalTexture2D failed: %s", path.string().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SetDontUnloadUnusedAsset(texture);
|
||||
LoadedLocalTextureHandles.emplace(cacheKey, UnityResolve::Invoke<uint32_t>("il2cpp_gchandle_new", texture, false));
|
||||
Log::InfoFmt("Texture replaced from local file: %s", path.string().c_str());
|
||||
return texture;
|
||||
}
|
||||
|
||||
void* LoadLocalTexture2DFromCandidates(const std::vector<std::filesystem::path>& candidates) {
|
||||
for (const auto& candidate : candidates) {
|
||||
if (auto texture = LoadLocalTexture2D(candidate)) {
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ApplyLocalImageToTexture2D(void* texture2D, const std::filesystem::path& path) {
|
||||
if (!IsTexture2D(texture2D) || !std::filesystem::is_regular_file(path)) return false;
|
||||
|
||||
auto cacheKey = NormalizeLocalAssetKey(path)
|
||||
+ "|" + std::to_string(reinterpret_cast<std::uintptr_t>(texture2D));
|
||||
if (AppliedLocalTextureKeys.contains(cacheKey)) return true;
|
||||
|
||||
static auto ImageConversion_LoadImage = [] {
|
||||
using LoadImageFn = bool (*)(void*, void*, bool);
|
||||
if (const auto icall = Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.ImageConversion::LoadImage(UnityEngine.Texture2D,System.Byte[],System.Boolean)")) {
|
||||
return reinterpret_cast<LoadImageFn>(icall);
|
||||
}
|
||||
|
||||
for (const auto& assemblyName : {"UnityEngine.ImageConversionModule.dll", "UnityEngine.CoreModule.dll"}) {
|
||||
const auto assembly = UnityResolve::Get(assemblyName);
|
||||
const auto imageConversionClass = assembly ? assembly->Get("ImageConversion", "UnityEngine") : nullptr;
|
||||
const auto method = imageConversionClass
|
||||
? Il2cppUtils::il2cpp_class_get_method_from_name(imageConversionClass->address, "LoadImage", 3)
|
||||
: nullptr;
|
||||
if (method) {
|
||||
return reinterpret_cast<LoadImageFn>(method->methodPointer);
|
||||
}
|
||||
}
|
||||
return static_cast<LoadImageFn>(nullptr);
|
||||
}();
|
||||
static auto File_ReadAllBytes = [] {
|
||||
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
||||
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
||||
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
|
||||
if (!ImageConversion_LoadImage || !File_ReadAllBytes) {
|
||||
Log::Error("ApplyLocalImageToTexture2D failed: Unity ImageConversion/File API not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto fileBytes = File_ReadAllBytes(Il2cppString::New(path.string()));
|
||||
if (!fileBytes) return false;
|
||||
|
||||
if (!ImageConversion_LoadImage(texture2D, fileBytes, false)) {
|
||||
Log::ErrorFmt("ApplyLocalImageToTexture2D failed: %s", path.string().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
SetDontUnloadUnusedAsset(texture2D);
|
||||
AppliedLocalTextureKeys.emplace(std::move(cacheKey));
|
||||
Log::InfoFmt("Texture replaced in-place from local file: %s", path.string().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApplyLocalImageToTexture2DFromCandidates(void* texture2D, const std::vector<std::filesystem::path>& candidates) {
|
||||
for (const auto& candidate : candidates) {
|
||||
if (ApplyLocalImageToTexture2D(texture2D, candidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReplaceSpriteTextureInPlace(void* sprite, const std::vector<std::filesystem::path>& candidates) {
|
||||
if (!sprite || !Sprite_get_texture_Orig) return false;
|
||||
|
||||
const auto texture = Sprite_get_texture_Orig(sprite);
|
||||
if (!IsTexture2D(texture)) return false;
|
||||
|
||||
return ApplyLocalImageToTexture2DFromCandidates(texture, candidates);
|
||||
}
|
||||
|
||||
void* ReplaceTextureOrSpriteAsset(void* result, const std::string& assetName) {
|
||||
if (!Config::replaceTexture && !Config::dumpRuntimeTexture) return result;
|
||||
|
||||
if (Config::dumpRuntimeTexture) {
|
||||
DumpTextureOrSpriteAsset(result);
|
||||
}
|
||||
if (!Config::replaceTexture) return result;
|
||||
|
||||
if (IsSprite(result)) {
|
||||
if (ReplaceSpriteTextureInPlace(result, GetSpriteAssetTextureCandidates(result, assetName))) {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result && !IsTexture2D(result)) return result;
|
||||
|
||||
if (auto localTexture = LoadLocalTexture2DFromCandidates(GetNamedTextureCandidates(std::filesystem::path(assetName)))) {
|
||||
return localTexture;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void* ReplaceTextureOrSpriteByObjectName(void* result) {
|
||||
if ((!Config::replaceTexture && !Config::dumpRuntimeTexture) || !result) return result;
|
||||
|
||||
const auto objectName = GetObjectName(result);
|
||||
if (!objectName) return result;
|
||||
|
||||
const auto assetPath = std::filesystem::path(objectName->ToString());
|
||||
if (Config::dumpRuntimeTexture) {
|
||||
DumpTextureOrSpriteAsset(result);
|
||||
}
|
||||
if (!Config::replaceTexture) return result;
|
||||
|
||||
if (IsSprite(result)) {
|
||||
std::vector<std::filesystem::path> candidates;
|
||||
AppendTextureCandidates(candidates, GetSpriteTextureCandidates(objectName->ToString()));
|
||||
if (ReplaceSpriteTextureInPlace(result, candidates)) {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (IsTexture2D(result)) {
|
||||
if (auto localTexture = LoadLocalTexture2DFromCandidates(GetNamedTextureCandidates(assetPath))) {
|
||||
return localTexture;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReplaceAllAssetTextures(void* allAssets) {
|
||||
if ((!Config::replaceTexture && !Config::dumpRuntimeTexture) || !allAssets) return;
|
||||
|
||||
auto assets = reinterpret_cast<UnityResolve::UnityType::Array<void*>*>(allAssets);
|
||||
for (std::uintptr_t i = 0; i < assets->max_length; ++i) {
|
||||
auto asset = assets->At(static_cast<unsigned int>(i));
|
||||
auto replacedAsset = ReplaceTextureOrSpriteByObjectName(asset);
|
||||
if (replacedAsset != asset) {
|
||||
assets->At(static_cast<unsigned int>(i)) = replacedAsset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* ReplaceSpriteAssetByTextureName(void* sprite) {
|
||||
if (!Config::replaceTexture || !sprite) return sprite;
|
||||
|
||||
if (!IsSprite(sprite)) {
|
||||
return sprite;
|
||||
}
|
||||
|
||||
if (ReplaceSpriteTextureInPlace(sprite, GetSpriteAssetTextureCandidates(sprite, ""))) {
|
||||
return sprite;
|
||||
}
|
||||
return sprite;
|
||||
}
|
||||
|
||||
void* ReplaceSpriteTexture(void* texture2D) {
|
||||
if ((!Config::replaceTexture && !Config::dumpRuntimeTexture) || !IsTexture2D(texture2D)) return texture2D;
|
||||
|
||||
const auto objectName = GetObjectName(texture2D);
|
||||
if (!objectName) return texture2D;
|
||||
|
||||
if (Config::dumpRuntimeTexture) {
|
||||
DumpTexture2D(texture2D);
|
||||
}
|
||||
if (!Config::replaceTexture) return texture2D;
|
||||
|
||||
if (ApplyLocalImageToTexture2DFromCandidates(texture2D, GetSpriteTextureCandidates(objectName->ToString()))) {
|
||||
return texture2D;
|
||||
}
|
||||
return texture2D;
|
||||
}
|
||||
|
||||
void* ResolveSpriteGetTextureHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Sprite::get_texture(UnityEngine.Sprite)")) {
|
||||
return addr;
|
||||
}
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Sprite::get_texture()")) {
|
||||
return addr;
|
||||
}
|
||||
return Il2cppUtils::GetMethodPointer("UnityEngine.CoreModule.dll", "UnityEngine", "Sprite", "get_texture");
|
||||
}
|
||||
|
||||
void* ResolveAssetBundleLoadAssetHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.AssetBundle::LoadAsset_Internal(System.String,System.Type)")) {
|
||||
return addr;
|
||||
}
|
||||
return Il2cppUtils::GetMethodPointer("UnityEngine.AssetBundleModule.dll", "UnityEngine", "AssetBundle",
|
||||
"LoadAsset_Internal", {"System.String", "System.Type"});
|
||||
}
|
||||
|
||||
void* ResolveAssetBundleLoadAssetAsyncHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.AssetBundle::LoadAssetAsync_Internal(System.String,System.Type)")) {
|
||||
return addr;
|
||||
}
|
||||
return Il2cppUtils::GetMethodPointer("UnityEngine.AssetBundleModule.dll", "UnityEngine", "AssetBundle",
|
||||
"LoadAssetAsync_Internal", {"System.String", "System.Type"});
|
||||
}
|
||||
|
||||
void* ResolveAssetBundleRequestResultHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.AssetBundleRequest::GetResult()")) {
|
||||
return addr;
|
||||
}
|
||||
return Il2cppUtils::GetMethodPointer("UnityEngine.AssetBundleModule.dll", "UnityEngine", "AssetBundleRequest", "GetResult");
|
||||
}
|
||||
|
||||
void* ResolveAssetBundleRequestAssetHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.AssetBundleRequest::get_asset()")) {
|
||||
return addr;
|
||||
}
|
||||
return Il2cppUtils::GetMethodPointer("UnityEngine.AssetBundleModule.dll", "UnityEngine", "AssetBundleRequest", "get_asset");
|
||||
}
|
||||
|
||||
void* ResolveAssetBundleRequestAllAssetsHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.AssetBundleRequest::get_allAssets()")) {
|
||||
return addr;
|
||||
}
|
||||
return Il2cppUtils::GetMethodPointer("UnityEngine.AssetBundleModule.dll", "UnityEngine", "AssetBundleRequest", "get_allAssets");
|
||||
}
|
||||
|
||||
void* ResolveResourcesLoadHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.ResourcesAPIInternal::Load(System.String,System.Type)")) {
|
||||
return addr;
|
||||
}
|
||||
return Il2cppUtils::GetMethodPointer("UnityEngine.CoreModule.dll", "UnityEngine", "ResourcesAPIInternal",
|
||||
"Load", {"System.String", "System.Type"});
|
||||
}
|
||||
|
||||
std::unordered_set<void*> updatedFontPtrs{};
|
||||
void UpdateFont(void* TMP_Textself) {
|
||||
if (!Config::replaceFont) return;
|
||||
|
|
@ -1688,12 +2523,18 @@ namespace GakumasLocal::HookMain {
|
|||
UnityResolve::Mode::Il2Cpp, Config::lazyInit);
|
||||
#endif
|
||||
|
||||
ADD_HOOK(AssetBundle_LoadAssetAsync, Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.AssetBundle::LoadAssetAsync_Internal(System.String,System.Type)"));
|
||||
ADD_HOOK(AssetBundleRequest_GetResult, Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.AssetBundleRequest::GetResult()"));
|
||||
ADD_HOOK(Resources_Load, Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.ResourcesAPIInternal::Load(System.String,System.Type)"));
|
||||
// Temporarily isolate texture replacement to CanvasRenderer.SetTexture only.
|
||||
// ADD_HOOK(AssetBundle_LoadAsset, ResolveAssetBundleLoadAssetHookAddress());
|
||||
// ADD_HOOK(AssetBundle_LoadAssetAsync, ResolveAssetBundleLoadAssetAsyncHookAddress());
|
||||
// ADD_HOOK(AssetBundleRequest_GetResult, ResolveAssetBundleRequestResultHookAddress());
|
||||
// ADD_HOOK(AssetBundleRequest_get_asset, ResolveAssetBundleRequestAssetHookAddress());
|
||||
// ADD_HOOK(AssetBundleRequest_get_allAssets, ResolveAssetBundleRequestAllAssetsHookAddress());
|
||||
// ADD_HOOK(Resources_Load, ResolveResourcesLoadHookAddress());
|
||||
// ADD_HOOK(Sprite_get_texture, ResolveSpriteGetTextureHookAddress());
|
||||
// ADD_HOOK(Image_set_sprite, Il2cppUtils::GetMethodPointer("UnityEngine.UI.dll", "UnityEngine.UI", "Image", "set_sprite"));
|
||||
// ADD_HOOK(Image_set_overrideSprite, Il2cppUtils::GetMethodPointer("UnityEngine.UI.dll", "UnityEngine.UI", "Image", "set_overrideSprite"));
|
||||
ADD_HOOK(CanvasRenderer_SetTexture, Il2cppUtils::GetMethodPointer("UnityEngine.UIModule.dll", "UnityEngine", "CanvasRenderer", "SetTexture", {"UnityEngine.Texture"}));
|
||||
// ADD_HOOK(SpriteRenderer_set_sprite, Il2cppUtils::GetMethodPointer("UnityEngine.CoreModule.dll", "UnityEngine", "SpriteRenderer", "set_sprite"));
|
||||
|
||||
ADD_HOOK(I18nHelper_SetUpI18n, Il2cppUtils::GetMethodPointer("quaunity-ui.Runtime.dll", "Qua.UI",
|
||||
"I18nHelper", "SetUpI18n"));
|
||||
|
|
@ -1987,7 +2828,7 @@ namespace GakumasLocal::HookMain {
|
|||
UnityResolveProgress::startInit = true;
|
||||
UnityResolveProgress::assembliesProgress.total = 2;
|
||||
UnityResolveProgress::assembliesProgress.current = 1;
|
||||
UnityResolveProgress::classProgress.total = 36;
|
||||
UnityResolveProgress::classProgress.total = 43;
|
||||
UnityResolveProgress::classProgress.current = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ namespace GakumasLocal::Config {
|
|||
bool enabled = true;
|
||||
bool lazyInit = true;
|
||||
bool replaceFont = true;
|
||||
bool replaceTexture = true;
|
||||
bool forceExportResource = true;
|
||||
bool textTest = false;
|
||||
bool useMasterTrans = true;
|
||||
int gameOrientation = 0;
|
||||
bool dumpText = false;
|
||||
bool dumpRuntimeTexture = false;
|
||||
bool enableFreeCamera = false;
|
||||
int targetFrameRate = 0;
|
||||
bool unlockAllLive = false;
|
||||
|
|
@ -66,11 +68,13 @@ namespace GakumasLocal::Config {
|
|||
GetConfigItem(enabled);
|
||||
GetConfigItem(lazyInit);
|
||||
GetConfigItem(replaceFont);
|
||||
GetConfigItem(replaceTexture);
|
||||
GetConfigItem(forceExportResource);
|
||||
GetConfigItem(gameOrientation);
|
||||
GetConfigItem(textTest);
|
||||
GetConfigItem(useMasterTrans);
|
||||
GetConfigItem(dumpText);
|
||||
GetConfigItem(dumpRuntimeTexture);
|
||||
GetConfigItem(targetFrameRate);
|
||||
GetConfigItem(enableFreeCamera);
|
||||
GetConfigItem(unlockAllLive);
|
||||
|
|
@ -122,11 +126,13 @@ namespace GakumasLocal::Config {
|
|||
SetConfigItem(enabled);
|
||||
SetConfigItem(lazyInit);
|
||||
SetConfigItem(replaceFont);
|
||||
SetConfigItem(replaceTexture);
|
||||
SetConfigItem(forceExportResource);
|
||||
SetConfigItem(gameOrientation);
|
||||
SetConfigItem(textTest);
|
||||
SetConfigItem(useMasterTrans);
|
||||
SetConfigItem(dumpText);
|
||||
SetConfigItem(dumpRuntimeTexture);
|
||||
SetConfigItem(targetFrameRate);
|
||||
SetConfigItem(enableFreeCamera);
|
||||
SetConfigItem(unlockAllLive);
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ namespace GakumasLocal::Config {
|
|||
extern bool enabled;
|
||||
extern bool lazyInit;
|
||||
extern bool replaceFont;
|
||||
extern bool replaceTexture;
|
||||
extern bool forceExportResource;
|
||||
extern int gameOrientation;
|
||||
extern bool textTest;
|
||||
extern bool useMasterTrans;
|
||||
extern bool dumpText;
|
||||
extern bool dumpRuntimeTexture;
|
||||
extern bool enableFreeCamera;
|
||||
extern int targetFrameRate;
|
||||
extern bool unlockAllLive;
|
||||
|
|
|
|||
|
|
@ -2,19 +2,23 @@
|
|||
#include "i18nData/strings_en.hpp"
|
||||
#include "i18nData/strings_ja.hpp"
|
||||
#include "i18nData/strings_zh-rCN.hpp"
|
||||
#include "i18nData/strings_zh-rTW.hpp"
|
||||
|
||||
|
||||
namespace GkmsGUII18n {
|
||||
const LANGID localLanguage = GetUserDefaultUILanguage();
|
||||
|
||||
std::unordered_set<LANGID> sChineseLangIds{ { 0x0004, 0x0804, 0x1004 } }; // zh-Hans, zh-CN, zh-SG
|
||||
// std::unordered_set<LANGID> tChineseLangIds{ { 0x0404, 0x0c04, 0x1404, 0x048E } }; // zh-TW, zh-HK, zh-MO, zh-yue-HK
|
||||
std::unordered_set<LANGID> tChineseLangIds{ { 0x0404, 0x0c04, 0x1404, 0x048E } }; // zh-TW, zh-HK, zh-MO, zh-yue-HK
|
||||
std::unordered_set<LANGID> jpnLangIds{ { 0x0011, 0x0411 } }; // ja, ja-JP
|
||||
|
||||
std::unordered_map<std::string, std::string> GetI18nData() {
|
||||
if (sChineseLangIds.contains(localLanguage)) {
|
||||
return I18nData::i18nData_zh_rCN;
|
||||
}
|
||||
else if (tChineseLangIds.contains(localLanguage)) {
|
||||
return I18nData::i18nData_zh_rTW;
|
||||
}
|
||||
else if (jpnLangIds.contains(localLanguage)) {
|
||||
return I18nData::i18nData_ja;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ namespace GkmsGUILoop {
|
|||
if (ImGui::Begin("Gakumas Plugin Config")) {
|
||||
ImGui::Text("Plugin Version: %s", PLUGIN_VERSION);
|
||||
ImGui::Text("Resource Version: %s", GkmsResourceUpdate::GetCurrentResourceVersion(true).c_str());
|
||||
ImGui::Text("%s: %s", ts("texture_version"), GkmsResourceUpdate::GetCurrentTextureVersion(true).c_str());
|
||||
|
||||
if (ImGui::Button("Reload Config And Translation Data")) {
|
||||
g_reload_all_data();
|
||||
|
|
@ -169,6 +170,7 @@ namespace GkmsGUILoop {
|
|||
|
||||
ImGui::Checkbox(ts("lazy_init"), &Config::lazyInit);
|
||||
ImGui::Checkbox(ts("replace_font"), &Config::replaceFont);
|
||||
ImGui::Checkbox(ts("replace_texture"), &Config::replaceTexture);
|
||||
|
||||
ImGui::Unindent(indentWidth);
|
||||
}
|
||||
|
|
@ -207,6 +209,24 @@ namespace GkmsGUILoop {
|
|||
}
|
||||
}
|
||||
|
||||
if (Config::replaceTexture) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("%s: %s", ts("downloaded_texture_version"), GkmsResourceUpdate::GetCurrentTextureVersion(true).c_str());
|
||||
ImGui::Checkbox(ts("check_texture_from_api"), &g_useAPITextureAssets);
|
||||
if (g_useAPITextureAssets) {
|
||||
ImGui::Checkbox(ts("del_texture_remote_after_update"), &g_delTextureRemoteAfterUpdate);
|
||||
InputTextString(ts("texture_api_addr"), &g_useAPITextureAssetsURL);
|
||||
if (!downloading && ImGui::Button((std::string(ts("check_update")) + "##APITexture").c_str())) {
|
||||
GkmsResourceUpdate::CheckTextureUpdateFromAPI(true);
|
||||
}
|
||||
if (downloading) {
|
||||
ImGui::ProgressBar(downloadProgress);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Downloading");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Unindent(indentWidth);
|
||||
}
|
||||
|
||||
|
|
@ -308,6 +328,7 @@ namespace GkmsGUILoop {
|
|||
ImGui::Checkbox(ts("useMasterDBTrans"), &Config::useMasterTrans);
|
||||
ImGui::Checkbox(ts("text_hook_test_mode"), &Config::textTest);
|
||||
ImGui::Checkbox(ts("export_text"), &Config::dumpText);
|
||||
ImGui::Checkbox(ts("dump_runtime_texture"), &Config::dumpRuntimeTexture);
|
||||
ImGui::Checkbox(ts("login_as_ios"), &Config::loginAsIOS);
|
||||
|
||||
ImGui::Unindent(indentWidth);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace I18nData {
|
|||
{ "gakumas_localify", "Gakumas Localify" },
|
||||
{ "enable_plugin", "Enable Plugin (Not Hot Reloadable)" },
|
||||
{ "replace_font", "Replace Font" },
|
||||
{ "replace_texture", "Replace Texture" },
|
||||
{ "lazy_init", "Fast Initialization (Lazy loading)" },
|
||||
{ "enable_free_camera", "Enable Free Camera" },
|
||||
{ "start_game", "Start Game / Hot Reload Config" },
|
||||
|
|
@ -27,6 +28,7 @@ namespace I18nData {
|
|||
{ "text_hook_test_mode", "Text Hook Test Mode" },
|
||||
{ "useMasterDBTrans", "Enable MasterDB Localization" },
|
||||
{ "export_text", "Export Text" },
|
||||
{ "dump_runtime_texture", "Dump Runtime Texture" },
|
||||
{ "force_export_resource", "Force Update Resource" },
|
||||
{ "login_as_ios", "Login as iOS" },
|
||||
{ "max_high", "Ultra" },
|
||||
|
|
@ -88,13 +90,18 @@ namespace I18nData {
|
|||
{ "cancel", "Cancel" },
|
||||
{ "ok", "OK" },
|
||||
{ "downloaded_resource_version", "Downloaded Version" },
|
||||
{ "downloaded_texture_version", "Downloaded Texture Version" },
|
||||
{ "del_remote_after_update", "Delete Cache File After Update" },
|
||||
{ "del_texture_remote_after_update", "Delete Cache File After Update" },
|
||||
{ "warning", "Warning" },
|
||||
{ "install", "Install" },
|
||||
{ "installing", "Installing" },
|
||||
{ "check_resource_from_api", "Check Resource Update From API" },
|
||||
{ "check_texture_from_api", "Check Texture Update From API" },
|
||||
{ "api_addr", "API Address(Github Latest Release API)" },
|
||||
{ "texture_api_addr", "Texture API Address(Github Latest Release API)" },
|
||||
{ "check_update", "Check" },
|
||||
{ "texture_version", "Texture Version" },
|
||||
{ "translation_resource_update", "Translation Resource Update" },
|
||||
{ "game_patch", "Game Patch" },
|
||||
{ "patch_mode", "Patch Mode" },
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ namespace I18nData {
|
|||
{ "character_counter_pattern", "%1$d/%2$d" },
|
||||
{ "check_built_in_resource", "内蔵アセットのアップデートを確認" },
|
||||
{ "check_resource_from_api", "リソースアップデートを API から確認" },
|
||||
{ "check_texture_from_api", "テクスチャアップデートを API から確認" },
|
||||
{ "check_update", "確認" },
|
||||
{ "clear_text_end_icon_content_description", "テキストを消去" },
|
||||
{ "close_drawer", "ナビゲーションメニューを閉じる" },
|
||||
|
|
@ -48,9 +49,11 @@ namespace I18nData {
|
|||
{ "default_error_message", "入力が無効です" },
|
||||
{ "default_popup_window_title", "ポップアップウィンドウ" },
|
||||
{ "del_remote_after_update", "キャッシュファイルをアップデート後に削除" },
|
||||
{ "del_texture_remote_after_update", "キャッシュファイルをアップデート後に削除" },
|
||||
{ "delete_plugin_resource", "プラグインリソースを削除" },
|
||||
{ "download", "ダウンロード" },
|
||||
{ "downloaded_resource_version", "ダウンロードされたバージョン" },
|
||||
{ "downloaded_texture_version", "ダウンロードされたテクスチャバージョン" },
|
||||
{ "dropdown_menu", "ドロップダウンメニュー" },
|
||||
{ "enable_breast_param", "胸のパラメーターを有効化" },
|
||||
{ "enable_free_camera", "フリーカメラを有効化" },
|
||||
|
|
@ -58,6 +61,7 @@ namespace I18nData {
|
|||
{ "error_a11y_label", "エラー: 無効" },
|
||||
{ "error_icon_content_description", "エラー" },
|
||||
{ "export_text", "テキストをエクスポート" },
|
||||
{ "dump_runtime_texture", "ランタイムテクスチャをダンプ" },
|
||||
{ "exposed_dropdown_menu_content_description", "ドロップダウンメニューを表示" },
|
||||
{ "force_export_resource", "リソースのアップデートを強制する" },
|
||||
{ "login_as_ios", "iOSとしてログイン" },
|
||||
|
|
@ -108,6 +112,7 @@ namespace I18nData {
|
|||
{ "range_start", "範囲の開始" },
|
||||
{ "renderscale", "RenderScale (0.5/0.59/0.67/0.77/1.0)" },
|
||||
{ "replace_font", "フォントを置換する" },
|
||||
{ "replace_texture", "テクスチャを置換する" },
|
||||
{ "reserve_patched", "パッチ済みの APK を予約する" },
|
||||
{ "resource_settings", "リソース設定" },
|
||||
{ "resource_url", "リソース URL" },
|
||||
|
|
@ -128,6 +133,8 @@ namespace I18nData {
|
|||
{ "useMasterDBTrans", "MasterDB をローカライズする" },
|
||||
{ "translation_repository", "翻訳のリポジトリ" },
|
||||
{ "translation_resource_update", "翻訳リソースをアップデート" },
|
||||
{ "texture_api_addr", "テクスチャ API アドレス (GitHub の最新リリース API)" },
|
||||
{ "texture_version", "Texture version" },
|
||||
{ "unlockAllLive", "すべてのライブを開放" },
|
||||
{ "unlockAllLiveCostume", "すべてのライブ衣装を開放" },
|
||||
{ "useCustomeGraphicSettings", "カスタムグラフィック設定を使用する" },
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace I18nData {
|
|||
{ "gakumas_localify", "Gakumas Localify" },
|
||||
{ "enable_plugin", "启用插件 (不可热重载)" },
|
||||
{ "replace_font", "替换字体" },
|
||||
{ "replace_texture", "替换贴图" },
|
||||
{ "lazy_init", "快速初始化(懒加载配置)" },
|
||||
{ "enable_free_camera", "启用自由视角(可热重载; 需使用实体键盘)" },
|
||||
{ "start_game", "以上述配置启动游戏/重载配置" },
|
||||
|
|
@ -27,6 +28,7 @@ namespace I18nData {
|
|||
{ "text_hook_test_mode", "文本 hook 测试模式" },
|
||||
{ "useMasterDBTrans", "使用 MasterDB 本地化" },
|
||||
{ "export_text", "导出文本" },
|
||||
{ "dump_runtime_texture", "导出运行时贴图" },
|
||||
{ "force_export_resource", "启动后强制导出资源" },
|
||||
{ "login_as_ios", "以 iOS 登陆" },
|
||||
{ "max_high", "极高" },
|
||||
|
|
@ -88,13 +90,18 @@ namespace I18nData {
|
|||
{ "cancel", "取消" },
|
||||
{ "ok", "确定" },
|
||||
{ "downloaded_resource_version", "已下载资源版本" },
|
||||
{ "downloaded_texture_version", "已下载贴图资源版本" },
|
||||
{ "del_remote_after_update", "替换文件后删除下载缓存" },
|
||||
{ "del_texture_remote_after_update", "替换文件后删除下载缓存" },
|
||||
{ "warning", "注意" },
|
||||
{ "install", "安装" },
|
||||
{ "installing", "安装中" },
|
||||
{ "check_resource_from_api", "从服务器检查热更新资源" },
|
||||
{ "check_texture_from_api", "从服务器检查贴图资源更新" },
|
||||
{ "api_addr", "API 地址(Github Latest Release API)" },
|
||||
{ "texture_api_addr", "贴图 API 地址(Github Latest Release API)" },
|
||||
{ "check_update", "检查更新" },
|
||||
{ "texture_version", "Texture version" },
|
||||
{ "translation_resource_update", "翻译资源更新" },
|
||||
{ "game_patch", "游戏修补" },
|
||||
{ "patch_mode", "修补模式" },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace I18nData {
|
||||
static const std::unordered_map<std::string, std::string> i18nData_zh_rTW = {
|
||||
{ "local_file_already_latest", "本機檔案已經是最新版本,是否繼續更新?" },
|
||||
{ "dmmUnlockSize", "解鎖視窗大小" },
|
||||
{ "dmmUnlockSizeHelp", "可隨意拖動視窗大小。使用 F11 切換全螢幕。" },
|
||||
|
||||
{ "app_name", "Gakumas Localify" },
|
||||
{ "gakumas_localify", "Gakumas Localify" },
|
||||
{ "enable_plugin", "啟用插件 (不可熱重載)" },
|
||||
{ "replace_font", "替換字體" },
|
||||
{ "replace_texture", "替換貼圖" },
|
||||
{ "lazy_init", "快速初始化(懶人設定)" },
|
||||
{ "enable_free_camera", "啟用自由視角(可熱重載; 需使用實體鍵盤)" },
|
||||
{ "start_game", "以上述設定啟動遊戲/重載設定" },
|
||||
{ "setFpsTitle", "最大 FPS (0 為保持遊戲原設定)" },
|
||||
{ "unlockAllLive", "解鎖所有 Live" },
|
||||
{ "unlockAllLiveCostume", "解鎖所有 Live 服裝" },
|
||||
{ "liveUseCustomeDress", "Live 使用自定義角色" },
|
||||
{ "live_costume_head_id", "Live 自定義頭部 ID (例: costume_head_hski-cstm-0002)" },
|
||||
{ "live_custome_dress_id", "Live 自定義服裝 ID (例: hski-cstm-0002)" },
|
||||
{ "useCustomeGraphicSettings", "使用自定義畫質設定" },
|
||||
{ "renderscale", "RenderScale (0.5/0.59/0.67/0.77/1.0)" },
|
||||
{ "text_hook_test_mode", "文本 hook 測試模式" },
|
||||
{ "useMasterDBTrans", "使用 MasterDB 翻譯" },
|
||||
{ "export_text", "導出文本" },
|
||||
{ "dump_runtime_texture", "導出運行時貼圖" },
|
||||
{ "force_export_resource", "啟動後強制導出資源" },
|
||||
{ "login_as_ios", "模擬以 iOS 登入" },
|
||||
{ "max_high", "極高" },
|
||||
{ "very_high", "超高" },
|
||||
{ "hign", "高" },
|
||||
{ "middle", "中" },
|
||||
{ "low", "低" },
|
||||
{ "orientation_orig", "原版" },
|
||||
{ "orientation_portrait", "豎屏" },
|
||||
{ "orientation_landscape", "橫屏" },
|
||||
{ "orientation_lock", "方向鎖定" },
|
||||
{ "enable_breast_param", "啟用胸部參數" },
|
||||
{ "damping", "阻尼 (Damping)" },
|
||||
{ "stiffness", "剛度 (Stiffness)" },
|
||||
{ "spring", "彈簧係數 (Spring)" },
|
||||
{ "pendulum", "鐘擺係數 (Pendulum)" },
|
||||
{ "pendulumrange", "鐘擺範圍 (PendulumRange)" },
|
||||
{ "average", "Average" },
|
||||
{ "rootweight", "RootWeight" },
|
||||
{ "uselimit_0_1", "範圍限制倍率 (0 為不限制, 1 為原版)" },
|
||||
{ "usearmcorrection", "使用手臂矯正" },
|
||||
{ "isdirty", "IsDirty" },
|
||||
{ "usescale", "應用縮放" },
|
||||
{ "breast_scale", "胸部縮放倍率" },
|
||||
{ "uselimitmultiplier", "啟用範圍限制倍率" },
|
||||
{ "axisx_x", "axisX.x" },
|
||||
{ "axisy_x", "axisY.x" },
|
||||
{ "axisz_x", "axisZ.x" },
|
||||
{ "axisx_y", "axisX.y" },
|
||||
{ "axisy_y", "axisY.y" },
|
||||
{ "axisz_y", "axisZ.y" },
|
||||
{ "basic_settings", "基本設定" },
|
||||
{ "graphic_settings", "畫面設定" },
|
||||
{ "camera_settings", "攝影機設定" },
|
||||
{ "test_mode_live", "測試模式 - LIVE" },
|
||||
{ "debug_settings", "調試設定" },
|
||||
{ "breast_param", "胸部參數" },
|
||||
{ "about", "關於" },
|
||||
{ "home", "主頁" },
|
||||
{ "advanced_settings", "進階設定" },
|
||||
{ "about_warn_title", "使用前警告" },
|
||||
{ "about_warn_p1", "本插件僅供學習和交流使用。" },
|
||||
{ "about_warn_p2", "使用外部插件屬於違反遊戲條款的行為。若使用插件後帳號被封禁,造成的後果由用户自行承擔。" },
|
||||
{ "about_about_title", "關於本插件" },
|
||||
{ "about_about_p1", "本插件完全免費。若您付費購買了本插件,請檢舉店家。" },
|
||||
{ "about_about_p2", "插件交流QQ群: 991990192" },
|
||||
{ "project_contribution", "項目貢獻" },
|
||||
{ "plugin_code", "插件本體" },
|
||||
{ "contributors", "貢獻者列表" },
|
||||
{ "translation_repository", "譯文倉庫" },
|
||||
{ "resource_settings", "資源設定" },
|
||||
{ "check_built_in_resource", "檢查內置翻譯資源更新" },
|
||||
{ "delete_plugin_resource", "清除遊戲目錄內的插件翻譯資源" },
|
||||
{ "use_remote_zip_resource", "使用雲端 ZIP 翻譯資源" },
|
||||
{ "resource_url", "資源地址" },
|
||||
{ "download", "下載" },
|
||||
{ "invalid_zip_file", "文件解析失敗" },
|
||||
{ "invalid_zip_file_warn", "此 ZIP 文件不是一個有效的翻譯資源包" },
|
||||
{ "cancel", "取消" },
|
||||
{ "ok", "確定" },
|
||||
{ "downloaded_resource_version", "已下載資源版本" },
|
||||
{ "downloaded_texture_version", "已下載貼圖資源版本" },
|
||||
{ "del_remote_after_update", "替換文件後刪除下載緩存" },
|
||||
{ "del_texture_remote_after_update", "替換文件後刪除下載緩存" },
|
||||
{ "warning", "注意" },
|
||||
{ "install", "安裝" },
|
||||
{ "installing", "安裝中" },
|
||||
{ "check_resource_from_api", "从伺服器檢查更新資源" },
|
||||
{ "check_texture_from_api", "从伺服器檢查貼圖資源更新" },
|
||||
{ "api_addr", "API 地址(Github Latest Release API)" },
|
||||
{ "texture_api_addr", "貼圖 API 地址(Github Latest Release API)" },
|
||||
{ "check_update", "檢查更新" },
|
||||
{ "texture_version", "Texture version" },
|
||||
{ "translation_resource_update", "翻譯資源更新" },
|
||||
{ "game_patch", "遊戲修補" },
|
||||
{ "patch_mode", "修補模式" },
|
||||
{ "patch_local", "本地模式" },
|
||||
{ "patch_local_desc", "為未嵌入模塊的遊戲程式打補丁。\\nXposed 範圍可動態更改,無需重新打補丁。\\n以本地模式修補的遊戲程式只能在本地設備上執行。" },
|
||||
{ "patch_integrated", "集成模式" },
|
||||
{ "patch_integrated_desc", "修補遊戲程式並內置模塊。\\n經集成模式修補的遊戲可以在沒有插件管理器的情况下執行,但不能動態管理設定。\\n以集成模式修補的遊戲可在未安裝 LSPatch 管理器的設備上執行。" },
|
||||
{ "shizuku_available", "Shizuku 服務可用" },
|
||||
{ "shizuku_unavailable", "Shizuku 服務未連接" },
|
||||
{ "home_shizuku_warning", "部分功能不可用" },
|
||||
{ "patch_debuggable", "可調試" },
|
||||
{ "reserve_patched", "安裝時保留修補包" },
|
||||
{ "support_file_types", "支援文件類型:\\n單/多選 apk\\n單選 apks, xapk, zip" },
|
||||
{ "patch_uninstall_text", "由於程式簽名不同,安裝修補版的遊戲前需要先刪除原版。\\n請確保您已備份好個人資料。" },
|
||||
{ "patch_uninstall_confirm", "您確定要刪除吗" },
|
||||
{ "patch_finished", "修補完成,是否開始安裝?" },
|
||||
{ "about_contributors_asset_file", "about_contributors_zh_cn.json" },
|
||||
{ "default_assets_check_api", "https://uma.chinosk6.cn/api/gkms_trans_data" },
|
||||
};
|
||||
}
|
||||
17
src/main.cpp
17
src/main.cpp
|
|
@ -25,8 +25,11 @@ bool g_has_config_file = false;
|
|||
bool g_enable_console = true;
|
||||
bool g_useRemoteAssets = false;
|
||||
bool g_useAPIAssets = false;
|
||||
bool g_useAPITextureAssets = false;
|
||||
bool g_delTextureRemoteAfterUpdate = true;
|
||||
std::string g_remoteResourceUrl = "";
|
||||
std::string g_useAPIAssetsURL = "";
|
||||
std::string g_useAPITextureAssetsURL = "https://texture.gakumas.cn/api/gkms_texture_data";
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -84,6 +87,18 @@ void readProgramConfig() {
|
|||
g_useAPIAssetsURL = document["useAPIAssetsURL"].GetString();
|
||||
}
|
||||
|
||||
if (document.HasMember("useAPITextureAssets")) {
|
||||
g_useAPITextureAssets = document["useAPITextureAssets"].GetBool();
|
||||
}
|
||||
|
||||
if (document.HasMember("useAPITextureAssetsURL")) {
|
||||
g_useAPITextureAssetsURL = document["useAPITextureAssetsURL"].GetString();
|
||||
}
|
||||
|
||||
if (document.HasMember("delTextureRemoteAfterUpdate")) {
|
||||
g_delTextureRemoteAfterUpdate = document["delTextureRemoteAfterUpdate"].GetBool();
|
||||
}
|
||||
|
||||
}
|
||||
config_stream.close();
|
||||
}
|
||||
|
|
@ -142,7 +157,7 @@ int __stdcall DllMain(HINSTANCE dllModule, DWORD reason, LPVOID)
|
|||
std::condition_variable cond;
|
||||
std::atomic<bool> hookIsReady(false);
|
||||
|
||||
// 依赖检查游戏版本的指针加载,因此在 hook 完成后再加载翻译数据
|
||||
// 渚濊禆妫€鏌ユ父鎴忕増鏈殑鎸囬拡鍔犺浇锛屽洜姝ゅ湪 hook 瀹屾垚鍚庡啀鍔犺浇缈昏瘧鏁版嵁
|
||||
std::unique_lock lock(mutex);
|
||||
cond.wait(lock, [&] {
|
||||
return hookIsReady.load(std::memory_order_acquire);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#define LogMinVersion ANDROID_LOG_DEBUG
|
||||
|
||||
#define PLUGIN_VERSION "3.2.0"
|
||||
#define PLUGIN_VERSION "v3.3.1"
|
||||
|
||||
#define ADD_HOOK(name, addr) \
|
||||
name##_Addr = reinterpret_cast<name##_Type>(addr); \
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
#include "stdinclude.hpp"
|
||||
#include "stdinclude.hpp"
|
||||
#include "cpprest/http_client.h"
|
||||
#include "cpprest/filestream.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "GakumasLocalify/Log.h"
|
||||
#include "gkmsGUI/GUII18n.hpp"
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <format>
|
||||
#include <sstream>
|
||||
#include "unzip.hpp"
|
||||
|
||||
extern std::filesystem::path gakumasLocalPath;
|
||||
|
|
@ -13,8 +17,82 @@ extern bool downloading;
|
|||
extern float downloadProgress;
|
||||
extern std::function<void()> g_reload_all_data;
|
||||
std::string resourceVersionCache = "";
|
||||
std::string textureVersionCache = "";
|
||||
|
||||
namespace GkmsResourceUpdate {
|
||||
std::atomic_bool updateJobRunning = false;
|
||||
|
||||
class UpdateJobGuard {
|
||||
public:
|
||||
explicit UpdateJobGuard(const char* jobName) : active(false) {
|
||||
bool expected = false;
|
||||
if (!updateJobRunning.compare_exchange_strong(expected, true)) {
|
||||
GakumasLocal::Log::InfoFmt("Skip %s: another resource update job is running.", jobName);
|
||||
return;
|
||||
}
|
||||
active = true;
|
||||
downloading = true;
|
||||
downloadProgress = 0.0f;
|
||||
}
|
||||
|
||||
~UpdateJobGuard() {
|
||||
if (!active) return;
|
||||
downloading = false;
|
||||
updateJobRunning = false;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return active;
|
||||
}
|
||||
|
||||
private:
|
||||
bool active;
|
||||
};
|
||||
|
||||
std::string trimString(std::string content) {
|
||||
auto is_not_space = [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
};
|
||||
content.erase(content.begin(), std::find_if(content.begin(), content.end(), is_not_space));
|
||||
content.erase(std::find_if(content.rbegin(), content.rend(), is_not_space).base(), content.end());
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string readTrimmedFile(const std::filesystem::path& filePath) {
|
||||
std::ifstream file(filePath);
|
||||
if (!file) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
return trimString(buffer.str());
|
||||
}
|
||||
|
||||
std::filesystem::path getTextureResourceRoot() {
|
||||
return gakumasLocalPath / "texture2d";
|
||||
}
|
||||
|
||||
std::filesystem::path findTextureZipSourceDir(const std::filesystem::path& extractDir) {
|
||||
std::error_code ec;
|
||||
std::filesystem::path fallback;
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(
|
||||
extractDir, std::filesystem::directory_options::skip_permission_denied, ec)) {
|
||||
if (ec) break;
|
||||
if (!entry.is_regular_file(ec)) continue;
|
||||
if (entry.path().filename() != "texture_version.txt") continue;
|
||||
|
||||
const auto parent = entry.path().parent_path();
|
||||
if (parent.filename() == "texture2d") {
|
||||
return parent;
|
||||
}
|
||||
if (fallback.empty()) {
|
||||
fallback = parent;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
void saveProgramConfig() {
|
||||
nlohmann::json config;
|
||||
config["enableConsole"] = g_enable_console;
|
||||
|
|
@ -22,6 +100,9 @@ namespace GkmsResourceUpdate {
|
|||
config["transRemoteZipUrl"] = g_remoteResourceUrl;
|
||||
config["useAPIAssets"] = g_useAPIAssets;
|
||||
config["useAPIAssetsURL"] = g_useAPIAssetsURL;
|
||||
config["useAPITextureAssets"] = g_useAPITextureAssets;
|
||||
config["useAPITextureAssetsURL"] = g_useAPITextureAssetsURL;
|
||||
config["delTextureRemoteAfterUpdate"] = g_delTextureRemoteAfterUpdate;
|
||||
|
||||
std::ofstream out(ProgramConfigJson);
|
||||
if (!out) {
|
||||
|
|
@ -47,18 +128,18 @@ namespace GkmsResourceUpdate {
|
|||
using namespace concurrency::streams;
|
||||
|
||||
try {
|
||||
// 打开输出文件流(同步方式)
|
||||
// 打开输出文件流(同步方式)
|
||||
auto outTask = fstream::open_ostream(conversions::to_string_t(outputPath));
|
||||
outTask.wait();
|
||||
auto fileStream = outTask.get();
|
||||
|
||||
// 创建 HTTP 客户端,注意:如果 url 包含完整路径,cpprestsdk 会自动解析
|
||||
// 创建 HTTP 客户端,注意:如果 url 包含完整路径,cpprestsdk 会自动解析
|
||||
http_client client(conversions::to_string_t(url));
|
||||
|
||||
downloading = true;
|
||||
downloadProgress = 0.0f;
|
||||
|
||||
// 发起 GET 请求
|
||||
// 发起 GET 请求
|
||||
auto responseTask = client.request(methods::GET);
|
||||
responseTask.wait();
|
||||
http_response response = responseTask.get();
|
||||
|
|
@ -68,12 +149,12 @@ namespace GkmsResourceUpdate {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 获取响应头中的文件大小(如果存在)
|
||||
// 获取响应头中的文件大小(如果存在)
|
||||
uint64_t contentLength = 0;
|
||||
if (response.headers().has(L"Content-Length"))
|
||||
contentLength = std::stoull(conversions::to_utf8string(response.headers().find(L"Content-Length")->second));
|
||||
|
||||
// 读取响应体,逐块写入文件,同时更新进度
|
||||
// 读取响应体,逐块写入文件,同时更新进度
|
||||
auto inStream = response.body();
|
||||
const size_t bufferSize = 8192;
|
||||
// std::vector<unsigned char> buffer(bufferSize);
|
||||
|
|
@ -108,26 +189,20 @@ namespace GkmsResourceUpdate {
|
|||
}
|
||||
|
||||
auto resourceVersionFile = gakumasLocalPath / "version.txt";
|
||||
std::ifstream file(resourceVersionFile);
|
||||
if (!file) {
|
||||
// GakumasLocal::Log::ErrorFmt("Can't open file: %s", resourceVersionFile.string().c_str());
|
||||
return "Unknown";
|
||||
resourceVersionCache = readTrimmedFile(resourceVersionFile);
|
||||
return resourceVersionCache;
|
||||
}
|
||||
|
||||
std::string GetCurrentTextureVersion(bool useCache) {
|
||||
if (useCache) {
|
||||
if (!textureVersionCache.empty()) {
|
||||
return textureVersionCache;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string content = buffer.str();
|
||||
|
||||
// 去除首尾空格和换行符
|
||||
auto is_not_space = [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
};
|
||||
// 去除前导空白
|
||||
content.erase(content.begin(), std::find_if(content.begin(), content.end(), is_not_space));
|
||||
// 去除尾部空白
|
||||
content.erase(std::find_if(content.rbegin(), content.rend(), is_not_space).base(), content.end());
|
||||
resourceVersionCache = content;
|
||||
return content;
|
||||
auto textureVersionFile = getTextureResourceRoot() / "texture_version.txt";
|
||||
textureVersionCache = readTrimmedFile(textureVersionFile);
|
||||
return textureVersionCache;
|
||||
}
|
||||
|
||||
bool unzipFileFromURL(std::string downloadUrl, const std::string& unzipPath, const std::string& targetDir = "") {
|
||||
|
|
@ -146,12 +221,106 @@ namespace GkmsResourceUpdate {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool installTextureZipFromURL(const std::string& downloadUrl, const std::string& expectedVersion) {
|
||||
auto textureRoot = getTextureResourceRoot();
|
||||
auto tempZipFile = gakumasLocalPath / "temp_texture_download.zip";
|
||||
auto extractDir = gakumasLocalPath / "texture2d.extract";
|
||||
auto installDir = gakumasLocalPath / "texture2d.tmp";
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
std::filesystem::remove_all(extractDir, ec);
|
||||
std::filesystem::remove_all(installDir, ec);
|
||||
std::filesystem::create_directories(gakumasLocalPath, ec);
|
||||
|
||||
if (!DownloadFile(downloadUrl, tempZipFile.string())) {
|
||||
GakumasLocal::Log::Error("Download texture zip file failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UnzipFile(tempZipFile.string(), extractDir.string())) {
|
||||
GakumasLocal::Log::Error("Unzip texture file failed.");
|
||||
if (g_delTextureRemoteAfterUpdate) {
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sourceDir = findTextureZipSourceDir(extractDir);
|
||||
if (sourceDir.empty()) {
|
||||
GakumasLocal::Log::Error("Texture zip validation failed: texture_version.txt not found.");
|
||||
std::filesystem::remove_all(extractDir, ec);
|
||||
if (g_delTextureRemoteAfterUpdate) {
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto versionFile = sourceDir / "texture_version.txt";
|
||||
const auto packageVersion = readTrimmedFile(versionFile);
|
||||
if (packageVersion.empty() || packageVersion == "Unknown") {
|
||||
GakumasLocal::Log::Error("Texture zip validation failed: texture_version.txt is empty or not found.");
|
||||
std::filesystem::remove_all(extractDir, ec);
|
||||
if (g_delTextureRemoteAfterUpdate) {
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!expectedVersion.empty() && packageVersion != expectedVersion) {
|
||||
GakumasLocal::Log::ErrorFmt(
|
||||
"Texture zip validation failed: texture_version.txt (%s) differs from release tag (%s).",
|
||||
packageVersion.c_str(), expectedVersion.c_str());
|
||||
std::filesystem::remove_all(extractDir, ec);
|
||||
if (g_delTextureRemoteAfterUpdate) {
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::copy(sourceDir, installDir,
|
||||
std::filesystem::copy_options::recursive | std::filesystem::copy_options::overwrite_existing, ec);
|
||||
if (ec) {
|
||||
GakumasLocal::Log::ErrorFmt("Copy texture resource failed: %s", ec.message().c_str());
|
||||
std::filesystem::remove_all(extractDir, ec);
|
||||
if (g_delTextureRemoteAfterUpdate) {
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::remove_all(textureRoot, ec);
|
||||
ec.clear();
|
||||
std::filesystem::rename(installDir, textureRoot, ec);
|
||||
if (ec) {
|
||||
GakumasLocal::Log::ErrorFmt("Install texture resource failed: %s", ec.message().c_str());
|
||||
std::filesystem::remove_all(installDir, ec);
|
||||
std::filesystem::remove_all(extractDir, ec);
|
||||
if (g_delTextureRemoteAfterUpdate) {
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::remove_all(extractDir, ec);
|
||||
if (g_delTextureRemoteAfterUpdate) {
|
||||
std::filesystem::remove(tempZipFile, ec);
|
||||
}
|
||||
|
||||
textureVersionCache.clear();
|
||||
const auto installedVersion = GetCurrentTextureVersion(false);
|
||||
GakumasLocal::Log::InfoFmt("Texture zip installed into %s, version=%s",
|
||||
textureRoot.string().c_str(), installedVersion.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheckUpdateFromAPI(bool isManual) {
|
||||
std::thread([isManual]() {
|
||||
try {
|
||||
if (!g_useAPIAssets) {
|
||||
return;
|
||||
}
|
||||
UpdateJobGuard job("resource API update");
|
||||
if (!job) return;
|
||||
|
||||
GakumasLocal::Log::Info("Checking update from API...");
|
||||
|
||||
|
|
@ -204,7 +373,7 @@ namespace GkmsResourceUpdate {
|
|||
g_reload_all_data();
|
||||
GakumasLocal::Log::Info("Update completed.");
|
||||
}
|
||||
// 仅解压一个文件
|
||||
// 仅解压一个文件
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -217,8 +386,100 @@ namespace GkmsResourceUpdate {
|
|||
}).detach();
|
||||
}
|
||||
|
||||
void CheckTextureUpdateFromAPI(bool isManual) {
|
||||
std::thread([isManual]() {
|
||||
try {
|
||||
if (!g_useAPITextureAssets) {
|
||||
return;
|
||||
}
|
||||
UpdateJobGuard job("texture API update");
|
||||
if (!job) return;
|
||||
|
||||
GakumasLocal::Log::Info("Checking texture update from API...");
|
||||
|
||||
auto response = send_get(g_useAPITextureAssetsURL, 30);
|
||||
if (response.status_code() != 200) {
|
||||
GakumasLocal::Log::ErrorFmt("Failed to check texture update from API: %d\n", response.status_code());
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = nlohmann::json::parse(response.extract_utf8string().get());
|
||||
if (!data.contains("tag_name") || !data["tag_name"].is_string()) {
|
||||
GakumasLocal::Log::Error("Texture API response doesn't contain tag_name.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string remoteVersion = data["tag_name"];
|
||||
const auto localVersion = GetCurrentTextureVersion(false);
|
||||
|
||||
if (localVersion == remoteVersion) {
|
||||
if (isManual) {
|
||||
auto check = MessageBoxW(NULL, utility::conversions::to_string_t(GkmsGUII18n::ts("local_file_already_latest")).c_str(),
|
||||
L"Texture Resource Update", MB_OKCANCEL);
|
||||
if (check != IDOK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string description = "";
|
||||
if (data.contains("body") && data["body"].is_string()) {
|
||||
description = data["body"];
|
||||
}
|
||||
|
||||
auto check = MessageBoxW(NULL, std::format(L"{} -> {}\n\n{}", utility::conversions::to_string_t(localVersion),
|
||||
utility::conversions::to_string_t(remoteVersion), utility::conversions::to_string_t(description)).c_str(),
|
||||
L"Texture Resource Update", MB_OKCANCEL);
|
||||
if (check != IDOK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.contains("assets") || !data["assets"].is_array()) {
|
||||
GakumasLocal::Log::Error("Texture API response doesn't contain assets array.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string downloadUrl = "";
|
||||
for (const auto& asset : data["assets"]) {
|
||||
if (!asset.contains("name") || !asset.contains("browser_download_url") || !asset["name"].is_string() || !asset["browser_download_url"].is_string()) {
|
||||
continue;
|
||||
}
|
||||
std::string name = asset["name"];
|
||||
std::string lowerName = name;
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
if (lowerName.ends_with(".zip")) {
|
||||
downloadUrl = asset["browser_download_url"];
|
||||
if (lowerName == "texture2d.zip") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadUrl.empty()) {
|
||||
GakumasLocal::Log::Error("No texture .zip file found.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (installTextureZipFromURL(downloadUrl, remoteVersion)) {
|
||||
g_reload_all_data();
|
||||
GakumasLocal::Log::Info("Texture update completed.");
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
GakumasLocal::Log::ErrorFmt("Exception occurred in CheckTextureUpdateFromAPI: %s\n", e.what());
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void checkUpdateFromURL(const std::string& downloadUrl) {
|
||||
std::thread([downloadUrl]() {
|
||||
UpdateJobGuard job("remote zip update");
|
||||
if (!job) return;
|
||||
if (unzipFileFromURL(downloadUrl, gakumasLocalPath.string(), "local-files")) {
|
||||
g_reload_all_data();
|
||||
GakumasLocal::Log::Info("Update completed.");
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace GkmsResourceUpdate {
|
||||
void saveProgramConfig();
|
||||
std::string GetCurrentResourceVersion(bool useCache);
|
||||
std::string GetCurrentTextureVersion(bool useCache);
|
||||
void CheckUpdateFromAPI(bool isManual);
|
||||
void CheckTextureUpdateFromAPI(bool isManual);
|
||||
void checkUpdateFromURL(const std::string& downloadUrl);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#define NOMINMAX
|
||||
|
||||
|
|
@ -38,10 +38,13 @@
|
|||
|
||||
|
||||
extern bool g_has_config_file;
|
||||
// config 区
|
||||
// config 区
|
||||
extern bool g_enable_console;
|
||||
extern bool g_useRemoteAssets;
|
||||
extern bool g_useAPIAssets;
|
||||
extern bool g_useAPITextureAssets;
|
||||
extern bool g_delTextureRemoteAfterUpdate;
|
||||
extern std::string g_remoteResourceUrl;
|
||||
extern std::string g_useAPIAssetsURL;
|
||||
// config 区结束
|
||||
extern std::string g_useAPITextureAssetsURL;
|
||||
// config 区结束
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "windowsPlatform.hpp"
|
||||
#include "windowsPlatform.hpp"
|
||||
#include "GakumasLocalify/Plugin.h"
|
||||
#include "GakumasLocalify/Log.h"
|
||||
#include "GakumasLocalify/Local.h"
|
||||
|
|
@ -158,6 +158,7 @@ void reload_all_data() {
|
|||
readProgramConfig();
|
||||
loadConfig(ConfigJson);
|
||||
GkmsResourceUpdate::GetCurrentResourceVersion(false);
|
||||
GkmsResourceUpdate::GetCurrentTextureVersion(false);
|
||||
GakumasLocal::Local::LoadData();
|
||||
GakumasLocal::MasterLocal::LoadData();
|
||||
}
|
||||
|
|
@ -234,7 +235,7 @@ namespace GakumasLocal::WinHooks {
|
|||
v8[0] = 0x100000000LL;
|
||||
|
||||
if (currFullScreen) {
|
||||
// 取消全屏
|
||||
// 取消全屏
|
||||
if (savedWidth == -1) {
|
||||
savedWidth = 542;
|
||||
savedHeight = 990;
|
||||
|
|
@ -270,7 +271,7 @@ namespace GakumasLocal::WinHooks {
|
|||
UINT size = sizeof(RAWINPUT);
|
||||
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &rawInput, &size, sizeof(RAWINPUTHEADER)) == size) {
|
||||
|
||||
/* 鼠标事件,后面加上
|
||||
/* 鼠标事件,后面加上
|
||||
if (rawInput.header.dwType == RIM_TYPEMOUSE)
|
||||
{
|
||||
switch (rawInput.data.mouse.ulButtons) {
|
||||
|
|
@ -373,7 +374,7 @@ namespace GakumasLocal::WinHooks {
|
|||
ScreenToClient(hWnd, &pt);
|
||||
RECT rcClient;
|
||||
GetClientRect(hWnd, &rcClient);
|
||||
const int borderWidth = 8; // 根据需要调整边缘宽度
|
||||
const int borderWidth = 8; // 根据需要调整边缘宽度
|
||||
|
||||
bool left = pt.x < borderWidth;
|
||||
bool right = pt.x >= rcClient.right - borderWidth;
|
||||
|
|
@ -396,10 +397,10 @@ namespace GakumasLocal::WinHooks {
|
|||
{
|
||||
if (GakumasLocal::Config::dmmUnlockSize) {
|
||||
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
|
||||
// 设置最大尺寸为屏幕分辨率,这样就不限制窗口的最大尺寸
|
||||
// 设置最大尺寸为屏幕分辨率,这样就不限制窗口的最大尺寸
|
||||
lpMMI->ptMaxTrackSize.x = GetSystemMetrics(SM_CXSCREEN) * 3;
|
||||
lpMMI->ptMaxTrackSize.y = GetSystemMetrics(SM_CYSCREEN) * 3;
|
||||
// 可选:设置窗口最小尺寸(例如200x200)
|
||||
// 可选:设置窗口最小尺寸(例如200x200)
|
||||
lpMMI->ptMinTrackSize.x = 200;
|
||||
lpMMI->ptMinTrackSize.y = 200;
|
||||
return 1;
|
||||
|
|
@ -421,7 +422,7 @@ namespace GakumasLocal::WinHooks {
|
|||
// printf("WM_NCPAINT: 0x%x\n", style);
|
||||
|
||||
if (!(style & WS_POPUP)) {
|
||||
// 添加可调整大小的边框和最大化按钮
|
||||
// 添加可调整大小的边框和最大化按钮
|
||||
style |= WS_THICKFRAME | WS_MAXIMIZEBOX;
|
||||
SetWindowLong(hWnd, GWL_STYLE, style);
|
||||
}
|
||||
|
|
@ -439,7 +440,7 @@ namespace GakumasLocal::WinHooks {
|
|||
auto hWnd = FindWindowW(L"UnityWndClass", L"gakumas");
|
||||
g_pfnOldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
||||
SetWindowLongPtr(FindWindowW(L"UnityWndClass", L"gakumas"), GWLP_WNDPROC, (LONG_PTR)WndProcCallback);
|
||||
// 添加可调整大小的边框和最大化按钮
|
||||
// 添加可调整大小的边框和最大化按钮
|
||||
LONG style = GetWindowLong(hWnd, GWL_STYLE);
|
||||
style |= WS_THICKFRAME | WS_MAXIMIZEBOX;
|
||||
SetWindowLong(hWnd, GWL_STYLE, style);
|
||||
|
|
|
|||
Loading…
Reference in New Issue