2015-01-19 21:47:09 +00:00
|
|
|
local dat_obj = {}
|
|
|
|
local match_key = nil
|
2015-01-07 17:34:37 +00:00
|
|
|
|
2015-01-25 17:59:52 +00:00
|
|
|
local function dat_lexer(f, fname)
|
2015-01-07 17:34:37 +00:00
|
|
|
local line, err = f:read("*l")
|
2015-01-25 17:59:52 +00:00
|
|
|
local location = {line_no = 1, column = 1, fname = fname}
|
2015-01-07 17:34:37 +00:00
|
|
|
return function()
|
|
|
|
local tok = nil
|
|
|
|
while not tok do
|
|
|
|
if not line then
|
|
|
|
return nil
|
|
|
|
end
|
2015-01-25 17:59:52 +00:00
|
|
|
pre_space, tok, line = string.match(line, "^(%s*)(..-)([()]*%s.*)")
|
2015-01-07 17:34:37 +00:00
|
|
|
if tok and string.match(tok, "^\"") then
|
|
|
|
tok, line = string.match(tok..line, "^\"([^\"]-)\"(.*)")
|
2015-01-25 17:59:52 +00:00
|
|
|
elseif tok and string.match(tok, "^[()]") then
|
|
|
|
line = tok:sub(2) .. line
|
|
|
|
tok = tok:sub(1,1)
|
2015-01-07 17:34:37 +00:00
|
|
|
end
|
2015-01-25 17:59:52 +00:00
|
|
|
location.column = location.column + #(pre_space or "")
|
|
|
|
tok_loc = {
|
|
|
|
line_no = location.line_no,
|
|
|
|
column = location.column,
|
|
|
|
fname = location.fname
|
|
|
|
}
|
2015-01-07 17:34:37 +00:00
|
|
|
if not line then
|
|
|
|
line = f:read("*l")
|
2015-01-25 17:59:52 +00:00
|
|
|
location.line_no = location.line_no + 1
|
|
|
|
location.column = 1
|
|
|
|
else
|
|
|
|
location.column = location.column + #tok
|
2015-01-07 17:34:37 +00:00
|
|
|
end
|
|
|
|
end
|
2015-01-25 18:05:02 +00:00
|
|
|
-- print(tok)
|
2015-01-25 17:59:52 +00:00
|
|
|
return tok, tok_loc
|
2015-01-07 17:34:37 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-01-25 17:59:52 +00:00
|
|
|
local function dat_parse_table(lexer, start_loc)
|
2015-01-07 17:34:37 +00:00
|
|
|
local res = {}
|
|
|
|
local state = "key"
|
|
|
|
local key = nil
|
2015-01-25 17:59:52 +00:00
|
|
|
for tok, loc in lexer do
|
2015-01-07 17:34:37 +00:00
|
|
|
if state == "key" then
|
|
|
|
if tok == ")" then
|
|
|
|
return res
|
2015-01-25 17:59:52 +00:00
|
|
|
elseif tok == "(" then
|
|
|
|
error(string.format(
|
|
|
|
"%s:%d:%d: fatal error: Unexpected '(' instead of key",
|
|
|
|
loc.fname,
|
|
|
|
loc.line_no,
|
|
|
|
loc.column
|
|
|
|
))
|
2015-01-07 17:34:37 +00:00
|
|
|
else
|
|
|
|
key = tok
|
|
|
|
state = "value"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if tok == "(" then
|
2015-01-25 17:59:52 +00:00
|
|
|
res[key] = dat_parse_table(lexer, loc)
|
|
|
|
elseif tok == ")" then
|
|
|
|
error(string.format(
|
|
|
|
"%s:%d:%d: fatal error: Unexpected ')' instead of value",
|
|
|
|
loc.fname,
|
|
|
|
loc.line_no,
|
|
|
|
loc.column
|
|
|
|
))
|
2015-01-07 17:34:37 +00:00
|
|
|
else
|
|
|
|
res[key] = tok
|
|
|
|
end
|
|
|
|
state = "key"
|
|
|
|
end
|
|
|
|
end
|
2015-01-25 17:59:52 +00:00
|
|
|
error(string.format(
|
|
|
|
"%s:%d:%d: fatal error: Missing ')' for '('",
|
|
|
|
start_loc.fname,
|
|
|
|
start_loc.line_no,
|
|
|
|
start_loc.column
|
|
|
|
))
|
2015-01-07 17:34:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local function dat_parser(lexer)
|
|
|
|
local res = {}
|
|
|
|
local state = "key"
|
|
|
|
local key = nil
|
|
|
|
local skip = true
|
2015-01-25 17:59:52 +00:00
|
|
|
for tok, loc in lexer do
|
2015-01-07 17:34:37 +00:00
|
|
|
if state == "key" then
|
|
|
|
if tok == "game" then
|
|
|
|
skip = false
|
|
|
|
end
|
|
|
|
state = "value"
|
|
|
|
else
|
|
|
|
if tok == "(" then
|
2015-01-25 17:59:52 +00:00
|
|
|
local v = dat_parse_table(lexer, loc)
|
2015-01-07 17:34:37 +00:00
|
|
|
if not skip then
|
|
|
|
table.insert(res, v)
|
|
|
|
skip = true
|
|
|
|
end
|
|
|
|
else
|
2015-01-25 17:59:52 +00:00
|
|
|
error(string.format(
|
|
|
|
"%s:%d:%d: fatal error: Expected '(' found '%s'",
|
|
|
|
loc.fname,
|
|
|
|
loc.line_no,
|
|
|
|
loc.column,
|
|
|
|
tok
|
|
|
|
))
|
2015-01-07 17:34:37 +00:00
|
|
|
end
|
|
|
|
state = "key"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return res
|
|
|
|
end
|
|
|
|
|
|
|
|
local function unhex(s)
|
|
|
|
if not s then return nil end
|
2015-01-14 17:51:09 +00:00
|
|
|
return (s:gsub('..', function (c)
|
|
|
|
return string.char(tonumber(c, 16))
|
2015-01-07 17:34:37 +00:00
|
|
|
end))
|
|
|
|
end
|
|
|
|
|
2015-01-19 21:47:09 +00:00
|
|
|
local function get_match_key(mk, t)
|
|
|
|
for p in string.gmatch(mk, "(%w+)[.]?") do
|
|
|
|
if p == nil or t == nil then
|
|
|
|
error("Invalid match key '"..mk.."'")
|
|
|
|
end
|
|
|
|
t = t[p]
|
|
|
|
end
|
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
|
|
|
table.update = function(a, b)
|
|
|
|
for k,v in pairs(b) do
|
|
|
|
a[k] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-01-07 17:34:37 +00:00
|
|
|
function init(...)
|
|
|
|
local args = {...}
|
2015-01-19 21:47:09 +00:00
|
|
|
table.remove(args, 1)
|
|
|
|
if #args == 0 then
|
|
|
|
assert(dat_path, "dat file argument is missing")
|
|
|
|
end
|
|
|
|
|
|
|
|
if #args > 1 then
|
|
|
|
match_key = table.remove(args, 1)
|
2015-01-07 17:34:37 +00:00
|
|
|
end
|
|
|
|
|
2015-01-19 21:47:09 +00:00
|
|
|
local dat_hash = {}
|
|
|
|
for _, dat_path in ipairs(args) do
|
|
|
|
local dat_file, err = io.open(dat_path, "r")
|
|
|
|
if err then
|
|
|
|
error("could not open dat file '" .. dat_path .. "':" .. err)
|
|
|
|
end
|
|
|
|
|
|
|
|
print("Parsing dat file '" .. dat_path .. "'...")
|
2015-01-25 17:59:52 +00:00
|
|
|
local objs = dat_parser(dat_lexer(dat_file, dat_path))
|
2015-01-19 21:47:09 +00:00
|
|
|
dat_file:close()
|
|
|
|
for _, obj in pairs(objs) do
|
|
|
|
if match_key then
|
|
|
|
local mk = get_match_key(match_key, obj)
|
|
|
|
if mk == nil then
|
|
|
|
error("missing match key '" .. match_key .. "' in one of the entries")
|
|
|
|
end
|
|
|
|
if dat_hash[mk] == nil then
|
|
|
|
dat_hash[mk] = {}
|
|
|
|
table.insert(dat_obj, dat_hash[mk])
|
|
|
|
end
|
|
|
|
table.update(dat_hash[mk], obj)
|
|
|
|
else
|
|
|
|
table.insert(dat_obj, obj)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-01-07 17:34:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function get_value()
|
|
|
|
local t = table.remove(dat_obj)
|
|
|
|
if not t then
|
|
|
|
return
|
|
|
|
else
|
|
|
|
return {
|
|
|
|
name = t.name,
|
|
|
|
description = t.description,
|
|
|
|
rom_name = t.rom.name,
|
|
|
|
size = uint(tonumber(t.rom.size)),
|
|
|
|
users = uint(tonumber(t.users)),
|
|
|
|
releasemonth = uint(tonumber(t.releasemonth)),
|
|
|
|
releaseyear = uint(tonumber(t.releaseyear)),
|
|
|
|
rumble = uint(tonumber(t.rumble)),
|
|
|
|
analog = uint(tonumber(t.analog)),
|
2015-01-25 17:59:52 +00:00
|
|
|
|
2015-02-02 03:42:10 +00:00
|
|
|
famitsu_rating = uint(tonumber(t.famitsu_rating)),
|
2015-01-20 02:36:50 +00:00
|
|
|
edge_rating = uint(tonumber(t.edge_rating)),
|
|
|
|
edge_issue = uint(tonumber(t.edge_issue)),
|
2015-01-27 18:20:39 +00:00
|
|
|
edge_review = t.edge_review,
|
2015-01-07 17:34:37 +00:00
|
|
|
|
2015-01-28 10:08:44 +00:00
|
|
|
enhancement_hw = t.enhancement_hw,
|
2015-01-20 02:36:50 +00:00
|
|
|
barcode = t.barcode,
|
2015-01-07 17:34:37 +00:00
|
|
|
esrb_rating = t.esrb_rating,
|
|
|
|
elspa_rating = t.elspa_rating,
|
|
|
|
pegi_rating = t.pegi_rating,
|
|
|
|
cero_rating = t.cero_rating,
|
2015-01-27 17:09:19 +00:00
|
|
|
franchise = t.franchise,
|
2015-01-07 17:34:37 +00:00
|
|
|
|
2015-01-24 07:19:37 +00:00
|
|
|
developer = t.developer,
|
2015-01-07 17:34:37 +00:00
|
|
|
publisher = t.publisher,
|
|
|
|
origin = t.origin,
|
|
|
|
|
|
|
|
crc = binary(unhex(t.rom.crc)),
|
|
|
|
md5 = binary(unhex(t.rom.md5)),
|
|
|
|
sha1 = binary(unhex(t.rom.sha1)),
|
2015-01-20 18:36:36 +00:00
|
|
|
serial = binary(t.serial or t.rom.serial),
|
2015-01-07 17:34:37 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|