Example #1
0
        static List <string> GetLabels(this LandTable land)
        {
            List <string> labels = new List <string>()
            {
                land.Name
            };

            if (land.COLName != null)
            {
                labels.Add(land.COLName);
                foreach (COL col in land.COL)
                {
                    if (col.Model != null)
                    {
                        labels.AddRange(col.Model.GetLabels());
                    }
                }
            }
            if (land.AnimName != null)
            {
                labels.Add(land.AnimName);
                foreach (GeoAnimData gan in land.Anim)
                {
                    if (gan.Model != null)
                    {
                        labels.AddRange(gan.Model.GetLabels());
                    }
                    if (gan.Animation != null)
                    {
                        labels.Add(gan.Animation.Name);
                    }
                }
            }
            return(labels);
        }
Example #2
0
        public static void ExportINI(DllIniData IniData,
                                     Dictionary <string, bool> itemsToExport, string fileName)
        {
            string     dstfol = Path.GetDirectoryName(fileName);
            DllIniData output = new DllIniData()
            {
                Name     = IniData.Name,
                Game     = IniData.Game,
                Exports  = IniData.Exports,
                TexLists = IniData.TexLists
            };
            List <string> labels = new List <string>();

            foreach (KeyValuePair <string, FileTypeHash> item in
                     IniData.Files.Where(i => itemsToExport[i.Key]))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(dstfol, item.Key)));
                File.Copy(item.Key, Path.Combine(dstfol, item.Key), true);
                switch (item.Value.Type)
                {
                case "landtable":
                    LandTable tbl = LandTable.LoadFromFile(item.Key);
                    labels.AddRange(tbl.GetLabels());
                    break;

                case "model":
                case "basicmodel":
                case "chunkmodel":
                case "gcmodel":
                case "basicdxmodel":
                    NJS_OBJECT mdl = new ModelFile(item.Key).Model;
                    labels.AddRange(mdl.GetLabels());
                    break;

                case "animation":
                    NJS_MOTION ani = NJS_MOTION.Load(item.Key);
                    labels.Add(ani.Name);
                    break;
                }
                output.Files.Add(item.Key, new FileTypeHash(item.Value.Type, null));
            }
            output.Items = new List <DllItemInfo>(IniData.Items.Where(a => labels.Contains(a.Label)));
            foreach (var item in IniData.DataItems.Where(i => itemsToExport[i.Filename]))
            {
                Directory.CreateDirectory(Path.Combine(dstfol, item.Filename));
                CopyDirectory(new DirectoryInfo(item.Filename), Path.Combine(dstfol, item.Filename));
                output.DataItems.Add(item);
            }
            IniSerializer.Serialize(output, fileName);
        }
Example #3
0
        private void button6_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog fd = new SaveFileDialog()
            {
                DefaultExt = "ini", Filter = "INI files|*.ini", InitialDirectory = Environment.CurrentDirectory, RestoreDirectory = true
            })
                if (fd.ShowDialog(this) == DialogResult.OK)
                {
                    string     dstfol = Path.GetDirectoryName(fd.FileName);
                    DllIniData output = new DllIniData()
                    {
                        Name     = IniData.Name,
                        Game     = IniData.Game,
                        Exports  = IniData.Exports,
                        TexLists = IniData.TexLists,
                        Files    = new DictionaryContainer <FileTypeHash>()
                    };
                    List <string> labels = new List <string>();
                    foreach (KeyValuePair <string, FileTypeHash> item in IniData.Files.Where((a, i) => listView1.CheckedIndices.Contains(i)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(dstfol, item.Key)));
                        File.Copy(item.Key, Path.Combine(dstfol, item.Key), true);
                        switch (item.Value.Type)
                        {
                        case "landtable":
                            LandTable tbl = LandTable.LoadFromFile(item.Key);
                            labels.AddRange(tbl.GetLabels());
                            break;

                        case "model":
                        case "basicmodel":
                        case "chunkmodel":
                        case "basicdxmodel":
                            NJS_OBJECT mdl = new ModelFile(item.Key).Model;
                            labels.AddRange(mdl.GetLabels());
                            break;

                        case "animation":
                            Animation ani = Animation.Load(item.Key);
                            labels.Add(ani.Name);
                            break;
                        }
                        output.Files.Add(item.Key, new FileTypeHash(item.Value.Type, null));
                    }
                    output.Items = new List <DllItemInfo>(IniData.Items.Where(a => labels.Contains(a.Label)));
                    IniSerializer.Serialize(output, fd.FileName);
                }
        }
Example #4
0
        /// <summary>
        /// Reads a file and loads it into the Inspector
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenFile(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new()
            {
                Filter = "All Supported Formats (*.*mdl, *.nj, *.gj, *.*lvl)|*.BFMDL;*.SA1MDL;*.SA2MDL;*.SA2BMDL;*.NJ;*.GJ;*.BFLVL;*.SA1LVL;*.SA2LVL;*.SA2BLVL"
                         + "|Model File (*.*mdl, *.nj, *.gj)|*.BFMDL;*.SA1MDL;*.SA2MDL;*.SA2BMDL;*.NJ;*.GJ"
                         + "|Level File (*.*lvl)|*.BFLVL;*.SA1LVL;*.SA2LVL;*.SA2BLVL"
            };

            if (ofd.ShowDialog() != true)
            {
                return;
            }

            byte[] file = File.ReadAllBytes(ofd.FileName);

            ModelFile mdlFile = ModelFile.Read(file, ofd.FileName);

            if (mdlFile != null)
            {
                loaded = mdlFile;
                Inspector.LoadNewObject(mdlFile);
                return;
            }


            LandTable ltbl = LandTable.ReadFile(file);

            if (ltbl != null)
            {
                loaded = ltbl;

                Inspector.LoadNewObject(ltbl);
                return;
            }
        }
Example #5
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);
        }
