Panel: German/English, redesigned sidebar, in-game admins page, Steam Web API key link, RAR/7z save upload (SharpCompress), responsive cards; upload errors return 400; tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+45
-14
@@ -97,31 +97,62 @@ public static class SaveStore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spielstand aus einer ZIP lesen. Nur die bekannten Spielstand-Dateien werden genommen (egal in welchem Ordner
|
||||
/// der ZIP) - Pfade aus der ZIP werden nie benutzt. Gibt einen Fehler zurück oder null.
|
||||
/// Spielstand aus einem Archiv lesen (ZIP, RAR, 7z, tar ...). Nur die bekannten Spielstand-Dateien werden genommen
|
||||
/// (egal in welchem Ordner im Archiv) - Pfade aus dem Archiv werden nie benutzt. Gibt einen Fehler zurück oder null.
|
||||
/// </summary>
|
||||
public static string ExtractZip(byte[] zip, out Dictionary<string, byte[]> found)
|
||||
public static string ExtractArchive(byte[] data, out Dictionary<string, byte[]> found)
|
||||
{
|
||||
found = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
|
||||
try
|
||||
{
|
||||
using var archive = new ZipArchive(new MemoryStream(zip), ZipArchiveMode.Read);
|
||||
using var archive = SharpCompress.Archives.ArchiveFactory.OpenArchive(new MemoryStream(data), null);
|
||||
long total = 0;
|
||||
foreach (var entry in archive.Entries)
|
||||
var result = found;
|
||||
// Eintrag lesen: nur bekannte Spielstand-Dateien, Größe begrenzt. Gibt einen Fehler zurück oder null.
|
||||
string Take(SharpCompress.Common.IEntry entry, Func<Stream> open)
|
||||
{
|
||||
string file = Path.GetFileName(entry.FullName.Replace('\\', '/'));
|
||||
if (entry.IsDirectory || entry.Key == null) return null;
|
||||
string file = Path.GetFileName(entry.Key.Replace('\\', '/'));
|
||||
string known = SaveFiles.FirstOrDefault(f => string.Equals(f, file, StringComparison.OrdinalIgnoreCase));
|
||||
if (known == null) continue;
|
||||
if (entry.Length > MaxZipBytes || (total += entry.Length) > MaxZipBytes) return "The save game is too large";
|
||||
using var s = entry.Open();
|
||||
if (known == null) return null;
|
||||
if (entry.IsEncrypted) return "The archive is password protected";
|
||||
using var s = open();
|
||||
using var ms = new MemoryStream();
|
||||
s.CopyTo(ms);
|
||||
found[known] = ms.ToArray();
|
||||
var chunk = new byte[81920];
|
||||
int read;
|
||||
while ((read = s.Read(chunk, 0, chunk.Length)) > 0)
|
||||
{
|
||||
total += read;
|
||||
if (total > MaxZipBytes) return "The save game is too large";
|
||||
ms.Write(chunk, 0, read);
|
||||
}
|
||||
result[known] = ms.ToArray();
|
||||
return null;
|
||||
}
|
||||
|
||||
string error = null;
|
||||
if (archive.IsSolid || archive.Type == SharpCompress.Common.ArchiveType.SevenZip)
|
||||
{
|
||||
// "Solide" RAR/7z: nur der Reihe nach lesbar
|
||||
using var reader = archive.ExtractAllEntries();
|
||||
while (error == null && reader.MoveToNextEntry()) error = Take(reader.Entry, reader.OpenEntryStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
error = Take(entry, entry.OpenEntryStream);
|
||||
if (error != null) break;
|
||||
}
|
||||
}
|
||||
if (error != null) return error;
|
||||
}
|
||||
catch (Exception e) when (e is not OutOfMemoryException)
|
||||
{
|
||||
return "This file could not be read - upload a ZIP, RAR or 7z archive (" + e.Message + ")";
|
||||
}
|
||||
catch (InvalidDataException) { return "This is not a valid ZIP file"; }
|
||||
if (!found.ContainsKey("defaultES2File.txt"))
|
||||
return "No defaultES2File.txt in the ZIP - zip the files from %USERPROFILE%\\AppData\\LocalLow\\Amistech\\My Summer Car";
|
||||
return "No defaultES2File.txt in the archive - pack the files from %USERPROFILE%\\AppData\\LocalLow\\Amistech\\My Summer Car";
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -145,7 +176,7 @@ public static class SaveStore
|
||||
/// <summary>Server aus: ZIP als neuen Spielstand übernehmen (Backup vorher, neuer Reset-Punkt, Weltzustand leer).</summary>
|
||||
public static string ImportZipOffline(string dataDir, byte[] zip, string by)
|
||||
{
|
||||
string error = ExtractZip(zip, out var files);
|
||||
string error = ExtractArchive(zip, out var files);
|
||||
if (error != null) return error;
|
||||
try
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user