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

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


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

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

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

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

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

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

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

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
Ссылка на комментарий
Поделиться на другие сайты

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

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

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

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

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

Войти

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

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

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