public async Task <ActionResult> Diff(string from, string to)
        {
            var installFrom = await NGDP.GetInstall(from, true);

            var installTo = await NGDP.GetInstall(to, true);

            var installFromDict = new Dictionary <string, InstallFileEntry>();

            foreach (var entry in installFrom.entries)
            {
                installFromDict.Add(entry.name, entry);
            }

            var installToDict = new Dictionary <string, InstallFileEntry>();

            foreach (var entry in installTo.entries)
            {
                installToDict.Add(entry.name, entry);
            }

            var fromEntries = installFromDict.Keys.ToHashSet();
            var toEntries   = installToDict.Keys.ToHashSet();

            var commonEntries  = fromEntries.Intersect(toEntries);
            var removedEntries = fromEntries.Except(commonEntries);
            var addedEntries   = toEntries.Except(commonEntries);

            var modifiedFiles = new List <InstallFileEntry>();

            foreach (var entry in commonEntries)
            {
                var originalFile = installFromDict[entry];
                var patchedFile  = installToDict[entry];

                if (originalFile.contentHash.Equals(patchedFile.contentHash))
                {
                    continue;
                }

                modifiedFiles.Add(patchedFile);
            }

            return(Json(new
            {
                added = addedEntries,
                modified = modifiedFiles,
                removed = removedEntries
            }));
        }
        public async Task <ActionResult> DumpByHash(string hash)
        {
            var install = await NGDP.GetInstall(hash, true);

            return(Json(install.entries));
        }