Пример #1
0
        static void Main(string[] args)
        {
            string filename;

            if (args.Length > 0)
            {
                filename = args[0];
                Console.WriteLine("File: {0}", filename);
            }
            else
            {
                Console.Write("File: ");
                filename = Console.ReadLine();
            }
            ModelFile model = new ModelFile(filename);

            switch (model.Format)
            {
            case ModelFormat.Basic:
                foreach (NJS_OBJECT obj in model.Model.GetObjects().Where(obj => obj.Attach is BasicAttach))
                {
                    obj.Attach = obj.Attach.ToChunk();
                }
                ModelFile.CreateFile(System.IO.Path.ChangeExtension(filename, "sa2mdl"), model.Model, null, null, null, null, ModelFormat.Chunk);
                break;

            case ModelFormat.Chunk:
                foreach (NJS_OBJECT obj in model.Model.GetObjects().Where(obj => obj.Attach is ChunkAttach))
                {
                    obj.Attach = obj.Attach.ToBasic();
                }
                ModelFile.CreateFile(System.IO.Path.ChangeExtension(filename, "sa1mdl"), model.Model, null, null, null, null, ModelFormat.Basic);
                break;
            }
        }
Пример #2
0
        static void Main(string[] args)
        {
            Queue <string> argq = new Queue <string>(args);
            string         inifilename;

            if (argq.Count > 0)
            {
                inifilename = argq.Dequeue();
                Console.WriteLine("INI File: {0}", inifilename);
            }
            else
            {
                Console.Write("INI File: ");
                inifilename = Console.ReadLine();
            }
            Dictionary <int, string> modelnames = IniSerializer.Deserialize <Dictionary <int, string> >(inifilename, inisettings);
            string mdlfilename;

            if (argq.Count > 0)
            {
                mdlfilename = argq.Dequeue();
                Console.WriteLine("Model File: {0}", mdlfilename);
            }
            else
            {
                Console.Write("Model File: ");
                mdlfilename = Console.ReadLine();
            }
            ModelFile model = new ModelFile(mdlfilename);

            NJS_OBJECT[] objects = model.Model.GetObjects();
            string       repmdlfilename;

            if (argq.Count > 0)
            {
                repmdlfilename = argq.Dequeue();
                Console.WriteLine("Replacement Model File: {0}", repmdlfilename);
            }
            else
            {
                Console.Write("Replacement Model File: ");
                repmdlfilename = Console.ReadLine();
            }
            ModelFile repmodel = new ModelFile(repmdlfilename);

            NJS_OBJECT[] repobjects = repmodel.Model.GetObjects();
            if (objects.Length != repobjects.Length)
            {
                Console.WriteLine("Models have different structures, the game may crash.");
            }
            foreach (KeyValuePair <int, string> item in modelnames.ToList())
            {
                if (objects.Any((obj) => obj.Name == item.Value))
                {
                    modelnames[item.Key] = repobjects[Array.IndexOf(objects, objects.Single((o) => o.Name == item.Value))].Name;
                }
            }
            ModelFile.CreateFile(mdlfilename, repmodel.Model, null, null, repmodel.Author, repmodel.Description, "replaceMDL", repmodel.Metadata, repmodel.Format);
            IniSerializer.Serialize(modelnames, inisettings, inifilename);
        }
Пример #3
0
        private void ExportFolder(string dir)
        {
            Directory.CreateDirectory(dir);
            string fname = Path.GetFileName(dir);

            IniSerializer.Serialize(meta, Path.Combine(dir, fname + ".ini"));
            meta.Icon.Save(Path.Combine(dir, fname + ".bmp"));
            if (meta.TextureData != null && meta.TextureData.Length > 0)
            {
                File.WriteAllBytes(Path.Combine(dir, fname + ".pvm"), meta.TextureData);
            }
            if (meta.SoundData != null && meta.SoundData.Length > 0)
            {
                File.WriteAllBytes(Path.Combine(dir, fname + ".mlt"), meta.SoundData);
            }
            if (meta.ModelData != null && meta.ModelData.Length > 0)
            {
                byte[]     prsdata      = FraGag.Compression.Prs.Decompress(meta.ModelData);
                uint       modelpointer = BitConverter.ToUInt32(prsdata, 0) - 0xCCA4000;
                NJS_OBJECT mdl          = new NJS_OBJECT(prsdata, (int)modelpointer, 0xCCA4000, ModelFormat.Basic, null);
                ModelFile.CreateFile(Path.Combine(dir, fname + ".sa1mdl"), mdl, null, null, null, null, ModelFormat.Basic);
                if (exportBinaryDataToolStripMenuItem.Checked)
                {
                    File.WriteAllBytes(Path.Combine(dir, fname + ".prs"), meta.ModelData);
                    File.WriteAllBytes(Path.Combine(dir, fname + ".bin"), prsdata);
                }
            }
        }
Пример #4
0
 public DeathZoneFlags Save(string path, int i)
 {
     ModelFile.CreateFile(Path.Combine(path, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl"), Model, null, null, null, LevelData.LevelName + " Death Zone " + i.ToString(NumberFormatInfo.InvariantInfo), "SADXLVL2", null, ModelFormat.Basic);
     return(new DeathZoneFlags()
     {
         Flags = Flags
     });
 }
Пример #5
0
 public DeathZoneFlags Save(string path, string filename)
 {
     ModelFile.CreateFile(Path.Combine(path, filename), Model, null, null, LevelData.LevelName + " Death Zone " + filename, null, ModelFormat.Basic);
     return(new DeathZoneFlags()
     {
         Flags = Flags, Filename = filename
     });
 }
Пример #6
0
        public void ExportModel()
        {
            using (System.Windows.Forms.SaveFileDialog a = new System.Windows.Forms.SaveFileDialog
            {
                DefaultExt = "dae",
                Filter = "SAModel Files|*.sa1mdl|Collada|*.dae|Wavefront|*.obj"
            })
            {
                if (a.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    string ftype = "collada";
                    switch (System.IO.Path.GetExtension(a.FileName).ToLowerInvariant())
                    {
                    case ".sa1mdl":
                        ModelFile.CreateFile(a.FileName, COL.Model, null, null, null, null, COL.Model.GetModelFormat());
                        return;

                    case ".fbx":
                        ftype = "fbx";
                        break;

                    case ".obj":
                        ftype = "obj";
                        break;
                    }
                    Assimp.AssimpContext context = new Assimp.AssimpContext();
                    Assimp.Scene         scene   = new Assimp.Scene();
                    scene.Materials.Add(new Assimp.Material());
                    Assimp.Node n = new Assimp.Node();
                    n.Name         = "RootNode";
                    scene.RootNode = n;
                    string        rootPath     = System.IO.Path.GetDirectoryName(a.FileName);
                    List <string> texturePaths = new List <string>();
                    int           numSteps     = 0;
                    if (LevelData.TextureBitmaps != null && LevelData.TextureBitmaps.Count > 0)
                    {
                        numSteps = LevelData.TextureBitmaps[LevelData.leveltexs].Length;
                    }
                    for (int i = 0; i < numSteps; i++)
                    {
                        BMPInfo bmp = LevelData.TextureBitmaps[LevelData.leveltexs][i];
                        texturePaths.Add(System.IO.Path.Combine(rootPath, bmp.Name + ".png"));
                        bmp.Image.Save(System.IO.Path.Combine(rootPath, bmp.Name + ".png"));
                    }
                    SAEditorCommon.Import.AssimpStuff.AssimpExport(COL.Model, scene, Matrix.Identity, texturePaths.Count > 0 ? texturePaths.ToArray() : null, scene.RootNode);
                    context.ExportFile(scene, a.FileName, ftype, Assimp.PostProcessSteps.ValidateDataStructure | Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.FlipUVs);                    //
                }
            }
        }
Пример #7
0
 private void buttonSaveModel_Click(object sender, EventArgs e)
 {
     using (SaveFileDialog sv = new SaveFileDialog()
     {
         FileName = Path.ChangeExtension(Path.GetFileName(currentFilename), ".sa1mdl"), Title = "Save Model", Filter = "SA1MDL Files|*.sa1mdl|All Files|*.*", DefaultExt = "sa1mdl"
     })
     {
         if (sv.ShowDialog() == DialogResult.OK)
         {
             byte[]     modeldata    = FraGag.Compression.Prs.Decompress(meta.ModelData);
             uint       modelpointer = BitConverter.ToUInt32(modeldata, 0) - 0xCCA4000;
             NJS_OBJECT mdl          = new NJS_OBJECT(modeldata, (int)modelpointer, 0xCCA4000, ModelFormat.Basic, null);
             ModelFile.CreateFile(sv.FileName, mdl, null, null, null, new System.Collections.Generic.Dictionary <uint, byte[]>(), ModelFormat.Basic);
         }
     }
 }
Пример #8
0
 private void saveToolStripMenuItem_Click(object sender, EventArgs e)
 {
     using (SaveFileDialog a = new SaveFileDialog()
     {
         DefaultExt = (outfmt == ModelFormat.Chunk ? "sa2" : "sa1") + "mdl",
         Filter = (outfmt == ModelFormat.Chunk ? "SA2" : "SA1") + "MDL Files|*." + (outfmt == ModelFormat.Chunk ? "sa2" : "sa1") + "mdl|All Files|*.*"
     })
         if (a.ShowDialog(this) == DialogResult.OK)
         {
             if (modelFile != null)
             {
                 modelFile.Tool = "SAMDL";
                 modelFile.SaveToFile(a.FileName);
             }
             else
             {
                 ModelFile.CreateFile(a.FileName, model, null, null, null, null, "SAMDL", null, outfmt);
             }
         }
 }
Пример #9
0
        public void ExportModel()
        {
            using (System.Windows.Forms.SaveFileDialog a = new System.Windows.Forms.SaveFileDialog
            {
                DefaultExt = "dae",
                Filter = "SAModel Files|*.sa1mdl|Collada|*.dae|Wavefront|*.obj"
            })
            {
                if (a.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    string ftype = "collada";
                    switch (System.IO.Path.GetExtension(a.FileName).ToLowerInvariant())
                    {
                    case ".sa1mdl":
                        ModelFile.CreateFile(a.FileName, Model, null, null, null, null, Model.GetModelFormat());
                        return;

                    case ".fbx":
                        ftype = "fbx";
                        break;

                    case ".obj":
                        ftype = "obj";
                        break;
                    }
                    Assimp.AssimpContext context = new Assimp.AssimpContext();
                    Assimp.Scene         scene   = new Assimp.Scene();
                    scene.Materials.Add(new Assimp.Material());
                    Assimp.Node n = new Assimp.Node();
                    n.Name         = "RootNode";
                    scene.RootNode = n;
                    string rootPath = System.IO.Path.GetDirectoryName(a.FileName);
                    SAEditorCommon.Import.AssimpStuff.AssimpExport(Model, scene, Matrix.Identity, null, scene.RootNode);
                    context.ExportFile(scene, a.FileName, ftype, Assimp.PostProcessSteps.ValidateDataStructure | Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.FlipUVs);                    //
                }
            }
        }
Пример #10
0
 private void exportSA2MDLToolStripMenuItem_Click(object sender, EventArgs e)
 {
     using (SaveFileDialog dlg = new SaveFileDialog()
     {
         DefaultExt = "sa2mdl", Filter = "SA2MDL files|*.sa2mdl"
     })
         if (dlg.ShowDialog(this) == DialogResult.OK)
         {
             List <string> anims = new List <string>();
             if (selectedObject.Motion != null)
             {
                 string animname = Path.GetFileNameWithoutExtension(dlg.FileName) + "_sklmtn.saanim";
                 selectedObject.Motion.Save(Path.Combine(Path.GetDirectoryName(dlg.FileName), animname));
                 anims.Add(animname);
             }
             if (selectedObject.ShapeMotion != null)
             {
                 string animname = Path.GetFileNameWithoutExtension(dlg.FileName) + "_shpmtn.saanim";
                 selectedObject.ShapeMotion.Save(Path.Combine(Path.GetDirectoryName(dlg.FileName), animname));
                 anims.Add(animname);
             }
             ModelFile.CreateFile(dlg.FileName, selectedObject.Model, anims.ToArray(), null, null, null, ModelFormat.Chunk);
         }
 }
Пример #11
0
        public static void SplitNBFile(string filename, bool extractchunks, string outdir, int verbose = 0, string inifilename = null, bool overwrite = true)
        {
            Dictionary <int, string>     sectionlist = new Dictionary <int, string>();
            Dictionary <int, NJS_OBJECT> modellist   = new Dictionary <int, NJS_OBJECT>();
            Dictionary <int, NJS_MOTION> animlist    = new Dictionary <int, NJS_MOTION>();

            byte[] file = File.ReadAllBytes(filename);
            if (file.Length == 180920) // Patch in unused rotation for E101R, required for rebuilding with correct pointers
            {
                file[129896] = 0xE0;
                file[129897] = 0x0A;
                file[129904] = 0x02;
            }
            if (BitConverter.ToInt32(file, 0) != 0x04424A4E)
            {
                Console.WriteLine("Invalid NB file.");
                return;
            }
            if (!Directory.Exists(outdir))
            {
                Directory.CreateDirectory(outdir);
            }
            Environment.CurrentDirectory = outdir;
            int numfiles = BitConverter.ToInt16(file, 4);
            Dictionary <int, string> splitfilenames = new Dictionary <int, string>();

            if (inifilename != null)
            {
                inifilename = Path.GetFullPath(inifilename);
            }
            if (File.Exists(inifilename))
            {
                splitfilenames = IniSerializer.Deserialize <Dictionary <int, string> >(inifilename);
                if (verbose > 0)
                {
                    Console.WriteLine("Split INI: {0}", inifilename);
                }
            }
            else
            {
                if (verbose > 0)
                {
                    Console.WriteLine("Split INI {0} not found!", inifilename);
                }
                for (int i = 0; i < numfiles; i++)
                {
                    splitfilenames[i] = i.ToString("D2");
                }
            }
            int curaddr = 8;

            for (int i = 0; i < numfiles; i++)
            {
                ushort type  = BitConverter.ToUInt16(file, curaddr);
                byte[] chunk = new byte[BitConverter.ToInt32(file, curaddr + 4)];
                Array.Copy(file, curaddr + 8, chunk, 0, chunk.Length);
                switch (type)
                {
                case 0:
                    if (verbose > 0)
                    {
                        Console.WriteLine("\nSection {0} at {1} is empty", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X"));
                    }
                    sectionlist.Add(i, "NULL");
                    break;

                case 1:
                    if (verbose > 0)
                    {
                        Console.WriteLine("\nSection {0} at {1} is a model", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X"));
                    }
                    if (extractchunks)
                    {
                        File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + ".bin", chunk);
                    }
                    NJS_OBJECT mdl = ProcessModel(chunk, verbose, curaddr + 8);
                    //if (extractchunks) File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + "_p.bin", GetSections(mdl));
                    // Assume there is no description/texture for SAMDL project mode
                    string[] metadata    = new string[0];
                    string   outFilename = splitfilenames[i];
                    if (splitfilenames[i].Contains("|"))
                    {
                        metadata    = splitfilenames[i].Split('|');  // Filename|Description|Texture file
                        outFilename = metadata[0];
                    }
                    string outResult = outFilename + ".sa1mdl";
                    modellist.Add(i, mdl);
                    if (metadata.Length > 1)
                    {
                        outResult += ("|" + metadata[1]);
                    }
                    if (metadata.Length > 2)
                    {
                        outResult += ("|" + metadata[2]);
                    }
                    sectionlist.Add(i, outResult);
                    break;

                case 3:
                    if (verbose > 0)
                    {
                        Console.WriteLine("\nSection {0} at {1} is a motion", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X"));
                    }
                    if (extractchunks)
                    {
                        File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + ".bin", chunk);
                    }
                    string desc = null;
                    if (splitfilenames[i].Contains("|"))
                    {
                        metadata    = splitfilenames[i].Split('|');                              // Filename|Description
                        outFilename = metadata[0];
                        desc        = metadata[1];
                    }
                    else
                    {
                        outFilename = splitfilenames[i];
                    }
                    NJS_MOTION mot = ProcessMotion(chunk, verbose, curaddr + 8);
                    mot.Description = desc;
                    //if (extractchunks) File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + "_p.bin", GetSections(mot));
                    animlist.Add(i, mot);
                    sectionlist.Add(i, outFilename + ".saanim");
                    break;

                default:
                    if (verbose > 0)
                    {
                        Console.WriteLine("\nSection {0} at {1} is an unknown type", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X"));
                    }
                    if (extractchunks)
                    {
                        File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + ".bin", chunk);
                    }
                    sectionlist.Add(i, splitfilenames[i] + ".wtf");
                    break;
                }
                curaddr += chunk.Length + 8;
            }
            // Save models and animations
            foreach (var modelitem in modellist)
            {
                // If the filename field contains description/texture, split it
                string filenameString = splitfilenames[modelitem.Key];
                if (splitfilenames[modelitem.Key].Contains("|"))
                {
                    string[] filenameSplit = filenameString.Split('|');
                    filenameString = filenameSplit[0];
                }

                List <string> anims = new List <string>();
                foreach (var animitem in animlist)
                {
                    string filenameStringAnim = splitfilenames[animitem.Key];
                    if (splitfilenames[animitem.Key].Contains("|"))
                    {
                        string[] filenameSplitAnim = filenameStringAnim.Split('|');
                        filenameStringAnim = filenameSplitAnim[0];
                    }
                    if (modelitem.Value.CountAnimated() == animitem.Value.ModelParts)
                    {
                        if (File.Exists(Path.Combine(outdir, filenameStringAnim + ".saanim")) && !overwrite)
                        {
                            return;
                        }
                        if (!Directory.Exists(Path.GetDirectoryName(filenameStringAnim + ".saanim")) && Path.GetDirectoryName(filenameStringAnim + ".saanim") != "")
                        {
                            Directory.CreateDirectory(Path.GetDirectoryName(filenameStringAnim + ".saanim"));
                        }
                        animitem.Value.Save(filenameStringAnim + ".saanim");
                        System.Text.StringBuilder sb = new System.Text.StringBuilder(1024);
                        PathRelativePathTo(sb, Path.GetFullPath(Path.Combine(outdir, filenameStringAnim + ".saanim")), 0, Path.GetFullPath(filenameStringAnim + ".saanim"), 0);
                        anims.Add(sb.ToString());
                    }
                }
                if (File.Exists(Path.Combine(outdir, filenameString + ".sa1mdl")) && !overwrite)
                {
                    return;
                }
                if (!Directory.Exists(Path.GetDirectoryName(filenameString + ".sa1mdl")) && Path.GetDirectoryName(filenameString + ".sa1mdl") != "")
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(filenameString + ".sa1mdl"));
                }
                ModelFile.CreateFile(filenameString + ".sa1mdl", modelitem.Value, anims.ToArray(), null, null, null, ModelFormat.Basic);
            }
            string sectionListFilename = Path.GetFileNameWithoutExtension(filename) + ".ini";

            if (inifilename != null)
            {
                sectionListFilename = Path.GetFileNameWithoutExtension(inifilename) + "_data.ini";
            }
            IniSerializer.Serialize(sectionlist, Path.Combine(outdir, sectionListFilename));
        }
Пример #12
0
        static void Main(string[] args)
        {
            bool          nometa  = false;
            bool          nolabel = false;
            string        mode;
            string        fullpath_out;
            bool          bigendian = false;
            List <string> mdlanimfiles;

            if (args.Length == 0)
            {
                Console.WriteLine("Split any binary files supported by SA Tools.\n");
                Console.WriteLine("Usage:\n");
                Console.WriteLine("-Splitting binary files with INI data-");
                Console.WriteLine("split binary <file> <inifile> [output path] [-nometa] [-nolabel]\n");
                Console.WriteLine("-Splitting SA1/SADX NB files-");
                Console.WriteLine("split nb <file> [output path] -ini [split INI file]\n");
                Console.WriteLine("-Splitting SA2 MDL files-");
                Console.WriteLine("split mdl <file> [output path] -anim [animation files]\n");
                Console.WriteLine("-Splitting SA2B MDL files-");
                Console.WriteLine("split mdl_b <file> [output path] -anim [animation files]\n");
                Console.WriteLine("-Splitting dllexport entries from DLL files-");
                Console.WriteLine("split dllexport <file> <type> <name> [output path] [-p numparts]\n");
                Console.WriteLine("Press ENTER to exit.");
                Console.ReadLine();
                return;
            }
#if DEBUG
            if (SplitExtensions(args) == true)
            {
                return;
            }
#endif
            for (int u = 2; u < args.Length; u++)
            {
                if (args[u] == "-nometa")
                {
                    nometa = true;
                }
                if (args[u] == "-nolabel")
                {
                    nolabel = true;
                }
            }
            mode = args[0];
            switch (mode.ToLowerInvariant())
            {
            case "binary":
                string fullpath_bin = Path.GetFullPath(args[1]);
                if (!File.Exists(fullpath_bin))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_bin);
                    return;
                }
                Console.WriteLine("File: {0}", fullpath_bin);
                string fullpath_ini = Path.GetFullPath(args[2]);
                if (!File.Exists(fullpath_ini))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_ini);
                    return;
                }
                Console.WriteLine("Data mapping: {0}", fullpath_ini);
                fullpath_out = Path.GetDirectoryName(fullpath_bin);
                if (args.Length > 3)
                {
                    fullpath_out = args[3];
                    if (fullpath_out[fullpath_out.Length - 1] != '/')
                    {
                        fullpath_out = string.Concat(fullpath_out, '/');
                    }
                    fullpath_out = Path.GetFullPath(fullpath_out);
                }
                Console.WriteLine("Output folder: {0}", fullpath_out);
                if (nometa)
                {
                    Console.WriteLine("Labels are disabled");
                }
                if (Path.GetExtension(args[1]).ToLowerInvariant() == ".dll")
                {
                    SA_Tools.SplitDLL.SplitDLL.SplitDLLFile(fullpath_bin, fullpath_ini, fullpath_out, nometa, nolabel);
                }
                else
                {
                    SA_Tools.Split.Split.SplitFile(fullpath_bin, fullpath_ini, fullpath_out, nometa, nolabel);
                }
                break;

            case "nb":
            case "nb_b":
                string fullpath_nb = Path.GetFullPath(args[1]);
                string path_ini    = null;
                if (args[args.Length - 2].ToLowerInvariant() == "-ini")
                {
                    path_ini = Path.GetFullPath(args[args.Length - 1]);
                }
                if (!File.Exists(fullpath_nb))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_nb);
                    return;
                }
                Console.WriteLine("File: {0}", fullpath_nb);
                fullpath_out = Path.GetDirectoryName(fullpath_nb);
                if (args.Length > 2)
                {
                    fullpath_out = args[2];
                    if (fullpath_out[fullpath_out.Length - 1] != '/')
                    {
                        fullpath_out = string.Concat(fullpath_out, '/');
                    }
                    fullpath_out = Path.GetFullPath(fullpath_out);
                }
                Console.WriteLine("Output folder: {0}", fullpath_out);
                SA_Tools.Split.SplitNB.SplitNBFile(fullpath_nb, false, fullpath_out, 1, path_ini);
                break;

            case "mdl":
            case "mdl_b":
                string fullpath_mdl = Path.GetFullPath(args[1]);
                if (!File.Exists(fullpath_mdl))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_mdl);
                    return;
                }
                Console.Write("File: {0}", fullpath_mdl);
                if (mode == "mdl_b")
                {
                    bigendian = true;
                    Console.Write(" (Big Endian)\n");
                }
                else
                {
                    Console.Write(System.Environment.NewLine);
                }
                fullpath_out = Path.GetDirectoryName(fullpath_mdl);
                if (args.Length > 2)
                {
                    fullpath_out = args[2];
                    if (fullpath_out[fullpath_out.Length - 1] != '/')
                    {
                        fullpath_out = string.Concat(fullpath_out, '/');
                    }
                    fullpath_out = Path.GetFullPath(fullpath_out);
                }
                Console.WriteLine("Output path: {0}", fullpath_out);
                if (args.Length > 3)
                {
                    mdlanimfiles = new List <string>();
                    Console.WriteLine("Animation files:");
                    for (int u = 3; u < args.Length; u++)
                    {
                        string animpath = Path.GetFullPath(args[u]);
                        if (File.Exists(animpath))
                        {
                            mdlanimfiles.Add(animpath);
                            Console.WriteLine(animpath);
                        }
                        else
                        {
                            Console.WriteLine("File {0} doesn't exist.", animpath);
                        }
                    }
                    SA_Tools.SAArc.sa2MDL.Split(bigendian, fullpath_mdl, fullpath_out, mdlanimfiles.ToArray());
                }
                else
                {
                    SA_Tools.SAArc.sa2MDL.Split(bigendian, fullpath_mdl, fullpath_out, null);
                }
                break;

            case "dllexport":
                string fullpath_dllex = Path.GetFullPath(args[1]);
                string type           = args[2];
                string name           = args[3];
                string fileOutputPath = "";
                if (args.Length > 4)
                {
                    for (int i = 4; i < args.Length; i++)
                    {
                        if (args[i].Substring(0, 1) != "-" && args[i - 1].Substring(0, 1) != "-")
                        {
                            fileOutputPath = args[i];
                        }
                    }
                }
                if (!File.Exists(fullpath_dllex))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_dllex);
                    return;
                }
                Console.Write("File: {0}", fullpath_dllex);
                byte[] datafile  = File.ReadAllBytes(fullpath_dllex);
                uint   imageBase = SA_Tools.HelperFunctions.SetupEXE(ref datafile).Value;
                Dictionary <string, int> exports;
                Dictionary <int, string> labels = new Dictionary <int, string>();
                {
                    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 namex = datafile.GetCString(BitConverter.ToInt32(datafile, nameaddr),
                                                           System.Text.Encoding.ASCII);
                        int addr = BitConverter.ToInt32(datafile,
                                                        dir.AddressOfFunctions + (BitConverter.ToInt16(datafile, ordaddr) * 4));
                        exports.Add(namex, addr);
                        labels.Add(addr, namex);
                        nameaddr += 4;
                        ordaddr  += 2;
                    }
                    Console.Write(" ({0} exports)\n", exports.Count);
                }
                if (!exports.ContainsKey(name))
                {
                    Console.WriteLine("The export table has no item named {0}", name);
                    return;
                }
                int address = exports[name];
                Console.WriteLine("{0} {1}:{2}", type, name, address.ToString("X8"));
                switch (type)
                {
                // Landtables
                case "landtable":
                case "sa1landtable":
                case "sadxlandtable":
                case "sa2landtable":
                case "sa2blandtable":
                case "battlelandtable":
                    LandTableFormat landfmt_cur;
                    string          landext;
                    switch (type)
                    {
                    case "sa1landtable":
                        landfmt_cur = LandTableFormat.SA1;
                        landext     = ".sa1lvl";
                        break;

                    case "sadxlandtable":
                        landfmt_cur = LandTableFormat.SADX;
                        landext     = ".sa1lvl";
                        break;

                    case "sa2landtable":
                        landfmt_cur = LandTableFormat.SA2;
                        landext     = ".sa2lvl";
                        break;

                    case "sa2blandtable":
                    case "battlelandtable":
                        landfmt_cur = LandTableFormat.SA2B;
                        landext     = ".sa2blvl";
                        break;

                    case "landtable":
                    default:
                        landfmt_cur = LandTableFormat.SADX;
                        landext     = ".sa1lvl";
                        break;
                    }
                    LandTable land = new LandTable(datafile, address, imageBase, landfmt_cur, labels);
                    if (fileOutputPath == "")
                    {
                        fileOutputPath = land.Name + landext;
                    }
                    if (!Directory.Exists(Path.GetDirectoryName(fileOutputPath)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                    }
                    land.SaveToFile(fileOutputPath, landfmt_cur, nometa);
                    break;

                // NJS_OBJECT
                case "model":
                case "object":
                case "basicmodel":
                case "basicdxmodel":
                case "chunkmodel":
                case "gcmodel":
                {
                    ModelFormat modelfmt_obj;
                    string      modelext;
                    switch (type)
                    {
                    case "basicmodel":
                        modelfmt_obj = ModelFormat.Basic;
                        modelext     = ".sa1mdl";
                        break;

                    case "basicdxmodel":
                        modelfmt_obj = ModelFormat.BasicDX;
                        modelext     = ".sa1mdl";
                        break;

                    case "chunkmodel":
                        modelfmt_obj = ModelFormat.Chunk;
                        modelext     = ".sa2mdl";
                        break;

                    case "gcmodel":
                        modelfmt_obj = ModelFormat.GC;
                        modelext     = ".sa2bmdl";
                        break;

                    default:
                        modelfmt_obj = ModelFormat.BasicDX;
                        modelext     = ".sa1mdl";
                        break;
                    }
                    NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, modelfmt_obj, labels, new Dictionary <int, Attach>());
                    if (fileOutputPath == "")
                    {
                        fileOutputPath = mdl.Name + modelext;
                    }
                    if (!Directory.Exists(Path.GetDirectoryName(fileOutputPath)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                    }
                    ModelFile.CreateFile(fileOutputPath, mdl, null, null, null, null, modelfmt_obj, nometa);
                }
                break;

                // NJS_MOTION
                case "animation":
                case "motion":
                    int numparts = 0;
                    for (int a = 3; a < args.Length; a++)
                    {
                        if (args[a] == "-p")
                        {
                            numparts = int.Parse(args[a + 1], System.Globalization.NumberStyles.Integer);
                        }
                    }
                    NJS_MOTION ani = new NJS_MOTION(datafile, address, imageBase, numparts, labels);
                    if (fileOutputPath == "")
                    {
                        fileOutputPath = ani.Name + ".saanim";
                    }
                    string outpath = Path.GetDirectoryName(Path.GetFullPath(fileOutputPath));
                    Console.WriteLine("Output file: {0}", Path.GetFullPath(fileOutputPath));
                    if (!Directory.Exists(outpath))
                    {
                        Directory.CreateDirectory(outpath);
                    }
                    ani.Save(fileOutputPath, nometa);
                    break;

                default:
                    Console.WriteLine("Unrecognized export type {0}", type);
                    break;
                }
                break;

            default:
                Console.WriteLine("Incorrect mode specified. Press ENTER to exit.");
                Console.ReadLine();
                return;
            }
        }
