×

Langue

Fermer
Atelier 801
  • Forums
  • Dev Tracker
  • Connexion
    • English Français
      Português do Brasil Español
      Türkçe Polski
      Magyar Română
      العربية Skandinavisk
      Nederlands Deutsch
      Bahasa Indonesia Русский
      中文 Filipino
      Lietuvių kalba 日本語
      Suomi עברית
      Italiano Česky
      Hrvatski Slovensky
      Български Latviešu
      Estonian
  • Langue
  • Forums
  • /
  • Transformice
  • /
  • Modules
  • /
  • XML library
XML library
Makinit
« Citoyen »
1396749420000
    • Makinit#0095
    • Profil
    • Derniers messages
    • Tribu
#1
  1
  • code
  • description
  • example
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
local namePattern = "[%a_:][%w%.%-_:]*"
function parseXml(xml, fast)
if not fast then
xml = string.gsub(xml, "<!%[CDATA%[(.-)%]%]>", xmlEscape) -- replace CDATA with escaped text
xml = string.gsub(xml, "<%?.-%?>", "") -- remove processing instructions
xml = string.gsub(xml, "<!%-%-.-%-%->", "") -- remove comments
xml = string.gsub(xml, "<!.->", "")
end
local root = {}
local parents = {}
local element = root
for closing, name, attributes, empty, text in string.gmatch(xml, "<(/?)(" .. namePattern .. ")(.-)(/?)>%s*([^<]*)%s*") do
if closing == "/" then
local parent = parents[element]
if parent and name == element.name then
element = parent
end
else
local child = {name = name, attribute = {}}
table.insert(element, child)
parents[child] = element
if empty ~= "/" then
element = child
end
for name, value in string.gmatch(attributes, "(" .. namePattern .. ")%s*=%s*\"(.-)\"") do
child.attribute[name] = fast and value or xmlUnescape(value)
end
end
if text ~= "" then
local child = {text = fast and text or xmlUnescape(text)}
table.insert(element, child)
parents[child] = element
end
end
return root[1]
end

function generateXml(element, fast)
if element.name then
local xml = "<" .. element.name
for name, value in pairs(element.attribute) do
xml = xml .. " " .. name .. "=\"" .. (fast and tostring(value) or xmlEscape(tostring(value))) .. "\""
end
if #element == 0 then
xml = xml .. " />"
else
xml = xml .. ">"
for i, child in ipairs(element) do
xml = xml .. generateXml(child, fast)
end
xml = xml .. "</" .. element.name .. ">"
end
return xml
elseif element.text then
return fast and tostring(element.text) or xmlEscape(tostring(element.text))
end
end

function path(nodes, ...)
nodes = {nodes}
for i, name in ipairs(arg) do
local match = {}
for i, node in ipairs(nodes) do
for i, child in ipairs(node) do
if child.name == name then
table.insert(match, child)
end
end
end
nodes = match
end
return nodes
end

--(un)escape functions
local escapeCache = {}
function xmlEscape(s)
local r = escapeCache[s ]
if not r then
local g = string.gsub
r = g(s, "&", "&amp;")
r = g(r, "\"", "&quot;")
r = g(r, "'", "&apos;")
r = g(r, "<", "&lt;")
r = g(r, ">", "&gt;")
escapeCache[s ] = r
end
return r
end
local unescapeCache = {}
function xmlUnescape(s)
local r = unescapeCache[s ]
if not r then
local g = string.gsub
r = g(s, "&quot;", "\"")
r = g(r, "&apos;", "'")
r = g(r, "&lt;", "<")
r = g(r, "&gt;", ">")
r = g(r, "&#(%d%d?%d?%d?);", dec2char)
r = g(r, "&#x(%x%x?%x?%x?);", hex2char)
r = g(r, "&amp;", "&")
unescapeCache[s ] = r
end
return r
end
function dec2char(code)
code = tonumber(code)
return string.char(code > 255 and 0 or code)
end
function hex2char(code)
code = tonumber(code, 16)
return string.char(code > 255 and 0 or code)
end
This is a set of functions I made to do the map editing in #sketch. It's a parser that can handle most XML documents and a generator that takes the output format (DOM) of the parser. The parseXml and generateXml functions both have a fast parameter. The fast mode does not escape and unescape text and cannot handle exotic nodes. However, using this is highly recommended if you only use it for simple XML like the map XML, as it's considerably faster. If you do this, you can remove the (un)escape functions, as they would only take space.

