aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authordrumsetmonkey <abelleisle@roadrunner.com>2016-01-10 21:03:33 -0500
committerdrumsetmonkey <abelleisle@roadrunner.com>2016-01-10 21:03:33 -0500
commit75a5569ed0d44494da379822aa5de519e4a4025b (patch)
tree1100ae214ad3a9eceac969d4796ef1ae825e8fb7 /src
parent5c7c99ac5fe1158adbdf0469fb36ddd153511f5d (diff)
parenta0597ff41fb4be979f9dfd70ace8be98a737affb (diff)
Work on player texture
Diffstat (limited to 'src')
-rw-r--r--src/common.cpp20
-rw-r--r--src/entities.cpp215
-rw-r--r--src/gameplay.cpp264
-rw-r--r--src/inventory.cpp4
-rwxr-xr-xsrc/tinyxml2.cpp2466
-rw-r--r--src/ui.cpp27
-rw-r--r--src/world.cpp334
7 files changed, 3098 insertions, 232 deletions
diff --git a/src/common.cpp b/src/common.cpp
index dbcef0b..8defd61 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -4,6 +4,13 @@
#include <chrono>
#ifndef __WIN32__
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <vector>
+#endif // __WIN32__
+
+#ifndef __WIN32__
unsigned int millis(void){
std::chrono::system_clock::time_point now=std::chrono::system_clock::now();
@@ -57,3 +64,16 @@ void safeSetColorA(int r,int g,int b,int a){
if(a<0)a=0;
glColor4ub(r,g,b,a);
}
+
+int getdir(const char *dir, std::vector<std::string> &files){
+ DIR *dp;
+ struct dirent *dirp;
+ if(!(dp = opendir(dir))){
+ std::cout <<"Error ("<<errno<<") opening "<<dir<<std::endl;
+ return errno;
+ }
+ while((dirp = readdir(dp)))
+ files.push_back(std::string(dirp->d_name));
+ closedir(dp);
+ return 0;
+}
diff --git a/src/entities.cpp b/src/entities.cpp
index 3849040..2cb83a8 100644
--- a/src/entities.cpp
+++ b/src/entities.cpp
@@ -77,8 +77,8 @@ Player::Player(){ //sets all of the player specific traits on object creation
subtype = 0;
health = maxHealth = 100;
speed = 1;
- //tex = new Texturec(3, "assets/player1.png", "assets/player.png", "assets/player2.png");
- tex = new Texturec(3, "assets/maybeplayer.png", "assets/maybeplayer.png", "assets/maybeplayer.png");
+ tex = new Texturec(3, "assets/player1.png", "assets/playerk.png", "assets/player2.png");
+ //tex = new Texturec(3, "assets/maybeplayer.png", "assets/maybeplayer.png", "assets/maybeplayer.png");
inv = new Inventory(PLAYER_INV_SIZE);
}
Player::~Player(){
@@ -101,7 +101,7 @@ NPC::NPC(){ //sets all of the NPC specific traits on object creation
tex = new Texturec(1,"assets/NPC.png");
inv = new Inventory(NPC_INV_SIZE);
- randDialog = rand() % 12 - 1;
+ randDialog = 6;//rand() % 12 - 1;
}
NPC::~NPC(){
while(!aiFunc.empty()){
@@ -120,9 +120,12 @@ Structures::Structures(){ //sets the structure type
near = false;
tex = new Texturec(3,"assets/house1.png", "assets/house2.png", "assets/fountain1.png");
+ ntex = new Texturec(1, "assets/house1N.png");
inWorld = NULL;
name = NULL;
+
+ inv = NULL;
}
Structures::~Structures(){
delete tex;
@@ -153,7 +156,7 @@ Mob::Mob(int sub){
break;
case MS_DOOR:
width = HLINE * 12;
- height = HLINE * 19;
+ height = HLINE * 20;
tex = new Texturec(1,"assets/door.png");
break;
case MS_PAGE:
@@ -171,6 +174,19 @@ Mob::~Mob(){
delete[] name;
}
+Object::Object(){
+ type = OBJECTT;
+ alive = true;
+ near = false;
+ width = 0;
+ height = 0;
+
+ maxHealth = health = 1;
+
+ tex = NULL;
+ inv = NULL;
+}
+
Object::Object(ITEM_ID id, bool qo, const char *pd){
identifier = id;
questObject = qo;
@@ -187,6 +203,7 @@ Object::Object(ITEM_ID id, bool qo, const char *pd){
maxHealth = health = 1;
tex = new Texturec(1,getItemTexturePath(id));
+ inv = NULL;
}
Object::~Object(){
delete[] pickupDialog;
@@ -194,9 +211,19 @@ Object::~Object(){
delete[] name;
}
+void Object::reloadTexture(void){
+ if(tex)
+ delete tex;
+
+ tex = new Texturec(1,getItemTexturePath(identifier));
+ width = getItemWidth(identifier);
+ height = getItemHeight(identifier);
+}
+
void Entity::draw(void){ //draws the entities
glPushMatrix();
glColor3ub(255,255,255);
+ //glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0);
if(type==NPCT){
if(NPCp(this)->aiFunc.size()){
glColor3ub(255,255,0);
@@ -223,23 +250,29 @@ void Entity::draw(void){ //draws the entities
//currentWorld->addParticle(loc.x,loc.y-HLINE,HLINE,HLINE,0,0,{0.0f,.17f,0.0f},1000);
if(up){
if(++texState==2)up=false;
+ glActiveTexture(GL_TEXTURE0);
tex->bindNext();
}else{
if(!--texState)up=true;
+ glActiveTexture(GL_TEXTURE0);
tex->bindPrev();
}
}
if(!ground){
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(0);
}else if(vel.x){
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(texState);
}else{
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(1);
}
break;
case MOBT:
switch(subtype){
case MS_RABBIT:
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(!ground);
break;
case MS_TRIGGER:
@@ -249,6 +282,7 @@ void Entity::draw(void){ //draws the entities
case MS_DOOR:
case MS_PAGE:
default:
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(0);
break;
}
@@ -257,26 +291,39 @@ void Entity::draw(void){ //draws the entities
for(auto &strt : currentWorld->build){
if(this == strt){
if(strt->bsubtype == HOUSE){
+ glActiveTexture(GL_TEXTURE1);
+ ntex->bind(0);
+ //When rendering an objectwith this program.
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(0);
+ //glBindSampler(0, linearFiltering);
+
+
}else if(strt->bsubtype == HOUSE2){
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(1);
}else if(strt->bsubtype == FOUNTAIN){
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(2);
}
}
}
break;
default:
+ glActiveTexture(GL_TEXTURE0 + 0);
tex->bind(0);
break;
}
glColor3ub(255,255,255);
+ glUseProgram(shaderProgram);
+ glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0);
glBegin(GL_QUADS);
glTexCoord2i(0,1);glVertex2i(loc.x, loc.y);
glTexCoord2i(1,1);glVertex2i(loc.x + width, loc.y);
glTexCoord2i(1,0);glVertex2i(loc.x + width, loc.y + height);
glTexCoord2i(0,0);glVertex2i(loc.x, loc.y + height);
glEnd();
+ glUseProgram(0);
NOPE:
glDisable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
@@ -341,7 +388,7 @@ const char *randomDialog[] = {
"How much wood could a woodchuck chuck if a woodchuck could chuck wood?",
"I don\'t think anyone has ever been able to climb up that hill.",
"If you ever see a hole in the ground, watch out; it could mean the end for you.",
- "Did you know this game has over 4000 lines of code? I didn\'t. I didn't even know I was in a game until now...",
+ "Did you know this game has over 5000 lines of code? I didn\'t. I didn't even know I was in a game until now...",
"HELP MY CAPS LOCK IS STUCK",
"You know, if anyone ever asked me who I wanted to be when I grow up, I would say Abby Ross.",
"I want to have the wallpaper in our house changed. It doesn\'t really fit the environment.",
@@ -490,3 +537,161 @@ void Mob::wander(int timeRun){
break;
}
}
+
+char *Entity::baseSave(void){
+ static EntitySavePacket *esp;
+ esp = new EntitySavePacket();
+ if(inv)
+ memcpy(&esp->isp,inv->save(),sizeof(InventorySavePacket));
+ else
+ memset(&esp->isp,0,sizeof(InventorySavePacket));
+ esp->loc = loc;
+ esp->vel = vel;
+ esp->width = width;
+ esp->height = height;
+ esp->speed = speed;
+ esp->health = health;
+ esp->maxHealth = maxHealth;
+ esp->subtype = subtype;
+ esp->ticksToUse = ticksToUse;
+ esp->randDialog = randDialog;
+ esp->ground = ground;
+ esp->near = near;
+ esp->canMove = canMove;
+ esp->right = right;
+ esp->left = left;
+ esp->alive = alive;
+ esp->hit = hit;
+ esp->type = type;
+ esp->gender = gender;
+ if(name){
+ esp->nameSize = strlen(name) + 1;
+ strncpy(esp->name,name,32);
+ }else{
+ esp->nameSize = 0;
+ strcpy(esp->name,"\0");
+ }
+ return (char *)esp;
+}
+
+void Entity::baseLoad(char *e){
+ EntitySavePacket *esp = (EntitySavePacket *)e;
+ if(esp->nameSize > 1)
+ inv->load(&esp->isp);
+ loc = esp->loc;
+ vel = esp->vel;
+ width = esp->width;
+ height = esp->height;
+ speed = esp->speed;
+ health = esp->health;
+ maxHealth = esp->maxHealth;
+ subtype = esp->subtype;
+ ticksToUse = esp->ticksToUse;
+ randDialog = esp->randDialog;
+ ground = esp->ground;
+ near = esp->near;
+ canMove = esp->canMove;
+ right = esp->right;
+ left = esp->left;
+ alive = esp->alive;
+ hit = esp->hit;
+ type = esp->type;
+ gender = esp->gender;
+ if(esp->nameSize){
+ name = new char[esp->nameSize+1];
+ strcpy(name,esp->name);
+ }else{
+ name = new char[4];
+ strncpy(name,"\0\0\0\0",4);
+ }
+}
+
+char *NPC::save(unsigned int *size){
+ static char *buf,*esp;
+ buf = new char[(*size = sizeof(EntitySavePacket) /*+ aiFunc.size() * sizeof(int(*)(NPC *))*/)];
+ memcpy(buf,(esp = baseSave()),sizeof(EntitySavePacket));
+ delete[] esp;
+ //memcpy(buf+sizeof(EntitySavePacket),aiFunc.data(),aiFunc.size() * sizeof(int(*)(NPC *)));
+ return buf;
+}
+
+void NPC::load(unsigned int size,char *b){
+ //unsigned int size2,i;
+ //int (*func)(NPC *);
+ baseLoad(b);
+ size--;
+ /*if(size > sizeof(EntitySavePacket)){
+ size2 = (size - sizeof(EntitySavePacket)) / sizeof(int(*)(NPC *));
+ std::cout<<size<<" "<<sizeof(EntitySavePacket)<<" "<<sizeof(int(*)(NPC *))<<" = "<<size2<<std::endl;
+ aiFunc.reserve(size2);
+ if(aiFunc.max_size() < size2){
+ std::cout<<"what"<<std::endl;
+ abort();
+ }
+ for(i=0;i<size2;i++){
+
+ aiFunc.push_back(
+ }
+ memcpy(aiFunc.data(),b+sizeof(EntitySavePacket),size2 * sizeof(int(*)(NPC *)));
+ //aiFunc.erase(aiFunc.begin());
+ std::cout<<aiFunc.size()<<std::endl;
+ }*/
+}
+
+char *Structures::save(void){
+ static StructuresSavePacket *ssp;
+ char *esp;
+ ssp = new StructuresSavePacket();
+ esp = baseSave();
+ memcpy(&ssp->esp,esp,sizeof(EntitySavePacket));
+ delete[] esp;
+ ssp->inWorld = inWorld;
+ ssp->inside = inside;
+ ssp->bsubtype = bsubtype;
+ return (char *)ssp;
+}
+
+void Structures::load(char *s){
+ StructuresSavePacket *ssp = (StructuresSavePacket *)s;
+ baseLoad((char *)&ssp->esp);
+ inWorld = ssp->inWorld;
+ inside = ssp->inside;
+ bsubtype = ssp->bsubtype;
+}
+
+char *Object::save(void){
+ static ObjectSavePacket *osp;
+ char *esp;
+ osp = new ObjectSavePacket();
+ memcpy(&osp->esp,(esp = baseSave()),sizeof(EntitySavePacket));
+ delete[] esp;
+ osp->identifier = identifier;
+ osp->questObject = questObject;
+ strncpy(osp->pickupDialog,pickupDialog,256);
+ return (char *)osp;
+}
+
+void Object::load(char *buf){
+ ObjectSavePacket *osp = (ObjectSavePacket *)buf;
+ baseLoad((char *)&osp->esp);
+ identifier = osp->identifier;
+ questObject = osp->questObject;
+ pickupDialog = new char[256];
+ strcpy(pickupDialog,osp->pickupDialog);
+}
+
+char *Mob::save(void){
+ static MobSavePacket *msp;
+ char *esp;
+ msp = new MobSavePacket();
+ memcpy(&msp->esp,(esp = baseSave()),sizeof(MobSavePacket));
+ delete[] esp;
+ msp->init_y = init_y;
+ return (char *)msp;
+}
+
+void Mob::load(char *m){
+ MobSavePacket *msp = (MobSavePacket *)m;
+ baseLoad((char *)&msp->esp);
+ init_y = msp->init_y;
+}
diff --git a/src/gameplay.cpp b/src/gameplay.cpp
index 5184136..10de69d 100644
--- a/src/gameplay.cpp
+++ b/src/gameplay.cpp
@@ -3,98 +3,12 @@
#include <world.h>
#include <ui.h>
-extern World *currentWorld;
-extern Player *player;
-
-/*
- * int (npc*)
- *
- * dialog
- * wait...
- *
- * switch optchosen
- *
- * qh.assign
- * addAIFunc?
- *
- * return 1 = repeat
- */
-
-void story(Mob *callee){
- player->vel.x = 0;
- Mix_FadeOutMusic(0);
- ui::importantText("It was a dark and stormy night...");
- ui::waitForDialog();
- callee->alive = false;
-}
-
-/*
- * Gens
- */
-
-float gen_worldSpawnHill1(float x){
- return (float)(pow(2,(-x+200)/5) + GEN_MIN);
-}
-
-float gen_worldSpawnHill3(float x){
- float tmp = 60*atan(-(x/30-20))+GEN_MIN*2;
- return tmp>GEN_MIN?tmp:GEN_MIN;
-}
-
-/*
- * Thing-thangs
- */
-
-void worldSpawnHill1_hillBlock(Mob *callee){
- player->vel.x = 0;
- player->loc.x = callee->loc.x + callee->width;
- ui::dialogBox(player->name,NULL,false,"This hill seems to steep to climb up...");
- callee->alive = true;
-}
-
-static Arena *a;
-void worldSpawnHill2_infoSprint(Mob *callee){
-
- ui::dialogBox(player->name,":Sure:Nah",false,"This page would like to take you somewhere.");
- ui::waitForDialog();
- switch(ui::dialogOptChosen){
- case 1:
- ui::dialogBox(player->name,NULL,true,"Cool.");
- callee->alive = false;
- a = new Arena(currentWorld,player);
- a->setBackground(BG_FOREST);
- a->setBGM("assets/music/embark.wav");
- ui::toggleWhiteFast();
- ui::waitForCover();
- currentWorld = a;
- ui::toggleWhiteFast();
- break;
- case 2:
- default:
- ui::dialogBox(player->name,NULL,false,"Okay then.");
- break;
- }
-
- //ui::dialogBox("B-) ",NULL,true,"Press \'Shift\' to run!");
-}
+#include <tinyxml2.h>
-int worldSpawnHill2_Quest2(NPC *callee){
- ui::dialogBox(callee->name,NULL,false,"Yo.");
- ui::waitForDialog();
- return 0;
-}
+using namespace tinyxml2;
-int worldSpawnHill2_Quest1(NPC *callee){
- ui::dialogBox(callee->name,":Cool.",false,"Did you know that I\'m the coolest NPC in the world?");
- ui::waitForDialog();
- if(ui::dialogOptChosen == 1){
- ui::dialogBox(callee->name,NULL,false,"Yeah, it is.");
- currentWorld->getAvailableNPC()->addAIFunc(worldSpawnHill2_Quest2,true);
- ui::waitForDialog();
- return 0;
- }
- return 1;
-}
+extern World *currentWorld;
+extern Player *player;
/*
* new world
@@ -109,72 +23,135 @@ int worldSpawnHill2_Quest1(NPC *callee){
* World definitions
*/
-static World *worldSpawnHill1;
-static World *worldSpawnHill2;
-static World *worldSpawnHill3;
-
-static IndoorWorld *worldSpawnHill2_Building1;
-
/*
* initEverything() start
*/
-void destroyEverything(void);
-void initEverything(void){
-
- worldSpawnHill1 = new World();
- worldSpawnHill1->generateFunc(400,gen_worldSpawnHill1);
- worldSpawnHill1->setBackground(BG_FOREST);
- worldSpawnHill1->setBGM("assets/music/embark.wav");
- worldSpawnHill1->addMob(MS_TRIGGER,0,0,worldSpawnHill1_hillBlock);
-
- worldSpawnHill2 = new World();
- worldSpawnHill2->generate(700);
- worldSpawnHill2->setBackground(BG_FOREST);
- worldSpawnHill2->setBGM("assets/music/ozone.wav");
- worldSpawnHill2->addMob(MS_PAGE,-400,0,worldSpawnHill2_infoSprint);
-
- worldSpawnHill3 = new World();
- worldSpawnHill3->generateFunc(1000,gen_worldSpawnHill3);
- worldSpawnHill3->setBackground(BG_FOREST);
- worldSpawnHill3->setBGM("assets/music/embark.wav");
- //worldSpawnHill3->addMob(MS_TRIGGER,-500,0,worldSpawnHill3_itemGet);
- //worldSpawnHill3->addMob(MS_TRIGGER,0,0,worldSpawnHill3_itemSee);
- worldSpawnHill3->addObject(FLASHLIGHT,false,"",-200,300);
- //worldSpawnHill3->addMob(MS_TRIGGER,400,0,worldSpawnHill3_leave);
-
- worldSpawnHill3->addHole(800,1000);
-
- worldSpawnHill1->toRight = worldSpawnHill2;
- worldSpawnHill2->toLeft = worldSpawnHill1;
- worldSpawnHill2->toRight = worldSpawnHill3;
- worldSpawnHill3->toLeft = worldSpawnHill2;
-
- /*
- * Spawn some entities.
- */
-
- //playerSpawnHill->addMob(MS_TRIGGER,player->loc.x,0,story);
+typedef struct {
+ World *ptr;
+ char *file;
+} WorldXML;
- //playerSpawnHill->addStructure(STRUCTURET,FOUNTAIN,(rand()%120*HLINE)+100*HLINE,100,test,iw);
- //playerSpawnHill->addStructure(STRUCTURET,HOUSE2,(rand()%120*HLINE)+300*HLINE,100,test,iw);
- //playerSpawnHill->addVillage(5,1,4,STRUCTURET,rand()%500+120,(float)200,playerSpawnHill,iw);
- //playerSpawnHill->addMob(MS_TRIGGER,-1300,0,CUTSCENEEE);*/
+typedef struct {
+ NPC *npc;
+ unsigned int index;
+} NPCDialog;
+std::vector<NPCDialog> npcd;
+std::vector<WorldXML> earthxml;
+std::vector<World *> earth;
- worldSpawnHill2_Building1 = new IndoorWorld();
- worldSpawnHill2_Building1->generate(300);
- worldSpawnHill2_Building1->setBackground(BG_WOODHOUSE);
- worldSpawnHill2_Building1->setBGM("assets/music/theme_jazz.wav");
+int commonAIFunc(NPC *speaker){
+ XMLDocument xml;
+ XMLElement *exml;
+ unsigned int idx;
+
+ for(auto &n : npcd){
+ if(n.npc == speaker){
+ idx = n.index;
+ break;
+ }
+ }
+
+ for(auto &e : earthxml){
+ if(e.ptr == currentWorld){
+ xml.LoadFile(e.file);
+ exml = xml.FirstChildElement("Dialog")->FirstChildElement();
+
+ do{
+ if(!strcmp(exml->Name(),"text")){
+ if(!strcmp(exml->Attribute("name"),speaker->name) && exml->UnsignedAttribute("id") == idx){
+ ui::dialogBox(speaker->name,"",false,exml->GetText());
+ ui::waitForDialog();
+ if(exml->QueryUnsignedAttribute("nextid",&idx) == XML_NO_ERROR){
+ for(auto &n : npcd){
+ if(n.npc == speaker){
+ n.index = idx;
+ break;
+ }
+ }
+ return 1;
+ }
+ return 0;
+ }
+ }
+ exml = exml->NextSiblingElement();
+ }while(exml);
+ }
+ }
+ return 0;
+}
- worldSpawnHill2->addStructure(STRUCTURET,HOUSE,(rand()%120*HLINE),100,worldSpawnHill2_Building1);
- worldSpawnHill2->getAvailableNPC()->addAIFunc(worldSpawnHill2_Quest1,false);
+void destroyEverything(void);
+void initEverything(void){
+ const char *name;
+ std::vector<std::string> xmlFiles;
+ static char *file;
+ bool dialog;
+ XMLDocument xml;
+ XMLElement *wxml;
+
+ if(getdir("./xml/",xmlFiles)){
+ std::cout<<"Error reading XML files!!!1"<<std::endl;
+ abort();
+ }
+
+ for(auto x : xmlFiles){
+ if(strncmp(x.c_str(),".",1) && strncmp(x.c_str(),"..",2)){
+ file = new char[5 + x.size()];
+ strncpy(file,"xml/",4);
+ strcpy(file+4,x.c_str());
+ xml.LoadFile(file);
+
+ wxml = xml.FirstChildElement("World")->FirstChildElement();
+
+ earth.push_back(new World());
+
+ do{
+ name = wxml->Name();
+
+ if(!strcmp(name,"style")){
+ earth.back()->setBackground((WORLD_BG_TYPE)wxml->UnsignedAttribute("background"));
+ earth.back()->setBGM(wxml->Attribute("bgm"));
+ }else if(!strcmp(name,"generation")){
+ if(!strcmp(wxml->Attribute("type"),"Random")){
+ earth.back()->generate(wxml->UnsignedAttribute("width"));
+ }
+ }else if(!strcmp(name,"mob")){
+ earth.back()->addMob(wxml->UnsignedAttribute("type"),wxml->FloatAttribute("x"),wxml->FloatAttribute("y"));
+ }else if(!strcmp(name,"npc")){
+ earth.back()->addNPC(wxml->FloatAttribute("x"),wxml->FloatAttribute("y"));
+ if(wxml->Attribute("name")){
+ delete[] earth.back()->npc.back()->name;
+ earth.back()->npc.back()->name = new char[strlen(wxml->Attribute("name"))+1];
+ strcpy(earth.back()->npc.back()->name,wxml->Attribute("name"));
+ }
+ dialog = false;
+ if(wxml->QueryBoolAttribute("hasDialog",&dialog) == XML_NO_ERROR && dialog){
+ for(auto &ex : earthxml){
+ if(ex.ptr == earth.back())
+ goto SKIP;
+ }
+ earthxml.push_back((WorldXML){earth.back(),new char[64]});
+ strcpy(earthxml.back().file,file);
+SKIP:
+ earth.back()->npc.back()->addAIFunc(commonAIFunc,false);
+ npcd.push_back((NPCDialog){earth.back()->npc.back(),0});
+ }
+ }
+
+ wxml = wxml->NextSiblingElement();
+ }while(wxml);
+
+ delete[] file;
+ }
+ }
player = new Player();
player->spawn(200,100);
- currentWorld = worldSpawnHill1;
+ currentWorld = earth.front();
currentWorld->bgmPlay(NULL);
atexit(destroyEverything);
}
@@ -183,6 +160,7 @@ extern std::vector<int (*)(NPC *)> AIpreload;
extern std::vector<NPC *> AIpreaddr;
void destroyEverything(void){
+
while(!AIpreload.empty())
AIpreload.pop_back();
while(!AIpreaddr.empty())
diff --git a/src/inventory.cpp b/src/inventory.cpp
index c0f4163..b6819d1 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -291,7 +291,8 @@ void itemDraw(Player *p,ITEM_ID id,ITEM_TYPE type){
default:
hangle = 0.0f;
}
-
+ glUseProgram(shaderProgram);
+ glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0);
glTranslatef(itemLoc.x,itemLoc.y,0);
glRotatef(hangle, 0.0f, 0.0f, 1.0f);
glTranslatef(-itemLoc.x,-itemLoc.y,0);
@@ -307,6 +308,7 @@ void itemDraw(Player *p,ITEM_ID id,ITEM_TYPE type){
glDisable(GL_TEXTURE_2D);
glTranslatef(player->loc.x*2,0,0);
glPopMatrix();
+ glUseProgram(0);
}
int Inventory::useItem(void){
diff --git a/src/tinyxml2.cpp b/src/tinyxml2.cpp
new file mode 100755
index 0000000..c4ea7cd
--- /dev/null
+++ b/src/tinyxml2.cpp
@@ -0,0 +1,2466 @@
+/*
+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.
+*/
+
+#include "tinyxml2.h"
+
+#include <new> // yes, this one new style header, is in the Android SDK.
+#if defined(ANDROID_NDK) || defined(__QNXNTO__)
+# include <stddef.h>
+# include <stdarg.h>
+#else
+# include <cstddef>
+# include <cstdarg>
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+ // Microsoft Visual Studio, version 2005 and higher. Not WinCE.
+ /*int _snprintf_s(
+ char *buffer,
+ size_t sizeOfBuffer,
+ size_t count,
+ const char *format [,
+ argument] ...
+ );*/
+ static 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;
+ }
+
+ static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
+ {
+ int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+ return result;
+ }
+
+ #define TIXML_VSCPRINTF _vscprintf
+ #define TIXML_SSCANF sscanf_s
+#elif defined _MSC_VER
+ // Microsoft Visual Studio 2003 and earlier or WinCE
+ #define TIXML_SNPRINTF _snprintf
+ #define TIXML_VSNPRINTF _vsnprintf
+ #define TIXML_SSCANF sscanf
+ #if (_MSC_VER < 1400 ) && (!defined WINCE)
+ // Microsoft Visual Studio 2003 and not WinCE.
+ #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
+ #else
+ // Microsoft Visual Studio 2003 and earlier or WinCE.
+ static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+ {
+ int len = 512;
+ for (;;) {
+ len = len*2;
+ char* str = new char[len]();
+ const int required = _vsnprintf(str, len, format, va);
+ delete[] str;
+ if ( required != -1 ) {
+ TIXMLASSERT( required >= 0 );
+ len = required;
+ break;
+ }
+ }
+ TIXMLASSERT( len >= 0 );
+ return len;
+ }
+ #endif
+#else
+ // GCC version 3 and higher
+ //#warning( "Using sn* functions." )
+ #define TIXML_SNPRINTF snprintf
+ #define TIXML_VSNPRINTF vsnprintf
+ static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+ {
+ int len = vsnprintf( 0, 0, format, va );
+ TIXMLASSERT( len >= 0 );
+ return len;
+ }
+ #define TIXML_SSCANF sscanf
+#endif
+
+
+static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
+static const char LF = LINE_FEED;
+static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
+static const char CR = CARRIAGE_RETURN;
+static const char SINGLE_QUOTE = '\'';
+static const char DOUBLE_QUOTE = '\"';
+
+// Bunch of unicode info at:
+// http://www.unicode.org/faq/utf_bom.html
+// ef bb bf (Microsoft "lead bytes") - designates UTF-8
+
+static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+namespace tinyxml2
+{
+
+struct Entity {
+ const char* pattern;
+ int length;
+ char value;
+};
+
+static const int NUM_ENTITIES = 5;
+static const Entity entities[NUM_ENTITIES] = {
+ { "quot", 4, DOUBLE_QUOTE },
+ { "amp", 3, '&' },
+ { "apos", 4, SINGLE_QUOTE },
+ { "lt", 2, '<' },
+ { "gt", 2, '>' }
+};
+
+
+StrPair::~StrPair()
+{
+ Reset();
+}
+
+
+void StrPair::TransferTo( StrPair* other )
+{
+ if ( this == other ) {
+ return;
+ }
+ // This in effect implements the assignment operator by "moving"
+ // ownership (as in auto_ptr).
+
+ TIXMLASSERT( other->_flags == 0 );
+ TIXMLASSERT( other->_start == 0 );
+ TIXMLASSERT( other->_end == 0 );
+
+ other->Reset();
+
+ other->_flags = _flags;
+ other->_start = _start;
+ other->_end = _end;
+
+ _flags = 0;
+ _start = 0;
+ _end = 0;
+}
+
+void StrPair::Reset()
+{
+ if ( _flags & NEEDS_DELETE ) {
+ delete [] _start;
+ }
+ _flags = 0;
+ _start = 0;
+ _end = 0;
+}
+
+
+void StrPair::SetStr( const char* str, int flags )
+{
+ TIXMLASSERT( str );
+ Reset();
+ size_t len = strlen( str );
+ TIXMLASSERT( _start == 0 );
+ _start = new char[ len+1 ];
+ memcpy( _start, str, len+1 );
+ _end = _start + len;
+ _flags = flags | NEEDS_DELETE;
+}
+
+
+char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
+{
+ TIXMLASSERT( endTag && *endTag );
+
+ char* start = p;
+ char endChar = *endTag;
+ size_t length = strlen( endTag );
+
+ // Inner loop of text parsing.
+ while ( *p ) {
+ if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
+ Set( start, p, strFlags );
+ return p + length;
+ }
+ ++p;
+ }
+ return 0;
+}
+
+
+char* StrPair::ParseName( char* p )
+{
+ if ( !p || !(*p) ) {
+ return 0;
+ }
+ if ( !XMLUtil::IsNameStartChar( *p ) ) {
+ return 0;
+ }
+
+ char* const start = p;
+ ++p;
+ while ( *p && XMLUtil::IsNameChar( *p ) ) {
+ ++p;
+ }
+
+ Set( start, p, 0 );
+ return p;
+}
+
+
+void StrPair::CollapseWhitespace()
+{
+ // Adjusting _start would cause undefined behavior on delete[]
+ TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
+ // Trim leading space.
+ _start = XMLUtil::SkipWhiteSpace( _start );
+
+ if ( *_start ) {
+ char* p = _start; // the read pointer
+ char* q = _start; // the write pointer
+
+ while( *p ) {
+ if ( XMLUtil::IsWhiteSpace( *p )) {
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( *p == 0 ) {
+ break; // don't write to q; this trims the trailing space.
+ }
+ *q = ' ';
+ ++q;
+ }
+ *q = *p;
+ ++q;
+ ++p;
+ }
+ *q = 0;
+ }
+}
+
+
+const char* StrPair::GetStr()
+{
+ TIXMLASSERT( _start );
+ TIXMLASSERT( _end );
+ if ( _flags & NEEDS_FLUSH ) {
+ *_end = 0;
+ _flags ^= NEEDS_FLUSH;
+
+ if ( _flags ) {
+ char* p = _start; // the read pointer
+ char* q = _start; // the write pointer
+
+ while( p < _end ) {
+ if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
+ // CR-LF pair becomes LF
+ // CR alone becomes LF
+ // LF-CR becomes LF
+ if ( *(p+1) == LF ) {
+ p += 2;
+ }
+ else {
+ ++p;
+ }
+ *q++ = LF;
+ }
+ else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
+ if ( *(p+1) == CR ) {
+ p += 2;
+ }
+ else {
+ ++p;
+ }
+ *q++ = LF;
+ }
+ else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
+ // Entities handled by tinyXML2:
+ // - special entities in the entity table [in/out]
+ // - numeric character reference [in]
+ // &#20013; or &#x4e2d;
+
+ if ( *(p+1) == '#' ) {
+ const int buflen = 10;
+ char buf[buflen] = { 0 };
+ int len = 0;
+ char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
+ if ( adjusted == 0 ) {
+ *q = *p;
+ ++p;
+ ++q;
+ }
+ else {
+ TIXMLASSERT( 0 <= len && len <= buflen );
+ TIXMLASSERT( q + len <= adjusted );
+ p = adjusted;
+ memcpy( q, buf, len );
+ q += len;
+ }
+ }
+ else {
+ bool entityFound = false;
+ for( int i = 0; i < NUM_ENTITIES; ++i ) {
+ const Entity& entity = entities[i];
+ if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
+ && *( p + entity.length + 1 ) == ';' ) {
+ // Found an entity - convert.
+ *q = entity.value;
+ ++q;
+ p += entity.length + 2;
+ entityFound = true;
+ break;
+ }
+ }
+ if ( !entityFound ) {
+ // fixme: treat as error?
+ ++p;
+ ++q;
+ }
+ }
+ }
+ else {
+ *q = *p;
+ ++p;
+ ++q;
+ }
+ }
+ *q = 0;
+ }
+ // The loop below has plenty going on, and this
+ // is a less useful mode. Break it out.
+ if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
+ CollapseWhitespace();
+ }
+ _flags = (_flags & NEEDS_DELETE);
+ }
+ TIXMLASSERT( _start );
+ return _start;
+}
+
+
+
+
+// --------- XMLUtil ----------- //
+
+const char* XMLUtil::ReadBOM( const char* p, bool* bom )
+{
+ TIXMLASSERT( p );
+ TIXMLASSERT( bom );
+ *bom = false;
+ const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
+ // Check for BOM:
+ if ( *(pu+0) == TIXML_UTF_LEAD_0
+ && *(pu+1) == TIXML_UTF_LEAD_1
+ && *(pu+2) == TIXML_UTF_LEAD_2 ) {
+ *bom = true;
+ p += 3;
+ }
+ TIXMLASSERT( p );
+ return p;
+}
+
+
+void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+ const unsigned long BYTE_MASK = 0xBF;
+ const unsigned long BYTE_MARK = 0x80;
+ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+ if (input < 0x80) {
+ *length = 1;
+ }
+ else if ( input < 0x800 ) {
+ *length = 2;
+ }
+ else if ( input < 0x10000 ) {
+ *length = 3;
+ }
+ else if ( input < 0x200000 ) {
+ *length = 4;
+ }
+ else {
+ *length = 0; // This code won't convert this correctly anyway.
+ return;
+ }
+
+ output += *length;
+
+ // Scary scary fall throughs.
+ switch (*length) {
+ case 4:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 3:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 2:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 1:
+ --output;
+ *output = (char)(input | FIRST_BYTE_MARK[*length]);
+ break;
+ default:
+ TIXMLASSERT( false );
+ }
+}
+
+
+const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
+{
+ // Presume an entity, and pull it out.
+ *length = 0;
+
+ if ( *(p+1) == '#' && *(p+2) ) {
+ unsigned long ucs = 0;
+ TIXMLASSERT( sizeof( ucs ) >= 4 );
+ ptrdiff_t delta = 0;
+ unsigned mult = 1;
+ static const char SEMICOLON = ';';
+
+ if ( *(p+2) == 'x' ) {
+ // Hexadecimal.
+ const char* q = p+3;
+ if ( !(*q) ) {
+ return 0;
+ }
+
+ q = strchr( q, SEMICOLON );
+
+ if ( !q ) {
+ return 0;
+ }
+ TIXMLASSERT( *q == SEMICOLON );
+
+ delta = q-p;
+ --q;
+
+ while ( *q != 'x' ) {
+ unsigned int digit = 0;
+
+ if ( *q >= '0' && *q <= '9' ) {
+ digit = *q - '0';
+ }
+ else if ( *q >= 'a' && *q <= 'f' ) {
+ digit = *q - 'a' + 10;
+ }
+ else if ( *q >= 'A' && *q <= 'F' ) {
+ digit = *q - 'A' + 10;
+ }
+ else {
+ return 0;
+ }
+ TIXMLASSERT( digit >= 0 && digit < 16);
+ TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+ const unsigned int digitScaled = mult * digit;
+ TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+ ucs += digitScaled;
+ TIXMLASSERT( mult <= UINT_MAX / 16 );
+ mult *= 16;
+ --q;
+ }
+ }
+ else {
+ // Decimal.
+ const char* q = p+2;
+ if ( !(*q) ) {
+ return 0;
+ }
+
+ q = strchr( q, SEMICOLON );
+
+ if ( !q ) {
+ return 0;
+ }
+ TIXMLASSERT( *q == SEMICOLON );
+
+ delta = q-p;
+ --q;
+
+ while ( *q != '#' ) {
+ if ( *q >= '0' && *q <= '9' ) {
+ const unsigned int digit = *q - '0';
+ TIXMLASSERT( digit >= 0 && digit < 10);
+ TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+ const unsigned int digitScaled = mult * digit;
+ TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+ ucs += digitScaled;
+ }
+ else {
+ return 0;
+ }
+ TIXMLASSERT( mult <= UINT_MAX / 10 );
+ mult *= 10;
+ --q;
+ }
+ }
+ // convert the UCS to UTF-8
+ ConvertUTF32ToUTF8( ucs, value, length );
+ return p + delta + 1;
+ }
+ return p+1;
+}
+
+
+void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
+}
+
+
+void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
+}
+
+
+void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
+}
+
+/*
+ ToStr() of a number is a very tricky topic.
+ https://github.com/leethomason/tinyxml2/issues/106
+*/
+void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
+}
+
+
+void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
+{
+ TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
+}
+
+
+bool XMLUtil::ToInt( const char* str, int* value )
+{
+ if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
+{
+ if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtil::ToBool( const char* str, bool* value )
+{
+ int ival = 0;
+ if ( ToInt( str, &ival )) {
+ *value = (ival==0) ? false : true;
+ return true;
+ }
+ if ( StringEqual( str, "true" ) ) {
+ *value = true;
+ return true;
+ }
+ else if ( StringEqual( str, "false" ) ) {
+ *value = false;
+ return true;
+ }
+ return false;
+}
+
+
+bool XMLUtil::ToFloat( const char* str, float* value )
+{
+ if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+bool XMLUtil::ToDouble( const char* str, double* value )
+{
+ if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
+ return true;
+ }
+ return false;
+}
+
+
+char* XMLDocument::Identify( char* p, XMLNode** node )
+{
+ TIXMLASSERT( node );
+ TIXMLASSERT( p );
+ char* const start = p;
+ p = XMLUtil::SkipWhiteSpace( p );
+ if( !*p ) {
+ *node = 0;
+ TIXMLASSERT( p );
+ return p;
+ }
+
+ // These strings define the matching patterns:
+ static const char* xmlHeader = { "<?" };
+ static const char* commentHeader = { "<!--" };
+ static const char* cdataHeader = { "<![CDATA[" };
+ static const char* dtdHeader = { "<!" };
+ static const char* elementHeader = { "<" }; // and a header for everything else; check last.
+
+ static const int xmlHeaderLen = 2;
+ static const int commentHeaderLen = 4;
+ static const int cdataHeaderLen = 9;
+ static const int dtdHeaderLen = 2;
+ static const int elementHeaderLen = 1;
+
+ TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
+ TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
+ XMLNode* returnNode = 0;
+ if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
+ TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
+ returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
+ returnNode->_memPool = &_commentPool;
+ p += xmlHeaderLen;
+ }
+ else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
+ TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
+ returnNode = new (_commentPool.Alloc()) XMLComment( this );
+ returnNode->_memPool = &_commentPool;
+ p += commentHeaderLen;
+ }
+ else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
+ TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
+ XMLText* text = new (_textPool.Alloc()) XMLText( this );
+ returnNode = text;
+ returnNode->_memPool = &_textPool;
+ p += cdataHeaderLen;
+ text->SetCData( true );
+ }
+ else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
+ TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
+ returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
+ returnNode->_memPool = &_commentPool;
+ p += dtdHeaderLen;
+ }
+ else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
+ TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
+ returnNode = new (_elementPool.Alloc()) XMLElement( this );
+ returnNode->_memPool = &_elementPool;
+ p += elementHeaderLen;
+ }
+ else {
+ TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
+ returnNode = new (_textPool.Alloc()) XMLText( this );
+ returnNode->_memPool = &_textPool;
+ p = start; // Back it up, all the text counts.
+ }
+
+ TIXMLASSERT( returnNode );
+ TIXMLASSERT( p );
+ *node = returnNode;
+ return p;
+}
+
+
+bool XMLDocument::Accept( XMLVisitor* visitor ) const
+{
+ TIXMLASSERT( visitor );
+ if ( visitor->VisitEnter( *this ) ) {
+ for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+ if ( !node->Accept( visitor ) ) {
+ break;
+ }
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLNode ----------- //
+
+XMLNode::XMLNode( XMLDocument* doc ) :
+ _document( doc ),
+ _parent( 0 ),
+ _firstChild( 0 ), _lastChild( 0 ),
+ _prev( 0 ), _next( 0 ),
+ _memPool( 0 )
+{
+}
+
+
+XMLNode::~XMLNode()
+{
+ DeleteChildren();
+ if ( _parent ) {
+ _parent->Unlink( this );
+ }
+}
+
+const char* XMLNode::Value() const
+{
+ // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr.
+ if ( this->ToDocument() )
+ return 0;
+ return _value.GetStr();
+}
+
+void XMLNode::SetValue( const char* str, bool staticMem )
+{
+ if ( staticMem ) {
+ _value.SetInternedStr( str );
+ }
+ else {
+ _value.SetStr( str );
+ }
+}
+
+
+void XMLNode::DeleteChildren()
+{
+ while( _firstChild ) {
+ TIXMLASSERT( _lastChild );
+ TIXMLASSERT( _firstChild->_document == _document );
+ XMLNode* node = _firstChild;
+ Unlink( node );
+
+ DeleteNode( node );
+ }
+ _firstChild = _lastChild = 0;
+}
+
+
+void XMLNode::Unlink( XMLNode* child )
+{
+ TIXMLASSERT( child );
+ TIXMLASSERT( child->_document == _document );
+ TIXMLASSERT( child->_parent == this );
+ if ( child == _firstChild ) {
+ _firstChild = _firstChild->_next;
+ }
+ if ( child == _lastChild ) {
+ _lastChild = _lastChild->_prev;
+ }
+
+ if ( child->_prev ) {
+ child->_prev->_next = child->_next;
+ }
+ if ( child->_next ) {
+ child->_next->_prev = child->_prev;
+ }
+ child->_parent = 0;
+}
+
+
+void XMLNode::DeleteChild( XMLNode* node )
+{
+ TIXMLASSERT( node );
+ TIXMLASSERT( node->_document == _document );
+ TIXMLASSERT( node->_parent == this );
+ Unlink( node );
+ DeleteNode( node );
+}
+
+
+XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
+{
+ TIXMLASSERT( addThis );
+ if ( addThis->_document != _document ) {
+ TIXMLASSERT( false );
+ return 0;
+ }
+ InsertChildPreamble( addThis );
+
+ if ( _lastChild ) {
+ TIXMLASSERT( _firstChild );
+ TIXMLASSERT( _lastChild->_next == 0 );
+ _lastChild->_next = addThis;
+ addThis->_prev = _lastChild;
+ _lastChild = addThis;
+
+ addThis->_next = 0;
+ }
+ else {
+ TIXMLASSERT( _firstChild == 0 );
+ _firstChild = _lastChild = addThis;
+
+ addThis->_prev = 0;
+ addThis->_next = 0;
+ }
+ addThis->_parent = this;
+ return addThis;
+}
+
+
+XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
+{
+ TIXMLASSERT( addThis );
+ if ( addThis->_document != _document ) {
+ TIXMLASSERT( false );
+ return 0;
+ }
+ InsertChildPreamble( addThis );
+
+ if ( _firstChild ) {
+ TIXMLASSERT( _lastChild );
+ TIXMLASSERT( _firstChild->_prev == 0 );
+
+ _firstChild->_prev = addThis;
+ addThis->_next = _firstChild;
+ _firstChild = addThis;
+
+ addThis->_prev = 0;
+ }
+ else {
+ TIXMLASSERT( _lastChild == 0 );
+ _firstChild = _lastChild = addThis;
+
+ addThis->_prev = 0;
+ addThis->_next = 0;
+ }
+ addThis->_parent = this;
+ return addThis;
+}
+
+
+XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
+{
+ TIXMLASSERT( addThis );
+ if ( addThis->_document != _document ) {
+ TIXMLASSERT( false );
+ return 0;
+ }
+
+ TIXMLASSERT( afterThis );
+
+ if ( afterThis->_parent != this ) {
+ TIXMLASSERT( false );
+ return 0;
+ }
+
+ if ( afterThis->_next == 0 ) {
+ // The last node or the only node.
+ return InsertEndChild( addThis );
+ }
+ InsertChildPreamble( addThis );
+ addThis->_prev = afterThis;
+ addThis->_next = afterThis->_next;
+ afterThis->_next->_prev = addThis;
+ afterThis->_next = addThis;
+ addThis->_parent = this;
+ return addThis;
+}
+
+
+
+
+const XMLElement* XMLNode::FirstChildElement( const char* name ) const
+{
+ for( const XMLNode* node = _firstChild; node; node = node->_next ) {
+ const XMLElement* element = node->ToElement();
+ if ( element ) {
+ if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
+ return element;
+ }
+ }
+ }
+ return 0;
+}
+
+
+const XMLElement* XMLNode::LastChildElement( const char* name ) const
+{
+ for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
+ const XMLElement* element = node->ToElement();
+ if ( element ) {
+ if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
+ return element;
+ }
+ }
+ }
+ return 0;
+}
+
+
+const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
+{
+ for( const XMLNode* node = _next; node; node = node->_next ) {
+ const XMLElement* element = node->ToElement();
+ if ( element
+ && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
+ return element;
+ }
+ }
+ return 0;
+}
+
+
+const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
+{
+ for( const XMLNode* node = _prev; node; node = node->_prev ) {
+ const XMLElement* element = node->ToElement();
+ if ( element
+ && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
+ return element;
+ }
+ }
+ return 0;
+}
+
+
+char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
+{
+ // This is a recursive method, but thinking about it "at the current level"
+ // it is a pretty simple flat list:
+ // <foo/>
+ // <!-- comment -->
+ //
+ // With a special case:
+ // <foo>
+ // </foo>
+ // <!-- comment -->
+ //
+ // Where the closing element (/foo) *must* be the next thing after the opening
+ // element, and the names must match. BUT the tricky bit is that the closing
+ // element will be read by the child.
+ //
+ // 'endTag' is the end tag for this node, it is returned by a call to a child.
+ // 'parentEnd' is the end tag for the parent, which is filled in and returned.
+
+ while( p && *p ) {
+ XMLNode* node = 0;
+
+ p = _document->Identify( p, &node );
+ if ( node == 0 ) {
+ break;
+ }
+
+ StrPair endTag;
+ p = node->ParseDeep( p, &endTag );
+ if ( !p ) {
+ DeleteNode( node );
+ if ( !_document->Error() ) {
+ _document->SetError( XML_ERROR_PARSING, 0, 0 );
+ }
+ break;
+ }
+
+ XMLDeclaration* decl = node->ToDeclaration();
+ if ( decl ) {
+ // A declaration can only be the first child of a document.
+ // Set error, if document already has children.
+ if ( !_document->NoChildren() ) {
+ _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0);
+ DeleteNode( decl );
+ break;
+ }
+ }
+
+ XMLElement* ele = node->ToElement();
+ if ( ele ) {
+ // We read the end tag. Return it to the parent.
+ if ( ele->ClosingType() == XMLElement::CLOSING ) {
+ if ( parentEnd ) {
+ ele->_value.TransferTo( parentEnd );
+ }
+ node->_memPool->SetTracked(); // created and then immediately deleted.
+ DeleteNode( node );
+ return p;
+ }
+
+ // Handle an end tag returned to this level.
+ // And handle a bunch of annoying errors.
+ bool mismatch = false;
+ if ( endTag.Empty() ) {
+ if ( ele->ClosingType() == XMLElement::OPEN ) {
+ mismatch = true;
+ }
+ }
+ else {
+ if ( ele->ClosingType() != XMLElement::OPEN ) {
+ mismatch = true;
+ }
+ else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
+ mismatch = true;
+ }
+ }
+ if ( mismatch ) {
+ _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 );
+ DeleteNode( node );
+ break;
+ }
+ }
+ InsertEndChild( node );
+ }
+ return 0;
+}
+
+void XMLNode::DeleteNode( XMLNode* node )
+{
+ if ( node == 0 ) {
+ return;
+ }
+ MemPool* pool = node->_memPool;
+ node->~XMLNode();
+ pool->Free( node );
+}
+
+void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
+{
+ TIXMLASSERT( insertThis );
+ TIXMLASSERT( insertThis->_document == _document );
+
+ if ( insertThis->_parent )
+ insertThis->_parent->Unlink( insertThis );
+ else
+ insertThis->_memPool->SetTracked();
+}
+
+// --------- XMLText ---------- //
+char* XMLText::ParseDeep( char* p, StrPair* )
+{
+ const char* start = p;
+ if ( this->CData() ) {
+ p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
+ if ( !p ) {
+ _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
+ }
+ return p;
+ }
+ else {
+ int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
+ if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
+ flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
+ }
+
+ p = _value.ParseText( p, "<", flags );
+ if ( p && *p ) {
+ return p-1;
+ }
+ if ( !p ) {
+ _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
+ }
+ }
+ return 0;
+}
+
+
+XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
+ text->SetCData( this->CData() );
+ return text;
+}
+
+
+bool XMLText::ShallowEqual( const XMLNode* compare ) const
+{
+ const XMLText* text = compare->ToText();
+ return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
+}
+
+
+bool XMLText::Accept( XMLVisitor* visitor ) const
+{
+ TIXMLASSERT( visitor );
+ return visitor->Visit( *this );
+}
+
+
+// --------- XMLComment ---------- //
+
+XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLComment::~XMLComment()
+{
+}
+
+
+char* XMLComment::ParseDeep( char* p, StrPair* )
+{
+ // Comment parses as text.
+ const char* start = p;
+ p = _value.ParseText( p, "-->", StrPair::COMMENT );
+ if ( p == 0 ) {
+ _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
+ }
+ return p;
+}
+
+
+XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
+ return comment;
+}
+
+
+bool XMLComment::ShallowEqual( const XMLNode* compare ) const
+{
+ TIXMLASSERT( compare );
+ const XMLComment* comment = compare->ToComment();
+ return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
+}
+
+
+bool XMLComment::Accept( XMLVisitor* visitor ) const
+{
+ TIXMLASSERT( visitor );
+ return visitor->Visit( *this );
+}
+
+
+// --------- XMLDeclaration ---------- //
+
+XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLDeclaration::~XMLDeclaration()
+{
+ //printf( "~XMLDeclaration\n" );
+}
+
+
+char* XMLDeclaration::ParseDeep( char* p, StrPair* )
+{
+ // Declaration parses as text.
+ const char* start = p;
+ p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
+ if ( p == 0 ) {
+ _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
+ }
+ return p;
+}
+
+
+XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
+ return dec;
+}
+
+
+bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
+{
+ TIXMLASSERT( compare );
+ const XMLDeclaration* declaration = compare->ToDeclaration();
+ return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
+}
+
+
+
+bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
+{
+ TIXMLASSERT( visitor );
+ return visitor->Visit( *this );
+}
+
+// --------- XMLUnknown ---------- //
+
+XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLUnknown::~XMLUnknown()
+{
+}
+
+
+char* XMLUnknown::ParseDeep( char* p, StrPair* )
+{
+ // Unknown parses as text.
+ const char* start = p;
+
+ p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
+ if ( !p ) {
+ _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
+ }
+ return p;
+}
+
+
+XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
+ return text;
+}
+
+
+bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
+{
+ TIXMLASSERT( compare );
+ const XMLUnknown* unknown = compare->ToUnknown();
+ return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
+}
+
+
+bool XMLUnknown::Accept( XMLVisitor* visitor ) const
+{
+ TIXMLASSERT( visitor );
+ return visitor->Visit( *this );
+}
+
+// --------- XMLAttribute ---------- //
+
+const char* XMLAttribute::Name() const
+{
+ return _name.GetStr();
+}
+
+const char* XMLAttribute::Value() const
+{
+ return _value.GetStr();
+}
+
+char* XMLAttribute::ParseDeep( char* p, bool processEntities )
+{
+ // Parse using the name rules: bug fix, was using ParseText before
+ p = _name.ParseName( p );
+ if ( !p || !*p ) {
+ return 0;
+ }
+
+ // Skip white space before =
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( *p != '=' ) {
+ return 0;
+ }
+
+ ++p; // move up to opening quote
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( *p != '\"' && *p != '\'' ) {
+ return 0;
+ }
+
+ char endTag[2] = { *p, 0 };
+ ++p; // move past opening quote
+
+ p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
+ return p;
+}
+
+
+void XMLAttribute::SetName( const char* n )
+{
+ _name.SetStr( n );
+}
+
+
+XMLError XMLAttribute::QueryIntValue( int* value ) const
+{
+ if ( XMLUtil::ToInt( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
+{
+ if ( XMLUtil::ToUnsigned( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryBoolValue( bool* value ) const
+{
+ if ( XMLUtil::ToBool( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryFloatValue( float* value ) const
+{
+ if ( XMLUtil::ToFloat( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryDoubleValue( double* value ) const
+{
+ if ( XMLUtil::ToDouble( Value(), value )) {
+ return XML_NO_ERROR;
+ }
+ return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+void XMLAttribute::SetAttribute( const char* v )
+{
+ _value.SetStr( v );
+}
+
+
+void XMLAttribute::SetAttribute( int v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute( unsigned v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute( bool v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( double v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( float v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ _value.SetStr( buf );
+}
+
+
+// --------- XMLElement ---------- //
+XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
+ _closingType( 0 ),
+ _rootAttribute( 0 )
+{
+}
+
+
+XMLElement::~XMLElement()
+{
+ while( _rootAttribute ) {
+ XMLAttribute* next = _rootAttribute->_next;
+ DeleteAttribute( _rootAttribute );
+ _rootAttribute = next;
+ }
+}
+
+
+const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
+{
+ for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
+ if ( XMLUtil::StringEqual( a->Name(), name ) ) {
+ return a;
+ }
+ }
+ return 0;
+}
+
+
+const char* XMLElement::Attribute( const char* name, const char* value ) const
+{
+ const XMLAttribute* a = FindAttribute( name );
+ if ( !a ) {
+ return 0;
+ }
+ if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
+ return a->Value();
+ }
+ return 0;
+}
+
+
+const char* XMLElement::GetText() const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ return FirstChild()->Value();
+ }
+ return 0;
+}
+
+
+void XMLElement::SetText( const char* inText )
+{
+ if ( FirstChild() && FirstChild()->ToText() )
+ FirstChild()->SetValue( inText );
+ else {
+ XMLText* theText = GetDocument()->NewText( inText );
+ InsertFirstChild( theText );
+ }
+}
+
+
+void XMLElement::SetText( int v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ SetText( buf );
+}
+
+
+void XMLElement::SetText( unsigned v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ SetText( buf );
+}
+
+
+void XMLElement::SetText( bool v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ SetText( buf );
+}
+
+
+void XMLElement::SetText( float v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ SetText( buf );
+}
+
+
+void XMLElement::SetText( double v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ SetText( buf );
+}
+
+
+XMLError XMLElement::QueryIntText( int* ival ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->Value();
+ if ( XMLUtil::ToInt( t, ival ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->Value();
+ if ( XMLUtil::ToUnsigned( t, uval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryBoolText( bool* bval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->Value();
+ if ( XMLUtil::ToBool( t, bval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryDoubleText( double* dval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->Value();
+ if ( XMLUtil::ToDouble( t, dval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryFloatText( float* fval ) const
+{
+ if ( FirstChild() && FirstChild()->ToText() ) {
+ const char* t = FirstChild()->Value();
+ if ( XMLUtil::ToFloat( t, fval ) ) {
+ return XML_SUCCESS;
+ }
+ return XML_CAN_NOT_CONVERT_TEXT;
+ }
+ return XML_NO_TEXT_NODE;
+}
+
+
+
+XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
+{
+ XMLAttribute* last = 0;
+ XMLAttribute* attrib = 0;
+ for( attrib = _rootAttribute;
+ attrib;
+ last = attrib, attrib = attrib->_next ) {
+ if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
+ break;
+ }
+ }
+ if ( !attrib ) {
+ TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
+ attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
+ attrib->_memPool = &_document->_attributePool;
+ if ( last ) {
+ last->_next = attrib;
+ }
+ else {
+ _rootAttribute = attrib;
+ }
+ attrib->SetName( name );
+ attrib->_memPool->SetTracked(); // always created and linked.
+ }
+ return attrib;
+}
+
+
+void XMLElement::DeleteAttribute( const char* name )
+{
+ XMLAttribute* prev = 0;
+ for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
+ if ( XMLUtil::StringEqual( name, a->Name() ) ) {
+ if ( prev ) {
+ prev->_next = a->_next;
+ }
+ else {
+ _rootAttribute = a->_next;
+ }
+ DeleteAttribute( a );
+ break;
+ }
+ prev = a;
+ }
+}
+
+
+char* XMLElement::ParseAttributes( char* p )
+{
+ const char* start = p;
+ XMLAttribute* prevAttribute = 0;
+
+ // Read the attributes.
+ while( p ) {
+ p = XMLUtil::SkipWhiteSpace( p );
+ if ( !(*p) ) {
+ _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
+ return 0;
+ }
+
+ // attribute.
+ if (XMLUtil::IsNameStartChar( *p ) ) {
+ TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
+ XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
+ attrib->_memPool = &_document->_attributePool;
+ attrib->_memPool->SetTracked();
+
+ p = attrib->ParseDeep( p, _document->ProcessEntities() );
+ if ( !p || Attribute( attrib->Name() ) ) {
+ DeleteAttribute( attrib );
+ _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
+ return 0;
+ }
+ // There is a minor bug here: if the attribute in the source xml
+ // document is duplicated, it will not be detected and the
+ // attribute will be doubly added. However, tracking the 'prevAttribute'
+ // avoids re-scanning the attribute list. Preferring performance for
+ // now, may reconsider in the future.
+ if ( prevAttribute ) {
+ prevAttribute->_next = attrib;
+ }
+ else {
+ _rootAttribute = attrib;
+ }
+ prevAttribute = attrib;
+ }
+ // end of the tag
+ else if ( *p == '>' ) {
+ ++p;
+ break;
+ }
+ // end of the tag
+ else if ( *p == '/' && *(p+1) == '>' ) {
+ _closingType = CLOSED;
+ return p+2; // done; sealed element.
+ }
+ else {
+ _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
+ return 0;
+ }
+ }
+ return p;
+}
+
+void XMLElement::DeleteAttribute( XMLAttribute* attribute )
+{
+ if ( attribute == 0 ) {
+ return;
+ }
+ MemPool* pool = attribute->_memPool;
+ attribute->~XMLAttribute();
+ pool->Free( attribute );
+}
+
+//
+// <ele></ele>
+// <ele>foo<b>bar</b></ele>
+//
+char* XMLElement::ParseDeep( char* p, StrPair* strPair )
+{
+ // Read the element name.
+ p = XMLUtil::SkipWhiteSpace( p );
+
+ // The closing element is the </element> form. It is
+ // parsed just like a regular element then deleted from
+ // the DOM.
+ if ( *p == '/' ) {
+ _closingType = CLOSING;
+ ++p;
+ }
+
+ p = _value.ParseName( p );
+ if ( _value.Empty() ) {
+ return 0;
+ }
+
+ p = ParseAttributes( p );
+ if ( !p || !*p || _closingType ) {
+ return p;
+ }
+
+ p = XMLNode::ParseDeep( p, strPair );
+ return p;
+}
+
+
+
+XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
+{
+ if ( !doc ) {
+ doc = _document;
+ }
+ XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
+ for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
+ element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
+ }
+ return element;
+}
+
+
+bool XMLElement::ShallowEqual( const XMLNode* compare ) const
+{
+ TIXMLASSERT( compare );
+ const XMLElement* other = compare->ToElement();
+ if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
+
+ const XMLAttribute* a=FirstAttribute();
+ const XMLAttribute* b=other->FirstAttribute();
+
+ while ( a && b ) {
+ if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
+ return false;
+ }
+ a = a->Next();
+ b = b->Next();
+ }
+ if ( a || b ) {
+ // different count
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+bool XMLElement::Accept( XMLVisitor* visitor ) const
+{
+ TIXMLASSERT( visitor );
+ if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
+ for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+ if ( !node->Accept( visitor ) ) {
+ break;
+ }
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLDocument ----------- //
+
+// Warning: List must match 'enum XMLError'
+const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
+ "XML_SUCCESS",
+ "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"
+};
+
+
+XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
+ XMLNode( 0 ),
+ _writeBOM( false ),
+ _processEntities( processEntities ),
+ _errorID( XML_NO_ERROR ),
+ _whitespace( whitespace ),
+ _errorStr1( 0 ),
+ _errorStr2( 0 ),
+ _charBuffer( 0 )
+{
+ // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
+ _document = this;
+}
+
+
+XMLDocument::~XMLDocument()
+{
+ Clear();
+}
+
+
+void XMLDocument::Clear()
+{
+ DeleteChildren();
+
+#ifdef DEBUG
+ const bool hadError = Error();
+#endif
+ _errorID = XML_NO_ERROR;
+ _errorStr1 = 0;
+ _errorStr2 = 0;
+
+ delete [] _charBuffer;
+ _charBuffer = 0;
+
+#if 0
+ _textPool.Trace( "text" );
+ _elementPool.Trace( "element" );
+ _commentPool.Trace( "comment" );
+ _attributePool.Trace( "attribute" );
+#endif
+
+#ifdef DEBUG
+ if ( !hadError ) {
+ TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
+ TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
+ TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
+ TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
+ }
+#endif
+}
+
+
+XMLElement* XMLDocument::NewElement( const char* name )
+{
+ TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
+ XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
+ ele->_memPool = &_elementPool;
+ ele->SetName( name );
+ return ele;
+}
+
+
+XMLComment* XMLDocument::NewComment( const char* str )
+{
+ TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
+ XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
+ comment->_memPool = &_commentPool;
+ comment->SetValue( str );
+ return comment;
+}
+
+
+XMLText* XMLDocument::NewText( const char* str )
+{
+ TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
+ XMLText* text = new (_textPool.Alloc()) XMLText( this );
+ text->_memPool = &_textPool;
+ text->SetValue( str );
+ return text;
+}
+
+
+XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
+{
+ TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
+ XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
+ dec->_memPool = &_commentPool;
+ dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
+ return dec;
+}
+
+
+XMLUnknown* XMLDocument::NewUnknown( const char* str )
+{
+ TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
+ XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
+ unk->_memPool = &_commentPool;
+ unk->SetValue( str );
+ return unk;
+}
+
+static FILE* callfopen( const char* filepath, const char* mode )
+{
+ TIXMLASSERT( filepath );
+ TIXMLASSERT( mode );
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+ FILE* fp = 0;
+ errno_t err = fopen_s( &fp, filepath, mode );
+ if ( err ) {
+ return 0;
+ }
+#else
+ FILE* fp = fopen( filepath, mode );
+#endif
+ return fp;
+}
+
+void XMLDocument::DeleteNode( XMLNode* node ) {
+ TIXMLASSERT( node );
+ TIXMLASSERT(node->_document == this );
+ if (node->_parent) {
+ node->_parent->DeleteChild( node );
+ }
+ else {
+ // Isn't in the tree.
+ // Use the parent delete.
+ // Also, we need to mark it tracked: we 'know'
+ // it was never used.
+ node->_memPool->SetTracked();
+ // Call the static XMLNode version:
+ XMLNode::DeleteNode(node);
+ }
+}
+
+
+XMLError XMLDocument::LoadFile( const char* filename )
+{
+ Clear();
+ FILE* fp = callfopen( filename, "rb" );
+ if ( !fp ) {
+ SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
+ return _errorID;
+ }
+ LoadFile( fp );
+ fclose( fp );
+ return _errorID;
+}
+
+// This is likely overengineered template art to have a check that unsigned long value incremented
+// by one still fits into size_t. If size_t type is larger than unsigned long type
+// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
+// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
+// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
+// types sizes relate to each other.
+template
+<bool = (sizeof(unsigned long) >= sizeof(size_t))>
+struct LongFitsIntoSizeTMinusOne {
+ static bool Fits( unsigned long value )
+ {
+ return value < (size_t)-1;
+ }
+};
+
+template <>
+bool LongFitsIntoSizeTMinusOne<false>::Fits( unsigned long /*value*/ )
+{
+ return true;
+}
+
+XMLError XMLDocument::LoadFile( FILE* fp )
+{
+ Clear();
+
+ fseek( fp, 0, SEEK_SET );
+ if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+ return _errorID;
+ }
+
+ fseek( fp, 0, SEEK_END );
+ const long filelength = ftell( fp );
+ fseek( fp, 0, SEEK_SET );
+ if ( filelength == -1L ) {
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+ return _errorID;
+ }
+
+ if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
+ // Cannot handle files which won't fit in buffer together with null terminator
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+ return _errorID;
+ }
+
+ if ( filelength == 0 ) {
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+ return _errorID;
+ }
+
+ const size_t size = filelength;
+ TIXMLASSERT( _charBuffer == 0 );
+ _charBuffer = new char[size+1];
+ size_t read = fread( _charBuffer, 1, size, fp );
+ if ( read != size ) {
+ SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+ return _errorID;
+ }
+
+ _charBuffer[size] = 0;
+
+ Parse();
+ return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( const char* filename, bool compact )
+{
+ FILE* fp = callfopen( filename, "w" );
+ if ( !fp ) {
+ SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
+ return _errorID;
+ }
+ SaveFile(fp, compact);
+ fclose( fp );
+ return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
+{
+ // Clear any error from the last save, otherwise it will get reported
+ // for *this* call.
+ SetError( XML_NO_ERROR, 0, 0 );
+ XMLPrinter stream( fp, compact );
+ Print( &stream );
+ return _errorID;
+}
+
+
+XMLError XMLDocument::Parse( const char* p, size_t len )
+{
+ Clear();
+
+ if ( len == 0 || !p || !*p ) {
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+ return _errorID;
+ }
+ if ( len == (size_t)(-1) ) {
+ len = strlen( p );
+ }
+ TIXMLASSERT( _charBuffer == 0 );
+ _charBuffer = new char[ len+1 ];
+ memcpy( _charBuffer, p, len );
+ _charBuffer[len] = 0;
+
+ Parse();
+ if ( Error() ) {
+ // clean up now essentially dangling memory.
+ // and the parse fail can put objects in the
+ // pools that are dead and inaccessible.
+ DeleteChildren();
+ _elementPool.Clear();
+ _attributePool.Clear();
+ _textPool.Clear();
+ _commentPool.Clear();
+ }
+ return _errorID;
+}
+
+
+void XMLDocument::Print( XMLPrinter* streamer ) const
+{
+ if ( streamer ) {
+ Accept( streamer );
+ }
+ else {
+ XMLPrinter stdoutStreamer( stdout );
+ Accept( &stdoutStreamer );
+ }
+}
+
+
+void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
+{
+ TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
+ _errorID = error;
+ _errorStr1 = str1;
+ _errorStr2 = str2;
+}
+
+const char* XMLDocument::ErrorName() const
+{
+ TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );
+ const char* errorName = _errorNames[_errorID];
+ TIXMLASSERT( errorName && errorName[0] );
+ return errorName;
+}
+
+void XMLDocument::PrintError() const
+{
+ if ( Error() ) {
+ static const int LEN = 20;
+ char buf1[LEN] = { 0 };
+ char buf2[LEN] = { 0 };
+
+ if ( _errorStr1 ) {
+ TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
+ }
+ if ( _errorStr2 ) {
+ TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
+ }
+
+ // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that
+ // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning
+ TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );
+ printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",
+ static_cast<int>( _errorID ), ErrorName(), buf1, buf2 );
+ }
+}
+
+void XMLDocument::Parse()
+{
+ TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
+ TIXMLASSERT( _charBuffer );
+ char* p = _charBuffer;
+ p = XMLUtil::SkipWhiteSpace( p );
+ p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
+ if ( !*p ) {
+ SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+ return;
+ }
+ ParseDeep(p, 0 );
+}
+
+XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
+ _elementJustOpened( false ),
+ _firstElement( true ),
+ _fp( file ),
+ _depth( depth ),
+ _textDepth( -1 ),
+ _processEntities( true ),
+ _compactMode( compact )
+{
+ for( int i=0; i<ENTITY_RANGE; ++i ) {
+ _entityFlag[i] = false;
+ _restrictedEntityFlag[i] = false;
+ }
+ for( int i=0; i<NUM_ENTITIES; ++i ) {
+ const char entityValue = entities[i].value;
+ TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );
+ _entityFlag[ (unsigned char)entityValue ] = true;
+ }
+ _restrictedEntityFlag[(unsigned char)'&'] = true;
+ _restrictedEntityFlag[(unsigned char)'<'] = true;
+ _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
+ _buffer.Push( 0 );
+}
+
+
+void XMLPrinter::Print( const char* format, ... )
+{
+ va_list va;
+ va_start( va, format );
+
+ if ( _fp ) {
+ vfprintf( _fp, format, va );
+ }
+ else {
+ const int len = TIXML_VSCPRINTF( format, va );
+ // Close out and re-start the va-args
+ va_end( va );
+ TIXMLASSERT( len >= 0 );
+ va_start( va, format );
+ TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
+ char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
+ TIXML_VSNPRINTF( p, len+1, format, va );
+ }
+ va_end( va );
+}
+
+
+void XMLPrinter::PrintSpace( int depth )
+{
+ for( int i=0; i<depth; ++i ) {
+ Print( " " );
+ }
+}
+
+
+void XMLPrinter::PrintString( const char* p, bool restricted )
+{
+ // Look for runs of bytes between entities to print.
+ const char* q = p;
+
+ if ( _processEntities ) {
+ const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
+ while ( *q ) {
+ TIXMLASSERT( p <= q );
+ // Remember, char is sometimes signed. (How many times has that bitten me?)
+ if ( *q > 0 && *q < ENTITY_RANGE ) {
+ // Check for entities. If one is found, flush
+ // the stream up until the entity, write the
+ // entity, and keep looking.
+ if ( flag[(unsigned char)(*q)] ) {
+ while ( p < q ) {
+ const size_t delta = q - p;
+ // %.*s accepts type int as "precision"
+ const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
+ Print( "%.*s", toPrint, p );
+ p += toPrint;
+ }
+ bool entityPatternPrinted = false;
+ for( int i=0; i<NUM_ENTITIES; ++i ) {
+ if ( entities[i].value == *q ) {
+ Print( "&%s;", entities[i].pattern );
+ entityPatternPrinted = true;
+ break;
+ }
+ }
+ if ( !entityPatternPrinted ) {
+ // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
+ TIXMLASSERT( false );
+ }
+ ++p;
+ }
+ }
+ ++q;
+ TIXMLASSERT( p <= q );
+ }
+ }
+ // Flush the remaining string. This will be the entire
+ // string if an entity wasn't found.
+ TIXMLASSERT( p <= q );
+ if ( !_processEntities || ( p < q ) ) {
+ Print( "%s", p );
+ }
+}
+
+
+void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
+{
+ if ( writeBOM ) {
+ static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
+ Print( "%s", bom );
+ }
+ if ( writeDec ) {
+ PushDeclaration( "xml version=\"1.0\"" );
+ }
+}
+
+
+void XMLPrinter::OpenElement( const char* name, bool compactMode )
+{
+ SealElementIfJustOpened();
+ _stack.Push( name );
+
+ if ( _textDepth < 0 && !_firstElement && !compactMode ) {
+ Print( "\n" );
+ }
+ if ( !compactMode ) {
+ PrintSpace( _depth );
+ }
+
+ Print( "<%s", name );
+ _elementJustOpened = true;
+ _firstElement = false;
+ ++_depth;
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, const char* value )
+{
+ TIXMLASSERT( _elementJustOpened );
+ Print( " %s=\"", name );
+ PrintString( value, false );
+ Print( "\"" );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, int v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, unsigned v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, bool v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, double v )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( v, buf, BUF_SIZE );
+ PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::CloseElement( bool compactMode )
+{
+ --_depth;
+ const char* name = _stack.Pop();
+
+ if ( _elementJustOpened ) {
+ Print( "/>" );
+ }
+ else {
+ if ( _textDepth < 0 && !compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ Print( "</%s>", name );
+ }
+
+ if ( _textDepth == _depth ) {
+ _textDepth = -1;
+ }
+ if ( _depth == 0 && !compactMode) {
+ Print( "\n" );
+ }
+ _elementJustOpened = false;
+}
+
+
+void XMLPrinter::SealElementIfJustOpened()
+{
+ if ( !_elementJustOpened ) {
+ return;
+ }
+ _elementJustOpened = false;
+ Print( ">" );
+}
+
+
+void XMLPrinter::PushText( const char* text, bool cdata )
+{
+ _textDepth = _depth-1;
+
+ SealElementIfJustOpened();
+ if ( cdata ) {
+ Print( "<![CDATA[%s]]>", text );
+ }
+ else {
+ PrintString( text, true );
+ }
+}
+
+void XMLPrinter::PushText( int value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( unsigned value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( bool value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( float value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( double value )
+{
+ char buf[BUF_SIZE];
+ XMLUtil::ToStr( value, buf, BUF_SIZE );
+ PushText( buf, false );
+}
+
+
+void XMLPrinter::PushComment( const char* comment )
+{
+ SealElementIfJustOpened();
+ if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ _firstElement = false;
+ Print( "<!--%s-->", comment );
+}
+
+
+void XMLPrinter::PushDeclaration( const char* value )
+{
+ SealElementIfJustOpened();
+ if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ _firstElement = false;
+ Print( "<?%s?>", value );
+}
+
+
+void XMLPrinter::PushUnknown( const char* value )
+{
+ SealElementIfJustOpened();
+ if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+ Print( "\n" );
+ PrintSpace( _depth );
+ }
+ _firstElement = false;
+ Print( "<!%s>", value );
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLDocument& doc )
+{
+ _processEntities = doc.ProcessEntities();
+ if ( doc.HasBOM() ) {
+ PushHeader( true, false );
+ }
+ return true;
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
+{
+ const XMLElement* parentElem = 0;
+ if ( element.Parent() ) {
+ parentElem = element.Parent()->ToElement();
+ }
+ const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
+ OpenElement( element.Name(), compactMode );
+ while ( attribute ) {
+ PushAttribute( attribute->Name(), attribute->Value() );
+ attribute = attribute->Next();
+ }
+ return true;
+}
+
+
+bool XMLPrinter::VisitExit( const XMLElement& element )
+{
+ CloseElement( CompactMode(element) );
+ return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLText& text )
+{
+ PushText( text.Value(), text.CData() );
+ return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLComment& comment )
+{
+ PushComment( comment.Value() );
+ return true;
+}
+
+bool XMLPrinter::Visit( const XMLDeclaration& declaration )
+{
+ PushDeclaration( declaration.Value() );
+ return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLUnknown& unknown )
+{
+ PushUnknown( unknown.Value() );
+ return true;
+}
+
+} // namespace tinyxml2
+
diff --git a/src/ui.cpp b/src/ui.cpp
index f73f48a..ead9d8c 100644
--- a/src/ui.cpp
+++ b/src/ui.cpp
@@ -95,6 +95,8 @@ namespace ui {
bool dialogImportant = false;
unsigned char dialogOptChosen = 0;
+ unsigned int textWrapLimit = 110;
+
/*
* Current font size. Changing this WILL NOT change the font size, see setFontSize() for
* actual font size changing.
@@ -266,10 +268,17 @@ namespace ui {
*/
do{
- if(s[i]=='\n'){ // Handle newlines
+ if(i && ((i / 110.0) == (i / 110))){
yo-=fontSize*1.05;
xo=x;
- }else if(s[i]==' '){ // Handle spaces
+ if(s[i] == ' ')
+ i++;
+ }
+ if(s[i] == '\n' || s[i] == '\r' || s[i] == '\t'){
+ /*if(s[i] == '\n'){
+ yo-=fontSize*1.05;
+ xo=x;
+ */}else if(s[i]==' '){ // Handle spaces
xo+=fontSize/2;
}else if(s[i]=='\b'){ // Handle backspaces?
xo-=add.x;
@@ -308,12 +317,13 @@ namespace ui {
* Draw a string in a typewriter-esque fashion. Each letter is rendered as calls are made
* to this function. Passing a different string to the function will reset the counters.
*/
-
+
+ static char *ret = NULL;
char *typeOut(char *str){
static unsigned int sinc, // Acts as a delayer for the space between each character.
linc=0, // Contains the number of letters that should be drawn.
size=0; // Contains the full size of the current string.
- static char *ret = NULL;
+ //static char *ret = NULL;
/*
* Create a well-sized buffer if we haven't yet.
@@ -453,6 +463,8 @@ namespace ui {
dialogBoxExists = true;
dialogImportant = false;
+ if(ret)
+ ret[0] = '\0';
}
void waitForDialog(void){
do{
@@ -557,7 +569,7 @@ namespace ui {
hub.y-=fontSize*1.15;
glRectf(hub.x,
hub.y,
- hub.x+(player->health/player->maxHealth)*130,
+ hub.x+(player->health/player->maxHealth?player->maxHealth:1)*130,
hub.y+12);
}
@@ -667,7 +679,6 @@ DONE:
memcpy(&player->loc,&tmppos,sizeof(vec2));
currentWorld = tmp;
toggleBlackFast();
- dialogBoxExists = false;
}
}
break;
@@ -689,7 +700,6 @@ DONE:
memcpy(&player->loc,&tmppos,sizeof(vec2));
currentWorld = tmp;
toggleBlackFast();
- dialogBoxExists = false;
}
}
break;
@@ -805,6 +815,9 @@ DONE:
case SDLK_RIGHT:
player->inv->sel++;
break;
+ case SDLK_f:
+ player->light^=true;
+ break;
default:
break;
}
diff --git a/src/world.cpp b/src/world.cpp
index 5663086..fea0e3e 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -33,6 +33,8 @@ const char *bgPaths[2][7]={
NULL}
};
+Texturec *grassT;
+
const float bgDraw[3][3]={
{100,240,.6 },
{150,250,.4 },
@@ -50,6 +52,7 @@ float worldGetYBase(World *w){
}
void World::setBackground(WORLD_BG_TYPE bgt){
+ bgType = bgt;
switch(bgt){
case BG_FOREST:
bgTex = new Texturec(7,bgPaths[0]);
@@ -60,12 +63,126 @@ void World::setBackground(WORLD_BG_TYPE bgt){
}
}
-void World::save(FILE *s){
- fclose(s);
+void World::save(std::ofstream *o){
+ static unsigned int size2;
+ unsigned int size,i;
+ size_t bgms = strlen(bgm) + 1;
+ char *bufptr;
+
+ o->write((char *)&lineCount, sizeof(unsigned int));
+ o->write((char *)line ,lineCount * sizeof(struct line_t));
+ o->write("GG" ,2 * sizeof(char));
+ o->write((char *)star ,100 * sizeof(vec2));
+ o->write((char *)&bgType , sizeof(WORLD_BG_TYPE));
+ o->write((char *)&bgms , sizeof(size_t));
+ o->write(bgm ,strlen(bgm)+1);
+ o->write("NO" ,2 * sizeof(char));
+ size = npc.size();
+ o->write((char *)&size,sizeof(unsigned int));
+ for(i=0;i<size;i++){
+ bufptr = npc[i]->save(&size2);
+ o->write((char *)&size2,sizeof(unsigned int));
+ o->write(bufptr,size2);
+ delete[] bufptr;
+ }
+ size = build.size();
+ o->write((char *)&size,sizeof(unsigned int));
+ for(i=0;i<size;i++){
+ bufptr = build[i]->save();
+ o->write(bufptr,sizeof(StructuresSavePacket));
+ delete[] bufptr;
+ }
+ size = object.size();
+ o->write((char *)&size,sizeof(unsigned int));
+ for(i=0;i<size;i++){
+ bufptr = object[i]->save();
+ o->write(bufptr,sizeof(ObjectSavePacket));
+ delete[] bufptr;
+ }
+ size = mob.size();
+ o->write((char *)&size,sizeof(unsigned int));
+ for(i=0;i<size;i++){
+ bufptr = mob[i]->save();
+ o->write(bufptr,sizeof(MobSavePacket));
+ delete[] bufptr;
+ }
}
-void World::load(FILE *s){
- fclose(s);
+void World::load(std::ifstream *i){
+ unsigned int size,size2,j;
+ size_t bgms;
+ char sig[2],*buf;
+
+ i->read((char *)&lineCount,sizeof(unsigned int));
+
+ line = new struct line_t[lineCount];
+ i->read((char *)line,lineCount * sizeof(struct line_t));
+
+ i->read(sig,2 * sizeof(char));
+ if(strncmp(sig,"GG",2)){
+ std::cout<<"world.dat corrupt: GG"<<std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ x_start = 0 - getWidth(this) / 2;
+
+ i->read((char *)star,100 * sizeof(vec2));
+ i->read((char *)&bgType,sizeof(WORLD_BG_TYPE));
+
+ i->read((char *)&bgms,sizeof(size_t));
+ bgm = new char[bgms];
+ i->read(bgm,bgms);
+ setBGM(bgm);
+
+ i->read(sig,2 * sizeof(char));
+ if(strncmp(sig,"NO",2)){
+ std::cout<<"world.dat corrupt: NO"<<std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ i->read((char *)&size,sizeof(unsigned int));
+ for(j=0;j<size;j++){
+ i->read((char *)&size2,sizeof(unsigned int));
+ buf = new char[size2];
+ i->read(buf,size2);
+ npc.push_back(new NPC());
+ npc.back()->load(size2,buf);
+ entity.push_back(npc.back());
+ delete[] buf;
+ }
+
+ static StructuresSavePacket *ssp;
+ ssp = new StructuresSavePacket();
+ i->read((char *)&size,sizeof(unsigned int));
+ for(j=0;j<size;j++){
+ i->read((char *)ssp,sizeof(StructuresSavePacket));
+ build.push_back(new Structures());
+ build.back()->load((char *)ssp);
+ entity.push_back(build.back());
+ }
+ delete ssp;
+
+ static ObjectSavePacket *osp;
+ osp = new ObjectSavePacket();
+ i->read((char *)&size,sizeof(unsigned int));
+ for(j=0;j<size;j++){
+ i->read((char *)osp,sizeof(ObjectSavePacket));
+ object.push_back(new Object());
+ object.back()->load((char *)osp);
+ object.back()->reloadTexture();
+ entity.push_back(object.back());
+ }
+ delete osp;
+
+ static MobSavePacket *msp;
+ msp = new MobSavePacket();
+ i->read((char *)&size,sizeof(unsigned int));
+ for(j=0;j<size;j++){
+ i->read((char *)msp,sizeof(MobSavePacket));
+ mob.push_back(new Mob(0));
+ mob.back()->load((char *)msp);
+ entity.push_back(mob.back());
+ }
}
World::World(void){
@@ -88,6 +205,7 @@ World::World(void){
star = new vec2[100];
memset(star,0,100 * sizeof(vec2));
+ grassT = new Texturec(1,"assets/grass.png");
}
void World::deleteEntities(void){
@@ -383,27 +501,24 @@ LLLOOP:
width = (-x_start) << 1;
glEnable(GL_TEXTURE_2D);
-
+
bgTex->bind(0);
safeSetColorA(255,255,255,255 - worldShade * 4);
-
glBegin(GL_QUADS);
glTexCoord2i(0,0);glVertex2i( cx_start,SCREEN_HEIGHT);
glTexCoord2i(1,0);glVertex2i(-cx_start,SCREEN_HEIGHT);
glTexCoord2i(1,1);glVertex2i(-cx_start,0);
glTexCoord2i(0,1);glVertex2i( cx_start,0);
glEnd();
-
+
bgTex->bindNext();
- safeSetColorA(255,255,255,worldShade * 4);
-
+ safeSetColorA(255,255,255,worldShade * 4);
glBegin(GL_QUADS);
glTexCoord2i(0,0);glVertex2i( cx_start,SCREEN_HEIGHT);
glTexCoord2i(1,0);glVertex2i(-cx_start,SCREEN_HEIGHT);
glTexCoord2i(1,1);glVertex2i(-cx_start,0);
glTexCoord2i(0,1);glVertex2i( cx_start,0);
glEnd();
-
glDisable(GL_TEXTURE_2D);
/*
@@ -426,13 +541,13 @@ LLLOOP:
}
}
+
glEnable(GL_TEXTURE_2D);
/*
* Draw the mountains.
*/
-
bgTex->bindNext();
safeSetColorA(150-bgshade,150-bgshade,150-bgshade,220);
@@ -551,79 +666,114 @@ LOOP2:
bool hey=false;
glEnable(GL_TEXTURE_2D);
+ glActiveTexture(GL_TEXTURE0);
bgTex->bindNext();
+
+ GLfloat pointArray[light.size()][2];
+ for(uint w = 0; w < light.size(); w++){
+ pointArray[w][0] = light[w].loc.x - offset.x;
+ pointArray[w][1] = light[w].loc.y;
+ }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //for the s direction
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //for the t direction
+ glUseProgram(shaderProgram);
+ glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0);
+ glUniform1f(glGetUniformLocation(shaderProgram, "amb"), float(shade+50.0f)/100.0f);
+ if(p->light){
+ glUniform1i(glGetUniformLocation(shaderProgram, "numLight"), 1);
+ glUniform2f(glGetUniformLocation(shaderProgram, "lightLocation"), p->loc.x - offset.x+SCREEN_WIDTH/2, p->loc.y);
+ glUniform3f(glGetUniformLocation(shaderProgram, "lightColor"), 1.0f,1.0f,1.0f);
+ }else if(!light.size()){
+ glUniform1i(glGetUniformLocation(shaderProgram, "numLight"), 0);
+ }else{
+ glUniform1i(glGetUniformLocation(shaderProgram, "numLight"), light.size());
+ glUniform2fv(glGetUniformLocation(shaderProgram, "lightLocation"), light.size(), (GLfloat *)&pointArray);
+ glUniform3f(glGetUniformLocation(shaderProgram, "lightColor"), 1.0f,1.0f,1.0f);
+ }
+
glBegin(GL_QUADS);
- for(i=is;i<(unsigned)ie-GEN_INC;i++){
- cline[i].y+=(yoff-DRAW_Y_OFFSET); // Add the y offset
- if(!cline[i].y){
- cline[i].y=base;
- hey=true;
- glColor4ub(0,0,0,255);
- }else
- safeSetColorA(150+shade*2,150+shade*2,150+shade*2,255);
- glTexCoord2i(0,0);glVertex2i(cx_start+i*HLINE ,cline[i].y-GRASS_HEIGHT);
- glTexCoord2i(1,0);glVertex2i(cx_start+i*HLINE+HLINE,cline[i].y-GRASS_HEIGHT);
- glTexCoord2i(1,(int)(cline[i].y/64)+cline[i].color);glVertex2i(cx_start+i*HLINE+HLINE,0);
- glTexCoord2i(0,(int)(cline[i].y/64)+cline[i].color);glVertex2i(cx_start+i*HLINE ,0);
- cline[i].y-=(yoff-DRAW_Y_OFFSET); // Restore the line's y value
- if(hey){
- hey=false;
- cline[i].y=0;
- }
+ for(i=is;i<(unsigned)ie-GEN_INC;i++){
+ cline[i].y+=(yoff-DRAW_Y_OFFSET); // Add the y offset
+ if(!cline[i].y){
+ cline[i].y=base;
+ hey=true;
+ glColor4ub(0,0,0,255);
+ }else safeSetColorA(150+shade*2,150+shade*2,150+shade*2,255);
+ glTexCoord2i(0,0);glVertex2i(cx_start+i*HLINE ,cline[i].y-GRASS_HEIGHT);
+ glTexCoord2i(1,0);glVertex2i(cx_start+i*HLINE+HLINE,cline[i].y-GRASS_HEIGHT);
+ glTexCoord2i(1,(int)(cline[i].y/64)+cline[i].color);glVertex2i(cx_start+i*HLINE+HLINE,0);
+ glTexCoord2i(0,(int)(cline[i].y/64)+cline[i].color);glVertex2i(cx_start+i*HLINE ,0);
+ cline[i].y-=(yoff-DRAW_Y_OFFSET); // Restore the line's y value
+ if(hey){
+ hey=false;
+ cline[i].y=0;
}
+ }
glEnd();
+ glUseProgram(0);
glDisable(GL_TEXTURE_2D);
-
/*
* Draw grass on every line.
*/
float cgh[2];
- glBegin(GL_QUADS);
- for(i=is;i<(unsigned)ie-GEN_INC;i++){
-
- /*
- * Load the current line's grass values
- */
-
- if(cline[i].y)memcpy(cgh,cline[i].gh,2*sizeof(float));
- else memset(cgh,0 ,2*sizeof(float));
-
-
-
- /*
- * Flatten the grass if the player is standing on it.
- */
-
- if(!cline[i].gs){
- cgh[0]/=4;
- cgh[1]/=4;
- }
-
- /*
- * Actually draw the grass.
- */
-
- cline[i].y+=(yoff-DRAW_Y_OFFSET);
-
- safeSetColor(shade,100+shade*1.5,shade);
-
- glVertex2i(cx_start+i*HLINE ,cline[i].y+cgh[0]);
- glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y+cgh[0]);
- glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y-GRASS_HEIGHT);
- glVertex2i(cx_start+i*HLINE ,cline[i].y-GRASS_HEIGHT);
-
- glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y+cgh[1]);
- glVertex2i(cx_start+i*HLINE+HLINE ,cline[i].y+cgh[1]);
- glVertex2i(cx_start+i*HLINE+HLINE ,cline[i].y-GRASS_HEIGHT);
- glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y-GRASS_HEIGHT);
-
- cline[i].y-=(yoff-DRAW_Y_OFFSET);
+
+ glEnable(GL_TEXTURE_2D);
+ glActiveTexture(GL_TEXTURE0);
+ grassT->bind(0);
+ glUseProgram(shaderProgram);
+ glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0);
+ //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //for the s direction
+ //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //for the t direction
+ //glBegin(GL_QUADS);
+
+ for(i=is;i<(unsigned)ie-GEN_INC;i++){
+
+ /*
+ * Load the current line's grass values
+ */
+
+ if(cline[i].y)memcpy(cgh,cline[i].gh,2*sizeof(float));
+ else memset(cgh,0 ,2*sizeof(float));
+
+
+
+ /*
+ * Flatten the grass if the player is standing on it.
+ */
+
+ if(!cline[i].gs){
+ cgh[0]/=4;
+ cgh[1]/=4;
}
- glEnd();
-
+
+ /*
+ * Actually draw the grass.
+ */
+
+ cline[i].y+=(yoff-DRAW_Y_OFFSET);
+ safeSetColorA(255,255,255,255);
+ glBegin(GL_QUADS);
+ glTexCoord2i(0,0);glVertex2i(cx_start+i*HLINE ,cline[i].y+cgh[0]);
+ glTexCoord2i(1,0);glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y+cgh[0]);
+ glTexCoord2i(1,1);glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y-GRASS_HEIGHT);
+ glTexCoord2i(0,1);glVertex2i(cx_start+i*HLINE ,cline[i].y-GRASS_HEIGHT);
+ glEnd();
+
+ glBegin(GL_QUADS);
+ glTexCoord2i(0,0);glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y+cgh[1]);
+ glTexCoord2i(1,0);glVertex2i(cx_start+i*HLINE+HLINE ,cline[i].y+cgh[1]);
+ glTexCoord2i(1,1);glVertex2i(cx_start+i*HLINE+HLINE ,cline[i].y-GRASS_HEIGHT);
+ glTexCoord2i(0,1);glVertex2i(cx_start+i*HLINE+HLINE/2,cline[i].y-GRASS_HEIGHT);
+ glEnd();
+
+ cline[i].y-=(yoff-DRAW_Y_OFFSET);
+ }
+ //glEnd();
+ glUseProgram(0);
+ glDisable(GL_TEXTURE_2D);
+
+
/*
* Draw non-structure entities.
*/
@@ -913,16 +1063,20 @@ void World::addStructure(_TYPE t,BUILD_SUB sub, float x,float y,World *inside){
entity.push_back(build.back());
}
-void World::addVillage(int bCount, int npcMin, int npcMax,_TYPE t,float x,float y,World *outside){
+void World::addVillage(int bCount, int npcMin, int npcMax,_TYPE t,World *inside){
std::cout << npcMin << ", " << npcMax << std::endl;
- int xwasd;
+ //int xwasd;
for(int i = 0; i < bCount; i++){
- xwasd = (rand()%(int)x+1000*HLINE);
+ addStructure(t,HOUSE,x_start + (i * 300),100,inside);
+ /*std::cout<<"1\n";
HERE:
+ xwasd = (rand()%(int)x+1000*HLINE);
for(auto &bu : build){
if(xwasd > bu->loc.x && xwasd < bu->loc.x+bu->width)goto HERE;
}
- addStructure(t,HOUSE,xwasd,y,outside);
+ std::cout<<"2\n";
+ addStructure(t,HOUSE,xwasd,y,inside);
+ std::cout<<"3\n";*/
}
}
void World::addMob(int t,float x,float y){
@@ -959,6 +1113,12 @@ void World::addParticle(float x, float y, float w, float h, float vx, float vy,
particles.back()->canMove = true;
}
+void World::addLight(vec2 loc, Color color){
+ light.push_back(Light());
+ light.back().loc = loc;
+ light.back().color = color;
+}
+
/*void World::removeObject(Object i){
object.delete[](i);
}*/
@@ -1111,6 +1271,25 @@ void IndoorWorld::draw(Player *p){
*/
glEnable(GL_TEXTURE_2D);
+ GLfloat pointArray[light.size()][2];
+ for(uint w = 0; w < light.size(); w++){
+ pointArray[w][0] = light[w].loc.x - offset.x;
+ pointArray[w][1] = light[w].loc.y;
+ }
+ glUseProgram(shaderProgram);
+ glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0);
+ glUniform1f(glGetUniformLocation(shaderProgram, "amb"), 0.0f);
+ if(p->light){
+ glUniform1i(glGetUniformLocation(shaderProgram, "numLight"), 1);
+ glUniform2f(glGetUniformLocation(shaderProgram, "lightLocation"), p->loc.x - offset.x+SCREEN_WIDTH/2, p->loc.y);
+ glUniform3f(glGetUniformLocation(shaderProgram, "lightColor"), 1.0f,1.0f,1.0f);
+ }else if(!light.size()){
+ glUniform1i(glGetUniformLocation(shaderProgram, "numLight"), 0);
+ }else{
+ glUniform1i(glGetUniformLocation(shaderProgram, "numLight"), light.size());
+ glUniform2fv(glGetUniformLocation(shaderProgram, "lightLocation"), light.size(), (GLfloat *)&pointArray);
+ glUniform3f(glGetUniformLocation(shaderProgram, "lightColor"), 1.0f,1.0f,1.0f);
+ }
bgTex->bind(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //for the s direction
@@ -1126,6 +1305,7 @@ void IndoorWorld::draw(Player *p){
//}
glEnd();
+ glUseProgram(0);
glDisable(GL_TEXTURE_2D);
/*
@@ -1146,7 +1326,8 @@ void IndoorWorld::draw(Player *p){
/*
* Draw the ground.
*/
-
+ glUseProgram(shaderProgram);
+ glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0);
glBegin(GL_QUADS);
for(;i < ie - GEN_INC;i++){
safeSetColor(150,100,50);
@@ -1158,6 +1339,7 @@ void IndoorWorld::draw(Player *p){
glVertex2i(x ,line[i].y - 50);
}
glEnd();
+ glUseProgram(0);
/*
* Draw all entities.