/* filter.h --

   This file is part of the UPX executable compressor.

   Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer
   Copyright (C) 1996-2024 Laszlo Molnar
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   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; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer              Laszlo Molnar
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
 */

#pragma once

/*************************************************************************
// A filter is a reversible operation that modifies a given
// block of memory.
//
// A filter can fail and return false. In this case the buffer
// must be unmodified (or otherwise restored).
//
// If a filter fails and somehow cannot restore the block it must
// call throwFilterException() - this will cause the compression
// to fail.
//
// Unfilters throw exceptions in case of errors.
//
// The main idea behind filters is to convert relative jumps and calls
// to absolute addresses so that the buffer compresses better.
**************************************************************************/

class Filter final {
public:
    explicit Filter(int level) noexcept;
    void init(int id = 0, unsigned addvalue = 0) noexcept;

    bool filter(SPAN_0(byte) buf, unsigned buf_len);
    void unfilter(SPAN_0(byte) buf, unsigned buf_len, bool verify_checksum = false);
    void verifyUnfilter();
    bool scan(SPAN_0(const byte) buf, unsigned buf_len);

    static bool isValidFilter(int filter_id);
    static bool isValidFilter(int filter_id, const int *allowed_filters);

public:
    // Will be set by each call to filter()/unfilter().
    // Read-only afterwards.
    byte *buf = nullptr;
    unsigned buf_len = 0;

    // Checksum of the buffer before applying the filter
    // or after un-applying the filter.
    unsigned adler;

    // Input parameters used by various filters.
    unsigned addvalue;
    const int *preferred_ctos = nullptr;

    // Input/output parameters used by various filters
    byte cto; // call trick offset

    // Output used by various filters. Read only.
    unsigned calls;
    unsigned noncalls;
    unsigned wrongcalls;
    unsigned firstcall;
    unsigned lastcall;
    unsigned n_mru; // ctojr only

    // Read only.
    int id;

private:
    int clevel; // compression level
};

/*************************************************************************
// We don't want a full OO interface here because of
// certain implementation speed reasons.
//
// This class is private to Filter - don't look.
**************************************************************************/

class FilterImpl final {
    friend class Filter;

private:
    explicit FilterImpl() noexcept DELETED_FUNCTION;

    struct FilterEntry {
        int id; // 0 .. 255
        unsigned min_buf_len;
        unsigned max_buf_len;
        int (*do_filter)(Filter *);   // filter a buffer
        int (*do_unfilter)(Filter *); // unfilter a buffer
        int (*do_scan)(Filter *);     // scan a buffer
    };

    // get a specific filter entry
    static const FilterEntry *getFilter(int id);

private:
    // strictly private filter database
    static const FilterEntry filters[];
    static const int n_filters; // number of filters[]
};

/* vim:set ts=4 sw=4 et: */
