# HG changeset patch # User John Tsiombikas # Date 1399337831 -10800 # Node ID 9862541fdcf582f69cca6400dc7f02da97054b57 # Parent 36e39632db75c1d65cda3e08f0988d49c8dc575b - build qt goatview on linux - fixed line endings in a bunch of files diff -r 36e39632db75 -r 9862541fdcf5 exporters/maxgoat/resource.h Binary file exporters/maxgoat/resource.h has changed diff -r 36e39632db75 -r 9862541fdcf5 exporters/maxgoat/src/config.h --- a/exporters/maxgoat/src/config.h Tue May 06 03:31:35 2014 +0300 +++ b/exporters/maxgoat/src/config.h Tue May 06 03:57:11 2014 +0300 @@ -1,7 +1,7 @@ -#ifndef CONFIG_H_ -#define CONFIG_H_ - -#define VER_MAJOR 0 -#define VER_MINOR 1 - -#endif /* CONFIG_H_ */ \ No newline at end of file +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#define VER_MAJOR 0 +#define VER_MINOR 1 + +#endif /* CONFIG_H_ */ diff -r 36e39632db75 -r 9862541fdcf5 exporters/maxgoat/src/logger.cc --- a/exporters/maxgoat/src/logger.cc Tue May 06 03:31:35 2014 +0300 +++ b/exporters/maxgoat/src/logger.cc Tue May 06 03:57:11 2014 +0300 @@ -1,31 +1,31 @@ -#include -#include -#include "logger.h" - -static FILE *logfile; - -bool maxlog_open(const char *fname) -{ - if(!(logfile = fopen(fname, "wb"))) { - return false; - } - setvbuf(logfile, 0, _IONBF, 0); - return true; -} - -void maxlog_close() -{ - if(logfile) { - fclose(logfile); - } -} - -void maxlog(const char *fmt, ...) -{ - if(!logfile) return; - - va_list ap; - va_start(ap, fmt); - vfprintf(logfile, fmt, ap); - va_end(ap); -} \ No newline at end of file +#include +#include +#include "logger.h" + +static FILE *logfile; + +bool maxlog_open(const char *fname) +{ + if(!(logfile = fopen(fname, "wb"))) { + return false; + } + setvbuf(logfile, 0, _IONBF, 0); + return true; +} + +void maxlog_close() +{ + if(logfile) { + fclose(logfile); + } +} + +void maxlog(const char *fmt, ...) +{ + if(!logfile) return; + + va_list ap; + va_start(ap, fmt); + vfprintf(logfile, fmt, ap); + va_end(ap); +} diff -r 36e39632db75 -r 9862541fdcf5 exporters/maxgoat/src/logger.h --- a/exporters/maxgoat/src/logger.h Tue May 06 03:31:35 2014 +0300 +++ b/exporters/maxgoat/src/logger.h Tue May 06 03:57:11 2014 +0300 @@ -1,8 +1,8 @@ -#ifndef MAXLOGGER_H_ -#define MAXLOGGER_H_ - -bool maxlog_open(const char *fname); -void maxlog_close(); -void maxlog(const char *fmt, ...); - -#endif /* MAXLOGGER_H_ */ \ No newline at end of file +#ifndef MAXLOGGER_H_ +#define MAXLOGGER_H_ + +bool maxlog_open(const char *fname); +void maxlog_close(); +void maxlog(const char *fmt, ...); + +#endif /* MAXLOGGER_H_ */ diff -r 36e39632db75 -r 9862541fdcf5 exporters/maxgoat/src/maxgoat.cc --- a/exporters/maxgoat/src/maxgoat.cc Tue May 06 03:31:35 2014 +0300 +++ b/exporters/maxgoat/src/maxgoat.cc Tue May 06 03:57:11 2014 +0300 @@ -1,892 +1,892 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "max.h" -#include "impexp.h" // SceneExport -#include "iparamb2.h" // ClassDesc2 -#include "plugapi.h" -#include "IGame.h" -#include "IGameExport.h" -#include "IGameControl.h" -#include "IConversionmanager.h" -#include "goat3d.h" -#include "config.h" -#include "logger.h" -#include "resource.h" - - -#pragma comment (lib, "core.lib") -#pragma comment (lib, "geom.lib") -#pragma comment (lib, "gfx.lib") -#pragma comment (lib, "mesh.lib") -#pragma comment (lib, "maxutil.lib") -#pragma comment (lib, "maxscrpt.lib") -#pragma comment (lib, "paramblk2.lib") -#pragma comment (lib, "msxml2.lib") -#pragma comment (lib, "igame.lib") -#pragma comment (lib, "comctl32.lib") - - -#define COPYRIGHT \ - L"Copyright 2014 (C) John Tsiombikas - GNU General Public License v3, see COPYING for details." -#define VERSION(major, minor) \ - ((major) * 100 + ((minor) < 10 ? (minor) * 10 : (minor))) - -static INT_PTR CALLBACK scene_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam); -static INT_PTR CALLBACK anim_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam); -static void get_position_keys(IGameControl *ctrl, goat3d_node *node); -static void get_xyz_position_keys(IGameControl *ctrl, goat3d_node *node); -static void get_rotation_keys(IGameControl *ctrl, goat3d_node *node); -static void get_euler_keys(IGameControl *ctrl, goat3d_node *node); -static void get_scaling_keys(IGameControl *ctrl, goat3d_node *node); -static const char *max_string(const MCHAR *wstr); - -HINSTANCE hinst; - -class GoatExporter : public SceneExport { -private: - std::map mtlmap; - std::map nodemap; - -public: - IGameScene *igame; - - int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); - - void process_materials(goat3d *goat); - - void process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode); - - void process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj); - void process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj); - void process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj); - - - int ExtCount() { return 1; } - const TCHAR *Ext(int n) { return L"goatsce"; } - const TCHAR *LongDesc() { return L"Goat3D scene file"; } - const TCHAR *ShortDesc() { return L"Goat3D"; } - const TCHAR *AuthorName() { return L"John Tsiombikas"; } - const TCHAR *CopyrightMessage() { return COPYRIGHT; } - const TCHAR *OtherMessage1() { return L"other1"; } - const TCHAR *OtherMessage2() { return L"other2"; } - unsigned int Version() { return VERSION(VER_MAJOR, VER_MINOR); } - void ShowAbout(HWND win) { MessageBoxA(win, "Goat3D exporter plugin", "About this plugin", 0); } -}; - -class GoatAnimExporter : public GoatExporter { -private: -public: - int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); - - const TCHAR *Ext(int n) { return L"goatanm"; } - const TCHAR *LongDesc() { return L"Goat3D animation file"; } -}; - - -// ---- GoatExporter implementation ---- - -int GoatExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, - BOOL non_interactive, DWORD opt) -{ - if(!DialogBox(hinst, MAKEINTRESOURCE(IDD_GOAT_SCE), 0, scene_gui_handler)) { - return IMPEXP_CANCEL; - } - - mtlmap.clear(); - nodemap.clear(); - - char fname[512]; - wcstombs(fname, name, sizeof fname - 1); - for(int i=0; fname[i]; i++) { - fname[i] = tolower(fname[i]); - } - char *basename = (char*)alloca(strlen(fname) + 1); - strcpy(basename, fname); - char *suffix = strrchr(basename, '.'); - if(suffix) *suffix = 0; - - maxlog("Exporting Goat3D Scene (text) file: %s\n", fname); - if(!(igame = GetIGameInterface())) { - maxlog("failed to get the igame interface\n"); - return IMPEXP_FAIL; - } - IGameConversionManager *cm = GetConversionManager(); - cm->SetCoordSystem(IGameConversionManager::IGAME_OGL); - igame->InitialiseIGame(); - - goat3d *goat = goat3d_create(); - goat3d_set_name(goat, basename); - - process_materials(goat); - - // process all nodes - for(int i=0; iGetTopLevelNodeCount(); i++) { - IGameNode *node = igame->GetTopLevelNode(i); - process_node(goat, 0, node); - } - - if(goat3d_save(goat, fname) == -1) { - goat3d_free(goat); - return IMPEXP_FAIL; - } - - goat3d_free(goat); - return IMPEXP_SUCCESS; -} - -void GoatExporter::process_materials(goat3d *goat) -{ - IGameProperty *prop; - - int num_mtl = igame->GetRootMaterialCount(); - for(int i=0; iGetRootMaterial(i); - if(maxmtl) { - goat3d_material *mtl = goat3d_create_mtl(); - - const char *name = max_string(maxmtl->GetMaterialName()); - if(name) { - goat3d_set_mtl_name(mtl, name); - } - - // diffuse - if((prop = maxmtl->GetDiffuseData())) { - Point3 diffuse(1, 1, 1); - prop->GetPropertyValue(diffuse); - goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_DIFFUSE, diffuse[0], - diffuse[1], diffuse[2]); - } - // specular - if((prop = maxmtl->GetSpecularData())) { - Point3 specular(0, 0, 0); - prop->GetPropertyValue(specular); - - float sstr = 1.0; - if((prop = maxmtl->GetSpecularLevelData())) { - prop->GetPropertyValue(sstr); - } - goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_SPECULAR, specular[0] * sstr, - specular[1] * sstr, specular[2] * sstr); - } - // shininess - if((prop = maxmtl->GetGlossinessData())) { - float shin; - prop->GetPropertyValue(shin); - goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_SHININESS, shin * 100.0); - } - - // textures - for(int j=0; jGetNumberOfTextureMaps(); j++) { - IGameTextureMap *tex = maxmtl->GetIGameTextureMap(j); - - const char *fname = max_string(tex->GetBitmapFileName()); - if(!fname) { - continue; - } - - int slot = tex->GetStdMapSlot(); - switch(slot) { - case ID_DI: // diffuse - goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_DIFFUSE, fname); - break; - - case ID_SP: - case ID_SS: - goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SPECULAR, fname); - break; - - case ID_SH: - goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SHININESS, fname); - break; - - case ID_BU: - goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_NORMAL, fname); - break; - - case ID_RL: - goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_REFLECTION, fname); - break; - - case ID_RR: - goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, fname); - break; - - default: - break; - } - } - - goat3d_add_mtl(goat, mtl); - mtlmap[maxmtl] = mtl; - } - } -} - -void GoatExporter::process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode) -{ - goat3d_node *node = goat3d_create_node(); - goat3d_add_node(goat, node); - - if(parent) { - goat3d_add_node_child(parent, node); - } - - const char *name = max_string(maxnode->GetName()); - if(name) { - goat3d_set_node_name(node, name); - } - - IGameObject *maxobj = maxnode->GetIGameObject(); - IGameObject::ObjectTypes type = maxobj->GetIGameType(); - - switch(type) { - case IGameObject::IGAME_MESH: - { - goat3d_mesh *mesh = goat3d_create_mesh(); - if(name) goat3d_set_mesh_name(mesh, name); - goat3d_set_node_object(node, GOAT3D_NODE_MESH, mesh); - - // get the node material and assign it to the mesh - IGameMaterial *maxmtl = maxnode->GetNodeMaterial(); - goat3d_material *mtl = mtlmap[maxmtl]; - if(mtl) { - goat3d_set_mesh_mtl(mesh, mtl); - } - - process_mesh(goat, mesh, maxobj); - goat3d_add_mesh(goat, mesh); - } - break; - - case IGameObject::IGAME_LIGHT: - { - goat3d_light *light = goat3d_create_light(); - //if(name) goat3d_set_light_name(light, name); - goat3d_set_node_object(node, GOAT3D_NODE_LIGHT, light); - - process_light(goat, light, maxobj); - goat3d_add_light(goat, light); - } - break; - - case IGameObject::IGAME_CAMERA: - { - goat3d_camera *cam = goat3d_create_camera(); - //if(name) goat3d_set_camera_name(camera, name); - goat3d_set_node_object(node, GOAT3D_NODE_CAMERA, cam); - - process_camera(goat, cam, maxobj); - goat3d_add_camera(goat, cam); - } - break; - - default: - // otherwise don't assign an object, essentially treating it as a null node - break; - } - - // grab the animation data - if(!dynamic_cast(this)) { - // no animation, just get the static PRS - GMatrix maxmatrix = maxnode->GetObjectTM(); - Point3 trans = maxmatrix.Translation(); - Quat rot = maxmatrix.Rotation(); - Point3 scale = maxmatrix.Scaling(); - - goat3d_set_node_position(node, trans.x, trans.y, trans.z, 0); - goat3d_set_node_rotation(node, rot.x, rot.y, rot.z, rot.w, 0); - goat3d_set_node_scaling(node, scale.x, scale.y, scale.z, 0); - - } else { - // exporting animations (if available) - // TODO sample keys if requested - IGameControl *ctrl = maxnode->GetIGameControl(); - if(ctrl) { - if(ctrl->IsAnimated(IGAME_POS) || ctrl->IsAnimated(IGAME_POS_X) || - ctrl->IsAnimated(IGAME_POS_Y) || ctrl->IsAnimated(IGAME_POS_Z)) { - get_position_keys(ctrl, node); - } - if(ctrl->IsAnimated(IGAME_ROT) || ctrl->IsAnimated(IGAME_EULER_X) || - ctrl->IsAnimated(IGAME_EULER_Y) || ctrl->IsAnimated(IGAME_EULER_Z)) { - get_rotation_keys(ctrl, node); - } - if(ctrl->IsAnimated(IGAME_SCALE)) { - get_scaling_keys(ctrl, node); - } - } else { - maxlog("%s: failed to get IGameControl for node: %s\n", __FUNCTION__, name); - } - } - - for(int i=0; iGetChildCount(); i++) { - process_node(goat, node, maxnode->GetNodeChild(i)); - } -} - -#define KEY_TIME(key) ((long)(TicksToSec(key.t) * 1000.0)) - -static void get_position_keys(IGameControl *ctrl, goat3d_node *node) -{ - const char *nodename = goat3d_get_node_name(node); - IGameKeyTab keys; - - if(ctrl->GetLinearKeys(keys, IGAME_POS)) { - maxlog("node %s: getting %d linear position keys\n", nodename, keys.Count()); - for(int i=0; iGetBezierKeys(keys, IGAME_POS)) { - maxlog("node %s: getting %d bezier position keys\n", nodename, keys.Count()); - for(int i=0; iGetTCBKeys(keys, IGAME_POS)) { - maxlog("node %s: getting %d tcb position keys\n", nodename, keys.Count()); - for(int i=0; i pos; - - for(int i=0; i<3; i++) { - if(ctrl->GetLinearKeys(keys, postype[i])) { - maxlog("node %s: getting %d linear position %c keys\n", nodename, keys.Count(), "xyz"[i]); - for(int j=0; jGetBezierKeys(keys, postype[i])) { - maxlog("node %s: getting %d bezier position %c keys\n", nodename, keys.Count(), "xyz"[i]); - for(int j=0; jGetTCBKeys(keys, postype[i])) { - maxlog("node %s: getting %d tcb position %c keys\n", nodename, keys.Count(), "xyz"[i]); - for(int j=0; j::iterator it = pos.begin(); - while(it != pos.end()) { - Point3 p = it->second; - goat3d_set_node_position(node, p.x, p.y, p.z, it->first); - ++it; - } -} - -static void get_rotation_keys(IGameControl *ctrl, goat3d_node *node) -{ - const char *nodename = goat3d_get_node_name(node); - IGameKeyTab rkeys; - - if(ctrl->GetLinearKeys(rkeys, IGAME_ROT)) { - maxlog("node %s: getting %d linear rotation keys\n", nodename, rkeys.Count()); - for(int i=0; iGetBezierKeys(rkeys, IGAME_ROT)) { - maxlog("node %s: getting %d bezier rotation keys\n", nodename, rkeys.Count()); - for(int i=0; iGetTCBKeys(rkeys, IGAME_ROT)) { - maxlog("node %s: getting %d TCB rotation keys\n", nodename, rkeys.Count()); - for(int i=0; i euler; - - for(int i=0; i<3; i++) { - if(ctrl->GetLinearKeys(keys, eulertype[i])) { - maxlog("node %s: getting %d linear euler %c keys\n", nodename, keys.Count(), "xyz"[i]); - for(int j=0; jGetBezierKeys(keys, eulertype[i])) { - maxlog("node %s: getting %d bezier euler %c keys\n", nodename, keys.Count(), "xyz"[i]); - for(int j=0; jGetTCBKeys(keys, eulertype[i])) { - maxlog("node %s: getting %d tcb euler %c keys\n", nodename, keys.Count(), "xyz"[i]); - for(int j=0; jGetEulerOrder(); - std::map::iterator it = euler.begin(); - while(it != euler.end()) { - Quat q; - EulerToQuat(it->second, q, order); - goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, it->first); - ++it; - } -} - -static void get_scaling_keys(IGameControl *ctrl, goat3d_node *node) -{ - const char *nodename = goat3d_get_node_name(node); - IGameKeyTab keys; - - // XXX the way I'm using the ScaleValue is wrong, but fuck it... - - if(ctrl->GetLinearKeys(keys, IGAME_SCALE)) { - maxlog("node %s: getting %d linear scaling keys\n", nodename, keys.Count()); - for(int i=0; iGetBezierKeys(keys, IGAME_SCALE)) { - maxlog("node %s: getting %d bezier scaling keys\n", nodename, keys.Count()); - for(int i=0; iGetTCBKeys(keys, IGAME_SCALE)) { - maxlog("node %s: getting %d tcb scaling keys\n", nodename, keys.Count()); - for(int i=0; iGetTopLevelNodeCount(); - for(int i=0; iGetTopLevelNode(i), &t0, &t1)) { - if(t0 < tmin) tmin = t0; - if(t1 > tmax) tmax = t1; - } - } - - if(tmin != LONG_MAX) { - *tstart = tmin; - *tend = tmax; - return true; - } - return false; -} - -static bool get_anim_bounds(IGameNode *node, long *tstart, long *tend) -{ - long tmin = LONG_MAX; - long tmax = LONG_MIN; - - get_node_anim_bounds(node, &tmin, &tmax); - - int num_children = node->GetChildCount(); - for(int i=0; iGetNodeChild(i), &t0, &t1)) { - if(t0 < tmin) tmin = t0; - if(t1 > tmax) tmax = t1; - } - } - - if(tmin != LONG_MAX) { - *tstart = tmin; - *tend = tmax; - return true; - } - return false; -} - -static bool get_node_anim_bounds(IGameNode *node, long *tstart, long *tend) -{ - static const IGameControlType ctypes[] = { - IGAME_POS, IGAME_POS_X, IGAME_POS_Y, IGAME_POS_Z, - IGAME_ROT, IGAME_EULER_X, IGAME_EULER_Y, IGAME_EULER_Z, - IGAME_SCALE - }; - - // NOTE: apparently if I don't call GetIGameObject, then GetIGameControl always returns null... - node->GetIGameObject(); - IGameControl *ctrl = node->GetIGameControl(); - if(!ctrl) { - maxlog("%s: failed to get IGameControl for node: %s\n", __FUNCTION__, max_string(node->GetName())); - return false; - } - - IGameKeyTab keys; - long t0, t1; - long tmin = LONG_MAX; - long tmax = LONG_MIN; - - for(int i=0; iGetBezierKeys(keys, ctypes[i]) && keys.Count()) { - t0 = KEY_TIME(keys[0]); - t1 = KEY_TIME(keys[keys.Count() - 1]); - if(t0 < tmin) tmin = t0; - if(t1 > tmax) tmax = t1; - } - if(ctrl->GetLinearKeys(keys, ctypes[i]) && keys.Count()) { - t0 = KEY_TIME(keys[0]); - t1 = KEY_TIME(keys[keys.Count() - 1]); - if(t0 < tmin) tmin = t0; - if(t1 > tmax) tmax = t1; - } - if(ctrl->GetTCBKeys(keys, ctypes[i]) && keys.Count()) { - t0 = KEY_TIME(keys[0]); - t1 = KEY_TIME(keys[keys.Count() - 1]); - if(t0 < tmin) tmin = t0; - if(t1 > tmax) tmax = t1; - } - } - - if(tmin != LONG_MAX) { - *tstart = tmin; - *tend = tmax; - return true; - } - return false; -} - -void GoatExporter::process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj) -{ - IGameMesh *maxmesh = (IGameMesh*)maxobj; - - maxmesh->SetCreateOptimizedNormalList(); // not needed any more according to docs - maxobj->InitializeData(); - - int num_verts = maxmesh->GetNumberOfVerts(); - int num_faces = maxmesh->GetNumberOfFaces(); - //assert(maxmesh->GetNumberOfTexVerts() == num_verts); - - float *vertices = new float[num_verts * 3]; - float *normals = new float[num_verts * 3]; - //float *texcoords = new float[num_verts * 2]; - int *indices = new int[num_faces * 3]; - - for(int i=0; iGetVertex(i, true); - vertices[i * 3] = v.x; - vertices[i * 3 + 1] = v.y; - vertices[i * 3 + 2] = v.z; - } - - for(int i=0; iGetNumberOfNormals(); i++) { - Point3 norm = maxmesh->GetNormal(i); - - int vidx = maxmesh->GetNormalVertexIndex(i); - normals[vidx * 3] = norm.x; - normals[vidx * 3 + 1] = norm.y; - normals[vidx * 3 + 2] = norm.z; - } - - /*for(int i=0; iGetNumberOfTexVerts(); i++) { - Point3 tex = maxmesh->GetTexVertex(i); - - texcoords[i * 2] = tex.x; - texcoords[i * 2 + 1] = tex.y; - }*/ - - // get the faces - for(int i=0; iGetFace(i); - indices[i * 3] = face->vert[0]; - indices[i * 3 + 1] = face->vert[1]; - indices[i * 3 + 2] = face->vert[2]; - // TODO at some point I'll have to split based on normal/texcoord indices - } - - goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_VERTEX, vertices, num_verts); - goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_NORMAL, normals, num_verts); - //goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_TEXCOORD, texcoords, num_verts); - goat3d_set_mesh_faces(mesh, indices, num_faces); - - delete [] vertices; - delete [] normals; - //delete [] texcoords; - delete [] indices; -} - -void GoatExporter::process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj) -{ - // TODO -} - -void GoatExporter::process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj) -{ - // TODO -} - -static INT_PTR CALLBACK scene_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam) -{ - switch(msg) { - case WM_INITDIALOG: - CheckDlgButton(win, IDC_GOAT_NODES, 1); - CheckDlgButton(win, IDC_GOAT_MESHES, 1); - CheckDlgButton(win, IDC_GOAT_LIGHTS, 1); - CheckDlgButton(win, IDC_GOAT_CAMERAS, 1); - break; - - case WM_COMMAND: - switch(LOWORD(wparam)) { - case IDOK: - EndDialog(win, 1); - break; - - case IDCANCEL: - EndDialog(win, 0); - break; - - default: - return 0; - } - break; - - default: - return 0; - } - - return 1; -} - - - -// ---- GoatAnimExporter implementation ---- -static long tstart, tend; - -int GoatAnimExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent, DWORD opt) -{ - if(!(igame = GetIGameInterface())) { - maxlog("failed to get the igame interface\n"); - return IMPEXP_FAIL; - } - IGameConversionManager *cm = GetConversionManager(); - cm->SetCoordSystem(IGameConversionManager::IGAME_OGL); - igame->InitialiseIGame(); - igame->SetStaticFrame(0); - - tstart = tend = 0; - get_anim_bounds(igame, &tstart, &tend); - - if(!DialogBox(hinst, MAKEINTRESOURCE(IDD_GOAT_ANM), 0, anim_gui_handler)) { - igame->ReleaseIGame(); - return IMPEXP_CANCEL; - } - - char fname[512]; - wcstombs(fname, name, sizeof fname - 1); - for(int i=0; fname[i]; i++) { - fname[i] = tolower(fname[i]); - } - - maxlog("Exporting Goat3D Animation (text) file: %s\n", fname); - - goat3d *goat = goat3d_create(); - - // process all nodes - for(int i=0; iGetTopLevelNodeCount(); i++) { - IGameNode *node = igame->GetTopLevelNode(i); - process_node(goat, 0, node); - } - - if(goat3d_save_anim(goat, fname) == -1) { - goat3d_free(goat); - igame->ReleaseIGame(); - return IMPEXP_FAIL; - } - - goat3d_free(goat); - igame->ReleaseIGame(); - return IMPEXP_SUCCESS; -} - -static INT_PTR CALLBACK anim_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam) -{ - switch(msg) { - case WM_INITDIALOG: - { - wchar_t buf[128]; - CheckDlgButton(win, IDC_GOAT_ANM_FULL, BST_CHECKED); - CheckDlgButton(win, IDC_RAD_KEYS_ORIG, BST_CHECKED); - wsprintf(buf, L"%ld", tstart); - SetDlgItemText(win, IDC_EDIT_TSTART, buf); - wsprintf(buf, L"%ld", tend); - SetDlgItemText(win, IDC_EDIT_TEND, buf); - } - break; - - case WM_COMMAND: - switch(LOWORD(wparam)) { - case IDOK: - EndDialog(win, 1); - break; - - case IDCANCEL: - EndDialog(win, 0); - break; - - default: - return 0; - } - break; - - default: - return 0; - } - - return 1; -} - -// ------------------------------------------ - -class GoatClassDesc : public ClassDesc2 { -public: - int IsPublic() { return TRUE; } - void *Create(BOOL loading = FALSE) { return new GoatExporter; } - const TCHAR *ClassName() { return L"GoatExporter"; } - SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } - Class_ID ClassID() { return Class_ID(0x77050f0d, 0x7d4c5ab5); } - const TCHAR *Category() { return L"Mutant Stargoat"; } - - const TCHAR *InternalName() { return L"GoatExporter"; } - HINSTANCE HInstance() { return hinst; } -}; - -class GoatAnimClassDesc : public ClassDesc2 { -public: - int IsPublic() { return TRUE; } - void *Create(BOOL loading = FALSE) { return new GoatAnimExporter; } - const TCHAR *ClassName() { return L"GoatAnimExporter"; } - SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } - Class_ID ClassID() { return Class_ID(0x51b94924, 0x2e0332f3); } - const TCHAR *Category() { return L"Mutant Stargoat"; } - - const TCHAR *InternalName() { return L"GoatAnimExporter"; } - HINSTANCE HInstance() { return hinst; } -}; - -// TODO: make 2 class descriptors, one for goat3d, one for goat3danim -static GoatClassDesc class_desc; -static GoatAnimClassDesc anim_class_desc; - -BOOL WINAPI DllMain(HINSTANCE inst_handle, ULONG reason, void *reserved) -{ - if(reason == DLL_PROCESS_ATTACH) { - hinst = inst_handle; - DisableThreadLibraryCalls(hinst); - } - return TRUE; -} - -extern "C" { - -__declspec(dllexport) const TCHAR *LibDescription() -{ - return L"test exporter"; -} - -__declspec(dllexport) int LibNumberClasses() -{ - return 1; -} - -__declspec(dllexport) ClassDesc *LibClassDesc(int i) -{ - switch(i) { - case 0: - return &class_desc; - case 1: - return &anim_class_desc; - default: - break; - } - return 0; -} - -__declspec(dllexport) ULONG LibVersion() -{ - return Get3DSMAXVersion(); -} - -__declspec(dllexport) int LibInitialize() -{ - static char path[1024]; - - SHGetFolderPathA(0, CSIDL_PERSONAL, 0, 0, path); - strcat(path, "/testexp.log"); - - maxlog_open(path); - return TRUE; -} - -__declspec(dllexport) int LibShutdown() -{ - maxlog_close(); - return TRUE; -} - -} // extern "C" - - -static const char *max_string(const MCHAR *wstr) -{ - if(!wstr) return 0; - static char str[512]; - wcstombs(str, wstr, sizeof str - 1); - return str; -} \ No newline at end of file +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max.h" +#include "impexp.h" // SceneExport +#include "iparamb2.h" // ClassDesc2 +#include "plugapi.h" +#include "IGame.h" +#include "IGameExport.h" +#include "IGameControl.h" +#include "IConversionmanager.h" +#include "goat3d.h" +#include "config.h" +#include "logger.h" +#include "resource.h" + + +#pragma comment (lib, "core.lib") +#pragma comment (lib, "geom.lib") +#pragma comment (lib, "gfx.lib") +#pragma comment (lib, "mesh.lib") +#pragma comment (lib, "maxutil.lib") +#pragma comment (lib, "maxscrpt.lib") +#pragma comment (lib, "paramblk2.lib") +#pragma comment (lib, "msxml2.lib") +#pragma comment (lib, "igame.lib") +#pragma comment (lib, "comctl32.lib") + + +#define COPYRIGHT \ + L"Copyright 2014 (C) John Tsiombikas - GNU General Public License v3, see COPYING for details." +#define VERSION(major, minor) \ + ((major) * 100 + ((minor) < 10 ? (minor) * 10 : (minor))) + +static INT_PTR CALLBACK scene_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam); +static INT_PTR CALLBACK anim_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam); +static void get_position_keys(IGameControl *ctrl, goat3d_node *node); +static void get_xyz_position_keys(IGameControl *ctrl, goat3d_node *node); +static void get_rotation_keys(IGameControl *ctrl, goat3d_node *node); +static void get_euler_keys(IGameControl *ctrl, goat3d_node *node); +static void get_scaling_keys(IGameControl *ctrl, goat3d_node *node); +static const char *max_string(const MCHAR *wstr); + +HINSTANCE hinst; + +class GoatExporter : public SceneExport { +private: + std::map mtlmap; + std::map nodemap; + +public: + IGameScene *igame; + + int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); + + void process_materials(goat3d *goat); + + void process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode); + + void process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj); + void process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj); + void process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj); + + + int ExtCount() { return 1; } + const TCHAR *Ext(int n) { return L"goatsce"; } + const TCHAR *LongDesc() { return L"Goat3D scene file"; } + const TCHAR *ShortDesc() { return L"Goat3D"; } + const TCHAR *AuthorName() { return L"John Tsiombikas"; } + const TCHAR *CopyrightMessage() { return COPYRIGHT; } + const TCHAR *OtherMessage1() { return L"other1"; } + const TCHAR *OtherMessage2() { return L"other2"; } + unsigned int Version() { return VERSION(VER_MAJOR, VER_MINOR); } + void ShowAbout(HWND win) { MessageBoxA(win, "Goat3D exporter plugin", "About this plugin", 0); } +}; + +class GoatAnimExporter : public GoatExporter { +private: +public: + int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); + + const TCHAR *Ext(int n) { return L"goatanm"; } + const TCHAR *LongDesc() { return L"Goat3D animation file"; } +}; + + +// ---- GoatExporter implementation ---- + +int GoatExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, + BOOL non_interactive, DWORD opt) +{ + if(!DialogBox(hinst, MAKEINTRESOURCE(IDD_GOAT_SCE), 0, scene_gui_handler)) { + return IMPEXP_CANCEL; + } + + mtlmap.clear(); + nodemap.clear(); + + char fname[512]; + wcstombs(fname, name, sizeof fname - 1); + for(int i=0; fname[i]; i++) { + fname[i] = tolower(fname[i]); + } + char *basename = (char*)alloca(strlen(fname) + 1); + strcpy(basename, fname); + char *suffix = strrchr(basename, '.'); + if(suffix) *suffix = 0; + + maxlog("Exporting Goat3D Scene (text) file: %s\n", fname); + if(!(igame = GetIGameInterface())) { + maxlog("failed to get the igame interface\n"); + return IMPEXP_FAIL; + } + IGameConversionManager *cm = GetConversionManager(); + cm->SetCoordSystem(IGameConversionManager::IGAME_OGL); + igame->InitialiseIGame(); + + goat3d *goat = goat3d_create(); + goat3d_set_name(goat, basename); + + process_materials(goat); + + // process all nodes + for(int i=0; iGetTopLevelNodeCount(); i++) { + IGameNode *node = igame->GetTopLevelNode(i); + process_node(goat, 0, node); + } + + if(goat3d_save(goat, fname) == -1) { + goat3d_free(goat); + return IMPEXP_FAIL; + } + + goat3d_free(goat); + return IMPEXP_SUCCESS; +} + +void GoatExporter::process_materials(goat3d *goat) +{ + IGameProperty *prop; + + int num_mtl = igame->GetRootMaterialCount(); + for(int i=0; iGetRootMaterial(i); + if(maxmtl) { + goat3d_material *mtl = goat3d_create_mtl(); + + const char *name = max_string(maxmtl->GetMaterialName()); + if(name) { + goat3d_set_mtl_name(mtl, name); + } + + // diffuse + if((prop = maxmtl->GetDiffuseData())) { + Point3 diffuse(1, 1, 1); + prop->GetPropertyValue(diffuse); + goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_DIFFUSE, diffuse[0], + diffuse[1], diffuse[2]); + } + // specular + if((prop = maxmtl->GetSpecularData())) { + Point3 specular(0, 0, 0); + prop->GetPropertyValue(specular); + + float sstr = 1.0; + if((prop = maxmtl->GetSpecularLevelData())) { + prop->GetPropertyValue(sstr); + } + goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_SPECULAR, specular[0] * sstr, + specular[1] * sstr, specular[2] * sstr); + } + // shininess + if((prop = maxmtl->GetGlossinessData())) { + float shin; + prop->GetPropertyValue(shin); + goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_SHININESS, shin * 100.0); + } + + // textures + for(int j=0; jGetNumberOfTextureMaps(); j++) { + IGameTextureMap *tex = maxmtl->GetIGameTextureMap(j); + + const char *fname = max_string(tex->GetBitmapFileName()); + if(!fname) { + continue; + } + + int slot = tex->GetStdMapSlot(); + switch(slot) { + case ID_DI: // diffuse + goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_DIFFUSE, fname); + break; + + case ID_SP: + case ID_SS: + goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SPECULAR, fname); + break; + + case ID_SH: + goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SHININESS, fname); + break; + + case ID_BU: + goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_NORMAL, fname); + break; + + case ID_RL: + goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_REFLECTION, fname); + break; + + case ID_RR: + goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, fname); + break; + + default: + break; + } + } + + goat3d_add_mtl(goat, mtl); + mtlmap[maxmtl] = mtl; + } + } +} + +void GoatExporter::process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode) +{ + goat3d_node *node = goat3d_create_node(); + goat3d_add_node(goat, node); + + if(parent) { + goat3d_add_node_child(parent, node); + } + + const char *name = max_string(maxnode->GetName()); + if(name) { + goat3d_set_node_name(node, name); + } + + IGameObject *maxobj = maxnode->GetIGameObject(); + IGameObject::ObjectTypes type = maxobj->GetIGameType(); + + switch(type) { + case IGameObject::IGAME_MESH: + { + goat3d_mesh *mesh = goat3d_create_mesh(); + if(name) goat3d_set_mesh_name(mesh, name); + goat3d_set_node_object(node, GOAT3D_NODE_MESH, mesh); + + // get the node material and assign it to the mesh + IGameMaterial *maxmtl = maxnode->GetNodeMaterial(); + goat3d_material *mtl = mtlmap[maxmtl]; + if(mtl) { + goat3d_set_mesh_mtl(mesh, mtl); + } + + process_mesh(goat, mesh, maxobj); + goat3d_add_mesh(goat, mesh); + } + break; + + case IGameObject::IGAME_LIGHT: + { + goat3d_light *light = goat3d_create_light(); + //if(name) goat3d_set_light_name(light, name); + goat3d_set_node_object(node, GOAT3D_NODE_LIGHT, light); + + process_light(goat, light, maxobj); + goat3d_add_light(goat, light); + } + break; + + case IGameObject::IGAME_CAMERA: + { + goat3d_camera *cam = goat3d_create_camera(); + //if(name) goat3d_set_camera_name(camera, name); + goat3d_set_node_object(node, GOAT3D_NODE_CAMERA, cam); + + process_camera(goat, cam, maxobj); + goat3d_add_camera(goat, cam); + } + break; + + default: + // otherwise don't assign an object, essentially treating it as a null node + break; + } + + // grab the animation data + if(!dynamic_cast(this)) { + // no animation, just get the static PRS + GMatrix maxmatrix = maxnode->GetObjectTM(); + Point3 trans = maxmatrix.Translation(); + Quat rot = maxmatrix.Rotation(); + Point3 scale = maxmatrix.Scaling(); + + goat3d_set_node_position(node, trans.x, trans.y, trans.z, 0); + goat3d_set_node_rotation(node, rot.x, rot.y, rot.z, rot.w, 0); + goat3d_set_node_scaling(node, scale.x, scale.y, scale.z, 0); + + } else { + // exporting animations (if available) + // TODO sample keys if requested + IGameControl *ctrl = maxnode->GetIGameControl(); + if(ctrl) { + if(ctrl->IsAnimated(IGAME_POS) || ctrl->IsAnimated(IGAME_POS_X) || + ctrl->IsAnimated(IGAME_POS_Y) || ctrl->IsAnimated(IGAME_POS_Z)) { + get_position_keys(ctrl, node); + } + if(ctrl->IsAnimated(IGAME_ROT) || ctrl->IsAnimated(IGAME_EULER_X) || + ctrl->IsAnimated(IGAME_EULER_Y) || ctrl->IsAnimated(IGAME_EULER_Z)) { + get_rotation_keys(ctrl, node); + } + if(ctrl->IsAnimated(IGAME_SCALE)) { + get_scaling_keys(ctrl, node); + } + } else { + maxlog("%s: failed to get IGameControl for node: %s\n", __FUNCTION__, name); + } + } + + for(int i=0; iGetChildCount(); i++) { + process_node(goat, node, maxnode->GetNodeChild(i)); + } +} + +#define KEY_TIME(key) ((long)(TicksToSec(key.t) * 1000.0)) + +static void get_position_keys(IGameControl *ctrl, goat3d_node *node) +{ + const char *nodename = goat3d_get_node_name(node); + IGameKeyTab keys; + + if(ctrl->GetLinearKeys(keys, IGAME_POS)) { + maxlog("node %s: getting %d linear position keys\n", nodename, keys.Count()); + for(int i=0; iGetBezierKeys(keys, IGAME_POS)) { + maxlog("node %s: getting %d bezier position keys\n", nodename, keys.Count()); + for(int i=0; iGetTCBKeys(keys, IGAME_POS)) { + maxlog("node %s: getting %d tcb position keys\n", nodename, keys.Count()); + for(int i=0; i pos; + + for(int i=0; i<3; i++) { + if(ctrl->GetLinearKeys(keys, postype[i])) { + maxlog("node %s: getting %d linear position %c keys\n", nodename, keys.Count(), "xyz"[i]); + for(int j=0; jGetBezierKeys(keys, postype[i])) { + maxlog("node %s: getting %d bezier position %c keys\n", nodename, keys.Count(), "xyz"[i]); + for(int j=0; jGetTCBKeys(keys, postype[i])) { + maxlog("node %s: getting %d tcb position %c keys\n", nodename, keys.Count(), "xyz"[i]); + for(int j=0; j::iterator it = pos.begin(); + while(it != pos.end()) { + Point3 p = it->second; + goat3d_set_node_position(node, p.x, p.y, p.z, it->first); + ++it; + } +} + +static void get_rotation_keys(IGameControl *ctrl, goat3d_node *node) +{ + const char *nodename = goat3d_get_node_name(node); + IGameKeyTab rkeys; + + if(ctrl->GetLinearKeys(rkeys, IGAME_ROT)) { + maxlog("node %s: getting %d linear rotation keys\n", nodename, rkeys.Count()); + for(int i=0; iGetBezierKeys(rkeys, IGAME_ROT)) { + maxlog("node %s: getting %d bezier rotation keys\n", nodename, rkeys.Count()); + for(int i=0; iGetTCBKeys(rkeys, IGAME_ROT)) { + maxlog("node %s: getting %d TCB rotation keys\n", nodename, rkeys.Count()); + for(int i=0; i euler; + + for(int i=0; i<3; i++) { + if(ctrl->GetLinearKeys(keys, eulertype[i])) { + maxlog("node %s: getting %d linear euler %c keys\n", nodename, keys.Count(), "xyz"[i]); + for(int j=0; jGetBezierKeys(keys, eulertype[i])) { + maxlog("node %s: getting %d bezier euler %c keys\n", nodename, keys.Count(), "xyz"[i]); + for(int j=0; jGetTCBKeys(keys, eulertype[i])) { + maxlog("node %s: getting %d tcb euler %c keys\n", nodename, keys.Count(), "xyz"[i]); + for(int j=0; jGetEulerOrder(); + std::map::iterator it = euler.begin(); + while(it != euler.end()) { + Quat q; + EulerToQuat(it->second, q, order); + goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, it->first); + ++it; + } +} + +static void get_scaling_keys(IGameControl *ctrl, goat3d_node *node) +{ + const char *nodename = goat3d_get_node_name(node); + IGameKeyTab keys; + + // XXX the way I'm using the ScaleValue is wrong, but fuck it... + + if(ctrl->GetLinearKeys(keys, IGAME_SCALE)) { + maxlog("node %s: getting %d linear scaling keys\n", nodename, keys.Count()); + for(int i=0; iGetBezierKeys(keys, IGAME_SCALE)) { + maxlog("node %s: getting %d bezier scaling keys\n", nodename, keys.Count()); + for(int i=0; iGetTCBKeys(keys, IGAME_SCALE)) { + maxlog("node %s: getting %d tcb scaling keys\n", nodename, keys.Count()); + for(int i=0; iGetTopLevelNodeCount(); + for(int i=0; iGetTopLevelNode(i), &t0, &t1)) { + if(t0 < tmin) tmin = t0; + if(t1 > tmax) tmax = t1; + } + } + + if(tmin != LONG_MAX) { + *tstart = tmin; + *tend = tmax; + return true; + } + return false; +} + +static bool get_anim_bounds(IGameNode *node, long *tstart, long *tend) +{ + long tmin = LONG_MAX; + long tmax = LONG_MIN; + + get_node_anim_bounds(node, &tmin, &tmax); + + int num_children = node->GetChildCount(); + for(int i=0; iGetNodeChild(i), &t0, &t1)) { + if(t0 < tmin) tmin = t0; + if(t1 > tmax) tmax = t1; + } + } + + if(tmin != LONG_MAX) { + *tstart = tmin; + *tend = tmax; + return true; + } + return false; +} + +static bool get_node_anim_bounds(IGameNode *node, long *tstart, long *tend) +{ + static const IGameControlType ctypes[] = { + IGAME_POS, IGAME_POS_X, IGAME_POS_Y, IGAME_POS_Z, + IGAME_ROT, IGAME_EULER_X, IGAME_EULER_Y, IGAME_EULER_Z, + IGAME_SCALE + }; + + // NOTE: apparently if I don't call GetIGameObject, then GetIGameControl always returns null... + node->GetIGameObject(); + IGameControl *ctrl = node->GetIGameControl(); + if(!ctrl) { + maxlog("%s: failed to get IGameControl for node: %s\n", __FUNCTION__, max_string(node->GetName())); + return false; + } + + IGameKeyTab keys; + long t0, t1; + long tmin = LONG_MAX; + long tmax = LONG_MIN; + + for(int i=0; iGetBezierKeys(keys, ctypes[i]) && keys.Count()) { + t0 = KEY_TIME(keys[0]); + t1 = KEY_TIME(keys[keys.Count() - 1]); + if(t0 < tmin) tmin = t0; + if(t1 > tmax) tmax = t1; + } + if(ctrl->GetLinearKeys(keys, ctypes[i]) && keys.Count()) { + t0 = KEY_TIME(keys[0]); + t1 = KEY_TIME(keys[keys.Count() - 1]); + if(t0 < tmin) tmin = t0; + if(t1 > tmax) tmax = t1; + } + if(ctrl->GetTCBKeys(keys, ctypes[i]) && keys.Count()) { + t0 = KEY_TIME(keys[0]); + t1 = KEY_TIME(keys[keys.Count() - 1]); + if(t0 < tmin) tmin = t0; + if(t1 > tmax) tmax = t1; + } + } + + if(tmin != LONG_MAX) { + *tstart = tmin; + *tend = tmax; + return true; + } + return false; +} + +void GoatExporter::process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj) +{ + IGameMesh *maxmesh = (IGameMesh*)maxobj; + + maxmesh->SetCreateOptimizedNormalList(); // not needed any more according to docs + maxobj->InitializeData(); + + int num_verts = maxmesh->GetNumberOfVerts(); + int num_faces = maxmesh->GetNumberOfFaces(); + //assert(maxmesh->GetNumberOfTexVerts() == num_verts); + + float *vertices = new float[num_verts * 3]; + float *normals = new float[num_verts * 3]; + //float *texcoords = new float[num_verts * 2]; + int *indices = new int[num_faces * 3]; + + for(int i=0; iGetVertex(i, true); + vertices[i * 3] = v.x; + vertices[i * 3 + 1] = v.y; + vertices[i * 3 + 2] = v.z; + } + + for(int i=0; iGetNumberOfNormals(); i++) { + Point3 norm = maxmesh->GetNormal(i); + + int vidx = maxmesh->GetNormalVertexIndex(i); + normals[vidx * 3] = norm.x; + normals[vidx * 3 + 1] = norm.y; + normals[vidx * 3 + 2] = norm.z; + } + + /*for(int i=0; iGetNumberOfTexVerts(); i++) { + Point3 tex = maxmesh->GetTexVertex(i); + + texcoords[i * 2] = tex.x; + texcoords[i * 2 + 1] = tex.y; + }*/ + + // get the faces + for(int i=0; iGetFace(i); + indices[i * 3] = face->vert[0]; + indices[i * 3 + 1] = face->vert[1]; + indices[i * 3 + 2] = face->vert[2]; + // TODO at some point I'll have to split based on normal/texcoord indices + } + + goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_VERTEX, vertices, num_verts); + goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_NORMAL, normals, num_verts); + //goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_TEXCOORD, texcoords, num_verts); + goat3d_set_mesh_faces(mesh, indices, num_faces); + + delete [] vertices; + delete [] normals; + //delete [] texcoords; + delete [] indices; +} + +void GoatExporter::process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj) +{ + // TODO +} + +void GoatExporter::process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj) +{ + // TODO +} + +static INT_PTR CALLBACK scene_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam) +{ + switch(msg) { + case WM_INITDIALOG: + CheckDlgButton(win, IDC_GOAT_NODES, 1); + CheckDlgButton(win, IDC_GOAT_MESHES, 1); + CheckDlgButton(win, IDC_GOAT_LIGHTS, 1); + CheckDlgButton(win, IDC_GOAT_CAMERAS, 1); + break; + + case WM_COMMAND: + switch(LOWORD(wparam)) { + case IDOK: + EndDialog(win, 1); + break; + + case IDCANCEL: + EndDialog(win, 0); + break; + + default: + return 0; + } + break; + + default: + return 0; + } + + return 1; +} + + + +// ---- GoatAnimExporter implementation ---- +static long tstart, tend; + +int GoatAnimExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent, DWORD opt) +{ + if(!(igame = GetIGameInterface())) { + maxlog("failed to get the igame interface\n"); + return IMPEXP_FAIL; + } + IGameConversionManager *cm = GetConversionManager(); + cm->SetCoordSystem(IGameConversionManager::IGAME_OGL); + igame->InitialiseIGame(); + igame->SetStaticFrame(0); + + tstart = tend = 0; + get_anim_bounds(igame, &tstart, &tend); + + if(!DialogBox(hinst, MAKEINTRESOURCE(IDD_GOAT_ANM), 0, anim_gui_handler)) { + igame->ReleaseIGame(); + return IMPEXP_CANCEL; + } + + char fname[512]; + wcstombs(fname, name, sizeof fname - 1); + for(int i=0; fname[i]; i++) { + fname[i] = tolower(fname[i]); + } + + maxlog("Exporting Goat3D Animation (text) file: %s\n", fname); + + goat3d *goat = goat3d_create(); + + // process all nodes + for(int i=0; iGetTopLevelNodeCount(); i++) { + IGameNode *node = igame->GetTopLevelNode(i); + process_node(goat, 0, node); + } + + if(goat3d_save_anim(goat, fname) == -1) { + goat3d_free(goat); + igame->ReleaseIGame(); + return IMPEXP_FAIL; + } + + goat3d_free(goat); + igame->ReleaseIGame(); + return IMPEXP_SUCCESS; +} + +static INT_PTR CALLBACK anim_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam) +{ + switch(msg) { + case WM_INITDIALOG: + { + wchar_t buf[128]; + CheckDlgButton(win, IDC_GOAT_ANM_FULL, BST_CHECKED); + CheckDlgButton(win, IDC_RAD_KEYS_ORIG, BST_CHECKED); + wsprintf(buf, L"%ld", tstart); + SetDlgItemText(win, IDC_EDIT_TSTART, buf); + wsprintf(buf, L"%ld", tend); + SetDlgItemText(win, IDC_EDIT_TEND, buf); + } + break; + + case WM_COMMAND: + switch(LOWORD(wparam)) { + case IDOK: + EndDialog(win, 1); + break; + + case IDCANCEL: + EndDialog(win, 0); + break; + + default: + return 0; + } + break; + + default: + return 0; + } + + return 1; +} + +// ------------------------------------------ + +class GoatClassDesc : public ClassDesc2 { +public: + int IsPublic() { return TRUE; } + void *Create(BOOL loading = FALSE) { return new GoatExporter; } + const TCHAR *ClassName() { return L"GoatExporter"; } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID() { return Class_ID(0x77050f0d, 0x7d4c5ab5); } + const TCHAR *Category() { return L"Mutant Stargoat"; } + + const TCHAR *InternalName() { return L"GoatExporter"; } + HINSTANCE HInstance() { return hinst; } +}; + +class GoatAnimClassDesc : public ClassDesc2 { +public: + int IsPublic() { return TRUE; } + void *Create(BOOL loading = FALSE) { return new GoatAnimExporter; } + const TCHAR *ClassName() { return L"GoatAnimExporter"; } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID() { return Class_ID(0x51b94924, 0x2e0332f3); } + const TCHAR *Category() { return L"Mutant Stargoat"; } + + const TCHAR *InternalName() { return L"GoatAnimExporter"; } + HINSTANCE HInstance() { return hinst; } +}; + +// TODO: make 2 class descriptors, one for goat3d, one for goat3danim +static GoatClassDesc class_desc; +static GoatAnimClassDesc anim_class_desc; + +BOOL WINAPI DllMain(HINSTANCE inst_handle, ULONG reason, void *reserved) +{ + if(reason == DLL_PROCESS_ATTACH) { + hinst = inst_handle; + DisableThreadLibraryCalls(hinst); + } + return TRUE; +} + +extern "C" { + +__declspec(dllexport) const TCHAR *LibDescription() +{ + return L"test exporter"; +} + +__declspec(dllexport) int LibNumberClasses() +{ + return 1; +} + +__declspec(dllexport) ClassDesc *LibClassDesc(int i) +{ + switch(i) { + case 0: + return &class_desc; + case 1: + return &anim_class_desc; + default: + break; + } + return 0; +} + +__declspec(dllexport) ULONG LibVersion() +{ + return Get3DSMAXVersion(); +} + +__declspec(dllexport) int LibInitialize() +{ + static char path[1024]; + + SHGetFolderPathA(0, CSIDL_PERSONAL, 0, 0, path); + strcat(path, "/testexp.log"); + + maxlog_open(path); + return TRUE; +} + +__declspec(dllexport) int LibShutdown() +{ + maxlog_close(); + return TRUE; +} + +} // extern "C" + + +static const char *max_string(const MCHAR *wstr) +{ + if(!wstr) return 0; + static char str[512]; + wcstombs(str, wstr, sizeof str - 1); + return str; +} diff -r 36e39632db75 -r 9862541fdcf5 exporters/maxgoat_stub/src/stub.cc --- a/exporters/maxgoat_stub/src/stub.cc Tue May 06 03:31:35 2014 +0300 +++ b/exporters/maxgoat_stub/src/stub.cc Tue May 06 03:57:11 2014 +0300 @@ -1,274 +1,274 @@ -#include -#include -#include -#include -#include -#include -#include -#include "max.h" -#include "impexp.h" // SceneExport -#include "iparamb2.h" // ClassDesc2 -#include "plugapi.h" -#include "IGame.h" -#include "IGameExport.h" -#include "IConversionmanager.h" - - -#pragma comment (lib, "core.lib") -#pragma comment (lib, "geom.lib") -#pragma comment (lib, "gfx.lib") -#pragma comment (lib, "mesh.lib") -#pragma comment (lib, "maxutil.lib") -#pragma comment (lib, "maxscrpt.lib") -#pragma comment (lib, "paramblk2.lib") -#pragma comment (lib, "msxml2.lib") -#pragma comment (lib, "igame.lib") -#pragma comment (lib, "comctl32.lib") - - -#define VER_MAJOR 1 -#define VER_MINOR 0 -#define VERSION(major, minor) \ - ((major) * 100 + ((minor) < 10 ? (minor) * 10 : (minor))) - -typedef int (*PluginInitFunc)(); -typedef int (*PluginShutdownFunc)(); -typedef ClassDesc *(*PluginClassDescFunc)(int); - -static FILE *logfile; -static HINSTANCE hinst; -static const wchar_t *copyright_str = L"Copyright 2013 (C) John Tsiombikas - GNU General Public License v3, see COPYING for details."; - -class GoatExporterStub : public SceneExport { -public: - IGameScene *igame; - - int ExtCount() { return 1; } - const TCHAR *Ext(int n) { return L"goatsce"; } - const TCHAR *LongDesc() { return L"Goat3D Scene file"; } - const TCHAR *ShortDesc() { return L"Goat3D Scene"; } - const TCHAR *AuthorName() { return L"John Tsiombikas"; } - const TCHAR *CopyrightMessage() { return copyright_str; } - const TCHAR *OtherMessage1() { return L"foo1"; } - const TCHAR *OtherMessage2() { return L"foo2"; } - unsigned int Version() { return VERSION(VER_MAJOR, VER_MINOR); } - void ShowAbout(HWND win) { MessageBoxA(win, "Goat3D exporter", "About this plugin", 0); } - - int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); -}; - -class GoatAnimExporterStub : public SceneExport { -public: - IGameScene *igame; - - int ExtCount() { return 1; } - const TCHAR *Ext(int n) { return L"goatanm"; } - const TCHAR *LongDesc() { return L"Goat3D Animation file"; } - const TCHAR *ShortDesc() { return L"Goat3D Animation"; } - const TCHAR *AuthorName() { return L"John Tsiombikas"; } - const TCHAR *CopyrightMessage() { return copyright_str; } - const TCHAR *OtherMessage1() { return L"bar1"; } - const TCHAR *OtherMessage2() { return L"bar2"; } - unsigned int Version() { return VERSION(VER_MAJOR, VER_MINOR); } - void ShowAbout(HWND win) { MessageBoxA(win, "Goat3D anim exporter", "About this plugin", 0); } - - int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); -}; - -static const char *find_dll_dir() -{ - static char path[MAX_PATH]; - - HMODULE dll; - if(!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - (LPCSTR)find_dll_dir, &dll)) { - return 0; - } - GetModuleFileNameA(dll, path, sizeof path); - - char *last_slash = strrchr(path, '\\'); - if(last_slash && last_slash[1]) { - *last_slash = 0; - } - - return path; -} - -static int do_export(int which, const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL non_int, DWORD opt) -{ - const char *dll_fname = "maxgoat.dll"; - char *dll_path; - HMODULE dll = 0; - PluginInitFunc init; - PluginShutdownFunc shutdown; - PluginClassDescFunc get_class_desc; - ClassDesc *desc; - SceneExport *ex; - int result = IMPEXP_FAIL; - - const char *plugdir = find_dll_dir(); - if(plugdir) { - dll_path = new char[strlen(dll_fname) + strlen(plugdir) + 2]; - sprintf(dll_path, "%s\\%s", plugdir, dll_fname); - } else { - dll_path = new char[strlen(dll_fname) + 1]; - strcpy(dll_path, dll_fname); - } - - if(!(dll = LoadLibraryA(dll_path))) { - fprintf(logfile, "failed to load exporter: %s\n", dll_path); - goto done; - } - - if(!(get_class_desc = (PluginClassDescFunc)GetProcAddress(dll, "LibClassDesc"))) { - fprintf(logfile, "maxgoat.dll is invalid (no LibClassDesc function)\n"); - goto done; - } - - // first initialize the library - if((init = (PluginInitFunc)GetProcAddress(dll, "LibInitialize"))) { - if(!init()) { - fprintf(logfile, "exporter initialization failed!\n"); - goto done; - } - } - - if(!(desc = get_class_desc(which))) { - fprintf(logfile, "failed to get the class descriptor\n"); - goto done; - } - - if(!(ex = (SceneExport*)desc->Create())) { - fprintf(logfile, "failed to create exporter class instance\n"); - goto done; - } - - __try { - result = ex->DoExport(name, eiface, iface); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - fprintf(logfile, "Exception caught!\n"); - delete ex; - goto done; - } - delete ex; - - if((shutdown = (PluginShutdownFunc)GetProcAddress(dll, "LibShutdown"))) { - shutdown(); - } - -done: - delete [] dll_path; - if(dll) { - FreeLibrary(dll); - } - return result; -} - -int GoatExporterStub::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, - BOOL non_interactive, DWORD opt) -{ - return do_export(0, name, eiface, iface, non_interactive, opt); -} - - -int GoatAnimExporterStub::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, - BOOL non_interactive, DWORD opt) -{ - return do_export(1, name, eiface, iface, non_interactive, opt); -} - -// ------------------------------------------ - -class GoatClassDesc : public ClassDesc2 { -public: - int IsPublic() { return TRUE; } - void *Create(BOOL loading = FALSE) { return new GoatExporterStub; } - const TCHAR *ClassName() { return L"GoatExporterStub"; } - SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } - Class_ID ClassID() { return Class_ID(0x2e4e6311, 0x2b154d91); } - const TCHAR *Category() { return L"Mutant Stargoat"; } - - const TCHAR *InternalName() { return L"GoatExporterStub"; } - HINSTANCE HInstance() { return hinst; } -}; - -class GoatAnimClassDesc : public ClassDesc2 { -public: - int IsPublic() { return TRUE; } - void *Create(BOOL loading = FALSE) { return new GoatAnimExporterStub; } - const TCHAR *ClassName() { return L"GoatAnimExporterStub"; } - SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } - Class_ID ClassID() { return Class_ID(0x75054666, 0x45487285); } - const TCHAR *Category() { return L"Mutant Stargoat"; } - - const TCHAR *InternalName() { return L"GoatAnimExporterStub"; } - HINSTANCE HInstance() { return hinst; } -}; - - -static GoatClassDesc class_desc; -static GoatAnimClassDesc anim_class_desc; - -BOOL WINAPI DllMain(HINSTANCE inst_handle, ULONG reason, void *reserved) -{ - if(reason == DLL_PROCESS_ATTACH) { - hinst = inst_handle; - DisableThreadLibraryCalls(hinst); - } - return TRUE; -} - -extern "C" { - -__declspec(dllexport) const TCHAR *LibDescription() -{ - return L"Goat3D exporter stub"; -} - -__declspec(dllexport) int LibNumberClasses() -{ - return 2; -} - -__declspec(dllexport) ClassDesc *LibClassDesc(int i) -{ - switch(i) { - case 0: - return &class_desc; - case 1: - return &anim_class_desc; - default: - break; - } - return 0; -} - -__declspec(dllexport) ULONG LibVersion() -{ - return Get3DSMAXVersion(); -} - -__declspec(dllexport) int LibInitialize() -{ - static char path[1024]; - - SHGetFolderPathA(0, CSIDL_PERSONAL, 0, 0, path); - strcat(path, "/testexpstub.log"); - - if((logfile = fopen(path, "w"))) { - setvbuf(logfile, 0, _IONBF, 0); - } - return TRUE; -} - -__declspec(dllexport) int LibShutdown() -{ - if(logfile) { - fclose(logfile); - logfile = 0; - } - return TRUE; -} - -} // extern "C" \ No newline at end of file +#include +#include +#include +#include +#include +#include +#include +#include "max.h" +#include "impexp.h" // SceneExport +#include "iparamb2.h" // ClassDesc2 +#include "plugapi.h" +#include "IGame.h" +#include "IGameExport.h" +#include "IConversionmanager.h" + + +#pragma comment (lib, "core.lib") +#pragma comment (lib, "geom.lib") +#pragma comment (lib, "gfx.lib") +#pragma comment (lib, "mesh.lib") +#pragma comment (lib, "maxutil.lib") +#pragma comment (lib, "maxscrpt.lib") +#pragma comment (lib, "paramblk2.lib") +#pragma comment (lib, "msxml2.lib") +#pragma comment (lib, "igame.lib") +#pragma comment (lib, "comctl32.lib") + + +#define VER_MAJOR 1 +#define VER_MINOR 0 +#define VERSION(major, minor) \ + ((major) * 100 + ((minor) < 10 ? (minor) * 10 : (minor))) + +typedef int (*PluginInitFunc)(); +typedef int (*PluginShutdownFunc)(); +typedef ClassDesc *(*PluginClassDescFunc)(int); + +static FILE *logfile; +static HINSTANCE hinst; +static const wchar_t *copyright_str = L"Copyright 2013 (C) John Tsiombikas - GNU General Public License v3, see COPYING for details."; + +class GoatExporterStub : public SceneExport { +public: + IGameScene *igame; + + int ExtCount() { return 1; } + const TCHAR *Ext(int n) { return L"goatsce"; } + const TCHAR *LongDesc() { return L"Goat3D Scene file"; } + const TCHAR *ShortDesc() { return L"Goat3D Scene"; } + const TCHAR *AuthorName() { return L"John Tsiombikas"; } + const TCHAR *CopyrightMessage() { return copyright_str; } + const TCHAR *OtherMessage1() { return L"foo1"; } + const TCHAR *OtherMessage2() { return L"foo2"; } + unsigned int Version() { return VERSION(VER_MAJOR, VER_MINOR); } + void ShowAbout(HWND win) { MessageBoxA(win, "Goat3D exporter", "About this plugin", 0); } + + int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); +}; + +class GoatAnimExporterStub : public SceneExport { +public: + IGameScene *igame; + + int ExtCount() { return 1; } + const TCHAR *Ext(int n) { return L"goatanm"; } + const TCHAR *LongDesc() { return L"Goat3D Animation file"; } + const TCHAR *ShortDesc() { return L"Goat3D Animation"; } + const TCHAR *AuthorName() { return L"John Tsiombikas"; } + const TCHAR *CopyrightMessage() { return copyright_str; } + const TCHAR *OtherMessage1() { return L"bar1"; } + const TCHAR *OtherMessage2() { return L"bar2"; } + unsigned int Version() { return VERSION(VER_MAJOR, VER_MINOR); } + void ShowAbout(HWND win) { MessageBoxA(win, "Goat3D anim exporter", "About this plugin", 0); } + + int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); +}; + +static const char *find_dll_dir() +{ + static char path[MAX_PATH]; + + HMODULE dll; + if(!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)find_dll_dir, &dll)) { + return 0; + } + GetModuleFileNameA(dll, path, sizeof path); + + char *last_slash = strrchr(path, '\\'); + if(last_slash && last_slash[1]) { + *last_slash = 0; + } + + return path; +} + +static int do_export(int which, const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL non_int, DWORD opt) +{ + const char *dll_fname = "maxgoat.dll"; + char *dll_path; + HMODULE dll = 0; + PluginInitFunc init; + PluginShutdownFunc shutdown; + PluginClassDescFunc get_class_desc; + ClassDesc *desc; + SceneExport *ex; + int result = IMPEXP_FAIL; + + const char *plugdir = find_dll_dir(); + if(plugdir) { + dll_path = new char[strlen(dll_fname) + strlen(plugdir) + 2]; + sprintf(dll_path, "%s\\%s", plugdir, dll_fname); + } else { + dll_path = new char[strlen(dll_fname) + 1]; + strcpy(dll_path, dll_fname); + } + + if(!(dll = LoadLibraryA(dll_path))) { + fprintf(logfile, "failed to load exporter: %s\n", dll_path); + goto done; + } + + if(!(get_class_desc = (PluginClassDescFunc)GetProcAddress(dll, "LibClassDesc"))) { + fprintf(logfile, "maxgoat.dll is invalid (no LibClassDesc function)\n"); + goto done; + } + + // first initialize the library + if((init = (PluginInitFunc)GetProcAddress(dll, "LibInitialize"))) { + if(!init()) { + fprintf(logfile, "exporter initialization failed!\n"); + goto done; + } + } + + if(!(desc = get_class_desc(which))) { + fprintf(logfile, "failed to get the class descriptor\n"); + goto done; + } + + if(!(ex = (SceneExport*)desc->Create())) { + fprintf(logfile, "failed to create exporter class instance\n"); + goto done; + } + + __try { + result = ex->DoExport(name, eiface, iface); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + fprintf(logfile, "Exception caught!\n"); + delete ex; + goto done; + } + delete ex; + + if((shutdown = (PluginShutdownFunc)GetProcAddress(dll, "LibShutdown"))) { + shutdown(); + } + +done: + delete [] dll_path; + if(dll) { + FreeLibrary(dll); + } + return result; +} + +int GoatExporterStub::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, + BOOL non_interactive, DWORD opt) +{ + return do_export(0, name, eiface, iface, non_interactive, opt); +} + + +int GoatAnimExporterStub::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, + BOOL non_interactive, DWORD opt) +{ + return do_export(1, name, eiface, iface, non_interactive, opt); +} + +// ------------------------------------------ + +class GoatClassDesc : public ClassDesc2 { +public: + int IsPublic() { return TRUE; } + void *Create(BOOL loading = FALSE) { return new GoatExporterStub; } + const TCHAR *ClassName() { return L"GoatExporterStub"; } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID() { return Class_ID(0x2e4e6311, 0x2b154d91); } + const TCHAR *Category() { return L"Mutant Stargoat"; } + + const TCHAR *InternalName() { return L"GoatExporterStub"; } + HINSTANCE HInstance() { return hinst; } +}; + +class GoatAnimClassDesc : public ClassDesc2 { +public: + int IsPublic() { return TRUE; } + void *Create(BOOL loading = FALSE) { return new GoatAnimExporterStub; } + const TCHAR *ClassName() { return L"GoatAnimExporterStub"; } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID() { return Class_ID(0x75054666, 0x45487285); } + const TCHAR *Category() { return L"Mutant Stargoat"; } + + const TCHAR *InternalName() { return L"GoatAnimExporterStub"; } + HINSTANCE HInstance() { return hinst; } +}; + + +static GoatClassDesc class_desc; +static GoatAnimClassDesc anim_class_desc; + +BOOL WINAPI DllMain(HINSTANCE inst_handle, ULONG reason, void *reserved) +{ + if(reason == DLL_PROCESS_ATTACH) { + hinst = inst_handle; + DisableThreadLibraryCalls(hinst); + } + return TRUE; +} + +extern "C" { + +__declspec(dllexport) const TCHAR *LibDescription() +{ + return L"Goat3D exporter stub"; +} + +__declspec(dllexport) int LibNumberClasses() +{ + return 2; +} + +__declspec(dllexport) ClassDesc *LibClassDesc(int i) +{ + switch(i) { + case 0: + return &class_desc; + case 1: + return &anim_class_desc; + default: + break; + } + return 0; +} + +__declspec(dllexport) ULONG LibVersion() +{ + return Get3DSMAXVersion(); +} + +__declspec(dllexport) int LibInitialize() +{ + static char path[1024]; + + SHGetFolderPathA(0, CSIDL_PERSONAL, 0, 0, path); + strcat(path, "/testexpstub.log"); + + if((logfile = fopen(path, "w"))) { + setvbuf(logfile, 0, _IONBF, 0); + } + return TRUE; +} + +__declspec(dllexport) int LibShutdown() +{ + if(logfile) { + fclose(logfile); + logfile = 0; + } + return TRUE; +} + +} // extern "C" diff -r 36e39632db75 -r 9862541fdcf5 goatview/Makefile --- a/goatview/Makefile Tue May 06 03:31:35 2014 +0300 +++ b/goatview/Makefile Tue May 06 03:57:11 2014 +0300 @@ -1,32 +1,41 @@ PREFIX = /usr/local -src = $(wildcard src/*.c) -obj = $(src:.c=.o) +src = $(wildcard src/*.cc) +mochdr = src/goatview.h +mocsrc = $(patsubst src/%.h,src/moc_%.cc,$(mochdr)) +obj = $(sort $(src:.cc=.o) $(mocsrc:.cc=.o)) dep = $(obj:.o=.d) bin = goatview goat_root = .. -CC = clang -CPP = clang -E -CFLAGS = -pedantic -Wall -g -I$(goat_root)/src -LDFLAGS = $(libgoat) $(libgl) +CXXFLAGS = -std=c++11 -pedantic -Wall -g $(pic) -I$(goat_root)/src $(qtinc) +LDFLAGS = $(libgoat) $(libgl) $(qtlib) +MOC = moc + +qtinc = `pkg-config --cflags Qt5Gui Qt5Core Qt5OpenGL` +qtlib = `pkg-config --libs Qt5Gui Qt5Core Qt5OpenGL` ifeq ($(shell uname -s), Darwin) libgl = -framework OpenGL -framework GLUT -lGLEW libgoat = $(goat_root)/libgoat3d.dylib else + pic = -fPIC libgl = -lGL -lGLU -lglut -lGLEW libgoat = $(goat_root)/libgoat3d.so.0.1 endif $(bin): $(obj) $(libgoat) - $(CC) -o $@ $(obj) $(LDFLAGS) + $(CXX) -o $@ $(obj) $(LDFLAGS) + +# rule for running moc on ui header files +moc_%.cc: %.h + $(MOC) -o $@ $< -include $(dep) -%.d: %.c - @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ .PHONY: clean clean: diff -r 36e39632db75 -r 9862541fdcf5 goatview/src/goatview.cc --- a/goatview/src/goatview.cc Tue May 06 03:31:35 2014 +0300 +++ b/goatview/src/goatview.cc Tue May 06 03:57:11 2014 +0300 @@ -1,117 +1,117 @@ -#include "goatview.h" - -GoatView::GoatView() -{ - make_menu(); - make_dock(); - make_center(); - - statusBar(); - - setWindowTitle("GoatView"); -} - -GoatView::~GoatView() -{ -} - -bool GoatView::make_menu() -{ - QMenu *menu_file = menuBar()->addMenu("&File"); - - QAction *act_open_sce = new QAction("&Open Scene", this); - act_open_sce->setShortcuts(QKeySequence::Open); - connect(act_open_sce, &QAction::triggered, this, &GoatView::open_scene); - menu_file->addAction(act_open_sce); - - QAction *act_open_anm = new QAction("Open &Animation", this); - connect(act_open_anm, &QAction::triggered, this, &GoatView::open_anim); - menu_file->addAction(act_open_anm); - - QAction *act_quit = new QAction("&Quit", this); - act_quit->setShortcuts(QKeySequence::Quit); - connect(act_quit, &QAction::triggered, [&](){qApp->quit();}); - menu_file->addAction(act_quit); - return true; -} - -bool GoatView::make_dock() -{ - // ---- side-dock ---- - QWidget *dock_cont = new QWidget; - QVBoxLayout *dock_vbox = new QVBoxLayout; - dock_cont->setLayout(dock_vbox); - - QPushButton *bn_quit = new QPushButton("quit"); - dock_vbox->addWidget(bn_quit); - connect(bn_quit, &QPushButton::clicked, [&](){qApp->quit();}); - - QDockWidget *dock = new QDockWidget("Scene graph", this); - dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); - dock->setWidget(dock_cont); - addDockWidget(Qt::LeftDockWidgetArea, dock); - - // ---- bottom dock ---- - dock_cont = new QWidget; - QHBoxLayout *dock_hbox = new QHBoxLayout; - dock_cont->setLayout(dock_hbox); - - QSlider *slider_time = new QSlider(Qt::Orientation::Horizontal); - slider_time->setDisabled(true); - dock_hbox->addWidget(slider_time); - - dock = new QDockWidget("Animation", this); - dock->setAllowedAreas(Qt::BottomDockWidgetArea); - dock->setWidget(dock_cont); - addDockWidget(Qt::BottomDockWidgetArea, dock); - - return true; -} - -bool GoatView::make_center() -{ - GoatViewport *vport = new GoatViewport; - setCentralWidget(vport); - return true; -} - -void GoatView::open_scene() -{ - statusBar()->showMessage("opening scene..."); -} - -void GoatView::open_anim() -{ - statusBar()->showMessage("opening animation..."); -} - - -// ---- OpenGL viewport ---- -GoatViewport::GoatViewport() - : QGLWidget(QGLFormat(QGL::DepthBuffer)) -{ -} - -GoatViewport::~GoatViewport() -{ -} - -QSize GoatViewport::sizeHint() const -{ - return QSize(800, 600); -} - -void GoatViewport::initializeGL() -{ -} - -void GoatViewport::resizeGL(int xsz, int ysz) -{ - glViewport(0, 0, xsz, ysz); -} - -void GoatViewport::paintGL() -{ - glClearColor(1, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); -} \ No newline at end of file +#include "goatview.h" + +GoatView::GoatView() +{ + make_menu(); + make_dock(); + make_center(); + + statusBar(); + + setWindowTitle("GoatView"); +} + +GoatView::~GoatView() +{ +} + +bool GoatView::make_menu() +{ + QMenu *menu_file = menuBar()->addMenu("&File"); + + QAction *act_open_sce = new QAction("&Open Scene", this); + act_open_sce->setShortcuts(QKeySequence::Open); + connect(act_open_sce, &QAction::triggered, this, &GoatView::open_scene); + menu_file->addAction(act_open_sce); + + QAction *act_open_anm = new QAction("Open &Animation", this); + connect(act_open_anm, &QAction::triggered, this, &GoatView::open_anim); + menu_file->addAction(act_open_anm); + + QAction *act_quit = new QAction("&Quit", this); + act_quit->setShortcuts(QKeySequence::Quit); + connect(act_quit, &QAction::triggered, [&](){qApp->quit();}); + menu_file->addAction(act_quit); + return true; +} + +bool GoatView::make_dock() +{ + // ---- side-dock ---- + QWidget *dock_cont = new QWidget; + QVBoxLayout *dock_vbox = new QVBoxLayout; + dock_cont->setLayout(dock_vbox); + + QPushButton *bn_quit = new QPushButton("quit"); + dock_vbox->addWidget(bn_quit); + connect(bn_quit, &QPushButton::clicked, [&](){qApp->quit();}); + + QDockWidget *dock = new QDockWidget("Scene graph", this); + dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + dock->setWidget(dock_cont); + addDockWidget(Qt::LeftDockWidgetArea, dock); + + // ---- bottom dock ---- + dock_cont = new QWidget; + QHBoxLayout *dock_hbox = new QHBoxLayout; + dock_cont->setLayout(dock_hbox); + + QSlider *slider_time = new QSlider(Qt::Orientation::Horizontal); + slider_time->setDisabled(true); + dock_hbox->addWidget(slider_time); + + dock = new QDockWidget("Animation", this); + dock->setAllowedAreas(Qt::BottomDockWidgetArea); + dock->setWidget(dock_cont); + addDockWidget(Qt::BottomDockWidgetArea, dock); + + return true; +} + +bool GoatView::make_center() +{ + GoatViewport *vport = new GoatViewport; + setCentralWidget(vport); + return true; +} + +void GoatView::open_scene() +{ + statusBar()->showMessage("opening scene..."); +} + +void GoatView::open_anim() +{ + statusBar()->showMessage("opening animation..."); +} + + +// ---- OpenGL viewport ---- +GoatViewport::GoatViewport() + : QGLWidget(QGLFormat(QGL::DepthBuffer)) +{ +} + +GoatViewport::~GoatViewport() +{ +} + +QSize GoatViewport::sizeHint() const +{ + return QSize(800, 600); +} + +void GoatViewport::initializeGL() +{ +} + +void GoatViewport::resizeGL(int xsz, int ysz) +{ + glViewport(0, 0, xsz, ysz); +} + +void GoatViewport::paintGL() +{ + glClearColor(1, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); +} diff -r 36e39632db75 -r 9862541fdcf5 goatview/src/goatview.h --- a/goatview/src/goatview.h Tue May 06 03:31:35 2014 +0300 +++ b/goatview/src/goatview.h Tue May 06 03:57:11 2014 +0300 @@ -1,36 +1,36 @@ -#ifndef GOATVIEW_H_ -#define GOATVIEW_H_ - -#include -#include - -class GoatView : public QMainWindow { - Q_OBJECT -private: - bool make_menu(); - bool make_dock(); - bool make_center(); - -private slots: - void open_scene(); - void open_anim(); - -public: - GoatView(); - ~GoatView(); -}; - -class GoatViewport : public QGLWidget { - Q_OBJECT -public: - GoatViewport(); - ~GoatViewport(); - - QSize sizeHint() const; - - void initializeGL(); - void resizeGL(int xsz, int ysz); - void paintGL(); -}; - -#endif // GOATVIEW_H_ \ No newline at end of file +#ifndef GOATVIEW_H_ +#define GOATVIEW_H_ + +#include +#include + +class GoatView : public QMainWindow { + Q_OBJECT +private: + bool make_menu(); + bool make_dock(); + bool make_center(); + +private slots: + void open_scene(); + void open_anim(); + +public: + GoatView(); + ~GoatView(); +}; + +class GoatViewport : public QGLWidget { + Q_OBJECT +public: + GoatViewport(); + ~GoatViewport(); + + QSize sizeHint() const; + + void initializeGL(); + void resizeGL(int xsz, int ysz); + void paintGL(); +}; + +#endif // GOATVIEW_H_ diff -r 36e39632db75 -r 9862541fdcf5 goatview/src/main.cc --- a/goatview/src/main.cc Tue May 06 03:31:35 2014 +0300 +++ b/goatview/src/main.cc Tue May 06 03:57:11 2014 +0300 @@ -1,12 +1,12 @@ -#include -#include "goatview.h" - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - - GoatView gview; - gview.show(); - - return app.exec(); -} \ No newline at end of file +#include +#include "goatview.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + GoatView gview; + gview.show(); + + return app.exec(); +} diff -r 36e39632db75 -r 9862541fdcf5 libs/tinyxml2/tinyxml2.h --- a/libs/tinyxml2/tinyxml2.h Tue May 06 03:31:35 2014 +0300 +++ b/libs/tinyxml2/tinyxml2.h Tue May 06 03:57:11 2014 +0300 @@ -1,1987 +1,1987 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#ifndef TINYXML2_INCLUDED -#define TINYXML2_INCLUDED - -#if defined(ANDROID_NDK) || defined(__BORLANDC__) -# include -# include -# include -# include -# include -# include -#else -# include -# include -# include -# include -# include -# include -#endif - -/* - TODO: intern strings instead of allocation. -*/ -/* - gcc: - g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe - - Formatting, Artistic Style: - AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h -*/ - -#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) -# ifndef DEBUG -# define DEBUG -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4251) -#endif - -#ifdef _WIN32 -# ifdef TINYXML2_EXPORT -# define TINYXML2_LIB __declspec(dllexport) -# elif defined(TINYXML2_IMPORT) -# define TINYXML2_LIB __declspec(dllimport) -# else -# define TINYXML2_LIB -# endif -#else -# define TINYXML2_LIB -#endif - - -#if defined(DEBUG) -# if defined(_MSC_VER) -# define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak() -# elif defined (ANDROID_NDK) -# include -# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } -# else -# include -# define TIXMLASSERT assert -# endif -# else -# define TIXMLASSERT( x ) {} -#endif - - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) -// Microsoft visual studio, version 2005 and higher. -/*int _snprintf_s( - char *buffer, - size_t sizeOfBuffer, - size_t count, - const char *format [, - argument] ... -);*/ -inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) -{ - va_list va; - va_start( va, format ); - int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - va_end( va ); - return result; -} -#define TIXML_SSCANF sscanf_s -#else -// GCC version 3 and higher -//#warning( "Using sn* functions." ) -#define TIXML_SNPRINTF snprintf -#define TIXML_SSCANF sscanf -#endif - -static const int TIXML2_MAJOR_VERSION = 1; -static const int TIXML2_MINOR_VERSION = 0; -static const int TIXML2_PATCH_VERSION = 11; - -namespace tinyxml2 -{ -class XMLDocument; -class XMLElement; -class XMLAttribute; -class XMLComment; -class XMLText; -class XMLDeclaration; -class XMLUnknown; -class XMLPrinter; - -/* - A class that wraps strings. Normally stores the start and end - pointers into the XML file itself, and will apply normalization - and entity translation if actually read. Can also store (and memory - manage) a traditional char[] -*/ -class StrPair -{ -public: - enum { - NEEDS_ENTITY_PROCESSING = 0x01, - NEEDS_NEWLINE_NORMALIZATION = 0x02, - COLLAPSE_WHITESPACE = 0x04, - - TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_NAME = 0, - ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - COMMENT = NEEDS_NEWLINE_NORMALIZATION - }; - - StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} - ~StrPair(); - - void Set( char* start, char* end, int flags ) { - Reset(); - _start = start; - _end = end; - _flags = flags | NEEDS_FLUSH; - } - - const char* GetStr(); - - bool Empty() const { - return _start == _end; - } - - void SetInternedStr( const char* str ) { - Reset(); - _start = const_cast(str); - } - - void SetStr( const char* str, int flags=0 ); - - char* ParseText( char* in, const char* endTag, int strFlags ); - char* ParseName( char* in ); - -private: - void Reset(); - void CollapseWhitespace(); - - enum { - NEEDS_FLUSH = 0x100, - NEEDS_DELETE = 0x200 - }; - - // After parsing, if *_end != 0, it can be set to zero. - int _flags; - char* _start; - char* _end; -}; - - -/* - A dynamic array of Plain Old Data. Doesn't support constructors, etc. - Has a small initial memory pool, so that low or no usage will not - cause a call to new/delete -*/ -template -class DynArray -{ -public: - DynArray< T, INIT >() { - _mem = _pool; - _allocated = INIT; - _size = 0; - } - - ~DynArray() { - if ( _mem != _pool ) { - delete [] _mem; - } - } - - void Push( T t ) { - EnsureCapacity( _size+1 ); - _mem[_size++] = t; - } - - T* PushArr( int count ) { - EnsureCapacity( _size+count ); - T* ret = &_mem[_size]; - _size += count; - return ret; - } - - T Pop() { - return _mem[--_size]; - } - - void PopArr( int count ) { - TIXMLASSERT( _size >= count ); - _size -= count; - } - - bool Empty() const { - return _size == 0; - } - - T& operator[](int i) { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - const T& operator[](int i) const { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - int Size() const { - return _size; - } - - int Capacity() const { - return _allocated; - } - - const T* Mem() const { - return _mem; - } - - T* Mem() { - return _mem; - } - -private: - void EnsureCapacity( int cap ) { - if ( cap > _allocated ) { - int newAllocated = cap * 2; - T* newMem = new T[newAllocated]; - memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs - if ( _mem != _pool ) { - delete [] _mem; - } - _mem = newMem; - _allocated = newAllocated; - } - } - - T* _mem; - T _pool[INIT]; - int _allocated; // objects allocated - int _size; // number objects in use -}; - - -/* - Parent virtual class of a pool for fast allocation - and deallocation of objects. -*/ -class MemPool -{ -public: - MemPool() {} - virtual ~MemPool() {} - - virtual int ItemSize() const = 0; - virtual void* Alloc() = 0; - virtual void Free( void* ) = 0; - virtual void SetTracked() = 0; -}; - - -/* - Template child class to create pools of the correct type. -*/ -template< int SIZE > -class MemPoolT : public MemPool -{ -public: - MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} - ~MemPoolT() { - // Delete the blocks. - for( int i=0; i<_blockPtrs.Size(); ++i ) { - delete _blockPtrs[i]; - } - } - - virtual int ItemSize() const { - return SIZE; - } - int CurrentAllocs() const { - return _currentAllocs; - } - - virtual void* Alloc() { - if ( !_root ) { - // Need a new block. - Block* block = new Block(); - _blockPtrs.Push( block ); - - for( int i=0; ichunk[i].next = &block->chunk[i+1]; - } - block->chunk[COUNT-1].next = 0; - _root = block->chunk; - } - void* result = _root; - _root = _root->next; - - ++_currentAllocs; - if ( _currentAllocs > _maxAllocs ) { - _maxAllocs = _currentAllocs; - } - _nAllocs++; - _nUntracked++; - return result; - } - virtual void Free( void* mem ) { - if ( !mem ) { - return; - } - --_currentAllocs; - Chunk* chunk = (Chunk*)mem; -#ifdef DEBUG - memset( chunk, 0xfe, sizeof(Chunk) ); -#endif - chunk->next = _root; - _root = chunk; - } - void Trace( const char* name ) { - printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", - name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() ); - } - - void SetTracked() { - _nUntracked--; - } - - int Untracked() const { - return _nUntracked; - } - - // This number is perf sensitive. 4k seems like a good tradeoff on my machine. - // The test file is large, 170k. - // Release: VS2010 gcc(no opt) - // 1k: 4000 - // 2k: 4000 - // 4k: 3900 21000 - // 16k: 5200 - // 32k: 4300 - // 64k: 4000 21000 - enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private - -private: - union Chunk { - Chunk* next; - char mem[SIZE]; - }; - struct Block { - Chunk chunk[COUNT]; - }; - DynArray< Block*, 10 > _blockPtrs; - Chunk* _root; - - int _currentAllocs; - int _nAllocs; - int _maxAllocs; - int _nUntracked; -}; - - - -/** - Implements the interface to the "Visitor pattern" (see the Accept() method.) - If you call the Accept() method, it requires being passed a XMLVisitor - class to handle callbacks. For nodes that contain other nodes (Document, Element) - you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs - are simply called with Visit(). - - If you return 'true' from a Visit method, recursive parsing will continue. If you return - false, no children of this node or its siblings will be visited. - - All flavors of Visit methods have a default implementation that returns 'true' (continue - visiting). You need to only override methods that are interesting to you. - - Generally Accept() is called on the XMLDocument, although all nodes support visiting. - - You should never change the document from a callback. - - @sa XMLNode::Accept() -*/ -class TINYXML2_LIB XMLVisitor -{ -public: - virtual ~XMLVisitor() {} - - /// Visit a document. - virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { - return true; - } - /// Visit a document. - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - /// Visit an element. - virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { - return true; - } - /// Visit an element. - virtual bool VisitExit( const XMLElement& /*element*/ ) { - return true; - } - - /// Visit a declaration. - virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { - return true; - } - /// Visit a text node. - virtual bool Visit( const XMLText& /*text*/ ) { - return true; - } - /// Visit a comment node. - virtual bool Visit( const XMLComment& /*comment*/ ) { - return true; - } - /// Visit an unknown node. - virtual bool Visit( const XMLUnknown& /*unknown*/ ) { - return true; - } -}; - - -/* - Utility functionality. -*/ -class XMLUtil -{ -public: - // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't - // correct, but simple, and usually works. - static const char* SkipWhiteSpace( const char* p ) { - while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { - ++p; - } - return p; - } - static char* SkipWhiteSpace( char* p ) { - while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { - ++p; - } - return p; - } - static bool IsWhiteSpace( char p ) { - return !IsUTF8Continuation(p) && isspace( static_cast(p) ); - } - - inline static bool IsNameStartChar( unsigned char ch ) { - return ( ( ch < 128 ) ? isalpha( ch ) : 1 ) - || ch == ':' - || ch == '_'; - } - - inline static bool IsNameChar( unsigned char ch ) { - return IsNameStartChar( ch ) - || isdigit( ch ) - || ch == '.' - || ch == '-'; - } - - inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { - int n = 0; - if ( p == q ) { - return true; - } - while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); - } - - /// Get the last child node, or null if none exists. - const XMLNode* LastChild() const { - return _lastChild; - } - - XMLNode* LastChild() { - return const_cast(const_cast(this)->LastChild() ); - } - - /** Get the last child element or optionally the last child - element with the specified name. - */ - const XMLElement* LastChildElement( const char* value=0 ) const; - - XMLElement* LastChildElement( const char* value=0 ) { - return const_cast(const_cast(this)->LastChildElement(value) ); - } - - /// Get the previous (left) sibling node of this node. - const XMLNode* PreviousSibling() const { - return _prev; - } - - XMLNode* PreviousSibling() { - return _prev; - } - - /// Get the previous (left) sibling element of this node, with an optionally supplied name. - const XMLElement* PreviousSiblingElement( const char* value=0 ) const ; - - XMLElement* PreviousSiblingElement( const char* value=0 ) { - return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); - } - - /// Get the next (right) sibling node of this node. - const XMLNode* NextSibling() const { - return _next; - } - - XMLNode* NextSibling() { - return _next; - } - - /// Get the next (right) sibling element of this node, with an optionally supplied name. - const XMLElement* NextSiblingElement( const char* value=0 ) const; - - XMLElement* NextSiblingElement( const char* value=0 ) { - return const_cast(const_cast(this)->NextSiblingElement( value ) ); - } - - /** - Add a child node as the last (right) child. - */ - XMLNode* InsertEndChild( XMLNode* addThis ); - - XMLNode* LinkEndChild( XMLNode* addThis ) { - return InsertEndChild( addThis ); - } - /** - Add a child node as the first (left) child. - */ - XMLNode* InsertFirstChild( XMLNode* addThis ); - /** - Add a node after the specified child node. - */ - XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); - - /** - Delete all the children of this node. - */ - void DeleteChildren(); - - /** - Delete a child of this node. - */ - void DeleteChild( XMLNode* node ); - - /** - Make a copy of this node, but not its children. - You may pass in a Document pointer that will be - the owner of the new Node. If the 'document' is - null, then the node returned will be allocated - from the current Document. (this->GetDocument()) - - Note: if called on a XMLDocument, this will return null. - */ - virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; - - /** - Test if 2 nodes are the same, but don't test children. - The 2 nodes do not need to be in the same Document. - - Note: if called on a XMLDocument, this will return false. - */ - virtual bool ShallowEqual( const XMLNode* compare ) const = 0; - - /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the - XML tree will be conditionally visited and the host will be called back - via the XMLVisitor interface. - - This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse - the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this - interface versus any other.) - - The interface has been based on ideas from: - - - http://www.saxproject.org/ - - http://c2.com/cgi/wiki?HierarchicalVisitorPattern - - Which are both good references for "visiting". - - An example of using Accept(): - @verbatim - XMLPrinter printer; - tinyxmlDoc.Accept( &printer ); - const char* xmlcstr = printer.CStr(); - @endverbatim - */ - virtual bool Accept( XMLVisitor* visitor ) const = 0; - - // internal - virtual char* ParseDeep( char*, StrPair* ); - -protected: - XMLNode( XMLDocument* ); - virtual ~XMLNode(); - XMLNode( const XMLNode& ); // not supported - XMLNode& operator=( const XMLNode& ); // not supported - - XMLDocument* _document; - XMLNode* _parent; - mutable StrPair _value; - - XMLNode* _firstChild; - XMLNode* _lastChild; - - XMLNode* _prev; - XMLNode* _next; - -private: - MemPool* _memPool; - void Unlink( XMLNode* child ); -}; - - -/** XML text. - - Note that a text node can have child element nodes, for example: - @verbatim - This is bold - @endverbatim - - A text node can have 2 ways to output the next. "normal" output - and CDATA. It will default to the mode it was parsed from the XML file and - you generally want to leave it alone, but you can change the output mode with - SetCData() and query it with CData(). -*/ -class TINYXML2_LIB XMLText : public XMLNode -{ - friend class XMLBase; - friend class XMLDocument; -public: - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLText* ToText() { - return this; - } - virtual const XMLText* ToText() const { - return this; - } - - /// Declare whether this should be CDATA or standard text. - void SetCData( bool isCData ) { - _isCData = isCData; - } - /// Returns true if this is a CDATA text element. - bool CData() const { - return _isCData; - } - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} - virtual ~XMLText() {} - XMLText( const XMLText& ); // not supported - XMLText& operator=( const XMLText& ); // not supported - -private: - bool _isCData; -}; - - -/** An XML Comment. */ -class TINYXML2_LIB XMLComment : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLComment* ToComment() { - return this; - } - virtual const XMLComment* ToComment() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLComment( XMLDocument* doc ); - virtual ~XMLComment(); - XMLComment( const XMLComment& ); // not supported - XMLComment& operator=( const XMLComment& ); // not supported - -private: -}; - - -/** In correct XML the declaration is the first entry in the file. - @verbatim - - @endverbatim - - TinyXML-2 will happily read or write files without a declaration, - however. - - The text of the declaration isn't interpreted. It is parsed - and written as a string. -*/ -class TINYXML2_LIB XMLDeclaration : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLDeclaration* ToDeclaration() { - return this; - } - virtual const XMLDeclaration* ToDeclaration() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLDeclaration( XMLDocument* doc ); - virtual ~XMLDeclaration(); - XMLDeclaration( const XMLDeclaration& ); // not supported - XMLDeclaration& operator=( const XMLDeclaration& ); // not supported -}; - - -/** Any tag that TinyXML-2 doesn't recognize is saved as an - unknown. It is a tag of text, but should not be modified. - It will be written back to the XML, unchanged, when the file - is saved. - - DTD tags get thrown into XMLUnknowns. -*/ -class TINYXML2_LIB XMLUnknown : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLUnknown* ToUnknown() { - return this; - } - virtual const XMLUnknown* ToUnknown() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLUnknown( XMLDocument* doc ); - virtual ~XMLUnknown(); - XMLUnknown( const XMLUnknown& ); // not supported - XMLUnknown& operator=( const XMLUnknown& ); // not supported -}; - - -enum XMLError { - XML_NO_ERROR = 0, - XML_SUCCESS = 0, - - XML_NO_ATTRIBUTE, - XML_WRONG_ATTRIBUTE_TYPE, - - XML_ERROR_FILE_NOT_FOUND, - XML_ERROR_FILE_COULD_NOT_BE_OPENED, - XML_ERROR_FILE_READ_ERROR, - XML_ERROR_ELEMENT_MISMATCH, - XML_ERROR_PARSING_ELEMENT, - XML_ERROR_PARSING_ATTRIBUTE, - XML_ERROR_IDENTIFYING_TAG, - XML_ERROR_PARSING_TEXT, - XML_ERROR_PARSING_CDATA, - XML_ERROR_PARSING_COMMENT, - XML_ERROR_PARSING_DECLARATION, - XML_ERROR_PARSING_UNKNOWN, - XML_ERROR_EMPTY_DOCUMENT, - XML_ERROR_MISMATCHED_ELEMENT, - XML_ERROR_PARSING, - - XML_CAN_NOT_CONVERT_TEXT, - XML_NO_TEXT_NODE -}; - - -/** An attribute is a name-value pair. Elements have an arbitrary - number of attributes, each with a unique name. - - @note The attributes are not XMLNodes. You may only query the - Next() attribute in a list. -*/ -class TINYXML2_LIB XMLAttribute -{ - friend class XMLElement; -public: - /// The name of the attribute. - const char* Name() const { - return _name.GetStr(); - } - /// The value of the attribute. - const char* Value() const { - return _value.GetStr(); - } - /// The next attribute in the list. - const XMLAttribute* Next() const { - return _next; - } - - /** IntValue interprets the attribute as an integer, and returns the value. - If the value isn't an integer, 0 will be returned. There is no error checking; - use QueryIntValue() if you need error checking. - */ - int IntValue() const { - int i=0; - QueryIntValue( &i ); - return i; - } - /// Query as an unsigned integer. See IntValue() - unsigned UnsignedValue() const { - unsigned i=0; - QueryUnsignedValue( &i ); - return i; - } - /// Query as a boolean. See IntValue() - bool BoolValue() const { - bool b=false; - QueryBoolValue( &b ); - return b; - } - /// Query as a double. See IntValue() - double DoubleValue() const { - double d=0; - QueryDoubleValue( &d ); - return d; - } - /// Query as a float. See IntValue() - float FloatValue() const { - float f=0; - QueryFloatValue( &f ); - return f; - } - - /** QueryIntValue interprets the attribute as an integer, and returns the value - in the provided parameter. The function will return XML_NO_ERROR on success, - and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. - */ - XMLError QueryIntValue( int* value ) const; - /// See QueryIntValue - XMLError QueryUnsignedValue( unsigned int* value ) const; - /// See QueryIntValue - XMLError QueryBoolValue( bool* value ) const; - /// See QueryIntValue - XMLError QueryDoubleValue( double* value ) const; - /// See QueryIntValue - XMLError QueryFloatValue( float* value ) const; - - /// Set the attribute to a string value. - void SetAttribute( const char* value ); - /// Set the attribute to value. - void SetAttribute( int value ); - /// Set the attribute to value. - void SetAttribute( unsigned value ); - /// Set the attribute to value. - void SetAttribute( bool value ); - /// Set the attribute to value. - void SetAttribute( double value ); - /// Set the attribute to value. - void SetAttribute( float value ); - -private: - enum { BUF_SIZE = 200 }; - - XMLAttribute() : _next( 0 ), _memPool( 0 ) {} - virtual ~XMLAttribute() {} - - XMLAttribute( const XMLAttribute& ); // not supported - void operator=( const XMLAttribute& ); // not supported - void SetName( const char* name ); - - char* ParseDeep( char* p, bool processEntities ); - - mutable StrPair _name; - mutable StrPair _value; - XMLAttribute* _next; - MemPool* _memPool; -}; - - -/** The element is a container class. It has a value, the element name, - and can contain other elements, text, comments, and unknowns. - Elements also contain an arbitrary number of attributes. -*/ -class TINYXML2_LIB XMLElement : public XMLNode -{ - friend class XMLBase; - friend class XMLDocument; -public: - /// Get the name of an element (which is the Value() of the node.) - const char* Name() const { - return Value(); - } - /// Set the name of the element. - void SetName( const char* str, bool staticMem=false ) { - SetValue( str, staticMem ); - } - - virtual XMLElement* ToElement() { - return this; - } - virtual const XMLElement* ToElement() const { - return this; - } - virtual bool Accept( XMLVisitor* visitor ) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none - exists. For example: - - @verbatim - const char* value = ele->Attribute( "foo" ); - @endverbatim - - The 'value' parameter is normally null. However, if specified, - the attribute will only be returned if the 'name' and 'value' - match. This allow you to write code: - - @verbatim - if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); - @endverbatim - - rather than: - @verbatim - if ( ele->Attribute( "foo" ) ) { - if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); - } - @endverbatim - */ - const char* Attribute( const char* name, const char* value=0 ) const; - - /** Given an attribute name, IntAttribute() returns the value - of the attribute interpreted as an integer. 0 will be - returned if there is an error. For a method with error - checking, see QueryIntAttribute() - */ - int IntAttribute( const char* name ) const { - int i=0; - QueryIntAttribute( name, &i ); - return i; - } - /// See IntAttribute() - unsigned UnsignedAttribute( const char* name ) const { - unsigned i=0; - QueryUnsignedAttribute( name, &i ); - return i; - } - /// See IntAttribute() - bool BoolAttribute( const char* name ) const { - bool b=false; - QueryBoolAttribute( name, &b ); - return b; - } - /// See IntAttribute() - double DoubleAttribute( const char* name ) const { - double d=0; - QueryDoubleAttribute( name, &d ); - return d; - } - /// See IntAttribute() - float FloatAttribute( const char* name ) const { - float f=0; - QueryFloatAttribute( name, &f ); - return f; - } - - /** Given an attribute name, QueryIntAttribute() returns - XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - XMLError QueryIntAttribute( const char* name, int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryIntValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryUnsignedValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryBoolAttribute( const char* name, bool* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryBoolValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryDoubleAttribute( const char* name, double* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryDoubleValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryFloatAttribute( const char* name, float* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryFloatValue( value ); - } - - - /** Given an attribute name, QueryAttribute() returns - XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. It is overloaded for the primitive types, - and is a generally more convenient replacement of - QueryIntAttribute() and related functions. - - If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - int QueryAttribute( const char* name, int* value ) const { - return QueryIntAttribute( name, value ); - } - - int QueryAttribute( const char* name, unsigned int* value ) const { - return QueryUnsignedAttribute( name, value ); - } - - int QueryAttribute( const char* name, bool* value ) const { - return QueryBoolAttribute( name, value ); - } - - int QueryAttribute( const char* name, double* value ) const { - return QueryDoubleAttribute( name, value ); - } - - int QueryAttribute( const char* name, float* value ) const { - return QueryFloatAttribute( name, value ); - } - - /// Sets the named attribute to value. - void SetAttribute( const char* name, const char* value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, int value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, unsigned value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, bool value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, double value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /** - Delete an attribute. - */ - void DeleteAttribute( const char* name ); - - /// Return the first attribute in the list. - const XMLAttribute* FirstAttribute() const { - return _rootAttribute; - } - /// Query a specific attribute in the list. - const XMLAttribute* FindAttribute( const char* name ) const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, GetText() is limited compared to getting the XMLText child - and accessing it directly. - - If the first child of 'this' is a XMLText, the GetText() - returns the character string of the Text node, else null is returned. - - This is a convenient method for getting the text of simple contained text: - @verbatim - This is text - const char* str = fooElement->GetText(); - @endverbatim - - 'str' will be a pointer to "This is text". - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then the value of str would be null. The first child node isn't a text node, it is - another element. From this XML: - @verbatim - This is text - @endverbatim - GetText() will return "This is ". - */ - const char* GetText() const; - - /** - Convenience method to query the value of a child text node. This is probably best - shown by example. Given you have a document is this form: - @verbatim - - 1 - 1.4 - - @endverbatim - - The QueryIntText() and similar functions provide a safe and easier way to get to the - "value" of x and y. - - @verbatim - int x = 0; - float y = 0; // types of x and y are contrived for example - const XMLElement* xElement = pointElement->FirstChildElement( "x" ); - const XMLElement* yElement = pointElement->FirstChildElement( "y" ); - xElement->QueryIntText( &x ); - yElement->QueryFloatText( &y ); - @endverbatim - - @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted - to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. - - */ - XMLError QueryIntText( int* ival ) const; - /// See QueryIntText() - XMLError QueryUnsignedText( unsigned* uval ) const; - /// See QueryIntText() - XMLError QueryBoolText( bool* bval ) const; - /// See QueryIntText() - XMLError QueryDoubleText( double* dval ) const; - /// See QueryIntText() - XMLError QueryFloatText( float* fval ) const; - - // internal: - enum { - OPEN, // - CLOSED, // - CLOSING // - }; - int ClosingType() const { - return _closingType; - } - char* ParseDeep( char* p, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -private: - XMLElement( XMLDocument* doc ); - virtual ~XMLElement(); - XMLElement( const XMLElement& ); // not supported - void operator=( const XMLElement& ); // not supported - - XMLAttribute* FindAttribute( const char* name ); - XMLAttribute* FindOrCreateAttribute( const char* name ); - //void LinkAttribute( XMLAttribute* attrib ); - char* ParseAttributes( char* p ); - - int _closingType; - // The attribute list is ordered; there is no 'lastAttribute' - // because the list needs to be scanned for dupes before adding - // a new attribute. - XMLAttribute* _rootAttribute; -}; - - -enum Whitespace { - PRESERVE_WHITESPACE, - COLLAPSE_WHITESPACE -}; - - -/** A Document binds together all the functionality. - It can be saved, loaded, and printed to the screen. - All Nodes are connected and allocated to a Document. - If the Document is deleted, all its Nodes are also deleted. -*/ -class TINYXML2_LIB XMLDocument : public XMLNode -{ - friend class XMLElement; -public: - /// constructor - XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); - ~XMLDocument(); - - virtual XMLDocument* ToDocument() { - return this; - } - virtual const XMLDocument* ToDocument() const { - return this; - } - - /** - Parse an XML file from a character string. - Returns XML_NO_ERROR (0) on success, or - an errorID. - - You may optionally pass in the 'nBytes', which is - the number of bytes which will be parsed. If not - specified, TinyXML-2 will assume 'xml' points to a - null terminated string. - */ - XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); - - /** - Load an XML file from disk. - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError LoadFile( const char* filename ); - - /** - Load an XML file from disk. You are responsible - for providing and closing the FILE*. - - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError LoadFile( FILE* ); - - /** - Save the XML file to disk. - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError SaveFile( const char* filename, bool compact = false ); - - /** - Save the XML file to disk. You are responsible - for providing and closing the FILE*. - - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError SaveFile( FILE* fp, bool compact = false ); - - bool ProcessEntities() const { - return _processEntities; - } - Whitespace WhitespaceMode() const { - return _whitespace; - } - - /** - Returns true if this document has a leading Byte Order Mark of UTF8. - */ - bool HasBOM() const { - return _writeBOM; - } - /** Sets whether to write the BOM when writing the file. - */ - void SetBOM( bool useBOM ) { - _writeBOM = useBOM; - } - - /** Return the root element of DOM. Equivalent to FirstChildElement(). - To get the first node, use FirstChild(). - */ - XMLElement* RootElement() { - return FirstChildElement(); - } - const XMLElement* RootElement() const { - return FirstChildElement(); - } - - /** Print the Document. If the Printer is not provided, it will - print to stdout. If you provide Printer, this can print to a file: - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Or you can use a printer to print to memory: - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - // printer.CStr() has a const char* to the XML - @endverbatim - */ - void Print( XMLPrinter* streamer=0 ) const; - virtual bool Accept( XMLVisitor* visitor ) const; - - /** - Create a new Element associated with - this Document. The memory for the Element - is managed by the Document. - */ - XMLElement* NewElement( const char* name ); - /** - Create a new Comment associated with - this Document. The memory for the Comment - is managed by the Document. - */ - XMLComment* NewComment( const char* comment ); - /** - Create a new Text associated with - this Document. The memory for the Text - is managed by the Document. - */ - XMLText* NewText( const char* text ); - /** - Create a new Declaration associated with - this Document. The memory for the object - is managed by the Document. - - If the 'text' param is null, the standard - declaration is used.: - @verbatim - - @endverbatim - */ - XMLDeclaration* NewDeclaration( const char* text=0 ); - /** - Create a new Unknown associated with - this Document. The memory for the object - is managed by the Document. - */ - XMLUnknown* NewUnknown( const char* text ); - - /** - Delete a node associated with this document. - It will be unlinked from the DOM. - */ - void DeleteNode( XMLNode* node ) { - node->_parent->DeleteChild( node ); - } - - void SetError( XMLError error, const char* str1, const char* str2 ); - - /// Return true if there was an error parsing the document. - bool Error() const { - return _errorID != XML_NO_ERROR; - } - /// Return the errorID. - XMLError ErrorID() const { - return _errorID; - } - /// Return a possibly helpful diagnostic location or string. - const char* GetErrorStr1() const { - return _errorStr1; - } - /// Return a possibly helpful secondary diagnostic location or string. - const char* GetErrorStr2() const { - return _errorStr2; - } - /// If there is an error, print it to stdout. - void PrintError() const; - - /// Clear the document, resetting it to the initial state. - void Clear(); - - // internal - char* Identify( char* p, XMLNode** node ); - - virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { - return 0; - } - virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { - return false; - } - -private: - XMLDocument( const XMLDocument& ); // not supported - void operator=( const XMLDocument& ); // not supported - - bool _writeBOM; - bool _processEntities; - XMLError _errorID; - Whitespace _whitespace; - const char* _errorStr1; - const char* _errorStr2; - char* _charBuffer; - - MemPoolT< sizeof(XMLElement) > _elementPool; - MemPoolT< sizeof(XMLAttribute) > _attributePool; - MemPoolT< sizeof(XMLText) > _textPool; - MemPoolT< sizeof(XMLComment) > _commentPool; -}; - - -/** - A XMLHandle is a class that wraps a node pointer with null checks; this is - an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 - DOM structure. It is a separate utility class. - - Take an example: - @verbatim - - - - - - - @endverbatim - - Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very - easy to write a *lot* of code that looks like: - - @verbatim - XMLElement* root = document.FirstChildElement( "Document" ); - if ( root ) - { - XMLElement* element = root->FirstChildElement( "Element" ); - if ( element ) - { - XMLElement* child = element->FirstChildElement( "Child" ); - if ( child ) - { - XMLElement* child2 = child->NextSiblingElement( "Child" ); - if ( child2 ) - { - // Finally do something useful. - @endverbatim - - And that doesn't even cover "else" cases. XMLHandle addresses the verbosity - of such code. A XMLHandle checks for null pointers so it is perfectly safe - and correct to use: - - @verbatim - XMLHandle docHandle( &document ); - XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement(); - if ( child2 ) - { - // do something useful - @endverbatim - - Which is MUCH more concise and useful. - - It is also safe to copy handles - internally they are nothing more than node pointers. - @verbatim - XMLHandle handleCopy = handle; - @endverbatim - - See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. -*/ -class TINYXML2_LIB XMLHandle -{ -public: - /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - XMLHandle( XMLNode* node ) { - _node = node; - } - /// Create a handle from a node. - XMLHandle( XMLNode& node ) { - _node = &node; - } - /// Copy constructor - XMLHandle( const XMLHandle& ref ) { - _node = ref._node; - } - /// Assignment - XMLHandle& operator=( const XMLHandle& ref ) { - _node = ref._node; - return *this; - } - - /// Get the first child of this handle. - XMLHandle FirstChild() { - return XMLHandle( _node ? _node->FirstChild() : 0 ); - } - /// Get the first child element of this handle. - XMLHandle FirstChildElement( const char* value=0 ) { - return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 ); - } - /// Get the last child of this handle. - XMLHandle LastChild() { - return XMLHandle( _node ? _node->LastChild() : 0 ); - } - /// Get the last child element of this handle. - XMLHandle LastChildElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 ); - } - /// Get the previous sibling of this handle. - XMLHandle PreviousSibling() { - return XMLHandle( _node ? _node->PreviousSibling() : 0 ); - } - /// Get the previous sibling element of this handle. - XMLHandle PreviousSiblingElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); - } - /// Get the next sibling of this handle. - XMLHandle NextSibling() { - return XMLHandle( _node ? _node->NextSibling() : 0 ); - } - /// Get the next sibling element of this handle. - XMLHandle NextSiblingElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); - } - - /// Safe cast to XMLNode. This can return null. - XMLNode* ToNode() { - return _node; - } - /// Safe cast to XMLElement. This can return null. - XMLElement* ToElement() { - return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); - } - /// Safe cast to XMLText. This can return null. - XMLText* ToText() { - return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); - } - /// Safe cast to XMLUnknown. This can return null. - XMLUnknown* ToUnknown() { - return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); - } - /// Safe cast to XMLDeclaration. This can return null. - XMLDeclaration* ToDeclaration() { - return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); - } - -private: - XMLNode* _node; -}; - - -/** - A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the - same in all regards, except for the 'const' qualifiers. See XMLHandle for API. -*/ -class TINYXML2_LIB XMLConstHandle -{ -public: - XMLConstHandle( const XMLNode* node ) { - _node = node; - } - XMLConstHandle( const XMLNode& node ) { - _node = &node; - } - XMLConstHandle( const XMLConstHandle& ref ) { - _node = ref._node; - } - - XMLConstHandle& operator=( const XMLConstHandle& ref ) { - _node = ref._node; - return *this; - } - - const XMLConstHandle FirstChild() const { - return XMLConstHandle( _node ? _node->FirstChild() : 0 ); - } - const XMLConstHandle FirstChildElement( const char* value=0 ) const { - return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); - } - const XMLConstHandle LastChild() const { - return XMLConstHandle( _node ? _node->LastChild() : 0 ); - } - const XMLConstHandle LastChildElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); - } - const XMLConstHandle PreviousSibling() const { - return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); - } - const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); - } - const XMLConstHandle NextSibling() const { - return XMLConstHandle( _node ? _node->NextSibling() : 0 ); - } - const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); - } - - - const XMLNode* ToNode() const { - return _node; - } - const XMLElement* ToElement() const { - return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); - } - const XMLText* ToText() const { - return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); - } - const XMLUnknown* ToUnknown() const { - return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); - } - const XMLDeclaration* ToDeclaration() const { - return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); - } - -private: - const XMLNode* _node; -}; - - -/** - Printing functionality. The XMLPrinter gives you more - options than the XMLDocument::Print() method. - - It can: - -# Print to memory. - -# Print to a file you provide. - -# Print XML without a XMLDocument. - - Print to Memory - - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - SomeFunction( printer.CStr() ); - @endverbatim - - Print to a File - - You provide the file pointer. - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Print without a XMLDocument - - When loading, an XML parser is very useful. However, sometimes - when saving, it just gets in the way. The code is often set up - for streaming, and constructing the DOM is just overhead. - - The Printer supports the streaming case. The following code - prints out a trivially simple XML file without ever creating - an XML document. - - @verbatim - XMLPrinter printer( fp ); - printer.OpenElement( "foo" ); - printer.PushAttribute( "foo", "bar" ); - printer.CloseElement(); - @endverbatim -*/ -class TINYXML2_LIB XMLPrinter : public XMLVisitor -{ -public: - /** Construct the printer. If the FILE* is specified, - this will print to the FILE. Else it will print - to memory, and the result is available in CStr(). - If 'compact' is set to true, then output is created - with only required whitespace and newlines. - */ - XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); - ~XMLPrinter() {} - - /** If streaming, write the BOM and declaration. */ - void PushHeader( bool writeBOM, bool writeDeclaration ); - /** If streaming, start writing an element. - The element must be closed with CloseElement() - */ - void OpenElement( const char* name ); - /// If streaming, add an attribute to an open element. - void PushAttribute( const char* name, const char* value ); - void PushAttribute( const char* name, int value ); - void PushAttribute( const char* name, unsigned value ); - void PushAttribute( const char* name, bool value ); - void PushAttribute( const char* name, double value ); - /// If streaming, close the Element. - void CloseElement(); - - /// Add a text node. - void PushText( const char* text, bool cdata=false ); - /// Add a text node from an integer. - void PushText( int value ); - /// Add a text node from an unsigned. - void PushText( unsigned value ); - /// Add a text node from a bool. - void PushText( bool value ); - /// Add a text node from a float. - void PushText( float value ); - /// Add a text node from a double. - void PushText( double value ); - - /// Add a comment - void PushComment( const char* comment ); - - void PushDeclaration( const char* value ); - void PushUnknown( const char* value ); - - virtual bool VisitEnter( const XMLDocument& /*doc*/ ); - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); - virtual bool VisitExit( const XMLElement& element ); - - virtual bool Visit( const XMLText& text ); - virtual bool Visit( const XMLComment& comment ); - virtual bool Visit( const XMLDeclaration& declaration ); - virtual bool Visit( const XMLUnknown& unknown ); - - /** - If in print to memory mode, return a pointer to - the XML file in memory. - */ - const char* CStr() const { - return _buffer.Mem(); - } - /** - If in print to memory mode, return the size - of the XML file in memory. (Note the size returned - includes the terminating null.) - */ - int CStrSize() const { - return _buffer.Size(); - } - -private: - void SealElement(); - void PrintSpace( int depth ); - void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. - void Print( const char* format, ... ); - - bool _elementJustOpened; - bool _firstElement; - FILE* _fp; - int _depth; - int _textDepth; - bool _processEntities; - bool _compactMode; - - enum { - ENTITY_RANGE = 64, - BUF_SIZE = 200 - }; - bool _entityFlag[ENTITY_RANGE]; - bool _restrictedEntityFlag[ENTITY_RANGE]; - - DynArray< const char*, 10 > _stack; - DynArray< char, 20 > _buffer; -#ifdef _MSC_VER - DynArray< char, 20 > _accumulator; -#endif -}; - - -} // tinyxml2 - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif - -#endif // TINYXML2_INCLUDED +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef TINYXML2_INCLUDED +#define TINYXML2_INCLUDED + +#if defined(ANDROID_NDK) || defined(__BORLANDC__) +# include +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +#endif + +/* + TODO: intern strings instead of allocation. +*/ +/* + gcc: + g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe + + Formatting, Artistic Style: + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h +*/ + +#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) +# ifndef DEBUG +# define DEBUG +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4251) +#endif + +#ifdef _WIN32 +# ifdef TINYXML2_EXPORT +# define TINYXML2_LIB __declspec(dllexport) +# elif defined(TINYXML2_IMPORT) +# define TINYXML2_LIB __declspec(dllimport) +# else +# define TINYXML2_LIB +# endif +#else +# define TINYXML2_LIB +#endif + + +#if defined(DEBUG) +# if defined(_MSC_VER) +# define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak() +# elif defined (ANDROID_NDK) +# include +# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } +# else +# include +# define TIXMLASSERT assert +# endif +# else +# define TIXMLASSERT( x ) {} +#endif + + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) +// Microsoft visual studio, version 2005 and higher. +/*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... +);*/ +inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) +{ + va_list va; + va_start( va, format ); + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; +} +#define TIXML_SSCANF sscanf_s +#else +// GCC version 3 and higher +//#warning( "Using sn* functions." ) +#define TIXML_SNPRINTF snprintf +#define TIXML_SSCANF sscanf +#endif + +static const int TIXML2_MAJOR_VERSION = 1; +static const int TIXML2_MINOR_VERSION = 0; +static const int TIXML2_PATCH_VERSION = 11; + +namespace tinyxml2 +{ +class XMLDocument; +class XMLElement; +class XMLAttribute; +class XMLComment; +class XMLText; +class XMLDeclaration; +class XMLUnknown; +class XMLPrinter; + +/* + A class that wraps strings. Normally stores the start and end + pointers into the XML file itself, and will apply normalization + and entity translation if actually read. Can also store (and memory + manage) a traditional char[] +*/ +class StrPair +{ +public: + enum { + NEEDS_ENTITY_PROCESSING = 0x01, + NEEDS_NEWLINE_NORMALIZATION = 0x02, + COLLAPSE_WHITESPACE = 0x04, + + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_NAME = 0, + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + COMMENT = NEEDS_NEWLINE_NORMALIZATION + }; + + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} + ~StrPair(); + + void Set( char* start, char* end, int flags ) { + Reset(); + _start = start; + _end = end; + _flags = flags | NEEDS_FLUSH; + } + + const char* GetStr(); + + bool Empty() const { + return _start == _end; + } + + void SetInternedStr( const char* str ) { + Reset(); + _start = const_cast(str); + } + + void SetStr( const char* str, int flags=0 ); + + char* ParseText( char* in, const char* endTag, int strFlags ); + char* ParseName( char* in ); + +private: + void Reset(); + void CollapseWhitespace(); + + enum { + NEEDS_FLUSH = 0x100, + NEEDS_DELETE = 0x200 + }; + + // After parsing, if *_end != 0, it can be set to zero. + int _flags; + char* _start; + char* _end; +}; + + +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ +template +class DynArray +{ +public: + DynArray< T, INIT >() { + _mem = _pool; + _allocated = INIT; + _size = 0; + } + + ~DynArray() { + if ( _mem != _pool ) { + delete [] _mem; + } + } + + void Push( T t ) { + EnsureCapacity( _size+1 ); + _mem[_size++] = t; + } + + T* PushArr( int count ) { + EnsureCapacity( _size+count ); + T* ret = &_mem[_size]; + _size += count; + return ret; + } + + T Pop() { + return _mem[--_size]; + } + + void PopArr( int count ) { + TIXMLASSERT( _size >= count ); + _size -= count; + } + + bool Empty() const { + return _size == 0; + } + + T& operator[](int i) { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& operator[](int i) const { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + int Size() const { + return _size; + } + + int Capacity() const { + return _allocated; + } + + const T* Mem() const { + return _mem; + } + + T* Mem() { + return _mem; + } + +private: + void EnsureCapacity( int cap ) { + if ( cap > _allocated ) { + int newAllocated = cap * 2; + T* newMem = new T[newAllocated]; + memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs + if ( _mem != _pool ) { + delete [] _mem; + } + _mem = newMem; + _allocated = newAllocated; + } + } + + T* _mem; + T _pool[INIT]; + int _allocated; // objects allocated + int _size; // number objects in use +}; + + +/* + Parent virtual class of a pool for fast allocation + and deallocation of objects. +*/ +class MemPool +{ +public: + MemPool() {} + virtual ~MemPool() {} + + virtual int ItemSize() const = 0; + virtual void* Alloc() = 0; + virtual void Free( void* ) = 0; + virtual void SetTracked() = 0; +}; + + +/* + Template child class to create pools of the correct type. +*/ +template< int SIZE > +class MemPoolT : public MemPool +{ +public: + MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} + ~MemPoolT() { + // Delete the blocks. + for( int i=0; i<_blockPtrs.Size(); ++i ) { + delete _blockPtrs[i]; + } + } + + virtual int ItemSize() const { + return SIZE; + } + int CurrentAllocs() const { + return _currentAllocs; + } + + virtual void* Alloc() { + if ( !_root ) { + // Need a new block. + Block* block = new Block(); + _blockPtrs.Push( block ); + + for( int i=0; ichunk[i].next = &block->chunk[i+1]; + } + block->chunk[COUNT-1].next = 0; + _root = block->chunk; + } + void* result = _root; + _root = _root->next; + + ++_currentAllocs; + if ( _currentAllocs > _maxAllocs ) { + _maxAllocs = _currentAllocs; + } + _nAllocs++; + _nUntracked++; + return result; + } + virtual void Free( void* mem ) { + if ( !mem ) { + return; + } + --_currentAllocs; + Chunk* chunk = (Chunk*)mem; +#ifdef DEBUG + memset( chunk, 0xfe, sizeof(Chunk) ); +#endif + chunk->next = _root; + _root = chunk; + } + void Trace( const char* name ) { + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", + name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() ); + } + + void SetTracked() { + _nUntracked--; + } + + int Untracked() const { + return _nUntracked; + } + + // This number is perf sensitive. 4k seems like a good tradeoff on my machine. + // The test file is large, 170k. + // Release: VS2010 gcc(no opt) + // 1k: 4000 + // 2k: 4000 + // 4k: 3900 21000 + // 16k: 5200 + // 32k: 4300 + // 64k: 4000 21000 + enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private + +private: + union Chunk { + Chunk* next; + char mem[SIZE]; + }; + struct Block { + Chunk chunk[COUNT]; + }; + DynArray< Block*, 10 > _blockPtrs; + Chunk* _root; + + int _currentAllocs; + int _nAllocs; + int _maxAllocs; + int _nUntracked; +}; + + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a XMLVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its siblings will be visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the XMLDocument, although all nodes support visiting. + + You should never change the document from a callback. + + @sa XMLNode::Accept() +*/ +class TINYXML2_LIB XMLVisitor +{ +public: + virtual ~XMLVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { + return true; + } + /// Visit a document. + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + /// Visit an element. + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { + return true; + } + /// Visit an element. + virtual bool VisitExit( const XMLElement& /*element*/ ) { + return true; + } + + /// Visit a declaration. + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { + return true; + } + /// Visit a text node. + virtual bool Visit( const XMLText& /*text*/ ) { + return true; + } + /// Visit a comment node. + virtual bool Visit( const XMLComment& /*comment*/ ) { + return true; + } + /// Visit an unknown node. + virtual bool Visit( const XMLUnknown& /*unknown*/ ) { + return true; + } +}; + + +/* + Utility functionality. +*/ +class XMLUtil +{ +public: + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. + static const char* SkipWhiteSpace( const char* p ) { + while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { + ++p; + } + return p; + } + static char* SkipWhiteSpace( char* p ) { + while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { + ++p; + } + return p; + } + static bool IsWhiteSpace( char p ) { + return !IsUTF8Continuation(p) && isspace( static_cast(p) ); + } + + inline static bool IsNameStartChar( unsigned char ch ) { + return ( ( ch < 128 ) ? isalpha( ch ) : 1 ) + || ch == ':' + || ch == '_'; + } + + inline static bool IsNameChar( unsigned char ch ) { + return IsNameStartChar( ch ) + || isdigit( ch ) + || ch == '.' + || ch == '-'; + } + + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { + int n = 0; + if ( p == q ) { + return true; + } + while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); + } + + /// Get the last child node, or null if none exists. + const XMLNode* LastChild() const { + return _lastChild; + } + + XMLNode* LastChild() { + return const_cast(const_cast(this)->LastChild() ); + } + + /** Get the last child element or optionally the last child + element with the specified name. + */ + const XMLElement* LastChildElement( const char* value=0 ) const; + + XMLElement* LastChildElement( const char* value=0 ) { + return const_cast(const_cast(this)->LastChildElement(value) ); + } + + /// Get the previous (left) sibling node of this node. + const XMLNode* PreviousSibling() const { + return _prev; + } + + XMLNode* PreviousSibling() { + return _prev; + } + + /// Get the previous (left) sibling element of this node, with an optionally supplied name. + const XMLElement* PreviousSiblingElement( const char* value=0 ) const ; + + XMLElement* PreviousSiblingElement( const char* value=0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); + } + + /// Get the next (right) sibling node of this node. + const XMLNode* NextSibling() const { + return _next; + } + + XMLNode* NextSibling() { + return _next; + } + + /// Get the next (right) sibling element of this node, with an optionally supplied name. + const XMLElement* NextSiblingElement( const char* value=0 ) const; + + XMLElement* NextSiblingElement( const char* value=0 ) { + return const_cast(const_cast(this)->NextSiblingElement( value ) ); + } + + /** + Add a child node as the last (right) child. + */ + XMLNode* InsertEndChild( XMLNode* addThis ); + + XMLNode* LinkEndChild( XMLNode* addThis ) { + return InsertEndChild( addThis ); + } + /** + Add a child node as the first (left) child. + */ + XMLNode* InsertFirstChild( XMLNode* addThis ); + /** + Add a node after the specified child node. + */ + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + + /** + Delete all the children of this node. + */ + void DeleteChildren(); + + /** + Delete a child of this node. + */ + void DeleteChild( XMLNode* node ); + + /** + Make a copy of this node, but not its children. + You may pass in a Document pointer that will be + the owner of the new Node. If the 'document' is + null, then the node returned will be allocated + from the current Document. (this->GetDocument()) + + Note: if called on a XMLDocument, this will return null. + */ + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; + + /** + Test if 2 nodes are the same, but don't test children. + The 2 nodes do not need to be in the same Document. + + Note: if called on a XMLDocument, this will return false. + */ + virtual bool ShallowEqual( const XMLNode* compare ) const = 0; + + /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the XMLVisitor interface. + + This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + XMLPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( XMLVisitor* visitor ) const = 0; + + // internal + virtual char* ParseDeep( char*, StrPair* ); + +protected: + XMLNode( XMLDocument* ); + virtual ~XMLNode(); + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported + + XMLDocument* _document; + XMLNode* _parent; + mutable StrPair _value; + + XMLNode* _firstChild; + XMLNode* _lastChild; + + XMLNode* _prev; + XMLNode* _next; + +private: + MemPool* _memPool; + void Unlink( XMLNode* child ); +}; + + +/** XML text. + + Note that a text node can have child element nodes, for example: + @verbatim + This is bold + @endverbatim + + A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCData() and query it with CData(). +*/ +class TINYXML2_LIB XMLText : public XMLNode +{ + friend class XMLBase; + friend class XMLDocument; +public: + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLText* ToText() { + return this; + } + virtual const XMLText* ToText() const { + return this; + } + + /// Declare whether this should be CDATA or standard text. + void SetCData( bool isCData ) { + _isCData = isCData; + } + /// Returns true if this is a CDATA text element. + bool CData() const { + return _isCData; + } + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + virtual ~XMLText() {} + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported + +private: + bool _isCData; +}; + + +/** An XML Comment. */ +class TINYXML2_LIB XMLComment : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLComment* ToComment() { + return this; + } + virtual const XMLComment* ToComment() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLComment( XMLDocument* doc ); + virtual ~XMLComment(); + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported + +private: +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXML-2 will happily read or write files without a declaration, + however. + + The text of the declaration isn't interpreted. It is parsed + and written as a string. +*/ +class TINYXML2_LIB XMLDeclaration : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLDeclaration* ToDeclaration() { + return this; + } + virtual const XMLDeclaration* ToDeclaration() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLDeclaration( XMLDocument* doc ); + virtual ~XMLDeclaration(); + XMLDeclaration( const XMLDeclaration& ); // not supported + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported +}; + + +/** Any tag that TinyXML-2 doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into XMLUnknowns. +*/ +class TINYXML2_LIB XMLUnknown : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLUnknown* ToUnknown() { + return this; + } + virtual const XMLUnknown* ToUnknown() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLUnknown( XMLDocument* doc ); + virtual ~XMLUnknown(); + XMLUnknown( const XMLUnknown& ); // not supported + XMLUnknown& operator=( const XMLUnknown& ); // not supported +}; + + +enum XMLError { + XML_NO_ERROR = 0, + XML_SUCCESS = 0, + + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_ELEMENT_MISMATCH, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_IDENTIFYING_TAG, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not XMLNodes. You may only query the + Next() attribute in a list. +*/ +class TINYXML2_LIB XMLAttribute +{ + friend class XMLElement; +public: + /// The name of the attribute. + const char* Name() const { + return _name.GetStr(); + } + /// The value of the attribute. + const char* Value() const { + return _value.GetStr(); + } + /// The next attribute in the list. + const XMLAttribute* Next() const { + return _next; + } + + /** IntValue interprets the attribute as an integer, and returns the value. + If the value isn't an integer, 0 will be returned. There is no error checking; + use QueryIntValue() if you need error checking. + */ + int IntValue() const { + int i=0; + QueryIntValue( &i ); + return i; + } + /// Query as an unsigned integer. See IntValue() + unsigned UnsignedValue() const { + unsigned i=0; + QueryUnsignedValue( &i ); + return i; + } + /// Query as a boolean. See IntValue() + bool BoolValue() const { + bool b=false; + QueryBoolValue( &b ); + return b; + } + /// Query as a double. See IntValue() + double DoubleValue() const { + double d=0; + QueryDoubleValue( &d ); + return d; + } + /// Query as a float. See IntValue() + float FloatValue() const { + float f=0; + QueryFloatValue( &f ); + return f; + } + + /** QueryIntValue interprets the attribute as an integer, and returns the value + in the provided parameter. The function will return XML_NO_ERROR on success, + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. + */ + XMLError QueryIntValue( int* value ) const; + /// See QueryIntValue + XMLError QueryUnsignedValue( unsigned int* value ) const; + /// See QueryIntValue + XMLError QueryBoolValue( bool* value ) const; + /// See QueryIntValue + XMLError QueryDoubleValue( double* value ) const; + /// See QueryIntValue + XMLError QueryFloatValue( float* value ) const; + + /// Set the attribute to a string value. + void SetAttribute( const char* value ); + /// Set the attribute to value. + void SetAttribute( int value ); + /// Set the attribute to value. + void SetAttribute( unsigned value ); + /// Set the attribute to value. + void SetAttribute( bool value ); + /// Set the attribute to value. + void SetAttribute( double value ); + /// Set the attribute to value. + void SetAttribute( float value ); + +private: + enum { BUF_SIZE = 200 }; + + XMLAttribute() : _next( 0 ), _memPool( 0 ) {} + virtual ~XMLAttribute() {} + + XMLAttribute( const XMLAttribute& ); // not supported + void operator=( const XMLAttribute& ); // not supported + void SetName( const char* name ); + + char* ParseDeep( char* p, bool processEntities ); + + mutable StrPair _name; + mutable StrPair _value; + XMLAttribute* _next; + MemPool* _memPool; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TINYXML2_LIB XMLElement : public XMLNode +{ + friend class XMLBase; + friend class XMLDocument; +public: + /// Get the name of an element (which is the Value() of the node.) + const char* Name() const { + return Value(); + } + /// Set the name of the element. + void SetName( const char* str, bool staticMem=false ) { + SetValue( str, staticMem ); + } + + virtual XMLElement* ToElement() { + return this; + } + virtual const XMLElement* ToElement() const { + return this; + } + virtual bool Accept( XMLVisitor* visitor ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none + exists. For example: + + @verbatim + const char* value = ele->Attribute( "foo" ); + @endverbatim + + The 'value' parameter is normally null. However, if specified, + the attribute will only be returned if the 'name' and 'value' + match. This allow you to write code: + + @verbatim + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); + @endverbatim + + rather than: + @verbatim + if ( ele->Attribute( "foo" ) ) { + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); + } + @endverbatim + */ + const char* Attribute( const char* name, const char* value=0 ) const; + + /** Given an attribute name, IntAttribute() returns the value + of the attribute interpreted as an integer. 0 will be + returned if there is an error. For a method with error + checking, see QueryIntAttribute() + */ + int IntAttribute( const char* name ) const { + int i=0; + QueryIntAttribute( name, &i ); + return i; + } + /// See IntAttribute() + unsigned UnsignedAttribute( const char* name ) const { + unsigned i=0; + QueryUnsignedAttribute( name, &i ); + return i; + } + /// See IntAttribute() + bool BoolAttribute( const char* name ) const { + bool b=false; + QueryBoolAttribute( name, &b ); + return b; + } + /// See IntAttribute() + double DoubleAttribute( const char* name ) const { + double d=0; + QueryDoubleAttribute( name, &d ); + return d; + } + /// See IntAttribute() + float FloatAttribute( const char* name ) const { + float f=0; + QueryFloatAttribute( name, &f ); + return f; + } + + /** Given an attribute name, QueryIntAttribute() returns + XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryIntAttribute( const char* name, int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryIntValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsignedValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryBoolAttribute( const char* name, bool* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryBoolValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryDoubleAttribute( const char* name, double* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryDoubleValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryFloatAttribute( const char* name, float* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryFloatValue( value ); + } + + + /** Given an attribute name, QueryAttribute() returns + XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. It is overloaded for the primitive types, + and is a generally more convenient replacement of + QueryIntAttribute() and related functions. + + If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + int QueryAttribute( const char* name, int* value ) const { + return QueryIntAttribute( name, value ); + } + + int QueryAttribute( const char* name, unsigned int* value ) const { + return QueryUnsignedAttribute( name, value ); + } + + int QueryAttribute( const char* name, bool* value ) const { + return QueryBoolAttribute( name, value ); + } + + int QueryAttribute( const char* name, double* value ) const { + return QueryDoubleAttribute( name, value ); + } + + int QueryAttribute( const char* name, float* value ) const { + return QueryFloatAttribute( name, value ); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, const char* value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, int value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, unsigned value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, bool value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, double value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /** + Delete an attribute. + */ + void DeleteAttribute( const char* name ); + + /// Return the first attribute in the list. + const XMLAttribute* FirstAttribute() const { + return _rootAttribute; + } + /// Query a specific attribute in the list. + const XMLAttribute* FindAttribute( const char* name ) const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the XMLText child + and accessing it directly. + + If the first child of 'this' is a XMLText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + */ + const char* GetText() const; + + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + XMLError QueryIntText( int* ival ) const; + /// See QueryIntText() + XMLError QueryUnsignedText( unsigned* uval ) const; + /// See QueryIntText() + XMLError QueryBoolText( bool* bval ) const; + /// See QueryIntText() + XMLError QueryDoubleText( double* dval ) const; + /// See QueryIntText() + XMLError QueryFloatText( float* fval ) const; + + // internal: + enum { + OPEN, // + CLOSED, // + CLOSING // + }; + int ClosingType() const { + return _closingType; + } + char* ParseDeep( char* p, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +private: + XMLElement( XMLDocument* doc ); + virtual ~XMLElement(); + XMLElement( const XMLElement& ); // not supported + void operator=( const XMLElement& ); // not supported + + XMLAttribute* FindAttribute( const char* name ); + XMLAttribute* FindOrCreateAttribute( const char* name ); + //void LinkAttribute( XMLAttribute* attrib ); + char* ParseAttributes( char* p ); + + int _closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. + XMLAttribute* _rootAttribute; +}; + + +enum Whitespace { + PRESERVE_WHITESPACE, + COLLAPSE_WHITESPACE +}; + + +/** A Document binds together all the functionality. + It can be saved, loaded, and printed to the screen. + All Nodes are connected and allocated to a Document. + If the Document is deleted, all its Nodes are also deleted. +*/ +class TINYXML2_LIB XMLDocument : public XMLNode +{ + friend class XMLElement; +public: + /// constructor + XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); + ~XMLDocument(); + + virtual XMLDocument* ToDocument() { + return this; + } + virtual const XMLDocument* ToDocument() const { + return this; + } + + /** + Parse an XML file from a character string. + Returns XML_NO_ERROR (0) on success, or + an errorID. + + You may optionally pass in the 'nBytes', which is + the number of bytes which will be parsed. If not + specified, TinyXML-2 will assume 'xml' points to a + null terminated string. + */ + XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); + + /** + Load an XML file from disk. + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError LoadFile( const char* filename ); + + /** + Load an XML file from disk. You are responsible + for providing and closing the FILE*. + + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError LoadFile( FILE* ); + + /** + Save the XML file to disk. + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError SaveFile( const char* filename, bool compact = false ); + + /** + Save the XML file to disk. You are responsible + for providing and closing the FILE*. + + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError SaveFile( FILE* fp, bool compact = false ); + + bool ProcessEntities() const { + return _processEntities; + } + Whitespace WhitespaceMode() const { + return _whitespace; + } + + /** + Returns true if this document has a leading Byte Order Mark of UTF8. + */ + bool HasBOM() const { + return _writeBOM; + } + /** Sets whether to write the BOM when writing the file. + */ + void SetBOM( bool useBOM ) { + _writeBOM = useBOM; + } + + /** Return the root element of DOM. Equivalent to FirstChildElement(). + To get the first node, use FirstChild(). + */ + XMLElement* RootElement() { + return FirstChildElement(); + } + const XMLElement* RootElement() const { + return FirstChildElement(); + } + + /** Print the Document. If the Printer is not provided, it will + print to stdout. If you provide Printer, this can print to a file: + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Or you can use a printer to print to memory: + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + // printer.CStr() has a const char* to the XML + @endverbatim + */ + void Print( XMLPrinter* streamer=0 ) const; + virtual bool Accept( XMLVisitor* visitor ) const; + + /** + Create a new Element associated with + this Document. The memory for the Element + is managed by the Document. + */ + XMLElement* NewElement( const char* name ); + /** + Create a new Comment associated with + this Document. The memory for the Comment + is managed by the Document. + */ + XMLComment* NewComment( const char* comment ); + /** + Create a new Text associated with + this Document. The memory for the Text + is managed by the Document. + */ + XMLText* NewText( const char* text ); + /** + Create a new Declaration associated with + this Document. The memory for the object + is managed by the Document. + + If the 'text' param is null, the standard + declaration is used.: + @verbatim + + @endverbatim + */ + XMLDeclaration* NewDeclaration( const char* text=0 ); + /** + Create a new Unknown associated with + this Document. The memory for the object + is managed by the Document. + */ + XMLUnknown* NewUnknown( const char* text ); + + /** + Delete a node associated with this document. + It will be unlinked from the DOM. + */ + void DeleteNode( XMLNode* node ) { + node->_parent->DeleteChild( node ); + } + + void SetError( XMLError error, const char* str1, const char* str2 ); + + /// Return true if there was an error parsing the document. + bool Error() const { + return _errorID != XML_NO_ERROR; + } + /// Return the errorID. + XMLError ErrorID() const { + return _errorID; + } + /// Return a possibly helpful diagnostic location or string. + const char* GetErrorStr1() const { + return _errorStr1; + } + /// Return a possibly helpful secondary diagnostic location or string. + const char* GetErrorStr2() const { + return _errorStr2; + } + /// If there is an error, print it to stdout. + void PrintError() const; + + /// Clear the document, resetting it to the initial state. + void Clear(); + + // internal + char* Identify( char* p, XMLNode** node ); + + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + return 0; + } + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + return false; + } + +private: + XMLDocument( const XMLDocument& ); // not supported + void operator=( const XMLDocument& ); // not supported + + bool _writeBOM; + bool _processEntities; + XMLError _errorID; + Whitespace _whitespace; + const char* _errorStr1; + const char* _errorStr2; + char* _charBuffer; + + MemPoolT< sizeof(XMLElement) > _elementPool; + MemPoolT< sizeof(XMLAttribute) > _attributePool; + MemPoolT< sizeof(XMLText) > _textPool; + MemPoolT< sizeof(XMLComment) > _commentPool; +}; + + +/** + A XMLHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + XMLElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + XMLElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + XMLElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + XMLElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity + of such code. A XMLHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + XMLHandle docHandle( &document ); + XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + XMLHandle handleCopy = handle; + @endverbatim + + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. +*/ +class TINYXML2_LIB XMLHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + XMLHandle( XMLNode* node ) { + _node = node; + } + /// Create a handle from a node. + XMLHandle( XMLNode& node ) { + _node = &node; + } + /// Copy constructor + XMLHandle( const XMLHandle& ref ) { + _node = ref._node; + } + /// Assignment + XMLHandle& operator=( const XMLHandle& ref ) { + _node = ref._node; + return *this; + } + + /// Get the first child of this handle. + XMLHandle FirstChild() { + return XMLHandle( _node ? _node->FirstChild() : 0 ); + } + /// Get the first child element of this handle. + XMLHandle FirstChildElement( const char* value=0 ) { + return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 ); + } + /// Get the last child of this handle. + XMLHandle LastChild() { + return XMLHandle( _node ? _node->LastChild() : 0 ); + } + /// Get the last child element of this handle. + XMLHandle LastChildElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 ); + } + /// Get the previous sibling of this handle. + XMLHandle PreviousSibling() { + return XMLHandle( _node ? _node->PreviousSibling() : 0 ); + } + /// Get the previous sibling element of this handle. + XMLHandle PreviousSiblingElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + } + /// Get the next sibling of this handle. + XMLHandle NextSibling() { + return XMLHandle( _node ? _node->NextSibling() : 0 ); + } + /// Get the next sibling element of this handle. + XMLHandle NextSiblingElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + } + + /// Safe cast to XMLNode. This can return null. + XMLNode* ToNode() { + return _node; + } + /// Safe cast to XMLElement. This can return null. + XMLElement* ToElement() { + return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + } + /// Safe cast to XMLText. This can return null. + XMLText* ToText() { + return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + } + /// Safe cast to XMLUnknown. This can return null. + XMLUnknown* ToUnknown() { + return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + } + /// Safe cast to XMLDeclaration. This can return null. + XMLDeclaration* ToDeclaration() { + return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + } + +private: + XMLNode* _node; +}; + + +/** + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the + same in all regards, except for the 'const' qualifiers. See XMLHandle for API. +*/ +class TINYXML2_LIB XMLConstHandle +{ +public: + XMLConstHandle( const XMLNode* node ) { + _node = node; + } + XMLConstHandle( const XMLNode& node ) { + _node = &node; + } + XMLConstHandle( const XMLConstHandle& ref ) { + _node = ref._node; + } + + XMLConstHandle& operator=( const XMLConstHandle& ref ) { + _node = ref._node; + return *this; + } + + const XMLConstHandle FirstChild() const { + return XMLConstHandle( _node ? _node->FirstChild() : 0 ); + } + const XMLConstHandle FirstChildElement( const char* value=0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); + } + const XMLConstHandle LastChild() const { + return XMLConstHandle( _node ? _node->LastChild() : 0 ); + } + const XMLConstHandle LastChildElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); + } + const XMLConstHandle PreviousSibling() const { + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); + } + const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + } + const XMLConstHandle NextSibling() const { + return XMLConstHandle( _node ? _node->NextSibling() : 0 ); + } + const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + } + + + const XMLNode* ToNode() const { + return _node; + } + const XMLElement* ToElement() const { + return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + } + const XMLText* ToText() const { + return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + } + const XMLUnknown* ToUnknown() const { + return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + } + const XMLDeclaration* ToDeclaration() const { + return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + } + +private: + const XMLNode* _node; +}; + + +/** + Printing functionality. The XMLPrinter gives you more + options than the XMLDocument::Print() method. + + It can: + -# Print to memory. + -# Print to a file you provide. + -# Print XML without a XMLDocument. + + Print to Memory + + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + SomeFunction( printer.CStr() ); + @endverbatim + + Print to a File + + You provide the file pointer. + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Print without a XMLDocument + + When loading, an XML parser is very useful. However, sometimes + when saving, it just gets in the way. The code is often set up + for streaming, and constructing the DOM is just overhead. + + The Printer supports the streaming case. The following code + prints out a trivially simple XML file without ever creating + an XML document. + + @verbatim + XMLPrinter printer( fp ); + printer.OpenElement( "foo" ); + printer.PushAttribute( "foo", "bar" ); + printer.CloseElement(); + @endverbatim +*/ +class TINYXML2_LIB XMLPrinter : public XMLVisitor +{ +public: + /** Construct the printer. If the FILE* is specified, + this will print to the FILE. Else it will print + to memory, and the result is available in CStr(). + If 'compact' is set to true, then output is created + with only required whitespace and newlines. + */ + XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); + ~XMLPrinter() {} + + /** If streaming, write the BOM and declaration. */ + void PushHeader( bool writeBOM, bool writeDeclaration ); + /** If streaming, start writing an element. + The element must be closed with CloseElement() + */ + void OpenElement( const char* name ); + /// If streaming, add an attribute to an open element. + void PushAttribute( const char* name, const char* value ); + void PushAttribute( const char* name, int value ); + void PushAttribute( const char* name, unsigned value ); + void PushAttribute( const char* name, bool value ); + void PushAttribute( const char* name, double value ); + /// If streaming, close the Element. + void CloseElement(); + + /// Add a text node. + void PushText( const char* text, bool cdata=false ); + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment + void PushComment( const char* comment ); + + void PushDeclaration( const char* value ); + void PushUnknown( const char* value ); + + virtual bool VisitEnter( const XMLDocument& /*doc*/ ); + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); + virtual bool VisitExit( const XMLElement& element ); + + virtual bool Visit( const XMLText& text ); + virtual bool Visit( const XMLComment& comment ); + virtual bool Visit( const XMLDeclaration& declaration ); + virtual bool Visit( const XMLUnknown& unknown ); + + /** + If in print to memory mode, return a pointer to + the XML file in memory. + */ + const char* CStr() const { + return _buffer.Mem(); + } + /** + If in print to memory mode, return the size + of the XML file in memory. (Note the size returned + includes the terminating null.) + */ + int CStrSize() const { + return _buffer.Size(); + } + +private: + void SealElement(); + void PrintSpace( int depth ); + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. + void Print( const char* format, ... ); + + bool _elementJustOpened; + bool _firstElement; + FILE* _fp; + int _depth; + int _textDepth; + bool _processEntities; + bool _compactMode; + + enum { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; + + DynArray< const char*, 10 > _stack; + DynArray< char, 20 > _buffer; +#ifdef _MSC_VER + DynArray< char, 20 > _accumulator; +#endif +}; + + +} // tinyxml2 + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TINYXML2_INCLUDED