/*
 *  Copyright (C) 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
 *  Copyright (C) 2007-2008 Sourcefire, Inc.
 *
 *  Authors: Michal 'GiM' Spadlinski
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 */

#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif

#include <stdio.h>
#ifdef        HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef        HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef        HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef        HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef        HAVE_STRING_H
#include <string.h>
#endif

#include "clamav.h"
#include "cltypes.h"
#include "pe.h"
#include "rebuildpe.h"
#include "others.h"
#include "upack.h"
#include "mew.h"

#define EC32(x) le32_to_host(x) /* Convert little endian to host */
#define CE32(x) be32_to_host(x) /* Convert big endian to host */

int unupack399(char *, uint32_t, uint32_t, char *, uint32_t, char *, char *, uint32_t, char *);

enum { UPACK_399, UPACK_11_12, UPACK_0151477, UPACK_0297729 };

int unupack(int upack, char *dest, uint32_t dsize, char *buff, uint32_t vma, uint32_t ep, uint32_t base, uint32_t va, int file)
{
	int j, searchval;
	char *loc_esi, *loc_edi = NULL, *loc_ebx, *end_edi, *save_edi, *alvalue;
	char *paddr, *pushed_esi, *save2;
	uint32_t save1, save3, loc_ecx, count, shlsize, original_ep, ret, loc_ebx_u;
	struct cli_exe_section section;
	int upack_version = UPACK_399;

	/* buff [168 bytes] doesn't have to be checked, since it was checked in pe.c */
	if (upack)
	{
		uint32_t aljump, shroff, lngjmpoff;

		/* dummy characteristics ;/ */
		if (buff[5] == '\xff' && buff[6] == '\x36')
			upack_version = UPACK_0297729;
		loc_esi = dest + (cli_readint32(buff + 1) -  vma);

		if (!CLI_ISCONTAINED(dest, dsize, loc_esi, 12))
			return -1;
		original_ep = cli_readint32(loc_esi);
		loc_esi += 4;
		/*cli_readint32(loc_esi);*/
		loc_esi += 4;

		original_ep -= vma;
		cli_dbgmsg("Upack: EP: %08x original:  %08X || %08x\n", ep, original_ep, cli_readint32(loc_esi-8));

		if (upack_version == UPACK_399)
		{
			/* jmp 1 */
			loc_edi = dest + (cli_readint32(loc_esi) -  vma);
			if (!CLI_ISCONTAINED(dest, dsize, dest+ep+0xa, 2) || dest[ep+0xa] != '\xeb')
				return -1;
			loc_esi = dest + *(dest + ep + 0xb) + ep + 0xc;

			/* use this as a temp var */
			/* jmp 2 + 0xa */
			alvalue = loc_esi+0x1a;
			if (!CLI_ISCONTAINED(dest, dsize, alvalue, 2) || *alvalue != '\xeb')
				return -1;
			alvalue++;
			alvalue += (*alvalue&0xff) + 1 + 0xa;
			lngjmpoff = 8;
		} else {
			if (!CLI_ISCONTAINED(dest, dsize, dest+ep+7, 5) || dest[ep+7] != '\xe9')
				return -1;
			loc_esi = dest + cli_readint32(dest + ep + 8) + ep + 0xc;
			alvalue = loc_esi + 0x25;
			lngjmpoff = 10;
		}

		if (!CLI_ISCONTAINED(dest, dsize, alvalue, 2) || *alvalue != '\xb5')
			return -1;
		alvalue++;
		count = *alvalue&0xff;

		if (!CLI_ISCONTAINED(dest, dsize, alvalue, lngjmpoff+5) || *(alvalue+lngjmpoff) != '\xe9')
			return -1;
		/* use this as a temp to make a long jmp to head of unpacking proc */
		shlsize = cli_readint32(alvalue + lngjmpoff+1);
		/* upack_399 + upack_0151477 */
		if (upack_version == UPACK_399)
			shlsize = shlsize + (loc_esi - dest) + *(loc_esi+0x1b) + 0x1c + 0x018; /* read checked above */
		else
			/* there is no additional jump in upack_0297729 */
			shlsize = shlsize + (loc_esi - dest) + 0x035;
		/* do the jump, 43 - point to jecxz */
		alvalue = dest+shlsize+43;

		/* 0.39 */
		aljump = 8;
		shroff = 24;
		if (!CLI_ISCONTAINED(dest, dsize, alvalue-1, 2) || *(alvalue-1) != '\xe3')
		{
			/* in upack_0297729 and upack_0151477 jecxz is at offset: 46 */
			alvalue = dest+shlsize+46;
			if (!CLI_ISCONTAINED(dest, dsize, alvalue-1, 2) || *(alvalue-1) != '\xe3')
				return -1;
			else {
				if (upack_version != UPACK_0297729)
					upack_version = UPACK_0151477;
				aljump = 7;
				shroff = 26;
			}
			
		}
		/* do jecxz */
		alvalue += (*alvalue&0xff) + 1;
		/* is there a long jump ? */
		if (!CLI_ISCONTAINED(dest, dsize, alvalue, aljump+5) || *(alvalue+aljump) != '\xe9')
			return -1;
		/* do jmp, 1+4 - size of jmp instruction, aljump - instruction offset, 27 offset to cmp al,xx*/
		ret = cli_readint32(alvalue+aljump+1);
		alvalue += ret + aljump+1+4 + 27;
		if (upack_version == UPACK_0297729)
			alvalue += 2;
		/* shr ebp */
		if (!CLI_ISCONTAINED(dest, dsize, dest+shlsize+shroff, 3) || *(dest+shlsize+shroff) != '\xc1' || *(dest+shlsize+shroff+1) != '\xed')
			return -1;
		shlsize = (*(dest + shlsize + shroff+2))&0xff;
		count *= 0x100;
		if (shlsize < 2 || shlsize > 8)
		{
			cli_dbgmsg ("Upack: context bits out of bounds\n");
			return -1;
		}
		cli_dbgmsg("Upack: Context Bits parameter used with lzma: %02x, %02x\n", shlsize, count);
		/* check if loc_esi + .. == 0xbe -> mov esi */
		/* upack_0297729 has mov esi, .. + mov edi, .., in upack_0151477 and upack_399 EDI has been already set before */
		if (upack_version == UPACK_0297729)
		{
			if (!CLI_ISCONTAINED(dest, dsize, loc_esi+6, 10) || *(loc_esi+6) != '\xbe' || *(loc_esi+11) != '\xbf')
				return -1;
			if ((uint32_t)cli_readint32(loc_esi + 7) < base || (uint32_t)cli_readint32(loc_esi+7) > vma)
				return -1;
			loc_edi = dest + (cli_readint32(loc_esi + 12) - vma);
			loc_esi = dest + (cli_readint32(loc_esi + 7) - base);
		} else {
			if (!CLI_ISCONTAINED(dest, dsize, loc_esi+7, 5) || *(loc_esi+7) != '\xbe')
				return -1;
			loc_esi = dest + (cli_readint32(loc_esi + 8) - vma);
		}

		if (upack_version == UPACK_0297729)
		{
			/* 0x16*4=0x58, 6longs*4 = 24, 0x64-last loc_esi read location */
			if (!CLI_ISCONTAINED(dest, dsize, loc_edi, (0x58 + 24 + 4*count)) || !CLI_ISCONTAINED(dest, dsize, loc_esi, (0x58 + 0x64 + 4)))
				return -1;

			/* XXX I don't know if this [0x16] is constant number, not enough samples provided */
			for (j=0; j<0x16; j++, loc_esi+=4, loc_edi+=4)
				cli_writeint32(loc_edi, cli_readint32(loc_esi)); 
		} else {
			/* 0x27*4=0x9c, 6longs*4 = 24, 0x34-last loc_esi read location */
			if (!CLI_ISCONTAINED(dest, dsize, loc_edi, (0x9c + 24 + 4*count)) || !CLI_ISCONTAINED(dest, dsize, loc_esi, (0x9c + 0x34 + 4)))
				return -1;
			for (j=0; j<0x27; j++, loc_esi+=4, loc_edi+=4)
				cli_writeint32(loc_edi, cli_readint32(loc_esi)); 
		}
		save3 = cli_readint32(loc_esi + 4);
		paddr = dest + ((uint32_t)cli_readint32(loc_edi - 4)) - vma;
		loc_ebx = loc_edi;
		cli_writeint32(loc_edi, 0xffffffff);
		loc_edi+=4;
		cli_writeint32(loc_edi, 0);
		loc_edi+=4;
		for (j=0; j<4; j++, loc_edi+=4)
		    cli_writeint32(loc_edi, (1));

		for (j=0; (unsigned int)j<count; j++, loc_edi+=4)
		    cli_writeint32(loc_edi, 0x400);
		
		loc_edi = dest + cli_readint32(loc_esi + 0xc) - vma;
		if (upack_version == UPACK_0297729)
			loc_edi = dest+vma-base; /* XXX not enough samples provided to be sure of it! */

		pushed_esi = loc_edi;
		if (upack_version == UPACK_0297729)
		{
			end_edi = dest + cli_readint32(loc_esi + 0x64) - vma;
			save3 = cli_readint32(loc_esi + 0x40);
		} else {
                        end_edi = dest + cli_readint32(loc_esi + 0x34) - vma;
                }
                if (loc_edi > end_edi) {
                        cli_dbgmsg("Upack: loc_edi > end_edi breaks cli_rebuildpe() bb#11216\n");
                        return -1;
                }
		/* begin end */
		cli_dbgmsg("Upack: data initialized, before upack lzma call!\n");
		if ((ret = (uint32_t)unupack399(dest, dsize, 0, loc_ebx, 0, loc_edi, end_edi, shlsize, paddr)) == 0xffffffff)
			return -1;
	/* alternative begin */
	} else {
		int ep_jmp_offs, rep_stosd_count_offs, context_bits_offs;
		loc_esi = dest + vma + ep;
		/* yet another dummy characteristics ;/ */
		if (buff[0] == '\xbe' && buff[5] == '\xad' && buff[6] == '\x8b' && buff[7] == '\xf8')
			upack_version = UPACK_11_12;

		if (upack_version == UPACK_11_12)
		{
			ep_jmp_offs = 0x1a4;
			rep_stosd_count_offs = 0x1b;
			context_bits_offs = 0x41;
			alvalue = loc_esi + 0x184;
		} else {
			ep_jmp_offs = 0x217;
			rep_stosd_count_offs = 0x3a;
			context_bits_offs = 0x5f;
			alvalue = loc_esi + 0x1c1;
		}

		if (!CLI_ISCONTAINED(dest, dsize, loc_esi, ep_jmp_offs+4))
			return -1;
		save1 = cli_readint32(loc_esi + ep_jmp_offs);
		original_ep = (loc_esi - dest) + ep_jmp_offs + 4;
		original_ep += (int32_t)save1;
		cli_dbgmsg("Upack: EP: %08x original %08x\n", ep, original_ep);

		/* this are really ugly hacks,
		 * rep_stosd_count_offs & context_bits_offs are < ep_jmp_offs,
		 * so checked in CLI_ISCONTAINED above */
		count = (*(loc_esi + rep_stosd_count_offs))&0xff;
		shlsize = (*(loc_esi + context_bits_offs))&0xff;
		shlsize = 8 - shlsize;
		if (shlsize < 2 || shlsize > 8)
		{
			cli_dbgmsg ("Upack: context bits out of bounds\n");
			return -1;
		}
		count *= 0x100;
		cli_dbgmsg("Upack: Context Bits parameter used with lzma: %02x, %02x\n", shlsize, count);
		if (upack_version == UPACK_399)
		{
			loc_esi += 4;
			loc_ecx = cli_readint32(loc_esi+2);
			cli_writeint32(loc_esi+2,0);
			if (!loc_ecx)
			{
				cli_dbgmsg("Upack: something's wrong, report back\n");
				return -1;/* XXX XXX XXX XXX */
			}
			loc_esi -= (loc_ecx - 2);
			if (!CLI_ISCONTAINED(dest, dsize, loc_esi, 12))
				return -1;

			cli_dbgmsg("Upack: %p %p %08x %08x\n", loc_esi, dest, cli_readint32(loc_esi), base);
			loc_ebx_u = loc_esi - (dest + cli_readint32(loc_esi) - base);
			cli_dbgmsg("Upack: EBX: %08x\n", loc_ebx_u);
			loc_esi += 4;
			save2 = loc_edi = dest + cli_readint32(loc_esi) - base;
			cli_dbgmsg("Upack: DEST: %08x, %08x\n", cli_readint32(loc_esi), cli_readint32(loc_esi) - base);
			loc_esi += 4;
			/* 2vGiM: j is signed. Is that really what you want? Will it cause problems with the following checks?
			 * yes! this is wrong! how did you notice that?!
			 */
			j = cli_readint32(loc_esi);
			if (j<0)
			{
				cli_dbgmsg("Upack: probably hand-crafted data, report back\n");
				return -1;
			}
			loc_esi += 4;
			cli_dbgmsg("Upack: ecx counter: %08x\n", j);

			if (((uint64_t)count+j) * 4 > UINT_MAX)
				return -1;
			if (!CLI_ISCONTAINED(dest, dsize, loc_esi, (j*4)) || !CLI_ISCONTAINED(dest, dsize, loc_edi, ((j+count)*4)))
				return -1;
			for (;j--; loc_edi+=4, loc_esi+=4)
				cli_writeint32(loc_edi, cli_readint32(loc_esi));
			if (!CLI_ISCONTAINED(dest, dsize, save2, 8))
				return -1;
			loc_ecx = cli_readint32(save2);
			save2 += 4;
			loc_esi = save2;
			/* I could probably do simple loc_esi+= (0xe<<2),
			 *  but I'm not sure if there is always 0xe and is always ebx =0
			 */
			do {
				loc_esi += loc_ebx_u;
				loc_esi += 4;
			} while (--loc_ecx);
			if (!CLI_ISCONTAINED(dest, dsize, loc_esi, 4))
				return -1;
			save1 = cli_readint32(loc_esi); /* loc_eax = 0x400 */
			loc_esi += 4;

			for (j=0; (uint32_t)j<count; j++, loc_edi+=4) /* checked above */
				cli_writeint32(loc_edi, (save1));

			if (!CLI_ISCONTAINED(dest, dsize, (loc_esi+0x10), 4))
				return -1;
			cli_writeint32(loc_esi+0x10, (uint32_t)cli_readint32(loc_esi+0x10)+loc_ebx_u);
			loc_ebx = loc_esi+0x14;
			loc_esi = save2;
			/* loc_ebx_u gets saved */
			/* checked above, (...save2, 8) */
			save_edi = loc_edi = dest + ((uint32_t)cli_readint32(loc_esi) - base);
			loc_esi +=4;
			cli_dbgmsg("Upack: before_fixing\n");
			/* fix values */
			if (!CLI_ISCONTAINED(dest, dsize, loc_ebx-4, (12 + 4*4)) || !CLI_ISCONTAINED(dest, dsize, loc_esi+0x24, 4) || !CLI_ISCONTAINED(dest, dsize, loc_esi+0x40, 4))
				return -1;
			for (j=2; j<6; j++)
			      cli_writeint32(loc_ebx+(j<<2), cli_readint32(loc_ebx+(j<<2)));
			paddr = dest + cli_readint32(loc_ebx - 4) - base;
			save1 = loc_ecx;
			pushed_esi = loc_edi;
			end_edi = dest + cli_readint32(loc_esi+0x24) - base;
			vma = cli_readint32(loc_ebx); cli_writeint32(loc_ebx, cli_readint32(loc_ebx + 4)); cli_writeint32((loc_ebx + 4), vma);
		/* Upack 1.1/1.2 is something between 0.39 2-section and 0.39 3-section */
		} else if (upack_version == UPACK_11_12) {
			cli_dbgmsg("Upack v 1.1/1.2\n");
			loc_esi = dest + 0x148; /* always constant? */
			loc_edi = dest + cli_readint32(loc_esi) - base; /* read checked above */
			loc_esi += 4;
			save_edi = loc_edi;
			/* movsd */
			paddr = dest + ((uint32_t)cli_readint32(loc_esi)) - base;
			loc_esi += 4;
			loc_edi += 4;
			loc_ebx = loc_edi;
		
			if (((uint64_t)count+6) * 4 > UINT_MAX)
				return -1;
			if (!CLI_ISCONTAINED(dest, dsize, loc_edi, ((6+count)*4)))
				return -1;
			cli_writeint32(loc_edi, 0xffffffff);
			loc_edi += 4;
			cli_writeint32(loc_edi, 0);
			loc_edi += 4;
			for (j=0; j<4; j++, loc_edi+=4)
				cli_writeint32(loc_edi, (1));

			for (j=0; (uint32_t)j<count; j++, loc_edi+=4)
				cli_writeint32(loc_edi, 0x400);
			
			loc_edi = dest + cli_readint32(loc_esi) - base; /* read checked above */
			pushed_esi = loc_edi;
			loc_esi += 4;
			loc_ecx = 0;

			loc_esi += 4;

			end_edi = dest + cli_readint32(loc_esi-0x28) - base; /* read checked above */
			loc_esi = save_edi;
		}
                if (loc_edi > end_edi) {
                        cli_dbgmsg("Upack(alt begin): loc_edi > end_edi breaks cli_rebuildpe() bb#11216\n");
                        return -1;
                }
		cli_dbgmsg("Upack: data initialized, before upack lzma call!\n");
		if ((ret = (uint32_t)unupack399(dest, dsize, loc_ecx, loc_ebx, loc_ecx, loc_edi, end_edi, shlsize, paddr)) == 0xffffffff)
			return -1;
		if (upack_version == UPACK_399)
			save3 = cli_readint32(loc_esi + 0x40);
		else if (upack_version == UPACK_11_12)
			save3 = cli_readint32(dest + vma + ep + 0x174);
	}

	/* let's fix calls */
	loc_ecx = 0;
	if (!CLI_ISCONTAINED(dest, dsize, alvalue, 1)) {
		cli_dbgmsg("Upack: alvalue out of bounds\n");
		return -1;
	}

	searchval = *alvalue&0xff;
	cli_dbgmsg("Upack: loops: %08x search value: %02x\n", save3, searchval);
	while(save3) {
		if (!CLI_ISCONTAINED(dest, dsize, pushed_esi + loc_ecx, 1))
		{
			cli_dbgmsg("Upack: callfixerr %p %08x = %p, %p\n", dest, dsize, dest+dsize, pushed_esi+loc_ecx);
			return -1;
		}
		if (pushed_esi[loc_ecx] == '\xe8' || pushed_esi[loc_ecx] == '\xe9')
		{
			char *adr = (pushed_esi + loc_ecx + 1);
			loc_ecx++;
			if (!CLI_ISCONTAINED(dest, dsize, adr, 4))
			{
				cli_dbgmsg("Upack: callfixerr\n");
				return -1;
			}
			if ((cli_readint32(adr)&0xff) != searchval)
				continue;
			cli_writeint32(adr, EC32(CE32((uint32_t)(cli_readint32(adr)&0xffffff00)))-loc_ecx-4);
			loc_ecx += 4;
			save3--;
		} else 
			loc_ecx++;
	}

	section.raw = 0;
	section.rva = va;
	section.rsz = end_edi-loc_edi;
	section.vsz = end_edi-loc_edi;

	/* bb#11282 - prevent dest+va/dest from passing an invalid dereference to cli_rebuildpe */
	/* check should trigger on broken PE files where the section exists outside of the file */
	if ((!upack && ((va + section.rsz) > dsize)) || (upack && (section.rsz > dsize))) {
		cli_dbgmsg("Upack: Rebuilt section exceeds allocated buffer; breaks cli_rebuildpe() bb#11282\n");
		return 0;
	}

	if (!cli_rebuildpe(dest + (upack?0:va), &section, 1, base, original_ep, 0, 0, file)) {
		cli_dbgmsg("Upack: Rebuilding failed\n");
		return 0;
	}
	return 1;
}


