Skip to content

Commit d603419

Browse files
committed
QAVS speedrun
1 parent cdb5794 commit d603419

9 files changed

Lines changed: 298 additions & 15 deletions

File tree

DiffCreator/Apkutils.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.IO.Compression;
2+
using QuestPatcher.Axml;
3+
4+
namespace DiffCreator;
5+
6+
public class Apkutils
7+
{
8+
public const string ManifestPath = "AndroidManifest.xml";
9+
10+
public static PatchingStatus GetPatchingStatus(ZipArchive apk, string packageId = "")
11+
{
12+
PatchingStatus status = new PatchingStatus();
13+
MemoryStream manifestStream = new MemoryStream();
14+
using (Stream s = apk.GetEntry(ManifestPath).Open())
15+
{
16+
s.CopyTo(manifestStream);
17+
}
18+
manifestStream.Position = 0;
19+
AxmlElement manifest = AxmlLoader.LoadDocument(manifestStream);
20+
foreach (AxmlAttribute a in manifest.Attributes)
21+
{
22+
if (a.Name == "versionName")
23+
{
24+
status.version = a.Value.ToString();
25+
}
26+
if (a.Name == "versionCode")
27+
{
28+
status.versionCode = a.Value.ToString();
29+
}
30+
if(a.Name == "package")
31+
{
32+
packageId = a.Value.ToString();
33+
}
34+
}
35+
AxmlElement appElement = manifest.Children.Single(element => element.Name == "application");
36+
status.copyOf = null;
37+
foreach (AxmlElement e in appElement.Children)
38+
{
39+
if (e.Attributes.Any(x => x.Name == "name" && x.Value.ToString() == "QAVS.copyOf"))
40+
{
41+
status.copyOf = (string)e.Attributes.FirstOrDefault(x => x.Name == "value").Value;
42+
//Logger.Log("App is copy of " + status.copyOf);
43+
}
44+
}
45+
status.package = packageId;
46+
manifestStream.Close();
47+
manifestStream.Dispose();
48+
apk.Dispose();
49+
return status;
50+
}
51+
}

