Esempio n. 1
0
        public byte[] GetBytes(uint imageBase, uint modelptr, LandTableFormat format)
        {
            List <byte> result = new List <byte>();

            result.AddRange(Bounds.GetBytes());
            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                result.AddRange(ByteConverter.GetBytes(WidthY));
                result.AddRange(ByteConverter.GetBytes(WidthZ));
                result.AddRange(ByteConverter.GetBytes(modelptr));
                result.AddRange(ByteConverter.GetBytes(BlockBits));
                break;

            case LandTableFormat.SA2:
            case LandTableFormat.SA2B:
                result.AddRange(ByteConverter.GetBytes(modelptr));
                result.AddRange(ByteConverter.GetBytes(WidthZ));
                result.AddRange(ByteConverter.GetBytes(BlockBits));
                break;
            }
            result.AddRange(ByteConverter.GetBytes(Flags));
            return(result.ToArray());
        }
Esempio n. 2
0
        public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, Dictionary <int, Attach> attaches)
        {
            ModelFormat mfmt = 0;

            switch (format)
            {
            case LandTableFormat.SA1:
                mfmt = ModelFormat.Basic;
                break;

            case LandTableFormat.SADX:
                mfmt = ModelFormat.BasicDX;
                break;

            case LandTableFormat.SA2:
                mfmt = ModelFormat.Chunk;
                break;
            }
            AnimationFrame = ByteConverter.ToSingle(file, address);
            AnimationSpeed = ByteConverter.ToSingle(file, address + 4);
            MaxFrame       = ByteConverter.ToSingle(file, address + 8);
            Model          = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address + 0xC) - imageBase), imageBase, mfmt, labels, attaches);
            int actionaddr = (int)(ByteConverter.ToUInt32(file, address + 0x10) - imageBase);

            TexlistPointer = ByteConverter.ToUInt32(file, address + 0x14);
            NJS_ACTION action = new NJS_ACTION(file, actionaddr, imageBase, mfmt, labels, attaches);

            Animation = action.Animation;
        }
Esempio n. 3
0
        public string ToStruct(LandTableFormat format)
        {
            StringBuilder result = new StringBuilder("{ ");

            result.Append(Bounds.ToStruct());
            result.Append(", ");
            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                result.Append(WidthY.ToC());
                result.Append(", ");
                result.Append(WidthZ.ToC());
                result.Append(", ");
                result.Append(Model != null ? "&" + Model.Name : "NULL");
                result.Append(", ");
                result.AppendFormat(BlockBits.ToCHex());
                break;

            case LandTableFormat.SA2:
                result.Append(Model != null ? "&" + Model.Name : "NULL");
                result.Append(", ");
                result.Append(WidthZ.ToC());
                result.Append(", ");
                result.Append(BlockBits.ToCHex());
                break;
            }
            result.Append(", ");
            result.AppendFormat(Flags.ToCHex());
            result.Append(" }");
            return(result.ToString());
        }
Esempio n. 4
0
        public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, Dictionary <int, Attach> attaches)
        {
            ModelFormat mfmt = 0;

            switch (format)
            {
            case LandTableFormat.SA1:
                mfmt = ModelFormat.Basic;
                break;

            case LandTableFormat.SADX:
                mfmt = ModelFormat.BasicDX;
                break;

            case LandTableFormat.SA2:
                mfmt = ModelFormat.Chunk;
                break;
            }
            Unknown1  = ByteConverter.ToInt32(file, address);
            Unknown2  = ByteConverter.ToSingle(file, address + 4);
            Unknown3  = ByteConverter.ToSingle(file, address + 8);
            Model     = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address + 0xC) - imageBase), imageBase, mfmt, labels, attaches);
            Animation = NJS_MOTION.ReadHeader(file, (int)(ByteConverter.ToUInt32(file, address + 0x10) - imageBase), imageBase, mfmt, labels, attaches);
            Unknown4  = ByteConverter.ToInt32(file, address + 0x14);
        }
Esempio n. 5
0
 public string ToStructVariables(LandTableFormat format, List <string> labels, string[] textures = null)
 {
     using (StringWriter sw = new StringWriter())
     {
         ToStructVariables(sw, format, labels, textures);
         return(sw.ToString());
     }
 }
Esempio n. 6
0
        private void WriteStructMetadata(StreamWriter sw, bool level, LandTableFormat fmt, string[] texnames = null)
        {
            sw.Write("/* Sonic Adventure ");
            switch (fmt)
            {
            case LandTableFormat.SA1:
                sw.Write("1");
                break;

            case LandTableFormat.SADX:
                sw.Write("DX");
                break;

            case LandTableFormat.SA2:
                sw.Write("2");
                break;

            case LandTableFormat.SA2B:
                sw.Write("2: Battle");
                break;
            }
            if (level)
            {
                sw.WriteLine(" LandTable");
            }
            else
            {
                sw.WriteLine(" Model");
            }
            sw.WriteLine(" * ");
            sw.WriteLine(" * Generated by SALVL");
            sw.WriteLine(" * ");
            if (!string.IsNullOrEmpty(LevelData.geo.Description))
            {
                sw.Write(" * Description: ");
                sw.WriteLine(LevelData.geo.Description);
                sw.WriteLine(" * ");
            }
            if (!string.IsNullOrEmpty(LevelData.geo.Author))
            {
                sw.Write(" * Author: ");
                sw.WriteLine(LevelData.geo.Author);
                sw.WriteLine(" * ");
            }
            sw.WriteLine(" */");
            sw.WriteLine();
            if (texnames != null)
            {
                sw.Write("enum {0}TexName", LevelData.leveltexs);
                sw.WriteLine();
                sw.WriteLine("{");
                sw.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", texnames));
                sw.WriteLine("};");
                sw.WriteLine();
            }
        }
Esempio n. 7
0
        public COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, bool?forceBasic)
        {
            Bounds = new BoundingSphere(file, address);
            ModelFormat mfmt = 0;

            switch (format)
            {
            case LandTableFormat.SA1:
                mfmt = ModelFormat.Basic;
                break;

            case LandTableFormat.SADX:
                mfmt = ModelFormat.BasicDX;
                break;

            case LandTableFormat.SA2:
                if (forceBasic.HasValue && forceBasic.Value)
                {
                    mfmt = ModelFormat.Basic;
                }
                else
                {
                    mfmt = ModelFormat.Chunk;
                }
                break;
            }
            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                Unknown1 = ByteConverter.ToInt32(file, address + 0x10);
                Unknown2 = ByteConverter.ToInt32(file, address + 0x14);
                uint tmpaddr = ByteConverter.ToUInt32(file, address + 0x18) - imageBase;
                Model    = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels);
                Unknown3 = ByteConverter.ToInt32(file, address + 0x1C);
                Flags    = ByteConverter.ToInt32(file, address + 0x20);
                break;

            case LandTableFormat.SA2:
                Flags = ByteConverter.ToInt32(file, address + 0x1C);
                if (!forceBasic.HasValue)
                {
                    mfmt = Flags < 0 ? ModelFormat.Chunk : ModelFormat.Basic;
                }
                tmpaddr  = ByteConverter.ToUInt32(file, address + 0x10) - imageBase;
                Model    = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels);
                Unknown2 = ByteConverter.ToInt32(file, address + 0x14);
                Unknown3 = ByteConverter.ToInt32(file, address + 0x18);
                break;
            }
        }
Esempio n. 8
0
 private void button1_Click(object sender, EventArgs e)
 {
     LandTableFormat format = (LandTableFormat)comboBox2.SelectedIndex;
     LandTableFormat outfmt = format;
     if (format == LandTableFormat.SADX) outfmt = LandTableFormat.SA1;
     ByteConverter.BigEndian = checkBox1.Checked;
     using (SaveFileDialog sd = new SaveFileDialog() { DefaultExt = outfmt.ToString().ToLowerInvariant() + "lvl", Filter = outfmt.ToString().ToUpperInvariant() + "LVL Files|*." + outfmt.ToString().ToLowerInvariant() + "lvl|All Files|*.*" })
         if (sd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
         {
             new LandTable(file, (int)NumericUpDown1.Value, (uint)numericUpDown2.Value, format) { Author = author.Text, Description = description.Text }.SaveToFile(sd.FileName, outfmt);
             Settings.Author = author.Text;
             Settings.Save();
         }
 }
Esempio n. 9
0
        public static int Size(LandTableFormat format)
        {
            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                return(0x24);

            case LandTableFormat.SA2:
                return(0x20);

            default:
                throw new ArgumentOutOfRangeException("format");
            }
        }
Esempio n. 10
0
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LandTableFormat outfmt = LevelData.geo.Format;

            if (outfmt == LandTableFormat.SADX)
            {
                outfmt = LandTableFormat.SA1;
            }
            using (SaveFileDialog a = new SaveFileDialog()
            {
                DefaultExt = outfmt.ToString().ToLowerInvariant() + "lvl",
                Filter = outfmt.ToString().ToUpperInvariant() + "LVL Files|*." + outfmt.ToString().ToLowerInvariant() + "lvl|All Files|*.*"
            })
                if (a.ShowDialog(this) == DialogResult.OK)
                {
                    LevelData.geo.SaveToFile(a.FileName, outfmt);
                }
        }
Esempio n. 11
0
 public COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary<int, string> labels, bool? forceBasic)
 {
     Bounds = new BoundingSphere(file, address);
     ModelFormat mfmt = 0;
     switch (format)
     {
         case LandTableFormat.SA1:
             mfmt = ModelFormat.Basic;
             break;
         case LandTableFormat.SADX:
             mfmt = ModelFormat.BasicDX;
             break;
         case LandTableFormat.SA2:
             if (forceBasic.HasValue && forceBasic.Value)
                 mfmt = ModelFormat.Basic;
             else
                 mfmt = ModelFormat.Chunk;
             break;
     }
     switch (format)
     {
         case LandTableFormat.SA1:
         case LandTableFormat.SADX:
             Unknown1 = ByteConverter.ToInt32(file, address + 0x10);
             Unknown2 = ByteConverter.ToInt32(file, address + 0x14);
             uint tmpaddr = ByteConverter.ToUInt32(file, address + 0x18) - imageBase;
             Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels);
             Unknown3 = ByteConverter.ToInt32(file, address + 0x1C);
             Flags = ByteConverter.ToInt32(file, address + 0x20);
             break;
         case LandTableFormat.SA2:
             Flags = ByteConverter.ToInt32(file, address + 0x1C);
             if (!forceBasic.HasValue)
                 mfmt = Flags < 0 ? ModelFormat.Chunk : ModelFormat.Basic;
             tmpaddr = ByteConverter.ToUInt32(file, address + 0x10) - imageBase;
             Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels);
             Unknown2 = ByteConverter.ToInt32(file, address + 0x14);
             Unknown3 = ByteConverter.ToInt32(file, address + 0x18);
             break;
     }
 }
Esempio n. 12
0
 public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary<int, string> labels)
 {
     ModelFormat mfmt = 0;
     switch (format)
     {
         case LandTableFormat.SA1:
             mfmt = ModelFormat.Basic;
             break;
         case LandTableFormat.SADX:
             mfmt = ModelFormat.BasicDX;
             break;
         case LandTableFormat.SA2:
             mfmt = ModelFormat.Chunk;
             break;
     }
     Unknown1 = ByteConverter.ToInt32(file, address);
     Unknown2 = ByteConverter.ToSingle(file, address + 4);
     Unknown3 = ByteConverter.ToSingle(file, address + 8);
     Model = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address + 0xC) - imageBase), imageBase, mfmt);
     Animation = Animation.ReadHeader(file, (int)(ByteConverter.ToUInt32(file, address + 0x10) - imageBase), imageBase, mfmt, labels);
     Unknown4 = ByteConverter.ToInt32(file, address + 0x14);
 }
Esempio n. 13
0
        public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, Dictionary <int, Attach> attaches)
        {
            ModelFormat mfmt = 0;

            switch (format)
            {
            case LandTableFormat.SA1:
                mfmt = ModelFormat.Basic;
                break;

            case LandTableFormat.SADX:
                mfmt = ModelFormat.BasicDX;
                break;

            case LandTableFormat.SA2:
                mfmt = ModelFormat.Chunk;
                break;
            }
            Unknown1 = ByteConverter.ToInt32(file, address);
            Unknown2 = ByteConverter.ToSingle(file, address + 4);
            Unknown3 = ByteConverter.ToSingle(file, address + 8);
            Model    = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address + 0xC) - imageBase), imageBase, mfmt, labels, attaches);
            int actionaddr = (int)(ByteConverter.ToUInt32(file, address + 0x10) - imageBase);
            int motionaddr = (int)(ByteConverter.ToUInt32(file, actionaddr + 4) - imageBase);

            Animation = NJS_MOTION.ReadDirect(file, Model.CountAnimated(), motionaddr, imageBase, labels, attaches);
            Unknown4  = ByteConverter.ToInt32(file, address + 0x14);
            if (labels.ContainsKey(actionaddr))
            {
                ActionName = labels[actionaddr];
            }
            else
            {
                NJS_ACTION action = new NJS_ACTION(file, actionaddr, imageBase, mfmt, labels, attaches);
                ActionName = action.Name;
                labels.Add(actionaddr + (int)imageBase, ActionName);
            }
        }
Esempio n. 14
0
 public byte[] GetBytes(uint imageBase, LandTableFormat format, out uint address)
 {
     return GetBytes(imageBase, format, new Dictionary<string, uint>(), out address);
 }
Esempio n. 15
0
        public void SaveToFile(string filename, LandTableFormat format)
        {
            bool be = ByteConverter.BigEndian;

            ByteConverter.BigEndian = false;
            if (format == LandTableFormat.SADX)
            {
                format = LandTableFormat.SA1;
            }
            List <byte> file = new List <byte>();
            ulong       magic;

            switch (format)
            {
            case LandTableFormat.SA1:
                magic = SA1LVLVer;
                break;

            case LandTableFormat.SA2:
                magic = SA2LVLVer;
                break;

            default:
                throw new ArgumentException("Cannot save " + format + " format levels to file!", "format");
            }
            file.AddRange(ByteConverter.GetBytes(magic));
            Dictionary <string, uint> labels = new Dictionary <string, uint>();

            byte[] lvl = GetBytes(0x10, format, labels, out uint addr);
            file.AddRange(ByteConverter.GetBytes(addr + 0x10));
            file.AddRange(ByteConverter.GetBytes(lvl.Length + 0x10));
            file.AddRange(lvl);
            if (labels.Count > 0)
            {
                List <byte> chunk    = new List <byte>(labels.Count * 8);
                int         straddr  = (labels.Count * 8) + 8;
                List <byte> strbytes = new List <byte>();
                foreach (KeyValuePair <string, uint> label in labels)
                {
                    chunk.AddRange(ByteConverter.GetBytes(label.Value));
                    chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count));
                    strbytes.AddRange(Encoding.UTF8.GetBytes(label.Key));
                    strbytes.Add(0);
                    strbytes.Align(4);
                }
                chunk.AddRange(ByteConverter.GetBytes(-1L));
                chunk.AddRange(strbytes);
                file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Label));
                file.AddRange(ByteConverter.GetBytes(chunk.Count));
                file.AddRange(chunk);
            }
            if (!string.IsNullOrEmpty(Author))
            {
                List <byte> chunk = new List <byte>(Author.Length + 1);
                chunk.AddRange(Encoding.UTF8.GetBytes(Author));
                chunk.Add(0);
                chunk.Align(4);
                file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Author));
                file.AddRange(ByteConverter.GetBytes(chunk.Count));
                file.AddRange(chunk);
            }
            if (!string.IsNullOrEmpty(Description))
            {
                List <byte> chunk = new List <byte>(Description.Length + 1);
                chunk.AddRange(Encoding.UTF8.GetBytes(Description));
                chunk.Add(0);
                chunk.Align(4);
                file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Description));
                file.AddRange(ByteConverter.GetBytes(chunk.Count));
                file.AddRange(chunk);
            }
            if (!string.IsNullOrEmpty(Tool))
            {
                List <byte> chunk = new List <byte>(Tool.Length + 1);
                chunk.AddRange(Encoding.UTF8.GetBytes(Tool));
                chunk.Add(0);
                chunk.Align(4);
                file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Tool));
                file.AddRange(ByteConverter.GetBytes(chunk.Count));
                file.AddRange(chunk);
            }
            foreach (KeyValuePair <uint, byte[]> item in Metadata)
            {
                file.AddRange(ByteConverter.GetBytes(item.Key));
                file.AddRange(ByteConverter.GetBytes(item.Value.Length));
                file.AddRange(item.Value);
            }
            file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.End));
            file.AddRange(new byte[4]);
            File.WriteAllBytes(filename, file.ToArray());
            ByteConverter.BigEndian = be;
        }