Example #6
0
        static void Main(string[] args)
        {
            Environment.CurrentDirectory = @"C:\SONICADVENTUREDX\Projects\ECPort";

            List <BMPInfo> textures  = new List <BMPInfo>(TextureArchive.GetTextures(@"C:\SONICADVENTUREDX\system\BEACH01.PVM"));
            LandTable      landTable = LandTable.LoadFromFile(@"Levels\Emerald Coast\Act 1\LandTable.sa1lvl");

            texmap = new Dictionary <int, int>();
            BMPInfo[] newtexs = TextureArchive.GetTextures(@"C:\SONICADVENTUREDX\system\BEACH03.PVM");
            for (int i = 0; i < newtexs.Length; i++)
            {
                BMPInfo found = textures.FirstOrDefault(a => a.Name.Equals(newtexs[i].Name));
                if (found == null)
                {
                    texmap[i] = textures.Count;
                    textures.Add(newtexs[i]);
                }
                else
                {
                    texmap[i] = textures.IndexOf(found);
                }
            }
            foreach (COL col in LandTable.LoadFromFile(@"Levels\Emerald Coast\Act 3\LandTable.sa1lvl").COL)
            {
                foreach (NJS_MATERIAL mat in ((BasicAttach)col.Model.Attach).Material)
                {
                    mat.TextureID = texmap[mat.TextureID];
                }
                landTable.COL.Add(col);
            }
            texmap  = new Dictionary <int, int>();
            newtexs = TextureArchive.GetTextures(@"C:\SONICADVENTUREDX\system\BEACH02.PVM");
            for (int i = 0; i < newtexs.Length; i++)
            {
                BMPInfo found = textures.FirstOrDefault(a => a.Name.Equals(newtexs[i].Name));
                if (found == null)
                {
                    texmap[i] = textures.Count;
                    textures.Add(newtexs[i]);
                }
                else
                {
                    texmap[i] = textures.IndexOf(found);
                }
            }
            foreach (COL col in LandTable.LoadFromFile(@"Levels\Emerald Coast\Act 2\LandTable.sa1lvl").COL)
            {
                col.Bounds.Center.Z  -= 2000;
                col.Model.Position.Z -= 2000;
                foreach (NJS_MATERIAL mat in ((BasicAttach)col.Model.Attach).Material)
                {
                    mat.TextureID = texmap[mat.TextureID];
                }
                landTable.COL.Add(col);
            }

            texmap  = new Dictionary <int, int>();
            newtexs = TextureArchive.GetTextures(@"C:\SONICADVENTUREDX\system\OBJ_BEACH.PVM");
            for (int i = 0; i < newtexs.Length; i++)
            {
                BMPInfo found = textures.FirstOrDefault(a => a.Name.Equals(newtexs[i].Name));
                if (found == null)
                {
                    texmap[i] = textures.Count;
                    textures.Add(newtexs[i]);
                }
                else
                {
                    texmap[i] = textures.IndexOf(found);
                }
            }

            PAKFile     pak       = new PAKFile();
            List <byte> inf       = new List <byte>();
            string      filenoext = "beach01";
            string      longdir   = "..\\..\\..\\sonic2\\resource\\gd_pc\\prs\\" + filenoext;

            using (System.Windows.Forms.Panel panel = new System.Windows.Forms.Panel())
                using (Direct3D d3d = new Direct3D())
                    using (Device dev = new Device(d3d, 0, DeviceType.Hardware, panel.Handle, CreateFlags.HardwareVertexProcessing, new PresentParameters(640, 480)))
                    {
                        for (int i = 0; i < textures.Count; i++)
                        {
                            using (Texture tex = textures[i].Image.ToTexture(dev))
                                using (DataStream str = Surface.ToStream(tex.GetSurfaceLevel(0), ImageFileFormat.Dds))
                                    using (MemoryStream ms = new MemoryStream())
                                    {
                                        str.CopyTo(ms);
                                        pak.Files.Add(new PAKFile.File(filenoext + '\\' + Path.ChangeExtension(textures[i].Name, ".dds"), longdir + '\\' + Path.ChangeExtension(textures[i].Name, ".dds"), ms.ToArray()));
                                    }
                            int infsz = inf.Count;
                            inf.AddRange(Encoding.ASCII.GetBytes(Path.ChangeExtension(textures[i].Name, null)));
                            inf.AddRange(new byte[0x1C - (inf.Count - infsz)]);
                            inf.AddRange(BitConverter.GetBytes(i + 200));
                            inf.AddRange(BitConverter.GetBytes(0));
                            inf.AddRange(BitConverter.GetBytes(0));
                            inf.AddRange(BitConverter.GetBytes(0));
                            inf.AddRange(BitConverter.GetBytes(textures[i].Image.Width));
                            inf.AddRange(BitConverter.GetBytes(textures[i].Image.Height));
                            inf.AddRange(BitConverter.GetBytes(0));
                            inf.AddRange(BitConverter.GetBytes(0x80000000));
                        }
                    }
            pak.Files.Insert(0, new PAKFile.File(filenoext + '\\' + filenoext + ".inf", longdir + '\\' + filenoext + ".inf", inf.ToArray()));
            pak.Save(@"C:\Program Files (x86)\Steam\steamapps\common\Sonic Adventure 2\mods\Emerald Coast\gd_PC\PRS\beach01.pak");

            List <COL> newcollist = new List <COL>();
            Dictionary <string, Attach> visitedAttaches = new Dictionary <string, Attach>();

            foreach (COL col in landTable.COL.Where((col) => col.Model != null && col.Model.Attach != null))
            {
                ConvertCOL(newcollist, visitedAttaches, col);
            }
            landTable.COL = newcollist;

            Console.WriteLine("Loading Object Definitions:");
            Console.WriteLine("Parsing...");

            LevelData.ObjDefs = new List <ObjectDefinition>();
            Dictionary <string, ObjectData> objdefini =
                IniSerializer.Deserialize <Dictionary <string, ObjectData> >("objdefs.ini");

            List <ObjectData> objectErrors = new List <ObjectData>();

            ObjectListEntry[] objlstini = ObjectList.Load(@"Levels\Emerald Coast\Object List.ini", false);
            Directory.CreateDirectory("dllcache").Attributes |= FileAttributes.Hidden;

            List <KeyValuePair <string, string> > compileErrors = new List <KeyValuePair <string, string> >();

            for (int ID = 0; ID < objlstini.Length; ID++)
            {
                string codeaddr = objlstini[ID].CodeString;

                if (!objdefini.ContainsKey(codeaddr))
                {
                    codeaddr = "0";
                }

                ObjectData       defgroup = objdefini[codeaddr];
                ObjectDefinition def;

                if (!string.IsNullOrEmpty(defgroup.CodeFile))
                {
                    Console.WriteLine("Compiling: " + defgroup.CodeFile);


                    def = CompileObjectDefinition(defgroup, out bool errorOccured, out string errorText);

                    if (errorOccured)
                    {
                        KeyValuePair <string, string> errorValue = new KeyValuePair <string, string>(
                            defgroup.CodeFile, errorText);

                        compileErrors.Add(errorValue);
                    }
                }
                else
                {
                    def = new DefaultObjectDefinition();
                }

                LevelData.ObjDefs.Add(def);

                // The only reason .Model is checked for null is for objects that don't yet have any
                // models defined for them. It would be annoying seeing that error all the time!
                if (string.IsNullOrEmpty(defgroup.CodeFile) && !string.IsNullOrEmpty(defgroup.Model))
                {
                    Console.WriteLine("Loading: " + defgroup.Model);
                    // Otherwise, if the model file doesn't exist and/or no texture file is defined,
                    // load the "default object" instead ("?").
                    if (!File.Exists(defgroup.Model))
                    {
                        ObjectData error = new ObjectData {
                            Name = defgroup.Name, Model = defgroup.Model, Texture = defgroup.Texture
                        };
                        objectErrors.Add(error);
                        defgroup.Model = null;
                    }
                }

                def.Init(defgroup, objlstini[ID].Name);
                def.SetInternalName(objlstini[ID].Name);
            }

            // Checks if there have been any errors added to the error list and does its thing
            // This thing is a mess. If anyone can think of a cleaner way to do this, be my guest.
            if (objectErrors.Count > 0)
            {
                int           count        = objectErrors.Count;
                List <string> errorStrings = new List <string> {
                    "The following objects failed to load:"
                };

                foreach (ObjectData o in objectErrors)
                {
                    bool texEmpty  = string.IsNullOrEmpty(o.Texture);
                    bool texExists = (!string.IsNullOrEmpty(o.Texture) && LevelData.Textures.ContainsKey(o.Texture));
                    errorStrings.Add("");
                    errorStrings.Add("Object:\t\t" + o.Name);
                    errorStrings.Add("\tModel:");
                    errorStrings.Add("\t\tName:\t" + o.Model);
                    errorStrings.Add("\t\tExists:\t" + File.Exists(o.Model));
                    errorStrings.Add("\tTexture:");
                    errorStrings.Add("\t\tName:\t" + ((texEmpty) ? "(N/A)" : o.Texture));
                    errorStrings.Add("\t\tExists:\t" + texExists);
                }

                // TODO: Proper logging. Who knows where this file may end up
                File.WriteAllLines("SADXLVL2.log", errorStrings.ToArray());
            }

            // Loading SET Layout
            Console.WriteLine("Loading SET items", "Initializing...");

            List <SETItem> setlist = new List <SETItem>();

            SonicRetro.SAModel.SAEditorCommon.UI.EditorItemSelection selection = new SonicRetro.SAModel.SAEditorCommon.UI.EditorItemSelection();
            if (LevelData.ObjDefs.Count > 0)
            {
                string setstr = @"C:\SONICADVENTUREDX\Projects\ECPort\system\SET0100S.BIN";
                if (File.Exists(setstr))
                {
                    Console.WriteLine("SET: " + setstr.Replace(Environment.CurrentDirectory, ""));

                    setlist = SETItem.Load(setstr, selection);
                }
                setstr = @"C:\SONICADVENTUREDX\Projects\ECPort\system\SET0102B.BIN";
                if (File.Exists(setstr))
                {
                    Console.WriteLine("SET: " + setstr.Replace(Environment.CurrentDirectory, ""));

                    setlist.AddRange(SETItem.Load(setstr, selection));
                }
                setstr = @"C:\SONICADVENTUREDX\Projects\ECPort\system\SET0101S.BIN";
                if (File.Exists(setstr))
                {
                    Console.WriteLine("SET: " + setstr.Replace(Environment.CurrentDirectory, ""));

                    List <SETItem> newlist = SETItem.Load(setstr, selection);
                    foreach (SETItem item in newlist)
                    {
                        item.Position.Z -= 2000;
                    }
                    setlist.AddRange(newlist);
                }
            }

            MatrixStack         transform = new MatrixStack();
            List <SETItem>      add       = new List <SETItem>();
            List <SETItem>      del       = new List <SETItem>();
            List <PalmtreeData> trees     = new List <PalmtreeData>();

            foreach (SETItem item in setlist)
            {
                switch (item.ID)
                {
                case 0xD:                         // item box
                    item.ID      = 0xA;
                    item.Scale.X = itemboxmap[(int)item.Scale.X];
                    break;

                case 0x15:                         // ring group to rings
                    for (int i = 0; i < Math.Min(item.Scale.X + 1, 8); i++)
                    {
                        if (item.Scale.Z == 1)                                 // circle
                        {
                            double  v4 = i * 360.0;
                            Vector3 v7 = new Vector3(
                                ObjectHelper.NJSin((int)(v4 / item.Scale.X * 65536.0 * 0.002777777777777778)) * item.Scale.Y,
                                0,
                                ObjectHelper.NJCos((int)(v4 / item.Scale.X * 65536.0 * 0.002777777777777778)) * item.Scale.Y);
                            transform.Push();
                            transform.NJTranslate(item.Position);
                            transform.NJRotateObject(item.Rotation);
                            Vector3 pos = Vector3.TransformCoordinate(v7, transform.Top);
                            transform.Pop();
                            add.Add(new SETItem(0, selection)
                            {
                                Position = pos.ToVertex()
                            });
                        }
                        else                                 // line
                        {
                            transform.Push();
                            transform.NJTranslate(item.Position);
                            transform.NJRotateObject(item.Rotation);
                            double v5;
                            if (i % 2 == 1)
                            {
                                v5 = i * item.Scale.Y * -0.5;
                            }
                            else
                            {
                                v5 = Math.Ceiling(i * 0.5) * item.Scale.Y;
                            }
                            Vector3 pos = Vector3.TransformCoordinate(new Vector3(0, 0, (float)v5), transform.Top);
                            transform.Pop();
                            add.Add(new SETItem(0, selection)
                            {
                                Position = pos.ToVertex()
                            });
                        }
                    }
                    del.Add(item);
                    break;

                case 0x1A:                         // tikal -> omochao
                    item.ID          = 0x19;
                    item.Position.Y += 3;
                    break;

                case 0x1D:                         // kiki
                    item.ID       = 0x5B;
                    item.Rotation = new Rotation();
                    item.Scale    = new Vertex();
                    break;

                case 0x1F:                         // sweep ->beetle
                    item.ID       = 0x38;
                    item.Rotation = new Rotation();
                    item.Scale    = new Vertex(1, 0, 0);
                    break;

                case 0x28:                         // launch ramp
                    item.ID       = 6;
                    item.Scale.X /= 2.75f;
                    item.Scale.Z  = 0.799999952316284f;
                    break;

                case 0x4F:                         // updraft
                    item.ID      = 0x35;
                    item.Scale.X = Math.Max(Math.Min(item.Scale.X, 200), 10) / 2;
                    item.Scale.Y = Math.Max(Math.Min(item.Scale.Y, 200), 10) / 2;
                    item.Scale.Z = Math.Max(Math.Min(item.Scale.Z, 200), 10) / 2;
                    break;

                case 0x52:                         // item box air
                    item.ID      = 0xB;
                    item.Scale.X = itemboxmap[(int)item.Scale.X];
                    break;

                // palm trees
                case 32:
                case 33:
                case 34:
                case 35:
                    trees.Add(new PalmtreeData((byte)(item.ID - 32), item.Position, item.Rotation));
                    del.Add(item);
                    break;

                // nonsolid objects
                case 47:
                case 48:
                case 49:
                case 50:
                case 51:
                case 52:
                case 59:
                case 62:
                case 63:
                case 64:
                case 70:
                    ConvertSETItem(newcollist, item, false, setlist.IndexOf(item));
                    del.Add(item);
                    break;

                // solid objects
                case 36:
                case 37:
                case 39:
                case 41:
                case 42:
                case 43:
                case 44:
                case 45:
                case 46:
                case 54:
                case 58:
                case 66:
                case 71:
                case 72:
                case 73:
                case 74:
                    ConvertSETItem(newcollist, item, true, setlist.IndexOf(item));
                    del.Add(item);
                    break;

                case 81:                         // goal
                    item.ID          = 0xE;
                    item.Position.Y += 30;
                    break;

                default:
                    if (idmap.ContainsKey(item.ID))
                    {
                        item.ID = idmap[item.ID];
                    }
                    else
                    {
                        del.Add(item);
                    }
                    break;
                }
            }
            setlist.AddRange(add);
            foreach (SETItem item in del)
            {
                setlist.Remove(item);
            }
            setlist.Add(new SETItem(0x55, selection)
            {
                Position = new Vertex(6158.6f, -88f, 2384.97f), Scale = new Vertex(3, 0, 0)
            });
            {
                COL col = new COL()
                {
                    Model = new ModelFile(@"E:\Bridge Model.sa1mdl").Model, SurfaceFlags = SurfaceFlags.Visible
                };
                col.Model.Position = new Vertex(2803, -1, 365);
                foreach (NJS_MATERIAL mat in ((BasicAttach)col.Model.Attach).Material)
                {
                    mat.TextureID = texmap[mat.TextureID];
                }
                col.Model.ProcessVertexData();
                col.CalculateBounds();
                ConvertCOL(newcollist, new Dictionary <string, Attach>(), col);
                col = new COL()
                {
                    Model = new ModelFile(@"E:\Bridge Model COL.sa1mdl").Model, SurfaceFlags = SurfaceFlags.Solid
                };
                col.Model.Position = new Vertex(2803, -1, 365);
                col.Model.ProcessVertexData();
                col.CalculateBounds();
                newcollist.Add(col);
                col = new COL()
                {
                    Model = new ModelFile(@"E:\BridgeSegment0.sa1mdl").Model, SurfaceFlags = SurfaceFlags.Solid
                };
                col.Model.ProcessVertexData();
                col.CalculateBounds();
                newcollist.Add(col);
                col = new COL()
                {
                    Model = new ModelFile(@"E:\BridgeSegment1.sa1mdl").Model, SurfaceFlags = SurfaceFlags.Solid
                };
                col.Model.ProcessVertexData();
                col.CalculateBounds();
                newcollist.Add(col);
                col = new COL()
                {
                    Model = new ModelFile(@"E:\BridgeSegment2.sa1mdl").Model, SurfaceFlags = SurfaceFlags.Solid
                };
                col.Model.ProcessVertexData();
                col.CalculateBounds();
                newcollist.Add(col);
                col = new COL()
                {
                    Model = new ModelFile(@"E:\BridgeSegment3.sa1mdl").Model, SurfaceFlags = SurfaceFlags.Solid
                };
                col.Model.ProcessVertexData();
                col.CalculateBounds();
                newcollist.Add(col);
            }
            landTable.SaveToFile(@"C:\Program Files (x86)\Steam\steamapps\common\Sonic Adventure 2\mods\Emerald Coast\LandTable.sa2lvl", LandTableFormat.SA2);
            ByteConverter.BigEndian = true;
            SETItem.Save(setlist, @"C:\Program Files (x86)\Steam\steamapps\common\Sonic Adventure 2\mods\Emerald Coast\gd_PC\set0013_s.bin");
            for (int i = 0; i < 4; i++)
            {
                ModelFile modelFile = new ModelFile($@"C:\SONICADVENTUREDX\Projects\Test\Objects\Levels\Emerald Coast\YASI{i}.sa1mdl");
                foreach (BasicAttach attach in modelFile.Model.GetObjects().Where(a => a.Attach != null).Select(a => a.Attach))
                {
                    foreach (NJS_MATERIAL mat in attach.Material)
                    {
                        mat.TextureID = texmap[mat.TextureID];
                    }
                }
                modelFile.SaveToFile($@"C:\Program Files (x86)\Steam\steamapps\common\Sonic Adventure 2\mods\Emerald Coast\YASI{i}.sa1mdl");
            }
            using (StreamWriter sw = File.CreateText(@"E:\Documents\Visual Studio 2017\Projects\LevelTest\LevelTest\pt.c"))
                sw.WriteLine(string.Join(",\r\n", trees));
        }
