2019-10-12 21:37:16 +00:00
using System ;
using System.Collections.Generic ;
2020-05-20 03:25:41 +00:00
using System.IO ;
2019-10-12 21:37:16 +00:00
using System.Linq ;
2020-05-20 03:25:41 +00:00
using System.Net.Http ;
2019-12-07 03:50:50 +00:00
using System.Threading.Tasks ;
2020-02-27 13:46:34 +00:00
using Alphaleonis.Win32.Filesystem ;
2019-11-04 23:21:58 +00:00
using Wabbajack.Common ;
2020-02-08 23:53:11 +00:00
using Wabbajack.Lib.Downloaders.UrlDownloaders ;
2019-10-12 21:37:16 +00:00
2019-10-16 03:10:34 +00:00
namespace Wabbajack.Lib.Downloaders
2019-10-12 21:37:16 +00:00
{
public static class DownloadDispatcher
{
2019-10-13 14:18:21 +00:00
public static readonly List < IDownloader > Downloaders = new List < IDownloader > ( )
2019-10-12 21:37:16 +00:00
{
2019-12-14 17:30:52 +00:00
new GameFileSourceDownloader ( ) ,
2019-10-12 21:37:16 +00:00
new MegaDownloader ( ) ,
new DropboxDownloader ( ) ,
new GoogleDriveDownloader ( ) ,
2019-10-12 22:31:07 +00:00
new ModDBDownloader ( ) ,
2019-10-12 22:15:20 +00:00
new NexusDownloader ( ) ,
2019-10-24 01:19:11 +00:00
new MediaFireDownloader ( ) ,
2021-06-19 04:05:58 +00:00
new LoversLabOAuthDownloader ( ) ,
2021-06-15 04:55:07 +00:00
new VectorPlexusOAuthDownloader ( ) ,
2020-01-06 15:08:54 +00:00
new DeadlyStreamDownloader ( ) ,
2020-01-22 09:50:09 +00:00
new TESAllianceDownloader ( ) ,
2020-05-28 23:44:58 +00:00
new TESAllDownloader ( ) ,
2020-05-09 22:16:16 +00:00
new WabbajackCDNDownloader ( ) ,
2020-05-28 22:31:01 +00:00
new YandexDownloader ( ) ,
2019-11-07 00:29:53 +00:00
new HTTPDownloader ( ) ,
2019-10-12 22:31:07 +00:00
new ManualDownloader ( ) ,
2021-06-19 16:16:22 +00:00
new DeprecatedVectorPlexusDownloader ( ) ,
new DeprecatedLoversLabDownloader ( ) ,
2019-10-12 21:37:16 +00:00
} ;
2020-02-08 23:53:11 +00:00
public static readonly List < IUrlInferencer > Inferencers = new List < IUrlInferencer > ( )
{
2020-05-09 22:16:16 +00:00
new WabbajackCDNInfluencer ( )
2020-02-08 23:53:11 +00:00
} ;
2019-10-12 22:31:07 +00:00
private static readonly Dictionary < Type , IDownloader > IndexedDownloaders ;
2019-10-12 21:37:16 +00:00
static DownloadDispatcher ( )
{
2019-10-12 22:31:07 +00:00
IndexedDownloaders = Downloaders . ToDictionary ( d = > d . GetType ( ) ) ;
2019-10-12 21:37:16 +00:00
}
2020-04-10 01:29:53 +00:00
public static async Task < AbstractDownloadState ? > Infer ( Uri uri )
2020-02-08 23:53:11 +00:00
{
2020-03-03 21:53:29 +00:00
foreach ( var inf in Inferencers )
{
var state = await inf . Infer ( uri ) ;
if ( state ! = null )
return state ;
}
2020-09-09 12:51:38 +00:00
var meta = string . Join ( "\n" , new string [ ]
{
"[General]" ,
$"directURL={uri}"
} ) ;
return ( AbstractDownloadState ) ( await ResolveArchive ( meta . LoadIniString ( ) ) ) ;
2020-02-08 23:53:11 +00:00
}
2019-12-08 17:00:22 +00:00
public static T GetInstance < T > ( ) where T : IDownloader
2019-10-12 21:37:16 +00:00
{
2019-12-08 17:00:22 +00:00
var inst = ( T ) IndexedDownloaders [ typeof ( T ) ] ;
return inst ;
2019-10-12 21:37:16 +00:00
}
2021-01-01 00:06:56 +00:00
public static async Task < AbstractDownloadState ? > ResolveArchive ( dynamic ini , bool quickMode = false )
2019-10-12 21:37:16 +00:00
{
2020-07-06 06:58:13 +00:00
var states = await Task . WhenAll ( Downloaders . Select ( d = >
2021-01-01 00:06:56 +00:00
( Task < AbstractDownloadState ? > ) d . GetDownloaderState ( ini , quickMode ) ) ) ;
2019-12-07 03:50:50 +00:00
return states . FirstOrDefault ( result = > result ! = null ) ;
2019-10-12 21:37:16 +00:00
}
2019-10-16 11:44:45 +00:00
/// <summary>
/// Reduced version of Resolve archive that requires less information, but only works
/// with a single URL string
/// </summary>
/// <param name="ini"></param>
/// <returns></returns>
2020-04-10 01:29:53 +00:00
public static AbstractDownloadState ? ResolveArchive ( string url )
2019-10-16 11:44:45 +00:00
{
return Downloaders . OfType < IUrlDownloader > ( ) . Select ( d = > d . GetDownloaderState ( url ) ) . FirstOrDefault ( result = > result ! = null ) ;
}
2020-04-10 19:06:32 +00:00
public static async Task PrepareAll ( IEnumerable < AbstractDownloadState > states )
2019-11-04 23:21:58 +00:00
{
2020-04-10 19:06:32 +00:00
await Task . WhenAll ( states . Select ( s = > s . GetDownloader ( ) . GetType ( ) )
2019-11-04 23:21:58 +00:00
. Distinct ( )
2020-04-10 19:06:32 +00:00
. Select ( t = > Downloaders . First ( d = > d . GetType ( ) = = t ) . Prepare ( ) ) ) ;
2019-11-04 23:21:58 +00:00
}
2020-02-27 13:46:34 +00:00
2020-08-20 22:02:50 +00:00
public enum DownloadResult
{
Failure ,
Update ,
Mirror ,
Success
}
public static async Task < DownloadResult > DownloadWithPossibleUpgrade ( Archive archive , AbsolutePath destination )
2020-02-27 13:46:34 +00:00
{
2020-08-05 00:34:09 +00:00
if ( await Download ( archive , destination ) )
{
2020-09-18 03:27:59 +00:00
var downloadedHash = await destination . FileHashCachedAsync ( ) ;
if ( downloadedHash = = archive . Hash | | archive . Hash = = default )
return DownloadResult . Success ;
2020-08-05 00:34:09 +00:00
}
if ( await DownloadFromMirror ( archive , destination ) )
2020-02-27 13:46:34 +00:00
{
await destination . FileHashCachedAsync ( ) ;
2020-08-20 22:02:50 +00:00
return DownloadResult . Mirror ;
2020-02-27 13:46:34 +00:00
}
2020-05-20 03:25:41 +00:00
if ( ! ( archive . State is IUpgradingState ) )
2020-02-27 13:46:34 +00:00
{
2020-05-20 03:25:41 +00:00
Utils . Log ( $"Download failed for {archive.Name} and no upgrade from this download source is possible" ) ;
2020-08-20 22:02:50 +00:00
return DownloadResult . Failure ;
2020-02-27 13:46:34 +00:00
}
2020-05-20 03:25:41 +00:00
Utils . Log ( $"Trying to find solution to broken download for {archive.Name}" ) ;
2020-08-12 22:23:02 +00:00
var result = await FindUpgrade ( archive ) ;
2021-03-11 02:28:28 +00:00
if ( result = = default )
2020-02-27 13:46:34 +00:00
{
2020-09-10 13:07:25 +00:00
result = await AbstractDownloadState . ServerFindUpgrade ( archive ) ;
if ( result = = default )
{
Utils . Log (
$"No solution for broken download {archive.Name} {archive.State.PrimaryKeyString} could be found" ) ;
return DownloadResult . Failure ;
}
2020-05-20 03:25:41 +00:00
}
2020-05-30 21:05:26 +00:00
Utils . Log ( $"Looking for patch for {archive.Name} ({(long)archive.Hash} {archive.Hash.ToHex()} -> {(long)result.Archive!.Hash} {result.Archive!.Hash.ToHex()})" ) ;
2020-05-20 03:25:41 +00:00
var patchResult = await ClientAPI . GetModUpgrade ( archive , result . Archive ! ) ;
2020-02-27 13:46:34 +00:00
2020-08-08 04:20:49 +00:00
Utils . Log ( $"Downloading patch for {archive.Name} from {patchResult}" ) ;
2020-05-20 03:25:41 +00:00
var tempFile = new TempFile ( ) ;
2020-08-31 23:29:48 +00:00
if ( WabbajackCDNDownloader . DomainRemaps . TryGetValue ( patchResult . Host , out var remap ) )
{
var builder = new UriBuilder ( patchResult ) { Host = remap } ;
patchResult = builder . Uri ;
}
2020-06-16 22:21:01 +00:00
using var response = await ( await ClientAPI . GetClient ( ) ) . GetAsync ( patchResult ) ;
2020-08-08 04:20:49 +00:00
2020-05-20 03:25:41 +00:00
await tempFile . Path . WriteAllAsync ( await response . Content . ReadAsStreamAsync ( ) ) ;
response . Dispose ( ) ;
Utils . Log ( $"Applying patch to {archive.Name}" ) ;
2020-05-25 14:31:56 +00:00
await using ( var src = await result . NewFile . Path . OpenShared ( ) )
2020-05-25 17:34:25 +00:00
await using ( var final = await destination . Create ( ) )
2020-02-27 13:46:34 +00:00
{
2020-05-25 14:31:56 +00:00
Utils . ApplyPatch ( src , ( ) = > tempFile . Path . OpenShared ( ) . Result , final ) ;
2020-02-27 13:46:34 +00:00
}
2020-05-20 03:25:41 +00:00
var hash = await destination . FileHashCachedAsync ( ) ;
if ( hash ! = archive . Hash & & archive . Hash ! = default )
{
Utils . Log ( "Archive hash didn't match after patching" ) ;
2020-08-20 22:02:50 +00:00
return DownloadResult . Failure ;
2020-05-20 03:25:41 +00:00
}
2020-02-27 13:46:34 +00:00
2020-08-20 22:02:50 +00:00
return DownloadResult . Update ;
2020-02-27 13:46:34 +00:00
}
2020-08-12 22:23:02 +00:00
public static async Task < ( Archive ? Archive , TempFile NewFile ) > FindUpgrade ( Archive a , Func < Archive , Task < AbsolutePath > > ? downloadResolver = null )
{
downloadResolver ? ? = async a = > default ;
return await a . State . FindUpgrade ( a , downloadResolver ) ;
}
2020-02-27 13:46:34 +00:00
2020-08-12 22:23:02 +00:00
2020-08-05 00:34:09 +00:00
private static async Task < bool > DownloadFromMirror ( Archive archive , AbsolutePath destination )
{
try
{
2020-08-08 03:40:03 +00:00
var url = await ClientAPI . GetMirrorUrl ( archive . Hash ) ;
if ( url = = null ) return false ;
2020-08-05 00:34:09 +00:00
var newArchive =
new Archive (
2020-08-08 03:40:03 +00:00
new WabbajackCDNDownloader . State ( url ) )
2020-08-05 00:34:09 +00:00
{
Hash = archive . Hash , Size = archive . Size , Name = archive . Name
} ;
return await Download ( newArchive , destination ) ;
}
2020-10-01 03:50:09 +00:00
catch ( Exception )
2020-08-05 00:34:09 +00:00
{
return false ;
}
}
2020-03-25 22:30:43 +00:00
private static async Task < bool > Download ( Archive archive , AbsolutePath destination )
2020-02-27 13:46:34 +00:00
{
try
{
var result = await archive . State . Download ( archive , destination ) ;
if ( ! result ) return false ;
2020-03-27 03:10:23 +00:00
if ( ! archive . Hash . IsValid ) return true ;
2020-02-27 13:46:34 +00:00
var hash = await destination . FileHashCachedAsync ( ) ;
if ( hash = = archive . Hash ) return true ;
Utils . Log ( $"Hashed download is incorrect" ) ;
return false ;
}
2020-03-28 13:33:39 +00:00
catch ( Exception )
2020-02-27 13:46:34 +00:00
{
return false ;
}
}
2019-10-12 21:37:16 +00:00
}
}