//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Feb 11 22:32:39 PST 2002
// Last Modified: Mon Feb 11 22:32:42 PST 2002
// Filename:      ...sig/examples/all/pmx2mus.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/score/pmx2mus.cpp
// Syntax:        C++; museinfo
//
// Description:   Convert SCORE PMX file into binary format.
//

#include "ScoreRecord.h"
#include "Options.h"

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

#ifndef OLDCPP
   #include <fstream>
   using namespace std;
#else
   #include <fstream.h>
#endif


// function declarations:
void   convertFile(const char* filename);
void   readScoreLine(istream& infile, ScoreRecord& record);
void   writeLittleFloat(ostream& out, float number);

// interface variables:
Options options;
int     verboseQ = 0;             // used with the -v option
const char* outputfilename = "";  // used with the -o option


//////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv) {
   options.define("v|verbose=b", "display debugging information in output");
   options.define("o|output=s:", "output filename if specified");
   options.process(argc, argv);
   verboseQ = options.getBoolean("verbose");
   outputfilename = options.getString("output");

   if (options.getArgCount() == 1) {
      convertFile(options.getArg(1));
   } else if (options.getArgCount() > 1) {
      for (int i=1; i<=options.getArgCount(); i++) {
         if (verboseQ) {
            cout << "processing file " << options.getArg(i) << "\n";
         }
         convertFile(options.getArg(i));
      }
   } else {
      cout << "Usage: " << argv[0] << " input.mus " << endl;
      exit(1);
   }

   return 0;
}


/////////////////////////////////////////////////////////////////////////


//////////////////////////////
//
// convertFile -- convert from binary to ASCII form.
//

void convertFile(const char* filename) {
   int length = strlen(filename);
   char ofilename[length + 100];

   if (strlen(outputfilename) > 0) {
      strcpy(ofilename, outputfilename);
   } else {
      strcpy(ofilename, filename);
      char* ptr = strrchr(ofilename, '.');
      if (ptr != NULL) {
         ptr[0] = '\0';
         strcat(ofilename, ".mus");
      } else {
         strcat(ofilename, ".mus");
      }
   }


   #ifndef OLDCPP
      fstream infile(filename, ios::in);
      #ifdef VISUAL
         fstream outfile(ofilename, ios::out | ios::binary);
      #else
         fstream outfile(ofilename, ios::out);
      #endif
   #else
      fstream infile(filename, ios::in | ios::nocreate);
      #ifdef VISUAL
         fstream outfile(ofilename, ios::out | ios::noreplace | ios::binary);
      #else
         fstream outfile(ofilename, ios::out | ios::noreplace);
      #endif
   #endif

   if (!infile.is_open()) {
      cerr << "Error: cannot read file: " << filename << endl;
      exit(1);
   }

   if (!outfile.is_open()) {
      cerr << "Error: cannot write file: " << ofilename << endl;
      exit(1);
   }


   // first write the number of numbers in the data file.
   // use a dummy value of 0 for now
   char dummy[2] = {0};
   outfile.write(dummy, 2);
   int writecount = 0;   // number of numbers which have been read

   ScoreRecord record;
   while (!infile.eof()) {
      readScoreLine(infile, record);
      writecount += record.writeBinary(outfile);
   }

   // write the trailer
   writeLittleFloat(outfile, 0.0);   // end of data marker
   union { float f; unsigned int i; } num;
   num.i = 0x50504153;
   writeLittleFloat(outfile, num.f);
   writeLittleFloat(outfile, 3.0);
   writeLittleFloat(outfile, 0.0);
   writeLittleFloat(outfile, 5.0);
   writeLittleFloat(outfile, -9999.0);
   writecount += 6;

   // go back an write the number of numbers written to the file
   unsigned char sbyte;
   outfile.seekg(0);
   sbyte = writecount & 0xff;
   outfile.write((char*)&sbyte, 1);
   sbyte = (writecount >> 8) & 0xff;
   outfile.write((char*)&sbyte, 1);

   infile.close();
   outfile.close();
}



//////////////////////////////
//
// readScoreLine -- read a PMX file for score data.
//   ignore any lines of text which do not start with a number.
//

void readScoreLine(istream& infile, ScoreRecord& record) {
   char buffer[1024] = {0};
   infile.getline(buffer, 1000, '\n');
   if (verboseQ) {
      cout << "Read line: " << buffer << endl;
   }

   record.clear();
   record.setAllocSize(100);
   char* ptr = strtok(buffer, "\n\t ");
   float number = 0.0;
   int counter = 0;
   if (ptr != NULL) {
      number = strtod(ptr, NULL);
      if (number == 0.0) {
         return;
      } else {
         record.setValue(counter++, number);
         ptr = strtok(NULL, "\n\t ");
         while (ptr != NULL) {
            number = strtod(ptr, NULL);
            record.setValue(counter++, number);
            ptr = strtok(NULL, "\n\t ");
         }
      }
   } 
}




///////////////////////////////
//
// writeLittleFloat --
//

void writeLittleFloat(ostream& out, float number) {
   union { float f; unsigned int i; } num;
   num.f = number;
   char byteinfo[4];
   byteinfo[0] = (char)( num.i        & 0xff);
   byteinfo[1] = (char)((num.i >> 8)  & 0xff);
   byteinfo[2] = (char)((num.i >> 16) & 0xff);
   byteinfo[3] = (char)((num.i >> 24) & 0xff);
   out.write(byteinfo, 4);
}



// md5sum: 3bbdd5b33a6f16a32236ff3341bc3b74 pmx2mus.cpp [20050403]