the-mars-rover / rsa_identification

A dart package for retrieving identification information from South African smart ID's and Driver's Licenses.
MIT License
8 stars 8 forks source link

Decode Drivers License Image Data #2

Open the-mars-rover opened 3 years ago

the-mars-rover commented 3 years ago

The image in the driver's license barcode seems to be encoded as based on the Coreldraw Wavelet image format at 8bit grayscale. But, little else is known about how to decode this image.

pjdupreez commented 3 years ago

Hi there. Just wondering if there is any progress on this? Would like to assist where I can.

the-mars-rover commented 3 years ago

Hi there. Just wondering if there is any progress on this? Would like to assist where I can.

For the moment this has stagnated - any contributions are of course welcome but personally I'm struggling to find time for this right now. If you have any additional info surrounding how the image data may have been encoded adding that to this issue may help move things along :-)

pjdupreez commented 3 years ago

Apart from it being a some wavelet algorithm that was used, unfortunately not. Apparently it is a custom algorithm that was used, but the source was lost when the company tanked in '99 πŸ˜’

Do you the range of the bytes that make up the photo?

the-mars-rover commented 3 years ago

Hi @pjdupreez :)

In terms of the range of bytes, the only insight I have is as per https://github.com/ugommirikwe/sa-license-decoder/blob/master/SPEC.md

Just as a note for everyone who happens to stumble on this issue, the following links are all useful for gaining context:

In addition, a Corrie Burger also emailed me personally and had the following to add with regards to decoding the image: "W.r.t. the image. I found a colour wavelet image online and inspected the header and found the first couple of bytes to be similar. See the first 20 characters of a license ans the colour iamage I found.

License Image b'W' b'I' b'\x04' # Number of image size bytes - 4 b'\x00' b'\xfa' # Height in pixels - 250 b'\x00' b'\xc8' # Width in pixels - 200 b'C' b'(' b'(' b'@' b'\x00' b'0' b'\x02' b'\x8c' b'*' b'%' b'\x92' b'Y' b'\xa3' 57490400fa00c8432828400030028c2a259259a3

Colour wavelet image b'W' b'I' b'\x04' # Number of image size bytes - 4 b'\x05' b'\x00' # Height in pixels - 1280 b'\x03' b'\xc0' # Width in pixels - 960 b'F' b'\x0f' b'H' b'\xc0' b'\x00' b' ' b'\x80' b'\x00' b'\x02' b'\x80' b'x' b'\x00' b'\x80' 574904050003c0460f48c0002080000280780080

Maybe the above info will help."

Hopefully at some point we have the collective brains to solve this :)

Xtr102 commented 2 years ago

Hi There, I have done some digging around this issue and can contribute the following.

There is a swi32.dll (Summus Wavelet Image) assembly that was written in C++. This is presumably the SDK assembly that was supplied by the now defunct company.

I have stumbled on a header file for this, though that does not provide much info on usage. This MyBroadBand user recently posted a screen recording of using this assembly to decode the image, though it does not seem as straight forward as passing the correct bytes to the assembly.

I have managed to decompress the image bytes using this assembly, but that is where I am stuck. The decompressed bytes does not seem to be in a known format, or some additional processing is needed on the data. If I pass 597 compressed image bytes to the decompress method on this assembly I get back around 853 decompressed bytes, which does not seem correct.

If anyone has more insight into this it will be greatly appreciated.

Apologies if any of this was general knowledge already.

pjdupreez commented 2 years ago

I found this: https://pedump.me/7198b62559fb8155733a8b5c3b573e03/

I'd really like to try and see if I can reverse engineer it in order to get it working in either Dart, or Java (Android), or C#, but I have too much on my plate at the moment. Maybe someone else with more time could have a crack at it?

Xtr102 commented 2 years ago

I found this: https://pedump.me/7198b62559fb8155733a8b5c3b573e03/

