Added Logger to TAL (#13)

* Added Logger for TAL with __VA_ARGS__ support

* Added Tatacon entries in SDL DB
* Added comments to exported config file

* Included keys for LayeredFS

* Cleaned up a bit of code in JPN39 patches
* Formatted all files using clang-format

* Force invalid cards on original readers
You can now use any cards with the original namco readers.

* Move helper functions to cpp file

---------

Co-authored-by: ptmaster <lty2008one@163.com>
This commit is contained in:
KIT!
2024-11-24 17:44:03 +01:00
committed by Farewell_
co-authored by ptmaster
parent 3933ecab00
commit d0374dca1b
22 changed files with 996 additions and 810 deletions
+58 -88
View File
@@ -17,113 +17,81 @@ If your game hangs on a black screen at launch for more than a minute, Start Tai
```toml
[amauth]
# server ip
server = "127.0.0.1"
# server port
port = "54430"
# dongle serial
chassis_id = "284111080000"
# shop name
shop_id = "TAIKO ARCADE LOADER"
# game version
game_ver = "00.00"
# country code
country_code = "JPN"
[patches]
# patch version
# auto: hash detection (you need to use the original exe)
# JPN00: JPN 00.18
# JPN08: JPN 08.18
# JPN39: JPN 39.06
# CHN00: CHN 00.32 (based on JPN 32.09)
version = "auto"
# unlock all songs
version = "auto" # Patch version
# | - auto: hash detection (you need to use the original exe otherwise it will not load).
# | - JPN00: For use with Taiko JPN 00.18
# | - JPN08: For use with Taiko JPN 08.18
# | - JPN39: For use with Taiko JPN 39.06
# | - CHN00: For use with Taiko CHN 00.32
unlock_songs = true
[patches.chn00]
# sync test mode language to attract etc
fix_language = false
# show demo movie
demo_movie = true
# enable one piece collab mode
mode_collabo025 = false
# enable ai soshina mode
mode_collabo026 = false
[patches.chn00] # These patches are only available for version CHN00
fix_language = false # Sync test mode language to attract etc
demo_movie = true # Show demo movie
mode_collabo025 = false # Enable one piece collab mode
mode_collabo026 = false # Enable ai soshina mode
[patches.jpn39]
# sync test mode language to attract etc
fix_language = false
# use cn font and chineseS wordlist value
chs_patch = false
# more options is now moved to testmode
[patches.jpn39] # These patches are only available for version JPN39
fix_language = false # Sync test mode language to attract etc
chs_patch = false # Use Chinese font and Simplified Chinese values from the wordlist
# More options are available in the ModManager, in the TestMode menu (Default key is F1)
[emulation]
# If usio emulation is disabled, you need to place bnusio_original.dll (unmodified bnusio.dll) in the executable folder.
usio = true
card_reader = true
qr = true
usio = true # If usio emulation is disabled, you need to place bnusio_original.dll (unmodified bnusio.dll) in the executable folder.
card_reader = true # Disable this if you have an original namco card reader
accept_invalid = false # Enable this if you want to accept cards incompatible with the original readers
qr = true # Disable this if you have an original namco qr code scanner
[graphics]
# window resolution
res = { x = 1920, y = 1080 }
# window mode
windowed = false
# vertical sync
vsync = false
# fps limit
fpslimit = 120
[audio]
# wasapi shared mode
# allows you to have multiple audio sources at once at a cost of having higher latency.
wasapi_shared = true
# use asio audio mode
asio = false
# asio driver name
# to find it, open up regedit then navigate to HKEY_LOCAL_MACHINE\SOFTWARE\ASIO
# the name is case sensitive.
asio_driver = ""
wasapi_shared = true # Wasapi shared mode, allows you to have multiple audio sources at once at a cost of having higher latency.
asio = false # Use asio audio mode
asio_driver = "ASIO4ALL v2" # Asio driver name
# | If you're not using asio4all, open up regedit then navigate to HKEY_LOCAL_MACHINE\SOFTWARE\ASIO for your driver's name.
# | It is case sensitive.
[qr]
# qr image path
image_path = ""
image_path = "" # Path to the image of the QR Code you want to use
# qr data used for other events (ex. gaiden, custom folder)
[qr.data]
# qr serial
serial = ""
# qr type
# 0: default (serial only)
# 5: custom folder
type = 0
# song noes used for custom folder
song_no = []
[drum]
# input interval (if using taiko drum controller, should be set to 0)
wait_period = 4
[qr.data] # qr data used for other events (ex. gaiden, custom folder)
serial = "" # qr serial
type = 0 # qr type
# | 0: default (serial only)
# | 5: custom folder
song_no = [] # Song noes used for custom folder
[controller]
# use analog input
analog_input = false
wait_period = 4 # Input interval (if using taiko drum controller, should be set to 0)
analog_input = false # Use analog input (you need a compatible controller, this allows playing small and big notes like on arcade cabinets)
[keyboard]
# auto change to english ime mode
auto_ime = false
# use jp layout scan code (if using jp layout keyboard, must be set to true)
jp_layout = false
auto_ime = false # Automatically change to english ime mode upon game startup
jp_layout = false # Use jp layout scan code (if using jp layout keyboard, must be set to true)
[layeredfs]
# replace assets from the game using a layered file system.
# For example if you want to edit the wordlist, add your edited version like so:
# .\Data_mods\x64\datatable\wordlist.bin
enabled = false
# AES encryption keys needed to dynamically encrypt datatable files and fumens.
# keys need to be provided in an hexlified form. A missing or incorrect key will crash the game.
# keys are not needed if you provide already encrypted files.
datatable_key = ""
fumen_key = ""
enabled = false # Replace assets from the game using a layered file system.
# | For example if you want to edit the wordlist, add your edited version like so:
# | .\Data_mods\x64\datatable\wordlist.json
# | You can provide both unencrypted and encrypted files.
[logging]
log_level = "INFO" # Log level, Can be either "NONE", "ERROR", "WARN", "INFO", "DEBUG" and "HOOKS"
# | Keep this as low as possible (Info is usually more than enough) as more logging will slow down your game
log_to_file = false # Log to file, set this to true to save the logs from your last session to TaikoArcadeLoader.log
# |Again, if you do not have a use for this (debugging mods or whatnot), turn it off.
```
## TestMode options (JPN39 only)
@@ -131,15 +99,17 @@ fumen_key = ""
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)
* 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**)
* Louder volume (Speaker Volume is now up to 300%, **WARNING: May damage your speakers**)
## Building
@@ -147,16 +117,16 @@ TaikoArcadeLoader can be a bit tricky to build if you've never done it before.
Go to the TaikoArcadeLoader folder and run the following commands:
```bash
apt install -y npm mingw-w64 ninja-build nasm pip 7zip cmake
apt install -y npm mingw-w64 ninja-build nasm 7zip cmake python3-pip
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 you entered TaikoArcadeLoader folder
# make sure you're in the TaikoArcadeLoader folder before running this
export PATH=`pwd`/xpacks/.bin:$PATH
make setup
make clean-setup
```
Once that's done, run `make` to build TAL.
The output will be written in the `out` folder.
The output will be written in the `out` folder.
+56 -28
View File
@@ -6,24 +6,36 @@ shop_id = "TAIKO ARCADE LOADER"
game_ver = "00.00"
country_code = "JPN"
[patches]
version = "auto"
version = "auto" # Patch version
# | - auto: hash detection (you need to use the original exe otherwise it will not load).
# | - JPN00: For use with Taiko JPN 00.18
# | - JPN08: For use with Taiko JPN 08.18
# | - JPN39: For use with Taiko JPN 39.06
# | - CHN00: For use with Taiko CHN 00.32
unlock_songs = true
[patches.chn00]
fix_language = false
demo_movie = true
mode_collabo025 = false
mode_collabo026 = false
[patches.jpn39]
fix_language = false
chs_patch = false
[patches.chn00] # These patches are only available for version CHN00
fix_language = false # Sync test mode language to attract etc
demo_movie = true # Show demo movie
mode_collabo025 = false # Enable one piece collab mode
mode_collabo026 = false # Enable ai soshina mode
[patches.jpn39] # These patches are only available for version JPN39
fix_language = false # Sync test mode language to attract etc
chs_patch = false # Use Chinese font and Simplified Chinese values from the wordlist
# More options are available in the ModManager, in the TestMode menu (Default key is F1)
[emulation]
usio = true
card_reader = true
qr = true
usio = true # Disable this if you want to use an original Namco USIO board. you need to place bnusio_original.dll (unmodified bnusio.dll) in the executable folder.
card_reader = true # Disable this if you want to use an original Namco card reader
accept_invalid = false # Enable this if you want to accept cards incompatible with the original readers
qr = true # Disable this if you want to use an original Namco QR code scanner
[graphics]
res = { x = 1920, y = 1080 }
@@ -31,30 +43,46 @@ windowed = false
vsync = false
fpslimit = 120
[audio]
wasapi_shared = true
asio = false
asio_driver = ""
wasapi_shared = true # Wasapi shared mode, allows you to have multiple audio sources at once at a cost of having higher latency.
asio = false # Use asio audio mode
asio_driver = "ASIO4ALL v2" # Asio driver name
# | If you're not using asio4all, open up regedit then navigate to HKEY_LOCAL_MACHINE\SOFTWARE\ASIO for your driver's name.
# | It is case sensitive.
[qr]
image_path = ""
image_path = "" # Path to the image of the QR Code you want to use
[qr.data]
serial = ""
type = 0
song_no = []
[drum]
wait_period = 4
[qr.data] # QR data used for other events (ex. gaiden, custom folder)
serial = "" # QR serial
type = 0 # QR type
# | 0: default (serial only)
# | 5: custom folder
song_no = [] # Song noes used for custom folder
[controller]
analog_input = false
wait_period = 4 # Input interval (if using taiko drum controller, should be set to 0)
analog_input = false # Use analog input (you need a compatible controller, this allows playing small and big notes like on arcade cabinets)
[keyboard]
auto_ime = false
jp_layout = false
auto_ime = false # Automatically change to english ime mode upon game startup
jp_layout = false # Use jp layout scan code (if using jp layout keyboard, must be set to true)
[layeredfs]
enabled = false
datatable_key = ""
fumen_key = ""
enabled = false # Replace assets from the game using a layered file system.
# | For example if you want to edit the wordlist, add your edited version like so:
# | .\Data_mods\x64\datatable\wordlist.json
# | You can provide both unencrypted and encrypted files.
[logging]
log_level = "INFO" # Log level, Can be either "NONE", "ERROR", "WARN", "INFO", "DEBUG" and "HOOKS"
# | Keep this as low as possible (Info is usually more than enough) as more logging will slow down your game
log_to_file = false # Log to file, set this to true to save the logs from your last session to TaikoArcadeLoader.log
# |Again, if you do not have a use for this (debugging mods or whatnot), turn it off.
+5
View File
@@ -1,6 +1,11 @@
# Game Controller DB for SDL in 2.0.16 format
# Source: https://github.com/gabomdq/SDL_GameControllerDB
# Taiko stuff
03000000de280000ff11000001007801,HORI Taiko Controller,platform:Windows,leftstick:b6,rightstick:b7,lefttrigger:b4,righttrigger:b5,
030000000d0f0000f000000000000000,Hori Taiko Controller,platform:Windows,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,
030000000d0f0000f000000011010000,HORI Taiko Controller,leftstick:b10,rightstick:b11,lefttrigger:b6,righttrigger:b7,platform:Linux,
# Windows
03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,
03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,
+1
View File
@@ -79,6 +79,7 @@ library(
sources : [
'src/dllmain.cpp',
'src/helpers.cpp',
'src/logger.cpp',
'src/poll.cpp',
'src/bnusio.cpp',
'src/patches/amauth.cpp',
+81 -59
View File
@@ -14,6 +14,7 @@ extern char chipId2[33];
extern bool autoIme;
extern bool emulateUsio;
extern bool emulateCardReader;
extern bool acceptInvalidCards;
typedef i32 (*callbackAttach) (i32, i32, i32 *);
typedef void (*callbackTouch) (i32, i32, u8[168], u64);
@@ -40,10 +41,10 @@ Keybindings P1_LEFT_BLUE = {.keycodes = {'D'}, .axis = {SDL_AXIS_LEFT_DOWN}};
Keybindings P1_LEFT_RED = {.keycodes = {'F'}, .axis = {SDL_AXIS_LEFT_RIGHT}};
Keybindings P1_RIGHT_RED = {.keycodes = {'J'}, .axis = {SDL_AXIS_RIGHT_RIGHT}};
Keybindings P1_RIGHT_BLUE = {.keycodes = {'K'}, .axis = {SDL_AXIS_RIGHT_DOWN}};
Keybindings P2_LEFT_BLUE = {};
Keybindings P2_LEFT_RED = {};
Keybindings P2_RIGHT_RED = {};
Keybindings P2_RIGHT_BLUE = {};
Keybindings P2_LEFT_BLUE = {.keycodes = {'Z'}};
Keybindings P2_LEFT_RED = {.keycodes = {'X'}};
Keybindings P2_RIGHT_RED = {.keycodes = {'C'}};
Keybindings P2_RIGHT_BLUE = {.keycodes = {'V'}};
bool testEnabled = false;
int coin_count = 0;
@@ -187,6 +188,30 @@ u16 __fastcall bnusio_GetCoin (i32 a1) { return coin_count; }
u16 __fastcall bnusio_GetService (i32 a1) { return service_count; }
}
void
InspectWaitTouch (i32 a1, i32 a2, u8 _cardData[168], u64 _touchData) {
if (AreAllBytesZero (_cardData, 0x00, 168)) // This happens when you enter test mode.
return touchCallback (a1, a2, _cardData, _touchData);
bool valid = !AreAllBytesZero (_cardData, 0x50, 21);
if (valid) {
LogMessage (LOG_LEVEL_DEBUG, "Card is valid");
} else {
memcpy (_cardData + 0x50, _cardData + 0x2C, 16); // 16 to match felica lite serial number length
LogMessage (LOG_LEVEL_DEBUG, "Card is usually not supported");
}
std::ostringstream oss;
for (size_t i = 0; i < 168; ++i) {
oss << std::setw (2) << std::setfill ('0') << std::hex << static_cast<int> (_cardData[i]) << " ";
if ((i + 1) % 21 == 0) oss << "\n";
}
LogMessage (LOG_LEVEL_DEBUG, "A1: %d, A2: %d, Card data: \n%s", a1, a2, oss.str ().c_str ());
if (touchCallback) return valid ? touchCallback (a1, a2, _cardData, _touchData) : touchCallback (0, 0, _cardData, _touchData);
}
FUNCTION_PTR (i64, bnusio_Open_Original, PROC_ADDRESS ("bnusio_original.dll", "bnusio_Open"));
FUNCTION_PTR (i64, bnusio_Close_Original, PROC_ADDRESS ("bnusio_original.dll", "bnusio_Close"));
FUNCTION_PTR (u64, bnusio_Communication_Original, PROC_ADDRESS ("bnusio_original.dll", "bnusio_Communication"), i32);
@@ -238,9 +263,9 @@ HOOK (void, bngrw_Fin, PROC_ADDRESS ("bngrw.dll", "BngRwFin")) { return; }
HOOK (u64, bngrw_IsCmdExec, PROC_ADDRESS ("bngrw.dll", "BngRwIsCmdExec")) { return 0xFFFFFFFF; }
HOOK (i32, bngrw_ReqCancel, PROC_ADDRESS ("bngrw.dll", "BngRwReqCancel")) { return 1; }
HOOK (i32, bngrw_ReqSendUrl, PROC_ADDRESS ("bngrw.dll", "BngRwReqSendUrlTo")) { return 1; }
HOOK (u64, bngrw_ReqLed, PROC_ADDRESS ("bngrw.dll", "BngRwReqLed")) { return 1; }
HOOK (u64, bngrw_ReqBeep, PROC_ADDRESS ("bngrw.dll", "BngRwReqBeep")) { return 1; }
HOOK (u64, bngrw_ReqAction, PROC_ADDRESS ("bngrw.dll", "BngRwReqAction")) { return 1; }
HOOK (u64, bngrw_ReqLed, PROC_ADDRESS ("bngrw.dll", "BngRwReqLed"), u32 a1, u32 ledType, i64 a3, i64 a4) { return 1; }
HOOK (u64, bngrw_ReqBeep, PROC_ADDRESS ("bngrw.dll", "BngRwReqBeep"), u32 a1, u32 beepType, i64 a3, i64 a4) { return 1; }
HOOK (u64, bngrw_ReqAction, PROC_ADDRESS ("bngrw.dll", "BngRwReqAction"), u32 a1, u32 actionType, i64 a3, i64 a4) { return 1; }
HOOK (u64, bngrw_SetLedPower, PROC_ADDRESS ("bngrw.dll", "BngRwSetLedPower")) { return 0; }
HOOK (u64, bngrw_GetRetryCount, PROC_ADDRESS ("bngrw.dll", "BngRwGetTotalRetryCount")) { return 0; }
HOOK (u64, bngrw_GetFwVersion, PROC_ADDRESS ("bngrw.dll", "BngRwGetFwVersion")) { return 0; }
@@ -252,22 +277,28 @@ HOOK (i32, bngrw_ReqSendMail, PROC_ADDRESS ("bngrw.dll", "BngRwReqSendMailTo"))
HOOK (i32, bngrw_ReqLatchID, PROC_ADDRESS ("bngrw.dll", "BngRwReqLatchID")) { return 1; }
HOOK (u64, bngrw_ReqAiccAuth, PROC_ADDRESS ("bngrw.dll", "BngRwReqAiccAuth")) { return 1; }
HOOK (u64, bngrw_DevReset, PROC_ADDRESS ("bngrw.dll", "BngRwDevReset")) { return 1; }
HOOK (u64, bngrw_Attach, PROC_ADDRESS ("bngrw.dll", "BngRwAttach"), i32 a1, char *a2, i32 a3, i32 a4, i32 (*callback) (i32, i32, i32 *), i32 *a6) {
HOOK (u64, bngrw_Attach, PROC_ADDRESS ("bngrw.dll", "BngRwAttach"), i32 a1, char *a2, i32 a3, i32 a4, callbackAttach callback, i32 *_attachData) {
LogMessage (LOG_LEVEL_DEBUG, "BngRwAttach");
// This is way too fucking jank
attachCallback = callback;
attachData = a6;
attachData = _attachData;
return 1;
}
HOOK (u64, bngrw_ReqWaitTouch, PROC_ADDRESS ("bngrw.dll", "BngRwReqWaitTouch"), u32 a1, i32 a2, u32 a3, void (*callback) (i32, i32, u8[168], u64),
u64 a5) {
waitingForTouch = true;
touchCallback = callback;
touchData = a5;
for (auto plugin : plugins) {
FARPROC touchEvent = GetProcAddress (plugin, "WaitTouch");
if (touchEvent) ((waitTouchEvent *)touchEvent) (callback, a5);
HOOK (u64, bngrw_ReqWaitTouch, PROC_ADDRESS ("bngrw.dll", "BngRwReqWaitTouch"), u32 a1, i32 a2, u32 a3, callbackTouch _callback, u64 _touchData) {
LogMessage (LOG_LEVEL_DEBUG, "BngRwReqWaitTouch");
touchCallback = _callback;
if (emulateCardReader) {
waitingForTouch = true;
touchData = _touchData;
for (auto plugin : plugins) {
FARPROC touchEvent = GetProcAddress (plugin, "WaitTouch");
if (touchEvent) ((waitTouchEvent *)touchEvent) (_callback, _touchData);
}
return 1;
} else {
// This is called when we use an original card reader and acceptInvalidCards is set to true
return originalbngrw_ReqWaitTouch.call<u64> (a1, a2, a3, InspectWaitTouch, _touchData);
}
return 1;
}
void
@@ -278,12 +309,11 @@ Init () {
std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
if (config_ptr) {
toml_table_t *config = config_ptr.get ();
auto drum = openConfigSection (config, "drum");
if (drum) drumWaitPeriod = readConfigInt (drum, "wait_period", drumWaitPeriod);
auto controller = openConfigSection (config, "controller");
auto controller = openConfigSection (config, "controller");
if (controller) {
analogInput = readConfigBool (controller, "analog_input", analogInput);
if (analogInput) printf ("Using analog input mode. All the keyboard drum inputs have been disabled.\n");
drumWaitPeriod = readConfigInt (controller, "wait_period", drumWaitPeriod);
analogInput = readConfigBool (controller, "analog_input", analogInput);
if (analogInput) LogMessage (LOG_LEVEL_WARN, "Using analog input mode. All the keyboard drum inputs have been disabled.");
}
}
@@ -317,7 +347,7 @@ Init () {
if (!emulateUsio && !std::filesystem::exists (std::filesystem::current_path () / "bnusio_original.dll")) {
emulateUsio = true;
std::cerr << "[Init] bnusio_original.dll not found! usio emulation enabled" << std::endl;
LogMessage (LOG_LEVEL_ERROR, "bnusio_original.dll not found! usio emulation enabled");
}
if (!emulateUsio) {
@@ -367,7 +397,7 @@ Init () {
INSTALL_HOOK_DIRECT (bnusio_DecService, bnusio_DecService_Original);
INSTALL_HOOK_DIRECT (bnusio_ResetCoin, bnusio_ResetCoin_Original);
std::cout << "[Init] USIO emulation disabled" << std::endl;
LogMessage (LOG_LEVEL_WARN, "USIO emulation disabled");
}
if (emulateCardReader) {
@@ -393,7 +423,11 @@ Init () {
INSTALL_HOOK (bngrw_Attach);
INSTALL_HOOK (bngrw_DevReset);
} else {
std::cout << "[Init] Card reader emulation disabled" << std::endl;
LogMessage (LOG_LEVEL_WARN, "Card reader emulation disabled");
if (acceptInvalidCards) {
LogMessage (LOG_LEVEL_WARN, "Original reader will accept invalid cards!");
INSTALL_HOOK (bngrw_ReqWaitTouch);
}
}
}
@@ -422,48 +456,34 @@ Update () {
if (IsButtonTapped (TEST)) testEnabled = !testEnabled;
if (IsButtonTapped (EXIT)) ExitProcess (0);
if (waitingForTouch) {
static 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};
bool hasInserted = false;
if (IsButtonTapped (CARD_INSERT_1)) {
if (IsButtonTapped (CARD_INSERT_1) || IsButtonTapped (CARD_INSERT_2)) {
bool p1 = IsButtonTapped (CARD_INSERT_1);
static 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};
for (auto plugin : plugins) {
FARPROC insertEvent = GetProcAddress (plugin, "BeforeCard1Insert");
FARPROC insertEvent = GetProcAddress (plugin, p1 ? "BeforeCard1Insert" : "BeforeCard2Insert");
if (insertEvent) ((event *)insertEvent) ();
}
for (auto plugin : plugins) {
FARPROC insertEvent = GetProcAddress (plugin, "Card1Insert");
insertEvent = GetProcAddress (plugin, p1 ? "Card1Insert" : "Card2Insert");
if (insertEvent) {
((event *)insertEvent) ();
hasInserted = true;
break;
}
}
if (!hasInserted) {
memcpy (cardData + 0x2C, chipId1, 33);
memcpy (cardData + 0x50, accessCode1, 21);
touchCallback (0, 0, cardData, touchData);
}
} else if (IsButtonTapped (CARD_INSERT_2)) {
for (auto plugin : plugins) {
FARPROC insertEvent = GetProcAddress (plugin, "BeforeCard2Insert");
if (insertEvent) ((event *)insertEvent) ();
}
for (auto plugin : plugins) {
FARPROC insertEvent = GetProcAddress (plugin, "Card2Insert");
if (insertEvent) {
((event *)insertEvent) ();
hasInserted = true;
}
}
if (!hasInserted) {
memcpy (cardData + 0x2C, chipId2, 33);
memcpy (cardData + 0x50, accessCode2, 21);
LogMessage (LOG_LEVEL_INFO, "Inserting card for player %d: %s", p1 ? "1" : "2", p1 ? accessCode1 : accessCode2);
memcpy (cardData + 0x2C, p1 ? chipId1 : chipId2, 33);
memcpy (cardData + 0x50, p1 ? accessCode1 : accessCode2, 21);
touchCallback (0, 0, cardData, touchData);
}
}
@@ -486,5 +506,7 @@ Close () {
FARPROC exitEvent = GetProcAddress (plugin, "Exit");
if (exitEvent) ((event *)exitEvent) ();
}
CleanupLogger ();
}
} // namespace bnusio
+50 -27
View File
@@ -3,31 +3,36 @@
#include "helpers.h"
#include "patches/patches.h"
#include "poll.h"
#include "logger.h"
GameVersion gameVersion = GameVersion::UNKNOWN;
std::vector<HMODULE> plugins;
u64 song_data_size = 1024 * 1024 * 64;
void *song_data;
std::string server = "127.0.0.1";
std::string port = "54430";
std::string chassisId = "284111080000";
std::string shopId = "TAIKO ARCADE LOADER";
std::string gameVerNum = "00.00";
std::string countryCode = "JPN";
char fullAddress[256] = {'\0'};
char placeId[16] = {'\0'};
char accessCode1[21] = "00000000000000000001";
char accessCode2[21] = "00000000000000000002";
char chipId1[33] = "00000000000000000000000000000001";
char chipId2[33] = "00000000000000000000000000000002";
bool windowed = false;
bool autoIme = false;
bool jpLayout = false;
bool useLayeredFs = false;
bool emulateUsio = true;
bool emulateCardReader = true;
bool emulateQr = true;
std::string server = "127.0.0.1";
std::string port = "54430";
std::string chassisId = "284111080000";
std::string shopId = "TAIKO ARCADE LOADER";
std::string gameVerNum = "00.00";
std::string countryCode = "JPN";
char fullAddress[256] = {'\0'};
char placeId[16] = {'\0'};
char accessCode1[21] = "00000000000000000001";
char accessCode2[21] = "00000000000000000002";
char chipId1[33] = "00000000000000000000000000000001";
char chipId2[33] = "00000000000000000000000000000002";
bool windowed = false;
bool autoIme = false;
bool jpLayout = false;
bool useLayeredFs = false;
bool emulateUsio = true;
bool emulateCardReader = true;
bool emulateQr = true;
bool acceptInvalidCards = false;
std::string logLevelStr = "INFO";
bool logToFile = true;
HWND hGameWnd;
HOOK (i32, ShowMouse, PROC_ADDRESS ("user32.dll", "ShowCursor"), bool) { return originalShowMouse.call<i32> (true); }
@@ -120,6 +125,7 @@ GetGameVersion () {
void
CreateCard () {
LogMessage (LOG_LEVEL_INFO, "Creating card.ini");
const char hexCharacterTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char buf[64] = {0};
srand (time (nullptr));
@@ -140,8 +146,12 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
// This is bad, dont do this
// I/O in DllMain can easily cause a deadlock
std::string version = "auto";
auto configPath = std::filesystem::current_path () / "config.toml";
// Init logger for loading config
InitializeLogger (GetLogLevel (logLevelStr), logToFile);
LogMessage (LOG_LEVEL_INFO, "Loading config...");
std::string version = "auto";
std::filesystem::path 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) {
toml_table_t *config = config_ptr.get ();
@@ -167,9 +177,10 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
if (patches) version = readConfigString (patches, "version", version);
auto emulation = openConfigSection (config, "emulation");
if (emulation) {
emulateUsio = readConfigBool (emulation, "usio", emulateUsio);
emulateCardReader = readConfigBool (emulation, "card_reader", emulateCardReader);
emulateQr = readConfigBool (emulation, "qr", emulateQr);
emulateUsio = readConfigBool (emulation, "usio", emulateUsio);
emulateCardReader = readConfigBool (emulation, "card_reader", emulateCardReader);
acceptInvalidCards = readConfigBool (emulation, "accept_invalid", acceptInvalidCards);
emulateQr = readConfigBool (emulation, "qr", emulateQr);
}
auto graphics = openConfigSection (config, "graphics");
if (graphics) windowed = readConfigBool (graphics, "windowed", windowed);
@@ -178,8 +189,18 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
autoIme = readConfigBool (keyboard, "auto_ime", autoIme);
jpLayout = readConfigBool (keyboard, "jp_layout", jpLayout);
}
auto logging = openConfigSection (config, "logging");
if (logging) {
logLevelStr = readConfigString (logging, "log_level", logLevelStr);
logToFile = readConfigBool (logging, "log_to_file", logToFile);
}
}
// Update the logger with the level read from config file.
InitializeLogger (GetLogLevel (logLevelStr), logToFile);
LogMessage (LOG_LEVEL_INFO, "Application started.");
if (version == "auto") {
GetGameVersion ();
} else if (version == "JPN00") {
@@ -191,9 +212,11 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
} else if (version == "CHN00") {
gameVersion = GameVersion::CHN00;
} else {
LogMessage (LOG_LEVEL_ERROR, "GameVersion is UNKNOWN!");
MessageBoxA (nullptr, "Unknown patch version", nullptr, MB_OK);
ExitProcess (0);
}
LogMessage (LOG_LEVEL_INFO, "GameVersion is %s", GameVersionToString (gameVersion));
auto pluginPath = std::filesystem::current_path () / "plugins";
@@ -201,13 +224,13 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
for (const auto &entry : std::filesystem::directory_iterator (pluginPath)) {
if (entry.path ().extension () == ".dll") {
auto name = entry.path ().wstring ();
auto shortName = entry.path ().filename ().wstring ();
HMODULE hModule = LoadLibraryW (name.c_str ());
if (!hModule) {
wchar_t buf[128];
wsprintfW (buf, L"Failed to load plugin %ls", name.c_str ());
MessageBoxW (0, buf, name.c_str (), MB_ICONERROR);
LogMessage (LOG_LEVEL_ERROR, L"Failed to load plugin " + shortName);
} else {
plugins.push_back (hModule);
LogMessage (LOG_LEVEL_INFO, L"Loaded plugin " + shortName);
}
}
}
+69 -43
View File
@@ -1,8 +1,6 @@
#include "helpers.h"
#include <windows.h>
void *consoleHandle = 0;
static void
toml_myfree (void *p) {
if (p) {
@@ -14,13 +12,13 @@ toml_myfree (void *p) {
toml_table_t *
openConfig (std::filesystem::path path) {
if (!std::filesystem::exists (path) || !path.has_filename ()) {
printWarning ("%s (%s): file does not exist\n", __func__, path.string ().c_str ());
LogMessage (LOG_LEVEL_WARN, (std::string (path.string ()) + ": file does not exist").c_str ());
return 0;
}
std::ifstream stream (path);
if (!stream.is_open ()) {
printWarning ("%s (%s): could not open\n", __func__, path.string ().c_str ());
LogMessage (LOG_LEVEL_WARN, ("Could not open " + std::string (path.string ())).c_str ());
return 0;
}
@@ -37,7 +35,7 @@ openConfig (std::filesystem::path path) {
free (buf);
if (!config) {
printWarning ("%s (%s): %s\n", __func__, path.string ().c_str (), errorbuf);
LogMessage (LOG_LEVEL_WARN, (path.string () + ": " + errorbuf).c_str ());
return 0;
}
@@ -48,7 +46,7 @@ toml_table_t *
openConfigSection (toml_table_t *config, const std::string &sectionName) {
toml_table_t *section = toml_table_in (config, sectionName.c_str ());
if (!section) {
printWarning ("%s (%s): cannot find section\n", __func__, sectionName.c_str ());
LogMessage (LOG_LEVEL_ERROR, ("Cannot find section " + sectionName).c_str ());
return 0;
}
@@ -58,33 +56,42 @@ openConfigSection (toml_table_t *config, const std::string &sectionName) {
bool
readConfigBool (toml_table_t *table, const std::string &key, bool notFoundValue) {
toml_datum_t data = toml_bool_in (table, key.c_str ());
if (!data.ok) return notFoundValue;
if (!data.ok) {
LogMessage (LOG_LEVEL_WARN, ("Could not find Boolean named " + key).c_str ());
return notFoundValue;
}
return (bool)data.u.b;
}
int64_t
readConfigInt (toml_table_t *table, const std::string &key, int64_t notFoundValue) {
toml_datum_t data = toml_int_in (table, key.c_str ());
if (!data.ok) return notFoundValue;
if (!data.ok) {
LogMessage (LOG_LEVEL_WARN, ("Could not find Int named " + key).c_str ());
return notFoundValue;
}
return data.u.i;
}
const std::string
readConfigString (toml_table_t *table, const std::string &key, const std::string &notFoundValue) {
toml_datum_t data = toml_string_in (table, key.c_str ());
if (!data.ok) return notFoundValue;
if (!data.ok) {
LogMessage (LOG_LEVEL_WARN, ("Could not find String named " + key).c_str ());
return notFoundValue;
}
std::string str = data.u.s;
toml_myfree (data.u.s);
return str;
}
std::vector<int64_t>
readConfigIntArray (toml_table_t *table, const std::string &key, std::vector<int64_t> notFoundValue) {
toml_array_t *array = toml_array_in (table, key.c_str ());
if (!array) return notFoundValue;
if (!array) {
LogMessage (LOG_LEVEL_WARN, ("Could not find int Array named " + key).c_str ());
return notFoundValue;
}
std::vector<int64_t> datas;
for (int i = 0;; i++) {
@@ -96,32 +103,17 @@ readConfigIntArray (toml_table_t *table, const std::string &key, std::vector<int
return datas;
}
void
printColour (int colour, const char *format, ...) {
va_list args;
va_start (args, format);
if (consoleHandle == 0) consoleHandle = GetStdHandle (STD_OUTPUT_HANDLE);
SetConsoleTextAttribute (consoleHandle, colour);
vprintf (format, args);
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);
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);
tempStr.replace (pos, oldStrLen, newStr);
pos += newStrLen;
}
@@ -130,18 +122,52 @@ replace (const std::wstring orignStr, const std::wstring oldStr, const std::wstr
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);
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);
tempStr.replace (pos, oldStrLen, newStr);
pos += newStrLen;
}
return tempStr;
}
const char *
GameVersionToString (GameVersion version) {
switch (version) {
case GameVersion::JPN00: return "JPN00";
case GameVersion::JPN08: return "JPN08";
case GameVersion::JPN39: return "JPN39";
case GameVersion::CHN00: return "CHN00";
default: return "UNKNOWN";
}
}
const char *
languageStr (int language) {
switch (language) {
case 1: return "en_us";
case 2: return "cn_tw";
case 3: return "kor";
case 4: return "cn_cn";
default: return "jpn";
}
}
std::string
ConvertWideToUtf8 (const std::wstring &wstr) {
std::wstring_convert<std::codecvt_utf8<wchar_t> > converter;
return converter.to_bytes (wstr);
}
bool
AreAllBytesZero (const uint8_t *array, size_t offset, size_t length) {
for (size_t i = 0; i < length; ++i)
if (array[offset + i] != 0x00) return false;
return true;
}
+67 -16
View File
@@ -10,6 +10,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <toml.h>
#include "constants.h"
#include "logger.h"
typedef int8_t i8;
typedef int16_t i16;
@@ -59,8 +61,11 @@ const HMODULE MODULE_HANDLE = GetModuleHandle (nullptr);
void *where##functionName = NULL; \
void implOf##functionName (SafetyHookContext &ctx)
#define INSTALL_HOOK(functionName) \
{ original##functionName = safetyhook::create_inline (where##functionName, implOf##functionName); }
#define INSTALL_HOOK(functionName) \
{ \
LogMessage (LOG_LEVEL_DEBUG, (std::string ("Installing hook for ") + #functionName).c_str ()); \
original##functionName = safetyhook::create_inline (where##functionName, implOf##functionName); \
}
#define INSTALL_HOOK_DYNAMIC(functionName, location) \
{ \
@@ -68,8 +73,11 @@ const HMODULE MODULE_HANDLE = GetModuleHandle (nullptr);
INSTALL_HOOK (functionName); \
}
#define INSTALL_HOOK_DIRECT(location, locationOfHook) \
{ directHooks.push_back (safetyhook::create_inline ((void *)location, (void *)locationOfHook)); }
#define INSTALL_HOOK_DIRECT(location, locationOfHook) \
{ \
LogMessage (LOG_LEVEL_DEBUG, (std::string ("Installing direct hook for ") + #location).c_str ()); \
directHooks.push_back (safetyhook::create_inline ((void *)location, (void *)locationOfHook)); \
}
#define INSTALL_VTABLE_HOOK(className, object, functionName, functionIndex) \
{ \
@@ -77,8 +85,11 @@ const HMODULE MODULE_HANDLE = GetModuleHandle (nullptr);
INSTALL_HOOK (className##functionName); \
}
#define INSTALL_MID_HOOK(functionName) \
{ midHook##functionName = safetyhook::create_mid (where##functionName, implOf##functionName); }
#define INSTALL_MID_HOOK(functionName) \
{ \
LogMessage (LOG_LEVEL_DEBUG, (std::string ("Installing mid hook for ") + #functionName).c_str ()); \
midHook##functionName = safetyhook::create_mid (where##functionName, implOf##functionName); \
}
#define INSTALL_MID_HOOK_DYNAMIC(functionName, location) \
{ \
@@ -86,6 +97,50 @@ const HMODULE MODULE_HANDLE = GetModuleHandle (nullptr);
INSTALL_MID_HOOK (functionName); \
}
bool sendFlag = false;
#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 (); \
}
#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); \
} \
}
#define CHANGE_RESULT_SIZE_HOOK(functionName, location, target) \
MID_HOOK (functionName, location, SafetyHookContext &ctx) { \
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; \
}
#define CHANGE_RESULT_INDEX_HOOK(functionName, location, target, offset, skip) \
MID_HOOK (functionName, location, SafetyHookContext &ctx) { \
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; \
*(u32 *)(ctx.rsp + offset) = currentStageCount - 1; \
ctx.rip += skip; \
}
#define READ_MEMORY(location, type) *(type *)location
#define WRITE_MEMORY(location, type, ...) \
@@ -124,14 +179,7 @@ const HMODULE MODULE_HANDLE = GetModuleHandle (nullptr);
}
#define COUNTOFARR(arr) sizeof (arr) / sizeof (arr[0])
#define INFO_COLOUR FOREGROUND_GREEN
#define WARNING_COLOUR (FOREGROUND_RED | FOREGROUND_GREEN)
#define ERROR_COLOUR FOREGROUND_RED
#define printInfo(format, ...) printColour (INFO_COLOUR, format, __VA_ARGS__)
#define printWarning(format, ...) printColour (WARNING_COLOUR, format, __VA_ARGS__)
#define printError(format, ...) printColour (ERROR_COLOUR, format, __VA_ARGS__)
#define round(num) ((num > 0) ? (int)(num + 0.5) : (int)(num - 0.5))
#define round(num) ((num > 0) ? (int)(num + 0.5) : (int)(num - 0.5))
toml_table_t *openConfig (std::filesystem::path path);
toml_table_t *openConfigSection (toml_table_t *config, const std::string &sectionName);
@@ -139,7 +187,10 @@ bool readConfigBool (toml_table_t *table, const std::string &key, bool notFoundV
int64_t readConfigInt (toml_table_t *table, const std::string &key, int64_t notFoundValue);
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 = {};
std::vector<SafetyHookInline> directHooks = {};
const char *GameVersionToString (GameVersion version);
const char *languageStr (int language);
std::string ConvertWideToUtf8 (const std::wstring &wstr);
bool AreAllBytesZero (const uint8_t *array, size_t offset, size_t length);
+111
View File
@@ -0,0 +1,111 @@
/*
// Copyright (c) 2019 Onur Dundar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include "logger.h"
#include "helpers.h"
#include <time.h>
#include <stdlib.h>
#include <string.h>
static Logger *loggerInstance = NULL;
void *consoleHandle = 0;
std::mutex logMutex; // Mutex for thread-safe logging
char timeStr[64];
time_t rawtime;
struct tm *timeinfo;
SYSTEMTIME systemTime;
void
InitializeLogger (LogLevel level, bool logToFile) {
if (loggerInstance == nullptr) {
loggerInstance = (Logger *)malloc (sizeof (Logger));
if (consoleHandle == 0) consoleHandle = GetStdHandle (STD_OUTPUT_HANDLE);
}
loggerInstance->logLevel = level;
if (logToFile) {
loggerInstance->logFile = fopen ("TaikoArcadeLoader.log", "w"); // Open in write mode
if (!loggerInstance->logFile) LogMessage (LOG_LEVEL_WARN, "Failed to open TaikoArcadeLoader.log for writing.");
} else loggerInstance->logFile = nullptr; // No file logging
}
void
LogMessageHandler (const char *function, const char *codeFile, int codeLine, LogLevel messageLevel, const std::string format, ...) {
// Return if no logger or log level is too high
if (loggerInstance == nullptr || messageLevel > loggerInstance->logLevel) return;
// Lock for thread safety
std::lock_guard<std::mutex> lock (logMutex);
// Format the user-provided message
va_list args;
va_start (args, format);
int requiredSize = vsnprintf (nullptr, 0, format.c_str (), args) + 1; // +1 for null terminator
std::unique_ptr<char[]> buffer (new char[requiredSize]); // Allocate buffer dynamically
vsnprintf (buffer.get (), requiredSize, format.c_str (), args); // Format the string
std::string formattedMessage (buffer.get ()); // Convert to std::string
va_end (args);
// Determine log type string
std::string logType = GetLogLevelString (messageLevel);
// Get current time and milliseconds
SYSTEMTIME systemTime;
GetSystemTime (&systemTime);
time_t rawtime = time (nullptr);
struct tm *timeinfo = localtime (&rawtime);
std::ostringstream timeStamp;
timeStamp << std::put_time (timeinfo, "%Y/%m/%d %H:%M:%S") << "." << std::setw (3) << std::setfill ('0') << systemTime.wMilliseconds;
// Construct the log message
std::ostringstream logStream;
logStream << function << " (" << codeFile << ":" << codeLine << "): " << formattedMessage;
std::string logMessage = logStream.str ();
// Print the log message
std::cout << "[" << timeStamp.str () << "] "; // Timestamp
SetConsoleTextAttribute (consoleHandle, GetLogLevelColor (messageLevel)); // Set Level color
std::cout << logType; // Level
SetConsoleTextAttribute (consoleHandle, 4 | 6 | 7 | 9 | 10 | 13); // Reset console color
std::cout << logMessage << std::endl; // Log message
std::cout.flush (); // Flush to ensure immediate writing
if (loggerInstance->logFile) {
fprintf (loggerInstance->logFile, "[%s] %s%s\n", timeStamp.str ().c_str (), logType.c_str (), logMessage.c_str ());
fflush (loggerInstance->logFile); // Flush to ensure immediate writing
}
}
void
LogMessageHandler (const char *function, const char *codeFile, int codeLine, LogLevel messageLevel, const std::wstring format, ...) {
std::string utf8Message = ConvertWideToUtf8 (format); // Convert wide string to UTF-8
va_list args;
va_start (args, format);
LogMessageHandler (function, codeFile, codeLine, messageLevel, utf8Message, args); // Delegate to the original handler
va_end (args);
}
void
CleanupLogger () {
if (loggerInstance != NULL) {
if (loggerInstance->logFile) fclose (loggerInstance->logFile);
free (loggerInstance);
loggerInstance = NULL;
}
}
+65
View File
@@ -0,0 +1,65 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <stdio.h>
#include <string>
typedef enum { LOG_LEVEL_NONE = 0, LOG_LEVEL_ERROR = 1, LOG_LEVEL_WARN = 2, LOG_LEVEL_INFO = 3, LOG_LEVEL_DEBUG = 4, LOG_LEVEL_HOOKS = 5 } LogLevel;
/**
* Logger Struct Used to Store Logging Preferences and State
*/
typedef struct {
LogLevel logLevel;
FILE *logFile;
} Logger;
/* Initializes a global Logger instance. */
void InitializeLogger (LogLevel level, bool logToFile);
/* Logs a message with file and line information, if the log level permits. */
#define LogMessage(level, format, ...) LogMessageHandler (__FUNCTION__, __FILE__, __LINE__, level, format, ##__VA_ARGS__)
void LogMessageHandler (const char *function, const char *codeFile, int codeLine, LogLevel messageLevel, const std::string format, ...);
void LogMessageHandler (const char *function, const char *codeFile, int codeLine, LogLevel messageLevel, const std::wstring format, ...);
/* Converts a string to a LogLevel type. */
LogLevel
GetLogLevel (const std::string &logLevelStr) {
if (logLevelStr == "DEBUG") return LOG_LEVEL_DEBUG;
else if (logLevelStr == "INFO") return LOG_LEVEL_INFO;
else if (logLevelStr == "WARN") return LOG_LEVEL_WARN;
else if (logLevelStr == "ERROR") return LOG_LEVEL_ERROR;
else if (logLevelStr == "HOOKS") return LOG_LEVEL_HOOKS;
return LOG_LEVEL_NONE;
}
/* Converts a LogLevel type to a string for logging. */
std::string
GetLogLevelString (LogLevel messageLevel) {
switch (messageLevel) {
case LOG_LEVEL_DEBUG: return "DEBUG: ";
case LOG_LEVEL_INFO: return "INFO: ";
case LOG_LEVEL_WARN: return "WARN: ";
case LOG_LEVEL_ERROR: return "ERROR: ";
case LOG_LEVEL_HOOKS: return "HOOKS: ";
default: return "NONE: ";
}
}
int
GetLogLevelColor (LogLevel messageLevel) {
// Colors: https://i.sstatic.net/ZG625.png
switch (messageLevel) {
case LOG_LEVEL_DEBUG: return 9; // Pale Blue
case LOG_LEVEL_INFO: return 10; // Pale Green
case LOG_LEVEL_WARN: return 6; // Bright Yellow
case LOG_LEVEL_ERROR: return 4; // Bright RED
case LOG_LEVEL_HOOKS: return 13; // Pale Purple
default: return 7;
}
}
/* Cleans up the logger, closing files if necessary. */
void CleanupLogger ();
#endif /* LOGGER_H */
+27 -106
View File
@@ -290,38 +290,19 @@ public:
return this->refCount;
}
virtual int64_t Unk3 (uint32_t a1) {
// printf ("Unk3 called\n");
return 1;
}
virtual int64_t Unk3 (uint32_t a1) { return 1; }
virtual int64_t Unk4 () {
// printf ("Unk4 called\n");
return 1;
}
virtual int64_t Unk4 () { return 1; }
virtual int32_t Unk5 () {
// printf ("Unk5 called\n");
return 0;
}
virtual int32_t Unk5 () { return 0; }
virtual int64_t Unk6 () {
// printf ("Unk6 called\n");
return 1;
}
virtual int64_t Unk6 () { return 1; }
virtual int32_t Unk7 () {
// printf ("Unk7 called\n");
return 0;
}
virtual int32_t Unk7 () { return 0; }
virtual int32_t Unk8 () {
// printf ("Unk8 called\n");
return 0;
}
virtual int32_t Unk8 () { return 0; }
virtual int32_t IAuth_GetUpdaterState (amcus_state_t *arr) {
// printf("IAuth_GetUpdaterState called\n");
memset (arr, 0, sizeof (*arr));
// Convert gameVerNum from string to double
double ver_d = std::stod (gameVerNum.c_str ());
@@ -378,7 +359,6 @@ public:
}
virtual int32_t IAuth_GetCabinetConfig (amcus_network_state_t *state) {
// printf("IAuth_GetCabinetConfig called\n");
memset (state, 0, sizeof (*state));
strcpy_s (state->mode, "STANDALONE");
strcpy_s (state->pcbid, "ABLN1080001");
@@ -400,7 +380,6 @@ public:
}
virtual int32_t IAuth_GetVersionInfo (amcus_version_info_t *version) {
// printf("IAuth_GetVersionInfo called\n");
memset (version, 0, sizeof (*version));
strcpy_s (version->game_rev, "1");
strcpy_s (version->auth_type, "ALL.NET");
@@ -414,18 +393,11 @@ public:
return 0;
}
virtual int32_t Unk12 () {
// printf ("Unk12 called\n");
return 1;
}
virtual int32_t Unk12 () { return 1; }
virtual int32_t Unk13 () {
// printf ("Unk13 called\n");
return 1;
}
virtual int32_t Unk13 () { return 1; }
virtual int32_t IAuth_GetAuthServerResp (amcus_auth_server_resp_t *resp) {
// printf("IAuth_GetAuthServerResp called\n");
memset (resp, 0, sizeof (*resp));
strcpy_s (resp->uri, fullAddress);
strcpy_s (resp->host, fullAddress);
@@ -447,24 +419,13 @@ public:
return 0;
}
virtual int32_t Unk15 () {
// printf ("Unk15 called\n");
return 0;
}
virtual int32_t Unk15 () { return 0; }
virtual int32_t Unk16 () {
// printf ("Unk16 called\n");
return 0;
}
virtual int32_t Unk16 () { return 0; }
virtual int32_t Unk17 () {
// printf ("Unk17 called\n");
return 0;
}
virtual int32_t Unk17 () { return 0; }
virtual int32_t IAuth_GetMuchaAuthResponse (mucha_boardauth_resp_t *arr) {
// printf("IAuth_GetMuchaAuthResponse called\n");
memset (arr, 0, sizeof (*arr));
strcpy_s (arr->shop_name, sizeof (arr->shop_name), shopId.c_str ());
strcpy_s (arr->shop_name_en, sizeof (arr->shop_name_en), shopId.c_str ());
@@ -500,80 +461,38 @@ public:
}
virtual int32_t Unk19 (uint8_t *a1) {
// printf("Unk19 called\n");
memset (a1, 0, 0x38);
a1[0] = 1;
return 1;
}
virtual int32_t Unk20 () {
// printf ("Unk20 called\n");
return 0;
}
virtual int32_t Unk20 () { return 0; }
virtual int32_t Unk21 () {
// printf ("Unk21 called\n");
return 1;
}
virtual int32_t Unk21 () { return 1; }
virtual int32_t Unk22 () {
// printf ("Unk22 called\n");
return 0;
}
virtual int32_t Unk22 () { return 0; }
virtual int32_t Unk23 () {
// printf ("Unk23 called\n");
return 0;
}
virtual int32_t Unk23 () { return 0; }
virtual int32_t Unk24 () {
// printf ("Unk24 called\n");
return 0;
}
virtual int32_t Unk24 () { return 0; }
virtual int32_t Unk25 () {
// printf ("Unk25 called\n");
return 1;
}
virtual int32_t Unk25 () { return 1; }
virtual int32_t Unk26 () {
// printf ("Unk26 called\n");
return 0;
}
virtual int32_t Unk26 () { return 0; }
virtual int32_t Unk27 () {
// printf ("Unk27 called\n");
return 1;
}
virtual int32_t Unk27 () { return 1; }
virtual int32_t Unk28 () {
// printf ("Unk28 called\n");
return 0;
}
virtual int32_t Unk28 () { return 0; }
virtual int32_t Unk29 () {
// printf ("Unk29 called\n");
return 0;
}
virtual int32_t Unk29 () { return 0; }
virtual int32_t Unk30 () {
// printf ("Unk30 called\n");
return 0;
}
virtual int32_t Unk30 () { return 0; }
virtual int32_t PrintDebugInfo () {
// printf ("Unk31 called\n");
return 0;
}
virtual int32_t PrintDebugInfo () { return 0; }
virtual int32_t Unk32 (void *a1) {
// printf ("Unk32 called\n");
return 0;
}
virtual int32_t Unk32 (void *a1) { return 0; }
virtual void Unk33 () {
// printf ("Unk33 called\n");
}
virtual void Unk33 () {}
public:
CAuth () {}
@@ -639,6 +558,8 @@ CoCreateInstanceHook (const IID *const rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsC
void
Init () {
LogMessage (LOG_LEVEL_DEBUG, "Init AmAuth patches");
g_origCoCreateInstance = safetyhook::create_inline (PROC_ADDRESS ("ole32.dll", "CoCreateInstance"), CoCreateInstanceHook);
struct addrinfo *res = 0;
+5
View File
@@ -23,6 +23,8 @@ bool asio = false;
std::string asioDriver = "";
HOOK_DYNAMIC (i64, NUSCDeviceInit, void *a1, nusc_init_config_t *a2, nusc_init_config_t *a3, void *a4) {
LogMessage (LOG_LEVEL_INFO, (std::string ("Device mode is ") + (asio ? "ASIO" : (wasapiShared ? "wasapi shared" : "wasapi exclusive"))).c_str ());
if (asio) LogMessage (LOG_LEVEL_INFO, (std::string ("ASIO driver is ") + asioDriver).c_str ());
a2->device_mode = asio;
a2->asio_driver_name = asio ? asioDriver.c_str () : "";
a2->wasapi_exclusive = asio ? 1 : wasapiShared ? 0 : 1;
@@ -31,6 +33,7 @@ HOOK_DYNAMIC (i64, NUSCDeviceInit, void *a1, nusc_init_config_t *a2, nusc_init_c
HOOK_DYNAMIC (bool, LoadASIODriver, void *a1, const char *a2) {
auto result = originalLoadASIODriver.call<bool> (a1, a2);
if (!result) {
LogMessage (LOG_LEVEL_ERROR, (std::string ("Failed to load ASIO driver ") + asioDriver).c_str ());
MessageBoxA (nullptr, "Failed to load ASIO driver", nullptr, MB_OK);
ExitProcess (0);
}
@@ -39,6 +42,8 @@ HOOK_DYNAMIC (bool, LoadASIODriver, void *a1, const char *a2) {
void
Init () {
LogMessage (LOG_LEVEL_DEBUG, "Init Audio patches");
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) {
+1
View File
@@ -198,6 +198,7 @@ D3D11CreateDeviceAndSwapChainWrap (IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE Drive
void
Init () {
LogMessage (LOG_LEVEL_DEBUG, "Init Dxgi patches");
i32 fpsLimit = 120;
auto configPath = std::filesystem::current_path () / "config.toml";
+1
View File
@@ -16,6 +16,7 @@ static u32 i = 0;
void
Init (float fpsLimit) {
if (fpsLimit > 0) LogMessage (LOG_LEVEL_DEBUG, "Init Fps Limiter");
targetFrameTime = 1000.0 / fpsLimit;
}
+45 -56
View File
@@ -3,22 +3,21 @@
#include <zlib.h>
bool useLayeredFs = false;
std::string datatableKey = "0000000000000000000000000000000000000000000000000000000000000000";
std::string fumenKey = "0000000000000000000000000000000000000000000000000000000000000000";
std::string datatableKey = "3530304242323633353537423431384139353134383346433246464231354534";
std::string fumenKey = "4434423946383537303842433443383030333843444132343339373531353830";
#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::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 = {};
std::vector<RegisteredHandler *> afterHandlers = {};
uint32_t
CRC32C (uint32_t crc, const unsigned char *buf, size_t len) {
@@ -161,12 +160,10 @@ EncryptFile (const std::string &input_file, const std::string &hex_key) {
bool
IsFumenEncrypted (const std::string &filename) {
// Check if the filename ends with ".bin"
if (filename.size() < 4 || filename.substr(filename.size() - 4) != ".bin") {
std::cout << "Not a Fumen file: " << filename << std::endl;
if (filename.size () < 4 || filename.substr (filename.size () - 4) != ".bin")
return true; // If it doesn't we return early, as the file we're seeing isn't a fumen !
}
std::ifstream file(filename, std::ios::binary);
std::ifstream file (filename, std::ios::binary);
file.seekg (0x214, std::ios::beg);
std::vector<unsigned char> buffer (24);
file.read (reinterpret_cast<char *> (buffer.data ()), buffer.size ());
@@ -180,7 +177,7 @@ IsFumenEncrypted (const std::string &filename) {
std::string
LayeredFsHandler (const std::string originalFileName, const std::string currentFileName) {
std::filesystem::path path (originalFileName.c_str());
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";
@@ -197,22 +194,24 @@ LayeredFsHandler (const std::string originalFileName, const std::string currentF
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;
LogMessage (LOG_LEVEL_DEBUG, ("Redirecting " + std::filesystem::relative (path).string ()).c_str ());
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) {
std::cout << "Encrypting " << std::filesystem::relative (newPath) << std::endl; // If we don't we encrypt the file
LogMessage (LOG_LEVEL_DEBUG,
("Encrypting " + std::filesystem::relative (newPath).string ()).c_str ()); // If we don't we encrypt the file
std::ifstream crc_file (newPath, std::ios::binary);
std::vector<uint8_t> crc_vector ((std::istreambuf_iterator<char> (crc_file)), std::istreambuf_iterator<char> ());
uint32_t crc = CRC32C (0, crc_vector.data (), crc_vector.size ());
WriteFile (encPath, EncryptFile (newPath, fumenKey), crc); // And we save it
} else {
std::cout << "Missing or invalid fumen key: " << std::filesystem::relative (newPath) << " couldn't be encrypted."
<< std::endl;
LogMessage (
LOG_LEVEL_ERROR,
("Missing or invalid fumen key: " + std::filesystem::relative (newPath).string () + " couldn't be encrypted.").c_str ());
encPath = path.string ();
}
} else std::cout << "Using cached file for " << std::filesystem::relative (newPath) << std::endl;
} else LogMessage (LOG_LEVEL_DEBUG, ("Using cached file for: " + std::filesystem::relative (newPath).string ()).c_str ());
return encPath;
}
}
@@ -232,58 +231,55 @@ LayeredFsHandler (const std::string originalFileName, const std::string currentF
if (!std::filesystem::exists (encPath) || crcBool) { // And if it hasn't been encrypted before
if (datatableKey.length () == 64) {
std::cout << "Encrypting " << std::filesystem::relative (json_path) << std::endl; // Encrypt the file
// Encrypt the file
LogMessage (LOG_LEVEL_DEBUG, ("Encrypting " + std::filesystem::relative (json_path).string ()).c_str ());
std::ifstream crc_file (json_path.string (), std::ios::binary);
std::vector<uint8_t> crc_vector ((std::istreambuf_iterator<char> (crc_file)), std::istreambuf_iterator<char> ());
uint32_t crc = CRC32C (0, crc_vector.data (), crc_vector.size ());
WriteFile (encPath, EncryptFile (json_path.string (), datatableKey), crc); // And save it
} else {
std::cout << "Missing or invalid datatable key: " << std::filesystem::relative (newPath) << " couldn't be encrypted."
<< std::endl;
LogMessage (
LOG_LEVEL_ERROR,
("Missing or invalid datatable key: " + std::filesystem::relative (newPath).string () + " couldn't be encrypted.").c_str ());
encPath = path.string ();
}
} else
std::cout << "Using cached file for " << std::filesystem::relative (json_path)
<< std::endl; // Otherwise use the already encrypted file.
// Otherwise use the already encrypted file.
LogMessage (LOG_LEVEL_DEBUG, ("Using cached file for: " + std::filesystem::relative (json_path).string ()).c_str ());
return encPath;
}
}
return "";
return ""; // we return an empty string, causing the rest of CreateFileAHook to not update the returned value
}
HOOK (
HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"),
LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile
) {
// std::wcout << "CreateFileA: file " << lpFileName << std::endl;
std::string originalFileName = std::string(lpFileName);
std::string currentFileName = originalFileName;
HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
std::string originalFileName = std::string (lpFileName);
std::string currentFileName = originalFileName;
LogMessage (LOG_LEVEL_HOOKS, ("CreateFileA: " + originalFileName).c_str ());
if (!beforeHandlers.empty()) {
if (!beforeHandlers.empty ()) {
for (auto handler : beforeHandlers) {
std::string result = handler->handlerMethod(originalFileName, currentFileName);
std::string result = handler->handlerMethod (originalFileName, currentFileName);
if (result != "") currentFileName = result;
}
}
if (useLayeredFs) {
std::string result = LayeredFsHandler(originalFileName, currentFileName);
std::string result = LayeredFsHandler (originalFileName, currentFileName);
if (result != "") currentFileName = result;
}
}
if (!afterHandlers.empty()) {
if (!afterHandlers.empty ()) {
for (auto handler : afterHandlers) {
std::string result = handler->handlerMethod(originalFileName, currentFileName);
std::string result = handler->handlerMethod (originalFileName, currentFileName);
if (result != "") currentFileName = result;
}
}
return originalCreateFileAHook.call<HANDLE> (
currentFileName.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile
);
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,
@@ -315,34 +311,27 @@ HOOK (
void
Init () {
LogMessage (LOG_LEVEL_DEBUG, "Init LayeredFs patches");
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);
}
auto layeredFs = openConfigSection (config_ptr.get (), "layeredfs");
if (layeredFs) useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
}
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));
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));
RegisterAfter (const std::function<std::string (const std::string, const std::string)> &fileHandler) {
afterHandlers.push_back (new RegisteredHandler (fileHandler));
}
} // namespace patches::LayeredFs
+6 -6
View File
@@ -35,16 +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);
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);
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);
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
+19 -24
View File
@@ -62,8 +62,7 @@ HOOK_DYNAMIC (bool, Send3, i64, char) { return true; }
HOOK_DYNAMIC (bool, Send4, i64, const void *, i64) { return true; }
HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
if (gState == State::CopyWait) {
std::cout << "Copy data, length: " << length << std::endl;
// std::cout << "Copy data, length: " << length << 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);
@@ -114,9 +113,10 @@ HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
byteBuffer.push_back (0xEE);
byteBuffer.push_back (0xFF);
std::stringstream hexStream;
for (auto byteData : byteBuffer)
std::cout << std::hex << std::uppercase << std::setfill ('0') << std::setw (2) << static_cast<int> (byteData) << " ";
std::cout << std::endl;
hexStream << std::hex << std::uppercase << std::setfill ('0') << std::setw (2) << static_cast<int> (byteData) << " ";
LogMessage (LOG_LEVEL_INFO, ("Data dump: " + hexStream.str ()).c_str ());
memcpy (dest, byteBuffer.data (), byteBuffer.size ());
gState = State::Ready;
@@ -132,8 +132,7 @@ HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
std::u8string u8PathStr (imagePath.begin (), imagePath.end ());
std::filesystem::path u8Path (u8PathStr);
if (!std::filesystem::is_regular_file (u8Path)) {
std::cerr << "Failed to open image: " << u8Path.string () << " (file not found)"
<< "\n";
LogMessage (LOG_LEVEL_ERROR, ("Failed to open image: " + u8Path.string () + " (file not found)").c_str ());
gState = State::Ready;
return 0;
}
@@ -142,8 +141,7 @@ HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
std::unique_ptr<stbi_uc, void (*) (void *)> buffer (stbi_load (u8Path.string ().c_str (), &width, &height, &channels, 3),
stbi_image_free);
if (!buffer) {
std::cerr << "Failed to read image: " << u8Path << " (" << stbi_failure_reason () << ")"
<< "\n";
LogMessage (LOG_LEVEL_ERROR, ("Failed to read image: " + u8Path.string () + " (" + stbi_failure_reason () + ")").c_str ());
gState = State::Ready;
return 0;
}
@@ -151,15 +149,14 @@ HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
ZXing::ImageView image{buffer.get (), width, height, ZXing::ImageFormat::RGB};
auto result = ReadBarcode (image);
if (!result.isValid ()) {
std::cerr << "Failed to read QR: " << imagePath << " (" << ToString (result.error ()) << ")"
<< "\n";
LogMessage (LOG_LEVEL_ERROR, ("Failed to read QR: " + imagePath + " (" + ToString (result.error ()) + ")").c_str ());
gState = State::Ready;
return 0;
}
std::cout << "Valid" << std::endl;
// std::cout << "Valid" << std::endl;
auto byteData = result.bytes ();
std::cout << ZXing::ToHex (byteData) << std::endl;
// std::cout << ZXing::ToHex (byteData) << std::endl;
auto dataSize = byteData.size ();
memcpy (dest, byteData.data (), dataSize);
@@ -171,12 +168,13 @@ HOOK_DYNAMIC (i64, CopyData, i64, void *dest, int length) {
unsigned char plugin_data[length];
int buf_len = ((getQrEvent *)getEvent) (length, plugin_data);
if (0 < buf_len && buf_len <= length) {
std::stringstream hexStream;
for (int i = 0; i < buf_len; i++)
std::cout << std::hex << std::uppercase << std::setfill ('0') << std::setw (2) << static_cast<int> (plugin_data[i]) << " ";
std::cout << std::endl;
hexStream << std::hex << std::uppercase << std::setfill ('0') << std::setw (2) << static_cast<int> (plugin_data[i]) << " ";
LogMessage (LOG_LEVEL_INFO, ("QR dump: " + hexStream.str ()).c_str ());
memcpy (dest, plugin_data, buf_len);
} else {
std::cerr << "QR discard! Length invalid: " << buf_len << ", valid range: 0~" << length << std::endl;
LogMessage (LOG_LEVEL_ERROR, ("QR discard! Length invalid: " + std::to_string (buf_len) + ", valid range: 0~").c_str ());
}
gState = State::Ready;
return buf_len;
@@ -198,35 +196,29 @@ void
Update () {
if (!emulateQr) return;
if (gState == State::Ready) {
// std::cout << "Insert" << std::endl;
if (IsButtonTapped (CARD_INSERT_1)) {
if (gameVersion != GameVersion::CHN00) return;
std::cout << "Insert" << std::endl;
accessCode = "BNTTCNID";
accessCode += accessCode1;
gState = State::CopyWait;
gMode = Mode::Card;
} else if (IsButtonTapped (CARD_INSERT_2)) {
if (gameVersion != GameVersion::CHN00) return;
std::cout << "Insert" << std::endl;
accessCode = "BNTTCNID";
accessCode += accessCode2;
gState = State::CopyWait;
gMode = Mode::Card;
} else if (IsButtonTapped (QR_DATA_READ)) {
std::cout << "Insert" << std::endl;
gState = State::CopyWait;
gMode = Mode::Data;
} else if (IsButtonTapped (QR_IMAGE_READ)) {
std::cout << "Insert" << std::endl;
gState = State::CopyWait;
gMode = Mode::Image;
} else if (qrPluginRegistered) {
for (auto plugin : qrPlugins) {
FARPROC checkEvent = GetProcAddress (plugin, "CheckQr");
if (checkEvent && ((checkQrEvent *)checkEvent) ()) {
std::cout << "Insert" << std::endl;
gState = State::CopyWait;
gMode = Mode::Plugin;
gPlugin = plugin;
@@ -239,8 +231,10 @@ Update () {
void
Init () {
LogMessage (LOG_LEVEL_DEBUG, "Init Qr patches");
if (!emulateQr) {
std::cout << "[Init] QR emulation disabled" << std::endl;
LogMessage (LOG_LEVEL_WARN, "QR emulation disabled");
return;
}
@@ -252,7 +246,8 @@ Init () {
if (usingQrEvent) qrPlugins.push_back (plugin);
}
if (qrPlugins.size () > 0) {
std::cout << "QR plugin found!" << std::endl;
LogMessage (LOG_LEVEL_INFO, "QR plugin found!");
qrPluginRegistered = true;
}
+195 -171
View File
@@ -11,281 +11,303 @@ 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;
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;
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<RegisteredItem *> registeredItems = {};
std::vector<RegisteredModify *> registeredModifies = {};
std::wstring moddedInitial = L"";
std::wstring modded = L"";
std::wstring moddedInitial = L"";
std::wstring modded = L"";
u64 appAccessor = 0;
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) {
LogMessage (LOG_LEVEL_DEBUG, L"Create Menu " + menuName);
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");
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");
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";
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");
if (menuItem.load_string (itemLine.c_str ())) menuCenter.append_copy (menuItem.first_child ().first_child ());
else LogMessage (LOG_LEVEL_ERROR, L"Failed to parse option line: " + item);
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();
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";
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());
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 (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")) {
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;
if (!doc.load_file (fileName.c_str ())) {
LogMessage (LOG_LEVEL_ERROR, L"Loading DeviceInitialize structure failed! path: " + fileName);
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");
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());
doc.save_file (modFileName.c_str ());
moddedInitial = modFileName;
fileName = 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()) {
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;
if (!doc.load_file (fileName.c_str ())) {
LogMessage (LOG_LEVEL_ERROR, L"Loading TestMode structure failed! path: " + fileName);
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);
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();
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);
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()) {
if (!registeredModifies.empty ()) {
for (RegisteredModify *modify : registeredModifies) {
pugi::xpath_query modifyQuery = pugi::xpath_query(modify->query.c_str());
pugi::xpath_query modifyQuery = pugi::xpath_query (modify->query.c_str ());
try {
pugi::xml_node modifyNode = doc.select_node(modifyQuery).node();
pugi::xml_node modifyNode = doc.select_node (modifyQuery).node ();
if (modifyNode) {
modify->nodeModify(modifyNode);
modify->registerInit();
modify->nodeModify (modifyNode);
modify->registerInit ();
}
} catch (std::exception &e) {
std::wcerr << "[TestMode Error] failed to find node by xpath: " << modify->query << std::endl;
}
} catch (std::exception &e) { LogMessage (LOG_LEVEL_ERROR, L"Failed to find node by xpath: " + modify->query); }
}
}
doc.save_file(modFileName.c_str());
modded = modFileName;
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());
LogMessage (LOG_LEVEL_DEBUG, L"TestModeLibrary load: " + fileName);
originalTestModeSetMenuHook.call<void> (testModeLibrary, fileName.c_str ());
}
void
CommonModify () {
// Default off Close time
TestMode::RegisterModify(
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"); }, [&](){}
);
[&] (pugi::xml_node &node) { node.attribute (L"default").set_value (L"0"); }, [&] () {});
}
void
LocalizationCHT () {
TestMode::RegisterModify(
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(
[&] (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(
[&] (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(
[&] (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(
[&] (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(
[&] (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(
[&] (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(
[&] (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"離開"); }, [](){}
);
[&] (pugi::xml_node &node) { node.attribute (L"label").set_value (L"離開"); }, [] () {});
}
void
LocalizationCHS () {
TestMode::RegisterModify(
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(
[&] (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(
[&] (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(
[&] (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(
[&] (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(
[&] (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:仅刷卡"); }, [](){}
);
[&] (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:仅刷卡"); }, [](){}
// [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"青春之达人模式"); node.attribute(L"replace-text").set_value(L"0:默认,
// 1:启用, 2:仅刷卡"); }, [](){}
// );
TestMode::RegisterModify(
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"离开"); }, [](){}
);
[&] (pugi::xml_node &node) { node.attribute (L"label").set_value (L"离开"); }, [] () {});
}
void
Init () {
LogMessage (LOG_LEVEL_DEBUG, "Init TestMode patches");
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);
}
}
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;
}
if (chsPatch) LocalizationCHT ();
} break;
case GameVersion::CHN00: break;
}
CommonModify ();
@@ -300,34 +322,36 @@ SetupAccessor (u64 appAccessor, RefTestModeMain refTestMode) {
}
int
ReadTestModeValue (const wchar_t* itemId) {
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);
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;
LogMessage (LOG_LEVEL_ERROR, (std::wstring (L"Read TestMode(") + itemId + L") failed!").c_str ());
return -1;
}
void
RegisterItem (const std::wstring item, const std::function<void()> &initMethod) {
registeredItems.push_back(new RegisteredItem(item, initMethod));
RegisterItem (const std::wstring item, const std::function<void ()> &initMethod) {
LogMessage (LOG_LEVEL_DEBUG, L"Register Item " + item);
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));
RegisterModify (const std::wstring query, const std::function<void (pugi::xml_node &)> &nodeModify, const std::function<void ()> &initMethod) {
LogMessage (LOG_LEVEL_DEBUG, L"Register Modify " + query);
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;
std::wstring attrValue = std::wstring (attribute.value ()) + append;
attribute.set_value (attrValue.c_str ());
}
}
+22 -18
View File
@@ -5,7 +5,7 @@
extern std::string chassisId;
namespace patches::CHN00 {
int language = 0;
u8 *haspBuffer;
HOOK (i32, HaspDecrypt, PROC_ADDRESS ("hasp_windows_x64.dll", "hasp_decrypt")) { return 0; }
HOOK (i32, HaspEncrypt, PROC_ADDRESS ("hasp_windows_x64.dll", "hasp_encrypt")) { return 0; }
@@ -35,38 +35,42 @@ lua_pushtrue (i64 a1) {
return 1;
}
HOOK (i64, AvailableMode_Dani_AI, ASLR (0x1401AC550), i64 a1) { return lua_pushtrue (a1); }
HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402BFF70), i64 *, i64 a2) { return lua_pushtrue (a2); }
HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402BC9B0), i64 a1) { return lua_pushtrue (a1); }
int language = 0;
const char *
languageStr () {
switch (language) {
case 1: return "en_us";
case 2: return "cn_tw";
case 3: return "kor";
case 4: return "cn_cn";
default: return "jpn";
}
HOOK (i64, AvailableMode_Dani_AI, ASLR (0x1401AC550), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "AvailableMode_Dani_AI was called");
return lua_pushtrue (a1);
}
HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402BFF70), i64 *, i64 a2) {
LogMessage (LOG_LEVEL_HOOKS, "AvailableMode_Collabo025 was called");
return lua_pushtrue (a2);
}
HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402BC9B0), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "AvailableMode_Collabo026 was called");
return lua_pushtrue (a1);
}
HOOK (i64, GetLanguage, ASLR (0x140023720), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "GetLanguage was called");
auto result = originalGetLanguage.call<i64> (a1);
language = *((u32 *)result);
return result;
}
HOOK (i64, GetRegionLanguage, ASLR (0x1401AC300), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "GetRegionLanguage was called");
lua_settop (a1, 0);
lua_pushstring (a1, (u64)languageStr ());
lua_pushstring (a1, (u64)languageStr (language));
return 1;
}
HOOK (i64, GetCabinetLanguage, ASLR (0x1401AF270), i64, i64 a2) {
LogMessage (LOG_LEVEL_HOOKS, "GetCabinetLanguage was called");
lua_settop (a2, 0);
lua_pushstring (a2, (u64)languageStr ());
lua_pushstring (a2, (u64)languageStr (language));
return 1;
}
HOOK_DYNAMIC (char, AMFWTerminate, i64) { return 0; }
HOOK_DYNAMIC (char, AMFWTerminate, i64) {
LogMessage (LOG_LEVEL_HOOKS, "AMFWTerminate was called");
return 0;
}
const i32 datatableBufferSize = 1024 * 1024 * 12;
safetyhook::Allocation datatableBuffer1;
-9
View File
@@ -2,15 +2,6 @@
#include "../patches.h"
#include <safetyhook.hpp>
extern u64 song_data_size;
extern void *song_data;
#define RDX_MOV 0x48, 0xBA
#define R8_MOV 0x49, 0xB8
#define GENERATE_MOV(instruction, location) \
instruction, (u8)(u64)(location), (u8)((u64)(location) >> 8), (u8)((u64)(location) >> 16), (u8)((u64)(location) >> 24), \
(u8)((u64)(location) >> 32), (u8)((u64)(location) >> 40), (u8)((u64)(location) >> 48), (u8)((u64)(location) >> 56)
namespace patches::JPN00 {
HOOK_DYNAMIC (char, AMFWTerminate, i64) { return 0; }
+99 -147
View File
@@ -4,11 +4,14 @@
#include <safetyhook.hpp>
namespace patches::JPN39 {
HOOK_DYNAMIC (char, AMFWTerminate, i64) { return 0; }
int language = 0;
HOOK_DYNAMIC (char, AMFWTerminate, i64) {
LogMessage (LOG_LEVEL_HOOKS, "AMFWTerminate was called");
return 0;
}
HOOK_DYNAMIC (i64, curl_easy_setopt, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5) {
// printf ("garmc curl_easy_setopt\n");
LogMessage (LOG_LEVEL_HOOKS, "Garmc curl_easy_setopt was called");
originalcurl_easy_setopt.call<i64> (a1, 64, 0, 0, 0);
originalcurl_easy_setopt.call<i64> (a1, 81, 0, 0, 0);
return originalcurl_easy_setopt.call<i64> (a1, a2, a3, a4, a5);
@@ -41,9 +44,10 @@ lua_pushbool (i64 a1, bool val) {
return 1;
}
u64 appAccessor = 0;
u64 appAccessor = 0;
u64 componentAccessor = 0;
HOOK (i64, DeviceCheck, ASLR(0x140464FC0), i64 a1, i64 a2, i64 a3) {
HOOK (i64, DeviceCheck, ASLR (0x140464FC0), i64 a1, i64 a2, i64 a3) {
LogMessage (LOG_LEVEL_HOOKS, "DeviceCheck was called");
TestMode::SetupAccessor (a3, RefTestModeMain);
componentAccessor = a2;
return originalDeviceCheck.call<i64> (a1, a2, a3);
@@ -53,55 +57,53 @@ int
GetUserStatus () {
if (appAccessor) {
u64 playDataManager = RefPlayDataManager (appAccessor);
if (playDataManager) return GetUserCount(playDataManager);
if (playDataManager) return GetUserCount (playDataManager);
}
return -1;
}
HOOK (i64, AvailableMode_Collabo024, ASLR (0x1402DE710), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "AvailableMode_Collabo024 was called");
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);
}
if (status == 1 && GetUserStatus () == 1) return lua_pushbool (a1, true);
return originalAvailableMode_Collabo024.call<i64> (a1);
}
HOOK (i64, AvailableMode_Collabo025, ASLR (0x1402DE6B0), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "AvailableMode_Collabo025 was called");
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);
}
if (status == 1 && GetUserStatus () == 1) return lua_pushbool (a1, true);
return originalAvailableMode_Collabo025.call<i64> (a1);
}
HOOK (i64, AvailableMode_Collabo026, ASLR (0x1402DE670), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "AvailableMode_Collabo026 was called");
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);
}
if (status == 1 && GetUserStatus () == 1) return lua_pushbool (a1, true);
return originalAvailableMode_Collabo026.call<i64> (a1);
}
HOOK (i64, AvailableMode_AprilFool001, ASLR (0x1402DE5B0), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "AvailableMode_AprilFool001 was called");
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);
}
if (status == 1) return lua_pushbool (a1, true);
return originalAvailableMode_AprilFool001.call<i64> (a1);
}
i64 __fastcall lua_freeze_timer (i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "lua_freeze_timer was called");
int tournamentMode = TestMode::ReadTestModeValue (L"TournamentMode");
if (tournamentMode == 1) return lua_pushbool (a1, true);
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) {
LogMessage (LOG_LEVEL_HOOKS, "FreezeTimer was called");
auto a1 = ctx.rdi;
int v9 = (int)(ctx.rax + 1);
lua_pushcclosure (a1, reinterpret_cast<i64> (&lua_freeze_timer), v9);
@@ -156,110 +158,53 @@ ExecuteSendResultData () {
);
}
bool sendFlag = false;
#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));
SCENE_RESULT_HOOK (SceneResultInitialize_AI, ASLR (0x140411FD0));
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 ( \
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));
SEND_RESULT_HOOK (SendResultData_AI, ASLR (0x1401755E0));
SEND_RESULT_HOOK (SendResultData_Collabo025_026, ASLR (0x140179A00));
SEND_RESULT_HOOK (SendResultData_AprilFool, ASLR (0x140177800));
#define CHANGE_RESULT_SIZE_HOOK(functionName, location, target) \
MID_HOOK (functionName, location, SafetyHookContext &ctx) { \
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; \
}
CHANGE_RESULT_SIZE_HOOK (ChangeResultDataSize_Enso, ASLR (0x140180074), rax);
CHANGE_RESULT_SIZE_HOOK (ChangeResultDataSize_AI, ASLR (0x140173774), rax);
CHANGE_RESULT_SIZE_HOOK (ChangeResultDataSize_Collabo025_026, ASLR (0x140178244), rax);
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) { \
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; \
*(u32 *)(ctx.rsp + offset) = currentStageCount - 1; \
ctx.rip += skip; \
}
CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_Enso, ASLR (0x14018074B), rax, 0x34, 0x07);
CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_AI, ASLR (0x140173EDD), r13, 0x24, 0x08);
CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_Collabo025_026, ASLR (0x1401789AD), r13, 0x24, 0x08);
CHANGE_RESULT_INDEX_HOOK (ChangeResultDataIndex_AprilFool, ASLR (0x140176716), rax, 0x34, 0x06);
int language = 0;
const char *
languageStr () {
switch (language) {
case 1: return "en_us";
case 2: return "cn_tw";
case 3: return "kor";
case 4: return "cn_cn";
default: return "jpn";
}
}
HOOK (i64, GetLanguage, ASLR (0x140024AC0), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "GetLanguage was called");
auto result = originalGetLanguage.call<i64> (a1);
language = *((u32 *)result);
return result;
}
HOOK (i64, GetRegionLanguage, ASLR (0x1401CE9B0), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "GetRegionLanguage was called");
lua_settop (a1, 0);
lua_pushstring (a1, languageStr ());
lua_pushstring (a1, languageStr (language));
return 1;
}
HOOK (i64, GetCabinetLanguage, ASLR (0x1401D1A60), i64, i64 a2) {
LogMessage (LOG_LEVEL_HOOKS, "GetCabinetLanguage was called");
lua_settop (a2, 0);
lua_pushstring (a2, languageStr ());
lua_pushstring (a2, languageStr (language));
return 1;
}
MID_HOOK (ChangeLanguageType, ASLR (0x1400B2016), SafetyHookContext &ctx) {
LogMessage (LOG_LEVEL_HOOKS, "ChangeLanguageType was called");
int *pFontType = (int *)ctx.rax;
if (*pFontType == 4) *pFontType = 2;
}
MID_HOOK (CountLockedCrown, ASLR (0x1403F2A25), SafetyHookContext &ctx) {
LogMessage (LOG_LEVEL_HOOKS, "CountLockedCrown was called");
ctx.r15 |= 1;
}
@@ -312,6 +257,7 @@ check_voice_tail (std::string bankName, uint8_t *pBinfBlock, std::map<std::strin
}
MID_HOOK (GenNus3bankId, ASLR (0x1407B97BD), SafetyHookContext &ctx) {
LogMessage (LOG_LEVEL_HOOKS, "GenNus3bankId was called");
std::lock_guard<std::mutex> lock (nus3bankMtx);
if ((uint8_t **)(ctx.rcx + 8) != nullptr) {
uint8_t *pNus3bankFile = *((uint8_t **)(ctx.rcx + 8));
@@ -337,6 +283,7 @@ FixToneName (std::string bankName, std::string toneName) {
size_t commonSize = 0;
HOOK (i64, PlaySound, ASLR (0x1404C6DC0), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "PlaySound was called");
if (enableSwitchVoice && language != 0) {
std::string bankName (lua_tolstring (a1, -3, &commonSize));
if (bankName[0] == 'v') {
@@ -348,6 +295,7 @@ HOOK (i64, PlaySound, ASLR (0x1404C6DC0), i64 a1) {
}
HOOK (i64, PlaySoundMulti, ASLR (0x1404C6D60), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "PlaySoundMulti was called");
if (enableSwitchVoice && language != 0) {
std::string bankName ((char *)lua_tolstring (a1, -3, &commonSize));
if (bankName[0] == 'v') {
@@ -369,6 +317,7 @@ FixToneNameEnso (u64 *Src, std::string &bankName) {
}
HOOK (bool, PlaySoundEnso, ASLR (0x1404ED590), u64 *a1, u64 *a2, i64 a3) {
LogMessage (LOG_LEVEL_HOOKS, "PlaySoundEnso was called");
if (enableSwitchVoice && language != 0) {
std::string bankName = a1[3] > 0x10 ? std::string (*((char **)a1)) : std::string ((char *)a1);
if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName);
@@ -377,6 +326,7 @@ HOOK (bool, PlaySoundEnso, ASLR (0x1404ED590), u64 *a1, u64 *a2, i64 a3) {
}
HOOK (bool, PlaySoundSpecial, ASLR (0x1404ED230), u64 *a1, u64 *a2) {
LogMessage (LOG_LEVEL_HOOKS, "PlaySoundSpecial was called");
if (enableSwitchVoice && language != 0) {
std::string bankName = a1[3] > 0x10 ? std::string (*((char **)a1)) : std::string ((char *)a1);
if (bankName[0] == 'v') a2 = FixToneNameEnso (a2, bankName);
@@ -386,6 +336,7 @@ HOOK (bool, PlaySoundSpecial, ASLR (0x1404ED230), u64 *a1, u64 *a2) {
int loaded_fail_count = 0;
HOOK (i64, LoadedBankAll, ASLR (0x1404C69F0), i64 a1) {
LogMessage (LOG_LEVEL_HOOKS, "LoadedBankAll was called");
originalLoadedBankAll.call<i64> (a1);
auto result = lua_toboolean (a1, -1);
lua_settop (a1, 0);
@@ -404,27 +355,29 @@ HOOK (i64, LoadedBankAll, ASLR (0x1404C69F0), i64 a1) {
float soundRate = 1.0F;
HOOK (i32, SetMasterVolumeSpeaker, ASLR (0x140160330), i32 a1) {
LogMessage (LOG_LEVEL_HOOKS, "SetMasterVolumeSpeaker was called");
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) {
LogMessage (LOG_LEVEL_HOOKS, "NuscBusVolume was called");
return originalNuscBusVolume.call<u64> (a1, a2, a3 * soundRate);
}
std::string* fontName = nullptr;
std::string *fontName = nullptr;
HOOK (u8, SetupFontInfo, ASLR (0x14049D820), u64 a1, u64 a2, size_t a3, u64 a4) {
LogMessage (LOG_LEVEL_HOOKS, "SetupFontInfo was called");
if (fontName != nullptr) delete fontName;
fontName = new std::string(((char*)a1) + 120);
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);
LogMessage (LOG_LEVEL_HOOKS, "ReadFontInfoInt was called");
std::string attribute ((char *)a2);
u32 result = originalReadFontInfoInt.call<u32> (a1, a2);
if (fontName->starts_with("cn_") && attribute == "offsetV") {
result += 1;
}
if (fontName->starts_with ("cn_") && attribute == "offsetV") result += 1;
return result;
}
@@ -457,13 +410,12 @@ ReplaceLeaBufferAddress (const std::vector<uintptr_t> &bufferAddresses, void *ne
void
Init () {
i32 xRes = 1920;
i32 yRes = 1080;
bool vsync = false;
bool unlockSongs = true;
bool fixLanguage = false;
bool chsPatch = false;
i32 xRes = 1920;
i32 yRes = 1080;
bool vsync = false;
bool unlockSongs = true;
bool fixLanguage = false;
bool chsPatch = false;
bool useLayeredfs = false;
auto configPath = std::filesystem::current_path () / "config.toml";
@@ -474,8 +426,8 @@ Init () {
unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
auto jpn39 = openConfigSection (patches, "jpn39");
if (jpn39) {
fixLanguage = readConfigBool (jpn39, "fix_language", fixLanguage);
chsPatch = readConfigBool (jpn39, "chs_patch", chsPatch);
fixLanguage = readConfigBool (jpn39, "fix_language", fixLanguage);
chsPatch = readConfigBool (jpn39, "chs_patch", chsPatch);
}
}
@@ -555,55 +507,55 @@ Init () {
}
// Freeze Timer
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); }
);
TestMode::RegisterItem (L"<select-item label=\"FREEZE TIMER\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" "
L"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);
}
);
TestMode::RegisterItem (L"<select-item label=\"KIMETSU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, "
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo024\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] () { INSTALL_HOOK (AvailableMode_Collabo024); });
TestMode::RegisterItem (L"<select-item label=\"ONE PIECE MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, "
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo025\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] () { INSTALL_HOOK (AvailableMode_Collabo025); });
TestMode::RegisterItem (L"<select-item label=\"AI SOSHINA MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, "
L"1:ENABLE, 2:CARD ONLY\" group=\"Setting\" id=\"ModModeCollabo026\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] () { INSTALL_HOOK (AvailableMode_Collabo026); });
TestMode::RegisterItem (L"<select-item label=\"AOHARU MODE\" param-offset-x=\"35\" replace-text=\"0:DEFAULT, 1:ENABLE, "
L"2:CARD ONLY\" group=\"Setting\" id=\"ModModeAprilFool001\" max=\"1\" min=\"0\" default=\"0\"/>",
[&] () { INSTALL_HOOK (AvailableMode_AprilFool001); });
TestMode::RegisterItem (L"<select-item label=\"INSTANT RESULT\" param-offset-x=\"35\" replace-text=\"0:OFF, 1:ON\" "
L"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(
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); }
);
[&] (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']",
@@ -636,10 +588,10 @@ Init () {
INSTALL_HOOK (ReadFontInfoInt);
}
LayeredFs::RegisterBefore([=](const std::string originalFileName, const std::string currentFileName) -> std::string {
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\\");
fileName = replace (fileName, "\\lumen\\", "\\lumen_cn\\");
if (std::filesystem::exists (fileName)) return fileName;
else return currentFileName;
});
+13 -12
View File
@@ -330,7 +330,7 @@ void
SetConfigValue (toml_table_t *table, const char *key, Keybindings *keybind) {
toml_array_t *array = toml_array_in (table, key);
if (!array) {
printWarning ("%s (%s): Cannot find array\n", __func__, key);
LogMessage (LOG_LEVEL_WARN, (std::string (key) + ": Cannot find array").c_str ());
return;
}
@@ -395,16 +395,16 @@ InitializePoll (HWND windowHandle) {
if (SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS | SDL_INIT_VIDEO) == 0) {
hasRumble = false;
} else {
printError ("SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS | SDL_INIT_VIDEO): "
"%s\n",
SDL_GetError ());
LogMessage (LOG_LEVEL_ERROR,
(std::string ("SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS | SDL_INIT_VIDEO): ")
+ SDL_GetError ())
.c_str ());
return false;
}
}
auto configPath = std::filesystem::current_path () / "gamecontrollerdb.txt";
if (SDL_GameControllerAddMappingsFromFile (configPath.string ().c_str ()) == -1)
printError ("%s (): Cannot read gamecontrollerdb.txt\n", __func__);
if (SDL_GameControllerAddMappingsFromFile (configPath.string ().c_str ()) == -1) LogMessage (LOG_LEVEL_ERROR, "Cannot read gamecontrollerdb.txt");
SDL_GameControllerEventState (SDL_ENABLE);
SDL_JoystickEventState (SDL_ENABLE);
@@ -413,15 +413,15 @@ InitializePoll (HWND windowHandle) {
SDL_GameController *controller = SDL_GameControllerOpen (i);
if (!controller) {
printWarning ("Could not open gamecontroller %s: %s\n", SDL_GameControllerNameForIndex (i), SDL_GetError ());
LogMessage (LOG_LEVEL_WARN,
(std::string ("Could not open gamecontroller ") + SDL_GameControllerNameForIndex (i) + ": " + SDL_GetError ()).c_str ());
continue;
}
controllers[i] = controller;
}
window = SDL_CreateWindowFrom (windowHandle);
if (window == NULL) printError ("SDL_CreateWindowFrom (windowHandle): %s\n", SDL_GetError ());
if (window == NULL) LogMessage (LOG_LEVEL_ERROR, (std::string ("SDL_CreateWindowFrom (windowHandle): ") + SDL_GetError ()).c_str ());
atexit (DisposePoll);
return hasRumble;
@@ -454,8 +454,9 @@ UpdatePoll (HWND windowHandle) {
controller = SDL_GameControllerOpen (event.cdevice.which);
if (!controller) {
printError ("%s (): Could not open gamecontroller %s: %s\n", __func__, SDL_GameControllerNameForIndex (event.cdevice.which),
SDL_GetError ());
LogMessage (LOG_LEVEL_ERROR, (std::string ("Could not open gamecontroller ") + SDL_GameControllerNameForIndex (event.cdevice.which)
+ ": " + SDL_GetError ())
.c_str ());
continue;
}
controllers[event.cdevice.which] = controller;
@@ -548,7 +549,7 @@ StringToConfigEnum (const char *value) {
return rval;
}
printError ("%s (%s): Unknown value\n", __func__, value);
LogMessage (LOG_LEVEL_ERROR, (std::string (value) + ": Unknown value").c_str ());
return rval;
}