Перейти к содержанию

Text.dll 2025 (мой неоффициальный апдейт на современный лад)


Рекомендуемые сообщения

и т.к. в Angel.ini будет прям очень много информации, соответственно скорее всего сам файл Angel.ini будет разделён на разные версии Angel_eng.ini Angel_rus.ini Angel_fr.ini и так далее. В каждом из инишников будет описание как на родном для локали языке, так и на английском. Где "особые" уникальные фиксы будут описываться подробнее (пример: почему его нет в другой локали, а в этой он есть). И сама структура инишников будет переделана - сначала будут идти общие фиксы, которые необходимы всем локалям, а потом будут идти уже узконаправленные фиксы для определённой локали.

Изменено пользователем ksteel
Ссылка на комментарий
Поделиться на другие сайты

  • Ответов 153
  • Создана
  • Последний ответ

Топ авторов темы

Топ авторов темы

Изображения в теме

1 час назад, olol сказал:

Насколько я помню, для плаванья аргонцев движок игры проверяет не ID расы, а ее название, которое отличается для других языков:

EN: Argonian
DE: Argonier
ES: Argoniano
FR: Argonien
HU: Argóniai
IT: Argoniano
PL: Argonian
PT: Argonianos
RU: Аргонианин

А у китайского шрифт Win32!!!

Фиксить нужно проверку не на название, а на ID расы, которая одинакова для всех языков.

Соответственно это исправление должно быть в MCP.

 

хоть я уже и перенёс фикс по "названию расы в игре", ваша мысль просто бриллиантовая! я переделаю этот фикс, сделаю его изначально "универсальным" для всех языков, которые НЕ английские!

тем более у нас есть уже явный пример от Angel Death в файле items_info.cpp. Он использует сравнение по ID типа предмета (0x4f4d4d41 = 'AMMO'), а не по строковому имени! Это именно тот подход, который нужен для аргониан!

Изменено пользователем ksteel
Ссылка на комментарий
Поделиться на другие сайты

ещё забавно, но факт что в стиме при скачивании En, Fr или De версий нет констракшн сета 😃 совсем безолаберность 

upd 

Construction Set и всякие источники показывают только строковое значение ID для расы = Argonian.

Совершенно бесполезно. Надо искать числовое значение внутри движка и вновь погружаюсь в декомпиляторы)

upd 

из строчек: 007af2a4 Unable to find race id "%s" in script %s. "Unable to find race id \"%s\" in script %s." и 004fde03 PUSH s_Unable_to_find_race_id_"%s"_in_s_007af2a4 DATA

прихожу к выводу, что Race id в "скриптах" передаётся как строка ("%s") (что грустно), а не число, Construction Set говорит, что id аргониан = Argonian, которую нещадно переводит наша (да и вообще любая) локализация. Надежда на то, что Движок анимаций использует числа, а не строку, продолжаю поиски. Можно конечно задать параметр, чтобы пеередавалось не как строка.....но хз чем это будет грозить в рамках всего движка (тотальным ахтунгом, потому что все функции ждут строку, а не число int)

upd (далее пойдёт немного хаотичное повествование, т.к. автор поста выпил вина 😄 )

что было выявлено посредством шаманства:

0x004C2B40 - 0x004C3090 - это ОДНА ФУНКЦИЯ выбора анимации

  • MOV EAX - загрузка пути анимации в регистр
  • PUSH EAX - передача пути в функцию загрузки
  • base_animKnA.nif vs argonian_swimKnA.nif - два варианта анимации!

Вывод в функции происходит ветвление:

  • Если аргонианин → argonian_swimKnA.nif

  • Если другая раса → base_animKnA.nif

А ещё я нашёл ЧИСЛОВУЮ (ура) проверку:

  • dec eax ; 
  • jz short loc_4C2B47 ; (Если EAX == 2 → зелёная ветка (аргониане))

Вывод EAX содержит числовой race_id.

Теперь смотрим на код в декомпиляторе:

Спойлер

