DataTableOptimizer.lua 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509
  1. --[[
  2. How to use:
  3. 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 )
  4. If you want to exlucde some input files in DatabaseRoot, just add theirs names into ExcludedFiles
  5. --]]
  6. local Root = "."
  7. if arg and arg[0] then
  8. local s, e = string.find( arg[0], "Assets" )
  9. if e then
  10. Root = string.sub( arg[0], 1, e )
  11. end
  12. end
  13. Root = string.gsub( Root, '\\', '/' )
  14. local DatabaseRoot = Root.."/Lua/config"
  15. local LuaRoot = Root.."/Lua/config"
  16. package.path = package.path..';'..DatabaseRoot..'/?.lua'..';'.. LuaRoot..'/?.lua'
  17. local EnableDatasetOptimize = true
  18. local EnableDefaultValueOptimize = true
  19. local EnableLocalization = true -- set to false to disable localization process
  20. local Database = {}
  21. local CSV --= require "std.csv"
  22. local DefaultNumberSerializedFormat = "%.14g"
  23. local NumberSerializedFormat = DefaultNumberSerializedFormat
  24. local DatabaseLocaleTextName = "_LocaleText"
  25. local StringBankOutput = DatabaseRoot.."/"..DatabaseLocaleTextName..".lua"
  26. local StringBankCSVOutput = DatabaseRoot.."/"..DatabaseLocaleTextName..".csv"
  27. local MaxStringBankRedundancy = 100
  28. local MaxStringBankBinSize = 524288
  29. local LocaleTextLeadingTag = '@'
  30. local MaxLocalVariableNum = 160 -- lparser.c #define MAXVARS 200
  31. local RefTableName = "__rt"
  32. local DefaultValueTableName = "__default_values"
  33. local PrintTableRefCount = false
  34. local UnknownName = "___noname___"
  35. local floor = math.floor
  36. local fmod = math.fmod
  37. local ExcludedFiles = {
  38. --Add file name to exclude from build
  39. _LocaleText = true,
  40. }
  41. local UniquifyTables = {} -- hash -> table
  42. local UniquifyTablesIds = {} -- id -> hash
  43. local UniquifyTablesInvIds = {} -- table -> id
  44. local UniquifyTablesRefCounter = {} -- table -> refcount
  45. local function HashString( v )
  46. local val = 0
  47. local fmod = fmod
  48. local gmatch = string.gmatch
  49. local byte = string.byte
  50. local MaxStringBankBinSize = MaxStringBankBinSize
  51. local c
  52. for _c in gmatch( v, "." ) do
  53. c = byte( _c )
  54. val = val + c * 193951
  55. val = fmod( val, MaxStringBankBinSize )
  56. val = val * 399283
  57. val = fmod( val, MaxStringBankBinSize )
  58. end
  59. return val
  60. end
  61. local function AddStringToBank( stringBank, str )
  62. local meta = getmetatable( stringBank )
  63. local reversed = nil
  64. local counter = nil
  65. if not meta then
  66. meta = {
  67. __counter = { used = {} }, -- mark used hash value
  68. __reversed = {} -- string -> hash reverse lookup
  69. }
  70. reversed = meta.__reversed
  71. counter = meta.__counter
  72. setmetatable( stringBank, meta )
  73. local remove = {}
  74. -- lazy initialize reverse lut
  75. for h, s in pairs( stringBank ) do
  76. local _h = reversed[ s ]
  77. assert( _h == nil )
  78. reversed[ s ] = h
  79. end
  80. end
  81. reversed = reversed or meta.__reversed
  82. counter = counter or meta.__counter
  83. local hash = reversed[ str ]
  84. if hash then
  85. counter.used[ hash ] = true
  86. return hash
  87. end
  88. hash = HashString( str )
  89. local _v = stringBank[ hash ]
  90. while _v do
  91. hash = hash + 1
  92. hash = fmod( hash, MaxStringBankBinSize )
  93. _v = stringBank[ hash ]
  94. end
  95. assert( not reversed[ str ] )
  96. stringBank[ hash ] = str
  97. reversed[ str ] = hash
  98. counter.used[ hash ] = true
  99. return hash
  100. end
  101. local function OrderedForeach( _table, _func )
  102. if type( _table ) == "table" then
  103. local kv = {}
  104. for k, v in pairs( _table ) do
  105. kv[ #kv + 1 ] = { k, v }
  106. end
  107. table.sort( kv,
  108. function( _l, _r )
  109. local l = _l[ 1 ]
  110. local r = _r[ 1 ]
  111. local lt = type( l )
  112. local rt = type( r )
  113. if lt == rt and lt ~= "table" then
  114. return l < r
  115. else
  116. return tostring( l ) < tostring( r )
  117. end
  118. end
  119. )
  120. for _, _v in ipairs( kv ) do
  121. local k = _v[ 1 ]
  122. local v = _v[ 2 ]
  123. _func( k, v )
  124. end
  125. end
  126. end
  127. local function OrderedForeachByValue( _table, _func )
  128. if type( _table ) == "table" then
  129. local kv = {}
  130. for k, v in pairs( _table ) do
  131. kv[ #kv + 1 ] = { k, v }
  132. end
  133. table.sort( kv,
  134. function( _l, _r )
  135. local l = _l[ 2 ]
  136. local r = _r[ 2 ]
  137. local lt = type( l )
  138. local rt = type( r )
  139. if lt == rt and lt ~= "table"then
  140. return l < r
  141. else
  142. return tostring( l ) < tostring( r )
  143. end
  144. end
  145. )
  146. for _, _v in ipairs( kv ) do
  147. local k = _v[ 1 ]
  148. local v = _v[ 2 ]
  149. if not pcall( _func, k, v ) then
  150. return false
  151. end
  152. end
  153. return true
  154. end
  155. end
  156. local function EncodeEscapeString( s )
  157. local buf = {}
  158. buf[#buf + 1] = "\""
  159. string.gsub( s, ".",
  160. function ( c )
  161. if c == '\n' then
  162. buf[#buf + 1] = "\\n"
  163. elseif c == '\t' then
  164. buf[#buf + 1] = "\\t"
  165. elseif c == '\r' then
  166. buf[#buf + 1] = "\\r"
  167. elseif c == '\a' then
  168. buf[#buf + 1] = "\\a"
  169. elseif c == '\b' then
  170. buf[#buf + 1] = "\\b"
  171. elseif c == '\\' then
  172. buf[#buf + 1] = "\\\\"
  173. elseif c == '\"' then
  174. buf[#buf + 1] = "\\\""
  175. elseif c == '\'' then
  176. buf[#buf + 1] = "\\\'"
  177. elseif c == '\v' then
  178. buf[#buf + 1] = "\\\v"
  179. elseif c == '\f' then
  180. buf[#buf + 1] = "\\\f"
  181. else
  182. buf[#buf + 1] = c
  183. end
  184. end
  185. )
  186. buf[#buf + 1] = "\""
  187. return table.concat( buf, "" )
  188. end
  189. local function StringBuilder()
  190. local sb = {}
  191. local f = function( str )
  192. if str then
  193. sb[ #sb + 1 ] = str
  194. end
  195. return f, sb
  196. end
  197. return f
  198. end
  199. local function CreateFileWriter( fileName, mode )
  200. local file = nil
  201. local indent = 0
  202. if mode and fileName then
  203. local _file, err = io.open( fileName )
  204. if _file ~= nil then
  205. --print( "remove file "..fileName )
  206. os.remove( fileName )
  207. end
  208. file = io.open( fileName, mode )
  209. end
  210. local ret = nil
  211. if file then
  212. ret = {
  213. write = function( ... )
  214. if indent > 0 then
  215. for i = 0, indent - 1 do
  216. file:write( "\t" )
  217. end
  218. end
  219. return file:write( ... )
  220. end,
  221. close = function( ... )
  222. return file:close()
  223. end
  224. }
  225. else
  226. ret = {
  227. write = function( ... )
  228. for i = 0, indent - 1 do
  229. io.write( "\t" )
  230. end
  231. return io.write( ... )
  232. end,
  233. close = function( ... )
  234. end
  235. }
  236. end
  237. ret.indent = function( count )
  238. count = count or 1
  239. indent = indent + count or 1
  240. end
  241. ret.outdent = function( count )
  242. count = count or 1
  243. if indent >= count then
  244. indent = indent - count
  245. end
  246. end
  247. return ret
  248. end
  249. local function SetNumberSerializedFormat( f )
  250. NumberSerializedFormat = f or DefaultNumberSerializedFormat
  251. if NumberSerializedFormat == "" then
  252. NumberSerializedFormat = DefaultNumberSerializedFormat
  253. end
  254. print( "set NumberSerializedFormat: ".. NumberSerializedFormat )
  255. end
  256. local DefaultVisitor = {
  257. recursive = true,
  258. iVisit = function( i, v, curPath )
  259. print( string.format( "%s[%d] = %s", curPath, i, tostring( v ) ) )
  260. return true
  261. end,
  262. nVisit = function( n, v, curPath )
  263. print( string.format( "%s[%g] = %s", curPath, n, tostring( v ) ) )
  264. return true
  265. end,
  266. sVisit = function( s, v, curPath )
  267. local _v = tostring( v )
  268. print( #curPath > 0 and curPath.."."..s.." = ".._v or s.." = ".._v )
  269. return true
  270. end,
  271. xVisit = function( k, v, curPath )
  272. local sk = tostring( k )
  273. local sv = tostring( v )
  274. print( #curPath > 0 and curPath.."."..sk.." = "..sv or sk.." = "..sv )
  275. return true
  276. end
  277. }
  278. local function WalkDataset( t, visitor, parent )
  279. if not parent then
  280. parent = ""
  281. end
  282. -- all integer key
  283. local continue = true
  284. if visitor.iVisit then
  285. for i, v in ipairs( t ) do
  286. local _t = type( v )
  287. if _t == "table" and visitor.recursive then
  288. continue = WalkDataset( v, visitor, string.format( "%s[%g]", parent, i ) )
  289. elseif _t == "string" or _t == "number" then
  290. continue = visitor.iVisit( i, v, parent )
  291. else
  292. -- not support value type
  293. if visitor.xVisit then
  294. continue = visitor.xVisit( i, v, parent )
  295. end
  296. end
  297. if not continue then
  298. return continue
  299. end
  300. end
  301. end
  302. local len = #t
  303. local keys = {}
  304. local idict = {}
  305. for k, v in pairs( t ) do
  306. local _t = type( k )
  307. if _t == "number" then
  308. local intKey = k == math.floor( k );
  309. if k > len or k <= 0 or not intKey then
  310. idict[k] = v
  311. end
  312. elseif _t == "string" then
  313. keys[#keys + 1] = k
  314. else
  315. --table, function, ...
  316. --not support data type for key
  317. if visitor.xVisit then
  318. continue = visitor.xVisit( k, v, parent )
  319. end
  320. end
  321. if not continue then
  322. return continue
  323. end
  324. end
  325. -- for all number keys those are not in array part
  326. -- key must be number
  327. for k, v in pairs( idict ) do
  328. local intKey = k == math.floor( k );
  329. local _t = type( v )
  330. if _t ~= "table" then
  331. if _t == "number" or _t == "string" then
  332. if intKey then
  333. if visitor.iVisit then
  334. continue = visitor.iVisit( k, v, parent )
  335. end
  336. else
  337. if visitor.nVisit then
  338. continue = visitor.nVisit( k, v, parent )
  339. end
  340. end
  341. else
  342. -- not support value data type
  343. if visitor.xVisit then
  344. continue = visitor.xVisit( k, v, parent )
  345. end
  346. end
  347. elseif visitor.recursive then
  348. if intKey then
  349. continue = WalkDataset( v, visitor, string.format( "%s[%d]", parent, k ) )
  350. else
  351. continue = WalkDataset( v, visitor, string.format( "%s[%g]", parent, k ) )
  352. end
  353. end
  354. if not continue then
  355. return continue
  356. end
  357. end
  358. -- sort all string keys
  359. table.sort( keys )
  360. -- for all none-table value
  361. local tableValue
  362. for k, v in pairs( keys ) do
  363. local value = t[v]
  364. local _t = type( value )
  365. if _t == "number" or _t == "string" then
  366. -- print all number or string value here
  367. if visitor.sVisit then
  368. continue = visitor.sVisit( v, value, parent )
  369. end
  370. elseif _t == "table" then
  371. -- for table value
  372. if not tableValue then
  373. tableValue = {}
  374. end
  375. tableValue[ k ] = v
  376. else
  377. if visitor.xVisit then
  378. continue = visitor.xVisit( v, value, parent )
  379. end
  380. end
  381. if not continue then
  382. return continue
  383. end
  384. end
  385. if visitor.recursive then
  386. -- for all table value
  387. if tableValue then
  388. for k, v in pairs( tableValue ) do
  389. local value = t[v]
  390. continue = WalkDataset( value, visitor, #parent > 0 and parent.."."..v or v )
  391. if not continue then
  392. return continue
  393. end
  394. end
  395. end
  396. end
  397. return continue
  398. end
  399. local function PrintDataset( t, parent )
  400. if not parent then
  401. parent = ""
  402. end
  403. local string_format = string.format
  404. -- all integer key
  405. for i, v in ipairs( t ) do
  406. local _t = type( v )
  407. if _t == "table" then
  408. PrintDataset( v, string_format( "%s[%g]", parent, i ) )
  409. elseif _t == "string" or _t == "number" then
  410. print( string.format( "%s[%d] = %s", parent, i, tostring( v ) ) )
  411. else
  412. -- not support value type
  413. end
  414. end
  415. local len = #t
  416. local keys = {}
  417. local idict = {}
  418. for k, v in pairs( t ) do
  419. local _t = type( k )
  420. if _t == "number" then
  421. if k > len or k <= 0 then
  422. idict[k] = v
  423. end
  424. elseif _t == "string" then
  425. keys[#keys + 1] = k
  426. else
  427. --table, function, ...
  428. --not support data type for key
  429. end
  430. end
  431. -- for all number keys those are not in array part
  432. -- key must be number
  433. for k, v in pairs( idict ) do
  434. local intKey = k == math.floor( k )
  435. local _t = type( v )
  436. if _t ~= "table" then
  437. if _t == "number" or _t == "string" then
  438. if intKey then
  439. print( string_format( "%s[%d] = %s", parent, k, tostring( v ) ) )
  440. else
  441. print( string_format( "%s[%g] = %s", parent, k, tostring( v ) ) )
  442. end
  443. else
  444. -- not support value data type
  445. end
  446. else
  447. if intKey then
  448. PrintDataset( v, string_format( "%s[%d]", parent, k ) )
  449. else
  450. PrintDataset( v, string_format( "%s[%g]", parent, k ) )
  451. end
  452. end
  453. end
  454. -- sort all string keys
  455. table.sort( keys )
  456. -- for all none-table value
  457. local tableValue
  458. for k, v in pairs( keys ) do
  459. local value = t[v]
  460. local _t = type( value )
  461. if _t ~= "table" then
  462. -- print all number or string value here
  463. local _value = tostring( value )
  464. print( #parent > 0 and parent.."."..v.." = ".._value or v.." = ".._value )
  465. else
  466. -- for table value
  467. if not tableValue then
  468. tableValue = {}
  469. end
  470. tableValue[ k ] = v
  471. end
  472. end
  473. -- for all table value
  474. if tableValue then
  475. for k, v in pairs( tableValue ) do
  476. local value = t[v]
  477. PrintDataset( value, #parent > 0 and parent.."."..v or v )
  478. end
  479. end
  480. end
  481. local function DeserializeTable( val )
  482. local loader = loadstring or load -- lua5.2 compat
  483. local chunk = loader( "return " .. val )
  484. local ok, ret = pcall( chunk )
  485. if not ok then
  486. ret = nil
  487. print( "DeserializeTable failed!"..val )
  488. end
  489. return ret
  490. end
  491. local function _SerializeTable( val, name, skipnewlines, campact, depth, tableRef )
  492. local valt = type( val )
  493. depth = depth or 0
  494. campact = campact or false
  495. local append = StringBuilder()
  496. local eqSign = " = "
  497. local tmp = ""
  498. local string_format = string.format
  499. if not campact then
  500. append( string.rep( "\t", depth ) )
  501. skipnewlines = skipnewlines or false
  502. else
  503. skipnewlines = true
  504. eqSign = "="
  505. end
  506. if name then
  507. local nt = type( name )
  508. if nt == "string" then
  509. if name ~= "" then
  510. if string.match( name,'^%d+' ) then
  511. append( "[\"" )
  512. append( name )
  513. append( "\"]" )
  514. else
  515. append( name )
  516. end
  517. else
  518. append( "[\"\"]" )
  519. end
  520. append( eqSign )
  521. elseif nt == "number" then
  522. append( string_format( "[%s]", tostring( name ) ) )
  523. append( eqSign )
  524. else
  525. tmp = tmp .. "\"[inserializeable datatype for key:" .. nt .. "]\""
  526. end
  527. end
  528. local ending = not skipnewlines and "\n" or ""
  529. if tableRef then
  530. local refName = tableRef[ val ]
  531. if refName then
  532. valt = "ref"
  533. val = refName
  534. end
  535. end
  536. if valt == "table" then
  537. append( "{" ) append( ending )
  538. local array_part = {}
  539. local count = 0
  540. for k, v in ipairs( val ) do
  541. if type( val ) ~= "function" then
  542. array_part[k] = true
  543. if count > 0 then
  544. append( "," )
  545. append( ending )
  546. end
  547. append( _SerializeTable( v, nil, skipnewlines, campact, depth + 1, tableRef ) )
  548. count = count + 1
  549. end
  550. end
  551. local sortedK = {}
  552. for k, v in pairs( val ) do
  553. if type( v ) ~= "function" then
  554. if not array_part[k] then
  555. sortedK[#sortedK + 1] = k
  556. end
  557. end
  558. end
  559. table.sort( sortedK )
  560. for i, k in ipairs( sortedK ) do
  561. local v = val[k]
  562. if count > 0 then
  563. append( "," )
  564. append( ending )
  565. end
  566. append( _SerializeTable( v, k, skipnewlines, campact, depth + 1, tableRef ) )
  567. count = count + 1
  568. end
  569. if count >= 1 then
  570. append( ending )
  571. end
  572. if not campact then
  573. append( string.rep( "\t", depth ) )
  574. end
  575. append( "}" )
  576. elseif valt == "number" then
  577. if DefaultNumberSerializedFormat == NumberSerializedFormat or math.floor( val ) == val then
  578. append( tostring( val ) )
  579. else
  580. append( string_format( NumberSerializedFormat, val ) )
  581. end
  582. elseif valt == "string" then
  583. append( EncodeEscapeString( val ) )
  584. elseif valt == "boolean" then
  585. append( val and "true" or "false" )
  586. elseif valt == "ref" then
  587. append( val or "nil" )
  588. else
  589. tmp = tmp .. "\"[inserializeable datatype:" .. valt .. "]\""
  590. end
  591. local _, slist = append()
  592. return table.concat( slist, "" )
  593. end
  594. local function SerializeTable( val, skipnewlines, campact, tableRef, name )
  595. getmetatable( "" ).__lt = function( a, b ) return tostring( a ):lower() < tostring( b ):lower() end
  596. local ret = _SerializeTable( val, name, skipnewlines, campact, 0, tableRef )
  597. getmetatable( "" ).__lt = nil
  598. return ret
  599. end
  600. local function DumpStringBank( stringBank )
  601. print( 'dump database local string bank begin...' )
  602. for k, v in pairs( stringBank ) do
  603. print( string.format( "\t[%g] = %s", k, v ) )
  604. end
  605. print( 'dump database local string bank end.' )
  606. end
  607. local function SaveStringBankToLua( stringBank, tofile )
  608. if tofile then
  609. local fileName = StringBankOutput
  610. local _file, err = io.open( fileName )
  611. if _file ~= nil then
  612. _file:close()
  613. os.remove( fileName )
  614. end
  615. file = io.open( fileName, "w" )
  616. local fmt = string.format
  617. file:write( fmt( "local %s = {\n", DatabaseLocaleTextName ) )
  618. for k, v in pairs( stringBank ) do
  619. file:write( fmt( "\t[%g] = %s,\n", k, EncodeEscapeString( v ) ) )
  620. end
  621. file:write( "}\n" )
  622. file:write( fmt( "return %s\n--EOF", DatabaseLocaleTextName ) )
  623. file:close()
  624. else
  625. DumpStringBank( stringBank )
  626. end
  627. end
  628. local function SaveStringBankToCSV( stringBank, tofile )
  629. local _exists = {}
  630. for k, v in pairs( stringBank ) do
  631. assert( not _exists[v] )
  632. _exists[ v ] = k
  633. end
  634. local csv = CSV
  635. if tofile and csv then
  636. local fileName = StringBankCSVOutput
  637. local _file, err = io.open( fileName )
  638. if _file ~= nil then
  639. _file:close()
  640. os.remove( fileName )
  641. end
  642. local t = {}
  643. local count = 1
  644. for k, v in pairs( stringBank ) do
  645. t[count] = { k, v }
  646. count = count + 1
  647. end
  648. table.sort( t,
  649. function( a, b )
  650. return a[1] < b[1]
  651. end
  652. )
  653. csv.save( fileName, t, true )
  654. else
  655. SaveStringBankToLua( stringBank, tofile )
  656. end
  657. end
  658. local function LoadStringBankFromLua( info )
  659. local stringBank = {}
  660. local chunk = loadfile( StringBankOutput )
  661. if chunk then
  662. print( 'load string bank: '..StringBankOutput )
  663. local last = chunk()
  664. if last and type( last ) == "table" then
  665. for k, v in pairs( last ) do
  666. stringBank[ k ] = v
  667. end
  668. end
  669. end
  670. return stringBank
  671. end
  672. local function LoadStringBankFromCSV()
  673. local stringBank = {}
  674. local csv = CSV
  675. if csv then
  676. local fileName = StringBankCSVOutput
  677. local file, err = io.open( fileName )
  678. if file then
  679. print( "Load StringBank: " .. fileName )
  680. file:close()
  681. local b = csv.load( fileName, true )
  682. local allstr = {}
  683. for i = 1, #b do
  684. local key = b[ i ][ 1 ]
  685. local value = b[ i ][ 2 ]
  686. local oldHash = allstr[ value ]
  687. if not oldHash then
  688. stringBank[ key ] = value
  689. allstr[ value ] = key
  690. else
  691. print( string.format( "\"%s\" already exists in StringBank with hash %d", value, oldHash ) )
  692. end
  693. end
  694. end
  695. else
  696. return LoadStringBankFromLua()
  697. end
  698. return stringBank
  699. end
  700. local function TrimStringBank( stringBank )
  701. -- remove useless values
  702. local meta = getmetatable( stringBank )
  703. if meta then
  704. local counter = meta.__counter
  705. if counter then
  706. local used = counter.used
  707. if used then
  708. local count = 0
  709. for hash, str in pairs( stringBank ) do
  710. count = count + 1
  711. end
  712. local unused = {}
  713. for hash, str in pairs( stringBank ) do
  714. if not used[ hash ] then
  715. unused[#unused + 1] = hash
  716. end
  717. end
  718. if #unused > MaxStringBankRedundancy then
  719. for _, h in ipairs( unused ) do
  720. stringBank[ h ] = nil
  721. end
  722. end
  723. end
  724. end
  725. end
  726. end
  727. local function GetAllFileNamesAtPath( path )
  728. path, _ = path:gsub( "/", "\\" )
  729. local ret = {}
  730. for dir in io.popen( string.format( "dir \"%s\" /S/b", path ) ):lines() do
  731. local s, e, f = dir:find( ".+\\(.+)%.lua$" )
  732. if f then
  733. table.insert( ret, f )
  734. end
  735. end
  736. table.sort( ret )
  737. return ret
  738. end
  739. local function LoadDataset( name )
  740. if not Database then
  741. _G["Database"] = {}
  742. Database.loaded = {}
  743. end
  744. local loader = function( name )
  745. Database.loaded = Database.loaded or {}
  746. local r = Database.loaded[name]
  747. if r then
  748. return r
  749. end
  750. local pname = string.gsub( name, "%.", "/" )
  751. local split = function( s, p )
  752. local rt= {}
  753. string.gsub( s, '[^'..p..']+', function( w ) table.insert( rt, w ) end )
  754. return rt
  755. end
  756. local curName = pname..".lua"
  757. local fileName = DatabaseRoot.."/"..curName
  758. local checkFileName = function( path, name )
  759. path, _ = path:gsub( "/", "\\" )
  760. local _name = string.lower( name )
  761. for dir in io.popen( string.format( "dir \"%s\" /s/b", path ) ):lines() do
  762. local s, e, f = dir:find( ".+\\(.+%.lua)$" )
  763. if f then
  764. local _f = string.lower( f )
  765. if _name == _f then
  766. return name == f, f -- not match, real name
  767. end
  768. end
  769. end
  770. end
  771. local m, real = checkFileName( DatabaseRoot, curName )
  772. if not m and real then
  773. local msg = string.format( "filename must be matched by case! realname: \"%s\", you pass: \"%s\"", real, curName )
  774. print( msg )
  775. os.execute( "pause" )
  776. end
  777. local chunk = loadfile( fileName )
  778. if not chunk then
  779. fileName = LuaRoot.."/"..pname..".lua"
  780. chunk, err = loadfile( fileName )
  781. if err then
  782. print( "\n\n" )
  783. print( "----------------------------------" )
  784. print( "Load lua failed: "..fileName )
  785. print( "Error:" )
  786. print( "\t"..err )
  787. print( "----------------------------------" )
  788. print( "\n\n" )
  789. end
  790. end
  791. print( fileName )
  792. assert( chunk )
  793. if not chunk then
  794. os.execute( "pause" )
  795. end
  796. local rval = chunk()
  797. if rval.__name ~= nil then
  798. os.execute( "pause table's key must not be '__name' which is the reserved keyword." )
  799. end
  800. if rval.__sourcefile ~= nil then
  801. os.execute( "pause table's key must not be '__sourcefile' which is the reserved keyword." )
  802. end
  803. rval.__name = name
  804. rval.__sourcefile = fileName
  805. local namespace = Database
  806. local ns = split( pname, '/' )
  807. local xname = ns[#ns] -- last one
  808. if #ns > 1 then
  809. for i = 1, #ns - 1 do
  810. local n = ns[i];
  811. if namespace[n] == nil then
  812. namespace[n] = {}
  813. end
  814. namespace = namespace[n]
  815. end
  816. end
  817. namespace[xname] = rval
  818. print( "dataset: "..name.." has been loaded" )
  819. Database.loaded[name] = rval
  820. return rval
  821. end
  822. return loader( name )
  823. end
  824. local function CheckNotAscii( v )
  825. if v ~= nil and type( v ) == "string" then
  826. local byte = string.byte
  827. for _c in string.gmatch( v, "." ) do
  828. local c = byte( _c )
  829. if c < 0 or c > 127 then
  830. return true
  831. end
  832. end
  833. end
  834. return false
  835. end
  836. local function LocalizeRecord( id, record, genCode, StringBank )
  837. local localized_fields = nil
  838. local subTable = nil
  839. OrderedForeach(
  840. record,
  841. function( k, v )
  842. local vt = type( v )
  843. if vt == "string" then
  844. if CheckNotAscii( v ) then
  845. if #v > 0 and string.sub( v, 1, 1 ) == LocaleTextLeadingTag then
  846. print( string.format( "invalid leading character for localized text! key, value: %s, %s", k, v ) )
  847. os.execute( "pause" )
  848. end
  849. if not localized_fields then
  850. localized_fields = {}
  851. end
  852. -- build localized id string with tag
  853. local sid = AddStringToBank( StringBank, v )
  854. localized_fields[ k ] = string.format( "%s%g", LocaleTextLeadingTag, sid )
  855. if genCode then
  856. genCode[ #genCode + 1 ] = {
  857. id,
  858. sid,
  859. v
  860. }
  861. end
  862. end
  863. elseif vt == "table" then
  864. if not subTable then
  865. subTable = {}
  866. end
  867. subTable[ #subTable + 1 ] = v
  868. end
  869. end
  870. )
  871. local localized = false
  872. if localized_fields then
  873. -- override localized string with tag
  874. localized = true
  875. for k, v in pairs( localized_fields ) do
  876. record[ k ] = localized_fields[ k ]
  877. end
  878. end
  879. if subTable then
  880. for _, sub in ipairs( subTable ) do
  881. localized = LocalizeRecord( 0, sub, genCode, StringBank ) or localized
  882. end
  883. end
  884. return localized
  885. end
  886. local function GetValueTypeNameCS( value )
  887. local t = type( value )
  888. if t == "string" then
  889. return "string"
  890. elseif t == "number" then
  891. if value == math.floor( value ) then
  892. return "int"
  893. else
  894. return "float"
  895. end
  896. elseif t == "boolean" then
  897. return "bool"
  898. elseif t == "table" then
  899. return "table"
  900. else
  901. return "void"
  902. end
  903. end
  904. local function UniquifyTable( t )
  905. if t == nil or type( t ) ~= "table" then
  906. return nil
  907. end
  908. local hash = SerializeTable( t, true, true )
  909. local ref = UniquifyTables[ hash ]
  910. if ref then
  911. local refcount = UniquifyTablesRefCounter[ ref ] or 1
  912. UniquifyTablesRefCounter[ ref ] = refcount + 1
  913. return ref
  914. end
  915. local overwrites = nil
  916. for k, v in pairs( t ) do
  917. overwrites = overwrites or {}
  918. if type( v ) == "table" then
  919. overwrites[ k ] = UniquifyTable( v )
  920. end
  921. end
  922. if overwrites then
  923. for k, v in pairs( overwrites ) do
  924. t[ k ] = overwrites[ k ]
  925. end
  926. end
  927. local id = #UniquifyTablesIds + 1
  928. UniquifyTablesIds[ id ] = hash
  929. UniquifyTables[ hash ] = t
  930. UniquifyTablesInvIds[ t ] = id
  931. UniquifyTablesRefCounter[ t ] = 1
  932. return t
  933. end
  934. local function OptimizeDataset( dataset )
  935. local ids = {}
  936. local names = {}
  937. local idType = nil
  938. -- choose cs data type
  939. -- for all fields in a record
  940. local typeNameTable = {}
  941. for k, v in pairs( dataset ) do
  942. local _sk = tostring( k )
  943. if _sk ~= "__name" and _sk ~= "__sourcefile" then
  944. if not idType then
  945. idType = type( k )
  946. end
  947. if idType == type( k ) then
  948. ids[ #ids + 1 ] = k
  949. end
  950. end
  951. end
  952. if EnableDefaultValueOptimize then
  953. -- find bigest table to generate all fields
  954. local majorItem = 1
  955. local f = 0
  956. for k, v in pairs( dataset ) do
  957. if type( v ) == "table" then
  958. local num = 0
  959. for _, _ in pairs( v ) do
  960. num = num + 1
  961. end
  962. if num > f then
  963. f = num
  964. majorItem = k
  965. end
  966. end
  967. end
  968. local v = dataset[ majorItem ]
  969. if type( v ) == "table" then
  970. for name, value in pairs( v ) do
  971. local nt = type( name )
  972. if nt == "string" and name == "id" then
  973. print( "this table already has a field named 'id'" )
  974. end
  975. if nt == "string" then
  976. names[ #names + 1 ] = name
  977. end
  978. end
  979. table.sort( names, function( a, b ) return a:lower() < b:lower() end )
  980. for i, field in ipairs( names ) do
  981. -- for all record / row
  982. for r, t in ipairs( ids ) do
  983. local record = dataset[ t ]
  984. if record[ field ] ~= nil then
  985. local v = record[ field ]
  986. local curType = GetValueTypeNameCS( v )
  987. if not typeNameTable[ field ] then
  988. typeNameTable[ field ] = curType
  989. elseif typeNameTable[ field ] == "int" and curType == "float" then
  990. -- overwrite int to float
  991. typeNameTable[ field ] = curType
  992. elseif curType == "table" then
  993. -- overwrite to table
  994. typeNameTable[ field ] = curType
  995. end
  996. end
  997. end
  998. -- patching miss fields with default values
  999. local curType = typeNameTable[ field ]
  1000. for r, t in ipairs( ids ) do
  1001. local record = dataset[ t ]
  1002. local v = record[ field ]
  1003. if v == nil then
  1004. local ft = typeNameTable[ field ]
  1005. if ft == "string" then
  1006. v = ""
  1007. elseif ft == "number" or ft == "int" or ft == "float" then
  1008. v = 0
  1009. elseif ft == "table" then
  1010. v = {}
  1011. elseif ft == "bool" then
  1012. v = false
  1013. end
  1014. record[ field ] = v
  1015. end
  1016. end
  1017. end
  1018. end
  1019. end
  1020. ids = {}
  1021. idType = nil
  1022. UniquifyTables = {}
  1023. UniquifyTablesIds = {}
  1024. UniquifyTablesInvIds = {}
  1025. UniquifyTablesRefCounter = {}
  1026. local isIntegerKey = true
  1027. local overwrites = nil
  1028. OrderedForeach(
  1029. dataset,
  1030. function( k, v )
  1031. local _sk = tostring( k )
  1032. if _sk ~= "__name" and _sk ~= "__sourcefile" then
  1033. if not idType then
  1034. idType = type( k )
  1035. end
  1036. -- check type
  1037. if idType == type( k ) then
  1038. ids[ #ids + 1 ] = k
  1039. if idType == "number" then
  1040. if isIntegerKey then
  1041. isIntegerKey = k == floor( k )
  1042. end
  1043. end
  1044. end
  1045. if type( v ) == "table" then
  1046. overwrites = overwrites or {}
  1047. overwrites[ k ] = UniquifyTable( v )
  1048. end
  1049. end
  1050. end
  1051. )
  1052. if overwrites then
  1053. for k, v in pairs( overwrites ) do
  1054. dataset[ k ] = overwrites[ k ]
  1055. end
  1056. end
  1057. local returnVal = nil
  1058. if EnableDefaultValueOptimize then
  1059. local defaultValues = nil
  1060. for i, field in ipairs( names ) do
  1061. local curType = typeNameTable[ field ]
  1062. -- for all record/row
  1063. local defaultValueStat = {
  1064. }
  1065. for r, t in ipairs( ids ) do
  1066. local record = dataset[ t ]
  1067. local v = record[ field ]
  1068. if v ~= nil then
  1069. local vcount = defaultValueStat[ v ] or 0
  1070. defaultValueStat[ v ] = vcount + 1
  1071. else
  1072. assert( "default value missing!" )
  1073. end
  1074. end
  1075. -- find the mostest used as a default value
  1076. local max = -1
  1077. local defaultValue = nil
  1078. local _defaultValue = "{}"
  1079. local result = OrderedForeachByValue(
  1080. defaultValueStat,
  1081. function( value, count )
  1082. if count >= max then
  1083. if count > max then
  1084. max = count
  1085. defaultValue = value
  1086. _defaultValue = SerializeTable( defaultValue, true, true )
  1087. else
  1088. if curType == "table" then
  1089. local _value = SerializeTable( value, true, true )
  1090. if #_value > #_defaultValue then
  1091. defaultValue = value
  1092. _defaultValue = SerializeTable( defaultValue, true, true )
  1093. end
  1094. else
  1095. local _value = value
  1096. local _defaultValue = defaultValue
  1097. if type( value ) == 'boolean' then
  1098. _value = value and 1 or 0
  1099. _defaultValue = defaultValue and 1 or 0
  1100. end
  1101. if _value < _defaultValue then
  1102. defaultValue = value
  1103. _defaultValue = SerializeTable( defaultValue, true, true )
  1104. end
  1105. end
  1106. end
  1107. end
  1108. end
  1109. )
  1110. if not result then
  1111. error( string.format( "create default value for \"%s\" failed. please make sure all the value's types are the same.", field ) )
  1112. end
  1113. if defaultValue ~= nil then
  1114. defaultValues = defaultValues or {}
  1115. defaultValues[ field ] = defaultValue
  1116. end
  1117. end
  1118. returnVal = defaultValues
  1119. end
  1120. -- remove tables whose's ref is 1 and re-mapping id
  1121. local newid = 1
  1122. local newIds = {}
  1123. local newInvIds = {}
  1124. OrderedForeach(
  1125. UniquifyTablesIds,
  1126. function( id, hash )
  1127. local table = UniquifyTables[ hash ]
  1128. local refcount = UniquifyTablesRefCounter[ table ]
  1129. if refcount == 1 then
  1130. UniquifyTables[ hash ] = nil
  1131. else
  1132. newIds[ newid ] = hash
  1133. newInvIds[ table ] = newid
  1134. newid = newid + 1
  1135. end
  1136. end
  1137. )
  1138. UniquifyTablesIds = newIds
  1139. UniquifyTablesInvIds = newInvIds
  1140. return returnVal
  1141. end
  1142. local function ToUniqueTableRefName( id )
  1143. if id <= MaxLocalVariableNum then
  1144. return string.format( RefTableName.."_%d", id )
  1145. else
  1146. return string.format( RefTableName.."[%d]", id - MaxLocalVariableNum )
  1147. end
  1148. end
  1149. local function SaveDatasetToFile( dataset, tofile, tableRef, name )
  1150. if tofile then
  1151. outFile = CreateFileWriter( dataset.__sourcefile, "w" )
  1152. else
  1153. outFile = CreateFileWriter()
  1154. end
  1155. local ptr2ref = nil
  1156. if tableRef and tableRef.ptr2ref then
  1157. ptr2ref = tableRef.ptr2ref
  1158. end
  1159. if tableRef and tableRef.name2value then
  1160. local name2table = tableRef.name2value
  1161. local tables = tableRef.tables
  1162. local tableIds = tableRef.tableIds
  1163. local ptr2ref = tableRef.ptr2ref
  1164. local refcounter = tableRef.refcounter
  1165. local maxLocalVariableNum = tableRef.maxLocalVariableNum or MaxLocalVariableNum
  1166. local refTableName = tableRef.refTableName or "__rt"
  1167. local tableNum = #tableIds
  1168. for id, hash in ipairs( tableIds ) do
  1169. local table = tables[ hash ]
  1170. if table and id <= maxLocalVariableNum then
  1171. local refname = ptr2ref[ table ]
  1172. -- temp comment out top level ref
  1173. ptr2ref[ table ] = nil
  1174. local refcount = refcounter[ table ]
  1175. outFile.write(
  1176. string.format(
  1177. "%slocal %s = %s\n",
  1178. PrintTableRefCount and string.format( "--ref:%d\n", refcount ) or "",
  1179. refname,
  1180. SerializeTable( table, false, false, ptr2ref )
  1181. )
  1182. )
  1183. ptr2ref[ table ] = refname
  1184. else
  1185. break
  1186. end
  1187. end
  1188. if tableNum > maxLocalVariableNum then
  1189. local maxCount = tableNum - maxLocalVariableNum
  1190. outFile.write( string.format( "local %s = createtable and createtable( %d, 0 ) or {}\n", refTableName, maxCount ) )
  1191. for id = maxLocalVariableNum + 1, tableNum do
  1192. local offset = id - maxLocalVariableNum
  1193. local hash = tableIds[ id ]
  1194. local table = tables[ hash ]
  1195. local refname = ptr2ref[ table ]
  1196. -- temp comment out top level ref
  1197. ptr2ref[ table ] = nil
  1198. local refcount = refcounter[ table ]
  1199. outFile.write(
  1200. string.format(
  1201. "%s%s[%d] = %s\n",
  1202. PrintTableRefCount and string.format( "-- %s, ref:%d\n", refname, refcount ) or "",
  1203. refTableName, offset,
  1204. SerializeTable( table, false, false, ptr2ref )
  1205. )
  1206. )
  1207. ptr2ref[ table ] = refname
  1208. end
  1209. end
  1210. end
  1211. local datasetName = dataset.__name or name
  1212. if not datasetName then
  1213. datasetName = UnknownName
  1214. dataset.__name = datasetName
  1215. end
  1216. outFile.write( string.format( "local %s = \n", datasetName ) )
  1217. -- remove none table value
  1218. local removed = nil
  1219. for k, v in pairs( dataset ) do
  1220. if type( v ) ~= "table" then
  1221. removed = removed or {}
  1222. removed[ #removed + 1 ] = k
  1223. end
  1224. end
  1225. if removed then
  1226. for _, k in ipairs( removed ) do
  1227. dataset[ k ] = nil
  1228. end
  1229. end
  1230. outFile.write( SerializeTable( dataset, false, false, ptr2ref ) )
  1231. outFile.write( "\n" )
  1232. if tableRef and tableRef.postOutput then
  1233. tableRef.postOutput( outFile )
  1234. end
  1235. outFile.write( string.format( "\nreturn %s\n", datasetName ) )
  1236. outFile.close()
  1237. end
  1238. local function ExportOptimizedDataset( t, StringBank )
  1239. local datasetName = t.__name
  1240. if not datasetName then
  1241. datasetName = UnknownName
  1242. t.__name = datasetName
  1243. end
  1244. local localized = false
  1245. local genCode = false
  1246. if EnableLocalization then
  1247. OrderedForeach(
  1248. t,
  1249. function( id, _record )
  1250. if type( _record ) == "table" then
  1251. localized = LocalizeRecord( id, _record, genCode, StringBank ) or localized
  1252. end
  1253. end
  1254. )
  1255. end
  1256. local tableRef = nil
  1257. local defaultValues = nil
  1258. if EnableDatasetOptimize then
  1259. defaultValues = OptimizeDataset( t )
  1260. if defaultValues then
  1261. local removeDefaultValues = function( record )
  1262. local removes = nil
  1263. local adds = nil
  1264. for field, defaultVal in pairs( defaultValues ) do
  1265. local value = record[ field ]
  1266. local hasValue = true
  1267. if value == nil then
  1268. assert( false, "OptimizeDataset should patch all missing fields!" )
  1269. hasValue = false
  1270. end
  1271. if value == defaultVal and hasValue then
  1272. removes = removes or {}
  1273. removes[ #removes + 1 ] = field
  1274. else
  1275. adds = adds or {}
  1276. adds[ field ] = value
  1277. end
  1278. end
  1279. -- remove fields with default value
  1280. if removes then
  1281. for _, f in ipairs( removes ) do
  1282. record[ f ] = nil
  1283. end
  1284. end
  1285. -- patch fields with none-default value
  1286. if adds then
  1287. for f, v in pairs( adds ) do
  1288. record[ f ] = v
  1289. end
  1290. end
  1291. end
  1292. local removed = {}
  1293. for _, record in pairs( t ) do
  1294. if type( record ) == "table" then
  1295. if not removed[ record ] then
  1296. removeDefaultValues( record )
  1297. removed[ record ] = true
  1298. end
  1299. end
  1300. end
  1301. end
  1302. local reftables = nil
  1303. local ptr2ref = nil
  1304. -- create ref table: table -> refname
  1305. for _, hash in pairs( UniquifyTablesIds ) do
  1306. local t = UniquifyTables[ hash ]
  1307. if t then
  1308. local refName = ToUniqueTableRefName( UniquifyTablesInvIds[ t ] )
  1309. reftables = reftables or {}
  1310. ptr2ref = ptr2ref or {}
  1311. reftables[ refName ] = t
  1312. ptr2ref[ t ] = refName
  1313. end
  1314. end
  1315. tableRef = {
  1316. name2value = reftables,
  1317. tables = UniquifyTables, -- hash -> table
  1318. tableIds = UniquifyTablesIds, -- id -> hash
  1319. ptr2ref = ptr2ref, -- table -> refname
  1320. refcounter = UniquifyTablesRefCounter, -- table -> refcount
  1321. maxLocalVariableNum = MaxLocalVariableNum,
  1322. refTableName = RefTableName,
  1323. postOutput = function( outFile )
  1324. if defaultValues then
  1325. outFile.write(
  1326. string.format(
  1327. "local %s = %s\n",
  1328. DefaultValueTableName,
  1329. SerializeTable( defaultValues, false, false, ptr2ref )
  1330. )
  1331. )
  1332. outFile.write( "do\n" )
  1333. outFile.write( string.format( "\tlocal base = { __index = %s, __newindex = function() error( \"Attempt to modify read-only table\" ) end }\n", DefaultValueTableName ) )
  1334. outFile.write( string.format( "\tfor k, v in pairs( %s ) do\n", datasetName ) )
  1335. outFile.write( "\t\tsetmetatable( v, base )\n" )
  1336. outFile.write( "\tend\n" )
  1337. outFile.write( "\tbase.__metatable = false\n" )
  1338. outFile.write( "end\n" )
  1339. end
  1340. end
  1341. }
  1342. end
  1343. return t, tableRef, localized
  1344. end
  1345. --tofile: not output to file, just for debug
  1346. --newStringBank: if false, exporter will use existing string hash for increamental building
  1347. local function ExportDatabaseLocalText( tofile, newStringBank )
  1348. local StringBank = nil
  1349. if newStringBank then
  1350. StringBank = {}
  1351. else
  1352. StringBank = LoadStringBankFromCSV()
  1353. end
  1354. StringBank = StringBank or {}
  1355. local localized_dirty = false
  1356. local files = GetAllFileNamesAtPath( DatabaseRoot )
  1357. for _, v in ipairs( files ) do
  1358. if not ExcludedFiles[ v ] then
  1359. print( "LoadDataset :"..v )
  1360. LoadDataset( v )
  1361. local t = Database[ v ]
  1362. local localized = false
  1363. if t then
  1364. local _t, tableRef, localized = ExportOptimizedDataset( t, StringBank )
  1365. assert( _t == t )
  1366. localized_dirty = localized_dirty or localized
  1367. SaveDatasetToFile( Database[ v ], tofile, tableRef )
  1368. end
  1369. end
  1370. end
  1371. TrimStringBank( StringBank )
  1372. if localized_dirty then
  1373. SaveStringBankToCSV( StringBank, tofile )
  1374. else
  1375. print( "\nDatabase LocaleText is up to date.\n" )
  1376. end
  1377. print( "Database Exporting LocaleText done." )
  1378. end
  1379. --[[
  1380. local test = {
  1381. {
  1382. 1,
  1383. 2,
  1384. 3,
  1385. a = "123",
  1386. b = "123"
  1387. },
  1388. {
  1389. 1,
  1390. 2,
  1391. 3,
  1392. a = "123",
  1393. b = "123"
  1394. },
  1395. {
  1396. 1,
  1397. 2,
  1398. 5,
  1399. a = "123",
  1400. b = "123"
  1401. },
  1402. [9] = {
  1403. 1,
  1404. 2,
  1405. 5,
  1406. a = "123",
  1407. b = "123"
  1408. },
  1409. [100] = {
  1410. 1,
  1411. 2,
  1412. 3,
  1413. a = "tttt",
  1414. b = "123"
  1415. },
  1416. [11] = {
  1417. 1,
  1418. 2,
  1419. 3,
  1420. a = "123",
  1421. b = "123",
  1422. c = { {1}, {1}, {2}, {2} },
  1423. d = { { a = 1, 1 }, { a = 2, 2 } },
  1424. e = { { a = 1, 1 }, { a = 2, 2 } },
  1425. }
  1426. }
  1427. EnableDefaultValueOptimize = false
  1428. local _localizedText = {}
  1429. local _src = SerializeTable( test )
  1430. local _clone = DeserializeTable( _src )
  1431. print( _src )
  1432. local t, tableRef = ExportOptimizedDataset( test, _localizedText )
  1433. assert( t == test )
  1434. --print( SerializeTable( t ) )
  1435. SaveDatasetToFile( t, false, tableRef, "test" )
  1436. local _dst = SerializeTable( t )
  1437. print( _dst )
  1438. assert( _src == _dst )
  1439. EnableDefaultValueOptimize = true
  1440. _localizedText = {}
  1441. t, tableRef = ExportOptimizedDataset( _clone, _localizedText )
  1442. assert( t == _clone )
  1443. --_clone.__name = "__cloned_test"
  1444. SaveDatasetToFile( _clone, false, tableRef )
  1445. local _dst = SerializeTable( _clone )
  1446. print( _dst )
  1447. print( _src ~= _dst )
  1448. --]]
  1449. ExportDatabaseLocalText( true )