I'd really like to try and see if I can reverse engineer it in order to get it working in either Dart, or Java (Android), or C#, but I have too much on my plate at the moment. Maybe someone else with more time could have a crack at it?

This is where I got the dll from. I wrote a C# wrapper for it. Seems to be working apart from the decompressed data as mentioned in my previous post.

pjdupreez commented 2 years ago

Mmm. Yeah the bytes you get out seems to be too little... the photo's should come back as 200Γ—250 pixels and should be around 50kb in size.

Have you tried doing it the other way round? Taking a photo and compressing it?

Just a crazy thought, might not work, but have you tried writing the bytes out to a bitmap?

Xtr102 commented 2 years ago

Mmm. Yeah the bytes you get out seems to be too little... the photo's should come back as 200Γ—250 pixels and should be around 50kb in size.

Have you tried doing it the other way round? Taking a photo and compressing it?

Just a crazy thought, might not work, but have you tried writing the bytes out to a bitmap?

Yes on both questions. Compressing seems to work if you fiddle with the compression quality etc. I have however not tried to take a known image and compress and decompress it again to see if it survives. Will try that next. I might also not be decrypting the last block correctly, though the other data is correct. Writing the decompressed bytes out to an image just produces garbage in the image.

Also, a decoded image from an online SADL decoding service results in a byte array of about 8272 bytes, so it seems that either the result of the decompress method does not need to return a 50k odd byte array or this is the size of the jpeg after some processing.

pjdupreez commented 2 years ago

Is it a 'garbage' image when you write to a bitmap, or is there garbage in the image? Can you make out a face, in other words?

Keep one thing in mind when compressing the image: have a look at the bytes it spits out, and see if the amount of compressed bytes are equal to the block that you are trying to decompress.

So in other words, if you feed it a 200Γ—250 image of 50kb and it compresses it to an X amount of bytes, see if that size matches whats in the block. Dont know if the dll resizes the image, as that may also have an effect on the output size πŸ€·β€β™‚οΈ. I dont know, I am just speculating, but I'd think that no matter what image (regardless of size and dimensions) to feed into it, it should spit out the same amount of compressed bytes each time?

Xtr102 commented 2 years ago

It's just noise in the image, no pattern or blurry face etc, just random pixels. The wavelet compression does not seem to generate the same amount of compressed bytes on subsequent runs. I just took a known decoded image and ran it through the compression at 30% quality, first run produced 943 compressed bytes and the second run produced 1564 bytes.

Trying to decompress that image passes the dll, though no way to determine the resulting size apart from the width and height. Reading this directly from memory results in another random garbage image.

pjdupreez commented 2 years ago

I have an hour open a bit later today. If you dont mind sending me a copy of what you've done, I'd like to play around with it?

Xtr102 commented 2 years ago

Yeah, I need to get back to my day job as well. I'll see if I can create a branch on this repo for testing. My current code is a very messy console app.

On second thought I'll make a repo available, this is written in Dart, so would take more to add my C# code in here.

pjdupreez commented 2 years ago

Messy console apps is where breakthroughs are made πŸ’ͺ

Xtr102 commented 2 years ago

Here is the simplified wrapper code. I still have a few things to test and play around with though.

In your test project, add the swi32.dll from PEDump and set the output to always copy. Configure the project for x86 as well.

using System;
using System.Runtime.InteropServices;

namespace SADL
{
    internal static class SWIWrapper
    {
        // Progressive
        public const int PR_NONE = 0;
        public const int PR_NORMAL = 1;
        public const int PR_FAST = 2;

        // Encoders
        public const int E_SLOW = 0;
        public const int E_NORMAL = 1;
        public const int E_FAST = 2;
        public const int E_FASTEST = 3;

        // EncodePaths
        public const int P_PATH1 = 0;
        public const int P_PATH2 = 1;
        public const int P_PATH3 = 2;

        // Split Block-Size Specifications
        public const int SP_OFF = 0;
        public const int SP_AUTO = -1;
        public const int SP_USER = -2;

