diff --git a/.gitmodules b/.gitmodules index d57d82200..c643c96a7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "scripts2"] path = scripts url = ../../DFHack/scripts.git +[submodule "depends/jsoncpp"] + path = depends/jsoncpp-sub + url = ../../open-source-parsers/jsoncpp.git diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index 33887c181..e98f7a2a6 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -17,7 +17,7 @@ default_flags = [ '-I','depends/protobuf', '-I','depends/lua/include', '-I','depends/md5', - '-I','depends/jsoncpp', + '-I','depends/jsoncpp/include', '-I','depends/tinyxml', '-I','depends/tthread', '-I','depends/clsocket/src', diff --git a/CMakeLists.txt b/CMakeLists.txt index 237c254f1..9a5aa7129 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ else(CMAKE_CONFIGURATION_TYPES) endif(CMAKE_CONFIGURATION_TYPES) OPTION(BUILD_DOCS "Choose whether to build the documentation (requires python and Sphinx)." OFF) +OPTION(REMOVE_SYMBOLS_FROM_DF_STUBS "Remove debug symbols from DF stubs. (Reduces libdfhack size to about half but removes a few useful symbols)" ON) ## some generic CMake magic cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) @@ -136,6 +137,21 @@ ${CMAKE_MODULE_PATH} # generates compile_commands.json, used for autocompletion by some editors SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) +include(CheckCXXSourceCompiles) +CHECK_CXX_SOURCE_COMPILES(" +#include +#include +int main(void) { + char32_t in = 0; + char out[MB_CUR_MAX]; + std::mbstate_t state{}; + std::c32rtomb(out, in, &state); + return 0; +}" HAVE_CUCHAR2) +if(HAVE_CUCHAR2) + add_definitions("-DHAVE_CUCHAR") +endif() + # mixing the build system with the source code is ugly and stupid. enforce the opposite :) if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") message(FATAL_ERROR "In-source builds are not allowed.") @@ -147,9 +163,9 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac endif() # set up versioning. -set(DF_VERSION "0.44.10") -set(DFHACK_RELEASE "r1") -set(DFHACK_PRERELEASE FALSE) +set(DF_VERSION "0.44.12") +set(DFHACK_RELEASE "alpha0") +set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") @@ -348,7 +364,6 @@ find_package(ZLIB REQUIRED) include_directories(depends/protobuf) include_directories(depends/lua/include) include_directories(depends/md5) -include_directories(depends/jsoncpp) # Support linking against external tinyxml # If we find an external tinyxml, set the DFHACK_TINYXML variable to "tinyxml" diff --git a/depends/CMakeLists.txt b/depends/CMakeLists.txt index d8442b12a..a096e7b0f 100644 --- a/depends/CMakeLists.txt +++ b/depends/CMakeLists.txt @@ -9,7 +9,9 @@ if(NOT TinyXML_FOUND) endif() add_subdirectory(tthread) -add_subdirectory(jsoncpp) +OPTION(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" OFF) +OPTION(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" OFF) +add_subdirectory(jsoncpp-sub) # build clsocket static and only as a dependency. Setting those options here overrides its own default settings. OPTION(CLSOCKET_SHARED "Build clsocket lib as shared." OFF) OPTION(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependency." ON) diff --git a/depends/jsoncpp-sub b/depends/jsoncpp-sub new file mode 160000 index 000000000..ddabf50f7 --- /dev/null +++ b/depends/jsoncpp-sub @@ -0,0 +1 @@ +Subproject commit ddabf50f72cf369bf652a95c4d9fe31a1865a781 diff --git a/depends/jsoncpp/CMakeLists.txt b/depends/jsoncpp/CMakeLists.txt deleted file mode 100644 index 112903ee3..000000000 --- a/depends/jsoncpp/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -PROJECT(jsoncpp) -ADD_LIBRARY(jsoncpp STATIC jsoncpp.cpp) diff --git a/depends/jsoncpp/jsoncpp.cpp b/depends/jsoncpp/jsoncpp.cpp deleted file mode 100644 index 009d04e7a..000000000 --- a/depends/jsoncpp/jsoncpp.cpp +++ /dev/null @@ -1,5091 +0,0 @@ -/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). -/// It is intended to be used with #include "json/json.h" - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - -/* -The JsonCpp library's source code, including accompanying documentation, -tests and demonstration applications, are licensed under the following -conditions... - -The author (Baptiste Lepilleur) explicitly disclaims copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, -this software is released into the Public Domain. - -In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is -released under the terms of the MIT License (see below). - -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual -Public Domain/MIT License conditions described here, as they choose. - -The MIT License is about as close to Public Domain as a license can get, and is -described in clear, concise terms at: - - http://en.wikipedia.org/wiki/MIT_License - -The full text of the MIT License follows: - -======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur - -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. -======================================================================== -(END LICENSE TEXT) - -The MIT license is compatible with both the GPL and commercial -software, affording one all of the rights of Public Domain with the -minor nuisance of being required to keep the above copyright notice -and license text in the source code. Note also that by accepting the -Public Domain "license" you can re-license your copy using whatever -license you like. - -*/ - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - - - - - - -#include "jsoncpp.h" - -#ifndef JSON_IS_AMALGAMATION -#error "Compile with -I PATH_TO_JSON_DIRECTORY" -#endif - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_tool.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED -#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED - -/* This header provides common string manipulation support, such as UTF-8, - * portable conversion from/to string... - * - * It is an internal header that must not be exposed. - */ - -namespace Json { - -/// Converts a unicode code-point to UTF-8. -static inline std::string codePointToUTF8(unsigned int cp) { - std::string result; - - // based on description from http://en.wikipedia.org/wiki/UTF-8 - - if (cp <= 0x7f) { - result.resize(1); - result[0] = static_cast(cp); - } else if (cp <= 0x7FF) { - result.resize(2); - result[1] = static_cast(0x80 | (0x3f & cp)); - result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); - } else if (cp <= 0xFFFF) { - result.resize(3); - result[2] = static_cast(0x80 | (0x3f & cp)); - result[1] = static_cast(0x80 | (0x3f & (cp >> 6))); - result[0] = static_cast(0xE0 | (0xf & (cp >> 12))); - } else if (cp <= 0x10FFFF) { - result.resize(4); - result[3] = static_cast(0x80 | (0x3f & cp)); - result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); - result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); - result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); - } - - return result; -} - -/// Returns true if ch is a control character (in range [1,31]). -static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } - -enum { - /// Constant that specify the size of the buffer that must be passed to - /// uintToString. - uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 -}; - -// Defines a char buffer for use with uintToString(). -typedef char UIntToStringBuffer[uintToStringBufferSize]; - -/** Converts an unsigned integer to string. - * @param value Unsigned interger to convert to string - * @param current Input/Output string buffer. - * Must have at least uintToStringBufferSize chars free. - */ -static inline void uintToString(LargestUInt value, char*& current) { - *--current = 0; - do { - *--current = static_cast(value % 10U + static_cast('0')); - value /= 10; - } while (value != 0); -} - -/** Change ',' to '.' everywhere in buffer. - * - * We had a sophisticated way, but it did not work in WinCE. - * @see https://github.com/open-source-parsers/jsoncpp/pull/9 - */ -static inline void fixNumericLocale(char* begin, char* end) { - while (begin < end) { - if (*begin == ',') { - *begin = '.'; - } - ++begin; - } -} - -} // namespace Json { - -#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_tool.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_reader.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2011 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include "json_tool.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below -#define snprintf _snprintf -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 -// Disable warning about strdup being deprecated. -#pragma warning(disable : 4996) -#endif - -static int const stackLimit_g = 1000; -static int stackDepth_g = 0; // see readValue() - -namespace Json { - -#if __cplusplus >= 201103L -typedef std::unique_ptr CharReaderPtr; -#else -typedef std::auto_ptr CharReaderPtr; -#endif - -// Implementation of class Features -// //////////////////////////////// - -Features::Features() - : allowComments_(true), strictRoot_(false), - allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} - -Features Features::all() { return Features(); } - -Features Features::strictMode() { - Features features; - features.allowComments_ = false; - features.strictRoot_ = true; - features.allowDroppedNullPlaceholders_ = false; - features.allowNumericKeys_ = false; - return features; -} - -// Implementation of class Reader -// //////////////////////////////// - -static bool containsNewLine(Reader::Location begin, Reader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; -} - -// Class Reader -// ////////////////////////////////////////////////////////////////// - -Reader::Reader() - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(Features::all()), - collectComments_() {} - -Reader::Reader(const Features& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} - -bool -Reader::parse(const std::string& document, Value& root, bool collectComments) { - document_ = document; - const char* begin = document_.c_str(); - const char* end = begin + document_.length(); - return parse(begin, end, root, collectComments); -} - -bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { - // std::istream_iterator begin(sin); - // std::istream_iterator end; - // Those would allow streamed input from a file, if parse() were a - // template function. - - // Since std::string is reference-counted, this at least does not - // create an extra copy. - std::string doc; - std::getline(sin, doc, (char)EOF); - return parse(doc, root, collectComments); -} - -bool Reader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { - if (!features_.allowComments_) { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; - errors_.clear(); - while (!nodes_.empty()) - nodes_.pop(); - nodes_.push(&root); - - stackDepth_g = 0; // Yes, this is bad coding, but options are limited. - bool successful = readValue(); - Token token; - skipCommentTokens(token); - if (collectComments_ && !commentsBefore_.empty()) - root.setComment(commentsBefore_, commentAfter); - if (features_.strictRoot_) { - if (!root.isArray() && !root.isObject()) { - // Set error location to start of doc, ideally should be first token found - // in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( - "A valid JSON document must be either an array or an object value.", - token); - return false; - } - } - return successful; -} - -bool Reader::readValue() { - // This is a non-reentrant way to support a stackLimit. Terrible! - // But this deprecated class has a security problem: Bad input can - // cause a seg-fault. This seems like a fair, binary-compatible way - // to prevent the problem. - if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); - ++stackDepth_g; - - Token token; - skipCommentTokens(token); - bool successful = true; - - if (collectComments_ && !commentsBefore_.empty()) { - currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_ = ""; - } - - switch (token.type_) { - case tokenObjectBegin: - successful = readObject(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenArrayBegin: - successful = readArray(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenNumber: - successful = decodeNumber(token); - break; - case tokenString: - successful = decodeString(token); - break; - case tokenTrue: - { - Value v(true); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { - Value v(false); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenArraySeparator: - case tokenObjectEnd: - case tokenArrayEnd: - if (features_.allowDroppedNullPlaceholders_) { - // "Un-read" the current token and mark the current value as a null - // token. - current_--; - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(current_ - begin_ - 1); - currentValue().setOffsetLimit(current_ - begin_); - break; - } // Else, fall through... - default: - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return addError("Syntax error: value, object or array expected.", token); - } - - if (collectComments_) { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - --stackDepth_g; - return successful; -} - -void Reader::skipCommentTokens(Token& token) { - if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); - } -} - -bool Reader::readToken(Token& token) { - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch (c) { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - token.type_ = tokenNumber; - readNumber(); - break; - case 't': - token.type_ = tokenTrue; - ok = match("rue", 3); - break; - case 'f': - token.type_ = tokenFalse; - ok = match("alse", 4); - break; - case 'n': - token.type_ = tokenNull; - ok = match("ull", 3); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if (!ok) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - -void Reader::skipSpaces() { - while (current_ != end_) { - Char c = *current_; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') - ++current_; - else - break; - } -} - -bool Reader::match(Location pattern, int patternLength) { - if (end_ - current_ < patternLength) - return false; - int index = patternLength; - while (index--) - if (current_[index] != pattern[index]) - return false; - current_ += patternLength; - return true; -} - -bool Reader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') - successful = readCppStyleComment(); - if (!successful) - return false; - - if (collectComments_) { - CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; - } - - addComment(commentBegin, current_, placement); - } - return true; -} - -static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { - std::string normalized; - normalized.reserve(end - begin); - Reader::Location current = begin; - while (current != end) { - char c = *current++; - if (c == '\r') { - if (current != end && *current == '\n') - // convert dos EOL - ++current; - // convert Mac EOL - normalized += '\n'; - } else { - normalized += c; - } - } - return normalized; -} - -void -Reader::addComment(Location begin, Location end, CommentPlacement placement) { - assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); - if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); - lastValue_->setComment(normalized, placement); - } else { - commentsBefore_ += normalized; - } -} - -bool Reader::readCStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '*' && *current_ == '/') - break; - } - return getNextChar() == '/'; -} - -bool Reader::readCppStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '\n') - break; - if (c == '\r') { - // Consume DOS EOL. It will be normalized in addComment. - if (current_ != end_ && *current_ == '\n') - getNextChar(); - // Break on Moc OS 9 EOL. - break; - } - } - return true; -} - -void Reader::readNumber() { - const char *p = current_; - char c = '0'; // stopgap for already consumed character - // integral part - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - // fractional part - if (c == '.') { - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } - // exponential part - if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : 0; - if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } -} - -bool Reader::readString() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '"') - break; - } - return c == '"'; -} - -bool Reader::readObject(Token& tokenStart) { - Token tokenName; - std::string name; - Value init(objectValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - while (readToken(tokenName)) { - bool initialTokenOk = true; - while (tokenName.type_ == tokenComment && initialTokenOk) - initialTokenOk = readToken(tokenName); - if (!initialTokenOk) - break; - if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object - return true; - name = ""; - if (tokenName.type_ == tokenString) { - if (!decodeString(tokenName, name)) - return recoverFromError(tokenObjectEnd); - } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; - if (!decodeNumber(tokenName, numberName)) - return recoverFromError(tokenObjectEnd); - name = numberName.asString(); - } else { - break; - } - - Token colon; - if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); - } - Value& value = currentValue()[name]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenObjectEnd); - - Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); - } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); - if (comma.type_ == tokenObjectEnd) - return true; - } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); -} - -bool Reader::readArray(Token& tokenStart) { - Value init(arrayValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - skipSpaces(); - if (*current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } - int index = 0; - for (;;) { - Value& value = currentValue()[index++]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenArrayEnd); - - Token token; - // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); - } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); - if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); - } - if (token.type_ == tokenArrayEnd) - break; - } - return true; -} - -bool Reader::decodeNumber(Token& token) { - Value decoded; - if (!decodeNumber(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeNumber(Token& token, Value& decoded) { - // Attempts to parse the number as an integer. If the number is - // larger than the maximum supported value of an integer then - // we decode the number as a double. - Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) - ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. - Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; - Value::LargestUInt value = 0; - while (current < token.end_) { - Char c = *current++; - if (c < '0' || c > '9') - return decodeDouble(token, decoded); - Value::UInt digit(c - '0'); - if (value >= threshold) { - // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and - // c) it's small enough to fit in that rounding delta, we're okay. - // Otherwise treat this number as a double to avoid overflow. - if (value > threshold || current != token.end_ || - digit > maxIntegerValue % 10) { - return decodeDouble(token, decoded); - } - } - value = value * 10 + digit; - } - if (isNegative && value == maxIntegerValue) - decoded = Value::minLargestInt; - else if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); - else - decoded = value; - return true; -} - -bool Reader::decodeDouble(Token& token) { - Value decoded; - if (!decodeDouble(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeDouble(Token& token, Value& decoded) { - double value = 0; - std::string buffer(token.start_, token.end_); - std::istringstream is(buffer); - if (!(is >> value)) - return addError("'" + std::string(token.start_, token.end_) + - "' is not a number.", - token); - decoded = value; - return true; -} - -bool Reader::decodeString(Token& token) { - std::string decoded_string; - if (!decodeString(token, decoded_string)) - return false; - Value decoded(decoded_string); - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool Reader::decodeString(Token& token, std::string& decoded) { - decoded.reserve(token.end_ - token.start_ - 2); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while (current != end) { - Char c = *current++; - if (c == '"') - break; - else if (c == '\\') { - if (current == end) - return addError("Empty escape sequence in string", token, current); - Char escape = *current++; - switch (escape) { - case '"': - decoded += '"'; - break; - case '/': - decoded += '/'; - break; - case '\\': - decoded += '\\'; - break; - case 'b': - decoded += '\b'; - break; - case 'f': - decoded += '\f'; - break; - case 'n': - decoded += '\n'; - break; - case 'r': - decoded += '\r'; - break; - case 't': - decoded += '\t'; - break; - case 'u': { - unsigned int unicode; - if (!decodeUnicodeCodePoint(token, current, end, unicode)) - return false; - decoded += codePointToUTF8(unicode); - } break; - default: - return addError("Bad escape sequence in string", token, current); - } - } else { - decoded += c; - } - } - return true; -} - -bool Reader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - - if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) { - // surrogate pairs - if (end - current < 6) - return addError( - "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++) == 'u') { - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } else - return false; - } else - return addError("expecting another \\u token to begin the second half of " - "a unicode surrogate pair", - token, - current); - } - return true; -} - -bool Reader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - if (end - current < 4) - return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, - current); - unicode = 0; - for (int index = 0; index < 4; ++index) { - Char c = *current++; - unicode *= 16; - if (c >= '0' && c <= '9') - unicode += c - '0'; - else if (c >= 'a' && c <= 'f') - unicode += c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - unicode += c - 'A' + 10; - else - return addError( - "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); - } - return true; -} - -bool -Reader::addError(const std::string& message, Token& token, Location extra) { - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back(info); - return false; -} - -bool Reader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); - Token skip; - for (;;) { - if (!readToken(skip)) - errors_.resize(errorCount); // discard errors caused by recovery - if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) - break; - } - errors_.resize(errorCount); - return false; -} - -bool Reader::addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken) { - addError(message, token); - return recoverFromError(skipUntilToken); -} - -Value& Reader::currentValue() { return *(nodes_.top()); } - -Reader::Char Reader::getNextChar() { - if (current_ == end_) - return 0; - return *current_++; -} - -void Reader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { - Location current = begin_; - Location lastLineStart = current; - line = 0; - while (current < location && current != end_) { - Char c = *current++; - if (c == '\r') { - if (*current == '\n') - ++current; - lastLineStart = current; - ++line; - } else if (c == '\n') { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - -std::string Reader::getLocationLineAndColumn(Location location) const { - int line, column; - getLocationLineAndColumn(location, line, column); - char buffer[18 + 16 + 16 + 1]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) -#if defined(WINCE) - _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#else - sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#endif -#else - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#endif - return buffer; -} - -// Deprecated. Preserved for backward compatibility -std::string Reader::getFormatedErrorMessages() const { - return getFormattedErrorMessages(); -} - -std::string Reader::getFormattedErrorMessages() const { - std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - formattedMessage += - "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) - formattedMessage += - "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; - } - return formattedMessage; -} - -std::vector Reader::getStructuredErrors() const { - std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - Reader::StructuredError structured; - structured.offset_start = error.token_.start_ - begin_; - structured.offset_limit = error.token_.end_ - begin_; - structured.message = error.message_; - allErrors.push_back(structured); - } - return allErrors; -} - -bool Reader::pushError(const Value& value, const std::string& message) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = 0; - errors_.push_back(info); - return true; -} - -bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = begin_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = begin_ + extra.getOffsetStart(); - errors_.push_back(info); - return true; -} - -bool Reader::good() const { - return !errors_.size(); -} - -// exact copy of Features -class OurFeatures { -public: - static OurFeatures all(); - OurFeatures(); - bool allowComments_; - bool strictRoot_; - bool allowDroppedNullPlaceholders_; - bool allowNumericKeys_; - bool allowSingleQuotes_; - bool failIfExtra_; - bool rejectDupKeys_; - int stackLimit_; -}; // OurFeatures - -// exact copy of Implementation of class Features -// //////////////////////////////// - -OurFeatures::OurFeatures() - : allowComments_(true), strictRoot_(false) - , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) - , allowSingleQuotes_(false) - , failIfExtra_(false) -{ -} - -OurFeatures OurFeatures::all() { return OurFeatures(); } - -// Implementation of class Reader -// //////////////////////////////// - -// exact copy of Reader, renamed to OurReader -class OurReader { -public: - typedef char Char; - typedef const Char* Location; - struct StructuredError { - size_t offset_start; - size_t offset_limit; - std::string message; - }; - - OurReader(OurFeatures const& features); - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments = true); - std::string getFormattedErrorMessages() const; - std::vector getStructuredErrors() const; - bool pushError(const Value& value, const std::string& message); - bool pushError(const Value& value, const std::string& message, const Value& extra); - bool good() const; - -private: - OurReader(OurReader const&); // no impl - void operator=(OurReader const&); // no impl - - enum TokenType { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo { - public: - Token token_; - std::string message_; - Location extra_; - }; - - typedef std::deque Errors; - - bool readToken(Token& token); - void skipSpaces(); - bool match(Location pattern, int patternLength); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - bool readStringSingleQuote(); - void readNumber(); - bool readValue(); - bool readObject(Token& token); - bool readArray(Token& token); - bool decodeNumber(Token& token); - bool decodeNumber(Token& token, Value& decoded); - bool decodeString(Token& token); - bool decodeString(Token& token, std::string& decoded); - bool decodeDouble(Token& token); - bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const std::string& message, Token& token, Location extra = 0); - bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken); - void skipUntilSpace(); - Value& currentValue(); - Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - std::string getLocationLineAndColumn(Location location) const; - void addComment(Location begin, Location end, CommentPlacement placement); - void skipCommentTokens(Token& token); - - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - std::string commentsBefore_; - int stackDepth_; - - OurFeatures const features_; - bool collectComments_; -}; // OurReader - -// complete copy of Read impl, for OurReader - -OurReader::OurReader(OurFeatures const& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} - -bool OurReader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { - if (!features_.allowComments_) { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; - errors_.clear(); - while (!nodes_.empty()) - nodes_.pop(); - nodes_.push(&root); - - stackDepth_ = 0; - bool successful = readValue(); - Token token; - skipCommentTokens(token); - if (features_.failIfExtra_) { - if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { - addError("Extra non-whitespace after JSON value.", token); - return false; - } - } - if (collectComments_ && !commentsBefore_.empty()) - root.setComment(commentsBefore_, commentAfter); - if (features_.strictRoot_) { - if (!root.isArray() && !root.isObject()) { - // Set error location to start of doc, ideally should be first token found - // in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( - "A valid JSON document must be either an array or an object value.", - token); - return false; - } - } - return successful; -} - -bool OurReader::readValue() { - if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); - ++stackDepth_; - Token token; - skipCommentTokens(token); - bool successful = true; - - if (collectComments_ && !commentsBefore_.empty()) { - currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_ = ""; - } - - switch (token.type_) { - case tokenObjectBegin: - successful = readObject(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenArrayBegin: - successful = readArray(token); - currentValue().setOffsetLimit(current_ - begin_); - break; - case tokenNumber: - successful = decodeNumber(token); - break; - case tokenString: - successful = decodeString(token); - break; - case tokenTrue: - { - Value v(true); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { - Value v(false); - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenArraySeparator: - case tokenObjectEnd: - case tokenArrayEnd: - if (features_.allowDroppedNullPlaceholders_) { - // "Un-read" the current token and mark the current value as a null - // token. - current_--; - Value v; - currentValue().swapPayload(v); - currentValue().setOffsetStart(current_ - begin_ - 1); - currentValue().setOffsetLimit(current_ - begin_); - break; - } // else, fall through ... - default: - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return addError("Syntax error: value, object or array expected.", token); - } - - if (collectComments_) { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - --stackDepth_; - return successful; -} - -void OurReader::skipCommentTokens(Token& token) { - if (features_.allowComments_) { - do { - readToken(token); - } while (token.type_ == tokenComment); - } else { - readToken(token); - } -} - -bool OurReader::readToken(Token& token) { - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch (c) { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '\'': - if (features_.allowSingleQuotes_) { - token.type_ = tokenString; - ok = readStringSingleQuote(); - break; - } // else continue - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - token.type_ = tokenNumber; - readNumber(); - break; - case 't': - token.type_ = tokenTrue; - ok = match("rue", 3); - break; - case 'f': - token.type_ = tokenFalse; - ok = match("alse", 4); - break; - case 'n': - token.type_ = tokenNull; - ok = match("ull", 3); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if (!ok) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - -void OurReader::skipSpaces() { - while (current_ != end_) { - Char c = *current_; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') - ++current_; - else - break; - } -} - -bool OurReader::match(Location pattern, int patternLength) { - if (end_ - current_ < patternLength) - return false; - int index = patternLength; - while (index--) - if (current_[index] != pattern[index]) - return false; - current_ += patternLength; - return true; -} - -bool OurReader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') - successful = readCppStyleComment(); - if (!successful) - return false; - - if (collectComments_) { - CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; - } - - addComment(commentBegin, current_, placement); - } - return true; -} - -void -OurReader::addComment(Location begin, Location end, CommentPlacement placement) { - assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); - if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); - lastValue_->setComment(normalized, placement); - } else { - commentsBefore_ += normalized; - } -} - -bool OurReader::readCStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '*' && *current_ == '/') - break; - } - return getNextChar() == '/'; -} - -bool OurReader::readCppStyleComment() { - while (current_ != end_) { - Char c = getNextChar(); - if (c == '\n') - break; - if (c == '\r') { - // Consume DOS EOL. It will be normalized in addComment. - if (current_ != end_ && *current_ == '\n') - getNextChar(); - // Break on Moc OS 9 EOL. - break; - } - } - return true; -} - -void OurReader::readNumber() { - const char *p = current_; - char c = '0'; // stopgap for already consumed character - // integral part - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - // fractional part - if (c == '.') { - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } - // exponential part - if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : 0; - if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : 0; - while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; - } -} -bool OurReader::readString() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '"') - break; - } - return c == '"'; -} - - -bool OurReader::readStringSingleQuote() { - Char c = 0; - while (current_ != end_) { - c = getNextChar(); - if (c == '\\') - getNextChar(); - else if (c == '\'') - break; - } - return c == '\''; -} - -bool OurReader::readObject(Token& tokenStart) { - Token tokenName; - std::string name; - Value init(objectValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - while (readToken(tokenName)) { - bool initialTokenOk = true; - while (tokenName.type_ == tokenComment && initialTokenOk) - initialTokenOk = readToken(tokenName); - if (!initialTokenOk) - break; - if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object - return true; - name = ""; - if (tokenName.type_ == tokenString) { - if (!decodeString(tokenName, name)) - return recoverFromError(tokenObjectEnd); - } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { - Value numberName; - if (!decodeNumber(tokenName, numberName)) - return recoverFromError(tokenObjectEnd); - name = numberName.asString(); - } else { - break; - } - - Token colon; - if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); - } - if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); - if (features_.rejectDupKeys_ && currentValue().isMember(name)) { - std::string msg = "Duplicate key: '" + name + "'"; - return addErrorAndRecover( - msg, tokenName, tokenObjectEnd); - } - Value& value = currentValue()[name]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenObjectEnd); - - Token comma; - if (!readToken(comma) || - (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); - } - bool finalizeTokenOk = true; - while (comma.type_ == tokenComment && finalizeTokenOk) - finalizeTokenOk = readToken(comma); - if (comma.type_ == tokenObjectEnd) - return true; - } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); -} - -bool OurReader::readArray(Token& tokenStart) { - Value init(arrayValue); - currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - skipSpaces(); - if (*current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } - int index = 0; - for (;;) { - Value& value = currentValue()[index++]; - nodes_.push(&value); - bool ok = readValue(); - nodes_.pop(); - if (!ok) // error already set - return recoverFromError(tokenArrayEnd); - - Token token; - // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); - } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); - if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); - } - if (token.type_ == tokenArrayEnd) - break; - } - return true; -} - -bool OurReader::decodeNumber(Token& token) { - Value decoded; - if (!decodeNumber(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeNumber(Token& token, Value& decoded) { - // Attempts to parse the number as an integer. If the number is - // larger than the maximum supported value of an integer then - // we decode the number as a double. - Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) - ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. - Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(-Value::minLargestInt) - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; - Value::LargestUInt value = 0; - while (current < token.end_) { - Char c = *current++; - if (c < '0' || c > '9') - return decodeDouble(token, decoded); - Value::UInt digit(c - '0'); - if (value >= threshold) { - // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and - // c) it's small enough to fit in that rounding delta, we're okay. - // Otherwise treat this number as a double to avoid overflow. - if (value > threshold || current != token.end_ || - digit > maxIntegerValue % 10) { - return decodeDouble(token, decoded); - } - } - value = value * 10 + digit; - } - if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) - decoded = Value::LargestInt(value); - else - decoded = value; - return true; -} - -bool OurReader::decodeDouble(Token& token) { - Value decoded; - if (!decodeDouble(token, decoded)) - return false; - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeDouble(Token& token, Value& decoded) { - double value = 0; - const int bufferSize = 32; - int count; - int length = int(token.end_ - token.start_); - - // Sanity check to avoid buffer overflow exploits. - if (length < 0) { - return addError("Unable to parse token length", token); - } - - // Avoid using a string constant for the format control string given to - // sscanf, as this can cause hard to debug crashes on OS X. See here for more - // info: - // - // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html - char format[] = "%lf"; - - if (length <= bufferSize) { - Char buffer[bufferSize + 1]; - memcpy(buffer, token.start_, length); - buffer[length] = 0; - count = sscanf(buffer, format, &value); - } else { - std::string buffer(token.start_, token.end_); - count = sscanf(buffer.c_str(), format, &value); - } - - if (count != 1) - return addError("'" + std::string(token.start_, token.end_) + - "' is not a number.", - token); - decoded = value; - return true; -} - -bool OurReader::decodeString(Token& token) { - std::string decoded_string; - if (!decodeString(token, decoded_string)) - return false; - Value decoded(decoded_string); - currentValue().swapPayload(decoded); - currentValue().setOffsetStart(token.start_ - begin_); - currentValue().setOffsetLimit(token.end_ - begin_); - return true; -} - -bool OurReader::decodeString(Token& token, std::string& decoded) { - decoded.reserve(token.end_ - token.start_ - 2); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while (current != end) { - Char c = *current++; - if (c == '"') - break; - else if (c == '\\') { - if (current == end) - return addError("Empty escape sequence in string", token, current); - Char escape = *current++; - switch (escape) { - case '"': - decoded += '"'; - break; - case '/': - decoded += '/'; - break; - case '\\': - decoded += '\\'; - break; - case 'b': - decoded += '\b'; - break; - case 'f': - decoded += '\f'; - break; - case 'n': - decoded += '\n'; - break; - case 'r': - decoded += '\r'; - break; - case 't': - decoded += '\t'; - break; - case 'u': { - unsigned int unicode; - if (!decodeUnicodeCodePoint(token, current, end, unicode)) - return false; - decoded += codePointToUTF8(unicode); - } break; - default: - return addError("Bad escape sequence in string", token, current); - } - } else { - decoded += c; - } - } - return true; -} - -bool OurReader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - - if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) { - // surrogate pairs - if (end - current < 6) - return addError( - "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++) == 'u') { - if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } else - return false; - } else - return addError("expecting another \\u token to begin the second half of " - "a unicode surrogate pair", - token, - current); - } - return true; -} - -bool OurReader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode) { - if (end - current < 4) - return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, - current); - unicode = 0; - for (int index = 0; index < 4; ++index) { - Char c = *current++; - unicode *= 16; - if (c >= '0' && c <= '9') - unicode += c - '0'; - else if (c >= 'a' && c <= 'f') - unicode += c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - unicode += c - 'A' + 10; - else - return addError( - "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); - } - return true; -} - -bool -OurReader::addError(const std::string& message, Token& token, Location extra) { - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back(info); - return false; -} - -bool OurReader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); - Token skip; - for (;;) { - if (!readToken(skip)) - errors_.resize(errorCount); // discard errors caused by recovery - if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) - break; - } - errors_.resize(errorCount); - return false; -} - -bool OurReader::addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken) { - addError(message, token); - return recoverFromError(skipUntilToken); -} - -Value& OurReader::currentValue() { return *(nodes_.top()); } - -OurReader::Char OurReader::getNextChar() { - if (current_ == end_) - return 0; - return *current_++; -} - -void OurReader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { - Location current = begin_; - Location lastLineStart = current; - line = 0; - while (current < location && current != end_) { - Char c = *current++; - if (c == '\r') { - if (*current == '\n') - ++current; - lastLineStart = current; - ++line; - } else if (c == '\n') { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - -std::string OurReader::getLocationLineAndColumn(Location location) const { - int line, column; - getLocationLineAndColumn(location, line, column); - char buffer[18 + 16 + 16 + 1]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) -#if defined(WINCE) - _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#else - sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#endif -#else - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#endif - return buffer; -} - -std::string OurReader::getFormattedErrorMessages() const { - std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - formattedMessage += - "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) - formattedMessage += - "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; - } - return formattedMessage; -} - -std::vector OurReader::getStructuredErrors() const { - std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; - OurReader::StructuredError structured; - structured.offset_start = error.token_.start_ - begin_; - structured.offset_limit = error.token_.end_ - begin_; - structured.message = error.message_; - allErrors.push_back(structured); - } - return allErrors; -} - -bool OurReader::pushError(const Value& value, const std::string& message) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = 0; - errors_.push_back(info); - return true; -} - -bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = begin_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = begin_ + extra.getOffsetStart(); - errors_.push_back(info); - return true; -} - -bool OurReader::good() const { - return !errors_.size(); -} - - -class OurCharReader : public CharReader { - bool const collectComments_; - OurReader reader_; -public: - OurCharReader( - bool collectComments, - OurFeatures const& features) - : collectComments_(collectComments) - , reader_(features) - {} - virtual bool parse( - char const* beginDoc, char const* endDoc, - Value* root, std::string* errs) { - bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); - if (errs) { - *errs = reader_.getFormattedErrorMessages(); - } - return ok; - } -}; - -CharReaderBuilder::CharReaderBuilder() -{ - setDefaults(&settings_); -} -CharReaderBuilder::~CharReaderBuilder() -{} -CharReader* CharReaderBuilder::newCharReader() const -{ - bool collectComments = settings_["collectComments"].asBool(); - OurFeatures features = OurFeatures::all(); - features.allowComments_ = settings_["allowComments"].asBool(); - features.strictRoot_ = settings_["strictRoot"].asBool(); - features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); - features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); - features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); - features.stackLimit_ = settings_["stackLimit"].asInt(); - features.failIfExtra_ = settings_["failIfExtra"].asBool(); - features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); - return new OurCharReader(collectComments, features); -} -static void getValidReaderKeys(std::set* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("collectComments"); - valid_keys->insert("allowComments"); - valid_keys->insert("strictRoot"); - valid_keys->insert("allowDroppedNullPlaceholders"); - valid_keys->insert("allowNumericKeys"); - valid_keys->insert("allowSingleQuotes"); - valid_keys->insert("stackLimit"); - valid_keys->insert("failIfExtra"); - valid_keys->insert("rejectDupKeys"); -} -bool CharReaderBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set valid_keys; - getValidReaderKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - std::string const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } - } - return 0u == inv.size(); -} -Value& CharReaderBuilder::operator[](std::string key) -{ - return settings_[key]; -} -// static -void CharReaderBuilder::strictMode(Json::Value* settings) -{ -//! [CharReaderBuilderStrictMode] - (*settings)["allowComments"] = false; - (*settings)["strictRoot"] = true; - (*settings)["allowDroppedNullPlaceholders"] = false; - (*settings)["allowNumericKeys"] = false; - (*settings)["allowSingleQuotes"] = false; - (*settings)["failIfExtra"] = true; - (*settings)["rejectDupKeys"] = true; -//! [CharReaderBuilderStrictMode] -} -// static -void CharReaderBuilder::setDefaults(Json::Value* settings) -{ -//! [CharReaderBuilderDefaults] - (*settings)["collectComments"] = true; - (*settings)["allowComments"] = true; - (*settings)["strictRoot"] = false; - (*settings)["allowDroppedNullPlaceholders"] = false; - (*settings)["allowNumericKeys"] = false; - (*settings)["allowSingleQuotes"] = false; - (*settings)["stackLimit"] = 1000; - (*settings)["failIfExtra"] = false; - (*settings)["rejectDupKeys"] = false; -//! [CharReaderBuilderDefaults] -} - -////////////////////////////////// -// global functions - -bool parseFromStream( - CharReader::Factory const& fact, std::istream& sin, - Value* root, std::string* errs) -{ - std::ostringstream ssin; - ssin << sin.rdbuf(); - std::string doc = ssin.str(); - char const* begin = doc.data(); - char const* end = begin + doc.size(); - // Note that we do not actually need a null-terminator. - CharReaderPtr const reader(fact.newCharReader()); - return reader->parse(begin, end, root, errs); -} - -std::istream& operator>>(std::istream& sin, Value& root) { - CharReaderBuilder b; - std::string errs; - bool ok = parseFromStream(b, sin, &root, &errs); - if (!ok) { - fprintf(stderr, - "Error from reader: %s", - errs.c_str()); - - throwRuntimeError("reader error"); - } - return sin; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_reader.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_valueiterator.inl -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp - -namespace Json { - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIteratorBase -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIteratorBase::ValueIteratorBase() - : current_(), isNull_(true) { -} - -ValueIteratorBase::ValueIteratorBase( - const Value::ObjectValues::iterator& current) - : current_(current), isNull_(false) {} - -Value& ValueIteratorBase::deref() const { - return current_->second; -} - -void ValueIteratorBase::increment() { - ++current_; -} - -void ValueIteratorBase::decrement() { - --current_; -} - -ValueIteratorBase::difference_type -ValueIteratorBase::computeDistance(const SelfType& other) const { -#ifdef JSON_USE_CPPTL_SMALLMAP - return other.current_ - current_; -#else - // Iterator for null value are initialized using the default - // constructor, which initialize current_ to the default - // std::map::iterator. As begin() and end() are two instance - // of the default std::map::iterator, they can not be compared. - // To allow this, we handle this comparison specifically. - if (isNull_ && other.isNull_) { - return 0; - } - - // Usage of std::distance is not portable (does not compile with Sun Studio 12 - // RogueWave STL, - // which is the one used by default). - // Using a portable hand-made version for non random iterator instead: - // return difference_type( std::distance( current_, other.current_ ) ); - difference_type myDistance = 0; - for (Value::ObjectValues::iterator it = current_; it != other.current_; - ++it) { - ++myDistance; - } - return myDistance; -#endif -} - -bool ValueIteratorBase::isEqual(const SelfType& other) const { - if (isNull_) { - return other.isNull_; - } - return current_ == other.current_; -} - -void ValueIteratorBase::copy(const SelfType& other) { - current_ = other.current_; - isNull_ = other.isNull_; -} - -Value ValueIteratorBase::key() const { - const Value::CZString czstring = (*current_).first; - if (czstring.data()) { - if (czstring.isStaticString()) - return Value(StaticString(czstring.data())); - return Value(czstring.data(), czstring.data() + czstring.length()); - } - return Value(czstring.index()); -} - -UInt ValueIteratorBase::index() const { - const Value::CZString czstring = (*current_).first; - if (!czstring.data()) - return czstring.index(); - return Value::UInt(-1); -} - -std::string ValueIteratorBase::name() const { - char const* keey; - char const* end; - keey = memberName(&end); - if (!keey) return std::string(); - return std::string(keey, end); -} - -char const* ValueIteratorBase::memberName() const { - const char* cname = (*current_).first.data(); - return cname ? cname : ""; -} - -char const* ValueIteratorBase::memberName(char const** end) const { - const char* cname = (*current_).first.data(); - if (!cname) { - *end = NULL; - return NULL; - } - *end = cname + (*current_).first.length(); - return cname; -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueConstIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueConstIterator::ValueConstIterator() {} - -ValueConstIterator::ValueConstIterator( - const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} - -ValueConstIterator& ValueConstIterator:: -operator=(const ValueIteratorBase& other) { - copy(other); - return *this; -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIterator::ValueIterator() {} - -ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) - : ValueIteratorBase(current) {} - -ValueIterator::ValueIterator(const ValueConstIterator& other) - : ValueIteratorBase(other) {} - -ValueIterator::ValueIterator(const ValueIterator& other) - : ValueIteratorBase(other) {} - -ValueIterator& ValueIterator::operator=(const SelfType& other) { - copy(other); - return *this; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_valueiterator.inl -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_value.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2011 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include -#ifdef JSON_USE_CPPTL -#include -#endif -#include // size_t -#include // min() - -#define JSON_ASSERT_UNREACHABLE assert(false) - -namespace Json { - -// This is a walkaround to avoid the static initialization of Value::null. -// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of -// 8 (instead of 4) as a bit of future-proofing. -#if defined(__ARMEL__) -#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) -#else -#define ALIGNAS(byte_alignment) -#endif -static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; -const unsigned char& kNullRef = kNull[0]; -const Value& Value::null = reinterpret_cast(kNullRef); -const Value& Value::nullRef = null; - -const Int Value::minInt = Int(~(UInt(-1) / 2)); -const Int Value::maxInt = Int(UInt(-1) / 2); -const UInt Value::maxUInt = UInt(-1); -#if defined(JSON_HAS_INT64) -const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); -const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); -const UInt64 Value::maxUInt64 = UInt64(-1); -// The constant is hard-coded because some compiler have trouble -// converting Value::maxUInt64 to a double correctly (AIX/xlC). -// Assumes that UInt64 is a 64 bits integer. -static const double maxUInt64AsDouble = 18446744073709551615.0; -#endif // defined(JSON_HAS_INT64) -const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); -const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); -const LargestUInt Value::maxLargestUInt = LargestUInt(-1); - -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -template -static inline bool InRange(double d, T min, U max) { - return d >= min && d <= max; -} -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -static inline double integerToDouble(Json::UInt64 value) { - return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); -} - -template static inline double integerToDouble(T value) { - return static_cast(value); -} - -template -static inline bool InRange(double d, T min, U max) { - return d >= integerToDouble(min) && d <= integerToDouble(max); -} -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - -/** Duplicates the specified string value. - * @param value Pointer to the string to duplicate. Must be zero-terminated if - * length is "unknown". - * @param length Length of the value. if equals to unknown, then it will be - * computed using strlen(value). - * @return Pointer on the duplicate instance of string. - */ -static inline char* duplicateStringValue(const char* value, - size_t length) { - // Avoid an integer overflow in the call to malloc below by limiting length - // to a sane value. - if (length >= (size_t)Value::maxInt) - length = Value::maxInt - 1; - - char* newString = static_cast(malloc(length + 1)); - if (newString == NULL) { - throwRuntimeError( - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); - } - memcpy(newString, value, length); - newString[length] = 0; - return newString; -} - -/* Record the length as a prefix. - */ -static inline char* duplicateAndPrefixStringValue( - const char* value, - unsigned int length) -{ - // Avoid an integer overflow in the call to malloc below by limiting length - // to a sane value. - JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, - "in Json::Value::duplicateAndPrefixStringValue(): " - "length too big for prefixing"); - unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; - char* newString = static_cast(malloc(actualLength)); - if (newString == 0) { - throwRuntimeError( - "in Json::Value::duplicateAndPrefixStringValue(): " - "Failed to allocate string value buffer"); - } - *reinterpret_cast(newString) = length; - memcpy(newString + sizeof(unsigned), value, length); - newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later - return newString; -} -inline static void decodePrefixedString( - bool isPrefixed, char const* prefixed, - unsigned* length, char const** value) -{ - if (!isPrefixed) { - *length = static_cast(strlen(prefixed)); - *value = prefixed; - } else { - *length = *reinterpret_cast(prefixed); - *value = prefixed + sizeof(unsigned); - } -} -/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). - */ -static inline void releaseStringValue(char* value) { free(value); } - -} // namespace Json - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ValueInternals... -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -#if !defined(JSON_IS_AMALGAMATION) - -#include "json_valueiterator.inl" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - -Exception::Exception(std::string const& msg) - : msg_(msg) -{} -Exception::~Exception() throw() -{} -char const* Exception::what() const throw() -{ - return msg_.c_str(); -} -RuntimeError::RuntimeError(std::string const& msg) - : Exception(msg) -{} -LogicError::LogicError(std::string const& msg) - : Exception(msg) -{} -void throwRuntimeError(std::string const& msg) -{ - throw RuntimeError(msg); -} -void throwLogicError(std::string const& msg) -{ - throw LogicError(msg); -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -Value::CommentInfo::CommentInfo() : comment_(0) {} - -Value::CommentInfo::~CommentInfo() { - if (comment_) - releaseStringValue(comment_); -} - -void Value::CommentInfo::setComment(const char* text, size_t len) { - if (comment_) { - releaseStringValue(comment_); - comment_ = 0; - } - JSON_ASSERT(text != 0); - JSON_ASSERT_MESSAGE( - text[0] == '\0' || text[0] == '/', - "in Json::Value::setComment(): Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue(text, len); -} - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CZString -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -// Notes: policy_ indicates if the string was allocated when -// a string is stored. - -Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} - -Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) - : cstr_(str) -{ - // allocate != duplicate - storage_.policy_ = allocate & 0x3; - storage_.length_ = ulength & 0x3FFFFFFF; -} - -Value::CZString::CZString(const CZString& other) - : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue(other.cstr_, other.storage_.length_) - : other.cstr_) -{ - storage_.policy_ = (other.cstr_ - ? (static_cast(other.storage_.policy_) == noDuplication - ? noDuplication : duplicate) - : static_cast(other.storage_.policy_)); - storage_.length_ = other.storage_.length_; -} - -Value::CZString::~CZString() { - if (cstr_ && storage_.policy_ == duplicate) - releaseStringValue(const_cast(cstr_)); -} - -void Value::CZString::swap(CZString& other) { - std::swap(cstr_, other.cstr_); - std::swap(index_, other.index_); -} - -Value::CZString& Value::CZString::operator=(CZString other) { - swap(other); - return *this; -} - -bool Value::CZString::operator<(const CZString& other) const { - if (!cstr_) return index_ < other.index_; - //return strcmp(cstr_, other.cstr_) < 0; - // Assume both are strings. - unsigned this_len = this->storage_.length_; - unsigned other_len = other.storage_.length_; - unsigned min_len = std::min(this_len, other_len); - int comp = memcmp(this->cstr_, other.cstr_, min_len); - if (comp < 0) return true; - if (comp > 0) return false; - return (this_len < other_len); -} - -bool Value::CZString::operator==(const CZString& other) const { - if (!cstr_) return index_ == other.index_; - //return strcmp(cstr_, other.cstr_) == 0; - // Assume both are strings. - unsigned this_len = this->storage_.length_; - unsigned other_len = other.storage_.length_; - if (this_len != other_len) return false; - int comp = memcmp(this->cstr_, other.cstr_, this_len); - return comp == 0; -} - -ArrayIndex Value::CZString::index() const { return index_; } - -//const char* Value::CZString::c_str() const { return cstr_; } -const char* Value::CZString::data() const { return cstr_; } -unsigned Value::CZString::length() const { return storage_.length_; } -bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::Value -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/*! \internal Default constructor initialization must be equivalent to: - * memset( this, 0, sizeof(Value) ) - * This optimization is used in ValueInternalMap fast allocator. - */ -Value::Value(ValueType vtype) { - initBasic(vtype); - switch (vtype) { - case nullValue: - break; - case intValue: - case uintValue: - value_.int_ = 0; - break; - case realValue: - value_.real_ = 0.0; - break; - case stringValue: - value_.string_ = 0; - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(); - break; - case booleanValue: - value_.bool_ = false; - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - -Value::Value(Int value) { - initBasic(intValue); - value_.int_ = value; -} - -Value::Value(UInt value) { - initBasic(uintValue); - value_.uint_ = value; -} -#if defined(JSON_HAS_INT64) -Value::Value(Int64 value) { - initBasic(intValue); - value_.int_ = value; -} -Value::Value(UInt64 value) { - initBasic(uintValue); - value_.uint_ = value; -} -#endif // defined(JSON_HAS_INT64) - -Value::Value(double value) { - initBasic(realValue); - value_.real_ = value; -} - -Value::Value(const char* value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); -} - -Value::Value(const char* beginValue, const char* endValue) { - initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); -} - -Value::Value(const std::string& value) { - initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); -} - -Value::Value(const StaticString& value) { - initBasic(stringValue); - value_.string_ = const_cast(value.c_str()); -} - -#ifdef JSON_USE_CPPTL -Value::Value(const CppTL::ConstString& value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); -} -#endif - -Value::Value(bool value) { - initBasic(booleanValue); - value_.bool_ = value; -} - -Value::Value(Value const& other) - : type_(other.type_), allocated_(false) - , - comments_(0), start_(other.start_), limit_(other.limit_) -{ - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if (other.value_.string_ && other.allocated_) { - unsigned len; - char const* str; - decodePrefixedString(other.allocated_, other.value_.string_, - &len, &str); - value_.string_ = duplicateAndPrefixStringValue(str, len); - allocated_ = true; - } else { - value_.string_ = other.value_.string_; - allocated_ = false; - } - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(*other.value_.map_); - break; - default: - JSON_ASSERT_UNREACHABLE; - } - if (other.comments_) { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { - const CommentInfo& otherComment = other.comments_[comment]; - if (otherComment.comment_) - comments_[comment].setComment( - otherComment.comment_, strlen(otherComment.comment_)); - } - } -} - -Value::~Value() { - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if (allocated_) - releaseStringValue(value_.string_); - break; - case arrayValue: - case objectValue: - delete value_.map_; - break; - default: - JSON_ASSERT_UNREACHABLE; - } - - if (comments_) - delete[] comments_; -} - -Value& Value::operator=(Value other) { - swap(other); - return *this; -} - -void Value::swapPayload(Value& other) { - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; - std::swap(value_, other.value_); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2 & 0x1; -} - -void Value::swap(Value& other) { - swapPayload(other); - std::swap(comments_, other.comments_); - std::swap(start_, other.start_); - std::swap(limit_, other.limit_); -} - -ValueType Value::type() const { return type_; } - -int Value::compare(const Value& other) const { - if (*this < other) - return -1; - if (*this > other) - return 1; - return 0; -} - -bool Value::operator<(const Value& other) const { - int typeDelta = type_ - other.type_; - if (typeDelta) - return typeDelta < 0 ? true : false; - switch (type_) { - case nullValue: - return false; - case intValue: - return value_.int_ < other.value_.int_; - case uintValue: - return value_.uint_ < other.value_.uint_; - case realValue: - return value_.real_ < other.value_.real_; - case booleanValue: - return value_.bool_ < other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - if (other.value_.string_) return true; - else return false; - } - unsigned this_len; - unsigned other_len; - char const* this_str; - char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); - unsigned min_len = std::min(this_len, other_len); - int comp = memcmp(this_str, other_str, min_len); - if (comp < 0) return true; - if (comp > 0) return false; - return (this_len < other_len); - } - case arrayValue: - case objectValue: { - int delta = int(value_.map_->size() - other.value_.map_->size()); - if (delta) - return delta < 0; - return (*value_.map_) < (*other.value_.map_); - } - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool Value::operator<=(const Value& other) const { return !(other < *this); } - -bool Value::operator>=(const Value& other) const { return !(*this < other); } - -bool Value::operator>(const Value& other) const { return other < *this; } - -bool Value::operator==(const Value& other) const { - // if ( type_ != other.type_ ) - // GCC 2.95.3 says: - // attempt to take address of bit-field structure member `Json::Value::type_' - // Beats me, but a temp solves the problem. - int temp = other.type_; - if (type_ != temp) - return false; - switch (type_) { - case nullValue: - return true; - case intValue: - return value_.int_ == other.value_.int_; - case uintValue: - return value_.uint_ == other.value_.uint_; - case realValue: - return value_.real_ == other.value_.real_; - case booleanValue: - return value_.bool_ == other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - return (value_.string_ == other.value_.string_); - } - unsigned this_len; - unsigned other_len; - char const* this_str; - char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); - if (this_len != other_len) return false; - int comp = memcmp(this_str, other_str, this_len); - return comp == 0; - } - case arrayValue: - case objectValue: - return value_.map_->size() == other.value_.map_->size() && - (*value_.map_) == (*other.value_.map_); - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool Value::operator!=(const Value& other) const { return !(*this == other); } - -const char* Value::asCString() const { - JSON_ASSERT_MESSAGE(type_ == stringValue, - "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) return 0; - unsigned this_len; - char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - return this_str; -} - -bool Value::getString(char const** str, char const** cend) const { - if (type_ != stringValue) return false; - if (value_.string_ == 0) return false; - unsigned length; - decodePrefixedString(this->allocated_, this->value_.string_, &length, str); - *cend = *str + length; - return true; -} - -std::string Value::asString() const { - switch (type_) { - case nullValue: - return ""; - case stringValue: - { - if (value_.string_ == 0) return ""; - unsigned this_len; - char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - return std::string(this_str, this_len); - } - case booleanValue: - return value_.bool_ ? "true" : "false"; - case intValue: - return valueToString(value_.int_); - case uintValue: - return valueToString(value_.uint_); - case realValue: - return valueToString(value_.real_); - default: - JSON_FAIL_MESSAGE("Type is not convertible to string"); - } -} - -#ifdef JSON_USE_CPPTL -CppTL::ConstString Value::asConstString() const { - unsigned len; - char const* str; - decodePrefixedString(allocated_, value_.string_, - &len, &str); - return CppTL::ConstString(str, len); -} -#endif - -Value::Int Value::asInt() const { - switch (type_) { - case intValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); - return Int(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); - return Int(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), - "double out of Int range"); - return Int(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int."); -} - -Value::UInt Value::asUInt() const { - switch (type_) { - case intValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); - return UInt(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); - return UInt(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), - "double out of UInt range"); - return UInt(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt."); -} - -#if defined(JSON_HAS_INT64) - -Value::Int64 Value::asInt64() const { - switch (type_) { - case intValue: - return Int64(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); - return Int64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), - "double out of Int64 range"); - return Int64(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int64."); -} - -Value::UInt64 Value::asUInt64() const { - switch (type_) { - case intValue: - JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); - return UInt64(value_.int_); - case uintValue: - return UInt64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), - "double out of UInt64 range"); - return UInt64(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); -} -#endif // if defined(JSON_HAS_INT64) - -LargestInt Value::asLargestInt() const { -#if defined(JSON_NO_INT64) - return asInt(); -#else - return asInt64(); -#endif -} - -LargestUInt Value::asLargestUInt() const { -#if defined(JSON_NO_INT64) - return asUInt(); -#else - return asUInt64(); -#endif -} - -double Value::asDouble() const { - switch (type_) { - case intValue: - return static_cast(value_.int_); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast(value_.uint_); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble(value_.uint_); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return value_.real_; - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0 : 0.0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to double."); -} - -float Value::asFloat() const { - switch (type_) { - case intValue: - return static_cast(value_.int_); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast(value_.uint_); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble(value_.uint_); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return static_cast(value_.real_); - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0f : 0.0f; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to float."); -} - -bool Value::asBool() const { - switch (type_) { - case booleanValue: - return value_.bool_; - case nullValue: - return false; - case intValue: - return value_.int_ ? true : false; - case uintValue: - return value_.uint_ ? true : false; - case realValue: - // This is kind of strange. Not recommended. - return (value_.real_ != 0.0) ? true : false; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to bool."); -} - -bool Value::isConvertibleTo(ValueType other) const { - switch (other) { - case nullValue: - return (isNumeric() && asDouble() == 0.0) || - (type_ == booleanValue && value_.bool_ == false) || - (type_ == stringValue && asString() == "") || - (type_ == arrayValue && value_.map_->size() == 0) || - (type_ == objectValue && value_.map_->size() == 0) || - type_ == nullValue; - case intValue: - return isInt() || - (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || - type_ == booleanValue || type_ == nullValue; - case uintValue: - return isUInt() || - (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || - type_ == booleanValue || type_ == nullValue; - case realValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; - case booleanValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; - case stringValue: - return isNumeric() || type_ == booleanValue || type_ == stringValue || - type_ == nullValue; - case arrayValue: - return type_ == arrayValue || type_ == nullValue; - case objectValue: - return type_ == objectValue || type_ == nullValue; - } - JSON_ASSERT_UNREACHABLE; - return false; -} - -/// Number of values in array or object -ArrayIndex Value::size() const { - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - case stringValue: - return 0; - case arrayValue: // size of the array is highest index + 1 - if (!value_.map_->empty()) { - ObjectValues::const_iterator itLast = value_.map_->end(); - --itLast; - return (*itLast).first.index() + 1; - } - return 0; - case objectValue: - return ArrayIndex(value_.map_->size()); - } - JSON_ASSERT_UNREACHABLE; - return 0; // unreachable; -} - -bool Value::empty() const { - if (isNull() || isArray() || isObject()) - return size() == 0u; - else - return false; -} - -bool Value::operator!() const { return isNull(); } - -void Value::clear() { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || - type_ == objectValue, - "in Json::Value::clear(): requires complex value"); - start_ = 0; - limit_ = 0; - switch (type_) { - case arrayValue: - case objectValue: - value_.map_->clear(); - break; - default: - break; - } -} - -void Value::resize(ArrayIndex newSize) { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, - "in Json::Value::resize(): requires arrayValue"); - if (type_ == nullValue) - *this = Value(arrayValue); - ArrayIndex oldSize = size(); - if (newSize == 0) - clear(); - else if (newSize > oldSize) - (*this)[newSize - 1]; - else { - for (ArrayIndex index = newSize; index < oldSize; ++index) { - value_.map_->erase(index); - } - assert(size() == newSize); - } -} - -Value& Value::operator[](ArrayIndex index) { - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, - "in Json::Value::operator[](ArrayIndex): requires arrayValue"); - if (type_ == nullValue) - *this = Value(arrayValue); - CZString key(index); - ObjectValues::iterator it = value_.map_->lower_bound(key); - if (it != value_.map_->end() && (*it).first == key) - return (*it).second; - - ObjectValues::value_type defaultValue(key, nullRef); - it = value_.map_->insert(it, defaultValue); - return (*it).second; -} - -Value& Value::operator[](int index) { - JSON_ASSERT_MESSAGE( - index >= 0, - "in Json::Value::operator[](int index): index cannot be negative"); - return (*this)[ArrayIndex(index)]; -} - -const Value& Value::operator[](ArrayIndex index) const { - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, - "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); - if (type_ == nullValue) - return nullRef; - CZString key(index); - ObjectValues::const_iterator it = value_.map_->find(key); - if (it == value_.map_->end()) - return nullRef; - return (*it).second; -} - -const Value& Value::operator[](int index) const { - JSON_ASSERT_MESSAGE( - index >= 0, - "in Json::Value::operator[](int index) const: index cannot be negative"); - return (*this)[ArrayIndex(index)]; -} - -void Value::initBasic(ValueType vtype, bool allocated) { - type_ = vtype; - allocated_ = allocated; - comments_ = 0; - start_ = 0; - limit_ = 0; -} - -// Access an object value by name, create a null member if it does not exist. -// @pre Type of '*this' is object or null. -// @param key is null-terminated. -Value& Value::resolveReference(const char* key) { - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::resolveReference(): requires objectValue"); - if (type_ == nullValue) - *this = Value(objectValue); - CZString actualKey( - key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); - if (it != value_.map_->end() && (*it).first == actualKey) - return (*it).second; - - ObjectValues::value_type defaultValue(actualKey, nullRef); - it = value_.map_->insert(it, defaultValue); - Value& value = (*it).second; - return value; -} - -// @param key is not null-terminated. -Value& Value::resolveReference(char const* key, char const* cend) -{ - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::resolveReference(key, end): requires objectValue"); - if (type_ == nullValue) - *this = Value(objectValue); - CZString actualKey( - key, static_cast(cend-key), CZString::duplicateOnCopy); - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); - if (it != value_.map_->end() && (*it).first == actualKey) - return (*it).second; - - ObjectValues::value_type defaultValue(actualKey, nullRef); - it = value_.map_->insert(it, defaultValue); - Value& value = (*it).second; - return value; -} - -Value Value::get(ArrayIndex index, const Value& defaultValue) const { - const Value* value = &((*this)[index]); - return value == &nullRef ? defaultValue : *value; -} - -bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } - -Value const* Value::find(char const* key, char const* cend) const -{ - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::find(key, end, found): requires objectValue or nullValue"); - if (type_ == nullValue) return NULL; - CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); - ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) return NULL; - return &(*it).second; -} -const Value& Value::operator[](const char* key) const -{ - Value const* found = find(key, key + strlen(key)); - if (!found) return nullRef; - return *found; -} -Value const& Value::operator[](std::string const& key) const -{ - Value const* found = find(key.data(), key.data() + key.length()); - if (!found) return nullRef; - return *found; -} - -Value& Value::operator[](const char* key) { - return resolveReference(key, key + strlen(key)); -} - -Value& Value::operator[](const std::string& key) { - return resolveReference(key.data(), key.data() + key.length()); -} - -Value& Value::operator[](const StaticString& key) { - return resolveReference(key.c_str()); -} - -#ifdef JSON_USE_CPPTL -Value& Value::operator[](const CppTL::ConstString& key) { - return resolveReference(key.c_str(), key.end_c_str()); -} -Value const& Value::operator[](CppTL::ConstString const& key) const -{ - Value const* found = find(key.c_str(), key.end_c_str()); - if (!found) return nullRef; - return *found; -} -#endif - -Value& Value::append(const Value& value) { return (*this)[size()] = value; } - -Value Value::get(char const* key, char const* cend, Value const& defaultValue) const -{ - Value const* found = find(key, cend); - return !found ? defaultValue : *found; -} -Value Value::get(char const* key, Value const& defaultValue) const -{ - return get(key, key + strlen(key), defaultValue); -} -Value Value::get(std::string const& key, Value const& defaultValue) const -{ - return get(key.data(), key.data() + key.length(), defaultValue); -} - - -bool Value::removeMember(const char* key, const char* cend, Value* removed) -{ - if (type_ != objectValue) { - return false; - } - CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) - return false; - *removed = it->second; - value_.map_->erase(it); - return true; -} -bool Value::removeMember(const char* key, Value* removed) -{ - return removeMember(key, key + strlen(key), removed); -} -bool Value::removeMember(std::string const& key, Value* removed) -{ - return removeMember(key.data(), key.data() + key.length(), removed); -} -Value Value::removeMember(const char* key) -{ - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, - "in Json::Value::removeMember(): requires objectValue"); - if (type_ == nullValue) - return nullRef; - - Value removed; // null - removeMember(key, key + strlen(key), &removed); - return removed; // still null if removeMember() did nothing -} -Value Value::removeMember(const std::string& key) -{ - return removeMember(key.c_str()); -} - -bool Value::removeIndex(ArrayIndex index, Value* removed) { - if (type_ != arrayValue) { - return false; - } - CZString key(index); - ObjectValues::iterator it = value_.map_->find(key); - if (it == value_.map_->end()) { - return false; - } - *removed = it->second; - ArrayIndex oldSize = size(); - // shift left all items left, into the place of the "removed" - for (ArrayIndex i = index; i < (oldSize - 1); ++i){ - CZString keey(i); - (*value_.map_)[keey] = (*this)[i + 1]; - } - // erase the last one ("leftover") - CZString keyLast(oldSize - 1); - ObjectValues::iterator itLast = value_.map_->find(keyLast); - value_.map_->erase(itLast); - return true; -} - -#ifdef JSON_USE_CPPTL -Value Value::get(const CppTL::ConstString& key, - const Value& defaultValue) const { - return get(key.c_str(), key.end_c_str(), defaultValue); -} -#endif - -bool Value::isMember(char const* key, char const* cend) const -{ - Value const* value = find(key, cend); - return NULL != value; -} -bool Value::isMember(char const* key) const -{ - return isMember(key, key + strlen(key)); -} -bool Value::isMember(std::string const& key) const -{ - return isMember(key.data(), key.data() + key.length()); -} - -#ifdef JSON_USE_CPPTL -bool Value::isMember(const CppTL::ConstString& key) const { - return isMember(key.c_str(), key.end_c_str()); -} -#endif - -Value::Members Value::getMemberNames() const { - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::getMemberNames(), value must be objectValue"); - if (type_ == nullValue) - return Value::Members(); - Members members; - members.reserve(value_.map_->size()); - ObjectValues::const_iterator it = value_.map_->begin(); - ObjectValues::const_iterator itEnd = value_.map_->end(); - for (; it != itEnd; ++it) { - members.push_back(std::string((*it).first.data(), - (*it).first.length())); - } - return members; -} -// -//# ifdef JSON_USE_CPPTL -// EnumMemberNames -// Value::enumMemberNames() const -//{ -// if ( type_ == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -// EnumValues -// Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type() ); -// return EnumValues(); -//} -// -//# endif - -static bool IsIntegral(double d) { - double integral_part; - return modf(d, &integral_part) == 0.0; -} - -bool Value::isNull() const { return type_ == nullValue; } - -bool Value::isBool() const { return type_ == booleanValue; } - -bool Value::isInt() const { - switch (type_) { - case intValue: - return value_.int_ >= minInt && value_.int_ <= maxInt; - case uintValue: - return value_.uint_ <= UInt(maxInt); - case realValue: - return value_.real_ >= minInt && value_.real_ <= maxInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - -bool Value::isUInt() const { - switch (type_) { - case intValue: - return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); - case uintValue: - return value_.uint_ <= maxUInt; - case realValue: - return value_.real_ >= 0 && value_.real_ <= maxUInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - -bool Value::isInt64() const { -#if defined(JSON_HAS_INT64) - switch (type_) { - case intValue: - return true; - case uintValue: - return value_.uint_ <= UInt64(maxInt64); - case realValue: - // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a - // double, so double(maxInt64) will be rounded up to 2^63. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= double(minInt64) && - value_.real_ < double(maxInt64) && IsIntegral(value_.real_); - default: - break; - } -#endif // JSON_HAS_INT64 - return false; -} - -bool Value::isUInt64() const { -#if defined(JSON_HAS_INT64) - switch (type_) { - case intValue: - return value_.int_ >= 0; - case uintValue: - return true; - case realValue: - // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a - // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && - IsIntegral(value_.real_); - default: - break; - } -#endif // JSON_HAS_INT64 - return false; -} - -bool Value::isIntegral() const { -#if defined(JSON_HAS_INT64) - return isInt64() || isUInt64(); -#else - return isInt() || isUInt(); -#endif -} - -bool Value::isDouble() const { return type_ == realValue || isIntegral(); } - -bool Value::isNumeric() const { return isIntegral() || isDouble(); } - -bool Value::isString() const { return type_ == stringValue; } - -bool Value::isArray() const { return type_ == arrayValue; } - -bool Value::isObject() const { return type_ == objectValue; } - -void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { - if (!comments_) - comments_ = new CommentInfo[numberOfCommentPlacement]; - if ((len > 0) && (comment[len-1] == '\n')) { - // Always discard trailing newline, to aid indentation. - len -= 1; - } - comments_[placement].setComment(comment, len); -} - -void Value::setComment(const char* comment, CommentPlacement placement) { - setComment(comment, strlen(comment), placement); -} - -void Value::setComment(const std::string& comment, CommentPlacement placement) { - setComment(comment.c_str(), comment.length(), placement); -} - -bool Value::hasComment(CommentPlacement placement) const { - return comments_ != 0 && comments_[placement].comment_ != 0; -} - -std::string Value::getComment(CommentPlacement placement) const { - if (hasComment(placement)) - return comments_[placement].comment_; - return ""; -} - -void Value::setOffsetStart(size_t start) { start_ = start; } - -void Value::setOffsetLimit(size_t limit) { limit_ = limit; } - -size_t Value::getOffsetStart() const { return start_; } - -size_t Value::getOffsetLimit() const { return limit_; } - -std::string Value::toStyledString() const { - StyledWriter writer; - return writer.write(*this); -} - -Value::const_iterator Value::begin() const { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator(value_.map_->begin()); - break; - default: - break; - } - return const_iterator(); -} - -Value::const_iterator Value::end() const { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator(value_.map_->end()); - break; - default: - break; - } - return const_iterator(); -} - -Value::iterator Value::begin() { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator(value_.map_->begin()); - break; - default: - break; - } - return iterator(); -} - -Value::iterator Value::end() { - switch (type_) { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator(value_.map_->end()); - break; - default: - break; - } - return iterator(); -} - -// class PathArgument -// ////////////////////////////////////////////////////////////////// - -PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} - -PathArgument::PathArgument(ArrayIndex index) - : key_(), index_(index), kind_(kindIndex) {} - -PathArgument::PathArgument(const char* key) - : key_(key), index_(), kind_(kindKey) {} - -PathArgument::PathArgument(const std::string& key) - : key_(key.c_str()), index_(), kind_(kindKey) {} - -// class Path -// ////////////////////////////////////////////////////////////////// - -Path::Path(const std::string& path, - const PathArgument& a1, - const PathArgument& a2, - const PathArgument& a3, - const PathArgument& a4, - const PathArgument& a5) { - InArgs in; - in.push_back(&a1); - in.push_back(&a2); - in.push_back(&a3); - in.push_back(&a4); - in.push_back(&a5); - makePath(path, in); -} - -void Path::makePath(const std::string& path, const InArgs& in) { - const char* current = path.c_str(); - const char* end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); - while (current != end) { - if (*current == '[') { - ++current; - if (*current == '%') - addPathInArg(path, in, itInArg, PathArgument::kindIndex); - else { - ArrayIndex index = 0; - for (; current != end && *current >= '0' && *current <= '9'; ++current) - index = index * 10 + ArrayIndex(*current - '0'); - args_.push_back(index); - } - if (current == end || *current++ != ']') - invalidPath(path, int(current - path.c_str())); - } else if (*current == '%') { - addPathInArg(path, in, itInArg, PathArgument::kindKey); - ++current; - } else if (*current == '.') { - ++current; - } else { - const char* beginName = current; - while (current != end && !strchr("[.", *current)) - ++current; - args_.push_back(std::string(beginName, current)); - } - } -} - -void Path::addPathInArg(const std::string& /*path*/, - const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind) { - if (itInArg == in.end()) { - // Error: missing argument %d - } else if ((*itInArg)->kind_ != kind) { - // Error: bad argument type - } else { - args_.push_back(**itInArg); - } -} - -void Path::invalidPath(const std::string& /*path*/, int /*location*/) { - // Error: invalid path. -} - -const Value& Path::resolve(const Value& root) const { - const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray() || !node->isValidIndex(arg.index_)) { - // Error: unable to resolve path (array value expected at position... - } - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) { - // Error: unable to resolve path (object value expected at position...) - } - node = &((*node)[arg.key_]); - if (node == &Value::nullRef) { - // Error: unable to resolve path (object has no member named '' at - // position...) - } - } - } - return *node; -} - -Value Path::resolve(const Value& root, const Value& defaultValue) const { - const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray() || !node->isValidIndex(arg.index_)) - return defaultValue; - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) - return defaultValue; - node = &((*node)[arg.key_]); - if (node == &Value::nullRef) - return defaultValue; - } - } - return *node; -} - -Value& Path::make(Value& root) const { - Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; - if (arg.kind_ == PathArgument::kindIndex) { - if (!node->isArray()) { - // Error: node is not an array at position ... - } - node = &((*node)[arg.index_]); - } else if (arg.kind_ == PathArgument::kindKey) { - if (!node->isObject()) { - // Error: node is not an object at position... - } - node = &((*node)[arg.key_]); - } - } - return *node; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_value.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_writer.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2011 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -#include -#include "json_tool.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 -#include -#define isfinite _finite -#elif defined(__sun) && defined(__SVR4) //Solaris -#include -#define isfinite finite -#else -#include -#ifndef isfinite // fix isfinite on Ubuntu 18.04 -#define isfinite std::isfinite -#endif -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below -#define snprintf _snprintf -#elif defined(__ANDROID__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#define snprintf std::snprintf -#endif - -#if defined(__BORLANDC__) -#include -#define isfinite _finite -#define snprintf _snprintf -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 -// Disable warning about strdup being deprecated. -#pragma warning(disable : 4996) -#endif - -namespace Json { - -#if __cplusplus >= 201103L -typedef std::unique_ptr StreamWriterPtr; -#else -typedef std::auto_ptr StreamWriterPtr; -#endif - -static bool containsControlCharacter(const char* str) { - while (*str) { - if (isControlCharacter(*(str++))) - return true; - } - return false; -} - -static bool containsControlCharacter0(const char* str, unsigned len) { - char const* end = str + len; - while (end != str) { - if (isControlCharacter(*str) || 0==*str) - return true; - ++str; - } - return false; -} - -std::string valueToString(LargestInt value) { - UIntToStringBuffer buffer; - char* current = buffer + sizeof(buffer); - if (value == Value::minLargestInt) { - uintToString(LargestUInt(Value::maxLargestInt) + 1, current); - *--current = '-'; - } else if (value < 0) { - uintToString(LargestUInt(-value), current); - *--current = '-'; - } else { - uintToString(LargestUInt(value), current); - } - assert(current >= buffer); - return current; -} - -std::string valueToString(LargestUInt value) { - UIntToStringBuffer buffer; - char* current = buffer + sizeof(buffer); - uintToString(value, current); - assert(current >= buffer); - return current; -} - -#if defined(JSON_HAS_INT64) - -std::string valueToString(Int value) { - return valueToString(LargestInt(value)); -} - -std::string valueToString(UInt value) { - return valueToString(LargestUInt(value)); -} - -#endif // # if defined(JSON_HAS_INT64) - -std::string valueToString(double value) { - // Allocate a buffer that is more than large enough to store the 16 digits of - // precision requested below. - char buffer[32]; - int len = -1; - -// Print into the buffer. We need not request the alternative representation -// that always has a decimal point because JSON doesn't distingish the -// concepts of reals and integers. -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with - // visual studio 2005 to - // avoid warning. -#if defined(WINCE) - len = _snprintf(buffer, sizeof(buffer), "%.17g", value); -#else - len = sprintf_s(buffer, sizeof(buffer), "%.17g", value); -#endif -#else - if (isfinite(value)) { - len = snprintf(buffer, sizeof(buffer), "%.17g", value); - } else { - // IEEE standard states that NaN values will not compare to themselves - if (value != value) { - len = snprintf(buffer, sizeof(buffer), "null"); - } else if (value < 0) { - len = snprintf(buffer, sizeof(buffer), "-1e+9999"); - } else { - len = snprintf(buffer, sizeof(buffer), "1e+9999"); - } - // For those, we do not need to call fixNumLoc, but it is fast. - } -#endif - assert(len >= 0); - fixNumericLocale(buffer, buffer + len); - return buffer; -} - -std::string valueToString(bool value) { return value ? "true" : "false"; } - -std::string valueToQuotedString(const char* value) { - if (value == NULL) - return ""; - // Not sure how to handle unicode... - if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && - !containsControlCharacter(value)) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = - strlen(value) * 2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - for (const char* c = value; *c != 0; ++c) { - switch (*c) { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - // case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something. - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } else { - result += *c; - } - break; - } - } - result += "\""; - return result; -} - -// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp -static char const* strnpbrk(char const* s, char const* accept, size_t n) { - assert((s || !n) && accept); - - char const* const end = s + n; - for (char const* cur = s; cur < end; ++cur) { - int const c = *cur; - for (char const* a = accept; *a; ++a) { - if (*a == c) { - return cur; - } - } - } - return NULL; -} -static std::string valueToQuotedStringN(const char* value, unsigned length) { - if (value == NULL) - return ""; - // Not sure how to handle unicode... - if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && - !containsControlCharacter0(value, length)) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = - length * 2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - char const* end = value + length; - for (const char* c = value; c != end; ++c) { - switch (*c) { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - // case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something.) - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } else { - result += *c; - } - break; - } - } - result += "\""; - return result; -} - -// Class Writer -// ////////////////////////////////////////////////////////////////// -Writer::~Writer() {} - -// Class FastWriter -// ////////////////////////////////////////////////////////////////// - -FastWriter::FastWriter() - : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), - omitEndingLineFeed_(false) {} - -void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } - -void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } - -void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } - -std::string FastWriter::write(const Value& root) { - document_ = ""; - writeValue(root); - if (!omitEndingLineFeed_) - document_ += "\n"; - return document_; -} - -void FastWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - if (!dropNullPlaceholders_) - document_ += "null"; - break; - case intValue: - document_ += valueToString(value.asLargestInt()); - break; - case uintValue: - document_ += valueToString(value.asLargestUInt()); - break; - case realValue: - document_ += valueToString(value.asDouble()); - break; - case stringValue: - { - // Is NULL possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); - break; - } - case booleanValue: - document_ += valueToString(value.asBool()); - break; - case arrayValue: { - document_ += '['; - int size = value.size(); - for (int index = 0; index < size; ++index) { - if (index > 0) - document_ += ','; - writeValue(value[index]); - } - document_ += ']'; - } break; - case objectValue: { - Value::Members members(value.getMemberNames()); - document_ += '{'; - for (Value::Members::iterator it = members.begin(); it != members.end(); - ++it) { - const std::string& name = *it; - if (it != members.begin()) - document_ += ','; - document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); - document_ += yamlCompatiblityEnabled_ ? ": " : ":"; - writeValue(value[name]); - } - document_ += '}'; - } break; - } -} - -// Class StyledWriter -// ////////////////////////////////////////////////////////////////// - -StyledWriter::StyledWriter() - : rightMargin_(74), indentSize_(3), addChildValues_() {} - -std::string StyledWriter::write(const Value& root) { - document_ = ""; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue(root); - writeValue(root); - writeCommentAfterValueOnSameLine(root); - document_ += "\n"; - return document_; -} - -void StyledWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - pushValue("null"); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble())); - break; - case stringValue: - { - // Is NULL possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) { - const std::string& name = *it; - const Value& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); - document_ += " : "; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - document_ += ','; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void StyledWriter::writeArrayValue(const Value& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isArrayMultiLine = isMultineArray(value); - if (isArrayMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - const Value& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - writeIndent(); - writeValue(childValue); - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - document_ += ','; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - document_ += "[ "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - document_ += ", "; - document_ += childValues_[index]; - } - document_ += " ]"; - } - } -} - -bool StyledWriter::isMultineArray(const Value& value) { - int size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; - isMultiLine = - isMultiLine || ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += int(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void StyledWriter::pushValue(const std::string& value) { - if (addChildValues_) - childValues_.push_back(value); - else - document_ += value; -} - -void StyledWriter::writeIndent() { - if (!document_.empty()) { - char last = document_[document_.length() - 1]; - if (last == ' ') // already indented - return; - if (last != '\n') // Comments may add new-line - document_ += '\n'; - } - document_ += indentString_; -} - -void StyledWriter::writeWithIndent(const std::string& value) { - writeIndent(); - document_ += value; -} - -void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } - -void StyledWriter::unindent() { - assert(int(indentString_.size()) >= indentSize_); - indentString_.resize(indentString_.size() - indentSize_); -} - -void StyledWriter::writeCommentBeforeValue(const Value& root) { - if (!root.hasComment(commentBefore)) - return; - - document_ += "\n"; - writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - document_ += *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) - writeIndent(); - ++iter; - } - - // Comments are stripped of trailing newlines, so add one here - document_ += "\n"; -} - -void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { - if (root.hasComment(commentAfterOnSameLine)) - document_ += " " + root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - document_ += "\n"; - document_ += root.getComment(commentAfter); - document_ += "\n"; - } -} - -bool StyledWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -// Class StyledStreamWriter -// ////////////////////////////////////////////////////////////////// - -StyledStreamWriter::StyledStreamWriter(std::string indentation) - : document_(NULL), rightMargin_(74), indentation_(indentation), - addChildValues_() {} - -void StyledStreamWriter::write(std::ostream& out, const Value& root) { - document_ = &out; - addChildValues_ = false; - indentString_ = ""; - indented_ = true; - writeCommentBeforeValue(root); - if (!indented_) writeIndent(); - indented_ = true; - writeValue(root); - writeCommentAfterValueOnSameLine(root); - *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. -} - -void StyledStreamWriter::writeValue(const Value& value) { - switch (value.type()) { - case nullValue: - pushValue("null"); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble())); - break; - case stringValue: - { - // Is NULL possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) { - const std::string& name = *it; - const Value& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedString(name.c_str())); - *document_ << " : "; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void StyledStreamWriter::writeArrayValue(const Value& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isArrayMultiLine = isMultineArray(value); - if (isArrayMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - const Value& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - if (!indented_) writeIndent(); - indented_ = true; - writeValue(childValue); - indented_ = false; - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - *document_ << "[ "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - *document_ << ", "; - *document_ << childValues_[index]; - } - *document_ << " ]"; - } - } -} - -bool StyledStreamWriter::isMultineArray(const Value& value) { - int size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { - const Value& childValue = value[index]; - isMultiLine = - isMultiLine || ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += int(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void StyledStreamWriter::pushValue(const std::string& value) { - if (addChildValues_) - childValues_.push_back(value); - else - *document_ << value; -} - -void StyledStreamWriter::writeIndent() { - // blep intended this to look at the so-far-written string - // to determine whether we are already indented, but - // with a stream we cannot do that. So we rely on some saved state. - // The caller checks indented_. - *document_ << '\n' << indentString_; -} - -void StyledStreamWriter::writeWithIndent(const std::string& value) { - if (!indented_) writeIndent(); - *document_ << value; - indented_ = false; -} - -void StyledStreamWriter::indent() { indentString_ += indentation_; } - -void StyledStreamWriter::unindent() { - assert(indentString_.size() >= indentation_.size()); - indentString_.resize(indentString_.size() - indentation_.size()); -} - -void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { - if (!root.hasComment(commentBefore)) - return; - - if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - *document_ << *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) - // writeIndent(); // would include newline - *document_ << indentString_; - ++iter; - } - indented_ = false; -} - -void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { - if (root.hasComment(commentAfterOnSameLine)) - *document_ << ' ' << root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - writeIndent(); - *document_ << root.getComment(commentAfter); - } - indented_ = false; -} - -bool StyledStreamWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -////////////////////////// -// BuiltStyledStreamWriter - -/// Scoped enums are not available until C++11. -struct CommentStyle { - /// Decide whether to write comments. - enum Enum { - None, ///< Drop all comments. - Most, ///< Recover odd behavior of previous versions (not implemented yet). - All ///< Keep all comments. - }; -}; - -struct BuiltStyledStreamWriter : public StreamWriter -{ - BuiltStyledStreamWriter( - std::string const& indentation, - CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::string const& endingLineFeedSymbol); - virtual int write(Value const& root, std::ostream* sout); -private: - void writeValue(Value const& value); - void writeArrayValue(Value const& value); - bool isMultineArray(Value const& value); - void pushValue(std::string const& value); - void writeIndent(); - void writeWithIndent(std::string const& value); - void indent(); - void unindent(); - void writeCommentBeforeValue(Value const& root); - void writeCommentAfterValueOnSameLine(Value const& root); - static bool hasCommentForValue(const Value& value); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::string indentString_; - int rightMargin_; - std::string indentation_; - CommentStyle::Enum cs_; - std::string colonSymbol_; - std::string nullSymbol_; - std::string endingLineFeedSymbol_; - bool addChildValues_ : 1; - bool indented_ : 1; -}; -BuiltStyledStreamWriter::BuiltStyledStreamWriter( - std::string const& indentation, - CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::string const& endingLineFeedSymbol) - : rightMargin_(74) - , indentation_(indentation) - , cs_(cs) - , colonSymbol_(colonSymbol) - , nullSymbol_(nullSymbol) - , endingLineFeedSymbol_(endingLineFeedSymbol) - , addChildValues_(false) - , indented_(false) -{ -} -int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) -{ - sout_ = sout; - addChildValues_ = false; - indented_ = true; - indentString_ = ""; - writeCommentBeforeValue(root); - if (!indented_) writeIndent(); - indented_ = true; - writeValue(root); - writeCommentAfterValueOnSameLine(root); - *sout_ << endingLineFeedSymbol_; - sout_ = NULL; - return 0; -} -void BuiltStyledStreamWriter::writeValue(Value const& value) { - switch (value.type()) { - case nullValue: - pushValue(nullSymbol_); - break; - case intValue: - pushValue(valueToString(value.asLargestInt())); - break; - case uintValue: - pushValue(valueToString(value.asLargestUInt())); - break; - case realValue: - pushValue(valueToString(value.asDouble())); - break; - case stringValue: - { - // Is NULL is possible for value.string_? - char const* str; - char const* end; - bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); - break; - } - case booleanValue: - pushValue(valueToString(value.asBool())); - break; - case arrayValue: - writeArrayValue(value); - break; - case objectValue: { - Value::Members members(value.getMemberNames()); - if (members.empty()) - pushValue("{}"); - else { - writeWithIndent("{"); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) { - std::string const& name = *it; - Value const& childValue = value[name]; - writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); - *sout_ << colonSymbol_; - writeValue(childValue); - if (++it == members.end()) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *sout_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("}"); - } - } break; - } -} - -void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { - unsigned size = value.size(); - if (size == 0) - pushValue("[]"); - else { - bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); - if (isMultiLine) { - writeWithIndent("["); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index = 0; - for (;;) { - Value const& childValue = value[index]; - writeCommentBeforeValue(childValue); - if (hasChildValue) - writeWithIndent(childValues_[index]); - else { - if (!indented_) writeIndent(); - indented_ = true; - writeValue(childValue); - indented_ = false; - } - if (++index == size) { - writeCommentAfterValueOnSameLine(childValue); - break; - } - *sout_ << ","; - writeCommentAfterValueOnSameLine(childValue); - } - unindent(); - writeWithIndent("]"); - } else // output on a single line - { - assert(childValues_.size() == size); - *sout_ << "["; - if (!indentation_.empty()) *sout_ << " "; - for (unsigned index = 0; index < size; ++index) { - if (index > 0) - *sout_ << ", "; - *sout_ << childValues_[index]; - } - if (!indentation_.empty()) *sout_ << " "; - *sout_ << "]"; - } - } -} - -bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { - int size = value.size(); - bool isMultiLine = size * 3 >= rightMargin_; - childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { - Value const& childValue = value[index]; - isMultiLine = - isMultiLine || ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); - } - if (!isMultiLine) // check if line length > max line length - { - childValues_.reserve(size); - addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { - if (hasCommentForValue(value[index])) { - isMultiLine = true; - } - writeValue(value[index]); - lineLength += int(childValues_[index].length()); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - -void BuiltStyledStreamWriter::pushValue(std::string const& value) { - if (addChildValues_) - childValues_.push_back(value); - else - *sout_ << value; -} - -void BuiltStyledStreamWriter::writeIndent() { - // blep intended this to look at the so-far-written string - // to determine whether we are already indented, but - // with a stream we cannot do that. So we rely on some saved state. - // The caller checks indented_. - - if (!indentation_.empty()) { - // In this case, drop newlines too. - *sout_ << '\n' << indentString_; - } -} - -void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { - if (!indented_) writeIndent(); - *sout_ << value; - indented_ = false; -} - -void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } - -void BuiltStyledStreamWriter::unindent() { - assert(indentString_.size() >= indentation_.size()); - indentString_.resize(indentString_.size() - indentation_.size()); -} - -void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { - if (cs_ == CommentStyle::None) return; - if (!root.hasComment(commentBefore)) - return; - - if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); - while (iter != comment.end()) { - *sout_ << *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) - // writeIndent(); // would write extra newline - *sout_ << indentString_; - ++iter; - } - indented_ = false; -} - -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { - if (cs_ == CommentStyle::None) return; - if (root.hasComment(commentAfterOnSameLine)) - *sout_ << " " + root.getComment(commentAfterOnSameLine); - - if (root.hasComment(commentAfter)) { - writeIndent(); - *sout_ << root.getComment(commentAfter); - } -} - -// static -bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { - return value.hasComment(commentBefore) || - value.hasComment(commentAfterOnSameLine) || - value.hasComment(commentAfter); -} - -/////////////// -// StreamWriter - -StreamWriter::StreamWriter() - : sout_(NULL) -{ -} -StreamWriter::~StreamWriter() -{ -} -StreamWriter::Factory::~Factory() -{} -StreamWriterBuilder::StreamWriterBuilder() -{ - setDefaults(&settings_); -} -StreamWriterBuilder::~StreamWriterBuilder() -{} -StreamWriter* StreamWriterBuilder::newStreamWriter() const -{ - std::string indentation = settings_["indentation"].asString(); - std::string cs_str = settings_["commentStyle"].asString(); - bool eyc = settings_["enableYAMLCompatibility"].asBool(); - bool dnp = settings_["dropNullPlaceholders"].asBool(); - CommentStyle::Enum cs = CommentStyle::All; - if (cs_str == "All") { - cs = CommentStyle::All; - } else if (cs_str == "None") { - cs = CommentStyle::None; - } else { - throwRuntimeError("commentStyle must be 'All' or 'None'"); - } - std::string colonSymbol = " : "; - if (eyc) { - colonSymbol = ": "; - } else if (indentation.empty()) { - colonSymbol = ":"; - } - std::string nullSymbol = "null"; - if (dnp) { - nullSymbol = ""; - } - std::string endingLineFeedSymbol = ""; - return new BuiltStyledStreamWriter( - indentation, cs, - colonSymbol, nullSymbol, endingLineFeedSymbol); -} -static void getValidWriterKeys(std::set* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("indentation"); - valid_keys->insert("commentStyle"); - valid_keys->insert("enableYAMLCompatibility"); - valid_keys->insert("dropNullPlaceholders"); -} -bool StreamWriterBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set valid_keys; - getValidWriterKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - std::string const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } - } - return 0u == inv.size(); -} -Value& StreamWriterBuilder::operator[](std::string key) -{ - return settings_[key]; -} -// static -void StreamWriterBuilder::setDefaults(Json::Value* settings) -{ - //! [StreamWriterBuilderDefaults] - (*settings)["commentStyle"] = "All"; - (*settings)["indentation"] = "\t"; - (*settings)["enableYAMLCompatibility"] = false; - (*settings)["dropNullPlaceholders"] = false; - //! [StreamWriterBuilderDefaults] -} - -std::string writeString(StreamWriter::Factory const& builder, Value const& root) { - std::ostringstream sout; - StreamWriterPtr const writer(builder.newStreamWriter()); - writer->write(root, &sout); - return sout.str(); -} - -std::ostream& operator<<(std::ostream& sout, Value const& root) { - StreamWriterBuilder builder; - StreamWriterPtr const writer(builder.newStreamWriter()); - writer->write(root, &sout); - return sout; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_writer.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - diff --git a/depends/jsoncpp/jsoncpp.h b/depends/jsoncpp/jsoncpp.h deleted file mode 100644 index ccb4f145b..000000000 --- a/depends/jsoncpp/jsoncpp.h +++ /dev/null @@ -1,2033 +0,0 @@ -/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). -/// It is intended to be used with #include "json/json.h" - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - -/* -The JsonCpp library's source code, including accompanying documentation, -tests and demonstration applications, are licensed under the following -conditions... - -The author (Baptiste Lepilleur) explicitly disclaims copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, -this software is released into the Public Domain. - -In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is -released under the terms of the MIT License (see below). - -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual -Public Domain/MIT License conditions described here, as they choose. - -The MIT License is about as close to Public Domain as a license can get, and is -described in clear, concise terms at: - - http://en.wikipedia.org/wiki/MIT_License - -The full text of the MIT License follows: - -======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur - -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. -======================================================================== -(END LICENSE TEXT) - -The MIT license is compatible with both the GPL and commercial -software, affording one all of the rights of Public Domain with the -minor nuisance of being required to keep the above copyright notice -and license text in the source code. Note also that by accepting the -Public Domain "license" you can re-license your copy using whatever -license you like. - -*/ - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - - - - - -#ifndef JSON_AMALGATED_H_INCLUDED -# define JSON_AMALGATED_H_INCLUDED -/// If defined, indicates that the source file is amalgated -/// to prevent private header inclusion. -#define JSON_IS_AMALGAMATION - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/version.h -// ////////////////////////////////////////////////////////////////////// - -// DO NOT EDIT. This file (and "version") is generated by CMake. -// Run CMake configure step to update it. -#ifndef JSON_VERSION_H_INCLUDED -# define JSON_VERSION_H_INCLUDED - -# define JSONCPP_VERSION_STRING "1.6.5" -# define JSONCPP_VERSION_MAJOR 1 -# define JSONCPP_VERSION_MINOR 6 -# define JSONCPP_VERSION_PATCH 5 -# define JSONCPP_VERSION_QUALIFIER -# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) - -#endif // JSON_VERSION_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/version.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_CONFIG_H_INCLUDED -#define JSON_CONFIG_H_INCLUDED - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of -/// std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 - -// If non-zero, the library uses exceptions to report bad input instead of C -// assertion macros. The default is to use exceptions. -#ifndef JSON_USE_EXCEPTION -#define JSON_USE_EXCEPTION 1 -#endif - -/// If defined, indicates that the source file is amalgated -/// to prevent private header inclusion. -/// Remarks: it is automatically defined in the generated amalgated header. -// #define JSON_IS_AMALGAMATION - -#ifdef JSON_IN_CPPTL -#include -#ifndef JSON_USE_CPPTL -#define JSON_USE_CPPTL 1 -#endif -#endif - -#ifdef JSON_IN_CPPTL -#define JSON_API CPPTL_API -#elif defined(JSON_DLL_BUILD) -#if defined(_MSC_VER) -#define JSON_API __declspec(dllexport) -#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING -#endif // if defined(_MSC_VER) -#elif defined(JSON_DLL) -#if defined(_MSC_VER) -#define JSON_API __declspec(dllimport) -#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING -#endif // if defined(_MSC_VER) -#endif // ifdef JSON_IN_CPPTL -#if !defined(JSON_API) -#define JSON_API -#endif - -// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for -// integer -// Storages, and 64 bits integer support is disabled. -// #define JSON_NO_INT64 1 - -#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 -// Microsoft Visual Studio 6 only support conversion from __int64 to double -// (no conversion from unsigned __int64). -#define JSON_USE_INT64_DOUBLE_CONVERSION 1 -// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' -// characters in the debug information) -// All projects I've ever seen with VS6 were using this globally (not bothering -// with pragma push/pop). -#pragma warning(disable : 4786) -#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 - -#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 -/// Indicates that the following function is deprecated. -#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -#elif defined(__clang__) && defined(__has_feature) -#if __has_feature(attribute_deprecated_with_message) -#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -#endif -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) -#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) -#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) -#endif - -#if !defined(JSONCPP_DEPRECATED) -#define JSONCPP_DEPRECATED(message) -#endif // if !defined(JSONCPP_DEPRECATED) - -#include - -namespace Json { -typedef int Int; -typedef unsigned int UInt; -#if defined(JSON_NO_INT64) -typedef int LargestInt; -typedef unsigned int LargestUInt; -#undef JSON_HAS_INT64 -#else // if defined(JSON_NO_INT64) -// For Microsoft Visual use specific types as long long is not supported -#if defined(_MSC_VER) // Microsoft Visual Studio -typedef __int64 Int64; -typedef unsigned __int64 UInt64; -#else // if defined(_MSC_VER) // Other platforms, use long long -typedef int64_t Int64; -typedef uint64_t UInt64; -#endif // if defined(_MSC_VER) -typedef Int64 LargestInt; -typedef UInt64 LargestUInt; -#define JSON_HAS_INT64 -#endif // if defined(JSON_NO_INT64) -} // end namespace Json - -#endif // JSON_CONFIG_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_FORWARDS_H_INCLUDED -#define JSON_FORWARDS_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "config.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - -// writer.h -class FastWriter; -class StyledWriter; - -// reader.h -class Reader; - -// features.h -class Features; - -// value.h -typedef unsigned int ArrayIndex; -class StaticString; -class Path; -class PathArgument; -class Value; -class ValueIteratorBase; -class ValueIterator; -class ValueConstIterator; - -} // namespace Json - -#endif // JSON_FORWARDS_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/features.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_FEATURES_H_INCLUDED -#define CPPTL_JSON_FEATURES_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "forwards.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - -/** \brief Configuration passed to reader and writer. - * This configuration object can be used to force the Reader or Writer - * to behave in a standard conforming way. - */ -class JSON_API Features { -public: - /** \brief A configuration that allows all features and assumes all strings - * are UTF-8. - * - C & C++ comments are allowed - * - Root object can be any JSON value - * - Assumes Value strings are encoded in UTF-8 - */ - static Features all(); - - /** \brief A configuration that is strictly compatible with the JSON - * specification. - * - Comments are forbidden. - * - Root object must be either an array or an object value. - * - Assumes Value strings are encoded in UTF-8 - */ - static Features strictMode(); - - /** \brief Initialize the configuration like JsonConfig::allFeatures; - */ - Features(); - - /// \c true if comments are allowed. Default: \c true. - bool allowComments_; - - /// \c true if root must be either an array or an object value. Default: \c - /// false. - bool strictRoot_; - - /// \c true if dropped null placeholders are allowed. Default: \c false. - bool allowDroppedNullPlaceholders_; - - /// \c true if numeric object key are allowed. Default: \c false. - bool allowNumericKeys_; -}; - -} // namespace Json - -#endif // CPPTL_JSON_FEATURES_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/features.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/value.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_H_INCLUDED -#define CPPTL_JSON_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "forwards.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include - -#ifndef JSON_USE_CPPTL_SMALLMAP -#include -#else -#include -#endif -#ifdef JSON_USE_CPPTL -#include -#endif - -// Disable warning C4251: : needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -/** \brief JSON (JavaScript Object Notation). - */ -namespace Json { - -/** Base class for all exceptions we throw. - * - * We use nothing but these internally. Of course, STL can throw others. - */ -class JSON_API Exception : public std::exception { -public: - Exception(std::string const& msg); - virtual ~Exception() throw(); - virtual char const* what() const throw(); -protected: - std::string const msg_; -}; - -/** Exceptions which the user cannot easily avoid. - * - * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input - * - * \remark derived from Json::Exception - */ -class JSON_API RuntimeError : public Exception { -public: - RuntimeError(std::string const& msg); -}; - -/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. - * - * These are precondition-violations (user bugs) and internal errors (our bugs). - * - * \remark derived from Json::Exception - */ -class JSON_API LogicError : public Exception { -public: - LogicError(std::string const& msg); -}; - -/// used internally -void throwRuntimeError(std::string const& msg); -/// used internally -void throwLogicError(std::string const& msg); - -/** \brief Type of the value held by a Value object. - */ -enum ValueType { - nullValue = 0, ///< 'null' value - intValue, ///< signed integer value - uintValue, ///< unsigned integer value - realValue, ///< double value - stringValue, ///< UTF-8 string value - booleanValue, ///< bool value - arrayValue, ///< array value (ordered list) - objectValue ///< object value (collection of name/value pairs). -}; - -enum CommentPlacement { - commentBefore = 0, ///< a comment placed on the line before a value - commentAfterOnSameLine, ///< a comment just after a value on the same line - commentAfter, ///< a comment on the line after a value (only make sense for - /// root value) - numberOfCommentPlacement -}; - -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator EnumMemberNames; -// typedef CppTL::AnyEnumerator EnumValues; -//# endif - -/** \brief Lightweight wrapper to tag static string. - * - * Value constructor and objectValue member assignement takes advantage of the - * StaticString and avoid the cost of string duplication when storing the - * string or the member name. - * - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ -class JSON_API StaticString { -public: - explicit StaticString(const char* czstring) : c_str_(czstring) {} - - operator const char*() const { return c_str_; } - - const char* c_str() const { return c_str_; } - -private: - const char* c_str_; -}; - -/** \brief Represents a JSON value. - * - * This class is a discriminated union wrapper that can represents a: - * - signed integer [range: Value::minInt - Value::maxInt] - * - unsigned integer (range: 0 - Value::maxUInt) - * - double - * - UTF-8 string - * - boolean - * - 'null' - * - an ordered list of Value - * - collection of name/value pairs (javascript object) - * - * The type of the held value is represented by a #ValueType and - * can be obtained using type(). - * - * Values of an #objectValue or #arrayValue can be accessed using operator[]() - * methods. - * Non-const methods will automatically create the a #nullValue element - * if it does not exist. - * The sequence of an #arrayValue will be automatically resized and initialized - * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. - * - * The get() methods can be used to obtain default value in the case the - * required element does not exist. - * - * It is possible to iterate over the list of a #objectValue values using - * the getMemberNames() method. - * - * \note #Value string-length fit in size_t, but keys must be < 2^30. - * (The reason is an implementation detail.) A #CharReader will raise an - * exception if a bound is exceeded to avoid security holes in your app, - * but the Value API does *not* check bounds. That is the responsibility - * of the caller. - */ -class JSON_API Value { - friend class ValueIteratorBase; -public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; -#if defined(JSON_HAS_INT64) - typedef Json::UInt64 UInt64; - typedef Json::Int64 Int64; -#endif // defined(JSON_HAS_INT64) - typedef Json::LargestInt LargestInt; - typedef Json::LargestUInt LargestUInt; - typedef Json::ArrayIndex ArrayIndex; - - static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). - static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null - /// Minimum signed integer value that can be stored in a Json::Value. - static const LargestInt minLargestInt; - /// Maximum signed integer value that can be stored in a Json::Value. - static const LargestInt maxLargestInt; - /// Maximum unsigned integer value that can be stored in a Json::Value. - static const LargestUInt maxLargestUInt; - - /// Minimum signed int value that can be stored in a Json::Value. - static const Int minInt; - /// Maximum signed int value that can be stored in a Json::Value. - static const Int maxInt; - /// Maximum unsigned int value that can be stored in a Json::Value. - static const UInt maxUInt; - -#if defined(JSON_HAS_INT64) - /// Minimum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 minInt64; - /// Maximum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 maxInt64; - /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. - static const UInt64 maxUInt64; -#endif // defined(JSON_HAS_INT64) - -private: -#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - class CZString { - public: - enum DuplicationPolicy { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; - CZString(ArrayIndex index); - CZString(char const* str, unsigned length, DuplicationPolicy allocate); - CZString(CZString const& other); - ~CZString(); - CZString& operator=(CZString other); - bool operator<(CZString const& other) const; - bool operator==(CZString const& other) const; - ArrayIndex index() const; - //const char* c_str() const; ///< \deprecated - char const* data() const; - unsigned length() const; - bool isStaticString() const; - - private: - void swap(CZString& other); - - struct StringStorage { - unsigned policy_: 2; - unsigned length_: 30; // 1GB max - }; - - char const* cstr_; // actually, a prefixed string, unless policy is noDup - union { - ArrayIndex index_; - StringStorage storage_; - }; - }; - -public: -#ifndef JSON_USE_CPPTL_SMALLMAP - typedef std::map ObjectValues; -#else - typedef CppTL::SmallMap ObjectValues; -#endif // ifndef JSON_USE_CPPTL_SMALLMAP -#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - -public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. -This is useful since clear() and resize() will not alter types. - - Examples: -\code -Json::Value null_value; // null -Json::Value arr_value(Json::arrayValue); // [] -Json::Value obj_value(Json::objectValue); // {} -\endcode - */ - Value(ValueType type = nullValue); - Value(Int value); - Value(UInt value); -#if defined(JSON_HAS_INT64) - Value(Int64 value); - Value(UInt64 value); -#endif // if defined(JSON_HAS_INT64) - Value(double value); - Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) - Value(const char* begin, const char* end); ///< Copy all, incl zeroes. - /** \brief Constructs a value from a static string. - - * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. - * \note This works only for null-terminated strings. (We cannot change the - * size of this class, so we have nowhere to store the length, - * which might be computed later for various operations.) - * - * Example of usage: - * \code - * static StaticString foo("some text"); - * Json::Value aValue(foo); - * \endcode - */ - Value(const StaticString& value); - Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. -#ifdef JSON_USE_CPPTL - Value(const CppTL::ConstString& value); -#endif - Value(bool value); - /// Deep copy. - Value(const Value& other); - ~Value(); - - /// Deep copy, then swap(other). - /// \note Over-write existing comments. To preserve comments, use #swapPayload(). - Value& operator=(Value other); - /// Swap everything. - void swap(Value& other); - /// Swap values but leave comments and source offsets in place. - void swapPayload(Value& other); - - ValueType type() const; - - /// Compare payload only, not comments etc. - bool operator<(const Value& other) const; - bool operator<=(const Value& other) const; - bool operator>=(const Value& other) const; - bool operator>(const Value& other) const; - bool operator==(const Value& other) const; - bool operator!=(const Value& other) const; - int compare(const Value& other) const; - - const char* asCString() const; ///< Embedded zeroes could cause you trouble! - std::string asString() const; ///< Embedded zeroes are possible. - /** Get raw char* of string-value. - * \return false if !string. (Seg-fault if str or end are NULL.) - */ - bool getString( - char const** begin, char const** end) const; -#ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; -#endif - Int asInt() const; - UInt asUInt() const; -#if defined(JSON_HAS_INT64) - Int64 asInt64() const; - UInt64 asUInt64() const; -#endif // if defined(JSON_HAS_INT64) - LargestInt asLargestInt() const; - LargestUInt asLargestUInt() const; - float asFloat() const; - double asDouble() const; - bool asBool() const; - - bool isNull() const; - bool isBool() const; - bool isInt() const; - bool isInt64() const; - bool isUInt() const; - bool isUInt64() const; - bool isIntegral() const; - bool isDouble() const; - bool isNumeric() const; - bool isString() const; - bool isArray() const; - bool isObject() const; - - bool isConvertibleTo(ValueType other) const; - - /// Number of values in array or object - ArrayIndex size() const; - - /// \brief Return true if empty array, empty object, or null; - /// otherwise, false. - bool empty() const; - - /// Return isNull() - bool operator!() const; - - /// Remove all object members and array elements. - /// \pre type() is arrayValue, objectValue, or nullValue - /// \post type() is unchanged - void clear(); - - /// Resize the array to size elements. - /// New elements are initialized to null. - /// May only be called on nullValue or arrayValue. - /// \pre type() is arrayValue or nullValue - /// \post type() is arrayValue - void resize(ArrayIndex size); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value& operator[](ArrayIndex index); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value& operator[](int index); - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value& operator[](ArrayIndex index) const; - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value& operator[](int index) const; - - /// If the array contains at least index+1 elements, returns the element - /// value, - /// otherwise returns defaultValue. - Value get(ArrayIndex index, const Value& defaultValue) const; - /// Return true if index < size(). - bool isValidIndex(ArrayIndex index) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; - Value& append(const Value& value); - - /// Access an object value by name, create a null member if it does not exist. - /// \note Because of our implementation, keys are limited to 2^30 -1 chars. - /// Exceeding that will cause an exception. - Value& operator[](const char* key); - /// Access an object value by name, returns null if there is no member with - /// that name. - const Value& operator[](const char* key) const; - /// Access an object value by name, create a null member if it does not exist. - /// \param key may contain embedded nulls. - Value& operator[](const std::string& key); - /// Access an object value by name, returns null if there is no member with - /// that name. - /// \param key may contain embedded nulls. - const Value& operator[](const std::string& key) const; - /** \brief Access an object value by name, create a null member if it does not - exist. - - * If the object has no entry for that name, then the member name used to store - * the new entry is not duplicated. - * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - Value& operator[](const StaticString& key); -#ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value& operator[](const CppTL::ConstString& key); - /// Access an object value by name, returns null if there is no member with - /// that name. - const Value& operator[](const CppTL::ConstString& key) const; -#endif - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const char* key, const Value& defaultValue) const; - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - /// \note key may contain embedded nulls. - Value get(const char* begin, const char* end, const Value& defaultValue) const; - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - /// \param key may contain embedded nulls. - Value get(const std::string& key, const Value& defaultValue) const; -#ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const CppTL::ConstString& key, const Value& defaultValue) const; -#endif - /// Most general and efficient version of isMember()const, get()const, - /// and operator[]const - /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 - Value const* find(char const* begin, char const* end) const; - /// Most general and efficient version of object-mutators. - /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 - /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. - Value const* demand(char const* begin, char const* end); - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \return the removed Value, or null. - /// \pre type() is objectValue or nullValue - /// \post type() is unchanged - /// \deprecated - Value removeMember(const char* key); - /// Same as removeMember(const char*) - /// \param key may contain embedded nulls. - /// \deprecated - Value removeMember(const std::string& key); - /// Same as removeMember(const char* begin, const char* end, Value* removed), - /// but 'key' is null-terminated. - bool removeMember(const char* key, Value* removed); - /** \brief Remove the named map member. - - Update 'removed' iff removed. - \param key may contain embedded nulls. - \return true iff removed (no exceptions) - */ - bool removeMember(std::string const& key, Value* removed); - /// Same as removeMember(std::string const& key, Value* removed) - bool removeMember(const char* begin, const char* end, Value* removed); - /** \brief Remove the indexed array element. - - O(n) expensive operations. - Update 'removed' iff removed. - \return true iff removed (no exceptions) - */ - bool removeIndex(ArrayIndex i, Value* removed); - - /// Return true if the object has a member named key. - /// \note 'key' must be null-terminated. - bool isMember(const char* key) const; - /// Return true if the object has a member named key. - /// \param key may contain embedded nulls. - bool isMember(const std::string& key) const; - /// Same as isMember(std::string const& key)const - bool isMember(const char* begin, const char* end) const; -#ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember(const CppTL::ConstString& key) const; -#endif - - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is objectValue or nullValue - /// \post if type() was nullValue, it remains nullValue - Members getMemberNames() const; - - //# ifdef JSON_USE_CPPTL - // EnumMemberNames enumMemberNames() const; - // EnumValues enumValues() const; - //# endif - - /// \deprecated Always pass len. - JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") - void setComment(const char* comment, CommentPlacement placement); - /// Comments must be //... or /* ... */ - void setComment(const char* comment, size_t len, CommentPlacement placement); - /// Comments must be //... or /* ... */ - void setComment(const std::string& comment, CommentPlacement placement); - bool hasComment(CommentPlacement placement) const; - /// Include delimiters and embedded newlines. - std::string getComment(CommentPlacement placement) const; - - std::string toStyledString() const; - - const_iterator begin() const; - const_iterator end() const; - - iterator begin(); - iterator end(); - - // Accessors for the [start, limit) range of bytes within the JSON text from - // which this value was parsed, if any. - void setOffsetStart(size_t start); - void setOffsetLimit(size_t limit); - size_t getOffsetStart() const; - size_t getOffsetLimit() const; - -private: - void initBasic(ValueType type, bool allocated = false); - - Value& resolveReference(const char* key); - Value& resolveReference(const char* key, const char* end); - - struct CommentInfo { - CommentInfo(); - ~CommentInfo(); - - void setComment(const char* text, size_t len); - - char* comment_; - }; - - // struct MemberNamesTransform - //{ - // typedef const char *result_type; - // const char *operator()( const CZString &name ) const - // { - // return name.c_str(); - // } - //}; - - union ValueHolder { - LargestInt int_; - LargestUInt uint_; - double real_; - bool bool_; - char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ - ObjectValues* map_; - } value_; - ValueType type_ : 8; - unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. - // If not allocated_, string_ must be null-terminated. - CommentInfo* comments_; - - // [start, limit) byte offsets in the source JSON text from which this Value - // was extracted. - size_t start_; - size_t limit_; -}; - -/** \brief Experimental and untested: represents an element of the "path" to - * access a node. - */ -class JSON_API PathArgument { -public: - friend class Path; - - PathArgument(); - PathArgument(ArrayIndex index); - PathArgument(const char* key); - PathArgument(const std::string& key); - -private: - enum Kind { - kindNone = 0, - kindIndex, - kindKey - }; - std::string key_; - ArrayIndex index_; - Kind kind_; -}; - -/** \brief Experimental and untested: represents a "path" to access a node. - * - * Syntax: - * - "." => root node - * - ".[n]" => elements at index 'n' of root node (an array value) - * - ".name" => member named 'name' of root node (an object value) - * - ".name1.name2.name3" - * - ".[0][1][2].name1[3]" - * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter - */ -class JSON_API Path { -public: - Path(const std::string& path, - const PathArgument& a1 = PathArgument(), - const PathArgument& a2 = PathArgument(), - const PathArgument& a3 = PathArgument(), - const PathArgument& a4 = PathArgument(), - const PathArgument& a5 = PathArgument()); - - const Value& resolve(const Value& root) const; - Value resolve(const Value& root, const Value& defaultValue) const; - /// Creates the "path" to access the specified node and returns a reference on - /// the node. - Value& make(Value& root) const; - -private: - typedef std::vector InArgs; - typedef std::vector Args; - - void makePath(const std::string& path, const InArgs& in); - void addPathInArg(const std::string& path, - const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind); - void invalidPath(const std::string& path, int location); - - Args args_; -}; - -/** \brief base class for Value iterators. - * - */ -class JSON_API ValueIteratorBase { -public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; - - bool operator==(const SelfType& other) const { return isEqual(other); } - - bool operator!=(const SelfType& other) const { return !isEqual(other); } - - difference_type operator-(const SelfType& other) const { - return other.computeDistance(*this); - } - - /// Return either the index or the member name of the referenced value as a - /// Value. - Value key() const; - - /// Return the index of the referenced Value, or -1 if it is not an arrayValue. - UInt index() const; - - /// Return the member name of the referenced Value, or "" if it is not an - /// objectValue. - /// \note Avoid `c_str()` on result, as embedded zeroes are possible. - std::string name() const; - - /// Return the member name of the referenced Value. "" if it is not an - /// objectValue. - /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. - JSONCPP_DEPRECATED("Use `key = name();` instead.") - char const* memberName() const; - /// Return the member name of the referenced Value, or NULL if it is not an - /// objectValue. - /// \note Better version than memberName(). Allows embedded nulls. - char const* memberName(char const** end) const; - -protected: - Value& deref() const; - - void increment(); - - void decrement(); - - difference_type computeDistance(const SelfType& other) const; - - bool isEqual(const SelfType& other) const; - - void copy(const SelfType& other); - -private: - Value::ObjectValues::iterator current_; - // Indicates that iterator is for a null value. - bool isNull_; - -public: - // For some reason, BORLAND needs these at the end, rather - // than earlier. No idea why. - ValueIteratorBase(); - explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); -}; - -/** \brief const iterator for object and array value. - * - */ -class JSON_API ValueConstIterator : public ValueIteratorBase { - friend class Value; - -public: - typedef const Value value_type; - //typedef unsigned int size_t; - //typedef int difference_type; - typedef const Value& reference; - typedef const Value* pointer; - typedef ValueConstIterator SelfType; - - ValueConstIterator(); - -private: -/*! \internal Use by Value to create an iterator. - */ - explicit ValueConstIterator(const Value::ObjectValues::iterator& current); -public: - SelfType& operator=(const ValueIteratorBase& other); - - SelfType operator++(int) { - SelfType temp(*this); - ++*this; - return temp; - } - - SelfType operator--(int) { - SelfType temp(*this); - --*this; - return temp; - } - - SelfType& operator--() { - decrement(); - return *this; - } - - SelfType& operator++() { - increment(); - return *this; - } - - reference operator*() const { return deref(); } - - pointer operator->() const { return &deref(); } -}; - -/** \brief Iterator for object and array value. - */ -class JSON_API ValueIterator : public ValueIteratorBase { - friend class Value; - -public: - typedef Value value_type; - typedef unsigned int size_t; - typedef int difference_type; - typedef Value& reference; - typedef Value* pointer; - typedef ValueIterator SelfType; - - ValueIterator(); - ValueIterator(const ValueConstIterator& other); - ValueIterator(const ValueIterator& other); - -private: -/*! \internal Use by Value to create an iterator. - */ - explicit ValueIterator(const Value::ObjectValues::iterator& current); -public: - SelfType& operator=(const SelfType& other); - - SelfType operator++(int) { - SelfType temp(*this); - ++*this; - return temp; - } - - SelfType operator--(int) { - SelfType temp(*this); - --*this; - return temp; - } - - SelfType& operator--() { - decrement(); - return *this; - } - - SelfType& operator++() { - increment(); - return *this; - } - - reference operator*() const { return deref(); } - - pointer operator->() const { return &deref(); } -}; - -} // namespace Json - - -namespace std { -/// Specialize std::swap() for Json::Value. -template<> -inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } -} - - -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(pop) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#endif // CPPTL_JSON_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/value.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/reader.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_READER_H_INCLUDED -#define CPPTL_JSON_READER_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "features.h" -#include "value.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include - -// Disable warning C4251: : needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -namespace Json { - -/** \brief Unserialize a JSON document into a - *Value. - * - * \deprecated Use CharReader and CharReaderBuilder. - */ -class JSON_API Reader { -public: - typedef char Char; - typedef const Char* Location; - - /** \brief An error tagged with where in the JSON text it was encountered. - * - * The offsets give the [start, limit) range of bytes within the text. Note - * that this is bytes, not codepoints. - * - */ - struct StructuredError { - size_t offset_start; - size_t offset_limit; - std::string message; - }; - - /** \brief Constructs a Reader allowing all features - * for parsing. - */ - Reader(); - - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. - */ - Reader(const Features& features); - - /** \brief Read a Value from a JSON - * document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - * back during - * serialization, \c false to discard comments. - * This parameter is ignored if - * Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an - * error occurred. - */ - bool - parse(const std::string& document, Value& root, bool collectComments = true); - - /** \brief Read a Value from a JSON - document. - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - back during - * serialization, \c false to discard comments. - * This parameter is ignored if - Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an - error occurred. - */ - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments = true); - - /// \brief Parse from input stream. - /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse(std::istream& is, Value& root, bool collectComments = true); - - /** \brief Returns a user friendly string that list errors in the parsed - * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. - * \deprecated Use getFormattedErrorMessages() instead (typo fix). - */ - JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") - std::string getFormatedErrorMessages() const; - - /** \brief Returns a user friendly string that list errors in the parsed - * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. - */ - std::string getFormattedErrorMessages() const; - - /** \brief Returns a vector of structured erros encounted while parsing. - * \return A (possibly empty) vector of StructuredError objects. Currently - * only one error can be returned, but the caller should tolerate - * multiple - * errors. This can occur if the parser recovers from a non-fatal - * parse error and then encounters additional errors. - */ - std::vector getStructuredErrors() const; - - /** \brief Add a semantic error message. - * \param value JSON Value location associated with the error - * \param message The error message. - * \return \c true if the error was successfully added, \c false if the - * Value offset exceeds the document size. - */ - bool pushError(const Value& value, const std::string& message); - - /** \brief Add a semantic error message with extra context. - * \param value JSON Value location associated with the error - * \param message The error message. - * \param extra Additional JSON Value location to contextualize the error - * \return \c true if the error was successfully added, \c false if either - * Value offset exceeds the document size. - */ - bool pushError(const Value& value, const std::string& message, const Value& extra); - - /** \brief Return whether there are any errors. - * \return \c true if there are no errors to report \c false if - * errors have occurred. - */ - bool good() const; - -private: - enum TokenType { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo { - public: - Token token_; - std::string message_; - Location extra_; - }; - - typedef std::deque Errors; - - bool readToken(Token& token); - void skipSpaces(); - bool match(Location pattern, int patternLength); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - void readNumber(); - bool readValue(); - bool readObject(Token& token); - bool readArray(Token& token); - bool decodeNumber(Token& token); - bool decodeNumber(Token& token, Value& decoded); - bool decodeString(Token& token); - bool decodeString(Token& token, std::string& decoded); - bool decodeDouble(Token& token); - bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const std::string& message, Token& token, Location extra = 0); - bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken); - void skipUntilSpace(); - Value& currentValue(); - Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - std::string getLocationLineAndColumn(Location location) const; - void addComment(Location begin, Location end, CommentPlacement placement); - void skipCommentTokens(Token& token); - - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - std::string commentsBefore_; - Features features_; - bool collectComments_; -}; // Reader - -/** Interface for reading JSON from a char array. - */ -class JSON_API CharReader { -public: - virtual ~CharReader() {} - /** \brief Read a Value from a JSON - document. - * The document must be a UTF-8 encoded string containing the document to read. - * - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param errs [out] Formatted error messages (if not NULL) - * a user friendly string that lists errors in the parsed - * document. - * \return \c true if the document was successfully parsed, \c false if an - error occurred. - */ - virtual bool parse( - char const* beginDoc, char const* endDoc, - Value* root, std::string* errs) = 0; - - class Factory { - public: - virtual ~Factory() {} - /** \brief Allocate a CharReader via operator new(). - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - virtual CharReader* newCharReader() const = 0; - }; // Factory -}; // CharReader - -/** \brief Build a CharReader implementation. - -Usage: -\code - using namespace Json; - CharReaderBuilder builder; - builder["collectComments"] = false; - Value value; - std::string errs; - bool ok = parseFromStream(builder, std::cin, &value, &errs); -\endcode -*/ -class JSON_API CharReaderBuilder : public CharReader::Factory { -public: - // Note: We use a Json::Value so that we can add data-members to this class - // without a major version bump. - /** Configuration of this builder. - These are case-sensitive. - Available settings (case-sensitive): - - `"collectComments": false or true` - - true to collect comment and allow writing them - back during serialization, false to discard comments. - This parameter is ignored if allowComments is false. - - `"allowComments": false or true` - - true if comments are allowed. - - `"strictRoot": false or true` - - true if root must be either an array or an object value - - `"allowDroppedNullPlaceholders": false or true` - - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) - - `"allowNumericKeys": false or true` - - true if numeric object keys are allowed. - - `"allowSingleQuotes": false or true` - - true if '' are allowed for strings (both keys and values) - - `"stackLimit": integer` - - Exceeding stackLimit (recursive depth of `readValue()`) will - cause an exception. - - This is a security issue (seg-faults caused by deeply nested JSON), - so the default is low. - - `"failIfExtra": false or true` - - If true, `parse()` returns false when extra non-whitespace trails - the JSON value in the input string. - - `"rejectDupKeys": false or true` - - If true, `parse()` returns false when a key is duplicated within an object. - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ - Json::Value settings_; - - CharReaderBuilder(); - virtual ~CharReaderBuilder(); - - virtual CharReader* newCharReader() const; - - /** \return true if 'settings' are legal and consistent; - * otherwise, indicate bad settings via 'invalid'. - */ - bool validate(Json::Value* invalid) const; - - /** A simple way to update a specific setting. - */ - Value& operator[](std::string key); - - /** Called by ctor, but you can use this to reset settings_. - * \pre 'settings' != NULL (but Json::null is fine) - * \remark Defaults: - * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults - */ - static void setDefaults(Json::Value* settings); - /** Same as old Features::strictMode(). - * \pre 'settings' != NULL (but Json::null is fine) - * \remark Defaults: - * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode - */ - static void strictMode(Json::Value* settings); -}; - -/** Consume entire stream and use its begin/end. - * Someday we might have a real StreamReader, but for now this - * is convenient. - */ -bool JSON_API parseFromStream( - CharReader::Factory const&, - std::istream&, - Value* root, std::string* errs); - -/** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - Json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { - "dir": { - "file": { - // The input stream JSON would be nested here. - } - } - } - \endverbatim - \throw std::exception on parse error. - \see Json::operator<<() -*/ -JSON_API std::istream& operator>>(std::istream&, Value&); - -} // namespace Json - -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(pop) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#endif // CPPTL_JSON_READER_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/reader.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/writer.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_WRITER_H_INCLUDED -#define JSON_WRITER_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -#include "value.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include - -// Disable warning C4251: : needs to have dll-interface to -// be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(push) -#pragma warning(disable : 4251) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -namespace Json { - -class Value; - -/** - -Usage: -\code - using namespace Json; - void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { - std::unique_ptr const writer( - factory.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush - } -\endcode -*/ -class JSON_API StreamWriter { -protected: - std::ostream* sout_; // not owned; will not delete -public: - StreamWriter(); - virtual ~StreamWriter(); - /** Write Value into document as configured in sub-class. - Do not take ownership of sout, but maintain a reference during function. - \pre sout != NULL - \return zero on success (For now, we always return zero, so check the stream instead.) - \throw std::exception possibly, depending on configuration - */ - virtual int write(Value const& root, std::ostream* sout) = 0; - - /** \brief A simple abstract factory. - */ - class JSON_API Factory { - public: - virtual ~Factory(); - /** \brief Allocate a CharReader via operator new(). - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - virtual StreamWriter* newStreamWriter() const = 0; - }; // Factory -}; // StreamWriter - -/** \brief Write into stringstream, then return string, for convenience. - * A StreamWriter will be created from the factory, used, and then deleted. - */ -std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); - - -/** \brief Build a StreamWriter implementation. - -Usage: -\code - using namespace Json; - Value value = ...; - StreamWriterBuilder builder; - builder["commentStyle"] = "None"; - builder["indentation"] = " "; // or whatever you like - std::unique_ptr writer( - builder.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush -\endcode -*/ -class JSON_API StreamWriterBuilder : public StreamWriter::Factory { -public: - // Note: We use a Json::Value so that we can add data-members to this class - // without a major version bump. - /** Configuration of this builder. - Available settings (case-sensitive): - - "commentStyle": "None" or "All" - - "indentation": "" - - "enableYAMLCompatibility": false or true - - slightly change the whitespace around colons - - "dropNullPlaceholders": false or true - - Drop the "null" string from the writer's output for nullValues. - Strictly speaking, this is not valid JSON. But when the output is being - fed to a browser's Javascript, it makes for smaller output and the - browser can handle the output just fine. - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ - Json::Value settings_; - - StreamWriterBuilder(); - virtual ~StreamWriterBuilder(); - - /** - * \throw std::exception if something goes wrong (e.g. invalid settings) - */ - virtual StreamWriter* newStreamWriter() const; - - /** \return true if 'settings' are legal and consistent; - * otherwise, indicate bad settings via 'invalid'. - */ - bool validate(Json::Value* invalid) const; - /** A simple way to update a specific setting. - */ - Value& operator[](std::string key); - - /** Called by ctor, but you can use this to reset settings_. - * \pre 'settings' != NULL (but Json::null is fine) - * \remark Defaults: - * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults - */ - static void setDefaults(Json::Value* settings); -}; - -/** \brief Abstract class for writers. - * \deprecated Use StreamWriter. (And really, this is an implementation detail.) - */ -class JSON_API Writer { -public: - virtual ~Writer(); - - virtual std::string write(const Value& root) = 0; -}; - -/** \brief Outputs a Value in JSON format - *without formatting (not human friendly). - * - * The JSON document is written in a single line. It is not intended for 'human' - *consumption, - * but may be usefull to support feature such as RPC where bandwith is limited. - * \sa Reader, Value - * \deprecated Use StreamWriterBuilder. - */ -class JSON_API FastWriter : public Writer { - -public: - FastWriter(); - virtual ~FastWriter() {} - - void enableYAMLCompatibility(); - - /** \brief Drop the "null" string from the writer's output for nullValues. - * Strictly speaking, this is not valid JSON. But when the output is being - * fed to a browser's Javascript, it makes for smaller output and the - * browser can handle the output just fine. - */ - void dropNullPlaceholders(); - - void omitEndingLineFeed(); - -public: // overridden from Writer - virtual std::string write(const Value& root); - -private: - void writeValue(const Value& value); - - std::string document_; - bool yamlCompatiblityEnabled_; - bool dropNullPlaceholders_; - bool omitEndingLineFeed_; -}; - -/** \brief Writes a Value in JSON format in a - *human friendly way. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per - *line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value - *types, - * and all the values fit on one lines, then print the array on a single - *line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their - *#CommentPlacement. - * - * \sa Reader, Value, Value::setComment() - * \deprecated Use StreamWriterBuilder. - */ -class JSON_API StyledWriter : public Writer { -public: - StyledWriter(); - virtual ~StyledWriter() {} - -public: // overridden from Writer - /** \brief Serialize a Value in JSON format. - * \param root Value to serialize. - * \return String containing the JSON document that represents the root value. - */ - virtual std::string write(const Value& root); - -private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); - void pushValue(const std::string& value); - void writeIndent(); - void writeWithIndent(const std::string& value); - void indent(); - void unindent(); - void writeCommentBeforeValue(const Value& root); - void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); - static std::string normalizeEOL(const std::string& text); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::string document_; - std::string indentString_; - int rightMargin_; - int indentSize_; - bool addChildValues_; -}; - -/** \brief Writes a Value in JSON format in a - human friendly way, - to a stream rather than to a string. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per - line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value - types, - * and all the values fit on one lines, then print the array on a single - line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their - #CommentPlacement. - * - * \param indentation Each level will be indented by this amount extra. - * \sa Reader, Value, Value::setComment() - * \deprecated Use StreamWriterBuilder. - */ -class JSON_API StyledStreamWriter { -public: - StyledStreamWriter(std::string indentation = "\t"); - ~StyledStreamWriter() {} - -public: - /** \brief Serialize a Value in JSON format. - * \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not - * return a value. - */ - void write(std::ostream& out, const Value& root); - -private: - void writeValue(const Value& value); - void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); - void pushValue(const std::string& value); - void writeIndent(); - void writeWithIndent(const std::string& value); - void indent(); - void unindent(); - void writeCommentBeforeValue(const Value& root); - void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); - static std::string normalizeEOL(const std::string& text); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::ostream* document_; - std::string indentString_; - int rightMargin_; - std::string indentation_; - bool addChildValues_ : 1; - bool indented_ : 1; -}; - -#if defined(JSON_HAS_INT64) -std::string JSON_API valueToString(Int value); -std::string JSON_API valueToString(UInt value); -#endif // if defined(JSON_HAS_INT64) -std::string JSON_API valueToString(LargestInt value); -std::string JSON_API valueToString(LargestUInt value); -std::string JSON_API valueToString(double value); -std::string JSON_API valueToString(bool value); -std::string JSON_API valueToQuotedString(const char* value); - -/// \brief Output using the StyledStreamWriter. -/// \see Json::operator>>() -JSON_API std::ostream& operator<<(std::ostream&, const Value& root); - -} // namespace Json - -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#pragma warning(pop) -#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) - -#endif // JSON_WRITER_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/writer.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/assertions.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED -#define CPPTL_JSON_ASSERTIONS_H_INCLUDED - -#include -#include - -#if !defined(JSON_IS_AMALGAMATION) -#include "config.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -/** It should not be possible for a maliciously designed file to - * cause an abort() or seg-fault, so these macros are used only - * for pre-condition violations and internal logic errors. - */ -#if JSON_USE_EXCEPTION - -// @todo <= add detail about condition in exception -# define JSON_ASSERT(condition) \ - {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} - -# define JSON_FAIL_MESSAGE(message) \ - { \ - std::ostringstream oss; oss << message; \ - Json::throwLogicError(oss.str()); \ - abort(); \ - } - -#else // JSON_USE_EXCEPTION - -# define JSON_ASSERT(condition) assert(condition) - -// The call to assert() will show the failure message in debug builds. In -// release builds we abort, for a core-dump or debugger. -# define JSON_FAIL_MESSAGE(message) \ - { \ - std::ostringstream oss; oss << message; \ - assert(false && oss.str().c_str()); \ - abort(); \ - } - - -#endif - -#define JSON_ASSERT_MESSAGE(condition, message) \ - if (!(condition)) { \ - JSON_FAIL_MESSAGE(message); \ - } - -#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/assertions.h -// ////////////////////////////////////////////////////////////////////// - - - - - -#endif //ifndef JSON_AMALGATED_H_INCLUDED diff --git a/docs/Authors.rst b/docs/Authors.rst index 4f60aea2b..f0710fbb3 100644 --- a/docs/Authors.rst +++ b/docs/Authors.rst @@ -19,10 +19,11 @@ Antalia tamarakorr Anuradha Dissanayake falconne Atkana Atkana AtomicChicken AtomicChicken +Bearskie Bearskie belal jimhester Ben Lubar BenLubar Ben Rosser TC01 -brndd brndd +brndd brndd burneddi Bumber Bumber64 Caldfir caldfir Carter Bray Qartar @@ -58,6 +59,7 @@ jj jjyg jj`` John Beisley huin John Shade gsvslto Jonas Ask +Josh Cooper cppcooper coope kane-t kane-t Kelly Kinkade ab9rf Kris Parker kaypy diff --git a/docs/Core.rst b/docs/Core.rst index b15305fa9..4e65f4dda 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -74,6 +74,11 @@ Note that the ``:foo`` syntax for whitespace in arguments is not compatible \ with '+ args'. +.. _dfhack-run: + +dfhack-run +.......... + If DF and DFHack are already running, calling ``dfhack-run my command`` in an external terminal is equivalent to calling ``my command`` in the DFHack console. Direct use of the DFhack console is generally easier, @@ -90,10 +95,10 @@ but ``dfhack-run`` can be useful in a variety of circumstances: Examples:: ./dfhack-run cursecheck - dfhack-run multicmd kill-lua; die + dfhack-run kill-lua The first (\*nix) example `checks for vampires `; the -second (Windows) example uses `kill-lua` to cancel a script and exits. +second (Windows) example uses `kill-lua` to stop a Lua script. Built-in Commands diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 46235e1bf..a2120d41c 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -2404,6 +2404,10 @@ environment by the mandatory init file dfhack.lua: If the argument is a lua table or DF object reference, prints all fields. +* ``printall_recurse(obj)`` + + If the argument is a lua table or DF object reference, prints all fields recursively. + * ``copyall(obj)`` Returns a shallow copy of the table or reference as a lua table. @@ -3517,6 +3521,8 @@ The widget implements: Same as with an ordinary list. +.. _lua-plugins: + ======= Plugins ======= @@ -3901,6 +3907,127 @@ A class with all the tcp functionality. Tries connecting to that address and port. Returns ``client`` object. +.. _cxxrandom: + +cxxrandom +========= + +Exposes some features of the C++11 random number library to Lua. + +Native functions (exported to Lua) +---------------------------------- + +- ``GenerateEngine(seed)`` + + returns engine id + +- ``DestroyEngine(rngID)`` + + destroys corresponding engine + +- ``NewSeed(rngID, seed)`` + + re-seeds engine + +- ``rollInt(rngID, min, max)`` + + generates random integer + +- ``rollDouble(rngID, min, max)`` + + generates random double + +- ``rollNormal(rngID, avg, stddev)`` + + generates random normal[gaus.] + +- ``rollBool(rngID, chance)`` + + generates random boolean + +- ``MakeNumSequence(start, end)`` + + returns sequence id + +- ``AddToSequence(seqID, num)`` + + adds a number to the sequence + +- ``ShuffleSequence(rngID, seqID)`` + + shuffles the number sequence + +- ``NextInSequence(seqID)`` + + returns the next number in sequence + + +Lua plugin functions +-------------------- + +- ``MakeNewEngine(seed)`` + + returns engine id + +Lua plugin classes +------------------ + +``crng`` +~~~~~~~~ + +- ``init(id, df, dist)``: constructor + + - ``id``: Reference ID of engine to use in RNGenerations + - ``df`` (optional): bool indicating whether to destroy the Engine when the crng object is garbage collected + - ``dist`` (optional): lua number distribution to use + +- ``changeSeed(seed)``: alters engine's seed value +- ``setNumDistrib(distrib)``: sets the number distribution crng object should use + + - ``distrib``: number distribution object to use in RNGenerations + +- ``next()``: returns the next number in the distribution +- ``shuffle()``: effectively shuffles the number distribution + +``normal_distribution`` +~~~~~~~~~~~~~~~~~~~~~~~ + +- ``init(avg, stddev)``: constructor +- ``next(id)``: returns next number in the distribution + + - ``id``: engine ID to pass to native function + +``real_distribution`` +~~~~~~~~~~~~~~~~~~~~~ + +- ``init(min, max)``: constructor +- ``next(id)``: returns next number in the distribution + + - ``id``: engine ID to pass to native function + +``int_distribution`` +~~~~~~~~~~~~~~~~~~~~ + +- ``init(min, max)``: constructor +- ``next(id)``: returns next number in the distribution + + - ``id``: engine ID to pass to native function + +``bool_distribution`` +~~~~~~~~~~~~~~~~~~~~~ + +- ``init(min, max)``: constructor +- ``next(id)``: returns next boolean in the distribution + + - ``id``: engine ID to pass to native function + +``num_sequence`` +~~~~~~~~~~~~~~~~ + +- ``init(a, b)``: constructor +- ``add(num)``: adds num to the end of the number sequence +- ``shuffle()``: shuffles the sequence of numbers +- ``next()``: returns next number in the sequence ======= Scripts diff --git a/docs/Plugins.rst b/docs/Plugins.rst index 03ccd809f..5f41bafed 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -2731,3 +2731,14 @@ can easily result in inconsistent state once this plugin is available again. The effects may be as weird as negative power being generated. +======= +Lua API +======= + +Some plugins consist solely of native libraries exposed to Lua. They are listed +in the `lua-api` file under `lua-plugins`: + +* `eventful` +* `building-hacks` +* `luasocket` +* `cxxrandom` diff --git a/docs/changelog.txt b/docs/changelog.txt index c377a8c1c..0d7235ba1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,32 +37,146 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ================================================================================ # Future +## Fixes +- `stonesense`: fixed ``PLANT:DESERT_LIME:LEAF`` typo + +## API +- Added C++-style linked list interface for DF linked lists + +================================================================================ +# 0.44.11-beta2.1 + +## Internals +-@ `stonesense`: fixed build + +================================================================================ +# 0.44.11-beta2 + +## Fixes +-@ Windows: Fixed console failing to initialize +- `command-prompt`: added support for commands that require a specific screen to be visible, e.g. `spotclean` +- `gui/workflow`: fixed advanced constraint menu for crafts + +## API +- Added ``Screen::Hide`` to temporarily hide screens, like `command-prompt` + +================================================================================ +# 0.44.11-beta1 + +## Fixes +- Fixed displayed names (from ``Units::getVisibleName``) for units with identities +- Fixed potential memory leak in ``Screen::show()`` +- `fix/dead-units`: fixed script trying to use missing isDiplomat function + +## Misc Improvements +- Console: + - added support for multibyte characters on Linux/macOS + - made the console exit properly when an interactive command is active (`liquids`, `mode`, `tiletypes`) +- Made the ``DFHACK_PORT`` environment variable take priority over ``remote-server.json`` +- Linux: added automatic support for GCC sanitizers in ``dfhack`` script +- `digfort`: added better map bounds checking +- `dfhack-run`: added support for port specified in ``remote-server.json``, to match DFHack's behavior +- `remove-stress`: + - added support for ``-all`` as an alternative to the existing ``all`` argument for consistency + - sped up significantly + - improved output/error messages + - now removes tantrums, depression, and obliviousness +- `ruby`: sped up handling of onupdate events + +## API +- New functions: + - ``Units::isDiplomat(unit)`` +- Exposed ``Screen::zoom()`` to C++ (was Lua-only) + +## Lua +- Added ``printall_recurse`` to print tables and DF references recursively. It can be also used with ``^`` from the `lua` interpreter. +- ``gui.widgets``: ``List:setChoices`` clones ``choices`` for internal table changes + +## Internals +- jsoncpp: updated to version 1.8.4 and switched to using a git submodule + +## Structures +- ``history_event_entity_expels_hfst``: added (new in 0.44.11) +- ``history_event_site_surrenderedst``: added (new in 0.44.11) +- ``history_event_type``: added ``SITE_SURRENDERED``, ``ENTITY_EXPELS_HF`` (new in 0.44.11) +- ``syndrome``: identified a few fields +- ``viewscreen_civlistst``: fixed layout and identified many fields + +================================================================================ +# 0.44.11-alpha1 + +## Structures +- Dropped 0.44.10 support +- Added support for automatically sizing arrays indexed with an enum +- Removed stale generated CSV files and DT layouts from pre-0.43.05 +- ``announcement_type``: new in 0.44.11: ``NEW_HOLDING``, ``NEW_MARKET_LINK`` +- ``breath_attack_type``: added ``OTHER`` +- ``historical_figure_info.relationships.list``: added ``unk_3a``-``unk_3c`` fields at end +- ``interface_key``: added bindings new in 0.44.11 +- ``occupation_type``: new in 0.44.11: ``MESSENGER`` +- ``profession``: new in 0.44.11: ``MESSENGER`` +- ``ui_sidebar_menus``: + - ``unit.in_squad``: renamed to ``unit.squad_list_opened``, fixed location + - ``unit``: added ``expel_error`` and other unknown fields new in 0.44.11 + - ``hospital``: added, new in 0.44.11 + - ``num_speech_tokens``, ``unk_17d8``: moved out of ``command_line`` to fix layout on x64 +-@ ``viewscreen_civlistst``: added a few new fields (incomplete) +- ``viewscreen_locationsst``: identified ``edit_input`` + +================================================================================ +# 0.44.10-r2 + +## New Plugins +- `cxxrandom`: exposes some features of the C++11 random number library to Lua + +## New Scripts +- `gui/stamper`: allows manipulation of designations by transforms such as translations, reflections, rotations, and inversion +- `add-recipe`: adds unknown crafting recipes to the player's civ + ## Fixes - Fixed many tools incorrectly using the ``dead`` unit flag (they should generally check ``flags2.killed`` instead) +- Fixed many tools passing incorrect arguments to printf-style functions, including a few possible crashes (`changelayer`, `follow`, `forceequip`, `generated-creature-renamer`) - Fixed ``-g`` flag (GDB) in Linux ``dfhack`` script (particularly on x64) +- Fixed several bugs in Lua scripts found by static analysis (df-luacheck) - `autochop`, `autodump`, `autogems`, `automelt`, `autotrade`, `buildingplan`, `dwarfmonitor`, `fix-unit-occupancy`, `fortplan`, `stockflow`: fix issues with periodic tasks not working for some time after save/load cycles -- `buildingplan`, `fortplan`: stop running before a world has fully loaded +- `autogems`, `fix-unit-occupancy`: stopped running when a fort isn't loaded (e.g. while embarking) +- `buildingplan`, `fortplan`: stopped running before a world has fully loaded - `autogems`: - stop running repeatedly when paused - fixed crash when furnaces are linked to same stockpiles as jeweler's workshops - `ban-cooking`: fixed errors introduced by kitchen structure changes in 0.44.10-r1 - `remove-stress`: fixed an error when running on soul-less units (e.g. with ``-all``) - `revflood`: stopped revealing tiles adjacent to tiles above open space inappropriately +- `dig`: fixed "Inappropriate dig square" announcements if digging job has been posted +- `stockpiles`: ``loadstock`` now sets usable and unusable weapon and armor settings +- `stocks`: stopped listing carried items under stockpiles where they were picked up from +- `deramp`: fixed deramp to find designations that already have jobs posted +- `fixnaked`: fixed errors due to emotion changes in 0.44 +- `autounsuspend`: now skips planned buildings ## Misc Improvements - Added script name to messages produced by ``qerror()`` in Lua scripts - Fixed an issue in around 30 scripts that could prevent edits to the files (adding valid arguments) from taking effect - Linux: Added several new options to ``dfhack`` script: ``--remotegdb``, ``--gdbserver``, ``--strace`` +- `bodyswap`: improved error handling - `buildingplan`: added max quality setting - `caravan`: documented (new in 0.44.10-alpha1) +- `deathcause`: added "slaughtered" to descriptions - `fix/retrieve-units`: now re-adds units to active list to counteract `fix/dead-units` +- `item-descriptions`: fixed several grammatical errors +- `modtools/create-unit`: + - added quantity argument + - now selects a caste at random if none is specified - `mousequery`: - migrated several features from TWBT's fork - added ability to drag with left/right buttons - added depth display for TWBT (when multilevel is enabled) - made shift+click jump to lower levels visible with TWBT - `title-version`: added version to options screen too -- `remotefortressreader`: added build menu information +- `embark-assistant`: + - changed region interaction matching to search for evil rain, syndrome rain, and reanimation rather than interaction presence (misleadingly called evil weather), reanimation, and thralling + - gave syndrome rain and reanimation wider ranges of criterion values +- `fix/dead-units`: added a delay of around 1 month before removing units ## API - New functions (also exposed to Lua): @@ -75,10 +189,22 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Added ``profiler`` module to measure lua performance - Enabled shift+cursor movement in WorkshopOverlay-derived screens +## Structures +- ``unit_flags1``: renamed ``dead`` to ``inactive`` to better reflect its use +- ``item_body_component``: fixed location of ``corpse_flags`` +- ``job_type``: added ``is_designation`` attribute +- ``unit_thought_type``: added ``SawDeadBody`` (new in 0.44.10) +- ``unit_personality``: fixed location of ``current_focus`` and ``undistracted_focus`` +- ``incident_sub6_performance``: identified some fields +- ``job_handler``: fixed static array layout + ## Internals - Added fallback for YouCompleteMe database lookup failures (e.g. for newly-created files) - jsoncpp: fixed constructor with ``long`` on Linux +- Improved efficiency and error handling in ``stl_vsprintf`` and related functions +- Added build option to generate symbols for large generated files containing df-structures metadata +================================================================================ # 0.44.10-r1 ## New Scripts @@ -134,6 +260,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``viewscreen_kitchenprefst.forbidden``, ``possible``: now a bitfield, ``kitchen_pref_flag`` - ``world_data.feature_map``: added extensive documentation (in XML) +================================================================================ # 0.44.10-beta1 ## New Scripts @@ -167,6 +294,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Added function names to DFHack's NullPointer and InvalidArgument exceptions - Linux: required plugins to have symbols resolved at link time, for consistency with other platforms +================================================================================ # 0.44.10-alpha1 ## New Scripts @@ -174,6 +302,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/autogems`: a configuration UI for the `autogems` plugin ## Fixes +- Fixed uninitialized pointer being returned from ``Gui::getAnyUnit()`` in rare cases +- `autohauler`, `autolabor`, `labormanager`: fixed fencepost error and potential crash +- `dwarfvet`: fixed infinite loop if an animal is not accepted at a hospital - `liquids`: fixed "range" command to default to 1 for dimensions consistently - `search`: fixed 4/6 keys in unit screen search - `view-item-info`: fixed an error with some armor diff --git a/docs/gen_changelog.py b/docs/gen_changelog.py index d042ca2e7..caca138e8 100644 --- a/docs/gen_changelog.py +++ b/docs/gen_changelog.py @@ -15,9 +15,9 @@ CHANGELOG_SECTIONS = [ 'Removed', 'API', 'Internals', - 'Structures', 'Lua', 'Ruby', + 'Structures', ] REPLACEMENTS = { diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 2047b3615..390efdcfe 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -267,14 +267,15 @@ ADD_CUSTOM_COMMAND( ) ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml) - -IF(UNIX) - # Don't produce debug info for generated stubs - SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES} - PROPERTIES COMPILE_FLAGS "-g0 -O1") -ELSE(WIN32) - SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES} - PROPERTIES COMPILE_FLAGS "/O1 /bigobj") +IF(REMOVE_SYMBOLS_FROM_DF_STUBS) + IF(UNIX) + # Don't produce debug info for generated stubs + SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES} + PROPERTIES COMPILE_FLAGS "-g0 -O1") + ELSE(WIN32) + SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES} + PROPERTIES COMPILE_FLAGS "/O1 /bigobj") + ENDIF() ENDIF() # Compilation @@ -365,10 +366,10 @@ IF(APPLE) SET_TARGET_PROPERTIES(dfhack PROPERTIES SOVERSION 1.0.0) ENDIF() -TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua jsoncpp dfhack-version ${PROJECT_LIBS}) +TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua jsoncpp_lib_static dfhack-version ${PROJECT_LIBS}) SET_TARGET_PROPERTIES(dfhack PROPERTIES INTERFACE_LINK_LIBRARIES "") -TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket) +TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket jsoncpp_lib_static) TARGET_LINK_LIBRARIES(dfhack-run dfhack-client) if(APPLE) diff --git a/library/Console-posix.cpp b/library/Console-posix.cpp index ef74e67d2..6602b0d0b 100644 --- a/library/Console-posix.cpp +++ b/library/Console-posix.cpp @@ -60,6 +60,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#ifdef HAVE_CUCHAR +#include +#else +#include +#endif // George Vulov for MacOSX #ifndef __LINUX__ @@ -133,6 +138,68 @@ const char * getANSIColor(const int c) } } + +#ifdef HAVE_CUCHAR +// Use u32string for GCC 6 and later and msvc to allow potable implementation +using u32string = std::u32string; +using std::c32rtomb; +using std::mbrtoc32; +#else +// Fallback for gcc 4 and 5 that don't have cuchar header +// But wchar_t is 4 bytes that is a good fallback implementation +using u32string = std::wstring; +size_t mbrtoc32(u32string::value_type* c, + const char* s, + std::size_t n, + std::mbstate_t* ps) +{ + return std::mbrtowc(c, s, n, ps); +} + +size_t c32rtomb(char* mb, + u32string::value_type c, + std::mbstate_t* ps) +{ + return std::wcrtomb(mb, c, ps); +} +#endif + +//! Convert a locale defined multibyte coding to UTF-32 string for easier +//! character processing. +static u32string fromLocaleMB(const std::string& str) +{ + u32string rv; + u32string::value_type ch; + size_t pos = 0; + ssize_t sz; + std::mbstate_t state{}; + while ((sz = mbrtoc32(&ch,&str[pos], str.size() - pos, &state)) != 0) { + if (sz == -1 || sz == -2) + break; + rv.push_back(ch); + if (sz == -3) /* multi value character */ + continue; + pos += sz; + } + return rv; +} + +//! Convert a UTF-32 string back to locale defined multibyte coding. +static std::string toLocaleMB(const u32string& wstr) +{ + std::stringstream ss{}; + char mb[MB_CUR_MAX]; + std::mbstate_t state{}; + const size_t err = -1; + for (auto ch: wstr) { + size_t sz = c32rtomb(mb, ch, &state); + if (sz == err) + break; + ss.write(mb, sz); + } + return ss.str(); +} + namespace DFHack { class Private @@ -157,7 +224,7 @@ namespace DFHack FD_SET(STDIN_FILENO, &descriptor_set); FD_SET(exit_pipe[0], &descriptor_set); int ret = TMP_FAILURE_RETRY( - select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL) + select (std::max(STDIN_FILENO,exit_pipe[0])+1,&descriptor_set, NULL, NULL, NULL) ); if(ret == -1) return false; @@ -356,15 +423,15 @@ namespace DFHack int count; if (enable_raw() == -1) return 0; if(state == con_lineedit) - return -1; + return Console::FAILURE; state = con_lineedit; count = prompt_loop(lock,ch); state = con_unclaimed; disable_raw(); print("\n"); - if(count != -1) + if(count > Console::FAILURE) { - output = raw_buffer; + output = toLocaleMB(raw_buffer); } return count; } @@ -374,9 +441,9 @@ namespace DFHack struct termios raw; if (!supported_terminal) - return -1; + return Console::FAILURE; if (tcgetattr(STDIN_FILENO,&orig_termios) == -1) - return -1; + return Console::FAILURE; raw = orig_termios; //modify the original mode // input modes: no break, no CR to NL, no parity check, no strip char, @@ -398,7 +465,7 @@ namespace DFHack raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer // put terminal in raw mode if (tcsetattr(STDIN_FILENO, TCSADRAIN, &raw) < 0) - return -1; + return Console::FAILURE; rawmode = 1; return 0; } @@ -414,26 +481,24 @@ namespace DFHack char seq[64]; int cols = get_columns(); int plen = prompt.size(); - const char * buf = raw_buffer.c_str(); int len = raw_buffer.size(); + int begin = 0; int cooked_cursor = raw_cursor; - // Use math! This is silly. - while((plen+cooked_cursor) >= cols) - { - buf++; - len--; - cooked_cursor--; - } - while (plen+len > cols) + if ((plen+cooked_cursor) >= cols) { - len--; + begin = plen+cooked_cursor-cols-1; + len -= plen+cooked_cursor-cols-1; + cooked_cursor -= plen+cooked_cursor-cols-1; } + if (plen+len > cols) + len -= plen+len - cols; + std::string mbstr = toLocaleMB(raw_buffer.substr(begin,len)); /* Cursor to left edge */ snprintf(seq,64,"\x1b[1G"); if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; /* Write the prompt and the current buffer content */ if (::write(STDIN_FILENO,prompt.c_str(),plen) == -1) return; - if (::write(STDIN_FILENO,buf,len) == -1) return; + if (::write(STDIN_FILENO,mbstr.c_str(),mbstr.length()) == -1) return; /* Erase to right */ snprintf(seq,64,"\x1b[0K"); if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return; @@ -453,7 +518,8 @@ namespace DFHack * initially is just an empty string. */ const std::string empty; history.add(empty); - if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1; + if (::write(fd,prompt.c_str(),prompt.size()) == -1) + return Console::FAILURE; while(1) { unsigned char c; @@ -463,7 +529,7 @@ namespace DFHack if(!read_char(c)) { lock->lock(); - return -2; + return Console::SHUTDOWN; } lock->lock(); /* Only autocomplete when the callback is set. It returns < 0 when @@ -496,8 +562,7 @@ namespace DFHack history.remove(); return raw_buffer.size(); case 3: // ctrl-c - errno = EAGAIN; - return -1; + return Console::RETRY; case 127: // backspace case 8: // ctrl-h if (raw_cursor > 0 && raw_buffer.size() > 0) @@ -512,7 +577,7 @@ namespace DFHack if (!read_char(seq[0])) { lock->lock(); - return -2; + return Console::SHUTDOWN; } lock->lock(); if (seq[0] == 'b') @@ -528,7 +593,7 @@ namespace DFHack if (!read_char(seq[1])) { lock->lock(); - return -2; + return Console::SHUTDOWN; } if (seq[1] == 'D') { @@ -555,7 +620,7 @@ namespace DFHack { /* Update the current history entry before to * overwrite it with tne next one. */ - history[history_index] = raw_buffer; + history[history_index] = toLocaleMB(raw_buffer); /* Show the new entry */ history_index += (seq[1] == 'A') ? 1 : -1; if (history_index < 0) @@ -568,7 +633,7 @@ namespace DFHack history_index = history.size()-1; break; } - raw_buffer = history[history_index]; + raw_buffer = fromLocaleMB(history[history_index]); raw_cursor = raw_buffer.size(); prompt_refresh(); } @@ -593,7 +658,7 @@ namespace DFHack if(!read_char(seq2)) { lock->lock(); - return -2; + return Console::SHUTDOWN; } lock->lock(); if (seq[1] == '3' && seq2 == '~' ) @@ -609,7 +674,7 @@ namespace DFHack if (!read_char(seq3[0]) || !read_char(seq3[1])) { lock->lock(); - return -2; + return Console::SHUTDOWN; } if (seq2 == ';') { @@ -672,15 +737,31 @@ namespace DFHack default: if (c >= 32) // Space { + u32string::value_type c32; + char mb[MB_CUR_MAX]; + size_t count = 1; + mb[0] = c; + ssize_t sz; + std::mbstate_t state{}; + // Read all bytes belonging to a multi byte + // character starting from the first bye already red + while ((sz = mbrtoc32(&c32,&mb[count-1],1, &state)) < 0) { + if (sz == -1 || sz == -3) + return Console::FAILURE; /* mbrtoc32 error (not valid utf-32 character */ + if(!read_char(c)) + return Console::SHUTDOWN; + mb[count++] = c; + } if (raw_buffer.size() == size_t(raw_cursor)) { - raw_buffer.append(1,c); + raw_buffer.append(1,c32); raw_cursor++; if (plen+raw_buffer.size() < size_t(get_columns())) { /* Avoid a full update of the line in the * trivial case. */ - if (::write(fd,&c,1) == -1) return -1; + if (::write(fd,mb,count) == -1) + return Console::FAILURE; } else { @@ -689,7 +770,7 @@ namespace DFHack } else { - raw_buffer.insert(raw_cursor,1,c); + raw_buffer.insert(raw_cursor,1,c32); raw_cursor++; prompt_refresh(); } @@ -712,8 +793,8 @@ namespace DFHack } state; bool in_batch; std::string prompt; // current prompt string - std::string raw_buffer; // current raw mode buffer - std::string yank_buffer; // last text deleted with Ctrl-K/Ctrl-U + u32string raw_buffer; // current raw mode buffer + u32string yank_buffer; // last text deleted with Ctrl-K/Ctrl-U int raw_cursor; // cursor position in the buffer // thread exit mechanism int exit_pipe[2]; @@ -730,8 +811,7 @@ Console::Console() } Console::~Console() { - if(inited) - shutdown(); + assert(!inited); if(wlock) delete wlock; if(d) @@ -768,11 +848,6 @@ bool Console::shutdown(void) if(!d) return true; lock_guard g(*wlock); - if(d->rawmode) - d->disable_raw(); - d->print("\n"); - inited = false; - // kill the thing close(d->exit_pipe[1]); return true; } @@ -814,7 +889,7 @@ void Console::add_text(color_value color, const std::string &text) int Console::get_columns(void) { lock_guard g(*wlock); - int ret = -1; + int ret = Console::FAILURE; if(inited) ret = d->get_columns(); return ret; @@ -823,7 +898,7 @@ int Console::get_columns(void) int Console::get_rows(void) { lock_guard g(*wlock); - int ret = -1; + int ret = Console::FAILURE; if(inited) ret = d->get_rows(); return ret; @@ -853,9 +928,17 @@ void Console::cursor(bool enable) int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch) { lock_guard g(*wlock); - int ret = -2; - if(inited) + int ret = Console::SHUTDOWN; + if(inited) { ret = d->lineedit(prompt,output,wlock,ch); + if (ret == Console::SHUTDOWN) { + // kill the thing + if(d->rawmode) + d->disable_raw(); + d->print("\n"); + inited = false; + } + } return ret; } diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index 3caacd924..d339e53a0 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -285,7 +285,10 @@ namespace DFHack INPUT_RECORD rec; DWORD count; lock->unlock(); - ReadConsoleInputA(console_in, &rec, 1, &count); + if (ReadConsoleInputA(console_in, &rec, 1, &count) == 0) { + lock->lock(); + return Console::SHUTDOWN; + } lock->lock(); if (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown) continue; @@ -379,7 +382,7 @@ namespace DFHack state = con_lineedit; this->prompt = prompt; count = prompt_loop(lock, ch); - if(count != -1) + if(count > Console::FAILURE) output = raw_buffer; state = con_unclaimed; print("\n"); @@ -588,7 +591,7 @@ void Console::cursor(bool enable) int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch) { wlock->lock(); - int ret = -2; + int ret = Console::SHUTDOWN; if(inited) ret = d->lineedit(prompt,output,wlock,ch); wlock->unlock(); diff --git a/library/Core.cpp b/library/Core.cpp index 49aa61ecd..479eaea08 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -77,7 +77,9 @@ using namespace DFHack; #include #include #include -#include "tinythread.h" +#include +#include +#include #include "md5wrapper.h" #include "SDL_events.h" @@ -86,7 +88,6 @@ using namespace DFHack; #include #endif -using namespace tthread; using namespace df::enums; using df::global::init; using df::global::world; @@ -96,48 +97,41 @@ using df::global::world; static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus = NULL); size_t loadScriptFiles(Core* core, color_ostream& out, const vector& prefix, const std::string& folder); -struct Core::Cond -{ - Cond() - { - predicate = false; - wakeup = new tthread::condition_variable(); - } - ~Cond() - { - delete wakeup; - } - bool Lock(tthread::mutex * m) - { - while(!predicate) - { - wakeup->wait(*m); - } - predicate = false; - return true; - } - bool Unlock() - { - predicate = true; - wakeup->notify_one(); - return true; +namespace DFHack { +struct MainThread { + //! MainThread::suspend keeps the main DF thread suspended from Core::Init to + //! thread exit. + static CoreSuspenderBase& suspend() { + static thread_local CoreSuspenderBase lock(std::defer_lock); + return lock; } - tthread::condition_variable * wakeup; - bool predicate; }; +} -struct Core::Private +CoreSuspendReleaseMain::CoreSuspendReleaseMain() { - tthread::mutex AccessMutex; - tthread::mutex StackMutex; - std::stack suspended_tools; - Core::Cond core_cond; - thread::id df_suspend_thread; - int df_suspend_depth; + MainThread::suspend().unlock(); +} - Private() { - df_suspend_depth = 0; - } +CoreSuspendReleaseMain::~CoreSuspendReleaseMain() +{ + MainThread::suspend().lock(); +} + +CoreSuspendClaimMain::CoreSuspendClaimMain() +{ + MainThread::suspend().lock(); +} + +CoreSuspendClaimMain::~CoreSuspendClaimMain() +{ + MainThread::suspend().unlock(); +} + +struct Core::Private +{ + std::thread iothread; + std::thread hotkeythread; }; struct CommandDepthCounter @@ -227,9 +221,10 @@ void fHKthread(void * iodata) cerr << "Hotkey thread has croaked." << endl; return; } - while(1) + bool keep_going = true; + while(keep_going) { - std::string stuff = core->getHotkeyCmd(); // waits on mutex! + std::string stuff = core->getHotkeyCmd(keep_going); // waits on mutex! if(!stuff.empty()) { color_ostream_proxy out(core->getConsole()); @@ -510,7 +505,7 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std:: bool Core::addScriptPath(string path, bool search_before) { - lock_guard lock(*script_path_mutex); + lock_guard lock(script_path_mutex); vector &vec = script_paths[search_before ? 0 : 1]; if (std::find(vec.begin(), vec.end(), path) != vec.end()) return false; @@ -522,7 +517,7 @@ bool Core::addScriptPath(string path, bool search_before) bool Core::removeScriptPath(string path) { - lock_guard lock(*script_path_mutex); + lock_guard lock(script_path_mutex); bool found = false; for (int i = 0; i < 2; i++) { @@ -541,7 +536,7 @@ bool Core::removeScriptPath(string path) void Core::getScriptPaths(std::vector *dest) { - lock_guard lock(*script_path_mutex); + lock_guard lock(script_path_mutex); dest->clear(); string df_path = this->p->getPath(); for (auto it = script_paths[0].begin(); it != script_paths[0].end(); ++it) @@ -1339,7 +1334,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v con.printerr("that is not loaded - try \"load %s\" or check stderr.log\n", first.c_str()); else if (p->size()) - con.printerr("that implements %i commands - see \"ls %s\" for details\n", + con.printerr("that implements %zi commands - see \"ls %s\" for details\n", p->size(), first.c_str()); else con.printerr("but does not implement any commands\n"); @@ -1449,13 +1444,15 @@ void fIOthread(void * iodata) while (true) { string command = ""; - int ret = con.lineedit("[DFHack]# ",command, main_history); - if(ret == -2) + int ret; + while ((ret = con.lineedit("[DFHack]# ",command, main_history)) + == Console::RETRY); + if(ret == Console::SHUTDOWN) { cerr << "Console is shutting down properly." << endl; return; } - else if(ret == -1) + else if(ret == Console::FAILURE) { cerr << "Console caught an unspecified error." << endl; continue; @@ -1480,10 +1477,33 @@ void fIOthread(void * iodata) } } -Core::Core() +Core::~Core() { - d = new Private(); + if (MainThread::suspend().owns_lock()) + MainThread::suspend().unlock(); + + if (d->hotkeythread.joinable()) { + std::lock_guard lock(HotkeyMutex); + hotkey_set = SHUTDOWN; + HotkeyCond.notify_one(); + } + if (d->iothread.joinable()) + con.shutdown(); + delete d; +} +Core::Core() : + d{new Private}, + script_path_mutex{}, + HotkeyMutex{}, + HotkeyCond{}, + alias_mutex{}, + misc_data_mutex{}, + CoreSuspendMutex{}, + CoreWakeup{}, + ownerThread{}, + toolCount{0} +{ // init the console. This must be always the first step! plug_mgr = 0; vif = 0; @@ -1494,11 +1514,7 @@ Core::Core() memset(&(s_mods), 0, sizeof(s_mods)); // set up hotkey capture - hotkey_set = false; - HotkeyMutex = 0; - HotkeyCond = 0; - alias_mutex = 0; - misc_data_mutex = 0; + hotkey_set = NO; last_world_data_ptr = NULL; last_local_map_ptr = NULL; last_pause_state = false; @@ -1508,7 +1524,6 @@ Core::Core() color_ostream::log_errors_to_stderr = true; - script_path_mutex = new mutex(); }; void Core::fatal (std::string output) @@ -1552,6 +1567,10 @@ bool Core::Init() if(errorstate) return false; + // Lock the CoreSuspendMutex until the thread exits or call Core::Shutdown + // Core::Update will temporary unlock when there is any commands queued + MainThread::suspend().lock(); + // Re-route stdout and stderr again - DF seems to set up stdout and // stderr.txt on Windows as of 0.43.05. Also, log before switching files to // make it obvious what's going on if someone checks the *.txt files. @@ -1642,7 +1661,6 @@ bool Core::Init() // Init global object pointers df::global::InitGlobals(); - alias_mutex = new recursive_mutex(); cerr << "Initializing Console.\n"; // init the console. @@ -1732,7 +1750,6 @@ bool Core::Init() } // create mutex for syncing with interactive tasks - misc_data_mutex=new mutex(); cerr << "Initializing Plugins.\n"; // create plugin manager plug_mgr = new PluginManager(this); @@ -1741,27 +1758,21 @@ bool Core::Init() temp->core = this; temp->plug_mgr = plug_mgr; - HotkeyMutex = new mutex(); - HotkeyCond = new condition_variable(); - if (!is_text_mode || is_headless) { cerr << "Starting IO thread.\n"; // create IO thread - thread * IO = new thread(fIOthread, (void *) temp); - (void)IO; + d->iothread = std::thread{fIOthread, (void*)temp}; } else { - cerr << "Starting dfhack.init thread.\n"; - thread * init = new thread(fInitthread, (void *) temp); - (void)init; + std::cerr << "Starting dfhack.init thread.\n"; + d->iothread = std::thread{fInitthread, (void*)temp}; } cerr << "Starting DF input capture thread.\n"; // set up hotkey capture - thread * HK = new thread(fHKthread, (void *) temp); - (void)HK; + d->hotkeythread = std::thread(fHKthread, (void *) temp); screen_window = new Windows::top_level_window(); screen_window->addChild(new Windows::dfhack_dummy(5,10)); started = true; @@ -1775,7 +1786,7 @@ bool Core::Init() if (df::global::ui_sidebar_menus) { vector args; - const string & raw = df::global::ui_sidebar_menus->command_line.raw; + const string & raw = df::global::ui_sidebar_menus->command_line.original; size_t offset = 0; while (offset < raw.size()) { @@ -1840,28 +1851,25 @@ bool Core::Init() bool Core::setHotkeyCmd( std::string cmd ) { // access command - HotkeyMutex->lock(); - { - hotkey_set = true; - hotkey_cmd = cmd; - HotkeyCond->notify_all(); - } - HotkeyMutex->unlock(); + std::lock_guard lock(HotkeyMutex); + hotkey_set = SET; + hotkey_cmd = cmd; + HotkeyCond.notify_all(); return true; } /// removes the hotkey command and gives it to the caller thread -std::string Core::getHotkeyCmd( void ) +std::string Core::getHotkeyCmd( bool &keep_going ) { string returner; - HotkeyMutex->lock(); - while ( ! hotkey_set ) - { - HotkeyCond->wait(*HotkeyMutex); + std::unique_lock lock(HotkeyMutex); + HotkeyCond.wait(lock, [this]() -> bool {return this->hotkey_set;}); + if (hotkey_set == SHUTDOWN) { + keep_going = false; + return returner; } - hotkey_set = false; + hotkey_set = NO; returner = hotkey_cmd; hotkey_cmd.clear(); - HotkeyMutex->unlock(); return returner; } @@ -1887,82 +1895,29 @@ void Core::printerr(const char *format, ...) void Core::RegisterData( void *p, std::string key ) { - misc_data_mutex->lock(); + std::lock_guard lock(misc_data_mutex); misc_data_map[key] = p; - misc_data_mutex->unlock(); } void *Core::GetData( std::string key ) { - misc_data_mutex->lock(); + std::lock_guard lock(misc_data_mutex); std::map::iterator it=misc_data_map.find(key); if ( it != misc_data_map.end() ) { void *p=it->second; - misc_data_mutex->unlock(); return p; } else { - misc_data_mutex->unlock(); return 0;// or throw an error. } } bool Core::isSuspended(void) { - lock_guard lock(d->AccessMutex); - - return (d->df_suspend_depth > 0 && d->df_suspend_thread == this_thread::get_id()); -} - -void Core::Suspend() -{ - auto tid = this_thread::get_id(); - - // If recursive, just increment the count - { - lock_guard lock(d->AccessMutex); - - if (d->df_suspend_depth > 0 && d->df_suspend_thread == tid) - { - d->df_suspend_depth++; - return; - } - } - - // put the condition on a stack - Core::Cond *nc = new Core::Cond(); - - { - lock_guard lock2(d->StackMutex); - - d->suspended_tools.push(nc); - } - - // wait until Core::Update() wakes up the tool - { - lock_guard lock(d->AccessMutex); - - nc->Lock(&d->AccessMutex); - - assert(d->df_suspend_depth == 0); - d->df_suspend_thread = tid; - d->df_suspend_depth = 1; - } -} - -void Core::Resume() -{ - auto tid = this_thread::get_id(); - lock_guard lock(d->AccessMutex); - - assert(d->df_suspend_depth > 0 && d->df_suspend_thread == tid); - (void)tid; - - if (--d->df_suspend_depth == 0) - d->core_cond.Unlock(); + return ownerThread.load() == std::this_thread::get_id(); } int Core::TileUpdate() @@ -1973,40 +1928,6 @@ int Core::TileUpdate() return true; } -int Core::ClaimSuspend(bool force_base) -{ - auto tid = this_thread::get_id(); - lock_guard lock(d->AccessMutex); - - if (force_base || d->df_suspend_depth <= 0) - { - assert(d->df_suspend_depth == 0); - - d->df_suspend_thread = tid; - d->df_suspend_depth = 1000000; - return 1000000; - } - else - { - assert(d->df_suspend_thread == tid); - return ++d->df_suspend_depth; - } -} - -void Core::DisclaimSuspend(int level) -{ - auto tid = this_thread::get_id(); - lock_guard lock(d->AccessMutex); - - assert(d->df_suspend_depth == level && d->df_suspend_thread == tid); - (void)tid; - - if (level == 1000000) - d->df_suspend_depth = 0; - else - --d->df_suspend_depth; -} - void Core::doUpdate(color_ostream &out, bool first_update) { Lua::Core::Reset(out, "DF code execution"); @@ -2109,8 +2030,6 @@ int Core::Update() // Pretend this thread has suspended the core in the usual way, // and run various processing hooks. { - CoreSuspendClaimer suspend(true); - // Initialize the core bool first_update = false; @@ -2126,27 +2045,9 @@ int Core::Update() doUpdate(out, first_update); } - // wake waiting tools - // do not allow more tools to join in while we process stuff here - lock_guard lock_stack(d->StackMutex); - - while (!d->suspended_tools.empty()) - { - Core::Cond * nc = d->suspended_tools.top(); - d->suspended_tools.pop(); - - lock_guard lock(d->AccessMutex); - // wake tool - nc->Unlock(); - // wait for tool to wake us - d->core_cond.Lock(&d->AccessMutex); - // verify - assert(d->df_suspend_depth == 0); - // destroy condition - delete nc; - // check lua stack depth - Lua::Core::Reset(out, "suspend"); - } + // Let all commands run that require CoreSuspender + CoreWakeup.wait(MainThread::suspend(), + [this]() -> bool {return this->toolCount.load() == 0;}); return 0; }; @@ -2360,12 +2261,31 @@ void Core::onStateChange(color_ostream &out, state_change_event event) handleLoadAndUnloadScripts(out, event); } -// FIXME: needs to terminate the IO threads and properly dismantle all the machinery involved. int Core::Shutdown ( void ) { if(errorstate) return true; errorstate = 1; + + // Make sure we release main thread if this is called from main thread + if (MainThread::suspend().owns_lock()) + MainThread::suspend().unlock(); + + // Make sure the console thread shutdowns before clean up to avoid any + // unlikely data races. + if (d->iothread.joinable()) { + con.shutdown(); + } + + if (d->hotkeythread.joinable()) { + std::unique_lock hot_lock(HotkeyMutex); + hotkey_set = SHUTDOWN; + HotkeyCond.notify_one(); + } + + d->hotkeythread.join(); + d->iothread.join(); + CoreSuspendClaimer suspend; if(plug_mgr) { @@ -2379,7 +2299,6 @@ int Core::Shutdown ( void ) } allModules.clear(); memset(&(s_mods), 0, sizeof(s_mods)); - con.shutdown(); return -1; } @@ -2530,7 +2449,7 @@ bool Core::SelectHotkey(int sym, int modifiers) std::string cmd; { - tthread::lock_guard lock(*HotkeyMutex); + std::lock_guard lock(HotkeyMutex); // Check the internal keybindings std::vector &bindings = key_bindings[sym]; @@ -2629,7 +2548,7 @@ bool Core::ClearKeyBindings(std::string keyspec) if (!parseKeySpec(keyspec, &sym, &mod, &focus)) return false; - tthread::lock_guard lock(*HotkeyMutex); + std::lock_guard lock(HotkeyMutex); std::vector &bindings = key_bindings[sym]; for (int i = bindings.size()-1; i >= 0; --i) { @@ -2668,7 +2587,7 @@ bool Core::AddKeyBinding(std::string keyspec, std::string cmdline) if (binding.command.empty()) return false; - tthread::lock_guard lock(*HotkeyMutex); + std::lock_guard lock(HotkeyMutex); // Don't add duplicates std::vector &bindings = key_bindings[sym]; @@ -2692,7 +2611,7 @@ std::vector Core::ListKeyBindings(std::string keyspec) if (!parseKeySpec(keyspec, &sym, &mod, &focus)) return rv; - tthread::lock_guard lock(*HotkeyMutex); + std::lock_guard lock(HotkeyMutex); std::vector &bindings = key_bindings[sym]; for (int i = bindings.size()-1; i >= 0; --i) { @@ -2712,7 +2631,7 @@ std::vector Core::ListKeyBindings(std::string keyspec) bool Core::AddAlias(const std::string &name, const std::vector &command, bool replace) { - tthread::lock_guard lock(*alias_mutex); + std::lock_guard lock(alias_mutex); if (!IsAlias(name) || replace) { aliases[name] = command; @@ -2723,7 +2642,7 @@ bool Core::AddAlias(const std::string &name, const std::vector &com bool Core::RemoveAlias(const std::string &name) { - tthread::lock_guard lock(*alias_mutex); + std::lock_guard lock(alias_mutex); if (IsAlias(name)) { aliases.erase(name); @@ -2734,14 +2653,14 @@ bool Core::RemoveAlias(const std::string &name) bool Core::IsAlias(const std::string &name) { - tthread::lock_guard lock(*alias_mutex); + std::lock_guard lock(alias_mutex); return aliases.find(name) != aliases.end(); } bool Core::RunAlias(color_ostream &out, const std::string &name, const std::vector ¶meters, command_result &result) { - tthread::lock_guard lock(*alias_mutex); + std::lock_guard lock(alias_mutex); if (!IsAlias(name)) { return false; @@ -2756,13 +2675,13 @@ bool Core::RunAlias(color_ostream &out, const std::string &name, std::map> Core::ListAliases() { - tthread::lock_guard lock(*alias_mutex); + std::lock_guard lock(alias_mutex); return aliases; } std::string Core::GetAliasCommand(const std::string &name, const std::string &default_) { - tthread::lock_guard lock(*alias_mutex); + std::lock_guard lock(alias_mutex); if (IsAlias(name)) return join_strings(" ", aliases[name]); else diff --git a/library/Hooks-windows.cpp b/library/Hooks-windows.cpp index 3d925bc27..8f77865e8 100644 --- a/library/Hooks-windows.cpp +++ b/library/Hooks-windows.cpp @@ -463,7 +463,7 @@ DFhackCExport char * SDL_GetError(void) } static void (*_SDL_SetError)(const char *fmt, ...) = 0; -DFhackCExport void SDL_SetError(const char *fmt, ...) +DFhackCExport void SDL_SetError(const char *fmt, ...) Wformat(printf,1,2) { char buf[1024]; va_list args; diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index e735eeb4b..ed37ebcea 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1626,6 +1626,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isMale), WRAPM(Units, isFemale), WRAPM(Units, isMerchant), + WRAPM(Units, isDiplomat), WRAPM(Units, isForest), WRAPM(Units, isMarkedForSlaughter), WRAPM(Units, isTame), @@ -2242,11 +2243,7 @@ int screen_show(lua_State *L) df::viewscreen *screen = dfhack_lua_viewscreen::get_pointer(L, 1, true); - bool ok = Screen::show(screen, before); - - // If it is a table, get_pointer created a new object. Don't leak it. - if (!ok && lua_istable(L, 1)) - delete screen; + bool ok = Screen::show(std::unique_ptr{screen}, before); lua_pushboolean(L, ok); return 1; diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 24d6917ff..3ac52bf45 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -283,9 +283,6 @@ static int lua_dfhack_is_interactive(lua_State *S) static int dfhack_lineedit_sync(lua_State *S, Console *pstream) { - if (!pstream) - return 2; - const char *prompt = luaL_optstring(S, 1, ">> "); const char *hfile = luaL_optstring(S, 2, NULL); @@ -296,10 +293,16 @@ static int dfhack_lineedit_sync(lua_State *S, Console *pstream) std::string ret; int rv = pstream->lineedit(prompt, ret, hist); + if (rv == Console::RETRY) + rv = 0; /* return empty string to lua */ + if (rv < 0) { lua_pushnil(S); - lua_pushstring(S, "input error"); + if (rv == Console::SHUTDOWN) + lua_pushstring(S, "shutdown requested"); + else + lua_pushstring(S, "input error"); return 2; } else @@ -333,8 +336,11 @@ static int dfhack_lineedit(lua_State *S) lua_settop(S, 2); Console *pstream = get_console(S); - if (!pstream) + if (!pstream) { + lua_pushnil(S); + lua_pushstring(S, "no console"); return 2; + } lua_rawgetp(S, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN); lua_rawgetp(S, -1, S); @@ -1058,7 +1064,11 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state, prompt = ">> "; std::string curline; - con.lineedit(prompt,curline,hist); + while((rv = con.lineedit(prompt,curline,hist)) == Console::RETRY); + if (rv <= Console::FAILURE) { + rv = rv == Console::SHUTDOWN ? LUA_OK : LUA_ERRRUN; + break; + } hist.add(curline); { diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index fe8309736..ef41d8076 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -27,6 +27,7 @@ distribution. #include #include #include +#include #include "MemAccess.h" #include "Core.h" @@ -1008,7 +1009,7 @@ static int meta_ptr_tostring(lua_State *state) const char *cname = lua_tostring(state, -1); if (has_length) - lua_pushstring(state, stl_sprintf("<%s[%llu]: %p>", cname, length, (void*)ptr).c_str()); + lua_pushstring(state, stl_sprintf("<%s[%" PRIu64 "]: %p>", cname, length, (void*)ptr).c_str()); else lua_pushstring(state, stl_sprintf("<%s: %p>", cname, (void*)ptr).c_str()); return 1; diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 91a860991..daaea64df 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -27,6 +27,8 @@ distribution. #include "MiscUtils.h" #ifndef LINUX_BUILD +// We don't want min and max macros +#define NOMINMAX #include #else #include @@ -40,6 +42,7 @@ distribution. #include #include +#include std::string stl_sprintf(const char *fmt, ...) { va_list lst; @@ -50,21 +53,25 @@ std::string stl_sprintf(const char *fmt, ...) { } std::string stl_vsprintf(const char *fmt, va_list args) { - std::vector buf; - buf.resize(4096); - for (;;) { - va_list args2; - va_copy(args2, args); - int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2); - va_end(args2); - - if (rsz < 0) - buf.resize(buf.size()*2); - else if (unsigned(rsz) >= buf.size()) - buf.resize(rsz+1); - else - return std::string(&buf[0], rsz); - } + /* Allow small (about single line) strings to be printed into stack memory + * with a call to vsnprintf. + */ + std::array buf; + va_list args2; + va_copy(args2, args); + int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2); + va_end(args2); + if (rsz < 0) + return std::string(); /* Error occurred */ + if (static_cast(rsz) < buf.size()) + return std::string(&buf[0], rsz); /* Whole string fits to a single line buffer */ + std::string rv; + // Allocate enough memory for the output and null termination + rv.resize(rsz); + rsz = vsnprintf(&rv[0], rv.size()+1, fmt, args); + if (rsz < static_cast(rv.size())) + rv.resize(std::max(rsz,0)); + return rv; } bool split_string(std::vector *out, diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 8e3f436cb..b17f8e214 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -49,7 +49,6 @@ using namespace DFHack; using namespace std; #include "tinythread.h" -using namespace tthread; #include @@ -83,8 +82,8 @@ struct Plugin::RefLock RefLock() { refcount = 0; - wakeup = new condition_variable(); - mut = new mutex(); + wakeup = new tthread::condition_variable(); + mut = new tthread::mutex(); } ~RefLock() { @@ -119,8 +118,8 @@ struct Plugin::RefLock wakeup->wait(*mut); } } - condition_variable * wakeup; - mutex * mut; + tthread::condition_variable * wakeup; + tthread::mutex * mut; int refcount; }; @@ -786,8 +785,8 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn) PluginManager::PluginManager(Core * core) : core(core) { - plugin_mutex = new recursive_mutex(); - cmdlist_mutex = new mutex(); + plugin_mutex = new tthread::recursive_mutex(); + cmdlist_mutex = new tthread::mutex(); ruby = NULL; } diff --git a/library/RemoteClient.cpp b/library/RemoteClient.cpp index 07f9c6961..25355530f 100644 --- a/library/RemoteClient.cpp +++ b/library/RemoteClient.cpp @@ -55,9 +55,10 @@ POSSIBILITY OF SUCH DAMAGE. #include -using namespace DFHack; - +#include "json/json.h" #include "tinythread.h" + +using namespace DFHack; using namespace tthread; using dfproto::CoreTextNotification; @@ -132,14 +133,34 @@ bool readFullBuffer(CSimpleSocket *socket, void *buf, int size) int RemoteClient::GetDefaultPort() { - const char *port = getenv("DFHACK_PORT"); - if (!port) port = "0"; + int port = DEFAULT_PORT; - int portval = atoi(port); - if (portval <= 0) - return 5000; + const char *port_env = getenv("DFHACK_PORT"); + if (port_env) + { + int port_val = atoi(port_env); + if (port_val > 0) + port = port_val; + } else - return portval; + { + for (const char *filename : {"dfhack-config/remote-server.json", "../dfhack-config/remote-server.json"}) + { + std::ifstream in_file(filename, std::ios_base::in); + if (in_file) + { + Json::Value config; + in_file >> config; + in_file.close(); + if (config.isMember("port")) { + port = config["port"].asInt(); + break; + } + } + } + } + + return port; } bool RemoteClient::connect(int port) @@ -157,7 +178,7 @@ bool RemoteClient::connect(int port) if (!socket->Open("localhost", port)) { - default_output().printerr("Could not connect to localhost: %d\n", port); + default_output().printerr("Could not connect to localhost:%d\n", port); return false; } diff --git a/library/RemoteServer.cpp b/library/RemoteServer.cpp index e4204014f..f110cc8b4 100644 --- a/library/RemoteServer.cpp +++ b/library/RemoteServer.cpp @@ -58,12 +58,11 @@ POSSIBILITY OF SUCH DAMAGE. #include -using namespace DFHack; - +#include "json/json.h" #include "tinythread.h" -using namespace tthread; -#include "jsoncpp.h" +using namespace DFHack; +using namespace tthread; using dfproto::CoreTextNotification; using dfproto::CoreTextFragment; @@ -395,11 +394,11 @@ bool ServerMain::listen(int port) inFile.close(); allow_remote = configJson.get("allow_remote", "false").asBool(); - port = configJson.get("port", port).asInt(); } + // rewrite/normalize config file configJson["allow_remote"] = allow_remote; - configJson["port"] = port; + configJson["port"] = configJson.get("port", RemoteClient::DEFAULT_PORT); std::ofstream outFile(filename, std::ios_base::trunc); @@ -409,6 +408,7 @@ bool ServerMain::listen(int port) outFile.close(); } + std::cerr << "Listening on port " << port << (allow_remote ? " (remote enabled)" : "") << std::endl; if (allow_remote) { if (!socket->Listen(NULL, port)) diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index 26833a3a3..1d45a19c0 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -652,8 +652,10 @@ static command_result SetUnitLabors(color_ostream &stream, const SetUnitLaborsIn return CR_OK; } -CoreService::CoreService() { - suspend_depth = 0; +CoreService::CoreService() : + suspend_depth{0}, + coreSuspender{nullptr} +{ // These 2 methods must be first, so that they get id 0 and 1 addMethod("BindMethod", &CoreService::BindMethod, SF_DONT_SUSPEND | SF_ALLOW_REMOTE); @@ -683,8 +685,7 @@ CoreService::CoreService() { CoreService::~CoreService() { - while (suspend_depth-- > 0) - Core::getInstance().Resume(); + delete coreSuspender; } command_result CoreService::BindMethod(color_ostream &stream, @@ -725,7 +726,8 @@ command_result CoreService::RunCommand(color_ostream &stream, command_result CoreService::CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt) { - Core::getInstance().Suspend(); + if (suspend_depth == 0) + coreSuspender = new CoreSuspender(); cnt->set_value(++suspend_depth); return CR_OK; } @@ -735,8 +737,11 @@ command_result CoreService::CoreResume(color_ostream &stream, const EmptyMessage if (suspend_depth <= 0) return CR_WRONG_USAGE; - Core::getInstance().Resume(); cnt->set_value(--suspend_depth); + if (suspend_depth == 0) { + delete coreSuspender; + coreSuspender = nullptr; + } return CR_OK; } diff --git a/library/include/BitArray.h b/library/include/BitArray.h index 1469091d1..cfb859bd0 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -25,12 +25,14 @@ distribution. #pragma once #include "Pragma.h" #include "Export.h" +#include "Error.h" #include #include #include #include #include -//#include +#include +#include namespace DFHack { template @@ -232,4 +234,338 @@ namespace DFHack m_data[idx] = item; } }; + + template + struct DfLinkedList + { + class iterator; + class const_iterator; + + class proxy + { + L *cur; + friend struct DfLinkedList; + friend class iterator; + proxy(L *cur) : cur(cur) + { + CHECK_NULL_POINTER(cur); + } + public: + operator I *const &() const + { + return cur->item; + } + I *operator->() const + { + return cur->item; + } + proxy & operator=(I *const & item) + { + if (item) + { + CHECK_INVALID_ARGUMENT(item->dfhack_get_list_link() == nullptr); + item->dfhack_set_list_link(cur); + } + if (cur->item) + { + cur->item->dfhack_set_list_link(nullptr); + } + cur->item = item; + + return *this; + } + }; + + class iterator + { + L *root; + L *cur; + friend struct DfLinkedList; + friend class const_iterator; + iterator(L *root, L *cur) : root(root), cur(cur) {} + public: + using difference_type = void; + using value_type = I *; + using pointer = I **; + using reference = proxy; + using iterator_category = std::bidirectional_iterator_tag; + + iterator() : root(nullptr), cur(nullptr) {} + iterator(const iterator & other) : root(other.root), cur(other.cur) {} + + iterator & operator++() + { + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); + + cur = cur->next; + return *this; + } + iterator & operator--() + { + CHECK_NULL_POINTER(root); + + if (!cur) + { + // find end() - 1 + for (cur = root->next; cur && cur->next; cur = cur->next) + { + } + return *this; + } + + CHECK_NULL_POINTER(cur); + CHECK_NULL_POINTER(cur->prev); + + cur = cur->prev; + return *this; + } + iterator operator++(int) + { + iterator copy(*this); + ++*this; + return copy; + } + iterator operator--(int) + { + iterator copy(*this); + --*this; + return copy; + } + iterator & operator=(const iterator & other) + { + root = other.root; + cur = other.cur; + return *this; + } + + proxy operator*() + { + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); + + return proxy(cur); + } + + I *const & operator*() const + { + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); + + return cur->item; + } + + operator const_iterator() const + { + return const_iterator(*this); + } + bool operator==(const iterator & other) const + { + return root == other.root && cur == other.cur; + } + bool operator!=(const iterator & other) const + { + return !(*this == other); + } + }; + class const_iterator + { + iterator iter; + friend struct DfLinkedList; + public: + using difference_type = void; + using value_type = I *; + using pointer = I *const *; + using reference = I *const &; + using iterator_category = std::bidirectional_iterator_tag; + + const_iterator(const iterator & iter) : iter(iter) {} + const_iterator(const const_iterator & other) : iter(other.iter) {} + + const_iterator & operator++() + { + ++iter; + return *this; + } + const_iterator & operator--() + { + --iter; + return *this; + } + const_iterator operator++(int) + { + const_iterator copy(*this); + ++iter; + return copy; + } + const_iterator operator--(int) + { + const_iterator copy(*this); + --iter; + return copy; + } + const_iterator & operator=(const const_iterator & other) + { + iter = other.iter; + return *this; + } + I *const & operator*() const + { + return *iter; + } + bool operator==(const const_iterator & other) const + { + return iter == other.iter; + } + bool operator!=(const const_iterator & other) const + { + return iter != other.iter; + } + }; + + using value_type = I *; + using reference_type = proxy; + using difference_type = void; + using size_type = size_t; + + bool empty() const + { + return static_cast(this)->next == nullptr; + } + size_t size() const + { + size_t n = 0; + for (value_type const & i : *this) + n++; + return n; + } + + iterator begin() + { + return iterator(static_cast(this), static_cast(this)->next); + } + const_iterator begin() const + { + return const_iterator(const_cast *>(this)->begin()); + } + const_iterator cbegin() const + { + return begin(); + } + iterator end() + { + return iterator(static_cast(this), nullptr); + } + const_iterator end() const + { + return const_iterator(const_cast *>(this)->end()); + } + const_iterator cend() const + { + return end(); + } + + iterator erase(const_iterator pos) + { + auto root = static_cast(this); + CHECK_INVALID_ARGUMENT(pos.iter.root == root); + CHECK_NULL_POINTER(pos.iter.cur); + + auto link = pos.iter.cur; + auto next = link->next; + + if (link->prev) + { + link->prev->next = link->next; + } + else + { + root->next = link->next; + } + + if (link->next) + { + link->next->prev = link->prev; + } + + proxy p(link); + p = nullptr; + + delete link; + + return iterator(root, next); + } + iterator insert(const_iterator pos, I *const & item) + { + auto root = static_cast(this); + CHECK_INVALID_ARGUMENT(pos.iter.root == root); + + auto link = pos.iter.cur; + if (!link || !link->prev) + { + if (!link && root->next) + { + pos--; + return insert_after(pos, item); + } + + CHECK_INVALID_ARGUMENT(root->next == link); + push_front(item); + return begin(); + } + + auto newlink = new L(); + newlink->prev = link->prev; + newlink->next = link; + link->prev = newlink; + if (newlink->prev) + { + newlink->prev->next = newlink; + } + else if (link == root->next) + { + root->next = newlink; + } + newlink->item = nullptr; + proxy p(newlink); + p = item; + return iterator(root, newlink); + } + iterator insert_after(const_iterator pos, I *const & item) + { + auto root = static_cast(this); + CHECK_INVALID_ARGUMENT(pos.iter.root == root); + CHECK_NULL_POINTER(pos.iter.cur); + + auto link = pos.iter.cur; + auto next = link->next; + auto newlink = new L(); + newlink->prev = link; + newlink->next = next; + link->next = newlink; + if (next) + { + next->prev = newlink; + } + newlink->item = nullptr; + proxy p(newlink); + p= item; + return iterator(root, newlink); + } + void push_front(I *const & item) + { + auto root = static_cast(this); + auto link = new L(); + link->prev = nullptr; + if (root->next) + { + root->next->prev = link; + link->next = root->next; + } + link->item = nullptr; + proxy p(link); + p = item; + root->next = link; + } + }; } diff --git a/library/include/ColorText.h b/library/include/ColorText.h index 006fa119d..f6da7ac88 100644 --- a/library/include/ColorText.h +++ b/library/include/ColorText.h @@ -106,12 +106,12 @@ namespace DFHack virtual ~color_ostream(); /// Print a formatted string, like printf - void print(const char *format, ...); - void vprint(const char *format, va_list args); + void print(const char *format, ...) Wformat(printf,2,3); + void vprint(const char *format, va_list args) Wformat(printf,2,0); /// Print a formatted string, like printf, in red - void printerr(const char *format, ...); - void vprinterr(const char *format, va_list args); + void printerr(const char *format, ...) Wformat(printf,2,3); + void vprinterr(const char *format, va_list args) Wformat(printf,2,0); /// Get color color_value color() { return cur_color; } diff --git a/library/include/Console.h b/library/include/Console.h index 0581f90e0..0517704d4 100644 --- a/library/include/Console.h +++ b/library/include/Console.h @@ -26,6 +26,7 @@ distribution. #include "Pragma.h" #include "Export.h" #include "ColorText.h" +#include #include #include #include @@ -152,6 +153,12 @@ namespace DFHack int get_rows(void); /// beep. maybe? //void beep (void); + //! \defgroup lineedit_return_values Possible errors from lineedit + //! \{ + static constexpr int FAILURE = -1; + static constexpr int SHUTDOWN = -2; + static constexpr int RETRY = -3; + //! \} /// A simple line edit (raw mode) int lineedit(const std::string& prompt, std::string& output, CommandHistory & history ); bool isInited (void) { return inited; }; @@ -163,6 +170,6 @@ namespace DFHack private: Private * d; tthread::recursive_mutex * wlock; - bool inited; + std::atomic inited; }; } diff --git a/library/include/Core.h b/library/include/Core.h index fa65645a1..529633ff2 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -34,6 +34,11 @@ distribution. #include "Console.h" #include "modules/Graphic.h" +#include +#include +#include +#include + #include "RemoteClient.h" #define DFH_MOD_SHIFT 1 @@ -42,13 +47,6 @@ distribution. struct WINDOW; -namespace tthread -{ - class mutex; - class condition_variable; - class thread; -} - namespace df { struct viewscreen; @@ -65,11 +63,21 @@ namespace DFHack class PluginManager; class Core; class ServerMain; + class CoreSuspender; + + namespace Lua { namespace Core { + DFHACK_EXPORT void Reset(color_ostream &out, const char *where); + } } namespace Windows { class df_window; } + namespace Screen + { + struct Hide; + } + enum state_change_event { SC_UNKNOWN = -1, @@ -133,10 +141,6 @@ namespace DFHack } /// check if the activity lock is owned by this thread bool isSuspended(void); - /// try to acquire the activity lock - void Suspend(void); - /// return activity lock - void Resume(void); /// Is everything OK? bool isValid(void) { return !errorstate; } @@ -149,7 +153,7 @@ namespace DFHack /// sets the current hotkey command bool setHotkeyCmd( std::string cmd ); /// removes the hotkey command and gives it to the caller thread - std::string getHotkeyCmd( void ); + std::string getHotkeyCmd( bool &keep_going ); /// adds a named pointer (for later or between plugins) void RegisterData(void *p,std::string key); @@ -191,8 +195,8 @@ namespace DFHack DFHack::VersionInfo * vinfo; DFHack::Windows::df_window * screen_window; - static void print(const char *format, ...); - static void printerr(const char *format, ...); + static void print(const char *format, ...) Wformat(printf,1,2); + static void printerr(const char *format, ...) Wformat(printf,1,2); PluginManager *getPluginManager() { return plug_mgr; } @@ -202,14 +206,11 @@ namespace DFHack DFHack::Console con; Core(); + ~Core(); struct Private; Private *d; - friend class CoreSuspendClaimer; - int ClaimSuspend(bool force_base); - void DisclaimSuspend(int level); - bool Init(); int Update (void); int TileUpdate (void); @@ -246,7 +247,7 @@ namespace DFHack DFHack::PluginManager * plug_mgr; std::vector script_paths[2]; - tthread::mutex *script_path_mutex; + std::mutex script_path_mutex; // hotkey-related stuff struct KeyBinding { @@ -260,12 +261,17 @@ namespace DFHack std::map > key_bindings; std::map hotkey_states; std::string hotkey_cmd; - bool hotkey_set; - tthread::mutex * HotkeyMutex; - tthread::condition_variable * HotkeyCond; + enum hotkey_set_t { + NO, + SET, + SHUTDOWN, + }; + hotkey_set_t hotkey_set; + std::mutex HotkeyMutex; + std::condition_variable HotkeyCond; std::map> aliases; - tthread::recursive_mutex * alias_mutex; + std::recursive_mutex alias_mutex; bool SelectHotkey(int key, int modifiers); @@ -273,6 +279,7 @@ namespace DFHack void *last_world_data_ptr; // for state change tracking void *last_local_map_ptr; + friend struct Screen::Hide; df::viewscreen *top_viewscreen; bool last_pause_state; // Very important! @@ -280,35 +287,158 @@ namespace DFHack // Additional state change scripts std::vector state_change_scripts; - tthread::mutex * misc_data_mutex; + std::mutex misc_data_mutex; std::map misc_data_map; + /*! + * \defgroup core_suspend CoreSuspender state handling serialization to + * DF memory. + * \sa DFHack::CoreSuspender + * \{ + */ + std::recursive_mutex CoreSuspendMutex; + std::condition_variable_any CoreWakeup; + std::atomic ownerThread; + std::atomic toolCount; + //! \} + friend class CoreService; friend class ServerConnection; + friend class CoreSuspender; + friend class CoreSuspenderBase; + friend struct CoreSuspendClaimMain; + friend struct CoreSuspendReleaseMain; ServerMain *server; }; - class CoreSuspender { - Core *core; + class CoreSuspenderBase : protected std::unique_lock { + protected: + using parent_t = std::unique_lock; + std::thread::id tid; + + CoreSuspenderBase(std::defer_lock_t d) : CoreSuspenderBase{&Core::getInstance(), d} {} + + CoreSuspenderBase(Core* core, std::defer_lock_t) : + /* Lock the core */ + parent_t{core->CoreSuspendMutex,std::defer_lock}, + /* Mark this thread to be the core owner */ + tid{} + {} public: - CoreSuspender() : core(&Core::getInstance()) { core->Suspend(); } - CoreSuspender(Core *core) : core(core) { core->Suspend(); } - ~CoreSuspender() { core->Resume(); } + void lock() + { + auto& core = Core::getInstance(); + parent_t::lock(); + tid = core.ownerThread.exchange(std::this_thread::get_id(), + std::memory_order_acquire); + } + + void unlock() + { + auto& core = Core::getInstance(); + /* Restore core owner to previous value */ + core.ownerThread.store(tid, std::memory_order_release); + if (tid == std::thread::id{}) + Lua::Core::Reset(core.getConsole(), "suspend"); + parent_t::unlock(); + } + + bool owns_lock() const noexcept + { + return parent_t::owns_lock(); + } + + ~CoreSuspenderBase() { + if (owns_lock()) + unlock(); + } + friend class MainThread; }; - /** Claims the current thread already has the suspend lock. - * Strictly for use in callbacks from DF. + /*! + * CoreSuspender allows serialization to DF data with std::unique_lock like + * interface. It includes handling for recursive CoreSuspender calls and + * notification to main thread after all queue tools have been handled. + * + * State transitions are: + * - Startup setups Core::SuspendMutex to unlocked states + * - Core::Init locks Core::SuspendMutex until the thread exits or that thread + * calls Core::Shutdown or Core::~Core. + * - Other thread request core suspend by atomic incrementation of Core::toolCount + * and then locking Core::CoreSuspendMutex. After locking CoreSuspendMutex + * success callers exchange their std::thread::id to Core::ownerThread. + * - Core::Update() makes sure that queued tools are run when it calls + * Core::CoreWakup::wait. The wait keeps Core::CoreSuspendMutex unlocked + * and waits until Core::toolCount is reduced back to zero. + * - CoreSuspender::~CoreSuspender() first stores the previous Core::ownerThread + * back. In case of recursive call Core::ownerThread equals tid. If tis is + * zero then we are releasing the recursive_mutex which means suspend + * context is over. It is time to reset lua. + * The last step is to decrement Core::toolCount and wakeup main thread if + * no more tools are queued trying to acquire the + * Core::CoreSuspenderMutex. */ - class CoreSuspendClaimer { - Core *core; - int level; + class CoreSuspender : public CoreSuspenderBase { + using parent_t = CoreSuspenderBase; public: - CoreSuspendClaimer(bool base = false) : core(&Core::getInstance()) { - level = core->ClaimSuspend(base); + CoreSuspender() : CoreSuspender{&Core::getInstance()} { } + CoreSuspender(std::defer_lock_t d) : CoreSuspender{&Core::getInstance(),d} { } + CoreSuspender(bool) : CoreSuspender{&Core::getInstance()} { } + CoreSuspender(Core* core, bool) : CoreSuspender{core} { } + CoreSuspender(Core* core) : + CoreSuspenderBase{core, std::defer_lock} + { + lock(); } - CoreSuspendClaimer(Core *core, bool base = false) : core(core) { - level = core->ClaimSuspend(base); + CoreSuspender(Core* core, std::defer_lock_t) : + CoreSuspenderBase{core, std::defer_lock} + {} + + void lock() + { + auto& core = Core::getInstance(); + core.toolCount.fetch_add(1, std::memory_order_relaxed); + parent_t::lock(); + } + + void unlock() + { + auto& core = Core::getInstance(); + parent_t::unlock(); + /* Notify core to continue when all queued tools have completed + * 0 = None wants to own the core + * 1+ = There are tools waiting core access + * fetch_add returns old value before subtraction + */ + if (core.toolCount.fetch_add(-1, std::memory_order_relaxed) == 1) + core.CoreWakeup.notify_one(); } - ~CoreSuspendClaimer() { core->DisclaimSuspend(level); } + + ~CoreSuspender() { + if (owns_lock()) + unlock(); + } + }; + + /*! + * Temporary release main thread ownership to allow alternative thread + * implement DF logic thread loop + */ + struct DFHACK_EXPORT CoreSuspendReleaseMain { + CoreSuspendReleaseMain(); + ~CoreSuspendReleaseMain(); }; + + /*! + * Temporary claim main thread ownership. This allows caller to call + * Core::Update from a different thread than original DF logic thread if + * logic thread has released main thread ownership with + * CoreSuspendReleaseMain + */ + struct DFHACK_EXPORT CoreSuspendClaimMain { + CoreSuspendClaimMain(); + ~CoreSuspendClaimMain(); + }; + + using CoreSuspendClaimer = CoreSuspender; } diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 31a949df1..149ac47a0 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -439,6 +439,7 @@ namespace df using DFHack::enum_list_attr; using DFHack::BitArray; using DFHack::DfArray; + using DFHack::DfLinkedList; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" diff --git a/library/include/Export.h b/library/include/Export.h index 87d38b45f..9e2a78d4b 100644 --- a/library/include/Export.h +++ b/library/include/Export.h @@ -58,3 +58,14 @@ distribution. #define DFhackCExport extern "C" __declspec(dllexport) #define DFhackDataExport extern "C" __declspec(dllexport) #endif + +// Make gcc warn if types and format string don't match for printf +#ifdef __GNUC__ + //! Tell GCC about format functions to allow parameter strict type checks + //! \param type The type of function can be printf, scanf, strftime or strfmon + //! \param fmtstr One based position index for format parameter + //! \param vararg One based position index for the first checked parameter + #define Wformat(type, fmtstr, vararg) __attribute__ ((format (type, fmtstr, vararg))) +#else + #define Wformat(type, fmtstr, vararg) +#endif diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index e1d828271..814afe179 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -395,7 +395,7 @@ namespace DFHack {namespace Lua { // Not exported; for use by the Core class bool Init(color_ostream &out); - void Reset(color_ostream &out, const char *where); + DFHACK_EXPORT void Reset(color_ostream &out, const char *where); // Events signalled by the core void onStateChange(color_ostream &out, int code); diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 378b7a728..51ccea656 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -31,6 +31,7 @@ distribution. #include #include #include +#include using std::ostream; using std::stringstream; @@ -44,6 +45,28 @@ using std::endl; #define DFHACK_FUNCTION_SIG __func__ #endif +/*! \namespace dts + * std.reverse() == dts, The namespace that include forward compatible helpers + * which can be used from newer standards. The preprocessor check prefers + * standard version if one is available. The standard version gets imported with + * using. + */ +namespace dts { +// Check if lib supports the feature test macro or version is over c++14. +#if __cpp_lib_make_unique < 201304 && __cplusplus < 201402L +//! Insert c++14 make_unique to be forward compatible. Array versions are +//! missing +template +typename std::enable_if::value, std::unique_ptr >::type +make_unique(Args&&... args) +{ + return std::unique_ptr{new T{std::forward(args)...}}; +} +#else /* >= c++14 */ +using std::make_unique; +#endif +} + template void print_bits ( T val, ostream& out ) { @@ -354,8 +377,8 @@ DFHACK_EXPORT int random_int(int max); */ DFHACK_EXPORT uint64_t GetTimeMs64(); -DFHACK_EXPORT std::string stl_sprintf(const char *fmt, ...); -DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args); +DFHACK_EXPORT std::string stl_sprintf(const char *fmt, ...) Wformat(printf,1,2); +DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args) Wformat(printf,1,0); // Conversion between CP437 and UTF-8 DFHACK_EXPORT std::string UTF2DF(const std::string &in); diff --git a/library/include/RemoteClient.h b/library/include/RemoteClient.h index e1a2c512a..cf07bba8b 100644 --- a/library/include/RemoteClient.h +++ b/library/include/RemoteClient.h @@ -233,6 +233,7 @@ namespace DFHack RemoteClient(color_ostream *default_output = NULL); ~RemoteClient(); + static constexpr int DEFAULT_PORT = 5000; static int GetDefaultPort(); color_ostream &default_output() { return *p_default_output; }; diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h index ead1c0aa1..d7d4eb4fe 100644 --- a/library/include/RemoteTools.h +++ b/library/include/RemoteTools.h @@ -133,6 +133,7 @@ namespace DFHack class CoreService : public RPCService { int suspend_depth; + CoreSuspender* coreSuspender; static int doRunLuaFunction(lua_State *L); public: diff --git a/depends/jsoncpp/jsoncpp-ex.h b/library/include/jsoncpp-ex.h similarity index 87% rename from depends/jsoncpp/jsoncpp-ex.h rename to library/include/jsoncpp-ex.h index 7d621dd72..78f81c0e4 100644 --- a/depends/jsoncpp/jsoncpp-ex.h +++ b/library/include/jsoncpp-ex.h @@ -1,4 +1,4 @@ -#include "jsoncpp.h" +#include "json/json.h" #pragma once namespace Json { @@ -24,8 +24,10 @@ namespace Json { inline std::string toSimpleString (const Json::Value &val) { - Json::FastWriter w; - return w.write(val); + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = "\t"; + return writeString(builder, val); } } diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index 36625719b..73a91d972 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -38,6 +38,8 @@ distribution. #include "df/item.h" #include "df/inclusion_type.h" +#include + namespace df { struct world_region_details; } @@ -127,22 +129,22 @@ public: /// Arbitrary tag field for flood fills etc. int16_t &tag(df::coord2d p) { if (!tags) init_tags(); - return index_tile(tags, p); + return index_tile(tags, p); } /// Base layer tile type (i.e. layer stone, veins, feature stone) df::tiletype baseTiletypeAt(df::coord2d p) { if (!tiles) init_tiles(); - return index_tile(tiles->base_tiles,p); + return index_tile(tiles->base_tiles,p); } /// Base layer material (i.e. layer stone, veins, feature stone) t_matpair baseMaterialAt(df::coord2d p) { if (!basemats) init_tiles(true); return t_matpair( - index_tile(basemats->mat_type,p), - index_tile(basemats->mat_index,p) + index_tile(basemats->mat_type,p), + index_tile(basemats->mat_index,p) ); } /// Check if the base layer tile is a vein @@ -164,13 +166,13 @@ public: int16_t veinMaterialAt(df::coord2d p) { if (!basemats) init_tiles(true); - return index_tile(basemats->veinmat,p); + return index_tile(basemats->veinmat,p); } /// Vein type at pos (even if there is no vein tile) df::inclusion_type veinTypeAt(df::coord2d p) { if (!basemats) init_tiles(true); - return (df::inclusion_type)index_tile(basemats->veintype,p); + return (df::inclusion_type)index_tile(basemats->veintype,p); } /** Sets the vein material at the specified tile position. @@ -207,7 +209,7 @@ public: { if (!tiles) init_tiles(); if (tiles->con_info) - return index_tile(tiles->con_info->tiles,p); + return index_tile(tiles->con_info->tiles,p); return baseTiletypeAt(p); } /// Static layer material (i.e. base + constructions) @@ -216,8 +218,8 @@ public: if (!basemats) init_tiles(true); if (tiles->con_info) return t_matpair( - index_tile(tiles->con_info->mat_type,p), - index_tile(tiles->con_info->mat_index,p) + index_tile(tiles->con_info->mat_type,p), + index_tile(tiles->con_info->mat_index,p) ); return baseMaterialAt(p); } @@ -232,47 +234,50 @@ public: { if (!block) return tiletype::Void; if (tiles) - return index_tile(tiles->raw_tiles,p); - return index_tile(block->tiletype,p); + return index_tile(tiles->raw_tiles,p); + return index_tile(block->tiletype,p); } bool setTiletypeAt(df::coord2d, df::tiletype tt, bool force = false); uint16_t temperature1At(df::coord2d p) { - return index_tile(temp1,p); + return index_tile(temp1,p); } bool setTemp1At(df::coord2d p, uint16_t temp) { if(!valid) return false; dirty_temperatures = true; - index_tile(temp1,p) = temp; + index_tile(temp1,p) = temp; return true; } uint16_t temperature2At(df::coord2d p) { - return index_tile(temp2,p); + return index_tile(temp2,p); } bool setTemp2At(df::coord2d p, uint16_t temp) { if(!valid) return false; dirty_temperatures = true; - index_tile(temp2,p) = temp; + index_tile(temp2,p) = temp; return true; } df::tile_designation DesignationAt(df::coord2d p) { - return index_tile(designation,p); + return index_tile(designation,p); } - bool setDesignationAt(df::coord2d p, df::tile_designation des) + bool setDesignationAt(df::coord2d p, df::tile_designation des, int32_t priority = 4000) { if(!valid) return false; dirty_designations = true; + designated_tiles[(p.x&15) + (p.y&15)*16] = true; //printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y); - index_tile(designation,p) = des; - if(des.bits.dig && block) + index_tile(designation,p) = des; + if((des.bits.dig || des.bits.smooth) && block) { block->flags.bits.designated = true; + setPriorityAt(p, priority); + } return true; } @@ -281,21 +286,21 @@ public: df::tile_occupancy OccupancyAt(df::coord2d p) { - return index_tile(occupancy,p); + return index_tile(occupancy,p); } bool setOccupancyAt(df::coord2d p, df::tile_occupancy des) { if(!valid) return false; dirty_occupancies = true; - index_tile(occupancy,p) = des; + index_tile(occupancy,p) = des; return true; } bool getFlagAt(df::coord2d p, df::tile_designation::Mask mask) { - return (index_tile(designation,p).whole & mask) != 0; + return (index_tile(designation,p).whole & mask) != 0; } bool getFlagAt(df::coord2d p, df::tile_occupancy::Mask mask) { - return (index_tile(occupancy,p).whole & mask) != 0; + return (index_tile(occupancy,p).whole & mask) != 0; } bool setFlagAt(df::coord2d p, df::tile_designation::Mask mask, bool set); bool setFlagAt(df::coord2d p, df::tile_occupancy::Mask mask, bool set); @@ -303,7 +308,7 @@ public: int itemCountAt(df::coord2d p) { if (!item_counts) init_item_counts(); - return index_tile(item_counts,p); + return index_tile(item_counts,p); } t_blockflags BlockFlags() @@ -316,7 +321,7 @@ public: int biomeIndexAt(df::coord2d p); int layerIndexAt(df::coord2d p) { - return index_tile(designation,p).bits.geolayer_index; + return index_tile(designation,p).bits.geolayer_index; } df::coord2d biomeRegionAt(df::coord2d p); @@ -342,13 +347,15 @@ private: void init(); - bool valid; + bool valid:1; bool dirty_designations:1; bool dirty_tiles:1; bool dirty_veins:1; bool dirty_temperatures:1; bool dirty_occupancies:1; + std::bitset<16*16> designated_tiles; + DFCoord bcoord; // Custom tags for floodfill @@ -548,13 +555,11 @@ class DFHACK_EXPORT MapCache return b ? b->DesignationAt(tilecoord) : df::tile_designation(); } // priority is optional, only set if >= 0 - bool setDesignationAt (DFCoord tilecoord, df::tile_designation des, int32_t priority = -1) + bool setDesignationAt (DFCoord tilecoord, df::tile_designation des, int32_t priority = 4000) { if (Block *b = BlockAtTile(tilecoord)) { - if (!b->setDesignationAt(tilecoord, des)) - return false; - if (priority >= 0 && b->setPriorityAt(tilecoord, priority)) + if (!b->setDesignationAt(tilecoord, des, priority)) return false; return true; } @@ -599,15 +604,8 @@ class DFHACK_EXPORT MapCache return b ? b->removeItemOnGround(item) : false; } - bool WriteAll() - { - std::map::iterator p; - for(p = blocks.begin(); p != blocks.end(); p++) - { - p->second->Write(); - } - return true; - } + bool WriteAll(); + void trash() { std::map::iterator p; diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 6b6e62f0a..0c41bf7f6 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -167,7 +167,9 @@ typedef uint16_t t_temperatures [16][16]; /** * Index a tile array by a 2D coordinate, clipping it to mod 16 */ -template inline R index_tile(T &v, df::coord2d p) { +template inline auto index_tile(T &v, df::coord2d p) + -> typename std::add_rvalue_reference::type +{ return v[p.x&15][p.y&15]; } diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 673533f3e..1775f3e15 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -31,10 +31,12 @@ distribution. #include #include +#include #include "DataDefs.h" #include "df/graphic.h" #include "df/viewscreen.h" +#include "df/zoom_commands.h" #include "modules/GuiHooks.h" @@ -182,6 +184,9 @@ namespace DFHack return rect2d(df::coord2d(0,0), getWindowSize()-df::coord2d(1,1)); } + /// Wrapper to call enabler->zoom_display from plugins + DFHACK_EXPORT void zoom(df::zoom_commands cmd); + /// Returns the state of [GRAPHICS:YES/NO] DFHACK_EXPORT bool inGraphicsMode(); @@ -210,9 +215,9 @@ namespace DFHack DFHACK_EXPORT bool findGraphicsTile(const std::string &page, int x, int y, int *ptile, int *pgs = NULL); // Push and remove viewscreens - DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL, Plugin *p = NULL); - inline bool show(df::viewscreen *screen, Plugin *p) - { return show(screen, NULL, p); } + DFHACK_EXPORT bool show(std::unique_ptr screen, df::viewscreen *before = NULL, Plugin *p = NULL); + inline bool show(std::unique_ptr screen, Plugin *p) + { return show(std::move(screen), NULL, p); } DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false); DFHACK_EXPORT bool isDismissed(df::viewscreen *screen); DFHACK_EXPORT bool hasActiveScreens(Plugin *p); @@ -301,6 +306,15 @@ namespace DFHack GUI_HOOK_DECLARE(set_tile, bool, (const Pen &pen, int x, int y, bool map)); } + //! Temporary hide a screen until destructor is called + struct DFHACK_EXPORT Hide { + Hide(df::viewscreen* screen); + ~Hide(); + private: + void extract(df::viewscreen*); + void merge(df::viewscreen*); + df::viewscreen* screen_; + }; } class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen { @@ -374,4 +388,5 @@ namespace DFHack virtual df::building *getSelectedBuilding(); virtual df::plant *getSelectedPlant(); }; + } diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index b6aa78bad..2a7364dbf 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -139,6 +139,7 @@ DFHACK_EXPORT bool isTamable(df::unit* unit); DFHACK_EXPORT bool isMale(df::unit* unit); DFHACK_EXPORT bool isFemale(df::unit* unit); DFHACK_EXPORT bool isMerchant(df::unit* unit); +DFHACK_EXPORT bool isDiplomat(df::unit* unit); DFHACK_EXPORT bool isForest(df::unit* unit); DFHACK_EXPORT bool isMarkedForSlaughter(df::unit* unit); DFHACK_EXPORT bool isTame(df::unit* unit); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 95c31c2d7..93ab48dca 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -180,6 +180,116 @@ function printall_ipairs(table) end end +local do_print_recurse + +local function print_string(printfn, v, seen, indent) + local str = tostring(v) + printfn(str) + return #str; +end + +local fill_chars = { + __index = function(table, key, value) + local rv = string.rep(' ', 23 - key) .. ' = ' + rawset(table, key, rv) + return rv + end, +} + +setmetatable(fill_chars, fill_chars) + +local function print_fields(value, seen, indent, prefix) + local ok,f,t,k = pcall(pairs,value) + if not ok then + dfhack.print(prefix) + dfhack.println('') + return 0 + end + local prev_value = "not a value" + local repeated = 0 + for k, v in f,t,k do + -- Only show set values of bitfields + if value._kind ~= "bitfield" or v then + local continue = false + if type(k) == "number" then + if prev_value == v then + repeated = repeated + 1 + continue = true + else + prev_value = v + end + else + prev_value = "not a value" + end + if not continue then + if repeated > 0 then + dfhack.println(prefix .. "") + repeated = 0 + end + dfhack.print(prefix) + local len = do_print_recurse(dfhack.print, k, seen, indent + 1) + dfhack.print(fill_chars[len <= 23 and len or 23]) + do_print_recurse(dfhack.println, v, seen, indent + 1) + end + end + end + if repeated > 0 then + dfhack.println(prefix .. "") + end + return 0 +end + +-- This should be same as print_array but userdata doesn't compare equal even if +-- they hold same pointer. +local function print_userdata(printfn, value, seen, indent) + local prefix = string.rep(' ', indent) + local strvalue = tostring(value) + dfhack.println(strvalue) + if seen[strvalue] then + dfhack.print(prefix) + dfhack.println('\n') + return 0 + end + seen[strvalue] = true + return print_fields(value, seen, indent, prefix) +end + +local function print_array(printfn, value, seen, indent) + local prefix = string.rep(' ', indent) + dfhack.println(tostring(value)) + if seen[value] then + dfhack.print(prefix) + dfhack.println('\n') + return 0 + end + seen[value] = true + return print_fields(value, seen, indent, prefix) +end + +local recurse_type_map = { + number = print_string, + string = print_string, + boolean = print_string, + ['function'] = print_string, + ['nil'] = print_string, + userdata = print_userdata, + table = print_array, +} + +do_print_recurse = function(printfn, value, seen, indent) + local t = type(value) + if not recurse_type_map[t] then + printfn("Unknown type " .. t .. " " .. tostring(value)) + return + end + return recurse_type_map[t](printfn, value, seen, indent) +end + +function printall_recurse(value) + local seen = {} + do_print_recurse(dfhack.println, value, seen, 0) +end + function copyall(table) local rv = {} for k,v in pairs(table) do rv[k] = v end @@ -334,6 +444,7 @@ function dfhack.interpreter(prompt,hfile,env) " '= foo' => '_1,_2,... = foo'\n".. " '! foo' => 'print(foo)'\n".. " '~ foo' => 'printall(foo)'\n".. + " '^ foo' => 'printall_recurse(foo)'\n".. " '@ foo' => 'printall_ipairs(foo)'\n".. "All of these save the first result as '_'.") print_banner = false @@ -358,6 +469,9 @@ function dfhack.interpreter(prompt,hfile,env) print(table.unpack(data,2,data.n)) printall_ipairs(data[2]) end, + ['^'] = function(data) + printall_recurse(data[2]) + end, ['='] = function(data) for i=2,data.n do local varname = '_'..vcnt diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 741d722dd..f891442b4 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -456,15 +456,17 @@ function List:init(info) end function List:setChoices(choices, selected) - self.choices = choices or {} + self.choices = {} - for i,v in ipairs(self.choices) do + for i,v in ipairs(choices or {}) do + local l = utils.clone(v); if type(v) ~= 'table' then - v = { text = v } - self.choices[i] = v + l = { text = v } + else + l.text = v.text or v.caption or v[1] end - v.text = v.text or v.caption or v[1] - parse_label_text(v) + parse_label_text(l) + self.choices[i] = l end self:setSelected(selected) diff --git a/library/lua/utils.lua b/library/lua/utils.lua index a6007a08f..60830e82f 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -505,17 +505,17 @@ function prompt_yes_no(msg,default) prompt = prompt..' (y/n)[n]: ' end while true do - local rv = dfhack.lineedit(prompt) - if rv then - if string.match(rv,'^[Yy]') then - return true - elseif string.match(rv,'^[Nn]') then - return false - elseif rv == 'abort' then - qerror('User abort') - elseif rv == '' and default ~= nil then - return default - end + local rv,err = dfhack.lineedit(prompt) + if not rv then + qerror(err); + elseif string.match(rv,'^[Yy]') then + return true + elseif string.match(rv,'^[Nn]') then + return false + elseif rv == 'abort' then + qerror('User abort') + elseif rv == '' and default ~= nil then + return default end end end @@ -524,7 +524,10 @@ end function prompt_input(prompt,check,quit_str) quit_str = quit_str or '~~~' while true do - local rv = dfhack.lineedit(prompt) + local rv,err = dfhack.lineedit(prompt) + if not rv then + qerror(err); + end if rv == quit_str then qerror('User abort') end diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 798ac1eaf..404d2342c 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -676,7 +676,7 @@ static void manageSyndromeEvent(color_ostream& out) { for ( auto a = df::global::world->units.all.begin(); a != df::global::world->units.all.end(); a++ ) { df::unit* unit = *a; /* - if ( unit->flags1.bits.dead ) + if ( unit->flags1.bits.inactive ) continue; */ for ( size_t b = 0; b < unit->syndromes.active.size(); b++ ) { @@ -723,7 +723,7 @@ static void manageEquipmentEvent(color_ostream& out) { itemIdToInventoryItem.clear(); currentlyEquipped.clear(); df::unit* unit = *a; - /*if ( unit->flags1.bits.dead ) + /*if ( unit->flags1.bits.inactive ) continue; */ @@ -952,7 +952,7 @@ static void manageUnitAttackEvent(color_ostream& out) { } if ( !wound1 && !wound2 ) { - //if ( unit1->flags1.bits.dead || unit2->flags1.bits.dead ) + //if ( unit1->flags1.bits.inactive || unit2->flags1.bits.inactive ) // continue; if ( reportStr.find("severed part") ) continue; diff --git a/library/modules/Kitchen.cpp b/library/modules/Kitchen.cpp index 705cb7bee..63372d564 100644 --- a/library/modules/Kitchen.cpp +++ b/library/modules/Kitchen.cpp @@ -33,7 +33,7 @@ void Kitchen::debug_print(color_ostream &out) out.print("Kitchen Exclusions\n"); for(std::size_t i = 0; i < size(); ++i) { - out.print("%2u: IT:%2i IS:%i MT:%3i MI:%2i ET:%i %s\n", + out.print("%2zu: IT:%2i IS:%i MT:%3i MI:%2i ET:%i %s\n", i, ui->kitchen.item_types[i], ui->kitchen.item_subtypes[i], diff --git a/library/modules/MapCache.cpp b/library/modules/MapCache.cpp index c0a2e4bed..d5c2e6c81 100644 --- a/library/modules/MapCache.cpp +++ b/library/modules/MapCache.cpp @@ -45,6 +45,7 @@ using namespace std; #include "modules/Buildings.h" #include "modules/MapCache.h" #include "modules/Maps.h" +#include "modules/Job.h" #include "modules/Materials.h" #include "df/block_burrow.h" @@ -57,6 +58,7 @@ using namespace std; #include "df/burrow.h" #include "df/feature_init.h" #include "df/flow_info.h" +#include "df/job.h" #include "df/plant.h" #include "df/plant_tree_info.h" #include "df/plant_tree_tile.h" @@ -91,7 +93,9 @@ const BiomeInfo MapCache::biome_stub = { #define COPY(a,b) memcpy(&a,&b,sizeof(a)) -MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) +MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : + parent(parent), + designated_tiles{} { dirty_designations = false; dirty_tiles = false; @@ -232,7 +236,7 @@ MapExtras::Block::BasematInfo::BasematInfo() bool MapExtras::Block::setFlagAt(df::coord2d p, df::tile_designation::Mask mask, bool set) { if(!valid) return false; - auto &val = index_tile(designation,p); + auto &val = index_tile(designation,p); bool cur = (val.whole & mask) != 0; if (cur != set) { @@ -245,7 +249,7 @@ bool MapExtras::Block::setFlagAt(df::coord2d p, df::tile_designation::Mask mask, bool MapExtras::Block::setFlagAt(df::coord2d p, df::tile_occupancy::Mask mask, bool set) { if(!valid) return false; - auto &val = index_tile(occupancy,p); + auto &val = index_tile(occupancy,p); bool cur = (val.whole & mask) != 0; if (cur != set) { @@ -1063,7 +1067,7 @@ int MapExtras::Block::biomeIndexAt(df::coord2d p) if (!block) return -1; - auto des = index_tile(designation,p); + auto des = index_tile(designation,p); uint8_t idx = des.bits.biome; if (idx >= 9) return -1; @@ -1141,12 +1145,12 @@ bool MapExtras::Block::addItemOnGround(df::item *item) if (inserted) { - int &count = index_tile(item_counts,item->pos); + int &count = index_tile(item_counts,item->pos); if (count++ == 0) { - index_tile(occupancy,item->pos).bits.item = true; - index_tile(block->occupancy,item->pos).bits.item = true; + index_tile(occupancy,item->pos).bits.item = true; + index_tile(block->occupancy,item->pos).bits.item = true; } } @@ -1166,13 +1170,13 @@ bool MapExtras::Block::removeItemOnGround(df::item *item) vector_erase_at(block->items, idx); - int &count = index_tile(item_counts,item->pos); + int &count = index_tile(item_counts,item->pos); if (--count == 0) { - index_tile(occupancy,item->pos).bits.item = false; + index_tile(occupancy,item->pos).bits.item = false; - auto &occ = index_tile(block->occupancy,item->pos); + auto &occ = index_tile(block->occupancy,item->pos); occ.bits.item = false; @@ -1250,6 +1254,38 @@ MapExtras::MapCache::MapCache() } } +bool MapExtras::MapCache::WriteAll() +{ + auto world = df::global::world; + df::job_list_link* job_link = world->jobs.list.next; + df::job_list_link* next = nullptr; + for (;job_link;job_link = next) { + next = job_link->next; + df::job* job = job_link->item; + df::coord pos = job->pos; + df::coord blockpos(pos.x>>4,pos.y>>4,pos.z); + auto iter = blocks.find(blockpos); + if (iter == blocks.end()) + continue; + df::coord2d bpos(pos.x - (blockpos.x<<4),pos.y - (blockpos.y<<4)); + auto block = iter->second; + if (!block->designated_tiles.test(bpos.x+bpos.y*16)) + continue; + bool is_designed = ENUM_ATTR(job_type,is_designation,job->job_type); + if (!is_designed) + continue; + // Remove designation job. DF will create a new one in the next tick + // processing. + Job::removeJob(job); + } + std::map::iterator p; + for(p = blocks.begin(); p != blocks.end(); p++) + { + p->second->Write(); + } + return true; +} + MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) { if(!valid) diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 20255dbe3..54f314db8 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -504,7 +504,7 @@ df::coord2d Maps::getBlockTileBiomeRgn(df::map_block *block, df::coord2d pos) if (!block || !world->world_data) return df::coord2d(); - auto des = index_tile(block->designation,pos); + auto des = index_tile(block->designation,pos); unsigned idx = des.bits.biome; if (idx < 9) { @@ -579,8 +579,8 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) if (!block1 || !block2) return false; - auto tile1 = index_tile(block1->walkable, pos1); - auto tile2 = index_tile(block2->walkable, pos2); + auto tile1 = index_tile(block1->walkable, pos1); + auto tile2 = index_tile(block2->walkable, pos2); return tile1 && tile1 == tile2; } @@ -607,7 +607,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) if ( !block1 || !block2 ) return false; - if ( !index_tile(block1->walkable,pos1) || !index_tile(block2->walkable,pos2) ) { + if ( !index_tile(block1->walkable,pos1) || !index_tile(block2->walkable,pos2) ) { return false; } @@ -626,7 +626,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) if ( dx == 0 && dy == 0 ) { //check for forbidden hatches and floors and such - df::tile_building_occ upOcc = index_tile(block2->occupancy,pos2).bits.building; + df::tile_building_occ upOcc = index_tile(block2->occupancy,pos2).bits.building; if ( upOcc == tile_building_occ::Impassable || upOcc == tile_building_occ::Obstacle || upOcc == tile_building_occ::Floored ) return false; @@ -659,7 +659,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) return false; //unusable ramp //there has to be an unforbidden hatch above the ramp - if ( index_tile(block2->occupancy,pos2).bits.building != tile_building_occ::Dynamic ) + if ( index_tile(block2->occupancy,pos2).bits.building != tile_building_occ::Dynamic ) return false; //note that forbidden hatches have Floored occupancy. unforbidden ones have dynamic occupancy df::building* building = Buildings::findAtTile(pos2); @@ -703,7 +703,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) if ( !blockUp ) return false; - df::tile_building_occ occupancy = index_tile(blockUp->occupancy,up).bits.building; + df::tile_building_occ occupancy = index_tile(blockUp->occupancy,up).bits.building; if ( occupancy == tile_building_occ::Obstacle || occupancy == tile_building_occ::Floored || occupancy == tile_building_occ::Impassable ) return false; return true; diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 6ac39aa05..733f8cb78 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -90,6 +90,10 @@ df::coord2d Screen::getWindowSize() return df::coord2d(gps->dimx, gps->dimy); } +void Screen::zoom(df::zoom_commands cmd) { + enabler->zoom_display(cmd); +} + bool Screen::inGraphicsMode() { return init && init->display.flag.is_set(init_display_flags::USE_GRAPHICS); @@ -299,7 +303,7 @@ bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *pt static std::map plugin_screens; -bool Screen::show(df::viewscreen *screen, df::viewscreen *before, Plugin *plugin) +bool Screen::show(std::unique_ptr screen, df::viewscreen *before, Plugin *plugin) { CHECK_NULL_POINTER(screen); CHECK_INVALID_ARGUMENT(!screen->parent && !screen->child); @@ -316,15 +320,16 @@ bool Screen::show(df::viewscreen *screen, df::viewscreen *before, Plugin *plugin screen->child = parent->child; screen->parent = parent; - parent->child = screen; - if (screen->child) - screen->child->parent = screen; + df::viewscreen* s = screen.release(); + parent->child = s; + if (s->child) + s->child->parent = s; - if (dfhack_viewscreen::is_instance(screen)) - static_cast(screen)->onShow(); + if (dfhack_viewscreen::is_instance(s)) + static_cast(s)->onShow(); if (plugin) - plugin_screens[screen] = plugin; + plugin_screens[s] = plugin; return true; } @@ -371,6 +376,42 @@ bool Screen::hasActiveScreens(Plugin *plugin) return false; } +namespace DFHack { namespace Screen { + +Hide::Hide(df::viewscreen* screen) : + screen_{screen} +{ + extract(screen_); +} + +Hide::~Hide() +{ + if (screen_) + merge(screen_); +} + +void Hide::extract(df::viewscreen* a) +{ + df::viewscreen* ap = a->parent; + df::viewscreen* ac = a->child; + + ap->child = ac; + if (ac) ac->parent = ap; + else Core::getInstance().top_viewscreen = ap; +} + +void Hide::merge(df::viewscreen* a) +{ + df::viewscreen* ap = a->parent; + df::viewscreen* ac = a->parent->child; + + ap->child = a; + a->child = ac; + if (ac) ac->parent = a; + else Core::getInstance().top_viewscreen = a; +} +} } + #ifdef _LINUX class DFHACK_EXPORT renderer { unsigned char *screen; diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index e8c7cfa88..bc8d1ae88 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -219,15 +219,9 @@ df::language_name *Units::getVisibleName(df::unit *unit) { CHECK_NULL_POINTER(unit); + // as of 0.44.11, identity names take precedence over associated histfig names if (auto identity = getIdentity(unit)) - { - auto id_hfig = df::historical_figure::find(identity->histfig_id); - - if (id_hfig) - return &id_hfig->name; - return &identity->name; - } return &unit->name; } @@ -1476,6 +1470,13 @@ bool Units::isMerchant(df::unit* unit) return unit->flags1.bits.merchant == 1; } +bool Units::isDiplomat(df::unit* unit) +{ + CHECK_NULL_POINTER(unit); + + return unit->flags1.bits.diplomat == 1; +} + bool Units::isForest(df::unit* unit) { CHECK_NULL_POINTER(unit); @@ -1579,7 +1580,7 @@ bool Units::isActive(df::unit *unit) { CHECK_NULL_POINTER(unit); - return !unit->flags1.bits.dead; + return !unit->flags1.bits.inactive; } bool Units::isKilled(df::unit *unit) diff --git a/library/xml b/library/xml index c3025feb8..372993c58 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c3025feb80c6f8e7441ea5dcf4f463a9cf89cbbd +Subproject commit 372993c58851153320f3390119cdd2d97caef9db diff --git a/package/linux/dfhack b/package/linux/dfhack index 008f691cf..d0c70765b 100755 --- a/package/linux/dfhack +++ b/package/linux/dfhack @@ -61,7 +61,12 @@ fi export LD_LIBRARY_PATH="./hack/libs:./hack:$LD_LIBRARY_PATH" -PRELOAD_LIB="${PRELOAD_LIB:+$PRELOAD_LIB:}./hack/libdfhack.so" +LIB="./hack/libdfhack.so" +LIBSAN="" +if which objdump > /dev/null; then + LIBSAN="$(objdump -p $LIB | sed -n 's/^.*NEEDED.*\(lib[a-z]san[a-z.0-9]*\).*$/\1/p' | head -n1):" +fi +PRELOAD_LIB="${PRELOAD_LIB:+$PRELOAD_LIB:}${LIBSAN}${LIB}" setarch_arch=$(cat hack/dfhack_setarch.txt || printf i386) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index 0bd2cd6c1..929ee24f6 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -589,7 +589,7 @@ bool VeinGenerator::init_biomes() if (info.geo_index < 0 || !info.geobiome) { - out.printerr("Biome %d is not defined.\n", i); + out.printerr("Biome %zd is not defined.\n", i); return false; } @@ -1567,7 +1567,7 @@ bool VeinGenerator::place_veins(bool verbose) sort(queue.begin(), queue.end(), vein_cmp); // Place tiles - out.print("Processing... ", queue.size()); + out.print("Processing... (%zu)", queue.size()); for (size_t j = 0; j < queue.size(); j++) { @@ -1588,7 +1588,7 @@ bool VeinGenerator::place_veins(bool verbose) out.print("done."); out.print( - "\nVein layer %d of %d: %s %s (%.2f%%)... ", + "\nVein layer %zu of %zu: %s %s (%.2f%%)... ", j+1, queue.size(), MaterialInfo(0,queue[j]->vein.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, queue[j]->vein.second).c_str(), @@ -1597,7 +1597,7 @@ bool VeinGenerator::place_veins(bool verbose) } else { - out.print("\rVein layer %d of %d... ", j+1, queue.size()); + out.print("\rVein layer %zu of %zu... ", j+1, queue.size()); out.flush(); } diff --git a/plugins/Brushes.h b/plugins/Brushes.h index 6095a03da..14a4188e7 100644 --- a/plugins/Brushes.h +++ b/plugins/Brushes.h @@ -214,7 +214,7 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out, bool hasConsole = true) { using namespace DFHack; - int newWidth = 0, newHeight = 0, newZLevels = 0; + int newWidth = 0, newHeight = 0, newZLevels = 0, rv = 0; if (end > start + 1) { @@ -237,7 +237,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out, str.str(""); str << "Set range width <" << width << "> "; - con.lineedit(str.str(), command, hist); + while ((rv = con.lineedit(str.str(), command, hist)) + == Console::RETRY); + if (rv <= Console::FAILURE) + return rv == Console::FAILURE ? CR_FAILURE : CR_FAILURE; hist.add(command); newWidth = command.empty() ? width : atoi(command.c_str()); } else { @@ -251,7 +254,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out, str.str(""); str << "Set range height <" << height << "> "; - con.lineedit(str.str(), command, hist); + while ((rv = con.lineedit(str.str(), command, hist)) + == Console::RETRY); + if (rv <= Console::FAILURE) + return rv == Console::FAILURE ? CR_FAILURE : CR_OK; hist.add(command); newHeight = command.empty() ? height : atoi(command.c_str()); } else { @@ -265,7 +271,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out, str.str(""); str << "Set range z-levels <" << zLevels << "> "; - con.lineedit(str.str(), command, hist); + while ((rv = con.lineedit(str.str(), command, hist)) + == Console::RETRY); + if (rv <= Console::FAILURE) + return rv == Console::FAILURE ? CR_FAILURE : CR_OK; hist.add(command); newZLevels = command.empty() ? zLevels : atoi(command.c_str()); } else { diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e8e1b6bc8..fbf458c4f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -85,7 +85,7 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(advtools advtools.cpp) DFHACK_PLUGIN(autochop autochop.cpp) DFHACK_PLUGIN(autodump autodump.cpp) - DFHACK_PLUGIN(autogems autogems.cpp LINK_LIBRARIES jsoncpp) + DFHACK_PLUGIN(autogems autogems.cpp LINK_LIBRARIES jsoncpp_lib_static) DFHACK_PLUGIN(autohauler autohauler.cpp) DFHACK_PLUGIN(autolabor autolabor.cpp) DFHACK_PLUGIN(automaterial automaterial.cpp) @@ -105,6 +105,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(confirm confirm.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(createitem createitem.cpp) DFHACK_PLUGIN(cursecheck cursecheck.cpp) + DFHACK_PLUGIN(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(deramp deramp.cpp) DFHACK_PLUGIN(dig dig.cpp) DFHACK_PLUGIN(digFlood digFlood.cpp) @@ -137,7 +138,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(misery misery.cpp) DFHACK_PLUGIN(mode mode.cpp) DFHACK_PLUGIN(mousequery mousequery.cpp) - DFHACK_PLUGIN(orders orders.cpp LINK_LIBRARIES jsoncpp) + DFHACK_PLUGIN(orders orders.cpp LINK_LIBRARIES jsoncpp_lib_static) DFHACK_PLUGIN(pathable pathable.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(petcapRemover petcapRemover.cpp) DFHACK_PLUGIN(plants plants.cpp) diff --git a/plugins/advtools.cpp b/plugins/advtools.cpp index 50868560d..a1965cba0 100644 --- a/plugins/advtools.cpp +++ b/plugins/advtools.cpp @@ -482,9 +482,9 @@ static void printCompanionHeader(color_ostream &out, size_t i, df::unit *unit) out << i; out << ": " << getUnitNameProfession(unit); - if (unit->flags1.bits.dead) + if (Units::isDead(unit)) out << " (DEAD)"; - if (unit->flags3.bits.ghostly) + if (Units::isGhost(unit)) out << " (GHOST)"; out << endl; diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index eccd16035..e7f3874d3 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -809,7 +809,7 @@ struct autochop_hook : public df::viewscreen_dwarfmodest if (isInDesignationMenu() && input->count(interface_key::CUSTOM_C)) { sendKey(interface_key::LEAVESCREEN); - Screen::show(new ViewscreenAutochop(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); } else { @@ -852,7 +852,7 @@ command_result df_autochop (color_ostream &out, vector & parameters) return CR_WRONG_USAGE; } if (Maps::IsValid()) - Screen::show(new ViewscreenAutochop(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); return CR_OK; } diff --git a/plugins/autogems.cpp b/plugins/autogems.cpp index b42d5bd16..4121b562f 100644 --- a/plugins/autogems.cpp +++ b/plugins/autogems.cpp @@ -222,7 +222,8 @@ void create_jobs() { } DFhackCExport command_result plugin_onupdate(color_ostream &out) { - if (running && !World::ReadPauseState() && (world->frame_counter % DELTA_TICKS == 0)) { + if (running && !World::ReadPauseState() && Maps::IsValid() && + (world->frame_counter % DELTA_TICKS == 0)) { create_jobs(); } diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 936ce4022..09a756642 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -1497,7 +1497,7 @@ command_result autolabor (color_ostream &out, std::vector & parame if (maximum < minimum || maximum < 0 || minimum < 0) { - out.printerr("Syntax: autolabor [] []\n", maximum, minimum); + out.printerr("Syntax: autolabor [] [], %d > %d\n", maximum, minimum); return CR_WRONG_USAGE; } diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index a3bb97319..c19eb02e9 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -156,7 +156,7 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest } else if (input->count(interface_key::CUSTOM_SHIFT_M)) { - Screen::show(new ViewscreenChooseMaterial(planner.getDefaultItemFilterForType(type)), plugin_self); + Screen::show(dts::make_unique(planner.getDefaultItemFilterForType(type)), plugin_self); } else if (input->count(interface_key::CUSTOM_Q)) { diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index 69325331e..3b1d3fe86 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -295,7 +295,7 @@ static void init_map(color_ostream &out) active = true; if (auto_grow && !grow_burrows.empty()) - out.print("Auto-growing %d burrows.\n", grow_burrows.size()); + out.print("Auto-growing %zu burrows.\n", grow_burrows.size()); } static void deinit_map(color_ostream &out) diff --git a/plugins/changelayer.cpp b/plugins/changelayer.cpp index cf7d4fdbc..5c252b102 100644 --- a/plugins/changelayer.cpp +++ b/plugins/changelayer.cpp @@ -120,12 +120,12 @@ command_result changelayer (color_ostream &out, std::vector & para { if(parameters[i] == "help" || parameters[i] == "?") { - out.print(changelayer_help.c_str()); + out.print("%s",changelayer_help.c_str()); return CR_OK; } if(parameters[i] == "trouble") { - out.print(changelayer_trouble.c_str()); + out.print("%s",changelayer_trouble.c_str()); return CR_OK; } if(parameters[i] == "force") diff --git a/plugins/cleanowned.cpp b/plugins/cleanowned.cpp index 4dd3fd212..90b0e743d 100644 --- a/plugins/cleanowned.cpp +++ b/plugins/cleanowned.cpp @@ -87,7 +87,7 @@ command_result df_cleanowned (color_ostream &out, vector & parameters) return CR_FAILURE; } - out.print("Found total %d items.\n", world->items.all.size()); + out.print("Found total %zd items.\n", world->items.all.size()); for (std::size_t i=0; i < world->items.all.size(); i++) { @@ -160,7 +160,7 @@ command_result df_cleanowned (color_ostream &out, vector & parameters) std::string description; item->getItemDescription(&description, 0); out.print( - "0x%x %s (wear %d)", + "0x%p %s (wear %d)", item, description.c_str(), item->getWear() diff --git a/plugins/command-prompt.cpp b/plugins/command-prompt.cpp index 449da0e80..1c68a8fbb 100644 --- a/plugins/command-prompt.cpp +++ b/plugins/command-prompt.cpp @@ -200,7 +200,10 @@ void viewscreen_commandpromptst::submit() return; submitted = true; prompt_ostream out(this); - Core::getInstance().runCommand(out, get_entry()); + { + Screen::Hide hide_guard(this); + Core::getInstance().runCommand(out, get_entry()); + } if(out.empty() && responses.empty()) Screen::dismiss(this); else @@ -312,7 +315,7 @@ void viewscreen_commandpromptst::feed(std::set *events) command_result show_prompt(color_ostream &out, std::vector & parameters) { - if (Gui::getCurFocus() == "dfhack/commandprompt") + if (Gui::getCurFocus(true) == "dfhack/commandprompt") { Screen::dismiss(Gui::getCurViewscreen(true)); return CR_OK; @@ -320,7 +323,7 @@ command_result show_prompt(color_ostream &out, std::vector & param std::string params; for(size_t i=0;i(params), plugin_self); return CR_OK; } bool hotkey_allow_all(df::viewscreen *top) diff --git a/plugins/cursecheck.cpp b/plugins/cursecheck.cpp index e006bd17c..d7e1248ad 100644 --- a/plugins/cursecheck.cpp +++ b/plugins/cursecheck.cpp @@ -160,8 +160,9 @@ command_result cursecheck (color_ostream &out, vector & parameters) { df::unit * unit = world->units.all[i]; + // filter out all "living" units that are currently removed from play // don't spam all completely dead creatures if not explicitly wanted - if(unit->flags1.bits.dead && ignoreDead) + if((!Units::isActive(unit) && !Units::isKilled(unit)) || (Units::isKilled(unit) && ignoreDead)) { continue; } @@ -217,7 +218,7 @@ command_result cursecheck (color_ostream &out, vector & parameters) cursetype.c_str(), // technically most cursed creatures are undead, // therefore output 'active' if they are not completely dead - unit->flags1.bits.dead ? "deceased" : "active", + unit->flags2.bits.killed ? "deceased" : "active", unit->flags3.bits.ghostly ? "-ghostly" : "", missing ? "-missing" : "" ); @@ -239,9 +240,9 @@ command_result cursecheck (color_ostream &out, vector & parameters) } if (checkWholeMap) - out.print("Number of cursed creatures on map: %d \n", cursecount); + out.print("Number of cursed creatures on map: %zd \n", cursecount); else - out.print("Number of cursed creatures on tile: %d \n", cursecount); + out.print("Number of cursed creatures on tile: %zd \n", cursecount); return CR_OK; } diff --git a/plugins/cxxrandom.cpp b/plugins/cxxrandom.cpp new file mode 100644 index 000000000..531c71b5e --- /dev/null +++ b/plugins/cxxrandom.cpp @@ -0,0 +1,274 @@ +/* Plugin for exporting C++11 random number functionality +*Exports functions for random number generation +*Functions: +- seedRNG(seed) +- rollInt(min, max) +- rollDouble(min, max) +- rollNormal(mean, std_deviation) +- rollBool(chance_for_true) +- resetIndexRolls(string, array_length) --String identifies the instance of SimpleNumDistribution to reset +- rollIndex(string, array_length) --String identifies the instance of SimpleNumDistribution to use + --(Shuffles a vector of indices, Next() increments through then reshuffles when end() is reached) +Author: Josh Cooper +Created: Dec. 13 2017 +Updated: Dec. 21 2017 +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Error.h" +#include "Core.h" +#include "DataFuncs.h" +#include +#include +#include + +using namespace DFHack; +DFHACK_PLUGIN("cxxrandom"); +#define PLUGIN_VERSION 2.0 +color_ostream *cout = nullptr; + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + cout = &out; + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) +{ + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + return CR_OK; +} + + +class EnginesKeeper +{ +private: + EnginesKeeper() {} + std::unordered_map m_engines; + uint16_t counter = 0; +public: + static EnginesKeeper& Instance() + { + static EnginesKeeper instance; + return instance; + } + uint16_t NewEngine( uint64_t seed ) + { + std::mt19937_64 engine( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() ); + m_engines[++counter] = engine; + return counter; + } + void DestroyEngine( uint16_t id ) + { + m_engines.erase( id ); + } + void NewSeed( uint16_t id, uint64_t seed ) + { + CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() ); + m_engines[id].seed( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() ); + } + std::mt19937_64& RNG( uint16_t id ) + { + CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() ); + return m_engines[id]; + } +}; + + +uint16_t GenerateEngine( uint64_t seed ) +{ + return EnginesKeeper::Instance().NewEngine( seed ); +} + +void DestroyEngine( uint16_t id ) +{ + EnginesKeeper::Instance().DestroyEngine( id ); +} + +void NewSeed( uint16_t id, uint64_t seed ) +{ + EnginesKeeper::Instance().NewSeed( id, seed ); +} + + +int rollInt(uint16_t id, int min, int max) +{ + std::uniform_int_distribution ND(min, max); + return ND(EnginesKeeper::Instance().RNG(id)); +} + +double rollDouble(uint16_t id, double min, double max) +{ + std::uniform_real_distribution ND(min, max); + return ND(EnginesKeeper::Instance().RNG(id)); +} + +double rollNormal(uint16_t id, double mean, double stddev) +{ + std::normal_distribution ND(mean, stddev); + return ND(EnginesKeeper::Instance().RNG(id)); +} + +bool rollBool(uint16_t id, float p) +{ + std::bernoulli_distribution ND(p); + return ND(EnginesKeeper::Instance().RNG(id)); +} + + +class NumberSequence +{ +private: + unsigned short m_position = 0; + std::vector m_numbers; +public: + NumberSequence(){} + NumberSequence( int64_t start, int64_t end ) + { + for( int64_t i = start; i <= end; ++i ) + { + m_numbers.push_back( i ); + } + } + void Add( int64_t num ) { m_numbers.push_back( num ); } + void Reset() { m_numbers.clear(); } + int64_t Next() + { + if(m_position >= m_numbers.size()) + { + m_position = 0; + } + return m_numbers[m_position++]; + } + void Shuffle( uint16_t id ) + { + std::shuffle( std::begin( m_numbers ), std::end( m_numbers ), EnginesKeeper::Instance().RNG( id ) ); + } + void Print() + { + char buffer1[256] = {0}; + char buffer2[256] = {0}; + for( auto v : m_numbers ) + { + sprintf( buffer2, "%s%" PRId64, buffer1, v ); + sprintf( buffer1, "%s ", buffer2 ); + } + cout->print( "%s", buffer1 ); + } +}; + +class SequenceKeeper +{ +private: + SequenceKeeper() {} + std::unordered_map m_sequences; + uint16_t counter = 0; +public: + static SequenceKeeper& Instance() + { + static SequenceKeeper instance; + return instance; + } + uint16_t MakeNumSequence( int64_t start, int64_t end ) + { + m_sequences[++counter] = NumberSequence( start, end ); + return counter; + } + uint16_t MakeNumSequence() + { + m_sequences[++counter] = NumberSequence(); + return counter; + } + void DestroySequence( uint16_t id ) + { + m_sequences.erase( id ); + } + void AddToSequence( uint16_t id, int64_t num ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + m_sequences[id].Add( num ); + } + void Shuffle( uint16_t id, uint16_t rng_id ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + m_sequences[id].Shuffle( rng_id ); + } + int64_t NextInSequence( uint16_t id ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + return m_sequences[id].Next(); + } + void PrintSequence( uint16_t id ) + { + CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() ); + auto seq = m_sequences[id]; + seq.Print(); + } +}; + + +uint16_t MakeNumSequence( int64_t start, int64_t end ) +{ + if( start == end ) + { + return SequenceKeeper::Instance().MakeNumSequence(); + } + return SequenceKeeper::Instance().MakeNumSequence( start, end ); +} + +void DestroyNumSequence( uint16_t id ) +{ + SequenceKeeper::Instance().DestroySequence( id ); +} + +void AddToSequence( uint16_t id, int64_t num ) +{ + SequenceKeeper::Instance().AddToSequence( id, num ); +} + +void ShuffleSequence( uint16_t rngID, uint16_t id ) +{ + SequenceKeeper::Instance().Shuffle( id, rngID ); +} + +int64_t NextInSequence( uint16_t id ) +{ + return SequenceKeeper::Instance().NextInSequence( id ); +} + +void DebugSequence( uint16_t id ) +{ + SequenceKeeper::Instance().PrintSequence( id ); +} + + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(GenerateEngine), + DFHACK_LUA_FUNCTION(DestroyEngine), + DFHACK_LUA_FUNCTION(NewSeed), + DFHACK_LUA_FUNCTION(rollInt), + DFHACK_LUA_FUNCTION(rollDouble), + DFHACK_LUA_FUNCTION(rollNormal), + DFHACK_LUA_FUNCTION(rollBool), + DFHACK_LUA_FUNCTION(MakeNumSequence), + DFHACK_LUA_FUNCTION(DestroyNumSequence), + DFHACK_LUA_FUNCTION(AddToSequence), + DFHACK_LUA_FUNCTION(ShuffleSequence), + DFHACK_LUA_FUNCTION(NextInSequence), + DFHACK_LUA_FUNCTION(DebugSequence), + DFHACK_LUA_END +}; diff --git a/plugins/deramp.cpp b/plugins/deramp.cpp index fd7ceb3d7..97a3dc184 100644 --- a/plugins/deramp.cpp +++ b/plugins/deramp.cpp @@ -7,10 +7,12 @@ #include "DataDefs.h" #include "modules/Maps.h" +#include "modules/Job.h" #include "TileTypes.h" #include "df/map_block.h" #include "df/world.h" +#include "df/job.h" using std::vector; using std::string; @@ -20,6 +22,24 @@ using namespace df::enums; DFHACK_PLUGIN("deramp"); REQUIRE_GLOBAL(world); +static void doDeramp(df::map_block* block, df::map_block* above, int x, int y, df::tiletype oldT) +{ + // Current tile is a ramp. + // Set current tile, as accurately as can be expected + df::tiletype newT = findSimilarTileType(oldT, tiletype_shape::FLOOR); + + // If no change, skip it (couldn't find a good tile type) + if (oldT == newT) + return; + // Set new tile type, clear designation + block->tiletype[x][y] = newT; + block->designation[x][y].bits.dig = tile_dig_designation::No; + + // Check the tile above this one, in case a downward slope needs to be removed. + if ((above) && (tileShape(above->tiletype[x][y]) == tiletype_shape::RAMP_TOP)) + above->tiletype[x][y] = tiletype::OpenSpace; // open space +} + command_result df_deramp (color_ostream &out, vector & parameters) { if (!parameters.empty()) @@ -36,6 +56,23 @@ command_result df_deramp (color_ostream &out, vector & parameters) int count = 0; int countbad = 0; + df::job_list_link* next; + for (df::job_list_link* jl = world->jobs.list.next;jl; jl = next) { + next = jl->next; + df::job* job = jl->item; + if (job->job_type != df::job_type::RemoveStairs) + continue; + df::map_block *block = Maps::getTileBlock(job->pos); + df::coord2d bpos = job->pos - block->map_pos; + df::tiletype oldT = block->tiletype[bpos.x][bpos.y]; + if (tileShape(oldT) != tiletype_shape::RAMP) + continue; + df::map_block *above = Maps::getTileBlock(job->pos + df::coord(0,0,1)); + doDeramp(block, above, bpos.x, bpos.y, oldT); + count++; + Job::removeJob(job); + } + int num_blocks = 0, blocks_total = world->map.map_blocks.size(); for (int i = 0; i < blocks_total; i++) { @@ -50,20 +87,7 @@ command_result df_deramp (color_ostream &out, vector & parameters) if ((tileShape(oldT) == tiletype_shape::RAMP) && (block->designation[x][y].bits.dig == tile_dig_designation::Default)) { - // Current tile is a ramp. - // Set current tile, as accurately as can be expected - df::tiletype newT = findSimilarTileType(oldT, tiletype_shape::FLOOR); - - // If no change, skip it (couldn't find a good tile type) - if (oldT == newT) - continue; - // Set new tile type, clear designation - block->tiletype[x][y] = newT; - block->designation[x][y].bits.dig = tile_dig_designation::No; - - // Check the tile above this one, in case a downward slope needs to be removed. - if ((above) && (tileShape(above->tiletype[x][y]) == tiletype_shape::RAMP_TOP)) - above->tiletype[x][y] = tiletype::OpenSpace; // open space + doDeramp(block, above, x, y, oldT); count++; } // ramp fixer diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index d64969c91..b5f9d34d4 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -113,7 +113,7 @@ command_result eventExample(color_ostream& out, vector& parameters) { //static int timerCount=0; //static int timerDenom=0; void jobInitiated(color_ostream& out, void* job_) { - out.print("Job initiated! 0x%X\n", job_); + out.print("Job initiated! %p\n", job_); /* df::job* job = (df::job*)job_; out.print(" completion_timer = %d\n", job->completion_timer); @@ -124,15 +124,15 @@ void jobInitiated(color_ostream& out, void* job_) { } void jobCompleted(color_ostream& out, void* job) { - out.print("Job completed! 0x%X\n", job); + out.print("Job completed! %p\n", job); } void timePassed(color_ostream& out, void* ptr) { - out.print("Time: %d\n", (intptr_t)(ptr)); + out.print("Time: %zi\n", (intptr_t)(ptr)); } void unitDeath(color_ostream& out, void* ptr) { - out.print("Death: %d\n", (intptr_t)(ptr)); + out.print("Death: %zi\n", (intptr_t)(ptr)); } void itemCreate(color_ostream& out, void* ptr) { @@ -143,15 +143,15 @@ void itemCreate(color_ostream& out, void* ptr) { df::item* item = df::global::world->items.all[item_index]; df::item_type type = item->getType(); df::coord pos = item->pos; - out.print("Item created: %d, %s, at (%d,%d,%d)\n", (intptr_t)(ptr), ENUM_KEY_STR(item_type, type).c_str(), pos.x, pos.y, pos.z); + out.print("Item created: %zi, %s, at (%d,%d,%d)\n", (intptr_t)(ptr), ENUM_KEY_STR(item_type, type).c_str(), pos.x, pos.y, pos.z); } void building(color_ostream& out, void* ptr) { - out.print("Building created/destroyed: %d\n", (intptr_t)ptr); + out.print("Building created/destroyed: %zi\n", (intptr_t)ptr); } void construction(color_ostream& out, void* ptr) { - out.print("Construction created/destroyed: 0x%X\n", ptr); + out.print("Construction created/destroyed: %p\n", ptr); df::construction* constr = (df::construction*)ptr; df::coord pos = constr->pos; out.print(" (%d,%d,%d)\n", pos.x, pos.y, pos.z); @@ -168,7 +168,7 @@ void syndrome(color_ostream& out, void* ptr) { } void invasion(color_ostream& out, void* ptr) { - out.print("New invasion! %d\n", (intptr_t)ptr); + out.print("New invasion! %zi\n", (intptr_t)ptr); } void unitAttack(color_ostream& out, void* ptr) { diff --git a/plugins/devel/kittens.cpp b/plugins/devel/kittens.cpp index 4ed884e27..5de94ced3 100644 --- a/plugins/devel/kittens.cpp +++ b/plugins/devel/kittens.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -24,13 +25,12 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(ui); REQUIRE_GLOBAL(world); -//FIXME: possible race conditions with calling kittens from the IO thread and shutdown from Core. -volatile bool shutdown_flag = false; -volatile bool final_flag = true; -bool timering = false; -bool trackmenu_flg = false; -bool trackpos_flg = false; -bool statetrack = false; +std::atomic shutdown_flag{false}; +std::atomic final_flag{true}; +std::atomic timering{false}; +std::atomic trackmenu_flg{false}; +std::atomic trackpos_flg{0}; +std::atomic statetrack{0}; int32_t last_designation[3] = {-30000, -30000, -30000}; int32_t last_mouse[2] = {-1, -1}; df::ui_sidebar_mode last_menu = df::ui_sidebar_mode::Default; @@ -93,14 +93,12 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { - if(timering == true) + if(timering) { uint64_t time2 = GetTimeMs64(); - // harmless potential data race here... uint64_t delta = time2-timeLast; - // harmless potential data race here... timeLast = time2; - out.print("Time delta = %d ms\n", delta); + out.print("Time delta = %d ms\n", int(delta)); } if(trackmenu_flg) { @@ -135,30 +133,30 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) command_result trackmenu (color_ostream &out, vector & parameters) { - if(trackmenu_flg) + bool is_running = trackmenu_flg.exchange(false); + if(is_running) { - trackmenu_flg = false; return CR_OK; } else { - trackmenu_flg = true; is_enabled = true; last_menu = ui->main.mode; out.print("Menu: %d\n",last_menu); + trackmenu_flg = true; return CR_OK; } } command_result trackpos (color_ostream &out, vector & parameters) { - trackpos_flg = !trackpos_flg; + trackpos_flg.fetch_xor(1); is_enabled = true; return CR_OK; } command_result trackstate ( color_ostream& out, vector< string >& parameters ) { - statetrack = !statetrack; + statetrack.fetch_xor(1); return CR_OK; } @@ -169,10 +167,10 @@ command_result colormods (color_ostream &out, vector & parameters) for(df::creature_raw* rawlion : vec) { df::caste_raw * caste = rawlion->caste[0]; - out.print("%s\nCaste addr 0x%x\n",rawlion->creature_id.c_str(), &caste->color_modifiers); + out.print("%s\nCaste addr %p\n",rawlion->creature_id.c_str(), &caste->color_modifiers); for(size_t j = 0; j < caste->color_modifiers.size();j++) { - out.print("mod %d: 0x%x\n", j, caste->color_modifiers[j]); + out.print("mod %zd: %p\n", j, caste->color_modifiers[j]); } } return CR_OK; @@ -180,20 +178,19 @@ command_result colormods (color_ostream &out, vector & parameters) command_result ktimer (color_ostream &out, vector & parameters) { - if(timering) + bool is_running = timering.exchange(false); + if(is_running) { - timering = false; return CR_OK; } uint64_t timestart = GetTimeMs64(); { CoreSuspender suspend; + uint64_t timeend = GetTimeMs64(); + timeLast = timeend; + timering = true; + out.print("Time to suspend = %d ms\n", int(timeend - timestart)); } - uint64_t timeend = GetTimeMs64(); - out.print("Time to suspend = %d ms\n",timeend - timestart); - // harmless potential data race here... - timeLast = timeend; - timering = true; is_enabled = true; return CR_OK; } @@ -255,7 +252,7 @@ command_result kittens (color_ostream &out, vector & parameters) Console::color_value color = COLOR_BLUE; while(1) { - if(shutdown_flag) + if(shutdown_flag || !con.isInited()) { final_flag = true; con.reset_color(); diff --git a/plugins/devel/memview.cpp b/plugins/devel/memview.cpp index d84a3b578..5019ac637 100644 --- a/plugins/devel/memview.cpp +++ b/plugins/devel/memview.cpp @@ -188,7 +188,7 @@ command_result memview (color_ostream &out, vector & parameters) isValid=true; if(!isValid) { - out.printerr("Invalid address:%x\n",memdata.addr); + out.printerr("Invalid address: %p\n",memdata.addr); mymutex->unlock(); return CR_OK; } diff --git a/plugins/devel/notes.cpp b/plugins/devel/notes.cpp index cc394dbd5..33b768af7 100644 --- a/plugins/devel/notes.cpp +++ b/plugins/devel/notes.cpp @@ -51,7 +51,7 @@ command_result df_notes (color_ostream &con, vector & parameters) { t_note* note = (*note_list)[i]; - con.print("Note %x at: %d/%d/%d\n",note, note->x, note->y, note->z); + con.print("Note %p at: %d/%d/%d\n",note, note->x, note->y, note->z); con.print("Note id: %d\n", note->id); con.print("Note symbol: '%c'\n", note->symbol); diff --git a/plugins/devel/vectors.cpp b/plugins/devel/vectors.cpp index 717f4bdae..0052fcede 100644 --- a/plugins/devel/vectors.cpp +++ b/plugins/devel/vectors.cpp @@ -135,8 +135,8 @@ static void printVec(color_ostream &con, const char* msg, t_vecTriplet *vec, uintptr_t length = (intptr_t)vec->end - (intptr_t)vec->start; uintptr_t offset = pos - start; - con.print("%8s offset %06p, addr %010p, start %010p, length %u", - msg, offset, pos, vec->start, length); + con.print("%8s offset 0x%06zx, addr 0x%01zx, start 0x%01zx, length %zi", + msg, offset, pos, intptr_t(vec->start), length); if (length >= 4 && length % 4 == 0) { void *ptr = vec->start; @@ -200,10 +200,10 @@ command_result df_vectors (color_ostream &con, vector & parameters) // Found the range containing the start if (!range.isInRange((void *)end)) { - con.print("Scanning %u bytes would read past end of memory " + con.print("Scanning %zi bytes would read past end of memory " "range.\n", bytes); size_t diff = end - (intptr_t)range.end; - con.print("Cutting bytes down by %u.\n", diff); + con.print("Cutting bytes down by %zi.\n", diff); end = (uintptr_t) range.end; } diff --git a/plugins/devel/zoom.cpp b/plugins/devel/zoom.cpp index d302d4900..80f4dfaf1 100644 --- a/plugins/devel/zoom.cpp +++ b/plugins/devel/zoom.cpp @@ -7,6 +7,7 @@ #include #include #include "modules/Gui.h" +#include "modules/Screen.h" #include "modules/World.h" #include "df/enabler.h" @@ -52,7 +53,7 @@ command_result df_zoom (color_ostream &out, std::vector & paramete return CR_FAILURE; } df::zoom_commands cmd = zcmap[parameters[0]]; - enabler->zoom_display(cmd); + Screen::zoom(cmd); if (cmd == df::zoom_commands::zoom_fullscreen) enabler->fullscreen = !enabler->fullscreen; return CR_OK; diff --git a/plugins/diggingInvaders/assignJob.cpp b/plugins/diggingInvaders/assignJob.cpp index 2b780bd63..b253e7833 100644 --- a/plugins/diggingInvaders/assignJob.cpp +++ b/plugins/diggingInvaders/assignJob.cpp @@ -260,7 +260,7 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_mapsite_id), 0); if ( out_items.size() != 1 ) { - out.print("%s, %d: wrong size: %d.\n", __FILE__, __LINE__, out_items.size()); + out.print("%s, %d: wrong size: %zu.\n", __FILE__, __LINE__, out_items.size()); return -1; } out_items[0]->moveToGround(firstInvader->pos.x, firstInvader->pos.y, firstInvader->pos.z); diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 9bbc5191e..4afcae6f8 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -62,6 +62,7 @@ #include #include #include +#include using namespace std; @@ -301,7 +302,7 @@ command_result diggingInvadersCommand(color_ostream& out, std::vectorunits.all.size(); a++ ) { df::unit* unit = world->units.all[a]; - if ( unit->flags1.bits.dead ) + if ( !Units::isActive(unit) ) continue; if ( Units::isCitizen(unit) ) { if ( localPts.find(unit->pos) != localPts.end() ) diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index e786b21dd..f4a62a118 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -1062,7 +1062,7 @@ public: { df::unit *selected_unit = (selected_column == 1) ? dwarf_activity_column.getFirstSelectedElem() : nullptr; Screen::dismiss(this); - Screen::show(new ViewscreenDwarfStats(selected_unit), plugin_self); + Screen::show(dts::make_unique(selected_unit), plugin_self); } else if (input->count(interface_key::CUSTOM_SHIFT_Z)) { @@ -1643,7 +1643,7 @@ public: { auto unitscr = df::allocate(); unitscr->unit = unit; - Screen::show(unitscr); + Screen::show(std::unique_ptr(unitscr)); } } else if (input->count(interface_key::CUSTOM_SHIFT_Z)) @@ -1737,7 +1737,7 @@ private: static void open_stats_screen() { - Screen::show(new ViewscreenFortStats(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); } static void add_work_history(df::unit *unit, activity_type type) @@ -1977,12 +1977,12 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector & par else if (cmd == 's' || cmd == 'S') { if(Maps::IsValid()) - Screen::show(new ViewscreenFortStats(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); } else if (cmd == 'p' || cmd == 'P') { if(Maps::IsValid()) - Screen::show(new ViewscreenPreferences(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); } else if (cmd == 'r' || cmd == 'R') { diff --git a/plugins/dwarfvet.cpp b/plugins/dwarfvet.cpp index 19f00e888..598388460 100644 --- a/plugins/dwarfvet.cpp +++ b/plugins/dwarfvet.cpp @@ -383,7 +383,7 @@ void AnimalHospital::processPatients(color_ostream &out) { } // Check to make sure the unit hasn't expired before assigning a job, or if they've been healed - if (!real_unit || real_unit->flags1.bits.dead || !real_unit->health->flags.bits.needs_healthcare) { + if (!real_unit || !Units::isActive(real_unit) || !real_unit->health->flags.bits.needs_healthcare) { // discharge the patient from the hospital this->dischargePatient(*patient, out); return; @@ -582,7 +582,7 @@ void tickHandler(color_ostream& out, void* data) { /* Now add it to the scratch AHZ */ for (vector::iterator current_hospital = to_be_added.begin(); current_hospital != to_be_added.end(); current_hospital++) { // Add it to the vector - out.print("Adding new hospital #id at x1 %d y1: %d z: %d\n", + out.print("Adding new hospital #id: %d at x1 %d y1: %d z: %d\n", (*current_hospital)->id, (*current_hospital)->x1, (*current_hospital)->y1, @@ -624,7 +624,7 @@ processUnits: df::unit* unit = units[a]; /* As hilarious as it would be, lets not treat FB :) */ - if ( unit->flags1.bits.dead || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) { + if ( !Units::isActive(unit) || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) { continue; } diff --git a/plugins/embark-assistant/defs.h b/plugins/embark-assistant/defs.h index 3196bd5ee..31e548b33 100644 --- a/plugins/embark-assistant/defs.h +++ b/plugins/embark-assistant/defs.h @@ -58,9 +58,15 @@ namespace embark_assist { int16_t biome_index[10]; // Indexed through biome_offset; -1 = null, Index of region, [0] not used int16_t biome[10]; // Indexed through biome_offset; -1 = null, df::biome_type, [0] not used uint8_t biome_count; - bool evil_weather[10]; - bool evil_weather_possible; - bool evil_weather_full; + bool blood_rain[10]; + bool blood_rain_possible; + bool blood_rain_full; + bool permanent_syndrome_rain[10]; + bool permanent_syndrome_rain_possible; + bool permanent_syndrome_rain_full; + bool temporary_syndrome_rain[10]; + bool temporary_syndrome_rain_possible; + bool temporary_syndrome_rain_full; bool reanimating[10]; bool reanimating_possible; bool reanimating_full; @@ -204,6 +210,25 @@ namespace embark_assist { Very_Deep }; + enum class syndrome_rain_ranges : int8_t { + NA = -1, + Any, + Permanent, + Temporary, + Not_Permanent, + None + }; + + enum class reanimation_ranges : int8_t { + NA = -1, + Both, + Any, + Thralling, + Reanimation, + Not_Thralling, + None + }; + /* // Future possible enhancement enum class freezing_ranges : int8_t { NA = -1, @@ -232,9 +257,9 @@ namespace embark_assist { all_present_ranges soil_min_everywhere; soil_ranges soil_max; /*freezing_ranges freezing;*/ - yes_no_ranges evil_weather; // Will probably blow up with the magic release arcs... - yes_no_ranges reanimation; - yes_no_ranges thralling; + yes_no_ranges blood_rain; // Will probably blow up with the magic release arcs... + syndrome_rain_ranges syndrome_rain; + reanimation_ranges reanimation; int8_t spire_count_min; // N/A(-1), 0-9 int8_t spire_count_max; // N/A(-1), 0-9 magma_ranges magma_min; diff --git a/plugins/embark-assistant/embark-assistant.cpp b/plugins/embark-assistant/embark-assistant.cpp index bd70df06a..728d96f58 100644 --- a/plugins/embark-assistant/embark-assistant.cpp +++ b/plugins/embark-assistant/embark-assistant.cpp @@ -259,7 +259,9 @@ command_result embark_assistant(color_ostream &out, std::vector & for (uint8_t l = 1; l < 10; l++) { embark_assist::main::state->survey_results[i][k].biome_index[l] = -1; embark_assist::main::state->survey_results[i][k].biome[l] = -1; - embark_assist::main::state->survey_results[i][k].evil_weather[l] = false; + embark_assist::main::state->survey_results[i][k].blood_rain[l] = false; + embark_assist::main::state->survey_results[i][k].permanent_syndrome_rain[l] = false; + embark_assist::main::state->survey_results[i][k].temporary_syndrome_rain[l] = false; embark_assist::main::state->survey_results[i][k].reanimating[l] = false; embark_assist::main::state->survey_results[i][k].thralling[l] = false; } diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 3f972635f..12571dc28 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -46,9 +46,9 @@ namespace embark_assist { soil_min, soil_min_everywhere, soil_max, - evil_weather, + blood_rain, + syndrome_rain, reanimation, - thralling, spire_count_min, spire_count_max, magma_min, @@ -451,9 +451,7 @@ namespace embark_assist { case fields::waterfall: case fields::flat: - case fields::evil_weather: - case fields::reanimation: - case fields::thralling: + case fields::blood_rain: { embark_assist::defs::yes_no_ranges k = embark_assist::defs::yes_no_ranges::NA; while (true) { @@ -576,6 +574,91 @@ namespace embark_assist { break; + case fields::syndrome_rain: + { + embark_assist::defs::syndrome_rain_ranges k = embark_assist::defs::syndrome_rain_ranges::NA; + while (true) { + switch (k) { + case embark_assist::defs::syndrome_rain_ranges::NA: + element->list.push_back({ "N/A", static_cast(k) }); + break; + + case embark_assist::defs::syndrome_rain_ranges::Any: + element->list.push_back({ "Any Syndrome", static_cast(k) }); + break; + + case embark_assist::defs::syndrome_rain_ranges::Permanent: + element->list.push_back({ "Permanent Syndrome", static_cast(k) }); + break; + + case embark_assist::defs::syndrome_rain_ranges::Temporary: + element->list.push_back({ "Temporary Syndrome", static_cast(k) }); + break; + + case embark_assist::defs::syndrome_rain_ranges::Not_Permanent: + element->list.push_back({ "Not Permanent Syndrome", static_cast(k) }); + break; + + case embark_assist::defs::syndrome_rain_ranges::None: + element->list.push_back({ "No Syndrome", static_cast(k) }); + break; + + } + + if (k == embark_assist::defs::syndrome_rain_ranges::None) { + break; + } + + k = static_cast (static_cast(k) + 1); + } + } + + break; + + case fields::reanimation: + { + embark_assist::defs::reanimation_ranges k = embark_assist::defs::reanimation_ranges::NA; + while (true) { + switch (k) { + case embark_assist::defs::reanimation_ranges::NA: + element->list.push_back({ "N/A", static_cast(k) }); + break; + + case embark_assist::defs::reanimation_ranges::Both: + element->list.push_back({ "Reanimation & Thralling", static_cast(k) }); + break; + + case embark_assist::defs::reanimation_ranges::Any: + element->list.push_back({ "Reanimation or Thralling", static_cast(k) }); + break; + + case embark_assist::defs::reanimation_ranges::Thralling: + element->list.push_back({ "Thralling", static_cast(k) }); + break; + + case embark_assist::defs::reanimation_ranges::Reanimation: + element->list.push_back({ "Reanimation", static_cast(k) }); + break; + + case embark_assist::defs::reanimation_ranges::Not_Thralling: + element->list.push_back({ "Not Thralling", static_cast(k) }); + break; + + case embark_assist::defs::reanimation_ranges::None: + element->list.push_back({ "None", static_cast(k) }); + break; + } + + if (k == embark_assist::defs::reanimation_ranges::None) { + break; + } + + k = static_cast (static_cast(k) + 1); + } + } + + break; + case fields::spire_count_min: case fields::spire_count_max: for (int16_t k = -1; k <= 9; k++) { @@ -834,16 +917,16 @@ namespace embark_assist { state->finder_list.push_back({ "Min Soil Everywhere", static_cast(i) }); break; - case fields::evil_weather: - state->finder_list.push_back({ "Evil Weather", static_cast(i) }); + case fields::blood_rain: + state->finder_list.push_back({ "Blood Rain", static_cast(i) }); break; - case fields::reanimation: - state->finder_list.push_back({ "Reanimation", static_cast(i) }); + case fields::syndrome_rain: + state->finder_list.push_back({ "Syndrome Rain", static_cast(i) }); break; - case fields::thralling: - state->finder_list.push_back({ "Thralling", static_cast(i) }); + case fields::reanimation: + state->finder_list.push_back({ "Reanimation", static_cast(i) }); break; case fields::clay: @@ -1058,19 +1141,19 @@ namespace embark_assist { static_cast(state->ui[static_cast(i)]->current_value); break; - case fields::evil_weather: - finder.evil_weather = + case fields::blood_rain: + finder.blood_rain = static_cast(state->ui[static_cast(i)]->current_value); break; - case fields::reanimation: - finder.reanimation = - static_cast(state->ui[static_cast(i)]->current_value); + case fields::syndrome_rain: + finder.syndrome_rain = + static_cast(state->ui[static_cast(i)]->current_value); break; - case fields::thralling: - finder.thralling = - static_cast(state->ui[static_cast(i)]->current_value); + case fields::reanimation: + finder.reanimation = + static_cast(state->ui[static_cast(i)]->current_value); break; case fields::clay: @@ -1380,7 +1463,7 @@ void embark_assist::finder_ui::init(DFHack::Plugin *plugin_self, embark_assist:: if (!embark_assist::finder_ui::state) { // First call. Have to do the setup embark_assist::finder_ui::ui_setup(find_callback, max_inorganic); } - Screen::show(new ViewscreenFindUi(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); } //=============================================================================== diff --git a/plugins/embark-assistant/help_ui.cpp b/plugins/embark-assistant/help_ui.cpp index 32fb28dbb..60f96637c 100644 --- a/plugins/embark-assistant/help_ui.cpp +++ b/plugins/embark-assistant/help_ui.cpp @@ -209,6 +209,11 @@ namespace embark_assist{ help_text.push_back("Min/Max soil uses the same terminology as DF for 1-4. The Min Soil"); help_text.push_back("Everywhere toggles the Min Soil parameter between acting as All and"); help_text.push_back("and Present."); + help_text.push_back("Syndrome Rain allows you to search for Permanent and Temporary syndromes,"); + help_text.push_back("where Permanent allows for Temporary ones as well, but not the reverse, as"); + help_text.push_back("Not Permanent matches everything except Permanent syndromes."); + help_text.push_back("Reanimation packages thralling and reanimation into a single search"); + help_text.push_back("criterion. Not Tralling means nothing and just reanimation is matched."); help_text.push_back("The parameters for biomes, regions, etc. all require that the required"); help_text.push_back("feature is Present in the embark, and entering the same value multiple"); help_text.push_back("times does nothing (the first match ticks all requirements off). It can be"); @@ -254,9 +259,9 @@ namespace embark_assist{ help_text.push_back(" emulate the sizing logic exactly."); help_text.push_back("- There's currently a DF bug (#0010267) that causes adamantine spires"); help_text.push_back(" reaching caverns that have been removed at world gen to fail to be"); - help_text.push_back(" generated. It's likely this bug also affects magma pools."); + help_text.push_back(" generated at all. It's likely this bug also affects magma pools."); help_text.push_back(" This plugin does not address this but scripts can correct it."); - help_text.push_back("Version 0.3 2018-02-26"); + help_text.push_back("Version 0.4 2018-06-21"); break; } @@ -322,5 +327,5 @@ namespace embark_assist{ //=============================================================================== void embark_assist::help_ui::init(DFHack::Plugin *plugin_self) { - Screen::show(new embark_assist::help_ui::ViewscreenHelpUi(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); } diff --git a/plugins/embark-assistant/matcher.cpp b/plugins/embark-assistant/matcher.cpp index d4a2f8ee3..96998f43b 100644 --- a/plugins/embark-assistant/matcher.cpp +++ b/plugins/embark-assistant/matcher.cpp @@ -48,7 +48,9 @@ namespace embark_assist { bool flux_found = false; uint8_t max_soil = 0; bool uneven = false; - bool evil_weather_found = false; + bool blood_rain_found = false; + bool permanent_syndrome_rain_found = false; + bool temporary_syndrome_rain_found = false; bool reanimation_found = false; bool thralling_found = false; uint8_t spire_count = 0; @@ -183,21 +185,39 @@ namespace embark_assist { if (finder->soil_max != embark_assist::defs::soil_ranges::NA && mlt->at(i).at(k).soil_depth > static_cast(finder->soil_max)) return false; - // Evil Weather - if (survey_results->at(x).at(y).evil_weather[mlt->at(i).at(k).biome_offset]) { - if (finder->evil_weather == embark_assist::defs::yes_no_ranges::No) return false; - evil_weather_found = true; + // Blood Rain + if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) { + if (finder->blood_rain == embark_assist::defs::yes_no_ranges::No) return false; + blood_rain_found = true; + } + + // Syndrome Rain, Permanent + if (survey_results->at(x).at(y).permanent_syndrome_rain[mlt->at(i).at(k).biome_offset]) { + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary || + finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Not_Permanent || + finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) return false; + permanent_syndrome_rain_found = true; + } + + // Syndrome Rain, Temporary + if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->at(i).at(k).biome_offset]) { + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent || + finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) return false; + temporary_syndrome_rain_found = true; } // Reanmation if (survey_results->at(x).at(y).reanimating[mlt->at(i).at(k).biome_offset]) { - if (finder->reanimation == embark_assist::defs::yes_no_ranges::No) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling || + finder->reanimation == embark_assist::defs::reanimation_ranges::None) return false; reanimation_found = true; } // Thralling if (survey_results->at(x).at(y).thralling[mlt->at(i).at(k).biome_offset]) { - if (finder->thralling == embark_assist::defs::yes_no_ranges::No) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation || + finder->reanimation == embark_assist::defs::reanimation_ranges::Not_Thralling || + finder->reanimation == embark_assist::defs::reanimation_ranges::None) return false; thralling_found = true; } @@ -303,14 +323,19 @@ namespace embark_assist { finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::Present && max_soil < static_cast(finder->soil_min)) return false; - // Evil Weather - if (finder->evil_weather == embark_assist::defs::yes_no_ranges::Yes && !evil_weather_found) return false; + // Blood Rain + if (finder->blood_rain == embark_assist::defs::yes_no_ranges::Yes && !blood_rain_found) return false; - // Reanimation - if (finder->reanimation == embark_assist::defs::yes_no_ranges::Yes && !reanimation_found) return false; + // Syndrome Rain + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Any && !permanent_syndrome_rain_found && !temporary_syndrome_rain_found) return false; + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent && !permanent_syndrome_rain_found) return false; + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary && !temporary_syndrome_rain_found) return false; - // Thralling - if (finder->thralling == embark_assist::defs::yes_no_ranges::Yes && !thralling_found) return false; + // Reanimation + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Both && !(reanimation_found && thralling_found)) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Any && !reanimation_found && !thralling_found) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling && !thralling_found) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation && !reanimation_found) return false; // Spires if (finder->spire_count_min != -1 && finder->spire_count_min > spire_count) return false; @@ -597,46 +622,74 @@ namespace embark_assist { break; } - // Evil Weather - switch (finder->evil_weather) { + // Blood Rain + switch (finder->blood_rain) { case embark_assist::defs::yes_no_ranges::NA: break; // No restriction case embark_assist::defs::yes_no_ranges::Yes: - if (!tile->evil_weather_possible) return false; + if (!tile->blood_rain_possible) return false; break; case embark_assist::defs::yes_no_ranges::No: - if (tile->evil_weather_full) return false; + if (tile->blood_rain_full) return false; break; } - // Reanimating - switch (finder->reanimation) { - case embark_assist::defs::yes_no_ranges::NA: + // Syndrome Rain + switch (finder->syndrome_rain) { + case embark_assist::defs::syndrome_rain_ranges::NA: break; // No restriction - case embark_assist::defs::yes_no_ranges::Yes: - if (!tile->reanimating_possible) return false; + case embark_assist::defs::syndrome_rain_ranges::Any: + if (!tile->permanent_syndrome_rain_possible && !tile->temporary_syndrome_rain_possible) return false; break; - case embark_assist::defs::yes_no_ranges::No: - if (tile->reanimating_full) return false; + case embark_assist::defs::syndrome_rain_ranges::Permanent: + if (!tile->permanent_syndrome_rain_possible) return false; + break; + + case embark_assist::defs::syndrome_rain_ranges::Temporary: + if (!tile->temporary_syndrome_rain_possible) return false; + break; + + case embark_assist::defs::syndrome_rain_ranges::Not_Permanent: + if (tile->permanent_syndrome_rain_full) return false; + break; + + case embark_assist::defs::syndrome_rain_ranges::None: + if (tile->permanent_syndrome_rain_full || tile->temporary_syndrome_rain_full) return false; break; } - // Thralling - switch (finder->thralling) { - case embark_assist::defs::yes_no_ranges::NA: + // Reanimating + switch (finder->reanimation) { + case embark_assist::defs::reanimation_ranges::NA: break; // No restriction - case embark_assist::defs::yes_no_ranges::Yes: + case embark_assist::defs::reanimation_ranges::Both: + if (!tile->reanimating_possible || !tile->thralling_possible) return false; + break; + + case embark_assist::defs::reanimation_ranges::Any: + if (!tile->reanimating_possible && !tile->thralling_possible) return false; + break; + + case embark_assist::defs::reanimation_ranges::Thralling: if (!tile->thralling_possible) return false; break; - case embark_assist::defs::yes_no_ranges::No: + case embark_assist::defs::reanimation_ranges::Reanimation: + if (!tile->reanimating_possible) return false; + break; + + case embark_assist::defs::reanimation_ranges::Not_Thralling: if (tile->thralling_full) return false; break; + + case embark_assist::defs::reanimation_ranges::None: + if (tile->reanimating_full || tile->thralling_full) return false; + break; } // Spire Count Min/Max @@ -950,46 +1003,74 @@ namespace embark_assist { // Soil Max // Can't say anything as the preliminary data isn't reliable - // Evil Weather - switch (finder->evil_weather) { + // Blood Rain + switch (finder->blood_rain) { case embark_assist::defs::yes_no_ranges::NA: break; // No restriction case embark_assist::defs::yes_no_ranges::Yes: - if (!tile->evil_weather_possible) return false; + if (!tile->blood_rain_possible) return false; break; case embark_assist::defs::yes_no_ranges::No: - if (tile->evil_weather_full) return false; + if (tile->blood_rain_full) return false; break; } - // Reanimating - switch (finder->reanimation) { - case embark_assist::defs::yes_no_ranges::NA: + // Syndrome Rain + switch (finder->syndrome_rain) { + case embark_assist::defs::syndrome_rain_ranges::NA: break; // No restriction - case embark_assist::defs::yes_no_ranges::Yes: - if (!tile->reanimating_possible) return false; + case embark_assist::defs::syndrome_rain_ranges::Any: + if (!tile->permanent_syndrome_rain_possible && !tile->temporary_syndrome_rain_possible) return false; break; - case embark_assist::defs::yes_no_ranges::No: - if (tile->reanimating_full) return false; + case embark_assist::defs::syndrome_rain_ranges::Permanent: + if (!tile->permanent_syndrome_rain_possible) return false; + break; + + case embark_assist::defs::syndrome_rain_ranges::Temporary: + if (!tile->temporary_syndrome_rain_possible) return false; + break; + + case embark_assist::defs::syndrome_rain_ranges::Not_Permanent: + if (tile->permanent_syndrome_rain_full) return false; + break; + + case embark_assist::defs::syndrome_rain_ranges::None: + if (tile->permanent_syndrome_rain_full || tile->temporary_syndrome_rain_full) return false; break; } - // Thralling - switch (finder->thralling) { - case embark_assist::defs::yes_no_ranges::NA: + // Reanimating + switch (finder->reanimation) { + case embark_assist::defs::reanimation_ranges::NA: break; // No restriction - case embark_assist::defs::yes_no_ranges::Yes: + case embark_assist::defs::reanimation_ranges::Both: + if (!tile->reanimating_possible || !tile->thralling_possible) return false; + break; + + case embark_assist::defs::reanimation_ranges::Any: + if (!tile->reanimating_possible && !tile->thralling_possible) return false; + break; + + case embark_assist::defs::reanimation_ranges::Thralling: if (!tile->thralling_possible) return false; break; - case embark_assist::defs::yes_no_ranges::No: + case embark_assist::defs::reanimation_ranges::Reanimation: + if (!tile->reanimating_possible) return false; + break; + + case embark_assist::defs::reanimation_ranges::Not_Thralling: if (tile->thralling_full) return false; break; + + case embark_assist::defs::reanimation_ranges::None: + if (tile->reanimating_full || tile->thralling_full) return false; + break; } // Spire Count Min/Max diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index b057ff290..ebe151117 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -259,12 +259,14 @@ namespace embark_assist { //================================================================================= void survey_evil_weather(embark_assist::defs::world_tile_data *survey_results) { -// color_ostream_proxy out(Core::getInstance().getConsole()); df::world_data *world_data = world->world_data; for (uint16_t i = 0; i < world->interaction_instances.all.size(); i++) { auto interaction = world->raws.interactions[world->interaction_instances.all[i]->interaction_id]; uint16_t region_index = world->interaction_instances.all[i]->region_index; + bool blood_rain = false; + bool permanent_syndrome_rain = false; + bool temporary_syndrome_rain = false; bool thralling = false; bool reanimating = false; @@ -282,62 +284,115 @@ namespace embark_assist { else if (interaction->targets[k]->getType() == df::interaction_target_type::MATERIAL) { df::interaction_target_materialst* material = virtual_cast(interaction->targets[k]); if (material && DFHack::MaterialInfo(material->mat_type, material->mat_index).isInorganic()) { - for (uint16_t l = 0; l < world->raws.inorganics[material->mat_index]->material.syndrome.size(); l++) { - for (uint16_t m = 0; m < world->raws.inorganics[material->mat_index]->material.syndrome[l]->ce.size(); m++) { - if (world->raws.inorganics[material->mat_index]->material.syndrome[l]->ce[m]->getType() == df::creature_interaction_effect_type::FLASH_TILE) { + for (const auto &syndrome : world->raws.inorganics[material->mat_index]->material.syndrome) { + for (const auto &ce : syndrome->ce) { + df::creature_interaction_effect_type ce_type = ce->getType(); + if (ce_type == df::creature_interaction_effect_type::FLASH_TILE) { // Using this as a proxy. There seems to be a group of 4 effects for thralls: // display symbol, flash symbol, phys att change and one more. thralling = true; } + else if (ce_type == df::creature_interaction_effect_type::PAIN || + ce_type == df::creature_interaction_effect_type::SWELLING || + ce_type == df::creature_interaction_effect_type::OOZING || + ce_type == df::creature_interaction_effect_type::BRUISING || + ce_type == df::creature_interaction_effect_type::BLISTERS || + ce_type == df::creature_interaction_effect_type::NUMBNESS || + ce_type == df::creature_interaction_effect_type::PARALYSIS || + ce_type == df::creature_interaction_effect_type::FEVER || + ce_type == df::creature_interaction_effect_type::BLEEDING || + ce_type == df::creature_interaction_effect_type::COUGH_BLOOD || + ce_type == df::creature_interaction_effect_type::VOMIT_BLOOD || + ce_type == df::creature_interaction_effect_type::NAUSEA || + ce_type == df::creature_interaction_effect_type::UNCONSCIOUSNESS || + ce_type == df::creature_interaction_effect_type::NECROSIS || + ce_type == df::creature_interaction_effect_type::IMPAIR_FUNCTION || + ce_type == df::creature_interaction_effect_type::DROWSINESS || + ce_type == df::creature_interaction_effect_type::DIZZINESS || + ce_type == df::creature_interaction_effect_type::ERRATIC_BEHAVIOR) { // Doubtful if possible for region. + if (ce->end == -1) { + permanent_syndrome_rain = true; + } + else { + temporary_syndrome_rain = true; + } + } } } } + else { // If not inorganic it's always blood, as far as known. + blood_rain = true; + } } } } for (uint16_t k = 0; k < world_data->regions[region_index]->region_coords.size(); k++) { - survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y).evil_weather[5] = true; - survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y).reanimating[5] = reanimating; - survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y).thralling[5] = thralling; + auto &results = survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y); + results.blood_rain[5] = blood_rain; + results.permanent_syndrome_rain[5] = permanent_syndrome_rain; + results.temporary_syndrome_rain[5] = temporary_syndrome_rain; + results.reanimating[5] = reanimating; + results.thralling[5] = thralling; } } for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) { - survey_results->at(i).at(k).evil_weather_possible = false; - survey_results->at(i).at(k).reanimating_possible = false; - survey_results->at(i).at(k).thralling_possible = false; - survey_results->at(i).at(k).evil_weather_full = true; - survey_results->at(i).at(k).reanimating_full = true; - survey_results->at(i).at(k).thralling_full = true; + auto &results = survey_results->at(i).at(k); + results.blood_rain_possible = false; + results.permanent_syndrome_rain_possible = false; + results.temporary_syndrome_rain_possible = false; + results.reanimating_possible = false; + results.thralling_possible = false; + results.blood_rain_full = true; + results.permanent_syndrome_rain_full = true; + results.temporary_syndrome_rain_full = true; + results.reanimating_full = true; + results.thralling_full = true; for (uint8_t l = 1; l < 10; l++) { - if (survey_results->at(i).at(k).biome_index[l] != -1) { + if (results.biome_index[l] != -1) { df::coord2d adjusted = apply_offset(i, k, l); - survey_results->at(i).at(k).evil_weather[l] = survey_results->at(adjusted.x).at(adjusted.y).evil_weather[5]; - survey_results->at(i).at(k).reanimating[l] = survey_results->at(adjusted.x).at(adjusted.y).reanimating[5]; - survey_results->at(i).at(k).thralling[l] = survey_results->at(adjusted.x).at(adjusted.y).thralling[5]; + results.blood_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).blood_rain[5]; + results.permanent_syndrome_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).permanent_syndrome_rain[5]; + results.temporary_syndrome_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).temporary_syndrome_rain[5]; + results.reanimating[l] = survey_results->at(adjusted.x).at(adjusted.y).reanimating[5]; + results.thralling[l] = survey_results->at(adjusted.x).at(adjusted.y).thralling[5]; + + if (results.blood_rain[l]) { + results.blood_rain_possible = true; + } + else { + results.blood_rain_full = false; + } - if (survey_results->at(i).at(k).evil_weather[l]) { - survey_results->at(i).at(k).evil_weather_possible = true; + if (results.permanent_syndrome_rain[l]) { + results.permanent_syndrome_rain_possible = true; } else { - survey_results->at(i).at(k).evil_weather_full = false; + results.permanent_syndrome_rain_full = false; } - if (survey_results->at(i).at(k).reanimating[l]) { - survey_results->at(i).at(k).reanimating_possible = true; + if (results.temporary_syndrome_rain[l]) { + results.temporary_syndrome_rain_possible = true; } else { - survey_results->at(i).at(k).reanimating_full = false; + results.temporary_syndrome_rain_full = false; } - if (survey_results->at(i).at(k).thralling[l]) { - survey_results->at(i).at(k).thralling_possible = true; + if (results.reanimating[l]) { + results.reanimating_possible = true; } else { - survey_results->at(i).at(k).thralling_full = false; + results.reanimating_full = false; + } + + if (results.thralling[l]) { + results.thralling_possible = true; + } + else { + results.thralling_full = false; } } } @@ -405,23 +460,24 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat uint16_t geo_index; uint16_t sav_ev; uint8_t offset_count = 0; - survey_results->at(i).at(k).surveyed = false; - survey_results->at(i).at(k).aquifer_count = 0; - survey_results->at(i).at(k).clay_count = 0; - survey_results->at(i).at(k).sand_count = 0; - survey_results->at(i).at(k).flux_count = 0; - survey_results->at(i).at(k).min_region_soil = 10; - survey_results->at(i).at(k).max_region_soil = 0; - survey_results->at(i).at(k).waterfall = false; - survey_results->at(i).at(k).savagery_count[0] = 0; - survey_results->at(i).at(k).savagery_count[1] = 0; - survey_results->at(i).at(k).savagery_count[2] = 0; - survey_results->at(i).at(k).evilness_count[0] = 0; - survey_results->at(i).at(k).evilness_count[1] = 0; - survey_results->at(i).at(k).evilness_count[2] = 0; - survey_results->at(i).at(k).metals.resize(state->max_inorganic); - survey_results->at(i).at(k).economics.resize(state->max_inorganic); - survey_results->at(i).at(k).minerals.resize(state->max_inorganic); + auto &results = survey_results->at(i).at(k); + results.surveyed = false; + results.aquifer_count = 0; + results.clay_count = 0; + results.sand_count = 0; + results.flux_count = 0; + results.min_region_soil = 10; + results.max_region_soil = 0; + results.waterfall = false; + results.savagery_count[0] = 0; + results.savagery_count[1] = 0; + results.savagery_count[2] = 0; + results.evilness_count[0] = 0; + results.evilness_count[1] = 0; + results.evilness_count[2] = 0; + results.metals.resize(state->max_inorganic); + results.economics.resize(state->max_inorganic); + results.minerals.resize(state->max_inorganic); // Evil weather and rivers are handled in later operations. Should probably be merged into one. for (uint8_t l = 1; l < 10; l++) @@ -430,53 +486,53 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat if (adjusted.x != i || adjusted.y != k || l == 5) { offset_count++; - survey_results->at(i).at(k).biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id; - survey_results->at(i).at(k).biome[l] = get_biome_type(adjusted.x, adjusted.y, k); + results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id; + results.biome[l] = get_biome_type(adjusted.x, adjusted.y, k); geo_index = world_data->region_map[adjusted.x][adjusted.y].geo_index; - if (!geo_summary->at(geo_index).aquifer_absent) survey_results->at(i).at(k).aquifer_count++; - if (!geo_summary->at(geo_index).clay_absent) survey_results->at(i).at(k).clay_count++; - if (!geo_summary->at(geo_index).sand_absent) survey_results->at(i).at(k).sand_count++; - if (!geo_summary->at(geo_index).flux_absent) survey_results->at(i).at(k).flux_count++; + if (!geo_summary->at(geo_index).aquifer_absent) results.aquifer_count++; + if (!geo_summary->at(geo_index).clay_absent) results.clay_count++; + if (!geo_summary->at(geo_index).sand_absent) results.sand_count++; + if (!geo_summary->at(geo_index).flux_absent) results.flux_count++; - if (geo_summary->at(geo_index).soil_size < survey_results->at(i).at(k).min_region_soil) - survey_results->at(i).at(k).min_region_soil = geo_summary->at(geo_index).soil_size; + if (geo_summary->at(geo_index).soil_size < results.min_region_soil) + results.min_region_soil = geo_summary->at(geo_index).soil_size; - if (geo_summary->at(geo_index).soil_size > survey_results->at(i).at(k).max_region_soil) - survey_results->at(i).at(k).max_region_soil = geo_summary->at(geo_index).soil_size; + if (geo_summary->at(geo_index).soil_size > results.max_region_soil) + results.max_region_soil = geo_summary->at(geo_index).soil_size; sav_ev = world_data->region_map[adjusted.x][adjusted.y].savagery / 33; if (sav_ev == 3) sav_ev = 2; - survey_results->at(i).at(k).savagery_count[sav_ev]++; + results.savagery_count[sav_ev]++; sav_ev = world_data->region_map[adjusted.x][adjusted.y].evilness / 33; if (sav_ev == 3) sav_ev = 2; - survey_results->at(i).at(k).evilness_count[sav_ev]++; + results.evilness_count[sav_ev]++; for (uint16_t m = 0; m < state->max_inorganic; m++) { - if (geo_summary->at(geo_index).possible_metals[m]) survey_results->at(i).at(k).metals[m] = true; - if (geo_summary->at(geo_index).possible_economics[m]) survey_results->at(i).at(k).economics[m] = true; - if (geo_summary->at(geo_index).possible_minerals[m]) survey_results->at(i).at(k).minerals[m] = true; + if (geo_summary->at(geo_index).possible_metals[m]) results.metals[m] = true; + if (geo_summary->at(geo_index).possible_economics[m]) results.economics[m] = true; + if (geo_summary->at(geo_index).possible_minerals[m]) results.minerals[m] = true; } } else { - survey_results->at(i).at(k).biome_index[l] = -1; - survey_results->at(i).at(k).biome[l] = -1; + results.biome_index[l] = -1; + results.biome[l] = -1; } } - survey_results->at(i).at(k).biome_count = 0; + results.biome_count = 0; for (uint8_t l = 1; l < 10; l++) { - if (survey_results->at(i).at(k).biome[l] != -1) survey_results->at(i).at(k).biome_count++; + if (results.biome[l] != -1) results.biome_count++; } - if (survey_results->at(i).at(k).aquifer_count == offset_count) survey_results->at(i).at(k).aquifer_count = 256; - if (survey_results->at(i).at(k).clay_count == offset_count) survey_results->at(i).at(k).clay_count = 256; - if (survey_results->at(i).at(k).sand_count == offset_count) survey_results->at(i).at(k).sand_count = 256; - if (survey_results->at(i).at(k).flux_count == offset_count) survey_results->at(i).at(k).flux_count = 256; + if (results.aquifer_count == offset_count) results.aquifer_count = 256; + if (results.clay_count == offset_count) results.clay_count = 256; + if (results.sand_count == offset_count) results.sand_count = 256; + if (results.flux_count == offset_count) results.flux_count = 256; for (uint8_t l = 0; l < 3; l++) { - if (survey_results->at(i).at(k).savagery_count[l] == offset_count) survey_results->at(i).at(k).savagery_count[l] = 256; - if (survey_results->at(i).at(k).evilness_count[l] == offset_count) survey_results->at(i).at(k).evilness_count[l] = 256; + if (results.savagery_count[l] == offset_count) results.savagery_count[l] = 256; + if (results.evilness_count[l] == offset_count) results.evilness_count[l] = 256; } } } @@ -543,7 +599,7 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data else { mlt->at(i).at(k).biome_offset = 5; - }; + } survey_results->at(x).at(y).biome_index[mlt->at(i).at(k).biome_offset] = world_data->region_map[adjusted.x][adjusted.y].region_id; diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 34613cafb..da6a2f3b1 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -696,7 +696,7 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest void display_settings() { - Screen::show(new embark_tools_settings, plugin_self); + Screen::show(dts::make_unique(), plugin_self); } inline bool is_valid_page() diff --git a/plugins/fix-unit-occupancy.cpp b/plugins/fix-unit-occupancy.cpp index b2e5d23ae..d602ae213 100644 --- a/plugins/fix-unit-occupancy.cpp +++ b/plugins/fix-unit-occupancy.cpp @@ -211,7 +211,7 @@ DFhackCExport command_result plugin_enable (color_ostream &out, bool enable) DFhackCExport command_result plugin_onupdate (color_ostream &out) { - if (is_enabled && World::isFortressMode()) + if (is_enabled && World::isFortressMode() && Maps::IsValid()) { if (world->frame_counter % run_interval == 0 && !World::ReadPauseState()) { diff --git a/plugins/follow.cpp b/plugins/follow.cpp index 21a297bd1..e9733d5da 100644 --- a/plugins/follow.cpp +++ b/plugins/follow.cpp @@ -156,7 +156,7 @@ command_result follow (color_ostream &out, std::vector & parameter ss << "Unpause to begin following " << world->raws.creatures.all[followedUnit->race]->name[0]; if (followedUnit->name.has_name) ss << " " << followedUnit->name.first_name; ss << ". Simply manually move the view to break the following.\n"; - out.print(ss.str().c_str()); + out.print("%s", ss.str().c_str()); } else followedUnit = 0; is_enabled = (followedUnit != NULL); diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index 19c1202d6..ab39bb0d6 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -298,7 +298,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u else { // The specified body part has not been found, and we've reached the end of the list. Report failure. - if (verbose) { Core::printerr("The specified body part (%s) does not belong to the chosen unit. Please double-check to ensure that your spelling is correct, and that you have not chosen a dismembered bodypart.\n"); } + if (verbose) { Core::printerr("The specified body part (%s) does not belong to the chosen unit. Please double-check to ensure that your spelling is correct, and that you have not chosen a dismembered bodypart.\n",targetBodyPart->token.c_str()); } return false; } diff --git a/plugins/fortplan.cpp b/plugins/fortplan.cpp index a123348d2..5dceb35c9 100644 --- a/plugins/fortplan.cpp +++ b/plugins/fortplan.cpp @@ -270,7 +270,7 @@ command_result fortplan(color_ostream &out, vector & params) { checkCode = layout[checkY][checkX]; } - con.print(" - Code at (%d,%d) is '%s': ",checkX,checkY,checkCode.c_str()); + con.print(" - Code at (%zu,%zu) is '%s': ",checkX,checkY,checkCode.c_str()); auto checkIndex = std::find_if(buildings.begin(), buildings.end(), MatchesCode(checkCode.c_str())); //if (checkIndex == buildings.end()) { // con.print("this is not a valid code, so we keep going.\n"); @@ -331,16 +331,16 @@ command_result fortplan(color_ostream &out, vector & params) { offsetCursor.y -= yOffset; DFHack::Gui::setCursorCoords(offsetCursor.x, offsetCursor.y, offsetCursor.z); if (!buildingInfo.allocate()) { - con.print("*** There was an error placing building with code '%s' centered at (%d,%d).\n",curCode.c_str(),x,y); + con.print("*** There was an error placing building with code '%s' centered at (%zu,%zu).\n",curCode.c_str(),x,y); } DFHack::Gui::setCursorCoords(cursor.x, cursor.y, cursor.z); } else if (block) { //con.print("Placing a building with code '%s' with corner at (%d,%d) and default size %dx%d.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight); if (!buildingInfo.allocate()) { - con.print("*** There was an error placing building with code '%s' with corner at (%d,%d).\n",curCode.c_str(),x,y); + con.print("*** There was an error placing building with code '%s' with corner at (%zu,%zu).\n",curCode.c_str(),x,y); } } else { - con.print("*** Found a code '%s' at (%d,%d) for a building with default size %dx%d with an invalid size designation.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight); + con.print("*** Found a code '%s' at (%zu,%zu) for a building with default size %dx%d with an invalid size designation.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight); } } else { //buildingSize = findBuildingExtent(layout, x, y, -1, -1, out); @@ -350,7 +350,7 @@ command_result fortplan(color_ostream &out, vector & params) { } else { //con.print("Building a(n) %s.\n",buildingInfo.name.c_str()); if (!buildingInfo.allocate()) { - con.print("*** There was an error placing the %s at (%d,%d).\n",buildingInfo.name.c_str(),x,y); + con.print("*** There was an error placing the %s at (%zu,%zu).\n",buildingInfo.name.c_str(),x,y); } } } diff --git a/plugins/generated-creature-renamer.cpp b/plugins/generated-creature-renamer.cpp index e70324dd2..625c093cf 100644 --- a/plugins/generated-creature-renamer.cpp +++ b/plugins/generated-creature-renamer.cpp @@ -199,11 +199,10 @@ command_result list_creatures(color_ostream &out, std::vector & pa auto creatureRaw = world->raws.creatures.all[i]; if (!creatureRaw->flags.is_set(df::enums::creature_raw_flags::GENERATED)) continue; - out.print(creatureRaw->creature_id.c_str()); + out.print("%s",creatureRaw->creature_id.c_str()); if (detailed) { - out.print("\t"); - out.print(creatureRaw->caste[0]->description.c_str()); + out.print("\t%s",creatureRaw->caste[0]->description.c_str()); } out.print("\n"); } diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index d3ee600e3..2728afcf9 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -312,7 +312,7 @@ static command_result hotkeys_cmd(color_ostream &out, vector & paramete if (Gui::getFocusString(top_screen) != "dfhack/viewscreen_hotkeys") { find_active_keybindings(top_screen); - Screen::show(new ViewscreenHotkeys(top_screen), plugin_self); + Screen::show(dts::make_unique(top_screen), plugin_self); } } } diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 3e3b6b2a0..0607b50c5 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -156,14 +156,14 @@ static command_result job_material_in_job(color_ostream &out, MaterialInfo &new_ if (item_mat != cur_mat) { - out.printerr("Job item %d has different material: %s\n", + out.printerr("Job item %zu has different material: %s\n", i, item_mat.toString().c_str()); return CR_FAILURE; } if (!new_mat.matches(*item)) { - out.printerr("Job item %d requirements not satisfied by %s.\n", + out.printerr("Job item %zu requirements not satisfied by %s.\n", i, new_mat.toString().c_str()); return CR_FAILURE; } diff --git a/plugins/labormanager/labormanager.cpp b/plugins/labormanager/labormanager.cpp index 7c749edce..9f874edc0 100644 --- a/plugins/labormanager/labormanager.cpp +++ b/plugins/labormanager/labormanager.cpp @@ -1224,7 +1224,7 @@ private: if (p1 || p2) { df::unit* other = p1 ? act->unit_noble : act->unit_actor; - if (other && !(other->flags1.bits.dead || + if (other && !(!Units::isActive(other) || (other->job.current_job && (other->job.current_job->job_type == df::job_type::Sleep || other->job.current_job->job_type == df::job_type::Rest)) || @@ -1247,7 +1247,7 @@ private: for (auto u2 = world->units.active.begin(); u2 != world->units.active.end(); ++u2) { if ((*u2)->relationship_ids[df::unit_relationship_type::Mother] == dwarf->dwarf->id && - !(*u2)->flags1.bits.dead && + Units::isActive(*u2) && ((*u2)->profession == df::profession::CHILD || (*u2)->profession == df::profession::BABY)) { dwarf->has_children = true; @@ -1757,7 +1757,7 @@ public: } if (print_debug) - out.print("available count = %d, distinct labors needed = %d\n", available_dwarfs.size(), pq.size()); + out.print("available count = %zu, distinct labors needed = %zu\n", available_dwarfs.size(), pq.size()); std::map to_assign; @@ -1958,7 +1958,7 @@ public: /* Assign any leftover dwarfs to "standard" labors */ if (print_debug) - out.print("After assignment, %d dwarfs left over\n", available_dwarfs.size()); + out.print("After assignment, %zu dwarfs left over\n", available_dwarfs.size()); for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++) { diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp index f0dcf1ce3..5dd64812f 100644 --- a/plugins/liquids.cpp +++ b/plugins/liquids.cpp @@ -188,8 +188,11 @@ command_result df_liquids (color_ostream &out_, vector & parameters) std::stringstream str; print_prompt(str, cur_mode); str << "# "; - if(out.lineedit(str.str(),input,liquids_hist) == -1) - return CR_FAILURE; + int rv; + while ((rv = out.lineedit(str.str(),input,liquids_hist)) + == Console::RETRY); + if (rv <= Console::FAILURE) + return rv == Console::FAILURE ? CR_FAILURE : CR_OK; liquids_hist.add(input); commands.clear(); diff --git a/plugins/lua/cxxrandom.lua b/plugins/lua/cxxrandom.lua new file mode 100644 index 000000000..1521c4c9e --- /dev/null +++ b/plugins/lua/cxxrandom.lua @@ -0,0 +1,214 @@ +local _ENV = mkmodule('plugins.cxxrandom') + +function MakeNewEngine(seed) + if type(seed) == 'number' then + if seed == 0 then + print(":WARNING: Seeds equal to 0 are used if no seed is provided. This indicates to cxxrandom.plug.dll that the engine needs to be seeded with the current time.\nRecommendation: use a non-zero value for your seed, or don't provide a seed to use the time since epoch(1969~).") + end + return GenerateEngine(seed) + elseif type(seed) == 'nil' then + return GenerateEngine(0) + else + error("Argument `seed` must be a number, or nil.") + end +end + +--Class: crng +------------- +crng = {} +function crng:new(engineID, destroyEngineOnDestruction, distrib) + local o = {} + self.__index = self + local idtype = type(engineID) + local flagtype = type(destroyEngineOnDestruction) + + if idtype == 'number' then + o.rngID = engineID + elseif idtype == 'nil' then + o.rngID = GenerateEngine(0) + else + error("Invalid argument type (engineID): " .. tostring(engineID)) + end + + if flagtype ~= 'nil' and flagtype == 'boolean' then + o.destroyid = destroyEngineOnDestruction + elseif flagtype == 'nil' then + o.destroyid = true + else + error("Invalid arugment type (destroyEngineOnDestruction): " .. tostring(destroyEngineOnDestruction)) + end + + if type(distrib) ~= 'nil' then + if type(distrib) == 'table' and type(distrib.next) == 'function' then + o.distrib = distrib + o.distrib.rngID = o.rngID + else + error("Invalid distribution used as an argument. Cannot set this as the number distribution.") + end + end + setmetatable(o,self) + return o +end +--crng destructor - we may need to destroy the engine, the user may be doing it manually though +function crng:__gc() + if self.destroyid then + DestroyEngine(self.rngID) + end +end +function crng:changeSeed(seed) + if type(seed) == 'number' then + if seed == 0 then + print(":WARNING: Seeds equal to 0 are used if no seed is provided. This indicates to cxxrandom.plug.dll that the engine needs to be seeded with the current time.\nRecommendation: use a non-zero value for your seed, or don't provide a seed to use the time since epoch(1969~).") + end + return NewSeed(self.rngID, seed) + elseif type(seed) == 'nil' then + return NewSeed(self.rngID, 0) + else + error("Argument `seed` must be a number, or nil.") + end +end +function crng:setNumDistrib(distrib) + if type(distrib) == 'table' and type(distrib.next) == 'function' then + self.distrib = distrib + self.distrib.rngID = self.rngID + else + error("Invalid distribution used as an argument. Cannot set this as the number distribution.") + end +end +function crng:next() + if type(self.distrib) == 'table' and type(self.distrib.next) == 'function' then + return self.distrib:next(self.rngID) + else + error("crng object does not have a valid number distribution set") + end +end +function crng:shuffle() + if type(self.distrib) == 'table' and type(self.distrib.shuffle) == 'function' then + self.distrib:shuffle(self.rngID) + else + print(":WARNING: No self.distrib.shuffle not found.") + changeSeed(0) + end +end + +--Class: normal_distribution +---------------------------- +normal_distribution = {} +function normal_distribution:new(avg, stddev) + local o = {} + self.__index = self + if type(avg) ~= 'number' or type(stddev) ~= 'number' then + error("Invalid arguments in normal_distribution construction. Average and standard deviation must be numbers.") + end + o.average = avg + o.std_deviation = stddev + setmetatable(o,self) + return o +end +function normal_distribution:next(id) + return rollNormal(id, self.average, self.std_deviation) +end + +--Class: real_distribution +---------------------------- +real_distribution = {} +function real_distribution:new(min, max) + local o = {} + self.__index = self + if type(min) ~= 'number' or type(max) ~= 'number' then + error("Invalid arguments in real_distribution construction. min and max must be numbers.") + end + o.min = min + o.max = max + setmetatable(o,self) + return o +end +function real_distribution:next(id) + return rollDouble(id, self.min, self.max) +end + +--Class: int_distribution +---------------------------- +int_distribution = {} +function int_distribution:new(min, max) + local o = {} + self.__index = self + if type(min) ~= 'number' or type(max) ~= 'number' then + error("Invalid arguments in int_distribution construction. min and max must be numbers.") + end + o.min = min + o.max = max + setmetatable(o,self) + return o +end +function int_distribution:next(id) + return rollInt(id, self.min, self.max) +end + +--Class: bool_distribution +---------------------------- +bool_distribution = {} +function bool_distribution:new(chance) + local o = {} + self.__index = self + if type(min) ~= 'number' or type(max) ~= 'number' then + error("Invalid arguments in bool_distribution construction. min and max must be numbers.") + end + o.p = chance + setmetatable(o,self) + return o +end +function bool_distribution:next(id) + return rollBool(id, self.p) +end + +--Class: num_sequence +---------------------------- +num_sequence = {} +function num_sequence:new(a,b) + local o = {} + self.__index = self + local btype = type(b) + local atype = type(a) + if atype == 'number' and btype == 'number' then + if a == b then + print(":WARNING: You've provided two equal arguments to initialize your sequence with. This is the mechanism used to indicate to cxxrandom.plug.dll that an empty sequence is desired.\nRecommendation: provide no arguments if you wish for an empty sequence.") + end + o.seqID = MakeNumSequence(a, b) + elseif atype == 'table' then + o.seqID = MakeNumSequence(0,0) + for _,v in pairs(a) do + if type(v) ~= 'number' then + error("num_sequence can only be initialized using numbers. " .. tostring(v) .. " is not a number.") + end + AddToSequence(o.seqID, v) + end + elseif atype == "nil" and btype == "nil" then + o.seqID = MakeNumSequence(0,0) + else + error("Invalid arguments - a: " .. tostring(a) .. " and b: " .. tostring(b)) + end + print("seqID:"..o.seqID) + setmetatable(o,self) + return o +end +function num_sequence:__gc() + DestroyNumSequence(self.seqID) +end +function num_sequence:add(x) + if type(x) ~= 'number' then + error("Cannot add non-number to num_sequence.") + end + AddToSequence(self.seqID, x) +end +function num_sequence:next() + return NextInSequence(self.seqID) +end +function num_sequence:shuffle() + if self.rngID == 'nil' then + error("Add num_sequence object to crng as distribution, before attempting to shuffle.") + end + ShuffleSequence(self.rngID, self.seqID) +end + +return _ENV \ No newline at end of file diff --git a/plugins/lua/workflow.lua b/plugins/lua/workflow.lua index 6c4230a51..9122f290d 100644 --- a/plugins/lua/workflow.lua +++ b/plugins/lua/workflow.lua @@ -185,7 +185,14 @@ end function job_outputs.MakeCrafts(callback, job) local mat_type, mat_index, mat_mask = guess_job_material(job) - callback{ is_craft = true, mat_type = mat_type, mat_index = mat_index, mat_mask = mat_mask } + callback{ + is_craft = true, + item_type = -1, + item_subtype = -1, + mat_type = mat_type, + mat_index = mat_index, + mat_mask = mat_mask + } end local plant_products = { diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 5dab2ab09..c41e782ed 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -1157,7 +1157,7 @@ viewscreen_unitlaborsst::viewscreen_unitlaborsst(vector &src, int cur if (!Units::isOwnGroup(unit)) cur->allowEdit = false; - if (unit->flags1.bits.dead) + if (!Units::isActive(unit)) cur->allowEdit = false; if (unit->flags2.bits.visitor) @@ -1814,14 +1814,14 @@ void viewscreen_unitlaborsst::feed(set *events) if (events->count(interface_key::CUSTOM_B)) { - Screen::show(new viewscreen_unitbatchopst(units, true, &do_refresh_names), plugin_self); + Screen::show(dts::make_unique(units, true, &do_refresh_names), plugin_self); } if (events->count(interface_key::CUSTOM_E)) { vector tmp; tmp.push_back(cur); - Screen::show(new viewscreen_unitbatchopst(tmp, false, &do_refresh_names), plugin_self); + Screen::show(dts::make_unique(tmp, false, &do_refresh_names), plugin_self); } if (events->count(interface_key::CUSTOM_P)) @@ -1832,11 +1832,11 @@ void viewscreen_unitlaborsst::feed(set *events) has_selected = true; if (has_selected) { - Screen::show(new viewscreen_unitprofessionset(units, true), plugin_self); + Screen::show(dts::make_unique(units, true), plugin_self); } else { vector tmp; tmp.push_back(cur); - Screen::show(new viewscreen_unitprofessionset(tmp, false), plugin_self); + Screen::show(dts::make_unique(tmp, false), plugin_self); } } @@ -2187,7 +2187,7 @@ struct unitlist_hook : df::viewscreen_unitlistst { if (units[page].size()) { - Screen::show(new viewscreen_unitlaborsst(units[page], cursor_pos[page]), plugin_self); + Screen::show(dts::make_unique(units[page], cursor_pos[page]), plugin_self); return; } } diff --git a/plugins/mode.cpp b/plugins/mode.cpp index 66fb488a6..5e58c50a0 100644 --- a/plugins/mode.cpp +++ b/plugins/mode.cpp @@ -96,6 +96,7 @@ command_result mode (color_ostream &out_, vector & parameters) string command = ""; bool set = false; bool abuse = false; + int rv = 0; t_gamemodes gm; for(auto iter = parameters.begin(); iter != parameters.end(); iter++) { @@ -139,9 +140,10 @@ command_result mode (color_ostream &out_, vector & parameters) string selected; input_again: CommandHistory hist; - out.lineedit("Enter new mode: ",selected, hist); - if(selected == "c") - return CR_OK; + while((rv = out.lineedit("Enter new mode: ",selected, hist)) + == Console::RETRY); + if(rv <= Console::FAILURE || selected == "c") + return rv == Console::FAILURE ? CR_FAILURE : CR_OK; const char * start = selected.c_str(); char * end = 0; select = strtol(start, &end, 10); @@ -178,14 +180,16 @@ command_result mode (color_ostream &out_, vector & parameters) { CommandHistory hist; string selected; - out.lineedit("Enter new game mode number (c for exit): ",selected, hist); - if(selected == "c") - return CR_OK; + while ((rv = out.lineedit("Enter new game mode number (c for exit): ",selected, hist)) + == Console::RETRY); + if(rv <= Console::FAILURE || selected == "c") + return rv == Console::FAILURE ? CR_FAILURE : CR_OK; const char * start = selected.c_str(); gm.g_mode = (GameMode) strtol(start, 0, 10); - out.lineedit("Enter new game type number (c for exit): ",selected, hist); - if(selected == "c") - return CR_OK; + while((rv = out.lineedit("Enter new game type number (c for exit): ",selected, hist)) + == Console::RETRY); + if(rv <= Console::FAILURE || selected == "c") + return rv == Console::FAILURE ? CR_FAILURE : CR_OK; start = selected.c_str(); gm.g_type = (GameType) strtol(start, 0, 10); } diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index 160fa3eb5..d9bba78b6 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -95,7 +95,7 @@ static vector get_units_at(const df::coord pos, bool only_one) df::unit_flags1 bad_flags; bad_flags.whole = 0; - bad_flags.bits.dead = true; + bad_flags.bits.inactive = true; bad_flags.bits.hidden_ambusher = true; bad_flags.bits.hidden_in_ambush = true; diff --git a/plugins/orders.cpp b/plugins/orders.cpp index 4a2e3b0c0..f600b0cd0 100644 --- a/plugins/orders.cpp +++ b/plugins/orders.cpp @@ -7,7 +7,7 @@ #include "modules/Filesystem.h" #include "modules/Materials.h" -#include "jsoncpp.h" +#include "json/json.h" #include "df/building.h" #include "df/historical_figure.h" diff --git a/plugins/petcapRemover.cpp b/plugins/petcapRemover.cpp index d35906a05..7504ab20e 100644 --- a/plugins/petcapRemover.cpp +++ b/plugins/petcapRemover.cpp @@ -64,7 +64,7 @@ void impregnateMany() { auto units = world->units.all; for ( size_t a = 0; a < units.size(); a++ ) { df::unit* unit = units[a]; - if ( unit->flags1.bits.dead || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) + if ( !Units::isActive(unit) || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) continue; popcount[unit->race]++; if ( unit->pregnancy_genes ) { diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 5f4a59ceb..3f39ec994 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -191,7 +191,7 @@ command_result df_probe (color_ostream &out, vector & parameters) } auto &block = *b->getRaw(); - out.print("block addr: 0x%x\n\n", &block); + out.print("block addr: 0x%p\n\n", &block); /* if (showBlock) { @@ -333,7 +333,7 @@ command_result df_probe (color_ostream &out, vector & parameters) out.print("%-16s", ""); out.print(" %4d", block.local_feature); out.print(" (%2d)", local.type); - out.print(" addr 0x%X ", local.origin); + out.print(" addr 0x%p ", local.origin); out.print(" %s\n", sa_feature(local.type)); } PRINT_FLAG( des, feature_global ); @@ -461,7 +461,7 @@ command_result df_bprobe (color_ostream &out, vector & parameters) case building_type::NestBox: { df::building_nest_boxst* nestbox = (df::building_nest_boxst*) building.origin; - out.print(", claimed:(%i), items:%i", nestbox->claimed_by, nestbox->contained_items.size()); + out.print(", claimed:(%i), items:%zu", nestbox->claimed_by, nestbox->contained_items.size()); break; } default: diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 90c38a06a..dda928afa 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -798,7 +798,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); creatureLightDef *def=getCreatureDef(u); - if(def && !u->flags1.bits.dead) + if(def && Units::isActive(u)) { addLight(getIndex(pos.x,pos.y),def->light.makeSource()); } @@ -1207,31 +1207,31 @@ void lightingEngineViewscreen::loadSettings() lua_pushlightuserdata(s, this); lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); - out.print("%d materials loaded\n",matDefs.size()); + out.print("%zu materials loaded\n",matDefs.size()); lua_pushcfunction(s, parseSpecial); lua_pushlightuserdata(s, this); lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); - out.print("%d day light colors loaded\n",dayColors.size()); + out.print("%zu day light colors loaded\n",dayColors.size()); lua_pushcfunction(s, parseBuildings); lua_pushlightuserdata(s, this); lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); - out.print("%d buildings loaded\n",buildingDefs.size()); + out.print("%zu buildings loaded\n",buildingDefs.size()); lua_pushcfunction(s, parseCreatures); lua_pushlightuserdata(s, this); lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); - out.print("%d creatures loaded\n",creatureDefs.size()); + out.print("%zu creatures loaded\n",creatureDefs.size()); lua_pushcfunction(s, parseItems); lua_pushlightuserdata(s, this); lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); - out.print("%d items loaded\n",itemDefs.size()); + out.print("%zu items loaded\n",itemDefs.size()); } } diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 3f5ea4ffe..ad5f4b239 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -677,6 +677,21 @@ sub get_compound_align { return $al; } +sub get_container_count { + my ($field) = @_; + my $count = $field->getAttribute('count'); + if ($count) { + return $count; + } + my $enum = $field->getAttribute('index-enum'); + if ($enum) { + my $tag = $global_types{$enum}; + return $tag->getAttribute('last-value') + 1; + } + + return 0; +} + sub sizeof { my ($field) = @_; my $meta = $field->getAttribute('ld:meta'); @@ -692,7 +707,7 @@ sub sizeof { return $SIZEOF_PTR; } elsif ($meta eq 'static-array') { - my $count = $field->getAttribute('count'); + my $count = get_container_count($field); my $tg = $field->findnodes('child::ld:item')->[0]; return $count * sizeof($tg); @@ -1038,7 +1053,7 @@ sub render_item_pointer { sub render_item_staticarray { my ($item) = @_; - my $count = $item->getAttribute('count'); + my $count = get_container_count($item); my $tg = $item->findnodes('child::ld:item')->[0]; my $tglen = sizeof($tg) if $tg; my $indexenum = $item->getAttribute('index-enum'); diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index c635efc2e..ca5d82393 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -487,7 +487,7 @@ module DFHack def length if @_length == -1 maxlen = 4096 - (@_memaddr & 0xfff) - maxlen += 4096 until len = DFHack.memory_read(@_memaddr, maxlen).index(?\0) + maxlen += 4096 until len = DFHack.memory_read(@_memaddr, maxlen).index("\0") len else @_length diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 7133dbed8..240a67cb2 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -814,7 +814,7 @@ static VALUE rb_dfmemory_pageprotect(VALUE self, VALUE ptr, VALUE len, VALUE pro ++prot_p; } - Core::printerr("pageprot %x %x %x\n", rb_num2ulong(ptr), rb_num2ulong(len), prot); + Core::printerr("pageprot %zx %zx %x\n", rb_num2ulong(ptr), rb_num2ulong(len), prot); ret = Core::getInstance().p->memProtect((void*)rb_num2ulong(ptr), rb_num2ulong(len), prot); return ret ? Qfalse : Qtrue; diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index c696c23e5..663f85111 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -80,7 +80,7 @@ module DFHack @onupdate_list ||= [] @onupdate_list << OnupdateCallback.new(descr, b, ticklimit, initialtickdelay) DFHack.onupdate_active = true - if onext = @onupdate_list.sort.first + if onext = @onupdate_list.min DFHack.onupdate_minyear = onext.minyear DFHack.onupdate_minyeartick = onext.minyeartick end @@ -123,7 +123,7 @@ module DFHack o.check_run(y, yt, ytmax) } - if onext = @onupdate_list.sort.first + if onext = @onupdate_list.min DFHack.onupdate_minyear = onext.minyear if ytmax > TICKS_PER_YEAR DFHack.onupdate_minyeartick = -1 diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index 3aaaf7653..13c01095d 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -57,8 +57,9 @@ module DFHack def unit_category(u) return if u.flags1.left or u.flags1.incoming # return if hostile & unit_invisible(u) (hidden_in_ambush or caged+mapblock.hidden or caged+holder.ambush - return :Dead if u.flags1.dead + return :Dead if u.flags2.killed return :Dead if u.flags3.ghostly # hostile ? + return if u.flags1.inactive return :Others if !unit_isfortmember(u) casteflags = u.race_tg.caste[u.caste].flags if u.caste >= 0 return :Livestock if casteflags and (casteflags[:PET] or casteflags[:PET_EXOTIC]) @@ -132,7 +133,7 @@ module DFHack # returns if an unit is openly hostile # does not include ghosts / wildlife def unit_ishostile(u) - # return true if u.flags3.ghostly and not u.flags1.dead + # return true if u.flags3.ghostly and not u.flags1.inactive return unless unit_category(u) == :Others case unit_other_category(u) diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index 9e3fa7ff7..8f7bb2462 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -173,7 +173,7 @@ command_result df_showmood (color_ostream &out, vector & parameters) for (size_t i = 0; i < job->job_items.size(); i++) { df::job_item *item = job->job_items[i]; - out.print("Item %i: ", i + 1); + out.print("Item %zu: ", i + 1); MaterialInfo matinfo(item->mat_type, item->mat_index); diff --git a/plugins/siege-engine.cpp b/plugins/siege-engine.cpp index 0d29e8c48..3df9edf29 100644 --- a/plugins/siege-engine.cpp +++ b/plugins/siege-engine.cpp @@ -1343,12 +1343,12 @@ static bool canTargetUnit(df::unit *unit) { CHECK_NULL_POINTER(unit); - if (unit->flags1.bits.dead || + if (!Units::isActive(unit) || unit->flags1.bits.caged || unit->flags1.bits.left || unit->flags1.bits.incoming || unit->flags1.bits.hidden_in_ambush || - unit->flags3.bits.ghostly) + Units::isGhost(unit)) return false; return true; diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 264da21db..ab2829655 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -147,7 +147,7 @@ bool read_order(color_ostream &out, lua_State *L, std::vector *order, if (lua_rawlen(L, -1) != size) { - out.printerr("Invalid ordering size: expected %d, actual %d\n", size, lua_rawlen(L, -1)); + out.printerr("Invalid ordering size: expected %zu, actual %zu\n", size, lua_rawlen(L, -1)); return false; } diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 80e438701..ef8c557c3 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1964,6 +1964,8 @@ void StockpileSerializer::read_weapons() bool usable = weapons.usable(); debug() << "unusable " << unusable << endl; debug() << "usable " << usable << endl; + mPile->settings.weapons.unusable = unusable; + mPile->settings.weapons.usable = usable; // weapon type unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& @@ -2127,6 +2129,8 @@ void StockpileSerializer::read_armor() bool usable = armor.usable(); debug() << "unusable " << unusable << endl; debug() << "usable " << usable << endl; + mPile->settings.armor.unusable = unusable; + mPile->settings.armor.usable = usable; // body type unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index a533f4669..b5831d159 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -677,7 +677,7 @@ public: } else if (input->count(interface_key::HELP)) { - Screen::show(new search_help, plugin_self); + Screen::show(dts::make_unique(), plugin_self); } bool key_processed = false; @@ -1342,7 +1342,7 @@ struct stocks_hook : public df::viewscreen_storesst if (input->count(interface_key::CUSTOM_E)) { Screen::dismiss(this); - Screen::show(new ViewscreenStocks(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); return; } INTERPOSE_NEXT(feed)(input); @@ -1377,7 +1377,7 @@ struct stocks_stockpile_hook : public df::viewscreen_dwarfmodest if (input->count(interface_key::CUSTOM_I)) { - Screen::show(new ViewscreenStocks(sp), plugin_self); + Screen::show(dts::make_unique(sp), plugin_self); return true; } @@ -1451,7 +1451,7 @@ static command_result stocks_cmd(color_ostream &out, vector & parameter } else if (toLower(parameters[0])[0] == 's') { - Screen::show(new ViewscreenStocks(), plugin_self); + Screen::show(dts::make_unique(), plugin_self); return CR_OK; } } diff --git a/plugins/stonesense b/plugins/stonesense index 164d2cd13..4a1953f27 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 164d2cd1349c090a91c1c19200ad03df9bb16650 +Subproject commit 4a1953f27c6acd1ceeb2d5447613c106e708c26c diff --git a/plugins/tiletypes.cpp b/plugins/tiletypes.cpp index 36bde1ac7..31ff096f1 100644 --- a/plugins/tiletypes.cpp +++ b/plugins/tiletypes.cpp @@ -876,9 +876,9 @@ command_result executePaintJob(color_ostream &out) } if (failures > 0) - out.printerr("Could not update %d tiles of %d.\n", failures, all_tiles.size()); + out.printerr("Could not update %d tiles of %zu.\n", failures, all_tiles.size()); else - out.print("Processed %d tiles.\n", all_tiles.size()); + out.print("Processed %zu tiles.\n", all_tiles.size()); if (map.WriteAll()) { @@ -984,9 +984,12 @@ command_result df_tiletypes (color_ostream &out_, vector & parameters) printState(out); std::string input = ""; + int rv = 0; - if (out.lineedit("tiletypes> ",input,tiletypes_hist) == -1) - return CR_FAILURE; + while ((rv = out.lineedit("tiletypes> ",input,tiletypes_hist)) + == Console::RETRY); + if (rv <= Console::FAILURE) + return rv == Console::FAILURE ? CR_FAILURE : CR_OK; tiletypes_hist.add(input); commands.clear(); diff --git a/plugins/tubefill.cpp b/plugins/tubefill.cpp index 1b77d466c..1803ed520 100644 --- a/plugins/tubefill.cpp +++ b/plugins/tubefill.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "Core.h" #include "Console.h" @@ -117,6 +118,6 @@ command_result tubefill(color_ostream &out, std::vector & params) } } } - out.print("Found and changed %d tiles.\n", count); + out.print("Found and changed %" PRId64 " tiles.\n", count); return CR_OK; } diff --git a/plugins/tweak/tweak.cpp b/plugins/tweak/tweak.cpp index 35f0f4705..49d96cc74 100644 --- a/plugins/tweak/tweak.cpp +++ b/plugins/tweak/tweak.cpp @@ -831,7 +831,7 @@ static command_result tweak(color_ostream &out, vector ¶meters) { // remove ghostly, set to dead instead unit->flags3.bits.ghostly = 0; - unit->flags1.bits.dead = 1; + unit->flags1.bits.inactive = 1; } else { diff --git a/plugins/uicommon.h b/plugins/uicommon.h index 17fcd18bd..d8624786c 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -348,10 +348,22 @@ public: bool inStockpile(df::item *i) { - df::item *container = Items::getContainer(i); + // Check if the item is assigned to a stockpile + if (i->isAssignedToStockpile()) + return i->isAssignedToThisStockpile(sp->id); + + // If this is in a container check where the container item is + df::item* container = Items::getContainer(i); if (container) return inStockpile(container); + // Inventory items seems to have stale position information. We can + // assume that they are not in the stockpile. + if (i->flags.bits.in_inventory) + return false; + + // Only containers are assigned to a stockpile. We need a coordinate + // check for free items. if (i->pos.z != z) return false; if (i->pos.x < x1 || i->pos.x >= x2 || i->pos.y < y1 || i->pos.y >= y2) return false; diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 8396a974e..18490039e 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -525,7 +525,7 @@ static void stop_protect(color_ostream &out) pending_recover.clear(); if (!known_jobs.empty()) - out.print("Unprotecting %d jobs.\n", known_jobs.size()); + out.print("Unprotecting %zd jobs.\n", known_jobs.size()); for (TKnownJobs::iterator it = known_jobs.begin(); it != known_jobs.end(); ++it) delete it->second; @@ -557,7 +557,7 @@ static void start_protect(color_ostream &out) check_lost_jobs(out, 0); if (!known_jobs.empty()) - out.print("Protecting %d jobs.\n", known_jobs.size()); + out.print("Protecting %zd jobs.\n", known_jobs.size()); } static void init_state(color_ostream &out) diff --git a/scripts b/scripts index b065ce444..6849148e8 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit b065ce4442da88a749f1b90f35d5102ed13b9afb +Subproject commit 6849148e8694f5da415cd6920dd9094c352110de