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() where T : Directive, new() { var v = new T(); v.To = Path; v.Hash = File.Hash; v.Size = File.Size; return v; } } [JsonName("ModList")] public class ModList { /// /// Archives required by this modlist /// public List Archives = new List(); /// /// Author of the ModList /// public string Author = string.Empty; /// /// Description of the ModList /// public string Description = string.Empty; /// /// Install directives /// public List Directives = new List(); /// /// The game variant to which this game applies /// public Game GameType; /// /// Hash of the banner-image /// public RelativePath Image; /// /// The Mod Manager used to create the modlist /// public ModManager ModManager; /// /// Name of the ModList /// public string Name = string.Empty; /// /// URL to the readme /// public string Readme = string.Empty; /// /// The build version of Wabbajack used when compiling the Modlist /// public Version? WabbajackVersion; /// /// Website of the ModList /// public Uri? Website; /// /// Current Version of the Modlist /// public Version Version = new Version(1, 0, 0, 0); /// /// Whether the Modlist is NSFW or not /// public bool IsNSFW; /// /// The size of all the archives once they're downloaded /// [JsonIgnore] public long DownloadSize => Archives.Sum(a => a.Size); /// /// The size of all the files once they are installed (excluding downloaded archives) /// [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(); } } public abstract class Directive { public Hash Hash { get; set; } public long Size { get; set; } /// /// location the file will be copied to, relative to the install path. /// public RelativePath To { get; set; } } public class IgnoredDirectly : Directive { public string Reason = string.Empty; } public class NoMatch : IgnoredDirectly { } [JsonName("InlineFile")] public class InlineFile : Directive { /// /// Data that will be written as-is to the destination location; /// public RelativePath SourceDataID { get; set; } [JsonIgnore] public VirtualFile? SourceDataFile { get; set; } } [JsonName("ArchiveMeta")] public class ArchiveMeta : Directive { public RelativePath SourceDataID { get; set; } } public enum PropertyType { Banner, Readme } /// /// File meant to be extracted before the installation /// [JsonName("PropertyFile")] public class PropertyFile : InlineFile { public PropertyType Type; } [JsonName("CleanedESM")] public class CleanedESM : InlineFile { public Hash SourceESMHash; } /// /// A file that has the game and MO2 folders remapped on installation /// [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 FileStates { get; set; } = new List(); public CreateBSA(ArchiveStateObject state, IEnumerable? items = null) { State = state; if (items != null) { FileStates.AddRange(items); } } } [JsonName("PatchedFromArchive")] public class PatchedFromArchive : FromArchive { public Hash FromHash { get; set; } /// /// The file to apply to the source file to patch it /// public RelativePath PatchID { get; set; } /// /// During compilation this holds several possible files that could be used as a patch source. At the end /// of compilation we'll go through all of these and find the smallest patch file. /// [JsonIgnore] public VirtualFile[] Choices { 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 Sources { get; set; } = new List(); } [JsonName("Archive")] public class Archive { /// /// xxHash64 of the archive /// public Hash Hash { get; set; } /// /// Meta INI for the downloaded archive /// public string? Meta { get; set; } /// /// Human friendly name of this archive /// 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 AbstractDownloadState? State { get; set; } public IndexedArchive(VirtualFile file) { File = file; } } }