// Methods
        public override void Load(Stream fileStream,
                                  Dictionary <string, SetObjectType> objectTemplates)
        {
            if (objectTemplates == null)
            {
                throw new ArgumentNullException("objectTemplates",
                                                "Cannot load Heroes set data without object templates.");
            }

            // We use lists so we don't have to skip null entries later
            var objs         = new List <SetObject>();
            var miscEntryIDs = new List <ushort>();

            // Object Entries
            var reader = new ExtendedBinaryReader(fileStream, Encoding.ASCII, true);

            for (uint i = 0; i < HeroesObjectLimit; ++i)
            {
                var   pos  = reader.ReadVector3();
                float rotX = (float)(reader.ReadInt32() * 180.0 / 32768.0);
                float rotY = (float)(reader.ReadInt32() * 180.0 / 32768.0);
                float rotZ = (float)(reader.ReadInt32() * 180.0 / 32768.0);

                ushort unknown1  = reader.ReadUInt16();
                byte   stageType = reader.ReadByte();
                byte   unknown2  = reader.ReadByte();
                uint   unknown3  = reader.ReadUInt32();
                ulong  unknown4  = reader.ReadUInt64(); // Repeat of the last 8 bytes?

                byte objList        = reader.ReadByte();
                byte objType        = reader.ReadByte();
                byte linkID         = reader.ReadByte();
                byte renderDistance = reader.ReadByte();

                ushort unknown5 = reader.ReadUInt16();
                if (unknown5 != 0)
                {
                    Console.WriteLine("WARNING: Unknown5 != 0! ({0})", unknown5);
                }

                ushort miscEntryID = reader.ReadUInt16();

                // Check if what we just read is a blank entry
                if (objList == 0 && objType == 0)
                {
                    continue;
                }

                // Make sure we have a template for the object as well
                string objectType = string.Format("{0:x2}-{1:x2}", objList, objType);
                if (!objectTemplates.ContainsKey(objectType))
                {
                    Console.WriteLine("Skipping obj type {0}...", objectType);
                    continue;
                }

                // Otherwise, generate a new object and add it to the list.
                miscEntryIDs.Add(miscEntryID);
                objs.Add(new SetObject()
                {
                    ObjectID   = i,
                    ObjectType = objectType,

                    CustomData =
                    {
                        { "StageType",      new SetObjectParam(typeof(byte), stageType)      },
                        { "LinkID",         new SetObjectParam(typeof(byte), linkID)         },
                        { "RenderDistance", new SetObjectParam(typeof(byte), renderDistance) },
                    },

                    Transform = new SetObjectTransform()
                    {
                        Position = pos,
                        // TODO: Make sure rotation is correct
                        Rotation = new Quaternion(new Vector3(rotX, rotY, rotZ), false)
                    }
                });
            }

            // Object Parameters
            for (int i = 0; i < objs.Count; ++i)
            {
                var obj = objs[i];

                // Get the Set Object Type and jump to the correct misc entry
                var setObjType = objectTemplates[obj.ObjectType];

                reader.BaseStream.Position = 4 + // We skip the first 4 bytes like the game does
                                             MiscEntriesStartPos + (miscEntryIDs[i] * MiscEntryLength);

                // Read the parameters according to the template
                foreach (var param in setObjType.Parameters)
                {
                    obj.Parameters.Add(new SetObjectParam(param.DataType,
                                                          reader.ReadByType(param.DataType)));
                }
            }

            Objects.AddRange(objs);
        }