Esempio n. 16
0
        public void ToStructVariables(TextWriter writer, LandTableFormat format, List <string> labels, string[] textures = null)
        {
            List <COL> cnk = new List <COL>();
            List <COL> bas = new List <COL>();

            foreach (COL item in COL)
            {
                if (item.Model.Attach is BasicAttach)
                {
                    bas.Add(item);
                }
                else
                {
                    cnk.Add(item);
                }
            }
            COL.Clear();
            COL.AddRange(cnk);
            COL.AddRange(bas);
            for (int i = 0; i < COL.Count; i++)
            {
                if (!labels.Contains(COL[i].Model.Name))
                {
                    labels.Add(COL[i].Model.Name);
                    COL[i].Model.ToStructVariables(writer, format == LandTableFormat.SADX, labels, textures);
                }
            }
            for (int i = 0; i < Anim.Count; i++)
            {
                string aniid = Anim[i].Animation.Name.MakeIdentifier();
                if (!labels.Contains(Anim[i].Model.Name))
                {
                    labels.Add(Anim[i].Model.Name);
                    Anim[i].Model.ToStructVariables(writer, format == LandTableFormat.SADX, labels, textures);
                }
                if (!labels.Contains(aniid))
                {
                    labels.Add(aniid);
                    Anim[i].Animation.ToStructVariables(writer);
                    writer.Write("NJS_ACTION action_");
                    writer.Write(aniid);
                    writer.Write(" = { &");
                    writer.Write(Anim[i].Model.Name);
                    writer.Write(", &");
                    writer.Write(aniid);
                    writer.WriteLine(" };");
                    writer.WriteLine();
                }
            }
            if (!labels.Contains(COLName))
            {
                labels.Add(COLName);
                writer.Write("COL ");
                writer.Write(COLName);
                writer.WriteLine("[] = {");
                List <string> lines = new List <string>(COL.Count);
                foreach (COL item in COL)
                {
                    lines.Add(item.ToStruct(format));
                }
                writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", lines.ToArray()));
                writer.WriteLine("};");
                writer.WriteLine();
            }
            if (Anim.Count > 0 && !labels.Contains(AnimName))
            {
                labels.Add(AnimName);
                writer.Write("GeoAnimData ");
                writer.Write(AnimName);
                writer.WriteLine("[] = {");
                List <string> lines = new List <string>(Anim.Count);
                foreach (GeoAnimData item in Anim)
                {
                    lines.Add(item.ToStruct());
                }
                writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", lines.ToArray()));
                writer.WriteLine("};");
                writer.WriteLine();
            }
            writer.Write("LandTable ");
            writer.Write(Name);
            writer.Write(" = { LengthOfArray<int16_t>(");
            writer.Write(COLName);
            writer.Write("), ");
            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                writer.Write(Anim.Count > 0 ? "LengthOfArray<int16_t>(" + AnimName + ")" : "0");
                writer.Write(", ");
                writer.Write(Flags.ToCHex());
                writer.Write(", ");
                writer.Write(Unknown1.ToC());
                writer.Write(", ");
                writer.Write(COLName);
                writer.Write(", ");
                writer.Write(Anim.Count > 0 ? AnimName : "NULL");
                writer.Write(", ");
                writer.Write(TextureFileName.ToC());
                writer.Write(", (NJS_TEXLIST *)");
                writer.Write(TextureList.ToCHex());
                writer.Write(", ");
                writer.Write(Unknown2.ToCHex());
                writer.Write(", ");
                writer.Write(Unknown3.ToCHex());
                break;

            case LandTableFormat.SA2:
                writer.Write(cnk.Count);
                writer.Write(", 0, 0, 0, 0, ");
                writer.Write(Unknown1.ToC());
                writer.Write(", ");
                writer.Write(COLName);
                writer.Write(", ");
                writer.Write(Anim.Count > 0 ? AnimName : "NULL");
                writer.Write(", ");
                writer.Write(TextureFileName.ToC());
                writer.Write(", (NJS_TEXLIST *)");
                writer.Write(TextureList.ToCHex());
                break;
            }
            writer.WriteLine(" };");
        }
Esempio n. 17
0
 public byte[] GetBytes(uint imageBase, LandTableFormat format, out uint address)
 {
     return(GetBytes(imageBase, format, new Dictionary <string, uint>(), out address));
 }
Esempio n. 18
0
        static void Main(string[] args)
        {
            string[] arguments = Environment.GetCommandLineArgs();
            Game     game;
            string   filename;
            string   dir       = Environment.CurrentDirectory;
            bool     bigendian = false;

            if (args.Length == 0)
            {
                Console.WriteLine("USplit is a tool that lets you extract any data supported by SA Tools from any binary file.");
                Console.WriteLine("Usage: usplit <GAME> <FILENAME> <KEY> <TYPE> <ADDRESS> <PARAMETER1> <PARAMETER2> [-name <NAME>]\n");
                Console.WriteLine("Argument description:");
                Console.WriteLine("<GAME>: SA1, SADX, SA2, SA2B. Add '_b' (e.g. SADX_b) to switch to Big Endian.\n");
                Console.WriteLine("<FILENAME>: The name of the binary file, e.g. sonic.exe.\n");
                Console.WriteLine("<KEY>: Binary key, e.g. 400000 for sonic.exe or C900000 for SA1 STG file.\n");
                Console.WriteLine("<TYPE>: One of the following:\n" +
                                  "binary <length> [hex],\nlandtable, model, basicmodel, basicdxmodel, chunkmodel, gcmodel, action, animation <NJS_OBJECT address> [shortrot],\n" +
                                  "objlist, startpos, texlist, leveltexlist, triallevellist, bosslevellist, fieldstartpos, soundlist, soundtestlist,\nnextlevellist, " +
                                  "levelclearflags, deathzone, levelrankscores, levelranktimes, endpos, levelpathlist, pathlist,\nstagelightdatalist, weldlist" +
                                  "bmitemattrlist, creditstextlist, animindexlist, storysequence, musiclist <count>,\n" +
                                  "stringarray <count> [language], skyboxscale <count>, stageselectlist <count>, animationlist <count>,\n" +
                                  "masterstringlist <count>, cutscenetext <count>, recapscreen <count>, npctext <count>\n");
                Console.WriteLine("<ADDRESS>: The location of data in the file.\n");
                Console.WriteLine("<PARAMETER1>: length, count, secondary address etc. depending on data type\n");
                Console.WriteLine("<PARAMETER2>: 'hex' for binary to read length as hexadecimal, 'shortrot' for animation to read rotation as short\n");
                Console.WriteLine("<NAME>: Output file name (optional)\n");
                Console.WriteLine("Press ENTER to exit");
                Console.ReadLine();
                return;
            }
            //Args list: game, filename, key, type, address, [address2/count], [language], [name]
            switch (args[0])
            {
            case "SA1":
                game = Game.SA1;
                break;

            case "SA1_b":
                game      = Game.SA1;
                bigendian = true;
                break;

            case "SADX":
                game = Game.SADX;
                break;

            case "SADX_b":
                game      = Game.SADX;
                bigendian = true;
                break;

            case "SA2":
                game = Game.SA2;
                break;

            case "SA2_b":
                game      = Game.SA2;
                bigendian = true;
                break;

            case "SA2B":
                game = Game.SA2B;
                break;

            case "SA2B_b":
                game      = Game.SA2B;
                bigendian = true;
                break;

            default:
                return;
            }
            string model_extension     = ".sa1mdl";
            string landtable_extension = ".sa1lvl";

            ByteConverter.BigEndian = SonicRetro.SAModel.ByteConverter.BigEndian = bigendian;
            filename = args[1];
            byte[] datafile = File.ReadAllBytes(filename);
            if (Path.GetExtension(filename).ToLowerInvariant() == ".prs")
            {
                datafile = FraGag.Compression.Prs.Decompress(datafile);
            }
            Environment.CurrentDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetDirectoryName(filename));
            uint            imageBase = uint.Parse(args[2], NumberStyles.AllowHexSpecifier);
            string          type      = args[3];
            int             address   = int.Parse(args[4], NumberStyles.AllowHexSpecifier);
            bool            SA2       = game == Game.SA2 | game == Game.SA2B;
            ModelFormat     modelfmt  = ModelFormat.BasicDX;
            LandTableFormat landfmt   = LandTableFormat.SADX;

            switch (game)
            {
            case Game.SA1:
                modelfmt            = ModelFormat.Basic;
                landfmt             = LandTableFormat.SA1;
                model_extension     = ".sa1mdl";
                landtable_extension = ".sa1lvl";
                break;

            case Game.SADX:
                modelfmt            = ModelFormat.BasicDX;
                landfmt             = LandTableFormat.SADX;
                model_extension     = ".sa1mdl";
                landtable_extension = ".sa1lvl";
                break;

            case Game.SA2:
                modelfmt            = ModelFormat.Chunk;
                landfmt             = LandTableFormat.SA2;
                model_extension     = ".sa2mdl";
                landtable_extension = ".sa2lvl";
                break;

            case Game.SA2B:
                modelfmt            = ModelFormat.Chunk;
                landfmt             = LandTableFormat.SA2B;
                model_extension     = ".sa2mdl";
                landtable_extension = ".sa2blvl";
                break;
            }
            Dictionary <string, MasterObjectListEntry>     masterobjlist = new Dictionary <string, MasterObjectListEntry>();
            Dictionary <string, Dictionary <string, int> > objnamecounts = new Dictionary <string, Dictionary <string, int> >();
            string fileOutputPath = dir + "\\" + address.ToString("X");

            Console.WriteLine("Game: {0}, file: {1}, key: 0x{2}, splitting {3} at 0x{4}", game.ToString(), filename, imageBase.ToString("X"), type, address.ToString("X"));
            if (args[args.Length - 2] == "-name")
            {
                fileOutputPath = dir + "\\" + args[args.Length - 1];
                Console.WriteLine("Name: {0}", args[args.Length - 1]);
            }
            switch (type)
            {
            case "landtable":
                new LandTable(datafile, address, imageBase, landfmt).SaveToFile(fileOutputPath + landtable_extension, landfmt);
                break;

            case "model":
            {
                NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, modelfmt, new Dictionary <int, Attach>());
                ModelFile.CreateFile(fileOutputPath + model_extension, mdl, null, null, null, null, modelfmt);
            }
            break;

            case "basicmodel":
            {
                NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic, new Dictionary <int, Attach>());
                ModelFile.CreateFile(fileOutputPath + ".sa1mdl", mdl, null, null, null, null, ModelFormat.Basic);
            }
            break;

            case "basicdxmodel":
            {
                NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX, new Dictionary <int, Attach>());
                ModelFile.CreateFile(fileOutputPath + ".sa1mdl", mdl, null, null, null, null, ModelFormat.BasicDX);
            }
            break;

            case "chunkmodel":
            {
                NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                ModelFile.CreateFile(fileOutputPath + ".sa2mdl", mdl, null, null, null, null, ModelFormat.Chunk);
            }
            break;

            case "gcmodel":
            {
                NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.GC, new Dictionary <int, Attach>());
                ModelFile.CreateFile(fileOutputPath + ".sa2mdl", mdl, null, null, null, null, ModelFormat.GC);
            }
            break;

            case "action":
            {
                NJS_ACTION ani = new NJS_ACTION(datafile, address, imageBase, modelfmt, new Dictionary <int, Attach>());
                ani.Animation.Save(fileOutputPath + ".saanim");
                string[]   mdlanis = new string[0];
                NJS_OBJECT mdl     = ani.Model;
                mdlanis = (fileOutputPath + ".saanim").Split(',');
                ModelFile.CreateFile(fileOutputPath + "_model" + model_extension, mdl, mdlanis, null, null, null, modelfmt);
            }
            break;

            case "animation":
            {
                bool shortrot_enabled = false;
                if (args.Length > 6 && args[6] == "shortrot")
                {
                    shortrot_enabled = true;
                }
                NJS_OBJECT mdl = new NJS_OBJECT(datafile, int.Parse(args[5], NumberStyles.AllowHexSpecifier), imageBase, modelfmt, new Dictionary <int, Attach>());
                new NJS_MOTION(datafile, address, imageBase, mdl.CountAnimated(), shortrot: shortrot_enabled).Save(fileOutputPath + ".saanim");
                string[] mdlanis = new string[0];
                mdlanis = (fileOutputPath + ".saanim").Split(',');
                ModelFile.CreateFile(fileOutputPath + "_model" + model_extension, mdl, mdlanis, null, null, null, modelfmt);
            }
            break;

            case "objlist":
            {
                ObjectListEntry[] objs = ObjectList.Load(datafile, address, imageBase, SA2);
                foreach (ObjectListEntry obj in objs)
                {
                    if (!masterobjlist.ContainsKey(obj.CodeString))
                    {
                        masterobjlist.Add(obj.CodeString, new MasterObjectListEntry(obj));
                    }
                    if (!objnamecounts.ContainsKey(obj.CodeString))
                    {
                        objnamecounts.Add(obj.CodeString, new Dictionary <string, int>()
                            {
                                { obj.Name, 1 }
                            });
                    }
                    else if (!objnamecounts[obj.CodeString].ContainsKey(obj.Name))
                    {
                        objnamecounts[obj.CodeString].Add(obj.Name, 1);
                    }
                    else
                    {
                        objnamecounts[obj.CodeString][obj.Name]++;
                    }
                }
                objs.Save(fileOutputPath + ".ini");
            }
            break;

            case "startpos":
                if (SA2)
                {
                    SA2StartPosList.Load(datafile, address).Save(fileOutputPath + ".ini");
                }
                else
                {
                    SA1StartPosList.Load(datafile, address).Save(fileOutputPath + ".ini");
                }
                break;

            case "texlist":
                TextureList.Load(datafile, address, imageBase).Save(fileOutputPath + ".ini");
                break;

            case "leveltexlist":
                new LevelTextureList(datafile, address, imageBase).Save(fileOutputPath + ".ini");
                break;

            case "triallevellist":
                TrialLevelList.Save(TrialLevelList.Load(datafile, address, imageBase), fileOutputPath + ".ini");
                break;

            case "bosslevellist":
                BossLevelList.Save(BossLevelList.Load(datafile, address), fileOutputPath + ".ini");
                break;

            case "fieldstartpos":
                FieldStartPosList.Load(datafile, address).Save(fileOutputPath + ".ini");
                break;

            case "soundtestlist":
                SoundTestList.Load(datafile, address, imageBase).Save(fileOutputPath + ".ini");
                break;

            case "musiclist":
            {
                int muscnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                MusicList.Load(datafile, address, imageBase, muscnt).Save(fileOutputPath + ".ini");
            }
            break;

            case "soundlist":
                SoundList.Load(datafile, address, imageBase).Save(fileOutputPath + ".ini");
                break;

            case "stringarray":
            {
                int       cnt  = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                Languages lang = Languages.Japanese;
                if (args.Length > 6)
                {
                    lang = (Languages)Enum.Parse(typeof(Languages), args[6], true);
                }
                StringArray.Load(datafile, address, imageBase, cnt, lang).Save(fileOutputPath + ".txt");
            }
            break;

            case "nextlevellist":
                NextLevelList.Load(datafile, address).Save(fileOutputPath + ".ini");
                break;

            case "cutscenetext":
            {
                int cnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                new CutsceneText(datafile, address, imageBase, cnt).Save(fileOutputPath + ".txt", out string[] hashes);
            }
            break;

            case "recapscreen":
            {
                int cnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                RecapScreenList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath + ".txt", out string[][] hashes);
            }
            break;

            case "npctext":
            {
                int cnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                NPCTextList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath + ".txt", out string[][] hashes);
            }
            break;

            case "levelclearflags":
                LevelClearFlagList.Save(LevelClearFlagList.Load(datafile, address), fileOutputPath + ".ini");
                break;

            case "deathzone":
            {
                List <DeathZoneFlags> flags = new List <DeathZoneFlags>();
                string        path          = Path.GetDirectoryName(fileOutputPath);
                List <string> hashes        = new List <string>();
                int           num           = 0;
                while (ByteConverter.ToUInt32(datafile, address + 4) != 0)
                {
                    flags.Add(new DeathZoneFlags(datafile, address));
                    string file = Path.Combine(path, num++.ToString(NumberFormatInfo.InvariantInfo) + (modelfmt == ModelFormat.Chunk ? ".sa2mdl" : ".sa1mdl"));
                    ModelFile.CreateFile(file, new NJS_OBJECT(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, modelfmt, new Dictionary <int, Attach>()), null, null, null, null, modelfmt);
                    address += 8;
                }
                flags.ToArray().Save(fileOutputPath + ".ini");
            }
            break;

            case "skyboxscale":
            {
                int cnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                SkyboxScaleList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath + ".ini");
            }
            break;

            case "stageselectlist":
            {
                int cnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                StageSelectLevelList.Load(datafile, address, cnt).Save(fileOutputPath + ".ini");
            }
            break;

            case "levelrankscores":
                LevelRankScoresList.Load(datafile, address).Save(fileOutputPath + ".ini");
                break;

            case "levelranktimes":
                LevelRankTimesList.Load(datafile, address).Save(fileOutputPath + ".ini");
                break;

            case "endpos":
                SA2EndPosList.Load(datafile, address).Save(fileOutputPath + ".ini");
                break;

            case "animationlist":
            {
                int cnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                SA2AnimationInfoList.Load(datafile, address, cnt).Save(fileOutputPath + ".ini");
            }
            break;

            case "levelpathlist":
            {
                ushort lvlnum = (ushort)ByteConverter.ToUInt32(datafile, address);
                while (lvlnum != 0xFFFF)
                {
                    int ptr = ByteConverter.ToInt32(datafile, address + 4);
                    if (ptr != 0)
                    {
                        ptr = (int)((uint)ptr - imageBase);
                        SA1LevelAct level  = new SA1LevelAct(lvlnum);
                        string      lvldir = Path.Combine(fileOutputPath, level.ToString());
                        PathList.Load(datafile, ptr, imageBase).Save(lvldir, out string[] lvlhashes);
                    }
                    address += 8;
                    lvlnum   = (ushort)ByteConverter.ToUInt32(datafile, address);
                }
            }
            break;

            case "pathlist":
            {
                PathList.Load(datafile, address, imageBase).Save(fileOutputPath, out string[] hashes);
            }
            break;

            case "stagelightdatalist":
                SA1StageLightDataList.Load(datafile, address).Save(fileOutputPath);
                break;

            case "weldlist":
                WeldList.Load(datafile, address, imageBase).Save(fileOutputPath);
                break;

            case "bmitemattrlist":
                BlackMarketItemAttributesList.Load(datafile, address, imageBase).Save(fileOutputPath);
                break;

            case "creditstextlist":
                CreditsTextList.Load(datafile, address, imageBase).Save(fileOutputPath);
                break;

            case "animindexlist":
            {
                Directory.CreateDirectory(fileOutputPath);
                List <string> hashes = new List <string>();
                int           i      = ByteConverter.ToInt16(datafile, address);
                while (i != -1)
                {
                    new NJS_MOTION(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, ByteConverter.ToInt16(datafile, address + 2))
                    .Save(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                    hashes.Add(i.ToString(NumberFormatInfo.InvariantInfo) + ":" + HelperFunctions.FileHash(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"));
                    address += 8;
                    i        = ByteConverter.ToInt16(datafile, address);
                }
            }
            break;

            case "storysequence":
                SA2StoryList.Load(datafile, address).Save(fileOutputPath);
                break;

            case "masterstringlist":
            {
                int cnt = int.Parse(args[5], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                for (int l = 0; l < 5; l++)
                {
                    Languages            lng = (Languages)l;
                    System.Text.Encoding enc = HelperFunctions.GetEncoding(game, lng);
                    string ld = Path.Combine(fileOutputPath, lng.ToString());
                    Directory.CreateDirectory(ld);
                    int ptr = datafile.GetPointer(address, imageBase);
                    for (int i = 0; i < cnt; i++)
                    {
                        int ptr2 = datafile.GetPointer(ptr, imageBase);
                        if (ptr2 != 0)
                        {
                            string fn = Path.Combine(ld, $"{i}.txt");
                            File.WriteAllText(fn, datafile.GetCString(ptr2, enc).Replace("\n", "\r\n"));
                        }
                        ptr += 4;
                    }
                    address += 4;
                }
            }
            break;

            case "binary":
            {
                int length;
                if (args.Length > 6 && args[6] == "hex")
                {
                    length = int.Parse(args[5], NumberStyles.AllowHexSpecifier);
                }
                else
                {
                    length = int.Parse(args[5]);
                }
                byte[] bin = new byte[length];
                Array.Copy(datafile, address, bin, 0, bin.Length);
                File.WriteAllBytes(fileOutputPath + ".bin", bin);
                Console.WriteLine("Length: {0} (0x{1}) bytes", length.ToString(), length.ToString("X"));
            }
            break;

            default:
                break;
            }
        }
Esempio n. 19
0
        /// <summary>
        /// Exports a single level, model or animation file as text.
        /// </summary>
        /// <param name="source">Source pathname.</param>
        /// <param name="type">Type of text conversion.</param>
        /// <param name="destination">Destination pathname. Leave blank to export in the same folder with a swapped extension.</param>
        /// <param name="basicDX">Use the SADX2004 format for Basic models.</param>
        public static void ConvertFileToText(string source, TextType type, string destination = "", bool basicDX = true, bool overwrite = true)
        {
            string outext    = ".c";
            string extension = Path.GetExtension(source);

            switch (extension.ToLowerInvariant())
            {
            case ".sa2lvl":
            case ".sa1lvl":
                if (type == TextType.CStructs)
                {
                    if (destination == "")
                    {
                        destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext);
                    }
                    if (!overwrite && File.Exists(destination))
                    {
                        while (File.Exists(destination))
                        {
                            destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext);
                        }
                    }
                    LandTable     land   = LandTable.LoadFromFile(source);
                    List <string> labels = new List <string>()
                    {
                        land.Name
                    };
                    using (StreamWriter sw = File.CreateText(destination))
                    {
                        sw.Write("/* Sonic Adventure ");
                        LandTableFormat fmt = land.Format;
                        switch (land.Format)
                        {
                        case LandTableFormat.SA1:
                        case LandTableFormat.SADX:
                            if (basicDX)
                            {
                                sw.Write("DX");
                                fmt = LandTableFormat.SADX;
                            }
                            else
                            {
                                sw.Write("1");
                                fmt = LandTableFormat.SA1;
                            }
                            break;

                        case LandTableFormat.SA2:
                            sw.Write("2");
                            fmt = LandTableFormat.SA2;
                            break;

                        case LandTableFormat.SA2B:
                            sw.Write("2 Battle");
                            fmt = LandTableFormat.SA2B;
                            break;
                        }
                        sw.WriteLine(" LandTable");
                        sw.WriteLine(" * ");
                        sw.WriteLine(" * Generated by DataToolbox");
                        sw.WriteLine(" * ");
                        if (!string.IsNullOrEmpty(land.Description))
                        {
                            sw.Write(" * Description: ");
                            sw.WriteLine(land.Description);
                            sw.WriteLine(" * ");
                        }
                        if (!string.IsNullOrEmpty(land.Author))
                        {
                            sw.Write(" * Author: ");
                            sw.WriteLine(land.Author);
                            sw.WriteLine(" * ");
                        }
                        sw.WriteLine(" */");
                        sw.WriteLine();
                        land.ToStructVariables(sw, fmt, labels, null);
                    }
                }
                break;

            case ".sa1mdl":
            case ".sa2mdl":
                ModelFile         modelFile  = new ModelFile(source);
                NJS_OBJECT        model      = modelFile.Model;
                List <NJS_MOTION> animations = new List <NJS_MOTION>(modelFile.Animations);
                if (type == TextType.CStructs)
                {
                    outext = ".c";
                    if (destination == "")
                    {
                        destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext);
                    }
                    if (!overwrite && File.Exists(destination))
                    {
                        while (File.Exists(destination))
                        {
                            destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext);
                        }
                    }
                    using (StreamWriter sw = File.CreateText(destination))
                    {
                        sw.Write("/* NINJA ");
                        switch (modelFile.Format)
                        {
                        case ModelFormat.Basic:
                        case ModelFormat.BasicDX:
                            if (basicDX)
                            {
                                sw.Write("Basic (with Sonic Adventure DX additions)");
                            }
                            else
                            {
                                sw.Write("Basic");
                            }
                            break;

                        case ModelFormat.Chunk:
                            sw.Write("Chunk");
                            break;

                        case ModelFormat.GC:
                            sw.Write("GC");
                            break;
                        }
                        sw.WriteLine(" model");
                        sw.WriteLine(" * ");
                        sw.WriteLine(" * Generated by DataToolbox");
                        sw.WriteLine(" * ");
                        if (modelFile != null)
                        {
                            if (!string.IsNullOrEmpty(modelFile.Description))
                            {
                                sw.Write(" * Description: ");
                                sw.WriteLine(modelFile.Description);
                                sw.WriteLine(" * ");
                            }
                            if (!string.IsNullOrEmpty(modelFile.Author))
                            {
                                sw.Write(" * Author: ");
                                sw.WriteLine(modelFile.Author);
                                sw.WriteLine(" * ");
                            }
                        }
                        sw.WriteLine(" */");
                        sw.WriteLine();
                        List <string> labels_m = new List <string>()
                        {
                            model.Name
                        };
                        model.ToStructVariables(sw, basicDX, labels_m, null);
                        foreach (NJS_MOTION anim in animations)
                        {
                            anim.ToStructVariables(sw);
                        }
                    }
                }
                else if (type == TextType.NJA)
                {
                    outext = ".nja";
                    if (destination == "")
                    {
                        destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext);
                    }
                    if (!overwrite && File.Exists(destination))
                    {
                        while (File.Exists(destination))
                        {
                            destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext);
                        }
                    }
                    using (StreamWriter sw2 = File.CreateText(destination))
                    {
                        List <string> labels_nj = new List <string>()
                        {
                            model.Name
                        };
                        model.ToNJA(sw2, basicDX, labels_nj, null);
                    }
                }
                break;

            case ".saanim":
                NJS_MOTION animation = NJS_MOTION.Load(source);
                if (type == TextType.CStructs)
                {
                    outext = ".c";
                    if (destination == "")
                    {
                        destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext);
                    }
                    if (!overwrite && File.Exists(destination))
                    {
                        while (File.Exists(destination))
                        {
                            destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext);
                        }
                    }
                    using (StreamWriter sw = File.CreateText(destination))
                    {
                        sw.WriteLine("/* NINJA Motion");
                        sw.WriteLine(" * ");
                        sw.WriteLine(" * Generated by DataToolbox");
                        sw.WriteLine(" * ");
                        sw.WriteLine(" */");
                        sw.WriteLine();
                        animation.ToStructVariables(sw);
                    }
                }
                else if (type == TextType.JSON)
                {
                    outext = ".json";
                    if (destination == "")
                    {
                        destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext);
                    }
                    if (!overwrite && File.Exists(destination))
                    {
                        while (File.Exists(destination))
                        {
                            destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext);
                        }
                    }
                    JsonSerializer js = new JsonSerializer()
                    {
                        Culture = System.Globalization.CultureInfo.InvariantCulture
                    };
                    using (TextWriter tw = File.CreateText(destination))
                        using (JsonTextWriter jtw = new JsonTextWriter(tw)
                        {
                            Formatting = Formatting.Indented
                        })
                            js.Serialize(jtw, animation);
                }
                break;
            }
        }
