@SuppressWarnings({"fallthrough","unused"}) public int Decompress(BinBuffer inData, int inLen, BinBuffer outData, int outLen) {
		BitBuffer bitbuf = new BitBuffer(inData);
		long startpos = inData.getPos();
		long endpos = inData.getPos() + inLen;

		int[] window = m_state.window;

		long window_posn = m_state.window_posn;
		long window_size = m_state.window_size;
		long R0 = m_state.R0;
		long R1 = m_state.R1;
		long R2 = m_state.R2;
		long i, j;

		int togo = outLen, this_run, main_element, match_length, match_offset, length_footer, extra, verbatim_bits;
		int rundest, runsrc, copy_length, aligned_bits;

		bitbuf.InitBitStream();

		/* read header if necessary */
		if (m_state.header_read == 0) {
			long intel = bitbuf.ReadBits(1);
			if (intel != 0) {
				// read the filesize
				i = bitbuf.ReadBits(16);
				j = bitbuf.ReadBits(16);
				m_state.intel_filesize = (int)((i << 16) | j);
			}
			m_state.header_read = 1;
		}

		/* main decoding loop */
		while (togo > 0) {
			/* last block finished, new block expected */
			if (m_state.block_remaining == 0) {
				// T O D O may screw something up here
				if (m_state.block_type == LZXConstants.BLOCKTYPE.UNCOMPRESSED) {
					if ((m_state.block_length & 1) == 1) inData.readByte(); /* realign bitstream to word */
					bitbuf.InitBitStream();
				}

				m_state.block_type = LZXConstants.BLOCKTYPE.values()[(int)bitbuf.ReadBits(3)];
				i = bitbuf.ReadBits(16);
				j = bitbuf.ReadBits(8);
				m_state.block_remaining = m_state.block_length = (i << 8) | j;

				switch (m_state.block_type) {
					case ALIGNED:
						for (i = 0, j = 0; i < 8; i++) {
							j = bitbuf.ReadBits(3);
							m_state.ALIGNED_len[(int)i] = (byte)j;
						}
						MakeDecodeTable(LZXConstants.ALIGNED_MAXSYMBOLS,LZXConstants.ALIGNED_TABLEBITS,m_state.ALIGNED_len,m_state.ALIGNED_table);
					case VERBATIM:
						ReadLengths(m_state.MAINTREE_len,0,256,bitbuf);
						ReadLengths(m_state.MAINTREE_len,256,m_state.main_elements,bitbuf);
						MakeDecodeTable(LZXConstants.MAINTREE_MAXSYMBOLS,LZXConstants.MAINTREE_TABLEBITS,m_state.MAINTREE_len,m_state.MAINTREE_table);
						if (m_state.MAINTREE_len[0xE8] != 0) m_state.intel_started = 1;

						ReadLengths(m_state.LENGTH_len,0,LZXConstants.NUM_SECONDARY_LENGTHS,bitbuf);
						MakeDecodeTable(LZXConstants.LENGTH_MAXSYMBOLS,LZXConstants.LENGTH_TABLEBITS,m_state.LENGTH_len,m_state.LENGTH_table);
					break;
					case UNCOMPRESSED:
						m_state.intel_started = 1; /* because we can't assume otherwise */
						bitbuf.EnsureBits(16); /* get up to 16 pad bits into the buffer */
						if (bitbuf.GetBitsLeft() > 16) inData.setPos(inData.getPos() - 2); /* and align the bitstream! */
						byte hi,
						mh,
						ml,
						lo;
						lo = (byte)inData.readByte();
						ml = (byte)inData.readByte();
						mh = (byte)inData.readByte();
						hi = (byte)inData.readByte();
						R0 = (lo | ml << 8 | mh << 16 | hi << 24);
						lo = (byte)inData.readByte();
						ml = (byte)inData.readByte();
						mh = (byte)inData.readByte();
						hi = (byte)inData.readByte();
						R1 = (lo | ml << 8 | mh << 16 | hi << 24);
						lo = (byte)inData.readByte();
						ml = (byte)inData.readByte();
						mh = (byte)inData.readByte();
						hi = (byte)inData.readByte();
						R2 = (lo | ml << 8 | mh << 16 | hi << 24);
					break;
					default:
						return -1; // T O D O throw proper exception
				}
			}

			/* buffer exhaustion check */
			if (inData.getPos() > (startpos + inLen)) {
				/*
				 * it's possible to have a file where the next run is less than
				 * 16 bits in size. In this case, the READ_HUFFSYM() macro used
				 * in building the tables will exhaust the buffer, so we should
				 * allow for this, but not allow those accidentally read bits to
				 * be used (so we check that there are at least 16 bits
				 * remaining - in this boundary case they aren't really part of
				 * the compressed data)
				 */
				System.out.println("WTF");
				if (inData.getPos() > (startpos + inLen + 2) || bitbuf.GetBitsLeft() < 16) return -1; // T O D O throw proper exception
			}

			while ((this_run = (int)m_state.block_remaining) > 0 && togo > 0) {
				if (this_run > togo) this_run = togo;
				togo -= this_run;
				m_state.block_remaining -= this_run;

				/* apply 2^x-1 mask */
				window_posn &= window_size - 1;
				/* runs can't straddle the window wraparound */
				if ((window_posn + this_run) > window_size) return -1; // T O D O throw proper exception

				switch (m_state.block_type) {
					case VERBATIM:
						while (this_run > 0) {
							main_element = (int)ReadHuffSym(m_state.MAINTREE_table,m_state.MAINTREE_len,LZXConstants.MAINTREE_MAXSYMBOLS,
									LZXConstants.MAINTREE_TABLEBITS,bitbuf);
							if (main_element < LZXConstants.NUM_CHARS) {
								/* literal: 0 to NUM_CHARS-1 */
								window[(int)window_posn++] = (byte)main_element;
								this_run--;
							} else {
								/* match: NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
								main_element -= LZXConstants.NUM_CHARS;

								match_length = main_element & LZXConstants.NUM_PRIMARY_LENGTHS;
								if (match_length == LZXConstants.NUM_PRIMARY_LENGTHS) {
									length_footer = (int)ReadHuffSym(m_state.LENGTH_table,m_state.LENGTH_len,LZXConstants.LENGTH_MAXSYMBOLS,
											LZXConstants.LENGTH_TABLEBITS,bitbuf);
									match_length += length_footer;
								}
								match_length += LZXConstants.MIN_MATCH;

								match_offset = main_element >> 3;

								if (match_offset > 2) {
									/* not repeated offset */
									if (match_offset != 3) {
										extra = extra_bits[match_offset];
										verbatim_bits = (int)bitbuf.ReadBits((byte)extra);
										match_offset = (int)position_base[match_offset] - 2 + verbatim_bits;
									} else {
										match_offset = 1;
									}

									/* update repeated offset LRU queue */
									R2 = R1;
									R1 = R0;
									R0 = match_offset;
								} else if (match_offset == 0) {
									match_offset = (int)R0;
								} else if (match_offset == 1) {
									match_offset = (int)R1;
									R1 = R0;
									R0 = match_offset;
								} else /* match_offset == 2 */
								{
									match_offset = (int)R2;
									R2 = R0;
									R0 = match_offset;
								}

								rundest = (int)window_posn;
								this_run -= match_length;

								/* copy any wrapped around source data */
								if (window_posn >= match_offset) {
									/* no wrap */
									runsrc = rundest - match_offset;
								} else {
									runsrc = rundest + ((int)window_size - match_offset);
									copy_length = match_offset - (int)window_posn;
									if (copy_length < match_length) {
										match_length -= copy_length;
										window_posn += copy_length;
										while (copy_length-- > 0)
											window[rundest++] = window[runsrc++];
										runsrc = 0;
									}
								}
								window_posn += match_length;

								/* copy match data - no worries about destination wraps */
								while (match_length-- > 0)
									window[rundest++] = window[runsrc++];
							}
						}
					break;

					case ALIGNED:
						while (this_run > 0) {
							main_element = (int)ReadHuffSym(m_state.MAINTREE_table,m_state.MAINTREE_len,LZXConstants.MAINTREE_MAXSYMBOLS,
									LZXConstants.MAINTREE_TABLEBITS,bitbuf);

							if (main_element < LZXConstants.NUM_CHARS) {
								/* literal 0 to NUM_CHARS-1 */
								window[(int)window_posn++] = (byte)main_element;
								this_run--;
							} else {
								/* match: NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
								main_element -= LZXConstants.NUM_CHARS;

								match_length = main_element & LZXConstants.NUM_PRIMARY_LENGTHS;
								if (match_length == LZXConstants.NUM_PRIMARY_LENGTHS) {
									length_footer = (int)ReadHuffSym(m_state.LENGTH_table,m_state.LENGTH_len,LZXConstants.LENGTH_MAXSYMBOLS,
											LZXConstants.LENGTH_TABLEBITS,bitbuf);
									match_length += length_footer;
								}
								match_length += LZXConstants.MIN_MATCH;

								match_offset = main_element >> 3;

								if (match_offset > 2) {
									/* not repeated offset */
									extra = extra_bits[match_offset];
									match_offset = (int)position_base[match_offset] - 2;
									if (extra > 3) {
										/* verbatim and aligned bits */
										extra -= 3;
										verbatim_bits = (int)bitbuf.ReadBits((byte)extra);
										match_offset += (verbatim_bits << 3);
										aligned_bits = (int)ReadHuffSym(m_state.ALIGNED_table,m_state.ALIGNED_len,LZXConstants.ALIGNED_MAXSYMBOLS,
												LZXConstants.ALIGNED_TABLEBITS,bitbuf);
										match_offset += aligned_bits;
									} else if (extra == 3) {
										/* aligned bits only */
										aligned_bits = (int)ReadHuffSym(m_state.ALIGNED_table,m_state.ALIGNED_len,LZXConstants.ALIGNED_MAXSYMBOLS,
												LZXConstants.ALIGNED_TABLEBITS,bitbuf);
										match_offset += aligned_bits;
									} else if (extra > 0) /* extra==1, extra==2 */
									{
										/* verbatim bits only */
										verbatim_bits = (int)bitbuf.ReadBits((byte)extra);
										match_offset += verbatim_bits;
									} else /* extra == 0 */
									{
										/* ??? */
										match_offset = 1;
									}

									/* update repeated offset LRU queue */
									R2 = R1;
									R1 = R0;
									R0 = match_offset;
								} else if (match_offset == 0) {
									match_offset = (int)R0;
								} else if (match_offset == 1) {
									match_offset = (int)R1;
									R1 = R0;
									R0 = match_offset;
								} else /* match_offset == 2 */
								{
									match_offset = (int)R2;
									R2 = R0;
									R0 = match_offset;
								}

								rundest = (int)window_posn;
								this_run -= match_length;

								/* copy any wrapped around source data */
								if (window_posn >= match_offset) {
									/* no wrap */
									runsrc = rundest - match_offset;
								} else {
									runsrc = rundest + ((int)window_size - match_offset);
									copy_length = match_offset - (int)window_posn;
									if (copy_length < match_length) {
										match_length -= copy_length;
										window_posn += copy_length;
										while (copy_length-- > 0)
											window[rundest++] = window[runsrc++];
										runsrc = 0;
									}
								}
								window_posn += match_length;

								/* copy match data - no worries about destination wraps */
								while (match_length-- > 0)
									window[rundest++] = window[runsrc++];
							}
						}
					break;
					case UNCOMPRESSED:
						if ((inData.getPos() + this_run) > endpos) return -1; // T O D O throw proper exception
						byte[] temp_buffer = inData.readBytes(this_run);
						for (int k = 0; k < temp_buffer.length; k++)
							window[(int)(window_posn + k)] = temp_buffer[k];
						window_posn += this_run;
					break;

					default:
						return -1; // T O D O throw proper exception
				}
			}
		}

		if (togo != 0) return -1; // T O D O throw proper exception
		int start_window_pos = (int)window_posn;
		if (start_window_pos == 0) start_window_pos = (int)window_size;
		start_window_pos -= outLen;
		for (int k = 0; k < outLen; k++)
			outData.writeByte(window[start_window_pos + k]);

		m_state.window_posn = window_posn;
		m_state.R0 = R0;
		m_state.R1 = R1;
		m_state.R2 = R2;

		// T O D O finish intel E8 decoding
		/* intel E8 decoding */
		if ((m_state.frames_read++ < 32768) && m_state.intel_filesize != 0) {
			if (outLen <= 6 || m_state.intel_started == 0) {
				m_state.intel_curpos += outLen;
			} else {
				int dataend = outLen - 10;
				long curpos = m_state.intel_curpos;
				long filesize = m_state.intel_filesize;
				long abs_off, rel_off;

				m_state.intel_curpos = (int)curpos + outLen;

				while (outData.getPos() < dataend) {
					if (outData.readByte() != 0xE8) {
						curpos++;
						continue;
					}
					// abs_off =
				}
			}
			return -1;
		}
		return 0;
	}