using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ManifestGenerator;
///
/// Um arquivo dentro do manifesto: caminho relativo, tamanho e hash MD5.
/// O launcher vai comparar esses três dados com os arquivos que já tem
/// localmente para decidir o que precisa baixar.
///
public class ManifestFileEntry
{
[JsonPropertyName("path")]
public string Path { get; set; } = string.Empty;
[JsonPropertyName("size")]
public long Size { get; set; }
[JsonPropertyName("md5")]
public string Md5 { get; set; } = string.Empty;
}
///
/// O manifesto completo que fica hospedado no servidor (ex: via XAMPP,
/// no mesmo esquema que você já usa em C:\xampp\htdocs\download).
///
public class Manifest
{
[JsonPropertyName("version")]
public string Version { get; set; } = string.Empty;
[JsonPropertyName("generatedAt")]
public string GeneratedAt { get; set; } = string.Empty;
[JsonPropertyName("totalFiles")]
public int TotalFiles { get; set; }
[JsonPropertyName("totalSize")]
public long TotalSize { get; set; }
[JsonPropertyName("files")]
public List Files { get; set; } = new();
}
internal class Program
{
// Arquivos/pastas que nunca devem entrar no manifesto.
private static readonly string[] ExcludedNames =
{
"manifest.json",
".git",
"Thumbs.db",
"desktop.ini"
};
private static int Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine("=== Gerador de Manifesto (Auto-Update) ===\n");
// --- 1. Ler argumentos ou perguntar interativamente ---
string sourceFolder = GetArg(args, "--source") ?? AskFolder("Pasta com os arquivos do jogo/patch");
string outputFile = GetArg(args, "--output") ?? System.IO.Path.Combine(sourceFolder, "manifest.json");
string? version = GetArg(args, "--version");
if (!Directory.Exists(sourceFolder))
{
Console.WriteLine($"[ERRO] Pasta não encontrada: {sourceFolder}");
return 1;
}
version ??= DateTime.UtcNow.ToString("yyyyMMddHHmmss"); // versão automática se não for passada
Console.WriteLine($"Pasta de origem : {sourceFolder}");
Console.WriteLine($"Arquivo de saída: {outputFile}");
Console.WriteLine($"Versão : {version}\n");
// --- 2. Varrer os arquivos recursivamente ---
var allFiles = Directory
.EnumerateFiles(sourceFolder, "*", SearchOption.AllDirectories)
.Where(f => !ExcludedNames.Contains(System.IO.Path.GetFileName(f), StringComparer.OrdinalIgnoreCase))
.ToList();
Console.WriteLine($"Encontrados {allFiles.Count} arquivo(s). Calculando hashes...\n");
var manifest = new Manifest
{
Version = version,
GeneratedAt = DateTime.UtcNow.ToString("o")
};
int processed = 0;
foreach (var fullPath in allFiles)
{
processed++;
string relativePath = System.IO.Path.GetRelativePath(sourceFolder, fullPath)
.Replace('\\', '/'); // sempre usar "/" no manifesto, independente do SO
long size = new FileInfo(fullPath).Length;
string hash = ComputeMd5(fullPath);
manifest.Files.Add(new ManifestFileEntry
{
Path = relativePath,
Size = size,
Md5 = hash
});
manifest.TotalSize += size;
// Barra de progresso simples
Console.Write($"\r[{processed}/{allFiles.Count}] {relativePath.PadRight(60).Substring(0, Math.Min(60, relativePath.Length))}");
}
manifest.TotalFiles = manifest.Files.Count;
// --- 3. Salvar o manifesto em JSON ---
var jsonOptions = new JsonSerializerOptions
{
WriteIndented = true
};
string json = JsonSerializer.Serialize(manifest, jsonOptions);
File.WriteAllText(outputFile, json);
Console.WriteLine("\n\n=== Concluído ===");
Console.WriteLine($"Total de arquivos : {manifest.TotalFiles}");
Console.WriteLine($"Tamanho total : {FormatBytes(manifest.TotalSize)}");
Console.WriteLine($"Manifesto salvo em: {outputFile}");
return 0;
}
private static string ComputeMd5(string filePath)
{
using var md5 = MD5.Create();
using var stream = File.OpenRead(filePath);
byte[] hashBytes = md5.ComputeHash(stream);
return Convert.ToHexString(hashBytes).ToLowerInvariant();
}
private static string? GetArg(string[] args, string name)
{
for (int i = 0; i < args.Length - 1; i++)
{
if (string.Equals(args[i], name, StringComparison.OrdinalIgnoreCase))
return args[i + 1];
}
return null;
}
private static string AskFolder(string prompt)
{
Console.Write($"{prompt}: ");
return Console.ReadLine()?.Trim().Trim('"') ?? string.Empty;
}
private static string FormatBytes(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
double len = bytes;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len /= 1024;
}
return $"{len:0.##} {sizes[order]}";
}
}