1local dat_obj = {} 2local match_key = nil 3 4local function dat_lexer(f, fname) 5 local line, err = f:read("*l") 6 local location = {line_no = 1, column = 1, fname = fname} 7 return function() 8 local tok = nil 9 while not tok do 10 if not line then 11 return nil 12 end 13 pre_space, tok, line = string.match(line, "^(%s*)(..-)([()]*%s.*)") 14 if tok and string.match(tok, "^\"") then 15 tok, line = string.match(tok..line, "^\"([^\"]-)\"(.*)") 16 elseif tok and string.match(tok, "^[()]") then 17 line = tok:sub(2) .. line 18 tok = tok:sub(1,1) 19 end 20 location.column = location.column + #(pre_space or "") 21 tok_loc = { 22 line_no = location.line_no, 23 column = location.column, 24 fname = location.fname 25 } 26 if not line then 27 line = f:read("*l") 28 location.line_no = location.line_no + 1 29 location.column = 1 30 else 31 location.column = location.column + #tok 32 end 33 end 34 -- print(tok) 35 return tok, tok_loc 36 end 37end 38 39local function dat_parse_table(lexer, start_loc) 40 local res = {} 41 local state = "key" 42 local key = nil 43 for tok, loc in lexer do 44 if state == "key" then 45 if tok == ")" then 46 return res 47 elseif tok == "(" then 48 error(string.format( 49 "%s:%d:%d: fatal error: Unexpected '(' instead of key", 50 loc.fname, 51 loc.line_no, 52 loc.column 53 )) 54 else 55 key = tok 56 state = "value" 57 end 58 else 59 if tok == "(" then 60 res[key] = dat_parse_table(lexer, loc) 61 elseif tok == ")" then 62 error(string.format( 63 "%s:%d:%d: fatal error: Unexpected ')' instead of value", 64 loc.fname, 65 loc.line_no, 66 loc.column 67 )) 68 else 69 res[key] = tok 70 end 71 state = "key" 72 end 73 end 74 error(string.format( 75 "%s:%d:%d: fatal error: Missing ')' for '('", 76 start_loc.fname, 77 start_loc.line_no, 78 start_loc.column 79 )) 80end 81 82local function dat_parser(lexer) 83 local res = {} 84 local state = "key" 85 local key = nil 86 local skip = true 87 for tok, loc in lexer do 88 if state == "key" then 89 if tok == "game" then 90 skip = false 91 end 92 state = "value" 93 else 94 if tok == "(" then 95 local v = dat_parse_table(lexer, loc) 96 if not skip then 97 table.insert(res, v) 98 skip = true 99 end 100 else 101 error(string.format( 102 "%s:%d:%d: fatal error: Expected '(' found '%s'", 103 loc.fname, 104 loc.line_no, 105 loc.column, 106 tok 107 )) 108 end 109 state = "key" 110 end 111 end 112 return res 113end 114 115local function unhex(s) 116 if not s then return nil end 117 return (s:gsub('..', function (c) 118 return string.char(tonumber(c, 16)) 119 end)) 120end 121 122local function get_match_key(mk, t) 123 for p in string.gmatch(mk, "(%w+)[.]?") do 124 if p == nil or t == nil then 125 error("Invalid match key '"..mk.."'") 126 end 127 t = t[p] 128 end 129 return t 130end 131 132table.update = function(a, b) 133 for k,v in pairs(b) do 134 a[k] = v 135 end 136end 137 138function init(...) 139 local args = {...} 140 table.remove(args, 1) 141 if #args == 0 then 142 assert(dat_path, "dat file argument is missing") 143 end 144 145 if #args > 1 then 146 match_key = table.remove(args, 1) 147 end 148 149 local dat_hash = {} 150 for _, dat_path in ipairs(args) do 151 local dat_file, err = io.open(dat_path, "r") 152 if err then 153 error(" could not open dat file '" .. dat_path .. "':" .. err) 154 end 155 156 print(" " .. dat_path) 157 local objs = dat_parser(dat_lexer(dat_file, dat_path)) 158 dat_file:close() 159 for _, obj in pairs(objs) do 160 if match_key then 161 local mk = get_match_key(match_key, obj) 162 if mk == nil then 163 error(" missing match key '" .. match_key .. "' in one of the entries") 164 end 165 if dat_hash[mk] == nil then 166 dat_hash[mk] = {} 167 table.insert(dat_obj, dat_hash[mk]) 168 end 169 table.update(dat_hash[mk], obj) 170 else 171 table.insert(dat_obj, obj) 172 end 173 end 174 end 175end 176 177function get_value() 178 local t = table.remove(dat_obj) 179 if not t then 180 return 181 else 182 return { 183 name = t.name, 184 description = t.description, 185 rom_name = t.rom.name, 186 size = uint(tonumber(t.rom.size)), 187 users = uint(tonumber(t.users)), 188 releasemonth = uint(tonumber(t.releasemonth)), 189 releaseyear = uint(tonumber(t.releaseyear)), 190 rumble = uint(tonumber(t.rumble)), 191 analog = uint(tonumber(t.analog)), 192 193 famitsu_rating = uint(tonumber(t.famitsu_rating)), 194 edge_rating = uint(tonumber(t.edge_rating)), 195 edge_issue = uint(tonumber(t.edge_issue)), 196 edge_review = t.edge_review, 197 198 enhancement_hw = t.enhancement_hw, 199 barcode = t.barcode, 200 esrb_rating = t.esrb_rating, 201 elspa_rating = t.elspa_rating, 202 pegi_rating = t.pegi_rating, 203 cero_rating = t.cero_rating, 204 franchise = t.franchise, 205 206 developer = t.developer, 207 publisher = t.publisher, 208 origin = t.origin, 209 210 crc = binary(unhex(t.rom.crc)), 211 md5 = binary(unhex(t.rom.md5)), 212 sha1 = binary(unhex(t.rom.sha1)), 213 serial = binary(t.serial or t.rom.serial), 214 } 215 end 216end 217