using System; using System.Collections.Generic; using System.IO; using System.Linq; using Compression.BSA; using Newtonsoft.Json; using Wabbajack.Common; using Wabbajack.Common.Serialization.Json; using Wabbajack.Lib.Downloaders; using Wabbajack.VirtualFileSystem; namespace Wabbajack.Lib { public class RawSourceFile { public readonly RelativePath Path; public RawSourceFile(VirtualFile file, RelativePath path) { File = file; Path = path; } public AbsolutePath AbsolutePath { get { if (!File.IsNative) throw new InvalidDataException("Can't get the absolute path of a non-native file"); return File.FullPath.Base; } } public VirtualFile File { get; } public Hash Hash => File.Hash; public T EvolveTo<T>() where T : Directive, new() { var v = new T(); v.To = Path; v.Hash = Hash; v.Size = File.Size; return v; } } [JsonName("ModList")] public class ModList { /// <summary> /// Archives required by this modlist /// </summary> public List<Archive> Archives = new List<Archive>(); /// <summary> /// Author of the ModList /// </summary> public string Author = string.Empty; /// <summary> /// Description of the ModList /// </summary> public string Description = string.Empty; /// <summary> /// Install directives /// </summary> public List<Directive> Directives = new List<Directive>(); /// <summary> /// The game variant to which this game applies /// </summary> public Game GameType; /// <summary> /// Hash of the banner-image /// </summary> public RelativePath Image; /// <summary> /// The Mod Manager used to create the modlist /// </summary> public ModManager ModManager; /// <summary> /// Name of the ModList /// </summary> public string Name = string.Empty; /// <summary> /// URL to the readme /// </summary> public string Readme = string.Empty; /// <summary> /// The build version of Wabbajack used when compiling the Modlist /// </summary> public Version? WabbajackVersion; /// <summary> /// Website of the ModList /// </summary> public Uri? Website; /// <summary> /// Current Version of the Modlist /// </summary> public Version Version = new Version(1, 0, 0, 0); /// <summary> /// Whether the Modlist is NSFW or not /// </summary> public bool IsNSFW; /// <summary> /// The size of all the archives once they're downloaded /// </summary> [JsonIgnore] public long DownloadSize => Archives.Sum(a => a.Size); /// <summary> /// The size of all the files once they are installed (excluding downloaded archives) /// </summary> [JsonIgnore] public long InstallSize => Directives.Sum(s => s.Size); public ModList Clone() { using var ms = new MemoryStream(); this.ToJson(ms); ms.Position = 0; return ms.FromJson<ModList>(); } } public abstract class Directive { public Hash Hash { get; set; } public long Size { get; set; } /// <summary> /// location the file will be copied to, relative to the install path. /// </summary> public RelativePath To { get; set; } } public class IgnoredDirectly : Directive { public string Reason = string.Empty; } public class NoMatch : IgnoredDirectly { } [JsonName("InlineFile")] public class InlineFile : Directive { /// <summary> /// Data that will be written as-is to the destination location; /// </summary> public RelativePath SourceDataID { get; set; } } [JsonName("ArchiveMeta")] public class ArchiveMeta : Directive { public RelativePath SourceDataID { get; set; } } public enum PropertyType { Banner, Readme } /// <summary> /// File meant to be extracted before the installation /// </summary> [JsonName("PropertyFile")] public class PropertyFile : InlineFile { public PropertyType Type; } [JsonName("CleanedESM")] public class CleanedESM : InlineFile { public Hash SourceESMHash; } /// <summary> /// A file that has the game and MO2 folders remapped on installation /// </summary> [JsonName("RemappedInlineFile")] public class RemappedInlineFile : InlineFile { } [JsonName("SteamMeta")] public class SteamMeta : ArchiveMeta { public int ItemID { get; set; } } [JsonName("FromArchive")] public class FromArchive : Directive { private string? _fullPath; public HashRelativePath ArchiveHashPath { get; set; } [JsonIgnore] public VirtualFile? FromFile { get; set; } [JsonIgnore] public string FullPath => _fullPath ??= string.Join("|", ArchiveHashPath); } [JsonName("CreateBSA")] public class CreateBSA : Directive { public RelativePath TempID { get; set; } public ArchiveStateObject State { get; } public List<FileStateObject> FileStates { get; set; } = new List<FileStateObject>(); public CreateBSA(ArchiveStateObject state, IEnumerable<FileStateObject>? items = null) { State = state; if (items != null) { FileStates.AddRange(items); } } } [JsonName("PatchedFromArchive")] public class PatchedFromArchive : FromArchive { public Hash FromHash { get; set; } /// <summary> /// The file to apply to the source file to patch it /// </summary> public RelativePath PatchID { get; set; } } [JsonName("SourcePatch")] public class SourcePatch { public Hash Hash { get; set; } public RelativePath RelativePath { get; set; } } [JsonName("MergedPatch")] public class MergedPatch : Directive { public RelativePath PatchID { get; set; } public List<SourcePatch> Sources { get; set; } = new List<SourcePatch>(); } [JsonName("Archive")] public class Archive { /// <summary> /// xxHash64 of the archive /// </summary> public Hash Hash { get; set; } /// <summary> /// Meta INI for the downloaded archive /// </summary> public string? Meta { get; set; } /// <summary> /// Human friendly name of this archive /// </summary> public string Name { get; set; } = string.Empty; public long Size { get; set; } public AbstractDownloadState State { get; } public Archive(AbstractDownloadState state) { State = state; } } public class IndexedArchive { public dynamic? IniData; public string Meta = string.Empty; public string Name = string.Empty; public VirtualFile File { get; } public IndexedArchive(VirtualFile file) { File = file; } } }