code:
<?php

/*
 * This file is part of the Stomp package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stomp\Transport;

/**
 * Message that contains a stream of uninterpreted bytes
 *
 * @package Stomp
 */
class Bytes extends Message
{
    /**
     * Constructor
     *
     * @param string $body
     * @param array $headers
     */
    public function __construct($body, array $headers = [])
    {
        parent::__construct($body, $headers);
        $this->headers['content-type'] = 'application/octet-stream';
        $this->expectLengthHeader(true);
    }

    /**
     * @inheritdoc
     */
    protected function getBodySize()
    {
        return count(unpack('c*', $this->getBody()));
    }
}


code:
<?php

/*
 * This file is part of the Stomp package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stomp\Transport;

use ArrayAccess;

/**
 * Stomp Frames are messages that are sent and received on a stomp connection.
 *
 * @package Stomp
 */
class Frame implements ArrayAccess
{
    /**
     * Stomp Command
     *
     * @var string
     */
    protected $command;

    /**
     * Frame Headers
     *
     * @var array
     */
    protected $headers;

    /**
     * Frame Content
     *
     * @var mixed
     */
    public $body;

    /**
     * Frame should set an content-length header on transmission
     *
     * @var bool
     */
    private $addLengthHeader = false;

    /**
     * Frame is in stomp 1.0 mode
     *
     * @var bool
     */
    private $legacyMode = false;

    /**
     * Constructor
     *
     * @param string $command
     * @param array  $headers
     * @param string $body
     */
    public function __construct($command = null, array $headers = [], $body = null)
    {
        $this->command = $command;
        $this->headers = $headers ?: [];
        $this->body = $body;
    }

    /**
     * Add given headers to currently set headers.
     *
     * Will override existing keys.
     *
     * @param array $header
     * @return Frame
     */
    public function addHeaders(array $header)
    {
        $this->headers += $header;
        return $this;
    }

    /**
     * Stomp message Id
     *
     * @return string
     */
    public function getMessageId()
    {
        return $this['message-id'];
    }

    /**
     * Is error frame.
     *
     * @return boolean
     */
    public function isErrorFrame()
    {
        return ($this->command == 'ERROR');
    }

    /**
     * Tell the frame that we expect an length header.
     *
     * @param bool|false $expected
     */
    public function expectLengthHeader($expected = false)
    {
        $this->addLengthHeader = $expected;
    }

    /**
     * Enable legacy mode for this frame
     *
     * @param bool|false $legacy
     */
    public function legacyMode($legacy = false)
    {
        $this->legacyMode = $legacy;
    }

    /**
     * Frame is in legacy mode.
     *
     * @return bool
     */
    public function isLegacyMode()
    {
        return $this->legacyMode;
    }

    /**
     * Command
     *
     * @return string
     */
    public function getCommand()
    {
        return $this->command;
    }

    /**
     * Body
     *
     * @return string
     */
    public function getBody()
    {
        return $this->body;
    }

    /**
     * Headers
     *
     * @return array
     */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
     * Convert frame to transportable string
     *
     * @return string
     */
    public function __toString()
    {
        $data = $this->command . "\n";

        if (!$this->legacyMode) {
            if ($this->body && ($this->addLengthHeader || stripos($this->body, "\x00") !== false)) {
                $this['content-length'] = $this->getBodySize();
            }
        }

        foreach ($this->headers as $name => $value) {
            $data .= $this->encodeHeaderValue($name) . ':' . $this->encodeHeaderValue($value) . "\n";
        }

        $data .= "\n";
        $data .= $this->body;
        return $data . "\x00";
    }

    /**
     * Size of Frame body.
     *
     * @return int
     */
    protected function getBodySize()
    {
        return strlen($this->body);
    }

    /**
     * Encodes header values.
     *
     * @param string $value
     * @return string
     */
    protected function encodeHeaderValue($value)
    {
        if ($this->legacyMode) {
            return str_replace(["\n"], ['\n'], $value);
        }
        return str_replace(["\\", "\r", "\n", ':'], ["\\\\", '\r', '\n', '\c'], $value);
    }

    /**
     * @inheritdoc
     */
    public function offsetExists($offset)
    {
        return isset($this->headers[$offset]);
    }

    /**
     * @inheritdoc
     */
    public function offsetGet($offset)
    {
        if (isset($this->headers[$offset])) {
            return $this->headers[$offset];
        }
        return null;
    }

    /**
     * @inheritdoc
     */
    public function offsetSet($offset, $value)
    {
        if ($value !== null) {
            $this->headers[$offset] = $value;
        }
    }

    /**
     * @inheritdoc
     */
    public function offsetUnset($offset)
    {
        unset($this->headers[$offset]);
    }
}

license: mit


code:
<?php

/*
 * This file is part of the Stomp package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stomp\Transport;

/**
 * FrameFactory defines how new frames are created after the frame details have been extracted.
 *
 * @package Stomp\Transport
 * @author Jens Radtke <swefl.oss@fin-sn.de>
 */
class FrameFactory
{
    /**
     * @var callable[]
     */
    private $resolver = [];

    /**
     * FrameFactory constructor.
     */
    public function __construct()
    {
        // register default additional builtin resolvers
        $this->resolver[] =
            function ($command, array $headers, $body) {
                if (isset($headers['transformation']) && strcasecmp($headers['transformation'], 'jms-map-json') == 0) {
                    return new Map($body, $headers, $command);
                }
            };
    }

    /**
     * Creates a frame instance out of the given frame details.
     *
     * @param string $command
     * @param array $headers
     * @param string $body
     * @param boolean $legacyMode stomp 1.0 mode (headers)
     * @return Frame
     */
    public function createFrame($command, array $headers, $body, $legacyMode)
    {
        foreach ($this->resolver as $resolver) {
            if ($frame = $resolver($command, $headers, $body, $legacyMode)) {
                return $frame;
            }
        }
        return $this->defaultFrame($command, $headers, $body, $legacyMode);
    }

    /**
     * Creates a new default frame instance.
     *
     * @param string $command
     * @param array $headers
     * @param string $body
     * @param boolean $legacyMode
     * @return Frame
     */
    private function defaultFrame($command, array $headers, $body, $legacyMode)
    {
        $frame = new Frame($command, $headers, $body);
        $frame->legacyMode($legacyMode);
        return $frame;
    }

    /**
     * Register a new resolver inside this frame factory.
     *
     * The new resolver becomes the first one which will be used to handle the frame create request.
     * The resolver must return null/false if he won't create a frame for the request.
     *
     * @param callable $callable
     * @return FrameFactory
     */
    public function registerResolver($callable)
    {
        array_unshift($this->resolver, $callable);
        return $this;
    }
}


code:
<?php

/*
 * This file is part of the Stomp package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stomp\Transport;

/**
 * Message that contains a set of name-value pairs
 *
 * @package Stomp
 */
class Map extends Message
{
    public $map;

    /**
     * Constructor
     *
     * @param array|object|string $body string will get decoded (receive), otherwise the body will be encoded (send)
     * @param array $headers
     * @param string $command
     */
    public function __construct($body, array $headers = [], $command = 'SEND')
    {
        if (is_string($body)) {
            parent::__construct($body, $headers);
            $this->command = $command;
            $this->map = json_decode($body, true);
        } else {
            parent::__construct(json_encode($body), $headers + ['transformation' => 'jms-map-json']);
            $this->command = $command;
            $this->map = $body;
        }
    }

    /**
     * Returns the received decoded json.
     *
     * @return mixed
     */
    public function getMap()
    {
        return $this->map;
    }
}


code:
<?php

/*
 * This file is part of the Stomp package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stomp\Transport;

/**
 * Basic text stomp message
 *
 * @package Stomp
 */
class Message extends Frame
{
    /**
     * Message constructor.
     *
     * @param string $body
     * @param array $headers
     */
    public function __construct($body, array $headers = [])
    {
        parent::__construct('SEND', $headers, $body);
    }
}


code:
<?php

/*
 * This file is part of the Stomp package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stomp\Transport;

use Stomp\Network\Observer\ConnectionObserver;

/**
 * A Stomp frame parser
 *
 * @package Stomp
 * @author Hiram Chirino <hiram@hiramchirino.com>
 * @author Dejan Bosanac <dejan@nighttale.net>
 * @author Michael Caplan <mcaplan@labnet.net>
 * @author Jens Radtke <swefl.oss@fin-sn.de>
 */
class Parser
{

    /**
     * Frame end
     */
    const FRAME_END = "\x00";

    /**
     * Frame that has been parsed last.
     *
     * @var Frame|null
     */
    private $frame;

    /**
     * Active Frame command
     *
     * @var string
     */
    private $command;

    /**
     * Active Frame headers
     *
     * @var array
     */
    private $headers = [];

    /**
     * Active Frame expected body size (content-length header)
     *
     * @var integer|null
     */
    private $expectedBodyLength;

    /**
     * Parser mode
     *
     * @var string
     */
    private $mode = self::MODE_HEADER;

    /**
     * Expecting header data mode
     */
    const MODE_HEADER = 'HEADER';

    /**
     * Expecting body end marker mode
     */
    const MODE_BODY = 'BODY';

    /**
     * Header end marker CR_LF
     */
    const HEADER_STOP_CR_LF = "\r\n\r\n";

    /**
     * Header end marker LF
     */
    const HEADER_STOP_LF = "\n\n";

    /**
     * Parser offset within buffer
     *
     * @var int
     */
    private $offset = 0;

    /**
     * Buffer size
     *
     * @var int
     */
    private $bufferSize;

    /**
     * Current buffer for new frames.
     *
     * @var string
     */
    private $buffer = '';

    /**
     * Parser is in stomp 1.0 mode
     *
     * @var bool
     */
    private $legacyMode = false;

    /**
     * @var FrameFactory
     */
    private $factory;

    /**
     * @var ConnectionObserver|null
     */
    private $observer;

    /**
     * Parser constructor.
     *
     * @param FrameFactory $factory
     */
    public function __construct(FrameFactory $factory = null)
    {
        $this->factory = $factory ?: new FrameFactory();
    }

    /**
     * Sets the observer for the parser, in order to receive heartbeat information.
     *
     * @param ConnectionObserver $observer
     * @return Parser
     */
    public function setObserver(ConnectionObserver $observer)
    {
        $this->observer = $observer;
        return $this;
    }

    /**
     * Returns the factory that will be used to create frame instances.
     *
     * @return FrameFactory
     */
    public function getFactory()
    {
        return $this->factory;
    }

    /**
     * Set parser in legacy mode.
     *
     * @param bool|false $legacy
     */
    public function legacyMode($legacy = false)
    {
        $this->legacyMode = $legacy;
    }