Esempio n. 20
0
        static bool CheckLandTable(uint address, LandTableFormat landfmt)
        {
            ByteConverter.BigEndian = BigEndian;
            if (address > (uint)datafile.Length - 52)
            {
                return(false);
            }
            short       COLCount;
            short       AnimCount;
            short       ChunkCount;
            ushort      Unknown1;
            uint        COLAddress;
            uint        AnimPointer;
            uint        Texlist;
            uint        Buffer;
            int         ObjAddrPointer;
            uint        ObjAddr;
            ModelFormat modelfmt = ModelFormat.Basic;

            switch (landfmt)
            {
            case LandTableFormat.SA1:
                modelfmt = ModelFormat.Basic;
                break;

            case LandTableFormat.SADX:
                modelfmt = ModelFormat.BasicDX;
                break;

            case LandTableFormat.SA2:
                modelfmt = ModelFormat.Chunk;
                break;

            case LandTableFormat.SA2B:
                modelfmt = ModelFormat.GC;
                break;
            }
            switch (landfmt)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                COLCount = ByteConverter.ToInt16(datafile, (int)address);
                if (COLCount <= 0 || COLCount > 2048)
                {
                    return(false);
                }
                AnimCount = ByteConverter.ToInt16(datafile, (int)address + 2);
                if (AnimCount < 0 || AnimCount > 2048)
                {
                    return(false);
                }
                COLAddress = ByteConverter.ToUInt32(datafile, (int)address + 0xC);
                if (COLAddress < ImageBase || COLAddress == 0)
                {
                    return(false);
                }
                if (COLAddress > datafile.Length - 32 + ImageBase)
                {
                    return(false);
                }
                AnimPointer = ByteConverter.ToUInt32(datafile, (int)address + 0x10);
                if (AnimPointer != 0 && AnimPointer < ImageBase)
                {
                    return(false);
                }
                if (AnimPointer > datafile.Length - 32 + ImageBase)
                {
                    return(false);
                }
                Texlist = ByteConverter.ToUInt32(datafile, (int)address + 0x18);
                if (Texlist != 0 && Texlist < ImageBase)
                {
                    return(false);
                }
                if (Texlist > datafile.Length - 32 + ImageBase)
                {
                    return(false);
                }
                ObjAddrPointer = (int)(COLAddress - ImageBase) + 0x18;
                ObjAddr        = ByteConverter.ToUInt32(datafile, ObjAddrPointer);
                if (ObjAddr < ImageBase)
                {
                    return(false);
                }
                if (!CheckModel(ObjAddr - ImageBase, -1, modelfmt, true))
                {
                    return(false);
                }
                break;

            case LandTableFormat.SA2:
            case LandTableFormat.SA2B:
                COLCount = ByteConverter.ToInt16(datafile, (int)address);
                if (COLCount < 0)
                {
                    return(false);
                }
                ChunkCount = ByteConverter.ToInt16(datafile, (int)address + 2);
                if (ChunkCount < -1)
                {
                    return(false);
                }
                Unknown1 = ByteConverter.ToUInt16(datafile, (int)address + 4);
                if (Unknown1 != 65535)
                {
                    return(false);
                }
                COLAddress = ByteConverter.ToUInt32(datafile, (int)address + 0x10);
                if (COLAddress < ImageBase)
                {
                    return(false);
                }
                if (COLAddress > datafile.Length - 32 + ImageBase)
                {
                    return(false);
                }
                Buffer = ByteConverter.ToUInt32(datafile, (int)address + 0x14);
                if (Buffer != 0)
                {
                    return(false);
                }
                AnimPointer = ByteConverter.ToUInt32(datafile, (int)address + 0x18);
                if (AnimPointer != 0 && AnimPointer < ImageBase)
                {
                    return(false);
                }
                if (AnimPointer > datafile.Length - 32 + ImageBase)
                {
                    return(false);
                }
                Texlist = ByteConverter.ToUInt32(datafile, (int)address + 0x1C);
                if (Texlist > datafile.Length - 32 + ImageBase)
                {
                    return(false);
                }
                if (Texlist == 0 || Texlist < ImageBase)
                {
                    return(false);
                }
                break;
            }
            return(true);
        }
Esempio n. 21
0
 public static int Size(LandTableFormat format)
 {
     switch (format)
     {
         case LandTableFormat.SA1:
         case LandTableFormat.SADX:
             return 0x24;
         case LandTableFormat.SA2:
             return 0x20;
         default:
             throw new ArgumentOutOfRangeException("format");
     }
 }
Esempio n. 22
0
 public string ToStructVariables(LandTableFormat format, List<string> labels)
 {
     return ToStructVariables(format, labels, null);
 }
Esempio n. 23
0
        // Scan for Landtables
        static void ScanLandtable(LandTableFormat landfmt)
        {
            CurrentStep++;
            CurrentScanData         = "Landtables " + landfmt.ToString();
            ByteConverter.BigEndian = BigEndian;
            Console.WriteLine("Step {0}: Scanning for {1} landtables", CurrentStep, landfmt.ToString());
            string landtable_extension = ".sa1lvl";
            int    count = 0;

            switch (landfmt)
            {
            case LandTableFormat.SA1:
                landtable_extension = ".sa1lvl";
                break;

            case LandTableFormat.SADX:
            default:
                landtable_extension = ".sa1lvl";
                break;

            case LandTableFormat.SA2:
                landtable_extension = ".sa2lvl";
                break;

            case LandTableFormat.SA2B:
                landtable_extension = ".sa2blvl";
                break;
            }
            if (!SingleOutputFolder)
            {
                Directory.CreateDirectory(Path.Combine(OutputFolder, "levels"));
            }
            for (uint address = StartAddress; address < EndAddress; address += 1)
            {
                if (CancelScan)
                {
                    break;
                }
                if (ConsoleMode && address % 1000 == 0)
                {
                    Console.Write("\r{0} ", address.ToString("X8"));
                }
                CurrentAddress = address;
                string fileOutputPath = Path.Combine(OutputFolder, "levels", address.ToString("X8"));
                if (SingleOutputFolder)
                {
                    fileOutputPath = Path.Combine(OutputFolder, address.ToString("X8"));
                }
                if (!CheckLandTable(address, landfmt))
                {
                    continue;
                }
                try
                {
                    //Console.WriteLine("Try {0}", address.ToString("X"));
                    LandTable land = new LandTable(datafile, (int)address, ImageBase, landfmt);
                    if (land.COL.Count > 3)
                    {
                        land.SaveToFile(fileOutputPath + landtable_extension, landfmt, NoMeta);
                        count++;
                        switch (landfmt)
                        {
                        case LandTableFormat.SA1:
                            FoundSA1Landtables++;
                            break;

                        case LandTableFormat.SADX:
                        default:
                            FoundSADXLandtables++;
                            break;

                        case LandTableFormat.SA2:
                            FoundSA2Landtables++;
                            break;

                        case LandTableFormat.SA2B:
                            FoundSA2BLandtables++;
                            break;
                        }
                        landtablelist.Add(address);
                        Console.WriteLine("\rLandtable {0} at {1}", landfmt.ToString(), address.ToString("X8"));
                        addresslist.Add(address, "landtable_" + landfmt.ToString());
                        address += (uint)LandTable.Size(landfmt) - 1;
                    }
                }
                catch (Exception)
                {
                    continue;
                }
            }
            Console.WriteLine("\r{0} landtables found", count);
        }
