private void ProcessAll(byte[] xxhdpiBytes, byte[] xhdpiBytes, byte[] hdpiBytes, byte[] mdpiBytes, Dispatcher uiThreadDispatcher) { const string internalDataInApkName = "data.save"; const string externalDataInApkName = "extdata.save"; IVisualProgress visualProgress = VisualProgress.Value; ITaskBarManager taskBarManager = TaskBarManager.Value; string apkFile = Apk.Value; string saveFile = Save.Value; string androidDataFile = Data.Value; string[] androidObbFiles = (string[])Obb.Value?.Clone() ?? new string[0]; string appTitle = AppTitle.Value; bool alternativeSigning = _settings.AlternativeSigning; BackupType backupType = _settings.BackupType; // initializing visualProgress?.SetBarIndeterminate(); visualProgress?.ShowBar(); visualProgress?.ShowIndeterminateLabel(); taskBarManager?.SetProgress(0); taskBarManager?.SetUsualState(); void SetStep(string step, int stepNumber) { WindowTitle.Value = step; Log(step); const int maxStep = 5; int percentage = (stepNumber - 1) * 100 / maxStep; visualProgress?.SetLabelText(step); taskBarManager?.SetProgress(percentage); } SetStep(MainResources.StepInitializing, 1); string resultFilePath = Path.Combine( Path.GetDirectoryName(apkFile) ?? string.Empty, Path.GetFileNameWithoutExtension(apkFile) + "_mod.apk" ); IApktool apktool = _apktoolProvider.Get(); IProcessDataHandler dataHandler = new ProcessDataCombinedHandler(Log); ITempFileProvider tempFileProvider = _tempUtils.CreateTempFileProvider(); ITempFolderProvider tempFolderProvider = _tempUtils.CreateTempFolderProvider(); using (var stgContainerExtracted = AndroidHelper.Logic.Utils.TempUtils.UseTempFolder(tempFolderProvider)) { // extracting SaveToGame container app SetStep(MainResources.CopyingStgApk, 2); string containerZipPath = Path.Combine(_globalVariables.PathToResources, "apk.zip"); using (var zip = new ZipFile(containerZipPath) { Password = _globalVariables.AdditionalFilePassword }) { zip.ExtractAll(stgContainerExtracted.TempFolder); } SetStep(MainResources.AddingData, 3); // creating assets folder for data string stgContainerAssetsPath = Path.Combine(stgContainerExtracted.TempFolder, "assets"); LDirectory.CreateDirectory(stgContainerAssetsPath); // adding backup if (!string.IsNullOrEmpty(saveFile)) { string internalDataPath = Path.Combine(stgContainerAssetsPath, internalDataInApkName); string externalDataPath = Path.Combine(stgContainerAssetsPath, externalDataInApkName); ApkModifer.ParseBackup( pathToBackup: saveFile, backupType: backupType, resultInternalDataPath: internalDataPath, resultExternalDataPath: externalDataPath, tempFolderProvider: tempFolderProvider ); } // adding external data if (!string.IsNullOrEmpty(androidDataFile)) { LFile.Copy( androidDataFile, Path.Combine(stgContainerAssetsPath, externalDataInApkName) ); } // adding obb files if (androidObbFiles.Length != 0) { using (var obbParts = AndroidHelper.Logic.Utils.TempUtils.UseTempFolder(tempFolderProvider)) { ApkModifer.SplitObbFiles( obbFilePaths: androidObbFiles, partsFolderPath: obbParts.TempFolder, // todo: add progress progressNotifier: null ); string assetsDir = Path.Combine(stgContainerExtracted.TempFolder, "assets", "111111222222333333"); LDirectory.CreateDirectory(assetsDir); IEnumerable <string> filesToAdd = LDirectory.EnumerateFiles(obbParts.TempFolder); foreach (var file in filesToAdd) { LFile.Copy(file, Path.Combine(assetsDir, Path.GetFileName(file))); } } } // adding resigned apk to container using (var sourceResigned = AndroidHelper.Logic.Utils.TempUtils.UseTempFile(tempFileProvider)) { apktool.Sign( sourceApkPath: apkFile, signedApkPath: sourceResigned.TempFile, tempFileProvider: tempFileProvider, dataHandler: dataHandler, deleteMetaInf: !alternativeSigning ); LFile.Copy( sourceFileName: sourceResigned.TempFile, destFileName: Path.Combine(stgContainerAssetsPath, "install.bin"), overwrite: false ); } // modifying AndroidManifest { string pathToManifest = Path.Combine(stgContainerExtracted.TempFolder, "AndroidManifest.xml"); string sourcePackageName; using (var sourceManifest = AndroidHelper.Logic.Utils.TempUtils.UseTempFile(tempFileProvider)) { apktool.ExtractSimpleManifest( apkPath: apkFile, resultManifestPath: sourceManifest.TempFile, tempFolderProvider: tempFolderProvider ); sourcePackageName = new AndroidManifest(sourceManifest.TempFile).Package; } LFile.WriteAllText( pathToManifest, LFile.ReadAllText(pathToManifest, Encoding.UTF8) .Replace("change_package", sourcePackageName) .Replace("@string/app_name", appTitle) ); } // adding icons { string iconsFolder = Path.Combine(stgContainerExtracted.TempFolder, "res", "mipmap-"); void DeleteIcon(string folder) => LFile.Delete(Path.Combine($"{iconsFolder}{folder}", "ic_launcher.png")); DeleteIcon("xxhdpi-v4"); DeleteIcon("xhdpi-v4"); DeleteIcon("hdpi-v4"); DeleteIcon("mdpi-v4"); void WriteIcon(string folder, byte[] imageBytes) => LFile.WriteAllBytes(Path.Combine($"{iconsFolder}{folder}", "ic_launcher.png"), imageBytes); WriteIcon("xxhdpi-v4", xxhdpiBytes); WriteIcon("xhdpi-v4", xhdpiBytes); WriteIcon("hdpi-v4", hdpiBytes); WriteIcon("mdpi-v4", mdpiBytes); } // compiling + signing using (var compiledContainer = AndroidHelper.Logic.Utils.TempUtils.UseTempFile(tempFileProvider)) { // compiling SetStep(MainResources.StepCompiling, 4); // todo: check errors List <Error> compilationErrors; apktool.Compile( projectFolderPath: stgContainerExtracted.TempFolder, destinationApkPath: compiledContainer.TempFile, dataHandler: dataHandler, errors: out compilationErrors ); if (compilationErrors.Count > 0) { Log(MainResources.ErrorUp); return; } // signing SetStep(MainResources.StepSigning, 5); apktool.Sign( sourceApkPath: compiledContainer.TempFile, signedApkPath: resultFilePath, tempFileProvider: tempFileProvider, dataHandler: dataHandler, deleteMetaInf: !alternativeSigning ); } } visualProgress?.HideIndeterminateLabel(); visualProgress?.HideBar(); SetStep(MainResources.AllDone, 6); Log(string.Empty); Log($"{MainResources.Path_to_file} {resultFilePath}"); _globalVariables.LatestModdedApkPath = resultFilePath; if (_settings.Notifications) { _notificationManager.Show( title: MainResources.Information_Title, text: MainResources.ModificationCompletedContent ); } string dialogResult = MessBox.ShowDial( $"{MainResources.Path_to_file} {resultFilePath}", MainResources.Successful, MainResources.OK, MainResources.Open, MainResources.Install ); if (dialogResult == MainResources.Open) { Process.Start("explorer.exe", $"/select,{resultFilePath}"); } else if (dialogResult == MainResources.Install) { uiThreadDispatcher.Invoke(() => _adbInstallWindowProvider.Get().ShowDialog()); } taskBarManager?.SetNoneState(); }
private void ProcessAll() { Dispatcher.Invoke(LogBox.Clear); Log( string.Format( "{0}{1}Start{1}{0}ExePath = {2}{0}Resources = {3}", Environment.NewLine, Line, _globalVariables.PathToExe, _globalVariables.PathToResources ) ); const int totalSteps = 3; _visualProgress.SetBarUsual(); _visualProgress.ShowBar(); _taskBarManager.SetProgress(0); _taskBarManager.SetUsualState(); void SetStep(int currentStep, string status) { int percentage = (currentStep - 1) * 100 / totalSteps; _visualProgress.SetBarValue(percentage); _visualProgress.SetLabelText(status); _taskBarManager.SetProgress(percentage); } #region Инициализация SetStep(1, MainResources.StepInitializing); _visualProgress.ShowIndeterminateLabel(); string sourceApkPath = ViewModel.CurrentApk.Value; bool alternativeSigning = _settings.AlternativeSigning; string popupText = ViewModel.PopupBoxText.Value; int messagesCount = ViewModel.MessagesCount.Value; bool needSave; bool needMessage; { bool onlySave = ViewModel.OnlySave.Value; bool savePlusMessage = ViewModel.SavePlusMess.Value; bool onlyMessage = ViewModel.OnlyMess.Value; needSave = onlySave || savePlusMessage; needMessage = (savePlusMessage || onlyMessage) && !string.IsNullOrEmpty(popupText) && messagesCount > 0; } BackupType backupType = ViewModel.BackupType.Value; ITempFileProvider tempFileProvider = _tempUtils.CreateTempFileProvider(); ITempFolderProvider tempFolderProvider = _tempUtils.CreateTempFolderProvider(); string resultApkPath = sourceApkPath.Remove(sourceApkPath.Length - Path.GetExtension(sourceApkPath).Length) + "_mod.apk"; string pathToSave = ViewModel.CurrentSave.Value; IApktool apktool = _apktoolProvider.Get(); IProcessDataHandler dataHandler = new ProcessDataCombinedHandler(data => Log(data)); #endregion #region Изменение apk using (var tempApk = ATempUtils.UseTempFile(tempFileProvider)) { LFile.Copy(sourceApkPath, tempApk.TempFile, true); #region Добавление данных SetStep(2, MainResources.StepAddingData); var aes = new AesManaged { KeySize = 128 }; aes.GenerateIV(); aes.GenerateKey(); bool backupFilesAdded = false; // adding local and external backup files if (needSave) { using (var internalDataBackup = ATempUtils.UseTempFile(tempFileProvider)) using (var externalDataBackup = ATempUtils.UseTempFile(tempFileProvider)) { ApkModifer.ParseBackup( pathToBackup: pathToSave, backupType: backupType, resultInternalDataPath: internalDataBackup.TempFile, resultExternalDataPath: externalDataBackup.TempFile, tempFolderProvider: tempFolderProvider ); string internalBackup = internalDataBackup.TempFile; string externalBackup = externalDataBackup.TempFile; var fileToAssetsName = new Dictionary <string, string> { { internalBackup, "data.save" }, { externalBackup, "extdata.save" } }; foreach (var(file, assetsName) in fileToAssetsName.Enumerate()) { if (!LFile.Exists(file) || FileUtils.FileLength(file) == 0) { continue; } using (var tempEncrypted = ATempUtils.UseTempFile(tempFileProvider)) { CommonUtils.EncryptFile( filePath: file, outputPath: tempEncrypted.TempFile, iv: aes.IV, key: aes.Key ); ApkModifer.AddFileToZip( zipPath: tempApk.TempFile, filePath: tempEncrypted.TempFile, pathInZip: "assets/" + assetsName, newEntryCompression: CompressionType.Store ); } backupFilesAdded = true; } } } // adding smali file for restoring if (backupFilesAdded || needMessage) { using (var decompiledFolder = ATempUtils.UseTempFolder(tempFolderProvider)) { apktool.Baksmali( apkPath: tempApk.TempFile, resultFolder: decompiledFolder.TempFolder, tempFolderProvider: tempFolderProvider, dataHandler: dataHandler ); var manifestPath = Path.Combine(decompiledFolder.TempFolder, "AndroidManifest.xml"); apktool.ExtractSimpleManifest( apkPath: tempApk.TempFile, resultManifestPath: manifestPath, tempFolderProvider: tempFolderProvider ); // have to have smali folders in the same directory as manifest // to find the main smali var manifest = new AndroidManifest(manifestPath); if (manifest.MainSmaliFile == null) { throw new Exception("main smali file not found"); } // using this instead of just pasting "folder/smali" as there can be // no smali folder sometimes (smali_1, etc) string smaliDir = manifest.MainSmaliPath.Substring(decompiledFolder.TempFolder.Length + 1); smaliDir = smaliDir.Substring(0, smaliDir.IndexOf(Path.DirectorySeparatorChar)); string saveGameDir = Path.Combine(decompiledFolder.TempFolder, smaliDir, "com", "savegame"); LDirectory.CreateDirectory(saveGameDir); CommonUtils.GenerateAndSaveSmali( filePath: Path.Combine(saveGameDir, "SavesRestoringPortable.smali"), iv: aes.IV, key: aes.Key, addSave: backupFilesAdded, message: needMessage ? popupText : string.Empty, messagesCount: needMessage ? messagesCount : 0 ); manifest.MainSmaliFile.AddTextToMethod(FileResources.MainSmaliCall); manifest.MainSmaliFile.Save(); using (var folderWithDexes = ATempUtils.UseTempFolder(tempFolderProvider)) { apktool.Smali( folderWithSmali: decompiledFolder.TempFolder, resultFolder: folderWithDexes.TempFolder, dataHandler: dataHandler ); string[] dexes = LDirectory.GetFiles(folderWithDexes.TempFolder, "*.dex"); ApkModifer.AddFilesToZip( zipPath: tempApk.TempFile, filePaths: dexes, pathsInZip: Array.ConvertAll(dexes, Path.GetFileName), newEntryCompression: CompressionType.Store ); } } } #endregion #region Подпись SetStep(3, MainResources.StepSigning); Log(Line); Log(MainResources.StepSigning); Log(Line); apktool.Sign( sourceApkPath: tempApk.TempFile, signedApkPath: resultApkPath, tempFileProvider: tempFileProvider, dataHandler: dataHandler, deleteMetaInf: !alternativeSigning ); #endregion } #endregion _visualProgress.HideIndeterminateLabel(); SetStep(4, MainResources.AllDone); Log(MainResources.AllDone); Log(string.Empty, false); Log($"{MainResources.Path_to_file} {resultApkPath}"); _globalVariables.LatestModdedApkPath = resultApkPath; if (_settings.Notifications) { _notificationManager.Show( title: MainResources.Information_Title, text: MainResources.ModificationCompletedContent ); } string dialogResult = MessBox.ShowDial( $"{MainResources.Path_to_file} {resultApkPath}", MainResources.Successful, MainResources.OK, MainResources.Open, MainResources.Install ); _visualProgress.HideBar(); _taskBarManager.SetNoneState(); if (dialogResult == MainResources.Open) { Process.Start("explorer.exe", $"/select,{resultApkPath}"); } else if (dialogResult == MainResources.Install) { Dispatcher.Invoke(() => _adbInstallWindowProvider.Get().ShowDialog()); } }
private void ProcessAll() { Dispatcher.Invoke(LogBox.Clear); Log( string.Format( "{0}{1}Start{1}{0}ExePath = {2}{0}Resources = {3}", Environment.NewLine, Line, _globalVariables.PathToExe, _globalVariables.PathToResources ) ); const int totalSteps = 3; _visualProgress.SetBarUsual(); _visualProgress.ShowBar(); _taskBarManager.SetProgress(0); _taskBarManager.SetUsualState(); void SetStep(int currentStep, string status) { int percentage = (currentStep - 1) * 100 / totalSteps; _visualProgress.SetBarValue(percentage); _visualProgress.SetLabelText(status); _taskBarManager.SetProgress(percentage); } #region Инициализация SetStep(1, MainResources.StepInitializing); _visualProgress.ShowIndeterminateLabel(); string sourceApkPath = ViewModel.CurrentApk.Value; bool alternativeSigning = _settings.AlternativeSigning; string popupText = ViewModel.PopupBoxText.Value; int messagesCount = ViewModel.MessagesCount.Value; bool needMessage = true; BackupType backupType = ViewModel.BackupType.Value; ITempFileProvider tempFileProvider = _tempUtils.CreateTempFileProvider(); ITempFolderProvider tempFolderProvider = _tempUtils.CreateTempFolderProvider(); string resultApkPath = sourceApkPath.Remove(sourceApkPath.Length - Path.GetExtension(sourceApkPath).Length) + "_signed.apk"; string pathToSave = ViewModel.CurrentSave.Value; IApktool apktool = _apktoolProvider.Get(); IProcessDataHandler dataHandler = new ProcessDataCombinedHandler(data => Log(data)); #endregion #region Change apk // Temp using (var tempApk = ATempUtils.UseTempFile(tempFileProvider)) { LFile.Copy(sourceApkPath, tempApk.TempFile, true); //Copy apk file to temp #region Adding data SetStep(2, MainResources.StepAddingData); var aes = new AesManaged { KeySize = 128 }; aes.GenerateIV(); aes.GenerateKey(); // adding smali file for restoring using (var decompiledFolder = ATempUtils.UseTempFolder(tempFolderProvider)) { apktool.Baksmali( apkPath: tempApk.TempFile, resultFolder: decompiledFolder.TempFolder, tempFolderProvider: tempFolderProvider, dataHandler: dataHandler ); var manifestPath = Path.Combine(decompiledFolder.TempFolder, "AndroidManifest.xml"); apktool.ExtractSimpleManifest( apkPath: tempApk.TempFile, resultManifestPath: manifestPath, tempFolderProvider: tempFolderProvider ); // have to have smali folders in the same directory as manifest // to find the main smali var manifest = new AndroidManifest(manifestPath); if (manifest.MainSmaliFile == null) { throw new Exception("main smali file not found"); } // using this instead of just pasting "folder/smali" as there can be // no smali folder sometimes (smali_1, etc) string smaliDir = manifest.MainSmaliPath.Substring(decompiledFolder.TempFolder.Length + 1); smaliDir = smaliDir.Substring(0, smaliDir.IndexOf(Path.DirectorySeparatorChar)); string saveGameDir = Path.Combine(decompiledFolder.TempFolder, smaliDir, "com", "savegame"); LDirectory.CreateDirectory(saveGameDir); //Encrypt smali CommonUtils.GenerateAndSaveSmali( filePath: Path.Combine(saveGameDir, "SavesRestoringPortable.smali"), message: needMessage ? popupText : string.Empty, messagesCount: needMessage ? messagesCount : 0 ); manifest.MainSmaliFile.AddTextToMethod(FileResources.MainSmaliCall); manifest.MainSmaliFile.Save(); using (var folderWithDexes = ATempUtils.UseTempFolder(tempFolderProvider)) { apktool.Smali( folderWithSmali: decompiledFolder.TempFolder, resultFolder: folderWithDexes.TempFolder, dataHandler: dataHandler ); string[] dexes = LDirectory.GetFiles(folderWithDexes.TempFolder, "*.dex"); ApkModifer.AddFilesToZip( zipPath: tempApk.TempFile, filePaths: dexes, pathsInZip: Array.ConvertAll(dexes, Path.GetFileName), newEntryCompression: CompressionType.Store ); } } #endregion #region Подпись SetStep(3, MainResources.StepSigning); Log(Line); Log(MainResources.StepSigning); Log(Line); LFile.Copy(tempApk.TempFile, Path.GetDirectoryName(sourceApkPath) + "\\" + Path.GetFileNameWithoutExtension(sourceApkPath) + "_unsigned.apk", true); //Copy apk file to temp //Signing //tempApk.TempFile "C:\\Users\\xxxxx\\AppData\\Local\\SaveToGame\\temp\\temp_entry_1" apktool.Sign( sourceApkPath: tempApk.TempFile, signedApkPath: resultApkPath, tempFileProvider: tempFileProvider, dataHandler: dataHandler, deleteMetaInf: !alternativeSigning ); #endregion } #endregion _visualProgress.HideIndeterminateLabel(); SetStep(4, MainResources.AllDone); Log(MainResources.AllDone); Log(string.Empty, false); Log($"{MainResources.Path_to_file} {resultApkPath}"); _globalVariables.LatestModdedApkPath = resultApkPath; if (_settings.Notifications) { _notificationManager.Show( title: MainResources.Information_Title, text: MainResources.ModificationCompletedContent ); } string dialogResult = MessBox.ShowDial( $"{MainResources.Path_to_file} {resultApkPath}", MainResources.Successful, MainResources.OK, MainResources.Open, MainResources.Install ); _visualProgress.HideBar(); _taskBarManager.SetNoneState(); //Dialog result if (dialogResult == MainResources.Open) { Process.Start("explorer.exe", $"/select,{resultApkPath}"); } else if (dialogResult == MainResources.Install) { Dispatcher.Invoke(() => _adbInstallWindowProvider.Get().ShowDialog()); } }