    /**
     * Add data to parse.
     *
     * @param string $data
     * @return void
     */
    public function addData($data)
    {
        $this->buffer .= $data;
    }

    /**
     * Get next parsed frame.
     *
     * @deprecated Will be removed in next version. Please use nextFrame().
     * @return Frame
     */
    public function getFrame()
    {
        return $this->frame;
    }

    /**
     * Parse current buffer and return the next available frame, otherwise return null.
     *
     * @return null|Frame
     */
    public function nextFrame()
    {
        if ($this->parse()) {
            $frame = $this->getFrame();
            $this->frame = null;
            if ($this->observer) {
                $this->observer->receivedFrame($frame);
            }
            return $frame;
        }
        return null;
    }

    /**
     * Parse current buffer for frames.
     *
     * @deprecated Will become private in next version. Please use nextFrame().
     *
     * @return bool
     */
    public function parse()
    {
        if ($this->buffer === '') {
            return false;
        }
        $this->frame = null;
        $this->offset = 0;
        $this->bufferSize = strlen($this->buffer);
        while ($this->offset < $this->bufferSize) {
            if ($this->mode === self::MODE_HEADER) {
                $this->skipEmptyLines();
                if ($this->detectFrameHead()) {
                    $this->mode = self::MODE_BODY;
                } else {
                    break;
                }
            }
            if ($this->detectFrameEnd()) {
                $this->mode = self::MODE_HEADER;
            }
            break;
        }

        if ($this->offset > 0) {
            // remove parsed buffer
            $this->buffer = substr($this->buffer, $this->offset);
        }
        return $this->frame !== null;
    }

    /**
     * Skips empty lines before frame headers (they are allowed after \00)
     */
    private function skipEmptyLines()
    {
        $foundHeartbeat = false;
        while ($this->offset < $this->bufferSize) {
            $char = substr($this->buffer, $this->offset, 1);
            if ($char === "\x00" || $char === "\n" || $char === "\r") {
                $this->offset++;
                $foundHeartbeat = true;
            } else {
                break;
            }
        }
        if ($foundHeartbeat && $this->observer) {
            $this->observer->emptyLineReceived();
        }
    }

    /**
     * Detect frame header end marker, starting from current offset.
     *
     * @return bool
     */
    private function detectFrameHead()
    {
        $firstCrLf = strpos($this->buffer, self::HEADER_STOP_CR_LF, $this->offset);
        $firstLf = strpos($this->buffer, self::HEADER_STOP_LF, $this->offset);

        // we need to use the first available marker, so we need to make sure that cr lf don't overrule lf
        if ($firstCrLf !== false && ($firstLf === false || $firstLf > $firstCrLf)) {
            $this->extractFrameMeta(substr($this->buffer, $this->offset, $firstCrLf - $this->offset));
            $this->offset = $firstCrLf + strlen(self::HEADER_STOP_CR_LF);
            return true;
        }

        if ($firstLf !== false) {
            $this->extractFrameMeta(substr($this->buffer, $this->offset, $firstLf - $this->offset));
            $this->offset = $firstLf + strlen(self::HEADER_STOP_LF);
            return true;
        }
        return false;
    }

    /**
     * Detect frame end marker, starting from current offset.
     *
     * @return bool
     */
    private function detectFrameEnd()
    {
        $bodySize = null;
        if ($this->expectedBodyLength) {
            if (($this->bufferSize - $this->offset) >= $this->expectedBodyLength) {
                $bodySize = $this->expectedBodyLength;
            }
        } elseif (($frameEnd = strpos($this->buffer, self::FRAME_END, $this->offset)) !== false) {
            $bodySize = $frameEnd - $this->offset;
        }

        if ($bodySize !== null) {
            $this->setFrame($bodySize);
            $this->offset += $bodySize + strlen(self::FRAME_END); // x00
        }
        return $bodySize !== null;
    }

    /**
     * Adds a frame from current known command, headers. Uses current offset and given body size.
     *
     * @param integer $bodySize
     */
    private function setFrame($bodySize)
    {
        $this->frame = $this->factory->createFrame(
            $this->command,
            $this->headers,
            (string)substr($this->buffer, $this->offset, $bodySize),
            $this->legacyMode
        );

        $this->expectedBodyLength = null;
        $this->headers = [];
        $this->mode = self::MODE_HEADER;
    }

    /**
     * Extracts command and headers from given header source.
     *
     * @param string $source
     * @return void
     */
    private function extractFrameMeta($source)
    {
        $headers = preg_split("/(\r?\n)+/", $source);

        $this->command = array_shift($headers);

        foreach ($headers as $header) {
            $headerDetails = explode(':', $header, 2);
            $name = $this->decodeHeaderValue($headerDetails[0]);
            $value = isset($headerDetails[1]) ? $this->decodeHeaderValue($headerDetails[1]) : true;
            if (!isset($this->headers[$name])) {
                $this->headers[$name] = $value;
            }
        }

        if (isset($this->headers['content-length'])) {
            $this->expectedBodyLength = (int)$this->headers['content-length'];
        }
    }

    /**
     * Decodes header values.
     *
     * @param string $value
     * @return string
     */
    private function decodeHeaderValue($value)
    {
        if ($this->legacyMode) {
            return str_replace(['\n'], ["\n"], $value);
        }
        return str_replace(['\r', '\n', '\c', "\\\\"], ["\r", "\n", ':', "\\"], $value);
    }

    /**
     * Resets the current buffer within this parser and returns the flushed buffer value.
     *
     * @return string
     */
    public function flushBuffer()
    {
        $this->expectedBodyLength = null;
        $this->headers = [];
        $this->mode = self::MODE_HEADER;

        $currentBuffer = substr($this->buffer, $this->offset);
        $this->offset = 0;
        $this->bufferSize = 0;
        $this->buffer = '';
        return $currentBuffer;
    }
}


code:
<?php
/*
 * This file is part of the Stomp package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Stomp\Util;

/**
 * IdGenerator generates Ids which are unique during the runtime scope.
 *
 * @package Stomp\Util
 * @author Jens Radtke <swefl.oss@fin-sn.de>
 */
class IdGenerator
{
    /**
     * @var array
     */
    private static $generatedIds = [];

    /**
     * Generate a not used id.
     *
     * @return int
     */
    public static function generateId()
    {
        while ($rand = rand(1, PHP_INT_MAX)) {
            if (!in_array($rand, static::$generatedIds, true)) {
                static::$generatedIds[] = $rand;
                return $rand;
            }
        }
    }

    /**
     * Removes a previous generated id from currently used ids.
     *
     * @param int $generatedId
     */
    public static function releaseId($generatedId)
    {
        $index = array_search($generatedId, static::$generatedIds, true);
        if ($index !== false) {
            unset(static::$generatedIds[$index]);
        }
    }
}


code:
# adriele

Chave de segurança, substituto para módulo PAM.

repo_name: slackjeff/adriele


code:
#!/usr/bin/env bash
#=================HEADER=====================================================|
#AUTHOR
# Jefferson Rocha <root@slackjeff.com.br>, Blemmi Secure
#
#PROGRAM
# Adriele
#
#LICENSE
# MIT
#
#CHANGELOG
# https://www.slackjeff.com.br/
#
#============================================================================|

# Trapando ctrl+c
trap "" int

#===================LIBRARY
. /etc/adriele.conf                      # Puxando arquivo de configuração.
. /usr/libexec/adriele/core-functions.sh # Chamando funções core.
if [ -e "/root/.${PRG}/${KEY_PENDRIVE}" ]; then
    . "/root/.${PRG}/${KEY_PENDRIVE}" # Chamando key pendrive.
else
    _HALT
fi

#===================VARS
directory_mount=$(mktemp -d) # Criando diretorio temporário

#===================TESTS
# Root?
[ "$UID" -ne "0" ] && exit 1

# Verificar se pendrive está montado
# se não está a máquina desliga.
blkid -U "$PENDRIVE_UUID" &>/dev/null || _ERROR

#===================START
# Se caso montando desmonte.
verify_device=$(blkid -U "$PENDRIVE_UUID") # Verificando Label do dispositivo.
umount "$verify_device" &>/dev/null # Desmontando pendrive.
mount "$verify_device" "$directory_mount" # Montando em diretório temporário

# Verificando existencia de KEY no pendrive.
[ ! -e "${directory_mount}/.${PRG}/${KEY_PC}" ] && _HALT

_CHECK

path: adriele


code:
#!/usr/bin/env bash
#=================HEADER=====================================================|
#AUTHOR
# Jefferson Rocha <root@slackjeff.com.br>, Blemmi Secure
#
#PROGRAM
# Adriele Instalador
#
#LICENSE
# MIT
#
#============================================================================|

#=============================LIBS
if [ -e "etc/adriele.conf" ]; then # Chamando conf externa
    . etc/adriele.conf
fi

#=============================TEST's
# É root?
[ "$UID" -ne '0' ] && { echo -e "${red}Need Root.${end}"; exit 1;}

#=============================FUNCTIONS
_MENU() # Menu principal
{
 echo -e "
 [ ${cyan_}1${end_} ] - Slackware
 [ ${cyan_}2${end_} ] - Debian/Linux-Mint/Ubuntu
 [ ${cyan_}3${end_} ] - Fedora
 [ ${cyan_}4${end_} ] - Arch Linux
"
 read -p $'\e[34;1mSelecione o ID da sua Distribuição:\e[m ' select_distro
 # É nulo? É somente númerico?
 if [ -z "$select_distro" ]; then
     echo -e "${red_}Você deve fornecer o ID da sua distribuição.${end_}"
     exit 1
 elif ! echo "$select_distro" | grep -q '^[0-9]\{1,2\}$' ; then # Somente números? 2 casas.
     echo -e "${red_}OPS. Somente Números.${end_}";
     exit 1
 fi

 # Verificando Qual Distribuição foi selecionada.
 case $select_distro in
     1)  DISTRO='slackware';;
     *)  _DIE "${red_}Nenhuma ID associada.${end_}"
 esac
}

_DIE() # Função para matar em caso de erro.
{
   echo -e "\n$1"
   exit 1
}

clear
_LOGO # Chamada do logo
_MENU # Chamada Menu

echo -e "
+++++++++++++++++++++++++++++
  Instalação do ${PRG}...
  Em Distribuição $DISTRO
+++++++++++++++++++++++++++++
"

