public ModelAnimationGraph ConvertModelAnimationGraph(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, ModelAnimationGraph definition) { if (BlamCache.ResourceGestalt == null) { BlamCache.LoadResourceTags(); } definition.ResourceGroups = ConvertModelAnimationGraphResourceGroups(cacheStream, resourceStreams, definition.ResourceGroups); definition.Modes = definition.Modes.OrderBy(a => a.Name.Set).ThenBy(a => a.Name.Index).ToList(); foreach (var mode in definition.Modes) { mode.WeaponClass = mode.WeaponClass.OrderBy(a => a.Label.Set).ThenBy(a => a.Label.Index).ToList(); foreach (var weaponClass in mode.WeaponClass) { weaponClass.WeaponType = weaponClass.WeaponType.OrderBy(a => a.Label.Set).ThenBy(a => a.Label.Index).ToList(); foreach (var weaponType in weaponClass.WeaponType) { weaponType.Actions = weaponType.Actions.OrderBy(a => a.Label.Set).ThenBy(a => a.Label.Index).ToList(); weaponType.Overlays = weaponType.Overlays.OrderBy(a => a.Label.Set).ThenBy(a => a.Label.Index).ToList(); weaponType.DeathAndDamage = weaponType.DeathAndDamage.OrderBy(a => a.Label.Set).ThenBy(a => a.Label.Index).ToList(); weaponType.Transitions = weaponType.Transitions.OrderBy(a => a.FullName.Set).ThenBy(a => a.FullName.Index).ToList(); foreach (var transition in weaponType.Transitions) { transition.Destinations = transition.Destinations.OrderBy(a => a.FullName.Set).ThenBy(a => a.FullName.Index).ToList(); } } } } return(definition); }
private void MergeMultilingualUnicodeStringList(Stream cacheStream, Stream blamCacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, CachedTag edTag, CachedTag h3Tag) { MultilingualUnicodeStringList h3Def = BlamCache.Deserialize <MultilingualUnicodeStringList>(blamCacheStream, h3Tag); var edDef = CacheContext.Deserialize <MultilingualUnicodeStringList>(cacheStream, edTag); ConvertMultilingualUnicodeStringList(cacheStream, blamCacheStream, resourceStreams, h3Def); var mergedStringCount = 0; for (var i = 0; i < h3Def.Strings.Count; i++) { var found = false; for (var j = 0; j < edDef.Strings.Count; j++) { if (h3Def.Strings[i].StringID == edDef.Strings[j].StringID) { found = true; break; } } if (!found) { var localizedStr = new LocalizedString { StringID = h3Def.Strings[i].StringID, StringIDStr = h3Def.Strings[i].StringIDStr }; edDef.Strings.Add(localizedStr); for (var x = 0; x < 12; x++) { edDef.SetString( localizedStr, (GameLanguage)x, h3Def.GetString( h3Def.Strings[i], (GameLanguage)x)); } mergedStringCount++; } } if (mergedStringCount > 0) { Console.WriteLine($"Merged {mergedStringCount} localized strings."); CacheContext.Serialize(cacheStream, edTag, edDef); } }
private Sound ConvertSound(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, Sound sound, string blamTag_Name) { if (BlamSoundGestalt == null) { BlamSoundGestalt = PortingContextFactory.LoadSoundGestalt(CacheContext, ref BlamCache); } if (!File.Exists(@"Tools\ffmpeg.exe") || !File.Exists(@"Tools\towav.exe")) { Console.WriteLine("Failed to locate sound conversion tools, please install ffmpeg and towav in the Tools folder"); return(null); } // // Convert Sound Tag Data // var platformCodec = BlamSoundGestalt.PlatformCodecs[sound.SoundReference.PlatformCodecIndex]; var playbackParameters = BlamSoundGestalt.PlaybackParameters[sound.SoundReference.PlaybackParameterIndex]; var scale = BlamSoundGestalt.Scales[sound.SoundReference.ScaleIndex]; var promotion = sound.SoundReference.PromotionIndex != -1 ? BlamSoundGestalt.Promotions[sound.SoundReference.PromotionIndex] : new Promotion(); var customPlayBack = sound.SoundReference.CustomPlaybackIndex != -1 ? new List <CustomPlayback> { BlamSoundGestalt.CustomPlaybacks[sound.SoundReference.CustomPlaybackIndex] } : new List <CustomPlayback>(); var loop = sound.Flags.HasFlag(Sound.FlagsValue.LoopingSound); sound.PlaybackParameters = playbackParameters; sound.Scale = scale; sound.PlatformCodec = platformCodec.DeepClone(); sound.Promotion = promotion; sound.CustomPlayBacks = customPlayBack; // // Tag fixes // sound.SampleRate = platformCodec.SampleRate; sound.ImportType = ImportType.SingleLayer; // helps looping sound? there is another value, 10 for Unknown2 but I don't know when to activate it. if (sound.SoundReference.PitchRangeCount > 1) { sound.ImportType = ImportType.MultiLayer; } sound.PlatformCodec.LoadMode = 0; // // Process all the pitch ranges // var xmaFileSize = BlamSoundGestalt.GetFileSize(sound.SoundReference.PitchRangeIndex, sound.SoundReference.PitchRangeCount); if (xmaFileSize < 0) { return(null); } var xmaData = BlamCache.GetSoundRaw(sound.SoundReference.ZoneAssetHandle, xmaFileSize); if (xmaData == null) { return(null); } sound.PitchRanges = new List <PitchRange>(sound.SoundReference.PitchRangeCount); var soundDataAggregate = new byte[0].AsEnumerable(); var currentSoundDataOffset = 0; var totalSampleCount = (uint)0; for (int pitchRangeIndex = sound.SoundReference.PitchRangeIndex; pitchRangeIndex < sound.SoundReference.PitchRangeIndex + sound.SoundReference.PitchRangeCount; pitchRangeIndex++) { totalSampleCount += BlamSoundGestalt.GetSamplesPerPitchRange(pitchRangeIndex); // // Convert Blam pitch range to ElDorado format // var pitchRange = BlamSoundGestalt.PitchRanges[pitchRangeIndex]; pitchRange.ImportName = ConvertStringId(BlamSoundGestalt.ImportNames[pitchRange.ImportNameIndex].Name); pitchRange.PitchRangeParameters = BlamSoundGestalt.PitchRangeParameters[pitchRange.PitchRangeParametersIndex]; pitchRange.Unknown1 = 0; pitchRange.Unknown2 = 0; pitchRange.Unknown3 = 0; pitchRange.Unknown4 = 0; pitchRange.Unknown5 = -1; pitchRange.Unknown6 = -1; //I suspect this unknown7 to be a flag to tell if there is a Unknownblock in extrainfo. (See a sound in udlg for example) pitchRange.Unknown7 = 0; pitchRange.PermutationCount = (byte)BlamSoundGestalt.GetPermutationCount(pitchRangeIndex); pitchRange.Unknown8 = -1; // Add pitch range to ED sound sound.PitchRanges.Add(pitchRange); var newPitchRangeIndex = pitchRangeIndex - sound.SoundReference.PitchRangeIndex; sound.PitchRanges[newPitchRangeIndex].Permutations = new List <Permutation>(); // // Determine the audio channel count // var channelCount = Encoding.GetChannelCount(sound.PlatformCodec.Encoding); // // Set compression format // sound.PlatformCodec.Compression = Compression.MP3; // // Convert Blam permutations to ElDorado format // var useCache = Sounds.UseAudioCacheCommand.AudioCacheDirectory != null; var basePermutationCacheName = Path.Combine( useCache ? Sounds.UseAudioCacheCommand.AudioCacheDirectory.FullName : TempDirectory.FullName, GetTagFileFriendlyName(blamTag_Name)); var permutationCount = BlamSoundGestalt.GetPermutationCount(pitchRangeIndex); var permutationOrder = BlamSoundGestalt.GetPermutationOrder(pitchRangeIndex); var relativePitchRangeIndex = pitchRangeIndex - sound.SoundReference.PitchRangeIndex; for (int i = 0; i < permutationCount; i++) { var permutationIndex = pitchRange.FirstPermutationIndex + i; var permutationSize = BlamSoundGestalt.GetPermutationSize(permutationIndex); var permutationOffset = BlamSoundGestalt.GetPermutationOffset(permutationIndex); var permutation = BlamSoundGestalt.GetPermutation(permutationIndex).DeepClone(); permutation.ImportName = ConvertStringId(BlamSoundGestalt.ImportNames[permutation.ImportNameIndex].Name); permutation.SkipFraction = new Bounds <float>(0.0f, permutation.Gain); permutation.PermutationChunks = new List <PermutationChunk>(); permutation.PermutationNumber = (uint)permutationOrder[i]; permutation.IsNotFirstPermutation = (uint)(permutation.PermutationNumber == 0 ? 0 : 1); string permutationName = $"{basePermutationCacheName}_{relativePitchRangeIndex}_{i}"; string extension = "mp3"; var cacheFileName = $"{permutationName}.{extension}"; bool exists = File.Exists(cacheFileName); byte[] permutationData = null; if (!exists || !useCache) { BlamSound blamSound = SoundConverter.ConvertGen3Sound(BlamCache, BlamSoundGestalt, sound, relativePitchRangeIndex, i, xmaData); permutationData = blamSound.Data; if (useCache) { using (EndianWriter output = new EndianWriter(new FileStream(cacheFileName, FileMode.Create, FileAccess.Write, FileShare.None), EndianFormat.BigEndian)) { output.WriteBlock(blamSound.Data); } } } else { permutationData = File.ReadAllBytes(cacheFileName); } permutation.PermutationChunks.Add(new PermutationChunk(currentSoundDataOffset, permutationData.Length)); currentSoundDataOffset += permutationData.Length; pitchRange.Permutations.Add(permutation); soundDataAggregate = soundDataAggregate.Concat(permutationData); } } sound.Promotion.LongestPermutationDuration = (uint)sound.SoundReference.MaximumPlayTime; sound.Promotion.TotalSampleSize = totalSampleCount; // // Convert Blam extra info to ElDorado format // var extraInfo = new ExtraInfo() { LanguagePermutations = new List <ExtraInfo.LanguagePermutation>(), EncodedPermutationSections = new List <ExtraInfo.EncodedPermutationSection>() }; for (int u = 0; u < sound.SoundReference.PitchRangeCount; u++) { var pitchRange = BlamSoundGestalt.PitchRanges[sound.SoundReference.PitchRangeIndex + u]; for (int i = 0; i < pitchRange.PermutationCount; i++) { var permutation = BlamSoundGestalt.GetPermutation(pitchRange.FirstPermutationIndex + i); var permutationChunk = BlamSoundGestalt.GetPermutationChunk(permutation.FirstPermutationChunkIndex); extraInfo.LanguagePermutations.Add(new ExtraInfo.LanguagePermutation { RawInfo = new List <ExtraInfo.LanguagePermutation.RawInfoBlock> { new ExtraInfo.LanguagePermutation.RawInfoBlock { SkipFractionName = StringId.Invalid, UnknownList = new List <ExtraInfo.LanguagePermutation.RawInfoBlock.Unknown> { new ExtraInfo.LanguagePermutation.RawInfoBlock.Unknown { Unknown1 = permutationChunk.UnknownA, Unknown2 = permutationChunk.UnknownSize, Unknown3 = 0, Unknown4 = permutation.SampleSize, Unknown5 = 0, Unknown6 = permutationChunk.EncodedSize & 0xFFFF } }, Compression = 8, ResourceSampleSize = pitchRange.Permutations[i].SampleSize, ResourceSampleOffset = (uint)pitchRange.Permutations[i].PermutationChunks[0].Offset, SampleCount = (uint)pitchRange.Permutations[i].PermutationChunks[0].EncodedSize & 0x3FFFFFF, //SampleCount = (uint)Math.Floor(pitchRange.Permutations[i].SampleSize * 128000.0 / (8 * sound.SampleRate.GetSampleRateHz())), Unknown24 = 480 } } }); } } if (sound.SoundReference.ExtraInfoIndex != -1) { foreach (var section in BlamSoundGestalt.ExtraInfo[sound.SoundReference.ExtraInfoIndex].EncodedPermutationSections) { var newSection = section.DeepClone(); foreach (var info in newSection.SoundDialogueInfo) { for (var i = ((info.MouthDataLength % 2) == 0 ? 0 : 1); (i + 1) < info.MouthDataLength; i += 2) { Array.Reverse(newSection.EncodedData, (int)(info.MouthDataOffset + i), 2); } for (var i = ((info.LipsyncDataLength % 2) == 0 ? 0 : 1); (i + 1) < info.LipsyncDataLength; i += 2) { Array.Reverse(newSection.EncodedData, (int)(info.LipsyncDataOffset + i), 2); } } extraInfo.EncodedPermutationSections.Add(newSection); } } sound.ExtraInfo = new List <ExtraInfo> { extraInfo }; // // Convert Blam languages to ElDorado format // if (sound.SoundReference.LanguageIndex != -1) { sound.Languages = new List <LanguageBlock>(); foreach (var language in BlamSoundGestalt.Languages) { sound.Languages.Add(new LanguageBlock { Language = language.Language, PermutationDurations = new List <LanguageBlock.PermutationDurationBlock>(), PitchRangeDurations = new List <LanguageBlock.PitchRangeDurationBlock>(), }); //Add all the Pitch Range Duration block (pitch range count dependent) var curLanguage = sound.Languages.Last(); for (int i = 0; i < sound.SoundReference.PitchRangeCount; i++) { curLanguage.PitchRangeDurations.Add(language.PitchRangeDurations[sound.SoundReference.LanguageIndex + i]); } //Add all the Permutation Duration Block and adjust offsets for (int i = 0; i < curLanguage.PitchRangeDurations.Count; i++) { var curPRD = curLanguage.PitchRangeDurations[i]; //Get current block count for new index short newPermutationIndex = (short)curLanguage.PermutationDurations.Count(); for (int j = curPRD.PermutationStartIndex; j < curPRD.PermutationStartIndex + curPRD.PermutationCount; j++) { curLanguage.PermutationDurations.Add(language.PermutationDurations[j]); } //apply new index curPRD.PermutationStartIndex = newPermutationIndex; } } } // // Prepare resource // sound.Unused = new byte[] { 0, 0, 0, 0 }; sound.Unknown12 = 0; sound.Resource = new PageableResource { Page = new RawPage { Index = -1, }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Sound, DefinitionData = new byte[20], DefinitionAddress = new CacheResourceAddress(CacheResourceAddressType.Definition, 536870912), ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; var data = soundDataAggregate.ToArray(); var resourceContext = new ResourceSerializationContext(CacheContext, sound.Resource); CacheContext.Serializer.Serialize(resourceContext, new SoundResourceDefinition { Data = new TagData(data.Length, new CacheResourceAddress(CacheResourceAddressType.Resource, 0)) }); var definitionFixup = new TagResourceGen3.ResourceFixup() { BlockOffset = 12, Address = new CacheResourceAddress(CacheResourceAddressType.Resource, 1073741824) }; sound.Resource.Resource.ResourceFixups.Add(definitionFixup); sound.Resource.ChangeLocation(ResourceLocation.Audio); var resource = sound.Resource; if (resource == null) { throw new ArgumentNullException("resource"); } var cache = CacheContext.GetResourceCache(ResourceLocation.Audio); if (!resourceStreams.ContainsKey(ResourceLocation.Audio)) { resourceStreams[ResourceLocation.Audio] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.Audio); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.Audio)) resourceStream.CopyTo(resourceStreams[ResourceLocation.Audio]); } } resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.Audio], data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)data.Length; resource.DisableChecksum(); for (int i = 0; i < 4; i++) { sound.Resource.Resource.DefinitionData[i] = (byte)(sound.Resource.Page.UncompressedBlockSize >> (i * 8)); } return(sound); }
private PageableResource ConvertStructureBspCacheFileTagResources(ScenarioStructureBsp bsp, Dictionary <ResourceLocation, Stream> resourceStreams) { // // Set up ElDorado resource reference // bsp.PathfindingResource = new PageableResource { Page = new RawPage { Index = -1 }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Pathfinding, DefinitionData = new byte[0x30], DefinitionAddress = new CacheResourceAddress(CacheResourceAddressType.Definition, 0), ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; // // Load Blam resource data // var resourceData = BlamCache.Version > CacheVersion.Halo3Retail ? BlamCache.GetRawFromID(bsp.ZoneAssetIndex4) : null; if (resourceData == null) { if (BlamCache.Version >= CacheVersion.Halo3ODST) { return(bsp.PathfindingResource); } resourceData = new byte[0x30]; } // // Port Blam resource definition // StructureBspCacheFileTagResources resourceDefinition = null; if (BlamCache.Version >= CacheVersion.Halo3ODST) { var resourceEntry = BlamCache.ResourceGestalt.TagResources[bsp.ZoneAssetIndex4.Index]; bsp.PathfindingResource.Resource.DefinitionAddress = resourceEntry.DefinitionAddress; bsp.PathfindingResource.Resource.DefinitionData = BlamCache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray(); using (var definitionStream = new MemoryStream(bsp.PathfindingResource.Resource.DefinitionData, true)) using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian)) using (var definitionWriter = new EndianWriter(definitionStream, EndianFormat.BigEndian)) { foreach (var fixup in resourceEntry.ResourceFixups) { var newFixup = new TagResourceGen3.ResourceFixup { BlockOffset = (uint)fixup.BlockOffset, Address = new CacheResourceAddress( fixup.Type == 4 ? CacheResourceAddressType.Resource : CacheResourceAddressType.Definition, fixup.Offset) }; definitionStream.Position = newFixup.BlockOffset; definitionWriter.Write(newFixup.Address.Value); bsp.PathfindingResource.Resource.ResourceFixups.Add(newFixup); } var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheResourceAddressType.Definition); definitionStream.Position = bsp.PathfindingResource.Resource.DefinitionAddress.Offset; resourceDefinition = BlamCache.Deserializer.Deserialize <StructureBspCacheFileTagResources>(dataContext); } } else { resourceDefinition = new StructureBspCacheFileTagResources() { SurfacePlanes = new TagBlock <ScenarioStructureBsp.SurfacesPlanes>(bsp.SurfacePlanes.Count, new CacheResourceAddress()), Planes = new TagBlock <ScenarioStructureBsp.Plane>(bsp.Planes.Count, new CacheResourceAddress()), EdgeToSeams = new TagBlock <ScenarioStructureBsp.EdgeToSeamMapping>(bsp.EdgeToSeams.Count, new CacheResourceAddress()), PathfindingData = new List <StructureBspCacheFileTagResources.PathfindingDatum>() // TODO: copy from bsp.PathfindingData... }; } // // Port Blam resource to ElDorado resource cache // using (var blamResourceStream = new MemoryStream(resourceData)) using (var resourceReader = new EndianReader(blamResourceStream, EndianFormat.BigEndian)) using (var dataStream = new MemoryStream()) using (var resourceWriter = new EndianWriter(dataStream, EndianFormat.LittleEndian)) { var dataContext = new DataSerializationContext(resourceReader, resourceWriter); // // Surfaces Planes // StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = resourceDefinition.SurfacePlanes.Address.Offset; } resourceDefinition.SurfacePlanes = new TagBlock <ScenarioStructureBsp.SurfacesPlanes>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.SurfacePlanes.Count : resourceDefinition.SurfacePlanes.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < resourceDefinition.SurfacePlanes.Count; i++) { var element = BlamCache.Version < CacheVersion.Halo3ODST ? bsp.SurfacePlanes[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.SurfacesPlanes>(dataContext); if (BlamCache.Version < CacheVersion.Halo3ODST) { element.PlaneIndexNew = element.PlaneIndexOld; element.PlaneCountNew = element.PlaneCountOld; } CacheContext.Serializer.Serialize(dataContext, element); } // // UnknownRaw1sts // StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = resourceDefinition.Planes.Address.Offset; } resourceDefinition.Planes = new TagBlock <ScenarioStructureBsp.Plane>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.Planes.Count : resourceDefinition.Planes.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < resourceDefinition.Planes.Count; i++) { var element = BlamCache.Version < CacheVersion.Halo3ODST ? bsp.Planes[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.Plane>(dataContext); CacheContext.Serializer.Serialize(dataContext, element); } // // UnknownRaw7ths // StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = resourceDefinition.EdgeToSeams.Address.Offset; } resourceDefinition.EdgeToSeams = new TagBlock <ScenarioStructureBsp.EdgeToSeamMapping>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.EdgeToSeams.Count : resourceDefinition.EdgeToSeams.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < resourceDefinition.EdgeToSeams.Count; i++) { var element = BlamCache.Version < CacheVersion.Halo3ODST ? bsp.EdgeToSeams[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.EdgeToSeamMapping>(dataContext); CacheContext.Serializer.Serialize(dataContext, element); } if (BlamCache.Version < CacheVersion.Halo3ODST && bsp.PathfindingData.Count != 0) { var pathfinding = new StructureBspCacheFileTagResources.PathfindingDatum() { StructureChecksum = bsp.PathfindingData[0].StructureChecksum, ObjectReferences = new List <StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference>(), Seams = new List <StructureBspCacheFileTagResources.PathfindingDatum.Seam>(), JumpSeams = new List <StructureBspCacheFileTagResources.PathfindingDatum.JumpSeam>() }; foreach (var oldObjectReference in bsp.PathfindingData[0].ObjectReferences) { var objectReference = new StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference { Flags = oldObjectReference.Flags, Bsps = new List <StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference.BspReference>(), ObjectUniqueID = oldObjectReference.ObjectUniqueID, OriginBspIndex = oldObjectReference.OriginBspIndex, ObjectType = oldObjectReference.ObjectType.DeepClone(), Source = oldObjectReference.Source }; foreach (var bspRef in oldObjectReference.Bsps) { objectReference.Bsps.Add(new StructureBspCacheFileTagResources.PathfindingDatum.ObjectReference.BspReference { BspIndex = bspRef.BspIndex, NodeIndex = bspRef.NodeIndex, Bsp2dRefs = new TagBlock <ScenarioStructureBsp.PathfindingDatum.ObjectReference.BspReference.Bsp2dRef>(bspRef.Bsp2dRefs.Count, new CacheResourceAddress()), VertexOffset = bspRef.VertexOffset }); } pathfinding.ObjectReferences.Add(objectReference); } foreach (var oldSeam in bsp.PathfindingData[0].Seams) { pathfinding.Seams.Add(new StructureBspCacheFileTagResources.PathfindingDatum.Seam { LinkIndices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Seam.LinkIndexBlock>( oldSeam.LinkIndices.Count, new CacheResourceAddress()) }); } foreach (var oldJumpSeam in bsp.PathfindingData[0].JumpSeams) { pathfinding.JumpSeams.Add(new StructureBspCacheFileTagResources.PathfindingDatum.JumpSeam { UserJumpIndex = oldJumpSeam.UserJumpIndex, DestOnly = oldJumpSeam.DestOnly, Length = oldJumpSeam.Length, JumpIndices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.JumpSeam.JumpIndexBlock>( oldJumpSeam.JumpIndices.Count, new CacheResourceAddress()) }); } resourceDefinition.PathfindingData.Add(pathfinding); } foreach (var pathfindingDatum in resourceDefinition.PathfindingData) { StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.Sectors.Address.Offset; } pathfindingDatum.Sectors = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Sector>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Sectors.Count : pathfindingDatum.Sectors.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.Sectors.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Sectors[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Sector>(dataContext)); } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.Links.Address.Offset; } pathfindingDatum.Links = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Link>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Links.Count : pathfindingDatum.Links.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.Links.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Links[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Link>(dataContext)); } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.References.Address.Offset; } pathfindingDatum.References = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Reference>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].References.Count : pathfindingDatum.References.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.References.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].References[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Reference>(dataContext)); } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.Bsp2dNodes.Address.Offset; } pathfindingDatum.Bsp2dNodes = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Bsp2dNode>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Bsp2dNodes.Count : pathfindingDatum.Bsp2dNodes.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.Bsp2dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Bsp2dNodes[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Bsp2dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.Vertices.Address.Offset; } pathfindingDatum.Vertices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Vertex>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Vertices.Count : pathfindingDatum.Vertices.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.Vertices.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Vertices[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Vertex>(dataContext)); } for (var objRefIdx = 0; objRefIdx < pathfindingDatum.ObjectReferences.Count; objRefIdx++) { for (var bspRefIdx = 0; bspRefIdx < pathfindingDatum.ObjectReferences[objRefIdx].Bsps.Count; bspRefIdx++) { var bspRef = pathfindingDatum.ObjectReferences[objRefIdx].Bsps[bspRefIdx]; StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = bspRef.Bsp2dRefs.Address.Offset; } bspRef.Bsp2dRefs.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var bsp2dRefIdx = 0; bsp2dRefIdx < bspRef.Bsp2dRefs.Count; bsp2dRefIdx++) { var bsp2dRef = BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].ObjectReferences[objRefIdx].Bsps[bspRefIdx].Bsp2dRefs[bsp2dRefIdx] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.ObjectReference.BspReference.Bsp2dRef>(dataContext); CacheContext.Serializer.Serialize(dataContext, bsp2dRef); } } } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.PathfindingHints.Address.Offset; } pathfindingDatum.PathfindingHints = new TagBlock <ScenarioStructureBsp.PathfindingDatum.PathfindingHint>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].PathfindingHints.Count : pathfindingDatum.PathfindingHints.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.PathfindingHints.Count; i++) { var hint = BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].PathfindingHints[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.PathfindingHint>(dataContext); if (BlamCache.Version < CacheVersion.Halo3ODST && (hint.HintType == JumpLink || hint.HintType == WallJumpLink)) { hint.Data[3] = (hint.Data[3] & ~ushort.MaxValue) | ((hint.Data[2] >> 16) & ushort.MaxValue); hint.Data[2] = (hint.Data[2] & ~(ushort.MaxValue << 16)); //remove old landing sector hint.Data[2] = (hint.Data[2] | ((hint.Data[2] & (byte.MaxValue << 8)) << 8)); //move jump height flags hint.Data[2] = (hint.Data[2] & ~(byte.MaxValue << 8)); //remove old flags } CacheContext.Serializer.Serialize(dataContext, hint); } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.InstancedGeometryReferences.Address.Offset; } pathfindingDatum.InstancedGeometryReferences = new TagBlock <ScenarioStructureBsp.PathfindingDatum.InstancedGeometryReference>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].InstancedGeometryReferences.Count : pathfindingDatum.InstancedGeometryReferences.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.InstancedGeometryReferences.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].InstancedGeometryReferences[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.InstancedGeometryReference>(dataContext)); } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.GiantPathfinding.Address.Offset; } pathfindingDatum.GiantPathfinding = new TagBlock <ScenarioStructureBsp.PathfindingDatum.GiantPathfindingBlock>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].GiantPathfinding.Count : pathfindingDatum.GiantPathfinding.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.GiantPathfinding.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].GiantPathfinding[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.GiantPathfindingBlock>(dataContext)); } for (var unk2Idx = 0; unk2Idx < pathfindingDatum.Seams.Count; unk2Idx++) { var unknown2 = pathfindingDatum.Seams[unk2Idx]; StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = unknown2.LinkIndices.Address.Offset; } unknown2.LinkIndices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Seam.LinkIndexBlock>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Seams[unk2Idx].LinkIndices.Count : unknown2.LinkIndices.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var unkIdx = 0; unkIdx < unknown2.LinkIndices.Count; unkIdx++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Seams[unk2Idx].LinkIndices[unkIdx] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Seam.LinkIndexBlock>(dataContext)); } } for (var unk3Idx = 0; unk3Idx < pathfindingDatum.JumpSeams.Count; unk3Idx++) { var unknown3 = pathfindingDatum.JumpSeams[unk3Idx]; StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = unknown3.JumpIndices.Address.Offset; } unknown3.JumpIndices = new TagBlock <ScenarioStructureBsp.PathfindingDatum.JumpSeam.JumpIndexBlock>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].JumpSeams[unk3Idx].JumpIndices.Count : unknown3.JumpIndices.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var unk4Idx = 0; unk4Idx < unknown3.JumpIndices.Count; unk4Idx++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].JumpSeams[unk3Idx].JumpIndices[unk4Idx] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.JumpSeam.JumpIndexBlock>(dataContext)); } } StreamUtil.Align(dataStream, 0x4); if (BlamCache.Version >= CacheVersion.Halo3ODST) { blamResourceStream.Position = pathfindingDatum.Doors.Address.Offset; } pathfindingDatum.Doors = new TagBlock <ScenarioStructureBsp.PathfindingDatum.Door>( (BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Doors.Count : pathfindingDatum.Doors.Count), new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position)); for (var i = 0; i < pathfindingDatum.Doors.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Version < CacheVersion.Halo3ODST ? bsp.PathfindingData[0].Doors[i] : BlamCache.Deserializer.Deserialize <ScenarioStructureBsp.PathfindingDatum.Door>(dataContext)); } } CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, bsp.PathfindingResource), resourceDefinition); resourceWriter.BaseStream.Position = 0; dataStream.Position = 0; bsp.PathfindingResource.ChangeLocation(ResourceLocation.ResourcesB); var resource = bsp.PathfindingResource; if (resource == null) { throw new ArgumentNullException("resource"); } if (!dataStream.CanRead) { throw new ArgumentException("The input stream is not open for reading", "dataStream"); } var cache = CacheContext.GetResourceCache(ResourceLocation.ResourcesB); if (!resourceStreams.ContainsKey(ResourceLocation.ResourcesB)) { resourceStreams[ResourceLocation.ResourcesB] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.ResourcesB); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.ResourcesB)) resourceStream.CopyTo(resourceStreams[ResourceLocation.ResourcesB]); } } var dataSize = (int)(dataStream.Length - dataStream.Position); var data = new byte[dataSize]; dataStream.Read(data, 0, dataSize); resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.ResourcesB], data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)dataSize; resource.DisableChecksum(); } if (BlamCache.Version < CacheVersion.Halo3ODST) { bsp.SurfacePlanes.Clear(); bsp.Planes.Clear(); bsp.EdgeToSeams.Clear(); bsp.PathfindingData.Clear(); } return(bsp.PathfindingResource); }
private void MergeCharacter(Stream cacheStream, Stream blamCacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, CachedTag edTag, CachedTag h3Tag) { var edDef = CacheContext.Deserialize <Character>(cacheStream, edTag); var h3Def = BlamCache.Deserialize <Character>(blamCacheStream, h3Tag); var merged = false; if (edDef.WeaponsProperties.Count == h3Def.WeaponsProperties.Count) { for (var i = 0; i < edDef.WeaponsProperties.Count; i++) { if (edDef.WeaponsProperties[i].Weapon != null || h3Def.WeaponsProperties[i].Weapon == null) { continue; } edDef.WeaponsProperties[i].Weapon = ConvertTag(cacheStream, blamCacheStream, resourceStreams, h3Def.WeaponsProperties[i].Weapon); merged = true; } } if (edDef.VehicleProperties.Count == h3Def.VehicleProperties.Count) { for (var i = 0; i < edDef.VehicleProperties.Count; i++) { if (edDef.VehicleProperties[i].Unit != null || h3Def.VehicleProperties[i].Unit == null) { continue; } edDef.VehicleProperties[i].Unit = ConvertTag(cacheStream, blamCacheStream, resourceStreams, h3Def.VehicleProperties[i].Unit); merged = true; } } if (edDef.EquipmentProperties.Count == h3Def.EquipmentProperties.Count) { for (var i = 0; i < edDef.EquipmentProperties.Count; i++) { if (edDef.EquipmentProperties[i].Equipment != null || h3Def.EquipmentProperties[i].Equipment == null) { continue; } edDef.EquipmentProperties[i].Equipment = ConvertTag(cacheStream, blamCacheStream, resourceStreams, h3Def.EquipmentProperties[i].Equipment); merged = true; } } if (edDef.FiringPatternProperties.Count == h3Def.FiringPatternProperties.Count) { for (var i = 0; i < edDef.FiringPatternProperties.Count; i++) { if (edDef.FiringPatternProperties[i].Weapon != null || h3Def.FiringPatternProperties[i].Weapon == null) { continue; } edDef.FiringPatternProperties[i].Weapon = ConvertTag(cacheStream, blamCacheStream, resourceStreams, h3Def.FiringPatternProperties[i].Weapon); merged = true; } } if (edDef.ActAttachments.Count == h3Def.ActAttachments.Count) { for (var i = 0; i < edDef.ActAttachments.Count; i++) { if (edDef.ActAttachments[i].Crate != null || h3Def.ActAttachments[i].Crate == null) { continue; } edDef.ActAttachments[i].Crate = ConvertTag(cacheStream, blamCacheStream, resourceStreams, h3Def.ActAttachments[i].Crate); merged = true; } } if (merged) { CacheContext.Serialize(cacheStream, edTag, edDef); } }
public List <ModelAnimationGraph.ResourceGroup> ConvertModelAnimationGraphResourceGroups(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, List <ModelAnimationGraph.ResourceGroup> resourceGroups) { if (BlamCache.ResourceGestalt == null) { BlamCache.LoadResourceTags(); } var resourceDefinition = new List <ModelAnimationTagResource>(); foreach (var group in resourceGroups) { var resourceEntry = BlamCache.ResourceGestalt.TagResources[group.ZoneAssetHandle.Index]; group.Resource = new PageableResource { Page = new RawPage { Index = -1, }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Animation, DefinitionData = BlamCache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray(), DefinitionAddress = resourceEntry.DefinitionAddress, ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; // Convert blam fixups // get the list of members in this resourcegroup. this list contains address, various offsets, and other info about the member. if (group.Resource.Resource.DefinitionData.Length != 0) { using (var definitionStream = new MemoryStream(group.Resource.Resource.DefinitionData, true)) using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian)) using (var definitionWriter = new EndianWriter(definitionStream, EndianFormat.BigEndian)) { foreach (var fixup in resourceEntry.ResourceFixups) { var newFixup = new TagResourceGen3.ResourceFixup { BlockOffset = (uint)fixup.BlockOffset, Address = new CacheResourceAddress(CacheResourceAddressType.Resource, fixup.Offset) }; definitionStream.Position = newFixup.BlockOffset; definitionWriter.Write(newFixup.Address.Value); group.Resource.Resource.ResourceFixups.Add(newFixup); } var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheResourceAddressType.Definition); definitionStream.Position = group.Resource.Resource.DefinitionAddress.Offset + 0x4; definitionWriter.Write(0x20000000); // ODST's resource type is 4 when it's supposed to be 2 because the resource definition is in the tag and not as a raw resource definitionStream.Position = group.Resource.Resource.DefinitionAddress.Offset; resourceDefinition.Add(BlamCache.Deserializer.Deserialize <ModelAnimationTagResource>(dataContext)); } } } var diffLines = new List <string>(); var resDefIndex = -1; foreach (var group in resourceGroups) { resDefIndex++; if (resourceDefinition.Count < resDefIndex + 1) { continue; // rare cases, might break the game } // Get the resource group real size, which is probably not in the resource definition var groupSize = 0; foreach (var groupMember in resourceDefinition[resDefIndex].GroupMembers) { groupSize += groupMember.AnimationData.Size; while (groupSize % 0x10 != 0) // align to 0x10. { groupSize += 4; } } var resourceData = BlamCache.GetRawFromID(group.ZoneAssetHandle, groupSize); if (resourceData == null) { return(null); } using (var blamResourceStream = new MemoryStream(resourceData)) using (var resourceReader = new EndianReader(blamResourceStream, EndianFormat.BigEndian)) using (var dataStream = new MemoryStream(new byte[groupSize])) using (var resourceWriter = new EndianWriter(dataStream, EndianFormat.LittleEndian)) { var dataContext = new DataSerializationContext(resourceReader, resourceWriter); var memberOffset = 0; for (var memberIndex = 0; memberIndex < resourceDefinition[resDefIndex].GroupMembers.Count; memberIndex++) { var member = resourceDefinition[resDefIndex].GroupMembers[memberIndex]; ModelAnimationTagResource.GroupMember.Codec codec; ModelAnimationTagResource.GroupMember.FrameInfo frameInfo; if (member.BaseHeader != ModelAnimationTagResource.GroupMemberHeaderType.Overlay) { blamResourceStream.Position = member.AnimationData.Address.Offset; dataStream.Position = member.AnimationData.Address.Offset; codec = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Codec>(dataContext); CacheContext.Serializer.Serialize(dataContext, codec); var Format1 = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Format1>(dataContext); CacheContext.Serializer.Serialize(dataContext, Format1); // blamResourceStream.Position = (long)member.AnimationData.Address.Offset + headerSize; // edResourceStream.Position = blamResourceStream.Position; for (int i = 0; i < codec.RotationNodeCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrame>(dataContext)); } blamResourceStream.Position = member.AnimationData.Address.Offset + Format1.DataStart; dataStream.Position = blamResourceStream.Position; for (int i = 0; i < codec.PositionNodeCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } blamResourceStream.Position = member.AnimationData.Address.Offset + Format1.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; for (int i = 0; i < codec.ScaleNodeCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } // If the overlay header is alone, member.OverlayOffset = 0 blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset; dataStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset; codec = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Codec>(dataContext); CacheContext.Serializer.Serialize(dataContext, codec); // deserialize second header. or as first header if the type1/format1 header isn't used. switch (codec.AnimationCodec) { case ModelAnimationTagResource.AnimationCompressionFormats.Type3: // should merge with type1 var header = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Format1>(dataContext); CacheContext.Serializer.Serialize(dataContext, header); for (int nodeIndex = 0; nodeIndex < codec.RotationNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + header.DataStart; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.PositionNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + header.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.ScaleNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } break; case ModelAnimationTagResource.AnimationCompressionFormats.Type4: case ModelAnimationTagResource.AnimationCompressionFormats.Type5: case ModelAnimationTagResource.AnimationCompressionFormats.Type6: case ModelAnimationTagResource.AnimationCompressionFormats.Type7: var overlay = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Overlay>(dataContext); CacheContext.Serializer.Serialize(dataContext, overlay); #region Description // Description by DemonicSandwich from http://remnantmods.com/forums/viewtopic.php?f=13&t=1574 (matches my previous observations) // Format 6 uses Keyframes the way there are supposed to be used. As KEY frames, with the majority of the frames being Tweens. // // This format adds two extra blocks of data to it's structure. // One block that determines how many Keyframes each Node will have, and an offset to to where it's Markers start from. // // Advantages: // This format requires far fewer Keyframes to make a complex animation. // You do not need a keyframe for each render frame. // Disadvantages: // It's a bit more complex to work with. // Since it's Keyrame Markers are only 1 byte in size, you're animation cannot be longer than 256 frames, or ~8.5 seconds for non - machine objects. > 12 bits for gen3, max 0xFFF frames // Machines are still limited to 256 frames but the frames can be stretched out. #endregion var RotationFrameCount = new List <uint>(); var PositionFrameCount = new List <uint>(); var ScaleFrameCount = new List <uint>(); for (int i = 0; i < codec.RotationNodeCount; i++) { frameInfo = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfo>(dataContext); CacheContext.Serializer.Serialize(dataContext, frameInfo); var keyframesOffset = frameInfo.FrameCount & 0x00FFF000; // unused in this conversion var keyframes = frameInfo.FrameCount & 0x00000FFF; RotationFrameCount.Add(keyframes); } for (int i = 0; i < codec.PositionNodeCount; i++) { frameInfo = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfo>(dataContext); CacheContext.Serializer.Serialize(dataContext, frameInfo); var keyframesOffset = frameInfo.FrameCount & 0x00FFF000; var keyframes = frameInfo.FrameCount & 0x00000FFF; PositionFrameCount.Add(keyframes); } for (int i = 0; i < codec.ScaleNodeCount; i++) { frameInfo = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfo>(dataContext); CacheContext.Serializer.Serialize(dataContext, frameInfo); var keyframesOffset = frameInfo.FrameCount & 0x00FFF000; var keyframes = frameInfo.FrameCount & 0x00000FFF; ScaleFrameCount.Add(keyframes); } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.RotationKeyframesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in RotationFrameCount) { for (int i = 0; i < framecount; i++) { if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type4) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type5) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type6) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type7) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.PositionKeyframesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in PositionFrameCount) { for (int i = 0; i < framecount; i++) { if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type4) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type5) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type6) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type7) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.ScaleKeyframesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in ScaleFrameCount) { for (int i = 0; i < framecount; i++) { if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type4) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type5) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type6) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Keyframe>(dataContext)); } else if (codec.AnimationCodec == ModelAnimationTagResource.AnimationCompressionFormats.Type7) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.KeyframeType5>(dataContext)); } } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.RotationFramesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in RotationFrameCount) { for (int i = 0; i < framecount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.PositionFramesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in PositionFrameCount) { for (int i = 0; i < framecount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + overlay.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; foreach (var framecount in ScaleFrameCount) { for (int i = 0; i < framecount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } break; case ModelAnimationTagResource.AnimationCompressionFormats.Type8: // Type 8 is basically a type 3 but with rotation frames using 4 floats, or a realQuaternion var Format8 = BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.Format8>(dataContext); CacheContext.Serializer.Serialize(dataContext, Format8); for (int nodeIndex = 0; nodeIndex < codec.RotationNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.RotationFrameFloat>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + Format8.PositionFramesOffset; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.PositionNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.PositionFrame>(dataContext)); } } blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + Format8.ScaleFramesOffset; dataStream.Position = blamResourceStream.Position; for (int nodeIndex = 0; nodeIndex < codec.ScaleNodeCount; nodeIndex++) { for (int frameIndex = 0; frameIndex < member.FrameCount; frameIndex++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } } break; default: throw new DataMisalignedException(); } #region How Footer/Flags works // Better description by DemonicSandwich from http://remnantmods.com/forums/viewtopic.php?f=13&t=1574 : Node List Block: (matches my previous observations) // Just a block of flags. Tick a flag and the respective node will be affected by animation. // The size of this block should always be a multiple of 12. It's size is determined my the meta value Node List Size [byte, offset: 61] // When set to 12, the list can handle objects with a node count up to 32 (0-31). // When set to 24, the object can have 64 nodes and so on. // The block is split into 3 groups of flags. // The first group determines what nodes are affected by rotation, the second group for position, and the third group for scale. // // If looking at it in hex, the Node ticks for each group will be in order as follows: // [7][6][5][4][3][2][1][0] - [15][14][13][12][11][10][9][8] - etc. // Each flag corresponding to a Node index. #endregion #region Footer/Flag block // There's one bitfield32 for every 32 nodes that are animated which i'll call a node flags. // There's at least 3 flags if the animation only has an overlay header, which i'll call a flag set. // There's at least 6 flags if the animation has both a base header and an overlay header, so 2 sets. // If the animated nodes count is over 32, then a new flags set is added. // 1 set per header is added, such as 32 nodes = 1 set, 64 = 2 sets, 96 = 3 sets etc , 128-256 maybe max blamResourceStream.Position = (long)member.AnimationData.Address.Offset + member.OverlayOffset + member.FlagsOffset; dataStream.Position = blamResourceStream.Position; var footerSizeBase = (byte)member.BaseHeader / 4; for (int flagsCount = 0; flagsCount < footerSizeBase; flagsCount++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } var footerSizeOverlay = (byte)member.OverlayHeader / 4; for (int flagsCount = 0; flagsCount < footerSizeOverlay; flagsCount++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } #endregion switch (member.MovementDataType) { case ModelAnimationTagResource.GroupMemberMovementDataType.None: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDyaw>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } break; case ModelAnimationTagResource.GroupMemberMovementDataType.dx_dy: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDy>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } break; case ModelAnimationTagResource.GroupMemberMovementDataType.dx_dy_dyaw: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDyaw>(dataContext)); } } break; case ModelAnimationTagResource.GroupMemberMovementDataType.dx_dy_dz_dyaw: if (member.Unknown1 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDzDyaw>(dataContext)); } } if (member.Unknown2 > 0) { for (int i = 0; i < member.FrameCount; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.FrameInfoDxDyDzDyaw>(dataContext)); } } break; default: break; } dataStream.Position = memberOffset + member.AnimationData.Size; // Before the next animation member, there's some padding that is garbage data in H3/ODST, but zeroed in HO. // In order to compare converted to original raw easily, copy the original data. while (blamResourceStream.Position % 0x10 != 0) // align to 0x10, useless padding of garbage data, it's zeroed in 1:1 HO raw, just read as 4 lame bytes { if (blamResourceStream.Position == blamResourceStream.Length) { break; } CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <ModelAnimationTagResource.GroupMember.ScaleFrame>(dataContext)); } // Align the next animation member to 0x10. memberOffset += member.AnimationData.Size; while (memberOffset % 0x10 != 0) { memberOffset += 4; } } dataStream.Position = 0; CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, group.Resource), resourceDefinition[resDefIndex]); group.Resource.ChangeLocation(ResourceLocation.ResourcesB); var resource = group.Resource; if (resource == null) { throw new ArgumentNullException("resource"); } if (!dataStream.CanRead) { throw new ArgumentException("The input stream is not open for reading", "dataStream"); } var cache = CacheContext.GetResourceCache(ResourceLocation.ResourcesB); if (!resourceStreams.ContainsKey(ResourceLocation.ResourcesB)) { resourceStreams[ResourceLocation.ResourcesB] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.ResourcesB); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.ResourcesB)) resourceStream.CopyTo(resourceStreams[ResourceLocation.ResourcesB]); } } var dataSize = (int)(dataStream.Length - dataStream.Position); var data = new byte[dataSize]; dataStream.Read(data, 0, dataSize); resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.ResourcesB], data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)dataSize; resource.DisableChecksum(); } } return(resourceGroups); }
private PageableResource ConvertStructureBspTagResources(ScenarioStructureBsp bsp, Dictionary <ResourceLocation, Stream> resourceStreams) { // // Set up ElDorado resource reference // bsp.CollisionBspResource = new PageableResource { Page = new RawPage { Index = -1 }, Resource = new TagResourceGen3 { ResourceType = TagResourceTypeGen3.Collision, DefinitionData = new byte[0x30], DefinitionAddress = new CacheResourceAddress(CacheResourceAddressType.Definition, 0), ResourceFixups = new List <TagResourceGen3.ResourceFixup>(), ResourceDefinitionFixups = new List <TagResourceGen3.ResourceDefinitionFixup>(), Unknown2 = 1 } }; // // Port Blam resource definition // var resourceEntry = BlamCache.ResourceGestalt.TagResources[bsp.ZoneAssetIndex3.Index]; bsp.CollisionBspResource.Resource.DefinitionAddress = resourceEntry.DefinitionAddress; bsp.CollisionBspResource.Resource.DefinitionData = BlamCache.ResourceGestalt.FixupInformation.Skip(resourceEntry.FixupInformationOffset).Take(resourceEntry.FixupInformationLength).ToArray(); StructureBspTagResources resourceDefinition = null; using (var definitionStream = new MemoryStream(bsp.CollisionBspResource.Resource.DefinitionData, true)) using (var definitionReader = new EndianReader(definitionStream, EndianFormat.BigEndian)) using (var definitionWriter = new EndianWriter(definitionStream, EndianFormat.BigEndian)) { foreach (var fixup in resourceEntry.ResourceFixups) { var newFixup = new TagResourceGen3.ResourceFixup { BlockOffset = (uint)fixup.BlockOffset, Address = new CacheResourceAddress( fixup.Type == 4 ? CacheResourceAddressType.Resource : CacheResourceAddressType.Definition, fixup.Offset) }; definitionStream.Position = newFixup.BlockOffset; definitionWriter.Write(newFixup.Address.Value); bsp.CollisionBspResource.Resource.ResourceFixups.Add(newFixup); } var dataContext = new DataSerializationContext(definitionReader, definitionWriter, CacheResourceAddressType.Definition); definitionStream.Position = bsp.CollisionBspResource.Resource.DefinitionAddress.Offset; resourceDefinition = BlamCache.Deserializer.Deserialize <StructureBspTagResources>(dataContext); // // Apply game-specific fixes to the resource definition // if (BlamCache.Version < CacheVersion.Halo3ODST) { resourceDefinition.LargeCollisionBsps = new List <StructureBspTagResources.LargeCollisionBspBlock>(); resourceDefinition.HavokData = new List <StructureBspTagResources.HavokDatum>(); } foreach (var instance in resourceDefinition.InstancedGeometry) { instance.Unknown5 = new TagBlock <StructureBspTagResources.InstancedGeometryBlock.Unknown4Block>(); instance.Unknown2 = new TagBlock <StructureBspTagResources.InstancedGeometryBlock.Unknown2Block>(); } } // // Load Blam resource data // var resourceData = BlamCache.GetRawFromID(bsp.ZoneAssetIndex3); if (resourceData == null) { CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, bsp.CollisionBspResource), resourceDefinition); return(bsp.CollisionBspResource); } // // Port Blam resource to ElDorado resource cache // using (var blamResourceStream = resourceData != null ? new MemoryStream(resourceData) : new MemoryStream()) using (var resourceReader = new EndianReader(blamResourceStream, EndianFormat.BigEndian)) using (var dataStream = new MemoryStream()) using (var resourceWriter = new EndianWriter(dataStream, EndianFormat.LittleEndian)) { var dataContext = new DataSerializationContext(resourceReader, resourceWriter); foreach (var collisionBsp in resourceDefinition.CollisionBsps) { StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionBsp.Bsp3dNodes.Address.Offset; collisionBsp.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Bsp3dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp3dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionBsp.Planes.Address.Offset; collisionBsp.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Planes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionBsp.Leaves.Address.Offset; collisionBsp.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Leaves.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext)); } StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = collisionBsp.Bsp2dReferences.Address.Offset; collisionBsp.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Bsp2dReferences.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dReference>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionBsp.Bsp2dNodes.Address.Offset; collisionBsp.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Bsp2dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionBsp.Surfaces.Address.Offset; collisionBsp.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Surfaces.Count; i++) { var surface = BlamCache.Deserializer.Deserialize <CollisionGeometry.Surface>(dataContext); CacheContext.Serializer.Serialize(dataContext, surface); } StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = collisionBsp.Edges.Address.Offset; collisionBsp.Edges.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Edges.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Edge>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionBsp.Vertices.Address.Offset; collisionBsp.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionBsp.Vertices.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Vertex>(dataContext)); } } foreach (var largeCollisionBsp in resourceDefinition.LargeCollisionBsps) { StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = largeCollisionBsp.Bsp3dNodes.Address.Offset; largeCollisionBsp.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Bsp3dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Bsp3dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = largeCollisionBsp.Planes.Address.Offset; largeCollisionBsp.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Planes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = largeCollisionBsp.Leaves.Address.Offset; largeCollisionBsp.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Leaves.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = largeCollisionBsp.Bsp2dReferences.Address.Offset; largeCollisionBsp.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Bsp2dReferences.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Bsp2dReference>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = largeCollisionBsp.Bsp2dNodes.Address.Offset; largeCollisionBsp.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Bsp2dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Bsp2dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = largeCollisionBsp.Surfaces.Address.Offset; largeCollisionBsp.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Surfaces.Count; i++) { var surface = BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Surface>(dataContext); // surface.Material = PortGlobalMaterialIndex(CacheContext, BlamCache, surface.Material); CacheContext.Serializer.Serialize(dataContext, surface); } StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = largeCollisionBsp.Edges.Address.Offset; largeCollisionBsp.Edges.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Edges.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Edge>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = largeCollisionBsp.Vertices.Address.Offset; largeCollisionBsp.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < largeCollisionBsp.Vertices.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.LargeCollisionBspBlock.Vertex>(dataContext)); } } foreach (var instance in resourceDefinition.InstancedGeometry) { StreamUtil.Align(dataStream, 0x10); // 0x8 > 0x10 blamResourceStream.Position = instance.CollisionInfo.Bsp3dNodes.Address.Offset; instance.CollisionInfo.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Bsp3dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp3dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = instance.CollisionInfo.Planes.Address.Offset; instance.CollisionInfo.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Planes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = instance.CollisionInfo.Leaves.Address.Offset; instance.CollisionInfo.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Leaves.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext)); } StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = instance.CollisionInfo.Bsp2dReferences.Address.Offset; instance.CollisionInfo.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Bsp2dReferences.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dReference>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = instance.CollisionInfo.Bsp2dNodes.Address.Offset; instance.CollisionInfo.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Bsp2dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = instance.CollisionInfo.Surfaces.Address.Offset; instance.CollisionInfo.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Surfaces.Count; i++) { var surface = BlamCache.Deserializer.Deserialize <CollisionGeometry.Surface>(dataContext); // surface.Material = PortGlobalMaterialIndex(CacheContext, BlamCache, surface.Material); CacheContext.Serializer.Serialize(dataContext, surface); } StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = instance.CollisionInfo.Edges.Address.Offset; instance.CollisionInfo.Edges.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Edges.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Edge>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = instance.CollisionInfo.Vertices.Address.Offset; instance.CollisionInfo.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.CollisionInfo.Vertices.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Vertex>(dataContext)); } foreach (var collisionGeometry in instance.CollisionGeometries) { StreamUtil.Align(dataStream, 0x10); // 0x8 > 0x10 blamResourceStream.Position = collisionGeometry.Bsp3dNodes.Address.Offset; collisionGeometry.Bsp3dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Bsp3dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp3dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionGeometry.Planes.Address.Offset; collisionGeometry.Planes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Planes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Plane>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionGeometry.Leaves.Address.Offset; collisionGeometry.Leaves.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Leaves.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Leaf>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionGeometry.Bsp2dReferences.Address.Offset; collisionGeometry.Bsp2dReferences.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Bsp2dReferences.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dReference>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionGeometry.Bsp2dNodes.Address.Offset; collisionGeometry.Bsp2dNodes.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Bsp2dNodes.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Bsp2dNode>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionGeometry.Surfaces.Address.Offset; collisionGeometry.Surfaces.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Surfaces.Count; i++) { var surface = BlamCache.Deserializer.Deserialize <CollisionGeometry.Surface>(dataContext); // surface.Material = PortGlobalMaterialIndex(CacheContext, BlamCache, surface.Material); CacheContext.Serializer.Serialize(dataContext, surface); } StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = collisionGeometry.Edges.Address.Offset; collisionGeometry.Edges.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Edges.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Edge>(dataContext)); } StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = collisionGeometry.Vertices.Address.Offset; collisionGeometry.Vertices.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < collisionGeometry.Vertices.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <CollisionGeometry.Vertex>(dataContext)); } } foreach (var moppCode in instance.BspPhysics) { StreamUtil.Align(dataStream, 0x10); blamResourceStream.Position = moppCode.Data.Address.Offset; moppCode.Data.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); var moppData = resourceReader.ReadBytes(moppCode.Data.Count).Select(i => new CollisionMoppCode.Datum { Value = i }).ToList(); if (BlamCache.Version < CacheVersion.Halo3ODST) { moppData = ConvertCollisionMoppData(moppData); } resourceWriter.Write(moppData.Select(i => i.Value).ToArray()); } StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = instance.Unknown1.Address.Offset; instance.Unknown1.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.Unknown1.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.InstancedGeometryBlock.Unknown1Block>(dataContext)); } /* * StreamUtil.Align(dataStream, 0x4); // 0x4 > 0x10 * blamResourceStream.Position = instance.Unknown2.Address.Offset; * instance.Unknown2.Address = new CacheAddress(CacheAddressType.Resource, (int)dataStream.Position); * for (var i = 0; i < instance.Unknown2.Count; i++) * { * var element = BlamCache.Deserializer.Deserialize<StructureBspTagResources.InstancedGeometryBlock.Unknown2Block>(dataContext); * if (BlamCache.Version <= CacheVersion.Halo3ODST) * { * element.Unknown1 = element.Unknown1_H3; * element.Unknown2 = element.Unknown2_H3; * } * CacheContext.Serializer.Serialize(dataContext, element); */ StreamUtil.Align(dataStream, 0x10); // 0x4 > 0x10 blamResourceStream.Position = instance.Unknown3.Address.Offset; instance.Unknown3.Address = new CacheResourceAddress(CacheResourceAddressType.Resource, (int)dataStream.Position); for (var i = 0; i < instance.Unknown3.Count; i++) { CacheContext.Serializer.Serialize(dataContext, BlamCache.Deserializer.Deserialize <StructureBspTagResources.InstancedGeometryBlock.Unknown3Block>(dataContext)); } } dataStream.Position = 0; CacheContext.Serializer.Serialize(new ResourceSerializationContext(CacheContext, bsp.CollisionBspResource), resourceDefinition); bsp.CollisionBspResource.ChangeLocation(ResourceLocation.ResourcesB); var resource = bsp.CollisionBspResource; if (resource == null) { throw new ArgumentNullException("resource"); } if (!dataStream.CanRead) { throw new ArgumentException("The input stream is not open for reading", "dataStream"); } var cache = CacheContext.GetResourceCache(ResourceLocation.ResourcesB); if (!resourceStreams.ContainsKey(ResourceLocation.ResourcesB)) { resourceStreams[ResourceLocation.ResourcesB] = FlagIsSet(PortingFlags.Memory) ? new MemoryStream() : (Stream)CacheContext.OpenResourceCacheReadWrite(ResourceLocation.ResourcesB); if (FlagIsSet(PortingFlags.Memory)) { using (var resourceStream = CacheContext.OpenResourceCacheRead(ResourceLocation.ResourcesB)) resourceStream.CopyTo(resourceStreams[ResourceLocation.ResourcesB]); } } var dataSize = (int)(dataStream.Length - dataStream.Position); var data = new byte[dataSize]; dataStream.Read(data, 0, dataSize); resource.Page.Index = cache.Add(resourceStreams[ResourceLocation.ResourcesB], data, out uint compressedSize); resource.Page.CompressedBlockSize = compressedSize; resource.Page.UncompressedBlockSize = (uint)dataSize; resource.DisableChecksum(); } return(bsp.CollisionBspResource); }
private void MergeCharacter(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, CachedTagInstance edTag, CacheFile.IndexItem h3Tag) { var edDef = CacheContext.Deserialize <Character>(cacheStream, edTag); var h3Def = BlamCache.Deserializer.Deserialize <Character>( new CacheSerializationContext(ref BlamCache, h3Tag)); var merged = false; if (edDef.WeaponsProperties.Count == h3Def.WeaponsProperties.Count) { for (var i = 0; i < edDef.WeaponsProperties.Count; i++) { if (edDef.WeaponsProperties[i].Weapon != null || h3Def.WeaponsProperties[i].Weapon == null) { continue; } edDef.WeaponsProperties[i].Weapon = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Def.WeaponsProperties[i].Weapon.Index)); merged = true; } } if (edDef.VehicleProperties.Count == h3Def.VehicleProperties.Count) { for (var i = 0; i < edDef.VehicleProperties.Count; i++) { if (edDef.VehicleProperties[i].Unit != null || h3Def.VehicleProperties[i].Unit == null) { continue; } edDef.VehicleProperties[i].Unit = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Def.VehicleProperties[i].Unit.Index)); merged = true; } } if (edDef.EquipmentProperties.Count == h3Def.EquipmentProperties.Count) { for (var i = 0; i < edDef.EquipmentProperties.Count; i++) { if (edDef.EquipmentProperties[i].Equipment != null || h3Def.EquipmentProperties[i].Equipment == null) { continue; } edDef.EquipmentProperties[i].Equipment = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Def.EquipmentProperties[i].Equipment.Index)); merged = true; } } if (merged) { CacheContext.Serialize(cacheStream, edTag, edDef); } }
private void MergeMultiplayerEvent(Stream cacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, MultiplayerGlobals.RuntimeBlock.EventBlock edEvent, MultiplayerGlobals.RuntimeBlock.EventBlock h3Event) { if (h3Event.EnglishSound != null) { edEvent.EnglishSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.EnglishSound.Index)); } if (h3Event.JapaneseSound != null) { edEvent.JapaneseSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.JapaneseSound.Index)); } if (h3Event.GermanSound != null) { edEvent.GermanSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.GermanSound.Index)); } if (h3Event.FrenchSound != null) { edEvent.FrenchSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.FrenchSound.Index)); } if (h3Event.SpanishSound != null) { edEvent.SpanishSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.SpanishSound.Index)); } if (h3Event.LatinAmericanSpanishSound != null) { edEvent.LatinAmericanSpanishSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.LatinAmericanSpanishSound.Index)); } if (h3Event.ItalianSound != null) { edEvent.ItalianSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.ItalianSound.Index)); } if (h3Event.KoreanSound != null) { edEvent.KoreanSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.KoreanSound.Index)); } if (h3Event.ChineseTraditionalSound != null) { edEvent.ChineseTraditionalSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.ChineseTraditionalSound.Index)); } if (h3Event.ChineseSimplifiedSound != null) { edEvent.ChineseSimplifiedSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.ChineseSimplifiedSound.Index)); } if (h3Event.PortugueseSound != null) { edEvent.PortugueseSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.PortugueseSound.Index)); } if (h3Event.PolishSound != null) { edEvent.PolishSound = ConvertTag(cacheStream, resourceStreams, BlamCache.GetIndexItemFromID(h3Event.PolishSound.Index)); } }
private RenderMethod FixAnimationProperties(Stream cacheStream, Stream blamCacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, GameCache blamCache, GameCacheHaloOnlineBase CacheContext, RenderMethod finalRm, RenderMethodTemplate edRmt2, RenderMethodTemplate bmRmt2, string blamTagName) { // finalRm is a H3 rendermethod with ported bitmaps, if (finalRm.ShaderProperties[0].Functions.Count == 0) { return(finalRm); } foreach (var properties in finalRm.ShaderProperties[0].Functions) { properties.InputName = ConvertStringId(properties.InputName); properties.RangeName = ConvertStringId(properties.RangeName); ConvertTagFunction(properties.Function); } var pixlTag = CacheContext.Deserialize(cacheStream, edRmt2.PixelShader); var edPixl = (PixelShader)pixlTag; var bmPixl = BlamCache.Deserialize <PixelShader>(blamCacheStream, bmRmt2.PixelShader); // Make a collection of drawmodes and their DrawModeItem's // DrawModeItem are has info about all registers modified by functions for each drawmode. var bmPixlParameters = new Dictionary <int, List <ArgumentMapping> >(); // key is shader index // pixl side // For each drawmode, find its shader, and get all that shader's parameter. // Each parameter has a registerIndex, a registerType, and a registerName. // We'll use this to know which function acts on what shader and which registers var RegistersList = new Dictionary <int, string>(); foreach (var a in finalRm.ShaderProperties[0].Parameters) { if (!RegistersList.ContainsKey(a.RegisterIndex)) { RegistersList.Add(a.RegisterIndex, ""); } } var DrawModeIndex = -1; foreach (var a in bmPixl.DrawModes) { DrawModeIndex++; bmPixlParameters.Add(DrawModeIndex, new List <ArgumentMapping>()); if (a.Count == 0) { continue; } foreach (var b in bmPixl.Shaders[a.Offset].XboxParameters) { var ParameterName = BlamCache.StringTable.GetString(b.ParameterName); bmPixlParameters[DrawModeIndex].Add(new ArgumentMapping { ShaderIndex = a.Offset, ParameterName = ParameterName, RegisterIndex = b.RegisterIndex, RegisterType = b.RegisterType }); } } // rm side var bmDrawmodesFunctions = new Dictionary <int, Unknown3Tagblock>(); // key is shader index DrawModeIndex = -1; foreach (var a in finalRm.ShaderProperties[0].EntryPoints) { DrawModeIndex++; // These are not modes. This is an indireciton table of packed 10_6 shorts // from RMT2 ShaderDrawmodes to RegisterOffsets // register_offset = ShaderDrawmodes[current_drawmode].Offset var drawmodeRegisterOffset = (int)a.Offset; var drawmodeRegisterCount = (int)a.Count; var ArgumentMappingsIndexSampler = (byte)finalRm.ShaderProperties[0].ParameterTables[drawmodeRegisterOffset].Texture.Offset; var ArgumentMappingsCountSampler = finalRm.ShaderProperties[0].ParameterTables[drawmodeRegisterOffset].Texture.Count; var ArgumentMappingsIndexUnknown = (byte)finalRm.ShaderProperties[0].ParameterTables[drawmodeRegisterOffset].RealVertex.Offset; var ArgumentMappingsCountUnknown = finalRm.ShaderProperties[0].ParameterTables[drawmodeRegisterOffset].RealVertex.Count; var ArgumentMappingsIndexVector = (byte)finalRm.ShaderProperties[0].ParameterTables[drawmodeRegisterOffset].RealPixel.Offset; var ArgumentMappingsCountVector = finalRm.ShaderProperties[0].ParameterTables[drawmodeRegisterOffset].RealPixel.Count; var ArgumentMappings = new List <ArgumentMapping>(); for (int j = 0; j < ArgumentMappingsCountSampler; j++) { ArgumentMappings.Add(new ArgumentMapping { RegisterIndex = finalRm.ShaderProperties[0].Parameters[ArgumentMappingsIndexSampler + j].RegisterIndex, ArgumentIndex = finalRm.ShaderProperties[0].Parameters[ArgumentMappingsIndexSampler + j].SourceIndex, // i don't think i can use it to match stuf ArgumentMappingsTagblockIndex = ArgumentMappingsIndexSampler + j, RegisterType = TagTool.Shaders.ShaderParameter.RType.Sampler, ShaderIndex = drawmodeRegisterOffset, // WARNING i think drawmodes in rm aren't the same as in pixl, because rm drawmodes can point to a global shader . // say rm.drawmodes[17]'s value is 13, pixl.drawmodes[17] would typically be 12 }); } for (int j = 0; j < ArgumentMappingsCountUnknown; j++) { ArgumentMappings.Add(new ArgumentMapping { RegisterIndex = finalRm.ShaderProperties[0].Parameters[ArgumentMappingsIndexUnknown + j].RegisterIndex, ArgumentIndex = finalRm.ShaderProperties[0].Parameters[ArgumentMappingsIndexUnknown + j].SourceIndex, ArgumentMappingsTagblockIndex = ArgumentMappingsIndexUnknown + j, RegisterType = TagTool.Shaders.ShaderParameter.RType.Vector, ShaderIndex = drawmodeRegisterOffset, // it's something else, uses a global shader or some shit, one water shader pointed to a vtsh in rasg, but not in H3, maybe coincidence // yeah guaranteed rmdf's glvs or rasg shaders }); } for (int j = 0; j < ArgumentMappingsCountVector; j++) { ArgumentMappings.Add(new ArgumentMapping { RegisterIndex = finalRm.ShaderProperties[0].Parameters[ArgumentMappingsIndexVector + j].RegisterIndex, ArgumentIndex = finalRm.ShaderProperties[0].Parameters[ArgumentMappingsIndexVector + j].SourceIndex, ArgumentMappingsTagblockIndex = ArgumentMappingsIndexVector + j, RegisterType = TagTool.Shaders.ShaderParameter.RType.Vector, ShaderIndex = drawmodeRegisterOffset, }); } bmDrawmodesFunctions.Add(DrawModeIndex, new Unknown3Tagblock { Unknown3Index = drawmodeRegisterOffset, // not shader index for rm and rmt2 Unknown3Count = drawmodeRegisterCount, // should always be 4 for enabled drawmodes ArgumentMappingsIndexSampler = ArgumentMappingsIndexSampler, ArgumentMappingsCountSampler = ArgumentMappingsCountSampler, ArgumentMappingsIndexUnknown = ArgumentMappingsIndexUnknown, // no clue what it's used for, global shaders? i know one of the drawmodes will use one or more shaders from glvs, no idea if always or based on something ArgumentMappingsCountUnknown = ArgumentMappingsCountUnknown, ArgumentMappingsIndexVector = ArgumentMappingsIndexVector, ArgumentMappingsCountVector = ArgumentMappingsCountVector, ArgumentMappings = ArgumentMappings }); } DrawModeIndex = -1; foreach (var a in bmDrawmodesFunctions) { DrawModeIndex++; if (a.Value.Unknown3Count == 0) { continue; } foreach (var b in a.Value.ArgumentMappings) { foreach (var c in bmPixlParameters[a.Key]) { if (b.RegisterIndex == c.RegisterIndex && b.RegisterType == c.RegisterType) { b.ParameterName = c.ParameterName; break; } } } } // // Now that we know which register is what for each drawmode, find its halo online equivalent register indexes based on register name. // // This is where it gets tricky because drawmodes count changed in HO. foreach (var a in bmDrawmodesFunctions) { if (a.Value.Unknown3Count == 0) { continue; } foreach (var b in a.Value.ArgumentMappings) { foreach (var c in edPixl.Shaders[edPixl.DrawModes[a.Key].Offset].PCParameters) { var ParameterName = CacheContext.StringTable.GetString(c.ParameterName); if (ParameterName == b.ParameterName && b.RegisterType == c.RegisterType) { if (RegistersList[b.RegisterIndex] == "") { RegistersList[b.RegisterIndex] = $"{c.RegisterIndex}"; } else { RegistersList[b.RegisterIndex] = $"{RegistersList[b.RegisterIndex]},{c.RegisterIndex}"; } b.EDRegisterIndex = c.RegisterIndex; } } } } // DEBUG draw registers // DEBUG check for invalid registers foreach (var a in bmDrawmodesFunctions) { if (a.Value.Unknown3Count == 0) { continue; } foreach (var b in a.Value.ArgumentMappings) { finalRm.ShaderProperties[0].Parameters[b.ArgumentMappingsTagblockIndex].RegisterIndex = (short)b.EDRegisterIndex; } } // one final check // Gather all register indexes from pixl tag. Then check against all the converted register indexes. // It should detect registers that are invalid and would crash, but it does not verify if the register is valid. var validEDRegisters = new List <int>(); foreach (var a in edPixl.Shaders) { foreach (var b in a.PCParameters) { if (!validEDRegisters.Contains(b.RegisterIndex)) { validEDRegisters.Add(b.RegisterIndex); } } } foreach (var a in finalRm.ShaderProperties[0].Parameters) { if (!validEDRegisters.Contains((a.RegisterIndex))) { // Display a warning // Console.WriteLine($"INVALID REGISTERS IN TAG {blamTagName}!"); finalRm.ShaderProperties[0].EntryPoints = new List <RenderMethodTemplate.PackedInteger_10_6>(); finalRm.ShaderProperties[0].ParameterTables = new List <ParameterTable>(); finalRm.ShaderProperties[0].Parameters = new List <ParameterMapping>(); finalRm.ShaderProperties[0].Functions = new List <ShaderFunction>(); foreach (var map in finalRm.ShaderProperties[0].TextureConstants) { map.Functions.Integer = 0; } return(finalRm); } } return(finalRm); }
private RenderMethod ConvertRenderMethod(Stream cacheStream, Stream blamCacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, RenderMethod finalRm, string blamTagName) { // Verify that the ShaderMatcher is ready to use if (!Matcher.IsInitialized()) { Matcher.Init(cacheStream, CacheContext, BlamCache); } // Set flags Matcher.SetMS30Flag(cacheStream, FlagIsSet(PortingFlags.Ms30)); // finalRm.ShaderProperties[0].ShaderMaps are all ported bitmaps // finalRm.BaseRenderMethod is a H3 tag // finalRm.ShaderProperties[0].Template is a H3 tag // TODO hardcode shader values such as argument changes for specific shaders var bmMaps = new List <string>(); var bmRealConstants = new List <string>(); var bmIntConstants = new List <string>(); var bmBoolConstants = new List <string>(); var edMaps = new List <string>(); var edRealConstants = new List <string>(); var edIntConstants = new List <string>(); var edBoolConstants = new List <string>(); // Reset rmt2 preset var pRmt2 = 0; // Make a template of ShaderProperty, with the correct bitmaps and arguments counts. var newShaderProperty = new RenderMethod.ShaderProperty { TextureConstants = new List <RenderMethod.ShaderProperty.TextureConstant>(), RealConstants = new List <RenderMethod.ShaderProperty.RealConstant>(), IntegerConstants = new List <uint>() }; // Get a simple list of bitmaps and arguments names var bmRmt2Instance = finalRm.ShaderProperties[0].Template; var bmRmt2 = BlamCache.Deserialize <RenderMethodTemplate>(blamCacheStream, bmRmt2Instance); // Get a simple list of H3 bitmaps and arguments names foreach (var a in bmRmt2.TextureParameterNames) { bmMaps.Add(BlamCache.StringTable.GetString(a.Name)); } foreach (var a in bmRmt2.RealParameterNames) { bmRealConstants.Add(BlamCache.StringTable.GetString(a.Name)); } foreach (var a in bmRmt2.IntegerParameterNames) { bmIntConstants.Add(BlamCache.StringTable.GetString(a.Name)); } foreach (var a in bmRmt2.BooleanParameterNames) { bmBoolConstants.Add(BlamCache.StringTable.GetString(a.Name)); } // Find a HO equivalent rmt2 var edRmt2Instance = Matcher.FixRmt2Reference(cacheStream, blamTagName, bmRmt2Instance, bmRmt2, bmMaps, bmRealConstants); if (edRmt2Instance == null) { throw new Exception($"Failed to find HO rmt2 for this RenderMethod instance"); } var edRmt2Tagname = edRmt2Instance.Name ?? $"0x{edRmt2Instance.Index:X4}"; // pRmsh pRmt2 now potentially have a new value if (pRmt2 != 0) { if (pRmt2 < CacheContext.TagCache.Count && pRmt2 >= 0) { var a = CacheContext.TagCache.GetTag(pRmt2); if (a != null) { edRmt2Instance = a; } } } var edRmt2 = CacheContext.Deserialize <RenderMethodTemplate>(cacheStream, edRmt2Instance); // fixup for no use_material_texture vector arg in ms23 //for (int index = 0; index < edRmt2.BooleanArguments.Count; index++) //{ // if (CacheContext.StringTable.GetString(edRmt2.BooleanArguments[index].Name) == "use_material_texture") // { // finalRm.ShaderProperties[0].BooleanArguments = (ushort)(index + 1); // break; // } //} foreach (var a in edRmt2.TextureParameterNames) { edMaps.Add(CacheContext.StringTable.GetString(a.Name)); } foreach (var a in edRmt2.RealParameterNames) { edRealConstants.Add(CacheContext.StringTable.GetString(a.Name)); } foreach (var a in edRmt2.IntegerParameterNames) { edIntConstants.Add(CacheContext.StringTable.GetString(a.Name)); } foreach (var a in edRmt2.BooleanParameterNames) { edBoolConstants.Add(CacheContext.StringTable.GetString(a.Name)); } // The bitmaps are default textures. // Arguments are probably default values. I took the values that appeared the most frequently, assuming they are the default value. foreach (var a in edMaps) { var newBitmap = Matcher.GetDefaultBitmapTag(a); if (pRmt2 >= CacheContext.TagCache.Count || pRmt2 < 0) { newBitmap = @"shaders\default_bitmaps\bitmaps\default_detail"; // would only happen for removed shaders } CachedTag bitmap = null; try { bitmap = CacheContext.GetTag <Bitmap>(newBitmap); } catch { bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag($"{newBitmap}.bitm")[0]); } newShaderProperty.TextureConstants.Add( new RenderMethod.ShaderProperty.TextureConstant { Bitmap = bitmap }); } foreach (var a in edRealConstants) { newShaderProperty.RealConstants.Add(Matcher.DefaultArgumentsValues(a)); } foreach (var a in edIntConstants) { newShaderProperty.IntegerConstants.Add(Matcher.DefaultIntegerArgumentsValues(a)); } // Reorder blam bitmaps to match the HO rmt2 order // Reorder blam real constants to match the HO rmt2 order // Reorder blam int constants to match the HO rmt2 order // Reorder blam bool constants to match the HO rmt2 order foreach (var eM in edMaps) { foreach (var bM in bmMaps) { if (eM == bM) { newShaderProperty.TextureConstants[edMaps.IndexOf(eM)] = finalRm.ShaderProperties[0].TextureConstants[bmMaps.IndexOf(bM)]; } } } foreach (var eA in edRealConstants) { foreach (var bA in bmRealConstants) { if (eA == bA) { newShaderProperty.RealConstants[edRealConstants.IndexOf(eA)] = finalRm.ShaderProperties[0].RealConstants[bmRealConstants.IndexOf(bA)]; } } } foreach (var eA in edIntConstants) { foreach (var bA in bmIntConstants) { if (eA == bA) { newShaderProperty.IntegerConstants[edIntConstants.IndexOf(eA)] = finalRm.ShaderProperties[0].IntegerConstants[bmIntConstants.IndexOf(bA)]; } } } foreach (var eA in edBoolConstants) { foreach (var bA in bmBoolConstants) { if (eA == bA) { if ((newShaderProperty.BooleanConstants & (1u << bmBoolConstants.IndexOf(bA))) != 0) { newShaderProperty.BooleanConstants &= ~(1u << bmBoolConstants.IndexOf(bA)); newShaderProperty.BooleanConstants |= (1u << edBoolConstants.IndexOf(eA)); } } } } // Remove some tagblocks // finalRm.Unknown = new List<RenderMethod.UnknownBlock>(); // hopefully not used; this gives rmt2's name. They correspond to the first tagblocks in rmdf, they tell what the shader does finalRm.ImportData = new List <RenderMethod.ImportDatum>(); // most likely not used finalRm.ShaderProperties[0].Template = edRmt2Instance; finalRm.ShaderProperties[0].TextureConstants = newShaderProperty.TextureConstants; finalRm.ShaderProperties[0].RealConstants = newShaderProperty.RealConstants; finalRm.ShaderProperties[0].IntegerConstants = newShaderProperty.IntegerConstants; finalRm.ShaderProperties[0].BooleanConstants = newShaderProperty.BooleanConstants; // fixup runtime queryable properties for (int i = 0; i < finalRm.ShaderProperties[0].QueryableProperties.Length; i++) { if (finalRm.ShaderProperties[0].QueryableProperties[i] == -1) { continue; } switch (i) { case 0: case 1: case 2: case 3: case 5: finalRm.ShaderProperties[0].QueryableProperties[i] = (short)edMaps.IndexOf(bmMaps[finalRm.ShaderProperties[0].QueryableProperties[i]]); break; case 4: finalRm.ShaderProperties[0].QueryableProperties[i] = (short)edRealConstants.IndexOf(bmRealConstants[finalRm.ShaderProperties[0].QueryableProperties[i]]); break; default: finalRm.ShaderProperties[0].QueryableProperties[i] = -1; break; } } // fixup xform arguments; foreach (var tex in finalRm.ShaderProperties[0].TextureConstants) { if (tex.XFormArgumentIndex != -1) { tex.XFormArgumentIndex = (sbyte)edRealConstants.IndexOf(bmRealConstants[tex.XFormArgumentIndex]); } } Matcher.FixRmdfTagRef(finalRm); FixAnimationProperties(cacheStream, blamCacheStream, resourceStreams, BlamCache, CacheContext, finalRm, edRmt2, bmRmt2, blamTagName); // Fix any null bitmaps, caused by bitm port failure foreach (var a in finalRm.ShaderProperties[0].TextureConstants) { if (a.Bitmap != null) { continue; } var defaultBitmap = Matcher.GetDefaultBitmapTag(edMaps[finalRm.ShaderProperties[0].TextureConstants.IndexOf(a)]); try { a.Bitmap = CacheContext.GetTag <Bitmap>(defaultBitmap); } catch { a.Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag($"{defaultBitmap}.bitm")[0]); } } switch (blamTagName) { case @"levels\dlc\chillout\shaders\chillout_flood_godrays" when finalRm is ShaderHalogram: { // Fixup bitmaps for (var i = 0; i < edRmt2.TextureParameterNames.Count; i++) { if (CacheContext.StringTable.GetString(edRmt2.TextureParameterNames[i].Name) == "overlay_map") { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\dlc\chillout\bitmaps\chillout_flood_godrays.bitmap")[0]); break; } } // Fixup arguments for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; switch (CacheContext.StringTable.GetString(templateArg.Name)) { case "overlay_map": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 0f, 0f }; break; case "overlay_tint": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.3764706f, 0.7254902f, 0.9215687f, 1f }; break; case "overlay_intensity": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1.25f, 1.25f, 1.25f, 1.25f }; break; } } break; } case @"levels\dlc\chillout\shaders\chillout_invis_godrays" when finalRm is ShaderHalogram: { // Fixup bitmaps for (var i = 0; i < edRmt2.TextureParameterNames.Count; i++) { if (CacheContext.StringTable.GetString(edRmt2.TextureParameterNames[i].Name) == "overlay_map") { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\dlc\chillout\bitmaps\chillout_invis_godrays.bitmap")[0]); break; } } // Fixup arguments for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; switch (CacheContext.StringTable.GetString(templateArg.Name)) { case "overlay_map": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 0f, 0f }; break; case "overlay_tint": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.3058824f, 0.7098039f, 0.937255f, 1f }; break; case "overlay_intensity": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 1f, 1f }; break; } } break; } case @"levels\solo\020_base\lights\light_volume_hatlight" when finalRm is ShaderHalogram: { // Fixup bitmaps for (var i = 0; i < edRmt2.TextureParameterNames.Count; i++) { if (CacheContext.StringTable.GetString(edRmt2.TextureParameterNames[i].Name) == "overlay_map") { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\solo\020_base\bitmaps\light_volume_hatlight.bitmap")[0]); break; } } // Fixup arguments for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; switch (CacheContext.StringTable.GetString(templateArg.Name)) { case "overlay_map": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 2f, 1f, 0f, 0f }; break; case "overlay_tint": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.9960785f, 1f, 0.8039216f, 1f }; break; case "overlay_intensity": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 1f, 1f }; break; } } break; } case @"objects\vehicles\ghost\shaders\ghost_dash_zcam" when finalRm is ShaderHalogram: case @"objects\weapons\rifle\sniper_rifle\shaders\scope_alpha" when finalRm is ShaderHalogram: finalRm.InputVariable = TagTool.Tags.TagMapping.VariableTypeValue.ParticleRandom1; finalRm.RangeVariable = TagTool.Tags.TagMapping.VariableTypeValue.ParticleAge; break; case @"levels\dlc\armory\shaders\concrete_floor_smooth" when finalRm is Shader: { // Fixup bitmaps for (var i = 0; i < edRmt2.TextureParameterNames.Count; i++) { if (CacheContext.StringTable.GetString(edRmt2.TextureParameterNames[i].Name) == "bump_map") { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\dlc\armory\bitmaps\concrete_floor_bump.bitmap")[0]); break; } } break; } case @"levels\dlc\sidewinder\shaders\side_tree_branch_snow" when finalRm is Shader: for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; if (CacheContext.StringTable.GetString(templateArg.Name) == "env_tint_color") { finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0f, 0f, 0f, 0f }; break; } } break; case @"levels\multi\isolation\sky\shaders\skydome" when finalRm is Shader: for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; if (CacheContext.StringTable.GetString(templateArg.Name) == "albedo_color") { finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.447059f, 0.376471f, 0.898039f, 1.0f }; break; } } break; case @"levels\multi\snowbound\shaders\cov_grey_icy" when finalRm is Shader: { // Fixup bitmaps for (var i = 0; i < edRmt2.TextureParameterNames.Count; i++) { switch (CacheContext.StringTable.GetString(edRmt2.TextureParameterNames[i].Name)) { case "base_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\for_metal_greytech_dif.bitmap")[0]); } catch { } break; case "detail_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\for_metal_greytech_icy.bitmap")[0]); } catch { } break; case "bump_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\for_metal_greytech_platebump.bitmap")[0]); } catch { } break; case "bump_detail_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\for_metal_greytech_bump.bitmap")[0]); } catch { } break; } } // Fixup arguments for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; switch (CacheContext.StringTable.GetString(templateArg.Name)) { case "base_map": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 0f, 0f }; break; case "detail_map": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1.5f, 1.5f, 0f, 0f }; break; case "albedo_color": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.554902f, 0.5588236f, 0.5921569f, 1f }; break; case "bump_map": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 0f, 0f }; break; case "bump_detail_map": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 2f, 2f, 0f, 0f }; break; case "diffuse_coefficient": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.4f, 0.4f, 0.4f, 0.4f }; break; case "specular_coefficient": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 1f, 1f }; break; case "roughness": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.2f, 0.2f, 0.2f, 0.2f }; break; case "analytical_specular_contribution": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.2f, 0.2f, 0.2f, 0.2f }; break; case "area_specular_contribution": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.1f, 0.1f, 0.1f, 0.1f }; break; case "environment_map_specular_contribution": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.15f, 0.15f, 0.15f, 0.15f }; break; case "specular_tint": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0.8431373f, 0.8470589f, 0.8117648f, 1f }; break; } } break; } case @"levels\multi\snowbound\shaders\rock_cliffs" when finalRm is Shader: case @"levels\multi\snowbound\shaders\rock_rocky" when finalRm is Shader: case @"levels\multi\snowbound\shaders\rock_rocky_icy" when finalRm is Shader: { // Fixup bitmaps for (var i = 0; i < edRmt2.TextureParameterNames.Count; i++) { switch (CacheContext.StringTable.GetString(edRmt2.TextureParameterNames[i].Name)) { case "base_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\rock_horiz.bitmap")[0]); } catch { } break; case "detail_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\rock_granite_detail.bitmap")[0]); } catch { } break; case "detail_map2": try { switch (blamTagName) { case @"levels\multi\snowbound\shaders\rock_rocky_icy": finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\rock_icy_blend.bitmap")[0]); break; default: finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\rock_cliff_dif.bitmap")[0]); break; } } catch { } break; case "bump_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\rock_horiz_bump.bitmap")[0]); } catch { } break; case "bump_detail_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\rock_granite_bump.bitmap")[0]); } catch { } break; case "height_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"levels\multi\snowbound\bitmaps\rock_horiz_parallax.bitmap")[0]); } catch { } break; case "environment_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = ConvertTag(cacheStream, blamCacheStream, resourceStreams, ParseLegacyTag(@"shaders\default_bitmaps\bitmaps\color_white.bitmap")[0]); } catch { } break; } } // Fixup arguments for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; switch (CacheContext.StringTable.GetString(templateArg.Name)) { case "base_map": switch (blamTagName) { case @"levels\multi\snowbound\shaders\rock_cliffs": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 16f, 2f, 0f, 0.5f }; break; } break; case "detail_map": switch (blamTagName) { case @"levels\multi\snowbound\shaders\rock_cliffs": case @"levels\multi\snowbound\shaders\rock_rocky": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 320f, 20f, 0f, 0f }; break; } break; case "detail_map2": switch (blamTagName) { case @"levels\multi\snowbound\shaders\rock_cliffs": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1f, 1f, 0f, 0f }; break; } break; case "bump_detail_map": switch (blamTagName) { case @"levels\multi\snowbound\shaders\rock_cliffs": case @"levels\multi\snowbound\shaders\rock_rocky": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 320f, 20f, 0f, 0f }; break; } break; case "diffuse_coefficient": switch (blamTagName) { case @"levels\multi\snowbound\shaders\rock_rocky_icy": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 1.2f, 1.2f, 1.2f, 1.2f }; break; } break; } } break; } case @"levels\multi\snowbound\shaders\cov_metalplates_icy" when finalRm is Shader: // Fixup bitmaps for (var i = 0; i < edRmt2.TextureParameterNames.Count; i++) { switch (CacheContext.StringTable.GetString(edRmt2.TextureParameterNames[i].Name)) { case "detail_map": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = CacheContext.GetTag <Bitmap>(@"levels\multi\snowbound\bitmaps\for_metal_greytech_icy4"); } catch { } break; case "detail_map2": try { finalRm.ShaderProperties[0].TextureConstants[i].Bitmap = CacheContext.GetTag <Bitmap>(@"levels\multi\snowbound\bitmaps\for_metal_greytech_icy3"); } catch { } break; } } break; case @"levels\multi\snowbound\shaders\invis_col_glass" when finalRm is Shader: // Fixup arguments for (var i = 0; i < edRmt2.RealParameterNames.Count; i++) { var templateArg = edRmt2.RealParameterNames[i]; switch (CacheContext.StringTable.GetString(templateArg.Name)) { case "albedo_color": finalRm.ShaderProperties[0].RealConstants[i].Values = new float[] { 0f, 0f, 0f, 0f }; break; } } break; } return(finalRm); }
private void MergeMultiplayerGlobals(Stream cacheStream, Stream blamCacheStream, Dictionary <ResourceLocation, Stream> resourceStreams, CachedTag edTag, CachedTag h3Tag) { var edDef = CacheContext.Deserialize <MultiplayerGlobals>(cacheStream, edTag); var h3Def = BlamCache.Deserialize <MultiplayerGlobals>(blamCacheStream, h3Tag); if (h3Def.Runtime == null || h3Def.Runtime.Count == 0) { return; } for (var i = 0; i < h3Def.Runtime[0].GeneralEvents.Count; i++) { var h3Event = h3Def.Runtime[0].GeneralEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].GeneralEvents.Count; j++) { var edEvent = edDef.Runtime[0].GeneralEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].FlavorEvents.Count; i++) { var h3Event = h3Def.Runtime[0].FlavorEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].FlavorEvents.Count; j++) { var edEvent = edDef.Runtime[0].FlavorEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].SlayerEvents.Count; i++) { var h3Event = h3Def.Runtime[0].SlayerEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].SlayerEvents.Count; j++) { var edEvent = edDef.Runtime[0].SlayerEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].CtfEvents.Count; i++) { var h3Event = h3Def.Runtime[0].CtfEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].CtfEvents.Count; j++) { var edEvent = edDef.Runtime[0].CtfEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].OddballEvents.Count; i++) { var h3Event = h3Def.Runtime[0].OddballEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].OddballEvents.Count; j++) { var edEvent = edDef.Runtime[0].OddballEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].KingOfTheHillEvents.Count; i++) { var h3Event = h3Def.Runtime[0].KingOfTheHillEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].KingOfTheHillEvents.Count; j++) { var edEvent = edDef.Runtime[0].KingOfTheHillEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].VipEvents.Count; i++) { var h3Event = h3Def.Runtime[0].VipEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].VipEvents.Count; j++) { var edEvent = edDef.Runtime[0].VipEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].JuggernautEvents.Count; i++) { var h3Event = h3Def.Runtime[0].JuggernautEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].JuggernautEvents.Count; j++) { var edEvent = edDef.Runtime[0].JuggernautEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].TerritoriesEvents.Count; i++) { var h3Event = h3Def.Runtime[0].TerritoriesEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].TerritoriesEvents.Count; j++) { var edEvent = edDef.Runtime[0].TerritoriesEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].AssaultEvents.Count; i++) { var h3Event = h3Def.Runtime[0].AssaultEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].AssaultEvents.Count; j++) { var edEvent = edDef.Runtime[0].AssaultEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } for (var i = 0; i < h3Def.Runtime[0].InfectionEvents.Count; i++) { var h3Event = h3Def.Runtime[0].InfectionEvents[i]; if (h3Event.DisplayString == StringId.Invalid) { continue; } var h3String = BlamCache.StringTable.GetString(h3Event.DisplayString); for (var j = 0; j < edDef.Runtime[0].InfectionEvents.Count; j++) { var edEvent = edDef.Runtime[0].InfectionEvents[j]; var edString = CacheContext.StringTable.GetString(edEvent.DisplayString); if (edString == h3String) { MergeMultiplayerEvent(cacheStream, blamCacheStream, resourceStreams, edEvent, h3Event); } } } CacheContext.Serialize(cacheStream, edTag, edDef); }