예제 #2
0
        private static SetObject ReadObject(ExtendedBinaryReader reader,
                                            SetObjectType objTemplate, string objType, SOBJType type,
                                            bool rawDataMode = false) // true = full, false = only remaining bytes
        {
            // For some reason these separate values are saved as one uint rather than two ushorts.
            // Because of this, the values are in a different order depending on endianness, and
            // this is the easiest known way to read them.
            uint   unknownValue = reader.ReadUInt32();
            ushort unknown1     = (ushort)((unknownValue >> 16) & 0xFFFF);
            ushort objID        = (ushort)(unknownValue & 0xFFFF);

            var obj = new SetObject()
            {
                ObjectType = objType,
                ObjectID   = objID
            };

            uint  unknown2 = reader.ReadUInt32();
            uint  unknown3 = reader.ReadUInt32();
            float unknown4 = reader.ReadSingle();

            float rangeIn          = reader.ReadSingle();
            float rangeOut         = reader.ReadSingle();
            uint  parent           = (type == SOBJType.LostWorld) ? reader.ReadUInt32() : 0;
            uint  transformsOffset = reader.ReadUInt32();

            uint transformCount = reader.ReadUInt32();
            uint unknown5       = reader.ReadUInt32();
            uint unknown6       = (type == SOBJType.LostWorld) ? reader.ReadUInt32() : 0;
            uint unknown7       = (type == SOBJType.LostWorld) ? reader.ReadUInt32() : 0;

            // Call me crazy, but I have a weird feeling these values aren't JUST padding
            if (unknown3 != 0 || unknown5 != 0 || unknown6 != 0 || unknown7 != 0)
            {
                Console.WriteLine("WARNING: Not padding?! ({0},{1},{2},{3})",
                                  unknown3, unknown5, unknown6, unknown7);
            }

            // Add custom data to object
            obj.CustomData.Add("Unknown1", new SetObjectParam(typeof(ushort), unknown1));
            obj.CustomData.Add("Unknown2", new SetObjectParam(typeof(uint), unknown2));
            obj.CustomData.Add("Unknown3", new SetObjectParam(typeof(uint), unknown3));
            obj.CustomData.Add("Unknown4", new SetObjectParam(typeof(float), unknown4));
            obj.CustomData.Add("RangeIn", new SetObjectParam(typeof(float), rangeIn));
            obj.CustomData.Add("RangeOut", new SetObjectParam(typeof(float), rangeOut));

            if (type == SOBJType.LostWorld)
            {
                obj.CustomData.Add("Parent", new SetObjectParam(typeof(uint), parent));
            }

            // Skip loading parameters if template doesn't exist
            if (objTemplate != null)
            {
                // Get Raw Byte Length
                var  rawDataLenExtra = objTemplate.GetExtra("RawByteLength");
                long paramBegin      = reader.BaseStream.Position;
                int  rawLength       = 0;

                if (rawDataLenExtra != null &&
                    !string.IsNullOrEmpty(rawDataLenExtra.Value))
                {
                    int.TryParse(rawDataLenExtra.Value, out rawLength);
                }

                // Read all the data then return to beginning
                if (rawDataMode == true && rawLength != 0)
                {
                    obj.CustomData.Add("RawParamData", new SetObjectParam(typeof(byte[]),
                                                                          reader.ReadBytes(rawLength)));
                    reader.JumpTo(paramBegin);
                }

                // Parameters
                foreach (var param in objTemplate.Parameters)
                {
                    // For compatibility with SonicGlvl templates.
                    if (param.Name == "Unknown1" || param.Name == "Unknown2" ||
                        param.Name == "Unknown3" || param.Name == "RangeIn" ||
                        param.Name == "RangeOut" || param.Name == "Parent")
                    {
                        continue;
                    }

                    // Read Special Types/Fix Padding
                    if (param.DataType == typeof(uint[]))
                    {
                        // Data Info
                        reader.FixPadding(4);
                        uint arrOffset  = reader.ReadUInt32();
                        uint arrLength  = reader.ReadUInt32();
                        uint arrUnknown = reader.ReadUInt32();
                        long curPos     = reader.BaseStream.Position;

                        // Data
                        var arr = new uint[arrLength];
                        reader.JumpTo(arrOffset, false);

                        for (uint i = 0; i < arrLength; ++i)
                        {
                            arr[i] = reader.ReadUInt32();
                        }

                        obj.Parameters.Add(new SetObjectParam(param.DataType, arr));
                        reader.BaseStream.Position = curPos;
                        continue;
                    }
                    else if (param.DataType == typeof(string))
                    {
                        // Data Info
                        uint   strOffset  = reader.ReadUInt32();
                        uint   strUnknown = reader.ReadUInt32();
                        string str        = null;

                        // Data
                        if (strOffset != 0)
                        {
                            long curPos = reader.BaseStream.Position;
                            reader.JumpTo(strOffset, false);

                            str = reader.ReadNullTerminatedString();
                            reader.BaseStream.Position = curPos;
                        }

                        obj.Parameters.Add(new SetObjectParam(param.DataType, str));
                        continue;
                    }
                    else if (param.DataType == typeof(float) ||
                             param.DataType == typeof(int) || param.DataType == typeof(uint))
                    {
                        reader.FixPadding(4);
                    }
                    else if (type == SOBJType.LostWorld && param.DataType == typeof(Vector3))
                    {
                        reader.FixPadding(16);
                    }

                    // Read Data
                    var objParam = new SetObjectParam(param.DataType,
                                                      reader.ReadByType(param.DataType));
                    obj.Parameters.Add(objParam);
                }

                if (rawDataMode == false)
                {
                    long knownParamLength = (reader.BaseStream.Position - paramBegin);
                    long remainingBytes   = (rawLength - knownParamLength);

                    obj.CustomData.Add("RawParamData", new SetObjectParam(typeof(byte[]),
                                                                          reader.ReadBytes((int)remainingBytes)));
                }
            }

            // Transforms
            uint childCount = transformCount - 1;

            obj.Children = new SetObjectTransform[childCount];
            reader.JumpTo(transformsOffset, false);

            obj.Transform = ReadTransform(reader, type == SOBJType.LostWorld);
            for (uint i = 0; i < childCount; ++i)
            {
                obj.Children[i] = ReadTransform(reader,
                                                type == SOBJType.LostWorld);
            }

            return(obj);
        }