Esempio n. 1
0
        public Array32 <uint> VaryingLocations; // Making this readonly breaks AsSpan

        /// <summary>
        /// Creates a new transform feedback descriptor.
        /// </summary>
        /// <param name="bufferIndex">Index of the transform feedback</param>
        /// <param name="stride">Amount of bytes consumed per vertex</param>
        /// <param name="varyingCount">Number of varyings written into the buffer. Indicates size in bytes of <paramref name="varyingLocations"/></param>
        /// <param name="varyingLocations">Location of varyings to be written into the buffer. Each byte is one location</param>
        public TransformFeedbackDescriptor(int bufferIndex, int stride, int varyingCount, ref Array32 <uint> varyingLocations)
        {
            BufferIndex      = bufferIndex;
            Stride           = stride;
            VaryingCount     = varyingCount;
            VaryingLocations = varyingLocations;
        }
Esempio n. 2
0
        public static void ResizeArray32 <T>(Array32 <T> array, uint newSize)
        {
            array.m_size = newSize;
            Array.Resize(ref array.m_buffer, (int)newSize);
            var unusedCount = ReflectionUtils.GetField <uint>(array, "m_unusedCount");
            var unusedItems = ReflectionUtils.GetField <uint[]>(array, "m_unusedItems");

            uint[] newUnusedItems = new uint[newSize];
            Buffer.BlockCopy(unusedItems, 0, newUnusedItems, 0, 4 * unusedItems.Length);

            // Now add our own unused items
            for (uint i = (uint)unusedItems.Length; i < newSize + 1; i++)
            {
                newUnusedItems[i - 1] = i;
            }

            // Update the unusedCount to be in line with the new array size
            // This is just adding the newly sized additions.
            unusedCount += newSize - unusedCount;

            ReflectionUtils.SetField(array, "m_unusedCount", unusedCount);
            ReflectionUtils.SetField(array, "m_unusedItems", unusedItems);

            // var nextFree = ReflectionUtils.InvokeMethod<uint>(array, "NextFreeItem");
            // var nextFree = array.NextFreeItem();
        }
        protected override void Awake()
        {
            base.Awake();
            this.m_zombies         = new Array32 <Zombie>(1048576u);
            this.m_instances       = new Array16 <ZombieInstance>(65536u);
            this.m_zombieGrid      = new ushort[4665600];
            this.m_renderBuffer    = new ulong[1024];
            this.m_materialBlock   = new MaterialPropertyBlock();
            this.ID_Color          = Shader.PropertyToID("_Color");
            this.ID_Speed          = Animator.StringToHash("Speed");
            this.ID_State          = Animator.StringToHash("State");
            this.ID_ZombieLocation = new int[16];
            this.ID_ZombieColor    = new int[16];
            for (int i = 0; i < 16; i++)
            {
                this.ID_ZombieLocation[i] = Shader.PropertyToID("_CitizenLocation" + i);
                this.ID_ZombieColor[i]    = Shader.PropertyToID("_CitizenColor" + i);
            }
            this.m_zombieLayer = LayerMask.NameToLayer("Citizens");
            this.m_audioGroup  = new AudioGroup(5, new SavedFloat(Settings.effectAudioVolume, Settings.gameSettingsFile, DefaultSettings.effectAudioVolume, true));
            uint num;

            this.m_zombies.CreateItem(out num);
            ushort num2;

            this.m_instances.CreateItem(out num2);
        }
Esempio n. 4
0
        private static void FixBrokenTrees()
        {
            var brokenCount = 0;

            // Fix broken trees
            Array32 <TreeInstance> trees = TreeManager.instance.m_trees;

            for (int i = 0; i < trees.m_size; i++)
            {
                if (trees.m_buffer[i].Info == null)
                {
                    try
                    {
                        TreeManager.instance.ReleaseTree((ushort)i);
                        brokenCount++;
                    }
                    catch (Exception e)
                    {
                        UnityEngine.Debug.LogException(e);
                    }
                }
            }

            if (brokenCount > 0)
            {
                Debug.Log("Removed " + brokenCount + " broken tree instances.");
            }
        }
Esempio n. 5
0
 public static IEnumerable <T> ToSeq <T>(this Array32 <T> arr)
 {
     for (int i = 0; i < arr.m_size; i++)
     {
         yield return(arr.m_buffer[i]);
     }
 }