Example #7
0
        // Scan for Landtables
        static void ScanLandtable(LandTableFormat landfmt)
        {
            CurrentStep++;
            CurrentScanData         = "Landtables " + landfmt.ToString();
            ByteConverter.BigEndian = BigEndian;
            Console.WriteLine("Step {0}: Scanning for {1} landtables", CurrentStep, landfmt.ToString());
            string landtable_extension = ".sa1lvl";
            int    count = 0;

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

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

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

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

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

                        case LandTableFormat.SA2:
                            FoundSA2Landtables++;
                            break;

                        case LandTableFormat.SA2B:
                            FoundSA2BLandtables++;
                            break;
                        }
                        landtablelist.Add(address);
                        Console.WriteLine("\rLandtable {0} at {1}", landfmt.ToString(), address.ToString("X8"));
                        addresslist.Add(address, "landtable_" + landfmt.ToString());
                        address += (uint)LandTable.Size(landfmt) - 1;
                    }
                }
                catch (Exception)
                {
                    continue;
                }
            }
            Console.WriteLine("\r{0} landtables found", count);
        }
Example #8
0
        static void Main(string[] args)
        {
            string[] arguments = Environment.GetCommandLineArgs();
            string   filename;
            bool     dx = true;
            string   outputfile;
            string   extension;
            string   dir = Environment.CurrentDirectory;

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

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

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

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

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

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

            case ".saanim":
                NJS_MOTION animation = NJS_MOTION.Load(filename);
                using (StreamWriter sw = File.CreateText(outputfile))
                {
                    sw.WriteLine("/* NINJA Motion");
                    sw.WriteLine(" * ");
                    sw.WriteLine(" * Generated by StructExporter");
                    sw.WriteLine(" * ");
                    sw.WriteLine(" */");
                    sw.WriteLine();
                    animation.ToStructVariables(sw);
                    break;
                }
            }
        }