int __thiscall sub_4C27C0(_DWORD *this)
{
  int j; // esi
  const char *v3; // eax
  int v4; // ebx
  const char *v5; // eax
  char *v6; // eax
  char *v7; // eax
  int v8; // eax
  int k; // eax
  char *v10; // eax
  int v11; // eax
  int v12; // ecx
  int v13; // edi
  bool v14; // zf
  char *v15; // eax
  int v16; // edi
  char **v17; // ebx
  char *v18; // eax
  char *v19; // eax
  int v20; // ebx
  const char *v21; // eax
  int *v22; // edi
  const char *v23; // eax
  char *v24; // eax
  char *v25; // ecx
  char *v26; // eax
  int v27; // eax
  int n; // eax
  char *v29; // eax
  int v30; // eax
  int v31; // ecx
  int v32; // esi
  int v33; // ebx
  char *v34; // eax
  char **v35; // edi
  char *v36; // esi
  _DWORD *v37; // ebx
  int result; // eax
  int i; // [esp+10h] [ebp-14h]
  int *v40; // [esp+14h] [ebp-10h]
  _BYTE *v41; // [esp+14h] [ebp-10h]
  int m; // [esp+14h] [ebp-10h]
  char *String1; // [esp+18h] [ebp-Ch]
  int v44; // [esp+1Ch] [ebp-8h]
  int *v45; // [esp+20h] [ebp-4h]

  v44 = sub_4D51B0(&unk_7CB478);
  String1 = (char *)sub_4D51E0(&unk_7CB478);
  for ( i = 0; i < 2; ++i )
  {
    for ( j = 0; j < 2; ++j )
    {
      v45 = this + 2 * i + j + 10699;
      if ( *v45 )
      {
        v3 = (const char *)sub_4C2720(i, j);
        nullsub_12("Clearing 3D for baseanim '%s'.", v3);
        sub_4EE5B0(*(this + 2 * i + j + 10695));
        sub_471B00(0);
      }
      v4 = j + 2 * i;
      memset(this + 150 * v4 + 10095, 0, 0x258u);
      v40 = this + v4 + 10695;
      if ( *v40 )
      {
        v5 = (const char *)sub_4C2720(i, j);
        nullsub_12("Clearing 3D for baseanim '%s'.", v5);
        sub_4EE5B0(*v40);
        sub_471B00(0);
      }
      if ( i )
      {
        if ( i == 1 )
        {
          if ( j == 1 )
            v6 = aBaseAnimFemale;
          else
            v6 = aBaseAnimFemale_0;
        }
        else
        {
          v6 = 0;
        }
      }
      else
      {
        v6 = aBaseAnim1stNif;
        if ( j != 1 )
          v6 = aBaseAnimNif;
      }
      if ( (unsigned __int8)sub_47D640(v6) )
      {
        sprintf(v44, aSBaseAnim, off_792674[i]);
        if ( i )
        {
          if ( i == 1 )
          {
            if ( j == 1 )
              v7 = aBaseAnimFemale;
            else
              v7 = aBaseAnimFemale_0;
          }
          else
          {
            v7 = 0;
          }
        }
        else
        {
          v7 = aBaseAnim1stNif;
          if ( j != 1 )
            v7 = aBaseAnimNif;
        }
        v8 = sub_4EE200(v7, v44);
        *v45 = v8;
        if ( v8 )
        {
          for ( k = *(_DWORD *)(v8 + 16); k; k = *(_DWORD *)(k + 32) )
            *(this + 150 * v4 + *(unsigned __int8 *)(k + 16) + 10095) = k;
        }
        if ( i )
        {
          if ( i == 1 )
          {
            if ( j == 1 )
              v10 = aBaseAnimFemale;
            else
              v10 = aBaseAnimFemale_0;
          }
          else
          {
            v10 = 0;
          }
        }
        else
        {
          v10 = aBaseAnim1stNif;
          if ( j != 1 )
            v10 = aBaseAnimNif;
        }
        sub_47D870(v10, (int)&off_789D88, (int)String1, 0);
        v11 = sub_4EE0A0(String1);
        v12 = *v40;
        v13 = v11;
        if ( *v40 != v11 )
        {
          if ( v12 )
          {
            v14 = (*(_DWORD *)(v12 + 4))-- == 1;
            if ( v14 )
              (**(void (__thiscall ***)(int, int))v12)(v12, 1);
          }
          *v40 = v13;
          if ( v13 )
            ++*(_DWORD *)(v13 + 4);
        }
        if ( *v40 )
        {
          if ( i )
          {
            if ( i == 1 )
            {
              if ( j == 1 )
                v15 = aBaseAnimFemale;
              else
                v15 = aBaseAnimFemale_0;
            }
            else
            {
              v15 = 0;
            }
          }
          else
          {
            v15 = aBaseAnim1stNif;
            if ( j != 1 )
              v15 = aBaseAnimNif;
          }
          sub_6EA1A0(v15);
          sub_472B70(*v40, 1);
          sub_6EB000(0, 0, 1);
        }
      }
      if ( !i )
      {
        v16 = 0;
        v17 = &off_78A970;
        v41 = (char *)&unk_7A6008 + j;
        do
        {
          if ( *v41 != 1 || *(this + 150 * j + v16 + 10095) )
          {
            if ( *v41 == 0xFF && *(this + 150 * j + v16 + 10095) )
            {
              v19 = aBaseAnim1stNif;
              if ( j != 1 )
                v19 = aBaseAnimNif;
              sub_477400("Please remove anim group (%s) in \"%s\".\r\n", *v17, v19);
            }
          }
          else
          {
            v18 = aBaseAnim1stNif;
            if ( j != 1 )
              v18 = aBaseAnimNif;
            sub_477400("Missing required anim group (%s) in \"%s\".\r\n", *v17, v18);
          }
          ++v17;
          ++v16;
          v41 += 2;
        }
        while ( (int)v17 < (int)off_78ABC8 );
      }
    }
  }
  v20 = 0;
  for ( m = 0; ; v20 = m )
  {
    if ( *(this + v20 + 11156) )
    {
      if ( v20 )
      {
        if ( v20 == 1 )
        {
          v21 = aBaseAnimkna1st;
        }
        else if ( v20 == 2 )
        {
          v21 = aArgonianSwimkn;
        }
        else
        {
          v21 = 0;
        }
      }
      else
      {
        v21 = aBaseAnimknaNif;
      }
      nullsub_12("Clearing 3D for baseanim '%s'.", v21);
      sub_4EE5B0(*(this + v20 + 11153));
      sub_471B00(0);
    }
    memset(this + 150 * v20 + 10703, 0, 0x258u);
    v22 = this + v20 + 11153;
    if ( *v22 )
    {
      if ( v20 )
      {
        if ( v20 == 1 )
        {
          v23 = aBaseAnimkna1st;
        }
        else if ( v20 == 2 )
        {
          v23 = aArgonianSwimkn;
        }
        else
        {
          v23 = 0;
        }
      }
      else
      {
        v23 = aBaseAnimknaNif;
      }
      nullsub_12("Clearing 3D for baseanim '%s'.", v23);
      sub_4EE5B0(*v22);
      sub_471B00(0);
    }
    if ( v20 )
    {
      if ( v20 == 1 )
      {
        v24 = aBaseAnimkna1st;
      }
      else if ( v20 == 2 )
      {
        v24 = aArgonianSwimkn;
      }
      else
      {
        v24 = 0;
      }
    }
    else
    {
      v24 = aBaseAnimknaNif;
    }
    if ( (unsigned __int8)sub_47D640(v24) )
    {
      if ( v20 )
      {
        if ( v20 == 1 )
        {
          v25 = aBaseAnimkna1st;
        }
        else if ( v20 == 2 )
        {
          v25 = aArgonianSwimkn;
        }
        else
        {
          v25 = 0;
        }
      }
      else
      {
        v25 = aBaseAnimknaNif;
      }
      if ( v20 )
      {
        if ( v20 == 1 )
        {
          v26 = aBaseAnimkna1st;
        }
        else if ( v20 == 2 )
        {
          v26 = aArgonianSwimkn;
        }
        else
        {
          v26 = 0;
        }
      }
      else
      {
        v26 = aBaseAnimknaNif;
      }
      v27 = sub_4EE200(v26, (int)v25);
      *(this + v20 + 11156) = v27;
      if ( v27 )
      {
        for ( n = *(_DWORD *)(v27 + 16); n; n = *(_DWORD *)(n + 32) )
          *(this + 150 * v20 + *(unsigned __int8 *)(n + 16) + 10703) = n;
      }
      if ( v20 )
      {
        if ( v20 == 1 )
        {
          v29 = aBaseAnimkna1st;
        }
        else if ( v20 == 2 )
        {
          v29 = aArgonianSwimkn;
        }
        else
        {
          v29 = 0;
        }
      }
      else
      {
        v29 = aBaseAnimknaNif;
      }
      sub_47D870(v29, (int)&off_789D88, (int)String1, 0);
      v30 = sub_4EE0A0(String1);
      v31 = *v22;
      v32 = v30;
      if ( *v22 != v30 )
      {
        if ( v31 )
        {
          v14 = (*(_DWORD *)(v31 + 4))-- == 1;
          if ( v14 )
            (**(void (__thiscall ***)(int, int))v31)(v31, 1);
        }
        *v22 = v32;
        if ( v32 )
          ++*(_DWORD *)(v32 + 4);
      }
      if ( *v22 )
      {
        if ( v20 )
        {
          v33 = v20 - 1;
          if ( v33 )
          {
            if ( v33 == 1 )
              v34 = aArgonianSwimkn;
            else
              v34 = 0;
          }
          else
          {
            v34 = aBaseAnimkna1st;
          }
        }
        else
        {
          v34 = aBaseAnimknaNif;
        }
        sub_6EA1A0(v34);
        sub_472B70(*v22, 1);
        sub_6EB000(0, 0, 1);
      }
    }
    v35 = &off_78A970;
    v36 = (char *)&unk_7A6008;
    v37 = this + 10703;
    do
    {
      if ( *v36 != 1 || *v37 )
      {
        if ( *v36 == -1 )
        {
          if ( *v37 )
            sub_477400("Please remove anim group (%s) in \"%s\".\r\n", *v35, aBaseAnimknaNif);
        }
      }
      else
      {
        sub_477400("Missing required anim group (%s) in \"%s\".\r\n", *v35, aBaseAnimknaNif);
      }
      v36 += 2;
      ++v37;
      ++v35;
    }
    while ( (int)v36 < (int)aHandlerCollisi );
    result = ++m;
    if ( m >= 3 )
      break;
  }
  return result;
}