Пример #13
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;
            }
        }
Пример #14
0
 static void Main(string[] args)
 {
     if (args.Length == 0)
     {
         Console.Write("Filename: ");
         args = new string[] { Console.ReadLine().Trim('"') };
     }
     foreach (string filename in args)
     {
         Console.WriteLine("Splitting file {0}...", filename);
         byte[] fc;
         if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
         {
             fc = Prs.Decompress(filename);
         }
         else
         {
             fc = File.ReadAllBytes(filename);
         }
         EventIniData ini = new EventIniData()
         {
             Name = Path.GetFileNameWithoutExtension(filename)
         };
         string path = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(Path.GetFullPath(filename)), Path.GetFileNameWithoutExtension(filename))).FullName;
         uint   key;
         if (fc[0] == 0x81)
         {
             Console.WriteLine("File is in GC/PC format.");
             ByteConverter.BigEndian = true;
             key      = 0x8125FE60;
             ini.Game = Game.SA2B;
         }
         else
         {
             Console.WriteLine("File is in DC format.");
             ByteConverter.BigEndian = false;
             key      = 0xC600000;
             ini.Game = Game.SA2;
         }
         List <string> nodenames = new List <string>();
         Dictionary <string, KeyValuePair <string, NJS_OBJECT> > modelfiles = new Dictionary <string, KeyValuePair <string, NJS_OBJECT> >();
         int ptr = fc.GetPointer(0x20, key);
         if (ptr != 0)
         {
             for (int i = 0; i < (ini.Game == Game.SA2B ? 18 : 16); i++)
             {
                 UpgradeInfo info = new UpgradeInfo();
                 info.RootNode = GetModel(fc, ptr, key, Path.Combine(path, $"Upgrade {i + 1} Root.sa2mdl"), nodenames, modelfiles);
                 if (info.RootNode != null)
                 {
                     int ptr2 = fc.GetPointer(ptr + 4, key);
                     if (ptr2 != 0)
                     {
                         info.AttachNode1 = $"object_{ptr2:X8}";
                     }
                     info.Model1 = GetModel(fc, ptr + 8, key, Path.Combine(path, $"Upgrade {i + 1} Model 1.sa2mdl"), nodenames, modelfiles);
                     ptr2        = fc.GetPointer(ptr + 0xC, key);
                     if (ptr2 != 0)
                     {
                         info.AttachNode2 = $"object_{ptr2:X8}";
                     }
                     info.Model2 = GetModel(fc, ptr + 0x10, key, Path.Combine(path, $"Upgrade {i + 1} Model 2.sa2mdl"), nodenames, modelfiles);
                 }
                 ini.Upgrades.Add(info);
                 ptr += 0x14;
             }
         }
         else
         {
             Console.WriteLine("Event contains no character upgrades.");
         }
         int gcnt = ByteConverter.ToInt32(fc, 8);
         ptr = fc.GetPointer(0, key);
         if (ptr != 0)
         {
             Console.WriteLine("Event contains {0} group(s).", gcnt + 1);
             for (int gn = 0; gn <= gcnt; gn++)
             {
                 GroupInfo info = new GroupInfo();
                 int       ptr2 = fc.GetPointer(ptr, key);
                 int       ecnt = ByteConverter.ToInt32(fc, ptr + 4);
                 if (ptr2 != 0)
                 {
                     Console.WriteLine("Group {0} contains {1} entit{2}.", gn + 1, ecnt, ecnt == 1 ? "y" : "ies");
                     for (int en = 0; en < ecnt; en++)
                     {
                         string name = GetModel(fc, ptr2, key, Path.Combine(path, $"Group {gn + 1} Entity {en + 1} Model.sa2mdl"), nodenames, modelfiles);
                         if (name != null)
                         {
                             info.Entities.Add(name);
                         }
                         ptr2 += ini.Game == Game.SA2B ? 0x2C : 0x20;
                     }
                 }
                 else
                 {
                     Console.WriteLine("Group {0} contains no entities.", gn + 1);
                 }
                 ptr2 = fc.GetPointer(ptr + 0x18, key);
                 if (ptr2 != 0)
                 {
                     info.Big = GetModel(fc, ptr2, key, Path.Combine(path, $"Group {gn + 1} Big Model.sa2mdl"), nodenames, modelfiles);
                 }
                 ini.Groups.Add(info);
                 ptr += 0x20;
             }
         }
         else
         {
             Console.WriteLine("Event contains no groups.");
         }
         ptr = fc.GetPointer(0x18, key);
         if (ptr != 0)
         {
             for (int i = 0; i < 93; i++)
             {
                 string name = GetModel(fc, ptr, key, Path.Combine(path, $"Mech Part {i + 1}.sa2mdl"), nodenames, modelfiles);
                 if (name != null)
                 {
                     ini.MechParts.Add(i, name);
                 }
                 ptr += 4;
             }
         }
         else
         {
             Console.WriteLine("Event contains no mech parts.");
         }
         ptr = fc.GetPointer(0x1C, key);
         if (ptr != 0)
         {
             ini.TailsTails = GetModel(fc, ptr, key, Path.Combine(path, $"Tails tails.sa2mdl"), nodenames, modelfiles);
         }
         else
         {
             Console.WriteLine("Event does not contain Tails' tails.");
         }
         foreach (var item in modelfiles)
         {
             ModelFile.CreateFile(item.Value.Key, item.Value.Value, null, null, null, null, null, ModelFormat.Chunk);
             ini.Files.Add(Path.GetFileName(item.Value.Key), HelperFunctions.FileHash(item.Value.Key));
         }
         IniSerializer.Serialize(ini, Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".ini")));
     }
 }
