-
-
Save rluiten/ea7a6f18a8caf7d3739cc4c9858af877 to your computer and use it in GitHub Desktop.
Source for XmlToDot for DesktopPet.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Linq; | |
using System.Xml.Serialization; | |
namespace XmlToDot | |
{ | |
class Program | |
{ | |
static readonly string appName = "XmlToDot"; | |
static readonly string appDescription = $"{appName} - DesktopPet XML to Graphviz"; | |
static readonly string appUsage = $"Usage: {appName} <xml file name>"; | |
static int Main(string[] args) | |
{ | |
if (args.Length != 1) | |
{ | |
Console.WriteLine(appDescription); | |
Console.WriteLine(appUsage); | |
return 1; | |
} | |
var app = new App(args[0]); | |
return app?.ProcessFile() ?? 1; | |
} | |
} | |
public class App | |
{ | |
private readonly string _fileName; | |
public App(string fileName) | |
{ | |
_fileName = fileName; | |
} | |
public int ProcessFile() | |
{ | |
try | |
{ | |
using (var fileStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read)) | |
{ | |
var mySerializer = new XmlSerializer(typeof(RootNode)); | |
var animationXML = (RootNode)mySerializer.Deserialize(fileStream); | |
return ProcessModel(animationXML); | |
} | |
} | |
catch | |
{ | |
Console.WriteLine($"Problem encountered getting model from \"{_fileName}\"."); | |
} | |
return 1; | |
} | |
int ProcessModel(RootNode model) | |
{ | |
var animations = model.Animations; | |
if (model.Animations == null) | |
{ | |
Console.WriteLine("No animations found exiting."); | |
return 1; | |
} | |
return ProcessAnimations(model.Animations.Animation); | |
} | |
int ProcessAnimations(AnimationNode[] animations) | |
{ | |
Console.WriteLine($"# Processing {animations.Length} animations."); | |
//foreach (var anim in animations) | |
//{ | |
// Console.WriteLine($"{anim.Id} {anim.Name}"); | |
//} | |
Console.WriteLine($"# Convert {_fileName} to Graphviz dot format by DesktopPet Xml2Gv {DateTime.Now}"); | |
Console.WriteLine($"digraph PetGraph {{"); | |
Console.WriteLine($" rankdir = LR;"); | |
foreach (var anim in animations) | |
{ | |
if (anim.Sequence.Next != null) | |
{ | |
Console.WriteLine($" # {anim.Id} Sequence {anim.Sequence.Next.Length}"); | |
var totalProbability = anim.Sequence.Next.Sum(n => n.Probability); | |
ProcessNext(Next.Sequence, totalProbability, anim, anim.Sequence.Next); | |
} | |
if (anim.Border != null && anim.Border.Next != null) | |
{ | |
Console.WriteLine($" # {anim.Id} Border {anim.Border.Next.Length}"); | |
var totalProbability = anim.Border.Next.Sum(n => n.Probability); | |
ProcessNext(Next.Border, totalProbability, anim, anim.Border.Next); | |
} | |
if (anim.Gravity != null && anim.Gravity.Next != null) | |
{ | |
Console.WriteLine($" # {anim.Id} Gravity {anim.Gravity.Next.Length}"); | |
var totalProbability = anim.Gravity.Next.Sum(n => n.Probability); | |
ProcessNext(Next.Gravity, totalProbability, anim, anim.Gravity.Next); | |
} | |
Console.WriteLine($" anim_{anim.Id} [ label=\"{anim.Name} ({anim.Id})\"; ]"); | |
} | |
Console.WriteLine($"}}"); | |
return 0; | |
} | |
private enum Next { Sequence, Border, Gravity }; | |
private const string sequenceColor = "black"; | |
private const string borderColor = "#5555DDFF"; | |
private const string gravityColor = "#55DD55FF"; | |
private void ProcessNext(Next type, int totalProbability, AnimationNode anim, NextNode[] nexts) | |
{ | |
var edgeColor = sequenceColor; | |
var typeMarker = "S"; | |
if (type == Next.Border) { edgeColor = borderColor; typeMarker = "B"; } | |
if (type == Next.Gravity) { edgeColor = gravityColor; typeMarker = "G"; } | |
foreach (var next in nexts) | |
{ | |
var relative = (double)next.Probability / totalProbability; | |
edgeColor = type == Next.Sequence ? convertProbabilityToGray(relative) : edgeColor; | |
var relative2Decimal = relative.ToString("00%"); | |
var probability = relative2Decimal == "100%" ? "" : $"({next.Probability})"; | |
var label = $"[ label=\"{relative2Decimal}{probability} {next.OnlyFlag} {typeMarker}\" color=\"{edgeColor}\" penwidth=\"1\" ]"; | |
Console.WriteLine($" anim_{anim.Id} -> anim_{next.Value} {label}"); | |
} | |
} | |
private string convertProbabilityToGray(double relativeProbability) | |
{ | |
// convert prob of 0 to 1 to gray50 down to gray0(black) | |
// linear mapping. | |
//var p1 = Math.Floor((0.5 + (relativeProbability / 2)) * 100); | |
//var p2 = 50 - (p1 - 50); | |
var p1 = Math.Floor((0.3 + (relativeProbability / (1/0.7))) * 100); | |
// range 30 to 100 | |
var p2 = 70 - (p1 - 30); | |
// input range 50 to 100. | |
// output range 50 to 0. | |
var p3 = p2.ToString(); | |
//p2 = p2 == "100" ? "0" : p2; | |
Console.WriteLine($" # {relativeProbability}, {p1}, {p2}, {p3}"); | |
return $"grey{p3}"; | |
//return "grey36"; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
using System.Xml.Serialization; | |
namespace XmlToDot | |
{ | |
class XmlModel | |
{ | |
} | |
/// <summary> | |
/// This Node class is used to store the XML data using the serialize function. | |
/// </summary> | |
/// <remarks>Once the XML was loaded, it is possible to see the header info in the about box.</remarks> | |
[XmlRoot("animations", Namespace = "http://esheep.petrucci.ch/", IsNullable = false)] | |
public class RootNode | |
{ | |
/// <summary> | |
/// Main informations about the animation XML. | |
/// </summary> | |
[XmlElement("header")] | |
public HeaderNode Header; | |
/// <summary> | |
/// Information about the sprite image. | |
/// </summary> | |
[XmlElement("image")] | |
public ImageNode Image; | |
/// <summary> | |
/// List of spawns. | |
/// </summary> | |
[XmlElement("spawns")] | |
public SpawnsNode Spawns; | |
/// <summary> | |
/// List of animations. | |
/// </summary> | |
[XmlElement("animations")] | |
public AnimationsNode Animations; | |
/// <summary> | |
/// List of child animations. | |
/// </summary> | |
[XmlElement("childs")] | |
public ChildsNode Childs; | |
/// <summary> | |
/// List of sounds. | |
/// </summary> | |
[XmlElement("sounds")] | |
public SoundsNode Sounds; | |
} | |
/// <summary> | |
/// Main informations about the animation XML. This information is taken from the loaded xml file. | |
/// </summary> | |
/// <remarks>Once the XML was loaded, it is possible to see the header info in the about box.</remarks> | |
public class HeaderNode | |
{ | |
/// <summary> | |
/// Author of the animation. | |
/// </summary> | |
[XmlElement("author")] | |
public string Author; | |
/// <summary> | |
/// Title of the animation. | |
/// </summary> | |
[XmlElement("title")] | |
public string Title; | |
/// <summary> | |
/// PetName. Similar to Title but shorter (max 16 chars). "eSheep" word will be replaced with this one in the context menu. | |
/// </summary> | |
[XmlElement("petname")] | |
public string Petname; | |
/// <summary> | |
/// Version of the animation (is a string and developer can insert what he want). | |
/// </summary> | |
[XmlElement("version")] | |
public string Version; | |
/// <summary> | |
/// Information (About and Copyright information) about the animation and the author. | |
/// </summary> | |
[XmlElement("info")] | |
public string Info; | |
/// <summary> | |
/// Application version. Used to parse different XML versions. | |
/// </summary> | |
[XmlElement("application")] | |
public string Application; | |
/// <summary> | |
/// Icon (base64) of the pet, used for the task bar. | |
/// </summary> | |
[XmlElement("icon")] | |
public string Icon; | |
} | |
/// <summary> | |
/// Sprite image | |
/// </summary> | |
public class ImageNode | |
{ | |
/// <summary> | |
/// Quantity of images on the X axis. | |
/// </summary> | |
[XmlElement("tilesx")] | |
public int TilesX; | |
/// <summary> | |
/// Quantity of images on the Y axis. | |
/// </summary> | |
[XmlElement("tilesy")] | |
public int TilesY; | |
/// <summary> | |
/// The sprite as base64 string. | |
/// </summary> | |
[XmlElement("png")] | |
public string Png; | |
/// <summary> | |
/// Color used for the transparency. | |
/// </summary> | |
[XmlElement("transparency")] | |
public string Transparency; | |
} | |
/// <summary> | |
/// Node with a list of spawns. | |
/// </summary> | |
public class SpawnsNode | |
{ | |
/// <summary> | |
/// List of spawn nodes. | |
/// </summary> | |
[XmlElement("spawn")] | |
public SpawnNode[] Spawn; | |
} | |
/// <summary> | |
/// List of animations. | |
/// </summary> | |
public class AnimationsNode | |
{ | |
/// <summary> | |
/// List of animation nodes. | |
/// </summary> | |
[XmlElement("animation")] | |
public AnimationNode[] Animation; | |
} | |
/// <summary> | |
/// List of childs. | |
/// </summary> | |
public class ChildsNode | |
{ | |
/// <summary> | |
/// List of child nodes. | |
/// </summary> | |
[XmlElement("child")] | |
public ChildNode[] Child; | |
} | |
/// <summary> | |
/// List of sounds. | |
/// </summary> | |
public class SoundsNode | |
{ | |
/// <summary> | |
/// List of sound nodes. | |
/// </summary> | |
[XmlElement("sound")] | |
public SoundNode[] Sound; | |
} | |
/// <summary> | |
/// Information about the spawn. Used to start the pet on the screen. | |
/// </summary> | |
public class SpawnNode | |
{ | |
/// <summary> | |
/// Unique ID of spawn. | |
/// </summary> | |
[XmlAttribute("id")] | |
public int Id; | |
/// <summary> | |
/// Probability to use this spawn. | |
/// </summary> | |
[XmlAttribute("probability")] | |
public int Probability; | |
/// <summary> | |
/// X start position of the pet. | |
/// </summary> | |
[XmlElement("x")] | |
public string X; | |
/// <summary> | |
/// Y start position of the pet. | |
/// </summary> | |
[XmlElement("y")] | |
public string Y; | |
/// <summary> | |
/// ID of the next animation. | |
/// </summary> | |
[XmlElement("next")] | |
public NextNode Next; | |
} | |
/// <summary> | |
/// All information of each single animation is stored here. | |
/// </summary> | |
public class AnimationNode | |
{ | |
/// <summary> | |
/// Unique ID, used to set the next animation. | |
/// </summary> | |
[XmlAttribute("id")] | |
public int Id; | |
/// <summary> | |
/// Name for this animation. With some key-names special actions can be defined. Otherwise it is used for debug purposes. | |
/// </summary> | |
[XmlElement("name")] | |
public string Name; | |
/// <summary> | |
/// Information about the start position (velocity, opacity, ...). | |
/// </summary> | |
[XmlElement("start")] | |
public MovingNode Start; | |
/// <summary> | |
/// Information about the end position (velocity, opacity, ...). | |
/// </summary> | |
[XmlElement("end")] | |
public MovingNode End; | |
/// <summary> | |
/// Sequence of frames to play and information about how to play. | |
/// </summary> | |
[XmlElement("sequence")] | |
public SequenceNode Sequence; | |
/// <summary> | |
/// What to do if a pet reach a border of the screen or window. | |
/// </summary> | |
[XmlElement("border")] | |
public HitNode Border; | |
/// <summary> | |
/// What to do if a pet doesn't have any gravity anymore. | |
/// </summary> | |
[XmlElement("gravity")] | |
public HitNode Gravity; | |
} | |
/// <summary> | |
/// This is like a spawn but for second/child animation. | |
/// </summary> | |
public class ChildNode | |
{ | |
/// <summary> | |
/// Id of animation. Once that animation is executed, this child is automatically played. | |
/// </summary> | |
[XmlAttribute("animationid")] | |
public int Id; | |
/// <summary> | |
/// X position when it is created. | |
/// </summary> | |
[XmlElement("x")] | |
public string X; | |
/// <summary> | |
/// Y position when it is created. | |
/// </summary> | |
[XmlElement("y")] | |
public string Y; | |
/// <summary> | |
/// The next animation used for this child. | |
/// </summary> | |
[XmlElement("next")] | |
public int Next; | |
} | |
/// <summary> | |
/// To add sounds on some defined animations. | |
/// </summary> | |
public class SoundNode | |
{ | |
/// <summary> | |
/// Id of animation. Once that animation is executed, this sound is automatically played. | |
/// </summary> | |
[XmlAttribute("animationid")] | |
public int Id; | |
/// <summary> | |
/// Probability (in %) that this sound will be played together with the animation. | |
/// </summary> | |
[XmlElement("probability")] | |
public int Probability; | |
/// <summary> | |
/// How many times the sound should loop (default: 0). 1 means that the sound will play 2 times. | |
/// </summary> | |
[DefaultValue(0), XmlElement("loop")] | |
public int Loop; | |
/// <summary> | |
/// Base64 string of the mp3 sound. | |
/// </summary> | |
[XmlElement("base64")] | |
public string Base64; | |
} | |
/// <summary> | |
/// Information about the moves. | |
/// </summary> | |
/// <remarks> | |
/// There are 2 types: start and end. If a sequence has 10 frame sequences, the other 8 frames will interpolate | |
/// the values from start and end. | |
/// </remarks> | |
public class MovingNode | |
{ | |
/// <summary> | |
/// How many pixels to move in the X axis. | |
/// </summary> | |
[XmlElement("x")] | |
public string X; | |
/// <summary> | |
/// How many pixels to move in the Y axis. | |
/// </summary> | |
[XmlElement("y")] | |
public string Y; | |
/// <summary> | |
/// Graphical offset, from the physical position calculated for the pet. | |
/// </summary> | |
[XmlElement("offsety")] | |
public int OffsetY; | |
/// <summary> | |
/// Opacity from 0.0 (invisible) to 1.0 (opaque). | |
/// </summary> | |
[XmlElement("opacity")] | |
public double Opacity = 1.0; | |
/// <summary> | |
/// Interval until the next sequence is executed. | |
/// </summary> | |
[XmlElement("interval")] | |
public string Interval; | |
} | |
/// <summary> | |
/// Sequence node. | |
/// </summary> | |
public class SequenceNode | |
{ | |
/// <summary> | |
/// If repeat is > 0, repeat from indicate from which frame it should be repeated. | |
/// </summary> | |
[XmlAttribute("repeatfrom")] | |
public int RepeatFromFrame; | |
/// <summary> | |
/// How many times the sequence should be executed (value of 0 or 1 is NO-REPEAT). | |
/// </summary> | |
[XmlAttribute("repeat")] | |
public string RepeatCount; | |
/// <summary> | |
/// An array of images to show for this sequence. | |
/// </summary> | |
[XmlElement("frame")] | |
public int[] Frame; | |
/// <summary> | |
/// The next animation, once the sequence is over. | |
/// </summary> | |
[XmlElement("next")] | |
public NextNode[] Next; | |
/// <summary> | |
/// Action to execute if this animation is over. | |
/// </summary> | |
[XmlElement("action")] | |
public string Action; | |
} | |
/// <summary> | |
/// Hit node indicate an array of next animations if the pet hit a border or has no gravity. | |
/// </summary> | |
public class HitNode | |
{ | |
/// <summary> | |
/// List of next animations. | |
/// </summary> | |
[XmlElement("next")] | |
public NextNode[] Next; | |
} | |
/// <summary> | |
/// Next animation to play. Animation, Border or Gravity have 0 or more Next-nodes. | |
/// </summary> | |
public class NextNode | |
{ | |
/// <summary> | |
/// Probability this will be the next animation. | |
/// </summary> | |
[XmlAttribute("probability")] | |
public int Probability; | |
/// <summary> | |
/// Only flag, <see cref="TNextAnimation.TOnly"/>. | |
/// </summary> | |
[XmlAttribute("only")] | |
public string OnlyFlag; | |
/// <summary> | |
/// Next animation ID. | |
/// </summary> | |
[XmlText] | |
public int Value; | |
} | |
/// <summary> | |
/// Sprite sheet (PNG with all possible positions). | |
/// </summary> | |
public struct Images | |
{ | |
/// <summary> | |
/// Memory stream containing the PNG sprite sheet. | |
/// </summary> | |
public MemoryStream bitmapImages; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment