mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-07-01 23:46:59 +00:00
279 lines
9.0 KiB
C++
279 lines
9.0 KiB
C++
///
|
|
/// Created by Anonymous275 on 4/11/2020
|
|
///
|
|
#include "Discord/discord_info.h"
|
|
#include "Network/network.h"
|
|
#include "Security/Enc.h"
|
|
#include <WS2tcpip.h>
|
|
#include <filesystem>
|
|
#include "Startup.h"
|
|
#include <algorithm>
|
|
#include "Logger.h"
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
|
|
|
|
namespace fs = std::experimental::filesystem;
|
|
std::string ListOfMods;
|
|
std::vector<std::string> Split(const std::string& String,const std::string& delimiter){
|
|
std::vector<std::string> Val;
|
|
size_t pos;
|
|
std::string token,s = String;
|
|
while ((pos = s.find(delimiter)) != std::string::npos) {
|
|
token = s.substr(0, pos);
|
|
if(!token.empty())Val.push_back(token);
|
|
s.erase(0, pos + delimiter.length());
|
|
}
|
|
if(!s.empty())Val.push_back(s);
|
|
return Val;
|
|
}
|
|
|
|
void STCPSendRaw(SOCKET socket,const std::vector<char>& Data){
|
|
if(socket == -1){
|
|
Terminate = true;
|
|
return;
|
|
}
|
|
int BytesSent = send(socket, Data.data(), int(Data.size()), 0);
|
|
if (BytesSent == 0){
|
|
debug(Sec("(TCP) Connection closing..."));
|
|
Terminate = true;
|
|
return;
|
|
}
|
|
else if (BytesSent < 0) {
|
|
debug(Sec("(TCP) send failed with error: ") + std::to_string(WSAGetLastError()));
|
|
closesocket(socket);
|
|
Terminate = true;
|
|
return;
|
|
}
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
}
|
|
void STCPSend(SOCKET socket,const std::string&Data){
|
|
STCPSendRaw(socket,std::vector<char>(Data.begin(),Data.end()));
|
|
}
|
|
std::pair<char*,size_t> STCPRecv(SOCKET socket){
|
|
char buf[64000];
|
|
int len = 64000;
|
|
ZeroMemory(buf, len);
|
|
int BytesRcv = recv(socket, buf, len,0);
|
|
if (BytesRcv == 0){
|
|
info(Sec("(TCP) Connection closing..."));
|
|
Terminate = true;
|
|
return std::make_pair((char*)"",0);
|
|
}else if (BytesRcv < 0) {
|
|
info(Sec("(TCP) recv failed with error: ") + std::to_string(WSAGetLastError()));
|
|
closesocket(socket);
|
|
Terminate = true;
|
|
return std::make_pair((char*)"",0);
|
|
}
|
|
char* Ret = new char[BytesRcv];
|
|
memcpy_s(Ret,BytesRcv,buf,BytesRcv);
|
|
ZeroMemory(buf, len);
|
|
return std::make_pair(Ret,BytesRcv);
|
|
}
|
|
void CheckForDir(){
|
|
struct stat info{};
|
|
if(stat( Sec("Resources"), &info) != 0){
|
|
_wmkdir(SecW(L"Resources"));
|
|
}
|
|
}
|
|
void WaitForConfirm(){
|
|
while(!Terminate && !ModLoaded){
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
ModLoaded = false;
|
|
}
|
|
|
|
int N,E;
|
|
void Parse(const std::string& msg){
|
|
std::stringstream ss(msg);
|
|
std::string t;
|
|
while (std::getline(ss, t, 'g')) {
|
|
if(t.find_first_not_of(Sec("0123456789abcdef")) != std::string::npos)return;
|
|
if(N == 0){
|
|
N = std::stoi(t, nullptr, 16);
|
|
}else if(E == 0){
|
|
E = std::stoi(t, nullptr, 16);
|
|
}else return;
|
|
}
|
|
}
|
|
std::string GenerateM(RSA*key){
|
|
std::stringstream stream;
|
|
stream << std::hex << key->n << "g" << key->e << "g" << RSA_E(Sec("IDC"),key->e,key->n);
|
|
return stream.str();
|
|
}
|
|
struct Hold{
|
|
SOCKET TCPSock{};
|
|
bool Done = false;
|
|
};
|
|
void Check(Hold* S){
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
if(S != nullptr){
|
|
if(!S->Done && S->TCPSock != -1){
|
|
closesocket(S->TCPSock);
|
|
}
|
|
}
|
|
}
|
|
std::vector<char> PrependSize(const std::string& str) {
|
|
uint32_t Size = htonl(uint32_t(str.size()) + 1);
|
|
std::vector<char> result;
|
|
// +1 for \0, +4 for the size
|
|
result.resize(str.size() + 1 + 4);
|
|
memcpy(result.data(), &Size, 4);
|
|
memcpy(result.data() + 4, str.data(), str.size() + 1);
|
|
return result;
|
|
}
|
|
std::string HandShake(SOCKET Sock,Hold*S,RSA*LKey){
|
|
S->TCPSock = Sock;
|
|
std::thread Timeout(Check,S);
|
|
Timeout.detach();
|
|
N = 0;E = 0;
|
|
auto Res = STCPRecv(Sock);
|
|
std::string msg(Res.first,Res.second);
|
|
Parse(msg);
|
|
if(N != 0 && E != 0) {
|
|
STCPSendRaw(Sock,PrependSize(GenerateM(LKey)));
|
|
Res = STCPRecv(Sock);
|
|
msg = std::string(Res.first,Res.second);
|
|
if(RSA_D(msg,LKey->d,LKey->n) != "HC"){
|
|
Terminate = true;
|
|
}
|
|
}else Terminate = true;
|
|
S->Done = true;
|
|
|
|
if(Terminate){
|
|
TCPTerminate = true;
|
|
UlStatus = Sec("UlDisconnected: full or outdated server");
|
|
info(Sec("Terminated!"));
|
|
return "";
|
|
}
|
|
msg = RSA_E("NR" + GetDName() + ":" + GetDID(),E,N);
|
|
if(!msg.empty()) {
|
|
STCPSendRaw(Sock, PrependSize(msg));
|
|
STCPSendRaw(Sock, PrependSize(RSA_E("VC" + GetVer(),E,N)));
|
|
Res = STCPRecv(Sock);
|
|
msg = Res.first;
|
|
}
|
|
|
|
if(N == 0 || E == 0 || msg.size() < 2 || msg.substr(0,2) != "WS"){
|
|
|
|
Terminate = true;
|
|
TCPTerminate = true;
|
|
UlStatus = Sec("UlDisconnected: full or outdated server");
|
|
info(Sec("Terminated!"));
|
|
return "";
|
|
}
|
|
STCPSend(Sock,Sec("SR"));
|
|
Res = STCPRecv(Sock);
|
|
if(strlen(Res.first) == 0 || std::string(Res.first) == "-"){
|
|
info(Sec("Didn't Receive any mods..."));
|
|
ListOfMods = "-";
|
|
STCPSend(Sock,Sec("Done"));
|
|
info(Sec("Done!"));
|
|
return "";
|
|
}
|
|
return Res.first;
|
|
}
|
|
|
|
void SyncResources(SOCKET Sock){
|
|
RSA*LKey = GenKey();
|
|
auto* S = new Hold;
|
|
std::string Ret = HandShake(Sock,S,LKey);
|
|
delete LKey;
|
|
delete S;
|
|
if(Ret.empty())return;
|
|
|
|
info(Sec("Checking Resources..."));
|
|
CheckForDir();
|
|
|
|
std::vector<std::string> list = Split(Ret, ";");
|
|
std::vector<std::string> FNames(list.begin(), list.begin() + (list.size() / 2));
|
|
std::vector<std::string> FSizes(list.begin() + (list.size() / 2), list.end());
|
|
list.clear();
|
|
Ret.clear();
|
|
int Amount = 0,Pos = 0;
|
|
std::string a,t;
|
|
for(const std::string&name : FNames){
|
|
if(!name.empty()){
|
|
t += name.substr(name.find_last_of('/') + 1) + ";";
|
|
}
|
|
}
|
|
if(t.empty())ListOfMods = "-";
|
|
else ListOfMods = t;
|
|
t.clear();
|
|
for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) {
|
|
auto pos = FN->find_last_of('/');
|
|
if (pos == std::string::npos)continue;
|
|
Amount++;
|
|
}
|
|
if(!FNames.empty())info(Sec("Syncing..."));
|
|
for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) {
|
|
auto pos = FN->find_last_of('/');
|
|
if (pos != std::string::npos) {
|
|
a = Sec("Resources") + FN->substr(pos);
|
|
} else continue;
|
|
Pos++;
|
|
if (fs::exists(a)) {
|
|
if (FS->find_first_not_of("0123456789") != std::string::npos)continue;
|
|
if (fs::file_size(a) == std::stoi(*FS)){
|
|
UlStatus = Sec("UlLoading Resource: (") + std::to_string(Pos) + "/" + std::to_string(Amount) +
|
|
"): " + a.substr(a.find_last_of('/'));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
fs::copy_file(a, Sec("BeamNG/mods")+a.substr(a.find_last_of('/')), fs::copy_options::overwrite_existing);
|
|
WaitForConfirm();
|
|
continue;
|
|
}else remove(a.c_str());
|
|
}
|
|
CheckForDir();
|
|
do {
|
|
STCPSend(Sock, "f" + *FN);
|
|
int Recv = 0,Size = std::stoi(*FS);
|
|
char*File = new char[Size];
|
|
ZeroMemory(File,Size);
|
|
do {
|
|
auto Pair = STCPRecv(Sock);
|
|
char* Data = Pair.first;
|
|
size_t BytesRcv = Pair.second;
|
|
if (strcmp(Data, Sec("Cannot Open")) == 0 || Terminate){
|
|
if(BytesRcv != 0)delete[] Data;
|
|
break;
|
|
}
|
|
memcpy_s(File+Recv,BytesRcv,Data,BytesRcv);
|
|
Recv += int(BytesRcv);
|
|
float per = float(Recv)/std::stof(*FS) * 100;
|
|
std::string Percent = std::to_string(truncf(per * 10) / 10);
|
|
UlStatus = Sec("UlDownloading Resource: (") + std::to_string(Pos) + "/" + std::to_string(Amount) +
|
|
"): " + a.substr(a.find_last_of('/')) + " (" +
|
|
Percent.substr(0, Percent.find('.') + 2) + "%)";
|
|
delete[] Data;
|
|
} while (Recv != Size && Recv < Size && !Terminate);
|
|
if(Terminate)break;
|
|
UlStatus = Sec("UlLoading Resource: (") + std::to_string(Pos) + "/" + std::to_string(Amount) +
|
|
"): " + a.substr(a.find_last_of('/'));
|
|
std::ofstream LFS;
|
|
LFS.open(a.c_str(), std::ios_base::app | std::ios::binary);
|
|
if (LFS.is_open()) {
|
|
LFS.write(File, Recv);
|
|
LFS.close();
|
|
}
|
|
delete[] File;
|
|
}while(fs::file_size(a) != std::stoi(*FS) && !Terminate);
|
|
if(!Terminate)fs::copy_file(a,Sec("BeamNG/mods")+a.substr(a.find_last_of('/')), fs::copy_options::overwrite_existing);
|
|
WaitForConfirm();
|
|
}
|
|
FNames.clear();
|
|
FSizes.clear();
|
|
a.clear();
|
|
if(!Terminate){
|
|
STCPSend(Sock,Sec("Done"));
|
|
info(Sec("Done!"));
|
|
}else{
|
|
UlStatus = Sec("Ulstart");
|
|
info(Sec("Connection Terminated!"));
|
|
}
|
|
} |