This is the format of the Document Object Model:
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
--<C><P /><Z><S><S L="200" Y="300" P="0,0,0.3,0.2,0,0,0,0" T="0" X="400" H="50" /></S><D><T X="350" Y="275" /><F X="450" Y="270" /></D><O /></Z></C>
dom = {
name = "C",
attribute = {},
[1] = {
name = "P",
attribute = {}
},
[2] = {
name = "Z",
attribute = {},
[1] = {
name = "S",
attribute = {},
[1] = {
name = "S",
attribute = {
L = "200",
Y = "300",
P = "0,0,0.3,0.2,0,0,0,0",
T = "0",
X = "400",
H = "50"
}
}
},
[2] = {
name = "D",
attribute = {},
[1] = {
name = "T",
attribute = {
X = "350",
Y = "275"
}
},
[2] = {
name = "F",
attribute = {
X = "450",
Y = "270"
}
}
},
[3] = {
name = "O",
attribute = {}
}
}
}
It starts with the root element. An element has a name property and a name-value list for attributes. It contains child nodes as indexed list. A node with the 'name' property set is an element and a node with the 'text' property set is a text node.

You can use the path method to navigate through the DOM more easily. It takes any element as first parameter and then the path of element names you want to take.

Feel free to use, modify and distribute this code. Please leave your comments, questions, suggestions and issues as a reply to this thread.
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
--example which gives grounds random colours and mixes up decorations

local ownXml = false
function eventNewGame()
--prevent reloading all the time
if ownXml then
ownXml = false
return
end

--create a table of the Document Object Model
local dom = parseXml(tfm.get.room.xmlMapInfo.xml, true)

local specialGrounds = {["8"] = true, ["9"] = true, ["13"] = true}

--iterate through ground elements
for i, ground in ipairs(path(dom, "Z", "S", "S")) do
--edit type and set random colour
if ground.attribute.T and not specialGrounds[ground.attribute.T] then
ground.attribute.T = "12"
end
ground.attribute.o = string.format("%x", math.random(0, 0xffffff))
end
--select the map properties element
local p = path(dom, "P")[1]

local specialObjects = {DS = true, DC = true, T = true}

--iterate through all decoration elements
for i, object in ipairs(path(dom, "Z", "D")[1]) do
if not specialObjects[object.name] then
--set random position attributes based on the map properties
object.attribute.X = math.random(0, p.attribute.L or 800)
object.attribute.Y = math.random(0, p.attribute.H or 400)
end
end

ownXml = true

--convert the edited DOM back to XML and load it
tfm.exec.newGame(generateXml(dom, true))
end
tfm.exec.newGame()

--library
local namePattern = "[%a_:][%w%.%-_:]*"
function parseXml(xml, fast)
if not fast then
xml = string.gsub(xml, "<!%[CDATA%[(.-)%]%]>", xmlEscape) -- replace CDATA with escaped text
xml = string.gsub(xml, "<%?.-%?>", "") -- remove processing instructions
xml = string.gsub(xml, "<!%-%-.-%-%->", "") -- remove comments
xml = string.gsub(xml, "<!.->", "")
end
local root = {}
local parents = {}
local element = root
for closing, name, attributes, empty, text in string.gmatch(xml, "<(/?)(" .. namePattern .. ")(.-)(/?)>%s*([^<]*)%s*") do
if closing == "/" then
local parent = parents[element]
if parent and name == element.name then
element = parent
end
else
local child = {name = name, attribute = {}}
table.insert(element, child)
parents[child] = element
if empty ~= "/" then
element = child
end
for name, value in string.gmatch(attributes, "(" .. namePattern .. ")%s*=%s*\"(.-)\"") do
child.attribute[name] = fast and value or xmlUnescape(value)
end
end
if text ~= "" then
local child = {text = fast and text or xmlUnescape(text)}
table.insert(element, child)
parents[child] = element
end
end
return root[1]
end

function generateXml(element, fast)
if element.name then
local xml = "<" .. element.name
for name, value in pairs(element.attribute) do
xml = xml .. " " .. name .. "=\"" .. (fast and tostring(value) or xmlEscape(tostring(value))) .. "\""
end
if #element == 0 then
xml = xml .. " />"
else
xml = xml .. ">"
for i, child in ipairs(element) do
xml = xml .. generateXml(child, fast)
end
xml = xml .. "</" .. element.name .. ">"
end
return xml
elseif element.text then
return fast and tostring(element.text) or xmlEscape(tostring(element.text))
end
end

function path(nodes, ...)
nodes = {nodes}
for i, name in ipairs(arg) do
local match = {}
for i, node in ipairs(nodes) do
for i, child in ipairs(node) do
if child.name == name then
table.insert(match, child)
end
end
end
nodes = match
end
return nodes
end