Esempio n. 6
0
 /// <summary>
 /// Creates a new GPU graphics state.
 /// </summary>
 /// <param name="earlyZForce">Early Z force enable</param>
 /// <param name="topology">Primitive topology</param>
 /// <param name="tessellationMode">Tessellation mode</param>
 /// <param name="alphaToCoverageEnable">Indicates whether alpha-to-coverage is enabled</param>
 /// <param name="alphaToCoverageDitherEnable">Indicates whether alpha-to-coverage dithering is enabled</param>
 /// <param name="viewportTransformDisable">Indicates whether the viewport transform is disabled</param>
 /// <param name="depthMode">Depth mode zero to one or minus one to one</param>
 /// <param name="programPointSizeEnable">Indicates if the point size is set on the shader or is fixed</param>
 /// <param name="pointSize">Point size if not set from shader</param>
 /// <param name="alphaTestEnable">Indicates whether alpha test is enabled</param>
 /// <param name="alphaTestCompare">When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded</param>
 /// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param>
 /// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
 public GpuChannelGraphicsState(
     bool earlyZForce,
     PrimitiveTopology topology,
     TessMode tessellationMode,
     bool alphaToCoverageEnable,
     bool alphaToCoverageDitherEnable,
     bool viewportTransformDisable,
     bool depthMode,
     bool programPointSizeEnable,
     float pointSize,
     bool alphaTestEnable,
     CompareOp alphaTestCompare,
     float alphaTestReference,
     ref Array32 <AttributeType> attributeTypes)
 {
     EarlyZForce                 = earlyZForce;
     Topology                    = topology;
     TessellationMode            = tessellationMode;
     AlphaToCoverageEnable       = alphaToCoverageEnable;
     AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable;
     ViewportTransformDisable    = viewportTransformDisable;
     DepthMode                   = depthMode;
     ProgramPointSizeEnable      = programPointSizeEnable;
     PointSize                   = pointSize;
     AlphaTestEnable             = alphaTestEnable;
     AlphaTestCompare            = alphaTestCompare;
     AlphaTestReference          = alphaTestReference;
     AttributeTypes              = attributeTypes;
 }
