Jessica James
4 years ago
commit
389794f82f
23 changed files with 1666 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||
|
.vs/ |
||||
|
Debug/ |
||||
|
Release/ |
@ -0,0 +1,28 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
# Visual Studio 15 |
||||
|
VisualStudioVersion = 15.0.26228.9 |
||||
|
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Rx_TCPLink", "Rx_TCPLink\Rx_TCPLink.vcxproj", "{988779E2-4B8D-4997-82B6-25BC4E0A705E}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|x64 = Debug|x64 |
||||
|
Debug|x86 = Debug|x86 |
||||
|
Release|x64 = Release|x64 |
||||
|
Release|x86 = Release|x86 |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Debug|x64.ActiveCfg = Debug|x64 |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Debug|x64.Build.0 = Debug|x64 |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Debug|x86.ActiveCfg = Debug|Win32 |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Debug|x86.Build.0 = Debug|Win32 |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Release|x64.ActiveCfg = Release|x64 |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Release|x64.Build.0 = Release|x64 |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Release|x86.ActiveCfg = Release|Win32 |
||||
|
{988779E2-4B8D-4997-82B6-25BC4E0A705E}.Release|x86.Build.0 = Release|Win32 |
||||
|
EndGlobalSection |
||||
|
GlobalSection(SolutionProperties) = preSolution |
||||
|
HideSolutionNode = FALSE |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
@ -0,0 +1,149 @@ |
|||||
|
#define _CRT_SECURE_NO_WARNINGS |
||||
|
|
||||
|
#include "DiscordRpc.h" |
||||
|
#include <cstdlib> |
||||
|
#include <cstring> |
||||
|
#include <chrono> |
||||
|
#include "discord_rpc.h" |
||||
|
|
||||
|
static constexpr int GDI_TEAM_NUM = 0; |
||||
|
static constexpr int NOD_TEAM_NUM = 1; |
||||
|
|
||||
|
int64_t now_seconds() { |
||||
|
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); |
||||
|
} |
||||
|
|
||||
|
extern "C" __declspec(dllexport) void UpdateDiscordRPC(wchar_t* in_server_name, wchar_t* in_level_name, int in_player_count, int in_max_players, int in_team_num, int in_time_elapsed, int in_time_remaining, int in_is_firestorm, wchar_t* in_image_name) { |
||||
|
// Convert our input strings
|
||||
|
char server_name[128]; |
||||
|
size_t server_name_length{}; |
||||
|
char level_name[128]; |
||||
|
size_t level_name_length{}; |
||||
|
char image_name[128]; |
||||
|
size_t image_name_length{}; |
||||
|
|
||||
|
server_name_length = std::wcstombs(server_name, in_server_name, sizeof(server_name)); |
||||
|
level_name_length = std::wcstombs(level_name, in_level_name, sizeof(level_name)); |
||||
|
image_name_length = std::wcstombs(image_name, in_image_name, sizeof(image_name)); |
||||
|
|
||||
|
// Populate our presence
|
||||
|
DiscordRichPresence presence{}; |
||||
|
|
||||
|
// state
|
||||
|
if (*level_name != '\0') { |
||||
|
// We're in a match
|
||||
|
presence.state = level_name; |
||||
|
} |
||||
|
else { |
||||
|
// We're not in a level; we must be at the main menu
|
||||
|
presence.state = "Main Menu"; |
||||
|
} |
||||
|
|
||||
|
// details, party info
|
||||
|
if (*server_name != '\0') { |
||||
|
// We're in a multiplayer server
|
||||
|
presence.details = server_name; |
||||
|
|
||||
|
presence.partySize = in_player_count; |
||||
|
presence.partyMax = in_max_players; |
||||
|
} |
||||
|
else { |
||||
|
// We're not in any server
|
||||
|
if (strcmp(level_name, "Black Dawn") == 0) { |
||||
|
// We're playing campaign
|
||||
|
presence.details = "Campaign"; |
||||
|
} |
||||
|
else if (*level_name != '\0') { |
||||
|
// We're playing skirmish
|
||||
|
presence.details = "Singleplayer Skirmish"; |
||||
|
} |
||||
|
// else // we're sitting at the main menu
|
||||
|
} |
||||
|
|
||||
|
if (in_is_firestorm == 0) { |
||||
|
presence.largeImageKey = "renegadex"; |
||||
|
presence.largeImageText = "Renegade X"; |
||||
|
|
||||
|
if (in_team_num == GDI_TEAM_NUM) { |
||||
|
presence.smallImageKey = "gdi"; |
||||
|
presence.smallImageText = "GDI"; |
||||
|
} |
||||
|
else if (in_team_num == NOD_TEAM_NUM) { |
||||
|
presence.smallImageKey = "nod"; |
||||
|
presence.smallImageText = "Nod"; |
||||
|
} |
||||
|
} |
||||
|
else { |
||||
|
presence.largeImageKey = "fs"; |
||||
|
presence.largeImageText = "Renegade X: Firestorm"; |
||||
|
|
||||
|
// For map-specfic images
|
||||
|
if (*image_name != '\0') { |
||||
|
presence.largeImageKey = image_name; |
||||
|
} |
||||
|
|
||||
|
if (in_team_num == GDI_TEAM_NUM) { |
||||
|
presence.smallImageKey = "tsgdi"; |
||||
|
presence.smallImageText = "GDI"; |
||||
|
} |
||||
|
else if (in_team_num == NOD_TEAM_NUM) { |
||||
|
presence.smallImageKey = "tsnod"; |
||||
|
presence.smallImageText = "Nod"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
// Discord client logic:
|
||||
|
// If endTimestamp is present: display time remaining
|
||||
|
|
||||
|
// If the match is over (negative in_time_remaining); don't display anything
|
||||
|
if (presence.endTimestamp >= 0) { |
||||
|
presence.startTimestamp = now_seconds() - in_time_elapsed; |
||||
|
|
||||
|
// If it's a timed match, display the time remaining
|
||||
|
if (in_time_remaining != 0) { |
||||
|
presence.endTimestamp = now_seconds() + in_time_remaining; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Update our presence
|
||||
|
Discord_UpdatePresence(&presence); |
||||
|
} |
||||
|
|
||||
|
//typedef struct DiscordRichPresence {
|
||||
|
// const char* state; /* max 128 bytes */
|
||||
|
// const char* details; /* max 128 bytes */
|
||||
|
// int64_t startTimestamp;
|
||||
|
// int64_t endTimestamp;
|
||||
|
// const char* largeImageKey; /* max 32 bytes */
|
||||
|
// const char* largeImageText; /* max 128 bytes */
|
||||
|
// const char* smallImageKey; /* max 32 bytes */
|
||||
|
// const char* smallImageText; /* max 128 bytes */
|
||||
|
// const char* partyId; /* max 128 bytes */
|
||||
|
// int partySize;
|
||||
|
// int partyMax;
|
||||
|
// const char* matchSecret; /* max 128 bytes */
|
||||
|
// const char* joinSecret; /* max 128 bytes */
|
||||
|
// const char* spectateSecret; /* max 128 bytes */
|
||||
|
// int8_t instance;
|
||||
|
//} DiscordRichPresence;
|
||||
|
|
||||
|
/*char buffer[256];
|
||||
|
DiscordRichPresence discordPresence; |
||||
|
memset(&discordPresence, 0, sizeof(discordPresence)); |
||||
|
discordPresence.state = "West of House"; |
||||
|
sprintf(buffer, "Frustration level: %d", FrustrationLevel); |
||||
|
discordPresence.details = buffer; |
||||
|
discordPresence.startTimestamp = StartTime; |
||||
|
discordPresence.endTimestamp = time(0) + 5 * 60; |
||||
|
discordPresence.largeImageKey = "canary-large"; |
||||
|
discordPresence.smallImageKey = "ptb-small"; |
||||
|
discordPresence.partyId = "party1234"; |
||||
|
discordPresence.partySize = 1; |
||||
|
discordPresence.partyMax = 6; |
||||
|
discordPresence.matchSecret = "xyzzy"; |
||||
|
discordPresence.joinSecret = "join"; |
||||
|
discordPresence.spectateSecret = "look"; |
||||
|
discordPresence.instance = 0; |
||||
|
Discord_UpdatePresence(&discordPresence);*/ |
@ -0,0 +1,29 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2018 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif // __cplusplus
|
||||
|
|
||||
|
__declspec(dllexport) void UpdateDiscordRPC(wchar_t* in_server_name, wchar_t* in_level_name, int in_player_count, int in_max_players, int in_team_num, int in_time_elapsed, int in_time_remaining, int is_in_firestorm, wchar_t* in_image_name); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif // __cplusplus
|
@ -0,0 +1,148 @@ |
|||||
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS |
||||
|
#include <WinSock2.h> |
||||
|
#include <Windows.h> |
||||
|
#include <Iphlpapi.h> |
||||
|
#include <string> |
||||
|
#include <iomanip> |
||||
|
#include <sstream> |
||||
|
#include "UDKArray.h" |
||||
|
|
||||
|
static std::string s_hwid; |
||||
|
|
||||
|
struct HWID { |
||||
|
union { |
||||
|
uint64_t hwid; |
||||
|
struct { |
||||
|
uint32_t left; |
||||
|
uint32_t right; |
||||
|
}; |
||||
|
}; |
||||
|
} hwid; |
||||
|
|
||||
|
#pragma pack(push, 4) |
||||
|
struct ByteArrayWrapper { |
||||
|
UDKArray<uint8_t> data; |
||||
|
}; |
||||
|
#pragma pack(pop) |
||||
|
|
||||
|
std::string GetSystemDriveGUID() { |
||||
|
char* system_drive; |
||||
|
char system_drive_mount_point[64]; |
||||
|
char system_drive_volume_name[64]; |
||||
|
char* system_drive_guid_start; |
||||
|
char* system_drive_guid_end; |
||||
|
|
||||
|
system_drive = getenv("SystemDrive"); |
||||
|
if (system_drive != NULL) { |
||||
|
if (GetVolumePathNameA(system_drive, system_drive_mount_point, sizeof(system_drive_mount_point))) { |
||||
|
if (GetVolumeNameForVolumeMountPointA(system_drive_mount_point, system_drive_volume_name, sizeof(system_drive_volume_name))) { |
||||
|
// Find start of guid
|
||||
|
system_drive_guid_start = system_drive_volume_name; |
||||
|
while (*system_drive_guid_start != '\0') { |
||||
|
if (*system_drive_guid_start == '{') { |
||||
|
// Found start of guid; interate over '{' and break
|
||||
|
++system_drive_guid_start; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
++system_drive_guid_start; |
||||
|
} |
||||
|
|
||||
|
// Find end of guid
|
||||
|
system_drive_guid_end = system_drive_guid_start; |
||||
|
while (*system_drive_guid_end != '\0') { |
||||
|
if (*system_drive_guid_end == '}') { |
||||
|
// Found end of guid; break
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
++system_drive_guid_end; |
||||
|
} |
||||
|
|
||||
|
// Copy guid to out_guid
|
||||
|
return std::string{ system_drive_guid_start, system_drive_guid_end }; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return {}; |
||||
|
} |
||||
|
|
||||
|
void fill_mac_hwid() { |
||||
|
IP_ADAPTER_ADDRESSES addresses[32]; |
||||
|
PIP_ADAPTER_ADDRESSES node; |
||||
|
ULONG addresses_size = sizeof(addresses); |
||||
|
|
||||
|
if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, addresses, &addresses_size) == ERROR_SUCCESS) { |
||||
|
for (node = addresses; node != NULL; node = node->Next) { |
||||
|
if (node->PhysicalAddressLength > 0) { |
||||
|
static_assert(MAX_ADAPTER_ADDRESS_LENGTH <= sizeof(struct HWID), "HWID struct is not large enough to fit adapter physical address"); |
||||
|
memcpy(&hwid, node->PhysicalAddress, node->PhysicalAddressLength); |
||||
|
|
||||
|
if (hwid.hwid != 0) { |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
hwid.hwid = 0; |
||||
|
} |
||||
|
|
||||
|
/** Integer to string (hexadecimal) conversion tables */ |
||||
|
|
||||
|
const char hexadecimal_rep_table_upper[][3] = |
||||
|
{ |
||||
|
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", |
||||
|
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", |
||||
|
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", |
||||
|
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", |
||||
|
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", |
||||
|
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F", |
||||
|
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F", |
||||
|
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", |
||||
|
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F", |
||||
|
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", |
||||
|
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", |
||||
|
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", |
||||
|
"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", |
||||
|
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", |
||||
|
"E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", |
||||
|
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" |
||||
|
}; |
||||
|
|
||||
|
template<typename T> |
||||
|
std::string to_hex(T in_integer) { |
||||
|
std::string result; |
||||
|
uint8_t* begin = reinterpret_cast<uint8_t*>(&in_integer); |
||||
|
uint8_t* itr = begin + sizeof(T); |
||||
|
|
||||
|
result.reserve(sizeof(in_integer) * 2); |
||||
|
while (itr != begin) { |
||||
|
--itr; |
||||
|
result += hexadecimal_rep_table_upper[*itr]; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
extern "C" __declspec(dllexport) void c_token(ByteArrayWrapper* out_hwid) { |
||||
|
std::string hwid_str; |
||||
|
|
||||
|
// Try mac address
|
||||
|
fill_mac_hwid(); |
||||
|
if (hwid.hwid != 0) { |
||||
|
hwid_str = 'm'; |
||||
|
hwid_str += to_hex(hwid.left) + to_hex(hwid.right); // keep this format so it matches old
|
||||
|
} |
||||
|
else { |
||||
|
// Fallback to System Drive GUID
|
||||
|
hwid_str = 'd'; |
||||
|
hwid_str += GetSystemDriveGUID(); |
||||
|
} |
||||
|
|
||||
|
// Return hwid_str through out_hwid
|
||||
|
out_hwid->data.reserve(hwid_str.size()); |
||||
|
out_hwid->data.ArrayNum = hwid_str.size(); |
||||
|
memcpy(out_hwid->data.Data, hwid_str.data(), hwid_str.size()); |
||||
|
} |
@ -0,0 +1,90 @@ |
|||||
|
#include <iostream> |
||||
|
#include <vector> |
||||
|
#include <algorithm> |
||||
|
#include <Windows.h> |
||||
|
#include <psapi.h> |
||||
|
#include "discord_rpc.h" |
||||
|
#include "discord_register.h" |
||||
|
#include "Rx_TCPLink.h" |
||||
|
#include "DiscordRpc.h" |
||||
|
#include "Viewport.h" |
||||
|
|
||||
|
extern "C" ReallocFunctionPtrType g_realloc_function = nullptr; |
||||
|
|
||||
|
void* searchMemory(const std::vector<byte>& bytes) |
||||
|
{ |
||||
|
MODULEINFO mi; |
||||
|
|
||||
|
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(nullptr), &mi, sizeof mi); |
||||
|
|
||||
|
byte* moduleHead = static_cast<byte*>(mi.lpBaseOfDll); |
||||
|
byte* moduleTail = moduleHead + mi.SizeOfImage; |
||||
|
|
||||
|
byte* region = moduleHead; |
||||
|
|
||||
|
while (region < moduleTail) |
||||
|
{ |
||||
|
MEMORY_BASIC_INFORMATION mbi; |
||||
|
|
||||
|
if (!VirtualQuery(region, &mbi, sizeof mbi)) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
byte* regionTail = region + mbi.RegionSize; |
||||
|
|
||||
|
constexpr DWORD protectionFlags = PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; |
||||
|
|
||||
|
if (mbi.State & MEM_COMMIT && !(mbi.Protect & PAGE_GUARD) && mbi.AllocationProtect & protectionFlags) |
||||
|
{ |
||||
|
void* result = std::search(region, regionTail, bytes.begin(), bytes.end()); |
||||
|
|
||||
|
if (result != regionTail) |
||||
|
{ |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
region = regionTail; |
||||
|
} |
||||
|
|
||||
|
return nullptr; |
||||
|
} |
||||
|
|
||||
|
#pragma pack(push, 4) |
||||
|
struct FDLLBindInitData |
||||
|
{ |
||||
|
INT Version; |
||||
|
ReallocFunctionPtrType ReallocFunctionPtr; |
||||
|
}; |
||||
|
#pragma pack(pop) |
||||
|
|
||||
|
void initViewport() |
||||
|
{ |
||||
|
#if _WIN64 |
||||
|
const auto address = searchMemory({ |
||||
|
0x48, 0x89, 0x5C, 0x24, 0x08, 0x48, 0x89, 0x6C, 0x24, 0x10, 0x48, 0x89, |
||||
|
0x74, 0x24, 0x18, 0x57, 0x48, 0x83, 0xEC, 0x30, 0x33, 0xC0, 0x49, 0x8B, |
||||
|
0xE8, 0x48, 0x8B, 0xFA, 0x48, 0x8B, 0xF1, 0x89, 0x42, 0x08, 0x39, 0x42, |
||||
|
0x0C, 0x74, 0x19, 0x48, 0x8B, 0x0A, 0x89, 0x42, 0x0C, 0x48, 0x85, 0xC9, |
||||
|
0x74, 0x0E, 0x44, 0x8D, 0x40, 0x08, 0x33, 0xD2, 0xE8, 0xA3, 0x5D, 0xC0 }); |
||||
|
#else |
||||
|
const auto address = searchMemory({ |
||||
|
0x53, 0x56, 0x8B, 0x74, 0x24, 0x0C, 0x83, 0x7E, 0x08, 0x00, 0x57, 0x8B, |
||||
|
0xD9, 0xC7, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x74, 0x1C, 0x8B, 0x06, |
||||
|
0xC7, 0x46, 0x08, 0x00, 0x00, 0x00, 0x00, 0x85, 0xC0, 0x74, 0x0F, 0x6A, |
||||
|
0x08, 0x6A, 0x00, 0x50, 0xE8, 0x53, 0x75, 0xC6, 0xFF, 0x83, 0xC4, 0x0C, |
||||
|
0x89, 0x06, 0x8B, 0x03, 0x8B, 0x50, 0x0C, 0x8B, 0xCB, 0xFF, 0xD2, 0x8B }); |
||||
|
#endif |
||||
|
|
||||
|
Viewport::setReadPixelsFunction(reinterpret_cast<Viewport::ReadPixelsFunctionType>(address)); |
||||
|
} |
||||
|
|
||||
|
extern "C" __declspec(dllexport) void DLLBindInit(FDLLBindInitData* in_init_data) { |
||||
|
g_realloc_function = in_init_data->ReallocFunctionPtr; |
||||
|
|
||||
|
init_wsa(); |
||||
|
Discord_Initialize("482746941256499220", nullptr, 1, nullptr); |
||||
|
UpdateDiscordRPC(L"", L"", 0, 0, 0, 0, 0, 0, L""); |
||||
|
initViewport(); |
||||
|
} |
@ -0,0 +1,254 @@ |
|||||
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS |
||||
|
#define PIO_APC_ROUTINE_DEFINED |
||||
|
#include <WinSock2.h> |
||||
|
#include <IPExport.h> |
||||
|
#include <winternl.h> |
||||
|
#include <IcmpAPI.h> |
||||
|
#include <WS2tcpip.h> |
||||
|
#include<unordered_map> |
||||
|
#include <string> |
||||
|
#include <atomic> |
||||
|
#include <mutex> |
||||
|
#include <forward_list> |
||||
|
|
||||
|
// Forward declarations and aliases
|
||||
|
struct PingContext; |
||||
|
using request_map = std::unordered_map<std::wstring, std::shared_ptr<PingContext>>; |
||||
|
|
||||
|
// Constants
|
||||
|
constexpr int PING_IN_PROGRESS = -1; |
||||
|
constexpr int PING_FAILED = -2; |
||||
|
|
||||
|
unsigned char icmp_request_data[]{ "Renegade X Game Client Ping Data" }; |
||||
|
constexpr size_t icmp_request_data_length = sizeof(icmp_request_data) - 1; |
||||
|
constexpr DWORD icmp_reply_buffer_size = sizeof(ICMP_ECHO_REPLY) + icmp_request_data_length + 8 + sizeof(IO_STATUS_BLOCK); |
||||
|
constexpr DWORD icmp_timeout = 1000; // 1 second
|
||||
|
|
||||
|
// Statics
|
||||
|
std::atomic<unsigned int> s_pings_in_progress{ 0 }; // Note, this is ALL ping requests in progress, not necessarily the ones in `s_pings` (and therefore, not necessarily contexts that still exist)
|
||||
|
std::atomic<bool> s_ping_thread_active{ false }; |
||||
|
std::shared_ptr<request_map> s_pings; |
||||
|
std::forward_list<std::weak_ptr<PingContext>> s_pending_requests; |
||||
|
std::thread s_ping_thread; |
||||
|
//std::mutex s_pings_mutex;
|
||||
|
std::mutex s_pending_requests_mutex; |
||||
|
|
||||
|
struct PingContext { |
||||
|
HANDLE icmp_handle{}; |
||||
|
IPAddr destination{}; |
||||
|
std::weak_ptr<request_map> request_group; |
||||
|
std::unique_ptr<unsigned char[]> reply_buffer; |
||||
|
std::atomic<int> ping_time_milliseconds{ PING_IN_PROGRESS }; |
||||
|
|
||||
|
// Performs any single-run cleanup needed
|
||||
|
void finish() { |
||||
|
// Cleanup handle
|
||||
|
IcmpCloseHandle(icmp_handle); |
||||
|
icmp_handle = nullptr; |
||||
|
|
||||
|
// Mark no longer in progress
|
||||
|
--s_pings_in_progress; |
||||
|
} |
||||
|
|
||||
|
PingContext() { |
||||
|
++s_pings_in_progress; |
||||
|
} |
||||
|
|
||||
|
~PingContext() { |
||||
|
if (icmp_handle != nullptr) { |
||||
|
IcmpCloseHandle(icmp_handle); |
||||
|
--s_pings_in_progress; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
IPAddr parse_address(const wchar_t* in_str) { |
||||
|
IN_ADDR buffer_addr{}; |
||||
|
InetPtonW(AF_INET, in_str, &buffer_addr); |
||||
|
|
||||
|
return buffer_addr.S_un.S_addr; |
||||
|
} |
||||
|
|
||||
|
void WINAPI icmp_ping_callback(void* in_context, PIO_STATUS_BLOCK in_status_block, ULONG) { |
||||
|
std::unique_ptr<std::weak_ptr<PingContext>> weak_context{ static_cast<std::weak_ptr<PingContext>*>(in_context) }; |
||||
|
auto strong_context = weak_context->lock(); |
||||
|
|
||||
|
if (strong_context == nullptr) { |
||||
|
// Context has been destroyed already; s_pings must have changed
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Cleanup handle
|
||||
|
strong_context->finish(); |
||||
|
|
||||
|
if (NT_ERROR(in_status_block->Status)) { |
||||
|
// Ping failed somehow; set status to error
|
||||
|
strong_context->ping_time_milliseconds = PING_FAILED; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Verify ping reply size
|
||||
|
if (in_status_block->Information < icmp_request_data_length) { |
||||
|
// Ping did not reply with same data as sent; set status to error
|
||||
|
strong_context->ping_time_milliseconds = PING_FAILED; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
DWORD reply_count = IcmpParseReplies(strong_context->reply_buffer.get(), icmp_reply_buffer_size); |
||||
|
if (reply_count <= 0) { |
||||
|
// No response was received; this should never happen since one of the above checks would certainly have been hit
|
||||
|
strong_context->ping_time_milliseconds = PING_FAILED; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ICMP_ECHO_REPLY* replies = reinterpret_cast<ICMP_ECHO_REPLY*>(strong_context->reply_buffer.get()); |
||||
|
for (DWORD index = 0; index < reply_count; ++index) { // reply_count should never be anything other than 1, but hey, whatever
|
||||
|
ICMP_ECHO_REPLY& reply = replies[index]; |
||||
|
|
||||
|
// Verify success
|
||||
|
if (reply.Status != IP_SUCCESS) { |
||||
|
strong_context->ping_time_milliseconds = PING_FAILED; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Verify reply size; this is probably redundant, but hey whatever
|
||||
|
if (reply.DataSize != icmp_request_data_length) { |
||||
|
strong_context->ping_time_milliseconds = PING_FAILED; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Verify ping data patches
|
||||
|
if (std::memcmp(icmp_request_data, reply.Data, icmp_request_data_length) != 0) { |
||||
|
strong_context->ping_time_milliseconds = PING_FAILED; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Ping request successful; set it
|
||||
|
strong_context->ping_time_milliseconds = reply.RoundTripTime; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void process_pending_requests() { |
||||
|
std::unique_lock<std::mutex> lock{ s_pending_requests_mutex }; |
||||
|
// Loop until there are no pings in progress
|
||||
|
while (s_pings_in_progress != 0) { |
||||
|
// Process pending any requests in the queue
|
||||
|
while (!s_pending_requests.empty()) { |
||||
|
// Pop front pending request
|
||||
|
auto pending_request = s_pending_requests.front().lock(); |
||||
|
s_pending_requests.pop_front(); |
||||
|
|
||||
|
if (pending_request != nullptr) { |
||||
|
// Kick off ICMP request
|
||||
|
IcmpSendEcho2( |
||||
|
pending_request->icmp_handle, |
||||
|
nullptr, // event
|
||||
|
&icmp_ping_callback, |
||||
|
new std::weak_ptr<PingContext>(pending_request), |
||||
|
pending_request->destination, |
||||
|
icmp_request_data, |
||||
|
icmp_request_data_length, |
||||
|
nullptr, // options
|
||||
|
pending_request->reply_buffer.get(), |
||||
|
icmp_reply_buffer_size, |
||||
|
icmp_timeout); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Process any thread alerts / ICMP callbacks
|
||||
|
lock.unlock(); |
||||
|
SleepEx(10, TRUE); |
||||
|
lock.lock(); |
||||
|
} |
||||
|
|
||||
|
s_ping_thread_active = false; |
||||
|
} |
||||
|
|
||||
|
void queue_pending_request(std::weak_ptr<PingContext> in_pending_request) { |
||||
|
std::lock_guard<std::mutex> lock{ s_pending_requests_mutex }; |
||||
|
s_pending_requests.push_front(std::move(in_pending_request)); |
||||
|
|
||||
|
// Start thread if necessary
|
||||
|
if (!s_ping_thread_active) { |
||||
|
// Join any existing thread
|
||||
|
if (s_ping_thread.joinable()) { |
||||
|
s_ping_thread.join(); |
||||
|
} |
||||
|
|
||||
|
// Mark processing active
|
||||
|
s_ping_thread_active = true; |
||||
|
|
||||
|
// Spin up thread
|
||||
|
s_ping_thread = std::thread(&process_pending_requests); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extern "C" __declspec(dllexport) bool start_ping_request(const wchar_t* in_str) { |
||||
|
// Parse address to ping
|
||||
|
IPAddr destination_address = parse_address(in_str); |
||||
|
if (destination_address == 0) { |
||||
|
// Failed to parse
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Get ICMP handle to send request
|
||||
|
HANDLE icmp_handle = IcmpCreateFile(); |
||||
|
if (icmp_handle == INVALID_HANDLE_VALUE) { |
||||
|
// Failed to open handle for ICMP requests
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Build request context
|
||||
|
auto context = std::make_shared<PingContext>(); |
||||
|
context->icmp_handle = icmp_handle; |
||||
|
context->destination = destination_address; |
||||
|
context->reply_buffer = std::make_unique<unsigned char[]>(icmp_reply_buffer_size); |
||||
|
{ |
||||
|
//std::lock_guard<std::mutex> guard{ s_pings_mutex };
|
||||
|
|
||||
|
if (s_pings == nullptr) { |
||||
|
s_pings = std::make_shared<request_map>(); |
||||
|
} |
||||
|
|
||||
|
context->request_group = s_pings; |
||||
|
s_pings->emplace(in_str, context); |
||||
|
} |
||||
|
|
||||
|
// Queue request context
|
||||
|
queue_pending_request(context); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
extern "C" __declspec(dllexport) int get_ping(const wchar_t* in_str) { |
||||
|
//std::lock_guard<std::mutex> guard{ s_pings_mutex };
|
||||
|
|
||||
|
// Safety check that s_pings actually exists
|
||||
|
if (s_pings == nullptr) { |
||||
|
// Ping request does not exist
|
||||
|
return PING_FAILED; |
||||
|
} |
||||
|
|
||||
|
// Search for IP in ping list
|
||||
|
auto itr = s_pings->find(in_str); |
||||
|
if (itr == s_pings->end()) { |
||||
|
// Ping request does not exist
|
||||
|
return PING_FAILED; |
||||
|
} |
||||
|
|
||||
|
// This is a massive hack, but we need to be able to join the processing thread when we're done to prevent a shutdown crash
|
||||
|
if (!s_ping_thread_active && s_ping_thread.joinable()) { |
||||
|
s_ping_thread.join(); |
||||
|
} |
||||
|
|
||||
|
return itr->second->ping_time_milliseconds; |
||||
|
} |
||||
|
|
||||
|
extern "C" __declspec(dllexport) void clear_pings() { |
||||
|
//std::lock_guard<std::mutex> guard{ s_pings_mutex };
|
||||
|
s_pings = nullptr; |
||||
|
|
||||
|
if (!s_ping_thread_active && s_ping_thread.joinable()) { |
||||
|
s_ping_thread.join(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,238 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2016-2018 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
#include <stdbool.h> |
||||
|
#include <wchar.h> |
||||
|
|
||||
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS |
||||
|
#include <WinSock2.h> |
||||
|
#include <Windows.h> |
||||
|
#include <Iphlpapi.h> |
||||
|
|
||||
|
/** Relative path to the launcher executable */ |
||||
|
#define LAUNCHER_PATH "..\\..\\..\\Launcher\\Renegade X Launcher.exe" |
||||
|
|
||||
|
bool init_wsa_success; |
||||
|
|
||||
|
#pragma pack(push, 4) |
||||
|
struct socket_t { |
||||
|
uint64_t sock; |
||||
|
} s_socket; |
||||
|
|
||||
|
struct AcceptedSocket |
||||
|
{ |
||||
|
uint64_t sock; |
||||
|
int32_t addr; |
||||
|
int32_t port; |
||||
|
} accepted_socket; |
||||
|
#pragma pack(pop) |
||||
|
|
||||
|
void init_wsa() |
||||
|
{ |
||||
|
WSADATA wsadata; |
||||
|
init_wsa_success = WSAStartup(WINSOCK_VERSION, &wsadata); |
||||
|
} |
||||
|
|
||||
|
void cleanup_wsa() |
||||
|
{ |
||||
|
if (init_wsa_success) |
||||
|
WSACleanup(); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) struct socket_t* c_socket() |
||||
|
{ |
||||
|
s_socket.sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
||||
|
return &s_socket; |
||||
|
} |
||||
|
|
||||
|
// since UDK only supports IPv4, just return the IP address as an integer (or 0 on failure).
|
||||
|
/*int32_t c_resolve(unsigned char *hostname)
|
||||
|
{ |
||||
|
struct hostent *host_entry; |
||||
|
IN_ADDR address; |
||||
|
|
||||
|
host_entry = gethostbyname(hostname); |
||||
|
if (host_entry == NULL) |
||||
|
return 0; |
||||
|
|
||||
|
memcpy(&address, host_entry->h_addr_list[0], host_entry->h_length); |
||||
|
|
||||
|
return address.s_addr; |
||||
|
}*/ |
||||
|
|
||||
|
struct sockaddr_in helper_make_sockaddr_in(int32_t in_address, int32_t in_port) |
||||
|
{ |
||||
|
struct sockaddr_in address; |
||||
|
IN_ADDR in_addr; |
||||
|
|
||||
|
memset(&address, 0, sizeof(address)); |
||||
|
|
||||
|
in_addr.s_addr = htonl(in_address); |
||||
|
address.sin_addr = in_addr; |
||||
|
address.sin_family = AF_INET; |
||||
|
address.sin_port = htons(in_port); |
||||
|
|
||||
|
return address; |
||||
|
} |
||||
|
|
||||
|
// Binds to in_port, or fails
|
||||
|
__declspec(dllexport) int32_t c_bind(struct socket_t* in_socket, int32_t in_port) |
||||
|
{ |
||||
|
struct sockaddr_in address; |
||||
|
|
||||
|
address = helper_make_sockaddr_in(INADDR_ANY, in_port); |
||||
|
|
||||
|
if (bind(in_socket->sock, (struct sockaddr *) &address, sizeof(address)) == 0) |
||||
|
return in_port; |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
// Binds to in_port, or the next available
|
||||
|
__declspec(dllexport) int32_t c_bind_next(struct socket_t* in_socket, int32_t in_port) |
||||
|
{ |
||||
|
struct sockaddr_in address; |
||||
|
|
||||
|
address = helper_make_sockaddr_in(INADDR_ANY, in_port); |
||||
|
|
||||
|
while (true) |
||||
|
{ |
||||
|
if (bind(in_socket->sock, (struct sockaddr *) &address, sizeof(address)) == 0) |
||||
|
return in_port; |
||||
|
|
||||
|
if (WSAGetLastError() != WSAEADDRINUSE) |
||||
|
return 0; |
||||
|
|
||||
|
address.sin_port = htons(++in_port); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Binds to any port
|
||||
|
__declspec(dllexport) int32_t c_bind_any(struct socket_t* in_socket) |
||||
|
{ |
||||
|
struct sockaddr_in address; |
||||
|
|
||||
|
address = helper_make_sockaddr_in(INADDR_ANY, 0); |
||||
|
|
||||
|
if (bind(in_socket->sock, (struct sockaddr *) &address, sizeof(address)) == 0) |
||||
|
return ntohs(address.sin_port); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_listen(struct socket_t* in_socket) |
||||
|
{ |
||||
|
return listen(in_socket->sock, SOMAXCONN); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) struct AcceptedSocket *c_accept(struct socket_t* in_socket) |
||||
|
{ |
||||
|
struct sockaddr addr; |
||||
|
int size; |
||||
|
|
||||
|
size = sizeof(addr); |
||||
|
accepted_socket.sock = accept(in_socket->sock, &addr, &size); |
||||
|
|
||||
|
if (accepted_socket.sock != INVALID_SOCKET) |
||||
|
{ |
||||
|
accepted_socket.addr = ((struct sockaddr_in *) &addr)->sin_addr.s_addr; |
||||
|
accepted_socket.port = ((struct sockaddr_in *) &addr)->sin_port; |
||||
|
} |
||||
|
|
||||
|
return &accepted_socket; |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_connect(struct socket_t* in_socket, int32_t in_address, int32_t in_port) |
||||
|
{ |
||||
|
struct sockaddr_in address; |
||||
|
|
||||
|
address = helper_make_sockaddr_in(in_address, in_port); |
||||
|
|
||||
|
return connect(in_socket->sock, (struct sockaddr *) &address, sizeof(address)); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_close(struct socket_t* in_socket) |
||||
|
{ |
||||
|
return closesocket(in_socket->sock); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_recv(struct socket_t* in_socket, unsigned char *out_buffer, int32_t in_buffer_size) |
||||
|
{ |
||||
|
return recv(in_socket->sock, out_buffer, in_buffer_size, 0); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_send(struct socket_t* in_socket, unsigned char *in_buffer, int32_t in_buffer_size) |
||||
|
{ |
||||
|
return send(in_socket->sock, in_buffer, in_buffer_size, 0); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_set_blocking(struct socket_t* in_socket, int32_t in_value) |
||||
|
{ |
||||
|
unsigned long block_mode; |
||||
|
block_mode = in_value == 0 ? 1 : 0; |
||||
|
return ioctlsocket(in_socket->sock, FIONBIO, &block_mode); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_get_last_error() |
||||
|
{ |
||||
|
return GetLastError(); |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) int32_t c_check_status(struct socket_t* in_socket) |
||||
|
{ |
||||
|
fd_set set_read, set_err; |
||||
|
struct timeval time_val; |
||||
|
int value; |
||||
|
|
||||
|
time_val.tv_sec = 0; |
||||
|
time_val.tv_usec = 0; |
||||
|
FD_ZERO(&set_read); |
||||
|
FD_ZERO(&set_err); |
||||
|
FD_SET(in_socket->sock, &set_read); |
||||
|
FD_SET(in_socket->sock, &set_err); |
||||
|
|
||||
|
// check for success
|
||||
|
value = select(in_socket->sock + 1, NULL, &set_read, &set_err, &time_val); |
||||
|
if (value > 0) // socket can be written to
|
||||
|
{ |
||||
|
if (FD_ISSET(in_socket->sock, &set_read)) |
||||
|
return 1; |
||||
|
if (FD_ISSET(in_socket->sock, &set_err)) |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
__declspec(dllexport) bool UpdateGame() { |
||||
|
STARTUPINFOA startupInfo; |
||||
|
PROCESS_INFORMATION processInformation; |
||||
|
|
||||
|
memset(&startupInfo, 0, sizeof(startupInfo)); |
||||
|
memset(&processInformation, 0, sizeof(processInformation)); |
||||
|
|
||||
|
// Create process
|
||||
|
if (!CreateProcessA(LAUNCHER_PATH, NULL, NULL, NULL, false, 0, NULL, NULL, &startupInfo, &processInformation)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Process successfully created; exit the application so that patching doesn't fail horribly
|
||||
|
exit(EXIT_SUCCESS); |
||||
|
return true; // unreachable
|
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2016 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#if defined __cplusplus |
||||
|
extern "C" |
||||
|
{ |
||||
|
#endif // __cplusplus
|
||||
|
|
||||
|
void init_wsa(); |
||||
|
void cleanup_wsa(); |
||||
|
|
||||
|
typedef void* (*ReallocFunctionPtrType)(void* Original, DWORD Count, DWORD Alignment); |
||||
|
extern ReallocFunctionPtrType g_realloc_function; |
||||
|
|
||||
|
#if defined __cplusplus |
||||
|
} |
||||
|
|
||||
|
#endif //__cplusplus
|
@ -0,0 +1,80 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup Label="ProjectConfigurations"> |
||||
|
<ProjectConfiguration Include="Debug|Win32"> |
||||
|
<Configuration>Debug</Configuration> |
||||
|
<Platform>Win32</Platform> |
||||
|
</ProjectConfiguration> |
||||
|
<ProjectConfiguration Include="Release|Win32"> |
||||
|
<Configuration>Release</Configuration> |
||||
|
<Platform>Win32</Platform> |
||||
|
</ProjectConfiguration> |
||||
|
<ProjectConfiguration Include="Debug|x64"> |
||||
|
<Configuration>Debug</Configuration> |
||||
|
<Platform>x64</Platform> |
||||
|
</ProjectConfiguration> |
||||
|
<ProjectConfiguration Include="Release|x64"> |
||||
|
<Configuration>Release</Configuration> |
||||
|
<Platform>x64</Platform> |
||||
|
</ProjectConfiguration> |
||||
|
</ItemGroup> |
||||
|
<PropertyGroup Label="Globals"> |
||||
|
<ProjectGuid>{988779E2-4B8D-4997-82B6-25BC4E0A705E}</ProjectGuid> |
||||
|
<RootNamespace>Rx_TCPLink</RootNamespace> |
||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> |
||||
|
<PropertyGroup Label="Configuration"> |
||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType> |
||||
|
<UseDebugLibraries>false</UseDebugLibraries> |
||||
|
<PlatformToolset>v142</PlatformToolset> |
||||
|
<CharacterSet>Unicode</CharacterSet> |
||||
|
</PropertyGroup> |
||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
||||
|
<ImportGroup Label="ExtensionSettings"> |
||||
|
</ImportGroup> |
||||
|
<ImportGroup Label="Shared"> |
||||
|
</ImportGroup> |
||||
|
<ImportGroup Label="PropertySheets"> |
||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||
|
</ImportGroup> |
||||
|
<PropertyGroup Label="UserMacros" /> |
||||
|
<PropertyGroup /> |
||||
|
<ItemDefinitionGroup> |
||||
|
<ClCompile> |
||||
|
<WarningLevel>Level3</WarningLevel> |
||||
|
<Optimization>Disabled</Optimization> |
||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> |
||||
|
<StructMemberAlignment>Default</StructMemberAlignment> |
||||
|
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">..\discord-rpc\win64-static\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
||||
|
<AdditionalIncludeDirectories Condition="'$(Platform)'=='Win32'">..\discord-rpc\win32-static\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
||||
|
</ClCompile> |
||||
|
<Link> |
||||
|
<OptimizeReferences>true</OptimizeReferences> |
||||
|
<AdditionalLibraryDirectories Condition="'$(Platform)'=='x64'">..\discord-rpc\win64-static\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> |
||||
|
<AdditionalLibraryDirectories Condition="'$(Platform)'=='Win32'">..\discord-rpc\win32-static\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> |
||||
|
<AdditionalDependencies>Ws2_32.lib;iphlpapi.lib;discord-rpc.lib;%(AdditionalDependencies)</AdditionalDependencies> |
||||
|
</Link> |
||||
|
</ItemDefinitionGroup> |
||||
|
<ItemGroup> |
||||
|
<ClCompile Include="DiscordRpc.cpp" /> |
||||
|
<ClCompile Include="HWID.cpp" /> |
||||
|
<ClCompile Include="Main.cpp" /> |
||||
|
<ClCompile Include="Ping.cpp" /> |
||||
|
<ClCompile Include="Rx_TCPLink.c" /> |
||||
|
<ClCompile Include="Screenshot.cpp" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<ClInclude Condition="'$(Platform)'=='x64'" Include="..\discord-rpc\win64-static\include\discord_register.h" /> |
||||
|
<ClInclude Condition="'$(Platform)'=='x64'" Include="..\discord-rpc\win64-static\include\discord_rpc.h" /> |
||||
|
<ClInclude Condition="'$(Platform)'=='Win32'" Include="..\discord-rpc\win32-static\include\discord_register.h" /> |
||||
|
<ClInclude Condition="'$(Platform)'=='Win32'" Include="..\discord-rpc\win32-static\include\discord_rpc.h" /> |
||||
|
<ClInclude Include="DiscordRpc.h" /> |
||||
|
<ClInclude Include="Rx_TCPLink.h" /> |
||||
|
<ClInclude Include="UDKArray.h" /> |
||||
|
<ClInclude Include="Viewport.h" /> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
||||
|
<ImportGroup Label="ExtensionTargets"> |
||||
|
</ImportGroup> |
||||
|
</Project> |
@ -0,0 +1,11 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |
||||
|
<LocalDebuggerCommand>D:\RenX\SVN\UDK_Uncooked\Binaries\Win64\UDK.exe</LocalDebuggerCommand> |
||||
|
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
||||
|
<LocalDebuggerCommand>D:\RenX\SVN\UDK_Uncooked\Binaries\Win32\UDK.exe</LocalDebuggerCommand> |
||||
|
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> |
||||
|
</PropertyGroup> |
||||
|
</Project> |
@ -0,0 +1,165 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2019 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#include <atlimage.h> |
||||
|
#include <string> |
||||
|
#include <ctime> |
||||
|
#include <fstream> |
||||
|
#include "Viewport.h" |
||||
|
|
||||
|
Viewport::ReadPixelsFunctionType Viewport::readPixelsFunction = nullptr; |
||||
|
|
||||
|
/** Filename related constants */ |
||||
|
static constexpr wchar_t prefix[]{ L"ScreenCapture " }; |
||||
|
static constexpr size_t prefix_length = sizeof(prefix) / sizeof(wchar_t) - 1; |
||||
|
static constexpr wchar_t extension[]{ L".jpg" }; |
||||
|
static constexpr size_t extension_length = sizeof(extension) / sizeof(wchar_t) - 1; |
||||
|
static constexpr size_t max_date_length = FILENAME_MAX - prefix_length - extension_length; |
||||
|
|
||||
|
#pragma pack(push, 4) |
||||
|
struct ScreenCapture |
||||
|
{ |
||||
|
UDKArray<uint8_t> data; |
||||
|
|
||||
|
// For pushing data to captures
|
||||
|
template<typename T> |
||||
|
void push(const T& in_data) { |
||||
|
// Expand capture as necessary
|
||||
|
int required_size = data.ArrayNum + sizeof(T); |
||||
|
if (required_size >= data.ArrayMax) { |
||||
|
// Expand to minimum required size, or double array size, whichever is larger
|
||||
|
data.Reallocate(max(required_size, data.ArrayMax * 2)); |
||||
|
} |
||||
|
|
||||
|
// Copy data to capture
|
||||
|
std::memcpy(data.Data + data.ArrayNum, &in_data, sizeof(T)); |
||||
|
data.ArrayNum += sizeof(T); |
||||
|
} |
||||
|
|
||||
|
// For popping from captures
|
||||
|
template<typename T> |
||||
|
T pop() { |
||||
|
T result{}; |
||||
|
if (sizeof(T) > data.ArrayNum) { |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
// Copy item to result
|
||||
|
uint8_t* item_begin = data.Data + data.ArrayNum - sizeof(T); |
||||
|
std::memcpy(&result, item_begin, sizeof(T)); |
||||
|
|
||||
|
// Pop item
|
||||
|
data.ArrayNum -= sizeof(T); |
||||
|
|
||||
|
// Return result
|
||||
|
return result; |
||||
|
} |
||||
|
}; |
||||
|
#pragma pack(pop) |
||||
|
|
||||
|
extern "C" __declspec(dllexport) void take_ss(Viewport::DllBindReference* viewport, ScreenCapture* result) |
||||
|
{ |
||||
|
// Read in screenshot data from UDK
|
||||
|
viewport->value->readPixels(result->data, { 6, 0, 1, 16000.0F }); |
||||
|
int sizeX = viewport->value->getSizeX(); |
||||
|
int sizeY = viewport->value->getSizeY(); |
||||
|
|
||||
|
// Import screenCapture data into CImage
|
||||
|
CImage outputImage; |
||||
|
outputImage.Create(sizeX, sizeY, 32); |
||||
|
for (int row = 0; row < sizeY; ++row) |
||||
|
{ |
||||
|
const void* screenCaptureRowAddress = &result->data[row * sizeX * sizeof(UDKColor)]; |
||||
|
void* outputRowAddress = outputImage.GetPixelAddress(0, row); |
||||
|
memcpy(outputRowAddress, screenCaptureRowAddress, sizeX * 4); |
||||
|
} |
||||
|
|
||||
|
// Save JPEG image data to memory stream
|
||||
|
auto stream = SHCreateMemStream(NULL, 0); |
||||
|
outputImage.Save(stream, Gdiplus::ImageFormatJPEG); |
||||
|
stream->Seek({}, STREAM_SEEK_SET, 0); |
||||
|
|
||||
|
// Get stream size
|
||||
|
STATSTG stream_stat{}; |
||||
|
stream->Stat(&stream_stat, STATFLAG::STATFLAG_NONAME); |
||||
|
auto stream_size = stream_stat.cbSize.QuadPart; |
||||
|
|
||||
|
// Read data from stream into buffer
|
||||
|
result->data.ArrayNum = stream_size; |
||||
|
stream->Read(result->data.Data, result->data.ArrayMax, nullptr); |
||||
|
stream->Release(); |
||||
|
} |
||||
|
|
||||
|
static_assert(sizeof(UDKColor) == 4, "UDKColor size != 4"); |
||||
|
|
||||
|
extern "C" __declspec(dllexport) void write_ss(ScreenCapture* screenCapture) |
||||
|
{ |
||||
|
// Get current date
|
||||
|
auto currentTime = std::time(nullptr); |
||||
|
auto currentTm = localtime(¤tTime); |
||||
|
wchar_t timeString[max_date_length + 1]; |
||||
|
std::wcsftime(timeString, max_date_length, L"%F - %H-%M-%S", currentTm); |
||||
|
|
||||
|
// Build filename
|
||||
|
std::wstring filename{ prefix }; |
||||
|
filename += timeString; |
||||
|
filename += extension; |
||||
|
|
||||
|
// Write to file
|
||||
|
std::ofstream file{ filename, std::ios::binary }; |
||||
|
file.write(reinterpret_cast<char*>(screenCapture->data.Data), screenCapture->data.ArrayNum); |
||||
|
} |
||||
|
|
||||
|
constexpr size_t byte_buffer_size = 256; |
||||
|
|
||||
|
/**
|
||||
|
* Copies data from a dynamic UDK byte array to a static UDK byte array |
||||
|
* |
||||
|
* @param out_destination Byte buffer to copy data to |
||||
|
* @param in_source Byte array to copy data from |
||||
|
* @param in_offset Position of in_source to start copying from |
||||
|
* @return Total number of bytes copied (will never exceed byte_buffer_size) |
||||
|
*/ |
||||
|
extern "C" __declspec(dllexport) int copy_array_to_buffer(uint8_t* out_destination, ScreenCapture* in_source, int in_offset) { |
||||
|
// Sanity check length and offset
|
||||
|
if (in_offset < 0 |
||||
|
|| in_source == nullptr |
||||
|
|| in_source->data.ArrayNum <= in_offset |
||||
|
|| out_destination == nullptr) { |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
size_t length = min(byte_buffer_size, in_source->data.ArrayNum - in_offset); // Remaining bytes in array or buffer size, whichever is smaller
|
||||
|
std::memcpy(out_destination, in_source->data.Data + in_offset, length); |
||||
|
return length; |
||||
|
} |
||||
|
|
||||
|
/** static shit */ |
||||
|
|
||||
|
uint8_t* s_cap_buffer = nullptr; |
||||
|
ScreenCapture* s_capture = nullptr; |
||||
|
|
||||
|
extern "C" __declspec(dllexport) void set_cap(uint8_t* buffer, ScreenCapture* screenCapture) { |
||||
|
s_cap_buffer = buffer; |
||||
|
s_capture = screenCapture; |
||||
|
} |
||||
|
|
||||
|
extern "C" __declspec(dllexport) int read_cap(int in_offset) { |
||||
|
int result = copy_array_to_buffer(s_cap_buffer, s_capture, in_offset); |
||||
|
return result; |
||||
|
} |
@ -0,0 +1,75 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2019 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <algorithm> |
||||
|
#include "Rx_TCPLink.h" |
||||
|
|
||||
|
#pragma pack(push, 4) |
||||
|
template<typename DataT> |
||||
|
struct UDKArray |
||||
|
{ |
||||
|
void Reallocate(int NewNum) |
||||
|
{ |
||||
|
ArrayMax = NewNum; |
||||
|
Data = static_cast<DataT*>((*g_realloc_function)(Data, ArrayMax * sizeof(DataT), 8)); |
||||
|
} |
||||
|
|
||||
|
void reserve(int in_size) { |
||||
|
if (in_size > ArrayMax) { |
||||
|
Reallocate(in_size); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
DataT& operator[](const int index) |
||||
|
{ |
||||
|
return Data[index]; |
||||
|
} |
||||
|
|
||||
|
const DataT& operator[](const int index) const |
||||
|
{ |
||||
|
return Data[index]; |
||||
|
} |
||||
|
|
||||
|
void push_back(const DataT& in_item) { |
||||
|
if (ArrayNum >= ArrayMax) { |
||||
|
Reallocate(max(ArrayNum * 2, 8)); |
||||
|
} |
||||
|
|
||||
|
Data[ArrayNum] = in_item; |
||||
|
++ArrayNum; |
||||
|
} |
||||
|
|
||||
|
void pop_back() { |
||||
|
--ArrayNum; |
||||
|
} |
||||
|
|
||||
|
DataT& back() const { |
||||
|
return Data[ArrayNum - 1]; |
||||
|
} |
||||
|
|
||||
|
DataT& front() const { |
||||
|
return Data[0]; |
||||
|
} |
||||
|
|
||||
|
DataT* Data; |
||||
|
int ArrayNum; |
||||
|
int ArrayMax; |
||||
|
}; |
||||
|
#pragma pack(pop) |
@ -0,0 +1,105 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2019 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <cinttypes> |
||||
|
#include "UDKArray.h" |
||||
|
|
||||
|
#pragma pack(push, 4) |
||||
|
struct UDKColor |
||||
|
{ |
||||
|
union { |
||||
|
struct { |
||||
|
uint8_t b, g, r, a; |
||||
|
}; |
||||
|
|
||||
|
uint8_t bytes[4]; |
||||
|
uint32_t uint; |
||||
|
int32_t sint; |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
static_assert(sizeof(UDKColor) == 4, "UDKColor has unexpected extra data; must be exactly 4"); |
||||
|
|
||||
|
struct ReadPixelsMetadata |
||||
|
{ |
||||
|
int cubeFace; |
||||
|
int compresssionMode; |
||||
|
int convertLinearToGamma; |
||||
|
float depth; |
||||
|
}; |
||||
|
|
||||
|
class Viewport |
||||
|
{ |
||||
|
public: |
||||
|
int readPixels(UDKArray<uint8_t>& result, ReadPixelsMetadata metadata) |
||||
|
{ |
||||
|
int value = readPixels(reinterpret_cast<UDKArray<UDKColor>&>(result), metadata); |
||||
|
|
||||
|
// expand to bytes
|
||||
|
result.ArrayNum *= sizeof(UDKColor); |
||||
|
result.ArrayMax *= sizeof(UDKColor); |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
int readPixels(UDKArray<UDKColor>& result, ReadPixelsMetadata metadata) |
||||
|
{ |
||||
|
return readPixelsFunction(this, result, metadata); |
||||
|
} |
||||
|
|
||||
|
int getSizeX() const |
||||
|
{ |
||||
|
return callVirtualFunction<2, int>(); |
||||
|
} |
||||
|
|
||||
|
int getSizeY() const |
||||
|
{ |
||||
|
return callVirtualFunction<3, int>(); |
||||
|
} |
||||
|
|
||||
|
using ReadPixelsFunctionType = int(__thiscall*)(void*, UDKArray<UDKColor>&, ReadPixelsMetadata); |
||||
|
|
||||
|
static void setReadPixelsFunction(const ReadPixelsFunctionType value) |
||||
|
{ |
||||
|
readPixelsFunction = value; |
||||
|
} |
||||
|
|
||||
|
static ReadPixelsFunctionType readPixelsFunction; |
||||
|
|
||||
|
struct DllBindReference |
||||
|
{ |
||||
|
Viewport* value; |
||||
|
}; |
||||
|
|
||||
|
private: |
||||
|
template<size_t Index, typename ReturnType, typename... ArgumentList> ReturnType callVirtualFunction(ArgumentList... arguments) |
||||
|
{ |
||||
|
using FunctionType = ReturnType(__thiscall*)(void*, ArgumentList...); |
||||
|
const auto p = (*reinterpret_cast<FunctionType**>(this))[Index]; |
||||
|
return p(this, arguments...); |
||||
|
} |
||||
|
|
||||
|
template<size_t Index, typename ReturnType, typename... ArgumentList> ReturnType callVirtualFunction(ArgumentList... arguments) const |
||||
|
{ |
||||
|
using FunctionType = ReturnType(__thiscall*)(const void*, ArgumentList...); |
||||
|
const auto p = (*reinterpret_cast<FunctionType*const*>(this))[Index]; |
||||
|
return p(this, arguments...); |
||||
|
} |
||||
|
}; |
||||
|
#pragma pack(pop) |
@ -0,0 +1,32 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2016 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#include "Rx_TCPLink.h" |
||||
|
|
||||
|
class WSAInitCleanup |
||||
|
{ |
||||
|
public: |
||||
|
WSAInitCleanup() |
||||
|
{ |
||||
|
init_wsa(); |
||||
|
} |
||||
|
~WSAInitCleanup() |
||||
|
{ |
||||
|
cleanup_wsa(); |
||||
|
} |
||||
|
} _wsa_init_cleanup; |
Binary file not shown.
@ -0,0 +1,26 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#if defined(DISCORD_DYNAMIC_LIB) |
||||
|
#if defined(_WIN32) |
||||
|
#if defined(DISCORD_BUILDING_SDK) |
||||
|
#define DISCORD_EXPORT __declspec(dllexport) |
||||
|
#else |
||||
|
#define DISCORD_EXPORT __declspec(dllimport) |
||||
|
#endif |
||||
|
#else |
||||
|
#define DISCORD_EXPORT __attribute__((visibility("default"))) |
||||
|
#endif |
||||
|
#else |
||||
|
#define DISCORD_EXPORT |
||||
|
#endif |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command); |
||||
|
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif |
@ -0,0 +1,87 @@ |
|||||
|
#pragma once |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
// clang-format off
|
||||
|
|
||||
|
#if defined(DISCORD_DYNAMIC_LIB) |
||||
|
# if defined(_WIN32) |
||||
|
# if defined(DISCORD_BUILDING_SDK) |
||||
|
# define DISCORD_EXPORT __declspec(dllexport) |
||||
|
# else |
||||
|
# define DISCORD_EXPORT __declspec(dllimport) |
||||
|
# endif |
||||
|
# else |
||||
|
# define DISCORD_EXPORT __attribute__((visibility("default"))) |
||||
|
# endif |
||||
|
#else |
||||
|
# define DISCORD_EXPORT |
||||
|
#endif |
||||
|
|
||||
|
// clang-format on
|
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
typedef struct DiscordRichPresence { |
||||
|
const char* state; /* max 128 bytes */ |
||||
|
const char* details; /* max 128 bytes */ |
||||
|
int64_t startTimestamp; |
||||
|
int64_t endTimestamp; |
||||
|
const char* largeImageKey; /* max 32 bytes */ |
||||
|
const char* largeImageText; /* max 128 bytes */ |
||||
|
const char* smallImageKey; /* max 32 bytes */ |
||||
|
const char* smallImageText; /* max 128 bytes */ |
||||
|
const char* partyId; /* max 128 bytes */ |
||||
|
int partySize; |
||||
|
int partyMax; |
||||
|
const char* matchSecret; /* max 128 bytes */ |
||||
|
const char* joinSecret; /* max 128 bytes */ |
||||
|
const char* spectateSecret; /* max 128 bytes */ |
||||
|
int8_t instance; |
||||
|
} DiscordRichPresence; |
||||
|
|
||||
|
typedef struct DiscordUser { |
||||
|
const char* userId; |
||||
|
const char* username; |
||||
|
const char* discriminator; |
||||
|
const char* avatar; |
||||
|
} DiscordUser; |
||||
|
|
||||
|
typedef struct DiscordEventHandlers { |
||||
|
void (*ready)(const DiscordUser* request); |
||||
|
void (*disconnected)(int errorCode, const char* message); |
||||
|
void (*errored)(int errorCode, const char* message); |
||||
|
void (*joinGame)(const char* joinSecret); |
||||
|
void (*spectateGame)(const char* spectateSecret); |
||||
|
void (*joinRequest)(const DiscordUser* request); |
||||
|
} DiscordEventHandlers; |
||||
|
|
||||
|
#define DISCORD_REPLY_NO 0 |
||||
|
#define DISCORD_REPLY_YES 1 |
||||
|
#define DISCORD_REPLY_IGNORE 2 |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_Initialize(const char* applicationId, |
||||
|
DiscordEventHandlers* handlers, |
||||
|
int autoRegister, |
||||
|
const char* optionalSteamId); |
||||
|
DISCORD_EXPORT void Discord_Shutdown(void); |
||||
|
|
||||
|
/* checks for incoming messages, dispatches callbacks */ |
||||
|
DISCORD_EXPORT void Discord_RunCallbacks(void); |
||||
|
|
||||
|
/* If you disable the lib starting its own io thread, you'll need to call this from your own */ |
||||
|
#ifdef DISCORD_DISABLE_IO_THREAD |
||||
|
DISCORD_EXPORT void Discord_UpdateConnection(void); |
||||
|
#endif |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence); |
||||
|
DISCORD_EXPORT void Discord_ClearPresence(void); |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} /* extern "C" */ |
||||
|
#endif |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,26 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#if defined(DISCORD_DYNAMIC_LIB) |
||||
|
#if defined(_WIN32) |
||||
|
#if defined(DISCORD_BUILDING_SDK) |
||||
|
#define DISCORD_EXPORT __declspec(dllexport) |
||||
|
#else |
||||
|
#define DISCORD_EXPORT __declspec(dllimport) |
||||
|
#endif |
||||
|
#else |
||||
|
#define DISCORD_EXPORT __attribute__((visibility("default"))) |
||||
|
#endif |
||||
|
#else |
||||
|
#define DISCORD_EXPORT |
||||
|
#endif |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command); |
||||
|
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif |
@ -0,0 +1,87 @@ |
|||||
|
#pragma once |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
// clang-format off
|
||||
|
|
||||
|
#if defined(DISCORD_DYNAMIC_LIB) |
||||
|
# if defined(_WIN32) |
||||
|
# if defined(DISCORD_BUILDING_SDK) |
||||
|
# define DISCORD_EXPORT __declspec(dllexport) |
||||
|
# else |
||||
|
# define DISCORD_EXPORT __declspec(dllimport) |
||||
|
# endif |
||||
|
# else |
||||
|
# define DISCORD_EXPORT __attribute__((visibility("default"))) |
||||
|
# endif |
||||
|
#else |
||||
|
# define DISCORD_EXPORT |
||||
|
#endif |
||||
|
|
||||
|
// clang-format on
|
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
typedef struct DiscordRichPresence { |
||||
|
const char* state; /* max 128 bytes */ |
||||
|
const char* details; /* max 128 bytes */ |
||||
|
int64_t startTimestamp; |
||||
|
int64_t endTimestamp; |
||||
|
const char* largeImageKey; /* max 32 bytes */ |
||||
|
const char* largeImageText; /* max 128 bytes */ |
||||
|
const char* smallImageKey; /* max 32 bytes */ |
||||
|
const char* smallImageText; /* max 128 bytes */ |
||||
|
const char* partyId; /* max 128 bytes */ |
||||
|
int partySize; |
||||
|
int partyMax; |
||||
|
const char* matchSecret; /* max 128 bytes */ |
||||
|
const char* joinSecret; /* max 128 bytes */ |
||||
|
const char* spectateSecret; /* max 128 bytes */ |
||||
|
int8_t instance; |
||||
|
} DiscordRichPresence; |
||||
|
|
||||
|
typedef struct DiscordUser { |
||||
|
const char* userId; |
||||
|
const char* username; |
||||
|
const char* discriminator; |
||||
|
const char* avatar; |
||||
|
} DiscordUser; |
||||
|
|
||||
|
typedef struct DiscordEventHandlers { |
||||
|
void (*ready)(const DiscordUser* request); |
||||
|
void (*disconnected)(int errorCode, const char* message); |
||||
|
void (*errored)(int errorCode, const char* message); |
||||
|
void (*joinGame)(const char* joinSecret); |
||||
|
void (*spectateGame)(const char* spectateSecret); |
||||
|
void (*joinRequest)(const DiscordUser* request); |
||||
|
} DiscordEventHandlers; |
||||
|
|
||||
|
#define DISCORD_REPLY_NO 0 |
||||
|
#define DISCORD_REPLY_YES 1 |
||||
|
#define DISCORD_REPLY_IGNORE 2 |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_Initialize(const char* applicationId, |
||||
|
DiscordEventHandlers* handlers, |
||||
|
int autoRegister, |
||||
|
const char* optionalSteamId); |
||||
|
DISCORD_EXPORT void Discord_Shutdown(void); |
||||
|
|
||||
|
/* checks for incoming messages, dispatches callbacks */ |
||||
|
DISCORD_EXPORT void Discord_RunCallbacks(void); |
||||
|
|
||||
|
/* If you disable the lib starting its own io thread, you'll need to call this from your own */ |
||||
|
#ifdef DISCORD_DISABLE_IO_THREAD |
||||
|
DISCORD_EXPORT void Discord_UpdateConnection(void); |
||||
|
#endif |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence); |
||||
|
DISCORD_EXPORT void Discord_ClearPresence(void); |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); |
||||
|
|
||||
|
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} /* extern "C" */ |
||||
|
#endif |
Binary file not shown.
Loading…
Reference in new issue