diff --git a/Makefile b/Makefile index 3a7b26ebe5b3b..cccdafe1c5908 100644 --- a/Makefile +++ b/Makefile @@ -318,13 +318,15 @@ $(TARGET): $(ODIR) $(DDIR) $(OBJS) $(LD) $(W32FLAGS) -o $(TARGET) $(DEFINES) $(CXXFLAGS) \ $(OBJS) $(LDFLAGS) -.PHONY: version +.PHONY: version verifyjson version: @( VERSION_STRING=$(VERSION) ; \ [ -e ".git" ] && GITVERSION=$$( git describe --tags --always --dirty --match "[0-9A-Z]*.[0-9A-Z]*" ) && VERSION_STRING=$$GITVERSION ; \ [ -e "$(SRC_DIR)/version.h" ] && OLDVERSION=$$(grep VERSION $(SRC_DIR)/version.h|cut -d '"' -f2) ; \ if [ "x$$VERSION_STRING" != "x$$OLDVERSION" ]; then echo "#define VERSION \"$$VERSION_STRING\"" | tee $(SRC_DIR)/version.h ; fi \ ) +verifyjson: + lua lua/json_verifier.lua $(ODIR): mkdir -p $(ODIR) diff --git a/lua/dkjson.lua b/lua/dkjson.lua new file mode 100644 index 0000000000000..b7475ba180ff8 --- /dev/null +++ b/lua/dkjson.lua @@ -0,0 +1,843 @@ + -- Module options: + local always_try_using_lpeg = true + local register_global_module_table = false + local global_module_name = 'json' + + --[==[ + +David Kolf's JSON module for Lua 5.1/5.2 +======================================== + +*Version 2.4* + +In the default configuration this module writes no global values, not even +the module table. Import it using + + json = require ("dkjson") + +In environments where `require` or a similiar function are not available +and you cannot receive the return value of the module, you can set the +option `register_global_module_table` to `true`. The module table will +then be saved in the global variable with the name given by the option +`global_module_name`. + +Exported functions and values: + +`json.encode (object [, state])` +-------------------------------- + +Create a string representing the object. `Object` can be a table, +a string, a number, a boolean, `nil`, `json.null` or any object with +a function `__tojson` in its metatable. A table can only use strings +and numbers as keys and its values have to be valid objects as +well. It raises an error for any invalid data types or reference +cycles. + +`state` is an optional table with the following fields: + + - `indent` + When `indent` (a boolean) is set, the created string will contain + newlines and indentations. Otherwise it will be one long line. + - `keyorder` + `keyorder` is an array to specify the ordering of keys in the + encoded output. If an object has keys which are not in this array + they are written after the sorted keys. + - `level` + This is the initial level of indentation used when `indent` is + set. For each level two spaces are added. When absent it is set + to 0. + - `buffer` + `buffer` is an array to store the strings for the result so they + can be concatenated at once. When it isn't given, the encode + function will create it temporary and will return the + concatenated result. + - `bufferlen` + When `bufferlen` is set, it has to be the index of the last + element of `buffer`. + - `tables` + `tables` is a set to detect reference cycles. It is created + temporary when absent. Every table that is currently processed + is used as key, the value is `true`. + +When `state.buffer` was set, the return value will be `true` on +success. Without `state.buffer` the return value will be a string. + +`json.decode (string [, position [, null]])` +-------------------------------------------- + +Decode `string` starting at `position` or at 1 if `position` was +omitted. + +`null` is an optional value to be returned for null values. The +default is `nil`, but you could set it to `json.null` or any other +value. + +The return values are the object or `nil`, the position of the next +character that doesn't belong to the object, and in case of errors +an error message. + +Two metatables are created. Every array or object that is decoded gets +a metatable with the `__jsontype` field set to either `array` or +`object`. If you want to provide your own metatables use the syntax + + json.decode (string, position, null, objectmeta, arraymeta) + +To prevent the assigning of metatables pass `nil`: + + json.decode (string, position, null, nil) + +`.__jsonorder` +------------------------- + +`__jsonorder` can overwrite the `keyorder` for a specific table. + +`.__jsontype` +------------------------ + +`__jsontype` can be either `"array"` or `"object"`. This value is only +checked for empty tables. (The default for empty tables is `"array"`). + +`.__tojson (self, state)` +------------------------------------ + +You can provide your own `__tojson` function in a metatable. In this +function you can either add directly to the buffer and return true, +or you can return a string. On errors nil and a message should be +returned. + +`json.null` +----------- + +You can use this value for setting explicit `null` values. + +`json.version` +-------------- + +Set to `"dkjson 2.4"`. + +`json.quotestring (string)` +--------------------------- + +Quote a UTF-8 string and escape critical characters using JSON +escape sequences. This function is only necessary when you build +your own `__tojson` functions. + +`json.addnewline (state)` +------------------------- + +When `state.indent` is set, add a newline to `state.buffer` and spaces +according to `state.level`. + +LPeg support +------------ + +When the local configuration variable `always_try_using_lpeg` is set, +this module tries to load LPeg to replace the `decode` function. The +speed increase is significant. You can get the LPeg module at + . +When LPeg couldn't be loaded, the pure Lua functions stay active. + +In case you don't want this module to require LPeg on its own, +disable the option `always_try_using_lpeg` in the options section at +the top of the module. + +In this case you can later load LPeg support using + +### `json.use_lpeg ()` + +Require the LPeg module and replace the functions `quotestring` and +and `decode` with functions that use LPeg patterns. +This function returns the module table, so you can load the module +using: + + json = require "dkjson".use_lpeg() + +Alternatively you can use `pcall` so the JSON module still works when +LPeg isn't found. + + json = require "dkjson" + pcall (json.use_lpeg) + +### `json.using_lpeg` + +This variable is set to `true` when LPeg was loaded successfully. + +--------------------------------------------------------------------- + +Contact +------- + +You can contact the author by sending an e-mail to 'david' at the +domain 'dkolf.de'. + +--------------------------------------------------------------------- + +*Copyright (C) 2010-2013 David Heiko Kolf* + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + diff --git a/lua/json_verifier.lua b/lua/json_verifier.lua new file mode 100644 index 0000000000000..f398f416e7b73 --- /dev/null +++ b/lua/json_verifier.lua @@ -0,0 +1,110 @@ +local json = require("lua/dkjson") +local lfs = require("lfs") + +local exit_code = 0 + +-- function to read a file completely into a string +function read_file(filename) + local f = io.open(filename, "r") + local content = f:read("*all") + f:close() + return content +end + +-- parse the JSON of an entire cataclysm file +function parse_cata_json(filename, handler) + local content = read_file(filename) + + local root, pos, err = json.decode(content, 1, nil) + if err then + print("Error in ", filename ,":", err) + os.exit(1) + else + -- top level should be a json array + if type(root) ~= "table" then + print("Wrong root element to JSON file ", filename, " :", type(root)) + end + + for _, entry in ipairs(root) do + if not entry.type then + print("Invalid entry type in ", filename, ": ", entry.type) + end + if handler[entry.type] then + handler[entry.type](entry, filename) + end + end + end +end + +local definitions = {} +function load_item_definition(entry, filename) + -- store that this item was defined + definitions[entry.id] = true +end + +-- define load_definition handlers +local load_definition = {} +load_definition.BOOK = load_item_definition +load_definition.TOOL = load_item_definition +load_definition.GUN = load_item_definition +load_definition.GUNMOD = load_item_definition +load_definition.TOOL_ARMOR = load_item_definition +load_definition.ARMOR = load_item_definition +load_definition.BIONIC_ITEM = load_item_definition +load_definition.GENERIC = load_item_definition +load_definition.AMMO = load_item_definition +load_definition.CONTAINER = load_item_definition +load_definition.COMESTIBLE = load_item_definition + +local recipe_handler = {} + +function ensure_definition(id, filename, parent_id) + if not definitions[id] then + -- signify that something went wrong + exit_code = 1 + + print("Trying to access non-existent item id ", id, " in ", filename, "(", parent_id, ")") + end +end + +recipe_handler.recipe = function(entry, filename) + ensure_definition(entry.result, filename, entry.result) + for _, alternatives in ipairs(entry.components) do + for _, item in ipairs(alternatives) do + ensure_definition(item[1], filename, entry.result) + end + end +end + +function string.endswith(mystr, myend) + return myend=="" or string.sub(mystr,string.len(mystr)-string.len(myend)+1)==myend +end + + +function load_all_jsons_recursive(path, handler) + for file in lfs.dir(path) do + if file ~= "." and file ~= ".." then + local f = path..'/'..file + + local attr = lfs.attributes(f) + if attr.mode == "directory" then + load_all_jsons_recursive(f, handler) + elseif attr.mode == "file" and string.endswith(f, ".json") then + parse_cata_json(f, handler) + end + end + end +end + +function load_all_jsons(handler) + load_all_jsons_recursive("data/json", handler) + load_all_jsons_recursive("data/mods", handler) +end + +-- first load all item definitions +load_all_jsons(load_definition) + +-- then verify recipes +load_all_jsons(recipe_handler) + +os.exit(exit_code)