// Stream Search -- Hunt for a given series of bytes in a stream.

#include <string>
#include <fstream>
#include <iostream>
using namespace std;

#define STREAMOFFSET 0
#define BLOCKSIZE 131072
#define REPORTNBLOCKS 4096

int fcout = 0;
char * key = "magic key sequence";

void writeFile(char *buf) {
  // What should the next file be called?
  char *fileOut = new char[32];
  sprintf(fileOut, "Candidate-%04i.wallet", fcout);
  fcout++;

  // Create and write output.
  cout << "  Writing [" << fileOut << "], a possiblity.\n";
  ofstream outFile(fileOut, ofstream::binary);
  outFile.write(buf, BLOCKSIZE);

  // Clean up.
  outFile.close();
}

void keyCheck(char *buf, unsigned long block) {
  // Check this block for key bytes.
  int matchStart = 0, matchCount = 0;
  bool keyFound = false;

  // This loop checks for the key.
  for( int i=0; i<(BLOCKSIZE - sizeof(key)); i++ ) {
    if( buf[i] == key[matchCount] ) {
      // Good.
      matchCount++;
      if( matchCount == sizeof(key) ) {
        // We found the key!
        keyFound = true;
        break;
      }
    } else {
      // Bugger, next.
      i = ++matchStart;
      matchCount = 0;
    }
  }

  // If the key was found, write it out!
  if( keyFound ) {
    cout << "* Key found! Current block: " << block << "\n";
    writeFile(buf);
  }
}

int main(int argc, char *argv[]) {
  cout << "Stream Search Started.\n";

  // Did we recieve the source fspec?
  if( argc != 2 ) {
    // No.
    cout << "Syntax: StreamSearch /dev/blah\n";
    exit(1);
  }

  // Open the source.
  cout << "* Open [" << argv[1] << "] as binary input stream.\n";
  ifstream inFile(argv[1], ifstream::binary);

  // Check size.
  inFile.seekg(0, ifstream::end);
  cout << "  Stream length: " << inFile.tellg() << "\n";
  inFile.seekg(STREAMOFFSET);

  // Create the buffer for reading whole blocks into.
  char * buf = new char[BLOCKSIZE];

  // Loop over the input stream.
  unsigned long block = 0;
  while( !inFile.eof() ) {
    // Report every N blocks...
    if( (block%REPORTNBLOCKS) == 0 ) {
      cout << "- " << block << " blocks scanned.\n";
    }

    // Read next block into buffer.
    inFile.read(buf, BLOCKSIZE);
    keyCheck(buf, block);
    block++;
  }

  // Report program termination.
  cout << "Found " << fcout << " candidates.\n";
  cout << "Stream Search Terminating. (Normal EOP)\n";
}

// EOF
