Exemple #1
0
        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());
            }
        }