' Read data from a string/file, then compress it with the free zlib library. 
' Files are written as a compressed .gz file or a QmemoryStream 
' using QmemoryStream you can compress arrays, UDT, bitmaps, whatever! 
' by JohnK , code parts by lucassioli.geo" <cassioli@... 

$TYPECHECK ON
DECLARE Function gzCompressFile(inFile As String, outFile As String) As Long
DECLARE Function gzCompressString(inString As String, OutMem AS QMEMORYSTREAM) AS Long
DECLARE Function gzDeCompressFile(inFile As String, outFile As String) As Long
DECLARE Function gzDecompressToString(InMem AS QMEMORYSTREAM) As String

$include "rapidq.inc"
$include "zlib.inc"     'RQ ported include file for windows 


''----------------------------------------------------------------- 
''  test code to compress / decompress a file 
''----------------------------------------------------------------- 
'pack junk.txt (or whatever file) 
IF gzCompressFile("junk.txt", "junk.txt.gz") = Z_OK THEN
    Showmessage "compression done"
ELSE
    Showmessage "compression error"
    END
END IF

'now test for unpacking 
IF gzDeCompressFile("junk.txt.gz", "text_out.txt") = Z_OK THEN
    Showmessage "Decompression done"
END IF



''------------------------------------------------------------ 
''        test code to compress / decompress memory  
''------------------------------------------------------------ 

DEFSTR inString = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
DEFSTR outString
DIM myMem AS QMEMORYSTREAM

gzCompressString(inString, myMem)           'myMem has compressed data 
inString = ""                               'deallocate 
outString = gzDecompressToString(myMem)     'now decompress 
Showmessage outString

END





Function gzCompressFile(inFile As String, outFile As String) As Long
    DIM inputfile as qfilestream
    DIM mem as qMemoryStream
    DIM r as Long                   'our result 
    Dim gFile As gzFile             'file handle 
    Dim Failure As Integer: Failure = False
    
    '- The input must exist 
    If FileExists(inFile) <> True Then Failure = True 'input exists? 
    If FileExists(outFile) = True Then Failure = True ' output cannot exist 
    '- Tell zLib to open the output file 
    IF Failure = False THEN
        InputFile.open(inFile,fmOpenRead)
        IF InputFile.Size < 1 THEN Failure = True 
    END IF
    
    'file has data in it, get it all open gzfile 
    IF Failure = False THEN
        mem.CopyFrom(InputFile, InputFile.Size) 'get the original data 
        InputFile.close                         'now we have all data 
        gFile = gzopen(outFile, "wb9")          'highest level compression 
        If gFile < Z_OK Then Showmessage "Error: gzopen("+ str$(gFile)  +")" : Failure = True
    END IF
    
    'gzfile is open ok, now store compressed data 
    IF Failure = False THEN
        r = gzwrite(gFile, mem.Pointer, mem.size)
        If r <> mem.size Then showmessage "Error: gzwrite ("+ str$(r)  +")" : Failure = True
    END IF
    
    ' clean up close gzfile 
    IF Failure = False THEN
        r = gzclose(gFile)
        IF r <> Z_OK Then showmessage "Error: gzclose ("+ str$(r)  +")": Failure = True
    END IF
    'now deallocate dynamic memory 
    Mem.Position = 0
    Mem.Size = 0
    Mem.WriteStr(" ", 1)        'bug in QmemoryStream, needs to have byte assigned 
    Mem.Close
    IF Failure = True THEN RESULT = -1 ELSE RESULT = Z_OK
END FUNCTION




