#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <io.h>
#include <direct.h>
#include <sys\stat.h>
//---------------------------------------------------------------------------
typedef int bool;

const true=1;
//---------------------------------------------------------------------------
typedef struct{
  char InternalUse[12];    // Used internally by the dll
  int Time;                // File time
  int Size;                // File size
  int CompressSize;        // Size in zipfile
  int HeaderOffset;        // File offset in zip
  long Crc;                // CRC, sort of checksum
  char FileName[260];      // File name
  WORD PackMethod;         /* 0=Stored, 1=Shrunk, 2=Reduced 1, 3=Reduced 2, 4=Reduced 3, 5=Reduced 4,
                              6=Imploded,7=Tokenized (format does not exist), 8=Deflated,
                              More than 8=Unknown method.
                              For this DLL this number can only be 0, 8, or more than 8
                              */
  WORD Attr;               // File attributes
  WORD Flags;              // Only used by ARJ unpacker (LOBYTE: arj_flags, HIBYTE: file_type)
}PackStruct;

WORD (_stdcall *GetUnzipDllVersion)();            //Get the DLL's version number
int (_stdcall *GetFirstInZip)(char*,PackStruct*); //find out what files are in the ZIP file (first file)
int (_stdcall *GetNextInZip)(PackStruct*);        //get next file in ZIP
void (_stdcall *CloseZipFile)(PackStruct*);       //free buffers and close ZIP after GetNextInZip()
BYTE (_stdcall *isZip)(char*);                    //determine if a file is a ZIP file
long (_stdcall *GetSupportedMethods)();           //find out which ZIP formats are supported by the DLL
int (_stdcall *UnzipFile)(char*,char*,WORD,long,void*,long);        //unzipping
int (_stdcall *UnzipFileToMemory)(char*,void*,long,long,void*,long);//unzipping to memory
int (_stdcall *UnzipTestIntegrity)(char*,long,void*,WORD,long*);     //test integrity of ZIP file

#define UNZIP_Ok           0              //Unpacked ok
#define UNZIP_CRCErr       1              //CRC error
#define UNZIP_WriteErr     2              //Error writing out file: maybe disk full
#define UNZIP_ReadErr      3              //Error reading zip file
#define UNZIP_ZipFileErr   4              //Error in zip structure
#define UNZIP_UserAbort    5              //Aborted by user
#define UNZIP_NotSupported 6              //ZIP Method not supported!
#define UNZIP_Encrypted    7              //Zipfile encrypted
#define UNZIP_InUse        -1             //DLL in use by other program!
#define UNZIP_DLLNotFound  -2             //DLL not loaded!
//---------------------------------------------------------------------------
typedef struct{
  char Path[260];
  int HeaderOffset,Attr;
}ZippedFileInfo;