Пример #15
0
        static void Main(string[] args)
        {
            string filename;

            if (args.Length > 0)
            {
                filename = args[0];
                Console.WriteLine("File: {0}", filename);
            }
            else
            {
                Console.Write("File: ");
                filename = Console.ReadLine();
            }
            ModelFile model = new ModelFile(filename);

            switch (model.Format)
            {
            case ModelFormat.Basic:
                foreach (NJS_OBJECT obj in model.Model.GetObjects().Where(obj => obj.Attach is BasicAttach))
                {
                    BasicAttach basatt = (BasicAttach)obj.Attach;
                    ChunkAttach cnkatt = new ChunkAttach(true, true)
                    {
                        Name = basatt.Name + "_cnk", Bounds = basatt.Bounds
                    };
                    obj.Attach = cnkatt;
                    VertexChunk vcnk;
                    bool        hasnormal = basatt.Normal?.Length > 0;
                    bool        hasvcolor = basatt.Mesh.Any(a => a.VColor != null);
                    if (hasvcolor)
                    {
                        vcnk = new VertexChunk(ChunkType.Vertex_VertexDiffuse8);
                    }
                    else if (hasnormal)
                    {
                        vcnk = new VertexChunk(ChunkType.Vertex_VertexNormal);
                    }
                    else
                    {
                        vcnk = new VertexChunk(ChunkType.Vertex_Vertex);
                    }
                    List <CachedVertex>       cache  = new List <CachedVertex>(basatt.Vertex.Length);
                    List <List <Strip> >      strips = new List <List <Strip> >();
                    List <List <List <UV> > > uvs    = new List <List <List <UV> > >();
                    foreach (NJS_MESHSET mesh in basatt.Mesh)
                    {
                        List <Strip>      polys = new List <Strip>();
                        List <List <UV> > us    = null;
                        bool hasUV             = mesh.UV != null;
                        bool hasVColor         = mesh.VColor != null;
                        int  currentstriptotal = 0;
                        switch (mesh.PolyType)
                        {
                        case Basic_PolyType.Triangles:
                        {
                            List <ushort>           tris  = new List <ushort>();
                            Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>();
                            foreach (Poly poly in mesh.Poly)
                            {
                                for (int i = 0; i < 3; i++)
                                {
                                    ushort ind = (ushort)cache.AddUnique(new CachedVertex(
                                                                             basatt.Vertex[poly.Indexes[i]],
                                                                             basatt.Normal[poly.Indexes[i]],
                                                                             hasVColor ? mesh.VColor[currentstriptotal] : Color.White,
                                                                             mesh.UV?[currentstriptotal]));
                                    if (hasUV)
                                    {
                                        uvmap[ind] = mesh.UV[currentstriptotal];
                                    }
                                    ++currentstriptotal;
                                    tris.Add(ind);
                                }
                            }

                            if (hasUV)
                            {
                                us = new List <List <UV> >();
                            }

                            nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups);

                            // Add strips
                            for (var i = 0; i < primitiveGroups.Length; i++)
                            {
                                var primitiveGroup = primitiveGroups[i];
                                System.Diagnostics.Debug.Assert(primitiveGroup.Type == PrimitiveType.TriangleStrip);

                                var       stripIndices = new ushort[primitiveGroup.Indices.Length];
                                List <UV> stripuv      = new List <UV>();
                                for (var j = 0; j < primitiveGroup.Indices.Length; j++)
                                {
                                    var vertexIndex = primitiveGroup.Indices[j];
                                    stripIndices[j] = vertexIndex;
                                    if (hasUV)
                                    {
                                        stripuv.Add(uvmap[vertexIndex]);
                                    }
                                }

                                polys.Add(new Strip(stripIndices, false));
                                if (hasUV)
                                {
                                    us.Add(stripuv);
                                }
                            }
                        }
                        break;

                        case Basic_PolyType.Quads:
                        {
                            List <ushort>           tris  = new List <ushort>();
                            Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>();
                            foreach (Poly poly in mesh.Poly)
                            {
                                ushort[] quad = new ushort[4];
                                for (int i = 0; i < 4; i++)
                                {
                                    ushort ind = (ushort)cache.AddUnique(new CachedVertex(
                                                                             basatt.Vertex[poly.Indexes[i]],
                                                                             basatt.Normal[poly.Indexes[i]],
                                                                             hasVColor ? mesh.VColor[currentstriptotal] : Color.White,
                                                                             mesh.UV?[currentstriptotal]));
                                    if (hasUV)
                                    {
                                        uvmap[ind] = mesh.UV[currentstriptotal];
                                    }
                                    ++currentstriptotal;
                                    quad[i] = ind;
                                }
                                tris.Add(quad[0]);
                                tris.Add(quad[1]);
                                tris.Add(quad[2]);
                                tris.Add(quad[2]);
                                tris.Add(quad[1]);
                                tris.Add(quad[3]);
                            }

                            if (hasUV)
                            {
                                us = new List <List <UV> >();
                            }

                            nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups);

                            // Add strips
                            for (var i = 0; i < primitiveGroups.Length; i++)
                            {
                                var primitiveGroup = primitiveGroups[i];
                                System.Diagnostics.Debug.Assert(primitiveGroup.Type == PrimitiveType.TriangleStrip);

                                var       stripIndices = new ushort[primitiveGroup.Indices.Length];
                                List <UV> stripuv      = new List <UV>();
                                for (var j = 0; j < primitiveGroup.Indices.Length; j++)
                                {
                                    var vertexIndex = primitiveGroup.Indices[j];
                                    stripIndices[j] = vertexIndex;
                                    if (hasUV)
                                    {
                                        stripuv.Add(uvmap[vertexIndex]);
                                    }
                                }

                                polys.Add(new Strip(stripIndices, false));
                                if (hasUV)
                                {
                                    us.Add(stripuv);
                                }
                            }
                        }
                        break;

                        case Basic_PolyType.NPoly:
                        case Basic_PolyType.Strips:
                            if (hasUV)
                            {
                                us = new List <List <UV> >();
                            }
                            foreach (Strip poly in mesh.Poly.Cast <Strip>())
                            {
                                List <UV> stripuv = new List <UV>();
                                ushort[]  inds    = (ushort[])poly.Indexes.Clone();
                                for (int i = 0; i < poly.Indexes.Length; i++)
                                {
                                    inds[i] = (ushort)cache.AddUnique(new CachedVertex(
                                                                          basatt.Vertex[poly.Indexes[i]],
                                                                          basatt.Normal[poly.Indexes[i]],
                                                                          hasVColor ? mesh.VColor[currentstriptotal] : Color.White));
                                    if (hasUV)
                                    {
                                        stripuv.Add(mesh.UV[currentstriptotal]);
                                    }
                                    ++currentstriptotal;
                                }

                                polys.Add(new Strip(inds, poly.Reversed));
                                if (hasUV)
                                {
                                    us.Add(stripuv);
                                }
                            }
                            break;
                        }
                        strips.Add(polys);
                        uvs.Add(us);
                    }
                    foreach (var item in cache)
                    {
                        vcnk.Vertices.Add(item.vertex);
                        if (hasnormal)
                        {
                            vcnk.Normals.Add(item.normal);
                        }
                        if (hasvcolor)
                        {
                            vcnk.Diffuse.Add(item.color);
                        }
                    }
                    vcnk.VertexCount = (ushort)cache.Count;
                    switch (vcnk.Type)
                    {
                    case ChunkType.Vertex_Vertex:
                        vcnk.Size = (ushort)(vcnk.VertexCount * 3 + 1);
                        break;

                    case ChunkType.Vertex_VertexDiffuse8:
                        vcnk.Size = (ushort)(vcnk.VertexCount * 4 + 1);
                        break;

                    case ChunkType.Vertex_VertexNormal:
                        vcnk.Size = (ushort)(vcnk.VertexCount * 6 + 1);
                        break;

                    case ChunkType.Vertex_VertexNormalDiffuse8:
                        vcnk.Size = (ushort)(vcnk.VertexCount * 7 + 1);
                        break;
                    }
                    cnkatt.Vertex.Add(vcnk);
                    for (int i = 0; i < basatt.Mesh.Count; i++)
                    {
                        NJS_MESHSET  mesh = basatt.Mesh[i];
                        NJS_MATERIAL mat  = null;
                        if (basatt.Material != null && mesh.MaterialID < basatt.Material.Count)
                        {
                            mat = basatt.Material[mesh.MaterialID];
                            cnkatt.Poly.Add(new PolyChunkTinyTextureID()
                            {
                                ClampU      = mat.ClampU,
                                ClampV      = mat.ClampV,
                                FilterMode  = mat.FilterMode,
                                FlipU       = mat.FlipU,
                                FlipV       = mat.FlipV,
                                SuperSample = mat.SuperSample,
                                TextureID   = (ushort)mat.TextureID
                            });
                            cnkatt.Poly.Add(new PolyChunkMaterial()
                            {
                                SourceAlpha      = mat.SourceAlpha,
                                DestinationAlpha = mat.DestinationAlpha,
                                Diffuse          = mat.DiffuseColor,
                                Specular         = mat.SpecularColor,
                                SpecularExponent = (byte)mat.Exponent
                            });
                        }
                        PolyChunkStrip strip;
                        if (mesh.UV != null)
                        {
                            strip = new PolyChunkStrip(ChunkType.Strip_StripUVN);
                        }
                        else
                        {
                            strip = new PolyChunkStrip(ChunkType.Strip_Strip);
                        }
                        if (mat != null)
                        {
                            strip.IgnoreLight        = mat.IgnoreLighting;
                            strip.IgnoreSpecular     = mat.IgnoreSpecular;
                            strip.UseAlpha           = mat.UseAlpha;
                            strip.DoubleSide         = mat.DoubleSided;
                            strip.FlatShading        = mat.FlatShading;
                            strip.EnvironmentMapping = mat.EnvironmentMap;
                        }
                        for (int i1 = 0; i1 < strips[i].Count; i1++)
                        {
                            Strip item = strips[i][i1];
                            UV[]  uv2  = null;
                            if (mesh.UV != null)
                            {
                                uv2 = uvs[i][i1].ToArray();
                            }
                            strip.Strips.Add(new PolyChunkStrip.Strip(item.Reversed, item.Indexes, uv2, null));
                        }
                        cnkatt.Poly.Add(strip);
                    }
                }
                ModelFile.CreateFile(System.IO.Path.ChangeExtension(filename, "sa2mdl"), model.Model, null, null, null, null, null, ModelFormat.Chunk);
                break;

            case ModelFormat.Chunk:
                Vertex[] VertexBuffer = new Vertex[0];
                Vertex[] NormalBuffer = new Vertex[0];
                foreach (NJS_OBJECT obj in model.Model.GetObjects().Where(obj => obj.Attach is ChunkAttach))
                {
                    ChunkAttach cnkatt = (ChunkAttach)obj.Attach;
                    BasicAttach basatt = new BasicAttach()
                    {
                        Name = cnkatt.Name, Bounds = cnkatt.Bounds
                    };
                    obj.Attach = basatt;
                    if (cnkatt.Vertex != null)
                    {
                        foreach (VertexChunk chunk in cnkatt.Vertex)
                        {
                            if (VertexBuffer.Length < chunk.IndexOffset + chunk.VertexCount)
                            {
                                Array.Resize(ref VertexBuffer, chunk.IndexOffset + chunk.VertexCount);
                                Array.Resize(ref NormalBuffer, chunk.IndexOffset + chunk.VertexCount);
                            }
                            Array.Copy(chunk.Vertices.ToArray(), 0, VertexBuffer, chunk.IndexOffset, chunk.Vertices.Count);
                            Array.Copy(chunk.Normals.ToArray(), 0, NormalBuffer, chunk.IndexOffset, chunk.Normals.Count);
                        }
                    }
                    NJS_MATERIAL material = new NJS_MATERIAL()
                    {
                        UseTexture = true
                    };
                    int minVtx = int.MaxValue;
                    int maxVtx = int.MinValue;
                    foreach (PolyChunk chunk in cnkatt.Poly)
                    {
                        switch (chunk.Type)
                        {
                        case ChunkType.Bits_BlendAlpha:
                        {
                            PolyChunkBitsBlendAlpha c2 = (PolyChunkBitsBlendAlpha)chunk;
                            material.SourceAlpha      = c2.SourceAlpha;
                            material.DestinationAlpha = c2.DestinationAlpha;
                        }
                        break;

                        case ChunkType.Bits_MipmapDAdjust:
                            break;

                        case ChunkType.Bits_SpecularExponent:
                            material.Exponent = ((PolyChunkBitsSpecularExponent)chunk).SpecularExponent;
                            break;

                        case ChunkType.Tiny_TextureID:
                        case ChunkType.Tiny_TextureID2:
                        {
                            PolyChunkTinyTextureID c2 = (PolyChunkTinyTextureID)chunk;
                            material.ClampU      = c2.ClampU;
                            material.ClampV      = c2.ClampV;
                            material.FilterMode  = c2.FilterMode;
                            material.FlipU       = c2.FlipU;
                            material.FlipV       = c2.FlipV;
                            material.SuperSample = c2.SuperSample;
                            material.TextureID   = c2.TextureID;
                        }
                        break;

                        case ChunkType.Material_Diffuse:
                        case ChunkType.Material_Ambient:
                        case ChunkType.Material_DiffuseAmbient:
                        case ChunkType.Material_Specular:
                        case ChunkType.Material_DiffuseSpecular:
                        case ChunkType.Material_AmbientSpecular:
                        case ChunkType.Material_DiffuseAmbientSpecular:
                        case ChunkType.Material_Diffuse2:
                        case ChunkType.Material_Ambient2:
                        case ChunkType.Material_DiffuseAmbient2:
                        case ChunkType.Material_Specular2:
                        case ChunkType.Material_DiffuseSpecular2:
                        case ChunkType.Material_AmbientSpecular2:
                        case ChunkType.Material_DiffuseAmbientSpecular2:
                        {
                            PolyChunkMaterial c2 = (PolyChunkMaterial)chunk;
                            material.SourceAlpha      = c2.SourceAlpha;
                            material.DestinationAlpha = c2.DestinationAlpha;
                            if (c2.Diffuse.HasValue)
                            {
                                material.DiffuseColor = c2.Diffuse.Value;
                            }
                            if (c2.Specular.HasValue)
                            {
                                material.SpecularColor = c2.Specular.Value;
                                material.Exponent      = c2.SpecularExponent;
                            }
                        }
                        break;

                        case ChunkType.Strip_Strip:
                        case ChunkType.Strip_StripUVN:
                        case ChunkType.Strip_StripUVH:
                        case ChunkType.Strip_StripNormal:
                        case ChunkType.Strip_StripUVNNormal:
                        case ChunkType.Strip_StripUVHNormal:
                        case ChunkType.Strip_StripColor:
                        case ChunkType.Strip_StripUVNColor:
                        case ChunkType.Strip_StripUVHColor:
                        case ChunkType.Strip_Strip2:
                        case ChunkType.Strip_StripUVN2:
                        case ChunkType.Strip_StripUVH2:
                        {
                            PolyChunkStrip c2 = (PolyChunkStrip)chunk;
                            material.DoubleSided    = c2.DoubleSide;
                            material.EnvironmentMap = c2.EnvironmentMapping;
                            material.FlatShading    = c2.FlatShading;
                            material.IgnoreLighting = c2.IgnoreLight;
                            material.IgnoreSpecular = c2.IgnoreSpecular;
                            material.UseAlpha       = c2.UseAlpha;
                            bool hasVColor = false;
                            switch (chunk.Type)
                            {
                            case ChunkType.Strip_StripColor:
                            case ChunkType.Strip_StripUVNColor:
                            case ChunkType.Strip_StripUVHColor:
                                hasVColor = true;
                                break;
                            }
                            bool hasUV = false;
                            switch (chunk.Type)
                            {
                            case ChunkType.Strip_StripUVN:
                            case ChunkType.Strip_StripUVH:
                            case ChunkType.Strip_StripUVNColor:
                            case ChunkType.Strip_StripUVHColor:
                            case ChunkType.Strip_StripUVN2:
                            case ChunkType.Strip_StripUVH2:
                                hasUV = true;
                                break;
                            }
                            List <Strip> strips  = new List <Strip>(c2.StripCount);
                            List <UV>    uvs     = hasUV ? new List <UV>() : null;
                            List <Color> vcolors = hasVColor ? new List <Color>() : null;
                            foreach (PolyChunkStrip.Strip strip in c2.Strips)
                            {
                                minVtx = Math.Min(minVtx, strip.Indexes.Min());
                                maxVtx = Math.Max(maxVtx, strip.Indexes.Max());
                                strips.Add(new Strip((ushort[])strip.Indexes.Clone(), strip.Reversed));
                                if (hasUV)
                                {
                                    uvs.AddRange(strip.UVs);
                                }
                                if (hasVColor)
                                {
                                    vcolors.AddRange(strip.VColors);
                                }
                            }
                            NJS_MESHSET mesh = new NJS_MESHSET(strips.ToArray(), false, hasUV, hasVColor);
                            if (hasUV)
                            {
                                uvs.CopyTo(mesh.UV);
                            }
                            if (hasVColor)
                            {
                                vcolors.CopyTo(mesh.VColor);
                            }
                            mesh.MaterialID = (ushort)basatt.Material.Count;
                            basatt.Mesh.Add(mesh);
                            basatt.Material.Add(material);
                            material = new NJS_MATERIAL(material.GetBytes(), 0);
                        }
                        break;
                        }
                    }
                    int numVtx = maxVtx - minVtx + 1;
                    basatt.ResizeVertexes(numVtx);
                    Array.Copy(VertexBuffer, minVtx, basatt.Vertex, 0, numVtx);
                    Array.Copy(NormalBuffer, minVtx, basatt.Normal, 0, numVtx);
                    foreach (NJS_MESHSET mesh in basatt.Mesh)
                    {
                        foreach (Poly poly in mesh.Poly)
                        {
                            for (int i = 0; i < poly.Indexes.Length; i++)
                            {
                                poly.Indexes[i] = (ushort)(poly.Indexes[i] - minVtx);
                            }
                        }
                    }
                }
                ModelFile.CreateFile(System.IO.Path.ChangeExtension(filename, "sa1mdl"), model.Model, null, null, null, null, null, ModelFormat.Basic);
                break;
            }
        }