Function gzDeCompressFile(inFile As String, outFile As String) As Long
    $DEFINE _BLOCK_SIZE   100000    'working value, bigger or smaller as you need 
    DIM Blk(_BLOCK_SIZE) As Byte
    DIM inputfile as qfilestream
    DIM mem as qMemoryStream
    DIM r as Long                   'our result 
    Dim gFile As gzFile             'file handle 
    Dim Failure As Integer: Failure = False
    
    '- The input must exist 
    If FileExists(inFile) <> True Then Failure = True 'input exists? 
    If FileExists(outFile) = True Then Failure = True ' output cannot exist 

    '- Tell zLib to open the input file, it decompresses into memory buffer 
    IF Failure = False THEN
        gFile = gzopen(inFile, "rb")
        If gFile < Z_OK Then showmessage "Error: gzopen("+ str$(gFile)  +")" :Failure = True
        mem.Position = 0
        WHILE( gzeof(gFile ) = 0 )
            r = gzread(gFile, VarPtr(Blk(0)), _BLOCK_SIZE )
            IF r = _BLOCK_SIZE THEN
                mem.SaveArray(Blk(0), _BLOCK_SIZE)      'automatic append to end of mem stream 
            ELSE
                mem.SaveArray(Blk(0), r)
            END IF
        WEND
    END IF

    'decompressed into memory OK, now close the file  
    IF Failure = False THEN
        r = gzclose(gFile)
        If r <> Z_OK Then showmessage "Error: gzclose ("+ str$(r)  +")" :Failure = True
    END IF

    'Now store the decompressed buffer into a file 
    IF Failure = False THEN
        InputFile.open(outFile,fmCReate)
        InputFile.CopyFrom(mem, 0)' whole stream is copied 
        InputFile.close
    END IF
    'now deallocate dynamic memory 
    Mem.Position = 0
    Mem.Size = 0
    Mem.WriteStr(" ", 1)        'bug in QmemoryStream, needs to have byte assigned 
    Mem.Close
    IF Failure = True THEN RESULT = -1 ELSE RESULT = Z_OK
END FUNCTION


'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
'  gzCompressString 
'  Compresses the string 
'  Code adapted from Don Dickinson PB example, modified for RQ by JohnK 
'  ddickinson@usinternet.com 
'  store compressed string in Qmemorystream because it might have a zero (early termination) 
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    
Function gzCompressString(inString As String, OutMem AS QMEMORYSTREAM) AS Long
   Dim iReturn As Long
   Dim iDeComp As Long
   Dim iComp As Long
   DEFSTR tmpStr = inString     'need this for string pointer 
   DIM tmpMem As QMemoryStream

   RESULT = False              'set in case of failure 
   If Len(inString) > 1  Then
      '- Calculate and allocate the compression buffer. 
      tmpMem.Size = (Len(tmpStr) * 1.2 + 12)
      iComp = tmpMem.Size       'need a ref pointer here too, MEM.size is set/get 
      iDeComp = Len(tmpStr)
      '- Compress it 
      iReturn = compress(tmpMem.Pointer, iComp, VarPtr(tmpStr), iDeComp)
      If iReturn = Z_OK Then
         '- the first 4 bytes in compressed stream has the length of the decompressed buffer 
         OutMem.Write(iDecomp)          'RQ knows Long takes 4 bytes 
         OutMem.CopyFrom(tmpMem, iComp) 'copy remaining buffer 
         RESULT = True
      Else
         ShowMessage "Error compressing buffer (" + Str$(iReturn) + ")"
         RESULT =  False
      End If
   End If
End Function


'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
Function gzDecompressToString(InMem AS QMEMORYSTREAM) As String
   Dim iReturn As Long                  'read results 
   Dim iDeComp As Long
   Dim deString As String
   Dim SizStr As String

   RESULT = ""                          'set in case of failure    
   If InMem.Size > 4 Then
      '- The first 4 bytes contain the length of the decompressed string 
      InMem.Position = 0
      InMem.Read(iDeComp)
      '- Create the decompression buffer, alloc string space 
      deString = SPACE$(iDeComp)
      iReturn = uncompress(VarPtr(deString), iDeComp, InMem.Pointer + 4, InMem.Size)
      if iReturn = Z_OK THEN 
            RESULT = deString
      else
            Showmessage "Decompression Failed ("+ Str$(iReturn) + ")"
      end if
   end if
End Function