1
1
mirror of synced 2025-01-29 19:07:22 +01:00

Refactor Plugins API / Card API / Qr API

Kinda fix exit voice issue by enter testmode before quit
fix bnusio::Update not invoked issue when fpsLimit=0
add option in config.toml for cursor (mouse)
move FixLanguage to testmode, upgrade it to support release attractdemo
add attractdemo control switch after attractmovie (only enable when FixLanguage on)
move unlock songs to testmode, add force option to release shop songs
move NusBusVolumeHook to audio patches
This commit is contained in:
ptmaster 2024-11-30 03:19:36 +08:00
parent 0988da9268
commit 7dc76ed958
15 changed files with 992 additions and 566 deletions

View File

@ -133,7 +133,9 @@ set(SOURCES
src/patches/dxgi.cpp
src/patches/fpslimiter.cpp
src/patches/audio.cpp
src/patches/qr.cpp
src/patches/plugins.cpp
src/patches/scanner.cpp
# src/patches/qr.cpp
src/patches/layeredfs.cpp
src/patches/testmode.cpp
src/patches/versions/JPN00.cpp

View File

@ -31,7 +31,7 @@ version = "auto" # Patch version
# | - JPN08: For use with Taiko JPN 08.18
# | - JPN39: For use with Taiko JPN 39.06
# | - CHN00: For use with Taiko CHN 00.32
unlock_songs = true
unlock_songs = true # not active for JPN39 (see TestMode)
[patches.chn00] # These patches are only available for version CHN00
fix_language = false # Sync test mode language to attract etc
@ -40,7 +40,6 @@ mode_collabo025 = false # Enable one piece collab mode
mode_collabo026 = false # Enable ai soshina mode
[patches.jpn39] # These patches are only available for version JPN39
fix_language = false # Sync test mode language to attract etc
chs_patch = false # Use Chinese font and Simplified Chinese values from the wordlist
# More options are available in the ModManager, in the TestMode menu (Default key is F1)
@ -53,6 +52,7 @@ qr = true # Disable this if you have an original namco qr code
[graphics]
res = { x = 1920, y = 1080 }
windowed = false
cursor = true
vsync = false
fpslimit = 120
@ -100,6 +100,8 @@ TaikoArcadeLoader offers several patches to select in TestMode
The follow options are available in "MOD MANAGER" menu:
* FIX LANGUAGE (sync test mode language to attract etc)
* UNLOCK SONGS (show all of the songs)
* FREEZE TIMER (stop timer count down)
* KIMETSU MODE (enable collabo024, will show a blank title)
* ONE PIECE MODE (enable collabo025)
@ -110,6 +112,7 @@ The follow options are available in "MOD MANAGER" menu:
Enhanced original option:
* Louder volume (Speaker Volume is now up to 300%, **WARNING: May damage your speakers**)
* Attract demo (Only available if FIX LANGUAGE is ON)
## Building Manually

2
dist/config.toml vendored
View File

@ -25,7 +25,6 @@ mode_collabo026 = false # Enable ai soshina mode
[patches.jpn39] # These patches are only available for version JPN39
fix_language = false # Sync test mode language to attract etc
chs_patch = false # Use Chinese font and Simplified Chinese values from the wordlist
# More options are available in the ModManager, in the TestMode menu (Default key is F1)
@ -40,6 +39,7 @@ qr = true # Disable this if you want to use an original Namco
[graphics]
res = { x = 1920, y = 1080 }
windowed = false
cursor = true
vsync = false
fpslimit = 120

View File

@ -2,9 +2,10 @@
#include "constants.h"
#include "helpers.h"
#include "patches/patches.h"
#include "bnusio.h"
#include "poll.h"
extern GameVersion version;
extern GameVersion gameVersion;
extern std::vector<HMODULE> plugins;
extern u64 song_data_size;
extern void *song_data;
@ -47,10 +48,12 @@ Keybindings P2_LEFT_RED = {.keycodes = {'X'}};
Keybindings P2_RIGHT_RED = {.keycodes = {'C'}};
Keybindings P2_RIGHT_BLUE = {.keycodes = {'V'}};
int exited = 0;
bool testEnabled = false;
int coin_count = 0;
int service_count = 0;
bool inited = false;
bool updateByCoin = false;
HWND windowHandle = nullptr;
HKL currentLayout;
@ -183,7 +186,10 @@ bnusio_GetAnalogIn (const u8 which) {
return 0;
}
u16 __fastcall bnusio_GetCoin (i32 a1) { return coin_count; }
u16 __fastcall bnusio_GetCoin (i32 a1) {
if (updateByCoin) bnusio::Update ();
return coin_count;
}
u16 __fastcall bnusio_GetService (i32 a1) { return service_count; }
}
@ -257,51 +263,12 @@ FUNCTION_PTR (i64, bnusio_DecCoin_Original, PROC_ADDRESS ("bnusio_original.dll",
FUNCTION_PTR (u64, bnusio_DecService_Original, PROC_ADDRESS ("bnusio_original.dll", "bnusio_DecService"), i32, u16);
FUNCTION_PTR (i64, bnusio_ResetCoin_Original, PROC_ADDRESS ("bnusio_original.dll", "bnusio_ResetCoin"));
HOOK (u64, bngrw_Init, PROC_ADDRESS ("bngrw.dll", "BngRwInit")) { return 0; }
HOOK (void, bngrw_Fin, PROC_ADDRESS ("bngrw.dll", "BngRwFin")) {}
HOOK (u64, bngrw_IsCmdExec, PROC_ADDRESS ("bngrw.dll", "BngRwIsCmdExec")) { return 0xFFFFFFFF; }
HOOK (i32, bngrw_ReqCancel, PROC_ADDRESS ("bngrw.dll", "BngRwReqCancel")) { return 1; }
HOOK (i32, bngrw_ReqSendUrl, PROC_ADDRESS ("bngrw.dll", "BngRwReqSendUrlTo")) { return 1; }
HOOK (u64, bngrw_ReqLed, PROC_ADDRESS ("bngrw.dll", "BngRwReqLed"), u32 a1, u32 ledType, i64 a3, i64 a4) { return 1; }
HOOK (u64, bngrw_ReqBeep, PROC_ADDRESS ("bngrw.dll", "BngRwReqBeep"), u32 a1, u32 beepType, i64 a3, i64 a4) { return 1; }
HOOK (u64, bngrw_ReqAction, PROC_ADDRESS ("bngrw.dll", "BngRwReqAction"), u32 a1, u32 actionType, i64 a3, i64 a4) { return 1; }
HOOK (u64, bngrw_SetLedPower, PROC_ADDRESS ("bngrw.dll", "BngRwSetLedPower")) { return 0; }
HOOK (u64, bngrw_GetRetryCount, PROC_ADDRESS ("bngrw.dll", "BngRwGetTotalRetryCount")) { return 0; }
HOOK (u64, bngrw_GetFwVersion, PROC_ADDRESS ("bngrw.dll", "BngRwGetFwVersion")) { return 0; }
HOOK (u64, bngrw_ReqFwVersionUp, PROC_ADDRESS ("bngrw.dll", "BngRwReqFwVersionUp")) { return 1; }
HOOK (u64, bngrw_ReqFwCleanup, PROC_ADDRESS ("bngrw.dll", "BngRwReqFwCleanup")) { return 1; }
HOOK (u64, bngrw_ReadMifare, PROC_ADDRESS ("bngrw.dll", "BngRwExReadMifareAllBlock")) { return 0xFFFFFF9C; }
HOOK (u64, bngrw_GetStationID, PROC_ADDRESS ("bngrw.dll", "BngRwGetStationID")) { return 0; }
HOOK (i32, bngrw_ReqSendMail, PROC_ADDRESS ("bngrw.dll", "BngRwReqSendMailTo")) { return 1; }
HOOK (i32, bngrw_ReqLatchID, PROC_ADDRESS ("bngrw.dll", "BngRwReqLatchID")) { return 1; }
HOOK (u64, bngrw_ReqAiccAuth, PROC_ADDRESS ("bngrw.dll", "BngRwReqAiccAuth")) { return 1; }
HOOK (u64, bngrw_DevReset, PROC_ADDRESS ("bngrw.dll", "BngRwDevReset")) { return 1; }
HOOK (u64, bngrw_Attach, PROC_ADDRESS ("bngrw.dll", "BngRwAttach"), i32 a1, char *a2, i32 a3, i32 a4, callbackAttach callback, i32 *attachDataH) {
LogMessage (LogLevel::DEBUG, "BngRwAttach");
// This is way too fucking jank
attachCallback = callback;
attachData = attachDataH;
return 1;
}
HOOK (u64, bngrw_ReqWaitTouch, PROC_ADDRESS ("bngrw.dll", "BngRwReqWaitTouch"), u32 a1, i32 a2, u32 a3, callbackTouch callbackH, u64 touchDataH) {
LogMessage (LogLevel::DEBUG, "BngRwReqWaitTouch");
touchCallback = callbackH;
if (emulateCardReader) {
waitingForTouch = true;
touchData = touchDataH;
for (const auto plugin : plugins)
if (const FARPROC touchEvent = GetProcAddress (plugin, "WaitTouch"))
reinterpret_cast<waitTouchEvent *> (touchEvent) (callbackH, touchDataH);
return 1;
}
// This is called when we use an original card reader and acceptInvalidCards is set to true
return originalbngrw_ReqWaitTouch (a1, a2, a3, InspectWaitTouch, touchDataH);
}
void
Init () {
SetKeyboardButtons ();
int fpsLimit = 120;
const auto configPath = std::filesystem::current_path () / "config.toml";
const std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
if (config_ptr) {
@ -311,8 +278,16 @@ Init () {
analogInput = readConfigBool (controller, "analog_input", analogInput);
if (analogInput) LogMessage (LogLevel::WARN, "Using analog input mode. All the keyboard drum inputs have been disabled.");
}
auto graphics = openConfigSection (config, "graphics");
if (graphics) {
fpsLimit = readConfigInt (graphics, "fpslimit", fpsLimit);
}
}
updateByCoin = fpsLimit == 0;
if (updateByCoin) {
LogMessage (LogLevel::INFO, "fpsLimit is set to 0, bnusio::Update() will invoke in getCoin callback");
}
const auto keyConfigPath = std::filesystem::current_path () / "keyconfig.toml";
const std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> keyConfig_ptr (openConfig (keyConfigPath), toml_free);
if (keyConfig_ptr) {
@ -392,113 +367,50 @@ Init () {
INSTALL_HOOK_DIRECT (bnusio_DecCoin, bnusio_DecCoin_Original);
INSTALL_HOOK_DIRECT (bnusio_DecService, bnusio_DecService_Original);
INSTALL_HOOK_DIRECT (bnusio_ResetCoin, bnusio_ResetCoin_Original);
LogMessage (LogLevel::WARN, "USIO emulation disabled");
}
if (emulateCardReader) {
INSTALL_HOOK (bngrw_Init)
INSTALL_HOOK (bngrw_Fin);
INSTALL_HOOK (bngrw_IsCmdExec);
INSTALL_HOOK (bngrw_ReqCancel);
INSTALL_HOOK (bngrw_ReqWaitTouch);
INSTALL_HOOK (bngrw_ReqSendUrl);
INSTALL_HOOK (bngrw_ReqLed);
INSTALL_HOOK (bngrw_ReqBeep);
INSTALL_HOOK (bngrw_ReqAction);
INSTALL_HOOK (bngrw_SetLedPower);
INSTALL_HOOK (bngrw_GetRetryCount);
INSTALL_HOOK (bngrw_GetFwVersion);
INSTALL_HOOK (bngrw_ReqFwVersionUp);
INSTALL_HOOK (bngrw_ReqFwCleanup);
INSTALL_HOOK (bngrw_ReadMifare);
INSTALL_HOOK (bngrw_GetStationID);
INSTALL_HOOK (bngrw_ReqSendMail);
INSTALL_HOOK (bngrw_ReqLatchID);
INSTALL_HOOK (bngrw_ReqAiccAuth);
INSTALL_HOOK (bngrw_Attach);
INSTALL_HOOK (bngrw_DevReset);
} else {
LogMessage (LogLevel::WARN, "Card reader emulation disabled");
if (acceptInvalidCards) {
LogMessage (LogLevel::WARN, "Original reader will accept invalid cards!");
INSTALL_HOOK (bngrw_ReqWaitTouch);
}
}
}
void
Update () {
if (exited && ++exited >= 50) ExitProcess (0);
if (!inited) {
windowHandle = FindWindowA ("nuFoundation.Window", nullptr);
InitializePoll (windowHandle);
if (autoIme) {
currentLayout = GetKeyboardLayout (0);
const auto engLayout = LoadKeyboardLayout (TEXT ("00000409"), KLF_ACTIVATE);
currentLayout = GetKeyboardLayout (0);
auto engLayout = LoadKeyboardLayout (TEXT ("00000409"), KLF_ACTIVATE);
ActivateKeyboardLayout (engLayout, KLF_SETFORPROCESS);
}
for (const auto plugin : plugins)
if (const auto initEvent = GetProcAddress (plugin, "Init")) initEvent ();
patches::Plugins::Init ();
inited = true;
}
UpdatePoll (windowHandle);
std::vector<uint8_t> buffer = {};
if (IsButtonTapped (COIN_ADD) && !testEnabled) coin_count++;
if (IsButtonTapped (SERVICE) && !testEnabled) service_count++;
if (IsButtonTapped (SERVICE) && !testEnabled) service_count++;
if (IsButtonTapped (TEST)) testEnabled = !testEnabled;
if (IsButtonTapped (EXIT)) ExitProcess (0);
if (waitingForTouch) {
if (IsButtonTapped (CARD_INSERT_1) || IsButtonTapped (CARD_INSERT_2)) {
bool hasInserted = false;
const bool p1 = IsButtonTapped (CARD_INSERT_1);
static u8 cardData[168]
= {0x01, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x2E, 0x58, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x5C, 0x97, 0x44, 0xF0, 0x88, 0x04, 0x00, 0x43, 0x26, 0x2C, 0x33, 0x00, 0x04,
0x06, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36,
0x00, 0x00, 0xFA, 0xE9, 0x69, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
for (const auto plugin : plugins) {
FARPROC insertEvent = GetProcAddress (plugin, p1 ? "BeforeCard1Insert" : "BeforeCard2Insert");
if (insertEvent) reinterpret_cast<event *> (insertEvent) ();
insertEvent = GetProcAddress (plugin, p1 ? "Card1Insert" : "Card2Insert");
if (insertEvent) {
reinterpret_cast<event *> (insertEvent) ();
hasInserted = true;
waitingForTouch = false;
break;
}
}
if (!hasInserted) {
LogMessage (LogLevel::INFO, "Inserting card for player %d: %s", p1 ? 1 : 2, p1 ? accessCode1 : accessCode2);
memcpy (cardData + 0x2C, p1 ? chipId1 : chipId2, 33);
memcpy (cardData + 0x50, p1 ? accessCode1 : accessCode2, 21);
touchCallback (0, 0, cardData, touchData);
waitingForTouch = false;
}
}
if (IsButtonTapped (EXIT)) { exited += 1; testEnabled = 1; }
if (GameVersion::CHN00 == gameVersion) {
if (IsButtonTapped (CARD_INSERT_1)) patches::Scanner::Qr::CommitLogin (accessCode1);
if (IsButtonTapped (CARD_INSERT_2)) patches::Scanner::Qr::CommitLogin (accessCode2);
} else {
if (IsButtonTapped (CARD_INSERT_1)) patches::Scanner::Card::Commit (accessCode1, chipId1);
if (IsButtonTapped (CARD_INSERT_2)) patches::Scanner::Card::Commit (accessCode2, chipId2);
}
if (IsButtonTapped (QR_DATA_READ)) patches::Scanner::Qr::Commit (patches::Scanner::Qr::ReadQRData (buffer));
if (IsButtonTapped (QR_IMAGE_READ)) patches::Scanner::Qr::Commit (patches::Scanner::Qr::ReadQRImage (buffer));
for (const auto plugin : plugins)
if (const auto updateEvent = GetProcAddress (plugin, "Update")) updateEvent ();
patches::Qr::Update ();
if (attachCallback) attachCallback (0, 0, attachData);
patches::Plugins::Update ();
patches::Scanner::Update ();
}
void
Close () {
if (autoIme) ActivateKeyboardLayout (currentLayout, KLF_SETFORPROCESS);
for (const auto plugin : plugins)
if (const FARPROC exitEvent = GetProcAddress (plugin, "Exit")) reinterpret_cast<event *> (exitEvent) ();
patches::Plugins::Exit ();
CleanupLogger ();
}
} // namespace bnusio

View File

@ -25,6 +25,7 @@ char chipId2[33] = "00000000000000000000000000000002";
bool windowed = false;
bool autoIme = false;
bool jpLayout = false;
bool cursor = true;
bool emulateUsio = true;
bool emulateCardReader = true;
bool emulateQr = true;
@ -177,7 +178,10 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) {
acceptInvalidCards = readConfigBool (emulation, "accept_invalid", acceptInvalidCards);
emulateQr = readConfigBool (emulation, "qr", emulateQr);
}
if (const auto graphics = openConfigSection (config, "graphics")) windowed = readConfigBool (graphics, "windowed", windowed);
if (const auto graphics = openConfigSection (config, "graphics")) {
windowed = readConfigBool (graphics, "windowed", windowed);
cursor = readConfigBool (graphics, "cursor", cursor);
}
if (const auto keyboard = openConfigSection (config, "keyboard")) {
autoIme = readConfigBool (keyboard, "auto_ime", autoIme);
jpLayout = readConfigBool (keyboard, "jp_layout", jpLayout);
@ -210,20 +214,8 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) {
}
LogMessage (LogLevel::INFO, "GameVersion is %s", GameVersionToString (gameVersion));
if (const auto pluginPath = std::filesystem::current_path () / "plugins"; exists (pluginPath)) {
for (const auto &entry : std::filesystem::directory_iterator (pluginPath)) {
if (entry.path ().extension () == ".dll") {
auto name = entry.path ().wstring ();
auto shortName = entry.path ().filename ().wstring ();
if (HMODULE hModule = LoadLibraryW (name.c_str ()); !hModule) {
LogMessage (LogLevel::ERROR, L"Failed to load plugin " + shortName);
} else {
plugins.push_back (hModule);
LogMessage (LogLevel::INFO, L"Loaded plugin " + shortName);
}
}
}
}
patches::Plugins::LoadPlugins ();
patches::Plugins::InitVersion (gameVersion);
if (!std::filesystem::exists (".\\card.ini")) CreateCard ();
GetPrivateProfileStringA ("card", "accessCode1", accessCode1, accessCode1, 21, ".\\card.ini");
@ -233,7 +225,7 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) {
LogMessage (LogLevel::WARN, "Loading patches, please wait...");
INSTALL_HOOK (ShowMouse);
if (cursor) INSTALL_HOOK (ShowMouse);
INSTALL_HOOK (ExitWindows);
INSTALL_HOOK (CreateWindow);
INSTALL_HOOK (SetWindowPosition);
@ -262,7 +254,7 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) {
case GameVersion::CHN00: patches::CHN00::Init (); break;
}
patches::Qr::Init ();
patches::Scanner::Init ();
patches::Audio::Init ();
patches::Dxgi::Init ();
patches::AmAuth::Init ();

View File

@ -45,6 +45,15 @@ LogMessageHandler (const char *function, const char *codeFile, int codeLine, Log
// Determine log type string
std::string logType = GetLogLevelString (messageLevel);
int find_idx = 0, begin = -2, end = 0;
while (function[find_idx] != 0) {
if (begin == -2 && function[find_idx] == ' ') begin = -1;
else if (begin == -1 && function[find_idx] == ' ') begin = find_idx + 1;
else if (end == 0 && ((begin > 0 && function[find_idx] == ' ') || function[find_idx] == '(')) end = find_idx;
find_idx ++;
}
std::string short_function = std::string(function + begin, end - begin);
// Remove the absolute path of the build dir
constexpr std::string_view build_dir = XSTRING (SOURCE_ROOT);
std::string_view filename = codeFile;
@ -60,7 +69,7 @@ LogMessageHandler (const char *function, const char *codeFile, int codeLine, Log
// Construct the log message
std::ostringstream logStream;
logStream << function << " (" << filename << ":" << codeLine << "): " << formattedMessage;
logStream << short_function << " (" << filename << ":" << codeLine << "): " << formattedMessage;
std::string logMessage = logStream.str ();
// Print the log message

View File

@ -3,6 +3,7 @@
#include "helpers.h"
#include <source_location>
#include <string_view>
#include <format>
#include <wincon.h>
#define STRING(x) #x
@ -41,14 +42,16 @@ void LogMessageHandler (const char *function, const char *codeFile, int codeLine
*/
template <typename... Args>
struct LogMessage {
LogMessage (const LogLevel level, const std::string_view format, Args &&...ts,
LogMessage (const LogLevel level, const std::string_view format, Args &&...args,
const std::source_location &loc = std::source_location::current ()) {
LogMessageHandler (loc.function_name (), loc.file_name (), loc.line (), level, format.data (), std::forward<Args> (ts)...);
std::string formatted_message = std::vformat(std::string(format), std::make_format_args(args...));
LogMessageHandler (loc.function_name (), loc.file_name (), loc.line (), level, formatted_message.c_str ());
}
LogMessage (const LogLevel level, const std::wstring_view format, Args &&...ts,
LogMessage (const LogLevel level, const std::wstring_view format, Args &&...args,
const std::source_location &loc = std::source_location::current ()) {
LogMessageHandler (loc.function_name (), loc.file_name (), loc.line (), level, format.data (), std::forward<Args> (ts)...);
std::wstring formatted_message = std::vformat(std::wstring(format), std::make_wformat_args(args...));
LogMessageHandler (loc.function_name (), loc.file_name (), loc.line (), level, formatted_message.c_str ());
}
};

View File

@ -22,6 +22,8 @@ bool wasapiShared = true;
bool asio = false;
std::string asioDriver;
float volumeRate = 0.0f;
HOOK_DYNAMIC (i64, NUSCDeviceInit, void *a1, nusc_init_config_t *a2, nusc_init_config_t *a3, void *a4) {
LogMessage (LogLevel::INFO, std::string ("Device mode is ") + (asio ? "ASIO" : wasapiShared ? "wasapi shared" : "wasapi exclusive"));
if (asio) LogMessage (LogLevel::INFO, (std::string ("ASIO driver is ") + asioDriver).c_str ());
@ -39,6 +41,19 @@ HOOK_DYNAMIC (bool, LoadASIODriver, void *a1, const char *a2) {
}
return result;
}
HOOK_DYNAMIC (u64, NuscBusVolume, u64 a1, u64 a2, float a3) {
if (volumeRate == 0.0f) {
int value = patches::TestMode::ReadTestModeValue (L"OutputLevelSpeakerItem");
if (value == -1) return originalNuscBusVolume (a1, a2, a3);
volumeRate = value <= 100 ? 1.0f : value / 100.0f;
}
return originalNuscBusVolume (a1, a2, a3 * volumeRate);
}
void
SetVolumeRate (float rate) {
volumeRate = rate;
}
void
Init () {
@ -68,6 +83,7 @@ Init () {
case GameVersion::JPN39: {
INSTALL_HOOK_DYNAMIC (NUSCDeviceInit, ASLR (0x1407C8620));
INSTALL_HOOK_DYNAMIC (LoadASIODriver, ASLR (0x1407D0F70));
INSTALL_HOOK_DYNAMIC (NuscBusVolume, ASLR (0x1407B1C30));
break;
}
case GameVersion::CHN00: {

View File

@ -212,16 +212,19 @@ Init () {
}
FpsLimiterEnable = fpsLimit > 0;
FpsLimiter::Init (static_cast<float> (fpsLimit));
MH_Initialize ();
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory", reinterpret_cast<LPVOID> (CreateDXGIFactoryWrap),
reinterpret_cast<void **> (&g_origCreateDXGIFactory));
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory2", reinterpret_cast<LPVOID> (CreateDXGIFactory2Wrap),
reinterpret_cast<void **> (&g_origCreateDXGIFactory2));
MH_CreateHookApi (L"d3d11.dll", "D3D11CreateDeviceAndSwapChain", reinterpret_cast<LPVOID> (D3D11CreateDeviceAndSwapChainWrap),
reinterpret_cast<void **> (&g_origD3D11CreateDeviceAndSwapChain));
MH_EnableHook (nullptr);
if (FpsLimiterEnable) {
FpsLimiter::Init (static_cast<float> (fpsLimit));
MH_Initialize ();
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory", reinterpret_cast<LPVOID> (CreateDXGIFactoryWrap),
reinterpret_cast<void **> (&g_origCreateDXGIFactory));
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory2", reinterpret_cast<LPVOID> (CreateDXGIFactory2Wrap),
reinterpret_cast<void **> (&g_origCreateDXGIFactory2));
MH_CreateHookApi (L"d3d11.dll", "D3D11CreateDeviceAndSwapChain", reinterpret_cast<LPVOID> (D3D11CreateDeviceAndSwapChainWrap),
reinterpret_cast<void **> (&g_origD3D11CreateDeviceAndSwapChain));
MH_EnableHook (nullptr);
}
}
} // namespace patches::Dxgi

View File

@ -3,6 +3,8 @@
#include <functional>
#include <pugixml.hpp>
#include "constants.h"
namespace patches {
namespace JPN00 {
void Init ();
@ -20,14 +22,15 @@ namespace Dxgi {
void Init ();
} // namespace Dxgi
namespace FpsLimiter {
void Init (float fpsLimit);
void Init (float fpsLimit);
void Update ();
} // namespace FpsLimiter
namespace Audio {
void Init ();
void Init ();
void SetVolumeRate (float rate);
} // namespace Audio
namespace Qr {
void Init ();
void Init ();
void Update ();
} // namespace Qr
namespace AmAuth {
@ -36,15 +39,62 @@ void Init ();
namespace LayeredFs {
void Init ();
void RegisterBefore (const std::function<std::string (const std::string, const std::string)> &fileHandler);
void RegisterAfter (const std::function<std::string (const std::string, const std::string)> &fileHandler);
void RegisterAfter (const std::function<std::string (const std::string, const std::string)> &fileHandler);
} // namespace LayeredFs
namespace TestMode {
typedef u64 (*RefTestModeMain) (u64);
void Init ();
void SetupAccessor (u64 appAccessor, RefTestModeMain refTestMode);
int ReadTestModeValue (const wchar_t *itemId);
void RegisterItem (const std::wstring& item, const std::function<void ()> &initMethod);
void RegisterModify (const std::wstring& query, const std::function<void (pugi::xml_node &)> &nodeModify, const std::function<void ()> &initMethod);
void Append (const pugi::xml_node &node, const wchar_t *attr, const std::wstring& append);
void Init ();
void Append (const pugi::xml_node &node, const wchar_t *attr, const std::wstring &append);
void SetupAccessor (u64 appAccessor, RefTestModeMain refTestMode);
int ReadTestModeValue (const wchar_t *itemId);
void RegisterItem (const std::wstring &item, const std::function<void ()> &initMethod);
void RegisterItemAfter (const std::wstring &query, const std::wstring &item, const std::function<void()> &initMethod);
void RegisterModify (const std::wstring &query, const std::function<void (pugi::xml_node &)> &nodeModify, const std::function<void ()> &initMethod);
} // namespace TestMode
namespace Plugins {
// typedefs
typedef void (*CallBackTouchCard) (int32_t, int32_t, uint8_t[168], uint64_t);
typedef bool (*CommitCardCallback) (std::string, std::string);
typedef bool (*CommitQrCallback) (std::vector<uint8_t> &);
typedef bool (*CommitQrLoginCallback) (std::string);
// Standard API
void Init ();
void Update ();
void Exit ();
// Lowlevel Card API
void WaitTouch (CallBackTouchCard callback, uint64_t touchData);
// Lowlevel QR API
void InitQr (GameVersion gameVersion);
void UsingQr ();
void * CheckQr ();
size_t GetQr (void *plugin, size_t size, uint8_t *buffer);
// New API
void InitVersion (GameVersion gameVersion);
void InitCardReader (CommitCardCallback touch);
void InitQRScanner (CommitQrCallback scan);
void InitQRLogin (CommitQrLoginCallback login);
void UpdateStatus (size_t type, bool status);
// Plugins Loader
void LoadPlugins ();
} // namespace Plugins
namespace Scanner {
enum class State { Disable, Ready, CopyWait };
void Init ();
void Update ();
namespace Card {
typedef int32_t (*CallbackAttach) (int32_t, int32_t, int32_t *);
typedef void (*CallbackTouch) (int32_t, int32_t, uint8_t[168], uint64_t);
void Init ();
void Update ();
bool Commit (std::string accessCode, std::string chipId);
} // namespace Card
namespace Qr {
void Init ();
void Update ();
bool Commit (std::vector<uint8_t> &buffer);
bool CommitLogin (std::string accessCode);
std::vector<uint8_t> &ReadQRData (std::vector<uint8_t> &buffer);
std::vector<uint8_t> &ReadQRImage (std::vector<uint8_t> &buffer);
} // namespace Qr
} // namespace Scanner
} // namespace patches

137
src/patches/plugins.cpp Normal file
View File

@ -0,0 +1,137 @@
#include "constants.h"
#include "helpers.h"
#include "patches.h"
namespace patches::Plugins {
std::vector<HMODULE> plugins = {};
typedef void (*BasicEvent) ();
typedef void (*WaitTouchEvent) (CallBackTouchCard callback, uint64_t touchData);
typedef void (*SendVersionEvent) (GameVersion gameVersion);
typedef bool (*CheckEvent) ();
typedef size_t (*CopyDataEvent) (size_t size, uint8_t *buffer);
typedef void (*SendCardReaderEvent) (CommitCardCallback touch);
typedef void (*SendQRScannerEvent) (CommitQrCallback scan);
typedef void (*SendQRLoginEvent) (CommitQrLoginCallback login);
typedef void (*StatusChangeEvent) (size_t type, bool status);
void
Init () {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "Init");
if (event) ((BasicEvent)event) ();
}
}
void
Update () {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "Update");
if (event) ((BasicEvent)event) ();
}
}
void
Exit () {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "Exit");
if (event) ((BasicEvent)event) ();
}
}
// Card API
void
WaitTouch (CallBackTouchCard callback, uint64_t touchData) {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "WaitTouch");
if (event) ((WaitTouchEvent)event) (callback, touchData);
}
}
// QR API (deprecated)
void
InitQr (GameVersion gameVersion) {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "InitQr");
if (event) ((SendVersionEvent)event) (gameVersion);
}
}
void
UsingQr () {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "UsingQr");
if (event) ((BasicEvent)event) ();
}
}
void *
CheckQr () {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "UsingQr");
if (event && ((CheckEvent)event) ()) return plugin;
}
return nullptr;
}
size_t
GetQr (void *plugin, size_t size, uint8_t *buffer) {
auto event = GetProcAddress (*(HMODULE *)plugin, "GetQr");
if (event) return ((CopyDataEvent)event) (size, buffer);
else return 0;
}
// New API
void
InitVersion (GameVersion gameVersion) {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "InitVersion");
if (event) ((SendVersionEvent)event) (gameVersion);
}
}
void
InitCardReader (CommitCardCallback touch) {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "InitCardReader");
if (event) ((SendCardReaderEvent)event) (touch);
}
}
void
InitQRScanner (CommitQrCallback scan) {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "InitQRScanner");
if (event) ((SendQRScannerEvent)event) (scan);
}
}
void
InitQRLogin (CommitQrLoginCallback login) {
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "InitQRLogin");
if (event) ((SendQRLoginEvent)event) (login);
}
}
void
UpdateStatus (size_t type, bool status) {
// printWarning ("Send UpdateStatus type=%d status=%d", type, status);
for (auto plugin : plugins) {
auto event = GetProcAddress (plugin, "UpdateStatus");
if (event) ((StatusChangeEvent)event) (type, status);
}
}
// Plugins Loader
void LoadPlugins () {
auto pluginPath = std::filesystem::current_path () / "plugins";
if (std::filesystem::exists (pluginPath)) {
for (const auto &entry : std::filesystem::directory_iterator (pluginPath)) {
if (entry.path ().extension () == ".dll") {
auto name = entry.path ().wstring ();
auto shortName = entry.path ().filename ().wstring ();
if (HMODULE hModule = LoadLibraryW (name.c_str ()); !hModule) {
LogMessage (LogLevel::ERROR, L"Failed to load plugin " + shortName);
} else {
plugins.push_back (hModule);
LogMessage (LogLevel::INFO, L"Loaded plugin " + shortName);
}
}
}
}
}
}

