示例#1
0
        public static DLCSong ConvertDLCSong(DataArray songDta, GameArchives.IDirectory songRoot)
        {
            var path          = songDta.Array("song").Array("name").String(1);
            var hopoThreshold = songDta.Array("song").Array("hopo_threshold")?.Int(1) ?? 170;
            var shortname     = path.Split('/').Last();
            var midPath       = shortname + ".mid";
            var artPath       = $"gen/{shortname}_keep.png_xbox";
            var miloPath      = $"gen/{shortname}.milo_xbox";
            var songId        = songDta.Array("song_id").Node(1);
            var name          = songDta.Array("name").String(1);
            var artist        = songDta.Array("artist").String(1);
            var mid           = MidiCS.MidiFileReader.FromBytes(songRoot.GetFileAtPath(midPath).GetBytes());

            // TODO: Catch possible conversion exceptions? i.e. Unsupported milo version
            var milo     = MiloFile.ReadFromStream(songRoot.GetFileAtPath(miloPath).GetStream());
            var songData = SongDataConverter.ToSongData(songDta);

            Texture.Texture artwork = null;
            if (songData.AlbumArt)
            {
                artwork = Texture.TextureConverter.MiloPngToTexture(songRoot.GetFileAtPath(artPath).GetStream());
            }
            return(new DLCSong
            {
                SongData = songData,
                Lipsync = LipsyncConverter.FromMilo(milo),
                Mogg = songRoot.GetFile(shortname + ".mogg"),
                MoggDta = MakeMoggDta(songDta),
                MoggSong = DTX.FromDtaString($"(mogg_path \"{shortname}.mogg\")\r\n(midi_path \"{shortname}.rbmid\")\r\n"),
                RBMidi = RBMidConverter.ToRBMid(mid, hopoThreshold),
                Artwork = artwork,
                RBSong = MakeRBSong(songDta)
            });
        }
示例#2
0
        public static DataArray MakeMoggDta(DataArray array)
        {
            var moggDta    = new DataArray();
            var trackArray = new DataArray();

            trackArray.AddNode(DataSymbol.Symbol("tracks"));
            var trackSubArray = trackArray.AddNode(new DataArray());

            foreach (var child in array.Array("song").Array("tracks").Array(1).Children)
            {
                if (child is DataArray a && a.Children[1] is DataArray b && b.Count > 0)
                {
                    trackSubArray.AddNode(child);
                }
            }
            var totalTracks = array.Array("song").Array("pans").Array(1).Children.Count;
            // Get the last track number. This is based on the assumption that the tracks are in order
            // We have to filter out empty track arrays because GHtoRB(?) does stuff like (keys ()) instead
            // of leaving out the keys array entirely
            var lastTrack = ((trackSubArray.Children
                              .Where(x => x is DataArray dx ? dx.Array(1).Children.Count > 0 : false)
                              .Last() as DataArray)
                             .Array(1).Children.Last() as DataAtom).Int;
            var crowdChannel = array.Array("song").Array("crowd_channels")?.Int(1);

            if (crowdChannel != null)
            {
                if (crowdChannel == lastTrack + 2)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1})"));
                }
                else if (crowdChannel == lastTrack + 3)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1} {lastTrack + 2})"));
                }
                trackSubArray.AddNode(DTX.FromDtaString($"crowd ({crowdChannel} {crowdChannel + 1})"));
            }
            else
            {
                if (totalTracks == lastTrack + 2)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1})"));
                }
                else if (totalTracks == lastTrack + 3)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1} {lastTrack + 2})"));
                }
            }
            moggDta.AddNode(trackArray);
            moggDta.AddNode(array.Array("song").Array("pans"));
            moggDta.AddNode(array.Array("song").Array("vols"));
            return(moggDta);
        }
示例#3
0
        /// <summary>
        /// Creates a PropKeys struct from the given keyframes
        /// </summary>
        static StructValue MakeAnimProps(string drivenProp, List <Tuple <float, string> > frames)
        {
            var propkeys = DTX.FromDtaString(
                "(keys ()) " +
                "(interpolation 0) " +
                $"(driven_prop (0 0 RBVenueAuthoring 0 1 {drivenProp}))");
            var keyframes = propkeys.Array("keys").Array(1);

            foreach (var(frame, key) in frames)
            {
                var keyframe = new DataArray();
                keyframe.AddNode(new DataAtom(frame));
                keyframe.AddNode(DataSymbol.Symbol(key));
                keyframes.AddNode(keyframe);
            }
            return(StructValue.FromData(propKeysType, propkeys));
        }
