#include "listmem.h"

#include <stdlib.h>
#include <string.h>
#include <iomanip>

using namespace std;

/*
 * Functions to list a memory buffer:
 */

/* Turn byte into Hexadecimal ascii representation */
static char *hexa(unsigned int i)
{
    int j;
    static char asc[3];

    asc[0] = (i >> 4) & 0x0f;
    asc[1] = i & 0x0f;
    asc[2] = 0;
    for (j = 0; j < 2; j++)
        if (asc[j] > 9) {
            asc[j] += 55;
        } else {
            asc[j] += 48;
        }
    return (asc);
}

static void l_swap16(unsigned char *d, const unsigned char *s, int n)
{
    if (n & 1) {
        n >>= 1;
        n++;
    } else {
        n >>= 1;
    }
    while (n--) {
        int i;
        i = 2 * n;
        d[i] = s[i + 1];
        d[i + 1] = s[i];
    }
}

static void l_swap32(unsigned char *d, const unsigned char *s, int n)
{
    if (n & 3) {
        n >>= 2;
        n++;
    } else {
        n >>= 2;
    }
    while (n--) {
        int i;
        i = 4 * n;
        d[i] = s[i + 3];
        d[i + 1] = s[i + 2];
        d[i + 2] = s[i + 1];
        d[i + 3] = s[i];
    }
}

/* Turn byte buffer into hexadecimal representation */
void charbuftohex(int len, unsigned char *dt, int maxlen, char *str)
{
    int i;
    char *bf;

    for (i = 0, bf = str; i < len; i++) {
        char *cp;
        if (bf - str >= maxlen - 4) {
            break;
        }
        cp = hexa((unsigned int)dt[i]);
        *bf++ = *cp++;
        *bf++ = *cp++;
        *bf++ = ' ';
    }
    *bf++ = 0;
}

void listmem(ostream& os, const void *_ptr, int siz, int adr, int opts)
{
    const unsigned char *ptr = (const unsigned char *)_ptr;
    int             i, j, c;
    char            lastlisted[16];
    int             alreadysame = 0;
    int         oneout = 0;
    unsigned char  *mpt;

    if (opts & (LISTMEM_SWAP16 | LISTMEM_SWAP32)) {
        if ((mpt = (unsigned char *)malloc(siz + 4)) == NULL) {
            os << "OUT OF MEMORY\n";
            return;
        }
        if (opts & LISTMEM_SWAP16) {
            l_swap16(mpt, ptr, siz);
        } else if (opts & LISTMEM_SWAP32) {
            l_swap32(mpt, ptr, siz);
        }
    } else {
        mpt = (unsigned char *)ptr;
    }

    for (i = 0; i < siz; i += 16) {
        /* Check for same data (only print first line in this case) */
        if (oneout != 0  &&
                siz - i >= 16 && memcmp(lastlisted, mpt + i, 16) == 0) {
            if (alreadysame == 0) {
                os << "*\n";
                alreadysame = 1;
            }
            continue;
        }
        alreadysame = 0;
        /* Line header */
        os << std::setw(4) << i + adr << " ";

        /* Hexadecimal representation */
        for (j = 0; j < 16; j++) {
            if ((i + j) < siz) {
                os << hexa(mpt[i + j]) << ((j & 1) ? " " : "");
            } else {
                os << "  " << ((j & 1) ? " " : "");
            }
        }
        os << "  ";

        /* Also print ascii for values that fit */
        for (j = 0; j < 16; j++) {
            if ((i + j) < siz) {
                c = mpt[i + j];
                if (c >= 0x20 && c <= 0x7f) {
                    os << char(c);
                } else {
                    os << ".";
                }
            } else {
                os << " ";
            }
        }
        os << "\n";
        memcpy(lastlisted, mpt + i, 16);
        oneout = 1;
    }
    if (mpt != ptr) {
        free(mpt);
    }
}
