1local xml = {}
2
3function xml.filename(name)
4	return name .. ".xml"
5end
6
7-- basic xml parser for mamecheat only
8local function xml_parse(data)
9	local function fix_gt(str)
10		str = str:gsub(">=", " ge ")
11		str = str:gsub(">", " gt ")
12		return str
13	end
14	data = data:gsub("(condition=%b\"\")", fix_gt)
15	local cheat_str = data:match("<mamecheat.->(.*)</ *mamecheat>")
16
17	local function get_tags(str)
18		local arr = {}
19		while str ~= "" do
20			local tag, attr, stop
21			tag, attr, stop, str = str:match("<([%w!%-]+) ?(.-)(/?)[ %-]->(.*)")
22
23			if not tag then
24				return arr
25			end
26			if tag:sub(0, 3) ~= "!--" then
27				local block = {}
28				if stop ~= "/" then
29					local nest
30					nest, str = str:match("(.-)</ *" .. tag .. " *>(.*)")
31					local children = get_tags(nest)
32					if not next(children) then
33						nest = nest:gsub("<!--.-%-%->", "")
34						nest = nest:gsub("^%s*(.-)%s*$", "%1")
35						block["text"] = nest
36					else
37						block = children
38					end
39				end
40				if attr then
41					for name, value in attr:gmatch("(%w-)=\"(.-)\"") do
42						block[name] = value:gsub("^%s*(.-)%s*$", "%1")
43					end
44				end
45				if not arr[tag] then
46					arr[tag] = {}
47				end
48				arr[tag][#arr[tag] + 1] = block
49			end
50		end
51		return arr
52	end
53	local xml_table = get_tags(cheat_str)
54	return xml_table
55end
56
57function xml.conv_cheat(data)
58	local spaces, regions, output
59	data = xml_parse(data)
60	local cpu_spaces = {}
61
62	for tag, device in pairs(manager:machine().devices) do
63		local sp
64		for name, space in pairs(device.spaces) do
65			if not sp then
66				sp = {}
67				cpu_spaces[tag] = sp
68			end
69			sp[space.index] = space.name
70		end
71	end
72
73	local function convert_expr(data)
74		local write = false
75
76		local function convert_memref(cpu, phys, space, width, addr, rw)
77			-- debug expressions address spaces by index not by name
78			local function get_space_name(index)
79				local prefix = cpu:sub(1,1)
80				if prefix == ":" then
81					return cpu_spaces[cpu][index]
82				else
83					return cpu_spaces[":" .. cpu][index]
84				end
85			end
86
87			local mod = ""
88			local count
89			if space == "p" then
90				fullspace = get_space_name(0)
91			elseif space == "d" then
92				fullspace = get_space_name(1)
93			elseif space == "i" then
94				fullspace = get_space_name(2)
95			elseif space == "r" then
96				fullspace = get_space_name(0)
97				mod = "direct_"
98				space = "p"
99			elseif space == "o" then
100				fullspace = get_space_name(3)
101				mod = "direct_"
102				space = "o"
103			end
104			if width == "b" then
105				width = "u8"
106			elseif width == "w" then
107				width = "u16"
108			elseif width == "d" then
109				width = "u32"
110			elseif width == "q" then
111				width = "u64"
112			end
113
114			local prefix = cpu:sub(1,1)
115			if prefix == ":" then
116				cpu = cpu:sub(2,cpu:len())
117			end
118
119			local cpuname = cpu:gsub(":", "_")
120			if space == "m" then
121				regions[cpuname .. space] = ":" .. cpu
122			else
123				spaces[cpuname .. space] = { tag = ":" .. cpu, type = fullspace }
124				if phys ~= "p" and mod == "" then
125					mod = "log_"
126				end
127			end
128			if rw == "=" then
129				write = true
130				ret = cpuname .. space .. ":" .. "write_" .. mod .. width .. "(" .. addr .. ","
131			else
132				ret = cpuname .. space .. ":" .. "read_" .. mod .. width .. "(" .. addr .. ")"
133			end
134			if rw == "==" then
135				ret = ret .. "=="
136			end
137			return ret
138		end
139
140		local function frame()
141			output = true
142			return "screen:frame_number()"
143		end
144
145		data = data:lower()
146		data = data:gsub("^[(](.-)[)]$", "%1")
147		data = data:gsub("%f[%w]lt%f[%W]", "<")
148		data = data:gsub("%f[%w]ge%f[%W]", ">=")
149		data = data:gsub("%f[%w]gt%f[%W]", ">")
150		data = data:gsub("%f[%w]le%f[%W]", "<=")
151		data = data:gsub("%f[%w]eq%f[%W]", "==")
152		data = data:gsub("%f[%w]ne%f[%W]", "~=")
153		data = data:gsub("!=", "~=")
154		data = data:gsub("||", " or ")
155		data = data:gsub("%f[%w]frame%f[%W]", frame)
156		data = data:gsub("%f[%w]band%f[%W]", "&")
157		data = data:gsub("%f[%w]bor%f[%W]", "|")
158		data = data:gsub("%f[%w]rshift%f[%W]", ">>")
159		data = data:gsub("%f[%w]lshift%f[%W]", "<<")
160		data = data:gsub("(%w-)%+%+", "%1 = %1 + 1")
161		data = data:gsub("%f[%w](%x+)%f[%W]", "0x%1")
162		-- 0?x? avoids an issue where db (data region byte) is interepeted as a hex number
163		data = data:gsub("([%w_:]-)%.(p?)0?x?([pmrodi3])([bwdq])@(%w+) *(=*)", convert_memref)
164		repeat
165			data, count = data:gsub("([%w_:]-)%.(p?)0?x?([pmrodi3])([bwdq])@(%b()) *(=*)", convert_memref)
166		until count == 0
167		if write then
168			data = data .. ")"
169		end
170		return data
171	end
172
173	local function convert_output(data)
174		local str = "draw_text(screen,"
175		if data["align"] then
176			str = str .. data["align"]
177		else
178			str = str .. "\"left\""
179		end
180		if data["line"] then
181			str = str .. ",\"" .. data["line"] .. "\""
182		else
183			str = str .. ", \"auto\""
184		end
185		str = str .. ", nil,\"" .. data["format"] .. "\""
186		if data["argument"] then
187			for count, block in pairs(data["argument"]) do
188				local expr = convert_expr(block["text"])
189				if block["count"] then
190					for i = 0, block["count"] - 1 do
191						str = str .. "," .. expr:gsub("argindex", i)
192					end
193				else
194					str = str .. "," .. expr
195				end
196			end
197		end
198		return str .. ")"
199	end
200
201	local function convert_script(data)
202		local str = ""
203		local state = "run"
204		for tag, block in pairs(data) do
205			if tag == "state" then
206				state = block
207			elseif tag == "action" then
208				for count, action in pairs(block) do
209					if action["condition"] then
210						str = str .. " if (" .. convert_expr(action["condition"]) .. ") then "
211						for expr in action["text"]:gmatch("([^,]+)") do
212							str = str .. convert_expr(expr) .. " "
213						end
214						str = str .. "end"
215					else
216						for expr in action["text"]:gmatch("([^,]+)") do
217							str = str .. " " .. convert_expr(expr) .. " "
218						end
219					end
220				end
221			elseif tag == "output" then
222				output = true
223				for count, output in pairs(block) do
224					if output["condition"] then
225						str = str .. " if " .. convert_expr(output["condition"]) .. " then "
226						str = str .. convert_output(output) .. " end "
227					else
228						str = str .. " " .. convert_output(output) .. " "
229					end
230				end
231			end
232		end
233		return state, str
234	end
235
236	for count, cheat in pairs(data["cheat"]) do
237		spaces = {}
238		regions = {}
239		output = false
240		for tag, block in pairs(cheat) do
241			if tag == "comment" then
242				data["cheat"][count]["comment"] = block[1]["text"]
243			elseif tag == "script" then
244				local scripts = {}
245				for count2, script in pairs(block) do
246					local state, str = convert_script(script)
247					scripts[state] = str
248				end
249				data["cheat"][count]["script"] = scripts
250			elseif tag == "parameter" then
251				if block[1]["min"] then
252					block[1]["min"] = block[1]["min"]:gsub("%$","0x")
253				end
254				if block[1]["max"] then
255					block[1]["max"] = block[1]["max"]:gsub("%$","0x")
256				end
257				if block[1]["step"] then
258					block[1]["step"] = block[1]["step"]:gsub("%$","0x")
259				end
260			data["cheat"][count]["parameter"] = block[1]
261			end
262		end
263		if next(spaces) then
264			data["cheat"][count]["space"] = {}
265			for name, space in pairs(spaces) do
266				data["cheat"][count]["space"] = {}
267				data["cheat"][count]["space"][name] = { type = space["type"], tag = space["tag"] }
268			end
269		end
270		if next(regions) then
271			data["cheat"][count]["region"] = {}
272			for name, region in pairs(regions) do
273				data["cheat"][count]["region"] = {}
274				data["cheat"][count]["region"][name] = region
275			end
276		end
277		if output then
278			data["cheat"][count]["screen"] = {}
279			data["cheat"][count]["screen"]["screen"] = ":screen"
280		end
281	end
282	return data["cheat"]
283end
284
285return xml
286