text: #include "rar.hpp"
  #include "dll.hpp"
  
  static int RarErrorToDll(int ErrCode);
  
  struct DataSet
  {
    CommandData Cmd;
    CmdExtract Extract;
    Archive Arc;
    int OpenMode;
    int HeaderSize;
  
    DataSet():Arc(&Cmd) {};
  };
  
  
  HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
  {
    RAROpenArchiveDataEx rx;
    memset(&rx,0,sizeof(rx));
    rx.ArcName=r->ArcName;
    rx.OpenMode=r->OpenMode;
    rx.CmtBuf=r->CmtBuf;
    rx.CmtBufSize=r->CmtBufSize;
    HANDLE hArc=RAROpenArchiveEx(&rx);
    r->OpenResult=rx.OpenResult;
    r->CmtSize=rx.CmtSize;
    r->CmtState=rx.CmtState;
    return(hArc);
  }
  
  
  HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
  {
    try
    {
      r->OpenResult=0;
      DataSet *Data=new DataSet;
      Data->Cmd.DllError=0;
      Data->OpenMode=r->OpenMode;
      Data->Cmd.FileArgs->AddString("*");
  
      char an[NM];
      if (r->ArcName==NULL && r->ArcNameW!=NULL)
      {
        WideToChar(r->ArcNameW,an,NM);
        r->ArcName=an;
      }
  
      Data->Cmd.AddArcName(r->ArcName,r->ArcNameW);
      Data->Cmd.Overwrite=OVERWRITE_ALL;
      Data->Cmd.VersionControl=1;
  
      Data->Cmd.Callback=r->Callback;
      Data->Cmd.UserData=r->UserData;
  
      if (!Data->Arc.Open(r->ArcName,r->ArcNameW))
      {
        r->OpenResult=ERAR_EOPEN;
        delete Data;
        return(NULL);
      }
      if (!Data->Arc.IsArchive(false))
      {
        r->OpenResult=Data->Cmd.DllError!=0 ? Data->Cmd.DllError:ERAR_BAD_ARCHIVE;
        delete Data;
        return(NULL);
      }
      r->Flags=Data->Arc.NewMhd.Flags;
      Array<byte> CmtData;
      if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtData,NULL))
      {
        r->Flags|=2;
        size_t Size=CmtData.Size()+1;
        r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
        r->CmtSize=(uint)Min(Size,r->CmtBufSize);
        memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
        if (Size<=r->CmtBufSize)
          r->CmtBuf[r->CmtSize-1]=0;
      }
      else
        r->CmtState=r->CmtSize=0;
      if (Data->Arc.Signed)
        r->Flags|=0x20;
      Data->Extract.ExtractArchiveInit(&Data->Cmd,Data->Arc);
      return((HANDLE)Data);
    }
    catch (int ErrCode)
    {
      r->OpenResult=RarErrorToDll(ErrCode);
      return(NULL);
    }
  }
  
  
  int PASCAL RARCloseArchive(HANDLE hArcData)
  {
    DataSet *Data=(DataSet *)hArcData;
    bool Success=Data==NULL ? false:Data->Arc.Close();
    delete Data;
    return(Success ? 0:ERAR_ECLOSE);
  }
  
  
  int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
  {
    DataSet *Data=(DataSet *)hArcData;
    try
    {
      if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(FILE_HEAD))<=0)
      {
        if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD &&
            (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME))
          if (MergeArchive(Data->Arc,NULL,false,'L'))
          {
            Data->Extract.SignatureFound=false;
            Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
            return(RARReadHeader(hArcData,D));
          }
          else
            return(ERAR_EOPEN);
        return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE);
      }
      if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE))
      {
        int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
        if (Code==0)
          return(RARReadHeader(hArcData,D));
        else
          return(Code);
      }
      strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName));
      strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName));
      D->Flags=Data->Arc.NewLhd.Flags;
      D->PackSize=Data->Arc.NewLhd.PackSize;
      D->UnpSize=Data->Arc.NewLhd.UnpSize;
      D->HostOS=Data->Arc.NewLhd.HostOS;
      D->FileCRC=Data->Arc.NewLhd.FileCRC;
      D->FileTime=Data->Arc.NewLhd.FileTime;
      D->UnpVer=Data->Arc.NewLhd.UnpVer;
      D->Method=Data->Arc.NewLhd.Method;
      D->FileAttr=Data->Arc.NewLhd.FileAttr;
      D->CmtSize=0;
      D->CmtState=0;
    }
    catch (int ErrCode)
    {
      return(RarErrorToDll(ErrCode));
    }
    return(0);
  }
  
  
  int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
  {
    DataSet *Data=(DataSet *)hArcData;
    try
    {
      if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(FILE_HEAD))<=0)
      {
        if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD &&
            (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME))
          if (MergeArchive(Data->Arc,NULL,false,'L'))
          {
            Data->Extract.SignatureFound=false;
            Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
            return(RARReadHeaderEx(hArcData,D));
          }
          else
            return(ERAR_EOPEN);
        return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE);
      }
      if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE))
      {
        int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
        if (Code==0)
          return(RARReadHeaderEx(hArcData,D));
        else
          return(Code);
      }
      strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName));
      if (*Data->Arc.FileNameW)
        wcsncpy(D->ArcNameW,Data->Arc.FileNameW,ASIZE(D->ArcNameW));
      else
        CharToWide(Data->Arc.FileName,D->ArcNameW);
      strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName));
      if (*Data->Arc.NewLhd.FileNameW)
        wcsncpy(D->FileNameW,Data->Arc.NewLhd.FileNameW,ASIZE(D->FileNameW));
      else
      {
  #ifdef _WIN_ALL
        char AnsiName[NM];
        OemToCharA(Data->Arc.NewLhd.FileName,AnsiName);
        if (!CharToWide(AnsiName,D->FileNameW,ASIZE(D->FileNameW)))
          *D->FileNameW=0;
  #else
        if (!CharToWide(Data->Arc.NewLhd.FileName,D->FileNameW,ASIZE(D->FileNameW)))
          *D->FileNameW=0;
  #endif
      }
      D->Flags=Data->Arc.NewLhd.Flags;
      D->PackSize=Data->Arc.NewLhd.PackSize;
      D->PackSizeHigh=Data->Arc.NewLhd.HighPackSize;
      D->UnpSize=Data->Arc.NewLhd.UnpSize;
      D->UnpSizeHigh=Data->Arc.NewLhd.HighUnpSize;
      D->HostOS=Data->Arc.NewLhd.HostOS;
      D->FileCRC=Data->Arc.NewLhd.FileCRC;
      D->FileTime=Data->Arc.NewLhd.FileTime;
      D->UnpVer=Data->Arc.NewLhd.UnpVer;
      D->Method=Data->Arc.NewLhd.Method;
      D->FileAttr=Data->Arc.NewLhd.FileAttr;
      D->CmtSize=0;
      D->CmtState=0;
  	/* these four next lines were added by me */
  	Data->Arc.NewLhd.mtime.GetLocal((RarLocalTime *) &D->mtime);
  	Data->Arc.NewLhd.ctime.GetLocal((RarLocalTime *) &D->ctime);
  	Data->Arc.NewLhd.atime.GetLocal((RarLocalTime *) &D->atime);
  	Data->Arc.NewLhd.arctime.GetLocal((RarLocalTime *) &D->arctime);
    }
    catch (int ErrCode)
    {
      return(RarErrorToDll(ErrCode));
    }
    return(0);
  }
  
  
  int PASCAL ProcessFile(HANDLE hArcData, int Operation, char *DestPath,
                         char *DestName, wchar *DestPathW, wchar *DestNameW,
                         void *Buffer, size_t BufferSize, size_t *ReadSize,
                         bool InitDataIO, int *finished)
  {
    DataSet *Data=(DataSet *)hArcData;
  
    /* if not extracting chunks, we want to init IO all the time
     * (that was the behaviour before adding RAR_EXTRACT_CHUNK, which thus
     * remains unaltered) */
    if (Operation != RAR_EXTRACT_CHUNK)
      InitDataIO = TRUE;
  
    /* we must set these here because the function may return before executing the
     * code that updates the variables. */
    if (ReadSize != NULL)
      *ReadSize = 0;
    if (finished != NULL)
  	  *finished = TRUE;
  
    try
    {
      Data->Cmd.DllError=0;
      if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT ||
          Operation==RAR_SKIP && !Data->Arc.Solid)
      {
        if (Data->Arc.Volume &&
            Data->Arc.GetHeaderType()==FILE_HEAD &&
            (Data->Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0)
          if (MergeArchive(Data->Arc,NULL,false,'L'))
          {
            Data->Extract.SignatureFound=false;
            Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
            return(0);
          }
          else
            return(ERAR_EOPEN);
        Data->Arc.SeekToNext();
      }
      else
      {
        Data->Cmd.DllOpMode=Operation;
  
        if (DestPath!=NULL || DestName!=NULL)
        {
  #ifdef _WIN_ALL
          OemToCharA(NullToEmpty(DestPath),Data->Cmd.ExtrPath);
  #else
          strcpy(Data->Cmd.ExtrPath,NullToEmpty(DestPath));
  #endif
          AddEndSlash(Data->Cmd.ExtrPath);
  #ifdef _WIN_ALL
          OemToCharA(NullToEmpty(DestName),Data->Cmd.DllDestName);
  #else
          strcpy(Data->Cmd.DllDestName,NullToEmpty(DestName));
  #endif
        }
        else
        {
          *Data->Cmd.ExtrPath=0;
          *Data->Cmd.DllDestName=0;
        }
  
        if (DestPathW!=NULL || DestNameW!=NULL)
        {
          wcsncpy(Data->Cmd.ExtrPathW,NullToEmpty(DestPathW),NM-2);
          AddEndSlash(Data->Cmd.ExtrPathW);
          wcsncpy(Data->Cmd.DllDestNameW,NullToEmpty(DestNameW),NM-1);
  
          if (*Data->Cmd.DllDestNameW!=0 && *Data->Cmd.DllDestName==0)
            WideToChar(Data->Cmd.DllDestNameW,Data->Cmd.DllDestName);
        }
        else
        {
          *Data->Cmd.ExtrPathW=0;
          *Data->Cmd.DllDestNameW=0;
        }
  
        strcpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? "X":"T");
        Data->Cmd.Test=Operation!=RAR_EXTRACT;
        if (Operation == RAR_EXTRACT_CHUNK)
        {
          //disabled completion percentage calculation/printing?
          Data->Cmd.DisablePercentage = true;
          //doesn't seem to be read except for inactive preproc. blocks anyway:
          Data->Cmd.DisableDone = true;
          Data->Extract.Buffer = Buffer;
          Data->Extract.BufferSize = BufferSize;
        }
  
        bool Repeat=false;
        if (Operation != RAR_EXTRACT_CHUNK)
          Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,
            Data->HeaderSize,Repeat);
        else
        {
          if (InitDataIO) //chunk, init
          {
            bool res;
            res = Data->Extract.ExtractCurrentFileChunkInit(&Data->Cmd, Data->Arc,
              Data->HeaderSize, Repeat);
            if (!res && Data->Cmd.DllError == 0)
              Data->Cmd.DllError = ERAR_UNKNOWN;
            return Data->Cmd.DllError;
          }
          else //chunk, no init
            //returns always true
            //changes *ReadSize and *finished
            Data->Extract.ExtractCurrentFileChunk(&Data->Cmd, Data->Arc,
              ReadSize, finished);
        }
  
        /* if extracting by chunks, do move to next block, not even if we've read
         * the whole file. The only purpose of this code seems to be applying
         * permissions and other metadata to files, so we're not interested if
         * extracting chunks */
        if (Operation != RAR_EXTRACT_CHUNK) {
          while (Data->Arc.ReadHeader()!=0 && Data->Arc.GetHeaderType()==NEWSUB_HEAD)
          {
            Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat);
            Data->Arc.SeekToNext();
          }
          Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
        }
  
      }
    }
    catch (int ErrCode)
    {
      return(RarErrorToDll(ErrCode));
    }
    return(Data->Cmd.DllError);
  }
  
  
  int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName)
  {
    return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL,NULL,0,
      NULL,false,NULL));
  }
  
  
  int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName)
  {
    return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName,NULL,0,
      NULL,false,NULL));
  }
  
  int PASCAL RARProcessFileChunkInit(HANDLE hArcData)
  {
    return ProcessFile(hArcData, RAR_EXTRACT_CHUNK, NULL, NULL, NULL, NULL,
      NULL, 0, NULL, true, NULL);
  }
  
  int PASCAL RARProcessFileChunk(HANDLE hArcData,
                                 void *Buffer,
                                 size_t BufferSize,
                                 size_t *ReadSize,
                                 int *finished)
  {
    return ProcessFile(hArcData, RAR_EXTRACT_CHUNK, NULL, NULL, NULL, NULL,
      Buffer, BufferSize, ReadSize, false, finished);
  }
  
  void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc)
  {
    DataSet *Data=(DataSet *)hArcData;
    Data->Cmd.ChangeVolProc=ChangeVolProc;
  }
  
  
  void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData)
  {
    DataSet *Data=(DataSet *)hArcData;
    Data->Cmd.Callback=Callback;
    Data->Cmd.UserData=UserData;
  }
  
  //added by me
  void PASCAL RARSetProcessExtendedData(HANDLE hArcData, int value)
  {
    DataSet *Data = (DataSet *) hArcData;
    Data->Cmd.ProcessOwners = value != 0 ? true : false;
    Data->Cmd.ProcessEA = value != 0 ? true : false;
  }
  
  
  void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc)
  {
    DataSet *Data=(DataSet *)hArcData;
    Data->Cmd.ProcessDataProc=ProcessDataProc;
  }
  
  
  #ifndef RAR_NOCRYPT
  void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
  {
    DataSet *Data=(DataSet *)hArcData;
    GetWideName(Password,NULL,Data->Cmd.Password,ASIZE(Data->Cmd.Password));
  }
  #endif
  
  
  int PASCAL RARGetDllVersion()
  {
    return(RAR_DLL_VERSION);
  }
  
  
  static int RarErrorToDll(int ErrCode)
  {
    switch(ErrCode)
    {
      case FATAL_ERROR:
        return(ERAR_EREAD);
      case CRC_ERROR:
        return(ERAR_BAD_DATA);
      case WRITE_ERROR:
        return(ERAR_EWRITE);
      case OPEN_ERROR:
        return(ERAR_EOPEN);
      case CREATE_ERROR:
        return(ERAR_ECREATE);
      case MEMORY_ERROR:
        return(ERAR_NO_MEMORY);
      case NO_PASSWORD_ERROR: //not in original
        return(ERAR_MISSING_PASSWORD);
      case SUCCESS:
        return(0);
      default:
        return(ERAR_UNKNOWN);
    }
  }
  