手把手实现andriod应用增量升级

转载地址:http://blog.csdn.net/mockingbirds/article/details/47701635?utm_source=tuicool

最近研究了android应用增量升级的应用,其中用到了android NDK编程,先说下为什么要使用增量升级,当我们的应用达到一定大小的时候,比如目前有30M,如果新版本35M只是增加了几个功能,而之前都保持不变,那么这个时候我们可以生成一个差分包,这个差分包一般就6M左右大小,大大的为用户节省了流量。

增量升级原理

android增量升级,首先在服务器端生成差分包,然后用户下载差分包,在手机端,将该差分包和旧版本的apk合成为新的版本。

使用到的开源项目

bsdiff :是一个二进制差分工具, 用于生成差分包

bspatch :是一个开源的合成工具,android通过ndk将其编译为so文件然后调用其代码即可实现:差分包和旧版本apk的合成。

另外还需要下载:bzip2-1.0.6

http://www.bzip.org/downloads.html

增量升级的实现

increaseone1.0.apk

在正式实现之前,当然是先准备我们的apk了,这里我编写一个简单的工程,运行效果如下:

将该该工程bin目录下生成的apk拷贝到”D:\increase”目录下,并且重新命名为”increaseone1.0.apk”

increaseone2.0.apk

现在添加一个activity,并且在该工程mainactivity中添加一个button可以跳转到该activity,主要是为了区别。此时运行后的效果如下:

同样将bin目录下的最新的apk文件拷贝至”d:\increase”目录下,重命名为”increaseone2.0.apk”

生成差分包

准备好不同版本的apk后,就需要生成差分包,这里一般是在服务端来实现的,这里用到了”bsdiff4.3-win32”工具:

进入命令行下输入如下命令:”bsdiff.exe 旧版本的apk路径 新版本的apk路径 生成的差分包的路径”,如下图:

此时在”d:\increase”目录下回生成一个increase.patch的差分包。

手机端实现patch合成

创建android工程

新建一个android工程selfincrease新建类:”PatchUpdate.java”用来调用底层的c代码实现patch的合成。

package com.example.selfincrease;

public class PatchUpdate {
    public  native  int patch(String oldApkPath,    String  newApkPath, String  patchPath);
}

调用javac和javah生成头文件:

详细的步骤可以参考 一步一步学习androidNDK编程(hello world)

创建jni实现c代码

在该android工程目录下,新建一个jni目录,将中的如下文件拷贝到该目录下:

blocksort.c

bzip2.c

bzip2recover.c

bzlib_private.h

bzlib.c

bzlib.h

com_example_selfincrease_PatchUpdate.h

compress.c

crctable.c

decompress.c

dlltest.c

huffman.c

mk251.c

randtable.c

spewG.c

unzcrash.c

期中”com_example_selfincrease_PatchUpdate.h”是刚才生成的头文件。

在jni目录下新建”com_example_selfincrease_PatchUpdate.c”文件,内容如下:

#include <stdio.h>
#include "bzlib_private.h"
#include "bzlib.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
#include <android/log.h>
#include <jni.h>
#include "com_example_selfincrease_PatchUpdate.h"
/*---------------------------------------------------*/
void BZ2_hbCreateDecodeTables ( Int32 *limit,
                      Int32 *base,
                      Int32 *perm,
                      UChar *length,
                      Int32 minLen,
                      Int32 maxLen,
                      Int32 alphaSize )
{
  Int32 pp, i, j, vec;
  pp = 0;
  for (i = minLen; i <= maxLen; i++)
    for (j = 0; j < alphaSize; j++)
      if (length[j] == i) { perm[pp] = j; pp++; };
  for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
  for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
  for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];
  for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
  vec = 0;
  for (i = minLen; i <= maxLen; i++) {
    vec += (base[i+1] - base[i]);
    limit[i] = vec-1;
    vec <<= 1;
  }
  for (i = minLen + 1; i <= maxLen; i++)
    base[i] = ((limit[i-1] + 1) << 1) - base[i];
}
static
void makeMaps_d ( DState* s )
{
  Int32 i;
  s->nInUse = 0;
  for (i = 0; i < 256; i++)
    if (s->inUse[i]) {
      s->seqToUnseq[s->nInUse] = i;
      s->nInUse++;
    }
}
/*---------------------------------------------------*/
#define RETURN(rrr)										 \
  { retVal = rrr; goto save_state_and_return; };
#define GET_BITS(lll,vvv,nnn)							\
{																 \
  case lll: s->state = lll;							 \
  while (True) {											\
    if (s->bsLive >= nnn) {							\
      UInt32 v;										  \
      v = (s->bsBuff >>								\
         (s->bsLive-nnn)) & ((1 << nnn)-1);	\
      s->bsLive -= nnn;								\
      vvv = v;											\
      break;											  \
    }														 \
    if (s->strm->avail_in == 0) RETURN(BZ_OK);  \
    s->bsBuff											  \
      = (s->bsBuff << 8) |							\
        ((UInt32)										\
          (*((UChar*)(s->strm->next_in))));	\
    s->bsLive += 8;									  \
    s->strm->next_in++;								 \
    s->strm->avail_in--;								\
    s->strm->total_in_lo32++;						 \
    if (s->strm->total_in_lo32 == 0)				\
      s->strm->total_in_hi32++;					 \
  }															 \
}
#define GET_UCHAR(lll,uuu)								\
  GET_BITS(lll,uuu,8)
#define GET_BIT(lll,uuu)								  \
  GET_BITS(lll,uuu,1)
