Skip to content
Merged
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
19 changes: 19 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.{cs,csproj,props,config,slnx}]
indent_style = space
indent_size = 4

[*.{yml,yaml,json}]
indent_style = space
indent_size = 2

[**/obj/**]
generated_code = true
end_of_line = unset
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
pull_request:
push:
branches: [main]
workflow_call:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: "10.0.112"
cache: true
cache-dependency-path: "**/packages.lock.json"

- run: dotnet restore UnityVietnameseFontSync.slnx --locked-mode
- run: dotnet format whitespace UnityVietnameseFontSync.slnx --no-restore --verify-no-changes
- run: dotnet build UnityVietnameseFontSync.slnx -c Release --no-restore --warnaserror
- run: dotnet test tests/UnityVietnameseFontSync.Tests.csproj -c Release --no-build --no-restore
- run: python3 -m pip install --require-hashes -r scripts/requirements.txt
- run: python3 -m unittest discover -s scripts -p 'test_*.py'

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: plugin
path: src/bin/Release/netstandard2.1/UnityVietnameseFontSync.dll
if-no-files-found: error
50 changes: 50 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: read

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
check:
uses: ./.github/workflows/ci.yml

publish:
needs: check
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: plugin
path: artifacts/plugin

- run: python3 -m pip install --require-hashes -r scripts/requirements.txt

- name: Validate release metadata and package plugin
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python3 scripts/package.py --tag "$RELEASE_TAG" --dll artifacts/plugin/UnityVietnameseFontSync.dll

- name: Publish release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_TAG: ${{ github.ref_name }}
run: >-
gh release create "$RELEASE_TAG"
artifacts/release/*.zip
--verify-tag --title "$RELEASE_TAG"
--notes-file artifacts/release/notes.md
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
**/bin/
**/obj/
.vs/
.idea/
.venv/
*.user
*.suo
Directory.Build.props
artifacts/
__pycache__/
*.pyc
37 changes: 37 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Agent Instructions

## Toolchain

- Use stable .NET 10 via `global.json`, Python 3 for packaging checks, and `fd` for file discovery.
- Restore with `dotnet restore UnityVietnameseFontSync.slnx --locked-mode`.
- Stop and report missing development tools or dependencies.

## Commands

| Task | Command |
|---|---|
| Configuration/adapter tests | `dotnet test tests/UnityVietnameseFontSync.Tests.csproj -c Release` |
| Release packaging tests | `python3 -m unittest discover -s scripts -p 'test_*.py'` |
| Check formatting | `dotnet format whitespace UnityVietnameseFontSync.slnx --no-restore --verify-no-changes` |
| Build | `dotnet build UnityVietnameseFontSync.slnx -c Release --no-restore --warnaserror` |

## Key Conventions

- Edit plugin identity/version in `src/UnityVietnameseFontSync.csproj`; do not edit generated `MyPluginInfo` constants.
- Keep game paths/mappings, game DLLs, and proprietary fonts out of committed runtime/build configuration.
- Keep runtime compatibility checks capability-based; do not add game-name/version heuristics.
- Keep destructive synchronization opt-in and unmatched fonts unchanged.
- Update lock files with dependency changes; review Unity baseline upgrades for compatibility.
- Record user-visible changes under `Unreleased` in `CHANGELOG.md`.
- Do not deploy, publish, or commit unless requested.

## External References

| Need | File |
|---|---|
| User setup | `README.md` |
| Configuration contract | `docs/configuration.md` |
| Runtime design, build, testing, release | `docs/development.md` |
| Change history | `CHANGELOG.md` |
| CI | `.github/workflows/ci.yml` |
| Published releases | `.github/workflows/release.yml` |
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Changelog

All notable user-facing changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/2.0.0/),
and this project uses [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [1.0.0] - 2026-10-15

### Added

- Vietnamese glyph fallback for supported Unity Mono games using TextMeshPro.
- Optional font synchronization to keep accented and unaccented letters visually consistent.
- Multiple font profiles, ordered matching rules, and exclusions for fonts such as icons and symbols.
- Ordered fallback font files within a profile, with required glyph coverage checked across the complete chain.
- Configurable font files, atlas settings, and support for late-loaded game fonts.
- Warnings for missing font files, unsupported TMP APIs, and characters the selected font cannot provide.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Zeldris

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
8 changes: 8 additions & 0 deletions NuGet.Config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="BepInEx" value="https://nuget.bepinex.dev/v3/index.json" />
</packageSources>
</configuration>
117 changes: 117 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Unity Vietnamese Font Sync

A configurable **BepInEx 5 plugin for Unity Mono games using TextMeshPro**. Add missing Vietnamese glyphs or use a consistent replacement font for selected font groups.

This plugin does not translate text. It can work alongside XUnity.AutoTranslator or an existing Vietnamese translation.

## Compatibility

- Windows, BepInEx 5, Unity Mono, and compatible TextMeshPro APIs.
- No game-specific font mappings or paths are built in.
- BepInEx 6, IL2CPP, legacy Unity UI, NGUI, and UI Toolkit are not supported.
- TMP APIs are discovered at runtime; missing required capabilities disable font changes with a log message. This is not a guarantee of compatibility with every Unity/TMP version.

## Features

- Named font profiles and explicitly ordered matching rules.
- **Fallback:** retain original glyphs and append a Vietnamese-capable font.
- **Synchronize:** opt-in replacement of a matched font group's glyphs.
- Preserve existing fallback chains; leave unmatched fonts unchanged.
- Validate required glyph coverage before applying a profile.
- Discover late-loaded fonts with configurable scanning, retry delays, and diagnostics.
- Load local font files without downloading fonts or using a translation service.

## Installation

1. Install [BepInEx 5 for Unity Mono](https://docs.bepinex.dev/articles/user_guide/installation/index.html).
2. Extract the release archive into `<Game>/BepInEx/`. The plugin DLL should be at:
```text
<Game>/BepInEx/plugins/zeldrisho.UnityVietnameseFontSync/UnityVietnameseFontSync.dll
```
3. Run the game once with the plugin installed to generate:
```text
<Game>/BepInEx/config/zeldrisho.UnityVietnameseFontSync.cfg
```
4. Close the game, configure a font file and matching rules, then start it again.

There is no need for a separate BepInEx-only run before installing this plugin. If you previously used a UnityExplorer font script, restart with that script disabled and do not run it alongside the plugin.

The initial configuration makes **no font changes**. No game assemblies or proprietary font files are bundled.

## Worked configuration example

The generated config starts with no matching rules, so it makes no font changes. Close the game before editing the file, then restart it. Enable diagnostics to find the TMP font asset name in `BepInEx/LogOutput.log`; match the asset name, not the text component name.

This complete example synchronizes one fictional asset named `MainMenu SDF`, using a primary font plus a secondary font for missing characters. Replace the filenames and exact asset name with values available in your installation. Put both licensed font files in the plugin's `Fonts/` directory. **Synchronize replaces the matched font's characters in memory**; do not broaden the match to icon or unrelated fonts.

```ini
[General]
Enabled = true
Diagnostics = true
ScanInterval = 2
RetryInterval = 30

[Fonts]
Profiles = menu
CustomCharacters =

[Font.menu]
File = MyVietnameseFont.ttf
FallbackFiles = CoverageFont.ttf
SamplingSize = 36
Padding = 4
AtlasSize = 2048
MultiAtlas = true

[Rules]
Order = menu-text
Exclude = Icons, Symbols

[Rule.menu-text]
Profile = menu
Match = Exact
Pattern = MainMenu SDF
Mode = Synchronize
```

`FallbackFiles` supplies characters missing from the primary file, while keeping its style for available characters. Coverage must be complete across the ordered files; otherwise the profile is disabled. Leave this setting empty when the primary font covers the whole preset.

Keep `Synchronize` when replacing incorrect existing glyphs or making accented and unaccented letters use the same replacement style. Use `Mode = Fallback` instead if you only need missing-character coverage: it retains the original glyphs and cannot replace incorrect ones. Both modes preserve existing fallback chains and leave unmatched fonts unchanged. Restart without the plugin to restore the game's original assets.

See [configuration](docs/configuration.md) for multiple profiles, matching modes, exclusions, and atlas settings.

**Synchronization clears the matched font's character table in memory.** Use narrow rules and check symbols, untranslated text, and layout. Restart to recover. Matching outlines, shadows, spacing, and line breaks is not guaranteed.

## With XUnity.AutoTranslator

If XUnity.AutoTranslator's `FallbackFontTextMeshPro` already renders Vietnamese with acceptable styling, you don't need this plugin. Use it when you need targeted font replacement, consistent accented/unaccented styling, or different replacement fonts for different text groups.

Only when migrating TMP font handling to this plugin, start with XUnity's competing font settings empty:

```ini
[Behaviour]
OverrideFontTextMeshPro=
FallbackFontTextMeshPro=
```

Keep translation enabled. This plugin neither edits XUnity configuration nor rewrites translated strings. Avoid `RemoveAllDiacritics` in translation postprocessing.

## Troubleshooting

| Symptom | Check |
|---|---|
| Nothing changes | `Rules.Order`, exact asset names, `Enabled`, and profile `File`; rules are empty by default |
| Startup logs appear, but no later font discovery | If the game removes or disables BepInEx's manager object, try `[Chainloader] HideManagerGameObject = true` in `BepInEx/config/BepInEx.cfg`, then restart |
| Profile disabled | Log warnings for missing files, API failures, or missing `U+...` characters; try another font or larger atlas |
| Accents look wrong | Fallback cannot replace an existing incorrect glyph; consider narrowly targeted synchronization |
| Mixed styles | Fallback preserves available original glyphs; synchronization changes the whole matched font group |
| Icons/other languages disappear | Avoid broad synchronization and verify preserved fallback coverage |
| Combining marks fail | Add marks to `CustomCharacters`; test the source font and TMP shaping behavior |

`HideManagerGameObject` is a BepInEx setting, not a plugin setting; do not add it to this plugin's config. It hides BepInEx's manager object from scene discovery to help protect it from game cleanup. It affects all plugins on that manager and is not required in every game. Keep it enabled in installations where it restores ongoing scanning.

To disable, set `Enabled = false` and restart. To uninstall, close the game and remove the plugin DLL.

## License

[MIT](LICENSE). The license covers this plugin and its documentation, not third-party fonts or game assemblies. Do not redistribute proprietary system fonts or game assets.
8 changes: 8 additions & 0 deletions UnityVietnameseFontSync.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/UnityVietnameseFontSync.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/UnityVietnameseFontSync.Tests.csproj" />
</Folder>
</Solution>
Loading
Loading