public Edge(string gamePath) { var versionInfo = FileVersionInfo.GetVersionInfo(GamePath = gamePath); EngineVersion = new Version(versionInfo.FileMajorPart, versionInfo.FileMinorPart, versionInfo.FileBuildPart, versionInfo.FilePrivatePart); GameDirectory = Path.GetDirectoryName(GamePath); Directory.CreateDirectory(ModsDirectory = Path.Combine(GameDirectory, "mods")); AudioDirectory = Path.Combine(GameDirectory, "audio"); LevelsDirectory = Path.Combine(GameDirectory, "levels"); ModelsDirectory = Path.Combine(GameDirectory, "models"); TexturesDirectory = Path.Combine(GameDirectory, "textures"); disabledEdgeMods = new StringSetFile(Path.Combine(ModsDirectory, "disabledEdgeMods.txt")); ModifiedFiles = new StringSetFile(Path.Combine(ModsDirectory, "modifiedFiles.txt")); if (IsCracked) { SteamOtl = new SteamOtl(Path.Combine(GameDirectory, "steam_otl.ini")); } RefreshEdgeMods(); foreach (var file in Directory.EnumerateFiles(TexturesDirectory, "*.etx")) { try { ExFormat = ETX.FromFile(file) is ETX1804; break; } catch { } } }
public void Draw(string path, Matrix3D?parentMatrix = null) { ESO eso; do { var matrix = GetMatrix(eso = ESO.FromFile(path)); if (parentMatrix.HasValue) { matrix *= parentMatrix.Value; } foreach (var model in eso.Models) { BitmapImage image; var material = new DiffuseMaterial(Brushes.White); var geom = new MeshGeometry3D { Positions = new Point3DCollection(model.Vertices.Select(AssetHelper.ConvertVertex)), Normals = new Vector3DCollection(model.Normals.Select(AssetHelper.ConvertVector)) }; var ema = EMA.FromFile(Path.Combine(MainWindow.Edge.ModelsDirectory, model.MaterialAsset + ".ema")); if (ema.Textures.Length > 0 && model.HasTexCoords) { geom.TextureCoordinates = new PointCollection(model.TexCoords.Select(ConvertTexCoord)); var etx = ETX.FromFile(Path.Combine(MainWindow.Edge.TexturesDirectory, ema.Textures[0].Asset + ".etx")); image = etx.GetBitmap().GetBitmapImage(); Viewport2DVisual3D.SetIsVisualHostMaterial(material, true); } else { image = new BitmapImage(); } if (!parentMatrix.HasValue && model.HasColors) { TaskDialog.Show(this, Localization.Warning, Localization.ModelColorWarning, Localization.ModelColorWarningDetails, TaskDialogType.Warning); } for (var i = model.Vertices.Count - 1; i >= 0; i--) { geom.TriangleIndices.Add(i); } var transform = new MatrixTransform3D(matrix); Model.Children.Add(new Viewport2DVisual3D { Geometry = geom, Material = material, Visual = new Image { Source = image }, Transform = transform }); if (!DebugMode) { continue; } var lines = new ScreenSpaceLines3D { Color = Colors.Red, Transform = transform }; Model.Children.Add(lines); var k = 0; while (k < model.Vertices.Count) { lines.Points.Add(AssetHelper.ConvertVertex(model.Vertices[k])); lines.Points.Add(AssetHelper.ConvertVertex(model.Vertices[k + 1])); lines.Points.Add(AssetHelper.ConvertVertex(model.Vertices[k + 1])); lines.Points.Add(AssetHelper.ConvertVertex(model.Vertices[k + 2])); lines.Points.Add(AssetHelper.ConvertVertex(model.Vertices[k + 2])); lines.Points.Add(AssetHelper.ConvertVertex(model.Vertices[k])); k += 3; } } if (!DrawChildModels) { return; } if (!eso.Header.NodeChild.IsZero()) { Draw(Path.Combine(Path.GetDirectoryName(path), eso.Header.NodeChild + ".eso"), matrix); } path = Path.Combine(Path.GetDirectoryName(path), eso.Header.NodeSibling + ".eso"); }while (!eso.Header.NodeSibling.IsZero()); }
public void Install(HashSet <string> allModifiedFiles, StringBuilder error, Dictionary <string, EdgeMod> conflicts, ProgressCallback callback = null) { if (MinEngineVersion != null && parent.EngineVersion < MinEngineVersion || MaxEngineVersion != null && parent.EngineVersion > MaxEngineVersion) { error.AppendLine(string.Format(Localization.EdgeModErrorEngineNotSupported, ID, parent.EngineVersion)); return; } if (!Dependency.IsSubsetOf(from edgeMod in parent.EdgeMods where edgeMod.Enabled select edgeMod.ID)) { error.AppendLine(string.Format(Localization.EdgeModErrorEngineDependenciesNotInstalled, ID)); return; } if (conflicts.ContainsKey(ID)) { error.AppendLine(string.Format(Localization.EdgeModErrorConflict, conflicts[ID].ID, ID)); return; } conflicts.Add(ID, this); // self conflict foreach (var conflict in Conflicts.Where(conflict => !conflicts.ContainsKey(conflict))) { conflicts.Add(conflict, this); } using (var extractor = new SevenZipExtractor(FilePath)) { try { extractor.ExtractFiles(e => { if (e.Reason == ExtractFileCallbackReason.Start) { callback?.Invoke(ID + '\\' + e.ArchiveFileInfo.FileName); } var path = Path.Combine(parent.GameDirectory, e.ArchiveFileInfo.FileName); try { if (e.ArchiveFileInfo.IsDirectory) { return; // ignore directories } var lowered = e.ArchiveFileInfo.FileName.ToLower(); // and files that has been processed if (lowered == "mod.xml" || lowered == "description.txt" || lowered == "levels\\mapping.xsl" || lowered == "config\\settings_readme.txt" || lowered == "config\\settings_template_do_not_modify.ini" || lowered.StartsWith("mods\\", true, CultureInfo.InvariantCulture) || lowered.EndsWith(".bak", true, CultureInfo.InvariantCulture)) { return; } var isSfx = lowered.StartsWith("sfx\\", true, CultureInfo.InvariantCulture); if (isSfx) { path = Path.Combine(parent.BeginSfx(), e.ArchiveFileInfo.FileName); } Directory.CreateDirectory(Path.GetDirectoryName(path)); var fileInfo = new FileInfo(path); switch (e.Reason) { case ExtractFileCallbackReason.Start: if (isSfx) { File.Delete(path); e.ExtractToFile = path; } else { if (Type == EdgeModType.Level && fileInfo.Exists && !allModifiedFiles.Contains(e.ArchiveFileInfo.FileName.ToLower())) { throw new IOException(Localization.EdgeModErrorLevelOverwriteForbidden); } if (!fileInfo.Exists || (ulong)fileInfo.Length != e.ArchiveFileInfo.Size || Math.Abs(fileInfo.LastWriteTime.Ticks - e.ArchiveFileInfo.LastWriteTime.Ticks) > new TimeSpan(0, 0, 1).Ticks) // fast check { parent.CreateCopy(allModifiedFiles, lowered); File.Delete(path); e.ExtractToFile = path; } parent.ModifiedFiles.Add(lowered); allModifiedFiles.Add(lowered); } break; case ExtractFileCallbackReason.Done: if (e.ExtractToFile != null) { fileInfo.LastWriteTime = e.ArchiveFileInfo.LastWriteTime; if (!e.ExtractToFile.EndsWith(".etx", StringComparison.Ordinal)) { return; } try { var etx = ETX.FromFile(e.ExtractToFile); if (parent.ExFormat && etx is ETX1803) { ETX1804.CreateFromImage(etx.GetBitmap(), etx.AssetHeader) .Save(e.ExtractToFile); } else if (!parent.ExFormat && etx is ETX1804) { ETX1803.CreateFromImage(etx.GetBitmap(), etx.AssetHeader) .Save(e.ExtractToFile); } } catch { } } break; case ExtractFileCallbackReason.Failure: throw e.Exception; } } catch (Exception exc) { error.AppendFormat(Localization.EdgeModFileError, ID, path, exc.Message); } }); } catch (Exception exc) { error.AppendFormat(Localization.EdgeModError, ID, exc.Message); } } try { if (Xsl == null) { return; } var mappingPath = Path.Combine(parent.GameDirectory, "levels\\mapping.xml"); parent.CreateCopy(allModifiedFiles, "levels\\mapping.xml"); var transform = new XslCompiledTransform(true); transform.Load(XmlReader.Create(new StringReader(parent.IsCracked ? XslCracked : Xsl)), new XsltSettings(true, true), new XmlUrlResolver()); var writer = new StringWriter(); transform.Transform(XmlReader.Create(new StringReader(File.ReadAllText( File.Exists(mappingPath) ? mappingPath : (mappingPath + ".bak")))), null, XmlWriter.Create(writer, new XmlWriterSettings { Indent = true }), new XmlUrlResolver()); File.WriteAllText(mappingPath, writer.ToString()); parent.ModifiedFiles.Add("levels\\mapping.xml"); allModifiedFiles.Add("levels\\mapping.xml"); } catch (Exception exc) { error.AppendFormat(Localization.EdgeModErrorXsl, exc.Message); } }
public static Tuple <Exception, string, List <FileEntry> > Compile(bool exFormat, string file, string directory = null) { var list = new List <FileEntry>(); var fileName = Path.GetFileNameWithoutExtension(file); if (string.IsNullOrWhiteSpace(directory)) { directory = Path.GetDirectoryName(file); } string inputPath = Path.Combine(Path.GetDirectoryName(file), fileName), outputPath = Path.Combine(directory, fileName); Warning.Start(); try { switch ((Path.GetExtension(file) ?? string.Empty).ToLowerInvariant()) { case ".bin": switch (fileName.ToLowerInvariant()) { case "cos": var array = new short[181]; using (var stream = File.OpenRead(file)) using (var reader = new BinaryReader(stream)) for (var i = 0; i <= 180; i++) { array[i] = reader.ReadInt16(); } File.WriteAllText(outputPath += ".txt", string.Join(Environment.NewLine, array.Select(value => value / 256.0))); list.Add(new FileEntry(outputPath, "cos.txt")); break; case "font": DecompileFont(file, ref outputPath); list.Add(new FileEntry(outputPath, "font.xml")); break; default: { var index = 0; foreach (var path in Level.CreateFromCompiled(file).Decompile(outputPath)) { list.Add(new FileEntry(path, index == 0 ? "level.xml" : index == 1 ? "level.png" : "level.z.png")); index++; } break; } } break; case ".xml": var root = XHelper.Load(file).Elements().First(); switch (root.Name.LocalName.ToLowerInvariant()) { case "level": { var index = 0; foreach (var path in Level.CreateFromDecompiled(inputPath) .Compile(outputPath + ".bin")) { list.Add(new FileEntry(path, index == 0 ? "level.bin" : "model.eso")); index++; } break; } case "animation": AssetHelper.ParseEan(root, fileName).Save(outputPath = Path.Combine(directory, AssetUtil.CrcFullName(fileName, "models") + ".ean")); list.Add(new FileEntry(outputPath, "animation.ean")); break; case "material": { string name; Helper.AnalyzeFileName(out name, out outputPath, fileName); var ema = AssetHelper.ParseEma(root, name); ema.Save(Path.Combine(directory, outputPath += ".ema")); list.Add(new FileEntry(outputPath, "material.ema")); break; } case "models": { string name; Helper.AnalyzeFileName(out name, out outputPath, fileName); var eso = AssetHelper.ParseEso(root, name); eso.Save(Path.Combine(directory, outputPath += ".eso")); list.Add(new FileEntry(outputPath, "model.eso")); break; } case "font": { CompileFont(inputPath, out outputPath, root); list.Add(new FileEntry(outputPath, "font.bin")); break; } } break; case ".loc": LOC.FromFile(file).SaveXls(outputPath += ".xls"); list.Add(new FileEntry(outputPath, "text.xls")); break; case ".xls": LocHelper.FromXls(file).Save(outputPath += ".loc"); list.Add(new FileEntry(outputPath, "text.loc")); break; case ".etx": var etx = ETX.FromFile(file); etx.GetBitmap().Save(outputPath = Path.Combine(directory, etx.AssetHeader.Name + ".png")); list.Add(new FileEntry(outputPath, "texture.png")); break; case ".png": { var name = AssetUtil.CrcFullName(fileName, "textures") + ".etx"; (exFormat ? (ETX)ETX1804.CreateFromImage(file) : ETX1803.CreateFromImage(file)) .Save(outputPath = Path.Combine(directory, name)); list.Add(new FileEntry(outputPath, "texture.etx")); break; } case ".ean": var ean = EAN.FromFile(file); File.WriteAllText(outputPath = Path.Combine(directory, Helper.GetDecompiledFileName(fileName, ean) + ".xml"), AssetHelper.GetEanElement(ean).ToString()); list.Add(new FileEntry(outputPath, "animation.xml")); break; case ".ema": { var ema = EMA.FromFile(file); File.WriteAllText(outputPath = Path.Combine(directory, Helper.GetDecompiledFileName(fileName, ema) + ".xml"), AssetHelper.GetEmaElement(ema).ToString()); list.Add(new FileEntry(outputPath, "material.xml")); break; } case ".eso": { var eso = ESO.FromFile(file); File.WriteAllText(outputPath = Path.Combine(directory, Helper.GetDecompiledFileName(fileName, eso) + ".xml"), AssetHelper.GetEsoElement(eso).ToString()); list.Add(new FileEntry(outputPath, "model.xml")); break; } case ".txt": using (var stream = new FileStream(outputPath += ".bin", FileMode.Create, FileAccess.Write, FileShare.Read)) { var writer = new BinaryWriter(stream); foreach (var num in File.ReadAllText(file) .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(double.Parse)) { writer.Write((short)Math.Round(num * 256)); } } list.Add(new FileEntry(outputPath, "cos.bin")); break; default: switch (Path.GetFileName(file)) { case "audio": if (Directory.Exists(outputPath = Path.Combine(directory, "sfx"))) { Directory.Delete(outputPath, true); } Directory.CreateDirectory(outputPath); string xsb = Path.Combine(file, "sfx.xsb"), xwb = Path.Combine(file, "sfx.xwb"); int offset; using (var stream = new FileStream(xsb, FileMode.Open)) { stream.Position = 0x2A; using (var reader = new BinaryReader(stream)) offset = reader.ReadInt32(); } var unxwb = new Process { StartInfo = new ProcessStartInfo( Path.Combine(CurrentApp.Directory, "Resources/Libraries/unxwb.exe"), FormattableString.Invariant($"-d \"{outputPath}\" -b \"{xsb}\" {offset} \"{xwb}\"")) { UseShellExecute = false, CreateNoWindow = true } }; unxwb.Start(); unxwb.WaitForExit(); list.Add(new FileEntry(outputPath, "sfx")); break; case "sfx": Directory.CreateDirectory(outputPath = Path.Combine(directory, "audio")); string tempPath = Helper.GetRandomDirectory(), tempInputPath = Path.Combine(tempPath, "sfx"), tempOutputPath = Path.Combine(tempPath, "audio"); /************************************************************************************ * create temp input dir because it's still occupied for unknown reason; * * create temp output dir because COMException will be thrown if output dir and * * temp input dir is not under the same drive. * ************************************************************************************/ Directory.CreateDirectory(tempInputPath); var projectPath = GenerateXactProject(file, tempInputPath); using (var project = new CXACTMasterProject()) { project.Create(); project.Load(projectPath, new CXACTMasterProjectCallback(), 0); project.Build(new CXACTMasterProjectCallback(), tempOutputPath, false, false); } foreach (var path in Directory.EnumerateFiles(tempOutputPath)) { var target = Path.Combine(outputPath, Path.GetFileName(path)); File.Delete(target); File.Move(path, target); } try { Directory.Delete(tempPath, true); } catch { Trace.WriteLine(tempPath, "Delete tempPath failed"); } list.Add(new FileEntry(outputPath, "audio")); break; default: throw new NotSupportedException(Localization.DecompileUnrecognizedFile); } break; } return(new Tuple <Exception, string, List <FileEntry> >(null, Warning.Fetch(), list)); } catch (Exception exc) { return(new Tuple <Exception, string, List <FileEntry> >(exc, Warning.Fetch(), list)); } finally { Warning.Clear(); } }