DiffCreator/DiffCreator.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using System.Collections.Generic;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.IO.Compression;
5+
using System.Linq;
6+
using System.Text.Json;
7+
using ComputerUtils.Logging;
8+
using DiffCreator;
9+
10+
namespace QuestAppVersionSwitcher.DiffDowngrading
11+
{
12+
public class DiffCreator
13+
{
14+
public static DiffDowngradeEntry CreateDiff(string sourceBackup, string targetBackup,
15+
string outputDir)
16+
{
17+
DiffDowngradeEntry baseEntry = new DiffDowngradeEntry();
18+
if (!sourceBackup.EndsWith(Path.DirectorySeparatorChar)) sourceBackup += Path.DirectorySeparatorChar;
19+
if (!targetBackup.EndsWith(Path.DirectorySeparatorChar)) targetBackup += Path.DirectorySeparatorChar;
20+
if (!outputDir.EndsWith(Path.DirectorySeparatorChar)) outputDir += Path.DirectorySeparatorChar;
21+
if(!Directory.Exists(targetBackup)) Directory.CreateDirectory(targetBackup);
22+
23+
Logger.Log("Creating diff");
24+
if(!File.Exists(sourceBackup + "app.apk") || !File.Exists(targetBackup + "app.apk"))
25+
{
26+
Logger.Log("App.apk not found in source or target backup");
27+
return null;
28+
}
29+
30+
PatchingStatus sPatching = Apkutils.GetPatchingStatus(ZipFile.OpenRead(sourceBackup + "app.apk"));
31+
baseEntry.SV = sPatching.version;
32+
baseEntry.appid = sPatching.package;
33+
baseEntry.TV = Apkutils.GetPatchingStatus(ZipFile.OpenRead(targetBackup + "app.apk")).version;
34+
baseEntry.isXDelta3 = true;
35+
36+
// Create entries
37+
baseEntry.Set(CreateDiffOfFile(baseEntry, sourceBackup + "app.apk", targetBackup + "app.apk", outputDir));
38+
// add obbs and other files
39+
List<string> allSourceFiles = new List<string>();
40+
List<string> allTargetFiles = new List<string>();
41+
if (Directory.Exists(sourceBackup + "/obb"))
42+
{
43+
allSourceFiles = Directory.GetFiles(sourceBackup + "/obb").ToList();
44+
allSourceFiles.Insert(0, sourceBackup + "app.apk");
45+
}
46+
47+
if (Directory.Exists(targetBackup + "/obb"))
48+
{
49+
allTargetFiles = Directory.GetFiles(targetBackup + "/obb").ToList();
50+
allTargetFiles.Insert(0, targetBackup + "app.apk");
51+
}
52+
53+
for (int i = 1; i < allTargetFiles.Count; i++)
54+
{
55+
// generate one diff for every target backup obbs
56+
baseEntry.otherFiles.Add(CreateDiffOfFile(baseEntry, allSourceFiles[i % allSourceFiles.Count],
57+
allTargetFiles[i], outputDir));
58+
}
59+
60+
// The diff file is now created
61+
Logger.Log("Writing version.json file");
62+
File.WriteAllText(outputDir + "version.json", JsonSerializer.Serialize(baseEntry));
63+
Logger.Log("Finished creating diff");
64+
Logger.Log("\n\nSummary:");
65+
Logger.Log("Diff for " + baseEntry.SV + " (apk) -> " + baseEntry.TV + " (apk) created");
66+
foreach (FileDiffDowngradeEntry otherFile in baseEntry.otherFiles)
67+
{
68+
Logger.Log("Diff for " + otherFile.sourceFilename + " -> " + otherFile.outputFilename + " created");
69+
}
70+
return baseEntry;
71+
}
72+
73+
public static FileDiffDowngradeEntry CreateDiffOfFile(DiffDowngradeEntry baseEntry, string sourcePath,
74+
string targetPath,
75+
string outputPath)
76+
{
77+
FileDiffDowngradeEntry e = new FileDiffDowngradeEntry();
78+
e.sourceFilename = Path.GetFileName(sourcePath);
79+
e.outputFilename = Path.GetFileName(targetPath);
80+
e.diffFilename = baseEntry.GetDowngradeBaseName() + e.sourceFilename + ".xdelta3";
81+
e.type = e.sourceFilename.ToLower().EndsWith(".apk")
82+
? FileDiffDowngradeEntryType.Apk
83+
: FileDiffDowngradeEntryType.Obb;
84+
e.isXDelta3 = true;
85+
e.SourceByteSize = new FileInfo(sourcePath).Length;
86+
e.TargetByteSize = new FileInfo(targetPath).Length;
87+
e.SSHA256 = Utils.GetSHA256OfFile(sourcePath);
88+
e.TSHA256 = Utils.GetSHA256OfFile(targetPath);
89+
e.download = "";
90+
e.isDirectDownload = true;
91+
string diffPath = outputPath + e.diffFilename;
92+
Logger.Log("Encoding diff file for " + e.sourceFilename + " -> " + e.outputFilename + " to " +
93+
e.diffFilename);
94+
Process.Start("xdelta3.exe", "-e -s \"" + sourcePath + "\" \"" + targetPath + "\" \"" + diffPath + "\"").WaitForExit();
95+
96+
e.DSHA256 = Utils.GetSHA256OfFile(diffPath);
97+
e.DiffByteSize = new FileInfo(diffPath).Length;
98+
return e;
99+
}
100+
}
101+
}

DiffCreator/DiffDowngradeEntry.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
4+
namespace QuestAppVersionSwitcher.DiffDowngrading
5+
{
6+
public class DiffDowngradeEntry : FileDiffDowngradeEntry
7+
{
8+
public string SV { get; set; }
9+
public string TV { get; set; }
10+
public string appid { get; set; }
11+
12+
public string GetDowngradeBaseName()
13+
{
14+
return appid + "." + SV + "TO" + TV + ".";
15+
}
16+
17+
public List<FileDiffDowngradeEntry> otherFiles { get; set; } = new List<FileDiffDowngradeEntry>();
18+
}
19+
20+
public class FileDiffDowngradeEntry
21+
{
22+
public string sourceFilename { get; set; } = "";
23+
private string _diffFilename = "";
24+
25+
public string diffFilename
26+
{
27+
get
28+
{
29+
if (_diffFilename == "") return Path.GetFileName(download.Split('?')[0]);
30+
return _diffFilename;
31+
}
32+
set
33+
{
34+
_diffFilename = value;
35+
}
36+
}
37+
38+
public string outputFilename { get; set; } = "";
39+
public FileDiffDowngradeEntryType type { get; set; } = FileDiffDowngradeEntryType.Apk;
40+
public bool isXDelta3 { get; set; } = false;
41+
public long TargetByteSize { get; set; } = 0;
42+
public long DiffByteSize { get; set; } = 0;
43+
public long SourceByteSize { get; set; } = 0;
44+
public string SSHA256 { get; set; } = "";
45+
public string DSHA256 { get; set; } = "";
46+
public string TSHA256 { get; set; } = "";
47+
public string download { get; set; } = "";
48+
public bool isDirectDownload { get; set; } = false;
49+
50+
public void Set(FileDiffDowngradeEntry e)
51+
{
52+
this.sourceFilename = e.sourceFilename;
53+
this.diffFilename = e.diffFilename;
54+
this.outputFilename = e.outputFilename;
55+
this.type = e.type;
56+
this.isXDelta3 = e.isXDelta3;
57+
this.TargetByteSize = e.TargetByteSize;
58+
this.SourceByteSize = e.SourceByteSize;
59+
this.SSHA256 = e.SSHA256;
60+
this.DSHA256 = e.DSHA256;
61+
this.TSHA256 = e.TSHA256;
62+
this.download = e.download;
63+
this.isDirectDownload = e.isDirectDownload;
64+
}
65+
}
66+
67+
public enum FileDiffDowngradeEntryType
68+
{
69+
Unknown = -1,
70+
Apk = 0,
71+
Obb = 1
72+
}
73+
74+
public class DiffDowngradeEntryContainer
75+
{
76+
public List<DiffDowngradeEntry> versions { get; set; }
77+
}
78+
}