Esempio n. 7
0
        public static void IterateLinkedList <T>(this Array32 <T> arr, uint startIndex, NextIndex32 <T> nextIndex, RefAction <T> action) where T : struct
        {
            var cur = startIndex;

            while (cur != 0)
            {
                action(cur, ref arr.m_buffer[cur]);
                cur = nextIndex(ref arr.m_buffer[cur]);
            }
        }
		private static void EndRenderingImpl(TreeManager tm, RenderManager.CameraInfo cameraInfo)
		{
			unsafe
			{
				if (Input.GetKeyDown(KeyCode.F5) && Event.current.control)
				{
					DebugOutputPanel.AddMessage(PluginManager.MessageType.Message, string.Format("TreeLimit: TreeCount={0}, TreeLimit={1}, CanPlaceMoreTrees={2}", tm.m_treeCount, LimitTreeManager.Helper.TreeLimit, tm.CheckLimits()));
					Array32<TreeInstance> mTrees = tm.m_trees;
					DebugOutputPanel.AddMessage(PluginManager.MessageType.Message, string.Format("TreeLimit: ArraySize={0}, ItemCount={1}, UnusedCount={2}", mTrees.m_size, mTrees.ItemCount(), mTrees.GetType().GetField("m_unusedCount", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(mTrees)));
				}
				FastList<RenderGroup> mRenderedGroups = Singleton<RenderManager>.instance.m_renderedGroups;
				for (int i = 0; i < mRenderedGroups.m_size; i++)
				{
					RenderGroup mBuffer = mRenderedGroups.m_buffer[i];
					if ((mBuffer.m_instanceMask & 1 << (tm.m_treeLayer & 31)) != 0)
					{
						int mX = mBuffer.m_x * 540 / 45;
						int mZ = mBuffer.m_z * 540 / 45;
						int num = (mBuffer.m_x + 1) * 540 / 45 - 1;
						int mZ1 = (mBuffer.m_z + 1) * 540 / 45 - 1;
						for (int j = mZ; j <= mZ1; j++)
						{
							for (int k = mX; k <= num; k++)
							{
								uint mTreeGrid = tm.m_treeGrid[j * 540 + k];
								int num1 = 0;
								while (mTreeGrid != 0)
								{
									tm.m_trees.m_buffer[mTreeGrid].RenderInstance(cameraInfo, mTreeGrid, mBuffer.m_instanceMask);
									mTreeGrid = tm.m_trees.m_buffer[mTreeGrid].m_nextGridTree;
									int num2 = num1 + 1;
									num1 = num2;
									if (num2 < LimitTreeManager.Helper.TreeLimit)
									{
										continue;
									}
									CODebugBase<LogChannel>.Error(LogChannel.Core, string.Concat("Invalid list detected!\n", Environment.StackTrace));
									break;
								}
							}
						}
					}
				}
				int num3 = PrefabCollection<TreeInfo>.PrefabCount();
				for (int l = 0; l < num3; l++)
				{
					TreeInfo prefab = PrefabCollection<TreeInfo>.GetPrefab((uint)l);
					if (prefab != null && prefab.m_lodCount != 0)
					{
						TreeInstance.RenderLod(cameraInfo, prefab);
					}
				}
			}
		}
        private static void DestroyTrees(int seed, InstanceManager.Group group, Vector3 position, float totalRadius, float removeRadius, float destructionRadiusMin, float destructionRadiusMax, float burnRadiusMin, float burnRadiusMax)
        {
            int num  = Mathf.Max((int)((position.x - totalRadius) / 32f + 270f), 0);
            int num2 = Mathf.Max((int)((position.z - totalRadius) / 32f + 270f), 0);
            int num3 = Mathf.Min((int)((position.x + totalRadius) / 32f + 270f), 539);
            int num4 = Mathf.Min((int)((position.z + totalRadius) / 32f + 270f), 539);
            Array32 <global::TreeInstance> trees = Singleton <TreeManager> .instance.m_trees;

            uint[] treeGrid = Singleton <TreeManager> .instance.m_treeGrid;
            for (int i = num2; i <= num4; i++)
            {
                for (int j = num; j <= num3; j++)
                {
                    uint num5 = treeGrid[i * 540 + j];
                    int  num6 = 0;
                    while (num5 != 0u)
                    {
                        uint nextGridTree = trees.m_buffer[(int)((UIntPtr)num5)].m_nextGridTree;
                        global::TreeInstance.Flags flags = (global::TreeInstance.Flags)trees.m_buffer[(int)((UIntPtr)num5)].m_flags;
                        if ((flags & (global::TreeInstance.Flags.Created | global::TreeInstance.Flags.Deleted)) == global::TreeInstance.Flags.Created)
                        {
                            Vector3 position2 = trees.m_buffer[(int)((UIntPtr)num5)].Position;
                            float   num7      = VectorUtils.LengthXZ(position2 - position);
                            if (num7 < totalRadius)
                            {
                                Randomizer randomizer = new Randomizer(num5 | (uint)((uint)seed << 16));
                                float      num8       = (burnRadiusMax - num7) / Mathf.Max(1f, burnRadiusMax - burnRadiusMin);
                                bool       flag       = num7 < removeRadius;
                                bool       flag2      = (float)randomizer.Int32(1000u) < num8 * 1000f;
                                if (flag)
                                {
                                    Singleton <TreeManager> .instance.ReleaseTree(num5);
                                }
                                else if (flag2 && (flags & global::TreeInstance.Flags.FireDamage) == global::TreeInstance.Flags.None)
                                {
                                    Singleton <TreeManager> .instance.BurnTree(num5, group, 128);
                                }
                            }
                        }
                        num5 = nextGridTree;
                        if (++num6 >= LimitTreeManager.Helper.TreeLimit)
                        {
                            CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);

                            break;
                        }
                    }
                }
            }
        }
Esempio n. 10
0
        private static void LocateNextTreeInstance(PrefabInfo prefab)
        {
            Array32 <TreeInstance> trees = TreeManager.instance.m_trees;

            for (uint i = (treeInstanceCounter + 1) % trees.m_size; i != treeInstanceCounter; i = (i + 1) % trees.m_size)
            {
                if (trees.m_buffer[i].Info == prefab)
                {
                    bool isValid = ((TreeInstance.Flags)trees.m_buffer[i].m_flags != TreeInstance.Flags.None && (TreeInstance.Flags)trees.m_buffer[i].m_flags != TreeInstance.Flags.Deleted);
                    if (!isValid)
                    {
                        continue;
                    }
                    SetCameraPosition(trees.m_buffer[i].Position);
                    treeInstanceCounter = (i + 1) % trees.m_size;
                    return;
                }
            }
            treeInstanceCounter = 0;
        }
