Files
TinfoilVibeServer/TinfoilVibeServer/Utilities/MultipartRarStream.cs
T
ecenshu 0e2fec8c01
Build & Push Docker image / build-and-push (push) Successful in 14m38s
ci / build_linux (push) Successful in 4m43s
Skip hashed and same location files
Explicit usings
Multipart rar handling
2025-11-23 21:05:58 +10:30

83 lines
2.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace TinfoilVibeServer.Utilities;
/// <summary>
/// Streams a multipart RAR as a single, seekable stream.
/// Supports:
/// • myGame.rar (single volume)
/// • myGame.r00 / r01 … (RAR 4.x style)
/// • myGame.part01.rar … (WinRAR “part” style)
/// </summary>
public static class MultiPartRarHelper
{
public static string GetBaseNameForRarVolume(string fileName)
{
string baseName = string.Empty;
// Multipart remove the ".rNN" or ".partNN" suffix
var m = Regex.Match(fileName,
@"^(?<base>.+?)(\.r\d\d|\.part\d\d)\.rar$",
RegexOptions.IgnoreCase);
if (!m.Success)
{
if (fileName.EndsWith(".rar", StringComparison.OrdinalIgnoreCase))
{
// Singlevolume archive just drop the suffix
baseName = fileName.Substring(0, fileName.Length - 4);
}
}
else
{
baseName = m.Groups["base"].Value;
}
return baseName;
}
/// <summary>
/// Returns the list of files that belong to the same multipart set.
/// </summary>
public static List<string> DiscoverVolumes(string dir, string baseName)
{
// Pattern: <base>.(rar | rNN | partNN.rar)
string pattern =
$@"^{Regex.Escape(baseName)}(\.rar|\.r\d\d|\.part\d\d\.rar)$";
return Directory.GetFiles(dir)
.Where(f => Regex.IsMatch(Path.GetFileName(f), pattern, RegexOptions.IgnoreCase))
.OrderBy(f => GetPartNumber(Path.GetFileName(f), baseName))
.ToList();
}
/// <summary>
/// Gives each file a numeric key that guarantees the correct order.
/// 0 → .rar (first volume)
/// 1 → .r00 or .part01
/// 2 → .r01 or .part02
/// … and so on.
/// </summary>
private static int GetPartNumber(string fileName, string baseName)
{
// 1️⃣ ".rar" → 0
if (fileName.Equals($"{baseName}.rar", StringComparison.OrdinalIgnoreCase))
return 0;
// 2️⃣ ".rNN" or ".partNN.rar"
var m = Regex.Match(fileName,
$@"\.r(?<num>\d\d)$|\.part(?<num>\d\d)\.rar$",
RegexOptions.IgnoreCase);
if (m.Success)
{
int partNum = int.Parse(m.Groups["num"].Value);
return partNum + 1; // so r00 → 1, r01 → 2; part01 → 1, part02 → 2
}
// 3️⃣ unknown pattern treat as first part
return 0;
}
}