Merge pull request #25 from ryumiyax/experimental

Several fix & optimize
This commit is contained in:
RyuMiyaX
2025-01-23 01:32:15 +08:00
committed by GitHub
18 changed files with 477 additions and 163 deletions
+2
View File
@@ -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
+10 -2
View File
@@ -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
+3 -1
View File
@@ -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]
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <string>
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;
};
}
+143
View File
@@ -0,0 +1,143 @@
#include "MinimumLatencyAudioClient.h"
#include <Audioclient.h>
#include <mmdeviceapi.h>
#include <cassert>
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<IAudioClient3*>(m_pAudioClient)->Release();
m_pAudioClient = nullptr;
CoTaskMemFree(m_pFormat);
m_pFormat = nullptr;
}
std::expected<MinimumLatencyAudioClient::Properties, WindowsError> MinimumLatencyAudioClient::GetProperties() const {
Properties properties;
HRESULT hr = static_cast<IAudioClient3*>(m_pAudioClient)->GetSharedModeEnginePeriod(
static_cast<WAVEFORMATEX*>(m_pFormat),
&properties.defaultBufferSize,
&properties.fundamentalBufferSize,
&properties.minimumBufferSize,
&properties.maximumBufferSize);
if (hr != S_OK) {
return std::unexpected(WindowsError());
}
properties.sampleRate = static_cast<WAVEFORMATEX*>(m_pFormat)->nSamplesPerSec;
properties.bitsPerSample = static_cast<WAVEFORMATEX*>(m_pFormat)->wBitsPerSample;
properties.numChannels = static_cast<WAVEFORMATEX*>(m_pFormat)->nChannels;
return properties;
}
std::expected<MinimumLatencyAudioClient, WindowsError> 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<void**>(&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<void**>(&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);
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include "WindowsError.h"
#include <expected>
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<Properties, WindowsError> GetProperties() const;
static std::expected<MinimumLatencyAudioClient, WindowsError> Start();
private:
void* m_pAudioClient;
void* m_pFormat;
MinimumLatencyAudioClient(void* pAudioClient, void* pFormat);
void Uninitialise();
};
}
+13
View File
@@ -0,0 +1,13 @@
#include "WindowsError.h"
#include <Windows.h>
using namespace miniant::Windows;
std::string GetErrorMessage() {
DWORD lastError = ::GetLastError();
return "Last error: " + std::to_string(lastError);
}
WindowsError::WindowsError():
ExpectedError(GetErrorMessage()) {}
+18
View File
@@ -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) {}
};
}
+11 -13
View File
@@ -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);
}
+1 -4
View File
@@ -3,10 +3,7 @@
static void
toml_myfree (void *p) {
if (p) {
const char *pp = static_cast<char *> (p);
delete[] pp;
}
if (p) free (p);
}
toml_table_t *
+22
View File
@@ -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<toml_table_t, void (*) (toml_table_t *)> 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));
+22 -13
View File
@@ -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"<select-item label=\"TITLE SCREEN(NEED FILES)\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:SEASON(CHN), "
L"2:MAID\" group=\"Setting\" id=\"ModTitleScreen\" max=\"2\" min=\"0\" default=\"0\"/>",
L"<select-item label=\"THEME\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:SEASON(CHN), "
L"2:MAID\" group=\"Setting\" id=\"ModTheme\" max=\"2\" min=\"0\" default=\"0\"/>",
[&](){
LayeredFs::RegisterBefore ([&] (const std::string &originalFileName, const std::string &currentFileName) -> 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 &currentFileName) -> 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;
}
}
}
+12
View File
@@ -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
+86 -111
View File
@@ -242,10 +242,8 @@ std::vector<RegisteredModify *> 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<char *> (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;
}
+2 -18
View File
@@ -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<DWORD *> (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);
+60
View File
@@ -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);
}
}
}
}
}
+2 -1
View File
@@ -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<KBDLLHOOKSTRUCT*>(lParam);
DWORD keyCode = pKeyboard->vkCode;
if (!currentKeyboardState[keyCode]) keyboardCount[keyCode] = std::min (keyboardCount[keyCode] + 1, maxCount);
+1
View File
@@ -1,3 +1,4 @@
#pragma once
#include "poll.h"
struct KeyCodePair {