Example #1
0
 static void Main(string[] args)
 {
     Queue<string> argq = new Queue<string>(args);
     string inifilename;
     if (argq.Count > 0)
     {
         inifilename = argq.Dequeue();
         Console.WriteLine("INI File: {0}", inifilename);
     }
     else
     {
         Console.Write("INI File: ");
         inifilename = Console.ReadLine();
     }
     Dictionary<int, string> modelnames = IniSerializer.Deserialize<Dictionary<int, string>>(inifilename, inisettings);
     string mdlfilename;
     if (argq.Count > 0)
     {
         mdlfilename = argq.Dequeue();
         Console.WriteLine("Model File: {0}", mdlfilename);
     }
     else
     {
         Console.Write("Model File: ");
         mdlfilename = Console.ReadLine();
     }
     ModelFile model = new ModelFile(mdlfilename);
     NJS_OBJECT[] objects = model.Model.GetObjects();
     string repmdlfilename;
     if (argq.Count > 0)
     {
         repmdlfilename = argq.Dequeue();
         Console.WriteLine("Replacement Model File: {0}", repmdlfilename);
     }
     else
     {
         Console.Write("Replacement Model File: ");
         repmdlfilename = Console.ReadLine();
     }
     ModelFile repmodel = new ModelFile(repmdlfilename);
     NJS_OBJECT[] repobjects = repmodel.Model.GetObjects();
     if (objects.Length != repobjects.Length)
         Console.WriteLine("Models have different structures, the game may crash.");
     foreach (KeyValuePair<int, string> item in modelnames.ToList())
         if (objects.Any((obj) => obj.Name == item.Value))
             modelnames[item.Key] = repobjects[Array.IndexOf(objects, objects.Single((o) => o.Name == item.Value))].Name;
     ModelFile.CreateFile(mdlfilename, repmodel.Model, null, null, repmodel.Author, repmodel.Description, "replaceMDL", repmodel.Metadata, repmodel.Format);
     IniSerializer.Serialize(modelnames, inisettings, inifilename);
 }
