Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
<OutputPath>bin\$(Configuration)\$(MSBuildProjectName)\</OutputPath>
<DefaultItemExcludes>$(DefaultItemExcludes);obj\**;obj\**\*;bin\**;bin\**\*</DefaultItemExcludes>
</PropertyGroup>
</Project>
4 changes: 2 additions & 2 deletions FontMod/FontMod-KM.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<TargetFramework>net481</TargetFramework>
<AssemblyName>FontMod</AssemblyName>
<Description>FontMod</Description>
<Version>1.1.2</Version>
<Version>1.1.3</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RootNamespace>FontMod</RootNamespace>
Expand All @@ -28,7 +28,7 @@
<None Include="Info.json" CopyToOutputDirectory="PreserveNewest" Link="%(Filename)%(Extension)" />
<None Update="Fonts\Ldfcomicsans-jj7l.ttf" CopyToOutputDirectory="PreserveNewest" />
<None Update="Fonts\No Name Sans.ttf" CopyToOutputDirectory="PreserveNewest" />
<None Update="TMPro_Plugin.dll" CopyToOutputDirectory="PreserveNewest" />
<None Update="TMP_Utils\TMPro_Plugin.dll" CopyToOutputDirectory="PreserveNewest" Link="TMPro_Plugin.dll" />
</ItemGroup>

<Target Name="Deploy" AfterTargets="Build">
Expand Down
4 changes: 2 additions & 2 deletions FontMod/FontMod-RT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<TargetFramework>net481</TargetFramework>
<AssemblyName>FontMod</AssemblyName>
<Description>FontMod</Description>
<Version>1.1.2</Version>
<Version>1.1.3</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RootNamespace>FontMod</RootNamespace>
Expand Down Expand Up @@ -41,7 +41,7 @@
<ItemGroup>
<Files Include="$(TargetDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(Files)" DestinationFiles="@(Files->'$(RogueTraderInstallDir)\Mods\$(AssemblyName)\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(Files)" DestinationFiles="@(Files->'$(RogueTraderData)\UnityModManager\$(AssemblyName)\%(RecursiveDir)%(Filename)%(Extension)')" />
<ZipDirectory SourceDirectory="$(MSBuildProjectDirectory)\$(OutputPath)" DestinationFile="$(MSBuildProjectDirectory)\$(OutputPath)\..\$(AssemblyName)-$(Version)-RT.zip" Overwrite="true" />
</Target>

Expand Down
2 changes: 1 addition & 1 deletion FontMod/FontMod-WOTR.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<TargetFramework>net481</TargetFramework>
<AssemblyName>FontMod</AssemblyName>
<Description>FontMod</Description>
<Version>1.1.2</Version>
<Version>1.1.3</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RootNamespace>FontMod</RootNamespace>
Expand Down
147 changes: 131 additions & 16 deletions FontMod/FontSwap/FontDataModel-KM.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,46 @@
#if KM
using Newtonsoft.Json;
using System;
using System.Drawing;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Kingmaker.Localization;
using TMPro;
using TMPro.EditorUtilities;
using UnityEngine;
using static RootMotion.FinalIK.GrounderQuadruped;

namespace FontMod.FontSwap;

