Delphi, Asm, C, WinAPI, PHP, ..., FAQ |
Добро пожаловать, гость ( Вход | Регистрация )
Delphi, Asm, C, WinAPI, PHP, ..., FAQ |
Siberian GRemlin |
Jul 2 2020, 18:29
Сообщение
#161
|
Advanced Member Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) |
В игре выводится количество денег с разделением порядков запятыми. В ЕХЕ упоминается формат вывода
CODE %0lld CODE %03lld Как я понимаю, это оно. Есть ли формат вывода с разделением порядков пробелами?Нужно, чтобы было 1,234,567, а стало 1 234 567. |
-=CHE@TER=- |
Jul 2 2020, 19:03
Сообщение
#162
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Это слегка не так работает.
%d - вывести digit %ld - вывести long digit (для архитектур, где размер int больше long, например int 16, а long 32) %lld - вывести long long digit (как правило int 64) %0lld - тоже самое что и %lld (символ один и не указан размер) %03lld - тоже самое, что %03d (выравнять тремя нулями слева, если число меньше), но для int64 CODE #include <stdio.h> #include <stdlib.h> #include <windows.h> int main(void) { ULARGE_INTEGER x; x.QuadPart = 1; printf("%0lld\n", x.QuadPart); // будет просто 1 printf("%03lld\n", x.QuadPart); // будет 001 return(0); } В сях нет стандартных функций для разделения тысяч, чтобы из 1234567 сделать 1,234,567 или 1 234 567. Боюсь тебе придётся вручную искать код который это делает. Попробуй поискать по работе со строками и символом 0x2C (запятая). Ставлю на то, что как раз твои строки и работают с числами - смотри где они используются. "%03lld" нужно чтобы у тебя числа менее тысячи добивались нулями: 3007 => 3,007, а не 3,7. "%0lld" - это остаток в левой части, в примере 3007 - это будет тройка, т.к. её добивать нулями не нужно. Спасибо сказали:
|
Siberian GRemlin |
Feb 17 2021, 13:57
Сообщение
#163
|
Advanced Member Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) |
Нашёл. Число выводится несколькими строками, затем склеивается со вставкой запятой в одну строку и выводится на экран. Почему-то я сначала думал, что это стандартная процедура вывода, и поленился сразу в IDA поискать.
Помню, что десятичный разделитель можно было выводить и запятой, и точкой. Но после школы я этим никогда не пользовался. |
-=CHE@TER=- |
Feb 17 2021, 18:27
Сообщение
#164
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Помню, что десятичный разделитель можно было выводить и запятой, и точкой. Но после школы я этим никогда не пользовался. В Delphi есть глобальная переменная DecimalSeparator, которой можно присвоить '.' или ',' в зависимости от того что тебе в качестве разделителя нужно. Если не ошибаюсь, это влияет на функции типа StrToFloat(), FloatToStr() и прочие. В сях, конечно, ничего подобного нет. |
Siberian GRemlin |
Feb 26 2021, 05:30
Сообщение
#165
|
Advanced Member Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) |
Рано я радовался. Хоть я и заменил удачно запятую на пробел, и в игре это стало отображаться, но оказалось, что игра в основной части вылетает, так как эта переменная с запятой, видимо, используется не только там.
Кто-нибудь может глянуть код на асме функции склейки этих «%03lld» с запятой и убрать из неё прибавление запятой? Игра Win64. Работу оплачу. |
-=CHE@TER=- |
Feb 26 2021, 11:23
Сообщение
#166
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Рано я радовался. Хоть я и заменил удачно запятую на пробел, и в игре это стало отображаться, но оказалось, что игра в основной части вылетает, так как эта переменная с запятой, видимо, используется не только там. Вышли мне на почту исполняемый файл игры, я его могу в статике посмотреть. И ещё приложи к нему:Кто-нибудь может глянуть код на асме функции склейки этих «%03lld» с запятой и убрать из неё прибавление запятой? Игра Win64. Работу оплачу. fc /b old.exe new.exe >file.txt Чтобы было понятно что ты там и где менял (меня интересует только место где ты пробел на запятую заменил). |
-=CHE@TER=- |
Apr 2 2022, 14:13
Сообщение
#167
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Про ограничение у имён файлов.
Мы все прекрасно знаем, что есть программы, которые не работают с русскими или не английскими буквами. На самом деле ограничений гораздо больше, если программа написана не очень хорошо. Ниже будет приведён список из множества тех, с которыми можно так или иначе столкнуться. 1) Программы работающие корректно только с английскими буквами в пути и именах файлов. Вернее сказать, работают они корректно только с цифрами "0".."9", английскими буквами "A..Z" (маленькими и большими) и символом подчерк "_". Ещё можно использовать минус-тире "-", тильду "~" и много чего ещё, но не стоит, потому что, как минимум, с тире есть проблемы из-за того что некоторые не особо умные программы, сами парсят командную строку, причём криво, и видя где-то "-" считают что это начало ключа командной строки. Что до не английских букв (русских, немецких и других языков) - так у них в ANSI кодировке код символа более 127, что такие программы считают за мусорные и обрубают строку по первому такому символу. 2) Программы работающие корректно только с именами без пробелов (подмножество предыдущих). Аналогичная предыдущей ситуация, когда кривой парсер командной строки считает пробел как разделитель аргументов. Поэтому исключайте из пути пробелы в имени каталогов и файлов. 3) Программы Windows работающие с ограничением в MAX_PATH (260) символов. Причём, я не помню, вроде бы, MAX_PATH - это включая завершающий ноль, так что, на самом деле, есть только 259 символов. Суть такова: если путь до программы или каталога более MAX_PATH символов, то программа может либо вылететь с ошибкой, либо не запуститься вообще. Чтобы было понятно в чём проблема - большинство функций Windows работающих с файлами и каталогами, типа получения полного пути текущего каталога, принимают два аргумента: буфер и его размер в символах. Можно сделать путь длиннее MAX_PATH, но тогда нужно сперва вызывать функцию чтобы узнать сколько символов нужно, затем выделять память, ещё раз вызывать чтобы уже прочитать в буфер, а после использования этот буфер удалить, чтобы память не текла. Поэтому в 95% случаев (а то и в 99%) разработчики создают на стеке статический буфер в MAX_PATH символов и его используют. При этом некоторые функции, типа GetModuleFileName() (см. документацию в MSDN и пометки о её работе в Windows XP и более младших системах) даже не возвращают нужного размера буфера, так что их приходится вызывать увеличивая размер буфера "на глаз", пока не останется неиспользуемое место в конце. 4) Программы для DOS запущенные в Windows XP / Me / 98 - ограничение в 64 символа. Аналогичная предыдущей ситуация. В 64 символа, вроде бы, входит также и нулевой, так что есть только 63 для пути с именем файла. Тут, правда, всё во много раз хуже из-за того, что в функциях DOS передавался только буфер, без его размера. При этом Windows пишет туда путь даже если он более 64 символов, что, как правило, приводит к разрушению стека и вылету программы с фатальной ошибкой. 5) Ограничение на имя каталога и файла для DOS в 8.3 (8 символов на имя, точка и ещё 3 на расширение). Иными словами такой путь: C:\Program Files\My Files\New File List.txt Превратится во что-то типа такого для DOS приложения: C:\PROGRA~1\MYFILE~1\NEWFIL~1.TXT А если сделать, например, вот такой путь: C:\PROGRAMS\MY_FILES\FILELIST.NEW То он будет одинаковай при его указании как в DOS, так и в Windows и проблем не создаст. А ещё обратите внимание, что в DOS используются только заглавные английские буквы в именах файлов, а также там почему-то, в отличие от Windows, дополнительно запрещены символы "[" и "]" в имени. Большинство из описанных выше проблем решаются вот таким простым пакетным файлом "RUNSHORT.BAT": CODE @echo off subst Z: . Z: program.exe subst Z: /d Виртуальный диск Z: можно заменить на любой другой, как и имя запускаемой программы "program.exe". Обратите внимание, что при создании виртуального диска (первый subst) в конце стоит точка - указатель на текущий каталог (откуда был запущен .BAT файл). Для использования нужно положить "RUNSHORT.BAT" в каталог программы рядом с "program.exe" и запустить. И последнее - не забывайте, что русское (в общем случае не английское или с произвольными символами) имя пользователя в системе тоже может вызвать проблемы (ссылка), т.к. оно входит в часть пути до временного каталога %TEMP% и %TMP%. |
-=CHE@TER=- |
Oct 1 2022, 16:13
Сообщение
#168
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Обсуждали когда-то давно в этой теме код и я такой спрашиваю:
3) MUTEX обязательно должен называться '851137EC-3D96-4EA6-817B-30969CCF477B' или можно своё имя сунуть?.. На что мне отвечают:Можно и своё. Но никто не объяснил, почему это хитровыделанное имя лучше.Дело в том, что GUID - это всегда уникальное имя. Если, конечно, оно не написано абы как, а создано специальной программой, по специальному алгоритму. У кого стоит Microsoft Visual Studio 6 (1998), то там в утилитах нужная программа точно есть - называется "Create GUID": C:\Program Files\Microsoft Visual Studio\Common\Tools\GUIDGEN.EXE Кому лень искать и ставить, то есть аналоги в Интернете (первое что вышло в Google за запросу "create unique guid online"): - https://www.guidgen.com/ - утверждается, что делает это также, как и Microsoft - https://www.guidgenerator.com/ Наверное именно поэтому в том же Far Manager последних версий (в смысле, в версии 3.x точно, может и в 2.x - не проверял) для идентификации плагинов, а также других вещей (типа диалоговых окон) используются именно GUID'ы. Кому интересно к чему приводит попытка назвать что-то простым именем: Two bugs for the price of one. В статье, правда, всё несколько проще, т.к. имя для CreateEvent() можно, вообще, не задавать (указать как NULL), если планируется работать только внутри одного процесса и есть другие способы (помимо имени) для передачи дескриптора между разными частями программы. |
Siberian GRemlin |
Aug 6 2023, 11:17
Сообщение
#169
|
Advanced Member Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) |
Привет.
Кто-нибудь знает, в каком виде нужно пихать буфер в Zcrc32? https://github.com/ashumkin/delphi-zlib/tree/master |
-=CHE@TER=- |
Aug 6 2023, 17:40
Сообщение
#170
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
А с чем там проблема?
P.S. Извини, что на почту не отвечал - очень не до того было. CODE { test.dpr } uses ZLibEx, SysUtils; {$APPTYPE CONSOLE} var fl: File; p: pointer; x: longint; s: AnsiString; begin AssignFile(fl, 'test.dpr'); Reset(fl, 1); x:=FileSize(fl); GetMem(p, x); BlockRead(fl, p^, x); s:='test'; WriteLn( 'D87F7E0C = ', IntToHex( ZCrc32(0, s[1], Length(s)), 8) ); WriteLn( '???????? = ', IntToHex( ZCrc32(0, p^, x), 8) ); FreeMem(p, x); CloseFile(fl); end. |
Siberian GRemlin |
Aug 7 2023, 06:34
Сообщение
#171
|
Advanced Member Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) |
Я как всегда поторопился. Вчера хотел прогу дописать, голова уже не варила. А с утра увидел, что забыл уже ненужную строчку удалить.
|
-=CHE@TER=- |
Aug 9 2023, 09:01
Сообщение
#172
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Ковырял я недавно ресурсы одной игры, а они зашифрованы.
Вот код расшифровки: CODE mov ecx, _data; pointer to the data buffer mov esi, _size; size of the data buffer @uncrypt: mov al, [ecx] mov dl, al not dl xor dl, al and dl, 55h not al xor dl, al mov [ecx], dl inc ecx dec esi jnz @uncrypt Что можно переписать так (внутренняя часть цикла - расшифровка байта): CODE // C uint8_t data; // BYTE data; ... data = (((~data) ^ data) & 0x55) ^ (~data); // Pascal var data: byte; ... data := (((not data) xor data) and $55) xor (not data); И я бы так и оставил, но чем больше я смотрел на это выражение, тем больше у меня возникало ощущение, что я смотрю на какую-то фигню. А давайте распишем логические операции (напомню, что мы работаем в пределах байта): Операция "not data" для байта эквивалентна "data xor 0xFF", отсюда: (not data) xor data => (data xor 0xFF) xor data => data xor 0xFF xor data Так как "data xor data" это ноль, то получаем "0 xor 0xFF", что даёт просто 0xFF и далее: 0xFF and 0x55 - так и останется 0x55 из чего получается: 0x55 xor (not data) Опять заменяем "not data" на "data xor 0xFF": 0x55 xor data xor 0xFF 0xFF xor 0x55 = 0xAA Итого всё выражение: CODE data := (((not data) xor data) and $55) xor (not data); // Pascal Сворачивается просто до: CODE data := data xor 0xAA; // Pascal Кстати, если выражение: CODE data = (((~data) ^ data) & 0x55) ^ (~data); // C Скомпилировать компилятором GCC (версия 3.2 за 2002-08-17) с оптимизацией по размеру, то он в ассемблерном коде всё и свернёт до "data ^= 0xAA". Игра была написана на Microsoft Visual C++ 5.0 (1999) под Windows. Может быть, конечно, тот VC5 ещё не умел оптимизировать такие выражение или не был включён режим оптимизации (он замедляет компиляцию, а в те времена компьютеры были не шибко быстрые) при сборке финального исполняемого файла. Но в любом случае, это не отменяет того факта, что тот кто писал этот код не понимал как работает битовая логика и что вся эта замудрённая дурь вырождается тупо в константу. Иными словами, можно было легко по'xor'ить ресурсы игры байтом от 0x00 до 0xFF и сохранить в отдельные файлы (например, DATA.###, где ### - номер байта, которым xor'илось), после чего просмотреть их (всего-то 256 штук) и найти расшифрованный. Что-то похожее я прочитал в 2011 году на одном зарубежном сайте, правда про другую игру: QUOTE 2009/06/02 / exvcpak © asmodean's reverse engineering pageI kept looking at the scramble algorithm trying to figure out why it does several operations that result in constant values ... finally I concluded that whoever wrote the code is just an idiot. До кучи напомню про это: Command & Conquer: Red Alert [Hidden Easter Egg] - там аж целый главный погром-мист и разработчик, ООП, абстракция на абстракции сидит и абстракцией погоняет, а основ и базы даже не языка программирования, а обычной работы с данными, не знает и не понимает. |
-=CHE@TER=- |
Aug 14 2023, 18:28
Сообщение
#173
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Оказывается 16-ти битные (два байта) изображения могут быть не только в форматах:
RGB_565 (RRRRRGGG GGGBBBBB) или: RGBA_4444 (RRRRGGGG BBBBAAAA) но и в совсем уж наркоманском (пришлось документацию на поддерживаемые Android форматы искать): RGBA_5551 (RRRRRGGG GGBBBBBA) Когда при декодировании в формате RGB_565 красный стоит как надо, но идёт слишком много синего и он очень резко обрываясь переходит в зелёный на границах, где должен быть плавный переход - знайте, что, на самом деле, это RGBA_5551. |
Siberian GRemlin |
Aug 22 2023, 11:16
Сообщение
#174
|
Advanced Member Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) |
|
-=CHE@TER=- |
Aug 22 2023, 15:25
Сообщение
#175
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Это-то понятно. Но я никогда до этого не видел, чтобы альфа была последим битом - обычно он первый.
|
-=CHE@TER=- |
Dec 16 2023, 09:35
Сообщение
#176
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
Спросили меня как-то о том, как я пишу код.
CODE // example 1 if (condition) { code(); } // example 2 if (condition) { code(); } Я ответил, что первый вариант. Объясняю почему на конкретном примере забытой или случайно поставленной не там ";" в коде. CODE // example 1 if (argc != 2) {; return(1); } // example 2 if (argc != 2); { return(1); } Первому примеру люто пофиг, а вот второй пример из-за не там оставленной ";" вырождается в: CODE if (argc != 2) {} return(1); Иными словами код условия начинает выполняться всегда независимо от значения условия. Такая же штука и в Delphi: CODE Program Test; {$APPTYPE CONSOLE} Begin If (ParamCount = 1) Then; // note ";" here Begin WriteLn('two'); End; WriteLn('one'); End. Я теперь понимаю, почему в своих программах товарищ jTommy писал вот так: CODE if (condition) then begin code(); end; В отличие от C выглядит не очень из-за того что тут слова begin-end, да ещё и с разным количеством букв, вместо фигурных скобок, зато поставленная в конце строки ";" ни на что не повлияет (можете проверить). |
Siberian GRemlin |
Aug 11 2024, 04:44
Сообщение
#177
|
Advanced Member Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) |
Я теперь понимаю, почему в своих программах товарищ jTommy писал вот так: CODE if (condition) then begin code(); end; В отличие от C выглядит не очень из-за того что тут слова begin-end, да ещё и с разным количеством букв, вместо фигурных скобок, зато поставленная в конце строки ";" ни на что не повлияет (можете проверить). Я тоже всегда так писал, ибо при такой структуре чётко видно, где блок начинается, а где заканчивается. P. S. Никогда не понимал, зачем ты в коде каждое слово пишешь с большой буквы. |
-=CHE@TER=- |
Aug 11 2024, 13:16
Сообщение
#178
|
Walter Sullivan Группа: Root Admin Сообщений: 1,361 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 314 раз(а) |
P. S. Никогда не понимал, зачем ты в коде каждое слово пишешь с большой буквы. CamelCase потому что. Несколько раздражает читать функции типа "gethostbyname", которые именно что так объявлены (в сях, например, все имена функций регистрозависимые), поэтому ты долго смотришь на них, пытаясь понять что это за слова и где заканчивается одно и начинается другое, чтобы разбить на отдельные слова и понять по имени что функция делает. Можно ещё подчерки использовать get_some_value - но такое мне не очень нравится, ибо символ подчерка увеличивает на один знак строку с именем, а при достаточно длинном имени это разносит в ширину строку, она смотрится громоздко и занимает много места в коде программы.Частным случаем, кстати говоря, является lowerCamelCase - когда заглавные все буквы, кроме первой, так что имена состоящие из одного слова так с маленькой буквы и пишутся, но много-много-много лет назад, когда я только начинал программировать, я о такой штуке не знал, сам не додумался, а сейчас уже привычка. Плюс я точно также могу тебя спросить, зачем ты пишешь предложения с большой буквы? (*улыбается*) Программа - это тоже, в каком-то смысле, текст. В общем, все люди со своими особенностями. |
Упрощённая версия | Сейчас: 1st November 2024 - 03:37 |