Esempio n. 11
0
        public NsoExecutable(IStorage inStorage, string name = null)
        {
            NsoReader reader = new NsoReader();

            reader.Initialize(inStorage.AsFile(OpenMode.Read)).ThrowIfFailure();

            TextOffset = reader.Header.Segments[0].MemoryOffset;
            RoOffset   = reader.Header.Segments[1].MemoryOffset;
            DataOffset = reader.Header.Segments[2].MemoryOffset;
            BssSize    = reader.Header.BssSize;

            reader.GetSegmentSize(NsoReader.SegmentType.Data, out uint uncompressedSize).ThrowIfFailure();

            Program = new byte[DataOffset + uncompressedSize];

            TextSize = DecompressSection(reader, NsoReader.SegmentType.Text, TextOffset);
            RoSize   = DecompressSection(reader, NsoReader.SegmentType.Ro, RoOffset);
            DataSize = DecompressSection(reader, NsoReader.SegmentType.Data, DataOffset);

            Name    = name;
            BuildId = reader.Header.ModuleId;

            PrintRoSectionInfo();
        }
Esempio n. 12
0
 public static void RemoveUnused <T>(this Array32 <T> arr, uint id)
 {
     ArrayXHelper <Array32 <T>, uint> .RemoveUnused(arr, id, "Array32");
 }
        public virtual void OnAwake()
        {
            this.m_laneLocation = new uint[262144];
            this.m_laneTarget = new PathUnit.Position[262144];
            this.m_buffer = new BufferItem[65536];
            this.m_bufferMin = new int[1024];
            this.m_bufferMax = new int[1024];
            this.m_queueLock = new object();
            this.m_bufferLock = Singleton<PathManager>.instance.m_bufferLock;
            this.m_pathUnits = Singleton<PathManager>.instance.m_pathUnits;
            this.m_pathFindThread = new Thread(new ThreadStart(this.PathFindThread));
            this.m_pathFindThread.Name = "Pathfind";
            this.m_pathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;
            this.m_pathFindThread.Start();
            if (!this.m_pathFindThread.IsAlive)
            {
                CODebugBase<LogChannel>.Error(LogChannel.Core, "Path find thread failed to start!");
            }
		}
