mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-10-02 16:28: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:
|
||||
name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }}
|
||||
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.
|
||||
* Don't throw exceptions.
|
||||
* 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.
|
||||
|
||||
@@ -616,6 +616,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
hooks/networkhook.cpp
|
||||
hooks/icmphook_net.cpp
|
||||
hooks/icmphook_iphlpapi.cpp
|
||||
hooks/nicspoof.cpp
|
||||
hooks/powrprof.cpp
|
||||
#hooks/rom.cpp
|
||||
hooks/setupapihook.cpp
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "client.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "external/rapidjson/error/en.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
@@ -92,6 +93,7 @@ Controller::Controller(unsigned short port, std::string password, bool pretty)
|
||||
// bind socket to address
|
||||
if (bind(this->server, (sockaddr *) &server_address, sizeof(sockaddr)) == -1) {
|
||||
log_warning("api", "could not bind socket on port {}: {}", port, get_last_error_string());
|
||||
closesocket(this->server);
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
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
|
||||
if (listen(this->server, server_backlog) == -1) {
|
||||
log_warning("api", "could not listen to socket on port {}: {}", port, get_last_error_string());
|
||||
closesocket(this->server);
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
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
|
||||
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
|
||||
out->push_back(0);
|
||||
@@ -443,7 +448,9 @@ void Controller::free_socket() {
|
||||
this->server = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
this->websocket->free_socket();
|
||||
if (this->websocket) {
|
||||
this->websocket->free_socket();
|
||||
}
|
||||
|
||||
for (auto &s : this->serial) {
|
||||
s->free_port();
|
||||
|
||||
@@ -41,21 +41,21 @@ namespace api {
|
||||
bool pretty;
|
||||
|
||||
// server
|
||||
WebSocketController *websocket;
|
||||
WebSocketController *websocket = nullptr;
|
||||
std::vector<SerialController *> serial;
|
||||
std::vector<std::thread> server_workers;
|
||||
std::vector<std::thread> server_handlers;
|
||||
std::mutex server_handlers_m;
|
||||
std::vector<api::ClientState *> client_states;
|
||||
std::mutex client_states_m;
|
||||
SOCKET server;
|
||||
SOCKET server = INVALID_SOCKET;
|
||||
void server_worker();
|
||||
void connection_handler(ClientState client_state);
|
||||
|
||||
public:
|
||||
|
||||
// state
|
||||
bool server_running;
|
||||
bool server_running = false;
|
||||
|
||||
// constructor / destructor
|
||||
Controller(unsigned short port, std::string password, bool pretty);
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace api::modules {
|
||||
return error_type(res, "amount", "int");
|
||||
|
||||
// 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 {
|
||||
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
|
||||
SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B,
|
||||
SCARD_ATR_PROTOCOL_ISO15693_PART4 = 0x0C,
|
||||
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
|
||||
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];
|
||||
BOOL shouldReverseUid = 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");
|
||||
shouldReverseUid = true;
|
||||
} else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3) {
|
||||
log_info("scard", "card protocol: ISO14443_PART3");
|
||||
} else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART4) {
|
||||
log_info("scard", "card protocol: ISO15693_PART4");
|
||||
shouldReverseUid = true;
|
||||
} else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) {
|
||||
log_info("scard", "card protocol: FELICA_212K");
|
||||
is_felica = true;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#if SPICE64 && !SPICE_XP
|
||||
|
||||
#include <array>
|
||||
#include <d3d9.h>
|
||||
#include "mf_wrappers.h"
|
||||
#include "avs/game.h"
|
||||
@@ -46,9 +47,7 @@ struct PredefinedHook {
|
||||
uintptr_t hook_afp_texture_offset;
|
||||
};
|
||||
|
||||
PredefinedHook g_predefinedHooks[] = {};
|
||||
|
||||
const DWORD g_predefinedHooksLength = ARRAYSIZE(g_predefinedHooks);
|
||||
std::array<PredefinedHook, 0> g_predefinedHooks = {};
|
||||
|
||||
namespace games::iidx {
|
||||
|
||||
@@ -110,14 +109,14 @@ namespace games::iidx {
|
||||
auto pe = fmt::format("{:x}_{:x}", time_date_stamp, address_of_entry_point);
|
||||
log_info("iidx:camhook", "Locating predefined hook addresses for LDJ-{}", pe);
|
||||
|
||||
for (DWORD i = 0; i < g_predefinedHooksLength; i++) {
|
||||
if (pe.compare(g_predefinedHooks[i].pe_identifier) == 0) {
|
||||
for (auto &hook : g_predefinedHooks) {
|
||||
if (pe.compare(hook.pe_identifier) == 0) {
|
||||
log_misc("iidx:camhook", "Found predefined addresses");
|
||||
addr_hook_a = g_predefinedHooks[i].hook_a;
|
||||
addr_textures = g_predefinedHooks[i].hook_textures;
|
||||
addr_camera_manager = g_predefinedHooks[i].hook_camera_manager;
|
||||
addr_device_offset = g_predefinedHooks[i].hook_device_offset;
|
||||
addr_afp_texture_offset = g_predefinedHooks[i].hook_afp_texture_offset;
|
||||
addr_hook_a = hook.hook_a;
|
||||
addr_textures = hook.hook_textures;
|
||||
addr_camera_manager = hook.hook_camera_manager;
|
||||
addr_device_offset = hook.hook_device_offset;
|
||||
addr_afp_texture_offset = hook.hook_afp_texture_offset;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ DEFINE_GUID(IID_IAudioClient,
|
||||
0x1cb9ad4c, 0xdbfa, 0x4c32,
|
||||
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,
|
||||
0xcd63314f, 0x3fba, 0x4a1b,
|
||||
0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7);
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
#include "d3d9_device.h"
|
||||
|
||||
// windows.h defines many stupid things
|
||||
#undef small
|
||||
|
||||
bool gfdm_two_head_exclusive() {
|
||||
return games::gitadora::is_arena_model()
|
||||
&& 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/networkhook.h"
|
||||
#include "hooks/icmphook_net.h"
|
||||
#include "hooks/nicspoof.h"
|
||||
#include "hooks/unisintrhook.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "launcher/logger.h"
|
||||
@@ -257,6 +258,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
bool load_stubs = false;
|
||||
bool netfix_disable = false;
|
||||
bool icmphook_enable = false;
|
||||
bool nicspoof_enable = false;
|
||||
bool lang_disable = false;
|
||||
std::string process_priority_str = "high";
|
||||
bool cardio_enabled = false;
|
||||
@@ -799,6 +801,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::EnableICMPHook].value_bool()) {
|
||||
icmphook_enable = true;
|
||||
}
|
||||
if (options[launcher::Options::EnableNICSpoof].value_bool()) {
|
||||
nicspoof_enable = true;
|
||||
}
|
||||
if (options[launcher::Options::DisableACPHook].value_bool()) {
|
||||
lang_disable = true;
|
||||
}
|
||||
@@ -2558,6 +2563,11 @@ int main_implementation(int argc, char *argv[]) {
|
||||
icmphook_net_init();
|
||||
}
|
||||
|
||||
// NIC spoof (opt-in; fake Ethernet with no real NIC)
|
||||
if (nicspoof_enable) {
|
||||
nicspoof_init();
|
||||
}
|
||||
|
||||
// net fix
|
||||
if (!netfix_disable) {
|
||||
networkhook_init();
|
||||
|
||||
+645
-814
File diff suppressed because it is too large
Load Diff
@@ -316,6 +316,7 @@ namespace launcher {
|
||||
OtocaCamHook,
|
||||
DisableHighResTimer,
|
||||
EnableICMPHook,
|
||||
EnableNICSpoof,
|
||||
AutoElevate,
|
||||
CfgForceSoftwareRender,
|
||||
OBSWebSocketEnabled,
|
||||
@@ -325,6 +326,8 @@ namespace launcher {
|
||||
OBSWebSocketDebug,
|
||||
ScreenshotIncludeOverlay,
|
||||
ScreenshotSubscreens,
|
||||
|
||||
_Count,
|
||||
};
|
||||
|
||||
enum class OptionsCategory {
|
||||
|
||||
@@ -331,8 +331,8 @@ int eamuse_coin_consume_stock() {
|
||||
return COIN_STOCK.exchange(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int eamuse_coin_add() {
|
||||
return COIN_STOCK.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
void eamuse_coin_add(int amount) {
|
||||
COIN_STOCK.fetch_add(amount, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void eamuse_coin_insert() {
|
||||
@@ -621,6 +621,7 @@ int eamuse_get_game_keypads() {
|
||||
avs::game::is_model("JDX") ||
|
||||
avs::game::is_model("KDX") ||
|
||||
avs::game::is_model("MDX") ||
|
||||
avs::game::is_model("KDM") ||
|
||||
avs::game::is_model("J33") ||
|
||||
avs::game::is_model("K33") ||
|
||||
avs::game::is_model("L33") ||
|
||||
@@ -637,6 +638,7 @@ int eamuse_get_game_keypads_name() {
|
||||
|
||||
if (game_name == "Beatmania IIDX" ||
|
||||
game_name == "Dance Dance Revolution" ||
|
||||
game_name == "Dance Evolution" ||
|
||||
game_name == "GitaDora")
|
||||
{
|
||||
return 2;
|
||||
|
||||
@@ -59,7 +59,7 @@ void eamuse_coin_set_stock(int amount);
|
||||
bool eamuse_coin_consume(int amount);
|
||||
int eamuse_coin_consume_stock();
|
||||
|
||||
int eamuse_coin_add();
|
||||
void eamuse_coin_add(int amount = 1);
|
||||
void eamuse_coin_insert();
|
||||
|
||||
void eamuse_pin_macro_start_thread();
|
||||
|
||||
@@ -237,6 +237,16 @@ typedef SPICE_SDK_STATUS_CODE (__cdecl spice_sdk_add_toast_func)(
|
||||
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 {
|
||||
uint32_t size;
|
||||
|
||||
@@ -262,6 +272,8 @@ typedef struct SPICE_SDK_V0 {
|
||||
|
||||
spice_sdk_add_toast_func *add_toast;
|
||||
|
||||
spice_sdk_insert_coin_func *insert_coin;
|
||||
|
||||
} SPICE_SDK_V0;
|
||||
|
||||
typedef void (__cdecl spice_sdk_destroy_callback_func)(
|
||||
|
||||
@@ -76,9 +76,35 @@ static ArrowButton arrow_buttons[] = {
|
||||
|
||||
// worker thread for I/O
|
||||
static void worker_thread_main(std::stop_token stop_token) {
|
||||
bool coin_previous_state[10] = {};
|
||||
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) {
|
||||
// check for ctrl + arrow keys and trigger p1 pad arrows
|
||||
if (((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
|
||||
((GetAsyncKeyState(arrow.key) & 0x8000) != 0)) {
|
||||
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) {
|
||||
spice.add_toast(
|
||||
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 {
|
||||
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_set_keypad_func sdk_set_keypad;
|
||||
static spice_sdk_add_toast_func sdk_add_toast;
|
||||
static spice_sdk_insert_coin_func sdk_insert_coin;
|
||||
|
||||
struct SdkModule {
|
||||
std::string dll;
|
||||
@@ -175,8 +176,13 @@ sdk_init(
|
||||
if (v0->size >= RTL_SIZEOF_THROUGH_FIELD(SPICE_SDK_V0, add_toast)) {
|
||||
v0->add_toast = sdk_add_toast;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
{
|
||||
@@ -219,6 +225,7 @@ sdk_log(
|
||||
log_warning(facility_str.c_str(), "{}", message);
|
||||
break;
|
||||
case SPICE_SDK_LOG_LEVEL_FATAL:
|
||||
lock.unlock();
|
||||
log_fatal(facility_str.c_str(), "{}", message);
|
||||
break;
|
||||
default:
|
||||
@@ -640,5 +647,21 @@ sdk_add_toast(
|
||||
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
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
#define LDR_DLL_NOTIFICATION_REASON_LOADED 1
|
||||
@@ -43,4 +44,4 @@ NTSTATUS NTAPI LdrRegisterDllNotification(
|
||||
|
||||
NTSTATUS NTAPI LdrUnregisterDllNotification(
|
||||
PVOID Cookie
|
||||
);
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user