/* mstream.c */

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

#include "log.h"
#include "mstream.h"

#undef EOF
#define EOF (-1)

struct mstream {
  const char *data;
  long int pos;
  size_t size;
};

mstream *
mstream_open(const char *block, size_t size)
{
  mstream *ms = malloc(sizeof(mstream));
  if (!ms)
  {
    LOG(("mstream_open: Out of memory\n"));
    return 0;
  }
  ms->data = block;
  ms->size = size;
  ms->pos = 0;
  return ms;
}

int
mstream_close(fmstream fm)
{
  free(fm.mp);
  return 0;
}

int
mstream_seek(fmstream fm, long int offset)
{
  if (offset < 0 || offset > fm.mp->size)
  {
    LOG(("mstream_seek: Bad offset\n"));
    return EOF;
  }
  fm.mp->pos = offset;
  return 0;
}

long int
mstream_tell(fmstream fm)
{
  return fm.mp->pos;
}

/* Here we deviate from stdio analogues, this nicely allocates the string
   to just the right length (with malloc) */
char *
mstream_read_line(fmstream fm)
{
  long int pos, p;
  char *result;
  if (fm.mp->pos >= fm.mp->size)
  {
    LOG(("mstream_read_line: 'EOF'\n"));
    return 0;
  }
  for (pos = 0;
       pos + fm.mp->pos < fm.mp->size && fm.mp->data[pos + fm.mp->pos] > 31;
       ++pos);
  result = malloc((size_t) pos + 1);
  if (!result)
  {
    LOG(("mstream_read_line: Out of memory\n"));
    return 0;
  }
  for (p = 0; p < pos; ++p)
    result[p] = fm.mp->data[p + fm.mp->pos];
  result[pos] = 0;
  fm.mp->pos += pos + 1;
  /* If not EOF, skip line terminator(s) */
  /*for (;
       fm.mp->pos < fm.mp->size && fm.mp->data[fm.mp->pos] < 32;
       ++fm.mp->pos);*/
  return result;
}

char *
mstream_read_bytes(fmstream fm, long int offset, size_t size, char *buffer)
{
  if (offset + size > fm.mp->size || offset + (long int) size < 0)
  {
    LOG(("Illegal offset/size for mstream_read_bytes\n"));
    return 0;
  }
  memcpy(buffer, fm.mp->data + offset, size);
  fm.mp->pos = offset + size;
  return buffer;
}

char *
mstream_read_pstring(fmstream fm)
{
  int size = fm.mp->data[fm.mp->pos];
  char *result;
  if (size + fm.mp->pos >= fm.mp->size)
  {
    LOG(("pstring overruns data\n"));
    return 0;
  }
  result = malloc(size + 1);
  if (!result)
  {
    LOG(("mstream_read_pstring out of memory\n"));
    return 0;
  }
  memcpy(result, fm.mp->data + fm.mp->pos + 1, size);
  result[size - 1] = 0;
  fm.mp->pos += (long int) size + (long int) 1;
  return result;
}

int
mstream_read_word16le(fmstream fm, fms_t16 *value)
{
  if (fm.mp->pos + 2 > fm.mp->size)
    return EOF;
  *value = fm.mp->data[fm.mp->pos] + (fm.mp->data[fm.mp->pos + 1] << 8);
  fm.mp->pos += 2;
  return 0;
}

int
mstream_read_word32le(fmstream fm, int *value)
{
  if (fm.mp->pos + 4 > fm.mp->size)
    return EOF;
  /* Might not be word-aligned */
  *value = fm.mp->data[fm.mp->pos]
           + (fm.mp->data[fm.mp->pos + 1] << 8)
           + (fm.mp->data[fm.mp->pos + 2] << 16)
           + (fm.mp->data[fm.mp->pos + 3] << 24);
  fm.mp->pos += 4;
  return 0;
}