Пример #16
0
        private void button1_Click(object sender, EventArgs e)
        {
            bool success = false;
            uint address = (uint)numericUpDownBinaryAddress.Value;

            if (checkBoxBinaryMemory.Checked)
            {
                address -= (uint)numericUpDownBinaryKey.Value;
            }
            LandTableFormat format = (LandTableFormat)comboBoxBinaryFormat.SelectedIndex;
            LandTableFormat outfmt = format;

            if (format == LandTableFormat.SADX)
            {
                outfmt = LandTableFormat.SA1;
            }
            ByteConverter.BigEndian = checkBoxBinaryBigEndian.Checked;
            Settings.Author         = textBoxBinaryAuthor.Text;
            Settings.Save();
            SaveFileDialog sd = new SaveFileDialog();

            switch (comboBoxBinaryItemType.SelectedIndex)
            {
            //Level
            case 0:
                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)numericUpDownBinaryAddress.Value, (uint)numericUpDownBinaryKey.Value, format)
                    {
                        Author = textBoxBinaryAuthor.Text, Description = textBoxBinaryDescription.Text
                    }.SaveToFile(sd.FileName, outfmt);
                    if (checkBoxBinaryStructs.Checked)
                    {
                        ConvertToText(sd.FileName);
                    }
                    if (!checkBoxBinarySAModel.Checked)
                    {
                        File.Delete(sd.FileName);
                    }
                    success = true;
                }
                break;

            //Model
            case 1:
                sd = new SaveFileDialog()
                {
                    DefaultExt = outfmt.ToString().ToLowerInvariant() + "mdl", Filter = outfmt.ToString().ToUpperInvariant() + "MDL Files|*." + outfmt.ToString().ToLowerInvariant() + "mdl|All Files|*.*"
                };
                if (sd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    NJS_OBJECT tempmodel = new NJS_OBJECT(file, (int)address, (uint)numericUpDownBinaryKey.Value, (ModelFormat)comboBoxBinaryFormat.SelectedIndex, null);
                    ModelFile.CreateFile(sd.FileName, tempmodel, null, textBoxBinaryAuthor.Text, textBoxBinaryDescription.Text, null, (ModelFormat)comboBoxBinaryFormat.SelectedIndex);
                    ConvertToText(sd.FileName, checkBoxBinaryStructs.Checked, checkBoxBinaryNJA.Checked, false);
                    if (!checkBoxBinarySAModel.Checked)
                    {
                        File.Delete(sd.FileName);
                    }
                    success = true;
                }
                break;

            //Action
            case 2:
                sd = new SaveFileDialog()
                {
                    DefaultExt = outfmt.ToString().ToLowerInvariant() + "mdl", Filter = outfmt.ToString().ToUpperInvariant() + "MDL Files|*." + outfmt.ToString().ToLowerInvariant() + "mdl|All Files|*.*"
                };
                if (sd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    //Model
                    NJS_ACTION tempaction = new NJS_ACTION(file, (int)address, (uint)numericUpDownBinaryKey.Value, (ModelFormat)comboBoxBinaryFormat.SelectedIndex, null);
                    NJS_OBJECT tempmodel  = tempaction.Model;
                    ModelFile.CreateFile(sd.FileName, tempmodel, null, textBoxBinaryAuthor.Text, textBoxBinaryDescription.Text, null, (ModelFormat)comboBoxBinaryFormat.SelectedIndex);
                    ConvertToText(sd.FileName, checkBoxBinaryStructs.Checked, checkBoxBinaryNJA.Checked, false);
                    if (!checkBoxBinarySAModel.Checked)
                    {
                        File.Delete(sd.FileName);
                    }

                    //Action
                    string saanimPath = Path.Combine(Path.GetDirectoryName(sd.FileName), Path.GetFileNameWithoutExtension(sd.FileName) + ".saanim");

                    tempaction.Animation.Save(saanimPath);
                    ConvertToText(saanimPath, checkBoxBinaryStructs.Checked, false, checkBoxBinaryJSON.Checked);

                    if (checkBoxBinarySAModel.Checked)
                    {
                        using (TextWriter twmain = File.CreateText(Path.Combine(Path.GetDirectoryName(sd.FileName), Path.GetFileNameWithoutExtension(sd.FileName) + ".action")))
                        {
                            twmain.WriteLine(Path.GetFileName(saanimPath));
                            twmain.Flush();
                            twmain.Close();
                        }
                    }
                    else
                    {
                        File.Delete(saanimPath);
                    }
                    success = true;
                }
                break;
            }
            if (success)
            {
                MessageBox.Show("Data extracted!", "Binary Data Extractor", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
Пример #17
0
        public static void Split(string filename)
        {
            nodenames.Clear();
            modelfiles.Clear();
            motionfiles.Clear();

            Console.WriteLine("Splitting file {0}...", filename);
            byte[] fc;
            if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
            {
                fc = Prs.Decompress(filename);
            }
            else
            {
                fc = File.ReadAllBytes(filename);
            }
            MiniEventIniData ini = new MiniEventIniData()
            {
                Name = Path.GetFileNameWithoutExtension(filename)
            };
            string            path = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(Path.GetFullPath(filename)), Path.GetFileNameWithoutExtension(filename))).FullName;
            uint              key;
            List <NJS_MOTION> motions = null;

            if (fc[4] == 0x81)
            {
                Console.WriteLine("File is in GC/PC format.");
                ByteConverter.BigEndian = true;
                key      = 0x816DFE60;
                ini.Game = Game.SA2B;
            }
            else
            {
                Console.WriteLine("File is in DC format.");
                ByteConverter.BigEndian = false;
                key      = 0xCB00000;
                ini.Game = Game.SA2;
            }
            int ptr = fc.GetPointer(8, key);

            if (ptr != 0)
            {
                Console.WriteLine("Sonic is in this Mini-Event");
                Directory.CreateDirectory(Path.Combine(path, "Sonic"));
                MiniEventChars data = new MiniEventChars();
                data.BodyAnims = GetMotion(fc, ptr, key, $"Sonic\\Body.saanim", motions, 62);
                int ptr2 = fc.GetPointer(ptr + 4, key);
                if (ptr2 != 0)
                {
                    data.HeadPart = GetModel(fc, ptr + 4, key, $"Sonic\\Head.sa2mdl");
                }
                if (data.HeadPart != null)
                {
                    data.HeadAnims = GetMotion(fc, ptr + 8, key, $"Sonic\\Head.saanim", motions, modelfiles[data.HeadPart].Model.CountAnimated());
                    if (data.HeadAnims != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"Head.saanim");
                    }
                    data.HeadShapeMotions = GetMotion(fc, ptr + 0xC, key, $"Sonic\\HeadShape.saanim", motions, modelfiles[data.HeadPart].Model.CountMorph());
                    if (data.HeadShapeMotions != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"HeadShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x10, key);
                if (ptr2 != 0)
                {
                    data.MouthPart = GetModel(fc, ptr + 0x10, key, $"Sonic\\Mouth.sa2mdl");
                }
                if (data.MouthPart != null)
                {
                    data.MouthAnims = GetMotion(fc, ptr + 0x14, key, $"Sonic\\Mouth.saanim", motions, modelfiles[data.MouthPart].Model.CountAnimated());
                    if (data.MouthAnims != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"Mouth.saanim");
                    }
                    data.MouthShapeMotions = GetMotion(fc, ptr + 0x18, key, $"Sonic\\MouthShape.saanim", motions, modelfiles[data.MouthPart].Model.CountMorph());
                    if (data.MouthShapeMotions != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"MouthShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x1C, key);
                if (ptr2 != 0)
                {
                    data.LHandPart = GetModel(fc, ptr + 0x1C, key, $"Sonic\\LeftHand.sa2mdl");
                }
                if (data.LHandPart != null)
                {
                    data.LHandAnims = GetMotion(fc, ptr + 0x20, key, $"Sonic\\LeftHand.saanim", motions, modelfiles[data.LHandPart].Model.CountAnimated());
                    if (data.LHandAnims != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHand.saanim");
                    }
                    data.LHandShapeMotions = GetMotion(fc, ptr + 0x24, key, $"Sonic\\LeftHandShape.saanim", motions, modelfiles[data.LHandPart].Model.CountMorph());
                    if (data.LHandShapeMotions != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHandShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x28, key);
                if (ptr2 != 0)
                {
                    data.RHandPart = GetModel(fc, ptr + 0x28, key, $"Sonic\\RightHand.sa2mdl");
                }
                if (data.RHandPart != null)
                {
                    data.RHandAnims = GetMotion(fc, ptr + 0x2C, key, $"Sonic\\RightHand.saanim", motions, modelfiles[data.RHandPart].Model.CountAnimated());
                    if (data.RHandAnims != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHand.saanim");
                    }
                    data.RHandShapeMotions = GetMotion(fc, ptr + 0x30, key, $"Sonic\\RightHandShape.saanim", motions, modelfiles[data.RHandPart].Model.CountMorph());
                    if (data.RHandShapeMotions != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHandShape.saanim");
                    }
                }
                ini.Sonic.Add(data);
            }
            ptr = fc.GetPointer(0xC, key);
            if (ptr != 0)
            {
                Console.WriteLine("Shadow is in this Mini-Event");
                Directory.CreateDirectory(Path.Combine(path, "Shadow"));
                MiniEventChars data = new MiniEventChars();
                data.BodyAnims = GetMotion(fc, ptr, key, $"Shadow\\Body.saanim", motions, 62);
                int ptr2 = fc.GetPointer(ptr + 4, key);
                if (ptr2 != 0)
                {
                    data.HeadPart = GetModel(fc, ptr + 4, key, $"Shadow\\Head.sa2mdl");
                }
                if (data.HeadPart != null)
                {
                    data.HeadAnims = GetMotion(fc, ptr + 8, key, $"Shadow\\Head.saanim", motions, modelfiles[data.HeadPart].Model.CountAnimated());
                    if (data.HeadAnims != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"Head.saanim");
                    }
                    data.HeadShapeMotions = GetMotion(fc, ptr + 0xC, key, $"Shadow\\HeadShape.saanim", motions, modelfiles[data.HeadPart].Model.CountMorph());
                    if (data.HeadShapeMotions != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"HeadShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x10, key);
                if (ptr2 != 0)
                {
                    data.MouthPart = GetModel(fc, ptr + 0x10, key, $"Shadow\\Mouth.sa2mdl");
                }
                if (data.MouthPart != null)
                {
                    data.MouthAnims = GetMotion(fc, ptr + 0x14, key, $"Shadow\\Mouth.saanim", motions, modelfiles[data.MouthPart].Model.CountAnimated());
                    if (data.MouthAnims != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"Mouth.saanim");
                    }
                    data.MouthShapeMotions = GetMotion(fc, ptr + 0x18, key, $"Shadow\\MouthShape.saanim", motions, modelfiles[data.MouthPart].Model.CountMorph());
                    if (data.MouthShapeMotions != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"MouthShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x1C, key);
                if (ptr2 != 0)
                {
                    data.LHandPart = GetModel(fc, ptr + 0x1C, key, $"Shadow\\LeftHand.sa2mdl");
                }
                if (data.LHandPart != null)
                {
                    data.LHandAnims = GetMotion(fc, ptr + 0x20, key, $"Shadow\\LeftHand.saanim", motions, modelfiles[data.LHandPart].Model.CountAnimated());
                    if (data.LHandAnims != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHand.saanim");
                    }
                    data.LHandShapeMotions = GetMotion(fc, ptr + 0x24, key, $"Shadow\\LeftHandShape.saanim", motions, modelfiles[data.LHandPart].Model.CountMorph());
                    if (data.LHandShapeMotions != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHandShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x28, key);
                if (ptr2 != 0)
                {
                    data.RHandPart = GetModel(fc, ptr + 0x28, key, $"Shadow\\RightHand.sa2mdl");
                }
                if (data.RHandPart != null)
                {
                    data.RHandAnims = GetMotion(fc, ptr + 0x2C, key, $"Shadow\\RightHand.saanim", motions, modelfiles[data.RHandPart].Model.CountAnimated());
                    if (data.RHandAnims != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHand.saanim");
                    }
                    data.RHandShapeMotions = GetMotion(fc, ptr + 0x30, key, $"Shadow\\RightHandShape.saanim", motions, modelfiles[data.RHandPart].Model.CountMorph());
                    if (data.RHandShapeMotions != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHandShape.saanim");
                    }
                }
                ini.Shadow.Add(data);
            }
            ptr = fc.GetPointer(0x18, key);
            if (ptr != 0)
            {
                Console.WriteLine("Knuckles is in this Mini-Event");
                Directory.CreateDirectory(Path.Combine(path, "Knuckles"));
                MiniEventChars data = new MiniEventChars();
                data.BodyAnims = GetMotion(fc, ptr, key, $"Knuckles\\Body.saanim", motions, 62);
                int ptr2 = fc.GetPointer(ptr + 4, key);
                if (ptr2 != 0)
                {
                    data.HeadPart = GetModel(fc, ptr + 4, key, $"Knuckles\\Head.sa2mdl");
                }
                if (data.HeadPart != null)
                {
                    data.HeadAnims = GetMotion(fc, ptr + 8, key, $"Knuckles\\Head.saanim", motions, modelfiles[data.HeadPart].Model.CountAnimated());
                    if (data.HeadAnims != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"Head.saanim");
                    }
                    data.HeadShapeMotions = GetMotion(fc, ptr + 0xC, key, $"Knuckles\\HeadShape.saanim", motions, modelfiles[data.HeadPart].Model.CountMorph());
                    if (data.HeadShapeMotions != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"HeadShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x10, key);
                if (ptr2 != 0)
                {
                    data.MouthPart = GetModel(fc, ptr + 0x10, key, $"Knuckles\\Mouth.sa2mdl");
                }
                if (data.MouthPart != null)
                {
                    data.MouthAnims = GetMotion(fc, ptr + 0x14, key, $"Knuckles\\Mouth.saanim", motions, modelfiles[data.MouthPart].Model.CountAnimated());
                    if (data.MouthAnims != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"Mouth.saanim");
                    }
                    data.MouthShapeMotions = GetMotion(fc, ptr + 0x18, key, $"Knuckles\\MouthShape.saanim", motions, modelfiles[data.MouthPart].Model.CountMorph());
                    if (data.MouthShapeMotions != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"MouthShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x1C, key);
                if (ptr2 != 0)
                {
                    data.LHandPart = GetModel(fc, ptr + 0x1C, key, $"Knuckles\\LeftHand.sa2mdl");
                }
                if (data.LHandPart != null)
                {
                    data.LHandAnims = GetMotion(fc, ptr + 0x20, key, $"Knuckles\\LeftHand.saanim", motions, modelfiles[data.LHandPart].Model.CountAnimated());
                    if (data.LHandAnims != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHand.saanim");
                    }
                    data.LHandShapeMotions = GetMotion(fc, ptr + 0x24, key, $"Knuckles\\LeftHandShape.saanim", motions, modelfiles[data.LHandPart].Model.CountMorph());
                    if (data.LHandShapeMotions != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHandShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x28, key);
                if (ptr2 != 0)
                {
                    data.RHandPart = GetModel(fc, ptr + 0x28, key, $"Knuckles\\RightHand.sa2mdl");
                }
                if (data.RHandPart != null)
                {
                    data.RHandAnims = GetMotion(fc, ptr + 0x2C, key, $"Knuckles\\RightHand.saanim", motions, modelfiles[data.RHandPart].Model.CountAnimated());
                    if (data.RHandAnims != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHand.saanim");
                    }
                    data.RHandShapeMotions = GetMotion(fc, ptr + 0x30, key, $"Knuckles\\RightHandShape.saanim", motions, modelfiles[data.RHandPart].Model.CountMorph());
                    if (data.RHandShapeMotions != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHandShape.saanim");
                    }
                }
                ini.Knuckles.Add(data);
            }
            ptr = fc.GetPointer(0x1C, key);
            if (ptr != 0)
            {
                Console.WriteLine("Rouge is in this Mini-Event");
                Directory.CreateDirectory(Path.Combine(path, "Rouge"));
                MiniEventChars data = new MiniEventChars();
                data.BodyAnims = GetMotion(fc, ptr, key, $"Rouge\\Body.saanim", motions, 62);
                int ptr2 = fc.GetPointer(ptr + 4, key);
                if (ptr2 != 0)
                {
                    data.HeadPart = GetModel(fc, ptr + 4, key, $"Rouge\\Head.sa2mdl");
                }
                if (data.HeadPart != null)
                {
                    data.HeadAnims = GetMotion(fc, ptr + 8, key, $"Rouge\\Head.saanim", motions, modelfiles[data.HeadPart].Model.CountAnimated());
                    if (data.HeadAnims != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"Head.saanim");
                    }
                    data.HeadShapeMotions = GetMotion(fc, ptr + 0xC, key, $"Rouge\\HeadShape.saanim", motions, modelfiles[data.HeadPart].Model.CountMorph());
                    if (data.HeadShapeMotions != null)
                    {
                        modelfiles[data.HeadPart].Motions.Add($"HeadShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x10, key);
                if (ptr2 != 0)
                {
                    data.MouthPart = GetModel(fc, ptr + 0x10, key, $"Rouge\\Mouth.sa2mdl");
                }
                if (data.MouthPart != null)
                {
                    data.MouthAnims = GetMotion(fc, ptr + 0x14, key, $"Rouge\\Mouth.saanim", motions, modelfiles[data.MouthPart].Model.CountAnimated());
                    if (data.MouthAnims != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"Mouth.saanim");
                    }
                    data.MouthShapeMotions = GetMotion(fc, ptr + 0x18, key, $"Rouge\\MouthShape.saanim", motions, modelfiles[data.MouthPart].Model.CountMorph());
                    if (data.MouthShapeMotions != null)
                    {
                        modelfiles[data.MouthPart].Motions.Add($"MouthShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x1C, key);
                if (ptr2 != 0)
                {
                    data.LHandPart = GetModel(fc, ptr + 0x1C, key, $"Rouge\\LeftHand.sa2mdl");
                }
                if (data.LHandPart != null)
                {
                    data.LHandAnims = GetMotion(fc, ptr + 0x20, key, $"Rouge\\LeftHand.saanim", motions, modelfiles[data.LHandPart].Model.CountAnimated());
                    if (data.LHandAnims != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHand.saanim");
                    }
                    data.LHandShapeMotions = GetMotion(fc, ptr + 0x24, key, $"Rouge\\LeftHandShape.saanim", motions, modelfiles[data.LHandPart].Model.CountMorph());
                    if (data.LHandShapeMotions != null)
                    {
                        modelfiles[data.LHandPart].Motions.Add($"LeftHandShape.saanim");
                    }
                }
                ptr2 = fc.GetPointer(ptr + 0x28, key);
                if (ptr2 != 0)
                {
                    data.RHandPart = GetModel(fc, ptr + 0x28, key, $"Rouge\\RightHand.sa2mdl");
                }
                if (data.RHandPart != null)
                {
                    data.RHandAnims = GetMotion(fc, ptr + 0x2C, key, $"Rouge\\RightHand.saanim", motions, modelfiles[data.RHandPart].Model.CountAnimated());
                    if (data.RHandAnims != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHand.saanim");
                    }
                    data.RHandShapeMotions = GetMotion(fc, ptr + 0x30, key, $"Rouge\\RightHandShape.saanim", motions, modelfiles[data.RHandPart].Model.CountMorph());
                    if (data.RHandShapeMotions != null)
                    {
                        modelfiles[data.RHandPart].Motions.Add($"RightHandShape.saanim");
                    }
                }
                ini.Rouge.Add(data);
            }
            ptr = fc.GetPointer(0x24, key);
            if (ptr != 0)
            {
                Console.WriteLine("Mech Eggman is in this Mini-Event");
                Directory.CreateDirectory(Path.Combine(path, "Mech Eggman"));
                ini.MechEggmanBodyAnims = GetMotion(fc, ptr, key, $"Mech Eggman\\Body.saanim", motions, 33);
            }
            ptr = fc.GetPointer(4, key);
            if (ptr != 0)
            {
                ini.Camera    = GetMotion(fc, ptr + 0x10, key, $"Camera.saanim", motions, 1);
                ini.CamFrames = ByteConverter.ToInt32(fc, ptr + 4);
            }
            else
            {
                Console.WriteLine("Mini-Event does not contain a camera.");
            }
            foreach (var item in motionfiles.Values)
            {
                string fn = item.Filename;
                string fp = Path.Combine(path, fn);
                item.Motion.Save(fp);
                ini.Files.Add(fn, HelperFunctions.FileHash(fp));
            }
            foreach (var item in modelfiles.Values)
            {
                string fp = Path.Combine(path, item.Filename);
                ModelFile.CreateFile(fp, item.Model, item.Motions.ToArray(), null, null, null, item.Format);
                ini.Files.Add(item.Filename, HelperFunctions.FileHash(fp));
            }
            JsonSerializer js = new JsonSerializer
            {
                Formatting        = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore
            };

            using (var tw = File.CreateText(Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".json"))))
                js.Serialize(tw, ini);
        }
Пример #18
0
        static void Main(string[] args)
        {
            bool writeall  = false;
            bool noencrypt = false;

            if (args.Length == 0)
            {
                Console.WriteLine("No file/folder specified.");
                Console.WriteLine("Press ENTER to exit");
                Console.ReadLine();
                return;
            }
            string filename = args[0];
            string fname    = Path.GetFileNameWithoutExtension(filename);
            string dir      = Path.Combine(Environment.CurrentDirectory, fname);

            for (int a = 0; a < args.Length; a++)
            {
                if (args[a] == "-w")
                {
                    writeall = true;
                }
                if (args[a] == "-d")
                {
                    noencrypt = true;
                }
                if (args[a] == "-t")
                {
                    byte[] zfile = File.ReadAllBytes(filename);
                    DecryptData(ref zfile);
                    File.WriteAllBytes(filename + "_dec.bin", zfile);
                    return;
                }
            }
            if (Directory.Exists(filename))
            {
                byte[] vms = ConvertMetadata(Path.Combine(dir, fname + ".ini"));
                if (!noencrypt)
                {
                    ProcessVMS(ref vms);
                }
                File.WriteAllBytes(Path.Combine(Environment.CurrentDirectory, fname + "_new.vms"), vms);
                Console.Write("Output file: {0}", Path.Combine(Environment.CurrentDirectory, fname + "_new.vms"));
                if (noencrypt)
                {
                    Console.Write(" (no encryption)");
                }
                Console.Write(System.Environment.NewLine);
                return;
            }
            byte[] file = File.ReadAllBytes(filename);
            Console.Write("Extracting DLC file: {0}", filename);
            if (BitConverter.ToUInt32(file, 0x280) != 0x2C0)
            {
                Console.Write(" (encrypted)");
                ProcessVMS(ref file);
            }
            if (writeall)
            {
                Console.Write(" with binary data");
            }
            Console.Write(System.Environment.NewLine);
            Directory.CreateDirectory(dir);
            MetatadaToINI(file, Path.Combine(dir, fname + ".ini"));
            SaveIcon(file, Path.Combine(dir, fname + ".bmp"));
            uint pvm_pointer = BitConverter.ToUInt32(file, 0x290);
            int  pvm_value   = BitConverter.ToInt32(file, 0x294);
            int  pvm_count   = BitConverter.ToInt32(file, 0x298);

            if (pvm_value != 0)
            {
                Console.WriteLine("PVM at {0}, number of textures {1}", pvm_pointer.ToString("X"), pvm_count);
            }
            uint mlt_pointer = BitConverter.ToUInt32(file, 0x29C);
            int  mlt_value   = BitConverter.ToInt32(file, 0x2A0);

            if (mlt_value != 0)
            {
                Console.WriteLine("MLT at {0}", mlt_pointer.ToString("X"));
            }
            uint prs_pointer = BitConverter.ToUInt32(file, 0x2A4);
            int  prs_value   = BitConverter.ToInt32(file, 0x2A8);

            if (prs_value != 0)
            {
                Console.WriteLine("PRS at {0}", prs_pointer.ToString("X"));
            }
            //Checksum
            uint crc = CalculateChecksum(ref file, 0x2C0, file.Length);

            Console.WriteLine("Checksum file / calculated: {0} ({1}) / {2} ({3})", BitConverter.ToInt32(file, 0x2AC).ToString("X"), BitConverter.ToInt32(file, 0x2AC), crc.ToString("X"), (int)crc);
            //Save sections
            int pvm_size = (int)mlt_pointer - (int)pvm_pointer;

            if (pvm_size > 0)
            {
                byte[] pvmdata = new byte[pvm_size];
                Array.Copy(file, pvm_pointer, pvmdata, 0, pvm_size);
                File.WriteAllBytes(Path.Combine(dir, fname + ".pvm"), pvmdata);
            }
            int mlt_size = (int)prs_pointer - (int)mlt_pointer;

            if (mlt_size > 0)
            {
                byte[] mltdata = new byte[mlt_size];
                Array.Copy(file, mlt_pointer, mltdata, 0, mlt_size);
                File.WriteAllBytes(Path.Combine(dir, fname + ".mlt"), mltdata);
            }
            uint sectionsize = BitConverter.ToUInt32(file, 0x48);
            int  text_count  = BitConverter.ToInt32(file, 0x28C);
            int  item_count  = BitConverter.ToInt32(file, 0x284);
            int  item_size   = (item_count * 30 + 12);          //12-byte header

            do
            {
                item_size++;
            }while (item_size % 16 != 0);
            int prs_size = file.Length - (int)prs_pointer;

            Console.WriteLine("Headerless size {0}, item size {1}, text size {2}, PVM size {3}, MLT size {4}, PRS size {5}", sectionsize, item_size, text_count * 64, pvm_size, mlt_size, prs_size);
            if (prs_size > 0)
            {
                byte[] prsdata = new byte[prs_size];
                //Console.WriteLine("Copy from array of size {0} from {1} to array size {2}", file.Length, prs_pointer, prsdata.Length);
                Array.Copy(file, prs_pointer, prsdata, 0, prs_size);
                if (writeall)
                {
                    File.WriteAllBytes(Path.Combine(dir, fname + ".prs"), prsdata);
                }
                prsdata = FraGag.Compression.Prs.Decompress(prsdata);
                if (writeall)
                {
                    File.WriteAllBytes(Path.Combine(dir, fname + ".bin"), prsdata);
                }
                //Model pointer
                uint modelpointer = BitConverter.ToUInt32(prsdata, 0) - 0xCCA4000;
                Console.WriteLine("Model pointer: {0}", modelpointer.ToString("X"));
                NJS_OBJECT mdl = new NJS_OBJECT(prsdata, (int)modelpointer, 0xCCA4000, ModelFormat.Basic, null);
                ModelFile.CreateFile((Path.Combine(dir, fname + ".sa1mdl")), mdl, null, null, null, null, ModelFormat.Basic);
            }
            Console.WriteLine("Output folder: {0}", dir);
        }
Пример #19
0
        static uint FindModel(string filename)
        {
            CurrentStep++;
            CurrentScanData         = "models similar to '" + Path.GetFileName(filename) + "'";
            ByteConverter.BigEndian = BigEndian;
            // Basic only for now
            uint        result    = 0;
            ModelFile   modelFile = new ModelFile(filename);
            ModelFormat modelfmt  = modelFile.Format;

            if (modelfmt == ModelFormat.Basic && BasicModelsAreDX)
            {
                modelfmt = ModelFormat.BasicDX;
            }
            Console.WriteLine("Model format: {0}", modelfmt);
            NJS_OBJECT originalmodel   = modelFile.Model;
            string     model_extension = ".sa1mdl";

            if (!SingleOutputFolder)
            {
                Directory.CreateDirectory(Path.Combine(OutputFolder, "models"));
            }
            for (uint address = StartAddress; address < EndAddress; address += 1)
            {
                if (CancelScan)
                {
                    break;
                }
                CurrentAddress = address;
                string fileOutputPath = Path.Combine(OutputFolder, "models", address.ToString("X8"));
                if (SingleOutputFolder)
                {
                    fileOutputPath = Path.Combine(OutputFolder, address.ToString("X8"));
                }
                try
                {
                    if (!CheckModel(address, 0, modelfmt))
                    {
                        //Console.WriteLine("Not found: {0}", address.ToString("X"));
                        continue;
                    }
                    NJS_OBJECT mdl = new NJS_OBJECT(datafile, (int)address, ImageBase, modelfmt, new Dictionary <int, Attach>());
                    if (!CompareModels(originalmodel, mdl))
                    {
                        continue;
                    }
                    NJS_OBJECT[] children1 = originalmodel.Children.ToArray();
                    NJS_OBJECT[] children2 = mdl.Children.ToArray();
                    if (children1.Length != children2.Length)
                    {
                        continue;
                    }
                    for (int k = 0; k < children1.Length; k++)
                    {
                        if (!CompareModels(children1[k], children2[k]))
                        {
                            continue;
                        }
                    }
                    ModelFile.CreateFile(fileOutputPath + model_extension, mdl, null, null, null, null, modelfmt, NoMeta);
                    Console.WriteLine("Model at {0} seems to match!", address.ToString("X"));
                    addresslist.Add(address, "NJS_OBJECT");
                    FoundBasicModels++;
                }
                catch (Exception)
                {
                    continue;
                }
            }
            return(result);
        }
Пример #20
0
        // Scan for models
        static void ScanModel(ModelFormat modelfmt)
        {
            CurrentStep++;
            CurrentScanData = "Models " + modelfmt.ToString();
            uint scan_end = (EndAddress == 0) ? EndAddress : (uint)datafile.Length - 52;             // 52 for NJS_OBJECT

            ByteConverter.BigEndian = BigEndian;
            Console.WriteLine("Step {0}: Scanning for {1} models", CurrentStep, modelfmt);
            string model_extension = ".sa1mdl";
            string model_dir       = "basicmodels";
            string model_type      = "NJS_OBJECT";
            int    count           = 0;

            switch (modelfmt)
            {
            case ModelFormat.Basic:
            default:
                model_extension = ".sa1mdl";
                model_dir       = "basicmodels";
                model_type      = "NJS_OBJECT_OLD";
                break;

            case ModelFormat.BasicDX:
                model_extension = ".sa1mdl";
                model_dir       = "basicmodels";
                model_type      = "NJS_OBJECT";
                break;

            case ModelFormat.Chunk:
                model_extension = ".sa2mdl";
                model_dir       = "chunkmodels";
                model_type      = "NJS_CNK_OBJECT";
                break;

            case ModelFormat.GC:
                model_extension = ".sa2bmdl";
                model_dir       = "gcmodels";
                model_type      = "NJS_GC_OBJECT";
                break;
            }
            if (!SingleOutputFolder)
            {
                Directory.CreateDirectory(Path.Combine(OutputFolder, model_dir));
            }
            for (uint address = StartAddress; address < scan_end; address += 1)
            {
                if (CancelScan)
                {
                    break;
                }
                if (ConsoleMode && address % 1000 == 0)
                {
                    Console.Write("\r{0} ", address.ToString("X8"));
                }
                CurrentAddress = address;
                string fileOutputPath = Path.Combine(OutputFolder, model_dir, address.ToString("X8"));
                if (SingleOutputFolder)
                {
                    fileOutputPath = Path.Combine(OutputFolder, address.ToString("X8"));
                }
                try
                {
                    if (!CheckModel(address, 0, modelfmt))
                    {
                        //Console.WriteLine("Not found: {0}", address.ToString("X"));
                        continue;
                    }
                    //else Console.WriteLine("found: {0}", address.ToString("X"));
                    NJS_OBJECT mdl = new NJS_OBJECT(datafile, (int)address, ImageBase, modelfmt, new Dictionary <int, Attach>());
                    // Additional checks to prevent false positives with empty nodes
                    if (CheckForModelData(mdl))
                    {
                        ModelFile.CreateFile(fileOutputPath + model_extension, mdl, null, null, null, null, modelfmt, NoMeta);
                        count++;
                        switch (modelfmt)
                        {
                        case ModelFormat.Basic:
                        case ModelFormat.BasicDX:
                            FoundBasicModels++;
                            break;

                        case ModelFormat.Chunk:
                            FoundChunkModels++;
                            break;

                        case ModelFormat.GC:
                            FoundGCModels++;
                            break;

                        default:
                            break;
                        }
                        addresslist.Add(address, model_type);
                        if (!KeepChildModels)
                        {
                            DeleteChildModels(mdl, model_dir, model_extension);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("\rError adding model at {0}: {1}", address.ToString("X"), ex.Message.ToString());
                    continue;
                }
            }
            Console.WriteLine("\r{0} models found", count);
        }
Пример #21
0
        public static int SplitFile(string datafilename, string inifilename, string projectFolderName, bool nometa = false, bool nolabel = false)
        {
#if !DEBUG
            try
#endif
            {
                byte[]  datafile;
                byte[]  datafile_temp           = File.ReadAllBytes(datafilename);
                IniData inifile                 = IniSerializer.Deserialize <IniData>(inifilename);
                string  listfile                = Path.Combine(Path.GetDirectoryName(inifilename), Path.GetFileNameWithoutExtension(datafilename) + "_labels.txt");
                Dictionary <int, string> labels = new Dictionary <int, string>();
                if (File.Exists(listfile) && !nolabel)
                {
                    labels = IniSerializer.Deserialize <Dictionary <int, string> >(listfile);
                }
                if (inifile.StartOffset != 0)
                {
                    byte[] datafile_new = new byte[inifile.StartOffset + datafile_temp.Length];
                    datafile_temp.CopyTo(datafile_new, inifile.StartOffset);
                    datafile = datafile_new;
                }
                else
                {
                    datafile = datafile_temp;
                }
                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)SplitERRORVALUE.InvalidDataMapping);
                    }
                }
                ByteConverter.BigEndian      = SonicRetro.SAModel.ByteConverter.BigEndian = inifile.BigEndian;
                ByteConverter.Reverse        = SonicRetro.SAModel.ByteConverter.Reverse = inifile.Reverse;
                Environment.CurrentDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetDirectoryName(datafilename));
                if (Path.GetExtension(datafilename).ToLowerInvariant() == ".prs" || (inifile.Compressed && Path.GetExtension(datafilename).ToLowerInvariant() != ".bin"))
                {
                    datafile = FraGag.Compression.Prs.Decompress(datafile);
                }
                uint imageBase = HelperFunctions.SetupEXE(ref datafile) ?? inifile.ImageBase.Value;
                if (Path.GetExtension(datafilename).Equals(".rel", StringComparison.OrdinalIgnoreCase))
                {
                    datafile = HelperFunctions.DecompressREL(datafile);
                    HelperFunctions.FixRELPointers(datafile, imageBase);
                }
                bool            SA2          = inifile.Game == Game.SA2 | inifile.Game == Game.SA2B;
                ModelFormat     modelfmt_def = 0;
                LandTableFormat landfmt_def  = 0;
                switch (inifile.Game)
                {
                case Game.SA1:
                    modelfmt_def = ModelFormat.Basic;
                    landfmt_def  = LandTableFormat.SA1;
                    break;

                case Game.SADX:
                    modelfmt_def = ModelFormat.BasicDX;
                    landfmt_def  = LandTableFormat.SADX;
                    break;

                case Game.SA2:
                    modelfmt_def = ModelFormat.Chunk;
                    landfmt_def  = LandTableFormat.SA2;
                    break;

                case Game.SA2B:
                    modelfmt_def = ModelFormat.GC;
                    landfmt_def  = LandTableFormat.SA2B;
                    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 new List <KeyValuePair <string, SA_Tools.FileInfo> >(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 = Path.Combine(projectFolderName, data.Filename);
                    Console.WriteLine(item.Key + ": " + data.Address.ToString("X") + " → " + fileOutputPath);
                    Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                    switch (type)
                    {
                    case "landtable":
                        LandTableFormat format = data.CustomProperties.ContainsKey("format") ? (LandTableFormat)Enum.Parse(typeof(LandTableFormat), data.CustomProperties["format"]) : landfmt_def;
                        new LandTable(datafile, address, imageBase, format, labels)
                        {
                            Description = item.Key
                        }.SaveToFile(fileOutputPath, landfmt_def, nometa);
                        break;

                    case "model":
                    case "basicmodel":
                    case "basicdxmodel":
                    case "chunkmodel":
                    case "gcmodel":
                    {
                        ModelFormat mdlformat;
                        switch (type)
                        {
                        case "basicmodel":
                            mdlformat = ModelFormat.Basic;
                            break;

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

                        case "chunkmodel":
                            mdlformat = ModelFormat.Chunk;
                            break;

                        case "gcmodel":
                            mdlformat = ModelFormat.GC;
                            break;

                        case "model":
                        default:
                            mdlformat = modelfmt_def;
                            break;
                        }
                        if (data.CustomProperties.ContainsKey("format"))
                        {
                            mdlformat = (ModelFormat)Enum.Parse(typeof(ModelFormat), data.CustomProperties["format"]);
                        }
                        NJS_OBJECT mdl     = new NJS_OBJECT(datafile, address, imageBase, mdlformat, labels, new Dictionary <int, Attach>());
                        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, null, item.Key, null, mdlformat, nometa);
                    }
                    break;

                    case "action":
                    {
                        ModelFormat modelfmt_act = data.CustomProperties.ContainsKey("format") ? (ModelFormat)Enum.Parse(typeof(ModelFormat), data.CustomProperties["format"]) : modelfmt_def;
                        NJS_ACTION  ani          = new NJS_ACTION(datafile, address, imageBase, modelfmt_act, labels, new Dictionary <int, Attach>());
                        if (!labels.ContainsValue(ani.Name) && !nolabel)
                        {
                            ani.Name = filedesc;
                        }
                        if (customProperties.ContainsKey("numparts"))
                        {
                            ani.Animation.ModelParts = int.Parse(customProperties["numparts"]);
                        }
                        if (ani.Animation.ModelParts == 0)
                        {
                            Console.WriteLine("Action {0} has no model data!", ani.Name);
                            continue;
                        }
                        ani.Animation.Save(fileOutputPath, nometa);
                    }
                    break;

                    case "animation":
                    case "motion":
                        int numparts = 0;
                        if (customProperties.ContainsKey("numparts"))
                        {
                            numparts = int.Parse(customProperties["numparts"], NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, NumberFormatInfo.InvariantInfo);
                        }
                        else
                        {
                            Console.WriteLine("Number of parts not specified for {0}", filedesc);
                        }
                        if (customProperties.ContainsKey("shortrot"))
                        {
                            NJS_MOTION mot = new NJS_MOTION(datafile, address, imageBase, numparts, labels, bool.Parse(customProperties["shortrot"]));
                            if (!labels.ContainsKey(address) && !nolabel)
                            {
                                mot.Name = filedesc;
                            }
                            mot.Save(fileOutputPath, nometa);
                        }
                        else
                        {
                            NJS_MOTION mot = new NJS_MOTION(datafile, address, imageBase, numparts, labels);
                            if (!labels.ContainsKey(address) && !nolabel)
                            {
                                mot.Name = filedesc;
                            }
                            mot.Save(fileOutputPath, nometa);
                        }
                        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 "texnamearray":
                        TexnameArray texnames = new TexnameArray(datafile, address, imageBase);
                        texnames.Save(fileOutputPath);
                        break;

                    case "texlistarray":
                    {
                        int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        for (int i = 0; i < cnt; i++)
                        {
                            uint ptr = BitConverter.ToUInt32(datafile, address);
                            if (data.Filename != null && ptr != 0)
                            {
                                ptr -= imageBase;
                                TexnameArray texarr = new TexnameArray(datafile, (int)ptr, imageBase);
                                string       fn     = Path.Combine(fileOutputPath, i.ToString("D3", NumberFormatInfo.InvariantInfo) + ".txt");
                                if (data.CustomProperties.ContainsKey("filename" + i.ToString()))
                                {
                                    fn = Path.Combine(fileOutputPath, data.CustomProperties["filename" + i.ToString()] + ".txt");
                                }
                                if (!Directory.Exists(Path.GetDirectoryName(fn)))
                                {
                                    Directory.CreateDirectory(Path.GetDirectoryName(fn));
                                }
                                texarr.Save(fn);
                            }
                            address += 4;
                        }
                        nohash = true;
                    }
                    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)
                        {
                            string file_tosave;
                            if (customProperties.ContainsKey("filename" + num.ToString()))
                            {
                                file_tosave = customProperties["filename" + num++.ToString()];
                            }
                            else
                            {
                                file_tosave = num++.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl";
                            }
                            string file = Path.Combine(path, file_tosave);
                            flags.Add(new DeathZoneFlags(datafile, address, file_tosave));
                            ModelFormat modelfmt_death = inifile.Game == Game.SADX ? ModelFormat.BasicDX : ModelFormat.Basic;                                             // Death zones in all games except SADXPC use Basic non-DX models
                            ModelFile.CreateFile(file, new NJS_OBJECT(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, modelfmt_death, new Dictionary <int, Attach>()), null, null, null, null, modelfmt_death, nometa);
                            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 "enemyanimationlist":
                    {
                        int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        SA2EnemyAnimInfoList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath);
                    }
                    break;

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

                    case "motiontable":
                    {
                        Directory.CreateDirectory(fileOutputPath);
                        List <MotionTableEntry> result = new List <MotionTableEntry>();
                        List <string>           hashes = new List <string>();
                        bool shortrot = false;
                        if (customProperties.ContainsKey("shortrot"))
                        {
                            shortrot = bool.Parse(customProperties["shortrot"]);
                        }
                        int nodeCount = int.Parse(data.CustomProperties["nodecount"], NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, NumberFormatInfo.InvariantInfo);
                        int Length    = int.Parse(data.CustomProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        Dictionary <int, string> mtns = new Dictionary <int, string>();
                        for (int i = 0; i < Length; i++)
                        {
                            MotionTableEntry bmte = new MotionTableEntry();
                            int mtnaddr           = (int)(ByteConverter.ToUInt32(datafile, address) - imageBase);
                            if (!mtns.ContainsKey(mtnaddr))
                            {
                                NJS_MOTION motion = new NJS_MOTION(datafile, mtnaddr, imageBase, nodeCount, null, shortrot);
                                bmte.Motion = motion.Name;
                                mtns.Add(mtnaddr, motion.Name);
                                motion.Save(Path.Combine(fileOutputPath, $"{i}.saanim"), nometa);
                                hashes.Add($"{i}.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{i}.saanim")));
                            }
                            else
                            {
                                bmte.Motion = mtns[mtnaddr];
                            }
                            bmte.LoopProperty    = ByteConverter.ToUInt16(datafile, address + 4);
                            bmte.Pose            = ByteConverter.ToUInt16(datafile, address + 6);
                            bmte.NextAnimation   = ByteConverter.ToInt32(datafile, address + 8);
                            bmte.TransitionSpeed = ByteConverter.ToUInt32(datafile, address + 0xC);
                            bmte.StartFrame      = ByteConverter.ToSingle(datafile, address + 0x10);
                            bmte.EndFrame        = ByteConverter.ToSingle(datafile, address + 0x14);
                            bmte.PlaySpeed       = ByteConverter.ToSingle(datafile, address + 0x18);
                            result.Add(bmte);
                            address += 0x1C;
                        }
                        IniSerializer.Serialize(result, Path.Combine(fileOutputPath, "info.ini"));
                        hashes.Add("info.ini:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, "info.ini")));
                        data.MD5Hash = string.Join("|", hashes.ToArray());
                        nohash       = true;
                    }
                    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", nometa);
                            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;

                    case "masterstringlist":
                    {
                        int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        for (int l = 0; l < 5; l++)
                        {
                            Languages            lng = (Languages)l;
                            System.Text.Encoding enc = HelperFunctions.GetEncoding(inifile.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"));
                                    inifile.Files.Add($"{filedesc} {lng} {i}", new FileInfo()
                                        {
                                            Type = "string", Filename = fn, PointerList = new int[] { ptr }, MD5Hash = HelperFunctions.FileHash(fn), CustomProperties = new Dictionary <string, string>()
                                            {
                                                { "language", lng.ToString() }
                                            }
                                        });
                                }
                                ptr += 4;
                            }
                            address += 4;
                        }
                        inifile.Files.Remove(filedesc);
                        nohash = true;
                    }
                    break;

                    case "camera":
                        NinjaCamera cam = new NinjaCamera(datafile, address);
                        cam.Save(fileOutputPath);
                        break;

                    case "fogdatatable":
                        int fcnt = 3;
                        if (customProperties.ContainsKey("count"))
                        {
                            fcnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        }
                        FogDataTable fga = new FogDataTable(datafile, address, imageBase, fcnt);
                        fga.Save(fileOutputPath);
                        break;

                    case "palettelightlist":
                        int count = 255;
                        if (customProperties.ContainsKey("count"))
                        {
                            count = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                        }
                        PaletteLightList pllist = new PaletteLightList(datafile, address, count);
                        pllist.Save(fileOutputPath);
                        break;

                    case "physicsdata":
                        PlayerParameter plpm = new PlayerParameter(datafile, address);
                        plpm.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 = Path.Combine(projectFolderName, inifile.MasterObjectList);

                    IniSerializer.Serialize(masterobjlist, masterObjectListOutputPath);
                }
                IniSerializer.Serialize(inifile, Path.Combine(projectFolderName, Path.GetFileNameWithoutExtension(inifilename) + "_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)SplitERRORVALUE.UnhandledException);
            }
#endif
            return((int)SplitERRORVALUE.Success);
        }
Пример #22
0
            /// <summary>
            /// Export an a folder with metadata + files for SADX DLC mod
            /// </summary>
            /// <param name="dir">Export path. Must be a folder.</param>
            /// <param name="writeall">Also write raw binary sections as separate files.</param>
            public void Export(string dir, bool writeall = false)
            {
                byte[] file = GetBytes();
                Directory.CreateDirectory(dir);
                string fname = Path.GetFileName(dir);

                IniSerializer.Serialize(new VMS_DLC(file), Path.Combine(dir, fname + ".ini"));
                Bitmap bmp = GetIconFromFile(file);

                bmp.Save(Path.Combine(dir, fname + ".bmp"), ImageFormat.Bmp);
                uint pvm_pointer = BitConverter.ToUInt32(file, 0x290);
                uint mlt_pointer = BitConverter.ToUInt32(file, 0x29C);
                uint prs_pointer = BitConverter.ToUInt32(file, 0x2A4);
                // Save sections
                int pvm_size = (int)mlt_pointer - (int)pvm_pointer;

                if (pvm_size > 0)
                {
                    byte[] pvmdata = new byte[pvm_size];
                    Array.Copy(file, pvm_pointer, pvmdata, 0, pvm_size);
                    File.WriteAllBytes(Path.Combine(dir, fname + ".pvm"), pvmdata);
                }
                int mlt_size = (int)prs_pointer - (int)mlt_pointer;

                if (mlt_size > 0)
                {
                    byte[] mltdata = new byte[mlt_size];
                    Array.Copy(file, mlt_pointer, mltdata, 0, mlt_size);
                    File.WriteAllBytes(Path.Combine(dir, fname + ".mlt"), mltdata);
                }
                uint sectionsize = BitConverter.ToUInt32(file, 0x48);
                int  text_count  = BitConverter.ToInt32(file, 0x28C);
                int  item_count  = BitConverter.ToInt32(file, 0x284);
                int  item_size   = (item_count * 30 + 12); //12-byte header

                do
                {
                    item_size++;
                }while (item_size % 16 != 0);
                int prs_size = file.Length - (int)prs_pointer;

                if (prs_size > 0)
                {
                    byte[] prsdata = new byte[prs_size];
                    Array.Copy(file, prs_pointer, prsdata, 0, prs_size);
                    if (writeall)
                    {
                        File.WriteAllBytes(Path.Combine(dir, fname + ".prs"), prsdata);
                    }
                    prsdata = FraGag.Compression.Prs.Decompress(prsdata);
                    if (writeall)
                    {
                        File.WriteAllBytes(Path.Combine(dir, fname + ".bin"), prsdata);
                    }
                    // Model pointer
                    uint modelpointer = BitConverter.ToUInt32(prsdata, 0) - 0xCCA4000;
                    //Console.WriteLine("Model pointer: {0}", modelpointer.ToString("X"));
                    NJS_OBJECT mdl = new NJS_OBJECT(prsdata, (int)modelpointer, 0xCCA4000, ModelFormat.Basic, null);
                    ModelFile.CreateFile((Path.Combine(dir, fname + ".sa1mdl")), mdl, null, null, null, null, ModelFormat.Basic);
                }
                //Console.WriteLine("Output folder: {0}", dir);
            }
Пример #23
0
        static void Main(string[] args)
        {
            string datafilename, inifilename;

            if (args.Length > 0)
            {
                datafilename = args[0];
                Console.WriteLine("File: {0}", datafilename);
            }
            else
            {
                Console.Write("File: ");
                datafilename = Console.ReadLine();
            }
            if (args.Length > 1)
            {
                inifilename = args[1];
                Console.WriteLine("INI File: {0}", inifilename);
            }
            else
            {
                Console.Write("INI File: ");
                inifilename = Console.ReadLine();
            }
            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 = HelperFunctions.GetCString(datafile, 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];
                if (data.Filename != null)
                {
                    Console.WriteLine(name + " -> " + data.Filename);
                    Directory.CreateDirectory(Path.GetDirectoryName(data.Filename));
                }
                else
                {
                    Console.WriteLine(name);
                }
                switch (type)
                {
                case "landtable":
                {
                    LandTable land = new LandTable(datafile, address, imageBase, landfmt)
                    {
                        Description = name, Tool = "splitDLL"
                    };
                    DllItemInfo info = new DllItemInfo()
                    {
                        Export = name,
                        Label  = land.Name
                    };
                    output.Items.Add(info);
                    if (!labels.Contains(land.Name))
                    {
                        land.SaveToFile(data.Filename, landfmt);
                        output.Files[data.Filename] = new FileTypeHash("landtable", HelperFunctions.FileHash(data.Filename));
                        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, Tool = "splitDLL"
                            };
                            DllItemInfo info = new DllItemInfo()
                            {
                                Export = name,
                                Index  = i,
                                Label  = land.Name
                            };
                            output.Items.Add(info);
                            if (!labels.Contains(land.Name))
                            {
                                string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + landext);
                                land.SaveToFile(fn, landfmt);
                                output.Files[fn] = new FileTypeHash("landtable", HelperFunctions.FileHash(fn));
                                labels.AddRange(land.GetLabels());
                            }
                        }
                        address += 4;
                    }
                    break;

                case "model":
                {
                    NJS_OBJECT  mdl  = new NJS_OBJECT(datafile, address, imageBase, modelfmt);
                    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);
                            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);
                    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);
                            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);
                    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);
                            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);
                    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);
                            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);
                            AnimationHeader ani = new AnimationHeader(datafile, ptr, imageBase, modelfmt);
                            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 fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                            ani.Animation.Save(fn);
                            output.Files[fn] = new FileTypeHash("animation", HelperFunctions.FileHash(fn));
                            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(mdl.Filename), 0, Path.GetFullPath(fn), 0);
                                mdl.Animations.Add(sb.ToString());
                            }
                            else
                            {
                                string mfn = Path.ChangeExtension(fn, modelext);
                                ModelFile.CreateFile(mfn, ani.Model, new[] { Path.GetFileName(fn) }, null, null,
                                                     idx + "->object", "splitDLL", null, modelfmt);
                                output.Files[mfn] = new FileTypeHash("model", HelperFunctions.FileHash(mfn));
                            }
                        }
                        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;
                }
                itemcount++;
            }
            foreach (ModelAnimations item in models)
            {
                ModelFile.CreateFile(item.Filename, item.Model, item.Animations.ToArray(), null, null, item.Name, "splitDLL",
                                     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(item.Filename));
            }
            IniSerializer.Serialize(output, Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(datafilename))
                                    + "_data.ini");
            timer.Stop();
            Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds.");
            Console.WriteLine();
        }