int unupack399(char *bs, uint32_t bl, uint32_t init_eax, char *init_ebx, uint32_t init_ecx, char *init_edi, char *end_edi, uint32_t shlsize, char *paddr)
{
	struct lzmastate p;
	uint32_t loc_eax, ret, loc_al, loc_ecx = init_ecx, loc_ebp, eax_copy = init_eax, temp, i, jakas_kopia;
	uint32_t state[6], temp_ebp;
	char *loc_edx, *loc_ebx = init_ebx, *loc_edi = init_edi, *loc_ebp8, *edi_copy;
	p.p0 = paddr;
	p.p1 = cli_readint32(init_ebx);
	p.p2 = cli_readint32(init_ebx + 4);

	cli_dbgmsg("\n\tp0: %p\n\tp1: %08x\n\tp2: %08x\n", p.p0, p.p1, p.p2);
	for (i = 0; i<6; i++) {
		state[i] = cli_readint32(loc_ebx + (i<<2));
		cli_dbgmsg("state[%d] = %08x\n", i, state[i]);
	}
	do {
		loc_eax = eax_copy;
		loc_edx = loc_ebx + (loc_eax<<2) + 0x58;

		if ((ret = lzma_upack_esi_00(&p, loc_edx, bs, bl)))
		{
			/* loc_483927 */
			loc_al = loc_eax&0xff;
			loc_al = ((loc_al + 0xf9) > 0xff)?(3+8):8;
			loc_eax = (loc_eax&0xffffff00)|(loc_al&0xff);
			loc_ebp = state[2];
			loc_ecx = (loc_ecx&0xffffff00)|0x30;
			loc_edx += loc_ecx;
			/* *(uint32_t *)(loc_ebx + 14) = loc_ebp; ???? */
			if (!(ret = lzma_upack_esi_00(&p, loc_edx, bs, bl))) {
				/* loc_48397c */
				loc_eax--;
				/*
				temp_ebp = loc_ebp; loc_ebp = cli_readint32(loc_ebx+0x0c); cli_writeint32(loc_ebx+0x0c, temp_ebp);
				temp_ebp = loc_ebp; loc_ebp = cli_readint32(loc_ebx+0x10); cli_writeint32(loc_ebx+0x10, temp_ebp);
				*/
				temp_ebp = loc_ebp;
				loc_ebp = state[4];
				state[4] = state[3];
				state[3] = temp_ebp;
				eax_copy = loc_eax;
				loc_edx = loc_ebx + 0xbc0;
				state[5] = loc_ebp;
				if (lzma_upack_esi_54(&p, loc_eax, &loc_ecx, &loc_edx, &temp, bs, bl) == 0xffffffff)
					return -1;
				loc_ecx = 3;
				jakas_kopia = temp;
				loc_eax = temp-1;
				if (loc_eax >= loc_ecx)
					loc_eax = loc_ecx;
				loc_ecx = 0x40;
				loc_eax <<= 6; /* ecx=0x40, mul cl */
				loc_ebp8 = loc_ebx + ((loc_eax<<2) + 0x378);
				if (lzma_upack_esi_50(&p, 1, loc_ecx, &loc_edx, loc_ebp8, &loc_eax, bs, bl) == 0xffffffff)
					return -1;
				loc_ebp = loc_eax;
				if ((loc_eax&0xff) >= 4)
				{
					/* loc_4839af */
					loc_ebp = 2 + (loc_eax&1);
					loc_eax >>= 1;
					loc_eax--;
					temp_ebp = loc_eax; loc_eax = loc_ecx; loc_ecx = temp_ebp;
					loc_ebp <<= (loc_ecx&0xff);
					loc_edx = loc_ebx + (loc_ebp<<2) + 0x178;
					if ((loc_ecx&0xff) > 5)
					{
						/* loc_4839c6 */
						loc_ecx = (loc_ecx&0xffffff00)|(((loc_ecx&0xff)-4)&0xff);
						loc_eax = 0;
						do {
							uint32_t temp_edx;
							/* compare with lzma_upack_esi_00 */
							/* do not put in one statment because of difference in signedness */
							if (!CLI_ISCONTAINED(bs, bl, p.p0, 4))
								return -1;
							temp_edx = cli_readint32((char *)p.p0);
							temp_edx = EC32(CE32(temp_edx));
							p.p1 >>= 1;
							temp_edx -= p.p2;
							loc_eax <<= 1;
							if (temp_edx >= p.p1)
							{
								temp_edx = p.p1;
								loc_eax++;
								p.p2 += temp_edx;
							}
							if(((p.p1)&0xff000000) == 0)
							{
								p.p2 <<= 8;
								p.p1 <<= 8;
								p.p0++;
							}
						} while (--loc_ecx);
						/* loc_4839e8 */
						loc_ecx = (loc_ecx&0xffffff00)|4;
						loc_eax <<= 4;
						loc_ebp += loc_eax;
						loc_edx = loc_ebx + 0x18;
					}
					/* loc4839f1 */
					loc_eax = 1;
					loc_eax <<= (loc_ecx&0xff);
					loc_ebp8 = loc_edx;
					temp_ebp = loc_ecx; loc_ecx = loc_eax; loc_eax = temp_ebp;
					if (lzma_upack_esi_50(&p, 1, loc_ecx, &loc_edx, loc_ebp8, &loc_eax, bs, bl) == 0xffffffff)
						return -1;
					/* cdq, loc_edx = (loc_eax&0x80000000)?0xffffffff:0; */
					loc_ecx = temp_ebp;
					temp_ebp = CLI_SRS((int32_t)loc_eax, 31); /* thx, desp */
					/* loc_483a00 */
					do {
						temp_ebp += temp_ebp;
						temp_ebp += (loc_eax&1);
						loc_eax >>= 1;
					} while (--loc_ecx);
					loc_ebp += temp_ebp;
					/* loc_483a06 */
				}
				/* loc_483a09 */
				loc_ebp++;
				loc_ecx = jakas_kopia;
			} else {
				/* loc_48393a */
				loc_edx += loc_ecx;
				if ((ret = lzma_upack_esi_00(&p, loc_edx, bs, bl))) {
					/* loc_483954 */
					loc_edx += 0x60;
					if ((ret = lzma_upack_esi_00(&p, loc_edx, bs, bl))) {
						/* loc_48395e */
						loc_edx += loc_ecx;
						ret = lzma_upack_esi_00(&p, loc_edx, bs, bl);
						temp_ebp = loc_ebp;
						loc_ebp = state[4];
						state[4] = state[3];
						state[3] = temp_ebp;
						if (ret)
						{
							temp_ebp = loc_ebp; loc_ebp = state[5]; state[5] = temp_ebp;
						}
					} else {
						temp_ebp = loc_ebp; loc_ebp = state[3]; state[3] = temp_ebp;
					}
				} else {
					/* loc_483940 */
					loc_edx += loc_ecx;
					if ((ret = lzma_upack_esi_00(&p, loc_edx, bs, bl))) {
					} else {
						/* loc_483946 */
						loc_eax |= 1;
						eax_copy = loc_eax;
						edi_copy = loc_edi;
						edi_copy -= state[2];
						loc_ecx = (loc_ecx&0xffffff00)|0x80;
						if (!CLI_ISCONTAINED(bs, bl, edi_copy, 1) || !CLI_ISCONTAINED(bs, bl, loc_edi, 1))
							return -1;
						loc_al = (*(uint8_t *)edi_copy)&0xff;
						/* loc_483922 */
						/* ok jmp to 483a19 */
						/* loc_483a19 */
						*loc_edi++ = loc_al;
						continue;
					}
				}
				/* loc_48396a */
				eax_copy = loc_eax;
				loc_edx = loc_ebx + 0x778;
				if (lzma_upack_esi_54(&p, loc_eax, &loc_ecx, &loc_edx, &temp, bs, bl) == 0xffffffff)
					return -1;
				loc_eax = loc_ecx;
				loc_ecx = temp;
			}
			/* loc_483a0b */
			if (!CLI_ISCONTAINED(bs, bl, loc_edi, loc_ecx) || !CLI_ISCONTAINED(bs, bl, loc_edi-loc_ebp, loc_ecx+1))
				return -1;
			state[2] = loc_ebp;
			for (i=0; i<loc_ecx; i++, loc_edi++)
				*loc_edi = *(loc_edi - loc_ebp);
			loc_eax = (loc_eax&0xffffff00)|*(uint8_t *)(loc_edi - loc_ebp);
			loc_ecx = 0x80;
		} else {
			/* loc_4838d8 */
			do {
				if ( (loc_al = (loc_eax&0xff)) + 0xfd > 0xff)
					loc_al -= 3; /* 0x100 - 0xfd = 3 */
				else
					loc_al = 0;
				loc_eax = (loc_eax&0xffffff00)|loc_al;
			} while (loc_al >= 7);
			/* loc_4838e2 */
			eax_copy = loc_eax;
			if (loc_edi > init_edi && loc_edi < bl+bs)
			{
				loc_ebp = (*(uint8_t *)(loc_edi - 1)) >> shlsize;
			} else {
				loc_ebp = 0;
			}
			loc_ebp *= (int)0x300; /* XXX */
			loc_ebp8 = loc_ebx + ((loc_ebp<<2) + 0x1008);
			/* XXX save edi */
			edi_copy = loc_edi;

			loc_eax = (loc_eax&0xffffff00)|1;
			if (loc_ecx) {
				uint8_t loc_cl = loc_ecx&0xff;
				loc_edi -= state[2];
				if (!CLI_ISCONTAINED(bs, bl, loc_edi, 1))
					return -1;
				do {
					loc_eax = (loc_eax&0xffff00ff)|((*loc_edi & loc_cl)?0x200:0x100);

					loc_edx = loc_ebp8 + (loc_eax<<2);
					ret = lzma_upack_esi_00(&p, loc_edx, bs, bl);
					loc_al = loc_eax&0xff;
					loc_al += loc_al;
					loc_al += ret;
					loc_al &= 0xff;
					loc_eax = (loc_eax&0xffffff00)|loc_al;
					loc_cl >>= 1;
					if (loc_cl) {
						uint8_t loc_ah = (loc_eax>>8)&0xff;
						loc_ah -= loc_al;
						loc_ah &= 1;
						if (!loc_ah)
						{
							loc_eax = (loc_eax&0xffff0000)|(loc_ah<<8)|loc_al;
							/* loc_483918, loc_48391a */
							if (lzma_upack_esi_50(&p, loc_eax, 0x100, &loc_edx, loc_ebp8, &loc_eax, bs, bl) == 0xffffffff)
								return -1;
							break;
						}
					} else
						break;
				} while(1);
			} else {
				/* loc_48391a */
				loc_ecx = (loc_ecx&0xffff00ff)|0x100;
				if (lzma_upack_esi_50(&p, loc_eax, loc_ecx, &loc_edx, loc_ebp8, &loc_eax, bs, bl) == 0xffffffff)
					return -1;
			}
			/* loc_48391f */
			loc_ecx = 0;
			loc_edi = edi_copy;
		}
		/* loc_483a19 */
		/* 2GiM: i think this one is not properly checked, 2aCaB: true */
		if (!CLI_ISCONTAINED(bs, bl, loc_edi, 1))
			return -1;
		*loc_edi++ = (loc_eax&0xff);
	} while (loc_edi < end_edi);

	return 1;
}