示例#4
0
        public static DataArray MakeMoggDta(DataArray array)
        {
            var moggDta    = new DataArray();
            var trackArray = new DataArray();

            trackArray.AddNode(DataSymbol.Symbol("tracks"));
            var trackSubArray = trackArray.AddNode(new DataArray());

            foreach (var child in array.Array("song").Array("tracks").Array(1).Children)
            {
                trackSubArray.AddNode(child);
            }
            var totalTracks = array.Array("song").Array("pans").Array(1).Children.Count;
            var lastTrack   = ((trackSubArray.Children.Last() as DataArray)
                               .Array(1).Children.Last() as DataAtom).Int;
            var crowdChannel = array.Array("song").Array("crowd_channels")?.Int(1);

            if (crowdChannel != null)
            {
                if (crowdChannel == lastTrack + 2)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1})"));
                }
                else if (crowdChannel == lastTrack + 3)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1} {lastTrack + 2})"));
                }
                trackSubArray.AddNode(DTX.FromDtaString($"crowd ({crowdChannel} {crowdChannel + 1})"));
            }
            else
            {
                if (totalTracks == lastTrack + 2)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1})"));
                }
                else if (totalTracks == lastTrack + 3)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1} {lastTrack + 2})"));
                }
            }
            moggDta.AddNode(trackArray);
            moggDta.AddNode(array.Array("song").Array("pans"));
            moggDta.AddNode(array.Array("song").Array("vols"));
            return(moggDta);
        }