Esempio n. 14
0
        /// <summary>
        /// Migrates from the old cache format to the new one.
        /// </summary>
        /// <param name="context">GPU context</param>
        /// <param name="hostStorage">Disk cache host storage (used to create the new shader files)</param>
        /// <returns>Number of migrated shaders</returns>
        public static int MigrateFromLegacyCache(GpuContext context, DiskCacheHostStorage hostStorage)
        {
            string baseCacheDirectory = CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId);
            string cacheDirectory     = CacheHelper.GenerateCachePath(baseCacheDirectory, CacheGraphicsApi.Guest, "", "program");

            // If the directory does not exist, we have no old cache.
            // Exist early as the CacheManager constructor will create the directories.
            if (!Directory.Exists(cacheDirectory))
            {
                return(0);
            }

            if (GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null)
            {
                CacheManager cacheManager = new CacheManager(CacheGraphicsApi.OpenGL, CacheHashType.XxHash128, "glsl", GraphicsConfig.TitleId, ShaderCodeGenVersion);

                bool isReadOnly = cacheManager.IsReadOnly;

                HashSet <Hash128> invalidEntries = null;

                if (isReadOnly)
                {
                    Logger.Warning?.Print(LogClass.Gpu, "Loading shader cache in read-only mode (cache in use by another program!)");
                }
                else
                {
                    invalidEntries = new HashSet <Hash128>();
                }

                ReadOnlySpan <Hash128> guestProgramList = cacheManager.GetGuestProgramList();

                for (int programIndex = 0; programIndex < guestProgramList.Length; programIndex++)
                {
                    Hash128 key = guestProgramList[programIndex];

                    byte[] guestProgram = cacheManager.GetGuestProgramByHash(ref key);

                    if (guestProgram == null)
                    {
                        Logger.Error?.Print(LogClass.Gpu, $"Ignoring orphan shader hash {key} in cache (is the cache incomplete?)");

                        continue;
                    }

                    ReadOnlySpan <byte> guestProgramReadOnlySpan = guestProgram;

                    ReadOnlySpan <GuestShaderCacheEntry> cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader);

                    if (cachedShaderEntries[0].Header.Stage == ShaderStage.Compute)
                    {
                        Debug.Assert(cachedShaderEntries.Length == 1);

                        GuestShaderCacheEntry entry = cachedShaderEntries[0];

                        byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray();

                        Span <byte> codeSpan = entry.Code;
                        byte[]      cb1Data  = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray();

                        ShaderProgramInfo info = new ShaderProgramInfo(
                            Array.Empty <BufferDescriptor>(),
                            Array.Empty <BufferDescriptor>(),
                            Array.Empty <TextureDescriptor>(),
                            Array.Empty <TextureDescriptor>(),
                            ShaderStage.Compute,
                            false,
                            false,
                            0,
                            0);

                        GpuChannelComputeState computeState = new GpuChannelComputeState(
                            entry.Header.GpuAccessorHeader.ComputeLocalSizeX,
                            entry.Header.GpuAccessorHeader.ComputeLocalSizeY,
                            entry.Header.GpuAccessorHeader.ComputeLocalSizeZ,
                            entry.Header.GpuAccessorHeader.ComputeLocalMemorySize,
                            entry.Header.GpuAccessorHeader.ComputeSharedMemorySize);

                        ShaderSpecializationState specState = new ShaderSpecializationState(computeState);

                        foreach (var td in entry.TextureDescriptors)
                        {
                            var handle = td.Key;
                            var data   = td.Value;

                            specState.RegisterTexture(
                                0,
                                handle,
                                -1,
                                data.UnpackFormat(),
                                data.UnpackSrgb(),
                                data.UnpackTextureTarget(),
                                data.UnpackTextureCoordNormalized());
                        }

                        CachedShaderStage   shader  = new CachedShaderStage(info, code, cb1Data);
                        CachedShaderProgram program = new CachedShaderProgram(null, specState, shader);

                        hostStorage.AddShader(context, program, ReadOnlySpan <byte> .Empty);
                    }
                    else
                    {
                        Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages);

                        CachedShaderStage[]  shaders        = new CachedShaderStage[Constants.ShaderStages + 1];
                        List <ShaderProgram> shaderPrograms = new List <ShaderProgram>();

                        TransformFeedbackDescriptorOld[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader);

                        GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray();

                        GuestGpuAccessorHeader accessorHeader = entries[0].Header.GpuAccessorHeader;

                        TessMode tessMode = new TessMode();

                        int  tessPatchType = accessorHeader.TessellationModePacked & 3;
                        int  tessSpacing   = (accessorHeader.TessellationModePacked >> 2) & 3;
                        bool tessCw        = (accessorHeader.TessellationModePacked & 0x10) != 0;

                        tessMode.Packed  = (uint)tessPatchType;
                        tessMode.Packed |= (uint)(tessSpacing << 4);

                        if (tessCw)
                        {
                            tessMode.Packed |= 0x100;
                        }

                        PrimitiveTopology topology = accessorHeader.PrimitiveTopology switch
                        {
                            InputTopology.Lines => PrimitiveTopology.Lines,
                            InputTopology.LinesAdjacency => PrimitiveTopology.LinesAdjacency,
                            InputTopology.Triangles => PrimitiveTopology.Triangles,
                            InputTopology.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency,
                            _ => PrimitiveTopology.Points
                        };

                        GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState(
                            accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
                            topology,
                            tessMode);

                        TransformFeedbackDescriptor[] tfdNew = null;

                        if (tfd != null)
                        {
                            tfdNew = new TransformFeedbackDescriptor[tfd.Length];

                            for (int tfIndex = 0; tfIndex < tfd.Length; tfIndex++)
                            {
                                Array32 <uint> varyingLocations     = new Array32 <uint>();
                                Span <byte>    varyingLocationsSpan = MemoryMarshal.Cast <uint, byte>(varyingLocations.ToSpan());
                                tfd[tfIndex].VaryingLocations.CopyTo(varyingLocationsSpan.Slice(0, tfd[tfIndex].VaryingLocations.Length));

                                tfdNew[tfIndex] = new TransformFeedbackDescriptor(
                                    tfd[tfIndex].BufferIndex,
                                    tfd[tfIndex].Stride,
                                    tfd[tfIndex].VaryingLocations.Length,
                                    ref varyingLocations);
                            }
                        }

                        ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, tfdNew);

                        for (int i = 0; i < entries.Length; i++)
                        {
                            GuestShaderCacheEntry entry = entries[i];

                            if (entry == null)
                            {
                                continue;
                            }

                            ShaderProgramInfo info = new ShaderProgramInfo(
                                Array.Empty <BufferDescriptor>(),
                                Array.Empty <BufferDescriptor>(),
                                Array.Empty <TextureDescriptor>(),
                                Array.Empty <TextureDescriptor>(),
                                (ShaderStage)(i + 1),
                                false,
                                false,
                                0,
                                0);

                            // NOTE: Vertex B comes first in the shader cache.
                            byte[] code  = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray();
                            byte[] code2 = entry.Header.SizeA != 0 ? entry.Code.AsSpan(entry.Header.Size, entry.Header.SizeA).ToArray() : null;

                            Span <byte> codeSpan = entry.Code;
                            byte[]      cb1Data  = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray();

                            shaders[i + 1] = new CachedShaderStage(info, code, cb1Data);

                            if (code2 != null)
                            {
                                shaders[0] = new CachedShaderStage(null, code2, cb1Data);
                            }

                            foreach (var td in entry.TextureDescriptors)
                            {
                                var handle = td.Key;
                                var data   = td.Value;

                                specState.RegisterTexture(
                                    i,
                                    handle,
                                    -1,
                                    data.UnpackFormat(),
                                    data.UnpackSrgb(),
                                    data.UnpackTextureTarget(),
                                    data.UnpackTextureCoordNormalized());
                            }
                        }

                        CachedShaderProgram program = new CachedShaderProgram(null, specState, shaders);

                        hostStorage.AddShader(context, program, ReadOnlySpan <byte> .Empty);
                    }
                }

                return(guestProgramList.Length);
            }

            return(0);
        }
    }