Esempio n. 24
0
 public string ToStructVariables(LandTableFormat format, List<string> labels, string[] textures)
 {
     StringBuilder result = new StringBuilder();
     List<COL> cnk = new List<COL>();
     List<COL> bas = new List<COL>();
     foreach (COL item in COL)
     {
         if (item.Model.Attach is BasicAttach)
             bas.Add(item);
         else
             cnk.Add(item);
     }
     COL.Clear();
     COL.AddRange(cnk);
     COL.AddRange(bas);
     for (int i = 0; i < COL.Count; i++)
     {
         if (!labels.Contains(COL[i].Model.Name))
         {
             labels.Add(COL[i].Model.Name);
             result.AppendLine(COL[i].Model.ToStructVariables(format == LandTableFormat.SADX, labels, textures));
         }
     }
     for (int i = 0; i < Anim.Count; i++)
     {
         string aniid = Anim[i].Animation.Name.MakeIdentifier();
         if (!labels.Contains(Anim[i].Model.Name))
         {
             labels.Add(Anim[i].Model.Name);
             result.AppendLine(Anim[i].Model.ToStructVariables(format == LandTableFormat.SADX, labels, textures));
         }
         if (labels.Contains(aniid))
         {
             labels.Add(aniid);
             result.AppendLine(Anim[i].Animation.ToStructVariables());
             result.Append("NJS_ACTION action_");
             result.Append(aniid);
             result.Append(" = { &");
             result.Append(Anim[i].Model.Name);
             result.Append(", &");
             result.Append(aniid);
             result.AppendLine(" };");
             result.AppendLine();
         }
     }
     if (!labels.Contains(COLName))
     {
         labels.Add(COLName);
         result.Append("COL ");
         result.Append(COLName);
         result.AppendLine("[] = {");
         List<string> lines = new List<string>(COL.Count);
         foreach (COL item in COL)
             lines.Add(item.ToStruct(format));
         result.AppendLine("\t" + string.Join("," + Environment.NewLine + "\t", lines.ToArray()));
         result.AppendLine("};");
         result.AppendLine();
     }
     if (Anim.Count > 0 && !labels.Contains(COLName))
     {
         labels.Add(COLName);
         result.Append("GeoAnimData ");
         result.Append(AnimName);
         result.AppendLine("[] = {");
         List<string> lines = new List<string>(Anim.Count);
         foreach (GeoAnimData item in Anim)
             lines.Add(item.ToStruct());
         result.AppendLine("\t" + string.Join("," + Environment.NewLine + "\t", lines.ToArray()));
         result.AppendLine("};");
         result.AppendLine();
     }
     result.Append("LandTable ");
     result.Append(Name);
     result.Append(" = { LengthOfArray(");
     result.Append(COLName);
     result.Append("), ");
     switch (format)
     {
         case LandTableFormat.SA1:
         case LandTableFormat.SADX:
             result.Append(Anim.Count > 0 ? "LengthOfArray(" + AnimName + ")" : "0");
             result.Append(", ");
             result.Append(Flags.ToCHex());
             result.Append(", ");
             result.Append(Unknown1.ToC());
             result.Append(", ");
             result.Append(COLName);
             result.Append(", ");
             result.Append(Anim.Count > 0 ? AnimName : "NULL");
             result.Append(", ");
             result.Append(TextureFileName.ToC());
             result.Append(", (NJS_TEXLIST *)");
             result.Append(TextureList.ToCHex());
             result.Append(", ");
             result.Append(Unknown2.ToCHex());
             result.Append(", ");
             result.Append(Unknown3.ToCHex());
             break;
         case LandTableFormat.SA2:
             result.Append(cnk.Count);
             result.Append(", 0, 0, 0, 0, ");
             result.Append(Unknown1.ToC());
             result.Append(", ");
             result.Append(COLName);
             result.Append(", ");
             result.Append(Anim.Count > 0 ? AnimName : "NULL");
             result.Append(", ");
             result.Append(TextureFileName.ToC());
             result.Append(", (NJS_TEXLIST *)");
             result.Append(TextureList.ToCHex());
             break;
     }
     result.AppendLine(" };");
     return result.ToString();
 }
Esempio n. 25
0
 public void SaveToFile(string filename, LandTableFormat format)
 {
     bool be = ByteConverter.BigEndian;
     ByteConverter.BigEndian = false;
     if (format == LandTableFormat.SADX)
         format = LandTableFormat.SA1;
     List<byte> file = new List<byte>();
     ulong magic;
     switch (format)
     {
         case LandTableFormat.SA1:
             magic = SA1LVLVer;
             break;
         case LandTableFormat.SA2:
             magic = SA2LVLVer;
             break;
         default:
             throw new ArgumentException("Cannot save " + format + " format levels to file!", "format");
     }
     file.AddRange(ByteConverter.GetBytes(magic));
     uint addr;
     Dictionary<string, uint> labels = new Dictionary<string, uint>();
     byte[] lvl = GetBytes(0x10, format, labels, out addr);
     file.AddRange(ByteConverter.GetBytes(addr + 0x10));
     file.AddRange(ByteConverter.GetBytes(lvl.Length + 0x10));
     file.AddRange(lvl);
     if (labels.Count > 0)
     {
         List<byte> chunk = new List<byte>(labels.Count * 8);
         int straddr = (labels.Count * 8) + 8;
         List<byte> strbytes = new List<byte>();
         foreach (KeyValuePair<string, uint> label in labels)
         {
             chunk.AddRange(ByteConverter.GetBytes(label.Value));
             chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count));
             strbytes.AddRange(Encoding.UTF8.GetBytes(label.Key));
             strbytes.Add(0);
             strbytes.Align(4);
         }
         chunk.AddRange(ByteConverter.GetBytes(-1L));
         chunk.AddRange(strbytes);
         file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Label));
         file.AddRange(ByteConverter.GetBytes(chunk.Count));
         file.AddRange(chunk);
     }
     if (!string.IsNullOrEmpty(Author))
     {
         List<byte> chunk = new List<byte>(Author.Length + 1);
         chunk.AddRange(Encoding.UTF8.GetBytes(Author));
         chunk.Add(0);
         chunk.Align(4);
         file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Author));
         file.AddRange(ByteConverter.GetBytes(chunk.Count));
         file.AddRange(chunk);
     }
     if (!string.IsNullOrEmpty(Description))
     {
         List<byte> chunk = new List<byte>(Description.Length + 1);
         chunk.AddRange(Encoding.UTF8.GetBytes(Description));
         chunk.Add(0);
         chunk.Align(4);
         file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Description));
         file.AddRange(ByteConverter.GetBytes(chunk.Count));
         file.AddRange(chunk);
     }
     if (!string.IsNullOrEmpty(Tool))
     {
         List<byte> chunk = new List<byte>(Tool.Length + 1);
         chunk.AddRange(Encoding.UTF8.GetBytes(Tool));
         chunk.Add(0);
         chunk.Align(4);
         file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Tool));
         file.AddRange(ByteConverter.GetBytes(chunk.Count));
         file.AddRange(chunk);
     }
     foreach (KeyValuePair<uint, byte[]> item in Metadata)
     {
         file.AddRange(ByteConverter.GetBytes(item.Key));
         file.AddRange(ByteConverter.GetBytes(item.Value.Length));
         file.AddRange(item.Value);
     }
     file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.End));
     file.AddRange(new byte[4]);
     File.WriteAllBytes(filename, file.ToArray());
     ByteConverter.BigEndian = be;
 }
Esempio n. 26
0
 public byte[] GetBytes(uint imageBase, LandTableFormat format)
 {
     uint address;
     return GetBytes(imageBase, format, out address);
 }
Esempio n. 27
0
        private void cStructsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog sd = new SaveFileDialog()
            {
                DefaultExt = "c", Filter = "C Files|*.c"
            })
                if (sd.ShowDialog(this) == DialogResult.OK)
                {
                    LandTableFormat fmt = LevelData.geo.Format;
                    switch (fmt)
                    {
                    case LandTableFormat.SA1:
                    case LandTableFormat.SADX:
                        using (StructExportDialog ed = new StructExportDialog()
                        {
                            Format = LevelData.geo.Format
                        })
                            if (ed.ShowDialog(this) == DialogResult.OK)
                            {
                                fmt = ed.Format;
                            }
                            else
                            {
                                return;
                            }
                        break;
                    }
                    List <string> labels = new List <string>()
                    {
                        LevelData.geo.Name
                    };
                    using (StreamWriter sw = File.CreateText(sd.FileName))
                    {
                        sw.Write("/* Sonic Adventure ");
                        switch (fmt)
                        {
                        case LandTableFormat.SA1:
                            sw.Write("1");
                            break;

                        case LandTableFormat.SADX:
                            sw.Write("DX");
                            break;

                        case LandTableFormat.SA2:
                            sw.Write("2");
                            break;
                        }
                        sw.WriteLine(" LandTable");
                        sw.WriteLine(" * ");
                        sw.WriteLine(" * Generated by SALVL");
                        sw.WriteLine(" * ");
                        if (!string.IsNullOrEmpty(LevelData.geo.Description))
                        {
                            sw.Write(" * Description: ");
                            sw.WriteLine(LevelData.geo.Description);
                            sw.WriteLine(" * ");
                        }
                        if (!string.IsNullOrEmpty(LevelData.geo.Author))
                        {
                            sw.Write(" * Author: ");
                            sw.WriteLine(LevelData.geo.Author);
                            sw.WriteLine(" * ");
                        }
                        sw.WriteLine(" */");
                        sw.WriteLine();
                        string[] texnames = null;
                        if (LevelData.leveltexs != null)
                        {
                            texnames = new string[LevelData.TextureBitmaps[LevelData.leveltexs].Length];
                            for (int i = 0; i < LevelData.TextureBitmaps[LevelData.leveltexs].Length; i++)
                            {
                                texnames[i] = string.Format("{0}TexName_{1}", LevelData.leveltexs,
                                                            LevelData.TextureBitmaps[LevelData.leveltexs][i].Name);
                            }
                            sw.Write("enum {0}TexName", LevelData.leveltexs);
                            sw.WriteLine();
                            sw.WriteLine("{");
                            sw.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", texnames));
                            sw.WriteLine("};");
                            sw.WriteLine();
                        }
                        LevelData.geo.ToStructVariables(sw, fmt, labels, texnames);
                    }
                }
        }
Esempio n. 28
0
 public LandTable(byte[] file, int address, uint imageBase, LandTableFormat format)
     : this(file, address, imageBase, format, new Dictionary <int, string>())
 {
 }
Esempio n. 29
0
 public string ToStruct(LandTableFormat format)
 {
     StringBuilder result = new StringBuilder("{ ");
     result.Append(Bounds.ToStruct());
     result.Append(", ");
     switch (format)
     {
         case LandTableFormat.SA1:
         case LandTableFormat.SADX:
             result.Append(Unknown1.ToCHex());
             result.Append(", ");
             result.Append(Unknown2.ToCHex());
             result.Append(", ");
             result.Append(Model != null ? "&" + Model.Name : "NULL");
             result.Append(", ");
             result.AppendFormat(Unknown3.ToCHex());
             break;
         case LandTableFormat.SA2:
             result.Append(Model != null ? "&" + Model.Name : "NULL");
             result.Append(", ");
             result.Append(Unknown2.ToCHex());
             result.Append(", ");
             result.Append(Unknown3.ToCHex());
             break;
     }
     result.Append(", ");
     result.AppendFormat(Flags.ToCHex());
     result.Append(" }");
     return result.ToString();
 }
Esempio n. 30
0
 public LandTable(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary<int, string> labels)
 {
     Format = format;
     if (labels.ContainsKey(address))
         Name = labels[address];
     else
         Name = "landtable_" + address.ToString("X8");
     short colcnt = ByteConverter.ToInt16(file, address);
     switch (format)
     {
         case LandTableFormat.SA1:
         case LandTableFormat.SADX:
             short anicnt = ByteConverter.ToInt16(file, address + 2);
             Flags = ByteConverter.ToInt32(file, address + 4);
             Unknown1 = ByteConverter.ToSingle(file, address + 8);
             COL = new List<COL>();
             int tmpaddr = ByteConverter.ToInt32(file, address + 0xC);
             if (tmpaddr != 0)
             {
                 tmpaddr = (int)unchecked((uint)tmpaddr - imageBase);
                 if (labels.ContainsKey(tmpaddr))
                     COLName = labels[tmpaddr];
                 else
                     COLName = "collist_" + tmpaddr.ToString("X8");
                 for (int i = 0; i < colcnt; i++)
                 {
                     COL.Add(new COL(file, tmpaddr, imageBase, format, labels));
                     tmpaddr += SAModel.COL.Size(format);
                 }
             }
             else
                 COLName = "collist_" + Extensions.GenerateIdentifier();
             Anim = new List<GeoAnimData>();
             tmpaddr = ByteConverter.ToInt32(file, address + 0x10);
             if (tmpaddr != 0)
             {
                 tmpaddr = (int)unchecked((uint)tmpaddr - imageBase);
                 if (labels.ContainsKey(tmpaddr))
                     AnimName = labels[tmpaddr];
                 else
                     AnimName = "animlist_" + tmpaddr.ToString("X8");
                 for (int i = 0; i < anicnt; i++)
                 {
                     Anim.Add(new GeoAnimData(file, tmpaddr, imageBase, format, labels));
                     tmpaddr += GeoAnimData.Size;
                 }
             }
             else
                 AnimName = "animlist_" + Extensions.GenerateIdentifier();
             tmpaddr = ByteConverter.ToInt32(file, address + 0x14);
             if (tmpaddr != 0)
             {
                 tmpaddr = (int)unchecked((uint)tmpaddr - imageBase);
                 TextureFileName = file.GetCString(tmpaddr, Encoding.ASCII);
             }
             TextureList = ByteConverter.ToUInt32(file, address + 0x18);
             Unknown2 = ByteConverter.ToInt32(file, address + 0x1C);
             Unknown3 = ByteConverter.ToInt32(file, address + 0x20);
             break;
         case LandTableFormat.SA2:
             short cnkcnt = ByteConverter.ToInt16(file, address + 2);
             Unknown1 = ByteConverter.ToSingle(file, address + 0xC);
             COL = new List<COL>();
             tmpaddr = ByteConverter.ToInt32(file, address + 0x10);
             if (tmpaddr != 0)
             {
                 tmpaddr = (int)unchecked((uint)tmpaddr - imageBase);
                 if (labels.ContainsKey(tmpaddr))
                     COLName = labels[tmpaddr];
                 else
                     COLName = "collist_" + tmpaddr.ToString("X8");
                 for (int i = 0; i < colcnt; i++)
                 {
                     COL.Add(new COL(file, tmpaddr, imageBase, format, labels, cnkcnt < 0 ? null : (bool?)(i >= cnkcnt)));
                     tmpaddr += SAModel.COL.Size(format);
                 }
             }
             else
                 COLName = "collist_" + Extensions.GenerateIdentifier();
             Anim = new List<GeoAnimData>();
             AnimName = "animlist_" + Extensions.GenerateIdentifier();
             tmpaddr = ByteConverter.ToInt32(file, address + 0x18);
             if (tmpaddr != 0)
             {
                 tmpaddr = (int)unchecked((uint)tmpaddr - imageBase);
                 TextureFileName = file.GetCString(tmpaddr, Encoding.ASCII);
             }
             TextureList = ByteConverter.ToUInt32(file, address + 0x1C);
             break;
     }
     Metadata = new Dictionary<uint, byte[]>();
 }