--(un)escape functions
local escapeCache = {}
function xmlEscape(s)
local r = escapeCache[s ]
if not r then
local g = string.gsub
r = g(s, "&", "&amp;")
r = g(r, "\"", "&quot;")
r = g(r, "'", "&apos;")
r = g(r, "<", "&lt;")
r = g(r, ">", "&gt;")
escapeCache[s ] = r
end
return r
end
local unescapeCache = {}
function xmlUnescape(s)
local r = unescapeCache[s ]
if not r then
local g = string.gsub
r = g(s, "&quot;", "\"")
r = g(r, "&apos;", "'")
r = g(r, "&lt;", "<")
r = g(r, "&gt;", ">")
r = g(r, "&#(%d%d?%d?%d?);", dec2char)
r = g(r, "&#x(%x%x?%x?%x?);", hex2char)
r = g(r, "&amp;", "&")
unescapeCache[s ] = r
end
return r
end
function dec2char(code)
code = tonumber(code)
return string.char(code > 255 and 0 or code)
end
function hex2char(code)
code = tonumber(code, 16)
return string.char(code > 255 and 0 or code)
end

Dernière modification le 1481367960000
Uditya
« Citoyen »
1396763220000
    • Uditya#0000
    • Profil
    • Derniers messages
    • Tribu
#2
  0
Wow thats amzaing !
Benbirkralm
« Citoyen »
1396775520000
    • Benbirkralm#0000
    • Profil
    • Derniers messages
    • Tribu
#3
  0
I like it. It's so crazy!
Arber
« Citoyen »
1396782720000
    • Arber#0000
    • Profil
    • Derniers messages
    • Tribu
#4
  0
haha this is really nice!
does this only change the tribe house map?
Makinit
« Citoyen »
1396790400000
    • Makinit#0095
    • Profil
    • Derniers messages
    • Tribu
#5
  0
Arber a dit :
haha this is really nice!
does this only change the tribe house map?

It should change any user map that's loaded in the tribe house.
Arber
« Citoyen »
1396795500000
    • Arber#0000
    • Profil
    • Derniers messages
    • Tribu
#6
  0
Makinit a dit :

It should change any user map that's loaded in the tribe house.

Oh.. because when i change map to /np 0 it automatically changes to tribe house map
Shamousey
« Consul »
1396803180000
    • Shamousey#0095
    • Profil
    • Derniers messages
    • Tribu
#7
  0
Arber a dit :
Oh.. because when i change map to /np 0 it automatically changes to tribe house map

Older vanilla maps don't have an XML.
Mouldychesse
« Citoyen »
1396808700000
    • Mouldychesse#0000
    • Profil
    • Derniers messages
    • Tribu
#8
  0
http://i.imgur.com/tebLkLR.png
I typed in one of the scripts with my maps.
Makinit
« Citoyen »
1396898940000
    • Makinit#0095
    • Profil
    • Derniers messages
    • Tribu
#9
  0
The parent property in the node table is removed, because it was not necessary and makes creating deep copies of nodes difficult. The parents are now kept track of inside the parseXml function.
In the generateXml function, the fast parameter was previously not passed to recursive calls, this is now fixed.
Epicsouris
« Citoyen »
1396900980000
    • Epicsouris#0000
    • Profil
    • Derniers messages
#10
  0
Love the amp& lt& commands, makes it noob friendly :3
Makinit
« Citoyen »
1397162220000
    • Makinit#0095
    • Profil
    • Derniers messages
    • Tribu
#11
  0
Epicsouris a dit :
Love the amp& lt& commands, makes it noob friendly :3

You can also use these to properly display text containing XML control characters, if that's what you're referring to.
Baasbase
« Citoyen »
1398858720000
    • Baasbase#0095
    • Profil
    • Derniers messages
#12
  0
thx mak
Ninjasmaus
« Citoyen »
1434271320000
    • Ninjasmaus#0000
    • Profil
    • Derniers messages
    • Tribu
#13
  0
Hi there Mak, I was wondering how you could extract information from the XML code.
For example I'm trying to find everything within <p></p> and from your generator it would be in [1] right?
However when I print(dom[1]) it doesn't give me anything.
Makinit
« Citoyen »
1434283380000
    • Makinit#0095
    • Profil
    • Derniers messages
    • Tribu
#14
  0
Ninjasmaus a dit :
Hi there Mak, I was wondering how you could extract information from the XML code.
For example I'm trying to find everything within &lt;p&gt;&lt;/p&gt; and from your generator it would be in [1] right?

Yes, if P is always the first element. To be sure you could use path:
a dit :
local p = path(dom, "P")[1]

Ninjasmaus a dit :
However when I print(dom[1]) it doesn't give me anything.

The DOM elements are just tables, so if you want to display them, you would need a way to convert them to text. For example by converting back to XML:
a dit :
print(xmlEscape(generateXml(dom[1], true)))
  • Forums
  • /
  • Transformice
  • /
  • Modules
  • /
  • XML library
© Atelier801 2018

Equipe Conditions Générales d'Utilisation Politique de Confidentialité Contact

Version 1.27