From f3170f0d23b2dd8b36809a95ae3480abe43385ce Mon Sep 17 00:00:00 2001 From: BricOOtaku <90196012+BricOOtaku@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:06:49 +0200 Subject: [PATCH 1/4] feat: add XTRA compatibility scale and TZ matching option --- src/game/tech_zone.cpp | 18 +++-- src/nc_state.cpp | 150 ++++++++++++++++++++++++++++++++++++++++- src/nc_state.h | 2 + 3 files changed, 162 insertions(+), 8 deletions(-) diff --git a/src/game/tech_zone.cpp b/src/game/tech_zone.cpp index 1b555fa..ecb21a4 100644 --- a/src/game/tech_zone.cpp +++ b/src/game/tech_zone.cpp @@ -75,13 +75,16 @@ void TechZoneDispState::Ctrl() if (scene == 0 || layer_name.empty()) { - // TODO: Implement song's default style (?) - // Detect BricOOtaku's X UI mode and set the style accordingly in 'Match' mode int32_t style = nc::GetSharedData().tech_zone_style; + uint32_t skinType = GetResolvedSkinType(); + if (style == TechZoneStyle_Match) { - if (style == TechZoneStyle_Match) - style = game::IsFutureToneMode() ? TechZoneStyle_FT : TechZoneStyle_M39; - + if (skinType != (uint32_t)-1) + style = static_cast(skinType); + else + style = game::IsFutureToneMode() ? TechZoneStyle_FT : TechZoneStyle_M39; + } + switch (style) { case TechZoneStyle_F: @@ -103,6 +106,11 @@ void TechZoneDispState::Ctrl() case TechZoneStyle_M39: layer_name = "bonus_zone"; scene = 14010085; + break; + default : + layer_name = "bonus_zone"; + scene = 14010081; + break; } } diff --git a/src/nc_state.cpp b/src/nc_state.cpp index 6645f54..566b0a2 100644 --- a/src/nc_state.cpp +++ b/src/nc_state.cpp @@ -4,6 +4,111 @@ #include "game/hit_state.h" #include "nc_state.h" +#include + +typedef const diva::vec3* (*FnGetBtnLongScale)(); +static FnGetBtnLongScale GetBtnLongScale = nullptr; + +typedef const diva::vec3* (*FnGetBtnScale)(); +static FnGetBtnScale GetBtnScale = nullptr; + +typedef int32_t(*FnGetSkinType)(); +static FnGetSkinType GetSkinType = nullptr; + +diva::vec3 noteScale = { 1.0f, 1.0f, 1.0f }; + +static HMODULE FindModuleWithExport(const char* exportName) +{ + HMODULE modules[512]; + DWORD needed = 0; + + if (!EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &needed)) + return nullptr; + + size_t count = needed / sizeof(HMODULE); + + for (size_t i = 0; i < count; i++) + { + FARPROC proc = GetProcAddress(modules[i], exportName); + if (proc) + return modules[i]; + } + + return nullptr; +} + +void ResolveBtnScale() +{ + HMODULE mod = FindModuleWithExport("GetBtnScale"); + if (!mod) { + GetBtnScale = nullptr; + return; + } + + GetBtnScale = reinterpret_cast( + GetProcAddress(mod, "GetBtnScale")); + + if (!GetBtnScale) + GetBtnScale = nullptr; +} + +void ResolveBtnLongScale() +{ + HMODULE mod = FindModuleWithExport("GetBtnLongScale"); + if (!mod) { + GetBtnLongScale = nullptr; + return; + } + + GetBtnLongScale = reinterpret_cast( + GetProcAddress(mod, "GetBtnLongScale")); + + if (!GetBtnLongScale) + GetBtnLongScale = nullptr; +} + +diva::vec3 ResolveScale( + FnGetBtnScale getter, + int idx, + const diva::vec3& fallback, + const char* logTag) +{ + diva::vec3 scale = fallback; + + if (getter) { + const diva::vec3* arr = getter(); + if (arr && idx >= 0) + scale = arr[idx]; + } + + return scale; +} + +void ResolveSkinType() +{ + HMODULE mod = FindModuleWithExport("GetSkinType"); + if (!mod) { + GetSkinType = nullptr; + return; + } + + FARPROC proc = GetProcAddress(mod, "GetSkinType"); + if (!proc) { + GetSkinType = nullptr; + return; + } + + GetSkinType = reinterpret_cast(proc); +} + +uint32_t GetResolvedSkinType() +{ + if (!GetSkinType) + return -1; + + return GetSkinType(); +} + void TargetStateEx::ResetPlayState() { hold_button = nullptr; @@ -85,7 +190,21 @@ bool TargetStateEx::SetLongNoteAet() aet::SetPlay(target_aet, false); aet::SetFrame(target_aet, 360.0f); - diva::vec3 scale = { 1.0f, 1.0f, 1.0f }; + int idx = -1; + switch (target_type) { + case TargetType_TriangleLong: idx = 0; break; + case TargetType_CircleLong: idx = 1; break; + case TargetType_CrossLong: idx = 2; break; + case TargetType_SquareLong: idx = 3; break; + } + + diva::vec3 scale = ResolveScale( + GetBtnLongScale, + idx, + noteScale, + "LongAet" + ); + aet::SetScale(target_aet, &scale); // NOTE: Free original aets @@ -125,7 +244,22 @@ bool TargetStateEx::SetRushNoteAet() diva::vec3 pos = { scaled_pos.x, scaled_pos.y, 0.0f }; aet::SetPosition(button_aet, &pos); - diva::vec3 scale = { 1.0f, 1.0f, 1.0f }; + int idx = -1; + switch (target_type) { + case TargetType_TriangleRush: idx = 0; break; + case TargetType_CircleRush: idx = 1; break; + case TargetType_CrossRush: idx = 2; break; + case TargetType_SquareRush: idx = 3; break; + case TargetType_StarRush: idx = 4; break; + } + + diva::vec3 scale = ResolveScale( + GetBtnScale, + idx, + noteScale, + "RushAet" + ); + aet::SetScale(button_aet, &scale); org->button_aet = 0; @@ -354,4 +488,14 @@ TargetStateEx* GetTargetStateEx(const PvGameTarget* org) extern "C" __declspec(dllexport) StateEx* GetState() { return &state; -} \ No newline at end of file +} + +struct BtnLongScaleResolver { + BtnLongScaleResolver() { + ResolveBtnLongScale(); + ResolveBtnScale(); + ResolveSkinType(); + } +}; + +static BtnLongScaleResolver _btnLongScaleResolver; \ No newline at end of file diff --git a/src/nc_state.h b/src/nc_state.h index b9cf534..6e91ff2 100644 --- a/src/nc_state.h +++ b/src/nc_state.h @@ -351,4 +351,6 @@ inline StateEx state = { }; TargetStateEx* GetTargetStateEx(int32_t index, int32_t sub_index); TargetStateEx* GetTargetStateEx(const PvGameTarget* org); +uint32_t GetResolvedSkinType(); + extern "C" __declspec(dllexport) StateEx* GetState(); \ No newline at end of file From e26b506b4ecaeff33303364dfbc803d501b48c2c Mon Sep 17 00:00:00 2001 From: BricOOtaku <90196012+BricOOtaku@users.noreply.github.com> Date: Tue, 1 Sep 2026 00:47:20 +0200 Subject: [PATCH 2/4] Renamed BtnLongScaleResolver to XtraResolver and added XtraSkn flag in NC to disable frame height patch when Xtra UI is active --- NewClassics.vcxproj | 8 ++++---- src/game/game.cpp | 5 +++-- src/nc_state.cpp | 33 +++++++++++++++++++++++++++++---- src/nc_state.h | 3 ++- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/NewClassics.vcxproj b/NewClassics.vcxproj index 2a21812..22d0781 100644 --- a/NewClassics.vcxproj +++ b/NewClassics.vcxproj @@ -29,26 +29,26 @@ DynamicLibrary true - v145 + v143 Unicode DynamicLibrary false - v145 + v143 true Unicode DynamicLibrary true - v145 + v143 Unicode DynamicLibrary false - v145 + v143 true Unicode diff --git a/src/game/game.cpp b/src/game/game.cpp index 7dd9413..d94399e 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -479,10 +479,11 @@ HOOK(void, __fastcall, ExecuteModeSelect, 0x1503B04A0, PVGamePvData* pv_data, in switch (mode) { case ModeSelect_ChallengeStart: - PatchFrmBtmHeight(ChallengeTimeHeight); + if (!isXtraSkn) + PatchFrmBtmHeight(ChallengeTimeHeight); break; case ModeSelect_ChanceStart: - SaveAndPatchCTHeight(); + if (!isXtraSkn) SaveAndPatchCTHeight(); SetChanceTimeMode(&pv_data->pv_game->ui, ModeSelect_ChanceStart); break; case ModeSelect_ChanceEnd: diff --git a/src/nc_state.cpp b/src/nc_state.cpp index 566b0a2..ff8a162 100644 --- a/src/nc_state.cpp +++ b/src/nc_state.cpp @@ -15,7 +15,11 @@ static FnGetBtnScale GetBtnScale = nullptr; typedef int32_t(*FnGetSkinType)(); static FnGetSkinType GetSkinType = nullptr; +typedef bool (*FnGetIsXtraSkn)(); +static FnGetIsXtraSkn GetIsXtraSkn = nullptr; + diva::vec3 noteScale = { 1.0f, 1.0f, 1.0f }; +bool isXtraSkn = false; static HMODULE FindModuleWithExport(const char* exportName) { @@ -109,6 +113,26 @@ uint32_t GetResolvedSkinType() return GetSkinType(); } +void ResolveIsXtraSkn() +{ + HMODULE mod = FindModuleWithExport("GetIsXtraSkn"); + if (!mod) + { + isXtraSkn = false; + return; + } + + FARPROC proc = GetProcAddress(mod, "GetIsXtraSkn"); + if (!proc) + { + isXtraSkn = false; + return; + } + + GetIsXtraSkn = reinterpret_cast(proc); + isXtraSkn = GetIsXtraSkn(); +} + void TargetStateEx::ResetPlayState() { hold_button = nullptr; @@ -481,7 +505,7 @@ TargetStateEx* GetTargetStateEx(const PvGameTarget* org) int32_t sub_index = 0; for (PvGameTarget* prev = org->prev; prev && prev->multi_count == org->multi_count; prev = prev->prev) sub_index++; - + return GetTargetStateEx(org->target_index, sub_index); } @@ -490,12 +514,13 @@ extern "C" __declspec(dllexport) StateEx* GetState() return &state; } -struct BtnLongScaleResolver { - BtnLongScaleResolver() { +struct XtraResolver { + XtraResolver() { ResolveBtnLongScale(); ResolveBtnScale(); ResolveSkinType(); + ResolveIsXtraSkn(); } }; -static BtnLongScaleResolver _btnLongScaleResolver; \ No newline at end of file +static XtraResolver _xtraResolver; \ No newline at end of file diff --git a/src/nc_state.h b/src/nc_state.h index 6e91ff2..700e72b 100644 --- a/src/nc_state.h +++ b/src/nc_state.h @@ -353,4 +353,5 @@ TargetStateEx* GetTargetStateEx(const PvGameTarget* org); uint32_t GetResolvedSkinType(); -extern "C" __declspec(dllexport) StateEx* GetState(); \ No newline at end of file +extern "C" __declspec(dllexport) StateEx* GetState(); +extern bool isXtraSkn; \ No newline at end of file From bf1c0c8474ae179cfd2ee10a46bdcabd0248a5b4 Mon Sep 17 00:00:00 2001 From: BricOOtaku <90196012+BricOOtaku@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:46:23 +0200 Subject: [PATCH 3/4] Load Xtra compat func on PostInit --- src/mod.cpp | 1 + src/nc_state.cpp | 16 ++++++---------- src/nc_state.h | 3 ++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/mod.cpp b/src/mod.cpp index f347e98..94175ee 100644 --- a/src/mod.cpp +++ b/src/mod.cpp @@ -379,5 +379,6 @@ extern "C" { InstallPvSelHooks(); InstallCustomizeSelHooks(); + XtraCompatibility(); } }; \ No newline at end of file diff --git a/src/nc_state.cpp b/src/nc_state.cpp index ff8a162..bde8e82 100644 --- a/src/nc_state.cpp +++ b/src/nc_state.cpp @@ -514,13 +514,9 @@ extern "C" __declspec(dllexport) StateEx* GetState() return &state; } -struct XtraResolver { - XtraResolver() { - ResolveBtnLongScale(); - ResolveBtnScale(); - ResolveSkinType(); - ResolveIsXtraSkn(); - } -}; - -static XtraResolver _xtraResolver; \ No newline at end of file +void XtraCompatibility() { + ResolveBtnLongScale(); + ResolveBtnScale(); + ResolveSkinType(); + ResolveIsXtraSkn(); +} \ No newline at end of file diff --git a/src/nc_state.h b/src/nc_state.h index 700e72b..a3528bc 100644 --- a/src/nc_state.h +++ b/src/nc_state.h @@ -354,4 +354,5 @@ TargetStateEx* GetTargetStateEx(const PvGameTarget* org); uint32_t GetResolvedSkinType(); extern "C" __declspec(dllexport) StateEx* GetState(); -extern bool isXtraSkn; \ No newline at end of file +extern bool isXtraSkn; +void XtraCompatibility(); \ No newline at end of file From 0c6955dc2ef2c53a48864f5c687c7a619839bec9 Mon Sep 17 00:00:00 2001 From: BricOOtaku <90196012+BricOOtaku@users.noreply.github.com> Date: Mon, 7 Sep 2026 10:48:17 +0200 Subject: [PATCH 4/4] Export tech_zone_style value --- src/exports.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/exports.cpp b/src/exports.cpp index df3a901..2d653f5 100644 --- a/src/exports.cpp +++ b/src/exports.cpp @@ -1,5 +1,6 @@ #include "build_info.h" #include "nc_state.h" +#include extern "C" { @@ -32,4 +33,9 @@ extern "C" GetState()->nc_song_entry.reset(); GetState()->nc_chart_entry.reset(); } + + __declspec(dllexport) int32_t GetSharedTechZoneStyle() + { + return nc::GetSharedData().tech_zone_style; + } } \ No newline at end of file