Les algorithmes de compression - partie 2 - La pratique
:: Boîte à outils :: Tutoriels
Page 1 sur 1 • Partager •
Les algorithmes de compression - partie 2 - La pratique
Les algorithmes de compression - partie 2 - La pratique
Tutoriel réalisé par Faiseur
Niveau: [ Intermédiaire ]
INTRODUCTION
Dans ce tutoriel nous allons passer en revue quatre méthodes de compressions différentes dans leur mise en pratique en assembleur en syntaxe Masm/JWasm.
Notez que les procédures en assembleur décrites dans ce tutoriel sont utilisables sans avoir besoin de retouches annexes, si ce n'est d'inclure la librairie qu'ils utilisent (ainsi que le fichier 'macros.asm' s'il y en a besoin).
LIBRAIRIE ZLIB
La librairie Zlib est performante, elle se place en moyenne au-dessus d'une compresison ZIP. L'utiliser dans un projet est simple mais il vous faudra utiliser une dll.
Avantage:
- Compression performante, en général plus efficace que ZIP en compression normale
- Compression rapide
- Décompression rapide
Inconvénients:
- Nécessite une dll d'environ 70ko
Il vous faut donc inclure la librairie dans votre projet:
- Code:
include Lib\zlib.inc
includelib Lib\zlib.lib
L'utilisation des fonctions essentielles de zlib sont très simples. Nous avons besoin de deux fonctions de base: compress et uncompress.
Pour la compression avec la fonction 'compress' je vous propose ma petite procédure suivante qui se sert des macros ('macros.asm' devra donc être inclus dans les définitions) de Masm32:
- Code:
ZLComp PROC Cible:DWORD,Dest:DWORD
LOCAL rMem,dMem,sizes:DWORD
mov rMem,InputFile(Cible)
.if eax == 0 ; si erreur, retour de valeur == 0
ret
.endif
mov sizes,ecx
mov dMem,alloc(ecx)
invoke compress,dMem,addr sizes,rMem,sizes
cmp OutputFile(Dest,dMem,sizes), 0
free rMem
free dMem
ret
ZLComp ENDP
On utilise la fonction 'compress' en lui envoyant comme paramètres:
- Le buffer qui va recevoir les données après compression (ici 'dMem')
- Une adresse qui contiendra la taille des données après compression (ici 'sizes')
- La mémoire contenant les données du fichier à compresser (ici 'rMem')
- La taile des données du fichier à compresser (ici 'sizes')
Nous utilisons donc 'sizes' à la fois pour envoyer la taille du fichier cible et pour stocker la valeur de la taille des données après compression.
La syntaxe d'appel:
- Code:
invoke ZLComp,<Source>,<Destination>
Pour la décompression le procédé est similaire. Avec Masm voici un exemple de procédure, toujours en s'aidant des macros:
- Code:
ZLDecomp PROC uses ebx Cible:DWORD,Dest:DWORD
LOCAL rMem,dMem,sizes,sizesd:DWORD
mov rMem,InputFile(Cible)
mov ebx,ecx
mov eax, ecx
xor edx, edx
mov ecx, 80 ; On multiplie pour un buffer assez large
mul ecx
mov sizesd,eax
mov dMem,alloc(eax)
invoke uncompress,dMem,addr sizesd,rMem,ebx
cmp OutputFile(Dest,dMem,sizesd), 0
free rMem
free dMem
ret
ZLDecomp endp
On utilise la fonction 'uncompress' en lui envoyant comme paramètres:
- Le buffer qui doit recevoir les données après décompression (ici 'dMem')
- Une adresse qui contient la valeur du buffer envoyé dans le premier paramètre et qui recevra la taille des données après décompression (ici 'sizesd')
- La mémoire contenant les données du fichier à décompresser (ici 'rMem')
- La taile des données du fichier à décompresser (ici le registre 'ebx')
Pour trouver la taille du buffer devant recevoir nos données après décompression nous multiplions la taille du fichier compressé selon une valeur fixe, ici 80.
- Code:
mov ecx, 80
La valeur de retour servira a définir la taille du buffer de sortie. Un multiplicateur de 32 serait suffisant dans beaucoup de situations mais ce n'est pas assez pour créer un buffer qui restituerait une image bitmap au complet de par l'énorme ratio de compression possible avec ce format (exemple: un fichier bitmap de 470ko est réduit vers 6ko une fois compressé). On approche donc des 80% et c'est pourquoi 80 est selon moi la valeur à choisir pour générer un buffer de taille suffisante dans tous les cas, mais cela peut sensiblement augmenter la taille du buffer.
Il doit y avoir moyen d'optimiser ce réglage, zlib inclus sûrement un paramètre permettant de déterminer la taille du fichier d'origine mais je ne me suis pas documenté sur la question. Tout fonctionne bien comme ça.
La syntaxe d'appel:
- Code:
invoke ZLDecomp,<Source>,<Destination>
Nous avons fait le tour des fonctions essentielles de Zlib mais elle en inclus d'autres (vérification de checksum par exemple).
LIBRAIRIE APLIB
Avantage:
- Possibilité de gérer une procédure callback lors de la compression
- Décompression rapide
- Ne nécessite pas de dll, légère à embarquer dans une application
Inconvénients:
- Compression pas toujours très bonne mais souvent proche d'une compression ZIp
- Compression un peu lente, en partie à cause de sa fonction callback mais cela peut aussi être un atout
La librairie ApLib de Jorgen Ibsen est tout à fait intéressante. Contrairement à Zlib elle a le mérite de ne pas nécessiter de dll pour l'embarquer dans nos applications. Enfin sa librairie est très légère: 12ko. Avant d'aller plus loin il faut préciser un point important: la version en exemple dans le package Masm32, Aplib 043, que vous seriez tentés d'utiliser comme modèle, date énormément et est à considérer comme totalement périmée. Récupérez la nouvelle version 1.01 sur le site de l'auteur, version qui date de 2009, elle offre un meilleur taux de compression (gain de plusieurs ko) lors de fichiers de taille moyenne.
De manière générale Aplib est moins performant que la librairie Zlib mais proche d'une compression ZIP. Cela dit quelques exceptions la placent parfois en avant. C'est le cas par exemple lorsqu'il s'agit de compresser des fichiers pas trop petits qui permettent de forts taux de compression.
Voici trois exemples testés par mes soins:
1. Fichier avi de 1,312Mo.
Compression NT (sélection 'maximum') = 464ko
Compression par Zlib = 146ko
Compression par ZIP (sélection 'meilleure') = 141ko
Compression par ApLib v101= 139 ko
Compression par Jcalg1 = 120ko
Compression par Rar (sélection 'meilleure') = 101ko
Compression par 7-zip = 97ko
2. Fichier bitmap de 3Mo
Compression NT (sélection 'maximum') = 1,360Mo
Compression par ApLib v101= 843ko
Compression par Zlib = 804ko
Compression par Jcalg1 = 804ko
Compression par ZIP (sélection 'meilleure') = 797ko
Compression par Rar (sélection 'meilleure') = 695ko
Compression par 7-zip = 611ko
3. Fichier bitmap de 120ko
Compression NT (sélection 'maximum') = 19,9ko
Compression par ZIP (sélection 'meilleure') = 1017octets
Compression par Rar (sélection 'meilleure') = 849octets
Compression par Zlib = 804octets
Compression par 7-zip = 457 octets
Compression par ApLib v101= 312 octets
Compression par Jcalg1 = 302 octets
Hé oui, j'ai même trouvé une situation où Aplib monte sur le podium. Aplib s'en sort très bien sans doute parce que son stub de décompression est inférieur à 149 bytes selon son auteur. Les autres applications partent sûrement avec un stub plus lourd, le fichier résultant même si de compression supérieure peut tout de même prendre plus de place. Voici donc une information intéressante. Mais kesako, quel est donc cet illustre inconnu qui le coiffe au poteau ? Jcalg est un format de compression freeware entièrement codé en assembleur, relativement peu connu car mal distribué (son auteur n'assure pas son support mais l'utilise dans ses applications professionnelles). Ne pas utiliser cette excellente librairie serait une erreur car elle est, de plus, gratuite. Je la détaille plus bas.
Passons à la pratique.
Il vous faut donc inclure la librairie dans votre projet:
- Code:
include Lib\aplib.inc
includelib Lib\aplib.lib
Tout comme pour zlib l'utilisation des fonctions essentielles de aplib sont simples mais nécessitent un peu plus de fonctions. Il nous faut par exemple gérer une procédure callback lors de la compression.
Voici la procédure de compression au complet, basée sur l'exemple du packer Masm32 mais purgé de toute fioriture et données annexes. J'ai également remplacé les macros de convention 'C' de l'auteur par les macros du package Masm32 et ajouté un code de retour: 0 si erreur, 1 si réussite. Rien de compliqué ici.
On vérifie que les données sont valides...
on charge le fichier cible...
on vérifie s'il n'est pas déjà compressé par Aplib...
on prépare un buffer pour contenir le fichier destination et on compresse
si le pack est impossible (source inexistante par exemple) on arrête...
Si tout se passe bien..on termine et on arrête également. En étant arrivé jusqu'à la fin on libère les mémoires et on place le code de retour de valeur 1 en EAX.
- Code:
APLIBPackFile proc uses esi szNameFile,szFileName:DWORD ; Aplib, retour 0 si erreur, 1 si réussite
LOCAL hFile,ln,br,source$,dest$,working$,clenth,erreur:DWORD
cmp szNameFile[0], 0
jne @F
mov eax, 0
xor eax,eax
ret
@@:
mov hFile,rv(CreateFile,szNameFile,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
mov ln,rv(GetFileSize,hFile,NULL)
mov source$,alloc(ln)
invoke ReadFile,hFile,source$,ln,ADDR br,NULL
invoke CloseHandle,hFile
mov esi, source$
lodsd
cmp eax, "23PA" ; test signature "AP32"
jne @F
free source$ ; déjà compressé par Apack...
xor eax,eax
ret
@@:
mov dest$,alloc(rv(aP_max_packed_size,ln))
invoke aP_workmem_size,ln
mov working$,alloc(eax)
mov clenth,rv(aPsafe_pack,source$,dest$,ln,working$,ADDR cbProc,NULL)
.if eax == 0
jmp Abort ; "packing aborted"
.endif
.if szFileName[0] != 0
mov hFile,rv(CreateFile,szFileName, GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
invoke WriteFile,hFile,dest$,clenth,ADDR br,NULL
invoke CloseHandle,hFile
.endif
Abort:
free source$
free dest$
free working$
mov eax,1
ret
APLIBPackFile endp
Cette procédure fait appel à une procédure callback, la voici. A vous de la remplir au besoin. Cette procédure permet à l'application d'être informée et/ou d'informer l'utilisateur de la progression de la compression.
- Code:
cbProc proc C orglen:DWORD,len1:DWORD,len2:DWORD,cbparam:DWORD
; ------------------------------------------------------
; This is an application defined callback that receives
; 2 parameters from the "aP_pack" procedure during the
; compression process. Note the "C" calling convention.
; ------------------------------------------------------
ret
cbProc endp
La syntaxe d'appel:
- Code:
invoke APLIBPackFile,<Source>,<Destination>
A présent la procédure de décompression, un peu plus simple. Cette fois :
on vérifie que les données sont valides...
on charge le fichier cible...
on vérifie s'il est bien compressé par Aplib...
on prépare un buffer pour contenir le fichier destination puis on décompresse..
Si tout se passe bien on libère les mémoires et on place le code de retour de valeur 1 en EAX.
- Code:
APLIBUnpackFile proc szNameFile,szFileName:DWORD ; Aplib, retour 0 si erreur, 1 si réussite
LOCAL hFile,ln,br,dsize,source$,dest$:DWORD
cmp szNameFile[0], 0
jne @F
xor eax,eax
ret
@@:
mov hFile,rv(CreateFile,szNameFile,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
mov ln,rv(GetFileSize,hFile,NULL)
mov source$,alloc(ln)
invoke ReadFile,hFile,source$,ln,ADDR br,NULL
invoke CloseHandle,hFile
mov dsize,rv(aPsafe_get_orig_size,source$)
test eax,eax
jnz @F
free source$ ; fichier non compressé par Apack
xor eax,eax
ret
@@:
mov dest$,alloc(dsize)
invoke aPsafe_depack,source$,ln,dest$,dsize
.if szFileName[0] != 0
mov hFile,rv(CreateFile,szFileName,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
invoke WriteFile,hFile,dest$,dsize,ADDR br,NULL
invoke CloseHandle,hFile
.endif
free source$
free dest$
mov eax,1
ret
APLIBUnpackFile endp
La syntaxe d'appel:
- Code:
invoke APLIBUnpackFile,<Source>,<Destination>
LIBRAIRIE JCALG1
Avantage:
- Un ratio de compression tout à fait concurrentiel à ZIP, similaire à Zlib
- Décompression très rapide
- Sa dll n'est pas obligatoire pour nos applications en assembleur, la librairie static est codée en assembleur et légère à embarquer
Inconvénients:
- La compression peut être très lente selon les paramètres utilisés !
- Utilise une fonction callback. Cela ralentit la compression mais peut aussi être un atout selon les besoins.
- Nécessite une dll de petite taille mais nous pouvons nous en passer en assembleur (la librairie a été codée avec Masm, ça aide)
Quelques informations préalables concernant Jcalg1 pour se mettre à jour. Tout d'abord l'auteur a changé plusieurs fois d'adresse semble-t-il mais à présent on le retrouve chez Bitsum Technologies. Derrière cette entreprise et son fer de lance PECompact, un logiciel bien connu dans le milieu, il y a Jeremy Collake. Vous trouverez la librairie Jcalg1 disponible avec ses sources sur ce site, tout comme d'autres contributions de sa part.
Faire un tour sur sa fiche sur le site est assez instructif. On notera sa collaboration avec Steve Gibson mais aussi que Joergen Ibsen, l'auteur de Aplib, a contribué à l'élaboration de Jcalg1. "Jcalg1 est essentiellement une implémentation open-source de aPLib avec quelques modifications et ajouts", dixit Jeremy Collake. Voilà qui explique mieux le superbe résultat de Jcalg1 dans le 3ème test vu plus haut (test avec un petit fichier bitmap). Le stub de Jcalg1 est certainement d'aussi petite taille que Aplib.
La librairie Jcalg1 n'est plus mise à jour me semble-t-il depuis 2004. Elle reste cependant tout à fait concurrentielle au niveau de son ratio de compression, globalement meilleur que Aplib et similaire voir plus performant que ZIP. Il était certainement temps de reparler de cette librairie gratuite en lui donnant la place qu'elle mérite. Nous pouvons de plus facilement nous en servir.
Jcalg1 a cependant un gros défaut: sa vitesse de compression. C'est transparent sur de petits fichiers mais cela peut être excessivement lent dès lors que l'on dépasse les mégaoctets. Nous avons heureusement la possibilité de modifier la vitesse de compression dans les paramètres. Il est ainsi possible d'adapter une compression plus rapide pour des fichiers plus importants, mais évidemment le ratio de compression sera moins élevé.
En résumé: Jcalg1 sera très performants pour compresser de petits fichiers à la volée ou pour préparer des fichiers compressés dans une application où ils seront simplement à décompresser (le temps de compression n'étant pas un paramètre important dans ce cas de figure).
Voici la page de Jcalg1 sur le site de l'auteur. Vous y trouverez expliquée en détail chaque fonction de la librairie.
Notez que dans l'archive de Jcalg1 vous trouverez le code source de cette librairie en assembleur.
Passons à la pratique.
Il vous faut donc inclure la librairie dans votre projet:
- Code:
includelib Lib\jcalg1_static.lib
include Lib\jcalg1_proto.inc
Voici ma version de la procédure de compression avec Jcalg1. Rien de compliqué, ill faut gérer différents paramètres dont la procédure callback.
- Code:
JCcompression PROC Cible:DWORD,Dest:DWORD ; retourne la taille du fichier compressé / Si pas de compression eax == 0
LOCAL hInputFile,hOutputFile,nFilesize,pData,pBuffer,nBytesRead,nCompressedSize:DWORD
mov hInputFile,FUNC(CreateFile,Cible,GENERIC_READ,0,0,OPEN_EXISTING,0,0)
.if eax == INVALID_HANDLE_VALUE
xor eax,eax
ret
.endif
mov nFilesize,FUNC(GetFileSize,hInputFile,NULL)
.if nFilesize == 0
invoke CloseHandle,hInputFile
xor eax,eax
ret
.endif
mov hOutputFile,FUNC(CreateFile,Dest,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
mov pData,FUNC( GlobalAlloc,GMEM_FIXED,nFilesize)
invoke ReadFile,hInputFile,pData,nFilesize,ADDR nBytesRead,0
mov pBuffer,FUNC( GlobalAlloc,GMEM_FIXED,FUNC(JCALG1_GetNeededBufferSize,nFilesize))
mov nCompressedSize,FUNC(JCALG1_Compress,pData,nFilesize,pBuffer,1024*1024,addr AllocFunc,addr DeallocFunc,addr CallbackFunc,TRUE)
.if nCompressedSize != 0
invoke WriteFile,hOutputFile,pBuffer,nCompressedSize,addr nBytesRead,0
.endif
invoke GlobalFree,pData
invoke GlobalFree,pBuffer
invoke CloseHandle,hOutputFile
invoke CloseHandle,hInputFile
mov eax, nCompressedSize
ret
JCcompression endp
Cette procédure fait référence à trois procédures annexes dont la fonction callback. Les voici:
- Code:
AllocFunc PROC nMemSize:DWORD
invoke GlobalAlloc,GMEM_FIXED,nMemSize
ret
AllocFunc endp
DeallocFunc PROC pBuffer:DWORD
invoke GlobalFree,pBuffer
mov eax,TRUE
ret
DeallocFunc endp
CallbackFunc PROC pSourcePos:DWORD,pDestinationPos:DWORD
mov eax,TRUE
ret
CallbackFunc endp
La syntaxe d'appel:
- Code:
invoke JCcompression,<Source>,<Destination>
A présent venons-en au point névralgique de cette librairie: comment régler sa vitesse de compression.
Le paramètre à définir se trouve dans la fonction d'appel 'JCALG1_Compress'. Il s'agit du 4ème paramètre, dont la documentation explique ceci:
"WindowSize is a nonzero value up to the size of the file. The larger, the better the compression ratio but the slower the compression."
La belle affaire, il n'explique rien de plus. Après multitude de tests j'en suis arrivé au constat que la valeur de 1024*1024 est le meilleur choix pour un ratio de compression optimum. Il s'agit du ratio que j'ai utilisé pour réaliser mes trois tests de comparaison cités plus haut.
Cette valeur est très performante pour compresser de petits fichiers mais au moment où vous approchez du mégaoctet il faut baisser cette valeur sous peine d'une grosse attente. Il suffit alors d'intégrer dans la procédure une vérification de la taille du fichier cible, valeur récupérée de toute manière dans la procédure avec 'nFilesize'. Un petit test sur 'nFilesize' et le tour est joué. On applique une seconde version d'appel à 'JCALG1_Compress' avec un WindowSize de valeur moindre si 'nFilesize' dépasse une certaine limite. Cet ajout n'est pas inclus dans cette version.
Voici à présent la procédure de décompression, qui est par contre toujours très rapide.
- Code:
JCdecompression PROC Cible:DWORD,Dest:DWORD ; retourne la taille du fichier décompressé / Si pas de décompression eax == 0
LOCAL hInputFile,hOutputFile,nFilesize,pData,pBuffer,nBytesRead,nCompressedSize,fSize:DWORD
mov hInputFile,FUNC(CreateFile,Cible,GENERIC_READ,0,0,OPEN_EXISTING,0,0)
.if eax == INVALID_HANDLE_VALUE
xor eax,eax
ret
.endif
mov nFilesize,FUNC(GetFileSize,hInputFile,NULL)
.if nFilesize == 0
invoke CloseHandle,hInputFile
xor eax,eax
ret
.endif
mov pBuffer,FUNC( GlobalAlloc,GMEM_FIXED,nFilesize)
invoke ReadFile,hInputFile,pBuffer,nFilesize,ADDR nBytesRead,0
mov fSize,FUNC(JCALG1_GetUncompressedSizeOfCompressedBlock,pBuffer)
.if fSize == 0
invoke CloseHandle,hInputFile
invoke GlobalFree,pBuffer
xor eax,eax
ret
.endif
mov pData,FUNC(GlobalAlloc,GMEM_FIXED,eax)
invoke JCALG1_Decompress_Fast,pBuffer,eax
mov hOutputFile,FUNC( CreateFile,Dest,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,0)
invoke WriteFile,hOutputFile,pData,fSize,ADDR nBytesRead,0
invoke CloseHandle,hOutputFile
invoke CloseHandle,hInputFile
invoke GlobalFree,pData
invoke GlobalFree,pBuffer
mov eax,fSize
ret
JCdecompression endp
La syntaxe d'appel:
- Code:
invoke JCdecompression,<Source>,<Destination>
LIBRAIRIE WINDOWS NT
'Windows NT' car il s'agit de se servir de fonctions NT qui étaient encore récemment non documentées par Microsoft. Ces fonctions n'ont pas été pas révélées au grand jour mais depuis quelques temps des informations sont fournies sur les API utilisées.
Avantages:
- Aucune nécessité de dll ou de librairie embarquée. Il est ainsi possible de réaliser les plus petites applications existantes permettant de compresser des données. 2ko ? Oui c'est faisable.
- La méthode de compression 'standard' est très rapide
Inconvénients:
- Cette librairie n'est pas compatible avec d'anciens OS. Selon la documentation Microsoft la compatibilité est assurée depuis Windows XP
- La méthode de compression 'meilleure' est très lente
- Le ratio de compression reste médiocre
Il semble que cette méthode de compression soit utilisée pour la compression des disques Windows, où la vitesse est plus importante que le taux de compression. La méthode de compression 'standard' est en effet très rapide avec cette librairie.
En ce qui nous concerne, cette librairie n'est évidemment pas à utiliser dans des applications courantes. Le ratio de compression est trop médiocre. Par contre dans des situations où il est nécessaire de créer de très petits exécutables cette possibilité est intéressante.
Un exemple de compression avec cette méthode a été expliqué sur le forum de Masm par MichaelW, qui en a fournit une application test. J'ai repris le principe de son code source que j'ai simplement adapté pour former deux procédures en standalone. Vous trouverez l'exemple de MichaelW dans les archives de la 3ème partie de ce tutoriel.
L'exécutable se base sur trois APi qui sont à présent suffisamment documentées par Microsoft.
RtlCompressBuffer
RtlDecompressBuffer
RtlGetCompressionWorkSpaceSize
Passons à la pratique.
Il vous faut donc inclure la librairie ntdll dans votre projet si vous souhaitez appeler directement ces fonctions. La librairie ntdll.lib et ntdll.inc ne sont pas inclus par défaut dans le package Masm32. On en trouve par contre une version fournie dans le package de développement de drivers de Four-F. Quoiqu'il en soit vous trouverez tout le nécessaire dans l'archive qui est jointe dans cette 3ème partie.
- Code:
include ntdll.inc
includelib ntdll.lib
La procédure de compression:
- Code:
NTCompressFile proc lpUncompressedFileName,lpCompressedFileName,engine:DWORD ; si erreur retourne INVALID_HANDLE_VALUE (-1)
LOCAL hFileU:DWORD,lofU:DWORD,hMMFU:DWORD,lpMemU:DWORD
LOCAL hFileC:DWORD,lofC:DWORD,hMMFC:DWORD,lpMemC:DWORD
LOCAL workSpaceSize:DWORD,lpWorkSpace:DWORD
LOCAL junk,finalSize:DWORD
LOCAL ratio:REAL8
invoke CreateFile,lpUncompressedFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
mov hFileU, eax
.IF eax == INVALID_HANDLE_VALUE ; "Error opening source file"
jmp @F
.ENDIF
invoke CreateFile,lpCompressedFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,
NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
mov hFileC, eax
.IF eax == INVALID_HANDLE_VALUE ; "Error opening destination file"
push eax
invoke CloseHandle,hFileU
pop eax
jmp @F
.ENDIF
mov hMMFU, rv(CreateFileMapping,hFileU,NULL,PAGE_READWRITE,0,0,NULL)
mov lpMemU, rv(MapViewOfFile,hMMFU,FILE_MAP_WRITE,0,0,0)
mov lofU, rv(GetFileSize,hFileU,NULL)
; ----------------------------
; Alloue lofU * 1.25 pour lofC
; ----------------------------
mov eax, lofU
shr eax, 2
add eax, lofU
mov lofC, eax
mov hMMFC, rv(CreateFileMapping,hFileC,NULL,PAGE_READWRITE,0,lofC,NULL)
mov lpMemC, rv(MapViewOfFile,hMMFC,FILE_MAP_WRITE,0,0,0)
mov edx, engine
or edx, COMPRESSION_FORMAT_LZNT1
push edx
invoke RtlGetCompressionWorkSpaceSize,edx,ADDR workSpaceSize,ADDR junk
mov lpWorkSpace, rv(HeapAlloc,rv(GetProcessHeap),0,workSpaceSize)
pop edx
invoke RtlCompressBuffer,edx,lpMemU,lofU,lpMemC,lofC,0,ADDR finalSize,lpWorkSpace
invoke UnmapViewOfFile,lpMemU
invoke UnmapViewOfFile,lpMemC
invoke CloseHandle,hMMFU
invoke CloseHandle,hMMFC
invoke CloseHandle,hFileU
invoke HeapFree,rv(GetProcessHeap),0,lpWorkSpace
invoke SetFilePointer,hFileC,finalSize,0,0
invoke SetEndOfFile,hFileC
invoke CloseHandle,hFileC
@@:
ret
NTCompressFile endp
MichaelW a utilisé le principe du mappage de fichier qui a l'avantage d'être bien mis en cache. La syntaxe d'appel est la suivante:
- Code:
fn NTCompressFile,<Source>,<Destination>,COMPRESSION_ENGINE_STANDARD
Le troisième paramètre a deux options possibles:
- une compression très rapide en utilisant 'COMPRESSION_ENGINE_STANDARD'
- une compression lente mais un peu plus efficace en utilisant 'COMPRESSION_ENGINE_MAXIMUM'
Et voici la procédure pour la décompression.
- Code:
NTDecompressFile proc lpCompressedFileName,lpUncompressedFileName,bufferSize:DWORD ; si erreur retourne INVALID_HANDLE_VALUE (-1)
LOCAL hFileC:DWORD,lofC:DWORD,hMMFC:DWORD,lpMemC,finalSize:DWORD
LOCAL hFileU:DWORD,lofU:DWORD,hMMFU:DWORD,lpMemU:DWORD
invoke CreateFile,lpCompressedFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
mov hFileC,eax
.IF eax == INVALID_HANDLE_VALUE
jmp @F
.ENDIF
invoke CreateFile,lpUncompressedFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,
NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
mov hFileU, eax
.IF eax == INVALID_HANDLE_VALUE
push eax
invoke CloseHandle,hFileC
pop eax
jmp @F
.ENDIF
mov hMMFC, rv(CreateFileMapping,hFileC,NULL,PAGE_READWRITE,0,0,NULL)
mov lpMemC, rv(MapViewOfFile,hMMFC,FILE_MAP_WRITE,0,0,0)
mov lofC, rv(GetFileSize,hFileC,NULL)
mov eax, bufferSize
.IF eax == 0
; ----------------------------
; Alloue environ ~93.8% ratio
; ----------------------------
mov eax, lofC
shl eax, 4
.ENDIF
mov lofU, eax
mov hMMFU, rv(CreateFileMapping,hFileU,NULL,PAGE_READWRITE,0,lofU,NULL)
mov lpMemU, rv(MapViewOfFile,hMMFU,FILE_MAP_WRITE,0,0,0)
invoke RtlDecompressBuffer,COMPRESSION_FORMAT_LZNT1,lpMemU,lofU,lpMemC,lofC,ADDR finalSize
invoke UnmapViewOfFile,lpMemC
invoke UnmapViewOfFile,lpMemU
invoke CloseHandle,hMMFC
invoke CloseHandle,hMMFU
invoke CloseHandle,hFileC
invoke SetFilePointer,hFileU,finalSize,0,0
invoke SetEndOfFile,hFileU
invoke CloseHandle,hFileU
@@:
ret
NTDecompressFile endp
La procédure d'appel est la suivante:
- Code:
fn NTDecompressFile,<Source>,<Destination>,0
Le troisième paramètre, tiré de la version test que j'ai conservé, est optionnel. Si vous placez '0' la procédure va calculer automatiquement un buffer pour la décompression. Si vous placez une valeur, c'est cette valeur qui sera prise en compte pour la taille du buffer.
Note: tout l'attirail pour se servir de ces librairies (librairies, includes, exemples) sera proposé dans la 3ème partie de ce tutoriel

faiseur- Admin
- Messages: 371
Date d'inscription: 02/05/2010

Sujets similaires» Algorithmes et métriques de routage
» Changer la couleur d'une partie d'une image avec Photofiltre
» Quelle partie de votre cerveau utilisez-vous ?
» [TUTORIEL] Les secret du clavier [Partie II]
» Le Staff: Les rôles / Comment en faire partie?
» Changer la couleur d'une partie d'une image avec Photofiltre
» Quelle partie de votre cerveau utilisez-vous ?
» [TUTORIEL] Les secret du clavier [Partie II]
» Le Staff: Les rôles / Comment en faire partie?
:: Boîte à outils :: Tutoriels
Page 1 sur 1
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum




