Add fps limit patch and movable window mode patch
This commit is contained in:
parent
40ab2372bb
commit
f07574ac3f
9
dist/config.toml
vendored
9
dist/config.toml
vendored
@ -8,9 +8,6 @@ country_code = "JPN"
|
||||
|
||||
[patches]
|
||||
version = "auto"
|
||||
res = { x = 1920, y = 1080 }
|
||||
windowed = false
|
||||
vsync = false
|
||||
unlock_songs = true
|
||||
|
||||
[patches.chn00]
|
||||
@ -19,6 +16,12 @@ unlock_songs = true
|
||||
mode_collabo025 = false
|
||||
mode_collabo026 = false
|
||||
|
||||
[graphics]
|
||||
res = { x = 1920, y = 1080 }
|
||||
windowed = false
|
||||
vsync = false
|
||||
fpslimit = 120
|
||||
|
||||
[audio]
|
||||
wasapi_shared = true
|
||||
asio = false
|
||||
|
@ -78,9 +78,12 @@ library(
|
||||
'src/helpers.cpp',
|
||||
'src/poll.cpp',
|
||||
'src/bnusio.cpp',
|
||||
'src/patches/amauth.cpp',
|
||||
'src/patches/dxgi.cpp',
|
||||
'src/patches/fpslimiter.cpp',
|
||||
'src/patches/audio.cpp',
|
||||
'src/patches/qr.cpp',
|
||||
'src/patches/amauth.cpp',
|
||||
'src/patches/layeredfs.cpp',
|
||||
'src/patches/versions/JPN00.cpp',
|
||||
'src/patches/versions/JPN08.cpp',
|
||||
'src/patches/versions/JPN39.cpp',
|
||||
|
@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
#include <xxhash.h>
|
||||
|
||||
#define crcPOLY 0x82f63b78
|
||||
|
||||
enum class GameVersion : XXH64_hash_t {
|
||||
UNKNOWN = 0,
|
||||
JPN00 = 0x4C07355966D815FB,
|
||||
|
344
src/dllmain.cpp
344
src/dllmain.cpp
@ -3,185 +3,58 @@
|
||||
#include "helpers.h"
|
||||
#include "patches/patches.h"
|
||||
#include "poll.h"
|
||||
#include <zlib.h>
|
||||
#include <tomcrypt.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 autoIME = false;
|
||||
bool jpLayout = false;
|
||||
bool useLayeredFs = false;
|
||||
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;
|
||||
std::string datatableKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
std::string fumenKey = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
uint32_t crc32c(uint32_t crc, const unsigned char *buf, size_t len)
|
||||
{
|
||||
int k;
|
||||
|
||||
crc = ~crc;
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
for (k = 0; k < 8; k++)
|
||||
crc = (crc >> 1) ^ (crcPOLY & (0 - (crc & 1)));
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
bool checkCRC(const std::string &path, uint32_t crc){
|
||||
if (std::filesystem::exists(path)){
|
||||
std::filesystem::path crc_path = path;
|
||||
crc_path.replace_extension(".crc");
|
||||
std::ifstream crc_file(crc_path, std::ios::binary);
|
||||
std::string crc_content((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
|
||||
return std::stoul(crc_content) != crc;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void create_directories(const std::string &path) {
|
||||
size_t pos = 0;
|
||||
std::string delimiter = "\\";
|
||||
std::string current_path;
|
||||
|
||||
while ((pos = path.find(delimiter, pos)) != std::string::npos) {
|
||||
current_path = path.substr(0, pos++);
|
||||
if (!current_path.empty() && !CreateDirectory(current_path.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
throw std::runtime_error("Error creating directory: " + current_path);
|
||||
}
|
||||
}
|
||||
if (!path.empty() && !CreateDirectory(path.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
throw std::runtime_error("Error creating directory: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
void write_file(const std::string &filename, const std::vector<uint8_t> &data, uint32_t original_crc) {
|
||||
std::string::size_type pos = filename.find_last_of("\\");
|
||||
if (pos != std::string::npos) {
|
||||
std::string directory = filename.substr(0, pos);
|
||||
create_directories(directory);
|
||||
}
|
||||
|
||||
std::filesystem::path crc_path = filename;
|
||||
crc_path.replace_extension(".crc");
|
||||
std::ofstream crc_file(crc_path.string());
|
||||
crc_file << std::to_string(original_crc);
|
||||
crc_file.close();
|
||||
|
||||
std::ofstream file(filename, std::ios::binary);
|
||||
file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
||||
}
|
||||
|
||||
std::vector<unsigned char> gzip_compress(const std::vector<unsigned char>& data) {
|
||||
z_stream deflate_stream;
|
||||
deflate_stream.zalloc = Z_NULL;
|
||||
deflate_stream.zfree = Z_NULL;
|
||||
deflate_stream.opaque = Z_NULL;
|
||||
deflate_stream.avail_in = data.size();
|
||||
deflate_stream.next_in = const_cast<Bytef*>(data.data());
|
||||
|
||||
deflateInit2(&deflate_stream, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
|
||||
std::vector<unsigned char> compressed_data;
|
||||
compressed_data.resize(deflateBound(&deflate_stream, data.size()));
|
||||
|
||||
deflate_stream.avail_out = compressed_data.size();
|
||||
deflate_stream.next_out = compressed_data.data();
|
||||
|
||||
deflate(&deflate_stream, Z_FINISH);
|
||||
deflateEnd(&deflate_stream);
|
||||
|
||||
compressed_data.resize(deflate_stream.total_out);
|
||||
return compressed_data;
|
||||
}
|
||||
|
||||
// Function to pad data according to PKCS7
|
||||
std::vector<uint8_t> pad_data(const std::vector<uint8_t> &data, size_t block_size) {
|
||||
size_t padding = block_size - (data.size() % block_size);
|
||||
std::vector<uint8_t> padded_data = data;
|
||||
padded_data.insert(padded_data.end(), padding, static_cast<uint8_t>(padding));
|
||||
return padded_data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> hex_to_bytes(const std::string &hex) {
|
||||
std::vector<uint8_t> bytes;
|
||||
for (size_t i = 0; i < hex.length(); i += 2) {
|
||||
uint8_t byte = static_cast<uint8_t>(std::stoi(hex.substr(i, 2), nullptr, 16));
|
||||
bytes.push_back(byte);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypt_file(const std::string &input_file, const std::string &hex_key) {
|
||||
// Convert the key from hex to bytes
|
||||
std::vector<uint8_t> key = hex_to_bytes(hex_key);
|
||||
|
||||
// Generate the 128 bits IV
|
||||
std::vector<uint8_t> iv(16);
|
||||
for (size_t i = 0; i < iv.size(); ++i) iv[i] = static_cast<uint8_t>(i);
|
||||
|
||||
// Read the entire file into memory
|
||||
std::ifstream file(input_file, std::ios::binary);
|
||||
|
||||
std::vector<uint8_t> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
// Compress the data
|
||||
std::vector<uint8_t> compressed_data = gzip_compress(data);
|
||||
|
||||
// Pad the compressed data
|
||||
std::vector<uint8_t> padded_data = pad_data(compressed_data, 16);
|
||||
|
||||
// Encrypt the data
|
||||
symmetric_CBC cbc;
|
||||
if (cbc_start(find_cipher("aes"), iv.data(), key.data(), key.size(), 0, &cbc) != CRYPT_OK) {
|
||||
throw std::runtime_error("Error initializing CBC");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypted_data(padded_data.size());
|
||||
if (cbc_encrypt(padded_data.data(), encrypted_data.data(), padded_data.size(), &cbc) != CRYPT_OK) {
|
||||
throw std::runtime_error("Error during encryption");
|
||||
}
|
||||
|
||||
cbc_done(&cbc);
|
||||
|
||||
// Return IV concatenated with the encrypted data
|
||||
encrypted_data.insert(encrypted_data.begin(), iv.begin(), iv.end());
|
||||
return encrypted_data;
|
||||
}
|
||||
|
||||
bool is_fumen_encrypted(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
file.seekg(0x210, std::ios::beg);
|
||||
std::vector<unsigned char> buffer(32);
|
||||
file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
|
||||
|
||||
// Check if the read bytes match the expected pattern
|
||||
std::vector<unsigned char> expected_bytes = {
|
||||
0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
return buffer != expected_bytes;
|
||||
}
|
||||
|
||||
HWND hGameWnd;
|
||||
HOOK (i32, ShowMouse, PROC_ADDRESS ("user32.dll", "ShowCursor"), bool) { return originalShowMouse (true); }
|
||||
HOOK (i32, ExitWindows, PROC_ADDRESS ("user32.dll", "ExitWindowsEx")) { ExitProcess (0); }
|
||||
HOOK (HWND, CreateWindow, PROC_ADDRESS ("user32.dll", "CreateWindowExW"), DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle,
|
||||
i32 X, i32 Y, i32 nWidth, i32 nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) {
|
||||
if (lpWindowName != NULL) {
|
||||
if (wcscmp (lpWindowName, L"Taiko") == 0) {
|
||||
if (windowed) dwStyle = WS_TILEDWINDOW ^ WS_MAXIMIZEBOX ^ WS_THICKFRAME;
|
||||
|
||||
hGameWnd
|
||||
= originalCreateWindow (dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
|
||||
return hGameWnd;
|
||||
}
|
||||
}
|
||||
return originalCreateWindow (dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
|
||||
}
|
||||
HOOK (bool, SetWindowPosition, PROC_ADDRESS ("user32.dll", "SetWindowPos"), HWND hWnd, HWND hWndInsertAfter, i32 X, i32 Y, i32 cx, i32 cy,
|
||||
u32 uFlags) {
|
||||
if (hWnd == hGameWnd) {
|
||||
RECT rw, rc;
|
||||
GetWindowRect (hWnd, &rw);
|
||||
GetClientRect (hWnd, &rc);
|
||||
cx = (rw.right - rw.left) - (rc.right - rc.left) + cx;
|
||||
cy = (rw.bottom - rw.top) - (rc.bottom - rc.top) + cy;
|
||||
}
|
||||
return originalSetWindowPosition (hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
|
||||
}
|
||||
|
||||
HOOK (i32, XinputGetState, PROC_ADDRESS ("xinput9_1_0.dll", "XInputGetState")) { return ERROR_DEVICE_NOT_CONNECTED; }
|
||||
HOOK (i32, XinputSetState, PROC_ADDRESS ("xinput9_1_0.dll", "XInputSetState")) { return ERROR_DEVICE_NOT_CONNECTED; }
|
||||
@ -200,109 +73,6 @@ HOOK (i32, ws2_getaddrinfo, PROC_ADDRESS ("ws2_32.dll", "getaddrinfo"), const ch
|
||||
return originalws2_getaddrinfo (server.c_str (), service, hints, out);
|
||||
}
|
||||
|
||||
HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
std::filesystem::path path (lpFileName);
|
||||
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";
|
||||
auto encryptedLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64_enc";
|
||||
|
||||
if (path.string ().find (originalDataFolder.string ()) == 0) {
|
||||
auto newPath = path.string ();
|
||||
auto encPath = path.string ();
|
||||
newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
|
||||
encPath.replace (0, originalDataFolder.string ().length (), encryptedLayeredFsFolder.string ());
|
||||
|
||||
//The following code handles file redirection and if need be, file encryption.
|
||||
//It's a bit of a mess but it works well ! -Kit
|
||||
|
||||
if (std::filesystem::exists (newPath)) { //If a file exists in the datamod folder
|
||||
if(is_fumen_encrypted(newPath)){ //And if it's an encrypted fumen or a different type of file, use it.
|
||||
std::cout << "Redirecting " << std::filesystem::relative (path).string() << std::endl;
|
||||
return originalCreateFileAHook (newPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
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
|
||||
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());
|
||||
write_file(encPath, encrypt_file(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;
|
||||
encPath = path.string();
|
||||
}
|
||||
} else std::cout << "Using cached file for " << std::filesystem::relative (newPath) << std::endl;
|
||||
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
}
|
||||
|
||||
//We check separately for unencrypted json files.
|
||||
std::filesystem::path json_path = newPath;
|
||||
json_path.replace_extension(".json");
|
||||
if (std::filesystem::exists(json_path)) { //If a json file exists in the folder
|
||||
bool crcBool = false;
|
||||
|
||||
if (std::filesystem::exists(encPath)){
|
||||
std::ifstream crc_file(json_path, 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());
|
||||
crcBool = checkCRC(encPath, crc);
|
||||
}
|
||||
|
||||
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
|
||||
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());
|
||||
write_file(encPath, encrypt_file(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;
|
||||
encPath = path.string();
|
||||
}
|
||||
}
|
||||
else std::cout << "Using cached file for " << std::filesystem::relative (json_path) << std::endl; //Otherwise use the already encrypted file.
|
||||
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
}
|
||||
|
||||
return originalCreateFileAHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
|
||||
hTemplateFile);
|
||||
}
|
||||
|
||||
// HOOK (HANDLE, CreateFileWHook, PROC_ADDRESS ("kernel32.dll", "CreateFileW"), LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
// LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
// std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
|
||||
// std::string strFileName = converter.to_bytes (lpFileName);
|
||||
|
||||
// std::filesystem::path path (strFileName);
|
||||
// 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";
|
||||
|
||||
// if (path.string ().find (originalDataFolder.string ()) != std::string::npos) {
|
||||
// auto newPath = path.string ();
|
||||
// newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
|
||||
|
||||
// if (std::filesystem::exists (newPath)) {
|
||||
// std::wstring wNewPath = converter.from_bytes (newPath);
|
||||
// std::wcout << L"Redirecting " << lpFileName << L" to " << wNewPath << std::endl;
|
||||
// return originalCreateFileWHook (wNewPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
// dwFlagsAndAttributes, hTemplateFile);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return originalCreateFileWHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
|
||||
// hTemplateFile);
|
||||
// }
|
||||
|
||||
void
|
||||
GetGameVersion () {
|
||||
wchar_t w_path[MAX_PATH];
|
||||
@ -378,25 +148,28 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
countryCode = readConfigString (amauth, "country_code", countryCode);
|
||||
|
||||
std::strcat (fullAddress, server.c_str ());
|
||||
std::strcat (fullAddress, ":");
|
||||
std::strcat (fullAddress, port.c_str ());
|
||||
if (port != "") {
|
||||
std::strcat (fullAddress, ":");
|
||||
std::strcat (fullAddress, port.c_str ());
|
||||
}
|
||||
|
||||
std::strcat (placeId, countryCode.c_str ());
|
||||
std::strcat (placeId, "0FF0");
|
||||
}
|
||||
auto patches = openConfigSection (config, "patches");
|
||||
if (patches) version = readConfigString (patches, "version", version);
|
||||
auto graphics = openConfigSection (config, "graphics");
|
||||
if (graphics) windowed = readConfigBool (graphics, "windowed", windowed);
|
||||
auto keyboard = openConfigSection (config, "keyboard");
|
||||
if (keyboard) {
|
||||
autoIME = readConfigBool (keyboard, "auto_ime", autoIME);
|
||||
jpLayout = readConfigBool (keyboard, "jp_layout", jpLayout);
|
||||
}
|
||||
|
||||
auto layeredFs = openConfigSection (config, "layeredfs");
|
||||
if (layeredFs) {
|
||||
useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
|
||||
datatableKey = readConfigString (layeredFs, "datatable_key", datatableKey);
|
||||
fumenKey = readConfigString (layeredFs, "fumen_key", fumenKey);
|
||||
auto layeredFS = openConfigSection (config, "layeredfs");
|
||||
if (layeredFS) {
|
||||
useLayeredFS = readConfigBool (layeredFS, "enabled", useLayeredFS);
|
||||
datatableKey = readConfigString (layeredFS, "datatable_key", datatableKey);
|
||||
fumenKey = readConfigString (layeredFS, "fumen_key", fumenKey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,6 +214,8 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
|
||||
INSTALL_HOOK (ShowMouse);
|
||||
INSTALL_HOOK (ExitWindows);
|
||||
INSTALL_HOOK (CreateWindow);
|
||||
INSTALL_HOOK (SetWindowPosition);
|
||||
|
||||
INSTALL_HOOK (XinputGetState);
|
||||
INSTALL_HOOK (XinputSetState);
|
||||
@ -454,13 +229,6 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
|
||||
INSTALL_HOOK (ws2_getaddrinfo);
|
||||
|
||||
if (useLayeredFs) {
|
||||
std::wcout << "Using LayeredFs!" << std::endl;
|
||||
register_cipher(&aes_desc);
|
||||
INSTALL_HOOK (CreateFileAHook);
|
||||
// INSTALL_HOOK (CreateFileWHook);
|
||||
}
|
||||
|
||||
bnusio::Init ();
|
||||
|
||||
switch (gameVersion) {
|
||||
@ -470,6 +238,10 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
|
||||
case GameVersion::JPN39: patches::JPN39::Init (); break;
|
||||
case GameVersion::CHN00: patches::CHN00::Init (); break;
|
||||
}
|
||||
|
||||
if (useLayeredFS) patches::LayeredFS::Init ();
|
||||
|
||||
patches::Dxgi::Init ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
257
src/patches/dxgi.cpp
Normal file
257
src/patches/dxgi.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
#define CINTERFACE
|
||||
#define D3D11_NO_HELPERS
|
||||
#define INITGUID
|
||||
#pragma optimize("", off)
|
||||
|
||||
#include "d3d11.h"
|
||||
#include "dxgi1_2.h"
|
||||
#include "dxgi1_3.h"
|
||||
#include "dxgi1_4.h"
|
||||
#include "dxgi1_5.h"
|
||||
#include "dxgi1_6.h"
|
||||
#include "helpers.h"
|
||||
#include <MinHook.h>
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
#include "d3d12.h"
|
||||
#pragma comment(lib, "d3d12.lib")
|
||||
|
||||
#include "patches.h"
|
||||
#include <intrin.h>
|
||||
|
||||
/*
|
||||
* Reference: https://github.com/teknogods/OpenParrot/blob/master/OpenParrot/src/Functions/WindowedDxgi.cpp
|
||||
*/
|
||||
|
||||
namespace patches::Dxgi {
|
||||
|
||||
// Local variables
|
||||
static bool FpsLimiterEnable = false;
|
||||
static bool DisableVSync = false;
|
||||
|
||||
// Prototypes
|
||||
static HRESULT (STDMETHODCALLTYPE *g_oldSetFullscreenState) (IDXGISwapChain *This, BOOL Fullscreen, IDXGIOutput *pTarget);
|
||||
static HRESULT (STDMETHODCALLTYPE *g_oldCreateSwapChain) (IDXGIFactory *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
|
||||
IDXGISwapChain **ppSwapChain);
|
||||
static HRESULT (STDMETHODCALLTYPE *g_oldSetFullscreenState1) (IDXGISwapChain1 *This, BOOL Fullscreen, IDXGIOutput *pTarget);
|
||||
static HRESULT (STDMETHODCALLTYPE *g_oldCreateSwapChainForHwnd) (IDXGIFactory2 *This, IUnknown *pDevice, HWND hWnd,
|
||||
const DXGI_SWAP_CHAIN_DESC1 *pDesc,
|
||||
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc,
|
||||
IDXGIOutput *pRestrictToOutput, IDXGISwapChain1 **ppSwapChain);
|
||||
static HRESULT (STDMETHODCALLTYPE *g_oldPresentWrap) (IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags);
|
||||
static HRESULT (STDMETHODCALLTYPE *g_oldPresent1Wrap) (IDXGISwapChain1 *pSwapChain, UINT SyncInterval, UINT Flags);
|
||||
static HRESULT (STDMETHODCALLTYPE *g_oldCreateSwapChain2) (IDXGIFactory2 *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
|
||||
IDXGISwapChain **ppSwapChain);
|
||||
static HRESULT (WINAPI *g_origCreateDXGIFactory2) (UINT Flags, REFIID riid, void **ppFactory);
|
||||
static HRESULT (WINAPI *g_origCreateDXGIFactory) (REFIID, void **);
|
||||
static HRESULT (WINAPI *g_origD3D11CreateDeviceAndSwapChain) (IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags,
|
||||
const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion,
|
||||
/*const*/ DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain,
|
||||
ID3D11Device **ppDevice, D3D_FEATURE_LEVEL *pFeatureLevel,
|
||||
ID3D11DeviceContext **ppImmediateContext);
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SetFullscreenStateWrap (IDXGISwapChain *This, BOOL Fullscreen, IDXGIOutput *pTarget);
|
||||
static HRESULT STDMETHODCALLTYPE CreateSwapChainWrap (IDXGIFactory *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
|
||||
IDXGISwapChain **ppSwapChain);
|
||||
static HRESULT STDMETHODCALLTYPE SetFullscreenState1Wrap (IDXGISwapChain1 *This, BOOL Fullscreen, IDXGIOutput *pTarget);
|
||||
static HRESULT STDMETHODCALLTYPE CreateSwapChainForHwndWrap (IDXGIFactory2 *This, IUnknown *pDevice, HWND hWnd, const DXGI_SWAP_CHAIN_DESC1 *pDesc,
|
||||
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, IDXGIOutput *pRestrictToOutput,
|
||||
IDXGISwapChain1 **ppSwapChain);
|
||||
static HRESULT STDMETHODCALLTYPE PresentWrap (IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags);
|
||||
static HRESULT STDMETHODCALLTYPE Present1Wrap (IDXGISwapChain1 *pSwapChain, UINT SyncInterval, UINT Flags);
|
||||
static HRESULT STDMETHODCALLTYPE CreateSwapChain2Wrap (IDXGIFactory2 *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
|
||||
IDXGISwapChain **ppSwapChain);
|
||||
static HRESULT WINAPI CreateDXGIFactory2Wrap (UINT Flags, REFIID riid, void **ppFactory);
|
||||
static HRESULT WINAPI CreateDXGIFactoryWrap (REFIID riid, _COM_Outptr_ void **ppFactory);
|
||||
static HRESULT WINAPI D3D11CreateDeviceAndSwapChainWrap (IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags,
|
||||
const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion,
|
||||
/*const*/ DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain,
|
||||
ID3D11Device **ppDevice, D3D_FEATURE_LEVEL *pFeatureLevel,
|
||||
ID3D11DeviceContext **ppImmediateContext);
|
||||
|
||||
// Functions
|
||||
template <typename T>
|
||||
inline T
|
||||
HookVtableFunction (T *functionPtr, T target) {
|
||||
if (*functionPtr == target) return nullptr;
|
||||
|
||||
auto old = *functionPtr;
|
||||
WRITE_MEMORY (functionPtr, T, target);
|
||||
// injector::WriteMemory (functionPtr, target, true);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
SetFullscreenStateWrap (IDXGISwapChain *This, BOOL Fullscreen, IDXGIOutput *pTarget) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
CreateSwapChainWrap (IDXGIFactory *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc, IDXGISwapChain **ppSwapChain) {
|
||||
HRESULT hr = g_oldCreateSwapChain (This, pDevice, pDesc, ppSwapChain);
|
||||
|
||||
if (*ppSwapChain) {
|
||||
if (FpsLimiterEnable || DisableVSync) {
|
||||
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, PresentWrap);
|
||||
g_oldPresentWrap = (old2) ? old2 : g_oldPresentWrap;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
SetFullscreenState1Wrap (IDXGISwapChain1 *This, BOOL Fullscreen, IDXGIOutput *pTarget) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
CreateSwapChainForHwndWrap (IDXGIFactory2 *This, IUnknown *pDevice, HWND hWnd, const DXGI_SWAP_CHAIN_DESC1 *pDesc,
|
||||
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, IDXGIOutput *pRestrictToOutput, IDXGISwapChain1 **ppSwapChain) {
|
||||
HRESULT hr = g_oldCreateSwapChainForHwnd (This, pDevice, hWnd, pDesc, NULL, pRestrictToOutput, ppSwapChain);
|
||||
|
||||
if (*ppSwapChain) {
|
||||
if (FpsLimiterEnable || DisableVSync) {
|
||||
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, Present1Wrap);
|
||||
g_oldPresent1Wrap = (old2) ? old2 : g_oldPresent1Wrap;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
PresentWrap (IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags) {
|
||||
if (DisableVSync) SyncInterval = 0;
|
||||
|
||||
if (FpsLimiterEnable) patches::FpsLimiter::Update ();
|
||||
|
||||
return g_oldPresentWrap (pSwapChain, SyncInterval, Flags);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
Present1Wrap (IDXGISwapChain1 *pSwapChain, UINT SyncInterval, UINT Flags) {
|
||||
if (DisableVSync) SyncInterval = 0;
|
||||
|
||||
if (FpsLimiterEnable) patches::FpsLimiter::Update ();
|
||||
|
||||
return g_oldPresent1Wrap (pSwapChain, SyncInterval, Flags);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
CreateSwapChain2Wrap (IDXGIFactory2 *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc, IDXGISwapChain **ppSwapChain) {
|
||||
HRESULT hr = g_oldCreateSwapChain2 (This, pDevice, pDesc, ppSwapChain);
|
||||
|
||||
if (*ppSwapChain) {
|
||||
if (FpsLimiterEnable || DisableVSync) {
|
||||
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, PresentWrap);
|
||||
g_oldPresentWrap = (old2) ? old2 : g_oldPresentWrap;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI
|
||||
CreateDXGIFactory2Wrap (UINT Flags, REFIID riid, void **ppFactory) {
|
||||
HRESULT hr = g_origCreateDXGIFactory2 (Flags, riid, ppFactory);
|
||||
|
||||
if (SUCCEEDED (hr)) {
|
||||
int factoryType = 0;
|
||||
|
||||
if (IsEqualIID (riid, IID_IDXGIFactory1)) factoryType = 1;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory2)) factoryType = 2;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory3)) factoryType = 3;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory4)) factoryType = 4;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory5)) factoryType = 5;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory6)) factoryType = 6;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory7)) factoryType = 7;
|
||||
|
||||
IDXGIFactory2 *factory = (IDXGIFactory2 *)*ppFactory;
|
||||
|
||||
auto old = HookVtableFunction (&factory->lpVtbl->CreateSwapChain, CreateSwapChain2Wrap);
|
||||
g_oldCreateSwapChain2 = (old) ? old : g_oldCreateSwapChain2;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI
|
||||
CreateDXGIFactoryWrap (REFIID riid, _COM_Outptr_ void **ppFactory) {
|
||||
HRESULT hr = g_origCreateDXGIFactory (riid, ppFactory);
|
||||
|
||||
if (SUCCEEDED (hr)) {
|
||||
int factoryType = 0;
|
||||
|
||||
if (IsEqualIID (riid, IID_IDXGIFactory1)) factoryType = 1;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory2)) factoryType = 2;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory3)) factoryType = 3;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory4)) factoryType = 4;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory5)) factoryType = 5;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory6)) factoryType = 6;
|
||||
else if (IsEqualIID (riid, IID_IDXGIFactory7)) factoryType = 7;
|
||||
|
||||
if (factoryType >= 0) {
|
||||
IDXGIFactory *factory = (IDXGIFactory *)*ppFactory;
|
||||
|
||||
auto old = HookVtableFunction (&factory->lpVtbl->CreateSwapChain, CreateSwapChainWrap);
|
||||
g_oldCreateSwapChain = (old) ? old : g_oldCreateSwapChain;
|
||||
}
|
||||
|
||||
if (factoryType >= 2) {
|
||||
IDXGIFactory2 *factory = (IDXGIFactory2 *)*ppFactory;
|
||||
|
||||
auto old = HookVtableFunction (&factory->lpVtbl->CreateSwapChainForHwnd, CreateSwapChainForHwndWrap);
|
||||
g_oldCreateSwapChainForHwnd = (old) ? old : g_oldCreateSwapChainForHwnd;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI
|
||||
D3D11CreateDeviceAndSwapChainWrap (IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags,
|
||||
const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion,
|
||||
/*const*/ DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain, ID3D11Device **ppDevice,
|
||||
D3D_FEATURE_LEVEL *pFeatureLevel, ID3D11DeviceContext **ppImmediateContext) {
|
||||
HRESULT hr = g_origD3D11CreateDeviceAndSwapChain (pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion,
|
||||
pSwapChainDesc, ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext);
|
||||
|
||||
if (ppSwapChain) {
|
||||
if (FpsLimiterEnable) {
|
||||
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, PresentWrap);
|
||||
g_oldPresentWrap = (old2) ? old2 : g_oldPresentWrap;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void
|
||||
Init () {
|
||||
bool vsync = false;
|
||||
i32 fpsLimit = 120;
|
||||
|
||||
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 graphics = openConfigSection (config_ptr.get (), "graphics");
|
||||
if (graphics) {
|
||||
vsync = readConfigBool (graphics, "vsync", vsync);
|
||||
fpsLimit = readConfigInt (graphics, "fpslimit", fpsLimit);
|
||||
}
|
||||
}
|
||||
|
||||
DisableVSync = !vsync;
|
||||
FpsLimiterEnable = fpsLimit > 0;
|
||||
patches::FpsLimiter::Init ((float)fpsLimit);
|
||||
|
||||
MH_Initialize ();
|
||||
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory", (LPVOID)CreateDXGIFactoryWrap, (void **)&g_origCreateDXGIFactory);
|
||||
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory2", (LPVOID)CreateDXGIFactory2Wrap, (void **)&g_origCreateDXGIFactory2);
|
||||
MH_CreateHookApi (L"d3d11.dll", "D3D11CreateDeviceAndSwapChain", (LPVOID)D3D11CreateDeviceAndSwapChainWrap,
|
||||
(void **)&g_origD3D11CreateDeviceAndSwapChain);
|
||||
MH_EnableHook (MH_ALL_HOOKS);
|
||||
}
|
||||
|
||||
} // namespace patches::Dxgi
|
65
src/patches/fpslimiter.cpp
Normal file
65
src/patches/fpslimiter.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include "helpers.h"
|
||||
#include "patches.h"
|
||||
|
||||
namespace patches::FpsLimiter {
|
||||
|
||||
/*
|
||||
* Reference: https://github.com/teknogods/OpenParrot/blob/master/OpenParrot/src/Functions/FpsLimiter.cpp
|
||||
*/
|
||||
|
||||
static LARGE_INTEGER PerformanceCount1;
|
||||
static LARGE_INTEGER PerformanceCount2;
|
||||
static bool bOnce1 = false;
|
||||
static double targetFrameTime = 1000.0 / 60.0;
|
||||
static double t = 0.0;
|
||||
static u32 i = 0;
|
||||
|
||||
void
|
||||
Init (float fpsLimit) {
|
||||
targetFrameTime = 1000.0 / fpsLimit;
|
||||
}
|
||||
|
||||
void
|
||||
Update () {
|
||||
if (!bOnce1) {
|
||||
bOnce1 = true;
|
||||
QueryPerformanceCounter (&PerformanceCount1);
|
||||
PerformanceCount1.QuadPart = PerformanceCount1.QuadPart >> i;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
QueryPerformanceCounter (&PerformanceCount2);
|
||||
|
||||
if (t == 0.0) {
|
||||
LARGE_INTEGER PerformanceCount3;
|
||||
static bool bOnce2 = false;
|
||||
|
||||
if (!bOnce2) {
|
||||
bOnce2 = true;
|
||||
QueryPerformanceFrequency (&PerformanceCount3);
|
||||
i = 0;
|
||||
t = 1000.0 / (double)PerformanceCount3.QuadPart;
|
||||
auto v = t * 2147483648.0;
|
||||
if (60000.0 > v) {
|
||||
while (true) {
|
||||
++i;
|
||||
v *= 2.0;
|
||||
t *= 2.0;
|
||||
if (60000.0 <= v) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SleepEx (0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (((double)((PerformanceCount2.QuadPart >> i) - PerformanceCount1.QuadPart) * t) >= targetFrameTime) break;
|
||||
|
||||
SleepEx (0, 1);
|
||||
}
|
||||
|
||||
QueryPerformanceCounter (&PerformanceCount2);
|
||||
PerformanceCount1.QuadPart = PerformanceCount2.QuadPart >> i;
|
||||
}
|
||||
|
||||
}
|
277
src/patches/layeredfs.cpp
Normal file
277
src/patches/layeredfs.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include "helpers.h"
|
||||
#include <tomcrypt.h>
|
||||
#include <zlib.h>
|
||||
|
||||
extern std::string datatableKey;
|
||||
extern std::string fumenKey;
|
||||
|
||||
#define CRCPOLY 0x82F63B78
|
||||
|
||||
namespace patches::LayeredFS {
|
||||
|
||||
uint32_t
|
||||
CRC32C (uint32_t crc, const unsigned char *buf, size_t len) {
|
||||
int k;
|
||||
|
||||
crc = ~crc;
|
||||
while (len--) {
|
||||
crc ^= *buf++;
|
||||
for (k = 0; k < 8; k++)
|
||||
crc = (crc >> 1) ^ (CRCPOLY & (0 - (crc & 1)));
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
bool
|
||||
CheckCRC (const std::string &path, uint32_t crc) {
|
||||
if (std::filesystem::exists (path)) {
|
||||
std::filesystem::path crc_path = path;
|
||||
crc_path.replace_extension (".crc");
|
||||
std::ifstream crc_file (crc_path, std::ios::binary);
|
||||
std::string crc_content ((std::istreambuf_iterator<char> (crc_file)), std::istreambuf_iterator<char> ());
|
||||
return std::stoul (crc_content) != crc;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectories (const std::string &path) {
|
||||
size_t pos = 0;
|
||||
std::string delimiter = "\\";
|
||||
std::string current_path;
|
||||
|
||||
while ((pos = path.find (delimiter, pos)) != std::string::npos) {
|
||||
current_path = path.substr (0, pos++);
|
||||
if (!current_path.empty () && !CreateDirectory (current_path.c_str (), NULL) && GetLastError () != ERROR_ALREADY_EXISTS)
|
||||
throw std::runtime_error ("Error creating directory: " + current_path);
|
||||
}
|
||||
if (!path.empty () && !CreateDirectory (path.c_str (), NULL) && GetLastError () != ERROR_ALREADY_EXISTS)
|
||||
throw std::runtime_error ("Error creating directory: " + path);
|
||||
}
|
||||
|
||||
void
|
||||
WriteFile (const std::string &filename, const std::vector<uint8_t> &data, uint32_t original_crc) {
|
||||
std::string::size_type pos = filename.find_last_of ("\\");
|
||||
if (pos != std::string::npos) {
|
||||
std::string directory = filename.substr (0, pos);
|
||||
CreateDirectories (directory);
|
||||
}
|
||||
|
||||
std::filesystem::path crc_path = filename;
|
||||
crc_path.replace_extension (".crc");
|
||||
std::ofstream crc_file (crc_path.string ());
|
||||
crc_file << std::to_string (original_crc);
|
||||
crc_file.close ();
|
||||
|
||||
std::ofstream file (filename, std::ios::binary);
|
||||
file.write (reinterpret_cast<const char *> (data.data ()), data.size ());
|
||||
}
|
||||
|
||||
std::vector<unsigned char>
|
||||
GZip_Compress (const std::vector<unsigned char> &data) {
|
||||
z_stream deflate_stream;
|
||||
deflate_stream.zalloc = Z_NULL;
|
||||
deflate_stream.zfree = Z_NULL;
|
||||
deflate_stream.opaque = Z_NULL;
|
||||
deflate_stream.avail_in = data.size ();
|
||||
deflate_stream.next_in = const_cast<Bytef *> (data.data ());
|
||||
|
||||
deflateInit2 (&deflate_stream, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
|
||||
std::vector<unsigned char> compressed_data;
|
||||
compressed_data.resize (deflateBound (&deflate_stream, data.size ()));
|
||||
|
||||
deflate_stream.avail_out = compressed_data.size ();
|
||||
deflate_stream.next_out = compressed_data.data ();
|
||||
|
||||
deflate (&deflate_stream, Z_FINISH);
|
||||
deflateEnd (&deflate_stream);
|
||||
|
||||
compressed_data.resize (deflate_stream.total_out);
|
||||
return compressed_data;
|
||||
}
|
||||
|
||||
// Function to pad data according to PKCS7
|
||||
std::vector<uint8_t>
|
||||
Pad_Data (const std::vector<uint8_t> &data, size_t block_size) {
|
||||
size_t padding = block_size - (data.size () % block_size);
|
||||
std::vector<uint8_t> padded_data = data;
|
||||
padded_data.insert (padded_data.end (), padding, static_cast<uint8_t> (padding));
|
||||
return padded_data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>
|
||||
Hex_To_Bytes (const std::string &hex) {
|
||||
std::vector<uint8_t> bytes;
|
||||
for (size_t i = 0; i < hex.length (); i += 2) {
|
||||
uint8_t byte = static_cast<uint8_t> (std::stoi (hex.substr (i, 2), nullptr, 16));
|
||||
bytes.push_back (byte);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>
|
||||
EncryptFile (const std::string &input_file, const std::string &hex_key) {
|
||||
// Convert the key from hex to bytes
|
||||
std::vector<uint8_t> key = Hex_To_Bytes (hex_key);
|
||||
|
||||
// Generate the 128 bits IV
|
||||
std::vector<uint8_t> iv (16);
|
||||
for (size_t i = 0; i < iv.size (); ++i)
|
||||
iv[i] = static_cast<uint8_t> (i);
|
||||
|
||||
// Read the entire file into memory
|
||||
std::ifstream file (input_file, std::ios::binary);
|
||||
|
||||
std::vector<uint8_t> data ((std::istreambuf_iterator<char> (file)), std::istreambuf_iterator<char> ());
|
||||
|
||||
// Compress the data
|
||||
std::vector<uint8_t> compressed_data = GZip_Compress (data);
|
||||
|
||||
// Pad the compressed data
|
||||
std::vector<uint8_t> padded_data = Pad_Data (compressed_data, 16);
|
||||
|
||||
// Encrypt the data
|
||||
symmetric_CBC cbc;
|
||||
if (cbc_start (find_cipher ("aes"), iv.data (), key.data (), key.size (), 0, &cbc) != CRYPT_OK)
|
||||
throw std::runtime_error ("Error initializing CBC");
|
||||
|
||||
std::vector<uint8_t> encrypted_data (padded_data.size ());
|
||||
if (cbc_encrypt (padded_data.data (), encrypted_data.data (), padded_data.size (), &cbc) != CRYPT_OK)
|
||||
throw std::runtime_error ("Error during encryption");
|
||||
|
||||
cbc_done (&cbc);
|
||||
|
||||
// Return IV concatenated with the encrypted data
|
||||
encrypted_data.insert (encrypted_data.begin (), iv.begin (), iv.end ());
|
||||
return encrypted_data;
|
||||
}
|
||||
|
||||
bool
|
||||
IsFumenEncrypted (const std::string &filename) {
|
||||
std::ifstream file (filename, std::ios::binary);
|
||||
file.seekg (0x210, std::ios::beg);
|
||||
std::vector<unsigned char> buffer (32);
|
||||
file.read (reinterpret_cast<char *> (buffer.data ()), buffer.size ());
|
||||
|
||||
// Check if the read bytes match the expected pattern
|
||||
std::vector<unsigned char> expected_bytes = {0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
return buffer != expected_bytes;
|
||||
}
|
||||
|
||||
HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
std::filesystem::path path (lpFileName);
|
||||
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";
|
||||
auto encryptedLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64_enc";
|
||||
|
||||
if (path.string ().find (originalDataFolder.string ()) == 0) {
|
||||
auto newPath = path.string ();
|
||||
auto encPath = path.string ();
|
||||
newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
|
||||
encPath.replace (0, originalDataFolder.string ().length (), encryptedLayeredFsFolder.string ());
|
||||
|
||||
// The following code handles file redirection and if need be, file encryption.
|
||||
// It's a bit of a mess but it works well ! -Kit
|
||||
|
||||
if (std::filesystem::exists (newPath)) { // If a file exists in the datamod folder
|
||||
if (IsFumenEncrypted (newPath)) { // And if it's an encrypted fumen or a different type of file, use it.
|
||||
std::cout << "Redirecting " << std::filesystem::relative (path).string () << std::endl;
|
||||
return originalCreateFileAHook (newPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
} 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
|
||||
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;
|
||||
encPath = path.string ();
|
||||
}
|
||||
} else std::cout << "Using cached file for " << std::filesystem::relative (newPath) << std::endl;
|
||||
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
}
|
||||
|
||||
// We check separately for unencrypted json files.
|
||||
std::filesystem::path json_path = newPath;
|
||||
json_path.replace_extension (".json");
|
||||
if (std::filesystem::exists (json_path)) { // If a json file exists in the folder
|
||||
bool crcBool = false;
|
||||
|
||||
if (std::filesystem::exists (encPath)) {
|
||||
std::ifstream crc_file (json_path, 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 ());
|
||||
crcBool = CheckCRC (encPath, crc);
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
encPath = path.string ();
|
||||
}
|
||||
} else
|
||||
std::cout << "Using cached file for " << std::filesystem::relative (json_path)
|
||||
<< std::endl; // Otherwise use the already encrypted file.
|
||||
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
}
|
||||
|
||||
return originalCreateFileAHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
|
||||
hTemplateFile);
|
||||
}
|
||||
|
||||
// HOOK (HANDLE, CreateFileWHook, PROC_ADDRESS ("kernel32.dll", "CreateFileW"), LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
// LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
// std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
|
||||
// std::string strFileName = converter.to_bytes (lpFileName);
|
||||
|
||||
// std::filesystem::path path (strFileName);
|
||||
// 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";
|
||||
|
||||
// if (path.string ().find (originalDataFolder.string ()) != std::string::npos) {
|
||||
// auto newPath = path.string ();
|
||||
// newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
|
||||
|
||||
// if (std::filesystem::exists (newPath)) {
|
||||
// std::wstring wNewPath = converter.from_bytes (newPath);
|
||||
// std::wcout << L"Redirecting " << lpFileName << L" to " << wNewPath << std::endl;
|
||||
// return originalCreateFileWHook (wNewPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
// dwFlagsAndAttributes, hTemplateFile);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return originalCreateFileWHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
|
||||
// hTemplateFile);
|
||||
// }
|
||||
|
||||
void
|
||||
Init () {
|
||||
std::wcout << "Using LayeredFs!" << std::endl;
|
||||
register_cipher (&aes_desc);
|
||||
INSTALL_HOOK (CreateFileAHook);
|
||||
// INSTALL_HOOK (CreateFileWHook);
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,13 @@ void Init ();
|
||||
namespace CHN00 {
|
||||
void Init ();
|
||||
} // namespace CHN00
|
||||
namespace Dxgi {
|
||||
void Init ();
|
||||
} // namespace Dxgi
|
||||
namespace FpsLimiter {
|
||||
void Init (float fpsLimit);
|
||||
void Update ();
|
||||
} // namespace FpsLimiter
|
||||
namespace Audio {
|
||||
void Init ();
|
||||
} // namespace Audio
|
||||
@ -21,4 +28,7 @@ void Update ();
|
||||
namespace AmAuth {
|
||||
void Init ();
|
||||
} // namespace AmAuth
|
||||
namespace LayeredFS {
|
||||
void Init ();
|
||||
} // namespace LayeredFS
|
||||
} // namespace patches
|
||||
|
@ -99,7 +99,6 @@ void
|
||||
Init () {
|
||||
i32 xRes = 1920;
|
||||
i32 yRes = 1080;
|
||||
bool vsync = false;
|
||||
bool unlockSongs = true;
|
||||
bool fixLanguage = false;
|
||||
bool demoMovie = true;
|
||||
@ -128,12 +127,6 @@ Init () {
|
||||
if (config_ptr) {
|
||||
auto patches = openConfigSection (config_ptr.get (), "patches");
|
||||
if (patches) {
|
||||
auto res = openConfigSection (patches, "res");
|
||||
if (res) {
|
||||
xRes = readConfigInt (res, "x", xRes);
|
||||
yRes = readConfigInt (res, "y", yRes);
|
||||
}
|
||||
vsync = readConfigBool (patches, "vsync", vsync);
|
||||
unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
auto chn00 = openConfigSection (patches, "chn00");
|
||||
if (chn00) {
|
||||
@ -143,12 +136,20 @@ Init () {
|
||||
modeCollabo026 = readConfigBool (chn00, "mode_collabo026", modeCollabo026);
|
||||
}
|
||||
}
|
||||
|
||||
auto graphics = openConfigSection (config_ptr.get (), "graphics");
|
||||
if (graphics) {
|
||||
auto res = openConfigSection (graphics, "res");
|
||||
if (res) {
|
||||
xRes = readConfigInt (res, "x", xRes);
|
||||
yRes = readConfigInt (res, "y", yRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply common config patch
|
||||
WRITE_MEMORY (ASLR (0x1404A4ED3), i32, xRes);
|
||||
WRITE_MEMORY (ASLR (0x1404A4EDA), i32, yRes);
|
||||
if (!vsync) WRITE_MEMORY (ASLR (0x1405FC5B9), u8, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x90);
|
||||
if (unlockSongs) WRITE_MEMORY (ASLR (0x140425BCD), u8, 0xB0, 0x01);
|
||||
|
||||
// Bypass errors
|
||||
|
@ -48,28 +48,27 @@ void
|
||||
Init () {
|
||||
i32 xRes = 1920;
|
||||
i32 yRes = 1080;
|
||||
bool vsync = false;
|
||||
bool unlockSongs = true;
|
||||
|
||||
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 patches = openConfigSection (config_ptr.get (), "patches");
|
||||
if (patches) {
|
||||
auto res = openConfigSection (patches, "res");
|
||||
if (patches) unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
|
||||
auto graphics = openConfigSection (config_ptr.get (), "graphics");
|
||||
if (graphics) {
|
||||
auto res = openConfigSection (graphics, "res");
|
||||
if (res) {
|
||||
xRes = readConfigInt (res, "x", xRes);
|
||||
yRes = readConfigInt (res, "y", yRes);
|
||||
}
|
||||
vsync = readConfigBool (patches, "vsync", vsync);
|
||||
unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply common config patch
|
||||
WRITE_MEMORY (ASLR (0x140224B2B), i32, xRes);
|
||||
WRITE_MEMORY (ASLR (0x140224B32), i32, yRes);
|
||||
if (!vsync) WRITE_MEMORY (ASLR (0x1403D6189), u8, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x90);
|
||||
if (unlockSongs) WRITE_MEMORY (ASLR (0x1401F6B78), u8, 0xB0, 0x01);
|
||||
|
||||
// Bypass errors
|
||||
|
@ -50,28 +50,27 @@ void
|
||||
Init () {
|
||||
i32 xRes = 1920;
|
||||
i32 yRes = 1080;
|
||||
bool vsync = false;
|
||||
bool unlockSongs = true;
|
||||
|
||||
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 patches = openConfigSection (config_ptr.get (), "patches");
|
||||
if (patches) {
|
||||
auto res = openConfigSection (patches, "res");
|
||||
if (patches) unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
|
||||
auto graphics = openConfigSection (config_ptr.get (), "graphics");
|
||||
if (graphics) {
|
||||
auto res = openConfigSection (graphics, "res");
|
||||
if (res) {
|
||||
xRes = readConfigInt (res, "x", xRes);
|
||||
yRes = readConfigInt (res, "y", yRes);
|
||||
}
|
||||
vsync = readConfigBool (patches, "vsync", vsync);
|
||||
unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply common config patch
|
||||
WRITE_MEMORY (ASLR (0x14035FC5B), i32, xRes);
|
||||
WRITE_MEMORY (ASLR (0x14035FC62), i32, yRes);
|
||||
if (!vsync) WRITE_MEMORY (ASLR (0x140517339), u8, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x90);
|
||||
if (unlockSongs) WRITE_MEMORY (ASLR (0x140314E8D), u8, 0xB0, 0x01);
|
||||
|
||||
// Bypass errors
|
||||
|
@ -44,28 +44,27 @@ void
|
||||
Init () {
|
||||
i32 xRes = 1920;
|
||||
i32 yRes = 1080;
|
||||
bool vsync = false;
|
||||
bool unlockSongs = true;
|
||||
|
||||
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 patches = openConfigSection (config_ptr.get (), "patches");
|
||||
if (patches) {
|
||||
auto res = openConfigSection (patches, "res");
|
||||
if (patches) unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
|
||||
auto graphics = openConfigSection (config_ptr.get (), "graphics");
|
||||
if (graphics) {
|
||||
auto res = openConfigSection (graphics, "res");
|
||||
if (res) {
|
||||
xRes = readConfigInt (res, "x", xRes);
|
||||
yRes = readConfigInt (res, "y", yRes);
|
||||
}
|
||||
vsync = readConfigBool (patches, "vsync", vsync);
|
||||
unlockSongs = readConfigBool (patches, "unlock_songs", unlockSongs);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply common config patch
|
||||
WRITE_MEMORY (ASLR (0x140494533), i32, xRes);
|
||||
WRITE_MEMORY (ASLR (0x14049453A), i32, yRes);
|
||||
if (!vsync) WRITE_MEMORY (ASLR (0x14064C7E9), u8, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x90);
|
||||
if (unlockSongs) WRITE_MEMORY (ASLR (0x1403F45CF), u8, 0xB0, 0x01);
|
||||
|
||||
// Bypass errors
|
||||
|
Loading…
Reference in New Issue
Block a user