#===========> SYSTEMS
_SLACKWARE()
{
    local rclocal="/etc/rc.d/rc.local"
    # Verificando se rc.local existe e tem permissão.
    if [ ! -e "${rclocal}" ]; then
        _DIE "${red_}ERRO 130${end_} $rclocal não existe."
    elif [ ! -x "${rclocal}" ]; then
        _DIE "${red_}ERRO${end_} $rclocal não tem permissão de execução!"
    fi

    # Copiando Executavel
    cp "${PRG}" "/etc/rc.d/rc.${PRG}" || _DIE "${red_}Impossível copiar ${PRG} para /etc/rc.d/ ${end_}"

    # Enviando informação do executável para rc.local
    if ! grep -qo "${PRG}" "$rclocal"; then # Se existe não faça.
        cat >> "${rclocal}" <<EOF
# Calling ${PRG}
if [ -x "/etc/rc.d/rc.${PRG}" ]; then
    /etc/rc.d/rc.${PRG}
fi
EOF
    fi
    return 0
}

#=============================START
# Pegando entrada de UUID's
read -p $'\e[34;1mInforme o UUID do Pendrive:\e[m '        pendrive_uuid
read -p $'\e[34;1mInforme o UUID da Raiz do Sistema:\e[m ' pc_uuid

if [ -z "$pendrive_uuid" ]; then
    _DIE "${red_}**Você deve informa o UUID do Pendrive.${end_}"
elif [ -z "$pc_uuid" ]; then
    _DIE "${red_}**Você deve informa o UUID da raiz ' / ' do seu sistema.${end_}"
fi

# Verificando Label's dos dispositivo.
count='0' # Contadora
for label in "$pendrive_uuid" "$pc_uuid"; do
   verify_device[$count]=$(blkid -U "$label")
   [ -z "$verify_device" ] && _DIE "${red_}ERRO 122${end_} Não identifiquei o UUID. '${label}'"
   echo -e "
  ------------------------------------------------------------------------
  UUID ${label}\n  Identificado como: ${verify_device[$count]}
  ------------------------------------------------------------------------"
  count=$(( $count + 1 )) # Incremento
done
read -p $'\n\e[34;1mConfirma dispositivos? [N/y]\e[m ' confirm
confirm=${confirm,,}   # Tudo em minusculo
confirm=${confirm:=n}  # Se enter pressionado ou nulo retorna n
[ "$confirm" = "n" ] && exit 0 # N? sai.

###################################
# Preparação para gerar as Hashes
###################################

# Se caso Pendrive estiver montado
# desmonte para montar em um lugar especifico.
umount "${verify_device[0]}" &>/dev/null # Pendrive

directory_mount_tmp=$(mktemp -d) # Criando diretorio temporário

#Montando pendrive em diretório temporário.
mount "${verify_device[0]}" "$directory_mount_tmp" || _DIE "Erro ao montar ${verify_device}"

# Criando Diretório no home do Root.
readonly home_root="/root/.${PRG}"
if [ ! -d "${home_root}" ]; then
    mkdir "$home_root" || _DIE "Erro ao criar diretório em ${home_root}"
fi

# Criando diretório dentro do Pendrive
readonly directory_mount_tmp_prg="${directory_mount_tmp}/.${PRG}"
if [ ! -d "${directory_mount_tmp_prg}" ]; then
    mkdir "${directory_mount_tmp_prg}" || _DIE "Erro ao criar diretório em ${directory_mount_tmp_prg}"
fi

####################################
# Gerando as Hashes Pendrive/PC
###################################

# Gerando Hash único para o 'Pendrive'
# no home do root.
HASH_PENDRIVE=$(
    sha512sum <<<"$pendrive_uuid" | cut -d ' ' -f 1 || \
    _DIE "${red_}ERRO${end_}126, Não foi possível gerar hash para Pendrive."
)
cat > "${home_root}/${KEY_PENDRIVE}" <<EOF
PENDRIVE_UUID="$pendrive_uuid"
HASH_PENDRIVE="${HASH_PENDRIVE}"
EOF

# Gerando hash para o pendrive...
# Hash único.
HASH_PC=$(
    sha512sum <<<"$pc_uuid" | cut -d ' ' -f 1 || \
    _DIE "${red_}ERRO 127${end_} Não foi possível gerar hash para PC."
)
cat > "${directory_mount_tmp_prg}/${KEY_PC}" <<EOF
PC_UUID="$pc_uuid"
HASH_PC="${HASH_PC}"
EOF

echo -e "\n${cyan_}-HASH's Geradas.${end_}"

####################################
# Permissões e outros enfeites
# antes de ir para configurações.
###################################

# Adequando permissões de arquivos
archives=(
    "${directory_mount_tmp_prg}/${KEY_PC}"
    "${home_root}/${KEY_PENDRIVE}"
)
for perm in "${archives[@]}"; do
   chmod u=rwx,g=,o= "$perm" || _DIE "Não foi possível dar permissão para arquivo ${perm}"
done
echo -e "\n${cyan_}-PERMISSÕES Ajustadas.${end_}"

# Verificação de diretórios e arquivos necessários
echo -e "\n${cyan_}-Copias de diretórios e Arquivos.${end_}"
cp -vra 'usr/' '/' || _DIE "Não foi possível copiar diretório usr/"
cp -vra 'etc/' '/' || _DIE "Não foi possível copiar diretório etc/"

####################################
# Preparando arquivos para envio
# de acordo com a distribuição que
# foi escolhida.
###################################

# Qual distribuição usuários escolheu?
# Vamos fazer as devidas configurações.
case $DISTRO in
    slackware) _SLACKWARE ;;
esac

####################################
# Desmontando pendrive e limpando
# arquivos/diretórios.
###################################
# Desmontando Pendrive
umount "${verify_device[0]}" &>/dev/null

# Removendo diretório temporário criado.
rm -r "$directory_mount_tmp"

# Concluído
echo -e "\n${PRG} INSTALADA COM SUCESSO."
exit 0


code:
Trabalhar com estes erros no manual impresso.

# Instalador
+[Erro 122] UUID não foi encontrado
+[Erro 126] Erro em geração de hash para Pendrive
+[Erro 127] Erro em geração de hash para PC
+[Erro 130] Erro rc.local não existe

# Diretorios
/etc/         # Arquivo de configuração
/etc/rc.d/    # Executável
/usr/libexec/ # Biblioteca core

path: returnerror


code:
_HALT()
{
   shutdown -h now
}

_ERROR()
{
   echo -e "${red_}FATAL ERROR IN HD.${end_}"
   _HALT
   exit
}

_CLEAR() # Limpando e desmonstando
{
    umount "$verify_device" &>/dev/null # Desmontando pendrive.
    rm -r "$directory_mount"
    return 0
}

_CHECK() # Checar se as hash batem
{
    # Abrindo arquivo em pendrive.
    . "${directory_mount}/.${PRG}/${KEY_PC}"
    # Pegando hash atual do pendrive
    local conf_hash_pendrive=$(sha512sum <<<"$PENDRIVE_UUID" | cut -d ' ' -f 1)
    # # Pegando hash atual do PC
    local conf_hash_pc=$(sha512sum <<<"$PC_UUID" | cut -d ' ' -f 1)
    if [ "$conf_hash_pendrive" = "$HASH_PENDRIVE" ] && [ "$conf_hash_pc" = "$HASH_PC" ]; then
        echo -e "${cyan_}OK.${end_}"
        _CLEAR
    else
        _HALT
    fi
}


code:
# Coolbar

Originally I made a simple mod that separates inventory from a bar, allowing you
to move things between them by shift-click, like in Minecraft. But because of
some technical specifics of the Minetest engine my mod was not really simple and
require significant changes to all mods that interact with inventory.

So I had the idea of a new mod (which you see now) that automatically manage
your inventory, removing all trash from the bar and putting your preferred items
on it, without affecting inventory structure, which makes it much mores
compatible with other mods.

As a result, it turned out to be very comfortable and easier than manual
shift-clicking!

![demo](images/screenrecord.gif)

## Explanation of the mod behavior

- The mod has a preferences that specifies one or more types of items for each
  slot on the bar. However, these settings can also be empty.
- The mod doesn't work when moving items **FROM** player's inventory within the
  formspeck, which allows a player to move things between the bar and the rest
  of the inventory manually.
- Adding items to the bar manually temporarily replaces the slot settings with
  this item until it disappears from the bar for any reason.
- If an item not specified in the preferences ends up on the bar, it is moved to
  the inventory.
- If the number of items on the bar decreases, the mod tries to fill shortage
  from the inventory.

## Compatibility notes

For `i3` users I recommend to check all checkboxes in its settings:

![demo](images/i3.png)

`Mineclone` users needs to change `bar_size` from `8` to `9` and `inv_start`
from `9` to `10` in the mod settings.

Please, be very careful changing these numbers. If the mod crashed, check twice
if these numbers was correct before reporting a bug!

## What can be improved

- The order of items in the settings is currently irrelevant. Maintaining a
  strict order is not easy task, requiring to rewrite half the code backwards.
- Would be cool to add the ability to automatically change settings depending on
  the loaded game.

---

This mod was created in [Rustroot Forge].

All my mods uses [Luanti LSP-API] annotations, [StyLua] and [Luacheck].

[Rustroot Forge]: https://notabug.org/rr
[Luanti LSP-API]: https://notabug.org/rr/luanti_lsp_api
[StyLua]: https://github.com/JohnnyMorganz/StyLua
[Luacheck]: https://github.com/mpeterv/luacheck

repo_name: rr/coolbar


code:
local settings = minetest.settings

-- Automatically detect and overwrite core settings for Mineclone game.
local mineclone = settings:get_bool("coolbar.mineclone_autodetect", true)
-- Visible bar size (excluding inventory and hidden slots).
local bar_size = tonumber(settings:get "coolbar.bar_size" or 8) --[[@as integer]]
-- The number of visible inventory rows (excluding bar and hidden slots).
local inv_rows = tonumber(settings:get "coolbar.inv_rows" or 3) --[[@as integer]]
-- Visible inventory size (excluding bar and hidden slots).
local inv_size = bar_size * inv_rows
-- Index of the first slot on a bar.
local bar_start = tonumber(settings:get "coolbar.bar_start" or 1) --[[@as integer]]
-- Index of the first inventory slot.
local inv_start = tonumber(settings:get "coolbar.inv_start" or 9) --[[@as integer]]
-- The last slot on bar
local bar_end = bar_start + bar_size - 1
-- The last inventory slot
local inv_end = inv_start + inv_size - 1
-- Default builtin array of itemstrings preferred to keep on the bar.
local default_bar_slots = {
  "group:sword",
  "group:shovel",
  "group:pickaxe",
  "group:axe",
  "default:water_bucket",
  "group:soil",
  "group:food|group:food_apple|group:food_mushroom",
  "group:torch",
}
-- Items preferred to keep on the bar.
---@type string[][]
local preferred_bar_slots = {}
for i = 1, bar_size do
  local slot =
    tostring(settings:get("coolbar.slot_" .. i) or default_bar_slots[i] or "")
  preferred_bar_slots[i] = slot:split "|"