Esempio n. 15
0
        private static IEnumerator FixEnumerableThread(ThreadBase t)
        {
            SimulationManager.instance.ForcedSimulationPaused = true;

            try
            {
                uint brokenCount   = 0;
                uint confusedCount = 0;

                // Fix broken offers
                TransferManager.TransferOffer[] incomingOffers = typeof(TransferManager).GetField("m_incomingOffers", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(TransferManager.instance) as TransferManager.TransferOffer[];
                TransferManager.TransferOffer[] outgoingOffers = typeof(TransferManager).GetField("m_outgoingOffers", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(TransferManager.instance) as TransferManager.TransferOffer[];

                ushort[] incomingCount = typeof(TransferManager).GetField("m_incomingCount", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(TransferManager.instance) as ushort[];
                ushort[] outgoingCount = typeof(TransferManager).GetField("m_outgoingCount", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(TransferManager.instance) as ushort[];

                int[] incomingAmount = typeof(TransferManager).GetField("m_incomingAmount", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(TransferManager.instance) as int[];
                int[] outgoingAmount = typeof(TransferManager).GetField("m_outgoingAmount", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(TransferManager.instance) as int[];

                // Based on TransferManager.RemoveAllOffers
                for (int i = 0; i < 64; i++)
                {
                    for (int j = 0; j < 8; j++)
                    {
                        int num  = i * 8 + j;
                        int num2 = (int)incomingCount[num];
                        for (int k = num2 - 1; k >= 0; k--)
                        {
                            int num3 = num * 256 + k;
                            if (IsInfoNull(incomingOffers[num3]))
                            {
                                incomingAmount[i]   -= incomingOffers[num3].Amount;
                                incomingOffers[num3] = incomingOffers[--num2];
                                brokenCount++;
                            }
                        }
                        incomingCount[num] = (ushort)num2;
                        int num4 = (int)outgoingCount[num];
                        for (int l = num4 - 1; l >= 0; l--)
                        {
                            int num5 = num * 256 + l;
                            if (IsInfoNull(outgoingOffers[num5]))
                            {
                                outgoingAmount[i]   -= outgoingOffers[num5].Amount;
                                outgoingOffers[num5] = outgoingOffers[--num4];
                                brokenCount++;
                            }
                        }
                        outgoingCount[num] = (ushort)num4;
                    }

                    yield return(null);
                }

                if (brokenCount > 0)
                {
                    Debug.Log("Removed " + brokenCount + " broken transfer offers.");
                }

                // Fix broken vehicles
                Array16 <Vehicle> vehicles = VehicleManager.instance.m_vehicles;
                for (int i = 0; i < vehicles.m_size; i++)
                {
                    if (vehicles.m_buffer[i].m_flags != Vehicle.Flags.None)
                    {
                        bool exists = (vehicles.m_buffer[i].m_flags & Vehicle.Flags.Spawned) != Vehicle.Flags.None;

                        // Vehicle validity
                        InstanceID target;
                        bool       isInfoNull = vehicles.m_buffer[i].Info == null;
                        bool       isLeading  = vehicles.m_buffer[i].m_leadingVehicle == 0;
                        bool       isWaiting  = !exists && (vehicles.m_buffer[i].m_flags & Vehicle.Flags.WaitingSpace) != Vehicle.Flags.None;
                        bool       isConfused = exists && isLeading && !isInfoNull && vehicles.m_buffer[i].Info.m_vehicleAI.GetLocalizedStatus((ushort)i, ref vehicles.m_buffer[i], out target) == Locale.Get("VEHICLE_STATUS_CONFUSED");

                        if (isInfoNull || isWaiting || isConfused)
                        {
                            try
                            {
                                VehicleManager.instance.ReleaseVehicle((ushort)i);
                                if (isInfoNull)
                                {
                                    brokenCount++;
                                }
                                if (isConfused)
                                {
                                    confusedCount++;
                                }
                            }
                            catch { }
                        }
                    }
                    if (i % 256 == 255)
                    {
                        yield return(null);
                    }
                }

                if (confusedCount > 0)
                {
                    Debug.Log("Removed " + confusedCount + " confused vehicle instances.");
                }

                Array16 <VehicleParked> vehiclesParked = VehicleManager.instance.m_parkedVehicles;
                for (int i = 0; i < vehiclesParked.m_size; i++)
                {
                    if (vehiclesParked.m_buffer[i].Info == null)
                    {
                        try
                        {
                            VehicleManager.instance.ReleaseParkedVehicle((ushort)i);
                            brokenCount++;
                        }
                        catch { }
                    }
                    if (i % 256 == 255)
                    {
                        yield return(null);
                    }
                }

                if (brokenCount > 0)
                {
                    Debug.Log("Removed " + brokenCount + " broken vehicle instances.");
                }
                brokenCount = 0;

                // Fix broken buildings
                Array16 <Building> buildings = BuildingManager.instance.m_buildings;
                for (int i = 0; i < buildings.m_size; i++)
                {
                    if (buildings.m_buffer[i].Info == null)
                    {
                        try
                        {
                            BuildingManager.instance.ReleaseBuilding((ushort)i);
                            brokenCount++;
                        }
                        catch { }
                    }
                    if (i % 256 == 255)
                    {
                        yield return(null);
                    }
                }

                if (brokenCount > 0)
                {
                    Debug.Log("Removed " + brokenCount + " broken building instances.");
                }
                brokenCount = 0;

                // Fix broken props
                Array16 <PropInstance> props = PropManager.instance.m_props;
                for (int i = 0; i < props.m_size; i++)
                {
                    if (props.m_buffer[i].Info == null)
                    {
                        try
                        {
                            PropManager.instance.ReleaseProp((ushort)i);
                            brokenCount++;
                        }
                        catch { }
                    }
                    if (i % 256 == 255)
                    {
                        yield return(null);
                    }
                }

                if (brokenCount > 0)
                {
                    Debug.Log("Removed " + brokenCount + " broken prop instances.");
                }
                brokenCount = 0;

                // Fix broken trees
                Array32 <TreeInstance> trees = TreeManager.instance.m_trees;
                for (int i = 0; i < trees.m_size; i++)
                {
                    if (trees.m_buffer[i].Info == null)
                    {
                        try
                        {
                            TreeManager.instance.ReleaseTree((ushort)i);
                            brokenCount++;
                        }
                        catch { }
                    }
                    if (i % 256 == 255)
                    {
                        yield return(null);
                    }
                }

                if (brokenCount > 0)
                {
                    Debug.Log("Removed " + brokenCount + " broken tree instances.");
                }
                brokenCount = 0;
            }
            finally
            {
                SimulationManager.instance.ForcedSimulationPaused = false;
            }
        }
        /// <summary>
        /// Stock code that transfers people/materials between the buildings/vehicles referenced in the given offers.
        /// </summary>
        private static bool StartTransfer(TransferManager.TransferReason material, TransferManager.TransferOffer offerOut, TransferManager.TransferOffer offerIn, int delta)
        {
            try
            {
                if (offerIn.Building != 0 && TransferManagerInfo.IsCustomVehiclesBuilding(offerIn.Building))
                {
                    VehicleManagerMod.CurrentSourceBuilding = offerIn.Building;
                }

                bool active1 = offerIn.Active;
                bool active2 = offerOut.Active;
                if (active1 && offerIn.Vehicle != 0)
                {
                    Array16 <Vehicle> vehicles = Singleton <VehicleManager> .instance.m_vehicles;
                    ushort            vehicle  = offerIn.Vehicle;
                    VehicleInfo       info     = vehicles.m_buffer[vehicle].Info;
                    offerOut.Amount = delta;
                    info.m_vehicleAI.StartTransfer(vehicle, ref vehicles.m_buffer[vehicle], material, offerOut);
                }
                else if (active2 && offerOut.Vehicle != 0)
                {
                    Array16 <Vehicle> vehicles = Singleton <VehicleManager> .instance.m_vehicles;
                    ushort            vehicle  = offerOut.Vehicle;
                    VehicleInfo       info     = vehicles.m_buffer[vehicle].Info;
                    offerIn.Amount = delta;
                    info.m_vehicleAI.StartTransfer(vehicle, ref vehicles.m_buffer[vehicle], material, offerIn);
                }
                else if (active1 && (int)offerIn.Citizen != 0)
                {
                    Array32 <Citizen> citizens = Singleton <CitizenManager> .instance.m_citizens;
                    uint        citizen        = offerIn.Citizen;
                    CitizenInfo citizenInfo    = citizens.m_buffer[citizen].GetCitizenInfo(citizen);
                    if (citizenInfo == null)
                    {
                        return(false);
                    }
                    offerOut.Amount = delta;

                    // Workaround a bug in ResidentAI.StartTransfer
                    if (material == TransferManager.TransferReason.ChildCare || material == TransferManager.TransferReason.ElderCare)
                    {
                        citizens.m_buffer[citizen].Sick = false;
                    }

                    citizenInfo.m_citizenAI.StartTransfer(citizen, ref citizens.m_buffer[citizen], material, offerOut);
                }
                else if (active2 && (int)offerOut.Citizen != 0)
                {
                    Array32 <Citizen> citizens = Singleton <CitizenManager> .instance.m_citizens;
                    uint        citizen        = offerOut.Citizen;
                    CitizenInfo citizenInfo    = citizens.m_buffer[citizen].GetCitizenInfo(citizen);
                    if (citizenInfo == null)
                    {
                        return(false);
                    }
                    offerIn.Amount = delta;

                    // Workaround a bug in ResidentAI.StartTransfer
                    if (material == TransferManager.TransferReason.ChildCare || material == TransferManager.TransferReason.ElderCare)
                    {
                        citizens.m_buffer[citizen].Sick = false;
                    }

                    citizenInfo.m_citizenAI.StartTransfer(citizen, ref citizens.m_buffer[citizen], material, offerIn);
                }
                else if (active2 && offerOut.Building != 0)
                {
                    Array16 <Building> buildings = Singleton <BuildingManager> .instance.m_buildings;
                    ushort             building  = offerOut.Building;
                    BuildingInfo       info      = buildings.m_buffer[building].Info;
                    offerIn.Amount = delta;
                    info.m_buildingAI.StartTransfer(building, ref buildings.m_buffer[building], material, offerIn);
                }
                else
                {
                    if (!active1 || offerIn.Building == 0)
                    {
                        return(false);
                    }
                    Array16 <Building> buildings = Singleton <BuildingManager> .instance.m_buildings;
                    ushort             building  = offerIn.Building;
                    BuildingInfo       info      = buildings.m_buffer[building].Info;
                    offerOut.Amount = delta;
                    info.m_buildingAI.StartTransfer(building, ref buildings.m_buffer[building], material, offerOut);
                }

                return(true);
            }
            finally
            {
                VehicleManagerMod.CurrentSourceBuilding = 0;
            }
        }