Esempio n. 31
0
        static void Main(string[] args)
        {
            string[] arguments = Environment.GetCommandLineArgs();
            string   filename;
            bool     dx = true;
            string   outputfile;
            string   extension;
            string   dir = Environment.CurrentDirectory;

            if (args.Length == 0)
            {
                Console.WriteLine("Struct Exporter is a tool that lets you convert level, model and animation files to C structs.");
                Console.WriteLine("Usage: StructExporter <filename> [output path] [-nodx]\n");
                Console.WriteLine("Arguments: -nodx to output Basic models without SADX additions\n");
                Console.WriteLine("Supported file types: sa1lvl, sa2lvl, sa1mdl, sa2mdl, saanim\n");
                Console.WriteLine("Examples:");
                Console.WriteLine("StructExporter mylevel.sa1lvl");
                Console.WriteLine("StructExporter mylevel.sa1lvl D:\\mylevel.c");
                Console.WriteLine("StructExporter mymodel.sa1mdl D:\\mymodel.c -nodx\n");
                Console.WriteLine("You can also drag your file onto StructExporter.exe to get it converted.");
                Console.WriteLine("Press ENTER to exit");
                Console.ReadLine();
                return;
            }
            //Args list: game, filename, key, type, address, [address2/count], [language], [name]
            filename   = args[0];
            outputfile = Path.GetFileNameWithoutExtension(filename) + ".c";
            if (args.Length > 1)
            {
                if (args[args.Length - 1] == "-nodx")
                {
                    dx = false;
                }
                if (args[1] != "-nodx")
                {
                    outputfile = args[1];
                }
            }
            byte[] file = File.ReadAllBytes(filename);
            extension = Path.GetExtension(filename);
            switch (extension)
            {
            case ".sa2lvl":
            case ".sa1lvl":
                LandTable     land   = LandTable.LoadFromFile(filename);
                List <string> labels = new List <string>()
                {
                    land.Name
                };
                using (StreamWriter sw = File.CreateText(outputfile))
                {
                    sw.Write("/* Sonic Adventure ");
                    LandTableFormat fmt = land.Format;
                    switch (land.Format)
                    {
                    case LandTableFormat.SA1:
                    case LandTableFormat.SADX:
                        if (dx)
                        {
                            sw.Write("DX");
                            fmt = LandTableFormat.SADX;
                        }
                        else
                        {
                            sw.Write("1");
                            fmt = LandTableFormat.SA1;
                        }
                        break;

                    case LandTableFormat.SA2:
                        sw.Write("2");
                        fmt = LandTableFormat.SA2;
                        break;

                    case LandTableFormat.SA2B:
                        sw.Write("2 Battle");
                        fmt = LandTableFormat.SA2B;
                        break;
                    }
                    sw.WriteLine(" LandTable");
                    sw.WriteLine(" * ");
                    sw.WriteLine(" * Generated by StructExporter");
                    sw.WriteLine(" * ");
                    if (!string.IsNullOrEmpty(land.Description))
                    {
                        sw.Write(" * Description: ");
                        sw.WriteLine(land.Description);
                        sw.WriteLine(" * ");
                    }
                    if (!string.IsNullOrEmpty(land.Author))
                    {
                        sw.Write(" * Author: ");
                        sw.WriteLine(land.Author);
                        sw.WriteLine(" * ");
                    }
                    sw.WriteLine(" */");
                    sw.WriteLine();
                    land.ToStructVariables(sw, fmt, labels, null);
                }
                break;

            case ".sa1mdl":
            case ".sa2mdl":
                ModelFile         modelFile  = new ModelFile(filename);
                NJS_OBJECT        model      = modelFile.Model;
                List <NJS_MOTION> animations = new List <NJS_MOTION>(modelFile.Animations);
                using (StreamWriter sw = File.CreateText(outputfile))
                {
                    sw.Write("/* NINJA ");
                    switch (modelFile.Format)
                    {
                    case ModelFormat.Basic:
                    case ModelFormat.BasicDX:
                        if (dx)
                        {
                            sw.Write("Basic (with Sonic Adventure DX additions)");
                        }
                        else
                        {
                            sw.Write("Basic");
                        }
                        break;

                    case ModelFormat.Chunk:
                        sw.Write("Chunk");
                        break;

                    case ModelFormat.GC:
                        sw.Write("GC");
                        break;
                    }
                    sw.WriteLine(" model");
                    sw.WriteLine(" * ");
                    sw.WriteLine(" * Generated by StructExporter");
                    sw.WriteLine(" * ");
                    if (modelFile != null)
                    {
                        if (!string.IsNullOrEmpty(modelFile.Description))
                        {
                            sw.Write(" * Description: ");
                            sw.WriteLine(modelFile.Description);
                            sw.WriteLine(" * ");
                        }
                        if (!string.IsNullOrEmpty(modelFile.Author))
                        {
                            sw.Write(" * Author: ");
                            sw.WriteLine(modelFile.Author);
                            sw.WriteLine(" * ");
                        }
                    }
                    sw.WriteLine(" */");
                    sw.WriteLine();
                    List <string> labels_m = new List <string>()
                    {
                        model.Name
                    };
                    model.ToStructVariables(sw, dx, labels_m, null);
                    foreach (NJS_MOTION anim in animations)
                    {
                        anim.ToStructVariables(sw);
                    }
                }
                break;

            case ".saanim":
                NJS_MOTION animation = NJS_MOTION.Load(filename);
                using (StreamWriter sw = File.CreateText(outputfile))
                {
                    sw.WriteLine("/* NINJA Motion");
                    sw.WriteLine(" * ");
                    sw.WriteLine(" * Generated by StructExporter");
                    sw.WriteLine(" * ");
                    sw.WriteLine(" */");
                    sw.WriteLine();
                    animation.ToStructVariables(sw);
                    break;
                }
            }
        }
Esempio n. 32
0
        public static void ExportCPP(DllIniData IniData,
                                     Dictionary <string, bool> itemsToExport, string fileName)
        {
            using (TextWriter writer = File.CreateText(fileName))
            {
                bool            SA2      = IniData.Game == SA_Tools.SplitDLL.Game.SA2B;
                ModelFormat     modelfmt = SA2 ? ModelFormat.Chunk : ModelFormat.BasicDX;
                LandTableFormat landfmt  = SA2 ? LandTableFormat.SA2 : LandTableFormat.SADX;
                writer.WriteLine("// Generated by SA Tools DLL Mod Generator");
                writer.WriteLine();
                if (SA2)
                {
                    writer.WriteLine("#include \"SA2ModLoader.h\"");
                }
                else
                {
                    writer.WriteLine("#include \"SADXModLoader.h\"");
                }
                writer.WriteLine();
                List <string>             labels   = new List <string>();
                Dictionary <string, uint> texlists = new Dictionary <string, uint>();
                foreach (KeyValuePair <string, FileTypeHash> item in
                         IniData.Files.Where(i => itemsToExport[i.Key]))
                {
                    switch (item.Value.Type)
                    {
                    case "landtable":
                        LandTable tbl = LandTable.LoadFromFile(item.Key);
                        texlists.Add(tbl.Name, tbl.TextureList);
                        tbl.ToStructVariables(writer, landfmt, new List <string>());
                        labels.AddRange(tbl.GetLabels());
                        break;

                    case "model":
                        NJS_OBJECT mdl = new ModelFile(item.Key).Model;
                        mdl.ToStructVariables(writer, modelfmt == ModelFormat.BasicDX, new List <string>());
                        labels.AddRange(mdl.GetLabels());
                        break;

                    case "basicmodel":
                    case "chunkmodel":
                    case "gcmodel":
                        mdl = new ModelFile(item.Key).Model;
                        mdl.ToStructVariables(writer, false, new List <string>());
                        labels.AddRange(mdl.GetLabels());
                        break;

                    case "basicdxmodel":
                        mdl = new ModelFile(item.Key).Model;
                        mdl.ToStructVariables(writer, true, new List <string>());
                        labels.AddRange(mdl.GetLabels());
                        break;

                    case "animation":
                        NJS_MOTION ani = NJS_MOTION.Load(item.Key);
                        ani.ToStructVariables(writer);
                        labels.Add(ani.Name);
                        break;
                    }
                    writer.WriteLine();
                }
                foreach (var item in IniData.DataItems.Where(i => itemsToExport[i.Filename]))
                {
                    switch (item.Type)
                    {
                    case "animindexlist":
                    {
                        SortedDictionary <short, NJS_MOTION> anims = new SortedDictionary <short, NJS_MOTION>();
                        foreach (string file in Directory.GetFiles(item.Filename, "*.saanim"))
                        {
                            if (short.TryParse(Path.GetFileNameWithoutExtension(file), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out short i))
                            {
                                anims.Add(i, NJS_MOTION.Load(file));
                            }
                        }
                        foreach (KeyValuePair <short, NJS_MOTION> obj in anims)
                        {
                            obj.Value.ToStructVariables(writer);
                            writer.WriteLine();
                        }
                        writer.WriteLine("AnimationIndex {0}[] = {{", item.Export);
                        List <string> objs = new List <string>(anims.Count);
                        foreach (KeyValuePair <short, NJS_MOTION> obj in anims)
                        {
                            objs.Add($"{{ {obj.Key}, {obj.Value.ModelParts}, &{obj.Value.Name} }}");
                        }
                        objs.Add("{ -1 }");
                        writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                        writer.WriteLine("};");
                    }
                    break;

                    case "charaobjectdatalist":
                    {
                        foreach (string file in Directory.GetFiles(item.Filename, "*.sa2mdl"))
                        {
                            new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>());
                            writer.WriteLine();
                        }
                        foreach (string file in Directory.GetFiles(item.Filename, "*.saanim"))
                        {
                            NJS_MOTION.Load(file).ToStructVariables(writer);
                            writer.WriteLine();
                        }
                        var data = IniSerializer.Deserialize <CharaObjectData[]>(Path.Combine(item.Filename, "info.ini"));
                        writer.WriteLine("CharaObjectData {0}[] = {{", item.Export);
                        List <string> objs = new List <string>(data.Length);
                        foreach (var obj in data)
                        {
                            objs.Add(obj.ToStruct());
                        }
                        writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                        writer.WriteLine("};");
                    }
                    break;

                    case "kartspecialinfolist":
                    {
                        foreach (string file in Directory.GetFiles(item.Filename, "*.sa2mdl"))
                        {
                            new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>());
                            writer.WriteLine();
                        }
                        var data = IniSerializer.Deserialize <KartSpecialInfo[]>(Path.Combine(item.Filename, "info.ini"));
                        writer.WriteLine("KartSpecialInfo {0}[] = {{", item.Export);
                        List <string> objs = new List <string>(data.Length);
                        for (int i = 0; i < data.Length; i++)
                        {
                            KartSpecialInfo obj = data[i];
                            objs.Add(obj.ToStruct());
                            texlists.Add($"{item.Export}[{i}]", obj.TexList);
                        }
                        writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                        writer.WriteLine("};");
                    }
                    break;

                    case "kartmodelsarray":
                    {
                        foreach (string file in Directory.GetFiles(item.Filename, "*.sa2bmdl"))
                        {
                            new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>());
                            writer.WriteLine();
                        }
                        foreach (string file in Directory.GetFiles(item.Filename, "*.sa1mdl"))
                        {
                            new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>());
                            writer.WriteLine();
                        }
                        var data = IniSerializer.Deserialize <CharaObjectData[]>(Path.Combine(item.Filename, "info.ini"));
                        writer.WriteLine("KartModelsArray {0}[] = {{", item.Export);
                        List <string> objs = new List <string>(data.Length);
                        foreach (var obj in data)
                        {
                            objs.Add(obj.ToStruct());
                        }
                        writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                        writer.WriteLine("};");
                    }
                    break;

                    case "motiontable":
                    {
                        foreach (string file in Directory.GetFiles(item.Filename, "*.saanim"))
                        {
                            NJS_MOTION.Load(file).ToStructVariables(writer);
                            writer.WriteLine();
                        }
                        var data = IniSerializer.Deserialize <MotionTableEntry[]>(Path.Combine(item.Filename, "info.ini"));
                        writer.WriteLine("MotionTableEntry {0}[] = {{", item.Export);
                        List <string> objs = new List <string>(data.Length);
                        foreach (var obj in data)
                        {
                            objs.Add(obj.ToStruct());
                        }
                        writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                        writer.WriteLine("};");
                    }
                    break;
                    }
                }
                writer.WriteLine("extern \"C\" __declspec(dllexport) void __cdecl Init(const char *path, const HelperFunctions &helperFunctions)");
                writer.WriteLine("{");
                writer.WriteLine("\tHMODULE handle = GetModuleHandle(L\"{0}\");", IniData.Name);
                List <string> exports = new List <string>(IniData.Items.Where(item => labels.Contains(item.Label)).Select(item => item.Export).Distinct());
                foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key)))
                {
                    writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key);
                }
                foreach (DllItemInfo item in IniData.Items.Where(item => labels.Contains(item.Label)))
                {
                    if (typemap[IniData.Exports[item.Export]].EndsWith("**"))
                    {
                        writer.WriteLine("\t{0} = &{1};", item.ToString(), item.Label);
                    }
                    else
                    {
                        writer.WriteLine("\t*{0} = {1};", item.ToString(), item.Label);
                    }
                }
                foreach (var item in IniData.DataItems.Where(item => itemsToExport[item.Filename]))
                {
                    switch (item.Type)
                    {
                    case "animindexlist":
                    case "charaobjectdatalist":
                    case "kartspecialinfolist":
                    case "kartmodelsarray":
                        writer.WriteLine("\tHookExport(handle, \"{0}\", {0});", item.Export);
                        break;

                    default:
                        writer.WriteLine("\t{0}{1}_exp = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Type], item.Export);
                        writer.WriteLine("\t*{0}_exp = {0};", item.Export);
                        break;
                    }
                }
                if (texlists.Count > 0 && IniData.TexLists != null && IniData.TexLists.Items != null)
                {
                    exports = new List <string>(IniData.TexLists.Where(item => texlists.Values.Contains(item.Key)).Select(item => item.Value.Export).Distinct());
                    foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key)))
                    {
                        writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key);
                    }
                    foreach (KeyValuePair <string, uint> item in texlists.Where(item => IniData.TexLists.ContainsKey(item.Value)))
                    {
                        DllTexListInfo tex = IniData.TexLists[item.Value];
                        string         str;
                        if (tex.Index.HasValue)
                        {
                            str = $"{tex.Export}[{tex.Index.Value}]";
                        }
                        else
                        {
                            str = tex.Export;
                        }
                        writer.WriteLine("\t{0}.TexList = {1};", item.Key, str);
                    }
                }
                writer.WriteLine("}");
                writer.WriteLine();
                writer.WriteLine("extern \"C\" __declspec(dllexport) const ModInfo {0}ModInfo = {{ ModLoaderVer }};", SA2 ? "SA2" : "SADX");
            }
        }
Esempio n. 33
0
 public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, Attach> attaches)
     : this(file, address, imageBase, format, new Dictionary <int, string>(), attaches)
 {
 }
Esempio n. 34
0
 public LandTable(byte[] file, int address, uint imageBase, LandTableFormat format)
     : this(file, address, imageBase, format, new Dictionary<int, string>())
 {
 }
Esempio n. 35
0
        public byte[] GetBytes(uint imageBase, LandTableFormat format, Dictionary <string, uint> labels, out uint address)
        {
            List <byte> result = new List <byte>();

            byte[]     tmpbyte;
            uint[]     colmdladdrs = new uint[COL.Count];
            uint       tmpaddr;
            List <COL> cnk = new List <COL>();
            List <COL> bas = new List <COL>();

            foreach (COL item in COL)
            {
                if (item.Model.Attach is BasicAttach)
                {
                    bas.Add(item);
                }
                else
                {
                    cnk.Add(item);
                }
            }
            COL.Clear();
            COL.AddRange(cnk);
            COL.AddRange(bas);
            for (int i = 0; i < COL.Count; i++)
            {
                if (labels.ContainsKey(COL[i].Model.Name))
                {
                    colmdladdrs[i] = labels[COL[i].Model.Name];
                }
                else
                {
                    result.Align(4);
                    tmpbyte        = COL[i].Model.GetBytes(imageBase + (uint)result.Count, format == LandTableFormat.SADX, labels, out tmpaddr);
                    colmdladdrs[i] = tmpaddr + (uint)result.Count + imageBase;
                    result.AddRange(tmpbyte);
                }
            }
            uint[] animmdladdrs = new uint[Anim.Count];
            uint[] animaniaddrs = new uint[Anim.Count];
            for (int i = 0; i < Anim.Count; i++)
            {
                if (labels.ContainsKey(Anim[i].Model.Name))
                {
                    animmdladdrs[i] = labels[Anim[i].Model.Name];
                }
                else
                {
                    result.Align(4);
                    tmpbyte = Anim[i].Model.GetBytes(imageBase + (uint)result.Count, format == LandTableFormat.SADX, labels,
                                                     out tmpaddr);
                    animmdladdrs[i] = tmpaddr + (uint)result.Count + imageBase;
                    result.AddRange(tmpbyte);
                }
                if (labels.ContainsKey(Anim[i].Animation.Name))
                {
                    animaniaddrs[i] = labels[Anim[i].Animation.Name];
                }
                else
                {
                    result.Align(4);
                    tmpbyte         = Anim[i].Animation.WriteHeader(imageBase + (uint)result.Count, animmdladdrs[i], labels, out tmpaddr);
                    animaniaddrs[i] = tmpaddr + (uint)result.Count + imageBase;
                    result.AddRange(tmpbyte);
                }
            }
            uint coladdr;

            if (COL.Count > 0)
            {
                if (labels.ContainsKey(COLName))
                {
                    coladdr = labels[COLName];
                }
                else
                {
                    coladdr = imageBase + (uint)result.Count;
                    labels.Add(COLName, coladdr);
                    for (int i = 0; i < COL.Count; i++)
                    {
                        result.Align(4);
                        result.AddRange(COL[i].GetBytes(imageBase + (uint)result.Count, colmdladdrs[i], format));
                    }
                }
            }
            else
            {
                coladdr = 0;
            }
            uint animaddr;

            if (Anim.Count > 0)
            {
                if (labels.ContainsKey(AnimName))
                {
                    animaddr = labels[AnimName];
                }
                else
                {
                    animaddr = imageBase + (uint)result.Count;
                    labels.Add(AnimName, animaddr);
                    for (int i = 0; i < Anim.Count; i++)
                    {
                        result.Align(4);
                        result.AddRange(Anim[i].GetBytes(imageBase + (uint)result.Count, animmdladdrs[i], animaniaddrs[i]));
                    }
                }
            }
            else
            {
                animaddr = 0;
            }
            result.Align(4);
            uint texnameaddr = 0;

            if (TextureFileName != null)
            {
                texnameaddr = imageBase + (uint)result.Count;
                result.AddRange(Encoding.ASCII.GetBytes(TextureFileName));
                result.Add(0);
            }
            result.Align(4);
            address = (uint)result.Count;
            result.AddRange(ByteConverter.GetBytes((ushort)COL.Count));
            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                result.AddRange(ByteConverter.GetBytes((ushort)Anim.Count));
                result.AddRange(ByteConverter.GetBytes(Flags));
                result.AddRange(ByteConverter.GetBytes(Unknown1));
                result.AddRange(ByteConverter.GetBytes(coladdr));
                result.AddRange(ByteConverter.GetBytes(animaddr));
                result.AddRange(ByteConverter.GetBytes(texnameaddr));
                result.AddRange(ByteConverter.GetBytes(TextureList));
                result.AddRange(ByteConverter.GetBytes(Unknown2));
                result.AddRange(ByteConverter.GetBytes(Unknown3));
                break;

            case LandTableFormat.SA2:
                result.AddRange(ByteConverter.GetBytes((ushort)cnk.Count));
                result.AddRange(new byte[8]);                         // TODO: figure out what these do
                result.AddRange(ByteConverter.GetBytes(Unknown1));
                result.AddRange(ByteConverter.GetBytes(coladdr));
                result.AddRange(ByteConverter.GetBytes(animaddr));
                result.AddRange(ByteConverter.GetBytes(texnameaddr));
                result.AddRange(ByteConverter.GetBytes(TextureList));
                break;
            }
            labels.Add(Name, address + imageBase);
            return(result.ToArray());
        }