end

-- Check if item corresponds to itemstring, including "group:something" format.
---@param item mt.ItemStack
---@param is string|string[]
---@return boolean
local function item_is(item, is)
  if type(is) == "table" then
    for _, value in ipairs(is) do
      if item_is(item, value) then return true end
    end
    return false
  end
  if is:find "^group:" then
    local group = is:sub(7)
    if item:get_definition().groups[group] then return true end
    return false
  end
  if item:get_name() == is then return true end
  return false
end

---@param player mt.PlayerObjectRef
---@param old_item mt.ItemStack
---@param new_item mt.ItemStack
---@param index integer
local function handle_item_increase(player, old_item, new_item, index)
  ---@return boolean is_it, integer|nil should_be
  local function is_new_item_position_correct()
    if old_item:get_name() == new_item:get_name() then return true, index end
    local should_be
    for i, preferred in ipairs(preferred_bar_slots) do
      if item_is(new_item, preferred) then
        should_be = i
        break
      end
    end
    if should_be == index then return true, should_be end
    if index >= inv_start and index <= inv_end then
      if should_be then return false, should_be end
      return true, should_be
    end
    return false, should_be
  end

  local is_it, should_be = is_new_item_position_correct()
  -- log(("[%s] : [%s == %s]"):format(is_it, item_index, should_be))
  if is_it then return end

  local inv = player:get_inventory()
  local list = inv:get_list "main"
  if should_be and list[should_be]:get_free_space() == 0 then
    should_be = nil
  end
  local new_item_name = new_item:get_name()
  local slot_empty, slot_same ---@type integer, integer|nil
  for i = inv_start, inv_end do
    local name = list[i]:get_name()
    if not slot_empty and name == "" then
      slot_empty = i
    elseif
      not slot_same
      and name == new_item_name
      and list[i]:get_free_space() > 0
    then
      slot_same = i
    end
    if slot_same and slot_empty then break end
  end
  if not slot_empty then return end
  -- log(("empty: [%s], same: [%s]"):format(slot_empty, slot_same))

  local stack_from = list[index]
  local stack_to = list[should_be or slot_same or slot_empty]

  local taken = stack_from:take_item(stack_from:get_count())
  local leftover = stack_to:add_item(taken)
  list[slot_empty]:add_item(leftover)

  inv:set_list("main", list)
  rr.player_inventory_main_lists[player:get_player_name()] = list
end

---@param player mt.PlayerObjectRef
---@param old_item mt.ItemStack
---@param new_item mt.ItemStack
---@param item_index integer
local function handle_bar_decrease(player, old_item, new_item, item_index)
  local inv = player:get_inventory()
  local list = inv:get_list "main"
  ---@type string|string[]
  local target_item = new_item:get_name()

  if target_item == "" then
    local bar_index = item_index - bar_start + 1
    local preferred = preferred_bar_slots[bar_index]
    if not item_is(old_item, preferred) then
      target_item = old_item:get_name()
    else
      target_item = preferred
    end
  end

  for i = inv_start, inv_end do
    local inv_stack = list[i]
    if item_is(inv_stack, target_item) then
      local bar_stack = list[item_index]
      local leftover = bar_stack:add_item(inv_stack)
      list[i] = leftover
      if bar_stack:get_count() == bar_stack:get_stack_max() then break end
      if not leftover or leftover:get_name() ~= "" then break end
    end
  end
  inv:set_list("main", list)
  rr.player_inventory_main_lists[player:get_player_name()] = list
end

---@type rr.on_player_inventory_change
local function on_player_inv_change(player, old_item, new_item, i, action, info)
  if info and info.from_list == "main" then return end
  if new_item:get_count() >= old_item:get_count() then
    handle_item_increase(player, old_item, new_item, i)
    return
  end
  if i >= bar_start and i <= bar_end then
    handle_bar_decrease(player, old_item, new_item, i)
  end
end

rr.register_on_player_inventory_change(on_player_inv_change)

minetest.register_on_mods_loaded(function()
  ---@diagnostic disable-next-line: undefined-global
  if mcl_vars and mineclone then
    bar_size = 9
    inv_start = 10
  end
  rr.auto_detect_inventory_changes()
end)

--[[ Debug
minetest.log(
  (
    "bar_size: %s, inv_rows: %s, inv_size: %s, "
    .. "bar_start: %s, bar_end: %s, "
    .. "inv_start: %s, inv_end: %s"
  ):format(
    bar_size,
    inv_rows,
    inv_size,
    bar_start,
    bar_end,
    inv_start,
    inv_end
  )
)
]]


code:
name = coolbar
title = Coolbar
description = Smart automatic items management between inventory and hotbar.
author = luarocks
min_minetest_version = 5.6
depends = inv_change_cb
author = luarocks


code:
# The number of visible inventory rows (excluding bar and hidden slots).
coolbar.inv_rows (Inventory rows) int 3

# Visible bar size (excluding inventory and hidden slots).
# Default: 8; use 9 for Mineclone.
coolbar.bar_size (Bar size) int 8

# Index of the first inventory slot.
# Default: 9; use 10 for Mineclone.
coolbar.inv_start (Inventory start) int 9

# Index of the first slot on a bar.
coolbar.bar_start (Bar start) int 1

# Automatically detect and overwrite core settings for Mineclone game.
coolbar.mineclone_autodetect (Mineclone autodetect) bool true

# Name of the item(s) in the slot #1.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_1 (Slot 1) string group:sword

# Name of the item(s) in the slot #2.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_2 (Slot 2) string group:shovel

# Name of the item(s) in the slot #3.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_3 (Slot 3) string group:pickaxe

# Name of the item(s) in the slot #4.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_4 (Slot 4) string group:axe

# Name of the item(s) in the slot #5.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_5 (Slot 5) string default:water_bucket

# Name of the item(s) in the slot #6.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_6 (Slot 6) string group:soil

# Name of the item(s) in the slot #7.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_7 (Slot 7) string group:food|group:food_apple|group:food_mushroom

# Name of the item(s) in the slot #8.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_8 (Slot 8) string group:torch

# Name of the item(s) in the slot #9.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_9 (Slot 9) string

# Name of the item(s) in the slot #10.
# You can set a group ("group:something").
# You can set many items, separated by "|".
coolbar.slot_10 (Slot 10) string


code:
# Auto detect text files and perform LF normalization
* text=auto

repo_name: dsconstructor/CheatMenuPlugin

path: .gitattributes


code:
CheatMenu/Assemblies/
CheatMenu/obj
bin/
*.exe
*.dll

# Visual Studio files
*.suo
*.sdf
*.pdb
*.ilk
Release*/
Debug*/
*.opensdf
*.user
*.vcxproj.filters
.vscode/
.vs/
*.code-workspace

# System Files
Thumbs.db
.DS_Store
._.DS_Store
tags
cscope.*
CheatMenu/Todo.txt

path: .gitignore


code:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CheatMenu", "CheatMenu\CheatMenu.csproj", "{A66A97B4-F32F-4748-8332-48F0876F0AAC}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{A66A97B4-F32F-4748-8332-48F0876F0AAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{A66A97B4-F32F-4748-8332-48F0876F0AAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{A66A97B4-F32F-4748-8332-48F0876F0AAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{A66A97B4-F32F-4748-8332-48F0876F0AAC}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {19B0272D-3305-4C60-8DD9-E15889B2FF54}
	EndGlobalSection
EndGlobal


code:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{A66A97B4-F32F-4748-8332-48F0876F0AAC}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>CheatMenu</RootNamespace>
    <AssemblyName>CheatMenu</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>..\bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <CodeAnalysisRuleSet>
    </CodeAnalysisRuleSet>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>..\bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <CodeAnalysisRuleSet>
    </CodeAnalysisRuleSet>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony">
      <HintPath>Assemblies\0Harmony.dll</HintPath>
    </Reference>
    <Reference Include="Assembly-CSharp-firstpass">
      <HintPath>Assemblies\Assembly-CSharp-firstpass.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="BepInEx">
      <HintPath>Assemblies\BepInEx.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="UnityEngine">
      <HintPath>Assemblies\UnityEngine.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule">
      <HintPath>Assemblies\UnityEngine.CoreModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.ImageConversionModule">
      <HintPath>Assemblies\UnityEngine.ImageConversionModule.dll</HintPath>
    </Reference>
    <Reference Include="UnityEngine.IMGUIModule">
      <HintPath>Assemblies\UnityEngine.IMGUIModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.TextRenderingModule">
      <HintPath>Assemblies\UnityEngine.TextRenderingModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="CheatMenuPatcher.cs" />
    <Compile Include="Managers\BindManager.cs" />
    <Compile Include="Menus\MenuButtonList.cs" />
    <Compile Include="Menus\MenuLoadScene.cs" />
    <Compile Include="Menus\MenuNotifications.cs" />
    <Compile Include="Menus\MenuPromtKeyBind.cs" />
    <Compile Include="Menus\MenuRandomIntegerPicker.cs" />
    <Compile Include="Menus\MenuScreenshot.cs" />
    <Compile Include="Menus\MenuTextEditor.cs" />
    <Compile Include="Patch\RandomIntegerPatcher.cs" />
    <Compile Include="Patch\RandomIntegerPatchState.cs" />
    <Compile Include="Patch\InstantTextPatcher.cs" />
    <Compile Include="Utils\BindsCollection.cs" />
    <Compile Include="Utils\BindsCollectionConverter.cs" />
    <Compile Include="Utils\Command.cs" />
    <Compile Include="Utils\CommandGroup.cs" />
    <Compile Include="Utils\DialogResult.cs" />
    <Compile Include="Utils\FilterInputType.cs" />
    <Compile Include="Managers\StateManager.cs" />
    <Compile Include="Managers\MenuManager.cs" />
    <Compile Include="Menus\MenuCheat.cs" />
    <Compile Include="Menus\MenuCommands.cs" />
    <Compile Include="Menus\MenuConfig.cs" />
    <Compile Include="Menus\MenuPrompt.cs" />
    <Compile Include="Menus\MenuStateViewer.cs" />
    <Compile Include="CheatMenuPlugin.cs" />
    <Compile Include="Menus\MenuDebug.cs" />
    <Compile Include="Menus\MenuBase.cs" />
    <Compile Include="Managers\GuiManager.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Utils\FilteredFlowChart.cs" />
    <Compile Include="Utils\Alignment.cs" />
    <Compile Include="Utils\GUIUtil.cs" />
    <Compile Include="Utils\Lazy.cs" />
    <Compile Include="Utils\Notification.cs" />
    <Compile Include="Utils\ReflectionUtil.cs" />
    <Compile Include="Utils\SceneUtil.cs" />
    <Compile Include="Utils\ScreenshotUtil.cs" />
    <Compile Include="Utils\VariableComparer.cs" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="Todo.txt" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
using Harmony;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CheatMenu.Patch;
using Logger = BepInEx.Logger;
using BepInEx.Logging;
using Fungus;

namespace CheatMenu
{
    public static class CheatMenuPatcher
    {
        public static void PatchAll()
        {
            var harmony = HarmonyInstance.Create(CheatMenuPlugin.PluginName);
            try
            {
                RandomIntegerPatcher.Patch(harmony);
                InstantTextPatcher.Patch(harmony);
            }
            catch (Exception ex)
            {
                Logger.Log(LogLevel.Error, $"Exception during patching: {ex}");
            }
        }
    }
}


code:
using BepInEx;
using CheatMenu.Managers;
using CheatMenu.Menus;
using Logger = BepInEx.Logger;
using BepInEx.Logging;
using System.Collections;
using System;
using UnityEngine;
using CheatMenu.Utils;

namespace CheatMenu
{
    [BepInPlugin(PluginName, PluginName, Version)]
    public class CheatMenuPlugin : BaseUnityPlugin
    {
        //Constants
        public const string PluginName = "CheatMenuPlugin";
        public const string Version = "1.0.6";

        private MenuCheat CheatMenu;
        private MenuConfig ConfigMenu;
        private MenuStateViewer StateViewerMenu;
        private MenuDebug DebugMenu;
        private MenuLoadScene LoadSceneMenu;
        private MenuRandomIntegerPicker RandomIntegerPickerMenu;
        private MenuScreenshot ScreenshotMenu;
        private MenuCommands CommandsMenu;
        private MenuNotifications NotificationMenu;

        public CheatMenuPlugin()
        {
            CheatMenuPatcher.PatchAll();
        }

        protected void Awake()
        {
            GUIManager.Initialize(this);
            StateManager.Initialize();
            BindManager.Initialize(this);

            CheatMenu = new MenuCheat { Draggable = true };
            StateViewerMenu = new MenuStateViewer();
            ConfigMenu = new MenuConfig { Draggable = true };
            DebugMenu = new MenuDebug { Draggable = true };
            LoadSceneMenu = new MenuLoadScene { Draggable = true };
            RandomIntegerPickerMenu = new MenuRandomIntegerPicker { Draggable = true };
            ScreenshotMenu = new MenuScreenshot { Draggable = true };
            CommandsMenu = new MenuCommands { Draggable = true };
            NotificationMenu = new MenuNotifications();

            KeyCode cheatBind = BindManager.GetBoundKeyForMenu(CheatMenu);
            NotificationMenu.PushNotification($"{PluginName} {Version} Loaded", cheatBind != KeyCode.None ? $"Hit {cheatBind} to toggle the menu" : "No key bound!");
        }

        protected void OnGUI()
        {
            MenuManager.OnGUI();
        }

        protected void Update()
        {
            StateManager.OnUpdate();
            MenuManager.OnUpdate();
            BindManager.OnUpdate();
        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BepInEx;
using CheatMenu.Menus;
using UnityEngine;

namespace CheatMenu.Managers
{
    public enum BindType
    {
        Action,
        Menu,
    }

    public class DefaultBind
    {
        public string Name;
        public KeyCode KeyCode;
        public bool CanUnbind = true;

        public DefaultBind(string name, KeyCode keyCode)
        {
            Name = name;
            KeyCode = keyCode;
        }

        public DefaultBind(string name, KeyCode keyCode, bool canUnbind) : this(name, keyCode)
        {
            CanUnbind = canUnbind;
        }
    }

    public class Keybind
    {
        public string Name;
        public BindType BindType = BindType.Action;
        public Action Action;
        public ConfigWrapper<KeyCode> ConfigKeyCode;
        public bool CanUnbind = true;

        public KeyCode KeyCode
        {
            get => ConfigKeyCode.Value;
            set => ConfigKeyCode.Value = value;
        }

        public Keybind(ConfigWrapper<KeyCode> config, Action action, BindType bindType = BindType.Action, bool canUnbind = true)
        {
            Name = config.Key;
            ConfigKeyCode = config;
            BindType = bindType;
            CanUnbind = canUnbind;
            Action = action;
        }

        public Keybind(ConfigWrapper<KeyCode> config, bool canUnbind = true)
            : this(config, () => { if (MenuManager.TryGetMenu(config.Key, out MenuBase menu)) menu.ToggleShown(); }, BindType.Menu, canUnbind)
        {
        }
    }

    public static class BindManager
    {
        private static readonly DefaultBind[] menuBinds =
        {
            new DefaultBind("CheatMenu", KeyCode.F9, false),
            new DefaultBind("StateViewer", KeyCode.F10),
            new DefaultBind("Commands", KeyCode.F11),
            new DefaultBind("Screenshot", KeyCode.None),
            new DefaultBind("Config", KeyCode.None),
            new DefaultBind("Debug", KeyCode.None),
            new DefaultBind("LoadScene", KeyCode.None)
        };

        public static List<Keybind> Keybinds { get; private set; } = new List<Keybind>();
        public static bool IgnoreNext { get; set; } = false;

        public static void Initialize(CheatMenuPlugin plugin)
        {
            for (int i = 0; i < menuBinds.Length; i++)
            {
                Keybinds.Add(new Keybind(new ConfigWrapper<KeyCode>(menuBinds[i].Name, plugin, menuBinds[i].KeyCode), menuBinds[i].CanUnbind));
            }
            Keybinds.Add(new Keybind(new ConfigWrapper<KeyCode>("Time-1", plugin, KeyCode.None), () => StateManager.AddInt("time", -1)));
            Keybinds.Add(new Keybind(new ConfigWrapper<KeyCode>("Time+1", plugin, KeyCode.None), () => StateManager.AddInt("time", 1)));
        }

        public static void OnUpdate()
        {
            for (int i = 0; i < Keybinds.Count; i++)
            {
                Keybind bind = Keybinds[i];
                if (bind.KeyCode != KeyCode.None && Input.GetKeyDown(bind.KeyCode))
                {
                    if (IgnoreNext)
                        IgnoreNext = false;
                    else
                        bind.Action?.Invoke();
                }
            }
        }

        public static Keybind GetKeyBind(string name)
        {
            Keybind result = null;

            for (int i = 0; i < Keybinds.Count; i++)
            {
                if (Keybinds[i].Name == name)
                {
                    result = Keybinds[i];
                    break;
                }
            }

            return result;
        }

        public static KeyCode GetBoundKeyForMenu(MenuBase menu) => GetKeyBind(menu.Name)?.KeyCode ?? KeyCode.None;
    }
}


code:
using BepInEx;
using CheatMenu.Patch;
using CheatMenu.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace CheatMenu.Managers
{
    public class GUIManager
    {
        //Constants
        public const float ScrollBarWidth = 16f;
        public const float TitleBarHeight = 10f;
        public const float PaddingSize = 5f;
        public const int DefaultProposedItemCount = 40;
        public const int MinProposedItemCount = 20;
        public const int MaxProposedItemCount = 60;
        public const int DefaultMinControlHeight = 15;
        public const int MinMinControlHeight = 10;
        public const int MaxMinControlHeight = 30;
        public const bool DefaultInstantText = false;
        public const bool DefaultRunInBackground = false;

        public static int ControlHeight { get; private set; }
        public static int ControlWidth { get; private set; }
        public static int ControlWidthTimesTwo { get; private set; }
        public static int ControlFontSize { get; private set; }
        public static int TopPadding { get; private set; }
        public static Color LabelHeaderColor { get; set; } = new Color(0.9f, 0.3f, 0.23f);
        public static Color LabelColor { get; set; } = Color.white;
        public static Color SelectedButtonColor { get; set; } = Color.green;

        private static ConfigWrapper<int> configMinControlHeight;
        public static int MinControlHeight
        {
            get => configMinControlHeight.Value;
            set => configMinControlHeight.Value = value;
        }
        private static ConfigWrapper<int> configProposedItemCount;
        public static int ProposedItemCount
        {
            get => configProposedItemCount.Value;
            set => configProposedItemCount.Value = value;
        }
        private static ConfigWrapper<bool> configInstantText;
        public static bool InstantText
        {
            get => configInstantText.Value;
            set => configInstantText.Value = value;
        }

        private static ConfigWrapper<bool> configRunInBackground;
        public static bool RunInBackground
        {
            get => configRunInBackground.Value;
            set => configRunInBackground.Value = value;
        }

        public static void Initialize(CheatMenuPlugin plugin)
        {
            configMinControlHeight = new ConfigWrapper<int>(nameof(MinControlHeight), plugin, DefaultMinControlHeight);
            if (MinControlHeight < MinMinControlHeight || MinControlHeight > MaxMinControlHeight)
                MinControlHeight = Mathf.Clamp(MinControlHeight, MinMinControlHeight, MaxMinControlHeight);
            configProposedItemCount = new ConfigWrapper<int>(nameof(ProposedItemCount), plugin, DefaultProposedItemCount);
            if (ProposedItemCount < MinProposedItemCount || ProposedItemCount > MaxProposedItemCount)
                ProposedItemCount = Mathf.Clamp(ProposedItemCount, MinProposedItemCount, MaxProposedItemCount);
            configInstantText = new ConfigWrapper<bool>(nameof(InstantText), plugin, DefaultInstantText);
            InstantTextPatcher.Enabled = InstantText;
            configRunInBackground = new ConfigWrapper<bool>(nameof(RunInBackground), plugin, DefaultRunInBackground);
            Application.runInBackground = RunInBackground;

            CalculateScaling();
        }

        public static void CalculateScaling()
        {
            float proposedButtonHeight = (float)Screen.height / ProposedItemCount;
            proposedButtonHeight = Math.Max(MinControlHeight, proposedButtonHeight);
            ControlHeight = (int)proposedButtonHeight;
            ControlWidth = (int)(proposedButtonHeight * 6f);
            ControlWidthTimesTwo = (int)(proposedButtonHeight * 12f);
            ControlFontSize = (int)(proposedButtonHeight * 0.75f);
            TopPadding = (int)(Screen.height * 0.075f);
        }

        public static Rect GetWorkingArea() => new Rect(0, TopPadding, Screen.width, Screen.height - TopPadding);
    }
}


code:
using BepInEx.Logging;
using CheatMenu.Menus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Logger = BepInEx.Logger;

namespace CheatMenu.Managers
{
    public static class MenuManager
    {
        private static Dictionary<string, MenuBase> menusDict = new Dictionary<string, MenuBase>();
        private static List<MenuBase> menusList = new List<MenuBase>();
        private static List<MenuBase> menusListRemove = new List<MenuBase>();

        public static T GetMenu<T>() where T : MenuBase
        {
            T result = null;

            for (int i = 0; i < menusList.Count; i++)
            {
                if(menusList[i] is T resultMenu)
                {
                    result = resultMenu;
                    break;
                }
            }

            return result;
        }

        public static T GetMenu<T>(string name) where T : MenuBase
        {
            return menusDict?[name] as T;
        }

        public static bool TryGetMenu<T>(string name, out T menu) where T : MenuBase
        {
            bool exists = false;

            if (menusDict?[name] is T resultMenu)
            {
                exists = true;
                menu = resultMenu;
            }
            else
            {
                menu = null;
            }

            return exists;
        }

        public static void HideAll()
        {
            for (int i = 0; i < menusList.Count; i++)
            {
                menusList[i].Shown = false;
            }
        }

        public static void AddMenu(MenuBase menu)
        {
            if (!menusDict.ContainsKey(menu.Name))
            {
                menusDict.Add(menu.Name, menu);
                menusList.Add(menu);
            }
            else
            {
                Logger.Log(LogLevel.Warning, $"Trying to add existing menu with name: {menu.Name}");
            }
        }

        public static void RemoveMenu(MenuBase menu)
        {
            if (menusDict.ContainsKey(menu.Name))
            {
                if (!menusListRemove.Contains(menu))
                {
                    menusListRemove.Add(menu);
                }
            }
            else
            {
                Logger.Log(LogLevel.Warning, $"Trying to remove non-existing menu with name: {menu.Name}");
            }
        }

        public static void OnUpdate()
        {
            if(menusListRemove.Count > 0)
            {
                for (int i = menusListRemove.Count - 1; i >= 0; i--)
                {
                    MenuBase menu = menusListRemove[i];
                    if(menu.Shown) menu.Shown = false;
                    menusDict.Remove(menu.Name);
                    menusList.Remove(menu);
                    menusListRemove.Remove(menu);
                }
            }
        }

        public static void OnGUI()
        {
            for (int i = 0; i < menusList.Count; i++)
            {
                MenuBase menu = menusList[i];
                if (menu.Shown) menu.OnGUI();
            }
        }

        public static void ReloadMenuGUI()
        {
            for (int i = 0; i < menusList.Count; i++)
            {
                menusList[i].ReinitializeGUI();
            }
        }
    }
}


code:
using BepInEx.Logging;
using CheatMenu.Menus;
using CheatMenu.Utils;
using Fungus;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
using Logger = BepInEx.Logger;

namespace CheatMenu.Managers
{
    public static class StateManager
    {
        public static bool HasPopulatedFlowcharts { get; private set; } = false;
        public static int InitCounter { get; private set; } = 0;
        public static int FlowchartCount { get; private set; } = 0;
        public static Scene LastScene { get; private set; }
        public static string LastSceneName { get => LastScene.name; }
        public static bool GUIEnabled { get; private set; } = true;

        static List<Flowchart> flowcharts;
        static List<FilteredFlowchart> filteredFlowcharts;
        static string filteredText;
        static readonly VariableComparer variableComparer = new VariableComparer();
        static int totalScenesToDump = 0;
        static bool dumpingScenes;
        static Queue<string> scenesToDump;
        static List<string> blackListScenes = new List<string>(new string[] { "Start" });

        private static Lazy<MenuStateViewer> StateViewerMenu = new Lazy<MenuStateViewer>(()=>MenuManager.GetMenu<MenuStateViewer>());
        private static Lazy<MenuRandomIntegerPicker> RandomIntegerPickerMenu = new Lazy<MenuRandomIntegerPicker>(() => MenuManager.GetMenu<MenuRandomIntegerPicker>());

        public static void Initialize()
        {
            SceneManager.activeSceneChanged += (oldScene, newScene) =>
            {
                HasPopulatedFlowcharts = false;
                flowcharts = null;
                filteredFlowcharts = null;
                InitCounter = 0;
                FlowchartCount = 0;
                LastScene = newScene;
                StateViewerMenu.Value.ResetScrollVector();
                RandomIntegerPickerMenu.Value.Shown = false;
                GUIEnabled = !blackListScenes.Contains(newScene.name);
            };
            LastScene = SceneManager.GetActiveScene();
            GUIEnabled = !blackListScenes.Contains(LastScene.name);
        }

        public static void OnUpdate()
        {
            if (!HasPopulatedFlowcharts)
            {
                if(InitCounter < 10)
                    InitFlowcharts();
            }
            else if (dumpingScenes)
            {
                SceneUtil.DumpScene();
                Logger.Log(LogLevel.Info, $"Dumping scene: {LastSceneName} ({totalScenesToDump - scenesToDump.Count}/{totalScenesToDump})");

                if (scenesToDump.Count > 0)
                {
                    string nextScene = scenesToDump.Dequeue();
                    Logger.Log(LogLevel.Info, $"Loading scene: {nextScene}");
                    SceneManager.LoadScene(nextScene);
                }
                else
                {
                    dumpingScenes = false;
                    Logger.Log(LogLevel.Info, $"Completed dumping all {totalScenesToDump} scenes");
                    MenuManager.GetMenu<MenuNotifications>().PushNotification($"Completed dumping all {totalScenesToDump} scenes", $"{Application.dataPath}{Path.DirectorySeparatorChar}*.dmp");                }
            }
        }

        public static void DumpAllScenes()
        {
            Scene activeScene = SceneManager.GetActiveScene();
            string[] sceneNames = SceneUtil.GetSceneNames();
            scenesToDump = new Queue<string>();

            for (int i = 0; i < sceneNames.Length; i++)
            {
                string sceneName = sceneNames[i];
                if (!sceneName.Equals("Start", StringComparison.OrdinalIgnoreCase) && !sceneName.Equals(activeScene.name, StringComparison.OrdinalIgnoreCase))
                    scenesToDump.Enqueue(sceneName);
            }
            scenesToDump.Enqueue("Start");

            totalScenesToDump = scenesToDump.Count;
            Logger.Log(LogLevel.Info, $"Started dumping all {totalScenesToDump} scenes");
            dumpingScenes = true;
        }

        public static List<FilteredFlowchart> GetFilteredFlowcharts(string filter)
        {
            if (HasPopulatedFlowcharts && (filteredFlowcharts == null || filteredText != filter))
            {
                filteredFlowcharts = new List<FilteredFlowchart>();
                filteredText = filter;
                FilterInputType filterInputType = GetFilterInputType(filteredText, out List<Type> typesFilter);

                for (int i = 0; i < flowcharts.Count; i++)
                {
                    Flowchart flowchart = flowcharts[i];
                    List<Variable> variables = new List<Variable>();
                    if (!string.IsNullOrEmpty(filteredText))
                    {
                        for (int j = 0; j < flowchart.Variables.Count; j++)
                        {
                            Variable variable = flowchart.Variables[j];
                            if(filterInputType == FilterInputType.Name && variable.Key.IndexOf(filteredText, StringComparison.OrdinalIgnoreCase) >= 0)
                                variables.Add(variable);
                            else if(filterInputType == FilterInputType.Types && typesFilter.Contains(variable.GetType()))
                                variables.Add(variable);
                        }
                    }
                    else
                    {
                        variables.AddRange(flowchart.Variables);
                    }

                    if(variables.Count > 0)
                    {
                        variables.Sort(variableComparer);
                        filteredFlowcharts.Add(new FilteredFlowchart(flowchart, variables));
                    }
                }
            }

            return filteredFlowcharts;
        }

        public static void AddInt(string name, int value)
        {
            if (!HasPopulatedFlowcharts) return;

            bool found = false;
            for (int j = 0; j < flowcharts.Count; j++)
            {
                if (found) break;

                for (int i = 0; i < flowcharts[j].Variables.Count; i++)
                {
                    if (found) break;

                    if (flowcharts[j].Variables[i].Key == name)
                    {
                        IntegerVariable variable = (IntegerVariable)flowcharts[j].Variables[i];
                        variable.Value += value;
                        PlayerPrefs.SetInt(SetSaveProfile.SaveProfile + "_" + flowcharts[j].SubstituteVariables(variable.Key), variable.Value);
                        found = true;
                    }
                }
            }
        }

        public static void SetInt(string name, int value)
        {
            if (!HasPopulatedFlowcharts) return;

            bool found = false;
            for (int j = 0; j < flowcharts.Count; j++)
            {
                if (found) break;

                for (int i = 0; i < flowcharts[j].Variables.Count; i++)
                {
                    if (found) break;

                    if (flowcharts[j].Variables[i].Key == name)
                    {
                        IntegerVariable variable = (IntegerVariable)flowcharts[j].Variables[i];
                        variable.Value = value;
                        PlayerPrefs.SetInt(SetSaveProfile.SaveProfile + "_" + flowcharts[j].SubstituteVariables(variable.Key), variable.Value);
                        found = true;
                    }
                }
            }
        }

        public static int GetInt(string name)
        {
            if (!HasPopulatedFlowcharts) return -1;

            int result = 0;
            bool found = false;
            for (int j = 0; j < flowcharts.Count; j++)
            {
                if (found) break;

                for (int i = 0; i < flowcharts[j].Variables.Count; i++)
                {
                    if (found) break;

                    if (flowcharts[j].Variables[i].Key == name)
                    {
                        IntegerVariable variable = (IntegerVariable)flowcharts[j].Variables[i];
                        result = variable.Value;
                        found = true;
                    }
                }
            }
            return result;
        }

        private static void InitFlowcharts()
        {
            InitCounter++;
            var fc = UnityEngine.Object.FindObjectsOfType<Flowchart>();
            if (fc != null && fc.Length != 0)
            {
                flowcharts = new List<Flowchart>(fc);
                FlowchartCount = flowcharts.Count;
                HasPopulatedFlowcharts = true;
            }
        }

        private static FilterInputType GetFilterInputType(string filter, out List<Type> types)
        {
            List<Type> resultTypes = new List<Type>();

            string[] filterParts = filter.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < filterParts.Length; i++)
            {
                string filterPart = filterParts[i].ToLower();
                switch (filterPart)
                {
                    case "type:int":
                        resultTypes.Add(typeof(IntegerVariable));
                        break;
                    case "type:float":
                        resultTypes.Add(typeof(FloatVariable));
                        break;
                    case "type:bool":
                        resultTypes.Add(typeof(BooleanVariable));
                        break;
                    case "type:str":
                        resultTypes.Add(typeof(StringVariable));
                        break;
                    default:
                        break;
                }
            }

            types = resultTypes;
            return types.Count == 0 ? FilterInputType.Name : FilterInputType.Types;
        }
    }
}


code:
using BepInEx.Logging;
using CheatMenu.Managers;
using CheatMenu.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Logger = BepInEx.Logger;

namespace CheatMenu.Menus
{
    public abstract class MenuBase
    {
        private readonly int windowID;
        private bool initializedGUI;
        private bool isOffsetDirty;

        protected bool ForceGUIEnabled { get; set; }
        protected Rect WindowRect { get; private set; }
        private Vector2 windowOffset;
        protected Vector2 WindowOffset
        {
            get => windowOffset;
            set
            {
                if (windowOffset != value)
                    isOffsetDirty = true;

                windowOffset = value;
            }
        }

        public string Name { get; private set; }
        public string Text { get; set; }
        public bool Draggable { get; set; }
        public Vector2 Size { get; set; }
        public Alignment Alignment { get; set; } = Alignment.None;

        private bool shown;
        public bool Shown
        {
            get => shown;
            set
            {
                bool lastShown = shown;
                shown = value;

                if (lastShown != shown)
                {
                    if (shown)
                        OnShown();
                    else
                        OnHidden();
                }
            }
        }

        protected MenuBase(string name, string text = null)
        {
            Name = name;
            Text = !string.IsNullOrEmpty(text) ? text : name;
            windowID = GetHashCode();
            Size = InitializeSize();

            MenuManager.AddMenu(this);
        }

        public void OnGUI()
        {
            if (!initializedGUI)
            {
                InitializeGUI();
                Size = InitializeSize();
                UpdateWindowSize();
                initializedGUI = true;
            }
            else if (isOffsetDirty)
            {
                UpdateWindowSize();
            }

            GUIUtil.DrawRoundedRectangle(WindowRect);
            WindowRect = GUILayout.Window(windowID, WindowRect, OnMenuWindow, Text);
        }

        public void ToggleShown() => Shown = !Shown;

        public void ReinitializeGUI() => initializedGUI = false;

        private void OnMenuWindow(int id)
        {
            try
            {
                GUI.enabled = ForceGUIEnabled ? true : StateManager.GUIEnabled;
                MenuWindow(id);
            }
            catch (Exception ex)
            {
                Logger.Log(LogLevel.Error, $"[{Name}] GUI Exception: {ex}");
            }
            if (Draggable) GUI.DragWindow();
        }

        public void UpdateWindowSize()
        {
            Rect screenRect = GUIManager.GetWorkingArea();

            float w = Size.x > 0 ? Size.x : screenRect.width;
            float h = Size.y > 0 ? Size.y : screenRect.height;
            if (Alignment != Alignment.None)
                WindowRect = GUIUtil.AlignRect(w, h, screenRect, Alignment);
            else
                WindowRect = new Rect(screenRect.x + windowOffset.x, screenRect.y + windowOffset.y, w, h);

            isOffsetDirty = false;
        }

        public void Focus()
        {
            GUI.FocusWindow(windowID);
        }

        public void BringWindowToFront()
        {
            GUI.BringWindowToFront(windowID);
        }

        protected virtual void InitializeGUI() { }
        protected virtual void OnShown() { }
        protected virtual void OnHidden() { }

        protected abstract Vector2 InitializeSize();
        protected abstract void MenuWindow(int id);
    }
}


code:
using CheatMenu.Managers;
using CheatMenu.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Logger = BepInEx.Logger;
using BepInEx.Logging;

namespace CheatMenu.Menus
{
    public class MenuButtonList : MenuBase
    {
        private GUIStyle defaultFontControlHeightButtonStyle;
        private GUIStyle defaultFontLabelHeaderStyle;
        private GUIStyle stretchHeightBoxStyle;
        private GUIStyle stretchWidthHeightBoxStyle;
        private GUIStyle commandButtonStyle;
        private GUIStyle labelHeaderStyle;

        private Vector2 commandsStateViewVector;

        protected readonly List<CommandGroup> CommandGroups = new List<CommandGroup>();

        public string HeaderText { get; set; }
        public bool CanClose { get; set; } = true;

        public void ResetScrollVector() => commandsStateViewVector = new Vector2();

        public MenuButtonList(string name, string text = null) : base(name, text) { }

        protected override Vector2 InitializeSize()
        => new Vector2((GUIManager.ControlHeight * 8f) + (GUIManager.PaddingSize * 2) + GUIManager.ScrollBarWidth, (GUIManager.ControlHeight * 12) + (GUIManager.PaddingSize * 2));

        protected override void InitializeGUI()
        {
            defaultFontControlHeightButtonStyle = new GUIStyle(GUI.skin.button)
            {
                fontSize = GUIManager.ControlFontSize,
                fixedHeight = GUIManager.ControlHeight
            };

            defaultFontLabelHeaderStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = GUIManager.ControlFontSize,
                fontStyle = FontStyle.Bold,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelHeaderColor
                }
            };

            stretchHeightBoxStyle = new GUIStyle(GUI.skin.box)
            {
                stretchHeight = true
            };

            stretchWidthHeightBoxStyle = new GUIStyle(GUI.skin.box)
            {
                stretchWidth = true,
                stretchHeight = true
            };

            commandButtonStyle = new GUIStyle(GUI.skin.button)
            {
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize,
                fixedHeight = GUIManager.ControlHeight
            };

            labelHeaderStyle = new GUIStyle(GUI.skin.label)
            {
                fixedHeight = GUIManager.ControlHeight,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontStyle = FontStyle.Bold,
                fontSize = GUIManager.ControlFontSize,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelHeaderColor
                }
            };
        }

        protected override void MenuWindow(int id)
        {
            GUILayout.BeginVertical();
            {
                GUILayout.BeginHorizontal();
                {
                    if (!string.IsNullOrEmpty(HeaderText))
                    {
                        GUILayout.BeginVertical(GUI.skin.box);
                        {
                            GUILayout.Label(HeaderText, defaultFontLabelHeaderStyle);
                        }
                        GUILayout.EndVertical();
                    }
                    GUILayout.FlexibleSpace();
                    GUILayout.BeginHorizontal(GUI.skin.box);
                    {
                        bool guiEnabled = GUI.enabled;
                        GUI.enabled = CanClose && guiEnabled;
                        if (GUILayout.Button("Close", defaultFontControlHeightButtonStyle))
                        {
                            Shown = false;
                        }
                        GUI.enabled = guiEnabled;
                    }
                    GUILayout.EndHorizontal();
                }
                GUILayout.EndHorizontal();

                GUILayout.BeginVertical(stretchHeightBoxStyle);
                {
                    commandsStateViewVector = GUILayout.BeginScrollView(commandsStateViewVector, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, stretchWidthHeightBoxStyle);
                    for (int i = 0; i < CommandGroups.Count; i++)
                    {
                        CommandGroup group = CommandGroups[i];
                        GUILayout.BeginVertical(GUI.skin.box);
                        {
                            GUILayout.Label(group.Name, labelHeaderStyle);
                            Color oldBackground = GUI.backgroundColor;
                            for (int j = 0; j < group.Commands.Count; j++)
                            {
                                Command command = group.Commands[j];
                                {
                                    if (command.ToggleState)
                                        GUI.backgroundColor = GUIManager.SelectedButtonColor;
                                    else
                                        GUI.backgroundColor = oldBackground;

                                    if (GUILayout.Button(command.Name, commandButtonStyle))
                                        command.InvokeCommand();
                                }
                            }
                            GUI.backgroundColor = oldBackground;
                        }
                        GUILayout.EndVertical();
                    }
                    GUILayout.EndScrollView();

                    //Hacky workaround for the ScrollView not taking mouse scrollwheel focus
                    if (GUIUtil.MouseScrollingAndContained(WindowRect))
                        Input.ResetInputAxes();
                }
                GUILayout.EndVertical();
            }
            GUILayout.EndVertical();
        }

        public void ClearCommandGroups()
        {
            CommandGroups.Clear();
        }

        public void AddCommandGroup(CommandGroup group)
        {
            if (CommandGroups.Where((c) => c.Name.Equals(group.Name, StringComparison.OrdinalIgnoreCase)).Count() == 0)
                CommandGroups.Add(group);
            else
                Logger.Log(LogLevel.Warning, $"Trying to add existing commandgroup with name: {group.Name}");
        }

        public void RemoveCommandGroup(CommandGroup group)
        {
            if (CommandGroups.Contains(group))
                CommandGroups.Remove(group);
            else
                Logger.Log(LogLevel.Warning, $"Trying to remove non-existing commandgroup with name: {group.Name}");
        }
    }
}


code:
using CheatMenu.Managers;
using CheatMenu.Utils;
using UnityEngine;

namespace CheatMenu.Menus
{
    public class MenuCheat : MenuBase
    {
        private GUIStyle defaultFontButtonStyle;
        private GUIStyle defaultFontControlHeightButtonStyle;

        public MenuCheat() : base("CheatMenu", null)
        {
            Alignment = Alignment.TopCentre;
        }

        protected override Vector2 InitializeSize()
        => new Vector2(GUIManager.ControlWidth + (GUIManager.PaddingSize * 2), (GUIManager.ControlHeight * 2) + (GUIManager.PaddingSize * 2));

        protected override void InitializeGUI()
        {
            defaultFontButtonStyle = new GUIStyle(GUI.skin.button)
            {
                fontSize = GUIManager.ControlFontSize,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0)
            };

            defaultFontControlHeightButtonStyle = new GUIStyle(GUI.skin.button)
            {
                fontSize = GUIManager.ControlFontSize,
                fixedHeight = GUIManager.ControlHeight
            };
        }

        protected override void MenuWindow(int id)
        {
            GUILayout.BeginVertical();
            {
                GUILayout.BeginHorizontal();
                {
                    GUILayout.FlexibleSpace();
                    GUILayout.BeginHorizontal(GUI.skin.box);
                    {
                        if (GUILayout.Button("Close", defaultFontControlHeightButtonStyle)) { Shown = false; }
                    }
                    GUILayout.EndHorizontal();
                }
                GUILayout.EndHorizontal();

                GUILayout.BeginVertical(GUI.skin.box);
                {
                    if (GUILayout.Button("State", defaultFontButtonStyle)) { MenuManager.GetMenu<MenuStateViewer>().ToggleShown(); }
                    if (GUILayout.Button("Commands", defaultFontButtonStyle)) { MenuManager.GetMenu<MenuCommands>().ToggleShown(); }
                    if (GUILayout.Button("Screenshot", defaultFontButtonStyle)) { MenuManager.GetMenu<MenuScreenshot>().ToggleShown(); }
                    if (GUILayout.Button("Config", defaultFontButtonStyle)) { MenuManager.GetMenu<MenuConfig>().ToggleShown(); }
                    if (GUILayout.Button("Debug", defaultFontButtonStyle)) { MenuManager.GetMenu<MenuDebug>().ToggleShown(); }
                }
                GUILayout.EndVertical();
            }
            GUILayout.EndVertical();
        }
    }
}


code:
using CheatMenu.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using CheatMenu.Utils;
using BepInEx.Logging;
using Logger = BepInEx.Logger;
using CheatMenu.Patch;

namespace CheatMenu.Menus
{
    public class MenuCommands : MenuButtonList
    {
        public MenuCommands() : base("Commands", null)
        {
            Alignment = Alignment.BottomRight;

            CommandGroup groupState = new CommandGroup("Gamestate");
            groupState.AddCommand("+$100", (c) => StateManager.AddInt("money", 100));
            groupState.AddCommand("Full Energy", (c) => StateManager.SetInt("energ", StateManager.GetInt("total_energ")));
            groupState.AddCommand("Time-1", (c) => StateManager.AddInt("time", -1));
            groupState.AddCommand("Time+1", (c) => StateManager.AddInt("time", 1));
            CommandGroups.Add(groupState);

            CommandGroup groupInventory = new CommandGroup("Inventory");
            groupInventory.AddCommand("Full Lube", (c) => StateManager.SetInt("lube", 3));
            groupInventory.AddCommand("Full Condoms", (c) => StateManager.SetInt("condoms", 3));
            groupInventory.AddCommand("Full Suncream", (c) => StateManager.SetInt("suncream", 3));
            groupInventory.AddCommand("Full VitaminX", (c) => StateManager.SetInt("drugs", 20));
            groupInventory.AddCommand("Full PlanB", (c) => StateManager.SetInt("mpills", 20));
            groupInventory.AddCommand("Full Sleeping", (c) => StateManager.SetInt("spills", 20));
            groupInventory.AddCommand("Full Lactation", (c) => StateManager.SetInt("lpills", 20));
            CommandGroups.Add(groupInventory);

            CommandGroup groupAdvanced = new CommandGroup("Advanced");
            groupAdvanced.AddCommand("LoadScene", (c) => MenuManager.GetMenu<MenuLoadScene>().ToggleShown());
            groupAdvanced.AddCommand("RandomInteger", (c) =>
            {
                c.ToggleState = RandomIntegerPatcher.Enabled = !RandomIntegerPatcher.Enabled;
            }, RandomIntegerPatcher.Enabled);
            groupAdvanced.AddCommand("InstantText", (c) =>
            {
                c.ToggleState = InstantTextPatcher.Enabled = GUIManager.InstantText = !GUIManager.InstantText;
            }, GUIManager.InstantText);
            groupAdvanced.AddCommand("RunInBackground", (c) =>
            {
                c.ToggleState = Application.runInBackground = GUIManager.RunInBackground = !GUIManager.RunInBackground;
            }, GUIManager.RunInBackground);
            //groupAdvanced.AddCommand("ExecBlock", () => promptExecBlockScene.ToggleShown());
            CommandGroups.Add(groupAdvanced);
        }

        protected override void OnHidden() => MenuManager.GetMenu<MenuLoadScene>().Shown = false;

        protected override Vector2 InitializeSize()
        => new Vector2((GUIManager.ControlHeight * 8f) + (GUIManager.PaddingSize * 2) + GUIManager.ScrollBarWidth, (GUIManager.ControlHeight * 12) + (GUIManager.PaddingSize * 2));
    }
}


code:
using CheatMenu.Managers;
using System.Collections.Generic;
using UnityEngine;
using CheatMenu.Utils;
using Logger = BepInEx.Logger;
using BepInEx.Logging;

namespace CheatMenu.Menus
{
    public class MenuConfig : MenuBase
    {
        private string minControlHeight = GUIManager.MinControlHeight.ToString();
        //private string proposedItemCount = GUIManager.ProposedItemCount.ToString();
        private float proposedItemCountSlider = GUIManager.ProposedItemCount;
        private Vector2 bindsViewVector;
        public void ResetScrollVector() => bindsViewVector = new Vector2();

        private GUIStyle defaultFontNoWrapLabelStyle;
        private GUIStyle defaultFontControlHeightButtonStyle;
        private GUIStyle defaultFontTextFieldStyle;
        private GUIStyle scaleSliderStyle;
        private GUIStyle scaleSliderThumbStyle;
        private GUIStyle stretchWidthHeightBoxStyle;
        private GUIStyle commandButtonStyle;

        MenuPromtKeyBind keyBindPrompt;

        public MenuConfig() : base("Config")
        {
            Alignment = Alignment.Centre;

            keyBindPrompt = new MenuPromtKeyBind("Key Binding");
            keyBindPrompt.KeyBoundEvent += (s, e) =>
            {
                Keybind keyBind = BindManager.GetKeyBind(keyBindPrompt.BindName);
                if (e == KeyCode.Escape)
                {
                    if (keyBind.CanUnbind)
                    {
                        keyBind.KeyCode = KeyCode.None;
                    }
                    else
                    {
                        MenuManager.GetMenu<MenuNotifications>().PushNotification("Invalid Action", "Can't unbind this key!");
                    }
                }
                else
                {
                    BindManager.IgnoreNext = true;
                    keyBind.KeyCode = e;
                }
            };
        }

        protected override Vector2 InitializeSize()
        => new Vector2((GUIManager.ControlWidth * 2) + (GUIManager.PaddingSize * 2), (GUIManager.ControlHeight * 15) + (GUIManager.PaddingSize * 2));

        protected override void InitializeGUI()
        {
            defaultFontNoWrapLabelStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = GUIManager.ControlFontSize,
                wordWrap = false,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelColor
                }
            };

            defaultFontControlHeightButtonStyle = new GUIStyle(GUI.skin.button)
            {
                fontSize = GUIManager.ControlFontSize,
                fixedHeight = GUIManager.ControlHeight
            };

            defaultFontTextFieldStyle = new GUIStyle(GUI.skin.textField)
            {
                fontSize = GUIManager.ControlFontSize
            };

            scaleSliderStyle = new GUIStyle(GUI.skin.horizontalSlider)
            {
                fixedHeight = GUIManager.ControlHeight
            };

            scaleSliderThumbStyle = new GUIStyle(GUI.skin.horizontalSliderThumb)
            {
                fixedHeight = GUIManager.ControlHeight,
                fixedWidth = GUIManager.ControlHeight
            };

            stretchWidthHeightBoxStyle = new GUIStyle(GUI.skin.box)
            {
                stretchWidth = true,
                stretchHeight = true
            };

            commandButtonStyle = new GUIStyle(GUI.skin.button)
            {
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize,
                fixedHeight = GUIManager.ControlHeight
            };
        }

        protected override void OnShown()
        {
            minControlHeight = GUIManager.MinControlHeight.ToString();
            proposedItemCountSlider = GUIManager.ProposedItemCount;
        }

        protected override void OnHidden()
        {
            keyBindPrompt.Shown = false;
        }

        protected override void MenuWindow(int id)
        {
            GUILayout.BeginVertical();
            {
                GUILayout.BeginHorizontal();
                {
                    GUILayout.FlexibleSpace();
                    GUILayout.BeginHorizontal(GUI.skin.box);
                    {
                        if (GUILayout.Button("Apply", defaultFontControlHeightButtonStyle))
                        {
                            bool dirty = ParseInputAndIsDirty();
                            if (dirty)
                            {
                                GUIManager.CalculateScaling();
                                MenuManager.ReloadMenuGUI();
                            }
                        }
                        if (GUILayout.Button("Close", defaultFontControlHeightButtonStyle))
                        {
                            Shown = false;
                        }
                    }
                    GUILayout.EndHorizontal();
                }
                GUILayout.EndHorizontal();

                GUILayout.BeginVertical(GUI.skin.box);
                {
                    float percent = (2f - (proposedItemCountSlider / GUIManager.DefaultProposedItemCount)) * 100f;
                    GUILayout.Label($"Scaling: {percent:0}%", defaultFontNoWrapLabelStyle);
                    proposedItemCountSlider = GUILayout.HorizontalSlider(proposedItemCountSlider, GUIManager.MaxProposedItemCount, GUIManager.MinProposedItemCount, scaleSliderStyle, scaleSliderThumbStyle);

                    GUILayout.Label("MinControlHeight", defaultFontNoWrapLabelStyle);
                    minControlHeight = GUILayout.TextField(minControlHeight, defaultFontTextFieldStyle);

                    GUILayout.Label("Binds", defaultFontNoWrapLabelStyle);
                    List<Keybind> keybinds = BindManager.Keybinds;

                    bindsViewVector = GUILayout.BeginScrollView(bindsViewVector, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, stretchWidthHeightBoxStyle);
                    for (int i = 0; i < keybinds.Count; i++)
                    {
                        Keybind bind = keybinds[i];
                        if (GUILayout.Button($"{bind.Name} ({bind.KeyCode})", commandButtonStyle))
                            keyBindPrompt.PopulateAndShow(bind.Name);
                    }
                    GUILayout.EndScrollView();
                }
                GUILayout.EndVertical();
            }
            GUILayout.EndVertical();
        }

        private bool ParseInputAndIsDirty()
        {
            bool dirty = false;

            if (int.TryParse(minControlHeight, out int outMinControlHeight))
            {
                if (outMinControlHeight < GUIManager.MinMinControlHeight || outMinControlHeight > GUIManager.MaxMinControlHeight)
                {
                    outMinControlHeight = Mathf.Clamp(outMinControlHeight, GUIManager.MinMinControlHeight, GUIManager.MaxMinControlHeight);
                    minControlHeight = outMinControlHeight.ToString();
                }
                if (GUIManager.MinControlHeight != outMinControlHeight)
                {
                    GUIManager.MinControlHeight = outMinControlHeight;
                    dirty = true;
                }
            }
            else
            {
                minControlHeight = GUIManager.MinControlHeight.ToString();
            }

            int proposedItemCount = Mathf.RoundToInt(proposedItemCountSlider);
            if (GUIManager.ProposedItemCount != proposedItemCount)
            {
                GUIManager.ProposedItemCount = proposedItemCount;
                dirty = true;
            }

            return dirty;
        }
    }
}