Ejemplo n.º 1
0
        static void EnsureInstalled(
            Apk apk,
            SerializedAssets assets,
            HashSet <string> existingLevels,
            InvocationResult res,
            Dictionary <string, string> ensureInstalled
            )
        {
            LevelCollectionBehaviorData extrasCollection = assets.FindExtrasLevelCollection();

            foreach (KeyValuePair <string, string> entry in ensureInstalled)
            {
                string levelID     = entry.Key;
                string levelFolder = entry.Value;
                try {
                    JsonLevel level = JsonLevel.LoadFromFolder(levelFolder);
                    if (existingLevels.Contains(levelID))
                    {
                        res.installSkipped.Add(levelID, "Present");
                    }
                    else
                    {
                        // We use transactions here so if these throw
                        // an exception, which happens when levels are
                        // invalid, then it doesn't modify the APK in
                        // any way that might screw things up later.
                        var      assetsTxn = new SerializedAssets.Transaction(assets);
                        var      apkTxn    = new Apk.Transaction();
                        AssetPtr levelPtr  = level.AddToAssets(assetsTxn, apkTxn, levelID);

                        // Danger should be over, nothing here should fail
                        assetsTxn.ApplyTo(assets);
                        extrasCollection.levels.Add(levelPtr);
                        apkTxn.ApplyTo(apk);

                        existingLevels.Add(levelID);
                        res.installedLevels.Add(levelID);
                    }
                } catch (FileNotFoundException e) {
                    res.installSkipped.Add(levelID, $"Missing file referenced by level: {e.FileName}");
                } catch (JsonReaderException e) {
                    res.installSkipped.Add(levelID, $"Invalid level JSON: {e.Message}");
                }
            }
        }
Ejemplo n.º 2
0
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("arguments: pathToAPKFileToModify levelFolders...");
                return;
            }
            string apkPath = args[0];

            using (Apk apk = new Apk(apkPath)) {
                apk.PatchSignatureCheck();

                byte[]           data   = apk.ReadEntireEntry(Apk.MainAssetsFile);
                SerializedAssets assets = SerializedAssets.FromBytes(data);

                HashSet <string>            existingLevels   = assets.ExistingLevelIDs();
                LevelCollectionBehaviorData extrasCollection = assets.FindExtrasLevelCollection();
                for (int i = 1; i < args.Length; i++)
                {
                    Utils.FindLevels(args[i], levelFolder => {
                        JsonLevel level = JsonLevel.LoadFromFolder(levelFolder);
                        string levelID  = level.LevelID();
                        if (existingLevels.Contains(levelID))
                        {
                            Console.WriteLine($"Present: {level._songName}");
                        }
                        else
                        {
                            Console.WriteLine($"Adding:  {level._songName}");
                            AssetPtr levelPtr = level.AddToAssets(assets, apk);
                            extrasCollection.levels.Add(levelPtr);
                            existingLevels.Add(levelID);
                        }
                    });
                }

                byte[] outData = assets.ToBytes();
                apk.ReplaceAssetsFile(Apk.MainAssetsFile, outData);
            }
        }
Ejemplo n.º 3
0
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("arguments: pathToAPKFileToModify levelFolders...");
                return;
            }
            string apkPath = args[0];

            using (Apk apk = new Apk(apkPath)) {
                apk.PatchSignatureCheck();

                byte[]           data   = apk.ReadEntireEntry(apk.MainAssetsFile());
                SerializedAssets assets = SerializedAssets.FromBytes(data, apk.version);

                HashSet <string>            existingLevels   = assets.ExistingLevelIDs();
                LevelCollectionBehaviorData extrasCollection = assets.FindExtrasLevelCollection();
                for (int i = 1; i < args.Length; i++)
                {
                    Utils.FindLevels(args[i], levelFolder => {
                        try {
                            JsonLevel level = JsonLevel.LoadFromFolder(levelFolder);
                            string levelID  = level.GenerateBasicLevelID();
                            if (existingLevels.Contains(levelID))
                            {
                                Console.WriteLine($"Present: {level._songName}");
                            }
                            else
                            {
                                Console.WriteLine($"Adding:  {level._songName}");
                                // We use transactions here so if these throw
                                // an exception, which happens when levels are
                                // invalid, then it doesn't modify the APK in
                                // any way that might screw things up later.
                                var assetsTxn     = new SerializedAssets.Transaction(assets);
                                var apkTxn        = new Apk.Transaction();
                                AssetPtr levelPtr = level.AddToAssets(assetsTxn, apkTxn, levelID);

                                // Danger should be over, nothing here should fail
                                assetsTxn.ApplyTo(assets);
                                extrasCollection.levels.Add(levelPtr);
                                existingLevels.Add(levelID);
                                apkTxn.ApplyTo(apk);
                            }
                        } catch (FileNotFoundException e) {
                            Console.WriteLine("[SKIPPING] Missing file referenced by level: {0}", e.FileName);
                        } catch (JsonReaderException e) {
                            Console.WriteLine("[SKIPPING] Invalid level JSON: {0}", e.Message);
                        }
                    });
                }

                byte[] outData = assets.ToBytes();
                apk.ReplaceAssetsFile(apk.MainAssetsFile(), outData);

                apk.Save();
            }

            Console.WriteLine("Signing APK...");
            Signer.Sign(apkPath);
        }