Esempio n. 36
0
 public COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary<int, string> labels)
     : this(file, address, imageBase, format, labels, false)
 {
 }
Esempio n. 37
0
 public byte[] GetBytes(uint imageBase, LandTableFormat format)
 {
     return(GetBytes(imageBase, format, out uint address));
 }
Esempio n. 38
0
 public COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, Dictionary <int, Attach> attaches)
     : this(file, address, imageBase, format, labels, false, attaches)
 {
 }
Esempio n. 39
0
 public byte[] GetBytes(uint imageBase, uint modelptr, LandTableFormat format)
 {
     List<byte> result = new List<byte>();
     result.AddRange(Bounds.GetBytes());
     switch (format)
     {
         case LandTableFormat.SA1:
         case LandTableFormat.SADX:
             result.AddRange(ByteConverter.GetBytes(Unknown1));
             result.AddRange(ByteConverter.GetBytes(Unknown2));
             result.AddRange(ByteConverter.GetBytes(modelptr));
             result.AddRange(ByteConverter.GetBytes(Unknown3));
             break;
         case LandTableFormat.SA2:
             result.AddRange(ByteConverter.GetBytes(modelptr));
             result.AddRange(ByteConverter.GetBytes(Unknown2));
             result.AddRange(ByteConverter.GetBytes(Unknown3));
             break;
     }
     result.AddRange(ByteConverter.GetBytes(Flags));
     return result.ToArray();
 }
Esempio n. 40
0
        public COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, bool?forceBasic, Dictionary <int, Attach> attaches)
        {
            Bounds = new BoundingSphere(file, address);
            ModelFormat mfmt = 0;

            switch (format)
            {
            case LandTableFormat.SA1:
                mfmt = ModelFormat.Basic;
                break;

            case LandTableFormat.SADX:
                mfmt = ModelFormat.BasicDX;
                break;

            case LandTableFormat.SA2:
                if (forceBasic.HasValue && forceBasic.Value)
                {
                    mfmt = ModelFormat.Basic;
                }
                else
                {
                    mfmt = ModelFormat.Chunk;
                }
                break;

            case LandTableFormat.SA2B:
                if (forceBasic.HasValue && forceBasic.Value)
                {
                    mfmt = ModelFormat.Basic;
                }
                else
                {
                    mfmt = ModelFormat.GC;
                }
                break;
            }
            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                WidthY = ByteConverter.ToSingle(file, address + 0x10);
                WidthZ = ByteConverter.ToSingle(file, address + 0x14);
                uint tmpaddr = ByteConverter.ToUInt32(file, address + 0x18) - imageBase;
                Model     = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels, attaches);
                BlockBits = ByteConverter.ToInt32(file, address + 0x1C);
                Flags     = ByteConverter.ToInt32(file, address + 0x20);
                break;

            case LandTableFormat.SA2:
            case LandTableFormat.SA2B:
                Flags = ByteConverter.ToInt32(file, address + 0x1C);
                if (!forceBasic.HasValue && Flags >= 0)
                {
                    mfmt = ModelFormat.Basic;
                }
                tmpaddr   = ByteConverter.ToUInt32(file, address + 0x10) - imageBase;
                Model     = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels, attaches);
                WidthZ    = ByteConverter.ToInt32(file, address + 0x14);
                BlockBits = ByteConverter.ToInt32(file, address + 0x18);
                break;
            }
        }
Esempio n. 41
0
        public static int SplitFile(string datafilename, string inifilename, string projectFolderName)
        {
            try
            {
                byte[]  datafile = File.ReadAllBytes(datafilename);
                IniData inifile  = IniSerializer.Deserialize <IniData>(inifilename);
                if (inifile.MD5 != null && inifile.MD5.Count > 0)
                {
                    string datahash = HelperFunctions.FileHash(datafile);
                    if (!inifile.MD5.Any(h => h.Equals(datahash, StringComparison.OrdinalIgnoreCase)))
                    {
                        Console.WriteLine("The file {0} is not valid for use with the INI {1}.", datafilename, inifilename);
                        return((int)ERRORVALUE.InvalidDataMapping);
                    }
                }
                ByteConverter.BigEndian      = SonicRetro.SAModel.ByteConverter.BigEndian = inifile.BigEndian;
                Environment.CurrentDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetDirectoryName(datafilename));
                if (inifile.Compressed)
                {
                    datafile = FraGag.Compression.Prs.Decompress(datafile);
                }
                uint imageBase = HelperFunctions.SetupEXE(ref datafile) ?? inifile.ImageBase.Value;
                if (Path.GetExtension(datafilename).Equals(".rel", StringComparison.OrdinalIgnoreCase))
                {
                    HelperFunctions.FixRELPointers(datafile);
                }
                bool            SA2      = inifile.Game == Game.SA2 | inifile.Game == Game.SA2B;
                ModelFormat     modelfmt = 0;
                LandTableFormat landfmt  = 0;
                switch (inifile.Game)
                {
                case Game.SA1:
                    modelfmt = ModelFormat.Basic;
                    landfmt  = LandTableFormat.SA1;
                    break;

                case Game.SADX:
                    modelfmt = ModelFormat.BasicDX;
                    landfmt  = LandTableFormat.SADX;
                    break;

                case Game.SA2:
                case Game.SA2B:
                    modelfmt = ModelFormat.Chunk;
                    landfmt  = LandTableFormat.SA2;
                    break;
                }
                int itemcount = 0;
                Dictionary <string, MasterObjectListEntry>     masterobjlist = new Dictionary <string, MasterObjectListEntry>();
                Dictionary <string, Dictionary <string, int> > objnamecounts = new Dictionary <string, Dictionary <string, int> >();
                Stopwatch timer = new Stopwatch();
                timer.Start();
                foreach (KeyValuePair <string, SA_Tools.FileInfo> item in inifile.Files)
                {
                    if (string.IsNullOrEmpty(item.Key))
                    {
                        continue;
                    }
                    string                      filedesc         = item.Key;
                    SA_Tools.FileInfo           data             = item.Value;
                    Dictionary <string, string> customProperties = data.CustomProperties;
                    string                      type             = data.Type;
                    int  address = data.Address;
                    bool nohash  = false;

                    string fileOutputPath = string.Concat(projectFolderName, data.Filename);
                    Console.WriteLine(item.Key + ": " + data.Address.ToString("X") + " → " + fileOutputPath);
                    Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                    switch (type)
                    {
                    case "landtable":
                        new LandTable(datafile, address, imageBase, landfmt)
                        {
                            Description = item.Key
                        }.SaveToFile(fileOutputPath, landfmt);
                        break;

                    case "model":
                    {
                        NJS_OBJECT mdl     = new NJS_OBJECT(datafile, address, imageBase, modelfmt);
                        string[]   mdlanis = new string[0];
                        if (customProperties.ContainsKey("animations"))
                        {
                            mdlanis = customProperties["animations"].Split(',');
                        }
                        string[] mdlmorphs = new string[0];
                        if (customProperties.ContainsKey("morphs"))
                        {
                            mdlmorphs = customProperties["morphs"].Split(',');
                        }
                        ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, mdlmorphs, null, item.Key, null, modelfmt);
                    }
                    break;

                    case "basicmodel":
                    {
                        NJS_OBJECT mdl     = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic);
                        string[]   mdlanis = new string[0];
                        if (customProperties.ContainsKey("animations"))
                        {
                            mdlanis = customProperties["animations"].Split(',');
                        }
                        string[] mdlmorphs = new string[0];
                        if (customProperties.ContainsKey("morphs"))
                        {
                            mdlmorphs = customProperties["morphs"].Split(',');
                        }
                        ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, mdlmorphs, null, item.Key, null, ModelFormat.Basic);
                    }
                    break;

                    case "basicdxmodel":
                    {
                        NJS_OBJECT mdl     = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX);
                        string[]   mdlanis = new string[0];
                        if (customProperties.ContainsKey("animations"))
                        {
                            mdlanis = customProperties["animations"].Split(',');
                        }
                        string[] mdlmorphs = new string[0];
                        if (customProperties.ContainsKey("morphs"))
                        {
                            mdlmorphs = customProperties["morphs"].Split(',');
                        }
                        ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, mdlmorphs, null, item.Key, null, ModelFormat.BasicDX);
                    }
                    break;

                    case "chunkmodel":
                    {
                        NJS_OBJECT mdl     = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk);
                        string[]   mdlanis = new string[0];
                        if (customProperties.ContainsKey("animations"))
                        {
                            mdlanis = customProperties["animations"].Split(',');
                        }
                        string[] mdlmorphs = new string[0];
                        if (customProperties.ContainsKey("morphs"))
                        {
                            mdlmorphs = customProperties["morphs"].Split(',');
                        }
                        ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, mdlmorphs, null, item.Key, null, ModelFormat.Chunk);
                    }
                    break;

                    case "action":
                    {
                        NJS_ACTION ani = new NJS_ACTION(datafile, address, imageBase, modelfmt);
                        ani.Animation.Name = filedesc;
                        ani.Animation.Save(fileOutputPath);
                    }
                    break;

                    case "animation":
                        new NJS_MOTION(datafile, address, imageBase, int.Parse(customProperties["numparts"], NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, NumberFormatInfo.InvariantInfo))
                        {
                            Name = filedesc
                        }
                        .Save(fileOutputPath);
                        break;

                    case "objlist":
                    {
                        ObjectListEntry[] objs = ObjectList.Load(datafile, address, imageBase, SA2);
                        if (inifile.MasterObjectList != null)
                        {
                            foreach (ObjectListEntry obj in objs)
                            {
                                if (!masterobjlist.ContainsKey(obj.CodeString))
                                {
                                    masterobjlist.Add(obj.CodeString, new MasterObjectListEntry(obj));
                                }
                                if (!objnamecounts.ContainsKey(obj.CodeString))
                                {
                                    objnamecounts.Add(obj.CodeString, new Dictionary <string, int>()
                                        {
                                            { obj.Name, 1 }
                                        });
                                }
                                else if (!objnamecounts[obj.CodeString].ContainsKey(obj.Name))
                                {
                                    objnamecounts[obj.CodeString].Add(obj.Name, 1);
                                }
                                else
                                {
                                    objnamecounts[obj.CodeString][obj.Name]++;
                                }
                            }
                        }
                        objs.Save(fileOutputPath);
                    }
                    break;

                    case "startpos":
                        if (SA2)
                        {
                            SA2StartPosList.Load(datafile, address).Save(fileOutputPath);
                        }
                        else
                        {
                            SA1StartPosList.Load(datafile, address).Save(fileOutputPath);
                        }
                        break;

                    case "texlist":
                        TextureList.Load(datafile, address, imageBase).Save(fileOutputPath);
                        break;

                    case "leveltexlist":
                        new LevelTextureList(datafile, address, imageBase).Save(fileOutputPath);
                        break;

                    case "triallevellist":
                        TrialLevelList.Save(TrialLevelList.Load(datafile, address, imageBase), fileOutputPath);
                        break;

                    case "bosslevellist":
                        BossLevelList.Save(BossLevelList.Load(datafile, address), fileOutputPath);
                        break;

                    case "fieldstartpos":
                        FieldStartPosList.Load(datafile, address).Save(fileOutputPath);
                        break;

                    case "soundtestlist":
                        SoundTestList.Load(datafile, address, imageBase).Save(fileOutputPath);
                        break;

                    case "musiclist":
                    {
                        int muscnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        MusicList.Load(datafile, address, imageBase, muscnt).Save(fileOutputPath);
                    }
                    break;

                    case "soundlist":
                        SoundList.Load(datafile, address, imageBase).Save(fileOutputPath);
                        break;

                    case "stringarray":
                    {
                        int       cnt  = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        Languages lang = Languages.Japanese;
                        if (data.CustomProperties.ContainsKey("language"))
                        {
                            lang = (Languages)Enum.Parse(typeof(Languages), data.CustomProperties["language"], true);
                        }
                        StringArray.Load(datafile, address, imageBase, cnt, lang).Save(fileOutputPath);
                    }
                    break;

                    case "nextlevellist":
                        NextLevelList.Load(datafile, address).Save(fileOutputPath);
                        break;

                    case "cutscenetext":
                    {
                        int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        new CutsceneText(datafile, address, imageBase, cnt).Save(fileOutputPath, out string[] hashes);
                        data.MD5Hash = string.Join(",", hashes);
                        nohash       = true;
                    }
                    break;

                    case "recapscreen":
                    {
                        int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        RecapScreenList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath, out string[][] hashes);
                        string[] hash2 = new string[hashes.Length];
                        for (int i = 0; i < hashes.Length; i++)
                        {
                            hash2[i] = string.Join(",", hashes[i]);
                        }
                        data.MD5Hash = string.Join(":", hash2);
                        nohash       = true;
                    }
                    break;

                    case "npctext":
                    {
                        int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        NPCTextList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath, out string[][] hashes);
                        string[] hash2 = new string[hashes.Length];
                        for (int i = 0; i < hashes.Length; i++)
                        {
                            hash2[i] = string.Join(",", hashes[i]);
                        }
                        data.MD5Hash = string.Join(":", hash2);
                        nohash       = true;
                    }
                    break;

                    case "levelclearflags":
                        LevelClearFlagList.Save(LevelClearFlagList.Load(datafile, address), fileOutputPath);
                        break;

                    case "deathzone":
                    {
                        List <DeathZoneFlags> flags = new List <DeathZoneFlags>();
                        string        path          = Path.GetDirectoryName(fileOutputPath);
                        List <string> hashes        = new List <string>();
                        int           num           = 0;
                        while (ByteConverter.ToUInt32(datafile, address + 4) != 0)
                        {
                            flags.Add(new DeathZoneFlags(datafile, address));
                            string file = Path.Combine(path, num++.ToString(NumberFormatInfo.InvariantInfo) + (modelfmt == ModelFormat.Chunk ? ".sa2mdl" : ".sa1mdl"));
                            ModelFile.CreateFile(file, new NJS_OBJECT(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, modelfmt), null, null, null, null, null, modelfmt);
                            hashes.Add(HelperFunctions.FileHash(file));
                            address += 8;
                        }
                        flags.ToArray().Save(fileOutputPath);
                        hashes.Insert(0, HelperFunctions.FileHash(fileOutputPath));
                        data.MD5Hash = string.Join(",", hashes.ToArray());
                        nohash       = true;
                    }
                    break;

                    case "skyboxscale":
                    {
                        int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        SkyboxScaleList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath);
                    }
                    break;

                    case "stageselectlist":
                    {
                        int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        StageSelectLevelList.Load(datafile, address, cnt).Save(fileOutputPath);
                    }
                    break;

                    case "levelrankscores":
                        LevelRankScoresList.Load(datafile, address).Save(fileOutputPath);
                        break;

                    case "levelranktimes":
                        LevelRankTimesList.Load(datafile, address).Save(fileOutputPath);
                        break;

                    case "endpos":
                        SA2EndPosList.Load(datafile, address).Save(fileOutputPath);
                        break;

                    case "animationlist":
                    {
                        int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        SA2AnimationInfoList.Load(datafile, address, cnt).Save(fileOutputPath);
                    }
                    break;

                    case "levelpathlist":
                    {
                        List <string> hashes = new List <string>();
                        ushort        lvlnum = (ushort)ByteConverter.ToUInt32(datafile, address);
                        while (lvlnum != 0xFFFF)
                        {
                            int ptr = ByteConverter.ToInt32(datafile, address + 4);
                            if (ptr != 0)
                            {
                                ptr = (int)((uint)ptr - imageBase);
                                SA1LevelAct level  = new SA1LevelAct(lvlnum);
                                string      lvldir = Path.Combine(fileOutputPath, level.ToString());
                                PathList.Load(datafile, ptr, imageBase).Save(lvldir, out string[] lvlhashes);
                                hashes.Add(level.ToString() + ":" + string.Join(",", lvlhashes));
                            }
                            address += 8;
                            lvlnum   = (ushort)ByteConverter.ToUInt32(datafile, address);
                        }
                        data.MD5Hash = string.Join("|", hashes.ToArray());
                        nohash       = true;
                    }
                    break;

                    case "pathlist":
                    {
                        PathList.Load(datafile, address, imageBase).Save(fileOutputPath, out string[] hashes);
                        data.MD5Hash = string.Join(",", hashes.ToArray());
                        nohash       = true;
                    }
                    break;

                    case "stagelightdatalist":
                        SA1StageLightDataList.Load(datafile, address).Save(fileOutputPath);
                        break;

                    case "weldlist":
                        WeldList.Load(datafile, address, imageBase).Save(fileOutputPath);
                        break;

                    case "bmitemattrlist":
                        BlackMarketItemAttributesList.Load(datafile, address, imageBase).Save(fileOutputPath);
                        break;

                    case "creditstextlist":
                        CreditsTextList.Load(datafile, address, imageBase).Save(fileOutputPath);
                        break;

                    case "animindexlist":
                    {
                        Directory.CreateDirectory(fileOutputPath);
                        List <string> hashes = new List <string>();
                        int           i      = ByteConverter.ToInt16(datafile, address);
                        while (i != -1)
                        {
                            new NJS_MOTION(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, ByteConverter.ToInt16(datafile, address + 2))
                            .Save(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                            hashes.Add(i.ToString(NumberFormatInfo.InvariantInfo) + ":" + HelperFunctions.FileHash(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"));
                            address += 8;
                            i        = ByteConverter.ToInt16(datafile, address);
                        }
                        data.MD5Hash = string.Join("|", hashes.ToArray());
                        nohash       = true;
                    }
                    break;

                    case "storysequence":
                        SA2StoryList.Load(datafile, address).Save(fileOutputPath);
                        break;

                    default:                             // raw binary
                    {
                        byte[] bin = new byte[int.Parse(customProperties["size"], NumberStyles.HexNumber)];
                        Array.Copy(datafile, address, bin, 0, bin.Length);
                        File.WriteAllBytes(fileOutputPath, bin);
                    }
                    break;
                    }
                    if (!nohash)
                    {
                        data.MD5Hash = HelperFunctions.FileHash(fileOutputPath);
                    }
                    itemcount++;
                }
                if (inifile.MasterObjectList != null)
                {
                    foreach (KeyValuePair <string, MasterObjectListEntry> obj in masterobjlist)
                    {
                        KeyValuePair <string, int> name = new KeyValuePair <string, int>();
                        foreach (KeyValuePair <string, int> it in objnamecounts[obj.Key])
                        {
                            if (it.Value > name.Value)
                            {
                                name = it;
                            }
                        }
                        obj.Value.Name  = name.Key;
                        obj.Value.Names = objnamecounts[obj.Key].Select((it) => it.Key).ToArray();
                    }

                    string masterObjectListOutputPath = string.Concat(projectFolderName, inifile.MasterObjectList);

                    IniSerializer.Serialize(masterobjlist, masterObjectListOutputPath);
                }
                IniSerializer.Serialize(inifile, Path.Combine(projectFolderName, Path.GetFileNameWithoutExtension(datafilename) + "_data.ini"));
                timer.Stop();
                Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds.");
                Console.WriteLine();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                Console.WriteLine("Press any key to exit.");
                Console.ReadLine();
                return((int)ERRORVALUE.UnhandledException);
            }

            return((int)ERRORVALUE.Success);
        }