View File

@ -1,298 +0,0 @@
#include "constants.h"
#include "helpers.h"
#include "poll.h"
#include <ReadBarcode.h>
#include <vector>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_WINDOWS_UTF8
#include "stb_image.h"
extern GameVersion gameVersion;
extern Keybindings CARD_INSERT_1;
extern Keybindings CARD_INSERT_2;
extern Keybindings QR_DATA_READ;
extern Keybindings QR_IMAGE_READ;
extern char accessCode1[21];
extern char accessCode2[21];
extern std::vector<HMODULE> plugins;
extern bool emulateQr;
typedef void event ();
typedef void initQrEvent (GameVersion gameVersion);
typedef bool checkQrEvent ();
typedef int getQrEvent (int, unsigned char *);
namespace patches::Qr {
enum class State { Ready, CopyWait };
enum class Mode { Card, Data, Image, Plugin };
auto gState = State::Ready;
auto gMode = Mode::Card;
HMODULE gPlugin;
std::string accessCode;
std::vector<HMODULE> qrPlugins;
bool qrPluginRegistered = false;
HOOK_DYNAMIC (char, QrInit, i64) { return 1; }
HOOK_DYNAMIC (char, QrClose, i64) { return 1; }
HOOK_DYNAMIC (char, QrRead, i64 a1) {
*reinterpret_cast<DWORD *> (a1 + 40) = 1;
*reinterpret_cast<DWORD *> (a1 + 16) = 1;
*reinterpret_cast<BYTE *> (a1 + 112) = 0;
return 1;
}
HOOK_DYNAMIC (i64, CallQrUnknown, i64) { return 1; }
HOOK_DYNAMIC (bool, Send1, i64 a1) {
*reinterpret_cast<BYTE *> (a1 + 88) = 1;
*reinterpret_cast<i64 *> (a1 + 32) = *reinterpret_cast<i64 *> (a1 + 24);
*reinterpret_cast<WORD *> (a1 + 89) = 0;
return true;
}
HOOK_DYNAMIC (bool, Send2, i64 a1) {
*reinterpret_cast<WORD *> (a1 + 88) = 0;
*reinterpret_cast<BYTE *> (a1 + 90) = 0;
return true;
}
HOOK_DYNAMIC (bool, Send3, i64, char) { return true; }
HOOK_DYNAMIC (bool, Send4, i64, const void *, i64) { return true; }
HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
if (gState == State::CopyWait) {
// std::cout << "Copy data, length: " << length << std::endl;
auto configPath = std::filesystem::current_path () / "config.toml";
std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
if (gMode == Mode::Card) {
memcpy (dest, accessCode.c_str (), accessCode.size () + 1);
gState = State::Ready;
return accessCode.size () + 1;
}
if (gMode == Mode::Data) {
std::string serial = "";
u16 type = 0;
std::vector<i64> songNoes;
if (config_ptr) {
if (auto qr = openConfigSection (config_ptr.get (), "qr")) {
if (auto data = openConfigSection (qr, "data")) {
serial = readConfigString (data, "serial", "");
type = static_cast<u16> (readConfigInt (data, "type", 0));
songNoes = readConfigIntArray (data, "song_no", songNoes);
}
}
}
BYTE serial_length = static_cast<BYTE> (serial.size ());
std::vector<BYTE> byteBuffer = {0x53, 0x31, 0x32, 0x00, 0x00, 0xFF, 0xFF, serial_length, 0x01, 0x00};
for (char c : serial)
byteBuffer.push_back (static_cast<BYTE> (c));
if (type == 5) {
std::vector<BYTE> folderData = {0xFF, 0xFF};
folderData.push_back (static_cast<u8> (songNoes.size ()) * 2);
folderData.push_back (static_cast<u8> (type & 0xFF));
folderData.push_back (static_cast<u8> (type >> 8 & 0xFF));
for (i64 songNo : songNoes) {
folderData.push_back (static_cast<u8> (songNo & 0xFF));
folderData.push_back (static_cast<u8> (songNo >> 8 & 0xFF));
}
for (auto c : folderData)
byteBuffer.push_back (c);
}
byteBuffer.push_back (0xEE);
byteBuffer.push_back (0xFF);
std::stringstream hexStream;
for (auto byteData : byteBuffer)
hexStream << std::hex << std::uppercase << std::setfill ('0') << std::setw (2) << static_cast<int> (byteData) << " ";
LogMessage (LogLevel::INFO, ("Data dump: " + hexStream.str ()).c_str ());
memcpy (dest, byteBuffer.data (), byteBuffer.size ());
gState = State::Ready;
return byteBuffer.size ();
}
if (gMode == Mode::Image) {
std::string imagePath = "";
if (config_ptr) {
if (auto qr = openConfigSection (config_ptr.get (), "qr")) imagePath = readConfigString (qr, "image_path", "");
}
std::u8string u8PathStr (imagePath.begin (), imagePath.end ());
std::filesystem::path u8Path (u8PathStr);
if (!is_regular_file (u8Path)) {
LogMessage (LogLevel::ERROR, ("Failed to open image: " + u8Path.string () + " (file not found)").c_str ());
gState = State::Ready;
return 0;
}
int width, height, channels;
std::unique_ptr<stbi_uc, void (*) (void *)> buffer (stbi_load (u8Path.string ().c_str (), &width, &height, &channels, 3),
stbi_image_free);
if (!buffer) {
LogMessage (LogLevel::ERROR, ("Failed to read image: " + u8Path.string () + " (" + stbi_failure_reason () + ")").c_str ());
gState = State::Ready;
return 0;
}
ZXing::ImageView image{buffer.get (), width, height, ZXing::ImageFormat::RGB};
auto result = ReadBarcode (image);
if (!result.isValid ()) {
LogMessage (LogLevel::ERROR, ("Failed to read QR: " + imagePath + " (" + ToString (result.error ()) + ")").c_str ());
gState = State::Ready;
return 0;
}
// std::cout << "Valid" << std::endl;
auto byteData = result.bytes ();
// std::cout << ZXing::ToHex (byteData) << std::endl;
auto dataSize = byteData.size ();
memcpy (dest, byteData.data (), dataSize);
gState = State::Ready;
return dataSize;
}
if (gMode == Mode::Plugin) {
if (FARPROC getEvent = GetProcAddress (gPlugin, "GetQr")) {
std::vector<unsigned char> plugin_data (length);
int buf_len = reinterpret_cast<getQrEvent *> (getEvent) (length, plugin_data.data ());
if (0 < buf_len && buf_len <= length) {
std::stringstream hexStream;
for (int i = 0; i < buf_len; i++)
hexStream << std::hex << std::uppercase << std::setfill ('0') << std::setw (2) << static_cast<int> (plugin_data[i]) << " ";
LogMessage (LogLevel::INFO, "QR dump: " + hexStream.str ());
memcpy (dest, plugin_data.data (), buf_len);
} else {
LogMessage (LogLevel::ERROR, ("QR discard! Length invalid: " + std::to_string (buf_len) + ", valid range: 0~").c_str ());
}
gState = State::Ready;
return buf_len;
}
gState = State::Ready;
return 0;
}
} else if (qrPluginRegistered) {
for (auto plugin : qrPlugins)
if (FARPROC usingQrEvent = GetProcAddress (plugin, "UsingQr")) reinterpret_cast<event *> (usingQrEvent) ();
}
return 0;
}
void
Update () {
if (!emulateQr) return;
if (gState == State::Ready) {
// std::cout << "Insert" << std::endl;
if (IsButtonTapped (CARD_INSERT_1)) {
if (gameVersion != GameVersion::CHN00) return;
accessCode = "BNTTCNID";
accessCode += accessCode1;
gState = State::CopyWait;
gMode = Mode::Card;
} else if (IsButtonTapped (CARD_INSERT_2)) {
if (gameVersion != GameVersion::CHN00) return;
accessCode = "BNTTCNID";
accessCode += accessCode2;
gState = State::CopyWait;
gMode = Mode::Card;
} else if (IsButtonTapped (QR_DATA_READ)) {
gState = State::CopyWait;
gMode = Mode::Data;
} else if (IsButtonTapped (QR_IMAGE_READ)) {
gState = State::CopyWait;
gMode = Mode::Image;
} else if (qrPluginRegistered) {
for (const auto plugin : qrPlugins) {
if (const FARPROC checkEvent = GetProcAddress (plugin, "CheckQr"); checkEvent && reinterpret_cast<checkQrEvent *> (checkEvent) ()) {
gState = State::CopyWait;
gMode = Mode::Plugin;
gPlugin = plugin;
break;
}
}
}
}
}
void
Init () {
LogMessage (LogLevel::INFO, "Init Qr patches");
if (!emulateQr) {
LogMessage (LogLevel::WARN, "QR emulation disabled");
return;
}
for (auto plugin : plugins) {
if (const FARPROC initEvent = GetProcAddress (plugin, "InitQr")) reinterpret_cast<initQrEvent *> (initEvent) (gameVersion);
if (GetProcAddress (plugin, "UsingQr")) qrPlugins.push_back (plugin);
}
if (qrPlugins.size () > 0) {
LogMessage (LogLevel::INFO, "QR plugin found!");
qrPluginRegistered = true;
}
SetConsoleOutputCP (CP_UTF8);
const auto amHandle = reinterpret_cast<u64> (GetModuleHandle ("AMFrameWork.dll"));
switch (gameVersion) {
case GameVersion::JPN00: {
INSTALL_HOOK_DYNAMIC (QrInit, reinterpret_cast<LPVOID> (amHandle + 0x1B3E0));
INSTALL_HOOK_DYNAMIC (QrClose, reinterpret_cast<LPVOID> (amHandle + 0x1B5B0));
INSTALL_HOOK_DYNAMIC (QrRead, reinterpret_cast<LPVOID> (amHandle + 0x1B600));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, reinterpret_cast<LPVOID> (amHandle + 0xFD40));
INSTALL_HOOK_DYNAMIC (Send1, reinterpret_cast<LPVOID> (amHandle + 0x1BBB0));
INSTALL_HOOK_DYNAMIC (Send2, reinterpret_cast<LPVOID> (amHandle + 0x1BBF0));
INSTALL_HOOK_DYNAMIC (Send3, reinterpret_cast<LPVOID> (amHandle + 0x1BC60));
// JPN00 has no Send4
INSTALL_HOOK_DYNAMIC (CopyData, reinterpret_cast<LPVOID> (amHandle + 0x1BC30));
break;
}
case GameVersion::JPN08: {
INSTALL_HOOK_DYNAMIC (QrInit, reinterpret_cast<LPVOID> (amHandle + 0x1BA00));
INSTALL_HOOK_DYNAMIC (QrClose, reinterpret_cast<LPVOID> (amHandle + 0x1BBD0));
INSTALL_HOOK_DYNAMIC (QrRead, reinterpret_cast<LPVOID> (amHandle + 0x1BC20));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, reinterpret_cast<LPVOID> (amHandle + 0xFD40));
INSTALL_HOOK_DYNAMIC (Send1, reinterpret_cast<LPVOID> (amHandle + 0x1C220));
INSTALL_HOOK_DYNAMIC (Send2, reinterpret_cast<LPVOID> (amHandle + 0x1C260));
INSTALL_HOOK_DYNAMIC (Send3, reinterpret_cast<LPVOID> (amHandle + 0x1C2D0));
// JPN08 has no Send4
INSTALL_HOOK_DYNAMIC (CopyData, reinterpret_cast<LPVOID> (amHandle + 0x1C2A0));
break;
}
case GameVersion::JPN39: {
INSTALL_HOOK_DYNAMIC (QrInit, reinterpret_cast<LPVOID> (amHandle + 0x1EDC0));
INSTALL_HOOK_DYNAMIC (QrClose, reinterpret_cast<LPVOID> (amHandle + 0x1EF60));
INSTALL_HOOK_DYNAMIC (QrRead, reinterpret_cast<LPVOID> (amHandle + 0x1EFB0));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, reinterpret_cast<LPVOID> (amHandle + 0x11A70));
INSTALL_HOOK_DYNAMIC (Send1, reinterpret_cast<LPVOID> (amHandle + 0x1F5B0));
INSTALL_HOOK_DYNAMIC (Send2, reinterpret_cast<LPVOID> (amHandle + 0x1F5F0));
INSTALL_HOOK_DYNAMIC (Send3, reinterpret_cast<LPVOID> (amHandle + 0x1F660));
INSTALL_HOOK_DYNAMIC (Send4, reinterpret_cast<LPVOID> (amHandle + 0x1F690));
INSTALL_HOOK_DYNAMIC (CopyData, reinterpret_cast<LPVOID> (amHandle + 0x1F630));
break;
}
case GameVersion::CHN00: {
INSTALL_HOOK_DYNAMIC (QrInit, reinterpret_cast<LPVOID> (amHandle + 0x161B0));
INSTALL_HOOK_DYNAMIC (QrClose, reinterpret_cast<LPVOID> (amHandle + 0x16350));
INSTALL_HOOK_DYNAMIC (QrRead, reinterpret_cast<LPVOID> (amHandle + 0x163A0));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, reinterpret_cast<LPVOID> (amHandle + 0x8F60));
INSTALL_HOOK_DYNAMIC (Send1, reinterpret_cast<LPVOID> (amHandle + 0x16940));
INSTALL_HOOK_DYNAMIC (Send2, reinterpret_cast<LPVOID> (amHandle + 0x16990));
INSTALL_HOOK_DYNAMIC (Send3, reinterpret_cast<LPVOID> (amHandle + 0x16A00));
INSTALL_HOOK_DYNAMIC (Send4, reinterpret_cast<LPVOID> (amHandle + 0x16A30));
INSTALL_HOOK_DYNAMIC (CopyData, reinterpret_cast<LPVOID> (amHandle + 0x169D0));
break;
}
default: {
break;
}
}
}
} // namespace patches::Qr