Example #9
0
        private void LoadFile(string filename)
        {
            loaded              = false;
            UseWaitCursor       = true;
            Enabled             = false;
            LevelData.leveltexs = null;
            cam = new EditorCamera(EditorOptions.RenderDrawDistance);
            if (LandTable.CheckLevelFile(filename))
            {
                LevelData.geo = LandTable.LoadFromFile(filename);
            }
            else
            {
                byte[] file = File.ReadAllBytes(filename);
                if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase))
                {
                    file = FraGag.Compression.Prs.Decompress(file);
                }
                using (LevelFileDialog dlg = new LevelFileDialog())
                {
                    dlg.ShowDialog(this);
                    LevelData.geo = new LandTable(file, (int)dlg.NumericUpDown1.Value, (uint)dlg.numericUpDown2.Value, (LandTableFormat)dlg.comboBox2.SelectedIndex);
                }
            }
            LevelData.ClearLevelItems();

            for (int i = 0; i < LevelData.geo.COL.Count; i++)
            {
                LevelData.AddLevelItem((new LevelItem(LevelData.geo.COL[i], i, selectedItems)));
            }

            LevelData.TextureBitmaps = new Dictionary <string, BMPInfo[]>();
            LevelData.Textures       = new Dictionary <string, Texture[]>();
            using (OpenFileDialog a = new OpenFileDialog()
            {
                DefaultExt = "pvm", Filter = "Texture Files|*.pvm;*.gvm;*.prs"
            })
            {
                if (!string.IsNullOrEmpty(LevelData.geo.TextureFileName))
                {
                    a.FileName = LevelData.geo.TextureFileName + ".pvm";
                }
                else
                {
                    a.FileName = string.Empty;
                }
                if (a.ShowDialog(this) == DialogResult.OK)
                {
                    BMPInfo[] TexBmps = TextureArchive.GetTextures(a.FileName);
                    Texture[] texs    = new Texture[TexBmps.Length];
                    for (int j = 0; j < TexBmps.Length; j++)
                    {
                        texs[j] = TexBmps[j].Image.ToTexture(d3ddevice);
                    }
                    string texname = Path.GetFileNameWithoutExtension(a.FileName);
                    if (!LevelData.TextureBitmaps.ContainsKey(texname))
                    {
                        LevelData.TextureBitmaps.Add(texname, TexBmps);
                    }
                    if (!LevelData.Textures.ContainsKey(texname))
                    {
                        LevelData.Textures.Add(texname, texs);
                    }
                    LevelData.leveltexs = texname;
                }
            }
            loaded                           = true;
            transformGizmo                   = new TransformGizmo();
            gizmoSpaceComboBox.Enabled       = false;
            gizmoSpaceComboBox.SelectedIndex = 0;

            clearLevelToolStripMenuItem.Enabled         = LevelData.geo != null;
            calculateAllBoundsToolStripMenuItem.Enabled = LevelData.geo != null;
            statsToolStripMenuItem.Enabled  = LevelData.geo != null;
            selectedItems.SelectionChanged += SelectionChanged;
            UseWaitCursor = false;
            Enabled       = editInfoToolStripMenuItem.Enabled = saveToolStripMenuItem.Enabled = exportToolStripMenuItem.Enabled = importToolStripMenuItem.Enabled = true;

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

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

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

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

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

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

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

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

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

                    default:
                        writer.WriteLine("\t{0}{1}_exp = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Type], item.Export);
                        writer.WriteLine("\t*{0}_exp = {0};", item.Export);
                        break;
                    }
                }
                if (texlists.Count > 0 && IniData.TexLists != null && IniData.TexLists.Items != null)
                {
                    exports = new List <string>(IniData.TexLists.Where(item => texlists.Values.Contains(item.Key)).Select(item => item.Value.Export).Distinct());
                    foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key)))
                    {
                        writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key);
                    }
                    foreach (KeyValuePair <string, uint> item in texlists.Where(item => IniData.TexLists.ContainsKey(item.Value)))
                    {
                        DllTexListInfo tex = IniData.TexLists[item.Value];
                        string         str;
                        if (tex.Index.HasValue)
                        {
                            str = $"{tex.Export}[{tex.Index.Value}]";
                        }
                        else
                        {
                            str = tex.Export;
                        }
                        writer.WriteLine("\t{0}.TexList = {1};", item.Key, str);
                    }
                }
                writer.WriteLine("}");
                writer.WriteLine();
                writer.WriteLine("extern \"C\" __declspec(dllexport) const ModInfo {0}ModInfo = {{ ModLoaderVer }};", SA2 ? "SA2" : "SADX");
            }
        }
Example #11
0
        // Scans a folder for .sa1lvl, .sa1mdl and .saanim files and creates a sorted split INI file based on their labels and filenames
        static void SplitLD(string[] args)
        {
            string dirpath = args[0];

            string[] modelfiles = System.IO.Directory.GetFiles(dirpath, "*.sa1mdl", SearchOption.AllDirectories);
            string[] levelfiles = System.IO.Directory.GetFiles(dirpath, "*.sa1lvl", SearchOption.AllDirectories);
            string[] animfiles  = System.IO.Directory.GetFiles(dirpath, "*.saanim", SearchOption.AllDirectories);
            Dictionary <string, SA_Tools.FileInfo> files_ini = new Dictionary <string, SA_Tools.FileInfo>();

            for (int u = 0; u < modelfiles.Length; u++)
            {
                ModelFile         mdl      = new ModelFile(modelfiles[u]);
                SA_Tools.FileInfo fileinfo = new SA_Tools.FileInfo();
                fileinfo.Type     = "model";
                fileinfo.Filename = modelfiles[u];
                string mdlname = mdl.Model.Name;
                fileinfo.Address = int.Parse(mdlname.Substring(mdlname.Length - 8, 8), System.Globalization.NumberStyles.AllowHexSpecifier);
                //Console.WriteLine("{0}={1}", fileinfo.Address.ToString("X8"), modelfiles[u]);
                if (!files_ini.ContainsKey(mdlname))
                {
                    files_ini.Add(mdlname, fileinfo);
                }
                else
                {
                    Console.WriteLine("Duplicate of {0}", mdlname);
                }
            }
            for (int u = 0; u < levelfiles.Length; u++)
            {
                LandTable         lvl      = LandTable.LoadFromFile(levelfiles[u]);
                SA_Tools.FileInfo fileinfo = new SA_Tools.FileInfo();
                fileinfo.Type     = "landtable";
                fileinfo.Filename = levelfiles[u];
                string lvlname = lvl.Name;
                fileinfo.Address = int.Parse(lvlname.Substring(lvlname.Length - 8, 8), System.Globalization.NumberStyles.AllowHexSpecifier);
                //Console.WriteLine("{0}={1}", fileinfo.Address.ToString("X8"), levelfiles[u]);
                if (!files_ini.ContainsKey(lvlname))
                {
                    files_ini.Add(lvlname, fileinfo);
                }
                else
                {
                    Console.WriteLine("Duplicate of {0}", lvlname);
                }
            }
            for (int u = 0; u < animfiles.Length; u++)
            {
                NJS_MOTION        mtn      = NJS_MOTION.Load(animfiles[u]);
                SA_Tools.FileInfo fileinfo = new SA_Tools.FileInfo();
                fileinfo.Type     = "animation";
                fileinfo.Filename = animfiles[u];
                string mtnname = mtn.Name;
                fileinfo.Address = int.Parse(mtnname.Substring(mtnname.Length - 8, 8), System.Globalization.NumberStyles.AllowHexSpecifier);
                //Console.WriteLine("{0}={1}", fileinfo.Address.ToString("X8"), animfiles[u]);
                if (!files_ini.ContainsKey(mtnname))
                {
                    files_ini.Add(mtnname, fileinfo);
                }
                else
                {
                    Console.WriteLine("Duplicate of {0}", mtnname);
                }
            }
            IniData inidata_new = new SA_Tools.IniData();

            inidata_new.Files = SortIniData(files_ini);
            IniSerializer.Serialize(inidata_new, dirpath + "_sorted.ini");
        }
Example #12
0
        public static void ExportCPP(DllIniData IniData,
                                     Dictionary <string, bool> itemsToExport, string fileName)
        {
            using (TextWriter writer = File.CreateText(fileName))
            {
                bool            SA2      = IniData.Game == Game.SA2B;
                ModelFormat     modelfmt = SA2 ? ModelFormat.Chunk : ModelFormat.BasicDX;
                LandTableFormat landfmt  = SA2 ? LandTableFormat.SA2 : LandTableFormat.SADX;
                writer.WriteLine("// Generated by SA Tools DLL Mod Generator");
                writer.WriteLine();
                if (SA2)
                {
                    writer.WriteLine("#include \"SA2ModLoader.h\"");
                }
                else
                {
                    writer.WriteLine("#include \"SADXModLoader.h\"");
                }
                writer.WriteLine();
                List <string>             labels   = new List <string>();
                Dictionary <string, uint> texlists = new Dictionary <string, uint>();
                foreach (KeyValuePair <string, FileTypeHash> item in
                         IniData.Files.Where(i => itemsToExport[i.Key]))
                {
                    switch (item.Value.Type)
                    {
                    case "landtable":
                        LandTable tbl = LandTable.LoadFromFile(item.Key);
                        texlists.Add(tbl.Name, tbl.TextureList);
                        tbl.ToStructVariables(writer, landfmt, new List <string>());
                        labels.AddRange(tbl.GetLabels());
                        break;

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

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

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

                    case "animation":
                    case "animindex":
                        NJS_MOTION ani = NJS_MOTION.Load(item.Key);
                        ani.ToStructVariables(writer);
                        labels.Add(ani.Name);
                        break;
                    }
                    writer.WriteLine();
                }
                writer.WriteLine("extern \"C\" __declspec(dllexport) void __cdecl Init(const char *path, const HelperFunctions &helperFunctions)");
                writer.WriteLine("{");
                writer.WriteLine("\tHMODULE handle = GetModuleHandle(L\"{0}\");", IniData.Name);
                List <string> exports = new List <string>(IniData.Items.Where(item => labels.Contains(item.Label)).Select(item => item.Export).Distinct());
                foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key)))
                {
                    writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key);
                }
                foreach (DllItemInfo item in IniData.Items.Where(item => labels.Contains(item.Label)))
                {
                    writer.WriteLine("\t{0} = &{1};", item.ToString(), item.Label);
                }
                if (texlists.Count > 0 && IniData.TexLists != null && IniData.TexLists.Items != null)
                {
                    exports = new List <string>(IniData.TexLists.Where(item => texlists.Values.Contains(item.Key)).Select(item => item.Value.Export).Distinct());
                    foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key)))
                    {
                        writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key);
                    }
                    foreach (KeyValuePair <string, uint> item in texlists.Where(item => IniData.TexLists.ContainsKey(item.Value)))
                    {
                        DllTexListInfo tex = IniData.TexLists[item.Value];
                        string         str;
                        if (tex.Index.HasValue)
                        {
                            str = $"{tex.Export}[{tex.Index.Value}]";
                        }
                        else
                        {
                            str = tex.Export;
                        }
                        writer.WriteLine("\t{0}.TexList = {1};", item.Key, str);
                    }
                }
                writer.WriteLine("}");
                writer.WriteLine();
                writer.WriteLine("extern \"C\" __declspec(dllexport) const ModInfo {0}ModInfo = {{ ModLoaderVer }};", SA2 ? "SA2" : "SADX");
            }
        }
Example #13
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().Trim('"');
            }
            LandTable level = LandTable.LoadFromFile(filename);
            Dictionary <string, Attach> visitedAttaches = new Dictionary <string, Attach>();

            switch (level.Format)
            {
            case LandTableFormat.SA1:
            {
                List <COL> newcollist = new List <COL>();
                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach != null))
                {
                    if ((col.SurfaceFlags & SA1SurfaceFlags.Visible) == SA1SurfaceFlags.Visible)
                    {
                        COL newcol = new COL()
                        {
                            Bounds = col.Bounds
                        };
                        newcol.SurfaceFlags = SA1SurfaceFlags.Visible;
                        newcol.Model        = new NJS_OBJECT()
                        {
                            Name = col.Model.Name + "_cnk"
                        };
                        newcol.Model.Position = col.Model.Position;
                        newcol.Model.Rotation = col.Model.Rotation;
                        newcol.Model.Scale    = col.Model.Scale;
                        BasicAttach basatt  = (BasicAttach)col.Model.Attach;
                        string      newname = basatt.Name + "_cnk";
                        if (visitedAttaches.ContainsKey(newname))
                        {
                            newcol.Model.Attach = visitedAttaches[newname];
                        }
                        else
                        {
                            ChunkAttach cnkatt = basatt.ToChunk();
                            visitedAttaches[newname] = cnkatt;
                            newcol.Model.Attach      = cnkatt;
                        }
                        newcollist.Add(newcol);
                    }
                    if ((col.SurfaceFlags & ~SA1SurfaceFlags.Visible) != 0)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Visible;
                        newcollist.Add(col);
                    }
                }
                level.COL = newcollist;
            }
                level.Anim = new List <GeoAnimData>();
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa2lvl"), LandTableFormat.SA2);
                break;

            case LandTableFormat.SA2:
                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach is ChunkAttach))
                {
                    col.Model.Attach = col.Model.Attach.ToBasic();
                }
                level.Anim  = new List <GeoAnimData>();
                level.Flags = 8;                         // set LandTable to use PVM/GVM
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa1lvl"), LandTableFormat.SA1);
                break;
            }
        }
Example #14
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();
        }
Example #15
0
        private void button5_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog fd = new SaveFileDialog()
            {
                DefaultExt = "cpp", Filter = "C++ source files|*.cpp", InitialDirectory = Environment.CurrentDirectory, RestoreDirectory = true
            })
                if (fd.ShowDialog(this) == DialogResult.OK)
                {
                    using (TextWriter writer = File.CreateText(fd.FileName))
                    {
                        bool            SA2      = IniData.Game == Game.SA2B;
                        ModelFormat     modelfmt = SA2 ? ModelFormat.Chunk : ModelFormat.BasicDX;
                        LandTableFormat landfmt  = SA2 ? LandTableFormat.SA2 : LandTableFormat.SADX;
                        writer.WriteLine("// Generated by SA Tools DLL Mod Generator");
                        writer.WriteLine();
                        if (SA2)
                        {
                            writer.WriteLine("#include \"SA2ModLoader.h\"");
                        }
                        else
                        {
                            writer.WriteLine("#include \"SADXModLoader.h\"");
                        }
                        writer.WriteLine();
                        List <string>             labels   = new List <string>();
                        Dictionary <string, uint> texlists = new Dictionary <string, uint>();
                        foreach (KeyValuePair <string, FileTypeHash> item in IniData.Files.Where((a, i) => listView1.CheckedIndices.Contains(i)))
                        {
                            switch (item.Value.Type)
                            {
                            case "landtable":
                                LandTable tbl = LandTable.LoadFromFile(item.Key);
                                texlists.Add(tbl.Name, tbl.TextureList);
                                tbl.ToStructVariables(writer, landfmt, new List <string>());
                                labels.AddRange(tbl.GetLabels());
                                break;

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

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

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

                            case "animation":
                                Animation ani = Animation.Load(item.Key);
                                ani.ToStructVariables(writer);
                                labels.Add(ani.Name);
                                break;
                            }
                            writer.WriteLine();
                        }
                        writer.WriteLine("extern \"C\" __declspec(dllexport) void __cdecl Init(const char *path, const HelperFunctions &helperFunctions)");
                        writer.WriteLine("{");
                        writer.WriteLine("\tHMODULE handle = GetModuleHandle(L\"{0}\");", IniData.Name);
                        List <string> exports = new List <string>(IniData.Items.Where(item => labels.Contains(item.Label)).Select(item => item.Export).Distinct());
                        foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key)))
                        {
                            writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key);
                        }
                        foreach (DllItemInfo item in IniData.Items.Where(item => labels.Contains(item.Label)))
                        {
                            writer.WriteLine("\t{0} = &{1};", item.ToString(), item.Label);
                        }
                        if (texlists.Count > 0 && IniData.TexLists != null && IniData.TexLists.Items != null)
                        {
                            exports = new List <string>(IniData.TexLists.Where(item => texlists.Values.Contains(item.Key)).Select(item => item.Value.Export).Distinct());
                            foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key)))
                            {
                                writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key);
                            }
                            foreach (KeyValuePair <string, uint> item in texlists.Where(item => IniData.TexLists.ContainsKey(item.Value)))
                            {
                                DllTexListInfo tex = IniData.TexLists[item.Value];
                                string         str;
                                if (tex.Index.HasValue)
                                {
                                    str = $"{tex.Export}[{tex.Index.Value}]";
                                }
                                else
                                {
                                    str = tex.Export;
                                }
                                writer.WriteLine("\t{0}.TexList = {1};", item.Key, str);
                            }
                        }
                        writer.WriteLine("}");
                        writer.WriteLine();
                        writer.WriteLine("extern \"C\" __declspec(dllexport) const ModInfo {0}ModInfo = {{ ModLoaderVer }};", SA2 ? "SA2" : "SADX");
                    }
                }
        }
Example #16
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;
            }
        }
