/*
Zlib – библиотека сжатия данных общего назначения. Версия 1.1.0
Это изменённая объектно-ориентированная версия библиотеки, полностью
совместимая с оригинальной библиотекой.
Copyright © 1995–2005 Jean-loup Gailly и Mark Adler
Copyright © 2000–2011 ymnk, JCraft, Inc.
Copyright © 2016, 2019 Малик Разработчик
Эта библиотека поставляется «как есть», без каких-либо явных или
подразумеваемых гарантий. Ни при каких обстоятельствах авторы не
несут какой-либо ответственности в случае потери данных вследствие
использования данной библиотеки.
Разрешается всем использовать эту библиотеку для любых целей, в том
числе и для коммерческих приложений, а также изменять её и
распространять свободно при соблюдении следующих условий:
1. Оригинал библиотеки не должен быть искажён; вы не должны
заявлять, что именно вы написали оригинальную библиотеку. Если вы
используете эту библиотеку в своём программном продукте, то ссылка
на авторов библиотеки была бы желательна, но это не является
обязательным требованием.
2. Изменённые версии исходных текстов должны быть отчётливо
маркированы и не должны выдаваться за оригинал библиотеки.
3. Эти замечания не могут быть удалены либо изменены при
каком-либо варианте распространения исходных текстов.
*/
package malik.emulator.compression.zlib;
final class InfBlocks extends Zlib
{
private static final int MANY = 1440;
private static final int TYPE = 0;
private static final int LENS = 1;
private static final int STORED = 2;
private static final int TABLE = 3;
private static final int BTREE = 4;
private static final int DTREE = 5;
private static final int CODES = 6;
private static final int DRY = 7;
private static final int DONE = 8;
private static final int BAD = 9;
private static final int[] INFLATE_MASK;
private static final int[] BORDER;
static
{
INFLATE_MASK = new int[] {
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff,
0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
BORDER = new int[] {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
}
public int bitk;
public int bitb;
public int end;
public int read;
public int write;
public byte[] window;
private boolean check;
private int mode;
private int left;
private int table;
private int index;
private int last;
private int[] blens;
private int[] bb;
private int[] tb;
private int[] hufts;
private InfCodes codes;
private InfTree inftree;
InfBlocks(ZStream z, int w)
{
this.end = w;
this.window = new byte[w];
this.check = z.istate.wrap != 0;
this.mode = TYPE;
this.bb = new int[1];
this.tb = new int[1];
this.hufts = new int[MANY * 3];
this.codes = new InfCodes();
this.inftree = new InfTree();
reset(z);
}
public void reset(ZStream z)
{
mode = TYPE;
bitk = 0;
bitb = 0;
read = write = 0;
if(check)
{
z.adler.reset();
}
}
public void free(ZStream z)
{
reset(z);
window = null;
hufts = null;
}
public void setDictionary(byte[] d, int start, int n)
{
Array.copy(d, start, window, 0, n);
read = write = n;
}
public int syncPoint()
{
return mode == LENS ? 1 : 0;
}
public int proc(ZStream z, int r)
{
int t;
int b;
int k;
int p;
int n;
int q;
int m;
p = z.nextInIndex;
n = z.availIn;
b = bitb;
k = bitk;
q = write;
m = q < read ? read - q - 1 : end - q;
do
{
switch(mode)
{
default:
r = STREAM_ERROR;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
case TYPE:
while(k < 3)
{
if(n != 0)
{
r = OK;
} else
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
n--;
b |= (z.nextIn[p++] & 0xff) << k;
k += 8;
}
t = b & 7;
last = t & 1;
switch(t >>> 1)
{
default:
break;
case 0:
b >>>= 3;
k -= 3;
t = k & 7;
b >>>= t;
k -= t;
mode = LENS;
break;
case 1:
int[] bl = new int[1];
int[] bd = new int[1];
int[][] tl = new int[1][];
int[][] td = new int[1][];
InfTree.inflateTreesFixed(bl, bd, tl, td);
codes.init(bl[0], bd[0], tl[0], 0, td[0], 0, z);
b >>>= 3;
k -= 3;
mode = CODES;
break;
case 2:
b >>>= 3;
k -= 3;
mode = TABLE;
break;
case 3:
b >>>= 3;
k -= 3;
mode = BAD;
z.msg = "invalid block type";
r = DATA_ERROR;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
break;
case LENS:
while(k < 32)
{
if(n != 0)
{
r = OK;
} else
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
n--;
b |= (z.nextIn[p++] & 0xff) << k;
k += 8;
}
if((((~b) >>> 16) & 0xffff) != (b & 0xffff))
{
mode = BAD;
z.msg = "invalid stored block lengths";
r = DATA_ERROR;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
left = (b & 0xffff);
b = k = 0;
mode = left != 0 ? STORED : (last != 0 ? DRY : TYPE);
break;
case STORED:
if(n == 0)
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
if(m == 0)
{
if(q == end && read != 0)
{
q = 0;
m = q < read ? read - q - 1 : end - q;
}
if(m == 0)
{
write = q;
r = inflateFlush(z, r);
q = write;
m = q < read ? read - q - 1 : end - q;
if(q == end && read != 0)
{
q = 0;
m = q < read ? read - q - 1 : end - q;
}
if(m == 0)
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
}
}
r = OK;
t = left;
if(t > n)
{
t = n;
}
if(t > m)
{
t = m;
}
Array.copy(z.nextIn, p, window, q, t);
p += t;
n -= t;
q += t;
m -= t;
if((left -= t) != 0)
{
break;
}
mode = last != 0 ? DRY : TYPE;
break;
case TABLE:
while(k < 14)
{
if(n != 0)
{
r = OK;
} else
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
n--;
b |= (z.nextIn[p++] & 0xff) << k;
k += 8;
}
table = t = (b & 0x3fff);
if((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
{
mode = BAD;
z.msg = "too many length or distance symbols";
r = DATA_ERROR;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
t = (t & 0x1f) + ((t >> 5) & 0x1f) + 258;
if(blens == null || blens.length < t)
{
blens = new int[t];
} else
{
for(int i = 0; i < t; i++)
{
blens[i] = 0;
}
}
b >>>= 14;
k -= 14;
index = 0;
mode = BTREE;
/* fall through */
case BTREE:
while(index < (table >>> 10) + 4)
{
while(k < (3))
{
if(n != 0)
{
r = OK;
} else
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
n--;
b |= (z.nextIn[p++] & 0xff) << k;
k += 8;
}
blens[BORDER[index++]] = b & 7;
b >>>= 3;
k -= 3;
}
while(index < 19)
{
blens[BORDER[index++]] = 0;
}
bb[0] = 7;
t = inftree.inflateTreesBits(blens, bb, tb, hufts, z);
if(t != OK)
{
r = t;
if(r == DATA_ERROR)
{
blens = null;
mode = BAD;
}
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
index = 0;
mode = DTREE;
/* fall through */
case DTREE:
do
{
int i;
int j;
int c;
t = table;
if(index >= (t & 0x1f) + ((t >> 5) & 0x1f) + 258)
{
break;
}
t = bb[0];
while(k < t)
{
if(n != 0)
{
r = OK;
} else
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
n--;
b |= (z.nextIn[p++] & 0xff) << k;
k += 8;
}
t = hufts[(tb[0] + (b & INFLATE_MASK[t])) * 3 + 1];
c = hufts[(tb[0] + (b & INFLATE_MASK[t])) * 3 + 2];
if(c < 16)
{
b >>>= t;
k -= t;
blens[index++] = c;
} else
{
i = c == 18 ? 7 : c - 14;
j = c == 18 ? 11 : 3;
while(k < t + i)
{
if(n != 0)
{
r = OK;
} else
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
n--;
b |= (z.nextIn[p++] & 0xff) << k;
k += 8;
}
b >>>= t;
k -= t;
j += b & INFLATE_MASK[i];
b >>>= i;
k -= i;
i = index;
t = table;
if(i + j > (t & 0x1f) + ((t >> 5) & 0x1f) + 258 || (c == 16 && i < 1))
{
blens = null;
mode = BAD;
z.msg = "invalid bit length repeat";
r = DATA_ERROR;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
c = c == 16 ? blens[i - 1] : 0;
do
{
blens[i++] = c;
} while(--j != 0);
index = i;
}
} while(true);
tb[0] = -1;
int[] bl = new int[1];
int[] bd = new int[1];
int[] tl = new int[1];
int[] td = new int[1];
bl[0] = 9;
bd[0] = 6;
t = table;
t = inftree.inflateTreesDynamic((t & 0x1f) + 257, ((t >> 5) & 0x1f) + 1, blens,
bl, bd, tl, td, hufts, z);
if(t != OK)
{
if(t == DATA_ERROR)
{
blens = null;
mode = BAD;
}
r = t;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0], z);
mode = CODES;
/* fall through */
case CODES:
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
if((r = codes.proc(this, z, r)) != STREAM_END)
{
return inflateFlush(z, r);
}
r = OK;
p = z.nextInIndex;
n = z.availIn;
b = bitb;
k = bitk;
q = write;
m = q < read ? read - q - 1 : end - q;
if(last == 0)
{
mode = TYPE;
break;
}
mode = DRY;
/* fall through */
case DRY:
write = q;
r = inflateFlush(z, r);
q = write;
m = q < read ? read - q - 1 : end - q;
if(read != write)
{
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
mode = DONE;
/* fall through */
case DONE:
r = STREAM_END;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
case BAD:
r = DATA_ERROR;
bitb = b;
bitk = k;
z.availIn = n;
z.totalIn += p - z.nextInIndex;
z.nextInIndex = p;
write = q;
return inflateFlush(z, r);
}
} while(true);
}
public int inflateFlush(ZStream z, int r)
{
int n;
int p;
int q;
p = z.nextOutIndex;
q = read;
n = (q <= write ? write : end) - q;
if(n > z.availOut)
{
n = z.availOut;
}
if(n != 0 && r == BUF_ERROR)
{
r = OK;
}
z.availOut -= n;
z.totalOut += n;
if(check)
{
z.adler.update(window, q, n);
}
Array.copy(window, q, z.nextOut, p, n);
p += n;
q += n;
if(q == end)
{
q = 0;
if(write == end)
{
write = 0;
}
n = write - q;
if(n > z.availOut)
{
n = z.availOut;
}
if(n != 0 && r == BUF_ERROR)
{
r = OK;
}
z.availOut -= n;
z.totalOut += n;
if(check)
{
z.adler.update(window, q, n);
}
Array.copy(window, q, z.nextOut, p, n);
p += n;
q += n;
}
z.nextOutIndex = p;
read = q;
return r;
}
}