Unit LZSSUnit; { LZSSUNIT - Compress and uncompress unit using LZ77 algorithm for Borland (Turbo) Pascal version 7.0. Assembler Programmer: Andy Tam, Pascal Conversion: Douglas Webb, Unit Conversion and Dynamic Memory Allocation: Andrew Eigus. Public Domain version 1.02, last changed on 30.11.94. Target platforms: DOS, DPMI, Windows. Written by Andrew Eigus (aka: Mr. Byte) of: Fidonet: 2:5100/33, Internet: aeigus@fgate.castle.riga.lv, aeigus@kristin.cclu.lv. } interface {#Z+} { This unit is ready for use with Dj. Murdoch's ScanHelp utility which will make a Borland .TPH file for it. } {#Z-} const LZRWBufSize = 8192; { Read buffer size } {#Z+} N = 4096; { Bigger N -> Better compression on big files only. } F = 18; Threshold = 2; Nul = N * 2; InBufPtr : word = LZRWBufSize; InBufSize : word = LZRWBufSize; OutBufPtr : word = 0; {#Z-} type {#X TWriteProc}{#X LZSquash}{#X LZUnsquash} TReadProc = function(var ReadBuf; var NumRead : word) : word; { This is declaration for custom read function. It should read #LZRWBufSize# bytes from ReadBuf. The return value is ignored. } {#X TReadProc}{#X LZSquash}{#X LZUnsquash} TWriteProc = function(var WriteBuf; Count : word; var NumWritten : word) : word; { This is declaration for custom write function. It should write Count bytes into WriteBuf and return number of actual bytes written into NumWritten variable. The return value is ignored. } {#Z+} PLZRWBuffer = ^TLZRWBuffer; TLZRWBuffer = array[0..LZRWBufSize - 1] of Byte; { file buffers } PLZTextBuf = ^TLZTextBuf; TLZTextBuf = array[0..N + F - 2] of Byte; PLeftMomTree = ^TLeftMomTree; TLeftMomTree = array[0..N] of Word; PRightTree = ^TRightTree; TRightTree = array[0..N + 256] of Word; const LZSSMemRequired = SizeOf(TLZRWBuffer) * 2 + SizeOf(TLZTextBuf) + SizeOf(TLeftMomTree) * 2 + SizeOf(TRightTree); {#Z-} function LZInit : boolean; { This function should be called before any other compression routines from this unit - it allocates memory and initializes all internal variables required by compression procedures. If allocation fails, LZInit returns False, this means that there isn't enough memory for compression or decompression process. It returns True if initialization was successful. } {#X LZDone}{#X LZSquash}{#X LZUnsquash} procedure LZSquash(ReadProc : TReadProc; WriteProc : TWriteProc); { This procedure is used for compression. ReadProc specifies custom read function that reads data, and WriteProc specifies custom write function that writes compressed data. } {#X LZUnsquash}{#X LZInit}{#X LZDone} procedure LZUnSquash(ReadProc : TReadProc; WriteProc : TWriteProc); { This procedure is used for decompression. ReadProc specifies custom read function that reads compressed data, and WriteProc specifies custom write function that writes decompressed data. } {#X LZSquash}{#X LZInit}{#X LZDone} procedure LZDone; { This procedure should be called after you finished compression or decompression. It deallocates (frees) all memory allocated by LZInit. Note: You should always call LZDone after you finished using compression routines from this unit. } {#X LZInit}{#X LZSquash}{#X LZUnsquash} implementation var Height, MatchPos, MatchLen, LastLen : word; TextBufP : PLZTextBuf; LeftP, MomP : PLeftMomTree; RightP : PRightTree; CodeBuf : array[0..16] of Byte; LZReadProc : TReadProc; LZWriteProc : TWriteProc; InBufP, OutBufP : PLZRWBuffer; Bytes : word; Initialized : boolean; Function LZSS_Read : word; { Returns # of bytes read } Begin LZReadProc(InBufP^, Bytes); LZSS_Read := Bytes; End; { LZSS_Read } Function LZSS_Write : word; { Returns # of bytes written } Begin LZWriteProc(OutBufP^, OutBufPtr, Bytes); LZSS_Write := Bytes End; { LZSS_Write } Procedure Getc; assembler; Asm { getc : return a character from the buffer RETURN : AL = input char Carry set when EOF } push bx mov bx, inBufPtr cmp bx, inBufSize jb @getc1 push cx push dx push di push si call LZSS_Read pop si pop di pop dx pop cx mov inBufSize, ax or ax, ax jz @getc2 { ; EOF } xor bx, bx @getc1: PUSH DI LES DI,[InBufP] MOV AL,BYTE PTR [ES:DI+BX] POP DI inc bx mov inBufPtr, bx pop bx clc { ; clear the carry flag } jmp @end @getc2: pop bx stc { ; set carry to indicate EOF } @end: End; { Getc } Procedure Putc; assembler; { putc : put a character into the output buffer Entry : AL = output char } Asm push bx mov bx, outBufPtr PUSH DI LES DI,[OutBufP] MOV BYTE PTR [ES:DI+BX],AL POP DI inc bx cmp bx, LZRWBufSize jb @putc1 mov OutBufPtr,LZRWBufSize { Just so the flush will work. } push cx push dx push di push si call LZSS_Write pop si pop di pop dx pop cx xor bx, bx @putc1: mov outBufPtr, bx pop bx End; { Putc } Procedure InitTree; assembler; { initTree : initialize all binary search trees. There are 256 BST's, one for all strings started with a particular character. The parent is tree K is the node N + K + 1 and it has only a right child } Asm cld push ds pop es LES DI,[RightP] { mov di,offset right} add di, (N + 1) * 2 mov cx, 256 mov ax, NUL rep stosw LES DI,[MomP] { mov di, offset mom} mov cx, N rep stosw End; { InitTree } Procedure Splay; assembler; { splay : use splay tree operations to move the node to the 'top' of tree. Note that it will not actual become the root of the tree because the root of each tree is a special node. Instead, it will become the right child of this special node. ENTRY : di = the node to be rotated } Asm @Splay1: PUSH BX LES BX,[MomP] MOV SI,[ES:BX+DI] POP BX { mov si, [Offset Mom + di]} cmp si, NUL { ; exit if its parent is a special node } ja @Splay4 PUSH DI LES DI,[MomP] ADD DI,SI MOV BX,[ES:DI] { mov bx, [Offset Mom + si]} POP DI cmp bx, NUL { ; check if its grandparent is special } jbe @Splay5 { ; if not then skip } PUSH BX LES BX,[LeftP] CMP DI,[ES:BX+SI] POP BX { cmp di, [Offset Left + si]} { ; is the current node is a left child ? } jne @Splay2 PUSH BX LES BX,[RightP] MOV DX,[ES:BX+DI] { mov dx, [Offset Right + di]} { ; perform a left zig operation } LES BX,[LeftP] MOV [ES:BX+SI],DX { mov [Offset Left + si], dx} LES BX,[RightP] MOV [ES:BX+DI],SI POP BX { mov [Offset Right + di], si} jmp @Splay3 @Splay2: PUSH BX LES BX,[LeftP] MOV DX,[ES:BX+DI] { mov dx, [Offset Left + di]} { ; perform a right zig } LES BX,[RightP] MOV [ES:BX+SI],DX { mov [Offset Right + si], dx} LES BX,[LeftP] MOV [ES:BX+DI],SI POP BX { mov [Offset Left + di], si} @Splay3: PUSH SI LES SI,[RightP] MOV [ES:SI+BX],DI POP SI { mov [Offset Right + bx], di} xchg bx, dx PUSH AX MOV AX,BX LES BX,[MomP] ADD BX,AX MOV [ES:BX],SI LES BX,[MomP] MOV [ES:BX+SI],DI LES BX,[MomP] MOV [ES:BX+DI],DX MOV BX,AX POP AX { mov [Offset Mom + bx], si mov [Offset Mom + si], di mov [Offset Mom + di], dx} @Splay4: jmp @end @Splay5: PUSH DI LES DI,[MomP] MOV CX,[ES:DI+BX] POP DI { mov cx, [Offset Mom + bx]} PUSH BX LES BX,[LeftP] CMP DI,[ES:BX+SI] POP BX { cmp di, [Offset Left + si]} jne @Splay7 PUSH DI LES DI,[LeftP] CMP SI,[ES:DI+BX] POP DI { cmp si, [Offset Left + bx]} jne @Splay6 PUSH AX MOV AX,DI LES DI,[RightP] ADD DI,SI MOV DX,[ES:DI] { mov dx, [Offset Right + si] } { ; perform a left zig-zig operation } LES DI,[LeftP] MOV [ES:DI+BX],DX { mov [Offset Left + bx], dx} xchg bx, dx LES DI,[MomP] MOV [ES:DI+BX],DX { mov [Offset Mom + bx], dx} LES DI,[RightP] ADD DI,AX MOV BX,[ES:DI] { mov bx, [Offset Right + di]} LES DI,[LeftP] ADD DI,SI MOV [ES:DI],BX LES DI,[MomP] MOV [ES:DI+BX],SI { mov [Offset Left +si], bx mov [Offset Mom + bx], si} mov bx, dx LES DI,[RightP] ADD DI,SI MOV [ES:DI],BX LES DI,[RightP] ADD DI,AX MOV [ES:DI],SI { mov [Offset Right + si], bx mov [Offset Right + di], si} LES DI,[MomP] MOV [ES:DI+BX],SI LES DI,[MomP] ADD DI,SI STOSW MOV DI,AX POP AX { mov [Offset Mom + bx], si mov [Offset Mom + si], di} jmp @Splay9 @Splay6: PUSH AX MOV AX,SI LES SI,[LeftP] ADD SI,DI MOV DX,[ES:SI] { mov dx, [Offset Left + di]} { ; perform a left zig-zag operation } LES SI,[RightP] MOV [ES:SI+BX],DX { mov [Offset Right + bx], dx} xchg bx, dx LES SI,[MomP] MOV [ES:SI+BX],DX { mov [Offset Mom + bx], dx} LES SI,[RightP] ADD SI,DI MOV BX,[ES:SI] { mov bx, [Offset Right + di]} LES SI,[LeftP] ADD SI,AX MOV [ES:SI],BX { mov [Offset Left + si], bx} LES SI,[MomP] MOV [ES:SI+BX],AX { mov [Offset Mom + bx], si} mov bx, dx LES SI,[LeftP] ADD SI,DI MOV [ES:SI],BX { mov [Offset Left + di], bx} LES SI,[RightP] ADD SI,DI MOV [ES:SI],AX { mov [Offset Right + di], si} LES SI,[MomP] ADD SI,AX MOV [ES:SI],DI { mov [Offset Mom + si], di} LES SI,[MomP] MOV [ES:SI+BX],DI MOV SI,AX POP AX { mov [Offset Mom + bx], di} jmp @Splay9 @Splay7: PUSH DI LES DI,[RightP] CMP SI,[ES:DI+BX] POP DI { cmp si, [Offset Right + bx]} jne @Splay8 PUSH AX MOV AX,SI LES SI,[LeftP] ADD SI,AX MOV DX,[ES:SI] { mov dx, [Offset Left + si]} { ; perform a right zig-zig } LES SI,[RightP] MOV [ES:SI+BX],DX { mov [Offset Right + bx], dx} xchg bx, dx LES SI,[MomP] MOV [ES:SI+BX],DX { mov [Offset Mom + bx], dx} LES SI,[LeftP] ADD SI,DI MOV BX,[ES:SI] { mov bx, [Offset Left + di]} LES SI,[RightP] ADD SI,AX MOV [ES:SI],BX { mov [Offset Right + si], bx} LES SI,[MomP] MOV [ES:SI+BX],AX { mov [Offset Mom + bx], si} mov bx, dx LES SI,[LeftP] ADD SI,AX MOV [ES:SI],BX { mov [Offset Left + si], bx} LES SI,[LeftP] ADD SI,DI MOV [ES:SI],AX { mov [Offset Left + di], si} LES SI,[MomP] MOV [ES:SI+BX],AX { mov [Offset Mom + bx], si} LES SI,[MomP] ADD SI,AX MOV [ES:SI],DI { mov [Offset Mom + si], di} MOV SI,AX POP AX jmp @Splay9 @Splay8: PUSH AX MOV AX,SI LES SI,[RightP] ADD SI,DI MOV DX,[ES:SI] { mov dx, [Offset Right + di]} { ; perform a right zig-zag } LES SI,[LeftP] MOV [ES:SI+BX],DX { mov [Offset Left + bx], dx} xchg bx, dx LES SI,[MomP] MOV [ES:SI+BX],DX { mov [Offset Mom + bx], dx} LES SI,[LeftP] ADD SI,DI MOV BX,[ES:SI] { mov bx, [Offset Left + di]} LES SI,[RightP] ADD SI,AX MOV [ES:SI],BX { mov [Offset Right + si], bx} LES SI,[MomP] MOV [ES:SI+BX],AX { mov [Offset Mom + bx], si} mov bx, dx LES SI,[RightP] ADD SI,DI MOV [ES:SI],BX { mov [Offset Right + di], bx} LES SI,[LeftP] ADD SI,DI MOV [ES:SI],AX { mov [Offset Left + di], si} LES SI,[MomP] ADD SI,AX MOV [ES:SI],DI LES SI,[MomP] MOV [ES:SI+BX],DI { mov [Offset Mom + si], di mov [Offset Mom + bx], di} MOV SI,AX POP AX @Splay9: mov si, cx cmp si, NUL ja @Splay10 PUSH DI LES DI,[LeftP] ADD DI,SI CMP BX,[ES:DI] POP DI { cmp bx, [Offset Left + si]} jne @Splay10 PUSH BX LES BX,[LeftP] MOV [ES:BX+SI],DI POP BX { mov [Offset Left + si], di} jmp @Splay11 @Splay10: PUSH BX LES BX,[RightP] MOV [ES:BX+SI],DI POP BX { mov [Offset Right + si], di} @Splay11: PUSH BX LES BX,[MomP] MOV [ES:BX+DI],SI POP BX { mov [Offset Mom + di], si} jmp @Splay1 @end: End; { SPlay } Procedure InsertNode; assembler; { insertNode : insert the new node to the corresponding tree. Note that the position of a string in the buffer also served as the node number. ENTRY : di = position in the buffer } Asm push si push dx push cx push bx mov dx, 1 xor ax, ax mov matchLen, ax mov height, ax LES SI,[TextBufP] ADD SI,DI MOV AL,BYTE PTR [ES:SI] { mov al, byte ptr [Offset TextBuf + di]} shl di, 1 add ax, N + 1 shl ax, 1 mov si, ax mov ax, NUL PUSH BX LES BX,[RightP] MOV WORD PTR [ES:BX+DI],AX { mov word ptr [Offset Right + di], ax} LES BX,[LeftP] MOV WORD PTR [ES:BX+DI],AX POP BX { mov word ptr [Offset Left + di], ax} @Ins1:inc height cmp dx, 0 jl @Ins3 PUSH DI LES DI,[RightP] ADD DI,SI MOV AX,WORD PTR [ES:DI] POP DI { mov ax, word ptr [Offset Right + si]} cmp ax, NUL je @Ins2 mov si, ax jmp @Ins5 @Ins2: PUSH BX LES BX,[RightP] MOV WORD PTR [ES:BX+SI],DI { mov word ptr [Offset Right + si], di} LES BX,[MomP] MOV WORD PTR [ES:BX+DI],SI POP BX { mov word ptr [Offset Mom + di], si} jmp @Ins11 @Ins3: PUSH BX LES BX,[LeftP] ADD BX,SI MOV AX,WORD PTR [ES:BX] POP BX { mov ax, word ptr [Offset Left + si]} cmp ax, NUL je @Ins4 mov si, ax jmp @Ins5 @Ins4: PUSH BX LES BX,[LeftP] ADD BX,SI MOV WORD PTR [ES:BX],DI { mov word ptr [Offset Left + si], di} LES BX,[MomP] ADD BX,DI MOV WORD PTR [ES:BX],SI POP BX { mov word ptr [Offset Mom + di], si} jmp @Ins11 @Ins5: mov bx, 1 shr si, 1 shr di, 1 xor ch, ch xor dh, dh @Ins6: PUSH SI LES SI,[TextBufP] ADD SI,DI MOV DL,BYTE PTR [ES:SI+BX] POP SI PUSH DI LES DI,[TextBufP] ADD DI,SI MOV CL,BYTE PTR [ES:DI+BX] POP DI { mov dl, byte ptr [Offset Textbuf + di + bx] mov cl, byte ptr [Offset TextBuf + si + bx]} sub dx, cx jnz @Ins7 inc bx cmp bx, F jb @Ins6 @Ins7: shl si, 1 shl di, 1 cmp bx, matchLen jbe @Ins1 mov ax, si shr ax, 1 mov matchPos, ax mov matchLen, bx cmp bx, F jb @Ins1 @Ins8: PUSH CX LES BX,[MomP] MOV AX,WORD PTR [ES:BX+SI] { mov ax, word ptr [Offset Mom + si]} LES BX,[MomP] MOV WORD PTR [ES:BX+DI],AX { mov word ptr [Offset Mom + di], ax} LES BX,[LeftP] MOV CX,WORD PTR [ES:BX+SI] { mov bx, word ptr [Offset Left + si]} LES BX,[LeftP] MOV WORD PTR [ES:BX+DI],CX { mov word ptr [Offset Left + di], bx} LES BX,[MomP] ADD BX,CX MOV WORD PTR [ES:BX],DI { mov word ptr [Offset Mom + bx], di} LES BX,[RightP] MOV CX,WORD PTR [ES:BX+SI] { mov bx, word ptr [Offset Right + si]} LES BX,[RightP] MOV WORD PTR [ES:BX+DI],CX { mov word ptr [Offset Right + di], bx} LES BX,[MomP] ADD BX,CX MOV WORD PTR [ES:BX],DI { mov word ptr [Offset Mom + bx], di} LES BX,[MomP] MOV CX,WORD PTR [ES:BX+SI] { mov bx, word ptr [Offset Mom + si]} MOV BX,CX POP CX PUSH DI LES DI,[RightP] CMP SI,WORD PTR [ES:DI+BX] POP DI { cmp si, word ptr [Offset Right + bx]} jne @Ins9 PUSH SI LES SI,[RightP] MOV WORD PTR [ES:SI+BX],DI POP SI { mov word ptr [Offset Right + bx], di} jmp @Ins10 @Ins9: PUSH SI LES SI,[LeftP] MOV WORD PTR [ES:SI+BX],DI POP SI { mov word ptr [Offset Left + bx], di} @Ins10: PUSH DI LES DI,[MomP] ADD DI,SI MOV WORD PTR [ES:DI],NUL POP DI { mov word ptr [Offset Mom + si], NUL} @Ins11: cmp height, 30 jb @Ins12 call Splay @Ins12: pop bx pop cx pop dx pop si shr di, 1 End; { InsertNode } Procedure DeleteNode; assembler; { deleteNode : delete the node from the tree ENTRY : SI = position in the buffer } Asm push di push bx shl si, 1 PUSH DI LES DI,[MomP] ADD DI,SI CMP WORD PTR [ES:DI],NUL POP DI { cmp word ptr [Offset Mom + si], NUL} { ; if it has no parent then exit } je @del7 PUSH DI LES DI,[RightP] ADD DI,SI CMP WORD PTR [ES:DI],NUL POP DI { cmp word ptr [Offset Right + si], NUL} { ; does it have right child ? } je @del8 PUSH BX LES BX,[LeftP] MOV DI,WORD PTR [ES:BX+SI] POP BX { mov di, word ptr [Offset Left + si] } { ; does it have left child ? } cmp di, NUL je @del9 PUSH SI LES SI,[RightP] ADD SI,DI MOV AX,WORD PTR [ES:SI] POP SI { mov ax, word ptr [Offset Right + di]} { ; does it have right grandchild ? } cmp ax, NUL je @del2 { ; if no then skip } @del1: mov di, ax { ; find the rightmost node in } PUSH SI LES SI,[RightP] ADD SI,DI MOV AX,WORD PTR [ES:SI] POP SI { mov ax, word ptr [Offset Right + di] } { ; the right subtree } cmp ax, NUL jne @del1 PUSH CX MOV CX,SI LES SI,[MomP] ADD SI,DI MOV BX,WORD PTR [ES:SI] { mov bx, word ptr [Offset Mom + di] } { ; move this node as the root of } LES SI,[LeftP] ADD SI,DI MOV AX,WORD PTR [ES:SI] { mov ax, word ptr [Offset Left + di]} { ; the subtree } LES SI,[RightP] MOV WORD PTR [ES:SI+BX],AX { mov word ptr [Offset Right + bx], ax} xchg ax, bx LES SI,[MomP] MOV WORD PTR [ES:SI+BX],AX { mov word ptr [Offset Mom + bx], ax} LES SI,[LeftP] ADD SI,CX MOV BX,WORD PTR [ES:SI] { mov bx, word ptr [Offset Left + si]} LES SI,[LeftP] ADD SI,DI MOV WORD PTR [ES:SI],BX { mov word ptr [Offset Left + di], bx} LES SI,[MomP] MOV WORD PTR [ES:SI+BX],DI { mov word ptr [Offset Mom + bx], di} MOV SI,CX POP CX @del2: PUSH CX MOV CX,SI LES SI,[RightP] ADD SI,CX MOV BX,WORD PTR [ES:SI] { mov bx, word ptr [Offset Right + si]} LES SI,[RightP] ADD SI,DI MOV WORD PTR [ES:SI],BX { mov word ptr [Offset Right + di], bx} LES SI,[MomP] MOV WORD PTR [ES:SI+BX],DI MOV SI,CX POP CX { mov word ptr [Offset Mom + bx], di} @del3: PUSH CX MOV CX,DI LES DI,[MomP] ADD DI,SI MOV BX,WORD PTR [ES:DI] { mov bx, word ptr [Offset Mom + si]} LES DI,[MomP] ADD DI,CX MOV WORD PTR [ES:DI],BX { mov word ptr [Offset Mom + di], bx} MOV DI,CX POP CX PUSH DI LES DI,[RightP] CMP SI,WORD PTR [ES:DI+BX] POP DI { cmp si, word ptr [Offset Right + bx]} jne @del4 PUSH SI LES SI,[RightP] MOV WORD PTR [ES:SI+BX],DI POP SI { mov word ptr [Offset Right + bx], di} jmp @del5 @del4: PUSH SI LES SI,[LeftP] MOV WORD PTR [ES:SI+BX],DI POP SI { mov word ptr [Offset Left + bx], di} @del5: PUSH DI LES DI,[MomP] ADD DI,SI MOV WORD PTR [ES:DI],NUL POP DI { mov word ptr [Offset Mom + si], NUL} @del7: pop bx pop di shr si, 1 jmp @end; @del8: PUSH BX LES BX,[LeftP] MOV DI,WORD PTR [ES:BX+SI] POP BX { mov di, word ptr [Offset Left + si]} jmp @del3 @del9: PUSH BX LES BX,[RightP] MOV DI,WORD PTR [ES:BX+SI] POP BX { mov di, word ptr [Offset Right + si]} jmp @del3 @end: End; { DeleteNode } Procedure Encode; assembler; Asm call initTree xor bx, bx mov [Offset CodeBuf + bx], bl mov dx, 1 mov ch, dl xor si, si mov di, N - F @Encode2: call getc jc @Encode3 PUSH SI LES SI,[TextBufP] ADD SI,DI MOV BYTE PTR [ES:SI+BX],AL POP SI { mov byte ptr [Offset TextBuf +di + bx], al} inc bx cmp bx, F jb @Encode2 @Encode3: or bx, bx jne @Encode4 jmp @Encode19 @Encode4: mov cl, bl mov bx, 1 push di sub di, 1 @Encode5: call InsertNode inc bx dec di cmp bx, F jbe @Encode5 pop di call InsertNode @Encode6: mov ax, matchLen cmp al, cl jbe @Encode7 mov al, cl mov matchLen, ax @Encode7: cmp al, THRESHOLD ja @Encode8 mov matchLen, 1 or byte ptr codeBuf, ch mov bx, dx PUSH SI LES SI,[TextBufP] ADD SI,DI MOV AL,BYTE PTR [ES:SI] POP SI { mov al, byte ptr [Offset TextBuf + di]} mov byte ptr [Offset CodeBuf + bx], al inc dx jmp @Encode9 @Encode8: mov bx, dx mov al, byte ptr matchPos mov byte ptr [Offset Codebuf + bx], al inc bx mov al, byte ptr (matchPos + 1) push cx mov cl, 4 shl al, cl pop cx mov ah, byte ptr matchLen sub ah, THRESHOLD + 1 add al, ah mov byte ptr [Offset Codebuf + bx], al inc bx mov dx, bx @Encode9: shl ch, 1 jnz @Encode11 xor bx, bx @Encode10: mov al, byte ptr [Offset CodeBuf + bx] call putc inc bx cmp bx, dx jb @Encode10 mov dx, 1 mov ch, dl mov byte ptr codeBuf, dh @Encode11: mov bx, matchLen mov lastLen, bx xor bx, bx @Encode12: call getc { jc @Encode14} jc @Encode15 push ax call deleteNode pop ax PUSH DI LES DI,[TextBufP] ADD DI,SI stosb POP DI { mov byte ptr [Offset TextBuf + si], al} cmp si, F - 1 jae @Encode13 PUSH DI LES DI,[TextBufP] ADD DI,SI MOV BYTE PTR [ES:DI+N],AL POP DI { mov byte ptr [Offset TextBuf + si + N], al} @Encode13: inc si and si, N - 1 inc di and di, N - 1 call insertNode inc bx cmp bx, lastLen jb @Encode12 (* @Encode14: sub printCount, bx jnc @Encode15 mov ax, printPeriod mov printCount, ax push dx { Print out a period as a sign. } mov dl, DBLARROW mov ah, 2 int 21h pop dx *) @Encode15: cmp bx, lastLen jae @Encode16 inc bx call deleteNode inc si and si, N - 1 inc di and di, N - 1 dec cl jz @Encode15 call insertNode jmp @Encode15 @Encode16: cmp cl, 0 jbe @Encode17 jmp @Encode6 @Encode17: cmp dx, 1 jb @Encode19 xor bx, bx @Encode18: mov al, byte ptr [Offset Codebuf + bx] call putc inc bx cmp bx, dx jb @Encode18 @Encode19: End; { Encode } Procedure Decode; assembler; Asm xor dx, dx mov di, N - F @Decode2: shr dx, 1 or dh, dh jnz @Decode3 call getc jc @Decode9 mov dh, 0ffh mov dl, al @Decode3: test dx, 1 jz @Decode4 call getc jc @Decode9 PUSH SI LES SI,[TextBufP] ADD SI,DI MOV BYTE PTR [ES:SI],AL POP SI { mov byte ptr [Offset TextBuf + di], al} inc di and di, N - 1 call putc jmp @Decode2 @Decode4: call getc jc @Decode9 mov ch, al call getc jc @Decode9 mov bh, al mov cl, 4 shr bh, cl mov bl, ch mov cl, al and cl, 0fh add cl, THRESHOLD inc cl @Decode5: and bx, N - 1 PUSH SI LES SI,[TextBufP] MOV AL,BYTE PTR [ES:SI+BX] ADD SI,DI MOV BYTE PTR [ES:SI],AL POP SI { mov al, byte ptr [Offset TextBuf + bx] mov byte ptr [Offset TextBuf + di], al} inc di and di, N - 1 call putc inc bx dec cl jnz @Decode5 jmp @Decode2 @Decode9: End; { Decode } Function LZInit : boolean; Begin if Initialized then Exit; LZInit := False; New(InBufP); New(OutBufP); New(TextBufP); New(LeftP); New(MomP); New(RightP); Initialized := (InBufP <> nil) and (OutBufP <> nil) and (TextBufP <> nil) and (LeftP <> nil) and (MomP <> nil) and (RightP <> nil); if Initialized then LZInit := True else begin Initialized := True; LZDone end End; { LZInit } Procedure LZDone; Begin if Initialized then begin Dispose(InBufP); Dispose(OutBufP); Dispose(RightP); Dispose(MomP); Dispose(LeftP); Dispose(TextBufP); Initialized := False end End; { LZDone } Procedure LZSquash; Begin if Initialized then begin InBufPtr := LZRWBufSize; InBufSize := LZRWBufSize; OutBufPtr := 0; Height := 0; MatchPos := 0; MatchLen := 0; LastLen := 0; FillChar(TextBufP^, SizeOf(TLZTextBuf), 0); FillChar(LeftP^, SizeOf(TLeftMomTree), 0); FillChar(MomP^, SizeOf(TLeftMomTree), 0); FillChar(RightP^, SizeOf(TRightTree), 0); FillChar(CodeBuf, SizeOf(CodeBuf), 0); LZReadProc := ReadProc; LZWriteProc := WriteProc; Encode; LZSS_Write end End; { LZSquash } Procedure LZUnSquash; Begin if Initialized then begin InBufPtr := LZRWBufSize; InBufSize := LZRWBufSize; OutBufPtr := 0; FillChar(TextBufP^, SizeOf(TLZTextBuf), 0); LZReadProc := ReadProc; LZWriteProc := WriteProc; Decode; LZSS_Write end End; { LZUnSquash } {$IFDEF Windows} Function HeapFunc(Size : word) : integer; far; assembler; Asm MOV AX,1 End; { HeapFunc } {$ENDIF} Begin {$IFDEF Windows} HeapError := @HeapFunc; {$ENDIF} Initialized := False End. { LZSSUNIT } { ------------------------- DEMO ---------------------------------} Program LZSSDemo; { Copyright (c) 1994 by Andrew Eigus Fidonet: 2:5100/33 } { Demonstrates the use of LZSSUnit (LZSSUNIT.PAS), Public Domain } uses LZSSUnit; var InFile, OutFile : file; Function ToUpper(S : string) : string; assembler; Asm PUSH DS CLD LDS SI,S LES DI,@Result LODSB STOSB XOR AH,AH XCHG AX,CX JCXZ @@3 @@1: LODSB CMP AL,'a' JB @@2 CMP AL,'z' JA @@2 SUB AL,20h @@2: STOSB LOOP @@1 @@3: POP DS End; { ToUpper } Function ReadProc(var ReadBuf; var NumRead : word) : word; far; Begin BlockRead(InFile, ReadBuf, LZRWBufSize, NumRead); Write(#13, FilePos(InFile), ' -> ') End; { ReadProc } Function WriteProc(var WriteBuf; Count : word; var NumWritten : word) : word; far;Begin BlockWrite(OutFile, WriteBuf, Count, NumWritten); Write(FilePos(OutFile), #13) End; { WriteProc } Begin if ParamCount < 2 then begin WriteLn('Usage: LZSSDEMO [unsquash]'); Halt(1) end; if not LZInit then begin WriteLn('Not enough memory'); Halt(8) end; Assign(InFile, ParamStr(1)); Reset(InFile, 1); if IOResult = 0 then begin Assign(OutFile, ParamStr(2)); Rewrite(OutFile, 1); if IOResult = 0 then begin if ToUpper(ParamStr(3)) = 'UNSQUASH' then LZUnSquash(ReadProc, WriteProc) else LZSquash(ReadProc, WriteProc); Close(OutFile) end else WriteLn('Cannot create output file'); Close(InFile) end else WriteLn('Cannot open input file'); LZDone; WriteLn End.