jerome@65: local M, bin = {}, require("casc.bin") jerome@65: local assert, loadstring, smatch = assert, loadstring or load, string.match jerome@65: jerome@65: local uint32_le, int32_le, float32_le = bin.uint32_le, bin.int32_le, bin.float32_le jerome@65: jerome@65: local function unpacker(data, format, rows, stride, hsize, sbase, tfunc) jerome@65: tfunc = type(tfunc) == "function" and tfunc or nil jerome@65: jerome@65: local skip, p, pe = 0, [=[-- casc.dbc:iterator jerome@65: local smatch, uint32_le, int32_le, float32_le, tfunc, data, rows, stride, sbase, rpos, i = ... jerome@65: return function() jerome@65: if i < rows then jerome@65: rpos, i = rpos + stride, i + 1 jerome@65: return ]=] .. (tfunc and "tfunc(i" or "i"), (tfunc and ")" or "") .. '\nend\nend' jerome@65: jerome@65: for r, t in format:gmatch("(%d*)(.)") do jerome@65: r = tonumber(r) or 1 jerome@65: for i=1,r do jerome@65: if t == '.' then jerome@65: skip = skip + 4 * r jerome@65: break jerome@65: elseif t == 'u' then jerome@65: p, skip = p .. ', uint32_le(data, rpos+' .. skip .. ')', skip + 4 jerome@65: elseif t == 'i' then jerome@65: p, skip = p .. ', int32_le(data, rpos+' .. skip .. ')', skip + 4 jerome@65: elseif t == 'f' then jerome@65: p, skip = p .. ', float32_le(data, rpos+' .. skip .. ')', skip + 4 jerome@65: elseif t == 's' then jerome@65: assert(sbase, "invalid signature: 's' requires a string block") jerome@65: p, skip = p .. ', smatch(data, "%Z*", sbase + uint32_le(data,rpos+' .. skip .. '))', skip + 4 jerome@65: else jerome@65: error('Unknown signature field type "' .. t .. '"') jerome@65: end jerome@65: end jerome@65: end jerome@65: jerome@65: return loadstring(p .. pe)(smatch, uint32_le, int32_le, float32_le, jerome@65: tfunc, data, rows, stride, sbase, hsize - stride, 0), skip jerome@65: end jerome@65: jerome@65: local header do jerome@65: local function dbc(data) jerome@65: assert(data:sub(1,4) == "WDBC", "DBC magic signature") jerome@65: local rows, fields, stride, stringSize = uint32_le(data, 4), uint32_le(data, 8), uint32_le(data, 12), uint32_le(data, 16) jerome@65: assert(20 + rows*stride + stringSize <= #data, "Data too short") jerome@65: jerome@65: return rows, fields, stride, 20, 21 + rows * stride jerome@65: end jerome@65: local function db2(data) jerome@65: local hsize = 48 jerome@65: local rows, fields, stride, stringSize = uint32_le(data, 4), uint32_le(data, 8), uint32_le(data, 12), uint32_le(data, 16) jerome@65: local build, minId, maxId, locale, rid = uint32_le(data, 24), uint32_le(data, 32), uint32_le(data, 36), uint32_le(data, 40) jerome@65: jerome@65: if maxId > 0 then jerome@65: local n, p = maxId-minId + 1, hsize jerome@65: rid, hsize = {}, hsize + 6 * n jerome@65: for i=1,n do jerome@65: rid[i], p = uint32_le(data, p), p + 6 jerome@65: end jerome@65: end jerome@65: assert(hsize + rows*stride + stringSize <= #data, "Data too short") jerome@65: jerome@65: return rows, fields, stride, hsize, hsize + 1 + rows * stride, rid, minId, maxId, build, locale jerome@65: end jerome@65: header = {WDBC=dbc, WDB2=db2, WCH2=db2} jerome@65: end jerome@65: jerome@65: function M.header(data) jerome@65: assert(type(data) == "string", 'Syntax: casc.dbc.header("data")') jerome@65: local fourCC = data:sub(1,4) jerome@65: return assert(header[fourCC], "Unsupported format")(data) jerome@65: end jerome@65: jerome@65: function M.rows(data, sig, loose) jerome@65: assert(type(data) == "string" and type(sig) == "string", 'Syntax: casc.dbc.rows("data", "rowSignature"[, loose])') jerome@65: jerome@65: local rows, _, stride, hsize, sbase, rid = M.header(data) jerome@65: local iter, skip = unpacker(data, sig, rows, stride, hsize, sbase, rid and function(i, ...) return rid[i], ... end) jerome@65: assert(skip <= stride, 'signature exceeds stride') jerome@65: assert(loose or skip == stride, 'signature/stride mismatch') jerome@65: jerome@65: return iter jerome@65: end jerome@65: jerome@65: return M