Пример #24
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);
        }
Пример #25
0
        public static void Split(string filename)
        {
            nodenames.Clear();
            modelfiles.Clear();
            motionfiles.Clear();

            Console.WriteLine("Splitting file {0}...", filename);
            byte[] fc;
            if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
            {
                fc = Prs.Decompress(filename);
            }
            else
            {
                fc = File.ReadAllBytes(filename);
            }
            EventIniData ini = new EventIniData()
            {
                Name = Path.GetFileNameWithoutExtension(filename)
            };
            string            path = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(Path.GetFullPath(filename)), Path.GetFileNameWithoutExtension(filename))).FullName;
            uint              key;
            List <NJS_MOTION> motions = null;
            bool              battle;
            bool              dcbeta;

            if (fc[0] == 0x81)
            {
                if (fc[0x2B] <= 0x01 && fc[0x2A] == 0)
                {
                    Console.WriteLine("File is in GC/PC format.");
                    ByteConverter.BigEndian = true;
                    key         = 0x8125FE60;
                    ini.Game    = Game.SA2B;
                    battle      = true;
                    dcbeta      = false;
                    motions     = ReadMotionFile(Path.ChangeExtension(filename, null) + "motion.bin");
                    ini.Motions = motions.Select(a => a?.Name).ToList();
                    foreach (var mtn in motions.Where(a => a != null))
                    {
                        motionfiles[mtn.Name] = new MotionInfo(null, mtn);
                    }
                }
                else
                {
                    Console.WriteLine("File is in GC/PC Beta format.");
                    ByteConverter.BigEndian = true;
                    key      = 0x812FFE60;
                    ini.Game = Game.SA2B;
                    battle   = false;
                    dcbeta   = false;
                }
            }
            else
            {
                if ((fc[37] == 0x25) || (fc[38] == 0x22) || ((fc[36] == 0) && ((fc[1] == 0xFE) || (fc[1] == 0xF2) || ((fc[1] == 0x27) && fc[2] == 0x9F))))
                {
                    Console.WriteLine("File is in DC Beta format.");
                    ByteConverter.BigEndian = false;
                    key      = 0xC600000;
                    ini.Game = Game.SA2;
                    battle   = false;
                    dcbeta   = true;
                }
                else
                {
                    Console.WriteLine("File is in DC format.");
                    ByteConverter.BigEndian = false;
                    key      = 0xC600000;
                    ini.Game = Game.SA2;
                    battle   = false;
                    dcbeta   = false;
                }
            }
            int ptr = fc.GetPointer(0x20, key);

            if (ptr != 0)
            {
                if (!dcbeta)
                {
                    for (int i = 0; i < (battle ? 18 : 16); i++)
                    {
                        string upnam = upgradenames[i];
                        string chnam = upnam;
                        switch (i)
                        {
                        case 0:
                            chnam = "Sonic";
                            break;

                        case 4:
                            chnam = "Shadow";
                            break;

                        case 6:
                            chnam = "Knuckles";
                            break;

                        case 12:
                            chnam = "Rouge";
                            break;

                        case 16:
                            chnam = "Mech Tails";
                            break;

                        case 17:
                            chnam = "Mech Eggman";
                            break;
                        }
                        UpgradeInfo info = new UpgradeInfo();
                        info.RootNode = GetModel(fc, ptr, key, $"{chnam} Root.sa2mdl");
                        if (info.RootNode != null)
                        {
                            int ptr2 = fc.GetPointer(ptr + 4, key);
                            if (ptr2 != 0)
                            {
                                info.AttachNode1 = $"object_{ptr2:X8}";
                            }
                            int ptr3 = fc.GetPointer(ptr + 8, key);
                            if (ptr3 != 0)
                            {
                                info.Model1 = GetModel(fc, ptr + 8, key, $"{upnam} Model 1.sa2mdl");
                            }
                            ptr2 = fc.GetPointer(ptr + 0xC, key);
                            if (ptr2 != 0)
                            {
                                info.AttachNode2 = $"object_{ptr2:X8}";
                            }
                            ptr3 = fc.GetPointer(ptr + 0x10, key);
                            if (ptr3 != 0)
                            {
                                info.Model2 = GetModel(fc, ptr + 0x10, key, $"{upnam} Model 2.sa2mdl");
                            }
                        }
                        ini.Upgrades.Add(info);
                        ptr += 0x14;
                    }
                }
                else
                {
                    for (int i = 0; i < 14; i++)
                    {
                        string upnam = upgradebetanames[i];
                        string chnam = upnam;
                        switch (i)
                        {
                        case 0:
                            chnam = "Sonic";
                            break;

                        case 4:
                            chnam = "Shadow";
                            break;

                        case 6:
                            chnam = "Knuckles";
                            break;

                        case 10:
                            chnam = "Rouge";
                            break;
                        }
                        UpgradeInfo info = new UpgradeInfo();
                        info.RootNode = GetModel(fc, ptr, key, $"{chnam} Root.sa2mdl");
                        if (info.RootNode != null)
                        {
                            int ptr2 = fc.GetPointer(ptr + 4, key);
                            if (ptr2 != 0)
                            {
                                info.AttachNode1 = $"object_{ptr2:X8}";
                            }
                            int ptr3 = fc.GetPointer(ptr + 8, key);
                            if (ptr3 != 0)
                            {
                                info.Model1 = GetModel(fc, ptr + 8, key, $"{upnam} Model 1.sa2mdl");
                            }
                            ptr2 = fc.GetPointer(ptr + 0xC, key);
                            if (ptr2 != 0)
                            {
                                info.AttachNode2 = $"object_{ptr2:X8}";
                            }
                            ptr3 = fc.GetPointer(ptr + 0x10, key);
                            if (ptr3 != 0)
                            {
                                info.Model2 = GetModel(fc, ptr + 0x10, key, $"{upnam} Model 2.sa2mdl");
                            }
                        }
                        ini.Upgrades.Add(info);
                        ptr += 0x14;
                    }
                }
            }
            else
            {
                Console.WriteLine("Event contains no character upgrades.");
            }
            ptr = fc.GetPointer(0x18, key);
            if (ptr != 0)
            {
                for (int i = 0; i < 93; i++)
                {
                    string name = $"object_{ptr:X8}";
                    if (name != null)
                    {
                        ini.MechParts.Add(i, name);
                    }
                    ptr += 4;
                }
            }
            else
            {
                Console.WriteLine("Event contains no mech parts.");
            }
            int gcnt = ByteConverter.ToInt32(fc, 8);

            ptr = fc.GetPointer(0, key);
            if (ptr != 0)
            {
                Console.WriteLine("Event contains {0} scene(s).", gcnt + 1);
                for (int gn = 0; gn <= gcnt; gn++)
                {
                    Directory.CreateDirectory(Path.Combine(path, $"Scene {gn + 1}"));
                    SceneInfo scn  = new SceneInfo();
                    int       ptr2 = fc.GetPointer(ptr, key);
                    int       ecnt = ByteConverter.ToInt32(fc, ptr + 4);
                    if (ptr2 != 0)
                    {
                        Console.WriteLine("Scene {0} contains {1} entit{2}.", gn + 1, ecnt, ecnt == 1 ? "y" : "ies");
                        for (int en = 0; en < ecnt; en++)
                        {
                            EntityInfo ent = new EntityInfo();
                            ent.Model = GetModel(fc, ptr2, key, $"Scene {gn + 1}\\Entity {en + 1} Model.sa2mdl");
                            if (ent.Model != null)
                            {
                                ent.Motion = GetMotion(fc, ptr2 + 4, key, $"Scene {gn + 1}\\Entity {en + 1} Motion.saanim", motions, modelfiles[ent.Model].Model.CountAnimated());
                                if (ent.Motion != null)
                                {
                                    modelfiles[ent.Model].Motions.Add(motionfiles[ent.Motion].Filename);
                                }
                                ent.ShapeMotion = GetMotion(fc, ptr2 + 8, key, $"Scene {gn + 1}\\Entity {en + 1} Shape Motion.saanim", motions, modelfiles[ent.Model].Model.CountMorph());
                                if (ent.ShapeMotion != null)
                                {
                                    modelfiles[ent.Model].Motions.Add(motionfiles[ent.ShapeMotion].Filename);
                                }
                            }
                            if (battle)
                            {
                                ent.GCModel     = GetGCModel(fc, ptr2 + 12, key, $"Scene {gn + 1}\\Entity {en + 1} GC Model.sa2bmdl");
                                ent.ShadowModel = GetModel(fc, ptr2 + 16, key, $"Scene {gn + 1}\\Entity {en + 1} Shadow Model.sa2mdl");
                                ent.Position    = new Vertex(fc, ptr2 + 24);
                                ent.Flags       = ByteConverter.ToUInt32(fc, ptr2 + 36);
                                ent.Layer       = ByteConverter.ToUInt32(fc, ptr2 + 40);
                            }
                            else
                            {
                                ent.Position = new Vertex(fc, ptr2 + 16);
                                ent.Flags    = ByteConverter.ToUInt32(fc, ptr2 + 28);
                            }
                            scn.Entities.Add(ent);
                            ptr2 += battle ? 0x2C : 0x20;
                        }
                    }
                    else
                    {
                        Console.WriteLine("Scene {0} contains no entities.", gn + 1);
                    }
                    ptr2 = fc.GetPointer(ptr + 8, key);
                    if (ptr2 != 0)
                    {
                        int cnt = ByteConverter.ToInt32(fc, ptr + 12);
                        for (int i = 0; i < cnt; i++)
                        {
                            scn.CameraMotions.Add(GetMotion(fc, ptr2, key, $"Scene {gn + 1}\\Camera Motion {i + 1}.saanim", motions, 1));
                            ptr2 += sizeof(int);
                        }
                    }
                    ptr2 = fc.GetPointer(ptr + 0x18, key);
                    if (ptr2 != 0)
                    {
                        BigInfo big = new BigInfo();
                        big.Model = GetModel(fc, ptr2, key, $"Scene {gn + 1}\\Big Model.sa2mdl");
                        if (big.Model != null)
                        {
                            int anicnt = modelfiles[big.Model].Model.CountAnimated();
                            int ptr3   = fc.GetPointer(ptr2 + 4, key);
                            if (ptr3 != 0)
                            {
                                int cnt = ByteConverter.ToInt32(fc, ptr2 + 8);
                                for (int i = 0; i < cnt; i++)
                                {
                                    big.Motions.Add(new string[] { GetMotion(fc, ptr3, key, $"Scene {gn + 1}\\Big Motion {i + 1}a.saanim", motions, anicnt), GetMotion(fc, ptr3 + 4, key, $"Scene {gn + 1}\\Big Motion {i + 1}b.saanim", motions, anicnt) });
                                    ptr3 += 8;
                                }
                            }
                        }
                        big.Unknown = ByteConverter.ToInt32(fc, ptr2 + 12);
                        scn.Big     = big;
                    }
                    scn.FrameCount = ByteConverter.ToInt32(fc, ptr + 28);
                    ini.Scenes.Add(scn);
                    ptr += 0x20;
                }
            }
            else
            {
                Console.WriteLine("Event contains no scenes.");
            }
            ptr = fc.GetPointer(0x1C, key);
            if (ptr != 0)
            {
                ini.TailsTails = GetModel(fc, ptr, key, $"Tails' tails.sa2mdl");
            }
            else
            {
                Console.WriteLine("Event does not contain Tails' tails.");
            }
            ptr = fc.GetPointer(4, key);
            if (ptr == 0)
            {
                Console.WriteLine("Event does not contain an internal texture list.");
            }
            ptr = fc.GetPointer(0xC, key);
            if (ptr == 0)
            {
                Console.WriteLine("Event does not contain texture sizes.");
            }
            ptr = fc.GetPointer(0x24, key);
            if (ptr == 0)
            {
                Console.WriteLine("Event does not contain texture animation data.");
            }
            if (battle && fc[0x2B] == 0)
            {
                Console.WriteLine("Event does not use GC shadow maps.");
            }
            if (battle && fc[0x2B] == 1)
            {
                Console.WriteLine("Event uses GC shadow maps.");
            }
            foreach (var item in motionfiles.Values)
            {
                string fn = item.Filename ?? $"Unknown Motion {motions.IndexOf(item.Motion)}.saanim";
                string fp = Path.Combine(path, fn);
                item.Motion.Save(fp);
                ini.Files.Add(fn, HelperFunctions.FileHash(fp));
            }
            foreach (var item in modelfiles.Values)
            {
                string fp = Path.Combine(path, item.Filename);
                ModelFile.CreateFile(fp, item.Model, item.Motions.ToArray(), null, null, null, item.Format);
                ini.Files.Add(item.Filename, HelperFunctions.FileHash(fp));
            }
            JsonSerializer js = new JsonSerializer
            {
                Formatting        = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore
            };

            using (var tw = File.CreateText(Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".json"))))
                js.Serialize(tw, ini);
        }
