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) }); }
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); }
/// <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)); }
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); }
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()); }
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); }
// 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 } } } } } }); }
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()); }