Font.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. Memory.Log.WriteLine($"{nameof(Font)} :: {nameof(LoadFonts)} ");
  65. var aw = ArchiveWorker.Load(Memory.Archives.A_MENU);
  66. var bufferTex = aw.GetBinaryFile("sysfnt.tex");
  67. var tex = new TEX(bufferTex);
  68. sysfnt = tex.GetTexture((int)ColorID.White);
  69. sysfntbig = TextureHandler.Create("sysfld{0:00}.tex", tex, 2, 1, (int)ColorID.White);
  70. var bufferTDW = aw.GetBinaryFile("sysfnt.tdw");
  71. var tim = new TDW(bufferTDW);
  72. charWidths = tim.CharWidths;
  73. menuFont = tim.GetTexture((ushort)ColorID.White);
  74. }
  75. 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)
  76. {
  77. var colorbak = color_;
  78. var color = ColorID2Color[color_];
  79. var faded_color = ColorID2Blink[color_];
  80. if (buffer == null) return new Rectangle();
  81. var ret = new Rectangle(pos.RoundedPoint(), new Point(0));
  82. var destRect = Rectangle.Empty;
  83. var real = pos.RoundedPoint();
  84. var charCountWidth = 21;
  85. var charSize = 12; //pixelhandler does the 2x scaling on the fly.
  86. var size = (new Vector2(0, charSize) * zoom).RoundedPoint();
  87. var baksize = size;
  88. var width = 0;
  89. var skipletter = false;
  90. for (var i = 0; i < buffer.Length; i++)
  91. {
  92. size = baksize;
  93. var c = buffer[i];
  94. if (c == 0) continue;
  95. else if (c == (byte)FF8TextTagCode.Dialog)
  96. {
  97. if (++i < buffer.Length - 1)
  98. {
  99. c = buffer[i];
  100. switch ((FF8TextTagDialog)c)
  101. {
  102. // Most of these should be replaced before it gets here becuase they have
  103. // values set by other objects.
  104. case FF8TextTagDialog.CustomICON:
  105. DrawIcon(buffer, zoom, Fade, skipdraw, real, ref size, ref skipletter, ref i, ref c);
  106. break;
  107. }
  108. //if (!skipletter)
  109. SetRetRec(pos, ref ret, ref real, size);
  110. continue;
  111. }
  112. }
  113. else if (c == (byte)FF8TextTagCode.Key)
  114. {
  115. if (++i < buffer.Length - 1)
  116. {
  117. var k = (FF8TextTagKey)buffer[i];
  118. var str = Input2.ButtonString(k);
  119. var retpos = RenderBasicText(str, real, zoom, whichFont, Fade, lineSpacing, skipdraw, ColorID.Green);
  120. size.X = retpos.Width;
  121. //size.Y = retpos.Height;
  122. //real.X += retpos.Width;
  123. //TODO add key/controller input icons/text here.
  124. //if (!skipletter)
  125. SetRetRec(pos, ref ret, ref real, size);
  126. continue;
  127. }
  128. }
  129. else if (c == (byte)FF8TextTagCode.Color)
  130. {
  131. if (++i < buffer.Length - 1)
  132. {
  133. c = buffer[i];
  134. blink = c >= (byte)FF8TextTagColor.Dark_GrayBlink ? true : false;
  135. GetColorFromTag(c, out var nc, out var fc);
  136. color = nc ?? ColorID2Color[colorbak];
  137. faded_color = fc ?? ColorID2Blink[colorbak];
  138. SetRetRec(pos, ref ret, ref real, size);
  139. continue;
  140. }
  141. }
  142. else if (c == (byte)FF8TextTagCode.Line && NewLine(pos, lineSpacing, ref real, size))
  143. {
  144. SetRetRec(pos, ref ret, ref real, size);
  145. continue;
  146. }
  147. if (!skipletter)
  148. {
  149. var deltaChar = GetDeltaChar(c);
  150. if (deltaChar >= 0 && charWidths != null && deltaChar < charWidths.Length)
  151. {
  152. width = charWidths[deltaChar];
  153. size.X = (int)(charWidths[deltaChar] * zoom.X);
  154. }
  155. else
  156. {
  157. width = charSize;
  158. size.X = (int)(charSize * zoom.X);
  159. }
  160. var curSize = size;
  161. var verticalPosition = deltaChar / charCountWidth;
  162. //i.e. 1280 is 100%, 640 is 50% and therefore 2560 is 200% which means multiply by 0.5f or 2.0f
  163. destRect = new Rectangle(real, size);
  164. if (!skipdraw)
  165. {
  166. var sourceRect = new Rectangle((deltaChar - (verticalPosition * charCountWidth)) * charSize,
  167. verticalPosition * charSize,
  168. width,
  169. charSize);
  170. DrawLetter(whichFont, Fade, blink ? Color.Lerp(color, faded_color, Menu.Blink_Amount) : color, destRect, sourceRect);
  171. }
  172. }
  173. skipletter = false;
  174. SetRetRec(pos, ref ret, ref real, size);
  175. }
  176. ret.Height = size.Y + (real.Y - (int)pos.Y);
  177. return ret;
  178. }
  179. private static void SetRetRec(Vector2 pos, ref Rectangle ret, ref Point real, Point size)
  180. {
  181. real.X += size.X;
  182. var curWidth = real.X - (int)pos.X;
  183. if (curWidth > ret.Width)
  184. ret.Width = curWidth;
  185. }
  186. 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)
  187. {
  188. if (i + 3 < buffer.Length)
  189. {
  190. c = buffer[++i];
  191. short ic = c;
  192. c = buffer[++i];
  193. ic |= (short)(c << 8);
  194. var pal = buffer[++i];
  195. Memory.Icons.Trim((Icons.ID)ic, pal);
  196. var icon = Memory.Icons[(Icons.ID)ic];
  197. //Vector2 scale = Memory.Icons.GetTexture((Icons.ID)ic).ScaleFactor;
  198. if (icon != null)
  199. {
  200. var adj = (12 / (float)(icon.Height));
  201. var scale = new Vector2(adj * zoom.X);
  202. size.X = (int)(icon.Width * scale.X);
  203. size.Y = (int)(icon.Height * scale.X);
  204. var destRect = new Rectangle(real, size);
  205. //real.X += size.X;
  206. //skipletter = true;
  207. if (!skipdraw)
  208. Memory.Icons.Draw((Icons.ID)ic, pal, destRect, Vector2.Zero, Fade);
  209. }
  210. }
  211. }
  212. 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)
  213. => RenderBasicText(buffer, pos.ToVector2(), zoom, whichFont, Fade, lineSpacing, skipdraw, color, blink);
  214. 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)
  215. => RenderBasicText(buffer, new Vector2(x, y), new Vector2(zoomWidth, zoomHeight), whichFont, Fade, lineSpacing, skipdraw, color,blink);
  216. private static void GetColorFromTag(byte c, out Color? color, out Color? faded_color)
  217. {
  218. color = null;
  219. faded_color = null;
  220. ColorID? cid = null;
  221. switch ((FF8TextTagColor)c)
  222. {
  223. case FF8TextTagColor.Blue:
  224. case FF8TextTagColor.BlueBlink:
  225. cid = ColorID.Blue;
  226. break;
  227. case FF8TextTagColor.Green:
  228. case FF8TextTagColor.GreenBlink:
  229. cid = ColorID.Green;
  230. break;
  231. case FF8TextTagColor.Grey:
  232. case FF8TextTagColor.GreyBlink:
  233. cid = ColorID.Grey;
  234. break;
  235. case FF8TextTagColor.Purple:
  236. case FF8TextTagColor.PurpleBlink:
  237. cid = ColorID.Purple;
  238. break;
  239. case FF8TextTagColor.Red:
  240. case FF8TextTagColor.RedBlink:
  241. cid = ColorID.Red;
  242. break;
  243. case FF8TextTagColor.White:
  244. case FF8TextTagColor.WhiteBlink:
  245. // since ending cid change reverts cid to white. if you have a custom cid set
  246. // this will allow reverting to that.
  247. break;
  248. case FF8TextTagColor.Yellow:
  249. case FF8TextTagColor.YellowBlink:
  250. cid = ColorID.Yellow;
  251. break;
  252. case FF8TextTagColor.Dark_Gray:
  253. case FF8TextTagColor.Dark_GrayBlink:
  254. cid = ColorID.Dark_Grey;
  255. break;
  256. }
  257. if (cid.HasValue)
  258. {
  259. color = ColorID2Color[cid.Value];
  260. faded_color = ColorID2Blink[cid.Value];
  261. }
  262. }
  263. private static int GetDeltaChar(byte c) => (c - 32);
  264. private static bool NewLine(Vector2 pos, int lineSpacing, ref Point real, Point size)
  265. {
  266. bool gonext;
  267. real.X = (int)pos.X;
  268. real.Y += size.Y + lineSpacing;
  269. gonext = true;
  270. return gonext;
  271. }
  272. private void DrawLetter(Type whichFont, float Fade, Color color, Rectangle destRect, Rectangle sourceRect)
  273. {
  274. switch (whichFont)
  275. {
  276. case Type.menuFont:
  277. case Type.sysfnt:
  278. // if you use Memory.SpriteBatchStartAlpha(SamplerState.PointClamp); you won't need
  279. // to trim last pixel. but it doesn't look good on low res fonts.
  280. //trim pixels to remove texture filtering artifacts.
  281. //sourceRect.Width -= 1;
  282. //sourceRect.Height -= 1;
  283. Memory.SpriteBatch.Draw(whichFont == Type.menuFont ? menuFont : sysfnt,
  284. destRect,
  285. sourceRect,
  286. color * Fade);
  287. break;
  288. case Type.sysFntBig:
  289. if (sysfntbig != null)
  290. {
  291. if (!sysfntbig.Modded)
  292. {
  293. var ShadowdestRect = new Rectangle(destRect.Location, destRect.Size);
  294. ShadowdestRect.Offset(2, 2);
  295. sysfntbig.Draw(ShadowdestRect, sourceRect, Color.Black * Fade * .5f);
  296. }
  297. sysfntbig.Draw(destRect, sourceRect, color * Fade);
  298. }
  299. break;
  300. }
  301. }
  302. #endregion Methods
  303. }
  304. }