ProjectDDD/Packages/SLUnity/SLString.cs

394 lines
12 KiB
C#
Raw Normal View History

using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Superlazy;
public static class SLString
{
private static readonly List<char[]> tags = new List<char[]>()
{
new char[]{ 's', 'c' },
new char[]{ '/', 's', 'c' },
new char[]{ 's', 'c', 'o', 'l', 'o', 'r' },
new char[]{ '/', 's', 'c', 'o', 'l', 'o', 'r' },
new char[]{ 'n', 'l', },
new char[]{ 's', 't', 'r' }
};
private static readonly List<string[]> koreanPostpositionStrTags = new List<string[]>()
{
new string[] { "을(를)", "을", "를" },
new string[] { "을/를", "을", "를" },
new string[] { "이(가)", "이", "가" },
new string[] { "이/가", "이", "가" },
new string[] { "은(는)", "은", "는" },
new string[] { "은/는", "은", "는" },
new string[] { "과(와)", "과", "와" },
new string[] { "과/와", "과", "와" }
};
private static readonly string englishPluralStrTags = "(s)";
public static string GetString(string str, SLEntity context)
{
return Translation(str, context);
}
private static string Translation(string str, SLEntity context)
{
if (string.IsNullOrEmpty(str)) return string.Empty;
if (str.IsLeft("STR_") == false) return Format(str, context);
var strKey = SLSystem.Data["Strings"][str];
var lang = SLGame.Session["Option"]["Language"];
if (strKey[lang])
{
return Format(strKey[lang], context);
}
else if (strKey["EN"])
{
return Format(strKey["EN"], context);
}
else if (strKey["KR"])
{
return Format(strKey["KR"], context);
}
else
{
return Format(str, context);
}
}
private static string Format(string value, SLEntity context)
{
var tagStart = value.LastIndexOf('{', value.Length - 1);
if (tagStart != -1)
{
var tagEnd = value.IndexOf('}', tagStart);
if (tagEnd == -1)
{
SLLog.Error($"Tagging Error [{value}]");
return value;
}
var sb = new StringBuilder(value);
var tagParamIdx = value.IndexOf(':', tagStart + 1, tagEnd - tagStart - 1);
string tagAttribute;
string tagType;
if (tagParamIdx != -1)
{
tagAttribute = sb.ToString(tagStart + 1, tagParamIdx - tagStart - 1);
tagType = sb.ToString(tagParamIdx + 1, tagEnd - tagParamIdx - 1);
}
else
{
tagAttribute = sb.ToString(tagStart + 1, tagEnd - tagStart - 1);
tagType = string.Empty;
}
var format = Translation(SLTag.Apply(context.Get(tagAttribute), tagType), context);
sb.Remove(tagStart, tagEnd - tagStart + 1);
sb.Insert(tagStart, format);
return Format(sb.ToString(), context); // 태그를 반복적으로 적용
}
else
{
return CustomTag(value, context);
}
}
private static string CustomTag(string str, SLEntity context)
{
if (string.IsNullOrEmpty(str)) return str;
var sb = new StringBuilder(GetTagApplyString(str, context));
return PostPositionCheck(sb);
}
private static string GetTagApplyString(string str, SLEntity context)
{
if (string.IsNullOrEmpty(str)) return str;
var tagStart = str.LastIndexOf('<', str.Length - 1);
var hasTag = tagStart != -1;
// end
if (hasTag == false)
{
return str;
}
var tagEnd = str.IndexOf('>', tagStart);
if (tagEnd == -1)
{
SLLog.Error($"Tagging Error [{str}]");
return str;
}
var sb = new StringBuilder(str);
if (TagCheck(sb, tagStart, tagEnd, tags[0]))
{
var colorTag = SLSystem.Data["SpecialColors"][sb.ToString(tagStart + 4, tagEnd - tagStart - 4)].ToString();
sb.Remove(tagStart + 1, tagEnd - tagStart - 1);
sb.Insert(tagStart + 1, colorTag);
sb.Insert(tagStart + 1, "color=");
}
else if (TagCheck(sb, tagStart, tagEnd, tags[1]))
{
sb.Remove(tagStart + 2, tagEnd - tagStart - 2);
sb.Insert(tagStart + 2, "color");
}
else if (TagCheck(sb, tagStart, tagEnd, tags[2]))
{
var colorTag = SLSystem.Data["SpecialColors"][sb.ToString(tagStart + 8, tagEnd - tagStart - 8)].ToString();
sb.Remove(tagStart + 1, tagEnd - tagStart - 1);
sb.Insert(tagStart + 1, colorTag);
sb.Insert(tagStart + 1, "color=");
}
else if (TagCheck(sb, tagStart, tagEnd, tags[3]))
{
sb.Remove(tagStart + 2, tagEnd - tagStart - 2);
sb.Insert(tagStart + 2, "color");
}
else if (TagCheck(sb, tagStart, tagEnd, tags[4]))
{
sb.Remove(tagStart, tagEnd - tagStart + 1);
sb.Insert(tagStart, "\n");
}
else if (TagCheck(sb, tagStart, tagEnd, tags[5]))
{
var newString = sb.ToString(tagStart + 5, tagEnd - tagStart - 5);
sb.Remove(tagStart, tagEnd - tagStart + 1);
sb.Insert(tagStart, Translation(newString, context));
}
var left = GetTagApplyString(sb.ToString(0, tagStart), context); // 태그 반복
sb.Remove(0, tagStart);
sb.Insert(0, left);
return sb.ToString();
}
private static string PostPositionCheck(StringBuilder sb)
{
var lang = SLGame.Session["Option"]["Language"];
if (lang == "KR")
{
foreach (var tag in koreanPostpositionStrTags)
{
int index = LastIndexOf(sb, tag[0]);
while (index != -1)
{
// index가 0보다 작으면 이전 문자가 없으므로 안전하게 체크
if (index - 1 < 0)
break;
char priorWord = sb[index - 1];
string correction = HasEndConsonant(priorWord) ? tag[1] : tag[2];
int tagLength = tag[0].Length;
sb.Remove(index, tagLength);
sb.Insert(index, correction);
// sb를 직접 검색하여 갱신
index = LastIndexOf(sb, tag[0]);
}
}
}
else if (lang == "EN")
{
int index = LastIndexOf(sb, englishPluralStrTags);
while (index != -1)
{
int blankEndIndex = LastIndexOf(sb, ' ', index - 1);
int blankStartIndex = LastIndexOf(sb, ' ', blankEndIndex - 1);
if (blankEndIndex == -1 || blankStartIndex == -1)
break;
string numberStr = Substring(sb, blankStartIndex + 1, blankEndIndex - blankStartIndex - 1);
// 공백 및 기타 whitespace 처리
numberStr = Regex.Replace(numberStr, @"\s+", " ");
int blankIndex = numberStr.LastIndexOf(' ');
if (blankIndex != -1)
{
numberStr = numberStr.Substring(blankIndex + 1);
}
bool plural;
string numberStrLower = numberStr.ToLower();
if (numberStrLower == "one" || numberStrLower == "a")
{
plural = false;
}
else
{
if (!int.TryParse(numberStr, out var number))
{
plural = true;
}
else
{
plural = number != 1;
}
}
sb.Remove(index, englishPluralStrTags.Length);
if (plural)
{
sb.Insert(index, 's');
}
index = LastIndexOf(sb, englishPluralStrTags);
}
}
return ColorTagCheck(sb);
}
private static bool HasEndConsonant(char word)
{
if (word < 0xAC00) return false;
return (word - 0xAC00) % 28 + 0x11a7 != 4519;
}
private static bool TagCheck(StringBuilder sb, int tagStart, int tagEnd, char[] tag)
{
var cnt = tag.Length;
if (tagEnd - tagStart - 1 < tag.Length) return false;
for (var i = 0; i < cnt; ++i)
{
if (sb[i + tagStart + 1] != tag[i] && sb[i + tagStart + 1] != char.ToUpper(tag[i])) return false;
}
return true;
}
private static string ColorTagCheck(StringBuilder sb)
{
int bracketStart;
var currentSearchPosition = 0;
while (currentSearchPosition < sb.Length && (bracketStart = IndexOf(sb, '[', currentSearchPosition)) != -1)
{
var bracketEnd = IndexOf(sb, ']', bracketStart);
if (bracketEnd == -1)
{
SLLog.Error($"Bracket Tagging Error [{sb}]");
break;
}
var colorCode = SLSystem.Data["SpecialColors"]["Default"];
var splitEnd = IndexOf(sb, '|', bracketStart);
if (splitEnd == -1 || splitEnd > bracketEnd) // 구분선이 없다면
{
splitEnd = bracketStart;
}
else
{
colorCode = SLSystem.Data["SpecialColors"][sb.ToString(bracketStart + 1, splitEnd - bracketStart - 1)];
}
var formattedText = $"<color={colorCode}>[{sb.ToString(splitEnd + 1, bracketEnd - splitEnd - 1)}]</color>";
sb.Remove(bracketStart, bracketEnd - bracketStart + 1);
sb.Insert(bracketStart, formattedText);
currentSearchPosition = bracketStart + formattedText.Length;
}
return sb.ToString();
}
public static List<string> GetTags(string text, SLEntity context)
{
var list = new List<string>();
int bracketStart;
var currentSearchPosition = 0;
text = GetString(text, context);
while (currentSearchPosition < text.Length && (bracketStart = text.IndexOf('[', currentSearchPosition)) != -1)
{
var bracketEnd = text.IndexOf(']', bracketStart);
if (bracketEnd == -1)
{
SLLog.Error($"Bracket Tagging Error [{text}]");
break;
}
var originalText = text.Substring(bracketStart + 1, bracketEnd - bracketStart - 1);
if (originalText.Contains('|')) // 태그붙음
{
originalText = originalText.Substring(originalText.IndexOf('|') + 1);
}
var tagID = Regex.Replace(originalText, @"[0-9\s!\""#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~×]", ""); // TODO: 추후에는 실제 태그 ID 변경
if (string.IsNullOrEmpty(tagID) == false)
{
var tag = SLSystem.Data["Tags"][tagID];
if (tag["Desc"])
{
list.Add(tagID);
}
}
currentSearchPosition = bracketStart + bracketEnd - bracketStart + 1;
}
return list;
}
private static int LastIndexOf(StringBuilder sb, string value)
{
if (value.Length == 0) return sb.Length;
for (int i = sb.Length - value.Length; i >= 0; i--)
{
bool found = true;
for (int j = 0; j < value.Length; j++)
{
if (sb[i + j] != value[j])
{
found = false;
break;
}
}
if (found) return i;
}
return -1;
}
private static int IndexOf(StringBuilder sb, char value, int startIndex = 0)
{
for (int i = startIndex; i < sb.Length; i++)
{
if (sb[i] == value)
return i;
}
return -1;
}
private static int LastIndexOf(StringBuilder sb, char c, int startIndex)
{
for (int i = startIndex; i >= 0; i--)
{
if (sb[i] == c) return i;
}
return -1;
}
private static string Substring(StringBuilder sb, int start, int length)
{
return sb.ToString(start, length);
}
}