1
1
mirror of synced 2025-01-30 19:33:44 +01:00

Add multiple new feature & update config/readme

累积更新
文档与配置更新
去掉多余的配置项读取
This commit is contained in:
ptmaster 2024-11-13 17:29:36 +08:00 committed by Farewell_
parent fb2c5c117d
commit 3933ecab00
19 changed files with 725 additions and 118 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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',

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 &notFoundValue);
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 = {};

View File

@ -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

View File

@ -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
View 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"";
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"");
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" ";
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" ", 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 ());
}
}

View File

@ -1,5 +1,5 @@
#include "../patches.h"
#include "helpers.h"
#include "../patches.h"
#include <safetyhook.hpp>
extern std::string chassisId;

View File

@ -1,5 +1,5 @@
#include "../patches.h"
#include "helpers.h"
#include "../patches.h"
#include <safetyhook.hpp>
extern u64 song_data_size;

View File

@ -1,5 +1,5 @@
#include "../patches.h"
#include "helpers.h"
#include "../patches.h"
#include <safetyhook.hpp>
extern u64 song_data_size;

View File

@ -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=\" \" param-offset-x=\"35\" replace-text=\"0:, 1:\" group=\"Setting\" id=\"ModFreezeTimer\" max=\"1\" min=\"0\" default=\"0\"/>",
[&]() { INSTALL_MID_HOOK (FreezeTimer); }
);
// Mode Unlock
TestMode::RegisterItem(
L"<select-item label=\" \" param-offset-x=\"35\" replace-text=\"0:, 1:, 2: \" group=\"Setting\" id=\"ModModeCollabo024\" max=\"1\" min=\"0\" default=\"0\"/>",
[&]() { INSTALL_HOOK (AvailableMode_Collabo024); }
);
TestMode::RegisterItem(
L"<select-item label=\" \" param-offset-x=\"35\" replace-text=\"0:, 1:, 2: \" group=\"Setting\" id=\"ModModeCollabo025\" max=\"1\" min=\"0\" default=\"0\"/>",
[&]() { INSTALL_HOOK (AvailableMode_Collabo025); }
);
TestMode::RegisterItem(
L"<select-item label=\" \" param-offset-x=\"35\" replace-text=\"0:, 1:, 2: \" group=\"Setting\" id=\"ModModeCollabo026\" max=\"1\" min=\"0\" default=\"0\"/>",
[&]() { INSTALL_HOOK (AvailableMode_Collabo026); }
);
TestMode::RegisterItem(
L"<select-item label=\" \" param-offset-x=\"35\" replace-text=\"0:, 1:, 2: \" group=\"Setting\" id=\"ModModeAprilFool001\" max=\"1\" min=\"0\" default=\"0\"/>",
[&]() { INSTALL_HOOK (AvailableMode_AprilFool001); }
);
TestMode::RegisterItem(
L"<select-item label=\" \" param-offset-x=\"35\" replace-text=\"0:, 1:\" 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
View 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