/* reveng.h
 * Greg Cook, 23/Feb/2019
 */

/* CRC RevEng: arbitrary-precision CRC calculator and algorithm finder
 * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
 * 2019  Gregory Cook
 *
 * This file is part of CRC RevEng.
 *
 * CRC RevEng is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CRC RevEng is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CRC RevEng.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef REVENG_H
#define REVENG_H 1

/* Configuration options */

#include "config.h"

#ifndef BMP_T
#  error config.h: BMP_T must be defined as unsigned long or a longer unsigned type
#endif

#ifndef BMP_C
#  error config.h: BMP_C() must define a BMP_T constant
#endif

#if !defined PRESETS && !defined BMPMACRO
#  undef BMP_BIT
#  undef BMP_SUB
#endif

#undef BMP_POF2

#ifdef BMP_BIT
#  ifndef BMP_SUB
#    error config.h: BMP_SUB must be defined as the highest power of two that is strictly less than BMP_BIT
#  elif BMP_BIT < 32
#    error config.h: BMP_BIT must be at least 32
#  elif BMP_SUB < 16
#    error config.h: BMP_SUB must be at least 16
#  elif (BMP_SUB >= BMP_BIT || BMP_SUB << 1 < BMP_BIT || BMP_SUB & (BMP_SUB - 1))
#    error config.h: BMP_SUB must be defined as the highest power of two that is strictly less than BMP_BIT
#  else /* BMP_SUB */
#    define SETBMP()
#  endif /* BMP_SUB */
#  if BMP_BIT == 32
#    define BMP_POF2 5
#  elif BMP_BIT == 64
#    define BMP_POF2 6
#  elif BMP_BIT == 128
#    define BMP_POF2 7
#  elif BMP_BIT == 256
#    define BMP_POF2 8
#  elif BMP_BIT == 512
#    define BMP_POF2 9
#  elif BMP_BIT == 1024
#    define BMP_POF2 10
#  elif BMP_BIT == 2048
#    define BMP_POF2 11
#  elif BMP_BIT == 4096
#    define BMP_POF2 12
#  elif BMP_BIT == 8192
#    define BMP_POF2 13
#  elif BMP_BIT == 16384
#    define BMP_POF2 14
#  elif BMP_BIT == 32768
#    define BMP_POF2 15
#  elif BMP_BIT == 65536
#    define BMP_POF2 16
/* may extend list as required */
#  elif (BMP_BIT & (BMP_BIT - 1)) == 0
#    define BMP_POF2 1
#  endif
#else /* BMP_BIT */
#  define BMP_BIT bmpbit
#  define BMP_SUB bmpsub
#  define SETBMP() setbmp()
#endif /* BMP_BIT */

/* Global definitions */

/* CRC RevEng version string */
#define VERSION "1.6.2"

/* bmpbit.c */
typedef BMP_T bmp_t;

extern int bmpbit, bmpsub;
void setbmp(void);

/* poly.c */
#define P_REFIN      1
#define P_REFOUT     2
#define P_MULXN      4
#define P_RTJUST     8
#define P_UPPER     16
#define P_SPACE     32
#define P_LTLBYT    64
#define P_DIRECT   128

/* class flags */
#define P_CLBIT0   256
#define P_CLBIT1   512
#define P_SOLID   1024
#define P_CLMASK (P_SOLID | P_CLBIT1 | P_CLBIT0)

#define P_UNDFCL     0
#define P_UNCONF (P_CLBIT0)
#define P_THIRDP (P_CLBIT1)
#define P_ACADEM (P_CLBIT1 | P_CLBIT0)
#define P_CONFIR (P_SOLID | P_CLBIT0)
#define P_ATTEST (P_SOLID | P_CLBIT1)

/* default flags */
#define P_BE     (P_RTJUST | P_MULXN)
#define P_LE     (P_REFIN | P_REFOUT | P_MULXN)
#define P_BELE   (P_REFOUT | P_MULXN)
#define P_LEBE   (P_REFIN | P_RTJUST | P_MULXN)

/* A poly_t constant representing the polynomial 0. */
#define PZERO {0UL, (bmp_t *) 0}

