Blog. Just Blog

Упаковка и распаковка данных с помощью функций D3D

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Упаковка и распаковка данных с помощью функций D3D
Упаковка и распаковка данных с помощью функций D3D

Функции Microsoft High Level Shader Language (HLSL) изначально предназначены для работы с шейдерами в различных графических приложениях. Но, как выяснилось, с помощью некоторых из этих функций можно весьма эффективно сжимать данные, а потом распаковывать их.

У нас будут использоваться конструкции для работы с объектами, про которые FASM не знает. Ну да, кто б сомневался.
  1. struct D3D_SHADER_DATA
  2.         pBytecode      dd ?
  3.         BytecodeLength dd ?
  4. ends
  5.  
  6. ; ID3D10Blob Interface
  7. struct ID3D10Blob
  8.     ; IUnknown
  9.     QueryInterface   dd ?   ; 000h
  10.     AddRef           dd ?   ; 004h
  11.     Release          dd ?   ; 008h
  12.     ; ID3D10Blob
  13.     GetBufferPointer dd ?   ; 00Ch
  14.     GetBufferSize    dd ?   ; 010h
  15. ends
  16.  
  17. D3D_COMPRESS_SHADER_KEEP_ALL_PARTS = 0x00000001
Начнем как всегда с теории. Для сжатия набора шейдеров в HLSL используется функция D3DCompressShaders, которая экспортируется из библиотеки D3DCompiler_47.dll. У меня в системе она есть, но также я встречал эту библиотеку разных версий в комплекте с некоторыми играми. Как я понимаю, это сделано для совместимости с разными разрядностями системы, на которой эта игра будет запускаться. На вход функции подается заполненная структура D3D_SHADER_DATA, на выходе получаем указатель на интерфейс объекта ID3DBlob. Дальше при помощи методов GetBufferPointer получаем указатель на сжатые данные, а с помощью метода GetBufferSize узнаем их размер. Все очень просто.
  1. ;------------------------------------------------------------
  2. ; Упаковка данных в формате D3D
  3. ;------------------------------------------------------------
  4. ; На входе:
  5. ;   lpOut - указатель на исходные данные
  6. ;   dLen - размер исходных данных
  7. ;   lpCompressed - указатель на буфер для упакованных данных
  8. ; На выходе:
  9. ;   EAX = размер упакованных данных
  10. ;------------------------------------------------------------
  11. proc d3d_pack lpInput:DWORD,dLen:DWORD,lpCompressed:DWORD
  12.         pusha
  13.  
  14.         ; Заполнить структуру D3D_SHADER_DATA
  15.         mov     eax,[lpInput]
  16.         mov     [dsa.pBytecode],eax
  17.         mov     eax,[dLen]
  18.         mov     [dsa.BytecodeLength],eax
  19.  
  20.         ; Упаковать данные
  21.         invoke  D3DCompressShaders,1,dsa,\
  22.                 D3D_COMPRESS_SHADER_KEEP_ALL_PARTS,blob
  23.  
  24.         ; Получить указатель на упакованные данные
  25.         mov     eax,[blob]
  26.         mov     eax,[eax]
  27.         stdcall dword [eax+ID3D10Blob.GetBufferPointer],[blob]
  28.  
  29.         mov     esi,eax
  30.         mov     edi,[lpCompressed]
  31.  
  32.         ; Получить размер упакованных данных
  33.         mov     eax,[blob]
  34.         mov     eax,[eax]
  35.         stdcall dword [eax+ID3D10Blob.GetBufferSize],[blob]
  36.  
  37.         mov     [esp+28],eax
  38.  
  39.         ; Записать упакованные данные в буфер
  40.         mov     ecx,eax
  41.         rep     movsb
  42.  
  43.         ; Прибраться за собой
  44.         mov     eax,[blob]
  45.         mov     eax,[eax]
  46.         stdcall dword [eax+ID3D10Blob.Release],[blob]
  47.  
  48.         popa
  49.  
  50.         ret
  51. endp
Тестирование показало, что небольшие файлы сжимаются плохо, а вот сжатие объемных текстовых файлов получилось гораздо эффективнее, чем даже у aPLib, BitBuster и ZLib. При этом скорость упаковки очень хорошая.

