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

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


ksteel

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

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

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

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

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

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

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

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

В 07.11.2025 в 11:07 PM, Pirate443 сказал:

а вот чего MCP хочет от text.dll непонятно.

Уважаемые форумчане!!! Я выяснил что делает MCP с text.dll!
Что я таки нашёл после патча text.dll от версии GFM 5.7 через MCP:
Оригинальный файл:

Спойлер

image.thumb.png.0f2acd6b9813fb258b23a574f360a6b0.png

Пропатченный через MCP:

Спойлер

image.thumb.png.397bcb0a120d7d836926b0f0f955d6ea.png

Различие в байтах на лицо, итого имеем:

Спойлер

image.thumb.png.fffaa98e9f8646136feff893f5bf9887.png

В Hex Workshop побаловались, идём в декомпилятор/дизассемблер и ищем:

  • в оригинальном text.dll последовательность CC CC CC CC 8B 44 24 04 56 8B 74 24 10 83 FE 08 F7 D0 0F 8C D4 00
  • в пропатченном text.dll последовательность CC CC CC CC B8 AF 88 56 38 C3 74 24 10 83 FE 08 F7 D0 0F 8C D4 00

По обоим-двум последовательностям В РАЗНЫХ text.dll мы находим:

image.png.bf5e85908a47bb6650fafd511efb54bc.png

image.png.2a3377ed84554177edb9398880fefd71.png

text:1000188C align 10h НО align 10h - это паддинг, а не код!!!

Подозрительно так? Заменять столько информации, на паддинг? Прыгаем в адрес 1000188C в обеих dll и смотрим код вокруг него:
оригинальный Text.dll:

Спойлер

image.thumb.png.6876ebc0efdcb2cc8a58492ed580da01.png

пропатченный через MCP text.dll:

 

Спойлер

image.thumb.png.4364ce7393e2df203b87cbd7eb3d528e.png

Ничего подозрительного не находите? В детстве играли в "найди различия на картинке"? Так вот они! (это не всё, я не буду сюда скидывать весь дизассемблер)

Спойлер

image.thumb.png.d811a31a627e43caf5aa4eb362f9c5ac.png

 

На лицо явная замена функции. Что за функции?

Смотрим вот в это место оригинала:

  • sub_10001890 proc near
  • mov eax, [esp+arg_0]
  • push esi
  • mov esi, [esp+4+arg_8]
  • cmp esi, 8
  • not eax
  • jl loc_10001978

А теперь в то же место патча:

  • sub_10001890 proc near
  • mov eax, 385688AFh
  • retn
  • всё. ТОЧКА.

MCP заменил всю сложную функцию CRC32 на простой возврат хардкодного значения.

Но вот как он это сделал...после значения retn идут случайные байты dw 2474h и повреждённый код. Грубо конечно, но для такого метода очень эффективно, хотя и не в полной мере.

А теперь немного по шаманим с плагинами декомпилятора и сравним полностью эти два файлика, их код и глянем разницу, сначала в HEX, а затем и в дизассемблере:

Первая вкладка:

Спойлер

image.png.7ca7240d0eda4246eca45c9569e8f2b2.png

Вторая вкладка:

Спойлер

image.png.f907a62402b7ef6c07d6e19a4499aed2.png

Начнём со второй вкладки - HEX Разница:

Спойлер

image.png.ef8319f00ac5ac150c3c955b2e72ad41.png

Знакомая в принципе картина, уже видели её в Hex Workshop.

А теперь самое интересное - дизассемблер:

Спойлер

Тадам

image.png.f766414e3c893f5d2d7268b3dc4e1603.png

БЫЛО: Сложный CRC32 алгоритм (50+ строк кода) СТАЛО: Всего 2 инструкции!

Таким образом получается mov eax, 385688AFh + retn - это магическое число, которое подсовывает MCP вместо реальных вычислений.

Выходит что:

  1. MCP обходит проверки целостности
  2. Возвращает фиксированный хеш вместо реального
  3.  Это объясняет, почему пропатченный text.dll работает с модифицированным Morrowind.exe

А теперь давайте посмотрим и найдём похожие функции, связанные с проверкой по crc ключу между text.dll и morrowind.exe...(кто следит за этой темой с самого начала и внимательно читал, наверное уже настальгирует сидит 😃 )

Спойлер

А вот и она, долго искать не пришлось. Да-да, эта та самая проверка, про фикс которой я писал ещё в самом первом своём посте в этой теме 😄 
не дословная цитата из того поста:  "После патча через MCP у Morrowind.exe менялся цифровой отпечаток и старенький text.dll не понимал что это за Морровинд такой. Ошибка Unknown Morrowind.exe"

Вот так вот, работаешь-работаешь и случайно вспоминаешь о прошлых деяниях 😃 .

image.png.48feba45440f2203359819151cb8a1cc.png

настальгия:
image.thumb.png.e6f7f59baa7bbd9197ce5b431e38e622.png

MCP ломает систему определения версии Morrowind в самом text.dll и САМ определяет её, вычисляя хэш Morrowind.exe! Всё оказалось просто: MCP эту функцию WORD crc = CalcExeCRC(); подменяет на пустышку и все дальнейшие проверки внутри text.dll уже бессмысленны. Вот почему MCP спокойно патчит Morrowind.exe и text.dll не ругается на изменение хэша исполняемого файла.

Итог расследования: Я уже сам лично купировал эту crc проверку (как указано в шапке темы), мой text.dll универсально работает с ЛЮБЫМ Morrowind.exe, которому нужен ЛЮБОЙ другой text.dll!!! Всем спасибо за внимание!!!

 

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

  • 2 месяца спустя...

>мой text.dll универсально работает с ЛЮБЫМ Morrowind.exe, которому нужен ЛЮБОЙ другой text.dll!!

И без нюансов поверху ставится MCP, так? Или какие-то фиксы из MCP теперь не надо активировать, с учётом твоих изменений в text.dll?

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

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

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

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

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

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

Войти

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

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

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...