Шрифти: формат FNT, українські літери й Franklin Gothic Medium
Гра не підтримує жодної мови з літерами І і Ї ї Є є Ґ ґ. Щоб показати
український текст, ці вісім літер треба було додати в обидва шрифти гри,
не маючи ні специфікації формату, ні інструментів розробника. Ця сторінка
пояснює, як улаштовані шрифти, як ми їх змінили і які помилки знайшли лише
в живій грі.
Два шрифти гри
Розділ «Два шрифти гри»| Font 1 — основний інтерфейс | Font 2 — «пригодницький» | |
|---|---|---|
| Файли | FONT_PC.FNT, PC_FONT.FNT, PC_FONT_PC.FNT, PC_FONT_NGC.FNT |
FONT2.FNT, FONT2_PC.FNT, FONT2_NGC.FNT |
| Внутрішня назва | starwars_font_pc — шрифт перейшов з LEGO Star Wars: The Complete Saga |
font2 |
| Вигляд | округлий, білий з темним обведенням | похилий, помаранчевий, у стилі постерів Індіани |
| Де видно | меню, підказки, імена, HUD | екрани завершення рівня, частково титри |
| Основа української версії | атлас сторонньої кириличної латки шрифту + 6 наших гліфів | оригінальний FONT2_PC.FNT + 33 гліфи з фанатського шрифту Adventure |
Гра шукає основний шрифт під чотирма іменами, а другий — під трьома. Усі
імена отримують однаковий вміст. У UKRAINIAN.uap однакові файли посилаються
на спільні байти, тож псевдоніми не збільшують розмір пакета.
Де саме рендериться Font 2, автор встановив спостереженням у грі: заголовок завершення рівня, «вільна гра розблокована», імена щойно відкритих персонажів, «справжній шукач пригод», відсоток проходження, скарби й артефакти, «продовжити історію», «Коледж Барнетта», а також окремі написи в титрах.
Кнопки геймпада (A, B, Y) — окремі шрифти BUTTONS_*.FNT, які ми не змінюємо.
Як улаштований .FNT
Розділ «Як улаштований .FNT».FNT — бінарний контейнер класичного рушія TT Games (LEGO Star Wars: TCS,
LEGO Indiana Jones, LEGO Batman). Публічної специфікації немає; формат
відновлено з байтів, дампів падінь і порівняння зі спорідненим форматом .ft2.
Усі числа — little-endian.
0x00 u32 відносна адреса relocation footer (НЕ magic: «pa»/«pb» — збіг байтів)0x08 u32 dds_pointer: DDS починається з 8 + значення0x1C u32 кількість записів метрик0x20 u32 кількість слотів таблиці mapping0x24 f32 height — висота рядка атласу, спільна для всіх гліфів (48 у Font 1)0x28 f32 base — базова лінія (37 в оригінальному Font 1)0x2C f32 ще не розшифроване поле міжлітерного проміжку0x44 u32 відносний вказівник на метрики (0x44 + значення = 0x60)0x48 u32 відносний вказівник на mapping (0x48 + значення)
0x60 метрики: по 12 байтів на гліф = float32 X, float32 Y, float32 Width адреса запису = 0x60 + glyph_index * 12... mapping: пари (u16 codepoint, u16 glyph_index), відсортовані за codepoint, термінатор (0xFFFF, 0xFFFF), далі нульове доповнення... 128-байтний опис текстури рушія: ширина, висота, кількість mip, розмір DDS... DDS (DXT5) — растровий атлас з усіма mip-рівнями... relocation footer: u32 count + знакові відносні зсуви до полів-вказівниківКілька важливих наслідків:
- Висота не зберігається на гліф. Кожен рядок атласу має рівно
heightпікселів, рядки йдуть впритул.Width— водночас ширина ділянки текстури і крок пера при друку. - Таблиця mapping має бути відсортована. Рушій шукає гліф бінарним пошуком.
- Перед DDS лежить не порожнеча, а опис текстури, а після DDS — footer, за яким рушій перетворює відносні вказівники на абсолютні. Збільшиш атлас, не оновивши їх, — гра впаде.
Як знайшли таблицю метрик
Розділ «Як знайшли таблицю метрик»Mapping розшифрували швидко, а метрики довго не піддавалися: перша спроба
бачила в них трійки (значення, 0.0, 1.0) і ламалася десь на 16-му записі.
Підказкою став відкритий парсер .ft2 — новішого шрифтового формату TT Games
з TTGames-Explorer-Rebirth
(Formats/FT2.cs). Там кожен гліф — (X, Y, Width) плюс спільна висота.
Ту саму форму перевірили на наших байтах: вузька I дала Width = 13,
широка K — 32, W — 48, m — 42. Гіпотеза підтвердилася з першої спроби.
Колишні «0.0» виявилися координатою Y = 0 у першому рядку атласу.
Font 2 і краш завантажувача
Розділ «Font 2 і краш завантажувача»Перша українська збірка Font 2 падала на старті. Дамп показав виняток
0xC0000005 у функції гри, яка проходить relocation footer контейнера:
006ad364 mov ecx, [edx] ; відносна адреса footer з поля 0x00006ad366 add ecx, edx006ad36b mov edx, [ecx] ; кількість записів...006ad37a mov esi, [edx+ecx] ; падіння: адреса поза ресурсомНаш старий збирач, збільшуючи атлас з 512×512 до 1024×1024, викидав опис
текстури й footer і не оновлював вказівник у 0x00. За старою адресою вже
лежали пікселі нового DDS, і рушій читав їх як кількість записів. Новий
збирач зберігає опис текстури, перераховує footer і всі pointer/count-поля
та перевіряє структуру після збірки.
| Поле | Оригінальний Font 2 | Український Font 2 |
|---|---|---|
| Розмір файла | 352 692 | 1 401 908 |
| Атлас | 512×512 | 1024×1024 |
| Записи метрик | 165 | 198 |
| Записи mapping | 164 | 230 |
| Relocation-записи | 16 | 16 |
Українські гліфи займають індекси 164–196. Font 2 — шрифт великих літер, тому великі й малі коди українських букв ділять ті самі 33 гліфи.
Font 1: вісім літер у чужому атласі
Розділ «Font 1: вісім літер у чужому атласі»Основу Font 1 взято з атласу сторонньої кириличної латки шрифту для цієї гри.
Там уже був повний кириличний алфавіт, але без І і Ї ї Є є Ґ ґ. Додавати
нові записи метрик для Font 1 ми не стали. Натомість використали непотрібні
для українського тексту польські гліфи атласу (Ą Ć Ę ę Ł ń Ś ś):
- Перемалювали пікселі в їхніх прямокутниках літерами Franklin Gothic Medium.
- Переписали codepoint у таблиці mapping на український і пересортували таблицю (див. помилку 1 нижче).
- Переписали
Widthна реальну ширину нової літери.
І та і не малюються взагалі: вони копіюють X/Y/Width латинських I та i
з того ж атласу. Форма однакова, а чистий оригінальний гліф ніколи не
перестискався. Малюються лише шість літер: Ї ї Є є Ґ ґ.
Звідки Franklin Gothic Medium
Розділ «Звідки Franklin Gothic Medium»Шрифт інтерфейсу гри — Franklin Gothic Medium з білою заливкою й темним обведенням. Автор ідентифікував його візуально й перевірив накладанням в Affinity: векторний текст поверх пікселів атласу TT.

