Skip to content

Instantly share code, notes, and snippets.

@chronologicaldot
Created February 29, 2020 01:40
Show Gist options
  • Save chronologicaldot/6a62e74e3810dfb80064041ac93891c6 to your computer and use it in GitHub Desktop.
Save chronologicaldot/6a62e74e3810dfb80064041ac93891c6 to your computer and use it in GitHub Desktop.
Program that converts Metasequoia (.mqo) files to Wavefront Object (.obj) and vice versa
/*
Cedar Cocoon
(c) 2011, Nicolaus Anderson
This is free and opensource software.
This software is provided AS IS without any guarantee that it will function.
The program is supposed to convert .mqo files to .obj files and vice versa.
Some functionality is not built in, thus certain aspects of models will not carry over
to the output file during conversion.
*/
// Using standard input/output library
#include <iostream>
// Using strings library
#include <string>
// Using file input and output libraries
#include <ostream>
using namespace std;
// Using file reader
#include "Freader.cpp"
// Using Linus Listor (linked list)
#include "LinusListorCp.cpp"
using namespace Linus;
// universal variables
// file to be converted
string input_file;
// file extension
string input_file_extension;
// new file extension
string output_file_extension;
// file reader
Freader reader;
// file output object
ofstream output;
// function prototypes
void converter_switch(int);
bool convert_mqo_to_obj();
// class for material information
class ELEM_material {
public:
// name of the material
string name;
// plain color info
float color[3]; // 0 = transparency/alpha, 1 = red, 2 = green, 3 = blue
// ambient color
float amb[3]; // 0 = red, 1 = green, 2 = blue
// diffuse color
float dif;
// specular color
float spec;
};
// class for vertices information
class ELEM_vertex {
public:
float x;
float y;
float z;
int group; // the group that this vertex belongs to
};
// class for face information
class ELEM_face {
public:
int v[4];
// v[4] = zero if only three vertices
bool v4th;
void swap(int elem, int elem2) {
int orig = 0;
orig = v[elem];
v[elem] = v[elem2];
v[elem2] = orig;
}
// material information
int mat; // which material (note: .mqo begins listing with 0; .obj begins with 1)
};
// Main function
void main()
{
// user response
string response;
// Console is displayed
cout << "\n\nWelcome to Cedar Cocoon Converter v1.0\nby Nic Anderson";
do {
// Request that the user input information about the file they want to convert
cout << "\n\nPlease enter the name of the file to convert\n:";
cin >> input_file;
cout << "\n\nPlease pick the type of conversion:"
<< "\n1) Metasequoia (.mqo) to Wavefront object (.obj)"
<< "\n:";
cin >> response;
converter_switch( atoi(response.c_str()) );
// ask if repetition desired
cout << "\n\nWould you like to convert another file?(y/n)\n:";
cin >> response;
// continue while the user wants to
} while ( response == "y" );
}
// Conversion switch: delegates which function will do the converting
void converter_switch(int conv)
{
switch (conv)
{
case 1: // mqo to obj
input_file_extension = "mqo";
output_file_extension = "obj";
if ( convert_mqo_to_obj() )
cout << "\n\nJob completed successfully.\n";
else
cout << "\n\nJob failed.";
default:
cout << "\n\nNo other conversions available.";
break;
}
}
//====================================================
// Conversion function for metasequoia to object files
/*
LOGIC:
Read from the mqo file using the reader function.
Skip the header, the scene and the scene sections.
*/
bool convert_mqo_to_obj()
{
// object for holding the information found in file
string package;
// string new output name
string output_name = input_file;
// set the file reader to the beginning of the new file
reader.read_position = 0;
reader.FileName = input_file.append("." + input_file_extension);
// create a new output file with the output extension appended
output.open( (output_name.append(".obj")).c_str() );
/* now the tricky part...
Metasequoia file format:
1) Header - basic info about the file
2) Scene - how Metasequoia is to display the file upon loadup (unimportant for .obj)
3) Material - color information that is shared amongst the faces
4) Object - contains the vertices and face information.
NOTE: There may be multiple objects. Furthermore, there is mirroring.
To handle mirroring, reserve x-number of vertices for every one vertex, where "x" is
the number of extra vertices per new vertex in the model (min 0, max 7 (for all 8
regions)).
*/
// skip the header and the scene info in the .mqo file
/*
1) Search for material definition sections
2) Scan for and through the "objects"
*/
/*
When scanning through the material definition sections:
Parts to look for:
> {
Preceded by a number indicating the the total number of materials and
followed by the definitions of the materials themselves.
> "name"
Material's name, which isn't important, but will be used for the name
of the texture in the .obj file anyways.
> col
This is followed by parenthesis containing the exact color information
in order of: alpha, red, green, blue. Values range from 0 to 1, and an
alpha setting of 1 gives no transparency (the material is a solid color).
>
*/
// list for containing all of the materials and their information
LListor<ELEM_material> * materials = new LListor<ELEM_material>();
// material to add to the list
ELEM_material mtr;
/*
When scanning through the objects:
Parts to look for:
> {
This is important considering that mirroring information can change with
multiple objects.
> vertex
Each object will have its own set of vertices. However, .obj files only
allow for all of the vertices to be placed in the beginning of them.
> face
As with vertices, each object will have its own set of faces. However, .obj
files only allow for all of the faces to be in the middle of the file
following the section giving the vertices.
*/
// mirroring type
//int mirror = 1; // 1 = separate, 2 = joined (not supported yet)
// axis of reflection
int mirror_axis = 0;
/*
>> 0 for none
>> 1 for x alone
>> 2 for y alone
>> 3 for y and x
>> 4 for z alone
>> 5 for z and x
>> 6 for z and y
>> 7 for x, y, and z axis
*/
// multiplicand for the vertex index
int multiplicand = 1;
// list for containing all of the vertices and their information
LListor<ELEM_vertex> * vertices = new LListor<ELEM_vertex>();
// list for containing all of the faces and their information
LListor<ELEM_face> * faces = new LListor<ELEM_face>();
// vertex to add to the list
ELEM_vertex vtx;
// face to add to the list
ELEM_face fce;
// special characters
#define all_specials 5
char specials[all_specials];
specials[0] = '\n';
specials[1] = '(';
specials[2] = ')';
specials[3] = '{';
specials[4] = '}';
// identify all of the objects
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
/*
NOTE: A .mtl file must be created to save the material of the model.
// found a material definition section
if ( package == "Material" ) {
// search for the open curly bracket
// Note, in doing so, we skip the indicator of the total number of materials.
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
} while ( package != "{" );
cout << "\nNow scanning materials:"
do { // while there are materials
// store the first one in the package in case this is the last one
package = reader.readFromFile_sys_nse(all_specials, specials);
if ( package == "}" ) break;
// the first item should be the name
package = reader.readFromFile_ns(false);
mtr.name = package;
// scan for the material information until at the end of the line
do {
package = reader.readFromFile_ns(true);
// if color info
if ( package == "col" ) {
// first search should turn up an open parenthesis
package = reader.readFromFile_sys_ns(all_specials, specials);
// the next four searches should give the argb values
mtr.color[0] = reader.readFromFile_ns(false);
mtr.color[1] = reader.readFromFile_ns(false);
mtr.color[2] = reader.readFromFile_ns(false);
mtr.color[3] = reader.readFromFile_ns(false);
}
} while ( package != "\n" );
// creation of a new slot in the list
materials = materials->addNewLink(mtr, false);
// NOTE: the last link in the chain will be duplicate of the last
cout << ".";
} while ( package != "}" );
}
*/
// found an object
if ( package == "Object" ) {
// indicate that this is the next group being examined
curr_grp += 1;
// search for the open curly bracket
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
} while ( package != "{" );
// search for the keyword "vertex" or "mirror_axis"
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
// there is a reflection axis
if ( package == "mirror_axis" ) {
// identify the axis
package = reader.readFromFile_sys_ns(all_specials, specials);
// convert this to a numeric value
mirror_axis = atoi( package.c_str() );
// set the multiplicand
switch(mirror_axis)
{
case 1:
case 2: multiplicand = 2; break;
case 3: multiplicand = 4; break;
case 4: multiplicand = 2; break;
case 5:
case 6: multiplicand = 4; break;
case 7: multiplicand = 8; break;
default: multiplicand = 1; break;
}
}
} while ( package != "vertex" );
/* Following "vertex" is a number giving the total number of
vertices, which in turn is followed by an open curly bracket. */
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
} while ( package != "{" );
/* Now that the keyword "vertex" has been found:
Examine each vertex, each of which consists of three numbers on a
single line. */
// update
cout << "\nNow scanning vertices:";
do { // while there are vertices
// store the first one in the package in case this is the last one
package = reader.readFromFile_sys_nse(all_specials, specials);
if ( package == "}" ) break;
// new vertex
vtx.x = atof( package.c_str() );
vtx.y = atof( reader.readFromFile_sys_nse(all_specials, specials).c_str() );
vtx.z = atof( reader.readFromFile_sys_nse(all_specials, specials).c_str() );
// copy the new vertex to the list
vertices->setThisItem(vtx);
// check for mirroring
switch ( mirror_axis )
{
/* NOTE: mirroring requires that the new vertex have a
negative/opposite value of the original.
*/
case 1: // x axis
vtx.x = -vtx.x;
vertices = vertices->addNewLink(vtx, false);
break;
case 2: // y axis
vtx.y = -vtx.y;
vertices = vertices->addNewLink(vtx, false);
break;
case 3: // x and y axis
// x axis
vtx.x = -vtx.x; // now negative
vertices = vertices->addNewLink(vtx, false);
// y axis
vtx.x = -vtx.x; // now positive
vtx.y = -vtx.y; // now negative
vertices = vertices->addNewLink(vtx, false);
// x and y axis
vtx.x = -vtx.x; // now negative
vertices = vertices->addNewLink(vtx, false);
break;
case 4: // z axis
vtx.z = -vtx.z;
vertices = vertices->addNewLink(vtx, false);
break;
case 5: // x and z axis
// x axis
vtx.x = -vtx.x;
vertices = vertices->addNewLink(vtx, false);
// z axis
vtx.x = -vtx.x;
vtx.z = -vtx.z;
vertices = vertices->addNewLink(vtx, false);
// x and z axis
vtx.x = -vtx.x;
vertices = vertices->addNewLink(vtx, false);
break;
case 6: // y and z axis
// y axis
vtx.y = -vtx.y;
vertices = vertices->addNewLink(vtx, false);
// z axis
vtx.y = -vtx.y;
vtx.z = -vtx.z;
vertices = vertices->addNewLink(vtx, false);
// y and z axis
vtx.y = -vtx.y;
vertices = vertices->addNewLink(vtx, false);
break;
case 7: // x, y, and z axis
// x axis
vtx.x = -vtx.x; // now negative
vertices = vertices->addNewLink(vtx, false);
// y axis
vtx.x = -vtx.x; // now positive
vtx.y = -vtx.y; // now negative
vertices = vertices->addNewLink(vtx, false);
// z axis
vtx.y = -vtx.y; // now positive
vtx.z = -vtx.z; // now negative
vertices = vertices->addNewLink(vtx, false);
// x and y axis
vtx.x = -vtx.x; // now negative
vtx.y = -vtx.y; // now negative
vtx.z = -vtx.z; // now positive
vertices = vertices->addNewLink(vtx, false);
// x and z axis
vtx.y = -vtx.y; // now positive
vtx.z = -vtx.z; // now negative
vertices = vertices->addNewLink(vtx, false);
// y and z axis
vtx.x = -vtx.x; // now positive
vtx.y = -vtx.y; // now negative
vertices = vertices->addNewLink(vtx, false);
// x, y, and z axis
vtx.x = -vtx.x; // now negative
vertices = vertices->addNewLink(vtx, false);
break;
default: break; // no mirroring
}
// creation of a new slot in the list
vertices = vertices->addNewLink(vtx, false);
// NOTE: the last link in the chain will be duplicate of the last
cout << ".";
} while ( package != "}" );
// the next thing should be "face"
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
} while ( package != "face" && package != "}" );
// if at the end of the object
if ( package == "}" ) break;
/* "face" will be followed by a number indicating the total number of faces
in the model and an open curly bracket */
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
} while ( package != "{" );
// update
cout << "\nNow scanning faces:";
// obtain all of the faces
do {
// number indicating the number of vertices in the face
package = reader.readFromFile_sys_nse(all_specials, specials);
// in case there are no more faces
if ( package == "}" ) break;
switch ( atoi( package.c_str() ) )
{
case 3: // ensure that the last face vertex index is set to zero
fce.v[3] = 0; // no fourth vertex
fce.v4th = false;
break;
default: // assume four
fce.v4th = true;
break;
}
/* Recall that for mirroring, vertices were added. Therefore, the
vertex index is shifted up so many places to account for the new
points from the mirroring. The shift, based on the number of vertices
added by the switch (see above) will be 1, 3, or 7.
This number + 1 will be the multiplicand of the index to give the
new index.
*/
// each face is on a new line
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
if ( package == "V" ) // vertices
{
// first search should turn up an open parenthesis
package = reader.readFromFile_sys_ns(all_specials, specials);
// next three searches should be of the vertices composing the face
// new vertex index = old vertex index * multiplicand
fce.v[0] = atoi( reader.readFromFile_sys_ns(all_specials, specials).c_str() )
* multiplicand;
fce.v[1] = atoi( reader.readFromFile_sys_ns(all_specials, specials).c_str() )
* multiplicand;
fce.v[2] = atoi( reader.readFromFile_sys_ns(all_specials, specials).c_str() )
* multiplicand;
// if there is a fourth vertex
if ( fce.v4th )
fce.v[3] = atoi( reader.readFromFile_sys_ns(all_specials, specials).c_str() )
* multiplicand;
else
fce.v[3] = 0;
// add this face to the list
faces->setThisItem(fce);
// now consider the mirror axis (ignoring mirror type for now)
switch( mirror_axis )
{
case 1: // x axis
case 2: // y axis
fce.v[0] = fce.v[0] + 1;
fce.v[1] = fce.v[1] + 1;
fce.v[2] = fce.v[2] + 1;
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) // if there is a fourth vertex
{
fce.v[3] = fce.v[3] + 1;
fce.swap(2,3);
}
faces = faces->addNewLink(fce,false); // add the new face
break;
case 3: // x and y
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) fce.swap(2,3);
for ( int i = 1; i <= 3; i++ )
{
fce.v[0] = fce.v[0] + 1;
fce.v[1] = fce.v[1] + 1;
fce.v[2] = fce.v[2] + 1;
if ( fce.v4th ) // if there is a fourth vertex
fce.v[3] = fce.v[3] + 1;
if ( i == 3 )
{
// switch back
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) fce.swap(2,3);
}
faces = faces->addNewLink(fce,false); // add the new face
}
break;
case 4: // z
fce.v[0] = fce.v[0] + 1;
fce.v[1] = fce.v[1] + 1;
fce.v[2] = fce.v[2] + 1;
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) // if there is a fourth vertex
{
fce.v[3] = fce.v[3] + 1;
fce.swap(2,3);
}
faces = faces->addNewLink(fce,false); // add the new face
break;
case 5: // x and z
case 6: // y and z
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) fce.swap(2,3);
for ( int i = 1; i <= 3; i++ )
{
fce.v[0] = fce.v[0] + 1;
fce.v[1] = fce.v[1] + 1;
fce.v[2] = fce.v[2] + 1;
if ( fce.v4th ) // if there is a fourth vertex
fce.v[3] = fce.v[3] + 1;
if ( i == 3 )
{
// switch back
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) fce.swap(2,3);
}
faces = faces->addNewLink(fce,false); // add the new face
}
break;
case 7: // x, y, and z
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) fce.swap(2,3);
for ( int i = 1; i <= 7; i++ )
{
fce.v[0] = fce.v[0] + 1;
fce.v[1] = fce.v[1] + 1;
fce.v[2] = fce.v[2] + 1;
if ( fce.v4th ) // if there is a fourth vertex
fce.v[3] = fce.v[3] + 1;
switch(i)
{
case 4:
case 7:
// switch back
fce.swap(0,1); // swap 0 and 1
if ( fce.v4th ) fce.swap(2,3);
break;
default: break;
}
faces = faces->addNewLink(fce,false); // add the new face
}
break;
default: break;
}
}
// if ( package == "M" ) // material - not implemented yet
// if ( package == "UV" ) // lighting - not implemented yet
} while ( package != "\n" );
// add the new face to the list
faces = faces->addNewLink(fce,false);
cout << ".";
} while ( package != "}" );
// Now search fo the last curly bracket defining the "Object"
do {
package = reader.readFromFile_sys_ns(all_specials, specials);
} while ( package != "}" );
}
} while ( package != "Eof" );
/* TO .OBJ ******************************* */
/* Now that the file information has been obtained, store it in an ANSI
encoded text file in the .obj format.
*/
// generic "group"
output << "g only\n";
// identify the total number of vertices as well as return the pointer to the first
int v_size = 0;
// assume starting at the last link
while ( vertices->hasWhat() == 1 || vertices->hasWhat() == 3 ) {
vertices = vertices->getPriorLink();
v_size++;
}
// identify the total number of faces as well as return the pointer to the first
int f_size = 0;
// assume starting at the last link
while ( faces->hasWhat() == 1 || faces->hasWhat() == 3 ) {
faces = faces->getPriorLink();
f_size++;
}
// add the vertex information to the file
for ( int vx = 0; vx < v_size; vx++ )
{
output << "\nv "
<< vertices->getThisItemCopy().x << " "
<< vertices->getThisItemCopy().y << " "
<< vertices->getThisItemCopy().z << " "
;
vertices = vertices->getPostLink();
}
// add the face information to the file
for ( int fa = 0; fa < f_size; fa++ )
{
output << "\nf "
<< (faces->getThisItemCopy().v[0] + 1) << " "
<< (faces->getThisItemCopy().v[1] + 1) << " "
<< (faces->getThisItemCopy().v[2] + 1) << " "
;
if ( faces->getThisItemCopy().v4th )
output << (faces->getThisItemCopy().v[3] + 1) << " ";
faces = faces->getPostLink();
}
// completed the conversion
return true;
}
#include <fstream>
#include <string>
using namespace::std;
/* Class functions for obtaining characters and strings
from standard text files.
(C) Nic Anderson
July 7, 2011
*/
class Freader {
public:
int read_position;
string FileName;
/* Functions used to call readFromFile functions repeatedly when spaces are not
desired. */
string readFromFile_ns(bool delim)
{
string ret;
do {
ret = readFromFile( delim );
} while ( ret == " " );
return ret;
}
string readFromFile_sys_ns(int _MaxSystemChars, char system_chars[])
{
string ret;
do {
ret = readFromFile_sys( _MaxSystemChars, system_chars );
} while ( ret == " " );
return ret;
}
string readFromFile_sys_nse(int _MaxSystemChars, char system_chars[])
{
string ret;
do {
ret = readFromFile_sys( _MaxSystemChars, system_chars );
} while ( ret == " " || ret == "\n" );
return ret;
}
/* Function used to obtain objects (tokens) from files.
\param delim - whether the newline character is singly considered a token. */
string readFromFile(bool delim)
{
/* prepare and open a file for reading */
ifstream theFile;
theFile.open( FileName.c_str() );
/* Set the get() position to the latest location. */
theFile.seekg( read_position, ios::cur );
/* read individual characters from the file */
// prep: create necessary variables
char character = ' '; /* This stores the character to be placed in a
string and returned to the function that called this
function */
bool set = false; /* Indicates whether or not the loop that collects
characters from file (and puts them in a string) should
be repeated. */
bool set_fail = false; /* Indicates whether or not to reset the set variable so
that the character-collecting loop can continue. */
int count = 0; /* Used in for-loop to compare system characters with
each new character extracted from file. */
// initialize a string to return to the function that called this one
string strng;
strng.clear();
//**********************************
// check to see if it is possible to get a character
character = theFile.get();
// do only if it is possible to extract characters
if ( theFile.good() )
{
/* Revert to the first character. This prevents the
interior do-while loop from messing up. */
theFile.seekg(-1, ios::cur);
// get characters from file
do
{
// get a character from the file
character = theFile.get();
/* Get rid of any tabs. Turn them into spaces. */
if (character == ' ')
{ character = ' '; }
/* Terminate search for letters if the character obtained from
file is a space. */
if (character == ' ')
{
set = true; // indicate the loop should end
}
/* Terminate search for letters if the character obtained from
file is a newline. */
if ( character == '\n' && delim == true )
{
set = true; // indicate the loop should end
}
// Add the character if it is going to be the only one in the word.
if ( set == false || strng.length() == 0 )
{
// Add the character to the word
strng.push_back( character );
/* Consider adding another character to the word ONLY if it
is a number, since this might be a floating point decimal
number. */
if ( character == '.'
&& ( theFile.peek() == '0' || theFile.peek() == '1'
|| theFile.peek() == '2' || theFile.peek() == '3'
|| theFile.peek() == '4' || theFile.peek() == '5'
|| theFile.peek() == '6' || theFile.peek() == '7'
|| theFile.peek() == '8' || theFile.peek() == '9' ) )
{
set = false;
}
} else {
// Apparently, set was true, so a system character was found
/* Only let a period be added to the word as long as there
is no other period in the word already. */
if ( character == '.'
&& ( strng[0] == '0' || strng[0] == '1'
|| strng[0] == '2' || strng[0] == '3'
|| strng[0] == '4' || strng[0] == '5'
|| strng[0] == '6' || strng[0] == '7'
|| strng[0] == '8' || strng[0] == '9'
) )
/* NOTE: Only the first character in the word needs
to be looked at, since the period will be added if it
is going to be the first character in the word anyways.
A number at the beginning of the word automatically
indicates a floating decimal number.
Minus signs are to be handled under the '-' data manipulator. */
{
// The system character is a period.
// Check for a period already in the word
set_fail = false; // assume there is no period in the word
// start looking for a period in the word
for (unsigned int w = 0; w < strng.length(); w++)
{
if (strng[w] == '.')
{ set_fail = true; }
}
if ( set_fail == true )
{
/* If there is a period in the word already, allow
the second period to be picked up next time. */
theFile.seekg(-1, ios::cur);
} else {
// Add the period to the end of the word
strng.push_back( character );
/* only consider adding another character to the string
if the character is a decimal place or a number */
if ( theFile.peek() == '0' || theFile.peek() == '1'
|| theFile.peek() == '2' || theFile.peek() == '3'
|| theFile.peek() == '4' || theFile.peek() == '5'
|| theFile.peek() == '6' || theFile.peek() == '7'
|| theFile.peek() == '8' || theFile.peek() == '9' )
{
set = false;
}
}
} else {
/* The string length was not zero, or the character was
not a period. */
/* The system character was not added to the end of a word,
so allow it to be extracted next time. */
theFile.seekg(-1, ios::cur);
}
}
/* Don't collect more characters if the file is at its end.
Simply return what has been collected already. */
if ( !theFile.good() && strng.length() > 0 )
{
/* Set the reading position to the point before the end. */
theFile.seekg( -1, ios::cur );
/* Store the reading position. */
read_position = theFile.tellg();
/* Return what has been acquired. */
return strng;
}
} while ( set == false && theFile.good() );
/* Store the reading position. */
read_position = theFile.tellg();
//************************************
// WHAT TO DO IF AT THE END OF MAIN
if ( !theFile.good() )
{
/* assign "end_main" to the return string to indicate the
end of the file has been reached */
strng = "end_main";
// close the file
theFile.close();
// end the readFromFile() use here
return strng;
} // endif WHAT TO DO IF AT THE END OF MAIN
else // normal shut down
{
theFile.close();
}
} //*********************************
// endif - file fails to open
else {
/* assign "end_main" to the return string to indicate the
end of the file has been reached */
strng = "end_main";
// close the file
theFile.close();
} //**********************************
// return the string
return strng;
}
//====================================================
/* Function used to obtain objects (tokens) from files
considering special characters.
\param delim - whether the newline character is singly considered a token. */
string readFromFile_sys(int _MaxSystemChars, char system_chars[])
{
/* prepare and open a file for reading */
ifstream theFile;
theFile.open( FileName.c_str() );
/* Set the get() position to the latest location. */
theFile.seekg( read_position, ios::cur );
/* read individual characters from the file */
// prep: create necessary variables
char character = ' '; /* This stores the character to be placed in a
string and returned to the function that called this
function */
bool set = false; /* Indicates whether or not the loop that collects
characters from file (and puts them in a string) should
be repeated. */
bool set_fail = false; /* Indicates whether or not to reset the set variable so
that the character-collecting loop can continue. */
int count = 0; /* Used in for-loop to compare system characters with
each new character extracted from file. */
// initialize a string to return to the function that called this one
string strng;
strng.clear();
//**********************************
// check to see if it is possible to get a character
character = theFile.get();
// do only if it is possible to extract characters
if ( theFile.good() )
{
/* Revert to the first character. This prevents the
interior do-while loop from messing up. */
theFile.seekg(-1, ios::cur);
// get characters from file
do
{
// get a character from the file
character = theFile.get();
/* Get rid of any tabs. Turn them into spaces. */
if (character == ' ')
{ character = ' '; }
/* check to make sure that the character is not a system command
character */
for (count = 0; count < _MaxSystemChars; count++)
{
/* If a character is a system character, plan to get rid of it. */
if ( character == system_chars[count] )
{ set = true; }
}
/* Terminate search for letters if the character obtained from
file is a space. */
if (character == ' ')
{
set = true; // indicate the loop should end
}
// Add the character if it is going to be the only one in the word.
if ( set == false || strng.length() == 0 )
{
// Add the character to the word
strng.push_back( character );
/* Consider adding another character to the word ONLY if it
is a number, since this might be a floating point decimal
number. */
if ( character == '.'
&& ( theFile.peek() == '0' || theFile.peek() == '1'
|| theFile.peek() == '2' || theFile.peek() == '3'
|| theFile.peek() == '4' || theFile.peek() == '5'
|| theFile.peek() == '6' || theFile.peek() == '7'
|| theFile.peek() == '8' || theFile.peek() == '9' ) )
{
set = false;
}
} else {
// Apparently, set was true, so a system character was found
/* Only let a period be added to the word as long as there
is no other period in the word already. */
if ( character == '.'
&& ( strng[0] == '0' || strng[0] == '1'
|| strng[0] == '2' || strng[0] == '3'
|| strng[0] == '4' || strng[0] == '5'
|| strng[0] == '6' || strng[0] == '7'
|| strng[0] == '8' || strng[0] == '9'
) )
/* NOTE: Only the first character in the word needs
to be looked at, since the period will be added if it
is going to be the first character in the word anyways.
A number at the beginning of the word automatically
indicates a floating decimal number.
Minus signs are to be handled under the '-' data manipulator. */
{
// The system character is a period.
// Check for a period already in the word
set_fail = false; // assume there is no period in the word
// start looking for a period in the word
for (unsigned int w = 0; w < strng.length(); w++)
{
if (strng[w] == '.')
{ set_fail = true; }
}
if ( set_fail == true )
{
/* If there is a period in the word already, allow
the second period to be picked up next time. */
theFile.seekg(-1, ios::cur);
} else {
// Add the period to the end of the word
strng.push_back( character );
/* only consider adding another character to the string
if the character is a decimal place or a number */
if ( theFile.peek() == '0' || theFile.peek() == '1'
|| theFile.peek() == '2' || theFile.peek() == '3'
|| theFile.peek() == '4' || theFile.peek() == '5'
|| theFile.peek() == '6' || theFile.peek() == '7'
|| theFile.peek() == '8' || theFile.peek() == '9' )
{
set = false;
}
}
} else {
/* The string length was not zero, or the character was
not a period. */
/* The system character was not added to the end of a word,
so allow it to be extracted next time. */
theFile.seekg(-1, ios::cur);
}
}
/* Don't collect more characters if the file is at its end.
Simply return what has been collected already. */
if ( !theFile.good() && strng.length() > 0 )
{
/* Set the reading position to the point before the end. */
theFile.seekg( -1, ios::cur );
/* Store the reading position. */
read_position = theFile.tellg();
/* Return what has been acquired. */
return strng;
}
} while ( set == false && theFile.good() );
/* Store the reading position. */
read_position = theFile.tellg();
//************************************
// WHAT TO DO IF AT THE END OF MAIN
if ( !theFile.good() )
{
/* assign "end_main" to the return string to indicate the
end of the file has been reached */
strng = "end_main";
// close the file
theFile.close();
// end the readFromFile() use here
return strng;
} // endif WHAT TO DO IF AT THE END OF MAIN
else // normal shut down
{
theFile.close();
}
} //*********************************
// endif - file fails to open
else {
/* assign "end_main" to the return string to indicate the
end of the file has been reached */
strng = "end_main";
// close the file
theFile.close();
} //**********************************
// return the string
return strng;
}
//==============================================================
/* Function for getting a select number of characters from file */
string readFileChars( int obtain )
{
/* prepare and open a file for reading */
ifstream theFile;
theFile.open( FileName.c_str() );
/* Set the get() position to the latest location. */
theFile.seekg( read_position, ios::cur );
/* read individual characters from the file */
// prep: create necessary variables
char character = ' '; /* This stores the character to be placed in a
string and returned to the function that called this
function */
int count = 0; /* Used in while loop to compare the number of characters
stored in the string with the number of characters
desired in the string. */
// initialize a string to return to the function that called this one
string strng;
//**********************************
// check to see if it is possible to get a character
character = theFile.get();
// do only if it is possible to extract characters
if ( theFile.good() )
{
/* Revert to the first character. This prevents the
interior do-while loop from messing up. */
theFile.seekg(-1, ios::cur);
// get characters from file
do
{
// get a character from the file
character = theFile.get();
// store the character in the string
strng.push_back( character );
// indicate another character has been put in the string
count++;
} while ( theFile.good() && count < obtain );
/* Store the reading position. */
read_position = theFile.tellg();
//************************************
// WHAT TO DO IF AT THE END OF MAIN
if ( !theFile.good() )
{
/* assign "end_main" to the return string to indicate the
end of the file has been reached */
strng = "end_main";
// close the file
theFile.close();
// end the readFromFile() use here
return strng;
} // endif WHAT TO DO IF AT THE END OF MAIN
else // normal shut down
{
theFile.close();
}
} //*********************************
// endif - file fails to open
else {
/* assign "end_main" to the return string to indicate the
end of the file has been reached */
strng = "end_main";
// close the file
theFile.close();
} //**********************************
// return the string
return strng;
}
};
/*
Name: Linus Listor for C++
(Linus Listor C Plus / Linus Listor Cp )
(C) Nic Anderson
Date: June 15, 2011
See LinusListorCp.h for copyright details.
Desc: This is a simple linked list for C++ programs.
*/
#ifndef _LLISTOR_
#define _LLISTOR_
// include the header
#include "LinusListorCp.h"
namespace Linus
{
template<class Unk>
LListor<Unk>::LListor()
{
// indicate this link has neither prior nor post links
possession_state = s_none;
}
// for the item being stored
template<class Unk>
Unk* LListor<Unk>::getThisItemAddress()
{
return &ThisItem;
}
template<class Unk>
Unk LListor<Unk>::getThisItemCopy()
{
return ThisItem;
}
template<class Unk>
void LListor<Unk>::setThisItem(Unk newItem)
{
ThisItem = newItem;
}
// for the prior and post links
template<class Unk>
LListor<Unk>* LListor<Unk>::getPriorLink()
{
if ( possession_state == s_prior || possession_state == s_both )
return PriorLink;
else return 0;
}
template<class Unk>
void LListor<Unk>::setPriorLink( LListor<Unk>* newPrior )
{
// set the new link (NOTE: Does not delete the old one in case it is needed)
PriorLink = newPrior;
// adjust the possession state if necessary
switch ( possession_state )
{
case s_none: possession_state = s_prior; break;
case s_prior: break;
case s_post: possession_state = s_both; break;
case s_both: break;
}
}
template<class Unk>
LListor<Unk>* LListor<Unk>::getPostLink()
{
if ( possession_state == s_post || possession_state == s_both )
return PostLink;
else return 0;
}
template<class Unk>
void LListor<Unk>::setPostLink( LListor<Unk>* newPost )
{
// set the new link (NOTE: Does not delete the old one in case it is needed)
PostLink = newPost;
// adjust the possession state if necessary
switch ( possession_state )
{
case s_none: possession_state = s_post; break;
case s_prior: possession_state = s_both; break;
case s_post: break;
case s_both: break;
}
}
// possession state
template<class Unk>
si32 LListor<Unk>::hasWhat()
{
switch ( possession_state )
{
case s_none: return 0;
case s_prior: return 1;
case s_post: return 2;
case s_both: return 3;
}
}
// for adding on new links onto the chain
template<class Unk>
LListor<Unk>* LListor<Unk>::addNewLink( Unk item , bool head )
{
// save the location of the new link, based on whether it is the prior or post
if (head) // prior
{
// determine if this link has a prior link already
switch ( possession_state )
{
case s_none:
// create a new link - use "new" to ensure it isn't deleted from the stack
PriorLink = new LListor<Unk>;
// assign it the new item
PriorLink->setThisItem(item);
// indicate the prior link now has a post link
PriorLink->setPostLink(this);
// indicate that this now has a prior link
possession_state = s_prior;
break;
case s_prior:
// considering this has a prior, send the item to that link to be added
return PriorLink->addNewLink( item, true );
//break; - unnecessary after a "return" statement
case s_post:
// create a new link - use "new" to ensure it isn't deleted from the stack
PriorLink = new LListor<Unk>;
// assign it the new item
PriorLink->setThisItem(item);
// indicate the prior link now has a post link
PriorLink->setPostLink(this);
// indicate this link now has both links
possession_state = s_both;
break;
case s_both:
// considering this has a prior, send the item to that link to be added
return PriorLink->addNewLink( item, true );
//break; - unnecessary after a return statement
}
// return the pointer to the link of the new object
return PriorLink;
} else {
// indicate that this link now has a new post link
switch ( possession_state )
{
case s_none:
// create a new link - use "new" to ensure it isn't deleted from the stack
PostLink = new LListor<Unk>;
// assign it the new item
PostLink->setThisItem(item);
// indicate the post link now has a prior link
PostLink->setPriorLink(this);
// indicate this link now has a post link
possession_state = s_post;
break;
case s_prior:
// create a new link - use "new" to ensure it isn't deleted from the stack
PostLink = new LListor<Unk>;
// assign it the new item
PostLink->setThisItem(item);
// indicate the post link now has a prior link
PostLink->setPriorLink(this);
// indicate this link now has both links
possession_state = s_both;
break;
case s_post:
// considering this has a post, send the item to that link to be added
return PostLink->addNewLink( item, false );
//break; - unnecessary after a return statement
case s_both:
// considering this has a post, send the item to that link to be added
return PostLink->addNewLink( item, false );
//break; - unnecessary after a return statement
}
// return the pointer to the link of the new object
return PostLink;
}
}
// for removing the current link from its current chain without deleting it
template<class Unk>
void LListor<Unk>::extractCurrLink() const
{
// check for whether this link has a prior and a post link
if ( possession_state == s_both )
{
PriorLink->setPostLink( PostLink );
PostLink->setPriorLink( PriorLink );
}
switch( possession_state )
{
case s_prior:
switch ( PriorLink->possession_state )
{
case s_post: PriorLink->possession_state = s_none; break;
case s_both: PriorLink->possession_state = s_prior; break;
default: break;
}
break;
case s_post: // flag that the prior link has been dropped
switch ( PostLink->possession_state )
{
case s_prior: PostLink->possession_state = s_none; break;
case s_both: PostLink->possession_state = s_post; break;
default: break;
}
break;
case s_both: // bridge the chain
PriorLink->setPostLink( PostLink );
PostLink->setPriorLink( PriorLink );
break;
default: break;
}
}
// for properly deleting the current link in the chain
template<class Unk>
void LListor<Unk>::delCurrLink() const
{
// check for whether this link has a prior and a post link
extractCurrLink();
// finally, delete this link
delete this;
}
// for scanning the chain
template<class Unk>
LListor<Unk>* LListor<Unk>::getFirstLink()
{
// if this link itself has a prior link, call it
if ( possession_state == s_prior || possession_state == s_both )
return PriorLink->getFirstLink();
else
return this; // otherwise, this (the current) link is the one to return
}
template<class Unk>
LListor<Unk>* LListor<Unk>::getLastLink()
{
// if this link itself has a prior link, call it
if ( possession_state == s_post || possession_state == s_both )
return PostLink->getLastLink();
else
return this; // otherwise, this (the current) link is the one to return
}
}
#endif
/*
This is the header file for LinusListorCp.cpp
(C) Nic Anderson
Date: June 15, 2011
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
// don't double define this
// if "_Linus_INCLUDED" is not defined...
#ifndef _Linus_INCLUDED_
// ... define it (and define the namespace as well)
#define _Linus_INCLUDED_
// simple definitions for usage with this list
#ifdef _MSC_VER
typedef __int32 si32;
#else
typedef signed int si32;
#endif
namespace Linus
{
// The main class in Linus - the actual linked list
template<class Unk>
class LListor
{
private:
// pointers to the prior link and post link (links before and after this one)
LListor<Unk>* PriorLink;
LListor<Unk>* PostLink;
/* indicates the posession state:
if this link has a link before it, after it, or both */
enum {
s_none = 0,
s_prior,
s_post,
s_both
} possession_state;
public:
// object being pointed to
Unk ThisItem;
//! Default constructor
/** Sets the possession state to zero, indicating this link
has neither a link that preceeds it nor one that follows. */
LListor();
// Dealing with the user's item being stored in the list
//! Return item address
/** This returns the address of the item/object
being pointed to by this class's pointer. */
virtual Unk* getThisItemAddress();
//! Return item copy
/** This returns a copy of the item/object
being pointed to by this class's pointer. */
virtual Unk getThisItemCopy();
//! Set/replace item
/** This sets a new item for this pointer or
replaces the current one. */
virtual void setThisItem(Unk newItem);
// Dealing with the chain
//! Return prior link
/** This returns a pointer to the link that
comes before this one in the chain. */
virtual LListor<Unk>* getPriorLink();
//! Set prior link
/** This sets a pointer to the link that is
now to precede this one. */
virtual void setPriorLink( LListor<Unk>* newPrior );
//! Return post link
/** This returns a pointer to the link that
follows this one in the chain. */
virtual LListor<Unk>* getPostLink();
//! Set post link
/** This sets a pointer to the link that is
now to follow this one. */
virtual void setPostLink( LListor<Unk>* newPost );
//! Get possession state
/** This returns one of four values, indicating what links
it has:
0 if it has neither a preceeding/prior link nor a following/post,
1 if it has a prior link,
2 if it has a post link,
3 if it has both a prior and a post link. */
virtual si32 hasWhat();
//! Add new link
/** Generate an entirely new link, assign it
the given object, and append it to this link.
\param item = new object being added
\param head = if this link is the new prior */
virtual LListor<Unk>* addNewLink( Unk item , bool head );
//! Extract the current link
/** Takes a link out of the chain it currently belongs to
without deleting it. The gap in the old chain is linked
by the linking of the former post and prior links of the
extracted link. */
virtual void extractCurrLink() const;
//! Delete the current link
/** Removes the current link in the chain by assigning
the post link and previous link to each other, returning
one of the two (post takes precedence over prior) if
available. The removed link is then deleted. */
virtual void delCurrLink() const;
//! Delete the next link
/** Removes the next link in the chain, if it exists,
and by calling delCurrLink() on its post link. */
void delPostLink() const
{
if ( possession_state == s_post || possession_state == s_both )
{
this->getPostLink()->delCurrLink();
}
}
//! Delete the previous link
/** Removes the previous link in the chain, if it exists,
and by calling delCurrLink() on its post link. */
void delPriorLink() const
{
if ( possession_state == s_prior || possession_state == s_both )
{
this->getPriorLink()->delCurrLink();
}
}
//! Get the first link in the chain
/** This returns the first link whose possession state
indicates it does not have a prior link. */
virtual LListor<Unk>* getFirstLink();
//! Get the last link in the chain
/** This returns the last link whose possession state
indicates it does not have a post link. */
virtual LListor<Unk>* getLastLink();
//! Compare items
/** Apply greater-than comparison operation to the
items pointed to by the links. */
void operator> ( LListor<Unk>* ll_out )
{
return ( ThisItem > ll_out->getThisItemCopy() );
}
//! Compare items
/** Apply less-than comparison operation to the
items pointed to by the links. */
void operator< ( LListor<Unk>* ll_out )
{
return ( ThisItem < ll_out->getThisItemCopy() );
}
//! Compare items
/** Apply greater-than-or-equal-to comparison operation
to the items pointed to by the links. */
void operator>= ( LListor<Unk>* ll_out )
{
return ( ThisItem >= ll_out->getThisItemCopy() );
}
//! Compare items
/** Apply less-than-or-equal-to comparison operation
to the items pointed to by the links. */
void operator<= ( LListor<Unk>* ll_out )
{
return ( ThisItem <= ll_out->getThisItemCopy() );
}
//! Compare items
/** Apply not-equal-to comparison operation to the
items pointed to by the links. */
void operator!= ( LListor<Unk>* ll_out )
{
return ( ThisItem != ll_out->getThisItemCopy() );
}
//! Compare items
/** Apply the equal-to comparison operations to the
items pointed to by the links. */
void operator== ( LListor<Unk>* ll_out )
{
return ( ThisItem == ll_out->getThisItemCopy() );
}
};
// type definitions (essentially just shortcuts)
//typedef LListor<int> LLii;
//typedef LListor<float> LLif;
}
// end the definition
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment