private void UpdateMtuLogic(int deltaTime)
        {
            if (_finishMtu)
            {
                return;
            }

            _mtuCheckTimer += deltaTime;
            if (_mtuCheckTimer < MtuCheckDelay)
            {
                return;
            }

            _mtuCheckTimer = 0;
            _mtuCheckAttempts++;
            if (_mtuCheckAttempts >= MaxMtuCheckAttempts)
            {
                _finishMtu = true;
                return;
            }

            lock (_mtuMutex)
            {
                if (_mtuIdx >= NetConstants.PossibleMtu.Length - 1)
                {
                    return;
                }

                //Send increased packet
                int newMtu = NetConstants.PossibleMtu[_mtuIdx + 1];
                var p      = _packetPool.GetPacket(newMtu);
                p.Property = PacketProperty.MtuCheck;
                FastBitConverter.GetBytes(p.RawData, 1, newMtu);          //place into start
                FastBitConverter.GetBytes(p.RawData, p.Size - 4, newMtu); //and end of packet

                //Must check result for MTU fix
                if (NetManager.SendRawAndRecycle(p, EndPoint) <= 0)
                {
                    _finishMtu = true;
                }
            }
        }
Beispiel #2
0
        internal void SendRawData(NetPacket packet)
        {
            //2 - merge byte + minimal packet size + datalen(ushort)
            if (_netManager.MergeEnabled && _mergePos + packet.Size + NetConstants.HeaderSize * 2 + 2 < _mtu)
            {
                FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + NetConstants.HeaderSize, (ushort)packet.Size);
                Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + NetConstants.HeaderSize + 2, packet.Size);
                _mergePos += packet.Size + 2;
                _mergeCount++;

                //DebugWriteForce("Merged: " + _mergePos + "/" + (_mtu - 2) + ", count: " + _mergeCount);
                return;
            }

            NetUtils.DebugWrite(ConsoleColor.DarkYellow, "[P]SendingPacket: " + packet.Property);
            _netManager.SendRaw(packet, _remoteEndPoint);
#if STATS_ENABLED
            Statistics.PacketsSent++;
            Statistics.BytesSent += (ulong)packet.Size;
#endif
        }
Beispiel #3
0
        /// <summary>
        /// Force closes connection and stop all threads.
        /// </summary>
        public void Stop()
        {
            //Send disconnect packets
            lock (_peers)
                foreach (NetPeer netPeer in _peers.Values)
                {
                    var disconnectPacket = NetPacket.CreateRawPacket(PacketProperty.Disconnect, 8);
                    FastBitConverter.GetBytes(disconnectPacket, 1, netPeer.ConnectId);
                    SendRaw(disconnectPacket, netPeer.EndPoint);
                }

            //Clear
            ClearPeers();

            //Stop
            if (IsRunning)
            {
                _logicThread.Stop();
                _socket.Close();
            }
        }
Beispiel #4
0
        public void TestDeterministicOutput()
        {
            var bitConverter     = new FastBitConverter();
            var keyGenerator     = new KeyGenerator(bitConverter);
            var blockManipulator = new BlockManipulator();
            var tea = new BlackTeaCryptoServiceProvider(blockManipulator, keyGenerator, bitConverter);

            var passwordBytes = new byte[16];
            var dataBytes     = new byte[16];

            for (var i = 0; i < 10000; i++)
            {
                Guid.NewGuid().TryWriteBytes(passwordBytes);
                Guid.NewGuid().TryWriteBytes(dataBytes);

                var cipher = tea.EncryptRaw(dataBytes, passwordBytes);
                var plain  = tea.DecryptRaw(cipher, passwordBytes);

                Assert.IsTrue(plain.SequenceEqual(dataBytes));
            }
        }