из которого делаю выводы:
if ( v20 ) { if ( v20 == 1 ) { v24 = aBaseAnimkna1st;} else if ( v20 == 2 ){ v24 = aArgonianSwimkn;} else { v24 = 0; } }
 

  • v24 = aBaseAnimkna1st; // "base_animkna1st.nif"
  • v24 = aArgonianSwimkn; // "argonian_swimKnA.nif" - аргониане

получается переменная v20

  • v20 = 0 → base_animkna.nif (стандартная анимация)
  • v20 = 1 → base_animkna1st.nif (анимация от первого лица)
  • v20 = 2 → argonian_swimKnA.nif (специальная анимация аргониан)

НО где же определяется эта переменная? Смотрим выше:

v20 = 0; for ( m = 0; ; v20 = m ) {... огромный блок кода ... result = ++m; if ( m >= 3 ) break; }

Вывод v20 (m) проходит значения 0, 1, 2, v20 = 2 соответствует аргонианам, Значит race_id аргониан = 2.

Нам надо перехватить в момент проверки когда v20 = 2 и обеспечить загрузку правильной анимации.

Изменено пользователем ksteel
Ссылка на комментарий
Поделиться на другие сайты

пока сделал заметку. по поводу фикса аргониан (имею в виду починку самой функции и замену "название расы" на "ID расы"), начал разбираться что да как, чуть не заблудился 😄 

А сейчас вот к этому молчаливому патчу:

22 часа назад, ksteel сказал:
Спойлер

Я обнаружил "молчаливый фикс" в MCP для русской локализации, о котором в принципе не пишется в логе, хотя этот фикс ОЧЕНЬ важен

Было:
Screenshot_3.png.ccd9a82edbb79b89cd3424ac58e63f20.png

Стало: Screenshot_6.png.522eecc79f94beae2ff377f36c5c251f.png
 

Фикс из категории БУФЕРОВ И БЕЗОПАСНОСТИ.

Предположение: Фикс переполнения буферов в системе обработки ошибок звуковой подсистемы Morrowind.

Я фиксил уже расширение консоли по переполнению буфера. Этот же фикс прорабатывает систему буферов по звукам.

  • Проблема: Слишком длинные сообщения об ошибках в английской версии
  • В русской локализации эти строки становятся еще длиннее из-за кириллицы
  • Приводит к переполнению буферов и возможным сбоям

Что делает MCP? Он укорачивает строки ошибок, чтобы избежать переполнения., заменяет list на l.

0x00 00 80 3F - скорее всего, padding или служебные данные.

Это довольно важный фикс безопасности, предотвращающий потенциальные сбои при работе со звуковой системой в русской локализации. НО переносить его в таком виде - кощунство. Я буду "забивать гвозди микроскопом". Надо использовать text.dll в полной мере.

