![]() |
Добро пожаловать, гость ( Вход | Регистрация )
![]() ![]() |
![]() |
Siberian GRemlin |
![]()
Сообщение
#161
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 533 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
В игре выводится количество денег с разделением порядков запятыми. В ЕХЕ упоминается формат вывода
CODE %0lld CODE %03lld Как я понимаю, это оно. Есть ли формат вывода с разделением порядков пробелами?Нужно, чтобы было 1,234,567, а стало 1 234 567. |
-=CHE@TER=- |
![]()
Сообщение
#162
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Это слегка не так работает.
%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 |
![]()
Сообщение
#163
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 533 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
Нашёл. Число выводится несколькими строками, затем склеивается со вставкой запятой в одну строку и выводится на экран. Почему-то я сначала думал, что это стандартная процедура вывода, и поленился сразу в IDA поискать.
Помню, что десятичный разделитель можно было выводить и запятой, и точкой. Но после школы я этим никогда не пользовался. |
-=CHE@TER=- |
![]()
Сообщение
#164
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Помню, что десятичный разделитель можно было выводить и запятой, и точкой. Но после школы я этим никогда не пользовался. В Delphi есть глобальная переменная DecimalSeparator, которой можно присвоить '.' или ',' в зависимости от того что тебе в качестве разделителя нужно. Если не ошибаюсь, это влияет на функции типа StrToFloat(), FloatToStr() и прочие. В сях, конечно, ничего подобного нет. |
Siberian GRemlin |
![]()
Сообщение
#165
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 533 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
Рано я радовался. Хоть я и заменил удачно запятую на пробел, и в игре это стало отображаться, но оказалось, что игра в основной части вылетает, так как эта переменная с запятой, видимо, используется не только там.
Кто-нибудь может глянуть код на асме функции склейки этих «%03lld» с запятой и убрать из неё прибавление запятой? Игра Win64. Работу оплачу. |
-=CHE@TER=- |
![]()
Сообщение
#166
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Рано я радовался. Хоть я и заменил удачно запятую на пробел, и в игре это стало отображаться, но оказалось, что игра в основной части вылетает, так как эта переменная с запятой, видимо, используется не только там. Вышли мне на почту исполняемый файл игры, я его могу в статике посмотреть. И ещё приложи к нему:Кто-нибудь может глянуть код на асме функции склейки этих «%03lld» с запятой и убрать из неё прибавление запятой? Игра Win64. Работу оплачу. fc /b old.exe new.exe >file.txt Чтобы было понятно что ты там и где менял (меня интересует только место где ты пробел на запятую заменил). |
-=CHE@TER=- |
![]()
Сообщение
#167
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Про ограничение у имён файлов.
Мы все прекрасно знаем, что есть программы, которые не работают с русскими или не английскими буквами. На самом деле ограничений гораздо больше, если программа написана не очень хорошо. Ниже будет приведён список из множества тех, с которыми можно так или иначе столкнуться. 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=- |
![]()
Сообщение
#168
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Обсуждали когда-то давно в этой теме код и я такой спрашиваю:
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 |
![]()
Сообщение
#169
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 533 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
Привет.
Кто-нибудь знает, в каком виде нужно пихать буфер в Zcrc32? https://github.com/ashumkin/delphi-zlib/tree/master |
-=CHE@TER=- |
![]()
Сообщение
#170
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
А с чем там проблема?
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 |
![]()
Сообщение
#171
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 533 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
Я как всегда поторопился. Вчера хотел прогу дописать, голова уже не варила. А с утра увидел, что забыл уже ненужную строчку удалить.
|
-=CHE@TER=- |
![]()
Сообщение
#172
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Ковырял я недавно ресурсы одной игры, а они зашифрованы.
Вот код расшифровки: 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=- |
![]()
Сообщение
#173
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Оказывается 16-ти битные (два байта) изображения могут быть не только в форматах:
RGB_565 (RRRRRGGG GGGBBBBB) или: RGBA_4444 (RRRRGGGG BBBBAAAA) но и в совсем уж наркоманском (пришлось документацию на поддерживаемые Android форматы искать): RGBA_5551 (RRRRRGGG GGBBBBBA) Когда при декодировании в формате RGB_565 красный стоит как надо, но идёт слишком много синего и он очень резко обрываясь переходит в зелёный на границах, где должен быть плавный переход - знайте, что, на самом деле, это RGBA_5551. |
Siberian GRemlin |
![]()
Сообщение
#174
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 533 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
|
-=CHE@TER=- |
![]()
Сообщение
#175
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Это-то понятно. Но я никогда до этого не видел, чтобы альфа была последим битом - обычно он первый.
|
-=CHE@TER=- |
![]()
Сообщение
#176
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,355 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 311 раз(а) ![]() |
Спросили меня как-то о том, как я пишу код.
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, да ещё и с разным количеством букв, вместо фигурных скобок, зато поставленная в конце строки ";" ни на что не повлияет (можете проверить). |
![]() ![]() |
Упрощённая версия | Сейчас: 27th April 2024 - 12:40 |