Beispiel #5
0
        internal bool Shutdown(byte[] data, int start, int length, bool force)
        {
            lock (_shutdownLock)
            {
                //trying to shutdown already disconnected
                if (_connectionState == ConnectionState.Disconnected ||
                    _connectionState == ConnectionState.ShutdownRequested)
                {
                    return(false);
                }

                //don't send anything
                if (force)
                {
                    _connectionState = ConnectionState.Disconnected;
                    return(true);
                }

                //reset time for reconnect protection
                _timeSinceLastPacket = 0;

                //send shutdown packet
                _shutdownPacket = new NetPacket(PacketProperty.Disconnect, length);
                _shutdownPacket.ConnectionNumber = _connectNum;
                FastBitConverter.GetBytes(_shutdownPacket.RawData, 1, _connectTime);
                if (_shutdownPacket.Size >= _mtu)
                {
                    //Drop additional data
                    NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU - 8!");
                }
                else if (data != null && length > 0)
                {
                    Buffer.BlockCopy(data, start, _shutdownPacket.RawData, 9, length);
                }
                _connectionState = ConnectionState.ShutdownRequested;
                NetDebug.Write("[Peer] Send disconnect");
                NetManager.SendRaw(_shutdownPacket, EndPoint);
                return(true);
            }
        }
Beispiel #6
0
        internal async Task RunAsync()
        {
            // 첫 접속 패킷을 보내자
            NetPacket sendPacket = NetPool.PacketPool.Alloc(PacketProperty.ResponseConnection, 8);

            sendPacket.SessionIdForConnection = SessionId;
            FastBitConverter.GetBytes(sendPacket.RawData, 5, _connectId);

            SendRawAsync(sendPacket, DeliveryMethod.Tcp);

            _isPossibleUpdate = true;

            try
            {
                await ProcessPacketAsync();
            }
            finally
            {
                _isPossibleUpdate = false;
                Close();
            }
        }
Beispiel #7
0
        private void DisconnectPeer(
            NetPeer peer,
            DisconnectReason reason,
            int socketErrorCode,
            bool sendDisconnectPacket,
            byte[] data,
            int start,
            int count)
        {
            if (sendDisconnectPacket)
            {
                if (count + 8 >= peer.Mtu)
                {
                    //Drop additional data
                    data  = null;
                    count = 0;
                    NetUtils.DebugWriteError("Disconnect additional data size more than MTU - 8!");
                }

                var disconnectPacket = NetPacket.CreateRawPacket(PacketProperty.Disconnect, 8 + count);
                FastBitConverter.GetBytes(disconnectPacket, 1, peer.ConnectId);
                if (data != null)
                {
                    Buffer.BlockCopy(data, start, disconnectPacket, 9, count);
                }
                SendRaw(disconnectPacket, peer.EndPoint);
            }
            var netEvent = CreateEvent(NetEventType.Disconnect);

            netEvent.Peer             = peer;
            netEvent.AdditionalData   = socketErrorCode;
            netEvent.DisconnectReason = reason;
            EnqueueEvent(netEvent);
            lock (_peersToRemove)
            {
                _peersToRemove.Enqueue(peer.EndPoint);
            }
        }
Beispiel #8
0
        /// <summary>
        /// Force closes connection and stop all threads.
        /// </summary>
        public void Stop()
        {
            //Send disconnect packets
            lock (_peers)
            {
                for (int i = 0; i < _peers.Count; i++)
                {
                    var disconnectPacket = _netPacketPool.Get(PacketProperty.Disconnect, 8);
                    FastBitConverter.GetBytes(disconnectPacket.RawData, 1, _peers[i].ConnectId);
                    SendRawAndRecycle(disconnectPacket, _peers[i].EndPoint);
                }
            }

            //Clear
            ClearPeers();

            //Stop
            if (IsRunning)
            {
                _logicThread.Stop();
                _socket.Close();
            }
        }
