Как я хотел просто заменить русский шрифт в A Plague Tale: Resonance — и за четыре дня дошёл до реверса DPC, SDF и собственного патчера (ОЧЕНЬ ДЛИННОПОСТ)
На этот мод ушло примерно четыре дня и около 40 часов работы.
Изначально ничего подобного я делать не собирался.
Мне просто не нравилось, как выглядит русский текст в A Plague Tale: Resonance. В английской версии интерфейс использует узкий Barlow Condensed, который отлично подходит визуальному стилю игры. Русская локализация переключалась на другой шрифт и рядом с оригиналом выглядела гораздо менее аккуратно.
Первая мысль была предельно простой:
а нельзя ли просто заменить русский шрифт на что-нибудь похожее?
Через четыре дня ответ на этот вопрос уже включал собственные кириллические версии Barlow и Cinzel, reverse engineering игрового формата шрифтов, SDF, BC3, LZ4, поиск физических atlas pages, donor-локализацию и отдельный бинарный патчер.
Но обо всём по порядку.
Не хочу делать вид, будто я четыре дня в одиночку сидел с дизассемблером и вручную писал весь инструментарий.
Большую часть программирования, анализа бинарных структур, написания Python-скриптов, расчётов и перебора технических гипотез выполняла нейросеть — ChatGPT.
Моя роль была другой.
Я вёл весь проект: определял, чего именно хочу добиться, вручную работал с самими шрифтами, постоянно проверял сборки непосредственно в игре, сравнивал результат с оригиналом, замечал ошибки, решал, какую гипотезу проверять следующей, останавливал бесперспективные ветки и несколько раз предлагал архитектурные решения, которые позволяли вообще выйти из тупика.
Это оказалось довольно важным разделением ролей.
Нейросеть может невероятно быстро разобрать бинарный файл, написать десяток инструментов и проверить сотни значений. Но она с такой же скоростью способна несколько часов очень убедительно развивать неправильную гипотезу.
Поэтому проект довольно быстро превратился в связку:
нейросеть — исследователь и программист; я — архитектор, арт-директор, тестировщик и человек, который периодически спрашивает: «А зачем мы вообще пытаемся решить проблему именно так?»
И несколько раз именно последний вопрос оказывался самым полезным.
Первым кандидатом стал PT Sans Narrow.
Он уже содержал кириллицу, был достаточно узким и хотя бы примерно соответствовал пропорциям английского интерфейса.
План казался простым:
1. найти русский шрифт внутри игровых файлов;
2. заменить его;
3. подобрать размер;
4. поправить положение;
5. готово.
Разумеется, уже на этом этапе всё пошло не по плану.
Пришлось выяснять, где игра хранит шрифты внутри DPC, искать атласы и таблицы глифов, понимать метрики, распаковывать ресурсы, собирать их обратно и после каждого изменения снова запускать игру.
PT Sans Narrow в итоге удалось довольно прилично подогнать.
И тут я сам испортил себе жизнь следующей мыслью:
если мы уже настолько глубоко залезли внутрь игры, зачем вообще использовать похожий шрифт?
Ведь можно взять тот самый Barlow Condensed, который используется в английской версии, и добавить кириллицу непосредственно в него.
С этого момента маленький мод на замену шрифта закончился.
Подходящей кириллицы у нужного Barlow не было, поэтому её пришлось делать самостоятельно.
Некоторые символы можно было собрать из существующих латинских форм. Какие-то кириллические буквы уже почти совпадают с латиницей. Другие пришлось фактически проектировать заново, стараясь не потерять характер оригинального шрифта.
Особенно много времени съели буквы вроде:
З, Д, Л, Ц, Щ, Ы и другие.
Менялись буквально мелочи:
· форма штрихов;
· пропорции;
· ширина;
· положение элементов;
· окончания;
· левый и правый bearing;
· расстояния между соседними буквами.
Одну З, например, я несколько раз практически собирал заново из элементов латинской S, двигая половинки и окончания, пока она не перестала выглядеть как посторонний символ, случайно попавший в Barlow.
Постоянно делались тестовые строки, увеличенные сравнения и проверки сочетаний букв.
Так постепенно появился мой собственный Barlow Condensed Cyrillic.
В обычном TTF он уже выглядел хорошо.
Оставалось «просто вставить его в игру».
Как выяснилось, создание самих букв было только половиной задачи.
Первый нормальный bake быстро показал неприятную вещь.
Тот же самый шрифт внутри игры выглядел иначе, чем исходный TTF.
Русские буквы оказывались немного:
· крупнее;
· толще;
· выше или ниже;
· рыхлее по spacing;
· с другими краями.
То есть проблема находилась уже не в дизайне символов.
Нужно было понять, как сама игра превращает исходный шрифт в то, что мы видим на экране.
Начался reverse engineering.
Постепенно пришлось разбираться с:
· glyph records;
· atlas pages;
· bearings и advances;
· RGB и alpha;
· signed distance field;
· BC3 / DXT5;
· LZ4;
· внутренней структурой DPC.
Неправильные гипотезы периодически давали очень художественные результаты: рваные буквы, полосы, грязь, какие-то орнаменты вместо текста, повреждённые атласы и иногда просто вылет игры.
Один из важных моментов — выяснилось, что игровой font atlas устроен не как обычная картинка.
RGB содержит grayscale distance field, а alpha — отдельную маску coverage.
То есть просто нарисовать белую букву на прозрачном фоне и засунуть её в текстуру было недостаточно.
Одним из первых серьёзных прорывов стало решение использовать в качестве эталона сами оригинальные буквы игры.
У латиницы и кириллицы есть символы с одинаковой формой:
a/а, e/е, o/о, p/р, x/х, часть заглавных и т. д.
Значит, вместо бесконечной подгонки «на глаз» можно было взять настоящий английский glyph из оригинального Barlow, перенести его между ресурсами и посмотреть, какое преобразование требуется, чтобы одна и та же буква снова выглядела одинаково.
Так для первого варианта, где Barlow переносился в русский ресурс BIG_RUS, удалось экспериментально получить масштаб примерно:
1.5595
и вертикальную компенсацию около:
+16 px.
Это был важный момент: у нас впервые появилась не просто визуальная догадка, а измеряемая связь между игровыми font resources.
Позже эти значения сыграли ещё одну важную роль: стало понятно, что они относятся именно к преобразованию BIG_FONT → BIG_RUS.
Когда архитектура мода изменилась и Barlow начал встраиваться непосредственно в native BIG_FONT, использовать 1.5595 и +16 уже было нельзя.
Но до этого понимания ещё нужно было дойти.
Следующей большой проблемой стали края символов.
Мы меняли:
· distance field;
· coverage;
· supersampling;
· crop;
· масштаб;
· положение внутри glyph rect;
· alpha inset;
· параметры RGB.
Некоторые изменения действительно помогали.
Другие выглядели совершенно логично в расчётах и почти ничего не меняли в самой игре.
Иногда два принципиально разных алгоритма после загрузки в игру выглядели практически одинаково.
Постепенно удалось восстановить модель raster, довольно близкую к оригинальной: нормальный SDF, отдельный alpha coverage, правильный supersampling, BC3/DXT5 и LZ4.
Но русский текст всё равно ощущался чуть более рыхлым.
Причина оказалась совсем в другом месте.
Оказалось, что при расчёте метрик использовалось лишнее промежуточное округление.
Ошибка в одной букве была почти незаметной.
Но строка состоит из десятков букв, и маленькие погрешности постепенно складывались.
В результате русский текст становился на несколько процентов более растянутым относительно того же TTF в нормальном рендерере.
После перехода на точные float-метрики непосредственно из TTF, без промежуточного округления, ритм строки наконец начал совпадать с оригиналом.
Это вообще стало одной из характерных особенностей проекта.
Можно несколько часов исследовать SDF, компрессию и устройство бинарного контейнера, а последняя заметная проблема внезапно оказывается следствием нескольких преждевременно округлённых чисел.
Когда Barlow наконец приблизился к нужному результату, стала особенно заметна другая проблема.
Оригинальный английский интерфейс использует два шрифта.
Barlow Condensed — для основного интерфейса, описаний и обычного текста.
Cinzel — для крупных заголовков и декоративных надписей.
А русский BIG_RUS фактически давал нам только один шрифтовой путь.
После замены русского шрифта на Barlow всё выглядело гораздо лучше — но теперь и те места, где английская версия показывала Cinzel, на русском тоже отображались Barlow.
То есть шрифт уже был правильным, а типографика игры всё равно оставалась неправильной.
И я решил, что одного Barlow недостаточно.
Если в оригинале используются два шрифта, значит и русская версия мода должна использовать два.
Сначала пришлось проделать похожую работу уже с самим Cinzel: добавить в него кириллицу, подобрать формы, размеры и метрики.
Так появился второй собственный TTF — Cinzel Cyrillic.
Теперь оставался гораздо более сложный вопрос:
как заставить игру использовать его именно там, где английская версия использует Cinzel?
С помощью одного BIG_RUS решить это не получалось.
Примерно в этот момент вышла новая релизная версия игры.
Внутренний ENGLISH.DPC изменился.
Старые offsets больше нельзя было просто перенести: изменилась не только позиция ресурсов в файле, но и сами таблицы.
Формально значительную часть работы по структуре пришлось начинать заново.
К счастью, теперь мы хотя бы уже знали, что ищем.
Именно здесь окончательно родилась новая архитектура:
не пытаться заставить русский BIG_RUS делать то, для чего он не предназначен, а отправить русский текст через обычный Latin font pipeline.
Для этого понадобился языковой слот-донор.
Им стал:
Spanish (Latin America), tt20.
На одном из промежуточных вариантов мы пытались встроить русский текст в существующую структуру донорского ресурса и начали упираться в ограничения размера и упаковки. Даже работа со сжатием уже не давала нормального пути вперёд.
Мы начали довольно бессмысленно топтаться вокруг того, как всё-таки заставить русский текст поместиться в испанский файл.
И тут я предложил вариант, который в ретроспективе кажется совершенно очевидным:
а зачем вообще пытаться его туда помещать?
У игры уже есть полностью готовый русский текстовый банк:
tt17.pc.
Можно просто взять его целиком, скопировать и назвать:
tt20.pc.
Всё.
Не переносить строки.
Не перепаковывать русский текст в испанскую структуру.
Не пытаться выиграть ещё несколько килобайт сжатием.
Просто:
tt17.pc → tt20.pc.
Игра считает, что выбран Spanish (Latin America), идёт через Latin font pipeline, но текст получает русский.
Эта довольно простая идея мгновенно убрала целое направление проблем, в котором мы уже начинали застревать.
Именно donor-схема в итоге стала основой финального мода.
После этого стало возможным работать уже с оригинальным общим BIG_FONT, которым пользуется Latin-версия игры.
В новой версии он содержал:
266 glyph records.
Для полного нужного набора пришлось расширить таблицу до:
321.
При этом создавать 55 новых картинок букв не потребовалось.
Часть кириллицы полностью совпадает с Latin outlines.
Например, кириллическую р можно привязать непосредственно к оригинальной Latin p, а — к a, а целый набор заглавных символов вообще может использовать существующие Latin raster и metrics байт-в-байт.
Кроме того, несколько кириллических символов уже присутствовали в самом BIG_FONT.
В результате реально новых уникальных Barlow raster glyphs осталось 39.
И здесь сформировалось важнейшее правило:
существующий Latin atlas лучше вообще не трогать, если этого можно избежать.
Следующие несколько тестов очень наглядно объяснили почему.
В первых donor-тестах была фактически перепакована заметная часть Latin atlas.
Русский появился.
А вместе с ним начали ломаться:
· English;
· German;
· Spanish;
· другие Latin-языки.
Отсюда появился первый жёсткий вывод:
никакого repack существующего Latin atlas.
В следующем варианте все оригинальные glyphs оставили на местах, а русские начали писать только туда, где пространство выглядело свободным.
Основной английский стал нормальным.
Зато начали портиться редкие Latin-символы.
И здесь обнаружился ещё один неприятный факт:
если на какой-то BC3-блок не ссылается glyph record, это совершенно не означает, что этот блок пустой.
В атласах находились реальные данные, которые невозможно было обнаружить простым просмотром glyph table.
Чтобы понять, ломает ли игру сама расширенная glyph table или наши записи в атлас, был сделан специальный диагностический билд.
В нём:
· таблица уже расширена 266 → 321;
· все новые Cyrillic records присутствуют;
· все четыре исходные atlas pages возвращены байт-в-байт в vanilla-состояние;
· tt20.pc уже является копией русского tt17.pc.
То есть русские буквы описаны, но их новых картинок в атласе фактически нет.
Результат:
· English идеально чистый;
· исходные ресурсы работают;
· таблица из 321 record совершенно безопасна;
· donor Russian ломается ровно потому, что нужного raster для новых букв ещё нет.
Это доказало главное:
структуру таблицы мы восстановили правильно.
Всё, что портило игру до этого, находилось исключительно в наших изменениях atlas pages.
TEST4 стал чистой контрольной точкой для следующих экспериментов.
После ещё нескольких тестов критерий свободного места пришлось сделать максимально строгим.
Теперь недостаточно было доказать, что BC3-блок не используется известным glyph record.
Он считался безопасным только в том случае, если байт-в-байт совпадал с подтверждённым blank block оригинального atlas.
Это наконец перестало портить English.
Но русский всё ещё имел характерную грязь: горизонтальные «полочки», обрубки и странные края.
Мы снова начали подозревать SDF.
И снова проблема находилась не там.
При сравнении с оригинальными Barlow glyphs обнаружилась интересная закономерность.
Glyph rect — это не вся область, которая нужна букве.
Сам видимый символ заканчивается внутри rect, но signed distance field продолжается ещё примерно на восемь пикселей наружу.
И именно поэтому оригинальные glyphs обычно не упакованы вплотную.
Наши же новые русские буквы местами находились всего в 0–3 пикселях друг от друга.
Сам glyph rect был совершенно правильным.
Но мы физически обрезали его внешний SDF.
Вот откуда бралась большая часть грязи.
После добавления настоящего ~8 px halo вокруг новых глифов русский Barlow резко очистился.
И тут возникла следующая проблема.
Тридцать девять новых букв с полноценным halo уже не помещались на четырёх штатных страницах BIG_FONT.
После добавления полноценного SDF halo выяснилась новая проблема:
39 новых Barlow-глифов физически не помещались на четырёх штатных страницах BIG_FONT.
Мы снова начали искать свободные области, пытаться оптимальнее размещать символы и оценивать, можно ли ещё что-то выиграть упаковкой.
Но запас места практически закончился.
И здесь мне пришла в голову другая идея.
В игре ведь есть не только латиница и кириллица. Есть ещё корейский, японский и китайский шрифты.
Я рассуждал довольно просто: для азиатских языков игра наверняка должна хранить значительно больше символов, а значит под них, скорее всего, выделено и гораздо больше atlas pages. Я в первую очередь подумал о корейском: визуально его алфавит сам по себе не кажется огромным, но ресурсов для полноценного шрифта всё равно должно требоваться заметно больше.
Почему бы тогда не взять свободное место у другого языкового ресурса?
После проверки предположение подтвердилось даже с запасом.
Внутри ENGLISH.DPC обнаружились большие наборы страниц:
· Korean — около 64 atlas pages;
· Japanese — около 86;
· Chinese — около 92.
То есть пока мы пытались уместить кириллицу в четыре страницы BIG_FONT, буквально рядом находились десятки других texture pages.
Это полностью изменило подход.
Вместо того чтобы ещё сильнее уплотнять основной Latin atlas, перепаковывать его или обрезать необходимый SDF halo, мы решили использовать проверенные свободные области уже существующих ресурсов других языков.
BIG_FONT получил дополнительные page references, а новые кириллические raster-глифы начали размещаться на этих страницах.
Так появились наши borrowed pages.
Первый успешный тест сразу подтвердил, что идея работает:
· новые буквы получили полноценный ~8 px halo;
· глобальная грязь русского практически исчезла;
· оригинальный English остался нетронутым.
Правда, в первой реализации туда заодно отправились несколько кириллических glyphs, которые уже имели собственные native slots внутри BIG_FONT. Из-за этого, например, исчезла У.
В следующем тесте их вернули на штатные места.
В итоге borrowed pages использовались только для 39 действительно новых Barlow-глифов.
После этого Barlow фактически был готов.
И это был ещё один момент проекта, когда решение пришло не из попытки лучше оптимизировать существующую схему, а из вопроса:
«Почему мы вообще обязаны помещать всё именно сюда?»
Оставались очень мелкие эффекты.
Иногда вертикальная часть р казалась немного неровной.
Иногда что-то похожее происходило с В.
Но у проблемы появилась странная особенность:
одна и та же буква в одном месте строки выглядела идеально, а в другом — чуть иначе.
Для проверки кириллическую р сделали буквально полной копией оригинальной Latin p: тот же raster, тот же rect, та же page, те же metrics.
Эффект всё равно иногда зависел от её положения в строке.
То же самое происходило с другими Cyrillic aliases, которые фактически являлись оригинальными Latin glyphs.
Это уже было хорошим доказательством того, что atlas исправен, а остаточная разница появляется из-за subpixel sampling самого renderer’а.
В какой-то момент важно уметь перестать чинить то, что уже не сломано.
Barlow был заморожен.
Теперь можно было заняться второй частью первоначальной идеи.
Для Cinzel к этому моменту уже существовал готовый Cyrillic TTF.
Внутри DPC был найден generic Latin Cinzel resource, который использует оригинальная игра.
Его glyph table тоже пришлось расширить и добавить туда кириллические records.
Сначала мы специально не использовали наш новый raster.
Кириллические records временно направили на уже существующие игровые символы.
Выглядело это неправильно, зато отвечало на один важный вопрос:
попадает ли вообще русская строка в нужный Cinzel resource?
Ответ оказался положительным.
Routing работал.
Значит теперь можно было заниматься самим Cinzel.
После первого настоящего bake Cinzel появились совершенно новые чудеса.
Например:
· Ы выглядела как П;
· Я превращалась в неправильную Х;
· У становилась Ч.
Первой подозреваемой снова стала растеризация.
Наш Cinzel действительно немного отличался по толщине от native Latin Cinzel.
Мы отдельно откалибровали его SDF.
Толщина стала заметно ближе к оригиналу.
Ы всё равно осталась П.
Самое интересное было в другом.
Если вручную декомпрессировать atlas и открыть координаты, указанные в record буквы Ы, там действительно лежит правильная Ы.
Получалась довольно абсурдная ситуация:
в файле Ы есть;
record указывает на Ы;
наш инструмент видит Ы;
игра показывает П.
Значит проблема находилась ещё на один уровень ниже.
Постепенно выяснилось, что цепочка выглядит примерно так:
glyph record → logical page → 64-bit page reference → physical texture внутри DPC.
Glyph record был правильным.
Logical page — тоже.
Но восстановленный нами mapping page reference оказался неверным.
В результате локальный инструмент открывал один physical atlas и видел правильную Ы, а игра по настоящему runtime mapping уходила на другую физическую страницу и находила в тех же координатах П.
Поэтому получались стабильные и очень странные подмены:
Ы → П
Я → Х
У → Ч.
Несколько тестов подряд пытались исправить эту схему эвристически.
Становилось только веселее.
В какой-то момент стало ясно, что автоматическому page-ref parser доверять больше нельзя.
Выходом стал практически криминалистический подход.
Вместо того чтобы пытаться вывести physical mapping из предполагаемой структуры контейнера, начали проверять его по содержимому самих vanilla glyphs.
Если мы знаем:
· какой символ должен быть в оригинальной игре;
· его glyph record;
· logical page;
· его rect;
можно проверить физические texture pages и найти ту, где в этих координатах действительно находится нужная буква.
Получилась цепочка:
Unicode → record → logical page → physical texture → rect → реальный raster.
Так необходимые physical pages были восстановлены уже не по догадке parser’а, а по фактическим данным игры.
Это наконец сломало многотестовый тупик с подменяющимися буквами.
После правильного physical mapping был собран:
TEST17_CORRECT_CINZEL_MAP.
Это стала первая версия, где одновременно всё выглядело правильно:
· English остался чистым;
· русский Barlow работает;
· русский Cinzel работает;
· Ы наконец является Ы;
· Я — Я;
· У — У;
· Б — Б;
· Ъ — Ъ;
· заголовки вроде ПАРАМЕТРЫ, ВЫБЕРИТЕ ЯЧЕЙКУ и КРОВНЫЕ УЗЫ отображаются так, как должны.
TEST17 стал золотой базой проекта.
Казалось, на этом сюрпризы закончились.
Не закончились.
Уже при подготовке нормального Nexus-патчера обнаружилось, что двенадцать физических страниц, которые мы использовали для нового Cinzel и некоторое время считали отдельным подходящим русским font resource, на самом деле принадлежат BIG_RUS.
То есть финальный Cinzel фактически переиспользует часть atlas resources штатного русского шрифта.
Это объяснило ещё одну особенность.
Через donor Spanish (Latin America) всё работает правильно.
Но если при установленном моде выбрать обычный Russian, его font path оказывается повреждён.
Сам русский текстовый файл tt17.pc при этом вообще не меняется.
Проблема именно в переиспользованном BIG_RUS.
Мы рассмотрели ещё один вариант: вынести Cinzel снова в свободные CJK pages и сохранить одновременно штатный Russian.
Но здесь начинался очередной большой круг работы:
· искать новые физические страницы;
· проверять mapping;
· искать достаточно большие непрерывные пустые области;
· учитывать SDF halo;
· проверять LZ4;
· тестировать, не пострадали ли Japanese/Korean/Chinese;
· снова рисковать ошибками page references.
К этому моменту мод уже полностью решал исходную задачу через donor slot.
Поэтому я решил остановиться.
Это осталось единственным сознательным ограничением:
при установленном моде нужно выбирать Spanish (Latin America), а штатный Russian использовать нельзя.
При удалении мода оригинальный BIG_RUS восстанавливается из backup.
До этого момента всё существовало в виде десятков экспериментальных DPC, Python-скриптов, тестовых архивов и заметок.
Такое можно назвать исследованием.
Но модом для Nexus — вряд ли.
Распространять целиком модифицированный игровой ENGLISH.DPC я тоже не хотел.
Поэтому следующим этапом стал отдельный binary patcher.
Его уже написала нейросеть на основе финального состояния TEST17.
Патчер:
· находит игровые файлы;
· проверяет поддерживаемую версию;
· автоматически создаёт backup;
· изменяет только нужные участки ENGLISH.DPC;
· копирует установленный у пользователя русский tt17.pc в donor tt20.pc;
· проверяет результат;
· умеет полностью удалить мод;
· восстанавливает исходные ресурсы.
Финальный DPC отличается от оригинала только в необходимых диапазонах — патчер работает примерно с 26 конкретными участками файла, а не заменяет весь контейнер вслепую.
Более того, для обновлённых версий игры сделана структурная проверка: если общий SHA изменился, но все области, которые собирается менять мод, остались байт-в-байт прежними, патчер может предложить экспериментальную установку с предупреждением.
Если изменилась хотя бы одна реально используемая font-область — установка блокируется.
Никакого blind force patch.
Первая релизная версия патчера получила номер v1.0.0.
И почти сразу нашёлся забавный баг в Check_Status.bat.
На non-Steam установках он до ручного выбора игры пытался рекурсивно искать ENGLISH.DPC в родительской директории.
Если архив лежал, например, в Downloads, PowerShell мог начать методично обходить огромное дерево файлов.
Со стороны это выглядело как намертво зависшая программа.
Сам Install при этом работал.
Так практически сразу появился v1.0.1:
· без огромного recursive scan;
· с нормальным выбором папки;
· с корректными предупреждениями про BIG_RUS;
· с обновлёнными README;
· с нормальным uninstall.
После этого я уже проверил установку патчера целиком на non-Steam копии игры.
Мод установился и заработал штатно.
К концу проекта стало очевидно, что просто сохранить финальный ENGLISH.DPC недостаточно.
Через несколько месяцев никто — включая меня и нейросеть — уже не вспомнит:
· почему конкретный glyph находится именно там;
· какой page mapping оказался настоящим;
· какие блоки действительно безопасны;
· где требуется 8 px halo;
· откуда взялись конкретные SDF-параметры;
· какие metrics считаются финальными;
· какие из старых тестов вообще были ошибочными.
Поэтому отдельно был собран полный reproducibility package.
В нём сохранено практически всё необходимое для полного воспроизведения результата: структура DPC, offsets и page maps, glyph records и метрики, atlas rects, SDF/alpha/halo, BC3/LZ4, hashes, история тестов и полный Python build pipeline.
Причём есть два способа восстановления.
build_exact_final.py способен из чистого исходного DPC получить байт-в-байт тот самый TEST17.
А build_from_source_ttf.py проходит настоящий путь от исходных шрифтов:
TTF → outline → supersampling → signed distance → RGB + alpha → atlas → BC3 → LZ4 → DPC.
То есть в итоге сохранён не просто готовый файл.
Сохранён сам процесс его создания.
Отдельно я подготовил облегчённый пакет для разработчиков A Plague Tale: Resonance.
Им, конечно, совершенно не нужны наши приключения с borrowed pages, binary offsets и поиском свободного места внутри готового DPC.
У них есть исходный font pipeline.
Поэтому в developer package лежит именно то, что может пригодиться им:
· готовый Barlow Cyrillic;
· готовый Cinzel Cyrillic;
· Unicode-набор;
· точные TTF-метрики;
· финальные игровые metrics;
· scale и baseline;
· SDF-параметры;
· таблица Latin-identical glyphs;
· контрольные игровые скриншоты;
· SHA-256 исходных TTF;
· описание полного reproducibility package.
Если разработчики когда-нибудь захотят добавить нормальную кириллицу штатно, им не придётся повторять самую трудоёмкую часть работы с нуля.
Самое забавное — всё началось с:
«Может, просто поставить PT Sans Narrow?»
А закончилось примерно такой цепочкой:
PT Sans Narrow → собственный Barlow Cyrillic → reverse engineering BIG_RUS → SDF и raster → собственный Cinzel Cyrillic → новая версия игры → donor localization → расширение BIG_FONT → borrowed pages → восстановление physical page mapping → TEST17 → binary patcher → reproducible build pipeline.
За эти четыре дня было огромное количество моментов, когда казалось, что осталось поправить буквально одну мелочь.
И почти каждая такая мелочь открывала ещё один слой устройства системы.
Почему правильный TTF выглядит неправильно?
Потому что scale.
Поправили scale.
Почему строка всё равно рыхлая?
Потому что metrics были округлены.
Поправили metrics.
Почему края грязные?
Наверное, SDF.
Нет. Оказалось, мы обрезали восьмипиксельный halo.
Почему ломается английский?
Потому что «неиспользуемый» BC3-блок на самом деле вовсе не пустой.
Почему правильная Ы превращается в П?
Потому что игра вообще читает другую физическую текстуру.
Почему мы часами пытаемся ужать русский текст в испанский ресурс?
А действительно — зачем?
Давайте просто дадим игре готовый русский файл под именем испанского.
И вот эта последняя категория вопросов оказалась особенно важной.
Нейросеть очень хорошо решает поставленную техническую задачу.
Но иногда важнее вовремя понять, что сама поставленная задача неправильная и её можно обойти на уровень выше.
Именно поэтому я не считаю этот мод ни «полностью сделанным нейросетью», ни, наоборот, исключительно своей ручной работой.
Это получился довольно чистый пример совместной разработки.
Нейросеть дала скорость анализа и программирования, которая в одиночку была бы для меня недостижима.
Я давал ей направление, визуальную оценку, реальное тестирование, принимал архитектурные решения и несколько раз вытаскивал весь процесс из локально логичного, но глобально бессмысленного тупика.
А сами Barlow и Cinzel при этом ещё пришлось буквально доводить руками по буквам.
Примерно через 40 часов за четыре дня получилось следующее:
Barlow Condensed Cyrillic — готов.
Cinzel Cyrillic — готов.
Русская версия снова использует два шрифта там, где оригинальный интерфейс использует Barlow и Cinzel.
Оригинальный Latin Barlow не перепаковывается без необходимости и сохраняет исходный вид.
Русский текст запускается через donor Spanish (Latin America).
Штатный Russian font path при установленном моде использовать нельзя из-за переиспользования части BIG_RUS для Cinzel — это известное и сознательно принятое ограничение.
Есть автоматическая установка, backup и uninstall.
Есть полный reproducibility package.
Есть отдельный пакет для разработчиков игры.
И самое главное: результат наконец выглядит не как очередная сторонняя «кириллическая замена шрифта».
Он выглядит так, как я и хотел в самом начале:
будто у оригинальных Barlow и Cinzel всегда была кириллица, а русская версия игры изначально использовала ту же типографику, что и английская.
Если кому-то пригодится сам мод, вот тут он на нексус модс: