/*
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;
import java.io.*;
final class Inflate extends Zlib
{
private static final int PRESET_DICT = 0x20;
private static final int Z_DEFLATED = 8;
private static final int DICT4 = 2;
private static final int DICT3 = 3;
private static final int DICT2 = 4;
private static final int DICT1 = 5;
private static final int DICT0 = 6;
private static final int BLOCKS = 7;
private static final int CHECK4 = 8;
private static final int CHECK3 = 9;
private static final int CHECK2 = 10;
private static final int CHECK1 = 11;
private static final int DONE = 12;
private static final int BAD = 13;
private static final int HEAD = 14;
private static final int LENGTH = 15;
private static final int TIME = 16;
private static final int OS = 17;
private static final int EXLEN = 18;
private static final int EXTRA = 19;
private static final int NAME = 20;
private static final int COMMENT = 21;
private static final int HCRC = 22;
private static final int FLAGS = 23;
private static final byte[] MARK;
static
{
MARK = new byte[] {
0, 0, -1, -1
};
}
public int mode;
public int wrap;
private boolean empty;
private int method;
private int marker;
private int wbits;
private int was;
private int need;
private int flags;
private int needBytes;
private byte[] crcbuf;
private ByteArrayOutputStream tmpString;
private InfBlocks blocks;
private GZIPHeader gheader;
private ZStream stream;
Inflate(ZStream stream)
{
this.needBytes = -1;
this.was = -1;
this.crcbuf = new byte[4];
this.stream = stream;
}
public int inflateEnd()
{
if(blocks != null)
{
blocks.free(stream);
}
return OK;
}
public int inflateInit(int w)
{
stream.msg = null;
blocks = null;
wrap = 0;
if(w < 0)
{
w = -w;
} else
{
wrap = (w >> 4) + 1;
if(w < 48)
{
w &= 15;
}
}
if(w < 8 || w > 15)
{
inflateEnd();
return STREAM_ERROR;
}
if(blocks != null && wbits != w)
{
blocks.free(stream);
blocks = null;
}
wbits = w;
blocks = new InfBlocks(stream, 1 << w);
inflateReset();
return OK;
}
public int inflate(int f)
{
int r;
int b;
if(stream == null || stream.nextIn == null)
{
return f == FINISH && mode == HEAD ? OK : STREAM_ERROR;
}
f = f == FINISH ? BUF_ERROR : OK;
r = BUF_ERROR;
do
{
switch(mode)
{
default:
return STREAM_ERROR;
case HEAD:
if(wrap == 0)
{
mode = BLOCKS;
break;
}
r = readBytes(stream, 2, r, f);
if(empty)
{
return r;
}
if((wrap & 2) != 0 && need == 0x8b1fL)
{
stream.adler = new CRC32();
checksum(2, need);
if(gheader == null)
{
gheader = new GZIPHeader();
}
mode = FLAGS;
break;
}
flags = 0;
method = need & 0xff;
b = (need >> 8) & 0xff;
if((wrap & 1) == 0 || (((method << 8) + b) % 31) != 0)
{
mode = BAD;
stream.msg = "incorrect header check";
break;
}
if((method & 0xf) != Z_DEFLATED)
{
mode = BAD;
stream.msg = "unknown compression method";
break;
}
if((method >> 4) + 8 > wbits)
{
mode = BAD;
stream.msg = "invalid window size";
break;
}
stream.adler = new Adler32();
if((b & PRESET_DICT) == 0)
{
mode = BLOCKS;
break;
}
mode = DICT4;
/* fall through */
case DICT4:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need = ((stream.nextIn[stream.nextInIndex++] & 0xff) << 24) & 0xff000000;
mode = DICT3;
/* fall through */
case DICT3:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 16) & 0xff0000;
mode = DICT2;
/* fall through */
case DICT2:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 8) & 0xff00;
mode = DICT1;
/* fall through */
case DICT1:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need += (stream.nextIn[stream.nextInIndex++] & 0xffL);
stream.adler.reset(need);
mode = DICT0;
return NEED_DICT;
case DICT0:
mode = BAD;
stream.msg = "need dictionary";
marker = 0;
return STREAM_ERROR;
case BLOCKS:
r = blocks.proc(stream, r);
if(r == DATA_ERROR)
{
mode = BAD;
marker = 0;
break;
}
if(r == OK)
{
r = f;
}
if(r != STREAM_END)
{
return r;
}
r = f;
was = stream.adler.getValue();
blocks.reset(stream);
if(wrap == 0)
{
mode = DONE;
break;
}
mode = CHECK4;
/* fall through */
case CHECK4:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need = ((stream.nextIn[stream.nextInIndex++] & 0xff) << 24) & 0xff000000;
mode = CHECK3;
/* fall through */
case CHECK3:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 16) & 0xff0000;
mode = CHECK2;
/* fall through */
case CHECK2:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 8) & 0xff00;
mode = CHECK1;
/* fall through */
case CHECK1:
if(stream.availIn == 0)
{
return r;
}
r = f;
stream.availIn--;
stream.totalIn++;
need += (stream.nextIn[stream.nextInIndex++] & 0xff);
if(flags != 0)
{
need = ((need & 0xff000000) >>> 24) | ((need & 0x00ff0000) >>> 8)
| ((need & 0x0000ff00) << 8) | ((need & 0x000000ff) << 24);
}
if(was != need)
{
stream.msg = "incorrect data check";
}
else if(flags != 0 && gheader != null)
{
gheader.crc = need;
}
mode = LENGTH;
/* fall through */
case LENGTH:
if(wrap != 0 && flags != 0)
{
r = readBytes(stream, 4, r, f);
if(empty)
{
return r;
}
if(stream.msg != null && stream.msg.equals("incorrect data check"))
{
mode = BAD;
marker = 5;
break;
}
if(need != (stream.totalOut & 0xffffffffL))
{
stream.msg = "incorrect length check";
mode = BAD;
break;
}
stream.msg = null;
} else
{
if(stream.msg != null && stream.msg.equals("incorrect data check"))
{
mode = BAD;
marker = 5;
break;
}
}
mode = DONE;
/* fall through */
case DONE:
return STREAM_END;
case BAD:
return DATA_ERROR;
case FLAGS:
r = readBytes(stream, 2, r, f);
if(empty)
{
return r;
}
flags = need & 0xffff;
if((flags & 0xff) != Z_DEFLATED)
{
stream.msg = "unknown compression method";
mode = BAD;
break;
}
if((flags & 0xe000) != 0)
{
stream.msg = "unknown header flags set";
mode = BAD;
break;
}
if((flags & 0x0200) != 0)
{
checksum(2, need);
}
mode = TIME;
/* fall through */
case TIME:
r = readBytes(stream, 4, r, f);
if(empty)
{
return r;
}
if(gheader != null)
{
gheader.time = need;
}
if((flags & 0x0200) != 0)
{
checksum(4, need);
}
mode = OS;
/* fall through */
case OS:
r = readBytes(stream, 2, r, f);
if(empty)
{
return r;
}
if(gheader != null)
{
gheader.xflags = need & 0xff;
gheader.os = (need >> 8) & 0xff;
}
if((flags & 0x0200) != 0)
{
checksum(2, need);
}
mode = EXLEN;
/* fall through */
case EXLEN:
if((flags & 0x0400) != 0)
{
r = readBytes(stream, 2, r, f);
if(empty)
{
return r;
}
if(gheader != null)
{
gheader.extra = new byte[need & 0xffff];
}
if((flags & 0x0200) != 0)
{
checksum(2, need);
}
}
else if(gheader != null)
{
gheader.extra = null;
}
mode = EXTRA;
/* fall through */
case EXTRA:
if((flags & 0x0400) != 0)
{
r = readBytes(stream, r, f);
if(empty)
{
return r;
}
if(gheader != null)
{
byte[] foo = tmpString.toByteArray();
tmpString = null;
if(foo.length == gheader.extra.length)
{
Array.copy(foo, 0, gheader.extra, 0, foo.length);
} else
{
stream.msg = "bad extra field length";
mode = BAD;
break;
}
}
}
else if(gheader != null)
{
gheader.extra = null;
}
mode = NAME;
/* fall through */
case NAME:
if((flags & 0x0800) != 0)
{
r = readString(stream, r, f);
if(empty)
{
return r;
}
if(gheader != null)
{
gheader.name = tmpString.toByteArray();
}
tmpString = null;
}
else if(gheader != null)
{
gheader.name = null;
}
mode = COMMENT;
/* fall through */
case COMMENT:
if((flags & 0x1000) != 0)
{
r = readString(stream, r, f);
if(empty)
{
return r;
}
if(gheader != null)
{
gheader.comment = tmpString.toByteArray();
}
tmpString = null;
}
else if(gheader != null)
{
gheader.comment = null;
}
mode = HCRC;
/* fall through */
case HCRC:
if((flags & 0x0200) != 0)
{
r = readBytes(stream, 2, r, f);
if(empty)
{
return r;
}
if(gheader != null)
{
gheader.hcrc = need & 0xffff;
}
if(need != (stream.adler.getValue() & 0xffffL))
{
mode = BAD;
stream.msg = "header crc mismatch";
marker = 5;
break;
}
}
stream.adler = new CRC32();
mode = BLOCKS;
break;
}
} while(true);
}
public int inflateSetDictionary(byte[] dictionary, int dictLength)
{
int index;
int length;
int adlerNeed;
if(stream == null || (mode != DICT0 && wrap != 0))
{
return STREAM_ERROR;
}
index = 0;
length = dictLength;
if(mode == DICT0)
{
adlerNeed = stream.adler.getValue();
stream.adler.reset();
stream.adler.update(dictionary, 0, dictLength);
if(stream.adler.getValue() != adlerNeed)
{
return DATA_ERROR;
}
}
stream.adler.reset();
if(length >= (1 << wbits))
{
length = (1 << wbits) - 1;
index = dictLength - length;
}
blocks.setDictionary(dictionary, index, length);
mode = BLOCKS;
return OK;
}
public int inflateSync()
{
int n;
int p;
int m;
long r;
long w;
if(stream == null)
{
return STREAM_ERROR;
}
if(mode != BAD)
{
mode = BAD;
marker = 0;
}
if((n = stream.availIn) == 0)
{
return BUF_ERROR;
}
p = stream.nextInIndex;
m = marker;
while(n != 0 && m < 4)
{
if(stream.nextIn[p] == MARK[m])
{
m++;
}
else if(stream.nextIn[p] != 0)
{
m = 0;
}
else
{
m = 4 - m;
}
p++;
n--;
}
stream.totalIn += p - stream.nextInIndex;
stream.nextInIndex = p;
stream.availIn = n;
marker = m;
if(m != 4)
{
return DATA_ERROR;
}
r = stream.totalIn;
w = stream.totalOut;
inflateReset();
stream.totalIn = r;
stream.totalOut = w;
mode = BLOCKS;
return OK;
}
public int inflateSyncPoint()
{
if(stream == null || blocks == null)
{
return STREAM_ERROR;
}
return blocks.syncPoint();
}
private void checksum(int n, int v)
{
int i;
for(i = 0; i < n; i++)
{
crcbuf[i] = (byte) (v & 0xff);
v >>= 8;
}
stream.adler.update(crcbuf, 0, n);
}
private int inflateReset()
{
if(stream == null)
{
return STREAM_ERROR;
}
stream.totalIn = stream.totalOut = 0;
stream.msg = null;
mode = HEAD;
needBytes = -1;
blocks.reset(stream);
return OK;
}
private int readBytes(ZStream z, int n, int r, int f)
{
if(needBytes == -1)
{
needBytes = n;
need = 0;
}
while(needBytes > 0)
{
if(z.availIn == 0)
{
empty = true;
return r;
}
r = f;
z.availIn--;
z.totalIn++;
need = need | ((z.nextIn[z.nextInIndex++] & 0xff) << ((n - needBytes) * 8));
needBytes--;
}
if(n == 2)
{
need &= 0xffffL;
}
else if(n == 4)
{
need &= 0xffffffff;
}
needBytes = -1;
empty = false;
return r;
}
private int readBytes(ZStream z, int r, int f)
{
if(tmpString == null)
{
tmpString = new ByteArrayOutputStream();
}
while(need > 0)
{
if(z.availIn == 0)
{
empty = true;
return r;
}
r = f;
z.availIn--;
z.totalIn++;
tmpString.write(z.nextIn, z.nextInIndex, 1);
z.adler.update(z.nextIn, z.nextInIndex, 1);
z.nextInIndex++;
need--;
}
empty = false;
return r;
}
private int readString(ZStream z, int r, int f)
{
if(tmpString == null)
{
tmpString = new ByteArrayOutputStream();
}
int b = 0;
do
{
if(z.availIn == 0)
{
empty = true;
return r;
}
r = f;
z.availIn--;
z.totalIn++;
b = z.nextIn[z.nextInIndex];
if(b != 0)
{
tmpString.write(z.nextIn, z.nextInIndex, 1);
}
z.adler.update(z.nextIn, z.nextInIndex, 1);
z.nextInIndex++;
} while(b != 0);
empty = false;
return r;
}
}