summaryrefslogtreecommitdiffstatshomepage
path: root/plugins/data/load_dat.lua
blob: ed13fbefdcca8d0b2f34ae8bf5e4f4e81a05367f (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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
local datfile = {}
local db = require("data/database")

local function readret(file)
	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]])
		db.check("reading " .. tag1 .. " - " .. tag2 .. " - " .. set)
		stmt:bind_values(tag1, tag2, set)
		if stmt:step() == db.ROW then
			data = stmt:get_value(0)
		end
		stmt:finalize()
		return data
	end
	return read
end


function datfile.open(file, vertag, fixupcb)
	if not db then
		return nil
	end
	local ver, dbver
	local filepath
	local fh

	for path in mame_manager.ui.options.entries.historypath:value():gmatch("([^;]+)") do
		filepath = emu.subst_env(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 = ?")
	db.check("reading version")
	stmt:bind_values(file)
	if stmt:step() == db.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 readret(file), 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.check("creating index")
		db.exec("CREATE TABLE \"" .. file .. "\" (data CLOB NOT NULL)")
		db.check("creating table")
		db.exec("CREATE INDEX \"typeval_" .. file .. "\" ON \"" .. file .. "_idx\"(type, val)")
		db.check("creating typeval index")
	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
	end
	if not ver then
		-- use file ctime for version
		ver = tostring(lfs.attributes(filepath, "change"))
	end
	if ver == dbver then
		fh:close()
		return readret(file), dbver
	end

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

	do
		fh:seek("set")
		local buffer = fh:read("a")
		db.exec("BEGIN TRANSACTION")
		db.check("beginning transaction")
		local function gmatchpos()
			local pos = 1
			local function iter()
				local tags, data
				while not data do
					local npos
					local spos, epos = buffer:find("[\n\r]$[^=]*=[^\n\r]*", pos)
					if not spos then
						return nil
					end
					npos, epos = buffer:find("[\n\r]$%w*[\n\r]+", epos)
					if not npos then
						return nil
					end
					tags = buffer:sub(spos, epos)
					spos, npos = buffer:find("[\n\r]$end[\n\r]", epos)
					if not spos then
						return nil
					end
					data = buffer:sub(epos, spos)
					pos = npos
				end
				return tags, data
			end
			return iter
		end
		for info, data in gmatchpos() do
			local tags = {}
			local infotype
			for s in info:gmatch("[\n\r]$([^\n\r]*)") do
				if s:find("=", 1, true) then
					local m1, m2 = s:match("([^=]*)=(.*)")
					for tag in m1:gmatch("[^,]+") do
						for set in m2:gmatch("[^,]+") do
							tags[#tags + 1] = { tag = tag, set = set }
						end
					end
				else
					infotype = s
					break
				end
			end

			if #tags > 0 and infotype then
				data = data:gsub("\r", "") -- strip crs
				if fixupcb then
					data = fixupcb(data)
				end
				stmt = db.prepare("INSERT INTO \"" .. file .. "\" VALUES (?)")
				db.check("inserting values")
				stmt:bind_values(data)
				stmt:step()
				local row = stmt:last_insert_rowid()
				stmt:finalize()
				for num, tag in pairs(tags) do
					stmt = db.prepare("INSERT INTO \"" .. file .. "_idx\" VALUES (?, ?, ?, ?)")
					db.check("inserting into index")
					stmt:bind_values(infotype, tag.tag, tag.set, row)
					stmt:step()
					stmt:finalize()
				end
			end
		end
		db.exec("END TRANSACTION")
		db.check("ending transaction")
	end
	fh:close()

	return readret(file), ver
end

return datfile