added block name and line numbers to all exception output
This commit is contained in:
parent
46ec2df6df
commit
4fc90cf8b7
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
private static void Main(string[] args)
|
private static int Main(string[] args)
|
||||||
{
|
{
|
||||||
string infile = null;
|
string infile = null;
|
||||||
string output = null;
|
string output = null;
|
||||||
|
@ -23,6 +23,7 @@
|
||||||
if (args[0] == "/?" || args[0] == "/help" || args[0] == "-help" || args[0] == "--help")
|
if (args[0] == "/?" || args[0] == "/help" || args[0] == "-help" || args[0] == "--help")
|
||||||
{
|
{
|
||||||
ShowHelp();
|
ShowHelp();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (args.Length > 1)
|
else if (args.Length > 1)
|
||||||
|
@ -55,23 +56,24 @@
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Console.WriteLine(@"Unknown option: {0}", args[i]);
|
Console.WriteLine(@"Unknown option: {0}", args[i]);
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ShowHelp();
|
ShowHelp();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(format))
|
if (string.IsNullOrWhiteSpace(format) || string.IsNullOrWhiteSpace(infile))
|
||||||
{
|
{
|
||||||
ShowHelp();
|
ShowHelp();
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(infile) || !File.Exists(infile))
|
if (!File.Exists(infile))
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Source file {0} not found.", infile);
|
Console.WriteLine(@"Source file {0} not found.", infile);
|
||||||
return;
|
return 2;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(output))
|
if (string.IsNullOrWhiteSpace(output))
|
||||||
if (format == "html")
|
if (format == "html")
|
||||||
|
@ -82,14 +84,14 @@
|
||||||
if (!string.IsNullOrWhiteSpace(output) && (Directory.Exists(output) || File.Exists(output)))
|
if (!string.IsNullOrWhiteSpace(output) && (Directory.Exists(output) || File.Exists(output)))
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Specified output {0} already exists.", output);
|
Console.WriteLine(@"Specified output {0} already exists.", output);
|
||||||
return;
|
return 2;
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrWhiteSpace(tempdir))
|
if (!string.IsNullOrWhiteSpace(tempdir))
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(tempdir))
|
if (!Directory.Exists(tempdir))
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Template directory {0} does not exist.", tempdir);
|
Console.WriteLine(@"Template directory {0} does not exist.", tempdir);
|
||||||
return;
|
return 2;
|
||||||
}
|
}
|
||||||
if (!File.Exists(Path.Combine(tempdir, "index.html")) ||
|
if (!File.Exists(Path.Combine(tempdir, "index.html")) ||
|
||||||
!File.Exists(Path.Combine(tempdir, "scene.html")) ||
|
!File.Exists(Path.Combine(tempdir, "scene.html")) ||
|
||||||
|
@ -103,7 +105,7 @@
|
||||||
if (!string.IsNullOrWhiteSpace(images) && !Directory.Exists(images))
|
if (!string.IsNullOrWhiteSpace(images) && !Directory.Exists(images))
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Images directory {0} does not exist.", images);
|
Console.WriteLine(@"Images directory {0} does not exist.", images);
|
||||||
return;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parser = new FicdownParser();
|
var parser = new FicdownParser();
|
||||||
|
@ -124,13 +126,13 @@
|
||||||
if (string.IsNullOrWhiteSpace(author))
|
if (string.IsNullOrWhiteSpace(author))
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Epub format requires the --author argument.");
|
Console.WriteLine(@"Epub format requires the --author argument.");
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
rend = new EpubRenderer(author);
|
rend = new EpubRenderer(author);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ShowHelp();
|
ShowHelp();
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(tempdir))
|
if (!string.IsNullOrWhiteSpace(tempdir))
|
||||||
|
@ -147,6 +149,7 @@
|
||||||
rend.Render(story, output, debug);
|
rend.Render(story, output, debug);
|
||||||
|
|
||||||
Console.WriteLine(@"Done.");
|
Console.WriteLine(@"Done.");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
|
|
||||||
public class UtilityTests
|
public class UtilityTests
|
||||||
{
|
{
|
||||||
|
private Utilities Utilities
|
||||||
|
{
|
||||||
|
get { return Utilities.GetInstance("none", 0); }
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FullAnchorMatches()
|
public void FullAnchorMatches()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@ -82,6 +81,9 @@
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<DependentUpon>Template.resx</DependentUpon>
|
<DependentUpon>Template.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Model\Parser\Line.cs" />
|
||||||
|
<Compile Include="Model\Parser\FicdownException.cs" />
|
||||||
|
<Compile Include="Parser\ParserExtensions.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
{
|
{
|
||||||
public BlockType Type { get; set; }
|
public BlockType Type { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public IList<string> Lines { get; set; }
|
public IList<Line> Lines { get; set; }
|
||||||
|
public int LineNumber { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
namespace Ficdown.Parser.Model.Parser
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class FicdownException : Exception
|
||||||
|
{
|
||||||
|
private string _blockName;
|
||||||
|
private int? _lineNumber;
|
||||||
|
private string _message;
|
||||||
|
|
||||||
|
public FicdownException(string blockName, int? lineNumber, string message) : base(message)
|
||||||
|
{
|
||||||
|
_blockName = blockName;
|
||||||
|
_lineNumber = lineNumber;
|
||||||
|
_message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FicdownException(string message) : base(message)
|
||||||
|
{
|
||||||
|
_message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(_blockName)
|
||||||
|
? string.Format("Error in block \"{0}\" (Line {1}): {2}",
|
||||||
|
_blockName,
|
||||||
|
_lineNumber.HasValue
|
||||||
|
? _lineNumber.ToString()
|
||||||
|
: "unknown", _message)
|
||||||
|
: string.Format("Error: {0}", _message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ficdown.Parser.Model.Parser
|
||||||
|
{
|
||||||
|
public class Line
|
||||||
|
{
|
||||||
|
public int Number { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,5 +5,6 @@
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Toggle { get; set; }
|
public string Toggle { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
public int LineNumber { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,6 @@
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public IDictionary<string, bool> Conditions { get; set; }
|
public IDictionary<string, bool> Conditions { get; set; }
|
||||||
|
public int LineNumber { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
{
|
{
|
||||||
var blocks = new List<Block>();
|
var blocks = new List<Block>();
|
||||||
Block currentBlock = null;
|
Block currentBlock = null;
|
||||||
|
var lineNum = 1;
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
{
|
{
|
||||||
var match = Regex.Match(line, @"^(?<level>#{1,3})\s+(?<name>[^#].*)$");
|
var match = Regex.Match(line, @"^(?<level>#{1,3})\s+(?<name>[^#].*)$");
|
||||||
|
@ -24,12 +25,17 @@
|
||||||
{
|
{
|
||||||
Type = (BlockType) match.Groups["level"].Length,
|
Type = (BlockType) match.Groups["level"].Length,
|
||||||
Name = match.Groups["name"].Value,
|
Name = match.Groups["name"].Value,
|
||||||
Lines = new List<string>()
|
Lines = new List<Line>(),
|
||||||
|
LineNumber = lineNum++
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (currentBlock != null) currentBlock.Lines.Add(line);
|
if (currentBlock != null) currentBlock.Lines.Add(new Line
|
||||||
|
{
|
||||||
|
Number = lineNum++,
|
||||||
|
Text = line
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentBlock != null) blocks.Add(currentBlock);
|
if (currentBlock != null) blocks.Add(currentBlock);
|
||||||
|
@ -40,14 +46,13 @@
|
||||||
{
|
{
|
||||||
// get the story
|
// get the story
|
||||||
var storyBlock = blocks.SingleOrDefault(b => b.Type == BlockType.Story);
|
var storyBlock = blocks.SingleOrDefault(b => b.Type == BlockType.Story);
|
||||||
if(storyBlock == null) throw new FormatException("No story block found");
|
if(storyBlock == null) throw new FicdownException("No story block found");
|
||||||
|
|
||||||
var storyAnchor = Utilities.ParseAnchor(storyBlock.Name);
|
var storyAnchor = Utilities.GetInstance(storyBlock.Name, storyBlock.LineNumber).ParseAnchor(storyBlock.Name);
|
||||||
|
|
||||||
if (storyAnchor.Href.Target == null || storyAnchor.Href.Conditions != null ||
|
if (storyAnchor.Href.Target == null || storyAnchor.Href.Conditions != null ||
|
||||||
storyAnchor.Href.Toggles != null)
|
storyAnchor.Href.Toggles != null)
|
||||||
throw new FormatException(string.Format("Story href should only have target: {0}",
|
throw new FicdownException(storyBlock.Name, storyBlock.LineNumber, "Story href should only have target");
|
||||||
storyAnchor.Original));
|
|
||||||
|
|
||||||
var story = new Story
|
var story = new Story
|
||||||
{
|
{
|
||||||
|
@ -69,7 +74,7 @@
|
||||||
blocks.Where(b => b.Type == BlockType.Action).Select(b => BlockToAction(b, aid++)).ToDictionary(a => a.Toggle, a => a);
|
blocks.Where(b => b.Type == BlockType.Action).Select(b => BlockToAction(b, aid++)).ToDictionary(a => a.Toggle, a => a);
|
||||||
|
|
||||||
if (!story.Scenes.ContainsKey(storyAnchor.Href.Target))
|
if (!story.Scenes.ContainsKey(storyAnchor.Href.Target))
|
||||||
throw new FormatException(string.Format("Story targets non-existent scene: {0}", storyAnchor.Href.Target));
|
throw new FicdownException(storyBlock.Name, storyBlock.LineNumber, string.Format("Story targets non-existent scene: {0}", storyAnchor.Href.Target));
|
||||||
story.FirstScene = storyAnchor.Href.Target;
|
story.FirstScene = storyAnchor.Href.Target;
|
||||||
|
|
||||||
return story;
|
return story;
|
||||||
|
@ -81,22 +86,23 @@
|
||||||
var scene = new Scene
|
var scene = new Scene
|
||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
Description = string.Join("\n", block.Lines).Trim()
|
LineNumber = block.LineNumber,
|
||||||
|
Description = string.Join("\n", block.Lines.Select(l => l.Text)).Trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sceneName = Utilities.ParseAnchor(block.Name);
|
var sceneName = Utilities.GetInstance(block.Name, block.LineNumber).ParseAnchor(block.Name);
|
||||||
scene.Name = sceneName.Title != null ? sceneName.Title.Trim() : sceneName.Text.Trim();
|
scene.Name = sceneName.Title != null ? sceneName.Title.Trim() : sceneName.Text.Trim();
|
||||||
scene.Key = Utilities.NormalizeString(sceneName.Text);
|
scene.Key = Utilities.GetInstance(block.Name, block.LineNumber).NormalizeString(sceneName.Text);
|
||||||
if(sceneName.Href.Target != null || sceneName.Href.Toggles != null)
|
if(sceneName.Href.Target != null || sceneName.Href.Toggles != null)
|
||||||
throw new FormatException(string.Format("Scene href should only have conditions: {0}", block.Name));
|
throw new FicdownException(block.Name, block.LineNumber, string.Format("Scene href should only have conditions: {0}", block.Name));
|
||||||
scene.Conditions = sceneName.Href.Conditions;
|
scene.Conditions = sceneName.Href.Conditions;
|
||||||
}
|
}
|
||||||
catch(FormatException)
|
catch(FicdownException)
|
||||||
{
|
{
|
||||||
scene.Name = block.Name.Trim();
|
scene.Name = block.Name.Trim();
|
||||||
scene.Key = Utilities.NormalizeString(block.Name);
|
scene.Key = Utilities.GetInstance(block.Name, block.LineNumber).NormalizeString(block.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return scene;
|
return scene;
|
||||||
|
@ -107,8 +113,9 @@
|
||||||
return new Action
|
return new Action
|
||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
Toggle = Utilities.NormalizeString(block.Name),
|
Toggle = Utilities.GetInstance(block.Name, block.LineNumber).NormalizeString(block.Name),
|
||||||
Description = string.Join("\n", block.Lines).Trim()
|
Description = string.Join("\n", block.Lines.Select(l => l.Text)).Trim(),
|
||||||
|
LineNumber = block.LineNumber
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
namespace Ficdown.Parser.Parser
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
public static class ParserExtensions
|
||||||
|
{
|
||||||
|
public static string ToHrefString(this IDictionary<string, bool> values, string separator)
|
||||||
|
{
|
||||||
|
return values != null
|
||||||
|
? string.Join(separator,
|
||||||
|
values.Where(v => !v.Key.StartsWith(">"))
|
||||||
|
.Select(v => string.Format("{0}{1}", v.Value ? null : "!", v.Key))
|
||||||
|
.ToArray())
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToHrefString(this IEnumerable<string> values, string separator)
|
||||||
|
{
|
||||||
|
return values != null ? string.Join(separator, values.ToArray()) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,13 +40,13 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveAnchor(Anchor anchor, IDictionary<string, bool> playerState, string targetHash)
|
private string ResolveAnchor(string blockName, int lineNumber, Anchor anchor, IDictionary<string, bool> playerState, string targetHash)
|
||||||
{
|
{
|
||||||
var text = anchor.Text;
|
var text = anchor.Text;
|
||||||
if (anchor.Href.Conditions != null)
|
if (anchor.Href.Conditions != null)
|
||||||
{
|
{
|
||||||
var satisfied = Utilities.ConditionsMet(playerState, anchor.Href.Conditions);
|
var satisfied = Utilities.GetInstance(blockName, lineNumber).ConditionsMet(playerState, anchor.Href.Conditions);
|
||||||
var alts = Utilities.ParseConditionalText(text);
|
var alts = Utilities.GetInstance(blockName, lineNumber).ParseConditionalText(text);
|
||||||
var replace = alts[satisfied];
|
var replace = alts[satisfied];
|
||||||
text = RegexLib.EscapeChar.Replace(replace, string.Empty);
|
text = RegexLib.EscapeChar.Replace(replace, string.Empty);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
if (page.State.ActionsToShow[i])
|
if (page.State.ActionsToShow[i])
|
||||||
{
|
{
|
||||||
var actionTuple = _story.Actions.Single(a => a.Value.Id == i + 1);
|
var actionTuple = _story.Actions.Single(a => a.Value.Id == i + 1);
|
||||||
var actionAnchors = Utilities.ParseAnchors(actionTuple.Value.Description);
|
var actionAnchors = Utilities.GetInstance(page.Scene.Name, page.Scene.LineNumber).ParseAnchors(actionTuple.Value.Description);
|
||||||
var anchorDict = GetStateDictionary(page);
|
var anchorDict = GetStateDictionary(page);
|
||||||
if (
|
if (
|
||||||
actionAnchors.Any(
|
actionAnchors.Any(
|
||||||
|
@ -80,19 +80,19 @@
|
||||||
resolved.AppendFormat("{0}\n\n", actionAnchors.Aggregate(actionTuple.Value.Description,
|
resolved.AppendFormat("{0}\n\n", actionAnchors.Aggregate(actionTuple.Value.Description,
|
||||||
(current, anchor) =>
|
(current, anchor) =>
|
||||||
current.Replace(anchor.Original,
|
current.Replace(anchor.Original,
|
||||||
ResolveAnchor(anchor, anchorDict,
|
ResolveAnchor(page.Scene.Name, page.Scene.LineNumber, anchor, anchorDict,
|
||||||
page.Links.ContainsKey(anchor.Original) ? page.Links[anchor.Original] : null))));
|
page.Links.ContainsKey(anchor.Original) ? page.Links[anchor.Original] : null))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var anchors = Utilities.ParseAnchors(page.Scene.Description);
|
var anchors = Utilities.GetInstance(page.Scene.Name, page.Scene.LineNumber).ParseAnchors(page.Scene.Description);
|
||||||
var stateDict = GetStateDictionary(page);
|
var stateDict = GetStateDictionary(page);
|
||||||
var text =
|
var text =
|
||||||
RegexLib.EmptyListItem.Replace(
|
RegexLib.EmptyListItem.Replace(
|
||||||
anchors.Aggregate(page.Scene.Description,
|
anchors.Aggregate(page.Scene.Description,
|
||||||
(current, anchor) =>
|
(current, anchor) =>
|
||||||
current.Replace(anchor.Original,
|
current.Replace(anchor.Original,
|
||||||
ResolveAnchor(anchor, stateDict,
|
ResolveAnchor(page.Scene.Name, page.Scene.LineNumber, anchor, stateDict,
|
||||||
page.Links.ContainsKey(anchor.Original) ? page.Links[anchor.Original] : null))),
|
page.Links.ContainsKey(anchor.Original) ? page.Links[anchor.Original] : null))),
|
||||||
string.Empty);
|
string.Empty);
|
||||||
var seen = page.State.ScenesSeen[page.Scene.Id - 1];
|
var seen = page.State.ScenesSeen[page.Scene.Id - 1];
|
||||||
|
|
|
@ -7,14 +7,34 @@ namespace Ficdown.Parser.Parser
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Model.Parser;
|
using Model.Parser;
|
||||||
|
|
||||||
internal static class Utilities
|
internal class Utilities
|
||||||
{
|
{
|
||||||
public static string NormalizeString(string raw)
|
public static Utilities GetInstance(string blockName, int lineNumber)
|
||||||
|
{
|
||||||
|
return new Utilities
|
||||||
|
{
|
||||||
|
_blockName = blockName,
|
||||||
|
_lineNumber = lineNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Utilities GetInstance(string blockName)
|
||||||
|
{
|
||||||
|
return new Utilities
|
||||||
|
{
|
||||||
|
_blockName = blockName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string _blockName;
|
||||||
|
protected int? _lineNumber;
|
||||||
|
|
||||||
|
public string NormalizeString(string raw)
|
||||||
{
|
{
|
||||||
return Regex.Replace(Regex.Replace(raw.ToLower(), @"^\W+|\W+$", string.Empty), @"\W+", "-");
|
return Regex.Replace(Regex.Replace(raw.ToLower(), @"^\W+|\W+$", string.Empty), @"\W+", "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Href ParseHref(string href)
|
private Href ParseHref(string href)
|
||||||
{
|
{
|
||||||
var match = RegexLib.Href.Match(href);
|
var match = RegexLib.Href.Match(href);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
|
@ -37,23 +57,23 @@ namespace Ficdown.Parser.Parser
|
||||||
: null
|
: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw new FormatException(string.Format("Invalid href: {0}", href));
|
throw new FicdownException(_blockName, _lineNumber, string.Format("Invalid href: {0}", href));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Anchor ParseAnchor(string anchorText)
|
public Anchor ParseAnchor(string anchorText)
|
||||||
{
|
{
|
||||||
var match = RegexLib.Anchors.Match(anchorText);
|
var match = RegexLib.Anchors.Match(anchorText);
|
||||||
if (!match.Success) throw new FormatException(string.Format("Invalid anchor: {0}", anchorText));
|
if (!match.Success) throw new FicdownException(_blockName, _lineNumber, string.Format("Invalid anchor: {0}", anchorText));
|
||||||
return MatchToAnchor(match);
|
return MatchToAnchor(match);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IList<Anchor> ParseAnchors(string text)
|
public IList<Anchor> ParseAnchors(string text)
|
||||||
{
|
{
|
||||||
var matches = RegexLib.Anchors.Matches(text);
|
var matches = RegexLib.Anchors.Matches(text);
|
||||||
return matches.Cast<Match>().Select(MatchToAnchor).ToList();
|
return matches.Cast<Match>().Select(MatchToAnchor).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Anchor MatchToAnchor(Match match)
|
private Anchor MatchToAnchor(Match match)
|
||||||
{
|
{
|
||||||
var astr = match.Groups["anchor"].Value;
|
var astr = match.Groups["anchor"].Value;
|
||||||
var txstr = match.Groups["text"].Value;
|
var txstr = match.Groups["text"].Value;
|
||||||
|
@ -73,10 +93,10 @@ namespace Ficdown.Parser.Parser
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDictionary<bool, string> ParseConditionalText(string text)
|
public IDictionary<bool, string> ParseConditionalText(string text)
|
||||||
{
|
{
|
||||||
var match = RegexLib.ConditionalText.Match(text);
|
var match = RegexLib.ConditionalText.Match(text);
|
||||||
if (!match.Success) throw new FormatException(string.Format(@"Invalid conditional text: {0}", text));
|
if (!match.Success) throw new FicdownException(_blockName, _lineNumber, string.Format(@"Invalid conditional text: {0}", text));
|
||||||
return new Dictionary<bool, string>
|
return new Dictionary<bool, string>
|
||||||
{
|
{
|
||||||
{true, match.Groups["true"].Value},
|
{true, match.Groups["true"].Value},
|
||||||
|
@ -84,22 +104,7 @@ namespace Ficdown.Parser.Parser
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToHrefString(this IDictionary<string, bool> values, string separator)
|
public bool ConditionsMet(IDictionary<string, bool> playerState, IDictionary<string, bool> conditions)
|
||||||
{
|
|
||||||
return values != null
|
|
||||||
? string.Join(separator,
|
|
||||||
values.Where(v => !v.Key.StartsWith(">"))
|
|
||||||
.Select(v => string.Format("{0}{1}", v.Value ? null : "!", v.Key))
|
|
||||||
.ToArray())
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToHrefString(this IEnumerable<string> values, string separator)
|
|
||||||
{
|
|
||||||
return values != null ? string.Join(separator, values.ToArray()) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ConditionsMet(IDictionary<string, bool> playerState, IDictionary<string, bool> conditions)
|
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
conditions.All(
|
conditions.All(
|
||||||
|
|
|
@ -99,9 +99,9 @@
|
||||||
{
|
{
|
||||||
var states = new HashSet<string>();
|
var states = new HashSet<string>();
|
||||||
|
|
||||||
var anchors = Utilities.ParseAnchors(currentState.Page.Scene.Description).ToList();
|
var anchors = Utilities.GetInstance(currentState.Page.Scene.Name, currentState.Page.Scene.LineNumber).ParseAnchors(currentState.Page.Scene.Description).ToList();
|
||||||
foreach (var action in GetActionsForPage(currentState.Page))
|
foreach (var action in GetActionsForPage(currentState.Page))
|
||||||
anchors.AddRange(Utilities.ParseAnchors(action.Description));
|
anchors.AddRange(Utilities.GetInstance(action.Toggle, action.LineNumber).ParseAnchors(action.Description));
|
||||||
var conditionals =
|
var conditionals =
|
||||||
anchors.SelectMany(
|
anchors.SelectMany(
|
||||||
a => a.Href.Conditions != null ? a.Href.Conditions.Select(c => c.Key) : new string[] {})
|
a => a.Href.Conditions != null ? a.Href.Conditions.Select(c => c.Key) : new string[] {})
|
||||||
|
@ -126,8 +126,8 @@
|
||||||
// don't follow links that would be hidden
|
// don't follow links that would be hidden
|
||||||
if (anchor.Href.Conditions != null &&
|
if (anchor.Href.Conditions != null &&
|
||||||
string.IsNullOrEmpty(
|
string.IsNullOrEmpty(
|
||||||
Utilities.ParseConditionalText(anchor.Text)[
|
Utilities.GetInstance(currentState.Page.Scene.Name, currentState.Page.Scene.LineNumber).ParseConditionalText(anchor.Text)[
|
||||||
Utilities.ConditionsMet(StateResolver.GetStateDictionary(currentState.Page),
|
Utilities.GetInstance(currentState.Page.Scene.Name, currentState.Page.Scene.LineNumber).ConditionsMet(StateResolver.GetStateDictionary(currentState.Page),
|
||||||
anchor.Href.Conditions)])) continue;
|
anchor.Href.Conditions)])) continue;
|
||||||
|
|
||||||
var newState = _manager.ResolveNewState(anchor, currentState.Page);
|
var newState = _manager.ResolveNewState(anchor, currentState.Page);
|
||||||
|
|
|
@ -21,14 +21,14 @@
|
||||||
_story = story;
|
_story = story;
|
||||||
var allScenes = _story.Scenes.SelectMany(s => s.Value);
|
var allScenes = _story.Scenes.SelectMany(s => s.Value);
|
||||||
_sceneCount = allScenes.Max(s => s.Id);
|
_sceneCount = allScenes.Max(s => s.Id);
|
||||||
_actionCount = _story.Actions.Max(a => a.Value.Id);
|
_actionCount = _story.Actions.Count > 0 ? _story.Actions.Max(a => a.Value.Id) : 0;
|
||||||
_stateMatrix = new Dictionary<string, int>();
|
_stateMatrix = new Dictionary<string, int>();
|
||||||
var state = 0;
|
var state = 0;
|
||||||
foreach (
|
foreach (
|
||||||
var toggle in
|
var toggle in
|
||||||
allScenes.SelectMany(
|
allScenes.SelectMany(
|
||||||
sc =>
|
sc =>
|
||||||
Utilities.ParseAnchors(sc.Description)
|
Utilities.GetInstance(sc.Name, sc.LineNumber).ParseAnchors(sc.Description)
|
||||||
.SelectMany(
|
.SelectMany(
|
||||||
a =>
|
a =>
|
||||||
a.Href.Toggles != null
|
a.Href.Toggles != null
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
if(actionFirstToggles == null) actionFirstToggles = new List<bool>();
|
if(actionFirstToggles == null) actionFirstToggles = new List<bool>();
|
||||||
newState.State.ActionsToShow[_story.Actions[toggle].Id - 1] = true;
|
newState.State.ActionsToShow[_story.Actions[toggle].Id - 1] = true;
|
||||||
if (
|
if (
|
||||||
Utilities.ParseAnchors(_story.Actions[toggle].Description)
|
Utilities.GetInstance(_story.Actions[toggle].Toggle, _story.Actions[toggle].LineNumber).ParseAnchors(_story.Actions[toggle].Description)
|
||||||
.Any(a => a.Href.Conditions != null && a.Href.Conditions.ContainsKey(toggle)))
|
.Any(a => a.Href.Conditions != null && a.Href.Conditions.ContainsKey(toggle)))
|
||||||
actionFirstToggles.Add(!current.State.PlayerState[_stateMatrix[toggle]]);
|
actionFirstToggles.Add(!current.State.PlayerState[_stateMatrix[toggle]]);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
newState.State.ActionFirstToggles = actionFirstToggles != null
|
newState.State.ActionFirstToggles = actionFirstToggles != null
|
||||||
? new BitArray(actionFirstToggles.ToArray())
|
? new BitArray(actionFirstToggles.ToArray())
|
||||||
: null;
|
: null;
|
||||||
newState.Scene = GetScene(target, newState.State.PlayerState);
|
newState.Scene = GetScene(current.Scene.Name, current.Scene.LineNumber, target, newState.State.PlayerState);
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,10 +138,10 @@
|
||||||
return GetUniqueHash(compressed, page.Scene.Key);
|
return GetUniqueHash(compressed, page.Scene.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scene GetScene(string target, BitArray playerState)
|
private Scene GetScene(string blockName, int lineNumber, string target, BitArray playerState)
|
||||||
{
|
{
|
||||||
if (!_story.Scenes.ContainsKey(target))
|
if (!_story.Scenes.ContainsKey(target))
|
||||||
throw new FormatException(string.Format("Encountered link to non-existant scene: {0}", target));
|
throw new FicdownException(blockName, lineNumber, string.Format("Encountered link to non-existent scene: {0}", target));
|
||||||
|
|
||||||
Scene newScene = null;
|
Scene newScene = null;
|
||||||
foreach (var scene in _story.Scenes[target])
|
foreach (var scene in _story.Scenes[target])
|
||||||
|
@ -154,13 +154,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newScene == null)
|
if (newScene == null)
|
||||||
throw new FormatException(string.Format("Scene {0} reached with unmatched player state", target));
|
throw new FicdownException(blockName, lineNumber, string.Format("Scene {0} reached with unmatched player state", target));
|
||||||
return newScene;
|
return newScene;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ConditionsMatch(Scene scene, BitArray playerState)
|
private bool ConditionsMatch(Scene scene, BitArray playerState)
|
||||||
{
|
{
|
||||||
if (scene.Conditions == null) return true;
|
if (scene.Conditions == null) return true;
|
||||||
|
scene.Conditions.ToList().ForEach(c =>
|
||||||
|
{
|
||||||
|
if(!_stateMatrix.ContainsKey(c.Key)) throw new FicdownException(scene.Name, scene.LineNumber, string.Format("Reference to non-existent state: {0}", c.Key));
|
||||||
|
});
|
||||||
return scene.Conditions.All(c => playerState[_stateMatrix[c.Key]] == c.Value);
|
return scene.Conditions.All(c => playerState[_stateMatrix[c.Key]] == c.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,91 @@
|
||||||
namespace Ficdown.Parser.Render
|
namespace Ficdown.Parser.Render
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Epub4Net;
|
using Epub4Net;
|
||||||
|
using Ionic.Zip;
|
||||||
|
using Ionic.Zlib;
|
||||||
|
|
||||||
|
#region fix for bug in epub4net
|
||||||
|
// https://bitbucket.org/dalager/epub4net/issues/2/windows-path-separator-hard-coded-in
|
||||||
|
|
||||||
|
public class FixedFileSystemManager : IFileSystemManager
|
||||||
|
{
|
||||||
|
private FileSystemManager _fsm;
|
||||||
|
|
||||||
|
public FixedFileSystemManager()
|
||||||
|
{
|
||||||
|
_fsm = new FileSystemManager(Guid.NewGuid().ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetupOutputDir()
|
||||||
|
{
|
||||||
|
_fsm.SetupOutputDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ContentDir
|
||||||
|
{
|
||||||
|
get { return _fsm.ContentDir; }
|
||||||
|
set { _fsm.ContentDir = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BuildDirectory
|
||||||
|
{
|
||||||
|
get { return _fsm.BuildDirectory; }
|
||||||
|
set { _fsm.BuildDirectory = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateTocFile(Epub epub)
|
||||||
|
{
|
||||||
|
_fsm.CreateTocFile(epub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateContentOpfFile(Epub epub)
|
||||||
|
{
|
||||||
|
_fsm.CreateContentOpfFile(epub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyChapterFilesToContentFolder(Epub epub)
|
||||||
|
{
|
||||||
|
_fsm.CopyChapterFilesToContentFolder(epub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ZipEpub(Epub epub)
|
||||||
|
{
|
||||||
|
var epubFilename = epub.Title + ".epub";
|
||||||
|
if(File.Exists(epubFilename)) File.Delete(epubFilename);
|
||||||
|
using(var zip = new ZipFile(epubFilename, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
zip.EmitTimesInWindowsFormatWhenSaving = false;
|
||||||
|
zip.CompressionLevel = CompressionLevel.None;
|
||||||
|
zip.AddFile(Path.Combine(_fsm.BuildDirectory, "mimetype"), "\\");
|
||||||
|
zip.Save();
|
||||||
|
File.Delete(Path.Combine(_fsm.BuildDirectory, "mimetype"));
|
||||||
|
zip.AddDirectory(_fsm.BuildDirectory);
|
||||||
|
zip.Save();
|
||||||
|
}
|
||||||
|
return epubFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyResourceFilesToContentFolder(Epub epub)
|
||||||
|
{
|
||||||
|
_fsm.CopyResourceFilesToContentFolder(epub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ValidatePathsExists(IEnumerable<IPathed> fileList)
|
||||||
|
{
|
||||||
|
_fsm.ValidatePathsExists(fileList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteBuildDir()
|
||||||
|
{
|
||||||
|
_fsm.DeleteBuildDir();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public class EpubRenderer : HtmlRenderer
|
public class EpubRenderer : HtmlRenderer
|
||||||
{
|
{
|
||||||
|
@ -46,7 +127,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder = new EPubBuilder();
|
var builder = new EPubBuilder(new FixedFileSystemManager(), Guid.NewGuid().ToString());
|
||||||
var built = builder.Build(epub);
|
var built = builder.Build(epub);
|
||||||
|
|
||||||
File.Move(built, outPath);
|
File.Move(built, outPath);
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
File.WriteAllText(Path.Combine(outPath, "styles.css"), StylesTemplate ?? Template.Styles);
|
File.WriteAllText(Path.Combine(outPath, "styles.css"), StylesTemplate ?? Template.Styles);
|
||||||
|
|
||||||
var content = page.Content;
|
var content = page.Content;
|
||||||
foreach (var anchor in Utilities.ParseAnchors(page.Content))
|
foreach (var anchor in Utilities.GetInstance(page.Name).ParseAnchors(page.Content))
|
||||||
{
|
{
|
||||||
var newAnchor = string.Format("[{0}]({1}.html)", anchor.Text, anchor.Href.Target);
|
var newAnchor = string.Format("[{0}]({1}.html)", anchor.Text, anchor.Href.Target);
|
||||||
content = content.Replace(anchor.Original, newAnchor);
|
content = content.Replace(anchor.Original, newAnchor);
|
||||||
|
|
Loading…
Reference in New Issue