Ejemplo n.º 4
0
        static void SyncLevels(
            Apk apk,
            SerializedAssets mainAssets,
            Invocation inv,
            InvocationResult res
            )
        {
            if (inv.levels == null || inv.packs == null)
            {
                throw new ApplicationException("Either the 'levels' or 'packs' key is missing. Note the 'levels' key changed names from 'ensureInstalled' in the new version.");
            }

            Dictionary <string, ulong> existingLevels = mainAssets.FindLevels();
            ulong maxBasePathID = mainAssets.MainAssetsMaxBaseGamePath();

            // === Load root level pack
            SerializedAssets rootPackAssets = SerializedAssets.FromBytes(apk.ReadEntireEntry(apk.RootPackFile()), apk.version);
            string           mainFileName   = apk.MainAssetsFileName();
            int mainFileI = rootPackAssets.externals.FindIndex(e => e.pathName == mainFileName) + 1;
            BeatmapLevelPackCollection rootLevelPack = rootPackAssets.FindMainLevelPackCollection();
            // Might be null if we're on a version older than v1.1.0
            AlwaysOwnedBehaviorData alwaysOwned = mainAssets.FindScript <AlwaysOwnedBehaviorData>(x => true);

            // === Remove existing custom packs
            rootLevelPack.beatmapLevelPacks.RemoveAll(ptr => ptr.fileID == mainFileI && ptr.pathID > maxBasePathID);
            if (alwaysOwned != null)
            {
                alwaysOwned.levelPacks.RemoveAll(ptr => ptr.fileID == 0 && ptr.pathID > maxBasePathID);
            }
            LevelPackBehaviorData.RemoveCustomPacksFromEnd(mainAssets);

            // === Remove old-school custom levels from Extras pack
            var extrasCollection = mainAssets.FindExtrasLevelCollection();

            extrasCollection.levels.RemoveAll(ptr => ptr.pathID > maxBasePathID);

            // === Remove existing levels
            var toRemove = new HashSet <string>();

            foreach (var entry in existingLevels)
            {
                if (inv.levels.ContainsKey(entry.Key))
                {
                    continue;                                   // requested
                }
                if (entry.Value <= maxBasePathID)
                {
                    continue;                              // base game level
                }
                toRemove.Add(entry.Key);
            }
            foreach (string levelID in toRemove)
            {
                var ao     = mainAssets.GetAssetObjectFromScript <LevelBehaviorData>(p => p.levelID == levelID);
                var apkTxn = new Apk.Transaction();
                Utils.RemoveLevel(mainAssets, ao, apkTxn);
                apkTxn.ApplyTo(apk);
            }
            res.removedLevels = toRemove.ToList();

            // === Install new levels
            var toInstall = new HashSet <string>();

            foreach (var entry in inv.levels)
            {
                if (existingLevels.ContainsKey(entry.Key))
                {
                    continue;                                       // already installed
                }
                toInstall.Add(entry.Key);
            }
            Program.Install(apk, mainAssets, toInstall, res, inv.levels);

            // === Create new custom packs
            Dictionary <string, ulong> availableLevels = mainAssets.FindLevels();

            foreach (LevelPack pack in inv.packs)
            {
                if (pack.name == null || pack.id == null || pack.levelIDs == null)
                {
                    throw new ApplicationException("Packs require name, id and levelIDs list");
                }
                var            txn  = new SerializedAssets.Transaction(mainAssets);
                CustomPackInfo info = LevelPackBehaviorData.CreateCustomPack(
                    txn, pack.id, pack.name, pack.coverImagePath
                    );
                txn.ApplyTo(mainAssets);

                var customCollection = info.collection.FollowToScript <LevelCollectionBehaviorData>(mainAssets);
                foreach (string levelID in pack.levelIDs)
                {
                    ulong levelPathID;
                    if (!availableLevels.TryGetValue(levelID, out levelPathID))
                    {
                        res.missingFromPacks.Add(levelID);
                        continue;
                    }
                    customCollection.levels.Add(new AssetPtr(0, levelPathID));
                }

                rootLevelPack.beatmapLevelPacks.Add(new AssetPtr(mainFileI, info.pack.pathID));
                if (alwaysOwned != null)
                {
                    alwaysOwned.levelPacks.Add(info.pack);
                }
            }
            res.presentLevels = availableLevels.Keys.ToList();

            apk.ReplaceAssetsFile(apk.RootPackFile(), rootPackAssets.ToBytes());
        }