A little Windows Application for creating funscript files and/or converting them to the corresponding csv format.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FunScript.cs 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. using Newtonsoft.Json;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Text.RegularExpressions;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10. namespace Funscripta
  11. {
  12. public class FunScript
  13. {
  14. [JsonIgnore]
  15. public static int HistoryLimit { get; set; } = 100;
  16. public class HistoryEntry
  17. {
  18. public enum HistoryAction
  19. {
  20. Create, Update, Delete
  21. }
  22. public HistoryAction Action { get; set; }
  23. public long Timestamp { get; set; }
  24. public int? PositionBefore { get; set; }
  25. public int? PositionAfter { get; set; }
  26. }
  27. [JsonIgnore]
  28. public List<HistoryEntry> History { get; set; } = new List<HistoryEntry>();
  29. [JsonIgnore]
  30. public List<HistoryEntry> UndoHistory { get; set; } = new List<HistoryEntry>();
  31. [JsonProperty( "version" )]
  32. public string Version { get; set; } = "1.0";
  33. [JsonProperty( "actions" )]
  34. public LinkedList<Action> Actions { get; private set; } = new LinkedList<Action>();
  35. [JsonProperty( "inverted" )]
  36. public Boolean Inverted { get; set; } = false;
  37. [JsonProperty( "range" )]
  38. public int Range { get; set; } = 100;
  39. [JsonIgnore]
  40. public bool CanUndo => History.Count > 0;
  41. [JsonIgnore]
  42. public bool CanRedo => UndoHistory.Count > 0;
  43. internal FunScript()
  44. {
  45. AddAction( 0, 0 );
  46. }
  47. public Action Find( long timestamp )
  48. {
  49. var distance = long.MaxValue;
  50. Action _action = null;
  51. foreach( var action in Actions )
  52. {
  53. var dist = timestamp - action.Timestamp;
  54. if ( dist > -1 && dist < distance )
  55. {
  56. distance = dist;
  57. _action = action;
  58. }
  59. }
  60. if ( _action == null )
  61. {
  62. Actions.AddFirst( new Action( 0, 0 ) );
  63. return Find( 0 );
  64. }
  65. return _action;
  66. }
  67. public void AddAction( long timestamp, int position )
  68. {
  69. var a = Find( timestamp );
  70. if ( a.Timestamp == timestamp && a.Position != position )
  71. {
  72. HistoryAdd( HistoryEntry.HistoryAction.Update, a.Timestamp, a.Position, position );
  73. a.Position = position;
  74. }
  75. else if ( a.Position == position ) return;
  76. else
  77. {
  78. var _a = Actions.Find( a );
  79. Actions.AddAfter( _a, new Action( timestamp, position ) );
  80. HistoryAdd( HistoryEntry.HistoryAction.Create, timestamp, null, position );
  81. }
  82. }
  83. public void RemoveAction( long timestamp )
  84. {
  85. var a = Find( timestamp );
  86. if ( a.Timestamp != 0 )
  87. {
  88. Actions.Remove( Actions.Find( a ) );
  89. HistoryAdd( HistoryEntry.HistoryAction.Delete, a.Timestamp, a.Position, null );
  90. }
  91. }
  92. private void HistoryAdd( HistoryEntry.HistoryAction action, long timestamp, int? posBefore, int? posAfter )
  93. {
  94. History.Add( new HistoryEntry()
  95. {
  96. Action = action,
  97. Timestamp = timestamp,
  98. PositionBefore = posBefore,
  99. PositionAfter = posAfter
  100. }
  101. );
  102. if ( History.Count > HistoryLimit ) History.RemoveAt( 0 );
  103. UndoHistory.Clear();
  104. }
  105. private void UndoHistoryAdd( HistoryEntry historyEntry )
  106. {
  107. UndoHistory.Add( historyEntry );
  108. }
  109. public void Undo()
  110. {
  111. if ( !CanUndo ) return;
  112. var e = History.Last();
  113. History.Remove( e );
  114. switch( e.Action )
  115. {
  116. case HistoryEntry.HistoryAction.Create:
  117. Actions.Remove( Find( e.Timestamp ) );
  118. break;
  119. case HistoryEntry.HistoryAction.Update:
  120. Find( e.Timestamp ).Position = e.PositionBefore.Value;
  121. break;
  122. case HistoryEntry.HistoryAction.Delete:
  123. Actions.AddAfter( Actions.Find( Find( e.Timestamp ) ), new Action( e.Timestamp, e.PositionBefore.Value ) );
  124. break;
  125. }
  126. UndoHistoryAdd( e );
  127. }
  128. public void Redo()
  129. {
  130. if ( !CanRedo ) return;
  131. var e = UndoHistory.Last();
  132. UndoHistory.Remove( e );
  133. switch ( e.Action )
  134. {
  135. case HistoryEntry.HistoryAction.Create:
  136. Actions.AddAfter( Actions.Find( Find( e.Timestamp ) ), new Action( e.Timestamp, e.PositionAfter.Value ) );
  137. break;
  138. case HistoryEntry.HistoryAction.Update:
  139. Find( e.Timestamp ).Position = e.PositionAfter.Value;
  140. break;
  141. case HistoryEntry.HistoryAction.Delete:
  142. Actions.Remove( Find( e.Timestamp ) );
  143. break;
  144. }
  145. History.Add( e );
  146. }
  147. public static FunScript LoadFile( string file )
  148. {
  149. var fi = new FileInfo( file );
  150. switch( fi.Extension.Substring( 1 ).ToLower().Trim() )
  151. {
  152. default:
  153. throw new ArgumentException( $"Files of type { fi.Extension } can't be parsed.", "file" );
  154. case "funscript":
  155. return JsonConvert.DeserializeObject<FunScript>( File.ReadAllText( file ) );
  156. case "csv":
  157. var fs = new FunScript();
  158. foreach( var line in File.ReadAllLines( file ) ) {
  159. if ( line.StartsWith( "#" ) ) continue;
  160. var match = Regex.Match( line, @"([0-9\.]+),([0-9\.]+)(?:,.*)?", RegexOptions.Multiline );
  161. if ( match.Success )
  162. {
  163. try
  164. {
  165. fs.Actions.AddLast( new Action( long.Parse( match.Groups[ 1 ].Value ), int.Parse( match.Groups[ 2 ].Value ) ) );
  166. } catch ( Exception ex )
  167. {
  168. var i = 0;
  169. }
  170. }
  171. }
  172. return fs;
  173. }
  174. }
  175. public void SaveFile( string file )
  176. {
  177. var fi = new FileInfo( file );
  178. switch ( fi.Extension.Substring( 1 ).ToLower().Trim() )
  179. {
  180. default:
  181. throw new ArgumentException( $"Files of type { fi.Extension } can't be saved.", "file" );
  182. case "funscript":
  183. File.WriteAllText( file, JsonConvert.SerializeObject( this ) );
  184. break;
  185. case "csv":
  186. var lines = new LinkedList<string>();
  187. lines.AddLast( "#Compiled using Dragons Funscripta Tool" );
  188. foreach ( var action in Actions )
  189. {
  190. lines.AddLast( $"{ action.Timestamp },{ action.RelativePosition( Range, Inverted ) }" );
  191. }
  192. File.WriteAllLines( file, lines );
  193. break;
  194. }
  195. }
  196. public class Action
  197. {
  198. [JsonProperty( "pos" )]
  199. public int Position { get; set; }
  200. [JsonProperty( "at" )]
  201. public long Timestamp { get; private set; }
  202. public int RelativePosition( int range, bool inverted = false )
  203. {
  204. var pos = Position;
  205. if ( inverted ) pos = range - pos;
  206. return ( pos * 100 ) / range;
  207. }
  208. public Action( long timestamp, int position )
  209. {
  210. Position = position;
  211. Timestamp = timestamp;
  212. }
  213. }
  214. }
  215. }