Browse Source

Initial commit from SVN

master
Jessica James 3 years ago
commit
389794f82f
  1. 3
      .gitignore
  2. 28
      Rx_TCPLink.sln
  3. 149
      Rx_TCPLink/DiscordRpc.cpp
  4. 29
      Rx_TCPLink/DiscordRpc.h
  5. 148
      Rx_TCPLink/HWID.cpp
  6. 90
      Rx_TCPLink/Main.cpp
  7. 254
      Rx_TCPLink/Ping.cpp
  8. 238
      Rx_TCPLink/Rx_TCPLink.c
  9. 33
      Rx_TCPLink/Rx_TCPLink.h
  10. 80
      Rx_TCPLink/Rx_TCPLink.vcxproj
  11. 11
      Rx_TCPLink/Rx_TCPLink.vcxproj.user
  12. 165
      Rx_TCPLink/Screenshot.cpp
  13. 75
      Rx_TCPLink/UDKArray.h
  14. 105
      Rx_TCPLink/Viewport.h
  15. 32
      Rx_TCPLink/WSAInitCleanup.cpp
  16. BIN
      discord-rpc/win32-static/bin/send-presence.exe
  17. 26
      discord-rpc/win32-static/include/discord_register.h
  18. 87
      discord-rpc/win32-static/include/discord_rpc.h
  19. BIN
      discord-rpc/win32-static/lib/discord-rpc.lib
  20. BIN
      discord-rpc/win64-static/bin/send-presence.exe
  21. 26
      discord-rpc/win64-static/include/discord_register.h
  22. 87
      discord-rpc/win64-static/include/discord_rpc.h
  23. BIN
      discord-rpc/win64-static/lib/discord-rpc.lib

3
.gitignore

@ -0,0 +1,3 @@
.vs/
Debug/
Release/

28
Rx_TCPLink.sln

@ -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

149
Rx_TCPLink/DiscordRpc.cpp

@ -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);*/

29
Rx_TCPLink/DiscordRpc.h

@ -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

148
Rx_TCPLink/HWID.cpp

@ -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());
}

90
Rx_TCPLink/Main.cpp

@ -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();
}

254
Rx_TCPLink/Ping.cpp

@ -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();
}
}

238
Rx_TCPLink/Rx_TCPLink.c

@ -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
}

33
Rx_TCPLink/Rx_TCPLink.h

@ -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

80
Rx_TCPLink/Rx_TCPLink.vcxproj

@ -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>

11
Rx_TCPLink/Rx_TCPLink.vcxproj.user

@ -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>

165
Rx_TCPLink/Screenshot.cpp

@ -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(&currentTime);
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;
}

75
Rx_TCPLink/UDKArray.h

@ -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)

105
Rx_TCPLink/Viewport.h

@ -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)

32
Rx_TCPLink/WSAInitCleanup.cpp

@ -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;

BIN
discord-rpc/win32-static/bin/send-presence.exe

Binary file not shown.

26
discord-rpc/win32-static/include/discord_register.h

@ -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

87
discord-rpc/win32-static/include/discord_rpc.h

@ -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

BIN
discord-rpc/win32-static/lib/discord-rpc.lib

Binary file not shown.

BIN
discord-rpc/win64-static/bin/send-presence.exe

Binary file not shown.

26
discord-rpc/win64-static/include/discord_register.h

@ -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

87
discord-rpc/win64-static/include/discord_rpc.h

@ -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

BIN
discord-rpc/win64-static/lib/discord-rpc.lib

Binary file not shown.
Loading…
Cancel
Save