fix: restore gyro and meishi fixes for v3.5.0

This commit is contained in:
chihya72 committed 2026-09-17 09:33:51 +08:00
1 parent d372b0d22c
commit 9462265bca
6 files changed
+82 -29

No files matched your search

+1 -5
View File
@@ -38,12 +38,8 @@ namespace GakumasLocal::GyroEmu {
dragging = false;
}
// 小键盘:4/6 yaw, 8/2 pitch, 7/9 roll, 5 复位
// 小键盘:7/9 翻滚, 5 复位。4/6/8/2 是游戏自己拍摄页的镜头键,不占用
const float step = 60.f * dt * Config::gyroSensitivity;
if (Down(VK_NUMPAD4)) yaw -= step * sx;
if (Down(VK_NUMPAD6)) yaw += step * sx;
if (Down(VK_NUMPAD8)) pitch -= step * sy;
if (Down(VK_NUMPAD2)) pitch += step * sy;
if (Down(VK_NUMPAD7)) roll += step;
if (Down(VK_NUMPAD9)) roll -= step;
if (Down(VK_NUMPAD5)) Reset();
+77 -20
View File
@@ -234,52 +234,92 @@ namespace GakumasLocal::HookMain {
return Photography3DPresenter_get_IsGyroSupported_Orig(self, mtd);
}
// PC 版把 LiveUtility.IsPanoramaGyroDisabledOnPlatform()(恒 true)内联成常量,CampusGyroController 根本不会被
// 创建/初始化(log 证实 IsGyroAvailable 从未被查询),IsGyroReady 永远 false。这里在 LateUpdate 里自己把它建出来。
// PC 版把 LiveUtility.IsPanoramaGyroDisabledOnPlatform()(恒 true)内联成常量,CampusGyroController 的 InitializeAsync
// 被整段跳过(log 证实 IsGyroAvailable 从未被查询),IsGyroReady 永远 false。Live 与拍摄各持有一个控制器,
// 所以在构造函数 hook 里统一把它置成初始化完成态,再在两条链路各留一个“控制器为空就自己建”的兜底。
// 字段偏移来自 3.3.0 il2cpp.h(CampusGyroController / VLGyro / LivePanoramaCameraManager)。
namespace GyroForceInit {
using Quaternion = UnityResolve::UnityType::Quaternion;
template <typename T> T& F(void* obj, size_t off) { return *reinterpret_cast<T*>(reinterpret_cast<char*>(obj) + off); }
void Ensure(void* manager) {
static auto ctrlClass = Il2cppUtils::GetClass("campus-submodule.Runtime.dll", "Campus.Common", "CampusGyroController");
static auto gyroClass = Il2cppUtils::GetClass("vl-unity.Runtime.dll", "VL", "VLGyro");
static auto ctrlCtor = reinterpret_cast<void (*)(void*, void*)>(Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common", "CampusGyroController", ".ctor"));
static auto gyroCtor = reinterpret_cast<void (*)(void*, void*)>(Il2cppUtils::GetMethodPointer("vl-unity.Runtime.dll", "VL", "VLGyro", ".ctor"));
static auto gyroReset = reinterpret_cast<void (*)(void*, void*)>(Il2cppUtils::GetMethodPointer("vl-unity.Runtime.dll", "VL", "VLGyro", "ResetRotation"));
static auto gyroRaw = reinterpret_cast<Quaternion* (*)(Quaternion*, void*, void*)>(Il2cppUtils::GetMethodPointer("vl-unity.Runtime.dll", "VL", "VLGyro", "get_gyroRotationRaw"));
static bool reported = false;
if (!ctrlClass || !gyroClass || !ctrlCtor || !gyroCtor || !gyroReset || !gyroRaw) {
if (!reported) { reported = true; Log::ErrorFmt("[GyroEmu] force-init: missing class/method (%p %p %p %p %p %p)", ctrlClass, gyroClass, ctrlCtor, gyroCtor, gyroReset, gyroRaw); }
return;
struct Api {
UnityResolve::Class* ctrlClass; UnityResolve::Class* gyroClass;
void (*ctrlCtor)(void*, void*); void (*gyroCtor)(void*, void*); void (*gyroReset)(void*, void*);
Quaternion* (*gyroRaw)(Quaternion*, void*, void*);
};
static const Api* GetApi() {
static Api api{
Il2cppUtils::GetClass("campus-submodule.Runtime.dll", "Campus.Common", "CampusGyroController"),
Il2cppUtils::GetClass("vl-unity.Runtime.dll", "VL", "VLGyro"),
reinterpret_cast<void (*)(void*, void*)>(Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common", "CampusGyroController", ".ctor")),
reinterpret_cast<void (*)(void*, void*)>(Il2cppUtils::GetMethodPointer("vl-unity.Runtime.dll", "VL", "VLGyro", ".ctor")),
reinterpret_cast<void (*)(void*, void*)>(Il2cppUtils::GetMethodPointer("vl-unity.Runtime.dll", "VL", "VLGyro", "ResetRotation")),
reinterpret_cast<Quaternion* (*)(Quaternion*, void*, void*)>(Il2cppUtils::GetMethodPointer("vl-unity.Runtime.dll", "VL", "VLGyro", "get_gyroRotationRaw")),
};
static const bool ok = [] {
if (api.ctrlClass && api.gyroClass && api.ctrlCtor && api.gyroCtor && api.gyroReset && api.gyroRaw) return true;
Log::ErrorFmt("[GyroEmu] force-init: missing class/method (%p %p %p %p %p %p)", api.ctrlClass, api.gyroClass, api.ctrlCtor, api.gyroCtor, api.gyroReset, api.gyroRaw);
return false;
}();
return ok ? &api : nullptr;
}
// 把一个已构造的 CampusGyroController 直接置成“初始化完成”态(等价于它自己 InitializeAsync 成功后的状态)
void InitController(void* ctrl) {
auto api = GetApi();
if (!api || !ctrl || F<bool>(ctrl, 0x18)) return; // _initialized
const Quaternion identity(0, 0, 0, 1);
auto& ctrl = F<void*>(manager, 0x60); // _gyroController
if (!ctrl) { ctrl = ctrlClass->New<void*>(); ctrlCtor(ctrl, nullptr); }
if (F<bool>(ctrl, 0x18)) return; // _initialized
auto& gyro = F<void*>(ctrl, 0x10); // _gyro
if (!gyro) { gyro = gyroClass->New<void*>(); gyroCtor(gyro, nullptr); }
if (!gyro) { gyro = api->gyroClass->New<void*>(); api->gyroCtor(gyro, nullptr); }
F<int>(gyro, 0x14) = 1; // _updateType = Gyro
F<bool>(gyro, 0x29) = true; // gyroSupported
F<bool>(gyro, 0x2A) = true; // _defaultGyroEnable
for (size_t off : {0x2C, 0x3C, 0x4C, 0x5C}) F<Quaternion>(gyro, off) = identity;
F<bool>(gyro, 0x6C) = true; // _initialized
F<bool>(gyro, 0x6D) = true; // _firstUpdate
gyroReset(gyro, nullptr);
Quaternion raw; gyroRaw(&raw, gyro, nullptr);
api->gyroReset(gyro, nullptr);
Quaternion raw; api->gyroRaw(&raw, gyro, nullptr);
F<Quaternion>(ctrl, 0x1C) = Quaternion(-raw.x, -raw.y, -raw.z, raw.w); // _baseRotation = Inverse(raw)
F<int>(ctrl, 0x2C) = INT_MIN; // _lastAccessFrame
F<Quaternion>(ctrl, 0x30) = identity; // _rotation
F<bool>(ctrl, 0x18) = true; // _initialized
Log::InfoFmt("[GyroEmu] force-init done: controller %p gyro %p", ctrl, gyro);
}
void* NewController() {
auto api = GetApi();
if (!api) return nullptr;
auto ctrl = api->ctrlClass->New<void*>();
api->ctrlCtor(ctrl, nullptr); // 构造函数 hook 会顺手 InitController
InitController(ctrl);
return ctrl;
}
}
// 所有 CampusGyroController 一出生就初始化完成,Live / 拍摄 / 其他持有者都不用再关心 PC 上被跳过的 InitializeAsync
DEFINE_HOOK(void, CampusGyroController_ctor, (void* self, void* mtd)) {
CampusGyroController_ctor_Orig(self, mtd);
if (Config::enableGyroEmu) GyroForceInit::InitController(self);
}
// Live:PC 版可能连控制器都没建,LateUpdate 里兜底
DEFINE_HOOK(void, LivePanoramaCameraManager_LateUpdate, (void* self, void* mtd)) {
if (Config::enableGyroEmu && self) GyroForceInit::Ensure(self);
if (Config::enableGyroEmu && self) {
auto& ctrl = GyroForceInit::F<void*>(self, 0x60); // _gyroController
if (!ctrl) ctrl = GyroForceInit::NewController();
else GyroForceInit::InitController(ctrl);
}
LivePanoramaCameraManager_LateUpdate_Orig(self, mtd);
}
// 拍摄:PhotographySceneManager 把控制器交给 PhotographyActivityController,这里是它进相机控制器的唯一入口
DEFINE_HOOK(void, PhotographyActivityController_Initialize, (void* self, void* volumeProfile, void* gyroController, float arg, void* mtd)) {
if (Config::enableGyroEmu) {
if (!gyroController) gyroController = GyroForceInit::NewController();
else GyroForceInit::InitController(gyroController);
}
PhotographyActivityController_Initialize_Orig(self, volumeProfile, gyroController, arg, mtd);
}
// PC 版 MSVC 把 SystemInfo.supportsGyroscope / Gyroscope.* 这些托管包装内联进了调用方,hook 函数本体拦不到。
// il2cpp 的 icall 包装里有一个 static 缓存指针(_il2cpp_icall_func),内联后所有副本共用同一个变量,
// 直接把这个变量改成我们的函数即可,不碰任何机器码,也不受链接器 ICF 合并影响。
@@ -1459,10 +1499,21 @@ namespace GakumasLocal::HookMain {
return ret;
}*/
// 名片编辑页的 P 偶像页签用 UserIdolCardSkinList.Exists(skin.Id) 过滤“拥有的皮肤”,随后按底卡去
// UserIdolCardList 查 UserIdolCard 取 IllustrationStage。下面 Exists 对所有 i_card-skin-* 返回 true 会把
// 没有底卡的皮肤也放进去,查到 null 直接 NRE,整个界面初始化中断(表现为卡死)。该模型构造期间放行真实结果。
static bool meishiModelConstructing = false;
DEFINE_HOOK(void, MeishiBasePIdolTabPageModel_ctor, (void* self, void* mtd)) {
meishiModelConstructing = true;
MeishiBasePIdolTabPageModel_ctor_Orig(self, mtd);
meishiModelConstructing = false;
}
DEFINE_HOOK(bool, UserIdolCardSkinCollection_Exists, (void* self, Il2cppString* id, void* mtd)) { // Live默认选择
auto ret = UserIdolCardSkinCollection_Exists_Orig(self, id, mtd);
// Log::DebugFmt("UserIdolCardSkinCollection_Exists: %s, ret: %d", id->ToString().c_str(), ret);
if (!(Config::dbgMode && Config::unlockAllLive)) return ret;
if (meishiModelConstructing) return ret;
if (id) {
std::string idStr = id->ToString();
@@ -2842,6 +2893,8 @@ namespace GakumasLocal::HookMain {
if (UserIdolCardSkinCollection_Exists_mtd) {
ADD_HOOK(UserIdolCardSkinCollection_Exists, UserIdolCardSkinCollection_Exists_mtd->methodPointer);
}
ADD_HOOK(MeishiBasePIdolTabPageModel_ctor, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
"MeishiBasePIdolTabPageModel", ".ctor"));
auto UserCostumeCollection_klass = Il2cppUtils::GetClass("Assembly-CSharp.dll", "Campus.Common.User",
"UserCostumeCollection");
@@ -3044,6 +3097,10 @@ namespace GakumasLocal::HookMain {
GyroIcall::Install();
ADD_HOOK(LivePanoramaCameraManager_LateUpdate, Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Live",
"LivePanoramaCameraManager", "LateUpdate"));
ADD_HOOK(CampusGyroController_ctor, Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
"CampusGyroController", ".ctor"));
ADD_HOOK(PhotographyActivityController_Initialize, Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Photography",
"PhotographyActivityController", "Initialize"));
ADD_HOOK(WindowHandle_SetWindowLong, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
"WindowHandle", "SetWindowLong"));
+1 -1
View File
@@ -17,7 +17,7 @@ namespace I18nData {
{ "lazy_init", "Fast Initialization (Lazy loading)" },
{ "enable_free_camera", "Enable Free Camera" },
{ "enable_gyro_emu", "Enable Gyroscope Emulation (Live/Photo)" },
{ "gyro_emu_help", "Hold right mouse button and drag to rotate; Numpad 4/6 8/2 yaw/pitch, 7/9 roll, 5 reset" },
{ "gyro_emu_help", "Hold right mouse button and drag to rotate; Numpad 7/9 roll, 5 reset" },
{ "gyro_sensitivity", "Gyro Sensitivity" },
{ "gyro_invert_x", "Invert X" },
{ "gyro_invert_y", "Invert Y" },
+1 -1
View File
@@ -58,7 +58,7 @@ namespace I18nData {
{ "enable_breast_param", "胸のパラメーターを有効化" },
{ "enable_free_camera", "フリーカメラを有効化" },
{ "enable_gyro_emu", "ジャイロ模擬を有効化(ライブ/撮影)" },
{ "gyro_emu_help", "右クリックドラッグで視点回転;テンキー 4/6 8/2 左右上下, 7/9 ロール, 5 リセット" },
{ "gyro_emu_help", "右クリックドラッグで視点回転;テンキー 7/9 ロール, 5 リセット" },
{ "gyro_sensitivity", "ジャイロ感度" },
{ "gyro_invert_x", "左右反転" },
{ "gyro_invert_y", "上下反転" },
+1 -1
View File
@@ -17,7 +17,7 @@ namespace I18nData {
{ "lazy_init", "快速初始化(懒加载配置)" },
{ "enable_free_camera", "启用自由视角(可热重载; 需使用实体键盘)" },
{ "enable_gyro_emu", "启用陀螺仪模拟(Live/拍摄)" },
{ "gyro_emu_help", "按住鼠标右键拖动转动视角;小键盘 4/6 8/2 左右上下, 7/9 翻滚, 5 复位" },
{ "gyro_emu_help", "按住鼠标右键拖动转动视角;小键盘 7/9 翻滚, 5 复位" },
{ "gyro_sensitivity", "陀螺仪灵敏度" },
{ "gyro_invert_x", "反转左右" },
{ "gyro_invert_y", "反转上下" },
+1 -1
View File
@@ -17,7 +17,7 @@ namespace I18nData {
{ "lazy_init", "快速初始化(懶人設定)" },
{ "enable_free_camera", "啟用自由視角(可熱重載; 需使用實體鍵盤)" },
{ "enable_gyro_emu", "啟用陀螺儀模擬(Live/拍攝)" },
{ "gyro_emu_help", "按住滑鼠右鍵拖動轉動視角;數字鍵盤 4/6 8/2 左右上下, 7/9 翻滾, 5 重設" },
{ "gyro_emu_help", "按住滑鼠右鍵拖動轉動視角;數字鍵盤 7/9 翻滾, 5 重設" },
{ "gyro_sensitivity", "陀螺儀靈敏度" },
{ "gyro_invert_x", "反轉左右" },
{ "gyro_invert_y", "反轉上下" },