Vice City: Multiplayer

VC:MP Discussion => General Discussion => Topic started by: MEGAMIND on Oct 19, 2025, 09:09 AM

Title: VCMP UPDATER URL DOWNLOADER
Post by: MEGAMIND on Oct 19, 2025, 09:09 AM
VCMP UPDATER URL DOWNLOADER a tool that downloads and installs the build version of vcmp, this tool was made due to an unexpected behaviour going on with vcmp official browser / or networks

(https://i.imgur.com/aD5oX9D.jpeg)(https://i.imgur.com/UMJu4xe.jpeg)
Its virus free 0 viruses marked by Virustotal (https://www.virustotal.com/gui/url/d6a79c73a52df76dfff8e0f2cfa7004366a2a814872fcd1bf63e6f2a38616a36?nocache=1)

(https://img.shields.io/github/downloads/MEGAMINDMK/vcmp-updater-url/total.svg?color=tuquoise&label=Downloads&logo=github&logoColor=white&style=for-the-badge) (https://github.com/MEGAMINDMK/vcmp-updater-url/releases/download/v1.0/vcmpupdaterurl.exe)

#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
#include <urlmon.h>
#include <shlobj.h>
#include <fstream>
#include <sstream>
#include <direct.h>

#pragma comment(lib, "urlmon.lib")
#pragma comment(lib, "shell32.lib")

struct VersionInfo {
    std::string version;
    std::string filename;
    std::string build;
};

class VCMPDownloader {
private:
    std::string baseUrl = "https://u04.thijn.ovh";
    std::string baseTargetPath;

public:
    VCMPDownloader() {
        // Get the base target path
        char appDataPath[MAX_PATH];
        if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataPath))) {
            baseTargetPath = std::string(appDataPath) + "\\Vice City Multiplayer\\";
        }
        else {
            baseTargetPath = "C:\\Users\\%username%\\AppData\\Local\\Vice City Multiplayer\\";
        }
    }

    bool directoryExists(const std::string& path) {
        DWORD attrib = GetFileAttributesA(path.c_str());
        return (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY));
    }

    bool createDirectory(const std::string& path) {
        // Create all directories in the path
        std::string current;
        for (size_t i = 0; i < path.length(); i++) {
            current += path[i];
            if (path[i] == '\\' || i == path.length() - 1) {
                if (!directoryExists(current) && current != "C:" && current != "C:\\") {
                    if (!CreateDirectoryA(current.c_str(), NULL)) {
                        if (GetLastError() != ERROR_ALREADY_EXISTS) {
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }

    std::string downloadPage(const std::string& url) {
        std::string tempFile = "temp_page.html";

        // Download the HTML page
        std::cout << "Downloading version information..." << std::endl;
        if (URLDownloadToFileA(NULL, url.c_str(), tempFile.c_str(), 0, NULL) != S_OK) {
            std::cout << "Failed to download page from: " << url << std::endl;
            return "";
        }

        // Read the downloaded file
        std::ifstream file(tempFile);
        if (!file.is_open()) {
            std::cout << "Failed to open temporary file" << std::endl;
            return "";
        }

        std::stringstream buffer;
        buffer << file.rdbuf();
        file.close();

        // Delete temporary file
        DeleteFileA(tempFile.c_str());

        return buffer.str();
    }

    std::vector<VersionInfo> parseVersions(const std::string& html) {
        std::vector<VersionInfo> versions;
        size_t pos = 0;

        while ((pos = html.find("Version <a href=\"/files/", pos)) != std::string::npos) {
            // Extract filename
            size_t href_start = html.find("href=\"", pos) + 6;
            size_t href_end = html.find("\"", href_start);
            if (href_end == std::string::npos) break;

            std::string filepath = html.substr(href_start, href_end - href_start);

            // Extract version
            size_t version_start = html.find(">", href_end) + 1;
            size_t version_end = html.find("<", version_start);
            if (version_end == std::string::npos) break;

            std::string version = html.substr(version_start, version_end - version_start);

            // Extract build from filename
            std::string filename = filepath.substr(filepath.find_last_of("/") + 1);
            std::string build = filename.substr(6, 8); // Extract build ID

            versions.push_back({ version, filename, build });

            pos = version_end;
        }

        return versions;
    }

    bool downloadFile(const std::string& filename) {
        std::string url = baseUrl + "/files/" + filename;
        std::string localPath = filename;

        std::cout << "Downloading: " << filename << std::endl;

        if (URLDownloadToFileA(NULL, url.c_str(), localPath.c_str(), 0, NULL) == S_OK) {
            std::cout << "Download completed: " << filename << std::endl;
            return true;
        }
        else {
            std::cout << "Download failed: " << filename << std::endl;
            return false;
        }
    }

    std::string cleanVersionName(const std::string& version) {
        std::string clean = version;
        // Replace characters that are not valid in folder names
        for (size_t i = 0; i < clean.length(); i++) {
            if (clean[i] == ':' || clean[i] == '\\' || clean[i] == '/' ||
                clean[i] == '*' || clean[i] == '?' || clean[i] == '"' ||
                clean[i] == '<' || clean[i] == '>' || clean[i] == '|') {
                clean[i] = '_';
            }
        }
        return clean;
    }

    bool extractWithWinRAR(const std::string& archivePath, const std::string& version) {
        // Common WinRAR installation paths
        const char* winRARPaths[] = {
            "C:\\Program Files\\WinRAR\\WinRAR.exe",
            "C:\\Program Files (x86)\\WinRAR\\WinRAR.exe",
            "C:\\Program Files\\WinRAR\\Rar.exe",
            "C:\\Program Files (x86)\\WinRAR\\Rar.exe"
        };

        std::string winRARExe;
        bool foundWinRAR = false;

        // Find WinRAR installation
        for (int i = 0; i < 4; i++) {
            if (GetFileAttributesA(winRARPaths[i]) != INVALID_FILE_ATTRIBUTES) {
                winRARExe = winRARPaths[i];
                foundWinRAR = true;
                std::cout << "Found WinRAR at: " << winRARExe << std::endl;
                break;
            }
        }

        if (!foundWinRAR) {
            std::cout << "WinRAR not found in standard locations!" << std::endl;
            return false;
        }

        // Create version-specific folder
        std::string cleanVersion = cleanVersionName(version);
        std::string versionTargetPath = baseTargetPath + cleanVersion + "\\";

        // Create the version directory
        if (!directoryExists(versionTargetPath)) {
            std::cout << "Creating version directory: " << versionTargetPath << std::endl;
            if (!createDirectory(versionTargetPath)) {
                std::cout << "Failed to create version directory!" << std::endl;
                return false;
            }
        }

        // Build WinRAR command
        std::string command = "\"" + winRARExe + "\" x -y -s -ibck \"" + archivePath + "\" \"" + versionTargetPath + "\"";

        std::cout << "Extracting to version folder: " << versionTargetPath << std::endl;
        std::cout << "Command: " << command << std::endl;

        STARTUPINFOA si = { sizeof(si) };
        PROCESS_INFORMATION pi;
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;

        if (CreateProcessA(NULL, (LPSTR)command.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
            WaitForSingleObject(pi.hProcess, INFINITE);

            DWORD exitCode;
            GetExitCodeProcess(pi.hProcess, &exitCode);

            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);

            if (exitCode == 0) {
                std::cout << "WinRAR extraction successful to version folder!" << std::endl;
                return true;
            }
            else {
                std::cout << "WinRAR extraction failed with exit code: " << exitCode << std::endl;
            }
        }
        else {
            std::cout << "Failed to start WinRAR process!" << std::endl;
        }

        return false;
    }

    bool extract7z(const std::string& archivePath, const std::string& version) {
        // Create version-specific folder
        std::string cleanVersion = cleanVersionName(version);
        std::string versionTargetPath = baseTargetPath + cleanVersion + "\\";

        // Create the version directory
        if (!directoryExists(versionTargetPath)) {
            std::cout << "Creating version directory: " << versionTargetPath << std::endl;
            if (!createDirectory(versionTargetPath)) {
                std::cout << "Failed to create version directory!" << std::endl;
                return false;
            }
        }

        // Use 7z.exe to extract
        std::string command = "7z x \"" + archivePath + "\" -o\"" + versionTargetPath + "\" -y";

        std::cout << "Extracting with 7-Zip to version folder: " << versionTargetPath << std::endl;

        STARTUPINFOA si = { sizeof(si) };
        PROCESS_INFORMATION pi;
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;

        if (CreateProcessA(NULL, (LPSTR)command.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
            WaitForSingleObject(pi.hProcess, INFINITE);

            DWORD exitCode;
            GetExitCodeProcess(pi.hProcess, &exitCode);

            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);

            if (exitCode == 0) {
                std::cout << "7-Zip extraction successful to version folder!" << std::endl;
                return true;
            }
        }

        std::cout << "7-Zip extraction failed!" << std::endl;
        return false;
    }

    void showVersions(const std::vector<VersionInfo>& versions) {
        std::cout << "\nAvailable VC:MP Versions:\n";
        std::cout << "==========================\n";

        for (size_t i = 0; i < versions.size(); i++) {
            std::cout << i + 1 << ". " << versions[i].version << " (Build: " << versions[i].build << ")\n";
        }
    }

    void run() {
        std::cout << "VC:MP Downloader and Installer\n";
        std::cout << "==============================\n\n";

        // Create base target directory if it doesn't exist
        if (!directoryExists(baseTargetPath)) {
            std::cout << "Creating base directory: " << baseTargetPath << std::endl;
            if (!createDirectory(baseTargetPath)) {
                std::cout << "Failed to create base directory: " << baseTargetPath << std::endl;
                return;
            }
        }

        // Download and parse the page
        std::string html = downloadPage(baseUrl);

        if (html.empty()) {
            std::cout << "Failed to fetch version information!" << std::endl;
            std::cout << "Press Enter to exit...";
            std::cin.ignore();
            std::cin.get();
            return;
        }

        std::vector<VersionInfo> versions = parseVersions(html);

        if (versions.empty()) {
            std::cout << "No versions found!" << std::endl;
            std::cout << "Press Enter to exit...";
            std::cin.ignore();
            std::cin.get();
            return;
        }

        // Show available versions
        showVersions(versions);

        // Let user choose version
        int choice;
        std::cout << "\nEnter the number of the version to download (1-" << versions.size() << "): ";
        std::cin >> choice;

        if (choice < 1 || choice > versions.size()) {
            std::cout << "Invalid choice!" << std::endl;
            std::cout << "Press Enter to exit...";
            std::cin.ignore();
            std::cin.get();
            return;
        }

        VersionInfo selected = versions[choice - 1];

        std::cout << "\nSelected: " << selected.version << std::endl;
        std::cout << "File: " << selected.filename << std::endl;
        std::cout << "Base Path: " << baseTargetPath << std::endl;

        // Download the file
        if (!downloadFile(selected.filename)) {
            std::cout << "Press Enter to exit...";
            std::cin.ignore();
            std::cin.get();
            return;
        }

        // Extract the file to version-specific folder
        std::cout << "\nExtracting files to version folder..." << std::endl;

        bool extracted = false;

        // Try WinRAR first
        extracted = extractWithWinRAR(selected.filename, selected.version);

        // If WinRAR fails, try 7-Zip
        if (!extracted) {
            std::cout << "Trying 7-Zip extraction..." << std::endl;
            extracted = extract7z(selected.filename, selected.version);
        }

        // Clean up downloaded archive
        if (extracted) {
            std::cout << "Cleaning up..." << std::endl;
            if (DeleteFileA(selected.filename.c_str())) {
                std::cout << "Temporary file removed." << std::endl;
            }
            std::cout << "Installation completed successfully!" << std::endl;
            std::cout << "Version installed to: " << baseTargetPath << cleanVersionName(selected.version) << "\\" << std::endl;
        }
        else {
            std::cout << "Installation failed! Make sure WinRAR or 7-Zip is installed." << std::endl;
            std::cout << "Downloaded file kept as: " << selected.filename << std::endl;
        }

        std::cout << "Press Enter to exit...";
        std::cin.ignore();
        std::cin.get();
    }
};

int main() {
    VCMPDownloader downloader;
    downloader.run();
    return 0;
}

Github repo (https://github.com/MEGAMINDMK/vcmp-updater-url)

(https://i.imgur.com/OP25oD0.jpeg)
How to use it?  (https://i.imgur.com/xEVYEMt.mp4)

Note:
Requires winrar or 7z