summaryrefslogtreecommitdiffstatshomepage
path: root/plugins/data/database.lua
blob: fe2b9ce84bb8f58fd758cba5425fb6af2f674e22 (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
local sql = require('lsqlite3')
local db

local function check_db_result(msg)
	if db:errcode() > sql.OK then
		emu.print_error(string.format('Error: %s (%s - %s)', msg, db:errcode(), db:errmsg()))
		return false
	end
	return true
end

local function settings_path()
	return manager.machine.options.entries.homepath:value():match('([^;]+)') .. '/data'
end

local function check_version_table()
	local found = false
	db:exec(
			[[SELECT COUNT(*) FROM sqlite_master WHERE name = 'version' AND type = 'table';]],
			function (udata, cols, values, names)
				found = tonumber(values[1]) > 0
				return 0
			end)
	if check_db_result('checking for "version" table') and (not found) then
		db:exec(
				[[CREATE TABLE version (
					datfile VARCHAR NOT NULL,
					version VARCHAR NOT NULL,
					PRIMARY KEY (datfile));]])
		found = check_db_result('creating "version" table')
	end
	if not found then
		db:close()
		db = nil
	end
end

local function open_existing()
	db = sql.open(settings_path() .. '/history.db', sql.OPEN_READWRITE)
	if db then
		check_version_table()
	end
	return db
end

local function open_create()
	-- first try to create settings directory
	local dir = settings_path()
	local attr = lfs.attributes(dir)
	if (not attr) and (not lfs.mkdir(dir)) then
		emu.print_error(string.format('Error creating data plugin settings directory "%s"', dir))
	elseif attr and (attr.mode ~= 'directory') then
		emu.print_error(string.format('Error opening data plugin database: "%s" is not a directory', dir))
	else
		-- now try to open the database
		local dbpath = dir .. '/history.db'
		db = sql.open(dbpath, sql.OPEN_READWRITE | sql.OPEN_CREATE)
		if not db then
			emu.print_error(string.format('Error opening data plugin database "%s"', dbpath))
		else
			check_version_table()
		end
	end
	return db
end


local dbtable = {
	ROW = sql.ROW,
	BUSY = sql.BUSY,
	DONE = sql.DONE,
	check = check_db_result }

function dbtable.sanitize_name(name)
	return name:gsub('[^%w%."]', '_')
end

function dbtable.get_version(filename)
	-- don't try to create the database here, just return nil if it doesn't exist
	if (not db) and (not open_existing()) then
		return nil
	end
	local query = db:prepare([[SELECT version FROM version WHERE datfile = ?;]])
	query:bind_values(filename)
	local result
	while result == nil do
		local status = query:step()
		if status == sql.ROW then
			result = query:get_value(0)
		elseif status ~= sql.BUSY then
			break
		end
	end
	query:finalize()
	return result
end

function dbtable.set_version(filename, version)
	if (not db) and (not open_create()) then
		return nil
	end
	local query
	if version ~= nil then
		query = db:prepare(
			[[INSERT INTO version (datfile, version) VALUES (?1, ?2)
				ON CONFLICT(datfile) DO UPDATE set version = ?2;]])
		query:bind_values(filename, version)
	else
		query = db:prepare([[DELETE FROM version WHERE datfile = ?1;]])
		query:bind_values(filename)
	end
	local result
	while result == nil do
		local status = query:step()
		if status == sql.DONE then
			result = true
		elseif result ~= sql.ROW then
			result = false
		end
	end
	query:finalize()
	return result
end

function dbtable.prepare(...)
	if (not db) and open_create() then
		dbtable.prepare = function (...) return db:prepare(...) end
	end
	if db then
		return db:prepare(...)
	else
		return nil
	end
end

function dbtable.exec(...)
	if (not db) and open_create() then
		dbtable.exec = function (...) return db:exec(...) end
	end
	if db then
		return db:exec(...)
	else
		return nil
	end
end

function dbtable.open_data_file(file)
	local fh, filepath
	for path in mame_manager.ui.options.entries.historypath:value():gmatch('([^;]+)') do
		filepath = path .. '/' .. file
		fh = io.open(filepath, 'r')
		if fh then
			break
		end
	end
	return fh, filepath, dbtable.sanitize_name(file), dbtable.get_version(file)
end

return dbtable