DiffCreator/PatchingStatus.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace DiffCreator;
2+
3+
public class PatchingStatus
4+
{
5+
public bool isPatched { get; set; } = false;
6+
public bool isInstalled { get; set; } = true;
7+
public bool canBePatched { get; set; } = true; // Not implemented yet.
8+
public string version { get; set; } = "";
9+
public string copyOf { get; set; } = "";
10+
public string versionCode { get; set; } = "";
11+
public string package { get; set; } = "";
12+
}

DiffCreator/Utils.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.IO;
3+
using System.Security.Cryptography;
4+
5+
namespace QuestAppVersionSwitcher
6+
{
7+
public class Utils
8+
{
9+
public static string GetSHA256OfFile(string filePath)
10+
{
11+
byte[] hash;
12+
using (FileStream fileStream = File.OpenRead(filePath))
13+
{
14+
using (SHA256 sha256 = SHA256.Create())
15+
{
16+
hash = sha256.ComputeHash(fileStream);
17+
}
18+
}
19+
20+
return BitConverter.ToString(hash).Replace("-", "").ToLower();
21+
}
22+
}
23+
}

QuestAppVersionSwitcher.sln

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestAppVersionSwitcher", "
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComputerUtils.Android", "ComputerUtils\ComputerUtils.Android\ComputerUtils.Android.csproj", "{D4B84F1A-A999-482F-8253-D6761AA93E24}"
99
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiffCreator", "DiffCreator\DiffCreator.csproj", "{5B57879B-170B-4804-B220-8CB11D332AD9}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComputerUtils", "ComputerUtils\ComputerUtils\ComputerUtils.csproj", "{21D96B62-FF5D-4E84-A13D-7EA1CE172A78}"
13+
EndProject
1014
Global
1115
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1216
Debug|Any CPU = Debug|Any CPU
@@ -23,6 +27,14 @@ Global
2327
{D4B84F1A-A999-482F-8253-D6761AA93E24}.Debug|Any CPU.Build.0 = Debug|Any CPU
2428
{D4B84F1A-A999-482F-8253-D6761AA93E24}.Release|Any CPU.ActiveCfg = Release|Any CPU
2529
{D4B84F1A-A999-482F-8253-D6761AA93E24}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{5B57879B-170B-4804-B220-8CB11D332AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31+
{5B57879B-170B-4804-B220-8CB11D332AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
32+
{5B57879B-170B-4804-B220-8CB11D332AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
33+
{5B57879B-170B-4804-B220-8CB11D332AD9}.Release|Any CPU.Build.0 = Release|Any CPU
34+
{21D96B62-FF5D-4E84-A13D-7EA1CE172A78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{21D96B62-FF5D-4E84-A13D-7EA1CE172A78}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{21D96B62-FF5D-4E84-A13D-7EA1CE172A78}.Release|Any CPU.ActiveCfg = Release|Any CPU
37+
{21D96B62-FF5D-4E84-A13D-7EA1CE172A78}.Release|Any CPU.Build.0 = Release|Any CPU
2638
EndGlobalSection
2739
GlobalSection(SolutionProperties) = preSolution
2840
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)