Skip to content

Commit 7d87341

Browse files
committed
feat: sar_download_file
This could potentially be unsafe, but it could also automate srconfigs updates, challenge_maplist, fast taunts, containerridesave, vaultsave, tram save, etc. Just be careful with usage, it overwrites! Smartly reads through game directories for existing file to overwrite (even p2common), falls back to main game directory i.e. portal2 / portalreloaded / whatever if file doesn't exist.
1 parent fa99751 commit 7d87341

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

src/Features/ConfigPlus.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "Features/Session.hpp"
33
#include "Modules/Client.hpp"
44
#include "Modules/Engine.hpp"
5+
#include "Modules/FileSystem.hpp"
56

67
#include <cstdlib>
78
#include <cstring>
@@ -11,6 +12,8 @@
1112
#include <vector>
1213
#include <unordered_set>
1314
#include <fstream>
15+
#include <curl/curl.h>
16+
#include <regex>
1417

1518
// Fuck you Windows
1619
#ifdef _WIN32
@@ -52,6 +55,62 @@ static void SavePersistentSvars() {
5255
}
5356
}
5457

58+
CON_COMMAND_F(sar_download_file, "sar_download_file <url> <filepath> [directory] - Downloads a file from a URL and saves it to a path relative to game directory (e.g. portal2)\nIf directory isn't specified or invalid, looks for and overwrites existing file or uses base game directory\n", FCVAR_DONTRECORD) {
59+
if (args.ArgC() < 3 || args.ArgC() > 4 || !args[1][0] || !args[2][0]) {
60+
return console->Print(sar_download_file.ThisPtr()->m_pszHelpString);
61+
}
62+
63+
std::string domain = std::regex_replace(args[1], std::regex("^[htps]*://([^/?:]+)/?.*$"), "$1");
64+
65+
if (!strcmp(domain.c_str(), args[1])) return console->Print("Invalid URL.\n"); // URL is missing protocol. Reject.
66+
67+
if (!Utils::EndsWith(domain, "portal2.sr") && !Utils::StartsWith(args[1], "https://raw.githubusercontent.com/p2sr/")) {
68+
return console->Print("URL domain is not portal2.sr.\n");
69+
}
70+
71+
std::string filepath = args[2];
72+
std::string gamedir = "";
73+
74+
auto paths = fileSystem->GetSearchPaths();
75+
if (args.ArgC() > 3 && args[3][0]) {
76+
for (auto path : paths) {
77+
if (strstr(path.c_str(), args[3]) == NULL) continue;
78+
gamedir = path;
79+
break;
80+
}
81+
}
82+
if (gamedir == "") {
83+
gamedir = std::string(engine->GetGameDirectory()) + "/";
84+
for (auto path : paths) {
85+
if (!std::ifstream(path + filepath).good()) continue;
86+
gamedir = path;
87+
break;
88+
}
89+
}
90+
91+
CURL *curl = curl_easy_init();
92+
FILE *fp;
93+
CURLcode res;
94+
if (curl) {
95+
fp = fopen((gamedir + filepath + ".tmp").c_str(), "wb");
96+
curl_easy_setopt(curl, CURLOPT_URL, args[1]);
97+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
98+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
99+
res = curl_easy_perform(curl);
100+
curl_easy_cleanup(curl);
101+
fclose(fp);
102+
}
103+
104+
if (res == CURLE_OK) {
105+
std::filesystem::remove(gamedir + filepath);
106+
std::filesystem::copy(gamedir + filepath + ".tmp", gamedir + filepath);
107+
} else {
108+
console->Print("An error occurred\n");
109+
}
110+
111+
std::filesystem::remove(gamedir + filepath + ".tmp");
112+
}
113+
55114
static void SetSvar(std::string name, std::string val) {
56115
g_svars[name] = val;
57116
if (g_persistentSvars.count(name) != 0) SavePersistentSvars();

0 commit comments

Comments
 (0)