Загрузка данных
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace AlexSelectLauncher;
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
public sealed class MainForm : Form
{
private readonly TextBox keyBox = new();
private readonly Label status = new();
private readonly Label code = new();
private readonly Button activate = new();
private readonly Button install = new();
private const string ApiBase = "http://localhost:5255/api/activation";
public MainForm()
{
Text = "ALEX SELECT — MOD BY ALEX";
Width = 620;
Height = 420;
StartPosition = FormStartPosition.CenterScreen;
BackColor = Color.FromArgb(16, 12, 24);
ForeColor = Color.White;
Font = new Font("Segoe UI", 10);
var title = new Label
{
Text = "ALEX SELECT",
Font = new Font("Segoe UI", 28, FontStyle.Bold),
AutoSize = true,
Location = new Point(35, 30)
};
var subtitle = new Label
{
Text = "GTA V • INSTALLER & LICENSE",
AutoSize = true,
Location = new Point(40, 82),
ForeColor = Color.Plum
};
var keyLabel = new Label
{
Text = "Ключ активации",
AutoSize = true,
Location = new Point(40, 125)
};
keyBox.Location = new Point(40, 153);
keyBox.Width = 520;
keyBox.PlaceholderText = "XXXX-XXXX-XXXX";
activate.Text = "АКТИВИРОВАТЬ";
activate.Location = new Point(40, 200);
activate.Width = 250;
activate.Height = 42;
activate.Click += async (_, _) => await ActivateAsync();
install.Text = "УСТАНОВИТЬ";
install.Location = new Point(310, 200);
install.Width = 250;
install.Height = 42;
install.Enabled = false;
install.Click += async (_, _) => await DownloadFilesAsync();
status.Text = "Статус: не активирован";
status.AutoSize = true;
status.Location = new Point(40, 270);
code.Text = "ONLINE CODE: —";
code.AutoSize = true;
code.Location = new Point(40, 305);
code.Font = new Font("Consolas", 12, FontStyle.Bold);
Controls.AddRange(
[
title,
subtitle,
keyLabel,
keyBox,
activate,
install,
status,
code
]);
}
private async Task ActivateAsync()
{
var key = keyBox.Text.Trim();
if (string.IsNullOrWhiteSpace(key))
{
status.Text = "Статус: введите ключ";
return;
}
activate.Enabled = false;
status.Text = "Статус: проверяем ключ онлайн…";
try
{
using var http = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
var machineId = GetMachineId();
var response = await http.PostAsJsonAsync(
$"{ApiBase}/activate",
new
{
key,
machine_id = machineId
});
if (!response.IsSuccessStatusCode)
{
status.Text = "Статус: ключ недействителен или заблокирован";
return;
}
var data =
await response.Content.ReadFromJsonAsync<ActivationResponse>();
if (data is null || !data.active)
{
status.Text = "Статус: активация отклонена";
return;
}
status.Text =
$"Статус: АКТИВИРОВАН • версия {data.version}";
code.Text =
$"ONLINE CODE: {data.online_code}";
install.Enabled = true;
}
catch
{
status.Text = "Статус: нет связи с сервером";
}
finally
{
activate.Enabled = true;
}
}
private async Task DownloadFilesAsync()
{
using var dialog = new FolderBrowserDialog
{
Description = "Выберите папку для файлов ALEX SELECT"
};
if (dialog.ShowDialog() != DialogResult.OK)
return;
var folder = dialog.SelectedPath;
const string publicUrl =
"https://cloud.mail.ru/public/Aeig/Mi6K8oZZz/Setup%20GPF%20v2.2.part1.exe";
var filePath =
Path.Combine(folder, "Setup GPF v2.2.part1.exe");
install.Enabled = false;
status.Text = "Статус: получаем данные Mail.ru…";
try
{
using var http = new HttpClient
{
Timeout = TimeSpan.FromMinutes(30)
};
var pageResponse = await http.GetAsync(publicUrl);
pageResponse.EnsureSuccessStatusCode();
var page = await pageResponse.Content.ReadAsStringAsync();
string? directUrl = null;
try
{
using var json = JsonDocument.Parse(page);
directUrl = FindDownloadUrl(json.RootElement);
}
catch
{
// Если страница не является JSON,
// ниже покажем понятную ошибку.
}
if (string.IsNullOrWhiteSpace(directUrl))
{
throw new Exception(
"Mail.ru не передал прямую ссылку на файл. " +
"Получена страница Cloud Mail.ru вместо файла.");
}
status.Text = "Статус: скачиваем настоящий файл…";
using var response = await http.GetAsync(
directUrl,
HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var totalBytes =
response.Content.Headers.ContentLength;
await using var input =
await response.Content.ReadAsStreamAsync();
await using var output =
File.Create(filePath);
var buffer = new byte[1024 * 1024];
long downloaded = 0;
int read;
while ((read = await input.ReadAsync(buffer)) > 0)
{
await output.WriteAsync(buffer.AsMemory(0, read));
downloaded += read;
if (totalBytes.HasValue && totalBytes.Value > 0)
{
var percent =
downloaded * 100 / totalBytes.Value;
var downloadedMb =
downloaded / 1024.0 / 1024.0;
var totalMb =
totalBytes.Value / 1024.0 / 1024.0;
status.Text =
$"Статус: скачивание {percent}% " +
$"({downloadedMb:F0} / {totalMb:F0} МБ)";
}
}
var finalSize =
new FileInfo(filePath).Length;
status.Text = "Статус: файл скачан ✅";
MessageBox.Show(
$"Файл успешно скачан!\n\n" +
$"Размер: {finalSize / 1024.0 / 1024.0:F1} МБ\n\n" +
$"Путь:\n{filePath}",
"ALEX SELECT",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
status.Text = "Статус: ошибка скачивания";
MessageBox.Show(
$"Не удалось скачать файл.\n\n{ex.Message}",
"ALEX SELECT",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
finally
{
install.Enabled = true;
}
}
private static string? FindDownloadUrl(JsonElement element)
{
if (element.ValueKind == JsonValueKind.Object)
{
foreach (var property in element.EnumerateObject())
{
if (property.Name.Equals(
"url",
StringComparison.OrdinalIgnoreCase))
{
if (property.Value.ValueKind == JsonValueKind.String)
{
var value = property.Value.GetString();
if (!string.IsNullOrWhiteSpace(value) &&
value.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
return value;
}
}
}
var result = FindDownloadUrl(property.Value);
if (!string.IsNullOrWhiteSpace(result))
return result;
}
}
else if (element.ValueKind == JsonValueKind.Array)
{
foreach (var item in element.EnumerateArray())
{
var result = FindDownloadUrl(item);
if (!string.IsNullOrWhiteSpace(result))
return result;
}
}
return null;
}
private static string GetMachineId()
{
var raw =
$"{Environment.MachineName}|{Environment.UserName}|AlexSelect";
return Convert.ToHexString(
SHA256.HashData(
Encoding.UTF8.GetBytes(raw)))[..32];
}
private sealed record ActivationResponse(
bool active,
string version,
string online_code);
}
private const string ApiBase = "http://localhost:5255/api/activation";
public MainForm()
{
Text = "ALEX SELECT — MOD BY ALEX";
Width = 620;
Height = 420;
StartPosition = FormStartPosition.CenterScreen;
BackColor = Color.FromArgb(16, 12, 24);
ForeColor = Color.White;
Font = new Font("Segoe UI", 10);
var title = new Label
{
Text = "ALEX SELECT",
Font = new Font("Segoe UI", 28, FontStyle.Bold),
AutoSize = true,
Location = new Point(35, 30)
};
var subtitle = new Label
{
Text = "GTA V • INSTALLER & LICENSE",
AutoSize = true,
Location = new Point(40, 82),
ForeColor = Color.Plum
};
var keyLabel = new Label
{
Text = "Ключ активации",
AutoSize = true,
Location = new Point(40, 125)
};
keyBox.Location = new Point(40, 153);
keyBox.Width = 520;
keyBox.PlaceholderText = "XXXX-XXXX-XXXX";
activate.Text = "АКТИВИРОВАТЬ";
activate.Location = new Point(40, 200);
activate.Width = 250;
activate.Height = 42;
activate.Click += async (_, _) => await ActivateAsync();
install.Text = "УСТАНОВИТЬ";
install.Location = new Point(310, 200);
install.Width = 250;
install.Height = 42;
install.Enabled = false;
install.Click += async (_, _) => await DownloadFilesAsync();
status.Text = "Статус: не активирован";
status.AutoSize = true;
status.Location = new Point(40, 270);
code.Text = "ONLINE CODE: —";
code.AutoSize = true;
code.Location = new Point(40, 305);
code.Font = new Font("Consolas", 12, FontStyle.Bold);
Controls.AddRange(
[
title,
subtitle,
keyLabel,
keyBox,
activate,
install,
status,
code
]);
}
private async Task ActivateAsync()
{
var key = keyBox.Text.Trim();
if (string.IsNullOrWhiteSpace(key))
{
status.Text = "Статус: введите ключ";
return;
}
activate.Enabled = false;
status.Text = "Статус: проверяем ключ онлайн…";
try
{
using var http = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
var machineId = GetMachineId();
var response = await http.PostAsJsonAsync(
$"{ApiBase}/activate",
new
{
key,
machine_id = machineId
});
if (!response.IsSuccessStatusCode)
{
status.Text = "Статус: ключ недействителен или заблокирован";
return;
}
var data =
await response.Content.ReadFromJsonAsync<ActivationResponse>();
if (data is null || !data.active)
{
status.Text = "Статус: активация отклонена";
return;
}
status.Text =
$"Статус: АКТИВИРОВАН • версия {data.version}";
code.Text =
$"ONLINE CODE: {data.online_code}";
install.Enabled = true;
}
catch
{
status.Text = "Статус: нет связи с сервером";
}
finally
{
activate.Enabled = true;
}
}
private async Task DownloadFilesAsync()
{
using var dialog = new FolderBrowserDialog
{
Description = "Выберите папку для файлов ALEX SELECT"
};
if (dialog.ShowDialog() != DialogResult.OK)
return;
var folder = dialog.SelectedPath;
const string publicUrl =
"https://cloud.mail.ru/public/Aeig/Mi6K8oZZz/Setup%20GPF%20v2.2.part1.exe";
var filePath =
Path.Combine(folder, "Setup GPF v2.2.part1.exe");
install.Enabled = false;
status.Text = "Статус: получаем данные Mail.ru…";
try
{
using var http = new HttpClient
{
Timeout = TimeSpan.FromMinutes(30)
};
var pageResponse = await http.GetAsync(publicUrl);
pageResponse.EnsureSuccessStatusCode();
var page = await pageResponse.Content.ReadAsStringAsync();
string? directUrl = null;
try
{
using var json = JsonDocument.Parse(page);
directUrl = FindDownloadUrl(json.RootElement);
}
catch
{
// Если страница не является JSON,
// ниже покажем понятную ошибку.
}
if (string.IsNullOrWhiteSpace(directUrl))
{
throw new Exception(
"Mail.ru не передал прямую ссылку на файл. " +
"Получена страница Cloud Mail.ru вместо файла.");
}
status.Text = "Статус: скачиваем настоящий файл…";
using var response = await http.GetAsync(
directUrl,
HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var totalBytes =
response.Content.Headers.ContentLength;
await using var input =
await response.Content.ReadAsStreamAsync();
await using var output =
File.Create(filePath);
var buffer = new byte[1024 * 1024];
long downloaded = 0;
int read;
while ((read = await input.ReadAsync(buffer)) > 0)
{
await output.WriteAsync(buffer.AsMemory(0, read));
downloaded += read;
if (totalBytes.HasValue && totalBytes.Value > 0)
{
var percent =
downloaded * 100 / totalBytes.Value;
var downloadedMb =
downloaded / 1024.0 / 1024.0;
var totalMb =
totalBytes.Value / 1024.0 / 1024.0;
status.Text =
$"Статус: скачивание {percent}% " +
$"({downloadedMb:F0} / {totalMb:F0} МБ)";
}
}
var finalSize =
new FileInfo(filePath).Length;
status.Text = "Статус: файл скачан ✅";
MessageBox.Show(
$"Файл успешно скачан!\n\n" +
$"Размер: {finalSize / 1024.0 / 1024.0:F1} МБ\n\n" +
$"Путь:\n{filePath}",
"ALEX SELECT",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
status.Text = "Статус: ошибка скачивания";
MessageBox.Show(
$"Не удалось скачать файл.\n\n{ex.Message}",
"ALEX SELECT",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
finally
{
install.Enabled = true;
}
}
private static string? FindDownloadUrl(JsonElement element)
{
if (element.ValueKind == JsonValueKind.Object)
{
foreach (var property in element.EnumerateObject())
{
if (property.Name.Equals(
"url",
StringComparison.OrdinalIgnoreCase))
{
if (property.Value.ValueKind == JsonValueKind.String)
{
var value = property.Value.GetString();
if (!string.IsNullOrWhiteSpace(value) &&
value.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
return value;
}
}
}
var result = FindDownloadUrl(property.Value);
if (!string.IsNullOrWhiteSpace(result))
return result;
}
}
else if (element.ValueKind == JsonValueKind.Array)
{
foreach (var item in element.EnumerateArray())
{
var result = FindDownloadUrl(item);
if (!string.IsNullOrWhiteSpace(result))
return result;
}
}
return null;
}
private static string GetMachineId()
{
var raw =
$"{Environment.MachineName}|{Environment.UserName}|AlexSelect";
return Convert.ToHexString(
SHA256.HashData(
Encoding.UTF8.GetBytes(raw)))[..32];
}
private sealed record ActivationResponse(
bool active,
string version,
string online_code);