You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

181 lines
5.7 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using IPA.JsonConverters;
using IPA.Utilities;
using SemVer;
using Version = SemVer.Version;
namespace IPA.Updating.BeatMods
{
#if BeatSaber
class ApiEndpoint
{
public const string BeatModBase = "https://beatmods.com";
public const string ApiBase = BeatModBase + "/api/v1/mod";
public const string GetModInfoEndpoint = "?name={0}&version={1}";
public const string GetModsByName = "?name={0}";
class HexArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(byte[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
try
{
return Utils.StringToByteArray((string)reader.Value);
}
catch (Exception ex)
{
throw new Exception($"Error parsing version string: {reader.Value}", ex);
}
}
throw new Exception(
$"Unexpected token or value when parsing hex string. Token: {reader.TokenType}, Value: {reader.Value}");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
}
else
{
if (!(value is byte[]))
{
throw new JsonSerializationException("Expected byte[] object value");
}
writer.WriteValue(Utils.ByteArrayToString((byte[]) value));
}
}
}
class ModMultiformatJsonConverter : JsonConverter<Mod>
{
public override Mod ReadJson(JsonReader reader, Type objectType, Mod existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
return new Mod { Id = reader.Value as string, IsIdReference = true };
else
{
if (reader.TokenType != JsonToken.StartObject)
return null;
return serializer.Deserialize<Mod>(reader);
}
}
public override void WriteJson(JsonWriter writer, Mod value, JsonSerializer serializer) => serializer.Serialize(writer, value);
}
[Serializable]
public class Mod
{
#pragma warning disable CS0649
/// <summary>
/// Will be a useless string of characters. Do not use.
/// </summary>
[JsonProperty("_id")]
public string Id;
[JsonIgnore]
public bool IsIdReference = false;
[JsonProperty("required")]
public bool Required;
[JsonProperty("name")]
public string Name;
[JsonProperty("version"),
JsonConverter(typeof(SemverVersionConverter))]
public Version Version;
[JsonProperty("gameVersion"),
JsonConverter(typeof(JsonConverters.AlmostVersionConverter))]
public AlmostVersion GameVersion;
[Serializable]
public class AuthorType
{
[JsonProperty("username")]
public string Name;
[JsonProperty("_id")]
public string Id;
public override string ToString() => Name;
}
[JsonProperty("author")]
public AuthorType Author;
[JsonProperty("status")]
public string Status;
public const string ApprovedStatus = "approved";
[JsonProperty("description")]
public string Description;
[JsonProperty("category")]
public string Category;
[JsonProperty("link")]
public Uri Link;
#pragma warning restore CS0649
[Serializable]
public class DownloadsObject
{
public const string TypeUniversal = "universal";
public const string TypeSteam = "steam";
public const string TypeOculus = "oculus";
[JsonProperty("type")]
public string Type;
[JsonProperty("url")]
public string Path;
[Serializable]
public class HashObject
{
[JsonProperty("hash"), JsonConverter(typeof(HexArrayConverter))]
public byte[] Hash;
[JsonProperty("file")]
public string File;
}
/// <summary>
/// Hashes stored are MD5
/// </summary>
[JsonProperty("hashMd5")]
public HashObject[] Hashes;
}
[JsonProperty("downloads")]
public DownloadsObject[] Downloads;
[JsonProperty("dependencies", ItemConverterType = typeof(ModMultiformatJsonConverter))]
public Mod[] Dependencies;
public override string ToString()
{
return $"{{\"{Name}\"v{Version} by {Author} files for {string.Join(", ", Downloads.Select(d => d.Type))}}}";
}
}
}
#endif
}