Beispiel #9
0
        private void CloseConnection(bool force, DisconnectReason reason, int socketErrorCode)
        {
            lock (_connectionCloseLock)
            {
                //Nothing to do
                if (!IsRunning)
                {
                    return;
                }

                //Send goodbye
                if (_peer != null && !force && _connected)
                {
                    //Send disconnect data
                    var disconnectPacket = NetPacket.CreateRawPacket(PacketProperty.Disconnect, 8);
                    FastBitConverter.GetBytes(disconnectPacket, 1, _connectId);
                    SendRaw(disconnectPacket, _peer.EndPoint);
                }

                //Close threads and socket close
                base.Stop();

                //Clear data
                _peer            = null;
                _connected       = false;
                _connectTimer    = 0;
                _connectAttempts = 0;
                SocketClearPeers();

                //Send event to Listener
                var netEvent = CreateEvent(NetEventType.Disconnect);
                netEvent.DisconnectReason = reason;
                netEvent.AdditionalData   = socketErrorCode;
                EnqueueEvent(netEvent);
            }
        }
Beispiel #10
0
        //Process incoming packet
        internal void ProcessPacket(NetPacket packet)
        {
            //not initialized
            if (_connectionState == ConnectionState.Outgoing)
            {
                _packetPool.Recycle(packet);
                return;
            }
            if (packet.ConnectionNumber != _connectNum && packet.Property != PacketProperty.ShutdownOk) //without connectionNum
            {
                NetDebug.Write(NetLogLevel.Trace, "[RR]Old packet");
                _packetPool.Recycle(packet);
                return;
            }
            _timeSinceLastPacket = 0;

            NetDebug.Write("[RR]PacketProperty: {0}", packet.Property);
            switch (packet.Property)
            {
            case PacketProperty.Merged:
                int pos = NetConstants.HeaderSize;
                while (pos < packet.Size)
                {
                    ushort size = BitConverter.ToUInt16(packet.RawData, pos);
                    pos += 2;
                    NetPacket mergedPacket = _packetPool.GetPacket(size, false);
                    if (!mergedPacket.FromBytes(packet.RawData, pos, size))
                    {
                        _packetPool.Recycle(packet);
                        break;
                    }
                    pos += size;
                    ProcessPacket(mergedPacket);
                }
                break;

            //If we get ping, send pong
            case PacketProperty.Ping:
                if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pongPacket.Sequence) > 0)
                {
                    NetDebug.Write("[PP]Ping receive, send pong");
                    FastBitConverter.GetBytes(_pongPacket.RawData, 3, DateTime.UtcNow.Ticks);
                    _pongPacket.Sequence = packet.Sequence;
                    NetManager.SendRaw(_pongPacket, EndPoint);
                }
                _packetPool.Recycle(packet);
                break;

            //If we get pong, calculate ping time and rtt
            case PacketProperty.Pong:
                if (packet.Sequence == _pingPacket.Sequence)
                {
                    _pingTimer.Stop();
                    int elapsedMs = (int)_pingTimer.ElapsedMilliseconds;
                    _remoteDelta = BitConverter.ToInt64(packet.RawData, 3) + (elapsedMs * TimeSpan.TicksPerMillisecond) / 2 - DateTime.UtcNow.Ticks;
                    UpdateRoundTripTime(elapsedMs);
                    NetManager.ConnectionLatencyUpdated(this, elapsedMs / 2);
                    NetDebug.Write("[PP]Ping: {0} - {1} - {2}", packet.Sequence, elapsedMs, _remoteDelta);
                }
                _packetPool.Recycle(packet);
                break;

            case PacketProperty.Ack:
            case PacketProperty.Channeled:
                if (packet.ChannelId > _channels.Length)
                {
                    _packetPool.Recycle(packet);
                    break;
                }
                var channel = _channels[packet.ChannelId] ?? (packet.Property == PacketProperty.Ack ? null : CreateChannel(packet.ChannelId));
                if (channel != null)
                {
                    if (!channel.ProcessPacket(packet))
                    {
                        _packetPool.Recycle(packet);
                    }
                }
                break;

            //Simple packet without acks
            case PacketProperty.Unreliable:
                AddIncomingPacket(packet);
                return;

            case PacketProperty.MtuCheck:
            case PacketProperty.MtuOk:
                ProcessMtuPacket(packet);
                break;

            case PacketProperty.ShutdownOk:
                if (_connectionState == ConnectionState.ShutdownRequested)
                {
                    _connectionState = ConnectionState.Disconnected;
                }
                _packetPool.Recycle(packet);
                break;

            default:
                NetDebug.WriteError("Error! Unexpected packet type: " + packet.Property);
                break;
            }
        }
Beispiel #11
0
    public void ProcessOutBoundPacket(IPEndPoint remoteEndPoint, ref byte[] data, ref int offset, ref int length)
    {
        int num = offset;

        Array.Copy(data, offset, data, offset + 5 + 16, length);
        offset += 21;
        bool flag = this.MatchesFilter(data, offset, length);

        PacketEncryptionLayer.EncryptionState encryptionState;
        bool flag2;

        if (!flag && this.TryGetEncryptionState(remoteEndPoint, out encryptionState, out flag2))
        {
            object sendMutex = encryptionState.sendMutex;
            lock (sendMutex)
            {
                uint nextSentSequenceNum = encryptionState.GetNextSentSequenceNum();
                FastBitConverter.GetBytes(data, offset + length, nextSentSequenceNum);
                PacketEncryptionLayer._tempHash = encryptionState.sendMac.ComputeHash(data, offset, length + 4);
                PacketEncryptionLayer.FastCopyMac(PacketEncryptionLayer._tempHash, 0, data, offset + length);
                length += 10;
                if (PacketEncryptionLayer._tempIV == null)
                {
                    PacketEncryptionLayer._tempIV = new byte[16];
                }
                PacketEncryptionLayer._random.GetBytes(PacketEncryptionLayer._tempIV);
                int num2 = (int)(PacketEncryptionLayer.GetRandomByte() % 64);
                int num3 = (length + num2 + 1) % 16;
                num2 -= num3;
                if (num2 < 0)
                {
                    num2 += 16;
                }
                if (offset + length + num2 >= data.Length)
                {
                    num2 -= 16;
                }
                for (int i = 0; i <= num2; i++)
                {
                    data[offset + length + i] = (byte)num2;
                }
                length += num2 + 1;
                using (ICryptoTransform cryptoTransform = this._aes.CreateEncryptor(encryptionState.sendKey, PacketEncryptionLayer._tempIV))
                {
                    int num4 = num + 5 + 16;
                    int num5;
                    for (int j = length; j >= cryptoTransform.InputBlockSize; j -= num5)
                    {
                        int inputCount = cryptoTransform.CanTransformMultipleBlocks ? (j / cryptoTransform.InputBlockSize * cryptoTransform.InputBlockSize) : cryptoTransform.InputBlockSize;
                        num5    = cryptoTransform.TransformBlock(data, offset, inputCount, data, num4);
                        offset += num5;
                        num4   += num5;
                    }
                    offset = num;
                    length = num4 - num;
                }
                data[offset] = 1;
                FastBitConverter.GetBytes(data, offset + 1, nextSentSequenceNum);
                PacketEncryptionLayer.FastCopyBlock(PacketEncryptionLayer._tempIV, 0, data, offset + 5);
            }
            return;
        }
        if (this._filterUnencryptedTraffic && !flag)
        {
            length = 0;
            return;
        }
        offset--;
        length++;
        data[offset] = 0;
    }
Beispiel #12
0
 public override void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length)
 {
     FastBitConverter.GetBytes(data, length, CRC32C.Compute(data, offset, length));
     length += CRC32C.ChecksumSize;
 }
        /// <summary>
        /// Parses a compressed resource data file
        /// </summary>
        /// <param name="filename">resource data file name</param>
        /// <returns>a Dictionary indexed by the resource filename's hashcode</returns>
        public static Dictionary <ulong, ResourceDataEntry> Parse(string filename)
        {
            Dictionary <ulong, ResourceDataEntry> resourceData;

            // The data should be compressed, read the whole file into memory first and decompress it
            // Compressed with Oodle Kraken, level 4
            var resourceDataFileBytes = File.ReadAllBytes(filename);
            var compressedData        = new byte[resourceDataFileBytes.Length - 8];

            Buffer.BlockCopy(resourceDataFileBytes, 8, compressedData, 0, compressedData.Length);

            long decompressedSize = FastBitConverter.ToInt64(resourceDataFileBytes, 0);
            var  decompressedData = OodleWrapper.Decompress(compressedData, decompressedSize);

            // Parse the binary data now
            using (var memoryStream = new MemoryStream(decompressedData))
            {
                using (var binaryReader = new BinaryReader(memoryStream, Encoding.Default, true))
                {
                    // Amount of entries
                    ulong amount = binaryReader.ReadUInt64();
                    resourceData = new Dictionary <ulong, ResourceDataEntry>((int)amount);

                    // Read each entry
                    for (ulong i = 0; i < amount; i++)
                    {
                        ulong  fileNameHash    = binaryReader.ReadUInt64();
                        ulong  streamDbHash    = binaryReader.ReadUInt64();
                        byte   version         = binaryReader.ReadByte();
                        byte   specialByte1    = binaryReader.ReadByte();
                        byte   specialByte2    = binaryReader.ReadByte();
                        byte   specialByte3    = binaryReader.ReadByte();
                        ushort typeNameLength  = binaryReader.ReadUInt16();
                        string typeName        = Encoding.Default.GetString(binaryReader.ReadBytes(typeNameLength));
                        ushort assetTypeLength = binaryReader.ReadUInt16();
                        string assetType       = typeName;
                        string assetName       = string.Empty;

                        if (assetTypeLength > 0)
                        {
                            assetType = Encoding.Default.GetString(binaryReader.ReadBytes(assetTypeLength));
                            ushort assetNameLength = binaryReader.ReadUInt16();
                            assetName = Encoding.Default.GetString(binaryReader.ReadBytes(assetNameLength));
                        }

                        resourceData.Add(fileNameHash, new ResourceDataEntry()
                        {
                            StreamDbHash    = streamDbHash,
                            Version         = version,
                            SpecialByte1    = specialByte1,
                            SpecialByte2    = specialByte2,
                            SpecialByte3    = specialByte3,
                            ResourceType    = typeName,
                            MapResourceName = assetName,
                            MapResourceType = assetType
                        });
                    }
                }
            }

            return(resourceData);
        }
Beispiel #14
0
 private void SetUInt64BE(int offset, ulong value)
 {
     FastBitConverter.GetBytes(Bytes, offset, SwapEndianness(value));
 }
Beispiel #15
0
    private bool TryDecryptData(byte[] data, PacketEncryptionLayer.EncryptionState state, int startingOffset, ref int offset, ref int length)
    {
        object receiveMutex = state.receiveMutex;
        bool   result;

        lock (receiveMutex)
        {
            uint num = BitConverter.ToUInt32(data, offset);
            offset += 4;
            length -= 4;
            if (!state.IsValidSequenceNum(num))
            {
                result = false;
            }
            else
            {
                if (PacketEncryptionLayer._tempIV == null)
                {
                    PacketEncryptionLayer._tempIV = new byte[16];
                }
                if (PacketEncryptionLayer._tempHash == null)
                {
                    PacketEncryptionLayer._tempHash = new byte[10];
                }
                PacketEncryptionLayer.FastCopyBlock(data, offset, PacketEncryptionLayer._tempIV, 0);
                offset += PacketEncryptionLayer._tempIV.Length;
                length -= PacketEncryptionLayer._tempIV.Length;
                using (ICryptoTransform cryptoTransform = this._aes.CreateDecryptor(state.receiveKey, PacketEncryptionLayer._tempIV))
                {
                    int num2 = startingOffset;
                    int num3;
                    for (int i = length; i >= cryptoTransform.InputBlockSize; i -= num3)
                    {
                        int inputCount = cryptoTransform.CanTransformMultipleBlocks ? (i / cryptoTransform.InputBlockSize * cryptoTransform.InputBlockSize) : cryptoTransform.InputBlockSize;
                        num3    = cryptoTransform.TransformBlock(data, offset, inputCount, data, num2);
                        offset += num3;
                        num2   += num3;
                    }
                    offset = startingOffset;
                    length = num2 - offset;
                }
                int  num4  = (int)data[offset + length - 1];
                bool flag2 = true;
                if (num4 + 10 + 1 > length)
                {
                    num4  = 0;
                    flag2 = false;
                }
                length -= num4 + 10 + 1;
                PacketEncryptionLayer.FastCopyMac(data, offset + length, PacketEncryptionLayer._tempHash, 0);
                FastBitConverter.GetBytes(data, offset + length, num);
                byte[] array = state.receiveMac.ComputeHash(data, offset, length + 4);
                if (!flag2)
                {
                    result = false;
                }
                else
                {
                    for (int j = 0; j < 10; j++)
                    {
                        if (PacketEncryptionLayer._tempHash[j] != array[j])
                        {
                            return(false);
                        }
                    }
                    if (!state.PutSequenceNum(num))
                    {
                        result = false;
                    }
                    else
                    {
                        result = true;
                    }
                }
            }
        }
        return(result);
    }
        /// <summary>
        /// Converts the map resources object to a byte array
        /// </summary>
        /// <returns>the map resources object as a byte array</returns>
        public byte[] ToByteArray()
        {
            using (var memoryStream = new MemoryStream())
            {
                // Write magic
                memoryStream.Write(FastBitConverter.GetBytes(Magic), 0, 4);

                // Write layer count (big-endian)
                var layerCountBytes = FastBitConverter.GetBytes(Layers.Count, true);
                memoryStream.Write(layerCountBytes, 0, 4);

                // Write layers
                foreach (var layer in Layers)
                {
                    var layerNameBytes = Encoding.UTF8.GetBytes(layer);
                    memoryStream.Write(FastBitConverter.GetBytes(layerNameBytes.Length), 0, 4);
                    memoryStream.Write(layerNameBytes, 0, layerNameBytes.Length);
                }

                // Write asset type count (big-endian)
                var assetTypeCountBytes = FastBitConverter.GetBytes((long)AssetTypes.Count, true);
                memoryStream.Write(assetTypeCountBytes, 0, 8);

                // Write asset types
                foreach (var assetType in AssetTypes)
                {
                    var assetTypeBytes = Encoding.UTF8.GetBytes(assetType);
                    memoryStream.Write(FastBitConverter.GetBytes(assetTypeBytes.Length), 0, 4);
                    memoryStream.Write(assetTypeBytes, 0, assetTypeBytes.Length);
                }

                // Write asset count (big-endian)
                var assetCountBytes = FastBitConverter.GetBytes(Assets.Count, true);
                memoryStream.Write(assetCountBytes, 0, 4);

                // Write assets
                foreach (var asset in Assets)
                {
                    var assetNameBytes = Encoding.UTF8.GetBytes(asset.Name);

                    // Write asset type index (big-endian)
                    var assetTypeIndexBytes = FastBitConverter.GetBytes(asset.AssetTypeIndex, true);
                    memoryStream.Write(assetTypeIndexBytes, 0, 4);

                    memoryStream.Write(FastBitConverter.GetBytes(assetNameBytes.Length), 0, 4);
                    memoryStream.Write(assetNameBytes, 0, assetNameBytes.Length);

                    // Write unknown data 1
                    memoryStream.Write(FastBitConverter.GetBytes(asset.UnknownData1), 0, 4);

                    // Write the remaining unknown data (big-endian)
                    var unknownData2Bytes = FastBitConverter.GetBytes(asset.UnknownData2, true);
                    memoryStream.Write(unknownData2Bytes, 0, 4);

                    var unknownData3Bytes = FastBitConverter.GetBytes(asset.UnknownData3, true);
                    memoryStream.Write(unknownData3Bytes, 0, 8);

                    var unknownData4Bytes = FastBitConverter.GetBytes(asset.UnknownData4, true);
                    memoryStream.Write(unknownData4Bytes, 0, 8);
                }

                // Write map count (big-endian)
                var mapCountBytes = FastBitConverter.GetBytes(Maps.Count, true);
                memoryStream.Write(mapCountBytes, 0, 4);

                // Write maps
                foreach (var map in Maps)
                {
                    var mapBytes = Encoding.UTF8.GetBytes(map);
                    memoryStream.Write(FastBitConverter.GetBytes(mapBytes.Length), 0, 4);
                    memoryStream.Write(mapBytes, 0, mapBytes.Length);
                }

                return(memoryStream.ToArray());
            }
        }
        /// <summary>
        /// Creates a MapResources object from a byte array
        /// </summary>
        /// <param name="rawData">raw map resources file data</param>
        /// <returns>the parsed MapResources object</returns>
        public static MapResourcesFile Parse(byte[] rawData)
        {
            MapResourcesFile mapResourcesFile = new MapResourcesFile();

            using (var memoryStream = new MemoryStream(rawData))
            {
                using (var binaryReader = new BinaryReader(memoryStream, Encoding.Default, true))
                {
                    // Read the magic
                    mapResourcesFile.Magic = binaryReader.ReadInt32();

                    // Read layer count (big-endian)
                    var layerCountBytes = binaryReader.ReadBytes(4);
                    int layerCount      = FastBitConverter.ToInt32(layerCountBytes, 0, true);

                    // Read layers
                    for (int i = 0; i < layerCount; i++)
                    {
                        int stringLength = binaryReader.ReadInt32();
                        mapResourcesFile.Layers.Add(Encoding.UTF8.GetString(binaryReader.ReadBytes(stringLength)));
                    }

                    // Read asset type count (big-endian)
                    var  assetTypeCountBytes = binaryReader.ReadBytes(8);
                    long assetTypeCount      = FastBitConverter.ToInt64(assetTypeCountBytes, 0, true);

                    // Read asset types
                    for (int i = 0; i < assetTypeCount; i++)
                    {
                        int stringLength = binaryReader.ReadInt32();
                        mapResourcesFile.AssetTypes.Add(Encoding.UTF8.GetString(binaryReader.ReadBytes(stringLength)));
                    }

                    // Read assets count (big-endian)
                    var assetCountBytes = binaryReader.ReadBytes(4);
                    int assetCount      = FastBitConverter.ToInt32(assetCountBytes, 0, true);

                    // Read assets
                    for (int i = 0; i < assetCount; i++)
                    {
                        var mapAsset            = new MapAsset();
                        var assetTypeIndexBytes = binaryReader.ReadBytes(4);
                        mapAsset.AssetTypeIndex = FastBitConverter.ToInt32(assetTypeIndexBytes, 0, true);

                        int stringLength = binaryReader.ReadInt32();
                        mapAsset.Name = Encoding.UTF8.GetString(binaryReader.ReadBytes(stringLength));

                        // Read unknown data
                        mapAsset.UnknownData1 = binaryReader.ReadInt32();

                        // Read the remaining unknown data (big-endian)
                        var unknownData2Bytes = binaryReader.ReadBytes(4);
                        mapAsset.UnknownData2 = FastBitConverter.ToInt32(unknownData2Bytes, 0, true);

                        var unknownData3Bytes = binaryReader.ReadBytes(8);
                        mapAsset.UnknownData3 = FastBitConverter.ToInt64(unknownData3Bytes, 0, true);

                        var unknownData4Bytes = binaryReader.ReadBytes(8);
                        mapAsset.UnknownData4 = FastBitConverter.ToInt64(unknownData4Bytes, 0, true);

                        mapResourcesFile.Assets.Add(mapAsset);
                    }

                    // Read map count (big-endian)
                    var mapCountBytes = binaryReader.ReadBytes(4);
                    int mapCount      = FastBitConverter.ToInt32(mapCountBytes, 0, true);

                    // Read asset types
                    for (int i = 0; i < mapCount; i++)
                    {
                        int stringLength = binaryReader.ReadInt32();
                        mapResourcesFile.Maps.Add(Encoding.UTF8.GetString(binaryReader.ReadBytes(stringLength)));
                    }
                }
            }

            return(mapResourcesFile);
        }