Esempio n. 42
0
 public byte[] GetBytes(uint imageBase, LandTableFormat format, Dictionary<string, uint> labels, out uint address)
 {
     List<byte> result = new List<byte>();
     byte[] tmpbyte;
     uint[] colmdladdrs = new uint[COL.Count];
     uint tmpaddr;
     List<COL> cnk = new List<COL>();
     List<COL> bas = new List<COL>();
     foreach (COL item in COL)
     {
         if (item.Model.Attach is BasicAttach)
             bas.Add(item);
         else
             cnk.Add(item);
     }
     COL.Clear();
     COL.AddRange(cnk);
     COL.AddRange(bas);
     for (int i = 0; i < COL.Count; i++)
     {
         if (labels.ContainsKey(COL[i].Model.Name))
             colmdladdrs[i] = labels[COL[i].Model.Name];
         else
         {
             result.Align(4);
             tmpbyte = COL[i].Model.GetBytes(imageBase + (uint)result.Count, format == LandTableFormat.SADX, labels, out tmpaddr);
             colmdladdrs[i] = tmpaddr + (uint)result.Count + imageBase;
             result.AddRange(tmpbyte);
         }
     }
     uint[] animmdladdrs = new uint[Anim.Count];
     uint[] animaniaddrs = new uint[Anim.Count];
     for (int i = 0; i < Anim.Count; i++)
     {
         if (labels.ContainsKey(Anim[i].Model.Name))
             animmdladdrs[i] = labels[Anim[i].Model.Name];
         else
         {
             result.Align(4);
             tmpbyte = Anim[i].Model.GetBytes(imageBase + (uint)result.Count, format == LandTableFormat.SADX, labels,
                 out tmpaddr);
             animmdladdrs[i] = tmpaddr + (uint)result.Count + imageBase;
             result.AddRange(tmpbyte);
         }
         if (labels.ContainsKey(Anim[i].Animation.Name))
             animaniaddrs[i] = labels[Anim[i].Animation.Name];
         else
         {
             result.Align(4);
             tmpbyte = Anim[i].Animation.WriteHeader(imageBase + (uint)result.Count, animmdladdrs[i], labels, out tmpaddr);
             animaniaddrs[i] = tmpaddr + (uint)result.Count + imageBase;
             result.AddRange(tmpbyte);
         }
     }
     uint coladdr;
     if (COL.Count > 0)
     {
         if (labels.ContainsKey(COLName))
             coladdr = labels[COLName];
         else
         {
             coladdr = imageBase + (uint)result.Count;
             labels.Add(COLName, coladdr);
             for (int i = 0; i < COL.Count; i++)
             {
                 result.Align(4);
                 result.AddRange(COL[i].GetBytes(imageBase + (uint)result.Count, colmdladdrs[i], format));
             }
         }
     }
     else
         coladdr = 0;
     uint animaddr;
     if (Anim.Count > 0)
     {
         if (labels.ContainsKey(AnimName))
             animaddr = labels[AnimName];
         else
         {
             animaddr = imageBase + (uint)result.Count;
             labels.Add(AnimName, animaddr);
             for (int i = 0; i < Anim.Count; i++)
             {
                 result.Align(4);
                 result.AddRange(Anim[i].GetBytes(imageBase + (uint)result.Count, animmdladdrs[i], animaniaddrs[i]));
             }
         }
     }
     else
         animaddr = 0;
     result.Align(4);
     uint texnameaddr = 0;
     if (TextureFileName != null)
     {
         texnameaddr = imageBase + (uint)result.Count;
         result.AddRange(Encoding.ASCII.GetBytes(TextureFileName));
         result.Add(0);
     }
     result.Align(4);
     address = (uint)result.Count;
     result.AddRange(ByteConverter.GetBytes((ushort)COL.Count));
     switch (format)
     {
         case LandTableFormat.SA1:
         case LandTableFormat.SADX:
             result.AddRange(ByteConverter.GetBytes((ushort)Anim.Count));
             result.AddRange(ByteConverter.GetBytes(Flags));
             result.AddRange(ByteConverter.GetBytes(Unknown1));
             result.AddRange(ByteConverter.GetBytes(coladdr));
             result.AddRange(ByteConverter.GetBytes(animaddr));
             result.AddRange(ByteConverter.GetBytes(texnameaddr));
             result.AddRange(ByteConverter.GetBytes(TextureList));
             result.AddRange(ByteConverter.GetBytes(Unknown2));
             result.AddRange(ByteConverter.GetBytes(Unknown3));
             break;
         case LandTableFormat.SA2:
             result.AddRange(ByteConverter.GetBytes((ushort)cnk.Count));
             result.AddRange(new byte[8]); // TODO: figure out what these do
             result.AddRange(ByteConverter.GetBytes(Unknown1));
             result.AddRange(ByteConverter.GetBytes(coladdr));
             result.AddRange(ByteConverter.GetBytes(animaddr));
             result.AddRange(ByteConverter.GetBytes(texnameaddr));
             result.AddRange(ByteConverter.GetBytes(TextureList));
             break;
     }
     labels.Add(Name, address + imageBase);
     return result.ToArray();
 }
Esempio n. 43
0
        public LandTable(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels)
        {
            Format = format;
            if (labels.ContainsKey(address))
            {
                Name = labels[address];
            }
            else
            {
                Name = "landtable_" + address.ToString("X8");
            }
            short colcnt = ByteConverter.ToInt16(file, address);

            switch (format)
            {
            case LandTableFormat.SA1:
            case LandTableFormat.SADX:
                short anicnt = ByteConverter.ToInt16(file, address + 2);
                Flags    = ByteConverter.ToInt32(file, address + 4);
                Unknown1 = ByteConverter.ToSingle(file, address + 8);
                COL      = new List <COL>();
                int tmpaddr = ByteConverter.ToInt32(file, address + 0xC);
                if (tmpaddr != 0)
                {
                    tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase);
                    if (labels.ContainsKey(tmpaddr))
                    {
                        COLName = labels[tmpaddr];
                    }
                    else
                    {
                        COLName = "collist_" + tmpaddr.ToString("X8");
                    }
                    for (int i = 0; i < colcnt; i++)
                    {
                        COL.Add(new COL(file, tmpaddr, imageBase, format, labels));
                        tmpaddr += SAModel.COL.Size(format);
                    }
                }
                else
                {
                    COLName = "collist_" + Extensions.GenerateIdentifier();
                }
                Anim    = new List <GeoAnimData>();
                tmpaddr = ByteConverter.ToInt32(file, address + 0x10);
                if (tmpaddr != 0)
                {
                    tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase);
                    if (labels.ContainsKey(tmpaddr))
                    {
                        AnimName = labels[tmpaddr];
                    }
                    else
                    {
                        AnimName = "animlist_" + tmpaddr.ToString("X8");
                    }
                    for (int i = 0; i < anicnt; i++)
                    {
                        Anim.Add(new GeoAnimData(file, tmpaddr, imageBase, format, labels));
                        tmpaddr += GeoAnimData.Size;
                    }
                }
                else
                {
                    AnimName = "animlist_" + Extensions.GenerateIdentifier();
                }
                tmpaddr = ByteConverter.ToInt32(file, address + 0x14);
                if (tmpaddr != 0)
                {
                    tmpaddr         = (int)unchecked ((uint)tmpaddr - imageBase);
                    TextureFileName = file.GetCString(tmpaddr, Encoding.ASCII);
                }
                TextureList = ByteConverter.ToUInt32(file, address + 0x18);
                Unknown2    = ByteConverter.ToInt32(file, address + 0x1C);
                Unknown3    = ByteConverter.ToInt32(file, address + 0x20);
                break;

            case LandTableFormat.SA2:
                short cnkcnt = ByteConverter.ToInt16(file, address + 2);
                Unknown1 = ByteConverter.ToSingle(file, address + 0xC);
                COL      = new List <COL>();
                tmpaddr  = ByteConverter.ToInt32(file, address + 0x10);
                if (tmpaddr != 0)
                {
                    tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase);
                    if (labels.ContainsKey(tmpaddr))
                    {
                        COLName = labels[tmpaddr];
                    }
                    else
                    {
                        COLName = "collist_" + tmpaddr.ToString("X8");
                    }
                    for (int i = 0; i < colcnt; i++)
                    {
                        COL.Add(new COL(file, tmpaddr, imageBase, format, labels, cnkcnt < 0 ? null : (bool?)(i >= cnkcnt)));
                        tmpaddr += SAModel.COL.Size(format);
                    }
                }
                else
                {
                    COLName = "collist_" + Extensions.GenerateIdentifier();
                }
                Anim     = new List <GeoAnimData>();
                AnimName = "animlist_" + Extensions.GenerateIdentifier();
                tmpaddr  = ByteConverter.ToInt32(file, address + 0x18);
                if (tmpaddr != 0)
                {
                    tmpaddr         = (int)unchecked ((uint)tmpaddr - imageBase);
                    TextureFileName = file.GetCString(tmpaddr, Encoding.ASCII);
                }
                TextureList = ByteConverter.ToUInt32(file, address + 0x1C);
                break;
            }
            Metadata = new Dictionary <uint, byte[]>();
        }
