Jump to content

Lua only XML parser


VicToMeyeZR
 Share

Recommended Posts

Here is a Lua only Parser.

 

test.xml

<?xml version="1.0" encoding="utf-8"?>
<animations>
<start>
	<idle value="infiltrator">460</idle> 
	<run value="infiltrator">1155</run>
</start>
<end>
	<idle value="infiltrator">850</idle>
	<run value="infiltrator">1195</run>
</end>
</animations>

 

xml.lua

 

XmlParser = {};

function XmlParser:ToXmlString(value)
value = string.gsub (value, "&", "&");		-- '&' -> "&"
value = string.gsub (value, "<", "<");		-- '<' -> "<"
value = string.gsub (value, ">", ">");		-- '>' -> ">"
--value = string.gsub (value, "'", "'");	-- '\'' -> "'"
value = string.gsub (value, "\"", """);	-- '"' -> """
-- replace non printable char -> "
"
  	value = string.gsub(value, "([^%w%&%;%p%\t% ])",
      	function (c) 
      		return string.format("%X;", string.byte(c)) 
      		--return string.format("%02X;", string.byte(c)) 
      		--return string.format("%02d;", string.byte(c)) 
      	end);
return value;
end

function XmlParser:FromXmlString(value)
 	value = string.gsub(value, "([%x]+)%;",
     	function(h) 
     		return string.char(tonumber(h,16)) 
     	end);
 	value = string.gsub(value, "([0-9]+)%;",
     	function(h) 
     		return string.char(tonumber(h,10)) 
     	end);
value = string.gsub (value, """, "\"");
value = string.gsub (value, "'", "'");
value = string.gsub (value, ">", ">");
value = string.gsub (value, "<", "<");
value = string.gsub (value, "&", "&");
return value;
end

function XmlParser:ParseArgs(s)
 local arg = {}
 string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
   	arg[w] = self:FromXmlString(a);
 	end)
 return arg
end

function XmlParser:ParseXmlText(xmlText)
 local stack = {}
 local top = {Name=nil,Value=nil,Attributes={},ChildNodes={}}
 table.insert(stack, top)
 local ni,c,label,xarg, empty
 local i, j = 1, 1
 while true do
   ni,j,c,label,xarg, empty = string.find(xmlText, "<(%/?)([%w:]+)(.-)(%/?)>", i)
   if not ni then break end
   local text = string.sub(xmlText, i, ni-1);
   if not string.find(text, "^%s*$") then
     top.Value=(top.Value or "")..self:FromXmlString(text);
   end
   if empty == "/" then  -- empty element tag
     table.insert(top.ChildNodes, {Name=label,Value=nil,Attributes=self:ParseArgs(xarg),ChildNodes={}})
   elseif c == "" then   -- start tag
     top = {Name=label, Value=nil, Attributes=self:ParseArgs(xarg), ChildNodes={}}
     table.insert(stack, top)   -- new level
     --log("openTag ="..top.Name);
   else  -- end tag
     local toclose = table.remove(stack)  -- remove top
     --log("closeTag="..toclose.Name);
     top = stack[#stack]
     if #stack < 1 then
       error("XmlParser: nothing to close with "..label)
     end
     if toclose.Name ~= label then
       error("XmlParser: trying to close "..toclose.Name.." with "..label)
     end
     table.insert(top.ChildNodes, toclose)
   end
   i = j+1
 end
 local text = string.sub(xmlText, i);
 if not string.find(text, "^%s*$") then
     stack[#stack].Value=(stack[#stack].Value or "")..self:FromXmlString(text);
 end
 if #stack > 1 then
   error("XmlParser: unclosed "..stack[stack.n].Name)
 end
 return stack[1].ChildNodes[1];
end

function XmlParser:ParseXmlFile(xmlFileName)
local hFile,err = io.open(xmlFileName,"r");
if (not err) then
	local xmlText=hFile:read("*a"); -- read file content
	io.close(hFile);
       return self:ParseXmlText(xmlText),nil;
else
	return nil,err;
end
end

 

Run this file:

require("Scripts/XML/xml")

local obj,err = XmlParser:ParseXmlFile("Scripts/test.xml")

if(not err) then
for i,xmlNode in pairs(obj.ChildNodes) do
if(xmlNode.Name=="start") then
	for i,subXmlNode in pairs(xmlNode.ChildNodes) do
		if(subXmlNode.Name) then
			Notify(subXmlNode.Name.." value=\""..subXmlNode.Attributes.value.."\"");
			if(subXmlNode.Value) then
				Notify("   Content=\""..subXmlNode.Value.."\"");
			end
		end
	end
end
end	
else
Notify("ERROR: "..err);		
end

 

It will return:

idle value="infiltrator"
content="460"
run value="infiltrator"
content="1155"

 

With this I can have it run a dynamic menu for the object, as well as pass the deafult values based on the XML file information.

 

I will post the scrip to run this part as soon as I have it.

AMD Phenom II x6 1100T - 16GB RAM - ATI 5870 HD - OCZ Vertex 2 60GB SSD

76561197984667096.png

Link to comment
Share on other sites

ok. My character script. This way I won't have to change much with it.

 

XML FILE:

<?xml version="1.0" encoding="utf-8"?>
<animations>
<start>
	<idle value="Idle Start Frame">460</idle> 
	<run value="Run Start Frame">1155</run>
</start>
<end>
	<idle value="Idle End Frame">850</idle>
	<run value="Run End Frame">1195</run>
</end>
</animations>

 

MODEL LUA FILE:

require("scripts/class")
require("scripts/XML/XML")

local class=CreateClass(...)

local obj,err = XmlParser:ParseXmlFile("Models/characters/Cyber/character_visor_full.xml")

function class:InitDialog(grid)

self.super:InitDialog(grid)
group = grid:AddGroup("Animations")
//This adds a property field based off the XML file data structure.  This file MUST meet the data structure set by my post, or you will have to modify this script
if (not err) then
	for i,xmlNode in pairs(obj.ChildNodes) do
		if(xmlNode.Name=="start") then
			for i,subXmlNode in pairs(xmlNode.ChildNodes) do
				if(subXmlNode.Name) then
					group:AddProperty(subXmlNode.Name.."_start", PROPERTY_INTEGER, "", ""..subXmlNode.Attributes.value.."")
				end
			end
		end
	end
	for i,xmlNode in pairs(obj.ChildNodes) do
		if(xmlNode.Name=="end") then
			for i,subXmlNode in pairs(xmlNode.ChildNodes) do
				if(subXmlNode.Name) then
					group:AddProperty(subXmlNode.Name.."_end", PROPERTY_INTEGER, "", ""..subXmlNode.Attributes.value.."")
				end
			end
		end
	end
end

group:Expand(1)
end

AMD Phenom II x6 1100T - 16GB RAM - ATI 5870 HD - OCZ Vertex 2 60GB SSD

76561197984667096.png

Link to comment
Share on other sites

With this I can have it run a dynamic menu for the object, as well as pass the deafult values based on the XML file information.

 

hmm, I think the issue is that the settings dialog will be the same for them all though. The InitDialog() runs the same for all instances. So if you wanted to have a character that had a different animation name than another character you couldn't.

 

I guess I was thinking that the xml file would describe animations for that given instance of the character thingoid instead of being a file that describes how the settings dialog box should look. In this case I get the fact that it wouldn't be the easiest because instead of settings in the dialog box they need to edit an xml file, BUT, follow me on my journey of thinking just now, I wonder if we could have the properties from the editor shell out to a custom exe that we create that makes editing the xml file easier. It could be a small little properties window that is more flexible in adding animations to the xml file. The result would be the xml file that the user selects into the editor settings dialog.

 

 

[edit]

Then in the setkey method, when the xml file gets selected, you parse it out and store the information in a lua table variable to that object.

 

 

I'm curious to hear where you are going with this more though.

Link to comment
Share on other sites

hmm, I think the issue is that the settings dialog will be the same for them all though. The InitDialog() runs the same for all instances. So if you wanted to have a character that had a different animation name than another character you couldn't.

 

I guess I was thinking that the xml file would describe animations for that given instance of the character thingoid instead of being a file that describes how the settings dialog box should look. In this case I get the fact that it wouldn't be the easiest because instead of settings in the dialog box they need to edit an xml file, BUT, follow me on my journey of thinking just now, I wonder if we could have the properties from the editor shell out to a custom exe that we create that makes editing the xml file easier. It could be a small little properties window that is more flexible in adding animations to the xml file. The result would be the xml file that the user selects into the editor settings dialog.

 

 

[edit]

Then in the setkey method, when the xml file gets selected, you parse it out and store the information in a lua table variable to that object.

 

 

I'm curious to hear where you are going with this more though.

 

Yeah, my thinking in this, and of course it will progress, is that no matter WHAT animations the character has, this character file will work for EVERY character/NPC.

 

You make the XML file based of that model, and as soon as you post each one in the editor, the properties fields are set.

 

If I could find out how to automatically pull the XML file name based off the model instance, then the file WOULD be fully automated.

 

the model lua file, and the model file name match, so if there was a way to tell the script the the XML file is the same, sure would help out a bunch. (*hint)

 

I will post a video, so you can see.

AMD Phenom II x6 1100T - 16GB RAM - ATI 5870 HD - OCZ Vertex 2 60GB SSD

76561197984667096.png

Link to comment
Share on other sites

The WoW data you noticed seams to be a hint of splitanimations ;) e.g. many small *.gmf`s which are just contain one single animation. If the characters are sharing the same skeleton and one can call the animation "clip" from that specific gmf to use on a (all) character i dont see a reason why not using split animations - guess its more a question of maintaining/sorting a lot of animation clips w/o having to look into a text file for which clip starts/ends where and for what character etc..

AMD 64 X2 Dual 5k - 4GB - XFX GForce9800GT - nv196.21 - WinXP Sp3

zBrush4R2 - Silo2Pro - Unwrap3DPro - Gile - MaPZone2.5

 

adv_banner-april2012_720x150_tex01.png

 

Xxploration FPS in progress ...

Link to comment
Share on other sites

ok. I had to add a little to the class.lua file to get this to work.

 

Name the XML file the same as your GMF model file.

 

change to Class.lua

--Creates a class from a model reference object
function CreateClass(modelreference)
local class={}
local super={}
class.instances={}
classtable[modelreference]=class
class.modelreference=modelreference
class.name=string.lower(StripAll(modelreference.path))
classnametable[class.name]=class
Print("Creating class "..class.name)
super.class=class

CHANGE TO:

--Creates a class from a model reference object
function CreateClass(modelreference)
local class={}
local super={}
class.instances={}
classtable[modelreference]=class
class.modelreference=modelreference
class.name=string.lower(StripAll(modelreference.path))
class.path=string.lower(StripExt(modelreference.path))
classnametable[class.name]=class
classnametable[class.path]=class.path
Print("Creating class "..class.name)
super.class=class

 

Then change the top of my file from :

 

local obj,err = XmlParser:ParseXmlFile("Models/characters/Cyber/character_visor_full.xml")

 

CHANGE TO:

local obj,err = XmlParser:ParseXmlFile(class.path..".xml")

AMD Phenom II x6 1100T - 16GB RAM - ATI 5870 HD - OCZ Vertex 2 60GB SSD

76561197984667096.png

Link to comment
Share on other sites

I don't think you want to go that route. Changing the class.lua file will cause portability/resyncing issues. You should be able to do that in the Thingoid lua file and it'll be much more portable and won't get overwritten when Josh changes the class.lua file and people resync.

 

Try:

 

string.lower(StripExt(objecttable[model].class.modelreference.path))

 

or

 

local cls = objecttable[model].class

string.lower(StripExt(cls.modelreference.path))

Link to comment
Share on other sites

ahh. ok.

 

 

Thats it. I just needed to look a little deeper. I was trying modelreference but couldn't get anything from it, but class is the table I needed.

 

local test = string.lower(StripExt(class.modelreference.path))

 

 

 

Thanks Rick

AMD Phenom II x6 1100T - 16GB RAM - ATI 5870 HD - OCZ Vertex 2 60GB SSD

76561197984667096.png

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...