478
src/patches/scanner.cpp Normal file
View File

@ -0,0 +1,478 @@
#include "constants.h"
#include "helpers.h"
#include "patches.h"
#include <ReadBarcode.h>
#include <chrono>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <vector>
#include <queue>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_WINDOWS_UTF8
#include "stb_image.h"
extern GameVersion gameVersion;
extern std::vector<HMODULE> plugins;
extern bool acceptInvalidCards;
extern bool emulateCardReader;
extern bool emulateQr;
extern char accessCode1[21];
extern char accessCode2[21];
extern char chipId1[33];
extern char chipId2[33];
namespace patches::Scanner {
namespace Card {
State state = State::Disable;
i32 *attachData;
u64 touchData;
CallbackAttach callbackAttach;
CallbackTouch callbackTouch;
std::string accessCodeTemplate = "00000000000000000000";
std::string chipIdTemplate = "00000000000000000000000000000000";
u8 cardData[168] = {
0x01, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x2E, 0x58, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x5C, 0x97, 0x44, 0xF0, 0x88, 0x04, 0x00, 0x43, 0x26, 0x2C, 0x33, 0x00, 0x04,
0x06, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36,
0x00, 0x00, 0xFA, 0xE9, 0x69, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
namespace Internal {
void
AgentInsertCard (uint8_t cardData[168], bool internalInvoke) {
if (callbackTouch) {
state = internalInvoke ? State::Disable : State::CopyWait;
patches::Plugins::UpdateStatus (1, false);
callbackTouch (0, 0, cardData, touchData);
}
}
void
AgentInsertCardPlugin (int32_t a1, int32_t a2, uint8_t *a3, uint64_t a4) {
if (callbackTouch) {
state = State::CopyWait;
patches::Plugins::UpdateStatus (1, false);
callbackTouch (a1, a2, a3, a4);
}
}
void
AgentInsertCardOfficial (int32_t a1, int32_t a2, uint8_t *a3, uint64_t a4) {
if (callbackTouch) {
state = State::CopyWait;
patches::Plugins::UpdateStatus (1, false);
if (acceptInvalidCards && !a3[0]) {
char AccessId[21] = "00000000000000000001";
uint8_t UID[8] = {a3[12], a3[14], a3[15], a3[16], 0x90, 0x00, 0x00, 0x00};
uint64_t ReversedAccessID;
for (int i = 0; i < 8; i++)
ReversedAccessID = (ReversedAccessID << 8) | UID[i];
sprintf(AccessId, "%020llu", ReversedAccessID);
std::string accessCode = std::string (AccessId), chipId = "";
if (chipId.length () == 0) chipId = accessCode;
if (chipId.length () < 32) chipId = accessCodeTemplate.substr (0, 32 - chipId.length ()) + chipId;
for (size_t i = 0; i < 32; i++) *(cardData + 0x2C + i) = i < chipId.length() ? chipId[i] : 0;
for (size_t i = 0; i < 20; i++) *(cardData + 0x50 + i) = i < accessCode.length() ? accessCode[i] : 0;
callbackTouch (0, 0, cardData, a4);
} else callbackTouch (a1, a2, a3, a4);
}
}
bool
Commit0 (std::string accessCode, std::string chipId, bool internalInvoke) {
if (chipId.length () == 0) chipId = accessCode;
if (chipId.length () < 32) chipId = accessCodeTemplate.substr (0, 32 - chipId.length ()) + chipId;
for (size_t i = 0; i < 32; i++) *(cardData + 0x2C + i) = i < chipId.length() ? chipId[i] : 0;
for (size_t i = 0; i < 20; i++) *(cardData + 0x50 + i) = i < accessCode.length() ? accessCode[i] : 0;
if (!internalInvoke) LogMessage (LogLevel::INFO, "[Card] Insert Card accessCode: \"{}\" chipId: \"{}\"\n", accessCode, chipId);
patches::Scanner::Card::Internal::AgentInsertCard (cardData, internalInvoke);
return true;
}
}
HOOK (u64, bngrw_Init, PROC_ADDRESS ("bngrw.dll", "BngRwInit")) { return 0; }
HOOK (void, bngrw_Fin, PROC_ADDRESS ("bngrw.dll", "BngRwFin")) { return; }
HOOK (u64, bngrw_IsCmdExec, PROC_ADDRESS ("bngrw.dll", "BngRwIsCmdExec")) { return 0xFFFFFFFF; }
HOOK (i32, bngrw_ReqSendUrl, PROC_ADDRESS ("bngrw.dll", "BngRwReqSendUrlTo")) { return 1; }
HOOK (u64, bngrw_ReqLed, PROC_ADDRESS ("bngrw.dll", "BngRwReqLed")) { return 1; }
HOOK (u64, bngrw_ReqBeep, PROC_ADDRESS ("bngrw.dll", "BngRwReqBeep")) { return 1; }
HOOK (u64, bngrw_ReqAction, PROC_ADDRESS ("bngrw.dll", "BngRwReqAction")) { return 1; }
HOOK (u64, bngrw_ReqSetLedPower, PROC_ADDRESS ("bngrw.dll", "BngRwReqSetLedPower")) { return 0; }
HOOK (u64, bngrw_GetRetryCount, PROC_ADDRESS ("bngrw.dll", "BngRwGetTotalRetryCount")) { return 0; }
HOOK (u64, bngrw_GetFwVersion, PROC_ADDRESS ("bngrw.dll", "BngRwGetFwVersion")) { return 0; }
HOOK (u64, bngrw_ReqFwVersionUp, PROC_ADDRESS ("bngrw.dll", "BngRwReqFwVersionUp")) { return 1; }
HOOK (u64, bngrw_ReqFwCleanup, PROC_ADDRESS ("bngrw.dll", "BngRwReqFwCleanup")) { return 1; }
HOOK (u64, bngrw_ReadMifare, PROC_ADDRESS ("bngrw.dll", "BngRwExReadMifareAllBlock")) { return 0xFFFFFF9C; }
HOOK (u64, bngrw_GetStationID, PROC_ADDRESS ("bngrw.dll", "BngRwGetStationID")) { return 0; }
HOOK (i32, bngrw_ReqSendMail, PROC_ADDRESS ("bngrw.dll", "BngRwReqSendMailTo")) { return 1; }
HOOK (i32, bngrw_ReqLatchID, PROC_ADDRESS ("bngrw.dll", "BngRwReqLatchID")) { return 1; }
HOOK (u64, bngrw_ReqAiccAuth, PROC_ADDRESS ("bngrw.dll", "BngRwReqAiccAuth")) { return 1; }
HOOK (u64, bngrw_DevReset, PROC_ADDRESS ("bngrw.dll", "BngRwDevReset")) { return 1; } // Invoke when enter testmode
HOOK (i32, bngrw_ReqCancel, PROC_ADDRESS ("bngrw.dll", "BngRwReqCancel")) {
LogMessage (LogLevel::DEBUG, "[Card] Cancel!\n");
if (state != State::Disable) {
state = State::Disable;
patches::Scanner::Card::Internal::Commit0 (accessCodeTemplate, chipIdTemplate, true);
}
return 1;
}
HOOK (u64, bngrw_Attach, PROC_ADDRESS ("bngrw.dll", "BngRwAttach"), i32 a1, char *a2, i32 a3, i32 a4, CallbackAttach callback, i32 *a6) {
callbackAttach = callback;
attachData = a6;
return 1;
}
HOOK (u64, bngrw_ReqWaitTouch, PROC_ADDRESS ("bngrw.dll", "BngRwReqWaitTouch"), u32 a1, i32 a2, u32 a3, CallbackTouch callback, u64 a5) {
state = State::Ready;
patches::Plugins::UpdateStatus (1, true);
patches::Plugins::WaitTouch (Internal::AgentInsertCardPlugin, a5);
callbackTouch = callback;
touchData = a5;
return 1;
}
HOOK (i64, bngrw_ReqCancelOfficial, PROC_ADDRESS ("bngrw.dll", "BngRwReqCancel"), u32 a1) {
if (state != State::Disable) {
state = State::Disable;
patches::Plugins::UpdateStatus (1, false);
}
return originalbngrw_ReqCancelOfficial(a1);
}
HOOK (u64, bngrw_ReqWaitTouchOfficial, PROC_ADDRESS ("bngrw.dll", "BngRwReqWaitTouch"), u32 a1, i32 a2, u32 a3, CallbackTouch callback, u64 a5) {
state = State::Ready;
patches::Plugins::UpdateStatus (1, true);
callbackTouch = callback;
touchData = a5;
return originalbngrw_ReqWaitTouchOfficial (a1, a2, a3, Internal::AgentInsertCardOfficial, a5);
}
bool
Commit(std::string accessCode, std::string chipId) {
if (!emulateCardReader) {
LogMessage (LogLevel::DEBUG, "[Card] Not emulate CardReader!\n");
return false;
}
if (state != State::Ready) {
LogMessage (LogLevel::DEBUG, "[Card] Not Waiting for Touch, please wait!\n");
return false;
}
if (accessCode.length() == 0 || accessCode.length() > 20) {
LogMessage (LogLevel::ERROR, "[Card] Not an effective accessCode: \"{}\"\n", accessCode);
return false;
}
if (chipId.length() > 32) {
LogMessage (LogLevel::ERROR, "[Card] Not an effective chipId: \"{}\"\n", chipId);
return false;
}
return patches::Scanner::Card::Internal::Commit0 (accessCode, chipId, false);
}
void
Update() {
if (callbackAttach) callbackAttach (0, 0, attachData);
}
void
Init() {
LogMessage (LogLevel::INFO, "Init Card patches");
if (!emulateCardReader) {
LogMessage (LogLevel::WARN, "[Card] Card reader emulation disabled!\n");
INSTALL_HOOK (bngrw_ReqCancelOfficial);
INSTALL_HOOK (bngrw_ReqWaitTouchOfficial);
// patches::Plugins::InitCardReader (patches::Scanner::Card::Commit);
return;
}
INSTALL_HOOK (bngrw_Init)
INSTALL_HOOK (bngrw_Fin);
INSTALL_HOOK (bngrw_IsCmdExec);
INSTALL_HOOK (bngrw_ReqCancel);
INSTALL_HOOK (bngrw_ReqWaitTouch);
INSTALL_HOOK (bngrw_ReqSendUrl);
INSTALL_HOOK (bngrw_ReqLed);
INSTALL_HOOK (bngrw_ReqBeep);
INSTALL_HOOK (bngrw_ReqAction);
INSTALL_HOOK (bngrw_ReqSetLedPower);
INSTALL_HOOK (bngrw_GetRetryCount);
INSTALL_HOOK (bngrw_GetFwVersion);
INSTALL_HOOK (bngrw_ReqFwVersionUp);
INSTALL_HOOK (bngrw_ReqFwCleanup);
INSTALL_HOOK (bngrw_ReadMifare);
INSTALL_HOOK (bngrw_GetStationID);
INSTALL_HOOK (bngrw_ReqSendMail);
INSTALL_HOOK (bngrw_ReqLatchID);
INSTALL_HOOK (bngrw_ReqAiccAuth);
INSTALL_HOOK (bngrw_Attach);
INSTALL_HOOK (bngrw_DevReset);
patches::Plugins::InitCardReader (patches::Scanner::Card::Commit);
}
}
namespace Qr {
std::queue<std::vector<uint8_t>> scanQueue;
State state = State::Disable;
long long lastScan;
HOOK_DYNAMIC (char, QrInit, i64) { return 1; }
HOOK_DYNAMIC (char, QrClose, i64) { return 1; }
HOOK_DYNAMIC (char, QrRead, i64 a1) {
*(DWORD *)(a1 + 40) = 1;
*(DWORD *)(a1 + 16) = 1;
*(BYTE *)(a1 + 112) = 0;
return 1;
}
HOOK_DYNAMIC (i64, CallQrUnknown, i64) { return 1; }
HOOK_DYNAMIC (bool, Send1, i64 a1) {
*(BYTE *)(a1 + 88) = 1;
*(i64 *)(a1 + 32) = *(i64 *)(a1 + 24);
*(WORD *)(a1 + 89) = 0;
return true;
}
HOOK_DYNAMIC (bool, Send2, i64 a1) {
*(WORD *)(a1 + 88) = 0;
*(BYTE *)(a1 + 90) = 0;
return true;
}
HOOK_DYNAMIC (bool, Send3, i64, char) { return true; }
HOOK_DYNAMIC (bool, Send4, i64, const void *, i64) { return true; }
HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
patches::Plugins::UsingQr ();
lastScan = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ();
if (state == State::CopyWait && scanQueue.size () > 0) {
std::vector<uint8_t> data = scanQueue.front ();
if ((int)data.size () > length) {
LogMessage (LogLevel::ERROR, "[QR] Not an effective code, length: {} require: {}\n", data.size (), length);
}
std::stringstream hexStream;
hexStream << std::hex << std::uppercase << std::setfill ('0') << std::setw (2);
for (size_t i = 0; i < data.size (); i++) hexStream << static_cast<int> (data[i]) << " ";
LogMessage (LogLevel::INFO, "[QR] Read QRData size: {} data: {}\n", data.size (), hexStream.str ());
memcpy (dest, data.data (), data.size () + 1);
scanQueue.pop ();
if (scanQueue.size () == 0) state = State::Ready;
return data.size ();
} else if (state == State::Disable) {
while (!scanQueue.empty ()) scanQueue.pop ();
state = State::Ready;
patches::Plugins::UpdateStatus (2, true);
}
return 0;
}
bool
Commit (std::vector<uint8_t> &buffer) {
if (!emulateQr) {
LogMessage (LogLevel::DEBUG, "[QR] Not emulate QR Scanner!\n");
return false;
}
if (state == State::Disable) {
LogMessage (LogLevel::DEBUG, "[QR] Not Ready to accept QRData!\n");
return false;
}
if (buffer.size () == 0) {
LogMessage (LogLevel::ERROR, "[QR] Not an effective code, length: 0\n");
return false;
}
if (scanQueue.empty() || scanQueue.back() != buffer) {
std::vector<uint8_t> scanData = {};
for (uint8_t byte_data : buffer)
scanData.push_back (byte_data);
scanQueue.push (scanData);
}
if (state == State::Ready) state = State::CopyWait;
return true;
}
bool
CommitLogin (std::string accessCode) {
if (!emulateQr) {
LogMessage (LogLevel::DEBUG, "[QR] Not emulate QR Scanner!\n");
return false;
}
std::vector<uint8_t> buffer = {};
std::string accessQRDataBase = "BNTTCNID";
if (!accessCode._Starts_with (accessQRDataBase))
for (char word : accessQRDataBase) buffer.push_back (word);
for (char word : accessCode) buffer.push_back (word);
return patches::Scanner::Qr::Commit (buffer);
}
void
Update () {
if (state != State::Disable) {
if ((lastScan + 200) < std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ()) {
state = State::Disable;
patches::Plugins::UpdateStatus (2, false);
} else {
void *plugin = patches::Plugins::CheckQr ();
if (plugin) {
uint8_t *space = (uint8_t *)calloc (600, sizeof (uint8_t));
size_t size = patches::Plugins::GetQr (plugin, 600, space);
if (size > 0) {
std::vector<uint8_t> data = {};
for (size_t i = 0; i < size; i ++) data.push_back (space[i]);
patches::Scanner::Qr::Commit (data);
}
}
}
}
}
std::vector<uint8_t> &
ReadQRData (std::vector<uint8_t> &buffer) {
std::string serial = "";
u16 type = 0;
std::vector<i64> songNoes;
buffer.clear ();
auto configPath = std::filesystem::current_path () / "config.toml";
std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
if (config_ptr) {
auto qr = openConfigSection (config_ptr.get (), "qr");
if (qr) {
auto data = openConfigSection (qr, "data");
if (data) {
serial = readConfigString (data, "serial", "");
type = (u16) readConfigInt (data, "type", 0);
songNoes = readConfigIntArray (data, "song_no", songNoes);
}
}
}
std::vector<uint8_t> header = { 0x53, 0x31, 0x32, 0x00, 0x00, 0xFF, 0xFF, (uint8_t)serial.size (), 0x01, 0x00 };
for (uint8_t byte_data : header) buffer.push_back (byte_data);
for (char word : serial) buffer.push_back ((uint8_t)word);
if (type == 5) {
std::vector<uint8_t> folder = { 0xFF, 0xFF, (uint8_t)(songNoes.size () * 2), (uint8_t)(type & 0xFF), (uint8_t)((type >> 8) & 0xFF) };
for (uint8_t byte_data : folder) buffer.push_back (byte_data);
for (i64 songNo : songNoes) {
buffer.push_back ((uint8_t)(songNo & 0xFF));
buffer.push_back ((uint8_t)((songNo >> 8) & 0xFF));
}
}
buffer.push_back (0xEE);
buffer.push_back (0xFF);
return buffer;
}
std::vector<uint8_t> &
ReadQRImage (std::vector<uint8_t> &buffer) {
std::string imagePath = "";
buffer.clear ();
auto configPath = std::filesystem::current_path () / "config.toml";
std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
if (config_ptr) {
auto qr = openConfigSection (config_ptr.get (), "qr");
if (qr) imagePath = readConfigString (qr, "image_path", "");
}
std::u8string u8PathStr (imagePath.begin (), imagePath.end ());
std::filesystem::path u8Path (u8PathStr);
if (!std::filesystem::is_regular_file (u8Path)) {
LogMessage (LogLevel::ERROR, "Failed to open image: {} (file not found)\n", u8Path.string());
return buffer;
}
int width, height, channels;
std::unique_ptr<stbi_uc, void (*) (void *)> img_buffer (stbi_load (u8Path.string ().c_str (), &width, &height, &channels, 3), stbi_image_free);
if (!img_buffer) {
LogMessage (LogLevel::ERROR, "Failed to read image: {} ({})\n", u8Path.string(), stbi_failure_reason ());
return buffer;
}
ZXing::ImageView image{img_buffer.get (), width, height, ZXing::ImageFormat::RGB};
auto result = ReadBarcode (image);
if (!result.isValid ()) {
LogMessage (LogLevel::ERROR, "Failed to read QR: {} ({})\n", u8Path.string(), ToString (result.error ()));
return buffer;
}
for (uint8_t byte_data : result.bytes()) buffer.push_back (byte_data);
return buffer;
}
void
Init () {
LogMessage (LogLevel::INFO, "Init Qr patches");
if (!emulateQr) {
LogMessage (LogLevel::WARN, "[QR] QR emulation disabled!\n");
return;
}
patches::Plugins::InitQr (gameVersion);
SetConsoleOutputCP (CP_UTF8);
auto amHandle = reinterpret_cast<u64> (GetModuleHandle ("AMFrameWork.dll"));
switch (gameVersion) {
case GameVersion::JPN00: {
INSTALL_HOOK_DYNAMIC (QrInit, (LPVOID)(amHandle + 0x1B3E0));
INSTALL_HOOK_DYNAMIC (QrClose, (LPVOID)(amHandle + 0x1B5B0));
INSTALL_HOOK_DYNAMIC (QrRead, (LPVOID)(amHandle + 0x1B600));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, (LPVOID)(amHandle + 0xFD40));
INSTALL_HOOK_DYNAMIC (Send1, (LPVOID)(amHandle + 0x1BBB0));
INSTALL_HOOK_DYNAMIC (Send2, (LPVOID)(amHandle + 0x1BBF0));
INSTALL_HOOK_DYNAMIC (Send3, (LPVOID)(amHandle + 0x1BC60));
// JPN00 has no Send4
INSTALL_HOOK_DYNAMIC (CopyData, (LPVOID)(amHandle + 0x1BC30));
break;
}
case GameVersion::JPN08: {
INSTALL_HOOK_DYNAMIC (QrInit, (LPVOID)(amHandle + 0x1BA00));
INSTALL_HOOK_DYNAMIC (QrClose, (LPVOID)(amHandle + 0x1BBD0));
INSTALL_HOOK_DYNAMIC (QrRead, (LPVOID)(amHandle + 0x1BC20));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, (LPVOID)(amHandle + 0xFD40));
INSTALL_HOOK_DYNAMIC (Send1, (LPVOID)(amHandle + 0x1C220));
INSTALL_HOOK_DYNAMIC (Send2, (LPVOID)(amHandle + 0x1C260));
INSTALL_HOOK_DYNAMIC (Send3, (LPVOID)(amHandle + 0x1C2D0));
// JPN08 has no Send4
INSTALL_HOOK_DYNAMIC (CopyData, (LPVOID)(amHandle + 0x1C2A0));
break;
}
case GameVersion::JPN39: {
INSTALL_HOOK_DYNAMIC (QrInit, (LPVOID)(amHandle + 0x1EDC0));
INSTALL_HOOK_DYNAMIC (QrClose, (LPVOID)(amHandle + 0x1EF60));
INSTALL_HOOK_DYNAMIC (QrRead, (LPVOID)(amHandle + 0x1EFB0));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, (LPVOID)(amHandle + 0x11A70));
INSTALL_HOOK_DYNAMIC (Send1, (LPVOID)(amHandle + 0x1F5B0));
INSTALL_HOOK_DYNAMIC (Send2, (LPVOID)(amHandle + 0x1F5F0));
INSTALL_HOOK_DYNAMIC (Send3, (LPVOID)(amHandle + 0x1F660));
INSTALL_HOOK_DYNAMIC (Send4, (LPVOID)(amHandle + 0x1F690));
INSTALL_HOOK_DYNAMIC (CopyData, (LPVOID)(amHandle + 0x1F630));
break;
}
case GameVersion::CHN00: {
INSTALL_HOOK_DYNAMIC (QrInit, (LPVOID)(amHandle + 0x161B0));
INSTALL_HOOK_DYNAMIC (QrClose, (LPVOID)(amHandle + 0x16350));
INSTALL_HOOK_DYNAMIC (QrRead, (LPVOID)(amHandle + 0x163A0));
INSTALL_HOOK_DYNAMIC (CallQrUnknown, (LPVOID)(amHandle + 0x8F60));
INSTALL_HOOK_DYNAMIC (Send1, (LPVOID)(amHandle + 0x16940));
INSTALL_HOOK_DYNAMIC (Send2, (LPVOID)(amHandle + 0x16990));
INSTALL_HOOK_DYNAMIC (Send3, (LPVOID)(amHandle + 0x16A00));
INSTALL_HOOK_DYNAMIC (Send4, (LPVOID)(amHandle + 0x16A30));
INSTALL_HOOK_DYNAMIC (CopyData, (LPVOID)(amHandle + 0x169D0));
break;
}
default: {
break;
}
}
patches::Plugins::InitQRScanner (patches::Scanner::Qr::Commit);
patches::Plugins::InitQRLogin (patches::Scanner::Qr::CommitLogin);
}
}
void
Update() {
patches::Scanner::Card::Update ();
patches::Scanner::Qr::Update ();
}
void
Init() {
LogMessage (LogLevel::INFO, "Init Scanner patches");
patches::Scanner::Card::Init ();
patches::Scanner::Qr::Init ();
}
}

