From 7e7a9018db8e637560d54708a7a8b8ce2afcadd7 Mon Sep 17 00:00:00 2001 From: ptmaster Date: Thu, 23 Jan 2025 00:53:13 +0800 Subject: [PATCH] Several fix & optimize --- CMakeLists.txt | 2 + README.md | 12 +- dist/config.toml | 4 +- src/Windows/ExpectedError.h | 30 ++++ src/Windows/MinimumLatencyAudioClient.cpp | 143 ++++++++++++++++ src/Windows/MinimumLatencyAudioClient.h | 39 +++++ src/Windows/WindowsError.cpp | 13 ++ src/Windows/WindowsError.h | 18 ++ src/dllmain.cpp | 24 ++- src/helpers.cpp | 5 +- src/patches/audio.cpp | 22 +++ src/patches/language.cpp | 35 ++-- src/patches/patches.h | 12 ++ src/patches/testmode.cpp | 197 ++++++++++------------ src/patches/versions/JPN39.cpp | 20 +-- src/patches/wordlist.cpp | 60 +++++++ src/pollasync.cpp | 3 +- src/polldef.h | 1 + 18 files changed, 477 insertions(+), 163 deletions(-) create mode 100644 src/Windows/ExpectedError.h create mode 100644 src/Windows/MinimumLatencyAudioClient.cpp create mode 100644 src/Windows/MinimumLatencyAudioClient.h create mode 100644 src/Windows/WindowsError.cpp create mode 100644 src/Windows/WindowsError.h create mode 100644 src/patches/wordlist.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 97b7a64..f4aee58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,6 +138,8 @@ set(SOURCES src/poll.cpp src/pollasync.cpp src/bnusio.cpp + src/Windows/WindowsError.cpp + src/Windows/MinimumLatencyAudioClient.cpp src/patches/amauth.cpp # src/patches/dxgi.cpp # src/patches/fpslimiter.cpp diff --git a/README.md b/README.md index fd8cc2c..48fde7a 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,12 @@ version = "auto" # Patch version # | - JPN08: For use with Taiko JPN 08.18 # | - JPN39: For use with Taiko JPN 39.06 # | - CHN00: For use with Taiko CHN 00.32 +<<<<<<< HEAD unlock_songs = true # Self-explanatory local_files = true # Only set this to false if you're using this on a Nijiiro Cabinet, running on BNA1 +======= +unlock_songs = true +>>>>>>> 684ee61 (Several fix & optimize) [patches.chn00] # These patches are only available for version CHN00 fix_language = false # Sync test mode language to attract etc @@ -63,13 +67,15 @@ model_res_rate = 1.0 # Don-Model resolution rate (currently JPN39 only) [audio] -wasapi_shared = false # Wasapi shared mode, allows you to have multiple audio sources at once at a cost of having higher latency. +real = true # Reduce audio latency when you are using "High Definition Audio" driver +wasapi_shared = true # Wasapi shared mode, allows you to have multiple audio sources at once at a cost of having higher latency. asio = false # Use asio audio mode asio_driver = "ASIO4ALL v2" # Asio driver name # | If you're not using asio4all, open up regedit then navigate to HKEY_LOCAL_MACHINE\SOFTWARE\ASIO for your driver's name. # | It is case-sensitive. + [qr] image_path = "" # Path to the image of the QR Code you want to use @@ -85,6 +91,8 @@ song_no = [] # Song noes used for custom folder [controller] wait_period = 0 # Input interval (if using taiko drum controller, should be set to 0) analog_input = false # Use analog input (you need a compatible controller, this allows playing small and big notes like on arcade cabinets) +global_keyboard = false # Accept keyboard input even if Taiko.exe is not foreground + [keyboard] @@ -151,7 +159,7 @@ Clone this repository, open *cmd* and run the following commands: call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 # Configure the build folder (this is only needed the first time) -cmake -B build -S . -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release +cmake -B build -S . -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION="10.0.26100.0" # Build TaikoArcadeLoader cmake --build build --config Release --target bnusio diff --git a/dist/config.toml b/dist/config.toml index 1e9cbd1..2346e8a 100644 --- a/dist/config.toml +++ b/dist/config.toml @@ -43,7 +43,8 @@ model_res_rate = 1.0 # Don-Model resolution rate (currently JPN39 only) [audio] -wasapi_shared = false # Wasapi shared mode, allows you to have multiple audio sources at once at a cost of having higher latency. +real = true # Reduce audio latency when you are using "High Definition Audio" driver +wasapi_shared = true # Wasapi shared mode, allows you to have multiple audio sources at once at a cost of having higher latency. asio = false # Use asio audio mode asio_driver = "ASIO4ALL v2" # Asio driver name # | If you're not using asio4all, open up regedit then navigate to HKEY_LOCAL_MACHINE\SOFTWARE\ASIO for your driver's name. @@ -65,6 +66,7 @@ song_no = [] # Song noes used for custom folder [controller] wait_period = 0 # Input interval (if using taiko drum controller, should be set to 0) analog_input = false # Use analog input (you need a compatible controller, this allows playing small and big notes like on arcade cabinets) +global_keyboard = false # Accept keyboard input even if Taiko.exe is not foreground [keyboard] diff --git a/src/Windows/ExpectedError.h b/src/Windows/ExpectedError.h new file mode 100644 index 0000000..3018b5b --- /dev/null +++ b/src/Windows/ExpectedError.h @@ -0,0 +1,30 @@ +#pragma once + +#include +namespace miniant { + +class ExpectedError { +public: + explicit ExpectedError(std::string message) noexcept: + m_message(std::move(message)) {} + + explicit ExpectedError(const char* message): + m_message(message) {} + + ExpectedError(const ExpectedError&) = default; + ExpectedError(ExpectedError&&) noexcept = default; + + ExpectedError& operator= (const ExpectedError&) = default; + ExpectedError& operator= (ExpectedError&&) noexcept = default; + + virtual ~ExpectedError() = default; + + virtual std::string GetMessage() const { + return m_message; + } + +private: + std::string m_message; +}; + +} diff --git a/src/Windows/MinimumLatencyAudioClient.cpp b/src/Windows/MinimumLatencyAudioClient.cpp new file mode 100644 index 0000000..dd8112f --- /dev/null +++ b/src/Windows/MinimumLatencyAudioClient.cpp @@ -0,0 +1,143 @@ +#include "MinimumLatencyAudioClient.h" + +#include +#include + +#include + +using namespace miniant::Windows; +using namespace miniant::Windows::WasapiLatency; + +const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); +const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); +const IID IID_IAudioClient3 = __uuidof(IAudioClient3); + +MinimumLatencyAudioClient::MinimumLatencyAudioClient(MinimumLatencyAudioClient&& other) noexcept { + assert(other.m_pAudioClient != nullptr); + assert(other.m_pFormat != nullptr); + + m_pAudioClient = other.m_pAudioClient; + m_pFormat = other.m_pFormat; + + other.m_pAudioClient = nullptr; + other.m_pFormat = nullptr; +} +MinimumLatencyAudioClient::MinimumLatencyAudioClient(void* pAudioClient, void* pFormat) : + m_pAudioClient(pAudioClient), m_pFormat(pFormat) {} + +MinimumLatencyAudioClient::~MinimumLatencyAudioClient() { + Uninitialise(); +} + +MinimumLatencyAudioClient& MinimumLatencyAudioClient::operator= (MinimumLatencyAudioClient&& rhs) noexcept { + assert(rhs.m_pAudioClient != nullptr); + assert(rhs.m_pFormat != nullptr); + + Uninitialise(); + m_pAudioClient = rhs.m_pAudioClient; + m_pFormat = rhs.m_pFormat; + + rhs.m_pAudioClient = nullptr; + rhs.m_pFormat = nullptr; + + return *this; +} + +void MinimumLatencyAudioClient::Uninitialise() { + if (m_pAudioClient == nullptr) { + assert(m_pFormat == nullptr); + return; + } + + assert(m_pFormat != nullptr); + + static_cast(m_pAudioClient)->Release(); + m_pAudioClient = nullptr; + + CoTaskMemFree(m_pFormat); + m_pFormat = nullptr; +} + +std::expected MinimumLatencyAudioClient::GetProperties() const { + Properties properties; + HRESULT hr = static_cast(m_pAudioClient)->GetSharedModeEnginePeriod( + static_cast(m_pFormat), + &properties.defaultBufferSize, + &properties.fundamentalBufferSize, + &properties.minimumBufferSize, + &properties.maximumBufferSize); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + properties.sampleRate = static_cast(m_pFormat)->nSamplesPerSec; + properties.bitsPerSample = static_cast(m_pFormat)->wBitsPerSample; + properties.numChannels = static_cast(m_pFormat)->nChannels; + + return properties; +} + +std::expected MinimumLatencyAudioClient::Start() { + + HRESULT hr = CoInitialize (nullptr); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + IMMDeviceEnumerator* pEnumerator; + hr = CoCreateInstance( + CLSID_MMDeviceEnumerator, nullptr, + CLSCTX_ALL, + IID_IMMDeviceEnumerator, + reinterpret_cast(&pEnumerator)); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + IMMDevice* pDevice; + hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + IAudioClient3* pAudioClient; + hr = pDevice->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, reinterpret_cast(&pAudioClient)); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + WAVEFORMATEX* pFormat; + hr = pAudioClient->GetMixFormat(&pFormat); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + UINT32 defaultPeriodInFrames; + UINT32 fundamentalPeriodInFrames; + UINT32 minPeriodInFrames; + UINT32 maxPeriodInFrames; + hr = pAudioClient->GetSharedModeEnginePeriod( + pFormat, + &defaultPeriodInFrames, + &fundamentalPeriodInFrames, + &minPeriodInFrames, + &maxPeriodInFrames); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + hr = pAudioClient->InitializeSharedAudioStream( + 0, + minPeriodInFrames, + pFormat, nullptr); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + hr = pAudioClient->Start(); + if (hr != S_OK) { + return std::unexpected(WindowsError()); + } + + return MinimumLatencyAudioClient(pAudioClient, pFormat); +} diff --git a/src/Windows/MinimumLatencyAudioClient.h b/src/Windows/MinimumLatencyAudioClient.h new file mode 100644 index 0000000..a9a6c43 --- /dev/null +++ b/src/Windows/MinimumLatencyAudioClient.h @@ -0,0 +1,39 @@ +#pragma once + +#include "WindowsError.h" + +#include + +namespace miniant::Windows::WasapiLatency { + +class MinimumLatencyAudioClient { +public: + struct Properties { + uint32_t defaultBufferSize; + uint32_t fundamentalBufferSize; + uint32_t minimumBufferSize; + uint32_t maximumBufferSize; + uint32_t sampleRate; + uint16_t bitsPerSample; + uint16_t numChannels; + }; + + MinimumLatencyAudioClient(MinimumLatencyAudioClient&& other) noexcept; + ~MinimumLatencyAudioClient(); + + MinimumLatencyAudioClient &operator= (MinimumLatencyAudioClient &&rhs) noexcept; + + std::expected GetProperties() const; + + static std::expected Start(); + +private: + void* m_pAudioClient; + void* m_pFormat; + + MinimumLatencyAudioClient(void* pAudioClient, void* pFormat); + + void Uninitialise(); +}; + +} diff --git a/src/Windows/WindowsError.cpp b/src/Windows/WindowsError.cpp new file mode 100644 index 0000000..9fbd124 --- /dev/null +++ b/src/Windows/WindowsError.cpp @@ -0,0 +1,13 @@ +#include "WindowsError.h" + +#include + +using namespace miniant::Windows; + +std::string GetErrorMessage() { + DWORD lastError = ::GetLastError(); + return "Last error: " + std::to_string(lastError); +} + +WindowsError::WindowsError(): + ExpectedError(GetErrorMessage()) {} diff --git a/src/Windows/WindowsError.h b/src/Windows/WindowsError.h new file mode 100644 index 0000000..51060a1 --- /dev/null +++ b/src/Windows/WindowsError.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ExpectedError.h" + +namespace miniant::Windows { + +class WindowsError : public ExpectedError { +public: + WindowsError(); + + WindowsError(std::string message) noexcept: + ExpectedError(std::move(message)) {} + + WindowsError(const char* message): + ExpectedError(message) {} +}; + +} diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 0549c2e..7556740 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -141,7 +141,10 @@ GetGameVersion () { case GameVersion::JPN08: case GameVersion::JPN39: case GameVersion::CHN00: break; - default: MessageBoxA (nullptr, "Unknown game version", nullptr, MB_OK); ExitProcess (0); + default: { + LogMessage (LogLevel::ERROR, "Detected GameVersion is Unsupported!"); + MessageBoxA (nullptr, "Unsupported game version", nullptr, MB_OK); ExitProcess (0); + } } } @@ -275,18 +278,13 @@ DllMain (HMODULE module, const DWORD reason, LPVOID reserved) { InitializeLogger (GetLogLevel (logLevelStr), logToFile, logPath); LogMessage (LogLevel::INFO, "Application started."); - if (version == "auto") { - GetGameVersion (); - } else if (version == "JPN00") { - gameVersion = GameVersion::JPN00; - } else if (version == "JPN08") { - gameVersion = GameVersion::JPN08; - } else if (version == "JPN39") { - gameVersion = GameVersion::JPN39; - } else if (version == "CHN00") { - gameVersion = GameVersion::CHN00; - } else { - LogMessage (LogLevel::ERROR, "GameVersion is UNKNOWN!"); + if (version == "auto") GetGameVersion (); + else if (version == "JPN00") gameVersion = GameVersion::JPN00; + else if (version == "JPN08") gameVersion = GameVersion::JPN08; + else if (version == "JPN39") gameVersion = GameVersion::JPN39; + else if (version == "CHN00") gameVersion = GameVersion::CHN00; + else { + LogMessage (LogLevel::ERROR, "Input GameVersion is UNKNOWN!"); MessageBoxA (nullptr, "Unknown patch version", nullptr, MB_OK); ExitProcess (0); } diff --git a/src/helpers.cpp b/src/helpers.cpp index e8deba5..0a05170 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -3,10 +3,7 @@ static void toml_myfree (void *p) { - if (p) { - const char *pp = static_cast (p); - delete[] pp; - } + if (p) free (p); } toml_table_t * diff --git a/src/patches/audio.cpp b/src/patches/audio.cpp index 89b9df0..00842ba 100644 --- a/src/patches/audio.cpp +++ b/src/patches/audio.cpp @@ -1,6 +1,7 @@ #include "constants.h" #include "helpers.h" #include "patches.h" +#include "../Windows/MinimumLatencyAudioClient.h" extern GameVersion gameVersion; @@ -18,6 +19,7 @@ typedef struct nusc_init_config { void *wasapi_audioSes; } nusc_init_config_t; +bool real = true; bool wasapiShared = true; bool asio = false; std::string asioDriver; @@ -56,6 +58,22 @@ SetVolumeRate (float rate) { volumeRate = rate; } +int +ReduceAudioLatency () { + // Start the low-latency audio client + if (const auto audioClient = miniant::Windows::WasapiLatency::MinimumLatencyAudioClient::Start (); !audioClient) { + LogMessage (LogLevel::INFO, "Unable to Reduce Audio Latency"); + return 1; // Exit with error + } else { + auto properties = audioClient->GetProperties (); + auto prop = properties.value(); + double currentDelay = prop.minimumBufferSize * 1000.0 / prop.sampleRate; + double defaultDelay = prop.defaultBufferSize * 1000.0 / prop.sampleRate; + LogMessage (LogLevel::INFO, "Audio latency: {:.2f}ms -> {:.2f}ms", defaultDelay, currentDelay); + } + return 0; +} + void Init () { LogMessage (LogLevel::INFO, "Init Audio patches"); @@ -64,12 +82,16 @@ Init () { const std::unique_ptr config_ptr (openConfig (configPath), toml_free); if (config_ptr) { if (const auto audio = openConfigSection (config_ptr.get (), "audio")) { + real = readConfigBool (audio, "real", real); wasapiShared = readConfigBool (audio, "wasapi_shared", wasapiShared); asio = readConfigBool (audio, "asio", asio); asioDriver = readConfigString (audio, "asio_driver", asioDriver); + } } + if (real) ReduceAudioLatency (); + switch (gameVersion) { case GameVersion::JPN00: { INSTALL_FAST_HOOK_DYNAMIC (NUSCDeviceInit, ASLR (0x140552160)); diff --git a/src/patches/language.cpp b/src/patches/language.cpp index 649f2cc..9a46dfe 100644 --- a/src/patches/language.cpp +++ b/src/patches/language.cpp @@ -114,7 +114,9 @@ Init () { languageData = calloc (languages.size () * languages.size () * 0x70 * 0x5, sizeof (u8)); switch (gameVersion) { - default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + default: break; + case GameVersion::JPN00: break; + case GameVersion::JPN08: break; case GameVersion::JPN39: { taiko_mapping = (Taiko_mapping)ASLR (0x140369B60); TestMode::RegisterModify ( @@ -167,6 +169,7 @@ Init () { } ); } break; + case GameVersion::CHN00: break; } } } @@ -370,11 +373,13 @@ bool titleExistSeason = true, titleExistMaid = true, titleExistMaidCn = true; std::string seasonNulm = "title_season.nulm", seasonNutexb = "title_" + season () + ".nutexb"; std::string maidNulmCn = "title_maid.nulm", maidNutexbCn = "title_maid.nutexb"; std::string maidNulm = "title_maid.nulm", maidNutexb = "title_maid.nutexb"; -TestMode::Value *titleScreen = TestMode::CreateValue (L"ModTitleScreen"); +TestMode::Value *theme = TestMode::CreateValue (L"ModTheme"); void Init () { switch (gameVersion) { - default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + default: break; + case GameVersion::JPN00: break; + case GameVersion::JPN08: break; case GameVersion::JPN39: { bool demoMovieExistAll = true; const char *movieToCheck[]{"movie\\attractdemo_cn_cn.wmv", "sound\\attractdemo_cn_cn.nus3bank"}; @@ -439,25 +444,25 @@ Init () { if (titleExistSeason || (titleExistMaid && titleExistMaidCn)) { TestMode::RegisterItem ( - L"", + L"", [&](){ LayeredFs::RegisterBefore ([&] (const std::string &originalFileName, const std::string ¤tFileName) -> std::string { if (currentFileName.starts_with ("F:\\lumen\\") || currentFileName.find ("title") == std::string::npos) return ""; if (currentFileName.ends_with ("\\title.nulm")) { - if (language == 4 && titleScreen->Read () == 1 && titleExistSeason) return seasonNulm; - if (language == 4 && titleScreen->Read () == 2 && titleExistMaidCn) return maidNulmCn; - if (titleScreen->Read () == 2 && titleExistMaid) return maidNulm; + if (language == 4 && theme->Read () == 1 && titleExistSeason) return seasonNulm; + if (language == 4 && theme->Read () == 2 && titleExistMaidCn) return maidNulmCn; + if (theme->Read () == 2 && titleExistMaid) return maidNulm; } else if (currentFileName.ends_with ("\\title.nutexb")) { LogMessage (LogLevel::DEBUG, "nutexb"); - if (language == 4 && titleScreen->Read () == 1 && titleExistSeason) return seasonNutexb; - if (language == 4 && titleScreen->Read () == 2 && titleExistMaidCn) return maidNutexbCn; - if (titleScreen->Read () == 2 && titleExistMaid) return maidNutexb; + if (language == 4 && theme->Read () == 1 && titleExistSeason) return seasonNutexb; + if (language == 4 && theme->Read () == 2 && titleExistMaidCn) return maidNutexbCn; + if (theme->Read () == 2 && titleExistMaid) return maidNutexb; } return ""; }); LayeredFs::RegisterBefore ([&] (const std::string &originalFileName, const std::string ¤tFileName) -> std::string { - if (titleScreen->Read () != 2 || currentFileName.starts_with ("F:\\lumen\\") || currentFileName.find ("\\lumen\\") == std::string::npos) return ""; + if (theme->Read () != 2 || currentFileName.starts_with ("F:\\lumen\\") || currentFileName.find ("\\lumen\\") == std::string::npos) return ""; std::string fileName = std::string (currentFileName); fileName = replace (fileName, "\\lumen\\", "\\lumen_maid\\"); if (std::filesystem::exists (fileName)) return fileName; @@ -466,6 +471,7 @@ Init () { }); } } break; + case GameVersion::CHN00: break; } } } @@ -657,7 +663,9 @@ CnFontPatches () { void Init () { switch (gameVersion) { - default: case GameVersion::JPN00: case GameVersion::JPN08: case GameVersion::CHN00: break; + default: break; + case GameVersion::JPN00: break; + case GameVersion::JPN08: break; case GameVersion::JPN39: { languageFix = [](int lang){ return lang == 4 ? 2 : lang; }; taiko_string = (Taiko_string)ASLR (0x1400203B0); @@ -670,6 +678,7 @@ Init () { Language::Lumen::Init (); Language::Voice::Init (); } break; + case GameVersion::CHN00: break; } } } \ No newline at end of file diff --git a/src/patches/patches.h b/src/patches/patches.h index 659d3e6..419f7f7 100644 --- a/src/patches/patches.h +++ b/src/patches/patches.h @@ -133,4 +133,16 @@ void Init (); namespace UnlimitSong { void Init (); } // namespace UnlimitSong +namespace Wordlist { +typedef u64 (*RefDataTableManager) (u64); +typedef struct { + std::string key; + std::string text; + int fontType; +} WordInfo; +void Init (); +void SetupAccessor (u64 appAccessor, RefDataTableManager refDataTableManager); +WordInfo *GetWordInfo (std::string &key); +void SetWordInfo (WordInfo *wordInfo); +} } // namespace patches diff --git a/src/patches/testmode.cpp b/src/patches/testmode.cpp index e3c2cd4..9aa7502 100644 --- a/src/patches/testmode.cpp +++ b/src/patches/testmode.cpp @@ -242,10 +242,8 @@ std::vector registeredModifies = {}; 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"; @@ -268,20 +266,30 @@ FAST_HOOK_DYNAMIC (void, SceneFirstInitialize, u64 a1, u64 a2, u64 a3) { originalSceneFirstInitialize.fastcall (a1, a2, a3); } +std::wstring +replaceFileName (std::wstring fullFileName, std::wstring usingLastFileName) { + size_t lastPosition = fullFileName.find_last_of (L"/"); + if (lastPosition == -1) return usingLastFileName; + else return fullFileName.substr(0, lastPosition) + usingLastFileName; +} + FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t *lFileName) { auto start = std::chrono::high_resolution_clock::now(); const auto originalFileName = std::wstring (lFileName); LogMessage (LogLevel::DEBUG, L"Begin Loading {}", originalFileName); std::wstring fileName = originalFileName; - if (fileName.ends_with (originalDeviceInitialize)) { + if (fileName.find (L"/DeviceInitialize") != -1) { if (moddedInitial == L"") { - fileName = replace (originalFileName, originalDeviceInitialize, usingDeviceInitialize); + if (usingDeviceInitialize != L"") { + fileName = replaceFileName (originalFileName, usingDeviceInitialize); + } else fileName = originalFileName; + LogMessage (LogLevel::DEBUG, L"final path: {}", fileName); if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) { LogMessage (LogLevel::ERROR, L"Loading DeviceInitialize structure failed! path: " + fileName); moddedInitial = originalFileName; fileName = originalFileName; } else { - const std::wstring modFileName = replace (originalFileName, originalDeviceInitialize, moddedDeviceInitialize); + const std::wstring modFileName = replaceFileName (originalFileName, 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); @@ -295,16 +303,19 @@ FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t fileName = modFileName; } } else fileName = moddedInitial; - } else if (fileName.ends_with (originalTestMode)) { + } else if (fileName.find (L"/TestMode") != -1) { if (modded == L"") { if (!((RegisteredMenu*)modManager)->items.empty () || registeredSingleItems.empty () || !registeredModifies.empty ()) { - fileName = replace (originalFileName, originalTestMode, usingTestMode); + if (usingTestMode != L"") { + fileName = replaceFileName (originalFileName, usingTestMode); + } else fileName = originalFileName; + LogMessage (LogLevel::DEBUG, L"final path: {}", fileName); if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) { LogMessage (LogLevel::ERROR, L"Loading TestMode structure failed! path: " + fileName); modded = originalFileName; fileName = originalFileName; } else { - const std::wstring modFileName = replace (originalFileName, originalTestMode, moddedTestMode); + const std::wstring modFileName = replaceFileName (originalFileName, moddedTestMode); if (!((RegisteredMenu*)modManager)->items.empty ()) { LogMessage (LogLevel::DEBUG, "Begin generate Mod Manager menu"); pugi::xpath_query menuQuery @@ -347,124 +358,90 @@ CommonModify () { [&] (const pugi::xml_node &node) { node.attribute (L"default").set_value (L"0"); }, [&] {}); } -void -LocalizationCHT () { - TestMode::RegisterModify( - L"/root/menu[@id='TopMenu']/layout[@type='Center']/menu-item[@menu='ModManagerMenu']", - [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"模組管理"); }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Header']/text-item", - [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"模組管理"); }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModFixLanguage']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"修復語言"); - node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModUnlockSongs']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"解鎖歌曲"); - node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓, 2:強制"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModFreezeTimer']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"凍結計時"); - node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo024']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"鬼滅之刃模式"); - node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo025']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"航海王模式"); - node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo026']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"AI粗品模式"); - node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeAprilFool001']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"青春之達人模式"); - node.attribute(L"replace-text").set_value(L"0:黙認, 1:啓用, 2:僅登入"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModInstantResult']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"即時保存"); - node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓"); - }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/menu-item[@menu='TopMenu']", - [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"離開"); }, [](){} - ); - TestMode::RegisterModify( - L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='AttractDemoItem']", - [&](pugi::xml_node &node) { - node.attribute(L"label").set_value(L"演示遊玩影片"); - node.attribute(L"replace-text").set_value(L"0:關閉, 1:開啓"); - }, [](){} - ); -} - void LocalizationCHS () { - RegisterModify ( + TestMode::RegisterModify ( L"/root/menu[@id='TopMenu']/layout[@type='Center']/menu-item[@menu='ModManagerMenu']", - [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模组管理"); }, [] {}); - RegisterModify ( + [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模组管理"); }, [](){}); + TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Header']/text-item", - [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模组管理"); }, [] {}); - RegisterModify ( + [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模组管理"); }, [](){}); + TestMode::RegisterModify ( + L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModUnlockSongs']", + [&] (const pugi::xml_node &node) { + node.attribute (L"label").set_value (L"解锁歌曲"); + node.attribute (L"replace-text").set_value (L"0:禁用, 1:启用, 2:强制"); + }, [](){}); + TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModFreezeTimer']", [&] (const pugi::xml_node &node) { - node.attribute (L"label").set_value (L"冻结计时"); + node.attribute (L"label").set_value (L"冻结计时器"); node.attribute (L"replace-text").set_value (L"0:禁用, 1:启用"); - }, - [] {}); - RegisterModify ( + }, [](){}); + TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo024']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"鬼灭之刃模式"); node.attribute (L"replace-text").set_value (L"0:默认, 1:启用, 2:仅刷卡"); - }, - [] {}); - RegisterModify ( + }, [](){}); + TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo025']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"航海王模式"); node.attribute (L"replace-text").set_value (L"0:默认, 1:启用, 2:仅刷卡"); - }, - [] {}); - RegisterModify ( + }, [](){}); + TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo026']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"AI粗品模式"); node.attribute (L"replace-text").set_value (L"0:默认, 1:启用, 2:仅刷卡"); - }, - [] {}); - RegisterModify ( + }, [](){}); + TestMode::RegisterModify( + L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeAprilFool001']", + [&](pugi::xml_node &node) { + node.attribute(L"label").set_value(L"青春之达人模式"); + node.attribute(L"replace-text").set_value(L"0:默认, 1:启用, 2:仅刷卡"); + }, [](){}); + TestMode::RegisterModify ( + L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModInstantResult']", + [&] (const pugi::xml_node &node) { + node.attribute (L"label").set_value (L"即时上传成绩"); + node.attribute (L"replace-text").set_value (L"0:禁用, 1:启用"); + }, [](){}); + TestMode::RegisterModify ( + L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModTheme']", + [&] (const pugi::xml_node &node) { + node.attribute (L"label").set_value (L"界面主题"); + node.attribute (L"replace-text").set_value (L"0:默认, 1:四季(仅中文), 2:秋叶原女仆"); + }, [](){}); + TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/menu-item[@menu='TopMenu']", - [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"离开"); }, [] {}); + [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"离开"); }, [](){}); + TestMode::RegisterModify( + L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='AttractDemoItem']", + [&](pugi::xml_node &node) { + node.attribute(L"label").set_value(L"演示游玩影片"); + node.attribute(L"replace-text").set_value(L"0:关闭, 1:开启"); + }, [](){}); + TestMode::RegisterModify( + L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']", + [&](pugi::xml_node &node) { + node.attribute(L"label").set_value(L"文本语言"); + const wchar_t *vals = node.attribute(L"replace-text").value (); + std::wstring langaugeReplace = std::wstring (vals); + langaugeReplace = replace (langaugeReplace, L"JPN", L"日语"); + langaugeReplace = replace (langaugeReplace, L"ENG", L"英语"); + langaugeReplace = replace (langaugeReplace, L"zh-tw", L"繁体中文"); + langaugeReplace = replace (langaugeReplace, L"KOR", L"韩语"); + langaugeReplace = replace (langaugeReplace, L"zh-cn", L"简体中文"); + node.attribute(L"replace-text").set_value(langaugeReplace.c_str ()); + }, [](){}); + TestMode::RegisterModify( + L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='VoiceLanguageItem']", + [&](pugi::xml_node &node) { + node.attribute(L"label").set_value(L"语音语言"); + node.attribute(L"replace-text").set_value(L"0:日语, 1:中文"); + }, [](){}); } void @@ -478,14 +455,12 @@ Init () { for (auto method : hooks) method (); switch (gameVersion) { - case GameVersion::UNKNOWN: break; case GameVersion::JPN00: break; case GameVersion::JPN08: break; case GameVersion::JPN39: { 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) @@ -496,13 +471,13 @@ Init () { 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; + LocalizationCHS (); + } } break; case GameVersion::CHN00: break; } diff --git a/src/patches/versions/JPN39.cpp b/src/patches/versions/JPN39.cpp index 8366b8d..589ab5f 100644 --- a/src/patches/versions/JPN39.cpp +++ b/src/patches/versions/JPN39.cpp @@ -301,9 +301,10 @@ MID_HOOK (AttractDemo, ASLR (0x14045A720), SafetyHookContext &ctx) { FAST_HOOK (DWORD*, AcquireMostCompatibleDisplayMode, ASLR (0x14064C870), i64 a1, DWORD *a2, DWORD *a3) { LogMessage (LogLevel::DEBUG, "AcquireMostCompatibleDisplayMode {:d} {:d} {:f} {:f}", a3[0], a3[1], (float)(int)a3[2], (float)(int)a3[3]); + a3[2] = (DWORD)(float)120.0; // a3[0] = xRes; // a3[1] = yRes; - // LogMessage (LogLevel::DEBUG, "AcquireMostCompatibleDisplayMode {:d} {:d} {:f} {:f}", a3[0], a3[1], (float)(int)a3[2], (float)(int)a3[3]); + LogMessage (LogLevel::DEBUG, "AcquireMostCompatibleDisplayMode {:d} {:d} {:f} {:f}", a3[0], a3[1], (float)(int)a3[2], (float)(int)a3[3]); return originalAcquireMostCompatibleDisplayMode.fastcall (a1, a2, a3); } @@ -363,23 +364,6 @@ Init () { WRITE_MEMORY (ASLR (0x1404F3D5B), i32, donModelX); WRITE_MEMORY (ASLR (0x1404F3D62), i32, donModelY); } - // // DonSystem::Renderer::Renderer - // WRITE_MEMORY (ASLR (0x1404FBF33), i32, xRes); - // WRITE_MEMORY (ASLR (0x1404FBF3A), i32, yRes); - // // DonSystem::Renderer::CreateOnpuPass - // WRITE_MEMORY (ASLR (0x1404FCF77), i32, xRes); - // WRITE_MEMORY (ASLR (0x1404FCF81), i32, yRes / 2); - // // nu::LightManager::LightManager - // WRITE_MEMORY (ASLR (0x1406BED7A), i32, xRes); - // WRITE_MEMORY (ASLR (0x1406BED82), i32, yRes); - // WRITE_MEMORY (ASLR (0x1406BEF26), i32, xRes); - // WRITE_MEMORY (ASLR (0x1406BEF33), i32, yRes); - // // google::protobuf::TextFormat::Parser::ParserImpl::ConsumeField - // WRITE_MEMORY (ASLR (0x1406F7E47), i32, xRes); - // // google::protobuf::DescriptorBuilder::AddSymbol - // WRITE_MEMORY (ASLR (0x140719191), i32, xRes); - // nuscDecoderCalcObjectSize - // WRITE_MEMORY (ASLR (0x140719191), i32, xRes); if (!vsync) WRITE_MEMORY (ASLR (0x14064C7E9), u8, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x90); diff --git a/src/patches/wordlist.cpp b/src/patches/wordlist.cpp new file mode 100644 index 0000000..e7591a2 --- /dev/null +++ b/src/patches/wordlist.cpp @@ -0,0 +1,60 @@ +#include "constants.h" +#include "helpers.h" +#include "patches.h" + +extern GameVersion gameVersion; + +namespace patches::Wordlist { +u64 GetWordInfoAddress; +u64 AddWordInfoAddress; + +u64 appAccessor; +RefDataTableManager refDataTableManager; + +void +Init () { + switch (gameVersion) { + case GameVersion::JPN00: break; + case GameVersion::JPN08: break; + case GameVersion::JPN39: { + GetWordInfoAddress = ASLR (0x1400AA640); + AddWordInfoAddress = ASLR (0x1400AAA20); + } break; + case GameVersion::CHN00: break; + } +} +void +SetupAccessor (u64 appAccessor, RefDataTableManager refDataTableManager) { + patches::Wordlist::refDataTableManager = refDataTableManager; + patches::Wordlist::appAccessor = appAccessor; +} +WordInfo * +GetWordInfo (std::string &key) { + WordInfo *wordInfo = (WordInfo *)malloc (sizeof(WordInfo)); + wordInfo->key = ""; + wordInfo->text = ""; + wordInfo->fontType = 0; + if (refDataTableManager != nullptr && appAccessor != 0) { + u64 dataTableManager = refDataTableManager (appAccessor); + if (dataTableManager) { + u64 wordInfoAccessor = *(u64*)(dataTableManager + 32); + if (wordInfoAccessor) { + ((char (__fastcall *)(u64, std::string *, WordInfo *))(GetWordInfoAddress))(wordInfoAccessor, &key, wordInfo); + } + } + } + return wordInfo; +} +void +SetWordInfo (WordInfo *wordInfo) { + if (wordInfo != nullptr && refDataTableManager != nullptr && appAccessor != 0) { + u64 dataTableManager = refDataTableManager (appAccessor); + if (dataTableManager) { + u64 wordInfoAccessor = *(u64*)(dataTableManager + 32); + if (wordInfoAccessor) { + ((u64 (__fastcall *)(u64, WordInfo *))(AddWordInfoAddress))(wordInfoAccessor, wordInfo); + } + } + } +} +} \ No newline at end of file diff --git a/src/pollasync.cpp b/src/pollasync.cpp index 17e1e54..4b7bed2 100644 --- a/src/pollasync.cpp +++ b/src/pollasync.cpp @@ -13,6 +13,7 @@ extern bool globalKeyboardInput; extern bool autoIme; extern HKL currentLayout; extern bool emulateUsio; +extern bool globalKeyboard; bool wndForeground = false; bool usingKeyboard = false; @@ -126,7 +127,7 @@ HHOOK mouseHook; LRESULT CALLBACK InputProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION) { switch (wParam) { - case WM_KEYDOWN: if (wndForeground) { + case WM_KEYDOWN: if (globalKeyboard || wndForeground) { auto* pKeyboard = reinterpret_cast(lParam); DWORD keyCode = pKeyboard->vkCode; if (!currentKeyboardState[keyCode]) keyboardCount[keyCode] = std::min (keyboardCount[keyCode] + 1, maxCount); diff --git a/src/polldef.h b/src/polldef.h index 0b1ccce..e09a618 100644 --- a/src/polldef.h +++ b/src/polldef.h @@ -1,3 +1,4 @@ +#pragma once #include "poll.h" struct KeyCodePair {