[Serializable]
public class FontDataModel
{
private const int FontAtlasCacheKeyVersion = 2;
private static string requiredCharacters;

[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public bool IsIgnored { get; set; }
[JsonIgnore]
public string FontPath { get; set; }
[JsonIgnore]
public TMP_FontAsset TMP_FontAsset { get; private set; }
public TMP_FontAsset TMP_FontAsset
{
get
{
// We're lazy because large fonts (e.g. chinese fonts) can take a lot of time
if (!fontAssetCreationAttempted && !IsIgnored && !string.IsNullOrEmpty(FontPath)) {
fontAssetCreationAttempted = true;
tmpFontAsset = CreateFontAsset(FontPath);
}

return tmpFontAsset;
}
}

private TMP_FontAsset tmpFontAsset;
private bool fontAssetCreationAttempted;

private FontDataModel() { }

Expand All @@ -33,7 +53,6 @@ private FontDataModel(string fontPath)

FontPath = fontPath;
Name = Path.GetFileNameWithoutExtension(fontPath);
TMP_FontAsset = CreateFontAsset(fontPath);
}
catch (Exception e)
{
Expand All @@ -44,7 +63,7 @@ private FontDataModel(string fontPath)
public override bool Equals(object obj)
{
if (obj is FontDataModel other)
return Equals(FontPath, other.FontPath) && Equals(TMP_FontAsset, other.TMP_FontAsset);
return Equals(FontPath, other.FontPath);

return false;
}
Expand All @@ -55,7 +74,6 @@ public override int GetHashCode()
{
int hash = 17;
hash = hash * 31 + (FontPath != null ? FontPath.GetHashCode() : 0);
hash = hash * 31 + (TMP_FontAsset != null ? TMP_FontAsset.GetHashCode() : 0);
return hash;
}
}
Expand All @@ -74,20 +92,47 @@ public static TMP_FontAsset CreateFontAsset(string fontPath)

var create = new TMPro_FontAssetCreatorWindow();
create.font_TTF_path = fontPath;
create.GenerateFontAtlas();
var characters = GetRequiredCharacters();
create.SetCharacterSet(characters);
var renderType = create.UseBitmapFontAsset ? "bitmap" : "SDF";
var cachePath = GetFontAtlasCachePath(fontPath, characters, create);
bool generated = false;

if (create.TryLoadFontAtlasCache(cachePath))
{
Main.Logger.Log($"Loaded cached {renderType} atlas for {name}.");
}
else
{
generated = true;
Main.Logger.Log($"Generating {create.CharacterCount} glyphs for {name} in a {create.AtlasSize}x{create.AtlasSize} {renderType} atlas.");
create.GenerateFontAtlas();
}

create.CreateFontTexture();

asset = create.Save_SDF_FontAsset();
asset = create.UseBitmapFontAsset ? create.Save_Normal_FontAsset() : create.Save_SDF_FontAsset();

if (asset == null)
throw new NullReferenceException($"Creation of TMP_FontAsset failed for font {name}");
else
Main.Logger.Log($"Created font asset {asset.name}");

asset.name = name;

if (asset != null)
MaterialReferenceManager.AddFontAsset(asset);
asset.ReadFontDefinition();

if (generated)
{
try
{
create.SaveFontAtlasCache(cachePath);
}
catch (Exception e)
{
Main.Logger.Warning($"Could not cache the generated atlas for {name}: {e.Message}");
}
}

Main.Logger.Log($"Created font asset {asset.name}");

MaterialReferenceManager.AddFontAsset(asset);
}
catch (Exception e)
{
Expand All @@ -96,7 +141,77 @@ public static TMP_FontAsset CreateFontAsset(string fontPath)

return asset;
}

private static string GetFontAtlasCachePath(string fontPath, string characters, TMPro_FontAssetCreatorWindow creator)
{
byte[] fontHash;
using (var stream = File.OpenRead(fontPath)) {
using var hash = SHA256.Create();
fontHash = hash.ComputeHash(stream);
}

string settings = $"{FontAtlasCacheKeyVersion}\n{creator.AtlasSize}\n{creator.UseBitmapFontAsset}\n{characters}";
byte[] settingsBytes = Encoding.UTF8.GetBytes(settings);
byte[] keyData = new byte[fontHash.Length + settingsBytes.Length];
Buffer.BlockCopy(fontHash, 0, keyData, 0, fontHash.Length);
Buffer.BlockCopy(settingsBytes, 0, keyData, fontHash.Length, settingsBytes.Length);

byte[] cacheHash;
using (var hash = SHA256.Create()) {
cacheHash = hash.ComputeHash(keyData);
}

string cacheKey = BitConverter.ToString(cacheHash).Replace("-", "").ToLowerInvariant();
return Path.Combine(Main.ModEntry.Path, "Cache", cacheKey + ".fontatlas");
}

private static string GetRequiredCharacters()
{
if (requiredCharacters != null) {
return requiredCharacters;
}

var characters = new HashSet<char>();
// Printable ASCII is required for TMP rich-text tags and ordinary UI text.
for (char character = ' '; character <= '~'; character++) {
characters.Add(character);
}

// TMP support characters: non-breaking space, zero-width space, ellipsis, and missing-glyph box.
characters.Add('\u00a0');
characters.Add('\u200b');
characters.Add('\u2026');
characters.Add('\u25a1');

AddLocalizationCharacters(characters, LocalizationManager.CurrentPack);
AddLocalizationCharacters(characters, LocalizationManager.CurrentPackFast);

requiredCharacters = new string(characters.OrderBy(character => character).ToArray());
Main.Logger.Log($"Preparing {requiredCharacters.Length} distinct characters for the current Kingmaker localization.");
return requiredCharacters;
}

