// File: /workspace/github_c_makefile_star2_O3/jsummers[P]deark/modules/ilbm.c
// Function: decompress_plane_vdelta
static void decompress_plane_vdelta(deark *c, lctx *d, struct imgbody_info *ibi,
	struct frame_ctx *frctx, i64 plane_idx,
	dbuf *inf, i64 codepos1, i64 datapos1, i64 endpos,
	i64 code_size, i64 dataelem_size, int separate_data_stream, u8 xor_mode)
{
	i64 pos = codepos1; // If !separate_data_stream, this is for code and data
	i64 datapos = datapos1;
	i64 num_columns;
	i64 col;
	i64 dststride = ibi->frame_buffer_rowspan;
	UI unc_threshold;
	u8 last_col_quirk = 0;
	int baddata_flag = 0;

	if(separate_data_stream) {
		de_dbg2(c, "vdelta(%d,%d) plane %d at (%"I64_FMT",%"I64_FMT")", (int)code_size,
			(int)dataelem_size, (int)plane_idx, codepos1, datapos1);
	}
	else {
		de_dbg2(c, "vdelta(%d) plane at (%"I64_FMT")", (int)code_size, codepos1);
	}
	if(code_size!=1 && code_size!=2 && code_size!=4) goto done;
	if(dataelem_size!=1 && dataelem_size!=2 && dataelem_size!=4) goto done;
	if(xor_mode && dataelem_size!=1) goto done;

	if(code_size==1) {
		unc_threshold = 0x80;
	}
	else if(code_size==2) {
		unc_threshold = 0x8000;
	}
	else {
		unc_threshold = 0x80000000U;
	}

	if(dataelem_size==1) {
		num_columns = (ibi->width+7)/8;
	}
	else if(dataelem_size==2) {
		num_columns = (ibi->width+15)/16;
	}
	else {
		num_columns = (ibi->width+31)/32;
	}

	if(frctx->op==8 && dataelem_size==4) {
		i64 num_columns_if_size2;

		num_columns_if_size2 = (ibi->width+15)/16;
		if(num_columns*2 != num_columns_if_size2) {
			// A dumb quirk of ANIM-8: The last column may use "word" mode,
			// even when when all other columns use "long" mode.
			last_col_quirk = 1;
		}
	}

	for(col=0; col<num_columns; col++) {
		i64 opcount;
		i64 opidx;
		i64 elem_bytes_to_write;
		i64 ypos = 0;
		i64 col_start_dstpos;
		i64 dataelem_size_thiscol;
		i64 code_size_thiscol;
		UI unc_threshold_thiscol;
		u8 is_last_col;

		if(c->debug_level>=3) {
			de_dbg3(c, "col %u at %"I64_FMT, (UI)col, pos);
		}

		is_last_col = (col+1 == num_columns);
		code_size_thiscol = code_size;
		dataelem_size_thiscol = dataelem_size;
		unc_threshold_thiscol = unc_threshold;
		if(is_last_col && last_col_quirk) {
			code_size_thiscol = 2;
			dataelem_size_thiscol = 2;
			unc_threshold_thiscol = 0x8000;
		}

		if(pos>=endpos) {
			baddata_flag = 1;
			goto done;
		}

		// Defend against writing beyond the right edge of this plane
		if(is_last_col && (dataelem_size_thiscol==4) && (ibi->bytes_per_row_per_plane%4)) {
			elem_bytes_to_write = 2;
		}
		else {
			elem_bytes_to_write = dataelem_size_thiscol;
		}

		col_start_dstpos = plane_idx * ibi->bytes_per_row_per_plane + dataelem_size*col;

		opcount = get_elem_as_int_p(inf, &pos, code_size_thiscol);
		if(c->debug_level>=3) {
			de_dbg3(c, "col %d op count: %"I64_FMT, (int)col, opcount);
		}

		for(opidx=0; opidx<opcount; opidx++) {
			i64 dstpos;
			i64 count;
			i64 k;
			UI op;
			u8 valbuf[4];

			if(pos>=endpos) {
				baddata_flag = 1;
				goto done;
			}
			op = (UI)get_elem_as_int_p(inf, &pos, code_size_thiscol);

			if(op==0) { // RLE
				count = get_elem_as_int_p(inf, &pos, code_size_thiscol);
				if(ypos+count > ibi->height) {
					// TODO: Should we tolerate this, and set count = 0?
					baddata_flag = 1;
					goto done;
				}

				if(count>0) {
					frctx->change_flag = 1;
				}

				if(separate_data_stream) {
					if(datapos>=endpos) {
						baddata_flag = 1;
						goto done;
					}
					dbuf_read(inf, valbuf, datapos, dataelem_size_thiscol);
					datapos += dataelem_size_thiscol;
				}
				else {
					dbuf_read(inf, valbuf, pos, dataelem_size_thiscol);
					pos += dataelem_size_thiscol;
				}

				for(k=0; k<count; k++) {
					dstpos = col_start_dstpos + ypos*dststride;
					if(xor_mode) {
						u8 val;

						val = valbuf[0] ^ dbuf_getbyte(frctx->frame_buffer, dstpos);
						dbuf_writebyte_at(frctx->frame_buffer, dstpos, val);
					}
					else {
						dbuf_write_at(frctx->frame_buffer, dstpos, valbuf, elem_bytes_to_write);
					}
					ypos++;
				}
			}
			else if(op < unc_threshold_thiscol) { // skip
				ypos += (i64)op;
			}
			else { // uncompressed run
				count = (i64)(op - unc_threshold_thiscol);
				if(ypos+count > ibi->height) {
					baddata_flag = 1;
					goto done;
				}

				if(count>0) {
					frctx->change_flag = 1;
				}

				for(k=0; k<count; k++) {
					if(separate_data_stream) {
						if(datapos>=endpos) {
							baddata_flag = 1;
							goto done;
						}
						dbuf_read(inf, valbuf, datapos, dataelem_size_thiscol);
						datapos += dataelem_size_thiscol;
					}
					else {
						dbuf_read(inf, valbuf, pos, dataelem_size_thiscol);
						pos += dataelem_size_thiscol;
					}

					dstpos = col_start_dstpos + ypos*dststride;
					if(xor_mode) {
						valbuf[0] ^= dbuf_getbyte(frctx->frame_buffer, dstpos);
					}
					dbuf_write_at(frctx->frame_buffer, dstpos, valbuf, elem_bytes_to_write);
					ypos++;
				}
			}
		}
	}

done:
	if(baddata_flag && !d->errflag) {
		de_err(c, "Delta decompression failed");
		d->errflag = 1;
	}
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xb8, %rsp
movq %r9, %r14
movq %r8, %r12
movq %rcx, %r13
movq %rsi, 0x88(%rsp)
movq %rdi, %r15
movq 0x110(%rsp), %rbp
movq 0x108(%rsp), %rbx
movq 0xf8(%rsp), %rax
movq 0xf0(%rsp), %r9
movq %r9, 0x18(%rsp)
movq %rdx, 0x60(%rsp)
movq 0x48(%rdx), %rcx
movq %rcx, 0x68(%rsp)
cmpl $0x0, 0x118(%rsp)
movq %rax, 0x38(%rsp)
je 0x7c7e4
movq %rax, (%rsp)
leaq 0xac96a(%rip), %rsi     # 0x12913b
movq %r15, %rdi
movl %ebx, %edx
movl %ebp, %ecx
movl %r12d, %r8d
xorl %eax, %eax
callq 0xe87e4
jmp 0x7c7fa
leaq 0xac974(%rip), %rsi     # 0x12915f
movq %r15, %rdi
movl %ebx, %edx
movq %r9, %rcx
xorl %eax, %eax
callq 0xe87e4
cmpq $0x4, %rbx
ja 0x7ccd9
movl $0x16, %eax
btq %rbx, %rax
jae 0x7ccd9
cmpq $0x4, %rbp
ja 0x7ccd9
btq %rbp, %rax
jae 0x7ccd9
cmpq $0x1, %rbp
setne %al
cmpb $0x0, 0x120(%rsp)
setne %cl
testb %cl, %al
jne 0x7ccd9
cmpq $0x2, %rbx
movl $0x8000, %eax           # imm = 0x8000
movl $0x80000000, %ecx       # imm = 0x80000000
cmovel %eax, %ecx
cmpq $0x1, %rbx
movl $0x80, %eax
cmovnel %ecx, %eax
movl %eax, 0x74(%rsp)
movq 0x60(%rsp), %rax
movq (%rax), %rax
cmpq $0x2, %rbp
je 0x7c88f
cmpq $0x1, %rbp
jne 0x7cceb
leaq 0x7(%rax), %rcx
addq $0xe, %rax
testq %rcx, %rcx
cmovnsq %rcx, %rax
sarq $0x3, %rax
jmp 0x7c8a2
leaq 0xf(%rax), %rcx
addq $0x1e, %rax
testq %rcx, %rcx
cmovnsq %rcx, %rax
sarq $0x4, %rax
movl $0x0, 0x24(%rsp)
movq %rax, %rdx
testq %rdx, %rdx
jle 0x7ccd9
movl $0x2, %esi
movq $0x0, 0x80(%rsp)
xorl %ebp, %ebp
movq %r15, 0x78(%rsp)
movq %r14, 0x30(%rsp)
movq %r12, 0xa0(%rsp)
movq %rdx, 0x98(%rsp)
cmpl $0x3, (%r15)
movq 0x18(%rsp), %rcx
jl 0x7c919
movq %r15, %rdi
leaq 0xac881(%rip), %rsi     # 0x129179
movq %rdx, %rbx
movl %ebp, %edx
xorl %eax, %eax
callq 0xe889f
movl $0x2, %esi
movq %rbx, %rdx
movq 0x108(%rsp), %rbx
movq 0x18(%rsp), %rcx
leaq 0x1(%rbp), %rdi
cmpq %rdx, %rdi
sete %al
testb %al, 0x24(%rsp)
movq 0x110(%rsp), %r14
cmovneq %rsi, %r14
cmovneq %rsi, %rbx
movl 0x74(%rsp), %r8d
movl $0x8000, %eax           # imm = 0x8000
cmovnel %eax, %r8d
movl %r8d, 0x54(%rsp)
cmpq 0x100(%rsp), %rcx
jge 0x7ccab
movq %rdi, 0xa8(%rsp)
movq %rdi, %rax
xorq %rdx, %rax
movq %r14, %rcx
xorq $0x4, %rcx
movq 0x60(%rsp), %rdx
movq 0x40(%rdx), %rdx
movq %rdx, 0x58(%rsp)
testb $0x3, %dl
movq %r14, %rdx
cmovneq %rsi, %rdx
orq %rax, %rcx
cmovneq %r14, %rdx
movq %rdx, 0x90(%rsp)
movq 0x30(%rsp), %rdi
leaq 0x18(%rsp), %rsi
movq %rbx, 0xb0(%rsp)
movq %rbx, %rdx
callq 0x7cd41
movq %rax, %rbx
cmpl $0x3, (%r15)
jl 0x7c9cd
movq %r15, %rdi
leaq 0xac7c6(%rip), %rsi     # 0x129187
movl %ebp, %edx
movq %rbx, %rcx
xorl %eax, %eax
callq 0xe889f
testq %rbx, %rbx
jle 0x7cc61
movq 0x58(%rsp), %rax
imulq %r12, %rax
addq 0x80(%rsp), %rax
movq %rax, 0x58(%rsp)
movq $0x0, 0x28(%rsp)
xorl %eax, %eax
movq %rbx, 0x40(%rsp)
movq %rax, 0x48(%rsp)
movq 0x100(%rsp), %rax
cmpq %rax, 0x18(%rsp)
jge 0x7cca6
movq 0x30(%rsp), %rbx
movq %rbx, %rdi
leaq 0x18(%rsp), %r15
movq %r15, %rsi
movq 0xb0(%rsp), %r12
movq %r12, %rdx
callq 0x7cd41
testl %eax, %eax
je 0x7ca50
movl %eax, %ecx
subl 0x54(%rsp), %ecx
jae 0x7cac8
movl %eax, %eax
addq %rax, 0x28(%rsp)
jmp 0x7cc4b
movq %rbx, %rdi
movq %r15, %rsi
movq %r12, %rdx
callq 0x7cd41
movq %rax, %rbp
movq 0x28(%rsp), %rax
addq %rbp, %rax
movq 0x60(%rsp), %rcx
cmpq 0x8(%rcx), %rax
jg 0x7cca6
testq %rbp, %rbp
jle 0x7ca85
movl $0x1, 0xc(%r13)
cmpl $0x0, 0x118(%rsp)
movq 0x38(%rsp), %rbx
je 0x7cbb7
cmpq 0x100(%rsp), %rbx
jge 0x7cca6
movq 0x30(%rsp), %rdi
leaq 0x14(%rsp), %rsi
movq 0x38(%rsp), %rbx
movq %rbx, %rdx
movq %r14, %rcx
callq 0xee8d4
addq %r14, %rbx
jmp 0x7cbd3
movq 0x28(%rsp), %rdx
addq %rcx, %rdx
movq 0x60(%rsp), %rsi
cmpq 0x8(%rsi), %rdx
jg 0x7cca6
cmpl 0x54(%rsp), %eax
je 0x7cc4b
movl $0x1, 0xc(%r13)
cmpq $0x1, %rcx
movl $0x0, %r12d
sbbq %rcx, %r12
movq 0x68(%rsp), %rbp
imulq 0x28(%rsp), %rbp
addq 0x58(%rsp), %rbp
xorl %r15d, %r15d
cmpl $0x0, 0x118(%rsp)
je 0x7cb52
movq 0x100(%rsp), %rax
cmpq %rax, 0x38(%rsp)
jge 0x7cca6
movq 0x30(%rsp), %rdi
leaq 0x14(%rsp), %rsi
movq 0x38(%rsp), %rbx
movq %rbx, %rdx
movq %r14, %rcx
callq 0xee8d4
addq %r14, %rbx
movq %rbx, 0x38(%rsp)
jmp 0x7cb6e
movq 0x18(%rsp), %rdx
movq 0x30(%rsp), %rdi
leaq 0x14(%rsp), %rsi
movq %r14, %rcx
callq 0xee8d4
addq %r14, 0x18(%rsp)
cmpb $0x0, 0x120(%rsp)
je 0x7cb88
movq 0x20(%r13), %rdi
movq %rbp, %rsi
callq 0xeead3
xorb %al, 0x14(%rsp)
movq 0x20(%r13), %rdi
movq %rbp, %rsi
leaq 0x14(%rsp), %rdx
movq 0x90(%rsp), %rcx
callq 0xf0c11
decq %r15
addq 0x68(%rsp), %rbp
cmpq %r15, %r12
jne 0x7cb11
jmp 0x7cc46
movq 0x18(%rsp), %rdx
movq 0x30(%rsp), %rdi
leaq 0x14(%rsp), %rsi
movq %r14, %rcx
callq 0xee8d4
addq %r14, 0x18(%rsp)
movq %rbx, 0x38(%rsp)
testq %rbp, %rbp
jle 0x7cc4b
negq %rbp
movq 0x68(%rsp), %r12
imulq 0x28(%rsp), %r12
addq 0x58(%rsp), %r12
xorl %r15d, %r15d
cmpb $0x0, 0x120(%rsp)
je 0x7cc20
movb 0x14(%rsp), %bl
movq 0x20(%r13), %rdi
movq %r12, %rsi
callq 0xeead3
xorb %bl, %al
movq 0x20(%r13), %rdi
movzbl %al, %edx
movq %r12, %rsi
callq 0xf0e8b
jmp 0x7cc39
movq 0x20(%r13), %rdi
movq %r12, %rsi
leaq 0x14(%rsp), %rdx
movq 0x90(%rsp), %rcx
callq 0xf0c11
decq %r15
addq 0x68(%rsp), %r12
cmpq %r15, %rbp
jne 0x7cbf3
subq %r15, 0x28(%rsp)
movq 0x40(%rsp), %rbx
movq 0x48(%rsp), %rax
incq %rax
cmpq %rbx, %rax
jne 0x7c9fc
movq 0x110(%rsp), %rax
addq %rax, 0x80(%rsp)
movq 0x98(%rsp), %rdx
movq 0xa8(%rsp), %rbp
cmpq %rdx, %rbp
movq 0x78(%rsp), %r15
movq 0xa0(%rsp), %r12
movq 0x108(%rsp), %rbx
movl $0x2, %esi
jne 0x7c8e3
jmp 0x7ccd9
movq 0x78(%rsp), %r15
movq 0x88(%rsp), %rax
cmpl $0x0, 0x1c(%rax)
jne 0x7ccd9
leaq 0xac4dc(%rip), %rsi     # 0x12919c
movq %r15, %rdi
xorl %eax, %eax
callq 0xe9295
movq 0x88(%rsp), %rax
movl $0x1, 0x1c(%rax)
addq $0xb8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x1f(%rax), %rcx
leaq 0x3e(%rax), %rdx
testq %rcx, %rcx
cmovnsq %rcx, %rdx
sarq $0x5, %rdx
movl $0x0, 0x24(%rsp)
cmpq $0x4, %rbp
jne 0x7c8ad
cmpb $0x8, 0x10(%r13)
jne 0x7c8ad
leaq 0xf(%rax), %rcx
addq $0x1e, %rax
testq %rcx, %rcx
cmovnsq %rcx, %rax
sarq $0x4, %rax
leaq (%rdx,%rdx), %rcx
cmpq %rax, %rcx
setne %al
movl %eax, 0x24(%rsp)
jmp 0x7c8ad