View File

@ -17,6 +17,17 @@ public:
this->registerInit = initMethod;
}
};
class RegisteredSingleItem {
public:
std::wstring query;
std::wstring selectItem;
std::function<void()> registerInit;
RegisteredSingleItem (const std::wstring query, const std::wstring selectItem, const std::function<void()> &initMethod) {
this->query = query;
this->selectItem = selectItem;
this->registerInit = initMethod;
}
};
class RegisteredModify {
public:
std::wstring query;
@ -29,10 +40,11 @@ public:
}
};
std::vector<RegisteredItem *> registeredItems = {};
std::vector<RegisteredModify *> registeredModifies = {};
std::wstring moddedInitial = L"";
std::wstring modded = L"";
std::vector<RegisteredItem *> registeredItems = {};
std::vector<RegisteredSingleItem *> registeredSingleItems = {};
std::vector<RegisteredModify *> registeredModifies = {};
std::wstring moddedInitial = L"";
std::wstring modded = L"";
u64 appAccessor = 0;
RefTestModeMain refTestMode = nullptr;
@ -51,7 +63,7 @@ CreateMenu (pugi::xml_document &menuMain, const std::wstring &menuId, const std:
menuHeader.append_attribute (L"type") = L"Header";
menuHeader.append_child (L"break-item");
pugi::xml_node menuTitle = menuHeader.append_child (L"text-item");
const std::wstring menuNameFull = L" " + menuName;
const std::wstring menuNameFull = L" " + menuName;
menuTitle.append_attribute (L"label") = menuNameFull.c_str ();
menuHeader.append_child (L"break-item");
menuHeader.append_child (L"break-item");
@ -84,8 +96,8 @@ CreateMenu (pugi::xml_document &menuMain, const std::wstring &menuId, const std:
std::wstring
ReadXMLFileSwitcher (std::wstring &fileName) {
const std::size_t pos = fileName.rfind (L"/");
std::wstring base = fileName.substr (0, pos + 1);
const std::wstring file = fileName.substr (pos + 1, fileName.size ());
std::wstring base = fileName.substr (0, pos + 1);
if (gameVersion == GameVersion::JPN39 && chsPatch) {
if (file.starts_with (L"DeviceInitialize")) base.append (L"DeviceInitialize_china.xml");
@ -98,7 +110,7 @@ ReadXMLFileSwitcher (std::wstring &fileName) {
HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t *lFileName) {
const auto originalFileName = std::wstring (lFileName);
std::wstring fileName = originalFileName;
std::wstring fileName = originalFileName;
if (fileName.ends_with (L"DeviceInitialize.xml") || fileName.ends_with (L"DeviceInitialize_asia.xml")
|| fileName.ends_with (L"DeviceInitialize_china.xml")) {
if (moddedInitial == L"") {
@ -156,6 +168,26 @@ HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t *lFi
topMenu.parent ().insert_copy_after (modMenu.first_child (), topMenu);
}
if (!registeredSingleItems.empty()) {
for (RegisteredSingleItem *item : registeredSingleItems) {
pugi::xpath_query singleQuery = pugi::xpath_query(item->query.c_str());
try {
pugi::xml_node singleNode = doc.select_node(singleQuery).node();
if (singleNode) {
pugi::xml_document singleItemDoc;
std::wstring itemLine = L"<root>" + item->selectItem + L"</root>";
if (singleItemDoc.load_string(itemLine.c_str())) {
pugi::xml_node breakItem = singleNode.parent().insert_child_after(L"break-item", singleNode);
singleNode.parent().insert_copy_after(singleItemDoc.first_child().first_child(), breakItem);
item->registerInit();
} else {
LogMessage (LogLevel::ERROR, L"Failed to parse option line: {}\n", item->selectItem);
}
}
} catch ([[maybe_unused]] std::exception &e) { LogMessage (LogLevel::ERROR, L"Failed to append node by xpath: {}\n", item->query); }
}
}
if (!registeredModifies.empty ()) {
for (const RegisteredModify *modify : registeredModifies) { auto modifyQuery = pugi::xpath_query (modify->query.c_str ());
try {
@ -163,7 +195,7 @@ HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t *lFi
modify->nodeModify (modifyNode);
modify->registerInit ();
}
} catch ([[maybe_unused]] std::exception &e) { LogMessage (LogLevel::ERROR, L"Failed to find node by xpath: " + modify->query); }
} catch ([[maybe_unused]] std::exception &e) { LogMessage (LogLevel::ERROR, L"Failed to find node by xpath: {}\n", modify->query); }
}
}
@ -189,50 +221,81 @@ CommonModify () {
void
LocalizationCHT () {
RegisterModify (
TestMode::RegisterModify(
L"/root/menu[@id='TopMenu']/layout[@type='Center']/menu-item[@menu='ModManagerMenu']",
[&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模組管理"); }, [] {});
RegisterModify (
[&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"模組管理"); }, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Header']/text-item",
[&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模組管理"); }, [] {});
RegisterModify (
[&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"模組管理"); }, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModFixLanguage']",
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"修復語言");
node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModUnlockSongs']",
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"解鎖歌曲");
node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓, 2:強制");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModFreezeTimer']",
[&] (const pugi::xml_node &node) {
node.attribute (L"label").set_value (L"凍結計時");
node.attribute (L"replace-text").set_value (L"0:關閉, 1:開啓");
},
[] {});
RegisterModify (
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"凍結計時");
node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo024']",
[&] (const pugi::xml_node &node) {
node.attribute (L"label").set_value (L"鬼滅之刃模式");
node.attribute (L"replace-text").set_value (L"0:黙認, 1:啓用, 2:僅刷卡");
},
[] {});
RegisterModify (
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"鬼滅之刃模式");
node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo025']",
[&] (const pugi::xml_node &node) {
node.attribute (L"label").set_value (L"航海王模式");
node.attribute (L"replace-text").set_value (L"0:黙認, 1:啓用, 2:僅刷卡");
},
[] {});
RegisterModify (
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"航海王模式");
node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo026']",
[&] (const pugi::xml_node &node) {
node.attribute (L"label").set_value (L"AI粗品模式");
node.attribute (L"replace-text").set_value (L"0:黙認, 1:啓用, 2:僅刷卡");
},
[] {});
RegisterModify (
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"AI粗品模式");
node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeAprilFool001']",
[&] (const pugi::xml_node &node) {
node.attribute (L"label").set_value (L"青春之達人模式");
node.attribute (L"replace-text").set_value (L"0:黙認, 1:啓用, 2:僅刷卡");
},
[] {});
RegisterModify (
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"青春之達人模式");
node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModInstantResult']",
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"即時保存");
node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓");
}, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/menu-item[@menu='TopMenu']",
[&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"離開"); }, [] {});
[&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"離開"); }, [](){}
);
TestMode::RegisterModify(
L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='AttractDemoItem']",
[&](pugi::xml_node &node) {
node.attribute(L"label").set_value(L"演示遊玩影片");
node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓");
}, [](){}
);
}
void
@ -326,13 +389,19 @@ ReadTestModeValue (const wchar_t *itemId) {
void
RegisterItem (const std::wstring &item, const std::function<void ()> &initMethod) {
LogMessage (LogLevel::DEBUG, L"Register Item " + item);
LogMessage (LogLevel::DEBUG, L"Register \n Item: {}", item);
registeredItems.push_back (new RegisteredItem (item, initMethod));
}
void
RegisterItemAfter (const std::wstring &query, const std::wstring &item, const std::function<void()> &initMethod) {
LogMessage (LogLevel::DEBUG, L"Register \n Query: {} \n Item: {}", query, item);
registeredSingleItems.push_back (new RegisteredSingleItem (query, item, initMethod));
}
void
RegisterModify (const std::wstring &query, const std::function<void (pugi::xml_node &)> &nodeModify, const std::function<void ()> &initMethod) {
LogMessage (LogLevel::DEBUG, L"Register Modify " + query);
LogMessage (LogLevel::DEBUG, L"Register \n Modify: {}", query);
registeredModifies.push_back (new RegisteredModify (query, nodeModify, initMethod));
}

View File

@ -61,6 +61,18 @@ GetUserStatus () {
return -1;
}
HOOK (bool, IsSongRelease, ASLR (0x1403F4510), i64 a1, i64 a2, int a3) {
if (TestMode::ReadTestModeValue (L"ModUnlockSongs") == 1) return true;
return originalIsSongRelease (a1, a2, a3);
}
HOOK (bool, IsSongReleasePlayer, ASLR (0x1403F45F0), i64 a1, u64 a2, i32 a3) {
if (TestMode::ReadTestModeValue (L"ModUnlockSongs") == 2) return true;
return originalIsSongReleasePlayer (a1, a2, a3);
}
MID_HOOK (DifficultyPanelCrown, ASLR (0x1403F2A25), SafetyHookContext &ctx) {
if (TestMode::ReadTestModeValue (L"ModUnlockSongs") != 1) return;
ctx.r15 |= 1;
}
HOOK (i64, AvailableMode_Collabo024, ASLR (0x1402DE710), i64 a1) {
LogMessage (LogLevel::HOOKS, "AvailableMode_Collabo024 was called");
if (const int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode"); tournamentMode == 1) return originalAvailableMode_Collabo024 (a1);
@ -184,15 +196,20 @@ HOOK (i64, GetLanguage, ASLR (0x140024AC0), i64 a1) {
}
HOOK (i64, GetRegionLanguage, ASLR (0x1401CE9B0), i64 a1) {
LogMessage (LogLevel::HOOKS, "GetRegionLanguage was called");
lua_settop (a1, 0);
lua_pushstring (a1, languageStr (language));
return 1;
if (patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) {
lua_settop (a1, 0);
lua_pushstring (a1, languageStr (language));
return 1;
} else return originalGetRegionLanguage (a1);
}
HOOK (i64, GetCabinetLanguage, ASLR (0x1401D1A60), i64, i64 a2) {
FUNCTION_PTR (void **, std_string_assign, ASLR (0x1400209E0), void **, const void *, size_t);
HOOK (i64, GetCabinetLanguage, ASLR (0x14014DB80), u64 *a1, i64 a2) {
LogMessage (LogLevel::HOOKS, "GetCabinetLanguage was called");
lua_settop (a2, 0);
lua_pushstring (a2, languageStr (language));
return 1;
i64 result = originalGetCabinetLanguage (a1, a2);
if (patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) {
std_string_assign ((void **)result, (const void*)languageStr (language), (language == 0 || language == 3) ? 3 : 5);
}
return result;
}
MID_HOOK (ChangeLanguageType, ASLR (0x1400B2016), SafetyHookContext &ctx) {
@ -353,15 +370,10 @@ HOOK (i64, LoadedBankAll, ASLR (0x1404C69F0), i64 a1) {
float soundRate = 1.0F;
HOOK (i32, SetMasterVolumeSpeaker, ASLR (0x140160330), i32 a1) {
LogMessage (LogLevel::HOOKS, "SetMasterVolumeSpeaker was called");
soundRate = static_cast<float> (a1 <= 100 ? 1.0F : a1 / 100.0);
patches::Audio::SetVolumeRate (a1 <= 100 ? 1.0f : a1 / 100.0f);
return originalSetMasterVolumeSpeaker (a1 > 100 ? 100 : a1);
}
HOOK (u64, NuscBusVolume, ASLR (0x1407B1C30), u64 a1, u64 a2, float a3) {
LogMessage (LogLevel::HOOKS, "NuscBusVolume was called");
return originalNuscBusVolume (a1, a2, a3 * soundRate);
}
std::string *fontName = nullptr;
HOOK (u8, SetupFontInfo, ASLR (0x14049D820), u64 a1, u64 a2, size_t a3, u64 a4) {
LogMessage (LogLevel::HOOKS, "SetupFontInfo was called");
@ -378,6 +390,17 @@ HOOK (u32, ReadFontInfoInt, ASLR (0x14049EAC0), u64 a1, u64 a2) {
return result;
}
MID_HOOK (AttractDemo, ASLR (0x14045A720), SafetyHookContext &ctx) {
if (TestMode::ReadTestModeValue (L"AttractDemoItem") == 1) ctx.r14 = 0;
}
HOOK (float, InitPlay, ASLR (0x1401345A0), u64 a1, i64 a2, i32 a3) {
LogMessage (LogLevel::DEBUG, "Enter InitPlay");
float result = originalInitPlay (a1, a2, a3);
LogMessage (LogLevel::DEBUG, "Exit InitPlay");
return result;
}
constexpr i32 datatableBufferSize = 1024 * 1024 * 12;
safetyhook::Allocation datatableBuffer1;
safetyhook::Allocation datatableBuffer2;
@ -441,6 +464,7 @@ Init () {
// Hook to get AppAccessor and ComponentAccessor
INSTALL_HOOK (DeviceCheck);
INSTALL_HOOK (luaL_newstate);
INSTALL_HOOK (InitPlay);
// Apply common config patch
WRITE_MEMORY (ASLR (0x140494533), i32, xRes);
@ -499,44 +523,66 @@ Init () {
ReplaceLeaBufferAddress (datatableBuffer3Addresses, datatableBuffer3.data ());
}
// Fix Language
TestMode::RegisterItem(
L"<select-item label=\"FIX LANGUAGE\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" group=\"Setting\" id=\"ModFixLanguage\" max=\"1\" min=\"0\" default=\"1\"/>",
[&]() { INSTALL_HOOK (GetLanguage); INSTALL_HOOK (GetRegionLanguage); INSTALL_HOOK (GetCabinetLanguage); }
);
// Unlock Songs
TestMode::RegisterItem(
L"<select-item label=\"UNLOCK SONGS\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON, 2:FORCE\" group=\"Setting\" id=\"ModUnlockSongs\" max=\"2\" min=\"0\" default=\"1\"/>",
[&]() { INSTALL_HOOK (IsSongRelease); INSTALL_HOOK (IsSongReleasePlayer); INSTALL_MID_HOOK (DifficultyPanelCrown); }
);
// Freeze Timer
TestMode::RegisterItem (L"<select-item label=\"FREEZE TIMER\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" "
L"group=\"Setting\" id=\"ModFreezeTimer\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_MID_HOOK (FreezeTimer); });
TestMode::RegisterItem (
L"<select-item label=\"FREEZE TIMER\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" "
L"group=\"Setting\" id=\"ModFreezeTimer\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_MID_HOOK (FreezeTimer); }
);
// Mode Unlock
TestMode::RegisterItem (L"<select-item label=\"KIMETSU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, "
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo024\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_Collabo024); });
TestMode::RegisterItem (L"<select-item label=\"ONE PIECE MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, "
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo025\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_Collabo025); });
TestMode::RegisterItem (L"<select-item label=\"AI SOSHINA MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, "
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo026\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_Collabo026); });
TestMode::RegisterItem (L"<select-item label=\"AOHARU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, "
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeAprilFool001\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_AprilFool001); });
TestMode::RegisterItem (L"<select-item label=\"INSTANT RESULT\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" "
L"group=\"Setting\" id=\"ModInstantResult\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] {
INSTALL_HOOK (SceneResultInitialize_Enso);
INSTALL_HOOK (SceneResultInitialize_AI);
INSTALL_HOOK (SceneResultInitialize_Collabo025);
INSTALL_HOOK (SceneResultInitialize_Collabo026);
INSTALL_HOOK (SceneResultInitialize_AprilFool);
INSTALL_HOOK (SendResultData_Enso);
INSTALL_HOOK (SendResultData_AI);
INSTALL_HOOK (SendResultData_Collabo025_026);
INSTALL_HOOK (SendResultData_AprilFool);
INSTALL_MID_HOOK (ChangeResultDataSize_Enso);
INSTALL_MID_HOOK (ChangeResultDataSize_AI);
INSTALL_MID_HOOK (ChangeResultDataSize_Collabo025_026);
INSTALL_MID_HOOK (ChangeResultDataSize_AprilFool);
INSTALL_MID_HOOK (ChangeResultDataIndex_Enso);
INSTALL_MID_HOOK (ChangeResultDataIndex_AI);
INSTALL_MID_HOOK (ChangeResultDataIndex_Collabo025_026);
INSTALL_MID_HOOK (ChangeResultDataIndex_AprilFool);
});
TestMode::RegisterItem (
L"<select-item label=\"KIMETSU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, "
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo024\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_Collabo024); }
);
TestMode::RegisterItem (
L"<select-item label=\"ONE PIECE MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, "
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo025\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_Collabo025); }
);
TestMode::RegisterItem (
L"<select-item label=\"AI SOSHINA MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, "
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo026\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_Collabo026); }
);
TestMode::RegisterItem (
L"<select-item label=\"AOHARU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, "
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeAprilFool001\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] { INSTALL_HOOK (AvailableMode_AprilFool001); }
);
TestMode::RegisterItem (
L"<select-item label=\"INSTANT RESULT\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" "
L"group=\"Setting\" id=\"ModInstantResult\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] {
INSTALL_HOOK (SceneResultInitialize_Enso);
INSTALL_HOOK (SceneResultInitialize_AI);
INSTALL_HOOK (SceneResultInitialize_Collabo025);
INSTALL_HOOK (SceneResultInitialize_Collabo026);
INSTALL_HOOK (SceneResultInitialize_AprilFool);
INSTALL_HOOK (SendResultData_Enso);
INSTALL_HOOK (SendResultData_AI);
INSTALL_HOOK (SendResultData_Collabo025_026);
INSTALL_HOOK (SendResultData_AprilFool);
INSTALL_MID_HOOK (ChangeResultDataSize_Enso);
INSTALL_MID_HOOK (ChangeResultDataSize_AI);
INSTALL_MID_HOOK (ChangeResultDataSize_Collabo025_026);
INSTALL_MID_HOOK (ChangeResultDataSize_AprilFool);
INSTALL_MID_HOOK (ChangeResultDataIndex_Enso);
INSTALL_MID_HOOK (ChangeResultDataIndex_AI);
INSTALL_MID_HOOK (ChangeResultDataIndex_Collabo025_026);
INSTALL_MID_HOOK (ChangeResultDataIndex_AprilFool);
}
);
// Unlimit Volume
TestMode::RegisterModify (
L"/root/menu[@id='SoundTestMenu']/layout[@type='Center']/select-item[@id='OutputLevelSpeakerItem']",
@ -545,10 +591,14 @@ Init () {
node.attribute (L"max").set_value (L"300");
node.attribute (L"delta").set_value (L"1");
},
[&] () {
INSTALL_HOOK (SetMasterVolumeSpeaker);
INSTALL_HOOK (NuscBusVolume);
});
[&] () { INSTALL_HOOK (SetMasterVolumeSpeaker); }
);
TestMode::RegisterItemAfter(
L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='AttractMovieItem']",
L"<select-item label=\"ATTRACT DEMO\" disable=\"True/ModFixLanguage:0\" param-offset-x=\"35\" replace-text=\"0:ON, 1:OFF\" group=\"Setting\" id=\"AttractDemoItem\" max=\"1\" min=\"0\" default=\"0\"/>",
[&](){ INSTALL_MID_HOOK (AttractDemo); }
);
// Instant Result
// TestMode::RegisterModify(
// L"/root/menu[@id='GameOptionsMenu']/layout[@type='Center']/select-item[@id='NumberOfStageItem']",