private async Task <bool> WriteJSON(string meta_data_path, ArchiveContents ac)
 {
     using (var file = new StreamWriter(File.OpenWrite(meta_data_path)))
     {
         await file.WriteAsync(JsonConvert.SerializeObject(ac, new JsonSerializerSettings()
         {
             Formatting = Formatting.Indented
         }));
     }
     return(true);
 }
        private async Task <IEnumerable <ArchiveContents> > GetDownloadMetadata(IProgress <string> progress)
        {
            var ini_files = Directory.EnumerateFiles(_transcompilerBase.MODirectory, "meta.ini", SearchOption.AllDirectories)
                            .AsParallel()
                            .Select(f => (new FileIniDataParser()).ReadFile(f))
                            .ToList();

            var archives = Directory.EnumerateFiles(_transcompilerBase.DownloadsDirectoryPath, "*.*");

            var tasks = archives.AsParallel()
                        .Select(async archive => {
                if (!SUPPORTED_ARCHIVES.Contains(Path.GetExtension(archive).ToLower()))
                {
                    return;
                }
                var meta_data_path = Path.Combine(_transcompilerBase.DownloadsDirectoryPath, archive) + METADATA_EXTENSION;

                if (File.Exists(meta_data_path))
                {
                    return;
                }
                Update(progress, "[META] Generating Metadata for: ", Path.GetFileName(archive));


                using (var archive_file = new ArchiveFile(archive))
                {
                    // Extract the contents  of the file and attach each to a hashing stream
                    var streams = new Dictionary <string, HashingStream>();
                    archive_file.Extract(e =>
                    {
                        if (e.IsFolder)
                        {
                            return(null);
                        }
                        if (streams.ContainsKey(e.FileName))
                        {
                            return(streams[e.FileName]);
                        }

                        var stream = new HashingStream(e.FileName);
                        streams.Add(e.FileName, stream);
                        return(stream);
                    });

                    var contents = from stream in streams.Values
                                   select new ArchiveEntry
                    {
                        FileName = stream.Filename,
                        MD5      = stream.MD5Hash,
                        SHA256   = stream.SHA256Hash,
                        Size     = stream.Size.ToString()
                    };

                    var ac = new ArchiveContents();


                    using (var file = File.OpenRead(archive))
                    {
                        var hasher = new MD5CryptoServiceProvider();
                        hasher.ComputeHash(file);
                        ac.MD5 = HashingStream.ToHex(hasher.Hash);
                    }

                    using (var file = File.OpenRead(archive))
                    {
                        var hasher = new SHA256CryptoServiceProvider();
                        hasher.ComputeHash(file);
                        ac.SHA256 = HashingStream.ToHex(hasher.Hash);
                    }
                    ac.DiskName = Path.GetFileName(archive);
                    ac.FileSize = (new FileInfo(archive)).Length.ToString();
                    ac.Contents = contents.ToArray();

                    // Look for an .meta file next to the archive itself
                    var meta_path  = archive + ".meta";
                    bool found_ini = false;
                    if (File.Exists(meta_path))
                    {
                        var meta_ini = (new FileIniDataParser()).ReadFile(meta_path);
                        var general  = meta_ini["General"];

                        ac.NexusModId  = general["modID"];
                        ac.NexusFileId = general["fileID"];
                        ac.TargetGame  = general["gameName"];
                        ac.ModName     = general["modName"];
                        ac.Version     = general["version"];
                        ac.Repository  = general["repository"];
                    }

                    if (found_ini == false)
                    {
                        // Try to find a matching .meta file in a mod that uses this archive
                        var archive_file_name = Path.GetFileName(archive);
                        var match             = ini_files.Where(d => Path.GetFileName(d.GetIn("General", "installationFile")) == archive_file_name)
                                                .FirstOrDefault();
                        if (match != null)
                        {
                            var general   = match["General"];
                            ac.TargetGame = general["gameName"];
                            ac.Repository = general["repository"];
                        }
                    }

                    // Try to find a matching nexus mod
                    var result = await _nexusApi.GetModsByMd5(new IntermediaryModObject
                    {
                        Md5             = ac.MD5,
                        TrueArchiveName = ac.DiskName,
                        TargetGame      = ac.TargetGame
                    });

                    if (result != null)
                    {
                        ac.Author        = result.AuthorName;
                        ac.NexusModId    = result.ModId;
                        ac.NexusFileName = result.NexusFileName;
                        ac.NexusFileId   = result.FileId;
                        ac.Version       = result.Version;
                        ac.FileSize      = (new FileInfo(archive)).Length.ToString();
                    }

                    await WriteJSON(meta_data_path, ac);
                    Update(progress, "[META] Finished", Path.GetFileName(archive));
                }
            });

            await Task.WhenAll(tasks);

            ConcurrentStack <ArchiveContents> loaded = new ConcurrentStack <ArchiveContents>();


            var result_tasks = Directory.EnumerateFiles(_transcompilerBase.DownloadsDirectoryPath, "*" + METADATA_EXTENSION)
                               .AsParallel()
                               .Select(async file =>
            {
                using (var r = new StreamReader(File.OpenRead(file)))
                {
                    return(JsonConvert.DeserializeObject <ArchiveContents>(await r.ReadToEndAsync()));
                }
            });

            await Task.WhenAll(result_tasks);

            var results = result_tasks.Select(t => t.Result).ToList();

            return(results);
        }