11using System ;
22using System . Collections . Generic ;
33using System . IO ;
4+ using System . Linq ;
45using System . Net ;
56using System . Text . Json ;
67using System . Threading ;
@@ -42,6 +43,9 @@ public override string progressString
4243 return String . Format ( "{0:0.#}" , progress * 100 ) + "%" ;
4344 }
4445 }
46+
47+ public virtual List < DownloadManager > downloadManagers { get ; set ; } = new List < DownloadManager > ( ) ;
48+
4549 public override string id { get ; set ; } = "" ;
4650 public override string status { get ; set ; } = "" ;
4751 public override string textColor { get ; set ; } = "#FFFFFF" ;
@@ -58,6 +62,7 @@ public override string progressString
5862 public override bool entitlementError { get ; set ; } = false ;
5963 public override bool done { get ; set ; } = false ;
6064 public string targetVersion { get ; set ; } = "" ;
65+ public List < FileDiffDowngradeEntry > diffsToDo { get ; set ; } = new List < FileDiffDowngradeEntry > ( ) ;
6166 [ CanBeNull ] public DiffDowngradeEntry entry { get ; set ; } = null ;
6267
6368 public DiffDowngrader ( DiffDownloadRequest r )
@@ -80,16 +85,13 @@ public DiffDowngrader(DiffDownloadRequest r)
8085
8186 private DownloadManager diffFileDownloadManager ;
8287 public Thread updateThread ;
83- public string diffFileDownloadPath = "" ;
8488
8589 public void StartDownload ( )
8690 {
87- id = DateTime . Now . Ticks . ToString ( ) ;
88- diffFileDownloadPath = CoreService . coreVars . QAVSTmpDowngradeDir + id + ".xdelta3" ;
8991 version = this . targetVersion ;
9092 gameName = this . packageName ;
9193 packageName = this . packageName ;
92- status = "Downloading patch for " + gameName + " " + version ;
94+ status = "Downloading patches for " + gameName + " " + version ;
9395
9496 this . backupName = gameName + " " + version + " Downgraded" ;
9597 foreach ( char r in QAVSWebserver . ReservedChars )
@@ -98,13 +100,13 @@ public void StartDownload()
98100 }
99101 status = gameName + " " + version ;
100102
101- // apk download
102- filesToDownload = 1 ;
103-
103+ // apk and obb download
104+ filesToDownload = entry . otherFiles . Count + 1 ;
105+ UpdateMaxConnections ( ) ;
104106
105107 diffFileDownloadManager = new DownloadManager ( ) ;
106- diffFileDownloadManager . connections = 10 ;
107- diffFileDownloadManager . StartDownload ( entry . download , diffFileDownloadPath ) ;
108+ diffFileDownloadManager . connections = maxConcurrentConnections ;
109+ diffFileDownloadManager . StartDownload ( entry . download , CoreService . coreVars . QAVSTmpDowngradeDir + entry . diffFilename ) ;
108110 diffFileDownloadManager . DownloadFinishedEvent += DownloadCompleted ;
109111 diffFileDownloadManager . NotFoundDownloadErrorEvent += NotFoundDownloadError ;
110112 diffFileDownloadManager . DownloadErrorEvent += DownloadError ;
@@ -121,8 +123,46 @@ public void StartDownload()
121123
122124 Done ( ) ;
123125 } ) ;
126+ diffsToDo . AddRange ( entry . otherFiles ) ;
124127 updateThread . Start ( ) ;
125128 }
129+
130+ public void UpdateManagersAndProgress ( )
131+ {
132+ UpdateMaxConnections ( ) ;
133+ totalBytes = diffFileDownloadManager . total + entry . otherFiles . Select ( x => x . DiffByteSize ) . Sum ( ) ;
134+ downloadedBytes = downloadedFilesTotalBytes + ( diffFileDownloadManager . downloadDone ? 0 : diffFileDownloadManager . done ) + downloadManagers . Where ( x => x . isObb ) . Select ( x => x . done ) . Sum ( ) ;
135+
136+ // Speed
137+ double secondsPassed = ( DateTime . Now - lastUpdate ) . TotalSeconds ;
138+ long bytesPerSec = ( long ) Math . Round ( ( downloadedBytes - lastBytes ) / secondsPassed ) ;
139+ lastBytesPerSec . Add ( bytesPerSec ) ;
140+ if ( lastBytesPerSec . Count > 15 ) lastBytesPerSec . RemoveAt ( 0 ) ;
141+ lastBytes = downloadedBytes ;
142+ long avg = 0 ;
143+ foreach ( long l in lastBytesPerSec ) avg += l ;
144+ avg /= lastBytesPerSec . Count ;
145+ lastUpdate = DateTime . Now ;
146+ if ( avg != 0 ) eTASeconds = ( totalBytes - downloadedBytes ) / avg ;
147+ speed = bytesPerSec ;
148+
149+
150+ for ( int i = 0 ; i < maxConcurrentDownloads - downloadManagers . Count ; i ++ )
151+ {
152+ if ( diffsToDo . Count <= 0 ) return ;
153+ DownloadManager m = new DownloadManager ( ) ;
154+ m . connections = maxConcurrentConnections ;
155+ m . DownloadFinishedEvent += DownloadCompleted ;
156+ m . NotFoundDownloadErrorEvent += NotFoundDownloadError ;
157+ m . DownloadErrorEvent += DownloadError ;
158+ m . isCancelable = false ;
159+ m . isObb = true ;
160+ m . StartDownload ( diffsToDo [ 0 ] . download , CoreService . coreVars . QAVSTmpDowngradeDir + diffsToDo [ 0 ] . diffFilename ) ;
161+ downloadManagers . Add ( m ) ;
162+ diffsToDo . RemoveAt ( 0 ) ;
163+ }
164+ QAVSWebserver . BroadcastDownloads ( false ) ;
165+ }
126166
127167 private void SetEntitlementError ( )
128168 {
@@ -132,30 +172,47 @@ private void SetEntitlementError()
132172
133173 public void Done ( )
134174 {
135- status = "Download completed. Applying diff patch to current apk. Please wait up to 5 minutes." ;
175+ int totalPatches = entry . otherFiles . Count + 1 ;
176+ int i = 1 ;
177+ status = "Download completed. Applying diff patches to game. Please wait up to 5 minutes (" + i + "/" + totalPatches + ")" ;
136178 downloadedBytes = totalBytes ;
137179 UpdateManagersAndProgress ( ) ;
138180 QAVSWebserver . BroadcastDownloads ( true ) ;
139181 string backupDir = CoreService . coreVars . QAVSBackupDir + this . packageName + "/" + this . backupName + "/" ;
140182 FileManager . RecreateDirectoryIfExisting ( backupDir ) ;
141183 // Get installed apk
142184 string appPath = AndroidService . FindAPKLocation ( entry . appid ) ;
143- using ( FileStream input = File . OpenRead ( appPath ) )
185+ // apk
186+ ApplyPatch ( appPath , CoreService . coreVars . QAVSTmpDowngradeDir + entry . diffFilename , backupDir + "app.apk" ) ;
187+ i ++ ;
188+ // obbs
189+ foreach ( FileDiffDowngradeEntry file in entry . otherFiles )
144190 {
145- using ( FileStream patch = File . OpenRead ( diffFileDownloadPath ) ) {
146- using ( FileStream output = File . Create ( backupDir + "app.apk" ) )
191+ status = "Download completed. Applying diff patches to game. Please wait up to 5 minutes (" + i + "/" + totalPatches + ")" ;
192+ ApplyPatch ( CoreService . coreVars . AndroidObbLocation + entry . appid + "/" + file . sourceFilename , CoreService . coreVars . QAVSTmpDowngradeDir + file . diffFilename , backupDir + "obb/" + entry . appid + "/" + file . outputFilename ) ;
193+ i ++ ;
194+ }
195+ BackupInfo info = BackupManager . GetBackupInfo ( backupDir , true ) ; // Populate info.json correctly
196+ RealDone ( ) ;
197+ }
198+
199+ public void ApplyPatch ( string sourcePath , string diffPath , string outputPath )
200+ {
201+ using ( FileStream sourceStream = File . OpenRead ( sourcePath ) )
202+ {
203+ using ( FileStream diffStream = File . OpenRead ( diffPath ) )
204+ {
205+ using ( FileStream output = File . Create ( outputPath ) )
147206 {
148- VCDiff . Decoders . VcDecoder decoder = new VCDiff . Decoders . VcDecoder ( input , patch , output ) ;
207+ VCDiff . Decoders . VcDecoder decoder = new VCDiff . Decoders . VcDecoder ( sourceStream , diffStream , output ) ;
149208 long bytesWritten ;
150- Logger . Log ( "Decoding diff file for " + gameName + " " + version + " to " + backupDir + "app.apk" ) ;
209+ Logger . Log ( "Decoding diff file for " + sourcePath + " with " + diffPath + " to " + outputPath ) ;
151210 decoder . Decode ( out bytesWritten ) ;
152- Logger . Log ( "Wrote " + bytesWritten + " bytes to " + backupDir + "app.apk" ) ;
211+ Logger . Log ( "Wrote " + bytesWritten + " bytes to " + outputPath ) ;
153212 decoder . Dispose ( ) ;
154213 }
155214 }
156215 }
157- BackupInfo info = BackupManager . GetBackupInfo ( backupDir , true ) ; // Populate info.json correctly
158- RealDone ( ) ;
159216 }
160217
161218 public void RealDone ( )
@@ -171,26 +228,6 @@ public void RealDone()
171228 private List < long > lastBytesPerSec = new List < long > ( ) ;
172229 private DateTime lastUpdate = DateTime . Now ;
173230
174- public void UpdateManagersAndProgress ( )
175- {
176- totalBytes = diffFileDownloadManager . total ;
177- downloadedBytes = diffFileDownloadManager . done ;
178-
179- // Speed
180- double secondsPassed = ( DateTime . Now - lastUpdate ) . TotalSeconds ;
181- long bytesPerSec = ( long ) Math . Round ( ( downloadedBytes - lastBytes ) / secondsPassed ) ;
182- lastBytesPerSec . Add ( bytesPerSec ) ;
183- if ( lastBytesPerSec . Count > 15 ) lastBytesPerSec . RemoveAt ( 0 ) ;
184- lastBytes = downloadedBytes ;
185- long avg = 0 ;
186- foreach ( long l in lastBytesPerSec ) avg += l ;
187- avg /= lastBytesPerSec . Count ;
188- lastUpdate = DateTime . Now ;
189- if ( avg != 0 ) eTASeconds = ( totalBytes - downloadedBytes ) / avg ;
190- speed = bytesPerSec ;
191- QAVSWebserver . BroadcastDownloads ( false ) ;
192- }
193-
194231 private void DownloadError ( DownloadManager manager )
195232 {
196233 Cancel ( ) ;
0 commit comments