Свернуть ↑
Ситуация такая. Корейский и китайский клиенты не поддерживают русские шрифты. То, что выводится т а к и м во т образом, является эффектом юникода.
Для работы нам понадобится софт:
UE Explorer
HxD Editor
Umodel
Нужны два файла из клиента \...\CookedPC\
00020428.upk - cодержит весь шрифт в формате TTF в игре.
EngineFonts.upk - содержит отображение шрифта в виде
блочного шифра
00020428.upk:
Enginefont.upk:
Операция А:
В чем заключается идея замены шрифтов? Логично, в ее замене. Но сделать это практически невозможно. UPK фактически можно распаковать, но нельзя запаковать обратно. Точнее, наверное можно в определенных случаях, но нужно писать программу под это.
Какой выход? Для себя я вижу только один выход - Хекс патчинг.
Теперь говорим о том, что следует заменить. В идеале конечно же, лучше было бы заменить весь шрифт целиком. Вопрос только в том, какой из них. Здесь два варианта. Либо YGO550 либо Calibri. Но судя по-всему нужно менять именно Calibri, на Calibri Bold с поддержкой русского языка.
И вот здесь начинается та часть, от которой у меня уже голова не соображает вообще. Как осуществить подмену, не нарушив размер блоков?
Для начала нам нужно определить начало и конец шрифта и его оффсеты.
Открываем 00020428.upk в ХЕКС редакторе:
А также открываем шрифт, на который будем менять. В данном случае Calibri.TTF
Смотрим где начинается шрифт. В данном случае это:
00 01 00 00 00 15 01 00 00 04 00 50 44 53 49 47
Ищем аналогичную последовательность байтов в файле со шрифтами:
00 00 01 00 00 00 15 01 00 00 04 00 50 44 53 49 47
Теперь мы знаем, где начинается нам нужный шрифт. Определить где он заканчивается, можно аналогичным образом.
В чем сложность данной операции? Сложность в том, чтобы соблюдать постоянно кол-во байтов. Сдвиг в лево или право - файл не читается. Т.е. вам нужно сочитать сколько байтов содержит шрифт в упаковке и подобрать свой шрифт по кол-ву байтов. Для Calibri подходит русская версия CalibriB (Bold) который использовал Локи в своем скрине.
Операция Б:
Мы замениили шрифт ура! Возможно на этом все закончится. У меня не хватает времени поиграться дольше с подбором разамеров, поэтому результат мне неизвестен.
Но есть вероятность того, что еще дополнительно нужно патчить EngineFont.upk
Фактически, он содержит в себе двухмерные текстуры символов букв и знаков. Чтобы раскрыть файл, вам следует поместить его в Umodel.
Такое вот, мы можем наблюдать внутри:
Ну что? Есть русские шрифты? А вот так должен выглядеть файл с русским шрфитом:
Понятно в чем разница?
Как изменить? А изменить можно только одним способом, через подмену текстур. Процесс аналогичен полностью замене шрифтов, поэтому выкладывать скрины не буду. Покажу другую программу, в которой можно посмотреть начало и конец текстур:
Программа называется
UDKexplorer:
Cлева мы выбираем нам нужный класс, а с права отображается его ХЕКС начало и конец. Таким образом мы просто можем вырезать байты и менять на свои.
Пока что столько. Выкладываю эту информацию для тех, кто может быть больше знает в хекс редакции, но не знаком с этой темой. К тому же в будущем, тема шрифтов в азиатских играх будет аукаться многократно.
Для тех, кто захочет написать программку для распаковки и упаковки UPK файлов, уверен Реборн уже состряпал себе такую. Выкладываю этот кусок кода, который при владении определенными знаниями, объясняет как распаковывается UPK для БНС:
if (Game == GAME_BladeNSoul && (Summary.PackageFlags & 0x08000000))
PatchBnSExports(ExportTable, Summary);
...
static void DecodeBnSPointer(int &Value, unsigned Code1, unsigned Code2, int Index)
{
unsigned tmp1 = ROR32(Value, (Index + Code2) & 0x1F);
unsigned tmp2 = ROR32(Code1, Index % 32);
Value = tmp2 ^ tmp1;
}
static void PatchBnSExports(FObjectExport *Exp, const FPackageFileSummary &Summary)
{
unsigned Code1 = ((Summary.HeadersSize & 0xFF) << 24) |
((Summary.NameCount & 0xFF) << 16) |
((Summary.NameOffset & 0xFF) << 8) |
((Summary.ExportCount & 0xFF));
unsigned Code2 = (Summary.ExportOffset + Summary.ImportCount + Summary.ImportOffset) & 0x1F;
for (int i = 0; i < Summary.ExportCount; i++, Exp++)
{
DecodeBnSPointer(Exp->SerialSize, Code1, Code2, i);
DecodeBnSPointer(Exp->SerialOffset, Code1, Code2, i);
}
}Code: C++