Font.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using OpenVIII.Encoding.Tags;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. namespace OpenVIII
  7. {
  8. /// <summary>
  9. /// Font Module
  10. /// </summary>
  11. /// <remarks>
  12. /// 21x10 characters; char is always 24x24; 2 files side by side; sysfnt00 is same as sysfld00,
  13. /// but sysfnt00 is missing sysfnt01
  14. /// </remarks>
  15. public class Font
  16. {
  17. #region Fields
  18. public static Dictionary<ColorID, Color> ColorID2Blink = new Dictionary<ColorID, Color>
  19. {
  20. { ColorID.Dark_Grey, new Color(12,15,12,255) },
  21. { ColorID.Grey, new Color(41,49,41,255) },
  22. { ColorID.Yellow, new Color(222,222,8,255) },
  23. { ColorID.Red, new Color(128,24,24,255) },
  24. { ColorID.Green, new Color(0,128,0,255) },
  25. { ColorID.Blue, new Color(53,90,128,255) },
  26. { ColorID.Purple, new Color(128,0,128,255) },
  27. { ColorID.White, new Color(148,148,164,255) }
  28. };
  29. public static Dictionary<ColorID, Color> ColorID2Color = new Dictionary<ColorID, Color>
  30. {
  31. { ColorID.Dark_Grey, new Color(41,49,41,255) },
  32. { ColorID.Grey, new Color(148,148,164,255) },
  33. { ColorID.Yellow, new Color(222,222,8,255) },
  34. { ColorID.Red, new Color(255,24,24,255) },
  35. { ColorID.Green, new Color(0,255,0,255) },
  36. { ColorID.Blue, new Color(106,180,238,255) },
  37. { ColorID.Purple, new Color(255,0,255,255) },
  38. { ColorID.White, Color.White },
  39. };
  40. private byte[] charWidths;
  41. private Texture2D menuFont;
  42. private Texture2D sysfnt; //21x10 characters; char is always 12x12
  43. private TextureHandler sysfntbig;
  44. #endregion Fields
  45. #region Constructors
  46. public Font() => LoadFonts();
  47. #endregion Constructors
  48. #region Enums
  49. public enum ColorID
  50. {
  51. Dark_Grey, Grey, Yellow, Red, Green, Blue, Purple, White,
  52. //these are darker versions that are faded to when blinking
  53. }
  54. public enum Type
  55. {
  56. sysFntBig,
  57. sysfnt,
  58. menuFont,
  59. }
  60. #endregion Enums
  61. #region Methods
  62. public void LoadFonts()
  63. {
  64. ArchiveWorker aw = new ArchiveWorker(Memory.Archives.A_MENU);
  65. string sysfntTdwFilepath = aw.GetListOfFiles().First(x => x.ToLower().Contains("sysfnt.tdw"));
  66. string sysfntFilepath = aw.GetListOfFiles().First(x => x.ToLower().Contains("sysfnt.tex"));
  67. TEX tex = new TEX(ArchiveWorker.GetBinaryFile(Memory.Archives.A_MENU, sysfntFilepath));
  68. sysfnt = tex.GetTexture((int)ColorID.White);
  69. sysfntbig = TextureHandler.Create("sysfld{0:00}.tex", tex, 2, 1, (int)ColorID.White);
  70. TDW tim = new TDW(ArchiveWorker.GetBinaryFile(Memory.Archives.A_MENU, sysfntTdwFilepath), 0);
  71. charWidths = tim.CharWidths;
  72. menuFont = tim.GetTexture((ushort)ColorID.White);
  73. }
  74. public Rectangle RenderBasicText(FF8String buffer, Vector2 pos, Vector2 zoom, Type whichFont = 0, float Fade = 1.0f, int lineSpacing = 0, bool skipdraw = false, ColorID color_ = ColorID.White, bool blink = false)
  75. {
  76. ColorID colorbak = color_;
  77. Color color = ColorID2Color[color_];
  78. Color faded_color = ColorID2Blink[color_];
  79. if (buffer == null) return new Rectangle();
  80. Rectangle ret = new Rectangle(pos.RoundedPoint(), new Point(0));
  81. Rectangle destRect = Rectangle.Empty;
  82. Point real = pos.RoundedPoint();
  83. int charCountWidth = 21;
  84. int charSize = 12; //pixelhandler does the 2x scaling on the fly.
  85. Point size = (new Vector2(0, charSize) * zoom).RoundedPoint();
  86. Point baksize = size;
  87. int width = 0;
  88. bool skipletter = false;
  89. for (int i = 0; i < buffer.Length; i++)
  90. {
  91. size = baksize;
  92. byte c = buffer[i];
  93. if (c == 0) continue;
  94. else if (c == (byte)FF8TextTagCode.Dialog)
  95. {
  96. if (++i < buffer.Length - 1)
  97. {
  98. c = buffer[i];
  99. switch ((FF8TextTagDialog)c)
  100. {
  101. // Most of these should be replaced before it gets here becuase they have
  102. // values set by other objects.
  103. case FF8TextTagDialog.CustomICON:
  104. DrawIcon(buffer, zoom, Fade, skipdraw, real, ref size, ref skipletter, ref i, ref c);
  105. break;
  106. }
  107. //if (!skipletter)
  108. SetRetRec(pos, ref ret, ref real, size);
  109. continue;
  110. }
  111. }
  112. else if (c == (byte)FF8TextTagCode.Key)
  113. {
  114. if (++i < buffer.Length - 1)
  115. {
  116. FF8TextTagKey k = (FF8TextTagKey)buffer[i];
  117. FF8String str = Input2.ButtonString(k);
  118. Rectangle retpos = RenderBasicText(str, real, zoom, whichFont, Fade, lineSpacing, skipdraw, ColorID.Green);
  119. size.X = retpos.Width;
  120. //size.Y = retpos.Height;
  121. //real.X += retpos.Width;
  122. //TODO add key/controller input icons/text here.
  123. //if (!skipletter)
  124. SetRetRec(pos, ref ret, ref real, size);
  125. continue;
  126. }
  127. }
  128. else if (c == (byte)FF8TextTagCode.Color)
  129. {
  130. if (++i < buffer.Length - 1)
  131. {
  132. c = buffer[i];
  133. blink = c >= (byte)FF8TextTagColor.Dark_GrayBlink ? true : false;
  134. GetColorFromTag(c, out Color? nc, out Color? fc);
  135. color = nc ?? ColorID2Color[colorbak];
  136. faded_color = fc ?? ColorID2Blink[colorbak];
  137. SetRetRec(pos, ref ret, ref real, size);
  138. continue;
  139. }
  140. }
  141. else if (c == (byte)FF8TextTagCode.Line && NewLine(pos, lineSpacing, ref real, size))
  142. {
  143. SetRetRec(pos, ref ret, ref real, size);
  144. continue;
  145. }
  146. if (!skipletter)
  147. {
  148. int deltaChar = GetDeltaChar(c);
  149. if (deltaChar >= 0 && deltaChar < charWidths.Length)
  150. {
  151. width = charWidths[deltaChar];
  152. size.X = (int)(charWidths[deltaChar] * zoom.X);
  153. }
  154. else
  155. {
  156. width = charSize;
  157. size.X = (int)(charSize * zoom.X);
  158. }
  159. Point curSize = size;
  160. int verticalPosition = deltaChar / charCountWidth;
  161. //i.e. 1280 is 100%, 640 is 50% and therefore 2560 is 200% which means multiply by 0.5f or 2.0f
  162. destRect = new Rectangle(real, size);
  163. if (!skipdraw)
  164. {
  165. Rectangle sourceRect = new Rectangle((deltaChar - (verticalPosition * charCountWidth)) * charSize,
  166. verticalPosition * charSize,
  167. width,
  168. charSize);
  169. DrawLetter(whichFont, Fade, blink ? Color.Lerp(color, faded_color, Menu.Blink_Amount) : color, destRect, sourceRect);
  170. }
  171. }
  172. skipletter = false;
  173. SetRetRec(pos, ref ret, ref real, size);
  174. }
  175. ret.Height = size.Y + (real.Y - (int)pos.Y);
  176. return ret;
  177. }
  178. private static void SetRetRec(Vector2 pos, ref Rectangle ret, ref Point real, Point size)
  179. {
  180. real.X += size.X;
  181. int curWidth = real.X - (int)pos.X;
  182. if (curWidth > ret.Width)
  183. ret.Width = curWidth;
  184. }
  185. private static void DrawIcon(FF8String buffer, Vector2 zoom, float Fade, bool skipdraw, Point real, ref Point size, ref bool skipletter, ref int i, ref byte c)
  186. {
  187. if (i + 3 < buffer.Length)
  188. {
  189. c = buffer[++i];
  190. short ic = c;
  191. c = buffer[++i];
  192. ic |= (short)(c << 8);
  193. byte pal = buffer[++i];
  194. Memory.Icons.Trim((Icons.ID)ic, pal);
  195. EntryGroup icon = Memory.Icons[(Icons.ID)ic];
  196. //Vector2 scale = Memory.Icons.GetTexture((Icons.ID)ic).ScaleFactor;
  197. if (icon != null)
  198. {
  199. float adj = (12 / (float)(icon.Height));
  200. Vector2 scale = new Vector2(adj * zoom.X);
  201. size.X = (int)(icon.Width * scale.X);
  202. size.Y = (int)(icon.Height * scale.X);
  203. Rectangle destRect = new Rectangle(real, size);
  204. //real.X += size.X;
  205. //skipletter = true;
  206. if (!skipdraw)
  207. Memory.Icons.Draw((Icons.ID)ic, pal, destRect, Vector2.Zero, Fade);
  208. }
  209. }
  210. }
  211. public Rectangle RenderBasicText(FF8String buffer, Point pos, Vector2 zoom, Type whichFont = 0, float Fade = 1.0f, int lineSpacing = 0, bool skipdraw = false, ColorID color = ColorID.White, bool blink = false)
  212. => RenderBasicText(buffer, pos.ToVector2(), zoom, whichFont, Fade, lineSpacing, skipdraw, color, blink);
  213. public Rectangle RenderBasicText(FF8String buffer, int x, int y, float zoomWidth = 2.545455f, float zoomHeight = 3.0375f, Type whichFont = 0, float Fade = 1.0f, int lineSpacing = 0, bool skipdraw = false, ColorID color = ColorID.White, bool blink = false)
  214. => RenderBasicText(buffer, new Vector2(x, y), new Vector2(zoomWidth, zoomHeight), whichFont, Fade, lineSpacing, skipdraw, color,blink);
  215. private static void GetColorFromTag(byte c, out Color? color, out Color? faded_color)
  216. {
  217. color = null;
  218. faded_color = null;
  219. ColorID? cid = null;
  220. switch ((FF8TextTagColor)c)
  221. {
  222. case FF8TextTagColor.Blue:
  223. case FF8TextTagColor.BlueBlink:
  224. cid = ColorID.Blue;
  225. break;
  226. case FF8TextTagColor.Green:
  227. case FF8TextTagColor.GreenBlink:
  228. cid = ColorID.Green;
  229. break;
  230. case FF8TextTagColor.Grey:
  231. case FF8TextTagColor.GreyBlink:
  232. cid = ColorID.Grey;
  233. break;
  234. case FF8TextTagColor.Purple:
  235. case FF8TextTagColor.PurpleBlink:
  236. cid = ColorID.Purple;
  237. break;
  238. case FF8TextTagColor.Red:
  239. case FF8TextTagColor.RedBlink:
  240. cid = ColorID.Red;
  241. break;
  242. case FF8TextTagColor.White:
  243. case FF8TextTagColor.WhiteBlink:
  244. // since ending cid change reverts cid to white. if you have a custom cid set
  245. // this will allow reverting to that.
  246. break;
  247. case FF8TextTagColor.Yellow:
  248. case FF8TextTagColor.YellowBlink:
  249. cid = ColorID.Yellow;
  250. break;
  251. case FF8TextTagColor.Dark_Gray:
  252. case FF8TextTagColor.Dark_GrayBlink:
  253. cid = ColorID.Dark_Grey;
  254. break;
  255. }
  256. if (cid.HasValue)
  257. {
  258. color = ColorID2Color[cid.Value];
  259. faded_color = ColorID2Blink[cid.Value];
  260. }
  261. }
  262. private static int GetDeltaChar(byte c) => (c - 32);
  263. private static bool NewLine(Vector2 pos, int lineSpacing, ref Point real, Point size)
  264. {
  265. bool gonext;
  266. real.X = (int)pos.X;
  267. real.Y += size.Y + lineSpacing;
  268. gonext = true;
  269. return gonext;
  270. }
  271. private void DrawLetter(Type whichFont, float Fade, Color color, Rectangle destRect, Rectangle sourceRect)
  272. {
  273. switch (whichFont)
  274. {
  275. case Type.menuFont:
  276. case Type.sysfnt:
  277. // if you use Memory.SpriteBatchStartAlpha(SamplerState.PointClamp); you won't need
  278. // to trim last pixel. but it doesn't look good on low res fonts.
  279. //trim pixels to remove texture filtering artifacts.
  280. //sourceRect.Width -= 1;
  281. //sourceRect.Height -= 1;
  282. Memory.spriteBatch.Draw(whichFont == Type.menuFont ? menuFont : sysfnt,
  283. destRect,
  284. sourceRect,
  285. color * Fade);
  286. break;
  287. case Type.sysFntBig:
  288. if (!sysfntbig.Modded)
  289. {
  290. Rectangle ShadowdestRect = new Rectangle(destRect.Location, destRect.Size);
  291. ShadowdestRect.Offset(2, 2);
  292. sysfntbig.Draw(ShadowdestRect, sourceRect, Color.Black * Fade * .5f);
  293. }
  294. sysfntbig.Draw(destRect, sourceRect, color * Fade);
  295. break;
  296. }
  297. }
  298. #endregion Methods
  299. }
  300. }