mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-10-06 10:08:13 -07:00
Compare commits
9 Commits
26-09-01
...
531e7a54b3
| Author | SHA1 | Date | |
|---|---|---|---|
| 531e7a54b3 | |||
| f11b9d0189 | |||
| 112b1aecb0 | |||
| de466db29f | |||
| 08e99dea4a | |||
| 00e904539e | |||
| eaab4698eb | |||
| a48eccab93 | |||
| 81c913726d |
@@ -29,4 +29,33 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }}
|
name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }}
|
||||||
path: src/spice2x/bin
|
path: src/spice2x/bin
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
msvc-ci:
|
||||||
|
name: Build (MSVC ${{ matrix.arch }}-bit)
|
||||||
|
# windows-latest ships Visual Studio 2026; pin to the VS 2022 image, which is
|
||||||
|
# the toolset spice2x is actually developed against
|
||||||
|
runs-on: windows-2022
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- arch: 32
|
||||||
|
platform: Win32
|
||||||
|
target: spicetools_spice spicetools_cfg
|
||||||
|
- arch: 64
|
||||||
|
platform: x64
|
||||||
|
target: spicetools_spice64
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./src/spice2x
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Configure
|
||||||
|
run: >
|
||||||
|
cmake -S . -B cmake-build-msvc-${{ matrix.arch }}
|
||||||
|
-A ${{ matrix.platform }}
|
||||||
|
- name: Compile
|
||||||
|
run: >
|
||||||
|
cmake --build cmake-build-msvc-${{ matrix.arch }}
|
||||||
|
--config Release --target ${{ matrix.target }}
|
||||||
|
|||||||
+1
-1
@@ -105,5 +105,5 @@ if (conditional)
|
|||||||
* We don't use GSL.
|
* We don't use GSL.
|
||||||
* Don't throw exceptions.
|
* Don't throw exceptions.
|
||||||
* Stick to smart pointers. For interfacing with C Win32 API and raw buffers, use `unique_plain_ptr`.
|
* Stick to smart pointers. For interfacing with C Win32 API and raw buffers, use `unique_plain_ptr`.
|
||||||
* Writing in C is also completely acceptable.
|
* Writing in C is also completely acceptable but prefer to use RAII patterns.
|
||||||
* Other than that, there are no strict rules for code formatting, but please attempt to emulate the style around the code you are modifying.
|
* Other than that, there are no strict rules for code formatting, but please attempt to emulate the style around the code you are modifying.
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
hooks/networkhook.cpp
|
hooks/networkhook.cpp
|
||||||
hooks/icmphook_net.cpp
|
hooks/icmphook_net.cpp
|
||||||
hooks/icmphook_iphlpapi.cpp
|
hooks/icmphook_iphlpapi.cpp
|
||||||
|
hooks/nicspoof.cpp
|
||||||
hooks/powrprof.cpp
|
hooks/powrprof.cpp
|
||||||
#hooks/rom.cpp
|
#hooks/rom.cpp
|
||||||
hooks/setupapihook.cpp
|
hooks/setupapihook.cpp
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
|
#include "external/rapidjson/error/en.h"
|
||||||
#include "util/crypt.h"
|
#include "util/crypt.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -92,6 +93,7 @@ Controller::Controller(unsigned short port, std::string password, bool pretty)
|
|||||||
// bind socket to address
|
// bind socket to address
|
||||||
if (bind(this->server, (sockaddr *) &server_address, sizeof(sockaddr)) == -1) {
|
if (bind(this->server, (sockaddr *) &server_address, sizeof(sockaddr)) == -1) {
|
||||||
log_warning("api", "could not bind socket on port {}: {}", port, get_last_error_string());
|
log_warning("api", "could not bind socket on port {}: {}", port, get_last_error_string());
|
||||||
|
closesocket(this->server);
|
||||||
this->server = INVALID_SOCKET;
|
this->server = INVALID_SOCKET;
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_fatal("api", "failed to start server");
|
log_fatal("api", "failed to start server");
|
||||||
@@ -102,6 +104,7 @@ Controller::Controller(unsigned short port, std::string password, bool pretty)
|
|||||||
// set socket to listen
|
// set socket to listen
|
||||||
if (listen(this->server, server_backlog) == -1) {
|
if (listen(this->server, server_backlog) == -1) {
|
||||||
log_warning("api", "could not listen to socket on port {}: {}", port, get_last_error_string());
|
log_warning("api", "could not listen to socket on port {}: {}", port, get_last_error_string());
|
||||||
|
closesocket(this->server);
|
||||||
this->server = INVALID_SOCKET;
|
this->server = INVALID_SOCKET;
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
log_fatal("api", "failed to start server");
|
log_fatal("api", "failed to start server");
|
||||||
@@ -312,6 +315,8 @@ bool Controller::process_request(ClientState *state, const char *in, size_t in_s
|
|||||||
|
|
||||||
// check for parse error
|
// check for parse error
|
||||||
if (document.HasParseError()) {
|
if (document.HasParseError()) {
|
||||||
|
log_warning("api", "Request JSON parse error: {} (byte offset {}, decrypted request length {} bytes)",
|
||||||
|
GetParseError_En(document.GetParseError()), document.GetErrorOffset(), in_size);
|
||||||
|
|
||||||
// return empty response and close connection
|
// return empty response and close connection
|
||||||
out->push_back(0);
|
out->push_back(0);
|
||||||
@@ -443,7 +448,9 @@ void Controller::free_socket() {
|
|||||||
this->server = INVALID_SOCKET;
|
this->server = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->websocket->free_socket();
|
if (this->websocket) {
|
||||||
|
this->websocket->free_socket();
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &s : this->serial) {
|
for (auto &s : this->serial) {
|
||||||
s->free_port();
|
s->free_port();
|
||||||
|
|||||||
@@ -41,21 +41,21 @@ namespace api {
|
|||||||
bool pretty;
|
bool pretty;
|
||||||
|
|
||||||
// server
|
// server
|
||||||
WebSocketController *websocket;
|
WebSocketController *websocket = nullptr;
|
||||||
std::vector<SerialController *> serial;
|
std::vector<SerialController *> serial;
|
||||||
std::vector<std::thread> server_workers;
|
std::vector<std::thread> server_workers;
|
||||||
std::vector<std::thread> server_handlers;
|
std::vector<std::thread> server_handlers;
|
||||||
std::mutex server_handlers_m;
|
std::mutex server_handlers_m;
|
||||||
std::vector<api::ClientState *> client_states;
|
std::vector<api::ClientState *> client_states;
|
||||||
std::mutex client_states_m;
|
std::mutex client_states_m;
|
||||||
SOCKET server;
|
SOCKET server = INVALID_SOCKET;
|
||||||
void server_worker();
|
void server_worker();
|
||||||
void connection_handler(ClientState client_state);
|
void connection_handler(ClientState client_state);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// state
|
// state
|
||||||
bool server_running;
|
bool server_running = false;
|
||||||
|
|
||||||
// constructor / destructor
|
// constructor / destructor
|
||||||
Controller(unsigned short port, std::string password, bool pretty);
|
Controller(unsigned short port, std::string password, bool pretty);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace api::modules {
|
|||||||
return error_type(res, "amount", "int");
|
return error_type(res, "amount", "int");
|
||||||
|
|
||||||
// add to coin stock
|
// add to coin stock
|
||||||
eamuse_coin_set_stock(eamuse_coin_get_stock() + std::max(0, req.params[0].GetInt()));
|
eamuse_coin_add(std::max(0, req.params[0].GetInt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Vendored
+7
-3
@@ -50,6 +50,7 @@ static const BYTE UID_CMD[5] = { 0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u };
|
|||||||
enum scard_atr_protocol {
|
enum scard_atr_protocol {
|
||||||
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
|
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
|
||||||
SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B,
|
SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B,
|
||||||
|
SCARD_ATR_PROTOCOL_ISO15693_PART4 = 0x0C,
|
||||||
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
|
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
|
||||||
SCARD_ATR_PROTOCOL_FELICA_424K = 0x12,
|
SCARD_ATR_PROTOCOL_FELICA_424K = 0x12,
|
||||||
};
|
};
|
||||||
@@ -121,11 +122,14 @@ void scard_update(SCARDCONTEXT hContext, LPCTSTR readerName, uint8_t unit_no) {
|
|||||||
BYTE cardProtocol = atr[12];
|
BYTE cardProtocol = atr[12];
|
||||||
BOOL shouldReverseUid = false;
|
BOOL shouldReverseUid = false;
|
||||||
bool is_felica = false;
|
bool is_felica = false;
|
||||||
if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART3) {
|
if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3) {
|
||||||
|
log_info("scard", "card protocol: ISO14443_PART3");
|
||||||
|
} else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART3) {
|
||||||
log_info("scard", "card protocol: ISO15693_PART3");
|
log_info("scard", "card protocol: ISO15693_PART3");
|
||||||
shouldReverseUid = true;
|
shouldReverseUid = true;
|
||||||
} else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3) {
|
} else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART4) {
|
||||||
log_info("scard", "card protocol: ISO14443_PART3");
|
log_info("scard", "card protocol: ISO15693_PART4");
|
||||||
|
shouldReverseUid = true;
|
||||||
} else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) {
|
} else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) {
|
||||||
log_info("scard", "card protocol: FELICA_212K");
|
log_info("scard", "card protocol: FELICA_212K");
|
||||||
is_felica = true;
|
is_felica = true;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#if SPICE64 && !SPICE_XP
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
#include "mf_wrappers.h"
|
#include "mf_wrappers.h"
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
@@ -46,9 +47,7 @@ struct PredefinedHook {
|
|||||||
uintptr_t hook_afp_texture_offset;
|
uintptr_t hook_afp_texture_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
PredefinedHook g_predefinedHooks[] = {};
|
std::array<PredefinedHook, 0> g_predefinedHooks = {};
|
||||||
|
|
||||||
const DWORD g_predefinedHooksLength = ARRAYSIZE(g_predefinedHooks);
|
|
||||||
|
|
||||||
namespace games::iidx {
|
namespace games::iidx {
|
||||||
|
|
||||||
@@ -110,14 +109,14 @@ namespace games::iidx {
|
|||||||
auto pe = fmt::format("{:x}_{:x}", time_date_stamp, address_of_entry_point);
|
auto pe = fmt::format("{:x}_{:x}", time_date_stamp, address_of_entry_point);
|
||||||
log_info("iidx:camhook", "Locating predefined hook addresses for LDJ-{}", pe);
|
log_info("iidx:camhook", "Locating predefined hook addresses for LDJ-{}", pe);
|
||||||
|
|
||||||
for (DWORD i = 0; i < g_predefinedHooksLength; i++) {
|
for (auto &hook : g_predefinedHooks) {
|
||||||
if (pe.compare(g_predefinedHooks[i].pe_identifier) == 0) {
|
if (pe.compare(hook.pe_identifier) == 0) {
|
||||||
log_misc("iidx:camhook", "Found predefined addresses");
|
log_misc("iidx:camhook", "Found predefined addresses");
|
||||||
addr_hook_a = g_predefinedHooks[i].hook_a;
|
addr_hook_a = hook.hook_a;
|
||||||
addr_textures = g_predefinedHooks[i].hook_textures;
|
addr_textures = hook.hook_textures;
|
||||||
addr_camera_manager = g_predefinedHooks[i].hook_camera_manager;
|
addr_camera_manager = hook.hook_camera_manager;
|
||||||
addr_device_offset = g_predefinedHooks[i].hook_device_offset;
|
addr_device_offset = hook.hook_device_offset;
|
||||||
addr_afp_texture_offset = g_predefinedHooks[i].hook_afp_texture_offset;
|
addr_afp_texture_offset = hook.hook_afp_texture_offset;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ DEFINE_GUID(IID_IAudioClient,
|
|||||||
0x1cb9ad4c, 0xdbfa, 0x4c32,
|
0x1cb9ad4c, 0xdbfa, 0x4c32,
|
||||||
0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2);
|
0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2);
|
||||||
|
|
||||||
|
DEFINE_GUID(IID_IAudioClient3,
|
||||||
|
0x7ed4ee07, 0x8e67, 0x4cd4,
|
||||||
|
0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42);
|
||||||
|
|
||||||
DEFINE_GUID(IID_IAudioClock,
|
DEFINE_GUID(IID_IAudioClock,
|
||||||
0xcd63314f, 0x3fba, 0x4a1b,
|
0xcd63314f, 0x3fba, 0x4a1b,
|
||||||
0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7);
|
0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7);
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
#include "d3d9_device.h"
|
#include "d3d9_device.h"
|
||||||
|
|
||||||
|
// windows.h defines many stupid things
|
||||||
|
#undef small
|
||||||
|
|
||||||
bool gfdm_two_head_exclusive() {
|
bool gfdm_two_head_exclusive() {
|
||||||
return games::gitadora::is_arena_model()
|
return games::gitadora::is_arena_model()
|
||||||
&& games::gitadora::ARENA_TWO_HEAD_EXCLUSIVE
|
&& games::gitadora::ARENA_TWO_HEAD_EXCLUSIVE
|
||||||
|
|||||||
@@ -0,0 +1,546 @@
|
|||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
|
#include "nicspoof.h"
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr uint32_t k_local_ip = 0x0A390302u; /* 10.57.3.2 */
|
||||||
|
constexpr uint32_t k_gateway = 0x0A390301u; /* 10.57.3.1 */
|
||||||
|
constexpr uint32_t k_dns = 0x0A390301u; /* 10.57.3.1 */
|
||||||
|
constexpr uint32_t k_dhcp = 0x0A390301u; /* 10.57.3.1 */
|
||||||
|
constexpr uint32_t k_mask = 0xFFFFFF00u; /* 255.255.255.0 */
|
||||||
|
constexpr uint32_t k_subnet = 0x0A390300u;
|
||||||
|
constexpr int k_prefix_len = 24;
|
||||||
|
constexpr DWORD k_ifindex = 77;
|
||||||
|
constexpr uint8_t k_fake_mac[6] = {0x12, 0x37, 0x13, 0x37, 0x13, 0x37};
|
||||||
|
constexpr char k_hostname[] = "N1CSP00F";
|
||||||
|
constexpr char k_domain[] = "local";
|
||||||
|
constexpr char k_hostname_fqdn[] = "N1CSP00F.local";
|
||||||
|
constexpr wchar_t k_domain_w[] = L"local";
|
||||||
|
constexpr char k_adapter_name[] = "{NICSPOOF-0880-0001-4250-4E6963537066}";
|
||||||
|
constexpr char k_adapter_desc[] = "NicSpoof Virtual Ethernet";
|
||||||
|
constexpr wchar_t k_friendly_name[] = L"NicSpoof";
|
||||||
|
constexpr wchar_t k_adapter_desc_w[] = L"NicSpoof Virtual Ethernet";
|
||||||
|
|
||||||
|
#define ALIGN_UP_PTR(p, a) \
|
||||||
|
((BYTE *)(((ULONG_PTR)(p) + ((ULONG_PTR)(a) - 1)) & ~((ULONG_PTR)(a) - 1)))
|
||||||
|
|
||||||
|
bool g_log_adapters = true;
|
||||||
|
bool g_log_params = true;
|
||||||
|
|
||||||
|
[[maybe_unused]] decltype(GetAdaptersAddresses) *GetAdaptersAddresses_orig = nullptr;
|
||||||
|
[[maybe_unused]] decltype(GetAdaptersInfo) *GetAdaptersInfo_orig = nullptr;
|
||||||
|
[[maybe_unused]] decltype(GetNetworkParams) *GetNetworkParams_orig = nullptr;
|
||||||
|
[[maybe_unused]] decltype(getaddrinfo) *getaddrinfo_orig = nullptr;
|
||||||
|
[[maybe_unused]] decltype(freeaddrinfo) *freeaddrinfo_orig = nullptr;
|
||||||
|
|
||||||
|
void ip_to_str(uint32_t ip, char *out, size_t n) {
|
||||||
|
if (!out || n == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snprintf(out, n, "%u.%u.%u.%u",
|
||||||
|
(ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
|
||||||
|
out[n - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t parse_ipv4(const char *s) {
|
||||||
|
unsigned a = 0;
|
||||||
|
unsigned b = 0;
|
||||||
|
unsigned c = 0;
|
||||||
|
unsigned d = 0;
|
||||||
|
char trail = 0;
|
||||||
|
if (!s) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (sscanf(s, "%u.%u.%u.%u%c", &a, &b, &c, &d, &trail) != 4) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (a > 255 || b > 255 || c > 255 || d > 255) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (a << 24) | (b << 16) | (c << 8) | d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool name_is_local(const char *name) {
|
||||||
|
if (!name || !name[0]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_stricmp(name, "localhost") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_stricmp(name, k_hostname) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_stricmp(name, k_hostname_fqdn) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool name_is_numeric_ipv4(const char *name) {
|
||||||
|
unsigned a = 0;
|
||||||
|
unsigned b = 0;
|
||||||
|
unsigned c = 0;
|
||||||
|
unsigned d = 0;
|
||||||
|
char trail = 0;
|
||||||
|
if (!name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sscanf(name, "%u.%u.%u.%u%c", &a, &b, &c, &d, &trail) != 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return a <= 255 && b <= 255 && c <= 255 && d <= 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI GetAdaptersInfo_hook(PIP_ADAPTER_INFO p, PULONG s) {
|
||||||
|
ULONG need = sizeof(IP_ADAPTER_INFO) + 32;
|
||||||
|
char ip[16];
|
||||||
|
char gw[16];
|
||||||
|
char dhcp[16];
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
if (!p || *s < need) {
|
||||||
|
*s = need;
|
||||||
|
return ERROR_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
memset(p, 0, need);
|
||||||
|
p->Index = k_ifindex;
|
||||||
|
strcpy(p->AdapterName, k_adapter_name);
|
||||||
|
strcpy(p->Description, k_adapter_desc);
|
||||||
|
p->AddressLength = 6;
|
||||||
|
memcpy(p->Address, k_fake_mac, 6);
|
||||||
|
p->Type = MIB_IF_TYPE_ETHERNET;
|
||||||
|
p->DhcpEnabled = 1;
|
||||||
|
ip_to_str(k_local_ip, ip, sizeof(ip));
|
||||||
|
ip_to_str(k_gateway, gw, sizeof(gw));
|
||||||
|
ip_to_str(k_dhcp, dhcp, sizeof(dhcp));
|
||||||
|
strcpy(p->IpAddressList.IpAddress.String, ip);
|
||||||
|
ip_to_str(k_mask, p->IpAddressList.IpMask.String,
|
||||||
|
sizeof(p->IpAddressList.IpMask.String));
|
||||||
|
p->IpAddressList.Context = 0;
|
||||||
|
p->Next = nullptr;
|
||||||
|
strcpy(p->GatewayList.IpAddress.String, gw);
|
||||||
|
p->GatewayList.IpMask.String[0] = 0;
|
||||||
|
p->GatewayList.Context = 0;
|
||||||
|
p->GatewayList.Next = nullptr;
|
||||||
|
strcpy(p->DhcpServer.IpAddress.String, dhcp);
|
||||||
|
p->DhcpServer.IpMask.String[0] = 0;
|
||||||
|
p->DhcpServer.Context = 0;
|
||||||
|
p->DhcpServer.Next = nullptr;
|
||||||
|
*s = need;
|
||||||
|
if (g_log_adapters) {
|
||||||
|
log_misc("network", "NIC spoof GetAdaptersInfo ip={} gw={} ifindex={}",
|
||||||
|
ip, gw, static_cast<unsigned>(k_ifindex));
|
||||||
|
g_log_adapters = false;
|
||||||
|
}
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI GetNetworkParams_hook(PFIXED_INFO p, PULONG s) {
|
||||||
|
ULONG need = static_cast<ULONG>(sizeof(FIXED_INFO));
|
||||||
|
char dns[16];
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
if (!p || *s < need) {
|
||||||
|
*s = need;
|
||||||
|
return ERROR_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
memset(p, 0, need);
|
||||||
|
strncpy(p->HostName, k_hostname, sizeof(p->HostName) - 1);
|
||||||
|
strncpy(p->DomainName, k_domain, sizeof(p->DomainName) - 1);
|
||||||
|
ip_to_str(k_dns, dns, sizeof(dns));
|
||||||
|
strcpy(p->DnsServerList.IpAddress.String, dns);
|
||||||
|
p->DnsServerList.IpMask.String[0] = 0;
|
||||||
|
p->DnsServerList.Context = 0;
|
||||||
|
p->DnsServerList.Next = nullptr;
|
||||||
|
p->CurrentDnsServer = &p->DnsServerList;
|
||||||
|
p->NodeType = 1;
|
||||||
|
p->EnableDns = 1;
|
||||||
|
*s = need;
|
||||||
|
if (g_log_params) {
|
||||||
|
log_misc("network", "NIC spoof GetNetworkParams host={} domain={} dns={}",
|
||||||
|
k_hostname, k_domain, dns);
|
||||||
|
g_log_params = false;
|
||||||
|
}
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG WINAPI GetAdaptersAddresses_hook(
|
||||||
|
ULONG Family,
|
||||||
|
ULONG Flags,
|
||||||
|
PVOID Reserved,
|
||||||
|
PIP_ADAPTER_ADDRESSES AdapterAddresses,
|
||||||
|
PULONG SizePointer) {
|
||||||
|
enum { STR_SLOT = 64 };
|
||||||
|
/*
|
||||||
|
* Layout is fixed: adapter header, 4 string slots, then unicast/prefix/
|
||||||
|
* gateway/dns + sockaddrs with alignment slack. need is computed to always
|
||||||
|
* fit; never return BUFFER_OVERFLOW after a successful size probe.
|
||||||
|
*/
|
||||||
|
ULONG need = static_cast<ULONG>(sizeof(IP_ADAPTER_ADDRESSES) + STR_SLOT * 4 +
|
||||||
|
sizeof(void *) * 8 +
|
||||||
|
sizeof(IP_ADAPTER_UNICAST_ADDRESS) +
|
||||||
|
sizeof(IP_ADAPTER_PREFIX) +
|
||||||
|
sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) +
|
||||||
|
sizeof(IP_ADAPTER_GATEWAY_ADDRESS) +
|
||||||
|
sizeof(sockaddr_in) * 4 + 64);
|
||||||
|
BYTE *blob = nullptr;
|
||||||
|
PIP_ADAPTER_ADDRESSES a = nullptr;
|
||||||
|
PIP_ADAPTER_UNICAST_ADDRESS u = nullptr;
|
||||||
|
PIP_ADAPTER_PREFIX pref = nullptr;
|
||||||
|
PIP_ADAPTER_GATEWAY_ADDRESS gw = nullptr;
|
||||||
|
PIP_ADAPTER_DNS_SERVER_ADDRESS dns = nullptr;
|
||||||
|
sockaddr_in *sa = nullptr;
|
||||||
|
sockaddr_in *sm = nullptr;
|
||||||
|
sockaddr_in *sg = nullptr;
|
||||||
|
sockaddr_in *ds = nullptr;
|
||||||
|
|
||||||
|
(void)Reserved;
|
||||||
|
(void)Flags;
|
||||||
|
|
||||||
|
if (Family != AF_INET && Family != AF_UNSPEC) {
|
||||||
|
if (SizePointer) {
|
||||||
|
*SizePointer = 0;
|
||||||
|
}
|
||||||
|
return ERROR_NO_DATA;
|
||||||
|
}
|
||||||
|
if (!SizePointer) {
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
if (!AdapterAddresses || *SizePointer < need) {
|
||||||
|
*SizePointer = need;
|
||||||
|
return ERROR_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(AdapterAddresses, 0, need);
|
||||||
|
a = AdapterAddresses;
|
||||||
|
blob = reinterpret_cast<BYTE *>(a + 1);
|
||||||
|
|
||||||
|
a->Length = sizeof(IP_ADAPTER_ADDRESSES);
|
||||||
|
a->IfIndex = k_ifindex;
|
||||||
|
a->AdapterName = reinterpret_cast<PCHAR>(blob);
|
||||||
|
strcpy(reinterpret_cast<char *>(blob), k_adapter_name);
|
||||||
|
blob += STR_SLOT;
|
||||||
|
a->FriendlyName = reinterpret_cast<PWCHAR>(blob);
|
||||||
|
wcscpy(reinterpret_cast<wchar_t *>(blob), k_friendly_name);
|
||||||
|
blob += STR_SLOT;
|
||||||
|
a->Description = reinterpret_cast<PWCHAR>(blob);
|
||||||
|
wcscpy(reinterpret_cast<wchar_t *>(blob), k_adapter_desc_w);
|
||||||
|
blob += STR_SLOT;
|
||||||
|
a->PhysicalAddressLength = 6;
|
||||||
|
memcpy(a->PhysicalAddress, k_fake_mac, 6);
|
||||||
|
a->Flags = IP_ADAPTER_DHCP_ENABLED;
|
||||||
|
a->Mtu = 1500;
|
||||||
|
a->IfType = IF_TYPE_ETHERNET_CSMACD;
|
||||||
|
a->OperStatus = IfOperStatusUp;
|
||||||
|
a->Ipv4Enabled = 1;
|
||||||
|
a->DnsSuffix = reinterpret_cast<PWCHAR>(blob);
|
||||||
|
wcsncpy(reinterpret_cast<wchar_t *>(blob), k_domain_w,
|
||||||
|
(STR_SLOT / sizeof(wchar_t)) - 1);
|
||||||
|
reinterpret_cast<wchar_t *>(blob)[(STR_SLOT / sizeof(wchar_t)) - 1] = 0;
|
||||||
|
blob += STR_SLOT;
|
||||||
|
|
||||||
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
||||||
|
u = reinterpret_cast<PIP_ADAPTER_UNICAST_ADDRESS>(blob);
|
||||||
|
blob += sizeof(*u);
|
||||||
|
memset(u, 0, sizeof(*u));
|
||||||
|
u->Length = sizeof(*u);
|
||||||
|
u->DadState = IpDadStatePreferred;
|
||||||
|
u->OnLinkPrefixLength = static_cast<UCHAR>(k_prefix_len);
|
||||||
|
sa = reinterpret_cast<sockaddr_in *>(blob);
|
||||||
|
blob += sizeof(*sa);
|
||||||
|
memset(sa, 0, sizeof(*sa));
|
||||||
|
sa->sin_family = AF_INET;
|
||||||
|
sa->sin_addr.s_addr = htonl(k_local_ip);
|
||||||
|
u->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sa);
|
||||||
|
u->Address.iSockaddrLength = sizeof(*sa);
|
||||||
|
a->FirstUnicastAddress = u;
|
||||||
|
|
||||||
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
||||||
|
pref = reinterpret_cast<PIP_ADAPTER_PREFIX>(blob);
|
||||||
|
blob += sizeof(*pref);
|
||||||
|
memset(pref, 0, sizeof(*pref));
|
||||||
|
pref->Length = sizeof(*pref);
|
||||||
|
pref->PrefixLength = static_cast<ULONG>(k_prefix_len);
|
||||||
|
sm = reinterpret_cast<sockaddr_in *>(blob);
|
||||||
|
blob += sizeof(*sm);
|
||||||
|
memset(sm, 0, sizeof(*sm));
|
||||||
|
sm->sin_family = AF_INET;
|
||||||
|
sm->sin_addr.s_addr = htonl(k_subnet);
|
||||||
|
pref->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sm);
|
||||||
|
pref->Address.iSockaddrLength = sizeof(*sm);
|
||||||
|
a->FirstPrefix = pref;
|
||||||
|
|
||||||
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
||||||
|
gw = reinterpret_cast<PIP_ADAPTER_GATEWAY_ADDRESS>(blob);
|
||||||
|
blob += sizeof(*gw);
|
||||||
|
memset(gw, 0, sizeof(*gw));
|
||||||
|
gw->Length = sizeof(*gw);
|
||||||
|
sg = reinterpret_cast<sockaddr_in *>(blob);
|
||||||
|
blob += sizeof(*sg);
|
||||||
|
memset(sg, 0, sizeof(*sg));
|
||||||
|
sg->sin_family = AF_INET;
|
||||||
|
sg->sin_addr.s_addr = htonl(k_gateway);
|
||||||
|
gw->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sg);
|
||||||
|
gw->Address.iSockaddrLength = sizeof(*sg);
|
||||||
|
a->FirstGatewayAddress = gw;
|
||||||
|
|
||||||
|
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
||||||
|
dns = reinterpret_cast<PIP_ADAPTER_DNS_SERVER_ADDRESS>(blob);
|
||||||
|
blob += sizeof(*dns);
|
||||||
|
memset(dns, 0, sizeof(*dns));
|
||||||
|
ds = reinterpret_cast<sockaddr_in *>(blob);
|
||||||
|
blob += sizeof(*ds);
|
||||||
|
memset(ds, 0, sizeof(*ds));
|
||||||
|
ds->sin_family = AF_INET;
|
||||||
|
ds->sin_addr.s_addr = htonl(k_dns);
|
||||||
|
dns->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(ds);
|
||||||
|
dns->Address.iSockaddrLength = sizeof(*ds);
|
||||||
|
dns->Next = nullptr;
|
||||||
|
a->FirstDnsServerAddress = dns;
|
||||||
|
|
||||||
|
a->Next = nullptr;
|
||||||
|
*SizePointer = need;
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t AI_MAGIC = 0x4E535041u; /* 'NSPA' */
|
||||||
|
|
||||||
|
struct SpoofAiHdr {
|
||||||
|
uint32_t magic;
|
||||||
|
ADDRINFOA ai;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpoofAiHdr *ai_hdr_from_ai(ADDRINFOA *ai) {
|
||||||
|
SpoofAiHdr *h = nullptr;
|
||||||
|
if (!ai) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
h = reinterpret_cast<SpoofAiHdr *>(
|
||||||
|
reinterpret_cast<BYTE *>(ai) - offsetof(SpoofAiHdr, ai));
|
||||||
|
if (h->magic != AI_MAGIC) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wine packed getaddrinfo: sockaddr lives immediately after addrinfo. */
|
||||||
|
bool ai_is_wine_packed(ADDRINFOA *ai) {
|
||||||
|
BYTE *p = reinterpret_cast<BYTE *>(ai);
|
||||||
|
BYTE *addr = nullptr;
|
||||||
|
if (!ai || !ai->ai_addr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
addr = reinterpret_cast<BYTE *>(ai->ai_addr);
|
||||||
|
return addr >= p + sizeof(*ai) &&
|
||||||
|
addr < p + sizeof(*ai) + sizeof(SOCKADDR_STORAGE) + 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dual-safe free for Wine + native Windows:
|
||||||
|
* - Our results: magic header + separate mallocs for addr/canon.
|
||||||
|
* - Wine packed foreign: single free(ai).
|
||||||
|
* - Else (native Windows foreign): free canon, addr, node per link.
|
||||||
|
*/
|
||||||
|
void WSAAPI freeaddrinfo_hook(PADDRINFOA ai) {
|
||||||
|
while (ai) {
|
||||||
|
ADDRINFOA *next = ai->ai_next;
|
||||||
|
SpoofAiHdr *hdr = ai_hdr_from_ai(ai);
|
||||||
|
if (hdr) {
|
||||||
|
free(ai->ai_canonname);
|
||||||
|
free(ai->ai_addr);
|
||||||
|
free(hdr);
|
||||||
|
} else if (ai_is_wine_packed(ai)) {
|
||||||
|
free(ai);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
free(ai->ai_canonname);
|
||||||
|
free(ai->ai_addr);
|
||||||
|
free(ai);
|
||||||
|
}
|
||||||
|
ai = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INT WSAAPI getaddrinfo_hook(
|
||||||
|
PCSTR pNodeName,
|
||||||
|
PCSTR pServiceName,
|
||||||
|
const ADDRINFOA *pHints,
|
||||||
|
PADDRINFOA *ppResult) {
|
||||||
|
SpoofAiHdr *hdr = nullptr;
|
||||||
|
ADDRINFOA *ai = nullptr;
|
||||||
|
sockaddr_in *sa = nullptr;
|
||||||
|
char *canon = nullptr;
|
||||||
|
const char *name_src = nullptr;
|
||||||
|
size_t name_len = 0;
|
||||||
|
uint32_t ip = 0;
|
||||||
|
int port = 0;
|
||||||
|
|
||||||
|
if (!ppResult) {
|
||||||
|
return EAI_FAIL;
|
||||||
|
}
|
||||||
|
*ppResult = nullptr;
|
||||||
|
|
||||||
|
if (pHints && pHints->ai_family == AF_INET6) {
|
||||||
|
return EAI_FAMILY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pNodeName || !pNodeName[0] || name_is_local(pNodeName)) {
|
||||||
|
if (pNodeName && (_stricmp(pNodeName, k_hostname) == 0 ||
|
||||||
|
_stricmp(pNodeName, k_hostname_fqdn) == 0)) {
|
||||||
|
ip = k_local_ip;
|
||||||
|
} else {
|
||||||
|
ip = 0x7F000001u;
|
||||||
|
}
|
||||||
|
} else if (name_is_numeric_ipv4(pNodeName)) {
|
||||||
|
ip = parse_ipv4(pNodeName);
|
||||||
|
} else {
|
||||||
|
/* offline=1: map external names to local_ip */
|
||||||
|
ip = k_local_ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pServiceName && pServiceName[0]) {
|
||||||
|
port = atoi(pServiceName);
|
||||||
|
if (port < 0 || port > 65535) {
|
||||||
|
port = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name_src = (pNodeName && pNodeName[0]) ? pNodeName : "localhost";
|
||||||
|
name_len = strlen(name_src) + 1;
|
||||||
|
if (name_len > 256) {
|
||||||
|
name_len = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = static_cast<SpoofAiHdr *>(calloc(1, sizeof(*hdr)));
|
||||||
|
sa = static_cast<sockaddr_in *>(calloc(1, sizeof(*sa)));
|
||||||
|
canon = static_cast<char *>(malloc(name_len));
|
||||||
|
if (!hdr || !sa || !canon) {
|
||||||
|
free(hdr);
|
||||||
|
free(sa);
|
||||||
|
free(canon);
|
||||||
|
return EAI_MEMORY;
|
||||||
|
}
|
||||||
|
hdr->magic = AI_MAGIC;
|
||||||
|
ai = &hdr->ai;
|
||||||
|
memcpy(canon, name_src, name_len - 1);
|
||||||
|
canon[name_len - 1] = 0;
|
||||||
|
|
||||||
|
sa->sin_family = AF_INET;
|
||||||
|
sa->sin_addr.s_addr = htonl(ip);
|
||||||
|
if (port > 0) {
|
||||||
|
sa->sin_port = htons(static_cast<u_short>(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
ai->ai_family = AF_INET;
|
||||||
|
ai->ai_socktype = pHints && pHints->ai_socktype ?
|
||||||
|
pHints->ai_socktype : SOCK_STREAM;
|
||||||
|
ai->ai_protocol = pHints && pHints->ai_protocol ?
|
||||||
|
pHints->ai_protocol : IPPROTO_TCP;
|
||||||
|
ai->ai_addrlen = sizeof(*sa);
|
||||||
|
ai->ai_addr = reinterpret_cast<struct sockaddr *>(sa);
|
||||||
|
ai->ai_canonname = canon;
|
||||||
|
ai->ai_next = nullptr;
|
||||||
|
*ppResult = ai;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void install_nicspoof_hooks() {
|
||||||
|
static bool done = false;
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
|
||||||
|
char ipstr[16];
|
||||||
|
char maskstr[16];
|
||||||
|
char gwstr[16];
|
||||||
|
ip_to_str(k_local_ip, ipstr, sizeof(ipstr));
|
||||||
|
ip_to_str(k_mask, maskstr, sizeof(maskstr));
|
||||||
|
ip_to_str(k_gateway, gwstr, sizeof(gwstr));
|
||||||
|
log_info("network",
|
||||||
|
"NIC spoof enabled ip={} mask={}/{} gw={} "
|
||||||
|
"mac={:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X} host={} ifindex={}",
|
||||||
|
ipstr, maskstr, k_prefix_len, gwstr,
|
||||||
|
static_cast<unsigned>(k_fake_mac[0]),
|
||||||
|
static_cast<unsigned>(k_fake_mac[1]),
|
||||||
|
static_cast<unsigned>(k_fake_mac[2]),
|
||||||
|
static_cast<unsigned>(k_fake_mac[3]),
|
||||||
|
static_cast<unsigned>(k_fake_mac[4]),
|
||||||
|
static_cast<unsigned>(k_fake_mac[5]),
|
||||||
|
k_hostname, static_cast<unsigned>(k_ifindex));
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"iphlpapi.dll", "GetAdaptersAddresses",
|
||||||
|
(void *) GetAdaptersAddresses_hook,
|
||||||
|
(void **) &GetAdaptersAddresses_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"iphlpapi.dll", "GetAdaptersInfo",
|
||||||
|
(void *) GetAdaptersInfo_hook,
|
||||||
|
(void **) &GetAdaptersInfo_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"iphlpapi.dll", "GetNetworkParams",
|
||||||
|
(void *) GetNetworkParams_hook,
|
||||||
|
(void **) &GetNetworkParams_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "getaddrinfo",
|
||||||
|
(void *) getaddrinfo_hook,
|
||||||
|
(void **) &getaddrinfo_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "freeaddrinfo",
|
||||||
|
(void *) freeaddrinfo_hook,
|
||||||
|
(void **) &freeaddrinfo_orig);
|
||||||
|
|
||||||
|
/* FreeAddrInfoA is often the same address; only hook if distinct. */
|
||||||
|
{
|
||||||
|
HMODULE ws = GetModuleHandleA("ws2_32.dll");
|
||||||
|
void *faa = nullptr;
|
||||||
|
void *fai = nullptr;
|
||||||
|
if (ws) {
|
||||||
|
faa = reinterpret_cast<void *>(GetProcAddress(ws, "FreeAddrInfoA"));
|
||||||
|
fai = reinterpret_cast<void *>(GetProcAddress(ws, "freeaddrinfo"));
|
||||||
|
}
|
||||||
|
if (faa && faa != fai) {
|
||||||
|
decltype(freeaddrinfo) *FreeAddrInfoA_orig = nullptr;
|
||||||
|
if (!detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "FreeAddrInfoA",
|
||||||
|
(void *) freeaddrinfo_hook,
|
||||||
|
(void **) &FreeAddrInfoA_orig)) {
|
||||||
|
log_warning("network",
|
||||||
|
"NIC spoof: FreeAddrInfoA hook was not installed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
log_warning("network",
|
||||||
|
"NIC spoof: one or more hooks failed to install");
|
||||||
|
} else {
|
||||||
|
log_info("network",
|
||||||
|
"NIC spoof hooks installed "
|
||||||
|
"(GAA/GAI/GNP/getaddrinfo/freeaddrinfo)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void nicspoof_init() {
|
||||||
|
install_nicspoof_hooks();
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void nicspoof_init();
|
||||||
@@ -84,6 +84,7 @@
|
|||||||
#include "hooks/lang.h"
|
#include "hooks/lang.h"
|
||||||
#include "hooks/networkhook.h"
|
#include "hooks/networkhook.h"
|
||||||
#include "hooks/icmphook_net.h"
|
#include "hooks/icmphook_net.h"
|
||||||
|
#include "hooks/nicspoof.h"
|
||||||
#include "hooks/unisintrhook.h"
|
#include "hooks/unisintrhook.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "launcher/logger.h"
|
#include "launcher/logger.h"
|
||||||
@@ -257,6 +258,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bool load_stubs = false;
|
bool load_stubs = false;
|
||||||
bool netfix_disable = false;
|
bool netfix_disable = false;
|
||||||
bool icmphook_enable = false;
|
bool icmphook_enable = false;
|
||||||
|
bool nicspoof_enable = false;
|
||||||
bool lang_disable = false;
|
bool lang_disable = false;
|
||||||
std::string process_priority_str = "high";
|
std::string process_priority_str = "high";
|
||||||
bool cardio_enabled = false;
|
bool cardio_enabled = false;
|
||||||
@@ -799,6 +801,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::EnableICMPHook].value_bool()) {
|
if (options[launcher::Options::EnableICMPHook].value_bool()) {
|
||||||
icmphook_enable = true;
|
icmphook_enable = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::EnableNICSpoof].value_bool()) {
|
||||||
|
nicspoof_enable = true;
|
||||||
|
}
|
||||||
if (options[launcher::Options::DisableACPHook].value_bool()) {
|
if (options[launcher::Options::DisableACPHook].value_bool()) {
|
||||||
lang_disable = true;
|
lang_disable = true;
|
||||||
}
|
}
|
||||||
@@ -2558,6 +2563,11 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
icmphook_net_init();
|
icmphook_net_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NIC spoof (opt-in; fake Ethernet with no real NIC)
|
||||||
|
if (nicspoof_enable) {
|
||||||
|
nicspoof_init();
|
||||||
|
}
|
||||||
|
|
||||||
// net fix
|
// net fix
|
||||||
if (!netfix_disable) {
|
if (!netfix_disable) {
|
||||||
networkhook_init();
|
networkhook_init();
|
||||||
|
|||||||
+645
-814
File diff suppressed because it is too large
Load Diff
@@ -316,6 +316,7 @@ namespace launcher {
|
|||||||
OtocaCamHook,
|
OtocaCamHook,
|
||||||
DisableHighResTimer,
|
DisableHighResTimer,
|
||||||
EnableICMPHook,
|
EnableICMPHook,
|
||||||
|
EnableNICSpoof,
|
||||||
AutoElevate,
|
AutoElevate,
|
||||||
CfgForceSoftwareRender,
|
CfgForceSoftwareRender,
|
||||||
OBSWebSocketEnabled,
|
OBSWebSocketEnabled,
|
||||||
@@ -325,6 +326,8 @@ namespace launcher {
|
|||||||
OBSWebSocketDebug,
|
OBSWebSocketDebug,
|
||||||
ScreenshotIncludeOverlay,
|
ScreenshotIncludeOverlay,
|
||||||
ScreenshotSubscreens,
|
ScreenshotSubscreens,
|
||||||
|
|
||||||
|
_Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class OptionsCategory {
|
enum class OptionsCategory {
|
||||||
|
|||||||
@@ -331,8 +331,8 @@ int eamuse_coin_consume_stock() {
|
|||||||
return COIN_STOCK.exchange(0, std::memory_order_relaxed);
|
return COIN_STOCK.exchange(0, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
int eamuse_coin_add() {
|
void eamuse_coin_add(int amount) {
|
||||||
return COIN_STOCK.fetch_add(1, std::memory_order_relaxed) + 1;
|
COIN_STOCK.fetch_add(amount, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void eamuse_coin_insert() {
|
void eamuse_coin_insert() {
|
||||||
@@ -621,6 +621,7 @@ int eamuse_get_game_keypads() {
|
|||||||
avs::game::is_model("JDX") ||
|
avs::game::is_model("JDX") ||
|
||||||
avs::game::is_model("KDX") ||
|
avs::game::is_model("KDX") ||
|
||||||
avs::game::is_model("MDX") ||
|
avs::game::is_model("MDX") ||
|
||||||
|
avs::game::is_model("KDM") ||
|
||||||
avs::game::is_model("J33") ||
|
avs::game::is_model("J33") ||
|
||||||
avs::game::is_model("K33") ||
|
avs::game::is_model("K33") ||
|
||||||
avs::game::is_model("L33") ||
|
avs::game::is_model("L33") ||
|
||||||
@@ -637,6 +638,7 @@ int eamuse_get_game_keypads_name() {
|
|||||||
|
|
||||||
if (game_name == "Beatmania IIDX" ||
|
if (game_name == "Beatmania IIDX" ||
|
||||||
game_name == "Dance Dance Revolution" ||
|
game_name == "Dance Dance Revolution" ||
|
||||||
|
game_name == "Dance Evolution" ||
|
||||||
game_name == "GitaDora")
|
game_name == "GitaDora")
|
||||||
{
|
{
|
||||||
return 2;
|
return 2;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ void eamuse_coin_set_stock(int amount);
|
|||||||
bool eamuse_coin_consume(int amount);
|
bool eamuse_coin_consume(int amount);
|
||||||
int eamuse_coin_consume_stock();
|
int eamuse_coin_consume_stock();
|
||||||
|
|
||||||
int eamuse_coin_add();
|
void eamuse_coin_add(int amount = 1);
|
||||||
void eamuse_coin_insert();
|
void eamuse_coin_insert();
|
||||||
|
|
||||||
void eamuse_pin_macro_start_thread();
|
void eamuse_pin_macro_start_thread();
|
||||||
|
|||||||
@@ -237,6 +237,16 @@ typedef SPICE_SDK_STATUS_CODE (__cdecl spice_sdk_add_toast_func)(
|
|||||||
const char *text
|
const char *text
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// insert_coin (v0.3 and up)
|
||||||
|
// adds to the shared coin stock, bypassing the coin blocker like the Spice API
|
||||||
|
//
|
||||||
|
// amount: number of coins to insert (0 to 255); use 1 for a single coin
|
||||||
|
// zero succeeds without changing the stock
|
||||||
|
|
||||||
|
typedef SPICE_SDK_STATUS_CODE (__cdecl spice_sdk_insert_coin_func)(
|
||||||
|
uint8_t amount
|
||||||
|
);
|
||||||
|
|
||||||
typedef struct SPICE_SDK_V0 {
|
typedef struct SPICE_SDK_V0 {
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
|
||||||
@@ -262,6 +272,8 @@ typedef struct SPICE_SDK_V0 {
|
|||||||
|
|
||||||
spice_sdk_add_toast_func *add_toast;
|
spice_sdk_add_toast_func *add_toast;
|
||||||
|
|
||||||
|
spice_sdk_insert_coin_func *insert_coin;
|
||||||
|
|
||||||
} SPICE_SDK_V0;
|
} SPICE_SDK_V0;
|
||||||
|
|
||||||
typedef void (__cdecl spice_sdk_destroy_callback_func)(
|
typedef void (__cdecl spice_sdk_destroy_callback_func)(
|
||||||
|
|||||||
@@ -76,9 +76,35 @@ static ArrowButton arrow_buttons[] = {
|
|||||||
|
|
||||||
// worker thread for I/O
|
// worker thread for I/O
|
||||||
static void worker_thread_main(std::stop_token stop_token) {
|
static void worker_thread_main(std::stop_token stop_token) {
|
||||||
|
bool coin_previous_state[10] = {};
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
|
|
||||||
|
// insert coin
|
||||||
|
const bool control_pressed = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
|
||||||
|
for (uint8_t amount = 0; amount < 10; amount++) {
|
||||||
|
const bool coin_pressed = control_pressed &&
|
||||||
|
((GetAsyncKeyState('0' + amount) & 0x8000) != 0);
|
||||||
|
|
||||||
|
if (coin_pressed && !coin_previous_state[amount] && spice.insert_coin) {
|
||||||
|
const auto status = spice.insert_coin(amount);
|
||||||
|
if (status != SPICE_SDK_STATUS_SUCCESS) {
|
||||||
|
LOG_INFO(std::format(
|
||||||
|
"coin insertion failed: {}",
|
||||||
|
static_cast<int>(status)).c_str());
|
||||||
|
} else {
|
||||||
|
const auto message = std::format(
|
||||||
|
"v0_cpp: inserted {} coin{}", amount, amount == 1 ? "" : "s");
|
||||||
|
LOG_INFO(message.c_str());
|
||||||
|
if (spice.add_toast) {
|
||||||
|
spice.add_toast(SPICE_SDK_TOAST_LEVEL_SUCCESS, message.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coin_previous_state[amount] = coin_pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for ctrl + arrow keys and trigger p1 pad arrows
|
||||||
for (auto& arrow : arrow_buttons) {
|
for (auto& arrow : arrow_buttons) {
|
||||||
// check for ctrl + arrow keys and trigger p1 pad arrows
|
|
||||||
if (((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
|
if (((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
|
||||||
((GetAsyncKeyState(arrow.key) & 0x8000) != 0)) {
|
((GetAsyncKeyState(arrow.key) & 0x8000) != 0)) {
|
||||||
spice.set_button(arrow.button, true, 1.f);
|
spice.set_button(arrow.button, true, 1.f);
|
||||||
@@ -88,7 +114,7 @@ static void worker_thread_main(std::stop_token stop_token) {
|
|||||||
if (spice.add_toast) {
|
if (spice.add_toast) {
|
||||||
spice.add_toast(
|
spice.add_toast(
|
||||||
SPICE_SDK_TOAST_LEVEL_INFO,
|
SPICE_SDK_TOAST_LEVEL_INFO,
|
||||||
std::format("let me hear you say: {}", arrow.name).c_str());
|
std::format("v0_cpp: let me hear you say: {}", arrow.name).c_str());
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO(std::format("let me hear you say: {}", arrow.name).c_str());
|
LOG_INFO(std::format("let me hear you say: {}", arrow.name).c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-1
@@ -31,6 +31,7 @@ static spice_sdk_clear_touch_func sdk_clear_touch;
|
|||||||
static spice_sdk_insert_card_func sdk_insert_card;
|
static spice_sdk_insert_card_func sdk_insert_card;
|
||||||
static spice_sdk_set_keypad_func sdk_set_keypad;
|
static spice_sdk_set_keypad_func sdk_set_keypad;
|
||||||
static spice_sdk_add_toast_func sdk_add_toast;
|
static spice_sdk_add_toast_func sdk_add_toast;
|
||||||
|
static spice_sdk_insert_coin_func sdk_insert_coin;
|
||||||
|
|
||||||
struct SdkModule {
|
struct SdkModule {
|
||||||
std::string dll;
|
std::string dll;
|
||||||
@@ -175,8 +176,13 @@ sdk_init(
|
|||||||
if (v0->size >= RTL_SIZEOF_THROUGH_FIELD(SPICE_SDK_V0, add_toast)) {
|
if (v0->size >= RTL_SIZEOF_THROUGH_FIELD(SPICE_SDK_V0, add_toast)) {
|
||||||
v0->add_toast = sdk_add_toast;
|
v0->add_toast = sdk_add_toast;
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of 0.2
|
// end of 0.2
|
||||||
|
|
||||||
|
if (v0->size >= RTL_SIZEOF_THROUGH_FIELD(SPICE_SDK_V0, insert_coin)) {
|
||||||
|
v0->insert_coin = sdk_insert_coin;
|
||||||
|
}
|
||||||
|
// end of 0.3
|
||||||
|
|
||||||
// any newer minor iterations will need to check the size
|
// any newer minor iterations will need to check the size
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -219,6 +225,7 @@ sdk_log(
|
|||||||
log_warning(facility_str.c_str(), "{}", message);
|
log_warning(facility_str.c_str(), "{}", message);
|
||||||
break;
|
break;
|
||||||
case SPICE_SDK_LOG_LEVEL_FATAL:
|
case SPICE_SDK_LOG_LEVEL_FATAL:
|
||||||
|
lock.unlock();
|
||||||
log_fatal(facility_str.c_str(), "{}", message);
|
log_fatal(facility_str.c_str(), "{}", message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -640,5 +647,21 @@ sdk_add_toast(
|
|||||||
return SPICE_SDK_STATUS_SUCCESS;
|
return SPICE_SDK_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPICE_SDK_STATUS_CODE
|
||||||
|
__cdecl
|
||||||
|
sdk_insert_coin(
|
||||||
|
uint8_t amount
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::shared_lock lock(sdk_global_mutex);
|
||||||
|
if (!sdk_initialized) {
|
||||||
|
return SPICE_SDK_STATUS_TOO_LATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount > 0) {
|
||||||
|
eamuse_coin_add(amount);
|
||||||
|
}
|
||||||
|
return SPICE_SDK_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sdk
|
} // namespace sdk
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
#include <winternl.h>
|
#include <winternl.h>
|
||||||
|
|
||||||
#define LDR_DLL_NOTIFICATION_REASON_LOADED 1
|
#define LDR_DLL_NOTIFICATION_REASON_LOADED 1
|
||||||
@@ -43,4 +44,4 @@ NTSTATUS NTAPI LdrRegisterDllNotification(
|
|||||||
|
|
||||||
NTSTATUS NTAPI LdrUnregisterDllNotification(
|
NTSTATUS NTAPI LdrUnregisterDllNotification(
|
||||||
PVOID Cookie
|
PVOID Cookie
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user