Tutoriel réalisé par Faiseur
Niveau: [ Intermédiaire ]
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.
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
0 commentaires:
Enregistrer un commentaire