Example #17
0
        static void Main(string[] args)
        {
            string filename_src;
            string filename_dst;

            if (args.Length == 1)
            {
                string[] filenames = File.ReadAllLines(args[0]);
                for (int f = 0; f < filenames.Length; f++)
                {
                    Console.WriteLine("Sorting file {0}", filenames[f]);
                    ModelFile model = new ModelFile(filenames[f]);
                    SortModel(model.Model, true);
                    model.SaveToFile(filenames[f]);
                }
                return;
            }
            if (args.Length > 1)
            {
                string filename_out = "Result.ini";
                for (int a = 0; a < args.Length; a++)
                {
                    if (args[a] == "-f")
                    {
                        Console.WriteLine("Folder mode");
                        DoFolderStuff(args);
                        return;
                    }
                    if (args[a] == "-s")
                    {
                        savediff = true;
                    }
                    if (args[a] == "-a")
                    {
                        overwrite = false;
                    }
                    if (args[a] == "-o")
                    {
                        filename_out = Path.GetFullPath(args[a + 1]);
                    }
                }
                filename_src = Path.GetFullPath(args[0]);
                filename_dst = Path.GetFullPath(args[1]);
                Console.WriteLine("Source file: {0}", filename_src);
                Console.WriteLine("Destination file: {0}", filename_dst);
                if (savediff)
                {
                    Console.Write("Output file: {0}, ", filename_out);
                    Console.Write("mode: " + (overwrite ? "Overwrite" : "Append") + "\n");
                }
            }
            else
            {
                Console.WriteLine("This tool compares two levels or models and outputs a list of differences between them.\n");
                Console.WriteLine("Usage:");
                Console.WriteLine("CompareTool <file1> <file2> [-s] [-a] [-o outputfile]\n");
                Console.WriteLine("Arguments:");
                Console.WriteLine("file1: Source level or model");
                Console.WriteLine("file2: Destination level or model");
                //Console.WriteLine("-s: Save the list of differences to an INI file");
                Console.WriteLine("-a: Append to the list of differences instead of overwriting it");
                Console.WriteLine("-o: Output filename (default is Result.ini)\n");
                Console.WriteLine("Example:");
                Console.WriteLine("CompareTool Level_PC.sa1lvl Level_Gamecube.sa1lvl\n");
                Console.WriteLine("Press ENTER to exit.");
                Console.ReadLine();
                return;
            }
            if (!File.Exists(filename_src) || !File.Exists(filename_dst))
            {
                Console.WriteLine("File {0} or {1} doesn't exist.", filename_src, filename_dst);
                Console.ReadLine();
                return;
            }
            string ext = Path.GetExtension(filename_src).ToLowerInvariant();

            //biglist = new Dictionary<int, List<DiffData>>();
            switch (ext)
            {
            case ".sa1lvl":
                LandTable land_src = LandTable.LoadFromFile(filename_src);
                LandTable land_dst = LandTable.LoadFromFile(filename_dst);
                COL[]     arr_src  = land_src.COL.ToArray();
                COL[]     arr_dst  = land_dst.COL.ToArray();
                bool      same     = true;
                for (int co = 0; co < arr_src.Length; co++)
                {
                    if (same && !CompareCOL(arr_src[co], arr_dst[co]))
                    {
                        Console.WriteLine("COL order different at item {0} ({1} / {2} / {3})! Trying manual match.", co, arr_src[co].Bounds.Center.X, arr_src[co].Bounds.Center.Y, arr_src[co].Bounds.Center.Z);
                        same = false;
                    }
                }
                //Compare using identical order
                if (same)
                {
                    for (int c = 0; c < arr_src.Length; c++)
                    {
                        if (arr_src[c].Model.Attach != null)
                        {
                            CompareAttach((BasicAttach)arr_src[c].Model.Attach, (BasicAttach)arr_dst[c].Model.Attach);
                        }
                    }
                }

                //Compare using different order
                else
                {
                    if (arr_dst.Length != arr_src.Length)
                    {
                        Console.WriteLine("COL count different: {0} vs {1}", arr_src.Length, arr_dst.Length);
                    }
                    Dictionary <int, int> matches = new Dictionary <int, int>();
                    for (int c1 = 0; c1 < arr_dst.Length; c1++)
                    {
                        bool found = false;
                        for (int c2 = 0; c2 < arr_dst.Length; c2++)
                        {
                            if (arr_src[c1].Model.Attach != null && CompareCOL(arr_src[c1], arr_dst[c2], 0))
                            {
                                if (!matches.ContainsKey(c2) && !matches.ContainsValue(c1))
                                {
                                    matches.Add(c2, c1);
                                    found = true;
                                    Console.WriteLine("COL item {0} matched with {1}", c1, c2);
                                    CompareAttach((BasicAttach)arr_src[c1].Model.Attach, (BasicAttach)arr_dst[c2].Model.Attach);
                                }
                            }
                        }
                        //Try again but less strict
                        if (!found)
                        {
                            for (int c2 = 0; c2 < arr_dst.Length; c2++)
                            {
                                if (arr_src[c1].Model.Attach != null && CompareCOL(arr_src[c1], arr_dst[c2], 1))
                                {
                                    if (!matches.ContainsKey(c2) && !matches.ContainsValue(c1))
                                    {
                                        matches.Add(c2, c1);
                                        Console.WriteLine("COL item {0} partially matched with {1}", c1, c2);
                                        CompareAttach((BasicAttach)arr_src[c1].Model.Attach, (BasicAttach)arr_dst[c2].Model.Attach);
                                    }
                                }
                            }
                        }
                    }
                    Console.WriteLine("Total COL items in landtables: {0} vs {1}, matches: {2}", arr_src.Length, arr_dst.Length, matches.Count);
                }
                //if (savediff) SerializeDiffList(filename_out);
                break;

            case ".sa1mdl":
                NJS_OBJECT mdl_src = new ModelFile(filename_src).Model;
                NJS_OBJECT mdl_dst = new ModelFile(filename_dst).Model;
                CompareModel(mdl_src, mdl_dst);
                //if (savediff) SerializeDiffList(filename_out);
                break;

            default:
                break;
            }
            if (savediff)
            {
                Console.WriteLine("Total UV array differences: {0}", uvcount);
            }
        }
Example #18
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();
            }
            LandTable level = LandTable.LoadFromFile(filename);
            Dictionary <string, Attach> visitedAttaches = new Dictionary <string, Attach>();

            switch (level.Format)
            {
            case LandTableFormat.SA1:
            {
                List <COL> newcollist = new List <COL>();
                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach != null))
                {
                    if ((col.SurfaceFlags & SurfaceFlags.Visible) == SurfaceFlags.Visible)
                    {
                        COL newcol = new COL()
                        {
                            Bounds = col.Bounds
                        };
                        newcol.SurfaceFlags = SurfaceFlags.Visible;
                        newcol.Model        = new NJS_OBJECT()
                        {
                            Name = col.Model.Name + "_cnk"
                        };
                        newcol.Model.Position = col.Model.Position;
                        newcol.Model.Rotation = col.Model.Rotation;
                        newcol.Model.Scale    = col.Model.Scale;
                        BasicAttach basatt  = (BasicAttach)col.Model.Attach;
                        string      newname = basatt.Name + "_cnk";
                        if (visitedAttaches.ContainsKey(newname))
                        {
                            newcol.Model.Attach = visitedAttaches[newname];
                        }
                        else
                        {
                            ChunkAttach cnkatt = new ChunkAttach(true, true)
                            {
                                Name = basatt.Name + "_cnk", Bounds = basatt.Bounds
                            };
                            visitedAttaches[newname] = cnkatt;
                            newcol.Model.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);
                            }
                        }
                        newcollist.Add(newcol);
                    }
                    if ((col.SurfaceFlags & ~SurfaceFlags.Visible) != 0)
                    {
                        col.SurfaceFlags &= ~SurfaceFlags.Visible;
                        newcollist.Add(col);
                    }
                }
                level.COL = newcollist;
            }
                level.Anim = new List <GeoAnimData>();
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa2lvl"), LandTableFormat.SA2);
                break;

            case LandTableFormat.SA2:
                Vertex[] VertexBuffer = new Vertex[0];
                Vertex[] NormalBuffer = new Vertex[0];
                Color?[] ColorBuffer  = new Color?[0];
                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach is ChunkAttach))
                {
                    ChunkAttach cnkatt = (ChunkAttach)col.Model.Attach;
                    BasicAttach basatt = new BasicAttach()
                    {
                        Name = cnkatt.Name, Bounds = cnkatt.Bounds
                    };
                    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.Resize(ref ColorBuffer, 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);
                            if (chunk.Diffuse.Count > 0)
                            {
                                Array.Copy(chunk.Diffuse.Cast <Color?>().ToArray(), 0, ColorBuffer, chunk.IndexOffset, chunk.Diffuse.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;
                            }
                            bool hasVertVColor = false;
                            if (!hasVColor && c2.Strips.All(a => a.Indexes.All(b => ColorBuffer[b].HasValue)))
                            {
                                hasVertVColor = true;
                            }
                            List <Strip> strips  = new List <Strip>(c2.StripCount);
                            List <UV>    uvs     = hasUV ? new List <UV>() : null;
                            List <Color> vcolors = hasVColor || hasVertVColor ? 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);
                                }
                                else if (hasVertVColor)
                                {
                                    foreach (short i in strip.Indexes)
                                    {
                                        vcolors.Add(ColorBuffer[i].Value);
                                    }
                                }
                            }
                            NJS_MESHSET mesh = new NJS_MESHSET(strips.ToArray(), false, hasUV, hasVColor || hasVertVColor);
                            if (hasUV)
                            {
                                uvs.CopyTo(mesh.UV);
                            }
                            if (hasVColor || hasVertVColor)
                            {
                                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);
                            }
                        }
                    }
                    col.Model.Attach = basatt;
                }
                level.Anim  = new List <GeoAnimData>();
                level.Flags = 8;                         // set LandTable to use PVM/GVM
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa1lvl"), LandTableFormat.SA1);
                break;
            }
        }
