mirror of
https://github.com/esuo1198/TaikoArcadeLoader.git
synced 2026-09-30 02:38:12 +03:00
Merge pull request #23 from ryumiyax/Refactor
[Experimental] Language patch | Input Optimize | TestMode Optimize
This commit is contained in:
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Configure CMake
|
||||
run: cmake -B build -S . -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION=10.0.26100.0
|
||||
run: cmake -B build -S . -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION="10.0.26100.0"
|
||||
- name: Build
|
||||
run: cmake --build build --config Release --target bnusio --verbose
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -142,6 +142,7 @@ set(SOURCES
|
||||
src/patches/layeredfs.cpp
|
||||
src/patches/testmode.cpp
|
||||
src/patches/timer.cpp
|
||||
src/patches/language.cpp
|
||||
src/patches/versions/JPN00.cpp
|
||||
src/patches/versions/JPN08.cpp
|
||||
src/patches/versions/JPN39.cpp
|
||||
|
||||
@@ -8,6 +8,8 @@ It currently supports the following versions:
|
||||
* JPN 39.06
|
||||
* CHN 00.32 (based on JPN 32.09)
|
||||
|
||||
**Attention**: As far as we know, game couldn't run on graphic card equals or lower than GTX 7xx series(GTX 7xx is not supported either), so if game crashes before it shows `avaliableRefreshRate`, that most likely means your graphic card is not supported!
|
||||
|
||||
## Setup
|
||||
|
||||
Copy the extracted contents of `dist.zip` to the same directory as Taiko.exe
|
||||
|
||||
Vendored
-5
@@ -24,11 +24,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
|
||||
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)
|
||||
|
||||
|
||||
[emulation]
|
||||
usio = true # Disable this if you want to use an original Namco USIO board. you need to place bnusio_original.dll (unmodified bnusio.dll) in the executable folder.
|
||||
card_reader = true # Disable this if you want to use an original Namco card reader
|
||||
|
||||
+77
-106
@@ -62,7 +62,9 @@ u16 service_count = 0;
|
||||
bool inited = false;
|
||||
bool updateByCoin = false;
|
||||
HWND windowHandle = nullptr;
|
||||
bool usePoll = false;
|
||||
float axisThreshold = 0.6f;
|
||||
u8 inputState = 1 | (1 << 2);
|
||||
bool globalKeyboard = false;;
|
||||
HKL currentLayout;
|
||||
|
||||
namespace bnusio {
|
||||
@@ -137,16 +139,10 @@ bool valueStates[] = {false, false, false, false, false, false, false, false};
|
||||
Keybindings *analogButtons[]
|
||||
= {&P1_LEFT_BLUE, &P1_LEFT_RED, &P1_RIGHT_RED, &P1_RIGHT_BLUE, &P2_LEFT_BLUE, &P2_LEFT_RED, &P2_RIGHT_RED, &P2_RIGHT_BLUE};
|
||||
|
||||
// u16 cooldown[8] = { 0 };
|
||||
u8 cooldown[8] = { 0 };
|
||||
u16 buttonWaitPeriod[] = { 0, 0 };
|
||||
std::queue<u8> buttonQueue[] = { {}, {} };
|
||||
|
||||
// u16 buttonWaitPeriodP1 = 0;
|
||||
// u16 buttonWaitPeriodP2 = 0;
|
||||
// std::queue<u8> buttonQueueP1;
|
||||
// std::queue<u8> buttonQueueP2;
|
||||
|
||||
|
||||
SDLAxis analogBindings[] = {
|
||||
SDL_AXIS_LEFT_LEFT, SDL_AXIS_LEFT_RIGHT, SDL_AXIS_LEFT_DOWN, SDL_AXIS_LEFT_UP, // P1: LB, LR, RR, RB
|
||||
SDL_AXIS_RIGHT_LEFT, SDL_AXIS_RIGHT_RIGHT, SDL_AXIS_RIGHT_DOWN, SDL_AXIS_RIGHT_UP, // P2: LB, LR, RR, RB
|
||||
@@ -154,12 +150,14 @@ SDLAxis analogBindings[] = {
|
||||
|
||||
u16
|
||||
bnusio_GetAnalogIn (const u8 which) {
|
||||
// LogMessage (LogLevel::DEBUG, "GetAnalogIn p={}", which);
|
||||
if (analogMethod) return analogMethod (which);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
u16 __fastcall bnusio_GetCoin (i32 a1) {
|
||||
if (updateByCoin) bnusio::Update ();
|
||||
// LogMessage (LogLevel::DEBUG, "GetCoin p={}", a1);
|
||||
if (updateByCoin && a1 == 1) bnusio::Update ();
|
||||
return coin_count;
|
||||
}
|
||||
u16 __fastcall bnusio_GetService (i32 a1) { return service_count; }
|
||||
@@ -213,9 +211,8 @@ FUNCTION_PTR (i64, bnusio_ResetCoin_Original, PROC_ADDRESS ("bnusio_original.dll
|
||||
|
||||
u16 __fastcall
|
||||
AnalogInputAxis (const u8 which) {
|
||||
const u16 analogValue = static_cast<u16> (32768 * ControllerAxisIsDown (analogBindings[which]));
|
||||
if (analogValue > 100) return analogValue;
|
||||
else return 0;
|
||||
u16 analogIn = (u16)(32768 * ControllerAxisIsDown (analogBindings[which])) + 1;
|
||||
return analogIn > 100 ? analogIn : 0;
|
||||
}
|
||||
|
||||
u16 __fastcall
|
||||
@@ -248,6 +245,22 @@ AnalogInputWaitPeriod (const u8 which) {
|
||||
return analogValue;
|
||||
}
|
||||
|
||||
u8 blueCooldown = 1, redCooldown = 1;
|
||||
u16 __fastcall
|
||||
AnalogInputCooldown (const u8 which) {
|
||||
const auto button = analogButtons[which];
|
||||
const bool blue = !(which % 4 % 3);
|
||||
|
||||
if (cooldown[which] > 0) cooldown[which]--;
|
||||
if (IsButtonTapped (*button)) {
|
||||
if (cooldown[which] > 0) return 0;
|
||||
cooldown[which] = blue ? blueCooldown : redCooldown;
|
||||
const u16 hitValue = !valueStates[which] ? 50 : 51;
|
||||
valueStates[which] = !valueStates[which];
|
||||
return (hitValue << 15) / 100 + 1;
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
u16 __fastcall
|
||||
AnalogInputSimple (const u8 which) {
|
||||
const auto button = analogButtons[which];
|
||||
@@ -259,65 +272,6 @@ AnalogInputSimple (const u8 which) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef ASYNC_UPDATE
|
||||
void
|
||||
Update () {
|
||||
#else
|
||||
std::thread pollThread;
|
||||
std::thread updateThread;
|
||||
std::mutex syncMtx;
|
||||
std::condition_variable syncCV;
|
||||
|
||||
void
|
||||
UpdateLoop () {
|
||||
LogMessage (LogLevel::WARN, "(experimental) Using Async Update!");
|
||||
std::unique_lock<std::mutex> syncLock(syncMtx);
|
||||
while (exited < exitWait) {
|
||||
syncCV.wait (syncLock);
|
||||
#endif
|
||||
if (exited && ++exited >= exitWait) ExitProcess (0);
|
||||
|
||||
#ifndef ASYNC_IO
|
||||
if (!inited) {
|
||||
windowHandle = FindWindowA ("nuFoundation.Window", nullptr);
|
||||
InitializePoll (windowHandle);
|
||||
if (autoIme) {
|
||||
currentLayout = GetKeyboardLayout (0);
|
||||
auto engLayout = LoadKeyboardLayout (TEXT ("00000409"), KLF_ACTIVATE);
|
||||
ActivateKeyboardLayout (engLayout, KLF_SETFORPROCESS);
|
||||
}
|
||||
|
||||
patches::Plugins::Init ();
|
||||
inited = true;
|
||||
}
|
||||
|
||||
UpdatePoll (windowHandle);
|
||||
#endif
|
||||
std::vector<uint8_t> buffer = {};
|
||||
if (IsButtonTapped (COIN_ADD)) coin_count++;
|
||||
if (IsButtonTapped (SERVICE)) service_count++;
|
||||
if (IsButtonTapped (TEST)) testEnabled = !testEnabled;
|
||||
if (IsButtonTapped (EXIT)) {
|
||||
LogMessage (LogLevel::INFO, "Exit By Press Exit Button!");
|
||||
exited += 1; testEnabled = 1; patches::Plugins::Exit ();
|
||||
}
|
||||
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));
|
||||
|
||||
patches::Plugins::Update ();
|
||||
patches::Scanner::Update ();
|
||||
#ifdef ASYNC_UPDATE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
SetKeyboardButtons ();
|
||||
@@ -331,6 +285,7 @@ Init () {
|
||||
if (const auto controller = openConfigSection (config, "controller")) {
|
||||
drumWaitPeriod = static_cast<short> (readConfigInt (controller, "wait_period", drumWaitPeriod));
|
||||
analogInput = readConfigBool (controller, "analog_input", analogInput);
|
||||
globalKeyboard = readConfigBool (controller, "global_keyboard", globalKeyboard);
|
||||
}
|
||||
auto graphics = openConfigSection (config, "graphics");
|
||||
if (graphics) {
|
||||
@@ -344,6 +299,9 @@ Init () {
|
||||
} else if (drumWaitPeriod > 0) {
|
||||
LogMessage (LogLevel::WARN, "[Analog Type] WaitPeriod: Fast input might be queued");
|
||||
analogMethod = AnalogInputWaitPeriod;
|
||||
} else if (drumWaitPeriod == 0) {
|
||||
LogMessage (LogLevel::INFO, "[Analog Type] Cooldown: Fastest and original input");
|
||||
analogMethod = AnalogInputCooldown;
|
||||
} else {
|
||||
LogMessage (LogLevel::INFO, "[Analog Type] Simple: Fastest and original input");
|
||||
analogMethod = AnalogInputSimple;
|
||||
@@ -356,35 +314,36 @@ Init () {
|
||||
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) {
|
||||
if (analogInput) usePoll = true;
|
||||
inputState = 0;
|
||||
if (analogInput) inputState |= (1 << 2);
|
||||
const toml_table_t *keyConfig = keyConfig_ptr.get ();
|
||||
SetConfigValue (keyConfig, "EXIT", &EXIT, &usePoll);
|
||||
SetConfigValue (keyConfig, "EXIT", &EXIT, &inputState);
|
||||
|
||||
SetConfigValue (keyConfig, "TEST", &TEST, &usePoll);
|
||||
SetConfigValue (keyConfig, "SERVICE", &SERVICE, &usePoll);
|
||||
SetConfigValue (keyConfig, "DEBUG_UP", &DEBUG_UP, &usePoll);
|
||||
SetConfigValue (keyConfig, "DEBUG_DOWN", &DEBUG_DOWN, &usePoll);
|
||||
SetConfigValue (keyConfig, "DEBUG_ENTER", &DEBUG_ENTER, &usePoll);
|
||||
SetConfigValue (keyConfig, "TEST", &TEST, &inputState);
|
||||
SetConfigValue (keyConfig, "SERVICE", &SERVICE, &inputState);
|
||||
SetConfigValue (keyConfig, "DEBUG_UP", &DEBUG_UP, &inputState);
|
||||
SetConfigValue (keyConfig, "DEBUG_DOWN", &DEBUG_DOWN, &inputState);
|
||||
SetConfigValue (keyConfig, "DEBUG_ENTER", &DEBUG_ENTER, &inputState);
|
||||
|
||||
SetConfigValue (keyConfig, "COIN_ADD", &COIN_ADD, &usePoll);
|
||||
SetConfigValue (keyConfig, "CARD_INSERT_1", &CARD_INSERT_1, &usePoll);
|
||||
SetConfigValue (keyConfig, "CARD_INSERT_2", &CARD_INSERT_2, &usePoll);
|
||||
SetConfigValue (keyConfig, "QR_DATA_READ", &QR_DATA_READ, &usePoll);
|
||||
SetConfigValue (keyConfig, "QR_IMAGE_READ", &QR_IMAGE_READ, &usePoll);
|
||||
SetConfigValue (keyConfig, "COIN_ADD", &COIN_ADD, &inputState);
|
||||
SetConfigValue (keyConfig, "CARD_INSERT_1", &CARD_INSERT_1, &inputState);
|
||||
SetConfigValue (keyConfig, "CARD_INSERT_2", &CARD_INSERT_2, &inputState);
|
||||
SetConfigValue (keyConfig, "QR_DATA_READ", &QR_DATA_READ, &inputState);
|
||||
SetConfigValue (keyConfig, "QR_IMAGE_READ", &QR_IMAGE_READ, &inputState);
|
||||
|
||||
SetConfigValue (keyConfig, "P1_LEFT_BLUE", &P1_LEFT_BLUE, &usePoll);
|
||||
SetConfigValue (keyConfig, "P1_LEFT_RED", &P1_LEFT_RED, &usePoll);
|
||||
SetConfigValue (keyConfig, "P1_RIGHT_RED", &P1_RIGHT_RED, &usePoll);
|
||||
SetConfigValue (keyConfig, "P1_RIGHT_BLUE", &P1_RIGHT_BLUE, &usePoll);
|
||||
SetConfigValue (keyConfig, "P2_LEFT_BLUE", &P2_LEFT_BLUE, &usePoll);
|
||||
SetConfigValue (keyConfig, "P2_LEFT_RED", &P2_LEFT_RED, &usePoll);
|
||||
SetConfigValue (keyConfig, "P2_RIGHT_RED", &P2_RIGHT_RED, &usePoll);
|
||||
SetConfigValue (keyConfig, "P2_RIGHT_BLUE", &P2_RIGHT_BLUE, &usePoll);
|
||||
if (!usePoll) {
|
||||
LogMessage (LogLevel::WARN, "Only Keyboard config detected, disable poll!");
|
||||
}
|
||||
SetConfigValue (keyConfig, "P1_LEFT_BLUE", &P1_LEFT_BLUE, &inputState);
|
||||
SetConfigValue (keyConfig, "P1_LEFT_RED", &P1_LEFT_RED, &inputState);
|
||||
SetConfigValue (keyConfig, "P1_RIGHT_RED", &P1_RIGHT_RED, &inputState);
|
||||
SetConfigValue (keyConfig, "P1_RIGHT_BLUE", &P1_RIGHT_BLUE, &inputState);
|
||||
SetConfigValue (keyConfig, "P2_LEFT_BLUE", &P2_LEFT_BLUE, &inputState);
|
||||
SetConfigValue (keyConfig, "P2_LEFT_RED", &P2_LEFT_RED, &inputState);
|
||||
SetConfigValue (keyConfig, "P2_RIGHT_RED", &P2_RIGHT_RED, &inputState);
|
||||
SetConfigValue (keyConfig, "P2_RIGHT_BLUE", &P2_RIGHT_BLUE, &inputState);
|
||||
}
|
||||
|
||||
LogMessage (LogLevel::INFO, "Finish Loading keyconfig.toml useKeyboard={} useMouse={} useController={}",
|
||||
(inputState & 1) ? "true" : "false", (inputState & (1 << 1)) ? "true" : "false", (inputState & (1 << 2)) ? "true" : "false");
|
||||
|
||||
if (!emulateUsio && !exists (std::filesystem::current_path () / "bnusio_original.dll")) {
|
||||
emulateUsio = true;
|
||||
LogMessage (LogLevel::ERROR, "bnusio_original.dll not found! usio emulation enabled");
|
||||
@@ -438,33 +397,45 @@ Init () {
|
||||
INSTALL_HOOK_DIRECT (bnusio_ResetCoin, bnusio_ResetCoin_Original);
|
||||
LogMessage (LogLevel::WARN, "USIO emulation disabled");
|
||||
}
|
||||
#ifdef ASYNC_UPDATE
|
||||
else {
|
||||
updateThread = std::thread (bnusio::UpdateLoop);
|
||||
updateThread.detach ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ASYNC_UPDATE
|
||||
void
|
||||
Update () {
|
||||
if (exited && ++exited >= exitWait) ExitProcess (0);
|
||||
if (!inited) {
|
||||
windowHandle = FindWindowA ("nuFoundation.Window", nullptr);
|
||||
if (usePoll) InitializePoll (windowHandle);
|
||||
InitializePoll (windowHandle);
|
||||
if (autoIme) {
|
||||
currentLayout = GetKeyboardLayout (0);
|
||||
auto engLayout = LoadKeyboardLayout (TEXT ("00000409"), KLF_ACTIVATE);
|
||||
ActivateKeyboardLayout (engLayout, KLF_SETFORPROCESS);
|
||||
}
|
||||
|
||||
patches::Plugins::Init ();
|
||||
inited = true;
|
||||
}
|
||||
UpdatePoll (windowHandle, usePoll);
|
||||
syncCV.notify_all ();
|
||||
UpdatePoll (windowHandle);
|
||||
|
||||
std::vector<uint8_t> buffer = {};
|
||||
if (IsButtonTapped (COIN_ADD)) coin_count++;
|
||||
if (IsButtonTapped (SERVICE)) service_count++;
|
||||
if (IsButtonTapped (TEST)) testEnabled = !testEnabled;
|
||||
if (exited == 0 && IsButtonTapped (EXIT)) {
|
||||
LogMessage (LogLevel::INFO, "Exit by Press Exit Button!");
|
||||
exited += 1; testEnabled = 1; std::thread([](){patches::Plugins::Exit ();}).detach();
|
||||
}
|
||||
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));
|
||||
|
||||
patches::Plugins::Update ();
|
||||
patches::Scanner::Update ();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
Close () {
|
||||
|
||||
@@ -7,4 +7,9 @@ enum class GameVersion : XXH64_hash_t {
|
||||
JPN08 = 0x67C0F3042746D488,
|
||||
JPN39 = 0x49F643ADB6B18705,
|
||||
CHN00 = 0xA7EE39F2CC2C57C8,
|
||||
};
|
||||
|
||||
enum StatusType {
|
||||
CardStatus = 1,
|
||||
QrStatus = 2
|
||||
};
|
||||
+6
-53
@@ -140,51 +140,6 @@ CreateCard () {
|
||||
WritePrivateProfileStringA ("card", "chipId2", buf, ".\\card.ini");
|
||||
}
|
||||
|
||||
void
|
||||
ElevateProcess () {
|
||||
HANDLE hToken;
|
||||
TOKEN_PRIVILEGES tp;
|
||||
LUID luid;
|
||||
|
||||
// 获取当前进程的访问令牌
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
||||
LogMessage (LogLevel::ERROR, "OpenProcessToken failed: {}", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取 SE_DEBUG_NAME 权限的 LUID
|
||||
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
|
||||
LogMessage (LogLevel::ERROR, "LookupPrivilegeValue failed: {}", GetLastError());
|
||||
CloseHandle(hToken);
|
||||
return;
|
||||
}
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// 调整权限
|
||||
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL)) {
|
||||
LogMessage (LogLevel::ERROR, "AdjustTokenPrivileges failed: {}", GetLastError());
|
||||
CloseHandle(hToken);
|
||||
return;
|
||||
}
|
||||
|
||||
LogMessage (LogLevel::INFO, "Privileges adjusted successfully!");
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
bool DisableDEP() {
|
||||
if (SetProcessDEPPolicy(PROCESS_DEP_ENABLE)) {
|
||||
// std::cout << "DEP disabled successfully.\n";
|
||||
return true;
|
||||
} else {
|
||||
LogMessage (LogLevel::ERROR, "Failed to disable DEP: {}", GetLastError());
|
||||
// std::cerr << "Failed to disable DEP: " << GetLastError() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
DllMain (HMODULE module, const DWORD reason, LPVOID reserved) {
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
@@ -192,16 +147,13 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) {
|
||||
// I/O in DllMain can easily cause a deadlock
|
||||
|
||||
// Init logger for loading config
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
InitializeLogger (GetLogLevel (logLevelStr), logToFile);
|
||||
// ElevateProcess ();
|
||||
// DisableDEP ();
|
||||
patches::Timer::Init ();
|
||||
#ifdef ASYNC_IO
|
||||
LogMessage (LogLevel::WARN, "(experimental) Using Async IO!");
|
||||
InitializeKeyboard ();
|
||||
#endif
|
||||
|
||||
// #ifdef ASYNC_IO
|
||||
// InitializeKeyboard ();
|
||||
// #endif
|
||||
|
||||
LogMessage (LogLevel::INFO, "Loading config...");
|
||||
|
||||
@@ -314,8 +266,9 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) {
|
||||
patches::Audio::Init ();
|
||||
patches::Dxgi::Init ();
|
||||
patches::AmAuth::Init ();
|
||||
patches::LayeredFs::Init ();
|
||||
patches::Language::Init ();
|
||||
patches::TestMode::Init ();
|
||||
patches::LayeredFs::Init ();
|
||||
|
||||
std::chrono::duration<double> duration = std::chrono::high_resolution_clock::now() - start;
|
||||
LogMessage (LogLevel::INFO, "==== Finished Loading patches! using: {:.2f}ms", duration.count () * 1000);
|
||||
|
||||
+32
-2
@@ -104,7 +104,7 @@ readConfigIntArray (const toml_table_t *table, const std::string &key, std::vect
|
||||
}
|
||||
|
||||
std::wstring
|
||||
replace (const std::wstring &orignStr, const std::wstring &oldStr, const std::wstring &newStr) {
|
||||
replace (const std::wstring orignStr, const std::wstring &oldStr, const std::wstring &newStr) {
|
||||
size_t pos = 0;
|
||||
std::wstring tempStr = orignStr;
|
||||
const std::wstring::size_type newStrLen = newStr.length ();
|
||||
@@ -121,7 +121,7 @@ replace (const std::wstring &orignStr, const std::wstring &oldStr, const std::ws
|
||||
}
|
||||
|
||||
std::string
|
||||
replace (const std::string &orignStr, const std::string &oldStr, const std::string &newStr) {
|
||||
replace (const std::string orignStr, const std::string &oldStr, const std::string &newStr) {
|
||||
size_t pos = 0;
|
||||
std::string tempStr = orignStr;
|
||||
const std::string::size_type newStrLen = newStr.length ();
|
||||
@@ -182,4 +182,34 @@ AreAllBytesZero (const u8 *array, const size_t offset, const size_t length) {
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
if (array[offset + i] != 0x00) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
withFile (const char *fileName, std::function<void (u8 *)> callback) {
|
||||
FILE *file = fopen (fileName, "rb");
|
||||
if (file == nullptr) return;
|
||||
|
||||
fseek (file, 0, SEEK_END);
|
||||
long size = ftell (file);
|
||||
fseek (file, 0, SEEK_SET);
|
||||
|
||||
u8 *buffer = (u8 *)malloc(size);
|
||||
fread (buffer, 1, size, file);
|
||||
|
||||
callback (buffer);
|
||||
|
||||
free (buffer);
|
||||
fclose (file);
|
||||
}
|
||||
|
||||
std::string
|
||||
season () {
|
||||
std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
std::tm* tm = std::localtime(&time);
|
||||
int month = tm->tm_mon + 1;
|
||||
return std::to_string (tm->tm_year + 1900) + (
|
||||
(month >= 3 && month <= 5) ? "spr" :
|
||||
(month >= 6 && month <= 8) ? "sum" :
|
||||
(month >= 9 && month <= 11) ? "aut" : "win"
|
||||
);
|
||||
}
|
||||
+8
-10
@@ -11,6 +11,8 @@
|
||||
#include <string>
|
||||
#include <toml.h>
|
||||
#include <windows.h>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include "constants.h"
|
||||
#include "logger.h"
|
||||
|
||||
@@ -123,15 +125,8 @@ const HMODULE MODULE_HANDLE = GetModuleHandle (nullptr);
|
||||
#define INSTALL_FAST_HOOK_DYNAMIC(functionName, location) \
|
||||
{ \
|
||||
LogMessage (LogLevel::DEBUG, std::string ("Installing fast hook dynamic for ") + #functionName); \
|
||||
if (auto hook = safetyhook::InlineHook::create((void *)location, (void*)implOf##functionName)) {\
|
||||
original##functionName = std::move(*hook); \
|
||||
} else { \
|
||||
LogMessage (LogLevel::ERROR, std::string ("Error Installing fast hook dynamic for ") + #functionName);\
|
||||
ExitProcess (1); \
|
||||
} \
|
||||
original##functionName = safetyhook::create_inline ((void *)location, (void*)implOf##functionName); \
|
||||
}
|
||||
// original##functionName = safetyhook::create_inline ((void *)location, (void*)implOf##functionName); \
|
||||
// }
|
||||
|
||||
inline bool sendFlag = false;
|
||||
#define SCENE_RESULT_HOOK(functionName, location) \
|
||||
@@ -215,6 +210,7 @@ inline bool sendFlag = false;
|
||||
}
|
||||
|
||||
#define round(num) ((num > 0) ? (int)(num + 0.5) : (int)(num - 0.5))
|
||||
#define timestamp() (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ())
|
||||
|
||||
toml_table_t *openConfig (const std::filesystem::path &path);
|
||||
toml_table_t *openConfigSection (const toml_table_t *config, const std::string §ionName);
|
||||
@@ -222,9 +218,11 @@ bool readConfigBool (const toml_table_t *table, const std::string &key, bool not
|
||||
i64 readConfigInt (const toml_table_t *table, const std::string &key, i64 notFoundValue);
|
||||
std::string readConfigString (const toml_table_t *table, const std::string &key, const std::string ¬FoundValue);
|
||||
std::vector<i64> readConfigIntArray (const toml_table_t *table, const std::string &key, std::vector<i64> notFoundValue);
|
||||
std::wstring replace (const std::wstring &orignStr, const std::wstring &oldStr, const std::wstring &newStr);
|
||||
std::string replace (const std::string &orignStr, const std::string &oldStr, const std::string &newStr);
|
||||
std::wstring replace (const std::wstring orignStr, const std::wstring &oldStr, const std::wstring &newStr);
|
||||
std::string replace (const std::string orignStr, const std::string &oldStr, const std::string &newStr);
|
||||
const char *GameVersionToString (GameVersion version);
|
||||
const char *languageStr (int language);
|
||||
std::string ConvertWideToUtf8 (const std::wstring &wstr);
|
||||
bool AreAllBytesZero (const u8 *array, size_t offset, size_t length);
|
||||
void withFile (const char *fileName, std::function<void (u8 *)> callback);
|
||||
std::string season ();
|
||||
|
||||
@@ -98,6 +98,11 @@ LogMessageHandler (const char *function, const char *codeFile, const int codeLin
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
bool
|
||||
EnableLogging (LogLevel messageLevel) {
|
||||
return loggerInstance != nullptr && messageLevel <= loggerInstance->logLevel;
|
||||
}
|
||||
|
||||
void
|
||||
CleanupLogger () {
|
||||
if (loggerInstance != nullptr) {
|
||||
|
||||
@@ -35,6 +35,8 @@ void InitializeLogger (LogLevel level, bool logToFile);
|
||||
void LogMessageHandler (const char *function, const char *codeFile, int codeLine, LogLevel messageLevel, const char *format, ...);
|
||||
void LogMessageHandler (const char *function, const char *codeFile, int codeLine, LogLevel messageLevel, const wchar_t *format, ...);
|
||||
|
||||
bool EnableLogging (LogLevel level);
|
||||
|
||||
/* *
|
||||
* Logs a message with file and line information, if the log level permits.
|
||||
*
|
||||
@@ -44,12 +46,14 @@ template <typename... Args>
|
||||
struct LogMessage {
|
||||
LogMessage (const LogLevel level, const std::string_view format, Args &&...args,
|
||||
const std::source_location &loc = std::source_location::current ()) {
|
||||
if (!EnableLogging (level)) return;
|
||||
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 &&...args,
|
||||
const std::source_location &loc = std::source_location::current ()) {
|
||||
if (!EnableLogging (level)) return;
|
||||
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 ());
|
||||
}
|
||||
|
||||
@@ -41,9 +41,10 @@ FAST_HOOK_DYNAMIC (bool, LoadASIODriver, void *a1, const char *a2) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
TestMode::Value *speakerLevel = TestMode::CreateValue(L"OutputLevelSpeakerItem");
|
||||
FAST_HOOK_DYNAMIC (u64, NuscBusVolume, u64 a1, u64 a2, float a3) {
|
||||
if (volumeRate == 0.0f) {
|
||||
int value = patches::TestMode::ReadTestModeValue (L"OutputLevelSpeakerItem");
|
||||
int value = speakerLevel->Read ();
|
||||
if (value == -1) return originalNuscBusVolume.fastcall<u64> (a1, a2, a3);
|
||||
volumeRate = value <= 100 ? 1.0f : value / 100.0f;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,658 @@
|
||||
#include "constants.h"
|
||||
#include "helpers.h"
|
||||
#include "patches.h"
|
||||
#include <emmintrin.h>
|
||||
|
||||
extern GameVersion gameVersion;
|
||||
|
||||
namespace patches::Language {
|
||||
const std::vector<std::string> languages = { "jpn", "en_us", "cn_tw", "kor", "cn_cn" };
|
||||
std::vector<SafetyHookMid> language_patch = { };
|
||||
boolean cnFontPatches = false;
|
||||
int language = 0;
|
||||
|
||||
FUNCTION_PTR (void, lua_settop, PROC_ADDRESS ("lua51.dll", "lua_settop"), i64, i32);
|
||||
FUNCTION_PTR (void, lua_replace, PROC_ADDRESS ("lua51.dll", "lua_replace"), i64, i32);
|
||||
FUNCTION_PTR (const char *, lua_pushstring, PROC_ADDRESS ("lua51.dll", "lua_pushstring"), i64, const char *);
|
||||
FUNCTION_PTR (const char *, lua_tolstring, PROC_ADDRESS ("lua51.dll", "lua_tolstring"), u64, i32, size_t *);
|
||||
FUNCTION_PTR (void, lua_pushnumber, PROC_ADDRESS ("lua51.dll", "lua_pushnumber"), i64, double);
|
||||
|
||||
typedef void *(*Taiko_string) (void *address, const char *src);
|
||||
typedef void *(*Taiko_assign) (void *address, const char *src, size_t size);
|
||||
typedef int (*LanguageFix) (int language);
|
||||
|
||||
Taiko_string taiko_string = nullptr;
|
||||
Taiko_assign taiko_assign = nullptr;
|
||||
Taiko_assign std_assign = nullptr;
|
||||
Taiko_assign std_append = nullptr;
|
||||
LanguageFix languageFix = [](int lang){ return lang; };
|
||||
|
||||
namespace Font {
|
||||
typedef u64 *(*Taiko_mapping) (u64 *a1, u8 *a2);
|
||||
Taiko_mapping taiko_mapping = nullptr;
|
||||
|
||||
typedef struct {
|
||||
const char *font_name;
|
||||
const char *font_shape;
|
||||
const char *font_file;
|
||||
} LanguageItem;
|
||||
|
||||
void *languageData = nullptr;
|
||||
const size_t languageOffset = languages.size () * 560;
|
||||
// jp_64, jp_30, jp_32, en_64, en_30, en_32, tw_64, tw_30, tw_32, ko_64, ko_30, ko_32, cn_64, cn_30, cn_32
|
||||
const float upA[15] = { -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
const float upB[15] = { -0.265625f, 0.15f, 0.078125f, -0.21875f, 0.2329999f, 0.140625f, -0.0625f, 0.333f, 0.28125f, -0.109375f, 0.333f, 0.28125f, -0.09375f, 0.30000001f, 0.25f };
|
||||
const float downA[15] = { -0.25f, 0.167f, 0.125f, -0.21875f, 0.233f, 0.140625f, -0.0625f, 0.333f, 0.25f, -0.125f, 0.333f, 0.28125f, -0.09375f, 0.30000001f, 0.25f };
|
||||
const float downB[15] = { 50.5f, 10.0f, 13.0f, 46.0f, 8.0f, 11.5f, 36.0f, 5.0f, 7.0f, 39.0f, 5.0f, 7.0f, 38.0f, 6.0f, 8.0f };
|
||||
// xx_64, 00_30, xx_32
|
||||
const float downC[3] = { 64.0f, 30.0f, 32.0f };
|
||||
const float shapes[3] = { 20.0f, 12.0f, 16.0f };
|
||||
const LanguageItem languageItems[5][5] = {
|
||||
{{"jp", "64", "jp_64"},{"jp", "30", "jp_30"},{"jp", "32_EB", "jp_32_EB"},{"jp", "32_B", "jp_32_B"},{"jp", "32_DB", "jp_32_DB"}},
|
||||
{{"en", "64", "en_64"},{"en", "30", "en_30"},{"en", "32_EB", "en_32" },{"en", "32_B", "en_32" },{"en", "32_DB", "en_32" }},
|
||||
{{"tw", "64", "tw_64"},{"tw", "30", "tw_30"},{"tw", "32_EB", "tw_32" },{"tw", "32_B", "tw_32" },{"tw", "32_DB", "tw_32" }},
|
||||
{{"kr", "64", "kr_64"},{"kr", "30", "kr_30"},{"kr", "32_EB", "kr_32" },{"kr", "32_B", "kr_32" },{"kr", "32_DB", "kr_32" }},
|
||||
{{"cn", "64", "cn_64"},{"cn", "30", "cn_30"},{"cn", "32_EB", "cn_32_EB"},{"cn", "32_B", "cn_32_B"},{"cn", "32_DB", "cn_32_DB"}},
|
||||
};
|
||||
|
||||
FAST_HOOK_DYNAMIC (int, initial_load_setting) {
|
||||
for (int repeat = 0; repeat < languages.size (); repeat ++) {
|
||||
u64 offsetR = repeat * languages.size () * 0x5 * 0x70;
|
||||
for (int lang = 0; lang < languages.size (); lang ++) {
|
||||
u64 offsetI = lang * 0x5 * 0x70;
|
||||
const LanguageItem *group = languageItems[lang];
|
||||
for (int shape = 0; shape < 0x5; shape ++) {
|
||||
LanguageItem item = group[shape];
|
||||
u64 basicOffset = (u64)languageData + offsetR + offsetI + shape * 0x70;
|
||||
u32 shapeIndex = shape > 2 ? 2 : shape;
|
||||
|
||||
std::string *str = new std::string (item.font_name);
|
||||
memcpy ((void *)(basicOffset + 0x00), str, 0x20);
|
||||
str = new std::string (item.font_shape);
|
||||
memcpy ((void *)(basicOffset + 0x20), str, 0x20);
|
||||
str = new std::string (item.font_file);
|
||||
memcpy ((void *)(basicOffset + 0x40), str, 0x20);
|
||||
*(float *)(basicOffset + 0x60) = shapes[shapeIndex];
|
||||
*(u32 *)(basicOffset + 0x64) = lang == 0 ? 0 : shapeIndex + 1;
|
||||
*(u32 *)(basicOffset + 0x68) = lang == 0 ? 0 : (3 * lang + shapeIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FAST_HOOK_DYNAMIC (double, GetCorrectionOffsetY, u64 a1, u32 a2) {
|
||||
if (!a2) return 0.0;
|
||||
double result;
|
||||
u8 type = *(u8 *)(a1 + 572);
|
||||
float varA = *(float *)(a1 + 532);
|
||||
if (type) {
|
||||
*(float *)&result = upA[a2 - 1] + upB[a2 - 1] * varA;
|
||||
} else {
|
||||
float varB = *(float *)(a1 + 564);
|
||||
float varC = downC[(a2 - 1) % 3];
|
||||
float val = (varC - varA) * downA[a2 - 1] + (varB - varC) * 0.5f + downB[a2 - 1] - varB * 0.5f;
|
||||
*(u64 *)&result = *(u64 *)&val ^ 0x8000000080000000ui64;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
bool fontExistAll = true;
|
||||
const char *fontToCheck[]{
|
||||
"cn_30.nutexb", "cn_30.xml", "cn_32_EB.nutexb", "cn_32_EB.xml", "cn_32_B.nutexb",
|
||||
"cn_32_B.xml", "cn_32_DB.nutexb", "cn_32_DB.xml", "cn_64.nutexb", "cn_64.xml"
|
||||
};
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\font\\") + fontToCheck[i])) continue;
|
||||
fontExistAll = false;
|
||||
}
|
||||
if (fontExistAll == false) return;
|
||||
LogMessage (LogLevel::INFO, "Detected cn_xx files, Install Font patches");
|
||||
languageData = calloc (languages.size () * languages.size () * 0x70 * 0x5, sizeof (u8));
|
||||
|
||||
switch (gameVersion) {
|
||||
default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break;
|
||||
case GameVersion::JPN39: {
|
||||
taiko_mapping = (Taiko_mapping)ASLR (0x140369B60);
|
||||
TestMode::RegisterModify (
|
||||
L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']",
|
||||
[](pugi::xml_node &node) {
|
||||
node.attribute (L"max").set_value (L"4");
|
||||
node.attribute (L"replace-text").set_value (L"0:JPN, 1:ENG, 2:zh-tw, 3:KOR, 4:zh-cn");
|
||||
}, [&](){
|
||||
// load_setting
|
||||
INSTALL_FAST_HOOK_DYNAMIC (initial_load_setting, ASLR (0x14001B010));
|
||||
// SceneBoot::LoadFont
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x140464235), [](SafetyHookContext &ctx){
|
||||
ctx.rbp = ctx.rbx * languageOffset;
|
||||
ctx.rax = (uintptr_t)languageData;
|
||||
ctx.rip = ASLR (0x140464243);
|
||||
}));
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x14046426C), [](SafetyHookContext &ctx){
|
||||
ctx.r15 = ctx.rbp + languageOffset;
|
||||
ctx.rip = ASLR (0x140464273);
|
||||
}));
|
||||
// SceneBoot::SetFontShift
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x1404644AF), [](SafetyHookContext &ctx){
|
||||
ctx.rdi = ctx.rcx * languageOffset;
|
||||
ctx.rax = (uintptr_t)languageData;
|
||||
ctx.rdi += ctx.rax;
|
||||
ctx.rsi = ctx.rdi + languageOffset;
|
||||
ctx.rip = ASLR (0x1404644C7);
|
||||
}));
|
||||
// AppLumenRenderer
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x140368961), [](SafetyHookContext &ctx){
|
||||
*(u64 *)(ctx.rsp + 0x20) = 4;
|
||||
void *buf = taiko_mapping ((u64 *)(ctx.r14), (u8 *)(ctx.rsp + 0x20));
|
||||
taiko_assign (buf, "cn", 2);
|
||||
}));
|
||||
// CorrectionOffsetY
|
||||
INSTALL_FAST_HOOK_DYNAMIC (GetCorrectionOffsetY, ASLR (0x14049FAD0));
|
||||
// user_name_entry
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x14016128D), [](SafetyHookContext &ctx)
|
||||
{ *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rcx % languages.size ()); ctx.rip = ASLR (0x1401612C7); }));
|
||||
// user_name_p
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x140160F63), [](SafetyHookContext &ctx)
|
||||
{ *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rdi % languages.size ()); ctx.rip = ASLR (0x140160F9D); }));
|
||||
// user_name_ai
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x14016146D), [](SafetyHookContext &ctx)
|
||||
{ *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rcx % languages.size ()); ctx.rip = ASLR (0x1401614A7); }));
|
||||
// user_name_soshina
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x14016164D), [](SafetyHookContext &ctx)
|
||||
{ *(int *)(ctx.rsp + 0xD0) = (int)(ctx.rcx % languages.size ()); ctx.rip = ASLR (0x140161687); }));
|
||||
cnFontPatches = true;
|
||||
}
|
||||
);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Datatable {
|
||||
const std::vector<std::string> languageTexts = {"japaneseText", "englishUsText", "chineseTText", "koreanText", "chineseSText"};
|
||||
const std::vector<std::string> languageFontTypes = {"japaneseFontType", "englishUsFontType", "chineseTFontType", "koreanFontType", "chineseSFontType"};
|
||||
const std::vector<std::string> languageFlas = {"taikoSelectFla", "taikoSelectFlaENUS", "taikoSelectFlaCNTW", "taikoSelectFlaKOR", "taikoSelectFlaZHCN"};
|
||||
|
||||
typedef bool (*JsonHasMember) (u64, const char *);
|
||||
typedef u64 *(*JsonGetMember) (u64, const char *);
|
||||
JsonHasMember jsonHasMember = nullptr;
|
||||
JsonGetMember jsonGetMember = nullptr;
|
||||
|
||||
uintptr_t WordlistLanguageTypeRIP = 0;
|
||||
MID_HOOK_DYNAMIC (WordlistLanguageType, SafetyHookContext &ctx) {
|
||||
int language = (int)ctx.r12, offset = (int)ctx.rdi;
|
||||
u64 wordlist = ctx.rbx;
|
||||
|
||||
int fallback = -1;
|
||||
language = language > 0 && language < languages.size () ? language : 0;
|
||||
u64 json = 0xFFFFFFFFFFFFi64 & wordlist + offset;
|
||||
if (jsonHasMember (json, languageTexts[language].c_str ())) {
|
||||
u64 *data = jsonGetMember (json, languageTexts[language].c_str ());
|
||||
if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF);
|
||||
taiko_assign ((void *)(ctx.rsp + 0x60), (char *)data, strlen ((char *)data));
|
||||
} else if (language == 4 && jsonHasMember (json, languageTexts[2].c_str ())) {
|
||||
fallback = 2;
|
||||
u64 *data = jsonGetMember (json, languageTexts[2].c_str ());
|
||||
if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF);
|
||||
taiko_assign ((void *)(ctx.rsp + 0x60), (char *)data, strlen ((char *)data));
|
||||
} else if (language != 0 && jsonHasMember (json, languageTexts[0].c_str ())) {
|
||||
fallback = 0;
|
||||
u64 *data = jsonGetMember (json, languageTexts[0].c_str ());
|
||||
if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF);
|
||||
taiko_assign ((void *)(ctx.rsp + 0x60), (char *)data, strlen ((char *)data));
|
||||
} else taiko_assign ((void *)(ctx.rsp + 0x60), "", 0);
|
||||
|
||||
if (fallback != -1) {
|
||||
if (jsonHasMember (json, languageFontTypes[fallback].c_str ())) {
|
||||
ctx.rcx = *jsonGetMember (json, languageFontTypes[fallback].c_str ());
|
||||
} else ctx.rcx = 0;
|
||||
} else if (jsonHasMember (json, languageFontTypes[language].c_str ())) {
|
||||
ctx.rcx = *jsonGetMember (json, languageFontTypes[language].c_str ());
|
||||
} else ctx.rcx = 0;
|
||||
|
||||
ctx.rip = WordlistLanguageTypeRIP;
|
||||
}
|
||||
|
||||
char *languageFla = nullptr;
|
||||
uintptr_t QrcodeInfoResourceTypeRIP = 0;
|
||||
MID_HOOK_DYNAMIC (QrcodeInfoResourceType, SafetyHookContext &ctx) {
|
||||
languageFla = (char *)(ctx.rsp + 0x130);
|
||||
u64 json = ctx.rbx;
|
||||
for (int i = 0; i < 4; i ++) {
|
||||
int lang = language == 4 ? (i == 2 ? 4 : i) : i;
|
||||
if (jsonHasMember (json, languageFlas[lang].c_str ())) {
|
||||
u64 *data = jsonGetMember (json, languageFlas[lang].c_str ());
|
||||
if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF);
|
||||
std_assign (languageFla + i * 0x20, (char *)data, strlen ((char *)data));
|
||||
} else if (language == 4 && jsonHasMember (json, languageFlas[2].c_str ())) {
|
||||
u64 *data = jsonGetMember (json, languageFlas[2].c_str ());
|
||||
if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF);
|
||||
std_assign (languageFla + i * 0x20, (char *)data, strlen ((char *)data));
|
||||
} else if (language != 0 && jsonHasMember (json, languageFlas[0].c_str ())) {
|
||||
u64 *data = jsonGetMember (json, languageFlas[0].c_str ());
|
||||
if ((*((u16 *)data + 7) & 0x1000) == 0) data = (u64 *)(data[1] & 0xFFFFFFFFFFFF);
|
||||
std_assign (languageFla + i * 0x20, (char *)data, strlen ((char *)data));
|
||||
} else std_assign (languageFla + i * 0x20, "", 0);
|
||||
}
|
||||
ctx.rip = QrcodeInfoResourceTypeRIP;
|
||||
}
|
||||
|
||||
std::string CHSGaidenTitle = "";
|
||||
MID_HOOK_DYNAMIC (GaidenTitleExtren, SafetyHookContext &ctx) {
|
||||
LogMessage (LogLevel::DEBUG, "GaidenTitle: {}", (char *)ctx.rcx);
|
||||
std::string gaidenTitle = std::string ((char *)ctx.rcx);
|
||||
if (gaidenTitle.find ("[CHS]") != std::string::npos) {
|
||||
CHSGaidenTitle = gaidenTitle;
|
||||
}
|
||||
}
|
||||
uintptr_t JumpInjectGaidenTitleRIP = 0;
|
||||
MID_HOOK_DYNAMIC (GaidenTitleInject, SafetyHookContext &ctx) {
|
||||
// only handle ctx.rdx > 2 2 => lang4 => zh-cn etc.
|
||||
if (ctx.rdx == 2) {
|
||||
ctx.rdx = (uintptr_t)CHSGaidenTitle.c_str ();
|
||||
ctx.r8 = CHSGaidenTitle.size ();
|
||||
LogMessage (LogLevel::DEBUG, "GaidenTitleInjectCHS title={} length={}", CHSGaidenTitle, CHSGaidenTitle.size ());
|
||||
ctx.rip = JumpInjectGaidenTitleRIP;
|
||||
}
|
||||
}
|
||||
MID_HOOK_DYNAMIC (GaidenTitleClean, SafetyHookContext &ctx) {
|
||||
CHSGaidenTitle = "";
|
||||
}
|
||||
uintptr_t JumpInjectGaidenLanguageRIP = 0;
|
||||
MID_HOOK_DYNAMIC (GaidenLanguageInject, SafetyHookContext &ctx) {
|
||||
if (ctx.rbx == 2) {
|
||||
*(int *)(ctx.rsp + 0x2D8) = 4;
|
||||
ctx.rip = JumpInjectGaidenLanguageRIP;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
switch (gameVersion) {
|
||||
default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break;
|
||||
case GameVersion::JPN39: {
|
||||
jsonHasMember = (JsonHasMember)ASLR (0x1400B5A30);
|
||||
jsonGetMember = (JsonGetMember)ASLR (0x1400B6080);
|
||||
// App::DataTableHolder<App::WordInfo>::Serialize
|
||||
WordlistLanguageTypeRIP = ASLR (0x1400B20B8);
|
||||
INSTALL_MID_HOOK_DYNAMIC (WordlistLanguageType, ASLR (0x1400B1E47));
|
||||
// App::DataTableHolder<App::QRCodeInfo>::Serialize
|
||||
QrcodeInfoResourceTypeRIP = ASLR (0x1400B2959);
|
||||
INSTALL_MID_HOOK_DYNAMIC (QrcodeInfoResourceType, ASLR (0x1400B276E));
|
||||
INSTALL_MID_HOOK_DYNAMIC (GaidenTitleExtren, ASLR (0x1403E2224));
|
||||
JumpInjectGaidenTitleRIP = ASLR (0x1403E2416);
|
||||
INSTALL_MID_HOOK_DYNAMIC (GaidenTitleInject, ASLR (0x1403E238F));
|
||||
INSTALL_MID_HOOK_DYNAMIC (GaidenTitleClean, ASLR (0x1403E2980));
|
||||
JumpInjectGaidenLanguageRIP = ASLR (0x1403E2721);
|
||||
INSTALL_MID_HOOK_DYNAMIC (GaidenLanguageInject, ASLR (0x1403E26E2));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Script {
|
||||
FAST_HOOK_DYNAMIC (i64, RefPlayDataManager, i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "RefPlayDataManager was called");
|
||||
const auto result = originalRefPlayDataManager.fastcall<i64> (a1);
|
||||
language = *reinterpret_cast<u32 *> (result);
|
||||
return result;
|
||||
}
|
||||
FAST_HOOK_DYNAMIC (i64, GetLanguage, u64 *a1, i64 a2) {
|
||||
LogMessage (LogLevel::HOOKS, "GetLanguage was called");
|
||||
lua_settop (a2, 0);
|
||||
int langFix = languageFix (language);
|
||||
const char* type = languageStr (langFix);
|
||||
lua_pushstring (a2, type);
|
||||
return 1;
|
||||
}
|
||||
FAST_HOOK_DYNAMIC (i64, GetRegionLanguage, i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "GetRegionLanguage was called");
|
||||
lua_settop (a1, 0);
|
||||
int langFix = languageFix (language);
|
||||
lua_pushstring (a1, languageStr (langFix));
|
||||
return 1;
|
||||
}
|
||||
std::thread::id *LoadMovieThreadId = nullptr;
|
||||
FAST_HOOK_DYNAMIC (i64, GetCabinetLanguage, u64 *a1, i64 a2) {
|
||||
LogMessage (LogLevel::HOOKS, "GetCabinetLanguage was called");
|
||||
i64 result = originalGetCabinetLanguage.fastcall<i64> (a1, a2);
|
||||
if (language == 4 && (LoadMovieThreadId == nullptr || *LoadMovieThreadId != std::this_thread::get_id())) {
|
||||
std_assign ((void *)result, languageStr (2), 5);
|
||||
} else std_assign ((void *)result, languageStr (language), (language == 0 || language == 3) ? 3 : 5);
|
||||
return result;
|
||||
}
|
||||
FAST_HOOK_DYNAMIC (i64, GetLanguageType, i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "GetLanguageType was called");
|
||||
lua_settop (a1, 0);
|
||||
lua_pushnumber (a1, languageFix (language));
|
||||
return 1;
|
||||
}
|
||||
void
|
||||
Init () {
|
||||
switch (gameVersion) {
|
||||
default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break;
|
||||
case GameVersion::JPN39: {
|
||||
INSTALL_FAST_HOOK_DYNAMIC (RefPlayDataManager, ASLR (0x140024AC0));
|
||||
// Export methods for lua invoke
|
||||
INSTALL_FAST_HOOK_DYNAMIC (GetLanguage, ASLR (0x1401D1930));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (GetRegionLanguage, ASLR (0x1401CE9B0));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (GetCabinetLanguage, ASLR (0x14014DB80));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (GetLanguageType, ASLR (0x1401CECE0));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Lumen {
|
||||
FAST_HOOK_DYNAMIC (i64, LoadDemoMovie, i64 a1, i64 a2, i64 a3) {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
Script::LoadMovieThreadId = &this_id;
|
||||
i64 result = originalLoadDemoMovie.fastcall<i64> (a1, a2, a3);
|
||||
Script::LoadMovieThreadId = nullptr;
|
||||
return result;
|
||||
}
|
||||
std::string onpCn = "textures/onpu_cn/onp_all.nutexb";
|
||||
MID_HOOK_DYNAMIC (ChangeOnpFile, SafetyHookContext &ctx) {
|
||||
if (language != 4) return;
|
||||
ctx.rdx = (uintptr_t) onpCn.c_str ();
|
||||
ctx.r8 = 0x1F;
|
||||
}
|
||||
void *
|
||||
GetLanguageType (Taiko_assign assign, int language, void *buf) {
|
||||
int langFix = languageFix (language);
|
||||
return assign (buf, languageStr (langFix), (langFix == 0 || langFix == 3) ? 3 : 5);
|
||||
}
|
||||
void
|
||||
InjectLanguageType (uintptr_t language, uintptr_t ®) {
|
||||
int langFix = languageFix ((int)language);
|
||||
reg = (uintptr_t)languageStr (langFix);
|
||||
}
|
||||
|
||||
FAST_HOOK_DYNAMIC (u64, GraphicBaseGetLanguageType, u64 a1, void *a2) {
|
||||
int language = *(int *)(a1 + 56);
|
||||
return (u64)GetLanguageType (taiko_assign, language, a2);
|
||||
}
|
||||
|
||||
std::string *season_nulm = nullptr;
|
||||
std::string *season_nutexb = nullptr;
|
||||
void
|
||||
Init () {
|
||||
switch (gameVersion) {
|
||||
default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break;
|
||||
case GameVersion::JPN39: {
|
||||
bool demoMovieExistAll = true;
|
||||
const char *movieToCheck[]{"movie\\attractdemo_cn_cn.wmv", "sound\\attractdemo_cn_cn.nus3bank"};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\") + movieToCheck[i])) continue;
|
||||
demoMovieExistAll = false;
|
||||
}
|
||||
if (demoMovieExistAll) {
|
||||
LogMessage (LogLevel::INFO, "Detected attractdemo_cn files, install attractdemo patches!");
|
||||
INSTALL_FAST_HOOK_DYNAMIC (LoadDemoMovie, ASLR (0x1404313F0));
|
||||
}
|
||||
|
||||
bool onpCnExist = false;
|
||||
if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\textures\\onpu_cn\\onp_all.nutexb"))) onpCnExist = true;
|
||||
if (onpCnExist) {
|
||||
LogMessage (LogLevel::INFO, "Detected onpu_cn files, install onp patches!");
|
||||
INSTALL_MID_HOOK_DYNAMIC (ChangeOnpFile, ASLR (0x140134D16));
|
||||
}
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x140134E22), [](SafetyHookContext &ctx){
|
||||
if (ctx.rcx == 4) *(int *)(ctx.r15 + 0x3100) = 2;
|
||||
else *(int *)(ctx.r15 + 0x3100) = (int)ctx.rcx;
|
||||
ctx.rip = ASLR (0x140134E29);
|
||||
}));
|
||||
|
||||
INSTALL_FAST_HOOK_DYNAMIC (GraphicBaseGetLanguageType, ASLR (0x1400E8740));
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x1400E8791), [](SafetyHookContext &ctx)
|
||||
{ InjectLanguageType (ctx.rsp + 0x58 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x1400E8796); })); // HitEffectGetLanguageType
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x14011F8E4), [](SafetyHookContext &ctx)
|
||||
{ InjectLanguageType (ctx.rbp + 0x57 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x14011F8E9); })); // KusudamaGetLanguageType
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x140122404), [](SafetyHookContext &ctx)
|
||||
{ InjectLanguageType (ctx.rbp + 0x140 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x14012240C); })); // NoteJumpGetLanguageType
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x14012425E), [](SafetyHookContext &ctx)
|
||||
{ InjectLanguageType (ctx.rbp + 0x57 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x140124263); })); // RendaEffectGetLanguageType
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x140129450), [](SafetyHookContext &ctx)
|
||||
{ InjectLanguageType (ctx.rbp + 0x57 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x140129455); })); // RendaEffectGetLanguageType
|
||||
language_patch.push_back (safetyhook::create_mid (ASLR (0x14012B8C8), [](SafetyHookContext &ctx)
|
||||
{ InjectLanguageType (ctx.rbp + 0x180 + ctx.rax * 8, ctx.rdx); ctx.rip = ASLR (0x14012B8D0); })); // ScoreGetLanguageType
|
||||
LayeredFs::RegisterBefore ([&] (const std::string &originalFileName, const std::string ¤tFileName) -> std::string {
|
||||
if (language != 4 || currentFileName.starts_with ("F:\\") || currentFileName.find ("\\lumen\\") == std::string::npos) return "";
|
||||
std::string fileName = std::string (currentFileName);
|
||||
fileName = replace (fileName, "\\lumen\\", "\\lumen_cn\\");
|
||||
if (std::filesystem::exists (fileName)) return fileName;
|
||||
return "";
|
||||
});
|
||||
|
||||
LayeredFs::RegisterBefore ([&] (const std::string &originalFileName, const std::string ¤tFileName) -> std::string {
|
||||
if (language != 4 || currentFileName.starts_with ("F:\\")) return "";
|
||||
if (currentFileName.ends_with ("title.nulm")) {
|
||||
std::string fileName = std::string (currentFileName);
|
||||
if (season_nulm == nullptr) {
|
||||
fileName = replace (fileName, "title.nulm", "title_season.nulm");
|
||||
if (std::filesystem::exists (fileName)) {
|
||||
std::string nutexb = std::string (currentFileName);
|
||||
nutexb = replace (nutexb, "title.nulm", "title_" + season () + ".nutexb");
|
||||
if (std::filesystem::exists (nutexb)) {
|
||||
season_nulm = new std::string (fileName), season_nutexb = new std::string (nutexb);
|
||||
return *season_nulm;
|
||||
}
|
||||
}
|
||||
season_nulm = new std::string (""), season_nutexb = new std::string ("");
|
||||
return "";
|
||||
} else return *season_nulm;
|
||||
} else if (currentFileName.ends_with ("title.nutexb") && season_nutexb != nullptr) {
|
||||
return *season_nutexb;
|
||||
} else return "";
|
||||
});
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Voice {
|
||||
std::map<std::string, bool> voiceCnExist;
|
||||
std::map<std::string, int> nus3bankMap;
|
||||
bool enableSwitchVoice = false;
|
||||
int nus3bankIdCounter = 0;
|
||||
std::mutex nus3bankMtx;
|
||||
|
||||
int
|
||||
get_bank_id (const std::string &bankName) {
|
||||
if (!nus3bankMap.contains (bankName)) {
|
||||
nus3bankMap[bankName] = nus3bankIdCounter;
|
||||
nus3bankIdCounter++;
|
||||
}
|
||||
LogMessage (LogLevel::DEBUG, "LoadBank {} id={}", bankName, nus3bankMap[bankName]);
|
||||
return nus3bankMap[bankName];
|
||||
}
|
||||
|
||||
void
|
||||
check_voice_tail (const std::string &bankName, u8 *pBinfBlock, std::map<std::string, bool> &voiceExist, const std::string &tail) {
|
||||
// check if any voice_xxx.nus3bank has xxx_cn audio inside while loading
|
||||
if (bankName.starts_with ("voice_")) {
|
||||
const int binfLength = *reinterpret_cast<int *> (pBinfBlock + 4);
|
||||
u8 *pGrpBlock = pBinfBlock + 8 + binfLength;
|
||||
const int grpLength = *reinterpret_cast<int *> (pGrpBlock + 4);
|
||||
u8 *pDtonBlock = pGrpBlock + 8 + grpLength;
|
||||
const int dtonLength = *reinterpret_cast<int *> (pDtonBlock + 4);
|
||||
u8 *pToneBlock = pDtonBlock + 8 + dtonLength;
|
||||
const int toneSize = *reinterpret_cast<int *> (pToneBlock + 8);
|
||||
u8 *pToneBase = pToneBlock + 12;
|
||||
for (int i = 0; i < toneSize; i++) {
|
||||
if (*reinterpret_cast<int *> (pToneBase + i * 8 + 4) <= 0x0C) continue; // skip empty space
|
||||
u8 *currToneBase = pToneBase + *reinterpret_cast<int *> (pToneBase + i * 8);
|
||||
int titleOffset = -1;
|
||||
switch (*currToneBase) {
|
||||
case 0xFF: titleOffset = 9; break; // audio mark
|
||||
case 0x7F: titleOffset = 5; break; // randomizer mark
|
||||
default: continue; // unknown mark skip
|
||||
}
|
||||
if (titleOffset > 0) {
|
||||
std::string title (reinterpret_cast<char *> (currToneBase + titleOffset));
|
||||
if (title.ends_with (tail)) {
|
||||
if (!voiceExist.contains (bankName) || !voiceExist[bankName]) {
|
||||
voiceExist[bankName] = true;
|
||||
enableSwitchVoice = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!voiceExist.contains (bankName) || voiceExist[bankName]) voiceExist[bankName] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
checkVoiceFile (const char *fileName) {
|
||||
withFile (fileName, [](u8 *bankData){
|
||||
if (bankData[0] == 'N' && bankData[1] == 'U' && bankData[2] == 'S' && bankData[3] == '3') {
|
||||
const int tocLength = *reinterpret_cast<int *> (bankData + 16);
|
||||
u8 *pPropBlock = bankData + 20 + tocLength;
|
||||
const int propLength = *reinterpret_cast<int *> (pPropBlock + 4);
|
||||
u8 *pBinfBlock = pPropBlock + 8 + propLength;
|
||||
const std::string bankName (reinterpret_cast<char *> (pBinfBlock + 0x11));
|
||||
check_voice_tail (bankName, pBinfBlock, voiceCnExist, "_cn");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MID_HOOK_DYNAMIC (GenNus3bankId, SafetyHookContext &ctx) {
|
||||
LogMessage (LogLevel::HOOKS, "GenNus3bankId was called");
|
||||
std::lock_guard lock (nus3bankMtx);
|
||||
if (reinterpret_cast<u8 **> (ctx.rcx + 8) != nullptr) {
|
||||
u8 *pNus3bankFile = *reinterpret_cast<u8 **> (ctx.rcx + 8);
|
||||
if (pNus3bankFile[0] == 'N' && pNus3bankFile[1] == 'U' && pNus3bankFile[2] == 'S' && pNus3bankFile[3] == '3') {
|
||||
const int tocLength = *reinterpret_cast<int *> (pNus3bankFile + 16);
|
||||
u8 *pPropBlock = pNus3bankFile + 20 + tocLength;
|
||||
const int propLength = *reinterpret_cast<int *> (pPropBlock + 4);
|
||||
u8 *pBinfBlock = pPropBlock + 8 + propLength;
|
||||
const std::string bankName (reinterpret_cast<char *> (pBinfBlock + 0x11));
|
||||
ctx.rax = get_bank_id (bankName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
FixToneName (const std::string &bankName, std::string toneName, int voiceLang) {
|
||||
if (voiceLang == 1) {
|
||||
if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) return toneName + "_cn";
|
||||
}
|
||||
return toneName;
|
||||
}
|
||||
|
||||
size_t commonSize = 0;
|
||||
TestMode::Value *voiceLanguage = TestMode::CreateValue (L"VoiceLanguageItem");
|
||||
FAST_HOOK_DYNAMIC (i64, PlaySoundMain, i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundMain was called");
|
||||
int lang = voiceLanguage->Read ();
|
||||
if (lang > 0) {
|
||||
const std::string bankName (lua_tolstring (a1, -3, &commonSize));
|
||||
if (bankName[0] == 'v') {
|
||||
lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ());
|
||||
lua_replace (a1, -3);
|
||||
}
|
||||
}
|
||||
return originalPlaySoundMain.fastcall<i64> (a1);
|
||||
}
|
||||
|
||||
FAST_HOOK_DYNAMIC (i64, PlaySoundMulti, i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundMulti was called");
|
||||
int lang = voiceLanguage->Read ();
|
||||
if (lang > 0) {
|
||||
const std::string bankName (const_cast<char *> (lua_tolstring (a1, -3, &commonSize)));
|
||||
if (bankName[0] == 'v') {
|
||||
lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ());
|
||||
lua_replace (a1, -3);
|
||||
}
|
||||
}
|
||||
return originalPlaySoundMulti.fastcall<i64> (a1);
|
||||
}
|
||||
|
||||
u64 *
|
||||
FixToneNameEnso (u64 *Src, const std::string &bankName, int voiceLang) {
|
||||
if (voiceLang == 1) {
|
||||
if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) Src = (u64 *)std_append (Src, "_cn", 3);
|
||||
}
|
||||
return Src;
|
||||
}
|
||||
|
||||
FAST_HOOK_DYNAMIC (bool, PlaySoundEnso, u64 *a1, u64 *a2, i64 a3) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundEnso was called");
|
||||
int lang = voiceLanguage->Read ();
|
||||
if (lang > 0) {
|
||||
const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast<char **> (a1)) : std::string (reinterpret_cast<char *> (a1));
|
||||
if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang);
|
||||
}
|
||||
return originalPlaySoundEnso.fastcall<bool> (a1, a2, a3);
|
||||
}
|
||||
|
||||
FAST_HOOK_DYNAMIC (bool, PlaySoundSpecial, u64 *a1, u64 *a2) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundSpecial was called");
|
||||
int lang = voiceLanguage->Read ();
|
||||
if (lang > 0) {
|
||||
const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast<char **> (a1)) : std::string (reinterpret_cast<char *> (a1));
|
||||
if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang);
|
||||
}
|
||||
return originalPlaySoundSpecial.fastcall<bool> (a1, a2);
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
switch (gameVersion) {
|
||||
default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break;
|
||||
case GameVersion::JPN39: {
|
||||
INSTALL_MID_HOOK_DYNAMIC (GenNus3bankId, ASLR (0x1407B97BD));
|
||||
std::vector<std::string> voiceFiles = {
|
||||
"voice_aienso_v12g.nus3bank", "voice_airesult_v12g.nus3bank", "voice_aistory_v12g.nus3bank",
|
||||
"voice_aoharu_v1238.nus3bank", "voice_common_v12a.nus3bank", "voice_daniodai_v12e.nus3bank",
|
||||
"voice_daniresult_v12e.nus3bank", "voice_enso_v12a.nus3bank", "voice_entry_v12a.nus3bank",
|
||||
"voice_howtoplay_v12a.nus3bank", "voice_result_v12a.nus3bank", "voice_senkuoku_v12a.nus3bank"
|
||||
};
|
||||
for (std::string voiceFile : voiceFiles) checkVoiceFile (("..\\..\\Data\\x64\\sound\\" + voiceFile).c_str ());
|
||||
if (enableSwitchVoice) {
|
||||
LogMessage (LogLevel::INFO, "Detected voice files, install voice patches!");
|
||||
TestMode::RegisterItemAfter(
|
||||
L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']",
|
||||
L"<select-item label=\"VOICE\" param-offset-x=\"35\" replace-text=\"0:JPN, 1:CHN\" group=\"Setting\" "
|
||||
L"id=\"VoiceLanguageItem\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&](){
|
||||
INSTALL_FAST_HOOK_DYNAMIC (PlaySoundMain, ASLR (0x1404C6DC0));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (PlaySoundMulti, ASLR (0x1404C6D60));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (PlaySoundEnso, ASLR (0x1404ED590));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (PlaySoundSpecial, ASLR (0x1404ED230));
|
||||
}
|
||||
);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean
|
||||
CnFontPatches () {
|
||||
return cnFontPatches;
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
switch (gameVersion) {
|
||||
default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break;
|
||||
case GameVersion::JPN39: {
|
||||
languageFix = [](int lang){ return lang == 4 ? 2 : lang; };
|
||||
taiko_string = (Taiko_string)ASLR (0x1400203B0);
|
||||
taiko_assign = (Taiko_assign)ASLR (0x140020850);
|
||||
std_assign = (Taiko_assign)ASLR (0x1400209E0);
|
||||
std_append = (Taiko_assign)ASLR (0x140028DA0);
|
||||
Language::Font::Init ();
|
||||
Language::Datatable::Init ();
|
||||
Language::Script::Init ();
|
||||
Language::Lumen::Init ();
|
||||
Language::Voice::Init ();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-21
@@ -251,51 +251,40 @@ LayeredFsHandler (const std::string &originalFileName, const std::string ¤
|
||||
return ""; // we return an empty string, causing the rest of CreateFileAHook to not update the returned value
|
||||
}
|
||||
|
||||
HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
FAST_HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
bool changed = false;
|
||||
const auto originalFileName = std::string (lpFileName);
|
||||
std::string currentFileName = originalFileName;
|
||||
LogMessage (LogLevel::HOOKS, ("CreateFileA: " + originalFileName).c_str ());
|
||||
LogMessage (LogLevel::HOOKS, "CreateFileA: {}", originalFileName);
|
||||
|
||||
if (!beforeHandlers.empty ()) {
|
||||
for (const auto handler : beforeHandlers) {
|
||||
std::string result = handler->handlerMethod (originalFileName, currentFileName);
|
||||
if (result != "") {
|
||||
currentFileName = result;
|
||||
changed = true;
|
||||
}
|
||||
if (result != "") { currentFileName = result; changed = true; }
|
||||
}
|
||||
}
|
||||
|
||||
if (useLayeredFs) {
|
||||
const std::string result = LayeredFsHandler (originalFileName, currentFileName);
|
||||
if (result != "") {
|
||||
currentFileName = result;
|
||||
changed = true;
|
||||
}
|
||||
if (result != "") { currentFileName = result; changed = true; }
|
||||
}
|
||||
|
||||
if (!afterHandlers.empty ()) {
|
||||
for (const auto handler : afterHandlers) {
|
||||
std::string result = handler->handlerMethod (originalFileName, currentFileName);
|
||||
if (result != "") {
|
||||
currentFileName = result;
|
||||
changed = true;
|
||||
}
|
||||
if (result != "") { currentFileName = result; changed = true; }
|
||||
}
|
||||
}
|
||||
|
||||
LPCSTR pFinalFileName = changed ? currentFileName.c_str () : lpFileName;
|
||||
if (changed) LogMessage (LogLevel::DEBUG, "Final Redirect: {}", currentFileName);
|
||||
return originalCreateFileAHook (pFinalFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
if (changed) LogMessage (LogLevel::HOOKS, "Final Redirect: {}", currentFileName);
|
||||
return originalCreateFileAHook.stdcall<HANDLE> (pFinalFileName, dwDesiredAccess, dwShareMode,
|
||||
lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
// LogMessage (LogLevel::INFO, "Init LayeredFs patches");
|
||||
|
||||
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) {
|
||||
@@ -306,17 +295,19 @@ Init () {
|
||||
if (useLayeredFs || !beforeHandlers.empty () || !afterHandlers.empty ()) {
|
||||
LogMessage (LogLevel::INFO, "using LayeredFs! Data_mods={} beforHandlers={} afterHandlers={}",
|
||||
useLayeredFs ? "enabled" : "disabled", beforeHandlers.size (), afterHandlers.size ());
|
||||
INSTALL_HOOK (CreateFileAHook);
|
||||
INSTALL_FAST_HOOK (CreateFileAHook);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RegisterBefore (const std::function<std::string (std::string, std::string)> &fileHandler) {
|
||||
LogMessage (LogLevel::DEBUG, "Registered Before");
|
||||
beforeHandlers.push_back (new RegisteredHandler (fileHandler));
|
||||
}
|
||||
|
||||
void
|
||||
RegisterAfter (const std::function<std::string (std::string, std::string)> &fileHandler) {
|
||||
LogMessage (LogLevel::DEBUG, "Registered After");
|
||||
afterHandlers.push_back (new RegisteredHandler (fileHandler));
|
||||
}
|
||||
|
||||
|
||||
+13
-4
@@ -38,10 +38,8 @@ void Init ();
|
||||
} // namespace AmAuth
|
||||
namespace LayeredFs {
|
||||
void Init ();
|
||||
void RegisterBeforeA (const std::function<std::string (const std::string, const std::string)> &fileHandler);
|
||||
void RegisterAfterA (const std::function<std::string (const std::string, const std::string)> &fileHandler);
|
||||
void RegisterBeforeW (const std::function<std::wstring (const std::wstring, const std::wstring)> &fileHandler);
|
||||
void RegisterAfterW (const std::function<std::wstring (const std::wstring, const std::wstring)> &fileHandler);
|
||||
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);
|
||||
} // namespace LayeredFs
|
||||
namespace TestMode {
|
||||
class Applicable {
|
||||
@@ -52,6 +50,12 @@ class Menu : public Applicable {
|
||||
public:
|
||||
virtual void RegisterItem (Applicable *item) = 0;
|
||||
};
|
||||
class Value {
|
||||
public:
|
||||
virtual int Read() = 0;
|
||||
virtual void Write(int value) = 0;
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
typedef u64 (*RefTestModeMain) (u64);
|
||||
void Init ();
|
||||
void Append (const pugi::xml_node &node, const wchar_t *attr, const std::wstring &append);
|
||||
@@ -59,6 +63,7 @@ void SetupAccessor (u64 appAccessor, RefTestModeMain refTestMode);
|
||||
int ReadTestModeValue (const wchar_t *itemId);
|
||||
void SetTestModeValue (const wchar_t *itemId, int value);
|
||||
Menu *CreateMenu (const std::wstring &menuName, const std::wstring &menuId);
|
||||
Value *CreateValue (const std::wstring &key);
|
||||
void RegisterItem (const std::wstring &item, const std::function<void ()> &initMethod, Menu *menu);
|
||||
void RegisterItem (const std::wstring &item, const std::function<void ()> &initMethod);
|
||||
void RegisterItem (const std::wstring &item, Menu *menu);
|
||||
@@ -121,4 +126,8 @@ std::vector<uint8_t> &ReadQRImage (std::vector<uint8_t> &buffer);
|
||||
namespace Timer {
|
||||
void Init ();
|
||||
} // namespace timer
|
||||
namespace Language {
|
||||
boolean CnFontPatches ();
|
||||
void Init ();
|
||||
}
|
||||
} // namespace patches
|
||||
|
||||
+20
-4
@@ -2,6 +2,8 @@
|
||||
#include "helpers.h"
|
||||
#include "patches.h"
|
||||
|
||||
extern int exited;
|
||||
|
||||
namespace patches::Plugins {
|
||||
std::vector<HMODULE> plugins = {};
|
||||
|
||||
@@ -15,6 +17,9 @@ namespace patches::Plugins {
|
||||
typedef void (*SendQRLoginEvent) (CommitQrLoginCallback login);
|
||||
typedef void (*StatusChangeEvent) (size_t type, bool status);
|
||||
|
||||
std::mutex updateMutex;
|
||||
std::condition_variable updateCV;
|
||||
|
||||
void
|
||||
Init () {
|
||||
for (auto plugin : plugins) {
|
||||
@@ -24,13 +29,11 @@ namespace patches::Plugins {
|
||||
}
|
||||
void
|
||||
Update () {
|
||||
for (auto plugin : plugins) {
|
||||
auto event = GetProcAddress (plugin, "Update");
|
||||
if (event) ((BasicEvent)event) ();
|
||||
}
|
||||
updateCV.notify_all ();
|
||||
}
|
||||
void
|
||||
Exit () {
|
||||
updateCV.notify_all ();
|
||||
for (auto plugin : plugins) {
|
||||
auto event = GetProcAddress (plugin, "Exit");
|
||||
if (event) ((BasicEvent)event) ();
|
||||
@@ -129,6 +132,19 @@ namespace patches::Plugins {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::thread ([&](){
|
||||
std::unique_lock<std::mutex> updateLock(updateMutex);
|
||||
while (exited == 0) {
|
||||
updateCV.wait (updateLock);
|
||||
if (exited == 0) {
|
||||
for (auto plugin : plugins) {
|
||||
auto event = GetProcAddress (plugin, "Update");
|
||||
if (event) ((BasicEvent)event) ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}).detach ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+10
-10
@@ -53,7 +53,7 @@ namespace Card {
|
||||
InsertCard (uint8_t cardData[168], bool internalInvoke) {
|
||||
if (callbackTouch) {
|
||||
state = internalInvoke ? State::Disable : State::CopyWait;
|
||||
patches::Plugins::UpdateStatus (1, false);
|
||||
patches::Plugins::UpdateStatus (StatusType::CardStatus, false);
|
||||
if (!internalInvoke) {
|
||||
LogMessage (LogLevel::DEBUG, "[Card] Call CallbackTouch!");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -73,7 +73,7 @@ namespace Card {
|
||||
InsertError (CardErrorCode errorCode) {
|
||||
if (callbackTouch) {
|
||||
state = State::CopyWait;
|
||||
patches::Plugins::UpdateStatus (1, false);
|
||||
patches::Plugins::UpdateStatus (StatusType::CardStatus, false);
|
||||
callbackTouch (0, errorCode, cardData, touchData);
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ namespace Card {
|
||||
AgentCallbackTouch (int32_t a1, int32_t a2, uint8_t *a3, uint64_t a4) {
|
||||
if (callbackTouch) {
|
||||
state = State::CopyWait;
|
||||
patches::Plugins::UpdateStatus (1, false);
|
||||
patches::Plugins::UpdateStatus (StatusType::CardStatus, false);
|
||||
callbackTouch (a1, a2, a3, a4);
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace Card {
|
||||
AgentCallbackTouchOfficial (int32_t a1, int32_t a2, uint8_t *a3, uint64_t a4) {
|
||||
if (callbackTouch) {
|
||||
state = State::CopyWait;
|
||||
patches::Plugins::UpdateStatus (1, false);
|
||||
patches::Plugins::UpdateStatus (StatusType::CardStatus, false);
|
||||
LogMessage (LogLevel::DEBUG, "Official CallbackTouch a1={} a2={}", a1, a2);
|
||||
if (acceptInvalidCards && !a3[0]) {
|
||||
char AccessId[21] = "00000000000000000001";
|
||||
@@ -154,7 +154,7 @@ namespace Card {
|
||||
}
|
||||
FAST_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::UpdateStatus (StatusType::CardStatus, true);
|
||||
patches::Plugins::WaitTouch (Internal::AgentCallbackTouch, a5);
|
||||
callbackTouch = callback;
|
||||
touchData = a5;
|
||||
@@ -164,13 +164,13 @@ namespace Card {
|
||||
HOOK (i64, bngrw_ReqCancelOfficial, PROC_ADDRESS ("bngrw.dll", "BngRwReqCancel"), u32 a1) {
|
||||
if (state != State::Disable) {
|
||||
state = State::Disable;
|
||||
patches::Plugins::UpdateStatus (1, false);
|
||||
patches::Plugins::UpdateStatus (StatusType::CardStatus, 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);
|
||||
patches::Plugins::UpdateStatus (StatusType::CardStatus, true);
|
||||
callbackTouch = callback;
|
||||
touchData = a5;
|
||||
return originalbngrw_ReqWaitTouchOfficial (a1, a2, a3, Internal::AgentCallbackTouchOfficial, a5);
|
||||
@@ -278,7 +278,7 @@ namespace Qr {
|
||||
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 ());
|
||||
size_t finalCopySize = std::min (data->size () + 1, (size_t)length);
|
||||
size_t finalCopySize = std::min (data->size (), (size_t)length);
|
||||
memcpy (dest, data->data (), finalCopySize);
|
||||
scanQueue.pop ();
|
||||
delete data;
|
||||
@@ -291,7 +291,7 @@ namespace Qr {
|
||||
scanQueue.pop ();
|
||||
}
|
||||
state = State::Ready;
|
||||
patches::Plugins::UpdateStatus (2, true);
|
||||
patches::Plugins::UpdateStatus (StatusType::QrStatus, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -339,7 +339,7 @@ namespace Qr {
|
||||
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);
|
||||
patches::Plugins::UpdateStatus (StatusType::QrStatus, false);
|
||||
} else {
|
||||
void *plugin = patches::Plugins::CheckQr ();
|
||||
if (plugin) {
|
||||
|
||||
+100
-101
@@ -16,25 +16,8 @@ std::vector<std::function<void ()>> hooks = {};
|
||||
std::thread *hookInstall;
|
||||
std::mutex hooksMutex;
|
||||
std::condition_variable hooksCV;
|
||||
|
||||
// void
|
||||
// AddHookInLoop (std::function<void ()> installMethod) {
|
||||
// // hooks.push (installMethod);
|
||||
// hooksCV.notify_all ();
|
||||
// }
|
||||
|
||||
// void
|
||||
// HookInstallLoop () {
|
||||
// std::unique_lock<std::mutex> hooksLock(hooksMutex);
|
||||
// while (!hookInstallFinished) {
|
||||
// while (!hooks.empty ()) {
|
||||
// auto installMethod = hooks.front ();
|
||||
// hooks.pop ();
|
||||
// if (installMethod) installMethod ();
|
||||
// }
|
||||
// if (!hookInstallFinished) hooksCV.wait (hooksLock);
|
||||
// }
|
||||
// }
|
||||
std::vector<SafetyHookMid> patches = {};
|
||||
std::vector<Value *> values = {};
|
||||
|
||||
std::wstring
|
||||
mergeCondition (std::wstring original, std::wstring addition, std::wstring value) {
|
||||
@@ -56,18 +39,6 @@ addConditionValue (pugi::xml_node *node, std::wstring key, std::wstring addition
|
||||
attr.set_value (mergeCondition(attr.value (), addition, value).c_str ());
|
||||
} else node->append_attribute (key.c_str ()) = std::format(L"True/{}:{}", addition, value).c_str ();
|
||||
}
|
||||
class RegisteredHook : public Applicable {
|
||||
public:
|
||||
std::function<void ()> registerInit;
|
||||
RegisteredHook (const std::function<void ()> &initMethod) {
|
||||
this->registerInit = initMethod;
|
||||
}
|
||||
pugi::xml_node *
|
||||
Apply (pugi::xml_node *doc, pugi::xml_node *node) override {
|
||||
// AddHookInLoop (this->registerInit);
|
||||
return node;
|
||||
}
|
||||
};
|
||||
class RegisteredItem : public Applicable {
|
||||
public:
|
||||
std::wstring selectItem;
|
||||
@@ -232,31 +203,67 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class TestModeValue : Value {
|
||||
public:
|
||||
std::wstring key;
|
||||
int value;
|
||||
boolean reset;
|
||||
TestModeValue (std::wstring key) {
|
||||
this->key = key;
|
||||
this->value = -1;
|
||||
this->reset = true;
|
||||
}
|
||||
int
|
||||
Read () override {
|
||||
if (reset) {
|
||||
this->value = ReadTestModeValue (key.c_str ());
|
||||
if (this->value != -1) {
|
||||
LogMessage (LogLevel::DEBUG, L"TestMode Value({}) got val: {}", this->key, this->value);
|
||||
reset = false;
|
||||
}
|
||||
}
|
||||
return this->value;
|
||||
}
|
||||
void
|
||||
Write (int value) override {
|
||||
SetTestModeValue (this->key.c_str (), value);
|
||||
this->reset = true;
|
||||
}
|
||||
void
|
||||
Reset () override {
|
||||
this->reset = true;
|
||||
}
|
||||
};
|
||||
Menu *modManager = new RegisteredMenu (L"MOD MANAGER", L"ModManagerMenu");
|
||||
// std::vector<RegisteredItem *> registeredItems = {};
|
||||
std::vector<RegisteredSingleItem *> registeredSingleItems = {};
|
||||
std::vector<RegisteredModify *> registeredModifies = {};
|
||||
std::vector<RegisteredHook *> registeredHooks = {};
|
||||
std::wstring moddedInitial = L"";
|
||||
std::wstring modded = L"";
|
||||
|
||||
std::wstring originalDeviceInitialize = L"";
|
||||
std::wstring usingDeviceInitialize = L"";
|
||||
std::wstring moddedDeviceInitialize = L"DeviceInitialize_mod.xml";
|
||||
std::wstring originalTestMode = L"";
|
||||
std::wstring usingTestMode = L"";
|
||||
std::wstring moddedTestMode = L"TestMode_mod.xml";
|
||||
|
||||
u64 appAccessor = 0;
|
||||
RefTestModeMain refTestMode = nullptr;
|
||||
|
||||
std::wstring
|
||||
ReadXMLFileSwitcher (std::wstring &fileName) {
|
||||
const std::size_t pos = fileName.rfind (L"/");
|
||||
const std::wstring file = fileName.substr (pos + 1, fileName.size ());
|
||||
std::wstring base = fileName.substr (0, pos + 1);
|
||||
FAST_HOOK_DYNAMIC (char, SceneTestModeLoading, u64 a1, u64 a2, u64 a3) {
|
||||
char result = originalSceneTestModeLoading.fastcall<char> (a1, a2, a3);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (gameVersion == GameVersion::JPN39 && chsPatch) {
|
||||
if (file.starts_with (L"DeviceInitialize")) base.append (L"DeviceInitialize_china.xml");
|
||||
if (file.starts_with (L"TestMode")) base.append (L"TestMode_china.xml");
|
||||
if (std::filesystem::exists (base)) return base;
|
||||
}
|
||||
FAST_HOOK_DYNAMIC (char, SceneTestModeFinalize, u64 a1, u64 a2, u64 a3) {
|
||||
char result = originalSceneTestModeFinalize.fastcall<char> (a1, a2, a3);
|
||||
std::thread ([](){for (auto value : values) { value->Reset (); value->Read (); }}).detach ();
|
||||
return result;
|
||||
}
|
||||
|
||||
return fileName;
|
||||
FAST_HOOK_DYNAMIC (void, SceneFirstInitialize, u64 a1, u64 a2, u64 a3) {
|
||||
std::thread ([](){for (auto value : values) { value->Reset (); value->Read (); }}).detach ();
|
||||
originalSceneFirstInitialize.fastcall<char *> (a1, a2, a3);
|
||||
}
|
||||
|
||||
FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t *lFileName) {
|
||||
@@ -264,17 +271,15 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t
|
||||
const auto originalFileName = std::wstring (lFileName);
|
||||
LogMessage (LogLevel::DEBUG, L"Begin Loading {}", 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 (fileName.ends_with (originalDeviceInitialize)) {
|
||||
if (moddedInitial == L"") {
|
||||
fileName = ReadXMLFileSwitcher (fileName);
|
||||
fileName = replace (originalFileName, originalDeviceInitialize, usingDeviceInitialize);
|
||||
if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) {
|
||||
LogMessage (LogLevel::ERROR, L"Loading DeviceInitialize structure failed! path: " + fileName);
|
||||
moddedInitial = fileName;
|
||||
moddedInitial = originalFileName;
|
||||
fileName = originalFileName;
|
||||
} else {
|
||||
const std::wstring modFileName
|
||||
= replace (replace (replace (fileName, L"lize_asia.xml", L"lize_mod.xml"), L"lize_china.xml", L"lize_mod.xml"), L"lize.xml",
|
||||
L"lize_mod.xml");
|
||||
const std::wstring modFileName = replace (originalFileName, originalDeviceInitialize, moddedDeviceInitialize);
|
||||
const pugi::xpath_query dongleQuery = pugi::xpath_query (L"/root/menu[@id='TopMenu']/layout[@type='Center']/select-item[@id='DongleItem']");
|
||||
const pugi::xml_node dongleItem = doc.select_node (dongleQuery).node ();
|
||||
pugi::xml_node talItem = dongleItem.parent ().append_copy (dongleItem);
|
||||
@@ -288,17 +293,16 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t
|
||||
fileName = modFileName;
|
||||
}
|
||||
} else fileName = moddedInitial;
|
||||
} else if (fileName.ends_with (L"TestMode.xml") || fileName.ends_with (L"TestMode_asia.xml") || fileName.ends_with (L"TestMode_china.xml")) {
|
||||
} else if (fileName.ends_with (originalTestMode)) {
|
||||
if (modded == L"") {
|
||||
if (!((RegisteredMenu*)modManager)->items.empty () || registeredSingleItems.empty () || !registeredModifies.empty ()) {
|
||||
fileName = ReadXMLFileSwitcher (fileName);
|
||||
fileName = replace (originalFileName, originalTestMode, usingTestMode);
|
||||
if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) {
|
||||
LogMessage (LogLevel::ERROR, L"Loading TestMode structure failed! path: " + fileName);
|
||||
modded = fileName;
|
||||
modded = originalFileName;
|
||||
fileName = originalFileName;
|
||||
} else {
|
||||
const std::wstring modFileName
|
||||
= replace (replace (replace (fileName, L"Mode_asia.xml", L"Mode_mod.xml"), L"Mode_china.xml", L"Mode_mod.xml"), L"Mode.xml",
|
||||
L"Mode_mod.xml");
|
||||
const std::wstring modFileName = replace (originalFileName, originalTestMode, moddedTestMode);
|
||||
if (!((RegisteredMenu*)modManager)->items.empty ()) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin generate Mod Manager menu");
|
||||
pugi::xpath_query menuQuery
|
||||
@@ -320,13 +324,6 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t
|
||||
LogMessage (LogLevel::DEBUG, "End Apply Modify Items");
|
||||
}
|
||||
|
||||
if (!registeredHooks.empty ()) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin Apply Hooks");
|
||||
for (RegisteredHook *hook : registeredHooks) hook->Apply (&doc, &doc);
|
||||
LogMessage (LogLevel::DEBUG, "End Apply Hooks");
|
||||
}
|
||||
|
||||
// AddHookInLoop ([&](){ hookInstallFinished = true; });
|
||||
[[maybe_unused]] auto _ = doc.save_file (modFileName.c_str ());
|
||||
modded = modFileName;
|
||||
fileName = modFileName;
|
||||
@@ -340,16 +337,6 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t
|
||||
originalTestModeSetMenuHook.fastcall<void> (testModeLibrary, fileName.c_str ());
|
||||
}
|
||||
|
||||
// HOOK_DYNAMIC (void, TestModeGetValueHook, u64 *reader, const wchar_t *lItemName, int *value) {
|
||||
// originalTestModeGetValueHook (reader, lItemName, value);
|
||||
// LogMessage (LogLevel::DEBUG, L"TestMode get Item: {} Value: {}", lItemName, *value);
|
||||
// }
|
||||
|
||||
// HOOK_DYNAMIC (void, TestModeSetValueHook, u64 *reader, const wchar_t *lItemName, int value) {
|
||||
// LogMessage (LogLevel::DEBUG, L"TestMode set Item: {} Value: {}", lItemName, value);
|
||||
// originalTestModeSetValueHook (reader, lItemName, value);
|
||||
// }
|
||||
|
||||
void
|
||||
CommonModify () {
|
||||
// Default off Close time
|
||||
@@ -482,44 +469,53 @@ void
|
||||
Init () {
|
||||
LogMessage (LogLevel::INFO, "Init TestMode patches");
|
||||
|
||||
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);
|
||||
|
||||
const u64 testModeLibrary = (u64)GetModuleHandle ("TestModeLibrary.dll");
|
||||
const u64 testModeSetMenu = testModeLibrary + 0x99D0;
|
||||
// const u64 testModeGetValue = testModeLibrary + 0x6FB18;
|
||||
// const u64 testModeSetValue = testModeLibrary + 0x6FAC8;
|
||||
|
||||
LogMessage (LogLevel::DEBUG, "TestModeLibrary: {}", testModeLibrary);
|
||||
// LogMessage (LogLevel::DEBUG, "TestModeLibrary set: {}", testModeSetValue);
|
||||
// LogMessage (LogLevel::DEBUG, "TestModeLibrary get: {}", testModeGetValue);
|
||||
CommonModify ();
|
||||
for (auto method : hooks) method ();
|
||||
|
||||
switch (gameVersion) {
|
||||
case GameVersion::UNKNOWN: break;
|
||||
case GameVersion::JPN00: break;
|
||||
case GameVersion::JPN08: break;
|
||||
case GameVersion::JPN39: {
|
||||
if (config_ptr) {
|
||||
if (const auto patches = openConfigSection (config_ptr.get (), "patches")) {
|
||||
if (const auto jpn39 = openConfigSection (patches, "jpn39")) chsPatch = readConfigBool (jpn39, "chs_patch", chsPatch);
|
||||
}
|
||||
}
|
||||
if (chsPatch) LocalizationCHT ();
|
||||
INSTALL_FAST_HOOK_DYNAMIC (SceneTestModeLoading, ASLR (0x1404793D0));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (SceneTestModeFinalize, ASLR (0x140479600));
|
||||
INSTALL_FAST_HOOK_DYNAMIC (SceneFirstInitialize, ASLR (0x1404574B0));
|
||||
originalDeviceInitialize = L"DeviceInitialize.xml";
|
||||
if (Language::CnFontPatches () && std::filesystem::exists ("..\\..\\Data\\x64\\testmode\\DeviceInitialize_china.xml")) {
|
||||
usingDeviceInitialize = L"DeviceInitialize_china.xml";
|
||||
patches.push_back (safetyhook::create_mid (ASLR (0x140465549), [](SafetyHookContext &ctx)
|
||||
{ ctx.r8 = 2; ctx.rdx = (uintptr_t)"cn"; ctx.rip = ASLR (0x140465556); })); // DeviceCheck = Loading Font
|
||||
patches.push_back (safetyhook::create_mid (ASLR (0x1404676E7), [](SafetyHookContext &ctx)
|
||||
{ ctx.r8 = 2; ctx.rdx = (uintptr_t)"cn"; ctx.rip = ASLR (0x1404676F4); })); // ??
|
||||
WRITE_MEMORY (ASLR (0x140CD1E40), wchar_t, L"加载中\0");
|
||||
WRITE_MEMORY (ASLR (0x140CD1E28), wchar_t, L"加载中.\0");
|
||||
WRITE_MEMORY (ASLR (0x140CD1E68), wchar_t, L"加载中..\0");
|
||||
WRITE_MEMORY (ASLR (0x140CD1E50), wchar_t, L"加载中...\0");
|
||||
} else usingDeviceInitialize = originalDeviceInitialize;
|
||||
originalTestMode = L"TestMode.xml";
|
||||
if (Language::CnFontPatches () && std::filesystem::exists ("..\\..\\Data\\x64\\testmode\\TestMode_china.xml")) {
|
||||
usingTestMode = L"TestMode_china.xml";
|
||||
patches.push_back (safetyhook::create_mid (ASLR (0x14047C603), [](SafetyHookContext &ctx)
|
||||
{ ctx.r8 = 2; ctx.rdx = (uintptr_t)"cn"; ctx.rip = ASLR (0x14047C610); })); // TestMode = DeviceInitialize
|
||||
} else usingTestMode = originalTestMode;
|
||||
} break;
|
||||
case GameVersion::CHN00: break;
|
||||
}
|
||||
|
||||
CommonModify ();
|
||||
|
||||
INSTALL_FAST_HOOK_DYNAMIC (TestModeSetMenuHook, testModeSetMenu);
|
||||
for (auto method : hooks) method ();
|
||||
// INSTALL_HOOK_DYNAMIC (TestModeGetValueHook, testModeGetValue);
|
||||
// INSTALL_HOOK_DYNAMIC (TestModeSetValueHook, testModeSetValue);
|
||||
}
|
||||
|
||||
void
|
||||
SetupAccessor (const u64 appAccessor, const RefTestModeMain refTestMode) {
|
||||
TestMode::appAccessor = appAccessor;
|
||||
TestMode::refTestMode = refTestMode;
|
||||
if (TestMode::appAccessor != appAccessor) {
|
||||
LogMessage (LogLevel::DEBUG, "TestMode setup!");
|
||||
TestMode::appAccessor = appAccessor;
|
||||
TestMode::refTestMode = refTestMode;
|
||||
// std::thread ([](){for (auto value : values) { value->Reset (); value->Read (); }}).detach ();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@@ -528,13 +524,10 @@ ReadTestModeValue (const wchar_t *itemId) {
|
||||
if (const u64 testModeMain = refTestMode (appAccessor)) {
|
||||
int value = 0;
|
||||
u64 *reader = *reinterpret_cast<u64 **> (testModeMain + 16);
|
||||
// LogMessage (LogLevel::DEBUG, "TestMode get:{} set:{}", *reader + 256, *reader + 176);
|
||||
(*reinterpret_cast<void (__fastcall **) (u64 *, const wchar_t *, int *)> (*reader + 256)) (reader, itemId, &value);
|
||||
// originalTestModeGetValueHook (reader, itemId, &value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
LogMessage (LogLevel::ERROR, (std::wstring (L"Read TestMode(") + itemId + L") failed!").c_str ());
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -544,7 +537,6 @@ SetTestModeValue (const wchar_t *itemId, int value) {
|
||||
if (const u64 testModeMain = refTestMode (appAccessor)) {
|
||||
u64 *reader = *reinterpret_cast<u64 **> (testModeMain + 16);
|
||||
(*reinterpret_cast<void (__fastcall **) (u64 *, const wchar_t *, int)> (*reader + 176)) (reader, itemId, value);
|
||||
// ((TestModeSetValueHook)whereTestModeSetMenuHook) (reader, itemId, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,6 +547,14 @@ CreateMenu (const std::wstring &menuName, const std::wstring &menuId) {
|
||||
return new RegisteredMenu (menuName, menuId);
|
||||
}
|
||||
|
||||
Value *
|
||||
CreateValue (const std::wstring &key) {
|
||||
LogMessage (LogLevel::DEBUG, L"Create TestMode Value key: {}", key);
|
||||
Value *value = (Value *)(new TestModeValue (key));
|
||||
values.push_back ((Value *)value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void
|
||||
RegisterItem (const std::wstring &item, const std::function<void ()> &initMethod, Menu *menu) {
|
||||
LogMessage (LogLevel::DEBUG, L"Register \nItem: {}", item);
|
||||
@@ -629,7 +629,6 @@ void
|
||||
RegisterHook (const std::function<void()> &initMethod) {
|
||||
LogMessage (LogLevel::DEBUG, L"Register Hook");
|
||||
hooks.push_back (initMethod);
|
||||
// registeredHooks.push_back (new RegisteredHook (initMethod));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+47
-404
@@ -59,62 +59,84 @@ FAST_HOOK (i64, DeviceCheck, ASLR (0x140464FC0), i64 a1, i64 a2, i64 a3) {
|
||||
}
|
||||
|
||||
i64
|
||||
GetUserStatus () {
|
||||
GetPlayererCount () {
|
||||
if (appAccessor) {
|
||||
if (const u64 playDataManager = RefPlayDataManager (appAccessor)) return GetUserCount (playDataManager);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
boolean
|
||||
HaveCardPlayer () {
|
||||
if (appAccessor) {
|
||||
if (const u64 playDataManager = RefPlayDataManager (appAccessor)) {
|
||||
int p1PlayerType = *(i32 *)(*(i64 *)(playDataManager + 9920) + 4);
|
||||
int p2PlayerType = *(i32 *)(*(i64 *)(playDataManager + 9928) + 4);
|
||||
return p1PlayerType == 2 || p2PlayerType == 2;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TestMode::Value *unlockSongs = TestMode::CreateValue (L"ModUnlockSongs");
|
||||
FAST_HOOK (bool, IsSongRelease, ASLR (0x1403F4510), i64 a1, i64 a2, int a3) {
|
||||
if (TestMode::ReadTestModeValue (L"ModUnlockSongs") == 1) return true;
|
||||
if (unlockSongs->Read () == 1) return true;
|
||||
return originalIsSongRelease.fastcall<bool> (a1, a2, a3);
|
||||
}
|
||||
FAST_HOOK (bool, IsSongReleasePlayer, ASLR (0x1403F45F0), i64 a1, u64 a2, i32 a3) {
|
||||
if (TestMode::ReadTestModeValue (L"ModUnlockSongs") == 2) return true;
|
||||
if (unlockSongs->Read () == 2) return true;
|
||||
return originalIsSongReleasePlayer.fastcall<bool> (a1, a2, a3);
|
||||
}
|
||||
MID_HOOK (DifficultyPanelCrown, ASLR (0x1403F2A25), SafetyHookContext &ctx) {
|
||||
if (TestMode::ReadTestModeValue (L"ModUnlockSongs") != 1) return;
|
||||
if (unlockSongs->Read () != 1) return;
|
||||
ctx.r15 |= 1;
|
||||
}
|
||||
TestMode::Value *tournmentMode = TestMode::CreateValue (L"TournamentMode");
|
||||
TestMode::Value *mode024 = TestMode::CreateValue (L"ModModeCollabo024");
|
||||
TestMode::Value *mode025 = TestMode::CreateValue (L"ModModeCollabo025");
|
||||
TestMode::Value *mode026 = TestMode::CreateValue (L"ModModeCollabo026");
|
||||
TestMode::Value *modeApril001 = TestMode::CreateValue (L"ModModeAprilFool001");
|
||||
FAST_HOOK (i64, AvailableMode_Collabo024, ASLR (0x1402DE710), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "AvailableMode_Collabo024 was called");
|
||||
const int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
const int tournamentMode = tournmentMode->Read ();
|
||||
if (tournamentMode == 1) return originalAvailableMode_Collabo024.fastcall<i64> (a1);
|
||||
const int status = TestMode::ReadTestModeValue (L"ModModeCollabo024");
|
||||
if (status == 1 && GetUserStatus () == 1) return lua_pushbool (a1, true);
|
||||
const int status = mode024->Read ();
|
||||
if (status == 1 && GetPlayererCount () == 1) return lua_pushbool (a1, true);
|
||||
else if (status == 2 && GetPlayererCount () == 1 && HaveCardPlayer ()) return lua_pushbool (a1, true);
|
||||
return originalAvailableMode_Collabo024.fastcall<i64> (a1);
|
||||
}
|
||||
FAST_HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402DE6B0), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "AvailableMode_Collabo025 was called");
|
||||
const int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
const int tournamentMode = tournmentMode->Read ();
|
||||
if (tournamentMode == 1) return originalAvailableMode_Collabo025.fastcall<i64> (a1);
|
||||
const int status = TestMode::ReadTestModeValue (L"ModModeCollabo025");
|
||||
if (status == 1 && GetUserStatus () == 1) return lua_pushbool (a1, true);
|
||||
const int status = mode025->Read ();
|
||||
if (status == 1 && GetPlayererCount () == 1) return lua_pushbool (a1, true);
|
||||
else if (status == 2 && GetPlayererCount () == 1 && HaveCardPlayer ()) return lua_pushbool (a1, true);
|
||||
return originalAvailableMode_Collabo025.fastcall<i64> (a1);
|
||||
}
|
||||
FAST_HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402DE670), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "AvailableMode_Collabo026 was called");
|
||||
const int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
const int tournamentMode = tournmentMode->Read ();
|
||||
if (tournamentMode == 1) return originalAvailableMode_Collabo026.fastcall<i64> (a1);
|
||||
const int status = TestMode::ReadTestModeValue (L"ModModeCollabo026");
|
||||
if (status == 1 && GetUserStatus () == 1) return lua_pushbool (a1, true);
|
||||
const int status = mode026->Read ();
|
||||
if (status == 1 && GetPlayererCount () == 1) return lua_pushbool (a1, true);
|
||||
else if (status == 2 && GetPlayererCount () == 1 && HaveCardPlayer ()) return lua_pushbool (a1, true);
|
||||
return originalAvailableMode_Collabo026.fastcall<i64> (a1);
|
||||
}
|
||||
FAST_HOOK (i64, AvailableMode_AprilFool001, ASLR (0x1402DE5B0), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "AvailableMode_AprilFool001 was called");
|
||||
const int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
const int tournamentMode = tournmentMode->Read ();
|
||||
if (tournamentMode == 1) return originalAvailableMode_AprilFool001.fastcall<i64> (a1);
|
||||
const int status = TestMode::ReadTestModeValue (L"ModModeAprilFool001");
|
||||
const int status = modeApril001->Read ();
|
||||
if (status == 1) return lua_pushbool (a1, true);
|
||||
else if (status == 2 && HaveCardPlayer ()) return lua_pushbool (a1, true);
|
||||
return originalAvailableMode_AprilFool001.fastcall<i64> (a1);
|
||||
}
|
||||
TestMode::Value *freezeTimer = TestMode::CreateValue (L"ModFreezeTimer");
|
||||
i64 __fastcall lua_freeze_timer (const i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "lua_freeze_timer was called");
|
||||
const int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
const int tournamentMode = tournmentMode->Read ();
|
||||
if (tournamentMode == 1) return lua_pushbool (a1, true);
|
||||
const int status = TestMode::ReadTestModeValue (L"ModFreezeTimer");
|
||||
const int status = freezeTimer-> Read ();
|
||||
if (status == 1) return lua_pushbool (a1, true);
|
||||
return lua_pushbool (a1, false);
|
||||
}
|
||||
@@ -192,195 +214,11 @@ CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_AI, ASLR (0x140173EDD), r13, 0x2
|
||||
CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_Collabo025_026, ASLR (0x1401789AD), r13, 0x24, 0x08);
|
||||
CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_AprilFool, ASLR (0x140176716), rax, 0x34, 0x06);
|
||||
|
||||
FAST_HOOK (i64, GetLanguage, ASLR (0x140024AC0), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "GetLanguage was called");
|
||||
const auto result = originalGetLanguage.fastcall<i64> (a1);
|
||||
language = *reinterpret_cast<u32 *> (result);
|
||||
return result;
|
||||
}
|
||||
FAST_HOOK (i64, GetRegionLanguage, ASLR (0x1401CE9B0), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "GetRegionLanguage was called");
|
||||
if (patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) {
|
||||
lua_settop (a1, 0);
|
||||
lua_pushstring (a1, languageStr (language));
|
||||
return 1;
|
||||
} else return originalGetRegionLanguage.fastcall<i64> (a1);
|
||||
}
|
||||
std::thread::id *LoadMovieThreadId = nullptr;
|
||||
FUNCTION_PTR (void **, std_string_assign, ASLR (0x1400209E0), void **, const void *, size_t);
|
||||
FAST_HOOK (i64, GetCabinetLanguage, ASLR (0x14014DB80), u64 *a1, i64 a2) {
|
||||
LogMessage (LogLevel::HOOKS, "GetCabinetLanguage was called");
|
||||
i64 result = originalGetCabinetLanguage.fastcall<i64> (a1, a2);
|
||||
if (patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) {
|
||||
if (language == 2 && LoadMovieThreadId != nullptr && *LoadMovieThreadId == std::this_thread::get_id()) {
|
||||
LogMessage (LogLevel::DEBUG, "Loading DemoMovie cn_tw, redirect to cn_cn");
|
||||
std_string_assign ((void **)result, (const void*)languageStr (4), 5);
|
||||
} else {
|
||||
std_string_assign ((void **)result, (const void*)languageStr (language), (language == 0 || language == 3) ? 3 : 5);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
FAST_HOOK (i64, LoadDemoMovie, ASLR (0x1404313F0), i64 a1, i64 a2, i64 a3) {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
LoadMovieThreadId = &this_id;
|
||||
i64 result = originalLoadDemoMovie.fastcall<i64> (a1, a2, a3);
|
||||
LoadMovieThreadId = nullptr;
|
||||
return result;
|
||||
}
|
||||
std::string onpCn = "textures/onpu_cn/onp_all.nutexb";
|
||||
MID_HOOK (ChangeOnpFile, ASLR (0x140134D16), SafetyHookContext &ctx) {
|
||||
if (language == 2 && patches::TestMode::ReadTestModeValue (L"ModFixLanguage") == 1) {
|
||||
LogMessage (LogLevel::DEBUG, "Load onp_all_cn");
|
||||
ctx.rdx = (uintptr_t) onpCn.c_str ();
|
||||
ctx.r8 = 0x1F;
|
||||
}
|
||||
}
|
||||
|
||||
MID_HOOK (ChangeLanguageType, ASLR (0x1400B2016), SafetyHookContext &ctx) {
|
||||
LogMessage (LogLevel::HOOKS, "ChangeLanguageType was called");
|
||||
if (const auto pFontType = reinterpret_cast<int *> (ctx.rax); *pFontType == 4) *pFontType = 2;
|
||||
}
|
||||
|
||||
MID_HOOK (CountLockedCrown, ASLR (0x1403F2A25), SafetyHookContext &ctx) {
|
||||
LogMessage (LogLevel::HOOKS, "CountLockedCrown was called");
|
||||
ctx.r15 |= 1;
|
||||
}
|
||||
|
||||
std::map<std::string, int> nus3bankMap;
|
||||
int nus3bankIdCounter = 0;
|
||||
std::map<std::string, bool> voiceCnExist;
|
||||
bool enableSwitchVoice = false;
|
||||
std::mutex nus3bankMtx;
|
||||
|
||||
int
|
||||
get_bank_id (const std::string &bankName) {
|
||||
if (!nus3bankMap.contains (bankName)) {
|
||||
nus3bankMap[bankName] = nus3bankIdCounter;
|
||||
nus3bankIdCounter++;
|
||||
}
|
||||
LogMessage (LogLevel::DEBUG, "LoadBank {} id={}", bankName, nus3bankMap[bankName]);
|
||||
return nus3bankMap[bankName];
|
||||
}
|
||||
|
||||
void
|
||||
check_voice_tail (const std::string &bankName, u8 *pBinfBlock, std::map<std::string, bool> &voiceExist, const std::string &tail) {
|
||||
// check if any voice_xxx.nus3bank has xxx_cn audio inside while loading
|
||||
if (bankName.starts_with ("voice_")) {
|
||||
const int binfLength = *reinterpret_cast<int *> (pBinfBlock + 4);
|
||||
u8 *pGrpBlock = pBinfBlock + 8 + binfLength;
|
||||
const int grpLength = *reinterpret_cast<int *> (pGrpBlock + 4);
|
||||
u8 *pDtonBlock = pGrpBlock + 8 + grpLength;
|
||||
const int dtonLength = *reinterpret_cast<int *> (pDtonBlock + 4);
|
||||
u8 *pToneBlock = pDtonBlock + 8 + dtonLength;
|
||||
const int toneSize = *reinterpret_cast<int *> (pToneBlock + 8);
|
||||
u8 *pToneBase = pToneBlock + 12;
|
||||
for (int i = 0; i < toneSize; i++) {
|
||||
if (*reinterpret_cast<int *> (pToneBase + i * 8 + 4) <= 0x0C) continue; // skip empty space
|
||||
u8 *currToneBase = pToneBase + *reinterpret_cast<int *> (pToneBase + i * 8);
|
||||
int titleOffset = -1;
|
||||
switch (*currToneBase) {
|
||||
case 0xFF: titleOffset = 9; break; // audio mark
|
||||
case 0x7F: titleOffset = 5; break; // randomizer mark
|
||||
default: continue; // unknown mark skip
|
||||
}
|
||||
if (titleOffset > 0) {
|
||||
std::string title (reinterpret_cast<char *> (currToneBase + titleOffset));
|
||||
if (title.ends_with (tail)) {
|
||||
if (!voiceExist.contains (bankName) || !voiceExist[bankName]) {
|
||||
voiceExist[bankName] = true;
|
||||
enableSwitchVoice = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!voiceExist.contains (bankName) || voiceExist[bankName]) voiceExist[bankName] = false;
|
||||
}
|
||||
}
|
||||
|
||||
MID_HOOK (GenNus3bankId, ASLR (0x1407B97BD), SafetyHookContext &ctx) {
|
||||
LogMessage (LogLevel::HOOKS, "GenNus3bankId was called");
|
||||
std::lock_guard lock (nus3bankMtx);
|
||||
if (reinterpret_cast<u8 **> (ctx.rcx + 8) != nullptr) {
|
||||
u8 *pNus3bankFile = *reinterpret_cast<u8 **> (ctx.rcx + 8);
|
||||
if (pNus3bankFile[0] == 'N' && pNus3bankFile[1] == 'U' && pNus3bankFile[2] == 'S' && pNus3bankFile[3] == '3') {
|
||||
const int tocLength = *reinterpret_cast<int *> (pNus3bankFile + 16);
|
||||
u8 *pPropBlock = pNus3bankFile + 20 + tocLength;
|
||||
const int propLength = *reinterpret_cast<int *> (pPropBlock + 4);
|
||||
u8 *pBinfBlock = pPropBlock + 8 + propLength;
|
||||
const std::string bankName (reinterpret_cast<char *> (pBinfBlock + 0x11));
|
||||
check_voice_tail (bankName, pBinfBlock, voiceCnExist, "_cn");
|
||||
ctx.rax = get_bank_id (bankName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
FixToneName (const std::string &bankName, std::string toneName, int voiceLang) {
|
||||
if (voiceLang == 1) {
|
||||
if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) return toneName + "_cn";
|
||||
}
|
||||
return toneName;
|
||||
}
|
||||
|
||||
size_t commonSize = 0;
|
||||
FAST_HOOK (i64, PlaySoundMain, ASLR (0x1404C6DC0), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundMain was called");
|
||||
int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem");
|
||||
if (enableSwitchVoice && lang != 0) {
|
||||
const std::string bankName (lua_tolstring (a1, -3, &commonSize));
|
||||
if (bankName[0] == 'v') {
|
||||
lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ());
|
||||
lua_replace (a1, -3);
|
||||
}
|
||||
}
|
||||
return originalPlaySoundMain.fastcall<i64> (a1);
|
||||
}
|
||||
|
||||
FAST_HOOK (i64, PlaySoundMulti, ASLR (0x1404C6D60), i64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundMulti was called");
|
||||
int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem");
|
||||
if (enableSwitchVoice && lang != 0) {
|
||||
const std::string bankName (const_cast<char *> (lua_tolstring (a1, -3, &commonSize)));
|
||||
if (bankName[0] == 'v') {
|
||||
lua_pushstring (a1, FixToneName (bankName, lua_tolstring (a1, -2, &commonSize), lang).c_str ());
|
||||
lua_replace (a1, -3);
|
||||
}
|
||||
}
|
||||
return originalPlaySoundMulti.fastcall<i64> (a1);
|
||||
}
|
||||
|
||||
FUNCTION_PTR (u64 *, append_chars_to_basic_string, ASLR (0x140028DA0), u64 *, const char *, size_t);
|
||||
|
||||
u64 *
|
||||
FixToneNameEnso (u64 *Src, const std::string &bankName, int voiceLang) {
|
||||
if (voiceLang == 1) {
|
||||
if (voiceCnExist.contains (bankName) && voiceCnExist[bankName]) Src = append_chars_to_basic_string (Src, "_cn", 3);
|
||||
}
|
||||
return Src;
|
||||
}
|
||||
|
||||
FAST_HOOK (bool, PlaySoundEnso, ASLR (0x1404ED590), u64 *a1, u64 *a2, i64 a3) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundEnso was called");
|
||||
int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem");
|
||||
if (enableSwitchVoice && lang != 0) {
|
||||
const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast<char **> (a1)) : std::string (reinterpret_cast<char *> (a1));
|
||||
if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang);
|
||||
}
|
||||
return originalPlaySoundEnso.fastcall<bool> (a1, a2, a3);
|
||||
}
|
||||
|
||||
FAST_HOOK (bool, PlaySoundSpecial, ASLR (0x1404ED230), u64 *a1, u64 *a2) {
|
||||
LogMessage (LogLevel::HOOKS, "PlaySoundSpecial was called");
|
||||
int lang = TestMode::ReadTestModeValue (L"VoiceLanguageItem");
|
||||
if (enableSwitchVoice && lang != 0) {
|
||||
const std::string bankName = a1[3] > 0x10 ? std::string (*reinterpret_cast<char **> (a1)) : std::string (reinterpret_cast<char *> (a1));
|
||||
if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName, lang);
|
||||
}
|
||||
return originalPlaySoundSpecial.fastcall<bool> (a1, a2);
|
||||
}
|
||||
|
||||
int loaded_fail_count = 0;
|
||||
FAST_HOOK (char, LoadedBankAll, ASLR (0x1404EC2F0), u64 a1) {
|
||||
LogMessage (LogLevel::HOOKS, "LoadedBankAll was called");
|
||||
@@ -405,67 +243,9 @@ FAST_HOOK (i32, SetMasterVolumeSpeaker, ASLR (0x140160330), i32 a1) {
|
||||
return originalSetMasterVolumeSpeaker.fastcall<i32> (a1 > 100 ? 100 : a1);
|
||||
}
|
||||
|
||||
// std::string *fontName = nullptr;
|
||||
// HOOK (u8, SetupFontInfo, ASLR (0x14049D820), u64 a1, u64 a2, size_t a3, u64 a4) {
|
||||
// LogMessage (LogLevel::HOOKS, "SetupFontInfo was called");
|
||||
// if (fontName != nullptr) delete fontName;
|
||||
// fontName = new std::string (reinterpret_cast<char *> (a1) + 120);
|
||||
// return originalSetupFontInfo (a1, a2, a3, a4);
|
||||
// }
|
||||
|
||||
// HOOK (u32, ReadFontInfoInt, ASLR (0x14049EAC0), u64 a1, u64 a2) {
|
||||
// LogMessage (LogLevel::HOOKS, "ReadFontInfoInt was called");
|
||||
// const std::string attribute (reinterpret_cast<char *> (a2));
|
||||
// u32 result = originalReadFontInfoInt (a1, a2);
|
||||
// if (fontName->starts_with ("cn_") && attribute == "offsetV") result += 1;
|
||||
// return result;
|
||||
// }
|
||||
|
||||
TestMode::Value *attractDemo = TestMode::CreateValue (L"AttractDemoItem");
|
||||
MID_HOOK (AttractDemo, ASLR (0x14045A720), SafetyHookContext &ctx) {
|
||||
if (TestMode::ReadTestModeValue (L"AttractDemoItem") == 1) ctx.r14 = 0;
|
||||
}
|
||||
|
||||
FAST_HOOK (u64, EnsoGameManagerInitialize, ASLR (0x1400E2520), u64 a1, u64 a2, u64 a3) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerInitialize");
|
||||
u64 result = originalEnsoGameManagerInitialize.fastcall<u64> (a1, a2, a3);
|
||||
LogMessage (LogLevel::DEBUG, "End EnsoGameManagerInitialize result={}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
FAST_HOOK (u64, EnsoGameManagerLoading, ASLR (0x1400E2750), u64 a1, u64 a2, u64 a3) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerLoading");
|
||||
u64 result = originalEnsoGameManagerLoading.fastcall<u64> (a1, a2, a3);
|
||||
LogMessage (LogLevel::DEBUG, "End EnsoGameManagerLoading result={}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
FAST_HOOK (bool, EnsoGameManagerPreparing, ASLR (0x1400E2990), u64 a1, u64 a2, u64 a3) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerPreparing");
|
||||
bool result = originalEnsoGameManagerPreparing.fastcall<u64> (a1, a2, a3); // crashes here
|
||||
LogMessage (LogLevel::DEBUG, "End EnsoGameManagerPreparing result={}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
FAST_HOOK (u64, EnsoGraphicManagerPreparing, ASLR (0x1400F0AB0), u64 a1, u64 a2, u64 a3) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin EnsoGraphicManagerPreparing");
|
||||
u64 result = originalEnsoGraphicManagerPreparing.fastcall<u64> (a1, a2, a3);
|
||||
LogMessage (LogLevel::DEBUG, "End EnsoGraphicManagerPreparing result={}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
FAST_HOOK (u64, EnsoGameManagerStart, ASLR (0x1400E2A10), u64 a1, u64 a2, u64 a3) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerStart");
|
||||
u64 result = originalEnsoGameManagerStart.fastcall<u64> (a1, a2, a3);
|
||||
LogMessage (LogLevel::DEBUG, "End EnsoGameManagerStart result={}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
FAST_HOOK (u64, EnsoGameManagerChechEnsoEnd, ASLR (0x1400E2A10), u64 a1, u64 a2, u64 a3) {
|
||||
LogMessage (LogLevel::DEBUG, "Begin EnsoGameManagerChechEnsoEnd");
|
||||
u64 result = originalEnsoGameManagerChechEnsoEnd.fastcall<u64> (a1, a2, a3);
|
||||
LogMessage (LogLevel::DEBUG, "End EnsoGameManagerChechEnsoEnd result={}", result);
|
||||
return result;
|
||||
if (attractDemo->Read () == 1) ctx.r14 = 0;
|
||||
}
|
||||
|
||||
// HOOK (DWORD*, AcquireMostCompatibleDisplayMode, ASLR (0x14064C870), i64 a1, DWORD *a2, DWORD *a3) {
|
||||
@@ -475,24 +255,6 @@ FAST_HOOK (u64, EnsoGameManagerChechEnsoEnd, ASLR (0x1400E2A10), u64 a1, u64 a2,
|
||||
// return originalAcquireMostCompatibleDisplayMode (a1, a2, a3);
|
||||
// }
|
||||
|
||||
FAST_HOOK (char, SceneTestModeLoading, ASLR (0x1404793D0), u64 a1, u64 a2, u64 a3) {
|
||||
// LogMessage (LogLevel::DEBUG, "Begin SceneTestModeLoading");
|
||||
char result = originalSceneTestModeLoading.fastcall<char> (a1, a2, a3);
|
||||
// LogMessage (LogLevel::DEBUG, "End originalSceneTestModeLoading result={}", (int)result);
|
||||
TestMode::SetTestModeValue (L"EnableSwitchVoice", (int)enableSwitchVoice);
|
||||
// if (!enableSwitchVoice) TestMode::SetTestModeValue (L"VoiceLanguageItem", 0);
|
||||
// LogMessage (LogLevel::DEBUG, "End SceneTestModeLoading");
|
||||
return result;
|
||||
}
|
||||
|
||||
MID_HOOK (LogLoadTexture12, ASLR (0x14065BC8A), SafetyHookContext &ctx) {
|
||||
char* src = (char*)(ctx.rsp + 0x2B0);
|
||||
if (src != nullptr) {
|
||||
std::string srcStr = std::string (src);
|
||||
LogMessage (LogLevel::HOOKS, "LogLoadTexture12 src={}", srcStr);
|
||||
} else LogMessage (LogLevel::HOOKS, "LogLoadTexture12 src=nullptr");
|
||||
}
|
||||
|
||||
constexpr i32 datatableBufferSize = 1024 * 1024 * 12;
|
||||
uint8_t *datatableBuffer[3] = { nullptr };
|
||||
std::vector<SafetyHookMid> datatable_patch = {};
|
||||
@@ -523,17 +285,12 @@ Init () {
|
||||
i32 yRes = 1080;
|
||||
bool vsync = false;
|
||||
bool unlockSongs = true;
|
||||
bool chsPatch = false;
|
||||
bool useLayeredfs = false;
|
||||
|
||||
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) {
|
||||
if (auto patches = openConfigSection (config_ptr.get (), "patches")) {
|
||||
unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
if (auto jpn39 = openConfigSection (patches, "jpn39")) {
|
||||
chsPatch = readConfigBool (jpn39, "chs_patch", chsPatch);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto graphics = openConfigSection (config_ptr.get (), "graphics")) {
|
||||
@@ -543,14 +300,11 @@ Init () {
|
||||
}
|
||||
vsync = readConfigBool (graphics, "vsync", vsync);
|
||||
}
|
||||
|
||||
if (auto layeredfs = openConfigSection (config_ptr.get (), "layeredfs")) useLayeredfs = readConfigBool (layeredfs, "enabled", useLayeredfs);
|
||||
}
|
||||
|
||||
// Hook to get AppAccessor and ComponentAccessor
|
||||
INSTALL_FAST_HOOK (DeviceCheck);
|
||||
INSTALL_FAST_HOOK (luaL_newstate);
|
||||
INSTALL_MID_HOOK (LogLoadTexture12);
|
||||
// INSTALL_HOOK (AcquireMostCompatibleDisplayMode);
|
||||
|
||||
// Apply common config patch
|
||||
@@ -591,52 +345,6 @@ Init () {
|
||||
|
||||
// Remove datatable size limit
|
||||
ReplaceDatatableBufferAddresses ();
|
||||
// {
|
||||
// for (auto address : memsetSizeAddresses)
|
||||
// WRITE_MEMORY (ASLR (address) + 2, i32, datatableBufferSize);
|
||||
|
||||
// auto bufferBase = MODULE_HANDLE - 0x03000000;
|
||||
// AllocateStaticBufferNear (bufferBase, datatableBufferSize, &datatableBuffer1);
|
||||
// bufferBase += datatableBufferSize;
|
||||
// AllocateStaticBufferNear (bufferBase, datatableBufferSize, &datatableBuffer2);
|
||||
// bufferBase += datatableBufferSize;
|
||||
// AllocateStaticBufferNear (bufferBase, datatableBufferSize, &datatableBuffer3);
|
||||
|
||||
// ReplaceLeaBufferAddress (datatableBuffer1Addresses, datatableBuffer1.data ());
|
||||
// ReplaceLeaBufferAddress (datatableBuffer2Addresses, datatableBuffer2.data ());
|
||||
// ReplaceLeaBufferAddress (datatableBuffer3Addresses, datatableBuffer3.data ());
|
||||
// }
|
||||
|
||||
// Language
|
||||
TestMode::Menu *langPatchesMenu = TestMode::CreateMenu (L"LANGUAGE PATCHES", L"LanguagePatchesMenu");
|
||||
TestMode::Menu *langResourceIndicator = TestMode::CreateMenu (L"RESOURCE INDICATOR", L"LanguageResourceIndicatorMenu");
|
||||
TestMode::RegisterItem (
|
||||
L"<select-item label=\"FIX LANGUAGE\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" "
|
||||
L"group=\"Setting\" id=\"ModFixLanguage\" max=\"1\" min=\"0\" default=\"1\"/>",
|
||||
[&]() {
|
||||
INSTALL_FAST_HOOK (GetLanguage);
|
||||
INSTALL_FAST_HOOK (GetRegionLanguage);
|
||||
INSTALL_FAST_HOOK (GetCabinetLanguage);
|
||||
}, langPatchesMenu
|
||||
);
|
||||
TestMode::RegisterItem(
|
||||
L"<select-item label=\"CHS_VOICE FILE\" param-offset-x=\"35\" id=\"EnableSwitchVoice\" select-skip=\"True\" "
|
||||
L"replace-text=\"0:@Color/Red;NONE@Color/Default;, 1:@Color/Lime;FOUND@Color/Default;\" param-color=\"Color/White\"/>",
|
||||
langResourceIndicator
|
||||
);
|
||||
TestMode::RegisterItemAfter(
|
||||
L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']",
|
||||
L"<select-item label=\"VOICE\" param-offset-x=\"35\" disable=\"True/EnableSwitchVoice:0\" "
|
||||
L"replace-text=\"0:JPN, 1:CHN\" group=\"Setting\" id=\"VoiceLanguageItem\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&](){
|
||||
INSTALL_FAST_HOOK (PlaySoundMain);
|
||||
INSTALL_FAST_HOOK (PlaySoundMulti);
|
||||
INSTALL_FAST_HOOK (PlaySoundEnso);
|
||||
INSTALL_FAST_HOOK (PlaySoundSpecial);
|
||||
}
|
||||
);
|
||||
TestMode::RegisterItem(langResourceIndicator, langPatchesMenu);
|
||||
TestMode::RegisterItem(langPatchesMenu);
|
||||
|
||||
// Unlock Songs
|
||||
TestMode::RegisterItem (
|
||||
@@ -658,22 +366,22 @@ Init () {
|
||||
// TestMode::Menu *modeUnlock = TestMode::CreateMenu (L"MODE UNLOCK", L"ModeUnlockMenu");
|
||||
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\"/>",
|
||||
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo024\" max=\"2\" min=\"0\" default=\"0\"/>",
|
||||
[&] { INSTALL_FAST_HOOK (AvailableMode_Collabo024); }//, modeUnlock
|
||||
);
|
||||
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\"/>",
|
||||
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo025\" max=\"2\" min=\"0\" default=\"0\"/>",
|
||||
[&] { INSTALL_FAST_HOOK (AvailableMode_Collabo025); }//, modeUnlock
|
||||
);
|
||||
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\"/>",
|
||||
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo026\" max=\"2\" min=\"0\" default=\"0\"/>",
|
||||
[&] { INSTALL_FAST_HOOK (AvailableMode_Collabo026); }//, modeUnlock
|
||||
);
|
||||
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\"/>",
|
||||
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeAprilFool001\" max=\"2\" min=\"0\" default=\"0\"/>",
|
||||
[&] { INSTALL_FAST_HOOK (AvailableMode_AprilFool001); }//, modeUnlock
|
||||
);
|
||||
// TestMode::RegisterItem (modeUnlock);
|
||||
@@ -713,20 +421,10 @@ Init () {
|
||||
);
|
||||
TestMode::RegisterItemAfter(
|
||||
L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='AttractMovieItem']",
|
||||
L"<select-item label=\"ATTRACT DEMO\" disable=\"True/(ModFixLanguage:0 or LanguageItem:0)\" param-offset-x=\"35\" "
|
||||
L"<select-item label=\"ATTRACT DEMO\" disable=\"True/LanguageItem:0\" param-offset-x=\"35\" "
|
||||
L"replace-text=\"0:ON, 1:OFF\" group=\"Setting\" id=\"AttractDemoItem\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&](){ INSTALL_MID_HOOK (AttractDemo); }
|
||||
);
|
||||
TestMode::RegisterHook([&](){
|
||||
INSTALL_FAST_HOOK (EnsoGameManagerInitialize);
|
||||
INSTALL_FAST_HOOK (EnsoGameManagerLoading);
|
||||
INSTALL_FAST_HOOK (EnsoGameManagerPreparing);
|
||||
INSTALL_FAST_HOOK (EnsoGraphicManagerPreparing);
|
||||
INSTALL_FAST_HOOK (EnsoGameManagerStart);
|
||||
INSTALL_FAST_HOOK (EnsoGameManagerChechEnsoEnd);
|
||||
INSTALL_FAST_HOOK (SceneTestModeLoading);
|
||||
});
|
||||
// for (size_t i=0; i < 40; i++) TestMode::RegisterItem(std::format (L"<text-item label=\"TEST{}\"/>", i + 1));
|
||||
|
||||
// Instant Result
|
||||
// TestMode::RegisterModify(
|
||||
@@ -735,63 +433,8 @@ Init () {
|
||||
// [&](){}
|
||||
// );
|
||||
|
||||
// Use chs font/wordlist instead of cht
|
||||
if (chsPatch) {
|
||||
WRITE_MEMORY (ASLR (0x140CD1E40), wchar_t, L"加載中\0");
|
||||
WRITE_MEMORY (ASLR (0x140CD1E28), wchar_t, L"加載中.\0");
|
||||
WRITE_MEMORY (ASLR (0x140CD1E68), wchar_t, L"加載中..\0");
|
||||
WRITE_MEMORY (ASLR (0x140CD1E50), wchar_t, L"加載中...\0");
|
||||
|
||||
bool fontExistAll = true;
|
||||
const char *fontToCheck[]{"cn_30.nutexb", "cn_30.xml", "cn_32.nutexb", "cn_32.xml", "cn_64.nutexb", "cn_64.xml"};
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (useLayeredfs && std::filesystem::exists (std::string ("..\\..\\Data_mods\\x64\\font\\") + fontToCheck[i])) continue;
|
||||
if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\font\\") + fontToCheck[i])) continue;
|
||||
fontExistAll = false;
|
||||
}
|
||||
if (fontExistAll) {
|
||||
LogMessage (LogLevel::INFO, "Font all exist, using chs_patch font part!");
|
||||
WRITE_MEMORY (ASLR (0x140CD1AE0), char, "cn_64");
|
||||
WRITE_MEMORY (ASLR (0x140CD1AF0), char, "cn_32");
|
||||
WRITE_MEMORY (ASLR (0x140CD1AF8), char, "cn_30");
|
||||
WRITE_MEMORY (ASLR (0x140C946A0), char, "chineseSText");
|
||||
WRITE_MEMORY (ASLR (0x140C946B0), char, "chineseSFontType");
|
||||
INSTALL_MID_HOOK (ChangeLanguageType);
|
||||
// INSTALL_HOOK (SetupFontInfo);
|
||||
// INSTALL_HOOK (ReadFontInfoInt);
|
||||
}
|
||||
|
||||
bool demoMovieExistAll = true;
|
||||
const char *movieToCheck[]{"movie\\attractdemo_cn_cn.wmv", "sound\\attractdemo_cn_cn.nus3bank"};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (useLayeredfs && std::filesystem::exists (std::string ("..\\..\\Data_mods\\x64\\") + movieToCheck[i])) continue;
|
||||
if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\") + movieToCheck[i])) continue;
|
||||
demoMovieExistAll = false;
|
||||
}
|
||||
if (demoMovieExistAll) {
|
||||
LogMessage (LogLevel::INFO, "Resource all exist, using chs_patch attractdemo part!");
|
||||
INSTALL_FAST_HOOK (LoadDemoMovie);
|
||||
}
|
||||
|
||||
bool onpCnExist = false;
|
||||
if (useLayeredfs && std::filesystem::exists (std::string ("..\\..\\Data_mods\\x64\\textures\\onpu_cn\\onp_all.nutexb"))) onpCnExist = true;
|
||||
if (std::filesystem::exists (std::string ("..\\..\\Data\\x64\\textures\\onpu_cn\\onp_all.nutexb"))) onpCnExist = true;
|
||||
if (onpCnExist) {
|
||||
LogMessage (LogLevel::INFO, "onpu_cn/onp_all exist, using chs_patch onp part!");
|
||||
INSTALL_MID_HOOK (ChangeOnpFile);
|
||||
}
|
||||
|
||||
// LayeredFs::RegisterBeforeA ([=] (const std::string &originalFileName, const std::string ¤tFileName) -> std::string {
|
||||
// if (currentFileName.find ("\\lumen\\") == std::string::npos) return "";
|
||||
// std::string fileName = currentFileName;
|
||||
// fileName = replace (fileName, "\\lumen\\", "\\lumen_cn\\");
|
||||
// if (std::filesystem::exists (fileName)) return fileName;
|
||||
// return "";
|
||||
// });
|
||||
}
|
||||
|
||||
// Fix normal song play after passing through silent song
|
||||
INSTALL_MID_HOOK (GenNus3bankId);
|
||||
// INSTALL_MID_HOOK (GenNus3bankId);
|
||||
INSTALL_FAST_HOOK (LoadedBankAll);
|
||||
|
||||
// Disable live check
|
||||
|
||||
+3
-3
@@ -68,7 +68,7 @@ bool InitializePoll (HWND windowHandle);
|
||||
void DisposePoll ();
|
||||
void SetKeyboardButtons ();
|
||||
ConfigValue StringToConfigEnum (const char *value);
|
||||
void SetConfigValue (const toml_table_t *table, const char *key, Keybindings *keybind, bool *usePoll);
|
||||
void SetConfigValue (const toml_table_t *table, const char *key, Keybindings *keybind, u8 *inputState);
|
||||
InternalButtonState GetInternalButtonState (const Keybindings &bindings);
|
||||
void SetRumble (int left, int right, int length);
|
||||
|
||||
@@ -108,7 +108,7 @@ bool ControllerAxisWasUp (const SDLAxis axis);
|
||||
bool ControllerAxisIsReleased (const SDLAxis axis);
|
||||
bool IsButtonReleased (const Keybindings &bindings);
|
||||
#else
|
||||
void UpdatePoll (HWND windowHandle, bool useController);
|
||||
void UpdatePoll (HWND windowHandle);
|
||||
void CleanPoll ();
|
||||
void InitializeKeyboard ();
|
||||
void DisposeKeyboard ();
|
||||
#endif
|
||||
+175
-128
@@ -7,8 +7,15 @@
|
||||
|
||||
extern bool jpLayout;
|
||||
extern int exited;
|
||||
extern u8 inputState;
|
||||
extern float axisThreshold;
|
||||
extern bool globalKeyboardInput;
|
||||
|
||||
bool wndForeground = false;
|
||||
bool usingKeyboard = false;
|
||||
bool usingMouse = false;
|
||||
bool usingController = false;
|
||||
bool usingSDLEvent = false;
|
||||
|
||||
bool currentKeyboardState[0xFF] = { false };
|
||||
uint8_t keyboardCount[0xFF] = { 0 };
|
||||
@@ -20,11 +27,13 @@ uint8_t controllerDiff[SDL_GAMEPAD_BUTTON_COUNT] = { 0 };
|
||||
char currentMouseWheelDirection = 0;
|
||||
uint8_t mouseWheelCount[2] = { 0 };
|
||||
uint8_t mouseWheelDiff[2] = { 0 };
|
||||
// SDLAxisState currentControllerAxisState;
|
||||
float currentControllerAxisState[SDL_AXIS_MAX] = { 0.0 };
|
||||
uint8_t controllerAxisCount[SDL_AXIS_MAX] = { 0 };
|
||||
uint8_t controllerAxisDiff[SDL_AXIS_MAX] = { 0 };
|
||||
|
||||
int maxCount = 1;
|
||||
|
||||
SDLAxisState currentControllerAxisState;
|
||||
|
||||
SDL_Window *window;
|
||||
SDL_Gamepad *controllers[255];
|
||||
bool flipped[255];
|
||||
@@ -39,7 +48,7 @@ SetKeyboardButtons () {
|
||||
}
|
||||
|
||||
void
|
||||
SetConfigValue (const toml_table_t *table, const char *key, Keybindings *key_bind, bool *usePoll) {
|
||||
SetConfigValue (const toml_table_t *table, const char *key, Keybindings *key_bind, u8 *inputState) {
|
||||
const toml_array_t *array = toml_array_in (table, key);
|
||||
if (!array) {
|
||||
LogMessage (LogLevel::WARN, std::string (key) + ": Cannot find array");
|
||||
@@ -58,6 +67,7 @@ SetConfigValue (const toml_table_t *table, const char *key, Keybindings *key_bin
|
||||
|
||||
switch (value.type) {
|
||||
case keycode: {
|
||||
*inputState |= 1;
|
||||
for (int i = 0; i < std::size (key_bind->keycodes); i++) {
|
||||
if (key_bind->keycodes[i] == 0) {
|
||||
key_bind->keycodes[i] = value.keycode;
|
||||
@@ -67,7 +77,7 @@ SetConfigValue (const toml_table_t *table, const char *key, Keybindings *key_bin
|
||||
break;
|
||||
}
|
||||
case button: {
|
||||
*usePoll = true;
|
||||
*inputState |= (1 << 2);
|
||||
for (int i = 0; i < std::size (key_bind->buttons); i++) {
|
||||
if (key_bind->buttons[i] == SDL_GAMEPAD_BUTTON_INVALID) {
|
||||
key_bind->buttons[i] = value.button;
|
||||
@@ -77,7 +87,7 @@ SetConfigValue (const toml_table_t *table, const char *key, Keybindings *key_bin
|
||||
break;
|
||||
}
|
||||
case axis: {
|
||||
*usePoll = true;
|
||||
*inputState |= (1 << 2);
|
||||
for (int i = 0; i < std::size (key_bind->axis); i++) {
|
||||
if (key_bind->axis[i] == 0) {
|
||||
key_bind->axis[i] = value.axis;
|
||||
@@ -86,6 +96,7 @@ SetConfigValue (const toml_table_t *table, const char *key, Keybindings *key_bin
|
||||
}
|
||||
}
|
||||
case scroll: {
|
||||
*inputState |= (1 << 1);
|
||||
for (int i = 0; i < std::size (key_bind->scroll); i++) {
|
||||
if (key_bind->scroll[i] == 0) {
|
||||
key_bind->scroll[i] = value.scroll;
|
||||
@@ -119,36 +130,61 @@ LRESULT CALLBACK InputProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
DWORD keyCode = pKeyboard->vkCode;
|
||||
currentKeyboardState[keyCode] = false;
|
||||
} break;
|
||||
case WM_MOUSEWHEEL: if (wndForeground) {
|
||||
MSLLHOOKSTRUCT *pMouseStruct = (MSLLHOOKSTRUCT*)lParam;
|
||||
int wheelDelta = GET_WHEEL_DELTA_WPARAM(pMouseStruct->mouseData);
|
||||
if (wheelDelta > 0) {
|
||||
mouseWheelCount[0] = std::min (mouseWheelCount[0] + wheelDelta, maxCount);
|
||||
currentMouseState.ScrolledUp = true;
|
||||
} else if (wheelDelta < 0) {
|
||||
mouseWheelCount[1] = std::min (mouseWheelCount[1] - wheelDelta, maxCount);
|
||||
currentMouseState.ScrolledDown = true;
|
||||
}
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(keyboardHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
void
|
||||
InitializeKeyboard () {
|
||||
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, InputProc, nullptr, 0);
|
||||
if (keyboardHook == nullptr) LogMessage (LogLevel::ERROR, "Failed to install keyboard hook!\n");
|
||||
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, InputProc, nullptr, 0);
|
||||
if (maxCount > 1) LogMessage (LogLevel::ERROR, "CHEATING MODE! max count is set to {}", maxCount);
|
||||
atexit (DisposeKeyboard);
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (msg) {
|
||||
case WM_DESTROY: PostQuitMessage(0); break;
|
||||
default: return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
HWND CreateInvisibleWindow(HINSTANCE hInstance) {
|
||||
WNDCLASS wc = {0};
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = "InvisibleWindowClass";
|
||||
if (!RegisterClass(&wc)) return nullptr;
|
||||
HWND hwnd = CreateWindowEx(
|
||||
WS_EX_LAYERED | WS_EX_TOOLWINDOW,
|
||||
wc.lpszClassName,
|
||||
"Invisible Window",
|
||||
0, 0, 0, 0, 0,
|
||||
NULL, NULL, hInstance, NULL
|
||||
);
|
||||
if (hwnd) SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
void KeyboardMainLoop() {
|
||||
HINSTANCE hInstance = GetModuleHandle(NULL);
|
||||
HWND hwnd = CreateInvisibleWindow(hInstance);
|
||||
if (!hwnd) LogMessage (LogLevel::ERROR, "Failed to create window!\n");
|
||||
if (usingKeyboard) {
|
||||
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, InputProc, nullptr, 0);
|
||||
if (keyboardHook == nullptr) LogMessage (LogLevel::ERROR, "Failed to install keyboard hook!\n");
|
||||
else LogMessage (LogLevel::INFO, "KeyboardLL hook installed!");
|
||||
}
|
||||
if (maxCount > 2) LogMessage (LogLevel::ERROR, "CHEATING MODE! max count is set to {}", maxCount);
|
||||
LogMessage (LogLevel::WARN, "(experimental) Using Async IO!");
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, hwnd, 0, 0) > 0) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
if (keyboardHook) UnhookWindowsHookEx(keyboardHook);
|
||||
}
|
||||
|
||||
void
|
||||
DisposeKeyboard () {
|
||||
if (keyboardHook) UnhookWindowsHookEx(keyboardHook);
|
||||
if (mouseHook) UnhookWindowsHookEx(mouseHook);
|
||||
InitializeKeyboard () {
|
||||
if (usingKeyboard) {
|
||||
std::thread (KeyboardMainLoop).detach ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -160,64 +196,72 @@ CheckFlipped (uint32_t which, SDL_Gamepad *gamepad) {
|
||||
|
||||
bool
|
||||
InitializePoll (HWND windowHandle) {
|
||||
LogMessage (LogLevel::DEBUG, "InitializePoll");
|
||||
bool hasRumble = true;
|
||||
wndForeground = windowHandle == GetForegroundWindow ();
|
||||
SDL_SetMainReady ();
|
||||
usingKeyboard = inputState & 1;
|
||||
usingMouse = inputState & (1 << 1);
|
||||
usingController = inputState & (1 << 2);
|
||||
usingSDLEvent = usingMouse || usingController;
|
||||
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
InitializeKeyboard ();
|
||||
if (usingSDLEvent) {
|
||||
LogMessage (LogLevel::DEBUG, "InitializePoll");
|
||||
bool hasRumble = true;
|
||||
wndForeground = windowHandle == GetForegroundWindow ();
|
||||
SDL_SetMainReady ();
|
||||
|
||||
if (!SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS | SDL_INIT_VIDEO)) {
|
||||
if (SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS | SDL_INIT_VIDEO)) {
|
||||
hasRumble = false;
|
||||
} else {
|
||||
LogMessage (LogLevel::ERROR, std::string ("SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS | SDL_INIT_VIDEO): ") + SDL_GetError ());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
|
||||
SDL_SetHint (SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
|
||||
auto configPath = std::filesystem::current_path () / "gamecontrollerdb.txt";
|
||||
if (SDL_AddGamepadMappingsFromFile (configPath.string ().c_str ()) == -1)
|
||||
LogMessage (LogLevel::ERROR, "Cannot read gamecontrollerdb.txt");
|
||||
SDL_SetGamepadEventsEnabled (true);
|
||||
SDL_SetJoystickEventsEnabled (true);
|
||||
|
||||
int numJoysticks = 0;
|
||||
auto* joyStickIds = SDL_GetJoysticks(&numJoysticks);
|
||||
if (joyStickIds == nullptr) {
|
||||
LogMessage (LogLevel::ERROR, "Error getting joystick ids");
|
||||
exit(1);
|
||||
}
|
||||
for (int i = 0; i < numJoysticks; i++) {
|
||||
auto id = joyStickIds[i];
|
||||
if (!SDL_IsGamepad (id)) continue;
|
||||
SDL_Gamepad *controller = SDL_OpenGamepad (id);
|
||||
|
||||
if (!controller) {
|
||||
LogMessage (LogLevel::WARN, std::string ("Could not open gamecontroller ") + SDL_GetGamepadNameForID (id) + ": " + SDL_GetError ());
|
||||
continue;
|
||||
if (!SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS | SDL_INIT_VIDEO)) {
|
||||
if (SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS | SDL_INIT_VIDEO)) {
|
||||
hasRumble = false;
|
||||
} else {
|
||||
LogMessage (LogLevel::ERROR, std::string ("SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS | SDL_INIT_VIDEO): ") + SDL_GetError ());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LogMessage (LogLevel::DEBUG, "Init Controller connected!");
|
||||
CheckFlipped (id, controller);
|
||||
controllers[id] = controller;
|
||||
}
|
||||
SDL_free (joyStickIds);
|
||||
auto configPath = std::filesystem::current_path () / "gamecontrollerdb.txt";
|
||||
if (SDL_AddGamepadMappingsFromFile (configPath.string ().c_str ()) == -1)
|
||||
LogMessage (LogLevel::ERROR, "Cannot read gamecontrollerdb.txt");
|
||||
SDL_SetGamepadEventsEnabled (true);
|
||||
SDL_SetJoystickEventsEnabled (true);
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
if(props == 0) {
|
||||
LogMessage (LogLevel::ERROR, "Unable to create SDL_Properties");
|
||||
exit(1);
|
||||
}
|
||||
SDL_SetPointerProperty (props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, windowHandle);
|
||||
window = SDL_CreateWindowWithProperties (props);
|
||||
if (window == NULL) LogMessage (LogLevel::ERROR, std::string ("SDL_CreateWindowFrom (windowHandle): ") + SDL_GetError ());
|
||||
int numJoysticks = 0;
|
||||
auto* joyStickIds = SDL_GetJoysticks(&numJoysticks);
|
||||
if (joyStickIds == nullptr) {
|
||||
LogMessage (LogLevel::ERROR, "Error getting joystick ids");
|
||||
exit(1);
|
||||
}
|
||||
for (int i = 0; i < numJoysticks; i++) {
|
||||
auto id = joyStickIds[i];
|
||||
if (!SDL_IsGamepad (id)) continue;
|
||||
SDL_Gamepad *controller = SDL_OpenGamepad (id);
|
||||
|
||||
atexit (DisposePoll);
|
||||
return hasRumble;
|
||||
if (!controller) {
|
||||
LogMessage (LogLevel::WARN, std::string ("Could not open gamecontroller ") + SDL_GetGamepadNameForID (id) + ": " + SDL_GetError ());
|
||||
continue;
|
||||
}
|
||||
|
||||
LogMessage (LogLevel::DEBUG, "Init Controller connected!");
|
||||
CheckFlipped (id, controller);
|
||||
controllers[id] = controller;
|
||||
}
|
||||
SDL_free (joyStickIds);
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
if(props == 0) {
|
||||
LogMessage (LogLevel::ERROR, "Unable to create SDL_Properties");
|
||||
exit(1);
|
||||
}
|
||||
SDL_SetPointerProperty (props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, windowHandle);
|
||||
window = SDL_CreateWindowWithProperties (props);
|
||||
if (window == NULL) LogMessage (LogLevel::ERROR, std::string ("SDL_CreateWindowFrom (windowHandle): ") + SDL_GetError ());
|
||||
|
||||
atexit (DisposePoll);
|
||||
return hasRumble;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -243,29 +287,46 @@ CorrectButton (uint32_t which, uint8_t button) {
|
||||
}
|
||||
|
||||
void
|
||||
UpdatePoll (HWND windowHandle, bool useController) {
|
||||
if (!CheckForegroundWindow (windowHandle)) return;
|
||||
|
||||
currentMouseState.ScrolledUp = false;
|
||||
currentMouseState.ScrolledDown = false;
|
||||
if (keyboardClean) {
|
||||
CleanPoll () {
|
||||
if (usingKeyboard && keyboardClean) {
|
||||
keyboardClean = false;
|
||||
for (int i=0; i<0xFF; i++) keyboardCount[i] -= (bool)keyboardDiff[i];
|
||||
memset (keyboardDiff, 0, 0XFF);
|
||||
}
|
||||
if (mouseWheelCount[0] > 0) mouseWheelCount[0] -= (bool)mouseWheelDiff[0];
|
||||
if (mouseWheelCount[1] > 0) mouseWheelCount[1] -= (bool)mouseWheelDiff[1];
|
||||
memset (mouseWheelDiff, 0, 2);
|
||||
if (usingMouse) {
|
||||
currentMouseState.ScrolledUp = false;
|
||||
currentMouseState.ScrolledDown = false;
|
||||
if (mouseWheelCount[0] > 0) mouseWheelCount[0] -= (bool)mouseWheelDiff[0];
|
||||
if (mouseWheelCount[1] > 0) mouseWheelCount[1] -= (bool)mouseWheelDiff[1];
|
||||
memset (mouseWheelDiff, 0, 2);
|
||||
}
|
||||
|
||||
// GetCursorPos (¤tMouseState.Position);
|
||||
// ScreenToClient (windowHandle, ¤tMouseState.Position);
|
||||
if (useController) {
|
||||
if (usingController) {
|
||||
for (int i=0; i<SDL_GAMEPAD_BUTTON_COUNT; i++) controllerCount[i] -= (bool)controllerDiff[i];
|
||||
for (int i=1; i<SDL_AXIS_MAX; i++) controllerAxisCount[i] -= (bool)controllerAxisDiff[i];
|
||||
memset (controllerDiff, 0, SDL_GAMEPAD_BUTTON_COUNT);
|
||||
memset (controllerAxisDiff, 0, SDL_AXIS_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UpdatePoll (HWND windowHandle) {
|
||||
if (!CheckForegroundWindow (windowHandle)) return;
|
||||
CleanPoll ();
|
||||
if (usingSDLEvent) {
|
||||
SDL_Event event;
|
||||
SDL_Gamepad *controller;
|
||||
while (SDL_PollEvent (&event) != 0) {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
if (event.wheel.y > 0) {
|
||||
mouseWheelCount[0] = std::min (mouseWheelCount[0] + 1, maxCount);
|
||||
currentMouseState.ScrolledUp = true;
|
||||
} else if (event.wheel.y < 0) {
|
||||
mouseWheelCount[1] = std::min (mouseWheelCount[1] + 1, maxCount);
|
||||
currentMouseState.ScrolledDown = true;
|
||||
}
|
||||
} break;
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
if (!SDL_IsGamepad (event.gdevice.which)) break;
|
||||
controller = SDL_OpenGamepad (event.gdevice.which);
|
||||
@@ -298,17 +359,27 @@ UpdatePoll (HWND windowHandle, bool useController) {
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
float sensorValue = event.gaxis.value == 0 ? 0 : event.gaxis.value / (event.gaxis.value > 0 ? 32767.0f : -32768.0f);
|
||||
if (wndForeground || sensorValue == 0) {
|
||||
if (wndForeground || sensorValue < axisThreshold) {
|
||||
switch (event.gaxis.axis) {
|
||||
case SDL_GAMEPAD_AXIS_LEFTX: currentControllerAxisState.LeftRight = sensorValue; break;
|
||||
case SDL_GAMEPAD_AXIS_LEFTY: currentControllerAxisState.LeftDown = sensorValue; break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHTX: currentControllerAxisState.RightRight = sensorValue; break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHTY: currentControllerAxisState.RightDown = sensorValue; break;
|
||||
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: currentControllerAxisState.LTriggerDown = sensorValue; break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: currentControllerAxisState.RTriggerDown = sensorValue; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_LEFTX: case SDL_GAMEPAD_AXIS_LEFTY: case SDL_GAMEPAD_AXIS_RIGHTX: case SDL_GAMEPAD_AXIS_RIGHTY: {
|
||||
int direction = event.gaxis.axis * 2 + (event.gaxis.value < 0 ? 1 : 2);
|
||||
int reverse = event.gaxis.axis * 2 + (event.gaxis.value < 0 ? 2 : 1);
|
||||
currentControllerAxisState[reverse] = 0.0;
|
||||
if (sensorValue > axisThreshold && currentControllerAxisState[direction] <= axisThreshold) {
|
||||
LogMessage (LogLevel::DEBUG, "direction: {} value: {}", direction, sensorValue);
|
||||
controllerAxisCount[direction] = std::min (controllerAxisCount[direction] + 1, maxCount);
|
||||
}
|
||||
currentControllerAxisState[direction] = sensorValue;
|
||||
} break;
|
||||
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: {
|
||||
int button = 5 + event.gaxis.axis;
|
||||
if (sensorValue > axisThreshold && currentControllerAxisState[button] <= axisThreshold) {
|
||||
controllerAxisCount[button] = std::min (controllerAxisCount[button] + 1, maxCount);
|
||||
}
|
||||
currentControllerAxisState[button] = sensorValue;
|
||||
} break;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,10 +442,6 @@ int maxKeyboardCount = 1;
|
||||
bool
|
||||
KeyboardIsTapped (const uint8_t keycode) {
|
||||
if (keyboardCount[keycode] > 0) {
|
||||
// if (maxKeyboardCount < keyboardCount[keycode]) {
|
||||
// maxKeyboardCount = keyboardCount[keycode];
|
||||
// LogMessage (LogLevel::DEBUG, "MAX KEYBOARD COLLECTED {} {}", keycode, maxKeyboardCount);
|
||||
// }
|
||||
keyboardDiff[keycode] = 1;
|
||||
keyboardClean = true;
|
||||
return true;
|
||||
@@ -402,10 +469,6 @@ bool
|
||||
GetMouseScrollIsTapped (const Scroll scroll) {
|
||||
if (scroll == MOUSE_SCROLL_INVALID) return false;
|
||||
if (mouseWheelCount[scroll - 1] > 0) {
|
||||
// if (maxWheelCount < mouseWheelCount[scroll - 1]) {
|
||||
// maxWheelCount = mouseWheelCount[scroll - 1];
|
||||
// LogMessage (LogLevel::DEBUG, "MAX WHEEL COLLECTED {} {}", (int)scroll, maxWheelCount);
|
||||
// }
|
||||
mouseWheelDiff[scroll - 1] = 1;
|
||||
return true;
|
||||
} return false;
|
||||
@@ -420,10 +483,6 @@ int maxButtonCount = 1;
|
||||
bool
|
||||
ControllerButtonIsTapped (const SDL_GamepadButton button) {
|
||||
if (controllerCount[button] > 0) {
|
||||
// if (maxButtonCount < controllerCount[button]) {
|
||||
// maxButtonCount = controllerCount[button];
|
||||
// LogMessage (LogLevel::DEBUG, "MAX BUTTON COLLECTED {} {}", (int)button, maxButtonCount);
|
||||
// }
|
||||
controllerDiff[button] = 1;
|
||||
return true;
|
||||
} return false;
|
||||
@@ -431,29 +490,19 @@ ControllerButtonIsTapped (const SDL_GamepadButton button) {
|
||||
|
||||
float
|
||||
ControllerAxisIsDown (const SDLAxis axis) {
|
||||
switch (axis) {
|
||||
case SDL_AXIS_LEFT_LEFT: return currentControllerAxisState.LeftLeft;
|
||||
case SDL_AXIS_LEFT_RIGHT: return currentControllerAxisState.LeftRight;
|
||||
case SDL_AXIS_LEFT_UP: return currentControllerAxisState.LeftUp;
|
||||
case SDL_AXIS_LEFT_DOWN: return currentControllerAxisState.LeftDown;
|
||||
case SDL_AXIS_RIGHT_LEFT: return currentControllerAxisState.RightLeft;
|
||||
case SDL_AXIS_RIGHT_RIGHT: return currentControllerAxisState.RightRight;
|
||||
case SDL_AXIS_RIGHT_UP: return currentControllerAxisState.RightUp;
|
||||
case SDL_AXIS_RIGHT_DOWN: return currentControllerAxisState.RightDown;
|
||||
case SDL_AXIS_LTRIGGER_DOWN: return currentControllerAxisState.LTriggerDown;
|
||||
case SDL_AXIS_RTRIGGER_DOWN: return currentControllerAxisState.RTriggerDown;
|
||||
default: return false;
|
||||
}
|
||||
return currentControllerAxisState[axis];
|
||||
}
|
||||
|
||||
bool
|
||||
ControllerAxisIsTapped (const SDLAxis axis) {
|
||||
return ControllerAxisIsDown (axis);
|
||||
if (controllerAxisCount[axis] > 0) {
|
||||
controllerAxisDiff[axis] = 1;
|
||||
return true;
|
||||
} return false;
|
||||
}
|
||||
|
||||
bool
|
||||
IsButtonTapped (const Keybindings &bindings) {
|
||||
|
||||
for (size_t i = 0; i < ConfigKeyboardButtonsCount; i++) {
|
||||
if (bindings.keycodes[i] == 0) continue;
|
||||
if (KeyboardIsTapped (bindings.keycodes[i])) return true;
|
||||
@@ -471,7 +520,6 @@ IsButtonTapped (const Keybindings &bindings) {
|
||||
if (GetMouseScrollIsTapped (bindings.scroll[i])) return true;
|
||||
}
|
||||
return false;
|
||||
// return GetInternalButtonState (bindings).Tapped;
|
||||
}
|
||||
|
||||
float
|
||||
@@ -493,6 +541,5 @@ IsButtonDown (const Keybindings &bindings) {
|
||||
if (GetMouseScrollIsDown (bindings.scroll[i])) return 1.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
// return GetInternalButtonState (bindings).Down;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user