public void Parse(Dictionary <ushort, List <ulong> > track, Dictionary <ulong, Record> map, CASCHandler handler, bool quiet, OverToolFlags flags) { string output = flags.Positionals[2]; List <string> maps = new List <string>(); if (flags.Positionals.Length > 3) { maps.AddRange(flags.Positionals.Skip(2).Select((it) => it.ToLower())); } bool wildcard = maps.Count == 0 || maps.Contains("*"); List <ulong> masters = track[0x9F]; Dictionary <ulong, ulong> replace = new Dictionary <ulong, ulong>(); foreach (ulong masterKey in masters) { if (!map.ContainsKey(masterKey)) { continue; } STUD masterStud = new STUD(Util.OpenFile(map[masterKey], handler)); if (masterStud.Instances == null || masterStud.Instances[0] == null) { continue; } MapMaster master = (MapMaster)masterStud.Instances[0]; if (master == null) { continue; } string name = Util.GetString(master.Header.name.key, map, handler); if (string.IsNullOrWhiteSpace(name)) { name = $"Unknown{GUID.Index(master.Header.data.key):X}"; } if (!maps.Contains(name.ToLowerInvariant()) && !wildcard) { continue; } if (!map.ContainsKey(master.DataKey(0xB))) { continue; } if (!map.ContainsKey(master.DataKey(0x11))) { continue; } Console.Out.WriteLine("Dumping audio for map {0}", name); Dictionary <ulong, List <ulong> > soundData = new Dictionary <ulong, List <ulong> >(); string path = string.Format("{0}{1}{2}{1}{3}{1}{4}{1}", output, Path.DirectorySeparatorChar, Util.Strip(Util.SanitizePath(name)), "Audio", GUID.Index(master.Header.data.key)); HashSet <ulong> soundDone = new HashSet <ulong>(); using (Stream mapBStream = Util.OpenFile(map[master.DataKey(0xB)], handler)) { if (mapBStream != null) { Map mapB = new Map(mapBStream); foreach (STUD stud in mapB.STUDs) { Sound.FindSoundsSTUD(stud, soundDone, soundData, map, handler, replace, masterKey, master.DataKey(0xB)); } for (int i = 0; i < mapB.Records.Length; ++i) { if (mapB.Records[i] != null && mapB.Records[i].GetType() != typeof(Map0B)) { continue; } Map0B mapprop = (Map0B)mapB.Records[i]; if (!map.ContainsKey(mapprop.Header.binding)) { continue; } Sound.FindSoundsEx(mapprop.Header.binding, soundDone, soundData, map, handler, replace, master.DataKey(0xB)); } } } using (Stream map11Stream = Util.OpenFile(map[master.DataKey(0x11)], handler)) { if (map11Stream != null) { Map11 map11 = new Map11(map11Stream); Sound.FindSoundsSTUD(map11.main, soundDone, soundData, map, handler, replace, masterKey, master.DataKey(0x11)); Sound.FindSoundsSTUD(map11.secondary, soundDone, soundData, map, handler, replace, masterKey, master.DataKey(0x11)); } } if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } DumpVoice.Save(path, soundData, map, handler, quiet); } }
public Dictionary <ulong, List <string> >[] Write(Stream output, Map map, Map detail1, Map detail2, Map props, Map lights, string name, IDataWriter modelFormat) { if (modelFormat == null) { modelFormat = new OWMDLWriter(); } //Console.Out.WriteLine("Writing OWMAP"); using (BinaryWriter writer = new BinaryWriter(output)) { writer.Write((ushort)1); // version major writer.Write((ushort)1); // version minor if (name.Length == 0) { writer.Write((byte)0); } else { writer.Write(name); } uint size = 0; for (int i = 0; i < map.Records.Length; ++i) { if (map.Records[i] != null && map.Records[i].GetType() != typeof(Map01)) { continue; } size++; } writer.Write(size); // nr objects size = 1; for (int i = 0; i < detail1.Records.Length; ++i) { if (detail1.Records[i] != null && detail1.Records[i].GetType() != typeof(Map02)) { continue; } size++; } for (int i = 0; i < detail2.Records.Length; ++i) { if (detail2.Records[i] != null && detail2.Records[i].GetType() != typeof(Map08)) { continue; } size++; } for (int i = 0; i < props.Records.Length; ++i) { if (props.Records[i] != null && props.Records[i].GetType() != typeof(Map0B)) { continue; } if (((Map0B)props.Records[i]).ModelKey == 0) { continue; } size++; } writer.Write(size); // nr details // Extension 1.1 - Lights size = 0; for (int i = 0; i < lights.Records.Length; ++i) { if (lights.Records[i] != null && lights.Records[i].GetType() != typeof(Map09)) { continue; } size++; } writer.Write(size); // nr Lights Dictionary <ulong, List <string> >[] ret = new Dictionary <ulong, List <string> > [2]; ret[0] = new Dictionary <ulong, List <string> >(); ret[1] = new Dictionary <ulong, List <string> >(); for (int i = 0; i < map.Records.Length; ++i) { if (map.Records[i] != null && map.Records[i].GetType() != typeof(Map01)) { continue; } Map01 obj = (Map01)map.Records[i]; string modelFn = $"{GUID.Index(obj.Header.model):X12}{modelFormat.Format}"; writer.Write(modelFn); if (!ret[0].ContainsKey(obj.Header.model)) { ret[0].Add(obj.Header.model, new List <string>()); } ret[0][obj.Header.model].Add(modelFn); writer.Write(obj.Header.groupCount); for (int j = 0; j < obj.Header.groupCount; ++j) { Map01.Map01Group group = obj.Groups[j]; string materialFn = $"{GUID.Index(obj.Header.model):X12}_{GUID.Index(@group.material):X12}.owmat"; writer.Write(materialFn); if (!ret[1].ContainsKey(group.material)) { ret[1].Add(group.material, new List <string>()); } ret[1][group.material].Add(materialFn); writer.Write(group.recordCount); for (int k = 0; k < group.recordCount; ++k) { Map01.Map01GroupRecord record = obj.Records[j][k]; writer.Write(record.position.x); writer.Write(record.position.y); writer.Write(record.position.z); writer.Write(record.scale.x); writer.Write(record.scale.y); writer.Write(record.scale.z); writer.Write(record.rotation.x); writer.Write(record.rotation.y); writer.Write(record.rotation.z); writer.Write(record.rotation.w); } } } writer.Write($"physics.{modelFormat.Format}"); writer.Write((byte)0); writer.Write(0.0f); writer.Write(0.0f); writer.Write(0.0f); writer.Write(1.0f); writer.Write(1.0f); writer.Write(1.0f); writer.Write(0.0f); writer.Write(0.0f); writer.Write(0.0f); writer.Write(1.0f); for (int i = 0; i < detail1.Records.Length; ++i) { if (detail1.Records[i] != null && detail1.Records[i].GetType() != typeof(Map02)) { continue; } Map02 obj = (Map02)detail1.Records[i]; string modelFn = $"{GUID.LongKey(obj.Header.model):X12}{modelFormat.Format}"; string matFn = $"{GUID.LongKey(obj.Header.model):X12}.owmat"; writer.Write(modelFn); writer.Write(matFn); writer.Write(obj.Header.position.x); writer.Write(obj.Header.position.y); writer.Write(obj.Header.position.z); writer.Write(obj.Header.scale.x); writer.Write(obj.Header.scale.y); writer.Write(obj.Header.scale.z); writer.Write(obj.Header.rotation.x); writer.Write(obj.Header.rotation.y); writer.Write(obj.Header.rotation.z); writer.Write(obj.Header.rotation.w); if (!ret[0].ContainsKey(obj.Header.model)) { ret[0].Add(obj.Header.model, new List <string>()); } ret[0][obj.Header.model].Add(modelFn); if (!ret[1].ContainsKey(obj.Header.material)) { ret[1].Add(obj.Header.material, new List <string>()); } ret[1][obj.Header.material].Add(matFn); } for (int i = 0; i < detail2.Records.Length; ++i) { if (detail2.Records[i] != null && detail2.Records[i].GetType() != typeof(Map08)) { continue; } Map08 obj = (Map08)detail2.Records[i]; string modelFn = $"{GUID.LongKey(obj.Header.model):X12}{modelFormat.Format}"; string matFn = $"{GUID.LongKey(obj.Header.model):X12}.owmat"; writer.Write(modelFn); writer.Write(matFn); writer.Write(obj.Header.position.x); writer.Write(obj.Header.position.y); writer.Write(obj.Header.position.z); writer.Write(obj.Header.scale.x); writer.Write(obj.Header.scale.y); writer.Write(obj.Header.scale.z); writer.Write(obj.Header.rotation.x); writer.Write(obj.Header.rotation.y); writer.Write(obj.Header.rotation.z); writer.Write(obj.Header.rotation.w); if (!ret[0].ContainsKey(obj.Header.model)) { ret[0].Add(obj.Header.model, new List <string>()); } ret[0][obj.Header.model].Add(modelFn); if (!ret[1].ContainsKey(obj.Header.material)) { ret[1].Add(obj.Header.material, new List <string>()); } ret[1][obj.Header.material].Add(matFn); } for (int i = 0; i < props.Records.Length; ++i) { if (props.Records[i] != null && props.Records[i].GetType() != typeof(Map0B)) { continue; } Map0B obj = (Map0B)props.Records[i]; if (obj.ModelKey == 0) { continue; } string modelFn = $"{GUID.LongKey(obj.ModelKey):X12}{modelFormat.Format}"; string matFn = $"{GUID.LongKey(obj.MaterialKey):X12}.owmat"; writer.Write(modelFn); writer.Write(matFn); writer.Write(obj.Header.position.x); writer.Write(obj.Header.position.y); writer.Write(obj.Header.position.z); writer.Write(obj.Header.scale.x); writer.Write(obj.Header.scale.y); writer.Write(obj.Header.scale.z); writer.Write(obj.Header.rotation.x); writer.Write(obj.Header.rotation.y); writer.Write(obj.Header.rotation.z); writer.Write(obj.Header.rotation.w); if (!ret[0].ContainsKey(obj.ModelKey)) { ret[0].Add(obj.ModelKey, new List <string>()); } ret[0][obj.ModelKey].Add(modelFn); if (!ret[1].ContainsKey(obj.MaterialKey)) { ret[1].Add(obj.MaterialKey, new List <string>()); } ret[1][obj.MaterialKey].Add(matFn); } // Extension 1.1 - Lights for (int i = 0; i < lights.Records.Length; ++i) { if (lights.Records[i] != null && lights.Records[i].GetType() != typeof(Map09)) { continue; } Map09 obj = (Map09)lights.Records[i]; writer.Write(obj.Header.position.x); writer.Write(obj.Header.position.y); writer.Write(obj.Header.position.z); writer.Write(obj.Header.rotation.x); writer.Write(obj.Header.rotation.y); writer.Write(obj.Header.rotation.z); writer.Write(obj.Header.rotation.w); writer.Write(obj.Header.LightType); writer.Write(obj.Header.LightFOV); writer.Write(obj.Header.Color.x); writer.Write(obj.Header.Color.y); writer.Write(obj.Header.Color.z); writer.Write(obj.Header.unknown1A); writer.Write(obj.Header.unknown1B); writer.Write(obj.Header.unknown2A); writer.Write(obj.Header.unknown2B); writer.Write(obj.Header.unknown2C); writer.Write(obj.Header.unknown2D); writer.Write(obj.Header.unknown3A); writer.Write(obj.Header.unknown3B); writer.Write(obj.Header.unknownPos1.x); writer.Write(obj.Header.unknownPos1.y); writer.Write(obj.Header.unknownPos1.z); writer.Write(obj.Header.unknownQuat1.x); writer.Write(obj.Header.unknownQuat1.y); writer.Write(obj.Header.unknownQuat1.z); writer.Write(obj.Header.unknownQuat1.w); writer.Write(obj.Header.unknownPos2.x); writer.Write(obj.Header.unknownPos2.y); writer.Write(obj.Header.unknownPos2.z); writer.Write(obj.Header.unknownQuat2.x); writer.Write(obj.Header.unknownQuat2.y); writer.Write(obj.Header.unknownQuat2.z); writer.Write(obj.Header.unknownQuat2.w); writer.Write(obj.Header.unknownPos3.x); writer.Write(obj.Header.unknownPos3.y); writer.Write(obj.Header.unknownPos3.z); writer.Write(obj.Header.unknownQuat3.x); writer.Write(obj.Header.unknownQuat3.y); writer.Write(obj.Header.unknownQuat3.z); writer.Write(obj.Header.unknownQuat3.w); writer.Write(obj.Header.unknown4A); writer.Write(obj.Header.unknown4B); writer.Write(obj.Header.unknown5); writer.Write(obj.Header.unknown6A); writer.Write(obj.Header.unknown6B); writer.Write(obj.Header.unknown7A); writer.Write(obj.Header.unknown7B); } return(ret); } }
public void Parse(Dictionary <ushort, List <ulong> > track, Dictionary <ulong, Record> map, CASCHandler handler, bool quiet, OverToolFlags flags) { string output = flags.Positionals[2]; List <string> maps = flags.Positionals.Skip(3).ToList(); bool skipCmodel = !flags.ExportCollision; for (int i = 0; i < maps.Count; ++i) { maps[i] = maps[i].ToUpperInvariant().TrimStart('0'); } bool mapWildcard = maps.Count == 0; if (maps.Count > 0 && maps.Contains("*")) { mapWildcard = true; } char animEncoding = flags.AnimFormat; if (flags.Raw) { animEncoding = '+'; } bool suppressAnimations = flags.SkipAnimations; if (animEncoding == '+' && !flags.RawAnimation) { suppressAnimations = true; } char modelEncoding = flags.ModelFormat; if (flags.Raw) { modelEncoding = '+'; } bool suppressModels = flags.SkipModels; if (modelEncoding == '+' && !flags.RawModel) { suppressModels = true; } IDataWriter animWriter = null; if (animEncoding != 0 && animEncoding != '+') { Assembly asm = typeof(IDataWriter).Assembly; Type t = typeof(IDataWriter); List <Type> types = asm.GetTypes().Where(tt => tt != t && t.IsAssignableFrom(tt)).ToList(); foreach (Type tt in types) { if (animWriter != null) { break; } if (tt.IsInterface) { continue; } IDataWriter tmp = (IDataWriter)Activator.CreateInstance(tt); for (int i = 0; i < tmp.Identifier.Length; ++i) { if (tmp.Identifier[i] == animEncoding) { animWriter = tmp; break; } } } } IDataWriter modelWriter = null; if (modelEncoding != 0 && modelEncoding != '+') { Assembly asm = typeof(IDataWriter).Assembly; Type t = typeof(IDataWriter); List <Type> types = asm.GetTypes().Where(tt => tt != t && t.IsAssignableFrom(tt)).ToList(); foreach (Type tt in types) { if (modelWriter != null) { break; } if (tt.IsInterface) { continue; } IDataWriter tmp = (IDataWriter)Activator.CreateInstance(tt); for (int i = 0; i < tmp.Identifier.Length; ++i) { if (tmp.Identifier[i] == modelEncoding) { modelWriter = tmp; break; } } } } List <ulong> masters = track[0x9F]; List <byte> LODs = new List <byte>(new byte[5] { 0, 1, 128, 254, 255 }); Dictionary <ulong, ulong> replace = new Dictionary <ulong, ulong>(); foreach (ulong masterKey in masters) { if (!map.ContainsKey(masterKey)) { continue; } STUD masterStud = new STUD(Util.OpenFile(map[masterKey], handler)); if (masterStud.Instances == null) { continue; } MapMaster master = (MapMaster)masterStud.Instances[0]; if (master == null) { continue; } string name = Util.GetString(master.Header.name.key, map, handler); if (string.IsNullOrWhiteSpace(name)) { name = $"Unknown{GUID.Index(master.Header.data.key):X}"; } if (!mapWildcard && !(maps.Contains(name.ToUpperInvariant()) || maps.Contains($"{GUID.Index(masterKey):X}"))) { continue; } string outputPath = string.Format("{0}{1}{2}{1}{3:X}{1}", output, Path.DirectorySeparatorChar, Util.SanitizePath(name), GUID.Index(master.Header.data.key)); if (!map.ContainsKey(master.Header.data.key)) { continue; } HashSet <ulong> parsed = new HashSet <ulong>(); Dictionary <ulong, ulong> animList = new Dictionary <ulong, ulong>(); Dictionary <ulong, List <ulong> > soundData = new Dictionary <ulong, List <ulong> >(); if (!map.ContainsKey(master.DataKey(1))) { continue; } if (!map.ContainsKey(master.DataKey(2))) { continue; } if (!map.ContainsKey(master.DataKey(8))) { continue; } if (!map.ContainsKey(master.DataKey(0xB))) { continue; } if (!map.ContainsKey(master.DataKey(0x11))) { continue; } if (!map.ContainsKey(master.DataKey(0x10))) { continue; } using (Stream mapStream = Util.OpenFile(map[master.DataKey(1)], handler)) { Console.Out.WriteLine("Extracting map {0} with ID {1:X8}", name, GUID.Index(master.Header.data.key)); Map mapData = new Map(mapStream); IDataWriter owmap = new OWMAPWriter(); Dictionary <ulong, List <string> >[] used = null; if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } HashSet <ulong> soundDone = new HashSet <ulong>(); Sound.FindSoundsEx(master.Header.audio.key, soundDone, soundData, map, handler, replace, master.Header.data.key); using (Stream map2Stream = Util.OpenFile(map[master.DataKey(2)], handler)) { if (map2Stream != null) { Map map2Data = new Map(map2Stream); using (Stream map8Stream = Util.OpenFile(map[master.DataKey(8)], handler)) { if (map8Stream != null) { Map map8Data = new Map(map8Stream); using (Stream mapBStream = Util.OpenFile(map[master.DataKey(0xB)], handler)) { if (mapBStream != null) { Map mapBData = new Map(mapBStream, true); using (Stream map11Stream = Util.OpenFile(map[master.DataKey(0x11)], handler)) { if (map11Stream != null) { Map11 map11 = new Map11(map11Stream); Sound.FindSoundsSTUD(map11.main, soundDone, soundData, map, handler, replace, masterKey, master.DataKey(0x11)); Sound.FindSoundsSTUD(map11.secondary, soundDone, soundData, map, handler, replace, masterKey, master.DataKey(0x11)); } } mapBStream.Position = (long)(Math.Ceiling((float)mapBStream.Position / 16.0f) * 16); // Future proofing for (int i = 0; i < mapBData.STUDs.Count; ++i) { STUD stud = mapBData.STUDs[i]; Sound.FindSoundsSTUD(stud, soundDone, soundData, map, handler, replace, master.DataKey(0xB), master.DataKey(0xB)); } for (int i = 0; i < mapBData.Records.Length; ++i) { if (mapBData.Records[i] != null && mapBData.Records[i].GetType() != typeof(Map0B)) { continue; } Map0B mapprop = (Map0B)mapBData.Records[i]; if (!map.ContainsKey(mapprop.Header.binding)) { continue; } Sound.FindSoundsEx(mapprop.Header.binding, soundDone, soundData, map, handler, replace, master.DataKey(0xB)); HashSet <ulong> bindingModels = new HashSet <ulong>(); Dictionary <ulong, List <ImageLayer> > bindingTextures = new Dictionary <ulong, List <ImageLayer> >(); using (Stream bindingFile = Util.OpenFile(map[mapprop.Header.binding], handler)) { STUD binding = new STUD(bindingFile, true, STUDManager.Instance, false, true); foreach (ISTUDInstance instance in binding.Instances) { if (instance == null) { continue; } if (instance.Name != binding.Manager.GetName(typeof(ComplexModelRecord))) { continue; } ComplexModelRecord cmr = (ComplexModelRecord)instance; mapprop.MaterialKey = cmr.Data.material.key; mapprop.ModelKey = cmr.Data.model.key; Skin.FindAnimations(cmr.Data.animationList.key, soundData, animList, replace, parsed, map, handler, bindingModels, bindingTextures, mapprop.ModelKey); Skin.FindAnimations(cmr.Data.secondaryAnimationList.key, soundData, animList, replace, parsed, map, handler, bindingModels, bindingTextures, mapprop.ModelKey); break; } } mapBData.Records[i] = mapprop; } using (Stream mapLStream = Util.OpenFile(map[master.DataKey(9)], handler)) { Map mapLData = new Map(mapLStream); using (Stream outputStream = File.Open($"{outputPath}{Util.SanitizePath(name)}{owmap.Format}", FileMode.Create, FileAccess.Write)) { used = owmap.Write(outputStream, mapData, map2Data, map8Data, mapBData, mapLData, name, modelWriter); } } } } } } } } IDataWriter owmat = new OWMATWriter(); using (Stream map10Stream = Util.OpenFile(map[master.DataKey(0x10)], handler)) { Map10 physics = new Map10(map10Stream); using (Stream outputStream = File.Open($"{outputPath}physics{modelWriter.Format}", FileMode.Create, FileAccess.Write)) { modelWriter.Write(physics, outputStream, new object[0]); } } if (used != null) { Dictionary <ulong, List <string> > models = used[0]; Dictionary <ulong, List <string> > materials = used[1]; Dictionary <ulong, Dictionary <ulong, List <ImageLayer> > > cache = new Dictionary <ulong, Dictionary <ulong, List <ImageLayer> > >(); if (!suppressModels) { foreach (KeyValuePair <ulong, List <string> > modelpair in models) { if (!map.ContainsKey(modelpair.Key)) { continue; } if (!parsed.Add(modelpair.Key)) { continue; } HashSet <string> extracted = new HashSet <string>(); using (Stream modelStream = Util.OpenFile(map[modelpair.Key], handler)) { Chunked mdl = new Chunked(modelStream, true); modelStream.Position = 0; if (modelEncoding != '+' && modelWriter != null) { foreach (string modelOutput in modelpair.Value) { if (!extracted.Add(modelOutput)) { continue; } using (Stream outputStream = File.Open($"{outputPath}{modelOutput}", FileMode.Create, FileAccess.Write)) { if (modelWriter.Write(mdl, outputStream, LODs, new Dictionary <ulong, List <ImageLayer> >(), new object[5] { null, null, null, null, skipCmodel })) { if (!quiet) { Console.Out.WriteLine("Wrote model {0}", modelOutput); } } else { if (!quiet) { Console.Out.WriteLine("Failed to write model"); } } } } } if (flags.RawModel) { using (Stream outputStream = File.Open($"{outputPath}{GUID.LongKey(modelpair.Key):X12}.{GUID.Type(modelpair.Key):X3}", FileMode.Create, FileAccess.Write)) { if (modelWriter.Write(mdl, outputStream, LODs, new Dictionary <ulong, List <ImageLayer> >(), new object[5] { null, null, null, null, skipCmodel })) { if (!quiet) { Console.Out.WriteLine("Wrote raw model {0:X12}.{1:X3}", GUID.LongKey(modelpair.Key), GUID.Type(modelpair.Key)); } } else { if (!quiet) { Console.Out.WriteLine("Failed to write model"); } } } } } } } if (!suppressAnimations) { foreach (KeyValuePair <ulong, ulong> kv in animList) { ulong parent = kv.Value; ulong key = kv.Key; Stream animStream = Util.OpenFile(map[key], handler); if (animStream == null) { continue; } Animation anim = new Animation(animStream); animStream.Position = 0; string outpath = string.Format("{0}Animations{1}{2:X12}{1}{5}{1}{3:X12}.{4:X3}", outputPath, Path.DirectorySeparatorChar, GUID.Index(parent), GUID.LongKey(key), GUID.Type(key), anim.Header.priority); if (!Directory.Exists(Path.GetDirectoryName(outpath))) { Directory.CreateDirectory(Path.GetDirectoryName(outpath)); } if (flags.RawAnimation) { using (Stream outp = File.Open(outpath, FileMode.Create, FileAccess.Write)) { animStream.CopyTo(outp); if (!quiet) { Console.Out.WriteLine("Wrote raw animation {0}", outpath); } } } if (animEncoding != '+' && animWriter != null) { outpath = string.Format("{0}Animations{1}{2:X12}{1}{5}{1}{3:X12}.{4}", outputPath, Path.DirectorySeparatorChar, GUID.Index(parent), GUID.LongKey(key), animWriter.Format, anim.Header.priority); using (Stream outp = File.Open(outpath, FileMode.Create, FileAccess.Write)) { animWriter.Write(anim, outp); if (!quiet) { Console.Out.WriteLine("Wrote animation {0}", outpath); } } } } } if (!flags.SkipSound) { Console.Out.WriteLine("Dumping sounds..."); string soundPath = $"{outputPath}Sounds{Path.DirectorySeparatorChar}"; if (!Directory.Exists(soundPath)) { Directory.CreateDirectory(soundPath); } DumpVoice.Save(soundPath, soundData, map, handler, quiet); } if (!flags.SkipTextures) { foreach (KeyValuePair <ulong, List <string> > matpair in materials) { Dictionary <ulong, List <ImageLayer> > tmp = new Dictionary <ulong, List <ImageLayer> >(); if (cache.ContainsKey(matpair.Key)) { tmp = cache[matpair.Key]; } else { Skin.FindTextures(matpair.Key, tmp, new Dictionary <ulong, ulong>(), new HashSet <ulong>(), map, handler); cache.Add(matpair.Key, tmp); } Dictionary <string, TextureType> types = new Dictionary <string, TextureType>(); foreach (KeyValuePair <ulong, List <ImageLayer> > kv in tmp) { ulong materialId = kv.Key; List <ImageLayer> sublayers = kv.Value; HashSet <ulong> materialParsed = new HashSet <ulong>(); foreach (ImageLayer layer in sublayers) { if (!materialParsed.Add(layer.key)) { continue; } KeyValuePair <string, TextureType> pair = Skin.SaveTexture(layer.key, materialId, map, handler, outputPath, quiet); if (pair.Key == null) { continue; } types.Add(pair.Key, pair.Value); } } foreach (string matOutput in matpair.Value) { if (File.Exists($"{outputPath}{matOutput}")) { continue; } using (Stream outputStream = File.Open($"{outputPath}{matOutput}", FileMode.Create, FileAccess.Write)) { if (owmat.Write(null, outputStream, null, tmp, new object[1] { types })) { if (!quiet) { Console.Out.WriteLine("Wrote material {0}", matOutput); } } else { if (!quiet) { Console.Out.WriteLine("Failed to write material"); } } } } } } } } } }