Example #19
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();
            }
            LandTable level = LandTable.LoadFromFile(filename);

            switch (level.Format)
            {
            case LandTableFormat.SA1:
            {
                List <COL> newcollist = new List <COL>();
                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach != null))
                {
                    if ((col.SurfaceFlags & SurfaceFlags.Visible) == SurfaceFlags.Visible)
                    {
                        COL newcol = new COL()
                        {
                            Bounds = col.Bounds
                        };
                        newcol.SurfaceFlags = SurfaceFlags.Visible;
                        newcol.Model        = new SonicRetro.SAModel.NJS_OBJECT()
                        {
                            Name = col.Model.Name + "_cnk"
                        };
                        newcol.Model.Position = col.Model.Position;
                        newcol.Model.Rotation = col.Model.Rotation;
                        newcol.Model.Scale    = col.Model.Scale;
                        BasicAttach basatt = (BasicAttach)col.Model.Attach;
                        ChunkAttach cnkatt = new ChunkAttach(true, true)
                        {
                            Name = basatt.Name + "_cnk", Bounds = basatt.Bounds
                        };
                        newcol.Model.Attach = cnkatt;
                        VertexChunk vcnk;
                        if (basatt.Normal != null && basatt.Normal.Length > 0)
                        {
                            vcnk = new VertexChunk(ChunkType.Vertex_VertexNormal);
                        }
                        else
                        {
                            vcnk = new VertexChunk(ChunkType.Vertex_Vertex);
                        }
                        vcnk.Vertices = new List <Vertex>(basatt.Vertex);
                        if (basatt.Normal != null)
                        {
                            vcnk.Normals = new List <Vertex>(basatt.Normal);
                        }
                        vcnk.VertexCount = (ushort)basatt.Vertex.Length;
                        vcnk.Size        = (ushort)((vcnk.Type == ChunkType.Vertex_VertexNormal ? vcnk.VertexCount * 6 : vcnk.VertexCount * 3) + 1);
                        cnkatt.Vertex.Add(vcnk);
                        foreach (NJS_MESHSET mesh in basatt.Mesh)
                        {
                            if (mesh.PolyType != Basic_PolyType.Strips)
                            {
                                Console.WriteLine("Warning: Skipping non-strip mesh in {0} ({1}).", basatt.MeshName, mesh.PolyType);
                                continue;
                            }
                            NJS_MATERIAL mat = null;
                            if (basatt.Material != null && mesh.MaterialID < basatt.Material.Count)
                            {
                                mat = basatt.Material[mesh.MaterialID];
                                cnkatt.Poly.Add(new PolyChunkBitsBlendAlpha()
                                    {
                                        SourceAlpha      = mat.SourceAlpha,
                                        DestinationAlpha = mat.DestinationAlpha
                                    });
                                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()
                                    {
                                        Diffuse          = mat.DiffuseColor,
                                        Specular         = mat.SpecularColor,
                                        SpecularExponent = (byte)mat.Exponent
                                    });
                            }
                            PolyChunkStrip strip;
                            if (mesh.UV != null & mesh.VColor != null)
                            {
                                strip = new PolyChunkStrip(ChunkType.Strip_StripUVNColor);
                            }
                            else if (mesh.UV != null)
                            {
                                strip = new PolyChunkStrip(ChunkType.Strip_StripUVN);
                            }
                            else if (mesh.VColor != null)
                            {
                                strip = new PolyChunkStrip(ChunkType.Strip_StripColor);
                            }
                            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;
                            }
                            int striptotal = 0;
                            foreach (Strip item in mesh.Poly.Cast <Strip>())
                            {
                                UV[] uvs = null;
                                if (mesh.UV != null)
                                {
                                    uvs = new UV[item.Indexes.Length];
                                    Array.Copy(mesh.UV, striptotal, uvs, 0, item.Indexes.Length);
                                }
                                Color[] vcolors = null;
                                if (mesh.VColor != null)
                                {
                                    vcolors = new Color[item.Indexes.Length];
                                    Array.Copy(mesh.VColor, striptotal, vcolors, 0, item.Indexes.Length);
                                }
                                strip.Strips.Add(new PolyChunkStrip.Strip(item.Reversed, item.Indexes, uvs, vcolors));
                                striptotal += item.Indexes.Length;
                            }
                            cnkatt.Poly.Add(strip);
                        }
                        newcollist.Add(newcol);
                    }
                    if ((col.SurfaceFlags & ~SurfaceFlags.Visible) != 0)
                    {
                        col.SurfaceFlags &= ~SurfaceFlags.Visible;
                        newcollist.Add(col);
                    }
                }
                level.COL = newcollist;
            }
                level.Anim = new List <GeoAnimData>();
                level.Tool = "SA Tools Level Converter";
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa2lvl"), LandTableFormat.SA2);
                break;

            case LandTableFormat.SA2:
                Vertex[] VertexBuffer = new Vertex[0];
                Vertex[] NormalBuffer = new Vertex[0];
                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach is ChunkAttach))
                {
                    ChunkAttach cnkatt = (ChunkAttach)col.Model.Attach;
                    BasicAttach basatt = new BasicAttach()
                    {
                        Name = cnkatt.Name, Bounds = cnkatt.Bounds
                    };
                    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;
                            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(strip.Indexes, 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);
                            }
                        }
                    }
                    col.Model.Attach = basatt;
                }
                level.Anim = new List <GeoAnimData>();
                level.Tool = "SA Tools Level Converter";
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa1lvl"), LandTableFormat.SA1);
                break;
            }
        }
Example #20
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;
            }
        }
Example #21
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().Trim('"');
            }
            LandTable level = LandTable.LoadFromFile(filename);
            Dictionary <string, Attach> visitedAttaches = new Dictionary <string, Attach>();

            switch (level.Format)
            {
            case LandTableFormat.SA1:
            {
                List <COL> newcollist = new List <COL>();
                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach != null))
                {
                    //fix flags differences
                    if ((col.SurfaceFlags & SA1SurfaceFlags.UseSkyDrawDistance) == SA1SurfaceFlags.UseSkyDrawDistance)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.UseSkyDrawDistance;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Diggable) == SA1SurfaceFlags.Diggable)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Diggable;
                        col.SurfaceFlags |= (SA1SurfaceFlags)SA2SurfaceFlags.Diggable;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Stairs) == SA1SurfaceFlags.Stairs)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Stairs;
                        col.SurfaceFlags |= (SA1SurfaceFlags)SA2SurfaceFlags.Stairs;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Waterfall) == SA1SurfaceFlags.Waterfall)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Waterfall;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Hurt) == SA1SurfaceFlags.Hurt)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Hurt;
                        col.SurfaceFlags |= (SA1SurfaceFlags)SA2SurfaceFlags.Hurt;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Unclimbable) == SA1SurfaceFlags.Unclimbable)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Unclimbable;
                        col.SurfaceFlags |= (SA1SurfaceFlags)SA2SurfaceFlags.Unclimbable;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.CannotLand) == SA1SurfaceFlags.CannotLand)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.CannotLand;
                        col.SurfaceFlags |= (SA1SurfaceFlags)SA2SurfaceFlags.CannotLand;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.LowDepth) == SA1SurfaceFlags.LowDepth)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.LowDepth;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Visible) == SA1SurfaceFlags.Visible)
                    {
                        COL newcol = new COL()
                        {
                            Bounds = col.Bounds
                        };
                        newcol.SurfaceFlags = SA1SurfaceFlags.Visible;
                        newcol.Model        = new NJS_OBJECT()
                        {
                            Name = col.Model.Name + "_cnk"
                        };
                        newcol.Model.Position = col.Model.Position;
                        newcol.Model.Rotation = col.Model.Rotation;
                        newcol.Model.Scale    = col.Model.Scale;
                        BasicAttach basatt  = (BasicAttach)col.Model.Attach;
                        string      newname = basatt.Name + "_cnk";
                        if (visitedAttaches.ContainsKey(newname))
                        {
                            newcol.Model.Attach = visitedAttaches[newname];
                        }
                        else
                        {
                            ChunkAttach cnkatt = basatt.ToChunk();
                            visitedAttaches[newname] = cnkatt;
                            newcol.Model.Attach      = cnkatt;
                        }
                        newcollist.Add(newcol);
                    }
                    if ((col.SurfaceFlags & ~SA1SurfaceFlags.Visible) != 0)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Visible;
                        newcollist.Add(col);
                    }
                }
                level.COL = newcollist;
            }
                level.Anim = new List <GeoAnimData>();
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa2lvl"), LandTableFormat.SA2);
                break;

            case LandTableFormat.SA2:

                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach is ChunkAttach))
                {
                    col.Model.Attach = col.Model.Attach.ToBasic();
                }

                foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach != null))
                {
                    //fix flags differences
                    if ((col.SurfaceFlags & SA1SurfaceFlags.Diggable) == SA1SurfaceFlags.Diggable)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Diggable;
                        col.SurfaceFlags |= SA1SurfaceFlags.Stairs;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.UseSkyDrawDistance) == SA1SurfaceFlags.UseSkyDrawDistance)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.UseSkyDrawDistance;
                        col.SurfaceFlags |= SA1SurfaceFlags.Diggable;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Unclimbable) == SA1SurfaceFlags.Unclimbable)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Unclimbable;
                        col.SurfaceFlags |= SA1SurfaceFlags.CannotLand;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.IncreasedAcceleration) == SA1SurfaceFlags.IncreasedAcceleration)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.IncreasedAcceleration;
                        col.SurfaceFlags |= SA1SurfaceFlags.Unclimbable;
                    }

                    if ((col.SurfaceFlags & SA1SurfaceFlags.Waterfall) == SA1SurfaceFlags.Waterfall)
                    {
                        col.SurfaceFlags &= ~SA1SurfaceFlags.Waterfall;
                        col.SurfaceFlags |= SA1SurfaceFlags.Hurt;
                    }
                }

                level.Anim       = new List <GeoAnimData>();
                level.Attributes = SA1LandtableAttributes.LoadTextureFile;                         // set LandTable to use PVM/GVM
                level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa1lvl"), LandTableFormat.SA1);
                break;
            }
        }
