PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ZipPla/ZipPlaInfo.cs

https://bitbucket.org/udaken/zippla-mirror
C# | 371 lines | 332 code | 23 blank | 16 comment | 63 complexity | c67d90d102afe1e9e0d3835d4481bff1 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using System.Threading.Tasks;
  8. namespace ZipPla
  9. {
  10. class ZipPlaInfo
  11. {
  12. public bool CoverInfoForArchive = true;
  13. // Length >= 1 であること
  14. private static readonly ZipPlaInfoParam[] zipPlaInfoParamsPrototype = new ZipPlaInfoParam[]
  15. {
  16. //new ZipPlaInfoParam("c", @"(\d{1,9})(\.\d+)?", // \d は全角の数字なども含む
  17. new ZipPlaInfoParam("c", @"([0-9]{1,9})(\.[0-9]+)?",
  18. (s)=>Tuple.Create(int.Parse(s[0]), string.IsNullOrEmpty(s[1])?0.0:double.Parse("0" + s[1])),
  19. (o)=>
  20. {
  21. var t = o as Tuple<int, double>;
  22. var v1 = t.Item1 < 0 ? 0 : t.Item1;
  23. var v2 = t.Item2 < 0 ? 0.0 : t.Item2 > 0.999 ? 0.999 : t.Item2;
  24. var result = v1 + v2.ToString("F3").Substring(1);
  25. while(result.EndsWith("0")) result = result.Substring(0, result.Length - 1);
  26. if(result.EndsWith(".")) result = result.Substring(0, result.Length - 1);
  27. return result;
  28. }),
  29. new ZipPlaInfoParam("b", @"(r|l|n)?",
  30. (s)=> { var s0 = s[0].ToLower(); return s0 == "l" ? BindingMode.LeftToRight : s0 == "r" ? BindingMode.RightToLeft : BindingMode.SinglePage; },
  31. (o)=> {var b = (BindingMode)o; return b == BindingMode.LeftToRight ? "l" : b == BindingMode.RightToLeft ? "r" : "n"; }),
  32. new ZipPlaInfoParam("r", @"([1-5])",
  33. (s)=>int.Parse(s[0]),
  34. (o)=>o.ToString()),
  35. new ZipPlaInfoParam("t", @"((?:[^,]+,)*[^,]+,?)",
  36. //(s)=>(from ss in s[0].Split(new char[1] {','}, StringSplitOptions.RemoveEmptyEntries) select ss.Trim()).OrderBy(entry => entry, new LogicalStringComparer()).ToArray(),
  37. //(o)=>string.Join(",", (o as string[]).OrderBy(entry => entry, new LogicalStringComparer()).ToArray()))
  38. (s)=>(from ss in s[0].Split(new char[1] {','}, StringSplitOptions.RemoveEmptyEntries) select ss.Trim()).ToArray(),
  39. (o)=>string.Join(",", o as string[])),
  40. new ZipPlaInfoParam("d", @"(l|r)?",
  41. (s)=>s[0].ToLower() == "r" ? BindingMode.LeftToRight : BindingMode.RightToLeft,
  42. (o)=>((BindingMode)o) == BindingMode.LeftToRight ? "r" : "l"),
  43. };
  44. private void FixLeftToRightInfo()
  45. {
  46. if (zipPlaInfoParams[4].Value is bool v)
  47. {
  48. var p = zipPlaInfoParams[1];
  49. if(p.Value as bool? == null) p.Value = v;
  50. zipPlaInfoParams[4].Value = null;
  51. }
  52. }
  53. private readonly ZipPlaInfoParam[] zipPlaInfoParams;
  54. public Tuple<int, double> ThumbnailInfo
  55. {
  56. get => zipPlaInfoParams[0].Value as Tuple<int, double>;
  57. set { zipPlaInfoParams[0].Value = value; FixLeftToRightInfo(); }
  58. }
  59. public BindingMode? BindingModeForSet
  60. {
  61. get => zipPlaInfoParams[1].Value as BindingMode? ?? zipPlaInfoParams[4].Value as BindingMode?;
  62. set
  63. {
  64. zipPlaInfoParams[1].Value = value;
  65. zipPlaInfoParams[4].Value = null;
  66. }
  67. }
  68. public int? Rating
  69. {
  70. get => zipPlaInfoParams[2].Value as int?;
  71. set { zipPlaInfoParams[2].Value = (value == null || (0 < value && value <= 5)) ? value : null; FixLeftToRightInfo(); }
  72. }
  73. public string[] TagArray
  74. {
  75. get => zipPlaInfoParams[3].Value as string[];
  76. set { zipPlaInfoParams[3].Value = value; FixLeftToRightInfo(); }
  77. }
  78. private readonly Tuple<int, int>[] indexLengthPairs;
  79. private readonly string path;
  80. public Tuple<int,int,string>[] GetIndexLengthTagTorios()
  81. {
  82. #if AUTOBUILD
  83. try
  84. #endif
  85. {
  86. var tags = TagArray;
  87. if (tags == null) return new Tuple<int, int, string>[0];
  88. var tagsCount = tags.Length;
  89. if (tagsCount == 0) return new Tuple<int, int, string>[0];
  90. var result = new Tuple<int, int, string>[tagsCount];
  91. var pair = indexLengthPairs[3];
  92. var index0 = pair.Item1 + 2;
  93. var pathLength = path.Length;
  94. while (index0 < pathLength && path[index0] == ' ') index0++;
  95. var fullLength = pair.Item2 - 2;
  96. var targetStr = path.Substring(index0, fullLength);
  97. //if(targetStr.Length < fullLength)
  98. //{
  99. // System.Windows.Forms.MessageBox.Show($"{targetStr}");
  100. //}
  101. var charArray = targetStr.ToArray();
  102. var j = 0;
  103. int index = 0;
  104. int index0FullLength = index0 + fullLength;
  105. for (var i = 0; i < fullLength; i++)
  106. {
  107. if (charArray[i] == ',')
  108. {
  109. var stop = i;
  110. while (stop > 0 && charArray[stop - 1] == ' ') stop--;
  111. var length = stop - index;
  112. if (length > 0)
  113. {
  114. result[j++] = new Tuple<int, int, string>(index + index0, length, targetStr.Substring(index, length));
  115. }
  116. while (i + 1 < fullLength && charArray[i + 1] == ' ') i++;
  117. index = i + 1;
  118. }
  119. }
  120. while (fullLength > 0 && charArray[fullLength - 1] == ' ') fullLength--;
  121. var len = fullLength - index;
  122. if (len > 0)
  123. {
  124. result[j++] = new Tuple<int, int, string>(index + index0, len, targetStr.Substring(index, len));
  125. }
  126. return result;
  127. }
  128. #if AUTOBUILD
  129. catch
  130. {
  131. return new Tuple<int, int, string>[0];
  132. }
  133. #endif
  134. }
  135. private static readonly Regex canBeTagRegex = new Regex(@"^(?:[^\s,;\\/:*?""<>{}|]|[^\s,;\\/:*?""<>{}|][^\t,;\\/:*?""<>{}|]*[^\s,;\\/:*?""<>{}|])$", RegexOptions.Compiled);
  136. public static bool CanBeTag(string tag)
  137. {
  138. return canBeTagRegex.IsMatch(tag);
  139. }
  140. public static bool CanBeTag(IEnumerable<string> tags)
  141. {
  142. return tags.All(tag => canBeTagRegex.IsMatch(tag));
  143. }
  144. //private static Regex getOnlyRatingRegex = new Regex(@"\{zpi\$(?:r|[^}]*;r)=([1-5]).*\}[^\\\/]*[\\\/]?$", RegexOptions.Compiled);
  145. private static readonly Regex getOnlyRatingRegex = new Regex(@"\{zpi\$\s*(?:r|[^}\\\/]*;\s*r)\s*=\s*([1-5])\s*(?:;[^}\\\/]*)?\}[^\\\/]*[\\\/]?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  146. public static int GetOnlyRating(string path)
  147. {
  148. var m = getOnlyRatingRegex.Match(path);
  149. if(m.Success)
  150. {
  151. return int.Parse(m.Groups[1].Value);
  152. }
  153. else
  154. {
  155. return -1;
  156. }
  157. }
  158. /// <summary>
  159. /// ランダムソートで使用することを想定し正確さよりも速さを優先
  160. /// </summary>
  161. public static string GetBasePathRoughly(string path)
  162. {
  163. if (path == null) return null;
  164. var start = path.IndexOf("{zpi$", StringComparison.OrdinalIgnoreCase);
  165. if (start < 0) return path;
  166. var startEnd = start + 5;
  167. if (path.Length <= startEnd) return path;
  168. if (path.IndexOf('\\', startEnd) >= 0) return path;
  169. var stop = path.IndexOf('}', startEnd);
  170. if (stop < 0) return path;
  171. var stopEnd = stop + 1;
  172. if (start > 0 && path[start - 1] == ' ') start--;
  173. return path.Substring(0, start) + path.Substring(stopEnd);
  174. }
  175. private static readonly Regex getOnlyPageSequenceFromFullNameRegex = new Regex(@"\{zpi\$\s*(?:b|[^}\\\/]*;\s*b)\s*=\s*(l|r|n)\s*(?:;[^}\\\/]*)?\}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  176. public static BindingMode? GetOnlyBindingModeFromFullName(string fullName)
  177. {
  178. var m = getOnlyPageSequenceFromFullNameRegex.Matches(fullName);
  179. if (m.Count > 0)
  180. {
  181. switch(m[m.Count - 1].Groups[1].Value.ToLower())
  182. {
  183. case "l": return BindingMode.LeftToRight;
  184. case "r": return BindingMode.RightToLeft;
  185. default: return BindingMode.SinglePage;
  186. }
  187. }
  188. else
  189. {
  190. return null;
  191. }
  192. }
  193. private static readonly Regex fileNameRegex;
  194. static ZipPlaInfo()
  195. {
  196. var pettern0 = "(?:";
  197. foreach (var info in zipPlaInfoParamsPrototype)
  198. {
  199. pettern0 += @"(?:" + info.MnemonicPatternPair + ")|";
  200. }
  201. pettern0 = pettern0.Substring(0, pettern0.Length - 1) + @")";
  202. //var fileNameRegexPattern = @"\s*\{\s*zpi\s*\$\s*((?:" + pettern0 + ";)*" + pettern0 + @")?;?\s*\}\s*"; // 前後の空白を吸収
  203. var fileNameRegexPattern = @"\s?\{\s*zpi\s*\$\s*((?:" + pettern0 + ";)*" + pettern0 + @")?;?\s*\}"; // 頭の空白を最大一つだけ吸収
  204. fileNameRegex = new Regex(fileNameRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  205. }
  206. public ZipPlaInfo(string path)
  207. {
  208. if (string.IsNullOrEmpty(path))
  209. {
  210. throw new ArgumentNullException("path");
  211. }
  212. this.path = path;
  213. zipPlaInfoParams = new ZipPlaInfoParam[zipPlaInfoParamsPrototype.Length];
  214. indexLengthPairs = new Tuple<int, int>[zipPlaInfoParamsPrototype.Length];
  215. for (var i = 0; i < zipPlaInfoParamsPrototype.Length; i++)
  216. {
  217. zipPlaInfoParams[i] = zipPlaInfoParamsPrototype[i].Clone() as ZipPlaInfoParam;
  218. }
  219. //if (path.EndsWith(Path.DirectorySeparatorChar.ToString())) path = path.Substring(0, path.Length - 1);
  220. //var m = fileNameRegex.Match(Path.GetFileName(path));
  221. var pathArray = path.ToArray();
  222. int sepPos;
  223. for (sepPos = pathArray.Length - 2; sepPos >= 0; sepPos--) { var c = pathArray[sepPos]; if (c == '\\' || c == '/') break; }
  224. sepPos++;
  225. var m = fileNameRegex.Match(sepPos > 0 ? path.Substring(sepPos) : path);
  226. if (m.Success)
  227. {
  228. var index = m.Groups[1].Index;
  229. foreach (var paramString in m.Groups[1].Value.Split(new char[1] { ';' }, StringSplitOptions.None))
  230. {
  231. for(var i = 0; i < zipPlaInfoParamsPrototype.Length; i++)
  232. {
  233. var info = zipPlaInfoParams[i];
  234. var m2 = info.Regex.Match(paramString);
  235. if (m2.Success)
  236. {
  237. info.SetValue(m2.Groups);
  238. indexLengthPairs[i] = Tuple.Create(index + sepPos, paramString.Length);
  239. break;
  240. }
  241. }
  242. index += paramString.Length + 1;
  243. }
  244. }
  245. }
  246. public string GetPathOfCurrentInfo(bool isDir)
  247. {
  248. string basePrefix;
  249. string baseSuffix;
  250. var dirName = Path.GetDirectoryName(path);
  251. if (dirName != null) // ルートなどだと null になる
  252. {
  253. if (isDir)
  254. {
  255. basePrefix = Path.Combine(dirName, fileNameRegex.Replace(Path.GetFileName(path), ""));
  256. baseSuffix = "";
  257. }
  258. else
  259. {
  260. basePrefix = Path.Combine(dirName, fileNameRegex.Replace(Path.GetFileNameWithoutExtension(path), ""));
  261. baseSuffix = Path.GetExtension(path);
  262. }
  263. }
  264. else
  265. {
  266. basePrefix = fileNameRegex.Replace(path, "");
  267. baseSuffix = "";
  268. }
  269. var infoString = GetInfoString();
  270. if (string.IsNullOrEmpty(infoString))
  271. {
  272. return basePrefix + baseSuffix;
  273. }
  274. else
  275. {
  276. return basePrefix + " " + infoString + baseSuffix;
  277. }
  278. }
  279. private string GetInfoString()
  280. {
  281. var result = "{zpi$";
  282. var existsParam = false;
  283. foreach (var info in zipPlaInfoParams)
  284. {
  285. if (info.Value != null)
  286. {
  287. existsParam = true;
  288. result += info.GetInfoString() + ";";
  289. }
  290. }
  291. if (!existsParam)
  292. {
  293. return null;
  294. }
  295. return result.Substring(0, result.Length - 1) + "}";
  296. }
  297. private delegate object Parse(string[] str);
  298. private delegate string DeParse(object obj);
  299. private class ZipPlaInfoParam : ICloneable
  300. {
  301. public readonly string Mnemonic;
  302. public readonly string Pattern;
  303. public readonly string MnemonicPatternPair;
  304. public readonly Regex Regex;
  305. private readonly Parse Parse;
  306. private readonly DeParse DeParse;
  307. public object Value;
  308. public ZipPlaInfoParam(string mnemonic, string pattern, Parse parse, DeParse deparse)
  309. {
  310. Mnemonic = mnemonic;
  311. Pattern = pattern;
  312. Parse = parse;
  313. DeParse = deparse;
  314. MnemonicPatternPair = @"\s*" + mnemonic + @"\s*=\s*" + pattern + @"\s*";
  315. // static readonly ではないので Compiled は不適切に見えるが、このコンストラクタは static readonly な ZipPlaInfo.zipPlaInfoParamsPrototype の長さ分しか呼び出されないので問題ない
  316. Regex = new Regex("^" + MnemonicPatternPair + "$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
  317. Value = null;
  318. }
  319. public ZipPlaInfoParam(string mnemonic, string pattern, string mnemonicPatternPair, Regex regex, Parse parse, DeParse deparse)
  320. {
  321. Mnemonic = mnemonic;
  322. Pattern = pattern;
  323. Parse = parse;
  324. DeParse = deparse;
  325. MnemonicPatternPair = mnemonicPatternPair;
  326. Regex = regex;
  327. Value = null;
  328. }
  329. public void SetValue(GroupCollection g)
  330. {
  331. var str = new string[g.Count - 1];
  332. for (var i = 0; i < str.Length; i++)
  333. {
  334. str[i] = g[i + 1].Value;
  335. }
  336. Value = Parse(str);
  337. }
  338. public string GetInfoString()
  339. {
  340. return Mnemonic + "=" + DeParse(Value);
  341. }
  342. public object Clone()
  343. {
  344. return new ZipPlaInfoParam(Mnemonic, Pattern, MnemonicPatternPair, Regex, Parse, DeParse);
  345. }
  346. }
  347. }
  348. }