/// <summary>
        /// Читает свойство FBX со специальным типом данных после прочтения кода типа данных
        ///
        /// Если свойство строковое, то
        /// Читает строку в UTF-8 по переданной длине в байтах.
        /// Если строка содержит заголовок Model, то
        /// Если имя модели не пустое
        /// (самая первая модель называется Environment и соответстует корневому узлу в navis),
        /// то редактирует согласно очереди.
        ///
        /// Записывает изменение длины строки в байтах
        /// Если заголовка Model нет или свойство не строковое или это узел без имени, то переписывает без изменений
        /// </summary>
        /// <param name="br"></param>
        /// <param name="bytesCount"></param>
        /// <param name="addToEnd"></param>
        /// <returns></returns>
        private void ReadFBXPropWithSpecialDataTypeEditModelName
            (BinaryReader br, BinaryWriter bw, bool isString, OffsetCounter offsetCounter)
        {
            int len = br.ReadInt32();

            if (isString)
            {
                byte[] value   = br.ReadBytes(len);//Строка в UTF-8
                string str     = System.Text.Encoding.UTF8.GetString(value);
                bool   changes = false;

                if (replacements.Count > 0 && str.EndsWith("\0\u0001Model") /*&& str.Length > 7*/)
                {
                    //Это свойство с именем модели
                    string modelName = str.Replace("\0\u0001Model", "");

                    //Последовательность узлов моделей, которые подлежат переименованию
                    //начинается только после узла, у которого имя совпадает
                    if (!renamingModelsStarted)
                    {
                        NameReplacement firstElem  = replacements.Peek();
                        NameReplacement secondElem = replacements.Count > 1 ?
                                                     replacements.ElementAt(1)
                                : null;
                        if (
                            //modelName.Equals("Environment")//УЗЕЛ Environment ЕСТЬ НЕ ВСЕГДА!!! В каких случаях его нет пока не понятно.
                            firstElem.OldName.Equals(modelName)
                            )
                        {
                            renamingModelsStarted = true;
                        }
                        else if (secondElem != null && secondElem.OldNameTrustable && secondElem.OldName.Equals(modelName))
                        {
                            // На всякий случай проверяем второй элемент в очереди (возможно 1-й вообще не появится в FBX?)
                            renamingModelsStarted = true;
                            replacements.Dequeue();//Удаляем узел, который должен был соответствовать Environment
                        }
                    }
                    if (renamingModelsStarted)
                    {
                        bool editAllowed = true;
                        #region hardcode
                        //hardcode//hardcode//hardcode//hardcode//hardcode//hardcode
                        //Замечено, что иногда при экспорте один узел Navis
                        //разбивается на несколько вложенных в FBX (они назывались "curve " и номер)
                        //if (strVal.StartsWith("curve "))
                        //{
                        //    editAllowed = false;
                        //}
                        //else
                        //{
                        //    curveSequenceStarted = false;
                        //}
                        //hardcode//hardcode//hardcode//hardcode//hardcode//hardcode
                        #endregion
                        //Более гибко - добавить сопоставление имен там, где оно надежно
                        //Это защищает от большинства узлов, которых не было в Navis, но они появились в FBX
                        NameReplacement currReplacement = replacements.Peek();
                        if (currReplacement.OldNameTrustable)
                        {
                            if (!modelName.Equals(currReplacement.OldName))
                            {
                                editAllowed = false;
                            }
                        }
                        if (editAllowed)//Не вытаскивать больше из очереди если идет последовательность несопоставляемых узлов
                        {
                            currReplacement = replacements.Dequeue();

                            if (!currReplacement.SkipNode)
                            {
                                changes = true;
                                //Строка редактируется, длина строки меняется
                                string strEdited = currReplacement.NewName + "\0\u0001Model";
                                byte[] newValue  = Encoding.UTF8.GetBytes(strEdited);
                                int    newLen    = newValue.Length;
                                int    lenDiff   = newLen - len;
                                offsetCounter.Incr(lenDiff);
                                bw.Write(newLen);
                                bw.Write(newValue);
                            }
                        }
                    }
                }

                if (!changes)
                {
                    //Ничего не меняется
                    bw.Write(len);
                    bw.Write(value);
                }
            }
            else
            {
                //Ничего не меняется
                bw.Write(len);
                ReadWriteBytes(br, bw, len);
            }
        }
        /// <summary>
        /// Возвращает false если узел пустой
        /// 13 null байтов - это как раз длина пустого узла FBX (для малого fbx)
        /// </summary>
        /// <param name="br"></param>
        /// <param name="nestingLevel"></param>
        /// <returns></returns>
        private bool ReadWriteFBXNodeSmall(BinaryReader br, BinaryWriter bw, uint nestingLevel, OffsetCounter parentOffsetCounter)
        {
            //Изменение длины узла (за счет изменения имен)
            OffsetCounter offsetCounter = new OffsetCounter();


            long endOffsetPos   = bw.BaseStream.Position;                                       //адрес байта, значение в котором возможно нужно будет изменить
            uint endOffset      = ReadWriteEndOffsetAccordingToGlobalOffsetChangeSmall(br, bw); //РЕДАКТИРОВАТЬ//ЭТО АДРЕС БАЙТА, С КОТОРОГО НАЧИНАЮТСЯ СЛЕДУЮЩИЕ ДАННЫЕ
            uint numProps       = ReadWriteUInt32(br, bw);
            long propListLenPos = bw.BaseStream.Position;                                       //адрес байта, значение в котором возможно нужно будет изменить
            uint propListLen    = ReadWriteUInt32(br, bw);                                      //РЕДАКТИРОВАТЬ - длина списка свойств в байтах


            offsetCounter.EndOffsetPos   = endOffsetPos;
            offsetCounter.PropListLenPos = propListLenPos;

            byte nameLen = ReadWriteByte(br, bw);//Имя узла - это не имя модели//максимум 255 символов



            string name = null;

            if (nameLen > 0)
            {
                name = new string(ReadWriteChars(br, bw, nameLen));
            }
            else
            {
                name = "---";
            }

            if (name.Equals("Model"))
            {
                //Найти имя модели в свойствах
                for (uint i = 0; i < numProps; i++)
                {
                    ReadFBXProperty(br, bw, offsetCounter);
                }

                if (offsetCounter.OwnOffsetChange != 0)
                {
                    //ВСЕ ПОСЛЕДУЮЩИЕ УЗЛЫ В ФАЙЛЕ ТАК ЖЕ МЕНЯЮТ ОФСЕТ
                    globalOffsetChange += offsetCounter.OwnOffsetChange;
                }
            }
            else
            {
                //просто прочитать и переписать свойства без изменеий
                ReadWriteBytes(br, bw, propListLen);
            }

            //Далее нужно прочитать все свойства

            //Если у узла есть вложенные, то после них будет 13 NULL–record
            //Если вложенных нет, то сразу же идет следующий узел
            if (br.BaseStream.Position < endOffset)
            {
                uint childrenNL = nestingLevel + 1;

                //long offsetChange = 0;
                //13 null байтов - это как раз длина пустого узла FBX
                while (ReadWriteFBXNodeSmall(br, bw, childrenNL, offsetCounter))
                {
                }
            }


            if (offsetCounter.OwnOffsetChange != 0 || offsetCounter.NestedNodesOffsetChanged)
            {
                if (parentOffsetCounter != null)
                {
                    parentOffsetCounter.NestedNodesOffsetChanged = true;//Сообщить родительскому узлу о том что длина вложенного узла изменилась
                }
                //Изменилась длина узла в байтах.
                //Отредактировать в записываемом файле указатель на конец узла и длину свойств для этого узла
                long currBWPos = bw.BaseStream.Position;
                bw.BaseStream.Position = offsetCounter.EndOffsetPos;                         //Переход на нужную позицию в памяти
                bw.Write(Convert.ToUInt32(currBWPos));                                       //Перезапись endOffset

                if (offsetCounter.OwnOffsetChange != 0)                                      //РЕДАКТИРОВАТЬ propListLen ТОЛЬКО ЕСЛИ СВОЙСТВО ОТРЕДАКТИРОВАНО В ЭТОМ УЗЛЕ
                {
                    bw.BaseStream.Position = offsetCounter.PropListLenPos;                   //Переход на нужную позицию в памяти
                    bw.Write(Convert.ToUInt32(propListLen + offsetCounter.OwnOffsetChange)); //Перезапись propListLen
                }


                bw.BaseStream.Position = currBWPos;//Возврат на текущую позицию
            }


            return(endOffset != 0);
        }
        /// <summary>
        /// Читает и переписывает любое свойство. Если это название модели, то изменяет его
        /// </summary>
        /// <param name="br"></param>
        private void ReadFBXProperty(BinaryReader br, BinaryWriter bw, OffsetCounter offsetCounter)
        {
            char typeCode  = ReadWriteChar(br, bw);
            bool isArray   = false;
            bool isSpecial = false;
            byte dataSize  = 0;

            switch (typeCode)
            {
            //Primitive Types
            case 'Y':
                dataSize = 2;
                break;

            case 'C':
                dataSize = 1;
                break;

            case 'I':
                dataSize = 4;
                break;

            case 'F':
                dataSize = 4;
                break;

            case 'D':
                dataSize = 8;
                break;

            case 'L':
                dataSize = 8;
                break;

            //Array types
            case 'f':
                isArray  = true;
                dataSize = 4;
                break;

            case 'd':
                isArray  = true;
                dataSize = 8;
                break;

            case 'l':
                isArray  = true;
                dataSize = 8;
                break;

            case 'i':
                isArray  = true;
                dataSize = 4;
                break;

            case 'b':
                isArray  = true;
                dataSize = 1;
                break;

            //Special types
            case 'S':
                isSpecial = true;
                break;

            case 'R':
                isSpecial = true;
                break;

            default:
                throw new Exception("Ошибка при чтении FBX. Неизвестный код свойства - " + typeCode);
                break;
            }

            if (isArray)
            {
                uint arrLen           = ReadWriteUInt32(br, bw);
                uint encoding         = ReadWriteUInt32(br, bw);
                uint compressedLength = ReadWriteUInt32(br, bw);
                if (encoding == 0)
                {
                    ReadWriteBytes(br, bw, dataSize * arrLen);
                }
                else if (encoding == 1)
                {
                    ReadWriteBytes(br, bw, compressedLength);
                }
                else
                {
                    throw new Exception("Ошибка при чтении FBX. Неизвестное значение encoding массива");
                }
            }
            else if (isSpecial)
            {
                //здесь может храниться имя модели, которое нудно отредактировать
                ReadFBXPropWithSpecialDataTypeEditModelName(br, bw, typeCode.Equals('S'), offsetCounter);
            }
            else
            {
                //простейший тип данных
                ReadWriteBytes(br, bw, dataSize);
            }
        }