Example #22
0
        /// <summary>
        /// Exports a single level, model or animation file as text.
        /// </summary>
        /// <param name="source">Source pathname.</param>
        /// <param name="type">Type of text conversion.</param>
        /// <param name="destination">Destination pathname. Leave blank to export in the same folder with a swapped extension.</param>
        /// <param name="basicDX">Use the SADX2004 format for Basic models.</param>
        public static void ConvertFileToText(string source, TextType type, string destination = "", bool basicDX = true, bool overwrite = true)
        {
            string outext    = ".c";
            string extension = Path.GetExtension(source);

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

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

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

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

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

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

            case ".saanim":
                NJS_MOTION animation = NJS_MOTION.Load(source);
                if (type == TextType.CStructs)
                {
                    outext = ".c";
                    if (destination == "")
                    {
                        destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext);
                    }
                    if (!overwrite && File.Exists(destination))
                    {
                        while (File.Exists(destination))
                        {
                            destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext);
                        }
                    }
                    using (StreamWriter sw = File.CreateText(destination))
                    {
                        sw.WriteLine("/* NINJA Motion");
                        sw.WriteLine(" * ");
                        sw.WriteLine(" * Generated by DataToolbox");
                        sw.WriteLine(" * ");
                        sw.WriteLine(" */");
                        sw.WriteLine();
                        animation.ToStructVariables(sw);
                    }
                }
                else if (type == TextType.JSON)
                {
                    outext = ".json";
                    if (destination == "")
                    {
                        destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext);
                    }
                    if (!overwrite && File.Exists(destination))
                    {
                        while (File.Exists(destination))
                        {
                            destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext);
                        }
                    }
                    JsonSerializer js = new JsonSerializer()
                    {
                        Culture = System.Globalization.CultureInfo.InvariantCulture
                    };
                    using (TextWriter tw = File.CreateText(destination))
                        using (JsonTextWriter jtw = new JsonTextWriter(tw)
                        {
                            Formatting = Formatting.Indented
                        })
                            js.Serialize(jtw, animation);
                }
                break;
            }
        }
Example #23
0
        static void CleanUpLandtable()
        {
            bool delete_basic = false;
            bool delete_chunk = false;
            bool delete_gc    = false;

            ByteConverter.BigEndian = BigEndian;
            string          model_extension = ".sa1mdl";
            string          model_dir       = "basicmodels";
            LandTableFormat landfmt         = LandTableFormat.SA1;

            foreach (uint landaddr in landtablelist)
            {
                switch (addresslist[landaddr])
                {
                case "landtable_SA1":
                    landfmt      = LandTableFormat.SA1;
                    delete_basic = true;
                    break;

                case "landtable_SADX":
                    landfmt      = LandTableFormat.SADX;
                    delete_basic = true;
                    break;

                case "landtable_SA2":
                    landfmt      = LandTableFormat.SA2;
                    delete_basic = true;
                    delete_chunk = true;
                    break;

                case "landtable_SA2B":
                    landfmt      = LandTableFormat.SA2B;
                    delete_basic = true;
                    delete_gc    = true;
                    delete_chunk = true;
                    break;
                }
                //Console.WriteLine("Landtable {0}, {1}, {2}", landaddr.ToString("X"), ImageBase.ToString("X"), landfmt.ToString());
                LandTable land    = new LandTable(datafile, (int)landaddr, ImageBase, landfmt);
                string    landdir = Path.Combine(OutputFolder, "levels", land.Name);
                Directory.CreateDirectory(landdir);
                if (land.COL.Count > 0)
                {
                    foreach (COL col in land.COL)
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            if (i == 0 && delete_basic)
                            {
                                model_dir       = "basicmodels";
                                model_extension = ".sa1mdl";
                            }
                            else if (i == 1 && delete_chunk)
                            {
                                model_dir       = "chunkmodels";
                                model_extension = ".sa2mdl";
                            }
                            else if (i == 2 && delete_gc)
                            {
                                model_dir       = "gcmodels";
                                model_extension = ".sa2bmdl";
                            }
                            string col_filename = Path.Combine(OutputFolder, model_dir, col.Model.Name.Substring(7, col.Model.Name.Length - 7) + model_extension);
                            if (SingleOutputFolder)
                            {
                                col_filename = Path.Combine(OutputFolder, col.Model.Name.Substring(7, col.Model.Name.Length - 7) + model_extension);
                            }
                            if (File.Exists(col_filename))
                            {
                                uint itemaddr = uint.Parse(col.Model.Name.Substring(7, col.Model.Name.Length - 7), NumberStyles.AllowHexSpecifier);
                                File.Move(col_filename, Path.Combine(landdir, Path.GetFileName(col_filename)));
                                Console.WriteLine("Moving landtable object {0}", Path.GetFileName(col_filename));
                                if (!KeepLandtableModels)
                                {
                                    if (addresslist.ContainsKey(itemaddr))
                                    {
                                        addresslist.Remove(itemaddr);
                                    }
                                }
                            }
                        }
                    }
                    foreach (GeoAnimData anim in land.Anim)
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            if (i == 0 && delete_basic)
                            {
                                model_dir       = "basicmodels";
                                model_extension = ".sa1mdl";
                            }
                            else if (i == 1 && delete_chunk)
                            {
                                model_dir       = "chunkmodels";
                                model_extension = ".sa2mdl";
                            }
                            else if (i == 2 && delete_gc)
                            {
                                model_dir       = "gcmodels";
                                model_extension = ".sa2bmdl";
                            }
                            string anim_filename = Path.Combine(OutputFolder, model_dir, anim.Model.Name.Substring(7, anim.Model.Name.Length - 7) + model_extension);
                            if (SingleOutputFolder)
                            {
                                anim_filename = Path.Combine(OutputFolder, anim.Model.Name.Substring(7, anim.Model.Name.Length - 7) + model_extension);
                            }
                            if (File.Exists(anim_filename))
                            {
                                uint itemaddr = uint.Parse(anim.Model.Name.Substring(7, anim.Model.Name.Length - 7), NumberStyles.AllowHexSpecifier);
                                Console.WriteLine("Moving landtable GeoAnim object {0}", anim_filename);
                                File.Move(anim_filename, Path.Combine(landdir, Path.GetFileName(anim_filename)));
                                if (File.Exists(Path.ChangeExtension(anim_filename, ".action")))
                                {
                                    File.Move(Path.ChangeExtension(anim_filename, ".action"), Path.Combine(landdir, Path.GetFileNameWithoutExtension(anim_filename) + ".action"));
                                    // TODO: Insert a reference to the parent folder in the action file so that the animation runs correctly
                                }
                                if (!KeepLandtableModels)
                                {
                                    if (addresslist.ContainsKey(itemaddr))
                                    {
                                        addresslist.Remove(itemaddr);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }