public void InitInterpreter(BioTlkFileSet editorTalkset = null) { DynamicByteProvider db = new DynamicByteProvider(pcc.Exports[Index].Data); hb1.ByteProvider = db; memory = pcc.Exports[Index].Data; memsize = memory.Length; // attempt to find a TlkFileSet associated with the object, else just pick the first one and hope it's correct if (editorTalkset == null) { PropertyReader.Property tlkSetRef = PropertyReader.getPropList(pcc, pcc.Exports[Index].Data).FirstOrDefault(x => pcc.getNameEntry(x.Name) == "m_oTlkFileSet"); if (tlkSetRef != null) { tlkset = new BioTlkFileSet(pcc, tlkSetRef.Value.IntValue - 1); } else { tlkset = new BioTlkFileSet(pcc); } } else { tlkset = editorTalkset; } }
private void ReadColors(PropertyReader.Property p, IMEPackage pcc) { int count = BitConverter.ToInt32(p.raw, 24); int start = 28; for (int i = 0; i < count; i++) { List <PropertyReader.Property> props = PropertyReader.ReadProp(pcc, p.raw, start); ColorOverride co = new ColorOverride(); foreach (PropertyReader.Property sp in props) { string propName = pcc.getNameEntry(sp.Name); switch (propName) { case "nName": int nameI = sp.Value.IntValue; if (pcc.isName(nameI)) { co.ParamName = pcc.Names[nameI]; } break; case "cValue": co.Value = new LinearColor(sp); break; case "None": break; } } ColorOverrides.Add(co); start = props[props.Count - 1].offend; } }
public LinearColor(PropertyReader.Property p) { R = BitConverter.ToSingle(p.raw, 32); G = BitConverter.ToSingle(p.raw, 36); B = BitConverter.ToSingle(p.raw, 40); A = BitConverter.ToSingle(p.raw, 44); }
private static Vector GetVector(PropertyReader.Property prop) { Vector vec; vec.X = BitConverter.ToSingle(prop.raw, 32); vec.Y = BitConverter.ToSingle(prop.raw, 36); vec.Z = BitConverter.ToSingle(prop.raw, 40); return(vec); }
private void ReadFinalSkeleton(PropertyReader.Property p) { int count = BitConverter.ToInt32(p.raw, 24); int propStart = 28; for (int i = 0; i < count; i++) { List <PropertyReader.Property> BoneOffsetProps = PropertyReader.ReadProp(pcc, p.raw, propStart); propStart = BoneOffsetProps[BoneOffsetProps.Count - 1].offend; BoneOffset bone = new BoneOffset(BoneOffsetProps, pcc); m_aFinalSkeleton.Add(bone); } }
public byte[] ToArray(int pccExportDataOffset) { MemoryStream buffer = new MemoryStream(); buffer.Write(headerData, 0, headerData.Length); foreach (KeyValuePair <string, PropertyReader.Property> kvp in properties) { PropertyReader.Property property = kvp.Value; // this is the part when I get rid of the LODGroup property!!!!!!!!!!!!!!! // the texture will use texturegroup_world as default texturegroup //if (kvp.Key == "LODGroup") // continue; if (kvp.Key == "LODBias") { continue; } if (kvp.Key == "InternalFormatLODBias") { continue; } buffer.Write(property.raw, 0, property.raw.Length); if (kvp.Key == "UnpackMin") { buffer.Write(property.raw, 0, property.raw.Length); buffer.Write(property.raw, 0, property.raw.Length); } } buffer.WriteValueU32(numMipMaps); foreach (ImageInfo imgInfo in imgList) { buffer.WriteValueS32((int)imgInfo.storageType); buffer.WriteValueS32(imgInfo.uncSize); buffer.WriteValueS32(imgInfo.cprSize); if (imgInfo.storageType == storage.pccSto) { buffer.WriteValueS32((int)(imgInfo.offset + pccExportDataOffset + dataOffset)); buffer.Write(imageData, imgInfo.offset, imgInfo.uncSize); } else { buffer.WriteValueS32(imgInfo.offset); } buffer.WriteValueU32(imgInfo.imgSize.width); buffer.WriteValueU32(imgInfo.imgSize.height); } // Texture2D footer, 24 bytes size buffer.Write(imageData, imageData.Length - 24, 24); return(buffer.ToArray()); }
private void ReadMorphFeatures(PropertyReader.Property p) { int count = BitConverter.ToInt32(p.raw, 24); int propStart = 28; for (int i = 0; i < count; i++) { List <PropertyReader.Property> FeatureProps = PropertyReader.ReadProp(pcc, p.raw, propStart); propStart = FeatureProps[FeatureProps.Count - 1].offend; BioFeature Feature = new BioFeature(FeatureProps, pcc); m_aMorphFeatures.Add(Feature); } }
private void ReadTextures(PropertyReader.Property p, IMEPackage pcc) { int count = BitConverter.ToInt32(p.raw, 24); int start = 28; for (int i = 0; i < count; i++) { // read next 68 bytes List <PropertyReader.Property> props = PropertyReader.ReadProp(pcc, p.raw, start); TextureOverride to = new TextureOverride(); foreach (PropertyReader.Property sp in props) { string propName = pcc.getNameEntry(sp.Name); switch (propName) { case "nName": int nameI = sp.Value.IntValue; if (pcc.isName(nameI)) { to.ParamName = pcc.Names[nameI]; } break; case "m_pTexture": int objTextIndex = sp.Value.IntValue; if (pcc.isExport(objTextIndex - 1)) { to.TextureName = pcc.Exports[objTextIndex - 1].ObjectName; } break; case "None": break; } } TextureOverrides.Add(to); start = props[props.Count - 1].offend; } }
private void ReadScalars(PropertyReader.Property p, IMEPackage pcc) { int count = BitConverter.ToInt32(p.raw, 24); int start = 28; for (int i = 0; i < count; i++) { // read next 68 bytes List <PropertyReader.Property> props = PropertyReader.ReadProp(pcc, p.raw, start); ScalarOverride so = new ScalarOverride(); foreach (PropertyReader.Property sp in props) { string propName = pcc.getNameEntry(sp.Name); switch (propName) { case "nName": int nameI = sp.Value.IntValue; if (pcc.isName(nameI)) { so.ParamName = pcc.Names[nameI]; } break; case "sValue": so.Value = BitConverter.ToSingle(sp.raw, sp.raw.Length - 4); break; case "None": break; } } ScalarOverrides.Add(so); start = props[props.Count - 1].offend; } }
public TreeNode MakeDefaultPropNode(PropertyReader.Property p) { string tp = PropertyReader.TypeToString((int)p.TypeVal); switch (tp) { case "Byte Property": return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ") : (" + pcc.GetName(BitConverter.ToInt32(p.raw, 24)) + ") " + pcc.GetName(BitConverter.ToInt32(p.raw, 32)))); case "Bool Property": return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ") : " + (p.Value.IntValue == 1))); case "Object Property": string s = " "; if (p.Value.IntValue != 0) { s = pcc.GetObject(p.Value.IntValue); } return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ") : " + p.Value.IntValue + s)); case "Integer Property": return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ") : " + p.Value.IntValue)); case "Name Property": return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ") : " + pcc.GetName(p.Value.IntValue))); case "Float Property": return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ") : " + BitConverter.ToSingle(p.raw, 24))); case "String Property": return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ") : " + p.Value.StringValue)); default: return(new TreeNode(pcc.GetName(p.Name) + " (" + tp + ")")); } }
} // showable image list public Texture2D(PCCObject pccObj, int texIdx) { pccRef = pccObj; // check if texIdx is an Export index and a Texture2D class if (pccObj.isExport(texIdx) && (pccObj.Exports[texIdx].ClassName == className)) { PCCObject.ExportEntry expEntry = pccObj.Exports[texIdx]; properties = new Dictionary <string, PropertyReader.Property>(); byte[] rawData = (byte[])expEntry.Data.Clone(); int propertiesOffset = PropertyReader.detectStart(pccObj, rawData); headerData = new byte[propertiesOffset]; Buffer.BlockCopy(rawData, 0, headerData, 0, propertiesOffset); pccOffset = (uint)expEntry.DataOffset; List <PropertyReader.Property> tempProperties = PropertyReader.getPropList(pccObj, rawData); texName = expEntry.ObjectName; for (int i = 0; i < tempProperties.Count; i++) { PropertyReader.Property property = tempProperties[i]; if (!properties.ContainsKey(pccObj.Names[property.Name])) { properties.Add(pccObj.Names[property.Name], property); } switch (pccObj.Names[property.Name]) { case "Format": texFormat = pccObj.Names[property.Value.IntValue].Substring(3); break; case "TextureFileCacheName": arcName = pccObj.Names[property.Value.IntValue]; break; case "LODGroup": LODGroup = pccObj.Names[property.Value.IntValue]; break; case "None": dataOffset = (uint)(property.offsetval + property.Size); break; } } // if "None" property isn't found throws an exception if (dataOffset == 0) { throw new Exception("\"None\" property not found"); } else { imageData = new byte[rawData.Length - dataOffset]; Buffer.BlockCopy(rawData, (int)dataOffset, imageData, 0, (int)(rawData.Length - dataOffset)); } } else { throw new Exception("Texture2D " + texIdx + " not found"); } pccExpIdx = texIdx; MemoryStream dataStream = new MemoryStream(imageData); numMipMaps = dataStream.ReadValueU32(); uint count = numMipMaps; imgList = new List <ImageInfo>(); while (dataStream.Position < dataStream.Length && count > 0) { ImageInfo imgInfo = new ImageInfo(); imgInfo.storageType = (storage)dataStream.ReadValueS32(); imgInfo.uncSize = dataStream.ReadValueS32(); imgInfo.cprSize = dataStream.ReadValueS32(); imgInfo.offset = dataStream.ReadValueS32(); if (imgInfo.storageType == storage.pccSto) { //imgInfo.offset = (int)(pccOffset + dataOffset); // saving pcc offset as relative to exportdata offset, not absolute imgInfo.offset = (int)dataStream.Position; // saving pcc offset as relative to exportdata offset, not absolute //MessageBox.Show("Pcc class offset: " + pccOffset + "\nimages data offset: " + imgInfo.offset.ToString()); dataStream.Seek(imgInfo.uncSize, SeekOrigin.Current); } imgInfo.imgSize = new ImageSize(dataStream.ReadValueU32(), dataStream.ReadValueU32()); imgList.Add(imgInfo); count--; } // save what remains /*int remainingBytes = (int)(dataStream.Length - dataStream.Position); * footerData = new byte[remainingBytes]; * dataStream.Read(footerData, 0, footerData.Length);*/ }
public void OneImageToRuleThemAll(string imageFileName, string archiveDir, out string newTextureGroup) { newTextureGroup = null; ImageMipMapHandler imgMipMap = new ImageMipMapHandler(imageFileName, null); // starts from the smaller image for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--) { ImageFile newImageFile = imgMipMap.imageList[i]; // insert images only with size > 64 if (newImageFile.imgSize.width < 64 && newImageFile.imgSize.height < 64) { continue; } // write the new image into a file (I know that's crappy solution, I'll find another one later...) using (FileStream newImageStream = File.Create(newImageFile.fileName)) { byte[] buffer = newImageFile.ToArray(); newImageStream.Write(buffer, 0, buffer.Length); } // if the image size exists inside the texture2d image list then we have to replace it if (imgList.Exists(img => img.imgSize == newImageFile.imgSize)) { // ...but at least for now I can reuse my replaceImage function... ;) replaceImage(newImageFile.imgSize.ToString(), newImageFile.fileName, archiveDir); } else // if the image doesn't exists then we have to add it { // ...and use my addBiggerImage function! :P addBiggerImage(newImageFile.fileName, archiveDir); } File.Delete(newImageFile.fileName); } // add texturegroup_world inside GamerSettings.ini in order to overwrite values ImageSize maxSize = imgList.Max(image => image.imgSize); uint maxValue = Math.Max(maxSize.width, maxSize.height); string section = "SystemSettings"; string key = "texturegroup_shadowmap"; string newValue = "(MinLODSize=128,MaxLODSize=" + maxValue + ",LODBias=0)"; IniFile iniFile = new IniFile(ME3Directory.GamerSettingsIniFile); string oldValue = iniFile.IniReadValue(section, key); if (oldValue == "") { iniFile.IniWriteValue(section, key, newValue); } else { char[] delimiters = new char[] { '=', ',' }; uint maxLODSize = Convert.ToUInt32(oldValue.Split(delimiters)[3]); if (maxValue > maxLODSize) { iniFile.IniWriteValue(section, key, newValue); } } // check that Texture2D has a TextureGroup if (!properties.ContainsKey("LODGroup")) { return; } // extracting values from LODGroup Property PropertyReader.Property LODGroup = properties["LODGroup"]; string textureGroupName = pccRef.Names[LODGroup.Value.IntValue]; string newTextureGroupName = "TEXTUREGROUP_Shadowmap"; textureGroupName = newTextureGroupName; if (!pccRef.Names.Exists(name => name == newTextureGroupName)) { pccRef.Names.Add(newTextureGroupName); } using (MemoryStream rawStream = new MemoryStream(LODGroup.raw)) { rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(pccRef.Names.FindIndex(name => name == newTextureGroupName)); //rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(0); properties["LODGroup"].raw = rawStream.ToArray(); } //LODGroup.Value.IntValue = pccRef.Names.FindIndex(name => name == newTextureGroupName); /*MessageBox.Show("Texturegroup Name: " + textureGroupName); * ImageSize maxImageSize = imgList.Max(image => image.imgSize); * int textureGroupValue = (int)Math.Max(maxImageSize.width, maxImageSize.height) + 1; * * // open Engine.pcc file and edit TextureGroup enumerator * { * PCCObject pccEngine = new PCCObject(ME3Directory.cookedPath + "Engine.pcc"); * int idxTexGroups = pccEngine.Exports.FindIndex(export => export.ObjectName == "TextureGroup"); * * TextureGroup texGroup = new TextureGroup(pccEngine, pccEngine.Exports[idxTexGroups].Data); * if (texGroup.ExistsTextureGroup(textureGroupName, textureGroupValue)) * return; * else * { * if (!pccEngine.Names.Exists(name => name == newTextureGroupName)) * pccEngine.Names.Add(newTextureGroupName); * * newTextureGroup = textureGroupName + "_" + (textureGroupValue - 1); * * texGroup.Add(textureGroupName, textureGroupValue); * MessageBox.Show("Now editing texgroup enum"); * pccEngine.Exports[idxTexGroups].Data = texGroup.ToArray(); * MessageBox.Show("Now saving engine.pcc"); * pccEngine.saveToFile(true, ME3Directory.cookedPath + "Engine.pcc"); * MessageBox.Show("Saved engine.pcc"); * * * } * }*/ }
public void OneImageToRuleThemAll(string archiveDir, ImageFile im, out string newTextureGroup, byte[] imgData) { newTextureGroup = null; ImageMipMapHandler imgMipMap = new ImageMipMapHandler("", imgData); // starts from the smaller image for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--) { ImageFile newImageFile = imgMipMap.imageList[i]; // insert images only with size > 64 if (newImageFile.imgSize.width < 64 && newImageFile.imgSize.height < 64) { continue; } // if the image size exists inside the texture2d image list then we have to replace it if (imgList.Exists(img => img.imgSize == newImageFile.imgSize)) { // ...but at least for now I can reuse my replaceImage function... ;) replaceImage(newImageFile.imgSize.ToString(), newImageFile, archiveDir); } else // if the image doesn't exists then we have to add it { // ...and use my addBiggerImage function! :P addBiggerImage(newImageFile, archiveDir); } File.Delete(newImageFile.fileName); } // add texturegroup_world inside GamerSettings.ini in order to overwrite values ImageSize maxSize = imgList.Max(image => image.imgSize); uint maxValue = Math.Max(maxSize.width, maxSize.height); string section = "SystemSettings"; string key = "texturegroup_shadowmap"; string newValue = "(MinLODSize=128,MaxLODSize=" + maxValue + ",LODBias=0)"; IniFile iniFile = new IniFile(ME3Directory.GamerSettingsIniFile); string oldValue = iniFile.IniReadValue(section, key); if (oldValue == "") { iniFile.IniWriteValue(section, key, newValue); } else { char[] delimiters = new char[] { '=', ',' }; uint maxLODSize = Convert.ToUInt32(oldValue.Split(delimiters)[3]); if (maxValue > maxLODSize) { iniFile.IniWriteValue(section, key, newValue); } } // check that Texture2D has a TextureGroup if (!properties.ContainsKey("LODGroup")) { return; } // extracting values from LODGroup Property PropertyReader.Property LODGroup = properties["LODGroup"]; string textureGroupName = pccRef.Names[LODGroup.Value.IntValue]; string newTextureGroupName = "TEXTUREGROUP_Shadowmap"; textureGroupName = newTextureGroupName; if (!pccRef.Names.Exists(name => name == newTextureGroupName)) { pccRef.Names.Add(newTextureGroupName); } using (MemoryStream rawStream = new MemoryStream(LODGroup.raw)) { rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(pccRef.Names.FindIndex(name => name == newTextureGroupName)); //rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(0); properties["LODGroup"].raw = rawStream.ToArray(); } }
public void LetsDump2(string classname) { if (string.IsNullOrEmpty(ME3Directory.cookedPath)) { MessageBox.Show("This functionality requires ME3 to be installed. Set its path at:\n Options > Set Custom Path > Mass Effect 3"); return; } string path = ME3Directory.cookedPath; string[] files = Directory.GetFiles(path, "*.pcc"); pb1.Minimum = 0; rtb1.Text = ""; pauseToolStripMenuItem.Visible = true; pb2.Value = 0; pb2.Maximum = files.Length; List <string> Names = new List <string>(); List <string> Types = new List <string>(); List <string> First = new List <string>(); DebugOutput.Clear(); for (int i = 0; i < files.Length; i++) { try { while (pause) { Application.DoEvents(); } using (ME3Package pcc = MEPackageHandler.OpenME3Package(files[i])) { DebugOutput.PrintLn(i + "/" + files.Length + " Scanning file : " + Path.GetFileName(files[i])); IReadOnlyList <IExportEntry> Exports = pcc.Exports; pb1.Maximum = Exports.Count; pb2.Value = i; for (int j = 0; j < Exports.Count; j++) { IExportEntry ent = Exports[j]; if (ent.ClassName == classname) { List <PropertyReader.Property> p = PropertyReader.getPropList(ent); for (int k = 0; k < p.Count; k++) { PropertyReader.Property prop = p[k]; int found = -1; for (int l = 0; l < Names.Count(); l++) { if (pcc.getNameEntry(prop.Name) == Names[l]) { found = l; } } if (found == -1) { Names.Add(pcc.getNameEntry(prop.Name)); Types.Add(PropertyReader.TypeToString((int)prop.TypeVal)); First.Add(Path.GetFileName(files[i]) + " #" + j); } } } if (j % 500 == 0) { pb1.Value = j; Status.Text = "State : " + j + " / " + Exports.Count; string s = "Possible properties found so far for class \"" + classname + "\":\n"; for (int k = 0; k < Names.Count(); k++) { s += Types[k] + " : \"" + Names[k] + "\" first found: " + First[k] + "\n"; } Action action = () => rtb1.Text = s; rtb1.Invoke(action); action = () => rtb1.SelectionStart = s.Length; rtb1.Invoke(action); action = () => rtb1.ScrollToCaret(); rtb1.Invoke(action); Application.DoEvents(); } } } } catch (Exception ex) { MessageBox.Show("Error:\n" + ex.Message); DebugOutput.PrintLn("Could not open file : " + Path.GetFileName(files[i])); } } Status.Text = "State : Done"; string t = "Possible properties found for class \"" + classname + "\":\n"; for (int k = 0; k < Names.Count(); k++) { t += Types[k] + " : \"" + Names[k] + "\" first found: " + First[k] + "\n"; } rtb1.Text = t; rtb1.SelectionStart = rtb1.TextLength; rtb1.ScrollToCaret(); pb1.Value = 0; pauseToolStripMenuItem.Visible = false; }
private void makeDialogDumpToolStripMenuItem_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(ME3Directory.cookedPath)) { MessageBox.Show("This functionality requires ME3 to be installed. Set its path at:\n Options > Set Custom Path > Mass Effect 3"); return; } string path = ME3Directory.cookedPath; string[] files = Directory.GetFiles(path); pb1.Minimum = 0; rtb1.Text = ""; int count = 0; int count2 = 0; string t = ""; pauseToolStripMenuItem.Visible = true; pb2.Value = 0; pb2.Maximum = files.Length; for (int i = 0; i < files.Length; i++) { try { if (files[i].ToLower().EndsWith(".pcc")) { while (pause) { Application.DoEvents(); } using (ME3Package pcc = MEPackageHandler.OpenME3Package(files[i])) { IReadOnlyList <IExportEntry> Exports = pcc.Exports; pb1.Maximum = Exports.Count; pb2.Value = i; string s = "String references for file " + files[i] + "\n"; for (int j = 0; j < Exports.Count; j++) { IExportEntry ent = Exports[j]; List <PropertyReader.Property> p = PropertyReader.getPropList(ent); for (int k = 0; k < p.Count; k++) { PropertyReader.Property prop = p[k]; if (prop.TypeVal == PropertyType.StringRefProperty) { s += "Object #" + j + " : " + PropertyReader.PropertyToText(prop, pcc) + "\n"; } if (prop.TypeVal == PropertyType.ArrayProperty) { string tt = DumpArray(pcc, ent.Data, prop.offsetval + 4, 1); if (tt.Length != 0) { s += "Object #" + j + " in : " + PropertyReader.PropertyToText(prop, pcc) + "\n"; s += tt; } } if (prop.TypeVal == PropertyType.StructProperty) { string tt = DumpArray(pcc, ent.Data, prop.offsetval + 8, 1); if (tt.Length != 0) { s += "Object #" + j + " in : " + PropertyReader.PropertyToText(prop, pcc) + "\n"; s += tt; } } } if (count++ > 500) { count = 0; pb1.Value = j; Status.Text = "State : " + j + " / " + Exports.Count; if (count2++ > 10) { count2 = 0; rtb1.Text = t; rtb1.SelectionStart = rtb1.TextLength; rtb1.ScrollToCaret(); rtb1.Visible = true; } Application.DoEvents(); } } t += s + "\n"; } } } catch (Exception ex) { MessageBox.Show("Error:\n" + ex.Message); } } Status.Text = "State : Done"; rtb1.Text = t; rtb1.SelectionStart = rtb1.TextLength; rtb1.ScrollToCaret(); pb1.Value = 0; pauseToolStripMenuItem.Visible = false; }
public InterpCurve(IMEPackage _pcc, PropertyReader.Property p) { pcc = _pcc; Curves = new ObservableCollection <Curve>(); Name = pcc.getNameEntry(p.Name); curveType = (CurveType)Enum.Parse(typeof(CurveType), pcc.getNameEntry(p.Value.IntValue)); float InVal = 0f; CurveMode InterpMode = CurveMode.CIM_Linear; var points = PropertyReader.ReadStructArrayProp(pcc, PropertyReader.getPropOrNull(pcc, p.raw, 32, "Points")); switch (curveType) { case CurveType.InterpCurveQuat: throw new NotImplementedException($"InterpCurveQuat has not been implemented yet."); case CurveType.InterpCurveFloat: float OutVal = 0f; float ArriveTangent = 0f; float LeaveTangent = 0f; LinkedList <CurvePoint> vals = new LinkedList <CurvePoint>(); foreach (var point in points) { foreach (var prop in point) { switch (pcc.getNameEntry(prop.Name)) { case "InVal": InVal = BitConverter.ToSingle(prop.raw, 24); break; case "OutVal": OutVal = BitConverter.ToSingle(prop.raw, 24); break; case "ArriveTangent": ArriveTangent = BitConverter.ToSingle(prop.raw, 24); break; case "LeaveTangent": LeaveTangent = BitConverter.ToSingle(prop.raw, 24); break; case "InterpMode": InterpMode = (CurveMode)Enum.Parse(typeof(CurveMode), pcc.getNameEntry(prop.Value.IntValue)); break; default: break; } } vals.AddLast(new CurvePoint(InVal, OutVal, ArriveTangent, LeaveTangent, InterpMode)); } Curves.Add(new Curve("X", vals)); break; case CurveType.InterpCurveVector: Vector OutValVec = new Vector(0, 0, 0); Vector ArriveTangentVec = new Vector(0, 0, 0); Vector LeaveTangentVec = new Vector(0, 0, 0); LinkedList <CurvePoint> x = new LinkedList <CurvePoint>(); LinkedList <CurvePoint> y = new LinkedList <CurvePoint>(); LinkedList <CurvePoint> z = new LinkedList <CurvePoint>(); foreach (var point in points) { foreach (var prop in point) { switch (pcc.getNameEntry(prop.Name)) { case "InVal": InVal = BitConverter.ToSingle(prop.raw, 24); break; case "OutVal": OutValVec = GetVector(prop); break; case "ArriveTangent": ArriveTangentVec = GetVector(prop); break; case "LeaveTangent": LeaveTangentVec = GetVector(prop); break; case "InterpMode": InterpMode = (CurveMode)Enum.Parse(typeof(CurveMode), pcc.getNameEntry(prop.Value.IntValue)); break; default: break; } } x.AddLast(new CurvePoint(InVal, OutValVec.X, ArriveTangentVec.X, LeaveTangentVec.X, InterpMode)); y.AddLast(new CurvePoint(InVal, OutValVec.Y, ArriveTangentVec.Y, LeaveTangentVec.Y, InterpMode)); z.AddLast(new CurvePoint(InVal, OutValVec.Z, ArriveTangentVec.Z, LeaveTangentVec.Z, InterpMode)); } if (Name == "EulerTrack") { Curves.Add(new Curve("Roll", x)); Curves.Add(new Curve("Pitch", y)); Curves.Add(new Curve("Yaw", z)); } else { Curves.Add(new Curve("X", x)); Curves.Add(new Curve("Y", y)); Curves.Add(new Curve("Z", z)); } break; case CurveType.InterpCurveVector2D: throw new NotImplementedException($"InterpCurveVector2D has not been implemented yet."); case CurveType.InterpCurveTwoVectors: throw new NotImplementedException($"InterpCurveTwoVectors has not been implemented yet."); case CurveType.InterpCurveLinearColor: throw new NotImplementedException($"InterpCurveLinearColor has not been implemented yet."); default: break; } foreach (var curve in Curves) { curve.SharedValueChanged += Curve_SharedValueChanged; curve.ListModified += Curve_ListModified; } }