--- /dev/null
+
+#include <database/database.h>
+
+// Set up DB
+void marstr::database::setUpDatabase(string dbname, vector<string> fieldnames, vector<int> datatypes)
+{
+ // Set the name
+ DatabaseName = dbname;
+
+ // Acquire the field names
+ int i;
+ for (i=1; i<=fieldnames.size(); i++)
+ { FieldNames.push_back(fieldnames[i-1]); }
+
+ // Acquire the field data types
+ for (i=1; i<=datatypes.size(); i++)
+ { RowDatatypes.push_back(datatypes[i-1]); }
+}
+
+
+// Adds a pre-created row
+void marstr::database::addRow(databaserow row)
+{
+ // Check if the row has the same amount of fields that we expect there to be
+ if (row.RowFields.size() == FieldNames.size())
+ { Rows.push_back(row); }
+}
+
+
+// Deletes the specified row
+void marstr::database::deleteRow(int rowNumber)
+{
+ // Create a new vector with the specified row omitted
+ vector<databaserow> rows;
+
+ // Go through existing rows and add them to the above vector
+ // (except rowNumber)
+ int i;
+ for (i=1; i<=Rows.size(); i++)
+ {
+ if (i!=rowNumber)
+ { rows.push_back(Rows[i-1]); }
+ }
+
+ // Now clear the real vector
+ Rows.clear();
+
+ // And add the rows we got to the real rows
+ for (i=1; i<=rows.size(); i++)
+ { Rows.push_back(rows[i-1]); }
+
+ // Clear the tmp vector
+ rows.clear();
+}
+
+
+// Updates the content of a specific field in a specific row
+void marstr::database::updateFieldInRow(int rowNumber, int fieldNumber, string content)
+{
+ // Make sure the row and field exist
+ if (rowNumber <= Rows.size() && fieldNumber <= FieldNames.size())
+ {
+ // When updating a field, it is probably not a null field
+ Rows[rowNumber].RowFields[fieldNumber].NullField = false;
+
+ // It can be either one of these... so make sure they exist
+ int newInt = 0;
+ short newShort = 0;
+ float newFloat = 0.0f;
+ double newDouble = 0.0f;
+ string newString = content;
+
+ // Find the datatype for the field
+ int datatype = RowDatatypes[fieldNumber];
+
+ // Convert the field
+ if (datatype == MARSTR_DB_INT)
+ {
+ newInt = atoi(content.c_str());
+ Rows[rowNumber].RowFields[fieldNumber].fieldInt = newInt;
+ }
+ if (datatype == MARSTR_DB_SHORT)
+ {
+ newShort = atoi(content.c_str());
+ Rows[rowNumber].RowFields[fieldNumber].fieldShort = newShort;
+ }
+ if (datatype == MARSTR_DB_FLOAT)
+ {
+ newFloat = atof(content.c_str());
+ Rows[rowNumber].RowFields[fieldNumber].fieldFloat = newFloat;
+ }
+ if (datatype == MARSTR_DB_DOUBLE)
+ {
+ newDouble = atof(content.c_str());
+ Rows[rowNumber].RowFields[fieldNumber].fieldDouble = newDouble;
+ }
+ if (datatype == MARSTR_DB_STRING)
+ { Rows[rowNumber].RowFields[fieldNumber].fieldString = content; }
+ }
+}
+
+
+// Defines a field to be a null field or not
+void marstr::database::fieldIsNullField(int rowNumber, int fieldNumber, bool isNull)
+{
+ // Make sure the row and field exist
+ if (rowNumber <= Rows.size() && fieldNumber <= FieldNames.size())
+ { Rows[rowNumber].RowFields[fieldNumber].NullField = isNull; }
+}
+
+
+// Creates an empty db to be filled later
+void marstr::database::createDatabase(string filename, string dbname, vector<string> fieldnames, vector<int> datatypes)
+{
+ setUpDatabase(dbname, fieldnames, datatypes);
+}
+
+
+
+// Load database, but only the header - no content
+void marstr::database::openDatabaseLink(string filename)
+{
+ // Remember file name
+ DatabaseFileName = filename;
+
+ // File IO object
+ _linkIO = new fileio();
+
+ // Open DB
+ _linkIO->openFileForReading(filename);
+
+ // Read header and version
+ string hdrString = _linkIO->readString();
+ float hdrVersion = _linkIO->readFloat();
+
+ // Check if the header works out
+ string hdrStringCheck = "MARSTRDB";
+ if (hdrVersion == 1) // String check to be implemented
+ {
+ // Then this is a valid database
+ DatabaseFileName = filename;
+
+ // Read database name
+ DatabaseName = _linkIO->readString();
+
+ // Get number of field names
+ int amountOfFields = _linkIO->readInt();
+
+ // Read in the field names themselves
+ int i, j;
+ for (i=1; i<=amountOfFields; i++)
+ { FieldNames.push_back(_linkIO->readString()); }
+
+ // Acquire the data types of each field
+ for (i=1; i<=amountOfFields; i++)
+ { RowDatatypes.push_back(_linkIO->readInt()); }
+
+ // Number of rows in total
+ int numberOfRows = _linkIO->readInt();
+ }
+}
+
+
+databaserow marstr::database::nextDatabaseRow()
+{
+ // The row to be constructed
+ databaserow row;
+
+ for (int j=1; j<=FieldNames.size(); j++)
+ {
+ // The field to be added to the row
+ databasefield fld;
+
+ // OK find out if the field has content (null field)
+ int isNullField = _linkIO->readInt();
+
+ // If this is a null field, set the field to be so and add it
+ // to the row
+ if (isNullField == 1)
+ { fld.NullField = true; row.RowFields.push_back(fld); }
+
+ // If we have data, read according to the data type, set the
+ // value into the field, and add the field to the row
+ if (isNullField == 0)
+ {
+ // Not a null field
+ fld.NullField = false;
+
+ if (RowDatatypes[j-1] == MARSTR_DB_INT)
+ { int fldval = _linkIO->readInt(); fld.fieldInt = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_UINT)
+ { unsigned int fldval = _linkIO->readUInt(); fld.fieldUInt = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_SHORT)
+ { short fldval = _linkIO->readInt(); fld.fieldShort = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_USHORT)
+ { unsigned short fldval = _linkIO->readUShort(); fld.fieldUShort = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_LONG)
+ { long fldval = _linkIO->readLong(); fld.fieldLong = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_ULONG)
+ { unsigned long fldval = _linkIO->readULong(); fld.fieldULong = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_FLOAT)
+ { float fldval = _linkIO->readFloat(); fld.fieldFloat = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_DOUBLE)
+ { double fldval = _linkIO->readDouble(); fld.fieldDouble = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_STRING)
+ { string fldval = _linkIO->readString(); fld.fieldString = fldval; }
+
+ // Add the field
+ row.RowFields.push_back(fld);
+ }
+ }
+
+ return row;
+}
+
+
+void marstr::database::goToFirstRow()
+{
+ _linkIO->closeFileForReading();
+ openDatabaseLink(DatabaseFileName);
+}
+
+
+// Loads a database from file
+void marstr::database::loadDatabase(string filename)
+{
+ // File IO object
+ fileio *FileIO = new fileio();
+
+ // Remember file name
+ DatabaseFileName = filename;
+
+ // Open DB
+ FileIO->openFileForReading(filename);
+
+ // Read header and version
+ string hdrString = FileIO->readString();
+ float hdrVersion = FileIO->readFloat();
+
+ // Check if the header works out
+ string hdrStringCheck = "MARSTRDB";
+ if (hdrVersion == 1) // String check to be implemented
+ {
+ // Then this is a valid database
+ DatabaseFileName = filename;
+
+ // Read database name
+ DatabaseName = FileIO->readString();
+
+ // Get number of field names
+ int amountOfFields = FileIO->readInt();
+
+ // Read in the field names themselves
+ int i, j;
+ for (i=1; i<=amountOfFields; i++)
+ { FieldNames.push_back(FileIO->readString()); }
+
+ // Acquire the data types of each field
+ for (i=1; i<=amountOfFields; i++)
+ { RowDatatypes.push_back(FileIO->readInt()); }
+
+ // Number of rows in total
+ int numberOfRows = FileIO->readInt();
+
+ // Go through the remainder of the file to acquire the content
+ // of each row and their fields
+ for (i=1; i<=numberOfRows; i++)
+ {
+ // The row to be constructed
+ databaserow row;
+
+ for (j=1; j<=FieldNames.size(); j++)
+ {
+ // The field to be added to the row
+ databasefield fld;
+
+ // OK find out if the field has content (null field)
+ int isNullField = FileIO->readInt();
+
+ // If this is a null field, set the field to be so and add it
+ // to the row
+ if (isNullField == 1)
+ { fld.NullField = true; row.RowFields.push_back(fld); }
+
+ // If we have data, read according to the data type, set the
+ // value into the field, and add the field to the row
+ if (isNullField == 0)
+ {
+ // Not a null field
+ fld.NullField = false;
+
+ if (RowDatatypes[j-1] == MARSTR_DB_INT)
+ { int fldval = FileIO->readInt(); fld.fieldInt = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_UINT)
+ { unsigned int fldval = FileIO->readUInt(); fld.fieldUInt = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_SHORT)
+ { short fldval = FileIO->readInt(); fld.fieldShort = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_USHORT)
+ { unsigned short fldval = FileIO->readUShort(); fld.fieldUShort = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_LONG)
+ { long fldval = FileIO->readLong(); fld.fieldLong = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_ULONG)
+ { unsigned long fldval = FileIO->readULong(); fld.fieldULong = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_FLOAT)
+ { float fldval = FileIO->readFloat(); fld.fieldFloat = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_DOUBLE)
+ { double fldval = FileIO->readDouble(); fld.fieldDouble = fldval; }
+ if (RowDatatypes[j-1] == MARSTR_DB_STRING)
+ { string fldval = FileIO->readString(); fld.fieldString = fldval; }
+
+ // Add the field
+ row.RowFields.push_back(fld);
+ }
+ }
+
+ // Add the completed row
+ Rows.push_back(row);
+ }
+
+ // Magic number check
+ int Magic = (Rows.size() * RowDatatypes.size());
+ int MagicInFile = FileIO->readInt();
+
+ // If the magic does not stack up, clear all data
+ if (Magic != MagicInFile)
+ { Rows.clear(); }
+ }
+
+ // Close the pipe
+ FileIO->closeFileForReading();
+
+ // That's it
+}
+
+
+// Saves the database to the specified file. Use when everything is done!
+void marstr::database::saveDatabase()
+{
+ // File IO object// File IO object
+ fileio *FileIO = new fileio();
+
+ // Open the DB
+ FileIO->openFileForWriting(DatabaseFileName);
+
+ // Write a header
+ FileIO->writeString("MARSTRDB");
+ // Write a version
+ FileIO->writeFloat(1.0);
+
+ // Write database name
+ FileIO->writeString(DatabaseName);
+
+ // Write number of field names
+ FileIO->writeInt(FieldNames.size());
+
+ // The field names themselves
+ int i, j;
+ for (i=1; i<=FieldNames.size(); i++)
+ { FileIO->writeString(FieldNames[i-1]); }
+
+ // The datatypes in each field
+ for (i=1; i<=RowDatatypes.size(); i++)
+ { FileIO->writeInt(RowDatatypes[i-1]); }
+
+ // Write number of rows
+ FileIO->writeInt(Rows.size());
+
+ // Go through the rows and write the content, depending on if the field is
+ // a null field or not
+ for (i=1; i<=Rows.size(); i++)
+ {
+ for (j=1; j<=Rows[i-1].RowFields.size(); j++)
+ {
+ // Write down an int to signalize if the field has data or not
+ int isNull = 0;
+ if (Rows[i-1].RowFields[j-1].NullField == true) { isNull = 1; }
+ if (Rows[i-1].RowFields[j-1].NullField == false) { isNull = 0; }
+ FileIO->writeInt(isNull);
+
+ // Write actual data if field is not null
+ if (isNull == 0)
+ {
+ if (RowDatatypes[j-1] == MARSTR_DB_SHORT) // short
+ { FileIO->writeInt(Rows[i-1].RowFields[j-1].fieldShort); }
+ if (RowDatatypes[j-1] == MARSTR_DB_INT) // int
+ { FileIO->writeInt(Rows[i-1].RowFields[j-1].fieldInt); }
+ if (RowDatatypes[j-1] == MARSTR_DB_UINT) // uint
+ { FileIO->writeUInt(Rows[i-1].RowFields[j-1].fieldUInt); }
+ if (RowDatatypes[j-1] == MARSTR_DB_LONG) // long
+ { FileIO->writeLong(Rows[i-1].RowFields[j-1].fieldLong); }
+ if (RowDatatypes[j-1] == MARSTR_DB_ULONG) // ulong
+ { FileIO->writeULong(Rows[i-1].RowFields[j-1].fieldULong); }
+ if (RowDatatypes[j-1] == MARSTR_DB_FLOAT) // float
+ { FileIO->writeFloat(Rows[i-1].RowFields[j-1].fieldFloat); }
+ if (RowDatatypes[j-1] == MARSTR_DB_DOUBLE) // double
+ { FileIO->writeDouble(Rows[i-1].RowFields[j-1].fieldDouble); }
+ if (RowDatatypes[j-1] == MARSTR_DB_STRING) // string
+ { FileIO->writeString(Rows[i-1].RowFields[j-1].fieldString); }
+ }
+ }
+ }
+
+ // Write a magic number for verification
+ int Magic = (Rows.size() * RowDatatypes.size());
+ FileIO->writeInt(Magic);
+
+
+ // Close DB
+ FileIO->closeFileForWriting();
+
+ // That's it
+}
+
+
+void marstr::database::closeDatabase(bool save)
+{
+ if (save == true)
+ { saveDatabase(); }
+
+ Rows.clear();
+}
+
+
+vector<int> marstr::database::findRowsWithValueInColumn(string value, string colname)
+{
+ // The result with the index IDs
+ vector<int> rows;
+
+ // The column index to be searched
+ int colindex = -1;
+ for (int i=0; i<FieldNames.size(); i++)
+ {
+ if (FieldNames[i].compare(colname) == 0)
+ { colindex = i; break; }
+ }
+
+ // Proceed if we have a column
+ if (colindex != -1)
+ {
+ // Look for results depending on datatype
+ for (int i=0; i<Rows.size(); i++)
+ {
+ bool push=false;
+
+ // The field to check
+ databasefield fld = Rows[i].RowFields[colindex];
+
+ if (RowDatatypes[colindex] == MARSTR_DB_INT)
+ {
+ int needle = stoi(value);
+ if (fld.NullField == false && fld.fieldInt == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_UINT)
+ {
+ unsigned int needle = stoi(value);
+ if (fld.NullField == false && fld.fieldUInt == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_SHORT)
+ {
+ short needle = stoi(value);
+ if (fld.NullField == false && fld.fieldShort == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_USHORT)
+ {
+ unsigned short needle = stoi(value);
+ if (fld.NullField == false && fld.fieldUShort == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_LONG)
+ {
+ long needle = stol(value);
+ if (fld.NullField == false && fld.fieldLong == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_ULONG)
+ {
+ long needle = stol(value);
+ if (fld.NullField == false && fld.fieldULong == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_FLOAT)
+ {
+ float needle = stof(value);
+ if (fld.NullField == false && fld.fieldFloat == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_DOUBLE)
+ {
+ double needle = stod(value);
+ if (fld.NullField == false && fld.fieldDouble == needle)
+ { push = true; }
+ }
+ if (RowDatatypes[colindex] == MARSTR_DB_STRING)
+ {
+ if (fld.NullField == false && fld.fieldString.compare(value) == 0)
+ { push = true; }
+ }
+
+ if (push == true) { rows.push_back(i); }
+ }
+ }
+
+ // Return list
+ return rows;
+}
+
+
+
+int marstr::database::numberOfRows() { return Rows.size(); }
--- /dev/null
+
+// This class represents a custom database format used in the Reason Engine. It
+// is small and quite simple to use. This format is used for a number of things,
+// mostly for models.
+
+#ifndef MSTR_DATABASE
+#define MSTR_DATABASE
+
+#include <cstdlib>
+#include <string>
+#include <vector>
+#include <fileio/fileio.h>
+#include <database/databaserow.h>
+#include <database/databasedefines.h>
+
+using namespace std;
+using namespace marstr;
+
+namespace marstr
+{
+ class database
+ {
+ public:
+ // Name of the database
+ string DatabaseName;
+ // Name of all fields in this database
+ vector<string> FieldNames;
+ // The database rows
+ vector<databaserow> Rows;
+ // Definitions of the single datatypes in each field
+ vector<int> RowDatatypes;
+
+ // The filename from which this database came from, if opened from file
+ string DatabaseFileName;
+
+ // Sets up this database with a name, the field names, and the data
+ // types each row should hold
+ void setUpDatabase(string dbname, vector<string> fieldnames, vector<int> datatypes);
+
+ // Adds a row to this database
+ void addRow(databaserow row);
+
+ // Deletes the row of the specified index
+ void deleteRow(int rowNumber);
+
+ // Updates the content of a specific field, in the specified row. For
+ // simplycity, the content is a string which we process to the correct
+ // field type for you.
+ void updateFieldInRow(int rowNumber, int fieldNumber, string content);
+
+ // Defines if a field has data or not
+ void fieldIsNullField(int rowNumber, int fieldNumber, bool isNull);
+
+ // Loads a database entirely from a file into RAM
+ void loadDatabase(string filename);
+
+ // Loads a database, but only reads the header info - you will need to scan
+ // rows of each DB separately. This is useful for larger databases and low amounts
+ // of RAM.
+ void openDatabaseLink(string filename);
+
+ // Returns the next row from the database link for evaluation
+ databaserow nextDatabaseRow();
+
+ // Goes back to the beginning of the database in the opened link
+ void goToFirstRow();
+
+ // Creates an empty database
+ void createDatabase(string filename, string dbname, vector<string> fieldnames, vector<int> datatypes);
+ // Saves the database to a file
+ void saveDatabase();
+
+ // Clears the database in RAM, basically frees up used space and lets
+ // you load another database with the same object
+ void closeDatabase(bool save);
+
+ // Acquires the amount of rows in DB
+ int numberOfRows();
+
+ // Find something in all rows, in the specified column. Looks for the
+ // EXACT value match. The value can also be an int, float or double, but
+ // must be encoded as a string, like "30.99", "64", or "19.645773373"
+ vector<int> findRowsWithValueInColumn(string value, string colname);
+
+ private:
+ // The link that remains open to the database file should it be requested
+ // to only load the header
+ fileio *_linkIO;
+ };
+}
+
+#endif