Esempio n. 1
0
        /// <summary>
        /// Reads a controller from a stream. If the controller is not implemented, the raw data is returned as byte array.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="controllerName">The name of the controller or null if the controller must be auto-detected (not optimal, some controllers cannot be detected).</param>
        /// <returns></returns>
        public object Read(Stream stream, string controllerName)
        {
            long currentPos    = stream.Position;
            int  availableData = (int)(stream.Length - stream.Position);

            var reader = new BinaryReader(stream, FileEncoding.Default);

            // In case of a normal controller (10/-1) subtype the next Int32 contains the size of the following structure. In case of animation controllers (sub type 0/1/2/4/5/6), see below.
            int controllerSize = reader.ReadInt32();

            // The size read from the stream matches the available data (incl. the 4 bytes of the Int32 already read), then we have a normal controller. Otherwise, we have to do some more logic to determine correct type of controller.
            // PROBLEM: In the odd unlucky case the size of a controller may match exactly, and it is then assumed to be a behavior controller.
            bool isBaseController = controllerSize + 4 != availableData;

            var profile = ControllerProfile.Unknown;

            // Base or animation controller? (byte stream)
            if (isBaseController)
            {
                // First 4 bytes means something else.
                ushort animationControllerSubType = unchecked ((ushort)(controllerSize & 0xFFFF));
                ushort unknown = unchecked ((ushort)((controllerSize & 0xFFFF0000) >> 16));
                if (string.IsNullOrEmpty(controllerName))
                {
                    // If no name provided, check if sub type matches that of an animation controller.
                    if (Enum.IsDefined(typeof(AnimationType), animationControllerSubType))
                    {
                        controllerName = ((AnimationType)animationControllerSubType).ToString();
                    }
                }

                // Only continue reading if we have a controller name.
                if (!string.IsNullOrEmpty(controllerName))
                {
                    profile = _controllerAssembly.GetControllerProfilesByName(controllerName).FirstOrDefault();

                    if (profile != ControllerProfile.Unknown && _controllerAssembly.TryGetControllerType(controllerName, profile, out Type controllerType))
                    {
                        // Check if the controller is indeed a base/animation controller. If it isn't, the size descriptor may have contained an invalid size for controller data. We just attempt normal deserialization then.
                        if (!controllerType.IsBehaviorController())
                        {
                            ControllerAttribute controllerAttribute = controllerType.GetCustomAttribute <ControllerAttribute>() ?? new ControllerAttribute();
                            // Check that the detected controller matches subtype.
                            if (controllerAttribute.SubType.HasValue && controllerAttribute.SubType.Value == animationControllerSubType)
                            {
                                // For animation controllers, the 2 bytes are part of the data and identify an 'ushort' count field for n number of frames.
                                if (controllerType.IsAnimationController())
                                {
                                    stream.Position -= 2;
                                }
                            }
                            else
                            // The subtype/count is part of the controller data, move back.
                            {
                                stream.Position -= 4;
                            }
                        }
                    }        // exits
                }            // exits
            }
            else             // Behavior controller, including names and size specifiers.
            {
                // Check if the stream contains a controller name.
                string readControllerName = PeekName(reader);

                // Substitute name of controller with the one we read from stream, if it does not match.
                if (controllerName == null || controllerName != readControllerName)
                {
                    Debug.WriteLine("Controller name mismatch between '{0}' and '{1}', using '{1}'.",
                                    controllerName ?? "<unspecified>",
                                    readControllerName);

                    controllerName = readControllerName;
                }

                profile = _controllerAssembly.GetControllerProfilesByName(controllerName).FirstOrDefault();
            }

            // If we have found a profile to use, try to read the controller.
            if (profile != ControllerProfile.Unknown && !string.IsNullOrEmpty(controllerName))
            {
                long controllerStartPosition = stream.Position;

                // Attempt to parse controller, starting with newest implementation.
                Type previousControllerType = null;
                Type controllerType         = null;
                while (true)
                {
                    try
                    {
                        if (_controllerAssembly.TryGetControllerType(controllerName, profile, out controllerType) && previousControllerType != controllerType)
                        {
                            Controller            controller = _controllerFactory.CreateController(controllerType, false);
                            IControllerSerializer cs         = _controllerSerializerResolver.GetSerializer(controllerType);
                            cs.Deserialize(stream, controller);
                            return(controller);
                        }

                        previousControllerType = controllerType;
                    }
                    catch (Exception ex)
                    {
                        // Reset stream to beginning.
                        stream.Position = controllerStartPosition;
                        Debug.WriteLine(ex.Message);

                        previousControllerType = controllerType;
                    }

                    // If we reach the oldest version of SH, we are finished.
                    if (profile >= ControllerProfile.SH3)
                    {
                        break;
                    }

                    // Move to next profile.
                    profile++;
                }
            }

            // If not implemented, return the raw bytes.
            stream.Position = currentPos;
            return(reader.ReadBytes((int)(stream.Length - currentPos)));
        }
Esempio n. 2
0
        /// <summary>
        /// Writes a controller to a stream.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="controller"></param>
        public void Write(Stream stream, object controller)
        {
            if (controller == null)
            {
                throw new ArgumentNullException(nameof(controller));
            }

            // For fallback support, allow byte arrays/streams to be passed.
            // We simply write it as is.
            if (controller is byte[] byteBuffer)
            {
                stream.Write(byteBuffer, 0, byteBuffer.Length);
                return;
            }

            if (controller is Stream inputStream)
            {
                inputStream.CopyTo(stream);
                return;
            }

            using (var writer = new BinaryWriter(stream, FileEncoding.Default, true))
            {
                Type controllerType      = controller.GetType();
                IControllerSerializer cs = _controllerSerializerResolver.GetSerializer(controllerType);

                if (!controllerType.IsBehaviorController())
                {
                    ControllerAttribute controllerAttribute = controllerType.GetCustomAttribute <ControllerAttribute>() ?? new ControllerAttribute();
                    if (controllerAttribute.SubType.HasValue)
                    {
                        writer.Write(controllerAttribute.SubType.Value);
                    }

                    if (controllerType.IsAnimationController() || !controllerAttribute.SubType.HasValue)
                    {
                        // Skip writing the count field, the serializer will take care of it.
                    }
                    else
                    {
                        writer.Write((ushort)0);
                    }

                    cs.Serialize(stream, (Controller)controller);

                    return;
                }

                // Test if the type is from our controller assembly.
                if (!_controllerFactory.CanCreate(controllerType))
                {
                    throw new NotSupportedException("Invalid controller.");
                }

                // We don't know the size yet, so just write 0 for now.
                writer.Write(0);

                long startPos = stream.Position;

                cs.Serialize(stream, (Controller)controller);

                // After the controller is written, determine and write the size.
                long currentPos = stream.Position;
                stream.Position = startPos - 4;
                writer.Write((int)(currentPos - startPos));

                // Restore position to the end of the controller.
                stream.Position = currentPos;
            }
        }