2、lib7z-Memory从内存或网络解压数据(分析源码,修改源码)

希望你有好运气能编译上一节我提供的Dec7z的源码,如果你成功了,接下来,我就分享一下分析7z源码并修改到符合目的的过程。

首先进入

BOOL Extra7zFileToPath(WCHAR* sTargetPath,
                       HWND hwnd,
                       BOOL bUpdate,
                       char* pAllData,
                       DWORD iLength)

这个函数,
CFileInStream archiveStream;//包含HANDLE句柄,Read和Seek
  CLookToRead lookStream;
  CSzArEx db;//压缩包内的文件名称和大小?
  SRes res;
  ISzAlloc allocImp;//包括malloc和free函数
  ISzAlloc allocTempImp;//Win32 HeapAlloc和HeapFree
这里主要的代码我都做了注释,关键点一是这个:

 //(*函数指针Read,Seek,Skip等赋值
  FileInStream_CreateVTable(&archiveStream);//这个好办

  //这个尚未清楚
  //也是用的上面的Read和Seek,但多了p->pos的操作
  LookToRead_CreateVTable(&lookStream, False);
  //*)

通过分析7z解压的源码,就知道7z解压的最后手段无非就是fopen,fwrite在Windows系统下用宏判断定义了,读取和Seek函数,

无非就是CreateFile(打开文件),ReadFile(读物数据到内存 ),SetFilePointer(设置文件指针),CloseHandle(关闭打开的文件句柄)。

7z用了结构体的面向技术,最关键的两个是

typedef struct
{
  ISeekInStream s;
  CSzFile file;
} CFileInStream;
typedef struct
{
  SRes (*Read)(void *p, void *buf, size_t *size);  /* same as ISeqInStream::Read */
  SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
} ISeekInStream;
最关键的读取和移动文件指针函数是 

WRes File_Read(CSzFile *p, void *data, size_t *size);

/* writes *size bytes */
WRes File_Write(CSzFile *p, const void *data, size_t *size);

WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);
WRes File_GetLength(CSzFile *p, UInt64 *length);

其中ISeekInStream的Read函数指针通过分析正指向File_Read函数,而File_Read函数正调用了Win32的ReadFile函数,Seek函数指针对应Win32的SetFilePointer,7z这样做非常高明,这样7z的结构体不但有数据成员,同时也通过函数指针拥有了函数成员,达到了和c++相同的面向对象的功能,又通过宏判断以适应X86,Arm等平台和编译器。

假如我们首先获取了全部数据指针char* pAllData和数据长度iLength,用memcpy代替File_Read,用自动位置的代替File_Seek不就实现了不用CreateFileA打开文件,直接解压的目的了吗? 这个想法理论上是成立的,我在修改中也遇到了不少问题。首先ReadFile函数用memcpy来代替是可以的,但ReadFile不但读取了数据,也同时改变了HANDLE文件的位置(Seek的偏移),所以需要一个全局的位置,记住文件的偏移。我建立了func.h和func.c文件,里面定义了一些全局变量和修改过的函数。

#ifndef FUNC_H
#define FUNC_H

#ifndef kChunkSizeMax
    #define kChunkSizeMax (1 << 22)
#endif

#include "7zFile.h"
#include "Types.h"

extern char* g_pAllData;
extern DWORD g_iAllDataLength;
extern DWORD g_iSeekPos;
extern HANDLE* g_handle;

void g_func_GetLowAndHeightFromDWORD(DWORD v, DWORD* iLow, LONG *iHeight);

UInt64 g_func_GetDWORDByLowAndHight(DWORD sizeLow,DWORD sizeHigh);

WRes g_func_File_Close(CSzFile *p);

WRes g_func_File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);

WRes g_func_File_Read(CSzFile *p,
                      void *data,
                      size_t *size);

#endif // FUNC_H

最关键的是这两个

WRes g_func_File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);

WRes g_func_File_Read(CSzFile *p,
                      void *data,
                      size_t *size);
我们要用这两个函数分别替换File_Seek和File_Read函数,才能达到去除文件HANDLE,内存直接解压的目的。g_ISeekPos是全局的文件指针偏移

我是先拿File_Read函数开刀,下面是关键的函数替换修改点(7zFile.c):