typedef struct {
    unsigned long length; /* number of significant bits */
    bmp_t *bitmap;        /* bitmap, MSB first, */
    /* left-justified in each word */
} poly_t;

poly_t filtop(FILE *input, unsigned long length, int flags, int bperhx);
poly_t strtop(const char *string, int flags, int bperhx);
char *ptostr(const poly_t poly, int flags, int bperhx);
char *pxsubs(const poly_t poly, int flags, int bperhx, unsigned long start, unsigned long end);
poly_t pclone(const poly_t poly);
void pcpy(poly_t *dest, const poly_t src);
void pcanon(poly_t *poly);
void pnorm(poly_t *poly);
void psnorm(poly_t *poly);
void pchop(poly_t *poly);
void pkchop(poly_t *poly);
unsigned long plen(const poly_t poly);
int pcmp(const poly_t *a, const poly_t *b);
int psncmp(const poly_t *a, const poly_t *b);
int ptst(const poly_t poly);
unsigned long pfirst(const poly_t poly);
unsigned long plast(const poly_t poly);
poly_t psubs(const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail);
void pright(poly_t *poly, unsigned long length);
void pshift(poly_t *dest, const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail);
void ppaste(poly_t *dest, const poly_t src, unsigned long skip, unsigned long seek, unsigned long end, unsigned long fulllength);
void pdiff(poly_t *dest, const poly_t src, unsigned long ofs);
void psum(poly_t *dest, const poly_t src, unsigned long ofs);
void prev(poly_t *poly);
void prevch(poly_t *poly, int bperhx);
void prcp(poly_t *poly);
void pinv(poly_t *poly);
poly_t pmod(const poly_t dividend, const poly_t divisor);
poly_t pcrc(const poly_t message, const poly_t divisor, const poly_t init, const poly_t xorout, int flags);
int piter(poly_t *poly);
void palloc(poly_t *poly, unsigned long length);
void pfree(poly_t *poly);
void praloc(poly_t *poly, unsigned long length);
int pmpar(const poly_t poly, const poly_t mask);
int pident(const poly_t a, const poly_t b);

/* model.c */

/* A model_t constant representing an uninitialised model or zero-bit CRC algorithm. */
#define MZERO {PZERO, PZERO, P_BE, PZERO, PZERO, PZERO, NULL}

typedef struct {
    poly_t spoly;       /* polynomial with highest-order term removed. length determines CRC width */
    poly_t init;        /* initial register value. length == spoly.length */
    int flags;          /* P_REFIN and P_REFOUT indicate reflected input/output */
    poly_t xorout;      /* final register XOR mask. length == spoly.length */
    poly_t check;       /* optional check value, the CRC of the UTF-8 string "123456789" */
    poly_t magic;       /* optional magic check value, the residue of a valid codeword */
    const char *name;   /* optional canonical name of the model */
} model_t;

void mcpy(model_t *dest, const model_t *src);
void mfree(model_t *model);
int mcmp(const model_t *a, const model_t *b);
char *mtostr(const model_t *model);
void mcanon(model_t *model);
void mcheck(model_t *model);
void mrev(model_t *model);
void mnovel(model_t *model);

/* preset.c */
#define M_OVERWR     1

int mbynam(model_t *dest, const char *key);
void mbynum(model_t *dest, int num);
int mcount(void);
char *mnames(void);
void mmatch(model_t *model, int flags);

/* reveng.c */
#define R_HAVEP      1
#define R_HAVEI      2
#define R_HAVERI     4
#define R_HAVERO     8
#define R_HAVEX     16
#define R_HAVEQ     32

#define R_SPMASK 0x7FFFFFFUL

model_t *reveng(const model_t *guess, const poly_t qpoly, int rflags, int args, const poly_t *argpolys);

/* cli.c */
#define C_INFILE  1
#define C_NOPCK   2
#define C_NOBFS   4
#define C_RESULT  8

#define BUFFER 32768

int reveng_main(int argc, char *argv[]);
void ufound(const model_t *model);
void uerror(const char *msg);
void uprog(const poly_t gpoly, int flags, unsigned long seq);

#endif /* REVENG_H */
