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.LevelItems = new List<LevelItem>(); for (int i = 0; i < LevelData.geo.COL.Count; i++) LevelData.LevelItems.Add(new LevelItem(LevelData.geo.COL[i], d3ddevice, 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] = new Texture(d3ddevice, TexBmps[j].Image, Usage.None, Pool.Managed); 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; statsToolStripMenuItem.Enabled = LevelData.geo != null; selectedItems.SelectionChanged += SelectionChanged; UseWaitCursor = false; Enabled = editInfoToolStripMenuItem.Enabled = saveToolStripMenuItem.Enabled = exportToolStripMenuItem.Enabled = importToolStripMenuItem.Enabled = true; DrawLevel(); }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { #if !DEBUG try { #endif int steps = 10; if (d3ddevice == null) ++steps; toolStrip1.Enabled = false; // HACK: Fixes Twinkle Circuit's geometry lingering if loaded before Sky Chase. // I'm sure the real problem is somewhere below, but this is sort of an all around cleanup. if (isStageLoaded) LevelData.Clear(); isStageLoaded = false; using (ProgressDialog progress = new ProgressDialog("Loading stage: " + levelName, steps)) { IniLevelData level = ini.Levels[levelID]; string syspath = Path.Combine(Environment.CurrentDirectory, ini.SystemPath); string modpath = ini.ModPath; SA1LevelAct levelact = new SA1LevelAct(level.LevelID); LevelData.leveltexs = null; cam = new EditorCamera(EditorOptions.RenderDrawDistance); Invoke((Action<IWin32Window>)progress.Show, this); if (d3ddevice == null) { progress.SetTask("Initializing Direct3D..."); Invoke((Action)InitializeDirect3D); progress.StepProgress(); } progress.SetTaskAndStep("Loading level data:", "Geometry"); if (string.IsNullOrEmpty(level.LevelGeometry)) LevelData.geo = null; else { LevelData.geo = LandTable.LoadFromFile(level.LevelGeometry); LevelData.LevelItems = new List<LevelItem>(); for (int i = 0; i < LevelData.geo.COL.Count; i++) LevelData.LevelItems.Add(new LevelItem(LevelData.geo.COL[i], d3ddevice, i, selectedItems)); } progress.StepProgress(); progress.SetStep("Textures"); LevelData.TextureBitmaps = new Dictionary<string, BMPInfo[]>(); LevelData.Textures = new Dictionary<string, Texture[]>(); if (LevelData.geo != null && !string.IsNullOrEmpty(LevelData.geo.TextureFileName)) { BMPInfo[] TexBmps = TextureArchive.GetTextures(Path.Combine(syspath, LevelData.geo.TextureFileName) + ".PVM"); Texture[] texs = new Texture[TexBmps.Length]; for (int j = 0; j < TexBmps.Length; j++) texs[j] = new Texture(d3ddevice, TexBmps[j].Image, Usage.None, Pool.Managed); if (!LevelData.TextureBitmaps.ContainsKey(LevelData.geo.TextureFileName)) LevelData.TextureBitmaps.Add(LevelData.geo.TextureFileName, TexBmps); if (!LevelData.Textures.ContainsKey(LevelData.geo.TextureFileName)) LevelData.Textures.Add(LevelData.geo.TextureFileName, texs); LevelData.leveltexs = LevelData.geo.TextureFileName; } progress.StepProgress(); #region Start Positions progress.SetTaskAndStep("Setting up start positions..."); LevelData.StartPositions = new StartPosItem[LevelData.Characters.Length]; for (int i = 0; i < LevelData.StartPositions.Length; i++) { progress.SetStep(string.Format("{0}/{1}", (i + 1), LevelData.StartPositions.Length)); IniCharInfo character; if (i == 0 && levelact.Level == SA1LevelIDs.PerfectChaos) character = ini.Characters["SuperSonic"]; else character = ini.Characters[LevelData.Characters[i]]; Dictionary<SA1LevelAct, SA1StartPosInfo> posini = SA1StartPosList.Load(character.StartPositions); Vertex pos = new Vertex(); int rot = 0; if (posini.ContainsKey(levelact)) { pos = posini[levelact].Position; rot = posini[levelact].YRotation; } LevelData.StartPositions[i] = new StartPosItem(new ModelFile(character.Model).Model, character.Textures, character.Height, pos, rot, d3ddevice, selectedItems); LoadTextureList(character.TextureList, syspath); } progress.StepProgress(); #endregion #region Death Zones progress.SetTaskAndStep("Death Zones:", "Initializing..."); if (string.IsNullOrEmpty(level.DeathZones)) LevelData.DeathZones = null; else { LevelData.DeathZones = new List<DeathZoneItem>(); DeathZoneFlags[] dzini = DeathZoneFlagsList.Load(level.DeathZones); string path = Path.GetDirectoryName(level.DeathZones); for (int i = 0; i < dzini.Length; i++) { progress.SetStep(String.Format("Loading model {0}/{1}", (i + 1), dzini.Length)); LevelData.DeathZones.Add(new DeathZoneItem( new ModelFile(Path.Combine(path, i.ToString(System.Globalization.NumberFormatInfo.InvariantInfo) + ".sa1mdl")) .Model, dzini[i].Flags, d3ddevice, selectedItems)); } } progress.StepProgress(); #endregion #region Textures and Texture Lists progress.SetTaskAndStep("Loading textures for:"); progress.SetStep("Common objects"); // Loads common object textures (e.g OBJ_REGULAR) LoadTextureList(ini.ObjectTextureList, syspath); progress.SetTaskAndStep("Loading stage texture lists..."); // Loads the textures in the texture list for this stage (e.g BEACH01) foreach (string file in Directory.GetFiles(ini.LevelTextureLists)) { LevelTextureList texini = LevelTextureList.Load(file); if (texini.Level != levelact) continue; LoadTextureList(texini.TextureList, syspath); } progress.SetTaskAndStep("Loading textures for:", "Objects"); // Object texture list(s) LoadTextureList(level.ObjectTextureList, syspath); progress.SetStep("Stage"); // The stage textures... again? "Extra"? if (level.Textures != null && level.Textures.Length > 0) foreach (string tex in level.Textures) { LoadPVM(tex, syspath); if (string.IsNullOrEmpty(LevelData.leveltexs)) LevelData.leveltexs = tex; } progress.StepProgress(); #endregion #region Object Definitions / SET Layout progress.SetTaskAndStep("Loading Object Definitions:", "Parsing..."); LevelData.ObjDefs = new List<ObjectDefinition>(); Dictionary<string, ObjectData> objdefini = IniSerializer.Deserialize<Dictionary<string, ObjectData>>(ini.ObjectDefinitions); if (!string.IsNullOrEmpty(level.ObjectList) && File.Exists(level.ObjectList)) { List<ObjectData> objectErrors = new List<ObjectData>(); ObjectListEntry[] objlstini = ObjectList.Load(level.ObjectList, false); Directory.CreateDirectory("dllcache").Attributes |= FileAttributes.Hidden; 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)) { progress.SetStep("Compiling: " + defgroup.CodeFile); // TODO: Split this out to a function #region Compile object code files string ty = defgroup.CodeType; string dllfile = Path.Combine("dllcache", ty + ".dll"); DateTime modDate = DateTime.MinValue; if (File.Exists(dllfile)) modDate = File.GetLastWriteTime(dllfile); string fp = defgroup.CodeFile.Replace('/', Path.DirectorySeparatorChar); if (modDate >= File.GetLastWriteTime(fp) && modDate > File.GetLastWriteTime(Application.ExecutablePath)) def = (ObjectDefinition) Activator.CreateInstance( Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, dllfile)) .GetType(ty)); else { string ext = Path.GetExtension(fp); CodeDomProvider pr = null; switch (ext.ToLowerInvariant()) { case ".cs": pr = new Microsoft.CSharp.CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; case ".vb": pr = new Microsoft.VisualBasic.VBCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; } if (pr != null) { CompilerParameters para = new CompilerParameters(new string[] { "System.dll", "System.Core.dll", "System.Drawing.dll", Assembly.GetAssembly(typeof (Vector3)).Location, Assembly.GetAssembly(typeof (Texture)).Location, Assembly.GetAssembly(typeof (D3DX)).Location, Assembly.GetExecutingAssembly().Location, Assembly.GetAssembly(typeof (LandTable)).Location, Assembly.GetAssembly(typeof (EditorCamera)).Location, Assembly.GetAssembly(typeof (SA1LevelAct)).Location, Assembly.GetAssembly(typeof (ObjectDefinition)).Location }) { GenerateExecutable = false, GenerateInMemory = false, IncludeDebugInformation = true, OutputAssembly = Path.Combine(Environment.CurrentDirectory, dllfile) }; CompilerResults res = pr.CompileAssemblyFromFile(para, fp); if (res.Errors.HasErrors) { // TODO: Merge with existing object error handler. I add too many ToDos. string errors = null; foreach (CompilerError item in res.Errors) errors += String.Format("\n\n{0}, {1}: {2}", item.Line, item.Column, item.ErrorText); MessageBox.Show("Failed to compile object code file:\n" + defgroup.CodeFile + errors, "Object compilation failure", MessageBoxButtons.OK, MessageBoxIcon.Error); def = new DefaultObjectDefinition(); } else { def = (ObjectDefinition)Activator.CreateInstance(res.CompiledAssembly.GetType(ty)); } } else def = new DefaultObjectDefinition(); } #endregion } 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)) { progress.SetStep("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) || string.IsNullOrEmpty(defgroup.Texture) || !LevelData.Textures.ContainsKey(defgroup.Texture)) { 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, d3ddevice); def.SetInternalName(objlstini[ID].Name); } // Loading SET Layout progress.SetTaskAndStep("Loading SET items", "Initializing..."); if (LevelData.ObjDefs.Count > 0) { LevelData.SETName = level.SETName ?? level.LevelID; string setstr = Path.Combine(syspath, "SET" + LevelData.SETName + "{0}.bin"); LevelData.SETItems = new List<SETItem>[LevelData.SETChars.Length]; for (int i = 0; i < LevelData.SETChars.Length; i++) { List<SETItem> list = new List<SETItem>(); byte[] setfile = null; string formatted = string.Format(setstr, LevelData.SETChars[i]); if (modpath != null && File.Exists(Path.Combine(modpath, formatted))) setfile = File.ReadAllBytes(Path.Combine(modpath, formatted)); else if (File.Exists(formatted)) setfile = File.ReadAllBytes(formatted); if (setfile != null) { progress.SetTask("SET: " + formatted.Replace(Environment.CurrentDirectory, "")); int count = BitConverter.ToInt32(setfile, 0); int address = 0x20; for (int j = 0; j < count; j++) { progress.SetStep(string.Format("{0}/{1}", (j + 1), count)); SETItem ent = new SETItem(setfile, address, selectedItems); list.Add(ent); address += 0x20; } } LevelData.SETItems[i] = list; } } else { LevelData.SETItems = null; } // 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()); MessageBox.Show(count + ((count == 1) ? " object" : " objects") + " failed to load their model(s).\n" + "\nThe level will still display, but the objects in question will not display their proper models." + "\n\nPlease check the log for details.", "Error loading models", MessageBoxButtons.OK, MessageBoxIcon.Information); } } else { LevelData.SETItems = null; } progress.StepProgress(); #endregion #region CAM Layout progress.SetTaskAndStep("Loading CAM items", "Initializing..."); string camstr = Path.Combine(syspath, "CAM" + LevelData.SETName + "{0}.bin"); LevelData.CAMItems = new List<CAMItem>[LevelData.SETChars.Length]; for (int i = 0; i < LevelData.SETChars.Length; i++) { List<CAMItem> list = new List<CAMItem>(); byte[] camfile = null; string formatted = string.Format(camstr, LevelData.SETChars[i]); if (modpath != null && File.Exists(Path.Combine(modpath, formatted))) camfile = File.ReadAllBytes(Path.Combine(modpath, formatted)); else if (File.Exists(formatted)) camfile = File.ReadAllBytes(formatted); if (camfile != null) { progress.SetTask("CAM: " + formatted.Replace(Environment.CurrentDirectory, "")); int count = BitConverter.ToInt32(camfile, 0); int address = 0x40; for (int j = 0; j < count; j++) { progress.SetStep(string.Format("{0}/{1}", (j + 1), count)); CAMItem ent = new CAMItem(camfile, address, selectedItems); list.Add(ent); address += 0x40; } } LevelData.CAMItems[i] = list; } CAMItem.Init(d3ddevice); progress.StepProgress(); #endregion #region Loading Level Effects LevelData.leveleff = null; if (!string.IsNullOrEmpty(level.Effects)) { progress.SetTaskAndStep("Loading Level Effects..."); LevelDefinition def = null; string ty = "SADXObjectDefinitions.Level_Effects." + Path.GetFileNameWithoutExtension(level.Effects); string dllfile = Path.Combine("dllcache", ty + ".dll"); DateTime modDate = DateTime.MinValue; if (File.Exists(dllfile)) modDate = File.GetLastWriteTime(dllfile); string fp = level.Effects.Replace('/', Path.DirectorySeparatorChar); if (modDate >= File.GetLastWriteTime(fp) && modDate > File.GetLastWriteTime(Application.ExecutablePath)) { def = (LevelDefinition) Activator.CreateInstance( Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, dllfile)).GetType(ty)); } else { string ext = Path.GetExtension(fp); CodeDomProvider pr = null; switch (ext.ToLowerInvariant()) { case ".cs": pr = new Microsoft.CSharp.CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; case ".vb": pr = new Microsoft.VisualBasic.VBCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; } if (pr != null) { CompilerParameters para = new CompilerParameters(new string[] { "System.dll", "System.Core.dll", "System.Drawing.dll", Assembly.GetAssembly(typeof (Vector3)).Location, Assembly.GetAssembly(typeof (Texture)).Location, Assembly.GetAssembly(typeof (D3DX)).Location, Assembly.GetExecutingAssembly().Location, Assembly.GetAssembly(typeof (LandTable)).Location, Assembly.GetAssembly(typeof (EditorCamera)).Location, Assembly.GetAssembly(typeof (SA1LevelAct)).Location, Assembly.GetAssembly(typeof (Item)).Location }) { GenerateExecutable = false, GenerateInMemory = false, IncludeDebugInformation = true, OutputAssembly = Path.Combine(Environment.CurrentDirectory, dllfile) }; CompilerResults res = pr.CompileAssemblyFromFile(para, fp); if (!res.Errors.HasErrors) def = (LevelDefinition)Activator.CreateInstance(res.CompiledAssembly.GetType(ty)); } } if (def != null) def.Init(level, levelact.Act, d3ddevice); LevelData.leveleff = def; } progress.StepProgress(); #endregion #region Loading Splines LevelData.LevelSplines = new List<SplineData>(); SplineData.Init(); if (!string.IsNullOrEmpty(ini.Paths)) { progress.SetTaskAndStep("Reticulating splines..."); String splineDirectory = Path.Combine(Path.Combine(Environment.CurrentDirectory, ini.Paths), levelact.ToString()); if (Directory.Exists(splineDirectory)) { List<string> pathFiles = new List<string>(); for (int i = 0; i < int.MaxValue; i++) { string path = string.Concat(splineDirectory, string.Format("/{0}.ini", i)); if (File.Exists(path)) { pathFiles.Add(path); } else break; } foreach (string pathFile in pathFiles) // looping through path files { SplineData newSpline = new SplineData(PathData.Load(pathFile), selectedItems); newSpline.RebuildMesh(d3ddevice); LevelData.LevelSplines.Add(newSpline); } } } progress.StepProgress(); #endregion #region Stage Lights progress.SetTaskAndStep("Loading lights..."); if ((stageLightList != null) && (stageLightList.Count > 0)) { List<SA1StageLightData> lightList = new List<SA1StageLightData>(); foreach (SA1StageLightData lightData in stageLightList) { if ((lightData.Level == levelact.Level) && (lightData.Act == levelact.Act)) lightList.Add(lightData); } if (lightList.Count > 0) { for (int i = 0; i < d3ddevice.Lights.Count; i++) // clear all default lights { d3ddevice.Lights[i].Enabled = false; } for (int i = 0; i < lightList.Count; i++) { SA1StageLightData lightData = lightList[i]; d3ddevice.Lights[i].Enabled = true; d3ddevice.Lights[i].Type = (lightData.UseDirection) ? LightType.Directional : LightType.Point; d3ddevice.Lights[i].Diffuse = lightData.RGB.ToColor(); d3ddevice.Lights[i].DiffuseColor = new ColorValue(lightData.RGB.X, lightData.RGB.Y, lightData.RGB.Z, 1.0f); d3ddevice.Lights[i].Ambient = lightData.AmbientRGB.ToColor(); d3ddevice.Lights[i].Specular = Color.Black; d3ddevice.Lights[i].Direction = lightData.Direction.ToVector3(); d3ddevice.Lights[i].Range = lightData.Dif; // guessing here } } else { MessageBox.Show("No lights were found for this stage. Using default lights instead.", "No lights found", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } progress.StepProgress(); #endregion transformGizmo = new TransformGizmo(); Invoke((Action)progress.Close); } #if !DEBUG } catch (Exception ex) { MessageBox.Show( ex.GetType().Name + ": " + ex.Message + "\nLog file has been saved to " + Path.Combine(Environment.CurrentDirectory, "SADXLVL2.log") + ".\nSend this to MainMemory on the Sonic Retro forums.", "SADXLVL2 Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Error); File.WriteAllText("SADXLVL2.log", ex.ToString()); initerror = true; } #endif }