public AllureLifecycle AddAttachment(string name, AttachFormat type, byte[] bytesArray, string fileExtension = "") { var asString = Encoding.Default.GetString(bytesArray); switch (type) { case AttachFormat.Xml: return(AddAttachment(name, type, asString)); case AttachFormat.Json: return(AddAttachment(name, type, asString)); case AttachFormat.ImagePng: return(AddAttachment(name, "image/png", bytesArray, ".png")); case AttachFormat.Txt: return(AddAttachment(name, type, asString)); case AttachFormat.Html: return(AddAttachment(name, type, asString)); case AttachFormat.Video: throw new ArgumentException( $"You cant use \"{type}\" argument at this method. Try use method with FileInfo parameter."); default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } }
public AllureLifecycle AddAttachment(string name, AttachFormat type, FileInfo file) { string inside; using (var u = new StreamReader(file.OpenRead())) { inside = u.ReadToEnd(); } switch (type) { case AttachFormat.Xml: return(AddAttachment(name, AttachFormat.Xml, inside)); case AttachFormat.Json: return(AddAttachment(name, AttachFormat.Json, inside)); case AttachFormat.ImagePng: var bytes = Encoding.Default.GetBytes(inside); return(AddAttachment(name, "image/png", bytes, ".png")); case AttachFormat.Txt: return(AddAttachment(name, AttachFormat.Txt, inside)); case AttachFormat.Video: return(AddAttachment(name, "video/mp4", file.FullName)); case AttachFormat.Html: return(AddAttachment(name, AttachFormat.Html, inside)); default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } }
/// <summary> /// Writes a model hierarchy to stream and returns the contents /// </summary> /// <param name="format">Format of the file</param> /// <param name="NJFile">Whether to write an nj binary</param> /// <param name="model">The root model to write to the file</param> /// <param name="author">Author of the file</param> /// <param name="description">Description of the files contents</param> /// <param name="metadata">Other meta data</param> /// <param name="animFiles">Animation file paths</param> public static byte[] Write(AttachFormat format, bool NJFile, NJObject model, MetaData metaData) { using ExtendedMemoryStream stream = new(); EndianWriter writer = new(stream); uint imageBase = 0; if (NJFile) { writer.WriteUInt16(NJ); switch (format) { case AttachFormat.BASIC: writer.WriteUInt32(BM); break; case AttachFormat.CHUNK: writer.WriteUInt32(CM); break; default: throw new ArgumentException($"Attach format {format} not supported for NJ binaries"); } writer.WriteUInt32(0); // file length placeholder imageBase = ~(8u); } else { ulong header = 0; header = format switch { AttachFormat.BASIC => SA1MDLVer, AttachFormat.CHUNK => SA2MDLVer, AttachFormat.GC => SA2BMDLVer, AttachFormat.Buffer => BFMDLVer, _ => throw new ArgumentException($"Attach format {format} not supported for SAMDL files"), }; writer.WriteUInt64(header); writer.WriteUInt32(0x10); writer.WriteUInt32(0); // labels placeholder } Dictionary <string, uint> labels = new(); model.WriteHierarchy(writer, imageBase, false, format == AttachFormat.Buffer, labels); if (NJFile) { // replace size writer.Stream.Seek(4, SeekOrigin.Begin); writer.WriteUInt32((uint)writer.Stream.Length); writer.Stream.Seek(0, SeekOrigin.End); } else { metaData.Write(writer, labels); } return(stream.ToArray()); }
private ModelFile(AttachFormat format, NJObject model, Motion[] animations, MetaData metaData, bool nj) { Format = format; Model = model; Animations = new ReadOnlyCollection <Motion>(animations); MetaData = metaData; NJFile = nj; }
/// <summary> /// Writes a model as an NJA file /// </summary> /// <param name="outputPath">Path to write the file to (extension will be forced to .NJA)</param> /// <param name="DX">Whether the file is for SADX</param> /// <param name="model">Top level object to write</param> /// <param name="textures">Texture list</param> public static void WriteNJA(string outputPath, bool DX, NJObject model, string[] textures = null) { NJObject[] objects = model.GetObjects(); Attach[] attaches = objects.Select(x => x.Attach).Distinct().ToArray(); AttachFormat fmt = AttachFormat.Buffer; if (attaches.Length > 0) { fmt = attaches[0].Format; foreach (Attach atc in attaches) { if (fmt != atc.Format) { throw new InvalidCastException("Not all attaches are of the same type!"); } } if (fmt == AttachFormat.Buffer) { throw new InvalidCastException("All attaches are of buffer format! Can't decide what format to write"); } } outputPath = Path.ChangeExtension(outputPath, ".NJA"); using TextWriter writer = File.CreateText(outputPath); List <string> labels = new(); foreach (var atc in attaches) { atc.WriteNJA(writer, DX, labels, textures); } writer.WriteLine("OBJECT_START"); writer.WriteLine(); foreach (NJObject obj in objects.Reverse()) { obj.WriteNJA(writer, labels); } writer.WriteLine("OBJECT_END"); writer.WriteLine(); writer.WriteLine(); writer.WriteLine("DEFAULT_START"); writer.WriteLine(); writer.WriteLine("#ifndef DEFAULT_OBJECT_NAME"); writer.Write("#define DEFAULT_OBJECT_NAME "); writer.WriteLine(model.Name); writer.WriteLine("#endif"); writer.WriteLine(); writer.WriteLine("DEFAULT_END"); writer.WriteLine(); }
private void Format_SelectionChanged(object sender, SelectionChangedEventArgs e) { ComboBox s = (ComboBox)sender; AttachFormat newFormat = (AttachFormat)s.SelectedIndex; if (_format == newFormat) { return; } _format = newFormat; RefreshFileExtension(); }
public WndSave(Mode appMode, string lastFilePath, AttachFormat lastFormat, bool lastNJ, bool lastOptimized) { if (appMode is not Mode.Level and not Mode.Model) { throw new NotImplementedException("Only model and level files can be saved atm"); } InitializeComponent(); _sfd = new(); _sfd.OverwritePrompt = false; FilepathControl.Dialog = _sfd; if (!string.IsNullOrWhiteSpace(lastFilePath)) { _sfd.InitialDirectory = Path.GetDirectoryName(lastFilePath); Filepath = lastFilePath; _format = lastFormat; FormatControl.SelectedIndex = (int)lastFormat; NJFormatControl.IsChecked = lastNJ; OptimizeControl.IsChecked = lastOptimized; } else { _format = AttachFormat.Buffer; } _appMode = appMode; switch (appMode) { case Mode.Model: Title = "Save Model File"; break; case Mode.Level: Title = "Save Level File"; break; case Mode.ProjectSA1: case Mode.ProjectSA2: case Mode.None: default: break; } RefreshFileExtension(); }
public AllureLifecycle AddAttachment(string name, AttachFormat type, string content, string fileExtension = "") { switch (type) { case AttachFormat.ImagePng: case AttachFormat.Video: throw new ArgumentException( $"You cant use \"{type}\" argument at this method. Try use method with FileInfo or with bytes array parameter."); case AttachFormat.Xml: content = XDocument.Parse(content).ToString(); if (string.IsNullOrEmpty(fileExtension)) { fileExtension = ".xml"; } return(AddAttachment(name, "text/xml", Encoding.UTF8.GetBytes(content), fileExtension)); case AttachFormat.Json: var obj = JsonConvert.DeserializeObject(content); content = JsonConvert.SerializeObject(obj, Formatting.Indented); if (string.IsNullOrEmpty(fileExtension)) { fileExtension = ".json"; } return(AddAttachment(name, "application/json", Encoding.UTF8.GetBytes(content), fileExtension)); case AttachFormat.Txt: if (string.IsNullOrEmpty(fileExtension)) { fileExtension = ".txt"; } return(AddAttachment(name, "text/txt", Encoding.UTF8.GetBytes(content), fileExtension)); case AttachFormat.Html: content = WebUtility.HtmlEncode(content); if (string.IsNullOrEmpty(fileExtension)) { fileExtension = ".html"; } return(AddAttachment(name, "text/html", Encoding.UTF8.GetBytes(content), fileExtension)); default: throw new ArgumentException($"You cant use \"{type}\" argument at this method."); } }
/// <summary> /// Reads an action from a byte array /// </summary> /// <param name="source">Byte source</param> /// <param name="address">Address at which the action is located</param> /// <param name="imagebase">Image base for all addresses</param> /// <param name="format">Attach format</param> /// <param name="DX">Whether the file is for sadx</param> /// <param name="labels">C struct labels</param> /// <param name="attaches">Attaches that have already been read</param> /// <returns></returns> public static Action Read(byte[] source, uint address, uint imagebase, AttachFormat format, bool DX, Dictionary <uint, string> labels, Dictionary <uint, Attach> attaches) { uint mdlAddress = source.ToUInt32(address); if (mdlAddress == 0) { throw new FormatException($"Action at {address:X8} does not have a model!"); } mdlAddress -= imagebase; NJObject mdl = NJObject.Read(source, mdlAddress, imagebase, format, DX, labels, attaches); uint aniAddress = source.ToUInt32(address + 4); if (aniAddress == 0) { throw new FormatException($"Action at {address:X8} does not have a model!"); } aniAddress -= imagebase; Motion mtn = Motion.Read(source, ref aniAddress, imagebase, (uint)mdl.Count(), labels); return(new(mdl, mtn)); }
/// <summary> /// Writes a model hierarchy (without meta data) to a stream and returns the contents /// </summary> /// <param name="format">Format of the file</param> /// <param name="NJFile">Whether to write an nj binary</param> /// <param name="model">The root model to write to the file</param> public static byte[] Write(AttachFormat format, bool NJFile, NJObject model) => Write(format, NJFile, model, new MetaData());
/// <summary> /// Writes a model hierarchy to a binary file /// </summary> /// <param name="format">Format of the file</param> /// <param name="outputPath">The path of the file</param> /// <param name="NJ">Whether to write an nj binary</param> /// <param name="model">The root model to write to the file</param> /// <param name="author">Author of the file</param> /// <param name="description">Description of the files contents</param> /// <param name="metadata">Other meta data</param> /// <param name="animFiles">Animation file paths</param> public static void WriteToFile(string outputPath, AttachFormat format, bool NJ, NJObject model, MetaData metaData) { File.WriteAllBytes(outputPath, Write(format, NJ, model, metaData)); }
/// <summary> /// Writes a model hierarchy (without meta data) to a binary file /// </summary> /// <param name="format">Format of the file</param> /// <param name="outputPath">The path of the file</param> /// <param name="NJ">Whether to write an nj binary</param> /// <param name="model">The root model to write to the file</param> public static void WriteToFile(string outputPath, AttachFormat format, bool NJ, NJObject model) => WriteToFile(outputPath, format, NJ, model, new MetaData());
/// <summary> /// Reads a geometry animation from a byte array /// </summary> /// <param name="source">Byte source</param> /// <param name="address">Address at which the geometry animation is located</param> /// <param name="imageBase">Image base for all addresses</param> /// <param name="format">Attach format</param> /// <param name="DX">Whether the animation is for sadx</param> /// <param name="labels">C struct labels</param> /// <param name="models">Models that have already been read</param> /// <param name="attaches">Attaches that have already been read</param> /// <returns></returns> public static LandEntryMotion Read(byte[] source, uint address, uint imageBase, AttachFormat format, bool DX, Dictionary <uint, string> labels, Dictionary <uint, Attach> attaches) { float frame = source.ToSingle(address); float step = source.ToSingle(address + 4); float maxFrame = source.ToSingle(address + 8); uint modelAddress = source.ToUInt32(address + 0xC) - imageBase; NJObject model = NJObject.Read(source, modelAddress, imageBase, format, DX, labels, attaches); uint motionAddress = source.ToUInt32(address + 0x10) - imageBase; Action action = Action.Read(source, motionAddress, imageBase, format, DX, labels, attaches); uint texListPtr = source.ToUInt32(address + 0x14); return(new LandEntryMotion(frame, step, maxFrame, model, action, texListPtr)); }
/// <summary> /// Reads a landtable from a byte array /// </summary> /// <param name="source"></param> /// <param name="address"></param> /// <param name="imageBase"></param> /// <param name="format"></param> /// <param name="labels"></param> /// <returns></returns> public static LandTable Read(byte[] source, uint address, uint imageBase, LandtableFormat format, Dictionary <uint, string> labels) { string name = labels.ContainsKey(address) ? labels[address] : "landtable_" + address.ToString("X8"); float radius; LandtableAttributes attribs = 0; string identifier = GenerateIdentifier(); List <LandEntry> geometry = new(); string geomName; List <LandEntryMotion> anim = new(); string animName; Dictionary <uint, Attach> attaches = new(); string texName = ""; uint texListPtr; uint tmpaddr; ushort geoCount = source.ToUInt16(address); switch (format) { case LandtableFormat.SA1: case LandtableFormat.SADX: short anicnt = source.ToInt16(address + 2); attribs = (LandtableAttributes)source.ToUInt32(address + 4); radius = source.ToSingle(address + 8); tmpaddr = source.ToUInt32(address + 0xC); if (tmpaddr != 0) { tmpaddr -= imageBase; geomName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "collist_" + tmpaddr.ToString("X8"); for (int i = 0; i < geoCount; i++) { geometry.Add(LandEntry.Read(source, tmpaddr, imageBase, AttachFormat.BASIC, format, labels, attaches)); tmpaddr += 0x24; } } else { geomName = "collist_" + identifier; } tmpaddr = source.ToUInt32(address + 0x10); if (tmpaddr != 0) { tmpaddr -= imageBase; animName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "animlist_" + tmpaddr.ToString("X8"); for (int i = 0; i < anicnt; i++) { anim.Add(LandEntryMotion.Read(source, tmpaddr, imageBase, AttachFormat.BASIC, format == LandtableFormat.SADX, labels, attaches)); tmpaddr += LandEntryMotion.Size; } } else { animName = "animlist_" + identifier; } tmpaddr = source.ToUInt32(address + 0x14); if (tmpaddr != 0) { tmpaddr -= imageBase; texName = source.GetCString(tmpaddr, Encoding.ASCII); } texListPtr = source.ToUInt32(address + 0x18); break; case LandtableFormat.SA2: case LandtableFormat.SA2B: AttachFormat atcFmt = format == LandtableFormat.SA2 ? AttachFormat.CHUNK : AttachFormat.GC; ushort visualCount = source.ToUInt16(address + 2); radius = source.ToSingle(address + 0xC); tmpaddr = source.ToUInt32(address + 0x10); if (tmpaddr != 0) { tmpaddr -= imageBase; geomName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "collist_" + tmpaddr.ToString("X8"); for (int i = 0; i < geoCount; i++) { geometry.Add(LandEntry.Read(source, tmpaddr, imageBase, i >= visualCount ? AttachFormat.BASIC : atcFmt, format, labels, attaches)); tmpaddr += 0x20; } } else { geomName = "collist_" + identifier; } animName = "animlist_" + identifier; tmpaddr = source.ToUInt32(address + 0x18); if (tmpaddr != 0) { tmpaddr -= imageBase; texName = source.GetCString(tmpaddr, Encoding.ASCII); } texListPtr = source.ToUInt32(address + 0x1C); break; default: throw new InvalidDataException("Landtable format not valid"); } return(new(geometry, anim, format) { Name = name, DrawDistance = radius, Attributes = attribs, GeoName = geomName, GeoAnimName = animName, TexListPtr = texListPtr, TextureFileName = texName }); }