Beispiel #1
0
        /// <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());
        }
Beispiel #2
0
 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;
 }
Beispiel #3
0
        /// <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();
        }
Beispiel #4
0
 /// <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());
Beispiel #5
0
 /// <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));
 }
Beispiel #6
0
 /// <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());
Beispiel #7
0
        /// <summary>
        /// Reads a model file from a byte source and its file path (for relative located files) <br/>
        /// Returns null if the modelfile couldnt be read
        /// </summary>
        /// <param name="source">Source of the file</param>
        /// <param name="filename">File path of the read file. Used if the model uses more files outside of the byte source</param>
        /// <returns></returns>
        public static ModelFile Read(byte[] source, string filename = null)
        {
            PushBigEndian(false);

            AttachFormat?format = null;
            NJObject     model;
            Dictionary <uint, Attach> attaches   = new();
            List <Motion>             Animations = new();
            MetaData metaData = new();
            bool     nj       = false;

            ushort NJMagic = source.ToUInt16(0);

            if (NJMagic == NJ || NJMagic == GJ)
            {
                NJMagic = source.ToUInt16(0x2);

                uint ninjaOffset = 8u;
                bool fileEndian  = source.CheckBigEndianInt32(0x8u);
texlistRetry:

                switch (NJMagic)
                {
                case BM:
                    format = AttachFormat.BASIC;
                    break;

                case CM:
                    format = AttachFormat.CHUNK;
                    break;

                case TL:
                    uint POF0Offset    = source.ToUInt32(0x4) + 0x8;
                    uint POF0Size      = source.ToUInt32(POF0Offset + 0x4);
                    uint texListOffset = POF0Offset + POF0Size + 0x8;
                    ninjaOffset = texListOffset + 0x8;

                    NJMagic = source.ToUInt16(texListOffset + 0x2);

                    PushBigEndian(fileEndian);

                    //Get Texture Listings for if that is ever implemented
                    uint          texCount  = source.ToUInt32(0xC);
                    uint          texOffset = 0;
                    List <string> texNames  = new();

                    for (int i = 0; i < texCount; i++)
                    {
                        uint textAddress = source.ToUInt32(texOffset + 0x10) + 0x8;
                        texNames.Add(source.GetCString(textAddress, System.Text.Encoding.UTF8));
                        texOffset += 0xC;
                    }
                    PopEndian();

                    goto texlistRetry;
                }

                PushBigEndian(fileEndian);

                // the addresses start 8 bytes ahead, and since we always subtract the image base from the addresses,
                // we have to add them this time, so we invert the 8 to add 8 by subtracting
                model = NJObject.Read(source, ninjaOffset, ~(ninjaOffset - 1), format.Value, false, new(), attaches);
                nj    = true;
            }
            else
            {
                // checking for mdl format
                ulong header8 = source.ToUInt64(0) & HeaderMask;
                switch (header8)
                {
                case SA1MDL:
                    format = AttachFormat.BASIC;
                    break;

                case SA2MDL:
                    format = AttachFormat.CHUNK;
                    break;

                case SA2BMDL:
                    format = AttachFormat.GC;
                    break;

                case BFMDL:
                    format = AttachFormat.Buffer;
                    break;

                default:
                    return(null);
                    //throw new InvalidDataException("File is not a valid model file");
                }

                // checking the version
                byte version = source[7];
                if (version > CurrentVersion)
                {
                    PopEndian();
                    return(null);
                    //throw new FormatException("Not a valid SA1LVL/SA2LVL file.");
                }

                metaData = MetaData.Read(source, version, true);
                Dictionary <uint, string> labels = new(metaData.Labels);

                model = NJObject.Read(source, source.ToUInt32(8), 0, format.Value, false, labels, attaches);

                // reading animations
                if (filename != null)
                {
                    string path = Path.GetDirectoryName(filename);
                    try
                    {
                        foreach (string item in metaData.AnimFiles)
                        {
                            Animations.Add(Motion.ReadFile(Path.Combine(path, item), model.CountAnimated()));
                        }
                    }
                    catch
                    {
                        Animations.Clear();
                    }
                }
            }

            PopEndian();
            return(new(format.Value, model, Animations.ToArray(), metaData, nj));
        }