        // Image Documentation Spec
        public const int SP_BKGRND = 0;
        public const int SP_LAST = -1;
        public const int SP_ALL = -2;

        // Compression Ratio Specifications
        public const int CR_QUALITY = 0;
        public const int CR_CLOSEST = 1;
        public const int CR_FLOOR = 2;

        public delegate int ByteIOFunc(IntPtr param0, ref int param1);
        public delegate int ScanlineIOFunc(IntPtr param0, ref WiImageBlock param1, int param2, ref IntPtr param3);
        public delegate int ExtensionIOFunc(IntPtr param0, int param1, ref IntPtr param2, ref int param3);

        [StructLayout(LayoutKind.Sequential)]
        public struct WiBox
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WiImageBlock
        {
            public int Page;
            public int PageLeft;
            public int PageTop;
            public int PageWidth;
            public int PageHeight;

            public int Block;
            public int BlockLeft;
            public int BlockTop;
            public int BlockWidth;
            public int BlockHeight;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WiRawImage
        {
            public IntPtr Raw;
            public int Height;
            public int Width;
            public int BitsPerPixel;
            public int Color;

            public int LevelHeight;
            public int LevelWidth;
            public IntPtr AppData;

            [MarshalAs(UnmanagedType.LPStr)]
            public string Comment;
            public int CommentLength;

            [MarshalAs(UnmanagedType.LPStr)]
            public string AppExtension;
            public int AppExtensionLength;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct WiCmpImage
        {
            public IntPtr CmpData;
            public int Size;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WiCmpOptions
        {
            public float Quality;
            public float CmpRatio;
            public int CmpControl;

            public int Encoder;
            public int EncodePath;
            public int Progressive;

            public ByteIOFunc WriteNextByte;
            public IntPtr WriteParam;

            public int Magnification;
            public int EdgeEnhancement;
            public int ContrastEnhancement;

            public int FocusWeight;
            public IntPtr FocusBoxes;
            public int nBoxes;

            public int HighColorQuality;

            public ScanlineIOFunc ReadScanline;
            public IntPtr ReadScanlineParam;

            public int BlockSize;
            public IntPtr Blocks;
            public int nBlocks;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct WiDecmpOptions
        {
            public int Smoothing;
            public int Fast;

            public ByteIOFunc ReadNextByte;
            public IntPtr ReadParam;

            public int Sharpening;

            public ScanlineIOFunc WriteScanLine;
            public IntPtr WriteScanLineParam;

            public WiImageBlock SubImage;

            public int Magnification;

            public ExtensionIOFunc WriteAppExtension;
            public IntPtr WriteAppExtensionParam;

        }

        internal static void Test(byte[] imageData)
        {
            var rawImagePointer = WiCreateRawImage();
            var cmpImagePointer = WiCreateCmpImage();
            var decmpOptionsPointer = WiCreateDecmpOptions();

            try
            {
                var cmpImage = new WiCmpImage();
                cmpImage.CmpData = Marshal.AllocHGlobal(imageData.Length);
                Marshal.Copy(imageData, 0, cmpImage.CmpData, imageData.Length);
                cmpImage.Size = imageData.Length;
                Marshal.StructureToPtr(cmpImage, cmpImagePointer, false);

                var result = WiDecompress(decmpOptionsPointer, rawImagePointer, cmpImagePointer);
                var rawImageResult = Marshal.PtrToStructure<WiRawImage>(rawImagePointer);

                var decompressedData = new byte[200 * 250];

                for (int offset = 0; offset < decompressedData.Length; offset++)
                {
                    decompressedData[offset] = Marshal.ReadByte(rawImageResult.Raw, offset);
                }

                var base64Image = Convert.ToBase64String(decompressedData);
            }
            catch (Exception ex)
            {

            }
            finally
            {
                WiDestroyRawImage(rawImagePointer);
                WiDestroyCmpImage(cmpImagePointer);
                WiDestroyDecmpOptions(decmpOptionsPointer);
            }
        }

        internal static void TestCompressionOfKnownImage()
        {
            var rawImagePointer = WiCreateRawImage();
            var cmpImagePointer = WiCreateCmpImage();
            var decmpOptionsPointer = WiCreateDecmpOptions();
            var cmpOptionsPointer = WiCreateCmpOptions();

            try
            {
                // Known image
                var rawBuffer = Convert.FromBase64String("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");
                var rawImage = new WiRawImage();
                rawImage.BitsPerPixel = 8;
                rawImage.Width = 200;
                rawImage.Height = 250;
                rawImage.LevelWidth = 200;
                rawImage.LevelHeight = 250;
                rawImage.Raw = Marshal.AllocHGlobal(rawBuffer.Length);
                Marshal.Copy(rawBuffer, 0, rawImage.Raw, rawBuffer.Length);
                Marshal.StructureToPtr(rawImage, rawImagePointer, false);

                var cmpOptions = new WiCmpOptions();
                cmpOptions.Quality = 0.25f;
                Marshal.StructureToPtr(cmpOptions, cmpOptionsPointer, false);

                var compressResult = WiCompress(cmpOptionsPointer, rawImagePointer, cmpImagePointer);
                var resultImage = Marshal.PtrToStructure<WiCmpImage>(cmpImagePointer);

                var compressedData = new byte[resultImage.Size];

                for (int offset = 0; offset < resultImage.Size; offset++)
                {
                    compressedData[offset] = Marshal.ReadByte(resultImage.CmpData, offset);
                }

                var cmpImage = new WiCmpImage();
                cmpImage.CmpData = Marshal.AllocHGlobal(compressedData.Length);
                Marshal.Copy(compressedData, 0, cmpImage.CmpData, compressedData.Length);
                cmpImage.Size = compressedData.Length;
                Marshal.StructureToPtr(cmpImage, cmpImagePointer, false);

                var decompressResult = WiDecompress(decmpOptionsPointer, rawImagePointer, cmpImagePointer);
                var rawImageResult = Marshal.PtrToStructure<WiRawImage>(rawImagePointer);

                var decompressedData = new byte[200 * 250];

                for (int offset = 0; offset < decompressedData.Length; offset++)
                {
                    decompressedData[offset] = Marshal.ReadByte(rawImageResult.Raw, offset);
                }

                var base64RawImage = Convert.ToBase64String(decompressedData);
            }
            catch (Exception ex)
            {

            }
            finally
            {
                WiDestroyRawImage(rawImagePointer);
                WiDestroyCmpImage(cmpImagePointer);
                WiDestroyCmpOptions(cmpOptionsPointer);
                WiDestroyDecmpOptions(decmpOptionsPointer);
            }
        }

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr WiCreateRawImage();

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr WiCreateCmpImage();

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr WiCreateDecmpOptions();

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr WiCreateCmpOptions();

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern void WiDestroyRawImage(IntPtr RawImage);

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern void WiDestroyCmpImage(IntPtr CmpImage);

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern void WiDestroyCmpOptions(IntPtr CmpOptions);

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern void WiDestroyDecmpOptions(IntPtr DecmpOptions);

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern int WiDecompress(IntPtr WiDecmpOptions, IntPtr WiRawImage, IntPtr WiCmpImage);

        [DllImport("SWI32.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern int WiCompress(IntPtr WiCmpOptions, IntPtr WiRawImage, IntPtr WiCmpImage);
    }
}
Xtr102 commented 2 years ago

And here is the original header contents:

Apologies for pasting, github does not allow .h or .cs files.

/*=========================================================================
  swi32.h
  Header file for Wavelet Image 32-bit SDK

  Version: 4.00

  Created:  Dec 20, 1994
  Revised:  Jan 17, 1997

  Copyright (c) 1994-1997 by Summus, Ltd. All rights reserved.
==========================================================================*/

#ifndef _SWI32_H_
#define _SWI32_H_

/*
** Defines the calling conventions for different platforms. 
*/
#ifndef CALLSPEC
#  if (defined __WIN32__)||(defined _WIN32)||(defined __NT__)||\
      (defined __WINDOWS__)||(defined _Windows)
     /* For MS-Windows Platforms */
#    include <windows.h>
#    define CALLSPEC PASCAL
#  elif (defined __OS2__)                /* For OS/2 Platform */
#    include <os2.h>
#    define CALLSPEC APIENTRY
#  else                                  /* Other platforms(UNIX, MAC...) */
#    define CALLSPEC
#  endif
#endif /* CALLSPEC */

/*
** variable argument list styles
*/
#ifndef ARGSPEC
#  if (defined FUNCPROTO)||(__STDC__)||(defined __cplusplus)||\
      (defined __TURBOC__)     /* ANSI C, C++ */
#    define ARGSPEC(alist) alist
#  else                                                      /* C Classic */
#    define ARGSPEC(alist) ()
#  endif
#endif /* ARGSPEC */

/*
** Progressive
*/
#define PR_NONE   0
#define PR_NORMAL 1
#define PR_FAST   2

/*
** Encoders
*/
#define E_SLOW    0
#define E_NORMAL  1
#define E_FAST    2
#define E_FASTEST 3

/*
** EncodePaths
*/
#define P_PATH1   0
#define P_PATH2   1
#define P_PATH3   2

/*
** Split Block-Size Specifications
*/
#define SP_OFF    0
#define SP_AUTO   (-1)
#define SP_USER   (-2)

/*
** Image Documentation Spec
*/
#define SP_BKGRND  (0)
#define SP_LAST   (-1)
#define SP_ALL    (-2)

/*
** Compression Ratio Specifications
*/
#define CR_QUALITY        0
#define CR_CLOSEST        1
#define CR_FLOOR          2

/*
** Box structure (for focusing box)
*/
typedef struct
{
    int              Left;
    int              Top;
    int              Right;
    int              Bottom;
}WiBox;

/*
** Block structure
*/
typedef struct
{
    int              Page;
    int              PageLeft;
    int              PageTop;
    int              PageWidth;
    int              PageHeight;

    int              Block;
    int              BlockLeft;
    int              BlockTop;
    int              BlockWidth;
    int              BlockHeight;
}WiImageBlock;

/*
** Raw Image
*/
typedef struct
{
    unsigned char    *Raw;
    int              Height;
    int              Width;
    int              BitsPerPixel;
    int              Color;

    int              LevelHeight;
    int              LevelWidth;
    void             *AppData;

    unsigned char    *Comment;
    int              CommentLength;

    unsigned char    *AppExtension;
    int              AppExtensionLength;
}WiRawImage;

/*
** Compressed Image
*/
typedef struct
{
    unsigned char    *CmpData;
    int              Size;
}WiCmpImage;

/*
** byte I/O function pointer
*/
typedef int (*ByteIOFunc)(void*, int*);

/*
** image row I/O function pointer
*/
typedef int (*ScanlineIOFunc)(void*, WiImageBlock*, int, unsigned char**);

/*
** image extension I/O function pointer
*/
typedef int (*ExtensionIOFunc)(void*, int, unsigned char**, int*);

/*
** Compression options
*/
typedef struct{
    float            Quality;
    float            CmpRatio;
    int              CmpControl;

    int              Encoder;
    int              EncodePath;
    int              Progressive;

    ByteIOFunc       WriteNextByte;
    void             *WriteParam;

    int              Magnification;
    int              EdgeEnhancement;
    int              ContrastEnhancement;

    int              FocusWeight;
    WiBox            *FocusBoxes;
    int              nBoxes;

    int              HighColorQuality;

    ScanlineIOFunc   ReadScanline;
    void             *ReadScanlineParam;

    int              BlockSize;
    WiBox            *Blocks;
    int              nBlocks;
}WiCmpOptions;

/*
** Decompression Options
*/
typedef struct
{
    int              Smoothing;
    int              Fast;

    ByteIOFunc       ReadNextByte;
    void             *ReadParam;

    int              Sharpening;

    ScanlineIOFunc   WriteScanline;
    void             *WriteScanlineParam;

    WiImageBlock     SubImage;

    int              Magnification;

    ExtensionIOFunc  WriteAppExtension;
    void             *WriteAppExtensionParam;
}WiDecmpOptions;

/*
** Obsolete data structures. They will be removed in future releases.
*/
typedef struct
{
    int              left;
    int              top;
    int              right;
    int              bottom;
}SiBox;

typedef struct
{
    unsigned char    *Raw;
    int              Height;
    int              Width;
    int              BitsPerPixel;
    int              color;

    int              LevelHeight;
    int              LevelWidth;
    void             *AppData;
}SiImageInfo;

typedef struct
{
    unsigned char    *CmpData;
    int              size;
}SiCmpDataInfo;

typedef struct{
    float            Quality;
    float            CmpRatio;
    int              AutoRatio;

    int              Encoder;
    int              EncodePath;
    int              Progressive;

    ByteIOFunc       WriteNextByte;
    void             *WriteParam;

    int              Magnification;
    int              EdgeEnhancement;
    int              ContrastEnhancement;

    int              FocusWeight;
    SiBox            *FocusBoxes;
    int              nBoxes;
}SiCmpOption;

typedef struct
{
    int              Smoothing;
    int              Fast;

    ByteIOFunc       ReadNextByte;
    void             *ReadParam;

    int              Sharpening;

    ScanlineIOFunc   WriteScanline;
    void             *WriteScanlineParam;
}SiDecmpOption;

/*
** entry points
*/
#ifdef __cplusplus
extern "C"{
#endif

#if (defined __MC68K__)||(defined __POWERPC__)
#  if (__MC68K__)||(__POWERPC__) /* For mac platforms */
#    pragma export on
#  endif
#endif

WiRawImage* CALLSPEC WiCreateRawImage ARGSPEC((
        void
    ));

WiCmpImage* CALLSPEC WiCreateCmpImage ARGSPEC((
        void
    ));

WiCmpOptions* CALLSPEC WiCreateCmpOptions ARGSPEC((
        void
    ));

WiDecmpOptions* CALLSPEC WiCreateDecmpOptions ARGSPEC((
        void
    ));

void CALLSPEC WiDestroyRawImage ARGSPEC((
        WiRawImage        *RawImage
    ));

void CALLSPEC WiDestroyCmpImage ARGSPEC((
        WiCmpImage        *CmpImage
    ));

void CALLSPEC WiDestroyCmpOptions ARGSPEC((
        WiCmpOptions      *CmpOptions
    ));

void CALLSPEC WiDestroyDecmpOptions ARGSPEC((
        WiDecmpOptions    *DecompOptions
    ));

int CALLSPEC WiCompress ARGSPEC((
        WiCmpOptions      *CmpOptions,
        WiRawImage        *RawImage,
        WiCmpImage        *CmpImage
    ));

int CALLSPEC WiDecompress ARGSPEC((
        WiDecmpOptions    *DecmpOptions,
        WiRawImage        *RawImage,
        WiCmpImage        *CmpImage
    ));

int CALLSPEC WiBeginDecompress ARGSPEC((
        WiDecmpOptions    *DecmpOptions,
        WiRawImage        *RawImage,
        WiCmpImage        *CmpImage
    ));

void CALLSPEC WiEndDecompress ARGSPEC((
        WiDecmpOptions    *DecmpOptions,
        WiRawImage        *RawImage,
        WiCmpImage        *CmpImage
    ));

int CALLSPEC WiDecompressSubHeader ARGSPEC((
        WiDecmpOptions    *DecmpOptions,
        WiRawImage        *RawImage,
        WiCmpImage        *CmpImage
    ));

int CALLSPEC WiDecompressSubImage ARGSPEC((
        WiDecmpOptions    *DecmpOptions,
        WiRawImage        *RawImage,
        WiCmpImage        *CmpImage
    ));

int CALLSPEC WiGetProgressiveImage ARGSPEC((
        WiRawImage        *RawImage
    ));

void CALLSPEC WiFreeRawImageData ARGSPEC((
        WiRawImage        *RawImage
    ));

void CALLSPEC WiFreeCmpImageData ARGSPEC((
        WiCmpImage        *CmpImage
    ));

int CALLSPEC WiInsertCmpImage ARGSPEC((
        WiImageBlock      *BlockIndex,
        WiCmpImage        *SrcCmpImage,
        WiCmpImage        *InsCmpImage,
        WiCmpImage        *DesCmpImage
    ));

/*
** Obsolete functions. They will be removed in future releases.
*/

int CALLSPEC SiCompress ARGSPEC((
        SiImageInfo       *RawImage,
        SiCmpOption       *CmpOptions,
        SiCmpDataInfo     *CmpImage
    ));

int CALLSPEC SiDecompress ARGSPEC((
        SiCmpDataInfo     *CmpImage,
        SiDecmpOption     *DecmpOptions,
        SiImageInfo       *RawImage
    ));

int CALLSPEC SiGetImageInfo ARGSPEC((
        SiImageInfo       *ImageInfo
    ));

void CALLSPEC SiFreeImageInfo ARGSPEC((
        SiImageInfo       *ImageInfo
    ));

void CALLSPEC SiFreeCmpDataInfo ARGSPEC((
        SiCmpDataInfo     *CmpDataInfo
    ));

#if (defined __MC68K__)||(defined __POWERPC__)
#  if (__MC68K__)||(__POWERPC__)
#    pragma export off
#  endif
#endif

#ifdef __cplusplus
}
#endif

#endif /* _SWI32_H_ */
pjdupreez commented 2 years ago

Great stuff, thanks. I'll hack away at this.

pjdupreez commented 2 years ago

I had a look at the code and played around a bit. Specifically feeding it a photo, compressing it, and then decompressing it to see if I can get the same image out.

No luck.

What I have seen, though, is if you set the size of the byte[] for the decompressed to the same size as the input image byte[], the resultant bytes seem very close to the original. I cant figure out where (or if) decompress options should be used.

I also used a dll decompiler on the swi32.dll to get to the C code. My idea is to try and see how the compression/decompression is done, then there should be no need for the file. But I'll have to do this some other time.

If you are feeling adventurous, download Snowman: https://derevenets.com/ and feed it the swi32.dll

Xtr102 commented 2 years ago

I have not tried reading the input size from the decompressed array, I have tried a few different variations on the structures, ultimately ended up using pointers and reading the memory directly.

When compressing an image it provides a nice size and pointer to work with. Decompressing would be more complex as one could feed it an image using different bit depths etc.

I actually found the header file on an old Nasa repo that is open on a university server. Looks like they used Summus to compress images taken by a rover. I did not find any examples of where they actually consume the decompression though.

There are decompress options, but I have not been able to spend time on that. I played around with the WriteScanLine callback. Seems like we should be able to either intercept where each scanline point to in memory, or it expects us to override the logic.

Re-writing the logic would be first prize. I also tried to decompile the dll, but the closest I got was getting assembler code using dumpbin, seemed like a lot to read through...

I will definitely give Snowman a go, never heard of it, but if it can spit out C code we should be good to go.

Thanks for your help, I'm sure we can figure this out.

pjdupreez commented 2 years ago

Awesome. Yeah, Snowman was the only decompiler that give C/C++ output, albeit a bit difficult to read without providing propper names for variables and methods. I just quickly had a look at it. Hope it may be of use.

Yip. We'll get this eventually

gertdreyer commented 2 years ago

This has been a pet project of mine for a while now too. I have started to reverse engineer the DLL with Ghidra but its matrix maths is done with basic instructions.

I got the DLL to work on Linux with a shim library and wrote a Rust wrapper for it. https://github.com/gertdreyer/wi_decode

pjdupreez commented 2 years ago

I'd like to give it a try at some point. Well done!

Now, the next thing is, how the heck would one get it to be a 64 bit library πŸ˜…

Wackymax commented 1 year ago

Hi, was there any more progress made on this? I'd like to help out if I can

the-mars-rover commented 1 year ago

Hi Hennie :-)

Thanks so much for reaching out. Unfortunately, I don’t have capacity available to continue working on this. If you’d like, I can transfer the repo to you?

On 31 Aug 2022, at 12:42, Hennie Brink @.***> wrote:

Hi, was there any more progress made on this? I'd like to help out if I can

β€” Reply to this email directly, view it on GitHub https://github.com/the-mars-rover/rsa_identification/issues/2#issuecomment-1232771594, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANRVYXDRACCVWC35DYL7JBDV34ZHXANCNFSM4TOCB3GA. You are receiving this because you authored the thread.

gertdreyer commented 1 year ago

Hi Hennie,

If you are brave, I can give you access to a private repo where I have started decompiling the DLL with Ghidra and fixing the decompiled C that it compiles again and works when my functions replace the internal DLL function calls with hooks. I am using taviso/loadlibrary for hooking the DLL. I am not actively working on this at the moment, however, I suspect it has about half of the functions needed to do a build without hooks...

If you just want to decode the image on a server see gertdreyer/wi_decode.

Wackymax commented 1 year ago

@the-mars-rover I'm not sure I want to take on that responsibility right now πŸ˜…. I will try and make pull requests if I do manage to figure out something.

@gertdreyer Yeah I am just looking to decode the image and it seems like a server side approach would be the easiest to go with right now. Thanks for the link, I will have a look at that.

pjdupreez commented 1 year ago

I am keen to have a go. I have 2 project that use your library to decode and get the info. I have forked it as I am looking at getting it upgraded to Flutter 3 for my projects.

Regards,

On Wed, 31 Aug 2022, 13:10 Hennie Brink, @.***> wrote:

@the-mars-rover https://github.com/the-mars-rover I'm not sure I want to take on that responsibility right now πŸ˜…. I will try and make pull requests if I do manage to figure out something.

@gertdreyer https://github.com/gertdreyer Yeah I am just looking to decode the image and it seems like a server side approach would be the easiest to go with right now. Thanks for the link, I will have a look at that.

β€” Reply to this email directly, view it on GitHub https://github.com/the-mars-rover/rsa_identification/issues/2#issuecomment-1232796400, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAH4NCDTMB4GQXBVLOEEUDDV344R3ANCNFSM4TOCB3GA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

saguran commented 1 year ago

Hi, I have a project where we need to decode the image in the barcode (seems like everyone is having that issue πŸ˜‚). Willing to help wherever I can. Let me know.

Going to try your package @the-mars-rover in Flutter, as it's already a good starting point for us. The other packages we have tried struggle with the driver's license specifically.

Businessware commented 1 year ago

Hi, thanks for all the contributions so far. I have provided a C# codec and encode/decode apps that wraps the Summus SWI32.dll. The codec works fine for encoding followed by decoding known images, but still fails when decoding the SADL WI image. The codec and interop code implements the new "Wi" prefixed methods API, as well as the obsolete "Si" methods API - both API's seem to work the same (was hoping that the old API would magically decode the SADL image, but sadly -no!) The source code and further comments are here:

https://github.com/Businessware/Swi

Please feel free to use the code any which way.

Best regards, Willem Fourie