Мы увеличим буфер для обработки такого типа ошибок (как я сделал фикс по переполнению буфера расширения консоли).

 

Что удалось выяснить: конкретно в FUN_0048b000 есть конкретная функция логирования: sub_477400 

Спойлер

; char sub_477400(int, ...)
sub_477400 proc near

var_568= byte ptr -568h
var_554= dword ptr -554h
var_550= dword ptr -550h
var_54C= dword ptr -54Ch
var_548= dword ptr -548h
var_544= dword ptr -544h
NumberOfBytesWritten= dword ptr -540h
Msg= tagMSG ptr -53Ch
Buffer= byte ptr -520h
var_C= dword ptr -0Ch
var_4= dword ptr -4
arg_0= dword ptr  4
arg_4= byte ptr  8

; FUNCTION CHUNK AT .text:0072AFD0 SIZE 0000002B BYTES

; __unwind { // SEH_477400
mov     eax, large fs:0
mov     ecx, [esp+arg_0]
push    0FFFFFFFFh
push    offset SEH_477400
push    eax
mov     large fs:0, esp
sub     esp, 548h
push    ebx
push    ebp
push    esi
push    edi
lea     eax, [esp+564h+arg_4]
push    eax
push    ecx
lea     edx, [esp+56Ch+Buffer]
push    edx
call    ds:vsprintf
or      ebp, 0FFFFFFFFh
xor     eax, eax
add     esp, 0Ch
lea     edi, [esp+564h+Buffer]
mov     ecx, ebp
repne scasb
not     ecx
dec     ecx
lea     eax, [esp+ecx+564h+Buffer]
cmp     byte ptr [eax-2], 0Dh
jnz     short loc_47745A

где 

  • Первый параметр — строка формата (наши сообщения об ошибках)

  • Второй параметр — дополнительная информация

Смотрим подробно структуру данного инструмента логирования:

 

Спойлер

image.thumb.png.c3e2368d742e0b71bfecd02a72cef0ab.png

CHAR Buffer[1300]; // [esp+6Ah] [ebp-520h] BYREF

Что собственно происходит?

 

vsprintf(Buffer, a1, va); - форматирование строки в буфер 1300 байт.

Находим этот "буфер": 00477419 FUN_00477400 SUB ESP,0x548

Немного математики: 

  • 0x548 в hex = 1352 байта в десятичной

  • Текущий буфер: 1300 байт (как видно в коде)

  • Разница: 52 байта - это (скорее всего) служебные данные/выравнивание

Увы, это не расширение консоли, где я могу увидеть весь исходный код и впаять запросто динамический буфер (хотя я попробую). Потому Моё решение: меняем SUB ESP,0x548 (1352 байта) на SUB ESP,0x800 (2048 байт) итого +696 байт доп пространства, чего должно хватить для кириллицы (да и вообще любого языка).

А т.к. я трогаю ТОЛЬКО размер буфера — нулевой риск, не затрагивающий основную логику поведения сообщений по звуковым ошибкам. Данный фикс должен покрывать все 5 найденных строк ошибок звуковой системы.

К сожалению (как я уже неоднократно говорил) я не плагиностроитель или мододел, потому воспроизвести случай появления этих ошибок я не смогу (для проведения тестов). У кого есть время, прошу провести тесты. Прикладываю файлы собсно для теста. Условия для тестов - чистый MTB без MCP/MGE/MWSE патча - это очень важно.

Мой файл по статичному увеличению буфера:

Спойлер

image.thumb.png.ee8b4cf96870a57395ab1795015516e6.png

angel.iniText.dll

Изменено пользователем ksteel
Ссылка на комментарий
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
  • Последние посетители   1 пользователь онлайн

×
×
  • Создать...