private static void AddLocalizationCharacters(HashSet<char> characters, LocalizationPack pack)
{
if (pack?.Strings == null) {
return;
}

foreach (string value in pack.Strings.Values)
{
if (string.IsNullOrEmpty(value)) {
continue;
}

foreach (char character in value)
{
if (!char.IsControl(character)) {
characters.Add(character);
}
}
}
}
}


#endif
#endif
56 changes: 44 additions & 12 deletions FontMod/FontSwap/FontSwapperPatches.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
using FontMod.Utility;
using HarmonyLib;
using Kingmaker.UI.Common;
#if KM
using Kingmaker.Localization;
#endif
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text.RegularExpressions;
using TMPro;
using UnityEngine;
namespace FontMod.FontSwap;

[HarmonyPatch]
public static class TMPTestPach
{
private static bool CanSwapFonts
{
get
{
#if KM
return LocalizationManager.CurrentPack != null;
#else
return true;
#endif
}
}

private static TMP_FontAsset GetMappedFontAsset(TMP_FontAsset fontAsset)
{
return fontAsset == null || !CanSwapFonts ? fontAsset : FontMapper.Instance.GetFontMapped(fontAsset);
}

#if RT
static bool _afterDelay = false;
Expand All @@ -31,8 +49,7 @@ static void TextPatch(TextMeshProUGUI __instance)
if (!_afterDelay)
return;
#endif
if (__instance.m_fontAsset != null)
__instance.m_fontAsset = FontMapper.Instance.GetFontMapped(__instance.m_fontAsset);
__instance.m_fontAsset = GetMappedFontAsset(__instance.m_fontAsset);
}

[HarmonyPatch(typeof(MaterialReferenceManager), nameof(MaterialReferenceManager.TryGetFontAsset))]
Expand All @@ -45,8 +62,7 @@ static void TryGetFontAsset(ref TMP_FontAsset fontAsset)
return;
#endif

if (fontAsset != null)
fontAsset = FontMapper.Instance.GetFontMapped(fontAsset);
fontAsset = GetMappedFontAsset(fontAsset);
}

[HarmonyPatch(typeof(TMP_Text), nameof(TMP_Text.ValidateHtmlTag))]
Expand All @@ -68,9 +84,8 @@ static IEnumerable<CodeInstruction> TagPatch(IEnumerable<CodeInstruction> instru

var patchCodes = new CodeInstruction[]
{
new(OpCodes.Call, AccessTools.PropertyGetter(typeof(FontMapper), nameof(FontMapper.Instance))),
new(OpCodes.Ldloc_S, ldlocs.operand),
new(OpCodes.Callvirt, AccessTools.Method(typeof(FontMapper), nameof(FontMapper.GetFontMapped))),
new(OpCodes.Call, AccessTools.Method(typeof(TMPTestPach), nameof(GetMappedFontAsset))),
new(OpCodes.Stloc_S, ldlocs.operand)
};

Expand All @@ -91,12 +106,29 @@ static IEnumerable<CodeInstruction> TagPatch(IEnumerable<CodeInstruction> instru
[HarmonyPrefix]
static void GetSaberBookFormatPatch(string name, Color color, int size, ref Material material)
{
if (material == null)
if (material == null || !CanSwapFonts)
return;

material = FontMapper.Instance.FontMappings.ContainsKey("Saber_Dist32") ?
FontMapper.Instance.FontMappings["Saber_Dist32"].TMP_FontAsset.material :
FontMapper.Instance.DefaultFontMapping.TMP_FontAsset.material;
FontMapper mapper = FontMapper.Instance;
FontDataModel mapping;

if (mapper.FontMappings.TryGetValue("Saber_Dist32", out mapping))
{
// An ignored mapping means that both the original font and its original material must pass through untouched.
// => Otherwise e.g. nameplate disappears?
if (mapping.IsIgnored) {
return;
}
}
else
{
mapping = mapper.DefaultFontMapping;
}

Material mappedMaterial = mapping?.TMP_FontAsset?.material;
if (mappedMaterial != null) {
material = mappedMaterial;
}
}
#endif
}
}
2 changes: 1 addition & 1 deletion FontMod/Info.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"Id": "FontMod",
"DisplayName": "FontMod",
"Author": "Hambeard",
"Version": "1.1.2",
"Version": "1.1.3",
"ManagerVersion": "0.23.0",
"Requirements": [],
"AssemblyName": "FontMod.dll",
Expand Down
Loading