views:

1817

answers:

6

I am trying to uncompress some data created in VB6 using the zlib API.

I have read this is possible with the qUncompress function: http://doc.trolltech.com/4.4/qbytearray.html#qUncompress

I have read the data in from QDataStream via readRawBytes into a char array, which I then converted to a QByteArray for decompression. I have the compressed length and the expected decompressed length but am not getting anything back from qUncompress.

However I need to prepend the expected decompressed length in big endian format. Has anybody done this and have an example?

+2  A: 

I haven't used VB6 in ages, so I hope this is approximately correct. I think that vb6 used () for array indexing. If I got anything wrong, please let me know.

Looking at the qUncompress docs, you should have put your data in your QByteArray starting at byte 5 (I'm going to assume that you left the array index base set to 1 for this example).

Let's say the array is named qArr, and the expected uncompressed size is Size. In a "big-endian" representation, the first byte is at the first address.

qArr(1) = int(Size/(256*256*256))
qArr(2) = 255 And int(Size/256*256)
qArr(3) = 255 And int(Size/256)
qArr(4) = 255 And int(Size)

Does that make sense?

If you needed little endian, you could just reverse the order of the indexes (qArr(4) - qArr(1)) and leave the calculations the same.

Mike G.
A: 

It looks like you want a C chunk of code that uncompresses some zlib compressed data. In that case is it possible for you to actually use zlib and just feed the zlib data to it. The zlib homepage: http://www.zlib.net/.

If I got it wrong, could you be specific what is the language that should be used for uncompressing the data and why zlib would not be a choice?

Dan Cristoloveanu
qCompress/qUncompress is just a wrapper around zlib.
David Dibben
A: 
//int length;
byte[] bigEndianBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(length))

Conversely:

//byte[] bigEndianBytes;
int length = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(bigEndianBytes))
Are BitConverter and IPAddress available in vb6, though?
Mike G.
+1  A: 

This is how I can convert arbitary data from one format to another.

Private Type LongByte
    H1 As Byte
    H2 As Byte
    L1 As Byte
    L2 As Byte
End Type

Private Type LongType
    L As Long
End Type

Function SwapEndian(ByVal LongData as Long) as Long
  Dim TempL As LongType
  Dim TempLB As LongByte
  Dim TempVar As Long

  TempL.L = LongData
  LSet TempLB = TempL
'Swap is a subroutine I wrote to swap two variables
  Swap TempLB.H1, TempLB.L2
  Swap TempLB.H2, TempLB.L1
  LSet TempL = TempLB
  TempVar = TempL.L

  SwapEndian = TempVar
End Function

If you are dealing with FileIO then you can use the Byte fields of TempLB

The trick is using LSET an obscure command of VB6

If you are using .NET then doing the process is much easier. Here the trick is using a MemoryStream to retrieve and set the individual bytes. Now you could do math for int16/int32/int64. But if you are dealing with with floating point data, using LSET or the MemoryStream is much clearer and easier to debug.

If you are using Framework version 1.1 or beyond then you have the BitConvertor Class which uses arrays of bytes.

Private Structure Int32Byte
    Public H1 As Byte
    Public H2 As Byte
    Public L1 As Byte
    Public L2 As Byte
    Public Function Convert() As Integer
        Dim M As New MemoryStream()
        Dim bR As IO.BinaryReader
        Dim bW As New IO.BinaryWriter(M)
        Swap(H1, L2)
        Swap(H2, L1)
        bW.Write(H1)
        bW.Write(H2)
        bW.Write(L1)
        bW.Write(L2)
        M.Seek(0, SeekOrigin.Begin)
        bR = New IO.BinaryReader(M)
        Convert = bR.ReadInt32()
    End Function
End Structure
RS Conley
A: 

It wasn't clear to me from your question whether you want to prepend the length in VB so that it is suitable for direct use by qUncompress or whether you wanted to use the VB produced data as it is now and prepend the length in C++ before calling qUncompress.

Mike G has posted a VB solution. If you want to do it in C++ then you have two choices, either add the length at the start of the QByteArray or call zlib's uncompress directly. In both cases the Qt source for qCompress and qUncompress (corelib/tools/qbytearray.cpp) are a good reference.

This is how qCompress adds the length (nbytes) to bazip, the compressed data:

bazip[0] = (nbytes & 0xff000000) >> 24;
bazip[1] = (nbytes & 0x00ff0000) >> 16;
bazip[2] = (nbytes & 0x0000ff00) >> 8;
bazip[3] = (nbytes & 0x000000ff);

where bazip is the result QByteArray

Alternatively if you want to call uncompress directly, instead of using the qUncompress wrapper the call it uses is

baunzip.resize(len);
res = ::uncompress((uchar*)baunzip.data(), &len,
                        (uchar*)data+4, nbytes-4);

where baunzip is a QByteArray. In your case you would drop the +4 and -4 since your data does not have the length prepended to it.

David Dibben
A: 

Thank you for all your help, it was useful.

The I got the code working with:

     char slideStr[currentCompressedLen];
     int slideByteRead = in.readRawData(slideStr, currentCompressedLen);
     QByteArray aInCompBytes = QByteArray(slideStr, slideByteRead);
     aInCompBytesPlusLen = aInCompBytes;
     aInCompBytesPlusLen.prepend(QByteArray::number(currentUnCompressedLen));
     aInUnCompBytes.resize(currentUnCompressedLen);
     aInUnCompBytes = qUncompress(aInCompBytesPlusLen);
Phil Hannent