ksteel Опубликовано 23 часа назад Автор Жалоба Поделиться Опубликовано 23 часа назад (изменено) и т.к. в Angel.ini будет прям очень много информации, соответственно скорее всего сам файл Angel.ini будет разделён на разные версии Angel_eng.ini Angel_rus.ini Angel_fr.ini и так далее. В каждом из инишников будет описание как на родном для локали языке, так и на английском. Где "особые" уникальные фиксы будут описываться подробнее (пример: почему его нет в другой локали, а в этой он есть). И сама структура инишников будет переделана - сначала будут идти общие фиксы, которые необходимы всем локалям, а потом будут идти уже узконаправленные фиксы для определённой локали. Изменено 23 часа назад пользователем ksteel Ссылка на комментарий Поделиться на другие сайты Поделиться
ksteel Опубликовано 23 часа назад Автор Жалоба Поделиться Опубликовано 23 часа назад (изменено) 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'), а не по строковому имени! Это именно тот подход, который нужен для аргониан! Изменено 22 часа назад пользователем ksteel Ссылка на комментарий Поделиться на другие сайты Поделиться
ksteel Опубликовано 22 часа назад Автор Жалоба Поделиться Опубликовано 22 часа назад (изменено) ещё забавно, но факт что в стиме при скачивании 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 и обеспечить загрузку правильной анимации. Изменено 22 часа назад пользователем ksteel Ссылка на комментарий Поделиться на другие сайты Поделиться
ksteel Опубликовано 2 часа назад Автор Жалоба Поделиться Опубликовано 2 часа назад (изменено) пока сделал заметку. по поводу фикса аргониан (имею в виду починку самой функции и замену "название расы" на "ID расы"), начал разбираться что да как, чуть не заблудился 😄 А сейчас вот к этому молчаливому патчу: 22 часа назад, ksteel сказал: Спойлер Я обнаружил "молчаливый фикс" в MCP для русской локализации, о котором в принципе не пишется в логе, хотя этот фикс ОЧЕНЬ важен. Было: Стало: Фикс из категории БУФЕРОВ И БЕЗОПАСНОСТИ. Предположение: Фикс переполнения буферов в системе обработки ошибок звуковой подсистемы 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 где Первый параметр — строка формата (наши сообщения об ошибках) Второй параметр — дополнительная информация Смотрим подробно структуру данного инструмента логирования: Спойлер 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 патча - это очень важно. Мой файл по статичному увеличению буфера: Спойлер angel.iniText.dll Изменено 2 часа назад пользователем ksteel Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения
Для публикации сообщений создайте учётную запись или авторизуйтесь
Вы должны быть пользователем, чтобы оставить комментарий
Создать учетную запись
Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!
Регистрация нового пользователяВойти
Уже есть аккаунт? Войти в систему.
Войти