| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509 |
- --[[
- How to use:
- Put all lua files into DatabaseRoot then call ExportDatabaseLocalText( tofile = true, newStringBank = false ) at the end of file. ( beware: all your original files will be replaced with optimized files )
- If you want to exlucde some input files in DatabaseRoot, just add theirs names into ExcludedFiles
- --]]
- local Root = "."
- if arg and arg[0] then
- local s, e = string.find( arg[0], "Assets" )
- if e then
- Root = string.sub( arg[0], 1, e )
- end
- end
- Root = string.gsub( Root, '\\', '/' )
- local DatabaseRoot = Root.."/Lua/config"
- local LuaRoot = Root.."/Lua/config"
- package.path = package.path..';'..DatabaseRoot..'/?.lua'..';'.. LuaRoot..'/?.lua'
- local EnableDatasetOptimize = true
- local EnableDefaultValueOptimize = true
- local EnableLocalization = true -- set to false to disable localization process
- local Database = {}
- local CSV --= require "std.csv"
- local DefaultNumberSerializedFormat = "%.14g"
- local NumberSerializedFormat = DefaultNumberSerializedFormat
- local DatabaseLocaleTextName = "_LocaleText"
- local StringBankOutput = DatabaseRoot.."/"..DatabaseLocaleTextName..".lua"
- local StringBankCSVOutput = DatabaseRoot.."/"..DatabaseLocaleTextName..".csv"
- local MaxStringBankRedundancy = 100
- local MaxStringBankBinSize = 524288
- local LocaleTextLeadingTag = '@'
- local MaxLocalVariableNum = 160 -- lparser.c #define MAXVARS 200
- local RefTableName = "__rt"
- local DefaultValueTableName = "__default_values"
- local PrintTableRefCount = false
- local UnknownName = "___noname___"
- local floor = math.floor
- local fmod = math.fmod
- local ExcludedFiles = {
- --Add file name to exclude from build
- _LocaleText = true,
- }
- local UniquifyTables = {} -- hash -> table
- local UniquifyTablesIds = {} -- id -> hash
- local UniquifyTablesInvIds = {} -- table -> id
- local UniquifyTablesRefCounter = {} -- table -> refcount
- local function HashString( v )
- local val = 0
- local fmod = fmod
- local gmatch = string.gmatch
- local byte = string.byte
- local MaxStringBankBinSize = MaxStringBankBinSize
- local c
- for _c in gmatch( v, "." ) do
- c = byte( _c )
- val = val + c * 193951
- val = fmod( val, MaxStringBankBinSize )
- val = val * 399283
- val = fmod( val, MaxStringBankBinSize )
- end
- return val
- end
- local function AddStringToBank( stringBank, str )
- local meta = getmetatable( stringBank )
- local reversed = nil
- local counter = nil
- if not meta then
- meta = {
- __counter = { used = {} }, -- mark used hash value
- __reversed = {} -- string -> hash reverse lookup
- }
- reversed = meta.__reversed
- counter = meta.__counter
- setmetatable( stringBank, meta )
- local remove = {}
- -- lazy initialize reverse lut
- for h, s in pairs( stringBank ) do
- local _h = reversed[ s ]
- assert( _h == nil )
- reversed[ s ] = h
- end
- end
- reversed = reversed or meta.__reversed
- counter = counter or meta.__counter
- local hash = reversed[ str ]
- if hash then
- counter.used[ hash ] = true
- return hash
- end
- hash = HashString( str )
- local _v = stringBank[ hash ]
- while _v do
- hash = hash + 1
- hash = fmod( hash, MaxStringBankBinSize )
- _v = stringBank[ hash ]
- end
- assert( not reversed[ str ] )
- stringBank[ hash ] = str
- reversed[ str ] = hash
- counter.used[ hash ] = true
- return hash
- end
- local function OrderedForeach( _table, _func )
- if type( _table ) == "table" then
- local kv = {}
- for k, v in pairs( _table ) do
- kv[ #kv + 1 ] = { k, v }
- end
- table.sort( kv,
- function( _l, _r )
- local l = _l[ 1 ]
- local r = _r[ 1 ]
- local lt = type( l )
- local rt = type( r )
- if lt == rt and lt ~= "table" then
- return l < r
- else
- return tostring( l ) < tostring( r )
- end
- end
- )
- for _, _v in ipairs( kv ) do
- local k = _v[ 1 ]
- local v = _v[ 2 ]
- _func( k, v )
- end
- end
- end
- local function OrderedForeachByValue( _table, _func )
- if type( _table ) == "table" then
- local kv = {}
- for k, v in pairs( _table ) do
- kv[ #kv + 1 ] = { k, v }
- end
- table.sort( kv,
- function( _l, _r )
- local l = _l[ 2 ]
- local r = _r[ 2 ]
- local lt = type( l )
- local rt = type( r )
- if lt == rt and lt ~= "table"then
- return l < r
- else
- return tostring( l ) < tostring( r )
- end
- end
- )
- for _, _v in ipairs( kv ) do
- local k = _v[ 1 ]
- local v = _v[ 2 ]
- if not pcall( _func, k, v ) then
- return false
- end
- end
- return true
- end
- end
- local function EncodeEscapeString( s )
- local buf = {}
- buf[#buf + 1] = "\""
- string.gsub( s, ".",
- function ( c )
- if c == '\n' then
- buf[#buf + 1] = "\\n"
- elseif c == '\t' then
- buf[#buf + 1] = "\\t"
- elseif c == '\r' then
- buf[#buf + 1] = "\\r"
- elseif c == '\a' then
- buf[#buf + 1] = "\\a"
- elseif c == '\b' then
- buf[#buf + 1] = "\\b"
- elseif c == '\\' then
- buf[#buf + 1] = "\\\\"
- elseif c == '\"' then
- buf[#buf + 1] = "\\\""
- elseif c == '\'' then
- buf[#buf + 1] = "\\\'"
- elseif c == '\v' then
- buf[#buf + 1] = "\\\v"
- elseif c == '\f' then
- buf[#buf + 1] = "\\\f"
- else
- buf[#buf + 1] = c
- end
- end
- )
- buf[#buf + 1] = "\""
- return table.concat( buf, "" )
- end
- local function StringBuilder()
- local sb = {}
- local f = function( str )
- if str then
- sb[ #sb + 1 ] = str
- end
- return f, sb
- end
- return f
- end
- local function CreateFileWriter( fileName, mode )
- local file = nil
- local indent = 0
- if mode and fileName then
- local _file, err = io.open( fileName )
- if _file ~= nil then
- --print( "remove file "..fileName )
- os.remove( fileName )
- end
- file = io.open( fileName, mode )
- end
- local ret = nil
- if file then
- ret = {
- write = function( ... )
- if indent > 0 then
- for i = 0, indent - 1 do
- file:write( "\t" )
- end
- end
- return file:write( ... )
- end,
- close = function( ... )
- return file:close()
- end
- }
- else
- ret = {
- write = function( ... )
- for i = 0, indent - 1 do
- io.write( "\t" )
- end
- return io.write( ... )
- end,
- close = function( ... )
- end
- }
- end
- ret.indent = function( count )
- count = count or 1
- indent = indent + count or 1
- end
- ret.outdent = function( count )
- count = count or 1
- if indent >= count then
- indent = indent - count
- end
- end
- return ret
- end
- local function SetNumberSerializedFormat( f )
- NumberSerializedFormat = f or DefaultNumberSerializedFormat
- if NumberSerializedFormat == "" then
- NumberSerializedFormat = DefaultNumberSerializedFormat
- end
- print( "set NumberSerializedFormat: ".. NumberSerializedFormat )
- end
- local DefaultVisitor = {
- recursive = true,
- iVisit = function( i, v, curPath )
- print( string.format( "%s[%d] = %s", curPath, i, tostring( v ) ) )
- return true
- end,
- nVisit = function( n, v, curPath )
- print( string.format( "%s[%g] = %s", curPath, n, tostring( v ) ) )
- return true
- end,
- sVisit = function( s, v, curPath )
- local _v = tostring( v )
- print( #curPath > 0 and curPath.."."..s.." = ".._v or s.." = ".._v )
- return true
- end,
- xVisit = function( k, v, curPath )
- local sk = tostring( k )
- local sv = tostring( v )
- print( #curPath > 0 and curPath.."."..sk.." = "..sv or sk.." = "..sv )
- return true
- end
- }
- local function WalkDataset( t, visitor, parent )
- if not parent then
- parent = ""
- end
- -- all integer key
- local continue = true
- if visitor.iVisit then
- for i, v in ipairs( t ) do
- local _t = type( v )
- if _t == "table" and visitor.recursive then
- continue = WalkDataset( v, visitor, string.format( "%s[%g]", parent, i ) )
- elseif _t == "string" or _t == "number" then
- continue = visitor.iVisit( i, v, parent )
- else
- -- not support value type
- if visitor.xVisit then
- continue = visitor.xVisit( i, v, parent )
- end
- end
- if not continue then
- return continue
- end
- end
- end
- local len = #t
- local keys = {}
- local idict = {}
- for k, v in pairs( t ) do
- local _t = type( k )
- if _t == "number" then
- local intKey = k == math.floor( k );
- if k > len or k <= 0 or not intKey then
- idict[k] = v
- end
- elseif _t == "string" then
- keys[#keys + 1] = k
- else
- --table, function, ...
- --not support data type for key
- if visitor.xVisit then
- continue = visitor.xVisit( k, v, parent )
- end
- end
- if not continue then
- return continue
- end
- end
- -- for all number keys those are not in array part
- -- key must be number
- for k, v in pairs( idict ) do
- local intKey = k == math.floor( k );
- local _t = type( v )
- if _t ~= "table" then
- if _t == "number" or _t == "string" then
- if intKey then
- if visitor.iVisit then
- continue = visitor.iVisit( k, v, parent )
- end
- else
- if visitor.nVisit then
- continue = visitor.nVisit( k, v, parent )
- end
- end
- else
- -- not support value data type
- if visitor.xVisit then
- continue = visitor.xVisit( k, v, parent )
- end
- end
- elseif visitor.recursive then
- if intKey then
- continue = WalkDataset( v, visitor, string.format( "%s[%d]", parent, k ) )
- else
- continue = WalkDataset( v, visitor, string.format( "%s[%g]", parent, k ) )
- end
- end
- if not continue then
- return continue
- end
- end
- -- sort all string keys
- table.sort( keys )
- -- for all none-table value
- local tableValue
- for k, v in pairs( keys ) do
- local value = t[v]
- local _t = type( value )
- if _t == "number" or _t == "string" then
- -- print all number or string value here
- if visitor.sVisit then
- continue = visitor.sVisit( v, value, parent )
- end
- elseif _t == "table" then
- -- for table value
- if not tableValue then
- tableValue = {}
- end
- tableValue[ k ] = v
- else
- if visitor.xVisit then
- continue = visitor.xVisit( v, value, parent )
- end
- end
- if not continue then
- return continue
- end
- end
- if visitor.recursive then
- -- for all table value
- if tableValue then
- for k, v in pairs( tableValue ) do
- local value = t[v]
- continue = WalkDataset( value, visitor, #parent > 0 and parent.."."..v or v )
- if not continue then
- return continue
- end
- end
- end
- end
- return continue
- end
- local function PrintDataset( t, parent )
- if not parent then
- parent = ""
- end
- local string_format = string.format
- -- all integer key
- for i, v in ipairs( t ) do
- local _t = type( v )
- if _t == "table" then
- PrintDataset( v, string_format( "%s[%g]", parent, i ) )
- elseif _t == "string" or _t == "number" then
- print( string.format( "%s[%d] = %s", parent, i, tostring( v ) ) )
- else
- -- not support value type
- end
- end
- local len = #t
- local keys = {}
- local idict = {}
- for k, v in pairs( t ) do
- local _t = type( k )
- if _t == "number" then
- if k > len or k <= 0 then
- idict[k] = v
- end
- elseif _t == "string" then
- keys[#keys + 1] = k
- else
- --table, function, ...
- --not support data type for key
- end
- end
- -- for all number keys those are not in array part
- -- key must be number
- for k, v in pairs( idict ) do
- local intKey = k == math.floor( k )
- local _t = type( v )
- if _t ~= "table" then
- if _t == "number" or _t == "string" then
- if intKey then
- print( string_format( "%s[%d] = %s", parent, k, tostring( v ) ) )
- else
- print( string_format( "%s[%g] = %s", parent, k, tostring( v ) ) )
- end
- else
- -- not support value data type
- end
- else
- if intKey then
- PrintDataset( v, string_format( "%s[%d]", parent, k ) )
- else
- PrintDataset( v, string_format( "%s[%g]", parent, k ) )
- end
- end
- end
- -- sort all string keys
- table.sort( keys )
- -- for all none-table value
- local tableValue
- for k, v in pairs( keys ) do
- local value = t[v]
- local _t = type( value )
- if _t ~= "table" then
- -- print all number or string value here
- local _value = tostring( value )
- print( #parent > 0 and parent.."."..v.." = ".._value or v.." = ".._value )
- else
- -- for table value
- if not tableValue then
- tableValue = {}
- end
- tableValue[ k ] = v
- end
- end
- -- for all table value
- if tableValue then
- for k, v in pairs( tableValue ) do
- local value = t[v]
- PrintDataset( value, #parent > 0 and parent.."."..v or v )
- end
- end
- end
- local function DeserializeTable( val )
- local loader = loadstring or load -- lua5.2 compat
- local chunk = loader( "return " .. val )
- local ok, ret = pcall( chunk )
- if not ok then
- ret = nil
- print( "DeserializeTable failed!"..val )
- end
- return ret
- end
- local function _SerializeTable( val, name, skipnewlines, campact, depth, tableRef )
- local valt = type( val )
- depth = depth or 0
- campact = campact or false
- local append = StringBuilder()
- local eqSign = " = "
- local tmp = ""
- local string_format = string.format
- if not campact then
- append( string.rep( "\t", depth ) )
- skipnewlines = skipnewlines or false
- else
- skipnewlines = true
- eqSign = "="
- end
- if name then
- local nt = type( name )
- if nt == "string" then
- if name ~= "" then
- if string.match( name,'^%d+' ) then
- append( "[\"" )
- append( name )
- append( "\"]" )
- else
- append( name )
- end
- else
- append( "[\"\"]" )
- end
- append( eqSign )
- elseif nt == "number" then
- append( string_format( "[%s]", tostring( name ) ) )
- append( eqSign )
- else
- tmp = tmp .. "\"[inserializeable datatype for key:" .. nt .. "]\""
- end
- end
- local ending = not skipnewlines and "\n" or ""
- if tableRef then
- local refName = tableRef[ val ]
- if refName then
- valt = "ref"
- val = refName
- end
- end
- if valt == "table" then
- append( "{" ) append( ending )
- local array_part = {}
- local count = 0
- for k, v in ipairs( val ) do
- if type( val ) ~= "function" then
- array_part[k] = true
- if count > 0 then
- append( "," )
- append( ending )
- end
- append( _SerializeTable( v, nil, skipnewlines, campact, depth + 1, tableRef ) )
- count = count + 1
- end
- end
- local sortedK = {}
- for k, v in pairs( val ) do
- if type( v ) ~= "function" then
- if not array_part[k] then
- sortedK[#sortedK + 1] = k
- end
- end
- end
- table.sort( sortedK )
- for i, k in ipairs( sortedK ) do
- local v = val[k]
- if count > 0 then
- append( "," )
- append( ending )
- end
- append( _SerializeTable( v, k, skipnewlines, campact, depth + 1, tableRef ) )
- count = count + 1
- end
- if count >= 1 then
- append( ending )
- end
- if not campact then
- append( string.rep( "\t", depth ) )
- end
- append( "}" )
- elseif valt == "number" then
- if DefaultNumberSerializedFormat == NumberSerializedFormat or math.floor( val ) == val then
- append( tostring( val ) )
- else
- append( string_format( NumberSerializedFormat, val ) )
- end
- elseif valt == "string" then
- append( EncodeEscapeString( val ) )
- elseif valt == "boolean" then
- append( val and "true" or "false" )
- elseif valt == "ref" then
- append( val or "nil" )
- else
- tmp = tmp .. "\"[inserializeable datatype:" .. valt .. "]\""
- end
- local _, slist = append()
- return table.concat( slist, "" )
- end
- local function SerializeTable( val, skipnewlines, campact, tableRef, name )
- getmetatable( "" ).__lt = function( a, b ) return tostring( a ):lower() < tostring( b ):lower() end
- local ret = _SerializeTable( val, name, skipnewlines, campact, 0, tableRef )
- getmetatable( "" ).__lt = nil
- return ret
- end
- local function DumpStringBank( stringBank )
- print( 'dump database local string bank begin...' )
- for k, v in pairs( stringBank ) do
- print( string.format( "\t[%g] = %s", k, v ) )
- end
- print( 'dump database local string bank end.' )
- end
- local function SaveStringBankToLua( stringBank, tofile )
- if tofile then
- local fileName = StringBankOutput
- local _file, err = io.open( fileName )
- if _file ~= nil then
- _file:close()
- os.remove( fileName )
- end
- file = io.open( fileName, "w" )
- local fmt = string.format
- file:write( fmt( "local %s = {\n", DatabaseLocaleTextName ) )
- for k, v in pairs( stringBank ) do
- file:write( fmt( "\t[%g] = %s,\n", k, EncodeEscapeString( v ) ) )
- end
- file:write( "}\n" )
- file:write( fmt( "return %s\n--EOF", DatabaseLocaleTextName ) )
- file:close()
- else
- DumpStringBank( stringBank )
- end
- end
- local function SaveStringBankToCSV( stringBank, tofile )
- local _exists = {}
- for k, v in pairs( stringBank ) do
- assert( not _exists[v] )
- _exists[ v ] = k
- end
- local csv = CSV
- if tofile and csv then
- local fileName = StringBankCSVOutput
- local _file, err = io.open( fileName )
- if _file ~= nil then
- _file:close()
- os.remove( fileName )
- end
- local t = {}
- local count = 1
- for k, v in pairs( stringBank ) do
- t[count] = { k, v }
- count = count + 1
- end
- table.sort( t,
- function( a, b )
- return a[1] < b[1]
- end
- )
- csv.save( fileName, t, true )
- else
- SaveStringBankToLua( stringBank, tofile )
- end
- end
- local function LoadStringBankFromLua( info )
- local stringBank = {}
- local chunk = loadfile( StringBankOutput )
- if chunk then
- print( 'load string bank: '..StringBankOutput )
- local last = chunk()
- if last and type( last ) == "table" then
- for k, v in pairs( last ) do
- stringBank[ k ] = v
- end
- end
- end
- return stringBank
- end
- local function LoadStringBankFromCSV()
- local stringBank = {}
- local csv = CSV
- if csv then
- local fileName = StringBankCSVOutput
- local file, err = io.open( fileName )
- if file then
- print( "Load StringBank: " .. fileName )
- file:close()
- local b = csv.load( fileName, true )
- local allstr = {}
- for i = 1, #b do
- local key = b[ i ][ 1 ]
- local value = b[ i ][ 2 ]
- local oldHash = allstr[ value ]
- if not oldHash then
- stringBank[ key ] = value
- allstr[ value ] = key
- else
- print( string.format( "\"%s\" already exists in StringBank with hash %d", value, oldHash ) )
- end
- end
- end
- else
- return LoadStringBankFromLua()
- end
- return stringBank
- end
- local function TrimStringBank( stringBank )
- -- remove useless values
- local meta = getmetatable( stringBank )
- if meta then
- local counter = meta.__counter
- if counter then
- local used = counter.used
- if used then
- local count = 0
- for hash, str in pairs( stringBank ) do
- count = count + 1
- end
- local unused = {}
- for hash, str in pairs( stringBank ) do
- if not used[ hash ] then
- unused[#unused + 1] = hash
- end
- end
- if #unused > MaxStringBankRedundancy then
- for _, h in ipairs( unused ) do
- stringBank[ h ] = nil
- end
- end
- end
- end
- end
- end
- local function GetAllFileNamesAtPath( path )
- path, _ = path:gsub( "/", "\\" )
- local ret = {}
- for dir in io.popen( string.format( "dir \"%s\" /S/b", path ) ):lines() do
- local s, e, f = dir:find( ".+\\(.+)%.lua$" )
- if f then
- table.insert( ret, f )
- end
- end
- table.sort( ret )
- return ret
- end
- local function LoadDataset( name )
- if not Database then
- _G["Database"] = {}
- Database.loaded = {}
- end
- local loader = function( name )
- Database.loaded = Database.loaded or {}
- local r = Database.loaded[name]
- if r then
- return r
- end
- local pname = string.gsub( name, "%.", "/" )
- local split = function( s, p )
- local rt= {}
- string.gsub( s, '[^'..p..']+', function( w ) table.insert( rt, w ) end )
- return rt
- end
- local curName = pname..".lua"
- local fileName = DatabaseRoot.."/"..curName
- local checkFileName = function( path, name )
- path, _ = path:gsub( "/", "\\" )
- local _name = string.lower( name )
- for dir in io.popen( string.format( "dir \"%s\" /s/b", path ) ):lines() do
- local s, e, f = dir:find( ".+\\(.+%.lua)$" )
- if f then
- local _f = string.lower( f )
- if _name == _f then
- return name == f, f -- not match, real name
- end
- end
- end
- end
- local m, real = checkFileName( DatabaseRoot, curName )
- if not m and real then
- local msg = string.format( "filename must be matched by case! realname: \"%s\", you pass: \"%s\"", real, curName )
- print( msg )
- os.execute( "pause" )
- end
- local chunk = loadfile( fileName )
- if not chunk then
- fileName = LuaRoot.."/"..pname..".lua"
- chunk, err = loadfile( fileName )
- if err then
- print( "\n\n" )
- print( "----------------------------------" )
- print( "Load lua failed: "..fileName )
- print( "Error:" )
- print( "\t"..err )
- print( "----------------------------------" )
- print( "\n\n" )
- end
- end
- print( fileName )
- assert( chunk )
- if not chunk then
- os.execute( "pause" )
- end
- local rval = chunk()
- if rval.__name ~= nil then
- os.execute( "pause table's key must not be '__name' which is the reserved keyword." )
- end
- if rval.__sourcefile ~= nil then
- os.execute( "pause table's key must not be '__sourcefile' which is the reserved keyword." )
- end
- rval.__name = name
- rval.__sourcefile = fileName
- local namespace = Database
- local ns = split( pname, '/' )
- local xname = ns[#ns] -- last one
- if #ns > 1 then
- for i = 1, #ns - 1 do
- local n = ns[i];
- if namespace[n] == nil then
- namespace[n] = {}
- end
- namespace = namespace[n]
- end
- end
- namespace[xname] = rval
- print( "dataset: "..name.." has been loaded" )
- Database.loaded[name] = rval
- return rval
- end
- return loader( name )
- end
- local function CheckNotAscii( v )
- if v ~= nil and type( v ) == "string" then
- local byte = string.byte
- for _c in string.gmatch( v, "." ) do
- local c = byte( _c )
- if c < 0 or c > 127 then
- return true
- end
- end
- end
- return false
- end
- local function LocalizeRecord( id, record, genCode, StringBank )
- local localized_fields = nil
- local subTable = nil
- OrderedForeach(
- record,
- function( k, v )
- local vt = type( v )
- if vt == "string" then
- if CheckNotAscii( v ) then
- if #v > 0 and string.sub( v, 1, 1 ) == LocaleTextLeadingTag then
- print( string.format( "invalid leading character for localized text! key, value: %s, %s", k, v ) )
- os.execute( "pause" )
- end
- if not localized_fields then
- localized_fields = {}
- end
- -- build localized id string with tag
- local sid = AddStringToBank( StringBank, v )
- localized_fields[ k ] = string.format( "%s%g", LocaleTextLeadingTag, sid )
- if genCode then
- genCode[ #genCode + 1 ] = {
- id,
- sid,
- v
- }
- end
- end
- elseif vt == "table" then
- if not subTable then
- subTable = {}
- end
- subTable[ #subTable + 1 ] = v
- end
- end
- )
- local localized = false
- if localized_fields then
- -- override localized string with tag
- localized = true
- for k, v in pairs( localized_fields ) do
- record[ k ] = localized_fields[ k ]
- end
- end
- if subTable then
- for _, sub in ipairs( subTable ) do
- localized = LocalizeRecord( 0, sub, genCode, StringBank ) or localized
- end
- end
- return localized
- end
- local function GetValueTypeNameCS( value )
- local t = type( value )
- if t == "string" then
- return "string"
- elseif t == "number" then
- if value == math.floor( value ) then
- return "int"
- else
- return "float"
- end
- elseif t == "boolean" then
- return "bool"
- elseif t == "table" then
- return "table"
- else
- return "void"
- end
- end
- local function UniquifyTable( t )
- if t == nil or type( t ) ~= "table" then
- return nil
- end
- local hash = SerializeTable( t, true, true )
- local ref = UniquifyTables[ hash ]
- if ref then
- local refcount = UniquifyTablesRefCounter[ ref ] or 1
- UniquifyTablesRefCounter[ ref ] = refcount + 1
- return ref
- end
- local overwrites = nil
- for k, v in pairs( t ) do
- overwrites = overwrites or {}
- if type( v ) == "table" then
- overwrites[ k ] = UniquifyTable( v )
- end
- end
- if overwrites then
- for k, v in pairs( overwrites ) do
- t[ k ] = overwrites[ k ]
- end
- end
- local id = #UniquifyTablesIds + 1
- UniquifyTablesIds[ id ] = hash
- UniquifyTables[ hash ] = t
- UniquifyTablesInvIds[ t ] = id
- UniquifyTablesRefCounter[ t ] = 1
- return t
- end
- local function OptimizeDataset( dataset )
- local ids = {}
- local names = {}
- local idType = nil
- -- choose cs data type
- -- for all fields in a record
- local typeNameTable = {}
- for k, v in pairs( dataset ) do
- local _sk = tostring( k )
- if _sk ~= "__name" and _sk ~= "__sourcefile" then
- if not idType then
- idType = type( k )
- end
- if idType == type( k ) then
- ids[ #ids + 1 ] = k
- end
- end
- end
- if EnableDefaultValueOptimize then
- -- find bigest table to generate all fields
- local majorItem = 1
- local f = 0
- for k, v in pairs( dataset ) do
- if type( v ) == "table" then
- local num = 0
- for _, _ in pairs( v ) do
- num = num + 1
- end
- if num > f then
- f = num
- majorItem = k
- end
- end
- end
- local v = dataset[ majorItem ]
- if type( v ) == "table" then
- for name, value in pairs( v ) do
- local nt = type( name )
- if nt == "string" and name == "id" then
- print( "this table already has a field named 'id'" )
- end
- if nt == "string" then
- names[ #names + 1 ] = name
- end
- end
- table.sort( names, function( a, b ) return a:lower() < b:lower() end )
- for i, field in ipairs( names ) do
- -- for all record / row
- for r, t in ipairs( ids ) do
- local record = dataset[ t ]
- if record[ field ] ~= nil then
- local v = record[ field ]
- local curType = GetValueTypeNameCS( v )
- if not typeNameTable[ field ] then
- typeNameTable[ field ] = curType
- elseif typeNameTable[ field ] == "int" and curType == "float" then
- -- overwrite int to float
- typeNameTable[ field ] = curType
- elseif curType == "table" then
- -- overwrite to table
- typeNameTable[ field ] = curType
- end
- end
- end
- -- patching miss fields with default values
- local curType = typeNameTable[ field ]
- for r, t in ipairs( ids ) do
- local record = dataset[ t ]
- local v = record[ field ]
- if v == nil then
- local ft = typeNameTable[ field ]
- if ft == "string" then
- v = ""
- elseif ft == "number" or ft == "int" or ft == "float" then
- v = 0
- elseif ft == "table" then
- v = {}
- elseif ft == "bool" then
- v = false
- end
- record[ field ] = v
- end
- end
- end
- end
- end
- ids = {}
- idType = nil
- UniquifyTables = {}
- UniquifyTablesIds = {}
- UniquifyTablesInvIds = {}
- UniquifyTablesRefCounter = {}
- local isIntegerKey = true
- local overwrites = nil
- OrderedForeach(
- dataset,
- function( k, v )
- local _sk = tostring( k )
- if _sk ~= "__name" and _sk ~= "__sourcefile" then
- if not idType then
- idType = type( k )
- end
- -- check type
- if idType == type( k ) then
- ids[ #ids + 1 ] = k
- if idType == "number" then
- if isIntegerKey then
- isIntegerKey = k == floor( k )
- end
- end
- end
- if type( v ) == "table" then
- overwrites = overwrites or {}
- overwrites[ k ] = UniquifyTable( v )
- end
- end
- end
- )
- if overwrites then
- for k, v in pairs( overwrites ) do
- dataset[ k ] = overwrites[ k ]
- end
- end
- local returnVal = nil
- if EnableDefaultValueOptimize then
- local defaultValues = nil
- for i, field in ipairs( names ) do
- local curType = typeNameTable[ field ]
- -- for all record/row
- local defaultValueStat = {
- }
- for r, t in ipairs( ids ) do
- local record = dataset[ t ]
- local v = record[ field ]
- if v ~= nil then
- local vcount = defaultValueStat[ v ] or 0
- defaultValueStat[ v ] = vcount + 1
- else
- assert( "default value missing!" )
- end
- end
- -- find the mostest used as a default value
- local max = -1
- local defaultValue = nil
- local _defaultValue = "{}"
- local result = OrderedForeachByValue(
- defaultValueStat,
- function( value, count )
- if count >= max then
- if count > max then
- max = count
- defaultValue = value
- _defaultValue = SerializeTable( defaultValue, true, true )
- else
- if curType == "table" then
- local _value = SerializeTable( value, true, true )
- if #_value > #_defaultValue then
- defaultValue = value
- _defaultValue = SerializeTable( defaultValue, true, true )
- end
- else
- local _value = value
- local _defaultValue = defaultValue
- if type( value ) == 'boolean' then
- _value = value and 1 or 0
- _defaultValue = defaultValue and 1 or 0
- end
- if _value < _defaultValue then
- defaultValue = value
- _defaultValue = SerializeTable( defaultValue, true, true )
- end
- end
- end
- end
- end
- )
- if not result then
- error( string.format( "create default value for \"%s\" failed. please make sure all the value's types are the same.", field ) )
- end
- if defaultValue ~= nil then
- defaultValues = defaultValues or {}
- defaultValues[ field ] = defaultValue
- end
- end
- returnVal = defaultValues
- end
- -- remove tables whose's ref is 1 and re-mapping id
- local newid = 1
- local newIds = {}
- local newInvIds = {}
- OrderedForeach(
- UniquifyTablesIds,
- function( id, hash )
- local table = UniquifyTables[ hash ]
- local refcount = UniquifyTablesRefCounter[ table ]
- if refcount == 1 then
- UniquifyTables[ hash ] = nil
- else
- newIds[ newid ] = hash
- newInvIds[ table ] = newid
- newid = newid + 1
- end
- end
- )
- UniquifyTablesIds = newIds
- UniquifyTablesInvIds = newInvIds
- return returnVal
- end
- local function ToUniqueTableRefName( id )
- if id <= MaxLocalVariableNum then
- return string.format( RefTableName.."_%d", id )
- else
- return string.format( RefTableName.."[%d]", id - MaxLocalVariableNum )
- end
- end
- local function SaveDatasetToFile( dataset, tofile, tableRef, name )
- if tofile then
- outFile = CreateFileWriter( dataset.__sourcefile, "w" )
- else
- outFile = CreateFileWriter()
- end
- local ptr2ref = nil
- if tableRef and tableRef.ptr2ref then
- ptr2ref = tableRef.ptr2ref
- end
- if tableRef and tableRef.name2value then
- local name2table = tableRef.name2value
- local tables = tableRef.tables
- local tableIds = tableRef.tableIds
- local ptr2ref = tableRef.ptr2ref
- local refcounter = tableRef.refcounter
- local maxLocalVariableNum = tableRef.maxLocalVariableNum or MaxLocalVariableNum
- local refTableName = tableRef.refTableName or "__rt"
- local tableNum = #tableIds
- for id, hash in ipairs( tableIds ) do
- local table = tables[ hash ]
- if table and id <= maxLocalVariableNum then
- local refname = ptr2ref[ table ]
- -- temp comment out top level ref
- ptr2ref[ table ] = nil
- local refcount = refcounter[ table ]
- outFile.write(
- string.format(
- "%slocal %s = %s\n",
- PrintTableRefCount and string.format( "--ref:%d\n", refcount ) or "",
- refname,
- SerializeTable( table, false, false, ptr2ref )
- )
- )
- ptr2ref[ table ] = refname
- else
- break
- end
- end
- if tableNum > maxLocalVariableNum then
- local maxCount = tableNum - maxLocalVariableNum
- outFile.write( string.format( "local %s = createtable and createtable( %d, 0 ) or {}\n", refTableName, maxCount ) )
- for id = maxLocalVariableNum + 1, tableNum do
- local offset = id - maxLocalVariableNum
- local hash = tableIds[ id ]
- local table = tables[ hash ]
- local refname = ptr2ref[ table ]
- -- temp comment out top level ref
- ptr2ref[ table ] = nil
- local refcount = refcounter[ table ]
- outFile.write(
- string.format(
- "%s%s[%d] = %s\n",
- PrintTableRefCount and string.format( "-- %s, ref:%d\n", refname, refcount ) or "",
- refTableName, offset,
- SerializeTable( table, false, false, ptr2ref )
- )
- )
- ptr2ref[ table ] = refname
- end
- end
- end
- local datasetName = dataset.__name or name
- if not datasetName then
- datasetName = UnknownName
- dataset.__name = datasetName
- end
- outFile.write( string.format( "local %s = \n", datasetName ) )
- -- remove none table value
- local removed = nil
- for k, v in pairs( dataset ) do
- if type( v ) ~= "table" then
- removed = removed or {}
- removed[ #removed + 1 ] = k
- end
- end
- if removed then
- for _, k in ipairs( removed ) do
- dataset[ k ] = nil
- end
- end
- outFile.write( SerializeTable( dataset, false, false, ptr2ref ) )
- outFile.write( "\n" )
- if tableRef and tableRef.postOutput then
- tableRef.postOutput( outFile )
- end
- outFile.write( string.format( "\nreturn %s\n", datasetName ) )
- outFile.close()
- end
- local function ExportOptimizedDataset( t, StringBank )
- local datasetName = t.__name
- if not datasetName then
- datasetName = UnknownName
- t.__name = datasetName
- end
- local localized = false
- local genCode = false
- if EnableLocalization then
- OrderedForeach(
- t,
- function( id, _record )
- if type( _record ) == "table" then
- localized = LocalizeRecord( id, _record, genCode, StringBank ) or localized
- end
- end
- )
- end
- local tableRef = nil
- local defaultValues = nil
- if EnableDatasetOptimize then
- defaultValues = OptimizeDataset( t )
- if defaultValues then
- local removeDefaultValues = function( record )
- local removes = nil
- local adds = nil
- for field, defaultVal in pairs( defaultValues ) do
- local value = record[ field ]
- local hasValue = true
- if value == nil then
- assert( false, "OptimizeDataset should patch all missing fields!" )
- hasValue = false
- end
- if value == defaultVal and hasValue then
- removes = removes or {}
- removes[ #removes + 1 ] = field
- else
- adds = adds or {}
- adds[ field ] = value
- end
- end
- -- remove fields with default value
- if removes then
- for _, f in ipairs( removes ) do
- record[ f ] = nil
- end
- end
- -- patch fields with none-default value
- if adds then
- for f, v in pairs( adds ) do
- record[ f ] = v
- end
- end
- end
- local removed = {}
- for _, record in pairs( t ) do
- if type( record ) == "table" then
- if not removed[ record ] then
- removeDefaultValues( record )
- removed[ record ] = true
- end
- end
- end
- end
- local reftables = nil
- local ptr2ref = nil
- -- create ref table: table -> refname
- for _, hash in pairs( UniquifyTablesIds ) do
- local t = UniquifyTables[ hash ]
- if t then
- local refName = ToUniqueTableRefName( UniquifyTablesInvIds[ t ] )
- reftables = reftables or {}
- ptr2ref = ptr2ref or {}
- reftables[ refName ] = t
- ptr2ref[ t ] = refName
- end
- end
- tableRef = {
- name2value = reftables,
- tables = UniquifyTables, -- hash -> table
- tableIds = UniquifyTablesIds, -- id -> hash
- ptr2ref = ptr2ref, -- table -> refname
- refcounter = UniquifyTablesRefCounter, -- table -> refcount
- maxLocalVariableNum = MaxLocalVariableNum,
- refTableName = RefTableName,
- postOutput = function( outFile )
- if defaultValues then
- outFile.write(
- string.format(
- "local %s = %s\n",
- DefaultValueTableName,
- SerializeTable( defaultValues, false, false, ptr2ref )
- )
- )
- outFile.write( "do\n" )
- outFile.write( string.format( "\tlocal base = { __index = %s, __newindex = function() error( \"Attempt to modify read-only table\" ) end }\n", DefaultValueTableName ) )
- outFile.write( string.format( "\tfor k, v in pairs( %s ) do\n", datasetName ) )
- outFile.write( "\t\tsetmetatable( v, base )\n" )
- outFile.write( "\tend\n" )
- outFile.write( "\tbase.__metatable = false\n" )
- outFile.write( "end\n" )
- end
- end
- }
- end
- return t, tableRef, localized
- end
- --tofile: not output to file, just for debug
- --newStringBank: if false, exporter will use existing string hash for increamental building
- local function ExportDatabaseLocalText( tofile, newStringBank )
- local StringBank = nil
- if newStringBank then
- StringBank = {}
- else
- StringBank = LoadStringBankFromCSV()
- end
- StringBank = StringBank or {}
- local localized_dirty = false
- local files = GetAllFileNamesAtPath( DatabaseRoot )
- for _, v in ipairs( files ) do
- if not ExcludedFiles[ v ] then
- print( "LoadDataset :"..v )
- LoadDataset( v )
- local t = Database[ v ]
- local localized = false
- if t then
- local _t, tableRef, localized = ExportOptimizedDataset( t, StringBank )
- assert( _t == t )
- localized_dirty = localized_dirty or localized
- SaveDatasetToFile( Database[ v ], tofile, tableRef )
- end
- end
- end
- TrimStringBank( StringBank )
- if localized_dirty then
- SaveStringBankToCSV( StringBank, tofile )
- else
- print( "\nDatabase LocaleText is up to date.\n" )
- end
- print( "Database Exporting LocaleText done." )
- end
- --[[
- local test = {
- {
- 1,
- 2,
- 3,
- a = "123",
- b = "123"
- },
- {
- 1,
- 2,
- 3,
- a = "123",
- b = "123"
- },
- {
- 1,
- 2,
- 5,
- a = "123",
- b = "123"
- },
- [9] = {
- 1,
- 2,
- 5,
- a = "123",
- b = "123"
- },
- [100] = {
- 1,
- 2,
- 3,
- a = "tttt",
- b = "123"
- },
- [11] = {
- 1,
- 2,
- 3,
- a = "123",
- b = "123",
- c = { {1}, {1}, {2}, {2} },
- d = { { a = 1, 1 }, { a = 2, 2 } },
- e = { { a = 1, 1 }, { a = 2, 2 } },
- }
- }
- EnableDefaultValueOptimize = false
- local _localizedText = {}
- local _src = SerializeTable( test )
- local _clone = DeserializeTable( _src )
- print( _src )
- local t, tableRef = ExportOptimizedDataset( test, _localizedText )
- assert( t == test )
- --print( SerializeTable( t ) )
- SaveDatasetToFile( t, false, tableRef, "test" )
- local _dst = SerializeTable( t )
- print( _dst )
- assert( _src == _dst )
- EnableDefaultValueOptimize = true
- _localizedText = {}
- t, tableRef = ExportOptimizedDataset( _clone, _localizedText )
- assert( t == _clone )
- --_clone.__name = "__cloned_test"
- SaveDatasetToFile( _clone, false, tableRef )
- local _dst = SerializeTable( _clone )
- print( _dst )
- print( _src ~= _dst )
- --]]
- ExportDatabaseLocalText( true )
|