aboutsummaryrefslogtreecommitdiff
package main

import (
	"fmt"
)

func (explainer *explainer) walkDelta(base, payload []byte, pos int) ([]byte, bool, error) {
	explainer.printf("\tdelta\n")

	building := base != nil

	var result []byte

	insn := 0

	for pos < len(payload) {
		op := payload[pos]
		pos++
		insn++

		switch {
		case op&0x80 != 0:
			next, seg, err := explainer.decodeCopy(base, payload, pos, op)
			if err != nil {
				return nil, false, err
			}

			pos = next

			if building {
				result = append(result, seg...)
			}
		case op != 0:
			next, lit, err := explainer.decodeInsert(payload, pos, int(op))
			if err != nil {
				return nil, false, err
			}

			pos = next

			if building {
				result = append(result, lit...)
			}
		default:
			explainer.printf("\t\tinvalid opcode 0x00; stopping delta decode\n")

			return nil, false, nil
		}
	}

	if !building {
		return nil, false, nil
	}

	return result, true, nil
}

func (explainer *explainer) decodeCopy(base, payload []byte, pos int, op byte) (int, []byte, error) {
	offset := 0

	for i := range 4 {
		if op&(1<<uint(i)) == 0 {
			continue
		}

		if pos >= len(payload) {
			return 0, nil, fmt.Errorf("truncated copy offset")
		}

		offset |= int(payload[pos]) << (8 * uint(i))
		pos++
	}

	size := 0

	for i := range 3 {
		if op&(1<<uint(4+i)) == 0 {
			continue
		}

		if pos >= len(payload) {
			return 0, nil, fmt.Errorf("truncated copy size")
		}

		size |= int(payload[pos]) << (8 * uint(i))
		pos++
	}

	if size == 0 {
		size = 0x10000
	}

	explainer.printf("\t\tcpy %d from %d\n", size, offset)

	if base == nil {
		return pos, nil, nil
	}

	if offset < 0 || offset+size > len(base) {
		return 0, nil, fmt.Errorf("copy of %d byte(s) from base offset %d exceeds base length %d", size, offset, len(base))
	}

	seg := base[offset : offset+size]
	hexBlock(explainer.out, "\t\t\t", seg)

	return pos, seg, nil
}

func (explainer *explainer) decodeInsert(payload []byte, pos, n int) (int, []byte, error) {
	if pos+n > len(payload) {
		return 0, nil, fmt.Errorf("truncated insert payload")
	}

	lit := payload[pos : pos+n]

	explainer.printf("\t\tins %d\n", n)
	hexBlock(explainer.out, "\t\t\t", lit)

	return pos + n, lit, nil
}