Example #2
0
 private List<string> ExportCPP(TextWriter writer, bool SA2)
 {
     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>();
     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);
                 writer.WriteLine(tbl.ToStructVariables(landfmt, new List<string>()));
                 labels.AddRange(tbl.GetLabels());
                 break;
             case "model":
                 SonicRetro.SAModel.NJS_OBJECT mdl = new ModelFile(item.Key).Model;
                 writer.WriteLine(mdl.ToStructVariables(modelfmt == ModelFormat.BasicDX, new List<string>()));
                 labels.AddRange(mdl.GetLabels());
                 break;
             case "basicmodel":
             case "chunkmodel":
                 mdl = new ModelFile(item.Key).Model;
                 writer.WriteLine(mdl.ToStructVariables(false, new List<string>()));
                 labels.AddRange(mdl.GetLabels());
                 break;
             case "basicdxmodel":
                 mdl = new ModelFile(item.Key).Model;
                 writer.WriteLine(mdl.ToStructVariables(true, new List<string>()));
                 labels.AddRange(mdl.GetLabels());
                 break;
             case "animation":
                 Animation ani = Animation.Load(item.Key);
                 writer.WriteLine(ani.ToStructVariables());
                 labels.Add(ani.Name);
                 break;
         }
     return labels;
 }
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();
             output.Name = IniData.Name;
             output.Game = IniData.Game;
             output.Exports = IniData.Exports;
             output.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":
                         SonicRetro.SAModel.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
 static void Main(string[] args)
 {
     Queue<string> argq = new Queue<string>(args);
     string mdlfilename;
     if (argq.Count > 0)
     {
         mdlfilename = argq.Dequeue();
         Console.WriteLine("Model File: {0}", mdlfilename);
     }
     else
     {
         Console.Write("Model File: ");
         mdlfilename = Console.ReadLine();
     }
     ModelFile model = new ModelFile(mdlfilename);
     NJS_OBJECT[] objects = model.Model.GetObjects();
     string repmdlfilename;
     if (argq.Count > 0)
     {
         repmdlfilename = argq.Dequeue();
         Console.WriteLine("Replacement Model File: {0}", repmdlfilename);
     }
     else
     {
         Console.Write("Replacement Model File: ");
         repmdlfilename = Console.ReadLine();
     }
     ModelFile repmodel = new ModelFile(repmdlfilename);
     NJS_OBJECT[] repobjects = repmodel.Model.GetObjects();
     if (model.Format != repmodel.Format)
         Console.WriteLine("Format mismatch between files! Most data will be unable to be relabeled.");
     if (objects.Length != repobjects.Length)
         Console.WriteLine("Models have different structures, the game may crash.");
     for (int i = 0; i < Math.Min(objects.Length, repobjects.Length); i++)
     {
         objects[i].Name = repobjects[i].Name;
         if (objects[i].Attach != null && repobjects[i].Attach != null)
         {
             objects[i].Attach.Name = repobjects[i].Attach.Name;
             if (objects[i].Attach is BasicAttach && repobjects[i].Attach is BasicAttach)
             {
                 BasicAttach attach = (BasicAttach)objects[i].Attach;
                 BasicAttach repattach = (BasicAttach)repobjects[i].Attach;
                 attach.VertexName = repattach.VertexName;
                 attach.NormalName = repattach.NormalName;
                 attach.MaterialName = repattach.MaterialName;
                 attach.MeshName = repattach.MeshName;
                 for (int j = 0; j < Math.Min(attach.Mesh.Count, repattach.Mesh.Count); j++)
                 {
                     attach.Mesh[j].PolyName = repattach.Mesh[j].PolyName;
                     attach.Mesh[j].PolyNormalName = repattach.Mesh[j].PolyNormalName;
                     attach.Mesh[j].UVName = repattach.Mesh[j].UVName;
                     attach.Mesh[j].VColorName = repattach.Mesh[j].VColorName;
                 }
             }
         }
         else if (objects[i].Attach is ChunkAttach && repobjects[i].Attach is ChunkAttach)
         {
             ChunkAttach attach = (ChunkAttach)objects[i].Attach;
             ChunkAttach repattach = (ChunkAttach)objects[i].Attach;
             attach.VertexName = repattach.VertexName;
             attach.PolyName = repattach.PolyName;
         }
     }
     model.SaveToFile(mdlfilename);
 }