示例#5
0
        static GameObject[] ExtractVenuePropAnimsFromMidi(MidiFile mf)
        {
            var   objs      = new List <GameObject>();
            short index     = 1;
            var   editorCom = new Component
            {
                Rev      = 3,
                Name1    = "Editor",
                Name2    = "Editor",
                Unknown2 = 2,
                Props    = new[] { new Property("capabilities", new UIntValue(62)) }
            };

            objs.Add(new GameObject
            {
                Id = new GameObjectId {
                    Index = 0, Layer = 0
                },
                Rev        = 2,
                Name       = "root",
                Components = new[]
                {
                    editorComponent,
                    entityHeaderComponent,
                    new Component
                    {
                        Rev      = 3,
                        Name1    = "PropAnim",
                        Name2    = "PropAnim",
                        Unknown2 = 0,
                        Props    = StructValue.FromData(
                            StructType.FromData(DTX.FromDtaString(
                                                    @"(props 
                            (frame_range_start float)
                            (frame_range_end float)
                            (time_units int)
                            (is_looping bool))")),
                            DTX.FromDtaString(
                                @"(frame_range_start 3.402823E+38)
                          (frame_range_end -3.402823E+38)
                          (time_units 0)
                          (is_looping 0)")).Props
                    }
                }
            });
            var tracks  = new Midi.MidiHelper().ProcessTracks(mf);
            var partMap = new Dictionary <string, string> {
                { "PART BASS", "bass_intensity" },
                { "PART GUITAR", "guitar_intensity" },
                { "PART DRUMS", "drum_intensity" },
                { "PART VOCALS", "mic_intensity" },
#if DEBUG
                { "PART KEYS", "keyboard_intensity" },
#endif
            };
            var intensityMap = new Dictionary <string, string>
            {
                // All
                { "[idle_realtime]", "idle_realtime" },
                { "[idle]", "idle" },
                { "[idle_intense]", "idle_intense" },
                { "[play]", "play" },
                { "[mellow]", "mellow" },
                { "[intense]", "intense" },
                // Guitar/bass only
                { "[play_solo]", "play_solo" },
                // Vocal only
                { "[tambourine_start]", "tambourine_start" },
                { "[tambourine_end]", "tambourine_end" },
                { "[cowbell_start]", "cowbell_start" },
                { "[cowbell_end]", "cowbell_end" },
                { "[clap_start]", "clap_start" },
                { "[clap_end]", "clap_end" },
                // Drum only
                { "[ride_side_true]", "ride_side_true" },
                { "[ride_side_false]", "ride_side_false" },
            };

            void AddAnimTrack(StructValue props)
            {
                objs.Add(new GameObject
                {
                    Id = new GameObjectId {
                        Index = index, Layer = index++
                    },
                    Rev        = 2,
                    Name       = "Keys type 11",
                    Components = new[]
                    {
                        editorCom,
                        new Component
                        {
                            Rev      = 3,
                            Name1    = "PropKeysSymCom",
                            Name2    = "PropKeys",
                            Unknown2 = 0,
                            Props    = props.Props
                        }
                    }
                });
            }

            StructValue MakeAnimProps(string drivenProp, List <Tuple <float, string> > frames)
            {
                var propkeys = DTX.FromDtaString(
                    "(keys ()) " +
                    "(interpolation 0) " +
                    $"(driven_prop (0 0 RBVenueAuthoring 0 1 {drivenProp}))");
                var keyframes = propkeys.Array("keys").Array(1);

                foreach (var(frame, key) in frames)
                {
                    var keyframe = new DataArray();
                    keyframe.AddNode(new DataAtom(frame));
                    keyframe.AddNode(DataSymbol.Symbol(key));
                    keyframes.AddNode(keyframe);
                }
                return(StructValue.FromData(propKeysType, propkeys));
            }

            foreach (var track in tracks)
            {
                if (!partMap.ContainsKey(track.Name))
                {
                    continue;
                }
                var frames = new List <Tuple <float, string> >();
                foreach (var n in track.Items)
                {
                    if (n is Midi.MidiText t)
                    {
                        if (intensityMap.ContainsKey(t.Text))
                        {
                            frames.Add(Tuple.Create((float)n.StartTicks / mf.TicksPerQN, intensityMap[t.Text]));
                        }
                    }
                }
                AddAnimTrack(MakeAnimProps(partMap[track.Name], frames));
            }
            AddAnimTrack(MakeAnimProps("shot_bg", new List <Tuple <float, string> > {
                Tuple.Create(0f, "coop_all_far")
            }));
            AddAnimTrack(MakeAnimProps("shot_bk", new List <Tuple <float, string> > {
                Tuple.Create(0f, "coop_all_far")
            }));
            AddAnimTrack(MakeAnimProps("shot_gk", new List <Tuple <float, string> > {
                Tuple.Create(0f, "coop_all_far")
            }));
            AddAnimTrack(MakeAnimProps("crowd", new List <Tuple <float, string> > {
                Tuple.Create(0f, "crowd_realtime")
            }));
            AddAnimTrack(MakeAnimProps("world_event", new List <Tuple <float, string> > {
                Tuple.Create(0f, "none")
            }));
            //AddAnimTrack(MakeAnimProps("part2_sing", new List<Tuple<float, string>> {Tuple.Create(0f, "singalong_off") }));
            //AddAnimTrack(MakeAnimProps("part3_sing", new List<Tuple<float, string>> {Tuple.Create(0f, "singalong_off") }));
            //AddAnimTrack(MakeAnimProps("part4_sing", new List<Tuple<float, string>> {Tuple.Create(0f, "singalong_off") }));
            AddAnimTrack(MakeAnimProps("lightpreset", new List <Tuple <float, string> > {
                Tuple.Create(0f, "silhouettes")
            }));
            AddAnimTrack(MakeAnimProps("postproc", new List <Tuple <float, string> > {
                Tuple.Create(0f, "profilm_a")
            }));
            AddAnimTrack(MakeAnimProps("lightpreset_keyframe", new List <Tuple <float, string> > {
                Tuple.Create(0f, "next")
            }));
            AddAnimTrack(MakeAnimProps("spot_bass", new List <Tuple <float, string> > {
                Tuple.Create(0f, "off")
            }));
            AddAnimTrack(MakeAnimProps("spot_guitar", new List <Tuple <float, string> > {
                Tuple.Create(0f, "off")
            }));
            AddAnimTrack(MakeAnimProps("spot_drums", new List <Tuple <float, string> > {
                Tuple.Create(0f, "off")
            }));
            //AddAnimTrack(MakeAnimProps("spot_keyboard", new List<Tuple<float, string>> {Tuple.Create(0f, "off") }));
            AddAnimTrack(MakeAnimProps("spot_vocal", new List <Tuple <float, string> > {
                Tuple.Create(0f, "off")
            }));
            //AddAnimTrack(MakeAnimProps("shot_5", new List<Tuple<float, string>> {Tuple.Create(0f, "coop_all_far") }));
            AddAnimTrack(MakeAnimProps("stagekit_fog", new List <Tuple <float, string> > {
                Tuple.Create(0f, "off")
            }));
            return(objs.ToArray());
        }
示例#6
0
        public static DataArray MakeMoggDta(DataArray array, float volumeAdjustment)
        {
            var moggDta    = new DataArray();
            var trackArray = new DataArray();

            trackArray.AddNode(DataSymbol.Symbol("tracks"));
            var trackSubArray = trackArray.AddNode(new DataArray());

            foreach (var child in array.Array("song").Array("tracks").Array(1).Children)
            {
                if (child is DataArray a && a.Children[1] is DataArray b && b.Count > 0)
                {
                    if (a.Symbol(0).Name == "drum")
                    {
                        switch (b.Count)
                        {
                        //Mix0 (2 channel) Whole kit in a stereo stream
                        case 2:
                            trackSubArray.AddNode(DTX.FromDtaString("drum (0 1)"));
                            break;

                        //Mix1 (4 channel) Mono kick, Mono Snare, Stereo Kit
                        case 4:
                            trackSubArray.AddNode(DTX.FromDtaString("drum (0)"));
                            trackSubArray.AddNode(DTX.FromDtaString("drum (1)"));
                            trackSubArray.AddNode(DTX.FromDtaString("drum (2 3)"));
                            break;

                        //Mix2 (5 channel) Mono kick, Stereo Snare, Stereo Kit
                        case 5:
                            trackSubArray.AddNode(DTX.FromDtaString("drum (0)"));
                            trackSubArray.AddNode(DTX.FromDtaString("drum (1 2)"));
                            trackSubArray.AddNode(DTX.FromDtaString("drum (3 4)"));
                            break;

                        //Mix3 (6 channel) Stereo kick, Stereo Snare, Stereo Kit
                        case 6:
                            trackSubArray.AddNode(DTX.FromDtaString("drum (0 1)"));
                            trackSubArray.AddNode(DTX.FromDtaString("drum (2 3)"));
                            trackSubArray.AddNode(DTX.FromDtaString("drum (4 5)"));
                            break;

                        //Mix4 (3 channel) Mono kick, Stereo Snare+Kit
                        case 3:
                            trackSubArray.AddNode(DTX.FromDtaString("drum (0)"));
                            trackSubArray.AddNode(DTX.FromDtaString("drum (1 2)"));
                            break;

                        default:
                            throw new Exception("You have too many or too few drum tracks. What are you doing?");
                        }
                    }
                    else
                    {
                        trackSubArray.AddNode(child);
                    }
                }
            }
            var totalTracks = array.Array("song").Array("pans").Array(1).Children.Count;
            // Get the last track number. This is based on the assumption that the tracks are in order
            // We have to filter out empty track arrays because GHtoRB(?) does stuff like (keys ()) instead
            // of leaving out the keys array entirely
            var lastTrack = ((trackSubArray.Children
                              .Where(x => x is DataArray dx ? dx.Array(1).Children.Count > 0 : false)
                              .Last() as DataArray)
                             .Array(1).Children.Last() as DataAtom).Int;
            var crowdChannel = array.Array("song").Array("crowd_channels")?.Int(1);

            if (crowdChannel != null)
            {
                if (crowdChannel == lastTrack + 2)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1})"));
                }
                else if (crowdChannel == lastTrack + 3)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1} {lastTrack + 2})"));
                }
                trackSubArray.AddNode(DTX.FromDtaString($"crowd ({crowdChannel} {crowdChannel + 1})"));
            }
            else
            {
                if (totalTracks == lastTrack + 2)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1})"));
                }
                else if (totalTracks == lastTrack + 3)
                {
                    trackSubArray.AddNode(DTX.FromDtaString($"fake ({lastTrack + 1} {lastTrack + 2})"));
                }
            }
            moggDta.AddNode(trackArray);
            moggDta.AddNode(array.Array("song").Array("pans"));

            // Process (vols (...))
            var vols    = array.Array("song").Array("vols");
            var newVols = new DataArray();

            newVols.AddNode(DataSymbol.Symbol("vols"));
            var volsArray = new DataArray();

            for (int i = 0; i < vols.Array(1).Count; i++)
            {
                volsArray.AddNode(new DataAtom(vols.Array(1).Number(i) + volumeAdjustment));
            }
            newVols.AddNode(volsArray);
            moggDta.AddNode(newVols);

            return(moggDta);
        }
