Translation.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Media;
  6. using System.Text.RegularExpressions;
  7. using NTERA.Core;
  8. using NTERA.EmuEra.Game.EraEmu.Sub;
  9. // Created by Bartoum.
  10. // Class that is initialized one time in Constant data.
  11. // Create a dictionary of dictionary for each *_TR.csv that is there
  12. namespace NTERA.EmuEra.Game.EraEmu.GameData.Variable
  13. {
  14. internal class Translation // JVN: making it internal allows to pass the Emueraconsole by reference here
  15. {
  16. public static Dictionary<string, Dictionary<string, string>> nameTlDictionnary;
  17. public static string[] fileArray;
  18. private static ArrayList memoryArray;
  19. private static string lastRet;
  20. private static int nbrMemory = 20;
  21. private static bool canLoadParam;
  22. private static Dictionary<Int64, TRCharaTemplate> TRCharaList; // JVN: A simple dictionary, only needs the character number as a key.
  23. private static IConsole output; // JVN: Console access is a must, debug is life, debug is <3
  24. public enum CharaData
  25. { Name = 0, Callname, Nickname, Mastername, CSTR} // JVN: enums are noice.
  26. // JVN: Templates are good too. Make it private too, because it's only used interally
  27. private class TRCharaTemplate
  28. {
  29. public Int64 No;
  30. public string Name;
  31. public string Callname;
  32. public string Nickname;
  33. public string Mastername;
  34. public Dictionary<Int32, string> CStr;
  35. // JVN: Construct
  36. public TRCharaTemplate(Int64 aNo = 0, string aName ="", string aCall ="", string aNick = "", string aMaster = "")
  37. {
  38. No = aNo;
  39. Name = aName;
  40. Callname = aCall;
  41. Nickname = aNick;
  42. Mastername = aMaster;
  43. CStr = new Dictionary<Int32, string>();
  44. }
  45. // JVN: Cloning is important for what we need to do
  46. public TRCharaTemplate Clone()
  47. {
  48. TRCharaTemplate tmp = new TRCharaTemplate(No, Name, Callname, Nickname, Mastername);
  49. foreach (KeyValuePair<Int32, string> cloneData in CStr)
  50. {
  51. tmp.CStr.Add(cloneData.Key, cloneData.Value);
  52. }
  53. return tmp;
  54. }
  55. }
  56. //Constructor
  57. public Translation(string csvDir, ref IConsole console, bool pcanLoadParam)
  58. {
  59. output = console; // JVN: Debugging is important, and the console allows for that
  60. canLoadParam = pcanLoadParam;
  61. loadTranslationData(csvDir);
  62. loadCharacterTranslationData(csvDir);
  63. }
  64. //JVN: I just wants it
  65. public static bool isCanLoadParam() { return canLoadParam; }
  66. private const bool translateCSVAnyways = false; // TODO: make this configurable, somehow, probably via CSV setting?
  67. //Normal way to translate original is the string of the variable we are working with and name is the file where it lives ex: Talent or Source. It can also be ALL.
  68. public static string translate(string original, string name, bool tryTranslate){
  69. string ret;
  70. ret = original;
  71. if (name == "Palam" && canLoadParam)
  72. name = "Param";
  73. //Change the original term to the translated one;
  74. if (original != null){
  75. if(nameTlDictionnary.ContainsKey(name) && nameTlDictionnary[name].ContainsKey(original)){
  76. if(tryTranslate || translateCSVAnyways)
  77. {
  78. ret = nameTlDictionnary[name][original];
  79. lastRet = ret;
  80. }
  81. else{
  82. if (memoryArray.IndexOf(original + "," + name) == -1)
  83. memoryArray.Add(original + "," + name);
  84. if (memoryArray.Count >= nbrMemory)
  85. memoryArray.RemoveAt(0);
  86. }
  87. }
  88. }
  89. return ret;
  90. }
  91. // JVN: Character string translation
  92. public static string translateChara(Int64 character, CharaData varname, string oriString, Int32 index = 0) {
  93. switch (varname)
  94. {
  95. case CharaData.Name: // "NAME","名前"
  96. if (TRCharaList.ContainsKey(character)) return TRCharaList[character].Name;
  97. break;
  98. case CharaData.Callname: // "CALLNAME","呼び名"
  99. if (TRCharaList.ContainsKey(character)) return TRCharaList[character].Callname;
  100. break;
  101. case CharaData.Nickname: // "NICKNAME","あだ名"
  102. if (TRCharaList.ContainsKey(character)) return TRCharaList[character].Nickname;
  103. break;
  104. case CharaData.Mastername: // "MASTERNAME","主人の呼び方"
  105. if (TRCharaList.ContainsKey(character)) return TRCharaList[character].Mastername;
  106. break;
  107. case CharaData.CSTR: // "CSTR"
  108. if (TRCharaList.ContainsKey(character))
  109. if (TRCharaList[character].CStr.ContainsKey(index)) return TRCharaList[character].CStr[index];
  110. break;
  111. }
  112. return oriString;
  113. }
  114. //The last way to translate something.
  115. public static string translateALL(string original, string pname ="ALL")
  116. {
  117. string ret = original;
  118. if (!String.IsNullOrEmpty(original))
  119. {
  120. bool found = false;
  121. string[] temp;
  122. int i = memoryArray.Count - 1;
  123. if (!String.IsNullOrEmpty(lastRet) && original.Contains(lastRet))
  124. {
  125. //output.Print("Did it brah\n");
  126. found = true;
  127. }
  128. while (i >= 0 && !found)
  129. {
  130. temp = memoryArray[i].ToString().Split(',');
  131. // output.Print("Checked: " + original + " against: " + temp[0] + " \\ ");
  132. if (original.Contains(temp[0]))
  133. {
  134. //output.Print("FOUND IT with:" +temp[1]+ " " + temp[0] + " \n");
  135. ret = original.Replace(temp[0], nameTlDictionnary[temp[1]][temp[0]]);
  136. found = true;
  137. //memoryArray.Remove(temp[0] + "," + temp[1]);
  138. }
  139. i--;
  140. }
  141. //output.Print("\n");
  142. if (!found)
  143. {
  144. if (nameTlDictionnary.ContainsKey(pname))
  145. {
  146. //We try to strip all the stuff around the csv variable name
  147. //Remove all numbers, remove LV and all special characters bellow
  148. //Parantheses are only remove if only numbers where inside them because some variable name use them
  149. bool numPresent = true;
  150. //We remove all the numbers
  151. while (numPresent)
  152. {
  153. if (Regex.Match(original, @"\d+").Value != "")
  154. {
  155. original = original.Replace(Regex.Match(original, @"\d+").Value, "");
  156. }
  157. else
  158. {
  159. numPresent = false;
  160. }
  161. }
  162. //original = original.Replace("-", "");
  163. //original = original.Replace("()", "");
  164. original = original.Replace("【", "");
  165. original = original.Replace("】", "");
  166. original = original.Replace("[", "");
  167. original = original.Replace("]", "");
  168. original = original.Replace("LV", "");
  169. original = original.Replace("『", "");
  170. original = original.Replace("』", "");
  171. /*
  172. original = original.Replace("1Lv", "");
  173. original = original.Replace("2Lv", "");
  174. original = original.Replace("3Lv", "");
  175. original = original.Replace("4Lv", "");
  176. original = original.Replace("5Lv", "");
  177. */
  178. //output.Print("Did it the old stupid way\n");
  179. if (nameTlDictionnary[pname].ContainsKey(original))
  180. {
  181. ret = ret.Replace(original, nameTlDictionnary[pname][original]);
  182. //output.Print("Found it the stupid way");
  183. }
  184. }
  185. }
  186. }
  187. return ret;
  188. }
  189. private void loadTranslationData(string csvDir)
  190. {
  191. fileArray = new[] {"Abl", "Talent", "Exp", "Mark", "Palam", "Param", "Item","Train", "Base","Source", "Ex", "EQUIP", "TEQUIP", "Flag", "TFLAG", "Cflag", "Tcvar", "CSTR", "Stain","Cdflag1", "Cdflag2", "Str", "TSTR","SaveStr", "GLOBAL", "GLOBALS","ALL"};
  192. nameTlDictionnary = new Dictionary<string, Dictionary<string, string> >();
  193. memoryArray = new ArrayList();
  194. string temp;
  195. for (int i = 0; i < fileArray.Length;i++){
  196. Dictionary<string,string> tempDictionnary = new Dictionary<string,string>();
  197. string csvPath = csvDir + fileArray[i] + "_TR.csv";
  198. if (!File.Exists(csvPath))
  199. continue;
  200. EraStreamReader eReader = new EraStreamReader(false);
  201. if (!eReader.Open(csvPath))
  202. throw new Exception(eReader.Filename + " can't be opened");
  203. ScriptPosition position = null;
  204. try
  205. {
  206. StringStream st = null;
  207. while ((st = eReader.ReadEnabledLine()) != null)
  208. {
  209. position = new ScriptPosition(eReader.Filename, eReader.LineNo, st.RowString);
  210. temp = st.Substring();
  211. if (temp[0] == ';')
  212. continue;
  213. if (temp.Contains(";"))
  214. temp = temp.Substring(0, temp.IndexOf(';'));
  215. string[] tokens = temp.Split(',');
  216. //Make it crash with a good message if there's not a ","
  217. if(tokens.Length != 2)
  218. throw new Exception("A line must have one \",\" to separate the original from the new string. The line with the problem begins by : " + tokens[0]);
  219. //Make it crash with a good message if the same key is present two times
  220. if (tempDictionnary.ContainsKey(tokens[0]))
  221. throw new Exception("You can only have one occurence of the same key. " + tokens[0] + " is already linked to : " + tempDictionnary[tokens[0]]);
  222. tempDictionnary.Add(tokens[0], tokens[1]);
  223. }
  224. nameTlDictionnary.Add(fileArray[i], tempDictionnary);
  225. }
  226. catch(Exception e)
  227. {
  228. throw new Exception(e.Message);
  229. }
  230. finally
  231. {
  232. eReader.Close();
  233. }
  234. }
  235. }
  236. //Receive the line that is being interpreted as a string.
  237. //if there's a TALENTXXX in this string it will return the XXX
  238. //if not it will return ALL.
  239. public static string searchParentFile(string name)
  240. {
  241. string ret = "ALL";
  242. int i = 0;
  243. bool found = false;
  244. try
  245. {
  246. if (name.Contains("NAME")){
  247. while (i < fileArray.Length - 1 && !found)
  248. {
  249. if (name.Contains(fileArray[i].ToUpper() + "NAME"))
  250. {
  251. ret = fileArray[i];
  252. found = true;
  253. }
  254. i++;
  255. }
  256. }
  257. }
  258. catch(Exception e)
  259. {
  260. throw new Exception(e.Message);
  261. }
  262. return ret;
  263. }
  264. // JVN: Loading fuctions for TRCHARA*.CSV
  265. private void loadCharacterTranslationData(string csvDir, bool useCompatiName = false, bool disp = false)
  266. {
  267. if (!Directory.Exists(csvDir))
  268. return;
  269. TRCharaList = new Dictionary<long, TRCharaTemplate>();
  270. // JVN: Search the CSV and subdirectories for TRCHARA files and send that to a function for futher loading
  271. List<KeyValuePair<string, string>> csvPaths = Config.Config.GetFiles(csvDir, "TRCHARA*.CSV");
  272. for (int i = 0; i < csvPaths.Count; i++)
  273. loadTRCharacterDataFile(csvPaths[i].Value, csvPaths[i].Key, disp);
  274. if (useCompatiName) // check for the compatibility flag, just in case
  275. {
  276. foreach (KeyValuePair<Int64, TRCharaTemplate> tmpl in TRCharaList)
  277. if (string.IsNullOrEmpty(tmpl.Value.Callname))
  278. tmpl.Value.Callname = tmpl.Value.Name;
  279. }
  280. }
  281. // JVN: Loads individual character files and data
  282. private void loadTRCharacterDataFile(string csvPath, string csvName, bool disp)
  283. {
  284. bool NextCharacter = false;
  285. EraStreamReader eReader = new EraStreamReader(false);
  286. if (!eReader.Open(csvPath, csvName))
  287. {
  288. output.PrintError("File " + eReader.Filename + " has failed to open");
  289. return;
  290. }
  291. ScriptPosition position = null;
  292. if (disp)
  293. output.PrintSystemLine(eReader.Filename + " is being read・・・");
  294. try
  295. {
  296. TRCharaTemplate tmpl = null;
  297. Int64 index = -1;
  298. StringStream st = null;
  299. bool Alive = true;
  300. while (Alive || NextCharacter)
  301. {
  302. Alive = (st = eReader.ReadEnabledLine()) != null;
  303. if (NextCharacter)
  304. {
  305. if (tmpl != null) {
  306. TRCharaTemplate tmp = tmpl.Clone();
  307. TRCharaList.Add(index, tmp);
  308. tmpl = null;
  309. NextCharacter = false;
  310. }
  311. if (!Alive) break;
  312. }
  313. position = new ScriptPosition(eReader.Filename, eReader.LineNo, st.RowString);
  314. string[] tokens = st.Substring().Split(',');
  315. if (tokens.Length < 2)
  316. {
  317. ParserMediator.Warn("\",\" is needed", position, 1);
  318. continue;
  319. }
  320. if (tokens[0].Length == 0)
  321. {
  322. ParserMediator.Warn("Begins in \",\"", position, 1);
  323. continue;
  324. }
  325. if ((tokens[0].Equals("NO", Config.Config.SCVariable)) || (tokens[0].Equals("番号", Config.Config.SCVariable)))
  326. {
  327. if (tmpl != null)
  328. {
  329. ParserMediator.Warn("Character number has been defined twice", position, 1);
  330. continue;
  331. }
  332. if (!Int64.TryParse(tokens[1].TrimEnd(), out index))
  333. {
  334. ParserMediator.Warn(tokens[1] + " cannot be changed into an integer", position, 1);
  335. continue;
  336. }
  337. tmpl = new TRCharaTemplate(index);
  338. continue;
  339. }
  340. if (tmpl == null)
  341. {
  342. ParserMediator.Warn("Data started before the character number is defined", position, 1);
  343. continue;
  344. }
  345. NextCharacter = toTRCharaTemplate(position, tmpl, tokens);
  346. }
  347. }
  348. catch (Exception e)
  349. {
  350. SystemSounds.Hand.Play();
  351. if (position != null)
  352. {
  353. output.PrintError(e.ToString());
  354. ParserMediator.Warn("An unexpected error has occurred", position, 3);
  355. }
  356. else
  357. {
  358. output.PrintError("An unexpected error has occurred");
  359. }
  360. output.PrintError("");
  361. }
  362. finally
  363. {
  364. eReader.Dispose();
  365. }
  366. }
  367. // JVN: Copy of the ConstantData's tryTo64
  368. private bool tryToInt64(string str, out Int64 p)
  369. {
  370. p = -1;
  371. if (string.IsNullOrEmpty(str))
  372. return false;
  373. StringStream st = new StringStream(str);
  374. int sign = 1;
  375. if (st.Current == '+')
  376. st.ShiftNext();
  377. else if (st.Current == '-')
  378. {
  379. sign = -1;
  380. st.ShiftNext();
  381. }
  382. //1803beta005 char.IsDigitは全角数字とかまでひろってしまうので・・・
  383. //if (!char.IsDigit(st.Current))
  384. // return false;
  385. switch (st.Current)
  386. {
  387. case '0':
  388. case '1':
  389. case '2':
  390. case '3':
  391. case '4':
  392. case '5':
  393. case '6':
  394. case '7':
  395. case '8':
  396. case '9':
  397. break;
  398. default:
  399. return false;
  400. }
  401. try
  402. {
  403. p = LexicalAnalyzer.ReadInt64(st, false);
  404. p = p * sign;
  405. }
  406. catch
  407. {
  408. return false;
  409. }
  410. return true;
  411. }
  412. // JVN: Takes care of each line being read and sends result back. That works too. Returns false if 'ENDCHR' is found.
  413. private bool toTRCharaTemplate(ScriptPosition position, TRCharaTemplate chara, string[] tokens)
  414. {
  415. if (chara == null)
  416. return true;
  417. int length = -1;
  418. Int64 p1 = -1;
  419. //Int64 p2 = -1;
  420. //Dictionary<int, Int64> intArray = null;
  421. Dictionary<int, string> strArray = null;
  422. Dictionary<string, int> namearray = null;
  423. string errPos = null;
  424. string varname = tokens[0].ToUpper();
  425. switch (varname)
  426. {
  427. case "NAME":
  428. case "名前":
  429. chara.Name = tokens[1];
  430. return false;
  431. case "CALLNAME":
  432. case "呼び名":
  433. chara.Callname = tokens[1];
  434. return false;
  435. case "NICKNAME":
  436. case "あだ名":
  437. chara.Nickname = tokens[1];
  438. return false;
  439. case "MASTERNAME":
  440. case "主人の呼び方":
  441. chara.Mastername = tokens[1];
  442. return false;
  443. case "CSTR":
  444. strArray = chara.CStr;
  445. errPos = "cstr.csv";
  446. break;
  447. case "ENDCHR":
  448. case "ENDCHAR":
  449. return true;
  450. default:
  451. ParserMediator.Warn("\"" + tokens[0] + "\" cannot be interpreted", position, 1);
  452. return false;
  453. }
  454. bool p1isNumeric = tryToInt64(tokens[1].TrimEnd(), out p1);
  455. int index = (int)p1;
  456. if ((!p1isNumeric) && (namearray != null))
  457. {
  458. if (!namearray.TryGetValue(tokens[1], out index))
  459. {
  460. ParserMediator.Warn(errPos + ": \"" + tokens[1] + "\" is undefined", position, 1);
  461. return false;
  462. }
  463. if (index >= length)
  464. {
  465. ParserMediator.Warn("\"" + tokens[1] + "\" is out of bounds of the array", position, 1);
  466. return false;
  467. }
  468. }
  469. if (strArray != null)
  470. {
  471. if (tokens.Length < 3)
  472. ParserMediator.Warn("There's no third identifier", position, 1);
  473. if (strArray.ContainsKey(index))
  474. ParserMediator.Warn(varname + "'s #" + index + " element is already denied (Overwritting it)", position, 1);
  475. strArray[index] = tokens[2];
  476. }
  477. return false;
  478. }
  479. }
  480. }