|
2 | 2 | #include "Features/Session.hpp" |
3 | 3 | #include "Modules/Client.hpp" |
4 | 4 | #include "Modules/Engine.hpp" |
| 5 | +#include "Modules/FileSystem.hpp" |
5 | 6 |
|
6 | 7 | #include <cstdlib> |
7 | 8 | #include <cstring> |
|
11 | 12 | #include <vector> |
12 | 13 | #include <unordered_set> |
13 | 14 | #include <fstream> |
| 15 | +#include <curl/curl.h> |
| 16 | +#include <regex> |
14 | 17 |
|
15 | 18 | // Fuck you Windows |
16 | 19 | #ifdef _WIN32 |
@@ -52,6 +55,62 @@ static void SavePersistentSvars() { |
52 | 55 | } |
53 | 56 | } |
54 | 57 |
|
| 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 | + |
55 | 114 | static void SetSvar(std::string name, std::string val) { |
56 | 115 | g_svars[name] = val; |
57 | 116 | if (g_persistentSvars.count(name) != 0) SavePersistentSvars(); |
|
0 commit comments