#include #include "Parser.h" using namespace std; // Constructors Parser::Parser() { rawBytes = 0; fileLength = 0; filePos = 0; stop = false; stopAddress = 0; force = false; } Parser::Parser(std::string filename) { rawBytes = 0; fileLength = 0; filePos = 0; stop = false; stopAddress = 0; force = false; SetFilename(filename); } // Deconstructors Parser::~Parser() { // Clear out temporary buffer delete[] rawBytes; // Clear out parsed buffer for(unsigned int i = 0; i < parsedBytes.size(); i++) { delete parsedBytes[i]; } } // Getters / Setters string Parser::GetFilename() { return filename; } void Parser::SetFilename(std::string value) { filename = value; Read(); } unsigned int Parser::GetStopAddress() { return stopAddress; } void Parser::SetStopAddress(unsigned int value) { stopAddress = value; } bool Parser::GetForce() { return force; } void Parser::SetForce(bool value) { force = value; } string Parser::GetParsedAsm() { string tmpStr; for(unsigned int i = 0; i < parsedString.size(); i++) { // Ensure each line isn't already a new-line, this prevents double or tripple empty lines from piling up if(parsedString[i] != "\n") tmpStr += parsedString[i] + "\n"; else tmpStr += parsedString[i]; } return tmpStr; } // File Operations // Absolutely no error checking at all - likely needs to be done at somepoint void Parser::Read() { // open File fstream tmpFile(filename, ios_base::in | ios_base::binary); // Get Length tmpFile.seekg(0, ios::end); fileLength = tmpFile.tellg(); tmpFile.seekg(0, ios::beg); // Allocate proper memory rawBytes = new char[fileLength]; // Read filedata tmpFile.read(rawBytes, fileLength); tmpFile.close(); rawBytesFixed = (unsigned char*)rawBytes; } // Code Operations void Parser::Parse(unsigned int offset) { filePos = offset; ParseNext(); } template bool Parser::ParseData(unsigned int& pos, bool reado) { // Create the class to use if correct and a dummy class for validating T* tmpC = 0; T dummy; // If the bytes are this data type then create and save it if(dummy.IsValid(&rawBytesFixed[pos])) { // Ensure this whole opperation isn't read-only (just peeking) if(!reado) { // Initialize the class tmpC = new T(&rawBytesFixed[pos]); // Push it onto the stack and it's assembly generation onto the output class parsedBytes.push_back(tmpC); // parsedString.push_back(tmpC->GenAsm()); // If the class had any arguments, increment the counter that much forward pos += tmpC->Arguments(); } return true; // Let the code know this class was valid } return false; // Let the code know this class wasn't valid } void Parser::ParseNext() // Parses the block immidiately following { stringstream tmpStr; stop = false; // Smart generation bool firstNonNote = false; // (unused so far)First byte wasn't a note or octacve switch, add ";Setup" comment bool firstNote = false; // (unused so far) First note or octave unsigned char lDataType = DATA_NA; bool newBranch = false; // Create a new branch stringstream pos; pos << "; " << hex << uppercase << (unsigned int)filePos; parsedString.push_back(pos.str()); unsigned int count = 1; // Counter for processed instructions newBranch = true; for(unsigned int i = filePos; (i <= fileLength) && (stop == false); i++) { if(newBranch) { stringstream _tmpBr; _tmpBr << "\n"; _tmpBr << "UnknSong_md_" << hex << i << ":"; parsedString.push_back(_tmpBr.str()); _tmpBr.str(""); newBranch = false; } // First peek to see what kind of data it is, then perform any pre and post setup if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_CALL; } else if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_DUTY; } else if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_JUMP; } else if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_MODULATION; } else if(ParseData(i, true)) { // Insert a newline after certain types if((lDataType == DATA_UNKCODE) || (lDataType == DATA_UNKEB)) parsedString.push_back("\n"); // If the previous item was a rest note then insert a new line else if(lDataType == DATA_NOTE) { Note* _tmpNote = (Note*)parsedBytes[parsedBytes.size() - 1]; if(_tmpNote->GetPitch() == _tmpNote->noteRst) parsedString.push_back("\n"); } ParseData(i); // Further indent each note parsedString[parsedString.size() - 1] = "\t" + parsedString[parsedString.size() - 1]; lDataType = DATA_NOTE; } else if(ParseData(i, true)) { // Insert new-line if previous line isn't a newline if(parsedString[parsedString.size() - 1] != "\n") parsedString.push_back("\n"); ParseData(i); lDataType = DATA_OCTAVE; } else if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_TEMPO; } else if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_VELOCITY; } else if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_VOLUME; } else if(ParseData(i, true)) // The opcode is 0xEB which is unknown and takes a 1-byte argument { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); lDataType = DATA_UNKEB; } else if(ParseData(i, true)) { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); if(!force) stop = true; // Raise the stop flag informing the parser to stop newBranch = true; lDataType = DATA_STOP; } else { if(lDataType == DATA_NOTE) parsedString.push_back("\n"); // Insert a newline after notes ParseData(i); // The opcode is unknown - process the raw byte and move on lDataType = DATA_UNKCODE; } // Put everything tabbed over at least 1 time to fix some weird RGBDS bug by pre-pending a tab character parsedString[parsedString.size() - 1] = "\t" + parsedString[parsedString.size() - 1]; // Append File Position in hexidecimal at end of line every 5 instructions if((count % 5) == 0) { stringstream _tmpCount; _tmpCount << hex << uppercase << i; parsedString[parsedString.size() - 1] = parsedString[parsedString.size() - 1] + "; " + _tmpCount.str(); } filePos = i; count++; // If the stop address parameter is set, break when we get there if( (stopAddress != 0) && (i >= stopAddress) ) break; } // Now record the postion we left off pos.str(""); pos << "; " << hex << uppercase << (unsigned int)filePos; parsedString.push_back(pos.str()); filePos += 1; // increment 1 for the start of the next possible song }