Add multiple new feature & update config/readme
累积更新 文档与配置更新 去掉多余的配置项读取
This commit is contained in:
parent
fb2c5c117d
commit
3933ecab00
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,6 +11,7 @@ subprojects/safetyhook
|
||||
subprojects/zxing-cpp-2.2.1
|
||||
subprojects/stb
|
||||
subprojects/zlib
|
||||
subprojects/pugixml-1.14
|
||||
libtomcrypt-1.18.2
|
||||
dist.7z
|
||||
.vscode
|
||||
|
3
Makefile
3
Makefile
@ -5,6 +5,9 @@ all:
|
||||
@strip build/bnusio.dll
|
||||
|
||||
setup:
|
||||
@meson setup build --cross cross-mingw-64.txt
|
||||
|
||||
clean-setup:
|
||||
@meson setup --wipe build --cross cross-mingw-64.txt
|
||||
|
||||
clean:
|
||||
|
12
PLUGINS.md
12
PLUGINS.md
@ -2,30 +2,30 @@
|
||||
|
||||
Plugins are just libraries with certain exported functions that go in the plugins folder.
|
||||
|
||||
```
|
||||
```c++
|
||||
void Init()
|
||||
```
|
||||
Runs on bngrw_Init, may be a bit late for some things but should generally allow functions which would cause loader locks to run fine if a bit late.
|
||||
```
|
||||
```c++
|
||||
void Exit()
|
||||
```
|
||||
Runs on bnusio_Close, dispose of any data here.
|
||||
```
|
||||
```c++
|
||||
void Update()
|
||||
```
|
||||
Runs once per frame.
|
||||
```
|
||||
```c++
|
||||
void WaitTouch(i32 (*callback) (i32, i32, u8[168], u64), u64 data)
|
||||
```
|
||||
Runs on bngrw_reqWaitTouch. Call the callback like so when you wish to have a card scanned.
|
||||
```
|
||||
```c++
|
||||
u8 cardData[168]= { 0x01, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x2E, 0x58, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x5C, 0x97, 0x44, 0xF0, 0x88, 0x04, 0x00, 0x43, 0x26, 0x2C, 0x33, 0x00, 0x04, 0x06, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36, 0x00, 0x00, 0xFA, 0xE9, 0x69, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
memcpy (cardData + 0x2C, chipId, 33);
|
||||
memcpy (cardData + 0x50, accessCode, 21);
|
||||
callback(0, 0, cardData, data);
|
||||
```
|
||||
|
||||
```
|
||||
```c++
|
||||
void CardInsert()
|
||||
```
|
||||
Runs when user presses CARD_INSERT, causes TAL to not insert a card if any plugins have this present
|
||||
|
30
README.md
30
README.md
@ -54,18 +54,9 @@ unlock_songs = true
|
||||
[patches.jpn39]
|
||||
# sync test mode language to attract etc
|
||||
fix_language = false
|
||||
# stop timer count down
|
||||
freeze_timer = false
|
||||
# use cn font and chineseS wordlist value
|
||||
chs_patch = false
|
||||
# send result per song
|
||||
instant_result = false
|
||||
# enable one piece collab mode
|
||||
mode_collabo025 = false
|
||||
# enable ai soshina mode
|
||||
mode_collabo026 = false
|
||||
# enable aoharu no tatsujin mode
|
||||
mode_aprilfool001 = false
|
||||
# more options is now moved to testmode
|
||||
|
||||
[emulation]
|
||||
# If usio emulation is disabled, you need to place bnusio_original.dll (unmodified bnusio.dll) in the executable folder.
|
||||
@ -135,6 +126,21 @@ datatable_key = ""
|
||||
fumen_key = ""
|
||||
```
|
||||
|
||||
## TestMode options (JPN39 only)
|
||||
|
||||
TaikoArcadeLoader offers several patches to select in TestMode
|
||||
|
||||
The follow options are available in "MOD MANAGER" menu:
|
||||
- FREEZE TIMER (stop timer count down)
|
||||
- KIMETSU MODE (enable collabo024, will show a blank title)
|
||||
- ONE PIECE MODE (enable collabo025)
|
||||
- AI SOSHINA MODE (enable collabo026)
|
||||
- AOHARU MODE (enable aprilfool001)
|
||||
- INSTANT RESULT (send result per song)
|
||||
|
||||
Enhanced original option:
|
||||
- Louder volume (Speaker Volume is now up to 300%, **WARNING: May damage your speakers**)
|
||||
|
||||
## Building
|
||||
|
||||
TaikoArcadeLoader can be a bit tricky to build if you've never done it before.
|
||||
@ -147,8 +153,8 @@ pip3 install meson
|
||||
npm install n -g && n latest && npm install --global xpm@latest
|
||||
npx xpm init && npx xpm install @xpack-dev-tools/mingw-w64-gcc@latest
|
||||
|
||||
# make sure to edit "path_to_tal" with the actual TaikoArcadeLoader folder path
|
||||
export PATH=/path_to_tal/xpacks/.bin:$PATH
|
||||
# make sure you entered TaikoArcadeLoader folder
|
||||
export PATH=`pwd`/xpacks/.bin:$PATH
|
||||
make setup
|
||||
```
|
||||
|
||||
|
5
dist/config.toml
vendored
5
dist/config.toml
vendored
@ -18,12 +18,7 @@ unlock_songs = true
|
||||
|
||||
[patches.jpn39]
|
||||
fix_language = false
|
||||
freeze_timer = false
|
||||
chs_patch = false
|
||||
instant_result = false
|
||||
mode_collabo025 = false
|
||||
mode_collabo026 = false
|
||||
mode_aprilfool001 = false
|
||||
|
||||
[emulation]
|
||||
usio = true
|
||||
|
8
dist/keyconfig.toml
vendored
8
dist/keyconfig.toml
vendored
@ -16,10 +16,10 @@ P1_LEFT_BLUE = ["D", "SDL_LTRIGGER"]
|
||||
P1_LEFT_RED = ["F", "SDL_LSTICK_PRESS"]
|
||||
P1_RIGHT_RED = ["J", "SDL_RSTICK_PRESS"]
|
||||
P1_RIGHT_BLUE = ["K", "SDL_RTRIGGER"]
|
||||
P2_LEFT_BLUE = []
|
||||
P2_LEFT_RED = []
|
||||
P2_RIGHT_RED = []
|
||||
P2_RIGHT_BLUE = []
|
||||
P2_LEFT_BLUE = ["Z"]
|
||||
P2_LEFT_RED = ["X"]
|
||||
P2_RIGHT_RED = ["C"]
|
||||
P2_RIGHT_BLUE = ["V"]
|
||||
|
||||
# ESCAPE F1 through F12
|
||||
# ` 1 through 0 -= BACKSPACE ^ YEN
|
||||
|
@ -43,6 +43,11 @@ zlib_proj = subproject('zlib')
|
||||
zlib_dep = zlib_proj.get_variable('zlib_dep')
|
||||
libtomcrypt = subproject('libtomcrypt')
|
||||
libtomcrypt_dep = libtomcrypt.get_variable('tomcrypt_dep')
|
||||
opt_var = cmake.subproject_options()
|
||||
opt_var.set_override_option('cpp_std', 'c++23')
|
||||
opt_var.add_cmake_defines({'PUGIXML_WCHAR_MODE': true})
|
||||
pugixml = cmake.subproject('pugixml', options: opt_var)
|
||||
pugixml_dep = pugixml.get_variable('pugixml_static_dep')
|
||||
|
||||
library(
|
||||
'bnusio',
|
||||
@ -69,6 +74,7 @@ library(
|
||||
zydis_dep,
|
||||
zlib_dep,
|
||||
libtomcrypt_dep,
|
||||
pugixml_dep,
|
||||
],
|
||||
sources : [
|
||||
'src/dllmain.cpp',
|
||||
@ -81,6 +87,7 @@ library(
|
||||
'src/patches/audio.cpp',
|
||||
'src/patches/qr.cpp',
|
||||
'src/patches/layeredfs.cpp',
|
||||
'src/patches/testmode.cpp',
|
||||
'src/patches/versions/JPN00.cpp',
|
||||
'src/patches/versions/JPN08.cpp',
|
||||
'src/patches/versions/JPN39.cpp',
|
||||
|
@ -47,6 +47,7 @@ Keybindings P2_RIGHT_BLUE = {};
|
||||
|
||||
bool testEnabled = false;
|
||||
int coin_count = 0;
|
||||
int service_count = 0;
|
||||
bool inited = false;
|
||||
HWND windowHandle = nullptr;
|
||||
HKL currentLayout;
|
||||
@ -92,7 +93,6 @@ RETURN_FALSE (i64, bnusio_SetHopperLimit, u16 a1, i16 a2);
|
||||
RETURN_FALSE (i64, bnusio_SramRead, i32 a1, u8 a2, i32 a3, u16 a4);
|
||||
RETURN_FALSE (i64, bnusio_SramWrite, i32 a1, u8 a2, i32 a3, u16 a4);
|
||||
RETURN_FALSE (void *, bnusio_GetCoinError, i32 a1);
|
||||
RETURN_FALSE (void *, bnusio_GetService, i32 a1);
|
||||
RETURN_FALSE (void *, bnusio_GetServiceError, i32 a1);
|
||||
RETURN_FALSE (i64, bnusio_DecCoin, i32 a1, u16 a2);
|
||||
RETURN_FALSE (i64, bnusio_DecService, i32 a1, u16 a2);
|
||||
@ -183,7 +183,8 @@ bnusio_GetAnalogIn (u8 which) {
|
||||
}
|
||||
}
|
||||
|
||||
u16 bnusio_GetCoin (i32 a1) { return coin_count; }
|
||||
u16 __fastcall bnusio_GetCoin (i32 a1) { return coin_count; }
|
||||
u16 __fastcall bnusio_GetService (i32 a1) { return service_count; }
|
||||
}
|
||||
|
||||
FUNCTION_PTR (i64, bnusio_Open_Original, PROC_ADDRESS ("bnusio_original.dll", "bnusio_Open"));
|
||||
@ -417,6 +418,7 @@ Update () {
|
||||
|
||||
UpdatePoll (windowHandle);
|
||||
if (IsButtonTapped (COIN_ADD) && !testEnabled) coin_count++;
|
||||
if (IsButtonTapped (SERVICE) && !testEnabled) service_count++;
|
||||
if (IsButtonTapped (TEST)) testEnabled = !testEnabled;
|
||||
if (IsButtonTapped (EXIT)) ExitProcess (0);
|
||||
if (waitingForTouch) {
|
||||
|
@ -28,8 +28,6 @@ bool useLayeredFs = false;
|
||||
bool emulateUsio = true;
|
||||
bool emulateCardReader = true;
|
||||
bool emulateQr = true;
|
||||
std::string datatableKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
std::string fumenKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
HWND hGameWnd;
|
||||
HOOK (i32, ShowMouse, PROC_ADDRESS ("user32.dll", "ShowCursor"), bool) { return originalShowMouse.call<i32> (true); }
|
||||
@ -180,12 +178,6 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
autoIme = readConfigBool (keyboard, "auto_ime", autoIme);
|
||||
jpLayout = readConfigBool (keyboard, "jp_layout", jpLayout);
|
||||
}
|
||||
auto layeredFs = openConfigSection (config, "layeredfs");
|
||||
if (layeredFs) {
|
||||
useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
|
||||
datatableKey = readConfigString (layeredFs, "datatable_key", datatableKey);
|
||||
fumenKey = readConfigString (layeredFs, "fumen_key", fumenKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (version == "auto") {
|
||||
@ -260,7 +252,8 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
patches::Audio::Init ();
|
||||
patches::Dxgi::Init ();
|
||||
patches::AmAuth::Init ();
|
||||
if (useLayeredFs) patches::LayeredFs::Init ();
|
||||
patches::LayeredFs::Init ();
|
||||
patches::TestMode::Init ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -108,4 +108,40 @@ printColour (int colour, const char *format, ...) {
|
||||
SetConsoleTextAttribute (consoleHandle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
|
||||
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
std::wstring
|
||||
replace (const std::wstring orignStr, const std::wstring oldStr, const std::wstring newStr) {
|
||||
size_t pos = 0;
|
||||
std::wstring tempStr = orignStr;
|
||||
std::wstring::size_type newStrLen = newStr.length();
|
||||
std::wstring::size_type oldStrLen = oldStr.length();
|
||||
while(true)
|
||||
{
|
||||
pos = tempStr.find(oldStr, pos);
|
||||
if (pos == std::wstring::npos) break;
|
||||
|
||||
tempStr.replace(pos, oldStrLen, newStr);
|
||||
pos += newStrLen;
|
||||
}
|
||||
|
||||
return tempStr;
|
||||
}
|
||||
|
||||
std::string
|
||||
replace (const std::string orignStr, const std::string oldStr, const std::string newStr) {
|
||||
size_t pos = 0;
|
||||
std::string tempStr = orignStr;
|
||||
std::string::size_type newStrLen = newStr.length();
|
||||
std::string::size_type oldStrLen = oldStr.length();
|
||||
while(true)
|
||||
{
|
||||
pos = tempStr.find(oldStr, pos);
|
||||
if (pos == std::string::npos) break;
|
||||
|
||||
tempStr.replace(pos, oldStrLen, newStr);
|
||||
pos += newStrLen;
|
||||
}
|
||||
|
||||
return tempStr;
|
||||
}
|
@ -140,4 +140,6 @@ int64_t readConfigInt (toml_table_t *table, const std::string &key, int64_t notF
|
||||
const std::string readConfigString (toml_table_t *table, const std::string &key, const std::string ¬FoundValue);
|
||||
std::vector<int64_t> readConfigIntArray (toml_table_t *table, const std::string &key, std::vector<int64_t> notFoundValue);
|
||||
void printColour (int colour, const char *format, ...);
|
||||
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::vector<SafetyHookInline> directHooks = {};
|
@ -2,12 +2,23 @@
|
||||
#include <tomcrypt.h>
|
||||
#include <zlib.h>
|
||||
|
||||
extern std::string datatableKey;
|
||||
extern std::string fumenKey;
|
||||
bool useLayeredFs = false;
|
||||
std::string datatableKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
std::string fumenKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
#define CRCPOLY 0x82F63B78
|
||||
|
||||
namespace patches::LayeredFs {
|
||||
class RegisteredHandler {
|
||||
public:
|
||||
std::function<std::string(std::string, std::string)> handlerMethod;
|
||||
RegisteredHandler (const std::function<std::string(std::string, std::string)> &handlerMethod) {
|
||||
this->handlerMethod = handlerMethod;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<RegisteredHandler *> beforeHandlers = {};
|
||||
std::vector<RegisteredHandler *> afterHandlers = {};
|
||||
|
||||
uint32_t
|
||||
CRC32C (uint32_t crc, const unsigned char *buf, size_t len) {
|
||||
@ -167,9 +178,9 @@ IsFumenEncrypted (const std::string &filename) {
|
||||
return buffer != expected_bytes;
|
||||
}
|
||||
|
||||
HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
std::filesystem::path path (lpFileName);
|
||||
std::string
|
||||
LayeredFsHandler (const std::string originalFileName, const std::string currentFileName) {
|
||||
std::filesystem::path path (originalFileName.c_str());
|
||||
if (!path.is_absolute ()) path = std::filesystem::absolute (path);
|
||||
auto originalDataFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data" / "x64";
|
||||
auto originalLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64";
|
||||
@ -187,8 +198,7 @@ HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPC
|
||||
if (std::filesystem::exists (newPath)) { // If a file exists in the datamod folder
|
||||
if (IsFumenEncrypted (newPath)) { // And if it's an encrypted fumen or a different type of file, use it.
|
||||
std::cout << "Redirecting " << std::filesystem::relative (path).string () << std::endl;
|
||||
return originalCreateFileAHook.call<HANDLE> (newPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||
return newPath;
|
||||
} else { // Otherwise if it's an unencrypted fumen.
|
||||
if (!std::filesystem::exists (encPath)) { // We check if we don't already have a cached file.
|
||||
if (fumenKey.length () == 64) {
|
||||
@ -203,8 +213,7 @@ HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPC
|
||||
encPath = path.string ();
|
||||
}
|
||||
} else std::cout << "Using cached file for " << std::filesystem::relative (newPath) << std::endl;
|
||||
return originalCreateFileAHook.call<HANDLE> (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
||||
return encPath;
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,13 +245,45 @@ HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPC
|
||||
} else
|
||||
std::cout << "Using cached file for " << std::filesystem::relative (json_path)
|
||||
<< std::endl; // Otherwise use the already encrypted file.
|
||||
return originalCreateFileAHook.call<HANDLE> (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
return encPath;
|
||||
}
|
||||
}
|
||||
|
||||
return originalCreateFileAHook.call<HANDLE> (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
return "";
|
||||
}
|
||||
|
||||
HOOK (
|
||||
HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"),
|
||||
LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile
|
||||
) {
|
||||
// std::wcout << "CreateFileA: file " << lpFileName << std::endl;
|
||||
std::string originalFileName = std::string(lpFileName);
|
||||
std::string currentFileName = originalFileName;
|
||||
|
||||
if (!beforeHandlers.empty()) {
|
||||
for (auto handler : beforeHandlers) {
|
||||
std::string result = handler->handlerMethod(originalFileName, currentFileName);
|
||||
if (result != "") currentFileName = result;
|
||||
}
|
||||
}
|
||||
|
||||
if (useLayeredFs) {
|
||||
std::string result = LayeredFsHandler(originalFileName, currentFileName);
|
||||
if (result != "") currentFileName = result;
|
||||
}
|
||||
|
||||
if (!afterHandlers.empty()) {
|
||||
for (auto handler : afterHandlers) {
|
||||
std::string result = handler->handlerMethod(originalFileName, currentFileName);
|
||||
if (result != "") currentFileName = result;
|
||||
}
|
||||
}
|
||||
|
||||
return originalCreateFileAHook.call<HANDLE> (
|
||||
currentFileName.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile
|
||||
);
|
||||
}
|
||||
|
||||
// HOOK (HANDLE, CreateFileWHook, PROC_ADDRESS ("kernel32.dll", "CreateFileW"), LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
@ -274,10 +315,34 @@ HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPC
|
||||
|
||||
void
|
||||
Init () {
|
||||
std::wcout << "Using LayeredFs!" << std::endl;
|
||||
auto configPath = std::filesystem::current_path () / "config.toml";
|
||||
std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
|
||||
if (config_ptr) {
|
||||
auto layeredFs = openConfigSection (config_ptr.get(), "layeredfs");
|
||||
if (layeredFs) {
|
||||
useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
|
||||
datatableKey = readConfigString (layeredFs, "datatable_key", datatableKey);
|
||||
fumenKey = readConfigString (layeredFs, "fumen_key", fumenKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (useLayeredFs) {
|
||||
std::wcout << "Using LayeredFs!" << std::endl;
|
||||
}
|
||||
|
||||
register_cipher (&aes_desc);
|
||||
INSTALL_HOOK (CreateFileAHook);
|
||||
// INSTALL_HOOK (CreateFileWHook);
|
||||
}
|
||||
|
||||
void
|
||||
RegisterBefore (const std::function<std::string(const std::string, const std::string)> &fileHandler) {
|
||||
beforeHandlers.push_back (new RegisteredHandler(fileHandler));
|
||||
}
|
||||
|
||||
void
|
||||
RegisterAfter (const std::function<std::string(const std::string, const std::string)> &fileHandler) {
|
||||
afterHandlers.push_back (new RegisteredHandler(fileHandler));
|
||||
}
|
||||
|
||||
} // namespace patches::LayeredFs
|
||||
|
@ -1,3 +1,8 @@
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <pugiconfig.hpp>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
namespace patches {
|
||||
namespace JPN00 {
|
||||
void Init ();
|
||||
@ -30,5 +35,16 @@ void Init ();
|
||||
} // namespace AmAuth
|
||||
namespace LayeredFs {
|
||||
void Init ();
|
||||
void RegisterBefore (const std::function<std::string(const std::string, const std::string)> &fileHandler);
|
||||
void RegisterAfter (const std::function<std::string(const std::string, const std::string)> &fileHandler);
|
||||
} // namespace LayeredFs
|
||||
namespace TestMode {
|
||||
typedef u64 (*RefTestModeMain)(u64);
|
||||
void Init ();
|
||||
void SetupAccessor (u64 appAccessor, RefTestModeMain refTestMode);
|
||||
int ReadTestModeValue (const wchar_t* itemId);
|
||||
void RegisterItem (const std::wstring item, const std::function<void()> &initMethod);
|
||||
void RegisterModify (const std::wstring query, const std::function<void(pugi::xml_node &)> &nodeModify, const std::function<void()> &initMethod);
|
||||
void Append (pugi::xml_node &node, const wchar_t *attr, const std::wstring append);
|
||||
} // namespace TestMode
|
||||
} // namespace patches
|
||||
|
333
src/patches/testmode.cpp
Normal file
333
src/patches/testmode.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
#include "constants.h"
|
||||
#include "helpers.h"
|
||||
#include "patches.h"
|
||||
#include <exception>
|
||||
|
||||
extern GameVersion gameVersion;
|
||||
|
||||
bool chsPatch = false;
|
||||
|
||||
namespace patches::TestMode {
|
||||
class RegisteredItem {
|
||||
public:
|
||||
std::wstring selectItem;
|
||||
std::function<void()> registerInit;
|
||||
RegisteredItem (const std::wstring selectItem, const std::function<void()> &initMethod) {
|
||||
this->selectItem = selectItem;
|
||||
this->registerInit = initMethod;
|
||||
}
|
||||
};
|
||||
class RegisteredModify {
|
||||
public:
|
||||
std::wstring query;
|
||||
std::function<void(pugi::xml_node &)> nodeModify;
|
||||
std::function<void()> registerInit;
|
||||
RegisteredModify (const std::wstring query, const std::function<void(pugi::xml_node &)> &nodeModify, const std::function<void()> &initMethod) {
|
||||
this->query = query;
|
||||
this->nodeModify = nodeModify;
|
||||
this->registerInit = initMethod;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<RegisteredItem *> registeredItems = {};
|
||||
std::vector<RegisteredModify *> registeredModifies = {};
|
||||
std::wstring moddedInitial = L"";
|
||||
std::wstring modded = L"";
|
||||
|
||||
u64 appAccessor = 0;
|
||||
RefTestModeMain refTestMode = nullptr;
|
||||
|
||||
pugi::xml_document &
|
||||
CreateMenu (pugi::xml_document &menuMain, std::wstring menuId, std::wstring menuName, std::vector<std::wstring> items, std::wstring backId) {
|
||||
std::wstring menuBasicLine = L"<menu id=\"" + menuId + L"\"></menu>";
|
||||
if (menuMain.load_string(menuBasicLine.c_str())) {
|
||||
pugi::xml_node menu = menuMain.first_child ();
|
||||
pugi::xml_node menuHeader = menu.append_child(L"layout");
|
||||
pugi::xml_node menuCenter = menu.append_child(L"layout");
|
||||
pugi::xml_node menuFooter = menu.append_child(L"layout");
|
||||
// Mod Manager Menu Header
|
||||
menuHeader.append_attribute(L"type") = L"Header";
|
||||
menuHeader.append_child(L"break-item");
|
||||
pugi::xml_node menuTitle = menuHeader.append_child(L"text-item");
|
||||
std::wstring menuNameFull = L" " + menuName;
|
||||
menuTitle.append_attribute(L"label") = menuNameFull.c_str();
|
||||
menuHeader.append_child(L"break-item");
|
||||
menuHeader.append_child(L"break-item");
|
||||
pugi::xml_node menuTitleData = menuHeader.append_child(L"data-item");
|
||||
menuTitleData.append_attribute(L"data") = L"HeaderTextData";
|
||||
menuTitleData.append_attribute(L"param-offset-x") = L"-15";
|
||||
menuHeader.append_child(L"break-item");
|
||||
// Mod Manager Menu Center
|
||||
menuCenter.append_attribute(L"type") = L"Center";
|
||||
menuCenter.append_attribute(L"padding-x") = L"23";
|
||||
for (std::wstring item : items) {
|
||||
pugi::xml_document menuItem;
|
||||
std::wstring itemLine = L"<root>" + item + L"</root>";
|
||||
if (menuItem.load_string(itemLine.c_str())) {
|
||||
menuCenter.append_copy(menuItem.first_child().first_child());
|
||||
} else {
|
||||
std::wcerr << "[TestMode Error] failed to parse option line: " << item << std::endl;
|
||||
}
|
||||
menuCenter.append_child(L"break-item");
|
||||
}
|
||||
menuCenter.append_child(L"break-item");
|
||||
pugi::xml_node menuCenterExit = menuCenter.append_child(L"menu-item");
|
||||
menuCenterExit.append_attribute(L"label") = L"EXIT";
|
||||
menuCenterExit.append_attribute(L"menu") = backId.c_str();
|
||||
// Mod Manager Menu Footer
|
||||
menuFooter.append_attribute(L"type") = L"Footer";
|
||||
pugi::xml_node menuFooterData = menuFooter.append_child(L"data-item");
|
||||
menuFooterData.append_attribute(L"data") = L"FooterTextData";
|
||||
menuFooterData.append_attribute(L"param-offset-x") = L"-20";
|
||||
}
|
||||
return menuMain;
|
||||
}
|
||||
|
||||
std::wstring
|
||||
ReadXMLFileSwitcher (std::wstring &fileName) {
|
||||
std::size_t pos = fileName.rfind(L"/");
|
||||
std::wstring base = fileName.substr(0, pos + 1);
|
||||
std::wstring file = fileName.substr(pos + 1, fileName.size());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t* lFileName) {
|
||||
std::wstring originalFileName = std::wstring(lFileName);
|
||||
std::wstring fileName = originalFileName;
|
||||
if (fileName.ends_with (L"DeviceInitialize.xml") || fileName.ends_with (L"DeviceInitialize_asia.xml") || fileName.ends_with (L"DeviceInitialize_china.xml")) {
|
||||
if (moddedInitial == L"") {
|
||||
fileName = ReadXMLFileSwitcher (fileName);
|
||||
pugi::xml_document doc;
|
||||
if (!doc.load_file(fileName.c_str())) {
|
||||
std::wcerr << "Loading DeviceInitialize structure failed! path: " << fileName << std::endl;
|
||||
moddedInitial = fileName;
|
||||
} else {
|
||||
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");
|
||||
pugi::xpath_query dongleQuery = pugi::xpath_query(L"/root/menu[@id='TopMenu']/layout[@type='Center']/select-item[@id='DongleItem']");
|
||||
pugi::xml_node dongleItem = doc.select_node(dongleQuery).node();
|
||||
pugi::xml_node talItem = dongleItem.parent().append_copy(dongleItem);
|
||||
talItem.attribute(L"label").set_value(L"TAIKOARCADELOADER");
|
||||
talItem.attribute(L"id").set_value(L"TaikoArcadeLoader");
|
||||
talItem.append_attribute(L"default") = L"1";
|
||||
dongleItem.parent().append_child(L"break-item");
|
||||
|
||||
doc.save_file(modFileName.c_str());
|
||||
moddedInitial = modFileName;
|
||||
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")) {
|
||||
if (modded == L"") {
|
||||
if (!registeredItems.empty() || !registeredModifies.empty()) {
|
||||
fileName = ReadXMLFileSwitcher (fileName);
|
||||
pugi::xml_document doc;
|
||||
if (!doc.load_file(fileName.c_str())) {
|
||||
std::wcerr << "Loading TestMode structure failed! path: " << fileName << std::endl;
|
||||
modded = fileName;
|
||||
} else {
|
||||
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");
|
||||
if (!registeredItems.empty()) {
|
||||
pugi::xpath_query menuQuery = pugi::xpath_query(L"/root/menu[@id='TopMenu']/layout[@type='Center']/menu-item[@menu='GameOptionsMenu']");
|
||||
pugi::xml_node menuItem = doc.select_node(menuQuery).node();
|
||||
menuItem = menuItem.next_sibling();
|
||||
pugi::xml_node modMenuEntry = menuItem.parent().insert_child_after(L"menu-item", menuItem);
|
||||
modMenuEntry.append_attribute(L"label") = L"MOD MANAGER";
|
||||
modMenuEntry.append_attribute(L"menu") = L"ModManagerMenu";
|
||||
menuItem.parent().insert_child_after(L"break-item", modMenuEntry);
|
||||
|
||||
pugi::xml_document modMenu;
|
||||
std::vector<std::wstring> toInsertItems = {};
|
||||
for (RegisteredItem *item : registeredItems) {
|
||||
toInsertItems.push_back(item->selectItem);
|
||||
item->registerInit();
|
||||
}
|
||||
CreateMenu(modMenu, L"ModManagerMenu", L"MOD MANAGER", toInsertItems, L"TopMenu");
|
||||
pugi::xpath_query topMenuQuery = pugi::xpath_query(L"/root/menu[@id='TopMenu']");
|
||||
pugi::xml_node topMenu = doc.select_node(topMenuQuery).node();
|
||||
topMenu.parent().insert_copy_after(modMenu.first_child(), topMenu);
|
||||
}
|
||||
|
||||
if (!registeredModifies.empty()) {
|
||||
for (RegisteredModify *modify : registeredModifies) {
|
||||
pugi::xpath_query modifyQuery = pugi::xpath_query(modify->query.c_str());
|
||||
try {
|
||||
pugi::xml_node modifyNode = doc.select_node(modifyQuery).node();
|
||||
if (modifyNode) {
|
||||
modify->nodeModify(modifyNode);
|
||||
modify->registerInit();
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
std::wcerr << "[TestMode Error] failed to find node by xpath: " << modify->query << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doc.save_file(modFileName.c_str());
|
||||
modded = modFileName;
|
||||
fileName = modFileName;
|
||||
}
|
||||
} else modded = fileName;
|
||||
} else fileName = modded;
|
||||
}
|
||||
|
||||
std::wcout << "TestModeLibrary load: " << fileName << std::endl;
|
||||
|
||||
originalTestModeSetMenuHook.call<void> (testModeLibrary, fileName.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
CommonModify () {
|
||||
// Default off Close time
|
||||
TestMode::RegisterModify(
|
||||
L"/root/menu[@id='CloseTimeSettingMenu']/layout[@type='Center']/select-item[@id='ScheduleTypeItem']",
|
||||
[&](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='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']/menu-item[@menu='TopMenu']",
|
||||
[&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"離開"); }, [](){}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LocalizationCHS () {
|
||||
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='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']/menu-item[@menu='TopMenu']",
|
||||
[&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"离开"); }, [](){}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
auto configPath = std::filesystem::current_path () / "config.toml";
|
||||
std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
|
||||
|
||||
u64 testModeSetMenuAddress = PROC_ADDRESS_OFFSET ("TestModeLibrary.dll", 0x99D0);
|
||||
switch (gameVersion) {
|
||||
case GameVersion::UNKNOWN: break;
|
||||
case GameVersion::JPN00: break;
|
||||
case GameVersion::JPN08: break;
|
||||
case GameVersion::JPN39: {
|
||||
if (config_ptr) {
|
||||
auto patches = openConfigSection (config_ptr.get (), "patches");
|
||||
if (patches) {
|
||||
auto jpn39 = openConfigSection (patches, "jpn39");
|
||||
if (jpn39) {
|
||||
chsPatch = readConfigBool (jpn39, "chs_patch", chsPatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chsPatch) LocalizationCHT();
|
||||
} break;
|
||||
case GameVersion::CHN00: break;
|
||||
}
|
||||
|
||||
CommonModify ();
|
||||
|
||||
INSTALL_HOOK_DYNAMIC (TestModeSetMenuHook, testModeSetMenuAddress);
|
||||
}
|
||||
|
||||
void
|
||||
SetupAccessor (u64 appAccessor, RefTestModeMain refTestMode) {
|
||||
patches::TestMode::appAccessor = appAccessor;
|
||||
patches::TestMode::refTestMode = refTestMode;
|
||||
}
|
||||
|
||||
int
|
||||
ReadTestModeValue (const wchar_t* itemId) {
|
||||
if (appAccessor) {
|
||||
u64 testModeMain = refTestMode (appAccessor);
|
||||
if (testModeMain) {
|
||||
int value = 0;
|
||||
u64* reader = *(u64**)(testModeMain + 16);
|
||||
(*(void (__fastcall **)(u64*, const wchar_t *, int *))(*reader + 256))(reader, itemId, &value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
std::wcerr << "Read TestMode(" << itemId << ") failed!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
RegisterItem (const std::wstring item, const std::function<void()> &initMethod) {
|
||||
registeredItems.push_back(new RegisteredItem(item, initMethod));
|
||||
}
|
||||
|
||||
void
|
||||
RegisterModify (const std::wstring query, const std::function<void(pugi::xml_node &)> &nodeModify, const std::function<void()> &initMethod) {
|
||||
registeredModifies.push_back(new RegisteredModify(query, nodeModify, initMethod));
|
||||
}
|
||||
|
||||
void
|
||||
Append (pugi::xml_node &node, const wchar_t *attr, const std::wstring append) {
|
||||
pugi::xml_attribute attribute = node.attribute (attr);
|
||||
std::wstring attrValue = std::wstring(attribute.value ()) + append;
|
||||
attribute.set_value (attrValue.c_str ());
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "../patches.h"
|
||||
#include "helpers.h"
|
||||
#include "../patches.h"
|
||||
#include <safetyhook.hpp>
|
||||
|
||||
extern std::string chassisId;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "../patches.h"
|
||||
#include "helpers.h"
|
||||
#include "../patches.h"
|
||||
#include <safetyhook.hpp>
|
||||
|
||||
extern u64 song_data_size;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "../patches.h"
|
||||
#include "helpers.h"
|
||||
#include "../patches.h"
|
||||
#include <safetyhook.hpp>
|
||||
|
||||
extern u64 song_data_size;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "../patches.h"
|
||||
#include "helpers.h"
|
||||
#include "../patches.h"
|
||||
#include <iostream>
|
||||
#include <safetyhook.hpp>
|
||||
|
||||
@ -30,23 +30,77 @@ FUNCTION_PTR (i32, luaL_loadstring, PROC_ADDRESS ("lua51.dll", "luaL_loadstring"
|
||||
#define LUA_MULTRET (-1)
|
||||
#define luaL_dostring(L, s) (luaL_loadstring (L, s) || lua_pcall (L, 0, LUA_MULTRET, 0))
|
||||
|
||||
FUNCTION_PTR (u64, RefTestModeMain, ASLR (0x1400337C0), u64);
|
||||
FUNCTION_PTR (u64, RefPlayDataManager, ASLR (0x140024AC0), u64);
|
||||
FUNCTION_PTR (i64, GetUserCount, ASLR (0x1403F1020), u64);
|
||||
|
||||
i64
|
||||
lua_pushtrue (i64 a1) {
|
||||
lua_pushbool (i64 a1, bool val) {
|
||||
lua_settop (a1, 0);
|
||||
lua_pushboolean (a1, 1);
|
||||
lua_pushboolean (a1, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
HOOK (i64, AvailableMode_Collabo024, ASLR (0x1402DE710), i64 a1) { return lua_pushtrue (a1); }
|
||||
HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402DE6B0), i64 a1) { return lua_pushtrue (a1); }
|
||||
HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402DE670), i64 a1) { return lua_pushtrue (a1); }
|
||||
HOOK (i64, AvailableMode_AprilFool001, ASLR (0x1402DE5B0), i64 a1) { return lua_pushtrue (a1); }
|
||||
|
||||
i64
|
||||
lua_freeze_timer (i64 a1) {
|
||||
return lua_pushtrue (a1);
|
||||
u64 appAccessor = 0;
|
||||
u64 componentAccessor = 0;
|
||||
HOOK (i64, DeviceCheck, ASLR(0x140464FC0), i64 a1, i64 a2, i64 a3) {
|
||||
TestMode::SetupAccessor (a3, RefTestModeMain);
|
||||
componentAccessor = a2;
|
||||
return originalDeviceCheck.call<i64> (a1, a2, a3);
|
||||
}
|
||||
|
||||
int
|
||||
GetUserStatus () {
|
||||
if (appAccessor) {
|
||||
u64 playDataManager = RefPlayDataManager (appAccessor);
|
||||
if (playDataManager) return GetUserCount(playDataManager);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
HOOK (i64, AvailableMode_Collabo024, ASLR (0x1402DE710), i64 a1) {
|
||||
int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
if (tournamentMode == 1) return originalAvailableMode_Collabo024.call<i64> (a1);
|
||||
int status = TestMode::ReadTestModeValue (L"ModModeCollabo024");
|
||||
if (status == 1 && GetUserStatus () == 1) {
|
||||
return lua_pushbool (a1, true);
|
||||
}
|
||||
return originalAvailableMode_Collabo024.call<i64> (a1);
|
||||
}
|
||||
HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402DE6B0), i64 a1) {
|
||||
int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
if (tournamentMode == 1) return originalAvailableMode_Collabo025.call<i64> (a1);
|
||||
int status = TestMode::ReadTestModeValue (L"ModModeCollabo025");
|
||||
if (status == 1 && GetUserStatus () == 1) {
|
||||
return lua_pushbool (a1, true);
|
||||
}
|
||||
return originalAvailableMode_Collabo025.call<i64> (a1);
|
||||
}
|
||||
HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402DE670), i64 a1) {
|
||||
int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
if (tournamentMode == 1) return originalAvailableMode_Collabo026.call<i64> (a1);
|
||||
int status = TestMode::ReadTestModeValue (L"ModModeCollabo026");
|
||||
if (status == 1 && GetUserStatus () == 1) {
|
||||
return lua_pushbool (a1, true);
|
||||
}
|
||||
return originalAvailableMode_Collabo026.call<i64> (a1);
|
||||
}
|
||||
HOOK (i64, AvailableMode_AprilFool001, ASLR (0x1402DE5B0), i64 a1) {
|
||||
int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
if (tournamentMode == 1) return originalAvailableMode_AprilFool001.call<i64> (a1);
|
||||
int status = TestMode::ReadTestModeValue (L"ModModeAprilFool001");
|
||||
if (status == 1) {
|
||||
return lua_pushbool (a1, true);
|
||||
}
|
||||
return originalAvailableMode_AprilFool001.call<i64> (a1);
|
||||
}
|
||||
i64 __fastcall lua_freeze_timer (i64 a1) {
|
||||
int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
|
||||
if (tournamentMode == 1) return lua_pushbool (a1, true);
|
||||
int status = TestMode::ReadTestModeValue (L"ModFreezeTimer");
|
||||
if (status == 1) return lua_pushbool (a1, true);
|
||||
return lua_pushbool (a1, false);
|
||||
}
|
||||
MID_HOOK (FreezeTimer, ASLR (0x14019FF51), SafetyHookContext &ctx) {
|
||||
auto a1 = ctx.rdi;
|
||||
int v9 = (int)(ctx.rax + 1);
|
||||
@ -103,11 +157,15 @@ ExecuteSendResultData () {
|
||||
}
|
||||
|
||||
bool sendFlag = false;
|
||||
#define SCENE_RESULT_HOOK(functionName, location) \
|
||||
HOOK (void, functionName, location, i64 a1, i64 a2, i64 a3) { \
|
||||
sendFlag = true; \
|
||||
original##functionName.call (a1, a2, a3); \
|
||||
ExecuteSendResultData (); \
|
||||
#define SCENE_RESULT_HOOK(functionName, location) \
|
||||
HOOK (void, functionName, location, i64 a1, i64 a2, i64 a3) { \
|
||||
if ( \
|
||||
TestMode::ReadTestModeValue (L"ModInstantResult") != 1 && \
|
||||
TestMode::ReadTestModeValue (L"NumberOfStageItem") <= 4 \
|
||||
) { original##functionName.call (a1, a2, a3); return; } \
|
||||
sendFlag = true; \
|
||||
original##functionName.call (a1, a2, a3); \
|
||||
ExecuteSendResultData (); \
|
||||
}
|
||||
|
||||
SCENE_RESULT_HOOK (SceneResultInitialize_Enso, ASLR (0x140411FD0));
|
||||
@ -116,12 +174,16 @@ SCENE_RESULT_HOOK (SceneResultInitialize_Collabo025, ASLR (0x140411FD0));
|
||||
SCENE_RESULT_HOOK (SceneResultInitialize_Collabo026, ASLR (0x140411FD0));
|
||||
SCENE_RESULT_HOOK (SceneResultInitialize_AprilFool, ASLR (0x140411FD0));
|
||||
|
||||
#define SEND_RESULT_HOOK(functionName, location) \
|
||||
HOOK (void, functionName, location, i64 a1) { \
|
||||
if (sendFlag) { \
|
||||
sendFlag = false; \
|
||||
original##functionName.call (a1); \
|
||||
} \
|
||||
#define SEND_RESULT_HOOK(functionName, location) \
|
||||
HOOK (void, functionName, location, i64 a1) { \
|
||||
if ( \
|
||||
TestMode::ReadTestModeValue (L"ModInstantResult") != 1 && \
|
||||
TestMode::ReadTestModeValue (L"NumberOfStageItem") <= 4 \
|
||||
) { original##functionName.call (a1); return; } \
|
||||
if (sendFlag) { \
|
||||
sendFlag = false; \
|
||||
original##functionName.call (a1); \
|
||||
} \
|
||||
}
|
||||
|
||||
SEND_RESULT_HOOK (SendResultData_Enso, ASLR (0x1401817B0));
|
||||
@ -131,7 +193,11 @@ SEND_RESULT_HOOK (SendResultData_AprilFool, ASLR (0x140177800));
|
||||
|
||||
#define CHANGE_RESULT_SIZE_HOOK(functionName, location, target) \
|
||||
MID_HOOK (functionName, location, SafetyHookContext &ctx) { \
|
||||
i64 instance = GetPlayDataManagerRef (*(i64 *)ctx.r12); \
|
||||
if ( \
|
||||
TestMode::ReadTestModeValue (L"ModInstantResult") != 1 && \
|
||||
TestMode::ReadTestModeValue (L"NumberOfStageItem") <= 4 \
|
||||
) { return; } \
|
||||
i64 instance = RefPlayDataManager (*(i64 *)ctx.r12); \
|
||||
u32 currentStageCount = *(u32 *)(instance + 8); \
|
||||
ctx.target &= 0xFFFFFFFF00000000; \
|
||||
ctx.target |= currentStageCount; \
|
||||
@ -144,7 +210,11 @@ CHANGE_RESULT_SIZE_HOOK (ChangeResultDataSize_AprilFool, ASLR (0x140176044), rax
|
||||
|
||||
#define CHANGE_RESULT_INDEX_HOOK(functionName, location, target, offset, skip) \
|
||||
MID_HOOK (functionName, location, SafetyHookContext &ctx) { \
|
||||
i64 instance = GetPlayDataManagerRef (*(i64 *)ctx.r12); \
|
||||
if ( \
|
||||
TestMode::ReadTestModeValue (L"ModInstantResult") != 1 && \
|
||||
TestMode::ReadTestModeValue (L"NumberOfStageItem") <= 4 \
|
||||
) { return; } \
|
||||
i64 instance = RefPlayDataManager (*(i64 *)ctx.r12); \
|
||||
u32 currentStageCount = *(u32 *)(instance + 8); \
|
||||
ctx.target &= 0xFFFFFFFF00000000; \
|
||||
ctx.target |= currentStageCount - 1; \
|
||||
@ -189,6 +259,10 @@ MID_HOOK (ChangeLanguageType, ASLR (0x1400B2016), SafetyHookContext &ctx) {
|
||||
if (*pFontType == 4) *pFontType = 2;
|
||||
}
|
||||
|
||||
MID_HOOK (CountLockedCrown, ASLR (0x1403F2A25), SafetyHookContext &ctx) {
|
||||
ctx.r15 |= 1;
|
||||
}
|
||||
|
||||
std::map<std::string, int> nus3bankMap;
|
||||
int nus3bankIdCounter = 0;
|
||||
std::map<std::string, bool> voiceCnExist;
|
||||
@ -328,6 +402,32 @@ HOOK (i64, LoadedBankAll, ASLR (0x1404C69F0), i64 a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
float soundRate = 1.0F;
|
||||
HOOK (i32, SetMasterVolumeSpeaker, ASLR (0x140160330), i32 a1) {
|
||||
soundRate = a1 <= 100 ? 1.0F : a1 / 100.0;
|
||||
return originalSetMasterVolumeSpeaker.call<i32> (a1 > 100 ? 100 : a1);
|
||||
}
|
||||
|
||||
HOOK (u64, NuscBusVolume, ASLR (0x1407B1C30), u64 a1, u64 a2, float a3) {
|
||||
return originalNuscBusVolume.call<u64> (a1, a2, a3 * soundRate);
|
||||
}
|
||||
|
||||
std::string* fontName = nullptr;
|
||||
HOOK (u8, SetupFontInfo, ASLR (0x14049D820), u64 a1, u64 a2, size_t a3, u64 a4) {
|
||||
if (fontName != nullptr) delete fontName;
|
||||
fontName = new std::string(((char*)a1) + 120);
|
||||
return originalSetupFontInfo.call<u8> (a1, a2, a3, a4);
|
||||
}
|
||||
|
||||
HOOK (u32, ReadFontInfoInt, ASLR (0x14049EAC0), u64 a1, u64 a2) {
|
||||
std::string attribute((char*)a2);
|
||||
u32 result = originalReadFontInfoInt.call<u32> (a1, a2);
|
||||
if (fontName->starts_with("cn_") && attribute == "offsetV") {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const i32 datatableBufferSize = 1024 * 1024 * 12;
|
||||
safetyhook::Allocation datatableBuffer1;
|
||||
safetyhook::Allocation datatableBuffer2;
|
||||
@ -362,12 +462,7 @@ Init () {
|
||||
bool vsync = false;
|
||||
bool unlockSongs = true;
|
||||
bool fixLanguage = false;
|
||||
bool freezeTimer = false;
|
||||
bool chsPatch = false;
|
||||
bool instantResult = false;
|
||||
bool modeCollabo025 = false;
|
||||
bool modeCollabo026 = false;
|
||||
bool modeAprilFool001 = false;
|
||||
|
||||
bool useLayeredfs = false;
|
||||
|
||||
@ -380,12 +475,7 @@ Init () {
|
||||
auto jpn39 = openConfigSection (patches, "jpn39");
|
||||
if (jpn39) {
|
||||
fixLanguage = readConfigBool (jpn39, "fix_language", fixLanguage);
|
||||
freezeTimer = readConfigBool (jpn39, "freeze_timer", freezeTimer);
|
||||
chsPatch = readConfigBool (jpn39, "chs_patch", chsPatch);
|
||||
instantResult = readConfigBool (jpn39, "instant_result", instantResult);
|
||||
modeCollabo025 = readConfigBool (jpn39, "mode_collabo025", modeCollabo025);
|
||||
modeCollabo026 = readConfigBool (jpn39, "mode_collabo026", modeCollabo026);
|
||||
modeAprilFool001 = readConfigBool (jpn39, "mode_aprilfool001", modeAprilFool001);
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,11 +493,18 @@ Init () {
|
||||
if (layeredfs) useLayeredfs = readConfigBool (layeredfs, "enabled", useLayeredfs);
|
||||
}
|
||||
|
||||
// Hook to get AppAccessor and ComponentAccessor
|
||||
INSTALL_HOOK (DeviceCheck);
|
||||
INSTALL_HOOK (luaL_newstate);
|
||||
|
||||
// Apply common config patch
|
||||
WRITE_MEMORY (ASLR (0x140494533), i32, xRes);
|
||||
WRITE_MEMORY (ASLR (0x14049453A), i32, yRes);
|
||||
if (!vsync) WRITE_MEMORY (ASLR (0x14064C7E9), u8, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x90);
|
||||
if (unlockSongs) WRITE_MEMORY (ASLR (0x1403F45CF), u8, 0xB0, 0x01);
|
||||
if (unlockSongs) {
|
||||
WRITE_MEMORY (ASLR (0x1403F45CF), u8, 0xB0, 0x01);
|
||||
INSTALL_MID_HOOK (CountLockedCrown);
|
||||
}
|
||||
|
||||
// Bypass errors
|
||||
WRITE_MEMORY (ASLR (0x140041A00), u8, 0xC3);
|
||||
@ -458,29 +555,61 @@ Init () {
|
||||
}
|
||||
|
||||
// Freeze Timer
|
||||
if (freezeTimer) INSTALL_MID_HOOK (FreezeTimer);
|
||||
|
||||
// Send result per song
|
||||
if (instantResult) {
|
||||
INSTALL_HOOK (luaL_newstate);
|
||||
INSTALL_HOOK (SceneResultInitialize_Enso);
|
||||
INSTALL_HOOK (SceneResultInitialize_AI);
|
||||
INSTALL_HOOK (SceneResultInitialize_Collabo025);
|
||||
INSTALL_HOOK (SceneResultInitialize_Collabo026);
|
||||
INSTALL_HOOK (SceneResultInitialize_AprilFool);
|
||||
INSTALL_HOOK (SendResultData_Enso);
|
||||
INSTALL_HOOK (SendResultData_AI);
|
||||
INSTALL_HOOK (SendResultData_Collabo025_026);
|
||||
INSTALL_HOOK (SendResultData_AprilFool);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_Enso);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_AI);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_Collabo025_026);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_AprilFool);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_Enso);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_AI);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_Collabo025_026);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_AprilFool);
|
||||
}
|
||||
TestMode::RegisterItem(
|
||||
L"<select-item label=\"FREEZE TIMER\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" group=\"Setting\" id=\"ModFreezeTimer\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&]() { INSTALL_MID_HOOK (FreezeTimer); }
|
||||
);
|
||||
// Mode Unlock
|
||||
TestMode::RegisterItem(
|
||||
L"<select-item label=\"KIMETSU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo024\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&]() { INSTALL_HOOK (AvailableMode_Collabo024); }
|
||||
);
|
||||
TestMode::RegisterItem(
|
||||
L"<select-item label=\"ONE PIECE MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo025\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&]() { INSTALL_HOOK (AvailableMode_Collabo025); }
|
||||
);
|
||||
TestMode::RegisterItem(
|
||||
L"<select-item label=\"AI SOSHINA MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo026\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&]() { INSTALL_HOOK (AvailableMode_Collabo026); }
|
||||
);
|
||||
TestMode::RegisterItem(
|
||||
L"<select-item label=\"AOHARU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeAprilFool001\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&]() { INSTALL_HOOK (AvailableMode_AprilFool001); }
|
||||
);
|
||||
TestMode::RegisterItem(
|
||||
L"<select-item label=\"INSTANT RESULT\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" group=\"Setting\" id=\"ModInstantResult\" max=\"1\" min=\"0\" default=\"0\"/>",
|
||||
[&]() {
|
||||
INSTALL_HOOK (SceneResultInitialize_Enso);
|
||||
INSTALL_HOOK (SceneResultInitialize_AI);
|
||||
INSTALL_HOOK (SceneResultInitialize_Collabo025);
|
||||
INSTALL_HOOK (SceneResultInitialize_Collabo026);
|
||||
INSTALL_HOOK (SceneResultInitialize_AprilFool);
|
||||
INSTALL_HOOK (SendResultData_Enso);
|
||||
INSTALL_HOOK (SendResultData_AI);
|
||||
INSTALL_HOOK (SendResultData_Collabo025_026);
|
||||
INSTALL_HOOK (SendResultData_AprilFool);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_Enso);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_AI);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_Collabo025_026);
|
||||
INSTALL_MID_HOOK (ChangeResultDataSize_AprilFool);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_Enso);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_AI);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_Collabo025_026);
|
||||
INSTALL_MID_HOOK (ChangeResultDataIndex_AprilFool);
|
||||
}
|
||||
);
|
||||
// Unlimit Volume
|
||||
TestMode::RegisterModify(
|
||||
L"/root/menu[@id='SoundTestMenu']/layout[@type='Center']/select-item[@id='OutputLevelSpeakerItem']",
|
||||
[&](pugi::xml_node &node) { TestMode::Append(node, L"label", L"*"); node.attribute(L"max").set_value(L"300"); node.attribute(L"delta").set_value(L"1"); },
|
||||
[&]() { INSTALL_HOOK (SetMasterVolumeSpeaker); INSTALL_HOOK (NuscBusVolume); }
|
||||
);
|
||||
// Instant Result
|
||||
// TestMode::RegisterModify(
|
||||
// L"/root/menu[@id='GameOptionsMenu']/layout[@type='Center']/select-item[@id='NumberOfStageItem']",
|
||||
// [&](pugi::xml_node &node) { TestMode::Append(node, L"label", L"*"); node.attribute(L"max").set_value(L"99"); },
|
||||
// [&](){}
|
||||
// );
|
||||
|
||||
// Use chs font/wordlist instead of cht
|
||||
if (chsPatch) {
|
||||
@ -498,8 +627,22 @@ Init () {
|
||||
WRITE_MEMORY (ASLR (0x140CD1AF8), char, "cn_30");
|
||||
WRITE_MEMORY (ASLR (0x140C946A0), char, "chineseSText");
|
||||
WRITE_MEMORY (ASLR (0x140C946B0), char, "chineseSFontType");
|
||||
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");
|
||||
INSTALL_MID_HOOK (ChangeLanguageType);
|
||||
INSTALL_HOOK (SetupFontInfo);
|
||||
INSTALL_HOOK (ReadFontInfoInt);
|
||||
}
|
||||
|
||||
LayeredFs::RegisterBefore([=](const std::string originalFileName, const std::string currentFileName) -> 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;
|
||||
else return currentFileName;
|
||||
});
|
||||
}
|
||||
|
||||
// Fix language
|
||||
@ -519,11 +662,6 @@ Init () {
|
||||
INSTALL_HOOK (PlaySoundSpecial);
|
||||
}
|
||||
|
||||
// Mode unlock
|
||||
if (modeCollabo025) INSTALL_HOOK (AvailableMode_Collabo025);
|
||||
if (modeCollabo026) INSTALL_HOOK (AvailableMode_Collabo026);
|
||||
if (modeAprilFool001) INSTALL_HOOK (AvailableMode_AprilFool001);
|
||||
|
||||
// Fix normal song play after passing through silent song
|
||||
INSTALL_MID_HOOK (GenNus3bankId);
|
||||
INSTALL_HOOK (LoadedBankAll);
|
||||
|
10
subprojects/pugixml.wrap
Normal file
10
subprojects/pugixml.wrap
Normal file
@ -0,0 +1,10 @@
|
||||
[wrap-file]
|
||||
directory = pugixml-1.14
|
||||
|
||||
source_url = https://github.com/zeux/pugixml/archive/v1.14.tar.gz
|
||||
source_filename = pugixml-1.14.tar.gz
|
||||
source_hash = 610f98375424b5614754a6f34a491adbddaaec074e9044577d965160ec103d2e
|
||||
method = cmake
|
||||
|
||||
[provide]
|
||||
pugixml = pugixml_static_dep
|
Loading…
x
Reference in New Issue
Block a user