/*---------------------------------------------------*/
#define GET_MTF_VAL(label1,label2,lval)			  \
{																 \
  if (groupPos == 0) {									\
    groupNo++;											 \
    if (groupNo >= nSelectors)						\
      RETURN(BZ_DATA_ERROR);						 \
    groupPos = BZ_G_SIZE;							  \
    gSel = s->selector[groupNo];					 \
    gMinlen = s->minLens[gSel];					  \
    gLimit = &(s->limit[gSel][0]);				  \
    gPerm = &(s->perm[gSel][0]);					 \
    gBase = &(s->base[gSel][0]);					 \
  }															 \
  groupPos--;												\
  zn = gMinlen;											 \
  GET_BITS(label1, zvec, zn);						  \
  while (1) {												\
    if (zn > 20 /* the longest code */)			\
      RETURN(BZ_DATA_ERROR);						 \
    if (zvec <= gLimit[zn]) break;				  \
    zn++;													\
    GET_BIT(label2, zj);								\
    zvec = (zvec << 1) | zj;						  \
  };															\
  if (zvec - gBase[zn] < 0							  \
     || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
    RETURN(BZ_DATA_ERROR);							 \
  lval = gPerm[zvec - gBase[zn]];					 \
}
//#define BZ_OK					 0
Int32 BZ2_rNums[512] = {
  619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
  985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
  733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
  419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
  878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
  862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
  150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
  170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
  73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
  909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
  641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
  161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
  382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
  98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
  227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
  469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
  184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
  715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
  951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
  652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
  645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
  609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
  653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
  411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
  170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
  857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
  669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
  944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
  344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
  897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
  433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
  686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
  946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
  978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
  680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
  707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
  297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
  134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
  343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
  140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
  170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
  369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
  804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
  896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
  661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
  768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
  61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
  372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
  780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
  920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
  645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
  936, 638
};
Int32 BZ2_decompress ( DState* s )
{
  UChar		uc;
  Int32		retVal;
  Int32		minLen, maxLen;
  bz_stream* strm = s->strm;
  /* stuff that needs to be saved/restored */
  Int32  i;
  Int32  j;
  Int32  t;
  Int32  alphaSize;
  Int32  nGroups;
  Int32  nSelectors;
  Int32  EOB;
  Int32  groupNo;
  Int32  groupPos;
  Int32  nextSym;
  Int32  nblockMAX;
  Int32  nblock;
  Int32  es;
  Int32  N;
  Int32  curr;
  Int32  zt;
  Int32  zn;
  Int32  zvec;
  Int32  zj;
  Int32  gSel;
  Int32  gMinlen;
  Int32* gLimit;
  Int32* gBase;
  Int32* gPerm;
  if (s->state == BZ_X_MAGIC_1) {
    /*initialise the save area*/
    s->save_i			  = 0;
    s->save_j			  = 0;
    s->save_t			  = 0;
    s->save_alphaSize	= 0;
    s->save_nGroups	  = 0;
    s->save_nSelectors  = 0;
    s->save_EOB			= 0;
    s->save_groupNo	  = 0;
    s->save_groupPos	 = 0;
    s->save_nextSym	  = 0;
    s->save_nblockMAX	= 0;
    s->save_nblock		= 0;
    s->save_es			 = 0;
    s->save_N			  = 0;
    s->save_curr		  = 0;
    s->save_zt			 = 0;
    s->save_zn			 = 0;
    s->save_zvec		  = 0;
    s->save_zj			 = 0;
    s->save_gSel		  = 0;
    s->save_gMinlen	  = 0;
    s->save_gLimit		= NULL;
    s->save_gBase		 = NULL;
    s->save_gPerm		 = NULL;
  }
  /*restore from the save area*/
  i			  = s->save_i;
  j			  = s->save_j;
  t			  = s->save_t;
  alphaSize	= s->save_alphaSize;
  nGroups	  = s->save_nGroups;
  nSelectors  = s->save_nSelectors;
  EOB			= s->save_EOB;
  groupNo	  = s->save_groupNo;
  groupPos	 = s->save_groupPos;
  nextSym	  = s->save_nextSym;
  nblockMAX	= s->save_nblockMAX;
  nblock		= s->save_nblock;
  es			 = s->save_es;
  N			  = s->save_N;
  curr		  = s->save_curr;
  zt			 = s->save_zt;
  zn			 = s->save_zn;
  zvec		  = s->save_zvec;
  zj			 = s->save_zj;
  gSel		  = s->save_gSel;
  gMinlen	  = s->save_gMinlen;
  gLimit		= s->save_gLimit;
  gBase		 = s->save_gBase;
  gPerm		 = s->save_gPerm;
  retVal = BZ_OK;
  switch (s->state) {
    GET_UCHAR(BZ_X_MAGIC_1, uc);
    if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);
    GET_UCHAR(BZ_X_MAGIC_2, uc);
    if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);
    GET_UCHAR(BZ_X_MAGIC_3, uc);
    if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);
    GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8);
    if (s->blockSize100k < (BZ_HDR_0 + 1) ||
       s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
    s->blockSize100k -= BZ_HDR_0;
    if (s->smallDecompress) {
      s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
      s->ll4  = BZALLOC(
               ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar)
             );
      if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
    } else {
      s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
      if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
    }
    GET_UCHAR(BZ_X_BLKHDR_1, uc);
    if (uc == 0x17) goto endhdr_2;
    if (uc != 0x31) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_2, uc);
    if (uc != 0x41) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_3, uc);
    if (uc != 0x59) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_4, uc);
    if (uc != 0x26) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_5, uc);
    if (uc != 0x53) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_6, uc);
    if (uc != 0x59) RETURN(BZ_DATA_ERROR);
    s->currBlockNo++;
    if (s->verbosity >= 2)
      VPrintf1 ( "\n	 [%d: huff+mtf ", s->currBlockNo );
    s->storedBlockCRC = 0;
    GET_UCHAR(BZ_X_BCRC_1, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_BCRC_2, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_BCRC_3, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_BCRC_4, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
    s->origPtr = 0;
    GET_UCHAR(BZ_X_ORIGPTR_1, uc);
    s->origPtr = (s->origPtr << 8) | ((Int32)uc);
    GET_UCHAR(BZ_X_ORIGPTR_2, uc);
    s->origPtr = (s->origPtr << 8) | ((Int32)uc);
    GET_UCHAR(BZ_X_ORIGPTR_3, uc);
    s->origPtr = (s->origPtr << 8) | ((Int32)uc);
    if (s->origPtr < 0)
      RETURN(BZ_DATA_ERROR);
    if (s->origPtr > 10 + 100000*s->blockSize100k)
      RETURN(BZ_DATA_ERROR);
    /*--- Receive the mapping table ---*/
    for (i = 0; i < 16; i++) {
      GET_BIT(BZ_X_MAPPING_1, uc);
      if (uc == 1)
        s->inUse16[i] = True; else
        s->inUse16[i] = False;
    }
    for (i = 0; i < 256; i++) s->inUse[i] = False;
    for (i = 0; i < 16; i++)
      if (s->inUse16[i])
        for (j = 0; j < 16; j++) {
          GET_BIT(BZ_X_MAPPING_2, uc);
          if (uc == 1) s->inUse[i * 16 + j] = True;
        }
    makeMaps_d ( s );
    if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
    alphaSize = s->nInUse+2;
    /*--- Now the selectors ---*/
    GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
    if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
    GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
    if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
    for (i = 0; i < nSelectors; i++) {
      j = 0;
      while (True) {
        GET_BIT(BZ_X_SELECTOR_3, uc);
        if (uc == 0) break;
        j++;
        if (j >= nGroups) RETURN(BZ_DATA_ERROR);
      }
      s->selectorMtf[i] = j;
    }
    /*--- Undo the MTF values for the selectors. ---*/
    {
      UChar pos[BZ_N_GROUPS], tmp, v;
      for (v = 0; v < nGroups; v++) pos[v] = v;
      for (i = 0; i < nSelectors; i++) {
        v = s->selectorMtf[i];
        tmp = pos[v];
        while (v > 0) { pos[v] = pos[v-1]; v--; }
        pos[0] = tmp;
        s->selector[i] = tmp;
      }
    }
    /*--- Now the coding tables ---*/
    for (t = 0; t < nGroups; t++) {
      GET_BITS(BZ_X_CODING_1, curr, 5);
      for (i = 0; i < alphaSize; i++) {
        while (True) {
          if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
          GET_BIT(BZ_X_CODING_2, uc);
          if (uc == 0) break;
          GET_BIT(BZ_X_CODING_3, uc);
          if (uc == 0) curr++; else curr--;
        }
        s->len[t][i] = curr;
      }
    }
    /*--- Create the Huffman decoding tables ---*/
    for (t = 0; t < nGroups; t++) {
      minLen = 32;
      maxLen = 0;
      for (i = 0; i < alphaSize; i++) {
        if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
        if (s->len[t][i] < minLen) minLen = s->len[t][i];
      }
      BZ2_hbCreateDecodeTables (
        &(s->limit[t][0]),
        &(s->base[t][0]),
        &(s->perm[t][0]),
        &(s->len[t][0]),
        minLen, maxLen, alphaSize
      );
      s->minLens[t] = minLen;
    }
    /*--- Now the MTF values ---*/
    EOB		= s->nInUse+1;
    nblockMAX = 100000 * s->blockSize100k;
    groupNo  = -1;
    groupPos = 0;
    for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
    /*-- MTF init --*/
    {
      Int32 ii, jj, kk;
      kk = MTFA_SIZE-1;
      for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
          s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
          kk--;
        }
        s->mtfbase[ii] = kk + 1;
      }
    }
    /*-- end MTF init --*/
    nblock = 0;
    GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
    while (True) {
      if (nextSym == EOB) break;
      if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
        es = -1;
        N = 1;
        do {
          /* Check that N doesn't get too big, so that es doesn't
            go negative.  The maximum value that can be
            RUNA/RUNB encoded is equal to the block size (post
            the initial RLE), viz, 900k, so bounding N at 2
            million should guard against overflow without
            rejecting any legitimate inputs. */
          if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR);
          if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
          if (nextSym == BZ_RUNB) es = es + (1+1) * N;
          N = N * 2;
          GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
        }
          while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
        es++;
        uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
        s->unzftab[uc] += es;
        if (s->smallDecompress)
          while (es > 0) {
            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
            s->ll16[nblock] = (UInt16)uc;
            nblock++;
            es--;
          }
        else
          while (es > 0) {
            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
            s->tt[nblock] = (UInt32)uc;
            nblock++;
            es--;
          };
        continue;
      } else {
        if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
        /*-- uc = MTF ( nextSym-1 ) --*/
        {
          Int32 ii, jj, kk, pp, lno, off;
          UInt32 nn;
          nn = (UInt32)(nextSym - 1);
          if (nn < MTFL_SIZE) {
            /* avoid general-case expense */
            pp = s->mtfbase[0];
            uc = s->mtfa[pp+nn];
            while (nn > 3) {
              Int32 z = pp+nn;
              s->mtfa[(z)  ] = s->mtfa[(z)-1];
              s->mtfa[(z)-1] = s->mtfa[(z)-2];
              s->mtfa[(z)-2] = s->mtfa[(z)-3];
              s->mtfa[(z)-3] = s->mtfa[(z)-4];
              nn -= 4;
            }
            while (nn > 0) {
              s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--;
            };
            s->mtfa[pp] = uc;
          } else {
            /* general case */
            lno = nn / MTFL_SIZE;
            off = nn % MTFL_SIZE;
            pp = s->mtfbase[lno] + off;
            uc = s->mtfa[pp];
            while (pp > s->mtfbase[lno]) {
              s->mtfa[pp] = s->mtfa[pp-1]; pp--;
            };
            s->mtfbase[lno]++;
            while (lno > 0) {
              s->mtfbase[lno]--;
              s->mtfa[s->mtfbase[lno]]
                = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
              lno--;
            }
            s->mtfbase[0]--;
            s->mtfa[s->mtfbase[0]] = uc;
            if (s->mtfbase[0] == 0) {
              kk = MTFA_SIZE-1;
              for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
                for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
                  s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
                  kk--;
                }
                s->mtfbase[ii] = kk + 1;
              }
            }
          }
        }
        /*-- end uc = MTF ( nextSym-1 ) --*/
        s->unzftab[s->seqToUnseq[uc]]++;
        if (s->smallDecompress)
          s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
          s->tt[nblock]	= (UInt32)(s->seqToUnseq[uc]);
        nblock++;
        GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
        continue;
      }
    }
    /* Now we know what nblock is, we can do a better sanity
      check on s->origPtr.
    */
    if (s->origPtr < 0 || s->origPtr >= nblock)
      RETURN(BZ_DATA_ERROR);
    /*-- Set up cftab to facilitate generation of T^(-1) --*/
    /* Check: unzftab entries in range. */
    for (i = 0; i <= 255; i++) {
      if (s->unzftab[i] < 0 || s->unzftab[i] > nblock)
        RETURN(BZ_DATA_ERROR);
    }
    /* Actually generate cftab. */
    s->cftab[0] = 0;
    for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
    for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
    /* Check: cftab entries in range. */
    for (i = 0; i <= 256; i++) {
      if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
        /* s->cftab[i] can legitimately be == nblock */
        RETURN(BZ_DATA_ERROR);
      }
    }
    /* Check: cftab entries non-descending. */
    for (i = 1; i <= 256; i++) {
      if (s->cftab[i-1] > s->cftab[i]) {
        RETURN(BZ_DATA_ERROR);
      }
    }
    s->state_out_len = 0;
    s->state_out_ch  = 0;
    BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
    s->state = BZ_X_OUTPUT;
    if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
    if (s->smallDecompress) {
      /*-- Make a copy of cftab, used in generation of T --*/
      for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
      /*-- compute the T vector --*/
      for (i = 0; i < nblock; i++) {
        uc = (UChar)(s->ll16[i]);
        SET_LL(i, s->cftabCopy[uc]);
        s->cftabCopy[uc]++;
      }
      /*-- Compute T^(-1) by pointer reversal on T --*/
      i = s->origPtr;
      j = GET_LL(i);
      do {
        Int32 tmp = GET_LL(j);
        SET_LL(j, i);
        i = j;
        j = tmp;
      }
        while (i != s->origPtr);
      s->tPos = s->origPtr;
      s->nblock_used = 0;
      if (s->blockRandomised) {
        BZ_RAND_INIT_MASK;
        BZ_GET_SMALL(s->k0); s->nblock_used++;
        BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
      } else {
        BZ_GET_SMALL(s->k0); s->nblock_used++;
      }
    } else {
      /*-- compute the T^(-1) vector --*/
      for (i = 0; i < nblock; i++) {
        uc = (UChar)(s->tt[i] & 0xff);
        s->tt[s->cftab[uc]] |= (i << 8);
        s->cftab[uc]++;
      }
      s->tPos = s->tt[s->origPtr] >> 8;
      s->nblock_used = 0;
      if (s->blockRandomised) {
        BZ_RAND_INIT_MASK;
        BZ_GET_FAST(s->k0); s->nblock_used++;
        BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
      } else {
        BZ_GET_FAST(s->k0); s->nblock_used++;
      }
    }
    RETURN(BZ_OK);
   endhdr_2:
    GET_UCHAR(BZ_X_ENDHDR_2, uc);
    if (uc != 0x72) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_3, uc);
    if (uc != 0x45) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_4, uc);
    if (uc != 0x38) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_5, uc);
    if (uc != 0x50) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_6, uc);
    if (uc != 0x90) RETURN(BZ_DATA_ERROR);
    s->storedCombinedCRC = 0;
    GET_UCHAR(BZ_X_CCRC_1, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_CCRC_2, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_CCRC_3, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_CCRC_4, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    s->state = BZ_X_IDLE;
    RETURN(BZ_STREAM_END);
    default: AssertH ( False, 4001 );
//		default:RETURN(BZ_OK);
  }
  AssertH ( False, 4002 );
  save_state_and_return:
  s->save_i			  = i;
  s->save_j			  = j;
  s->save_t			  = t;
  s->save_alphaSize	= alphaSize;
  s->save_nGroups	  = nGroups;
  s->save_nSelectors  = nSelectors;
  s->save_EOB			= EOB;
  s->save_groupNo	  = groupNo;
  s->save_groupPos	 = groupPos;
  s->save_nextSym	  = nextSym;
  s->save_nblockMAX	= nblockMAX;
  s->save_nblock		= nblock;
  s->save_es			 = es;
  s->save_N			  = N;
  s->save_curr		  = curr;
  s->save_zt			 = zt;
  s->save_zn			 = zn;
  s->save_zvec		  = zvec;
  s->save_zj			 = zj;
  s->save_gSel		  = gSel;
  s->save_gMinlen	  = gMinlen;
  s->save_gLimit		= gLimit;
  s->save_gBase		 = gBase;
  s->save_gPerm		 = gPerm;
  return retVal;
}
#define BZ_SETERR(eee)						  \
{													  \
  if (bzerror != NULL) *bzerror = eee;	\
  if (bzf != NULL) bzf->lastErr = eee;	\
}
typedef
  struct {
    FILE*	  handle;
    Char		buf[BZ_MAX_UNUSED];
    Int32	  bufN;
    Bool		writing;
    bz_stream strm;
    Int32	  lastErr;
    Bool		initialisedOk;
  }
  bzFile;
  static
  void* default_bzalloc ( void* opaque, Int32 items, Int32 size )
  {
    void* v = malloc ( items * size );
    return v;
  }
  static
  void default_bzfree ( void* opaque, void* addr )
  {
    if (addr != NULL) free ( addr );
  }
  static Bool myfeof ( FILE* f )
  {
    Int32 c = fgetc ( f );
    if (c == EOF) return True;
    ungetc ( c, f );
    return False;
  }
  static
  int bz_config_ok ( void )
  {
    if (sizeof(int)	!= 4) return 0;
    if (sizeof(short) != 2) return 0;
    if (sizeof(char)  != 1) return 0;
    return 1;
  }
  UInt32 BZ2_crc32Table[256] = {
    /*-- Ugly, innit? --*/
    0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
    0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
    0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
    0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
    0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
    0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
    0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
    0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
    0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
    0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
    0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
    0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
    0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
    0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
    0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
    0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
    0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
    0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
    0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
    0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
    0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
    0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
    0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
    0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
    0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
    0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
    0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
    0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
    0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
    0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
    0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
    0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
    0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
    0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
    0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
    0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
    0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
    0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
    0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
    0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
    0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
    0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
    0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
    0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
    0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
    0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
    0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
    0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
    0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
    0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
    0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
    0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
    0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
    0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
    0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
    0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
    0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
    0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
    0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
    0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
    0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
    0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
    0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
    0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
  };
  const char * BZ_API(BZ2_bzlibVersion)(void)
  {
    return BZ_VERSION;
  }
  __inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab )
  {
    Int32 nb, na, mid;
    nb = 0;
    na = 256;
    do {
      mid = (nb + na) >> 1;
      if (indx >= cftab[mid]) nb = mid; else na = mid;
    }
    while (na - nb != 1);
    return nb;
  }
  static
  Bool unRLE_obuf_to_output_SMALL ( DState* s )
  {
    UChar k1;
    if (s->blockRandomised) {
      while (True) {
        /* try to finish existing run */
        while (True) {
          if (s->strm->avail_out == 0) return False;
          if (s->state_out_len == 0) break;
          *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
          BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
          s->state_out_len--;
          s->strm->next_out++;
          s->strm->avail_out--;
          s->strm->total_out_lo32++;
          if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
        }
        /* can a new run be started? */
        if (s->nblock_used == s->save_nblock+1) return False;
        /* Only caused by corrupt data stream? */
        if (s->nblock_used > s->save_nblock+1)
          return True;
        s->state_out_len = 1;
        s->state_out_ch = s->k0;
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 2;
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 3;
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        s->state_out_len = ((Int32)k1) + 4;
        BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK;
        s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
      }
    } else {
      while (True) {
        /* try to finish existing run */
        while (True) {
          if (s->strm->avail_out == 0) return False;
          if (s->state_out_len == 0) break;
          *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
          BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
          s->state_out_len--;
          s->strm->next_out++;
          s->strm->avail_out--;
          s->strm->total_out_lo32++;
          if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
        }
        /* can a new run be started? */
        if (s->nblock_used == s->save_nblock+1) return False;
        /* Only caused by corrupt data stream? */
        if (s->nblock_used > s->save_nblock+1)
          return True;
        s->state_out_len = 1;
        s->state_out_ch = s->k0;
        BZ_GET_SMALL(k1); s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 2;
        BZ_GET_SMALL(k1); s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 3;
        BZ_GET_SMALL(k1); s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        BZ_GET_SMALL(k1); s->nblock_used++;
        s->state_out_len = ((Int32)k1) + 4;
        BZ_GET_SMALL(s->k0); s->nblock_used++;
      }
    }
  }
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
   JNIEnv* env = NULL;
   jint result = -1;
   if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
   //if (vm->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "ERROR: GetEnv failed");
      goto bail;
   }
