PDA

Просмотр полной версии : [Вопрос] sampgdk неверная кодировка



kenjutsu
21.11.2022, 20:59
char name[MAX_PLAYER_NAME + 1];
sampgdk::GetPlayerName(id, name, MAX_PLAYER_NAME + 1);

Если зайти под ником "Привет", то в name будет "яяяПяяяряяяияяявяяяеяяят"
Но если поставить английскую букву в начале, например "zПривет", то все нормально

YSF 2.2 (последняя на данный момент) - для поддержки кириллицы в нике
sampgdk 4.6.2 (последняя на данный момент)

Читал здесь (https://pro-pawn.ru/showthread.php?13007). Попробовал фикс с помощью побитового &=, не помогло
Капнул чуть глубже и выяснил, что sampgdk вызывает amx_GetString после вызова стандартной нативы GetPlayerName.
Не знаю как это может помочь в "исследовании", но думаю это важно.
В amx не особо разбираюсь, да и в кодировках тоже. Прошу помощи)

UPD: для интереса попробовал вызвать GetPlayerName через InvokeNative:
char name[MAX_PLAYER_NAME + 1];

AMX_NATIVE native = sampgdk::FindNative("GetPlayerName");
sampgdk::InvokeNative(native, "iS[25]i", id, name, 25);

Результат тот же

kenjutsu
28.11.2022, 00:13
100+ просмотров и ни одного ответа? :(

Daniel_Cortez
28.11.2022, 18:59
Проблема не в sampgdk, а именно в GetPlayerName() (а заодно и в куче других функций SA-MP, которые используют одну общую функцию для конверсии из cell в char и делают это неправильно, расширяя знаковый бит - отсюда и берутся лишние символы "я").
Как вариант, можно попробовать вызвать функцию через InvokeNative(), но только интерпретировать результат не как строку, а как массив ячеек (сам ни разу не пользовался sampgdk, но думаю, наверняка же можно указать в спецификаторах "A[25]" вместо "S[25]"), и уже из этого массива конвертировать в массив char.
При этом преобразование должно проводиться без переноса знакового бита, т.е. вместо, скажем, вот такой наивной конверсии:

name[i] = (char)name_cells[i];

должно быть:

name[i] = (char)(unsigned char)name_cells[i];

kenjutsu
29.11.2022, 01:38
Проблема не в sampgdk, а именно в GetPlayerName() (а заодно и в куче других функций SA-MP, которые используют одну общую функцию для конверсии из cell в char и делают это неправильно, расширяя знаковый бит - отсюда и берутся лишние символы "я").
Как вариант, можно попробовать вызвать функцию через InvokeNative(), но только интерпретировать результат не как строку, а как массив ячеек (сам ни разу не пользовался sampgdk, но думаю, наверняка же можно указать в спецификаторах "A[25]" вместо "S[25]"), и уже из этого массива конвертировать в массив char.
При этом преобразование должно проводиться без переноса знакового бита, т.е. вместо, скажем, вот такой наивной конверсии:

name[i] = (char)name_cells[i];

должно быть:

name[i] = (char)(unsigned char)name_cells[i];



// TODO: reverse samp server and implement a normal fix
AMX_NATIVE amx_native = sampgdk::FindNative("GetPlayerName");
if(amx_native != NULL)
{
// Kalcor moment -->
cell name_cells[MAX_PLAYER_NAME + 1];

char amx_format[8];
sprintf(amx_format, "iA[%i]i", sizeof(name_cells) / sizeof(cell));

sampgdk::InvokeNative(amx_native, amx_format, id, name_cells, MAX_PLAYER_NAME + 1);

char name[MAX_PLAYER_NAME + 1];
for (int i = 0; i <= MAX_PLAYER_NAME; i++)
{
name[i] = (char)(unsigned char)name_cells[i];
}
// <-- Kalcor moment
}


Работает, спасибо :)
По поводу S и A. Между ними нет разницы, посмотрел в исходниках sampgdk

kenjutsu
29.11.2022, 15:18
Все же решил одним глазком заглянуть в идашку :)

Цель была найти самопальную функцию set_amxstring о которой писал Daniel_Cortez здесь (https://pro-pawn.ru/showthread.php?13007).
Сначала нашел AMXPrintError (https://github.com/dashr9230/SA-MP/blob/master/server/scrcore.cpp) по статическим данным, а там рядом оказалась та самая set_amxstring


.text:0046FCB0 ; int __cdecl sub_46FCB0(int, int, _BYTE *, int)
.text:0046FCB0 sub_46FCB0 proc near
.text:0046FCB0
.text:0046FCB0
.text:0046FCB0 arg_0 = dword ptr 4
.text:0046FCB0 arg_4 = dword ptr 8
.text:0046FCB0 arg_8 = dword ptr 0Ch
.text:0046FCB0 arg_C = dword ptr 10h
.text:0046FCB0
.text:0046FCB0 mov eax, [esp+arg_0]
.text:0046FCB4 mov ecx, [eax]
.text:0046FCB6 mov eax, [ecx+10h]
.text:0046FCB9 mov edx, [esp+arg_4]
.text:0046FCBD add eax, ecx
.text:0046FCBF add eax, edx
.text:0046FCC1 mov edx, [esp+arg_C]
.text:0046FCC5 test edx, edx
.text:0046FCC7 push edi
.text:0046FCC8 mov edi, eax
.text:0046FCCA jz short loc_46FCE6
.text:0046FCCC push esi
.text:0046FCCD mov esi, [esp+8+arg_8]
.text:0046FCD1
.text:0046FCD1 loc_46FCD1:
.text:0046FCD1 mov cl, [esi]
.text:0046FCD3 dec edx
.text:0046FCD4 test cl, cl
.text:0046FCD6 jz short loc_46FCE5
.text:0046FCD8 movsx ecx, cl
.text:0046FCDB mov [eax], ecx
.text:0046FCDD add eax, 4
.text:0046FCE0 inc esi
.text:0046FCE1 test edx, edx
.text:0046FCE3 jnz short loc_46FCD1
.text:0046FCE5
.text:0046FCE5 loc_46FCE5:
.text:0046FCE5 pop esi
.text:0046FCE6
.text:0046FCE6 loc_46FCE6:
.text:0046FCE6 mov dword ptr [eax], 0
.text:0046FCEC sub eax, edi
.text:0046FCEE sar eax, 2
.text:0046FCF1 pop edi
.text:0046FCF2 retn
.text:0046FCF2 sub_46FCB0 endp
.text:0046FCF2
.text:0046FCF2 ; ---------------------------------------------------------------------------
.text:0046FCF3 align 10h


Дизассемблер помог понять, что это та самая функция:
int __cdecl sub_46FCB0(int a1, int a2, _BYTE *a3, int a4)
{
_DWORD *v4; // eax
int v5; // edx
_DWORD *i; // edi

v4 = (_DWORD *)(a2 + *(_DWORD *)a1 + *(_DWORD *)(*(_DWORD *)a1 + 16));
v5 = a4;
for ( i = v4; v5; ++a3 )
{
--v5;
if ( !*a3 )
break;
*v4++ = (char)*a3; // та самая строка, о которой писал Daniel_Cortez
}
*v4 = 0;
return v4 - i;
}

Оставалось только заменить movsx(signed extension) на movzx(zero extend).
Для этого достаточно было просто записать по адресу где находится movsx значение 0xB60F

Адреса для 03DL:
Windows: 0x46FCD8
Linux: 0x80E1AC7