static SRes FileInStream_Read(void *pp, void *buf, size_t *size)
{
  CFileInStream *p = (CFileInStream *)pp;
  //return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;
  return (g_func_File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;
  //return SZ_OK;
}

static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin)
{
  CFileInStream *p = (CFileInStream *)pp;
  //return File_Seek(&p->file, pos, origin);
  return g_func_File_Seek(&p->file, pos, origin);
}
这样,7z真实的Read和Seek函数便到了我们控制的函数里。

WRes g_func_File_Read(CSzFile *p,
                      void *data,
                      size_t *size)
{
    size_t originalSize = *size;
    if (originalSize == 0)
      return 0;

    *size = 0;
    do
    {
      DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
      DWORD processed = 0;
      DWORD iCurrentPos = 0;
      UInt64 iSeekPos = 0;
      //BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL);

      //获取当前文件指针位置
      iCurrentPos = SetFilePointer(*g_handle,NULL, NULL, FILE_CURRENT);

      //memcpy(data,g_pAllData+iCurrentPos,curSize);
      memcpy(data,g_pAllData+g_iSeekPos,curSize);
      processed = curSize;

      data = (void *)((Byte *)data + processed);
      originalSize -= processed;
      *size += processed;

      iSeekPos = (UInt64)processed;

      g_func_File_Seek(p,&iSeekPos,SZ_SEEK_CUR);

      if (processed == 0)
        break;
    }
    while (originalSize > 0);
    return 0;
}

真实的Read函数里直接用memcpy代替ReadFile函数,实现内存拷贝功能
WRes g_func_File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin)
{

    LARGE_INTEGER value;
    DWORD moveMethod;
    DWORD iCurrentPos = 0;
//    Int64 zzz;
    value.LowPart = (DWORD)*pos;
    value.HighPart = (LONG)((UInt64)*pos >> 16 >> 16);

//    zzz = *pos;
    switch (origin)
    {
      case SZ_SEEK_SET: moveMethod = FILE_BEGIN;g_iSeekPos=(DWORD)(*pos);break;
      case SZ_SEEK_CUR: moveMethod = FILE_CURRENT;g_iSeekPos+=(DWORD)(*pos);break;
      case SZ_SEEK_END: moveMethod = FILE_END;g_iSeekPos=g_iAllDataLength;break;
      default: return ERROR_INVALID_PARAMETER;
    }


    //value.LowPart = SetFilePointer(p->handle, value.LowPart, &value.HighPart, moveMethod);

    //iCurrentPos = SetFilePointer(*g_handle,NULL, NULL, FILE_CURRENT);

    //printf("XXOK--iCurrentPos:%d--g_iSeekPos:%d\n",iCurrentPos,g_iSeekPos);
    //g_iSeekPos = iCurrentPos;

//    if (value.LowPart == 0xFFFFFFFF)
//    {
//      WRes res = GetLastError();
//     if (res != NO_ERROR)
//        return res;
//    }

    *pos = g_iSeekPos;
    //*pos = ((Int64)value.HighPart << 32) | value.LowPart;

    //printf("--zzz:%d--*pos:%d\n",zzz,*pos);
    return 0;
}
真实的Seek函数直接用g_iSeekPos代替SetFilePointer,注意读取文件数据到内存的同时,文件偏移也变了,所以在

WRes g_func_File_Read(CSzFile *p,
                      void *data,
                      size_t *size)
里有这么一句



而对于获取文件大小的File_GetLength函数,则直接赋值即可。

WRes File_GetLength(CSzFile *p, UInt64 *length)
{
  #ifdef USE_WINDOWS_FILE

  *length = (UInt64)g_iAllDataLength;
  return 0;
/*
  DWORD sizeHigh;
  DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
  if (sizeLow == 0xFFFFFFFF)
  {
    DWORD res = GetLastError();
    if (res != NO_ERROR)
      return res;
  }
  *length = (((UInt64)sizeHigh) << 32) + sizeLow;
  return 0;*/
  
  #else
  
  long pos = ftell(p->file);
  int res = fseek(p->file, 0, SEEK_END);
  *length = ftell(p->file);
  fseek(p->file, pos, SEEK_SET);
  return res;
  
  #endif
}

通过这节的大刀阔斧的修改、增加源码,lib7z解压原理已经很清楚了,修改也已完毕,下一节就让我们试试吧。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值