summaryrefslogtreecommitdiffstatshomepage
path: root/plugins/data/load_dat.lua
blob: d1d57093d47f860488228041594bb986508c9acf (plain) (blame)
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
local datfile = {}
local db, sql = require("data/database")()

function datfile.open(file, vertag, fixupcb)
	if not db then
		return nil
	end
	local function read(tag1, tag2, set)
		local data
		local stmt = db:prepare("SELECT f.data FROM \"" .. file .. "_idx\" AS fi, \"" .. file .. [["
		 AS f WHERE fi.type = ? AND fi.val = ? AND fi.romset = ? AND f.rowid = fi.data]])
		stmt:bind_values(tag1, tag2, set)
		if stmt:step() == sql.ROW then
			data = stmt:get_value(0)
		end
		stmt:finalize()
		return data
	end

	local ver, dbver
	local filepath
	local fh

	for path in mame_manager:ui():options().entries.historypath:value():gmatch("([^;]+)") do
		filepath = lfs.env_replace(path) .. "/" .. file
		fh = io.open(filepath, "r")
		if fh then
			break
		end
	end
	-- remove unsafe chars from file for use in sql statement
	file = file:gsub("[^%w%._]", "")

	local stmt = db:prepare("SELECT version FROM version WHERE datfile = ?")
	stmt:bind_values(file)
	if stmt:step() == sql.ROW then
		dbver = stmt:get_value(0)
	end
	stmt:finalize()

	if not fh and dbver then
		-- data in database but missing file, just use what we have
		return read, dbver
	elseif not fh then
		return nil
	elseif not dbver then
		db:exec("CREATE TABLE \"" .. file .. [[_idx" (
				type VARCHAR NOT NULL,
				val VARCHAR NOT NULL,
				romset VARCHAR NOT NULL,
				data INTEGER NOT NULL)]])
		db:exec("CREATE TABLE \"" .. file .. "\" (data CLOB NOT NULL)")
		db:exec("CREATE INDEX \"typeval_" .. file .. "\" ON \"" .. file .. "_idx\"(type, val)")
	end

	if vertag then
		for line in fh:lines() do
			local match = line:match(vertag .. "%s*([^%s]+)")
			if match then
				ver = match
				break
			end
		end
	else
		-- use file ctime for version
		ver = tostring(lfs.attributes(filepath, "change"))
	end
	if ver == dbver then
		fh:close()
		return read, dbver
	end

	if dbver then
		db:exec("DELETE FROM \"" .. file .. "\"")
		db:exec("DELETE FROM \"" .. file .. "_idx\"")
		stmt = db:prepare("UPDATE version SET version = ? WHERE datfile = ?")
	else
		stmt = db:prepare("INSERT INTO version VALUES (?, ?)")
	end
	stmt:bind_values(ver, file)
	stmt:step()
	stmt:finalize()

	do
		local inblock = false
		fh:seek("set")
		local buffer = fh:read("a")
		db:exec("BEGIN TRANSACTION")
		local function gmatchpos()
			local pos = 1
			local function iter()
				local tag1, tag2, data, start, inblock = false
				while not data do
					local spos, epos, match = buffer:find("\n($[^\n]*)", pos)
					if not spos then
						return nil
					end
					if match ~= "$end" and not inblock then
						if not tag1 then
							tag1 = match
						else
							tag2 = match
							start = epos + 1
							inblock = true
						end
					elseif inblock == true then
						data = buffer:sub(start, spos)
						inblock = false
					end
					pos = epos
				end
				return tag1, tag2, data
			end
			return iter
		end
		for info1, info2, data in gmatchpos() do
			local tag, set = info1:match("^%$([^%s=]+)=?([^%s]*)")
			if set and set ~= "" then
				local tags = {}
				local sets = {}
				tag:gsub("([^,]+)", function(s) tags[#tags + 1] = s end)
				set:gsub("([^,]+)", function(s) sets[#sets + 1] = s end)
				if #tags > 0 and #sets > 0 then
					local tag1 = info2:match("^$([^%s]*)")
					data = data:gsub("\r", "") -- strip crs
					if fixupcb then
						data = fixupcb(data)
					end
					stmt = db:prepare("INSERT INTO \"" .. file .. "\" VALUES (?)")
					stmt:bind_values(data)
					stmt:step()
					local row = stmt:last_insert_rowid()
					stmt:finalize()
					for num1, tag2 in pairs(tags) do
						for num2, set in pairs(sets) do
							if fixupcb then
								fixupcb(data)
							end
							stmt = db:prepare("INSERT INTO \"" .. file .. "_idx\" VALUES (?, ?, ?, ?)")
							stmt:bind_values(tag1, tag2, set, row)
							stmt:step()
							stmt:finalize()
						end
					end
				end
			end
		end
		db:exec("END TRANSACTION")
	end
	fh:close()

	return read, ver
end

return datfile