Обратную распаковку упакованных данных выполняет функция D3DDecompressShaders. Но тут есть одна тонкость, из-за которой пришлось провести некоторое время в отладчике. Дело в том, что упакованные данные предваряются DWORD'ом с размером оригинальных данных, после которых следует маркер "BSCD" и остальная информация. А функция распаковки ожидает, что буфер с упакованными данными начинается сразу с заголовка. Поэтому тут надо решить для себя, как поступить. Или передавать упакованные данные, начиная с 4-го байта, или при распаковке увеличивать адрес указателя на эти же 4 байта. Я выбрал второй вариант, так как информация о размере оригинальных данных в большинстве случаев будет очень кстати.
  1. ;------------------------------------------------------------
  2. ; Распаковка данных в формате D3D
  3. ;------------------------------------------------------------
  4. ; На входе:
  5. ;   lpCompressed - указатель на упакованные данные
  6. ;   dLen - размер упакованных данных
  7. ;   lpOut - указатель на буфер для распакованных данных
  8. ; На выходе:
  9. ;   EAX = размер распакованных данных
  10. ;------------------------------------------------------------
  11. proc d3d_unpack lpCompressed:DWORD,dLen:DWORD,lpOut:DWORD
  12.         pusha
  13.  
  14.         mov     dword [esp+28],0
  15.  
  16.         mov     eax,[lpCompressed]
  17.         ; Пропустить DWORD с размером данных
  18.         add     eax,4
  19.  
  20.         ; Распаковать данные
  21.         invoke  D3DDecompressShaders,eax,[dLen],1,0,0,0,blob,NULL
  22.         or      eax,eax
  23.         jnz     .loc_exit
  24.  
  25.         ; Получить указатель на распакованные данные
  26.         mov     eax,[blob]
  27.         mov     eax,[eax]
  28.         stdcall dword [eax+ID3D10Blob.GetBufferPointer],[blob]
  29.  
  30.         mov     esi,eax
  31.         mov     edi,[lpOut]
  32.  
  33.         ; Получить размер распакованных данных
  34.         mov     eax,[blob]
  35.         mov     eax,[eax]
  36.         stdcall dword [eax+ID3D10Blob.GetBufferSize],[blob]
  37.  
  38.         mov     [esp+28],eax
  39.  
  40.         ; Записать распакованные данные в буфер
  41.         mov     ecx,eax
  42.         rep     movsb
  43.  
  44.         ; Прибраться за собой
  45.         mov     eax,[blob]
  46.         mov     eax,[eax]
  47.         stdcall dword [eax+ID3D10Blob.Release],[blob]
  48. .loc_exit:
  49.         popa
  50.         ret
  51. endp
После выполнения функция D3DDecompressShaders также возвращает указатель на интерфейс ID3DBlob, через который по аналогии получаем указатель на распакованные данные и их размер.

В приложении примеры программ с исходными текстами. Это простейший упаковщик данных, работающий через командную строку, и программа, которая извлекает из памяти иконку с помощью D3D-функций, а затем выводит ее на форму.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

D3D.Pack.Unpack.Demo.zip (9,458 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 596 | Комментариев: 5

Комментарии

Отзывы посетителей сайта о статье
Petya (14.02.2024 в 16:47):
DRON, а что это за программа на скриншоте? Ни по названию, ни по картинке не ищется.
ManHunter (13.02.2024 в 10:55):
DRON, ну вот архив с этим микроскопическим cab, там же собранный вручную d3d и пример, который все это распаковывает: https://www.upload.ee/files/16...cab.zip.html
Что интересно, повторить такое сжатие больше не получается.

upd: там иконки разные :)) только сейчас заметил
DRON (13.02.2024 в 01:03):
Ну так и вам никто не мешает вызывать FCIAddFile(...,"S0000!",...,TYPE_LZX+LZX_WINDOW_HI) из cabinet.dll
и получить точно те же 1067+36=1103 байт.
Вот специально только что проверил: файлы созданные D3D и напрямую через FCI API совпадают побайтово с точностью до заголовка.

>Обычный makecab жмет тестовую иконку до 324 байт
Что за параметры у makecab? У меня на разных операционках всегда получается 1059 байт.

И вообще 324 байта выглядят сомнительно: https://imgur.com/a/tHfODUj
ManHunter (12.02.2024 в 21:06):
Только степень сжатия получается далеко не cab'овская. Обычный makecab жмет тестовую иконку до 324 байт, это даже если не учитывать 32+4 байта заголовка D3D.

А вот если исходный файл переименовать в "S0000!" и упаковать makecab, а потом слепить с заголовком, то такой бутерброд будет прекрасно распаковываться через D3DDecompressShaders.
DRON (12.02.2024 в 19:07):
Нет в природе никакого D3D сжатия: это всего лишь CAB-архив с приклеенным к нему заголовком в ~36 байт.
Потому и маленькие файлы плохо сжимаются: там не только заголовок CAB-архива плюс заголовок файла в нём, но и этот D3D заголовок.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0038 сек.) / Память: 4.5 Mb
Наверх