/// <summary> /// Installs the specified r2d to the specified export. Specify an asset name if you wish to use a specific asset in the RTexture2D object. /// </summary> /// <param name="r2d"></param> /// <param name="export"></param> /// <param name="asset"></param> public static void InstallTexture(RTexture2D r2d, ExportEntry export, string asset = null) { // If no asset was specified pick a random asset asset ??= r2d.FetchRandomTextureAsset(); // 2. Is this asset already parsed? if (r2d.InstantiatedItems.TryGetValue(asset, out var instantiated)) { // It's already been instantiated. Just use this data instead MERLog.Information($@"Writing out cached asset {asset} for {export.InstancedFullPath}"); export.WritePropertiesAndBinary(instantiated.props, instantiated.texData); } else { MERLog.Information($@"Installing texture asset {asset} for {export.InstancedFullPath}"); // 3. Asset has not been setup yet. Write out the precomputed data. export.WriteBinary(GetTextureAssetBinary(asset)); // 4. Read in the data so it's in the correct context. var ut2d = ObjectBinary.From <UTexture2D>(export); // 5. Move compressed mips to TFC foreach (var mip in ut2d.Mips) { if (mip.IsCompressed) { mip.DataOffset = TFCBuilder.WriteMip(r2d, mip.Mip); // If it's compressed it needs to be stored externally. if (mip.StorageType == StorageTypes.pccLZO) { mip.StorageType = StorageTypes.extLZO; } if (mip.StorageType == StorageTypes.pccZlib) { mip.StorageType = StorageTypes.extZlib; } } } // 6. Setup the properties var props = export.GetProperties(); props.AddOrReplaceProp(new IntProperty(ut2d.Mips.Count - 1, @"MipTailBaseIdx")); props.AddOrReplaceProp(new IntProperty(ut2d.Mips[0].SizeX, @"SizeX")); props.AddOrReplaceProp(new IntProperty(ut2d.Mips[0].SizeY, @"SizeY")); props.AddOrReplaceProp(r2d.PreMountTexture ? ActiveBuilder.BGTFCNameProp : ActiveBuilder.DLCTFCNameProp); props.AddOrReplaceProp(r2d.PreMountTexture ? ActiveBuilder.BGTFCGuidProp : ActiveBuilder.DLCTFCGuidProp); if (r2d.LODGroup != null) { // Write a new LOD property props.AddOrReplaceProp(r2d.LODGroup); } // 7. Commit the export export.WritePropertiesAndBinary(props, ut2d); // 8. Cache the work that's been done so we don't need to it again. r2d.InstantiatedItems[asset] = (props, ut2d); } }
/// <summary> /// ME2 SPECIFIC<para/> /// Repoint a WwiseStream to play the data from another, typically across banks. /// </summary> /// <param name="originalExport">The audio you want to play (e.g. this is the audio that will be 'installed')</param> /// <param name="targetAudioStream">The audio stream that you want to replace.</param> public static void RepointWwiseStream(ExportEntry originalExport, ExportEntry targetAudioStream) { var props = originalExport.GetProperties(); var bin = ObjectBinary.From <WwiseStream>(originalExport); var targetId = targetAudioStream.GetProperty <IntProperty>("Id"); props.AddOrReplaceProp(targetId); targetAudioStream.WritePropertiesAndBinary(props, bin); }
private static void InstallDynamicAnimSetRefForSkeletalMesh(ExportEntry export, RBioEvtSysTrackGesture.Gesture gesture) { // We have parent sequence data var skmDynamicAnimSets = export.GetProperty <ArrayProperty <ObjectProperty> >("AnimSets") ?? new ArrayProperty <ObjectProperty>("AnimSets"); // Check to see if there is any item that uses our bioanimset var bioAnimSet = gesture.GetBioAnimSet(export.FileRef); if (bioAnimSet != null) { ExportEntry skmBioDynamicAnimSet = null; foreach (var skmDynAnimSet in skmDynamicAnimSets) { var kEntry = skmDynAnimSet.ResolveToEntry(export.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq var associatedset = kEntry.GetProperty <ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef); if (associatedset == bioAnimSet) { // It's this one skmBioDynamicAnimSet = kEntry; break; } } if (skmBioDynamicAnimSet == null) { // We need to generate a new one PropertyCollection props = new PropertyCollection(); props.Add(new NameProperty(gesture.GestureSet, "m_nmOrigSetName")); props.Add(new ArrayProperty <ObjectProperty>("Sequences")); props.Add(new ObjectProperty(bioAnimSet, "m_pBioAnimSetData")); skmBioDynamicAnimSet = ExportCreator.CreateExport(export.FileRef, $"BioDynamicAnimSet", "BioDynamicAnimSet", export); // Write a blank count of 0 - we will update this in subsequent call // This must be here to ensure parser can read it skmBioDynamicAnimSet.WritePropertiesAndBinary(props, new byte[4]); skmDynamicAnimSets.Add(new ObjectProperty(skmBioDynamicAnimSet)); // Add new export to sequence's list of biodynamicanimsets export.WriteProperty(skmDynamicAnimSets); } var currentObjs = skmBioDynamicAnimSet.GetProperty <ArrayProperty <ObjectProperty> >("Sequences"); if (currentObjs.All(x => x.Value != gesture.Entry.UIndex)) { // We need to add our item to it currentObjs.Add(new ObjectProperty(gesture.Entry)); var bin = ObjectBinary.From <BioDynamicAnimSet>(skmBioDynamicAnimSet); bin.SequenceNamesToUnkMap[gesture.GestureAnim] = 1; // Not sure what the value should be, or if game actually reads this // FIX IT IF WE EVER FIGURE IT OUT! skmBioDynamicAnimSet.WriteProperty(currentObjs); skmBioDynamicAnimSet.WriteBinary(bin); } } }
/// <summary> /// ME2 SPECIFIC<para/> /// Repoint a WwiseStream to play the data from another precomputed stream. /// </summary> /// <param name="originalExport">The audio you want to play (e.g. this is the audio that will be 'installed')</param> /// <param name="targetAudioStream">The audio stream that you want to replace.</param> public static void RepointWwiseStreamToInfo(RMusic.MusicStreamInfo streamInfo, ExportEntry targetAudioStream) { WwiseStream stream = new WwiseStream(); stream.Filename = ""; // Just make sure it's not set to null so internal code thinks this is not Pcc stored. stream.DataSize = streamInfo.DataSize; stream.Unk1 = stream.Unk2 = stream.Unk3 = stream.Unk4 = stream.Unk5 = 1; stream.UnkGuid = streamInfo.UnkGuid; stream.DataOffset = streamInfo.DataOffset; PropertyCollection props = new PropertyCollection(); props.Add(targetAudioStream.GetProperty <IntProperty>("Id")); // Use the existing Id props.Add(new NameProperty(streamInfo.Filename, "Filename")); // AFC file props.Add(new NameProperty(streamInfo.BankName, "BankName")); // ? targetAudioStream.WritePropertiesAndBinary(props, stream); }
/* * private static void VerifyGesturesWork(ExportEntry trackExport) * { * var gestures = RBioEvtSysTrackGesture.GetGestures(trackExport); * var defaultPose = RBioEvtSysTrackGesture.GetDefaultPose(trackExport); * * var gesturesToCheck = gestures.Append(defaultPose).ToList(); * * // Get the containing sequence * var owningSequence = SeqTools.GetParentSequence(trackExport); * while (owningSequence.ClassName != "Sequence") * { * owningSequence = owningSequence.Parent as ExportEntry; * var parSeq = SeqTools.GetParentSequence(owningSequence); * if (parSeq != null) * { * owningSequence = parSeq; * } * } * * var kismetBioDynamicAnimSets = owningSequence.GetProperty<ArrayProperty<ObjectProperty>>("m_aBioDynAnimSets"); * if (kismetBioDynamicAnimSets == null) * { * // We don't have any animsets! * throw new Exception("Track's sequence is missing animsets property!"); * } * * // Get a list of all supported animations * List<Gesture> supportedGestures = new List<Gesture>(); * foreach (var kbdas in kismetBioDynamicAnimSets) * { * var sequenceBioDynamicAnimSet = kbdas.ResolveToEntry(trackExport.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq * var associatedset = sequenceBioDynamicAnimSet.GetProperty<ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(trackExport.FileRef); * * } * * // Check all gestures * foreach (var gesture in gesturesToCheck) * { * var bioAnimSet = gesture.GetBioAnimSet(trackExport.FileRef); * * } * * * * } * * internal class TestingBioDynamicAnimSet * { * public NameReference OrigSetName { get; } * public List<string> SupportedGesturesFullPaths { get; } * public IEntry BioAnimSetData { get; } * * internal TestingBioDynamicAnimSet(ExportEntry export) * { * var props = export.GetProperties(); * OrigSetName = props.GetProp<NameProperty>("m_nmOrigSetName").Value; * BioAnimSetData = props.GetProp<ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef); * SupportedGesturesFullPaths = props.GetProp<ArrayProperty<ObjectProperty>>("Sequences").Select(x => x.ResolveToEntry(export.FileRef).InstancedFullPath).ToList(); * } * } */ private static void InstallDynamicAnimSetRefForSeq(ref ExportEntry owningSequence, ExportEntry export, Gesture gesture) { // Find owning sequence if (owningSequence == null) { owningSequence = export; } while (owningSequence.ClassName != "Sequence") { owningSequence = owningSequence.Parent as ExportEntry; var parSeq = SeqTools.GetParentSequence(owningSequence); if (parSeq != null) { owningSequence = parSeq; } } // We have parent sequence data var kismetBioDynamicAnimSets = owningSequence.GetProperty <ArrayProperty <ObjectProperty> >("m_aBioDynAnimSets") ?? new ArrayProperty <ObjectProperty>("m_aBioDynamicAnimSets"); // Check to see if there is any item that uses our bioanimset var bioAnimSet = gesture.GetBioAnimSet(export.FileRef); if (bioAnimSet != null) { ExportEntry kismetBDAS = null; foreach (var kbdas in kismetBioDynamicAnimSets) { var kEntry = kbdas.ResolveToEntry(export.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq var associatedset = kEntry.GetProperty <ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef); if (associatedset == bioAnimSet) { // It's this one kismetBDAS = kEntry; break; } } if (kismetBDAS == null) { // We need to generate a new one PropertyCollection props = new PropertyCollection(); props.Add(new NameProperty(gesture.GestureSet, "m_nmOrigSetName")); props.Add(new ArrayProperty <ObjectProperty>("Sequences")); props.Add(new ObjectProperty(bioAnimSet, "m_pBioAnimSetData")); kismetBDAS = ExportCreator.CreateExport(export.FileRef, $"KIS_DYN_{gesture.GestureSet}", "BioDynamicAnimSet", owningSequence); kismetBDAS.indexValue = 0; // Write a blank count of 0 - we will update this in subsequent call // This must be here to ensure parser can read it kismetBDAS.WritePropertiesAndBinary(props, new byte[4]); kismetBioDynamicAnimSets.Add(new ObjectProperty(kismetBDAS)); // Add new export to sequence's list of biodynamicanimsets owningSequence.WriteProperty(kismetBioDynamicAnimSets); } var currentObjs = kismetBDAS.GetProperty <ArrayProperty <ObjectProperty> >("Sequences"); if (currentObjs.All(x => x.Value != gesture.Entry.UIndex)) { // We need to add our item to it currentObjs.Add(new ObjectProperty(gesture.Entry)); var bin = ObjectBinary.From <BioDynamicAnimSet>(kismetBDAS); bin.SequenceNamesToUnkMap[gesture.GestureAnim] = 1; // Not sure what the value should be, or if game actually reads this // FIX IT IF WE EVER FIGURE IT OUT! kismetBDAS.WriteProperty(currentObjs); kismetBDAS.WriteBinary(bin); } } }