Esempio n. 44
0
        public static int SplitDLLFile(string datafilename, string inifilename, string projectFolderName)
        {
#if !DEBUG
            try
            {
#endif
            byte[] datafile = File.ReadAllBytes(datafilename);
            IniData inifile = IniSerializer.Deserialize <IniData>(inifilename);
            uint imageBase  = HelperFunctions.SetupEXE(ref datafile).Value;
            Dictionary <string, int> exports;
            {
                int      ptr               = BitConverter.ToInt32(datafile, BitConverter.ToInt32(datafile, 0x3c) + 4 + 20 + 96);
                GCHandle handle            = GCHandle.Alloc(datafile, GCHandleType.Pinned);
                IMAGE_EXPORT_DIRECTORY dir = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure(
                    Marshal.UnsafeAddrOfPinnedArrayElement(datafile, ptr), typeof(IMAGE_EXPORT_DIRECTORY));
                handle.Free();
                exports = new Dictionary <string, int>(dir.NumberOfFunctions);
                int nameaddr = dir.AddressOfNames;
                int ordaddr  = dir.AddressOfNameOrdinals;
                for (int i = 0; i < dir.NumberOfNames; i++)
                {
                    string name = datafile.GetCString(BitConverter.ToInt32(datafile, nameaddr),
                                                      System.Text.Encoding.ASCII);
                    int addr = BitConverter.ToInt32(datafile,
                                                    dir.AddressOfFunctions + (BitConverter.ToInt16(datafile, ordaddr) * 4));
                    exports.Add(name, addr);
                    nameaddr += 4;
                    ordaddr  += 2;
                }
            }
            ModelFormat modelfmt    = 0;
            LandTableFormat landfmt = 0;
            string modelext         = null;
            string landext          = null;
            switch (inifile.Game)
            {
            case Game.SADX:
                modelfmt = ModelFormat.BasicDX;
                landfmt  = LandTableFormat.SADX;
                modelext = ".sa1mdl";
                landext  = ".sa1lvl";
                break;

            case Game.SA2B:
                modelfmt = ModelFormat.Chunk;
                landfmt  = LandTableFormat.SA2;
                modelext = ".sa2mdl";
                landext  = ".sa2lvl";
                break;
            }
            int itemcount                    = 0;
            List <string> labels             = new List <string>();
            ModelAnimationsDictionary models = new ModelAnimationsDictionary();
            DllIniData output                = new DllIniData()
            {
                Name = inifile.ModuleName,
                Game = inifile.Game
            };
            Stopwatch timer = new Stopwatch();
            timer.Start();
            foreach (KeyValuePair <string, FileInfo> item in inifile.Files)
            {
                if (string.IsNullOrEmpty(item.Key))
                {
                    continue;
                }
                FileInfo data = item.Value;
                string   type = data.Type;
                string   name = item.Key;
                output.Exports[name] = type;
                int address = exports[name];

                string fileOutputPath = "";
                if (data.Filename != null)
                {
                    fileOutputPath = string.Concat(projectFolderName, data.Filename);

                    Console.WriteLine(name + " -> " + fileOutputPath);
                    Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                }
                else
                {
                    Console.WriteLine(name);
                }
                switch (type)
                {
                case "landtable":
                {
                    LandTable land = new LandTable(datafile, address, imageBase, landfmt)
                    {
                        Description = name
                    };
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = land.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(land.Name))
                    {
                        land.SaveToFile(fileOutputPath, landfmt);
                        output.Files[data.Filename] = new FileTypeHash("landtable", HelperFunctions.FileHash(fileOutputPath));
                        labels.AddRange(land.GetLabels());
                    }
                }
                break;

                case "battlelandtable":
                {
                    LandTable land = new LandTable(datafile, address, imageBase, LandTableFormat.SA2B)
                    {
                        Description = name
                    };
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = land.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(land.Name))
                    {
                        land.SaveToFile(fileOutputPath, LandTableFormat.SA2B);
                        output.Files[data.Filename] = new FileTypeHash("landtable", HelperFunctions.FileHash(fileOutputPath));
                        labels.AddRange(land.GetLabels());
                    }
                }
                break;

                case "landtablearray":
                    for (int i = 0; i < data.Length; i++)
                    {
                        int ptr = BitConverter.ToInt32(datafile, address);
                        if (ptr != 0)
                        {
                            ptr = (int)(ptr - imageBase);
                            string    idx  = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]";
                            LandTable land = new LandTable(datafile, ptr, imageBase, landfmt)
                            {
                                Description = idx
                            };
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = land.Name
                            };
                            output.Items.Add(info);
                            if (!labels.Contains(land.Name))
                            {
                                string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + landext);
                                string fileName = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + landext);

                                land.SaveToFile(outputFN, landfmt);
                                output.Files[fileName] = new FileTypeHash("landtable", HelperFunctions.FileHash(outputFN));
                                labels.AddRange(land.GetLabels());
                            }
                        }
                        address += 4;
                    }
                    break;

                case "model":
                {
                    NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, address, imageBase, modelfmt, new Dictionary <int, Attach>());
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = mdl.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(mdl.Name))
                    {
                        models.Add(new ModelAnimations(data.Filename, name, mdl, modelfmt));
                        labels.AddRange(mdl.GetLabels());
                    }
                }
                break;

                case "morph":
                {
                    BasicAttach dummy = new BasicAttach(datafile, address, imageBase, modelfmt == ModelFormat.BasicDX);
                    NJS_OBJECT  mdl   = new NJS_OBJECT()
                    {
                        Attach = dummy
                    };
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = dummy.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(dummy.Name))
                    {
                        models.Add(new ModelAnimations(data.Filename, name, mdl, modelfmt));
                        labels.AddRange(mdl.GetLabels());
                    }
                }
                break;

                case "modelarray":
                    for (int i = 0; i < data.Length; i++)
                    {
                        int ptr = BitConverter.ToInt32(datafile, address);
                        if (ptr != 0)
                        {
                            ptr = (int)(ptr - imageBase);
                            NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, ptr, imageBase, modelfmt, new Dictionary <int, Attach>());
                            string      idx  = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]";
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = mdl.Name
                            };
                            output.Items.Add(info);
                            if (!labels.Contains(mdl.Name))
                            {
                                string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + modelext);
                                models.Add(new ModelAnimations(fn, idx, mdl, modelfmt));
                                labels.AddRange(mdl.GetLabels());
                            }
                        }
                        address += 4;
                    }
                    break;

                case "modelsarray":
                    for (int i = 0; i < data.Length; i++)
                    {
                        int ptr = BitConverter.ToInt32(datafile, address);
                        if (ptr != 0)
                        {
                            ptr = (int)(ptr - imageBase);
                            BasicAttach dummy = new BasicAttach(datafile, ptr, imageBase, modelfmt == ModelFormat.BasicDX);
                            NJS_OBJECT  mdl   = new NJS_OBJECT()
                            {
                                Attach = dummy
                            };
                            string      idx  = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]";
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = dummy.Name
                            };
                            output.Items.Add(info);
                            if (!labels.Contains(dummy.Name))
                            {
                                string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + modelext);
                                models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.BasicDX));
                                labels.AddRange(mdl.GetLabels());
                            }
                        }
                        address += 4;
                    }
                    break;

                case "basicmodel":
                {
                    NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic, new Dictionary <int, Attach>());
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = mdl.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(mdl.Name))
                    {
                        models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.Basic));
                        labels.AddRange(mdl.GetLabels());
                    }
                }
                break;

                case "basicmodelarray":
                    for (int i = 0; i < data.Length; i++)
                    {
                        int ptr = BitConverter.ToInt32(datafile, address);
                        if (ptr != 0)
                        {
                            ptr = (int)(ptr - imageBase);
                            NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.Basic, new Dictionary <int, Attach>());
                            string      idx  = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]";
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = mdl.Name
                            };
                            output.Items.Add(info);
                            if (!labels.Contains(mdl.Name))
                            {
                                string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl");
                                models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.Basic));
                                labels.AddRange(mdl.GetLabels());
                            }
                        }
                        address += 4;
                    }
                    break;

                case "basicdxmodel":
                {
                    NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX, new Dictionary <int, Attach>());
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = mdl.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(mdl.Name))
                    {
                        models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.BasicDX));
                        labels.AddRange(mdl.GetLabels());
                    }
                }
                break;

                case "basicdxmodelarray":
                    for (int i = 0; i < data.Length; i++)
                    {
                        int ptr = BitConverter.ToInt32(datafile, address);
                        if (ptr != 0)
                        {
                            ptr = (int)(ptr - imageBase);
                            NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.BasicDX, new Dictionary <int, Attach>());
                            string      idx  = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]";
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = mdl.Name
                            };
                            output.Items.Add(info);
                            if (!labels.Contains(mdl.Name))
                            {
                                string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl");
                                models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.BasicDX));
                                labels.AddRange(mdl.GetLabels());
                            }
                        }
                        address += 4;
                    }
                    break;

                case "chunkmodel":
                {
                    NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = mdl.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(mdl.Name))
                    {
                        models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.Chunk));
                        labels.AddRange(mdl.GetLabels());
                    }
                }
                break;

                case "chunkmodelarray":
                    for (int i = 0; i < data.Length; i++)
                    {
                        int ptr = BitConverter.ToInt32(datafile, address);
                        if (ptr != 0)
                        {
                            ptr = (int)(ptr - imageBase);
                            NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                            string      idx  = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]";
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = mdl.Name
                            };
                            output.Items.Add(info);
                            if (!labels.Contains(mdl.Name))
                            {
                                string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa2mdl");
                                models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.Chunk));
                                labels.AddRange(mdl.GetLabels());
                            }
                        }
                        address += 4;
                    }
                    break;

                case "actionarray":
                    for (int i = 0; i < data.Length; i++)
                    {
                        int ptr = BitConverter.ToInt32(datafile, address);
                        if (ptr != 0)
                        {
                            ptr = (int)(ptr - imageBase);
                            NJS_ACTION ani = new NJS_ACTION(datafile, ptr, imageBase, modelfmt, new Dictionary <int, Attach>());
                            string     idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]";
                            ani.Animation.Name = item.Key + "_" + i;
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = ani.Animation.Name,
                                Field  = "motion"
                            };
                            output.Items.Add(info);
                            info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = ani.Model.Name,
                                Field  = "object"
                            };
                            output.Items.Add(info);
                            string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                            string fn       = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                            ani.Animation.Save(outputFN);
                            output.Files[fn] = new FileTypeHash("animation", HelperFunctions.FileHash(outputFN));
                            if (models.Contains(ani.Model.Name))
                            {
                                ModelAnimations           mdl = models[ani.Model.Name];
                                System.Text.StringBuilder sb  = new System.Text.StringBuilder(260);
                                PathRelativePathTo(sb, Path.GetFullPath(Path.Combine(projectFolderName, mdl.Filename)), 0, Path.GetFullPath(outputFN), 0);
                                mdl.Animations.Add(sb.ToString());                                                 // this is where the problem is
                            }
                            else
                            {
                                string mfn           = Path.ChangeExtension(fn, modelext);
                                string outputmfn     = Path.Combine(projectFolderName, mfn);
                                string animationName = Path.GetFileName(outputFN);

                                ModelFile.CreateFile(outputmfn, ani.Model, new[] { animationName }, null, idx + "->object",
                                                     null, modelfmt);
                                output.Files[mfn] = new FileTypeHash("model", HelperFunctions.FileHash(outputmfn));
                            }
                        }
                        address += 4;
                    }
                    break;

                case "texlist":
                    if (output.TexLists == null)
                    {
                        output.TexLists = new TexListContainer();
                    }
                    output.TexLists.Add((uint)(address + imageBase), new DllTexListInfo(name, null));
                    break;

                case "texlistarray":
                    if (output.TexLists == null)
                    {
                        output.TexLists = new TexListContainer();
                    }
                    for (int i = 0; i < data.Length; i++)
                    {
                        uint ptr = BitConverter.ToUInt32(datafile, address);
                        if (ptr != 0 && !output.TexLists.ContainsKey(ptr))
                        {
                            output.TexLists.Add(ptr, new DllTexListInfo(name, i));
                        }
                        address += 4;
                    }
                    break;

                case "animindexlist":
                {
                    Directory.CreateDirectory(fileOutputPath);
                    List <string> hashes = new List <string>();
                    int           i      = ByteConverter.ToInt16(datafile, address);
                    while (i != -1)
                    {
                        new NJS_MOTION(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, ByteConverter.ToInt16(datafile, address + 2))
                        .Save(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                        hashes.Add(i.ToString(NumberFormatInfo.InvariantInfo) + ":" + HelperFunctions.FileHash(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"));
                        address += 8;
                        i        = ByteConverter.ToInt16(datafile, address);
                    }
                    output.DataItems.Add(new DllDataItemInfo()
                        {
                            Type = type, Export = name, Filename = data.Filename, MD5Hash = string.Join("|", hashes.ToArray())
                        });
                }
                break;

                case "charaobjectdatalist":
                {
                    Directory.CreateDirectory(fileOutputPath);
                    List <CharaObjectData> result = new List <CharaObjectData>();
                    List <string>          hashes = new List <string>();
                    for (int i = 0; i < data.Length; i++)
                    {
                        string          chnm  = charaobjectnames[i];
                        CharaObjectData chara = new CharaObjectData();
                        NJS_OBJECT      model = new NJS_OBJECT(datafile, (int)(BitConverter.ToInt32(datafile, address) - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                        chara.MainModel = model.Name;
                        NJS_MOTION anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 4) - imageBase), imageBase, model.CountAnimated());
                        chara.Animation1 = anim.Name;
                        anim.Save(Path.Combine(fileOutputPath, $"{chnm} Anim 1.saanim"));
                        hashes.Add($"{chnm} Anim 1.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Anim 1.saanim")));
                        anim             = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 8) - imageBase), imageBase, model.CountAnimated());
                        chara.Animation2 = anim.Name;
                        anim.Save(Path.Combine(fileOutputPath, $"{chnm} Anim 2.saanim"));
                        hashes.Add($"{chnm} Anim 2.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Anim 2.saanim")));
                        anim             = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 12) - imageBase), imageBase, model.CountAnimated());
                        chara.Animation3 = anim.Name;
                        anim.Save(Path.Combine(fileOutputPath, $"{chnm} Anim 3.saanim"));
                        hashes.Add($"{chnm} Anim 3.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Anim 3.saanim")));
                        ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{chnm}.sa2mdl"), model, new[] { $"{chnm} Anim 1.saanim", $"{chnm} Anim 2.saanim", $"{chnm} Anim 3.saanim" }, null, null, null, ModelFormat.Chunk);
                        hashes.Add($"{chnm}.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm}.sa2mdl")));
                        int ptr = BitConverter.ToInt32(datafile, address + 16);
                        if (ptr != 0)
                        {
                            model = new NJS_OBJECT(datafile, (int)(ptr - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                            chara.AccessoryModel      = model.Name;
                            chara.AccessoryAttachNode = "object_" + (BitConverter.ToInt32(datafile, address + 20) - imageBase).ToString("X8");
                            ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{chnm} Accessory.sa2mdl"), model, null, null, null, null, ModelFormat.Chunk);
                            hashes.Add($"{chnm} Accessory.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Accessory.sa2mdl")));
                        }
                        ptr = BitConverter.ToInt32(datafile, address + 24);
                        if (ptr != 0)
                        {
                            model                 = new NJS_OBJECT(datafile, (int)(ptr - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                            chara.SuperModel      = model.Name;
                            anim                  = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 28) - imageBase), imageBase, model.CountAnimated());
                            chara.SuperAnimation1 = anim.Name;
                            anim.Save(Path.Combine(fileOutputPath, $"Super {chnm} Anim 1.saanim"));
                            hashes.Add($"Super {chnm} Anim 1.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm} Anim 1.saanim")));
                            anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 32) - imageBase), imageBase, model.CountAnimated());
                            chara.SuperAnimation2 = anim.Name;
                            anim.Save(Path.Combine(fileOutputPath, $"Super {chnm} Anim 2.saanim"));
                            hashes.Add($"Super {chnm} Anim 2.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm} Anim 2.saanim")));
                            anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 36) - imageBase), imageBase, model.CountAnimated());
                            chara.SuperAnimation3 = anim.Name;
                            anim.Save(Path.Combine(fileOutputPath, $"Super {chnm} Anim 3.saanim"));
                            hashes.Add($"Super {chnm} Anim 3.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm} Anim 3.saanim")));
                            ModelFile.CreateFile(Path.Combine(fileOutputPath, $"Super {chnm}.sa2mdl"), model, new[] { $"Super {chnm} Anim 1.saanim", $"Super {chnm} Anim 2.saanim", $"Super {chnm} Anim 3.saanim" }, null, null, null, ModelFormat.Chunk);
                            hashes.Add($"Super {chnm}.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm}.sa2mdl")));
                        }
                        chara.Unknown1        = BitConverter.ToInt32(datafile, address + 40);
                        chara.Rating          = BitConverter.ToInt32(datafile, address + 44);
                        chara.DescriptionID   = BitConverter.ToInt32(datafile, address + 48);
                        chara.TextBackTexture = BitConverter.ToInt32(datafile, address + 52);
                        chara.Unknown5        = BitConverter.ToSingle(datafile, address + 56);
                        result.Add(chara);
                        address += 60;
                    }
                    IniSerializer.Serialize(result, Path.Combine(fileOutputPath, "info.ini"));
                    hashes.Add("info.ini:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, "info.ini")));
                    output.DataItems.Add(new DllDataItemInfo()
                        {
                            Type = type, Export = name, Filename = data.Filename, MD5Hash = string.Join("|", hashes.ToArray())
                        });
                }
                break;

                case "kartspecialinfolist":
                {
                    Directory.CreateDirectory(fileOutputPath);
                    List <KartSpecialInfo> result = new List <KartSpecialInfo>();
                    List <string>          hashes = new List <string>();
                    for (int i = 0; i < data.Length; i++)
                    {
                        KartSpecialInfo kart = new KartSpecialInfo
                        {
                            ID = ByteConverter.ToInt32(datafile, address)
                        };
                        NJS_OBJECT model = new NJS_OBJECT(datafile, (int)(BitConverter.ToInt32(datafile, address + 4) - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                        kart.Model = model.Name;
                        ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{i}.sa2mdl"), model, null, null, null, null, ModelFormat.Chunk);
                        hashes.Add($"{i}.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{i}.sa2mdl")));
                        int ptr = BitConverter.ToInt32(datafile, address + 8);
                        if (ptr != 0)
                        {
                            model         = new NJS_OBJECT(datafile, (int)(ptr - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>());
                            kart.LowModel = model.Name;
                            ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{i} Low.sa2mdl"), model, null, null, null, null, ModelFormat.Chunk);
                            hashes.Add($"{i} Low.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{i} Low.sa2mdl")));
                        }
                        kart.TexList  = ByteConverter.ToUInt32(datafile, address + 12);
                        kart.Unknown1 = ByteConverter.ToInt32(datafile, address + 16);
                        kart.Unknown2 = ByteConverter.ToInt32(datafile, address + 20);
                        kart.Unknown3 = ByteConverter.ToInt32(datafile, address + 24);
                        result.Add(kart);
                        address += 0x1C;
                    }
                    IniSerializer.Serialize(result, Path.Combine(fileOutputPath, "info.ini"));
                    hashes.Add("info.ini:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, "info.ini")));
                    output.DataItems.Add(new DllDataItemInfo()
                        {
                            Type = type, Export = name, Filename = data.Filename, MD5Hash = string.Join("|", hashes.ToArray())
                        });
                }
                break;
                }
                itemcount++;
            }
            foreach (ModelAnimations item in models)
            {
                string modelOutputPath = string.Concat(projectFolderName, item.Filename);
                //string modelOutputPath = item.Filename;

                ModelFile.CreateFile(modelOutputPath, item.Model, item.Animations.ToArray(), null, item.Name, null, item.Format);
                string type = "model";
                switch (item.Format)
                {
                case ModelFormat.Basic:
                    type = "basicmodel";
                    break;

                case ModelFormat.BasicDX:
                    type = "basicdxmodel";
                    break;

                case ModelFormat.Chunk:
                    type = "chunkmodel";
                    break;
                }
                output.Files[item.Filename] = new FileTypeHash(type, HelperFunctions.FileHash(modelOutputPath));
            }
            IniSerializer.Serialize(output, Path.Combine(projectFolderName, Path.GetFileNameWithoutExtension(datafilename))
                                    + "_data.ini");
            timer.Stop();
            Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds.");
            Console.WriteLine();
#if !DEBUG
        }

        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            Console.WriteLine(e.StackTrace);
            Console.WriteLine("Press any key to exit.");
            Console.ReadLine();
            return((int)SA_Tools.Split.SplitERRORVALUE.UnhandledException);
        }
#endif
            return((int)SA_Tools.Split.SplitERRORVALUE.Success);
        }