/* Copyright (C) 2003  John Whitney
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Author: John Whitney <jjw@linuxmail.org>
 */

#include <zlib.h>
#include <bzlib.h>
class IStream {
public:
  virtual int read(void *data, int num) = 0;
  template<class T>
  int get(T &data) {return read(&data, sizeof(data));}
};

class OStream {
public:
  virtual int write(const void *data, int num) = 0;
  template<class T>
  int put(const T &data) {return write(&data, sizeof(data));}
};

class IFStream : public IStream {
  FILE *file;
public:
  IFStream(const char *fname) {file = fopen(fname, "r");}
  virtual ~IFStream() {if (!bad()) fclose(file);}
  virtual int read(void *data, int num) {
    return fread(data, 1, num, file);
  }
  
  bool bad() {return file==NULL;}
};

unsigned read_word(IStream &f) {
  unsigned char b, b2;
  f.get(b);
  f.get(b2);
  return (b2<<8)+b;
}
unsigned read_dword(IStream &f) {
  unsigned low = read_word(f);
  return (read_word(f)<<16)+low;
}

void write_word(FILE *f, unsigned number) {
  unsigned char b = number&255;
  fwrite(&b, 1, 1, f);
  b = number>>8;
  fwrite(&b, 1, 1, f);
}

void write_dword(FILE *f, unsigned number) {
  write_word(f, number&65535);
  write_word(f, number>>16);
}



class OFStream : public OStream {
  void *file;
public:
  OFStream(const char *fname) {file = fopen(fname, "w");}
  virtual ~OFStream() {fclose((FILE*)file);}
  virtual int write(const void *data, int num) {
    return fwrite(data, 1, num, (FILE*)file);
  }
  bool bad() {return file==NULL;}
};

class GZ_IFStream : public IStream {
  void *file;
public:
  GZ_IFStream(char *fname) {file = gzopen(fname, "rb");}
  virtual ~GZ_IFStream() {if (!bad()) gzclose(file);}
  virtual int read(void *data, int num) {return gzread(file, data, num);}
  bool bad() {return (file==NULL);}
};

class GZ_OFStream : public OStream {
  void *file;
public:
  GZ_OFStream(char *fname) {file = gzopen(fname, "wb");}
  virtual ~GZ_OFStream() {gzclose(file);}
  virtual int write(const void *data, int num) {return gzwrite(file, (voidp)data, num);}
  bool bad() {return (file==NULL);}
};

class BZ_IFStream : public IStream {
  void *file;
public:
  BZ_IFStream(char *fname) {file = BZ2_bzopen(fname, "rb");}
  virtual ~BZ_IFStream() {if (!bad()) BZ2_bzclose(file);}
  virtual int read(void *data, int num) {return BZ2_bzread(file, data, num);}
  bool bad() {return (file==NULL);}
};

class Injectable_IStream : public IStream {
  struct Injected {
    void *buf;
    void *start;
    unsigned num;
    Injected(void *buf, unsigned num) {
      this->buf = start = new char[num];
      memcpy(start, buf, num);
      this->num = num;
    }
  };
  IStream &f;
  DList<Injected> list;
public:
  Injectable_IStream(IStream &F) : f(F) {}
  virtual void inject(void *data, int num) {
    void *d = new char[num];
    memcpy(d, data, num);
    list.push_front(new Injected(data, num));
  }
  virtual int read(void *vdata, int num) {
    int numread=0;
    char *data = (char*)vdata;
    while (!list.empty() && num >= list.first->obj->num) {
      unsigned numbuf = list.first->obj->num;
      memcpy(data, list.first->obj->buf, numbuf);
         data+=numbuf;
          num-=numbuf;
      numread+=numbuf;
      delete (char*)list.first->obj->start;
      delete list.first->obj;
      list.erase(list.first);
    }
    if (!list.empty()) {
      //char *cbuf;
      memcpy(data, list.first->obj->buf, num);
      list.first->obj->num-=num;
      //cbuf = (char*)list.first->obj->buf;
      list.first->obj->buf=(char*)list.first->obj->buf+num;
      //cbuf+=num;
      numread+=num;
    } else {
      numread+=f.read(data, num);
    }
    return numread;
  }
};


syntax highlighted by Code2HTML, v. 0.9.1