using System.Security.Cryptography;
namespace XFEWarframe.Client;
public static class ClientProtocol
{
public static string BuildIdentity(string buildLabel, string cacheManifestHash)
{
if (string.IsNullOrWhiteSpace(buildLabel) || buildLabel.Contains('/') || buildLabel.Any(char.IsControl))
throw new InvalidDataException("Invalid game build label.");
if (cacheManifestHash.Length is < 16 or > 128 || !cacheManifestHash.All(c => char.IsAsciiLetterOrDigit(c) || c is '-' or '_' or '+' or '='))
throw new InvalidDataException("The game cache manifest hash is unavailable or invalid.");
return buildLabel + "/" + cacheManifestHash;
}
public static string NormalizeServer(string value)
{
if (!Uri.TryCreate(value.Trim(), UriKind.Absolute, out var uri) || uri.Scheme is not ("http" or "https") ||
uri.UserInfo.Length != 0 || uri.AbsolutePath != "/" || uri.Query.Length != 0 || uri.Fragment.Length != 0)
throw new ArgumentException("服务器地址必须为 http(s)://主机:端口,不包含账号、路径或查询参数。");
return uri.GetLeftPart(UriPartial.Authority);
}
public static string Redirect(string original, string origin, string buildLabel)
{
if (!Uri.TryCreate(original, UriKind.Absolute, out var uri) || uri.Scheme is not ("http" or "https"))
throw new InvalidDataException("游戏请求不是有效的 HTTP URL。");
var url = origin + uri.PathAndQuery;
var separator = uri.Query.Length == 0 ? '?' : '&';
if (uri.AbsolutePath.EndsWith("/login.php", StringComparison.OrdinalIgnoreCase) ||
uri.AbsolutePath.EndsWith("/worldState.php", StringComparison.OrdinalIgnoreCase))
// The game's build identity is version/manifest-hash. A slash is valid
// query data; keep it literal like the original client bridge because
// ServerInteractive 4.0.1 rejects encoded slashes even in the query.
url += separator + "buildLabel=" + Uri.EscapeDataString(buildLabel).Replace("%2F", "/", StringComparison.Ordinal) + "&clientMod=XFEWarframe";
else if (uri.AbsolutePath.EndsWith("/inventory.php", StringComparison.OrdinalIgnoreCase) ||
uri.AbsolutePath.EndsWith("/missionInventoryUpdate.php", StringComparison.OrdinalIgnoreCase))
url += separator + "xpBasedLevelCapDisabled=1";
return url;
}
public static byte[] DecodeProtected(ReadOnlySpan<byte> body)
{
if (body.Length <= 137 || (body.Length - 137) % 16 != 0)
throw new InvalidDataException("受保护请求长度无效。");
using var aes = Aes.Create();
aes.Key = Enumerable.Repeat((byte)'A', 24).ToArray();
return aes.DecryptCbc(body[137..], Enumerable.Repeat((byte)'A', 16).ToArray(), PaddingMode.PKCS7);
}
}
using System.Security.Cryptography;
namespace XFEWarframe.Client;
public static class ClientProtocol
{
public static string BuildIdentity(string buildLabel, string cacheManifestHash)
{
if (string.IsNullOrWhiteSpace(buildLabel) || buildLabel.Contains('/') || buildLabel.Any(char.IsControl))
throw new InvalidDataException("Invalid game build label.");
if (cacheManifestHash.Length is < 16 or > 128 || !cacheManifestHash.All(c => char.IsAsciiLetterOrDigit(c) || c is '-' or '_' or '+' or '='))
throw new InvalidDataException("The game cache manifest hash is unavailable or invalid.");
return buildLabel + "/" + cacheManifestHash;
}
public static string NormalizeServer(string value)
{
if (!Uri.TryCreate(value.Trim(), UriKind.Absolute, out var uri) || uri.Scheme is not ("http" or "https") ||
uri.UserInfo.Length != 0 || uri.AbsolutePath != "/" || uri.Query.Length != 0 || uri.Fragment.Length != 0)
throw new ArgumentException("服务器地址必须为 http(s)://主机:端口,不包含账号、路径或查询参数。");
return uri.GetLeftPart(UriPartial.Authority);
}
public static string Redirect(string original, string origin, string buildLabel)
{
if (!Uri.TryCreate(original, UriKind.Absolute, out var uri) || uri.Scheme is not ("http" or "https"))
throw new InvalidDataException("游戏请求不是有效的 HTTP URL。");
var url = origin + uri.PathAndQuery;
var separator = uri.Query.Length == 0 ? '?' : '&';
if (uri.AbsolutePath.EndsWith("/login.php", StringComparison.OrdinalIgnoreCase) ||
uri.AbsolutePath.EndsWith("/worldState.php", StringComparison.OrdinalIgnoreCase))
// The game's build identity is version/manifest-hash. A slash is valid
// query data; keep it literal like the original client bridge because
// ServerInteractive 4.0.1 rejects encoded slashes even in the query.
url += separator + "buildLabel=" + Uri.EscapeDataString(buildLabel).Replace("%2F", "/", StringComparison.Ordinal) + "&clientMod=XFEWarframe";
else if (uri.AbsolutePath.EndsWith("/inventory.php", StringComparison.OrdinalIgnoreCase) ||
uri.AbsolutePath.EndsWith("/missionInventoryUpdate.php", StringComparison.OrdinalIgnoreCase))
url += separator + "xpBasedLevelCapDisabled=1";
return url;
}
public static byte[] DecodeProtected(ReadOnlySpan<byte> body)
{
if (body.Length <= 137 || (body.Length - 137) % 16 != 0)
throw new InvalidDataException("受保护请求长度无效。");
using var aes = Aes.Create();
aes.Key = Enumerable.Repeat((byte)'A', 24).ToArray();
return aes.DecryptCbc(body[137..], Enumerable.Repeat((byte)'A', 16).ToArray(), PaddingMode.PKCS7);
}
}