public DecompileCache(ProjectFile pf)
        {
            Types = new AssetResolverTypes(pf);
            GlobalFunctionNames = new Dictionary <GMFunctionEntry, string>();
            if (pf.DataHandle.VersionInfo.IsNumberAtLeast(2, 3))
            {
                // Find all function names in global scripts
                GMChunkCODE code = pf.DataHandle.GetChunk <GMChunkCODE>();
                Parallel.ForEach(pf.DataHandle.GetChunk <GMChunkGLOB>().List, scr =>
                {
                    GMCode entry = code.List[scr];

                    // Find fragments
                    List <Fragment> fragments = Fragments.FindAndProcess(entry, false);

                    // Find blocks in the main fragment that come after another fagment
                    foreach (Block b in fragments[^ 1].Blocks.List)
                    {
                        if (b.AfterFragment && b.Instructions.Count > 2 &&
                            b.Instructions[0].Kind == GMCode.Bytecode.Instruction.Opcode.Push &&
                            b.Instructions[0].Type1 == GMCode.Bytecode.Instruction.DataType.Int32 &&
                            b.Instructions[0].Value == null)
                        {
                            string name = ASTBuilder.GetNameAfterFragment(b);
                            if (name != null)
                            {
                                GMFunctionEntry func = b.Instructions[0].Function.Target;
                                lock (GlobalFunctionNames)
                                    GlobalFunctionNames[func] = name;
                            }
                        }
                    }
                });