[Урок/Софт] Снятие сжатия с smx (Decompressor)

Kailo

Участник
Сообщения
168
Реакции
755
Для тех, кого не интересует урок, а нужна только программа, переходите сразу к разделу Программа.

Урок
Продолжим нашу одиссею по внутренностям smx.

Теперь, когда мы знаем структуру smx файлов, было бы не плохо использовать полученные нами знания. И начнем мы со снятия шифровки smx файлов. Важно отметить, что даже без сжатия файл является корректным и может использоваться на сервере.

Напомню, из прошлой статьи мы узнали, что информация в smx файле подвергнута сжатию по алгоритму deflate. Данный алгоритм используется в gzip, но наш файл не соответствует формату gzip, так что он нам не подходит.
Оффтоп

И так, сначала определим порядок действий нашей программы:
1) Получаем путь к файлу, у которого следует убрать сжатие.
2) Открываем файл, получаем его размер и проверяем его на соответствие:
a) Размер как минимум больше заголовка;
б) Magic файла соответствует требуемому.​
3) Проводим проверку корректности заголовка:
а) Реальный размер (length) соответствует указному действительному размеру (disksize) в заголовке;
б) Адрес начала данных (dataoffs) меньше реального размера и больше размера заголовка (sizeof(sp_file_hdr_t));
в) Размер файла без сжатия (imagesize) больше чем адрес начала данных.​
4) Когда мы убедились в правильности файла (более-менее), проверяем текущие состояние сжатия файла (compression), и в зависимости от этого действуем дальше. Но сначала немного о сжатии и реализации.
Нам требуется какая-либо реализация алгоритма deflate/inflate), иногда же его называют gzip. К примеру в Lysis от Peace-Maker, используются стандартные методы языка Java, но я пишу свою программу на C++ (в среде Visual Studio Community 2017), у которого нет реализации gzip, так что я решил использовать библиотеку zlib. Тут я к сожалению столкнулся с некоторыми трудностями, т.к. готовых статических версий библиотеки они не предоставляют, а с динамической много проблем и неудобств (компилятор SP так же использует статический метод, а точнее просто копирует необходимые файлы в проект). Так что, я создал отдельный проект статической библиотеки zlib в своем решении, и закинул в него необходимые файлы из исходников zlib 1.2.11. Теперь продолжим:
а) Если наш файл сжат по алгоритму gzip (compression == 1):​
Функция для разжатия имеет следующей прототип:
PHP:
ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen));
Теперь немного пояснений, Bytef тип эквивалентный unsigned char, uLongf - unsigned long.
dest - указатель на буфер, куда будет записываться результат
destLen - указатель на переменную, которая хранит размер этого буфера. Указатель т.к. в конце работы она будет перезаписана, и будет указывать размер данных полученных после работы алгоритма.
source - указатель на исходные данные
sourceLen - размер исходных данных
I) Определяем размер данных после снятия сжатия (uncompressedSize), как размер файла без сжатия с вычетом заголовка, imagesize - dataoffs;
II) Выделяем буфер под данные размером uncompressedSize;
III) Получаем указатель на данные, которые нужно разжать, для этого к указателю на начала файла прибавляем смещение до данных: buffer + dataoffs;
IV) Определяем размер данных для разжатия как размер файла за вычетом заголовка, disksize - dataoffs;
V) Запускаем работу алгоритма и потом проверяем, успешно ли выполнено разжатие;
VI) Изменяем значения переменных заголовка, обновляем значения размера (disksize) и сжатия (compression);
VII) Я решил сделать вывод результата без замены исходного файла в uncompressed.smx - записываем измененный заголовок прежнего файла и данные полученные после разжатия.
б) Если наш файл не имеет сжатия:​
PHP:
ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen, int level));
Здесь аналогичные параметры, только добавляется level - уровень сжатия.
Используется Z_BEST_COMPRESSION, т.к. так же делает оригинальный компилятор.
Для того что бы узнать максимальный требуемый размер буфера данных после сжатия используем
PHP:
ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
sourceLen - размер данных до сжатия.
I) Определяем размер данных до сжатия (uncompressedSize): disksize - dataoffs;
II) Получаем указатель на данные, которые требуется сжать: buffer + dataoffs;
III) Определяем размер буфера (compressedSize) с помощью compressBound(uncompressedSize);
IV) Выделяем буфер под данные размером compressedSize;
V) Запускаем работу алгоритма и потом проверяем, успешно ли выполнено сжатие;
VI) Изменяем значения переменных заголовка, обновляем значения размера (disksize) и сжатия (compression);
VII) Вывод делается в compressed.smx - записываем измененный заголовок прежнего файла и данные полученные после сжатия.
в) А если файл имеет не известный нам тип сжатия, ни чего поделать мы не может и выдаем сообщение об этом.​
5) В конце работы программы не забываем отчищать память за собой, если требуется.

Советы:
Было важно, чтобы файлы при открытии имели флаг binary (бинарный режим работы с файлом).

Продолжение серии уроков:
продолжение следует...

Можете попрактиковаться в программировании и написать себе такую же программу на нужном вам языке.

Программа
Сжатие будет убрано, если присутствует, и добавлено, если отсутствует.
Использование:
Имя файла передается 1-ым параметром:
Код:
Decompressor.exe <smx file>
Совет: Достаточно будет перетащить мышкой требуемый файл на Decompressor.exe.
В результате работы программы создается uncompressed.smx, если происходит снятие сжатия, или compressed.smx, если происходит добавление сжатия.

Результаты моей работы вы можете на посмотреть на GitHub:
Загрузка готового .exe (windows): Releases · Kailo97/Decompressor · GitHub
Исходные файлы: GitHub - Kailo97/Decompressor: (De)compressor for sourcemod's smx files
 
Последнее редактирование:

Webman

old-dev
Сообщения
797
Реакции
296
Для чего делается сжатие вообще?
И что мы получим с того, что уберём это сжатие?
 

Kailo

Участник
Сообщения
168
Реакции
755
Для чего делается сжатие вообще?
И что мы получим с того, что уберём это сжатие?
@Webman, Сжатие делается для уменьшения веса плагинов.
Так же помимо того, что выше сказал R1KO, так же позволяет увидеть строки плагинов в явном виде и редактировать их, и еще многое другое, о чем будет позднее в других уроках, это в одно сообщение не уместить.
 

Саша Шеин

Кому костылей?
Сообщения
1,619
Реакции
473
Пример разницы сжатого и не сжатого плагина. Оффтоп
Например, файл "decompressed.smx", даже не дало загрузить на форум :-)
_SMX_Всякое
 
Последнее редактирование:
Сверху