Пример #26
0
        static void Main(string[] args)
        {
            bool          nometa  = false;
            bool          nolabel = false;
            string        mode;
            string        fullpath_out;
            bool          bigendian = false;
            List <string> mdlanimfiles;

            if (args.Length == 0)
            {
                Console.WriteLine("Split any binary files supported by SA Tools.\n");
                Console.WriteLine("-Splitting using an XML template-");
                Console.WriteLine("split template <xmlfile> [-data sourcepath] [output path]\n");
                Console.WriteLine("-Splitting a binary file with INI data-");
                Console.WriteLine("split binary <file> <inifile> [output path]\n");
                Console.WriteLine("-Splitting a single item from a binary file without INI data-");
                Console.WriteLine("split single <game> <file> <key> <address> <type> [output filename] [-p custom properties] [-name entryName]\n");
                Console.WriteLine("-Splitting an NB file-");
                Console.WriteLine("split nb <file> [output path] [-ini split ini file]\n");
                Console.WriteLine("-Splitting SA2 MDL files-");
                Console.WriteLine("split mdl <file> [output path] [-anim animation files]\n");
                Console.WriteLine("-Splitting SA2B MDL files-");
                Console.WriteLine("split mdl_b <file> [output path] [-anim animation files]\n");
                Console.WriteLine("-Splitting dllexport entries from DLL files-");
                Console.WriteLine("split dllexport <file> <type> <name> [-id array ID] [output path] [-p numparts]\n");
                Console.WriteLine("Common switches: [-nometa], [-nolabel]");
                Console.WriteLine("Press ENTER to exit.");
                Console.ReadLine();
                return;
            }
            for (int u = 2; u < args.Length; u++)
            {
                if (args[u] == "-nometa")
                {
                    nometa = true;
                }
                if (args[u] == "-nolabel")
                {
                    nolabel = true;
                }
            }
            mode = args[0];
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
            switch (mode.ToLowerInvariant())
            {
            case "binary":
                string fullpath_bin = Path.GetFullPath(args[1]);
                if (!File.Exists(fullpath_bin))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_bin);
                    return;
                }
                Console.WriteLine("File: {0}", fullpath_bin);
                string fullpath_ini = Path.GetFullPath(args[2]);
                if (!File.Exists(fullpath_ini))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_ini);
                    return;
                }
                Console.WriteLine("Data mapping: {0}", fullpath_ini);
                fullpath_out = Path.GetDirectoryName(fullpath_bin);
                if (args.Length > 3)
                {
                    fullpath_out = args[3];
                    if (fullpath_out[fullpath_out.Length - 1] != '/')
                    {
                        fullpath_out = string.Concat(fullpath_out, '/');
                    }
                    fullpath_out = Path.GetFullPath(fullpath_out);
                }
                Console.WriteLine("Output folder: {0}", fullpath_out);
                if (nometa)
                {
                    Console.WriteLine("Labels are disabled");
                }
                if (Path.GetExtension(args[1]).ToLowerInvariant() == ".dll")
                {
                    SplitTools.SplitDLL.SplitDLL.SplitDLLFile(fullpath_bin, fullpath_ini, fullpath_out, nometa, nolabel);
                }
                else
                {
                    SplitTools.Split.SplitBinary.SplitFile(fullpath_bin, fullpath_ini, fullpath_out, nometa, nolabel);
                }
                break;

            case "template":
                string dataFolder = "";
                fullpath_out = "";
                if (args.Length < 2)
                {
                    Console.WriteLine("Insufficient arguments");
                    return;
                }
                if (!File.Exists(Path.GetFullPath(args[1])))
                {
                    Console.WriteLine("File {0} doesn't exist", Path.GetFullPath(args[1]));
                    return;
                }
                for (int i = 2; i < args.Length; i++)
                {
                    if (args[i] == "-nolabel")
                    {
                        nolabel = true;
                    }
                    else if (args[i] == "-nometa")
                    {
                        nometa = true;
                    }
                    else if (args[i] == "-data")
                    {
                        dataFolder = args[i + 1];
                        i++;
                    }
                    else
                    {
                        fullpath_out = args[i];
                    }
                }
                Templates.SplitTemplate template = ProjectFunctions.openTemplateFile(Path.GetFullPath(args[1]));
                if (template == null)
                {
                    Console.WriteLine("Failed to open template: {0}", Path.GetFullPath(args[1]));
                    return;
                }
                if (dataFolder == "")
                {
                    dataFolder = ProjectFunctions.GetGamePath(template.GameInfo.GameName);
                }
                Console.WriteLine("Data folder: {0}", dataFolder);
                string iniFolder = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName), "..\\GameConfig", template.GameInfo.DataFolder));
                Console.WriteLine("Splitting using template for {0} located at {1}", template.GameInfo.GameName, Path.GetFullPath(args[1]));
                if (!Directory.Exists(dataFolder))
                {
                    Console.WriteLine("\nData folder does not exist: {0}", Path.GetFullPath(dataFolder));
                    Console.WriteLine("Put your game files in {0} and run split again.", Path.GetFullPath(dataFolder));
                    Console.WriteLine("Press ENTER to exit.");
                    Console.ReadLine();
                    return;
                }
                Console.WriteLine("INI folder: {0}", iniFolder);
                if (fullpath_out == "")
                {
                    fullpath_out = Path.Combine(Environment.CurrentDirectory, template.GameInfo.GameName);
                }
                Console.WriteLine("Output folder: {0}", fullpath_out);
                foreach (Templates.SplitEntry splitEntry in template.SplitEntries)
                {
                    if (!File.Exists(Path.Combine(dataFolder, splitEntry.SourceFile)))
                    {
                        Console.WriteLine("Split source file {0} doesn't exist", Path.Combine(dataFolder, splitEntry.SourceFile));
                        continue;
                    }
                    Console.WriteLine("\n{0}: {1}: {2}", splitEntry.CmnName == null ? "No description" : splitEntry.CmnName, splitEntry.SourceFile, splitEntry.IniFile + ".ini");
                    ProjectFunctions.SplitTemplateEntry(splitEntry, null, dataFolder, iniFolder, fullpath_out, true, nometa, nolabel);
                }
                if (template.SplitMDLEntries != null)
                {
                    foreach (Templates.SplitEntryMDL splitEntryMDL in template.SplitMDLEntries)
                    {
                        if (!File.Exists(Path.Combine(dataFolder, splitEntryMDL.ModelFile)))
                        {
                            Console.WriteLine("Split MDL source file {0} doesn't exist", Path.Combine(dataFolder, splitEntryMDL.ModelFile));
                            continue;
                        }
                        Console.Write("\nSplitting MDL file: {0}", splitEntryMDL.ModelFile);
                        ProjectFunctions.SplitTemplateMDLEntry(splitEntryMDL, null, dataFolder, fullpath_out);
                    }
                }
                break;

            case "single":
                int    startoffset = 0;
                string game        = args[1];
                string filepath    = args[2];
                string outPath     = "";
                uint   key         = uint.Parse(args[3], System.Globalization.NumberStyles.HexNumber);
                int    eaddress    = int.Parse(args[4], System.Globalization.NumberStyles.HexNumber);
                string entryName   = "";
                string props       = "";
                string etype       = args[5];
                if (args.Length > 6)
                {
                    for (int a = 6; a < args.Length; a++)
                    {
                        switch (args[a])
                        {
                        case "-name":
                            entryName = args[a + 1];
                            a++;
                            break;

                        case "-offset":
                            startoffset = int.Parse(args[a + 1], System.Globalization.NumberStyles.HexNumber);
                            a++;
                            break;

                        case "-p":
                            props = args[a + 1];
                            a++;
                            break;

                        default:
                            outPath = args[a];
                            break;
                        }
                    }
                }
                // If no output filename is specified
                if (outPath == "")
                {
                    outPath = Path.Combine(Environment.CurrentDirectory, eaddress.ToString("X8"));
                }
                // If an output name is specified without a path
                else if (Path.GetDirectoryName(outPath) == "")
                {
                    outPath = Path.Combine(Environment.CurrentDirectory, outPath);
                }
                // If a path is specified without a filename
                else if (Path.GetFileName(outPath) == "")
                {
                    outPath = Path.Combine(outPath, eaddress.ToString("X8"));
                }
                Console.WriteLine("Splitting from {0} (key: {1}) in {2}: {3} at {4}, offset: {5}", Path.GetFileName(filepath), key.ToString("X"), game.ToUpperInvariant(), etype, eaddress.ToString("X"), startoffset.ToString("X"));
                Console.WriteLine("Output path: {0}", Path.GetFullPath(outPath));
                SplitTools.Split.SplitBinary.SplitManual(game, filepath, key, eaddress, etype, outPath, props, entryName, nometa, nolabel, startoffset);
                break;

            case "nb":
            case "nb_b":
                string fullpath_nb = Path.GetFullPath(args[1]);
                string path_ini    = null;
                if (args[args.Length - 2].ToLowerInvariant() == "-ini")
                {
                    path_ini = Path.GetFullPath(args[args.Length - 1]);
                }
                if (!File.Exists(fullpath_nb))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_nb);
                    return;
                }
                Console.WriteLine("File: {0}", fullpath_nb);
                fullpath_out = Path.GetDirectoryName(fullpath_nb);
                if (args.Length > 2)
                {
                    fullpath_out = args[2];
                    if (fullpath_out[fullpath_out.Length - 1] != '/')
                    {
                        fullpath_out = string.Concat(fullpath_out, '/');
                    }
                    fullpath_out = Path.GetFullPath(fullpath_out);
                }
                Console.WriteLine("Output folder: {0}", fullpath_out);
                SplitTools.Split.SplitNB.SplitNBFile(fullpath_nb, false, fullpath_out, 1, path_ini);
                break;

            case "mdl":
            case "mdl_b":
                string fullpath_mdl = Path.GetFullPath(args[1]);
                if (!File.Exists(fullpath_mdl))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_mdl);
                    return;
                }
                Console.Write("File: {0}", fullpath_mdl);
                if (mode == "mdl_b")
                {
                    bigendian = true;
                    Console.Write(" (Big Endian)\n");
                }
                else
                {
                    Console.Write(System.Environment.NewLine);
                }
                fullpath_out = Path.GetDirectoryName(fullpath_mdl);
                if (args.Length > 1)
                {
                    fullpath_out = args[2];
                    if (fullpath_out[fullpath_out.Length - 1] != '/')
                    {
                        fullpath_out = string.Concat(fullpath_out, '/');
                    }
                    fullpath_out = Path.GetFullPath(fullpath_out);
                }
                Console.WriteLine("Output path: {0}", fullpath_out);
                if (args.Length > 2)
                {
                    mdlanimfiles = new List <string>();
                    Console.WriteLine("Animation files:");
                    for (int u = 3; u < args.Length; u++)
                    {
                        string animpath = Path.GetFullPath(args[u]);
                        if (File.Exists(animpath))
                        {
                            mdlanimfiles.Add(animpath);
                            Console.WriteLine(animpath);
                        }
                        else
                        {
                            Console.WriteLine("File {0} doesn't exist.", animpath);
                        }
                    }
                    SplitTools.SAArc.sa2MDL.Split(bigendian, fullpath_mdl, fullpath_out, mdlanimfiles.ToArray());
                }
                else
                {
                    SplitTools.SAArc.sa2MDL.Split(bigendian, fullpath_mdl, fullpath_out, null);
                }
                break;

            case "dllexport":
                int    arrayid        = -1;
                string fullpath_dllex = Path.GetFullPath(args[1]);
                string type           = args[2];
                string name           = args[3];
                string fileOutputPath = "";
                if (args.Length > 4)
                {
                    for (int u = 4; u < args.Length; u++)
                    {
                        if (args[u] == "-id")
                        {
                            arrayid = int.Parse(args[u + 1]);
                            u++;
                        }
                        else
                        {
                            fileOutputPath = args[u];
                        }
                    }
                }
                if (!File.Exists(fullpath_dllex))
                {
                    Console.WriteLine("File {0} doesn't exist.", fullpath_dllex);
                    return;
                }
                Console.Write("File: {0}", fullpath_dllex);
                byte[] datafile  = File.ReadAllBytes(fullpath_dllex);
                uint   imageBase = SplitTools.HelperFunctions.SetupEXE(ref datafile).Value;
                Dictionary <string, int> exports;
                Dictionary <int, string> labels = new Dictionary <int, string>();
                {
                    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 namex = datafile.GetCString(BitConverter.ToInt32(datafile, nameaddr),
                                                           System.Text.Encoding.ASCII);
                        int addr = BitConverter.ToInt32(datafile,
                                                        dir.AddressOfFunctions + (BitConverter.ToInt16(datafile, ordaddr) * 4));
                        exports.Add(namex, addr);
                        labels.Add(addr, namex);
                        nameaddr += 4;
                        ordaddr  += 2;
                    }
                    Console.Write(" ({0} exports)\n", exports.Count);
                }
                if (!exports.ContainsKey(name))
                {
                    Console.WriteLine("The export table has no item named {0}", name);
                    return;
                }
                int address = exports[name];
                // If an array ID is specified, jump to the pointer needed and use it as the address to split
                if (arrayid != -1)
                {
                    uint newpointer = ByteConverter.ToUInt32(datafile, address + arrayid * 4);
                    address = (int)(newpointer - imageBase);
                }
                Console.WriteLine("{0} {1}:{2}", type, name, address.ToString("X8"));
                switch (type)
                {
                // Landtables
                case "landtable":
                case "sa1landtable":
                case "sadxlandtable":
                case "sa2landtable":
                case "sa2blandtable":
                case "battlelandtable":
                    LandTableFormat landfmt_cur;
                    string          landext;
                    switch (type)
                    {
                    case "sa1landtable":
                        landfmt_cur = LandTableFormat.SA1;
                        landext     = ".sa1lvl";
                        break;

                    case "sadxlandtable":
                        landfmt_cur = LandTableFormat.SADX;
                        landext     = ".sa1lvl";
                        break;

                    case "sa2landtable":
                        landfmt_cur = LandTableFormat.SA2;
                        landext     = ".sa2lvl";
                        break;

                    case "sa2blandtable":
                    case "battlelandtable":
                        landfmt_cur = LandTableFormat.SA2B;
                        landext     = ".sa2blvl";
                        break;

                    case "landtable":
                    default:
                        landfmt_cur = LandTableFormat.SADX;
                        landext     = ".sa1lvl";
                        break;
                    }
                    LandTable land = new LandTable(datafile, address, imageBase, landfmt_cur, labels);
                    fileOutputPath = MakePathThatExists(fileOutputPath, land.Name + landext);
                    if (!Directory.Exists(Path.GetDirectoryName(fileOutputPath)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                    }
                    land.SaveToFile(fileOutputPath, landfmt_cur, nometa);
                    break;

                // NJS_OBJECT
                case "model":
                case "object":
                case "basicmodel":
                case "basicdxmodel":
                case "chunkmodel":
                case "gcmodel":
                {
                    ModelFormat modelfmt_obj;
                    string      modelext;
                    switch (type)
                    {
                    case "basicmodel":
                        modelfmt_obj = ModelFormat.Basic;
                        modelext     = ".sa1mdl";
                        break;

                    case "basicdxmodel":
                        modelfmt_obj = ModelFormat.BasicDX;
                        modelext     = ".sa1mdl";
                        break;

                    case "chunkmodel":
                        modelfmt_obj = ModelFormat.Chunk;
                        modelext     = ".sa2mdl";
                        break;

                    case "gcmodel":
                        modelfmt_obj = ModelFormat.GC;
                        modelext     = ".sa2bmdl";
                        break;

                    default:
                        modelfmt_obj = ModelFormat.BasicDX;
                        modelext     = ".sa1mdl";
                        break;
                    }
                    NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, modelfmt_obj, labels, new Dictionary <int, Attach>());
                    fileOutputPath = MakePathThatExists(fileOutputPath, mdl.Name + modelext);
                    if (!Directory.Exists(Path.GetDirectoryName(fileOutputPath)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                    }
                    ModelFile.CreateFile(fileOutputPath, mdl, null, null, null, null, modelfmt_obj, nometa);
                }
                break;

                // NJS_MOTION
                case "animation":
                case "motion":
                    int numparts = 0;
                    for (int a = 3; a < args.Length; a++)
                    {
                        if (args[a] == "-p")
                        {
                            numparts = int.Parse(args[a + 1], System.Globalization.NumberStyles.Integer);
                        }
                    }
                    NJS_MOTION ani = new NJS_MOTION(datafile, address, imageBase, numparts, labels);
                    fileOutputPath = MakePathThatExists(fileOutputPath, ani.Name + "saanim");
                    string outpath = Path.GetDirectoryName(Path.GetFullPath(fileOutputPath));
                    Console.WriteLine("Output file: {0}", Path.GetFullPath(fileOutputPath));
                    if (!Directory.Exists(outpath))
                    {
                        Directory.CreateDirectory(outpath);
                    }
                    ani.Save(fileOutputPath, nometa);
                    break;

                // Attach
                case "attach":
                case "basicattach":
                case "basicdxattach":
                case "chunkattach":
                case "gcattach":
                {
                    Attach      dummy;
                    ModelFormat modelfmt_att;
                    string      modelext = ".sa1mdl";
                    switch (type)
                    {
                    case "basicattach":
                        modelfmt_att = ModelFormat.Basic;
                        modelext     = ".sa1mdl";
                        dummy        = new BasicAttach(datafile, address, imageBase, false);
                        break;

                    case "basicdxattach":
                        modelfmt_att = ModelFormat.BasicDX;
                        modelext     = ".sa1mdl";
                        dummy        = new BasicAttach(datafile, address, imageBase, true);
                        break;

                    case "chunkattach":
                        modelfmt_att = ModelFormat.Chunk;
                        modelext     = ".sa2mdl";
                        dummy        = new ChunkAttach(datafile, address, imageBase);
                        break;

                    case "gcattach":
                        modelfmt_att = ModelFormat.GC;
                        dummy        = new SAModel.GC.GCAttach(datafile, address, imageBase);
                        modelext     = ".sa2bmdl";
                        break;

                    default:
                        modelfmt_att = ModelFormat.BasicDX;
                        modelext     = ".sa1mdl";
                        dummy        = new BasicAttach(datafile, address, imageBase, true);
                        break;
                    }
                    NJS_OBJECT mdl = new NJS_OBJECT()
                    {
                        Attach = dummy
                    };
                    fileOutputPath = MakePathThatExists(fileOutputPath, mdl.Name + modelext);
                    if (!Directory.Exists(Path.GetDirectoryName(fileOutputPath)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath));
                    }
                    ModelFile.CreateFile(fileOutputPath, mdl, null, null, null, null, modelfmt_att, nometa);
                }
                break;

                default:
                    Console.WriteLine("Unrecognized export type {0}", type);
                    break;
                }
                break;

            default:
                Console.WriteLine("Incorrect mode specified. Press ENTER to exit.");
                Console.ReadLine();
                return;
            }
        }