Example #5
0
 private void ExportCPP(TextWriter writer, bool SA2)
 {
     Dictionary<uint, string> pointers = new Dictionary<uint, string>();
     uint imagebase = IniData.ImageBase ?? 0x400000;
     ModelFormat modelfmt = 0;
     LandTableFormat landfmt = 0;
     switch (IniData.Game)
     {
         case Game.SA1:
             modelfmt = ModelFormat.Basic;
             landfmt = LandTableFormat.SA1;
             break;
         case Game.SADX:
             modelfmt = ModelFormat.BasicDX;
             landfmt = LandTableFormat.SADX;
             break;
         case Game.SA2:
         case Game.SA2B:
             modelfmt = ModelFormat.Chunk;
             landfmt = LandTableFormat.SA2;
             break;
     }
     writer.WriteLine("// Generated by SA Tools Struct Converter");
     writer.WriteLine();
     if (SA2)
         writer.WriteLine("#include \"SA2ModLoader.h\"");
     else
         writer.WriteLine("#include \"SADXModLoader.h\"");
     writer.WriteLine();
     Dictionary<string, string> models = new Dictionary<string, string>();
     foreach (KeyValuePair<string, SA_Tools.FileInfo> item in IniData.Files.Where((a, i) => listView1.CheckedIndices.Contains(i)))
     {
         string name = item.Key.MakeIdentifier();
         SA_Tools.FileInfo data = item.Value;
         switch (data.Type)
         {
             case "landtable":
                 LandTable tbl = LandTable.LoadFromFile(data.Filename);
                 name = tbl.Name;
                 writer.WriteLine(tbl.ToStructVariables(landfmt, new List<string>()));
                 break;
             case "model":
                 SonicRetro.SAModel.NJS_OBJECT mdl = new ModelFile(data.Filename).Model;
                 name = mdl.Name;
                 writer.WriteLine(mdl.ToStructVariables(modelfmt == ModelFormat.BasicDX, new List<string>()));
                 models.Add(item.Key, mdl.Name);
                 break;
             case "basicmodel":
                 mdl = new SonicRetro.SAModel.ModelFile(data.Filename).Model;
                 name = mdl.Name;
                 writer.WriteLine(mdl.ToStructVariables(false, new List<string>()));
                 models.Add(item.Key, mdl.Name);
                 break;
             case "basicdxmodel":
                 mdl = new SonicRetro.SAModel.ModelFile(data.Filename).Model;
                 name = mdl.Name;
                 writer.WriteLine(mdl.ToStructVariables(true, new List<string>()));
                 models.Add(item.Key, mdl.Name);
                 break;
             case "chunkmodel":
                 mdl = new SonicRetro.SAModel.ModelFile(data.Filename).Model;
                 name = mdl.Name;
                 writer.WriteLine(mdl.ToStructVariables(false, new List<string>()));
                 models.Add(item.Key, mdl.Name);
                 break;
             case "action":
                 Animation ani = Animation.Load(data.Filename);
                 name = "action_" + ani.Name.MakeIdentifier();
                 writer.WriteLine(ani.ToStructVariables());
                 writer.WriteLine("NJS_ACTION {0} = {{ &{1}, &{2} }};", name, models[data.CustomProperties["model"]], ani.Name.MakeIdentifier());
                 break;
             case "animation":
                 ani = Animation.Load(data.Filename);
                 name = ani.Name.MakeIdentifier();
                 writer.WriteLine(ani.ToStructVariables());
                 break;
             case "objlist":
                 {
                     ObjectListEntry[] list = ObjectList.Load(data.Filename, SA2);
                     writer.WriteLine("ObjectListEntry {0}_list[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (ObjectListEntry obj in list)
                         objs.Add(obj.ToStruct() + " " + obj.Name.ToComment());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                     writer.WriteLine();
                     writer.WriteLine("ObjectList {0} = {{ arraylengthandptr({0}_list) }};", name);
                 }
                 break;
             case "startpos":
                 if (SA2)
                 {
                     Dictionary<SA2LevelIDs, SA2StartPosInfo> list = SA2StartPosList.Load(data.Filename);
                     writer.WriteLine("StartPosition {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Count + 1);
                     foreach (KeyValuePair<SA2LevelIDs, SA2StartPosInfo> obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ LevelIDs_Invalid }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 else
                 {
                     Dictionary<SA1LevelAct, SA1StartPosInfo> list = SA1StartPosList.Load(data.Filename);
                     writer.WriteLine("StartPosition {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Count + 1);
                     foreach (KeyValuePair<SA1LevelAct, SA1StartPosInfo> obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ LevelIDs_Invalid }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "texlist":
                 {
                     TextureListEntry[] list = TextureList.Load(data.Filename);
                     writer.WriteLine("PVMEntry {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Length + 1);
                     foreach (TextureListEntry obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ 0 }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "leveltexlist":
                 {
                     LevelTextureList list = LevelTextureList.Load(data.Filename);
                     writer.WriteLine("PVMEntry {0}_list[] = {{", name);
                     List<string> objs = new List<string>(list.TextureList.Length);
                     foreach (TextureListEntry obj in list.TextureList)
                         objs.Add(obj.ToStruct());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                     writer.WriteLine();
                     writer.WriteLine("LevelPVMList {0} = {{ {1}, arraylengthandptr({0}_list) }};", name, list.Level.ToC());
                 }
                 break;
             case "triallevellist":
                 {
                     SA1LevelAct[] list = TrialLevelList.Load(data.Filename);
                     writer.WriteLine("TrialLevelListEntry {0}_list[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (SA1LevelAct obj in list)
                         objs.Add(string.Format("{{ {0}, {1} }}", obj.Level.ToC("LevelIDs"), obj.Act));
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                     writer.WriteLine();
                     writer.WriteLine("TrialLevelList {0} = {{ arrayptrandlength({0}_list) }};", name);
                 }
                 break;
             case "bosslevellist":
                 {
                     SA1LevelAct[] list = BossLevelList.Load(data.Filename);
                     writer.WriteLine("__int16 {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Length + 1);
                     foreach (SA1LevelAct obj in list)
                         objs.Add(obj.ToC());
                     objs.Add("levelact(LevelIDs_Invalid, 0)");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "fieldstartpos":
                 {
                     Dictionary<SA1LevelIDs, FieldStartPosInfo> list = FieldStartPosList.Load(data.Filename);
                     writer.WriteLine("FieldStartPosition {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Count + 1);
                     foreach (KeyValuePair<SA1LevelIDs, FieldStartPosInfo> obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ LevelIDs_Invalid }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "soundtestlist":
                 {
                     SoundTestListEntry[] list = SoundTestList.Load(data.Filename);
                     writer.WriteLine("SoundTestEntry {0}_list[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (SoundTestListEntry obj in list)
                         objs.Add(obj.ToStruct() + " " + obj.Title.ToComment());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                     writer.WriteLine();
                     writer.WriteLine("SoundTestCategory {0} = {{ arrayptrandlength({0}_list) }};", name);
                 }
                 break;
             case "musiclist":
                 {
                     MusicListEntry[] list = MusicList.Load(data.Filename);
                     writer.WriteLine("MusicInfo {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (MusicListEntry obj in list)
                         objs.Add(obj.ToStruct());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "soundlist":
                 {
                     SoundListEntry[] list = SoundList.Load(data.Filename);
                     writer.WriteLine("SoundFileInfo {0}_list[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (SoundListEntry obj in list)
                         objs.Add(obj.ToStruct());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                     writer.WriteLine();
                     writer.WriteLine("SoundList {0} = {{ arraylengthandptr({0}_list) }};", name);
                 }
                 break;
             case "stringarray":
                 {
                     string[] strs = StringArray.Load(data.Filename);
                     Languages lang = Languages.Japanese;
                     if (data.CustomProperties.ContainsKey("language"))
                         lang = (Languages)Enum.Parse(typeof(Languages), data.CustomProperties["language"], true);
                     writer.WriteLine("char *{0}[] = {{", name);
                     List<string> objs = new List<string>(strs.Length);
                     foreach (string obj in strs)
                         objs.Add(obj.ToC(lang) + " " + obj.ToComment());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "nextlevellist":
                 {
                     NextLevelListEntry[] list = NextLevelList.Load(data.Filename);
                     writer.WriteLine("NextLevelData {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Length + 1);
                     foreach (NextLevelListEntry obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ 0, -1 }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "cutscenetext":
                 {
                     CutsceneText texts = new CutsceneText(data.Filename);
                     uint addr = (uint)(data.Address + imagebase);
                     for (int j = 0; j < 5; j++)
                     {
                         string[] strs = texts.Text[j];
                         Languages lang = (Languages)j;
                         writer.WriteLine("char *{0}_{1}[] = {{", name, lang);
                         writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", strs.Select((a) => a.ToC(lang) + " " + a.ToComment()).ToArray()));
                         writer.WriteLine("};");
                         writer.WriteLine();
                         pointers.Add(addr, string.Format("{0}_{1}", name, lang));
                         addr += 4;
                     }
                 }
                 break;
             case "recapscreen":
                 {
                     uint addr = (uint)(data.Address + imagebase);
                     RecapScreen[][] texts = RecapScreenList.Load(data.Filename, int.Parse(data.CustomProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo));
                     for (int l = 0; l < 5; l++)
                         for (int j = 0; j < texts.Length; j++)
                         {
                             writer.WriteLine("char *{0}_{1}_{2}_Text[] = {{", name, (Languages)l, j);
                             writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", texts[j][l].Text.Split('\n').Select((a) => a.ToC((Languages)l) + " " + a.ToComment()).ToArray()));
                             writer.WriteLine("};");
                             writer.WriteLine();
                         }
                     for (int l = 0; l < 5; l++)
                     {
                         writer.WriteLine("RecapScreen {0}_{1}[] = {{", name, (Languages)l);
                         List<string> objs = new List<string>(texts.Length);
                         for (int j = 0; j < texts.Length; j++)
                         {
                             RecapScreen scr = texts[j][l];
                             objs.Add(string.Format("{{ {0}, arraylengthandptr({1}_{2}_{3}_Text) }}",
                                 SA_Tools.HelperFunctions.ToC(scr.Speed), name, (Languages)l, j));
                         }
                         writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                         writer.WriteLine("};");
                         writer.WriteLine();
                         pointers.Add(addr, string.Format("{0}_{1}", name, (Languages)l));
                         addr += 4;
                     }
                 }
                 break;
             case "npctext":
                 {
                     NPCText[][] texts = NPCTextList.Load(data.Filename, int.Parse(data.CustomProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo));
                     uint headaddr = (uint)(data.Address + imagebase);
                     for (int l = 0; l < 5; l++)
                     {
                         for (int j = 0; j < texts[l].Length; j++)
                         {
                             if (texts[l][j].Groups.Count == 0)
                                 continue;
                             if (texts[l][j].HasControl)
                             {
                                 writer.WriteLine("__int16 {0}_{1}_{2}_Control[] = {{", name, (Languages)l, j);
                                 bool first = true;
                                 List<string> objs = new List<string>();
                                 foreach (NPCTextGroup group in texts[l][j].Groups)
                                 {
                                     if (!first)
                                         objs.Add(NPCTextControl.NewGroup.ToC());
                                     else
                                         first = false;
                                     foreach (ushort flag in group.EventFlags)
                                     {
                                         objs.Add(NPCTextControl.EventFlag.ToC());
                                         objs.Add(flag.ToCHex());
                                     }
                                     foreach (ushort flag in group.NPCFlags)
                                     {
                                         objs.Add(NPCTextControl.NPCFlag.ToC());
                                         objs.Add(flag.ToCHex());
                                     }
                                     if (group.Character != (SA1CharacterFlags)0xFF)
                                     {
                                         objs.Add(NPCTextControl.Character.ToC());
                                         objs.Add(group.Character.ToC("CharacterFlags"));
                                     }
                                     if (group.Voice.HasValue)
                                     {
                                         objs.Add(NPCTextControl.Voice.ToC());
                                         objs.Add(group.Voice.Value.ToString());
                                     }
                                     if (group.SetEventFlag.HasValue)
                                     {
                                         objs.Add(NPCTextControl.SetEventFlag.ToC());
                                         objs.Add(group.SetEventFlag.Value.ToCHex());
                                     }
                                 }
                                 objs.Add(NPCTextControl.End.ToC());
                                 writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                                 writer.WriteLine("};");
                                 writer.WriteLine();
                             }
                             if (texts[l][j].HasText)
                             {
                                 writer.WriteLine("HintText_Text {0}_{1}_{2}_Text[] = {{", name, (Languages)l, j);
                                 List<string> objs = new List<string>();
                                 foreach (NPCTextGroup group in texts[l][j].Groups)
                                 {
                                     foreach (NPCTextLine line in group.Lines)
                                         objs.Add(line.ToStruct((Languages)l) + " " + line.Line.ToComment());
                                     objs.Add("{ 0 }");
                                 }
                                 writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                                 writer.WriteLine("};");
                                 writer.WriteLine();
                             }
                         }
                     }
                     for (int l = 0; l < 5; l++)
                     {
                         if (l > 0)
                             writer.WriteLine();
                         writer.WriteLine("HintText_Entry {0}_{1}[] = {{", name, (Languages)l);
                         List<string> objs = new List<string>();
                         for (int j = 0; j < texts[l].Length; j++)
                         {
                             if (texts[l][j].Groups.Count == 0)
                             {
                                 objs.Add("{ 0 }");
                                 continue;
                             }
                             StringBuilder line = new StringBuilder("{ ");
                             if (texts[l][j].HasControl)
                                 line.AppendFormat("{0}_{1}_{2}_Control", name, (Languages)l, j);
                             else
                                 line.Append("NULL");
                             line.Append(", ");
                             if (texts[l][j].HasText)
                                 line.AppendFormat("{0}_{1}_{2}_Text", name, (Languages)l, j);
                             else
                                 line.Append("NULL");
                             line.Append(" }");
                             objs.Add(line.ToString());
                         }
                         writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                         writer.WriteLine("};");
                         pointers.Add(headaddr, string.Format("{0}_{1}", name, (Languages)l));
                         headaddr += 4;
                     }
                 }
                 break;
             case "levelclearflags":
                 {
                     LevelClearFlag[] list = LevelClearFlagList.Load(data.Filename);
                     writer.WriteLine("LevelClearFlagData {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (LevelClearFlag obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ -1 }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "deathzone":
                 {
                     DeathZoneFlags[] list = DeathZoneFlagsList.Load(data.Filename);
                     string path = Path.GetDirectoryName(data.Filename);
                     List<string> mdls = new List<string>(list.Length);
                     List<string> objs = new List<string>();
                     for (int j = 0; j < list.Length; j++)
                     {
                         SonicRetro.SAModel.NJS_OBJECT obj = new ModelFile(Path.Combine(path,
                             j.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl")).Model;
                         writer.WriteLine(obj.ToStructVariables(modelfmt == ModelFormat.BasicDX, objs));
                         mdls.Add(obj.Name);
                         objs.Clear();
                     }
                     writer.WriteLine("DeathZone {0}[] = {{", name);
                     for (int j = 0; j < list.Length; j++)
                         objs.Add(string.Format("{{ {0}, &{1} }}", list[j].Flags.ToC("CharacterFlags"), mdls[j]));
                     objs.Add("{ 0 }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "skyboxscale":
                 {
                     uint headaddr = (uint)(data.Address + imagebase);
                     int cnt = int.Parse(data.CustomProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                     SkyboxScale[] sclini = SkyboxScaleList.Load(data.Filename);
                     for (int j = 0; j < cnt; j++)
                     {
                         writer.WriteLine("SkyboxScale {0}_{1} = {2};", name, j, sclini[j].ToStruct());
                         pointers.Add(headaddr, string.Format("{0}_{1}", name, j));
                         headaddr += 4;
                     }
                 }
                 break;
             case "stageselectlist":
                 {
                     StageSelectLevel[] list = StageSelectLevelList.Load(data.Filename);
                     writer.WriteLine("StageSelectLevel {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (StageSelectLevel obj in list)
                         objs.Add(obj.ToStruct());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "levelrankscores":
                 {
                     Dictionary<SA2LevelIDs, LevelRankScores> list = LevelRankScoresList.Load(data.Filename);
                     writer.WriteLine("LevelRankScores {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Count);
                     foreach (KeyValuePair<SA2LevelIDs, LevelRankScores> obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ LevelIDs_Invalid }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "levelranktimes":
                 {
                     Dictionary<SA2LevelIDs, LevelRankTimes> list = LevelRankTimesList.Load(data.Filename);
                     writer.WriteLine("LevelRankTimes {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Count);
                     foreach (KeyValuePair<SA2LevelIDs, LevelRankTimes> obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ LevelIDs_Invalid }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "endpos":
                 {
                     Dictionary<SA2LevelIDs, SA2EndPosInfo> list = SA2EndPosList.Load(data.Filename);
                     writer.WriteLine("LevelEndPosition {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Count);
                     foreach (KeyValuePair<SA2LevelIDs, SA2EndPosInfo> obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ LevelIDs_Invalid }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "animationlist":
                 {
                     SA2AnimationInfo[] list = SA2AnimationInfoList.Load(data.Filename);
                     writer.WriteLine("AnimationInfo {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Length);
                     foreach (SA2AnimationInfo obj in list)
                         objs.Add(obj.ToStruct());
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
             case "levelpathlist":
                 {
                     List<SA1LevelAct> levels = new List<SA1LevelAct>();
                     foreach (string dir in Directory.GetDirectories(data.Filename))
                     {
                         SA1LevelAct level;
                         try { level = new SA1LevelAct(new DirectoryInfo(dir).Name); }
                         catch { continue; }
                         levels.Add(level);
                         List<PathData> paths = PathList.Load(dir);
                         for (int i = 0; i < paths.Count; i++)
                         {
                             writer.WriteLine("Loop {0}_{1}_{2}_Entries[] = {{", name, level.ToString().MakeIdentifier(), i);
                             List<string> objs = new List<string>(paths[i].Path.Count);
                             foreach (PathDataEntry entry in paths[i].Path)
                                 objs.Add(entry.ToStruct());
                             writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                             writer.WriteLine("};");
                             writer.WriteLine();
                             writer.WriteLine("LoopHead {0}_{1}_{2} = {{ {3}, LengthOfArray({0}_{1}_{2}_Entries), {4}, {0}_{1}_{2}_Entries, (ObjectFuncPtr){5} }};",
                                 name, level.ToString().MakeIdentifier(), i, paths[i].Unknown,
                                 HelperFunctions.ToC(paths[i].TotalDistance),
                                 HelperFunctions.ToCHex(paths[i].Code));
                             writer.WriteLine();
                         }
                         writer.WriteLine("LoopHead *{0}_{1}[] = {{", name, level.ToString().MakeIdentifier());
                         for (int i = 0; i < paths.Count; i++)
                             writer.WriteLine("\t&{0}_{1}_{2},", name, level.ToString().MakeIdentifier(), i);
                         writer.WriteLine("\tNULL");
                         writer.WriteLine("};");
                         writer.WriteLine();
                     }
                     writer.WriteLine("PathDataPtr {0}[] = {{", name);
                     foreach (SA1LevelAct level in levels)
                         writer.WriteLine("\t{{ {0}, {1}_{2} }},", level.ToC(), name,
                             level.ToString().MakeIdentifier());
                     writer.WriteLine("\t{ 0xFFFF }");
                     writer.WriteLine("};");
                     writer.WriteLine();
                 }
                 break;
             case "stagelightdatalist":
                 {
                     List<SA1StageLightData> list = SA1StageLightDataList.Load(data.Filename);
                     writer.WriteLine("StageLightData {0}[] = {{", name);
                     List<string> objs = new List<string>(list.Count + 1);
                     foreach (SA1StageLightData obj in list)
                         objs.Add(obj.ToStruct());
                     objs.Add("{ 0xFFu }");
                     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray()));
                     writer.WriteLine("};");
                 }
                 break;
         }
         writer.WriteLine();
         if (data.PointerList != null && data.PointerList.Length > 0)
             foreach (int ptr in data.PointerList)
                 pointers.Add((uint)(ptr + imagebase), name);
     }
     writer.WriteLine("PointerInfo pointers[] = {");
     List<string> ptrs = new List<string>(pointers.Count);
     foreach (KeyValuePair<uint, string> ptr in pointers)
         ptrs.Add(string.Format("ptrdecl({0}, &{1})", HelperFunctions.ToCHex(ptr.Key), ptr.Value));
     writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", ptrs.ToArray()));
     writer.WriteLine("};");
     writer.WriteLine();
 }