# HG changeset patch # User Santeri Piippo # Date 1363371078 -7200 # Node ID c51cce84a9acaa1f405d32dd445693aba349e6f6 Initial commit diff -r 000000000000 -r c51cce84a9ac bbox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bbox.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,66 @@ +#include "common.h" +#include "bbox.h" +#include "ldtypes.h" + +void bbox::calculate () { + // The bounding box, bbox for short, is the + // box that encompasses the model we have open. + // v0 is the minimum vertex, v1 is the maximum vertex. + for (uint i = 0; i < g_CurrentFile->objects.size(); i++) { + LDObject* obj = g_CurrentFile->objects[i]; + switch (obj->getType ()) { + + case OBJ_Line: + { + LDLine* line = static_cast (obj); + for (short i = 0; i < 2; ++i) + checkVertex (line->vaCoords[i]); + } + break; + + case OBJ_Triangle: + { + LDTriangle* tri = static_cast (obj); + for (short i = 0; i < 3; ++i) + checkVertex (tri->vaCoords[i]); + } + break; + + case OBJ_Quad: + { + LDQuad* quad = static_cast (obj); + for (short i = 0; i < 4; ++i) + checkVertex (quad->vaCoords[i]); + } + break; + + case OBJ_CondLine: + { + LDCondLine* line = static_cast (obj); + for (short i = 0; i < 2; ++i) { + checkVertex (line->vaCoords[i]); + checkVertex (line->vaControl[i]); + } + } + break; + + default: + break; + } + } +} + +#define CHECK_DIMENSION(V,X) \ + if (V.X < v0.X) v0.X = V.X; \ + if (V.X > v1.X) v1.X = V.X; +void bbox::checkVertex (vertex v) { + CHECK_DIMENSION (v, x) + CHECK_DIMENSION (v, y) + CHECK_DIMENSION (v, z) +} +#undef CHECK_DIMENSION + +bbox::bbox () { + memset (&v0, 0, sizeof v0); + memset (&v1, 0, sizeof v1); +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac bbox.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bbox.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,18 @@ +#ifndef __BBOX_H__ +#define __BBOX_H__ + +#include "common.h" + +class bbox { +public: + vertex v0; + vertex v1; + + bbox (); + void calculate (); + +private: + void checkVertex (vertex v); +}; + +#endif \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac cfgdef.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cfgdef.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,6 @@ +// SECT (prefix, Display name) +// CFG (type, prefix, name, description, default) + +// --------------------------------------------------------- +SECT (io, Files) +CFG (str, io, openfile, "Current open fle", "box5.dat") \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac color.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/color.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,75 @@ +#ifndef __COLOR_H__ +#define __COLOR_H__ + +#include + +#ifdef QT_VERSION + #include +#endif // QT_VERSION + +class color { +public: + unsigned char r, g, b; + + color () {} + color (const char* other) { + parseFromString (str (other)); + } + + bool parseFromString (str in) { +#ifdef QT_VERSION + // Use Qt's method for a quick color check. Handles + // named colors, too. + QColor col (in.chars ()); + if (col.isValid()) { + r = col.red (); + g = col.green (); + b = col.blue (); + return true; + } +#else + if (in[0] == '#') { + // Hex code + + if (~in != 4 && ~in != 7) + return false; // bad length + + printf ("%s\n", in.chars ()); + + if (~in == 4) { + in.format ("#%c%c%c%c%c%c", + in[1], in[1], + in[2], in[2], + in[3], in[3]); + } + + printf ("%s\n", in.chars ()); + + if (sscanf (in.chars(), "#%2hhx%2hhx%2hhx", &r, &g, &b)) + return true; + } +#endif // QT_VERSION + + return false; + } + + str toString () { + str val; + val.format ("#%.2X%.2X%.2X", r, g, b); + return val; + } + + char* chars() { + return toString ().chars (); + } + + operator str () { + return toString (); + } + + void operator= (const str other) { + parseFromString (other); + } +}; + +#endif // __COLOR_H__ \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,121 @@ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#define APPNAME "ldforge" +#define APPNAME_DISPLAY "LDForge" +#define APPNAME_CAPS "LDFORGE" + +#define VERSION_MAJOR 0 +#define VERSION_MAJOR_STR "0" +#define VERSION_MINOR 1 +#define VERSION_MINOR_STR "1" + +#define VERSION_STRING VERSION_MAJOR_STR "." VERSION_MINOR_STR + +#define CONFIG_WITH_QT + +#include +#include +#include +#include +#include +#include "stdarg.h" +#include "str.h" +#include "config.h" + +using std::vector; + +class LDForgeWindow; +class LDObject; +class bbox; + +// ============================================================================= +// vertex (v) +// +// Vertex class. Not to be confused with LDVertex, which is a vertex used in an +// LDraw code file. +// +// Methods: +// - midpoint (vertex&): returns a midpoint +// ============================================================================= +class vertex { +public: + double x, y, z; + + // ========================================================================= + // Midpoint between this vertex and another vertex. + vertex midpoint (vertex& other); + str getStringRep (); +}; + +// ============================================================================= +// bearing +// +// A bearing is a combination of an angle and a pitch. Essentially a 3D angle. +// The project method projects a vertex from a given vertex by a given length. +// +// Prefix: g, since b is bool +// ============================================================================= +class bearing { + double fAngle, fPitch; + + vertex project (vertex& vSource, ulong ulLength); +}; + +// ============================================================================= +typedef struct { + str filename; + vector objects; +} OpenFile; + +// Plural expression +#define PLURAL(n) ((n != 1) ? "s" : "") + +// Shortcut for formatting +#define PERFORM_FORMAT(in, out) \ + va_list v; \ + va_start (v, in); \ + char* out = vdynformat (in, v, 256); \ + va_end (v); + +// Shortcuts for stuffing vertices into printf-formatting. +#define FMT_VERTEX "(%.3f, %.3f, %.3f)" +#define FVERTEX(V) V.x, V.y, V.z + +typedef unsigned char byte; + +template inline T clamp (T a, T min, T max) { + return (a > max) ? max : (a < min) ? min : a; +} + +template inline T min (T a, T b) { + return (a < b) ? a : b; +} + +template inline T max (T a, T b) { + return (a > b) ? a : b; +} + +static const double pi = 3.14159265358979323846f; + +// main.cpp +extern OpenFile* g_CurrentFile; +extern bbox g_BBox; +extern LDForgeWindow* g_qWindow; +extern vector g_LoadedFiles; + +#ifndef unix +typedef unsigned int uint; +typedef unsigned long ulong; +#endif // unix + +typedef int8_t xchar; +typedef int16_t xshort; +typedef int32_t xlong; +typedef int64_t xlonglong; +typedef uint8_t xuchar; +typedef uint16_t xushort; +typedef uint32_t xulong; +typedef uint64_t xulonglong; + +#endif \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac config.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,351 @@ +#include +// #include +#include +#include +#include "str.h" +#include "config.h" + +#ifdef CONFIG_WITH_QT +#include +#endif // CONFIG_WITH_QT + +// ============================================================================= +// Define the configs +#define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) \ + TYPE##config SECT##_##NAME (CFGSECTNAME (SECT), DESCR, \ + DEFAULT, #NAME, #SECT "_" #NAME, #TYPE, #DEFAULT); + +#define SECT(...) + #include "cfgdef.h" +#undef CFG +#undef SECT + +// ============================================================================= +config* config::pointers[] = { +#define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) &SECT##_##NAME, +#define SECT(...) + #include "cfgdef.h" +#undef CFG +#undef SECT +}; + +// ============================================================================= +const char* config::sections[] = { +#define CFG(...) +#define SECT(A,B) #A, + #include "cfgdef.h" +#undef CFG +#undef SECT +}; + +// ============================================================================= +const char* config::sectionNames[] = { +#define CFG(...) +#define SECT(A,B) #B, + #include "cfgdef.h" +#undef CFG +#undef SECT +}; + +// ============================================================================= +const char* g_WeekdayNames[7] = { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", +}; + +// ============================================================================= +static const char* g_MonthNames[12] = { + "Januray", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November" + "December", +}; + +static const char* g_ConfigTypeNames[] = { + "None", + "Integer", + "String", + "Float", + "Boolean", + "Color", +}; + +// ============================================================================= +// Load the configuration from file +bool config::load () { + FILE* fp = fopen (filepath().chars(), "r"); + char linedata[MAX_INI_LINE]; + char* line; + size_t ln = 0; + configsection_e section = NO_CONFIG_SECTION; + + if (!fp) + return false; // can't open for reading + + // Read the values. + while (fgets (linedata, MAX_INI_LINE, fp)) { + ln++; + line = linedata; + + while (*line != 0 && (*line <= 32 || *line >= 127)) + line++; // Skip junk + + if (*line == '\0' || line[0] == '#') + continue; // Empty line or comment. + + if (line[0] == '[') { + // Section + char* endbracket = strchr (line, ']'); + + if (!endbracket) { + fprintf (stderr, "badly formed section: %s", line); + continue; + } + + str sectionName = str (line).substr (1, endbracket - line); + const configsection_e oldsection = section; + section = NO_CONFIG_SECTION; + + // Find the section + for (unsigned i = 0; i < NUM_ConfigSections && section == NO_CONFIG_SECTION; i++) + if (sectionName.compare (sectionNames[i]) == 0) + section = (configsection_e)i; + + if (section == NO_CONFIG_SECTION) { + fprintf (stderr, "unknown config section `%s`\n", sectionName.chars()); + section = oldsection; + } + + continue; + } + + // Find the equals sign. + char* equals = strchr (line, '='); + if (!equals) { + fprintf (stderr, "couldn't find `=` sign in entry `%s`\n", line); + continue; + } + + str entry = str (line).substr (0, equals - line); + + str configname; + configname.format ("%s_%s", sections[section], entry.chars ()); + + // Find the config entry for this. + config* cfg = NULL; + for (size_t i = 0; i < NUM_CONFIG && !cfg; i++) + if (configname.compare (pointers[i]->fullname) == 0) + cfg = pointers[i]; + + if (!cfg) { + fprintf (stderr, "unknown config `%s`\n", configname.chars()); + continue; + } + + str valstring = str (line).substr (equals - line + 1, -1); + + // Trim the crap off the end + while (~valstring) { + char c = valstring[~valstring - 1]; + if (c <= 32 || c >= 127) + valstring -= 1; + else + break; + } + + switch (const_cast (cfg)->getType()) { + case CONFIG_int: + static_cast (cfg)->value = atoi (valstring.chars()); + break; + case CONFIG_str: + static_cast (cfg)->value = valstring; + break; + case CONFIG_float: + static_cast (cfg)->value = atof (valstring.chars()); + break; + case CONFIG_bool: + { + bool& val = static_cast (cfg)->value; + + if (+valstring == "TRUE" || valstring == "1") + val = true; + else if (+valstring == "FALSE" || valstring == "0") + val = false; + break; + } + case CONFIG_color: + static_cast (cfg)->value.parseFromString (valstring); + break; + default: + break; + } + } + + fclose (fp); + return true; +} + +// ============================================================================= +// Write a given formatted string to the given file stream +static size_t writef (FILE* fp, const char* fmt, ...) { + va_list va; + + va_start (va, fmt); + char* buf = vdynformat (fmt, va, 256); + va_end (va); + + size_t len = fwrite (buf, 1, strlen (buf), fp); + delete[] buf; + + return len; +} + +// ============================================================================= +// Save the configuration to disk +bool config::save () { +#ifdef APPNAME + #ifdef CONFIG_WITH_QT + // If the directory doesn't exist, create it now. + if (!QDir (dirpath().chars()).exists ()) { + fprintf (stderr, "Creating config path %s...\n", dirpath().chars()); + if (!QDir ().mkpath (dirpath().chars())) { + fprintf (stderr, "Failed to create the directory. Configuration cannot be saved!\n"); + return false; // Couldn't create directory + } + } + #else + #warning Need QT to check for directories. Config will not be able + #warning to save properly if ~/.APPNAME/ does not exist. + #endif // CONFIG_WITH_QT +#endif // CONFIG_DIRECTORY + + FILE* fp = fopen (filepath().chars(), "w"); + printf ("writing cfg to %s\n", filepath().chars()); + + if (!fp) { + printf ("Couldn't open %s for writing\n", filepath().chars()); + return false; + } + + const time_t curtime = time (NULL); + const struct tm* timeinfo = localtime (&curtime); + const char* daysuffix = + (timeinfo->tm_mday % 10 == 1) ? "st" : + (timeinfo->tm_mday % 10 == 2) ? "nd" : + (timeinfo->tm_mday % 10 == 3) ? "rd" : "th"; + + writef (fp, "# Configuration file for " APPNAME "\n"); + writef (fp, "# Written on %s, %s %d%s %d %.2d:%.2d:%.2d\n", + g_WeekdayNames[timeinfo->tm_wday], g_MonthNames[timeinfo->tm_mon], + timeinfo->tm_mday, daysuffix, timeinfo->tm_year + 1900, + timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); + writef (fp, "\n"); + + for (int i = 0; i < NUM_ConfigSections; i++) { + if (i > 0) + writef (fp, "\n"); + + writef (fp, "[%s]\n", sectionNames[i]); + bool first = true; + + for (size_t j = 0; j < NUM_CONFIG; j++) { + config* cfg = pointers[j]; + + if (cfg->sect != i) + continue; + + if (!first) + writef (fp, "\n"); + + str valstring; + switch (cfg->getType()) { + case CONFIG_int: + valstring.format ("%d", static_cast (cfg)->value); + break; + case CONFIG_str: + valstring = static_cast (cfg)->value; + break; + case CONFIG_float: + valstring.format ("%f", static_cast (cfg)->value); + + // Trim any trailing zeros + if (valstring.first (".") != -1) { + while (valstring[~valstring - 1] == '0') + valstring -= 1; + + // But don't trim the only one out... + if (valstring[~valstring - 1] == '.') + valstring += '0'; + } + + break; + case CONFIG_bool: + valstring = (static_cast (cfg)->value) ? "true" : "false"; + break; + case CONFIG_color: + valstring = (str)(static_cast (cfg)->value); + break; + default: + break; + } + + // Write the entry now. + writef (fp, "# %s, default: %s\n", g_ConfigTypeNames[cfg->getType()], cfg->defaultstring); + writef (fp, "# %s: %s\n", cfg->fullname, cfg->description); + writef (fp, "%s=%s\n", cfg->name, valstring.chars()); + first = false; + } + } + + fclose (fp); + return true; +} + +// ============================================================================= +void config::reset () { + for (size_t i = 0; i < NUM_CONFIG; i++) + pointers[i]->resetValue (); +} + +// ============================================================================= +str config::filepath () { +#ifdef APPNAME + str path; + path.format ("%s" APPNAME ".ini", dirpath().chars()); + return path; +#else // APPNAME + return "config.ini"; +#endif // APPNAME +} + +// ============================================================================= +str config::dirpath () { +#ifdef APPNAME + str path; + + #ifdef CONFIG_WITH_QT + path = (QDir::homePath ().toStdString().c_str()); + #else // CONFIG_WITH_QT + path = "~"; + #endif // CONFIG_WITH_QT + + path += "/." APPNAME "/"; +#else + path = "./"; +#endif // APPNAME + + return path; +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,238 @@ +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + +#include "common.h" +#include "str.h" +#include "color.h" + +// ============================================================================= +// Determine configuration file. Use APPNAME if given. +#ifdef APPNAME + #define CONFIGFILE APPNAME ".ini" +#else // APPNAME + #define APPNAME "(unnamed application)" + #define CONFIGFILE "config.ini" +#endif // APPNAME + +#ifdef CONFIG_WITH_QT + #include +#endif // CONFIG_WITH_QT + +// ------------------------------- +#define CFGSECTNAME(X) CFGSECT_##X + +#define MAX_INI_LINE 512 +#define NUM_CONFIG (sizeof config::pointers / sizeof *config::pointers) + +// ============================================================================= +enum configsection_e { +#define CFG(...) +#define SECT(A,B) CFGSECTNAME (A), + #include "cfgdef.h" +#undef CFG +#undef SECT + NUM_ConfigSections, + NO_CONFIG_SECTION = -1 +}; + +// ============================================================================= +enum configtype_e { + CONFIG_none, + CONFIG_int, + CONFIG_str, + CONFIG_float, + CONFIG_bool, + CONFIG_color, +}; + +// ========================================================= +class config { +public: + configsection_e sect; + const char* description, *name, *fullname, *typestring, *defaultstring; + + virtual configtype_e getType () { + return CONFIG_none; + } + + virtual void resetValue () {} + + // ------------------------------------------ + static bool load (); + static bool save (); + static void reset (); + static config* pointers[]; + static const char* sections[]; + static const char* sectionNames[]; + static str dirpath (); + static str filepath (); +}; + +// ============================================================================= +#define DEFINE_UNARY_OPERATOR(T, OP) \ + T operator OP () { \ + return (OP value); \ + } \ + +#define DEFINE_BINARY_OPERATOR(T, OP) \ + T operator OP (const T other) { \ + return (value OP other); \ + } \ + +#define DEFINE_ASSIGN_OPERATOR(T, OP) \ + T& operator OP (const T other) { \ + return (value OP other); \ + } \ + +#define DEFINE_COMPARE_OPERATOR(T, OP) \ + bool operator OP (const T other) { \ + return (value OP other); \ + } \ + +#define DEFINE_CAST_OPERATOR(T) \ + operator T () { \ + return (T) value; \ + } \ + +#define DEFINE_ALL_COMPARE_OPERATORS(T) \ + DEFINE_COMPARE_OPERATOR (T, ==) \ + DEFINE_COMPARE_OPERATOR (T, !=) \ + DEFINE_COMPARE_OPERATOR (T, >) \ + DEFINE_COMPARE_OPERATOR (T, <) \ + DEFINE_COMPARE_OPERATOR (T, >=) \ + DEFINE_COMPARE_OPERATOR (T, <=) \ + +#define DEFINE_INCREMENT_OPERATORS(T) \ + T operator++ () {return ++value;} \ + T operator++ (int) {return value++;} \ + T operator-- () {return --value;} \ + T operator-- (int) {return value--;} + +#define CONFIGTYPE(T) \ +class T##config : public config + +#define IMPLEMENT_CONFIG(T) \ + T value, defval; \ + \ + T##config (const configsection_e _sect, const char* _description, \ + T _defval, const char* _name, const char* _fullname, const char* _typestring, \ + const char* _defaultstring) \ + { \ + sect = _sect; \ + description = _description; \ + value = defval = _defval; \ + name = _name; \ + fullname = _fullname; \ + typestring = _typestring; \ + defaultstring = _defaultstring; \ + } \ + operator T () { \ + return value; \ + } \ + configtype_e getType () { \ + return CONFIG_##T; \ + } \ + void resetValue () { \ + value = defval; \ + } + +// ============================================================================= +CONFIGTYPE (int) { +public: + IMPLEMENT_CONFIG (int) + + // Int-specific operators + DEFINE_ALL_COMPARE_OPERATORS (int) + DEFINE_INCREMENT_OPERATORS (int) + DEFINE_BINARY_OPERATOR (int, +) + DEFINE_BINARY_OPERATOR (int, -) + DEFINE_BINARY_OPERATOR (int, *) + DEFINE_BINARY_OPERATOR (int, /) + DEFINE_BINARY_OPERATOR (int, %) + DEFINE_BINARY_OPERATOR (int, ^) + DEFINE_BINARY_OPERATOR (int, |) + DEFINE_BINARY_OPERATOR (int, &) + DEFINE_BINARY_OPERATOR (int, >>) + DEFINE_BINARY_OPERATOR (int, <<) + DEFINE_UNARY_OPERATOR (int, !) + DEFINE_UNARY_OPERATOR (int, ~) + DEFINE_UNARY_OPERATOR (int, -) + DEFINE_UNARY_OPERATOR (int, +) + DEFINE_ASSIGN_OPERATOR (int, =) + DEFINE_ASSIGN_OPERATOR (int, +=) + DEFINE_ASSIGN_OPERATOR (int, -=) + DEFINE_ASSIGN_OPERATOR (int, *=) + DEFINE_ASSIGN_OPERATOR (int, /=) + DEFINE_ASSIGN_OPERATOR (int, %=) + DEFINE_ASSIGN_OPERATOR (int, >>=) + DEFINE_ASSIGN_OPERATOR (int, <<=) +}; + +// ============================================================================= +CONFIGTYPE (str) { +public: + IMPLEMENT_CONFIG (str) + + DEFINE_ALL_COMPARE_OPERATORS (str) + DEFINE_BINARY_OPERATOR (str, -) + DEFINE_BINARY_OPERATOR (str, *) + DEFINE_UNARY_OPERATOR (str, !) + DEFINE_ASSIGN_OPERATOR (str, =) + DEFINE_ASSIGN_OPERATOR (str, +=) + DEFINE_ASSIGN_OPERATOR (str, -=) + DEFINE_ASSIGN_OPERATOR (str, *=) + DEFINE_CAST_OPERATOR (char*) + + char operator[] (size_t n) { + return value[n]; + } + +#ifdef CONFIG_WITH_QT + operator QString () { + return QString (value.chars()); + } +#endif // CONFIG_WITH_QT +}; + +// ============================================================================= +CONFIGTYPE (float) { +public: + IMPLEMENT_CONFIG (float) + + DEFINE_ALL_COMPARE_OPERATORS (float) + DEFINE_INCREMENT_OPERATORS (float) + DEFINE_BINARY_OPERATOR (float, +) + DEFINE_BINARY_OPERATOR (float, -) + DEFINE_BINARY_OPERATOR (float, *) + DEFINE_UNARY_OPERATOR (float, !) + DEFINE_ASSIGN_OPERATOR (float, =) + DEFINE_ASSIGN_OPERATOR (float, +=) + DEFINE_ASSIGN_OPERATOR (float, -=) + DEFINE_ASSIGN_OPERATOR (float, *=) +}; + +// ============================================================================= +CONFIGTYPE (bool) { +public: + IMPLEMENT_CONFIG (bool) + DEFINE_ALL_COMPARE_OPERATORS (bool) + DEFINE_ASSIGN_OPERATOR (bool, =) +}; + +// ============================================================================= +CONFIGTYPE (color) { +public: + IMPLEMENT_CONFIG (color) + // DEFINE_COMPARE_OPERATOR (color, ==) + // DEFINE_COMPARE_OPERATOR (color, !=) +}; + +// ============================================================================= +// Extern the configurations now +#define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) extern TYPE##config SECT##_##NAME; +#define SECT(...) + #include "cfgdef.h" +#undef CFG +#undef SECT + +#endif // __OPTIONS_H__ \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac draw.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/draw.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,159 @@ +#include +#include +#include "common.h" +#include "io.h" +#include "draw.h" +#include "bbox.h" + +renderer::renderer (QWidget* parent) { + parent = parent; // shhh, GCC + fRotX = fRotY = fRotZ = 0.0; + fZoom = 1.0; +} + +void renderer::initializeGL () { + glLoadIdentity(); + glMatrixMode (GL_MODELVIEW); + glClearColor (0.8f, 0.8f, 0.85f, 1.0f); + glEnable (GL_DEPTH_TEST); + glShadeModel (GL_SMOOTH); + glEnable (GL_MULTISAMPLE); + + CompileObjects (); +} + +void renderer::hardRefresh () { + CompileObjects (); + paintGL (); +} + +void renderer::resizeGL (int w, int h) { + glViewport (0, 0, w, h); +} + +void renderer::paintGL () { + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glPushMatrix (); + glTranslatef ( + (g_BBox.v0.x + g_BBox.v1.x) / -2.0, + (g_BBox.v0.y + g_BBox.v1.y) / -2.0, + (g_BBox.v0.z + g_BBox.v1.z) / -2.0 + ); + + // glTranslatef (0.0f, 0.0f, -5.0f); + glTranslatef (0.0f, 0.0f, fZoom); + + // glScalef (0.75f, 1.15f, 0.0f); + glRotatef (fRotX, 1.0f, 0.0f, 0.0f); + glRotatef (fRotY, 0.0f, 1.0f, 0.0f); + glRotatef (fRotZ, 0.0f, 0.0f, 1.0f); + + glMatrixMode (GL_MODELVIEW); + + glCallList (objlist); + glColor3f (0.0, 0.5, 1.0); + glPopMatrix (); +} + +void renderer::CompileObjects () { + printf ("compile all objects\n"); + + objlist = glGenLists (1); + glNewList (objlist, GL_COMPILE); + + if (!g_CurrentFile) { + printf ("renderer: no files loaded, cannot compile anything\n"); + return; + } + + for (ulong i = 0; i < g_CurrentFile->objects.size(); i++) + CompileOneObject (g_CurrentFile->objects[i]); + + glEndList (); +} + +#define GL_VERTEX(V) \ + glVertex3d (V.x, V.y, V.z); + +void renderer::CompileOneObject (LDObject* obj) { + if (!obj) + return; + + switch (obj->getType ()) { + case OBJ_CondLine: // For now, treat condlines like normal lines. + case OBJ_Line: + { + glColor3f (0.0f, 0.0f, 0.0f); // Draw all lines black for now + // draw lines + LDLine* line = static_cast (obj); + glBegin (GL_LINES); + for (short i = 0; i < 2; ++i) + GL_VERTEX (line->vaCoords[i]) + glEnd (); + } + break; + + case OBJ_Triangle: + { + LDTriangle* tri = static_cast (obj); + glColor3f (0.5f, 0.5f, 0.5f); // Draw all polygons gray for now + glBegin (GL_TRIANGLES); + for (short i = 0; i < 3; ++i) + GL_VERTEX (tri->vaCoords[i]) + glEnd (); + } + break; + + case OBJ_Quad: + { + LDQuad* quad = static_cast (obj); + glColor3f (0.5f, 0.5f, 0.5f); + glBegin (GL_QUADS); + for (short i = 0; i < 4; ++i) + GL_VERTEX (quad->vaCoords[i]) + glEnd (); + } + break; + + default: + break; + } +} + +void renderer::ClampAngle (double& fAngle) { + while (fAngle < 0) + fAngle += 360.0; + while (fAngle > 360.0) + fAngle -= 360.0; +} + +void renderer::mouseMoveEvent (QMouseEvent *event) { + int dx = event->x () - lastPos.x (); + int dy = event->y () - lastPos.y (); + + if (event->buttons () & Qt::LeftButton) { + fRotX = fRotX + (dy); + fRotY = fRotY + (dx); + ClampAngle (fRotX); + ClampAngle (fRotY); + } + + if (event->buttons () & Qt::RightButton) { + fRotX = fRotX + (dy); + fRotZ = fRotZ + (dx); + ClampAngle (fRotX); + ClampAngle (fRotZ); + } + + if (event->buttons () & Qt::MidButton) { + fZoom += (dy / 100.0); + fZoom = clamp (fZoom, 0.1, 10.0); + } + + printf ("%.3f %.3f %.3f %.3f\n", + fRotX, fRotY, fRotZ, fZoom); + lastPos = event->pos(); + updateGL (); +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac draw.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/draw.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,32 @@ +#ifndef __REND_H__ +#define __REND_H__ + +#include +#include "common.h" +#include "ldtypes.h" + +class renderer : public QGLWidget { + Q_OBJECT + +public: + renderer(QWidget* parent = NULL); + void hardRefresh (); + void CompileObjects (); + double fRotX, fRotY, fRotZ; + QPoint lastPos; + double fZoom; + +protected: + void initializeGL (); + void resizeGL (int w, int h); + void paintGL (); + + void mouseMoveEvent (QMouseEvent *event); + +private: + GLuint objlist; + void CompileOneObject (LDObject* obj); + void ClampAngle (double& fAngle); +}; + +#endif \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac gui.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,353 @@ +#include + +#include "common.h" +#include "draw.h" +#include "gui.h" +#include "model.h" +#include "io.h" + +LDForgeWindow::LDForgeWindow () { + R = new renderer; + + qObjList = new QTreeWidget; + qObjList->setHeaderHidden (true); + qObjList->setMaximumWidth (256); + + qMessageLog = new QTextEdit; + qMessageLog->setReadOnly (true); + qMessageLog->setMaximumHeight (96); + + QWidget* w = new QWidget; + QGridLayout* layout = new QGridLayout; + layout->setColumnMinimumWidth (0, 192); + layout->setColumnStretch (0, 1); + layout->addWidget (R, 0, 0); + layout->addWidget (qObjList, 0, 1); + layout->addWidget (qMessageLog, 1, 0, 1, 2); + w->setLayout (layout); + setCentralWidget (w); + + createMenuActions (); + createMenus (); + createToolbars (); + + setWindowTitle (APPNAME_DISPLAY " v" VERSION_STRING); + setMinimumSize (320, 200); + resize (800, 600); +} + +#define MAKE_ACTION(OBJECT, DISPLAYNAME, IMAGENAME, DESCR) \ + qAct_##OBJECT = new QAction (QIcon ("./icons/" IMAGENAME ".png"), tr (DISPLAYNAME), this); \ + qAct_##OBJECT->setStatusTip (tr (DESCR)); \ + connect (qAct_##OBJECT, SIGNAL (triggered ()), this, SLOT (slot_##OBJECT ())); + +void LDForgeWindow::createMenuActions () { + // Long menu names go here so my cool action definition table doesn't get out of proportions + const char* sNewCdLineText = "New Conditional Line", + *sNewQuadText = "New Quadrilateral", + *sAboutText = "About " APPNAME_DISPLAY; + + MAKE_ACTION (new, "&New", "file-new", "Create a new part model.") + MAKE_ACTION (open, "&Open", "open", "Load a part model from a file.") + MAKE_ACTION (save, "&Save", "save", "Save the part model.") + MAKE_ACTION (saveAs, "Save &As", "save-as", "Save the part to a specific file.") + MAKE_ACTION (exit, "&Exit", "exit", "Close " APPNAME_DISPLAY ".") + + MAKE_ACTION (cut, "Cut", "cut", "Cut the current selection to clipboard.") + MAKE_ACTION (copy, "Copy", "copy", "Copy the current selection to clipboard.") + MAKE_ACTION (paste, "Paste", "paste", "Paste clipboard contents.") + MAKE_ACTION (about, sAboutText, "about", "Shows information about " APPNAME_DISPLAY ".") + MAKE_ACTION (aboutQt, "About Qt", "aboutQt", "Shows information about Qt.") + + MAKE_ACTION (newSubfile, "New Subfile", "add-subfile", "Creates a new subfile reference.") + MAKE_ACTION (newLine, "New Line", "add-line", "Creates a new line.") + MAKE_ACTION (newTriangle, "New Triangle", "add-triangle", "Creates a new triangle.") + MAKE_ACTION (newQuad, sNewQuadText, "add-quad", "Creates a new quadrilateral.") + MAKE_ACTION (newCondLine, sNewCdLineText, "add-condline", "Creates a new conditional line."); + MAKE_ACTION (newComment, "New Comment", "add-comment", "Creates a new comment."); + MAKE_ACTION (newVector, "New Vector", "add-vector", "Creates a new vector.") + MAKE_ACTION (newVertex, "New Vertex", "add-vertex", "Creates a new vertex.") + + // Keyboard shortcuts + qAct_new->setShortcut (Qt::CTRL | Qt::Key_N); + qAct_open->setShortcut (Qt::CTRL | Qt::Key_O); + qAct_save->setShortcut (Qt::CTRL | Qt::Key_S); + qAct_saveAs->setShortcut (Qt::CTRL | Qt::SHIFT | Qt::Key_S); + + qAct_cut->setShortcut (Qt::CTRL | Qt::Key_X); + qAct_copy->setShortcut (Qt::CTRL | Qt::Key_C); + qAct_paste->setShortcut (Qt::CTRL | Qt::Key_V); + + // things not implemented yet + QAction* qaDisabledActions[] = { + qAct_save, + qAct_saveAs, + qAct_newSubfile, + qAct_newLine, + qAct_newTriangle, + qAct_newQuad, + qAct_newCondLine, + qAct_newComment, + qAct_newVector, + qAct_newVertex, + qAct_cut, + qAct_copy, + qAct_paste, + qAct_about + }; + + for (ushort i = 0; i < sizeof qaDisabledActions / sizeof *qaDisabledActions; ++i) + qaDisabledActions[i]->setEnabled (false); +} + +void LDForgeWindow::createMenus () { + // File menu + qFileMenu = menuBar ()->addMenu (tr ("&File")); + qFileMenu->addAction (qAct_new); // New + qFileMenu->addAction (qAct_open); // Open + qFileMenu->addAction (qAct_save); // Save + qFileMenu->addAction (qAct_saveAs); // Save As + qFileMenu->addSeparator (); // ------- + qFileMenu->addAction (qAct_exit); // Exit + + // Edit menu + qInsertMenu = menuBar ()->addMenu (tr ("&Insert")); + qInsertMenu->addAction (qAct_newSubfile); // New Subfile + qInsertMenu->addAction (qAct_newLine); // New Line + qInsertMenu->addAction (qAct_newTriangle); // New Triangle + qInsertMenu->addAction (qAct_newQuad); // New Quad + qInsertMenu->addAction (qAct_newCondLine); // New Conditional Line + qInsertMenu->addAction (qAct_newComment); // New Comment + qInsertMenu->addAction (qAct_newVector); // New Vector + qInsertMenu->addAction (qAct_newVertex); // New Vertex + + qEditMenu = menuBar ()->addMenu (tr ("&Edit")); + qEditMenu->addAction (qAct_cut); // Cut + qEditMenu->addAction (qAct_copy); // Copy + qEditMenu->addAction (qAct_paste); // Paste + + // Help menu + qHelpMenu = menuBar ()->addMenu (tr ("&Help")); + qHelpMenu->addAction (qAct_about); // About + qHelpMenu->addAction (qAct_aboutQt); // About Qt +} + +void LDForgeWindow::createToolbars () { + qFileToolBar = new QToolBar ("File"); + qFileToolBar->addAction (qAct_new); + qFileToolBar->addAction (qAct_open); + qFileToolBar->addAction (qAct_save); + qFileToolBar->addAction (qAct_saveAs); + addToolBar (qFileToolBar); + + qInsertToolBar = new QToolBar ("Insert"); + qInsertToolBar->addAction (qAct_newSubfile); + qInsertToolBar->addAction (qAct_newLine); + qInsertToolBar->addAction (qAct_newTriangle); + qInsertToolBar->addAction (qAct_newQuad); + qInsertToolBar->addAction (qAct_newCondLine); + qInsertToolBar->addAction (qAct_newComment); + qInsertToolBar->addAction (qAct_newVector); + qInsertToolBar->addAction (qAct_newVertex); + addToolBar (qInsertToolBar); + + qEditToolBar = new QToolBar ("Edit"); + qEditToolBar->addAction (qAct_cut); + qEditToolBar->addAction (qAct_copy); + qEditToolBar->addAction (qAct_paste); + addToolBar (qEditToolBar); +} + +void LDForgeWindow::slot_new () { + printf ("new file\n"); + + closeModel (); + newModel (); +} + +void LDForgeWindow::slot_open () { + str name = QFileDialog::getOpenFileName (this, "Open file", + "", "LDraw files (*.dat *.ldr)").toStdString().c_str(); + + if (g_LoadedFiles.size()) + closeModel (); + + IO_ParseLDFile (name); +} + +void LDForgeWindow::slot_save () { + printf ("save file\n"); +} + +void LDForgeWindow::slot_saveAs () { + printf ("save as file\n"); +} + +void LDForgeWindow::slot_exit () { + printf ("exit\n"); + exit (0); +} + +void LDForgeWindow::slot_newSubfile () { + +} + +void LDForgeWindow::slot_newLine () { + +} + +void LDForgeWindow::slot_newTriangle () { + +} + +void LDForgeWindow::slot_newQuad () { + +} + +void LDForgeWindow::slot_newCondLine () { + +} + +void LDForgeWindow::slot_newComment () { + +} + +void LDForgeWindow::slot_about () { + +} + +void LDForgeWindow::slot_aboutQt () { + QMessageBox::aboutQt (this); +} + +void LDForgeWindow::slot_cut () { + +} + +void LDForgeWindow::slot_copy () { + +} + +void LDForgeWindow::slot_paste () { + +} + +void LDForgeWindow::slot_newVector () { + +} + +void LDForgeWindow::slot_newVertex () { + +} + + +static QIcon IconForObjectType (LDObject* obj) { + switch (obj->getType ()) { + case OBJ_Empty: + return QIcon ("icons/empty.png"); + + case OBJ_Line: + return QIcon ("icons/line.png"); + + case OBJ_Quad: + return QIcon ("icons/quad.png"); + + case OBJ_Subfile: + return QIcon ("icons/subfile.png"); + + case OBJ_Triangle: + return QIcon ("icons/triangle.png"); + + case OBJ_CondLine: + return QIcon ("icons/condline.png"); + + case OBJ_Comment: + return QIcon ("icons/comment.png"); + + case OBJ_Vector: + return QIcon ("icons/vector.png"); + + case OBJ_Vertex: + return QIcon ("icons/vertex.png"); + + case OBJ_Gibberish: + case OBJ_Unidentified: + return QIcon ("icons/error.png"); + } + + return QIcon (); +} + +void LDForgeWindow::buildObjList () { + QList qaItems; + for (ushort i = 0; i < g_CurrentFile->objects.size(); ++i) { + LDObject* obj = g_CurrentFile->objects[i]; + + str zText; + switch (obj->getType ()) { + case OBJ_Comment: + zText = static_cast (obj)->zText; + break; + + case OBJ_Empty: + break; // leave it empty + + case OBJ_Line: + case OBJ_CondLine: + { + LDLine* line = static_cast (obj); + zText.format ("%s, %s", + line->vaCoords[0].getStringRep ().chars(), + line->vaCoords[1].getStringRep ().chars()); + } + break; + + case OBJ_Triangle: + { + LDTriangle* triangle = static_cast (obj); + zText.format ("%s, %s, %s", + triangle->vaCoords[0].getStringRep ().chars(), + triangle->vaCoords[1].getStringRep ().chars(), + triangle->vaCoords[2].getStringRep ().chars()); + } + break; + + case OBJ_Quad: + { + LDQuad* quad = static_cast (obj); + zText.format ("%s, %s, %s", + quad->vaCoords[0].getStringRep ().chars(), + quad->vaCoords[1].getStringRep ().chars(), + quad->vaCoords[2].getStringRep ().chars(), + quad->vaCoords[3].getStringRep ().chars()); + } + break; + + case OBJ_Gibberish: + zText.format ("ERROR: %s", + static_cast (obj)->zContent.chars()); + break; + + case OBJ_Vector: + zText.format ("%s", static_cast (obj)->vPos.getStringRep().chars()); + break; + + case OBJ_Vertex: + zText.format ("%s", static_cast (obj)->vPosition.getStringRep().chars()); + break; + + default: + zText = g_saObjTypeNames[obj->getType ()]; + break; + } + + QTreeWidgetItem* item = new QTreeWidgetItem ((QTreeWidget*)nullptr, + QStringList (zText.chars()), 0); + item->setIcon (0, IconForObjectType (obj)); + + qaItems.append (item); + } + + printf ("insert top level items\n"); + qObjList->insertTopLevelItems (0, qaItems); +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac gui.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gui.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,76 @@ +#ifndef __GUI_H__ +#define __GUI_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "draw.h" + +class LDForgeWindow : public QMainWindow { + Q_OBJECT + +public: + renderer* R; + + // Object list view + QTreeWidget* qObjList; + + // Message log + QTextEdit* qMessageLog; + + // Menus + QMenu* qFileMenu, *qEditMenu, *qInsertMenu, *qHelpMenu; + + // Toolbars + QToolBar* qFileToolBar, *qEditToolBar, *qInsertToolBar; + + // ACTION ARMADA + QAction* qAct_new, *qAct_open, *qAct_save, *qAct_saveAs, *qAct_exit; + QAction* qAct_cut, *qAct_copy, *qAct_paste; + QAction* qAct_newSubfile, *qAct_newLine, *qAct_newTriangle, *qAct_newQuad; + QAction* qAct_newCondLine, *qAct_newComment, *qAct_newVector, *qAct_newVertex; + QAction* qAct_about, *qAct_aboutQt; + + LDForgeWindow (); + void buildObjList (); + +private: + void createMenuActions (); + void createMenus (); + void createToolbars (); + +private slots: + void slot_new (); + void slot_open (); + void slot_save (); + void slot_saveAs (); + void slot_exit (); + + void slot_newSubfile (); + void slot_newLine (); + void slot_newTriangle (); + void slot_newQuad (); + void slot_newCondLine (); + void slot_newComment (); + void slot_newVector (); + void slot_newVertex (); + + void slot_cut (); + void slot_copy (); + void slot_paste (); + + void slot_about (); + void slot_aboutQt (); +}; + +enum { + LDOLC_Icon, + LDOLC_Data, + NUM_LDOL_Columns +}; + +#endif \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac icons/.directory --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/icons/.directory Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,4 @@ +[Dolphin] +PreviewsShown=true +Timestamp=2013,3,14,16,3,19 +Version=3 diff -r 000000000000 -r c51cce84a9ac icons/add-line.png Binary file icons/add-line.png has changed diff -r 000000000000 -r c51cce84a9ac icons/add-quad.png Binary file icons/add-quad.png has changed diff -r 000000000000 -r c51cce84a9ac icons/add-subfile.png Binary file icons/add-subfile.png has changed diff -r 000000000000 -r c51cce84a9ac icons/add-triangle.png Binary file icons/add-triangle.png has changed diff -r 000000000000 -r c51cce84a9ac icons/add-vector.png Binary file icons/add-vector.png has changed diff -r 000000000000 -r c51cce84a9ac icons/copy.png Binary file icons/copy.png has changed diff -r 000000000000 -r c51cce84a9ac icons/cut.png Binary file icons/cut.png has changed diff -r 000000000000 -r c51cce84a9ac icons/del-line.png Binary file icons/del-line.png has changed diff -r 000000000000 -r c51cce84a9ac icons/empty.png Binary file icons/empty.png has changed diff -r 000000000000 -r c51cce84a9ac icons/error.png Binary file icons/error.png has changed diff -r 000000000000 -r c51cce84a9ac icons/file-new.png Binary file icons/file-new.png has changed diff -r 000000000000 -r c51cce84a9ac icons/file.png Binary file icons/file.png has changed diff -r 000000000000 -r c51cce84a9ac icons/line.png Binary file icons/line.png has changed diff -r 000000000000 -r c51cce84a9ac icons/paste.png Binary file icons/paste.png has changed diff -r 000000000000 -r c51cce84a9ac icons/quad.png Binary file icons/quad.png has changed diff -r 000000000000 -r c51cce84a9ac icons/save-as.png Binary file icons/save-as.png has changed diff -r 000000000000 -r c51cce84a9ac icons/save.png Binary file icons/save.png has changed diff -r 000000000000 -r c51cce84a9ac icons/subfile.png Binary file icons/subfile.png has changed diff -r 000000000000 -r c51cce84a9ac icons/triangle.png Binary file icons/triangle.png has changed diff -r 000000000000 -r c51cce84a9ac icons/vector.png Binary file icons/vector.png has changed diff -r 000000000000 -r c51cce84a9ac io.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/io.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,161 @@ +#include + +#include "common.h" +#include "io.h" +#include "ldtypes.h" +#include "misc.h" + +// ============================================================================= +// IO_FindLoadedFile (str) +// +// Returns a pointer to the first found open file with the given name. +// ============================================================================= +OpenFile* IO_FindLoadedFile (str name) { + OpenFile* file; + + for (uint i = 0; i < g_LoadedFiles.size(); i++) { + file = g_LoadedFiles[i]; + if (!file->filename.icompare (name)) + return file; + } + + return NULL; +} + +// ============================================================================= +// IO_ParseLDFile (str) +// +// Opens the given file and parses the LDraw code within. +// ============================================================================= +OpenFile* IO_ParseLDFile (str path) { + FILE* fp = fopen (path.chars (), "r"); + + if (!fp) { + printf ("Couldn't open %s!\n", path.chars ()); + return NULL; + } + + OpenFile* load = new OpenFile; + load->filename = path; + + vector lines; + + { + char line[1024]; + while (fgets (line, sizeof line, fp)) { + // Trim the trailing newline + str zLine = line; + while (zLine[~zLine - 1] == '\n' || zLine[~zLine - 1] == '\r') + zLine -= 1; + + lines.push_back (zLine); + } + } + + fclose (fp); + + for (ulong i = 0; i < lines.size(); ++i) + load->objects.push_back (ParseLine (lines[i])); + + g_LoadedFiles.push_back (load); + return g_LoadedFiles[g_LoadedFiles.size() - 1]; +} + +// ============================================================================= +// ParseLine (str) +// +// Parses a string line containing an LDraw object and returns the object parsed. +// ============================================================================= +LDObject* ParseLine (str zLine) { + str zNoWhitespace = zLine; + StripWhitespace (zNoWhitespace); + if (!~zNoWhitespace) { + // Line was empty, or only consisted of whitespace + return new LDEmpty; + } + + char c = zLine[0]; + vector tokens = zLine / " "; + printf ("line: %s\n", zLine.chars()); + + switch (c - '0') { + case 0: + { + // Comment + LDComment* obj = new LDComment; + obj->zText = zLine.substr (1, -1); + printf ("\t-> comment (%s)\n", obj->zText.chars()); + return obj; + } + + case 1: + { + // Subfile + LDSubfile* obj = new LDSubfile; + obj->dColor = atoi (tokens[1]); + obj->vPosition = ParseVertex (zLine, 2); // 2 - 4 + + for (short i = 0; i < 9; ++i) + obj->faMatrix[i] = atof (tokens[i + 5]); // 5 - 13 + + obj->zFileName = tokens[14]; + return obj; + } + + case 2: + { + // Line + LDLine* obj = new LDLine; + obj->dColor = GetWordInt (zLine, 1); + for (short i = 0; i < 2; ++i) + obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 7 + return obj; + } + + case 3: + { + // Triangle + LDTriangle* obj = new LDTriangle; + obj->dColor = GetWordInt (zLine, 1); + + for (short i = 0; i < 3; ++i) + obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 10 + + return obj; + } + + case 4: + { + // Quadrilateral + LDQuad* obj = new LDQuad; + obj->dColor = GetWordInt (zLine, 1); + + for (short i = 0; i < 4; ++i) + obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 13 + + return obj; + } + + case 5: + { + // Conditional line + LDCondLine* obj = new LDCondLine; + obj->dColor = GetWordInt (zLine, 1); + + for (short i = 0; i < 2; ++i) + obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 7 + + for (short i = 0; i < 2; ++i) + obj->vaControl[i] = ParseVertex (zLine, 8 + (i * 3)); // 8 - 13 + return obj; + } + + default: + { + // Strange line we couldn't parse + LDGibberish* obj = new LDGibberish; + obj->zContent = zLine; + return obj; + } + } +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac io.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/io.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,15 @@ +#ifndef __IO_H__ +#define __IO_H__ + +#include "common.h" +#include "ldtypes.h" +#include "str.h" + +// PROTOTYPES +OpenFile* IO_FindLoadedFile (str name); +OpenFile* IO_ParseLDFile (str path); +LDObject* ParseLine (str zLine); + +extern vector g_LoadedFiles; + +#endif \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac ldforge.kdev4 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ldforge.kdev4 Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,3 @@ +[Project] +Manager=KDevCustomMakeManager +Name=ldforge diff -r 000000000000 -r c51cce84a9ac ldforge.pro --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ldforge.pro Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,36 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Sat Sep 22 17:29:49 2012 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += bbox.h \ + common.h \ + draw.h \ + gui.h \ + io.h \ + ldtypes.h \ + misc.h \ + model.h \ + scanner.h \ + str.h \ + config.h \ + cfgdef.h +SOURCES += bbox.cpp \ + draw.cpp \ + gui.cpp \ + io.cpp \ + ldtypes.cpp \ + main.cpp \ + misc.cpp \ + model.cpp \ + scanner.cpp \ + str.cpp \ + config.cpp + +QMAKE_CXXFLAGS += -std=c++0x +QT += opengl \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac ldtypes.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ldtypes.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,62 @@ +#include "common.h" +#include "ldtypes.h" + +const char* g_saObjTypeNames[] = { + "unidentified", + "unknown", + "empty", + "comment", + "subfile", + "line", + "triangle", + "quadrilateral", + "condline", + "vector", + "vertex", +}; + +// ============================================================================= +// LDObject constructors +LDObject::LDObject () { + +} + +LDGibberish::LDGibberish () { + +} + +LDEmpty::LDEmpty () { + +} + +LDComment::LDComment () { + +} + +LDSubfile::LDSubfile () { + +} + +LDLine::LDLine () { + +} + +LDTriangle::LDTriangle () { + +} + +LDQuad::LDQuad () { + +} + +LDCondLine::LDCondLine () { + +} + +LDVector::LDVector () { + +} + +LDVertex::LDVertex () { + +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac ldtypes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ldtypes.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,208 @@ +#ifndef __LDTYPES_H__ +#define __LDTYPES_H__ + +#include "common.h" +#include "str.h" + +#define IMPLEMENT_LDTYPE(N) \ + LD##N (); \ + virtual LDObjectType_e getType () const { \ + return OBJ_##N; \ + } + +// ============================================================================= +// LDObjectType_e +// +// Object type codes +// ============================================================================= +enum LDObjectType_e { + OBJ_Unidentified, // Object is uninitialized (LDObject) + OBJ_Gibberish, // Object is unknown (LDUnknown; bad code number) + OBJ_Empty, // Object represents an empty line (LDEmpty) + OBJ_Comment, // Object represents a comment (LDComment, code: 0) + OBJ_Subfile, // Object represents a sub-file reference (LDSubfile, code: 1) + OBJ_Line, // Object represents a line (LDLine, code: 2) + OBJ_Triangle, // Object represents a triangle (LDTriangle, code: 3) + OBJ_Quad, // Object represents a quadrilateral (LDQuad, code: 4) + OBJ_CondLine, // Object represents a conditional line (LDCondLine, code: 5) + OBJ_Vector, // Object is a vector, LDForge extension object (LDVector) + OBJ_Vertex // Object is a vertex, LDForge extension object (LDVertex) +}; + +// ============================================================================= +// LDObject +// +// Base class object for all LD* types. Each LDObject represents a single line +// in the LDraw code file. The virtual method getType returns an enumerator +// which is a token of the object's type. The object can be casted into +// sub-classes based on this enumerator. +// ============================================================================= +class LDObject { +public: + LDObject (); + + virtual LDObjectType_e getType () const { + return OBJ_Unidentified; + }; +}; + +// ============================================================================= +// LDGibberish +// +// Represents a line in the LDraw file that could not be properly parsed. It is +// represented by a (!) ERROR in the code view. It exists for the purpose of +// allowing garbage lines be debugged and corrected within LDForge. The member +// zContent contains the contents of the unparsable line. +// ============================================================================= +class LDGibberish : public LDObject { +public: + IMPLEMENT_LDTYPE (Gibberish) + + // Content of this unknown line + str zContent; +}; + +// ============================================================================= +// LDEmptyLine +// +// Represents an empty line in the LDraw code file. +// ============================================================================= +class LDEmpty : public LDObject { +public: + IMPLEMENT_LDTYPE (Empty) +}; + +// ============================================================================= +// LDComment +// +// Represents a code-0 comment in the LDraw code file. Member zText contains +// the text of the comment. +// ============================================================================= +class LDComment : public LDObject { +public: + IMPLEMENT_LDTYPE (Comment) + + str zText; // The text of this comment +}; + +// ============================================================================= +// LDSubfile +// +// Represents a single code-1 subfile reference. +// ============================================================================= +class LDSubfile : public LDObject { +public: + IMPLEMENT_LDTYPE (Subfile) + + short dColor; // Color used by the reference + vertex vPosition; // Position of the subpart + double faMatrix[9]; // Transformation matrix for the subpart + str zFileName; // Filename of the subpart +}; + +// ============================================================================= +// LDLine +// +// Represents a single code-2 line in the LDraw code file. v0 and v1 are the end +// points of the line. The line is colored with dColor unless uncolored mode is +// set. +// ============================================================================= +class LDLine : public LDObject { +public: + IMPLEMENT_LDTYPE (Line) + + short dColor; // Color of this line + vertex vaCoords[2]; // End points of this line +}; + +// ============================================================================= +// LDCondLine +// +// Represents a single code-5 conditional line. The end-points v0 and v1 are +// inherited from LDLine, c0 and c1 are the control points of this line. +// ============================================================================= +class LDCondLine : public LDLine { +public: + IMPLEMENT_LDTYPE (CondLine) + + vertex vaControl[2]; // Control points +}; + +// ============================================================================= +// LDTriangle +// +// Represents a single code-3 triangle in the LDraw code file. Vertices v0, v1 +// and v2 contain the end-points of this triangle. dColor is the color the +// triangle is colored with. +// ============================================================================= +class LDTriangle : public LDObject { +public: + IMPLEMENT_LDTYPE (Triangle) + + short dColor; + vertex vaCoords[3]; + + LDTriangle (vertex _v0, vertex _v1, vertex _v2) { + vaCoords[0] = _v0; + vaCoords[1] = _v1; + vaCoords[2] = _v2; + } +}; + +// ============================================================================= +// LDQuad +// +// Represents a single code-4 quadrilateral. v0, v1, v2 and v3 are the end points +// of the quad, dColor is the color used for the quad. +// ============================================================================= +class LDQuad : public LDObject { +public: + IMPLEMENT_LDTYPE (Quad) + + short dColor; + vertex vaCoords[4]; +}; + +// ============================================================================= +// LDVector +// +// The vector is a LDForge-specific extension. It is essentially a line with an +// alternative definition. Instead of being defined with two vertices, the vector +// is defined with a single vertex, a length and a bearing. This makes it useful +// for dealing with lines positioned on arbitrary angles without fear of precision +// loss of vertex coordinates. A vector can be transformed into a line and vice +// versa. Vectors are not a part of official LDraw standard and should be +// converted to lines for finished parts. +// +// TODO: should a conditional vector be considered? +// ============================================================================= +class LDVector : public LDObject { +public: + IMPLEMENT_LDTYPE (Vector) + + vertex vPos; + bearing gAngle3D; + ulong ulLength; +}; + +// ============================================================================= +// LDVertex +// +// The vertex is another LDForce-specific extension. It represents a single +// vertex which can be used as a parameter to tools or to store coordinates +// with. Vertices are a part authoring tool and they should not appear in +// finished parts. +// ============================================================================= +class LDVertex : public LDObject { +public: + IMPLEMENT_LDTYPE (Vertex) + + vertex vPosition; +}; + +// ============================================================================= +// Object type names. Pass the return value of getType as the index to get a +// string representation of the object's type. +extern const char* g_saObjTypeNames[]; + +#endif \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,55 @@ +#include +#include "gui.h" +#include "io.h" +#include "bbox.h" + +vector g_LoadedFiles; +OpenFile* g_CurrentFile = NULL; +LDForgeWindow* g_qWindow = NULL; +bbox g_BBox; + +int main (int argc, char** argv) { + g_CurrentFile = IO_ParseLDFile ("55966.dat"); + g_BBox.calculate(); + + printf ("bbox: (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f)\n", + FVERTEX (g_BBox.v0), FVERTEX (g_BBox.v1)); + printf ("%u objects\n", g_CurrentFile->objects.size()); + + QApplication app (argc, argv); + LDForgeWindow* win = new LDForgeWindow; + g_qWindow = win; + g_qWindow->buildObjList (); + win->show (); + return app.exec (); +} + +vertex vertex::midpoint (vertex& other) { + vertex mid; + mid.x = (x + other.x); + mid.y = (y + other.y); + mid.z = (z + other.z); + return mid; +} + +static str getCoordinateRep (double fCoord) { + str zRep = str::mkfmt ("%.3f", fCoord); + + // Remove trailing zeroes + while (zRep[~zRep - 1] == '0') + zRep -= 1; + + // If there was only zeroes in the decimal place, remove + // the decimal point now. + if (zRep[~zRep - 1] == '.') + zRep -= 1; + + return zRep; +} + +str vertex::getStringRep () { + return str::mkfmt ("(%s, %s, %s)", + getCoordinateRep (x).chars(), + getCoordinateRep (y).chars(), + getCoordinateRep (z).chars()); +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac misc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,37 @@ +#include "common.h" +#include + +double GetWordFloat (str& s, int n) { + return atof ((s / " ")[n]); +} + +long GetWordInt (str& s, int n) { + return atol ((s / " ")[n]); +} + +vertex ParseVertex (str& s, int n) { + vertex v; + v.x = GetWordFloat (s, n); + v.y = GetWordFloat (s, n+1); + v.z = GetWordFloat (s, n+2); + + return v; +} + +void StripWhitespace (str& s) { + str other; + + for (size_t i = 0; i < ~s; i++) + if (s[i] > 32 && s[i] < 127) + other += s[i]; +} + +vertex bearing::project (vertex& vSource, ulong ulLength) { + vertex vDest = vSource; + + vDest.x += (cos (fAngle) * ulLength); + vDest.y += (sin (fAngle) * ulLength); + vDest.x += (cos (fPitch) * ulLength); + vDest.z += (sin (fPitch) * ulLength); + return vDest; +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac misc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,16 @@ +#ifndef __MISC_H__ +#define __MISC_H__ + +#include "common.h" +#include "str.h" + +inline str GetWord (str& zString, ulong ulIndex) { + return (zString / " ")[ulIndex]; +} + +double GetWordFloat (str& s, int n); +long GetWordInt (str& s, int n); +vertex ParseVertex (str& s, int n); +void StripWhitespace (str& s); + +#endif // __MISC_H__ \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac moc_draw.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moc_draw.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,81 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'draw.h' +** +** Created: Fri Mar 15 18:10:06 2013 +** by: The Qt Meta Object Compiler version 63 (Qt 4.8.3) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "draw.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'draw.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.3. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_renderer[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_renderer[] = { + "renderer\0" +}; + +void renderer::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + Q_UNUSED(_o); + Q_UNUSED(_id); + Q_UNUSED(_c); + Q_UNUSED(_a); +} + +const QMetaObjectExtraData renderer::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject renderer::staticMetaObject = { + { &QGLWidget::staticMetaObject, qt_meta_stringdata_renderer, + qt_meta_data_renderer, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &renderer::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *renderer::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *renderer::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_renderer)) + return static_cast(const_cast< renderer*>(this)); + return QGLWidget::qt_metacast(_clname); +} + +int renderer::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QGLWidget::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} +QT_END_MOC_NAMESPACE diff -r 000000000000 -r c51cce84a9ac moc_gui.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moc_gui.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,135 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'gui.h' +** +** Created: Fri Mar 15 19:46:05 2013 +** by: The Qt Meta Object Compiler version 63 (Qt 4.8.3) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "gui.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'gui.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 63 +#error "This file was generated using the moc from 4.8.3. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_LDForgeWindow[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 18, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 15, 14, 14, 14, 0x08, + 26, 14, 14, 14, 0x08, + 38, 14, 14, 14, 0x08, + 50, 14, 14, 14, 0x08, + 64, 14, 14, 14, 0x08, + 76, 14, 14, 14, 0x08, + 94, 14, 14, 14, 0x08, + 109, 14, 14, 14, 0x08, + 128, 14, 14, 14, 0x08, + 143, 14, 14, 14, 0x08, + 162, 14, 14, 14, 0x08, + 180, 14, 14, 14, 0x08, + 197, 14, 14, 14, 0x08, + 214, 14, 14, 14, 0x08, + 225, 14, 14, 14, 0x08, + 237, 14, 14, 14, 0x08, + 250, 14, 14, 14, 0x08, + 263, 14, 14, 14, 0x08, + + 0 // eod +}; + +static const char qt_meta_stringdata_LDForgeWindow[] = { + "LDForgeWindow\0\0slot_new()\0slot_open()\0" + "slot_save()\0slot_saveAs()\0slot_exit()\0" + "slot_newSubfile()\0slot_newLine()\0" + "slot_newTriangle()\0slot_newQuad()\0" + "slot_newCondLine()\0slot_newComment()\0" + "slot_newVector()\0slot_newVertex()\0" + "slot_cut()\0slot_copy()\0slot_paste()\0" + "slot_about()\0slot_aboutQt()\0" +}; + +void LDForgeWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + LDForgeWindow *_t = static_cast(_o); + switch (_id) { + case 0: _t->slot_new(); break; + case 1: _t->slot_open(); break; + case 2: _t->slot_save(); break; + case 3: _t->slot_saveAs(); break; + case 4: _t->slot_exit(); break; + case 5: _t->slot_newSubfile(); break; + case 6: _t->slot_newLine(); break; + case 7: _t->slot_newTriangle(); break; + case 8: _t->slot_newQuad(); break; + case 9: _t->slot_newCondLine(); break; + case 10: _t->slot_newComment(); break; + case 11: _t->slot_newVector(); break; + case 12: _t->slot_newVertex(); break; + case 13: _t->slot_cut(); break; + case 14: _t->slot_copy(); break; + case 15: _t->slot_paste(); break; + case 16: _t->slot_about(); break; + case 17: _t->slot_aboutQt(); break; + default: ; + } + } + Q_UNUSED(_a); +} + +const QMetaObjectExtraData LDForgeWindow::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject LDForgeWindow::staticMetaObject = { + { &QMainWindow::staticMetaObject, qt_meta_stringdata_LDForgeWindow, + qt_meta_data_LDForgeWindow, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &LDForgeWindow::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *LDForgeWindow::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *LDForgeWindow::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_LDForgeWindow)) + return static_cast(const_cast< LDForgeWindow*>(this)); + return QMainWindow::qt_metacast(_clname); +} + +int LDForgeWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QMainWindow::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 18) + qt_static_metacall(this, _c, _id, _a); + _id -= 18; + } + return _id; +} +QT_END_MOC_NAMESPACE diff -r 000000000000 -r c51cce84a9ac moc_ldforge.cpp diff -r 000000000000 -r c51cce84a9ac model.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/model.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,38 @@ +#include +#include +#include "common.h" +#include "io.h" +#include "gui.h" +#include "draw.h" + +// Clear everything from the model +void closeModel () { + // Remove all loaded files and the objects they contain + for (ushort i = 0; i < g_LoadedFiles.size(); i++) { + OpenFile* f = g_LoadedFiles[i]; + + for (ushort j = 0; j < f->objects.size(); ++j) + delete f->objects[j]; + + delete f; + } + + // Clear the array + g_LoadedFiles.clear(); + g_CurrentFile = NULL; + + g_qWindow->R->hardRefresh(); +} + +void newModel () { + // Create a new anonymous file and set it to our current + if (g_LoadedFiles.size()) + closeModel (); // Close any open file first, though + + OpenFile* f = new OpenFile; + f->filename = ""; + g_LoadedFiles.push_back (f); + g_CurrentFile = f; + + g_qWindow->R->hardRefresh(); +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac model.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/model.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,2 @@ +void closeModel (); +void newModel (); \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac scanner.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scanner.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,409 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `azimuth` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "string.h" +#include "str.h" +#include "common.h" +#include "scanner.h" +#include "stdarg.h" + +#define STORE_POSITION \ + const bool _atnewline = bAtNewLine; \ + const ulong ulStoredLineNumber = ulaLineNumber[fc]; \ + const ulong ulStoredCurChar = ulaCurChar[fc]; + +#define RESTORE_POSITION \ + bAtNewLine = _atnewline; \ + ulaLineNumber[fc] = ulStoredLineNumber; \ + ulaCurChar[fc] = ulStoredCurChar; + +// ============================================================================ +Scanner::Scanner (str path) { + token = ""; + zPrevToken = ""; + lPrevPos = 0; + fc = -1; + + for (unsigned int u = 0; u < MAX_FILESTACK; u++) + fp[u] = NULL; + + OpenFile (path); + dCommentMode = 0; +} + +// ============================================================================ +Scanner::~Scanner () { + // If comment mode is 2 by the time the file ended, the + // comment was left unterminated. 1 is no problem, since + // it's terminated by newlines anyway. + if (dCommentMode == 2) + ParserError ("unterminated `/*`-style comment"); + + for (unsigned int u = 0; u < MAX_FILESTACK; u++) { + if (fp[u]) { + ParserWarning ("file idx %u remained open after parsing", u); + CloseFile (u); + } + } +} + +// ============================================================================ +// Opens a file and pushes its pointer to stack +void Scanner::OpenFile (str path) { + if (fc+1 >= MAX_FILESTACK) + ParserError ("supposed to open file `%s` but file stack is full! do you have recursive `#include` directives?", + path.chars()); + + // Save the position first. + if (fc != -1) { + laSavedPos[fc] = ftell (fp[fc]); + } + + fc++; + + fp[fc] = fopen (path.chars(), "r"); + if (!fp[fc]) { + ParserError ("couldn't open %s for reading!\n", path.chars ()); + exit (1); + } + + fseek (fp[fc], 0, SEEK_SET); + saFilePath[fc] = path.chars(); + ulaLineNumber[fc] = 1; + ulaCurChar[fc] = 1; + ulaPosition[fc] = 0; + bAtNewLine = 0; +} + +// ============================================================================ +// Closes the current file +void Scanner::CloseFile (unsigned int u) { + if (u >= MAX_FILESTACK) + u = fc; + + if (!fp[u]) + return; + + fclose (fp[u]); + fp[u] = NULL; + fc--; + + if (fc != -1) + fseek (fp[fc], laSavedPos[fc], SEEK_SET); +} + +// ============================================================================ +char Scanner::ReadChar () { + if (feof (fp[fc])) + return 0; + + char c; + if (!fread (&c, 1, 1, fp[fc])) + return 0; + + // We're at a newline, thus next char read will begin the next line + if (bAtNewLine) { + bAtNewLine = false; + ulaLineNumber[fc]++; + ulaCurChar[fc] = 0; // gets incremented to 1 + } + + if (c == '\n') + bAtNewLine = true; + + ulaCurChar[fc]++; + return c; +} + +// ============================================================================ +// Peeks the next character +char Scanner::PeekChar (int offset) { + // Store current position + long curpos = ftell (fp[fc]); + STORE_POSITION + + // Forward by offset + fseek (fp[fc], offset, SEEK_CUR); + + // Read the character + char* c = (char*)malloc (sizeof (char)); + + if (!fread (c, sizeof (char), 1, fp[fc])) { + fseek (fp[fc], curpos, SEEK_SET); + return 0; + } + + // Rewind back + fseek (fp[fc], curpos, SEEK_SET); + RESTORE_POSITION + + return c[0]; +} + +// ============================================================================ +// Read a token from the file buffer. Returns true if token was found, false if not. +bool Scanner::Next (bool peek) { + lPrevPos = ftell (fp[fc]); + str tmp = ""; + + while (1) { + // Check end-of-file + if (feof (fp[fc])) { + // If we're just peeking, we shouldn't + // actually close anything.. + if (peek) + break; + + CloseFile (); + if (fc == -1) + break; + } + + // Check if the next token possibly starts a comment. + if (PeekChar () == '/' && !tmp.len()) { + char c2 = PeekChar (1); + // C++-style comment + if (c2 == '/') + dCommentMode = 1; + else if (c2 == '*') + dCommentMode = 2; + + // We don't need to actually read in the + // comment characters, since they will get + // ignored due to comment mode anyway. + } + + c = ReadChar (); + + // If this is a comment we're reading, check if this character + // gets the comment terminated, otherwise ignore it. + if (dCommentMode > 0) { + if (dCommentMode == 1 && c == '\n') { + // C++-style comments are terminated by a newline + dCommentMode = 0; + continue; + } else if (dCommentMode == 2 && c == '*') { + // C-style comments are terminated by a `*/` + if (PeekChar() == '/') { + dCommentMode = 0; + ReadChar (); + } + } + + // Otherwise, ignore it. + continue; + } + + // Non-alphanumber characters (sans underscore) break the word too. + // If there was prior data, the delimeter pushes the cursor back so + // that the next character will be the same delimeter. If there isn't, + // the delimeter itself is included (and thus becomes a token itself.) + if ((c >= 33 && c <= 47) || + (c >= 58 && c <= 64) || + (c >= 91 && c <= 96 && c != '_') || + (c >= 123 && c <= 126)) { + if (tmp.len()) + fseek (fp[fc], ftell (fp[fc]) - 1, SEEK_SET); + else + tmp += c; + break; + } + + if (c <= 32 || c >= 127) { + // Don't break if we haven't gathered anything yet. + if (tmp.len()) + break; + } else { + tmp += c; + } + } + + // If we got nothing here, read failed. This should + // only happen in the case of EOF. + if (!tmp.len()) { + token = ""; + return false; + } + + ulaPosition[fc]++; + zPrevToken = token; + token = tmp; + return true; +} + +// ============================================================================ +// Returns the next token without advancing the cursor. +str Scanner::PeekNext (int offset) { + // Store current information + str storedtoken = token; + int cpos = ftell (fp[fc]); + STORE_POSITION + + // Advance on the token. + while (offset >= 0) { + if (!Next (true)) + return ""; + offset--; + } + + str tmp = token; + + // Restore position + fseek (fp[fc], cpos, SEEK_SET); + ulaPosition[fc]--; + token = storedtoken; + RESTORE_POSITION + return tmp; +} + +// ============================================================================ +void Scanner::Seek (unsigned int n, int origin) { + switch (origin) { + case SEEK_SET: + fseek (fp[fc], 0, SEEK_SET); + ulaPosition[fc] = 0; + break; + case SEEK_CUR: + break; + case SEEK_END: + printf ("ScriptReader::Seek: SEEK_END not yet supported.\n"); + break; + } + + for (unsigned int i = 0; i < n+1; i++) + Next(); +} + +// ============================================================================ +void Scanner::MustNext (const char* c) { + if (!Next()) { + if (strlen (c)) + ParserError ("expected `%s`, reached end of file instead\n", c); + else + ParserError ("expected a token, reached end of file instead\n"); + } + + if (strlen (c)) + MustThis (c); +} + +// ============================================================================ +void Scanner::MustThis (const char* c) { + if (token.compare (c) != 0) + ParserError ("expected `%s`, got `%s` instead", c, token.chars()); +} + +// ============================================================================ +void Scanner::ParserError (const char* message, ...) { + PERFORM_FORMAT (message, outmessage); + ParserMessage ("\nError: ", outmessage); + exit (1); +} + +// ============================================================================ +void Scanner::ParserWarning (const char* message, ...) { + PERFORM_FORMAT (message, outmessage); + ParserMessage ("Warning: ", outmessage); +} + +// ============================================================================ +void Scanner::ParserMessage (const char* header, char* message) { + if (fc >= 0 && fc < MAX_FILESTACK) + fprintf (stderr, "%s%s:%lu:%lu: %s\n", + header, saFilePath[fc], ulaLineNumber[fc], ulaCurChar[fc], message); + else + fprintf (stderr, "%s%s\n", header, message); +} + +// ============================================================================ +// if gotquote == 1, the current token already holds the quotation mark. +void Scanner::MustString (bool gotquote) { + if (gotquote) + MustThis ("\""); + else + MustNext ("\""); + + str string; + // Keep reading characters until we find a terminating quote. + while (1) { + // can't end here! + if (feof (fp[fc])) + ParserError ("unterminated string"); + + char c = ReadChar (); + if (c == '"') + break; + + string += c; + } + + token = string; +} + +// ============================================================================ +void Scanner::MustNumber (bool fromthis) { + if (!fromthis) + MustNext (); + + str num = token; + if (!num.compare ("-")) { + MustNext (); + num += token; + } + + // "true" and "false" are valid numbers + if (!token.icompare ("true")) + token = "1"; + else if (!token.icompare ("false")) + token = "0"; + else { + if (!token.isnumber()) + ParserError ("expected a number, got `%s`", num.chars()); + + str check; + check.appendformat ("%d", atoi (num.chars ())); + if (token.compare (check) != 0) + ParserWarning ("integer too large: %s -> %s", num.chars(), check.chars()); + + token = num; + } +} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac scanner.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scanner.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,102 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `azimuth` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SCRIPTREADER_H__ +#define __SCRIPTREADER_H__ + +#include +#include "str.h" + +#define MAX_FILESTACK 8 +#define MAX_SCOPE 32 +#define MAX_CASE 64 + +#define PERFORM_FORMAT(in, out) \ + va_list v; \ + va_start (v, in); \ + char* out = vdynformat (in, v, 256); \ + va_end (v); + +class Scanner { +public: + // ==================================================================== + // MEMBERS + FILE* fp[MAX_FILESTACK]; + char* saFilePath[MAX_FILESTACK]; + int fc; + + ulong ulaPosition[MAX_FILESTACK]; + ulong ulaLineNumber[MAX_FILESTACK]; + ulong ulaCurChar[MAX_FILESTACK]; + long laSavedPos[MAX_FILESTACK]; // filepointer cursor position + str token; + short dCommentMode; + long lPrevPos; + str zPrevToken; + + // ==================================================================== + // METHODS + Scanner (str path); + ~Scanner (); + void OpenFile (str path); + void CloseFile (unsigned int u = MAX_FILESTACK); + char ReadChar (); + char PeekChar (int offset = 0); + bool Next (bool peek = false); + void Prev (); + str PeekNext (int offset = 0); + void Seek (unsigned int n, int origin); + void MustNext (const char* c = ""); + void MustThis (const char* c); + void MustString (bool gotquote = false); + void MustNumber (bool fromthis = false); + void MustBool (); + bool BoolValue (); + + void ParserError (const char* message, ...); + void ParserWarning (const char* message, ...); + +private: + bool bAtNewLine; + char c; + void ParserMessage (const char* header, char* message); +}; + +#endif // __SCRIPTREADER_H__ \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac str.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/str.cpp Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,466 @@ +#include +#include +#include +#include +#include +// #include +#include "str.h" + +#define ITERATE_STRING(u) \ + for (unsigned int u = 0; u < strlen (text); u++) + +// ============================================================================ +// vdynformat: Try to write to a formatted string with size bytes first, if +// that fails, double the size and keep recursing until it works. +char* vdynformat (const char* csFormat, va_list vArgs, long lSize) { + char* buffer = new char[lSize]; + int r = vsnprintf (buffer, lSize - 1, csFormat, vArgs); + if (r > (signed)(lSize - 1) || r < 0) { + delete[] buffer; + buffer = vdynformat (csFormat, vArgs, lSize * 2); + } + return buffer; +} + +// ============================================================================ +str::str () { + text = new char[1]; + clear(); + alloclen = strlen (text); +} + +str::str (const char* c) { + text = new char[1]; + clear (); + alloclen = strlen (text); + append (c); +} + +str::str (char c) { + text = new char[1]; + clear (); + alloclen = strlen (text); + append (c); +} + +str::~str () { + // delete[] text; +} + +// ============================================================================ +void str::clear () { + delete[] text; + text = new char[1]; + text[0] = '\0'; + curs = 0; + alloclen = 0; +} + +// ============================================================================ +void str::resize (unsigned int len) { + unsigned int oldlen = strlen (text); + char* oldtext = new char[oldlen]; + strncpy (oldtext, text, oldlen); + + delete[] text; + text = new char[len+1]; + for (unsigned int u = 0; u < len+1; u++) + text[u] = 0; + strncpy (text, oldtext, len); + delete[] oldtext; + + alloclen = len; +} + +// ============================================================================ +void str::dump () { + for (unsigned int u = 0; u <= alloclen; u++) + printf ("\t%u. %u (%c)\n", u, text[u], text[u]); +} + +// ============================================================================ +// Adds a new character at the end of the string. +void str::append (char c) { + // Out of space, thus resize + if (curs == alloclen) + resize (alloclen + 1); + text[curs] = c; + curs++; +} + +void str::append (const char* c) { + resize (alloclen + strlen (c)); + + for (unsigned int u = 0; u < strlen (c); u++) { + if (c[u] != 0) + append (c[u]); + } +} + +void str::append (str c) { + append (c.chars()); +} + +// ============================================================================ +void str::appendformat (const char* c, ...) { + va_list v; + + va_start (v, c); + char* buf = vdynformat (c, v, 256); + va_end (v); + + append (buf); + delete[] buf; +} + +void str::format (const char* fmt, ...) { + clear (); + + va_list v; + + va_start (v, fmt); + char* buf = vdynformat (fmt, v, 256); + va_end (v); + + append (buf); + delete[] buf; +} + +str str::format (...) { + va_list va; + char* buf; + + va_start (va, this); + buf = vdynformat (text, va, 256); + va_end (va); + + str val = buf; + delete[] buf; + return val; +} + +// ============================================================================ +char* str::chars () { + return text; +} + +// ============================================================================ +int str::first (const char* c, unsigned int a) { + unsigned int r = 0; + unsigned int index = 0; + for (; a < alloclen; a++) { + if (text[a] == c[r]) { + if (r == 0) + index = a; + + r++; + if (r == strlen (c)) + return index; + } else { + if (r != 0) { + // If the string sequence broke at this point, we need to + // check this character again, for a new sequence just + // might start right here. + a--; + } + + r = 0; + } + } + + return -1; +} + +// ============================================================================ +int str::last (const char* c, int a) { + if (a == -1) + a = len(); + + int max = strlen (c)-1; + + int r = max; + for (; a >= 0; a--) { + if (text[a] == c[r]) { + r--; + if (r == -1) + return a; + } else { + if (r != max) + a++; + + r = max; + } + } + + return -1; +} + +// ============================================================================ +str str::substr (unsigned int a, unsigned int b) { + if (a > len()) a = len(); + if (b > len()) b = len(); + + if (b == a) + return ""; + + if (b < a) { + printf ("str::substring:: indices %u and %u given, should be the other way around, swapping..\n", a, b); + + // Swap the variables + unsigned int c = a; + a = b; + b = c; + } + + char* s = new char[b - a + 1]; + strncpy (s, text + a, b - a); + s[b - a] = '\0'; + + str other = s; + delete[] s; + return other; +} + +// ============================================================================ +void str::remove (unsigned int idx, unsigned int dellen) { + str s1 = substr (0, idx); + str s2 = substr (idx + dellen, -1); + + clear(); + + append (s1); + append (s2); +} + +// ============================================================================ +str str::trim (int dellen) { + if (dellen > 0) + return substr (0, len() - dellen); + return substr (-dellen, len()); +} + +// ============================================================================ +void str::replace (const char* o, const char* n, unsigned int a) { + for (int idx; (idx = first (o, a)) != -1;) { + str s1 = substr (0, idx); + str s2 = substr (idx + strlen (o), len()); + + clear(); + + append (s1); + append (n); + append (s2); + } +} + +// ============================================================================ +/* +void str::strip (char c) { + strip ({c}); +} + +void str::strip (std::initializer_list unwanted) { + str cache = text; + uint oldlen = len(); + + char* buf = new char[oldlen]; + char* bufptr = buf; + for (uint i = 0; i < oldlen; i++) { + bool valid = true; + for (const char* j = unwanted.begin(); j < unwanted.end() && valid; j++) + if (text[i] == *j) + valid = false; + + if (valid) + *bufptr++ = text[i]; + } + + *bufptr = '\0'; + assert (bufptr <= buf + oldlen); + + clear(); + append (buf); + + delete[] buf; +} +*/ + +void str::insert (char* c, unsigned int pos) { + str s1 = substr (0, pos); + str s2 = substr (pos, len()); + + clear(); + append (s1); + append (c); + append (s2); +} + +str str::reverse () { + char* buf = new char[len() + 1]; + + for (uint i = 0; i < len(); i++) + buf[i] = text[len() - i - 1]; + buf[len()] = '\0'; + + str other = buf; + delete[] buf; + return other; +} + +str str::repeat (int n) { + assert (n >= 0); + + str other; + for (int i = 0; i < n; i++) + other += text; + return other; +} + +// ============================================================================ +bool str::isnumber () { + ITERATE_STRING (u) { + // Minus sign as the first character is allowed for negatives + if (!u && text[u] == '-') + continue; + + if (text[u] < '0' || text[u] > '9') + return false; + } + return true; +} + +// ============================================================================ +bool str::isword () { + ITERATE_STRING (u) { + // lowercase letters + if (text[u] >= 'a' || text[u] <= 'z') + continue; + + // uppercase letters + if (text[u] >= 'A' || text[u] <= 'Z') + continue; + + return false; + } + return true; +} + +int str::instanceof (const char* c, uint n) { + unsigned int r = 0; + unsigned int index = 0; + unsigned int x = 0; + for (uint a = 0; a < alloclen; a++) { + if (text[a] == c[r]) { + if (r == 0) + index = a; + + r++; + if (r == strlen (c)) { + if (x++ == n) + return index; + r = 0; + } + } else { + if (r != 0) + a--; + r = 0; + } + } + + return -1; +} + +// ============================================================================ +int str::compare (const char* c) { + return strcmp (text, c); +} + +int str::compare (str c) { + return compare (c.chars()); +} + +int str::icompare (const char* c) { + return icompare (str ((char*)c)); +} + +int str::icompare (str b) { + return strcmp (tolower().chars(), b.tolower().chars()); +} + +// ============================================================================ +str str::tolower () { + str n = text; + + for (uint u = 0; u < len(); u++) { + if (n[u] >= 'A' && n[u] < 'Z') + n.text[u] += ('a' - 'A'); + } + + return n; +} + +// ============================================================================ +str str::toupper () { + str n = text; + + for (uint u = 0; u < len(); u++) { + if (n[u] >= 'a' && n[u] < 'z') + n.text[u] -= ('a' - 'A'); + } + + return n; +} + +// ============================================================================ +unsigned str::count (char c) { + unsigned n = 0; + ITERATE_STRING (u) + if (text[u] == c) + n++; + return n; +} + +unsigned str::count (char* c) { + unsigned int r = 0; + unsigned int tmp = 0; + ITERATE_STRING (u) { + if (text[u] == c[r]) { + r++; + if (r == strlen (c)) { + r = 0; + tmp++; + } + } else { + if (r != 0) + u--; + r = 0; + } + } + + return tmp; +} + +// ============================================================================ +std::vector str::split (str del) { + std::vector res; + unsigned int a = 0; + + // Find all separators and store the text left to them. + while (1) { + int b = first (del, a); + + if (b == -1) + break; + + res.push_back (substr (a, b)); + a = b + strlen (del); + } + + // Add the string at the right of the last separator + res.push_back (substr (a, len())); + return res; +} + +std::vector str::operator/ (str splitstring) {return split(splitstring);} +std::vector str::operator/ (char* splitstring) {return split(splitstring);} +std::vector str::operator/ (const char* splitstring) {return split(splitstring);} \ No newline at end of file diff -r 000000000000 -r c51cce84a9ac str.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/str.h Fri Mar 15 20:11:18 2013 +0200 @@ -0,0 +1,259 @@ +#ifndef __STR_H__ +#define __STR_H__ + +#include +#include +#include +// #include +#include + +#ifdef QT_VERSION + #include +#endif // QT_VERSION + +char* vdynformat (const char* csFormat, va_list vArgs, long int lSize); + +// Dynamic string object, allocates memory when needed and +// features a good bunch of manipulation methods +class str { +private: + // The actual message + char* text; + + // Where will append() place new characters? + unsigned short curs; + + // Allocated length + unsigned short alloclen; + + // Resize the text buffer to len characters + void resize (unsigned int len); + +public: + // ====================================================================== + str (); + str (const char* c); + str (char c); + ~str (); + + static str mkfmt (const char* fmt, ...) { + va_list va; + char* buf; + + va_start (va, fmt); + buf = vdynformat (fmt, va, 256); + va_end (va); + + str val = buf; + delete[] buf; + return val; + } + + // ====================================================================== + // METHODS + + // Empty the string + void clear (); + + // Length of the string + size_t len () { + return strlen (text); + } + + // The char* form of the string + char* chars (); + + // Dumps the character table of the string + void dump (); + + // Appends text to the string + void append (char c); + void append (const char* c); + void append (str c); + + // Formats text to the string. + void format (const char* fmt, ...); + str format (...); + + // Appends formatted text to the string. + void appendformat (const char* c, ...); + + // Returns the first occurrence of c in the string, optionally starting + // from a certain position rather than the start. + int first (const char* c, unsigned int a = 0); + + // Returns the last occurrence of c in the string, optionally starting + // from a certain position rather than the end. + int last (const char* c, int a = -1); + + // Returns a substring of the string, from a to b. + str substr (unsigned int a, unsigned int b); + + // Replace a substring with another substring. + void replace (const char* o, const char* n, unsigned int a = 0); + + // Removes a given index from the string, optionally more characters than just 1. + void remove (unsigned int idx, unsigned int dellen=1); + + str trim (int dellen); + + // Inserts a substring into a certain position. + void insert (char* c, unsigned int pos); + + // Reverses the string. + str reverse (); + + // Repeats the string a given amount of times. + str repeat (int n); + + // Is the string a number? + bool isnumber (); + + // Is the string a word, i.e consists only of alphabetic letters? + bool isword (); + + // Convert string to lower case + str tolower (); + + // Convert string to upper case + str toupper (); + + // Compare this string with another + int compare (const char* c); + int compare (str c); + int icompare (str c); + int icompare (const char* c); + + // Counts the amount of substrings in the string + unsigned int count (char* s); + unsigned int count (char s); + + // Counts where the given substring is seen for the nth time + int instanceof (const char* s, unsigned n); + + char subscript (uint pos) { + return operator[] (pos); + } + + std::vector split (str del); + + /* + void strip (char c); + void strip (std::initializer_list unwanted); + */ + + // ====================================================================== + // OPERATORS + str operator+ (str& c) { + append (c); + return *this; + } + + str& operator+= (char c) { + append (c); + return *this; + } + + str& operator+= (const char* c) { + append (c); + return *this; + } + + str& operator+= (const str c) { + append (c); + return *this; + } + + str operator* (const int repcount) { + repeat (repcount); + return *this; + } + + str& operator*= (const int repcount) { + str other = repeat (repcount); + clear (); + append (other); + return *this; + } + + str operator- (const int trimcount) { + return trim (trimcount); + } + + str& operator-= (const int trimcount) { + str other = trim (trimcount); + clear (); + append (other); + return *this; + } + + std::vector operator/ (str splitstring); + std::vector operator/ (char* splitstring); + std::vector operator/ (const char* splitstring); + + int operator% (str splitstring) { + return count (splitstring.chars()); + } + + int operator% (char* splitstring) { + return count (splitstring); + } + + int operator% (const char* splitstring) { + return count (str (splitstring).chars()); + } + + str operator+ () { + return toupper (); + } + + str operator- () { + return tolower (); + } + + str operator! () { + return reverse (); + } + + size_t operator~ () { + return len (); + } + +#define DEFINE_OPERATOR_TYPE(OPER, TYPE) \ + bool operator OPER (TYPE other) {return compare(other) OPER 0;} +#define DEFINE_OPERATOR(OPER) \ + DEFINE_OPERATOR_TYPE (OPER, str) \ + DEFINE_OPERATOR_TYPE (OPER, char*) \ + DEFINE_OPERATOR_TYPE (OPER, const char*) + + DEFINE_OPERATOR (==) + DEFINE_OPERATOR (!=) + DEFINE_OPERATOR (>) + DEFINE_OPERATOR (<) + DEFINE_OPERATOR (>=) + DEFINE_OPERATOR (<=) + + char& operator[] (int pos) { + return text[pos]; + } + + operator char* () const { + return text; + } + +#ifdef QT_VERSION + operator QString () const { + return text; + } +#endif // QT_VERSION + + operator int () const { + return atoi (text); + } + + operator uint () const { + return operator int(); + } +}; + +#endif // __STR_H__