HINSTANCE hZip=NULL;
//---------------------------------------------------------------------------
bool LoadZipDLL()
{
  hZip=LoadLibrary("unzipd32.dll");
  if (hZip){
    GetUnzipDllVersion=GetProcAddress(hZip,"GetUnzipDllVersion");
    GetFirstInZip=GetProcAddress(hZip,"GetFirstInZip");
    GetNextInZip=GetProcAddress(hZip,"GetNextInZip");
    CloseZipFile=GetProcAddress(hZip,"CloseZipFile");
    isZip=GetProcAddress(hZip,"isZip");
    GetSupportedMethods=GetProcAddress(hZip,"GetSupportedMethods");
    UnzipFile=GetProcAddress(hZip,"unzipfile");
    UnzipFileToMemory=GetProcAddress(hZip,"unzipfiletomemory");
    UnzipTestIntegrity=GetProcAddress(hZip,"UnzipTestIntegrity");

    if (! (GetUnzipDllVersion && GetFirstInZip && GetNextInZip && CloseZipFile && isZip &&
           GetSupportedMethods && UnzipFile && UnzipFileToMemory && UnzipTestIntegrity) ){
      FreeLibrary(hZip);
      return 0;
    }
    return true;
  }
  return 0;
}
//---------------------------------------------------------------------------
void FreeZipDLL()
{
  FreeLibrary(hZip);
}
//---------------------------------------------------------------------------
int main(int argc,char *argv[])
{
  char DestFol[MAX_PATH],ZipFile[MAX_PATH],*Right;

  if (argc<2){
    printf("\n\nUsage: UnZipper SourceZip [DestFolder]\n\n");
    return EXIT_FAILURE;
  }

  strcpy(ZipFile,argv[1]);
  if (access(ZipFile,0)!=0){
    strcat(ZipFile,".zip");
    if (access(ZipFile,0)!=0){
      printf("\nCan't find the file %s\n",ZipFile);
      return EXIT_FAILURE;
    }
  }

  if (argc>=3){
    struct stat s;

    strcpy(DestFol,argv[2]);
    Right=DestFol+strlen(DestFol)-1;
    if (*Right=='\\' || *Right=='/') *Right=0;
    if (stat(DestFol,&s)==-1){
      mkdir(DestFol);
      if (stat(DestFol,&s)==-1){
        printf("\nCould not create the directory %s\n",DestFol);
        return EXIT_FAILURE;
      }
    }
    strcat(DestFol,"\\");
  }else{
    char *LastSlash;

    strcpy(DestFol,ZipFile);
    LastSlash=strrchr(DestFol,'\\');
    if (LastSlash!=NULL){
      *(LastSlash+1)=0;
    }else{
      strcpy(DestFol,argv[0]);
			*(strrchr(DestFol,'\\')+1)=0;
		}
  }

  if (LoadZipDLL()){
    PackStruct PackInfo;
    ZippedFileInfo *pZfi;
    int ErrVal,nFiles=1,i;
    char DestPath[MAX_PATH];

    // Get ZIP files
    ErrVal=GetFirstInZip(ZipFile,&PackInfo);
    if (ErrVal!=UNZIP_Ok){
      printf("\nThere is something wrong with this ZIP file\n");
      FreeZipDLL();
      return EXIT_FAILURE;
    }else{
      int n=0;

      while (GetNextInZip(&PackInfo)==UNZIP_Ok) nFiles++;
      CloseZipFile(&PackInfo);

      pZfi=malloc(sizeof(ZippedFileInfo)*nFiles);

      GetFirstInZip(ZipFile,&PackInfo);
      do{
        strcpy(pZfi[n].Path,PackInfo.FileName);
        pZfi[n].HeaderOffset=PackInfo.HeaderOffset;
        pZfi[n].Attr=PackInfo.Attr;
        n++;
      }while (GetNextInZip(&PackInfo)==UNZIP_Ok);
      CloseZipFile(&PackInfo);
    }

    // Sort file list (in case of silly ZIP software)
    {
      ZippedFileInfo TempZfi;
      int Src,Dest;

      for (Src=0;Src<nFiles;Src++)
      {
        for (Dest=0;Dest<nFiles;Dest++)
        {
          if (Dest!=Src)
          {
            if (strcmpi(pZfi[Src].Path,pZfi[Dest].Path)<0)
            {
              memcpy(&TempZfi,&pZfi[Src],sizeof(ZippedFileInfo));
              memcpy(&pZfi[Src],&pZfi[Dest],sizeof(ZippedFileInfo));
              memcpy(&pZfi[Dest],&TempZfi,sizeof(ZippedFileInfo));

              Src--;
              break;
            }
          }
        }
      }
    }

    // Unzip the files
    for (i=0;i<nFiles;i++){
      strcpy(DestPath,DestFol);
      strcat(DestPath,pZfi[i].Path);

      Right=pZfi[i].Path+strlen(pZfi[i].Path)-1;
      if (*Right=='\\' || *Right=='/'){
        *Right=0;
        mkdir(DestPath);
      }else{
        ErrVal=UnzipFile(ZipFile,DestPath,pZfi[i].Attr,pZfi[i].HeaderOffset,NULL,0);
        if (ErrVal!=UNZIP_Ok){
          printf("\n");
          switch (ErrVal){
            case UNZIP_CRCErr:
              printf("There was an CRC error unzipping %s",DestPath);
              break;
            case UNZIP_WriteErr:
              printf("Could not write the file %s",DestPath);
              break;
            case UNZIP_ReadErr:
              printf("Could not read the file %s",DestPath);
              break;
            case UNZIP_ZipFileErr:
              printf("There is something wrong with this ZIP file");
              break;
            case UNZIP_NotSupported:
              printf("%s is packed using an unsupported method",DestPath);
              break;
            case UNZIP_Encrypted:
              printf("%s is encrypted, give me a break!",DestPath);
              break;
            default:
              printf("An unknown error occurred while unzipping %s",DestPath);
          }
        }
      }
    }

    free(pZfi);
    FreeZipDLL();
    return EXIT_SUCCESS;
  }
  printf("Could not load unzipd32.dll");
  return EXIT_FAILURE;
}