//	 assert(env != NULL);
//	 if (register_android_boa(env) < 0) {
//		__android_log_print(ANDROID_LOG_INFO,"JNIMsg", "ERROR: Boa Server native registration failed");
//		  goto bail;
//	 }
   /* success -- return valid version number */
   result = JNI_VERSION_1_4;
bail:
   return result;
}
int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm )
{
  DState* s;
  if (strm == NULL) return BZ_PARAM_ERROR;
  s = strm->state;
  if (s == NULL) return BZ_PARAM_ERROR;
  if (s->strm != strm) return BZ_PARAM_ERROR;
  if (s->tt	!= NULL) BZFREE(s->tt);
  if (s->ll16 != NULL) BZFREE(s->ll16);
  if (s->ll4  != NULL) BZFREE(s->ll4);
  BZFREE(strm->state);
  strm->state = NULL;
  return BZ_OK;
}
void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b )
{
  bzFile* bzf = (bzFile*)b;
  BZ_SETERR(BZ_OK);
  if (bzf == NULL)
    { BZ_SETERR(BZ_OK); return; };
  if (bzf->writing)
    { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
  if (bzf->initialisedOk)
    (void)BZ2_bzDecompressEnd ( &(bzf->strm) );
  free ( bzf );
}
static
Bool unRLE_obuf_to_output_FAST ( DState* s )
{
  UChar k1;
  if (s->blockRandomised) {
    while (True) {
      /* try to finish existing run */
      while (True) {
        if (s->strm->avail_out == 0) return False;
        if (s->state_out_len == 0) break;
        *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
        BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
        s->state_out_len--;
        s->strm->next_out++;
        s->strm->avail_out--;
        s->strm->total_out_lo32++;
        if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
      }
      /* can a new run be started? */
      if (s->nblock_used == s->save_nblock+1) return False;
      /* Only caused by corrupt data stream? */
      if (s->nblock_used > s->save_nblock+1)
        return True;
      s->state_out_len = 1;
      s->state_out_ch = s->k0;
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      if (s->nblock_used == s->save_nblock+1) continue;
      if (k1 != s->k0) { s->k0 = k1; continue; };
      s->state_out_len = 2;
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      if (s->nblock_used == s->save_nblock+1) continue;
      if (k1 != s->k0) { s->k0 = k1; continue; };
      s->state_out_len = 3;
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      if (s->nblock_used == s->save_nblock+1) continue;
      if (k1 != s->k0) { s->k0 = k1; continue; };
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      s->state_out_len = ((Int32)k1) + 4;
      BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK;
      s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
    }
  } else {
    /* restore */
    UInt32		  c_calculatedBlockCRC = s->calculatedBlockCRC;
    UChar			c_state_out_ch		 = s->state_out_ch;
    Int32			c_state_out_len		= s->state_out_len;
    Int32			c_nblock_used		  = s->nblock_used;
    Int32			c_k0					  = s->k0;
    UInt32*		 c_tt					  = s->tt;
    UInt32		  c_tPos					= s->tPos;
    char*			cs_next_out			 = s->strm->next_out;
    unsigned int  cs_avail_out			= s->strm->avail_out;
    Int32			ro_blockSize100k	  = s->blockSize100k;
    /* end restore */
    UInt32		 avail_out_INIT = cs_avail_out;
    Int32		  s_save_nblockPP = s->save_nblock+1;
    unsigned int total_out_lo32_old;
    while (True) {
      /* try to finish existing run */
      if (c_state_out_len > 0) {
        while (True) {
          if (cs_avail_out == 0) goto return_notr;
          if (c_state_out_len == 1) break;
          *( (UChar*)(cs_next_out) ) = c_state_out_ch;
          BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
          c_state_out_len--;
          cs_next_out++;
          cs_avail_out--;
        }
        s_state_out_len_eq_one:
        {
          if (cs_avail_out == 0) {
            c_state_out_len = 1; goto return_notr;
          };
          *( (UChar*)(cs_next_out) ) = c_state_out_ch;
          BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
          cs_next_out++;
          cs_avail_out--;
        }
      }
      /* Only caused by corrupt data stream? */
      if (c_nblock_used > s_save_nblockPP)
        return True;
      /* can a new run be started? */
      if (c_nblock_used == s_save_nblockPP) {
        c_state_out_len = 0; goto return_notr;
      };
      c_state_out_ch = c_k0;
      BZ_GET_FAST_C(k1); c_nblock_used++;
      if (k1 != c_k0) {
        c_k0 = k1; goto s_state_out_len_eq_one;
      };
      if (c_nblock_used == s_save_nblockPP)
        goto s_state_out_len_eq_one;
      c_state_out_len = 2;
      BZ_GET_FAST_C(k1); c_nblock_used++;
      if (c_nblock_used == s_save_nblockPP) continue;
      if (k1 != c_k0) { c_k0 = k1; continue; };
      c_state_out_len = 3;
      BZ_GET_FAST_C(k1); c_nblock_used++;
      if (c_nblock_used == s_save_nblockPP) continue;
      if (k1 != c_k0) { c_k0 = k1; continue; };
      BZ_GET_FAST_C(k1); c_nblock_used++;
      c_state_out_len = ((Int32)k1) + 4;
      BZ_GET_FAST_C(c_k0); c_nblock_used++;
    }
    return_notr:
    total_out_lo32_old = s->strm->total_out_lo32;
    s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out);
    if (s->strm->total_out_lo32 < total_out_lo32_old)
      s->strm->total_out_hi32++;
    /* save */
    s->calculatedBlockCRC = c_calculatedBlockCRC;
    s->state_out_ch		 = c_state_out_ch;
    s->state_out_len		= c_state_out_len;
    s->nblock_used		  = c_nblock_used;
    s->k0					  = c_k0;
    s->tt					  = c_tt;
    s->tPos					= c_tPos;
    s->strm->next_out	  = cs_next_out;
    s->strm->avail_out	 = cs_avail_out;
    /* end save */
  }
  return False;
}
void BZ2_bz__AssertH__fail ( int errcode )
{
  fprintf(stderr,
    "\n\nbzip2/libbzip2: internal error number %d.\n"
    "This is a bug in bzip2/libbzip2, %s.\n"
    "Please report it to me at: jseward@bzip.org.  If this happened\n"
    "when you were using some program which uses libbzip2 as a\n"
    "component, you should also report this bug to the author(s)\n"
    "of that program.  Please make an effort to report this bug;\n"
    "timely and accurate bug reports eventually lead to higher\n"
    "quality software.  Thanks.  Julian Seward, 10 December 2007.\n\n",
    errcode,
    BZ2_bzlibVersion()
  );
  if (errcode == 1007) {
  fprintf(stderr,
    "\n*** A special note about internal error number 1007 ***\n"
    "\n"
    "Experience suggests that a common cause of i.e. 1007\n"
    "is unreliable memory or other hardware.  The 1007 assertion\n"
    "just happens to cross-check the results of huge numbers of\n"
    "memory reads/writes, and so acts (unintendedly) as a stress\n"
    "test of your memory system.\n"
    "\n"
    "I suggest the following: try compressing the file again,\n"
    "possibly monitoring progress in detail with the -vv flag.\n"
    "\n"
    "* If the error cannot be reproduced, and/or happens at different\n"
    "  points in compression, you may have a flaky memory system.\n"
    "  Try a memory-test program.  I have used Memtest86\n"
    "  (www.memtest86.com).  At the time of writing it is free (GPLd).\n"
    "  Memtest86 tests memory much more thorougly than your BIOSs\n"
    "  power-on test, and may find failures that the BIOS doesn't.\n"
    "\n"
    "* If the error can be repeatably reproduced, this is a bug in\n"
    "  bzip2, and I would very much like to hear about it.  Please\n"
    "  let me know, and, ideally, save a copy of the file causing the\n"
    "  problem -- without which I will be unable to investigate it.\n"
    "\n"
  );
  }
  exit(3);
}
int BZ_API(BZ2_bzDecompress) ( bz_stream *strm )
{
  Bool	 corrupt;
  DState* s;
  if (strm == NULL) return BZ_PARAM_ERROR;
  s = strm->state;
  if (s == NULL) return BZ_PARAM_ERROR;
  if (s->strm != strm) return BZ_PARAM_ERROR;
  while (True) {
    if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR;
    if (s->state == BZ_X_OUTPUT) {
      if (s->smallDecompress)
        corrupt = unRLE_obuf_to_output_SMALL ( s ); else
        corrupt = unRLE_obuf_to_output_FAST  ( s );
      if (corrupt) return BZ_DATA_ERROR;
      if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) {
        BZ_FINALISE_CRC ( s->calculatedBlockCRC );
        if (s->verbosity >= 3)
          VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC,
                  s->calculatedBlockCRC );
        if (s->verbosity >= 2) VPrintf0 ( "]" );
        if (s->calculatedBlockCRC != s->storedBlockCRC)
          return BZ_DATA_ERROR;
        s->calculatedCombinedCRC
          = (s->calculatedCombinedCRC << 1) |
              (s->calculatedCombinedCRC >> 31);
        s->calculatedCombinedCRC ^= s->calculatedBlockCRC;
        s->state = BZ_X_BLKHDR_1;
      } else {
        return BZ_OK;
      }
    }
    if (s->state >= BZ_X_MAGIC_1) {
      Int32 r = BZ2_decompress ( s );
      if (r == BZ_STREAM_END) {
        if (s->verbosity >= 3)
          VPrintf2 ( "\n	 combined CRCs: stored = 0x%08x, computed = 0x%08x",
                  s->storedCombinedCRC, s->calculatedCombinedCRC );
        if (s->calculatedCombinedCRC != s->storedCombinedCRC)
          return BZ_DATA_ERROR;
        return r;
      }
      if (s->state != BZ_X_OUTPUT) return r;
    }
  }
  AssertH ( 0, 6001 );
  return 0;  /*NOTREACHED*/
}
int BZ_API(BZ2_bzDecompressInit)
              ( bz_stream* strm,
                int		  verbosity,
                int		  small )
{
  DState* s;
  if (!bz_config_ok()) return BZ_CONFIG_ERROR;
  if (strm == NULL) return BZ_PARAM_ERROR;
  if (small != 0 && small != 1) return BZ_PARAM_ERROR;
  if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR;
  if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
  if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
  s = BZALLOC( sizeof(DState) );
  if (s == NULL) return BZ_MEM_ERROR;
  s->strm						= strm;
  strm->state				  = s;
  s->state					  = BZ_X_MAGIC_1;
  s->bsLive					 = 0;
  s->bsBuff					 = 0;
  s->calculatedCombinedCRC = 0;
  strm->total_in_lo32		= 0;
  strm->total_in_hi32		= 0;
  strm->total_out_lo32	  = 0;
  strm->total_out_hi32	  = 0;
  s->smallDecompress		 = (Bool)small;
  s->ll4						 = NULL;
  s->ll16						= NULL;
  s->tt						  = NULL;
  s->currBlockNo			  = 0;
  s->verbosity				 = verbosity;
  return BZ_OK;
}
int BZ_API(BZ2_bzRead)
        ( int*	 bzerror,
         BZFILE* b,
         void*	buf,
         int	  len )
{
  Int32	n, ret;
  bzFile* bzf = (bzFile*)b;
  BZ_SETERR(BZ_OK);
  if (bzf == NULL || buf == NULL || len < 0)
    { BZ_SETERR(BZ_PARAM_ERROR); return 0; };
  if (bzf->writing)
    { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; };
  if (len == 0)
    { BZ_SETERR(BZ_OK); return 0; };
  bzf->strm.avail_out = len;
  bzf->strm.next_out = buf;
  while (1) {
    if (ferror(bzf->handle))
      { BZ_SETERR(BZ_IO_ERROR); return 0; };
    if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) {
      n = fread ( bzf->buf, sizeof(UChar),
              BZ_MAX_UNUSED, bzf->handle );
      if (ferror(bzf->handle))
        { BZ_SETERR(BZ_IO_ERROR); return 0; };
      bzf->bufN = n;
      bzf->strm.avail_in = bzf->bufN;
      bzf->strm.next_in = bzf->buf;
    }
    ret = BZ2_bzDecompress ( &(bzf->strm) );
    if (ret != BZ_OK && ret != BZ_STREAM_END)
      { BZ_SETERR(ret); return 0; };
    if (ret == BZ_OK && myfeof(bzf->handle) &&
       bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0)
      { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; };
    if (ret == BZ_STREAM_END)
      { BZ_SETERR(BZ_STREAM_END);
        return len - bzf->strm.avail_out; };
    if (bzf->strm.avail_out == 0)
      { BZ_SETERR(BZ_OK); return len; };
  }
  return -1; /*not reached*/
}
BZFILE* BZ_API(BZ2_bzReadOpen)
             ( int*  bzerror,
              FILE* f,
              int	verbosity,
              int	small,
              void* unused,
              int	nUnused )
{
  bzFile* bzf = NULL;
  int	  ret;
  BZ_SETERR(BZ_OK);
  if (f == NULL ||
     (small != 0 && small != 1) ||
     (verbosity < 0 || verbosity > 4) ||
     (unused == NULL && nUnused != 0) ||
     (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED)))
    { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
  if (ferror(f))
    { BZ_SETERR(BZ_IO_ERROR); return NULL; };
  bzf = malloc ( sizeof(bzFile) );
  if (bzf == NULL)
    { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
  BZ_SETERR(BZ_OK);
  bzf->initialisedOk = False;
  bzf->handle		  = f;
  bzf->bufN			 = 0;
  bzf->writing		 = False;
  bzf->strm.bzalloc  = NULL;
  bzf->strm.bzfree	= NULL;
  bzf->strm.opaque	= NULL;
  while (nUnused > 0) {
    bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++;
    unused = ((void*)( 1 + ((UChar*)(unused))  ));
    nUnused--;
  }
  ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small );
  if (ret != BZ_OK)
    { BZ_SETERR(ret); free(bzf); return NULL; };
  bzf->strm.avail_in = bzf->bufN;
  bzf->strm.next_in  = bzf->buf;
  bzf->initialisedOk = True;
  return bzf;
}
static off_t offtin(u_char *buf)
{
   off_t y;
   y=buf[7]&0x7F;
   y=y*256;y+=buf[6];
   y=y*256;y+=buf[5];
   y=y*256;y+=buf[4];
   y=y*256;y+=buf[3];
   y=y*256;y+=buf[2];
   y=y*256;y+=buf[1];
   y=y*256;y+=buf[0];
   if(buf[7]&0x80) y=-y;
   return y;
}
int applypatch(int argc,char * argv[])
{
   FILE * f, * cpf, * dpf, * epf;
   BZFILE * cpfbz2, * dpfbz2, * epfbz2;
   int cbz2err, dbz2err, ebz2err;
   int fd;
   ssize_t oldsize,newsize;
   ssize_t bzctrllen,bzdatalen;
   u_char header[32],buf[8];
   u_char *old, *new;
   off_t oldpos,newpos;
   off_t ctrl[3];
   off_t lenread;
   off_t i;
   /* Open patch file */
   if ((f = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   /* Read header */
   if (fread(header, 1, 32, f) < 32) {
      if (feof(f))
        errx(1, "Corrupt patch\n");
      err(1, "fread(%s)", argv[3]);
   }
   /* Check for appropriate magic */
   if (memcmp(header, "BSDIFF40", 8) != 0)
      errx(1, "Corrupt patch\n");
   /* Read lengths from header */
   bzctrllen=offtin(header+8);
   bzdatalen=offtin(header+16);
   newsize=offtin(header+24);
   if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
      errx(1,"Corrupt patch\n");
   /* Close patch file and re-open it via libbzip2 at the right places */
   if (fclose(f))
      err(1, "fclose(%s)", argv[3]);
   if ((cpf = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   if (fseeko(cpf, 32, SEEK_SET))
      err(1, "fseeko(%s, %lld)", argv[3],
        (long long)32);
   cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0);
   if ((cpfbz2) == NULL)
      errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
   if ((dpf = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
      err(1, "fseeko(%s, %lld)", argv[3],
        (long long)(32 + bzctrllen));
   if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
      errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
   if ((epf = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
      err(1, "fseeko(%s, %lld)", argv[3],
        (long long)(32 + bzctrllen + bzdatalen));
   if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
      errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
   if(((fd=open(argv[1],O_RDONLY,0))<0) ||
      ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
      ((old=malloc(oldsize+1))==NULL) ||
      (lseek(fd,0,SEEK_SET)!=0) ||
      (read(fd,old,oldsize)!=oldsize) ||
      (close(fd)==-1)) err(1,"%s",argv[1]);
   if((new=malloc(newsize+1))==NULL) err(1,NULL);
   oldpos=0;newpos=0;
   while(newpos<newsize) {
      /* Read control data */
      for(i=0;i<=2;i++) {
        lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
        if ((lenread < 8) || ((cbz2err != BZ_OK) &&
           (cbz2err != BZ_STREAM_END)))
           errx(1, "Corrupt patch\n");
        ctrl[i]=offtin(buf);
      }
      /* Sanity-check */
      if(newpos+ctrl[0]>newsize)
        errx(1,"Corrupt patch\n");
      /* Read diff string */
      lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
      if ((lenread < ctrl[0]) ||
        ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
        errx(1, "Corrupt patch\n");
      /* Add old data to diff string */
      for(i=0;i<ctrl[0];i++)
        if((oldpos+i>=0) && (oldpos+i<oldsize))
           new[newpos+i]+=old[oldpos+i];
      /* Adjust pointers */
      newpos+=ctrl[0];
      oldpos+=ctrl[0];
      /* Sanity-check */
      if(newpos+ctrl[1]>newsize)
        errx(1,"Corrupt patch\n");
      /* Read extra string */
      lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
      if ((lenread < ctrl[1]) ||
        ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
        errx(1, "Corrupt patch\n");
      /* Adjust pointers */
      newpos+=ctrl[1];
      oldpos+=ctrl[2];
   }
   /* Clean up the bzip2 reads */
   BZ2_bzReadClose(&cbz2err, cpfbz2);
   BZ2_bzReadClose(&dbz2err, dpfbz2);
   BZ2_bzReadClose(&ebz2err, epfbz2);
   if (fclose(cpf) || fclose(dpf) || fclose(epf)){
      err(1, "fclose(%s)", argv[3]);
   }
   if((fd=open(argv[2],O_CREAT|O_RDWR|O_TRUNC,0777))<0){
   }
   if(write(fd,new,newsize)!=newsize){
   }
   if(close(fd)==-1){
   }
   free(new);
   free(old);
   return 0;
}
JNIEXPORT jint JNICALL Java_com_example_selfincrease_PatchUpdate_patch(JNIEnv *env,
      jobject obj, jstring old, jstring new , jstring patch)
{
    char * ch[4];
    ch[0]="bspatch";
    ch[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));
    ch[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));
    ch[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));
    int ret=applypatch(4, ch);
    (*env)->ReleaseStringUTFChars(env,old,ch[1]);
    (*env)->ReleaseStringUTFChars(env,new,ch[2]);
    (*env)->ReleaseStringUTFChars(env,patch,ch[3]);
    //return (*env)->NewStringUTF(env,"success");
    return ret;
}
注意两点:

1. 引入”头文件”

#include “com_example_selfincrease_PatchUpdate.h”

2. 实现生命的本地方法patch

Java_com_example_selfincrease_PatchUpdate_patch

JNIEXPORT jint JNICALL Java_com_example_selfincrease_PatchUpdate_patch(JNIEnv *env,
    jobject obj, jstring old, jstring new , jstring patch)
{
  char * ch[4];
  ch[0]="bspatch";
  ch[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));
  ch[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));
  ch[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));
  int ret=applypatch(4, ch);
  (*env)->ReleaseStringUTFChars(env,old,ch[1]);
  (*env)->ReleaseStringUTFChars(env,new,ch[2]);
  (*env)->ReleaseStringUTFChars(env,patch,ch[3]);
  //return (*env)->NewStringUTF(env,"success");
  return ret;
}

编写Android.mk文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := increase
LOCAL_CXXFLAGS :=
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES := com_example_selfincrease_PatchUpdate.c
LOCAL_LDLIBS := -lz -llog
include $(BUILD_SHARED_LIBRARY)

这里我声明编译出来的模块是increase。

上层代码调用

使用loadLibrary引入模块

底层的c代码以及Android.mk文件都实现了以后,就是在上层加载了,这里我们在PatchUpdate类中加载:

static{
        System.loadLibrary("increase");
    }

可以看到这里加载的是increase模块,就是在Android.mk文件中声明的。

MainActivity中调用

在调用之前,当然需要将之前生成好的”increaseone1.0.apk”以及increase.patch文件拷贝至手机里,这里我拷贝到”storage/sdcard0/123”目录下:

MainActivity.java

package com.example.selfincrease;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;
public class MainActivity extends Activity {
  String sd  =   Environment.getExternalStorageDirectory()   +   "/123/";
  String patch   =   "increase.patch";
  String oldapk  =   "increaseone1.0.apk";
  String oldapk_filepath =   sd  +   oldapk;
  String newapk_savepath =   sd  +   "new.apk";
  String patchpath = sd  +   patch;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     PatchUpdate patchInterence  =   new PatchUpdate();
     /**
      * oldapk_filepath  旧apk的路径
      * newapk_savepath  合成的apk路径
      * patchpath	   查分包patch的路径
      */
     patchInterence.patch(oldapk_filepath, newapk_savepath, patchpath);
     Toast.makeText(MainActivity.this, "新的apk已经合成"   +   sd  +   "目录下面", Toast.LENGTH_LONG).show();
    }
}

可以看到这里主要是调用的patchInterence.patch(oldapk_filepath, newapk_savepath, patchpath);来实现patch的合成,也就是核心的工作是由底层的c代码来实现的。

使用ndk-build编译

接下来就是使用ndk-build来编译生成so文件。这里我在window下使用的是cygwine,不清楚的可以参考 一步一步学习androidNDK编程(搭建开发环境)

编译好之后,会生成一个libincrease.so文件,此时运行selfincrease工程,就会在指定目录下生成新的合成后的new.apk文件。

注意:由于需要给sdcard上写入新的apk文件,所以需要在manifest中声明一下权限:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

此时安装运行new.apk,如图:

和我们最新版本的apk运行效果是相同的,说明我们已经合成新的apk成功了。

总结

  1. 使用bsdiff4.3对新旧apk生成差分包
  2. 根据差分包合成新的apk文件。

源码下载

最近研究了android应用增量升级的应用,其中用到了android NDK编程,先说下为什么要使用增量升级,当我们的应用达到一定大小的时候,比如目前有30M,如果新版本35M只是增加了几个功能,而之前都保持不变,那么这个时候我们可以生成一个差分包,这个差分包一般就6M左右大小,大大的为用户节省了流量。

增量升级原理

android增量升级,首先在服务器端生成差分包,然后用户下载差分包,在手机端,将该差分包和旧版本的apk合成为新的版本。

使用到的开源项目

bsdiff :是一个二进制差分工具, 用于生成差分包

bspatch :是一个开源的合成工具,android通过ndk将其编译为so文件然后调用其代码即可实现:差分包和旧版本apk的合成。

另外还需要下载:bzip2-1.0.6

http://www.bzip.org/downloads.html

增量升级的实现

increaseone1.0.apk

在正式实现之前,当然是先准备我们的apk了,这里我编写一个简单的工程,运行效果如下:

将该该工程bin目录下生成的apk拷贝到”D:\increase”目录下,并且重新命名为”increaseone1.0.apk”

increaseone2.0.apk

现在添加一个activity,并且在该工程mainactivity中添加一个button可以跳转到该activity,主要是为了区别。此时运行后的效果如下:

同样将bin目录下的最新的apk文件拷贝至”d:\increase”目录下,重命名为”increaseone2.0.apk”

生成差分包

准备好不同版本的apk后,就需要生成差分包,这里一般是在服务端来实现的,这里用到了”bsdiff4.3-win32”工具:

进入命令行下输入如下命令:”bsdiff.exe 旧版本的apk路径 新版本的apk路径 生成的差分包的路径”,如下图:

此时在”d:\increase”目录下回生成一个increase.patch的差分包。

手机端实现patch合成

创建android工程

新建一个android工程selfincrease新建类:”PatchUpdate.java”用来调用底层的c代码实现patch的合成。

package com.example.selfincrease;

public class PatchUpdate {
    public  native  int patch(String oldApkPath,    String  newApkPath, String  patchPath);
}

调用javac和javah生成头文件:

详细的步骤可以参考 一步一步学习androidNDK编程(hello world)

创建jni实现c代码

在该android工程目录下,新建一个jni目录,将中的如下文件拷贝到该目录下:

blocksort.c

bzip2.c

bzip2recover.c

bzlib_private.h

bzlib.c

bzlib.h

com_example_selfincrease_PatchUpdate.h

compress.c

crctable.c

decompress.c

dlltest.c

huffman.c

mk251.c

randtable.c

spewG.c

unzcrash.c

期中”com_example_selfincrease_PatchUpdate.h”是刚才生成的头文件。

在jni目录下新建”com_example_selfincrease_PatchUpdate.c”文件,内容如下:

#include <stdio.h>
#include "bzlib_private.h"
#include "bzlib.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
#include <android/log.h>
#include <jni.h>
#include "com_example_selfincrease_PatchUpdate.h"
/*---------------------------------------------------*/
void BZ2_hbCreateDecodeTables ( Int32 *limit,
                      Int32 *base,
                      Int32 *perm,
                      UChar *length,
                      Int32 minLen,
                      Int32 maxLen,
                      Int32 alphaSize )
{
  Int32 pp, i, j, vec;
  pp = 0;
  for (i = minLen; i <= maxLen; i++)
    for (j = 0; j < alphaSize; j++)
      if (length[j] == i) { perm[pp] = j; pp++; };
  for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
  for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
  for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];
  for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
  vec = 0;
  for (i = minLen; i <= maxLen; i++) {
    vec += (base[i+1] - base[i]);
    limit[i] = vec-1;
    vec <<= 1;
  }
  for (i = minLen + 1; i <= maxLen; i++)
    base[i] = ((limit[i-1] + 1) << 1) - base[i];
}
static
void makeMaps_d ( DState* s )
{
  Int32 i;
  s->nInUse = 0;
  for (i = 0; i < 256; i++)
    if (s->inUse[i]) {
      s->seqToUnseq[s->nInUse] = i;
      s->nInUse++;
    }
}
/*---------------------------------------------------*/
#define RETURN(rrr)										 \
  { retVal = rrr; goto save_state_and_return; };
#define GET_BITS(lll,vvv,nnn)							\
{																 \
  case lll: s->state = lll;							 \
  while (True) {											\
    if (s->bsLive >= nnn) {							\
      UInt32 v;										  \
      v = (s->bsBuff >>								\
         (s->bsLive-nnn)) & ((1 << nnn)-1);	\
      s->bsLive -= nnn;								\
      vvv = v;											\
      break;											  \
    }														 \
    if (s->strm->avail_in == 0) RETURN(BZ_OK);  \
    s->bsBuff											  \
      = (s->bsBuff << 8) |							\
        ((UInt32)										\
          (*((UChar*)(s->strm->next_in))));	\
    s->bsLive += 8;									  \
    s->strm->next_in++;								 \
    s->strm->avail_in--;								\
    s->strm->total_in_lo32++;						 \
    if (s->strm->total_in_lo32 == 0)				\
      s->strm->total_in_hi32++;					 \
  }															 \
}
#define GET_UCHAR(lll,uuu)								\
  GET_BITS(lll,uuu,8)
#define GET_BIT(lll,uuu)								  \
  GET_BITS(lll,uuu,1)
/*---------------------------------------------------*/
#define GET_MTF_VAL(label1,label2,lval)			  \
{																 \
  if (groupPos == 0) {									\
    groupNo++;											 \
    if (groupNo >= nSelectors)						\
      RETURN(BZ_DATA_ERROR);						 \
    groupPos = BZ_G_SIZE;							  \
    gSel = s->selector[groupNo];					 \
    gMinlen = s->minLens[gSel];					  \
    gLimit = &(s->limit[gSel][0]);				  \
    gPerm = &(s->perm[gSel][0]);					 \
    gBase = &(s->base[gSel][0]);					 \
  }															 \
  groupPos--;												\
  zn = gMinlen;											 \
  GET_BITS(label1, zvec, zn);						  \
  while (1) {												\
    if (zn > 20 /* the longest code */)			\
      RETURN(BZ_DATA_ERROR);						 \
    if (zvec <= gLimit[zn]) break;				  \
    zn++;													\
    GET_BIT(label2, zj);								\
    zvec = (zvec << 1) | zj;						  \
  };															\
  if (zvec - gBase[zn] < 0							  \
     || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
    RETURN(BZ_DATA_ERROR);							 \
  lval = gPerm[zvec - gBase[zn]];					 \
}
//#define BZ_OK					 0
Int32 BZ2_rNums[512] = {
  619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
  985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
  733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
  419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
  878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
  862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
  150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
  170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
  73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
  909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
  641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
  161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
  382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
  98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
  227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
  469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
  184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
  715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
  951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
  652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
  645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
  609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
  653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
  411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
  170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
  857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
  669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
  944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
  344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
  897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
  433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
  686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
  946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
  978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
  680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
  707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
  297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
  134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
  343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
  140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
  170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
  369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
  804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
  896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
  661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
  768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
  61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
  372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
  780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
  920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
  645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
  936, 638
};
Int32 BZ2_decompress ( DState* s )
{
  UChar		uc;
  Int32		retVal;
  Int32		minLen, maxLen;
  bz_stream* strm = s->strm;
  /* stuff that needs to be saved/restored */
  Int32  i;
  Int32  j;
  Int32  t;
  Int32  alphaSize;
  Int32  nGroups;
  Int32  nSelectors;
  Int32  EOB;
  Int32  groupNo;
  Int32  groupPos;
  Int32  nextSym;
  Int32  nblockMAX;
  Int32  nblock;
  Int32  es;
  Int32  N;
  Int32  curr;
  Int32  zt;
  Int32  zn;
  Int32  zvec;
  Int32  zj;
  Int32  gSel;
  Int32  gMinlen;
  Int32* gLimit;
  Int32* gBase;
  Int32* gPerm;
  if (s->state == BZ_X_MAGIC_1) {
    /*initialise the save area*/
    s->save_i			  = 0;
    s->save_j			  = 0;
    s->save_t			  = 0;
    s->save_alphaSize	= 0;
    s->save_nGroups	  = 0;
    s->save_nSelectors  = 0;
    s->save_EOB			= 0;
    s->save_groupNo	  = 0;
    s->save_groupPos	 = 0;
    s->save_nextSym	  = 0;
    s->save_nblockMAX	= 0;
    s->save_nblock		= 0;
    s->save_es			 = 0;
    s->save_N			  = 0;
    s->save_curr		  = 0;
    s->save_zt			 = 0;
    s->save_zn			 = 0;
    s->save_zvec		  = 0;
    s->save_zj			 = 0;
    s->save_gSel		  = 0;
    s->save_gMinlen	  = 0;
    s->save_gLimit		= NULL;
    s->save_gBase		 = NULL;
    s->save_gPerm		 = NULL;
  }
  /*restore from the save area*/
  i			  = s->save_i;
  j			  = s->save_j;
  t			  = s->save_t;
  alphaSize	= s->save_alphaSize;
  nGroups	  = s->save_nGroups;
  nSelectors  = s->save_nSelectors;
  EOB			= s->save_EOB;
  groupNo	  = s->save_groupNo;
  groupPos	 = s->save_groupPos;
  nextSym	  = s->save_nextSym;
  nblockMAX	= s->save_nblockMAX;
  nblock		= s->save_nblock;
  es			 = s->save_es;
  N			  = s->save_N;
  curr		  = s->save_curr;
  zt			 = s->save_zt;
  zn			 = s->save_zn;
  zvec		  = s->save_zvec;
  zj			 = s->save_zj;
  gSel		  = s->save_gSel;
  gMinlen	  = s->save_gMinlen;
  gLimit		= s->save_gLimit;
  gBase		 = s->save_gBase;
  gPerm		 = s->save_gPerm;
  retVal = BZ_OK;
  switch (s->state) {
    GET_UCHAR(BZ_X_MAGIC_1, uc);
    if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);
    GET_UCHAR(BZ_X_MAGIC_2, uc);
    if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);
    GET_UCHAR(BZ_X_MAGIC_3, uc);
    if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);
    GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8);
    if (s->blockSize100k < (BZ_HDR_0 + 1) ||
       s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
    s->blockSize100k -= BZ_HDR_0;
    if (s->smallDecompress) {
      s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
      s->ll4  = BZALLOC(
               ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar)
             );
      if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
    } else {
      s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
      if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
    }
    GET_UCHAR(BZ_X_BLKHDR_1, uc);
    if (uc == 0x17) goto endhdr_2;
    if (uc != 0x31) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_2, uc);
    if (uc != 0x41) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_3, uc);
    if (uc != 0x59) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_4, uc);
    if (uc != 0x26) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_5, uc);
    if (uc != 0x53) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_BLKHDR_6, uc);
    if (uc != 0x59) RETURN(BZ_DATA_ERROR);
    s->currBlockNo++;
    if (s->verbosity >= 2)
      VPrintf1 ( "\n	 [%d: huff+mtf ", s->currBlockNo );
    s->storedBlockCRC = 0;
    GET_UCHAR(BZ_X_BCRC_1, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_BCRC_2, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_BCRC_3, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_BCRC_4, uc);
    s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
    GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
    s->origPtr = 0;
    GET_UCHAR(BZ_X_ORIGPTR_1, uc);
    s->origPtr = (s->origPtr << 8) | ((Int32)uc);
    GET_UCHAR(BZ_X_ORIGPTR_2, uc);
    s->origPtr = (s->origPtr << 8) | ((Int32)uc);
    GET_UCHAR(BZ_X_ORIGPTR_3, uc);
    s->origPtr = (s->origPtr << 8) | ((Int32)uc);
    if (s->origPtr < 0)
      RETURN(BZ_DATA_ERROR);
    if (s->origPtr > 10 + 100000*s->blockSize100k)
      RETURN(BZ_DATA_ERROR);
    /*--- Receive the mapping table ---*/
    for (i = 0; i < 16; i++) {
      GET_BIT(BZ_X_MAPPING_1, uc);
      if (uc == 1)
        s->inUse16[i] = True; else
        s->inUse16[i] = False;
    }
    for (i = 0; i < 256; i++) s->inUse[i] = False;
    for (i = 0; i < 16; i++)
      if (s->inUse16[i])
        for (j = 0; j < 16; j++) {
          GET_BIT(BZ_X_MAPPING_2, uc);
          if (uc == 1) s->inUse[i * 16 + j] = True;
        }
    makeMaps_d ( s );
    if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
    alphaSize = s->nInUse+2;
    /*--- Now the selectors ---*/
    GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
    if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
    GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
    if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
    for (i = 0; i < nSelectors; i++) {
      j = 0;
      while (True) {
        GET_BIT(BZ_X_SELECTOR_3, uc);
        if (uc == 0) break;
        j++;
        if (j >= nGroups) RETURN(BZ_DATA_ERROR);
      }
      s->selectorMtf[i] = j;
    }
    /*--- Undo the MTF values for the selectors. ---*/
    {
      UChar pos[BZ_N_GROUPS], tmp, v;
      for (v = 0; v < nGroups; v++) pos[v] = v;
      for (i = 0; i < nSelectors; i++) {
        v = s->selectorMtf[i];
        tmp = pos[v];
        while (v > 0) { pos[v] = pos[v-1]; v--; }
        pos[0] = tmp;
        s->selector[i] = tmp;
      }
    }
    /*--- Now the coding tables ---*/
    for (t = 0; t < nGroups; t++) {
      GET_BITS(BZ_X_CODING_1, curr, 5);
      for (i = 0; i < alphaSize; i++) {
        while (True) {
          if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
          GET_BIT(BZ_X_CODING_2, uc);
          if (uc == 0) break;
          GET_BIT(BZ_X_CODING_3, uc);
          if (uc == 0) curr++; else curr--;
        }
        s->len[t][i] = curr;
      }
    }
    /*--- Create the Huffman decoding tables ---*/
    for (t = 0; t < nGroups; t++) {
      minLen = 32;
      maxLen = 0;
      for (i = 0; i < alphaSize; i++) {
        if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
        if (s->len[t][i] < minLen) minLen = s->len[t][i];
      }
      BZ2_hbCreateDecodeTables (
        &(s->limit[t][0]),
        &(s->base[t][0]),
        &(s->perm[t][0]),
        &(s->len[t][0]),
        minLen, maxLen, alphaSize
      );
      s->minLens[t] = minLen;
    }
    /*--- Now the MTF values ---*/
    EOB		= s->nInUse+1;
    nblockMAX = 100000 * s->blockSize100k;
    groupNo  = -1;
    groupPos = 0;
    for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
    /*-- MTF init --*/
    {
      Int32 ii, jj, kk;
      kk = MTFA_SIZE-1;
      for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
          s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
          kk--;
        }
        s->mtfbase[ii] = kk + 1;
      }
    }
    /*-- end MTF init --*/
    nblock = 0;
    GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
    while (True) {
      if (nextSym == EOB) break;
      if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
        es = -1;
        N = 1;
        do {
          /* Check that N doesn't get too big, so that es doesn't
            go negative.  The maximum value that can be
            RUNA/RUNB encoded is equal to the block size (post
            the initial RLE), viz, 900k, so bounding N at 2
            million should guard against overflow without
            rejecting any legitimate inputs. */
          if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR);
          if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
          if (nextSym == BZ_RUNB) es = es + (1+1) * N;
          N = N * 2;
          GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
        }
          while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
        es++;
        uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
        s->unzftab[uc] += es;
        if (s->smallDecompress)
          while (es > 0) {
            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
            s->ll16[nblock] = (UInt16)uc;
            nblock++;
            es--;
          }
        else
          while (es > 0) {
            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
            s->tt[nblock] = (UInt32)uc;
            nblock++;
            es--;
          };
        continue;
      } else {
        if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
        /*-- uc = MTF ( nextSym-1 ) --*/
        {
          Int32 ii, jj, kk, pp, lno, off;
          UInt32 nn;
          nn = (UInt32)(nextSym - 1);
          if (nn < MTFL_SIZE) {
            /* avoid general-case expense */
            pp = s->mtfbase[0];
            uc = s->mtfa[pp+nn];
            while (nn > 3) {
              Int32 z = pp+nn;
              s->mtfa[(z)  ] = s->mtfa[(z)-1];
              s->mtfa[(z)-1] = s->mtfa[(z)-2];
              s->mtfa[(z)-2] = s->mtfa[(z)-3];
              s->mtfa[(z)-3] = s->mtfa[(z)-4];
              nn -= 4;
            }
            while (nn > 0) {
              s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--;
            };
            s->mtfa[pp] = uc;
          } else {
            /* general case */
            lno = nn / MTFL_SIZE;
            off = nn % MTFL_SIZE;
            pp = s->mtfbase[lno] + off;
            uc = s->mtfa[pp];
            while (pp > s->mtfbase[lno]) {
              s->mtfa[pp] = s->mtfa[pp-1]; pp--;
            };
            s->mtfbase[lno]++;
            while (lno > 0) {
              s->mtfbase[lno]--;
              s->mtfa[s->mtfbase[lno]]
                = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
              lno--;
            }
            s->mtfbase[0]--;
            s->mtfa[s->mtfbase[0]] = uc;
            if (s->mtfbase[0] == 0) {
              kk = MTFA_SIZE-1;
              for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
                for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
                  s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
                  kk--;
                }
                s->mtfbase[ii] = kk + 1;
              }
            }
          }
        }
        /*-- end uc = MTF ( nextSym-1 ) --*/
        s->unzftab[s->seqToUnseq[uc]]++;
        if (s->smallDecompress)
          s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
          s->tt[nblock]	= (UInt32)(s->seqToUnseq[uc]);
        nblock++;
        GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
        continue;
      }
    }
    /* Now we know what nblock is, we can do a better sanity
      check on s->origPtr.
    */
    if (s->origPtr < 0 || s->origPtr >= nblock)
      RETURN(BZ_DATA_ERROR);
    /*-- Set up cftab to facilitate generation of T^(-1) --*/
    /* Check: unzftab entries in range. */
    for (i = 0; i <= 255; i++) {
      if (s->unzftab[i] < 0 || s->unzftab[i] > nblock)
        RETURN(BZ_DATA_ERROR);
    }
    /* Actually generate cftab. */
    s->cftab[0] = 0;
    for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
    for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
    /* Check: cftab entries in range. */
    for (i = 0; i <= 256; i++) {
      if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
        /* s->cftab[i] can legitimately be == nblock */
        RETURN(BZ_DATA_ERROR);
      }
    }
    /* Check: cftab entries non-descending. */
    for (i = 1; i <= 256; i++) {
      if (s->cftab[i-1] > s->cftab[i]) {
        RETURN(BZ_DATA_ERROR);
      }
    }
    s->state_out_len = 0;
    s->state_out_ch  = 0;
    BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
    s->state = BZ_X_OUTPUT;
    if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
    if (s->smallDecompress) {
      /*-- Make a copy of cftab, used in generation of T --*/
      for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
      /*-- compute the T vector --*/
      for (i = 0; i < nblock; i++) {
        uc = (UChar)(s->ll16[i]);
        SET_LL(i, s->cftabCopy[uc]);
        s->cftabCopy[uc]++;
      }
      /*-- Compute T^(-1) by pointer reversal on T --*/
      i = s->origPtr;
      j = GET_LL(i);
      do {
        Int32 tmp = GET_LL(j);
        SET_LL(j, i);
        i = j;
        j = tmp;
      }
        while (i != s->origPtr);
      s->tPos = s->origPtr;
      s->nblock_used = 0;
      if (s->blockRandomised) {
        BZ_RAND_INIT_MASK;
        BZ_GET_SMALL(s->k0); s->nblock_used++;
        BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
      } else {
        BZ_GET_SMALL(s->k0); s->nblock_used++;
      }
    } else {
      /*-- compute the T^(-1) vector --*/
      for (i = 0; i < nblock; i++) {
        uc = (UChar)(s->tt[i] & 0xff);
        s->tt[s->cftab[uc]] |= (i << 8);
        s->cftab[uc]++;
      }
      s->tPos = s->tt[s->origPtr] >> 8;
      s->nblock_used = 0;
      if (s->blockRandomised) {
        BZ_RAND_INIT_MASK;
        BZ_GET_FAST(s->k0); s->nblock_used++;
        BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
      } else {
        BZ_GET_FAST(s->k0); s->nblock_used++;
      }
    }
    RETURN(BZ_OK);
   endhdr_2:
    GET_UCHAR(BZ_X_ENDHDR_2, uc);
    if (uc != 0x72) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_3, uc);
    if (uc != 0x45) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_4, uc);
    if (uc != 0x38) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_5, uc);
    if (uc != 0x50) RETURN(BZ_DATA_ERROR);
    GET_UCHAR(BZ_X_ENDHDR_6, uc);
    if (uc != 0x90) RETURN(BZ_DATA_ERROR);
    s->storedCombinedCRC = 0;
    GET_UCHAR(BZ_X_CCRC_1, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_CCRC_2, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_CCRC_3, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    GET_UCHAR(BZ_X_CCRC_4, uc);
    s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
    s->state = BZ_X_IDLE;
    RETURN(BZ_STREAM_END);
    default: AssertH ( False, 4001 );
//		default:RETURN(BZ_OK);
  }
  AssertH ( False, 4002 );
  save_state_and_return:
  s->save_i			  = i;
  s->save_j			  = j;
  s->save_t			  = t;
  s->save_alphaSize	= alphaSize;
  s->save_nGroups	  = nGroups;
  s->save_nSelectors  = nSelectors;
  s->save_EOB			= EOB;
  s->save_groupNo	  = groupNo;
  s->save_groupPos	 = groupPos;
  s->save_nextSym	  = nextSym;
  s->save_nblockMAX	= nblockMAX;
  s->save_nblock		= nblock;
  s->save_es			 = es;
  s->save_N			  = N;
  s->save_curr		  = curr;
  s->save_zt			 = zt;
  s->save_zn			 = zn;
  s->save_zvec		  = zvec;
  s->save_zj			 = zj;
  s->save_gSel		  = gSel;
  s->save_gMinlen	  = gMinlen;
  s->save_gLimit		= gLimit;
  s->save_gBase		 = gBase;
  s->save_gPerm		 = gPerm;
  return retVal;
}
#define BZ_SETERR(eee)						  \
{													  \
  if (bzerror != NULL) *bzerror = eee;	\
  if (bzf != NULL) bzf->lastErr = eee;	\
}
typedef
  struct {
    FILE*	  handle;
    Char		buf[BZ_MAX_UNUSED];
    Int32	  bufN;
    Bool		writing;
    bz_stream strm;
    Int32	  lastErr;
    Bool		initialisedOk;
  }
  bzFile;
  static
  void* default_bzalloc ( void* opaque, Int32 items, Int32 size )
  {
    void* v = malloc ( items * size );
    return v;
  }
  static
  void default_bzfree ( void* opaque, void* addr )
  {
    if (addr != NULL) free ( addr );
  }
  static Bool myfeof ( FILE* f )
  {
    Int32 c = fgetc ( f );
    if (c == EOF) return True;
    ungetc ( c, f );
    return False;
  }
  static
  int bz_config_ok ( void )
  {
    if (sizeof(int)	!= 4) return 0;
    if (sizeof(short) != 2) return 0;
    if (sizeof(char)  != 1) return 0;
    return 1;
  }
  UInt32 BZ2_crc32Table[256] = {
    /*-- Ugly, innit? --*/
    0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
    0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
    0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
    0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
    0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
    0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
    0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
    0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
    0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
    0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
    0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
    0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
    0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
    0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
    0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
    0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
    0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
    0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
    0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
    0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
    0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
    0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
    0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
    0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
    0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
    0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
    0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
    0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
    0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
    0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
    0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
    0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
    0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
    0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
    0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
    0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
    0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
    0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
    0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
    0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
    0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
    0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
    0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
    0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
    0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
    0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
    0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
    0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
    0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
    0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
    0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
    0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
    0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
    0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
    0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
    0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
    0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
    0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
    0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
    0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
    0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
    0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
    0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
    0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
  };
  const char * BZ_API(BZ2_bzlibVersion)(void)
  {
    return BZ_VERSION;
  }
  __inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab )
  {
    Int32 nb, na, mid;
    nb = 0;
    na = 256;
    do {
      mid = (nb + na) >> 1;
      if (indx >= cftab[mid]) nb = mid; else na = mid;
    }
    while (na - nb != 1);
    return nb;
  }
  static
  Bool unRLE_obuf_to_output_SMALL ( DState* s )
  {
    UChar k1;
    if (s->blockRandomised) {
      while (True) {
        /* try to finish existing run */
        while (True) {
          if (s->strm->avail_out == 0) return False;
          if (s->state_out_len == 0) break;
          *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
          BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
          s->state_out_len--;
          s->strm->next_out++;
          s->strm->avail_out--;
          s->strm->total_out_lo32++;
          if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
        }
        /* can a new run be started? */
        if (s->nblock_used == s->save_nblock+1) return False;
        /* Only caused by corrupt data stream? */
        if (s->nblock_used > s->save_nblock+1)
          return True;
        s->state_out_len = 1;
        s->state_out_ch = s->k0;
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 2;
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 3;
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
        k1 ^= BZ_RAND_MASK; s->nblock_used++;
        s->state_out_len = ((Int32)k1) + 4;
        BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK;
        s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
      }
    } else {
      while (True) {
        /* try to finish existing run */
        while (True) {
          if (s->strm->avail_out == 0) return False;
          if (s->state_out_len == 0) break;
          *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
          BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
          s->state_out_len--;
          s->strm->next_out++;
          s->strm->avail_out--;
          s->strm->total_out_lo32++;
          if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
        }
        /* can a new run be started? */
        if (s->nblock_used == s->save_nblock+1) return False;
        /* Only caused by corrupt data stream? */
        if (s->nblock_used > s->save_nblock+1)
          return True;
        s->state_out_len = 1;
        s->state_out_ch = s->k0;
        BZ_GET_SMALL(k1); s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 2;
        BZ_GET_SMALL(k1); s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        s->state_out_len = 3;
        BZ_GET_SMALL(k1); s->nblock_used++;
        if (s->nblock_used == s->save_nblock+1) continue;
        if (k1 != s->k0) { s->k0 = k1; continue; };
        BZ_GET_SMALL(k1); s->nblock_used++;
        s->state_out_len = ((Int32)k1) + 4;
        BZ_GET_SMALL(s->k0); s->nblock_used++;
      }
    }
  }
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
   JNIEnv* env = NULL;
   jint result = -1;
   if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
   //if (vm->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "ERROR: GetEnv failed");
      goto bail;
   }
//	 assert(env != NULL);
//	 if (register_android_boa(env) < 0) {
//		__android_log_print(ANDROID_LOG_INFO,"JNIMsg", "ERROR: Boa Server native registration failed");
//		  goto bail;
//	 }
   /* success -- return valid version number */
   result = JNI_VERSION_1_4;
bail:
   return result;
}
int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm )
{
  DState* s;
  if (strm == NULL) return BZ_PARAM_ERROR;
  s = strm->state;
  if (s == NULL) return BZ_PARAM_ERROR;
  if (s->strm != strm) return BZ_PARAM_ERROR;
  if (s->tt	!= NULL) BZFREE(s->tt);
  if (s->ll16 != NULL) BZFREE(s->ll16);
  if (s->ll4  != NULL) BZFREE(s->ll4);
  BZFREE(strm->state);
  strm->state = NULL;
  return BZ_OK;
}
void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b )
{
  bzFile* bzf = (bzFile*)b;
  BZ_SETERR(BZ_OK);
  if (bzf == NULL)
    { BZ_SETERR(BZ_OK); return; };
  if (bzf->writing)
    { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
  if (bzf->initialisedOk)
    (void)BZ2_bzDecompressEnd ( &(bzf->strm) );
  free ( bzf );
}
static
Bool unRLE_obuf_to_output_FAST ( DState* s )
{
  UChar k1;
  if (s->blockRandomised) {
    while (True) {
      /* try to finish existing run */
      while (True) {
        if (s->strm->avail_out == 0) return False;
        if (s->state_out_len == 0) break;
        *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
        BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
        s->state_out_len--;
        s->strm->next_out++;
        s->strm->avail_out--;
        s->strm->total_out_lo32++;
        if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
      }
      /* can a new run be started? */
      if (s->nblock_used == s->save_nblock+1) return False;
      /* Only caused by corrupt data stream? */
      if (s->nblock_used > s->save_nblock+1)
        return True;
      s->state_out_len = 1;
      s->state_out_ch = s->k0;
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      if (s->nblock_used == s->save_nblock+1) continue;
      if (k1 != s->k0) { s->k0 = k1; continue; };
      s->state_out_len = 2;
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      if (s->nblock_used == s->save_nblock+1) continue;
      if (k1 != s->k0) { s->k0 = k1; continue; };
      s->state_out_len = 3;
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      if (s->nblock_used == s->save_nblock+1) continue;
      if (k1 != s->k0) { s->k0 = k1; continue; };
      BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
      k1 ^= BZ_RAND_MASK; s->nblock_used++;
      s->state_out_len = ((Int32)k1) + 4;
      BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK;
      s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
    }
  } else {
    /* restore */
    UInt32		  c_calculatedBlockCRC = s->calculatedBlockCRC;
    UChar			c_state_out_ch		 = s->state_out_ch;
    Int32			c_state_out_len		= s->state_out_len;
    Int32			c_nblock_used		  = s->nblock_used;
    Int32			c_k0					  = s->k0;
    UInt32*		 c_tt					  = s->tt;
    UInt32		  c_tPos					= s->tPos;
    char*			cs_next_out			 = s->strm->next_out;
    unsigned int  cs_avail_out			= s->strm->avail_out;
    Int32			ro_blockSize100k	  = s->blockSize100k;
    /* end restore */
    UInt32		 avail_out_INIT = cs_avail_out;
    Int32		  s_save_nblockPP = s->save_nblock+1;
    unsigned int total_out_lo32_old;
    while (True) {
      /* try to finish existing run */
      if (c_state_out_len > 0) {
        while (True) {
          if (cs_avail_out == 0) goto return_notr;
          if (c_state_out_len == 1) break;
          *( (UChar*)(cs_next_out) ) = c_state_out_ch;
          BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
          c_state_out_len--;
          cs_next_out++;
          cs_avail_out--;
        }
        s_state_out_len_eq_one:
        {
          if (cs_avail_out == 0) {
            c_state_out_len = 1; goto return_notr;
          };
          *( (UChar*)(cs_next_out) ) = c_state_out_ch;
          BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
          cs_next_out++;
          cs_avail_out--;
        }
      }
      /* Only caused by corrupt data stream? */
      if (c_nblock_used > s_save_nblockPP)
        return True;
      /* can a new run be started? */
      if (c_nblock_used == s_save_nblockPP) {
        c_state_out_len = 0; goto return_notr;
      };
      c_state_out_ch = c_k0;
      BZ_GET_FAST_C(k1); c_nblock_used++;
      if (k1 != c_k0) {
        c_k0 = k1; goto s_state_out_len_eq_one;
      };
      if (c_nblock_used == s_save_nblockPP)
        goto s_state_out_len_eq_one;
      c_state_out_len = 2;
      BZ_GET_FAST_C(k1); c_nblock_used++;
      if (c_nblock_used == s_save_nblockPP) continue;
      if (k1 != c_k0) { c_k0 = k1; continue; };
      c_state_out_len = 3;
      BZ_GET_FAST_C(k1); c_nblock_used++;
      if (c_nblock_used == s_save_nblockPP) continue;
      if (k1 != c_k0) { c_k0 = k1; continue; };
      BZ_GET_FAST_C(k1); c_nblock_used++;
      c_state_out_len = ((Int32)k1) + 4;
      BZ_GET_FAST_C(c_k0); c_nblock_used++;
    }
    return_notr:
    total_out_lo32_old = s->strm->total_out_lo32;
    s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out);
    if (s->strm->total_out_lo32 < total_out_lo32_old)
      s->strm->total_out_hi32++;
    /* save */
    s->calculatedBlockCRC = c_calculatedBlockCRC;
    s->state_out_ch		 = c_state_out_ch;
    s->state_out_len		= c_state_out_len;
    s->nblock_used		  = c_nblock_used;
    s->k0					  = c_k0;
    s->tt					  = c_tt;
    s->tPos					= c_tPos;
    s->strm->next_out	  = cs_next_out;
    s->strm->avail_out	 = cs_avail_out;
    /* end save */
  }
  return False;
}
void BZ2_bz__AssertH__fail ( int errcode )
{
  fprintf(stderr,
    "\n\nbzip2/libbzip2: internal error number %d.\n"
    "This is a bug in bzip2/libbzip2, %s.\n"
    "Please report it to me at: jseward@bzip.org.  If this happened\n"
    "when you were using some program which uses libbzip2 as a\n"
    "component, you should also report this bug to the author(s)\n"
    "of that program.  Please make an effort to report this bug;\n"
    "timely and accurate bug reports eventually lead to higher\n"
    "quality software.  Thanks.  Julian Seward, 10 December 2007.\n\n",
    errcode,
    BZ2_bzlibVersion()
  );
  if (errcode == 1007) {
  fprintf(stderr,
    "\n*** A special note about internal error number 1007 ***\n"
    "\n"
    "Experience suggests that a common cause of i.e. 1007\n"
    "is unreliable memory or other hardware.  The 1007 assertion\n"
    "just happens to cross-check the results of huge numbers of\n"
    "memory reads/writes, and so acts (unintendedly) as a stress\n"
    "test of your memory system.\n"
    "\n"
    "I suggest the following: try compressing the file again,\n"
    "possibly monitoring progress in detail with the -vv flag.\n"
    "\n"
    "* If the error cannot be reproduced, and/or happens at different\n"
    "  points in compression, you may have a flaky memory system.\n"
    "  Try a memory-test program.  I have used Memtest86\n"
    "  (www.memtest86.com).  At the time of writing it is free (GPLd).\n"
    "  Memtest86 tests memory much more thorougly than your BIOSs\n"
    "  power-on test, and may find failures that the BIOS doesn't.\n"
    "\n"
    "* If the error can be repeatably reproduced, this is a bug in\n"
    "  bzip2, and I would very much like to hear about it.  Please\n"
    "  let me know, and, ideally, save a copy of the file causing the\n"
    "  problem -- without which I will be unable to investigate it.\n"
    "\n"
  );
  }
  exit(3);
}
int BZ_API(BZ2_bzDecompress) ( bz_stream *strm )
{
  Bool	 corrupt;
  DState* s;
  if (strm == NULL) return BZ_PARAM_ERROR;
  s = strm->state;
  if (s == NULL) return BZ_PARAM_ERROR;
  if (s->strm != strm) return BZ_PARAM_ERROR;
  while (True) {
    if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR;
    if (s->state == BZ_X_OUTPUT) {
      if (s->smallDecompress)
        corrupt = unRLE_obuf_to_output_SMALL ( s ); else
        corrupt = unRLE_obuf_to_output_FAST  ( s );
      if (corrupt) return BZ_DATA_ERROR;
      if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) {
        BZ_FINALISE_CRC ( s->calculatedBlockCRC );
        if (s->verbosity >= 3)
          VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC,
                  s->calculatedBlockCRC );
        if (s->verbosity >= 2) VPrintf0 ( "]" );
        if (s->calculatedBlockCRC != s->storedBlockCRC)
          return BZ_DATA_ERROR;
        s->calculatedCombinedCRC
          = (s->calculatedCombinedCRC << 1) |
              (s->calculatedCombinedCRC >> 31);
        s->calculatedCombinedCRC ^= s->calculatedBlockCRC;
        s->state = BZ_X_BLKHDR_1;
      } else {
        return BZ_OK;
      }
    }
    if (s->state >= BZ_X_MAGIC_1) {
      Int32 r = BZ2_decompress ( s );
      if (r == BZ_STREAM_END) {
        if (s->verbosity >= 3)
          VPrintf2 ( "\n	 combined CRCs: stored = 0x%08x, computed = 0x%08x",
                  s->storedCombinedCRC, s->calculatedCombinedCRC );
        if (s->calculatedCombinedCRC != s->storedCombinedCRC)
          return BZ_DATA_ERROR;
        return r;
      }
      if (s->state != BZ_X_OUTPUT) return r;
    }
  }
  AssertH ( 0, 6001 );
  return 0;  /*NOTREACHED*/
}
int BZ_API(BZ2_bzDecompressInit)
              ( bz_stream* strm,
                int		  verbosity,
                int		  small )
{
  DState* s;
  if (!bz_config_ok()) return BZ_CONFIG_ERROR;
  if (strm == NULL) return BZ_PARAM_ERROR;
  if (small != 0 && small != 1) return BZ_PARAM_ERROR;
  if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR;
  if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
  if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
  s = BZALLOC( sizeof(DState) );
  if (s == NULL) return BZ_MEM_ERROR;
  s->strm						= strm;
  strm->state				  = s;
  s->state					  = BZ_X_MAGIC_1;
  s->bsLive					 = 0;
  s->bsBuff					 = 0;
  s->calculatedCombinedCRC = 0;
  strm->total_in_lo32		= 0;
  strm->total_in_hi32		= 0;
  strm->total_out_lo32	  = 0;
  strm->total_out_hi32	  = 0;
  s->smallDecompress		 = (Bool)small;
  s->ll4						 = NULL;
  s->ll16						= NULL;
  s->tt						  = NULL;
  s->currBlockNo			  = 0;
  s->verbosity				 = verbosity;
  return BZ_OK;
}
int BZ_API(BZ2_bzRead)
        ( int*	 bzerror,
         BZFILE* b,
         void*	buf,
         int	  len )
{
  Int32	n, ret;
  bzFile* bzf = (bzFile*)b;
  BZ_SETERR(BZ_OK);
  if (bzf == NULL || buf == NULL || len < 0)
    { BZ_SETERR(BZ_PARAM_ERROR); return 0; };
  if (bzf->writing)
    { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; };
  if (len == 0)
    { BZ_SETERR(BZ_OK); return 0; };
  bzf->strm.avail_out = len;
  bzf->strm.next_out = buf;
  while (1) {
    if (ferror(bzf->handle))
      { BZ_SETERR(BZ_IO_ERROR); return 0; };
    if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) {
      n = fread ( bzf->buf, sizeof(UChar),
              BZ_MAX_UNUSED, bzf->handle );
      if (ferror(bzf->handle))
        { BZ_SETERR(BZ_IO_ERROR); return 0; };
      bzf->bufN = n;
      bzf->strm.avail_in = bzf->bufN;
      bzf->strm.next_in = bzf->buf;
    }
    ret = BZ2_bzDecompress ( &(bzf->strm) );
    if (ret != BZ_OK && ret != BZ_STREAM_END)
      { BZ_SETERR(ret); return 0; };
    if (ret == BZ_OK && myfeof(bzf->handle) &&
       bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0)
      { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; };
    if (ret == BZ_STREAM_END)
      { BZ_SETERR(BZ_STREAM_END);
        return len - bzf->strm.avail_out; };
    if (bzf->strm.avail_out == 0)
      { BZ_SETERR(BZ_OK); return len; };
  }
  return -1; /*not reached*/
}
BZFILE* BZ_API(BZ2_bzReadOpen)
             ( int*  bzerror,
              FILE* f,
              int	verbosity,
              int	small,
              void* unused,
              int	nUnused )
{
  bzFile* bzf = NULL;
  int	  ret;
  BZ_SETERR(BZ_OK);
  if (f == NULL ||
     (small != 0 && small != 1) ||
     (verbosity < 0 || verbosity > 4) ||
     (unused == NULL && nUnused != 0) ||
     (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED)))
    { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
  if (ferror(f))
    { BZ_SETERR(BZ_IO_ERROR); return NULL; };
  bzf = malloc ( sizeof(bzFile) );
  if (bzf == NULL)
    { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
  BZ_SETERR(BZ_OK);
  bzf->initialisedOk = False;
  bzf->handle		  = f;
  bzf->bufN			 = 0;
  bzf->writing		 = False;
  bzf->strm.bzalloc  = NULL;
  bzf->strm.bzfree	= NULL;
  bzf->strm.opaque	= NULL;
  while (nUnused > 0) {
    bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++;
    unused = ((void*)( 1 + ((UChar*)(unused))  ));
    nUnused--;
  }
  ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small );
  if (ret != BZ_OK)
    { BZ_SETERR(ret); free(bzf); return NULL; };
  bzf->strm.avail_in = bzf->bufN;
  bzf->strm.next_in  = bzf->buf;
  bzf->initialisedOk = True;
  return bzf;
}
static off_t offtin(u_char *buf)
{
   off_t y;
   y=buf[7]&0x7F;
   y=y*256;y+=buf[6];
   y=y*256;y+=buf[5];
   y=y*256;y+=buf[4];
   y=y*256;y+=buf[3];
   y=y*256;y+=buf[2];
   y=y*256;y+=buf[1];
   y=y*256;y+=buf[0];
   if(buf[7]&0x80) y=-y;
   return y;
}
int applypatch(int argc,char * argv[])
{
   FILE * f, * cpf, * dpf, * epf;
   BZFILE * cpfbz2, * dpfbz2, * epfbz2;
   int cbz2err, dbz2err, ebz2err;
   int fd;
   ssize_t oldsize,newsize;
   ssize_t bzctrllen,bzdatalen;
   u_char header[32],buf[8];
   u_char *old, *new;
   off_t oldpos,newpos;
   off_t ctrl[3];
   off_t lenread;
   off_t i;
   /* Open patch file */
   if ((f = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   /* Read header */
   if (fread(header, 1, 32, f) < 32) {
      if (feof(f))
        errx(1, "Corrupt patch\n");
      err(1, "fread(%s)", argv[3]);
   }
   /* Check for appropriate magic */
   if (memcmp(header, "BSDIFF40", 8) != 0)
      errx(1, "Corrupt patch\n");
   /* Read lengths from header */
   bzctrllen=offtin(header+8);
   bzdatalen=offtin(header+16);
   newsize=offtin(header+24);
   if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
      errx(1,"Corrupt patch\n");
   /* Close patch file and re-open it via libbzip2 at the right places */
   if (fclose(f))
      err(1, "fclose(%s)", argv[3]);
   if ((cpf = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   if (fseeko(cpf, 32, SEEK_SET))
      err(1, "fseeko(%s, %lld)", argv[3],
        (long long)32);
   cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0);
   if ((cpfbz2) == NULL)
      errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
   if ((dpf = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
      err(1, "fseeko(%s, %lld)", argv[3],
        (long long)(32 + bzctrllen));
   if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
      errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
   if ((epf = fopen(argv[3], "r")) == NULL)
      err(1, "fopen(%s)", argv[3]);
   if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
      err(1, "fseeko(%s, %lld)", argv[3],
        (long long)(32 + bzctrllen + bzdatalen));
   if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
      errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
   if(((fd=open(argv[1],O_RDONLY,0))<0) ||
      ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
      ((old=malloc(oldsize+1))==NULL) ||
      (lseek(fd,0,SEEK_SET)!=0) ||
      (read(fd,old,oldsize)!=oldsize) ||
      (close(fd)==-1)) err(1,"%s",argv[1]);
   if((new=malloc(newsize+1))==NULL) err(1,NULL);
   oldpos=0;newpos=0;
   while(newpos<newsize) {
      /* Read control data */
      for(i=0;i<=2;i++) {
        lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
        if ((lenread < 8) || ((cbz2err != BZ_OK) &&
           (cbz2err != BZ_STREAM_END)))
           errx(1, "Corrupt patch\n");
        ctrl[i]=offtin(buf);
      }
      /* Sanity-check */
      if(newpos+ctrl[0]>newsize)
        errx(1,"Corrupt patch\n");
      /* Read diff string */
      lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
      if ((lenread < ctrl[0]) ||
        ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
        errx(1, "Corrupt patch\n");
      /* Add old data to diff string */
      for(i=0;i<ctrl[0];i++)
        if((oldpos+i>=0) && (oldpos+i<oldsize))
           new[newpos+i]+=old[oldpos+i];
      /* Adjust pointers */
      newpos+=ctrl[0];
      oldpos+=ctrl[0];
      /* Sanity-check */
      if(newpos+ctrl[1]>newsize)
        errx(1,"Corrupt patch\n");
      /* Read extra string */
      lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
      if ((lenread < ctrl[1]) ||
        ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
        errx(1, "Corrupt patch\n");
      /* Adjust pointers */
      newpos+=ctrl[1];
      oldpos+=ctrl[2];
   }
   /* Clean up the bzip2 reads */
   BZ2_bzReadClose(&cbz2err, cpfbz2);
   BZ2_bzReadClose(&dbz2err, dpfbz2);
   BZ2_bzReadClose(&ebz2err, epfbz2);
   if (fclose(cpf) || fclose(dpf) || fclose(epf)){
      err(1, "fclose(%s)", argv[3]);
   }
   if((fd=open(argv[2],O_CREAT|O_RDWR|O_TRUNC,0777))<0){
   }
   if(write(fd,new,newsize)!=newsize){
   }
   if(close(fd)==-1){
   }
   free(new);
   free(old);
   return 0;
}
JNIEXPORT jint JNICALL Java_com_example_selfincrease_PatchUpdate_patch(JNIEnv *env,
      jobject obj, jstring old, jstring new , jstring patch)
{
    char * ch[4];
    ch[0]="bspatch";
    ch[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));
    ch[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));
    ch[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));
    int ret=applypatch(4, ch);
    (*env)->ReleaseStringUTFChars(env,old,ch[1]);
    (*env)->ReleaseStringUTFChars(env,new,ch[2]);
    (*env)->ReleaseStringUTFChars(env,patch,ch[3]);
    //return (*env)->NewStringUTF(env,"success");
    return ret;
}
注意两点:

1. 引入”头文件”

#include “com_example_selfincrease_PatchUpdate.h”

2. 实现生命的本地方法patch

Java_com_example_selfincrease_PatchUpdate_patch

JNIEXPORT jint JNICALL Java_com_example_selfincrease_PatchUpdate_patch(JNIEnv *env,
    jobject obj, jstring old, jstring new , jstring patch)
{
  char * ch[4];
  ch[0]="bspatch";
  ch[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));
  ch[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));
  ch[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));
  int ret=applypatch(4, ch);
  (*env)->ReleaseStringUTFChars(env,old,ch[1]);
  (*env)->ReleaseStringUTFChars(env,new,ch[2]);
  (*env)->ReleaseStringUTFChars(env,patch,ch[3]);
  //return (*env)->NewStringUTF(env,"success");
  return ret;
}

编写Android.mk文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := increase
LOCAL_CXXFLAGS :=
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES := com_example_selfincrease_PatchUpdate.c
LOCAL_LDLIBS := -lz -llog
include $(BUILD_SHARED_LIBRARY)

这里我声明编译出来的模块是increase。

上层代码调用

使用loadLibrary引入模块

底层的c代码以及Android.mk文件都实现了以后,就是在上层加载了,这里我们在PatchUpdate类中加载:

static{
        System.loadLibrary("increase");
    }

可以看到这里加载的是increase模块,就是在Android.mk文件中声明的。

MainActivity中调用

在调用之前,当然需要将之前生成好的”increaseone1.0.apk”以及increase.patch文件拷贝至手机里,这里我拷贝到”storage/sdcard0/123”目录下:

MainActivity.java

package com.example.selfincrease;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;
public class MainActivity extends Activity {
  String sd  =   Environment.getExternalStorageDirectory()   +   "/123/";
  String patch   =   "increase.patch";
  String oldapk  =   "increaseone1.0.apk";
  String oldapk_filepath =   sd  +   oldapk;
  String newapk_savepath =   sd  +   "new.apk";
  String patchpath = sd  +   patch;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     PatchUpdate patchInterence  =   new PatchUpdate();
     /**
      * oldapk_filepath  旧apk的路径
      * newapk_savepath  合成的apk路径
      * patchpath	   查分包patch的路径
      */
     patchInterence.patch(oldapk_filepath, newapk_savepath, patchpath);
     Toast.makeText(MainActivity.this, "新的apk已经合成"   +   sd  +   "目录下面", Toast.LENGTH_LONG).show();
    }
}

可以看到这里主要是调用的patchInterence.patch(oldapk_filepath, newapk_savepath, patchpath);来实现patch的合成,也就是核心的工作是由底层的c代码来实现的。

使用ndk-build编译

接下来就是使用ndk-build来编译生成so文件。这里我在window下使用的是cygwine,不清楚的可以参考 一步一步学习androidNDK编程(搭建开发环境)

编译好之后,会生成一个libincrease.so文件,此时运行selfincrease工程,就会在指定目录下生成新的合成后的new.apk文件。

注意:由于需要给sdcard上写入新的apk文件,所以需要在manifest中声明一下权限:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

此时安装运行new.apk,如图:

和我们最新版本的apk运行效果是相同的,说明我们已经合成新的apk成功了。

总结

  1. 使用bsdiff4.3对新旧apk生成差分包
  2. 根据差分包合成新的apk文件。

源码下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值