На знімку верхній («ГДЕ») і нижній («СТУФ») рядки — пікселі атласу гри, середній рядок — живий текст Franklin Gothic Medium з такими параметрами:
| Параметр | Значення |
|---|---|
| Гарнітура | Franklin Gothic Medium, Regular |
| Кегль | 46,62 pt (інтерліньяж 52,86 pt) |
| Обведення | 3 pt, назовні (align outside) |
| З’єднання | округле (round join), miter 2 |
| Заливка | суцільна біла, обведення темно-сіре (41, 40, 41) |
Контури збігаються з атласом, тож нові літери малюються тим самим шрифтом і тими самими параметрами. Крім власного накладання, для звірки форм використовувався знімок атласу, видобутий сторонньою людиною. Його ми не публікуємо: це чужий матеріал.
Ліцензія шрифту
Розділ «Ліцензія шрифту»Franklin Gothic Medium — комерційний шрифт (ITC, нині Monotype). Він не входить до складу Windows; Microsoft зазначає, що він постачається з програмами Office. Автор отримав його окремо.
- У репозиторії й релізі є лише растрові гліфи шести літер (PNG і пікселі
в атласі
.FNT). Файлу шрифту немає ніде. - Для власних експериментів отримайте шрифт легально, наприклад з ліцензією
від правовласника (ITC Franklin Gothic на MyFonts)
або разом із ліцензованим Office, і покладіть у
local/fonts/. Очікувана назва й SHA-256 — уmanifests/inputs.json, інструкція — у setup.md.
Шрифт Adventure для Font 2 — фанатський шрифт Neale Davidson
(Pixel Sagas); перед використанням
перевірте умови автора. Його файл також лежить лише в local/donors/.
Шістнадцять помилок, які знайшла тільки жива гра
Розділ «Шістнадцять помилок, які знайшла тільки жива гра»Перша збірка пройшла всі байтові перевірки: парсер читав і записував файл без втрат, PNG-прев’ю на білому тлі виглядало правильно. У грі ж знайшлося ось що.
- Невідсортований mapping. Codepoint змінювали на місці, не переносячи
запис. На одному екрані
імалювалася, на іншому — ні: бінарний пошук рушія збивався. Рішення — повне пересортування таблиці після перепризначення. - Без заливки. Гліфи малювалися лише контуром. На білому PNG це було
непомітно, у грі текст став майже прозорим. Справжні гліфи мають запечену
білу заливку
(255, 255, 255, 255). - Різний масштаб. Кожна літера масштабувалася окремо під прямокутник
донора, і малі літери ставали вищими за сусідні. Тепер один глобальний
масштаб калібрується за висотою кириличної
О(39 px). - Неправильна базова лінія. Рядки донорів резервують місце під
«хвостики» польських
Ę ą, тому нижній край прямокутника не є базовою лінією. Її виміряли на сусідніх гліфах без хвостиків. Ємайже якє. Запобіжник від виходу за межі клітинки стискав обидва виміри. Тепер стискається лише ширина.- Прямокутники «на око». Після розшифрування метрик виявилося, що прямокутники знято з PNG на 1–3 px не там. Тепер координати беруться з таблиці.
ЇіҐвиходили вгору за межі рядка. Додано обмеження висоти відносно вільного місця над базовою лінією, як у штатноїЙ.- Зайвий проміжок після
і. Бічний відступ +6 px підібрали на широких літерах; у слові «Дітріх» він розривав слово. Зменшено до 3 px. - Базова лінія на 1 px вище. Перевірено на слові «Увімк.» з рискою під рядком: зсув на 2 px виявився забагато, зупинилися на 1 px.
- Ореол від DXT5. Перестискання всього атласу змішувало кольори в блоках 4×4 на межі з сусідніми гліфами. Тепер змінюються лише блоки під маскою наших гліфів (792 з 65 536), а зона очищення вирівняна по блоках.
- Ореол від mip-рівнів. Патчився лише mip0, а в mip1 лишалися старі польські літери. При трилінійній фільтрації вони просвічували. Тепер оновлюються mip0–mip4 (до 64×64).
- Слід сусідньої літери біля
і/ї. Атлас чистий, але GPU семплить частку пікселя за межею прямокутника. Розширити очищення не вийшло: впритул стоїть справжня рискаł. Допомогло інше: автор у переглядачі гліфів звузив і зсунув самі прямокутники вибірки, і ці ручні X/Width тепер застосовуються при кожній збірці. - Фіолетова пляма в контурі. Загальний DXT5-кодер Pillow підбирав
кольори каналів окремо, і сірий піксель
(41, 40, 41)ставав(54, 0, 54). Написано власний кодер блоків, який ставить обидві опорні точки на сіру вісьR = G = B. Інтерполяція між двома сірими кольорами не може дати відтінку. - Бруд під
Ґ. Одразу під рядком лежить акут літериź, якої немає в перекладі. Її прямокутник стирається; mapping лишається. І/іяк латиниця — спрощення, а не баг: копіювання метрик латинських гліфів замість малювання.- Надто вузька
Є. Стелею ширини була клітинка польськоїĘ(30 px), а природнаЄмає 36 px. Межу перенесено до реального сусіда, стиск зменшився з ~22 % до ~6 %.
Для таких перевірок автор тимчасово ставив замість «Нова гра» рядок з усіма вісьмома літерами поруч. Саме так вдалося зловити помилки 4 і 5.
Пізніші виправлення
Розділ «Пізніші виправлення»Іконки геймпада над текстом (0.1.3)
Розділ «Іконки геймпада над текстом (0.1.3)»Донорський атлас мав base = 0 замість штатних 37. Літери від цього не
зсунулися, а іконки кнопок Xbox, які рушій вирівнює за базовою лінією
шрифту, піднялися над текстом. Відновлення base = 37 змінило лише два
байти float-поля в кожній копії шрифту.
Трикутник прицілу (0.1.10)
Розділ «Трикутник прицілу (0.1.10)»Під ціллю кидка замість нижнього трикутника з’являвся синій прямокутник. Рушій малює чотири трикутники прицілу текстовим рендерером:
| Позиція | Код | Font 1 до виправлення |
|---|---|---|
| Знизу | U+00AB | відсутній |
| Зверху | U+00A3 | є |
| Зліва | U+003E | є |
| Справа | U+003C | є |
Адреси символів видно в коді гри (0x502371–0x502381 бере нижній трикутник
з 0x90995C і передає його рендереру 0x6997E0). Справжнє зображення
трикутника в донорі було, але під кодом U+00AC (гліф 21). Виправлення додає
U+00AB як другий mapping до того самого гліфа. Новий запис займає 4 байти
нульового доповнення перед описом текстури, тож розмір файла й адреси структур
не змінюються. Перевірка рантаймових символів, які генерує сам рушій, тепер
входить до тестів: звичайна перевірка покриття тексту їх не бачить.
Експеримент: атлас ×2
Розділ «Експеримент: атлас ×2»Автор намалював атлас 2048×2048 для чіткішого тексту. Гра падала, бо
інструкція 0x703b3a: mov [eax+0x78], 0x200000 передає DirectX лише 2 МіБ як
розмір DDS. Експериментальна збірка плагіна перехоплює завантаження саме
текстури шрифту і повертає рушію логічні 1024×1024, щоб метрики й UV лишилися
коректними. Варіант працює, але відкладений. Заодно з’ясувалося, що загальне
розмиття давав TextureQuality=0 у pcconfig.txt, а не шрифт.
Стан експерименту описано в experiments/.
Інструменти
Розділ «Інструменти»- TT Font Studio — браузерний
редактор
.FNTавтора: атлас, прямокутники, метрики, попередній перегляд тексту. Виріс із переглядача гліфів, у якому знайдено помилки 12–16. ua/formats/fnt.py— читання й запис.FNT, перепризначення codepoint із пересортуванням, додавання гліфів зі збереженням службових структур.ua/formats/bc_gray.py,bc_color.py— блокові DXT-кодери.
Повні докази — в архіві досліджень автора.