示例#7
0
        // TODO: RBSONG
        public static RBSong.RBSong MakeRBSong(DataArray array)
        {
            var drumBank = array.Array("drum_bank")?.Any(1)
                           .Replace("sfx", "fusion/patches")
                           .Replace("_bank.milo", ".fusion")
                           ?? "fusion/patches/kit01.fusion";
            var editorComponent = new Component
            {
                ClassName = "Editor",
                Name      = "Editor",
                Unknown1  = 3,
                Unknown2  = 2,
                Props     = new[]
                {
                    new Property("capabilities", new FlagValue(50))
                }
            };
            var vec3     = StructType.FromData(DTX.FromDtaString("define vec3 (props (x float) (y float) (z float))"));
            var xfm_type = StructType.FromData(DTX.FromDtaString(
                                                   @"define xfm
            (props
              (basis_x vec3)
              (basis_y vec3)
              (basis_z vec3)
              (translate vec3))"));
            var entityHeaderComponent = new Component
            {
                ClassName = "EntityHeader",
                Name      = "EntityHeader",
                Unknown1  = 3,
                Unknown2  = 1,
                Props     = new[]
                {
                    new Property("copy_on_instance", new BoolValue(true)),
                    new Property("drives_parent", new BoolValue(false)),
                    new Property("static", new BoolValue(false)),
                    new Property("instance_polling_mode", new IntValue(0)),
                    new Property("num_instances", new IntValue(0)),
                    new Property("num_meshes", new IntValue(0)),
                    new Property("num_particles", new IntValue(0)),
                    new Property("num_propanims", new IntValue(0)),
                    new Property("num_lights", new IntValue(0)),
                    new Property("num_verts", new IntValue(0)),
                    new Property("num_faces", new IntValue(0)),
                    new Property("changelist", new IntValue(0)),
                    new Property("icon_cam_initialized", new BoolValue(false)),
                    new Property("icon_cam_near", new FloatValue(0.1f)),
                    new Property("icon_cam_far", new FloatValue(1000f)),
                    new Property("icon_cam_xfm", StructValue.FromData(xfm_type, DTX.FromDtaString(
                                                                          @"(basis_x ((x 1.0) (y 0.0) (z 0.0)))
              (basis_y ((x 0.0) (y 1.0) (z 0.0)))
              (basis_z ((x 0.0) (y 0.0) (z 1.0)))
              (translate ((x 0.0) (y 0.0) (z 0.0)))"))),
                    new Property("icon_data",
                                 new ArrayValue(new ArrayType {
                        ElementType = PrimitiveType.Byte, InternalType = RBSong.DataType.Uint8 | RBSong.DataType.Array
                    }, new Value[] { }))
                }
            };

            return(new RBSong.RBSong
            {
                Version = 0xE,
                Object1 = new ObjectContainer
                {
                    Unknown1 = 20,
                    Unknown2 = 1,
                    Unknown3 = 20,
                    Unknown4 = 0,
                    Unknown5 = 1,
                    Entities = new[] {
                        new Entity
                        {
                            Index0 = 0,
                            Index1 = 0,
                            Name = "root",
                            Coms = new Component[] {
                                editorComponent,
                                entityHeaderComponent,
                                new Component
                                {
                                    ClassName = "RBSongMetadata",
                                    Name = "RBSongMetadata",
                                    Unknown1 = 3,
                                    Unknown2 = 4,
                                    Props = new[]
                                    {
                                        new Property("tempo", new SymbolValue("medium")),
                                        new Property("vocal_tonic_note", new LongValue(array.Array("vocal_tonic_note")?.Int(1) ?? 0)),
                                        new Property("vocal_track_scroll_duration_ms", new LongValue(array.Array("song_scroll_speed")?.Int(1) ?? 2300)),
                                        new Property("global_tuning_offset", new FloatValue(array.Array("tuning_offset_cents")?.Number(1) ?? 0)),
                                        new Property("band_fail_sound_event", new SymbolValue("", true)),
                                        new Property("vocal_percussion_patch", new ResourcePathValue("fusion/patches/vox_perc_tambourine.fusion", true)),
                                        new Property("drum_kit_patch", new ResourcePathValue(drumBank)),
                                        new Property("improv_solo_patch", new SymbolValue("gtrsolo_amer_03")),
                                        new Property("dynamic_drum_fill_override", new IntValue(10)),
                                        new Property("improv_solo_volume_db", new FloatValue(-9))
                                    }
                                },
                                new Component
                                {
                                    ClassName = "RBVenueAuthoring",
                                    Name = "RBVenueAuthoring",
                                    Unknown1 = 3,
                                    Unknown2 = 0,
                                    Props = new[]
                                    {
                                        new Property("part2_instrument", new IntValue(2)),
                                        new Property("part3_instrument", new IntValue(0)),
                                        new Property("part4_instrument", new IntValue(1))
                                    }
                                }
                            }
                        }
                    }
                },
                KV = new KeyValue
                {
                    Str1 = "PropAnimResource",
                    Str2 = "venue_authoring_data"
                },
                Object2 = new ObjectContainer
                {
                    Unknown1 = 20,
                    Unknown2 = 1,
                    Unknown3 = 20,
                    Unknown4 = 0,
                    Unknown5 = 1,
                    Entities = new[] {
                        new Entity
                        {
                            Index0 = 0,
                            Index1 = 0,
                            Name = "root",
                            Coms = new[]
                            {
                                editorComponent,
                                entityHeaderComponent,
                                new Component
                                {
                                    ClassName = "PropAnim",
                                    Name = "PropAnim",
                                    Unknown1 = 3,
                                    Unknown2 = 0,
                                    Props = StructValue.FromData(
                                        StructType.FromData(DTX.FromDtaString(
                                                                @"(props 
                                  (frame_range_start float)
                                  (frame_range_end float)
                                  (time_units int)
                                  (is_looping bool))")),
                                        DTX.FromDtaString(
                                            @"(frame_range_start 3.402823E+38)
                                (frame_range_end -3.402823E+38)
                                (time_units 0)
                                (is_looping 0)")).Props
                                }
                            }
                        }
                    }
                }
            });
        }
示例#8
0
        static GameObject[] ExtractVenuePropAnimsFromMidi(MidiFile mf)
        {
            var   objs      = new List <GameObject>();
            short index     = 1;
            var   editorCom = new Component
            {
                Rev      = 3,
                Name1    = "Editor",
                Name2    = "Editor",
                Unknown2 = 2,
                Props    = new[] { new Property("capabilities", new UIntValue(62)) }
            };

            objs.Add(new GameObject
            {
                Id = new GameObjectId {
                    Index = 0, Layer = 0
                },
                Rev        = 2,
                Name       = "root",
                Components = new[]
                {
                    editorComponent,
                    entityHeaderComponent,
                    new Component
                    {
                        Rev      = 3,
                        Name1    = "PropAnim",
                        Name2    = "PropAnim",
                        Unknown2 = 0,
                        Props    = StructValue.FromData(
                            StructType.FromData(DTX.FromDtaString(
                                                    @"(props 
                            (frame_range_start float)
                            (frame_range_end float)
                            (time_units int)
                            (is_looping bool))")),
                            DTX.FromDtaString(
                                @"(frame_range_start 3.402823E+38)
                          (frame_range_end -3.402823E+38)
                          (time_units 0)
                          (is_looping 0)")).Props
                    }
                }
            });
            void AddAnimTrack(StructValue props)
            {
                objs.Add(new GameObject
                {
                    Id = new GameObjectId {
                        Index = index, Layer = index++
                    },
                    Rev        = 2,
                    Name       = "Keys type 11",
                    Components = new[]
                    {
                        editorCom,
                        new Component
                        {
                            Rev      = 3,
                            Name1    = "PropKeysSymCom",
                            Name2    = "PropKeys",
                            Unknown2 = 0,
                            Props    = props.Props
                        }
                    }
                });
            }

            foreach (var kv in ExtractPlayerIntensities(mf))
            {
                AddAnimTrack(MakeAnimProps(kv.Key, kv.Value));
            }
            foreach (var kv in ExtractVenueAnims(mf))
            {
                AddAnimTrack(MakeAnimProps(kv.Key, kv.Value));
            }
            return(objs.ToArray());
        }