Пример #27
0
        static void Main(string[] args)
        {
            string dir = Environment.CurrentDirectory;

            try
            {
                Queue <string> argq = new Queue <string>(args);
                if (argq.Count > 0 && argq.Peek().Equals("/be", StringComparison.OrdinalIgnoreCase))
                {
                    ByteConverter.BigEndian = true;
                    argq.Dequeue();
                }
                string mdlfilename;
                if (argq.Count > 0)
                {
                    mdlfilename = argq.Dequeue();
                    Console.WriteLine("File: {0}", mdlfilename);
                }
                else
                {
                    Console.Write("File: ");
                    mdlfilename = Console.ReadLine();
                }
                mdlfilename = Path.GetFullPath(mdlfilename);
                string[] anifilenames = new string[argq.Count];
                for (int j = 0; j < anifilenames.Length; j++)
                {
                    Console.WriteLine("Animations: {0}", argq.Peek());
                    anifilenames[j] = Path.GetFullPath(argq.Dequeue());
                }
                Environment.CurrentDirectory = Path.GetDirectoryName(mdlfilename);
                byte[] mdlfile = File.ReadAllBytes(mdlfilename);
                if (Path.GetExtension(mdlfilename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
                {
                    mdlfile = FraGag.Compression.Prs.Decompress(mdlfile);
                }
                Directory.CreateDirectory(Path.GetFileNameWithoutExtension(mdlfilename));
                int address = 0;
                int i       = ByteConverter.ToInt32(mdlfile, address);
                SortedDictionary <int, int> modeladdrs = new SortedDictionary <int, int>();
                while (i != -1)
                {
                    modeladdrs[i] = ByteConverter.ToInt32(mdlfile, address + 4);
                    address      += 8;
                    i             = ByteConverter.ToInt32(mdlfile, address);
                }
                Dictionary <int, NJS_OBJECT> models     = new Dictionary <int, NJS_OBJECT>();
                Dictionary <int, string>     modelnames = new Dictionary <int, string>();
                List <string> partnames = new List <string>();
                foreach (KeyValuePair <int, int> item in modeladdrs)
                {
                    NJS_OBJECT obj = new NJS_OBJECT(mdlfile, item.Value, 0, ModelFormat.Chunk);
                    modelnames[item.Key] = obj.Name;
                    if (!partnames.Contains(obj.Name))
                    {
                        List <string> names = new List <string>(obj.GetObjects().Select((o) => o.Name));
                        foreach (int idx in modelnames.Where(a => names.Contains(a.Value)).Select(a => a.Key))
                        {
                            models.Remove(idx);
                        }
                        models[item.Key] = obj;
                        partnames.AddRange(names);
                    }
                }
                Dictionary <int, string>    animfns = new Dictionary <int, string>();
                Dictionary <int, Animation> anims   = new Dictionary <int, Animation>();
                foreach (string anifilename in anifilenames)
                {
                    byte[] anifile = File.ReadAllBytes(anifilename);
                    if (Path.GetExtension(anifilename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
                    {
                        anifile = FraGag.Compression.Prs.Decompress(anifile);
                    }
                    Directory.CreateDirectory(Path.GetFileNameWithoutExtension(anifilename));
                    address = 0;
                    i       = ByteConverter.ToInt16(anifile, address);
                    while (i != -1)
                    {
                        anims[i]   = new Animation(anifile, ByteConverter.ToInt32(anifile, address + 4), 0, ByteConverter.ToInt16(anifile, address + 2));
                        animfns[i] = Path.Combine(Path.GetFileNameWithoutExtension(anifilename), i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                        address   += 8;
                        i          = ByteConverter.ToInt16(anifile, address);
                    }
                }
                foreach (KeyValuePair <int, NJS_OBJECT> model in models)
                {
                    List <string> animlist = new List <string>();
                    foreach (KeyValuePair <int, Animation> anim in anims)
                    {
                        if (model.Value.CountAnimated() == anim.Value.ModelParts)
                        {
                            animlist.Add("../" + animfns[anim.Key]);
                        }
                    }
                    ModelFile.CreateFile(Path.Combine(Path.GetFileNameWithoutExtension(mdlfilename),
                                                      model.Key.ToString(NumberFormatInfo.InvariantInfo) + ".sa2mdl"), model.Value, animlist.ToArray(),
                                         null, null, null, "splitMDL", null, ModelFormat.Chunk);
                }
                IniSerializer.Serialize(modelnames, new IniCollectionSettings(IniCollectionMode.IndexOnly),
                                        Path.Combine(Path.GetFileNameWithoutExtension(mdlfilename), Path.GetFileNameWithoutExtension(mdlfilename) + ".ini"));
                foreach (KeyValuePair <int, Animation> anim in anims)
                {
                    anim.Value.Save(animfns[anim.Key]);
                }
            }
            finally
            {
                Environment.CurrentDirectory = dir;
            }
        }
Пример #28
0
        static void Main(string[] args)
        {
            string basemdlname;

            if (args.Length > 0)
            {
                basemdlname = args[0];
            }
            else
            {
                Console.Write("Base Model: ");
                basemdlname = Console.ReadLine().Trim('"');
            }
            string      fext      = Path.GetExtension(basemdlname);
            ModelFile   modelFile = new ModelFile(basemdlname);
            ModelFormat fmt       = modelFile.Format;
            NJS_OBJECT  basemdl   = modelFile.Model;
            string      mtnname;

            if (args.Length > 1)
            {
                mtnname = args[1];
            }
            else
            {
                Console.Write("Motion: ");
                mtnname = Console.ReadLine().Trim('"');
            }
            NJS_MOTION mtn = NJS_MOTION.Load(mtnname, basemdl.CountMorph());
            string     outdir;

            if (args.Length > 2)
            {
                outdir = args[2];
            }
            else
            {
                outdir = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(mtnname)), Path.GetFileNameWithoutExtension(mtnname));
            }
            Directory.CreateDirectory(outdir);
            MTNInfo inf = new MTNInfo()
            {
                ModelFormat = fmt, Name = mtn.Name, Frames = mtn.Frames, InterpolationMode = mtn.InterpolationMode
            };

            IniSerializer.Serialize(inf, Path.Combine(outdir, Path.ChangeExtension(Path.GetFileName(mtnname), ".ini")));
            NJS_OBJECT[] objs = basemdl.GetObjects().Where(a => a.Morph).ToArray();
            for (int frame = 0; frame < mtn.Frames; frame++)
            {
                if (!mtn.Models.Any(a => a.Value.Vertex.ContainsKey(frame)))
                {
                    continue;
                }
                foreach ((int idx, AnimModelData amd) in mtn.Models)
                {
                    if (amd.Vertex.ContainsKey(frame))
                    {
                        Vertex[] verts = amd.Vertex[frame];
                        amd.Normal.TryGetValue(frame, out Vertex[] norms);
                        switch (objs[idx].Attach)
                        {
                        case BasicAttach batt:
                            for (int i = 0; i < Math.Min(verts.Length, batt.Vertex.Length); i++)
                            {
                                batt.Vertex[i] = verts[i];
                                if (norms != null && batt.Normal != null)
                                {
                                    batt.Normal[i] = norms[i];
                                }
                            }
                            break;

                        case ChunkAttach catt:
                            if (catt.Vertex == null)
                            {
                                continue;
                            }
                            int vcnt = catt.Vertex.Sum(a => a.VertexCount);
                            int ci   = 0;
                            int vi   = 0;
                            for (int i = 0; i < Math.Min(verts.Length, vcnt); i++)
                            {
                                catt.Vertex[ci].Vertices[vi] = verts[i];
                                if (norms != null && catt.Vertex[ci].Normals != null)
                                {
                                    catt.Vertex[ci].Normals[vi] = norms[i];
                                }
                                if (++vi >= catt.Vertex[ci].VertexCount)
                                {
                                    ++ci;
                                    vi = 0;
                                }
                            }
                            break;
                        }
                    }
                }
                ModelFile.CreateFile(Path.Combine(outdir, $"{frame}{fext}"), basemdl, null, null, null, null, fmt);
            }
        }
Пример #29
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);
        }
Пример #30
0
        public static void Split(bool isBigEndian, string filePath, string outputFolder, string[] animationPaths)
        {
            string dir = Environment.CurrentDirectory;

            try
            {
                if (outputFolder[outputFolder.Length - 1] != '/')
                {
                    outputFolder = string.Concat(outputFolder, "/");
                }
                ByteConverter.BigEndian = isBigEndian;

                // get file name, read it from the console if nothing
                string mdlfilename = filePath;

                mdlfilename = Path.GetFullPath(mdlfilename);

                // look through the argumetns for animationfiles
                string[] anifilenames = animationPaths;

                // load model file
                Environment.CurrentDirectory = (outputFolder.Length != 0) ? outputFolder : Path.GetDirectoryName(mdlfilename);
                byte[] mdlfile = File.ReadAllBytes(mdlfilename);
                if (Path.GetExtension(mdlfilename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
                {
                    mdlfile = FraGag.Compression.Prs.Decompress(mdlfile);
                }
                Directory.CreateDirectory(Path.GetFileNameWithoutExtension(mdlfilename));

                // getting model pointers
                int address = 0;
                int i       = ByteConverter.ToInt32(mdlfile, address);
                SortedDictionary <int, int> modeladdrs = new SortedDictionary <int, int>();
                while (i != -1)
                {
                    modeladdrs[i] = ByteConverter.ToInt32(mdlfile, address + 4);
                    address      += 8;
                    i             = ByteConverter.ToInt32(mdlfile, address);
                }

                // load models from pointer list
                Dictionary <int, NJS_OBJECT> models     = new Dictionary <int, NJS_OBJECT>();
                Dictionary <int, string>     modelnames = new Dictionary <int, string>();
                List <string> partnames = new List <string>();
                foreach (KeyValuePair <int, int> item in modeladdrs)
                {
                    NJS_OBJECT obj = new NJS_OBJECT(mdlfile, item.Value, 0, ModelFormat.Chunk);
                    modelnames[item.Key] = obj.Name;
                    if (!partnames.Contains(obj.Name))
                    {
                        List <string> names = new List <string>(obj.GetObjects().Select((o) => o.Name));
                        foreach (int idx in modelnames.Where(a => names.Contains(a.Value)).Select(a => a.Key))
                        {
                            models.Remove(idx);
                        }
                        models[item.Key] = obj;
                        partnames.AddRange(names);
                    }
                }

                // load animations
                Dictionary <int, string>     animfns = new Dictionary <int, string>();
                Dictionary <int, NJS_MOTION> anims   = new Dictionary <int, NJS_MOTION>();
                foreach (string anifilename in anifilenames)
                {
                    Dictionary <int, int>    processedanims = new Dictionary <int, int>();
                    Dictionary <int, string> ini            = new Dictionary <int, string>();
                    byte[] anifile = File.ReadAllBytes(anifilename);
                    if (Path.GetExtension(anifilename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
                    {
                        anifile = FraGag.Compression.Prs.Decompress(anifile);
                    }
                    Directory.CreateDirectory(Path.GetFileNameWithoutExtension(anifilename));
                    address = 0;
                    i       = ByteConverter.ToInt16(anifile, address);
                    while (i != -1)
                    {
                        int aniaddr = ByteConverter.ToInt32(anifile, address + 4);
                        if (!processedanims.ContainsKey(aniaddr))
                        {
                            anims[i]   = new NJS_MOTION(anifile, ByteConverter.ToInt32(anifile, address + 4), 0, ByteConverter.ToInt16(anifile, address + 2));
                            animfns[i] = Path.Combine(Path.GetFileNameWithoutExtension(anifilename), i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim");
                            anims[i].Save(animfns[i]);
                            processedanims[aniaddr] = i;
                        }
                        ini[i]   = "animation_" + aniaddr.ToString("X8");
                        address += 8;
                        i        = ByteConverter.ToInt16(anifile, address);
                    }
                    IniSerializer.Serialize(ini, new IniCollectionSettings(IniCollectionMode.IndexOnly), Path.Combine(Path.GetFileNameWithoutExtension(anifilename), Path.GetFileNameWithoutExtension(anifilename) + ".ini"));
                }

                // save output model files
                foreach (KeyValuePair <int, NJS_OBJECT> model in models)
                {
                    List <string> animlist = new List <string>();
                    foreach (KeyValuePair <int, NJS_MOTION> anim in anims)
                    {
                        if (model.Value.CountAnimated() == anim.Value.ModelParts)
                        {
                            string rel = animfns[anim.Key].Replace(outputFolder, string.Empty);
                            if (rel.Length > 1 && rel[1] != ':')
                            {
                                rel = "../" + rel;
                            }
                            animlist.Add(rel);
                        }
                    }

                    ModelFile.CreateFile(Path.Combine(Path.GetFileNameWithoutExtension(mdlfilename),
                                                      model.Key.ToString(NumberFormatInfo.InvariantInfo) + ".sa2mdl"), model.Value, animlist.ToArray(),
                                         null, null, null, null, ModelFormat.Chunk);
                }

                // save ini file
                IniSerializer.Serialize(modelnames, new IniCollectionSettings(IniCollectionMode.IndexOnly),
                                        Path.Combine(Path.GetFileNameWithoutExtension(mdlfilename), Path.GetFileNameWithoutExtension(mdlfilename) + ".ini"));
            }
            finally
            {
                Environment.CurrentDirectory = dir;
            }
        }