diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c705a53 --- /dev/null +++ b/.clang-format @@ -0,0 +1,29 @@ +--- +Language: Cpp +BasedOnStyle: Chromium +AccessModifierOffset: '-4' +IndentWidth: '4' +ColumnLimit: 256 +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakConstructorInitializers: BeforeComma +ConstructorInitializerAllOnOneLineOrOnePerLine: false +IndentPPDirectives: None +FixNamespaceComments: true +NamespaceIndentation: Inner +InsertNewlineAtEOF: true +SeparateDefinitionBlocks: Always +... diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..70d301d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,152 @@ +name: Build and Test + +on: + push: + branches: ['**'] + tags: ['v[0-9]*.[0-9]*.[0-9]*'] + +jobs: + build: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: Windows x86 + os: windows-latest + artifact_platform: windows-x86 + cmake_args: -A Win32 + archive_ext: zip + + - name: Windows x64 + os: windows-latest + artifact_platform: windows-x64 + cmake_args: -A x64 + archive_ext: zip + + - name: macOS Universal + os: macos-latest + artifact_platform: macos-universal + cmake_args: -DCMAKE_OSX_ARCHITECTURES=arm64\;x86_64 + archive_ext: tar.gz + + - name: Linux x86_64 + os: ubuntu-latest + artifact_platform: linux-x86_64 + cmake_args: '' + archive_ext: tar.gz + + - name: Linux x86 + os: ubuntu-latest + artifact_platform: linux-x86 + cmake_args: >- + -DCMAKE_C_FLAGS=-m32 + -DCMAKE_CXX_FLAGS=-m32 + -DCMAKE_EXE_LINKER_FLAGS=-m32 + extra_packages: gcc-multilib g++-multilib + archive_ext: tar.gz + + - name: Linux arm64 + os: ubuntu-22.04-arm + artifact_platform: linux-arm64 + cmake_args: '' + archive_ext: tar.gz + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install extra packages + if: matrix.extra_packages != '' + run: sudo apt-get update && sudo apt-get install -y ${{ matrix.extra_packages }} + + - name: Configure + run: cmake -B build -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} + + - name: Build + run: cmake --build build --config Release + + - name: Test + run: ctest --test-dir build -C Release --output-on-failure + + # ── Determine archive name (nightly vs. tagged release) ────────────── + - name: Set archive name + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') + shell: bash + run: | + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + echo "ARCHIVE_NAME=vorlage-${GITHUB_REF_NAME}-${{ matrix.artifact_platform }}.${{ matrix.archive_ext }}" >> "$GITHUB_ENV" + else + echo "ARCHIVE_NAME=vorlage-nightly-${{ matrix.artifact_platform }}.${{ matrix.archive_ext }}" >> "$GITHUB_ENV" + fi + + # ── Package (Unix) ──────────────────────────────────────────────────── + - name: Package (Unix) + if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && runner.os != 'Windows' + shell: bash + run: | + STAGE=$(mktemp -d) + cp build/Vorlage/vorlage "$STAGE/" + cp build/VPP/vpp "$STAGE/" + cp README.md LICENSE "$STAGE/" + tar czf "$ARCHIVE_NAME" -C "$STAGE" . + + # ── Package (Windows) ───────────────────────────────────────────────── + - name: Package (Windows) + if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && runner.os == 'Windows' + shell: pwsh + run: | + $stage = New-Item -ItemType Directory -Path "$env:RUNNER_TEMP\stage" + Copy-Item "build\Vorlage\Release\vorlage.exe" $stage + Copy-Item "build\VPP\Release\vpp.exe" $stage + Copy-Item "README.md", "LICENSE" $stage + Compress-Archive -Path "$stage\*" -DestinationPath "$env:ARCHIVE_NAME" + + # ── Upload artifact ─────────────────────────────────────────────────── + - name: Upload artifact + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact_platform }} + path: ${{ env.ARCHIVE_NAME }} + retention-days: 14 + + # ── Create GitHub Release (tags only) ────────────────────────────────────── + release: + name: Create Release + needs: build + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Create source tarball + shell: bash + run: | + VERSION="${GITHUB_REF_NAME#v}" + git archive \ + --format=tar.bz2 \ + --prefix="vorlage-${VERSION}/" \ + HEAD \ + > "vorlage-${VERSION}-source.tar.bz2" + echo "SOURCE_TARBALL=vorlage-${VERSION}-source.tar.bz2" >> "$GITHUB_ENV" + + - name: Publish release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + files: | + artifacts/**/* + ${{ env.SOURCE_TARBALL }} diff --git a/.gitignore b/.gitignore index 259148f..cf48de3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,16 @@ *.exe *.out *.app + +/build*/ +.vscode/ +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +*~ +.idea +cmake-build-* +xbuild + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a5876dc --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.16) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version" FORCE) +project(PBEMTools VERSION 1.9.1) + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +set(PBEMTOOLS_MSVC_STATIC_RUNTIME ON) +include(CommonSetup) +#set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +#set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +include(FetchContent) +FetchContent_Declare( + pcre2 + URL https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.43/pcre2-10.43.tar.bz2 + DOWNLOAD_EXTRACT_TIMESTAMP FALSE +) +set(PCRE2_BUILD_PCRE2_8 ON CACHE BOOL "" FORCE) +set(PCRE2_BUILD_PCRE2_16 OFF CACHE BOOL "" FORCE) +set(PCRE2_BUILD_PCRE2_32 OFF CACHE BOOL "" FORCE) +set(PCRE2_SUPPORT_JIT ON CACHE BOOL "" FORCE) +set(PCRE2_BUILD_PCRE2GREP OFF CACHE BOOL "" FORCE) +set(PCRE2_BUILD_TESTS OFF CACHE BOOL "" FORCE) + +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.4.0 +) + +FetchContent_Declare( + fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 10.2.1 +) + +FetchContent_MakeAvailable(pcre2 Catch2 fmt) +list(APPEND CMAKE_MODULE_PATH "${catch2_SOURCE_DIR}/extras") +include(CTest) + +include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}) + +set(PCRE_BUILD_PCRECPP OFF CACHE BOOL "enable X functionality" FORCE) +set(PCRE_BUILD_PCREGREP OFF CACHE BOOL "enable X functionality" FORCE) +set(PCRE_BUILD_TESTS OFF CACHE BOOL "enable X functionality" FORCE) +add_definitions(-DPCRE_STATIC) +add_subdirectory(EBase) +add_subdirectory(Vorlage) +add_subdirectory(VPP) + + + diff --git a/EBase/CMakeLists.txt b/EBase/CMakeLists.txt new file mode 100644 index 0000000..547d4a7 --- /dev/null +++ b/EBase/CMakeLists.txt @@ -0,0 +1,39 @@ +set(EBASE_SOURCES + charencoding.cpp + #expr.cpp + Expression.cpp + Hash.cpp + hierarchy.cpp + regexp.cpp + Report.cpp + ReportBase.cpp + ReportStream.cpp + Utility.cpp + Value.cpp +) + +set(EBASE_HEADERS + charencoding.h + #expr.hpp + Expression.h + Hash.h + hierarchy.h + regexp.h + Report.h + ReportBase.h + ReportStream.h + Utility.h + Value.h + utf8.hpp +) + +add_library(ebase STATIC ${EBASE_SOURCES} ${EBASE_HEADERS}) +target_link_libraries(ebase PUBLIC pcre2-8 fmt::fmt) +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(ebase PRIVATE _CRT_SECURE_NO_WARNINGS) + #target_compile_options(ebase PRIVATE "$<$:/utf-8>") + #target_compile_options(ebase PRIVATE "$<$:/utf-8>") +endif() + +add_subdirectory(test) + diff --git a/EBase/Expression.cpp b/EBase/Expression.cpp new file mode 100644 index 0000000..538e634 --- /dev/null +++ b/EBase/Expression.cpp @@ -0,0 +1,1379 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/Vorlage/Expression.cpp,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:56:46 $ + * $Revision: 1.10 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Klassen fuer die Ausdrucksauswertung in Metabefehlen + ***************************************************************************** + * + * $Log: Expression.cpp,v $ + * Revision 1.10 2000/02/24 09:56:46 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.9 1999/11/28 17:38:41 S.Schuemann + * - Mannigfaltige Änderungen für Vorlage V1.4 beta 9 + * + * Revision 1.8 1999/11/08 11:14:45 S.Schuemann + * - Attribute region.gewinn, region[x,y], unit.bewache, + * region.pool.ding + * - Ecaping in Strings + * - Vars in Attributzugriffen + * - Funktionen + * - Sortierung nach BESCHREIBE PRIVAT + * - Liste fremder Einheiten (normal/verbose) + * - Bugfix: Leerzeile nach Kapitänsinfo entfernt + * + * Revision 1.7 1999/11/03 10:21:56 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.6 1999/10/28 12:39:58 S.Schuemann + * - Änderungen für den Linux-Port + * + * Revision 1.5 1999/10/26 13:27:01 S.Schuemann + * - Anpassungen fuer Vorlage 1.4 b 5 + * + * - Neue Objekt-Attribute + * + * - User-Variable und #while + * + * Revision 1.4 1999/10/24 08:02:18 S.Schuemann + * - Anpassungen fuer 1.4 b 4 + * - CReference als Value eingefuehrt + * - Unterprogramme mit #proc und #call implementiert + * + * Revision 1.3 1999/10/18 21:32:19 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.2 1999/09/27 10:26:07 S.Schuemann + * - Neues Objekt REPORT eingetragen + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#include +#include + +#ifdef VAX +#include +#include +#endif + +#include "../EBase/Utility.h" +#include "Expression.h" +#include "Report.h" +#include "ReportStream.h" + +// #define USEOBJMAP +#define USEFUNCMAP + +#define ERR(n) \ + { \ + g_nERROR = n; \ + g_nERPOS = 0; \ + g_sERTOK = _token; \ + _recurse = 0; \ + throw ExpressionException(n); \ + } +#define ERRX(t) \ + { \ + g_nERROR = -1; \ + g_nERPOS = 0; \ + g_sERTOK = _token; \ + _recurse = 0; \ + throw ExpressionException(std::string(t)); \ + } +#define ERR2(n, t) \ + { \ + g_nERROR = n; \ + g_nERPOS = 0; \ + g_sERTOK = _token; \ + _recurse = 0; \ + throw ExpressionException(n, t); \ + } + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_E +#define M_E 2.71828182845904523536 +#endif + +///////////////////////////////////////////////////////////////////// +//.block: Globale Variable (noch) +///////////////////////////////////////////////////////////////////// + +int g_nERROR; // Fehler-Code +std::string g_sERTOK; // Fehler-Token +int g_nERPOS; // Fehler-Position +const char* g_pcERANC; // Hilfspointer zur g_nERPOS-Berechnung + +///////////////////////////////////////////////////////////////////// +//.block: Mathematische Funktionen die in Ausdruecken erlaubt sind +///////////////////////////////////////////////////////////////////// + +std::map g_cpoFunctionMap; +std::map g_cpoObjectMap; + +Expression::FUNCTION Funcs[] = { + // Name, Anzahl der Argumente, Funktionspointer + /* + { "sin", 1, sin }, + { "cos", 1, cos }, + { "tan", 1, tan }, + { "asin", 1, asin }, + { "acos", 1, acos }, + { "atan", 1, atan }, + { "sinh", 1, sinh }, + { "cosh", 1, cosh }, + { "tanh", 1, tanh }, + { "exp", 1, exp }, + { "log", 1, log }, + { "log10", 1, log10 }, + { "sqrt", 1, sqrt }, + { "floor", 1, floor }, + { "ceil", 1, ceil }, + { "abs", 1, fabs }, + { "hypot", 2, hypot }, + */ + {"LocalUnit", 1, false, FGetUnitOfRegion}, + {"and", 2, false, FAnd}, + {"or", 2, false, FOr}, + {"xor", 2, false, FXor}, + {"not", 1, false, FNot}, + {"abs", 1, false, FAbs}, + {"after", 2, false, FAfter}, + {"antoi", 2, false, FAntoi}, + {"before", 2, false, FBefore}, + {"ceil", 1, false, FCeil}, + {"change", 3, false, FChange}, + {"crop", 2, false, FCrop}, + {"equals", 2, false, FEquals}, + {"flatten", 1, false, FFlatten}, + {"float", 1, false, FFloat}, + {"floor", 1, false, FFloor}, + {"int", 1, false, FInt}, + {"isnothing", 1, false, FIsNothing}, + {"itoan", 2, false, FItoan}, + {"length", 1, false, FLength}, + {"match", 2, false, FMatch}, + {"random", 0, false, FRandom}, + {"sign", 1, false, FSign}, + {"sqrt", 1, false, FSqrt}, + {"substr", 3, false, FSubStr}, + {"time", 0, false, FTime}, + {"tolower", 1, false, FToLower}, + {"toupper", 1, false, FToUpper}, + {"typeof", 1, false, FTypeOf}, + {"xname", 2, false, FXName}, + {"_gv_", 4, false, Fgv}, + {"_cv_", 4, false, Fcv}, + {"open", 2, true, FOpen}, + {"close", 1, true, FClose}, + // { "seek", 2, FSeek }, + // { "read", 2, FRead }, + // { "write", 2, FWrite }, + {"readline", 1, true, FReadLine}, + {"writeline", 2, true, FWriteLine}, + {"read", 1, true, FReadValue}, + {"write", 2, true, FWriteValue}, + {"status", 1, true, FStatus}, + {"statustext", 1, true, FStatusText}, + {"system", 1, true, FSystem}, + {"exp", 1, false, FExp}, + {"log", 1, false, FLog}, + {"log10", 1, false, FLog10}, + {nullptr, 0, false, nullptr}}; + +Expression::OBJECTS Objects[] = { + // Objectname, Indizierbar?, Funktionspointer + {"region", DoRegion}, {"grenze", DoGrenze}, {"unit", DoUnit}, {"einheit", DoUnit}, {"partei", DoPartei}, {"ship", DoShip}, {"schiff", DoShip}, + {"building", DoBuilding}, {"burg", DoBuilding}, {"report", DoReport}, {"things", DoThings}, {"races", DoRaces}, {"db", DoDB}, {nullptr, nullptr}}; + +Expression::Variables Expression::_vars; +Expression::Variables Expression::_consts; + +const char* g_apcErrorText[] = {"Ok.", "Syntaktischer Fehler.", "Klammerung Fehlerhaft.", "Division durch Null.", "Zugriff auf unbekannte Variable.", + // "Zu viele Variable definiert.", + "Unbekannte Funktion.", "Falsche Anzahl von Parametern.", "Argument fehlt.", "Leerer Ausdruck.", "Value Fehler.", "Ueberlauf der Rekursionstiefe.", "Index oder Key ungueltig.", "Interner Fehler in Datenstruktur.", + "Fehlerhafte Fliesskommazahl.", "Gesperrte Funktion im Restricted-Modus.", 0}; + +// ----------------------------------------------------------------------------- +//.class: Expression +// ----------------------------------------------------------------------------- +Expression::Expression(std::string sExpr, const char* pcFile, int32_t nLine) + : _pos(0) + , _force(false) + , _expectInplace(true) + , _recurse(0) + , _type(TokenType::NONE) + , _line(nLine) + , _file(pcFile) +{ + int i; + _exp = sExpr; + _expWork = sExpr; + _isExp = _expWork.begin(); + + if (g_cpoFunctionMap.empty()) { + for (i = 0; Funcs[i].name; i++) { + g_cpoFunctionMap[std::string(Funcs[i].name)] = &(Funcs[i]); + } + for (i = 0; Objects[i].name; i++) { + g_cpoObjectMap[Flatten(Objects[i].name)] = &(Objects[i]); + } + } +} + +char Expression::peekChar(bool bInString, int nOffset) +{ + if (!_expectInplace) { + return _pos + nOffset >= (int)_expWork.length() ? 0 : *(_isExp + nOffset); + } + return expandInplace(nOffset, bInString); +} + +char Expression::popChar(bool bInString) +{ + char c; + if (!_expectInplace) { + c = _pos++ >= (int)_expWork.length() ? 0 : *_isExp++; + } + else { + c = expandInplace(-1, bInString); + _pos++; + _isExp++; + } + return c; +} + +char Expression::expandInplace(int nOffset, bool bInString) +{ + int32_t nPos = _pos; + std::string::const_iterator isExp = _isExp; + if (nOffset > 0) { + nPos += nOffset; + isExp += nOffset; + } + if (!bInString) { + while (nPos + 2 < (int32_t)_expWork.length() && *isExp == '$' && *(isExp + 1) == '(') { + if (IsFlag(VF_VERSION2WARNING)) { + ERRMSG(0, ("%s(%d) : Warnung: Inplace wird ab Version 2.0 nicht mehr unterstuetzt!\n", _file, _line)); + } + std::string sExp(_expWork.substr(static_cast(nPos + 2))); + Expression oExp(sExp, _file, _line); + Value oVal; + int dummy, rc; + rc = oExp.evaluate(*_context, sExp.c_str(), &oVal, &dummy, _force); + if (rc != E_OK) + ERR(rc); + if (oExp._pos + nPos + 1 >= (int32_t)_expWork.length() || _expWork[static_cast(oExp._pos + nPos + 1)] != ')') + ERR(E_SYNTAX); + _expWork.replace((size_t)nPos, (size_t)oExp._pos + 2, oVal.asString()); + _isExp = _expWork.begin() + _pos; + isExp = _isExp + nOffset; + } + } + if (nPos >= (int)_expWork.length()) + return 0; + return *isExp; +} + +void Expression::clearAllVars() +{ + _vars.clear(); + _consts.clear(); +} + +bool Expression::clearVar(const char* name) +{ + // false wenn Var nicht gefunden + return _vars.erase(std::string(name)) > 0; +} + +bool Expression::isContainer(const char* name, Value** pvalue) +{ + Variables::iterator i; + + if (pvalue) { + *pvalue = 0; + } + + i = _context->find(std::string(name)); + if (i != _context->end()) { + if ((*i).second.getType() == VT_MAP || (*i).second.getType() == VT_VECTOR) { + if (pvalue) + *pvalue = &(*i).second; + return true; + } + return false; + } + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + if ((*i).second.getType() == VT_MAP || (*i).second.getType() == VT_VECTOR) { + if (pvalue) + *pvalue = &(*i).second; + return true; + } + return false; + } + return false; +} + +Value* Expression::getValueRef(const char* name) +{ + Variables::iterator i; + + i = _context->find(std::string(name)); + if (i != _context->end()) { + return &(*i).second; + } + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + return &(*i).second; + } + else { + return 0; + } +} + +bool Expression::getValue(const char* name, Value* value) +{ + Variables::iterator i; + + i = _context->find(std::string(name)); + if (i != _context->end()) { + *value = (*i).second; + return true; + } + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + *value = (*i).second; + return true; + } + else { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Undeklarierte Variable '%s' verwendet.", name); + value->error(Buff); + // *value = Value( name ); + return false; + } +} + +bool Expression::setValue(const char* name, const Value* value, bool bForce) +{ + Variables::iterator i; + if (_context) { + i = _context->find(std::string(name)); + if (i != _context->end()) { + (*i).second = *value; + } + else { + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + (*i).second = *value; + } + else { + if (!bForce) + _context->insert(Variables::value_type(std::string(name), *value)); + else { + // char Buff[512]; + // sprintf( Buff, "Zuweisung an undefinierte Variable '%s'.", name ); + // value->error( Buff ); + return false; + } + } + } + return true; + } + return false; +} + +bool Expression::setValue(Variables& oContext, const char* name, const Value* value, bool bForce) +{ + Variables::iterator i; + i = oContext.find(std::string(name)); + if (i != oContext.end()) { + (*i).second = *value; + } + else { + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + (*i).second = *value; + } + else { + if (!bForce) + oContext.insert(Variables::value_type(std::string(name), *value)); + else { + // char Buff[512]; + // sprintf( Buff, "Zuweisung an undefinierte Variable '%s'.", name ); + // value->error( Buff ); + return false; + } + } + } + return true; +} + +bool Expression::setGlobal(const char* name, const Value* value, bool bForce) +{ + Variables::iterator i; + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + (*i).second = *value; + } + else { + if (!bForce) + _vars.insert(Variables::value_type(std::string(name), *value)); + else { + // char Buff[512]; + // sprintf( Buff, "Zuweisung an undefinierte Variable '%s'.", name ); + // value->error( Buff ); + return false; + } + } + return true; +} + +void Expression::setConstant(const char* name, const Value* value) +{ + Variables::iterator i; + i = _consts.find(std::string(name)); + if (i != _consts.end()) { + (*i).second = *value; + } + else { + _consts.insert(Variables::value_type(std::string(name), *value)); + } +} + +Value Expression::getGlobal(const char* name) +{ + Variables::iterator i; + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + return (*i).second; + } + i = _consts.find(std::string(name)); + if (i != _consts.end()) { + return (*i).second; + } + return Value(); +} + +Value* Expression::getGlobalRef(const char* name) +{ + Variables::iterator i; + i = _vars.find(std::string(name)); + if (i != _vars.end()) { + return &(*i).second; + } + i = _consts.find(std::string(name)); + if (i != _consts.end()) { + return &(*i).second; + } + return 0; +} + +Value* Expression::getLocalRef(Expression::Variables& oContext, const char* name) +{ + Variables::iterator i; + i = oContext.find(std::string(name)); + if (i != oContext.end()) { + return &(*i).second; + } + return 0; +} + +void Expression::nextToken(bool bRecursion) +{ + if (++_recurse > MAX_RECURSION_DEPTH) { + ERR(E_RECOVL); + } + + _type = TokenType::NONE; + _token.clear(); + _container = nullptr; + + while (IsSpace(peekChar())) + popChar(); + auto c = peekChar(); + if (!c) { + _type = TokenType::DELIMITER; + } + else if (c == '\x27') { + _type = TokenType::STRING; + popChar(); + while (peekChar(true) != '\x27') { + if (!peekChar(true)) { + ERR(E_SYNTAX); + } + if (peekChar(true) == '\\') { + popChar(true); + if (peekChar(true) && !IsDigit(peekChar(true))) { + _token += popChar(true); + } + else { + c = (peekChar(true) - '0'); + popChar(true); + for (int i = 0; i < 2 && peekChar(true) && IsDigit(peekChar(true)); i++) { + c = (c * 10) + (popChar(true) - '0'); + } + _token += c; + } + } + else { + _token += peekChar(true); + popChar(true); + } + } + if (peekChar(true) != '\x27') + ERR(E_SYNTAX); + popChar(true); + } + else if (IsDelim(c)) { + _type = TokenType::DELIMITER; + _token += popChar(); + if ((_token[0] == '=' || _token[0] == '!' || _token[0] == '<' || _token[0] == '>') && (peekChar() == '=')) { + _token += popChar(); + } + else if ((_token[0] == '&' || _token[0] == '|') && (peekChar() == _token[0])) { + _token += popChar(); + } + } + else if (c == '$') { + if (!bRecursion) + parseObject(); + else { + _type = TokenType::VARNAME; + _token += popChar(); + c = peekChar(); + if (c == '`' || c == (char)0xb4 || c == '&' || c == '+') { + _token += popChar(); + } + else { + while ((IsAlpha(peekChar()) || IsDigit(peekChar()) || IsUmlaut(peekChar()))) { + _token += popChar(); + } + } + } + } + else if (IsDigit(c)) { + _type = TokenType::NUMBER; + while (IsDigit(peekChar()) || peekChar() == '.') { + c = popChar(); + if (c == '.') { + if (_type == TokenType::FLOATNUMBER) { + ERR(E_FLOAT); + } + _type = TokenType::FLOATNUMBER; + } + _token += c; + } + if (isalpha(peekChar())) { + _type = TokenType::UNITNUMBER; + while (IsAlpha(peekChar()) || IsDigit(peekChar())) { + _token += popChar(); + } + } + } + else if (IsAlpha(c) || IsUmlaut(c)) { + if (!bRecursion) + parseObject(); + else { + _type = TokenType::IDENTIFIER; + while (IsAlpha(peekChar()) || IsUmlaut(peekChar())) { + _token += popChar(); + } + if (IsDigit(peekChar())) { + _type = TokenType::UNITNUMBER; + while (IsAlpha(peekChar()) || IsDigit(peekChar())) { + _token += popChar(); + } + } + } + } + else if (c) { + _token += popChar(); + ERR2(E_SYNTAX, "Invalides Zeichen: " + to_hex(c)); + } + while (IsSpace(peekChar())) + popChar(); + + _recurse--; +} + +void Expression::parseObject(bool bGetRef) +{ + CObjectPart* pCP; + Value oVal; + Value oHVal; + Value* poContainer = nullptr; + std::shared_ptr pObj(new CObjectPart()); + bool bVarName = false; + bool bUndefined = false; + std::string sToken; + + _valueRef = nullptr; + pCP = pObj.get(); + nextToken(true); + if (_type == TokenType::VARNAME) { + sToken = _token; + bVarName = true; + if (sToken == "$RETURN" && IsFlag(VF_VERSION2WARNING)) { + ERRMSG(0, ("%s(%d) : Warnung: Statt $RETURN wird ab Version 2.0 nur noch #return unterstuetzt!\n", _file, _line)); + } + if (isContainer(_token.c_str(), &poContainer)) { + pCP->label = _token; + } + else if (!bGetRef) { + if (getValue(_token.c_str(), &oVal)) { + pCP->label = _token; + oHVal = oVal; + } + else { + if (_force) + bUndefined = true; + } + } + else if (bGetRef) { + _valueRef = getValueRef(_token.c_str()); + if (!_valueRef && _force) + bUndefined = true; + } + else { + pCP->label = _token; + oHVal = Value(_token); + } + } + else { + pCP->label = _token; + oHVal = Value(_token); + } + + while (peekChar() == '[' || peekChar() == '.') { + nextToken(true); + if (_token[0] == '[') { + pCP->bracket = _token[0]; + if (peekChar() == pCP->bracket) + ERR(E_NOARG); + do { + nextToken(); + if (_token[0] == ',') + ERR(E_NOARG); + evalExpr(&oVal); + if (oVal.getType() == VT_MAP || oVal.getType() == VT_VECTOR) + ERR(E_BADINDEX); + pCP->index.push_back(oVal); + if (poContainer && pCP->index.size() > 1) + ERR(E_NUMARGS); + } while (_token[0] == ','); + if (_token[0] != ']') + ERR(E_UNBALAN); + // Sind wir in oberster Ebene in einem Container? + if (poContainer && pCP == pObj.get() && pCP->index.size() == 1 && (bGetRef || peekChar() == '[' || peekChar() == '.' || peekChar() == '(')) { + const Value* poHelp; + poHelp = &(poContainer->getAt(oVal)); + if (poHelp->getType() == VT_MAP || poHelp->getType() == VT_VECTOR) { + poContainer = const_cast(poHelp); + pCP->index.clear(); + } + else { + ERR(E_BADINDEX); + } + } + } + else if (_token[0] == '.') { + pCP->next = new CObjectPart(); + pCP = pCP->next; + nextToken(true); + if (_token[0] == '(') { + nextToken(); + if (_token[0] == ')') + ERR(E_NOARG); + evalExpr(&oVal); + pCP->label = oVal.asString(); + if (_token[0] != ')') + ERR(E_UNBALAN); + } + else if (_type == TokenType::VARNAME) { + if (getValue(_token.c_str(), &oVal)) { + pCP->label = oVal.asString(); + } + else { + ERR(E_UNKNOWN); + } + } + else + pCP->label = _token; + } + else { + break; + } + } + + if (!sToken.empty()) { + _token = sToken; + } + + if (poContainer) + _valueRef = poContainer; + _container = poContainer; + _object = pObj; + _objVal = oHVal; + _type = bUndefined ? TokenType::VARNAME : TokenType::OBJECT; + + if (!bVarName) + _valueRef = nullptr; +} + +// Zuweisung +int Expression::parseAssignment(Value* r) +{ + Value* poContainer; + + std::string sT; + + if (peekChar() == '=' && peekChar(false, 1) != '=') { + if (_type != TokenType::OBJECT) { + if (_type != TokenType::VARNAME) + r->error("Zuweisung an einen Wert statt einer Referenz."); + else { + std::string err; + err = std::string("Undeklarierte Variable '") + _token + std::string("' verwendet."); + r->error(err.c_str()); + } + ERR(E_SYNTAX); + } + + poContainer = _container; + std::shared_ptr poObject(_object); + CReference oRef; + Value oVal; + sT = _token; + _assign = true; + parsePrimary(&oVal, &oRef); + _assign = false; + nextToken(); + if (_token.empty() && _type == TokenType::DELIMITER) { + if (poContainer /*_objVal.getType()==VT_MAP || _objVal.getType()==VT_VECTOR*/) { + if (poObject->index.empty()) { + poContainer->clear(); + } + else if (poObject->index.size() == 1) { + poContainer->remove(poObject->index[0]); + } + else { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Falsche Indizierung im Behaelter '%s'.", poObject->label.c_str()); + r->error(Buff); + ERR(E_BADINDEX); + } + } + else { + Value oV; + if (!setValue(sT.c_str(), &oV, _force)) { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Zuweisung an undefinierte Variable '%s'.", sT.c_str()); + r->error(Buff); + ERR(E_UNKNOWN); + } + } + return 1; + } + evalExpr(r); + if (poContainer) { + if (poObject->index.size() == 1) { + if (!poContainer->setAt(poObject->index[0], *r)) { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Falsche Indizierung im Behaelter '%s'.", poObject->label.c_str()); + r->error(Buff); + ERR(E_BADINDEX); + } + } + else { + if (poContainer->getType() == r->getType()) { + *poContainer = *r; + } + else if (poObject->index.size()) { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Falsche Indizierung im Behaelter '%s'.", poObject->label.c_str()); + r->error(Buff); + ERR(E_BADINDEX); + } + else { + r->error("Inkompatible Typen in Zuweisung."); + ERR(E_VALUE); + } + } + } + else if (_context && poObject->label == "ARG") { + if (poObject->index.size() != 1) + ERR(E_BADINDEX); + int idx = poObject->index[0].asLong(); + Value oVal2, oRef0; + + getValue("#ARG0", &oVal2); + getValue("#REF0", &oRef0); + + if (idx < 0 || idx >= oVal2.asLong()) + ERR(E_BADINDEX); + if (idx < oRef0.asLong()) { + if (getValue(std::string(std::string("#REF") + ToString((int32_t)(idx + 1))).c_str(), &oVal2)) { + if (!setValue(oVal2.asString().c_str(), r)) + ERR(E_INTERNAL); + } + else + ERR(E_INTERNAL); + } + else { + if (!setValue(std::string(std::string("#ARG") + ToString((int32_t)(idx + 1))).c_str(), r)) + ERR(E_INTERNAL); + } + } + else if (sT[0] == '$' && (!poObject->index.empty() || poObject->next)) { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Zuweisung ueber Behaelter/Objekt-Zugriff an Variable '%s'.", sT.c_str()); + r->error(Buff); + ERR(E_BADINDEX); + } + else if (sT[0] == '$') { + if (!setValue(sT.c_str(), r, _force)) { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Zuweisung an undefinierte Variable '%s'.", sT.c_str()); + r->error(Buff); + ERR(E_UNKNOWN); + } + } + else if (oRef.isValid()) { + oRef.self() = *r; + } + else { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Fehlerhafte Zuweisung, '%s' ist kein gueltiges Zuweisungsziel.", sT.c_str()); + r->error(Buff); + ERR(E_SYNTAX); + } + return 1; + } + evalExpr(r); + return 0; +} + +// Shunting-Yard: evaluates binary-operator expressions. +// parseUnary is called for each operand (it handles unary operators and calls parsePrimary). +void Expression::evalExpr(Value* r) +{ + std::vector valStack; + std::vector opStack; + + // Apply the top operator in opStack to the top two values in valStack. + auto applyTop = [&]() { + std::string op = opStack.back(); + opStack.pop_back(); + // Copy-construct before pop_back() to avoid dangling-reference UB with unique_ptr members. + Value rhs = valStack.back(); + valStack.pop_back(); + Value lhs = valStack.back(); + valStack.pop_back(); + Value res; + + if (op == "+") + res = lhs + rhs; + else if (op == "-") + res = lhs - rhs; + else if (op == "*") + res = lhs * rhs; + else if (op == "/") { + res = lhs / rhs; + if (res.getType() == VT_ERROR) + throw ExpressionException(res.asString()); + } + else if (op == "%") { + res = lhs % rhs; + if (res.getType() == VT_ERROR) + throw ExpressionException(res.asString()); + } + else if (op == "^") + res = lhs.pow(rhs); + else if (op == "<") + res = Value(lhs < rhs ? 1 : 0); + else if (op == ">") + res = Value(lhs > rhs ? 1 : 0); + else if (op == "<=") + res = Value(lhs <= rhs ? 1 : 0); + else if (op == ">=") + res = Value(lhs >= rhs ? 1 : 0); + else if (op == "==") + res = Value(lhs == rhs ? 1 : 0); + else if (op == "!=") + res = Value(lhs == rhs ? 0 : 1); + else if (op[0] == '&') // & or && + res = Value((lhs && rhs) ? 1 : 0); + else if (op[0] == '|') // | or || + res = Value((lhs || rhs) ? 1 : 0); + else { + ERR(E_SYNTAX); + } + + valStack.push_back(res); + }; + + // Returns true when the current token is a binary operator. + // Guards TokenType::STRING so string-valued tokens are never misread as operators. + auto isBinOp = [&]() -> bool { + if (_type == TokenType::STRING) + return false; + if (_token.empty()) + return false; + if (_token[0] == '&' || _token[0] == '|') + return true; // & && | || + if (_token[0] == '<' || _token[0] == '>') + return true; // < <= > >= + if (_token[0] == '=' && _token[1] == '=') + return true; // == (not single =) + if (_token[0] == '!' && _token[1] == '=') + return true; // != (not unary !) + if (_token[0] == '+' || _token[0] == '-') + return true; + if (_token[0] == '*' || _token[0] == '/' || _token[0] == '%') + return true; + if (_token[0] == '^') + return true; + return false; + }; + + // Parse first primary expression (parseUnary handles unary ops and calls parsePrimary). + Value first; + parseUnary(&first); + valStack.push_back(first); + + // Main Shunting-Yard loop. + while (isBinOp()) { + std::string op = _token; + + // Emit legacy warning for single-char & or | (use && / || instead). + if (IsFlag(VF_VERSION2WARNING) && op.length() == 1 && (op[0] == '&' || op[0] == '|')) { + ERRMSG(0, ("%s(%d) : Warnung: Ab Version 2.0 wird statt & bzw. | nur noch && bzw. || akzeptiert!\n", _file, _line)); + } + + int prec = static_cast(getPrecedence(op)); + bool rightAssoc = (getAssociativity(op) == ASSOC_RIGHT); + + // Pop operators with higher precedence, or equal precedence when left-associative. + while (!opStack.empty()) { + int topPrec = static_cast(getPrecedence(opStack.back())); + if (topPrec > prec || (topPrec == prec && !rightAssoc)) + applyTop(); + else + break; + } + + opStack.push_back(op); + nextToken(); + + // Parse next primary expression. + Value next; + parseUnary(&next); + valStack.push_back(next); + } + + // Drain remaining operators. + while (!opStack.empty()) + applyTop(); + + *r = valStack.back(); +} + +// Vorzeichen und Logisches Nicht +void Expression::parseUnary(Value* r) +{ + char o = 0; + + if (_type != TokenType::STRING && (_token[0] == '+' || _token[0] == '-' || _token[0] == '!') && _token.size() == 1) { + o = _token[0]; + nextToken(); + } + parsePrimary(r); + if (o == '-') { + *r = -*r; + } + else if (o == '!') { + if (r->getType() == VT_STRING) { + *r = "!" + r->asString(); + } + else { + *r = Value(!*r ? 1 : 0); + } + } +} + +// Variable / Funktionen / Objekte / Klammerungen +void Expression::parsePrimary(Value* r, CReference* pRef) +{ + int i; + ArgumentList a; + + if (_type != TokenType::STRING && _token[0] == '(') { + nextToken(); + if (_token[0] == ')') + ERR(E_NOARG); + parseAssignment(r); + if (_token[0] != ')') + ERR(E_UNBALAN); + nextToken(); + } + else if (_type != TokenType::STRING && _token[0] == '[') { + nextToken(); + Value oArray(VT_VECTOR); + Value oVal; + while (_token[0] != ']' || _type != TokenType::DELIMITER) { + parseAssignment(&oVal); + oArray.setAt(Value((int32_t)oArray.size()), oVal); + if (_token[0] == ',' && _type == TokenType::DELIMITER) + nextToken(); + else if (_token[0] != ']' || _type != TokenType::DELIMITER) + ERR(E_SYNTAX); + } + *r = oArray; + nextToken(); + } + else { + if (_type == TokenType::NUMBER || _type == TokenType::FLOATNUMBER) { + double fTemp = atof(_token.c_str()); + if (_type == TokenType::NUMBER) + *r = Value((int32_t)fTemp); + else + *r = Value(fTemp); + nextToken(); + } + else if (_type == TokenType::STRING) { + *r = Value(_token); + nextToken(); + } + else if (_type == TokenType::OBJECT) { + if (_container) { + if (_object->index.size() == 1) { + *r = _container->getAt(_object->index[0]); + if (r->getType() == VT_ERROR) { + ERR(E_VALUE); + } + } + else if (_object->next && IsEqual(_object->next->label, "size") && !_object->next->next) { + *r = _container->size(); + } + else if (peekChar() == '(') { + Value* poContainer = _container; + _container = nullptr; + nextToken(); + Value oIdx; + nextToken(); + evalExpr(&oIdx); + if (_token[0] != ')' || (oIdx.asLong() < 0 && oIdx.asLong() > poContainer->size())) { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Falsche Indizierung im Behaelter '%s'.", _object->label.c_str()); + r->error(Buff); + ERR(E_SYNTAX); + } + *r = poContainer->getNth(oIdx.asLong()); + } + else if (_object->index.empty()) { + *r = *_container; + } + else { + char Buff[512]; + snprintf(Buff, sizeof(Buff), "Falsche Indizierung im Behaelter '%s'.", _object->label.c_str()); + r->error(Buff); + ERR(E_SYNTAX); + } + nextToken(); + return; + } + else if (_object->label == "ARG") { + if (_object->next) { + if (_object->index.empty() && IsEqual(_object->next->label, "SIZE")) { + if (!getValue("#ARG0", r)) + *r = Value(0); + nextToken(); + return; + } + else { + ERR(E_BADINDEX); + } + } + else { + if (_object->index.size() != 1) + ERR(E_BADINDEX); + int idx = _object->index[0].asLong(); + Value oVal, oRef0; + + getValue("#ARG0", &oVal); + getValue("#REF0", &oRef0); + + if (idx < 0 || idx >= oVal.asLong()) + ERR(E_BADINDEX); + if (idx < oRef0.asLong()) { + if (getValue(std::string(std::string("#REF") + ToString((int32_t)(idx + 1))).c_str(), &oVal)) { + if (!getValue(oVal.asString().c_str(), r)) + ERR(E_INTERNAL); + } + else + ERR(E_INTERNAL); + } + else { + if (!getValue(std::string(std::string("#ARG") + ToString((int32_t)(idx + 1))).c_str(), r)) + ERR(E_INTERNAL); + } + nextToken(); + return; + } + } + Value oConst(getGlobal(_object->label.c_str())); + if (oConst.getType() != VT_EMPTY) { + *r = oConst; + nextToken(); + return; + } + + if (!_object->index.empty() || _object->next) { + // Propagate assignment context to the object handler. + _object->assign = _assign; +#ifdef USEOBJMAP + std::map::const_iterator oi = g_cpoObjectMap.find(Flatten(_object->label)); + if (oi != g_cpoObjectMap.end()) { + if (!_object->next && _object->index.empty()) + *r = _objVal; + else { + if (pRef) { + Value oR((*oi).second->func(_object.get())); + if (oR.getType() == VT_REF) + pRef->set(oR); + *r = oR; + } + else { + *r = (*oi).second->func(_object.get()); + } + } + if (r->getType() == VT_ERROR) + throw ExpressionException(r->asString()); + nextToken(); + return; + } +#else + for (i = 0; Objects[i].name; i++) { + if (IsEqual(_object->label, Objects[i].name)) { + if (!_object->next && _object->index.empty()) + *r = _objVal; + else { + if (pRef) { + Value oR(Objects[i].func(_object.get())); + if (oR.getType() == VT_REF) + pRef->set(oR); + *r = oR; + } + else { + *r = Objects[i].func(_object.get()); + } + } + if (r->getType() == VT_ERROR) + throw ExpressionException(r->asString()); + nextToken(); + return; + } + } +#endif + } + + if (!_object->label.empty() && _object->label[0] == '$' && (!_object->index.empty() || _object->next)) { + ERR(E_BADINDEX); + } + + if (peekChar() == '(') { + std::string sFName = _token; + nextToken(); + a.clear(); + do { + nextToken(); + if (a.size() && _type == TokenType::DELIMITER && (_token[0] == ')' || _token[0] == ',')) + ERR(E_NOARG); + if (_type != TokenType::DELIMITER || _token[0] != ')') { + a.push_back(Value()); + parseAssignment(&a[a.size() - 1]); + if (a[a.size() - 1].getType() == VT_ERROR) + throw ExpressionException(a[a.size() - 1].asString()); + } + } while (_token[0] == ','); + nextToken(); + + if (!DoUserFunction(sFName, a, r)) { +#ifdef USEFUNCMAP + std::map::const_iterator fi = g_cpoFunctionMap.find(sFName); + if (fi != g_cpoFunctionMap.end()) { + if (a.size() != (*fi).second->args) { + ERR(E_NUMARGS); + } + if ((*fi).second->risc && IsFlag(VF_RESTRICTED)) { + ERR(E_RESTRICTED); + } + *r = (*fi).second->func(this, a); + if (r->getType() == VT_ERROR) + throw ExpressionException(r->asString()); + } + else { + ERRX(std::string("Unbekannte Funktion: ") + sFName); + } +#else + for (i = 0; Funcs[i].name; i++) { + if (IsEqual(sFName.c_str(), Funcs[i].name)) { + if (a.size() != Funcs[i].args) { + ERR(E_NUMARGS); + } + if (Funcs[i].risc && IsFlag(VF_RESTRICTED)) { + ERR(E_RESTRICTED); + } + *r = Funcs[i].func(this, a); + if (r->getType() == VT_ERROR) + throw ExpressionException(r->asString()); + return; + } + } + if (!Funcs[i].name) { + ERRX(std::string("Unbekannte Funktion: ") + sFName); + } +#endif + } + } + else { + if (!_object->next && _object->index.empty()) + *r = _objVal; + else { + *r = CBlockBase::GetCfgObjValue(_object.get()); + if (r->getType() == VT_ERROR) + throw ExpressionException(r->asString()); + } + nextToken(); + } + } + else if (_type == TokenType::VARNAME) { + if (!getValue(_token.c_str(), r)) + ERR(E_UNKNOWN); + nextToken(); + } + else if (_type == TokenType::UNITNUMBER || _type == TokenType::IDENTIFIER) { + *r = Value(_token); + nextToken(); + } + else + ERR(E_SYNTAX); + } +} + +int Expression::evaluate(Variables& oContext, const char* e, Value* result, int* a, bool bForce, bool bExpectInplace) +{ + _context = &oContext; + _force = bForce; + _expectInplace = bExpectInplace; + + try { + _exp = e; + _expWork = e; + _pos = 0; + _isExp = _expWork.begin(); + g_pcERANC = e; + *result = Value(); + nextToken(); + if (_token.empty() && _type != TokenType::STRING) + ERR(E_EMPTY); + _assign = false; + *a = parseAssignment(result); + _assign = false; + if (result->getType() == VT_ERROR) { + result->error(result->asString().c_str()); + return E_VALUE; + } + if (_type != TokenType::NONE && _type != TokenType::DELIMITER) + ERR(E_SYNTAX); + } + catch (ExpressionException E) { + _assign = false; + if (result->getType() != VT_ERROR) { + if (E.num < 0) + result->error(E.text.c_str()); + else + result->error((g_apcErrorText[E.num] + std::string(" ") + E.text).c_str()); + } + return (E.num); + /* + const char* str = e; + while( *str ) + { + if( !IsAlNum(*str) ) break; + str++; + } + if( *str ) + { + result->error( g_apcErrorText[E.num] ); + return( E.num ); + } + *result = Value( e ); + */ + } + return E_OK; +} + +std::shared_ptr Expression::parseObjectAccess(const std::string& sObjAcc /*, int nRecurse, Value** poContainer*/, Variables* poContext) +{ + Variables oContext; + Value oVal; + Expression oExp(sObjAcc, "", 0); + oExp._recurse = 0; // nRecurse; + oExp._context = poContext ? poContext : &oContext; + oExp.nextToken(); + /* + if (poContainer) + *poContainer = oExp._container; + */ + return oExp._object; +} + +Value* Expression::getObjectReference(Expression::Variables& oContext, const std::string& sObj) +{ + Expression oExp(sObj, "", 0); + oExp._recurse = 0; + oExp._context = &oContext; + oExp._valueRef = 0; + oExp.parseObject(true); + return oExp._valueRef; +} + +Expression::Precedence Expression::getPrecedence(const std::string& token) +{ + if (token == "&" || token == "|" || token == "&&" || token == "||") + return PREC_LOGICAL; + if (token == "<" || token == ">" || token == "<=" || token == ">=" || token == "==" || token == "!=") + return PREC_COMPARISON; + if (token == "+" || token == "-") + return PREC_ADDITIVE; + if (token == "*" || token == "/" || token == "%") + return PREC_MULTIPLICATIVE; + if (token == "^") + return PREC_EXPONENT; + if (token == "!" || token == "UNARY_MINUS") + return PREC_UNARY; + return PREC_NONE; +} + +Expression::Associativity Expression::getAssociativity(const std::string& token) +{ + if (token == "^") + return ASSOC_RIGHT; + if (token == "!" || token == "UNARY_MINUS") + return ASSOC_RIGHT; + return ASSOC_LEFT; +} diff --git a/EBase/Expression.h b/EBase/Expression.h new file mode 100644 index 0000000..16f4606 --- /dev/null +++ b/EBase/Expression.h @@ -0,0 +1,252 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/Vorlage/Expression.h,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:56:47 $ + * $Revision: 1.9 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Klassen fuer die Ausdrucksauswertung in Metabefehlen + ***************************************************************************** + * + * $Log: Expression.h,v $ + * Revision 1.9 2000/02/24 09:56:47 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.8 1999/11/28 17:38:41 S.Schuemann + * - Mannigfaltige Änderungen für Vorlage V1.4 beta 9 + * + * Revision 1.7 1999/11/03 10:21:56 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.6 1999/10/28 12:39:58 S.Schuemann + * - Änderungen für den Linux-Port + * + * Revision 1.5 1999/10/26 13:27:01 S.Schuemann + * - Anpassungen fuer Vorlage 1.4 b 5 + * + * - Neue Objekt-Attribute + * + * - User-Variable und #while + * + * Revision 1.4 1999/10/24 08:02:18 S.Schuemann + * - Anpassungen fuer 1.4 b 4 + * - CReference als Value eingefuehrt + * - Unterprogramme mit #proc und #call implementiert + * + * Revision 1.3 1999/10/18 21:32:19 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.2 1999/09/27 10:26:07 S.Schuemann + * - Neues Objekt REPORT eingetragen + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +// Token types produced by the lexer (nextToken). +enum class TokenType { NONE = 0, VARNAME, OBJECT, IDENTIFIER, DELIMITER, NUMBER, FLOATNUMBER, UNITNUMBER, STRING }; + +class ExpressionException +{ +public: + ExpressionException(int n, const std::string& txt = "") + : num(n) + , text(txt) + { + } + + ExpressionException(const std::string& sTxt) + : num(-1) + , text(sTxt) + { + } + + ~ExpressionException() {} + + int num; + std::string text; +}; + +using ArgumentList = std::vector; + +class Expression +{ +public: + enum RC { E_OK, E_SYNTAX, E_UNBALAN, E_DIVZERO, E_UNKNOWN, E_BADFUNC, E_NUMARGS, E_NOARG, E_EMPTY, E_VALUE, E_RECOVL, E_BADINDEX, E_INTERNAL, E_FLOAT, E_RESTRICTED }; + + typedef std::map Variables; + + typedef struct + { + const char* name; // Functionsname + size_t args; // Anzahl der Parameter + bool risc; // true wenn verboten bei --restricted + Value (*func)(Expression* poContext, ArgumentList& coArgs); // Zeiger auf die Funktion + } FUNCTION; + + typedef struct + { + const char* name; // Functionsname + Value (*func)(CObjectPart* poPart); // Zeiger auf die Funktion + } OBJECTS; + + Expression(std::string sExpr, const char* pcFile, int32_t nLine); + + int evaluate(Variables& oContext, const char* e, Value* result, int* a, bool bForce, bool bExpectInplace = true); + + Value* getContainer() const { return _container; } + + bool isContainer(const char* name, Value** pvalue); + bool getValue(const char* name, Value* value); + Value* getValueRef(const char* name); + bool setValue(const char* name, const Value* value, bool bForce = false); + static bool setValue(Variables& oContext, const char* name, const Value* value, bool bForce = false); + static bool setGlobal(const char* name, const Value* value, bool bForce = false); + static void setConstant(const char* name, const Value* value); + static Value getGlobal(const char* name); + static Value* getGlobalRef(const char* name); + static Value* getLocalRef(Expression::Variables& oContext, const char* name); + + static void clearAllVars(); + static bool clearVar(const char* name); + + static Variables& globalContext() { return _vars; } + + static std::shared_ptr parseObjectAccess(const std::string& sObjAcc, Variables* poContext = nullptr); + static Value* getObjectReference(Expression::Variables& oContext, const std::string& sObj); + + enum Precedence { + PREC_NONE = 0, + PREC_LOGICAL = 10, // Level 1a: &, |, &&, || + PREC_COMPARISON = 20, // Level 1b: <, >, <=, >=, ==, != + PREC_ADDITIVE = 30, // Level 2: +, - + PREC_MULTIPLICATIVE = 40, // Level 3: *, /, % + PREC_EXPONENT = 50, // Level 4: ^ + PREC_UNARY = 60 // Level 5: unary -, ! + }; + + enum Associativity { ASSOC_NONE, ASSOC_LEFT, ASSOC_RIGHT }; + + static Precedence getPrecedence(const std::string& token); + static Associativity getAssociativity(const std::string& token); + +protected: + char peekChar(bool bInString = false, int nOffset = 0); + char popChar(bool bInString = false); + char expandInplace(int nOffset, bool bInString); + + void nextToken(bool bRecursion = false); // lexer: advance to next token + void parseObject(bool bGetRef = false); // parse a variable/object-path LHS + int parseAssignment(Value* r); // handle assignment (=) + void evalExpr(Value* r); // Shunting-Yard binary-expression evaluator + void parseUnary(Value* r); // handle prefix unary operators (-, +, !) + void parsePrimary(Value* r, CReference* pRef = nullptr); // literals, variables, function calls, (…), […] + +protected: + std::string _exp; + std::string _expWork; + std::string::const_iterator _isExp; + + int _pos; + bool _force; + bool _expectInplace; + int _recurse; + TokenType _type; + std::string _token; // current token text (replaces the old malloc'd char* buffer) + int32_t _line; + const char* _file; + std::shared_ptr _object; + Value _objVal; + Value* _container = nullptr; + Value* _valueRef = nullptr; + Variables* _context = nullptr; + + bool _assign = false; // true while evaluating the RHS of an assignment + + static constexpr int MAX_RECURSION_DEPTH = 64; + static Variables _vars; + static Variables _consts; +}; + +class CAScriptObject +{ +public: + virtual const CAScriptObject& operator[](const Value& oVal) = 0; + virtual const Value& getValue(ArgumentList) = 0; +}; + +extern Value DoPartei(CObjectPart* poPart); +extern Value DoUnit(CObjectPart* poPart); +extern Value DoShip(CObjectPart* poPart); +extern Value DoBuilding(CObjectPart* poPart); +extern Value DoRegion(CObjectPart* poPart); +extern Value DoGrenze(CObjectPart* poPart); +extern Value DoReport(CObjectPart* poPart); +extern Value DoThings(CObjectPart* poPart); +extern Value DoRaces(CObjectPart* poPart); +extern Value DoDB(CObjectPart* poPart); + +extern Value FGetUnitOfRegion(Expression* poContext, ArgumentList& coArgs); +extern Value FRandom(Expression* poContext, ArgumentList& coArgs); +extern Value FEquals(Expression* poContext, ArgumentList& coArgs); +extern Value FMatch(Expression* poContext, ArgumentList& coArgs); +extern Value FBefore(Expression* poContext, ArgumentList& coArgs); +extern Value FAfter(Expression* poContext, ArgumentList& coArgs); +extern Value FCrop(Expression* poContext, ArgumentList& coArgs); +extern Value FChange(Expression* poContext, ArgumentList& coArgs); +extern Value FSubStr(Expression* poContext, ArgumentList& coArgs); +extern Value FCeil(Expression* poContext, ArgumentList& coArgs); +extern Value FFloor(Expression* poContext, ArgumentList& coArgs); +extern Value FAbs(Expression* poContext, ArgumentList& coArgs); +extern Value FSign(Expression* poContext, ArgumentList& coArgs); +extern Value FSqrt(Expression* poContext, ArgumentList& coArgs); +extern Value FExp(Expression* poContext, ArgumentList& coArgs); +extern Value FLog(Expression* poContext, ArgumentList& coArgs); +extern Value FLog10(Expression* poContext, ArgumentList& coArgs); +extern Value FFloat(Expression* poContext, ArgumentList& coArgs); +extern Value FInt(Expression* poContext, ArgumentList& coArgs); +extern Value FIsNothing(Expression* poContext, ArgumentList& coArgs); +extern Value FItoan(Expression* poContext, ArgumentList& coArgs); +extern Value FAntoi(Expression* poContext, ArgumentList& coArgs); +extern Value FXName(Expression* poContext, ArgumentList& coArgs); +extern Value FLength(Expression* poContext, ArgumentList& coArgs); +extern Value FFlatten(Expression* poContext, ArgumentList& coArgs); +extern Value FToLower(Expression* poContext, ArgumentList& coArgs); +extern Value FToUpper(Expression* poContext, ArgumentList& coArgs); +extern Value FTypeOf(Expression* poContext, ArgumentList& coArgs); +extern Value FTime(Expression* poContext, ArgumentList& coArgs); +extern Value FAnd(Expression* poContext, ArgumentList& coArgs); +extern Value FOr(Expression* poContext, ArgumentList& coArgs); +extern Value FXor(Expression* poContext, ArgumentList& coArgs); +extern Value FNot(Expression* poContext, ArgumentList& coArgs); +extern Value Fgv(Expression* poContext, ArgumentList& coArgs); +extern Value Fcv(Expression* poContext, ArgumentList& coArgs); + +extern Value FOpen(Expression* poContext, ArgumentList& coArgs); +extern Value FClose(Expression* poContext, ArgumentList& coArgs); +// extern Value FSeek( Expression* poContext, ArgumentList& coArgs ); +// extern Value FRead( Expression* poContext, ArgumentList& coArgs ); +// extern Value FWrite( Expression* poContext, ArgumentList& coArgs ); +extern Value FReadLine(Expression* poContext, ArgumentList& coArgs); +extern Value FWriteLine(Expression* poContext, ArgumentList& coArgs); +extern Value FReadValue(Expression* poContext, ArgumentList& coArgs); +extern Value FWriteValue(Expression* poContext, ArgumentList& coArgs); +extern Value FStatus(Expression* poContext, ArgumentList& coArgs); +extern Value FStatusText(Expression* poContext, ArgumentList& coArgs); + +extern Value FSystem(Expression* poContext, ArgumentList& coArgs); + +extern bool DoUserFunction(const std::string& sName, ArgumentList& coArgs, Value* poVal); diff --git a/EBase/Hash.cpp b/EBase/Hash.cpp new file mode 100644 index 0000000..fe85887 --- /dev/null +++ b/EBase/Hash.cpp @@ -0,0 +1,166 @@ +// Code by Bob Jenkins, 1997, placed in public domain +#include "Hash.h" + +#define hashsize(n) ((ub4)1 << (n)) +#define hashmask(n) (hashsize(n) - 1) + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bit set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() takes 36 machine instructions, but only 18 cycles on a superscalar + machine (like a Pentium or a Sparc). No faster mixer seems to work, + that's the result of my brute-force search. There were about 2^^68 + hashes to choose from. I only tested about a billion of those. +-------------------------------------------------------------------- +*/ +#define mix(a, b, c) \ + { \ + a -= b; \ + a -= c; \ + a ^= (c >> 13); \ + b -= c; \ + b -= a; \ + b ^= (a << 8); \ + c -= a; \ + c -= b; \ + c ^= (b >> 13); \ + a -= b; \ + a -= c; \ + a ^= (c >> 12); \ + b -= c; \ + b -= a; \ + b ^= (a << 16); \ + c -= a; \ + c -= b; \ + c ^= (b >> 5); \ + a -= b; \ + a -= c; \ + a ^= (c >> 3); \ + b -= c; \ + b -= a; \ + b ^= (a << 10); \ + c -= a; \ + c -= b; \ + c ^= (b >> 15); \ + } + +/* same, but slower, works on systems that might have 8 byte ub4's */ +#define mix2(a, b, c) \ + { \ + a -= b; \ + a -= c; \ + a ^= (c >> 13); \ + b -= c; \ + b -= a; \ + b ^= (a << 8); \ + c -= a; \ + c -= b; \ + c ^= ((b & 0xffffffff) >> 13); \ + a -= b; \ + a -= c; \ + a ^= ((c & 0xffffffff) >> 12); \ + b -= c; \ + b -= a; \ + b = (b ^ (a << 16)) & 0xffffffff; \ + c -= a; \ + c -= b; \ + c = (c ^ (b >> 5)) & 0xffffffff; \ + a -= b; \ + a -= c; \ + a = (a ^ (c >> 3)) & 0xffffffff; \ + b -= c; \ + b -= a; \ + b = (b ^ (a << 10)) & 0xffffffff; \ + c -= a; \ + c -= b; \ + c = (c ^ (b >> 15)) & 0xffffffff; \ + } + +/* +-------------------------------------------------------------------- +hash() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 36+6len instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i= 12) { + a += (k[0] + ((ub4)k[1] << 8) + ((ub4)k[2] << 16) + ((ub4)k[3] << 24)); + b += (k[4] + ((ub4)k[5] << 8) + ((ub4)k[6] << 16) + ((ub4)k[7] << 24)); + c += (k[8] + ((ub4)k[9] << 8) + ((ub4)k[10] << 16) + ((ub4)k[11] << 24)); + mix(a, b, c); + k += 12; + len -= 12; + } + +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch (len) /* all the case statements fall through */ + { + case 11: + c += ((ub4)k[10] << 24); + case 10: + c += ((ub4)k[9] << 16); + case 9: + c += ((ub4)k[8] << 8); + /* the first byte of c is reserved for the length */ + case 8: + b += ((ub4)k[7] << 24); + case 7: + b += ((ub4)k[6] << 16); + case 6: + b += ((ub4)k[5] << 8); + case 5: + b += k[4]; + case 4: + a += ((ub4)k[3] << 24); + case 3: + a += ((ub4)k[2] << 16); + case 2: + a += ((ub4)k[1] << 8); + case 1: + a += k[0]; + /* case 0: nothing left to add */ + } + mix(a, b, c); + /*-------------------------------------------- report the result */ + return c; +} diff --git a/EBase/Hash.h b/EBase/Hash.h new file mode 100644 index 0000000..cc6edf2 --- /dev/null +++ b/EBase/Hash.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +typedef uint32_t ub4; /* unsigned 4-byte quantities */ +typedef uint8_t ub1; +ub4 Hash(const ub1* k, ub4 length, ub4 initval); + diff --git a/EBase/Report.cpp b/EBase/Report.cpp new file mode 100644 index 0000000..810a7eb --- /dev/null +++ b/EBase/Report.cpp @@ -0,0 +1,5391 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/EBase/Report.cpp,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:55:52 $ + * $Revision: 1.12 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: ERESSEA-Datenklassen inclusive CR-Parser + ***************************************************************************** + * + * $Log: Report.cpp,v $ + * Revision 1.12 2000/02/24 09:55:52 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.11 1999/11/28 17:38:10 S.Schuemann + * - Mannigfaltige Änderungen für Vorlage V1.4 beta 9 + * + * Revision 1.10 1999/11/17 08:58:14 S.Schuemann + * - support für multiple CRs + * + * - vielfache Änderungen für Vorlage 1.4 beta 8 + * + * Revision 1.9 1999/11/08 11:10:45 S.Schuemann + * - verbessertes Luxusgut-Handling + * - Korrektur der Kapazitaetsberechnung + * - Neue Flags + * + * Revision 1.8 1999/11/03 10:21:37 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.7 1999/10/26 13:25:43 S.Schuemann + * - Anpassungen fuer neue Object-Attribute und Vorlage 1.4 b 5 + * + * Revision 1.6 1999/10/24 07:59:28 S.Schuemann + * - Korrekturen fuer Troll-Kapazitaeten (Trolle ziehen Wagen) + * + * Revision 1.5 1999/10/20 02:22:32 S.Schuemann + * - Anpassungen der Reportklassen fuer die Features von Vorlage 1.4 b 3 + * + * Revision 1.4 1999/10/18 21:31:46 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.3 1999/09/29 08:02:56 S.Schuemann + * - Implementation der Behandlung des Kapitels EINHEITSBOTSCHAFTEN + * + * Revision 1.2 1999/09/27 06:22:31 S.Schuemann + * - CWorldDB heisst jetzt CReport; + * - Die CR-Felder "Runde" und "Anzahl Personen" werden unterstützt; + * - CReport::GetValue() fuer das Objekt REPORT implementiert; + * - Fehler im Kampfstatus behoben; + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#include "Report.h" +#include +#include +#include "Expression.h" +#include "ReportStream.h" +#include "Utility.h" +#include "hierarchy.h" +#include "regexp.h" + +using namespace std; + +MessagePool g_oMessagePool; + +std::string g_sConfigFile; + +static StringTable g_stringTable; + +// CRegion::Einheiten g_cpoEinheiten; +int g_nMaxVersion = 0; +bool g_bIsRealBuildingType = true; + +RegionDB g_coRDB; +RegionDB::iterator g_iRDB; +int32_t g_nRDBIndex = -1; + +EinheitenDB g_coEDB; +EinheitenDB::iterator g_iEDB; +int32_t g_nEDBIndex = -1; + +RegEinheitenDB g_coREDB; + +RRegionDB g_coRRegionDB; +REinheitenDB g_coREinheitenDB; + +CReport::Reports CReport::m_cpoReports; +CReport::ParteiInfos CReport::g_cpoParteiInfos; +CReport::Gruppen CReport::m_cpoGruppen; + +typedef std::map TAGMAP; + +TAGMAP g_coTags; + +CKarte* g_poKarte = nullptr; +CReport* g_poCurrentReport = nullptr; +CRegion* g_poCurrentRegion = nullptr; +CEinheit* g_poCurrentUnit = nullptr; +CBauwerk* g_poCurrentBuilding = nullptr; +CSchiff* g_poCurrentShip = nullptr; + + +class AdditionalTag +{ + typedef std::set AdditionalTagSet; + typedef std::map AdditionalTags; + +public: + static void Init(); + static void Add(const std::string& sBlock, const std::string& sTag); + static bool Check(const std::string& sBlock, const std::string& sTag); + +private: + static AdditionalTags m_coAdditionalTags; +}; + +AdditionalTag::AdditionalTags AdditionalTag::m_coAdditionalTags; + +void AdditionalTag::Init() +{ + static bool bInit = false; + if (!bInit) { + CConfigFile oCF(g_sConfigFile); + std::string sBlock; + size_t i = 1; + size_t j; + while (true) { + if (!oCF.FetchLine("CRTags", i++, false)) + break; + sBlock = oCF.GetString(0); + j = 1; + while (oCF.IsString(j)) { + Add(sBlock, oCF.GetString(j++)); + } + } + } +} + +void AdditionalTag::Add(const std::string& sBlock, const std::string& sTag) +{ + m_coAdditionalTags[Flatten(sBlock)].insert(Flatten(sTag)); +} + +bool AdditionalTag::Check(const std::string& sBlock, const std::string& sTag) +{ + AdditionalTags::const_iterator ati = m_coAdditionalTags.find(Flatten(sBlock)); + if (ati != m_coAdditionalTags.end()) { + return (*ati).second.find(Flatten(sTag)) != (*ati).second.end(); + } + return false; +} + +typedef std::map GEGENSTANDINFO; + +GEGENSTANDINFO g_coGInfo; + +CGegenstandsInfo& CGegenstandsInfo::Lookup(const std::string& sThing) +{ + static CGegenstandsInfo oUnbekannt; + + if (g_coGInfo.empty()) { + CConfigFile oCF(g_sConfigFile); + size_t i = 1; + while (true) { + if (!oCF.FetchLine("Things", i++)) + break; + + g_coGInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Name"), Value(oCF.GetString(0))); + g_coGInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Gewicht"), Value(oCF.GetReal(1))); + if (oCF.GetReal(2) > 0) + g_coGInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Kapazit\xE4t"), Value(oCF.GetReal(2))); + if (oCF.GetString(3).empty()) + g_coGInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Plural"), Value(oCF.GetString(0))); + else + g_coGInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Plural"), Value(oCF.GetString(3))); + } + } + + map::iterator i = g_coGInfo.find(DeUmlaut(sThing)); + + if (i == g_coGInfo.end()) { + if (!oUnbekannt.NumValues()) + oUnbekannt.SetValue(std::string("Name"), Value(std::string("Unbekannt"))); + return oUnbekannt; + } + return (*i).second; +} + +typedef map RASSENINFO; + +RASSENINFO g_coRaceInfo; + +CRasse& CRasse::Lookup(const std::string& sRace) +{ + static CRasse oUnbekannt; + + if (g_coRaceInfo.empty()) { + std::vector coRaces; + CConfigFile oCF(g_sConfigFile); + size_t i = 1; + + if (oCF.FetchLine("Races", i++)) { + std::string sTRace; + size_t r = 0; + while (!(sTRace = oCF.GetString(r++)).empty()) { + coRaces.push_back(DeUmlaut(sTRace)); + } + + while (true) { + if (!oCF.FetchLine("Races", i++)) + break; + + for (r = 1; r < coRaces.size(); r++) { + if (std::fabs(oCF.GetReal(r)) > 0.00001 || (!oCF.GetString(r).empty() && isdigit(oCF.GetString(r)[0]))) { + if (std::fabs((double)oCF.GetLong(r) - oCF.GetReal(r)) > 0.00001) + g_coRaceInfo[coRaces[r]].SetValue(oCF.GetString(0), Value((double)oCF.GetReal(r))); + else + g_coRaceInfo[coRaces[r]].SetValue(oCF.GetString(0), Value(oCF.GetLong(r))); + } + else { + g_coRaceInfo[coRaces[r]].SetValue(oCF.GetString(0), Value(oCF.GetString(r))); + } + } + } + } + } + + map::iterator i = g_coRaceInfo.find(DeUmlaut(sRace)); + + if (i == g_coRaceInfo.end()) { + if (!oUnbekannt.NumValues()) + oUnbekannt.SetValue(std::string("Einzahl"), Value(std::string("Unbekannt"))); + return oUnbekannt; + } + return (*i).second; +} + +typedef map BURGINFO; + +BURGINFO g_coBurgInfo; + +CBurgInfo& CBurgInfo::Lookup(const std::string& sBurg) +{ + static CBurgInfo oUnbekannt; + + if (g_coBurgInfo.empty()) { + CConfigFile oCF(g_sConfigFile); + size_t i = 1; + while (true) { + if (!oCF.FetchLine("Castles", i++)) + break; + + g_coBurgInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Name"), Value(oCF.GetString(0))); + g_coBurgInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Groesse"), Value(oCF.GetLong(1))); + g_coBurgInfo[DeUmlaut(oCF.GetString(0))].SetValue(std::string("Bonus"), Value(oCF.GetLong(2))); + } + } + + map::iterator i = g_coBurgInfo.find(DeUmlaut(sBurg)); + + if (i == g_coBurgInfo.end()) { + if (!oUnbekannt.NumValues()) + oUnbekannt.SetValue(std::string("Name"), Value(std::string("Unbekannt"))); + return oUnbekannt; + } + return (*i).second; +} + +CBurgInfo* CBurgInfo::Lookup(int32_t nSize) +{ + CBurgInfo* pBT = 0; + int32_t nGroesse = 0; + for (map::iterator bi = g_coBurgInfo.begin(); bi != g_coBurgInfo.end(); ++bi) { + int32_t groesse = bi->second.GetValue("Groesse").asLong(); + if (groesse && groesse < nSize && groesse > nGroesse) + pBT = &(bi->second); + } + return pBT; +} + +typedef map BUILDINGINFO; + +BUILDINGINFO g_coBuildingInfo; + +CBlockBase::Ptr CBuildingInfo::Lookup(const std::string& sBurg) +{ + static CBlockBase::Ptr poUnbekannt(new CBuildingInfo()); + + if (g_coBuildingInfo.empty()) { + CConfigFile oCF(g_sConfigFile); + size_t i = 1, j; + std::shared_ptr pBlock; + std::string sTmp; + + while (true) { + if (!oCF.FetchLine("Buildings", i++)) + break; + // pBlock = new CBuildingInfo(); + g_coBuildingInfo[DeUmlaut(oCF.GetString(0))].reset(new CBuildingInfo()); + g_coBuildingInfo[DeUmlaut(oCF.GetString(0))]->SetValue(std::string("Name"), Value(oCF.GetString(0))); + j = 1; + + sTmp = oCF.GetString(j); + while (!sTmp.empty() && (isdigit(sTmp[0]) || sTmp[0] == '-' || sTmp[0] == '+')) { + if (std::fabs((double)oCF.GetLong(j) - oCF.GetReal(j)) > 0.00001) + g_coBuildingInfo[DeUmlaut(oCF.GetString(0))]->SetValue(oCF.GetString(j + 1), Value((double)oCF.GetReal(j))); + else + g_coBuildingInfo[DeUmlaut(oCF.GetString(0))]->SetValue(oCF.GetString(j + 1), Value(oCF.GetLong(j))); + j += 2; + sTmp = oCF.GetString(j); + } + while (!sTmp.empty() && !(isdigit(sTmp[0]) || sTmp[0] == '-' || sTmp[0] == '+')) { + pBlock.reset(new CBlockBase("BuildingSubObject", oCF.GetString(j++))); + g_coBuildingInfo[DeUmlaut(oCF.GetString(0))]->AddBlock(pBlock); + sTmp = oCF.GetString(j); + while (!sTmp.empty() && (isdigit(sTmp[0]) || sTmp[0] == '-' || sTmp[0] == '+')) { + if (std::fabs((double)oCF.GetLong(j) - oCF.GetReal(j)) > 0.00001) + pBlock->SetValue(oCF.GetString(j + 1), Value((double)oCF.GetReal(j))); + else + pBlock->SetValue(oCF.GetString(j + 1), Value(oCF.GetLong(j))); + j += 2; + sTmp = oCF.GetString(j); + } + } + /* + while( !oCF.GetString( j ).empty() && !IsDigit( oCF.GetString( j )[0] ) ) + { + pBlock.reset( new CBlockBase( "BuildingSubObject", oCF.GetString( j++ ) ) ); + g_coBuildingInfo[DeUmlaut( oCF.GetString( 0 ) )]->AddBlock( pBlock ); + while( oCF.GetLong( j ) ) + { + pBlock->SetValue( oCF.GetString( j+1 ), Value( oCF.GetLong( j ) ) ); + j += 2; + } + } + */ + +#ifdef _DEBUG +// g_coBuildingInfo[DeUmlaut( oCF.GetString( 0 ) )]->Dump(); +#endif + } + } + + map::iterator i = g_coBuildingInfo.find(DeUmlaut(sBurg)); + + if (i == g_coBuildingInfo.end()) { + if (((CBuildingInfo*)poUnbekannt.get())->NumValues()) + poUnbekannt->SetValue(std::string("Name"), Value(std::string("Unbekannt"))); + return poUnbekannt; + } + return (*i).second; +} + +/* +CGegenstandsInfo g_coGegenstandsInfos[]= +{ + { "Stein", "Steine", 60 }, + { "Eisen", "Eisen", 5 }, + { "Holz", "Hoelzer", 5 }, + { "Pferd", "Pferde", 50 }, + { "Schwert", "Schwerter", 1 }, + { "Speer", "Speere", 1 }, + { "Armbrust", "Armbrueste", 1 }, + { "Bogen", "Boegen", 1 }, + { "Katapult", "Katapulte", 120 }, + { "Kettenhemd", "Kettenhemden", 2 }, + { "Plattenpanzer", "Plattenpanzer",4 }, + { "Wagen", "Wagen", 40 }, + + { "Oel", "Oel", 3 }, + { "Balsam", "Balsam", 2 }, + { "Gewuerz", "Gewuerze", 2 }, + { "Gew�rz", "Gew�rze", 2 }, + { "Juwel", "Juwelen", 1 }, + { "Myrrhe", "Myrrhe", 2 }, + { "Seide", "Seide", 3 }, + { "Weihrauch", "Weihrauch", 2 }, + + { "Flachwurz", "Flachwurz", 0 }, + { "Wuerziger Wagemut", "Wuerziger Wagemut", 0 }, + { "Wuerzige Wagemut", "Wuerziger Wagemut", 0 }, + { "W�rziger Wagemut", "W�rziger Wagemut", 0 }, + { "W�rzige Wagemut", "W�rziger Wagemut", 0 }, + { "Eulenauge", "Eulenaugen", 0 }, + { "Gruener Spinnerich", "Gruene Spinneriche", 0 }, + { "Gr�ner Spinnerich", "Gr�ne Spinneriche", 0 }, + { "Blauer Baumringel", "Blaue Baumringel", 0 }, + { "Elfenlieb", "Elfenlieb", 0 }, + { "Gurgelkraut", "Gurgelkraeuter", 0 }, + { "Knotiger Saugwurz","Knotige Saugwurze", 0 }, + { "Blasenmorchel", "Blasenmorcheln", 0 }, + { "Wasserfinder", "Wasserfinder", 0 }, + { "Kakteenschwitz", "Kakteenschwitze", 0 }, + { "Sandfaeule", "Sandfaeulen", 0 }, + { "Sandf�ule", "Sandf�ulen", 0 }, + { "Windbeutel", "Windbeutel", 0 }, + { "Fjordwuchs", "Fjordwuchse", 0 }, + { "Alraune", "Alraunen", 0 }, + { "Steinbeisser", "Steinbeisser", 0 }, + { "Spaltwachs", "Spaltwachse", 0 }, + { "Hoehlenglimm", "Hoehlenglimme",0 }, + { "H�hlenglimm", "H�hlenglimme", 0 }, + { "Eisblume", "Eisblumen", 0 }, + { "Weisser Wueterich","Weisse Wueteriche", 0 }, + { "Wei�er W�terich","Wei�e W�teriche", 0 }, + { "Weisser W�terich","Weisse W�teriche", 0 }, + { "Schneekristall", "Schneekristalle", 0 }, + + { "Siebenmeilentee","Siebenmeilentees", 0 }, + { "Goliathwasser", "Goliathwasser", 0 }, + { "Wasser des Lebens","Wasser des Lebens", 0 }, + { "Schaffenstrunk", "Schaffenstraenke", 0 }, + { "Scheusalsbier", "Scheusalsbiere", 0 }, + { "Duft der Rose", "Duefte der Rosen", 0 }, + { "Bauernblut", "Bauernblut", 0 }, + + { "Gehirnschmalz", "Gehirnschmalz", 0 }, + { "Dumpfbackenbrot","Dumpfbackenbrote", 0 }, + { "Stahlpaste", "Stahlpasten", 0 }, + { "Pferdeglueck", "Pferdeglueck", 0 }, + { "Pferdegl�ck", "Pferdegl�ck", 0 }, + { "Berserkerblut", "Berserkerblut", 0 }, + { "Bauernlieb", "Bauernlieb", 0 }, + { "Riesengras", "Riesengraeser", 0 }, + { "Trank der Wahrheit", "Traenke der Wahrheit", 0 }, + { "Faulobstschnaps","Faulobstschnaepse", 0 }, + { "Elixier der Macht","Elixiere der Macht", 0 }, + { "Heiltrank", "Heiltraenke", 0 }, + { "Amulett der Dunkelheit", "Amulette der Dunkelheit", 0 }, + { "Amulett des Todes","Amulette des Todes", 0 }, + { "Amulett der Heilung","Amulette der Heilung", 0 }, + { "Amulett des wahren Sehens","Amulette des wahren Sehens", 0 }, + { "Mantel der Unverletzlichkeit","Maentel der Unverletzlichkeit", 0 }, + { "Ring der Unsichtbarkeit", "Ringe der Unsichtbarkeit", 0 }, + { "Ring der Macht", "Ringe der Macht", 0 }, + { "Runenschwert", "Runenschwerter", 1 }, // stimmt das? + { "Schildstein", "Schildsteine", 0 }, + { "Zauberstab des Feuers","Zauberstaebe des Feuers", 0 }, + { "Zauberstab der Blitze","Zauberstaebe der Blitze", 0 }, + { "Zauberstab der Teleportation","Zauberstaebe der Teleportation", 0 }, + + { "Drachenkopf", "Drachenkoepfe", 5 }, // Gewicht unsicher +// { "Drachenblut", "Drachenblut", 1 }, + { "Mallorn", "Mallorn", 5 }, + { "Laen", "Laen", 2 }, + { "Laenschild", "Laenschilde", 0 }, + { "Laenkettenhemd", "Laenkettenhemden", 1 }, + { "Schild", "Schilde", 1 }, + { "Bihaender", "Bihaender", 2 }, + { "Bih�nder", "Bih�nder", 2 }, + { "Kriegsaxt", "Kriegsaexte", 1 }, + { "Elfenbogen", "Elfenboegen", 1 }, + { "Laenschwert", "Laenschwerter", 1 }, + { "Hellebarde", "Hellebarden", 2 }, + { "Lanze", "Lanzen", 2 }, + { 0,0,0 } +}; +*/ + +enum TAGID { + T_Alias, + T_Anzahl, + T_Anderepartei, + T_Aura, + T_Auramax, + T_Baeume, + T_Bauern, + T_belagert, + T_Beschr, + T_Besitzer, + T_Belagerer, + T_bewacht, + T_Burg, + T_capacity, + T_cargo, + T_Default, + T_Eisen, + T_ejcOrdersConfirmed, + T_folgt, + T_Groesse, + T_Gruppe, + T_herb, + T_hero, + T_hp, + T_Hunger, + T_Insel, + T_Kampfstatus, + T_Kapazitaet, + T_Kapitaen, + T_Kueste, + T_Ladung, + T_Laen, + T_Lohn, + T_Mallorn, + T_MaxLadung, + T_Name, + T_Partei, + T_Parteitarnung, + T_Pferde, + T_Prozent, + T_privat, + T_Rekruten, + T_Richtung, + T_Runde, + T_Schaden, + T_Schiff, + T_Silber, + T_Schoesslinge, + T_Steine, + T_Strasse, + T_Tarnung, + T_temp, + T_Terrain, + T_Typ, + T_typprefix, + T_unaided, + T_Unterh, + T_Unterhalt, + T_Verkleidung, + T_Verorkt, + T_Verraeter, + T_visibility, + T_wahrerTyp, + T_weight, + + T_TNorden, + T_TSueden, + T_TOsten, + T_TWesten, + T_NNorden, + T_NSueden, + T_NOsten, + T_NWesten, + T_Parteiname, + T_maxLuxus +}; + +#define ADDTAG(t) g_coTags.insert(TAGMAP::value_type(DeUmlaut(#t), T_##t)) +#define ADDTAGSZ(n, t) g_coTags.insert(TAGMAP::value_type(n, T_##t)) + +static void InitTags() +{ + ADDTAG(Alias); + ADDTAG(Anzahl); + ADDTAG(Anderepartei); + ADDTAG(Aura); + ADDTAG(Auramax); + ADDTAG(Baeume); + ADDTAG(Bauern); + ADDTAG(belagert); + ADDTAG(Beschr); + ADDTAG(Besitzer); + ADDTAG(Belagerer); + ADDTAG(bewacht); + ADDTAG(Burg); + ADDTAG(capacity); + ADDTAG(cargo); + ADDTAG(Default); + ADDTAG(Eisen); + ADDTAG(ejcOrdersConfirmed); + ADDTAG(folgt); + ADDTAG(Groesse); + ADDTAG(Gruppe); + ADDTAG(herb); + ADDTAG(hero); + ADDTAG(hp); + ADDTAG(Hunger); + ADDTAG(Insel); + ADDTAG(Kampfstatus); + ADDTAG(Kapazitaet); + ADDTAG(Kapitaen); + ADDTAG(Kueste); + ADDTAG(Ladung); + ADDTAG(Laen); + ADDTAG(Lohn); + ADDTAG(Mallorn); + ADDTAG(Mallorn); + ADDTAG(MaxLadung); + ADDTAG(Name); + ADDTAG(Partei); + ADDTAG(Parteitarnung); + ADDTAG(Pferde); + ADDTAG(privat); + ADDTAG(Prozent); + ADDTAG(Rekruten); + ADDTAG(Richtung); + ADDTAG(Runde); + ADDTAG(Schaden); + ADDTAG(Schiff); + ADDTAG(Silber); + ADDTAG(Schoesslinge); + ADDTAG(Steine); + ADDTAG(Strasse); + ADDTAG(Tarnung); + ADDTAG(Terrain); + ADDTAG(temp); + ADDTAG(Typ); + ADDTAG(typprefix); + ADDTAG(unaided); + ADDTAG(Unterh); + ADDTAG(Unterhalt); + ADDTAG(Verkleidung); + ADDTAG(Verorkt); + ADDTAG(Verraeter); + ADDTAG(visibility); + ADDTAG(wahrerTyp); + ADDTAG(weight); + + ADDTAGSZ("B\xE4ume", Baeume); + ADDTAGSZ( + "Gr\xF6\xDF" + "e", + Groesse); + ADDTAGSZ("Kapazit\xE4t", Kapazitaet); + ADDTAGSZ("Kapit\xE4n", Kapitaen); + ADDTAGSZ("K\xFCste", Kueste); + ADDTAGSZ("Schoe\xDFlinge", Schoesslinge); + ADDTAGSZ("Sch\xF6sslinge", Schoesslinge); + ADDTAGSZ("Sch\xF6\xDFlinge", Schoesslinge); + ADDTAGSZ( + "Stra\xDF" + "e", + Strasse); + ADDTAGSZ("Verr\xE4ter", Verraeter); + + ADDTAG(TNorden); + ADDTAG(TSueden); + ADDTAG(TOsten); + ADDTAG(TWesten); + ADDTAG(NNorden); + ADDTAG(NSueden); + ADDTAG(NOsten); + ADDTAG(NWesten); + ADDTAG(Parteiname); + ADDTAG(maxLuxus); +} + +///////////////////////////////////////////////////////////////////// +//.class: CBlockBase +///////////////////////////////////////////////////////////////////// + +CBlockBase::Ptr CBlockBase::g_poConfigObjects; + +CBlockBase::CBlockBase(const char* pcType, const std::string& sName, const Value& oKey1, const Value& oKey2, const Value& oKey3) + : m_pcType(pcType) + , m_nName(g_stringTable.insert(sName)) + , m_oKey1(oKey1) + , m_oKey2(oKey2) + , m_oKey3(oKey3) +{ + (void)m_pcType; + // TRACEMSG(( "C: %s\n", m_pcType )); +} + +CBlockBase::CBlockBase(const char* pcType, const char* pcName) + : m_pcType(pcType) + , m_nName(g_stringTable.insert(pcName ? pcName : "")) +{ + // TRACEMSG(( "C: %s\n", m_pcType )); +} + +CBlockBase::CBlockBase(const char* pcType, CReportStream& oRS) + : m_pcType(pcType) +{ + // TRACEMSG(( "C: %s\n", m_pcType )); + std::string sTag; + m_nName = g_stringTable.insert(oRS.GetValue()); + if (oRS.GetNumDat() > 0) { + m_oKey1 = Value(oRS.GetDat(0)); + } + if (oRS.GetNumDat() > 1) { + m_oKey2 = Value(oRS.GetDat(1)); + } + if (oRS.GetNumDat() > 2) { + m_oKey3 = Value(oRS.GetDat(2)); + } + oRS.Next(); + while (!oRS.EOS()) { + sTag = oRS.GetComment(); + if (sTag.empty()) + sTag = std::string("@") + ToString((int32_t)m_coNamedValues.size()); + + if (oRS.GetType() == CReportStream::enBLOCK) { + if (CHierarchy::IsChild(pcType, oRS.GetValue())) { + LoadSubObject(oRS); + } + else { + break; + } + } + else if (!oRS.GetComment().empty()) { + if (oRS.GetType() == CReportStream::enINTEGER) { + if (oRS.GetNumDat()) + SetValue(sTag, Value(oRS.GetDat(0))); + for (int i = 1; i < oRS.GetNumDat(); i++) { + SetValue(oRS.GetComment() + ':' + ToString(i), Value(oRS.GetDat(i))); + } + } + else if (oRS.GetType() == CReportStream::enSTRING) { + SetValue(sTag, Value(oRS.GetValue())); + } + } + oRS.Next(); + } +} + +CBlockBase::~CBlockBase() +{ + // TRACEMSG(( "D: %s\n", m_pcType )); +} + +void CBlockBase::LoadSubObject(CReportStream& oRS) +{ + std::string sName = oRS.GetValue(); + CBlockBase::Ptr pBlk(new CBlockBase(sName.c_str(), oRS)); + if (pBlk.get()) { + pBlk->m_poHInfo = CHierarchy::Lookup(sName); + } + AddBlock(pBlk); +} + +std::string CBlockBase::ID() const +{ + return CalcID(m_nName, m_oKey1, m_oKey2, m_oKey3); +} + +void CBlockBase::SetValue(const std::string& sName, const Value& oVal) +{ + m_coNamedValues[CStringDB::Str2SID(sName)] = oVal; +} + +void CBlockBase::SetValue(CReportStream& oRS) +{ + if (oRS.GetComment().empty()) + return; + switch (oRS.GetType()) { + case CReportStream::enINTEGER: { + SetValue(oRS.GetComment(), Value(oRS.GetDat(0))); + for (int32_t i = 1; i < oRS.GetNumDat(); i++) { + SetValue(oRS.GetComment() + ':' + ToString(i), Value(oRS.GetDat(i))); + } + } break; + case CReportStream::enSTRING: + SetValue(oRS.GetComment(), Value(oRS.GetValue())); + break; + default: + break; + } +} + +Value CBlockBase::GetValue(int32_t nIdx, std::string& sName) const +{ + if (nIdx >= (int32_t)NumValues()) { + sName = ""; + return Value(); + } + else { + int c = 0; + NamedValues::const_iterator vi; + for (vi = m_coNamedValues.begin(); c < nIdx && vi != m_coNamedValues.end(); vi++, c++) + ; + sName = CStringDB::SID2Str((*vi).first); + return (*vi).second; + } +} + +Value CBlockBase::GetValue(const std::string& sName, const Value& oDefault) const +{ + CObjectPart oOP; + oOP.label = sName; + return GetValue((CBlockBase*)this, &oOP, oDefault); + /* + NamedValues::const_iterator i = m_coNamedValues.find( CStringDB::Str2SID( sName ) ); + if( i == m_coNamedValues.end() ) + return oDefault; + return (*i).second; + */ +} + +Value CBlockBase::GetValue(std::shared_ptr pBlk, CObjectPart* pOP, const Value& oDefault) +{ + return GetValue(pBlk.get(), pOP, oDefault); +} + +Value CBlockBase::GetValue(CBlockBase* pBlk, CObjectPart* pOP, const Value& oDefault) +{ + if (pOP) { + CBlockBase* pOrgBlk = pBlk; + CBlockBase* pHBlk; + Value oVal; + CObjectPart* pCOP = pOP; + bool bClassFound = false; + while (pBlk /*&& pCOP->next*/) { + if (pCOP->next && IsEqual(pCOP->next->label, "size")) { + NamedSubblocks::iterator i = pBlk->m_coSubblocks.find(g_stringTable.insert(pCOP->label)); + if (i != pBlk->m_coSubblocks.end()) { + if ((*i).second.size() == 1 && !((*i).second.begin()->second->HasKeys())) { + return Value((int32_t)((*i).second.begin()->second->NumValues())); + } + else + return Value((int32_t)((*i).second.size())); + } + else { + return Value(0); + } + } + switch (pCOP->index.size()) { + case 0: + pHBlk = pBlk->GetBlock(pCOP->label, bClassFound).get(); + break; + case 1: + pHBlk = pBlk->GetBlock(pCOP->label, bClassFound, pCOP->index[0]).get(); + if (pHBlk && !pHBlk->HasKeys() && !pCOP->next) { + return pHBlk->GetValue(std::string("@") + pCOP->index[0].asString()); + } + break; + case 2: + pHBlk = pBlk->GetBlock(pCOP->label, bClassFound, pCOP->index[0], pCOP->index[1]).get(); + break; + default: + pHBlk = pBlk->GetBlock(pCOP->label, bClassFound, pCOP->index[0], pCOP->index[1], pCOP->index[2]).get(); + } + + if (!pHBlk) + break; + + pBlk = pHBlk; + pCOP = pCOP->next; + if (!pCOP) + break; + } + if (pBlk) { + if (pCOP) { + NamedValues::const_iterator i; + if (pCOP->index.size() && pCOP->index[0].asLong()) { + i = pBlk->m_coNamedValues.find(CStringDB::Str2SID(pCOP->label + ':' + ToString(pCOP->index[0].asLong()))); + } + else { + i = pBlk->m_coNamedValues.find(CStringDB::Str2SID(pCOP->label)); + } + if (i == pBlk->m_coNamedValues.end()) { + if (pCOP == pOP && g_poConfigObjects.get() == pOrgBlk && !bClassFound) { + Value oErr; + std::string sErr(std::string("Zugriff auf nicht existierendes Object '") + pOP->label + "'!"); + oErr.error(sErr.c_str()); + return oErr; + } + oVal = oDefault; + } + else + oVal = (*i).second; + } + else { + oVal = (oDefault.asLong() == 0) ? Value(1) : oDefault; + } + } + else { + if (pCOP == pOP && g_poConfigObjects.get() == pOrgBlk && !bClassFound) { + Value oErr; + std::string sErr(std::string("Zugriff auf nicht existierendes Object '") + pOP->label + "'!"); + oErr.error(sErr.c_str()); + return oErr; + } + oVal = oDefault; + } + return oVal; + } + else { + return oDefault; + } +} + +Value CBlockBase::GetValue(std::shared_ptr pBlk, const std::string& sOAS, const Value& oDefault) +{ + std::shared_ptr pOP = Expression::parseObjectAccess(sOAS); + if (pOP.get()) { + Value oVal(GetValue(pBlk, pOP.get(), oDefault)); + return oVal; + } + else { + NamedValues::const_iterator i = pBlk->m_coNamedValues.find(CStringDB::Str2SID(sOAS)); + if (i == pBlk->m_coNamedValues.end()) + return oDefault; + return (*i).second; + } +} + +Value CBlockBase::GetValue(CBlockBase* pBlk, const std::string& sOAS, const Value& oDefault) +{ + std::shared_ptr pOP = Expression::parseObjectAccess(sOAS); + if (pOP.get()) { + Value oVal(GetValue(pBlk, pOP.get(), oDefault)); + return oVal; + } + else { + NamedValues::const_iterator i = pBlk->m_coNamedValues.find(CStringDB::Str2SID(sOAS)); + if (i == pBlk->m_coNamedValues.end()) + return oDefault; + return (*i).second; + } +} + +Value CBlockBase::GetValue(CObjectPart* pOP, const Value& oDefault) const +{ + return GetValue((CBlockBase*)this, pOP, oDefault); +} + +void CBlockBase::AddBlock(std::shared_ptr pBlock) +{ + NamedSubblocks::iterator i = m_coSubblocks.find(pBlock->m_nName); + if (i == m_coSubblocks.end()) { + BlockGroup bg; + i = m_coSubblocks.insert(NamedSubblocks::value_type(pBlock->m_nName, bg)).first; + } + (*i).second[pBlock->ID()] = pBlock; +} + +std::shared_ptr CBlockBase::GetBlock(const std::string& sName, bool& bClassFound, const Value& oKey1, const Value& oKey2, const Value& oKey3) +{ + int32_t id = g_stringTable.insert(sName); + // std::clog << "CBlockBase::GetBlock(" << sName << ") : sid = " << id << std::endl; + NamedSubblocks::iterator i = m_coSubblocks.find(id); + if (i != m_coSubblocks.end()) { + bClassFound = true; + if ((*i).second.size() == 1 && !(*i).second.begin()->second->HasKeys()) { + if (oKey1.getType() != VT_EMPTY) + return (*i).second.begin()->second; + } + BlockGroup::iterator bi = (*i).second.find(CalcID(id, oKey1, oKey2, oKey3)); + if (bi != (*i).second.end()) { + return (*bi).second; + } + } + else { + bClassFound = false; + } + return std::shared_ptr(); +} + +std::string CBlockBase::CalcID(const std::string& sName, const Value& oKey1, const Value& oKey2, const Value& oKey3) +{ + return CalcID(g_stringTable.insert(sName), oKey1, oKey2, oKey3); +} + +std::string CBlockBase::CalcID(int nName, const Value& oKey1, const Value& oKey2, const Value& oKey3) +{ + return std::to_string(nName) + '/' + Flatten(oKey1.asString()) + '/' + Flatten(oKey2.asString()) + '/' + Flatten(oKey3.asString()); +} + +void CBlockBase::ReadConfigObjects(const std::string& sFile, const std::string& sCap, CFGOBJMODE enCOM, const std::vector& coTitles) +{ + std::string sFileName = PathedFileName(sFile.empty() ? g_sConfigFile : sFile); + bool bClassFound; + if (!g_poConfigObjects.get()) { + g_poConfigObjects.reset(new CBlockBase("CFGObjectBase", std::string("ObjBase"))); + } + + switch (enCOM) { + case enTABH: { + CBlockBase::Ptr pBlock; + CConfigFile oCF(sFileName); + size_t i = 1; + while (true) { + if (!oCF.FetchLine(sCap, i++)) + break; + + pBlock = g_poConfigObjects->GetBlock(sCap, bClassFound, Value(DeUmlaut(oCF.GetString(0)))); + if (!pBlock.get()) { + pBlock.reset(new CBlockBase("ConfigObjectTabH", sCap, Value(DeUmlaut(oCF.GetString(0))))); + g_poConfigObjects->AddBlock(pBlock); + } + + pBlock->SetValue(std::string("Name"), Value(oCF.GetString(0))); + + for (size_t j = 0; j < coTitles.size(); j++) { + if (oCF.IsString(j + 1)) + pBlock->SetValue(coTitles[j], Value(oCF.GetString(j + 1))); + else if (std::fabs((double)oCF.GetLong(j + 1) - oCF.GetReal(j + 1)) > 0.00001) + pBlock->SetValue(coTitles[j], Value(oCF.GetReal(j + 1))); + else + pBlock->SetValue(coTitles[j], Value(oCF.GetLong(j + 1))); + } + } + } break; + case enTABV: { + std::vector coIdx; + CConfigFile oCF(sFileName); + size_t i = 1; + + if (oCF.FetchLine(sCap, i++)) { + std::string sIdx; + CBlockBase::Ptr pBlock; + size_t r = 0; + + while (!(sIdx = oCF.GetString(r++)).empty()) { + if (!coIdx.empty()) { + pBlock = g_poConfigObjects->GetBlock(sCap, bClassFound, Value(DeUmlaut(sIdx))); + if (!pBlock.get()) { + pBlock = Ptr(new CBlockBase("ConfigObjectTabV", sCap, Value(DeUmlaut(sIdx)))); + g_poConfigObjects->AddBlock(pBlock); + } + pBlock->SetValue("Name", Value(sIdx)); + } + coIdx.push_back(DeUmlaut(sIdx)); + } + + while (true) { + if (!oCF.FetchLine(sCap, i++)) + break; + + for (r = 1; r < coIdx.size(); r++) { + pBlock = g_poConfigObjects->GetBlock(sCap, bClassFound, Value(coIdx[r])); + + if (oCF.IsString(r)) + pBlock->SetValue(oCF.GetString(0), Value(oCF.GetString(r))); + else if (std::fabs((double)oCF.GetLong(r) - oCF.GetReal(r)) > 0.00001) + pBlock->SetValue(oCF.GetString(0), Value((double)oCF.GetReal(r))); + else + pBlock->SetValue(oCF.GetString(0), Value(oCF.GetLong(r))); + } + } + } + } break; + case enNEST: { + CConfigFile oCF(sFileName); + CBlockBase::Ptr pBlock, pNBlock; + std::string sTmp; + size_t i = 1, j; + + while (true) { + if (!oCF.FetchLine(sCap, i++)) + break; + // pBlock = new CBuildingInfo(); + pBlock = g_poConfigObjects->GetBlock(sCap, bClassFound, Value(DeUmlaut(oCF.GetString(0)))); + if (!pBlock.get()) { + pBlock.reset(new CBlockBase("ConfigObjectNest", sCap, Value(DeUmlaut(oCF.GetString(0))))); + g_poConfigObjects->AddBlock(pBlock); + } + pBlock->SetValue(std::string("Name"), Value(oCF.GetString(0))); + + j = 1; + + sTmp = oCF.GetString(j); + while (!sTmp.empty() && (isdigit(sTmp[0]) || sTmp[0] == '-' || sTmp[0] == '+')) { + if (std::fabs((double)oCF.GetLong(j) - oCF.GetReal(j)) > 0.00001) + pBlock->SetValue(oCF.GetString(j + 1), Value((double)oCF.GetReal(j))); + else + pBlock->SetValue(oCF.GetString(j + 1), Value(oCF.GetLong(j))); + j += 2; + sTmp = oCF.GetString(j); + } + while (!sTmp.empty() && !(isdigit(sTmp[0]) || sTmp[0] == '-' || sTmp[0] == '+')) { + pNBlock = pBlock->GetBlock(sTmp, bClassFound); + if (!pNBlock.get()) { + pNBlock.reset(new CBlockBase("ConfigObjectNestSub", sTmp)); + pBlock->AddBlock(pNBlock); + } + sTmp = oCF.GetString(++j); + while (!sTmp.empty() && (isdigit(sTmp[0]) || sTmp[0] == '-' || sTmp[0] == '+')) { + if (std::fabs((double)oCF.GetLong(j) - oCF.GetReal(j)) > 0.00001) + pNBlock->SetValue(oCF.GetString(j + 1), Value((double)oCF.GetReal(j))); + else + pNBlock->SetValue(oCF.GetString(j + 1), Value(oCF.GetLong(j))); + j += 2; + sTmp = oCF.GetString(j); + } + } + } + } break; + } +} + +void CBlockBase::Dump(unsigned int lvl) +{ + char spc[16]; + snprintf(spc, sizeof(spc), "%%%ds", lvl * 2); + fprintf(stderr, spc, ""); + fprintf(stderr, "<%s", g_stringTable.i2s(m_nName).c_str()); + if (m_oKey1.getType() != VT_EMPTY) { + fprintf(stderr, " k1=%c%s%c", 34, m_oKey1.asString().c_str(), 34); + if (m_oKey2.getType() != VT_EMPTY) { + fprintf(stderr, " k2=%c%s%c", 34, m_oKey2.asString().c_str(), 34); + if (m_oKey3.getType() != VT_EMPTY) { + fprintf(stderr, " k3=%c%s%c", 34, m_oKey3.asString().c_str(), 34); + } + } + } + fprintf(stderr, ">\n"); + for (NamedValues::iterator ai = m_coNamedValues.begin(); ai != m_coNamedValues.end(); ai++) { + fprintf(stderr, spc, ""); + fprintf(stderr, " <%s>%s\n", CStringDB::SID2Str((*ai).first).c_str(), (*ai).second.asString().c_str(), CStringDB::SID2Str((*ai).first).c_str()); + } + for (NamedSubblocks::iterator bi = m_coSubblocks.begin(); bi != m_coSubblocks.end(); bi++) { + for (BlockGroup::iterator bgi = (*bi).second.begin(); bgi != (*bi).second.end(); bgi++) { + (*bgi).second->Dump(lvl + 1); + } + } + fprintf(stderr, spc, ""); + fprintf(stderr, "\n", g_stringTable.i2s(m_nName).c_str()); +} + +///////////////////////////////////////////////////////////////////// +//.class: CMessage +///////////////////////////////////////////////////////////////////// + +std::map* CMessage::m_pcoMessageTypes = 0; +bool CMessage::m_bForceRender = false; +CMessage::RENDERER CMessage::m_enRenderer = CMessage::NONE; + +void CMessage::registerMessage(int32_t round) +{ + g_oMessagePool[round].push_back(this); +} + +CMessage::CMessage(int32_t round) + : CBlockBase("CMessage", std::string("MESSAGE")) +{ + registerMessage(round); +} + +CMessage::CMessage(const std::string& sRendered, int32_t round) + : CBlockBase("CMessage", std::string("MESSAGE")) +{ + registerMessage(round); + SetValue(std::string("rendered"), Value(sRendered)); +} + +CMessage::CMessage(CReportStream& oRS, int32_t round) + : CBlockBase("CMessage", oRS) +{ + registerMessage(round); +} + +void CMessage::GetCoords(std::string sTag, int32_t& x, int32_t& y, int32_t& z) const +{ + x = 0; + y = 0; + z = 0; + if (GetValue(sTag, Value("")).getType() == VT_INT) { + x = GetValue(sTag, Value(0)).asLong(); + y = GetValue(sTag + ":1", Value(0)).asLong(); + z = GetValue(sTag + ":2", Value(0)).asLong(); + } + else { + std::string sReg = GetValue(sTag, Value("")).asString(); + if (!sReg.empty()) { + x = atoi(sReg.c_str()); + auto p = sReg.find(','); + if (p != std::string::npos) { + y = atoi(sReg.c_str() + p + 1); + p = sReg.find(',', p + 1); + if (p != std::string::npos) { + z = atoi(sReg.c_str() + p + 1); + } + else + z = 0; + } + } + } +} + +int32_t CMessage::Messages(int32_t round) +{ + MessagePool::const_iterator mpi = g_oMessagePool.find(round); + if (mpi == g_oMessagePool.end()) + return 0; + return (int32_t)mpi->second.size(); +} + +CMessage* CMessage::FindMessage(int32_t round, int32_t idx) +{ + MessagePool::const_iterator mpi = g_oMessagePool.find(round); + if (mpi == g_oMessagePool.end()) + return 0; + if (size_t(idx) >= mpi->second.size()) + return 0; + return mpi->second[size_t(idx)]; +} + +std::string CMessage::Render(CReport* pRep) const +{ + std::string sMsg; + + if (!m_bForceRender) + sMsg = GetValue("rendered", Value("")).asString(); + + if (sMsg.empty()) { + switch (m_enRenderer) { + case ERESSEA1: + return Eressea1_Render(pRep); + break; + case ERESSEA2: + return Eressea2_Render(pRep); + break; + default: + // TODO: Handle error + break; + } + } + + return sMsg; +} + +std::string CMessage::Eressea1_Render(CReport* pRep) const +{ + std::string sMsg; + int32_t nID = GetValue("type").asLong(); + std::map::const_iterator i = m_pcoMessageTypes->find(nID); + std::string sRule, sFunc, sVal; + std::string::size_type p = 0; + std::string::size_type e = 0; + + if (i == m_pcoMessageTypes->end()) { + sMsg = GetValue("rendered", Value("")).asString(); + if (sMsg.empty()) + return std::string("(MSG: kein Messagetyp und kein 'rendered')"); + return sMsg; + } + sRule = (*i).second; + + while ((p = sRule.find_first_of('{', e)) != std::string::npos) { + sMsg += sRule.substr(e, p - e); + e = sRule.find_first_of('}', p); + if (e == std::string::npos) { + ERRMSG(0, ("Fehler: Syntaktischer Fehler in Message-Typ: %s", (*i).second.c_str())); + break; + } + e++; + sFunc = sRule.substr(p + 1, e - p - 2); + sVal = sFunc; + if (!sFunc.empty() && sFunc[0] == '$') { + auto h = sFunc.find_last_of(' '); + if (h != std::string::npos) { + sVal = sFunc.substr(h + 1); + sFunc.erase(h); + } + + if (IsEqual(sFunc, "$travel")) { + switch (GetValue(sVal).asLong()) { + case 1: + sVal = "reitet"; + break; + case 2: + sVal = "wandert"; + break; + default: + sVal = "reist"; + } + } + else if (IsEqual(sFunc, "$travelthru")) { + if (!GetValue(sVal, Value("")).asString().empty()) + sVal = std::string(" Dabei wurde ") + GetValue(sVal).asString() + " durchquert."; + else + sVal = ""; + } + else if (IsEqual(sFunc, "$of")) { + if (GetValue("amount").asLong() != GetValue(sVal).asLong()) + sVal = std::string("von ") + GetValue(sVal).asString() + " "; + else + sVal = ""; + if (nID == 2097 || nID == 771334452) { + if (!sVal.empty()) + sVal += ' '; + sVal += "Silber"; + } + } + else if (IsEqual(sFunc, "$earn")) { + switch (GetValue(sVal).asLong()) { + case 0: + sVal = "Arbeit"; + break; + case 1: + sVal = "Unterhaltung"; + break; + case 2: + sVal = "Steuereinteiben"; + break; + case 3: + sVal = "Handeln"; + break; + case 4: + sVal = "Handelssteuern"; + break; + default: + sVal = + "dunklen Gesch\xE4" + "ften"; + } + } + } + else if (IsEqual(sVal, "unit") || IsEqual(sVal, "target")) { + CEinheit* pU = pRep->SearchUnit(GetValue(sVal).asLong()); + if (pU) + sVal = pU->Name() + " (" + itoan(GetValue(sVal).asLong(), pRep->ENrBase()) + ")"; + else + sVal = std::string("Unbekannte Einheit (") + itoan(GetValue(sVal).asLong(), pRep->ENrBase()) + ")"; + } + else if (IsEqual(sVal, "from") || IsEqual(sVal, "to")) { + sVal = pRep->Parteiname(GetValue(sVal).asLong()).substr(1) + " (" + itoan(GetValue(sVal).asLong(), pRep->PNrBase()) + ")"; + ; + } + else if (IsEqual(sVal, "building")) { + CBauwerk* pB = pRep->GetBuilding(GetValue(sVal).asLong()); + if (pB) + sVal = pB->Name() + " (" + itoan(GetValue(sVal).asLong(), pRep->BNrBase()) + ")"; + else + sVal = "unbekannt"; + } + else if (IsEqual(sVal, "ship")) { + CSchiff* pS = pRep->GetShip(GetValue(sVal).asLong()); + if (pS) + sVal = pS->Name() + " (" + itoan(GetValue(sVal).asLong(), pRep->BNrBase()) + ")"; + else + sVal = "unbekannt"; + } + else if (IsEqual(sVal, "region") || IsEqual(sVal, "start") || IsEqual(sVal, "end")) { + int32_t x, y, z; + GetCoords(sVal, x, y, z); + CRegion* pReg = pRep->Karte()->GetFromECords(x, y, z, true); + if (pReg) { + sVal = pReg->GetName() + " (" + ToString(x) + "," + ToString(y); + if (z) + sVal += "," + ToString(z); + sVal += ")"; + } + else + sVal = "Unbekante Region"; + } + else + sVal = GetValue(sVal).asString(); + + sMsg += sVal; + } + sMsg += sRule.substr(e); + + return sMsg; +} + +std::string CMessage::Eressea2_Render(CReport* pRep) const +{ + std::string sMsg; + int32_t nID = GetValue("type").asLong(); + std::map::const_iterator i = m_pcoMessageTypes->find(nID); + std::string sRule, sFunc, sVal; + + if (i == m_pcoMessageTypes->end()) { + sMsg = GetValue("rendered", Value("")).asString(); + if (sMsg.empty()) + return std::string("(MSG: kein Messagetyp und kein 'rendered')"); + return sMsg; + } + sRule = (*i).second; + + std::string::const_iterator iRule(sRule.begin()); + return E2R_Parse(iRule, sRule.end()); +} + +std::string CMessage::E2R_Parse(std::string::const_iterator& iRule, const std::string::const_iterator& iEnd) const +{ + ArgumentList coArgs; + std::string sErg; + std::string sArg; + + if (iRule == iEnd) { + return ""; + } + else if (*iRule == '"') { + iRule++; + if (iRule != iEnd && *iRule != '"') { + do { + if (*iRule == ',') { + sArg += ','; + ++iRule; + } + sArg += E2R_Parse(iRule, iEnd); + } while (iRule != iEnd && *iRule != '"'); + } + if (iRule != iEnd && *iRule == '"') + iRule++; + return sArg; + } + else if (*iRule == '$') { + std::string sID; + bool ref = false; + iRule++; + if (iRule != iEnd && *iRule == '{') { + iRule++; + ref = true; + } + while (iRule != iEnd && IsAlNum(*iRule)) { + sID += *iRule++; + } + if (ref && iRule != iEnd && *iRule == '}') { + iRule++; + return GetValue(sID, Value("")).asString(); + } + if (iRule != iEnd && *iRule != '(') { + Value oVal0 = GetValue(sID, Value("")); + Value oVal1, oVal2; + + sArg = oVal0.asString(); + if (oVal0.getType() == VT_INT) { + oVal1 = GetValue(sID + ":1", Value()); + oVal2 = GetValue(sID + ":2", Value(0)); + if (oVal1.getType() == VT_INT) { + sArg = std::string("REGION[") + ToString(oVal0.asLong()) + ',' + ToString(oVal1.asLong()) + ',' + ToString(oVal2.asLong()) + "]"; + } + } + return sArg; + } + std::string sParam; + do { + // Klammer oder Komma �berspringen + sParam = ""; + do { + iRule++; + while (iRule != iEnd && *iRule == ' ') + ++iRule; + sParam += E2R_Parse(iRule, iEnd); + while (iRule != iEnd && *iRule == ' ') + ++iRule; + } while (iRule != iEnd && *iRule == '.'); + coArgs.push_back(Value(sParam)); + } while (iRule != iEnd && *iRule == ','); + if (iRule != iEnd && *iRule != ')') { + ERRMSG(0, ("Fehler: Fehlende Klammer fuer Funktion '%s' in Renderregel!", std::string(std::string("EMR_") + sID).c_str())); + } + else { + iRule++; + } + Value oVErg; + if (sID == "if" && coArgs.size() == 2) { + coArgs.push_back(Value("")); + } + if (!DoUserFunction(std::string("EMR_") + sID, coArgs, &oVErg)) { + ERRMSG(0, ("Fehler: Benoetigte Render-Funktion '%s' nicht gefunden!", std::string(std::string("EMR_") + sID).c_str())); + return std::string("$") + sID + "()"; + } + return oVErg.asString(); + } + else { + while (iRule != iEnd && *iRule != '$' && *iRule != ')' && *iRule != ',' && *iRule != '"') { + sErg += *iRule++; + } + } + return sErg; +} + +/* +std::map CMessage::m_coMessageTypes; + +CMessage::CMessage( CReportStream& oRS ) +{ + do + { + if( !oRS.GetComment().empty() ) + { + if( oRS.GetType()==CReportStream::enINTEGER ) + { + if( oRS.GetNumDat() ) + m_coParams[oRS.GetComment()] = ToString((int32_t)oRS.GetDat(0)); + } + else if( oRS.GetType()==CReportStream::enSTRING ) + { + m_coParams[oRS.GetComment()] = oRS.GetValue(); + } + } + + oRS.Next(); + } + while( !oRS.EOS() && oRS.GetType()!=CReportStream::enBLOCK ); +} + +CMessage::~CMessage() +{ +} + +std::string CMessage::Render( int32_t nID ) const +{ + std::map::const_iterator i = m_coMessageTypes.find( nID ); + std::string sMsg,sRule; + int p = 0; + int e = 0; + + if( i == m_coMessageTypes.end() ) + return Element( "rendered" ) + "(kein Messagetyp)"; + sRule = (*i).second; + + while( (p=sRule.find_first_of( '{', e )) != std::string::npos ) + { + e = sRule.find_first_of( '}', p ); + if( e == std::string::npos ) + { + ERRMSG( 0, ( "Fehler: Syntaktischer Fehler in Message-Typ: %s", (*i).second.c_str() )); + break; + } + sMsg += std::string( "[" ) + Element( sRule.substr( p+1, e-p-1 ) ) + std::string( "]" ); + } + return sMsg; +} + +std::string CMessage::Element( const std::string& sKey ) const +{ + std::map::const_iterator i = m_coParams.find( sKey ); + if( i == m_coParams.end() ) + return "(unbekannt)"; + else + return (*i).second; +} +*/ + +///////////////////////////////////////////////////////////////////// +//.class: CKarte +///////////////////////////////////////////////////////////////////// + +CKarte::CKarte(CReport* poReport) + : m_bMap(false) + , m_nCX(0) + , m_nCY(0) + , m_nCursorX(0) + , m_nCursorY(0) + , m_nLeft(0) + , m_nRight(0) + , m_nTop(0) + , m_nBottom(0) + , m_poReport(poReport) +{ +} + +CKarte::CKarte(CReportStream& oRS, CReport* poReport) + : m_bMap(false) + , m_nCX(0) + , m_nCY(0) + , m_nCursorX(0) + , m_nCursorY(0) + , m_nLeft(0) + , m_nRight(0) + , m_nTop(0) + , m_nBottom(0) + , m_poReport(poReport) +{ + Import(oRS); +} + +void CKarte::Import(CReportStream& oRS, int nRunde) +{ + int32_t nPos = 0; + CRegion* pReg; + do { + if (oRS.GetType() == CReportStream::enBLOCK && (oRS.GetValue() == "REGION" || oRS.GetValue() == "DURCHREISEREGION" || oRS.GetValue() == "SPEZIALREGION")) { + pReg = new CRegion(oRS, this, nRunde, nPos++); + Set(pReg); + } + else + oRS.Next(); + } while (!oRS.EOS()); +} + +void CKarte::Write(CReportStream& oRS) +{ + for (RegionMap::iterator i = m_cpoRegions.begin(); i != m_cpoRegions.end(); i++) { + (*i).second->Write(oRS); + } +} + +CKarte::~CKarte() +{ + for (RegionMap::iterator i = m_cpoRegions.begin(); i != m_cpoRegions.end(); i++) { + delete (*i).second; + } + m_cpoRegions.clear(); +} + +int CKarte::m_nWLeft; +int CKarte::m_nWRight; +int CKarte::m_nWTop; +int CKarte::m_nWBottom; +bool CKarte::m_bWMap = false; + +void CKarte::Set(CRegion* poRegion) +{ + RegionMap::iterator i; + CRegionKey oRKey = poRegion->GetKey(); + i = m_cpoRegions.find(oRKey); + if (i != m_cpoRegions.end()) { + if (poRegion->GetBlock() < (*i).second->GetBlock()) { + delete (*i).second; + (*i).second = poRegion; + for (std::vector::iterator vri = m_cpoVRegions.begin(); vri != m_cpoVRegions.end(); vri++) { + if ((*vri)->GetKey() == oRKey) { + (*vri) = poRegion; + break; + } + } + } + else { + delete poRegion; + return; + } + } + else { + if (!m_bMap && (poRegion->GetBlock() < CRegion::enSPEZIALREGION || poRegion->GetBlock() == CRegion::enSCHEMEN)) { + m_nLeft = poRegion->GetEX() + poRegion->GetEY() / 2; + m_nRight = poRegion->GetEX() + poRegion->GetEY() / 2; + m_nTop = poRegion->GetEY(); + m_nBottom = poRegion->GetEY(); + m_bMap = true; + } + if (!m_bWMap && (poRegion->GetBlock() < CRegion::enSPEZIALREGION || poRegion->GetBlock() == CRegion::enSCHEMEN)) { + m_nWLeft = poRegion->GetEX() + poRegion->GetEY() / 2; + m_nWRight = poRegion->GetEX() + poRegion->GetEY() / 2; + m_nWTop = poRegion->GetEY(); + m_nWBottom = poRegion->GetEY(); + m_bWMap = true; + } + m_cpoRegions.insert(RegionMap::value_type(poRegion->GetKey(), poRegion)); + m_cpoVRegions.push_back(poRegion); + if (poRegion->GetBlock() < CRegion::enSPEZIALREGION || poRegion->GetBlock() == CRegion::enSCHEMEN) { + if (poRegion->GetEY() > m_nTop) + m_nTop = poRegion->GetEY(); + if (poRegion->GetEY() < m_nBottom) + m_nBottom = poRegion->GetEY(); + if (poRegion->GetEX() + poRegion->GetEY() / 2 < m_nLeft) + m_nLeft = poRegion->GetEX() + poRegion->GetEY() / 2; + if (poRegion->GetEX() + poRegion->GetEY() / 2 > m_nRight) + m_nRight = poRegion->GetEX() + poRegion->GetEY() / 2; + + if (poRegion->GetEY() > m_nWTop) + m_nWTop = poRegion->GetEY(); + if (poRegion->GetEY() < m_nWBottom) + m_nWBottom = poRegion->GetEY(); + if (poRegion->GetEX() + poRegion->GetEY() / 2 < m_nWLeft) + m_nWLeft = poRegion->GetEX() + poRegion->GetEY() / 2; + if (poRegion->GetEX() + poRegion->GetEY() / 2 > m_nWRight) + m_nWRight = poRegion->GetEX() + poRegion->GetEY() / 2; + } + } + poRegion->SetMap(this); +} + +CRegion* CKarte::GetFromECords(int32_t nX, int32_t nY, int32_t nZ, bool bDeep) +{ + static CRegion oUnknown(std::string("")); + RegionMap::iterator i; + i = m_cpoRegions.find(CRegion::CalcKey(nX, nY, nZ)); + if (i == m_cpoRegions.end()) { + if (bDeep) { + RegionDB::iterator i2; + i2 = g_coRDB.find(CRegion::CalcKey(nX, nY, nZ)); + if (i2 != g_coRDB.end()) + return *((*i2).second.begin()); + } + return &oUnknown; + } + return (*i).second; +} + +CRegion* CKarte::GetFromDCords(int nX, int nY, int nZ, bool bDeep) +{ + static CRegion oUnknown(std::string("")); + RegionMap::iterator i; + i = m_cpoRegions.find(CalcMapKey(nX, nY, nZ)); + if (i == m_cpoRegions.end()) { + if (bDeep) { + RegionDB::iterator i2; + i2 = g_coRDB.find(CalcMapKey(nX, nY, nZ)); + if (i2 != g_coRDB.end()) + return *((*i2).second.begin()); + } + return &oUnknown; + } + return (*i).second; +} + +CRegionKey CKarte::CalcMapKey(int nX, int nY, int nZ) +{ + return CRegion::CalcKey(nX - nY / 2 - (nY > 0 && (nY & 1)), nY, nZ); +} + +CRegionKey CKarte::GetIsland(int32_t x, int32_t y, int32_t z) +{ + RegionDB::iterator ri = g_coRDB.find(CRegionKey(x, y, z)); + if (ri == g_coRDB.end()) + return CRegionKey(0x7fffffffl, 0x7fffffffl, 0x7fffffffl); + else + return (*(*ri).second.begin())->GetIsland(); +} + +int CKarte::Islandize() +{ + RegionDB::iterator i; + set coInfo; + bool bChg; + CRegionKey nMin, t; + int32_t x, y, z; + + do { + // printf( "iterating islandizer... (%d)\n", ++ic ); + bChg = false; + for (i = g_coRDB.begin(); i != g_coRDB.end(); i++) { + if ((*(*i).second.begin())->IsLand()) { + x = (*(*i).second.begin())->GetEX(); + y = (*(*i).second.begin())->GetEY(); + z = (*(*i).second.begin())->GetEZ(); + nMin = (*(*i).second.begin())->GetIsland(); + t = GetIsland(x - 1, y + 1, z); + if (t < nMin) + nMin = t; + t = GetIsland(x, y + 1, z); + if (t < nMin) + nMin = t; + t = GetIsland(x + 1, y, z); + if (t < nMin) + nMin = t; + t = GetIsland(x + 1, y - 1, z); + if (t < nMin) + nMin = t; + t = GetIsland(x, y - 1, z); + if (t < nMin) + nMin = t; + t = GetIsland(x - 1, y, z); + if (t < nMin) + nMin = t; + if ((*(*i).second.begin())->GetIsland() != nMin) { + (*(*i).second.begin())->SetIsland(nMin); + bChg = true; + } + } + } + } while (bChg); + + for (i = g_coRDB.begin(); i != g_coRDB.end(); i++) { + // printf(" %8lx ", (*i).second->GetIsland() ); + coInfo.insert((*(*i).second.begin())->GetIsland()); + } + /* + do + { + // printf( "iterating islandizer... (%d)\n", ++ic ); + bChg = false; + for( i = m_cpoRegions.begin(); i != m_cpoRegions.end(); i++ ) + { + if( (*i).second->IsLand() ) + { + nMin = (*i).second->GetIsland(); + t = (*i).second->GetNW()->GetIsland(); if( tGetNO()->GetIsland(); if( tGetO()->GetIsland(); if( tGetSO()->GetIsland(); if( tGetSW()->GetIsland(); if( tGetW()->GetIsland(); if( tGetIsland()!= nMin ) + { + (*i).second->SetIsland( nMin ); + bChg = true; + } + } + } + } + while( bChg ); + + for( i = m_cpoRegions.begin(); i != m_cpoRegions.end(); i++ ) + { + // printf(" %8lx ", (*i).second->GetIsland() ); + coInfo.insert( (*i).second->GetIsland() ); + } + */ + return (int)coInfo.size() - 1; +} + +#ifdef RUMBURAK +int CKarte::Islandize(RegionMap& oRDB) +{ + RegionMap::iterator i; + set coInfo; + bool bChg; + int nMin, t; + int ic = 0; + + do { + // printf( "iterating islandizer... (%d)\n", ++ic ); + bChg = false; + for (i = oRDB.begin(); i != oRDB.end(); i++) { + if ((*i).second->IsLand()) { + nMin = (*i).second->GetIsland(); + /* + t = (*i).second->GetNW()->GetIsland(); if( tGetNO()->GetIsland(); if( tGetO()->GetIsland(); if( tGetSO()->GetIsland(); if( tGetSW()->GetIsland(); if( tGetW()->GetIsland(); if( tGetIsland() != nMin) { + (*i).second->SetIsland(nMin); + bChg = true; + } + } + } + } while (bChg); + + for (i = m_cpoRegions.begin(); i != m_cpoRegions.end(); i++) { + // printf(" %8lx ", (*i).second->GetIsland() ); + coInfo.insert((*i).second->GetIsland()); + } + + return (int)coInfo.size() - 1; +} +#endif + +void CKarte::FillIslandQueue(IslandQueue& cpoQueue) +{ + RegionMap::iterator i; + + for (i = m_cpoRegions.begin(); i != m_cpoRegions.end(); i++) { + // printf(" %8lx ", (*i).second->GetIsland() ); + if (!(*i).second->GetIslandName().empty()) + cpoQueue.push_back((*i).second); + } +} + +void CKarte::DumpMap(const std::string& sTarget, int nCX, int nCY, int nB, int nH, const char* pcPref) +{ + int h, x, y, z; + int x1, x2, sx, sy; + char fmt[16], fmt2[16]; + char c, o; + int e; + nB >>= 1; + nH >>= 1; + if (nCY & 1) + nCX++; + + y = nCY + nH + 1; + h = (y & 1) & !(nCY & 1); + + x1 = (nCX - nB + h - ((y > 0) ? y + 1 : y) / 2); + x2 = (nCX + nB + h - ((y > 0) ? y + 1 : y) / 2); + sx = (log10((double)abs(x1)) > log10((double)abs(x2))) ? (int)log10((double)abs(x1)) : (int)log10((double)abs(x2)); + sy = (log10((double)abs(y)) > log10((double)abs(nCY - nH))) ? (int)log10((double)abs(y)) : (int)log10((double)abs(nCY - nH)); + sy++; + snprintf(fmt, sizeof(fmt), "%%+%dd ", sy + 1); + snprintf(fmt2, sizeof(fmt2), "%%%ds ", sy + 1); + + if (pcPref) + COutput::TPrintf(sTarget, "%s ", pcPref); + COutput::TPrintf(sTarget, " "); + if ((y & 1) ^ (nCY & 1)) + COutput::TPrintf(sTarget, " "); + e = ((y & 1) ^ (nCY & 1)) ? 0 : 1; + for (x = nCX - nB + h; x <= nCX + nB + h + e; x++) + COutput::TPrintf(sTarget, "%c ", (x - ((y > 0) ? y + 1 : y) / 2) < 0 ? '-' : ((x - ((y > 0) ? y + 1 : y) / 2) ? '+' : '|')); + COutput::TPrintf(sTarget, "\n"); + + for (z = (int)pow((double)10, (double)sx); z > 0; z /= 10) { + if (pcPref) + COutput::TPrintf(sTarget, "%s ", pcPref); + COutput::TPrintf(sTarget, fmt2, ""); + if ((y & 1) ^ (nCY & 1)) + COutput::TPrintf(sTarget, " "); + for (x = nCX - nB + h; x <= nCX + nB + h + e; x++) + COutput::TPrintf(sTarget, "%c ", ((abs(x - ((y > 0) ? y + 1 : y) / 2) / z) % 10) + '0'); + COutput::TPrintf(sTarget, "\n"); + } + + for (--y; y >= nCY - nH; y--) { + if (pcPref) + COutput::TPrintf(sTarget, "%s ", pcPref); + COutput::TPrintf(sTarget, fmt, y); + if ((y & 1) ^ (nCY & 1)) + COutput::TPrintf(sTarget, " "); + h = (y & 1) & !(nCY & 1); + for (x = nCX - nB + h; x <= nCX + nB + h; x++) { + c = GetFromDCords(x, y, 0, true)->GetRegionChar(); + o = ' '; // GetFromDCords( x, y, 0, false )->IsOwnUnit()?'!':' '; + if (c == '/' && (abs(x - ((y > 0) ? y + 1 : y) / 2) % 10)) + c = ' '; + if (c == ' ' && !(y % 10)) + c = '-'; + COutput::TPrintf(sTarget, "%c%c", c, o); + } + if (!((y & 1) ^ (nCY & 1))) + COutput::TPrintf(sTarget, " "); + COutput::TPrintf(sTarget, fmt, y); + COutput::TPrintf(sTarget, "\n"); + } + + h = (y & 1) & !(nCY & 1); + x1 = (nCX - nB + h - ((y > 0) ? y + 1 : y) / 2); + x2 = (nCX + nB + h - ((y > 0) ? y + 1 : y) / 2); + sx = (log10((double)abs(x1)) > log10((double)abs(x2))) ? (int)log10((double)abs(x1)) : (int)log10((double)abs(x2)); + + if (pcPref) + COutput::TPrintf(sTarget, "%s ", pcPref); + COutput::TPrintf(sTarget, " "); + if ((y & 1) ^ (nCY & 1)) + COutput::TPrintf(sTarget, " "); + e = ((y & 1) ^ (nCY & 1)) ? 0 : 1; + for (x = nCX - nB + h; x <= nCX + nB + h + e; x++) + COutput::TPrintf(sTarget, "%c ", (x - ((y > 0) ? y + 1 : y) / 2) < 0 ? '-' : ((x - ((y > 0) ? y + 1 : y) / 2) ? '+' : '|')); + COutput::TPrintf(sTarget, "\n"); + + for (z = (int)pow((double)10, (double)sx); z > 0; z /= 10) { + if (pcPref) + COutput::TPrintf(sTarget, "%s ", pcPref); + COutput::TPrintf(sTarget, fmt2, ""); + if ((y & 1) ^ (nCY & 1)) + COutput::TPrintf(sTarget, " "); + for (x = nCX - nB + h; x <= nCX + nB + h + e; x++) + COutput::TPrintf(sTarget, "%c ", ((abs(x - ((y > 0) ? y + 1 : y) / 2) / z) % 10) + '0'); + COutput::TPrintf(sTarget, "\n"); + } +} + +void CKarte::DumpFullMap(const std::string& sTarget, const char* pcPref) +{ + int nCX, nCY, nB, nH; + + nCY = (m_nTop + m_nBottom) / 2; + nCX = ((m_nLeft + (nCY - m_nBottom) / 2) + (m_nRight - (m_nTop - nCY) / 2)) / 2; + nB = m_nRight - m_nLeft; //(m_nRight-(m_nTop-nCY)/2) - (m_nLeft+(nCY-m_nBottom)/2); + nH = m_nTop - m_nBottom; + + DumpMap(sTarget, nCX, nCY, nB + 5, nH + 1, pcPref); +} + +void CKarte::DumpWorldMap(const std::string& sTarget, const char* pcPref) +{ + int nCX, nCY, nB, nH; + + nCY = (m_nWTop + m_nWBottom) / 2; + nCX = ((m_nWLeft + (nCY - m_nWBottom) / 2) + (m_nWRight - (m_nWTop - nCY) / 2)) / 2; + nB = m_nWRight - m_nWLeft; //(m_nRight-(m_nTop-nCY)/2) - (m_nLeft+(nCY-m_nBottom)/2); + nH = m_nWTop - m_nWBottom; + + DumpMap(sTarget, nCX, nCY, nB + 5, nH + 1, pcPref); +} + +///////////////////////////////////////////////////////////////////// +//.class: CReport +///////////////////////////////////////////////////////////////////// + +std::string CReport::m_sDefaultPassword; +int32_t CReport::m_nMaxRound = 0; + +CReport::CReport(const std::string& sFName) + : CBlockBase("CReport") + , m_bIsValid(false) + , m_bHasIslandTags(false) + , m_bHasSpiel(false) + , m_bUTF8(false) + , m_poMap(0) + , m_sOrgCRName(sFName) + , m_nVersion(36) + , m_sSpiel("Eressea") + , m_sKonfiguration("Standard") + , m_nENrBase(10) + , m_nPNrBase(10) + , m_nBNrBase(10) + , m_nRunde(0) + , m_nZeitalter(1) + , m_nPartei(-1) + , m_nRekrutierungskosten(0) + , m_nPersonen(0) + , m_nPunkte(-1) + , m_nPunkteschnitt(-1) + , m_nEinkommen(0) + , m_nAusgaben(0) + , m_nMsgEinkommen(0) + , m_nMsgAusgaben(0) +{ + g_sConfigFile = GetConfigFileName(); + + m_poMap = new CKarte(this); + + if (g_coTags.empty()) + InitTags(); + + m_coParteien[0] = std::string("-Monster"); + m_coParteien[-1] = std::string("-parteigetarnt"); + + if (!sFName.empty()) { + Import(sFName); + } + + m_cpoReports.push_back(this); +} + +CReport::~CReport() +{ + m_cpoReports.remove(this); + for (Handelspartner::iterator i = m_cpoHPartner.begin(); i != m_cpoHPartner.end(); i++) { + delete (*i).second; + } + m_cpoHPartner.clear(); + delete m_poMap; +} + +void CReport::Import(const std::string& sFName) +{ + CReportStream oRS(sFName); + CPartei::Ptr pPartei; + CGruppe::Ptr pGruppe; + // int nLetztePartei = -1; + // int nRunde = 0; + int nParteiPhase = 0; + int32_t nRepPartei = 0; + int32_t nIgnoredPartei = 0; + int32_t battle_x = 0, battle_y = 0, battle_z = 0; + bool inBattle = false; + bool bFirstRegion = true; + int nBlocks = 0; + + oRS.Next(); + + if (oRS.EOS()) + return; + + do { + if (oRS.GetType() == CReportStream::enBLOCK) { + if (nBlocks == 1) + AdditionalTag::Init(); + nBlocks++; + if (oRS.GetType() == CReportStream::enBLOCK && !IsEqual(oRS.GetValue(), "MESSAGE")) { + inBattle = false; + } + } + if (oRS.GetType() == CReportStream::enBLOCK && IsEqual(oRS.GetValue(), "VERSION")) { + m_nVersion = oRS.GetDat(0); + + if (m_nVersion < 20 && g_nMaxVersion + 1 < m_nVersion) { + m_sSpiel = "Empiria"; + m_bHasSpiel = true; + } + + if (m_nVersion >= 29) + g_coFlags.insert(VF_HEXMAP); + + if (m_nVersion >= 32) { + g_coFlags.insert(VF_BASE36); + m_nENrBase = 36; + } + + if (m_nVersion >= 49) { + g_coFlags.insert(VF_FULLBASE36); + m_nENrBase = 36; + m_nPNrBase = 36; + m_nBNrBase = 36; + } + + if (m_nVersion >= 57) { + g_coFlags.insert(VF_NEWERESSEASTATI); + } + + if (m_nVersion < 50 && g_nMaxVersion + 1 < m_nVersion) + g_bIsRealBuildingType = false; + + if (m_nVersion >= 59) + g_coFlags.insert(VF_RESOURCEBLOCKS); + + if (m_nVersion > g_nMaxVersion) + g_nMaxVersion = m_nVersion; + + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "charset")) { + std::string enc = oRS.GetValue(); + if (IsEqual(enc, "utf-8") || IsEqual(enc, "utf8")) { + m_bUTF8 = true; + oRS.Utf8Mode(true); + } + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "Spiel")) { + std::string::size_type p; + m_sSpiel = oRS.GetValue(); + m_bHasSpiel = true; + if (!IsEqual(m_sSpiel, "eressea")) { + g_coFlags.erase(VF_FULLBASE36); + m_nPNrBase = 10; + m_nBNrBase = 10; + } + p = g_sConfigFile.find_last_of("\\/:"); + if (p == std::string::npos) + p = 0; + + std::string sSpiel = m_sSpiel; + for (auto& c : sSpiel) + c = (char)tolower(c); + +#ifdef _WIN32 + g_sConfigFile = g_sConfigFile.substr(0, p + 1) + Flatten(sSpiel) + std::string(".cfg"); +#else + g_sConfigFile = g_sConfigFile.substr(0, p + 1) + std::string(".") + Flatten(sSpiel) + std::string("rc"); +#endif + SetConfigFileName(g_sConfigFile); + AdditionalTag::Init(); + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "noskillpoints")) { + if (oRS.GetDat(0) && !IsFlag(VF_SHOWEMULATEDDAYS)) + g_coFlags.insert(VF_NOSKILLPOINTS); + SetValue(oRS); + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "Konfiguration")) { + m_sKonfiguration = oRS.GetValue(); + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Basis")) { + m_nENrBase = oRS.GetDat(0); + if (oRS.GetDat(0) != 36) + g_coFlags.erase(VF_BASE36); + m_nENrBase = oRS.GetDat(0); + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "Koordinaten")) { + if (!IsEqual(oRS.GetValue().c_str(), "Hex")) + g_coFlags.erase(VF_HEXMAP); + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Runde")) { + m_nRunde = oRS.GetDat(0); + oRS.Next(); + if (m_nMaxRound < m_nRunde) + m_nMaxRound = m_nRunde; + if (m_nRunde >= 208 && IsEqual(m_sSpiel, "eressea")) { + g_coFlags.insert(VF_FULLBASE36); + m_nENrBase = 36; + m_nPNrBase = 36; + m_nBNrBase = 36; + } + } + else if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Zeitalter")) { + m_nZeitalter = oRS.GetDat(0); + oRS.Next(); + } + // else if( oRS.GetType()==CReportStream::enBLOCK && IsEqual( oRS.GetValue(), "PARTEI" ) && m_nPartei<0 ) + // { + // m_nPartei = oRS.GetDat(0); oRS.Next(); + // } + else if (oRS.GetType() == CReportStream::enBLOCK && IsEqual(oRS.GetValue(), "OPTIONEN")) { + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) { + if (oRS.GetType() == CReportStream::enINTEGER && oRS.GetDat(0)) { + m_csOptionen.push_back(std::string("+") + oRS.GetComment()); + } + else { + m_csOptionen.push_back(std::string("-") + oRS.GetComment()); + } + oRS.Next(); + } + } + else if (oRS.GetType() == CReportStream::enBLOCK && + (oRS.GetValue() == "MELDUNGEN" || oRS.GetValue() == "EREIGNISSE" || oRS.GetValue() == "EINKOMMEN" || oRS.GetValue() == "HANDEL" || oRS.GetValue() == "PRODUKTION" || oRS.GetValue() == "BEWEGUNGEN")) { + std::string sMsg; + int enMT = MT_UNKNOWN; + + if (oRS.GetValue() == "EREIGNISSE") + enMT = MT_EREIGNISSE; + + if (oRS.GetValue() == "HANDEL") + enMT = MT_HANDEL; + + if (oRS.GetValue() == "EINKOMMEN") + enMT = MT_EINKOMMEN; + + oRS.Next(); + while (!oRS.EOS()) { + if (oRS.GetType() == CReportStream::enSTRING) { + sMsg = oRS.GetValue(); + if (!sMsg.empty() && sMsg[sMsg.size() - 1] != '.' && sMsg[sMsg.size() - 1] != '!') { + oRS.Next(); + if (oRS.GetType() == CReportStream::enSTRING) { + sMsg += ' '; + sMsg += oRS.GetValue(); + } + } + m_csNachrichten.push_back(Nachricht(enMT, sMsg)); + if (oRS.GetType() != CReportStream::enSTRING) + break; + } + else + break; + oRS.Next(); + } + } + else if (oRS.GetType() == CReportStream::enBLOCK && oRS.GetValue() == "MESSAGE") { + std::string sMsg; + bool bRegion = false; + int nType = 0; + int nFrom = 0; + // int nTo = 0; + int nAmount = 0; + int32_t x = 0, y = 0, z = 0; + int32_t nUnit = -1; + int nBuilding = -1; + bool bDrop = false; + CMessage::Ptr pMsg(new CMessage(oRS, m_nRunde)); + if (inBattle && pMsg->GetValue("region", Value("")).asString().empty()) { + pMsg->SetValue("region", Value(battle_x)); + pMsg->SetValue("region:1", Value(battle_y)); + pMsg->SetValue("region:2", Value(battle_z)); + } + if (m_nRunde < 227) { + switch (pMsg->GetValue("type").asLong()) { + case 9386: + nFrom = pMsg->GetValue("from").asLong(); + // nTo = pMsg->GetValue( "to" ).asLong(); + nAmount = pMsg->GetValue("amount").asLong(); + break; + case 2097: + nFrom = -1; // nTo = Partei(); + nAmount = pMsg->GetValue("amount").asLong(); + break; + case 24543: + nFrom = -1; // nTo = Partei(); + nAmount = -pMsg->GetValue("cost").asLong(); + break; + case 364: + nFrom = -1; // nTo = Partei(); + nAmount = -pMsg->GetValue("money").asLong(); + } + } + else if (m_nRunde == 227) { + switch (pMsg->GetValue("type").asLong()) { + case 1682429624: + nFrom = pMsg->GetValue("from").asLong(); + // nTo = pMsg->GetValue( "to" ).asLong(); + nAmount = pMsg->GetValue("amount").asLong(); + break; + case -1376149197: + nFrom = -1; // nTo = Partei(); + nAmount = pMsg->GetValue("amount").asLong(); + break; + case 443066738: + nFrom = -1; // nTo = Partei(); + nAmount = -pMsg->GetValue("cost").asLong(); + break; + case 170076: + nFrom = -1; // nTo = Partei(); + nAmount = -pMsg->GetValue("money").asLong(); + } + } + else if (m_nRunde >= 227) { + switch (pMsg->GetValue("type").asLong()) { + case 1682429624L: + nFrom = pMsg->GetValue("from").asLong(); + // nTo = pMsg->GetValue( "to" ).asLong(); + nAmount = pMsg->GetValue("amount").asLong(); + break; + case 771334452L: + nFrom = -1; // nTo = Partei(); + nAmount = pMsg->GetValue("amount").asLong(); + break; + case 443066738L: + nFrom = -1; // nTo = Partei(); + nAmount = -pMsg->GetValue("cost").asLong(); + break; + case 170076L: + nFrom = -1; // nTo = Partei(); + nAmount = -pMsg->GetValue("money").asLong(); + } + } + + nUnit = pMsg->GetValue("unit").asLong(); + nBuilding = pMsg->GetValue("building").asLong(); + + Value oVRegion = pMsg->GetValue("region", Value()); + if (oVRegion.getType() == VT_STRING) { + std::string sReg = pMsg->GetValue("region").asString(); + if (!sReg.empty()) { + x = atoi(sReg.c_str()); + auto p = sReg.find(','); + if (p != std::string::npos) { + y = atoi(sReg.c_str() + p + 1); + p = sReg.find(',', p + 1); + if (p != std::string::npos) { + z = atoi(sReg.c_str() + p + 1); + } + else + z = 0; + bRegion = true; + } + } + } + else if (oVRegion.getType() == VT_INT) { + x = oVRegion.asLong(); + y = pMsg->GetValue("region:1").asLong(); + z = pMsg->GetValue("region:2").asLong(); + bRegion = true; + } + m_cpoMessages.insert(std::make_pair(m_cpoMessages.size(), pMsg)); + if (!bDrop && (nAmount || (nBuilding > 0 && ((m_nRunde < 227 && nType == 7835) || (m_nRunde == 227 && nType == -1376149197L) || (m_nRunde > 227 && nType == 761324692L))))) { + if (nFrom == Partei()) + nAmount = -nAmount; + if (nAmount < 0) { + m_nMsgAusgaben -= nAmount; + } + else { + m_nMsgEinkommen += nAmount; + } + if (!bRegion) + z = -1; + m_coTradeInfos.push_back(CTradeInfo(nUnit, nBuilding, x, y, z, nAmount)); + } + } + else if (oRS.GetType() == CReportStream::enBLOCK && (oRS.GetValue() == "ADRESSEN" || oRS.GetValue() == "ALLIIERTE")) { + std::string sParteiname; + int32_t nP = -1; + const char* ac = "-"; + + CPartei::Ptr pP; + + if (oRS.GetValue() == "ALLIIERTE") { + ac = "+"; + } + + oRS.Next(); + while (!oRS.EOS() && oRS.GetType() != CReportStream::enBLOCK) { + if (pP.get()) + pP->SetValue(oRS); + + if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Partei")) { + ParteiInfos::iterator pi = m_cpoLocalParteiInfos.find(oRS.GetDat(0)); + if (pi == m_cpoLocalParteiInfos.end()) { + pP.reset(new CPartei(oRS.GetDat(0))); + pP->SetValue("Nummer", Value((int32_t)oRS.GetDat(0))); + m_cpoLocalParteiInfos.insert(ParteiInfos::value_type(oRS.GetDat(0), pP)); + } + else + pP = (*pi).second; + + nP = oRS.GetDat(0); + oRS.Next(); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "Parteiname")) { + sParteiname = oRS.GetValue(); + oRS.Next(); + } + else { + oRS.Next(); + } + if (nP >= 0 && !sParteiname.empty()) { + if (m_coParteien[nP].empty() || m_coParteien[nP][0] == '-') + m_coParteien[nP] = std::string(ac) + sParteiname; + nP = -1; + sParteiname = ""; + } + } + } + else if (oRS.GetType() == CReportStream::enBLOCK && (oRS.GetValue() == "ALLIANZ" || oRS.GetValue() == "PARTEI")) { + // std::string sPass; + // std::string sPName; + int32_t nRekrutierungskosten = 0; + int32_t nPersonen = 0; + int32_t nPunkte = 0; + int32_t nPunkteschnitt = 0; + int32_t nRunde = m_nRunde; + int32_t nPNum = 0; + std::string sParteiname; + std::string sPasswort; + int32_t nP = -1; + const char* ac = "-"; + bool bPartei = false; + CPartei::Ptr pP; + + ParteiInfos::iterator pi = m_cpoLocalParteiInfos.find(oRS.GetDat(0)); + if (pi == m_cpoLocalParteiInfos.end()) { + nPNum = oRS.GetDat(0); + pP.reset(new CPartei(nPNum)); + pP->SetValue("Nummer", Value(nPNum)); + pP->SetValue("Runde", Value(m_nRunde)); + m_cpoLocalParteiInfos.insert(ParteiInfos::value_type(oRS.GetDat(0), pP)); + } + else + pP = (*pi).second; + + ParteiInfos::iterator pig = g_cpoParteiInfos.find(oRS.GetDat(0)); + if (pig == g_cpoParteiInfos.end()) { + g_cpoParteiInfos.insert(ParteiInfos::value_type(oRS.GetDat(0), pP)); + } + else { + if (pig->second->GetValue("Runde") < m_nRunde) { + pig->second = pP; + } + } + + if (oRS.GetValue() == "ALLIANZ") { + ac = "+"; + nP = oRS.GetDat(0); + } + else if (oRS.GetValue() == "PARTEI") { + pGruppe.reset(); + pPartei = pP; + nP = oRS.GetDat(0); + if (nParteiPhase == 1) + nParteiPhase = 2; + bPartei = true; + // nLetztePartei = oRS.GetDat(0); + } + + oRS.Next(); + while (!oRS.EOS() && oRS.GetType() != CReportStream::enBLOCK) { + if (pP.get() && pPartei.get()) + pP->SetValue(oRS); + if (m_nRunde >= m_nMaxRound && oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "status")) { + if (pPartei.get()) { + ((CPartei*)pPartei.get())->SetAllianz(nP, oRS.GetDat(0)); + } + else if (pGruppe.get()) { + ((CGruppe*)pGruppe.get())->SetAllianz(nP, oRS.GetDat(0)); + } + } + if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Rekrutierungskosten")) { + nRekrutierungskosten = oRS.GetDat(0); + } + else if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Anzahl Personen")) { + nPersonen = oRS.GetDat(0); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "Passwort")) { + ac = "+"; + sPasswort = oRS.GetValue(); + if (!nRepPartei) { + nRepPartei = nPNum; + nParteiPhase = 1; + } + else { + if (nPNum != nRepPartei) { + if (nPNum != nIgnoredPartei) { + ERRMSG(0, ("Line %d, Warnung: Mehr als eine authorisierte Partei! Ignoriere %s", oRS.GetLine(), itoan(nP, PNrBase()))); + nIgnoredPartei = nPNum; + } + nParteiPhase++; + } + } + } + else if (m_nPunkte <= 0 && oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Punkte")) { + nPunkte = oRS.GetDat(0); + if (!nRepPartei) { + nRepPartei = nPNum; + nParteiPhase = 1; + } + else { + if (nPNum != nRepPartei) { + if (nPNum != nIgnoredPartei) { + ERRMSG(0, ("Line %d, Warnung: Mehr als eine authorisierte Partei! Ignoriere %s", oRS.GetLine(), itoan(nP, PNrBase()))); + nIgnoredPartei = nPNum; + } + nParteiPhase++; + } + } + } + else if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Punktedurchschnitt")) { + nPunkteschnitt = oRS.GetDat(0); + if (!nRepPartei) { + nRepPartei = nPNum; + nParteiPhase = 1; + } + else { + if (nPNum != nRepPartei) { + if (nPNum != nIgnoredPartei) { + ERRMSG(0, ("Line %d, Warnung: Mehr als eine authorisierte Partei! Ignoriere %s", oRS.GetLine(), itoan(nP, PNrBase()))); + nIgnoredPartei = nPNum; + } + nParteiPhase++; + } + } + } + else if (oRS.GetType() == CReportStream::enINTEGER && IsEqual(oRS.GetComment(), "Runde")) { + nRunde = m_nRunde ? m_nRunde : oRS.GetDat(0); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "Parteiname")) { + sParteiname = oRS.GetValue(); + } + else if (oRS.GetType() == CReportStream::enSTRING && IsEqual(oRS.GetComment(), "Magiegebiet")) { + if (!nRepPartei) { + nRepPartei = nPNum; + nParteiPhase = 1; + } + else { + if (nPNum != nRepPartei) { + if (nPNum != nIgnoredPartei) { + // ERRMSG( 0, ( "Line %d, Warnung: Mehr als eine authorisierte Partei! Ignoriere %s", oRS.GetLine(), itoan(nP,PNrBase()) )); + nIgnoredPartei = nPNum; + } + nParteiPhase++; + } + } + } + oRS.Next(); + } + if (bPartei && nParteiPhase == 1) { + m_nPartei = nP; + m_sParteiname = sParteiname; + m_nRekrutierungskosten = nRekrutierungskosten; + m_nPersonen = nPersonen; + m_sPasswort = sPasswort; + m_nPunkte = nPunkte; + m_nPunkteschnitt = nPunkteschnitt; + m_nRunde = nRunde; + } + if (nP >= 0 && !sParteiname.empty()) { + if (m_coParteien[nP].empty() || m_coParteien[nP][0] == '-') + m_coParteien[nP] = std::string(ac) + sParteiname; + nP = -1; + } + } + else if (oRS.GetType() == CReportStream::enBLOCK && oRS.GetValue() == "GRUPPE") { + Gruppen::iterator gi = m_cpoGruppen.find(oRS.GetDat(0)); + CGruppe::Ptr pG; + int32_t nID = oRS.GetDat(0); + if (gi == m_cpoGruppen.end()) { + pG.reset(new CPartei(nID)); + pG->SetValue("Nummer", Value(nID)); + m_cpoGruppen.insert(Gruppen::value_type(nID, pG)); + } + else + pG = (*gi).second; + + pPartei.reset(); + pGruppe = pG; + + oRS.Next(); + while (!oRS.EOS() && oRS.GetType() != CReportStream::enBLOCK) { + if (pG.get()) + pG->SetValue(oRS); + oRS.Next(); + } + } + else if (oRS.GetType() == CReportStream::enBLOCK && (oRS.GetValue() == "REGION" || oRS.GetValue() == "DURCHREISEREGION" || oRS.GetValue() == "SPEZIALREGION")) { + if (bFirstRegion) { + CMessage::Ptr pMsg(new CMessage(m_nRunde)); + pMsg->SetValue("type", Value(-2)); + m_cpoMessages[m_cpoMessages.size()] = pMsg; + bFirstRegion = false; + } + + m_poMap->Import(oRS, m_nRunde); + } + else if (oRS.GetValue() == "BATTLE") { + inBattle = true; + battle_x = oRS.GetDat(0); + battle_y = oRS.GetDat(1); + battle_z = oRS.GetDat(2); + CMessage::Ptr pMsg(new CMessage(m_nRunde)); + pMsg->SetValue("region", Value(battle_x)); + pMsg->SetValue("region:1", Value(battle_y)); + pMsg->SetValue("region:2", Value(battle_z)); + pMsg->SetValue("type", Value(-1)); + m_cpoMessages[m_cpoMessages.size()] = pMsg; + oRS.Next(); + while (!oRS.EOS() && (oRS.GetType() != CReportStream::enBLOCK || oRS.GetValue() != "MESSAGE")) + oRS.Next(); + } + else if (oRS.GetValue() == "ZAUBER" || oRS.GetValue() == "TRAENKE") { + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) + oRS.Next(); + } + else if (oRS.GetValue() == "BATTLESPEC") { + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) + oRS.Next(); + } + else { + if (oRS.GetType() != CReportStream::enBLOCK) { + SetValue(oRS); + } + else { + if (CHierarchy::IsChild("VERSION", oRS.GetValue())) { + LoadSubObject(oRS); + } + } + oRS.Next(); + } + } while (!oRS.EOS()); + + CRegion::Einheiten::iterator ui; + int32_t en1, en2, en3, en4; + std::string::size_type pos; + bool bUsed; + + if (MessageRenderer() == CMessage::ERESSEA2) { + SetMessageRule(-1, "\"In $region($region) fand ein Kampf statt.\""); + SetMessageRule(-2, "\"Ende der globalen Nachrichten.\""); + } + else { + SetMessageRule(-1, "In {region} fand ein Kampf statt."); + SetMessageRule(-2, "Ende der globalen Nachrichten."); + } + SetMessageSection(-1, "battle"); + SetMessageSection(-2, "dummy"); + + for (Messages::const_iterator mi = m_cpoMessages.begin(); mi != m_cpoMessages.end(); mi++) { + bUsed = false; + // TRACEMSG(( "%s\n", ((CMessage*)((*mi).second.get()))->Render( this ).c_str() )); + en1 = (*mi).second->GetValue("unit", Value("")).asLong(); + en2 = (*mi).second->GetValue("target", Value("")).asLong(); + en3 = (*mi).second->GetValue("teacher", Value("")).asLong(); + en4 = (*mi).second->GetValue("student", Value("")).asLong(); + if (en1) { + ui = m_cpoGEinheiten.find(en1); + if (ui != m_cpoGEinheiten.end() && (*ui).second->Partei() == Partei()) { + (*ui).second->AddMessage((*mi).second); + bUsed = true; + } + } + if (en2) { + ui = m_cpoGEinheiten.find(en2); + if (ui != m_cpoGEinheiten.end() && (*ui).second->Partei() == Partei()) { + (*ui).second->AddMessage((*mi).second); + bUsed = true; + } + } + if (en3) { + ui = m_cpoGEinheiten.find(en3); + if (ui != m_cpoGEinheiten.end() && (*ui).second->Partei() == Partei()) { + (*ui).second->AddMessage((*mi).second); + bUsed = true; + } + } + if (en4) { + ui = m_cpoGEinheiten.find(en4); + if (ui != m_cpoGEinheiten.end() && (*ui).second->Partei() == Partei()) { + (*ui).second->AddMessage((*mi).second); + bUsed = true; + } + } + if (!bUsed) { + if (!(*mi).second->GetValue("region", Value("")).asString().empty()) { + int32_t x, y, z; + ((CMessage*)((*mi).second.get()))->GetCoords(/*(*mi).second->GetValue(*/ "region" /*, Value( "" ) ).asString()*/, x, y, z); + CRegion* pReg = GetMap()->GetFromECords(x, y, z); + if (pReg) { + pReg->AddMessage((*mi).second); + bUsed = true; + } + } + if (!(*mi).second->GetValue("start", Value("")).asString().empty()) { + int32_t x, y, z; + ((CMessage*)((*mi).second.get()))->GetCoords(/*(*mi).second->GetValue(*/ "start" /*, Value( "" ) ).asString()*/, x, y, z); + CRegion* pReg = GetMap()->GetFromECords(x, y, z); + if (pReg) { + pReg->AddMessage((*mi).second); + bUsed = true; + } + } + if (!(*mi).second->GetValue("end", Value("")).asString().empty()) { + int32_t x, y, z; + ((CMessage*)((*mi).second.get()))->GetCoords(/*(*mi).second->GetValue( */ "end" /*, Value( "" ) ).asString()*/, x, y, z); + CRegion* pReg = GetMap()->GetFromECords(x, y, z); + if (pReg) { + pReg->AddMessage((*mi).second); + bUsed = true; + } + } + } + if (bUsed) { + (*mi).second->SetValue("_used", Value(1)); + } + } + + for (Nachrichten::const_iterator ni = m_csNachrichten.begin(); ni != m_csNachrichten.end(); ni++) { + // switch( (*ni).first ) + // { + // case MT_HANDEL: StatistikHandel( (*ni).second ); break; + // } + + pos = 0; + bUsed = false; + en1 = FindNextENum((*ni).second, pos); + if (en1 > 0) { + ui = m_cpoGEinheiten.find(en1); + if (ui != m_cpoGEinheiten.end() && (*ui).second->Partei() == Partei()) { + (*ui).second->AddMessage((*ni).second); + bUsed = true; + } + + en2 = FindNextENum((*ni).second, pos); + if (en2 > 0 && en2 != en1) { + ui = m_cpoGEinheiten.find(en2); + if (ui != m_cpoGEinheiten.end() && (*ui).second->Partei() == Partei()) { + (*ui).second->AddMessage((*ni).second); + bUsed = true; + } + } + } + + if (!bUsed) { + int x, y, z; + pos = 0; + if (FindNextRegion((*ni).second, pos, x, y, z)) { + CRegion* pReg = GetMap()->GetFromECords(x, y, z); + if (pReg) + pReg->AddMessage((*ni).second); + } + } + } + + CConfigFile oCF(g_sConfigFile); + std::string sTxt; + size_t i = 1; + while (oCF.FetchLine("Options", i++, false)) { + sTxt = oCF.GetString(0); + if (CRegExp::Match(sTxt, "EBase\\s*=\\s*")) { + CRegExp::Replace(sTxt, "EBase\\s*=\\s*", ""); + int b = atoi(sTxt.c_str()); + if (b >= 2 && b <= 36) + m_nENrBase = b; + } + else if (CRegExp::Match(sTxt, "PBase\\s*=\\s*")) { + CRegExp::Replace(sTxt, "PBase\\s*=\\s*", ""); + int b = atoi(sTxt.c_str()); + if (b >= 2 && b <= 36) + m_nPNrBase = b; + } + else if (CRegExp::Match(sTxt, "BBase\\s*=\\s*")) { + CRegExp::Replace(sTxt, "BBase\\s*=\\s*", ""); + int b = atoi(sTxt.c_str()); + if (b >= 2 && b <= 36) + m_nBNrBase = b; + } + else if (m_sPasswort.empty() && m_sDefaultPassword.empty() && CRegExp::Match(sTxt, std::string("Passwor[dt](") + itoan(m_nPartei, m_nPNrBase) + ")?\\s*=\\s*")) { + CRegExp::Replace(sTxt, std::string("Passwor[dt](") + itoan(m_nPartei, m_nPNrBase) + ")?\\s*=\\s*", ""); + if (sTxt.length() > 1 && sTxt[0] == 34) + sTxt = sTxt.substr(1, sTxt.length() - 2); + m_sPasswort = sTxt; + } + } + + if (m_sPasswort.empty()) { + m_sPasswort = m_sDefaultPassword; + } + + m_bIsValid = true; +} + +int32_t CReport::GetGroupIdByName(const std::string& sName) +{ + Gruppen::iterator gi = m_cpoGruppen.begin(); + while (gi != m_cpoGruppen.end()) { + if (CRegExp::Match((*gi).second->GetValue("name").asString(), sName)) { + return (*gi).first; + } + gi++; + } + return -1; +} + +void CReport::Write(const std::string& sFName) +{ + CReportStream oRS(sFName, false); + + oRS.WriteBlock("VERSION", "Version des Computer Reports", 10029); + oRS.WriteLine("Standard", "Konfiguration"); + m_poMap->Write(oRS); +} + +Value CReport::GetValue(const std::string& sKey) +{ + if (IsEqual(sKey.c_str(), "Runde")) + return Value(int32_t(m_nRunde)); + if (IsEqual(sKey.c_str(), "Partei")) + return Value(itoan(m_nPartei, PNrBase())); + if (IsEqual(sKey.c_str(), "Rekrutierungskosten")) + return Value(int32_t(m_nRekrutierungskosten)); + if (IsEqual(sKey.c_str(), "Personen")) + return Value(int32_t(m_nPersonen)); + if (IsEqual(sKey.c_str(), "Spiel")) + return Value(m_sSpiel); + return CBlockBase::GetValue(sKey, Value(0)); +} + +CEinheit* CReport::SearchUnit(int32_t nENr, bool bDeep) +{ + CRegion::Einheiten::iterator ui; + Reports::iterator ri; + ui = m_cpoGEinheiten.find(nENr); + if (ui != m_cpoGEinheiten.end()) + return (*ui).second; + else { + if (!bDeep) + return 0; + for (ri = m_cpoReports.begin(); ri != m_cpoReports.end(); ri++) { + // if( (*ri)->Runde() == Runde() ) + // { + ui = (*ri)->m_cpoGEinheiten.find(nENr); + if (ui != (*ri)->m_cpoGEinheiten.end()) + return (*ui).second; + // } + } + } + return 0; +} + +int32_t CReport::PNrFromENr(int32_t nENr) +{ + CEinheit* pE; + + pE = SearchUnit(nENr); + if (pE) { + return pE->Partei(); + } + return -1; +} + +void CReport::CalculateStatistics() +{ + CRegion* pReg; + CEinheit* pE; + int32_t nP1, nP2; + + m_nEinkommen = m_nMsgEinkommen; + m_nAusgaben = m_nMsgAusgaben; + + for (Messages::const_iterator mi = m_cpoMessages.begin(); mi != m_cpoMessages.end(); mi++) { + if (m_nRunde < 227) { + switch ((*mi).second->GetValue("type").asLong()) { + case 581: + nP1 = PNrFromENr((*mi).second->GetValue("unit").asLong()); + nP2 = PNrFromENr((*mi).second->GetValue("target").asLong()); + if (nP1 != nP2) { + if (nP1 == m_nPartei) + InsertHandel(nP2, -(*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource").asString()); + else + InsertHandel(nP1, (*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource").asString()); + } + break; + case 9386: + nP1 = (*mi).second->GetValue("from").asLong(); + nP2 = (*mi).second->GetValue("to").asLong(); + if (nP1 != nP2) { + if (nP1 == m_nPartei) + InsertHandel(nP2, -(*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource", Value("Silber")).asString()); + else + InsertHandel(nP1, (*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource", Value("Silber")).asString()); + } + break; + } + } + else if (m_nRunde >= 227) { + switch ((*mi).second->GetValue("type").asLong()) { + case 5281483: + case 1235024123: + nP1 = PNrFromENr((*mi).second->GetValue("unit").asLong()); + nP2 = PNrFromENr((*mi).second->GetValue("target").asLong()); + if (nP1 != nP2) { + if (nP1 == m_nPartei) + InsertHandel(nP2, -(*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource").asString()); + else + InsertHandel(nP1, (*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource").asString()); + } + break; + case 1682429624: + nP1 = (*mi).second->GetValue("from").asLong(); + nP2 = (*mi).second->GetValue("to").asLong(); + if (nP1 != nP2) { + if (nP1 == m_nPartei) + InsertHandel(nP2, -(*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource", Value("Silber")).asString()); + else + InsertHandel(nP1, (*mi).second->GetValue("amount").asLong(), (*mi).second->GetValue("resource", Value("Silber")).asString()); + } + break; + } + } + } + + for (Nachrichten::const_iterator ni = m_csNachrichten.begin(); ni != m_csNachrichten.end(); ni++) { + switch ((*ni).first) { + case MT_EREIGNISSE: + StatistikEreignisse((*ni).second); + break; + case MT_HANDEL: + StatistikHandel((*ni).second); + break; + case MT_EINKOMMEN: + StatistikEinkommen((*ni).second); + break; + } + } + + int nSilber = 0; + + for (TradeInfos::const_iterator ti = m_coTradeInfos.begin(); ti != m_coTradeInfos.end(); ti++) { + pReg = nullptr; + if ((*ti).m_nZ != -1) { + pReg = GetMap()->GetFromECords((*ti).m_nX, (*ti).m_nY, (*ti).m_nZ); + } + else if ((*ti).m_nUnit > 0) { + pE = SearchUnit((*ti).m_nUnit); + if (pE) { + pReg = (CRegion*)pE->Region(); + } + } + + nSilber = (*ti).m_nSilber; + if ((*ti).m_nBuilding > 0 && pReg) { + CBauwerk* pB = pReg->GetBuilding((*ti).m_nBuilding); + if (pB) + nSilber = -(pB->Unterhalt()); + } + + if (pReg) { + if (nSilber > 0) + pReg->AddEinkommen(nSilber); + else + pReg->AddAusgaben(-nSilber); + } + } + + if (ExistUserFunction("CalcRegionIncome") || ExistUserFunction("CalcRegionExpenses")) { + Value oVErg; + ArgumentList coArgs; + for (CKarte::RegionMap::const_iterator mi = m_poMap->Regions().begin(); mi != m_poMap->Regions().end(); mi++) { + g_poCurrentRegion = (*mi).second; + if (DoUserFunction(std::string("CalcRegionIncome"), coArgs, &oVErg)) { + g_poCurrentRegion->SetEinkommen(oVErg.asLong()); + } + if (DoUserFunction(std::string("CalcRegionExpenses"), coArgs, &oVErg)) { + g_poCurrentRegion->SetAusgaben(oVErg.asLong()); + } + } + } +} + +void CReport::StatistikEreignisse(const std::string& sMsg) +{ + CEinheit* pE; + CRegion* pReg = 0; + char* pStr; + int32_t e1; + std::string::size_type pos, pos2 = 0; + // int32_t p1 = -1; + int32_t cnt; + + pos = sMsg.find(") bezahlt "); + if (pos == std::string::npos) { + // StatistikHandel( sMsg ); + return; + } + else + pos += 10; + + e1 = FindNextENum(sMsg, pos2); + if (e1 > 0) { + pE = SearchUnit(e1); + if (pE) { + // p1 = pE->Partei(); + pReg = (CRegion*)pE->Region(); + } + } + + if (sMsg[pos] == '$') + pos++; + + cnt = (int32_t)strtol(sMsg.c_str() + pos, &pStr, 10); + if (!*pStr) + return; + + m_nAusgaben += cnt; + if (pReg) + pReg->AddAusgaben(cnt); +} + +void CReport::InsertHandel(int32_t nPNr, int32_t nAmount, const std::string& sProduct) +{ + Handelspartner::iterator hi; + CParteihandel::Produkte::iterator pi; + hi = m_cpoHPartner.find(nPNr); + if (hi == m_cpoHPartner.end()) { + hi = m_cpoHPartner.insert(Handelspartner::value_type(nPNr, new CParteihandel)).first; + } + pi = (*hi).second->m_coProdukte.find(sProduct); + if (pi == (*hi).second->m_coProdukte.end()) { + (*hi).second->m_coProdukte.insert(CParteihandel::Produkte::value_type(sProduct, nAmount)); + } + else { + (*pi).second += nAmount; + } +} + +void CReport::StatistikHandel(const std::string& sMsg) +{ + CEinheit* pE; + CRegion* pReg = 0; + char* pStr; + int32_t e1 = -1, e2 = -1; + std::string::size_type pos = 0, pos2; + int32_t p1 = -1, p2 = -1, ph; + int32_t cnt; + bool bKauf = false; + + if (sMsg.empty() || sMsg[0] == 'F') + return; + + e1 = FindNextENum(sMsg, pos); + if (e1 > 0) { + pE = SearchUnit(e1, true); + if (pE) { + p1 = pE->Partei(); + pReg = (CRegion*)pE->Region(); + } + + e2 = FindNextENum(sMsg, pos); + if (e2 > 0) { + pE = SearchUnit(e2, true); + if (pE) + p2 = pE->Partei(); + } + } + + if (p1 == p2) { + return; + } + + pos = sMsg.find(") gibt "); + if (pos == std::string::npos) { + pos = sMsg.find(") zahlte "); + if (pos == std::string::npos) { + pos = sMsg.find(") kauft f\xFCr "); + if (pos == std::string::npos) { + pos = sMsg.find(") kauft fuer "); + if (pos == std::string::npos) { + return; + } + else { + pos += 13; + bKauf = true; + } + } + else { + pos += 12; + bKauf = true; + } + } + else + pos += 9; + } + else + pos += 7; + + cnt = (int32_t)strtol(sMsg.c_str() + pos, &pStr, 10); + if (!*pStr) + return; + + if (bKauf) { + if (pReg) { + pReg->AddAusgaben(cnt); + } + m_nAusgaben += cnt; + return; + } + + pos = (size_t)(pStr - sMsg.c_str() + 1); + pos2 = sMsg.find(" an ", pos); + if (pos2 == std::string::npos) + return; + + if (p1 == m_nPartei) { + cnt = -cnt; + ph = p2; + } + else { + ph = p1; + } + + InsertHandel(ph, cnt, sMsg.substr(pos, pos2 - pos)); + + /* + hi = m_cpoHPartner.find( ph ); + if( hi == m_cpoHPartner.end() ) + { + hi = m_cpoHPartner.insert( Handelspartner::value_type( ph, new CParteihandel ) ).first; + } + pi = (*hi).second->m_coProdukte.find( sMsg.substr( pos, pos2-pos ) ); + if( pi == (*hi).second->m_coProdukte.end() ) + { + (*hi).second->m_coProdukte.insert( CParteihandel::Produkte::value_type( sMsg.substr( pos, pos2-pos ), cnt ) ); + } + else + { + (*pi).second += cnt; + } + */ + // printf( " P%d -> P%d: %d %s\n", p1, p2, cnt, sMsg.substr( pos, pos2-pos ).c_str() ); +} + +void CReport::StatistikProduktion(const std::string& sMsg) {} + +void CReport::StatistikEinkommen(const std::string& sMsg) +{ + CEinheit* pE; + CRegion* pReg = 0; + char* pStr; + int32_t e1 = -1; + std::string::size_type pos = 0, pos2 = 0; + // int32_t p1 = -1; + int32_t cnt; + + pos = sMsg.find(") verdient "); + if (pos == std::string::npos) { + pos = sMsg.find(") treibt "); + if (pos == std::string::npos) + return; + else + pos += 9; + } + else + pos += 11; + + e1 = FindNextENum(sMsg, pos2); + if (e1 > 0) { + pE = SearchUnit(e1); + if (pE) { + // p1 = pE->Partei(); + pReg = (CRegion*)pE->Region(); + } + } + + if (sMsg[pos] == '$') + pos++; + + if (!isdigit(sMsg[pos])) { + pos = sMsg.find("Silber"); + if (pos != std::string::npos) { + pos--; + while (pos > 1 && isdigit(sMsg[pos - 1])) + pos--; + } + } + + cnt = (int32_t)strtol(sMsg.c_str() + pos, &pStr, 10); + if (!*pStr) + return; + + m_nEinkommen += cnt; + if (pReg) + pReg->AddEinkommen(cnt); +} + +///////////////////////////////////////////////////////////////////// +//.class: CRegionSorter +///////////////////////////////////////////////////////////////////// + +bool CRegionSorter::operator()(CRegion* pR1, CRegion* pR2) const +{ + static int32_t noName = CStringDB::Str2SID(""); + if (m_nFlags) { + if (!(pR1->m_idInsel == noName || pR1->m_idInsel == noName) && pR1->GetSortIsland() == pR2->GetSortIsland()) { + if (pR1->m_idInsel == pR2->m_idInsel) + return pR1->m_nPos < pR2->m_nPos; + else + return CStringDB::SID2Str(pR1->m_idInsel) < CStringDB::SID2Str(pR2->m_idInsel); + } + else + return pR1->GetSortIsland() < pR2->GetSortIsland(); + } + else + return pR1->m_nPos < pR2->m_nPos; +} + +bool rqcmp::operator()(const CRegion* pR1, const CRegion* pR2) const +{ + return pR1->GetQuality() > pR2->GetQuality(); +} + +///////////////////////////////////////////////////////////////////// +//.class: CRegion +///////////////////////////////////////////////////////////////////// + +const char* g_pcLuxusName[] = {"Balsam", "Gewuerz", "Juwel", "Myrrhe", "Oel", "Seide", "Weihrauch"}; + +CRegion::TerrainTypes CRegion::m_coTerrains; +CRegion::ResourceImpacts CRegion::m_coRImpacts; +int32_t CRegion::m_nCurrentPlayer = -2; +int32_t CRegion::m_nCurrentRound = -2; +int32_t CRegion::m_nMoveX = 0; +int32_t CRegion::m_nMoveY = 0; + +CRegion::CRegion(const std::string& sType, int32_t nX, int32_t nY, int32_t nZ, int nRunde) + : CBlockBase("CRegion") + , m_poMap(NULL) + , m_nInsel(0x7fffffffl, 0x7fffffffl, 0x7fffffffl) + , m_idInsel(CStringDB::Str2SID("")) + , m_enBlock(enUNKNOWN) + , m_nRunde(nRunde) + , m_nPos(-1) + , m_nX(nX) + , m_nY(nY) + , m_nZ(nZ) + , m_nPartei(0) + , m_bVerorkt(false) + , m_poTerrain(0) + , m_nBauern(-1) + , m_nPferde(0) + , m_nBaeume(0) + , m_nMallorn(0) + , m_nEisen(-2) + , m_nLaen(-2) + , m_nSilber(-1) + , m_nUnterhalt(-1) + , m_nRekruten(-1) + , m_nLohn(-1) + , m_nStrasse(-1) + , m_nVerkauf(-1) + , m_nMaxBurg(0) + , m_bOwnUnit(false) + , m_nEinkommen(0) + , m_nAusgaben(0) +{ + m_poTerrain = FindTerrain(sType); + + if (m_poTerrain->m_bLand) + m_nInsel = GetKey(); + else { + m_nBauern = 0; + m_nBaeume = 0; + m_nPferde = 0; + m_nBaeume = 0; + m_nMallorn = 0; + m_nEisen = 0; + m_nLaen = 0; + m_nSilber = 0; + m_nUnterhalt = 0; + m_nRekruten = 0; + } +} + +CRegion::CRegion(CReportStream& oRS, CKarte* poMap, int nRunde, int32_t nPos) + : CBlockBase("CRegion") + , m_poMap(poMap) + , m_nInsel(0x7fffffffl, 0x7fffffffl, 0x7fffffffl) + , m_idInsel(CStringDB::Str2SID("")) + , m_enBlock(enUNKNOWN) + , m_nRunde(nRunde) + , m_nPos(nPos) + , m_nX(0) + , m_nY(0) + , m_nZ(0) + , m_nPartei(0) + , m_bVerorkt(false) + , m_poTerrain(0) + , m_nBauern(0) + , m_nPferde(0) + , m_nBaeume(0) + , m_nMallorn(0) + , m_nEisen(-2) + , m_nLaen(-2) + , m_nSilber(0) + , m_nUnterhalt(0) + , m_nRekruten(0) + , m_nLohn(0) + , m_nStrasse(0) + , m_nVerkauf(-1) + , m_nMaxBurg(0) + , m_nBonus(0) + , m_bOwnUnit(false) + , m_nEinkommen(0) + , m_nAusgaben(0) +{ + int32_t h; + std::string sTerrain; + + if (IsEqual(oRS.GetValue(), "REGION")) { + m_enBlock = enREGION; + } + else if (IsEqual(oRS.GetValue(), "DURCHREISEREGION")) { + m_enBlock = enDURCHREISEREGION; + } + else if (IsEqual(oRS.GetValue(), "SCHEMEN")) { + m_enBlock = enSCHEMEN; + } + else if (IsEqual(oRS.GetValue(), "SPEZIALREGION")) { + m_enBlock = enSPEZIALREGION; + } + m_nX = oRS.GetDat(0) + m_nMoveX; + m_nY = oRS.GetDat(1) + m_nMoveY; + m_nZ = oRS.GetDat(2); + + oRS.Next(); + do { + if (oRS.GetType() != CReportStream::enBLOCK) { + TAGMAP::iterator ti = g_coTags.find(DeUmlaut(oRS.GetComment())); + if (ti != g_coTags.end()) { + switch ((*ti).second) { + case T_Runde: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nRunde = oRS.GetDat(0); + break; + case T_Partei: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nPartei = oRS.GetDat(0); + break; + case T_Name: + if (oRS.GetType() == CReportStream::enSTRING) + m_sName = oRS.GetValue(); + break; + case T_Terrain: + if (oRS.GetType() == CReportStream::enSTRING) { + m_poTerrain = FindTerrain(oRS.GetValue()); + if (!m_poTerrain->m_bEisen) + m_nEisen = -1; + if (!m_poTerrain->m_bLaen) + m_nLaen = -1; + /* + switch( oRS.GetValue()[0] ) + { + case 'E': m_enType = enEBENE; break; + case 'O': m_enType = enOZEAN; break; + case 'S': m_enType = enSUMPF; break; + case 'W': + if( IsEqual( oRS.GetValue().c_str(),"Wueste") ) + m_enType = enWUESTE; + else + m_enType = enWALD; + break; + case 'H': m_enType = enHOCHLAND; break; + case 'G': m_enType = enGLETSCHER; break; + case 'B': m_enType = enBERG; break; + case 'f': + case 'F': m_enType = enFEUERWAND; break; + } + */ + } + break; + case T_Bauern: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nBauern = oRS.GetDat(0); + break; + case T_Beschr: + if (oRS.GetType() == CReportStream::enSTRING) + m_sBeschreibung = oRS.GetValue(); + break; + case T_Insel: + if (oRS.GetType() == CReportStream::enSTRING) { + m_idInsel = CStringDB::Str2SID(oRS.GetValue()); + Map()->Report()->m_bHasIslandTags = true; + } + else if (oRS.GetType() == CReportStream::enINTEGER) { + m_idInsel = CStringDB::Str2SID(CBlockBase::GetValue(Map()->Report(), std::string("island[") + ToString(int32_t(oRS.GetDat(0))) + "].name").asString()); + Map()->Report()->m_bHasIslandTags = true; + } + break; + case T_Pferde: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nPferde = oRS.GetDat(0); + break; + case T_Baeume: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nBaeume = oRS.GetDat(0); + break; + case T_Mallorn: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nMallorn = oRS.GetDat(0); + break; + case T_Eisen: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nEisen = oRS.GetDat(0); + break; + case T_Laen: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nLaen = oRS.GetDat(0); + break; + case T_Silber: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nSilber = oRS.GetDat(0); + break; + case T_Unterh: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nUnterhalt = oRS.GetDat(0); + break; + case T_Rekruten: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nRekruten = oRS.GetDat(0); + break; + case T_Lohn: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nLohn = oRS.GetDat(0); + break; + case T_Strasse: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nStrasse = oRS.GetDat(0); + break; + case T_TNorden: + case T_TSueden: + case T_TOsten: + case T_TWesten: + if (oRS.GetType() == CReportStream::enSTRING) { + sTerrain = oRS.GetValue(); + /* + switch( oRS.GetValue()[0] ) + { + case 'E': enTerrain = enEBENE; break; + case 'O': enTerrain = enOZEAN; break; + case 'S': enTerrain = enSUMPF; break; + case 'W': + if( IsEqual( oRS.GetValue().c_str(),"Wueste") ) + enTerrain = enWUESTE; + else + enTerrain = enWALD; + break; + case 'H': enTerrain = enHOCHLAND; break; + case 'G': enTerrain = enGLETSCHER; break; + case 'B': enTerrain = enBERG; break; + case 'F': enTerrain = enFEUERWAND; break; + } + */ + } + break; + case T_NNorden: + if (m_poMap->GetFromECords(m_nX, m_nY - 1, m_nZ)->m_poTerrain->m_sName.empty()) + m_poMap->Set(new CRegion(sTerrain, m_nX, m_nY - 1, m_nZ)); + break; + case T_NSueden: + if (m_poMap->GetFromECords(m_nX, m_nY + 1, m_nZ)->m_poTerrain->m_sName.empty()) + m_poMap->Set(new CRegion(sTerrain, m_nX, m_nY + 1, m_nZ)); + break; + case T_NOsten: + if (m_poMap->GetFromECords(m_nX + 1, m_nY, m_nZ)->m_poTerrain->m_sName.empty()) + m_poMap->Set(new CRegion(sTerrain, m_nX + 1, m_nY, m_nZ)); + break; + case T_NWesten: + if (m_poMap->GetFromECords(m_nX - 1, m_nY, m_nZ)->m_poTerrain->m_sName.empty()) + m_poMap->Set(new CRegion(sTerrain, m_nX - 1, m_nY, m_nZ)); + break; + case T_maxLuxus: + break; + case T_Verorkt: + if (oRS.GetType() == CReportStream::enINTEGER) + m_bVerorkt = (oRS.GetDat(0) != 0); + break; + case T_herb: + SetValue(oRS); + Map()->Report()->m_bHasIslandTags = true; + break; + case T_Schoesslinge: + case T_Steine: + case T_visibility: + SetValue(oRS); + break; + default: + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("REGION", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + } + else { + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("REGION", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + oRS.Next(); + } + else { + if (m_enBlock == enSCHEMEN || oRS.GetValue() == "REGION" || oRS.GetValue() == "DURCHREISEREGION" || oRS.GetValue() == "SPEZIALREGION") { + break; + } + else if (oRS.GetValue() == "RESOURCE") { + CResource* pR; + // int32_t h = oRS.GetDat(0); + pR = new CResource(oRS); + m_cpoResourcen.insert(Resourcen::value_type(Flatten(pR->GetValue("type").asString()), pR)); + m_cpoVResourcen.push_back(pR); + } + else if (oRS.GetValue() == "MESSAGETYPES") { + m_poMap->Report()->SetMessageRenderer(CMessage::ERESSEA1); + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) { + if (oRS.GetType() == CReportStream::enSTRING) + m_poMap->Report()->SetMessageRule(atoi(oRS.GetComment().c_str()), oRS.GetValue()); + oRS.Next(); + } + } + else if (oRS.GetValue() == "MESSAGETYPE") { + int32_t nID = oRS.GetDat(0); + m_poMap->Report()->SetMessageRenderer(CMessage::ERESSEA2); + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) { + if (oRS.GetType() == CReportStream::enSTRING) { + if (IsEqual(oRS.GetComment(), "text")) + m_poMap->Report()->SetMessageRule(nID, oRS.GetValue()); + else if (IsEqual(oRS.GetComment(), "section")) + m_poMap->Report()->SetMessageSection(nID, oRS.GetValue()); + } + oRS.Next(); + } + } + else if (oRS.GetValue() == "TRANSLATION") { + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) { + if (oRS.GetType() == CReportStream::enSTRING) { + CTranslationDB::m_coI2L[oRS.GetComment()] = oRS.GetValue(); + CTranslationDB::m_coL2I[oRS.GetValue()] = oRS.GetComment(); + } + oRS.Next(); + } + } + else if (oRS.GetValue() == "PREISE") { + Luxusgut m_oLG; + int preis = 0; + while (!oRS.EOS()) { + oRS.Next(); + if (oRS.GetType() == CReportStream::enBLOCK) + break; + if (oRS.GetDat(0) < 0) + m_nVerkauf = preis; + m_oLG.first = oRS.GetComment(); + m_oLG.second = oRS.GetDat(0); + m_coLuxusgueter.push_back(m_oLG); + preis++; + } + // oRS.Next(); + } + else if (oRS.GetValue() == "SCHEMEN") { + poMap->Set(new CRegion(oRS, poMap, nRunde, nPos)); + } + else if (oRS.GetValue() == "REGIONSBOTSCHAFTEN" || oRS.GetValue() == "UMGEBUNG" || oRS.GetValue() == "REGIONSKOMMENTAR" || oRS.GetValue() == "REGIONSEREIGNISSE") { + // REGIONSBOTSCHAFTEN werden zur Zeit noch nicht gespeichert + CBlockBase::Ptr pBlk(new CBlockBase(oRS.GetValue().c_str(), oRS.GetValue().c_str())); + AddBlock(pBlk); + int cnt = 0; + + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) { + if (oRS.GetType() == CReportStream::enSTRING) { + m_coBotschaften.push_back(oRS.GetValue()); + pBlk->SetValue(std::string("@") + ToString(int32_t(cnt++)), oRS.GetValue()); + } + else + break; + oRS.Next(); + } + } + else if (oRS.GetValue() == "BURG") { + CBauwerk* pB; + h = oRS.GetDat(0); + pB = new CBauwerk(oRS, nRunde); + if (IsEqual(pB->Typ().c_str(), "Burg") || CBurgInfo::Lookup(pB->Typ()).GetValue("Groesse").asLong()) { + if (m_nMaxBurg < pB->Groesse()) { + m_nMaxBurg = pB->Groesse(); + m_nBonus = CBurgInfo::Lookup(pB->Typ()).GetValue("Bonus").asLong(); + } + } + if (!m_pcpoBauwerke) + m_pcpoBauwerke.reset(new Bauwerke()); + m_pcpoBauwerke->insert(Bauwerke::value_type(h, pB)); + if (!m_pcpoVBauwerke) + m_pcpoVBauwerke.reset(new VBauwerke()); + m_pcpoVBauwerke->push_back(pB); + if (Map() && Map()->Report()) + Map()->Report()->AddBuilding(h, pB); + } + else if (oRS.GetValue() == "SCHIFF") { + CSchiff* pS; + h = oRS.GetDat(0); + if (!m_pcpoSchiffe) + m_pcpoSchiffe.reset(new Schiffe()); + m_pcpoSchiffe->insert(Schiffe::value_type(h, pS = new CSchiff(oRS, nRunde))); + if (!m_pcpoVSchiffe) + m_pcpoVSchiffe.reset(new VSchiffe()); + m_pcpoVSchiffe->push_back(pS); + if (Map() && Map()->Report()) + Map()->Report()->AddShip(h, pS); + } + else if (oRS.GetValue() == "GRENZE") { + CGrenze* pG; + // h = oRS.GetDat(0); + pG = new CGrenze(oRS); + m_cpoGrenzen.insert(Grenzen::value_type(pG->Richtung(), pG)); + m_cpoVGrenzen.push_back(pG); + } + else if (oRS.GetValue() == "EFFECTS") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enSTRING) { + m_coEffects.push_back(oRS.GetValue()); + oRS.Next(); + } + } + else if (oRS.GetValue() == "EINHEIT") { + CEinheit* pE; + h = oRS.GetDat(0); + m_cpoEinheiten.insert(Einheiten::value_type(h, pE = new CEinheit(oRS, this))); + m_cpoVEinheiten.push_back(pE); + m_poMap->m_poReport->m_cpoGEinheiten.insert(Einheiten::value_type(h, pE)); + + if (pE->Partei() == m_poMap->m_poReport->Partei() && pE->Bauwerk()) { + CBauwerk* pBW = GetBuilding(pE->Bauwerk()); + if (pBW && (pBW->Besitzer() == pE->Nummer())) { + m_nAusgaben += pBW->Unterhalt(); + } + } + } + else if (oRS.GetValue() == "DURCHREISE") { + oRS.Next(); + do { + if (oRS.GetType() == CReportStream::enSTRING) + m_coDurchreisen.push_back(oRS.GetValue()); + else + break; + oRS.Next(); + } while (!oRS.EOS() && oRS.GetType() == CReportStream::enSTRING); + } + else if (oRS.GetValue() == "DURCHSCHIFFUNG") { + oRS.Next(); + do { + if (oRS.GetType() == CReportStream::enSTRING) + m_coDurchschiffungen.push_back(oRS.GetValue()); + else + break; + oRS.Next(); + } while (!oRS.EOS() && oRS.GetType() == CReportStream::enSTRING); + } + else if (oRS.GetValue() == "BATTLESPEC") { + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) + oRS.Next(); + } + else if (oRS.GetValue() == "MESSAGE") { + CMessage::Ptr pMsg(new CMessage(oRS, m_nRunde)); + // m_cpoMessages.push_back( pMsg ); + Map()->Report()->AddMessage(pMsg); + if (IsEqual(Map()->Report()->m_sSpiel, "eressea") && pMsg->GetValue("type").asLong() == 1638122429) + m_bVerorkt = true; + pMsg->SetValue(std::string("localmsg"), Value(1)); + pMsg->SetValue(std::string("region"), Value(int32_t(GetEX()))); + pMsg->SetValue(std::string("region:1"), Value(int32_t(GetEY()))); + if (GetEZ()) + pMsg->SetValue(std::string("region:2"), Value(int32_t(GetEZ()))); + /* + oRS.Next(); + while( !oRS.EOS() && !oRS.GetType()==CReportStream::enBLOCK ) + { + if( oRS.GetType()==CReportStream::enSTRING && IsEqual( oRS.GetComment(), "rendered" ) ) + { + m_coBotschaften.push_back( oRS.GetValue() ); + } + oRS.Next(); + } + */ + } + else { + if (CHierarchy::IsChild("REGION", oRS.GetValue())) { + LoadSubObject(oRS); + } + else { + ERRMSG(0, ("Line %d, Warnung: Unbekannter Block: %s", oRS.GetLine(), oRS.GetValue().c_str())); + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) + oRS.Next(); + } + } + } + } while (!oRS.EOS()); + + for (Einheiten::iterator ei = m_cpoEinheiten.begin(); ei != m_cpoEinheiten.end(); ei++) { + if ((*ei).second->Schiff() && GetShip((*ei).second->Schiff()) && !GetShip((*ei).second->Schiff())->CRKap()) { + GetShip((*ei).second->Schiff())->AddWeight((int32_t)((*ei).second->Gewicht() + 0.9901)); + } + } + + // int nPers = 0; + CBauwerk* poBW; + CSchiff* poSH; + + for (size_t j = 0; j < m_cpoVEinheiten.size(); j++) { + if (m_cpoVEinheiten[j]->m_nBauwerk) { + poBW = GetBuilding(m_cpoVEinheiten[j]->m_nBauwerk); + if (poBW) { + m_cpoVEinheiten[j]->m_nPlace = poBW->Insassen() + 1; + poBW->AddInsassen(m_cpoVEinheiten[j]->Anzahl()); + } + } + else if (m_cpoVEinheiten[j]->m_nSchiff) { + poSH = GetShip(m_cpoVEinheiten[j]->m_nSchiff); + if (poSH) { + m_cpoVEinheiten[j]->m_nPlace = poSH->Insassen() + 1; + poSH->AddInsassen(m_cpoVEinheiten[j]->Anzahl()); + } + } + } + + if (!m_poTerrain) + m_poTerrain = FindTerrain(""); + + if (m_poTerrain->m_bLand) + m_nInsel = GetKey(); + else { + m_nBauern = 0; + m_nBaeume = 0; + m_nPferde = 0; + m_nBaeume = 0; + m_nMallorn = 0; + m_nEisen = 0; + m_nLaen = 0; + m_nSilber = 0; + m_nUnterhalt = 0; + m_nRekruten = 0; + } +} + +CRegion::~CRegion() +{ + if (m_pcpoBauwerke) { + for (Bauwerke::iterator bi = m_pcpoBauwerke->begin(); bi != m_pcpoBauwerke->end(); bi++) { + delete (*bi).second; + } + m_pcpoBauwerke->clear(); + } + + if (m_pcpoSchiffe) { + for (Schiffe::iterator si = m_pcpoSchiffe->begin(); si != m_pcpoSchiffe->end(); si++) { + delete (*si).second; + } + m_pcpoSchiffe->clear(); + } + + for (Einheiten::iterator ei = m_cpoEinheiten.begin(); ei != m_cpoEinheiten.end(); ei++) { + delete (*ei).second; + } + m_cpoEinheiten.clear(); + + for (Grenzen::iterator gi = m_cpoGrenzen.begin(); gi != m_cpoGrenzen.end(); gi++) { + delete (*gi).second; + } + m_cpoGrenzen.clear(); + + for (Resourcen::iterator ri = m_cpoResourcen.begin(); ri != m_cpoResourcen.end(); ri++) { + delete (*ri).second; + } + m_cpoResourcen.clear(); +} + +// 0: Nur Typ +// 2: Der Name ist bekannt +// 5: Einheiten sind hier +// 8: Report enth�lt Eisen-Info +// 10: Report enth�lt Laen-Info +int CRegion::GetQuality() const +{ + int q = (Runde() - CurrentRound()) * 100; + if (m_nLaen >= 0 && !m_cpoEinheiten.empty()) + return q + 10; + if (m_nEisen >= 0 && !m_cpoEinheiten.empty()) + return q + 8; + if (!m_cpoEinheiten.empty()) + return q + 5; + if (!m_sName.empty()) + return q + 2; + return q; +} + +void CRegion::Write(CReportStream& oRS) +{ + if (m_poTerrain->m_sName.empty()) + return; + if (m_nZ) + oRS.WriteBlock("REGION", "", m_nX, m_nY, m_nZ); + else + oRS.WriteBlock("REGION", "", m_nX, m_nY); + if (m_nRunde) + oRS.WriteLine(m_nRunde, "Runde"); + if (m_nPartei) + oRS.WriteLine(m_nPartei, "Partei"); + if (!m_sName.empty()) + oRS.WriteLine(m_sName, "Name"); + oRS.WriteLine(m_poTerrain->m_sName, "Terrain"); + if (m_nBauern >= 0) + oRS.WriteLine(m_nBauern, "Bauern"); + if (!m_sBeschreibung.empty()) + oRS.WriteLine(m_sBeschreibung, "Beschr"); + if (m_nPferde >= 0) + oRS.WriteLine(m_nPferde, "Pferde"); + if (m_nBaeume >= 0) + oRS.WriteLine(m_nBaeume, "Baeume"); + if (m_nMallorn >= 0) + oRS.WriteLine(m_nMallorn, "Mallorn"); + if (m_nEisen > -2) + oRS.WriteLine(m_nEisen, "Eisen"); + if (m_nLaen > -2) + oRS.WriteLine(m_nLaen, "Laen"); + if (m_nSilber >= 0) + oRS.WriteLine(m_nSilber, "Silber"); + if (m_nUnterhalt >= 0) + oRS.WriteLine(m_nUnterhalt, "Unterh"); + if (m_nRekruten >= 0) + oRS.WriteLine(m_nRekruten, "Rekruten"); + if (m_nLohn >= 0) + oRS.WriteLine(m_nLohn, "Lohn"); + if (m_nVerkauf >= 0) { + oRS.WriteBlock("PREISE"); + for (size_t preis = 0; preis < m_coLuxusgueter.size(); preis++) { + oRS.WriteLine(m_coLuxusgueter[preis].second, m_coLuxusgueter[preis].first); + } + } + + if (m_pcpoBauwerke) { + for (Bauwerke::iterator bi = m_pcpoBauwerke->begin(); bi != m_pcpoBauwerke->end(); bi++) { + (*bi).second->Write(oRS); + } + } + + if (m_pcpoSchiffe) { + for (Schiffe::iterator si = m_pcpoSchiffe->begin(); si != m_pcpoSchiffe->end(); si++) { + (*si).second->Write(oRS); + } + } + + for (Einheiten::iterator ei = m_cpoEinheiten.begin(); ei != m_cpoEinheiten.end(); ei++) { + (*ei).second->Write(oRS); + } +} + +int32_t CRegion::SilverOf(int32_t nPlayer) const +{ + int32_t nSilver = 0; + + for (size_t i = 0; i < m_cpoVEinheiten.size(); i++) { + if (m_cpoVEinheiten[i]->Partei() == nPlayer) { + nSilver += m_cpoVEinheiten[i]->Silber(); + } + } + return nSilver; +} + +int32_t CRegion::PersonsOf(int32_t nPlayer, bool realPersons) const +{ + int32_t nAnzahl = 0; + + for (size_t i = 0; i < m_cpoVEinheiten.size(); i++) { + if (m_cpoVEinheiten[i]->Partei() == nPlayer && (!realPersons || !m_cpoVEinheiten[i]->m_nVerraeter)) { + nAnzahl += m_cpoVEinheiten[i]->Anzahl(); + } + } + return nAnzahl; +} + +bool CRegion::IsGroup(int32_t nGroupID) const +{ + for (size_t i = 0; i < m_cpoVEinheiten.size(); i++) { + if (m_cpoVEinheiten[i]->GruppenID() == nGroupID) { + return true; + } + } + return false; +} + +int CRegion::GetBonus() const +{ + return m_nBonus; + // if( m_nMaxBurg<2 ) return 0; + // if( m_nMaxBurg<10 ) return 1; + // if( m_nMaxBurg<50 ) return 2; + // if( m_nMaxBurg<250 ) return 3; + // if( m_nMaxBurg<1250 ) return 4; + // return 5; +} + +int32_t CRegion::CalcJobs() const +{ + int32_t nJobs = GetRegionKap(); + int32_t nRes; + + if (m_coRImpacts.empty()) { + CConfigFile oCF(g_sConfigFile); + size_t i = 1; + while (true) { + if (!oCF.FetchLine("Resources", i++, false)) + break; + m_coRImpacts[DeUmlaut(oCF.GetString(0))] = oCF.GetReal(1); + } + m_coRImpacts[DeUmlaut("unknown")] = 0.0; + } + + if (m_coRImpacts.size() > 1) { + bool bBaum = false; + for (ResourceImpacts::const_iterator ri = m_coRImpacts.begin(); ri != m_coRImpacts.end(); ri++) { + if (IsEqual((*ri).first, "Mallorn") || IsEqual((*ri).first, "Baeume")) { + if (!bBaum) { + bBaum = true; + nRes = GetValue((*ri).first).asLong(); + if (nRes > 0) { + nJobs -= (int32_t)(((*ri).second) * nRes); + } + } + } + else { + nRes = GetValue((*ri).first).asLong(); + if (nRes > 0) { + nJobs -= (int32_t)(((*ri).second) * nRes); + } + } + } + } + else { + return nJobs - GetValue("Baeume").asLong() * 8 - GetValue("Schoesslinge").asLong() * 4; + } + return nJobs; +} + +int32_t CRegion::CalcProfit() const +{ + if (m_nBauern > 0) { + int32_t nArbeit = CalcJobs(); // GetRegionKap() - GetValue( "Baeume" ).asLong()*8 - GetValue( "Schoesslinge" ).asLong()*4; + /* + if( nArbeit < 0) + { + int i = 1123; i++; + } + */ + return (nArbeit < m_nBauern ? nArbeit : m_nBauern) * (GetBonus() + 11) - m_nBauern * 10; + } + return 0; +} + +char CRegion::GetRegionChar() const +{ + // static char RCHARS[]="/.ESDHBGWF"; + + if (m_enBlock == enSCHEMEN || m_enBlock == enSPEZIALREGION) { + if (IsEqual(m_sName, "Ozean")) + return '.'; + else + return '?'; + } + return m_bOwnUnit ? m_poTerrain->m_cMCY : m_poTerrain->m_cMCN; +} + +const std::string& CRegion::GetRegionTypeName() const +{ + // static char* RTNAMES[]={ + // "Unbekannt","Ozean","Ebene","Sumpf","Wueste","Hochebene","Berg","Gletscher","Wald","Feuerwand" + // }; + static std::string sSchemen("Schemen"); + static std::string sSpezial("Nebel"); + if (m_enBlock == enSCHEMEN) { + return sSchemen; + } + if (m_enBlock == enSPEZIALREGION) { + return sSpezial; + } + return m_poTerrain->m_sName; +} + +int32_t CRegion::GetRegionKap() const +{ + // static int32_t RTKAP[]={ + // 0,0,10000,2000,500,4000,1000,100,10000,0 + // }; + return m_poTerrain->m_nMaxWork; +} + +Value CRegion::GetValue(const std::string& sKey) const +{ + char sTmp[2]; + if (IsEqual(sKey.c_str(), "X")) + return Value(int32_t(m_nX)); + if (IsEqual(sKey.c_str(), "Y")) + return Value(int32_t(m_nY)); + if (IsEqual(sKey.c_str(), "Z")) + return Value(int32_t(m_nZ)); + if (IsEqual(sKey.c_str(), "Char")) { + sTmp[0] = GetRegionChar(); + sTmp[1] = 0; + return Value(std::string(sTmp)); + } + if (IsEqual(sKey.c_str(), "Baeume") && GetValue("Mallorn").asLong() > 1) { + return GetValue("Mallorn"); + } + if (IsEqual(sKey.c_str(), "Mallorn") && m_nMallorn == 1) { + return m_nBaeume; + } + CResource* pR = GetResource(sKey); + if (pR) { + return Value(pR->GetValue("number").asLong()); + } + if (IsEqual(sKey.c_str(), "Bauern")) { + return Value(int32_t(m_nBauern)); + } + if (IsEqual(sKey.c_str(), "Pferde")) { + return Value(int32_t(m_nPferde)); + } + if (IsEqual(sKey.c_str(), "Baeume")) { + return Value(int32_t(m_nBaeume)); + } + if (IsEqual(sKey.c_str(), "Mallorn")) { + return Value(int32_t(m_nMallorn)); + } + if (IsEqual(sKey.c_str(), "Eisen")) { + return Value(int32_t(m_nEisen)); + } + if (IsEqual(sKey.c_str(), "Insel")) { + return Value(CStringDB::SID2Str(m_idInsel)); + } + if (IsEqual(sKey.c_str(), "Silber")) { + return Value(int32_t(m_nSilber)); + } + if (IsEqual(sKey.c_str(), "Unterhalt")) { + return Value(int32_t(m_nUnterhalt)); + } + if (IsEqual(sKey.c_str(), "Rekruten")) { + return Value(int32_t(m_nRekruten)); + } + if (IsEqual(sKey.c_str(), "Lohn")) { + return Value(int32_t(m_nLohn)); + } + if (IsEqual(sKey.c_str(), "Laen")) { + return Value(int32_t(m_nLaen)); + } + if (IsEqual(sKey.c_str(), "Strasse")) { + return Value(int32_t(m_nStrasse)); + } + if (IsEqual(sKey.c_str(), "Einnahmen")) { + return Value(int32_t(GetEinkommen())); + } + if (IsEqual(sKey.c_str(), "Ausgaben")) { + return Value(int32_t(GetAusgaben())); + } + if (IsEqual(sKey.c_str(), "verorkt")) { + return Value(int32_t(m_bVerorkt ? 1 : 0)); + } + if (IsEqual(sKey.c_str(), "arbeitsplaetze")) { + return Value(int32_t(CalcJobs())); + } + return CBlockBase::GetValue(sKey, Value()); +} + +Value CRegion::DeepGetValue(const std::string& sKey) +{ + Value oVal; + oVal = GetValue(sKey); + if (oVal.getType() == VT_EMPTY) { + RegionDB::iterator rdbi; + rdbi = g_coRDB.find(GetKey()); + if (rdbi != g_coRDB.end()) { + RegionSet::iterator rsi = (*rdbi).second.begin(); + while (rsi != (*rdbi).second.end()) { + oVal = (*rsi)->GetValue(sKey); + if (oVal.getType() != VT_EMPTY) + break; + rsi++; + } + if (oVal.getType() == VT_EMPTY) { + oVal = Value(0); + } + } + } + return oVal; +} + +const CRegion::CTerrainType* CRegion::FindTerrain(const std::string& sType) +{ + static CTerrainType oUnknown("", '/', '/', 0, false, false, false); + TerrainTypes::const_iterator ti; + + if (!m_coTerrains.size()) { + CConfigFile oCF(g_sConfigFile); + size_t i = 1; + while (true) { + if (!oCF.FetchLine("Terrains", i++)) + break; + m_coTerrains.insert(TerrainTypes::value_type(DeUmlaut(oCF.GetString(0)), CTerrainType(oCF.GetString(0), oCF.GetString(1)[0], oCF.GetString(2)[0], oCF.GetLong(3), oCF.GetLong(4) != 0, oCF.GetLong(5) != 0, oCF.GetLong(6) != 0))); + } + } + ti = m_coTerrains.find(DeUmlaut(sType)); + if (ti == m_coTerrains.end()) { + if (!sType.empty()) { + std::string sTemp = Flatten(sType) + "abcdefghijklmnopqrstuvwxyz"; + std::string::const_iterator si = sTemp.begin(); + char rc; + while (si != sTemp.end()) { + ti = m_coTerrains.begin(); + while (ti != m_coTerrains.end()) { + if (tolower((*ti).second.m_cMCY) == (*si) || tolower((*ti).second.m_cMCN) == (*si)) + break; + ti++; + } + if (ti == m_coTerrains.end()) + break; + si++; + } + if (si == sTemp.end()) + rc = '/'; + else + rc = (*si); + + ERRMSG(0, ("Warnung: Unbekannter Regionstyp, simuliere Config-Eintrag: \x22%s\x22, \x22%c\x22, \x22%c\x22, 0, 1, 1, 1", sType.c_str(), toupper(rc), rc)); + return &(m_coTerrains.insert(TerrainTypes::value_type(DeUmlaut(sType), CTerrainType(sType, (char)toupper(rc), rc, 0, true, true, true))).first->second); + } + return &oUnknown; + } + else + return &(*ti).second; +} + +void CRegion::AddMaterialpool(int32_t nPartei, Materialpool& coPool, bool bSearchable) +{ + for (size_t i = 0; i < m_cpoVEinheiten.size(); i++) { + if (m_cpoVEinheiten[i]->Partei() == nPartei) { + m_cpoVEinheiten[i]->AddMaterialpool(coPool, bSearchable); + } + } +} + +///////////////////////////////////////////////////////////////////// +//.class: CBauwerk +///////////////////////////////////////////////////////////////////// + +CBauwerk::BuildingTypes CBauwerk::m_coBuildings; + +CBauwerk::CBauwerk(CReportStream& oRS, int nRunde) + : CBlockBase("CBauwerk") + , m_nGroesse(0) + , m_nBesitzer(0) + , m_nPartei(0) + , m_nUnterhalt(0) + , m_nBelagerer(0) + , m_nInsassen(0) +{ + m_nNummer = oRS.GetDat(0); + oRS.Next(); + + SetValue("runde", Value(int32_t(nRunde))); + + do { + if (oRS.GetType() != CReportStream::enBLOCK) { + TAGMAP::iterator ti = g_coTags.find(DeUmlaut(oRS.GetComment())); + if (ti != g_coTags.end()) { + switch ((*ti).second) { + case T_Typ: + if (oRS.GetType() == CReportStream::enSTRING) + m_sTyp = oRS.GetValue(); + break; + case T_Name: + if (oRS.GetType() == CReportStream::enSTRING) + m_sName = oRS.GetValue(); + break; + case T_Beschr: + if (oRS.GetType() == CReportStream::enSTRING) + m_sBeschreibung = oRS.GetValue(); + break; + case T_Groesse: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nGroesse = oRS.GetDat(0); + break; + case T_Besitzer: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nBesitzer = oRS.GetDat(0); + break; + case T_Partei: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nPartei = oRS.GetDat(0); + break; + case T_Unterhalt: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nUnterhalt = oRS.GetDat(0); + break; + case T_Belagerer: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nBelagerer = oRS.GetDat(0); + break; + case T_wahrerTyp: + SetValue(oRS); + break; + default: + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("BURG", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + } + else { + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("BURG", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + oRS.Next(); + } + else { + if (oRS.GetValue() == "EFFECTS") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enSTRING) { + m_coEffects.push_back(oRS.GetValue()); + oRS.Next(); + } + } + else { + if (CHierarchy::IsChild("BURG", oRS.GetValue())) { + LoadSubObject(oRS); + } + else { + break; + } + } + } + } while (!oRS.EOS()); + + if (!m_nUnterhalt) { + m_nUnterhalt = CBlockBase::GetValue(CBuildingInfo::Lookup(Typ()), "unterhalt.silber").asLong(); + if (m_nUnterhalt < 0) + m_nUnterhalt = m_nGroesse * (-m_nUnterhalt); + } +} + +CBauwerk::~CBauwerk() {} + +void CBauwerk::Write(CReportStream& oRS) +{ + oRS.WriteBlock("BURG", "", m_nNummer); + if (!m_sTyp.empty()) + oRS.WriteLine(m_sTyp, "Typ"); + if (!m_sName.empty()) + oRS.WriteLine(m_sName, "Name"); + if (!m_sBeschreibung.empty()) + oRS.WriteLine(m_sBeschreibung, "Beschr"); + if (m_nGroesse) + oRS.WriteLine(m_nGroesse, "Groesse"); + if (m_nBesitzer) + oRS.WriteLine(m_nBesitzer, "Besitzer"); + if (m_nPartei) + oRS.WriteLine(m_nPartei, "Partei"); + if (m_nUnterhalt) + oRS.WriteLine(m_nUnterhalt, "Unterhalt"); + if (m_nBelagerer) + oRS.WriteLine(m_nBelagerer, "Belagerer"); +} + +std::string CBauwerk::XTyp() const +{ + if (IsEqual(m_sTyp.c_str(), "Burg") && !g_bIsRealBuildingType) { + CBurgInfo* pBT = CBurgInfo::Lookup(m_nGroesse); + if (!pBT) { + if (m_nGroesse == 1) + return std::string("Grundmauern"); + if (m_nGroesse < 10) + return std::string("Befestigung"); + if (m_nGroesse < 50) + return std::string("Turm"); + if (m_nGroesse < 250) + return std::string("Burg"); + if (m_nGroesse < 1250) + return std::string("Festung"); + return std::string("Zitadelle"); + } + else { + return pBT->GetValue("Name").asString(); + } + } + return m_sTyp; +} + +///////////////////////////////////////////////////////////////////// +//.class: CSchiff +///////////////////////////////////////////////////////////////////// +CSchiff::ShipTypes CSchiff::m_coShips; + +CSchiff::CSchiff(CReportStream& oRS, int nRunde) + : CBlockBase("CSchiff") + , m_nAnzahl(1) + , m_nSchaden(0) + , m_nProzent(0) + , m_nKapitaen(0) + , m_nPartei(0) + , m_nLadung(0) + , m_nMaxLadung(0) + , m_nKueste(-1) + , m_bCRKap(false) + , m_nInsassen(0) +{ + m_nNummer = oRS.GetDat(0); + oRS.Next(); + + SetValue("runde", Value(int32_t(nRunde))); + + do { + if (oRS.GetType() != CReportStream::enBLOCK) { + TAGMAP::iterator ti = g_coTags.find(DeUmlaut(oRS.GetComment())); + if (ti != g_coTags.end()) { + switch ((*ti).second) { + case T_Name: + if (oRS.GetType() == CReportStream::enSTRING) + m_sName = oRS.GetValue(); + break; + case T_Beschr: + if (oRS.GetType() == CReportStream::enSTRING) + m_sBeschreibung = oRS.GetValue(); + break; + case T_Typ: + if (oRS.GetType() == CReportStream::enSTRING) + m_sTyp = oRS.GetValue(); + break; + case T_Anzahl: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nAnzahl = oRS.GetDat(0); + break; + case T_Prozent: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nProzent = oRS.GetDat(0); + break; + case T_Schaden: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nSchaden = oRS.GetDat(0); + break; + case T_Kapitaen: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nKapitaen = oRS.GetDat(0); + break; + case T_Partei: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nPartei = oRS.GetDat(0); + break; + case T_Ladung: + if (oRS.GetType() == CReportStream::enINTEGER) { + m_nLadung = oRS.GetDat(0); + m_bCRKap = true; + } + break; + case T_MaxLadung: + if (oRS.GetType() == CReportStream::enINTEGER) { + m_nMaxLadung = oRS.GetDat(0); + m_bCRKap = true; + } + break; + case T_Kueste: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nKueste = oRS.GetDat(0); + break; + case T_cargo: + if (oRS.GetType() == CReportStream::enINTEGER) { + m_nLadung = (oRS.GetDat(0) + 99) / 100; + m_bCRKap = true; + } + SetValue(oRS); + break; + case T_capacity: + if (oRS.GetType() == CReportStream::enINTEGER) { + m_nMaxLadung = (oRS.GetDat(0) + 99) / 100; + m_bCRKap = true; + } + SetValue(oRS); + break; + case T_Groesse: + case T_wahrerTyp: + SetValue(oRS); + break; + default: + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("SCHIFF", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + } + else { + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("SCHIFF", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + oRS.Next(); + } + else { + if (oRS.GetValue() == "EFFECTS") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enSTRING) { + m_coEffects.push_back(oRS.GetValue()); + oRS.Next(); + } + } + else { + if (CHierarchy::IsChild("SCHIFF", oRS.GetValue())) { + LoadSubObject(oRS); + } + else { + break; + } + } + } + } while (!oRS.EOS()); + + if (!m_bCRKap) { + m_nLadung = 0; + } +} + +CSchiff::~CSchiff() {} + +void CSchiff::Write(CReportStream& oRS) +{ + oRS.WriteBlock("SCHIFF", "", m_nNummer); + if (!m_sName.empty()) + oRS.WriteLine(m_sName, "Name"); + if (!m_sBeschreibung.empty()) + oRS.WriteLine(m_sBeschreibung, "Beschr"); + if (!m_sTyp.empty()) + oRS.WriteLine(m_sTyp, "Typ"); + if (m_nProzent) + oRS.WriteLine(m_nProzent, "Prozent"); + if (m_nSchaden) + oRS.WriteLine(m_nSchaden, "Schaden"); + if (m_nKapitaen) + oRS.WriteLine(m_nKapitaen, "Kapitaen"); + if (m_nPartei) + oRS.WriteLine(m_nPartei, "Partei"); + if (m_nLadung) + oRS.WriteLine(m_nLadung, "Ladung"); + if (m_nMaxLadung) + oRS.WriteLine(m_nMaxLadung, "MaxLadung"); + if (m_nKueste >= 0 && m_nKueste <= 5) + oRS.WriteLine(m_nKueste, "Kueste"); +} + +int32_t CSchiff::Kapazitaet() const +{ + int32_t nKap = 0; + if (m_sTyp.empty()) + return 0; + const CShipType* pST = FindShip(m_sTyp); + if (pST) { + nKap = pST->m_nKap; + } + else { + switch (toupper(m_sTyp[0])) { + case 'B': + nKap = 50; + break; + case 'L': + nKap = 500; + break; + case 'D': + nKap = 1000; + break; + case 'K': + nKap = 3000; + break; + case 'T': + nKap = 2000; + break; + case 'G': + nKap = 20000; + break; + } + } + nKap = int32_t(int64_t(nKap * m_nAnzahl) * (100 - m_nSchaden) / 100); + return nKap; +} + +int32_t CSchiff::MaxHolz() const +{ + if (m_sTyp.empty()) + return 0; + const CShipType* pST = FindShip(m_sTyp); + return pST->m_nHolz; +} + +int32_t CSchiff::Holz() const +{ + return GetValue("groesse").asLong(); +} + +const CSchiff::CShipType* CSchiff::FindShip(const std::string& sType) +{ + static CShipType oUnknown("", 0, 0); + ShipTypes::const_iterator ti; + std::string sName; + int32_t nKap, nHolz; + + if (!m_coShips.size()) { + CConfigFile oCF(g_sConfigFile); + size_t i = 1; + while (true) { + if (!oCF.FetchLine("Ships", i++)) + break; + sName = oCF.GetString(0); + nKap = oCF.GetLong(1); + nHolz = oCF.GetLong(5); + m_coShips.insert(ShipTypes::value_type(DeUmlaut(sName), CShipType(sName, nKap, nHolz))); + } + } + ti = m_coShips.find(DeUmlaut(sType)); + if (ti == m_coShips.end()) + return &oUnknown; + else + return &(*ti).second; +} + +///////////////////////////////////////////////////////////////////// +//.class: CGrenze +///////////////////////////////////////////////////////////////////// + +CGrenze::CGrenze(CReportStream& oRS) + : CBlockBase("CGrenze") + , m_nNummer(-1) + , m_nRichtung(-1) + , m_nProzent(0) +{ + oRS.Next(); + + do { + if (oRS.GetType() != CReportStream::enBLOCK) { + // while( oRS.GetType()!=CReportStream::enBLOCK ) + // { + TAGMAP::iterator ti = g_coTags.find(DeUmlaut(oRS.GetComment())); + if (ti != g_coTags.end()) { + switch ((*ti).second) { + case T_Typ: + if (oRS.GetType() == CReportStream::enSTRING) + m_sTyp = oRS.GetValue(); + break; + case T_Prozent: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nProzent = oRS.GetDat(0); + break; + case T_Richtung: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nRichtung = oRS.GetDat(0); + break; + default: + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("GRENZE", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + } + else { + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("GRENZE", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + oRS.Next(); + } + else { + if (oRS.GetValue() == "EFFECTS") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enSTRING) { + m_coEffects.push_back(oRS.GetValue()); + oRS.Next(); + } + } + else { + if (CHierarchy::IsChild("GRENZE", oRS.GetValue())) { + LoadSubObject(oRS); + } + else { + break; + } + } + } + } while (!oRS.EOS()); +} + +CGrenze::~CGrenze() {} + +void CGrenze::Write(CReportStream& oRS) +{ + if (m_nNummer < 0) + return; + oRS.WriteBlock("GRENZE", "", m_nNummer); + if (!m_sTyp.empty()) + oRS.WriteLine(m_sTyp, "typ"); + if (m_nRichtung) + oRS.WriteLine(m_nRichtung, "richtung"); + if (m_nProzent) + oRS.WriteLine(m_nProzent, "prozent"); +} + +///////////////////////////////////////////////////////////////////// +//.class: CKampfzauber +///////////////////////////////////////////////////////////////////// + +CKampfzauber::CKampfzauber(CReportStream& oRS) + : CBlockBase("KAMPFZAUBER", oRS) +{ +} + +CKampfzauber::~CKampfzauber() {} + +///////////////////////////////////////////////////////////////////// +//.class: CTalentSorter +///////////////////////////////////////////////////////////////////// + +bool CTalentSorter::operator()(const CTalent& oT1, const CTalent& oT2) const +{ + return oT1.m_nStufe > oT2.m_nStufe; +} + +///////////////////////////////////////////////////////////////////// +//.class: CEinheitenSorter +///////////////////////////////////////////////////////////////////// + +bool CEinheitenSorter::operator()(CEinheit* pE1, CEinheit* pE2) const +{ + if (IsFlag(VF_SORTBURGEN)) { + if (IsFlag(VF_SORTKOMMANDO) && pE1->m_nPlace && (pE1->Aufenthaltsort() == pE2->Aufenthaltsort())) { + if (pE1->m_nPlace == 1) + return true; + if (pE2->m_nPlace == 1) + return false; + } + if (IsFlag(VF_SORTPRIVAT) && (pE1->Aufenthaltsort() == pE2->Aufenthaltsort())) { + if (pE1->m_sPrivat < pE2->m_sPrivat) + return true; + if (pE1->m_sPrivat > pE2->m_sPrivat) + return false; + } + if (IsFlag(VF_SORTTALENTE) && (pE1->Aufenthaltsort() == pE2->Aufenthaltsort())) { + if (pE1->Talents().size() == 0) + return false; + if (pE2->Talents().size() == 0) + return true; + return pE1->Talents()[0].m_sTyp < pE2->Talents()[0].m_sTyp; + } + return (pE1->Aufenthaltsort() ? pE1->Aufenthaltsort() : 0x10000000) < (pE2->Aufenthaltsort() ? pE2->Aufenthaltsort() : 0x10000000); + } + else { + if (IsFlag(VF_SORTKOMMANDO) && pE1->m_nPlace && (pE1->Aufenthaltsort() == pE2->Aufenthaltsort())) { + if (pE1->m_nPlace == 1) + return true; + if (pE2->m_nPlace == 1) + return false; + } + if (IsFlag(VF_SORTPRIVAT)) { + if (pE1->m_sPrivat < pE2->m_sPrivat) + return true; + if (pE1->m_sPrivat > pE2->m_sPrivat) + return false; + } + if (pE1->Talents().size() == 0) + return false; + if (pE2->Talents().size() == 0) + return true; + return pE1->Talents()[0].m_sTyp < pE2->Talents()[0].m_sTyp; + } +} + +///////////////////////////////////////////////////////////////////// +//.class: CEinheit +///////////////////////////////////////////////////////////////////// + +CEinheit::CEinheit(CReportStream& oRS, CRegion* poRegion) + : CBlockBase("CEinheit") + , m_poRegion(poRegion) + , m_nPlace(0) + , m_nNummer(0) + , m_nTemp(0) + , m_nAlias(0) + , m_nPartei(-1) + , m_nVerkleidung(0) + , m_nAnzahl(1) + , m_nBauwerk(0) + , m_nSchiff(0) + , m_nSilber(0) + , m_nGruppe(0) + , m_nKampfStatus(0) + , m_nBewacht(0) + , m_nBelagert(0) + , m_nParteitarnung(0) + , m_nTarnung(-1) + , m_nAura(-1) + , m_nAuramax(-1) + , m_nHunger(0) + , m_nVerraeter(0) + , m_fKapReiten(0.0) + , m_fFKapReiten(0.0) + , m_nRHO(-1) + , m_fKapGehen(0.0) + , m_fFKapGehen(0.0) + , m_nGHO(0) +{ + m_nNummer = oRS.GetDat(0); + oRS.Next(); + + do { + if (oRS.GetType() != CReportStream::enBLOCK) { + TAGMAP::iterator ti = g_coTags.find(DeUmlaut(oRS.GetComment())); + if (ti != g_coTags.end()) { + switch ((*ti).second) { + case T_temp: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nTemp = oRS.GetDat(0); + break; + case T_Name: + if (oRS.GetType() == CReportStream::enSTRING) + m_sName = oRS.GetValue(); + break; + case T_Beschr: + if (oRS.GetType() == CReportStream::enSTRING) + m_sBeschreibung = oRS.GetValue(); + break; + case T_Partei: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nPartei = oRS.GetDat(0); + break; + case T_Anzahl: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nAnzahl = oRS.GetDat(0); + break; + case T_Typ: + if (oRS.GetType() == CReportStream::enSTRING) + m_sTyp = oRS.GetValue(); + break; + case T_wahrerTyp: + if (oRS.GetType() == CReportStream::enSTRING) + m_sWahrerTyp = oRS.GetValue(); + break; + case T_Burg: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nBauwerk = oRS.GetDat(0); + break; + case T_Schiff: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nSchiff = oRS.GetDat(0); + break; + case T_Silber: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nSilber = oRS.GetDat(0); + break; + case T_Kampfstatus: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nKampfStatus = oRS.GetDat(0); + break; + case T_Default: + if (oRS.GetType() == CReportStream::enSTRING) + m_sDefault = oRS.GetValue(); + break; + case T_Gruppe: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nGruppe = oRS.GetDat(0); + break; + case T_bewacht: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nBewacht = oRS.GetDat(0); + break; + case T_belagert: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nBelagert = oRS.GetDat(0); + break; + case T_Parteitarnung: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nParteitarnung = oRS.GetDat(0); + break; + case T_Tarnung: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nTarnung = oRS.GetDat(0); + break; + case T_Aura: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nAura = oRS.GetDat(0); + break; + case T_Auramax: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nAuramax = oRS.GetDat(0); + break; + case T_Hunger: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nHunger = oRS.GetDat(0); + break; + case T_Alias: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nAlias = oRS.GetDat(0); + break; + case T_hp: + if (oRS.GetType() == CReportStream::enSTRING) + m_shp = oRS.GetValue(); + break; + case T_privat: + if (oRS.GetType() == CReportStream::enSTRING) + m_sPrivat = oRS.GetValue(); + break; + case T_Anderepartei: + case T_Verkleidung: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nVerkleidung = oRS.GetDat(0); + break; + case T_Verraeter: + if (oRS.GetType() == CReportStream::enINTEGER) + m_nVerraeter = oRS.GetDat(0); + break; + case T_Parteiname: + case T_Kapazitaet: + case T_Ladung: + // SetValue( oRS ); + break; + case T_typprefix: + case T_unaided: + case T_ejcOrdersConfirmed: + case T_folgt: + case T_hero: + case T_weight: + SetValue(oRS); + break; + default: + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("EINHEIT", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + } + else { + if (!IsFlag(VF_SUPPRESSKEYWARN) && !AdditionalTag::Check("EINHEIT", oRS.GetComment())) + ERRMSG(0, ("Line %d, Warnung: Unbekannte Feldkennung: %s", oRS.GetLine(), oRS.GetComment().c_str())); + SetValue(oRS); + } + oRS.Next(); + } + else { + /*if( oRS.GetValue()=="EINHEIT" || + oRS.GetValue()=="REGION" || + oRS.GetValue()=="DURCHREISEREGION" || + oRS.GetValue()=="SCHEMEN" || + oRS.GetValue()=="SPEZIALREGION" || + oRS.GetValue()=="MESSAGETYPE" || + oRS.GetValue()=="MESSAGETYPES" || + oRS.GetValue()=="TRANSLATION" ) + { + break; + } + else*/ + if (oRS.GetValue() == "COMMANDS") { + oRS.Next(); + std::string cmd; + while (oRS.GetType() == CReportStream::enSTRING) { + cmd = oRS.GetValue(); + m_csKommandos.push_back(oRS.GetValue()); + if (IsFlag(VF_FULLCOMMANDOUTPUT) && !cmd.empty() && cmd[0] == '/') + m_csMetaOut.push_back(oRS.GetValue()); + m_cnKomLines.push_back(oRS.GetLine()); + oRS.Next(); + } + m_csMetaOut.changed(false); + } + else if (oRS.GetValue() == "EFFECTS") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enSTRING) { + m_coEffects.push_back(oRS.GetValue()); + oRS.Next(); + } + } + else if (oRS.GetValue() == "TALENTE") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enINTEGER) { + m_coTalente.push_back(CTalent(oRS.GetComment(), oRS.GetDat(0), oRS.GetDat(1), oRS.GetDat(2))); + oRS.Next(); + } + } + else if (oRS.GetValue() == "GEGENSTAENDE" || oRS.GetValue() == "GEGENST\xC4NDE") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enINTEGER) { + if (!m_nSilber && IsEqual(oRS.GetComment(), "Silber")) + m_nSilber = oRS.GetDat(0); + m_coGegenstaende.push_back(CGegenstand(oRS.GetComment(), oRS.GetDat(0))); + oRS.Next(); + } + } + else if (oRS.GetValue() == "SPRUECHE" || oRS.GetValue() == + "SPR\xDC" + "CHE") { + oRS.Next(); + while (!oRS.EOS() && !(oRS.GetType() == CReportStream::enBLOCK)) { + m_coSprueche.push_back(oRS.GetValue()); + oRS.Next(); + } + } + else if (oRS.GetValue() == "EINHEITSBOTSCHAFTEN") { + oRS.Next(); + while (oRS.GetType() == CReportStream::enSTRING) { + m_coBotschaften.push_back(oRS.GetValue()); + oRS.Next(); + } + } + else if (oRS.GetValue() == "KAMPFZAUBER") { + m_cpoKampfzauber.push_back(CKampfzauber::Ptr(new CKampfzauber(oRS))); + } + else { + if (CHierarchy::IsChild("EINHEIT", oRS.GetValue())) { + LoadSubObject(oRS); + } + else { + break; + } + /* + ERRMSG( 0, ( "Line %d, Warnung: Unbekannter Block: %s", oRS.GetLine(), oRS.GetValue().c_str() )); + oRS.Next(); + while( !oRS.EOS() && !oRS.GetType()==CReportStream::enBLOCK ) oRS.Next(); + */ + } + } + } while (!oRS.EOS()); + + if (m_csKommandos.empty() && !m_sDefault.empty()) + m_csKommandos.push_back(m_sDefault); + if (m_nPartei && m_nPartei == m_poRegion->Map()->Report()->Partei()) + m_poRegion->hasOwnUnit(true); + + std::stable_sort(m_coTalente.begin(), m_coTalente.end(), CTalentSorter(0)); +} + +CEinheit::~CEinheit() {} + +void CEinheit::Write(CReportStream& oRS) +{ + oRS.WriteBlock("EINHEIT", "", m_nNummer); + if (!m_sName.empty()) + oRS.WriteLine(m_sName, "Name"); + if (!m_sBeschreibung.empty()) + oRS.WriteLine(m_sBeschreibung, "Beschr"); + if (m_nPartei >= 0) + oRS.WriteLine(m_nPartei, "Partei"); + if (m_nAnzahl != 1) + oRS.WriteLine(m_nAnzahl, "Anzahl"); + if (!m_sTyp.empty()) + oRS.WriteLine(m_sTyp, "Typ"); + if (!m_sWahrerTyp.empty()) + oRS.WriteLine(m_sTyp, "wahrerTyp"); + if (m_nBauwerk) + oRS.WriteLine(m_nBauwerk, "Burg"); + if (m_nSchiff) + oRS.WriteLine(m_nSchiff, "Schiff"); + if (m_nSilber) + oRS.WriteLine(m_nSilber, "Silber"); + if (m_nBelagert) + oRS.WriteLine(m_nBelagert, "belagert"); + if (m_nBewacht) + oRS.WriteLine(m_nBewacht, "bewacht"); + else if (m_nKampfStatus >= 0) + oRS.WriteLine(m_nKampfStatus, "Kampfstatus"); + if (m_nParteitarnung) + oRS.WriteLine(m_nParteitarnung, "Parteitarnung"); + if (m_nTarnung >= 0) + oRS.WriteLine(m_nTarnung, "Tarnung"); + + if (!m_csKommandos.empty()) { + oRS.WriteBlock("COMMANDS"); + for (VKommandos::iterator ki = m_csKommandos.begin(); ki != m_csKommandos.end(); ki++) { + if (!(*ki).asString().empty()) + oRS.WriteLine((*ki).asString()); + } + } + + if (!m_coTalente.empty()) { + oRS.WriteBlock("TALENTE"); + for (Talente::iterator ti = m_coTalente.begin(); ti != m_coTalente.end(); ti++) { + oRS.WriteLine((*ti).m_nTage, (*ti).m_nStufe, (*ti).m_sTyp); + } + } + + if (!m_coGegenstaende.empty()) { + oRS.WriteBlock("GEGENSTAENDE"); + for (Gegenstaende::iterator gi = m_coGegenstaende.begin(); gi != m_coGegenstaende.end(); gi++) { + oRS.WriteLine((*gi).second, (*gi).first); + } + } +} + +// 0: Nur Name und Nummer +// 1: Name, Nummer, Partei +// 2: Name, Nummer, Partei, Talente +// 3: Name, Nummer, Partei, Talentwerte +// 10: Parteiinfo aus Report der Partei +int CEinheit::GetQuality() const +{ + int q = (Region()->Runde() - CRegion::CurrentRound()) * 100; + if (m_poRegion->Map()->Report()->Partei() == m_nPartei) + return q + 10; + if (m_coTalente.size()) + return q + 2; + if (m_nPartei >= 0) + return q + 1; + return q; +} + +/* +inline double RundeGewicht( double w ) +{ + if( w>0 ) + return double(int64_t((w+0.0005)*1000))/1000.0f; + else + return double(int64_t((w-0.0005)*1000))/1000.0f; +} +*/ + +void CEinheit::CalcKapazitaeten(double& fKapReiten, double& fFKapReiten, int32_t& nRHO, double& fKapGehen, double& fFKapGehen, int32_t& nGHO) const +{ + if (m_nRHO >= 0) { + fKapReiten = m_fKapReiten; + fFKapReiten = m_fFKapReiten; + nRHO = m_nRHO; + fKapGehen = m_fKapGehen; + fKapGehen = m_fFKapGehen; + nGHO = m_nGHO; + } + + if (ExistUserFunction("CalcUnitCapacities")) { + Value oVErg; + ArgumentList coArgs; + coArgs.push_back(Value(itoan(m_nNummer, m_poRegion->Map()->Report()->ENrBase()))); + if (DoUserFunction(std::string("CalcUnitCapacities"), coArgs, &oVErg)) { + if (oVErg.getType() != VT_VECTOR || oVErg.size() != 6) { + ERRMSG(0, ("FEHLER: '#func CalcUnitCapacities' muss ein Array mit sechs Werten liefern!")); + } + else { + fKapReiten = oVErg.getAt(Value(0)).asReal(); + fFKapReiten = oVErg.getAt(Value(1)).asReal(); + nRHO = oVErg.getAt(Value(2)).asLong(); + fKapGehen = oVErg.getAt(Value(3)).asReal(); + fFKapGehen = oVErg.getAt(Value(4)).asReal(); + nGHO = oVErg.getAt(Value(5)).asLong(); + + m_fKapReiten = fKapReiten; + m_fFKapReiten = fFKapReiten; + m_nRHO = nRHO; + m_fKapGehen = fKapGehen; + m_fKapGehen = fFKapGehen; + m_nGHO = nGHO; + return; + } + } + } + + double fPersGew; + double fPersKap; + double fWagenGewicht; + double fWagenKapazitaet; + double fPferdGewicht; + double fPferdKapazitaet; + fPersGew = CRasse::Lookup(RealType()).GetValue(std::string("Gewicht")).asReal(); + if (fPersGew < 0.001) + fPersGew = RealType() == "Trolle" ? 20.0 : 10.0; + fPersKap = CRasse::Lookup(RealType()).GetValue(std::string("Kapazit\xE4t")).asReal(); + if (fPersKap < 0.001) + fPersKap = RealType() == "Trolle" ? 10.8 : 5.4; + fWagenGewicht = CGegenstandsInfo::Lookup("Wagen").GetValue("Gewicht").asReal(); + if (fWagenGewicht < 0.001) + fWagenGewicht = 40.0; + fWagenKapazitaet = CGegenstandsInfo::Lookup("Wagen").GetValue("Kapazit\xE4t").asReal(); + if (fWagenKapazitaet < 0.001) + fWagenKapazitaet = 140.0; + fPferdGewicht = CGegenstandsInfo::Lookup("Pferd").GetValue("Gewicht").asReal(); + if (fPferdGewicht < 0.001) + fPferdGewicht = 50.0; + fPferdKapazitaet = CGegenstandsInfo::Lookup("Pferd").GetValue("Kapazit\xE4t").asReal(); + if (fPferdKapazitaet < 0.001) + fPferdKapazitaet = 20.0; + + bool bVerdanon = IsEqual(m_poRegion->Map()->Report()->Spiel(), "Verdanon"); + int nWagen = ((CEinheit*)this)->GetValue(std::string("Wagen"), std::string("")).asLong(); + int nPferde = ((CEinheit*)this)->GetValue(std::string("Pferd"), std::string("")).asLong(); + int nTReiten = ((CEinheit*)this)->GetValue(std::string("Reiten"), std::string("Stufe")).asLong(); + int nReiten = nTReiten * m_nAnzahl; + + int nPferdePerWagen = int(fWagenGewicht / fPferdKapazitaet); + int nErlPferdeReiten = nReiten * 2; + int nErlPferdeGehen = nReiten * (bVerdanon ? 3 : 4) + m_nAnzahl; + int nErlWagenGehen = nReiten * 2; + int nPferdGezogeneWagen; + int nTrollGezogeneWagen; + int nZuvielWagen; + int nLaufendePersonen; + double fTransportgut; + double fKapazitaet, fFreieKapaz; + double fGewicht = Gewicht(); + + fKapReiten = 0.0; + fFKapReiten = 0.0; + nRHO = 0; + fKapGehen = 0.0; + fFKapGehen = 0.0; + nGHO = 0; + + // if( m_poRegion->Map()->Report()->Version()<20 || IsEqual( m_poRegion->Map()->Report()->Spiel(), "Ermpiria" ) ) + // fPersKap = (RealType()=="Trolle"?10.0:5.0); + + // if( bVerdanon ) + // fPersKap = 6.0; + + if (nErlPferdeReiten > nPferde) + nErlPferdeReiten = nPferde; + if (nErlPferdeGehen > nPferde) + nErlPferdeGehen = nPferde; + + fTransportgut = fGewicht - fWagenGewicht * nWagen - fPferdGewicht * nPferde - fPersGew * m_nAnzahl; + + // RK: %1.1f/%1.1f GK: + if (!nPferde || !nErlPferdeReiten) { + fKapReiten = 0.0; + nRHO = nPferde; + } + else { + // Reitkapazitaet ermitteln + if (!nPferdePerWagen) { + nPferdGezogeneWagen = 0; + } + else { + nPferdGezogeneWagen = nErlPferdeReiten / nPferdePerWagen; // erlaubte wagen + } + if (nPferdGezogeneWagen > nWagen) { + nPferdGezogeneWagen = nWagen; // wenn wagenmaximum nicht ausgeschoepft wird + } + if (nWagen > nPferdGezogeneWagen) { + nZuvielWagen = nWagen - nPferdGezogeneWagen; // nicht gezogene wagen + } + else { + nZuvielWagen = 0; + } + fKapazitaet = fWagenKapazitaet * nPferdGezogeneWagen + fPferdKapazitaet * (nErlPferdeReiten - nPferdePerWagen * nPferdGezogeneWagen); // die wagen sowie die restpferde + fFreieKapaz = fKapazitaet - fPersGew * m_nAnzahl - fTransportgut - fWagenGewicht * nZuvielWagen; // alles rauf auf wagen und Pferde + + fKapReiten = fKapazitaet; + fFKapReiten = fFreieKapaz; + if (nPferde > nErlPferdeReiten) { + nRHO = nPferde - nErlPferdeReiten; + } + else { + nRHO = 0; + } + } + + nTrollGezogeneWagen = 0; + nLaufendePersonen = m_nAnzahl; + if (!nPferdePerWagen) { + nPferdGezogeneWagen = 0; + } + else { + nPferdGezogeneWagen = nErlPferdeGehen / nPferdePerWagen; // maximal moegliche wagen + } + if (nPferdGezogeneWagen > nErlWagenGehen) { + nPferdGezogeneWagen = nErlWagenGehen; // es werden nur erlaubte gezogen + } + if (nPferdGezogeneWagen > nWagen) { + nPferdGezogeneWagen = nWagen; // wenn wagenmaximum nicht ausgeschoepft wird + } + if (nWagen > nPferdGezogeneWagen) { + nZuvielWagen = nWagen - nPferdGezogeneWagen; // nicht gezogene wagen + if (CRasse::Lookup(RealType()).GetValue("Wagenzug").asLong()) { + int h = nLaufendePersonen - (nErlPferdeGehen + nTReiten) / (nTReiten + 1); + int hh = CRasse::Lookup(RealType()).GetValue("Wagenzug").asLong(); + h = h / hh; + if (h > 0) { + if (h > nZuvielWagen) { + h = nZuvielWagen; + } + nZuvielWagen -= h; + nLaufendePersonen -= h * CRasse::Lookup(RealType()).GetValue("Wagenzug").asLong(); + nTrollGezogeneWagen = h; + } + } + } + else { + nZuvielWagen = 0; + } + fKapazitaet = fWagenKapazitaet * (nPferdGezogeneWagen + nTrollGezogeneWagen) + fPferdKapazitaet * (nErlPferdeGehen - nPferdePerWagen * nPferdGezogeneWagen) + fPersKap * nLaufendePersonen; // die wagen sowie die restpferde und die Leute tragen mit + fFreieKapaz = fKapazitaet - fTransportgut - fWagenGewicht * nZuvielWagen; // alles rauf auf wagen und Pferde + + fKapGehen = fKapazitaet; + fFKapGehen = fFreieKapaz; + nGHO = nPferde > nErlPferdeGehen ? nPferde - nErlPferdeGehen : 0; + + m_fKapReiten = fKapReiten; + m_fFKapReiten = fFKapReiten; + m_nRHO = nRHO; + m_fKapGehen = fKapGehen; + m_fKapGehen = fFKapGehen; + m_nGHO = nGHO; +} + +// Gesammtgewicht, Reitkapazität/frei, Gehkapazität/frei, Ladung +void CEinheit::Kapazitaeten(const std::string& sTarget) const +{ + double fKapReiten, fFKapReiten, fKapGehen, fFKapGehen; + int32_t nRHO, nGHO; + + COutput::TPrintf(sTarget, " ; Gew: %sGE", ToString(Gewicht()).c_str()); + + CalcKapazitaeten(fKapReiten, fFKapReiten, nRHO, fKapGehen, fFKapGehen, nGHO); + + if (!nRHO && ((CEinheit*)this)->GetValue(std::string("Pferd"), std::string("")).asLong()) { + COutput::TPrintf(sTarget, " Reiten: %sGE/%sGE", ToString(fFKapReiten).c_str(), ToString(fKapReiten).c_str()); + } + if (nGHO) { + COutput::TPrintf(sTarget, " (%d Pferd%s zuviel!)", nGHO, (nGHO > 1) ? "e" : ""); + } + else { + COutput::TPrintf(sTarget, " Gehen: %sGE/%sGE", ToString(fFKapGehen).c_str(), ToString(fKapGehen).c_str()); + } + COutput::TPrintf(sTarget, "\n"); +} + +double CEinheit::Gewicht() const +{ + Gegenstaende::const_iterator gi; + GEGENSTANDINFO::const_iterator gii; + double fGew = 0.0; + // int nPferde = 0, nWagen = 0; + + fGew = CRasse::Lookup(RealType()).GetValue(std::string("Gewicht")).asReal(); + if (fGew < 0.001) + fGew = (RealType() == "Trolle" ? 20.0 : 10.0); + fGew *= m_nAnzahl; + for (gi = m_coGegenstaende.begin(); gi != m_coGegenstaende.end(); gi++) { + if (!IsEqual((*gi).first, "Silber")) { + fGew += CGegenstandsInfo::Lookup((*gi).first).GetValue(std::string("Gewicht")).asReal() * gi->second; + } + } + // if( m_poRegion->Map()->Report()->Version()>20 || !IsEqual( m_poRegion->Map()->Report()->Spiel(), "Empiria" ) ) + fGew += CGegenstandsInfo::Lookup("Silber").GetValue(std::string("Gewicht")).asReal() * m_nSilber; + return fGew; +} + +Value CEinheit::GetValue(const std::string& sKey, const std::string& sKey2) const +{ + if (IsEqual(sKey.c_str(), "Anzahl")) { + return Value(int32_t(m_nAnzahl)); + } + if (IsEqual(sKey.c_str(), "Aura")) { + return Value(int32_t(m_nAura)); + } + if (IsEqual(sKey.c_str(), "Auramax")) { + return Value(int32_t(m_nAuramax)); + } + if (IsEqual(sKey.c_str(), "Hunger")) { + return Value(int32_t(m_nHunger)); + } + if (IsEqual(sKey.c_str(), "Silber")) { + return Value(int32_t(m_nSilber)); + } + if (IsEqual(sKey.c_str(), "Partei")) { + return Value(itoan(m_nPartei, m_poRegion->Map()->Report()->PNrBase())); + } + if (IsEqual(sKey.c_str(), "Bauwerk")) { + if (m_nBauwerk) { + return Value(itoan(m_nBauwerk, m_poRegion->Map()->Report()->BNrBase())); + } + return Value(0); + } + if (IsEqual(sKey.c_str(), "Position")) { + return Value(int32_t(m_nPlace)); + } + if (IsEqual(sKey.c_str(), "Schiff")) { + if (m_nSchiff) { + return Value(itoan(m_nSchiff, m_poRegion->Map()->Report()->BNrBase())); + } + return Value(0); + } + if (IsEqual(sKey.c_str(), "Bewacht")) { + return Value(int32_t(m_nBewacht)); + } + if (IsEqual(sKey.c_str(), "Kampfstatus")) { + return Value(int32_t(m_nKampfStatus)); + } + if (IsEqual(sKey.c_str(), "Parteitarnung")) { + return Value(int32_t(m_nParteitarnung)); + } + if (IsEqual(sKey.c_str(), "Verraeter")) { + return Value(int32_t(m_nVerraeter)); + } + if (IsEqual(sKey.c_str(), "Anderepartei")) { + return Value(itoan(m_nVerkleidung, m_poRegion->Map()->Report()->PNrBase())); + } + if (IsEqual(sKey.c_str(), "Verkleidung")) { + return Value(itoan(m_nVerkleidung, m_poRegion->Map()->Report()->PNrBase())); + } + if (IsEqual(sKey.c_str(), "X")) { + return Value(int32_t(m_poRegion->GetEX())); + } + if (IsEqual(sKey.c_str(), "Y")) { + return Value(int32_t(m_poRegion->GetEY())); + } + if (IsEqual(sKey.c_str(), "privat")) { + return Value(m_sPrivat); + } + if (IsEqual(sKey.c_str(), "beschr")) { + return Value(m_sBeschreibung); + } + if (IsEqual(sKey.c_str(), "parteiname")) { + // abort(); + return Value(m_poRegion->Map()->Report()->Parteiname(m_nPartei).substr(1)); + } + if (IsEqual(sKey.c_str(), "status")) { + return Value(m_poRegion->Map()->Report()->Parteiname(m_nPartei).substr(0, 1)); + } + if (IsEqual(sKey.c_str(), "Tarnung") && sKey2.empty()) { + return Value(int32_t(m_nTarnung)); + } + if (IsEqual(sKey.c_str(), "temp")) { + if (m_nTemp) { + return Value(itoan(m_nTemp, m_poRegion->Map()->Report()->ENrBase())); + } + return Value(0); + } + if (IsEqual(sKey.c_str(), "alias")) { + if (m_nAlias) { + return Value(itoan(m_nAlias, m_poRegion->Map()->Report()->ENrBase())); + } + return Value(0); + } + if (IsEqual(sKey.c_str(), "hasmetas")) { + return Value(HasMetas() ? 1 : 0); + } + + for (Talente::const_iterator ti = m_coTalente.begin(); ti != m_coTalente.end(); ti++) { + if (IsEqual(sKey.c_str(), (*ti).m_sTyp.c_str())) { + if (IsEqual(sKey2.c_str(), "Tage") || IsEqual(sKey2.c_str(), "punkte")) { + if (IsFlag(VF_NOSKILLPOINTS)) { + int32_t nBonus = CRasse::Lookup(RealType()).GetValue(sKey).asLong(); + int32_t nStufe = int32_t((*ti).m_nStufe); + return (nStufe - nBonus) * (nStufe - nBonus + 1) * 15 * Anzahl(); + } + else { + return Value(int32_t((*ti).m_nTage)); + } + } + if (IsEqual(sKey2.c_str(), "Stufe")) { + return Value(int32_t((*ti).m_nStufe)); + } + if (IsFlag(VF_NOSKILLPOINTS) && IsEqual(sKey2.c_str(), "Mod")) { + return Value(int32_t((*ti).m_nTage)); + } + } + } + + for (Gegenstaende::const_iterator gi = m_coGegenstaende.begin(); gi != m_coGegenstaende.end(); gi++) { + if (IsEqual(sKey.c_str(), (*gi).first.c_str())) { + return Value(int32_t((*gi).second)); + } + } + + return CBlockBase::GetValue(sKey, Value(0)); +} + +void CEinheit::AddMaterialpool(CRegion::Materialpool& coPool, bool bSearchable) +{ + CRegion::Materialpool::iterator mi; + + for (Gegenstaende::iterator gi = m_coGegenstaende.begin(); gi != m_coGegenstaende.end(); gi++) { + if (bSearchable) + mi = coPool.find(DeUmlaut((*gi).first)); + else + mi = coPool.find((*gi).first); + if (mi == coPool.end()) { + if (bSearchable) + coPool.insert(CRegion::Materialpool::value_type(DeUmlaut((*gi).first), (*gi).second)); + else + coPool.insert(CRegion::Materialpool::value_type((*gi).first, (*gi).second)); + } + else + (*mi).second += (*gi).second; + } +} + +CEinheit* CEinheit::GlobalUnit(int32_t nENr) +{ + CReport::Einheiten::iterator i; + i = m_poRegion->Map()->Report()->GEinheiten().find(nENr); + if (i != m_poRegion->Map()->Report()->GEinheiten().end()) + return (*i).second; + return 0; +} + +bool CEinheit::HasMetas() const +{ + VKommandos::const_iterator ki; + for (ki = m_csKommandos.begin(); ki != m_csKommandos.end(); ki++) + if (CRegExp::Match((*ki).asString(), "//\\s+#")) + return true; + return false; +} + +std::string CEinheit::PrefixedTyp(bool bWahr) const +{ + std::string sPrefix; + if (!CBlockBase::GetValue("typprefix", Value("")).asString().empty()) { + sPrefix = CBlockBase::GetValue("typprefix", Value("")).asString(); + } + if (sPrefix.empty() && m_nGruppe >= 0 && m_poRegion && m_poRegion->Map()) { + CGruppe::Ptr pG = m_poRegion->Map()->Report()->GetGruppe(m_nGruppe); + Value oVal(0); + if (pG.get()) + sPrefix = pG->GetValue("typprefix", Value("")).asString(); + } + if (sPrefix.empty() && m_poRegion && m_poRegion->Map()) { + CPartei::Ptr pP = m_poRegion->Map()->Report()->GetLocalParteiInfo(m_nPartei); + Value oVal(0); + if (pP.get()) + sPrefix = pP->GetValue("typprefix", Value("")).asString(); + } + if (sPrefix.empty()) { + return bWahr ? WahrerTyp() : Typ(); + } + return sPrefix + Flatten(bWahr ? WahrerTyp() : Typ()); +} diff --git a/EBase/Report.h b/EBase/Report.h new file mode 100644 index 0000000..3b5fae7 --- /dev/null +++ b/EBase/Report.h @@ -0,0 +1,1706 @@ +/**************************************************************************** + * $Source: D:\\Development\\Repository/ETools/EBase/Report.h,v $ + * $Author: ssh $ + * $Date: 2003/07/01 09:39:30 $ + * $Revision: 1.1 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: ERESSEA-Datenklassen inclusive CR-Parser + ***************************************************************************** + * + * $Log: Report.h,v $ + * Revision 1.1 2003/07/01 09:39:30 ssh + * *** empty log message *** + * + * Revision 1.1 2003/07/01 09:13:41 ssh + * Initial recvsing of Source... + * + * Revision 1.10 2000/02/24 09:55:53 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.9 1999/11/28 17:38:11 S.Schuemann + * - Mannigfaltige �nderungen f�r Vorlage V1.4 beta 9 + * + * Revision 1.8 1999/11/17 08:58:15 S.Schuemann + * - support f�r multiple CRs + * + * - vielfache �nderungen f�r Vorlage 1.4 beta 8 + * + * Revision 1.7 1999/11/08 11:10:45 S.Schuemann + * - verbessertes Luxusgut-Handling + * - Korrektur der Kapazitaetsberechnung + * - Neue Flags + * + * Revision 1.6 1999/11/03 10:21:38 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.5 1999/10/26 13:25:43 S.Schuemann + * - Anpassungen fuer neue Object-Attribute und Vorlage 1.4 b 5 + * + * Revision 1.4 1999/10/20 02:22:32 S.Schuemann + * - Anpassungen der Reportklassen fuer die Features von Vorlage 1.4 b 3 + * + * Revision 1.3 1999/10/18 21:31:46 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.2 1999/09/27 06:22:31 S.Schuemann + * - CWorldDB heisst jetzt CReport; + * - Die CR-Felder "Runde" und "Anzahl Personen" werden unterst�tzt; + * - CReport::GetValue() fuer das Objekt REPORT implementiert; + * - Fehler im Kampfstatus behoben; + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ +#pragma once + +#include + +#include "Expression.h" +#include "ReportStream.h" +#include "Value.h" +#include "hierarchy.h" + +class CReport; +class CRegion; +class CBauwerk; +class CSchiff; +class CGrenze; +class CEinheit; +class CMessage; + +typedef std::vector MessageVector; +typedef std::map MessagePool; +extern MessagePool g_oMessagePool; + +class StringTable +{ +public: + typedef std::map String2Int; + typedef std::map Int2String; + + StringTable() { insert(""); } + + int size() const { return int(_s2i.size()); } + + int insert(const std::string& s) + { + int i = s2i(s); + if (i < 0) { + i = (int)_i2s.size(); + _s2i[s] = i; + _i2s[i] = s; + } + return i; + } + + bool known(const std::string& s) const { return s2i(s) != -1; } + + int s2i(const std::string& s) const + { + String2Int::const_iterator it = _s2i.find(s); + return it != _s2i.end() ? it->second : -1; + } + + const std::string& i2s(int i) const + { + static std::string nix(""); + Int2String::const_iterator it = _i2s.find(i); + return it != _i2s.end() ? it->second : nix; + } + +protected: + Int2String _i2s; + String2Int _s2i; +}; + +class CRegionKey +{ +public: + CRegionKey() + : m_nX(0) + , m_nY(0) + , m_nZ(0) + { + } + + CRegionKey(int32_t x, int32_t y, int32_t z) + : m_nX(x) + , m_nY(y) + , m_nZ(z) + { + } + + CRegionKey(const CRegionKey&) = default; + CRegionKey& operator=(const CRegionKey&) = default; + ~CRegionKey() = default; + + bool operator==(const CRegionKey& oRK) const { return m_nX == oRK.m_nX && m_nY == oRK.m_nY && m_nZ == oRK.m_nZ; } + + bool operator!=(const CRegionKey& oRK) const { return !(*this == oRK); } + + bool operator<(const CRegionKey& oRK) const + { + if (m_nX < oRK.m_nX) + return true; + else if (m_nX > oRK.m_nX) + return false; + if (m_nY < oRK.m_nY) + return true; + else if (m_nY > oRK.m_nY) + return false; + if (m_nZ < oRK.m_nZ) + return true; + return false; + } + + std::string AsString() const { return ToString(m_nX) + ',' + ToString(m_nY) + ',' + ToString(m_nZ); } + +protected: + int32_t m_nX, m_nY, m_nZ; +}; + +// typedef std::vector VKommandos; +typedef CValArray VKommandos; +typedef std::vector Effects; + +struct rqcmp +{ + bool operator()(const CRegion* pR1, const CRegion* pR2) const; +}; + +typedef std::set RegionSet; +typedef std::map RegionDB; +typedef std::map EinheitenDB; +typedef std::map RegEinheitenDB; +extern RegionDB g_coRDB; +extern RegionDB::iterator g_iRDB; +extern int32_t g_nRDBIndex; +extern EinheitenDB g_coEDB; +extern EinheitenDB::iterator g_iEDB; +extern int32_t g_nEDBIndex; +extern RegEinheitenDB g_coREDB; + +typedef std::map RRegionDB; +typedef std::map REinheitenDB; +extern RRegionDB g_coRRegionDB; +extern REinheitenDB g_coREinheitenDB; + +class CBlockBase +{ +public: + enum CFGOBJMODE { enTABH, enTABV, enNEST }; + + typedef std::shared_ptr Ptr; + typedef std::map NamedValues; + typedef std::map BlockGroup; + typedef std::map NamedSubblocks; + CBlockBase(const char* pcType, const char* pcName = 0); + CBlockBase(const char* pcType, const std::string& sName, const Value& oKey1 = Value(), const Value& oKey2 = Value(), const Value& oKey3 = Value()); + CBlockBase(const char* pcType, CReportStream& oRS); + virtual ~CBlockBase(); + std::string ID() const; + void LoadSubObject(CReportStream& oRS); + void SetValue(CReportStream& oRS); + void SetValue(const std::string& sName, const Value& oVal); + + size_t NumValues() const { return m_coNamedValues.size(); } + + size_t NumSubblocks() const { return m_coSubblocks.size(); } + + bool HasKeys() const { return m_oKey1.getType() != VT_EMPTY || m_oKey2.getType() != VT_EMPTY || m_oKey3.getType() != VT_EMPTY; } + + Value GetKey(int32_t nIdx) const + { + switch (nIdx) { + case 0: + return m_oKey1; + case 1: + return m_oKey2; + case 2: + return m_oKey3; + default: + return Value(); + } + } + + Value GetValue(int32_t nIdx, std::string& sName) const; + Value GetValue(const std::string& sName, const Value& oDefault = Value(int32_t(0))) const; + Value GetValue(CObjectPart* pOP, const Value& oDefault = Value(int32_t(0))) const; + void AddBlock(std::shared_ptr); + std::shared_ptr GetBlock(const std::string& sName, bool& bClassFound, const Value& oKey1 = Value(), const Value& oKey2 = Value(), const Value& oKey3 = Value()); + void Dump(unsigned int lvl = 0); + + static Value GetValue(std::shared_ptr pBlk, const std::string& sOAS, const Value& oDefault = Value(int32_t(0))); + static Value GetValue(CBlockBase* pBlk, const std::string& sOAS, const Value& oDefault = Value(int32_t(0))); + static std::string CalcID(int nName, const Value& oKey1 = Value(), const Value& oKey2 = Value(), const Value& oKey3 = Value()); + static std::string CalcID(const std::string& sName, const Value& oKey1 = Value(), const Value& oKey2 = Value(), const Value& oKey3 = Value()); + static void ReadConfigObjects(const std::string& sFile, const std::string& sCap, CFGOBJMODE enCOM, const std::vector& coTitles = std::vector()); + static Value GetValue(std::shared_ptr pBlk, CObjectPart* pOP, const Value& oDefault = Value(int32_t(0))); + static Value GetValue(CBlockBase* pBlk, CObjectPart* pOP, const Value& oDefault = Value(int32_t(0))); + + static Value GetCfgObjValue(CObjectPart* pOP, const Value& oDefault = Value(int32_t(0))) { return GetValue(g_poConfigObjects, pOP, oDefault); } + + static Value GetCfgObjValue(const std::string& sOAS, const Value& oDefault = Value(int32_t(0))) { return GetValue(g_poConfigObjects, sOAS, oDefault); } + + static void DumpCfgObjs() + { + if (g_poConfigObjects.get()) + g_poConfigObjects->Dump(); + } + +private: + const char* m_pcType; + int m_nName; + CHierarchyInfo::ptr m_poHInfo; + Value m_oKey1, m_oKey2, m_oKey3; + NamedValues m_coNamedValues; + NamedSubblocks m_coSubblocks; + static Ptr g_poConfigObjects; +}; + +class CGegenstandsInfo : public CBlockBase +{ +public: + CGegenstandsInfo() + : CBlockBase("CGegenstandsInfo") + { + } + + virtual ~CGegenstandsInfo() {} + + static CGegenstandsInfo& Lookup(const std::string& sThing); +}; + +class CRasse : public CBlockBase +{ +public: + CRasse() + : CBlockBase("CRasse") + { + } + + virtual ~CRasse() {} + + static CRasse& Lookup(const std::string& sRace); +}; + +class CBurgInfo : public CBlockBase +{ +public: + CBurgInfo() + : CBlockBase("CBurgInfo") + { + } + + virtual ~CBurgInfo() {} + + static CBurgInfo& Lookup(const std::string& sBurg); + static CBurgInfo* Lookup(int32_t nSize); + static std::vector m_vpoBInfos; +}; + +class CBuildingInfo : public CBlockBase +{ +public: + CBuildingInfo() + : CBlockBase("CBuildingInfo") + { + } + + virtual ~CBuildingInfo() {} + + static CBlockBase::Ptr Lookup(const std::string& sBuilding); +}; + +class CMessage : public CBlockBase +{ +public: + typedef std::shared_ptr Ptr; + + enum RENDERER { NONE, ERESSEA1, ERESSEA2 }; + + CMessage(int32_t round); + CMessage(const std::string& sRendered, int32_t round); + CMessage(CReportStream& oRS, int32_t round); + + virtual ~CMessage() {} + + std::string Render(CReport* pRep) const; + void GetCoords(std::string sTag, int32_t& x, int32_t& y, int32_t& z) const; + + /* + static void SetRule( int32_t nID, const std::string& sRule ) + { + m_coMessageTypes[nID] = sRule; + } + */ + static void ForceRendering(bool bForce) { m_bForceRender = bForce; } + + static void SelectRenderer(RENDERER enRenderer, std::map* pRules) + { + m_enRenderer = enRenderer; + m_pcoMessageTypes = pRules; + } + + static int32_t Messages(int32_t round); + static CMessage* FindMessage(int32_t round, int32_t idx); + +protected: + void registerMessage(int32_t round); + std::string Eressea1_Render(CReport* pRep) const; + + std::string Eressea2_Render(CReport* pRep) const; + std::string E2R_Parse(std::string::const_iterator& iRule, const std::string::const_iterator& iEnd) const; + + static bool m_bForceRender; + static RENDERER m_enRenderer; + static std::map* m_pcoMessageTypes; +}; + +/* +class CMessage +{ +public: + CMessage( CReportStream& oRS ); + virtual ~CMessage(); + std::string Render( int32_t nID ) const; + std::string Element( const std::string& sKey ) const; + + static void SetRule( int32_t nID, const std::string& sRule ) + { + m_coMessageTypes[nID] = sRule; + } + +protected: + std::map m_coParams; + static std::map m_coMessageTypes; +}; +*/ + +class CKarte +{ +public: + friend class CRegion; + + typedef std::map RegionMap; + typedef std::list IslandQueue; + + CKarte(CReport* poReport); + CKarte(CReportStream& oRS, CReport* poReport); + virtual ~CKarte(); + + void Import(CReportStream& oRS, int nRunde = 0); + void Write(CReportStream& oRS); + void Set(CRegion* poRegion); + CRegion* GetFromECords(int32_t nX, int32_t nY, int32_t nZ, bool bDeep = false); + CRegion* GetFromDCords(int32_t nX, int32_t nY, int32_t nZ, bool bDeep = false); + + int32_t GetCX() const { return m_nCX; } + + int32_t GetCY() const { return m_nCY; } + + void SetCenter(int nCX, int nCY) + { + m_nCX = nCX; + m_nCY = nCY; + } + + int32_t GetCursorX() const { return m_nCursorX; } + + int32_t GetCursorY() const { return m_nCursorY; } + + int32_t GetCursorEX() const { return m_nCursorX - m_nCursorY / 2 - ((m_nCursorY > 0) && (m_nCursorY & 1) ? 1 : 0); } + + int32_t GetCursorEY() const { return m_nCursorY; } + + void SetCursor(int32_t nCursorX, int32_t nCursorY) + { + m_nCursorX = nCursorX; + m_nCursorY = nCursorY; + } + + CReport* Report() { return m_poReport; } + + RegionMap& Regions() { return m_cpoRegions; } + + std::vector& VRegions() { return m_cpoVRegions; } + + int Islandize(); + void FillIslandQueue(IslandQueue& cpoQueue); + + void DumpMap(const std::string& sTarget, int32_t nCX, int32_t nCY, int32_t nB, int32_t nH, const char* pcPref = NULL); + void DumpFullMap(const std::string& sTarget, const char* pcPref = NULL); + void DumpWorldMap(const std::string& sTarget, const char* pcPref = NULL); + + static CRegionKey GetIsland(int32_t x, int32_t y, int32_t z); + +protected: + CRegionKey CalcMapKey(int32_t nX, int32_t nY, int32_t nZ); + bool m_bMap; + int32_t m_nCX; + int32_t m_nCY; + int32_t m_nCursorX; + int32_t m_nCursorY; + int32_t m_nLeft, m_nRight; + int32_t m_nTop, m_nBottom; + CReport* m_poReport; + std::vector m_cpoVRegions; + RegionMap m_cpoRegions; + static int32_t m_nWLeft, m_nWRight; + static int32_t m_nWTop, m_nWBottom; + static bool m_bWMap; +}; + +class CParteihandel +{ +public: + typedef std::map Produkte; + Produkte m_coProdukte; +}; + +class CTradeInfo +{ +public: + CTradeInfo(int32_t nUnit, int nBuilding, int nX, int nY, int nZ, int nSilber) + : m_nUnit(nUnit) + , m_nBuilding(nBuilding) + , m_nX(nX) + , m_nY(nY) + , m_nZ(nZ) + , m_nSilber(nSilber) + { + } + + int32_t m_nUnit; + int m_nBuilding; + int m_nX, m_nY, m_nZ; + int m_nSilber; +}; + +class CPartei : public CBlockBase +{ +public: + CPartei(int32_t nPartei) + : CBlockBase("CPartei", "PARTEI", Value(nPartei)) + { + } + + virtual ~CPartei() {} + + void SetAllianz(int32_t nPartei, int32_t nStatus) { m_cnAllianzen[nPartei] = nStatus; } + + int32_t GetAllianz(int32_t nPartei) const + { + std::map::const_iterator i = m_cnAllianzen.find(nPartei); + if (i != m_cnAllianzen.end()) + return (*i).second; + return 0; + } + +protected: + std::map m_cnAllianzen; +}; + +class CGruppe : public CBlockBase +{ +public: + CGruppe(int32_t nGruppe) + : CBlockBase("CGruppe", "GRUPPE", Value(nGruppe)) + { + } + + virtual ~CGruppe() {} + + void SetAllianz(int32_t nPartei, int32_t nStatus) { m_cnAllianzen[nPartei] = nStatus; } + + int32_t GetAllianz(int32_t nPartei) const + { + std::map::const_iterator i = m_cnAllianzen.find(nPartei); + if (i != m_cnAllianzen.end()) + return (*i).second; + return 0; + } + +protected: + std::map m_cnAllianzen; +}; + +class CReport : public CBlockBase +{ +public: + friend class CRegion; + friend class CVorlage; + + typedef std::map Messages; + typedef std::pair Nachricht; + typedef std::map Parteien; + typedef std::map ParteiInfos; + typedef std::map Gruppen; + typedef std::list Nachrichten; + typedef std::map Einheiten; + typedef std::map Bauwerke; + typedef std::map Schiffe; + typedef std::list Reports; + typedef std::map Handelspartner; + typedef std::list TradeInfos; + typedef std::vector Optionen; + + enum MSGTYPE { MT_UNKNOWN, MT_EREIGNISSE, MT_HANDEL, MT_EINKOMMEN }; + + CReport(const std::string& sFName = ""); + ~CReport(); + + bool IsUtf8() const { return m_bUTF8; } + + bool IsValid() const { return m_bIsValid; } + + void Import(const std::string& sFName); + void Write(const std::string& sFName); + + CKarte* GetMap() { return m_poMap; } + + int32_t Version() const { return m_nVersion; } + + int32_t Partei() const { return m_nPartei; } + + int32_t Runde() const { return m_nRunde; } + + CMessage::RENDERER MessageRenderer() const { return m_enMsgRenderer; } + + std::map* MessageRules() { return &m_coMessageTypes; } + + std::map* MessageSections() { return &m_coMessageSections; } + + void SetMessageRenderer(CMessage::RENDERER enRenderer) { m_enMsgRenderer = enRenderer; } + + void SetMessageRule(int32_t nID, const std::string& sRule) { m_coMessageTypes[nID] = sRule; } + + void SetMessageSection(int32_t nID, const std::string& sSection) { m_coMessageSections[nID] = sSection; } + + int Zeitalter() const { return m_nZeitalter; } + + int32_t Jahr() const + { + if (Zeitalter() == 1) + return (Runde() - 1) / 12 + 1; + else + return (Runde() - 184) / 27 + 1; + } + + int32_t Monat() const + { + if (Zeitalter() == 1) + return (Runde()) % 12 ? (Runde()) % 12 : 12; + else + return ((Runde() - 184) / 3) % 9 ? ((Runde() - 184) / 3) % 9 : 9; + } + + int32_t Woche() const + { + if (Zeitalter() == 1) + return 1; + else + return (Runde() - 184) % 3 + 1; + } + + int ENrBase() const { return m_nENrBase; } + + int PNrBase() const { return m_nPNrBase; } + + int BNrBase() const { return m_nBNrBase; } + + int32_t Rekrutierungskosten() const { return m_nRekrutierungskosten; } + + std::string FileName() const { return m_sOrgCRName; } + + std::string Spiel() const { return m_sSpiel; } + + bool HasSpiel() const { return m_bHasSpiel; } + + std::string Konfiguration() const { return m_sKonfiguration; } + + std::string Parteiname(int32_t nPNr = -2) const + { + switch (nPNr) { + case -2: + return m_sParteiname; + case -1: + return std::string("-parteigetarnt"); + case 0: + return std::string("-Monster"); + } + Parteien::const_iterator pi = m_coParteien.find(nPNr); + return pi == m_coParteien.end() ? std::string("-unbekannt") : (*pi).second; + } + + std::string Passwort() const { return m_sPasswort; } + + std::string Gruppe(int32_t nID) + { + CGruppe::Ptr pG = GetGruppe(nID); + std::string sGruppe; + if (pG.get()) + sGruppe = pG->GetValue("name", Value("")).asString(); + return sGruppe; + } + + CKarte* Karte() { return m_poMap; } + + Einheiten& GEinheiten() { return m_cpoGEinheiten; } + + CEinheit* SearchUnit(int32_t nENr, bool bDeep = true); + Value GetValue(const std::string& sKey); + + void AddBuilding(int32_t nID, CBauwerk* pB) { m_cpoBauwerke[nID] = pB; } + + CBauwerk* GetBuilding(int32_t nID) + { + Bauwerke::iterator bi = m_cpoBauwerke.find(nID); + return bi == m_cpoBauwerke.end() ? 0 : (*bi).second; + } + + void AddShip(int32_t nID, CSchiff* pS) { m_cpoSchiffe[nID] = pS; } + + CSchiff* GetShip(int32_t nID) + { + Schiffe::iterator si = m_cpoSchiffe.find(nID); + return si == m_cpoSchiffe.end() ? 0 : (*si).second; + } + + size_t NumMessage() const { return m_cpoMessages.size(); } + + void AddMessage(CMessage::Ptr pMsg) { m_cpoMessages[m_cpoMessages.size()] = pMsg; } + + CMessage::Ptr GetMessage(size_t nID) + { + Messages::iterator mi = m_cpoMessages.find(nID); + if (mi != m_cpoMessages.end()) { + return (*mi).second; + } + else { + return CMessage::Ptr(); + } + } + + size_t NumNachrichten() const { return m_csNachrichten.size(); } + + std::string GetNachricht(int32_t nID) + { + Nachrichten::iterator ni; + int32_t nCnt = 0; + for (ni = m_csNachrichten.begin(); nCnt < nID && ni != m_csNachrichten.end(); ni++, nCnt++) + ; + return nCnt == nID ? (*ni).second : std::string(""); + } + + void CalculateStatistics(); + + CPartei::Ptr GetLocalParteiInfo(int32_t nPartei) + { + ParteiInfos::iterator pi = m_cpoLocalParteiInfos.find(nPartei); + if (pi == m_cpoLocalParteiInfos.end()) + return CPartei::Ptr(); + else + return (*pi).second; + } + + size_t GetParteiNum() { return m_cpoLocalParteiInfos.size(); } + + static CPartei::Ptr GetGlobalParteiInfo(int32_t nPartei) + { + ParteiInfos::iterator pi = g_cpoParteiInfos.find(nPartei); + if (pi == g_cpoParteiInfos.end()) + return CPartei::Ptr(); + else + return (*pi).second; + } + + CPartei::Ptr GetNthParteiInfo(int32_t nIndex) + { + ParteiInfos::iterator pi = m_cpoLocalParteiInfos.begin(); + while (pi != m_cpoLocalParteiInfos.end() && nIndex) { + nIndex--; + pi++; + } + if (pi == m_cpoLocalParteiInfos.end()) + return CPartei::Ptr(); + else + return (*pi).second; + } + + static size_t GetGruppenNum() { return m_cpoGruppen.size(); } + + static CGruppe::Ptr GetGruppe(int32_t nGruppe) + { + Gruppen::iterator gi = m_cpoGruppen.find(nGruppe); + if (gi == m_cpoGruppen.end()) + return CGruppe::Ptr(); + else + return (*gi).second; + } + + static CGruppe::Ptr GetNthGruppe(int32_t nIndex) + { + Gruppen::iterator gi = m_cpoGruppen.begin(); + while (gi != m_cpoGruppen.end() && nIndex) { + nIndex--; + gi++; + } + if (gi == m_cpoGruppen.end()) + return CGruppe::Ptr(); + else + return (*gi).second; + } + + static int32_t GetGroupIdByName(const std::string& sName); + + Optionen& Options() { return m_csOptionen; } + + static void SetDefaultPassword(const std::string& sPass) { m_sDefaultPassword = sPass; } + +protected: + void StatistikEreignisse(const std::string& sMsg); + void StatistikHandel(const std::string& sMsg); + void StatistikProduktion(const std::string& sMsg); + void StatistikEinkommen(const std::string& sMsg); + void InsertHandel(int32_t nPNr, int32_t nAmount, const std::string& sProduct); + int32_t PNrFromENr(int32_t nENr); + +protected: + bool m_bIsValid; + bool m_bHasIslandTags; + bool m_bHasSpiel; + bool m_bUTF8; + CKarte* m_poMap; + std::string m_sOrgCRName; + int32_t m_nVersion; + std::string m_sSpiel; + std::string m_sKonfiguration; + std::string m_sPasswort; + std::string m_sParteiname; + int m_nENrBase, m_nPNrBase, m_nBNrBase; + int32_t m_nRunde; + int m_nZeitalter; + int32_t m_nPartei; + int32_t m_nRekrutierungskosten; + int32_t m_nPersonen; + int32_t m_nPunkte; + int32_t m_nPunkteschnitt; + int32_t m_nEinkommen; + int32_t m_nAusgaben; + int32_t m_nMsgEinkommen; + int32_t m_nMsgAusgaben; + CMessage::RENDERER m_enMsgRenderer; + Nachrichten m_csNachrichten; + Einheiten m_cpoGEinheiten; + Handelspartner m_cpoHPartner; + Parteien m_coParteien; + ParteiInfos m_cpoLocalParteiInfos; + TradeInfos m_coTradeInfos; + Messages m_cpoMessages; + Bauwerke m_cpoBauwerke; + Schiffe m_cpoSchiffe; + Optionen m_csOptionen; + std::map m_coMessageTypes; + std::map m_coMessageSections; + static std::string m_sDefaultPassword; + static Reports m_cpoReports; + static ParteiInfos g_cpoParteiInfos; + static Gruppen m_cpoGruppen; + static int32_t m_nMaxRound; +}; + +class CResource : public CBlockBase +{ +public: + CResource(CReportStream& oRS) + : CBlockBase("CResource", oRS) + { + } + + virtual ~CResource() {} +}; + +class CRegionSorter +{ +public: + CRegionSorter(int nFlags) + : m_nFlags(nFlags) + { + } + + virtual ~CRegionSorter() {} + + bool operator()(CRegion* pR1, CRegion* pR2) const; + +protected: + int m_nFlags; +}; + +class DummyRegion : public CBlockBase +{ + struct CTerrainType + { + std::string m_sName; + char m_cMCY, m_cMCN; + int32_t m_nMaxWork; + bool m_bLand, m_bEisen, m_bLaen; + + CTerrainType(const std::string& sName, char cMCY, char cMCN, int nMaxWork, bool bLand, bool bEisen, bool bLaen) + : m_sName(sName) + , m_cMCY(cMCY) + , m_cMCN(cMCN) + , m_nMaxWork(nMaxWork) + , m_bLand(bLand) + , m_bEisen(bEisen) + , m_bLaen(bLaen) + { + } + + CTerrainType(const CTerrainType&) = default; + + const CTerrainType& operator=(const CTerrainType& oTT) + { + m_sName = oTT.m_sName; + m_cMCY = oTT.m_cMCY; + m_cMCN = oTT.m_cMCN; + m_nMaxWork = oTT.m_nMaxWork; + m_bLand = oTT.m_bLand; + m_bEisen = oTT.m_bEisen; + m_bLaen = oTT.m_bLaen; + return *this; + } + }; + + typedef std::map TerrainTypes; + typedef std::map Bauwerke; + typedef std::vector VBauwerke; + typedef std::map Schiffe; + typedef std::vector VSchiffe; + typedef std::map Einheiten; + typedef std::vector VEinheiten; + typedef std::map Grenzen; + typedef std::vector VGrenzen; + typedef std::map Resourcen; + typedef std::vector VResourcen; + typedef std::vector Durchreisen; + typedef std::list Botschaften; + typedef std::list Messages; + typedef std::pair Luxusgut; + typedef std::vector Luxusgueter; + typedef std::map Materialpool; + typedef std::map ResourceImpacts; + + enum REGIONBLOCKS { enREGION, enDURCHREISEREGION, enSPEZIALREGION, enSCHEMEN, enUNKNOWN }; + + CRegionKey m_nInsel; + std::string m_sInsel; + std::string m_sName; + std::string m_sBeschreibung; + Luxusgueter m_coLuxusgueter; + std::unique_ptr m_pcpoBauwerke; + std::unique_ptr m_pcpoVBauwerke; + std::unique_ptr m_pcpoSchiffe; + std::unique_ptr m_pcpoVSchiffe; + Grenzen m_cpoGrenzen; + VGrenzen m_cpoVGrenzen; + Resourcen m_cpoResourcen; + VResourcen m_cpoVResourcen; + Einheiten m_cpoEinheiten; + VEinheiten m_cpoVEinheiten; + Durchreisen m_coDurchreisen; + Durchreisen m_coDurchschiffungen; + Botschaften m_coBotschaften; + VKommandos m_coKommandos; + VKommandos m_coEndKommandos; + Effects m_coEffects; + Messages m_cpoMessages; +}; + +class CRegion : public CBlockBase +{ +public: + struct CTerrainType + { + std::string m_sName; + char m_cMCY, m_cMCN; + int32_t m_nMaxWork; + bool m_bLand, m_bEisen, m_bLaen; + + CTerrainType(const std::string& sName, char cMCY, char cMCN, int nMaxWork, bool bLand, bool bEisen, bool bLaen) + : m_sName(sName) + , m_cMCY(cMCY) + , m_cMCN(cMCN) + , m_nMaxWork(nMaxWork) + , m_bLand(bLand) + , m_bEisen(bEisen) + , m_bLaen(bLaen) + { + } + + CTerrainType(const CTerrainType&) = default; + + const CTerrainType& operator=(const CTerrainType& oTT) + { + m_sName = oTT.m_sName; + m_cMCY = oTT.m_cMCY; + m_cMCN = oTT.m_cMCN; + m_nMaxWork = oTT.m_nMaxWork; + m_bLand = oTT.m_bLand; + m_bEisen = oTT.m_bEisen; + m_bLaen = oTT.m_bLaen; + return *this; + } + }; + + friend class CRegionSorter; + + typedef std::map TerrainTypes; + typedef std::map Bauwerke; + typedef std::vector VBauwerke; + typedef std::map Schiffe; + typedef std::vector VSchiffe; + typedef std::map Einheiten; + typedef std::vector VEinheiten; + typedef std::map Grenzen; + typedef std::vector VGrenzen; + typedef std::map Resourcen; + typedef std::vector VResourcen; + typedef std::vector Durchreisen; + typedef std::list Botschaften; + typedef std::list Messages; + typedef std::pair Luxusgut; + typedef std::vector Luxusgueter; + typedef std::map Materialpool; + typedef std::map ResourceImpacts; + + enum REGIONBLOCKS { enREGION, enDURCHREISEREGION, enSPEZIALREGION, enSCHEMEN, enUNKNOWN }; + + CRegion(const std::string& sType, int32_t nX = 0, int32_t nY = 0, int32_t nZ = 0, int nRunde = 0); + CRegion(CReportStream& oRS, CKarte* poMap, int nRund = 0, int32_t nPos = 0); + ~CRegion(); + + int GetQuality() const; + + REGIONBLOCKS GetBlock() const { return m_enBlock; } + + CKarte* Map() const { return m_poMap; } + + int Runde() const + { + if (m_nRunde) + return m_nRunde; + return 0; + } + + void SetMap(CKarte* poMap) { m_poMap = poMap; } + + void Write(CReportStream& oRS); + void Vorlage(int32_t nPlayer); + + std::string GetName() const { return m_sName; } + + std::string Beschr() const { return m_sBeschreibung; } + + Value GetValue(const std::string& sKey) const; + Value DeepGetValue(const std::string& sKey); + int32_t SilverOf(int32_t nPlayer) const; + int32_t PersonsOf(int32_t nPlayer, bool realPersons = false) const; + bool IsGroup(int32_t nGroupID) const; + + bool IsOwnUnit() const { return m_bOwnUnit; } + + int32_t GetEX() const { return m_nX; } + + int32_t GetEY() const { return m_nY; } + + int32_t GetEZ() const { return m_nZ; } + + int32_t GetLohn() const { return m_nLohn; } + + int32_t GetBauern() const { return m_nBauern; } + + int32_t GetSilber() const { return m_nSilber; } + + int32_t GetUnterhalt() const { return m_nUnterhalt; } + + int32_t GetRekruten() const { return m_nRekruten; } + + int32_t GetPferde() const { return m_nPferde; } + + int32_t GetBaeume() const { return m_nBaeume; } + + int32_t GetEisen() const { return m_nEisen; } + + int32_t GetLaen() const { return m_nLaen; } + + bool isMallorn() const { return m_nMallorn != 0; } + + bool isVerorkt() const { return m_bVerorkt; } + + int GetVerkauf() const { return m_nVerkauf; } + + int GetBonus() const; + + void hasOwnUnit(bool flag) { m_bOwnUnit = flag; } + + CRegionKey GetKey() const { return CalcKey(m_nX, m_nY, m_nZ); } + + CRegionKey GetIsland() const { return m_nInsel; } + + CRegionKey GetSortIsland() const { return CKarte::GetIsland(m_nX, m_nY, m_nZ); } + + void SetIsland(CRegionKey nInsel) { m_nInsel = nInsel; } + + void SetIslandName(const std::string& name) { m_idInsel = CStringDB::Str2SID(name); } + + const std::string& GetIslandName() const { return CStringDB::SID2Str(m_idInsel); } + + char GetRegionChar() const; + const std::string& GetRegionTypeName() const; + int32_t GetRegionKap() const; + int32_t CalcJobs() const; + int32_t CalcProfit() const; + + int32_t GetEinkommen() const { return m_nEinkommen; } + + int32_t GetAusgaben() const { return m_nAusgaben; } + + void SetEinkommen(int32_t i) { m_nEinkommen = i; } + + void SetAusgaben(int32_t i) { m_nAusgaben = i; } + + const Durchreisen& GetDurchreisen() const { return m_coDurchreisen; } + + const Durchreisen& GetDurchschiffungen() const { return m_coDurchschiffungen; } + + VKommandos& GetKommandos() { return m_coKommandos; } + + VKommandos& GetEndKommandos() { return m_coEndKommandos; } + + VResourcen& GetResourcen() { return m_cpoVResourcen; } + + Luxusgueter& GetLuxusgueter() { return m_coLuxusgueter; } + + CResource* GetResource(const std::string& sName) const + { + Resourcen::const_iterator ri = m_cpoResourcen.find(Flatten(sName)); + if (ri != m_cpoResourcen.end()) + return (*ri).second; + return 0; + /* + for( int i=0; iGetValue( "type" ).asString(), sName.c_str() ) ) + return m_cpoVResourcen[i]; + } + + return 0; + */ + } + + size_t NumFrontiers() const { return m_cpoGrenzen.size(); } + + CGrenze* GetFrontier(int32_t nID) + { + Grenzen::iterator gi = m_cpoGrenzen.find(nID); + if (gi != m_cpoGrenzen.end()) + return (*gi).second; + else + return 0; + } + + size_t NumBuildings() const { return m_pcpoBauwerke ? m_pcpoBauwerke->size() : 0; } + + CBauwerk* GetBuilding(int32_t nID) + { + if (!m_pcpoBauwerke) + return 0; + Bauwerke::iterator bi = m_pcpoBauwerke->find(nID); + if (bi != m_pcpoBauwerke->end()) + return (*bi).second; + else + return 0; + } + + size_t NumShips() const { return m_pcpoSchiffe ? m_pcpoSchiffe->size() : 0; } + + CSchiff* GetShip(int32_t nID) + { + if (!m_pcpoSchiffe) + return 0; + Schiffe::iterator si = m_pcpoSchiffe->find(nID); + if (si != m_pcpoSchiffe->end()) + return (*si).second; + else + return 0; + } + + bool IsLand() const { return m_poTerrain->m_bLand; } + + const CTerrainType* GetTerrain() const { return m_poTerrain; } + + const CTerrainType* FindTerrain(const std::string& sType); + + VEinheiten& GetVEinheiten() { return m_cpoVEinheiten; } + + VBauwerke* GetVBauwerke() { return m_pcpoVBauwerke.get(); } + + VSchiffe* GetVSchiffe() { return m_pcpoVSchiffe.get(); } + + const VGrenzen* GetVGrenzen() { return &m_cpoVGrenzen; } + + const Botschaften& GetBotschaften() const { return m_coBotschaften; } + + void AddMaterialpool(int32_t nPartei, Materialpool& coPool, bool bSearchable = true); + + void AddEinkommen(int32_t nSilber) { m_nEinkommen += nSilber; } + + void AddAusgaben(int32_t nSilber) { m_nAusgaben += nSilber; } + + void AddMessage(const std::string& sTxt) { m_coBotschaften.push_back(sTxt); } + + void AddMessage(CMessage::Ptr pMsg) { m_cpoMessages.push_back(pMsg); } + + size_t NumMessage() const { return m_cpoMessages.size(); } + + CMessage::Ptr GetMessage(int32_t nID) + { + Messages::iterator mi = m_cpoMessages.begin(); + while (mi != m_cpoMessages.end() && nID) { + nID--; + mi++; + } + if (mi != m_cpoMessages.end()) { + return (*mi); + } + else { + return CMessage::Ptr(); + } + } + + const Effects& GetEffects() const { return m_coEffects; } + + CRegion* GetNW() { return m_poMap->GetFromECords(m_nX - 1, m_nY + 1, m_nZ); } + + CRegion* GetN() { return m_poMap->GetFromECords(m_nX, m_nY + 1, m_nZ); } + + CRegion* GetNO() { return m_poMap->GetFromECords(m_nX, m_nY + 1, m_nZ); } + + CRegion* GetO() { return m_poMap->GetFromECords(m_nX + 1, m_nY, m_nZ); } + + CRegion* GetSO() { return m_poMap->GetFromECords(m_nX + 1, m_nY - 1, m_nZ); } + + CRegion* GetS() { return m_poMap->GetFromECords(m_nX, m_nY - 1, m_nZ); } + + CRegion* GetSW() { return m_poMap->GetFromECords(m_nX, m_nY - 1, m_nZ); } + + CRegion* GetW() { return m_poMap->GetFromECords(m_nX - 1, m_nY, m_nZ); } + + CRegionKey CalcRelKey(int32_t nDX, int32_t nDY) const { return CalcKey(nDX + m_nX, nDY + m_nY, m_nZ); } + + static CRegionKey CalcKey(int32_t nX, int32_t nY, int32_t nZ) { return CRegionKey(nX, nY, nZ); } + + static int32_t CurrentPlayer() { return m_nCurrentPlayer; } + + static void SetCurrentPlayer(int32_t nP) { m_nCurrentPlayer = nP; } + + static int CurrentRound() { return m_nCurrentRound; } + + static void SetCurrentRound(int32_t nR) { m_nCurrentRound = nR; } + + static void SetMoveOffset(int32_t nX, int32_t nY) + { + m_nMoveX = nX; + m_nMoveY = nY; + } + +protected: + CKarte* m_poMap; + CRegionKey m_nInsel; + int32_t m_idInsel; + REGIONBLOCKS m_enBlock; + int32_t m_nRunde; + int32_t m_nPos; + int32_t m_nX; + int32_t m_nY; + int32_t m_nZ; + int32_t m_nPartei; + std::string m_sName; + bool m_bVerorkt; + const CTerrainType* m_poTerrain; + // std::string m_sType; + int32_t m_nBauern; + std::string m_sBeschreibung; + int32_t m_nPferde; + int32_t m_nBaeume; + int32_t m_nMallorn; + int32_t m_nEisen; + int32_t m_nLaen; + int32_t m_nSilber; + int32_t m_nUnterhalt; + int32_t m_nRekruten; + int32_t m_nLohn; + int32_t m_nStrasse; + // int32_t m_nPreise[7]; + // std::string m_sLuxusgut[7]; + Luxusgueter m_coLuxusgueter; + int32_t m_nVerkauf; + int32_t m_nMaxBurg; + int32_t m_nBonus; + bool m_bOwnUnit; + int32_t m_nEinkommen; + int32_t m_nAusgaben; + std::unique_ptr m_pcpoBauwerke; + std::unique_ptr m_pcpoVBauwerke; + std::unique_ptr m_pcpoSchiffe; + std::unique_ptr m_pcpoVSchiffe; + Grenzen m_cpoGrenzen; + VGrenzen m_cpoVGrenzen; + Resourcen m_cpoResourcen; + VResourcen m_cpoVResourcen; + Einheiten m_cpoEinheiten; + VEinheiten m_cpoVEinheiten; + Durchreisen m_coDurchreisen; + Durchreisen m_coDurchschiffungen; + Botschaften m_coBotschaften; + VKommandos m_coKommandos; + VKommandos m_coEndKommandos; + Effects m_coEffects; + Messages m_cpoMessages; + static int32_t m_nCurrentPlayer; + static int32_t m_nCurrentRound; + static int32_t m_nMoveX; + static int32_t m_nMoveY; + static TerrainTypes m_coTerrains; + static ResourceImpacts m_coRImpacts; +}; + +class CBauwerk : public CBlockBase +{ +public: + struct CBuildingType + { + std::string m_sName; + int32_t m_nUSilber; + + CBuildingType(const std::string& sName, int32_t nUSilber) + : m_sName(sName) + , m_nUSilber(nUSilber) + { + } + + CBuildingType(const CBuildingType&) = default; + + const CBuildingType& operator=(const CBuildingType& oBT) + { + m_sName = oBT.m_sName; + m_nUSilber = oBT.m_nUSilber; + return *this; + } + }; + + friend class CVorlage; + + typedef std::map BuildingTypes; + + CBauwerk(CReportStream& oRS, int32_t nRunde); + ~CBauwerk(); + void Write(CReportStream& oRS); + + std::string Typ() const { return m_sTyp; } + + std::string XTyp() const; + + std::string Name() const { return m_sName; } + + std::string Beschreibung() const { return m_sBeschreibung; } + + int32_t Nummer() const { return m_nNummer; } + + int32_t Besitzer() const { return m_nBesitzer; } + + int32_t Belagerer() const { return m_nBelagerer; } + + int32_t Groesse() const { return m_nGroesse; } + + int32_t Insassen() const { return m_nInsassen; } + + int32_t Unterhalt() const { return m_nUnterhalt; } + + void AddInsassen(int32_t nAnz) { m_nInsassen += nAnz; } + + const Effects& GetEffects() const { return m_coEffects; } + + VKommandos& GetKommandos() { return m_coKommandos; } + +protected: + int32_t m_nNummer; + std::string m_sTyp; + std::string m_sName; + std::string m_sBeschreibung; + int32_t m_nGroesse; + int32_t m_nBesitzer; + int32_t m_nPartei; + int32_t m_nUnterhalt; + int32_t m_nBelagerer; + int32_t m_nInsassen; + Effects m_coEffects; + VKommandos m_coKommandos; + static BuildingTypes m_coBuildings; +}; + +class CSchiff : public CBlockBase +{ +public: + struct CShipType + { + std::string m_sName; + int32_t m_nKap; + int32_t m_nHolz; + + CShipType(const std::string& sName, int32_t nKap, int32_t nHolz) + : m_sName(sName) + , m_nKap(nKap) + , m_nHolz(nHolz) + { + } + + CShipType(const CShipType&) = default; + + const CShipType& operator=(const CShipType& oBT) + { + m_sName = oBT.m_sName; + m_nKap = oBT.m_nKap; + m_nHolz = oBT.m_nHolz; + return *this; + } + }; + + friend class CVorlage; + + typedef std::map ShipTypes; + + CSchiff(CReportStream& oRS, int32_t nRunde); + ~CSchiff(); + void Write(CReportStream& oRS); + + std::string Typ() const { return m_sTyp; } + + std::string Name() const { return m_sName; } + + std::string Beschreibung() const { return m_sBeschreibung; } + + int32_t Nummer() const { return m_nNummer; } + + int32_t Kapitaen() const { return m_nKapitaen; } + + int32_t Kueste() const { return m_nKueste; } + + int32_t Anzahl() const { return m_nAnzahl; } + + int32_t Schaden() const { return m_nSchaden; } + + int32_t Prozent() const { return m_nProzent; } + + int32_t Ladung() const { return m_nLadung; } + + int32_t MaxLadung() const + { + if (m_bCRKap) + return m_nMaxLadung; + else + return Kapazitaet(); + } + + int32_t Kapazitaet() const; + int32_t MaxHolz() const; + int32_t Holz() const; + + bool CRKap() const { return m_bCRKap; } + + int32_t Insassen() const { return m_nInsassen; } + + void AddWeight(int32_t nWeight) { m_nLadung += nWeight; } + + void AddInsassen(int32_t nAnz) { m_nInsassen += nAnz; } + + const Effects& GetEffects() const { return m_coEffects; } + + VKommandos& GetKommandos() { return m_coKommandos; } + + static const CShipType* FindShip(const std::string& sType); + +protected: + int32_t m_nNummer; + std::string m_sName; + std::string m_sBeschreibung; + std::string m_sTyp; + int32_t m_nAnzahl; + int32_t m_nSchaden; + int32_t m_nProzent; + int32_t m_nKapitaen; + int32_t m_nPartei; + int32_t m_nLadung; + int32_t m_nMaxLadung; + int32_t m_nKueste; + bool m_bCRKap; + int32_t m_nInsassen; + Effects m_coEffects; + VKommandos m_coKommandos; + static ShipTypes m_coShips; +}; + +class CTalent +{ +public: + CTalent(const std::string& sTyp, int32_t nTage, int32_t nStufe, int32_t nAddon = 0) + : m_sTyp(sTyp) + , m_nTage(nTage) + , m_nStufe(nStufe) + , m_nAddon(nAddon) + { + } + + std::string m_sTyp; + int32_t m_nTage; + int32_t m_nStufe; + int32_t m_nAddon; +}; + +class CGrenze : public CBlockBase +{ +public: + friend class CVorlage; + + CGrenze(CReportStream& oRS); + ~CGrenze(); + void Write(CReportStream& oRS); + + std::string Typ() const { return m_sTyp; } + + int32_t Richtung() const { return m_nRichtung; } + + int32_t Prozent() const { return m_nProzent; } + + const Effects& GetEffects() const { return m_coEffects; } + +protected: + int32_t m_nNummer; + std::string m_sTyp; + int32_t m_nRichtung; + int32_t m_nProzent; + Effects m_coEffects; +}; + +class CTalentSorter +{ +public: + CTalentSorter(int32_t nFlags) + : m_nFlags(nFlags) + { + } + + virtual ~CTalentSorter() {} + + bool operator()(const CTalent& oT1, const CTalent& oT2) const; + +protected: + int32_t m_nFlags; +}; + +class CEinheitenSorter +{ +public: + CEinheitenSorter(int32_t nFlags) + : m_nFlags(nFlags) + { + } + + virtual ~CEinheitenSorter() {} + + bool operator()(CEinheit* pE1, CEinheit* pE2) const; + +protected: + int32_t m_nFlags; +}; + +class CKampfzauber : public CBlockBase +{ +public: + CKampfzauber(CReportStream& oRS); + ~CKampfzauber(); +}; + +class CEinheit : public CBlockBase +{ +public: + friend class CEinheitenSorter; + friend class CMetaCommand; + friend class CVorlage; + friend class CRegion; + typedef std::vector KommandoZeilen; + typedef std::vector Talente; + typedef std::pair CGegenstand; + typedef std::vector Gegenstaende; + typedef std::vector Botschaften; + typedef std::vector Sprueche; + typedef std::list Messages; + typedef std::vector Kampfzauber; + + CEinheit(CReportStream& oRS, CRegion* poRegion); + ~CEinheit(); + void Write(CReportStream& oRS); + int32_t GetQuality() const; + using CBlockBase::GetValue; + Value GetValue(const std::string& sKey, const std::string& sKey2) const; + + std::string Typ() const { return m_sTyp; } + + std::string WahrerTyp() const { return m_sWahrerTyp; } + + std::string RealType() const { return m_sWahrerTyp.empty() ? m_sTyp : m_sWahrerTyp; } + + std::string PrefixedTyp(bool bWahr = false) const; + + std::string Name() const { return m_sName; } + + std::string Beschreibung() const { return m_sBeschreibung; } + + int32_t Runde() const { return m_poRegion ? m_poRegion->Runde() : 0; } + + int32_t GruppenID() const { return m_nGruppe; } + + std::string Gruppe() const + { + if (m_nGruppe > 0 && m_poRegion && m_poRegion->Map()) { + CGruppe::Ptr pG = m_poRegion->Map()->Report()->GetGruppe(m_nGruppe); + std::string sGruppe; + if (pG.get()) + sGruppe = pG->GetValue("name", Value("")).asString(); + return sGruppe; + } + return ""; + } + + CGruppe::Ptr GetGruppe() const + { + CGruppe::Ptr pG; + if (m_nGruppe > 0 && m_poRegion && m_poRegion->Map()) { + pG = m_poRegion->Map()->Report()->GetGruppe(m_nGruppe); + } + return pG; + } + + int32_t Nummer() const { return m_nNummer; } + + int32_t Partei() const { return m_nPartei; } + + int32_t Silber() const { return m_nSilber; } + + int32_t Anzahl() const { return m_nAnzahl; } + + int32_t Schiff() const { return m_nSchiff; } + + int32_t Bauwerk() const { return m_nBauwerk; } + + int32_t Aufenthaltsort() const { return m_nSchiff ? m_nSchiff + 0x10000000 : m_nBauwerk; } + + const CRegion* Region() const { return m_poRegion; } + + double Gewicht() const; + + std::string HP() const { return m_shp; } + + void CalcKapazitaeten(double& fKapReiten, double& fFKapReiten, int32_t& nRHO, double& fKapGehen, double& fFKapGehen, int32_t& nGHO) const; + void Kapazitaeten(const std::string& sTarget) const; + + const Talente& Talents() const { return m_coTalente; } + + const Gegenstaende& Things() const { return m_coGegenstaende; } + + const Kampfzauber& CSpells() const { return m_cpoKampfzauber; } + + const Effects& GetEffects() const { return m_coEffects; } + + void Vorlage(CRegion* poReg); + + void AddMessage(std::string sMsg) { m_coBotschaften.push_back(sMsg); } + + void AddMessage(CMessage::Ptr pMsg) { m_cpoMessages.push_back(pMsg); } + + void AddMaterialpool(CRegion::Materialpool& coPool, bool bSearchable); + + CEinheit* GlobalUnit(int32_t nENr); + + bool HasMetas() const; + + VKommandos& GetKommandos() { return m_csKommandos; } + + VKommandos& GetMetaOut() { return m_csMetaOut; } + + Botschaften& GetBotschaften() { return m_coBotschaften; } + +protected: + CRegion* m_poRegion; + int32_t m_nPlace; + int32_t m_nNummer; + int32_t m_nTemp; + int32_t m_nAlias; + std::string m_sName; + std::string m_sBeschreibung; + int32_t m_nPartei; + int32_t m_nVerkleidung; + int32_t m_nAnzahl; + std::string m_sTyp; + std::string m_sWahrerTyp; + int32_t m_nBauwerk; + int32_t m_nSchiff; + int32_t m_nSilber; + int32_t m_nGruppe; + int32_t m_nKampfStatus; + int32_t m_nBewacht; + int32_t m_nBelagert; + int32_t m_nParteitarnung; + int32_t m_nTarnung; + int32_t m_nAura; + int32_t m_nAuramax; + int32_t m_nHunger; + int32_t m_nVerraeter; + mutable double m_fKapReiten; + mutable double m_fFKapReiten; + mutable int32_t m_nRHO; + mutable double m_fKapGehen; + mutable double m_fFKapGehen; + mutable int32_t m_nGHO; + std::string m_sDefault; + std::string m_sPrivat; + std::string m_shp; + KommandoZeilen m_cnKomLines; + VKommandos m_csKommandos; + VKommandos m_csMetaOut; + Talente m_coTalente; + Gegenstaende m_coGegenstaende; + Botschaften m_coBotschaften; + Sprueche m_coSprueche; + Effects m_coEffects; + Messages m_cpoMessages; + Kampfzauber m_cpoKampfzauber; +}; + +extern std::string GetConfigFileName(); +extern void SetConfigFileName(const std::string& sFName); +extern bool ExistUserFunction(const std::string& sName); +extern bool DoUserFunction(const std::string& sName, ArgumentList& coArgs, Value* poVal); + +extern CKarte* g_poKarte; +extern CReport* g_poCurrentReport; +extern CRegion* g_poCurrentRegion; +extern CEinheit* g_poCurrentUnit; +extern CBauwerk* g_poCurrentBuilding; +extern CSchiff* g_poCurrentShip; diff --git a/EBase/ReportBase.cpp b/EBase/ReportBase.cpp new file mode 100644 index 0000000..16adaba --- /dev/null +++ b/EBase/ReportBase.cpp @@ -0,0 +1,68 @@ + +#include "ReportBase.h" +#include +#include "Utility.h" + +CReportObjID::CReportObjID(CReportStream& oRS) +{ + int num = oRS.GetNumDat(); + for (int i = 0; i < num; i++) { + m_coIDVals.push_back(Value((int32_t)oRS.GetDat(i))); + } +} + +CReportObjID::~CReportObjID() {} + +const CReportObjID& CReportObjID::operator=(const CReportObjID& oROID) +{ + m_coIDVals.assign(oROID.m_coIDVals.begin(), oROID.m_coIDVals.end()); + return *this; +} + +bool CReportObjID::operator==(const CReportObjID& oROID) +{ + int num = int(m_coIDVals.size()); + if (num != int(oROID.m_coIDVals.size())) + return false; + for (int i = 0; i < num; i++) { + if (m_coIDVals[size_t(i)] != oROID.m_coIDVals[size_t(i)]) + return false; + } + return true; +} + +bool CReportObjID::operator<(const CReportObjID& oROID) +{ + int num = int((std::min)(m_coIDVals.size(), oROID.m_coIDVals.size())); + for (int i = 0; i < num; i++) { + if (m_coIDVals[size_t(i)] >= oROID.m_coIDVals[size_t(i)]) + return false; + } + if (m_coIDVals.size() > oROID.m_coIDVals.size()) + return false; + return true; +} + +CReportObj::CReportObj(CReportStream& oRS) +{ + m_sName = oRS.GetValue(); + oRS.Next(); + while (!oRS.EOS() && oRS.GetType() != CReportStream::enBLOCK) { + // switch + oRS.Next(); + } +} + +CReportObj::~CReportObj() {} + +void CReportObj::Write(CReportStream& oRS) {} + +Value CReportObj::Get(const std::string& sName) const +{ + return Value(); +} + +Value CReportObj::Get(int32_t nSID) const +{ + return Value(); +} diff --git a/EBase/ReportBase.h b/EBase/ReportBase.h new file mode 100644 index 0000000..45b6f77 --- /dev/null +++ b/EBase/ReportBase.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/EBase/Report.h,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:55:53 $ + * $Revision: 1.10 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: ERESSEA-Datenklassen inclusive CR-Parser + ***************************************************************************** + * + * $Log: Report.h,v $ + * + *****************************************************************************/ +#pragma once + +#include +#include +#include +#include "ReportStream.h" + +class CReportObjID +{ +public: + CReportObjID(CReportStream& oRS); + ~CReportObjID(); + const CReportObjID& operator=(const CReportObjID& oROID); + bool operator==(const CReportObjID& oROID); + bool operator<(const CReportObjID& oROID); + +private: + std::vector m_coIDVals; +}; + +typedef std::vector CReportAttrib; + +class CReportObj +{ +public: + CReportObj(CReportStream& oRS); + virtual ~CReportObj(); + virtual void Write(CReportStream& oRS); + + Value Get(const std::string& sName) const; + Value Get(int32_t nSID) const; + + int32_t NumNN() const { return int(m_coNNAttribs.size()); } + + Value GetNN(int32_t nIdx) const { return m_coNNAttribs[size_t(nIdx)]; } + +private: + std::string m_sName; + std::map m_coAttribs; + std::map m_coSubObj; + std::vector m_coNNAttribs; +}; diff --git a/EBase/ReportStream.cpp b/EBase/ReportStream.cpp new file mode 100644 index 0000000..cd21087 --- /dev/null +++ b/EBase/ReportStream.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/EBase/ReportStream.cpp,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:55:53 $ + * $Revision: 1.5 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: ERESSEA-CR-Stream-IO, eine Hilfsklasse zum CR-Parsen + ***************************************************************************** + * + * $Log: ReportStream.cpp,v $ + * Revision 1.5 2000/02/24 09:55:53 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.4 1999/10/28 12:39:26 S.Schuemann + * - Änderungen für den Linux-Port + * + * Revision 1.3 1999/10/20 02:22:33 S.Schuemann + * - Anpassungen der Reportklassen fuer die Features von Vorlage 1.4 b 3 + * + * Revision 1.2 1999/10/18 21:31:46 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +//#include "StdAfx.h" +//#pragma hdrstop + +#include "ReportStream.h" +#include +#include "Utility.h" + +using namespace std; + +#define RSUSEIOSTREAM + +///////////////////////////////////////////////////////////////////// +//.class: CReportStream +///////////////////////////////////////////////////////////////////// + +CReportStream::CReportStream(const std::string& sFName, bool bRead) + : m_bEOS(false) + , m_bRead(bRead) + , m_bUnread(false) + , m_bUTF8(false) + , m_nLineNumber(0) + , m_pcBufferPos(0) + , m_pcBufferFill(0) +{ + m_coBuffer.resize(32768); + m_pcBufferPos = &m_coBuffer[0]; + m_pcBufferFill = &m_coBuffer[0]; +#ifdef RSUSEIOSTREAM + m_oIS.open(sFName.c_str(), bRead ? ios::in : ios::out); + if (m_oIS.fail()) { + m_bEOS = true; + // Message( "Auf die Datei kann nicht zugegriffen werden!" ); + } +#else + m_hFile = fopen(sFName.c_str(), "r+b"); + if (!m_hFile) { + m_bEOS = true; + } +#endif + // if( bRead ) + // PrepareLine(); +} + +CReportStream::~CReportStream() +{ +#ifdef RSUSEIOSTREAM + m_oIS.close(); +#else + fclose(m_hFile); +#endif +} + +bool CReportStream::GetLine(std::string& sLine) +{ + char* pcStart = m_pcBufferPos; + while (true) { + while (m_pcBufferPos < m_pcBufferFill && (unsigned char)*m_pcBufferPos && (unsigned char)*m_pcBufferPos != 10 && (unsigned char)*m_pcBufferPos != 13) { + m_pcBufferPos++; + } + if (m_pcBufferPos >= m_pcBufferFill) { +#ifdef RSUSEIOSTREAM + if (m_oIS.fail()) +#else + if (feof(m_hFile) || ferror(m_hFile)) +#endif + { + if (m_pcBufferPos == &m_coBuffer[0]) + return false; + sLine.assign(pcStart, size_t(m_pcBufferPos - pcStart)); + m_pcBufferPos = &m_coBuffer[0]; + m_pcBufferFill = &m_coBuffer[0]; + return true; + } + // umkopieren + char* pcPos = pcStart; + pcStart = &m_coBuffer[0]; + std::memcpy(pcStart, pcPos, size_t(m_pcBufferPos - pcPos)); + m_pcBufferPos -= pcPos - pcStart; + m_pcBufferFill -= pcPos - pcStart; + if (m_coBuffer.size() + pcStart - m_pcBufferFill == 0) { + size_t nPos = size_t(m_pcBufferPos - pcStart); + size_t nFill = size_t(m_pcBufferFill - pcStart); + m_coBuffer.resize(m_coBuffer.size() + 32768); + pcStart = &m_coBuffer[0]; + m_pcBufferPos = pcStart + nPos; + m_pcBufferFill = pcStart + nFill; + } +#ifdef RSUSEIOSTREAM + m_oIS.read(m_pcBufferFill, m_coBuffer.size() + pcStart - m_pcBufferFill); + m_pcBufferFill += m_oIS.gcount(); +#else + m_pcBufferFill += fread(m_pcBufferFill, 1, m_coBuffer.size() + pcStart - m_pcBufferFill, m_hFile); +#endif + } + else { + sLine.assign(pcStart, size_t(m_pcBufferPos - pcStart)); + while (m_pcBufferPos < m_pcBufferFill && (unsigned char)*m_pcBufferPos < 32) + m_pcBufferPos++; + // pcStart=m_pcBufferPos; + return true; + } + } +} + +void CReportStream::PrepareLine() +{ + char c; + // int i; + size_t p = 0; + + m_sValue = ""; + m_sComment = ""; + m_enType = enERROR; + m_coVals.clear(); + + if (m_bEOS) + return; + + do { + /* + getline( m_oIS, m_sLine ); + if( m_oIS.fail() ) + { + m_bEOS = true; + return; + } + */ + if (!GetLine(m_sLine)) { + m_bEOS = true; + return; + } + else { + // TODO: Real UTF8-Handling + if (!m_nLineNumber) { + // ggf. BOM l�schen + if (m_sLine.substr(0, 3) == "\xEF\xBB\xBF") + m_sLine.erase(0, 3); + } + if (m_bUTF8) + Utf8toIso885915(m_sLine); + } + + m_nLineNumber++; + + // while( !m_sLine.empty() && m_sLine[m_sLine.size()-1]<32 && m_sLine[m_sLine.size()-1]>0 ) + // m_sLine.erase( m_sLine.size()-1, 1 ); + } while (m_sLine.empty()); + + // i = 0; + if (!m_sLine.empty()) { + c = m_sLine[0]; + if (c == 34) { + p = m_sLine.find_last_of(34); + m_sValue = DeEscape(m_sLine.substr(1, p - 1)); + m_enType = enSTRING; + p++; + } + else if (c >= 'A' && c <= 'Z') { + p = m_sLine.find_first_of(" \t;", 1); + if (p == string::npos) + m_sValue = m_sLine; + else { + m_sValue = m_sLine.substr(0, p); + p = m_sLine.find_first_not_of(" \t", p); + if (p != string::npos && (m_sLine[p] == '-' || isdigit(m_sLine[p]))) { + do { + m_coVals.push_back(int32_t(std::strtol(m_sLine.c_str() + p, NULL, 10))); + p = m_sLine.find_first_of(" \t;", p); + if (p != string::npos) { + p = m_sLine.find_first_not_of(" \t", p); + } + } while (p != string::npos && m_sLine[p] != ';'); + } + } + m_enType = enBLOCK; + } + else if (c == '-' || isdigit(c)) { + do { + m_coVals.push_back(int32_t(std::strtol(m_sLine.c_str() + p, NULL, 10))); + p = m_sLine.find_first_of(" \t;,", p); + if (p != string::npos) { + p = m_sLine.find_first_not_of(" \t,", p); + } + } while (p != string::npos && m_sLine[p] != ';'); + m_enType = enINTEGER; + } + else { + char Buff[256]; + snprintf(Buff, sizeof(Buff), "Systax error in line %ld!\n", m_nLineNumber); + // Message( Buff ); + m_enType = enERROR; + } + if (m_enType != enERROR) { + if (p != string::npos && p < m_sLine.length() && m_sLine[p] == ';') { + m_sComment = m_sLine.substr(p + 1, 256); + AddKeyword(m_sComment); + } + } + } +} + +void CReportStream::WriteBlock(const std::string sValue, const std::string sComment, int32_t nDat1, int32_t nDat2, int32_t nDat3) +{ + if (nDat1 != 0x7fffffffL) { + if (nDat2 != 0x7fffffffL) { + if (nDat3 != 0x7fffffffL) + m_oIS << sValue << " " << nDat1 << " " << nDat2 << " " << nDat3; + else + m_oIS << sValue << " " << nDat1 << " " << nDat2; + } + else { + m_oIS << sValue << " " << nDat1; + } + } + else + m_oIS << sValue; + + if (!sComment.empty()) + m_oIS << ";" << sComment << endl; + else + m_oIS << endl; +} + +void CReportStream::WriteLine(const std::string sValue, const std::string sComment) +{ + m_oIS << "\x22" << sValue << "\x22"; + + if (!sComment.empty()) + m_oIS << ";" << sComment << endl; + else + m_oIS << endl; +} + +void CReportStream::WriteLine(int32_t nValue, const std::string sComment) +{ + m_oIS << nValue; + + if (!sComment.empty()) + m_oIS << ";" << sComment << endl; + else + m_oIS << endl; +} + +void CReportStream::WriteLine(int32_t nValue1, int32_t nValue2, const std::string sComment) +{ + m_oIS << nValue1 << " " << nValue2; + + if (!sComment.empty()) + m_oIS << ";" << sComment << endl; + else + m_oIS << endl; +} diff --git a/EBase/ReportStream.h b/EBase/ReportStream.h new file mode 100644 index 0000000..4df72fd --- /dev/null +++ b/EBase/ReportStream.h @@ -0,0 +1,103 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/EBase/ReportStream.h,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:55:53 $ + * $Revision: 1.2 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: ERESSEA-CR-Stream-IO, eine Hilfsklasse zum CR-Parsen + ***************************************************************************** + * $Log: ReportStream.h,v $ + * Revision 1.2 2000/02/24 09:55:53 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include + +#include "Value.h" + +class CReportStream +{ +public: + enum TOKEN { enBLOCK, enINTEGER, enSTRING, enCOMMENT, enERROR }; + + CReportStream(const std::string& sFName, bool bRead = true); + ~CReportStream(); + + bool EOS() const { return m_bEOS; } + + bool Next() + { + if (m_bUnread) { + m_bUnread = false; + } + else { + PrepareLine(); + } + return !m_bEOS; + } + + bool Unget() + { + if (m_bUnread) + return false; + m_bUnread = true; + return true; + } + + int32_t GetLine() { return m_nLineNumber; } + + TOKEN GetType() const { return m_enType; } + + bool IsBlock() const { return m_enType == enBLOCK; } + + const std::string& GetValue() const { return m_sValue; } + + const std::string& GetComment() const { return m_sComment; } + + const std::vector& Data() const { return m_coVals; } + + int32_t GetNumDat() const { return int32_t(m_coVals.size()); } + + int32_t GetDat(int nIdx) const { return nIdx < GetNumDat() ? m_coVals[size_t(nIdx)] : 0; } + + void WriteBlock(const std::string sValue, const std::string sComment = "", int32_t nDat1 = 0x7fffffffL, int32_t nDat2 = 0x7fffffffL, int32_t nDat3 = 0x7fffffffL); + void WriteLine(const std::string sValue, const std::string sComment = ""); + void WriteLine(int32_t nValue, const std::string sComment = ""); + void WriteLine(int32_t nValue1, int32_t nValue2, const std::string sComment = ""); + + void Utf8Mode(bool utf8) { m_bUTF8 = utf8; } + +protected: + bool GetLine(std::string& sLine); + void PrepareLine(); + +protected: + bool m_bEOS; + bool m_bRead; + bool m_bUnread; + bool m_bUTF8; + std::fstream m_oIS; + FILE* m_hFile; + int32_t m_nLineNumber; + std::string m_sLine; + std::string m_sValue; + std::vector m_coVals; + std::vector m_coBuffer; + char* m_pcBufferPos; + char* m_pcBufferFill; + TOKEN m_enType; + std::string m_sComment; +}; diff --git a/EBase/Utility.cpp b/EBase/Utility.cpp new file mode 100644 index 0000000..291d5fd --- /dev/null +++ b/EBase/Utility.cpp @@ -0,0 +1,2363 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/EBase/Utility.cpp,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:55:53 $ + * $Revision: 1.6 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Algemeine Utility-Funktionen + ***************************************************************************** + * + * $Log: Utility.cpp,v $ + * Revision 1.6 2000/02/24 09:55:53 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.5 1999/11/17 08:58:15 S.Schuemann + * - support für multiple CRs + * + * - vielfache Änderungen für Vorlage 1.4 beta 8 + * + * Revision 1.4 1999/11/03 10:21:38 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.3 1999/10/26 13:24:31 S.Schuemann + * - IsEqual() ignoriert nun Spaces + * + * Revision 1.2 1999/10/18 21:31:46 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#include "Utility.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Expression.h" +#include "Hash.h" +#include "Value.h" +#include "charencoding.h" +#include "regexp.h" +#include "utf8.hpp" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#ifndef __APPLE__ +#include +#endif +#include +#include +#include +#define _vsnprintf vsnprintf +#endif + +extern std::string GetConfigFileName(); + +using namespace std; + +// int32_t g_nFlags = 0; +std::set g_coFlags; +std::string g_sSrcFile; +int32_t g_nSrcLine = -1; +uint32_t g_nStepCount = 0; +uint32_t g_nLastErrorStep = ~0U; +int g_returnCode = 0; +std::shared_ptr g_pOutputMapper; +bool g_bUTF8 = false; + +Keywords g_coKeywords; + +// ISO-8859-1 tolower-Mapping (evtl. incl. DOS-Mapping) +int g_toLowerMapping[256] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + +#ifdef _WIN32 + // DOS-Mappings eingemischt (Ohne DOS-Beta bzw. �, da sonst ISO-� �berdeckt wird) + 0xe7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xe4, 0xe5, 0xe9, 0xe6, 0xe6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, 0xff, 0xf6, 0xfc, 0xa2, 0xa3, 0xa5, 0x9e, 0x9f, 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xf1, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +#else + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, + 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +#endif +}; + +std::string iso2utf8(const std::string& txt, bool doit) +{ + if (!doit) + return txt; + std::string str(txt); + return iso885915ToUtf8(str); +} + +std::string& iso885915ToUtf8(std::string& text) +{ + std::unique_ptr pUTF8; + + // MPF_DEBUGLOG(DecodingContext, 3, ("ToUTF8: '%s'") % text ); + for (std::string::const_iterator i = text.begin(); i != text.end(); ++i) { + if ((unsigned char)(*i) < 128) { + if (pUTF8) { + *pUTF8 += *i; + } + } + else if ((unsigned char)(*i) == 0xa4) { + if (!pUTF8) { + pUTF8.reset(new std::string()); + pUTF8->reserve(text.length() + 5); + pUTF8->assign((std::string::const_iterator)text.begin(), i); + } + *pUTF8 += char(0xe2); + *pUTF8 += char(0x82); + *pUTF8 += char(0xac); + } + else { + if (!pUTF8) { + pUTF8.reset(new std::string()); + pUTF8->reserve(text.length() + 5); + pUTF8->assign((std::string::const_iterator)text.begin(), i); + } + *pUTF8 += char((((unsigned char)(*i) >> 6) & 31) | 192); + *pUTF8 += char(((*i) & 63) | 128); + } + } + if (pUTF8) { + text = *pUTF8; + } + return text; +} + +std::string& Utf8toIso885915(std::string& text) +{ + std::unique_ptr pISO; + for (std::string::const_iterator i = text.begin(); i != text.end(); ++i) { + if ((unsigned char)(*i) < 128) { + if (pISO) { + *pISO += *i; + } + } + else if ((unsigned char)(*i) >= 0xe0) { + if (!pISO) { + pISO.reset(new std::string()); + pISO->reserve(text.length()); + pISO->assign((std::string::const_iterator)text.begin(), i); + } + if ((unsigned char)(*i++) == 0xe2) { + if (i != text.end() && (unsigned char)(*i++) == 0x82) { + if (i != text.end() && (unsigned char)(*i) == 0xac) { + *pISO += char(0xa4); + } + else { + *pISO += '?'; + } + } + else { + *pISO += '?'; + } + } + else { + *pISO += '?'; + } + } + else { + if (!pISO) { + pISO.reset(new std::string()); + pISO->reserve(text.length()); + pISO->assign((std::string::const_iterator)text.begin(), i); + } + char c = char(((*i++) & 31) << 6); + if (i != text.end()) { + *pISO += (char)(c | (*i & 63)); + } + } + if (i == text.end()) { + break; + } + } + if (pISO) { + text = *pISO; + } + return text; +} + +static void Default(void* pDat, const char* pszTxt) {} + +void (*g_pfErrorFunc)(void*, const char*) = Default; + +char* FormatMsg(const char* msg, ...) +{ + char* p = new char[4096]; + va_list list; + va_start(list, msg); + vsnprintf(p, 4096, msg, list); + return p; +} + +void ErrorMessage(void* pDat, const char* pszStr) +{ + static std::set coErrMsgPool; + bool bWarn = CRegExp::Match(pszStr, "(?i)(Warnung|Warning):"); + if (!IsFlag(VF_NOWARNINGS) || !bWarn) { + std::string sErr = std::string(pszStr) + "\n"; + if (!IsFlag(VF_SUPPRESSMULTIERRORS) || coErrMsgPool.find(sErr) == coErrMsgPool.end()) { + g_pfErrorFunc(pDat, sErr.c_str()); + coErrMsgPool.insert(sErr); + COutput::Target("stderr")->Write(sErr); + } + // Message( sErr.c_str() ); + } + if (!bWarn) { + g_nLastErrorStep = g_nStepCount; + g_returnCode = -1; + } + delete[] (char*)pszStr; +} + +/* +void VErrorMessage( const char* pszStr ) +{ + std::string sErr = std::string( pszStr ) + "\n"; + COutput::Target( "stderr" )->Write( sErr ); +// Message( sErr.c_str() ); + delete[] (char*)pszStr; +} +*/ + +void ConsoleMessage(const char* pszStr) +{ + if (!IsFlag(VF_NOCONSOLE)) { + COutput::Target("console")->Write(pszStr); + } + delete[] (char*)pszStr; +} + +void TraceMessage(const char* pszStr) +{ + COutput::Target("trace")->Write(pszStr); + // Message( pszStr ); + delete[] (char*)pszStr; +} + +bool g_bForceEOL = false; + +#ifndef _GRAPHICAL_ + +// extern FILE* g_hErr; +void Message(const char* pszStr) +{ + /* + static bool bEOL = true; + + int l = strlen( pszStr ); + if( g_bForceEOL || ( !bEOL && strchr( pszStr, ':' ) ) ) + { + fprintf( g_hErr, "\n" ); + } + fprintf( g_hErr, "%s", pszStr ); + if( l && ( pszStr[l-1]==10 || pszStr[l-1]==13 ) ) + bEOL = true; + else + bEOL = false; + g_bForceEOL = false; + */ +} + +#else + +void Message(const char* pszStr) {} + +#endif + +void (*g_pfHandler)(void); + +#ifdef _WIN32 + +BOOL WINAPI BreakHandler(DWORD nCtrlWord) +{ + if (nCtrlWord == CTRL_BREAK_EVENT) { + g_pfHandler(); + return TRUE; + } + else { + return FALSE; + } +} + +void SetBreakHandler(void (*pfHandler)(void)) +{ + SetConsoleCtrlHandler(BreakHandler, TRUE); + g_pfHandler = pfHandler; +} + +#else + +void SetBreakHandler(void (*pfHandler)(void)) +{ + g_pfHandler = pfHandler; +} + +#endif + +const char* itoa36(int i) +{ + static char s[8]; + char* dst; + char c; + int neg = 0; + + s[7] = 0; + dst = s + 6; + + if (i != 0) { + if (i < 0) { + i = -i; + neg = 1; + } + while (i) { + int x = i % 36; + i = i / 36; + if (x < 10) + *(dst--) = (char)('0' + x); + else { + c = (char)('a' + (x - 10)); + if (c == 'l') + c = 'L'; + *(dst--) = c; + } + } + if (neg) + *(dst) = '-'; + else + ++dst; + } + else + *dst = '0'; + + return dst; +} + +const char* itoan(int32_t i, int base) +{ + static char s[66]; + char* dst; + char c; + int neg = 0; + + s[65] = 0; + dst = s + 64; + + if (i != 0) { + if (i < 0) { + i = -i; + neg = 1; + } + while (i) { + int x = i % base; + i = i / base; + if (x < 10) + *(dst--) = (char)('0' + x); + else { + c = (char)('a' + (x - 10)); + if (c == 'l') + c = 'L'; + *(dst--) = c; + } + } + if (neg) + *(dst) = '-'; + else + ++dst; + } + else + *dst = '0'; + + return dst; +} + +int32_t EinheitenNummer(const std::string& sENStr) +{ + int32_t en; + if (IsFlag(VF_BASE36)) + en = (int32_t)strtol(sENStr.c_str(), NULL, 36); + else + en = (int32_t)strtol(sENStr.c_str(), NULL, 10); + return en; +} + +int32_t FindNextENum(const std::string& sTxt, std::string::size_type& p) +{ + std::string::size_type p1, p2 = 0, ph; + + p1 = size_t(p); + + while (p1 < sTxt.size()) { + p1 = sTxt.find('(', p1); + if (p1 == std::string::npos) + return -1; + + p2 = sTxt.find(')', p1); + if (p2 == std::string::npos) + return -1; + + ph = sTxt.find(',', p1); + if (ph == std::string::npos || ph > p2) + break; + + p1++; + } + if (p1 >= sTxt.size()) { + return -1; + } + + if (!p2) { + return -1; + } + p = p2; + return EinheitenNummer(sTxt.substr(p1 + 1, p2 - p1 - 1)); +} + +bool FindNextRegion(const std::string& sTxt, std::string::size_type& p, int& x, int& y, int& z) +{ + size_t p1, p2 = std::string::npos, ph; + + p1 = size_t(p); + + while (p1 < sTxt.size()) { + p1 = sTxt.find('(', p1); + if (p1 == std::string::npos) + return false; + + p2 = sTxt.find(')', p1); + if (p2 == std::string::npos) + return false; + + ph = sTxt.find(',', p1); + if (ph != std::string::npos && ph < p2) + break; + + p1++; + } + if (p1 >= sTxt.size()) + return false; + + if (p2 != std::string::npos) { + p = p2; + } + x = int(atol(sTxt.c_str() + p1 + 1)); + ph = sTxt.find(',', p1 + 1); + if (ph != std::string::npos) { + y = int(atol(sTxt.c_str() + ph + 1)); + ph = sTxt.find(',', ph + 1); + if (ph != std::string::npos) { + z = int(atol(sTxt.c_str() + ph + 1)); + } + else + z = 0; + } + + return true; +} + +namespace detail { +struct DecodeResult +{ + char32_t cp = 0; + std::size_t len = 0; + bool valid = false; +}; + +inline DecodeResult try_decode_utf8(std::string_view s, std::size_t pos) +{ + const std::size_t rem = s.size() - pos; + const auto b0 = static_cast(s[pos]); + + if (b0 <= 0x7F) + return {b0, 1, true}; + + if (b0 < 0xC2) + return {}; + + if (b0 <= 0xDF) { + if (rem < 2) + return {}; + const auto b1 = static_cast(s[pos + 1]); + if ((b1 & 0xC0) != 0x80) + return {}; + + return {static_cast(((b0 & 0x1F) << 6) | (b1 & 0x3F)), 2, true}; + } + + if (b0 <= 0xEF) { + if (rem < 3) + return {}; + const auto b1 = static_cast(s[pos + 1]); + const auto b2 = static_cast(s[pos + 2]); + + if ((b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80) + return {}; + + if (b0 == 0xE0 && b1 < 0xA0) + return {}; + if (b0 == 0xED && b1 >= 0xA0) + return {}; + + return {static_cast(((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F)), 3, true}; + } + + if (b0 <= 0xF4) { + if (rem < 4) + return {}; + const auto b1 = static_cast(s[pos + 1]); + const auto b2 = static_cast(s[pos + 2]); + const auto b3 = static_cast(s[pos + 3]); + + if ((b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) + return {}; + + if (b0 == 0xF0 && b1 < 0x90) + return {}; + if (b0 == 0xF4 && b1 > 0x8F) + return {}; + + return {static_cast(((b0 & 0x07) << 18) | ((b1 & 0x3F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)), 4, true}; + } + + return {}; +} + +inline std::u32string decode_mixed_utf8_latin1(std::string_view input) +{ + std::u32string out; + out.reserve(input.size()); + + std::size_t i = 0; + while (i < input.size()) { + DecodeResult r = try_decode_utf8(input, i); + if (r.valid) { + out.push_back(r.cp); + i += r.len; + } + else { + out.push_back(static_cast(input[i])); + ++i; + } + } + + return out; +} + +struct Replacement +{ + std::u32string_view from; + std::u32string_view to; +}; + +inline void append_folded_latin1(std::string& out, char32_t cp, char replacement) +{ + if (cp <= 0xFF) { + out.push_back(static_cast(static_cast(cp))); + return; + } + + switch (cp) { + case U'\u2018': // ‘ + case U'\u2019': // ’ + case U'\u2032': // ′ + out.push_back('\''); + return; + + case U'\u201C': // “ + case U'\u201D': // ” + case U'\u2033': // ″ + out.push_back('"'); + return; + + case U'\u2013': // – + case U'\u2014': // — + case U'\u2212': // − + out.push_back('-'); + return; + + case U'\u2026': // … + out += "..."; + return; + + case U'\u00A0': // nbsp + out.push_back(' '); + return; + + default: + out.push_back(replacement); + return; + } +} + +inline std::string encode_latin1_with_folding(std::u32string_view input, char replacement) +{ + std::string out; + out.reserve(input.size()); + + for (char32_t cp : input) + append_folded_latin1(out, cp, replacement); + + return out; +} + +inline bool starts_with_at(std::u32string_view text, std::size_t pos, std::u32string_view needle) +{ + return pos + needle.size() <= text.size() && std::equal(needle.begin(), needle.end(), text.begin() + pos); +} + +inline std::u32string apply_replacement_table(std::u32string_view input) +{ + // Conservative, explicit mojibake table. + // Left side: common bad text as it appears after UTF-8 bytes were read as Latin-1. + // Right side: intended Unicode text. + static constexpr std::u32string_view A_umlaut_bad = U"ä"; + static constexpr std::u32string_view O_umlaut_bad = U"ö"; + static constexpr std::u32string_view U_umlaut_bad = U"ü"; + static constexpr std::u32string_view A_umlaut_cap_bad = U"Ä"; + static constexpr std::u32string_view O_umlaut_cap_bad = U"Ö"; + static constexpr std::u32string_view U_umlaut_cap_bad = U"Ü"; + static constexpr std::u32string_view sz_bad = U"ß"; + + static constexpr std::u32string_view agrave_bad = U"à "; + static constexpr std::u32string_view aacute_bad = U"á"; + static constexpr std::u32string_view acirc_bad = U"â"; + static constexpr std::u32string_view atilde_bad = U"ã"; + static constexpr std::u32string_view aring_bad = U"Ã¥"; + static constexpr std::u32string_view aelig_bad = U"æ"; + static constexpr std::u32string_view cced_bad = U"ç"; + static constexpr std::u32string_view egrave_bad = U"è"; + static constexpr std::u32string_view eacute_bad = U"é"; + static constexpr std::u32string_view ecirc_bad = U"ê"; + static constexpr std::u32string_view euml_bad = U"ë"; + static constexpr std::u32string_view igrave_bad = U"ì"; + static constexpr std::u32string_view iacute_bad = U"í"; + static constexpr std::u32string_view icirc_bad = U"î"; + static constexpr std::u32string_view iuml_bad = U"ï"; + static constexpr std::u32string_view ntilde_bad = U"ñ"; + static constexpr std::u32string_view ograve_bad = U"ò"; + static constexpr std::u32string_view oacute_bad = U"ó"; + static constexpr std::u32string_view ocirc_bad = U"ô"; + static constexpr std::u32string_view otilde_bad = U"õ"; + static constexpr std::u32string_view oslash_bad = U"ø"; + static constexpr std::u32string_view ugrave_bad = U"ù"; + static constexpr std::u32string_view uacute_bad = U"ú"; + static constexpr std::u32string_view ucirc_bad = U"û"; + static constexpr std::u32string_view yacute_bad = U"ý"; + static constexpr std::u32string_view thorn_bad = U"þ"; + + static constexpr std::u32string_view Agrave_bad = U"À"; + static constexpr std::u32string_view Aacute_bad = U"Ã�"; + static constexpr std::u32string_view Acirc_bad = U"Â"; + static constexpr std::u32string_view Atilde_bad = U"Ã"; + static constexpr std::u32string_view Aring_bad = U"Ã…"; + static constexpr std::u32string_view Aelig_bad = U"Æ"; + static constexpr std::u32string_view Cced_bad = U"Ç"; + static constexpr std::u32string_view Egrave_bad = U"È"; + static constexpr std::u32string_view Eacute_bad = U"É"; + static constexpr std::u32string_view Ecirc_bad = U"Ê"; + static constexpr std::u32string_view Euml_bad = U"Ë"; + static constexpr std::u32string_view Igrave_bad = U"ÃŒ"; + static constexpr std::u32string_view Iacute_bad = U"Ã�"; + static constexpr std::u32string_view Icirc_bad = U"ÃŽ"; + static constexpr std::u32string_view Iuml_bad = U"Ã�"; + static constexpr std::u32string_view Ntilde_bad = U"Ñ"; + static constexpr std::u32string_view Ograve_bad = U"Ã’"; + static constexpr std::u32string_view Oacute_bad = U"Ó"; + static constexpr std::u32string_view Ocirc_bad = U"Ô"; + static constexpr std::u32string_view Otilde_bad = U"Õ"; + static constexpr std::u32string_view Oslash_bad = U"Ø"; + static constexpr std::u32string_view Ugrave_bad = U"Ù"; + static constexpr std::u32string_view Uacute_bad = U"Ú"; + static constexpr std::u32string_view Ucirc_bad = U"Û"; + static constexpr std::u32string_view Yacute_bad = U"Ã�"; + static constexpr std::u32string_view Thorn_bad = U"Þ"; + + static constexpr std::u32string_view nbsp_bad = U" "; + static constexpr std::u32string_view pound_bad = U"£"; + static constexpr std::u32string_view section_bad = U"§"; + static constexpr std::u32string_view copy_bad = U"©"; + static constexpr std::u32string_view reg_bad = U"®"; + static constexpr std::u32string_view degree_bad = U"°"; + static constexpr std::u32string_view plusmn_bad = U"±"; + + static constexpr std::u32string_view rsquo_bad = U"’"; + static constexpr std::u32string_view lsquo_bad = U"‘"; + static constexpr std::u32string_view rdquo_bad = U"â€�"; + static constexpr std::u32string_view ldquo_bad = U"“"; + static constexpr std::u32string_view endash_bad = U"–"; + static constexpr std::u32string_view emdash_bad = U"—"; + static constexpr std::u32string_view ellipsis_bad = U"…"; + static constexpr std::u32string_view bullet_bad = U"•"; + static constexpr std::u32string_view euro_bad = U"€"; + + static constexpr Replacement table[] = {{A_umlaut_bad, U"ä"}, {O_umlaut_bad, U"ö"}, {U_umlaut_bad, U"ü"}, {A_umlaut_cap_bad, U"Ä"}, {O_umlaut_cap_bad, U"Ö"}, {U_umlaut_cap_bad, U"Ü"}, {sz_bad, U"ß"}, + + {agrave_bad, U"à"}, {aacute_bad, U"á"}, {acirc_bad, U"â"}, {atilde_bad, U"ã"}, {aring_bad, U"å"}, {aelig_bad, U"æ"}, {cced_bad, U"ç"}, {egrave_bad, U"è"}, {eacute_bad, U"é"}, + {ecirc_bad, U"ê"}, {euml_bad, U"ë"}, {igrave_bad, U"ì"}, {iacute_bad, U"í"}, {icirc_bad, U"î"}, {iuml_bad, U"ï"}, {ntilde_bad, U"ñ"}, {ograve_bad, U"ò"}, {oacute_bad, U"ó"}, + {ocirc_bad, U"ô"}, {otilde_bad, U"õ"}, {oslash_bad, U"ø"}, {ugrave_bad, U"ù"}, {uacute_bad, U"ú"}, {ucirc_bad, U"û"}, {yacute_bad, U"ý"}, {thorn_bad, U"þ"}, + + {Agrave_bad, U"À"}, {Aacute_bad, U"Á"}, {Acirc_bad, U"Â"}, {Atilde_bad, U"Ã"}, {Aring_bad, U"Å"}, {Aelig_bad, U"Æ"}, {Cced_bad, U"Ç"}, {Egrave_bad, U"È"}, {Eacute_bad, U"É"}, + {Ecirc_bad, U"Ê"}, {Euml_bad, U"Ë"}, {Igrave_bad, U"Ì"}, {Iacute_bad, U"Í"}, {Icirc_bad, U"Î"}, {Iuml_bad, U"Ï"}, {Ntilde_bad, U"Ñ"}, {Ograve_bad, U"Ò"}, {Oacute_bad, U"Ó"}, + {Ocirc_bad, U"Ô"}, {Otilde_bad, U"Õ"}, {Oslash_bad, U"Ø"}, {Ugrave_bad, U"Ù"}, {Uacute_bad, U"Ú"}, {Ucirc_bad, U"Û"}, {Yacute_bad, U"Ý"}, {Thorn_bad, U"Þ"}, + + {nbsp_bad, U" "}, {pound_bad, U"£"}, {section_bad, U"§"}, {copy_bad, U"©"}, {reg_bad, U"®"}, {degree_bad, U"°"}, {plusmn_bad, U"±"}, + + {rsquo_bad, U"’"}, {lsquo_bad, U"‘"}, {rdquo_bad, U"”"}, {ldquo_bad, U"“"}, {endash_bad, U"–"}, {emdash_bad, U"—"}, {ellipsis_bad, U"…"}, {bullet_bad, U"•"}, {euro_bad, U"€"}}; + + std::u32string out; + out.reserve(input.size()); + + std::size_t i = 0; + while (i < input.size()) { + bool matched = false; + + for (const auto& r : table) { + if (starts_with_at(input, i, r.from)) { + out.append(r.to); + i += r.from.size(); + matched = true; + break; + } + } + + if (!matched) { + out.push_back(input[i]); + ++i; + } + } + + return out; +} +} // namespace detail + +std::string mixed_utf8_latin1_to_latin1(std::string_view input, char replacement) +{ + std::u32string decoded = detail::decode_mixed_utf8_latin1(input); + std::u32string repaired = detail::apply_replacement_table(decoded); + return detail::encode_latin1_with_folding(repaired, replacement); +} + +std::string DeUmlaut(const std::string& sText) +{ + std::string::const_iterator is; + static char Buff[256]; + char* str = Buff; + int l = 0; + + is = sText.begin(); + while (is != sText.end()) { + switch (*is) { + case char(0xE4): + *str++ = 'a'; + *str++ = 'e'; + break; // Latin-1: ä + case char(0xF6): + *str++ = 'o'; + *str++ = 'e'; + break; // Latin-1: ö + case char(0xFC): + *str++ = 'u'; + *str++ = 'e'; + break; // Latin-1: ü + case char(0xC4): + *str++ = 'a'; + *str++ = 'e'; + break; // Latin-1: Ä + case char(0xD6): + *str++ = 'o'; + *str++ = 'e'; + break; // Latin-1: Ö + case char(0xDC): + *str++ = 'u'; + *str++ = 'e'; + break; // Latin-1: Ü + case char(0xDF): + *str++ = 's'; + *str++ = 's'; + break; // Latin-1: ß +#ifdef _WIN32 + case char(132): // DOS-ä + case char(142): // DOS-Ä + *str++ = 'a'; + *str++ = 'e'; + break; + case char(148): // DOS-ö + case char(153): // DOS-Ö + *str++ = 'o'; + *str++ = 'e'; + break; + case char(129): // DOS-ü + case char(154): // DOS-Ü + *str++ = 'u'; + *str++ = 'e'; + break; + case char(225): // DOS-ß + *str++ = 's'; + *str++ = 's'; + break; +#endif + default: + *str++ = ToLower(*is); + } + is++; + if (++l > 250) + break; + } + *str = 0; + return std::string(Buff); +} + +bool IsEqual(const char* pcS1i, const char* pcS2i) +{ +#ifdef _WIN32 + static const unsigned char pcUml[] = {0xc4, 0xd6, 0xdc, 0xe4, 0xf6, 0xfc, 0xdf, 142, 153, 154, 132, 148, 129, 225, 0}; + static const unsigned char pcAlt[] = "aouaousaouaous"; +#else + static const unsigned char pcUml[] = {0xc4, 0xd6, 0xdc, 0xe4, 0xf6, 0xfc, 0xdf, 0}; + static const unsigned char pcAlt[] = "aouaous"; +#endif + const unsigned char* pcS1 = (const unsigned char*)pcS1i; + const unsigned char* pcS2 = (const unsigned char*)pcS2i; + const unsigned char* h1; + const unsigned char* h2; + unsigned char c1, c2; + while (*pcS1 && *pcS2) { + do { + while (' ' == (c1 = uint8_t(ToLower(*pcS1++)))) + ; + } while (c1 == '~'); + do { + while (' ' == (c2 = uint8_t(ToLower(*pcS2++)))) + ; + } while (c2 == '~'); + if (!c1 || !c2) { + return (c1 == c2); + } + if (c1 != c2) { + h1 = (const unsigned char*)strchr((const char*)pcUml, c1); + h2 = (const unsigned char*)strchr((const char*)pcUml, c2); +#ifdef _WIN32 + if (h1 > pcUml + 6) + h1 -= 6; + if (h2 > pcUml + 6) + h2 -= 6; +#endif + if (h1 == NULL && h2 == NULL) + return false; + if (h1 && h2) { + if (h1 > h2) { + const unsigned char* ht = h2; + h2 = h1; + h1 = ht; + } + + if ((*h1 == (unsigned char)0xc4 && *h2 != (unsigned char)0xe4) || (*h1 == (unsigned char)0xd6 && *h2 != (unsigned char)0xf6) || (*h1 == (unsigned char)0xdc && *h2 != (unsigned char)0xfc)) + return false; + } + else { + if (h2) { + h1 = h2; + h2 = pcS1++; + } + else { + h2 = pcS2++; + c1 = c2; + } + if (pcAlt[h1 - pcUml] != c1) + return false; + if (h1 - pcUml < 6) { + if (*h2 != 'e' && *h2 != 'E') + return false; + } + else { + if (*h2 != 's' && *h2 != 'S') + return false; + } + } + } + } + + while (*pcS1 == ' ' || *pcS1 == '~') + pcS1++; + while (*pcS2 == ' ' || *pcS2 == '~') + pcS2++; + + return (!*pcS1 && !*pcS2); +} + +bool IsEqual(const std::string& sS1, const char* pcS2) +{ + return IsEqual(sS1.c_str(), pcS2); +} + +bool IsMetaCommand(const std::string& sLine) +{ + std::string::const_iterator si; + si = sLine.begin(); + while (si != sLine.end() && (*si == 32 || *si == 9)) + si++; + if (si == sLine.end() || *si++ != '/') + return false; + if (si == sLine.end() || *si++ != '/') + return false; + while (si != sLine.end() && (*si == 32 || *si == 9)) + si++; + return si != sLine.end() && *si == '#'; +} + +std::string Wrap(std::string& sTxt, size_t len) +{ + std::string sOut; + + if (sTxt.length() <= size_t(len)) { + sOut = sTxt; + sTxt = ""; + } + else { + size_t nPos, nKom; + nPos = sTxt.find_last_of(" \t", size_t(len)); + nKom = sTxt.find_last_of(",", size_t(len)); + if (nPos != std::string::npos) { + if (nKom != std::string::npos && nPos - nKom > 0 && nPos - nKom < 5) + nPos = nKom + 1; + sOut = sTxt.substr(0, nPos); + sTxt.erase(0, nPos); + } + else { + sOut = sTxt.substr(0, len); + sTxt.erase(0, len); + } + nPos = sTxt.find_first_not_of(" \t"); + if (nPos != std::string::npos) + sTxt.erase(0, nPos); + else + sTxt = ""; + } + return sOut; +} + +std::string ToString(int32_t n) +{ + return std::to_string(n); +} + +std::string ToString(double f, unsigned n) +{ + std::stringstream os; + os << std::fixed << std::setprecision(n > 8 ? 8 : static_cast(n)) << f; + return os.str(); +} + +std::string ToStringS(int32_t n) +{ + return n < 0 ? std::to_string(n) : "+" + std::to_string(n); +} + +std::string ToStringS(double f, unsigned n) +{ + return f < 0 ? ToString(f, n) : "-" + ToString(f, n); +} + +typedef std::mersenne_twister_engine mt11213b_t; + +int32_t Random(int32_t seed) +{ + static mt11213b_t prng; + static bool bInit = false; + + if (!bInit) { + uint32_t nSeed = (uint32_t)(seed); + if (!seed) { + // 0x6E788FA0 + time_t nSeedStart = time(0); + int32_t nCount = 0; + while (time(0) == nSeedStart) + nCount++; + // nSeedStart = 0x42C123E6; + // nCount = 0x15555E; + char pcSeed[1024]; + snprintf(pcSeed, sizeof(pcSeed), "%s, %ld, %ld", ctime(&nSeedStart), (long)nSeedStart, nCount); + nSeed = Hash((const unsigned char*)pcSeed, (uint32_t)strlen(pcSeed), 4711); + } + TRACEMSG(("Random used, seed: 0x%lX\n", nSeed)); + prng.seed((mt11213b_t::result_type)nSeed); + bInit = true; + return seed ? seed : prng() & 0x7fffffffL; + } + else { + return prng() & 0x7fffffffL; + } +} + +bool FileExists(const std::string& sFName) +{ + FILE* fh; + + // fprintf( stderr, "checke nach %s...\n", sFName.c_str() ); + fh = fopen(sFName.c_str(), "r"); + if (fh) + fclose(fh); + return fh ? true : false; +} + +bool IsConsole(FILE* hFile) +{ +#ifdef _WIN32 + struct _stat oStat; + _fstat(_fileno(hFile), &oStat); + return (oStat.st_mode & _S_IFCHR) != 0; +#else + struct stat oStat; + fstat(fileno(hFile), &oStat); + return (oStat.st_mode & S_IFCHR) != 0; +#endif +} + +bool GetConsoleSize(int& width, int& height) +{ +#if defined(_WIN32) + { + CONSOLE_SCREEN_BUFFER_INFO scr; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr)) { + height = scr.srWindow.Bottom - scr.srWindow.Top + 1; + width = scr.srWindow.Right - scr.srWindow.Left + 1; + } + else + return false; + } +#elif defined(TIOCGWINSZ) + { + struct winsize w; + int nFile; + if (IsConsole(stderr)) + nFile = 2; + else if (IsConsole(stderr)) + nFile = 1; + else + return false; + if (ioctl(nFile, TIOCGWINSZ, &w) == 0) { + if (w.ws_row > 0) + height = w.ws_row; + if (w.ws_col > 0) + width = w.ws_col; + } + } +#elif defined(WIOCGETD) + { + struct uwdata w; + int nFile; + if (IsConsole(stderr)) + nFile = 2; + else if (IsConsole(stderr)) + nFile = 1; + else + return false; + if (ioctl(nFile, WIOCGETD, &w) == 0) { + if (w.uw_height > 0) + height = w.uw_height / w.uw_vs; + if (w.uw_width > 0) + width = w.uw_width / w.uw_hs; + } + } +#endif + return width > 0 && height > 0; +} + +int _nLineCount = 0; +std::map _bStreamMap; + +void WriteToConsole(FILE* hFile, const char* pcText) +{ + static int width, height; + static int nPos = 0; + static char pcBuff[512]; +#ifdef _WIN32 + static char pcOEMBuff[4096]; +#endif +#ifdef DARWIN + static CharacterMapper oLatinToMacConsole("iso-8859-1", "macintosh"); +#endif + std::string sOut; + + std::map::const_iterator si = _bStreamMap.find(hFile); + if (si == _bStreamMap.end()) { + si = _bStreamMap.insert(std::map::value_type(hFile, IsConsole(hFile))).first; + } + + if ((*si).second) { +#ifdef _WIN32 + CharToOem(pcText, pcOEMBuff); + pcText = pcOEMBuff; +#elif defined(DARWIN) + sOut = oLatinToMacConsole.Map(std::string(pcText)); + pcText = sOut.c_str(); +#endif + } + else { + if (g_pOutputMapper->IsOK()) { + sOut = g_pOutputMapper->Map(pcText); + pcText = sOut.c_str(); + } + } + + if ((*si).second && IsFlag(VF_PAGER)) { + char* pStr = pcBuff; + + if (!_nLineCount) { + if (!GetConsoleSize(width, height)) { + width = 80; + height = 25; + } + } + + while (*pcText) { + while (*pcText && nPos < width) { + switch (*pcText) { + case 9: { + int nEnd = (nPos & 3) + 8; + while (nPos < nEnd) { + if (nPos++ < width) + *pStr++ = ' '; + } + if (nPos >= width) { + *pStr = 0; + fprintf(hFile, "%s\n", pcBuff); + _nLineCount++; + pStr = pcBuff; + nPos -= width; + if (_nLineCount >= height - 1 && height > 5) { + fprintf(hFile, "[Weiter mit der Eingabetaste]"); + InputFromConsole(); + } + } + nEnd = 0; + while (nEnd++ < nPos) + *pStr++ = ' '; + } break; + case 10: + *pStr = 0; + fprintf(hFile, "%s\n", pcBuff); + _nLineCount++; + nPos = 0; + pStr = pcBuff; + if (_nLineCount >= height - 1 && height > 5) { + fprintf(hFile, "[Weiter mit der Eingabetaste]"); + InputFromConsole(); + } + break; + case 12: + break; + case 13: + *pStr++ = 13; + *pStr = 0; + fprintf(hFile, "%s", pcBuff); + nPos = 0; + break; + default: + *pStr++ = *pcText; + nPos++; + } + pcText++; + } + if (*pcText) { + *pStr = 0; + fprintf(hFile, "%s", pcBuff); + _nLineCount++; + nPos = 0; + pStr = pcBuff; + if (_nLineCount >= height - 1 && height > 5) { + fprintf(hFile, "[Weiter mit der Eingabetaste]"); + InputFromConsole(); + } + } + } + if (nPos) { + *pStr = 0; + fprintf(hFile, "%s", pcBuff); + } + } + else { + fprintf(hFile, "%s", pcText); + } +} + +std::string InputFromConsole() +{ + static char pcBuffer[1024]; +#ifdef _WIN32 + static char pcOEMBuffer[1024]; + auto res = std::fgets(pcOEMBuffer, 1023, stdin); + OemToChar(pcOEMBuffer, pcBuffer); +#else + auto res = std::fgets(pcBuffer, 1023, stdin); +#endif + _nLineCount = 0; + return res ? std::string(pcBuffer) : ""; +} + +std::string GetExecutablePathname() +{ +#ifdef _WIN32 + char pcBuffer[1024]; + int i = GetModuleFileName(NULL, pcBuffer, 1023); + return std::string(pcBuffer); +#else + return std::string(""); +#endif +} + +std::string PathedFileName(const std::string& sFName) +{ + std::string sFileName(sFName); + std::string sPath; + + if (!FileExists(sFileName)) { + CConfigFile oCF(GetConfigFileName()); + size_t i = 1; + while (true) { + if (!oCF.FetchLine("Path", i++)) + break; + sPath = oCF.GetString(0); + +#ifdef _WIN32 + if (!sPath.empty() && sPath[sPath.length() - 1] != '\\' && sPath[sPath.length() - 1] != '/') + sPath += '\\'; +#else + if (!sPath.empty() && sPath[sPath.length() - 1] != '/') + sPath += '/'; +#endif + if (FileExists(sPath + sFileName)) { + sFileName = sPath + sFileName; + break; + } + } + } + return sFileName; +} + +#ifndef _NEWSPLIT +void split(Args& dest, const std::string& s, const std::string& sep, const std::string& caps, char esc) +{ + std::string::size_type left; + std::string::size_type right; + // std::string::size_type cpos; + std::string fullsep = sep + caps; + std::string fullcap; + char c, cap; + + if (esc) + fullcap += esc; + + right = 0; + + while (true) { + left = s.find_first_not_of(sep, right); + if (left == std::string::npos) + return; + else { + c = s[left]; + if (caps.find(c) != std::string::npos) { + cap = c; + fullcap = c; + if (esc) + fullcap += esc; + left++; + } + else { + cap = 0; + fullcap = sep; + } + } + + right = left; + do { + right = s.find_first_of(fullcap, right); + if (right == std::string::npos) { + right = s.size(); + c = sep[0]; + } + else + c = s[right]; + + if (c == esc) + right = (right + 2 > s.size()) ? s.size() : right + 2; + } while (c == esc); + + dest.push_back(s.substr(left, right - left)); + + if (cap) + right++; + } +} +#else +void split(Args& dest, const std::string& s, const std::string& sep, const std::string& caps, char esc) +{ + std::string::size_type left; + std::string::size_type right; + // std::string::size_type cpos; + std::string fullsep = sep + caps; + std::string fullcap; + char c, cap; + + if (esc) + fullcap += esc; + + right = 0; + + while (true) { + left = s.find_first_not_of(sep, right); + if (left == std::string::npos) + return; + else { + int p = left; + do { + p = sCom.find_first_of(sep + caps "\t \x27", p); + if (p == std::string::npos) { + p = sCom.size(); + } + else { + if (sCom[p] == '\x27') { + do { + p = sCom.find_first_of("\\\x27", p + 1); + if (p == std::string::npos) + p = sCom.size(); + else { + if (sCom[p] == '\\') { + if (p < sCom.size()) + p++; + } + else { + p++; + break; + } + } + } while (p < sCom.size()); + } + else { + break; + } + } + } while (p < sCom.size()); + + c = s[left]; + if (caps.find(c) != std::string::npos) { + cap = c; + fullcap = c; + if (esc) + fullcap += esc; + left++; + } + else { + cap = 0; + fullcap = sep; + } + } + + right = left; + do { + right = s.find_first_of(fullcap, right); + if (right == std::string::npos) { + right = s.size(); + c = sep[0]; + } + else + c = s[right]; + + if (c == esc) + right = (right + 2 > s.size()) ? s.size() : right + 2; + } while (c == esc); + + dest.push_back(s.substr(left, right - left)); + + if (cap) + right++; + } +} +#endif + +std::string g_LastConfigFileName; + +CConfigFile::CConfigFile(const std::string& sFName) + : m_sFName(sFName) + , m_nLine(0) +{ + std::fstream oIS; + std::fstream oIS2; + size_t nLineCount = 0; + + // if( sFName != g_LastConfigFileName ) + // fprintf( stderr, "Configfile '%s' benutzt.\n", sFName.c_str() ); + + if (!sFName.empty()) + oIS.open(sFName.c_str(), std::ios::in); + else { + ERRMSG(0, ("FEHLER: Interner Fehler beim Zugriff auf Konfigurations-Daten!")); + } + + if (oIS.fail()) { + if (sFName != g_LastConfigFileName) { + ERRMSG(0, ("FEHLER: Auf die Datei '%s' kann nicht zugegriffen werden!", sFName.c_str())); + g_LastConfigFileName = sFName; + } + return; + } + + g_LastConfigFileName = sFName; + + std::string sFName2(sFName); + CRegExp::RuledReplace(sFName2, "(.*)\\.(.+)rc", "$1.$2-user-rc"); + CRegExp::RuledReplace(sFName2, "(.+)\\.cfg", "$1-user.cfg"); + if (sFName == sFName2) { + sFName2 = ""; + } + + while (1) { + getline(oIS, m_sLine); + if (oIS.fail()) + break; + nLineCount++; + while (!m_sLine.empty() && m_sLine[m_sLine.size() - 1] <= 32) + m_sLine.erase(m_sLine.size() - 1, 1); + while (!m_sLine.empty() && m_sLine[0] <= 32) + m_sLine.erase(0, 1); + if (!m_sLine.empty() && m_sLine[0] != ';') { + m_csLines.push_back(m_sLine); + m_cnPos.push_back(nLineCount); + } + } + + oIS.close(); + + nLineCount = 0; + if (!sFName2.empty()) + oIS2.open(sFName2.c_str(), std::ios::in); + else { + return; + } + + if (oIS2.fail()) { + return; + } + + while (1) { + getline(oIS2, m_sLine); + if (oIS2.fail()) + break; + nLineCount++; + while (!m_sLine.empty() && m_sLine[m_sLine.size() - 1] <= 32) + m_sLine.erase(m_sLine.size() - 1, 1); + while (!m_sLine.empty() && m_sLine[0] <= 32) + m_sLine.erase(0, 1); + if (!m_sLine.empty() && m_sLine[0] != ';') { + m_csLines2.push_back(m_sLine); + m_cnPos2.push_back(nLineCount); + } + } + + m_sFName2 = sFName2; + + oIS2.close(); +} + +CConfigFile::~CConfigFile() {} + +bool CConfigFile::FetchLine(const std::string& sChapter, size_t nIdx, bool bForce) +{ + std::string::size_type p; + size_t i = 0; + m_sLine = ""; + + if (sChapter == m_sChapter && m_nLine == nIdx) + return true; + std::string sChapTitle = std::string("[") + sChapter + std::string("]"); + + // optionales Zweit-File befragen + if (!m_sFName2.empty()) { + for (i = 0; i < m_csLines2.size(); i++) { + if (IsEqual(m_csLines2[i].c_str(), sChapTitle.c_str())) + break; + } + + if ((i < m_csLines2.size()) && (i + nIdx < m_csLines2.size())) { + m_sChapter = sChapter; + m_nLine = 0; + m_nChapterStart = i; + m_sLine = m_csLines2[i + nIdx]; + } + } + + if (m_sLine.empty()) { + for (i = 0; i < m_csLines.size(); i++) { + if (IsEqual(m_csLines[i].c_str(), sChapTitle.c_str())) + break; + } + + if (i >= m_csLines.size()) { + static std::string sLastChapter; + if (bForce && sLastChapter != sChapTitle) { + if (g_nSrcLine > 0) + ERRMSG(0, ("%s(%ld) : FEHLER: Kapitel '%s' in der Config-Datei '%s' nicht gefunden!", g_sSrcFile.c_str(), g_nSrcLine, sChapTitle.c_str(), m_sFName.c_str())); + else + ERRMSG(0, ("FEHLER: Kapitel '%s' in der Config-Datei '%s' nicht gefunden!", sChapTitle.c_str(), m_sFName.c_str())); + sLastChapter = sChapTitle; + } + return false; + } + + m_sChapter = sChapter; + m_nLine = 0; + m_nChapterStart = i; + + if (i + nIdx >= m_csLines.size()) + return false; + + m_sLine = m_csLines[i + nIdx]; + } + + m_csStrings.clear(); + + if (m_sLine.empty() || m_sLine[0] == '[') + return false; + + std::string sTemp; + p = 0; + while (true) { + p = m_sLine.find_first_not_of(" \t", p); + if (p == std::string::npos) + break; + if (m_sLine[p] == '\x22') { + // String-Eintrag + std::string::size_type s = p++; + p = m_sLine.find('\x22', p); + if (p == std::string::npos) { + ERRMSG(0, ("%s(%ld) : FEHLER: Fehlendes schliessendes Anfuehrungszeichen!", m_sFName.c_str(), m_cnPos[i + nIdx])); + break; + } + p++; + m_csStrings.push_back(m_sLine.substr(s, p - s)); + } + else if (IsDigit(m_sLine[p]) || m_sLine[p] == '-' || m_sLine[p] == '+') { + // numerischer Eintrag + std::string::size_type s = p; + p = m_sLine.find_first_not_of("0123456789.+-", p); + if (p == std::string::npos) { + p = m_sLine.length(); + } + sTemp = m_sLine.substr(s, p - s); + if (!CRegExp::Match(std::string("#") + sTemp + '#', "#[-+]?((\\d*\\.\\d+)|(\\d+))#")) { + ERRMSG(0, ("%s(%ld) : FEHLER: Fehlerhaftes Zahlenformat (%s)!", m_sFName.c_str(), m_cnPos[i + nIdx], sTemp.c_str())); + break; + } + m_csStrings.push_back(sTemp); + } + else if (!p && IsAlpha(m_sLine[0])) { + // potentielle Options-Zuweisung + p = m_sLine.find_first_of("\x22="); + if (p == std::string::npos || m_sLine[p] != '=') { + ERRMSG(0, ("%s(%ld) : FEHLER: Fehlendes oeffnendes Anfuehrungszeichen!", m_sFName.c_str(), m_cnPos[i + nIdx])); + break; + } + m_csStrings.push_back(m_sLine); + break; + } + else { + ERRMSG(0, ("%s(%ld) : FEHLER: Unerwartetes Zeichen an Offset %lu!", m_sFName.c_str(), m_cnPos[i + nIdx], p)); + break; + } + p = m_sLine.find_first_not_of(" \t", p); + if (p == std::string::npos) + break; + if (m_sLine[p] != ',') { + ERRMSG(0, ("%s(%ld) : FEHLER: Fehlendes Komma zur Trennung der Eintraege!", m_sFName.c_str(), m_cnPos[i + nIdx])); + break; + } + p++; + } + + /* + m_cnStart.clear(); + m_cnStart.push_back( 0 ); + + CRegExp oRE; + p = 0; + oRE.Prepare( "([^ \\t\\n\\r\\f,\"]+|(\"([^\"\\\\]|\\\\.)*\"))[ \t]*," ); + while( oRE.Find( m_sLine, p ) ) + { + m_cnStart.push_back( oRE.End() ); + p = oRE.End(); + } + m_cnStart.push_back( m_sLine.size()+1 ); + */ + + /* + p = 0; + while( true ) + { + p = m_sLine.find( ',', p ); + if( p == std::string::npos ) + break; + p++; + m_cnStart.push_back( p ); + } + m_cnStart.push_back( m_sLine.size()+1 ); + */ + m_nLine = nIdx; + + return (m_sLine[0] == '[') || m_csStrings.empty() ? false : true; +} + +bool CConfigFile::IsString(size_t nIdx) +{ + if (nIdx >= m_csStrings.size()) { + return false; + } + if (m_csStrings[nIdx][0] == '\x22') { + return true; + } + return false; +} + +std::string CConfigFile::GetString(size_t nIdx) +{ + if (size_t(nIdx) >= m_csStrings.size()) { + return std::string(""); + } + if (IsString(nIdx)) { + return m_csStrings[nIdx].substr(1, m_csStrings[nIdx].length() - 2); + } + else { + return m_csStrings[nIdx]; + } +} + +int32_t CConfigFile::GetLong(size_t nIdx) +{ + if (size_t(nIdx) >= m_csStrings.size()) { + return 0; + } + return std::atoi(m_csStrings[nIdx].c_str()); +} + +double CConfigFile::GetReal(size_t nIdx) +{ + if (size_t(nIdx) >= m_csStrings.size()) { + return 0; + } + return std::atof(m_csStrings[nIdx].c_str()); +} + +COutputTable& COutputTable::Col(const std::string& sText, COutputTable::FORMAT enFormat) +{ + m_nCol++; + if (m_nCol > m_nMaxCols) { + m_nMaxCols = m_nCol; + } + m_pRow->push_back(TABENTRY(enFormat, sText)); + return *this; +} + +COutputTable& COutputTable::Col(const char* pcText, COutputTable::FORMAT enFormat) +{ + m_nCol++; + if (m_nCol > m_nMaxCols) { + m_nMaxCols = m_nCol; + } + m_pRow->push_back(TABENTRY(enFormat, std::string(pcText))); + return *this; +} + +COutputTable& COutputTable::Col(int32_t nNum, COutputTable::FORMAT enFormat) +{ + m_nCol++; + if (m_nCol > m_nMaxCols) { + m_nMaxCols = m_nCol; + } + m_pRow->push_back(TABENTRY(enFormat, std::to_string(nNum))); + return *this; +} + +COutputTable& COutputTable::Col(double fNum, int nScale, COutputTable::FORMAT enFormat) +{ + char Fmt[16]; + char Buff[80]; + m_nCol++; + if (m_nCol > m_nMaxCols) { + m_nMaxCols = m_nCol; + } + snprintf(Fmt, sizeof(Fmt), "%%.%df", nScale < 10 ? nScale : 10); + snprintf(Buff, sizeof(Buff), Fmt, fNum); + m_pRow->push_back(TABENTRY(enFormat, std::string(Buff))); + return *this; +} + +void COutputTable::Format() +{ + const std::string sSPC(" "); + TABLE::iterator iT; + size_t nWidth; + + if (m_coTable.back().empty()) { + m_coTable.pop_back(); + } + for (size_t c = 0; c < m_nMaxCols; c++) { + // Spaltenbreite ermitteln + nWidth = 0; + for (iT = m_coTable.begin(); iT != m_coTable.end(); iT++) { + if ((*iT).size() <= c) { + (*iT).push_back(TABENTRY(enLEFT, std::string(""))); + } + if ((*iT)[c].second.size() > nWidth) { + nWidth = (*iT)[c].second.size(); + } + } + + if (nWidth > 60) { + break; + } + + // Spalten formatieren + for (iT = m_coTable.begin(); iT != m_coTable.end(); iT++) { + if ((*iT)[c].second.size() < nWidth) { + switch ((*iT)[c].first) { + case enRIGHT: + (*iT)[c].second = sSPC.substr(0, nWidth - (*iT)[c].second.size()) + (*iT)[c].second; + break; + case enCENTER: + (*iT)[c].second = sSPC.substr(0, (nWidth - (*iT)[c].second.size()) / 2) + (*iT)[c].second; + break; + case enLEFT: + (*iT)[c].second += sSPC.substr(0, nWidth - (*iT)[c].second.size()); + break; + } + } + } + } +} + +void COutputTable::Output(const std::string& sTarget, const std::string& sPfx) +{ + TABLE::iterator iT; + bool bBorder; + Format(); + + for (iT = m_coTable.begin(); iT != m_coTable.end(); iT++) { + COutput::TPrintf(sTarget, "%s", sPfx.c_str()); + bBorder = false; + for (size_t c = 0; c < (*iT).size(); c++) { + if (!strcmp((*iT)[c].second.c_str(), "|")) { + COutput::TPrintf(sTarget, "|"); + bBorder = true; + } + else { + if (c && !bBorder) { + COutput::TPrintf(sTarget, " %s", (*iT)[c].second.c_str()); + } + else { + COutput::TPrintf(sTarget, "%s", (*iT)[c].second.c_str()); + } + bBorder = false; + } + } + COutput::TPrintf(sTarget, "\n"); + } +} + +void COutputTable::Output(CValArray& oDump, const std::string& sPfx) +{ + TABLE::iterator iT; + bool bBorder; + Format(); + std::string sLine; + + for (iT = m_coTable.begin(); iT != m_coTable.end(); iT++) { + sLine = sPfx; + bBorder = false; + for (size_t c = 0; c < (*iT).size(); c++) { + if (!strcmp((*iT)[c].second.c_str(), "|")) { + sLine += "|"; + bBorder = true; + } + else { + if (c && !bBorder) { + sLine += " "; + } + sLine += (*iT)[c].second; + bBorder = false; + } + } + oDump.push_back(sLine); + } +} + +void COutputTable::Output(FILE* pStream) +{ + TABLE::iterator iT; + bool bBorder; + Format(); + std::string sLine; + + for (iT = m_coTable.begin(); iT != m_coTable.end(); iT++) { + sLine = ""; + bBorder = false; + for (size_t c = 0; c < (*iT).size(); c++) { + if (!strcmp((*iT)[c].second.c_str(), "|")) { + sLine += "|"; + bBorder = true; + } + else { + if (c && !bBorder) { + sLine += " "; + } + sLine += (*iT)[c].second; + bBorder = false; + } + } + fprintf(pStream, "%s\n", sLine.c_str()); + } +} + +std::string Flatten(const std::string& sText) +{ + std::string::const_iterator is; + std::string sOut; + static char Buff[256]; + char* str = Buff; + int l = 0; + + is = sText.begin(); + while (is != sText.end()) { + switch (static_cast(*is)) { + case 196: + *str++ = 'a'; + *str++ = 'e'; + break; + case 214: + *str++ = 'o'; + *str++ = 'e'; + break; + case 220: + *str++ = 'u'; + *str++ = 'e'; + break; + case 228: + *str++ = 'a'; + *str++ = 'e'; + break; + case 246: + *str++ = 'o'; + *str++ = 'e'; + break; + case 252: + *str++ = 'u'; + *str++ = 'e'; + break; + case 223: + *str++ = 's'; + *str++ = 's'; + break; +#ifdef _WIN32 + case 132: // DOS-� + case 142: // DOS-� + *str++ = 'a'; + *str++ = 'e'; + break; + case 148: // DOS-� + case 153: // DOS-� + *str++ = 'o'; + *str++ = 'e'; + break; + case 129: // DOS-� + case 154: // DOS-� + *str++ = 'u'; + *str++ = 'e'; + break; + case 225: // DOS-� + *str++ = 's'; + *str++ = 's'; + break; +#endif + case '~': + case ' ': + case '\t': + break; + default: + *str++ = static_cast(tolower(*is)); + } + is++; + if (++l > 250) { + *str = 0; + sOut += Buff; + str = Buff; + l = 0; + } + } + *str = 0; + return std::string(Buff); +} + +static void EscapeStringContent(std::string& sTxt) +{ + CRegExp::Replace(sTxt, "\"", ""); + CRegExp::Replace(sTxt, "[\\s]+", "~"); +} + +std::string Escape(const std::string& sText, bool bTildenize) +{ + std::string sErg(sText); + std::string sEsc("\\"); + std::string::size_type p = 0; + + if (bTildenize) { + CRegExp::ReplaceCall(sErg, "\"[^\"]*[\"]?", EscapeStringContent); + } + else { + while (true) { + p = sErg.find_first_of("\\\"", p); + if (p == std::string::npos) { + break; + } + sErg.insert(p, sEsc); + p += 2; + } + } + return sErg; +} + +std::string DeEscape(const std::string& sText) +{ + std::string sErg(sText); + std::string::size_type p = 0; + + while (true) { + p = sErg.find('\\', p); + if (p == std::string::npos) { + break; + } + sErg.erase(p, 1); + p++; + } + return sErg; +} + +bool IsIdentifier(const std::string& sText, bool bVar) +{ + std::string::const_iterator i = sText.begin(); + + if (bVar && i != sText.end() && *i++ != '$') { + return false; + } + + if (i != sText.end() && (!(IsAlpha(*i) || IsUmlaut(*i)) || *i == '@')) { + return false; + } + i++; + + while (i != sText.end()) { + if (*i == '@' || !(IsAlpha(*i) || IsDigit(*i) || IsUmlaut(*i))) { + return false; + } + i++; + } + return true; +} + +/* +int32_t CStringDB::m_nStrCnt = 0; +std::map CStringDB::m_coStrToSID; +std::map CStringDB::m_coSIDToStr; + +int32_t CStringDB::Insert( const std::string& sText ) +{ + std::string sDText = DeUmlaut( sText ); + std::map::iterator i = m_coStrToSID.find( sDText ); + if( i == m_coStrToSID.end() ) + { + m_coStrToSID.insert( std::map::value_type( sDText, ++m_nStrCnt ) ); + m_coSIDToStr.insert( std::map::value_type( m_nStrCnt, sText ) ); + return m_sStrCnt; + } + else + { + return (*i).second; + } +} + +const std::string& CStringDB::Get( int32_t nSID ) +{ + static const std::string sUnknown( "-unbekannt-" ); + std::map::iterator i = m_coSIDToStr.find( nSID ); + if( i == m_coSIDToStr.end() ) + { + return sUnknown; + } + else + { + return (*i).second; + } +} +*/ + +std::map CStringDB::m_coS2I; +std::map CStringDB::m_coI2S; + +int32_t CStringDB::Str2SID(const std::string& sStr) +{ + std::string sFlat = Flatten(sStr); + auto i = m_coS2I.find(sFlat); + if (i == m_coS2I.end()) { + size_t s = m_coS2I.size() + 1; + m_coS2I[sFlat] = s; + m_coI2S[s] = sStr; + return static_cast(s); + } + else { + return static_cast((*i).second); + } +} + +const std::string& CStringDB::SID2Str(int32_t nSID) +{ + static std::string sUnknown("unknown sid"); + auto i = m_coI2S.find(static_cast(nSID)); + if (i == m_coI2S.end()) { + return sUnknown; + } + else { + return (*i).second; + } +} + +std::map CTranslationDB::m_coI2L; +std::map CTranslationDB::m_coL2I; + +std::map COutput::g_csFilter; +std::map COutput::g_cpoTargets; +std::map COutput::g_cpoDestinations; + +COutput::COutput(const std::string& sFileName, bool bFlushed) + : m_bIsOkay(false) + , m_bStdStream(false) + , m_bFlushed(bFlushed) + , m_nRefCount(1) + , m_sFileName(sFileName) + , m_hFile(0) + , m_poRoute(0) +{ + std::map::const_iterator di = g_cpoDestinations.find(sFileName); + if (di == g_cpoDestinations.end()) { + m_hFile = fopen(sFileName.c_str(), "w"); + if (m_hFile) { + m_bIsOkay = true; + } + g_cpoDestinations[sFileName] = this; + + if (m_bFlushed && m_hFile) { + fclose(m_hFile); + m_hFile = 0; + } + } + else { + (*di).second->m_nRefCount++; + m_poRoute = (*di).second; + m_bIsOkay = true; + } +} + +COutput::COutput(FILE* hFile) + : m_bIsOkay(false) + , m_bStdStream(true) + , m_bFlushed(false) + , m_nRefCount(1) + , m_hFile(hFile) + , m_poRoute(0) +{ + if (!g_pOutputMapper.get()) { + g_pOutputMapper.reset(new CharacterMapper("iso-8859-1", "iso-8859-1")); + } + + if (m_hFile) { + m_bIsOkay = true; + } +} + +COutput::~COutput() +{ + if (m_nRefCount > 1) { + fprintf(stderr, "FEHLER: Freigabe eines benutzen Ausgabekanals! (interner Fehler)\n"); + } + + Disconnect(false); +} + +bool COutput::Disconnect(bool bSuicide) +{ + if (m_poRoute) { + m_poRoute->Disconnect(); + } + + m_nRefCount--; + if (!m_nRefCount) { + if (!m_poRoute) { + if (!m_bStdStream) { + g_cpoDestinations.erase(m_sFileName); + if (m_hFile) { + fclose(m_hFile); + } + m_hFile = 0; + } + } + m_poRoute = 0; + m_bIsOkay = false; + if (bSuicide) { + delete this; + } + return true; + } + return false; +} + +void COutput::DoWrite(const char* pcTxt) +{ + if (m_poRoute) { + m_poRoute->DoWrite(pcTxt); + return; + } + + std::string sOut; + if (g_pOutputMapper->IsOK()) { + if (m_bWritePrefix && g_pOutputMapper->IsToUtf8()) { + sOut = std::string("\xEF\xBB\xBF", 3) + g_pOutputMapper->Map(pcTxt); + m_bWritePrefix = false; + } + else { + sOut = g_pOutputMapper->Map(pcTxt); + } + pcTxt = sOut.c_str(); + } + + if (m_bStdStream) { + // static bool bEOL = true; + size_t l = strlen(pcTxt); + if ((g_bForceEOL && strstr(pcTxt, "\n")) /*|| ( !bEOL && strchr( pcTxt, ':' ) )*/) { + WriteToConsole(m_hFile, "\n"); + g_bForceEOL = false; + } + WriteToConsole(m_hFile, pcTxt); + /* + if( l && ( pcTxt[l-1]==10 || pcTxt[l-1]==13 ) ) { + bEOL = true; + } + else { + bEOL = false; + } + */ + } + else if (m_bFlushed) { + FILE* hFile; + hFile = fopen(m_sFileName.c_str(), "a"); + if (hFile) { + fprintf(hFile, "%s", pcTxt); + fclose(hFile); + } + else { + fprintf(stderr, "%s", pcTxt); + } + } + else if (m_hFile) { + fprintf(m_hFile, "%s", pcTxt); + } + else { + fprintf(stderr, "%s", pcTxt); + } +} + +extern bool DoUserFunction(const std::string& sName, ArgumentList& coArgs, Value* poVal); + +void COutput::FilterWrite(const char* pcTxt) +{ + std::map::iterator i = g_csFilter.find(m_sTargetName); + if (i != g_csFilter.end()) { + ArgumentList coArgs; + Value oVal; + coArgs.push_back(Value(pcTxt)); + if (DoUserFunction((*i).second, coArgs, &oVal)) { + DoWrite(oVal.asString().c_str()); + } + else { + // error + if (oVal.getType() == VT_ERROR) { + ERRMSG(0, ("FEHLER: Fehler in Ausgabefilter '%s', Filter wird abgeschaltet: %s", (*i).second.c_str(), oVal.asString().c_str())); + } + COutput::SetFilter((*i).first.c_str(), ""); + DoWrite(pcTxt); + } + } + else { + DoWrite(pcTxt); + } +} + +void COutput::Write(const char* pcTxt) +{ + std::map::iterator i = g_csFilter.find(m_sTargetName); + if (i == g_csFilter.end()) { + DoWrite(pcTxt); + return; + } + + std::string sTxt = pcTxt; + size_t p; + + while (true) { + p = sTxt.find_first_of("\r\n"); + if (p == std::string::npos) { + break; + } + m_sOutBuffer += sTxt.substr(0, p + 1); + FilterWrite(m_sOutBuffer.c_str()); + sTxt.erase(0, p + 1); + m_sOutBuffer = ""; + } + m_sOutBuffer += sTxt; +} + +void COutput::Write(const std::string& sTxt) +{ + Write(sTxt.c_str()); +} + +void COutput::Printf(const char* msg, ...) +{ + static char pcBuff[4096]; + va_list list; + va_start(list, msg); + _vsnprintf(pcBuff, 4095, msg, list); + pcBuff[4095] = 0; + Write(pcBuff); +} + +void COutput::TWrite(const std::string& sID, const std::string& sTxt) +{ + Target(sID)->Write(sTxt.c_str()); +} + +void COutput::TPrintf(const std::string& sID, const char* msg, ...) +{ + static char pcBuff[4096]; + va_list list; + va_start(list, msg); + _vsnprintf(pcBuff, 4095, msg, list); + pcBuff[4095] = 0; + Target(sID)->Write(pcBuff); +} + +void COutput::CloseTargets() +{ + std::map::iterator ti = g_cpoTargets.begin(); + std::list cpoHelp; + + while (ti != g_cpoTargets.end()) { + if ((*ti).second->m_poRoute) { + delete (*ti++).second; + } + else { + cpoHelp.push_back((*ti++).second); + } + } + g_cpoTargets.clear(); + std::list::iterator hi = cpoHelp.begin(); + while (hi != cpoHelp.end()) { + delete (*hi++); + } + cpoHelp.clear(); + g_cpoDestinations.clear(); +} + +void COutput::SetTarget(const std::string& sID, COutput* poTarget) +{ + std::map::iterator ti = g_cpoTargets.find(sID); + poTarget->m_sTargetName = sID; + + if (ti == g_cpoTargets.end()) { + g_cpoTargets.insert(std::map::value_type(sID, poTarget)); + } + else { + if ((*ti).second->m_nRefCount <= 1) { + delete (*ti).second; + } + (*ti).second = poTarget; + } +} + +void COutput::SetFilter(const std::string& sID, const std::string& sFilter) +{ + if (sFilter.empty()) { + g_csFilter.erase(sID); + } + else { + g_csFilter[sID] = sFilter; + } +} + +void COutput::RenameTarget(const std::string& sIDOld, const std::string& sIDNew) +{ + std::map::iterator ti = g_cpoTargets.find(sIDOld); + COutput* poTarget; + if (ti != g_cpoTargets.end()) { + poTarget = (*ti).second; + g_cpoTargets.erase(sIDOld); + SetTarget(sIDNew, poTarget); + } +} + +COutput* COutput::Target(const std::string& sID) +{ + std::map::const_iterator ti = g_cpoTargets.find(sID); + if (ti == g_cpoTargets.end()) { + SetTarget(sID, new COutput(stderr)); + ERRMSG(0, ("FEHLER: Der interne Ausgabekanal '%s' wurde nicht gefunden!", sID.c_str())); + return Target(sID); + } + return (*ti).second; +} + +void COutput::SetEncoding(const std::string& sEnc) +{ + if (IsEqual(sEnc, "utf-8") || IsEqual(sEnc, "utf8")) { + g_bUTF8 = true; + g_pOutputMapper.reset(new CharacterMapper("iso-8859-1", "utf8")); + } + else if (CharacterMapper::IsSupported(sEnc)) { + g_bUTF8 = false; + g_pOutputMapper.reset(new CharacterMapper("iso-8859-1", "iso-8859-1")); + } +} diff --git a/EBase/Utility.h b/EBase/Utility.h new file mode 100644 index 0000000..f29b0f9 --- /dev/null +++ b/EBase/Utility.h @@ -0,0 +1,386 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/EBase/Utility.h,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:55:53 $ + * $Revision: 1.8 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Algemeine Utility-Funktionen + ***************************************************************************** + * + * $Log: Utility.h,v $ + * Revision 1.8 2000/02/24 09:55:53 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.7 1999/11/28 17:38:11 S.Schuemann + * - Mannigfaltige �nderungen f�r Vorlage V1.4 beta 9 + * + * Revision 1.6 1999/11/17 08:58:15 S.Schuemann + * - support f�r multiple CRs + * + * - vielfache �nderungen f�r Vorlage 1.4 beta 8 + * + * Revision 1.5 1999/11/08 11:10:45 S.Schuemann + * - verbessertes Luxusgut-Handling + * - Korrektur der Kapazitaetsberechnung + * - Neue Flags + * + * Revision 1.4 1999/11/03 10:21:38 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.3 1999/10/20 02:22:33 S.Schuemann + * - Anpassungen der Reportklassen fuer die Features von Vorlage 1.4 b 3 + * + * Revision 1.2 1999/10/18 21:31:46 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IsAlpha(c) isalpha((unsigned char)(c)) +#define IsAlNum(c) isalnum((unsigned char)(c)) +#define IsDigit(c) isdigit((unsigned char)(c)) +#define IsSpace(c) isspace((unsigned char)(c)) + +inline bool IsUmlaut(char cc) +{ + unsigned char c = static_cast(cc); + return (c == 0xC4 || c == 0xD6 || c == 0xDC || c == 0xE4 || c == 0xF6 || c == 0xFC || c == 0xDF || c == '~' || c == '_' || c == '@'); +} + +#define IsDelim(c) (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '<' || c == '>' || c == '=' || c == '^' || c == '(' || c == ')' || c == ',' || c == '=' || c == '[' || c == ']' || c == '.' || c == '!') +#define IsNameChar(c) (isalpha((unsigned char)(c)) || (unsigned char)(c) >= 0xc0 || (c) == '\'') + +#define ToLower(c) ((char)(g_toLowerMapping[(unsigned char)(c)])) + +#define ERRMSG(pobj, msg) ErrorMessage((void*)pobj, FormatMsg msg) +// #define VERRMSG( msg ) VErrorMessage( FormatMsg msg ) +#define CONMSG(msg) ConsoleMessage(FormatMsg msg) +#define TRACEMSG(msg) TraceMessage(FormatMsg msg) + +extern int g_toLowerMapping[256]; +extern std::string g_sSrcFile; +extern int32_t g_nSrcLine; +extern bool g_bForceEOL; +extern uint32_t g_nStepCount; +extern uint32_t g_nLastErrorStep; +extern int g_returnCode; +char* FormatMsg(const char* msg, ...); +void ErrorMessage(void* pDat, const char* pszStr); +void ConsoleMessage(const char* pszStr); +void TraceMessage(const char* pszStr); +extern void (*g_pfErrorFunc)(void*, const char*); + +std::string iso2utf8(const std::string& txt, bool doit); +std::string& Utf8toIso885915(std::string& text); +std::string& iso885915ToUtf8(std::string& text); +void SetBreakHandler(void (*pfHandler)(void)); +void Message(const char* pszStr); +const char* itoa36(int i); +const char* itoan(int32_t n, int base); +int32_t EinheitenNummer(const std::string& sENStr); +int32_t FindNextENum(const std::string& sTxt, std::string::size_type& p); +bool FindNextRegion(const std::string& sTxt, std::string::size_type& p, int& x, int& y, int& z); +bool IsMetaCommand(const std::string& sLine); +std::string DeUmlaut(const std::string& sText); +std::string Flatten(const std::string& sText); +std::string Escape(const std::string& sText, bool bTildenize = false); +std::string DeEscape(const std::string& sText); +bool IsIdentifier(const std::string& sText, bool bVar = true); +bool IsEqual(const char* pcS1, const char* pcS2); +bool IsEqual(const std::string& sS1, const char* pcS2); +bool FileExists(const std::string& sFName); +bool IsConsole(FILE* hFile); +bool GetConsoleSize(int& width, int& height); +void WriteToConsole(FILE* hFile, const char* pcText); +std::string InputFromConsole(); +std::string GetExecutablePathname(); +std::string PathedFileName(const std::string& sFName); +std::string Wrap(std::string& sTxt, size_t len); +std::string ToString(int32_t n); +std::string ToString(double f, unsigned n = 2); +std::string ToStringS(int32_t n); +std::string ToStringS(double f, unsigned n = 2); + +int32_t Random(int32_t seed = 0); + +typedef std::vector Args; +void split(Args& dest, const std::string& s, const std::string& sep, const std::string& caps, char esc); + +enum VORLAGENFLAGS { + VF_SORTBURGEN, + VF_SORTTALENTE, + VF_SHOWTALENTE, + VF_SHOWGEGENSTAENDE, + VF_SHOWMINIKARTE, + VF_SHOWMESSAGES, + VF_PRIVATMETA, + VF_SHOWLASTEN, + VF_SHOWKOMPKARTE, + VF_SORTISLANDS, + VF_SHOWHANDEL, + VF_SHOWPRIVAT, + VF_HEXMAP, + VF_BASE36, + VF_CROUTPUT, + VF_SORTPRIVAT, + VF_SHOWUNITS, + VF_SHOWUNITSVERBOSE, + VF_SHOWMATPOOL, + VF_SHOWLUXUS, + VF_SHOWUNITSNEW, + VF_SUPPRESSUNITS, + VF_SHOWLPROD, + VF_SHOWTDIFF, + VF_SHOWTRIBEOVERVIEW, + VF_SORTFOREIGN, + VF_SHOWBESCHREIBUNG, + VF_FULLBASE36, + VF_SUPPRESSKEYWARN, + VF_DEBUGMODE, + VF_PROGRESSINFO, + VF_DONTKILLCOMMANDS, + VF_TRACEONERROR, + VF_NEWERESSEASTATI, + VF_SORTKOMMANDO, + VF_NOCONSOLE, + VF_PAGER, + VF_RESOURCEBLOCKS, + VF_SHOWVERBOSEINFO, + VF_NOWARNINGS, + VF_NOSKILLPOINTS, + VF_SUPPRESSTURNOUTPUT, + VF_DIAGPEDANTIC, + VF_SHOWEMULATEDDAYS, + VF_VERSION2WARNING, + VF_RUNALLVISIBLEREGIONS, + VF_SUPPRESSMULTIERRORS, + VF_FORCEDECLARES, + VF_EXPORTWITHROUND, + VF_FULLCOMMANDOUTPUT, + VF_SHOWWORLDKARTE, + VF_STRIPDUPLICATEDESCR, + VF_RESTRICTED, + VF_NOBATTLEMESSAGES, + VF_FIXENCODINGS +}; + +// extern int32_t g_nFlags; +extern std::set g_coFlags; +#define IsFlag(n) (g_coFlags.find(n) != g_coFlags.end()) + +typedef std::set Keywords; +extern Keywords g_coKeywords; + +inline void AddKeyword(const std::string& sText) +{ + g_coKeywords.insert(DeUmlaut(sText)); +} + +inline bool IsKeyword(const std::string& sText) +{ + Keywords::iterator ki = g_coKeywords.find(DeUmlaut(sText)); + return ki != g_coKeywords.end(); +} + +inline std::string getAndReset(std::ostringstream& os) +{ + auto result = os.str(); + os.str(""); + os.clear(); + return result; +} + +/* +inline int IsSpace( char x ) +{ + return isspace(x); //x>0 && x<33; +} +*/ + +class CConfigFile +{ +public: + CConfigFile(const std::string& sFName); + ~CConfigFile(); + bool FetchLine(const std::string& sChapter, size_t nIdx, bool bForce = true); + bool IsString(size_t nIdx); + std::string GetString(size_t nIdx); + int32_t GetLong(size_t nIdx); + double GetReal(size_t nIdx); + +protected: + std::string m_sFName; + std::string m_sFName2; + std::string m_sChapter; + std::string m_sLine; + size_t m_nLine; + size_t m_nChapterStart; + std::vector m_csStrings; + std::vector m_csLines; + std::vector m_csLines2; + std::vector m_cnPos; + std::vector m_cnPos2; +}; + +class CValArray; + +class COutputTable +{ +public: + enum FORMAT { enLEFT, enRIGHT, enCENTER }; + +protected: + typedef std::pair TABENTRY; + typedef std::vector TABLEROW; + typedef std::list TABLE; + +public: + COutputTable() { Clear(); } + + void Clear() + { + m_coTable.clear(); + m_nMaxCols = 0; + Next(); + } + + COutputTable& Col(const std::string& sText, FORMAT enFormat = enLEFT); + COutputTable& Col(const char* pcText, FORMAT enFormat = enLEFT); + COutputTable& Col(int32_t nNum, FORMAT enFormat = enRIGHT); + COutputTable& Col(double fNum, int nScale = 2, FORMAT enFormat = enRIGHT); + + void Next() + { + m_nCol = 0; + m_coTable.push_back(TABLEROW()); + m_pRow = &m_coTable.back(); + } + + void Output(const std::string& sTarget, const std::string& sPfx); + void Output(CValArray& oDump, const std::string& sPfx); + void Output(FILE* pStream); + +protected: + void Format(); + + size_t m_nCol, m_nMaxCols; + TABLEROW* m_pRow; + TABLE m_coTable; +}; + +/* +class CStringDB +{ +public: + static int32_t Insert( const std::string& sText ); + static const std::string& Get( int32_t nSID ); + +private: + static int32_t m_nStrCnt; + static std::map m_coStrToSID; + static std::map m_coSIDToStr; +}; +*/ +class CStringDB +{ +public: + static int32_t Str2SID(const std::string& sStr); + static const std::string& SID2Str(int32_t nSID); + static std::map m_coS2I; + static std::map m_coI2S; +}; + +class CTranslationDB +{ +public: + static const std::string& ToLocale(const std::string& sStr); + static const std::string& ToInternal(const std::string& sStr); + static std::map m_coI2L; + static std::map m_coL2I; +}; + +class CharacterMapper; + +class COutput +{ +public: + COutput(const std::string& sFileName, bool bFlushed = false); + COutput(FILE* hFile); + ~COutput(); + + bool IsOkay() const { return m_bIsOkay; } + + std::string FileName() const { return m_sFileName; } + + void Write(const char* pcTxt); + void Write(const std::string& sTxt); + void Printf(const char* msg, ...); + + bool Disconnect(bool bSuicide = true); + + static void TWrite(const std::string& sID, const std::string& sTxt); + static void TPrintf(const std::string& sID, const char* msg, ...); + static void CloseTargets(); + static void SetTarget(const std::string& sID, COutput* poTrace); + static void SetFilter(const std::string& sID, const std::string& sFilter); + static void RenameTarget(const std::string& sIDOld, const std::string& sIDNew); + static COutput* Target(const std::string& sID); + static void SetEncoding(const std::string& sEnc); + +private: + void DoWrite(const char* pcTxt); + void FilterWrite(const char* pcTxt); + + bool m_bIsOkay; + bool m_bStdStream; + bool m_bFlushed; + bool m_bWritePrefix = true; + int m_nRefCount; + std::string m_sOutBuffer; + std::string m_sFileName; + std::string m_sTargetName; + FILE* m_hFile; + COutput* m_poRoute; + + static std::map g_csFilter; + static std::map g_cpoTargets; + static std::map g_cpoDestinations; +}; + +template +inline std::string to_hex(T i) +{ + std::stringstream stream; + stream << "0x" << std::setfill('0') << std::setw(sizeof(T) * 2) << std::hex << i; + return stream.str(); +} + +template <> +inline std::string to_hex(char i) +{ + std::stringstream stream; + stream << "0x" << std::setfill('0') << std::setw(2) << std::hex << (unsigned)((unsigned char)i); + return stream.str(); +} + +std::string mixed_utf8_latin1_to_latin1(std::string_view input, char replacement = '?'); + +#endif // __UTILITY_H__ diff --git a/EBase/Value.cpp b/EBase/Value.cpp new file mode 100644 index 0000000..35918e5 --- /dev/null +++ b/EBase/Value.cpp @@ -0,0 +1,1023 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/Vorlage/Value.cpp,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:56:47 $ + * $Revision: 1.4 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Klassen fuer die Ausdrucksauswertung in Metabefehlen + ***************************************************************************** + * $Log: Value.cpp,v $ + * Revision 1.4 2000/02/24 09:56:47 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.3 1999/11/28 17:38:41 S.Schuemann + * - Mannigfaltige Aenderungen fuer Vorlage V1.4 beta 9 + * + * Revision 1.2 1999/10/24 08:02:18 S.Schuemann + * - Anpassungen fuer 1.4 b 4 + * - CReference als Value eingefuehrt + * - Unterprogramme mit #proc und #call implementiert + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#include "Value.h" +#include +#include +#include "Utility.h" + +#define EPSILON 0.00001 +#define ROUND(f) (f < 0 ? f - EPSILON : f + EPSILON) +#define TRUNC(f) ((int32_t)(f < 0 ? f - EPSILON : f + EPSILON)) + +int32_t Value::_cnt = 0; +int32_t g_nContainerLimit = 0x7fffffffL; + +#ifdef _DEBUG + +int32_t Value::_idCnt = 0; +std::map Value::_valPool; + +#ifdef _VALDEBUG + +#define REGVALUE \ + _id = _idCnt; \ + _valPool[_idCnt++] = this +#define UNREGVALUE _valPool.erase(_id) + +#else + +#define REGVALUE +#define UNREGVALUE + +#endif + +void Value::dumpPool() +{ + for (auto i = _valPool.begin(); i != _valPool.end(); i++) { + TRACEMSG(("%5d - %s\n", (*i).first, (*i).second->asString().c_str())); + } +} + +#else + +#define REGVALUE +#define UNREGVALUE + +#endif + +#define MAX_FLOAT 1e50 +#define MAX_FLOAT_LENGTH 64 + +inline double rangeCheck(double val) +{ + if (fabs(val) > MAX_FLOAT) { + return val < 0 ? -MAX_FLOAT : MAX_FLOAT; + } + return val; +} + +// Helper: deep-copy a Data variant. unique_ptr alternatives are deep-copied; +// all other alternatives are value-copied. The variant's own copy constructor +// is deleted (because of unique_ptr members), so we use std::visit. +static Value::Data copyData(const Value::Data& src) +{ + return std::visit( + [](const auto& v) -> Value::Data { + using T = std::decay_t; + if constexpr (std::is_same_v>>) { + return std::make_unique>(*v); + } + else if constexpr (std::is_same_v>>) { + return std::make_unique>(*v); + } + else { + return v; // copy monostate, ErrorState, int32_t, double, string, RefPtr + } + }, + src); +} + +// ----------------------------------------------------------------------------- +// Constructors / destructor +// ----------------------------------------------------------------------------- + +Value::Value() + : _data(std::monostate{}) + , _protectType(false) +{ + REGVALUE; + _cnt++; +} + +Value::Value(const Value& oVal, bool bRef) + : _protectType(false) +{ + REGVALUE; + if (bRef) { + _data = detail::RefPtr{const_cast(&oVal.cself())}; + } + else if (std::holds_alternative(oVal._data)) { + // Copy of a ref = ref with the same target (not a deep copy). + // Assign the RefPtr value directly (not the whole variant) to avoid + // triggering the deleted variant copy-assignment operator. + _data = std::get(oVal._data); + } + else { + _data = copyData(oVal.cself()._data); + } + _cnt++; +} + +Value::Value(Value&& oVal) noexcept + : _data(std::move(oVal._data)) + , _protectType(oVal._protectType) +{ + REGVALUE; + oVal._data = std::monostate{}; + _cnt++; +} + +Value::Value(double fVal) + : _data(rangeCheck(fVal)) + , _protectType(false) +{ + REGVALUE; + _cnt++; +} + +Value::Value(int32_t nVal) + : _data(nVal) + , _protectType(false) +{ + REGVALUE; + _cnt++; +} + +Value::Value(const char* pcVal) + : _data(std::string(pcVal)) + , _protectType(false) +{ + REGVALUE; + _cnt++; +} + +Value::Value(std::string sVal) + : _data(std::move(sVal)) + , _protectType(false) +{ + REGVALUE; + _cnt++; +} + +Value::Value(TYPE enType) + : _protectType(false) +{ + REGVALUE; + switch (enType) { + case VT_VECTOR: + _data = std::make_unique>(); + break; + case VT_MAP: + _data = std::make_unique>(); + break; + case VT_ERROR: + _data = detail::ErrorState{}; + break; + case VT_REF: + _data = detail::RefPtr{nullptr}; + break; + default: + _data = std::monostate{}; // VT_EMPTY and others + break; + } + _cnt++; +} + +Value::Value(const CValArray& arr) + : _protectType(false) +{ + REGVALUE; + auto vec = std::make_unique>(arr.begin(), arr.end()); + _data = std::move(vec); + _cnt++; +} + +Value::~Value() +{ + UNREGVALUE; + _cnt--; +} + +// ----------------------------------------------------------------------------- +// Type query +// ----------------------------------------------------------------------------- + +ValueType Value::getType() const noexcept +{ + static constexpr ValueType types[] = {VT_EMPTY, VT_ERROR, VT_INT, VT_FLOAT, VT_STRING, VT_REF, VT_VECTOR, VT_MAP}; + return types[_data.index()]; +} + +// ----------------------------------------------------------------------------- +// Value accessors +// ----------------------------------------------------------------------------- + +int32_t Value::asLong() const noexcept +{ + const Value& t = cself(); + if (const auto* v = std::get_if(&t._data)) + return *v; + if (const auto* v = std::get_if(&t._data)) + return static_cast(*v); + return 0; +} + +double Value::asReal() const noexcept +{ + const Value& t = cself(); + if (const auto* v = std::get_if(&t._data)) + return *v; + if (const auto* v = std::get_if(&t._data)) + return static_cast(*v); + return 0.0; +} + +void Value::error(const char* pcMsg) +{ + self()._data = detail::ErrorState{pcMsg}; +} + +std::string Value::asString(bool bForceLiteral) const +{ + const Value& t = cself(); + const TYPE type = t.getType(); + + if (type == VT_INT) { + return std::to_string(std::get(t._data)); + } + if (type == VT_FLOAT) { + const double f = std::get(t._data); + if (fabs(f) >= MAX_FLOAT) + return f < 0 ? std::string("-1.#INF") : std::string("1.#INF"); + return fmt::format("{:.3f}", ROUND(f)); + } + if (type == VT_ERROR) { + return std::get(t._data).message; + } + if (type == VT_MAP) { + return fmt::format("[{} Elements]", t.size()); + } + if (type == VT_VECTOR) { + if (bForceLiteral) { + std::string sErg("["); + const int32_t n = t.size(); + for (int32_t i = 0; i < n; i++) { + sErg += t.getAt(Value(i)).asString(true); + sErg += (i < n - 1) ? "," : "]"; + } + return sErg; + } + return fmt::format("[{} Elements]", t.size()); + } + if (type == VT_STRING) { + return std::get(t._data); + } + // VT_EMPTY, VT_REF (shouldn't reach REF via cself) + return {}; +} + +// ----------------------------------------------------------------------------- +// Assignment +// ----------------------------------------------------------------------------- + +const Value& Value::operator=(const Value& oVal) +{ + static Value oErr; + + if (&oVal == this) + return *this; + + if (_protectType && cself().getType() != oVal.cself().getType()) { + oErr.error("Zuweisung an inkompatible Variable!"); + return oErr; + } + + self()._data = copyData(oVal.cself()._data); + return *this; +} + +// ----------------------------------------------------------------------------- +// Container operations +// ----------------------------------------------------------------------------- + +bool Value::setAt(const Value& oKey, const Value& oVal) +{ + const TYPE enType = cself().getType(); + const TYPE enKType = oKey.cself().getType(); + + if (enKType == VT_VECTOR || enKType == VT_MAP) + return false; + + if (enType == VT_MAP) { + auto& map = *std::get>>(cself()._data); + if (oVal.cself().getType() == VT_EMPTY) { + map.erase(oKey.cself().asString()); + } + else { + map[oKey.cself().asString()] = oVal; + if (map.size() == size_t(g_nContainerLimit)) { + ERRMSG(0, ("Warnung: Ueberschreiten des Behaelterlimits!")); + } + } + return true; + } + if (enType == VT_VECTOR) { + if (enKType != VT_INT && enKType != VT_FLOAT) { + ERRMSG(0, ("FEHLER: Nichtnumerischer Wert als Index fuer Array benutzt!")); + } + auto& vec = *std::get>>(cself()._data); + const size_t nIdx = size_t(oKey.cself().asLong()); + if (nIdx > vec.size()) + return false; + if (nIdx == vec.size()) { + vec.push_back(oVal); + if (vec.size() == size_t(g_nContainerLimit)) { + ERRMSG(0, ("Warnung: Ueberschreiten des Behaelterlimits!")); + } + } + else { + vec[nIdx] = oVal; + } + return true; + } + return false; +} + +Value& Value::getAt(const Value& oKey) const +{ + static Value oEmpty; + static Value oErr; + + const TYPE enType = cself().getType(); + const TYPE enKType = oKey.cself().getType(); + + if (enType == VT_ERROR) { + oErr = cself(); + return oErr; + } + if (enKType == VT_ERROR) { + oErr = oKey.cself(); + return oErr; + } + if (enKType == VT_VECTOR || enKType == VT_MAP) { + oErr.error("Behaelter als Index/Schluessel verwendet."); + return oErr; + } + + if (enType == VT_VECTOR) { + if (enKType != VT_INT && enKType != VT_FLOAT) { + ERRMSG(0, ("FEHLER: Nichtnumerischer Wert als Index fuer Array benutzt!")); + } + const auto& vec = *std::get>>(cself()._data); + const size_t i = size_t(oKey.cself().asLong()); + if (i < vec.size()) + return const_cast(vec[i]); + // Out-of-range: original code fell through to return oEmpty (not oErr) + } + if (enType == VT_MAP) { + auto& map = *std::get>>(cself()._data); + auto it = map.find(oKey.cself().asString()); + if (it != map.end()) + return it->second; + } + return oEmpty; +} + +Value Value::getNth(int32_t nIdx) const +{ + if (cself().getType() == VT_ERROR) + return Value(); + if (nIdx >= 0) { + if (cself().getType() == VT_VECTOR) { + const auto& vec = *std::get>>(cself()._data); + if (size_t(nIdx) < vec.size()) + return Value(nIdx); + } + else if (cself().getType() == VT_MAP) { + const auto& map = *std::get>>(cself()._data); + auto it = map.begin(); + int cnt = 0; + while (it != map.end() && cnt < nIdx) { + ++it; + ++cnt; + } + if (it != map.end()) + return Value(it->first); + } + } + return Value(); +} + +void Value::remove(const Value& oKey) +{ + if (cself().getType() == VT_MAP) { + auto& map = *std::get>>(cself()._data); + map.erase(oKey.cself().asString()); + } + else if (cself().getType() == VT_VECTOR) { + if (oKey.cself().getType() != VT_INT && oKey.cself().getType() != VT_FLOAT) { + ERRMSG(0, ("FEHLER: Nichtnumerischer Wert als Index fuer Array benutzt!")); + } + auto& vec = *std::get>>(cself()._data); + const size_t nIdx = size_t(oKey.cself().asLong()); + if (nIdx < vec.size()) + vec.erase(vec.begin() + int32_t(nIdx)); + } +} + +void Value::clear() +{ + if (cself().getType() == VT_MAP) { + std::get>>(cself()._data)->clear(); + } + if (cself().getType() == VT_VECTOR) { + std::get>>(cself()._data)->clear(); + } +} + +int32_t Value::size() const +{ + const TYPE type = cself().getType(); + if (type == VT_MAP) + return int32_t(std::get>>(cself()._data)->size()); + if (type == VT_VECTOR) + return int32_t(std::get>>(cself()._data)->size()); + if (type == VT_STRING) + return int32_t(std::get(cself()._data).size()); + return -1; +} + +void Value::swap(Value& oVal) +{ + const TYPE ta = cself().getType(); + const TYPE tb = oVal.cself().getType(); + const bool aHasPtr = (ta == VT_REF || ta == VT_VECTOR || ta == VT_MAP); + const bool bHasPtr = (tb == VT_REF || tb == VT_VECTOR || tb == VT_MAP); + + if (aHasPtr && bHasPtr) { + std::swap(self()._data, oVal.self()._data); + } + else { + Value oTemp(oVal); + oVal = *this; + *this = oTemp; + } +} + +// ----------------------------------------------------------------------------- +// Arithmetic operators +// ----------------------------------------------------------------------------- + +Value Value::operator+(const Value& oVal) const +{ + Value oE(*this); + + if (oVal.cself().getType() == VT_ERROR) + oE.error(std::get(oVal.cself()._data).message.c_str()); + if (cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + oE.error("Operation mit typlosem Operanden."); + if (oE.self().getType() == VT_ERROR) + return oE; + + const TYPE lhs = cself().getType(); + const TYPE rhs = oVal.cself().getType(); + + switch (rhs) { + case VT_INT: + switch (lhs) { + case VT_INT: + std::get(oE.self()._data) += std::get(oVal.cself()._data); + break; + case VT_FLOAT: { + double f = rangeCheck(std::get(oE.self()._data) + std::get(oVal.cself()._data)); + oE.self()._data = f; + } break; + case VT_STRING: + std::get(oE.self()._data) += fmt::format("{}", std::get(oVal.cself()._data)); + break; + default: + break; + } + break; + case VT_FLOAT: + switch (lhs) { + case VT_INT: + std::get(oE.self()._data) += TRUNC(std::get(oVal.cself()._data)); + break; + case VT_FLOAT: { + double f = rangeCheck(std::get(oE.self()._data) + std::get(oVal.cself()._data)); + oE.self()._data = f; + } break; + case VT_STRING: + std::get(oE.self()._data) += oVal.asString(); + break; + default: + break; + } + break; + case VT_STRING: + switch (lhs) { + case VT_INT: + case VT_FLOAT: + oE.error("String kann nicht in numerischen Wert gewandelt werden."); + break; + case VT_STRING: + std::get(oE.self()._data) += std::get(oVal.cself()._data); + break; + default: + break; + } + break; + default: + break; + } + return oE; +} + +Value Value::operator-(const Value& oVal) const +{ + Value oE(*this); + + if (oVal.cself().getType() == VT_ERROR) + oE.error(std::get(oVal.cself()._data).message.c_str()); + if (cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + oE.error("Operation mit typlosem Operanden."); + if (oE.self().getType() == VT_ERROR) + return oE; + + const TYPE lhs = cself().getType(); + const TYPE rhs = oVal.cself().getType(); + + switch (rhs) { + case VT_INT: + switch (lhs) { + case VT_INT: + std::get(oE.self()._data) -= std::get(oVal.cself()._data); + break; + case VT_FLOAT: { + double f = std::get(oE.self()._data) - std::get(oVal.cself()._data); + oE.self()._data = f; + } break; + case VT_STRING: + // Preserved original (copy-paste) behaviour: string - int appends the int + std::get(oE.self()._data) += fmt::format("{}", std::get(oVal.cself()._data)); + break; + default: + break; + } + break; + case VT_FLOAT: + switch (lhs) { + case VT_INT: + std::get(oE.self()._data) -= TRUNC(std::get(oVal.cself()._data)); + break; + case VT_FLOAT: { + double f = std::get(oE.self()._data) - std::get(oVal.cself()._data); + oE.self()._data = f; + } break; + case VT_STRING: + oE.error("Strings koennen nicht in Subtraktionen verwendet werden."); + break; + default: + break; + } + break; + case VT_STRING: + oE.error("Strings koennen nicht in Subtraktionen verwendet werden."); + break; + default: + break; + } + return oE; +} + +Value Value::operator*(const Value& oVal) const +{ + Value oE(*this); + + if (oVal.cself().getType() == VT_ERROR) + oE.error(std::get(oVal.cself()._data).message.c_str()); + if (cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + oE.error("Operation mit typlosem Operanden."); + if (oE.self().getType() == VT_ERROR) + return oE; + + const TYPE lhs = cself().getType(); + const TYPE rhs = oVal.cself().getType(); + + switch (rhs) { + case VT_INT: + switch (lhs) { + case VT_INT: + std::get(oE.self()._data) *= std::get(oVal.cself()._data); + break; + case VT_FLOAT: { + double f = rangeCheck(std::get(oE.self()._data) * std::get(oVal.cself()._data)); + oE.self()._data = f; + } break; + case VT_STRING: + oE.error("Strings koennen nicht in Multiplikationen verwendet werden."); + break; + default: + break; + } + break; + case VT_FLOAT: + switch (lhs) { + case VT_INT: + std::get(oE.self()._data) *= TRUNC(std::get(oVal.cself()._data)); + break; + case VT_FLOAT: { + double f = rangeCheck(std::get(oE.self()._data) * std::get(oVal.cself()._data)); + oE.self()._data = f; + } break; + case VT_STRING: + oE.error("Strings koennen nicht in Multiplikationen verwendet werden."); + break; + default: + break; + } + break; + case VT_STRING: + oE.error("Strings koennen nicht in Multiplikationen verwendet werden."); + break; + default: + break; + } + return oE; +} + +Value Value::operator/(const Value& oVal) const +{ + Value oE(*this); + + if (oVal.cself().getType() == VT_ERROR) + oE.error(std::get(oVal.cself()._data).message.c_str()); + if (cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + oE.error("Operation mit typlosem Operanden."); + if (oE.self().getType() == VT_ERROR) + return oE; + + const TYPE lhs = cself().getType(); + const TYPE rhs = oVal.cself().getType(); + + switch (rhs) { + case VT_INT: { + const int32_t divisor = std::get(oVal.cself()._data); + switch (lhs) { + case VT_INT: + if (!divisor) + oE.error("Division durch Null."); + else + std::get(oE.self()._data) /= divisor; + break; + case VT_FLOAT: + if (!divisor) + oE.error("Division durch Null."); + else { + double f = rangeCheck(std::get(oE.self()._data) / divisor); + oE.self()._data = f; + } + break; + case VT_STRING: + oE.error("Strings koennen nicht in Divisionen verwendet werden."); + break; + default: + break; + } + } break; + case VT_FLOAT: { + const double divisor = std::get(oVal.cself()._data); + switch (lhs) { + case VT_INT: + if (fabs(divisor) < EPSILON) + oE.error("Division durch Null."); + else + std::get(oE.self()._data) /= TRUNC(divisor); + break; + case VT_FLOAT: + if (fabs(divisor) < EPSILON) + oE.error("Division durch Null."); + else { + double f = rangeCheck(std::get(oE.self()._data) / divisor); + oE.self()._data = f; + } + break; + case VT_STRING: + oE.error("Strings koennen nicht in Divisionen verwendet werden."); + break; + default: + break; + } + } break; + case VT_STRING: + oE.error("Strings koennen nicht in Divisionen verwendet werden."); + break; + default: + break; + } + return oE; +} + +Value Value::operator%(const Value& oVal) const +{ + Value oE(*this); + + if (oVal.cself().getType() == VT_ERROR) + oE.error(std::get(oVal.cself()._data).message.c_str()); + if (cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + oE.error("Operation mit typlosem Operanden."); + if (oE.self().getType() == VT_ERROR) + return oE; + + const TYPE lhs = cself().getType(); + const TYPE rhs = oVal.cself().getType(); + + switch (rhs) { + case VT_INT: { + const int32_t divisor = std::get(oVal.cself()._data); + switch (lhs) { + case VT_INT: + if (!divisor) + oE.error("Division durch Null."); + else + std::get(oE.self()._data) %= divisor; + break; + case VT_FLOAT: + if (!divisor) + oE.error("Division durch Null."); + else { + double f = double(int32_t(std::get(oE.self()._data)) % divisor); + oE.self()._data = f; + } + break; + case VT_STRING: + oE.error("Strings koennen nicht in Divisionen verwendet werden."); + break; + default: + break; + } + } break; + case VT_FLOAT: { + const double divisor = std::get(oVal.cself()._data); + switch (lhs) { + case VT_INT: + if (fabs(divisor) < EPSILON) + oE.error("Division durch Null."); + else + std::get(oE.self()._data) %= TRUNC(divisor); + break; + case VT_FLOAT: + if (fabs(divisor) < EPSILON) + oE.error("Division durch Null."); + else { + double f = double(int32_t(std::get(oE.self()._data)) % TRUNC(divisor)); + oE.self()._data = f; + } + break; + case VT_STRING: + oE.error("Strings koennen nicht in Modulooperationen verwendet werden."); + break; + default: + break; + } + } break; + case VT_STRING: + oE.error("Strings koennen nicht in Modulooperation verwendet werden."); + break; + default: + break; + } + return oE; +} + +Value Value::operator-() const +{ + Value oE(*this); + + if (cself().getType() == VT_EMPTY) + oE.error("Operation mit typlosem Operanden."); + if (oE.self().getType() == VT_ERROR) + return oE; + + const TYPE type = cself().getType(); + if (type == VT_INT) { + std::get(oE.self()._data) = -std::get(cself()._data); + } + else if (type == VT_FLOAT) { + std::get(oE.self()._data) = -std::get(cself()._data); + } + else { + oE.error("Negierung eines Strings nicht moeglich."); + } + return oE; +} + +Value Value::pow(const Value& oVal) const +{ + Value oE(*this); + + if (oVal.cself().getType() == VT_ERROR) + oE.error(std::get(oVal.cself()._data).message.c_str()); + if (cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + oE.error("Operation mit typlosem Operanden."); + if (oE.self().getType() == VT_ERROR) + return oE; + + const TYPE lhs = cself().getType(); + const TYPE rhs = oVal.cself().getType(); + + switch (rhs) { + case VT_INT: { + const int32_t exp = std::get(oVal.cself()._data); + switch (lhs) { + case VT_INT: + oE.self()._data = int32_t(::pow(double(std::get(cself()._data)), exp)); + break; + case VT_FLOAT: + oE.self()._data = rangeCheck(::pow(std::get(cself()._data), exp)); + break; + case VT_STRING: + oE.error("Strings koennen nicht in Multiplikationen verwendet werden."); + break; + default: + break; + } + } break; + case VT_FLOAT: { + const double exp = std::get(oVal.cself()._data); + switch (lhs) { + case VT_INT: + oE.self()._data = int32_t(::pow(double(std::get(cself()._data)), TRUNC(exp))); + break; + case VT_FLOAT: + oE.self()._data = rangeCheck(::pow(std::get(cself()._data), exp)); + break; + case VT_STRING: + oE.error("Strings koennen nicht in Multiplikationen verwendet werden."); + break; + default: + break; + } + } break; + case VT_STRING: + oE.error("Strings koennen nicht in Multiplikationen verwendet werden."); + break; + default: + break; + } + return oE; +} + +// ----------------------------------------------------------------------------- +// Comparison operators +// ----------------------------------------------------------------------------- + +static bool AlmostEqualRelativeOrAbsolute(double A, double B, double maxRelativeError = 0.000001, double maxAbsoluteError = 0.000001) +{ + if (fabs(A - B) < maxAbsoluteError) + return true; + double relativeError; + if (fabs(B) > fabs(A)) + relativeError = fabs((A - B) / B); + else + relativeError = fabs((A - B) / A); + return relativeError <= maxRelativeError; +} + +bool Value::operator==(const Value& oVal) const +{ + const TYPE lt = cself().getType(); + const TYPE rt = oVal.cself().getType(); + + if (lt == VT_ERROR || rt == VT_ERROR || lt == VT_EMPTY || rt == VT_EMPTY) + return false; + + if (lt == VT_STRING || rt == VT_STRING) + return cself().asString() == oVal.cself().asString(); + + if ((lt == VT_INT || lt == VT_FLOAT) && (rt == VT_INT || rt == VT_FLOAT)) + return AlmostEqualRelativeOrAbsolute(cself().asReal(), oVal.cself().asReal()); + + return false; +} + +bool Value::operator<(const Value& oVal) const +{ + const TYPE lt = cself().getType(); + const TYPE rt = oVal.cself().getType(); + + if (lt == VT_ERROR || rt == VT_ERROR || lt == VT_EMPTY || rt == VT_EMPTY) + return false; + + if (lt == VT_STRING || rt == VT_STRING) + return cself().asString() < oVal.cself().asString(); + + if ((lt == VT_INT || lt == VT_FLOAT) && (rt == VT_INT || rt == VT_FLOAT)) + return cself().asReal() < oVal.cself().asReal(); + + return false; +} + +bool Value::operator>(const Value& oVal) const +{ + const TYPE lt = cself().getType(); + const TYPE rt = oVal.cself().getType(); + + if (lt == VT_ERROR || rt == VT_ERROR || lt == VT_EMPTY || rt == VT_EMPTY) + return false; + + if (lt == VT_STRING || rt == VT_STRING) + return cself().asString() > oVal.cself().asString(); + + if ((lt == VT_INT || lt == VT_FLOAT) && (rt == VT_INT || rt == VT_FLOAT)) + return cself().asReal() > oVal.cself().asReal(); + + return false; +} + +bool Value::operator<=(const Value& oVal) const +{ + const TYPE lt = cself().getType(); + const TYPE rt = oVal.cself().getType(); + + if (lt == VT_ERROR || rt == VT_ERROR || lt == VT_EMPTY || rt == VT_EMPTY) + return false; + + if (lt == VT_STRING || rt == VT_STRING) + return cself().asString() <= oVal.cself().asString(); + + if ((lt == VT_INT || lt == VT_FLOAT) && (rt == VT_INT || rt == VT_FLOAT)) + return cself().asReal() <= oVal.cself().asReal(); + + return false; +} + +bool Value::operator>=(const Value& oVal) const +{ + const TYPE lt = cself().getType(); + const TYPE rt = oVal.cself().getType(); + + if (lt == VT_ERROR || rt == VT_ERROR || lt == VT_EMPTY || rt == VT_EMPTY) + return false; + + if (lt == VT_STRING || rt == VT_STRING) + return cself().asString() >= oVal.cself().asString(); + + if ((lt == VT_INT || lt == VT_FLOAT) && (rt == VT_INT || rt == VT_FLOAT)) + return cself().asReal() >= oVal.cself().asReal(); + + return false; +} + +// ----------------------------------------------------------------------------- +// Logical operators +// ----------------------------------------------------------------------------- + +bool Value::operator&&(const Value& oVal) const +{ + if (cself().getType() == VT_ERROR || oVal.cself().getType() == VT_ERROR || cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + return false; + + const int32_t a = (cself().getType() == VT_STRING) ? !std::get(cself()._data).empty() : asLong(); + const int32_t b = (oVal.cself().getType() == VT_STRING) ? !std::get(oVal.cself()._data).empty() : oVal.asLong(); + return a && b; +} + +bool Value::operator||(const Value& oVal) const +{ + if (cself().getType() == VT_ERROR || oVal.cself().getType() == VT_ERROR || cself().getType() == VT_EMPTY || oVal.cself().getType() == VT_EMPTY) + return false; + + const int32_t a = (cself().getType() == VT_STRING) ? !std::get(cself()._data).empty() : asLong(); + const int32_t b = (oVal.cself().getType() == VT_STRING) ? !std::get(oVal.cself()._data).empty() : oVal.asLong(); + return a || b; +} + +bool Value::operator!() const +{ + if (cself().getType() == VT_ERROR || cself().getType() == VT_EMPTY) + return false; + + if (cself().getType() == VT_STRING) + return std::get(cself()._data).empty(); + + return asLong() == 0; +} diff --git a/EBase/Value.h b/EBase/Value.h new file mode 100644 index 0000000..4af3749 --- /dev/null +++ b/EBase/Value.h @@ -0,0 +1,283 @@ +/**************************************************************************** + * $Source: D:\\Development\\Repository/ETools/EBase/Value.h,v $ + * $Author: ssh $ + * $Date: 2003/07/01 09:39:30 $ + * $Revision: 1.1 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Klassen fuer die Ausdrucksauswertung in Metabefehlen + ***************************************************************************** + * $Log: Value.h,v $ + * Revision 1.1 2003/07/01 09:39:30 ssh + * *** empty log message *** + * + * Revision 1.1 2003/07/01 09:13:41 ssh + * Initial recvsing of Source... + * + * Revision 1.3 2000/02/24 09:56:47 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.2 1999/10/24 08:02:18 S.Schuemann + * - Anpassungen fuer 1.4 b 4 + * - CReference als Value eingefuehrt + * - Unterprogramme mit #proc und #call implementiert + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include + +using namespace std; + +class CObjectPart; +class Value; +class CValArray; + +// template f(T t) {...}; +#ifdef THIS +#undef THIS +#endif + +enum ValueType { VT_EMPTY, VT_ERROR, VT_INT, VT_FLOAT, VT_STRING, VT_REF, VT_VECTOR, VT_MAP }; + +// Internal storage types for the variant. +// Kept in a namespace to avoid polluting the global scope. +namespace detail { +struct ErrorState +{ + std::string message; +}; + +struct RefPtr +{ + Value* ptr; +}; +} // namespace detail + +class Value +{ +public: + using TYPE = ValueType; + + // The variant alternatives are ordered to match the ValueType enum values, + // so that _data.index() maps directly to ValueType. + using Data = std::variant>, // VT_VECTOR (6) + std::unique_ptr> // VT_MAP (7) + >; + + Value(); + Value(const Value& oVal, bool bRef = false); + Value(Value&& oVal) noexcept; + Value(double fVal); + Value(int32_t nVal); + explicit Value(const char* pcVal); + Value(std::string sVal); + Value(TYPE enType); + explicit Value(const CValArray& arr); + ~Value(); + + ValueType getType() const noexcept; + + bool isContainer() const { return cself().getType() == VT_VECTOR || cself().getType() == VT_MAP; } + + bool isProtectedType() const { return cself()._protectType; } + + void error(const char* pcMsg); + int32_t asLong() const noexcept; + double asReal() const noexcept; + std::string asString(bool bForceLiteral = false) const; + + const char* c_str() const + { + static std::string sErg; + sErg = asString(); + return sErg.c_str(); + } + + bool empty() const { return size() == 0; } + + Value& self() + { + if (auto* r = std::get_if(&_data)) + return *(r->ptr); + return *this; + } + + const Value& cself() const + { + if (const auto* r = std::get_if(&_data)) + return *(r->ptr); + return *this; + } + + const Value& operator=(const Value& oVal); + + bool setAt(const Value& oKey, const Value& oVal = Value()); + Value& getAt(const Value& oKey) const; + Value getNth(int32_t nIdx) const; + int32_t size() const; + void remove(const Value& oKey); + void clear(); + + void swap(Value& oVal); + + Value operator-() const; + Value operator+(const Value& oVal) const; + Value operator-(const Value& oVal) const; + Value operator*(const Value& oVal) const; + Value operator/(const Value& oVal) const; + Value operator%(const Value& oVal) const; + + Value pow(const Value& oVal) const; + + bool operator==(const Value& oVal) const; + + bool operator!=(const Value& oVal) const { return !(*this == oVal); } + + bool operator<(const Value& oVal) const; + bool operator>(const Value& oVal) const; + bool operator<=(const Value& oVal) const; + bool operator>=(const Value& oVal) const; + bool operator&&(const Value& oVal) const; + bool operator||(const Value& oVal) const; + bool operator!() const; + + static int32_t valueCount() { return _cnt; } +#ifdef _DEBUG + static void resetPool() + { + _valPool.clear(); + _idCnt = 0; + } + + static void dumpPool(); +#endif + +protected: + Data _data; + bool _protectType = false; +#ifdef _DEBUG + int32_t _id = 0; +#endif + + static int32_t _cnt; +#ifdef _DEBUG + static int32_t _idCnt; + static std::map _valPool; +#endif +}; + +// Non-owning reference to a Value. Standalone class — does not inherit Value. +class CReference +{ +public: + CReference() + : _ptr(nullptr) + { + } + + explicit CReference(Value& oVal) + : _ptr(&oVal.self()) + { + } + + void set(Value& oVal) { _ptr = &oVal.self(); } + + bool isValid() const { return _ptr != nullptr; } + + Value& self() { return *_ptr; } + + const Value& cself() const { return *_ptr; } + + // Convenience forwarders so callers don't need to go through self() + std::string asString() const { return _ptr ? _ptr->asString() : std::string(); } + + ValueType getType() const { return _ptr ? _ptr->getType() : VT_EMPTY; } + +private: + Value* _ptr; +}; + +// Resizable array of Value. Standalone class — does not inherit Value. +// Used as VKommandos throughout the project. +class CValArray +{ +public: + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + CValArray() + : _changed(false) + { + } + + iterator begin() { return _data.begin(); } + + const_iterator begin() const { return _data.begin(); } + + iterator end() { return _data.end(); } + + const_iterator end() const { return _data.end(); } + + size_t size() const { return _data.size(); } + + bool empty() const { return _data.empty(); } + + void clear() { _data.clear(); } + + Value& operator[](int32_t pos) { return _data[size_t(pos)]; } + + const Value& operator[](int32_t pos) const { return _data[size_t(pos)]; } + + void push_back(const Value& oVal) + { + _data.push_back(oVal); + _changed = true; + } + + bool changed() const { return _changed; } + + void changed(bool chg) { _changed = chg; } + +private: + std::vector _data; + bool _changed; +}; + +class CObjectPart +{ +public: + typedef std::vector IndexField; + + CObjectPart() + : next(nullptr) + , bracket(0) + , assign(false) + { + } + + ~CObjectPart() { delete next; } + + std::string label; + IndexField index; + CObjectPart* next; + char bracket; + bool assign; // true when this object is the LHS of an assignment +}; diff --git a/EBase/charencoding.cpp b/EBase/charencoding.cpp new file mode 100644 index 0000000..3055a83 --- /dev/null +++ b/EBase/charencoding.cpp @@ -0,0 +1,233 @@ +//--------------------------------------------------------------------------------------- +// charencoding.cpp +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#ifdef WIN32 +#pragma warning(disable : 4786) +#endif + +#include +#include + +#include "charencoding.h" +#include "Utility.h" +#include "regexp.h" + +typedef struct _CodePage +{ + const char* _pszName; + UCS2 _pwMap[256]; +} CodePage; + +const CodePage CodePageArray[] = { + {"(iso-8859-1|iso_8859-1|iso-ir-100|csISOLatin1|latin1|l1|ibm819|cp819)", + {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, + 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, + 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, + 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, + 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, + 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff}}, + + {"(iso-8859-15|iso_8859-15|latin-9)", + {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, + 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, + 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, + 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0160, 0x00a7, 0x0161, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, + 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x017d, 0x00b5, 0x00b6, 0x00b7, 0x017e, 0x00b9, 0x00ba, 0x00bb, 0x0152, 0x0153, 0x0178, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, + 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff}}, + + {"windows-1252", {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, + 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, + 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, 0x0090, + 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, + 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, + 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff}}, + + {"(ibm437|cp437|437|csPC8CodePage437)", + {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, + 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, + 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, + 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, + 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, + 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, + 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0}}, + + {"(ibm850|cp850|850|csPC850Multilingual)", + {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, + 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, + 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, + 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, + 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, 0x255a, 0x2554, 0x2569, + 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe, + 0x00de, 0x00da, 0x00db, 0x00d9, 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x25a0, 0x00a0}}, + + {"(macintosh|mac|csmacintosh|macroman)", + {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, + 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, + 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, + 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, + 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, + 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x20ac, 0x2039, 0x203a, 0xfb01, 0xfb02, 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, + 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7}}, + + {0, {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, + 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, + 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, + 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, + 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, + 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff}}}; + +static const CodePage* FindCodePage(const char* pszName) +{ + int i = 0; + while (CodePageArray[i]._pszName) { + if (CRegExp::Match(std::string(pszName), std::string("^(?i)") + CodePageArray[i]._pszName + "$")) + break; + i++; + } + return (CodePageArray[i]._pszName) ? &CodePageArray[i] : 0; +} + +bool CharacterMapper::IsSupported(const std::string& sEnc) +{ + return FindCodePage(sEnc.c_str()) != 0 || IsEqual(sEnc, "utf-8") || IsEqual(sEnc, "utf8"); +} + +inline int CharacterMapper::Map(int c) const +{ + if (_fromUtf8) + return (int)'?'; + if (!_bInitialised) + return c; + else if (_pcCharMap) + return _pcCharMap[c & 0xffff]; + else + return _pwWordMap[c & 0xff]; +} + +std::string CharacterMapper::Map(const std::string& sText) const +{ + if (_fromUtf8) { + std::string utf8 = sText; + return Utf8toIso885915(utf8); + } + if (_toUtf8) { + std::string iso = sText; + return iso885915ToUtf8(iso); + } + std::string enc; + std::string::const_iterator i; + if (!_bInitialised) + return sText; + for (i = sText.begin(); i != sText.end(); i++) { + enc += char(Map(int(*i))); + } + return enc; +} + +CharacterMapper::CharacterMapper(const char* pszFrom, const char* pszTo) +{ + Init(pszFrom, pszTo); +} + +CharacterMapper::~CharacterMapper() +{ + delete[] _pcCharMap; + delete[] _pwWordMap; +} + +void CharacterMapper::Init(const char* pszFrom, const char* pszTo) +{ + const _CodePage* poCP; + int i; + + if ((IsEqual(pszFrom, "utf-8") || IsEqual(pszFrom, "utf8")) && IsEqual(pszTo, "iso-8859-1")) { + _fromUtf8 = true; + _bInitialised = true; + return; + } + if (IsEqual(pszFrom, "iso-8859-1") && (IsEqual(pszTo, "utf-8") || IsEqual(pszTo, "utf8"))) { + _toUtf8 = true; + _bInitialised = true; + return; + } + delete[] _pcCharMap; + _pcCharMap = 0; + delete[] _pwWordMap; + _pwWordMap = 0; + + if (IsEqual(pszFrom, pszTo)) + return; + + if (!IsEqual(pszFrom, "UNICODE")) { + // Ausgangszeichensatz ist 8-Bit + poCP = FindCodePage(pszFrom); + if (!poCP) { + ERRMSG(0, ("Fehler: Keine Zeichenkonvertierung fuer '%s' bekannt.", pszFrom)); + return; + } + _pwWordMap = new UCS2[256]; + for (i = 0; i < 256; i++) + _pwWordMap[i] = poCP->_pwMap[i]; + if (!IsEqual(pszTo, "UNICODE")) { + // Zielzeichensatz ist 8-Bit + CharacterMapper oCM("UNICODE", pszTo); + if (!oCM.IsOK()) + return; + for (i = 0; i < 256; i++) + _pwWordMap[i] = uint16_t(oCM.Map(_pwWordMap[i])); + } + } + else { + // Ausganszeichensatz ist Unicode + poCP = FindCodePage(pszTo); + if (!poCP) { + ERRMSG(0, ("Fehler: Keine Zeichenkonvertierung fuer '%s' bekannt.", pszTo)); + return; + } + _pcCharMap = new char[65536]; + memset(_pcCharMap, (int)'?', 65536); + for (i = 0; i < 256; i++) + _pcCharMap[poCP->_pwMap[i]] = char(i); + } + _bInitialised = true; +} diff --git a/EBase/charencoding.h b/EBase/charencoding.h new file mode 100644 index 0000000..610cb63 --- /dev/null +++ b/EBase/charencoding.h @@ -0,0 +1,56 @@ +//--------------------------------------------------------------------------------------- +// charencoding.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#pragma once + +#include + +typedef unsigned short UCS2; + +class CharacterMapper +{ +public: + // CharacterMapper(int enFrom,int enTo); + CharacterMapper(const char* pszFrom, const char* pszTo); + ~CharacterMapper(); + + bool IsOK() const { return _bInitialised; } + + bool IsToUtf8() const { return _toUtf8; } + + int Map(int c) const; + std::string Map(const std::string& sText) const; + + static bool IsSupported(const std::string& sEnc); + +private: + void Init(const char* pszFrom, const char* pszTo); + + bool _bInitialised = false; + bool _fromUtf8 = false; + bool _toUtf8 = false; + char* _pcCharMap = nullptr; + UCS2* _pwWordMap = nullptr; +}; diff --git a/EBase/hierarchy.cpp b/EBase/hierarchy.cpp new file mode 100644 index 0000000..eb361d7 --- /dev/null +++ b/EBase/hierarchy.cpp @@ -0,0 +1,137 @@ +//--------------------------------------------------------------------------------------- +// hierarchy.cpp +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +// #include "StdAfx.h" +#include "hierarchy.h" + +extern std::string GetConfigFileName(); + +bool CHierarchy::m_bInitialized = false; +CHierarchy::InfoMap CHierarchy::m_coHInfos; +CHierarchy::BlockNames CHierarchy::m_csBlockNames; + +CHierarchyInfo::CHierarchyInfo(CConfigFile& oCF, const std::string& sName) + : m_sName(sName) +{ + size_t i = 1; + size_t j; + while (true) { + if (!oCF.FetchLine("CRTags", i++, false)) + break; + if (IsEqual(oCF.GetString(0), sName.c_str())) { + j = 1; + while (oCF.IsString(j)) { + m_csAttributes.insert(oCF.GetString(j++)); + } + } + } +} + +bool CHierarchy::IsInitialized() +{ + return m_bInitialized; +} + +void CHierarchy::Init(const std::string& sFName) +{ + CConfigFile oCF(sFName); + CHierarchyInfo::ptr pHI; + std::string sBlk; + size_t i = 1; + size_t j; + size_t k; + while (true) { + if (!oCF.FetchLine("CRHierarchy", i, false)) + break; + pHI = CHierarchyInfo::ptr(new CHierarchyInfo(oCF, oCF.GetString(0))); + if (!oCF.FetchLine("CRHierarchy", i++, false)) + break; + m_csBlockNames.insert(Flatten(pHI->m_sName)); + pHI->m_bHasUniqueID = oCF.GetLong(1) != 0; + j = size_t(oCF.GetLong(4)); + for (k = 0; k < j; k++) { + pHI->m_csKeys.push_back(Flatten(oCF.GetString(k + 5))); + pHI->m_ciKeyMap[Flatten(oCF.GetString(k + 5))] = k; + } + while (true) { + sBlk = Flatten(oCF.GetString(k + 5)); + if (sBlk.empty()) + break; + pHI->m_csSubBlocks.insert(sBlk); + m_csBlockNames.insert(sBlk); + k++; + } + m_coHInfos[Flatten(pHI->m_sName)] = pHI; + } + m_bInitialized = true; +} + +void CHierarchy::Free() +{ + m_coHInfos.clear(); +} + +bool CHierarchy::IsChild(const std::string& sBlockName, const std::string& sChildName) +{ + if (!IsInitialized()) { + Init(GetConfigFileName()); + } + + InfoMap::iterator hi = m_coHInfos.find(Flatten(sBlockName)); + if (hi != m_coHInfos.end() && (*hi).second) { + CHierarchyInfo::SubBlocks::iterator hii = (*hi).second->m_csSubBlocks.find(Flatten(sChildName)); + return hii != (*hi).second->m_csSubBlocks.end(); + } + + return false; +} + +CHierarchyInfo::ptr CHierarchy::Lookup(const std::string& sBlockName) +{ + CHierarchyInfo::ptr pHI; + + if (!IsInitialized()) { + Init(GetConfigFileName()); + } + + InfoMap::iterator hi = m_coHInfos.find(Flatten(sBlockName)); + if (hi != m_coHInfos.end()) { + pHI = (*hi).second; + } + else { + CConfigFile oCF(GetConfigFileName()); + + pHI = CHierarchyInfo::ptr(new CHierarchyInfo(oCF, sBlockName)); + if (!pHI->m_csAttributes.empty()) { + m_csBlockNames.insert(Flatten(sBlockName)); + m_coHInfos[Flatten(sBlockName)] = pHI; + } + else { + m_coHInfos[Flatten(sBlockName)] = CHierarchyInfo::ptr(); + } + } + + return pHI; +} diff --git a/EBase/hierarchy.h b/EBase/hierarchy.h new file mode 100644 index 0000000..0396ed3 --- /dev/null +++ b/EBase/hierarchy.h @@ -0,0 +1,69 @@ +//--------------------------------------------------------------------------------------- +// hierarchy.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#pragma once + +#include +#include +#include +#include + +#include "Utility.h" + +class CHierarchyInfo +{ +public: + typedef std::shared_ptr ptr; + typedef std::vector KeyNames; + typedef std::map KeyMap; + typedef std::set SubBlocks; + typedef std::set Attributes; + + CHierarchyInfo(CConfigFile& oCF, const std::string& sName); + std::string m_sName; + bool m_bHasUniqueID; + KeyNames m_csKeys; + KeyMap m_ciKeyMap; + SubBlocks m_csSubBlocks; + Attributes m_csAttributes; +}; + +class CHierarchy +{ +public: + typedef std::map InfoMap; + typedef std::set BlockNames; + + static bool IsInitialized(); + static void Init(const std::string& sFName); + static void Free(); + static bool IsChild(const std::string& sBlockName, const std::string& sChildName); + static CHierarchyInfo::ptr Lookup(const std::string& sBlockName); + +private: + static bool m_bInitialized; + static InfoMap m_coHInfos; + static BlockNames m_csBlockNames; +}; diff --git a/EBase/iso8859-1.c b/EBase/iso8859-1.c new file mode 100644 index 0000000..a7ad1bf --- /dev/null +++ b/EBase/iso8859-1.c @@ -0,0 +1,183 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file is automatically written by the dftables auxiliary +program. If you edit it by hand, you might like to edit the Makefile to +prevent its ever being regenerated. + +This file is #included in the compilation of pcre.c to build the default +character tables which are used when no tables are passed to the compile +function. */ + +static unsigned char pcre_ISO_8859_1_tables[] = { + +/* This table is a lower casing table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,154,139,156,141,158,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,255, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,215, + 248,249,250,251,252,253,254,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table is a case flipping table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,154,139,156,141,158,143, + 144,145,146,147,148,149,150,151, + 152,153,138,155,140,157,142,255, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,215, + 248,249,250,251,252,253,254,223, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,247, + 216,217,218,219,220,221,222,159, + +/* This table contains bit maps for various character classes. +Each map is 32 bytes long and the bits run from the least +significant end of each byte. The classes that have their own +maps are: space, xdigit, digit, upper, lower, word, graph +print, punct, and cntrl. Other classes are built from combinations. */ + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, + 0x00,0x54,0x00,0x80,0x00,0x00,0x00,0x00, + 0xff,0xff,0x7f,0x7f,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, + 0x08,0x00,0x00,0x54,0x00,0x04,0x20,0x04, + 0x00,0x00,0x00,0x80,0xff,0xff,0x7f,0xff, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x08,0x54,0x00,0xd4,0x00,0x04,0x2c,0x06, + 0xff,0xff,0x7f,0xff,0xff,0xff,0x7f,0xff, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0xfc,0x5e,0xfe,0xdc,0xfe,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0x00,0x02,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0xfc,0x5e,0xfe,0xdc,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, + 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, + 0xf4,0x0a,0xfe,0x08,0xfe,0xff,0xff,0xff, + 0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00, + + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, + 0x02,0xa0,0x01,0x20,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00, /* 128- */ + 0x00,0x00,0x12,0x00,0x12,0x00,0x12,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144- */ + 0x00,0x00,0x12,0x00,0x12,0x00,0x12,0x12, /* 152- */ + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - */ + 0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00, /* - */ + 0x00,0x00,0x14,0x14,0x00,0x12,0x00,0x00, /* - */ + 0x00,0x14,0x12,0x00,0x00,0x00,0x00,0x00, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x00, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x00, /* - */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12};/* - */ + +/* End of chartables.c */ diff --git a/EBase/regexp.cpp b/EBase/regexp.cpp new file mode 100644 index 0000000..fcf38ce --- /dev/null +++ b/EBase/regexp.cpp @@ -0,0 +1,281 @@ +//--------------------------------------------------------------------------------------- +// regexp.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#include "regexp.h" + +#include "iso8859-1.c" + +const unsigned char* CRegExp::m_pCharTables = pcre_ISO_8859_1_tables; + +CRegExp::CRegExp(const std::string& sRE, int nMode) + : m_pRE(NULL) + , m_pMatchData(NULL) + , m_nCount(0) + , m_pOffsets(NULL) +{ + if (!sRE.empty()) + Prepare(sRE, nMode); +} + +CRegExp::~CRegExp() +{ + if (m_pMatchData) + pcre2_match_data_free(m_pMatchData); + if (m_pRE) + pcre2_code_free(m_pRE); +} + +bool CRegExp::Prepare(const std::string& sRE, int nMode) +{ + int errorcode; + PCRE2_SIZE erroroffset; + + m_nCount = 0; + + if (m_sRE == sRE) + return true; + + m_sLastError = ""; + + if (m_pMatchData) { + pcre2_match_data_free(m_pMatchData); + m_pMatchData = NULL; + } + if (m_pRE) { + pcre2_code_free(m_pRE); + m_pRE = NULL; + } + + pcre2_compile_context* ccontext = pcre2_compile_context_create(NULL); + if (m_pCharTables) + pcre2_set_character_tables(ccontext, m_pCharTables); + + m_pRE = pcre2_compile((PCRE2_SPTR)sRE.c_str(), PCRE2_ZERO_TERMINATED, (uint32_t)nMode, &errorcode, &erroroffset, ccontext); + pcre2_compile_context_free(ccontext); + + if (!m_pRE) { + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); + m_sLastError = (const char*)buffer; + return false; + } + + m_pMatchData = pcre2_match_data_create_from_pattern(m_pRE, NULL); + m_pOffsets = pcre2_get_ovector_pointer(m_pMatchData); + + m_sRE = sRE; + + return true; +} + +bool CRegExp::Find(const std::string& sText, int nPos) +{ + m_nCount = pcre2_match(m_pRE, (PCRE2_SPTR)sText.c_str(), sText.size(), nPos, PCRE2_NOTEMPTY, m_pMatchData, NULL); + return m_nCount > 0; +} + +void CRegExp::UseLocale() +{ + m_pCharTables = pcre2_maketables(NULL); +} + +bool CRegExp::Match(const std::string& sText, const std::string& sRE) +{ + CRegExp oRE; + + if (oRE.Prepare(sRE)) { + return oRE.Find(sText, 0); + } + return false; +} + +bool CRegExp::Replace(std::string& sText, const std::string& sRE, const std::string& sRep) +{ + CRegExp oRE; + bool bLoop = true; + int pos = 0; + + if (oRE.Prepare(sRE)) { + do { + if (oRE.Find(sText, pos)) { + sText.replace(size_t(oRE.Begin()), size_t(oRE.Size()), sRep); + pos = oRE.Begin() + int(sRep.size()); + } + else + bLoop = false; + } while (bLoop); + return true; + } + else + return false; +} + +bool CRegExp::Replace(std::string& sText, const std::vector& coRE, const std::vector& coRep) +{ + if (coRE.size() != coRep.size()) + return false; + for (size_t i = 0; i < coRE.size(); i++) + if (!Replace(sText, coRE[i], coRep[i])) + return false; + return true; +} + +bool CRegExp::Replace(std::string& sText, const char** ppcRE, const char** ppcRep) +{ + while (*ppcRE && *ppcRep) + if (!Replace(sText, std::string(*ppcRE++), std::string(*ppcRep++))) + return false; + return true; +} + +bool CRegExp::ReplaceCall(std::string& sText, const std::string& sRE, ModifyFunction pfMod) +{ + std::string sRep; + CRegExp oRE; + bool bLoop = true; + int pos = 0; + + if (oRE.Prepare(sRE)) { + do { + if (oRE.Find(sText, pos)) { + sRep = sText.substr(size_t(oRE.Begin()), size_t(oRE.Size())); + pfMod(sRep); + sText.replace(size_t(oRE.Begin()), size_t(oRE.Size()), sRep); + pos = oRE.Begin() + int(sRep.size()); + } + else + bLoop = false; + } while (bLoop); + return true; + } + else + return false; +} + +bool CRegExp::ReplaceCall(std::string& sText, const std::string& sRE, const std::string& sRep, ModifyFunction2 pfMod) +{ + std::string sRepStr; + CRegExp oRE; + bool bLoop = true; + int pos = 0; + + if (oRE.Prepare(sRE)) { + do { + if (oRE.Find(sText, pos)) { + sRepStr = sText.substr(size_t(oRE.Begin()), size_t(oRE.Size())); + pfMod(sRep, sRepStr); + sText.replace(size_t(oRE.Begin()), size_t(oRE.Size()), sRepStr); + pos = oRE.Begin() + int(sRepStr.size()); + } + else + bLoop = false; + } while (bLoop); + return true; + } + else + return false; +} + +bool CRegExp::RuledReplace(std::string& sText, const std::string& sRE, const std::string& sRep) +{ + typedef std::pair PatternPosition; + typedef std::list Patterns; + std::string sRepStr; + Patterns coPattPos; + CRegExp oRE; + bool bLoop = true; + int pos = 0; + + // TODO: Verify accent acute solution! + if (oRE.Prepare(R"((?!\\)\$([+&`']|[\d]+))")) { + do { + if (oRE.Find(sRep, pos)) { + coPattPos.push_back(PatternPosition(oRE.Begin(), oRE.Size())); + pos = oRE.Begin() + oRE.Size(); + } + else + bLoop = false; + } while (bLoop); + } + else + return false; + + bLoop = true; + pos = 0; + if (oRE.Prepare(sRE)) { + do { + if (oRE.Find(sText, pos)) { + sRepStr = sRep; + std::string sR; + for (Patterns::const_iterator pi = coPattPos.begin(); pi != coPattPos.end(); pi++) { + switch (sRep[size_t((*pi).first + 1)]) { + case '0': + case '&': + sR = oRE.SubStr(sText); + break; + case '`': + sR = sText.substr(0, size_t(oRE.Begin())); + break; + case '\'': + sR = sText.substr(size_t(oRE.Begin() + oRE.Size())); + break; + case '+': + sR = oRE.SubStr(sText, oRE.Count() - 1); + break; + default: + int idx = atoi(sRep.c_str() + (*pi).first + 1); + sR = oRE.SubStr(sText, idx); + } + sRepStr.replace(size_t((*pi).first), size_t((*pi).second), sR); + } + sText.replace(size_t(oRE.Begin()), size_t(oRE.Size()), sRepStr); + pos = oRE.Begin() + int(sRepStr.size()); + } + else + bLoop = false; + } while (bLoop); + return true; + } + else + return false; +} + +bool CRegExp::RuledReplace(std::string& sText, const std::vector& coRE, const std::vector& coRep) +{ + if (coRE.size() != coRep.size()) + return false; + for (size_t i = 0; i < coRE.size(); i++) + if (!RuledReplace(sText, coRE[i], coRep[i])) + return false; + return true; +} + +bool CRegExp::RuledReplace(std::string& sText, const char** ppcRE, const char** ppcRep) +{ + while (*ppcRE && *ppcRep) + if (!RuledReplace(sText, std::string(*ppcRE++), std::string(*ppcRep++))) + return false; + return true; +} diff --git a/EBase/regexp.h b/EBase/regexp.h new file mode 100644 index 0000000..142a4c1 --- /dev/null +++ b/EBase/regexp.h @@ -0,0 +1,232 @@ +//--------------------------------------------------------------------------------------- +// regexp.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#pragma once + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include +#include +#include +#include + +/** + * C++-Kapselung der Regular-Expression-Library libpcre. + * + * Diese Klasse kapselt die Benutzung regulärer Ausdrücke auf eine für + * C++-Anwendung angenehmere Weise. + * Libpcre ist von Philip Hazel, nähere Infos unter http://www.pcre.org + * + * @attention Die Klasse unterstützt zur Zeit nicht mehr als 96 Subexpressions! + * @author Steffen Schümann, Hamburg + */ +class CRegExp +{ +public: + /** Veränderungsfunktion für ReplaceCall. + * @param string Hier wird der Treffer übergeben, kann verändert werden + * und wird hinterher wieder eingefügt. + */ + typedef void (*ModifyFunction)(std::string&); + /** Veränderungsfunktion für ReplaceCall. + * @param string1 Hier wird der Replace-Text übergeben + * @param string2 Hier wird der Treffer übergeben, kann verändert werden + * und wird hinterher wieder eingefügt. + */ + typedef void (*ModifyFunction2)(const std::string&, std::string&); + + /** Konstruktor der Klasse. + * Der Konstruktor initialisiert, bei übergebenem Pattern, die Auswertung. + * @param sRE Ausdruck der für das Matching gelten soll. + * @see Prepare() + */ + CRegExp(const std::string& sRE = std::string(), int nMode = 0); + + /** Destruktor. */ + ~CRegExp(); + + /** Neues Muster übergeben und initialisieren + * @param sRE Ausdruck der für das Matching gelten soll. + * @param RE-Optionen aus libpcre + * @return Trat ein Fehler auf, wird false, andernfalls + * true zurückgegeben. + */ + bool Prepare(const std::string& sRE, int nMode = 0); + + /** Durchsuchen eines Textes. + * Die Methode dient dem Durchführen einer Suche auf einem übergebenen Text, + * ab einer optional angegebenen Position. + * @param sText Text der duchsucht werden soll. + * @param nPos Position ab der gesucht werden soll. + * @return Wurde ein Treffer gefunden wird true, andernfalls + * false zurückgegeben. + */ + bool Find(const std::string& sText, int nPos = 0); + + /** Anzahl der Teilausdrücke inclusive Gesamttreffer. + * Mit dieser Methode wird die Anzahl der (Teil-)Ausdrücke zurückgegeben + * die gepasst haben. Dabei ist der Treffer insgesammt enthalten. + */ + int Count() const { return m_nCount; } + + /** Startoffset des nIdx-ten (Teil-)Ausdrucks ermitteln (0=Gesammtausdruck)*/ + int Begin(int nIdx = 0) const { return (m_nCount > 0 && nIdx >= 0 && nIdx < m_nCount) ? (int)m_pOffsets[nIdx * 2] : 0; } + + /** Endoffset des nIdx-ten (Teil-)Ausdrucks ermitteln (0=Gesammtausdruck)*/ + int End(int nIdx = 0) const { return (m_nCount > 0 && nIdx >= 0 && nIdx < m_nCount) ? (int)m_pOffsets[nIdx * 2 + 1] : 0; } + + /** Größe des nIdx-ten (Teil-)Ausdrucks ermitteln (0=Gesammtausdruck)*/ + int Size(int nIdx = 0) const { return (m_nCount > 0 && nIdx >= 0 && nIdx < m_nCount) ? (int)(m_pOffsets[nIdx * 2 + 1] - m_pOffsets[nIdx * 2]) : 0; } + + /** Teilstring aus Text gemäß eines Subpatterns extrahieren (0=Gesammtausdruck)*/ + std::string SubStr(const std::string& sText, int nIdx = 0) const { return Begin(nIdx) < 0 ? "" : sText.substr(size_t(Begin(nIdx)), size_t(Size(nIdx))); } + + /** Fehlermeldung eines aufgetretenen Fehlers in dem Ausdruck ermitteln */ + std::string Error() const { return m_sLastError; } + + /** Verändern der verwendeten Locale-Einstellungen des Pattern-Matchers. + */ + static void UseLocale(); + + /** Vereinfachte Abfrage, ob der Ausdruck auf den Text passt. + * Um Festzustellen ob der Text den Ausdruck kommplett erfüllt, + * muß der Ausdruck mit Ankern versehen sein, also '^' bzw. '$'. + * @param sText Text der duchsucht werden soll. + * @param sRE Regulärer Ausdruck der auf den Text passen soll. + * @return Wurde ein Treffer gefunden wird true, andernfalls + * false zurückgegeben. + * @attention Um auf viele Texte nach einem Treffer zu suchen sollte + * besser eine CRegExp-Instanz erzeugt werden und mit + * Find() gesucht werden, um kein wiederholtes übersetzen + * des regulären Ausdrucks zu erzwingen. + */ + static bool Match(const std::string& sText, const std::string& sRE); + + /** Vereinfachter Aufruf um alle Vorkommen eines Ausdrucks in einem Text zu ersetzen. + * @param sText Text der duchsucht werden soll. + * @param sRE Regulärer Ausdruck der auf den Text passen soll. + * @param sRep Text durch den die Treffer ersetzt werden sollen. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool Replace(std::string& sText, const std::string& sRE, const std::string& sRep); + + /** Vereinfachter Aufruf um alle Vorkommen diverse Ausdrücke in einem Text zu ersetzen. + * @param sText Text der duchsucht werden soll. + * @param coRE Vector regulärer Ausdrücke die auf den Text passen sollen. + * @param coRep Vector von Ersetzungstexten mit gleicher Anzahl wie coRE. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool Replace(std::string& sText, const std::vector& coRE, const std::vector& coRep); + + /** Vereinfachter Aufruf um alle Vorkommen diverse Ausdrücke in einem Text zu ersetzen. + * @param sText Text der duchsucht werden soll. + * @param ppcRE Array regulärer Ausdrücke die auf den Text passen sollen. + * @param ppcRep Array von Ersetzungstexten mit gleicher Anzahl wie ppcRE. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool Replace(std::string& sText, const char** ppcRE, const char** ppcRep); + + /** Vereinfachter Aufruf um Vorkommen eines Ausdrucks CallBack-Modifiziert zu ersetzen/verändern. + * @param sText Text der duchsucht werden soll. + * @param sRE Regulärer Ausdruck der auf den Text passen soll. + * @param pfMod Funktionszeiger auf eine Funktion vom Typ ModifyFunction, die + * die Treffer per Referenz bekommt und verändern kann. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool ReplaceCall(std::string& sText, const std::string& sRE, ModifyFunction pfMod); + + /** Vereinfachter Aufruf um Vorkommen eines Ausdrucks CallBack-Modifiziert zu ersetzen/verändern. + * @param sText Text der duchsucht werden soll. + * @param sRE Regulärer Ausdruck der auf den Text passen soll. + * @param sRep Text-(Vorschlag) durch den die Treffer ersetzt werden sollen. + * @param pfMod Funktionszeiger auf eine Funktion vom Typ ModifyFunction, die + * den Textvorschlag und die Treffer per Referenz bekommt und + * verändern kann. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool ReplaceCall(std::string& sText, const std::string& sRE, const std::string& sRep, ModifyFunction2 pfMod); + + /** Vereinfachter Aufruf um alle Vorkommen eines Ausdrucks in einem Text regelbasiert zu ersetzen. + * Es finden die Ersetzungsregeln von Perl Verwendung, d.h. im Ersetzungstext können Regeln + * angegeben werden: + * $0 o. $& Fügt den gesammten Treffertext ein. + * $i Fügt den Treffertext des i-ten Subpatterns ein. + * $+ Fügt den Treffertext des letzten Subpatterns ein. + * $` Fügt den Text vor dem Treffer ein. + * $´ Fügt den Text nach dem Treffer ein. + * @param sText Text der duchsucht werden soll. + * @param sRE Regulärer Ausdruck der auf den Text passen soll. + * @param sRep Text durch den die Treffer ersetzt werden sollen. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool RuledReplace(std::string& sText, const std::string& sRE, const std::string& sRep); + + /** Vereinfachter Aufruf um alle Vorkommen diverser Ausdrücke in einem Text regelbasiert zu ersetzen. + * Es finden die Ersetzungsregeln von Perl Verwendung, d.h. im Ersetzungstext können Regeln + * angegeben werden: + * $0 o. $& Fügt den gesammten Treffertext ein. + * $i Fügt den Treffertext des i-ten Subpatterns ein. + * $+ Fügt den Treffertext des letzten Subpatterns ein. + * $` Fügt den Text vor dem Treffer ein. + * $´ Fügt den Text nach dem Treffer ein. + * @param sText Text der duchsucht werden soll. + * @param coRE Vector mit regulären Ausdrücken die auf den Text passen sollen. + * @param coRep Vector mit Texten durch den die Treffer ersetzt werden sollen. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool RuledReplace(std::string& sText, const std::vector& coRE, const std::vector& coRep); + + /** Vereinfachter Aufruf um alle Vorkommen diverser Ausdrücke in einem Text regelbasiert zu ersetzen. + * Es finden die Ersetzungsregeln von Perl Verwendung, d.h. im Ersetzungstext können Regeln + * angegeben werden: + * $0 o. $& Fügt den gesammten Treffertext ein. + * $i Fügt den Treffertext des i-ten Subpatterns ein. + * $+ Fügt den Treffertext des letzten Subpatterns ein. + * $` Fügt den Text vor dem Treffer ein. + * $´ Fügt den Text nach dem Treffer ein. + * @param sText Text der duchsucht werden soll. + * @param ppcRE Array mit regulären Ausdrücken die auf den Text passen sollen. + * @param ppcRep Array mit Texten durch den die Treffer ersetzt werden sollen. + * @return Ging alles gut wird true, bei einem Fehler im Ausdruck + * false zurückgegeben. + */ + static bool RuledReplace(std::string& sText, const char** ppcRE, const char** ppcRep); + +private: + std::string m_sRE; + std::string m_sLastError; + pcre2_code* m_pRE; + pcre2_match_data* m_pMatchData; + int m_nCount; + size_t* m_pOffsets; + + static const unsigned char* m_pCharTables; +}; diff --git a/EBase/test/CMakeLists.txt b/EBase/test/CMakeLists.txt new file mode 100644 index 0000000..3dec28f --- /dev/null +++ b/EBase/test/CMakeLists.txt @@ -0,0 +1,12 @@ + +add_executable(ebase_catch_tests ctest_main.cpp expression_tests.cpp value_tests.cpp charencoding_test.cpp) +target_link_libraries(ebase_catch_tests ebase Catch2::Catch2WithMain) + +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(ebase_catch_tests PRIVATE _CRT_SECURE_NO_WARNINGS) + target_compile_options(ebase_catch_tests PRIVATE "$<$:/utf-8>") + target_compile_options(ebase_catch_tests PRIVATE "$<$:/utf-8>") +endif() + +include(Catch) +catch_discover_tests(ebase_catch_tests) diff --git a/EBase/test/charencoding_test.cpp b/EBase/test/charencoding_test.cpp new file mode 100644 index 0000000..5ab2149 --- /dev/null +++ b/EBase/test/charencoding_test.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include + +TEST_CASE("charencoding iso-8859-1 to utf-8") +{ + CharacterMapper enc("iso-8859-1", "utf-8"); + CHECK(enc.Map("\xE4\xF6\xFC\xDF") == "äöüß"); +} + diff --git a/EBase/test/ctest_main.cpp b/EBase/test/ctest_main.cpp new file mode 100644 index 0000000..c70dc0a --- /dev/null +++ b/EBase/test/ctest_main.cpp @@ -0,0 +1 @@ +#include diff --git a/EBase/test/expression_tests.cpp b/EBase/test/expression_tests.cpp new file mode 100644 index 0000000..e1cec1b --- /dev/null +++ b/EBase/test/expression_tests.cpp @@ -0,0 +1,322 @@ +#include +#include + +// Mocking required externs to avoid linking with half of the project +#include +#include + +Value DoPartei(CObjectPart*) { return Value(); } +Value DoUnit(CObjectPart*) { return Value(); } +Value DoShip(CObjectPart*) { return Value(); } +Value DoBuilding(CObjectPart*) { return Value(); } +Value DoRegion(CObjectPart*) { return Value(); } +Value DoGrenze(CObjectPart*) { return Value(); } +Value DoReport(CObjectPart*) { return Value(); } +Value DoThings(CObjectPart*) { return Value(); } +Value DoRaces(CObjectPart*) { return Value(); } +Value DoDB(CObjectPart*) { return Value(); } + +Value FGetUnitOfRegion(Expression*, ArgumentList&) { return Value(); } +Value FRandom(Expression*, ArgumentList&) { return Value(); } +Value FEquals(Expression*, ArgumentList& coArgs) { + if (coArgs.size() >= 2) return Value(coArgs[0] == coArgs[1] ? 1 : 0); + return Value(0); +} +Value FMatch(Expression*, ArgumentList&) { return Value(); } +Value FBefore(Expression*, ArgumentList&) { return Value(); } +Value FAfter(Expression*, ArgumentList&) { return Value(); } +Value FCrop(Expression*, ArgumentList&) { return Value(); } +Value FChange(Expression*, ArgumentList&) { return Value(); } +Value FSubStr(Expression*, ArgumentList&) { return Value(); } +Value FCeil(Expression*, ArgumentList& coArgs) { return Value(ceil(coArgs[0].asReal())); } +Value FFloor(Expression*, ArgumentList& coArgs) { return Value(floor(coArgs[0].asReal())); } +Value FAbs(Expression*, ArgumentList& coArgs) { return Value(fabs(coArgs[0].asReal())); } +Value FSign(Expression*, ArgumentList&) { return Value(); } +Value FSqrt(Expression*, ArgumentList& coArgs) { return Value(sqrt(coArgs[0].asReal())); } +Value FExp(Expression*, ArgumentList& coArgs) { return Value(exp(coArgs[0].asReal())); } +Value FLog(Expression*, ArgumentList& coArgs) { return Value(log(coArgs[0].asReal())); } +Value FLog10(Expression*, ArgumentList& coArgs) { return Value(log10(coArgs[0].asReal())); } +Value FFloat(Expression*, ArgumentList& coArgs) { return Value(coArgs[0].asReal()); } +Value FInt(Expression*, ArgumentList& coArgs) { return Value(coArgs[0].asLong()); } +Value FIsNothing(Expression*, ArgumentList&) { return Value(); } +Value FItoan(Expression*, ArgumentList&) { return Value(); } +Value FAntoi(Expression*, ArgumentList&) { return Value(); } +Value FXName(Expression*, ArgumentList&) { return Value(); } +Value FLength(Expression*, ArgumentList&) { return Value(); } +Value FFlatten(Expression*, ArgumentList&) { return Value(); } +Value FToLower(Expression*, ArgumentList& coArgs) { + std::string s = coArgs[0].asString(); + for (auto& c : s) c = tolower(c); + return Value(s); +} +Value FToUpper(Expression*, ArgumentList& coArgs) { + std::string s = coArgs[0].asString(); + for (auto& c : s) c = toupper(c); + return Value(s); +} +Value FTypeOf(Expression*, ArgumentList&) { return Value(); } +Value FTime(Expression*, ArgumentList&) { return Value(); } +Value FAnd(Expression*, ArgumentList& coArgs) { return Value(coArgs[0].asLong() && coArgs[1].asLong() ? 1 : 0); } +Value FOr(Expression*, ArgumentList& coArgs) { return Value(coArgs[0].asLong() || coArgs[1].asLong() ? 1 : 0); } +Value FXor(Expression*, ArgumentList& coArgs) { return Value(coArgs[0].asLong() ^ coArgs[1].asLong() ? 1 : 0); } +Value FNot(Expression*, ArgumentList& coArgs) { return Value(!coArgs[0].asLong() ? 1 : 0); } +Value Fgv(Expression*, ArgumentList&) { return Value(); } +Value Fcv(Expression*, ArgumentList&) { return Value(); } + +Value FOpen(Expression*, ArgumentList&) { return Value(); } +Value FClose(Expression*, ArgumentList&) { return Value(); } +Value FReadLine(Expression*, ArgumentList&) { return Value(); } +Value FWriteLine(Expression*, ArgumentList&) { return Value(); } +Value FReadValue(Expression*, ArgumentList&) { return Value(); } +Value FWriteValue(Expression*, ArgumentList&) { return Value(); } +Value FStatus(Expression*, ArgumentList&) { return Value(); } +Value FStatusText(Expression*, ArgumentList&) { return Value(); } +Value FSystem(Expression*, ArgumentList&) { return Value(); } +bool DoUserFunction(const std::string&, ArgumentList&, Value*) { return false; } + +std::string GetConfigFileName() { return ""; } +void SetConfigFileName(const std::string&) {} +bool ExistUserFunction(const std::string&) { return false; } + +TEST_CASE("Expression basics", "[cexpression]") { + Expression::Variables vars; + Value result; + + SECTION("Simple arithmetic") { + std::string exprStr = "1 + 2 * 3"; + Expression expr(exprStr, __FILE__, __LINE__); + int a; + expr.evaluate(vars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asLong() == 7); + } + + SECTION("Parentheses") { + std::string exprStr = "(1 + 2) * 3"; + Expression expr(exprStr, __FILE__, __LINE__); + int a; + expr.evaluate(vars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asLong() == 9); + } + + SECTION("Floating point") { + std::string exprStr = "3.5 * 2"; + Expression expr(exprStr, __FILE__, __LINE__); + int a; + expr.evaluate(vars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asReal() == Catch::Approx(7.0)); + } + + SECTION("Global variables") { + Expression::Variables localVars; + Value val(42); + + std::string exprStr = "X + 8"; + Expression expr(exprStr, __FILE__, __LINE__); + expr.setGlobal("X", &val); + + int a; + expr.evaluate(localVars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asLong() == 50); + + Expression::clearAllVars(); + } + + SECTION("Local variables") { + // Local variables need a leading $ sign. + Expression::Variables localVars; + Value val(42); + + std::string exprStr = "$X + 8"; + Expression expr(exprStr, __FILE__, __LINE__); + localVars.insert(std::make_pair("$X", val)); + + int a; + expr.evaluate(localVars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asLong() == 50); + + Expression::clearAllVars(); + } + + SECTION("Strings") { + std::string exprStr = "'Hello ' + 'World'"; + Expression expr(exprStr, __FILE__, __LINE__); + int a; + expr.evaluate(vars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asString() == "Hello World"); + } + + SECTION("Arithmetic precedence") { + Expression::Variables v; + int a; + auto eval = [&](std::string s) { + Expression e(s, __FILE__, __LINE__); + Value r; + e.evaluate(v, s.c_str(), &r, &a, false, false); + return r; + }; + + // Level 2 (+, -) vs Level 3 (*, /, %) + CHECK(eval("1 + 2 * 3").asLong() == 7); + CHECK(eval("10 - 4 / 2").asLong() == 8); + CHECK(eval("10 % 3 + 1").asLong() == 2); + + // Level 3 vs Level 4 (^) + CHECK(eval("2 * 3 ^ 2").asLong() == 18); // 2 * (3^2) + CHECK(eval("2 ^ 3 * 2").asLong() == 16); // (2^3) * 2 + + // Level 4 (^) vs Level 5 (unary -, !) + // In this parser, Level 4 calls Level 5 for the base, so unary has HIGHER precedence. + CHECK(eval("-2 ^ 2").asLong() == 4); // (-2)^2 = 4 + + // Let's verify unary precedence + CHECK(eval("!0 + 1").asLong() == 2); // (!0) + 1 = 1 + 1 = 2 + } + + SECTION("Unary operators") { + Expression::Variables v; + int a; + auto eval = [&](std::string s) { + Expression e(s, __FILE__, __LINE__); + Value r; + e.evaluate(v, s.c_str(), &r, &a, false, false); + return r; + }; + + CHECK(eval("-5").asLong() == -5); + // NOTE: The current parser does NOT support multiple unary operators like --5 + // Level 5 only checks for ONE operator before calling Level 6. + // CHECK(eval("--5").asLong() == 5); + + CHECK(eval("!1").asLong() == 0); + CHECK(eval("!0").asLong() == 1); + + // String negation (special case in Level 5) + CHECK(eval("!'abc'").asString() == "!abc"); + } + + SECTION("Logical operators") { + Expression::Variables v; + int a; + auto eval = [&](std::string s) { + Expression e(s, __FILE__, __LINE__); + Value r; + e.evaluate(v, s.c_str(), &r, &a, false, false); + return r.asLong(); + }; + + // Bitwise/Logical & and | (Level 1a) + CHECK(eval("1 & 0") == 0); + CHECK(eval("1 | 0") == 1); + + // Precedence: & and | have SAME precedence in Level 1a + CHECK(eval("0 | 1 & 0") == 0); + CHECK(eval("1 | 0 & 0") == 0); // (1 | 0) & 0 = 1 & 0 = 0 + + // Standard logical operators (&&, ||) also supported by Level 1a + CHECK(eval("1 && 0") == 0); + CHECK(eval("1 || 0") == 1); + CHECK(eval("0 || 1 && 0") == 0); + } + + SECTION("Comparison operators") { + Expression::Variables v; + int a; + auto eval = [&](std::string s) { + Expression e(s, __FILE__, __LINE__); + Value r; + e.evaluate(v, s.c_str(), &r, &a, false, false); + return r.asLong(); + }; + + // Level 1b: <, >, <=, >=, ==, != + CHECK(eval("1 == 1") == 1); + CHECK(eval("1 == 2") == 0); + CHECK(eval("1 != 2") == 1); + CHECK(eval("1 < 2") == 1); + CHECK(eval("2 < 1") == 0); + CHECK(eval("1 <= 1") == 1); + CHECK(eval("2 <= 1") == 0); + CHECK(eval("2 > 1") == 1); + CHECK(eval("1 > 2") == 0); + CHECK(eval("2 >= 2") == 1); + CHECK(eval("1 >= 2") == 0); + + // Precedence: Comparisons higher than & + CHECK(eval("1 == 1 & 0 == 1") == 0); + CHECK(eval("1 == 1 | 0 == 1") == 1); + } + + SECTION("Complex precedence example") { + Expression::Variables v; + int a; + auto eval = [&](std::string s) { + Expression e(s, __FILE__, __LINE__); + Value r; + e.evaluate(v, s.c_str(), &r, &a, false, false); + return r; + }; + + // 1 + 2 * 3 ^ 2 == 19 & 1 + // 1 + 2 * 9 == 19 & 1 + // 1 + 18 == 19 & 1 + // 19 == 19 & 1 + // 1 & 1 -> 1 + CHECK(eval("1 + 2 * 3 ^ 2 == 19 & 1").asLong() == 1); + } + + SECTION("Metadata for Shunting-Yard") { + // Precedence + CHECK(Expression::getPrecedence("^") == Expression::PREC_EXPONENT); + CHECK(Expression::getPrecedence("*") == Expression::PREC_MULTIPLICATIVE); + CHECK(Expression::getPrecedence("+") == Expression::PREC_ADDITIVE); + CHECK(Expression::getPrecedence("==") == Expression::PREC_COMPARISON); + CHECK(Expression::getPrecedence("&&") == Expression::PREC_LOGICAL); + CHECK(Expression::getPrecedence("!") == Expression::PREC_UNARY); + + // Relative precedence + CHECK(Expression::getPrecedence("^") > Expression::getPrecedence("*")); + CHECK(Expression::getPrecedence("*") > Expression::getPrecedence("+")); + CHECK(Expression::getPrecedence("+") > Expression::getPrecedence("==")); + CHECK(Expression::getPrecedence("==") > Expression::getPrecedence("&&")); + + // Associativity + CHECK(Expression::getAssociativity("+") == Expression::ASSOC_LEFT); + CHECK(Expression::getAssociativity("*") == Expression::ASSOC_LEFT); + CHECK(Expression::getAssociativity("^") == Expression::ASSOC_RIGHT); + CHECK(Expression::getAssociativity("!") == Expression::ASSOC_RIGHT); + } +} + +TEST_CASE("Expression functions", "[cexpression]") { + Expression::Variables vars; + Value result; + + SECTION("abs") { + std::string exprStr = "abs(-5)"; + Expression expr(exprStr, __FILE__, __LINE__); + int a; + expr.evaluate(vars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asLong() == 5); + } + + SECTION("sqrt") { + std::string exprStr = "sqrt(16)"; + Expression expr(exprStr, __FILE__, __LINE__); + int a; + expr.evaluate(vars, exprStr.c_str(), &result, &a, false, false); + CHECK(result.asReal() == Catch::Approx(4.0)); + } + + SECTION("tolower/toupper") { + std::string exprStr1 = "tolower('AbC')"; + Expression expr1(exprStr1, __FILE__, __LINE__); + int a; + expr1.evaluate(vars, exprStr1.c_str(), &result, &a, false, false); + CHECK(result.asString() == "abc"); + + std::string exprStr2 = "toupper('AbC')"; + Expression expr2(exprStr2, __FILE__, __LINE__); + expr2.evaluate(vars, exprStr2.c_str(), &result, &a, false, false); + CHECK(result.asString() == "ABC"); + } +} diff --git a/EBase/test/value_tests.cpp b/EBase/test/value_tests.cpp new file mode 100644 index 0000000..dd5cbfd --- /dev/null +++ b/EBase/test/value_tests.cpp @@ -0,0 +1,638 @@ +#include +#include + +// ----------------------------------------------------------------------------- +// Construction & type tagging +// ----------------------------------------------------------------------------- + +TEST_CASE("Value construction", "[cvalue]") { + SECTION("default ctor yields VT_EMPTY") { + Value v; + //CHECK(sizeof(Value) == 48); // formerly 64 + CHECK(v.getType() == VT_EMPTY); + } + + SECTION("int32_t ctor yields VT_INT") { + Value v(int32_t(42)); + CHECK(v.getType() == VT_INT); + CHECK(v.asLong() == 42); + CHECK(v.asReal() == Catch::Approx(42.0)); + } + + SECTION("double ctor yields VT_FLOAT") { + Value v(3.14); + CHECK(v.getType() == VT_FLOAT); + CHECK(v.asReal() == Catch::Approx(3.14)); + CHECK(v.asLong() == 3); // truncated + } + + SECTION("const char* ctor yields VT_STRING") { + Value v("hello"); + CHECK(v.getType() == VT_STRING); + CHECK(v.asString() == "hello"); + } + + SECTION("std::string ctor yields VT_STRING") { + Value v(std::string("world")); + CHECK(v.getType() == VT_STRING); + CHECK(v.asString() == "world"); + } + + SECTION("VT_VECTOR ctor creates empty vector container") { + Value v(VT_VECTOR); + CHECK(v.getType() == VT_VECTOR); + CHECK(v.isContainer()); + CHECK(v.size() == 0); + } + + SECTION("VT_MAP ctor creates empty map container") { + Value v(VT_MAP); + CHECK(v.getType() == VT_MAP); + CHECK(v.isContainer()); + CHECK(v.size() == 0); + } + + SECTION("isContainer is false for scalar types") { + CHECK_FALSE(Value().isContainer()); + CHECK_FALSE(Value(int32_t(1)).isContainer()); + CHECK_FALSE(Value(1.0).isContainer()); + CHECK_FALSE(Value("str").isContainer()); + } +} + +TEST_CASE("Value copy construction", "[cvalue]") { + SECTION("scalar copy is independent") { + Value a(int32_t(7)); + Value b(a); + CHECK(b.asLong() == 7); + b = Value(int32_t(99)); + CHECK(a.asLong() == 7); // a not affected + } + + SECTION("copy with bRef=true creates VT_REF") { + Value a(int32_t(5)); + Value ref(a, /*bRef=*/true); + CHECK(ref.getType() == VT_REF); + // dereferencing gives the original value + CHECK(ref.self().asLong() == 5); + } + + SECTION("copy of VT_REF propagates the same reference") { + Value target(int32_t(10)); + Value ref(target, true); + Value ref2(ref); // copy of a ref + CHECK(ref2.getType() == VT_REF); + CHECK(ref2.self().asLong() == 10); + } + + SECTION("vector deep copy is independent") { + Value arr(VT_VECTOR); + arr.setAt(Value(int32_t(0)), Value(int32_t(100))); + Value arr2(arr); + arr2.setAt(Value(int32_t(0)), Value(int32_t(999))); + CHECK(arr.getAt(Value(int32_t(0))).asLong() == 100); // original unchanged + } + + SECTION("map deep copy is independent") { + Value m(VT_MAP); + m.setAt(Value("key"), Value(int32_t(1))); + Value m2(m); + m2.setAt(Value("key"), Value(int32_t(2))); + CHECK(m.getAt(Value("key")).asLong() == 1); // original unchanged + } +} + +// ----------------------------------------------------------------------------- +// asString conversions +// ----------------------------------------------------------------------------- + +TEST_CASE("Value::asString", "[cvalue]") { + SECTION("VT_INT returns decimal string") { + CHECK(Value(int32_t(42)).asString() == "42"); + CHECK(Value(int32_t(-7)).asString() == "-7"); + CHECK(Value(int32_t(0)).asString() == "0"); + } + + SECTION("VT_FLOAT returns 3-decimal string") { + CHECK(Value(1.5).asString() == "1.500"); + CHECK(Value(0.0).asString() == "0.000"); + CHECK(Value(-2.0).asString() == "-2.000"); + } + + SECTION("VT_STRING returns the string value") { + CHECK(Value("hello").asString() == "hello"); + CHECK(Value("").asString() == ""); + } + + SECTION("VT_VECTOR returns '[N Elements]' without literal flag") { + Value v(VT_VECTOR); + v.setAt(Value(int32_t(0)), Value(int32_t(1))); + v.setAt(Value(int32_t(1)), Value(int32_t(2))); + CHECK(v.asString() == "[2 Elements]"); + } + + SECTION("VT_VECTOR with bForceLiteral returns [e0,e1,...] form") { + Value v(VT_VECTOR); + v.setAt(Value(int32_t(0)), Value(int32_t(10))); + v.setAt(Value(int32_t(1)), Value(int32_t(20))); + CHECK(v.asString(true) == "[10,20]"); + } + + SECTION("VT_MAP returns '[N Elements]'") { + Value m(VT_MAP); + m.setAt(Value("a"), Value(int32_t(1))); + CHECK(m.asString() == "[1 Elements]"); + } + + SECTION("VT_EMPTY returns empty string") { + Value v; + CHECK(v.asString() == ""); + } +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- + +TEST_CASE("Value::size", "[cvalue]") { + CHECK(Value(VT_VECTOR).size() == 0); + CHECK(Value(VT_MAP).size() == 0); + CHECK(Value(std::string("abc")).size() == 3); + CHECK(Value(std::string("")).size() == 0); + // Non-string scalars return -1 + CHECK(Value(int32_t(5)).size() == -1); + CHECK(Value(2.0).size() == -1); + CHECK(Value().size() == -1); // VT_EMPTY +} + +// ----------------------------------------------------------------------------- +// Assignment +// ----------------------------------------------------------------------------- + +TEST_CASE("Value assignment", "[cvalue]") { + SECTION("self-assignment is safe") { + Value v(int32_t(3)); + Value* ptr = &v; + v = *ptr; // use pointer to suppress -Wself-assign-overloaded + CHECK(v.asLong() == 3); + } + + SECTION("assign changes type and value") { + Value v(int32_t(1)); + v = Value("text"); + CHECK(v.getType() == VT_STRING); + CHECK(v.asString() == "text"); + } + + SECTION("assign changes type and value") { + Value v(int32_t(1)); + v = Value("text"); + CHECK(v.getType() == VT_STRING); + CHECK(v.asString() == "text"); + } +} + +// ----------------------------------------------------------------------------- +// Error state +// ----------------------------------------------------------------------------- + +TEST_CASE("Value error state", "[cvalue]") { + SECTION("error() sets VT_ERROR and stores message") { + Value v(int32_t(1)); + v.error("something went wrong"); + CHECK(v.getType() == VT_ERROR); + CHECK(v.asString() == "something went wrong"); + } +} + +// ----------------------------------------------------------------------------- +// Arithmetic operators +// ----------------------------------------------------------------------------- + +TEST_CASE("Value arithmetic", "[cvalue]") { + SECTION("int + int") { + Value a(int32_t(3)), b(int32_t(4)); + Value r = a + b; + CHECK(r.getType() == VT_INT); + CHECK(r.asLong() == 7); + } + + SECTION("float + float") { + Value a(1.5), b(2.5); + Value r = a + b; + CHECK(r.getType() == VT_FLOAT); + CHECK(r.asReal() == Catch::Approx(4.0)); + } + + SECTION("float + int promotes to float arithmetic") { + Value a(1.5), b(int32_t(2)); + Value r = a + b; + CHECK(r.getType() == VT_FLOAT); + CHECK(r.asReal() == Catch::Approx(3.5)); + } + + SECTION("string + string concatenates") { + Value a("foo"), b("bar"); + Value r = a + b; + CHECK(r.getType() == VT_STRING); + CHECK(r.asString() == "foobar"); + } + + SECTION("string + int appends formatted integer") { + Value a("num="), b(int32_t(42)); + Value r = a + b; + CHECK(r.getType() == VT_STRING); + CHECK(r.asString() == "num=42"); + } + + SECTION("int + string is an error") { + Value a(int32_t(1)), b("x"); + Value r = a + b; + CHECK(r.getType() == VT_ERROR); + } + + SECTION("int - int") { + Value r = Value(int32_t(10)) - Value(int32_t(3)); + CHECK(r.getType() == VT_INT); + CHECK(r.asLong() == 7); + } + + SECTION("int * int") { + Value r = Value(int32_t(6)) * Value(int32_t(7)); + CHECK(r.getType() == VT_INT); + CHECK(r.asLong() == 42); + } + + SECTION("int / int") { + Value r = Value(int32_t(10)) / Value(int32_t(4)); + CHECK(r.getType() == VT_INT); + CHECK(r.asLong() == 2); // integer division + } + + SECTION("division by zero is an error") { + Value r = Value(int32_t(1)) / Value(int32_t(0)); + CHECK(r.getType() == VT_ERROR); + } + + SECTION("int % int") { + Value r = Value(int32_t(10)) % Value(int32_t(3)); + CHECK(r.asLong() == 1); + } + + SECTION("modulo by zero is an error") { + Value r = Value(int32_t(5)) % Value(int32_t(0)); + CHECK(r.getType() == VT_ERROR); + } + + SECTION("unary minus on int") { + Value r = -Value(int32_t(5)); + CHECK(r.asLong() == -5); + } + + SECTION("unary minus on float") { + Value r = -Value(2.5); + CHECK(r.asReal() == Catch::Approx(-2.5)); + } + + SECTION("pow") { + Value r = Value(int32_t(2)).pow(Value(int32_t(10))); + CHECK(r.asLong() == 1024); + } + + SECTION("error propagates through arithmetic") { + Value err; + err.error("bad"); + Value r = Value(int32_t(1)) + err; + CHECK(r.getType() == VT_ERROR); + } + + SECTION("VT_EMPTY in arithmetic is an error") { + Value r = Value() + Value(int32_t(1)); + CHECK(r.getType() == VT_ERROR); + } +} + +// ----------------------------------------------------------------------------- +// Comparison operators +// ----------------------------------------------------------------------------- + +TEST_CASE("Value comparisons", "[cvalue]") { + SECTION("int equality") { + CHECK(Value(int32_t(3)) == Value(int32_t(3))); + CHECK_FALSE(Value(int32_t(3)) == Value(int32_t(4))); + CHECK(Value(int32_t(3)) != Value(int32_t(4))); + } + + SECTION("float equality uses epsilon") { + CHECK(Value(1.0) == Value(1.0)); + CHECK_FALSE(Value(1.0) == Value(2.0)); + } + + SECTION("int vs float compares numerically") { + CHECK(Value(int32_t(3)) == Value(3.0)); + } + + SECTION("string equality") { + CHECK(Value("abc") == Value("abc")); + CHECK_FALSE(Value("abc") == Value("def")); + } + + SECTION("ordering integers") { + CHECK(Value(int32_t(1)) < Value(int32_t(2))); + CHECK(Value(int32_t(2)) > Value(int32_t(1))); + CHECK(Value(int32_t(1)) <= Value(int32_t(1))); + CHECK(Value(int32_t(1)) >= Value(int32_t(1))); + } + + SECTION("ordering strings is lexicographic") { + CHECK(Value("apple") < Value("banana")); + CHECK(Value("z") > Value("a")); + } + + SECTION("VT_EMPTY comparisons are always false") { + Value empty; + CHECK_FALSE(empty == Value(int32_t(0))); + CHECK_FALSE(empty < Value(int32_t(1))); + } + + SECTION("VT_ERROR comparisons are always false") { + Value err; + err.error("e"); + CHECK_FALSE(err == Value(int32_t(0))); + } +} + +// ----------------------------------------------------------------------------- +// Logical operators +// ----------------------------------------------------------------------------- + +TEST_CASE("Value logical operators", "[cvalue]") { + SECTION("operator! on zero int is true") { + CHECK(!Value(int32_t(0))); + CHECK_FALSE(!Value(int32_t(1))); + CHECK_FALSE(!Value(int32_t(-1))); + } + + SECTION("operator! on empty string is true") { + CHECK(!Value("")); + CHECK_FALSE(!Value("x")); + } + + SECTION("operator! on VT_EMPTY is false") { + CHECK_FALSE(!Value()); + } + + SECTION("operator&& short-circuits on false first arg") { + // Catch2 can't decompose Value::operator&&/||; extract to bool first + CHECK_FALSE(bool(Value(int32_t(0)) && Value(int32_t(1)))); + CHECK(bool(Value(int32_t(1)) && Value(int32_t(1)))); + } + + SECTION("operator|| returns true if either is non-zero") { + CHECK(bool(Value(int32_t(0)) || Value(int32_t(1)))); + CHECK_FALSE(bool(Value(int32_t(0)) || Value(int32_t(0)))); + } + + SECTION("VT_EMPTY in logical ops is always false") { + CHECK_FALSE(bool(Value() && Value(int32_t(1)))); + CHECK_FALSE(bool(Value() || Value(int32_t(1)))); + } +} + +// ----------------------------------------------------------------------------- +// Vector container +// ----------------------------------------------------------------------------- + +TEST_CASE("Value VT_VECTOR operations", "[cvalue]") { + Value v(VT_VECTOR); + + SECTION("append with consecutive integer keys") { + v.setAt(Value(int32_t(0)), Value(int32_t(10))); + v.setAt(Value(int32_t(1)), Value(int32_t(20))); + v.setAt(Value(int32_t(2)), Value(int32_t(30))); + CHECK(v.size() == 3); + CHECK(v.getAt(Value(int32_t(0))).asLong() == 10); + CHECK(v.getAt(Value(int32_t(1))).asLong() == 20); + CHECK(v.getAt(Value(int32_t(2))).asLong() == 30); + } + + SECTION("replace existing element") { + v.setAt(Value(int32_t(0)), Value(int32_t(1))); + v.setAt(Value(int32_t(0)), Value(int32_t(99))); + CHECK(v.size() == 1); + CHECK(v.getAt(Value(int32_t(0))).asLong() == 99); + } + + SECTION("getNth returns the index value (not the element)") { + v.setAt(Value(int32_t(0)), Value("a")); + v.setAt(Value(int32_t(1)), Value("b")); + // getNth for VT_VECTOR returns the index as Value(int), not the element + CHECK(v.getNth(0).asLong() == 0); + CHECK(v.getNth(1).asLong() == 1); + // Out-of-range returns VT_EMPTY + CHECK(v.getNth(5).getType() == VT_EMPTY); + } + + SECTION("remove erases by index") { + v.setAt(Value(int32_t(0)), Value(int32_t(1))); + v.setAt(Value(int32_t(1)), Value(int32_t(2))); + v.remove(Value(int32_t(0))); + CHECK(v.size() == 1); + } + + SECTION("clear empties the vector") { + v.setAt(Value(int32_t(0)), Value(int32_t(1))); + v.clear(); + CHECK(v.size() == 0); + } + + SECTION("out-of-range key (non-consecutive) fails setAt") { + bool ok = v.setAt(Value(int32_t(5)), Value(int32_t(1))); // gap + CHECK_FALSE(ok); + CHECK(v.size() == 0); + } + + SECTION("getAt out-of-range returns VT_EMPTY") { + v.setAt(Value(int32_t(0)), Value(int32_t(42))); + Value& r = v.getAt(Value(int32_t(99))); + CHECK(r.getType() == VT_EMPTY); + } +} + +// ----------------------------------------------------------------------------- +// Map container +// ----------------------------------------------------------------------------- + +TEST_CASE("Value VT_MAP operations", "[cvalue]") { + Value m(VT_MAP); + + SECTION("insert and retrieve by string key") { + m.setAt(Value("name"), Value("Alice")); + m.setAt(Value("age"), Value(int32_t(30))); + CHECK(m.size() == 2); + CHECK(m.getAt(Value("name")).asString() == "Alice"); + CHECK(m.getAt(Value("age")).asLong() == 30); + } + + SECTION("setAt with VT_EMPTY value erases the key") { + m.setAt(Value("x"), Value(int32_t(1))); + m.setAt(Value("x"), Value()); // VT_EMPTY = erase + CHECK(m.size() == 0); + CHECK(m.getAt(Value("x")).getType() == VT_EMPTY); + } + + SECTION("remove erases by key") { + m.setAt(Value("a"), Value(int32_t(1))); + m.remove(Value("a")); + CHECK(m.size() == 0); + } + + SECTION("clear empties the map") { + m.setAt(Value("a"), Value(int32_t(1))); + m.setAt(Value("b"), Value(int32_t(2))); + m.clear(); + CHECK(m.size() == 0); + } + + SECTION("getNth returns the nth key") { + m.setAt(Value("alpha"), Value(int32_t(1))); + // Map is ordered: "alpha" is at index 0 + Value key = m.getNth(0); + CHECK(key.getType() == VT_STRING); + CHECK(key.asString() == "alpha"); + } + + SECTION("getNth out of range returns VT_EMPTY") { + CHECK(m.getNth(0).getType() == VT_EMPTY); + } + + SECTION("getAt missing key returns VT_EMPTY") { + CHECK(m.getAt(Value("no-such-key")).getType() == VT_EMPTY); + } + + SECTION("integer key is converted to string for map") { + m.setAt(Value(int32_t(1)), Value("one")); + CHECK(m.getAt(Value("1")).asString() == "one"); + } +} + +// ----------------------------------------------------------------------------- +// CReference +// ----------------------------------------------------------------------------- + +TEST_CASE("CReference", "[cvalue]") { + SECTION("default CReference is invalid") { + CReference ref; + // Standalone CReference has no type tag of its own; null ref reports VT_EMPTY + CHECK(ref.getType() == VT_EMPTY); + CHECK_FALSE(ref.isValid()); + } + + SECTION("CReference pointing to a value dereferences through self()") { + Value target(int32_t(42)); + CReference ref(target); + CHECK(ref.isValid()); + CHECK(ref.self().asLong() == 42); + } + + SECTION("modifying the target is visible through the reference") { + Value target(int32_t(1)); + CReference ref(target); + target = Value(int32_t(99)); + CHECK(ref.self().asLong() == 99); + } + + SECTION("set() rebinds the reference") { + Value a(int32_t(1)), b(int32_t(2)); + CReference ref(a); + ref.set(b); + CHECK(ref.self().asLong() == 2); + } +} + +// ----------------------------------------------------------------------------- +// CValArray +// ----------------------------------------------------------------------------- + +TEST_CASE("CValArray", "[cvalue]") { + CValArray arr; + + SECTION("starts empty") { + CHECK(arr.empty()); + CHECK(arr.size() == 0); + } + + SECTION("push_back appends elements accessible by index") { + arr.push_back(Value(int32_t(10))); + arr.push_back(Value(int32_t(20))); + arr.push_back(Value(int32_t(30))); + CHECK(arr.size() == 3); + CHECK(arr[0].asLong() == 10); + CHECK(arr[1].asLong() == 20); + CHECK(arr[2].asLong() == 30); + } + + SECTION("begin/end iterators work") { + arr.push_back(Value(int32_t(1))); + arr.push_back(Value(int32_t(2))); + int32_t sum = 0; + for (auto& v : arr) sum += v.asLong(); + CHECK(sum == 3); + } + + SECTION("clear empties the array") { + arr.push_back(Value(int32_t(1))); + arr.clear(); + CHECK(arr.empty()); + } + + SECTION("changed() flag tracks push_back") { + arr.changed(false); + arr.push_back(Value(int32_t(1))); + CHECK(arr.changed()); + } + + SECTION("copy-assigns correctly between CValArrays") { + CValArray other; + other.push_back(Value(int32_t(42))); + arr = other; + CHECK(arr.size() == 1); + CHECK(arr[0].asLong() == 42); + } +} + +// ----------------------------------------------------------------------------- +// swap +// ----------------------------------------------------------------------------- + +TEST_CASE("Value::swap", "[cvalue]") { + SECTION("swaps scalar values") { + Value a(int32_t(1)), b(int32_t(2)); + a.swap(b); + CHECK(a.asLong() == 2); + CHECK(b.asLong() == 1); + } + + SECTION("swaps different types") { + Value a(int32_t(42)), b("hello"); + a.swap(b); + CHECK(a.getType() == VT_STRING); + CHECK(a.asString() == "hello"); + CHECK(b.getType() == VT_INT); + CHECK(b.asLong() == 42); + } +} + +// ----------------------------------------------------------------------------- +// valueCount tracking +// ----------------------------------------------------------------------------- + +TEST_CASE("Value::valueCount", "[cvalue]") { + int32_t before = Value::valueCount(); + { + Value a(int32_t(1)); + Value b(int32_t(2)); + CHECK(Value::valueCount() == before + 2); + } + CHECK(Value::valueCount() == before); +} diff --git a/EBase/utf8.hpp b/EBase/utf8.hpp new file mode 100644 index 0000000..703ab52 --- /dev/null +++ b/EBase/utf8.hpp @@ -0,0 +1,283 @@ +//--------------------------------------------------------------------------------------- +// utf8.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2019, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#pragma once + +#include +#include + +#define GHC_INLINE inline + +namespace ghc { +namespace detail { + + GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi) + { + return (static_cast(c - lo) < (hi - lo + 1)); + } + + GHC_INLINE bool is_surrogate(uint32_t c) + { + return in_range(c, 0xd800, 0xdfff); + } + + GHC_INLINE bool is_high_surrogate(uint32_t c) + { + return (c & 0xfffffc00) == 0xd800; + } + + GHC_INLINE bool is_low_surrogate(uint32_t c) + { + return (c & 0xfffffc00) == 0xdc00; + } + + GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) + { + if (unicode <= 0x7f) { + str.push_back(static_cast(unicode)); + } + else if (unicode >= 0x80 && unicode <= 0x7ff) { + str.push_back(static_cast((unicode >> 6) + 192)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) { + str.push_back(static_cast((unicode >> 12) + 224)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if (unicode >= 0x10000 && unicode <= 0x10ffff) { + str.push_back(static_cast((unicode >> 18) + 240)); + str.push_back(static_cast(((unicode & 0x3ffff) >> 12) + 128)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(str, 0xfffd); +#endif + } + } + + // Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/) + // and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding; + // Generating debugging and shrinking my own DFA from scratch was a day of fun! + enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; + + GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) + { + static const uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, + 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, + }; + uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment); + return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); + } + + GHC_INLINE bool validUtf8(const std::string& utf8String) + { + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_RJCT) { + return false; + } + } + if (utf8_state) { + return false; + } + return true; + } + +} // namespace detail + +GHC_INLINE int characterWidth(std::uint32_t codepoint) +{ +#ifndef _WIN32 + return ::wcwidth(static_cast(codepoint)); +#else + return 1 + (codepoint >= 0x1100 && (codepoint <= 0x115f || // Hangul Jamo init. consonants + codepoint == 0x2329 || codepoint == 0x232a || (codepoint >= 0x2e80 && codepoint <= 0xa4cf && codepoint != 0x303f) || // CJK ... Yi + (codepoint >= 0xac00 && codepoint <= 0xd7a3) || // Hangul Syllables + (codepoint >= 0xf900 && codepoint <= 0xfaff) || // CJK Compatibility Ideographs + (codepoint >= 0xfe10 && codepoint <= 0xfe19) || // Vertical forms + (codepoint >= 0xfe30 && codepoint <= 0xfe6f) || // CJK Compatibility Forms + (codepoint >= 0xff00 && codepoint <= 0xff60) || // Fullwidth Forms + (codepoint >= 0xffe0 && codepoint <= 0xffe6) || (codepoint >= 0x20000 && codepoint <= 0x2fffd) || (codepoint >= 0x30000 && codepoint <= 0x3fffd))); +#endif +} + +GHC_INLINE std::uint32_t utf8Increment(std::string::const_iterator& iter, const std::string::const_iterator& end) +{ + unsigned utf8_state = detail::S_STRT; + std::uint32_t codepoint = 0; + while (iter != end) { + if ((utf8_state = detail::consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == detail::S_STRT) { + return codepoint; + } + else if (utf8_state == detail::S_RJCT) { + return 0xfffd; + } + } + return 0xfffd; +} + +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(utf8String.begin(), utf8String.end(), alloc); +} + +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = detail::S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = detail::consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == detail::S_STRT) { + if (codepoint <= 0xffff) { + result += static_cast(codepoint); + } + else { + codepoint -= 0x10000; + result += static_cast((codepoint >> 10) + 0xd800); + result += static_cast((codepoint & 0x3ff) + 0xdc00); + } + codepoint = 0; + } + else if (utf8_state == detail::S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = detail::S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = detail::S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = detail::consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == detail::S_STRT) { + result += static_cast(codepoint); + codepoint = 0; + } + else if (utf8_state == detail::S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = detail::S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template ::type size = 1> +inline std::string toUtf8(const std::basic_string& unicodeString) +{ + return std::string(unicodeString.begin(), unicodeString.end()); +} + +template ::type size = 2> +inline std::string toUtf8(const std::basic_string& unicodeString) +{ + std::string result; + for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { + char32_t c = *iter; + if (detail::is_surrogate(c)) { + ++iter; + if (iter != unicodeString.end() && detail::is_high_surrogate(c) && detail::is_low_surrogate(*iter)) { + detail::appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + detail::appendUTF8(result, 0xfffd); + if (iter == unicodeString.end()) { + break; + } +#endif + } + } + else { + detail::appendUTF8(result, c); + } + } + return result; +} + +template ::type size = 4> +inline std::string toUtf8(const std::basic_string& unicodeString) +{ + std::string result; + for (auto c : unicodeString) { + detail::appendUTF8(result, static_cast(c)); + } + return result; +} + +template +inline std::string toUtf8(const charT* unicodeString) +{ + return toUtf8(std::basic_string>(unicodeString)); +} + +} // namespace ghc + +#undef GHC_INLINE diff --git a/LICENSE b/LICENSE index 1cc56e5..ce0d252 100644 --- a/LICENSE +++ b/LICENSE @@ -1,29 +1,19 @@ -BSD 3-Clause License +Copyright (c) 1999, Steffen Schümann -Copyright (c) 2019, Steffen Schümann -All rights reserved. +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: -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0d487a1..acb8f71 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,201 @@ -# pbemtools +# Vorlage V2.0.0 rc 1 -Dieses Repository dient im Moment als [Issue-Tracker](https://github.com/gulrak/pbemtools/issues) und [Doku-Wiki](https://github.com/gulrak/pbemtools/wiki) -für meine beiden schon 20 Jahre alten Tools **"Vorlage"** und **"VPP"** für [Eressea](https://www.eressea.de) und eng verwandte Atlantis-Play-by-EMail-Nachkommen. +[![Build Status](https://github.com/gulrak/pbemtools/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/gulrak/pbemtools/actions/workflows/build.yml) +[![GitHub release](https://img.shields.io/github/v/release/gulrak/pbemtools)](https://github.com/gulrak/pbemtools/releases/latest) +[![Platforms](https://img.shields.io/badge/platforms-Windows%20%7C%20macOS%20%7C%20Linux-blue)](#) +[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) -Die Releases gibt es aktuell unter https://gulrak.de/pbemtools/#downloads zu finden, für Windows ist die erste neue -Version seit 12 Jahren mit v1.7.1 verfügbar. Wer also die Programme nutzt und Fehler kennt oder wünsche hat, kann die -hier als Issues eintüten, ich will sehen was sich im Jubiläumsjahr machen lässt. +## PBeM-Zugvorlagen-Generator +Copyright (C) 1999-2026 by Steffen Schümann -Im `config` Ordner sind erste aktualisierte Konfigurationsdateien für Eressea abgelegt, gegen die `unbekannte -Feldkennung`-Warnung für `id`, `familiarmage`und `speed`, weitere Updates folgen. +**Support:** [Discussions hier](https://github.com/gulrak/pbemtools/discussions) und #vorlage auf dem [Eressea Discord](https://discord.gg/mjjNMS9u)\ +**Web:** https://gulrak.de/pbemtools \ +**Dokumentation:** https://github.com/gulrak/pbemtools/wiki/VorlageDokuAllgemeines \ +**Alte Binaries:** https://gulrak.de/pbemtools/#downloads + +_Metabefehle inspiriert durch Georg Edelmayers PERL-Vorlagen-Generator._ + +> ## Persönliche Anmerkung +> Ich stelle dieses Projekt nach vielen Jahren unter der MIT-Lizenz als Open Source zur Verfügung, +> weil ich es selbst nicht mehr aktiv pflegen kann, es aber weiterhin von der Eressea-Community +> genutzt wird. +> +> Der Code ist das Produkt einer anderen Zeit und spiegelt weder meine heutigen Ansprüche noch +> meinen heutigen Stil als Entwickler wider. Seit ungefähr 20 Jahren bestand die Pflege im +> Wesentlichen nur noch aus kleineren Korrekturen und Maßnahmen, um das Programm lauffähig zu halten. +> +> Ich veröffentliche den Quellcode deshalb nicht als Vorzeigeprojekt, sondern damit die +> Community eine Grundlage hat, auf der sie weiterarbeiten kann. + +> ## Personal Note (English version) +> I am releasing this project as open source under the MIT License after many years because I can +> no longer actively maintain it myself, even though it is still being used by the Eressea community. +> +> This code comes from a very different time and does not reflect my current standards or my +> current style as a software developer. For roughly the last 20 years, maintenance has consisted +> mostly of small fixes and basic life support to keep the program usable. +> +> I am publishing the source code not as a showcase of good modern software engineering, but +> simply to give the community a foundation it can continue to use, maintain, and build upon. +> + +# Einleitung + +`VORLAGE` ist ein Konsolen-Programm, welches es ermöglicht, aus dem Computer-Report (CR) von Eressea, +Verdanon, Empiria o.ä. eine Zugvorlage erstellen zu lassen, ähnlich der, die man mit dem normalen +Report bekommt. + +Die Beispiele und Texte in der Doku sind eher auf Eressea Spiel bezogen. Dies soll weder eine +Wertung sein, noch andeuten, nur dieses Spiel werde unterstützt. + +Die von Vorlage erzeugte Zugvorlage kann in einigen Bereichen aufgepeppt werden, um die Erstellung +des Zuges zu vereinfachen. + +Ein Beispiel (Auszug einer imaginären Vorlage): + +``` + REGION -3,6 ; Grollbat (Berg, 175 Personen, 4132$ Silber) + ; ECheck Lohn 13 + ; H S |Bauern: 453 +28|Silber: 17278 +127|Unterhalt: 810 +7| + ; S B . |Rekruten: 22 +1|Eisen: 513 -11|Gewinn: 1812 +84| + ; E . |Pferde: 13 -3|Laen: 0 |Baeume: 0 | + ; |Balsam: 48 +0|Gewürz: 35 +0|Juwel: 0 +7| + ; |Myrrhe: 50 +0|Öl: 39 +0|Seide: 84 -6| + ; |Weihrauch: 56 -4 + ; Regionseinnahmen: 1628 Silber + ; Regionsausgaben: 1750 Silber + ; > Die Testaten (87) spendete 20 Silber an Schmarotzer (12). + ; Durchgereist: Schlampige Transporteure (p0ng) + + ; - - - - - - - - - - - - + ; In Burg 'Cammelot' (4321) [3/50]: + + EINHEIT b1ob; Die Waffenschmiede [3,0$] flieht + ; Gew: 174.0GE Gehen: -127.8GE/16.2GE + ; I Die Waffenschmiede (b1ob) in Grollbat (-3,6) produziert 4 Kriegsäxte. + ; Waffenbau 5 [180] + + ; 18 Holz, 27 Kriegsaxt + MACHE Kriegsaxt +``` +Die Gewichtsangaben bedeuten: Gesamtgewicht der Einheit, sowie freie / theoretische Kapazität beim Reiten bzw. Gehen. Ist Reiten nicht möglich (z.B. keine Pferde oder kein Talent), wird die Reitangabe weggelassen. Sind zu viele Pferde vorhanden, so wird statt dessen dieses angezeigt. + +Die eigentliche Dokumentation ist über das Wiki realisiert: + +https://github.com/gulrak/pbemtools/wiki/VorlageDokuAllgemeines + +# Compilieren von Vorlage + +Für alle Plattformen werden folgende Werkzeuge benötigt: + +- **CMake** 3.16 oder neuer +- **Git** (wird auch von CMake benötigt, um die Abhängigkeiten zu laden) +- Ein C++20-fähiger Compiler (siehe plattformspezifische Hinweise) + +Die Abhängigkeiten PCRE2, fmtlib und Catch2 werden beim ersten Build-Lauf automatisch +von CMake heruntergeladen und compiliert – es ist keine manuelle Installation erforderlich. + +## Windows + +Empfohlen wird **Visual Studio 2022** (Community-Edition reicht aus) mit der installierten +Komponente „Desktopentwicklung mit C++". Visual Studio bringt CMake bereits mit; alternativ +kann CMake von [cmake.org](https://cmake.org/download/) installiert werden. + +**Build mit Visual Studio (Developer Command Prompt oder PowerShell):** + +```bat +git clone https://github.com/gulrak/pbemtools.git +cd pbemtools +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build --config Release +``` + +Das fertige Programm liegt anschließend unter `build\Vorlage\Release\vorlage.exe`. + +> **Hinweis:** Auf Windows werden die MSVC-Laufzeitbibliotheken statisch eingebunden +> (`/MT`), sodass die erzeugte EXE-Datei ohne zusätzliche DLLs lauffähig ist. + +Alternativ lässt sich das Projekt auch mit **MSYS2/MinGW-w64** bauen – dabei gelten +dieselben Befehle wie unter Linux. + +## macOS + +Voraussetzung sind die **Xcode Command Line Tools** sowie **CMake**. Die Command Line +Tools lassen sich über das Terminal installieren: + +```bash +xcode-select --install +``` + +CMake kann entweder über [Homebrew](https://brew.sh/) oder direkt von +[cmake.org](https://cmake.org/download/) bezogen werden: + +```bash +brew install cmake # optional, falls noch nicht vorhanden +``` + +**Build:** + +```bash +git clone https://github.com/gulrak/pbemtools.git +cd pbemtools +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +Das fertige Programm liegt unter `build/Vorlage/vorlage`. + +> **Hinweis:** Als Mindestanforderung gilt macOS 10.12 (Sierra). Getestet wurde mit +> Apple Clang aus den aktuellen Xcode Command Line Tools. + +## Linux + +Benötigt wird ein C++20-fähiger Compiler (**GCC 10+** oder **Clang 10+**) sowie CMake +und Git. Unter Debian/Ubuntu lassen sich alle Voraussetzungen so installieren: + +```bash +sudo apt update +sudo apt install build-essential cmake git +``` + +Unter Fedora/RHEL: + +```bash +sudo dnf install gcc-c++ cmake git +``` + +**Build:** + +```bash +git clone https://github.com/gulrak/pbemtools.git +cd pbemtools +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` + +Das fertige Programm liegt unter `build/Vorlage/vorlage`. + +> **Hinweis:** Unter Linux werden libgcc und libstdc++ statisch eingebunden, sodass die +> erzeugte Binärdatei ohne passende Laufzeitbibliotheken auf anderen Systemen läuft. +> Bei Verwendung von Clang wird vollständig statisch gelinkt; `libc++abi-dev` muss dann +> ggf. zusätzlich installiert werden (`sudo apt install libc++abi-dev`). + + +# Lizenz + +Das Programm ist unter der MIT-Lizenz lizenziert. Siehe die Datei [LICENSE](LICENSE) für Details. + +# Danksagungen + +Ich möchte meinen Testern und Ideenlieferanten herzlich für die Mitarbeit danken. +Ohne motivierte Anwender ließe sich das Tool sicher nicht so vorantreiben. Mein Dank +geht (in alphabetischer Reihenfolge) an: + +_Andreas "Trickstar" Beer, Martin "Erchamion" Ehler, Thomas Gritzan, Günter Grossberger, +Klaus Lieberum, Michael "micham" Möller, Ralf Polster, Enno "Igrarjuk" Rehling, Jens Ricke, +Guido "Locksley" Sassmannshausen, Jens "Shannera" Schulze, "Tok", Karl Schwarz, +Matthias Strunk, "Widersach" und "Wuzel"._ + +Wenn ich genau Dich vergessen habe, so bitte ich das meinem porösen Hirn zuzuschreiben +und mir ruhig mitzuteilen. Ich habe leider nicht Buch geführt und die Namen sind aus +dem Gedächtnis zusammengetragen und werden viel zu selten ergänzt. \ No newline at end of file diff --git a/VPP/CMakeLists.txt b/VPP/CMakeLists.txt new file mode 100644 index 0000000..22b10d6 --- /dev/null +++ b/VPP/CMakeLists.txt @@ -0,0 +1,16 @@ +set(VPP_SOURCES + VPP.cpp +) + +set(VPP_HEADERS + StdAfx.h +) + +add_executable(vpp ${VPP_SOURCES} ${VPP_HEADER} ${CMAKE_BINARY_DIR}/${PROJECT_LOWERCASE_NAME}/version.h) +target_link_libraries(vpp ebase) +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(vpp PRIVATE _CRT_SECURE_NO_WARNINGS) + #target_compile_options(vpp PRIVATE "$<$:/utf-8>") + #target_compile_options(vpp PRIVATE "$<$:/utf-8>") +endif() + diff --git a/VPP/StdAfx.h b/VPP/StdAfx.h new file mode 100644 index 0000000..278d0a0 --- /dev/null +++ b/VPP/StdAfx.h @@ -0,0 +1,33 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__4B2B5686_2000_11D3_A27E_00E0290BEBE3__INCLUDED_) +#define AFX_STDAFX_H__4B2B5686_2000_11D3_A27E_00E0290BEBE3__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +// TODO: reference additional headers your program requires here + +//#pragma warning(disable:4786) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__4B2B5686_2000_11D3_A27E_00E0290BEBE3__INCLUDED_) diff --git a/VPP/VPP.cpp b/VPP/VPP.cpp new file mode 100644 index 0000000..9abb5b7 --- /dev/null +++ b/VPP/VPP.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** + * $Source: D:\\Development\\Repository/ETools/Vorlage/VPP.cpp,v $ + * $Author: ssh $ + * $Date: 2003/07/01 09:39:30 $ + * $Revision: 1.1 $ + * $State: Exp $ + * Copyright: (c) Copyright 2000 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Algemeine Utility-Funktionen + ***************************************************************************** + * + * $Log: VPP.cpp,v $ + * Revision 1.1 2003/07/01 09:39:30 ssh + * *** empty log message *** + * + * Revision 1.1 2003/07/01 09:19:28 ssh + * Initial recvsing of Source... + * + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include + +size_t g_nLineSize = 100; + +extern std::string GetConfigFileName(); +extern bool DoUserFunction(const std::string& sName, ArgumentList& coArgs, Value* poVal); + +#define VERSIONINFO "VPP " PBEMTOOLS_VERSION_STRING_LONG + +std::string GetConfigFileName() +{ + return ""; +} + +bool DoUserFunction(const std::string& sName, ArgumentList& coArgs, Value* poVal) +{ + return false; +} + +int main(int argc, char* argv[]) +{ + std::fstream oIS; + std::string sLine; + std::string sFileName; + int32_t nLineNumber = 0; + int32_t nInSize = 0; + int32_t nOutSize = 0; + FILE* hOut = stdout; + bool bFile = false; + bool bSpaceBreak = false; + size_t p; + int i = 1; + +#ifdef _WIN32 + int NLSIZE = 2; +#else + int NLSIZE = 1; +#endif + + setlocale(LC_CTYPE, "German"); + + if (i >= argc) { + fprintf(stderr, "\nVorlage-Post-Prozessor\n%s (Build %d) [%s]\n(C) Copyright 2000-2026 by Steffen Schuemann\n", VERSIONINFO, PBEMTOOLS_BUILD_NUMBER_EMU, __DATE__); + fprintf(stderr, "\nAufruf:\n VPP [Optionen] [> ]\n\n"); + fprintf(stderr, " -o f Die Ausgabe der Zugdatei erfolgt nicht auf stdout, sondern\n"); + fprintf(stderr, " in die Datei mit dem Namen f\n"); + fprintf(stderr, " -w l Nachrichten und Info-Zeilen auf l Zeichen umbrechen\n"); + fprintf(stderr, " -s Nur an Spaces umbrechen und diese loeschen (z.B. fuer Sitanleta)\n"); + exit(10); + } + + while (i < argc) { + if (i < argc && !std::strcmp(argv[i], "-w")) { + if (++i < argc) + g_nLineSize = (size_t)atoi(argv[i]); + else { + fprintf(stderr, "FEHLER: Keine Zeilenlaenge fuer die Option '-w'!\n"); + exit(10); + } + } + else if (i < argc && !strcmp(argv[i], "-o")) { + if (++i < argc) { + hOut = fopen(argv[i], "w+"); + if (!hOut) { + fprintf(stderr, "FEHLER: Konnte Datei '%s' nicht zur Ausgabe oeffnen!\n", argv[i]); + } + else + bFile = true; + } + else { + fprintf(stderr, "FEHLER: Kein Dateiname fuer die Ausgabe mit Option '-o'!\n"); + exit(10); + } + } + else if (!strcmp(argv[i], "-s")) { + bSpaceBreak = true; + } + else if (argv[i][0] != '-') { + if (!sFileName.empty()) { + fprintf(stderr, "FEHLER: Mehr als eine Zugdatei angegeben!"); + exit(10); + } + sFileName = argv[i]; + } + i++; + } + + oIS.open(sFileName.c_str(), std::ios::in); + + if (oIS.fail()) { + fprintf(stderr, "Auf die Vorlage-Datei '%s' kann nicht zugegriffen werden!", sFileName.c_str()); + exit(10); + } + + while (1) { + sLine.clear(); + + do { + if (!sLine.empty() && sLine[sLine.size() - 1] == '\\') { + sLine.erase(sLine.size() - 1, 1); + } + std::string sLinePart; + std::getline(oIS, sLinePart); + if (oIS.fail()) + break; + p = sLinePart.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sLinePart.size(); + } + sLinePart.erase(0, p); + sLine += sLinePart; + nInSize += (int)sLine.length() + NLSIZE; + while (!sLine.empty() && sLine[sLine.size() - 1] < 32) + sLine.erase(sLine.size() - 1, 1); + nLineNumber++; + } while (!sLine.empty() && sLine[sLine.size() - 1] == '\\'); + + if (oIS.fail()) + break; + + if (!sLine.empty()) { + if (sLine[0] == ';') { + p = sLine.find_first_not_of(" \t", 1); + if (p != std::string::npos && IsEqual(sLine.substr(p, 6), "ECHECK")) { + if (sLine.length() > 78) + fprintf(stderr, "Zeile %ld moeglicherweise zu lang:\n%s\n", nLineNumber, sLine.c_str()); + fprintf(hOut, "%s\n", sLine.c_str()); + nOutSize += (int)sLine.length() + NLSIZE; + } + else if (p != std::string::npos && IsEqual(sLine.substr(p, 7), "VERSION")) { + if (sLine.length() > 78) + fprintf(stderr, "Zeile %ld moeglicherweise zu lang:\n%s\n", nLineNumber, sLine.c_str()); + fprintf(hOut, "%s\n", sLine.c_str()); + nOutSize += (int)sLine.length() + NLSIZE; + } + } + else { + if (IsEqual(sLine.substr(0, 7), "EINHEIT")) { + std::string::size_type q, r; + p = sLine.find_last_of(']'); + if (p != std::string::npos) { + sLine.erase(p + 1); + q = sLine.rfind(std::string(",b")); + if (q == std::string::npos) + q = sLine.rfind(std::string(",B")); + r = sLine.find_last_of('['); + if (r != std::string::npos && q != std::string::npos && q > r) + sLine.erase(q, p - q); + } + } + else if (IsEqual(sLine.substr(0, 6), "REGION")) { + p = sLine.find_last_of('('); + if (p != std::string::npos) + sLine.erase(p); + } + + { + std::string sTemp; + CRegExp oRE; + p = 0; + oRE.Prepare("([^ \\t\\n\\r\\f'\"]|'([^']|\\\\')+'|\"([^\"]|\\\\\")+\")+"); + while (oRE.Find(sLine, (int)p)) { + if (p) { + // putchar( ' ' ); + sTemp += ' '; + } + // printf( "%s", sLine.substr( oRE.Begin(), oRE.Size() ).c_str() ); + sTemp += sLine.substr((size_t)oRE.Begin(), (size_t)oRE.Size()); + p = (size_t)oRE.End() + 1; + } + // puts(""); + sLine = sTemp; + } + // int p = sLine.find_last_of( "~ " ); + // if( sLine[0]!='/' && ( sLine[sLine.size()-1]=='"' || + // ( p != std::string::npos && sLine[p]=='~' ) ) ) + { + while (sLine.length() > g_nLineSize) { + if (bSpaceBreak) { + size_t j; + for (j = g_nLineSize - 1; j > 0 && sLine[j] != ' '; j--) + ; + if (j > 0) { + fprintf(hOut, "%s\\\n", sLine.substr(0, j).c_str()); + sLine.erase(0, j); + nOutSize += i + 1 + NLSIZE; + } + else { + break; + } + } + else { + if (sLine[g_nLineSize - 2] == ' ') { + fprintf(hOut, "%s\\\n", sLine.substr(0, g_nLineSize - 1).c_str()); + sLine.erase(0, g_nLineSize - 1); + nOutSize += (int)g_nLineSize + NLSIZE; + } + else { + fprintf(hOut, "%s\\\n", sLine.substr(0, g_nLineSize - 2).c_str()); + sLine.erase(0, g_nLineSize - 2); + nOutSize += (int)g_nLineSize - 1 + NLSIZE; + } + } + } + } + if (sLine.length() > g_nLineSize) { + fprintf(stderr, "Zeile %ld moeglicherweise zu lang:\n%s\n", nLineNumber, sLine.c_str()); + } + fprintf(hOut, "%s\n", sLine.c_str()); + nOutSize += (int)sLine.length() + NLSIZE; + } + } + } + + if (bFile) + fclose(hOut); + + fprintf(stderr, "\nEs konnten %.1f%% der Zeichen entfernt werden.\n", 100.0 - ((float)nOutSize * 100) / nInSize); + // char Buff[128]; + // gets( Buff ); + + return 0; +} diff --git a/Vorlage/CMakeLists.txt b/Vorlage/CMakeLists.txt new file mode 100644 index 0000000..518efc1 --- /dev/null +++ b/Vorlage/CMakeLists.txt @@ -0,0 +1,22 @@ +SET(VORLAGE_SOURCES + #crashdumphandler.cpp + CRNE.cpp + Metascript.cpp + Zugvorlage.cpp +) + +SET(VORLAGE_HEADERS + #crashdumphandler.h + CRNE.h + Metascript.h + Zugvorlage.h +) + +add_executable(vorlage ${VORLAGE_SOURCES} ${VORLAGE_HEADERS} ${CMAKE_BINARY_DIR}/${PROJECT_LOWERCASE_NAME}/version.h) +target_link_libraries(vorlage ebase) +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(vorlage PRIVATE _CRT_SECURE_NO_WARNINGS) + #target_compile_options(vorlage PRIVATE "$<$:/utf-8>") + #target_compile_options(vorlage PRIVATE "$<$:/utf-8>") +endif() + diff --git a/Vorlage/CRNE.cpp b/Vorlage/CRNE.cpp new file mode 100644 index 0000000..2ce96fa --- /dev/null +++ b/Vorlage/CRNE.cpp @@ -0,0 +1,295 @@ +//--------------------------------------------------------------------------------------- +// CRNE.cpp +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable : 4786) +#endif +#include +#include +#include + +#include +#include "CRNE.h" + +int32_t CRNENode::m_nRefCnt = 0; +CRNENode::RNEPOOL CRNENode::m_cpoRNEPool; + +CRNENode::~CRNENode() +{ + m_nRefCnt--; + if (!m_nRefCnt) { + for (RNEPOOL::iterator i = m_cpoRNEPool.begin(); i != m_cpoRNEPool.end(); i++) { + delete (*i).second; + } + } +} + +CRNENode* CRNENode::CreateFromString(const std::string& sExp) +{ + std::string::const_iterator it = sExp.begin(); + return CreateNewRNENode(it, sExp.end()); +} + +void CRNENode::AddToPool(const std::string& sName, const std::string& sRNE) +{ + RNEPOOL::iterator i = m_cpoRNEPool.find(sName); + CRNENode* poNode; + std::string::const_iterator it = sRNE.begin(); + poNode = CreateNewRNENode(it, sRNE.end()); + if (i != m_cpoRNEPool.end()) { + delete (*i).second; + m_cpoRNEPool.erase(i); + } + m_cpoRNEPool.insert(RNEPOOL::value_type(sName, poNode)); +} + +void CRNENode::DumpRNEPool() +{ + for (RNEPOOL::iterator i = m_cpoRNEPool.begin(); i != m_cpoRNEPool.end(); i++) { + printf("$%s = %s\n", (*i).first.c_str(), (*i).second->String().c_str()); + } +} + +CRNENode* CreateNewRNENode(std::string::const_iterator& it, const std::string::const_iterator& iend) +{ + CRNENode* poNode = NULL; + while (it != iend && IsSpace(*it)) + it++; + if (it != iend) { + switch (*it) { + case '(': + poNode = new CRNENode_Combination(it, iend); + break; + case '[': + poNode = new CRNENode_Selection(it, iend); + break; + case '$': + poNode = new CRNENode_Reference(it, iend); + break; + default: + if (IsAlpha(*it) || *it == '#') { + poNode = new CRNENode_Literal(it, iend); + } + else + throw CRNEException("Weder Namenszeichen, noch '#' gefunden!"); + } + } + return poNode; +} + +void CRNENode::ReadRules(const std::string& sFileName) +{ + std::fstream oIS; + std::string sLine; + size_t p; + int l = 0; + + srand((uint32_t)time(0)); + + oIS.open(sFileName.c_str(), std::ios::in); + + if (oIS.fail()) { + ERRMSG(0, ("FEHLER: Konnte Regeldatei '%s' fuer Namensgenerator nicht oeffnen!\n", sFileName.c_str())); + return; + } + + while (true) { + std::getline(oIS, sLine); + if (oIS.fail()) + break; + l++; + while (!sLine.empty() && sLine[sLine.size() - 1] < 32) + sLine.erase(sLine.size() - 1, 1); + while (!sLine.empty() && sLine[0] <= 32) + sLine.erase(0, 1); + if (!sLine.empty() && sLine[0] != ';') { + std::string sName; + if (sLine[0] != '$') { + ERRMSG(0, ("%s(%d) : FEHLER: Erwarte '$' zu Beginn eines Regelnamens!\n", sFileName.c_str(), l)); + return; + } + p = sLine.find('='); + if (p == std::string::npos) { + ERRMSG(0, ("%s(%d) : FEHLER: Erwarte '=' in Namensregel!\n", sFileName.c_str(), l)); + return; + } + sName = sLine.substr(1, p - 1); + while (!sName.empty() && sName[sName.size() - 1] <= 32) + sName.erase(sName.size() - 1, 1); + try { + CRNENode::AddToPool(sName, sLine.substr(p + 1)); + } + catch (CRNEException e) { + ERRMSG(0, ("%s(%d) : FEHLER: Fehlerhafter Namensausdruck (%s)!\n", sFileName.c_str(), l, e.why().c_str())); + } + } + } +} + +void CRNENode::ClearRules() +{ + for (RNEPOOL::iterator i = m_cpoRNEPool.begin(); i != m_cpoRNEPool.end(); i++) { + delete (*i).second; + } +} + +#if 0 + +int main( int argc, char* argv[] ) +{ + std::fstream oIS; + std::string sLine; + CRNENode* poRNE; + size_t p; + int i, l = 0; + +// CRNENode::AddToPool( std::string("MDVorname"), std::string("[Anton|Bert|Casper|Dieter|Emil|Friedrich|Gerd|Heiko|Ingo|Jochen|Klaus|Lars|Martin|Norbert|Oskar|Paul|Rolf|Stephan|Torsten|Uwe]") ); +// CRNENode::AddToPool( std::string("MDwarfName"), std::string("([B|G|K|R|T|S][a|e|i|o|u][b|l|m|n|v][grat|galk|gerk|pak|perk|polk|rak|rek|ralk|tark|tolk|terk])") ); +// CRNENode::AddToPool( std::string("MDwarfClan"), std::string("[Bargh|Groth|Kreth|Tarsh|Lorkh|Prath|Ralsh]") ); + + if( argc<3 || argc>4 ) + { + FILE* io; + + if( argc <= 1 ) + io = stdout; + else + io = stderr; + + fprintf( io, "\nxnamer V1.0 a rule based name generator\n" ); + fprintf( io, "(c) Copyright 1999 by Steffen Schuemann, Hamburg, Germany\n" ); + fprintf( io, "\nUSAGE: xNamer []\n" ); + fprintf( io, "\n A textfile containing emty lines, comment\n" ); + fprintf( io, " lines (starting with ';') or rule defines:\n" ); + fprintf( io, " A rule, describing what to produce, the\n" ); + fprintf( io, " simplest rule ist $name, using rule 'name'\n" ); + fprintf( io, " from the used rule fine.\n" ); + fprintf( io, " How much names schould be createt (one is\n" ); + fprintf( io, " the default)\n" ); + fprintf( io, "\nThe rules:\n" ); + fprintf( io, "( ...) comines the output of the rules\n" ); + fprintf( io, "[||...] selects randomly one of the rules\n" ); + fprintf( io, "[||...]n selects n times\n" ); + fprintf( io, "[||...]n:m selects n to m times randomly\n" ); + fprintf( io, "$name use output of rule named 'name'\n" ); + fprintf( io, "any letters insert ths letters (#=space)\n" ); + fprintf( io, "\nExample rule file example.txt:\n" ); + fprintf( io, "\n; This are some forenames\n" ); + fprintf( io, "$fore = [Carl|Pete|Hank]\n" ); + fprintf( io, "\n; This are some surnames\n" ); + fprintf( io, "$sur = [Higgins|Johnson|Smith]\n" ); + fprintf( io, "\n; This Rule defines a name\n" ); + fprintf( io, "$name = ($fore # $sur)\n" ); + fprintf( io, "\nExample usage:\n" ); + fprintf( io, " xnamer example.txt \x22$name\x22 5\n" ); + fprintf( io, "\nResults in something like:\n" ); + fprintf( io, " Carl Johnson\n" ); + fprintf( io, " Hank Higgins\n" ); + fprintf( io, " Hank Johnson\n" ); + fprintf( io, " Pete Smith\n" ); + fprintf( io, " Carl Higgins\n" ); + exit( 0 ); + } + + srand( time(0) ); + + std::string sFileName = argv[1]; + std::string sExpression = argv[2]; + int32_t nCount = (argc==4?atoi( argv[3] ):1); + + oIS.open( sFileName.c_str(), std::ios::in ); + + if( oIS.fail() ) + { + fprintf( stderr, "Error: Could not open rules file!" ); + exit( 1 ); + } + + while( true ) + { + std::getline( oIS, sLine ); + if( oIS.fail() ) + break; + l++; + while( !sLine.empty() && sLine[sLine.size()-1]<32 ) + sLine.erase( sLine.size()-1, 1 ); + while( !sLine.empty() && sLine[0]<=32 ) + sLine.erase( 0, 1 ); + if( !sLine.empty() && sLine[0]!=';' ) + { + std::string sName; + if( sLine[0]!='$' ) + { + fprintf( stderr, "Error line %d: Expected '$' at begin of rulename!\n", l ); + exit( 1 ); + } + p = sLine.find( '=' ); + if( p==std::string::npos ) + { + fprintf( stderr, "Error line %d: Epected '=' in rule!\n", l ); + exit( 1 ); + } + sName = sLine.substr( 1, p-1 ); + while( !sName.empty() && sName[sName.size()-1]<=32 ) + sName.erase( sName.size()-1, 1 ); + try + { + CRNENode::AddToPool( sName, sLine.substr( p+1 ) ); + } + catch(CRNEException e) + { + fprintf( stderr, "Line %d: Error in expression!\n", l ); + poRNE = 0; + } + } + } + + if( !strcmp( argv[2], "?" ) ) + { + CRNENode::DumpRNEPool(); + } + else + { + try + { + poRNE = CRNENode::CreateFromString( sExpression ); + } + catch(CRNEException e) + { + puts("Error in expression!"); + poRNE = 0; + } + if( poRNE ) + for( i=0 ; iGenAVal().c_str() ); + } + + return 0; +} + +#endif diff --git a/Vorlage/CRNE.h b/Vorlage/CRNE.h new file mode 100644 index 0000000..98d041f --- /dev/null +++ b/Vorlage/CRNE.h @@ -0,0 +1,251 @@ +//--------------------------------------------------------------------------------------- +// CRNE.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#pragma once + +#include +#include +#include +#include +#include + +class CRNEException +{ +public: + CRNEException(const char* txt) + : _text(txt) + { + } + + CRNEException(const std::string& txt) + : _text(txt) + { + } + + std::string why() const { return _text; } + +private: + std::string _text; +}; + +class CRNENode +{ +public: + CRNENode() { m_nRefCnt++; } + + virtual ~CRNENode(); + virtual std::string GenAVal() = 0; + virtual std::string String() = 0; + static CRNENode* CreateFromString(const std::string& sExp); + static void AddToPool(const std::string& sName, const std::string& sRNE); + static void DumpRNEPool(); + static void ReadRules(const std::string& sFileName); + static void ClearRules(); + +protected: + typedef std::map RNEPOOL; + static int32_t m_nRefCnt; + static RNEPOOL m_cpoRNEPool; +}; + +CRNENode* CreateNewRNENode(std::string::const_iterator& it, const std::string::const_iterator& iend); + +class CRNENode_Literal : public CRNENode +{ +public: + CRNENode_Literal(std::string::const_iterator& it, const std::string::const_iterator& iend) + { + while (it != iend && (IsNameChar(*it) || *it == '#')) { + if (*it == '#') { + m_sLiteral += ' '; + it++; + } + else + m_sLiteral += *it++; + } + } + + virtual std::string GenAVal() { return m_sLiteral; } + + virtual std::string String() + { + std::string sVal; + for (size_t i = 0; i < m_sLiteral.size(); i++) { + if (m_sLiteral[i] == ' ') + sVal += '#'; + else + sVal += m_sLiteral[i]; + } + return sVal; + } + +private: + std::string m_sLiteral; +}; + +class CRNENode_Reference : public CRNENode +{ +public: + CRNENode_Reference(std::string::const_iterator& it, const std::string::const_iterator& iend) + { + it++; + while (it != iend && IsAlNum(*it)) + m_sName += *it++; + } + + virtual std::string GenAVal() + { + RNEPOOL::iterator i = m_cpoRNEPool.find(m_sName); + if (i != m_cpoRNEPool.end()) { + return (*i).second->GenAVal(); + } + else { + return std::string("-"); + } + } + + virtual std::string String() { return std::string("$") + m_sName; } + +private: + std::string m_sName; +}; + +class CRNENode_Selection : public CRNENode +{ + typedef std::vector ALTERNATIVES; + +public: + CRNENode_Selection(std::string::const_iterator& it, const std::string::const_iterator& iend) + : m_nMin(1) + , m_nMax(1) + { + CRNENode* poNode; + int v1, v2; + if (it == iend || *it++ != '[') + throw CRNEException("Erwartetes '[' nicht gefunden!"); + while (it != iend && *it != ']') { + poNode = CreateNewRNENode(it, iend); + m_cpoAlternatives.push_back(poNode); + if (*it != ']' && *it != '|') + throw CRNEException("Erwartete '|' oder ']' aber fand beides nicht!"); + if (*it == '|') + it++; + } + it++; + if (it != iend && IsDigit(*it)) { + v1 = 0; + v2 = 0; + while (it != iend && IsDigit(*it)) + v1 = v1 * 10 + (*it++) - '0'; + if (*it == ':') { + it++; + while (it != iend && IsDigit(*it)) + v2 = v2 * 10 + (*it++) - '0'; + m_nMin = v1; + m_nMax = v2; + } + else { + m_nMin = 0; + m_nMax = v1; + } + } + } + + virtual ~CRNENode_Selection() + { + for (size_t i = 0; i < m_cpoAlternatives.size(); i++) { + delete m_cpoAlternatives[i]; + } + } + + virtual std::string GenAVal() + { + std::string sVal; + int n = (rand() % (m_nMax - m_nMin + 1)) + m_nMin; + for (int i = 0; i < n; i++) + sVal += m_cpoAlternatives[size_t(rand() % (int)m_cpoAlternatives.size())]->GenAVal(); + return sVal; + } + + virtual std::string String() + { + std::string sVal("["); + for (ALTERNATIVES::iterator i = m_cpoAlternatives.begin(); i < m_cpoAlternatives.end(); i++) { + sVal += (*i)->String() + "|"; + } + sVal[sVal.size() - 1] = ']'; + return sVal; + } + +private: + ALTERNATIVES m_cpoAlternatives; + int m_nMin, m_nMax; +}; + +class CRNENode_Combination : public CRNENode +{ + typedef std::vector PARTS; + +public: + CRNENode_Combination(std::string::const_iterator& it, const std::string::const_iterator& iend) + { + CRNENode* poNode; + if (it == iend || *it++ != '(') + throw CRNEException("Erwartetes '(' nicht gefunden!"); + while (it != iend && *it != ')') { + poNode = CreateNewRNENode(it, iend); + m_cpoParts.push_back(poNode); + } + it++; + } + + virtual ~CRNENode_Combination() + { + for (size_t i = 0; i < m_cpoParts.size(); i++) { + delete m_cpoParts[i]; + } + } + + virtual std::string GenAVal() + { + std::string sVal; + for (size_t i = 0; i < m_cpoParts.size(); i++) + sVal += m_cpoParts[i]->GenAVal(); + return sVal; + } + + virtual std::string String() + { + std::string sVal("("); + for (PARTS::iterator i = m_cpoParts.begin(); i < m_cpoParts.end(); i++) { + sVal += (*i)->String(); + } + sVal += ')'; + return sVal; + } + +private: + PARTS m_cpoParts; +}; diff --git a/Vorlage/Makefile b/Vorlage/Makefile new file mode 100644 index 0000000..ee152e9 --- /dev/null +++ b/Vorlage/Makefile @@ -0,0 +1,94 @@ +OBJ = Zugvorlage.o StdAfx.o Metascript.o CRNE.o crashdumphandler.o ../EBase/Expression.o \ + ../EBase/regexp.o ../EBase/Report.o ../EBase/ReportStream.o ../EBase/hierarchy.o \ + ../EBase/StdAfx.o ../EBase/Utility.o ../EBase/Value.o ../EBase/Hash.o \ + ../EBase/charencoding.o + +OS = $(shell uname -s) +CPU = $(shell uname -m) +# CC = @echo 'ERROR: Unsupported plattform: ' $(OS); exit 10 ; + +ifeq ($(OS),Linux) + +BIN = vorlage +CPP = g++-3.3 +# CC = @echo 'ERROR: Missing or unsupported MODE: ' $(MODE); exit 10 ; + +ifeq ($(MODE),debug) + CC = $(CPP) -I/home/gulrak/development/boost_1_32_0 + CFLAGS = -g -static + CPPFLAGS = + LFLAGS = -static +else +ifeq ($(MODE),release) + CC = $(CPP) -I/home/gulrak/development/boost_1_32_0 -O2 + CFLAGS = -static + CPPFLAGS = + LFLAGS = -static -Xlinker -M +else +ifeq ($(MODE),prerelease) + CC = $(CPP) -I/home/gulrak/development/boost_1_32_0 -O2 + CFLAGS = -g -static + CPPFLAGS = + LFLAGS = -static +else + $(error unsupported mode (use MODE=debug or MODE=release)) +endif +endif +endif + +else + +BIN = vorlage.exe +CPP = D:/Development/MinGW/bin/g++ +# CC = @echo 'ERROR: Missing or unsupported MODE: ' $(MODE); exit 10 ; + +ifeq ($(MODE),debug) + CC = $(CPP) -ID:/Development/boost_1_29_0 + CFLAGS = -g -static -DSTATIC + CPPFLAGS = + LFLAGS = -static +else +ifeq ($(MODE),release) + CC = $(CPP) -ID:/Development/boost_1_29_0 -Os + CFLAGS = -static -DSTATIC + CPPFLAGS = + LFLAGS = -static +else +ifeq ($(MODE),prerelease) + CC = $(CPP) -ID:/Development/boost_1_29_0 -Os + CFLAGS = -g -static -DSTATIC + CPPFLAGS = + LFLAGS = -static +else + $(error unsupported mode (use MODE=debug or MODE=release)) +endif +endif +endif + +# $(error unsupported plattform: $(OS)) + +endif + + +%.o : %.cpp + $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ + +$(BIN) : $(OBJ) + $(CC) -o $(BIN) $(LFLAGS) $(OBJ) /usr/lib/libpcre.a + +include depends.mak + +.PHONY : prepare +prepare : + -d:/Development/MingW/bin/strip --strip-unneeded $(BIN) + -upx --best $(BIN) + +.PHONY : clean +clean : + -rm -f $(BIN) $(OBJ) + +depend : + $(CC) -M $(patsubst %.o,%.cpp,$(OBJ)) >depends.mak + +vpp : + $(CC) -o vpp $(CFLAGS) $(CPPFLAGS) VPP.cpp diff --git a/Vorlage/Metascript.cpp b/Vorlage/Metascript.cpp new file mode 100644 index 0000000..8a1bdd2 --- /dev/null +++ b/Vorlage/Metascript.cpp @@ -0,0 +1,5496 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/Vorlage/Metascript.cpp,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:56:47 $ + * $Revision: 1.11 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Klassen fuer die Metabefehlsauswertung + ***************************************************************************** + * + * $Log: Metascript.cpp,v $ + * Revision 1.11 2000/02/24 09:56:47 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.10 1999/11/28 17:38:41 S.Schuemann + * - Mannigfaltige Änderungen für Vorlage V1.4 beta 9 + * + * Revision 1.9 1999/11/17 08:59:12 S.Schuemann + * - support für multiple CRs + * + * - vielfache Anderungen für Vorlage 1.4 beta 8 + * + * Revision 1.8 1999/11/08 11:14:46 S.Schuemann + * - Attribute region.gewinn, region[x,y], unit.bewache, + * region.pool.ding + * - Ecaping in Strings + * - Vars in Attributzugriffen + * - Funktionen + * - Sortierung nach BESCHREIBE PRIVAT + * - Liste fremder Einheiten (normal/verbose) + * - Bugfix: Leerzeile nach Kapitänsinfo entfernt + * + * Revision 1.7 1999/11/03 10:21:56 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.6 1999/10/28 12:39:58 S.Schuemann + * - Änderungen für den Linux-Port + * + * Revision 1.5 1999/10/26 13:27:02 S.Schuemann + * - Anpassungen fuer Vorlage 1.4 b 5 + * + * - Neue Objekt-Attribute + * + * - User-Variable und #while + * + * Revision 1.4 1999/10/24 08:02:18 S.Schuemann + * - Anpassungen fuer 1.4 b 4 + * - CReference als Value eingefuehrt + * - Unterprogramme mit #proc und #call implementiert + * + * Revision 1.3 1999/10/18 21:32:20 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.2 1999/09/27 10:27:10 S.Schuemann + * - Scripthandling fuer das neue Objekt REPORT implementiert + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#include "Metascript.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRNE.h" + +using namespace std; + +extern std::string GetConfigFileName(); +extern FILE* g_hErr; +extern int32_t g_nTimeCorrection; + +struct StackInfo +{ + CMetaCommand* m_pMC; + CMCI* m_pMCI; +}; + +std::vector g_coCallStack; +std::vector g_coBreakConditions; +int g_nBreakCondition = 0; +bool g_bNoMoreBreaks = false; +int32_t g_nLimitRuntime = 0; +time_t g_nStartTime = 0; + +Value DoUnit(CObjectPart* poPart); +Value DoPartei(CObjectPart* poPart); +Value DoBuilding(CObjectPart* poPart); +Value DoShip(CObjectPart* poPart); +Value DoRegion(CObjectPart* poPart); +Value DoGrenze(CObjectPart* poPart); +Value DoReport(CObjectPart* poPart); +Value DoThings(CObjectPart* poPart); +Value DoRaces(CObjectPart* poPart); + +// Internals +Value _DoRegion(CObjectPart* poPart, CRegion* pReg, CRegion* pRegQ); +Value _DoBuilding(CObjectPart* poPart, CRegion* pReg); +Value _DoShip(CObjectPart* poPart, CRegion* pReg); +Value _DoGrenze(CObjectPart* poPart, CRegion* pReg); +Value _DoUnit(CObjectPart* poPart, CEinheit* poUnit, CEinheit* poUnitQ); + +static std::map g_pseudoFiles; +CScriptBase g_oScriptBase; + +int32_t CMetaCommand::g_nTrace = 0; +int32_t CMetaCommand::g_nTraceSteps = -1; +std::string CMetaCommand::g_sErrMsg; +CMCI* g_poStepOver = nullptr; +CMCI* g_poStepOut = nullptr; + +std::map g_cnMetaTokens; +#define GMT(x) {"#" #x, GMT_##x} + +enum METATOKENID { + GMT_after, + GMT_array, + GMT_assert, + GMT_break, + GMT_call, + GMT_config, + GMT_continue, + GMT_debug, + GMT_default, + GMT_dict, + GMT_error, + GMT_every, + GMT_forever, + GMT_if, + GMT_ifregion, + GMT_ifunit, + GMT_input, + GMT_message, + GMT_next, + GMT_notrace, + GMT_return, + GMT_sort, + GMT_table, + GMT_tag, + GMT_trace, + GMT_var, + GMT_warning, + GMT_while +}; + +static struct +{ + const char* name; + int value; +} MetaTokens[] = {GMT(after), GMT(array), GMT(assert), GMT(break), GMT(call), GMT(config), GMT(continue), GMT(debug), GMT(default), GMT(dict), GMT(error), GMT(every), GMT(forever), GMT(if), GMT(ifregion), + GMT(ifunit), GMT(input), GMT(message), GMT(next), GMT(notrace), GMT(return), GMT(sort), GMT(table), GMT(tag), GMT(trace), GMT(var), GMT(warning), GMT(while), {0, 0}}; + +class CBreakException +{ +public: + CBreakException() {} + + ~CBreakException() {} +}; + +class CContinueException +{ +public: + CContinueException() {} + + ~CContinueException() {} +}; + +class CReturnException +{ +public: + CReturnException(const Value& oVal) + : m_oVal(oVal) + { + } + + ~CReturnException() {} + + Value m_oVal; +}; + +//------------------------------------------------------------------------ +// THINGS[].NAME +// .GEWICHT +// .KAPAZITAET +// .PLURAL +//------------------------------------------------------------------------ +Value DoThings(CObjectPart* poPart) +{ + CObjectPart* pOP; + Value oVal; + if (poPart->index.size() != 1) { + oVal.error("Falsche Indizierung fuer Objekt THINGS"); + return oVal; + } + pOP = poPart->next; + if (!pOP) { + oVal.error("Objekt ohne Attribut benutzt"); + return oVal; + } + if (pOP->next) { + oVal.error("Subattribute werden fuer THINGS. nicht unterstuetzt"); + return oVal; + } + return CGegenstandsInfo::Lookup(poPart->index[0].asString()).GetValue(pOP->label); +} + +//------------------------------------------------------------------------ +// RACES[]. +//------------------------------------------------------------------------ +Value DoRaces(CObjectPart* poPart) +{ + CObjectPart* pOP; + Value oVal; + if (poPart->index.size() != 1) { + oVal.error("Falsche Indizierung fuer Objekt RACES"); + return oVal; + } + pOP = poPart->next; + if (!pOP) { + oVal.error("Objekt ohne Attribut benutzt"); + return oVal; + } + if (pOP->next) { + oVal.error("Subattribute werden fuer RACES. nicht unterstuetzt"); + return oVal; + } + return CRasse::Lookup(poPart->index[0].asString()).GetValue(pOP->label); +} + +//------------------------------------------------------------------------ +// DB.REGION.SIZE +// .REGION[]. +// .UNIT.SIZE +// .UNIT[]. +//------------------------------------------------------------------------ +Value DoDB(CObjectPart* poPart) +{ + CObjectPart* pOP; + Value oVal; + + pOP = poPart->next; + + if (!pOP) { + oVal.error("Objekt ohne Attribut benutzt"); + return oVal; + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "REGION") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + oVal = Value((int32_t)g_coRDB.size()); + } + else if (IsEqual(pOP->label.c_str(), "REGION")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt 'db.region' unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].getType() != VT_INT || pOP->index[0].asLong() >= (int)g_coRDB.size() || pOP->index[0].asLong() < 0) { + oVal.error("Index von Objekt 'db.region[]' korrupt"); + return oVal; + } + if (g_nRDBIndex < 0 || !pOP->index[0].asLong()) { + g_iRDB = g_coRDB.begin(); + g_nRDBIndex = 0; + } + int32_t nPos = pOP->index[0].asLong(); + if (nPos > g_nRDBIndex) { + while (g_nRDBIndex < nPos) { + g_iRDB++; + g_nRDBIndex++; + } + } + else { + while (g_nRDBIndex > nPos) { + g_iRDB--; + g_nRDBIndex--; + } + } + CRegion* pReg = *((*g_iRDB).second.begin()); + oVal = _DoRegion(pOP, pReg, pReg); + } + else if (pOP->next && pOP->index.empty() && (IsEqual(pOP->label.c_str(), "UNIT") || IsEqual(pOP->label.c_str(), "EINHEIT")) && IsEqual(pOP->next->label.c_str(), "SIZE")) { + oVal = Value((int32_t)g_coEDB.size()); + } + else if (IsEqual(pOP->label.c_str(), "UNIT") || IsEqual(pOP->label.c_str(), "EINHEIT")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt 'db.unit' unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].getType() != VT_INT || pOP->index[0].asLong() >= (int)g_coEDB.size() || pOP->index[0].asLong() < 0) { + oVal.error("Index von Objekt 'db.unit[]' korrupt"); + return oVal; + } + CObjectPart oPart; + oPart.label = "UNIT"; + if (g_nEDBIndex < 0 || !pOP->index[0].asLong()) { + g_iEDB = g_coEDB.begin(); + g_nEDBIndex = 0; + } + int32_t nPos = pOP->index[0].asLong(); + if (nPos > g_nEDBIndex) { + while (g_nEDBIndex < nPos) { + g_iEDB++; + g_nEDBIndex++; + } + } + else { + while (g_nEDBIndex > nPos) { + g_iEDB--; + g_nEDBIndex--; + } + } + CEinheit* pUnit = (*g_iEDB).second; + oVal = _DoUnit(pOP, pUnit, pUnit); + } + else { + oVal.error("Unbekanntes Attribut des Objekts 'db'"); + } + return oVal; +} + +//------------------------------------------------------------------------ +// GRUPPE.NUMMER +// .ALLIANZ[] +// . +//------------------------------------------------------------------------ +static Value _DoGruppe(CObjectPart* poPart, CGruppe::Ptr pG) +{ + CObjectPart* pOP = poPart; + + if (IsEqual(pOP->label, "nummer")) { + return Value(itoan(pG->GetKey(0).asLong(), g_poCurrentReport->PNrBase())); + } + if (IsEqual(pOP->label, "allianz") && pOP->index.size() == 1) { + int32_t nStat = ((CGruppe*)pG.get())->GetAllianz((int32_t)strtol(pOP->index[0].asString().c_str(), 0, g_poCurrentReport->PNrBase())); + return Value(nStat); + } + if (pOP->next) + return ((CBlockBase*)pG.get())->GetValue(pOP); + else + return pG->GetValue(pOP->label); +} + +//------------------------------------------------------------------------ +// PARTEI.NUMMER +// .ALLIANZ[] +// . +//------------------------------------------------------------------------ +static Value _DoPartei(CObjectPart* poPart, CPartei::Ptr pP) +{ + CObjectPart* pOP = poPart; + + if (IsEqual(pOP->label, "nummer")) { + return Value(itoan(pP->GetKey(0).asLong(), g_poCurrentReport->PNrBase())); + } + if (IsEqual(pOP->label, "allianz") && pOP->index.size() == 1) { + int32_t nStat = ((CPartei*)pP.get())->GetAllianz((int32_t)strtol(pOP->index[0].asString().c_str(), 0, g_poCurrentReport->PNrBase())); + return Value(nStat); + } + if (pOP->next) + return ((CBlockBase*)pP.get())->GetValue(pOP); + else + return pP->GetValue(pOP->label); +} + +//------------------------------------------------------------------------ +// REPORT[].UNIT[]. +// .REGION[,]. +// .REGION[,,]. +// REPORT.REGION.SIZE +// .REGION[]. +// .MESSAGE.SIZE +// .MESSAGE[]. (RENDERED auch, wenn nicht im CR) +// .RUNDE +// .OPTIONEN.SIZE +// .OPTIONEN[].NAME +// .OPTIONEN[].AKTIV +// .PARTEI +// .PARTEI.SIZE +// .PARTEI[]. +// .GRUPPE.SIZE +// .GRUPPE[]. +// .REKRUTIERUNGSKOSTEN +// .PERSONEN +// .SPIEL +// . +//------------------------------------------------------------------------ +Value DoReport(CObjectPart* poPart) +{ + CObjectPart* pOP; + CRegion* pReg; + CRegion* pRegQ; + CEinheit* poUnit; + CEinheit* poUnitQ; + Value oVal; + + if (poPart->index.size() > 1) { + oVal.error("Objekt REPORT unterstuetzt nur einen Index"); + return oVal; + } + + if (poPart->index.size() == 1) { + int32_t nDRunde = poPart->index[0].asLong(); + int32_t nRunde = g_poCurrentReport->Runde() + nDRunde; + pOP = poPart->next; + if (!pOP) { + return g_coRRegionDB[-nDRunde].empty() ? Value(0) : Value(1); + } + else if (IsEqual(pOP->label.c_str(), "REGION")) { + if (pOP->next && pOP->index.empty() && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)g_coRRegionDB[-nDRunde].size()); + } + if (pOP->index.size() == 1) { + if (pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int32_t)g_coRRegionDB[-nDRunde].size()) { + oVal.error("Index in REPORT[dr].REGION[idx] korrupt"); + return oVal; + } + int32_t i = pOP->index[0].asLong(); + RegionDB::iterator rdbi; + for (rdbi = g_coRRegionDB[-nDRunde].begin(); i && rdbi != g_coRRegionDB[-nDRunde].end(); ++rdbi, ++i) { + } + if (rdbi != g_coRRegionDB[-nDRunde].end()) { + pReg = *((*rdbi).second.begin()); + pRegQ = *((*rdbi).second.begin()); + } + else { + pReg = 0; + pRegQ = 0; + } + return _DoRegion(pOP, pReg, pRegQ); + } + if (pOP->index.size() < 2 || pOP->index.size() > 3) { + oVal.error("Objekt REPORT[dr].REGION[x,y[,z]] braucht zwei oder drei Indizes"); + return oVal; + } + RegionDB::iterator rdbi; + if (pOP->index.size() == 2) + rdbi = g_coRRegionDB[-nDRunde].find(CRegion::CalcKey(pOP->index[0].asLong(), pOP->index[1].asLong(), 0)); + else + rdbi = g_coRRegionDB[-nDRunde].find(CRegion::CalcKey(pOP->index[0].asLong(), pOP->index[1].asLong(), pOP->index[2].asLong())); + if (rdbi != g_coRRegionDB[-nDRunde].end()) { + pReg = *((*rdbi).second.begin()); + pRegQ = *((*rdbi).second.begin()); + } + else { + pReg = 0; + pRegQ = 0; + } + return _DoRegion(pOP, pReg, pRegQ); + } + else if (IsEqual(pOP->label.c_str(), "UNIT") || IsEqual(pOP->label.c_str(), "EINHEIT")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt REPORT[dr].UNIT[enr] braucht einen Index"); + return oVal; + } + EinheitenDB::iterator edbi; + edbi = g_coREinheitenDB[-nDRunde].find(EinheitenNummer(pOP->index[0].asString())); + if (edbi != g_coREinheitenDB[-nDRunde].end()) { + poUnit = (*edbi).second; + poUnitQ = (*edbi).second; + } + else { + poUnit = 0; + poUnitQ = 0; + } + return _DoUnit(pOP, poUnit, poUnitQ); + } + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "MESSAGE") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)CMessage::Messages(nRunde)); + } + else if (IsEqual(pOP->label.c_str(), "MESSAGE")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt REPORT.MESSAGE unterstuetzt nur einen Index"); + return oVal; + } + if (g_poCurrentReport->NumMessage()) { + CMessage* pMsg = CMessage::FindMessage(nRunde, pOP->index[0].asLong()); + if (!pOP->next) { + return pMsg ? Value(1) : Value(0); + } + if (!pMsg) { + oVal.error("Index von REPORT.MESSAGE[] korrupt"); + return oVal; + } + if (IsEqual(pOP->next->label.c_str(), "rendered")) { + return Value(pMsg->Render(g_poCurrentReport)); + } + else if (IsEqual(pOP->next->label.c_str(), "section")) { + return (*(g_poCurrentReport->MessageSections()))[pMsg->GetValue("type", Value(0)).asLong()]; + } + else { + return pMsg->GetValue(pMsg, pOP->next, Value(0)); + } + } + } + /* + else if( IsEqual( pOP->label.c_str(), "MESSAGE" ) ) + { + if( g_poCurrentReport->NumMessage() ) + return Value( (int32_t)g_poCurrentReport->NumMessage() ); + else + return Value( (int32_t)g_poCurrentReport->NumNachrichten() ); + } + */ + else { + oVal.error("Unbekanntes Subattribut oder Subobjekt fuer REPORT[dr]"); + } + + return oVal; + } + + if (!g_poCurrentReport) { + oVal.error("Objekt REPORT ausserhalb des gueltigen Kontextes benutzt"); + return oVal; + } + + pOP = poPart->next; + + if (!pOP) { + oVal.error("Objekt ohne Attribut benutzt"); + return oVal; + } + + /* + if( !IsKeyword( pOP->label ) ) + { + std::string sErr = std::string("Unbekanntes Attribut '") + pOP->label + std::string("' benutzt"); + oVal.error( sErr.c_str() ); + return oVal; + } + */ + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "REGION") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + oVal = Value((int32_t)g_poCurrentReport->GetMap()->Regions().size()); + } + else if (IsEqual(pOP->label.c_str(), "REGION")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt REPORT.REGION unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].getType() != VT_INT || pOP->index[0].asLong() >= (int)g_poCurrentReport->GetMap()->Regions().size() || pOP->index[0].asLong() < 0) { + oVal.error("Index von Objekt REPORT.REGION[] korrupt"); + return oVal; + } + pReg = g_poCurrentReport->GetMap()->VRegions()[(size_t)pOP->index[0].asLong()]; + oVal = _DoRegion(pOP, pReg, pReg); + /* + CObjectPart oPart; + oPart.label = "REGION"; + CKarte::RegionMap::iterator rmi = g_poCurrentReport->GetMap()->Regions().begin(); + int cnt = 0; + int idx = pOP->index[0].asLong(); + while( rmi != g_poCurrentReport->GetMap()->Regions().end() ) + { + if( cnt == idx ) + break; + rmi++; cnt++; + } + pReg = (*rmi).second; + oPart.index.push_back( Value( pReg->GetEX() ) ); + oPart.index.push_back( Value( pReg->GetEY() ) ); + oPart.index.push_back( Value( pReg->GetEZ() ) ); + oPart.next = pOP->next; + int i = oPart.index.size(); + oVal = DoRegion( &oPart ); + oPart.next = 0; + */ + } + //***PARTEI***************************************** + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "PARTEI") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)g_poCurrentReport->GetParteiNum()); + } + else if (pOP->next && pOP->index.size() && IsEqual(pOP->label.c_str(), "PARTEI")) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int32_t)g_poCurrentReport->GetParteiNum()) { + oVal.error("Index von REPORT.PARTEI[] korrupt"); + return oVal; + } + CPartei::Ptr pP(g_poCurrentReport->GetNthParteiInfo(pOP->index[0].asLong())); + if (pP.get()) { + return _DoPartei(pOP->next, pP); + } + } + //***GRUPPE***************************************** + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "GRUPPE") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)CReport::GetGruppenNum()); + } + else if (pOP->next && pOP->index.size() && IsEqual(pOP->label.c_str(), "GRUPPE")) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int32_t)CReport::GetGruppenNum()) { + oVal.error("Index von REPORT.GRUPPE[] korrupt"); + return oVal; + } + CGruppe::Ptr pG(CReport::GetNthGruppe(pOP->index[0].asLong())); + if (pG.get()) { + return _DoGruppe(pOP->next, pG); + } + } + //***OPTIONEN***************************************** + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "OPTIONEN") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)g_poCurrentReport->Options().size()); + } + else if (pOP->next && pOP->index.size() && IsEqual(pOP->label.c_str(), "OPTIONEN")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt REPORT.OPTIONEN unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)g_poCurrentReport->Options().size()) { + oVal.error("Index von REPORT.OPTIONEN[] korrupt"); + return oVal; + } + if (IsEqual(pOP->next->label, "NAME")) { + std::string sOpt = g_poCurrentReport->Options()[(size_t)pOP->index[0].asLong()]; + return Value(sOpt.substr(1)); + } + if (IsEqual(pOP->next->label, "AKTIV")) { + std::string sOpt = g_poCurrentReport->Options()[(size_t)pOP->index[0].asLong()]; + return Value(sOpt[0] == '+' ? 1 : 0); + } + oVal.error("Unbekanntes Subattribut von REPORT.OPTIONEN[]"); + return oVal; + } + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "MESSAGE") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + if (g_poCurrentReport->NumMessage()) + return Value((int32_t)g_poCurrentReport->NumMessage()); + else + return Value((int32_t)g_poCurrentReport->NumNachrichten()); + } + else if (IsEqual(pOP->label.c_str(), "MESSAGE")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt REPORT.MESSAGE unterstuetzt nur einen Index"); + return oVal; + } + if (g_poCurrentReport->NumMessage()) { + CMessage::Ptr pMsg = g_poCurrentReport->GetMessage((size_t)pOP->index[0].asLong()); + if (!pOP->next) { + return pMsg.get() ? Value(1) : Value(0); + } + if (!pMsg.get()) { + oVal.error("Index von REPORT.MESSAGE[] korrupt"); + return oVal; + } + if (IsEqual(pOP->next->label.c_str(), "rendered")) { + return Value(((CMessage*)(pMsg.get()))->Render(g_poCurrentReport)); + } + else if (IsEqual(pOP->next->label.c_str(), "section")) { + return (*(g_poCurrentReport->MessageSections()))[pMsg->GetValue("type", Value(0)).asLong()]; + } + else { + return pMsg->GetValue(pMsg, pOP->next, Value(0)); + } + } + else { + std::string sMsg = g_poCurrentReport->GetNachricht(pOP->index[0].asLong()); + if (!pOP->next) { + return sMsg.empty() ? Value(0) : Value(1); + } + if (sMsg.empty()) { + oVal.error("Index von REPORT.MESSAGE[] korrupt"); + return oVal; + } + if (IsEqual(pOP->next->label.c_str(), "rendered")) { + return Value(sMsg); + } + else { + return Value(0); + } + } + } + else if (pOP->next || pOP->index.size()) { + oVal = ((CBlockBase*)g_poCurrentReport)->GetValue(pOP); + // std::string sErr = std::string("Attribut '") + pOP->label + std::string("' unterstuetzt weder Index noch Subattribute"); + // oVal.error( sErr.c_str() ); + } + else { + oVal = g_poCurrentReport->GetValue(pOP->label); + } + return oVal; +} + +//------------------------------------------------------------------------ +// PARTEI[]. +//------------------------------------------------------------------------ +Value DoPartei(CObjectPart* poPart) +{ + CObjectPart* pOP; + CPartei::Ptr pP; + + Value oVal; + if (poPart->index.size() != 1 || poPart->index.empty() || poPart->index[0].asLong() < 0) { + oVal.error("Falsche Indizierung fuer Objekt PARTEI"); + return oVal; + } + + pP = CReport::GetGlobalParteiInfo((int32_t)strtol(poPart->index[0].asString().c_str(), 0, g_poCurrentReport->PNrBase())); + pOP = poPart->next; + + if (!pOP) { + return pP.get() ? Value(1) : Value(0); + } + + if (!pP.get()) { + oVal.error("Falsche Indizierung fuer Objekt PARTEI"); + return oVal; + } + + if (!pOP) { + oVal.error("Objekt ohne Attribut benutzt"); + return oVal; + } + /* + if( pOP->next ) + { + oVal.error( "Subattribute werden fuer PARTEI[]. nicht unterstuetzt" ); + return oVal; + } + */ + return _DoPartei(pOP, pP); +} + +//------------------------------------------------------------------------ +// REGION. +// REGION[,]. +// REGION[,,] +// .AUSGABEN +// .BAEUME +// .BAUERN +// .BAUWERKE +// .BESCHR +// .BUILDING.SIZE +// .BUILDING[]. +// .CHAR +// .DURCHREISE.SIZE +// .DURCHREISE[] +// .DURCHSCHIFFUNGEN.SIZE +// .DURCHSCHIFFUNGEN[] +// .EFFECTS.SIZE +// .EFFECTS[] +// .EINHEITEN +// .EINNAHMEN +// .EISEN +// .GEWINN +// .GRENZE.SIZE +// .GRENZE[]. +// .HERB (In CRs aus anderen Tools) +// .INSEL +// .LAEN +// .LAND +// .LOHN +// .MALLORN (Nur das Mallorn-Flag, Zahl in BAEUME) +// .NAME +// .PERSONEN +// .PFERDE +// .POOL.SILBER +// .POOL. +// .PREISE.SIZE +// .PREISE[].SILBER +// .PREISE[].NAME +// .PREISE. +// .REKRUTEN +// .RESOURCE.SIZE +// .RESOURCE[]. +// .RESOURCE[]. +// .RUNDE +// .SCHIFFE +// .SHIP.SIZE +// .SHIP. +// .SILBER +// .SILBERPOOL +// .STRASSE (f�r Spiele ohne GRENZE-Bl�cke) +// .TERRAIN +// .UNIT.SIZE +// .UNIT[]. +// .UNTERHALT +// .VERORKT +// .X +// .Y +// .Z +// . +//------------------------------------------------------------------------ +Value _DoRegion(CObjectPart* poPart, CRegion* pReg, CRegion* pRegQ) +{ + static CRegion::Materialpool coMPool; + static CRegion* poLastReg = 0; + static int32_t nLastPartei = 0; + Value oVal; + CObjectPart* pOP; + + pOP = poPart->next; + + if (!pOP) { + return pReg ? Value(1) : Value(0); + } + + if (!pReg) { + oVal.error("Objekt REGION ausserhalb des gueltigen Kontextes benutzt"); + return oVal; + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "GRENZE") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)pReg->GetVGrenzen()->size()); + } + if (IsEqual(pOP->label.c_str(), "GRENZE")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt REGION.GRENZE unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].getType() != VT_INT || pOP->index[0].asLong() >= (int)pReg->GetVGrenzen()->size() || pOP->index[0].asLong() < 0) { + oVal.error("Index von Objekt REGION.GRENZE[] korrupt"); + return oVal; + } + CObjectPart oPart; + oPart.label = "GRENZE"; + oPart.index.push_back(Value(pOP->index[0].asLong())); + oPart.index.push_back(Value(0)); + oPart.next = pOP->next; + // int i = oPart.index.size(); + oVal = _DoGrenze(&oPart, pReg); + oPart.next = 0; + return oVal; + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "DURCHREISE") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)pRegQ->GetDurchreisen().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "DURCHREISE") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)pRegQ->GetDurchreisen().size()) { + oVal.error("Falsche Indizierung in 'region.durchreise[idx]'"); + return oVal; + } + return Value(pRegQ->GetDurchreisen()[(size_t)pOP->index[0].asLong()]); + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "DURCHSCHIFFUNG") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)pRegQ->GetDurchschiffungen().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "DURCHSCHIFFUNG") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)pRegQ->GetDurchschiffungen().size()) { + oVal.error("Falsche Indizierung in 'region.durchschiffung[idx]'"); + return oVal; + } + return Value(pRegQ->GetDurchschiffungen()[(size_t)pOP->index[0].asLong()]); + } + + if (pOP->next && pOP->index.empty() && (IsEqual(pOP->label.c_str(), "REGIONSBOTSCHAFTEN") || IsEqual(pOP->label.c_str(), "REGIONSEREIGNISSE")) && IsEqual(pOP->next->label.c_str(), "size")) { + return Value(pRegQ->CBlockBase::GetValue(pOP)); + } + else if (!pOP->next && (IsEqual(pOP->label.c_str(), "REGIONSBOTSCHAFTEN") || IsEqual(pOP->label.c_str(), "REGIONSEREIGNISSE")) && pOP->index.size()) { + int numIdx = 0; + bool foundClass = false; + std::shared_ptr pBlock = pRegQ->CBlockBase::GetBlock(pOP->label, foundClass); + if (pBlock) + numIdx = (int)pBlock->NumValues(); + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= numIdx) { + oVal.error((std::string("Falsche Indizierung in 'region.") + pOP->label + "[idx]'").c_str()); + return oVal; + } + return Value(pRegQ->GetValue(std::string("@") + pOP->index[0].asString())); + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "EFFECTS") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)pRegQ->GetEffects().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "EFFECTS") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)pRegQ->GetEffects().size()) { + oVal.error("Falsche Indizierung in 'region.effects[idx]'"); + return oVal; + } + return Value(pRegQ->GetEffects()[(size_t)pOP->index[0].asLong()]); + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "RESOURCE") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)pRegQ->GetResourcen().size()); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "RESOURCE") && pOP->index.size()) { + if (pOP->index.size() == 1 && pOP->index[0].getType() == VT_STRING) { + CResource* pRes = pRegQ->GetResource(pOP->index[0].asString()); + if (pRes) + return Value(pRes->GetValue(pOP->next->label)); + return Value(0); + } + else if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)pRegQ->GetResourcen().size()) { + oVal.error("Falsche Indizierung in 'region.resource[idx]'"); + return oVal; + } + return Value(pRegQ->GetResourcen()[(size_t)pOP->index[0].asLong()]->GetValue(pOP->next->label)); + } + + if (IsEqual(pOP->label.c_str(), "Einheiten")) + return Value((int32_t)pReg->GetVEinheiten().size()); + + if (IsEqual(pOP->label.c_str(), "unit") || IsEqual(pOP->label.c_str(), "EINHEIT")) { + if (pOP->next && pOP->index.empty() && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)pReg->GetVEinheiten().size()); + } + if (pOP->index.size() != 1) { + oVal.error("Objekt REGION.UNIT unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].getType() != VT_INT || pOP->index[0].asLong() >= (int)pReg->GetVEinheiten().size() || pOP->index[0].asLong() < 0) { + oVal.error("Index von Objekt REGION.UNIT[] korrupt"); + return oVal; + } + CObjectPart oPart; + oPart.label = "UNIT"; + // if( IsFlag( VF_BASE36 ) ) + oPart.index.push_back(Value(itoan(pReg->GetVEinheiten()[(size_t)pOP->index[0].asLong()]->Nummer(), g_poCurrentReport->ENrBase()))); + // else + // oPart.index.push_back( Value( pReg->GetVEinheiten()[pOP->index[0].asLong()]->Nummer() ) ); + oPart.next = pOP->next; + // int i = oPart.index.size(); + oVal = DoUnit(&oPart); + oPart.next = 0; + return oVal; + } + + if (IsEqual(pOP->label.c_str(), "building") || IsEqual(pOP->label.c_str(), "burg")) { + if (pOP->next && pOP->index.empty() && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value(pReg->GetVBauwerke() ? (int32_t)pReg->GetVBauwerke()->size() : 0); + } + if (pOP->index.size() != 1) { + oVal.error("Objekt REGION.BUILDING unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].getType() != VT_INT || !pReg->GetVBauwerke() || pOP->index[0].asLong() >= (int)pReg->GetVBauwerke()->size() || pOP->index[0].asLong() < 0) { + oVal.error("Index von Objekt REGION.BUILDING[] korrupt"); + return oVal; + } + CObjectPart oPart; + oPart.label = "BUILDING"; + oPart.index.push_back(Value(itoan((*(pReg->GetVBauwerke()))[(size_t)pOP->index[0].asLong()] -> Nummer(), g_poCurrentReport -> BNrBase()))); + oPart.next = pOP->next; + // int i = oPart.index.size(); + oVal = _DoBuilding(&oPart, pReg); + oPart.next = 0; + return oVal; + /* + if( pOP->index.size()!=1 ) + { + oVal.error( "Objekt REGION.BUILDING unterstuetzt nur einen Index" ); + return oVal; + } + if( pOP->index[0].getType()!=VT_INT || + !pReg->GetBuilding( pOP->index[0].asLong() ) ) + { + oVal.error( "Index von Objekt REGION.BUILDING[] korrupt" ); + return oVal; + } + CObjectPart* pNOP = pOP->next; + CBauwerk* pB = pReg->GetBuilding( pOP->index[0].asLong() ); + + if( pNOP && IsEqual( pNOP->label.c_str(), "Typ" ) ) + { + return Value( pB->Typ() ); + } + else if( pNOP && IsEqual( pNOP->label.c_str(), "Groesse" ) ) + { + return Value( pB->Groesse() ); + } + + oVal.error( "Unbekanntes Attribut des Objektes BUILDING verwendet" ); + return oVal; + */ + } + + if (IsEqual(pOP->label.c_str(), "ship") || IsEqual(pOP->label.c_str(), "schiff")) { + if (pOP->next && pOP->index.empty() && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value(pReg->GetVSchiffe() ? (int32_t)pReg->GetVSchiffe()->size() : 0); + } + if (pOP->index.size() != 1) { + oVal.error("Objekt REGION.SHIP unterstuetzt nur einen Index"); + return oVal; + } + if (pOP->index[0].getType() != VT_INT || !pReg->GetVSchiffe() || pOP->index[0].asLong() >= (int)pReg->GetVSchiffe()->size() || pOP->index[0].asLong() < 0) { + oVal.error("Index von Objekt REGION.SHIP[] korrupt"); + return oVal; + } + CObjectPart oPart; + oPart.label = "SHIP"; + oPart.index.push_back(Value(itoan((*(pReg->GetVSchiffe()))[(size_t)pOP->index[0].asLong()] -> Nummer(), g_poCurrentReport -> BNrBase()))); + oPart.next = pOP->next; + // int i = oPart.index.size(); + oVal = _DoShip(&oPart, pReg); + oPart.next = 0; + return oVal; + /* + + + if( pOP->index.size()!=1 ) + { + oVal.error( "Objekt REGION.SHIP unterstuetzt nur einen Index" ); + return oVal; + } + if( pOP->index[0].getType()!=VT_INT || + !pReg->GetShip( pOP->index[0].asLong() ) ) + { + oVal.error( "Index von Objekt REGION.SHIP[] korrupt" ); + return oVal; + } + CObjectPart* pNOP = pOP->next; + CSchiff* pS = pReg->GetShip( pOP->index[0].asLong() ); + + if( pNOP && IsEqual( pNOP->label.c_str(), "Typ" ) ) + { + return Value( pS->Typ() ); + } + else if( pNOP && IsEqual( pNOP->label.c_str(), "MaxLadung" ) ) + { + return Value( pS->MaxLadung() ); + } + + oVal.error( "Unbekanntes Attribut des Objektes SHIP verwendet" ); + return oVal; + */ + } + + if (IsEqual(pOP->label.c_str(), "Pool")) { + CRegion::Materialpool::iterator mi; + CObjectPart* pPOP; + int32_t nP = g_poCurrentReport->Partei(); + pPOP = pOP->next; + if (!pPOP) { + std::string sErr = std::string("Attribut POOL ohne Subattribute benutzt"); + oVal.error(sErr.c_str()); + return oVal; + } + + if (IsEqual(pPOP->label.c_str(), "Silber")) { + return Value(pReg->SilverOf(g_poCurrentReport->Partei())); + } + if (nP != nLastPartei || pReg != poLastReg) { + coMPool.clear(); + pReg->AddMaterialpool(nP, coMPool); + nLastPartei = nP; + poLastReg = pReg; + } + + if (!pPOP->next && IsEqual(pPOP->label.c_str(), "Size")) { + return Value((int32_t)coMPool.size()); + } + + if (pOP->next && pOP->index.size() == 1) { + int32_t nIdx = pOP->index[0].asLong(); + mi = coMPool.begin(); + while (mi != coMPool.end() && nIdx) { + mi++; + nIdx--; + } + if (mi == coMPool.end()) { + oVal.error("Index auf REGION.POOL[] korrupt"); + return oVal; + } + if (IsEqual(pPOP->label, "name")) { + return Value((*mi).first); + } + if (IsEqual(pPOP->label, "anzahl")) { + return Value((int32_t)((*mi).second)); + } + oVal.error("Unbekanntes Subattribut von REGION.POOL[]"); + return oVal; + } + + mi = coMPool.find(DeUmlaut(pPOP->label)); + if (mi != coMPool.end()) + oVal = Value((int32_t)((*mi).second)); + else { + oVal = Value(0); + for (mi = coMPool.begin(); mi != coMPool.end(); mi++) { + if (Flatten((*mi).first) == Flatten(pPOP->label)) { + oVal = Value((int32_t)((*mi).second)); + break; + } + } + } + + return oVal; + } + + if (IsEqual(pOP->label.c_str(), "PREISE")) { + if (pOP->next && pOP->index.empty() && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)pReg->GetLuxusgueter().size()); + } + + if (pOP->index.size() == 1 && pOP->next) { + if (pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)pReg->GetLuxusgueter().size()) { + oVal.error("Index von Objekt REGION.PREISE[] korrupt"); + return oVal; + } + if (IsEqual(pOP->next->label, "Silber")) { + return Value((int32_t)pReg->GetLuxusgueter()[(size_t)pOP->index[0].asLong()].second); + } + else if (IsEqual(pOP->next->label, "Name")) { + return Value(pReg->GetLuxusgueter()[(size_t)pOP->index[0].asLong()].first); + } + oVal.error("Unbekanntes Subattribut fuer REGION.PREISE[]"); + return oVal; + } + + CObjectPart* pPOP; + pPOP = pOP->next; + if (!pPOP) { + std::string sErr = std::string("Attribut PREISE ohne Subattribute benutzt"); + oVal.error(sErr.c_str()); + return oVal; + } + + for (size_t li = 0; li < pReg->GetLuxusgueter().size(); li++) { + if (IsEqual(pReg->GetLuxusgueter()[li].first, pPOP->label.c_str())) + return Value((int32_t)pReg->GetLuxusgueter()[li].second); + } + + return Value(0); + } + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "MESSAGE") && IsEqual(pOP->next->label.c_str(), "SIZE")) { + return Value((int32_t)pReg->NumMessage()); + } + else if (IsEqual(pOP->label.c_str(), "MESSAGE")) { + if (pOP->index.size() != 1) { + oVal.error("Objekt REGION.MESSAGE unterstuetzt nur einen Index"); + return oVal; + } + if (pReg->NumMessage()) { + CMessage::Ptr pMsg = pReg->GetMessage(pOP->index[0].asLong()); + if (!pOP->next) { + return pMsg.get() ? Value(1) : Value(0); + } + if (!pMsg.get()) { + oVal.error("Index von REGION.MESSAGE[] korrupt"); + return oVal; + } + if (IsEqual(pOP->next->label.c_str(), "rendered")) { + return Value(((CMessage*)(pMsg.get()))->Render(g_poCurrentReport)); + } + else if (IsEqual(pOP->next->label.c_str(), "section")) { + return (*(g_poCurrentReport->MessageSections()))[pMsg->GetValue("type", Value(0)).asLong()]; + } + else { + return pMsg->GetValue(pMsg, pOP->next, Value(0)); + } + } + } + + if (pOP->next || pOP->index.size()) { + oVal = ((CBlockBase*)pRegQ)->GetValue(pOP); + // std::string sErr = std::string("Attribut '") + pOP->label + std::string("' unterstuetzt weder Index noch Subattribute"); + // oVal.error( sErr.c_str() ); + return oVal; + } + + if (IsEqual(pOP->label.c_str(), "Gewinn")) + oVal = Value(pRegQ->CalcProfit()); + else if (IsEqual(pOP->label.c_str(), "Land")) + oVal = Value(pRegQ->IsLand() ? 1 : 0); + else if (IsEqual(pOP->label.c_str(), "Runde")) + oVal = Value((int32_t)(pRegQ->Runde())); + else if (IsEqual(pOP->label.c_str(), "Name")) + oVal = Value(pRegQ->GetName()); + else if (IsEqual(pOP->label.c_str(), "Beschr")) + oVal = Value(pRegQ->Beschr()); + else if (IsEqual(pOP->label.c_str(), "Terrain")) + oVal = Value(pRegQ->GetRegionTypeName()); + else if (IsEqual(pOP->label.c_str(), "Personen")) + oVal = Value(pReg->PersonsOf(g_poCurrentReport->Partei())); + else if (IsEqual(pOP->label.c_str(), "Silberpool")) + oVal = Value(pReg->SilverOf(g_poCurrentReport->Partei())); + else if (IsEqual(pOP->label.c_str(), "Bauwerke")) + oVal = Value((int32_t)pReg->NumBuildings()); + else if (IsEqual(pOP->label.c_str(), "Schiffe")) + oVal = Value((int32_t)pReg->NumShips()); + else { + if (pOP->next) + oVal = ((CBlockBase*)pRegQ)->GetValue(pOP); + else + oVal = pRegQ->DeepGetValue(pOP->label); + /* + oVal = pRegQ->GetValue( pOP->label ); + if( oVal.getType() == VT_EMPTY ) + { + RegionDB::iterator rdbi; + rdbi = g_coRDB.find( pRegQ->GetKey() ); + if( rdbi != g_coRDB.end() ) + { + RegionSet::iterator rsi = (*rdbi).second.begin(); + while( rsi != (*rdbi).second.end() ) + { + oVal = (*rsi)->GetValue( pOP->label ); + if( oVal.getType() != VT_EMPTY ) + break; + rsi++; + } + if( oVal.getType() == VT_EMPTY ) + { + oVal = Value( 0 ); + } + } + } + */ + } + return oVal; +} + +Value DoRegion(CObjectPart* poPart) +{ + CRegion* pReg = g_poCurrentRegion; + CRegion* pRegQ = pReg; + Value oVal; + + if (poPart->index.size() == 1 || poPart->index.size() > 3) { + oVal.error("Falsche Indizierung fuer Objekt REGION"); + return oVal; + } + if (poPart->index.size() == 2) { + RegionDB::iterator rdbi; + + if (!g_poKarte) { + oVal.error("Objekt REGION[x,y] ausserhalb des gueltigen Kontextes benutzt"); + return oVal; + } + + pReg = g_poKarte->GetFromECords(poPart->index[0].asLong(), poPart->index[1].asLong(), 0); + if (pReg->GetEX() != poPart->index[0].asLong() || pReg->GetEY() != poPart->index[1].asLong() || pReg->GetEZ() != 0) { + rdbi = g_coRDB.find(CRegion::CalcKey(poPart->index[0].asLong(), poPart->index[1].asLong(), 0)); + if (rdbi != g_coRDB.end()) { + pReg = *((*rdbi).second.begin()); + pRegQ = *((*rdbi).second.begin()); + } + else { + pReg = 0; + pRegQ = 0; + } + } + else { + rdbi = g_coRDB.find(pReg->GetKey()); + if (rdbi != g_coRDB.end()) + pRegQ = *((*rdbi).second.begin()); + } + } + if (poPart->index.size() == 3) { + RegionDB::iterator rdbi; + + if (!g_poKarte) { + oVal.error("Objekt REGION[x,y,z] ausserhalb des gueltigen Kontextes benutzt"); + return oVal; + } + + pReg = g_poKarte->GetFromECords(poPart->index[0].asLong(), poPart->index[1].asLong(), poPart->index[2].asLong()); + if (pReg->GetEX() != poPart->index[0].asLong() || pReg->GetEY() != poPart->index[1].asLong() || pReg->GetEZ() != poPart->index[2].asLong()) { + rdbi = g_coRDB.find(CRegion::CalcKey(poPart->index[0].asLong(), poPart->index[1].asLong(), poPart->index[2].asLong())); + if (rdbi != g_coRDB.end()) { + pReg = *((*rdbi).second.begin()); + pRegQ = *((*rdbi).second.begin()); + } + else { + pReg = 0; + pRegQ = 0; + } + } + else { + rdbi = g_coRDB.find(pReg->GetKey()); + if (rdbi != g_coRDB.end()) + pRegQ = *((*rdbi).second.begin()); + } + } + + if (pReg && pRegQ == pReg) { + RegionDB::iterator rdbi; + rdbi = g_coRDB.find(pReg->GetKey()); + if (rdbi != g_coRDB.end()) { + pRegQ = *((*rdbi).second.begin()); + } + } + + return _DoRegion(poPart, pReg, pRegQ); + + // pReg = g_poKarte->GetFromECords( coArgs[1].asLong(), coArgs[2].asLong() ); + // if( pReg ) + // { + // oVal = Value( pReg->GetValue( coArgs[3].asString() ) ); + // } + // return oVal; +} + +//------------------------------------------------------------------------ +// GRENZE.RICHTUNG +// .TYP +// .PROZENT +// .EFFECTS.SIZE +// .EFFECTS[] +// . +//------------------------------------------------------------------------ +Value _DoGrenze(CObjectPart* poPart, CRegion* pReg) +{ + // CRegion::Einheiten::iterator i; + CObjectPart* pOP; + CGrenze* poGrenze = 0; + Value oVal; + + // int i = poPart->index.size(); + + pOP = poPart->next; + + int32_t nGNr = poPart->index[0].asLong(); + + if (poPart->index.size() == 1) + poGrenze = pReg->GetFrontier(nGNr); + else + poGrenze = (*(pReg->GetVGrenzen()))[(size_t)nGNr]; + + if (!pOP) { + return poGrenze ? Value(1) : Value(0); + } + + if (!poGrenze) { + oVal.error("Falsche Indizierung fuer Objekt GRENZE"); + return oVal; + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "EFFECTS") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)poGrenze->GetEffects().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "EFFECTS") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poGrenze->GetEffects().size()) { + oVal.error("Falsche Indizierung in 'grenze.effects[idx]'"); + return oVal; + } + return Value(poGrenze->GetEffects()[(size_t)pOP->index[0].asLong()]); + } + + if (IsEqual(pOP->label.c_str(), "Richtung")) + oVal = Value((int32_t)poGrenze->Richtung()); + else if (IsEqual(pOP->label.c_str(), "Typ")) + oVal = Value(poGrenze->Typ()); + else if (IsEqual(pOP->label.c_str(), "Prozent")) + oVal = Value((int32_t)poGrenze->Prozent()); + else { + if (pOP->next) + oVal = ((CBlockBase*)poGrenze)->GetValue(pOP); + else + oVal = poGrenze->GetValue(pOP->label); //.error("Unbekanntes Attribut von Objekt GRENZE benutzt."); + } + return oVal; +} + +Value DoGrenze(CObjectPart* poPart) +{ + // CRegion::Einheiten::iterator i; + // CObjectPart* pOP; + // CGrenze* poGrenze = 0; + Value oVal; + + if (poPart->index.size() != 1) { + oVal.error("Objekt GRENZE unterstuetzt nur einen Index"); + return oVal; + } + + return _DoGrenze(poPart, g_poCurrentRegion); + /* + int i = poPart->index.size(); + + pOP = poPart->next; + + int32_t nGNr = poPart->index[0].asLong(); + + if( poPart->index.size()==1 ) + poGrenze = g_poCurrentRegion->GetFrontier( nGNr ); + else + poGrenze = (*(g_poCurrentRegion->GetVGrenzen()))[nGNr]; + + if( !pOP ) + { + return poGrenze ? Value( 1 ) : Value( 0 ); + } + + if( !poGrenze ) + { + oVal.error( "Falsche Indizierung fuer Objekt GRENZE" ); + return oVal; + } + + if( IsEqual( pOP->label.c_str(), "Richtung" ) ) + oVal = Value( (int32_t)poGrenze->Richtung() ); + else if( IsEqual( pOP->label.c_str(), "Typ" ) ) + oVal = Value( poGrenze->Typ() ); + else if( IsEqual( pOP->label.c_str(), "Prozent" ) ) + oVal = Value( (int32_t)poGrenze->Prozent() ); + else + { + oVal = poGrenze->GetValue( pOP->label );//.error("Unbekanntes Attribut von Objekt GRENZE benutzt."); + } + return oVal; + */ +} + +//------------------------------------------------------------------------ +// UNIT. +// UNIT[]. +// .ALIAS +// .ANZAHL +// .AURA +// .AURAMAX +// .BAUWERK +// .BESCHR +// .BEWACHT +// .COMMANDS.SIZE +// .COMMANDS[] +// .EFFECTS.SIZE +// .EFFECTS[] +// .EINHEITSBOTSCHAFTEN.SIZE +// .EINHEITSBOTSCHAFTEN[] +// .FREI.REITEN +// .FREI.GEHEN +// .GEGENSTAENDE.SIZE +// .GEGENSTAENDE[].NAME +// .GEGENSTAENDE[].ANZAHL +// . +// .GEWICHT +// .GRUPPE +// .GRUPPE. +// .HASMETAS (1, wenn Metabefehle in der Einheit) +// .HP +// .HUNGER +// .KAMPFSTATUS +// .KAMPFZAUBER.SIZE +// .KAMPFZAUBER[].KEY +// .KAMPFZAUBER[]. +// .KAP.REITEN +// .KAP.GEHEN +// .NAME +// .NUMMER +// .PARTEI +// .PARTEINAME +// .PARTEITARNUNG +// .POSITION (in Bauwerk) +// .PRIVAT +// .REGION. +// .RUNDE +// .SCHIFF +// .SILBER +// .TALENTE.SIZE +// .TALENTE[].NAME +// .TALENTE[].STUFE +// .TALENTE[].TAGE +// .TALENTE.[] +// ..STUFE +// ..TAGE +// .TEMP +// .TYP +// .VERKLEIDET +// .VERRAETER +// .WAHRERTYP +// .X +// .Y +// .Z +// . +//------------------------------------------------------------------------ +Value _DoUnit(CObjectPart* poPart, CEinheit* poUnit, CEinheit* poUnitQ) +{ + CObjectPart* pOP; + Value oVal; + + pOP = poPart->next; + + if (!pOP) { + return poUnit ? Value(1) : Value(0); + } + + if (!poUnit) { + if (poPart->index.size()) + oVal.error("Falsche Indizierung fuer Objekt UNIT"); + else + oVal.error("Objekt UNIT ausserhalb gueltigen Kontextes"); + return oVal; + } + + if (poUnitQ) { + if (IsEqual(pOP->label.c_str(), "Nummer")) { + oVal = Value(itoan(poUnitQ->Nummer(), g_poCurrentReport->ENrBase())); + } + else if (IsEqual(pOP->label.c_str(), "Typ")) { + oVal = Value(poUnitQ->Typ()); + } + else if (IsEqual(pOP->label.c_str(), "WahrerTyp")) { + oVal = Value(poUnitQ->WahrerTyp()); + } + else if (IsEqual(pOP->label.c_str(), "Name")) { + oVal = Value(poUnitQ->Name()); + } + else if (IsEqual(pOP->label.c_str(), "Gewicht")) { + oVal = Value(poUnitQ->Gewicht()); + } + else if (IsEqual(pOP->label.c_str(), "hp")) { + oVal = Value(poUnitQ->HP()); + } + else if (IsEqual(pOP->label.c_str(), "Runde")) { + oVal = Value((int32_t)(poUnitQ->Runde())); + } + else if (IsEqual(pOP->label.c_str(), "Region")) { + if (pOP->index.size() != 2) { + oVal.error("Falsche Indizierung in 'unit.region[dx,dy]'"); + return oVal; + } + CObjectPart oPart; + oPart.label = "REGION"; + oPart.index.push_back(Value(poUnitQ->Region()->GetEX() + pOP->index[0].asLong())); + oPart.index.push_back(Value(poUnitQ->Region()->GetEY() + pOP->index[1].asLong())); + oPart.index.push_back(Value(poUnitQ->Region()->GetEZ())); + oPart.next = pOP->next; + // int i = oPart.index.size(); + oVal = DoRegion(&oPart); + oPart.next = 0; + return oVal; + } + else if (IsEqual(pOP->label.c_str(), "X")) { + oVal = Value(poUnitQ->Region()->GetEX()); + } + else if (IsEqual(pOP->label.c_str(), "Y")) { + oVal = Value(poUnitQ->Region()->GetEY()); + } + else if (IsEqual(pOP->label.c_str(), "Z")) { + oVal = Value(poUnitQ->Region()->GetEZ()); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "frei") && IsEqual(pOP->next->label.c_str(), "reiten")) { + double fKapReiten, fFKapReiten; + double fKapGehen, fFKapGehen; + int32_t nRHO, nGHO; + poUnitQ->CalcKapazitaeten(fKapReiten, fFKapReiten, nRHO, fKapGehen, fFKapGehen, nGHO); + oVal = Value(fFKapReiten); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "frei") && IsEqual(pOP->next->label.c_str(), "gehen")) { + double fKapReiten, fFKapReiten; + double fKapGehen, fFKapGehen; + int32_t nRHO, nGHO; + poUnitQ->CalcKapazitaeten(fKapReiten, fFKapReiten, nRHO, fKapGehen, fFKapGehen, nGHO); + oVal = Value(fFKapGehen); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "kap") && IsEqual(pOP->next->label.c_str(), "reiten")) { + double fKapReiten, fFKapReiten; + double fKapGehen, fFKapGehen; + int32_t nRHO, nGHO; + poUnitQ->CalcKapazitaeten(fKapReiten, fFKapReiten, nRHO, fKapGehen, fFKapGehen, nGHO); + oVal = Value(fKapReiten); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "kap") && IsEqual(pOP->next->label.c_str(), "gehen")) { + double fKapReiten, fFKapReiten; + double fKapGehen, fFKapGehen; + int32_t nRHO, nGHO; + poUnitQ->CalcKapazitaeten(fKapReiten, fFKapReiten, nRHO, fKapGehen, fFKapGehen, nGHO); + oVal = Value(fKapGehen); + } + // ************ Kampfzauber ************ + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "kampfzauber") && IsEqual(pOP->next->label.c_str(), "size")) { + oVal = Value((int32_t)poUnitQ->CSpells().size()); + } + else if (pOP->next && pOP->index.size() == 1 && IsEqual(pOP->label.c_str(), "kampfzauber")) { + if (pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poUnitQ->CSpells().size()) { + return Value(0); + } + + CKampfzauber::Ptr pK = poUnitQ->CSpells()[(size_t)pOP->index[0].asLong()]; + + if (IsEqual(pOP->next->label.c_str(), "key")) { + if (pOP->next->index.size()) { + return pK->GetKey(0); + } + return Value(0); + } + return pK->GetValue(pOP->next->label); + } + // ************ Talente ************ + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "talente") && IsEqual(pOP->next->label.c_str(), "size")) { + oVal = Value((int32_t)poUnitQ->Talents().size()); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "talente") && + (IsEqual(pOP->next->label.c_str(), "name") || IsEqual(pOP->next->label.c_str(), "tage") || IsEqual(pOP->next->label.c_str(), "stufe") || IsEqual(pOP->next->label.c_str(), "punkte") || IsEqual(pOP->next->label.c_str(), "mod"))) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poUnitQ->Talents().size()) { + oVal.error("Falsche Indizierung in 'unit.talente[idx]'"); + return oVal; + } + const CTalent* pT = &(poUnitQ->Talents()[(size_t)pOP->index[0].asLong()]); + std::string sTalent = pT->m_sTyp; + if (IsEqual(pOP->next->label.c_str(), "name")) + oVal = Value(pT->m_sTyp); + else if (IsEqual(pOP->next->label.c_str(), "tage")) + oVal = poUnitQ->GetValue(sTalent, "tage"); + else if (IsEqual(pOP->next->label.c_str(), "stufe")) + oVal = Value((int32_t)pT->m_nStufe); + else if (IsFlag(VF_NOSKILLPOINTS) && IsEqual(pOP->next->label.c_str(), "mod")) + oVal = poUnitQ->GetValue(sTalent, "mod"); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "talente") && pOP->index.empty() && pOP->next && !pOP->next->next) { + // const CTalent* pT; + for (size_t i = 0; i < poUnitQ->Talents().size(); i++) { + if (IsEqual(poUnitQ->Talents()[i].m_sTyp, pOP->next->label.c_str())) { + int idx = pOP->next->index.empty() ? 0 : pOP->next->index[0].asLong(); + switch (idx) { + case 0: + return Value(poUnitQ->Talents()[i].m_nTage); + case 1: + return Value((int32_t)(poUnitQ->Talents()[i].m_nStufe)); + case 2: + return Value(poUnitQ->Talents()[i].m_nAddon); + default: + return Value(0); + } + } + } + return Value(0); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "talente")) { + oVal.error("Unbekanntes Subattribut fuer 'unit.talente'"); + return oVal; + } + // ************ Kommandos ************ + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "COMMANDS") && IsEqual(pOP->next->label.c_str(), "size")) { + oVal = Value((int32_t)poUnitQ->GetKommandos().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "COMMANDS") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poUnitQ->GetKommandos().size()) { + oVal.error("Falsche Indizierung in 'unit.commands[idx]'"); + return oVal; + } + oVal = Value(poUnitQ->GetKommandos()[pOP->index[0].asLong()]); + } + // ************ MetaOut ************ + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "OUTPUT") && IsEqual(pOP->next->label.c_str(), "size")) { + oVal = Value((int32_t)poUnitQ->GetMetaOut().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "OUTPUT") && pOP->index.size()) { + if (pOP->assign && pOP->index.size() == 1) { + if (pOP->index[0].asLong() == (int32_t)poUnitQ->GetMetaOut().size()) { + poUnitQ->GetMetaOut().push_back(Value("")); + poUnitQ->GetMetaOut().changed(true); + return Value(poUnitQ->GetMetaOut()[pOP->index[0].asLong()], true); + } + if (pOP->index[0].asLong() >= 0 || pOP->index[0].asLong() < (int)poUnitQ->GetMetaOut().size()) { + poUnitQ->GetMetaOut().changed(true); + return Value(poUnitQ->GetMetaOut()[pOP->index[0].asLong()], true); + } + } + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poUnitQ->GetMetaOut().size()) { + oVal.error("Falsche Indizierung in 'unit.output[idx]'"); + return oVal; + } + return Value(poUnitQ->GetMetaOut()[pOP->index[0].asLong()], false); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "OUTPUT") && !pOP->index.size()) { + return Value(poUnitQ->GetMetaOut()); + } + // ************ Effekte ************ + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "EFFECTS") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)poUnitQ->GetEffects().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "EFFECTS") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poUnitQ->GetEffects().size()) { + oVal.error("Falsche Indizierung in 'unit.effects[idx]'"); + return oVal; + } + return Value(poUnitQ->GetEffects()[(size_t)pOP->index[0].asLong()]); + } + // ************ Einheitsbotschaften ************ + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "EINHEITSBOTSCHAFTEN") && IsEqual(pOP->next->label.c_str(), "size")) { + oVal = Value((int32_t)poUnitQ->GetBotschaften().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "EINHEITSBOTSCHAFTEN") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poUnitQ->GetBotschaften().size()) { + oVal.error("Falsche Indizierung in 'unit.einheitsbotschaften[idx]'"); + return oVal; + } + oVal = Value(poUnitQ->GetBotschaften()[(size_t)pOP->index[0].asLong()]); + } + // ************ Gruppe ************ + else if (!pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "GRUPPE")) { + CGruppe::Ptr pG = poUnitQ->GetGruppe(); + oVal = Value(pG.get() ? 1 : 0); + } + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "GRUPPE")) { + CGruppe::Ptr pG = poUnitQ->GetGruppe(); + if (!pG.get()) { + oVal.error("Es existiert kein Subobjekt 'unit.gruppe'"); + return oVal; + } + return _DoGruppe(pOP->next, pG); + } + // ************ Gegenstaende ************ + else if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "GEGENSTAENDE") && IsEqual(pOP->next->label.c_str(), "size")) { + oVal = Value((int32_t)poUnitQ->Things().size()); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "GEGENSTAENDE") && (IsEqual(pOP->next->label.c_str(), "name") || IsEqual(pOP->next->label.c_str(), "anzahl"))) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poUnitQ->Things().size()) { + oVal.error("Falsche Indizierung in 'unit.gegenstaende[idx]'"); + return oVal; + } + const CEinheit::CGegenstand* pG = &(poUnitQ->Things()[(size_t)pOP->index[0].asLong()]); + if (IsEqual(pOP->next->label.c_str(), "name")) + oVal = Value(pG->first); + else if (IsEqual(pOP->next->label.c_str(), "anzahl")) + oVal = Value((int32_t)pG->second); + } + else if (pOP->next && IsEqual(pOP->label.c_str(), "GEGENSTAENDE")) { + oVal.error("Unbekanntes Subattribut fuer 'unit.gegenstaende'"); + return oVal; + } + else { + if (pOP->next == 0 || (IsEqual(pOP->next->label.c_str(), "Tage") || IsEqual(pOP->next->label.c_str(), "Stufe"))) { + if (pOP->index.empty()) + return poUnitQ->GetValue(pOP->label, pOP->next ? pOP->next->label : std::string("")); + } + if (pOP->next || pOP->index.size()) { + oVal = ((CBlockBase*)poUnitQ)->GetValue(pOP); + /* + char Buff[256]; + sprintf( Buff, "Unbekanntes Subattribut '%s'", pOP->next->label.c_str() ); + oVal.error( Buff ); + */ + } + else { + oVal = Value(0); + } + } + } + else { + oVal.error("Unbekannte Einheitennummer"); + } + return oVal; +} + +Value DoUnit(CObjectPart* poPart) +{ + // CRegion::Einheiten::iterator i; + // CObjectPart* pOP; + CEinheit* poUnit = 0; + CEinheit* poUnitQ = 0; + Value oVal; + + if (poPart->index.size() > 1) { + oVal.error("Objekt UNIT unterstuetzt nur einen Index"); + return oVal; + } + + // int i = poPart->index.size(); + + // pOP = poPart->next; + + /* + if( pOP->next && pOP->next->index.size() ) + { + std::string sErr = std::string("Attribut '") + pOP->label + std::string("' unterstuetzt keinen Index"); + oVal.error( sErr.c_str() ); + return oVal; + } + */ + poUnit = g_poCurrentUnit; + poUnitQ = poUnit; + + if (poPart->index.size() == 1) { + int32_t nENr = EinheitenNummer(poPart->index[0].asString()); + poUnit = g_poCurrentReport->SearchUnit(nENr, false); + poUnitQ = poUnit; + if (!poUnit || poUnit->GetQuality() != 10) { + EinheitenDB::iterator edbi; + edbi = g_coEDB.find(nENr); + if (edbi != g_coEDB.end()) { + poUnitQ = (*edbi).second; + if (!poUnit) + poUnit = poUnitQ; + } + } + } + + return _DoUnit(poPart, poUnit, poUnitQ); +} + +//------------------------------------------------------------------------ +// SHIP. +// .BESCHR +// .EFFECTS.SIZE +// .EFFECTS[] +// .GROESSE +// .MAXGROESSE +// .INSASSEN +// .KAPAZITAET +// .KAPITAEN +// .KUESTE +// .LADUNG +// .MAXLADUNG +// .NAME +// .NUMMER +// .PROZENT +// .SCHADEN +// .TYP +// . +//------------------------------------------------------------------------ +Value _DoShip(CObjectPart* poPart, CRegion* pReg) +{ + // CRegion::Einheiten::iterator i; + CObjectPart* pOP; + CSchiff* poShip = 0; + Value oVal; + + // int i = poPart->index.size(); + + pOP = poPart->next; + + int32_t nSNr = (int32_t)strtol(poPart->index[0].asString().c_str(), 0, g_poCurrentReport->BNrBase()); + + poShip = g_poCurrentReport->GetShip(nSNr); + if (!poShip) + poShip = pReg->GetShip(nSNr); + + if (!pOP) { + return poShip ? Value(1) : Value(0); + } + + if (!poShip) { + oVal.error("Falsche Indizierung fuer Objekt SHIP"); + return oVal; + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "EFFECTS") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)poShip->GetEffects().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "EFFECTS") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poShip->GetEffects().size()) { + oVal.error("Falsche Indizierung in 'ship.effects[idx]'"); + return oVal; + } + return Value(poShip->GetEffects()[(size_t)pOP->index[0].asLong()]); + } + + if (IsEqual(pOP->label.c_str(), "Nummer")) + oVal = Value(itoan(poShip->Nummer(), g_poCurrentReport->BNrBase())); + else if (IsEqual(pOP->label.c_str(), "Typ")) + oVal = Value(poShip->Typ()); + else if (IsEqual(pOP->label.c_str(), "Name")) + oVal = Value(poShip->Name()); + else if (IsEqual(pOP->label.c_str(), "Kapitaen")) + oVal = Value(itoan(poShip->Kapitaen(), g_poCurrentReport->ENrBase())); + else if (IsEqual(pOP->label.c_str(), "Kueste")) + oVal = Value(poShip->Kueste()); + else if (IsEqual(pOP->label.c_str(), "Anzahl")) + oVal = Value(poShip->Anzahl()); + else if (IsEqual(pOP->label.c_str(), "Schaden")) + oVal = Value(poShip->Schaden()); + else if (IsEqual(pOP->label.c_str(), "Prozent")) + oVal = Value(poShip->Prozent()); + else if (IsEqual(pOP->label.c_str(), "Ladung")) + oVal = Value(poShip->Ladung()); + else if (IsEqual(pOP->label.c_str(), "MaxLadung")) + oVal = Value(poShip->MaxLadung()); + else if (IsEqual(pOP->label.c_str(), "MaxGroesse")) + oVal = Value(poShip->MaxHolz()); + else if (IsEqual(pOP->label.c_str(), "Kapazitaet")) + oVal = Value(poShip->Kapazitaet()); + else if (IsEqual(pOP->label.c_str(), "Insassen")) + oVal = Value(poShip->Insassen()); + else if (IsEqual(pOP->label.c_str(), "Beschr")) + oVal = Value(poShip->Beschreibung()); + else { + if (pOP->next) + oVal = ((CBlockBase*)poShip)->GetValue(pOP); + else + oVal = poShip->GetValue(pOP->label); + // oVal.error("Unbekanntes Attribut von Objekt SHIP benutzt."); + } + return oVal; +} + +Value DoShip(CObjectPart* poPart) +{ + // CRegion::Einheiten::iterator i; + // CObjectPart* pOP; + // CSchiff* poShip = 0; + Value oVal; + + if (!poPart->index.size() && g_poCurrentShip) { + CObjectPart oPart; + oPart.label = "SHIP"; + oPart.index.push_back(Value(itoan(g_poCurrentShip->Nummer(), g_poCurrentReport->BNrBase()))); + oPart.next = poPart->next; + oVal = _DoShip(&oPart, g_poCurrentRegion); + oPart.next = 0; + return oVal; + } + + if (poPart->index.size() != 1) { + oVal.error("Objekt SHIP unterstuetzt nur einen Index"); + return oVal; + } + + return _DoShip(poPart, g_poCurrentRegion); +} + +//------------------------------------------------------------------------ +// BUILDING. +// .BESCHR +// .BESITZER +// .BELAGERER +// .BONUS +// .EFFECTS.SIZE +// .EFFECTS[] +// .GROESSE +// .INSASSEN +// .NAME +// .NUMMER +// .TYP +// .UNTERHALT +// . +//------------------------------------------------------------------------ +Value _DoBuilding(CObjectPart* poPart, CRegion* pReg) +{ + CObjectPart* pOP; + CBauwerk* poBuilding = 0; + Value oVal; + + pOP = poPart->next; + + int32_t nBNr = (int32_t)strtol(poPart->index[0].asString().c_str(), 0, g_poCurrentReport->BNrBase()); + + poBuilding = g_poCurrentReport->GetBuilding(nBNr); + if (!poBuilding) + poBuilding = pReg->GetBuilding(nBNr); + + if (!pOP) { + return poBuilding ? Value(1) : Value(0); + } + + if (!poBuilding) { + oVal.error("Falsche Indizierung fuer Objekt BUILDING"); + return oVal; + } + + if (pOP->next && pOP->index.empty() && IsEqual(pOP->label.c_str(), "EFFECTS") && IsEqual(pOP->next->label.c_str(), "size")) { + return Value((int32_t)poBuilding->GetEffects().size()); + } + else if (!pOP->next && IsEqual(pOP->label.c_str(), "EFFECTS") && pOP->index.size()) { + if (pOP->index.size() != 1 || pOP->index[0].asLong() < 0 || pOP->index[0].asLong() >= (int)poBuilding->GetEffects().size()) { + oVal.error("Falsche Indizierung in 'building.effects[idx]'"); + return oVal; + } + return Value(poBuilding->GetEffects()[(size_t)pOP->index[0].asLong()]); + } + + if (IsEqual(pOP->label.c_str(), "Nummer")) + oVal = Value(itoan(poBuilding->Nummer(), g_poCurrentReport->BNrBase())); + else if (IsEqual(pOP->label.c_str(), "Typ")) + oVal = Value(poBuilding->Typ()); + else if (IsEqual(pOP->label.c_str(), "Name")) + oVal = Value(poBuilding->Name()); + else if (IsEqual(pOP->label.c_str(), "Besitzer")) + oVal = Value(itoan(poBuilding->Besitzer(), g_poCurrentReport->ENrBase())); + else if (IsEqual(pOP->label.c_str(), "Belagerer")) + oVal = Value(itoan(poBuilding->Belagerer(), g_poCurrentReport->ENrBase())); + else if (IsEqual(pOP->label.c_str(), "Groesse")) + oVal = Value(poBuilding->Groesse()); + else if (IsEqual(pOP->label.c_str(), "Unterhalt")) + oVal = Value(poBuilding->Unterhalt()); + else if (IsEqual(pOP->label.c_str(), "Insassen")) + oVal = Value(poBuilding->Insassen()); + else if (IsEqual(pOP->label.c_str(), "Beschr")) + oVal = Value(poBuilding->Beschreibung()); + else if (IsEqual(pOP->label.c_str(), "Bonus")) { + if (IsEqual(poBuilding->Typ().c_str(), "Burg") || CBurgInfo::Lookup(poBuilding->Typ()).GetValue("Groesse").asLong()) { + oVal = Value(CBurgInfo::Lookup(poBuilding->Typ()).GetValue("Bonus").asLong()); + } + else + oVal = Value(0); + } + else { + if (pOP->next) + oVal = ((CBlockBase*)poBuilding)->GetValue(pOP); + else + oVal = poBuilding->GetValue(pOP->label); + // oVal.error("Unbekanntes Attribut von Objekt SHIP benutzt."); + } + return oVal; +} + +Value DoBuilding(CObjectPart* poPart) +{ + // CRegion::Einheiten::iterator i; + // CObjectPart* pOP; + // CBauwerk* poBuilding = 0; + Value oVal; + + if (!poPart->index.size() && g_poCurrentBuilding) { + CObjectPart oPart; + oPart.label = "BUILDING"; + oPart.index.push_back(Value(itoan(g_poCurrentBuilding->Nummer(), g_poCurrentReport->BNrBase()))); + oPart.next = poPart->next; + oVal = _DoBuilding(&oPart, g_poCurrentRegion); + oPart.next = 0; + return oVal; + } + + if (poPart->index.size() != 1) { + oVal.error("Objekt BUILDING unterstuetzt nur einen Index"); + return oVal; + } + + return _DoBuilding(poPart, g_poCurrentRegion); + + /* + int i = poPart->index.size(); + + pOP = poPart->next; + + int32_t nBNr = strtol( poPart->index[0].asString().c_str(), 0, g_poCurrentReport->BNrBase() ); + + poBuilding = g_poCurrentRegion->GetBuilding( nBNr ); + + if( !pOP ) + { + return poBuilding ? Value( 1 ) : Value( 0 ); + } + + if( !poBuilding ) + { + oVal.error( "Falsche Indizierung fuer Objekt BUILDING" ); + return oVal; + } + + if( IsEqual( pOP->label.c_str(), "Nummer" ) ) + oVal = Value( itoan( poBuilding->Nummer(), g_poCurrentReport->BNrBase() ) ); + else if( IsEqual( pOP->label.c_str(), "Typ" ) ) + oVal = Value( poBuilding->Typ() ); + else if( IsEqual( pOP->label.c_str(), "Name" ) ) + oVal = Value( poBuilding->Name() ); + else if( IsEqual( pOP->label.c_str(), "Besitzer" ) ) + oVal = Value( itoan( poBuilding->Besitzer(), g_poCurrentReport->ENrBase() ) ); + else if( IsEqual( pOP->label.c_str(), "Belagerer" ) ) + oVal = Value( itoan( poBuilding->Belagerer(), g_poCurrentReport->ENrBase() ) ); + else if( IsEqual( pOP->label.c_str(), "Groesse" ) ) + oVal = Value( poBuilding->Groesse() ); + else if( IsEqual( pOP->label.c_str(), "Unterhalt" ) ) + oVal = Value( poBuilding->Unterhalt() ); + else if( IsEqual( pOP->label.c_str(), "Insassen" ) ) + oVal = Value( poBuilding->Insassen() ); + else + { + oVal = poBuilding->GetValue( pOP->label ); + // oVal.error("Unbekanntes Attribut von Objekt SHIP benutzt."); + } + return oVal; + */ +} + +Value FGetUnitOfRegion(Expression* poContext, ArgumentList& coArgs) +{ + CRegion::VEinheiten* pVE; + // CEinheit* poUnit; + Value oVal; + + pVE = &g_poCurrentRegion->GetVEinheiten(); + if (coArgs[0].getType() != VT_INT) { + oVal.error("Falscher Parameter fuer 'localunit(idx)'!"); + return oVal; + } + if (coArgs[0].asLong() >= (int)pVE->size()) { + oVal.error("Index ausserhalb des gueltigen Bereichen fuer 'localunit(idx)'!"); + return oVal; + } + oVal = Value(itoan((*pVE)[(size_t)coArgs[0].asLong()]->Nummer(), g_poCurrentReport->ENrBase())); + return oVal; +} + +Value FRandom(Expression* poContext, ArgumentList& coArgs) +{ + if (!coArgs.empty()) { + Value oVal; + oVal.error("Unerwartete(r) Parameter fuer 'random()'!"); + return oVal; + } + return Value(Random()); +} + +Value FEquals(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'equals(val1,val2)'!"); + return oVal; + } + return Value(IsEqual(coArgs[0].asString().c_str(), coArgs[1].asString().c_str()) ? 1 : 0); +} + +Value FMatch(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'match(val,regx)'!"); + return oVal; + } + if (coArgs[1].asString().empty()) { + Value oVal; + oVal.error("Leerer regulaerer Ausdruck fuer 'match(val,regx)'!"); + return oVal; + } + + CRegExp oRE; + std::string sText = coArgs[0].asString(); + bool rc; + if (oRE.Prepare(coArgs[1].asString())) { + rc = oRE.Find(sText, 0); + } + else { + rc = false; + } + Value oVal; + oVal = Value(oRE.SubStr(sText)); + poContext->setValue("$&", &oVal); + oVal = Value(oRE.Begin() < 0 ? std::string() : sText.substr(0, (size_t)oRE.Begin())); + poContext->setValue("$`", &oVal); + oVal = Value(oRE.Begin() < 0 ? std::string() : sText.substr((size_t)(oRE.Begin() + oRE.Size()))); + poContext->setValue("$\xB4", &oVal); + oVal = Value(oRE.SubStr(sText, oRE.Count() - 1)); + poContext->setValue("$+", &oVal); + for (int32_t spc = 1; spc < oRE.Count(); spc++) { + oVal = Value(oRE.SubStr(sText, spc)); + poContext->setValue((std::string("$") + ToString(spc)).c_str(), &oVal); + } + + return Value(rc ? 1 : 0); +} + +Value FBefore(Expression* poContext, ArgumentList& coArgs) +{ + CRegExp oRE; + std::string sText; + + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'before(val,regx)'!"); + return oVal; + } + if (coArgs[1].asString().empty()) { + Value oVal; + oVal.error("Leerer regulaerer Ausdruck fuer 'before(val,regx)'!"); + return oVal; + } + + sText = coArgs[0].asString(); + + if (oRE.Prepare(coArgs[1].asString())) { + if (oRE.Find(sText, 0)) + sText.erase((size_t)oRE.Begin()); + } + else { + Value oVal; + oVal.error("Fehlerhafter regulaerer Ausdruck fuer 'before(val,regx)'!"); + return oVal; + } + + return Value(sText); +} + +Value FAfter(Expression* poContext, ArgumentList& coArgs) +{ + CRegExp oRE; + std::string sText; + + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'after(val,regx)'!"); + return oVal; + } + if (coArgs[1].asString().empty()) { + Value oVal; + oVal.error("Leerer regulaerer Ausdruck fuer 'after(val,regx)'!"); + return oVal; + } + + sText = coArgs[0].asString(); + + if (oRE.Prepare(coArgs[1].asString())) { + if (oRE.Find(sText, 0)) + sText.erase(0, (size_t)oRE.End()); + else + sText = ""; + } + else { + Value oVal; + oVal.error("Fehlerhafter regulaerer Ausdruck fuer 'after(val,regx)'!"); + return oVal; + } + + return Value(sText); +} + +Value FCrop(Expression* poContext, ArgumentList& coArgs) +{ + CRegExp oRE; + std::string sText; + + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'crop(val,regx)'!"); + return oVal; + } + if (coArgs[1].asString().empty()) { + Value oVal; + oVal.error("Leerer regulaerer Ausdruck fuer 'crop(val,regx)'!"); + return oVal; + } + + sText = coArgs[0].asString(); + + if (oRE.Prepare(coArgs[1].asString())) { + if (oRE.Find(sText, 0)) + sText = sText.substr((size_t)oRE.Begin(), (size_t)oRE.Size()); + else + sText = ""; + } + else { + Value oVal; + oVal.error("Fehlerhafter regulaerer Ausdruck fuer 'crop(val,regx)'!"); + return oVal; + } + + return Value(sText); +} + +Value FChange(Expression* poContext, ArgumentList& coArgs) +{ + std::string sText; + + if (coArgs.size() != 3) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'change(val,regx,rep)'!"); + return oVal; + } + if (coArgs[1].asString().empty()) { + Value oVal; + oVal.error("Leerer regulaerer Ausdruck fuer 'change(val,regx,rep)'!"); + return oVal; + } + + sText = coArgs[0].asString(); + if (!CRegExp::RuledReplace(sText, coArgs[1].asString(), coArgs[2].asString())) { + Value oVal; + oVal.error("Fehlerhafter regulaerer Ausdruck fuer 'change(val,regx,rep)'!"); + return oVal; + } + + return Value(sText); +} + +Value FSubStr(Expression* poContext, ArgumentList& coArgs) +{ + std::string sText; + + if (coArgs.size() != 3) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'substr(val,pos,num)'!"); + return oVal; + } + sText = coArgs[0].asString(); + int nPos = coArgs[1].asLong(); + int nLen = coArgs[2].asLong(); + + if (nPos < 0) { + nPos = (int32_t)sText.length() + nPos; + if (nPos < 0) + nPos = 0; + } + if (nLen < 0) { + nLen = (int32_t)sText.length() + nLen; + if (nLen < 0) + nLen = 0; + } + if (nPos >= (int)sText.length()) + return Value(""); + + if (nPos + nLen > (int)sText.length()) { + nLen = (int32_t)sText.length() - nPos; + } + + return Value(sText.substr((size_t)nPos, (size_t)nLen)); +} + +Value FCeil(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'ceil(val)'!"); + return oVal; + } + return Value((int32_t)ceil(coArgs[0].asReal())); +} + +Value FFloor(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'floor(val)'!"); + return oVal; + } + return Value((int32_t)floor(coArgs[0].asReal())); +} + +Value FAbs(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'abs(val)'!"); + return oVal; + } + return Value(fabs(coArgs[0].asReal())); +} + +Value FSign(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'sign(val)'!"); + return oVal; + } + if (coArgs[0].asReal() < 0) { + return Value(-1); + } + else if (coArgs[0].asReal() > 0) { + return Value(1); + } + else { + return Value(0); + } +} + +Value FSqrt(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'sqrt(val)'!"); + return oVal; + } + double val = coArgs[0].asReal(); + if (val >= 0) { + return Value(sqrt(coArgs[0].asReal())); + } + else { + Value oVal; + oVal.error("Negativer Parameter fuer 'sqrt(val)'!"); + return oVal; + } +} + +Value FExp(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'exp(val)'!"); + return oVal; + } + return Value(exp(coArgs[0].asReal())); +} + +Value FLog(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'log(val)'!"); + return oVal; + } + double val = coArgs[0].asReal(); + if (val > 0) { + return Value(log(val)); + } + else { + Value oVal; + oVal.error("Negativer Parameter fuer 'log(val)'!"); + return oVal; + } +} + +Value FLog10(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'log10(val)'!"); + return oVal; + } + double val = coArgs[0].asReal(); + if (val > 0) { + return Value(log10(val)); + } + else { + Value oVal; + oVal.error("Negativer Parameter fuer 'log10(val)'!"); + return oVal; + } +} + +Value FFloat(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'float(val)'!"); + return oVal; + } + return Value(coArgs[0].asReal()); +} + +Value FInt(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'int(val)'!"); + return oVal; + } + return Value(coArgs[0].asLong()); +} + +Value FIsNothing(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'isempty(val)'!"); + return oVal; + } + return Value(coArgs[0].getType() == VT_EMPTY ? 1 : 0); +} + +Value FLength(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'length(val)'!"); + return oVal; + } + return Value(coArgs[0].getType() == VT_EMPTY ? 0 : (int32_t)(coArgs[0].asString().length())); +} + +Value FFlatten(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'flatten(val)'!"); + return oVal; + } + return Value(Flatten(coArgs[0].asString())); +} + +Value FItoan(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'itoan(val,base)'!"); + return oVal; + } + if (coArgs[1].asLong() < 2 || coArgs[1].asLong() > 36) { + Value oVal; + oVal.error("Falsche Basis fuer 'itoan(val,base)'!"); + return oVal; + } + return Value(std::string(itoan(coArgs[0].asLong(), coArgs[1].asLong()))); +} + +Value FAntoi(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'antoi(val,base)'!"); + return oVal; + } + if (coArgs[1].asLong() < 2 || coArgs[1].asLong() > 36) { + Value oVal; + oVal.error("Falsche Basis fuer 'antoi(val,base)'!"); + return oVal; + } + return Value((int32_t)strtol(coArgs[0].asString().c_str(), 0, coArgs[1].asLong())); +} + +Value FXName(Expression* poContext, ArgumentList& coArgs) +{ + static std::set coNames; + CRNENode* poRNE; + Value oVal; + int32_t nRep = 10; + + if (coArgs.size() < 1 || coArgs.size() > 2) { + oVal.error("Falsche Parameterzahl fuer 'xname(rne,maxrep)'!"); + return oVal; + } + + if (coArgs[1].asLong() > 0) + nRep = coArgs[1].asLong(); + + if (coNames.empty()) { + EinheitenDB::iterator ei; + for (ei = g_coEDB.begin(); ei != g_coEDB.end(); ei++) { + coNames.insert((*ei).second->Name()); + } + RegionDB::iterator ri; + for (ri = g_coRDB.begin(); ri != g_coRDB.end(); ri++) { + coNames.insert((*(*ri).second.begin())->GetName()); + } + } + + if (coArgs[0].asString().empty()) { + oVal.error("Fehlende Regelangabe fuer 'xname(rne,maxrep)'!"); + return oVal; + } + + try { + poRNE = CRNENode::CreateFromString(coArgs[0].asString()); + } + catch (CRNEException e) { + oVal.error((std::string("Fehler in Namensausdruck beim Aufruf von xname(rne,maxrep): ") + e.why()).c_str()); + return oVal; + } + + if (poRNE) { + std::string sStr; + for (int i = 0; i < nRep; i++) { + sStr = poRNE->GenAVal(); + if (coNames.find(sStr) == coNames.end()) { + coNames.insert(sStr); + break; + } + } + oVal = Value(sStr); + delete poRNE; + } + return oVal; +} + +static int myTolower(int c) +{ + switch (c) { + case 0xC4: + return 0xE4; + case 0xD6: + return 0xF6; + case 0xDC: + return 0xFC; + } + return tolower(c); +} + +static int myToupper(int c) +{ + switch (c) { + case 0xE4: + return 0xC4; + case 0xF6: + return 0xD6; + case 0xFC: + return 0xDC; + } + return toupper(c); +} + +Value FToLower(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'tolower(val)'!"); + return oVal; + } + + std::string sTxt = coArgs[0].asString(); + std::transform(sTxt.begin(), sTxt.end(), sTxt.begin(), static_cast(myTolower)); + + return Value(sTxt); +} + +Value FToUpper(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'tolower(val)'!"); + return oVal; + } + + std::string sTxt = coArgs[0].asString(); + std::transform(sTxt.begin(), sTxt.end(), sTxt.begin(), static_cast(myToupper)); + + return Value(sTxt); +} + +Value FTime(Expression* poContext, ArgumentList& coArgs) +{ + if (!coArgs.empty()) { + Value oVal; + oVal.error("Unerwartete(r) Parameter fuer 'time()'!"); + return oVal; + } + return Value(int32_t(double(clock() - (uint32_t)g_nTimeCorrection) / CLOCKS_PER_SEC * 1000)); +} + +Value FAnd(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'and(val1,val2)'!"); + return oVal; + } + return Value(coArgs[0].asLong() & coArgs[1].asLong()); +} + +Value FOr(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'or(val1,val2)'!"); + return oVal; + } + return Value(coArgs[0].asLong() | coArgs[1].asLong()); +} + +Value FXor(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 2) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'xor(val1,val2)'!"); + return oVal; + } + return Value(coArgs[0].asLong() ^ coArgs[1].asLong()); +} + +Value FNot(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'not(val)'!"); + return oVal; + } + return Value(~coArgs[0].asLong()); +} + +Value FTypeOf(Expression* poContext, ArgumentList& coArgs) +{ + if (coArgs.size() != 1) { + Value oVal; + oVal.error("Falsche Parameterzahl fuer 'typeof(exp)'!"); + return oVal; + } + return Value(int32_t(coArgs[0].getType())); +} + +Value Fgv(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + if (coArgs.size() != 4) { + oVal.error("Falsche Parameterzahl fuer '_gv_(,,,)'!"); + return oVal; + } + + std::string sText = Flatten(coArgs[3].asString()); + if (!coArgs[2].asString().empty()) + sText += coArgs[2].asString(); + uint32_t nAusweis = Hash((const ub1*)(sText.c_str()), (uint32_t)sText.length(), (uint32_t)coArgs[0].asLong()); + int nUntil = coArgs[1].asLong(); + sText = itoan((int32_t)nAusweis, 36); + if (sText.length() > 4) + sText = sText.substr(sText.length() - 4); + while (sText.length() < 4) + sText = std::string("0") + sText; + std::string sUntil = itoan(nUntil > 0 ? nUntil : 0, 36); + if (sUntil.length() < 2) + sUntil = std::string("0") + sUntil; + sText = std::string("(") + sUntil + sText; + + if (coArgs[2].asString().empty()) + sText = std::string("G") + sText; + else + sText = std::string("L") + sText; + if (nUntil < 0) + sText = std::string("P") + sText; + else + sText = std::string("V") + sText; + + uint32_t nChk = Hash((const ub1*)(sText.c_str()), (uint32_t)sText.length(), (uint32_t)coArgs[0].asLong()); + std::string sChk = itoan((int32_t)nChk, 36); + if (sChk.length() > 2) + sChk = sChk.substr(sChk.length() - 2); + if (sChk.length() < 2) + sChk = std::string("0") + sChk; + sText += sChk + ")"; + return Value(sText); +} + +Value Fcv(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + if (coArgs.size() != 4) { + oVal.error("Falsche Parameterzahl fuer '_cv_(,,,)'!"); + return oVal; + } + + std::string sAusweis = coArgs[1].asString(); + std::string sText = Flatten(coArgs[3].asString()); + if (sAusweis.length() > 2 && sAusweis[1] == 'L') + sText += coArgs[2].asString(); + uint32_t nAusweis = Hash((const ub1*)(sText.c_str()), (uint32_t)sText.length(), (uint32_t)coArgs[0].asLong()); + sText = itoan((int32_t)nAusweis, 36); + if (sText.length() > 4) + sText = sText.substr(sText.length() - 4); + while (sText.length() < 4) + sText = std::string("0") + sText; + if (sAusweis.length() < 12 && sText != sAusweis.substr(5, 4)) + return Value(4); + uint32_t nChk = Hash((const ub1*)(sAusweis.c_str()), 9, (uint32_t)coArgs[0].asLong()); + std::string sChk = itoan((int32_t)nChk, 36); + if (sChk.length() > 2) + sChk = sChk.substr(sChk.length() - 2); + if (sChk.length() < 2) + sChk = std::string("0") + sChk; + if (sChk != sAusweis.substr(9, 2)) + return Value(3); + int32_t nUntil = (int32_t)strtol(sAusweis.substr(3, 2).c_str(), 0, 36); + if (nUntil && nUntil < g_poCurrentReport->Runde()) + return Value(2); + if (nUntil == g_poCurrentReport->Runde()) + return Value(1); + + return Value(0); +} + +namespace GF { +enum GF_MODE { enREAD, enWRITE, enAPPEND, enPIPE }; + +enum GF_STATE { enOK, enEOF = -1, enERROR = 1 }; +} // namespace GF + +struct FileInfo +{ + std::string sName; + std::string sMessage; + FILE* hFile; + int nState; + int nMode; +}; + +int32_t g_nHandleCount; +std::map g_coOpenFiles; + +static FileInfo* GetFileInfo(int32_t nHandle) +{ + std::map::iterator fi = g_coOpenFiles.find(nHandle); + if (fi == g_coOpenFiles.end()) + return 0; + return &((*fi).second); +} + +#define RL2SBUFFSIZE 512 + +static bool ReadLine2String(FILE* hFile, std::string& sLine) +{ + char Buff[RL2SBUFFSIZE]; + char* rc; + sLine = ""; + do { + Buff[0] = 0; + rc = fgets(Buff, 512, hFile); + sLine += Buff; + if (feof(hFile)) + break; + } while (strlen(Buff) == RL2SBUFFSIZE - 1 && (Buff[RL2SBUFFSIZE - 2] != 10 || Buff[RL2SBUFFSIZE - 2] != 13)); + while (!sLine.empty() && (sLine[sLine.size() - 1] == 10 || sLine[sLine.size() - 1] == 13)) + sLine.erase(sLine.size() - 1, 1); + return rc != 0; +} + +void CloseAllOpenFiles() +{ + std::map::iterator i = g_coOpenFiles.begin(); + while (i != g_coOpenFiles.end()) { + if (i->second.hFile) { + if (i->second.nMode == GF::enPIPE) { +#ifdef _WIN32 + i->second.sMessage = ToString((int32_t)_pclose(i->second.hFile)); +#else + i->second.sMessage = ToString((int32_t)pclose(i->second.hFile)); +#endif + } + else { + fclose(i->second.hFile); + } + i->second.hFile = 0; + } + i++; + } +} + +Value FSystem(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + if (coArgs.size() != 1) { + oVal.error("Falsche Parameterzahl fuer 'system(command)'!"); + return oVal; + } + g_nHandleCount++; + FileInfo* pfi = &(g_coOpenFiles[g_nHandleCount]); + pfi->sName = coArgs[0].asString(); + pfi->nMode = GF::enPIPE; + pfi->nState = 0; +#ifdef _WIN32 + pfi->hFile = _popen(pfi->sName.c_str(), "rt"); +#else + pfi->hFile = popen(pfi->sName.c_str(), "r"); +#endif + if (!pfi->hFile) { + pfi->nState = GF::enERROR; + pfi->sMessage = std::string("Couldn't execute command '") + pfi->sName + "'"; + } + return Value(g_nHandleCount); +} + +Value FOpen(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + if (coArgs.size() != 2) { + oVal.error("Falsche Parameterzahl fuer 'open(filename,mode)'!"); + return oVal; + } + g_nHandleCount++; + FileInfo* pfi = &(g_coOpenFiles[g_nHandleCount]); + pfi->sName = coArgs[0].asString(); + pfi->nMode = coArgs[1].asLong(); + pfi->nState = 0; + if (pfi->nMode == GF::enREAD) { + pfi->hFile = fopen(pfi->sName.c_str(), "r"); + } + else if (pfi->nMode == GF::enWRITE) { + pfi->hFile = fopen(pfi->sName.c_str(), "w"); + } + else if (pfi->nMode == GF::enAPPEND) { + pfi->hFile = fopen(pfi->sName.c_str(), "a"); + } + else { + oVal.error("Falscher Modus fuer 'open(filename,mode)'!"); + return oVal; + } + if (!pfi->hFile) { + pfi->nState = GF::enERROR; + pfi->sMessage = std::string("Couldn't open file '") + pfi->sName + "'"; + } + return Value(g_nHandleCount); +} + +Value FClose(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + FileInfo* pfi = GetFileInfo(coArgs[0].asLong()); + if (!pfi) { + oVal.error("Unbekanntes Filehandle!"); + return oVal; + } + pfi->nState = 0; + pfi->sMessage = ""; + if (!pfi || !pfi->hFile) { + return Value(0); + } + if (pfi->nMode == GF::enPIPE) { +#ifdef _WIN32 + pfi->sMessage = ToString((int32_t)_pclose(pfi->hFile)); +#else + pfi->sMessage = ToString((int32_t)pclose(pfi->hFile)); +#endif + } + else { + fclose(pfi->hFile); + } + pfi->hFile = 0; + return Value(0); +} + +Value FSeek(Expression* poContext, ArgumentList& coArgs); +Value FRead(Expression* poContext, ArgumentList& coArgs); +Value FWrite(Expression* poContext, ArgumentList& coArgs); + +Value FReadLine(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + FileInfo* pfi = GetFileInfo(coArgs[0].asLong()); + if (!pfi) { + oVal.error("Unbekanntes Datei-Handle!"); + return oVal; + } + pfi->nState = GF::enOK; + pfi->sMessage = ""; + if (!pfi || !pfi->hFile) { + pfi->nState = GF::enERROR; + pfi->sMessage = "Datei ist nicht geoeffnet!"; + return Value(""); + } + std::string sLine; + bool ok = ReadLine2String(pfi->hFile, sLine); + if (!ok) { + pfi->nState = GF::enERROR; + pfi->sMessage = strerror(errno); + } + if (feof(pfi->hFile)) { + pfi->nState = GF::enEOF; + pfi->sMessage = ""; + } + return Value(sLine); +} + +Value FWriteLine(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + FileInfo* pfi = GetFileInfo(coArgs[0].asLong()); + if (!pfi) { + oVal.error("Unbekanntes Datei-Handle!"); + return Value(0); + } + pfi->nState = GF::enOK; + pfi->sMessage = ""; + if (!pfi || !pfi->hFile) { + pfi->nState = GF::enERROR; + pfi->sMessage = "Datei ist nicht geoeffnet!"; + return Value(0); + } + int rc = fprintf(pfi->hFile, "%s\n", coArgs[1].asString().c_str()); + if (rc < 0) { + pfi->nState = GF::enERROR; + pfi->sMessage = strerror(errno); + } + return Value((int32_t)(pfi->nState == GF::enOK ? coArgs[1].asString().length() + 1 : 0)); +} + +static std::string EscapeXML(const std::string& sText) +{ + std::string sTxt(sText); + CRegExp::Replace(sTxt, "&", "&"); + CRegExp::Replace(sTxt, "<", "<"); + CRegExp::Replace(sTxt, ">", ">"); + return sTxt; +} + +/* +static std::string UnescapeXML( const std::string& sText ) +{ + std::string sTxt( sText ); + CRegExp::Replace( sTxt, "<","<" ); + CRegExp::Replace( sTxt, ">",">" ); + CRegExp::Replace( sTxt, "$amp;","&" ); + return sTxt; +} +*/ + +char cBuff = 0; + +static char GetChar(FILE* pStream) +{ + char c; + if (cBuff) { + c = cBuff; + cBuff = 0; + return c; + } + return (char)fgetc(pStream); +} + +static void UngetChar(char c) +{ + cBuff = c; +} + +static std::string ReadXMLTag(FILE* pStream, std::string* pID = 0) +{ + // Tag-Start suchen + std::string sTag; + char c; + while ((c = GetChar(pStream)) != '<' && !ferror(pStream) && !feof(pStream)) { + } + do { + c = GetChar(pStream); + if (IsAlpha(c) || c == '/') + sTag += c; + else + break; + } while (!ferror(pStream) && !ferror(pStream) && !feof(pStream)); + if (c == '>') + UngetChar(c); + else if (IsSpace(c) && pID) { + int q = 0; + *pID = ""; + do { + c = GetChar(pStream); + if (c == '\"') + q++; + if (q == 1 && c != '\"') + *pID += c; + } while (c != '>' && !ferror(pStream) && !feof(pStream)); + if (c == '>') + UngetChar(c); + } + while ((c = GetChar(pStream)) != '>' && !ferror(pStream) && !feof(pStream)) { + } + return sTag; +} + +static std::string ReadXMLValue(FILE* pStream) +{ + std::string sVal; + char c; + do { + c = GetChar(pStream); + if (c != '<') + sVal += c; + } while (c != '<' && !ferror(pStream) && !feof(pStream)); + if (c == '<') + UngetChar(c); + return sVal; +} + +static bool ReadValueXML(FILE* pStream, Value& oVal) +{ + static std::string sLastTag; + std::string sTag = ReadXMLTag(pStream); + if (!sTag.empty()) { + switch (sTag[0]) { + case 'n': + oVal = Value(); + break; + case 'e': + oVal.error(ReadXMLValue(pStream).c_str()); + ReadXMLTag(pStream); + break; + case 'i': + oVal = Value((int32_t)strtol(ReadXMLValue(pStream).c_str(), NULL, 10)); + ReadXMLTag(pStream); + break; + case 'f': + oVal = Value((double)strtod(ReadXMLValue(pStream).c_str(), NULL)); + ReadXMLTag(pStream); + break; + case 's': + oVal = Value(ReadXMLValue(pStream)); + ReadXMLTag(pStream); + break; + case 'a': { + Value vT; + oVal = Value(VT_VECTOR); + while (ReadValueXML(pStream, vT)) { + oVal.setAt(Value(oVal.size()), vT); + } + } break; + case 'd': { + std::string sT2, sID; + Value vT; + oVal = Value(VT_MAP); + while (true) { + sT2 = ReadXMLTag(pStream, &sID); + if (sT2 == "pair") { + if (ReadValueXML(pStream, vT)) + oVal.setAt(Value(sID), vT); + ReadXMLTag(pStream); + } + else if (sT2 == "/dic") { + break; + } + } + } break; + case '/': + sLastTag = sTag; + oVal = Value(); + return false; + } + } + return true; +} + +static int32_t WriteValueXML(FILE* pStream, const Value& oVal, int32_t indent = 0) +{ + static std::string sSpace(" "); + if (indent > 58) + indent = 58; + + fprintf(pStream, "%s", sSpace.substr(0, (size_t)indent).c_str()); + + switch (oVal.cself().getType()) { + case VT_EMPTY: + fprintf(pStream, "\n"); + break; + case VT_ERROR: + fprintf(pStream, "%s\n", EscapeXML(oVal.asString()).c_str()); + break; + case VT_INT: + fprintf(pStream, "%ld\n", oVal.asLong()); + break; + case VT_FLOAT: + fprintf(pStream, "%.3f\n", oVal.asReal()); + break; + case VT_STRING: + fprintf(pStream, "%s\n", EscapeXML(oVal.asString()).c_str()); + break; + case VT_VECTOR: { + fprintf(pStream, "\n"); + for (int32_t i = 0; i < oVal.size(); i++) { + WriteValueXML(pStream, oVal.getAt(Value(i)), indent + 1); + } + fprintf(pStream, "%s\n", sSpace.substr(0, (size_t)indent).c_str()); + } break; + case VT_MAP: { + fprintf(pStream, "\n"); + for (int32_t i = 0; i < oVal.size(); i++) { + fprintf(pStream, "%s\n", sSpace.substr(0, (size_t)indent + 1).c_str(), EscapeXML(oVal.getNth(i).asString()).c_str()); + WriteValueXML(pStream, oVal.getAt(oVal.getNth(i)), indent + 2); + fprintf(pStream, "%s\n", sSpace.substr(0, (size_t)indent + 1).c_str()); + } + fprintf(pStream, "%s\n", sSpace.substr(0, (size_t)indent).c_str()); + } break; + case VT_REF: + // TODO: Missing support + break; + } + return 1; +} + +Value FReadValue(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + FileInfo* pfi = GetFileInfo(coArgs[0].asLong()); + if (!pfi) { + oVal.error("Unbekanntes Datei-Handle!"); + return oVal; + } + pfi->nState = GF::enOK; + pfi->sMessage = ""; + if (!pfi || !pfi->hFile) { + pfi->nState = GF::enERROR; + pfi->sMessage = "Datei ist nicht geoeffnet!"; + return Value(""); + } + if (!ReadValueXML(pfi->hFile, oVal)) { + pfi->nState = GF::enERROR; + pfi->sMessage = "Value konnte nicht aus XML-Strom gelesen werden!"; + oVal.error(pfi->sMessage.c_str()); + } + if (ferror(pfi->hFile)) { + pfi->nState = GF::enERROR; + pfi->sMessage = strerror(errno); + oVal.error(pfi->sMessage.c_str()); + } + return oVal; +} + +Value FWriteValue(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + FileInfo* pfi = GetFileInfo(coArgs[0].asLong()); + if (!pfi) { + oVal.error("Unbekanntes Datei-Handle!"); + return oVal; + } + pfi->nState = GF::enOK; + pfi->sMessage = ""; + if (!pfi || !pfi->hFile) { + pfi->nState = GF::enERROR; + pfi->sMessage = "Datei ist nicht geoeffnet!"; + return Value(""); + } + int32_t rc = WriteValueXML(pfi->hFile, coArgs[1]); + if (ferror(pfi->hFile)) { + pfi->nState = GF::enERROR; + pfi->sMessage = strerror(errno); + } + return Value((int32_t)(pfi->nState == GF::enOK ? rc : 0)); +} + +Value FStatus(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + FileInfo* pfi = GetFileInfo(coArgs[0].asLong()); + if (!pfi) { + oVal.error("Unbekanntes Datei-Handle!"); + return oVal; + } + return Value((int32_t)(pfi->nState)); +} + +Value FStatusText(Expression* poContext, ArgumentList& coArgs) +{ + Value oVal; + FileInfo* pfi = GetFileInfo(coArgs[0].asLong()); + if (!pfi) { + oVal.error("Unbekanntes Datei-Handle!"); + return oVal; + } + return Value(pfi->sMessage); +} + +static VKommandos _dummy; +static VKommandos* _pcoCmd = &_dummy; + +bool ExistUserFunction(const std::string& sName) +{ + CMetaCommand* pMC; + pMC = g_oScriptBase.FindProc(sName); + if (!pMC) + return false; + if (!pMC->IsFunction()) + return false; + return true; +} + +bool DoUserFunction(const std::string& sName, ArgumentList& coArgs, Value* poVal) +{ + std::vector coRefs; + Expression::Variables oVars; + // VKommandos* pcoCmd; + CMetaCommand* pMC; + std::string sPName; + std::string sAName; + std::string sStackInfo; + int nPArgs = 0, argi = 1; + size_t nTC = 0; + + pMC = g_oScriptBase.FindProc(sName); + if (!pMC) + return false; + + sStackInfo = std::string("#func ") + sName; + if (!pMC->IsFunction()) { + char Buff[256]; + snprintf(Buff, sizeof(Buff), "Die Prozedur '#proc %s' kann nicht als Funktion aufgerufen werden!", sName.c_str()); + poVal->error(std::string(Buff).c_str()); + return false; + } + + nPArgs = atoi((*pMC)[0].c_str()) - 1; + coRefs.resize((size_t)nPArgs, 0); + + while (nTC < coArgs.size()) { + argi++; + oVars[std::string("#ARG") + ToString((int32_t)(argi - 1))] = coArgs[nTC]; + sStackInfo += std::string(" ") + coArgs[nTC].asString(); + if (argi - 1 <= nPArgs) { + sAName = (*pMC)[(size_t)argi]; + oVars[sAName[0] == '&' ? sAName.substr(1) : sAName] = coArgs[nTC]; + // coRefs[argi-2] = m_pvRef; m_pvRef = 0; + } + else { + char Buff[256]; + snprintf(Buff, sizeof(Buff), "Ueberzaehliges Argument '%s' fuer Funktion '%s'!", coArgs[nTC].asString().c_str(), sName.c_str()); + poVal->error(std::string(Buff).c_str()); + return false; + } + nTC++; + } + oVars[std::string("#ARG0")] = Value((int32_t)(argi - 1)); + if (argi - 1 == nPArgs) { + // printf( " ; Call to procedure: %s\n", sPName.c_str() ); + // for( Expression::Variables::iterator evi = oVars.begin(); evi != oVars.end(); evi++ ) + // { + // printf( " ; %s = %s\n", (*evi).first.c_str(), (*evi).second.asString().c_str() ); + // } + // CALL + oVars[std::string("$RETURN")] = Value(); + CMCI oMCI(nPArgs + 2); + g_coCallStack.push_back(sStackInfo); + try { + pMC->SubBlock(oMCI, oVars, true, 0, *_pcoCmd, nPArgs + 2); + } + catch (CReturnException oRX) { + oVars[std::string("$RETURN")] = oRX.m_oVal; + if (g_poStepOut == &oMCI) + CMetaCommand::SetTrace(2); + } +#ifdef ROCK_SOLID_CATCH + catch (...) { + char Buff[256]; + snprintf(Buff, sizeof(Buff), "Unerwartete Ausnahmebehandlung in Funktion '%s'!", sName.c_str()); + poVal->error(std::string(Buff).c_str()); + if (g_poStepOut == &oMCI) + CMetaCommand::SetTrace(2); + g_coCallStack.pop_back(); + return false; + } +#endif + g_coCallStack.pop_back(); + *poVal = oVars[std::string("$RETURN")]; + return true; + /* + for( int ih=0; iherror(std::string(Buff).c_str()); + return false; + } + } + return false; +} + +static std::string Quotionmarks(std::string& sText) +{ + std::string t; + char c; + for (size_t i = 0; i < sText.size(); i++) { + c = sText[i]; + switch (c) { + /* + case '\\': + i++; + if( i= sStr.size()) + return true; + return false; +} + +void CMetaCommand::Skip(CMCI& oMCI) +{ + if (oMCI.m_nTC > (int)Args()) + oMCI.m_sArg = ""; + else { + oMCI.m_nLine = m_coLocs[(size_t)oMCI.m_nTC].first; + oMCI.m_sArg = m_coArgs[(size_t)oMCI.m_nTC++]; + } +} + +bool CMetaCommand::Parse(CMCI& oMCI, Expression::Variables& oContext, VKommandos& coCmd, bool bAlone, bool bExpand) +{ + int help = 0; + _pcoCmd = &coCmd; + + if (oMCI.m_pvRef) + delete oMCI.m_pvRef; + oMCI.m_pvRef = 0; + oMCI.m_pVal = 0; + oMCI.m_vArg = Value(); + + if (oMCI.m_nTC > (int)Args()) + oMCI.m_sArg = ""; + else { + oMCI.m_nLine = m_coLocs[(size_t)oMCI.m_nTC].first; + oMCI.m_sOArg = m_coArgs[(size_t)oMCI.m_nTC++]; + oMCI.m_sArg = oMCI.m_sOArg; + if (IsVariable(oMCI.m_sArg)) { + Expression::Variables::iterator vi; + std::string sVName; + sVName = oMCI.m_sArg; + + vi = oContext.find(sVName); + if (vi != oContext.end()) { + oMCI.m_pvRef = new CReference((*vi).second); + oMCI.m_pVal = &(*vi).second; + if (bExpand) { + oMCI.m_sArg = oMCI.m_pvRef->asString(); + oMCI.m_vArg = oMCI.m_pvRef->self(); + } + } + else { + vi = Expression::globalContext().find(sVName); + if (vi != Expression::globalContext().end()) { + if (oMCI.m_pvRef) + delete oMCI.m_pvRef; + oMCI.m_pvRef = new CReference((*vi).second); + oMCI.m_pVal = &(*vi).second; + if (bExpand) { + oMCI.m_sArg = oMCI.m_pvRef->asString(); + oMCI.m_vArg = oMCI.m_pvRef->self(); + } + } + else { + // char Buff[512]; + // if( m_nLine>0 ) + // sprintf( Buff, "; %s(%d) : FEHLER: Unbekannte Variable '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, sVName.c_str() ); + // else + // sprintf( Buff, "; FEHLER: Unbekannte Variable '%s'!", sVName.c_str() ); + // coCmd.push_back( std::string(Buff) ); + if (bExpand) + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Unbekannte Variable '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, sVName.c_str())); + } + } + } + else if (!strchr("/#{}:", oMCI.m_sArg[0])) { + // Value oVal; + if (bExpand) { + Expression oExp(oMCI.m_sArg.c_str(), m_sSrcFile.c_str(), oMCI.m_nLine); + if (!oExp.evaluate(oContext, oMCI.m_sArg.c_str(), &oMCI.m_vArg, &help, m_bForceDeclare, m_bInplace)) { + if (oMCI.m_vArg.getType() == VT_ERROR) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Ausdruck ungueltig '%s': %s", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str(), oMCI.m_vArg.asString().c_str())); + m_bParseError = true; + } + else { + oMCI.m_sArg = oMCI.m_vArg.asString(); + if (oExp.getContainer()) + oMCI.m_pvRef = new CReference(*oExp.getContainer()); + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Ausdruck ungueltig '%s': %s", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str(), oMCI.m_vArg.asString().c_str())); + // coCmd.push_back( std::string(Buff) ); + // m_sArg = std::string("?") + m_sArg + std::string("?"); + m_bParseError = true; + return true; + } + } + else { + Value* pVal = Expression::getObjectReference(oContext, oMCI.m_sArg); + if (pVal) { + oMCI.m_pvRef = new CReference(*pVal); + oMCI.m_pVal = pVal; + } + } + } + } + if (help) { + if (bAlone) + return true; + // coCmd.push_back( std::string("; FEHLER: Zuweisung in Anweisung oder Ausdruck verwendet!" ) ); + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Zuweisung in Anweisung oder Ausdruck verwendet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + return false; + } + return false; +} + +static void TraceLinesFromFile(const std::string& sFileName, int32_t nLine, int32_t nNum = 1) +{ + std::fstream ifs; + std::istringstream iss; + std::istream* is = nullptr; + std::string sLine; + int32_t nLineNumber = 0; + + if (nLine == 0) { + CONMSG(("\n")); + return; + } + + if (g_pseudoFiles.count(sFileName)) { + iss.str(g_pseudoFiles[sFileName]); + is = &iss; + } + else { + ifs.open(sFileName.c_str(), ios::in); + if (ifs.fail()) { + CONMSG(("Auf die Datei '%s' kann nicht zugegriffen werden!\n", sFileName.c_str())); + return; + } + is = &ifs; + } + while (true) { + getline(*is, sLine); + if (is->fail()) { + CONMSG(("\n")); + break; + } + nLineNumber++; + if (nLineNumber >= nLine + nNum) + break; + if (nLineNumber >= nLine) + CONMSG(("%s\n", sLine.c_str())); + } +} + +void CMetaCommand::DumpContext(Expression::Variables& oContext) +{ + Expression::Variables::iterator vi; + + for (vi = oContext.begin(); vi != oContext.end(); vi++) { + if (!((*vi).first.empty())) { +#ifndef _DEBUG + if ((*vi).first[0] != '#') +#endif + { + DumpVariable((*vi).first, (*vi).second); + } + } + } +} + +void CMetaCommand::DumpVariable(const std::string& sName, const Value& oVal, bool bExtendedOutput) +{ + const char* pcType; + switch (oVal.getType()) { + case VT_INT: + pcType = "int"; + break; + case VT_FLOAT: + pcType = "flt"; + break; + case VT_STRING: + pcType = "str"; + break; + case VT_ERROR: + pcType = "err"; + break; + case VT_REF: + pcType = "ref"; + break; + case VT_MAP: + pcType = "dic"; + break; + case VT_VECTOR: + pcType = "arr"; + break; + default: + pcType = "non"; + } + CONMSG(("%-16s (%s): %s\n", (sName[0] == '#' ? sName.substr(1).c_str() : sName.c_str()), pcType, oVal.asString().c_str())); +} + +COutputTable g_oOT; + +bool bFirstTrace = true; + +void CMetaCommand::QuicksortHelper1(CMCI& oMCI, VKommandos& coCmd, const std::string& sArrayName, Value* pVector, int32_t l, int32_t r, const std::string& sCmpFunc, const ArgumentList& oArgs) +{ + ArgumentList::const_iterator ai; + int32_t i, j; + if (r > l) { + i = l - 1; + j = r; + for (;;) { + if (sCmpFunc.empty()) { + while (pVector->getAt(Value(++i)) < pVector->getAt(Value(r))) + ; + while (pVector->getAt(Value(--j)) > pVector->getAt(Value(r))) + ; + } + else { + ArgumentList coArgs; + Value oVErg; + do { + coArgs.clear(); + coArgs.push_back(Value(sArrayName)); + coArgs.push_back(Value(++i)); + coArgs.push_back(Value(r)); + for (ai = oArgs.begin(); ai != oArgs.end(); ai++) + coArgs.push_back(*ai); + if (i >= r || i < 0) + break; + if (!DoUserFunction(sCmpFunc, coArgs, &oVErg)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Die kleiner-als-Funktion '#func %s $arrayname $i1 $i2' fuer #sort wurde nicht gefunden!", m_sSrcFile.c_str(), oMCI.m_nLine, sCmpFunc.c_str())); + return; + } + } while (i < r && oVErg.asLong()); + do { + coArgs.clear(); + coArgs.push_back(Value(sArrayName)); + coArgs.push_back(Value(r)); + coArgs.push_back(Value(--j)); + for (ai = oArgs.begin(); ai != oArgs.end(); ai++) + coArgs.push_back(*ai); + if (r >= j || r < 0) + break; + if (!DoUserFunction(sCmpFunc, coArgs, &oVErg)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Die kleiner-als-Funktion '#func %s $arrayname $i1 $i2' fuer #sort wurde nicht gefunden!", m_sSrcFile.c_str(), oMCI.m_nLine, sCmpFunc.c_str())); + return; + } + } while (j > r && oVErg.asLong()); + + // while( pVector->getAt( Value( (int32_t)++i ) ) < pVector->getAt( Value( (int32_t)r ) ) ) ; + // while( pVector->getAt( Value( (int32_t)--j ) ) > pVector->getAt( Value( (int32_t)r ) ) ) ; + } + if (i >= j) + break; + pVector->getAt(Value(i)).swap(pVector->getAt(Value(j))); + } + pVector->getAt(Value(i)).swap(pVector->getAt(Value(r))); + QuicksortHelper1(oMCI, coCmd, sArrayName, pVector, l, i - 1, sCmpFunc, oArgs); + QuicksortHelper1(oMCI, coCmd, sArrayName, pVector, i + 1, r, sCmpFunc, oArgs); + } +} + +int32_t CMetaCommand::Partition(CMCI& oMCI, VKommandos& coCmd, const std::string& sArrayName, Value* pVector, int32_t l, int32_t r, const std::string& sCmpFunc, const ArgumentList& oArgs) +{ + // Partition(A,l,r) + ArgumentList::const_iterator ai; + int32_t j, k, p; + + j = l - 1; + k = r + 1; + p = l; + while (true) { + if (sCmpFunc.empty()) { + do + j++; + while (pVector->getAt(Value(j)) < pVector->getAt(Value(p))); + do + k--; + while (pVector->getAt(Value(k)) > pVector->getAt(Value(p))); + } + else { + ArgumentList coArgs; + Value oVErg; + + do { + j++; + if (j != p) { + coArgs.clear(); + coArgs.push_back(Value(sArrayName)); + coArgs.push_back(Value(j)); + coArgs.push_back(Value(p)); + for (ai = oArgs.begin(); ai != oArgs.end(); ai++) + coArgs.push_back(*ai); + if (!DoUserFunction(sCmpFunc, coArgs, &oVErg)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Die kleiner-als-Funktion '#func %s $arrayname $i1 $i2' fuer #sort wurde nicht gefunden!", m_sSrcFile.c_str(), oMCI.m_nLine, sCmpFunc.c_str())); + return -1; + } + } + else + oVErg = 0; + } while (oVErg.asLong()); // pVector->getAt( Value( j ) ) < pVector->getAt( Value( l ) ) ); + + do { + k--; + if (k != p) { + coArgs.clear(); + coArgs.push_back(Value(sArrayName)); + coArgs.push_back(Value(p)); + coArgs.push_back(Value(k)); + for (ai = oArgs.begin(); ai != oArgs.end(); ai++) + coArgs.push_back(*ai); + if (!DoUserFunction(sCmpFunc, coArgs, &oVErg)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Die kleiner-als-Funktion '#func %s $arrayname $i1 $i2' fuer #sort wurde nicht gefunden!", m_sSrcFile.c_str(), oMCI.m_nLine, sCmpFunc.c_str())); + return -1; + } + } + else + oVErg = 0; + } while (oVErg.asLong()); + // do k--; while( pVector->getAt( Value( k ) ) > pVector->getAt( Value( l ) ) ); + } + if (j < k) { + if (p == j) + p = k; + else if (p == k) + p = j; + pVector->getAt(Value(j)).swap(pVector->getAt(Value(k))); + } + else + return k; + } +} + +void CMetaCommand::QuicksortHelper2(CMCI& oMCI, VKommandos& coCmd, const std::string& sArrayName, Value* pVector, int32_t l, int32_t r, const std::string& sCmpFunc, const ArgumentList& oArgs) +{ + // Quicksort(A,l,r) + int32_t k; + + if (l < r) { + k = Partition(oMCI, coCmd, sArrayName, pVector, l, r, sCmpFunc, oArgs); + if (k >= 0) { + QuicksortHelper2(oMCI, coCmd, sArrayName, pVector, l, k, sCmpFunc, oArgs); + QuicksortHelper2(oMCI, coCmd, sArrayName, pVector, k + 1, r, sCmpFunc, oArgs); + } + } +} + +void CMetaCommand::RunScript(CMCI& oMCI, Expression::Variables& oContext, std::string* psCom, VKommandos& coCmd, int nIdx) +{ + /* + if( !psCom ) + { + coCmd.push_back( std::string("; FATAL ERROR in script!") ); + return; + } + */ + oMCI.m_nTC = nIdx; + if (!oMCI.m_nTC && (*this)[(size_t)oMCI.m_nTC] == "//") + oMCI.m_nTC++; + + do { + do { + if (g_coBreakConditions.size() && !g_nBreakCondition) { + Expression oExp("1+1", "", 0); + Value oVal; + int h; + for (size_t i = 0; i < g_coBreakConditions.size(); i++) { + if (!oExp.evaluate(oContext, g_coBreakConditions[i].c_str(), &oVal, &h, false)) { + if (!(!oVal)) { + CMetaCommand::SetTrace(2); + g_nBreakCondition = (int)(i + 1); + break; + } + } + } + } + + if (g_nLimitRuntime) { + if (!g_nStartTime) { + g_nStartTime = time(NULL); + } + static int divider = 0; + if (divider++ > 100) { + divider = 0; + if (time(NULL) > g_nStartTime + g_nLimitRuntime) { + throw CTimeoutException(int32_t(time(NULL) - g_nStartTime)); + } + } + } + + if (g_nTrace && !IsFlag(VF_NOCONSOLE) && (!g_poStepOver || g_poStepOver == &oMCI)) { + if (g_nTraceSteps) { + if (bFirstTrace) { + CONMSG(("\n----------------------------------------\n")); + bFirstTrace = false; + } + else + CONMSG(("----------------------------------------\n")); + CONMSG(("File: %s(%d)\n", m_sSrcFile.c_str(), m_coLocs[(size_t)oMCI.m_nTC].first)); + TraceLinesFromFile(m_sSrcFile, m_coLocs[(size_t)oMCI.m_nTC].first); + if (g_nBreakCondition) { + CONMSG(("Ausnahme duch Abbruchbedingung %d: %s\n", g_nBreakCondition, g_coBreakConditions[(size_t)g_nBreakCondition - 1].c_str())); + g_nBreakCondition = 0; + } + DumpContext(oContext); + } + if (g_nTraceSteps > 0) { + if (!(--g_nTraceSteps)) { + g_nTraceSteps = -1; + g_nTrace = 2; + } + } + if (g_nTrace == 2) { + std::vector coArgs; + // char Buff[128]; + + g_nTraceSteps = 0; + g_poStepOver = 0; + g_poStepOut = 0; + while (true) { + fprintf(stderr, "> "); + fflush(stderr); + auto nStart = clock(); + std::string sInput = InputFromConsole(); + g_nTimeCorrection += clock() - nStart; + coArgs.clear(); + // std::string sInput( Buff ); + CRegExp oRE; + int p = 0; + // oRE.Prepare( "([^ \\t\\n\\r\\f']|'([^']|\\')+')+" ); + // oRE.Prepare( "([^ \\t\\n\\r\\f']|'([^'\\\\]|\\\\')*')+" ); + oRE.Prepare("([^ \\t\\n\\r\\f']+|'([^'\\\\]|\\\\.)*')+"); + while (oRE.Find(sInput, p)) { + coArgs.push_back(sInput.substr((size_t)oRE.Begin(), (size_t)oRE.Size())); + // printf( "%s\n", sInput.substr( oRE.Begin(), oRE.Size() ).c_str() ); + p = oRE.End() + 1; + } + + // split( coArgs, std::string( Buff ), std::string( " \t" ), std::string( "'" ), '\\' ); + + if (coArgs.size()) { + if (IsEqual(coArgs[0].c_str(), "b")) { + if (coArgs.size() == 1) { + for (size_t i = 0; i < g_coBreakConditions.size(); i++) { + CONMSG(("%2d: %s\n", i + 1, g_coBreakConditions[i].c_str())); + } + } + else if (coArgs.size() == 2) { + g_coBreakConditions.push_back(coArgs[1]); + CONMSG(("Als %i. Abbruchbedingung gesetzt.\n", g_coBreakConditions.size())); + } + else { + CONMSG(("Zu viele Parameter!\n")); + } + } + else if (IsEqual(coArgs[0].c_str(), "bd")) { + if (coArgs.size() == 2) { + int id = atoi(coArgs[1].c_str()) - 1; + if (id < 0 || size_t(id) > g_coBreakConditions.size()) { + CONMSG(("Ungueltige Nummer fuer Abbruchbedingung!\n")); + } + else { + g_coBreakConditions.erase(g_coBreakConditions.begin() + id); + CONMSG(("Abbruchbedingung aus Liste geloescht.\n")); + } + } + else { + CONMSG(("Falsche Parameterzahl!\n")); + } + } + else if (IsEqual(coArgs[0].c_str(), "e")) { + Expression oExp(std::string("3+3"), "", 0); + Value oVal; + int help; + int32_t nTrace = g_nTrace; + int32_t nTraceSteps = g_nTraceSteps; + + g_nTrace = 0; + g_nTraceSteps = -1; + + if (coArgs.size() >= 2) { + if (!oExp.evaluate(oContext, coArgs[1].c_str(), &oVal, &help, false)) + DumpVariable("Ergebnis", oVal); + else { + CONMSG(("Fehler in Ausdruck: %s\n", oVal.asString().c_str())); + } + } + else { + CONMSG(("Ausdruck fehlt!\n")); + } + g_nTrace = nTrace; + g_nTraceSteps = nTraceSteps; + } + else if (IsEqual(coArgs[0].c_str(), "g")) { + DumpContext(Expression::globalContext()); + break; + } +#ifdef _DEBUG + else if (IsEqual(coArgs[0].c_str(), "i")) { + CONMSG(("Anzahl instanziierter Values: %d\n", Value::ValueCount())); + break; + } + else if (IsEqual(coArgs[0].c_str(), "cvp")) { + Value::ResetPool(); + CONMSG(("Valuepool zurueckgesetzt.\n")); + break; + } + else if (IsEqual(coArgs[0].c_str(), "dvp")) { + CONMSG(("Valuepool seit letztem Reset:\n")); + Value::DumpPool(); + break; + } +#endif + else if (IsEqual(coArgs[0].c_str(), "l")) { + g_nTrace = 0; + g_nTraceSteps = -1; + g_poStepOut = &oMCI; + break; + } + else if (IsEqual(coArgs[0].c_str(), "q")) { + g_nTrace = 0; + g_nTraceSteps = -1; + break; + } + else if (IsEqual(coArgs[0].c_str(), "q!")) { + g_nTrace = 0; + g_nTraceSteps = -1; + g_bNoMoreBreaks = true; + break; + } + else if (IsEqual(coArgs[0].c_str(), "r")) { + g_nTrace = 1; + g_nTraceSteps = -1; + break; + } + else if (IsEqual(coArgs[0].c_str(), "v")) { + DumpContext(oContext); + } + else if (IsEqual(coArgs[0].c_str(), "s")) { + if (coArgs.size() == 2) { + g_nTraceSteps = atoi(coArgs[1].c_str()); + } + if (g_nTraceSteps <= 0) + g_nTraceSteps = 1; + + g_nTrace = 1; + break; + } + else if (IsEqual(coArgs[0].c_str(), "n")) { + if (coArgs.size() == 2) { + g_nTraceSteps = atoi(coArgs[1].c_str()); + } + if (g_nTraceSteps <= 0) + g_nTraceSteps = 1; + + g_nTrace = 1; + g_poStepOver = &oMCI; + break; + } + else if (IsEqual(coArgs[0].c_str(), "w")) { + if (g_coCallStack.size()) { + for (size_t i = 0; i < g_coCallStack.size(); i++) { + CONMSG(("%d: %s\n", i, g_coCallStack[g_coCallStack.size() - i - 1].c_str())); + } + } + } + else if (!strcmp(sInput.c_str(), "?") || IsEqual(sInput, "hilfe") || IsEqual(sInput, "help")) { + CONMSG(("Metaskript-Debugger Befehlsliste:\n")); + CONMSG(("---------------------------------\n")); + CONMSG(("b [exp] Abbruchbedingungen anzeigen oder setzen\n")); + CONMSG(("bd n Abbruchbedingung loeschen\n")); + CONMSG(("e exp Ausdruck auswerten und Ergebnis anzeigen\n")); + CONMSG(("g Globals-Information auflisten\n")); +#ifdef _DEBUG + CONMSG(("i Interne Informationen\n")); + CONMSG(("cvp Interne Informationen\n")); + CONMSG(("dvp Interne Informationen\n")); +#endif + CONMSG(("l Ohne Trace bis zum Verlassen des Kontextes fortfahren\n")); + CONMSG(("n [n] Einen (bzw. ) Schritt(e) ausfuehren, Aufrufe ueberspringen\n")); + CONMSG(("q Debugger beenden und ohne Trace fortfahren\n")); + CONMSG(("q! Debugger beenden und weitere #trace ignorieren\n")); + CONMSG(("r Ausfuehrung mit Trace fortsetzen\n")); + CONMSG(("s [n] Einen (bzw. ) Schritt(e) in der Ausfuehrung fortsetzen\n")); + CONMSG(("v Ausgeben des lokalen Kontextes\n")); + CONMSG(("w Ausgeben des Call-Stacks\n")); + } + else if (!sInput.empty()) { + CONMSG(("Unbekannter Befehl! (?=Hilfe)\n")); + } + } + } + } + } + } while (!g_nTraceSteps); + + g_nStepCount++; + + if (Parse(oMCI, oContext, coCmd, true)) { + // Eine einfach Zuweisung hat stattgefunden + Parse(oMCI, oContext, coCmd); + } + else { + std::map::const_iterator mti = g_cnMetaTokens.find(oMCI.m_sArg); + if (mti != g_cnMetaTokens.end()) { + switch ((*mti).second) { + case GMT_forever: { + SubBlock(oMCI, oContext, true, psCom, coCmd); + } break; + case GMT_after: { + int n; + if (oMCI.m_nTC <= (int)Args()) { + CReference* pRef; + char Buff[16]; + size_t nPos = (size_t)oMCI.m_nTC; + Parse(oMCI, oContext, coCmd); + n = atoi(oMCI.m_sArg.c_str()); + pRef = oMCI.m_pvRef; + oMCI.m_pvRef = 0; + if (--n == 0) { + coCmd.push_back(std::string("; INFO: #after ist abgelaufen!")); + SubBlock(oMCI, oContext, true, psCom, coCmd); + // (*this)[m_nTC] = ";"; + } + else { + SubBlock(oMCI, oContext, false, psCom, coCmd); + } + if (pRef) { + pRef->self() = Value((int32_t)(n < 0 ? 0 : n)); + delete pRef; + } + else { + if (psCom) { + snprintf(Buff, sizeof(Buff), "%d", n < 0 ? 0 : n); + (*this)[nPos] = Buff; + if (n <= 0 && nPos >= 2) + (*this)[nPos - 2] = ";"; + *psCom = AsString(); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: In Prozeduren sind fuer #after nur Variable erlaubt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + // coCmd.push_back( std::string("; FEHLER: In Prozeduren sind fuer #after nur Variable erlaubt!") ); + } + } + } + } break; + case GMT_next: { + int n; + if (oMCI.m_nTC <= (int)Args()) { + CReference* pRef; + char Buff[16]; + size_t nPos = (size_t)oMCI.m_nTC; + Parse(oMCI, oContext, coCmd); + n = atoi(oMCI.m_sArg.c_str()); + pRef = oMCI.m_pvRef; + oMCI.m_pvRef = 0; + if (n > 0) { + n--; + SubBlock(oMCI, oContext, true, psCom, coCmd); + } + else { + SubBlock(oMCI, oContext, false, psCom, coCmd); + coCmd.push_back(std::string("; INFO: #next ist abgelaufen!")); + // (*this)[0] = ";"; + } + if (pRef) { + pRef->self() = Value(int32_t(n)); + delete pRef; + } + else { + if (psCom) { + snprintf(Buff, sizeof(Buff), "%d", n); + (*this)[nPos] = Buff; + *psCom = AsString(); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: In Prozeduren sind fuer #next nur Variable erlaubt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + // coCmd.push_back( std::string("; FEHLER: In Prozeduren sind fuer #next nur Variable erlaubt!") ); + } + } + } + } break; + case GMT_every: { + int n, m; + if (oMCI.m_nTC <= (int)Args()) { + CReference* pRef; + char Buff[16]; + size_t nPos; + Parse(oMCI, oContext, coCmd); + m = atoi(oMCI.m_sArg.c_str()); + nPos = (size_t)oMCI.m_nTC; + Parse(oMCI, oContext, coCmd); + n = atoi(oMCI.m_sArg.c_str()); + pRef = oMCI.m_pvRef; + oMCI.m_pvRef = 0; + if (--n == 0) { + n = m; + SubBlock(oMCI, oContext, true, psCom, coCmd); + } + else { + SubBlock(oMCI, oContext, false, psCom, coCmd); + } + if (pRef) { + pRef->self() = Value(int32_t(n)); + delete pRef; + } + else { + if (psCom) { + snprintf(Buff, sizeof(Buff), "%d", n); + (*this)[nPos] = Buff; + *psCom = AsString(); + } + else { + // coCmd.push_back( std::string("; FEHLER: In Prozeduren sind fuer #every nur Variable erlaubt!") ); + ERRMSG(&coCmd, ("%s(%d) : FEHLER: In Prozeduren sind fuer #every nur Variable erlaubt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + } + } break; + case GMT_ifunit: { + int32_t en, ei; + + if (oMCI.m_nTC <= (int)Args()) { + Parse(oMCI, oContext, coCmd); + + en = EinheitenNummer(oMCI.m_sArg); + + for (ei = 0; ei < (int32_t)g_poCurrentRegion->GetVEinheiten().size(); ei++) { + if (en == g_poCurrentRegion->GetVEinheiten()[(size_t)ei]->m_nNummer) + break; + } + + SubBlock(oMCI, oContext, ei < (int32_t)g_poCurrentRegion->GetVEinheiten().size(), psCom, coCmd); + if (oMCI.m_sArg == "else" || oMCI.m_sArg == "#else") { + SubBlock(oMCI, oContext, !(ei < (int32_t)g_poCurrentRegion->GetVEinheiten().size()), psCom, coCmd); + } + } + } break; + case GMT_ifregion: { + if (oMCI.m_nTC <= (int)Args()) { + bool bVal; + Parse(oMCI, oContext, coCmd); + if (g_poCurrentRegion) { + bVal = (oMCI.m_sArg == g_poCurrentRegion->GetName()); + } + else { + bVal = false; + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #ifregion ausserhalb eines Regionskontextes benutzt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + if ((*this)[(size_t)oMCI.m_nTC] == "{") { + SubBlock(oMCI, oContext, bVal, psCom, coCmd); + if (oMCI.m_sArg == "else" || oMCI.m_sArg == "#else") { + SubBlock(oMCI, oContext, !bVal, psCom, coCmd); + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: '{' zu Beginn eines Komandoblocks erwartet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + } break; + case GMT_if: { + if (oMCI.m_nTC <= (int)Args()) { + bool bVal; + Parse(oMCI, oContext, coCmd); + bVal = !(!oMCI.m_vArg); //(atoi( oMCI.m_sArg.c_str() )!=0); + if ((*this)[(size_t)oMCI.m_nTC] == "{") { + SubBlock(oMCI, oContext, bVal, psCom, coCmd); + if (oMCI.m_sArg == "else" || oMCI.m_sArg == "#else") { + SubBlock(oMCI, oContext, !bVal, psCom, coCmd); + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: '{' zu Beginn eines Komandoblocks erwartet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + } break; + case GMT_while: { + if (psCom) { + // coCmd.push_back( std::string("; FEHLER: #while ist nur in Prozeduren erlaubt!") ); + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #while ist nur in Prozeduren erlaubt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + if (oMCI.m_nTC <= (int)Args()) { + int nLoopPos = oMCI.m_nTC; + bool bVal; + oMCI.m_nLoop++; + do { + oMCI.m_nTC = nLoopPos; + Parse(oMCI, oContext, coCmd); + bVal = (atoi(oMCI.m_sArg.c_str()) != 0); + try { + SubBlock(oMCI, oContext, bVal, psCom, coCmd); + } + catch (CBreakException oBX) { + oMCI.m_nTC = nLoopPos; + Parse(oMCI, oContext, coCmd); + SubBlock(oMCI, oContext, false, psCom, coCmd); + bVal = false; + } + catch (CContinueException oCX) { + bVal = true; + } + } while (bVal); + oMCI.m_nLoop--; + } + } break; + case GMT_break: { + if (!oMCI.m_nLoop) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #break ausserhalb von Schleifen benutzt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + Parse(oMCI, oContext, coCmd); + } + else { + throw CBreakException(); + } + } break; + case GMT_continue: { + if (!oMCI.m_nLoop) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #continue ausserhalb von Schleifen benutzt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + Parse(oMCI, oContext, coCmd); + } + else { + throw CContinueException(); + } + } break; + case GMT_assert: { + if (oMCI.m_nTC <= (int)Args()) { + bool bVal; + Parse(oMCI, oContext, coCmd); + bVal = !(!oMCI.m_vArg); + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd); + if (!bVal) { + ERRMSG(&coCmd, ("%s(%d) : #assert fehlgeschlagen: %s", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + } + else { + if (!bVal) { + ERRMSG(&coCmd, ("%s(%d) : #assert fehlgeschlagen!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + Parse(oMCI, oContext, coCmd); + } + } break; + case GMT_error: { + if (oMCI.m_nTC <= (int)Args()) { + Parse(oMCI, oContext, coCmd); + ERRMSG(&coCmd, ("%s(%d) : FEHLER: %s", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Keine Fehlermeldung fuer #error!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } break; + case GMT_warning: { + if (oMCI.m_nTC <= (int)Args()) { + Parse(oMCI, oContext, coCmd); + ERRMSG(&coCmd, ("%s(%d) : Warnung: %s", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Keine Fehlermeldung fuer #warning!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } break; + case GMT_message: { + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd); + coCmd.push_back(std::string("; " + oMCI.m_sArg)); + } + else { + coCmd.push_back(std::string("")); + } + Parse(oMCI, oContext, coCmd); + } break; + case GMT_debug: { + if (oMCI.m_nTC <= (int)Args()) { + Parse(oMCI, oContext, coCmd); + if (IsEqual(oMCI.m_sArg, "progress")) { + Parse(oMCI, oContext, coCmd); + if (IsFlag(VF_PROGRESSINFO)) { + g_bForceEOL = true; + COutput::TPrintf("console", "\r%s", oMCI.m_sArg.c_str()); + } + } + else { + if (IsFlag(VF_DEBUGMODE)) + COutput::TPrintf("debug", "%s\n", oMCI.m_sArg.c_str()); + } + Parse(oMCI, oContext, coCmd); + } + } break; + case GMT_input: { + if (IsFlag(VF_RESTRICTED)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #input ist im Restricted-Modus deaktiviert!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + std::string sVarName; + Value* pVal = 0; + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd); + CONMSG(("\n%s\n", oMCI.m_sArg.c_str())); + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd, true, false); + sVarName = oMCI.m_sArg.c_str(); + pVal = oMCI.m_pVal; + } + } + else { + if (IsFlag(VF_PROGRESSINFO)) { + g_bForceEOL = true; + CONMSG(("\r[Weiter mit der Eingabetaste]")); + } + else { + CONMSG(("\n[Weiter mit der Eingabetaste]\n")); + } + } + auto nStart = clock(); + std::string sInput; + if (!IsFlag(VF_NOCONSOLE)) + sInput = InputFromConsole(); + g_nTimeCorrection += clock() - nStart; + if (!sVarName.empty()) { + Value oErg(sInput); + if (pVal) { + *pVal = oErg; + } + else if (m_bForceDeclare || !Expression::setValue(oContext, sVarName.c_str(), &oErg, m_bForceDeclare)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Undefinierte Variable '%s' als Ziel fuer #input verwendet!", m_sSrcFile.c_str(), oMCI.m_nLine, sVarName.c_str())); + } + } + if (IsFlag(VF_NOCONSOLE)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #input bei umgeleiteten Ausgabekanaelen verwendet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + Parse(oMCI, oContext, coCmd); + } break; + case GMT_table: { + if (oMCI.m_nTC <= (int)Args()) { + Parse(oMCI, oContext, coCmd); + if (IsEqual(oMCI.m_sArg, "CLEAR")) + g_oOT.Clear(); + else if (IsEqual(oMCI.m_sArg, "DUMP")) { + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd); + FileInfo* pfi = GetFileInfo(oMCI.m_vArg.asLong()); + if (!pfi || !pfi->hFile) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Ungueltiges Datei-Handle fuer #table DUMP verwendet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + else { + g_oOT.Output(pfi->hFile); + } + } + else { + g_oOT.Output(coCmd, "; "); + } + g_oOT.Clear(); + } + else if (IsEqual(oMCI.m_sArg, "DEBUG")) { + if (IsFlag(VF_DEBUGMODE)) + g_oOT.Output("debug", ""); + g_oOT.Clear(); + } + else if (IsEqual(oMCI.m_sArg, "NEXT")) + g_oOT.Next(); + else { + COutputTable::FORMAT enFormat = COutputTable::enLEFT; + bool bHaveFormat = false; + if (IsEqual(oMCI.m_sArg, "LEFT")) { + bHaveFormat = true; + enFormat = COutputTable::enLEFT; + } + else if (IsEqual(oMCI.m_sArg, "RIGHT")) { + bHaveFormat = true; + enFormat = COutputTable::enRIGHT; + } + else if (IsEqual(oMCI.m_sArg, "CENTER")) { + bHaveFormat = true; + enFormat = COutputTable::enCENTER; + } + if (bHaveFormat) { + Parse(oMCI, oContext, coCmd); + } + switch (oMCI.m_vArg.getType()) { + case VT_INT: + if (bHaveFormat) + g_oOT.Col(oMCI.m_vArg.asLong(), enFormat); + else + g_oOT.Col(oMCI.m_vArg.asLong()); + break; + case VT_FLOAT: + if (bHaveFormat) + g_oOT.Col(oMCI.m_vArg.asReal(), 2, enFormat); + else + g_oOT.Col(oMCI.m_vArg.asReal()); + break; + case VT_STRING: + if (bHaveFormat) + g_oOT.Col(oMCI.m_vArg.asString(), enFormat); + else + g_oOT.Col(oMCI.m_vArg.asString()); + break; + default: + g_oOT.Col("???", enFormat); + } + } + + // coCmd.push_back( std::string( "; " + oMCI.m_sArg ) ); + Parse(oMCI, oContext, coCmd); + } + } break; + case GMT_config: { + if (oMCI.m_nTC <= (int)Args()) { + CBlockBase::CFGOBJMODE enMode(CBlockBase::enTABH); + std::vector coVNames; + std::string sFile, sMode, sCaption; + bool bMode = true; + + Parse(oMCI, oContext, coCmd); + sCaption = oMCI.m_sArg; + + Parse(oMCI, oContext, coCmd); + sMode = oMCI.m_sArg; + if (IsEqual(sMode, "FILE")) { + Parse(oMCI, oContext, coCmd); + sFile = oMCI.m_sArg; + if (IsFlag(VF_RESTRICTED) && sFile.find_first_of("/\\:") != std::string::npos) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #config darf im Restricted-Modus nur ohne Pfad benutzt werden!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + + Parse(oMCI, oContext, coCmd); + sMode = oMCI.m_sArg; + } + + if (IsEqual(sMode, "TABH")) { + enMode = CBlockBase::enTABH; + } + else if (IsEqual(sMode, "TABV")) { + enMode = CBlockBase::enTABV; + } + else if (IsEqual(sMode, "NESTED")) { + enMode = CBlockBase::enNEST; + } + else { + bMode = false; + } + + while (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd); + coVNames.push_back(oMCI.m_sArg); + } + + Parse(oMCI, oContext, coCmd); + + if (bMode && !sCaption.empty() && (enMode == CBlockBase::enTABH || coVNames.empty())) { + g_sSrcFile = m_sSrcFile; + g_nSrcLine = oMCI.m_nLine; + CBlockBase::ReadConfigObjects(sFile, sCaption, enMode, coVNames); + g_nSrcLine = -1; + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Falsche Parameter fuer #config [FILE ] [] benutzt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + } break; + case GMT_call: { + if (oMCI.m_nTC <= (int)Args()) { + std::vector coRefs; + Expression::Variables oVars; + CMetaCommand* pMC; + std::string sPName; + std::string sAName; + std::string sStackInfo; + int nPArgs = 0, argi = 1; + size_t nPos; + size_t nRealArgs = 0; + bool bVArg = false; + bool bWasFunc = false; + bool invalidArgs = false; + + Parse(oMCI, oContext, coCmd); + sPName = oMCI.m_sArg; + sStackInfo = std::string("#proc ") + sPName; + pMC = g_oScriptBase.FindProc(sPName); + if (pMC) { + if (pMC->IsFunction()) { + pMC = 0; + bWasFunc = true; + } + else { + nPArgs = atoi((*pMC)[0].c_str()); + if (nPArgs < 0) { + nPArgs = -nPArgs; + bVArg = true; + } + nPArgs--; + } + } + + nPos = (size_t)oMCI.m_nTC; + + while (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + argi++; + if (Parse(oMCI, oContext, coCmd)) { + invalidArgs = true; + } + nRealArgs++; + sStackInfo += std::string(" ") + oMCI.m_vArg.asString(); + oVars[std::string("#ARG") + ToString(int32_t(argi - 1))] = oMCI.m_vArg; + coRefs.push_back(oMCI.m_pvRef); + oMCI.m_pvRef = NULL; + if (pMC && argi - 1 <= nPArgs) { + sAName = (*pMC)[(size_t)argi]; + oVars[sAName[0] == '&' ? sAName.substr(1) : sAName] = oMCI.m_vArg; + oVars[std::string("#REF") + ToString(int32_t(argi - 1))] = (sAName[0] == '&' ? sAName.substr(1) : sAName); + } + else if (pMC) { + // char Buff[256]; + // sprintf( Buff, "; FEHLER: Ueberzaehliges Argument '%s' fuer Prozedur '%s'!", oMCI.m_sArg.c_str(), sPName.c_str() ); + // coCmd.push_back( std::string(Buff) ); + if (!bVArg) + ERRMSG(&coCmd, ("%s(%d) : Warnung: Ueberzaehliges Argument '%s' fuer Prozedur '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str(), sPName.c_str())); + } + } + oVars[std::string("#ARG0")] = Value(int32_t(argi - 1)); + oVars[std::string("#REF0")] = Value(int32_t(nPArgs)); + Parse(oMCI, oContext, coCmd); + if (pMC) { + if (argi - 1 < nPArgs) { + // char Buff[256]; + // sprintf( Buff, "; FEHLER: Zu wenig Argumente fuer Prozedur '%s'!", sPName.c_str() ); + // coCmd.push_back( std::string(Buff) ); + if (!bVArg) + ERRMSG(&coCmd, ("%s(%d) : Warnung: Zu wenig Argumente fuer Prozedur '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, sPName.c_str())); + while (argi - 1 < nPArgs) { + sAName = (*pMC)[(size_t)++argi]; + oVars[sAName[0] == '&' ? sAName.substr(1) : sAName] = Value(0); + coRefs.push_back(0); + } + } + // printf( " ; Call to procedure: %s\n", sPName.c_str() ); + // for( Expression::Variables::iterator evi = oVars.begin(); evi != oVars.end(); evi++ ) + // { + // printf( " ; %s = %s\n", (*evi).first.c_str(), (*evi).second.asString().c_str() ); + // } + // CALL + // int nOldTC = oMCI.m_nTC; // Um Rekursionen zu erm�glichen den TC retten + CMCI oNMCI(nPArgs + 2); + + if (!invalidArgs) { + g_coCallStack.push_back(sStackInfo); + + try { + pMC->SubBlock(oNMCI, oVars, true, 0, coCmd, nPArgs + 2); + } + catch (CReturnException oRX) { + } +#ifdef ROCK_SOLID_CATCH + catch (...) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Unerwartete Ausnahmebehandlung in Prozedur '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, sPName.c_str())); + } +#endif + g_coCallStack.pop_back(); + } + if (g_poStepOut == &oNMCI) + CMetaCommand::SetTrace(2); + + // oMCI.m_nTC = nOldTC; + for (size_t ih = 0; ih < coRefs.size(); ih++) { + if ((int)ih < nPArgs && (*pMC)[ih + 2][0] == '&') { + Expression::Variables::iterator vi = oVars.find((*pMC)[ih + 2].substr(1)); + if (vi != oVars.end()) { + if (coRefs[ih]) { + coRefs[ih]->self() = (*vi).second; + } + else { + if (psCom && ih < nRealArgs) { + if ((*vi).second.getType() == VT_STRING) + (*this)[nPos + ih] = std::string("'") + (*vi).second.asString() + "'"; + else + (*this)[nPos + ih] = (*vi).second.asString(); + } + } + } + } + else if ((int)ih >= nPArgs && pMC->m_bVRef) { + Expression::Variables::iterator vi = oVars.find(std::string("#ARG") + ToString(int32_t(ih + 1))); + if (vi != oVars.end()) { + if (coRefs[ih]) { + coRefs[ih]->self() = (*vi).second; + } + else { + if (psCom && ih < nRealArgs) { + if ((*vi).second.getType() == VT_STRING) + (*this)[nPos + ih] = std::string("'") + (*vi).second.asString() + "'"; + else + (*this)[nPos + ih] = (*vi).second.asString(); + } + } + } + } + + if (coRefs[ih]) + delete coRefs[ih]; + } + if (psCom) + *psCom = AsString(); + } + else { + // char Buff[256]; + // sprintf( Buff, "; FEHLER: Unbekannte Prozedur '%s'!", sPName.c_str() ); + // coCmd.push_back( std::string(Buff) ); + if (bWasFunc) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Funktion '#func %s' kann nicht mit #call aufgerufen werden!", m_sSrcFile.c_str(), oMCI.m_nLine, sPName.c_str())); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Unbekannte Prozedur '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, sPName.c_str())); + } + } + } + } break; + case GMT_return: { + if (!m_bIsFunc) { + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #return ausserhalb von Funktionen benutzt!", m_sSrcFile.c_str(), oMCI.m_nLine)); + Parse(oMCI, oContext, coCmd); + } + else { + if (psCom) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #return direkt im Zug verwendet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + else + throw CReturnException(Value()); + } + } + else { + Parse(oMCI, oContext, coCmd); + throw CReturnException(oMCI.m_vArg); + } + } break; + case GMT_default: { + if (g_poCurrentUnit) { + for (int32_t ki = 0; ki < (int)g_poCurrentUnit->m_csKommandos.size(); ki++) { + if (g_poCurrentUnit->m_csKommandos[ki].asString()[0] != '/' && g_poCurrentUnit->m_csKommandos[ki].asString()[0] != ';' && + (g_poCurrentUnit->m_csKommandos[ki].asString().size() < 8 || + (!IsEqual(g_poCurrentUnit->m_csKommandos[ki].asString().substr(0, 8), "reservie") && !IsEqual(g_poCurrentUnit->m_csKommandos[ki].asString().substr(0, 8), "kampfzau")))) { + coCmd.push_back(g_poCurrentUnit->m_csKommandos[ki]); + } + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #default ausserhalb eines Einheitenkontext verwendet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + Parse(oMCI, oContext, coCmd); + } break; + case GMT_sort: { + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Value* pVal; + Parse(oMCI, oContext, coCmd, true, false); + pVal = oMCI.m_pVal; + if (pVal && pVal->getType() == VT_VECTOR) { + std::string sArrayName(oMCI.m_sOArg); + ArgumentList oArgs; + std::string sFunc; + + if (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd); + sFunc = oMCI.m_sArg; + while (oMCI.m_nTC <= (int)Args() && (*this)[(size_t)oMCI.m_nTC] != ":" && (*this)[(size_t)oMCI.m_nTC] != "}") { + Parse(oMCI, oContext, coCmd); + oArgs.push_back(oMCI.m_vArg); + } + } + if (pVal) { + QuicksortHelper2(oMCI, coCmd, sArrayName, pVal, 0, pVal->size() - 1, sFunc, oArgs); + } + else { + ERRMSG(&coCmd, ("%s(%d) : #sort kann nur direkte #array-Behaelter sortieren, keine eingebetteten!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : #sort unterstuetzt nur Arrays!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + Parse(oMCI, oContext, coCmd); + } break; + case GMT_trace: { + if (IsFlag(VF_RESTRICTED)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #trace ist im Restricted-Modus deaktiviert!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + Parse(oMCI, oContext, coCmd); + if (!g_bNoMoreBreaks) + g_nTrace = atoi(oMCI.m_sArg.c_str()); + Parse(oMCI, oContext, coCmd); + } break; + case GMT_notrace: { + Parse(oMCI, oContext, coCmd); + g_nTrace = 0; + } break; + case GMT_tag: { + std::string sBlock; + std::string sTag; + Value oVal; + std::shared_ptr poOA; + + if (oMCI.m_nTC <= (int)Args()) { + oMCI.m_nLine = m_coLocs[(size_t)oMCI.m_nTC].first; + oMCI.m_sOArg = m_coArgs[(size_t)oMCI.m_nTC++]; + oMCI.m_sArg = oMCI.m_sOArg; + // Parse( oMCI, oContext, coCmd ); + sBlock = oMCI.m_sArg; + poOA = Expression::parseObjectAccess(sBlock, &oContext); + sBlock = poOA->label; + } + if (oMCI.m_nTC <= (int)Args()) { + Parse(oMCI, oContext, coCmd); + sTag = oMCI.m_sArg; + } + if (oMCI.m_nTC <= (int)Args()) { + Parse(oMCI, oContext, coCmd); + oVal = oMCI.m_vArg; + } + if (!sBlock.empty() && !sTag.empty() && oVal.getType() != VT_EMPTY) { + if (IsEqual(sBlock, "REGION")) { + if (poOA->index.empty() && g_poCurrentRegion) { + g_poCurrentRegion->SetValue(std::string("!") + sTag, oVal); + } + else if (poOA->index.size() == 2 && g_poCurrentReport) { + CRegion* pReg = g_poCurrentReport->GetMap()->GetFromECords(poOA->index[0].asLong(), poOA->index[1].asLong(), 0); + if (pReg) { + pReg->SetValue(std::string("!") + sTag, oVal); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #tag fuer nicht in CR enthaltenen REGION-Block aufgerufen", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + else if (poOA->index.size() == 3 && g_poCurrentReport) { + CRegion* pReg = g_poCurrentReport->GetMap()->GetFromECords(poOA->index[0].asLong(), poOA->index[1].asLong(), poOA->index[2].asLong(), 0); + if (pReg && pReg->Map()) { + pReg->SetValue(std::string("!") + sTag, oVal); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #tag fuer nicht in CR enthaltenen REGION-Block aufgerufen", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: REGION-Block fuer #tag falsch indiziert oder aus falschem Kontext aufgerufen", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + else if (IsEqual(sBlock, "EINHEIT")) { + if (g_poCurrentUnit && poOA->index.empty()) { + g_poCurrentUnit->SetValue(std::string("!") + sTag, oVal); + } + else if (g_poCurrentReport && poOA->index.size() == 1) { + CEinheit* pUnit = g_poCurrentReport->SearchUnit(EinheitenNummer(poOA->index[0].asString()), false); + if (pUnit) { + pUnit->SetValue(std::string("!") + sTag, oVal); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #tag fuer nicht in CR enthaltenen EINHEIT-Block aufgerufen", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: EINHEIT-Block fuer #tag falsch indiziert oder aus falschem Kontext aufgerufen", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + else if (IsEqual(sBlock, "BURG") && poOA->index.size() == 1) { + int32_t nBNr = (int32_t)strtol(poOA->index[0].asString().c_str(), 0, g_poCurrentReport->BNrBase()); + CBauwerk* poBuilding = g_poCurrentReport->GetBuilding(nBNr); + if (poBuilding) { + poBuilding->SetValue(std::string("!") + sTag, oVal); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #tag fuer nicht in CR enthaltenen BURG-Block aufgerufen", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + else if (IsEqual(sBlock, "SCHIFF") && poOA->index.size() == 1) { + int32_t nSNr = (int32_t)strtol(poOA->index[0].asString().c_str(), 0, g_poCurrentReport->BNrBase()); + CSchiff* poShip = g_poCurrentReport->GetShip(nSNr); + if (poShip) { + poShip->SetValue(std::string("!") + sTag, oVal); + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: #tag fuer nicht in CR enthaltenen SHIFF-Block aufgerufen", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + } + else { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: zu wenig Parameter fuer #tag ", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + Parse(oMCI, oContext, coCmd); + } break; + case GMT_var: { + Expression oExp("3+3", m_sSrcFile.c_str(), oMCI.m_nLine); + int bAssign; + + Parse(oMCI, oContext, coCmd, true, false); + while (oMCI.m_sArg != ":" && oMCI.m_sArg != "}") { + if (IsIdentifier(oMCI.m_sArg)) { + if (!Expression::getLocalRef(oContext, oMCI.m_sArg.c_str())) { + Value oVal; + Expression::setValue(oContext, oMCI.m_sArg.c_str(), &oVal, false); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: Im lokalen Kontext existiert bereits ein Bezeichner '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + } + else { + Value oVal; + if (oExp.evaluate(oContext, oMCI.m_sArg.c_str(), &oVal, &bAssign, false)) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Ausdruck ungueltig '%s': %s", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str(), oVal.asString().c_str())); + } + if (bAssign) { + } + } + Parse(oMCI, oContext, coCmd, true, false); + } + } break; + case GMT_array: { + // Value oVal; + + Parse(oMCI, oContext, coCmd, true, false); + while (oMCI.m_sArg != ":" && oMCI.m_sArg != "}") { + if (IsIdentifier(oMCI.m_sArg)) { + if (!Expression::getLocalRef(oContext, oMCI.m_sArg.c_str())) { + Value oVal(VT_VECTOR); + Expression::setValue(oContext, oMCI.m_sArg.c_str(), &oVal, false); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: Im lokalen Kontext existiert bereits ein Bezeichner '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: '%s' ist kein gueltiger Bezeichner!", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + Parse(oMCI, oContext, coCmd, true, false); + } + } break; + case GMT_dict: { + // Value oVal; + + Parse(oMCI, oContext, coCmd, true, false); + while (oMCI.m_sArg != ":" && oMCI.m_sArg != "}") { + if (IsIdentifier(oMCI.m_sArg)) { + if (!Expression::getLocalRef(oContext, oMCI.m_sArg.c_str())) { + Value oVal(VT_MAP); + Expression::setValue(oContext, oMCI.m_sArg.c_str(), &oVal, false); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: Im lokalen Kontext existiert bereits ein Bezeichner '%s'!", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: '%s' ist kein gueltiger Bezeichner!", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + Parse(oMCI, oContext, coCmd, true, false); + } + } break; + // default: + } + } + else if (oMCI.m_sArg[0] == '{') { + ERRMSG(0, ("%s(%d) : FEHLER: Unerwarteter Beginn eines Befehlsblocks!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + else if (oMCI.m_sArg[0] != '#') { + AddCommand(oMCI, oContext, coCmd); + } + else { + if (oMCI.m_sArg == "#proc" || oMCI.m_sArg == "#func" || oMCI.m_sArg == "#const" || oMCI.m_sArg == "#include") { + ERRMSG(0, ("%s(%d) : FEHLER: '%s' Ist nicht innerhalb von Bloecken erlaubt! Vermutlich fehlt davor ein schliessendes '}'.", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: '%s' ist kein gueltiger Befehl!", m_sSrcFile.c_str(), oMCI.m_nLine, oMCI.m_sArg.c_str())); + } + } + } + + if (!oMCI.m_sArg.empty() && oMCI.m_sArg != ":" && oMCI.m_sArg != "}") { + while (oMCI.m_nTC <= (int)Args() && oMCI.m_sArg != ":" && oMCI.m_sArg != "}") { + oMCI.m_nLine = m_coLocs[(size_t)oMCI.m_nTC].first; + oMCI.m_sArg = m_coArgs[(size_t)oMCI.m_nTC++]; + } + { + if (g_nStepCount != g_nLastErrorStep) { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Fehlendes ':' als Befehlstrenner oder ueberzaehlige Parameter!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + } + } + + if (oMCI.m_sArg == "}") { + if (!nIdx) + ERRMSG(&coCmd, ("%s(%d) : FEHLER: Unerwartetes '}' gefunden!", m_sSrcFile.c_str(), oMCI.m_nLine)); + return; + } + } while (!oMCI.m_sArg.empty()); +} + +void CMetaCommand::SubBlock(CMCI& oMCI, Expression::Variables& oContext, bool bCond, std::string* psCom, VKommandos& coCmd, int nIdx) +{ + if (nIdx >= 0) + oMCI.m_nTC = nIdx; + Parse(oMCI, oContext, coCmd); + if (oMCI.m_sArg != "{") { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: '{' zu Beginn eines Komandoblocks erwartet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + return; + } + + if (bCond) { + RunScript(oMCI, oContext, psCom, coCmd, oMCI.m_nTC); + } + else { + // VKommandos coDummy; + int nCnt = 1; + while (!oMCI.m_sArg.empty() && nCnt > 0) { + Skip(oMCI); + if (oMCI.m_sArg == "{") + nCnt++; + if (oMCI.m_sArg == "}") + nCnt--; + } + } + + if (oMCI.m_sArg != "}") { + ERRMSG(&coCmd, ("%s(%d) : FEHLER: '}' am Ende eines Komandoblocks erwartet!", m_sSrcFile.c_str(), oMCI.m_nLine)); + } + else { + Parse(oMCI, oContext, coCmd); + } +} + +bool CMetaCommand::ProcExists(const std::string& sProc) +{ + CMetaCommand* pMC; + pMC = g_oScriptBase.FindProc(sProc); + return (pMC && !pMC->IsFunction()); +} + +bool CMetaCommand::Call(const std::string& sProc, VKommandos& coCmd) +{ + // VKommandos coCmd; + Expression::Variables oVars; + CMetaCommand* pMC; + int nPArgs = 0; + + pMC = g_oScriptBase.FindProc(sProc); + if (pMC) { + CMCI oMCI(nPArgs + 2); + nPArgs = atoi((*pMC)[0].c_str()); + if (nPArgs < 0) + nPArgs = -nPArgs; + nPArgs--; + g_coCallStack.push_back(std::string("#proc ") + sProc); + try { + pMC->SubBlock(oMCI, oVars, true, 0, coCmd, nPArgs + 2); + } + catch (CReturnException oRX) { + } +#ifdef ROCK_SOLID_CATCH + catch (...) { + ERRMSG(&coCmd, ("KeinFile(0) : FEHLER: Unerwartete Ausnahmebehandlung in Prozedur '%s'!", sProc.c_str())); + } +#endif + g_coCallStack.pop_back(); + return true; + } + + return false; +} + +void CMetaCommand::AddCommand(CMCI& oMCI, Expression::Variables& oContext, VKommandos& coCmd, bool bMulti) +{ + std::string sCmd; + int nBC = 0; + // int n = nIdx; + + // Parse( oMCI, oContext, coCmd ); + // if( oMCI.m_sArg != "{" ) + // { + // coCmd.push_back( std::string("; FEHLER: '{' zu Beginn eines Komandoblocks erwartet!") ); + // return; + // } + + // Parse( oMCI, oContext, coCmd ); + m_bParseError = false; + for (; oMCI.m_nTC <= (int)Args(); Parse(oMCI, oContext, coCmd)) { + if (oMCI.m_sArg.empty() && oMCI.m_vArg.getType() != VT_STRING) + break; + if (oMCI.m_sArg == "{") + nBC++; + if (oMCI.m_sArg == "}") { + if (nBC <= 0) + break; + nBC--; + } + if (nBC <= 0 && oMCI.m_sArg == ":") + break; + if (oMCI.m_vArg.getType() == VT_STRING) + sCmd += Quotionmarks(oMCI.m_sArg); + else { + if (oMCI.m_vArg.getType() == VT_FLOAT) { + oMCI.m_vArg = Value(oMCI.m_vArg.asLong()); + sCmd += oMCI.m_vArg.asString(); + } + else + sCmd += oMCI.m_sArg; + } + sCmd += ' '; + } + + if (!m_bParseError) { + if (!sCmd.empty()) + coCmd.push_back(sCmd); + } + else { + if (!sCmd.empty()) + coCmd.push_back(std::string("; ") + sCmd); + } +} + +CScriptBase::CScriptBase() {} + +bool CScriptBase::Import(const std::string& sIFName, const std::string& sText) +{ + if (g_pseudoFiles.count(sIFName)) { + ERRMSG(0, ("Import of pseudo file with duplicate name '%s'!", sIFName.c_str())); + return false; + } + g_pseudoFiles[sIFName] = sText; + std::istringstream oIS(sText.c_str()); + return Import(oIS, sIFName); +} + +bool CScriptBase::Import(const std::string& sIFName) +{ + std::fstream oIS; + std::string sFileName = sIFName; + + sFileName = PathedFileName(sFileName); + oIS.open(sFileName.c_str(), ios::in); + + if (oIS.fail()) { + ERRMSG(0, ("Auf die Skript-Datei '%s' kann nicht zugegriffen werden!", sFileName.c_str())); + return false; + } + + return Import(oIS, sFileName); +} + +void CScriptBase::GetLine(std::istream& oIS, std::string& sBuff, int32_t& nLineCounter) +{ + std::string sLine; + size_t p; + sBuff = ""; + getline(oIS, sLine); + if (oIS.fail()) + return; + while (!sLine.empty() && sLine[sLine.size() - 1] <= 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + while (!sLine.empty() && sLine[sLine.size() - 1] == '\\') { + sLine.erase(sLine.size() - 1, 1); + sBuff += sLine; + sLine = ""; + nLineCounter++; + getline(oIS, sLine); + if (oIS.fail()) + break; + while (!sLine.empty() && sLine[sLine.size() - 1] <= 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + p = sLine.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sLine.size(); + } + sLine.erase(0, p); + } + sBuff += sLine; +} + +bool CScriptBase::Import(std::istream& oIS, const std::string& sFileName) +{ + std::string sLine; + int32_t nLineNumber = 0; + std::shared_ptr pMapper(new CharacterMapper("iso-8859-1", "iso-8859-1")); + Value oVal; + size_t p; + + while (1) { + GetLine(oIS, sLine, nLineNumber); + if (oIS.fail()) + break; + if (!nLineNumber && sLine.length() >= 3 && sLine.substr(0, 3) == "\xEF\xBB\xBF") { + pMapper.reset(new CharacterMapper("utf-8", "iso-8859-1")); + sLine.erase(0, 3); + } + + p = sLine.find_first_of(';'); + if (p != std::string::npos) { + sLine.erase(p); + } + while (!sLine.empty() && sLine[sLine.size() - 1] <= 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + nLineNumber++; + p = sLine.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sLine.size(); + } + sLine.erase(0, p); + if (IsFlag(VF_FIXENCODINGS)) { + sLine = mixed_utf8_latin1_to_latin1(sLine); + } + if (!sLine.empty()) { + sLine = pMapper->Map(sLine); + if (!strncmp("#encoding", sLine.c_str(), 5)) { + std::string sEnc = sLine.substr(9); + p = sEnc.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sEnc.size(); + } + sEnc.erase(0, p); + p = sEnc.find_last_not_of(" \t"); + if (p != std::string::npos) { + sEnc.erase(p + 1); + } + if (!CharacterMapper::IsSupported(sEnc) && !(IsEqual(sEnc, "utf-8") || IsEqual(sEnc, "utf8"))) { + ERRMSG(0, ("%s(%d) : FEHLER: Encoding '%s' wird nicht unterstuetzt!", sFileName.c_str(), nLineNumber, sEnc.c_str())); + } + else + pMapper.reset(new CharacterMapper(sEnc.c_str(), "iso-8859-1")); + } + else if (!strncmp("#proc", sLine.c_str(), 5)) { + nLineNumber += AddProc(pMapper.get(), sFileName, nLineNumber, oIS, sLine, false); + } + else if (!strncmp("#func", sLine.c_str(), 5)) { + nLineNumber += AddProc(pMapper.get(), sFileName, nLineNumber, oIS, sLine, true); + } + else if (!strncmp("#const", sLine.c_str(), 6)) { + std::vector coArgs; + CRegExp oRE; + int pp = 0; + oRE.Prepare("([^ \\t\\n\\r\\f']|'([^']|\\')+')+"); + while (oRE.Find(sLine, pp)) { + coArgs.push_back(sLine.substr((size_t)oRE.Begin(), (size_t)oRE.Size())); + pp = oRE.End() + 1; + } + + if (coArgs.size() != 3) { + ERRMSG(0, ("%s(%d) : FEHLER: Falsche Anzahl von Argumenten fuer '#const '!", sFileName.c_str(), nLineNumber)); + } + else { + if (IsIdentifier(coArgs[1], false)) { + Expression oExp(coArgs[2], sFileName.c_str(), nLineNumber); + Value oValt; + int dummy; + oExp.evaluate(Expression::globalContext(), coArgs[2].c_str(), &oValt, &dummy, false); + Expression::setConstant(coArgs[1].c_str(), &oValt); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: Ungueltiger Bezeichner '%s' fuer '#const '!", sFileName.c_str(), nLineNumber, coArgs[1].c_str())); + } + } + } + else if (!strncmp("#var", sLine.c_str(), 4)) { + std::string sName = sLine.substr(4); + do { + p = sName.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sName.size(); + } + sName.erase(0, p); + p = sName.find_first_of(" \t"); + if (p == std::string::npos) { + p = sName.size(); + } + if (IsIdentifier(sName.substr(0, p))) { + if (!Expression::getGlobalRef(sName.substr(0, p).c_str())) { + Expression::setGlobal(sName.substr(0, p).c_str(), &oVal, false); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: Im globalen Kontext existiert bereits ein Bezeichner '%s'!", sFileName.c_str(), nLineNumber, sName.substr(0, p).c_str())); + } + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: '%s' ist kein gueltiger Bezeichner!", sFileName.c_str(), nLineNumber, sName.substr(0, p).c_str())); + } + sName.erase(0, p); + } while (!sName.empty()); + } + else if (!strncmp("#array", sLine.c_str(), 6)) { + std::string sName = sLine.substr(6); + do { + p = sName.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sName.size(); + } + sName.erase(0, p); + p = sName.find_first_of(" \t"); + if (p == std::string::npos) { + p = sName.size(); + } + if (IsIdentifier(sName.substr(0, p))) { + if (!Expression::getGlobalRef(sName.substr(0, p).c_str())) { + Value oVect(VT_VECTOR); + Expression::setGlobal(sName.substr(0, p).c_str(), &oVect, false); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: Im globalen Kontext existiert bereits ein Bezeichner '%s'!", sFileName.c_str(), nLineNumber, sName.substr(0, p).c_str())); + } + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: '%s' ist kein gueltiger Bezeichner!", sFileName.c_str(), nLineNumber, sName.substr(0, p).c_str())); + } + sName.erase(0, p); + } while (!sName.empty()); + } + else if (!strncmp("#dict", sLine.c_str(), 5)) { + std::string sName = sLine.substr(5); + do { + p = sName.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sName.size(); + } + sName.erase(0, p); + p = sName.find_first_of(" \t"); + if (p == std::string::npos) { + p = sName.size(); + } + if (IsIdentifier(sName.substr(0, p))) { + if (!Expression::getGlobalRef(sName.substr(0, p).c_str())) { + Value oMap(VT_MAP); + Expression::setGlobal(sName.substr(0, p).c_str(), &oMap, false); + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: Im globalen Kontext existiert bereits ein Bezeichner '%s'!", sFileName.c_str(), nLineNumber, sName.substr(0, p).c_str())); + } + } + else { + ERRMSG(0, ("%s(%d) : FEHLER: '%s' ist kein gueltiger Bezeichner!", sFileName.c_str(), nLineNumber, sName.substr(0, p).c_str())); + } + sName.erase(0, p); + } while (!sName.empty()); + } + else if (!strncmp("#include", sLine.c_str(), 8)) { + std::string sFName = sLine.substr(8); + p = sFName.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sFName.size(); + } + sFName.erase(0, p); + p = sFName.find_last_not_of(" \t"); + if (p != std::string::npos) { + sFName.erase(p + 1); + } + + if (!Import(sFName)) + return false; + } + else if (!strncmp("#", sLine.c_str(), 1)) { + std::string sBef; + p = sLine.find_first_of(" \t;"); + if (p != std::string::npos) + sBef = sLine.substr(0, p); + else + sBef = sLine; + ERRMSG(0, ("%s(%d) : FEHLER: Unbekannter oder ausserhalb von Funktionen oder Prozeduren unerlaubter Befehl '%s'!", sFileName.c_str(), nLineNumber, sBef.c_str())); + return false; + } + else if (sLine[0] != ';') { + ERRMSG(0, ("%s(%d) : FEHLER: Unerwartete Zeichen: %s", sFileName.c_str(), nLineNumber, sLine.c_str())); + return false; + } + } + } + + // fprintf( stderr, "\n"); + // for( COMMANDBASE::iterator i=m_cpoSubs.begin(); i!=m_cpoSubs.end(); i++ ) + // { + // fprintf( stderr, "[%s] %s\n", (*i).first.c_str(), (*i).second->asString().c_str() ); + // } + // fprintf( stderr, "\n"); + return true; +} + +CScriptBase::~CScriptBase() +{ + for (COMMANDBASE::iterator i = m_cpoSubs.begin(); i != m_cpoSubs.end(); i++) { + delete (*i).second; + } + m_cpoSubs.clear(); +} + +int32_t CScriptBase::AddProc(const CharacterMapper* pMapper, const std::string& sFileName, int32_t nLine, std::istream& oIS, std::string& sLine, bool bIsFunc) +{ + CMetaCommand* pMC; + int32_t nLineNumber = 0; + int nBC = 0; + int i; + size_t p; + bool bVArg = false; + bool bVRef = false; + + while (!sLine.empty() && sLine[sLine.size() - 1] <= 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + p = sLine.find_first_of("'"); + if (p != std::string::npos) + sLine.erase(p); + if (sLine.substr(sLine.length() - 3) == "...") { + if (sLine.substr(sLine.length() - 4) == "&...") { + if (bIsFunc) + ERRMSG(0, ("%s(%d) : FEHLER: Funktionen koennen nicht mit Referenzparametern arbeiten!", sFileName.c_str(), nLine)); + else + bVRef = true; + } + + if (strncmp("#proc", sLine.c_str(), 5)) { + ERRMSG(0, ("%s(%d) : FEHLER: Es wurde '...' ohne #proc verwendet!", sFileName.c_str(), nLine)); + } + if (bVRef) + sLine.erase(sLine.length() - 4); + else + sLine.erase(sLine.length() - 3); + bVArg = true; + } + pMC = new CMetaCommand(sLine, nLine, bIsFunc, bVRef); + pMC->SetFile(sFileName); + // Es geht nicht, wenn nach #proc eine Leerzeile kommt!!! + do { + do { + GetLine(oIS, sLine, nLineNumber); + if (oIS.fail()) + break; + nLineNumber++; + p = 0; + do { + p = sLine.find_first_of("'\\;", p); + if (p != std::string::npos) { + if (sLine[p] == '\\') + p += 2; + else if (sLine[p] == '\'') { + p = sLine.find_first_of("'", p + 1); + if (p != std::string::npos) + p++; + } + } + } while (p != std::string::npos && sLine[p] != ';'); + + if (p != std::string::npos) { + sLine.erase(p); + } + while (!sLine.empty() && sLine[sLine.size() - 1] < 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + } while (sLine.empty()); + + if (oIS.fail()) + break; + + p = sLine.find_first_not_of(" \t"); + if (p == std::string::npos) { + p = sLine.size(); + } + sLine.erase(0, p); + + sLine = pMapper->Map(sLine); + + if (!sLine.empty() && sLine[0] != ';') + nBC += pMC->AddScript(sLine, nLine + nLineNumber); + } while (nBC > 0); + + if (nBC < 0) { + ERRMSG(0, ("%s(%d) : FEHLER: Ein ueberzaehliges '}' gefunden!", sFileName.c_str(), nLine + nLineNumber)); + delete pMC; + return nLineNumber; + } + + i = 2; + while (i <= (int32_t)pMC->Args()) { + if (bIsFunc && !(*pMC)[(size_t)i].empty() && (*pMC)[(size_t)i][0] == '&') + ERRMSG(0, ("%s(%d) : FEHLER: Funktionen koennen nicht mit Referenzparametern arbeiten!", sFileName.c_str(), nLine)); + if ((*pMC)[(size_t)i] == "{") + break; + i++; + } + + char Buff[8]; + snprintf(Buff, sizeof(Buff), "%d", bVArg ? -(i - 1) : i - 1); + (*pMC)[0] = Buff; + + if (pMC->Args() >= 2) { + COMMANDBASE::iterator cbi = m_cpoSubs.find((*pMC)[1]); + if (cbi != m_cpoSubs.end()) { + if ((*cbi).second->GetFile().rfind("GetFile() == sFileName) { + ERRMSG(0, + ("%s(%d) : Warnung: Mehrfache Definition der %s %s in Datei '%s', die letzte wird verwendet!", sFileName.c_str(), nLine, (*cbi).second->IsFunction() ? "Funktion" : "Prozedur", (*((*cbi).second))[1].c_str(), sFileName.c_str())); + } + else { + ERRMSG(0, ("%s(%d) : Warnung: Mehrfache Definition der %s %s in Datei '%s' und '%s', die letzte wird verwendet!", sFileName.c_str(), nLine, (*cbi).second->IsFunction() ? "Funktion" : "Prozedur", (*((*cbi).second))[1].c_str(), + (*cbi).second->GetFile().c_str(), sFileName.c_str())); + } + } + delete (*cbi).second; + (*cbi).second = pMC; + } + else { + m_cpoSubs.insert(COMMANDBASE::value_type((*pMC)[1], pMC)); + } + } + else + delete pMC; + + return nLineNumber; +} + +CMetaCommand* CScriptBase::FindProc(const std::string& sPName) +{ + COMMANDBASE::iterator cbi = m_cpoSubs.find(sPName); + if (cbi != m_cpoSubs.end()) { + return (*cbi).second; + } + return 0; +} diff --git a/Vorlage/Metascript.h b/Vorlage/Metascript.h new file mode 100644 index 0000000..75991fb --- /dev/null +++ b/Vorlage/Metascript.h @@ -0,0 +1,222 @@ +/**************************************************************************** + * $Source: D:\\Development\\Repository/ETools/Vorlage/Metascript.h,v $ + * $Author: ssh $ + * $Date: 2003/07/01 09:39:30 $ + * $Revision: 1.1 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Klassen fuer die Metabefehlsauswertung + ***************************************************************************** + * + * $Log: Metascript.h,v $ + * Revision 1.1 2003/07/01 09:39:30 ssh + * *** empty log message *** + * + * Revision 1.1 2003/07/01 09:19:28 ssh + * Initial recvsing of Source... + * + * Revision 1.9 2000/02/24 09:56:47 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.8 1999/11/17 08:59:13 S.Schuemann + * - support für multiple CRs + * + * - vielfache Änderungen für Vorlage 1.4 beta 8 + * + * Revision 1.7 1999/11/08 11:14:46 S.Schuemann + * - Attribute region.gewinn, region[x,y], unit.bewache, + * region.pool.ding + * - Ecaping in Strings + * - Vars in Attributzugriffen + * - Funktionen + * - Sortierung nach BESCHREIBE PRIVAT + * - Liste fremder Einheiten (normal/verbose) + * - Bugfix: Leerzeile nach Kapitänsinfo entfernt + * + * Revision 1.6 1999/11/03 10:21:57 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.5 1999/10/26 13:27:02 S.Schuemann + * - Anpassungen fuer Vorlage 1.4 b 5 + * + * - Neue Objekt-Attribute + * + * - User-Variable und #while + * + * Revision 1.4 1999/10/24 08:02:18 S.Schuemann + * - Anpassungen fuer 1.4 b 4 + * - CReference als Value eingefuehrt + * - Unterprogramme mit #proc und #call implementiert + * + * Revision 1.3 1999/10/18 21:32:20 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.2 1999/09/27 10:27:10 S.Schuemann + * - Scripthandling fuer das neue Objekt REPORT implementiert + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +class CharacterMapper; + +class CTimeoutException +{ +public: + int32_t _runtime; + + CTimeoutException(int32_t runtime) + : _runtime(runtime) + { + } +}; + +class CMCI +{ +public: + CMCI(int32_t nIdx) + : m_nTC(nIdx) + , m_nLoop(0) + , m_nLine(0) + , m_pVal(0) + , m_pvRef(0) + { + } + + ~CMCI() + { + if (m_pvRef) + delete m_pvRef; + } + + int32_t m_nTC; + int32_t m_nLoop; + int32_t m_nLine; + std::string m_sOArg; + std::string m_sArg; + Value m_vArg; + Value* m_pVal; + CReference* m_pvRef; +}; + +class CMetaCommand +{ + typedef std::pair LOCATION; + typedef std::vector ARGS; + typedef std::vector LOCS; + +public: + CMetaCommand(std::string sCom, int32_t nLine, bool bIsFunc = false, bool bVRef = false); + ~CMetaCommand(); + + int AddScript(std::string sCom, int32_t nLine); + void RunScript(CMCI& oMCI, Expression::Variables& oContext, std::string* psCom, VKommandos& coCmd, int nIdx = 0); + + size_t Args() const { return m_coArgs.size() - 1; } + + void AddCommand(CMCI& oMCI, Expression::Variables& oContext, VKommandos& coCmd, bool bMulti = true); + + void Add(std::string sText) { m_coArgs.push_back(sText); } + + std::string AsString() + { + std::string sErg; + for (size_t i = 0; i < m_coArgs.size(); i++) { + sErg += m_coArgs[i]; + sErg += ' '; + } + return sErg; + } + + std::string& operator[](size_t pos) { return m_coArgs[pos]; } + + bool IsFunction() const { return m_bIsFunc; } + + void SetFile(const std::string& sFileName) { m_sSrcFile = sFileName; } + + const std::string& GetFile() const { return m_sSrcFile; } + + // void SetLine( int32_t nLine ) { m_nLine = nLine; } + void SubBlock(CMCI& oMCI, Expression::Variables& oContext, bool bCond, std::string* psCom, VKommandos& coCmd, int nIdx = -1); + + static bool ProcExists(const std::string& sProc); + static bool Call(const std::string& sProc, VKommandos& coCmd); + + static void ForceDeclares(bool bForce) { m_bForceDeclare = bForce; } + + static void SetTrace(int32_t nTrace) { g_nTrace = nTrace; } + + static void SetErrMsg(const std::string& sErrMsg) { g_sErrMsg = sErrMsg; } + +protected: + void Skip(CMCI& oMCI); + bool Parse(CMCI& oMCI, Expression::Variables& oContext, VKommandos& coCmd, bool bAlone = false, bool bExpand = true); + // std::string& SEL() { return oMCI.m_sArg; } + // Value& VAL() { return oMCI.m_vArg; } + void DumpContext(Expression::Variables& oContext); + void DumpVariable(const std::string& sName, const Value& oVal, bool bExtendedOutput = false); + void QuicksortHelper1(CMCI& oMCI, VKommandos& coCmd, const std::string& sArrayName, Value* pVector, int32_t l, int32_t r, const std::string& sCmpFunc, const ArgumentList& oArgs); + void QuicksortHelper2(CMCI& oMCI, VKommandos& coCmd, const std::string& sArrayName, Value* pVector, int32_t l, int32_t r, const std::string& sCmpFunc, const ArgumentList& oArgs); + int32_t Partition(CMCI& oMCI, VKommandos& coCmd, const std::string& sArrayName, Value* pVector, int32_t l, int32_t r, const std::string& sCmpFunc, const ArgumentList& oArgs); + +private: + bool m_bIsFunc = false; + bool m_bVRef = false; + bool m_bParseError = false; + bool m_bInplace = false; + ARGS m_coArgs; + LOCS m_coLocs; + std::string m_sSrcFile; + + static std::string g_sErrMsg; + static bool m_bForceDeclare; + static int32_t g_nTrace; + // static int32_t g_nStepOver; + static int32_t g_nTraceSteps; +}; + +class CScriptBase +{ +public: + typedef std::map COMMANDBASE; + + CScriptBase(); + ~CScriptBase(); + + bool Import(const std::string& sFileName); + bool Import(const std::string& sIFName, const std::string& sText); + CMetaCommand* FindProc(const std::string& sPName); + +protected: + bool Import(std::istream& oIS, const std::string& sFileName); + int32_t AddProc(const CharacterMapper* pMapper, const std::string& sFileName, int32_t nLine, std::istream& oIS, std::string& sLine, bool bIsFunc); + static void GetLine(std::istream& oIS, std::string& sBuff, int32_t& nLineCounter); + + COMMANDBASE m_cpoSubs; +}; + +extern int32_t g_nLimitRuntime; +extern CKarte* g_poKarte; +extern CReport* g_poCurrentReport; +extern CRegion* g_poCurrentRegion; +extern CBauwerk* g_poCurrentBuilding; +extern CSchiff* g_poCurrentShip; +extern CEinheit* g_poCurrentUnit; +extern CScriptBase g_oScriptBase; + +extern void CloseAllOpenFiles(); diff --git a/Vorlage/Zugvorlage.cpp b/Vorlage/Zugvorlage.cpp new file mode 100644 index 0000000..dabd37a --- /dev/null +++ b/Vorlage/Zugvorlage.cpp @@ -0,0 +1,3609 @@ +/**************************************************************************** + * $Source: f:\\SourceArchive/EresseaTools/Vorlage/Zugvorlage.cpp,v $ + * $Author: S.Schuemann $ + * $Date: 2000/02/24 09:56:47 $ + * $Revision: 1.14 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Algemeine Utility-Funktionen + ***************************************************************************** + * + * $Log: Zugvorlage.cpp,v $ + * Revision 1.14 2000/02/24 09:56:47 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.13 1999/11/28 17:38:41 S.Schuemann + * - Mannigfaltige Änderungen für Vorlage V1.4 beta 9 + * + * Revision 1.12 1999/11/17 08:59:13 S.Schuemann + * - support für multiple CRs + * + * - vielfache Änderungen für Vorlage 1.4 beta 8 + * + * Revision 1.11 1999/11/08 11:14:46 S.Schuemann + * - Attribute region.gewinn, region[x,y], unit.bewache, + * region.pool.ding + * - Ecaping in Strings + * - Vars in Attributzugriffen + * - Funktionen + * - Sortierung nach BESCHREIBE PRIVAT + * - Liste fremder Einheiten (normal/verbose) + * - Bugfix: Leerzeile nach Kapitänsinfo entfernt + * + * Revision 1.10 1999/11/03 10:21:57 S.Schuemann + * - Anpassungen an Vorlage 1.4 beta 7 + * + * Revision 1.9 1999/10/28 12:39:58 S.Schuemann + * - Änderungen für den Linux-Port + * + * Revision 1.8 1999/10/26 13:27:02 S.Schuemann + * - Anpassungen fuer Vorlage 1.4 b 5 + * + * - Neue Objekt-Attribute + * + * - User-Variable und #while + * + * Revision 1.7 1999/10/24 08:02:19 S.Schuemann + * - Anpassungen fuer 1.4 b 4 + * - CReference als Value eingefuehrt + * - Unterprogramme mit #proc und #call implementiert + * + * Revision 1.6 1999/10/20 02:19:39 S.Schuemann + * - Die neue Option '-hb' erlaubt es, zu Beginn der Zugvorlage + * eine Handelsübersicht, nach Parteien und Produkten einzuf�gen, + * um den Überblick zu behalten + * + * - Die neue Option '-si' erlaubt es, die Regionen nach + * Inselzugehörigkeit zu sortieren, statt nach Report- + * Reihenfolge, dabei liegen alle Regionen beisammen, die + * miteinander Verbunden sind + * + * - Die neue Option '-ox ext' leitet die Zugvorlage, wie die + * Option '-o filename' in eine Datei um, die aber den selben + * Basisnamen wie der Bezugsreport hat, aber die Datei- + * erweiterung ext bekommt; Der Report muß die Endung '.cr' + * haben (wie ja üblich) + * + * - Die neue Option '-pb' zeigt BESCHREIBE-PRIVAT-Inhalte in + * der Vorlage an + * + * - Bei Schiffen steht nun auch die freie und die theoretische + * Kapazität + * + * - In den Regionsinfos steht nun der von den Bauern + * erwirtschaftete Gewinn, also die Menge, die man maximal + * Abschöpfen kann, ohne die Regionsreserven zu gefährden + * + * - In der REGION-Zeile steht nun auch noch der Geländetyp + * + * Revision 1.5 1999/10/18 21:32:20 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.4 1999/09/29 08:04:24 S.Schuemann + * - Fehler in der Kapitaensbehandlung fuehrte zum Absturz + * + * Revision 1.3 1999/09/27 10:31:20 S.Schuemann + * - Final 1.3 + * - Kampfstatus wird bei Einheiten angezeigt + * - Schiff und Ablegekueste werden beim Kapitaen angezeigt + * - Anpassungen durch neue Klasse CReport statt CWorldDB mit + * leicht veraenderten Zustaendigkeiten + * + * Revision 1.2 1999/09/20 15:02:33 Steffen + * - kosmetische Aenderungen am Source; + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ + +#ifdef _MSC_VER + +#include + +#include + +_PNH _old_new_handler; + +int my_new_handler(size_t) +{ + throw std::bad_alloc(); + return 0; +} + +#else +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "CRNE.h" +#include "Metascript.h" +#include "Zugvorlage.h" +#include "crashdumphandler.h" + +#define VERSIONINFO "Vorlage " PBEMTOOLS_VERSION_STRING_LONG + +int g_nLineSize = 100; +int g_nCommandLineSize = g_nLineSize; +int g_nNumErrors = 0; +int g_nNumWarnings = 0; +int32_t g_nPassNum = 0; +int32_t g_nMinPasses = 0; +int32_t g_nTimeCorrection = 0; + +// FILE* g_hOut = NULL; +// FILE* g_hErr = NULL; +// FILE* g_hDeb = NULL; + +bool g_bFile = false; +bool g_bError = false; +bool g_bDebug = false; +bool g_bWait = false; + +extern int32_t g_nContainerLimit; + +std::string g_sConfigPathName; +std::string g_sSpiel = "vorlage"; +std::string g_sCmdOptions = "Aufruf:"; + +std::set g_csDupDescrFilter; + +int32_t g_nOnlyGroup = -1; + +std::string GetConfigFileName() +{ + return g_sConfigPathName; +} + +void SetConfigFileName(const std::string& sFName) +{ + if (FileExists(sFName)) + g_sConfigPathName = sFName; +} + +static void EXIT(int32_t nCode) +{ + if (g_bWait && !IsFlag(VF_NOCONSOLE)) { + char Buff[32]; + CONMSG(("Fertig, weiter mit der Eingabetaste.\n")); + auto res = fgets(Buff, 28, stdin); + if (!res) { + exit(-1); + } + } +#ifdef _MSC_VER + _set_new_handler(_old_new_handler); +#endif + exit(nCode); +} + +static void VorlageErrorMsg(void* pDat, const char* pszTxt) +{ + CMetaCommand::SetErrMsg(pszTxt); + + if (IsFlag(VF_TRACEONERROR)) { + CMetaCommand::SetTrace(2); + } + if (pDat) { + VKommandos* poCmd = (VKommandos*)pDat; + poCmd->push_back(std::string("; ") + std::string(pszTxt)); + } + if (CRegExp::Match(pszTxt, "(?i)(Fehler|Error):")) + g_nNumErrors++; + if (CRegExp::Match(pszTxt, "(?i)(Warnung|Warning):")) + g_nNumWarnings++; +} + +static void MyBreak(void) +{ + CMetaCommand::SetTrace(2); +} + +static void WrapOut(const std::string& sPfx, const std::string& sText, size_t nLen, const std::string sFirstPfx = "") +{ + std::string sTxt = sText; + std::string sFPfx = sFirstPfx; + int c = 0; + if (sFirstPfx.empty()) + sFPfx = sPfx; + do { + COutput::TPrintf("vorlage", "%s%s\n", c ? sPfx.c_str() : sFPfx.c_str(), Wrap(sTxt, nLen - (c ? sPfx.size() : sFPfx.size())).c_str()); + c++; + } while (!sTxt.empty()); +} + +const char* pcJahr[] = {"Dezember", "Januar", "Februar", "Maerz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November"}; + +const char* pcJahr2[] = {"Feldsegen", "Nebeltage", "Sturmmond", "Herdfeuer", "Eiswind", "Schneebann", "Bl\xFCtenregen", "Mond der milden Winde", "Sonnenfeuer"}; + +void CVorlage::InitDBS(int32_t nRunde, std::vector& cpoReports) +{ + CKarte::RegionMap::const_iterator rmi; + RegionDB::iterator rdbi; + RegEinheitenDB::iterator redbi; + EinheitenDB::iterator ui; + unsigned int nRep, nEIdx; + + for (nRep = 0; nRep < cpoReports.size(); nRep++) { + // TRACEMSG(( "Analysiere Report %s\n", cpoReports[nRep]->m_sOrgCRName.c_str() )); + for (rmi = cpoReports[nRep]->Karte()->Regions().begin(); rmi != cpoReports[nRep]->Karte()->Regions().end(); rmi++) { + // Region in DB eintragen + if ((*rmi).second->GetBlock() <= CRegion::enSCHEMEN) { + rdbi = g_coRDB.find((*rmi).second->GetKey()); + if (rdbi == g_coRDB.end()) + rdbi = g_coRDB.insert(RegionDB::value_type((*rmi).second->GetKey(), RegionSet())).first; + (*rdbi).second.insert((*rmi).second); + + // Rundenbasierte Region-DB + // g_coRRegionDB[nRunde-cpoReports[nRep]->Runde()].insert( (*rmi).second ); + rdbi = g_coRRegionDB[nRunde - cpoReports[nRep]->Runde()].find((*rmi).second->GetKey()); + if (rdbi == g_coRRegionDB[nRunde - cpoReports[nRep]->Runde()].end()) + rdbi = g_coRRegionDB[nRunde - cpoReports[nRep]->Runde()].insert(RegionDB::value_type((*rmi).second->GetKey(), RegionSet())).first; + (*rdbi).second.insert((*rmi).second); + } + + // Einheiten in DB eintragen + redbi = g_coREDB.find((*rmi).second->GetKey()); + if (redbi == g_coREDB.end()) { + redbi = g_coREDB.insert(RegEinheitenDB::value_type((*rmi).second->GetKey(), EinheitenDB())).first; + } + for (nEIdx = 0; nEIdx < (*rmi).second->GetVEinheiten().size(); nEIdx++) { + // Eintragung in Region-UnitDB + // TODO: Check if this is right + if (true /*|| cpoReports[nRep]->Runde() == nRunde*/) { + ui = (*redbi).second.find((*rmi).second->GetVEinheiten()[nEIdx]->Nummer()); + if (ui == (*redbi).second.end()) { + (*redbi).second.insert(EinheitenDB::value_type((*rmi).second->GetVEinheiten()[nEIdx]->Nummer(), (*rmi).second->GetVEinheiten()[nEIdx])); + // COutput::TPrintf( "vorlage", " Neue Einheit: %s\n", itoa36( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer() ) ); + } + else { + if ((*ui).second->GetQuality() < (*rmi).second->GetVEinheiten()[nEIdx]->GetQuality()) { + (*ui).second = (*rmi).second->GetVEinheiten()[nEIdx]; + // COutput::TPrintf( "vorlage", " Bessere Info: %s\n", itoa36( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer() ) ); + } + } + } + // Eintragung in Global-UnitDB + ui = g_coEDB.find((*rmi).second->GetVEinheiten()[nEIdx]->Nummer()); + if (ui == g_coEDB.end()) { + g_coEDB.insert(EinheitenDB::value_type((*rmi).second->GetVEinheiten()[nEIdx]->Nummer(), (*rmi).second->GetVEinheiten()[nEIdx])); + } + else { + if ((*ui).second->GetQuality() < (*rmi).second->GetVEinheiten()[nEIdx]->GetQuality()) { + (*ui).second = (*rmi).second->GetVEinheiten()[nEIdx]; + } + } + + // Rundenbasierte Einheiten-DB + ui = g_coREinheitenDB[nRunde - cpoReports[nRep]->Runde()].find((*rmi).second->GetVEinheiten()[nEIdx]->Nummer()); + if (ui == g_coREinheitenDB[nRunde - cpoReports[nRep]->Runde()].end()) { + g_coREinheitenDB[nRunde - cpoReports[nRep]->Runde()].insert(EinheitenDB::value_type((*rmi).second->GetVEinheiten()[nEIdx]->Nummer(), (*rmi).second->GetVEinheiten()[nEIdx])); + } + else { + if ((*ui).second->GetQuality() < (*rmi).second->GetVEinheiten()[nEIdx]->GetQuality()) { + (*ui).second = (*rmi).second->GetVEinheiten()[nEIdx]; + } + } + } + + /* + if( cpoReports[nRep]->Runde() == nRunde ) + { + // COutput::TPrintf( "vorlage", " Region %s\n", (*rmi).second->m_sName.c_str() ); + // Region in DB eintragen + if( (*rmi).second->GetBlock()<=CRegion::enSCHEMEN ) + { + rdbi = g_coRDB.find( (*rmi).second->GetKey() ); + if( rdbi == g_coRDB.end() ) + { + g_coRDB.insert( RegionDB::value_type( (*rmi).second->GetKey(), (*rmi).second ) ); + } + else + { + if( (*rdbi).second->m_nRunde != nRunde && (*rmi).second->m_nRunde == nRunde ) + (*rdbi).second = (*rmi).second; + else if( (*rdbi).second->GetQuality() < (*rmi).second->GetQuality() ) + (*rdbi).second = (*rmi).second; + } + } + // Einheiten in DB eintragen + redbi = g_coREDB.find( (*rmi).second->GetKey() ); + if( redbi == g_coREDB.end() ) + { + redbi = g_coREDB.insert( RegEinheitenDB::value_type( (*rmi).second->GetKey(), EinheitenDB() ) ).first; + } + for( nEIdx = 0; nEIdx < (*rmi).second->GetVEinheiten().size(); nEIdx++ ) + { + // Eintragung in Region-UnitDB + ui = (*redbi).second.find( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer() ); + if( ui == (*redbi).second.end() ) + { + (*redbi).second.insert( EinheitenDB::value_type( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer(), (*rmi).second->GetVEinheiten()[nEIdx] ) ); + // COutput::TPrintf( "vorlage", " Neue Einheit: %s\n", itoa36( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer() ) ); + } + else + { + if( (*ui).second->GetQuality() < (*rmi).second->GetVEinheiten()[nEIdx]->GetQuality() ) + { + (*ui).second = (*rmi).second->GetVEinheiten()[nEIdx]; + // COutput::TPrintf( "vorlage", " Bessere Info: %s\n", itoa36( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer() ) ); + } + } + + // Eintragung in Global-UnitDB + ui = g_coEDB.find( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer() ); + if( ui == g_coEDB.end() ) + { + g_coEDB.insert( EinheitenDB::value_type( (*rmi).second->GetVEinheiten()[nEIdx]->Nummer(), (*rmi).second->GetVEinheiten()[nEIdx] ) ); + } + else + { + if( (*ui).second->GetQuality() < (*rmi).second->GetVEinheiten()[nEIdx]->GetQuality() ) + { + (*ui).second = (*rmi).second->GetVEinheiten()[nEIdx]; + } + } + } + } + else + { + // Region in DB eintragen + rdbi = g_coRDB.find( (*rmi).second->GetKey() ); + if( rdbi == g_coRDB.end() ) + { + g_coRDB.insert( RegionDB::value_type( (*rmi).second->GetKey(), (*rmi).second ) ); + } + else + { + if( (*rdbi).second->GetQuality() < (*rmi).second->GetQuality() ) + (*rdbi).second = (*rmi).second; + } + } + */ + } + } +} + +void CVorlage::Islandize(CKarte::IslandQueue& cpoQueue, RegionDB& coRDB) +{ + RegionDB::iterator rdbi; + CRegion* poReg; + CRegion* poR2; + int32_t x, y, z; + + while (!cpoQueue.empty()) { + poReg = cpoQueue.front(); + /* + if( poReg && poReg->GetValue("herb").asString().size()>2 ) + { + rdbi = coRDB.find( poReg->GetKey() ); + if( rdbi != coRDB.end() ) + { + (*rdbi).second->SetValue( "herb", poReg->GetValue("herb") ); + } + } + */ + if (poReg && !poReg->GetIslandName().empty()) { + // fprintf( stderr, "Region: %s\n", poReg->m_sName.c_str() ); + + rdbi = coRDB.find(poReg->GetKey()); + if (rdbi != coRDB.end()) { + (*(*rdbi).second.begin())->SetIslandName(poReg->GetIslandName()); + } + + x = poReg->GetEX(); + y = poReg->GetEY(); + z = poReg->GetEZ(); + + // Nordosten + rdbi = coRDB.find(CRegion::CalcKey(x, y + 1, z)); + if (rdbi != coRDB.end()) { + poR2 = *(*rdbi).second.begin(); + if (poR2->GetIslandName().empty() && poR2->IsLand()) { + poR2->SetIslandName(poReg->GetIslandName()); + cpoQueue.push_back(poR2); + } + } + // Osten + rdbi = coRDB.find(CRegion::CalcKey(x + 1, y, z)); + if (rdbi != coRDB.end()) { + poR2 = *(*rdbi).second.begin(); + if (poR2->GetIslandName().empty() && poR2->IsLand()) { + poR2->SetIslandName(poReg->GetIslandName()); + cpoQueue.push_back(poR2); + } + } + // Suedosten + rdbi = coRDB.find(CRegion::CalcKey(x + 1, y - 1, z)); + if (rdbi != coRDB.end()) { + poR2 = *(*rdbi).second.begin(); + if (poR2->GetIslandName().empty() && poR2->IsLand()) { + poR2->SetIslandName(poReg->GetIslandName()); + cpoQueue.push_back(poR2); + } + } + // Suedwesten + rdbi = coRDB.find(CRegion::CalcKey(x, y - 1, z)); + if (rdbi != coRDB.end()) { + poR2 = *(*rdbi).second.begin(); + if (poR2->GetIslandName().empty() && poR2->IsLand()) { + poR2->SetIslandName(poReg->GetIslandName()); + cpoQueue.push_back(poR2); + } + } + // Westen + rdbi = coRDB.find(CRegion::CalcKey(x - 1, y, z)); + if (rdbi != coRDB.end()) { + poR2 = *(*rdbi).second.begin(); + if (poR2->GetIslandName().empty() && poR2->IsLand()) { + poR2->SetIslandName(poReg->GetIslandName()); + cpoQueue.push_back(poR2); + } + } + // Nordwesten + rdbi = coRDB.find(CRegion::CalcKey(x - 1, y + 1, z)); + if (rdbi != coRDB.end()) { + poR2 = *(*rdbi).second.begin(); + if (poR2->GetIslandName().empty() && poR2->IsLand()) { + poR2->SetIslandName(poReg->GetIslandName()); + cpoQueue.push_back(poR2); + } + } + } + cpoQueue.pop_front(); + } +} + +#define PIPRINT \ + g_bForceEOL = true; \ + COutput::Target("console")->Printf + +void CVorlage::RunMetacommands(CReport& oReport) +{ + CRegion* poReg = nullptr; + const CRegion::VEinheiten* poVE = nullptr; + CEinheit* poUnit = nullptr; + int32_t nUnitCnt = 0; + bool bDoneReg = false; + char pcPass[16]; + // Expression::clearAllVars(); + + m_nPlayer = oReport.Partei(); + m_poKarte = oReport.Karte(); + g_poCurrentReport = &oReport; + g_poKarte = m_poKarte; + + if (g_nMinPasses) + snprintf(pcPass, sizeof(pcPass), "Pass %ld: ", g_nPassNum); + else + pcPass[0] = 0; + + if (IsFlag(VF_PROGRESSINFO)) { + if (g_nPassNum <= 1) + TRACEMSG(("\n")); + PIPRINT("\r%sEinheiten: %3d%% - OnInit", pcPass, nUnitCnt * 100 / m_nUnits); + } + Value vCurrentMeta(-1); + Expression::setGlobal("$CURRENTMETA", &vCurrentMeta); + + CMetaCommand::Call(std::string("OnInit"), m_coInitCmd); + + // for( CKarte::RegionMap::const_iterator rmi = m_poKarte->Regions().begin(); rmi != m_poKarte->Regions().end(); rmi++ ) + for (RegionDB::iterator rmi = g_coRDB.begin(); rmi != g_coRDB.end(); rmi++) { + // cpoRegions.push_back((*rmi).second); + // poReg = (*rmi).second; + poReg = *((*rmi).second.begin()); + /* + if( poReg->DeepGetValue("visibility").getType() == VT_STRING ) + { + TRACEMSG(( "%s/%s\n", poReg->GetName().c_str(), poReg->GetRegionTypeName().c_str() )); + } + */ + vCurrentMeta = Value(-1); + Expression::setGlobal("$CURRENTMETA", &vCurrentMeta); + + m_poCurrentRegion = poReg; + g_poCurrentRegion = poReg; + if (poReg->Map() == m_poKarte || poReg->DeepGetValue("visibility").getType() == VT_STRING) { + poVE = &poReg->GetVEinheiten(); + bDoneReg = false; + if (IsFlag(VF_RUNALLVISIBLEREGIONS)) { + if (IsFlag(VF_PROGRESSINFO)) { + if (poReg->GetEZ()) { + PIPRINT("\r%sEinheiten: %3d%% - OnRegion(%d,%d,%d)", pcPass, nUnitCnt * 100 / m_nUnits, poReg->GetEX(), poReg->GetEY(), poReg->GetEZ()); + } + else { + PIPRINT("\r%sEinheiten: %3d%% - OnRegion(%d,%d)", pcPass, nUnitCnt * 100 / m_nUnits, poReg->GetEX(), poReg->GetEY()); + } + } + CMetaCommand::Call(std::string("OnRegion"), poReg->GetKommandos()); + if (poReg->GetVBauwerke()) { + for (CRegion::VBauwerke::const_iterator bi = poReg->GetVBauwerke()->begin(); bi != poReg->GetVBauwerke()->end(); ++bi) { + g_poCurrentBuilding = *bi; + CMetaCommand::Call(std::string("OnBuilding"), (*bi)->GetKommandos()); + g_poCurrentBuilding = 0; + } + } + if (poReg->GetVSchiffe()) { + for (CRegion::VSchiffe::const_iterator si = poReg->GetVSchiffe()->begin(); si != poReg->GetVSchiffe()->end(); ++si) { + g_poCurrentShip = *si; + CMetaCommand::Call(std::string("OnShip"), (*si)->GetKommandos()); + g_poCurrentShip = 0; + } + } + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r "); + } + bDoneReg = true; + } + for (unsigned i = 0; i < poVE->size(); i++) { + if (poVE->operator[](i)->Partei() == m_nPlayer && !poVE->operator[](i)->GetValue("Verraeter")) { + if (!bDoneReg) { + if (IsFlag(VF_PROGRESSINFO)) { + if (poReg->GetEZ()) { + PIPRINT("\r%sEinheiten: %3d%% - OnRegion(%d,%d,%d)", pcPass, nUnitCnt * 100 / m_nUnits, poReg->GetEX(), poReg->GetEY(), poReg->GetEZ()); + } + else { + PIPRINT("\r%sEinheiten: %3d%% - OnRegion(%d,%d)", pcPass, nUnitCnt * 100 / m_nUnits, poReg->GetEX(), poReg->GetEY()); + } + } + CMetaCommand::Call(std::string("OnRegion"), poReg->GetKommandos()); + if (poReg->GetVBauwerke()) { + for (CRegion::VBauwerke::const_iterator bi = poReg->GetVBauwerke()->begin(); bi != poReg->GetVBauwerke()->end(); ++bi) { + g_poCurrentBuilding = *bi; + CMetaCommand::Call(std::string("OnBuilding"), (*bi)->GetKommandos()); + g_poCurrentBuilding = 0; + } + } + if (poReg->GetVSchiffe()) { + for (CRegion::VSchiffe::const_iterator si = poReg->GetVSchiffe()->begin(); si != poReg->GetVSchiffe()->end(); ++si) { + g_poCurrentShip = *si; + CMetaCommand::Call(std::string("OnShip"), (*si)->GetKommandos()); + g_poCurrentShip = 0; + } + } + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r "); + } + bDoneReg = true; + } + + poUnit = poVE->operator[](i); + m_poCurrentUnit = poUnit; + g_poCurrentUnit = poUnit; + + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r%sEinheiten: %3d%% - OnUnit(%s) ", pcPass, nUnitCnt * 100 / m_nUnits, itoan(poUnit->m_nNummer, g_poCurrentReport->ENrBase())); + } + + CMetaCommand::Call(std::string("OnUnit"), poUnit->m_csMetaOut); + + if (Expression::getGlobal("$EXECINLINE").asLong()) { + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r%sEinheiten: %3d%% - [%s] ", pcPass, nUnitCnt * 100 / m_nUnits, itoan(poUnit->m_nNummer, g_poCurrentReport->ENrBase())); + } + + if (IsFlag(VF_PRIVATMETA)) { + // Metabefehle in privaten Beschreibungen + if (!poUnit->m_sPrivat.empty()) { + std::string sNewBP; + std::string sTemp; + int c = 0; + // bMetas = true; + Expression::Variables oContext; + CMetaCommand oMC(poUnit->m_sPrivat, 0); + oMC.SetFile(g_poCurrentReport->FileName()); + // oMC.SetLine( 0 ); + CMCI oMCI(0); + oMC.RunScript(oMCI, oContext, &poUnit->m_sPrivat, poUnit->m_csMetaOut); + sNewBP = std::string("BESCHREIBE PRIVAT %c%s%c\n") + '"' + poUnit->m_sPrivat + '"'; + do { + sTemp = std::string(c ? " " : "") + Wrap(sNewBP, size_t(g_nLineSize - (c ? 16 : 1))); + if (!sNewBP.empty() && oReport.Version() >= 35 && IsEqual(oReport.Spiel(), "Eressea")) + sTemp += " \\"; + poUnit->m_csMetaOut.push_back(sTemp); + c++; + } while (!sNewBP.empty()); + + // poUnit->m_csMetaOut.push_back( std::string( Buff ) ); + } + } + else { + // Metabefehle in persistenten Kommentaren + std::string oldcmd; + std::string newcmd; + size_t realidx = 0; + for (size_t j = 0; j < poUnit->m_csKommandos.size(); j++) { + oldcmd = poUnit->m_csKommandos[(int32_t)j].asString(); + if (IsMetaCommand(oldcmd)) { + // bMetas = true; + Expression::Variables oContext; + newcmd = oldcmd; + CMetaCommand oMC(oldcmd, poUnit->m_cnKomLines[j]); + oMC.SetFile(g_poCurrentReport->FileName()); + CMCI oMCI(0); + oMCI.m_nLine = poUnit->m_cnKomLines[j]; + // oMC.SetLine( poUnit->m_cnKomLines[j] ); + if (poUnit->m_csMetaOut.size() > realidx && oldcmd == poUnit->m_csMetaOut[(int32_t)realidx].asString()) { + vCurrentMeta = Value(int32_t(realidx)); + Expression::setGlobal("$CURRENTMETA", &vCurrentMeta); + } + else { + vCurrentMeta = Value(-1); + Expression::setGlobal("$CURRENTMETA", &vCurrentMeta); + // ERRMSG( poUnit->m_csMetaOut, ( "%s(%d) : Warnung: Konflikt zwischen manueller Aenderung von 'UNIT.OUTPUT[%d]' und Referenzparametern!", g_poCurrentReport->FileName(), + // poUnit->m_cnKomLines[j], sName.substr( 0, p ).c_str(), realidx ) ); + } + oMC.RunScript(oMCI, oContext, &newcmd, poUnit->m_csMetaOut); + vCurrentMeta = Value(-1); + Expression::setGlobal("$CURRENTMETA", &vCurrentMeta); + if (IsFlag(VF_FULLCOMMANDOUTPUT)) { + if (!IsEqual(newcmd.c_str(), oldcmd.c_str())) { + if (poUnit->m_csMetaOut.size() > realidx && oldcmd == poUnit->m_csMetaOut[(int32_t)realidx].asString()) { + poUnit->m_csMetaOut[(int32_t)realidx] = Value(newcmd); + } + else { + ERRMSG((void*)&(poUnit->m_csMetaOut), + ("%s(%d) : Warnung: Konflikt zwischen manueller Aenderung von 'UNIT.OUTPUT[%d]' und Referenzparametern!", g_poCurrentReport->FileName().c_str(), poUnit->m_cnKomLines[j], realidx)); + } + } + } + else { + poUnit->m_csKommandos[(int32_t)j] = Value(newcmd); + } + } + if (!oldcmd.empty() && oldcmd[0] == '/') + realidx++; + } + } + } + + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r%sEinheiten: %3d%% - EndUnit(%s) ", pcPass, nUnitCnt * 100 / m_nUnits, itoan(poUnit->m_nNummer, g_poCurrentReport->ENrBase())); + nUnitCnt++; + } + CMetaCommand::Call(std::string("EndUnit"), poUnit->m_csMetaOut); + } + } + } + + if (bDoneReg) { + if (IsFlag(VF_PROGRESSINFO)) { + if (poReg->GetEZ()) { + PIPRINT("\r%sEinheiten: %3d%% - EndRegion(%d,%d,%d)", pcPass, nUnitCnt * 100 / m_nUnits, poReg->GetEX(), poReg->GetEY(), poReg->GetEZ()); + } + else { + PIPRINT("\r%sEinheiten: %3d%% - EndRegion(%d,%d)", pcPass, nUnitCnt * 100 / m_nUnits, poReg->GetEX(), poReg->GetEY()); + } + } + CMetaCommand::Call(std::string("EndRegion"), poReg->GetEndKommandos()); + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r "); + } + } + } + + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r%sEinheiten: %3d%% - OnExit", pcPass, nUnitCnt * 100 / m_nUnits); + } + CMetaCommand::Call(std::string("OnExit"), m_coExitCmd); + + if (IsFlag(VF_PROGRESSINFO)) { + PIPRINT("\r%sEinheiten: %3d%% \n", pcPass, nUnitCnt * 100 / m_nUnits); + } +} + +std::string CVorlage::MutateCRBlock(std::fstream& oIS, CBlockBase* poBlockObj, bool utf8) +{ + std::string sLine; + std::string sTag, sTagCR; + std::set coUserTags; + Value oVal; + bool custom = false; + + while (true) { + getline(oIS, sLine); + if (oIS.fail()) + break; + + if (!sLine.empty() && IsAlpha(sLine[0])) + break; + + while (!sLine.empty() && sLine[sLine.size() - 1] < 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + + custom = false; + if (poBlockObj && poBlockObj->NumValues()) { + std::string::size_type p = sLine.find_last_of(';'); + if (p != std::string::npos) { + // TODO: Real UTF8-Handling + sTag = sTagCR = sLine.substr(p + 1); + if (utf8) + Utf8toIso885915(sTag); + oVal = poBlockObj->CBlockBase::GetValue(std::string("!") + sTag, Value()); + coUserTags.insert(std::string("!") + sTag); + if (oVal.getType() != VT_EMPTY) { + custom = true; + } + } + } + + if (custom) { + if (oVal.getType() == VT_STRING) { + std::string strVal = Escape(oVal.asString()); + COutput::TPrintf("vorlage", "\x22%s\x22;%s\n", (utf8 ? iso885915ToUtf8(strVal).c_str() : strVal.c_str()), sTagCR.c_str()); + } + else { + COutput::TPrintf("vorlage", "%d;%s\n", oVal.asLong(), sTagCR.c_str()); + } + } + else { + COutput::TPrintf("vorlage", "%s\n", sLine.c_str()); + } + } + + if (poBlockObj) { + std::string sName; + Value oValt; + for (int32_t k = 0; k < (int32_t)poBlockObj->NumValues(); k++) { + oValt = poBlockObj->CBlockBase::GetValue(k, sName); + if (!sName.empty() && sName[0] == '!') { + if (coUserTags.find(sName) == coUserTags.end()) { + if (utf8) + iso885915ToUtf8(sName); + if (oValt.getType() == VT_STRING) { + std::string strVal = Escape(oValt.asString()); + COutput::TPrintf("vorlage", "\x22%s\x22;%s\n", (utf8 ? iso885915ToUtf8(strVal).c_str() : strVal.c_str()), sName.substr(1).c_str()); + } + else { + COutput::TPrintf("vorlage", "%d;%s\n", oValt.asLong(), sName.substr(1).c_str()); + } + } + } + } + } + + return sLine; +} + +void CVorlage::Vorlage(CReport& oReport, CReport* poRep2, bool bTime) +{ + std::vector cpoRegions; + RegionDB::iterator rdbi; + CRegion* poReg2; + // CRegion* poRQ = 0; + // FILE* hHold = 0; + clock_t nStart; + int32_t nUnits = 0, nPersons = 0, nNeededFood = 0; + int ic; + + if (IsFlag(VF_CROUTPUT) || IsFlag(VF_SUPPRESSTURNOUTPUT)) { + COutput::RenameTarget("vorlage", "vorlage_backup"); +#ifdef _WIN32 + COutput::SetTarget("vorlage", new COutput("nul")); +#else + COutput::SetTarget("vorlage", new COutput("/dev/null")); +#endif + } + + COutput::SetFilter("vorlage", "OutputLineFilter"); + + g_nTimeCorrection = 0; + g_poCurrentReport = &oReport; + m_poCurrentReport = &oReport; + CMessage::SelectRenderer(oReport.MessageRenderer(), oReport.MessageRules()); + oReport.CalculateStatistics(); + + m_nPlayer = oReport.Partei(); + for (CReport::Einheiten::iterator ei = oReport.GEinheiten().begin(); ei != oReport.GEinheiten().end(); ei++) { + if ((*ei).second->Partei() == m_nPlayer) { + nUnits++; + nPersons += (*ei).second->Anzahl(); + if (0 != CRasse::Lookup((*ei).second->RealType()).GetValue(std::string("Unterhalt")).asLong()) { + nNeededFood += CRasse::Lookup((*ei).second->RealType()).GetValue(std::string("Unterhalt")).asLong() * (*ei).second->Anzahl(); + } + } + } + m_nUnits = nUnits; + + COutput::TPrintf("vorlage", "%s %s %c%s%c\n", IsEqual(g_poCurrentReport->m_sSpiel, "eressea") ? "ERESSEA" : "PARTEI", itoan(oReport.Partei(), oReport.PNrBase()), 34, oReport.Passwort().c_str(), 34); + if (IsEqual(g_poCurrentReport->m_sSpiel, "eressea") || IsEqual(g_poCurrentReport->m_sSpiel, "empiria") || IsEqual(g_poCurrentReport->m_sSpiel, "vinyambar i") || IsEqual(g_poCurrentReport->m_sSpiel, "vinyambar ii")) + COutput::TPrintf("vorlage", "\n ; ECHECK -l -w4 -r%d\n", oReport.Rekrutierungskosten()); + COutput::TPrintf("vorlage", "\n ; %s, (C) 1999-2026 by S.Schuemann\n ; [%s %s]\n", VERSIONINFO, __DATE__, __TIME__); + WrapOut(" ; ", g_sCmdOptions, (size_t)g_nLineSize, " ; "); + if (poRep2 && oReport.Version() != poRep2->Version()) { + std::ostringstream out; + out << "Achtung: CR-Versionswechsel von V" << poRep2->Version() << " auf V" << oReport.Version() << "!"; + auto msg = out.str(); + if (bTime) + TRACEMSG(("%s\n", msg.c_str())); + COutput::TPrintf("vorlage", " ; %s\n", msg.c_str()); + } + + if (oReport.Zeitalter() == 1) + COutput::TPrintf("vorlage", "\n ; Zugvorlage aus Report Runde %d (%s %d)\n", oReport.Runde(), pcJahr[(oReport.Runde()) % 12], (oReport.Runde() - 1) / 12 + 1); + else + COutput::TPrintf("vorlage", "\n ; Zugvorlage aus Report Runde %d (%d. Woche, %s, %d)\n", oReport.Runde(), (oReport.Runde() - 184) % 3 + 1, pcJahr2[((oReport.Runde() - 184) / 3) % 9], (oReport.Runde() - 184) / 27 + 1); + + CPartei::Ptr parteiInfo = oReport.GetLocalParteiInfo(oReport.Partei()); + CPartei::Ptr lastParteiInfo; + if (poRep2) + lastParteiInfo = poRep2->GetLocalParteiInfo(poRep2->Partei()); + + int32_t maxHeroes = 0; + if (parteiInfo) + maxHeroes = parteiInfo->GetValue("max_heroes").asLong(); + int32_t heroes = 0; + if (parteiInfo) + heroes = parteiInfo->GetValue("heroes").asLong(); + + if (oReport.m_nPunkte > 0 && oReport.m_nPunkteschnitt > 0) { + if (!poRep2) { + if (maxHeroes > 0) + COutput::TPrintf("vorlage", " ; Personen: %d, Einheiten: %d, Helden: %d/%d\n", nPersons, nUnits, heroes, maxHeroes); + else + COutput::TPrintf("vorlage", " ; Personen: %d, Einheiten: %d\n", nPersons, nUnits); + COutput::TPrintf("vorlage", " ; Punkte: %d (%.2f%% des Durchschnitts)\n", oReport.m_nPunkte, (double)oReport.m_nPunkte * 100.0 / oReport.m_nPunkteschnitt); + } + else { + int32_t nLastUnits = 0, nLastPersons = 0; + for (CReport::Einheiten::iterator ei = poRep2->GEinheiten().begin(); ei != poRep2->GEinheiten().end(); ei++) { + if ((*ei).second->Partei() == m_nPlayer) { + nLastUnits++; + nLastPersons += (*ei).second->Anzahl(); + // if( !IsEqual( (*ei).second->Typ(), "Untote" ) ) + // nEater += (*ei).second->Anzahl(); + } + } + + int32_t lastMaxHeroes = 0; + if (lastParteiInfo) + lastMaxHeroes = lastParteiInfo->GetValue("max_heroes").asLong(); + int32_t lastHeroes = 0; + if (lastParteiInfo) + lastHeroes = lastParteiInfo->GetValue("heroes").asLong(); + if (maxHeroes > 0) + COutput::TPrintf("vorlage", " ; Personen: %d (%+d), Einheiten: %d (%+d), Helden: %d/%d (%+d/%+d)\n", nPersons, nPersons - nLastPersons, nUnits, nUnits - nLastUnits, heroes, maxHeroes, heroes - lastHeroes, maxHeroes - lastMaxHeroes); + else + COutput::TPrintf("vorlage", " ; Personen: %d (%+d), Einheiten: %d (%+d)\n", nPersons, nPersons - nLastPersons, nUnits, nUnits - nLastUnits); + + if (poRep2->m_nPunkte) + COutput::TPrintf("vorlage", " ; Punkte: %d (%.2f%% des Durchschnitts, %+.2f%%)\n", oReport.m_nPunkte, (double)oReport.m_nPunkte * 100.0 / oReport.m_nPunkteschnitt, + (double)oReport.m_nPunkte * 100.0 / oReport.m_nPunkteschnitt - (double)poRep2->m_nPunkte * 100.0 / poRep2->m_nPunkteschnitt); + else + COutput::TPrintf("vorlage", " ; Punkte: %d (%.2f%% des Durchschnitts)\n", oReport.m_nPunkte, (double)oReport.m_nPunkte * 100.0 / oReport.m_nPunkteschnitt); + } + } + + // Expression::clearAllVars(); + + m_poKarte = oReport.Karte(); + g_poKarte = m_poKarte; + + // if( IsFlag( VF_SORTISLANDS ) ) + // { + if (bTime) + TRACEMSG((" [")); + nStart = clock(); + ic = m_poKarte->Islandize(); + if (bTime) { + TRACEMSG(("%d Inseln (%1.2f sek.)]\n", ic, float(clock() - nStart) / CLOCKS_PER_SEC)); + } + // } + + Value oPassNumVal, oMinPassesVal, oExecInline, oGame; + + m_coInitCmd.clear(); + m_coExitCmd.clear(); + + oGame = Value(g_poCurrentReport->m_sSpiel); + Expression::setConstant("GAME", &oGame); + + oExecInline = Value(1); + Expression::setGlobal("$EXECINLINE", &oExecInline); + + oPassNumVal = Value(1); + Expression::setGlobal("$PASSNUM", &oPassNumVal); + + if (g_nMinPasses) { + g_nPassNum = 1; + oPassNumVal = g_nPassNum; + oMinPassesVal = g_nMinPasses; + Expression::setGlobal("$PASSNUM", &oPassNumVal); + Expression::setGlobal("$MINPASSES", &oMinPassesVal); + } + + bool bLoop = true; + int32_t nPassNum; + + do { + RunMetacommands(oReport); + + oExecInline = Value(0); + Expression::setGlobal("$EXECINLINE", &oExecInline); + + if (g_nMinPasses) { + nPassNum = Expression::getGlobal("$PASSNUM").asLong(); + if (nPassNum != g_nPassNum) { + g_nPassNum = nPassNum; + oPassNumVal = g_nPassNum; + Expression::setGlobal("$PASSNUM", &oPassNumVal); + } + else if (nPassNum < g_nMinPasses) { + g_nPassNum++; + oPassNumVal = g_nPassNum; + Expression::setGlobal("$PASSNUM", &oPassNumVal); + } + else { + bLoop = false; + } + } + else { + bLoop = false; + } + } while (bLoop); + + if (!m_coInitCmd.empty()) { + for (int32_t i = 0; i < (int32_t)m_coInitCmd.size(); i++) { + if (m_coInitCmd[i].empty()) { + COutput::TPrintf("vorlage", "\n"); + } + else { + if (m_coInitCmd[i].asString()[0] == ';') + WrapOut(" ; ", m_coInitCmd[i].asString().substr(2), (size_t)g_nLineSize); + } + } + } + + if (IsFlag(VF_SHOWHANDEL)) { + COutput::TPrintf("vorlage", "\n ; Wirtschaftsbilanz:\n"); + COutput::TPrintf("vorlage", " ; Gesamteinkommen:%9ld Silber\n", oReport.m_nEinkommen); + COutput::TPrintf("vorlage", " ; Gesamtausgaben: %9ld Silber %s\n", oReport.m_nAusgaben + nNeededFood /*Eater*10*/, oReport.Version() > 40 ? "" : "(z.Zt. ohne kostenpfl. Talente)"); + int64_t nVermoegen = 0; + for (CKarte::RegionMap::const_iterator rmi = m_poKarte->Regions().begin(); rmi != m_poKarte->Regions().end(); rmi++) { + nVermoegen += (*rmi).second->SilverOf(oReport.Partei()); + } + COutput::TPrintf("vorlage", " ; Gesamtverm\xF6gen: %9ld Silber\n", nVermoegen); + + if (oReport.m_cpoHPartner.size()) + COutput::TPrintf("vorlage", "\n ; Warenaustausch:\n"); + + for (CReport::Handelspartner::const_iterator rhi = oReport.m_cpoHPartner.begin(); rhi != oReport.m_cpoHPartner.end(); rhi++) { + std::ostringstream out; + out << oReport.Parteiname((*rhi).first).c_str() + 1 << "(" << itoan((*rhi).first, oReport.PNrBase()) << "): "; + for (CParteihandel::Produkte::const_iterator ppi = (*rhi).second->m_coProdukte.begin(); ppi != (*rhi).second->m_coProdukte.end(); ppi++) { + if (ppi != (*rhi).second->m_coProdukte.begin()) { + out << ", "; + } + out << std::showpos << (*ppi).second << std::noshowpos << " " << (*ppi).first; + } + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize, " ; "); + } + } + + for (CKarte::RegionMap::const_iterator rmi = m_poKarte->Regions().begin(); rmi != m_poKarte->Regions().end(); rmi++) { + rdbi = g_coRDB.find((*rmi).second->GetKey()); + if (rdbi != g_coRDB.end()) { + if ((*(*rdbi).second.begin())->GetIslandName().empty()) + (*rmi).second->SetIslandName((*(*rdbi).second.begin())->GetIslandName()); + } + cpoRegions.push_back((*rmi).second); + } + std::stable_sort(cpoRegions.begin(), cpoRegions.end(), CRegionSorter(IsFlag(VF_SORTISLANDS))); + + if (IsFlag(VF_SHOWKOMPKARTE)) { + COutput::TPrintf("vorlage", "\n ; Uebersichtskarte:\n"); + oReport.Karte()->DumpFullMap("vorlage", " ; "); + } + + if (IsFlag(VF_SHOWWORLDKARTE)) { + COutput::TPrintf("vorlage", "\n ; Karte der bekannten Welt:\n"); + oReport.Karte()->DumpWorldMap("vorlage", " ; "); + } + + // for( CKarte::RegionMap::const_iterator i = m_poKarte->Regions().begin(); i != m_poKarte->Regions().end(); i++ ) + for (size_t i = 0; i < cpoRegions.size(); i++) { + poReg2 = 0; + if (poRep2) { + CKarte::RegionMap::const_iterator ri; + ri = poRep2->Karte()->Regions().find(cpoRegions[i] /*.second*/->GetKey()); + if (ri != poRep2->Karte()->Regions().end()) { + poReg2 = (*ri).second; + if (!poReg2->PersonsOf(m_nPlayer) || !poReg2->IsLand()) + poReg2 = 0; + } + } + if (g_nOnlyGroup < 0 || cpoRegions[i]->IsGroup(g_nOnlyGroup)) { + if (cpoRegions[i]->PersonsOf(m_nPlayer)) + Regionsvorlage(cpoRegions[i] /*.second*/, poReg2, poRep2); + } + } + + COutput::TPrintf("vorlage", "\n NAECHSTER\n\n"); + + if (!m_coExitCmd.empty()) { + for (int32_t i = 0; i < (int32_t)m_coExitCmd.size(); i++) { + if (m_coExitCmd[i].empty()) { + COutput::TPrintf("vorlage", "\n"); + } + else { + if (m_coExitCmd[i].asString()[0] == ';') + WrapOut(" ; ", m_coExitCmd[i].asString().substr(2), (size_t)g_nLineSize); + } + } + } + + COutput::SetFilter("vorlage", ""); + + if (IsFlag(VF_CROUTPUT)) { + CEinheit* poUnit = 0; + std::fstream oIS; + std::string sLine; + std::string sCmd; + int32_t nENr = -1; + // int32_t nPNr = 0; + bool bGotLine = false; + bool bKonfiguration = false; + // size_t i; + + COutput::RenameTarget("vorlage_backup", "vorlage"); + + // TODO: Real UTF8-Handling + oIS.open(oReport.m_sOrgCRName.c_str(), ios::in); + + if (oIS.fail()) { + ERRMSG(0, ("Auf die Report-Datei kann nicht zugegriffen werden!")); + EXIT(1); + } + + while (1) { + if (!bGotLine) { + getline(oIS, sLine); + if (oIS.fail()) + break; + } + else + bGotLine = false; + + while (!sLine.empty() && sLine[sLine.size() - 1] < 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + + if (!bKonfiguration && sLine.length() > 15 && CRegExp::Match(sLine, "^(\"[^\"]*\"\\s*;\\s*(?i:Konfiguration)|[A-Z]+)")) { + bKonfiguration = true; + if (CRegExp::Match(sLine, "^\"[^\"]*\"\\s*;\\s*(?i:Konfiguration)")) { + COutput::TPrintf("vorlage", "\"Vorlage\";Konfiguration\n"); + } + else { + COutput::TPrintf("vorlage", "\"Vorlage\";Konfiguration\n"); + COutput::TPrintf("vorlage", "%s\n", sLine.c_str()); + } + } + else { + COutput::TPrintf("vorlage", "%s\n", sLine.c_str()); + } + + if (!strncmp(sLine.c_str(), "REGION ", 7)) { + static char buff[128]; + CRegion* pReg; + char* sptr; + int32_t x = 0, y = 0, z = 0; + strncpy(buff, sLine.c_str() + 7, 127); + buff[127] = 0; + sptr = buff; + x = (int32_t)strtol(sptr, &sptr, 10); + if (*sptr) + sptr++; + y = (int32_t)strtol(sptr, &sptr, 10); + if (*sptr) + sptr++; + z = (int32_t)strtol(sptr, &sptr, 10); + pReg = oReport.Karte()->GetFromECords(x, y, z); + sLine = MutateCRBlock(oIS, pReg, oReport.m_bUTF8); + bGotLine = true; + } + else if (!strncmp(sLine.c_str(), "EINHEIT ", 8)) { + nENr = (int32_t)strtol(sLine.substr(8).c_str(), NULL, 10); + poUnit = oReport.SearchUnit(nENr, false); + // if( poUnit && poUnit->Partei() != oReport.Partei() ) + // poUnit = 0; + + sLine = MutateCRBlock(oIS, poUnit, oReport.m_bUTF8); + bGotLine = true; + } + else if (!strncmp(sLine.c_str(), "SCHIFF ", 7)) { + int32_t nShipNr = (int32_t)strtol(sLine.substr(7).c_str(), NULL, 10); + CSchiff* poShip = oReport.GetShip(nShipNr); + sLine = MutateCRBlock(oIS, poShip, oReport.m_bUTF8); + bGotLine = true; + } + else if (!strncmp(sLine.c_str(), "BURG ", 5)) { + int32_t nBurgNr = (int32_t)strtol(sLine.substr(5).c_str(), NULL, 10); + CBauwerk* poBuilding = oReport.GetBuilding(nBurgNr); + sLine = MutateCRBlock(oIS, poBuilding, oReport.m_bUTF8); + bGotLine = true; + } + else if (poUnit && !strncmp(sLine.c_str(), "COMMANDS", 8)) { + // TODO: Real UTF8-Handling + while (true) { + getline(oIS, sLine); + if (oIS.fail() || sLine[0] != '\x22') + break; + } + + if (!IsFlag(VF_FULLCOMMANDOUTPUT)) { + for (int32_t i = 0; i < (int32_t)poUnit->m_csKommandos.size(); i++) { + std::string strVal = Escape(poUnit->m_csKommandos[i].asString(), true); + if (!poUnit->m_csKommandos[i].empty()) + COutput::TPrintf("vorlage", "\x22%s\x22\n", (oReport.m_bUTF8 ? iso885915ToUtf8(strVal).c_str() : strVal.c_str())); + } + } + + for (int32_t i = 0; i < (int32_t)poUnit->m_csMetaOut.size(); i++) { + bool bAZ = false; + sCmd = poUnit->m_csMetaOut[i].asString(); + for (unsigned c = 0; c < sCmd.size(); c++) { + if (sCmd[c] == 34) { + bAZ = !bAZ; + sCmd[c] = ' '; + } + else if (bAZ && sCmd[c] < 33 && sCmd[c] > 0) + sCmd[c] = '~'; + + while (!sCmd.empty() && sCmd[sCmd.size() - 1] < 33 && sCmd[sCmd.size() - 1] > 0) + sCmd.erase(sCmd.size() - 1, 1); + } + std::string strVal = Escape(sCmd, true); + COutput::TPrintf("vorlage", "\x22%s\x22\n", (oReport.m_bUTF8 ? iso885915ToUtf8(strVal).c_str() : strVal.c_str())); + } + + if (poUnit->m_csMetaOut.empty() && (poUnit->m_csKommandos.empty() || IsFlag(VF_FULLCOMMANDOUTPUT))) { + COutput::TPrintf("vorlage", "\x22\x22\n"); + } + + bGotLine = true; + poUnit = 0; + } + } + } +} + +void CVorlage::WriteMap(const std::string& sFile) +{ + if (m_poCurrentReport && m_poKarte) { + std::fstream oIS, oOS; + std::string sLine; + std::string sCmd; + + oIS.open(m_poCurrentReport->m_sOrgCRName.c_str(), ios::in); + + if (oIS.fail()) { + ERRMSG(0, ("Auf die Report-Datei kann nicht zugegriffen werden!")); + EXIT(1); + } + + // TODO: Real UTF8-Handling + oOS.open(sFile.c_str(), ios::out); + if (oOS.fail()) { + ERRMSG(0, ("Karte kann nicht geschrieben werden!")); + EXIT(1); + } + + // Copy head from current report + while (1) { + // TODO: Real UTF8-Handling + getline(oIS, sLine); + if (oIS.fail()) + break; + + while (!sLine.empty() && sLine[sLine.size() - 1] < 32 && sLine[sLine.size() - 1] > 0) + sLine.erase(sLine.size() - 1, 1); + + if (!sLine.empty()) { + if (IsAlpha(sLine[0]) && sLine[0] != 'V' && sLine[0] != '\xef') + break; + if (!CRegExp::Match(sLine, "Passwort")) + oOS << sLine << std::endl; + // COutput::TPrintf( "vorlage", "%s\n", sLine.c_str() ); + } + } + bool utf8 = m_poCurrentReport->m_bUTF8; + for (RegionDB::iterator rmi = g_coRDB.begin(); rmi != g_coRDB.end(); rmi++) { + if ((*(*rmi).second.begin())->GetEZ()) { + oOS << "REGION " << (*(*rmi).second.begin())->GetEX() << " " << (*(*rmi).second.begin())->GetEY() << " " << (*(*rmi).second.begin())->GetEZ() << std::endl; + } + else { + oOS << "REGION " << (*(*rmi).second.begin())->GetEX() << " " << (*(*rmi).second.begin())->GetEY() << std::endl; + } + if (IsFlag(VF_EXPORTWITHROUND)) { + oOS << (*(*rmi).second.begin())->Runde() << ";Runde" << std::endl; + } + if ((*(*rmi).second.begin())->IsLand()) { + oOS << "\"" << iso2utf8((*(*rmi).second.begin())->GetName(), utf8) << "\";Name" << std::endl; + } + oOS << "\"" << iso2utf8((*(*rmi).second.begin())->GetRegionTypeName(), utf8) << "\";Terrain" << std::endl; + if (!(*(*rmi).second.begin())->GetIslandName().empty()) { + oOS << "\"" << iso2utf8((*(*rmi).second.begin())->GetIslandName(), utf8) << "\";Insel" << std::endl; + } + if ((*(*rmi).second.begin())->GetValue("herb").asString().length() > 3) { + oOS << "\"" << iso2utf8((*(*rmi).second.begin())->GetValue("herb").asString(), utf8) << "\";herb" << std::endl; + } + } + } +} + +///////////////////////////////////////////////////////////// +// Regionsvorlage erstellen +void CVorlage::Regionsvorlage(CRegion* poReg, CRegion* poReg2, CReport* poRep2) +{ + std::map coPersonen; + std::map coPersonen2; + std::map coWaffen; + std::set coBauwerke; + std::set coSchiffe; + std::set bewachendeParteien; + RegionDB::iterator rdbi; + std::ostringstream out; + CRegion* poRQ = 0; + CRegion::Materialpool coMPool, coMPool2; + int32_t nOrt = -1; + bool bRegHead = false; + bool bDiff = poReg2 && abs(poReg2->GetQuality() % 100) >= 5; + + g_csDupDescrFilter.clear(); + + CRegion::VEinheiten* poVE = &poReg->GetVEinheiten(); + m_poCurrentRegion = poReg; + g_poCurrentRegion = poReg; + + if (poReg->GetVBauwerke()) { + coBauwerke.insert(poReg->GetVBauwerke()->begin(), poReg->GetVBauwerke()->end()); + } + if (poReg->GetVSchiffe()) { + coSchiffe.insert(poReg->GetVSchiffe()->begin(), poReg->GetVSchiffe()->end()); + } + rdbi = g_coRDB.find(poReg->GetKey()); + if (rdbi != g_coRDB.end()) { + poRQ = *(*rdbi).second.begin(); + } + if (!poRQ) { + poRQ = poReg; + } + if (IsFlag(VF_SORTBURGEN) || IsFlag(VF_SORTTALENTE) || IsFlag(VF_SORTPRIVAT)) { + std::stable_sort(poReg->GetVEinheiten().begin(), poReg->GetVEinheiten().end(), CEinheitenSorter(0)); + } + for (size_t i = 0; i < poVE->size(); i++) { + if (coPersonen.find(poVE->operator[](i)->Partei()) == coPersonen.end()) { + coPersonen[poVE->operator[](i)->Partei()] = 0; + coWaffen[poVE->operator[](i)->Partei()] = 0; + } + coPersonen[poVE->operator[](i)->Partei()] += poVE->operator[](i)->Anzahl(); + if (poVE->operator[](i)->m_nBewacht) { + bewachendeParteien.insert(poVE->operator[](i)->Partei()); + } + if (true /* poVE->operator[](i)->Partei() == m_nPlayer */) { + std::string sInsel; + if (!poRQ->GetIslandName().empty() || !IsFlag(VF_SHOWVERBOSEINFO)) { + sInsel = std::string("[") + poRQ->GetIslandName() + std::string("]"); + } + else { + sInsel = ""; // poReg->GetIsland().asString(); + } + if (!bRegHead) { + if (IsFlag(VF_SHOWVERBOSEINFO)) { + COutput::TPrintf("vorlage", "\n; --------------------------------------------------------------\n\n"); + } + else { + COutput::TPrintf("vorlage", "\n"); + } + if (CMetaCommand::ProcExists("CreateRegionHeader")) { + VKommandos coOutput; + CMetaCommand::Call("CreateRegionHeader", coOutput); + if (!coOutput.empty()) { + for (size_t j = 0; j < coOutput.size(); j++) { + COutput::TPrintf("vorlage", " %s\n", coOutput[(int32_t)j].c_str()); + } + } + } + else { + if (IsEqual(g_poCurrentReport->m_sSpiel, "Verdanon")) { + if (!IsFlag(VF_SHOWVERBOSEINFO)) { + if (poReg->GetEZ()) { + COutput::TPrintf("vorlage", " ; %s (%d,%d,%d)\n", poReg->GetName().empty() ? poReg->GetRegionTypeName().c_str() : poReg->GetName().c_str(), poReg->GetEX(), poReg->GetEY(), poReg->GetEZ()); + } + else { + COutput::TPrintf("vorlage", " ; %s (%d,%d)\n", poReg->GetName().empty() ? poReg->GetRegionTypeName().c_str() : poReg->GetName().c_str(), poReg->GetEX(), poReg->GetEY()); + } + } + else if (poReg->GetBlock() == CRegion::enSPEZIALREGION) { + COutput::TPrintf("vorlage", " ; Astralebene (%s, %d Personen, %d$ Silber)\n", poReg->GetRegionTypeName().c_str(), poReg->PersonsOf(m_nPlayer, true), poReg->SilverOf(m_nPlayer)); + } + else if (poReg->GetEZ()) { + COutput::TPrintf("vorlage", " ; %s (%d,%d,%d) (%s, %d Personen, %d$ Silber) %s\n", poReg->GetName().c_str(), poReg->GetEX(), poReg->GetEY(), poReg->GetEZ(), poReg->GetRegionTypeName().c_str(), poReg->PersonsOf(m_nPlayer, true), + poReg->SilverOf(m_nPlayer), sInsel.c_str()); + } + else { + COutput::TPrintf("vorlage", " ; %s (%d,%d) (%s, %d Personen, %d$ Silber) %s\n", poReg->GetName().c_str(), poReg->GetEX(), poReg->GetEY(), poReg->GetRegionTypeName().c_str(), poReg->PersonsOf(m_nPlayer, true), + poReg->SilverOf(m_nPlayer), sInsel.c_str()); + } + } + else { + if (!IsFlag(VF_SHOWVERBOSEINFO)) { + if (poReg->GetEZ()) { + COutput::TPrintf("vorlage", " REGION $d,%d,%d ; %s\n", poReg->GetEX(), poReg->GetEY(), poReg->GetEZ(), poReg->GetName().empty() ? poReg->GetRegionTypeName().c_str() : poReg->GetName().c_str()); + } + else { + COutput::TPrintf("vorlage", " REGION %d,%d ; %s\n", poReg->GetEX(), poReg->GetEY(), poReg->GetName().empty() ? poReg->GetRegionTypeName().c_str() : poReg->GetName().c_str()); + } + } + else if (poReg->GetBlock() == CRegion::enSPEZIALREGION) { + COutput::TPrintf("vorlage", " REGION; Astralebene (%s, %d Personen, %d$ Silber)\n", poReg->GetRegionTypeName().c_str(), poReg->PersonsOf(m_nPlayer, true), poReg->SilverOf(m_nPlayer)); + } + else if (poReg->GetEZ()) { + COutput::TPrintf("vorlage", " REGION %d,%d,%d ; %s (%s, %d Personen, %d$ Silber) %s\n", poReg->GetEX(), poReg->GetEY(), poReg->GetEZ(), poReg->GetName().c_str(), poReg->GetRegionTypeName().c_str(), + poReg->PersonsOf(m_nPlayer, true), poReg->SilverOf(m_nPlayer), sInsel.c_str()); + } + else { + COutput::TPrintf("vorlage", " REGION %d,%d ; %s (%s, %d Personen, %d$ Silber) %s\n", poReg->GetEX(), poReg->GetEY(), poReg->GetName().c_str(), poReg->GetRegionTypeName().c_str(), poReg->PersonsOf(m_nPlayer, true), + poReg->SilverOf(m_nPlayer), sInsel.c_str()); + } + COutput::TPrintf("vorlage", " ; ECheck Lohn %d\n", poReg->GetLohn() ? poReg->GetLohn() : 10); + } + if (poReg->GetBlock() != CRegion::enSPEZIALREGION && IsFlag(VF_SHOWMINIKARTE)) { + static const std::set explicitResources{"Bauern", "Silber", "Unterhalt", "Rekruten", "Pferde", "Gewinn", "Pl. frei"}; + COutputTable OT; + std::string sT; + + if (IsFlag(VF_HEXMAP)) { + sT = " "; + sT += poReg->Map()->GetFromECords(poReg->GetEX() - 1, poReg->GetEY() + 1, poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX(), poReg->GetEY() + 1, poReg->GetEZ())->GetRegionChar(); + } + else { + sT = " "; + sT += poReg->Map()->GetFromECords(poReg->GetEX() - 1, poReg->GetEY() - 1, poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX(), poReg->GetEY() - 1, poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX() + 1, poReg->GetEY() - 1, poReg->GetEZ())->GetRegionChar(); + } + OT.Col(sT); + OT.Col("|").Col("Bauern:").Col(poReg->GetBauern()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetBauern() - poReg2->GetBauern()), COutputTable::enRIGHT); + } + OT.Col("|").Col("Silber:").Col(poReg->GetSilber()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetSilber() - poReg2->GetSilber()), COutputTable::enRIGHT); + } + OT.Col("|").Col("Unterhalt:").Col(poReg->GetUnterhalt()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetUnterhalt() - poReg2->GetUnterhalt()), COutputTable::enRIGHT); + } + OT.Col("|").Next(); + if (IsFlag(VF_HEXMAP)) { + sT = poReg->Map()->GetFromECords(poReg->GetEX() - 1, poReg->GetEY(), poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX() + 1, poReg->GetEY(), poReg->GetEZ())->GetRegionChar(); + } + else { + sT = " "; + sT += poReg->Map()->GetFromECords(poReg->GetEX() - 1, poReg->GetEY(), poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX() + 1, poReg->GetEY(), poReg->GetEZ())->GetRegionChar(); + } + OT.Col(sT + ' '); + if (IsFlag(VF_RESOURCEBLOCKS)) { + OT.Col("|").Col("Rekruten:").Col(poReg->GetRekruten()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetRekruten() - poReg2->GetRekruten()), COutputTable::enRIGHT); + } + OT.Col("|").Col("Pferde:").Col(poReg->GetPferde()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetPferde() - poReg2->GetPferde()), COutputTable::enRIGHT); + } + OT.Col("|").Col("Gewinn:").Col(poReg->CalcProfit()); + if (bDiff) { + OT.Col(ToStringS(poReg->CalcProfit() - poReg2->CalcProfit()), COutputTable::enRIGHT); + } + } + else { + OT.Col("|").Col("Rekruten:").Col(poReg->GetRekruten()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetRekruten() - poReg2->GetRekruten()), COutputTable::enRIGHT); + } + OT.Col("|").Col("Eisen:").Col(poReg->GetEisen() == -2 ? std::string("?") : (poReg->GetEisen() == -1 ? std::string("-") : ToString(poReg->GetEisen())), COutputTable::enRIGHT); + if (bDiff) { + OT.Col(poReg->GetEisen() >= 0 && poReg2->GetEisen() >= 0 ? ToStringS(poReg->GetEisen() - poReg2->GetEisen()) : std::string(""), COutputTable::enRIGHT); + } + OT.Col("|").Col("Gewinn:").Col(poReg->CalcProfit()); + if (bDiff) { + OT.Col(ToStringS(poReg->CalcProfit() - poReg2->CalcProfit()), COutputTable::enRIGHT); + } + } + OT.Col("|").Next(); + if (IsFlag(VF_HEXMAP)) { + sT = " "; + sT += poReg->Map()->GetFromECords(poReg->GetEX(), poReg->GetEY() - 1, poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX() + 1, poReg->GetEY() - 1, poReg->GetEZ())->GetRegionChar(); + } + else { + sT = " "; + sT += poReg->Map()->GetFromECords(poReg->GetEX() - 1, poReg->GetEY() + 1, poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX(), poReg->GetEY() + 1, poReg->GetEZ())->GetRegionChar(); + sT += ' '; + sT += poReg->Map()->GetFromECords(poReg->GetEX() + 1, poReg->GetEY() + 1, poReg->GetEZ())->GetRegionChar(); + } + OT.Col(sT); + if (IsFlag(VF_RESOURCEBLOCKS)) { + int nRCnt = 0; + OT.Col("|").Col("Pl. frei:").Col(poReg->CalcJobs() - poReg->GetBauern()); + if (bDiff) { + OT.Col(ToStringS((poReg->CalcJobs() - poReg->GetBauern()) - (poReg2->CalcJobs() - poReg2->GetBauern())), COutputTable::enRIGHT); + } + nRCnt++; + if (!poReg->GetResource("Baeume") && !poReg->GetResource("Mallorn") && (poReg->GetValue("Baeume").asLong() || (bDiff && !poReg2->GetResource("Baeume") && !poReg2->GetResource("Mallorn") && poReg2->GetBaeume()))) { + OT.Col("|").Col(poReg->isMallorn() ? "Mallorn:" : "Baeume:").Col(poReg->GetBaeume()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetBaeume() - poReg2->GetBaeume()), COutputTable::enRIGHT); + } + nRCnt++; + } + if (!poReg->GetResource("Schoesslinge") && !poReg->GetResource("Mallornschoesslinge") && + (poReg->GetValue("Schoesslinge").asLong() || (bDiff && !poReg2->GetResource("Schoesslinge") && !poReg2->GetResource("Mallornschoesslinge") && poReg2->GetValue("Schoesslinge").asLong()))) { + OT.Col("|").Col("Sch\xF6\xDFlinge:").Col(poReg->GetValue("Schoesslinge").asLong()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetValue("Schoesslinge").asLong() - poReg2->GetValue("Schoesslinge").asLong()), COutputTable::enRIGHT); + } + nRCnt++; + } + if (!poReg->GetResource("Steine") && (poReg->GetValue("Steine").asLong() || (bDiff && !poReg2->GetResource("Steine") && poReg2->GetValue("Steine").asLong()))) { + if (nRCnt && nRCnt % 3 == 0) { + OT.Col("|").Next(); + OT.Col(" "); + } + OT.Col("|").Col("Steine:").Col(poReg->GetValue("Steine").asLong()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetValue("Steine").asLong() - poReg2->GetValue("Steine").asLong()), COutputTable::enRIGHT); + } + nRCnt++; + } + // if( poReg->GetResourcen().size()>1 ) + { + std::string sName; + std::set coResourceSet; + CResource* pRes; + CResource* pRes1 = 0; + CResource* pRes2 = 0; + int32_t nNum1, nNum2, nSkill1; //, nSkill2; + // OT.Col("|").Col(" ").Col(" "); if( bDiff ) OT.Col(" "); + for (unsigned j = 0; j < poReg->GetResourcen().size(); j++) { + coResourceSet.insert(std::string(poReg->GetResourcen()[j]->GetValue("skill").asLong() ? "1" : "0") + Flatten(poReg->GetResourcen()[j]->GetValue("type").asString())); + } + if (bDiff) { + for (unsigned j = 0; j < poReg2->GetResourcen().size(); j++) { + coResourceSet.insert(std::string(poReg2->GetResourcen()[j]->GetValue("skill").asLong() ? "1" : "0") + Flatten(poReg2->GetResourcen()[j]->GetValue("type").asString())); + } + } + + for (std::set::const_iterator rsi = coResourceSet.begin(); rsi != coResourceSet.end(); rsi++) { + pRes1 = poReg->GetResource((*rsi).substr(1)); + if (bDiff) { + pRes2 = poReg2->GetResource((*rsi).substr(1)); + } + pRes = pRes1 ? pRes1 : pRes2; + sName = pRes->GetValue("type").asString(); + if (!explicitResources.count(sName)) { + if (nRCnt && nRCnt % 3 == 0) { + OT.Col("|").Next(); + OT.Col(" "); + } + nNum1 = pRes1 ? pRes1->GetValue("number").asLong() : 0; + nSkill1 = pRes1 ? pRes1->GetValue("skill").asLong() : 0; + nNum2 = pRes2 ? pRes2->GetValue("number").asLong() : 0; + // nSkill2 = pRes2 ? pRes2->GetValue( "skill" ).asLong() : 0; + OT.Col("|").Col(sName + (nSkill1 ? "(" + ToString(nSkill1) + "):" : ":")).Col(nNum1); + if (bDiff) { + if (pRes2) { + OT.Col(nNum2 ? ToStringS(nNum1 - nNum2) : std::string(""), COutputTable::enRIGHT); + } + else + OT.Col(" "); + } + nRCnt++; + } + } + while (nRCnt % 3) { + OT.Col("|").Col(" ").Col(" "); + if (bDiff) { + OT.Col(" "); + } + nRCnt++; + } + } + } + else { + OT.Col("|").Col("Pferde:").Col(poReg->GetPferde()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetPferde() - poReg2->GetPferde()), COutputTable::enRIGHT); + } + OT.Col("|").Col("Laen:").Col(poReg->GetLaen() == -2 ? std::string("?") : (poReg->GetLaen() == -1 ? std::string("-") : ToString(poReg->GetLaen())), COutputTable::enRIGHT); + if (bDiff) { + OT.Col(poReg->GetLaen() >= 0 ? ToStringS(poReg->GetLaen() - poReg2->GetLaen()) : std::string(""), COutputTable::enRIGHT); + } + OT.Col("|").Col(poReg->isMallorn() ? "Mallorn:" : "Baeume:").Col(poReg->GetBaeume()); + if (bDiff) { + OT.Col(ToStringS(poReg->GetBaeume() - poReg2->GetBaeume()), COutputTable::enRIGHT); + } + } + OT.Col("|").Next(); + + if (IsFlag(VF_SHOWLUXUS)) { + // static char Delta[32]; + // static char Fmt[8]; + int colcnt = 0; + for (size_t preis = 0; preis < poReg->GetLuxusgueter().size(); preis++) { + if (poReg->GetVerkauf() != (int32_t)preis) { + if (!(colcnt % 3)) { + OT.Col(" "); + } + OT.Col("|").Col(poReg->GetLuxusgueter()[preis].first + ":").Col(poReg->GetLuxusgueter()[preis].second); + if (bDiff) { + OT.Col((poReg2->GetLuxusgueter().size() > preis ? ToStringS(poReg->GetLuxusgueter()[preis].second - poReg2->GetLuxusgueter()[preis].second) : " "), COutputTable::enRIGHT); + } + if (colcnt && !((colcnt + 1) % 3)) { + OT.Col("|").Next(); + } + colcnt++; + } + } + // if( poReg->GetLuxusgueter().size() ) COutput::TPrintf( "vorlage", "\n" ); + } + + OT.Output("vorlage", std::string(" ; ")); + + if (IsFlag(VF_SHOWLUXUS) || IsFlag(VF_SHOWLPROD)) { + static char Delta[32]; + if (poReg->GetVerkauf() >= 0 && size_t(poReg->GetVerkauf()) < poReg->GetLuxusgueter().size()) { + COutput::TPrintf("vorlage", " ; Prod.: "); + if (bDiff) { + snprintf(Delta, sizeof(Delta), "%+5ld", + (poReg2->GetLuxusgueter().size() > size_t(poReg2->GetVerkauf())) ? poReg->GetLuxusgueter()[size_t(poReg->GetVerkauf())].second - poReg2->GetLuxusgueter()[size_t(poReg2->GetVerkauf())].second : 0); + } + else { + Delta[0] = 0; + } + COutput::TPrintf("vorlage", "%-10s%4d%s max. handelbar: %d\n", (poReg->GetLuxusgueter()[size_t(poReg->GetVerkauf())].first + ":").c_str(), poReg->GetLuxusgueter()[size_t(poReg->GetVerkauf())].second, Delta, + poReg->GetBauern() / 100); + } + } + if (IsFlag(VF_SHOWMINIKARTE)) { + if (poRQ->DeepGetValue("herb").asString().size() > 2) { + COutput::TPrintf("vorlage", " ; Kraut: %s\n", poRQ->DeepGetValue("herb").asString().c_str()); + } + } + + if (poReg->isVerorkt()) + COutput::TPrintf("vorlage", " ; Die Region ist verorkt!\n"); + + if (poReg->GetVGrenzen() && !poReg->GetVGrenzen()->empty()) { + std::ostringstream os; + for (size_t j = 0; j < poReg->GetVGrenzen()->size(); j++) { + std::string sGrenze; + switch ((*(poReg->GetVGrenzen()))[j] -> Richtung()) { + case 0: + sGrenze = "Nordwesten"; + break; + case 1: + sGrenze = "Nordosten"; + break; + case 2: + sGrenze = "Osten"; + break; + case 3: + sGrenze = "Suedosten"; + break; + case 4: + sGrenze = "Suedwesten"; + break; + case 5: + sGrenze = "Westen"; + break; + default: + sGrenze = "unbekannter Richtung"; + break; + } + os << " ; " << (*(poReg->GetVGrenzen()))[j] -> Typ() << " (" << (*(poReg->GetVGrenzen()))[j] -> Prozent() << "%) in " << sGrenze; + COutput::TPrintf("vorlage", "%s\n", getAndReset(os).c_str()); + } + } + } + + if (IsFlag(VF_SHOWBESCHREIBUNG) && !(poReg->Beschr().empty())) { + WrapOut(" ; ", poReg->Beschr(), (size_t)g_nLineSize); + } + if (IsFlag(VF_SHOWHANDEL)) { + int32_t nKosten = 0; + for (size_t j = 0; j < poVE->size(); j++) { + if (poVE->operator[](j)->Partei() == m_nPlayer && !poVE->operator[](j)->m_nVerraeter) { + nKosten += CRasse::Lookup(poVE->operator[](j)->Typ()).GetValue("Unterhalt").asLong() * poVE->operator[](j)->Anzahl(); + } + } + if (poReg->GetEinkommen() > 0) { + COutput::TPrintf("vorlage", " ; Regionseinnahmen:%6d Silber\n", poReg->GetEinkommen()); + } + if (poReg->GetAusgaben() > 0) { + COutput::TPrintf("vorlage", " ; Regionsausgaben: %6d Silber\n", poReg->GetAusgaben() + nKosten); + } + else if (nKosten) { + COutput::TPrintf("vorlage", " ; Nahrungskosten: %6d Silber\n", nKosten); + } + } + } + + if (IsFlag(VF_SHOWMATPOOL)) { + std::string sMat; + poReg->AddMaterialpool(m_nPlayer, coMPool); + for (CRegion::Materialpool::iterator mi = coMPool.begin(); mi != coMPool.end(); mi++) { + if (mi != coMPool.begin()) { + out << ", "; + } + sMat = (*mi).first; + if (!sMat.empty()) { + size_t p = 0; + sMat[0] = (char)toupper(sMat[0]); + while ((p = sMat.find_first_of(' ', p)) != std::string::npos) { + if (++p < sMat.size()) { + sMat[p] = (char)toupper(sMat[p]); + } + } + } + out << (*mi).second << " " << sMat; + } + if (!coMPool.empty()) { + WrapOut(" ; ", std::string("Materialpool: ") + getAndReset(out), (size_t)g_nLineSize, " ; "); + } + } + + if (!poReg->GetEffects().empty() && IsFlag(VF_SHOWVERBOSEINFO)) { + if (!bewachendeParteien.empty()) { + std::string bewachend; + for (auto pnr : bewachendeParteien) { + bewachend += (bewachend.empty() ? "" : ", ") + g_poCurrentReport->Parteiname(pnr).substr(1) + " (" + itoan(pnr, g_poCurrentReport->PNrBase()) + ")"; + } + WrapOut(" ; ", "Bewacht von: " + bewachend, (size_t)g_nLineSize); + } + for (size_t j = 0; j < poReg->GetEffects().size(); j++) { + WrapOut(" ; ", poReg->GetEffects()[j], (size_t)g_nLineSize); + } + } + + if (IsFlag(VF_SHOWMESSAGES)) { + for (CRegion::Botschaften::const_iterator bi = poReg->GetBotschaften().begin(); bi != poReg->GetBotschaften().end(); bi++) { + WrapOut(" ; ", (*bi), (size_t)g_nLineSize, " ; > "); + // COutput::TPrintf( "vorlage", " ; > %s\n", (*bi).c_str() ); + } + for (int32_t j = 0; j < (int32_t)poReg->NumMessage(); ++j) { + auto pMsg = poReg->GetMessage(j); + auto type = pMsg->GetValue("type", Value(0)); + if (type == -1 || !(IsFlag(VF_NOBATTLEMESSAGES) && IsEqual((*(g_poCurrentReport->MessageSections()))[pMsg->GetValue("type", Value(0)).asLong()], "battle"))) { + WrapOut(" ; ", pMsg->Render(g_poCurrentReport), (size_t)g_nLineSize, " ; > "); + } + } + CRegion::Durchreisen::const_iterator di; + for (di = poReg->GetDurchreisen().begin(); di != poReg->GetDurchreisen().end(); di++) { + COutput::TPrintf("vorlage", " ; Durchgereist: %s\n", (*di).c_str()); + } + for (di = poReg->GetDurchschiffungen().begin(); di != poReg->GetDurchschiffungen().end(); di++) { + COutput::TPrintf("vorlage", " ; Durchgesegelt: %s\n", (*di).c_str()); + } + } + + if (!poReg->GetKommandos().empty()) { + for (int32_t j = 0; j < (int32_t)poReg->GetKommandos().size(); j++) { + if (poReg->GetKommandos()[j].empty()) { + COutput::TPrintf("vorlage", "\n"); + } + else { + if (poReg->GetKommandos()[j].asString()[0] == ';') { + WrapOut(" ; ", poReg->GetKommandos()[j].asString().substr(2), (size_t)g_nLineSize); + } + } + } + } + if (!IsFlag(VF_SHOWVERBOSEINFO)) { + COutput::TPrintf("vorlage", "\n"); + } + if (poReg->PersonsOf(m_nPlayer) - poReg->PersonsOf(m_nPlayer, true) > 0) { + WrapOut(" ; ", std::string("In dieser Region sind Einheiten als (") + itoan(m_nPlayer, g_poCurrentReport->PNrBase()) + ") getarnt!", (size_t)g_nLineSize); + } + + bRegHead = true; + } + + if (!IsFlag(VF_SUPPRESSUNITS)) { + if (IsFlag(VF_SORTBURGEN) && nOrt != poReg->GetVEinheiten()[i]->Aufenthaltsort()) { + nOrt = poReg->GetVEinheiten()[i]->Aufenthaltsort(); + COutput::TPrintf("vorlage", "\n ; - - - - - - - - - - - -\n"); + if (!nOrt) { + COutput::TPrintf("vorlage", " ; Auf freiem Feld:\n"); + } + else if (nOrt > 0x10000000) { + if (poReg->GetShip(nOrt - 0x10000000)) { + coSchiffe.erase(poReg->GetShip(nOrt - 0x10000000)); + SchiffAusgabe(poReg->GetShip(nOrt - 0x10000000)); + } + } + else if (nOrt < 0x10000000) { + if (poReg->GetBuilding(nOrt)) { + coBauwerke.erase(poReg->GetBuilding(nOrt)); + BauwerkAusgabe(poReg->GetBuilding(nOrt), poVE); + } + } + } + + ///////////////////////////////////////////////////////////// + // Einheitenvorlage erstellen + if (poVE->operator[](i)->Partei() == m_nPlayer && !(poVE->operator[](i)->m_nVerraeter) && (g_nOnlyGroup < 0 || poVE->operator[](i)->m_nGruppe == g_nOnlyGroup)) { + Einheitenvorlage(poVE->operator[](i), poRep2); + } + else if (IsFlag(VF_SORTFOREIGN) || (poVE->operator[](i)->Partei() == m_nPlayer && !(poVE->operator[](i)->m_nVerraeter))) { + FremdEinheiten(poVE->operator[](i), poRep2); + } + } + } + } + + if (IsFlag(VF_SORTFOREIGN)) { + ShowInvisibles(poReg, poReg2, poRep2); + } + + if (IsFlag(VF_SORTBURGEN)) { + while (!coBauwerke.empty()) { + COutput::TPrintf("vorlage", "\n ; - - - - - - - - - - - -\n"); + BauwerkAusgabe(*(coBauwerke.begin())); + coBauwerke.erase(coBauwerke.begin()); + } + + while (!coSchiffe.empty()) { + COutput::TPrintf("vorlage", "\n ; - - - - - - - - - - - -\n"); + SchiffAusgabe(*(coSchiffe.begin())); + coSchiffe.erase(coSchiffe.begin()); + } + } + + if (!IsFlag(VF_SUPPRESSUNITS) && bRegHead && !IsFlag(VF_SORTFOREIGN) && (IsFlag(VF_SHOWUNITS) || IsFlag(VF_SHOWUNITSVERBOSE))) { + CEinheit* poHU; + CEinheit* poLU; + bool bHead = false; + for (size_t i = 0; i < poVE->size(); i++) { + poHU = poVE->operator[](i); + poLU = poRep2 ? poRep2->SearchUnit(poHU->Nummer(), false) : 0; + if ((!IsFlag(VF_SHOWUNITSNEW) || !poLU || (poLU && poLU->Region()->GetKey() != poHU->Region()->GetKey())) && (poHU->Partei() != m_nPlayer)) { + if (!bHead) { + COutput::TPrintf("vorlage", "\n ; - - - - - - - - - - - -\n"); + if (IsFlag(VF_SHOWUNITSNEW)) { + COutput::TPrintf("vorlage", " ; Neue fremde Einheiten:\n"); + } + else { + COutput::TPrintf("vorlage", " ; Fremde Einheiten:\n"); + } + bHead = true; + } + FremdEinheiten(poVE->operator[](i), poRep2); + } + } + } + + if (!IsFlag(VF_SUPPRESSUNITS) && bRegHead && !IsFlag(VF_SORTFOREIGN) && (IsFlag(VF_SHOWUNITS) || IsFlag(VF_SHOWUNITSVERBOSE))) { + ShowInvisibles(poReg, poReg2, poRep2); + } + + if (bRegHead && IsFlag(VF_SHOWTRIBEOVERVIEW) && coPersonen.size() > 1) { + COutput::TPrintf("vorlage", "\n ; - - - - - - - - - - - -\n"); + COutput::TPrintf("vorlage", + " ; Partei\xFC" + "bersicht:\n"); + std::string sPfx; + + if (poReg2 && IsFlag(VF_SHOWTDIFF)) { + CRegion::VEinheiten* poVEt = &poReg2->GetVEinheiten(); + for (size_t i = 0; i < poVEt->size(); i++) { + if (coPersonen2.find(poVEt->operator[](i)->Partei()) == coPersonen2.end()) { + coPersonen2[poVEt->operator[](i)->Partei()] = 0; + } + + coPersonen2[poVEt->operator[](i)->Partei()] += poVEt->operator[](i)->Anzahl(); + } + } + + for (std::map::iterator ppi = coPersonen.begin(); ppi != coPersonen.end(); ppi++) { + if ((*ppi).first != m_nPlayer) { + std::map::iterator ppi2 = coPersonen2.find((*ppi).first); + + COutput::TPrintf("vorlage", "\n"); + out.str(""); + out.clear(); + + std::string sPName = g_poCurrentReport->m_coParteien[(*ppi).first]; + if (sPName.empty()) { + sPName = "#"; + } + + sPfx = " ; "; + sPfx += (*ppi).first ? sPName[0] : '-'; + + if ((*ppi).first >= 0) { + out << " "; + out << sPName.substr(1); + out << " ("; + out << itoan((*ppi).first, g_poCurrentReport->PNrBase()); + out << ")"; + } + else { + out << " parteigetarnt"; + } + + if (IsFlag(VF_SHOWTDIFF) && ppi2 != coPersonen2.end() && (*ppi).second != (*ppi2).second) { + out << ", " << (*ppi).second << "(" << std::showpos << ((*ppi).second - (*ppi2).second) << std::noshowpos << ((*ppi).second == 1 ? ") Person" : ") Personen"); + } + else { + out << ", " << (*ppi).second << ((*ppi).second == 1 ? " Person" : " Personen"); + } + + if (IsFlag(VF_SHOWMATPOOL)) { + CRegion::Materialpool::iterator mi2; + std::string sMat; + coMPool.clear(); + poReg->AddMaterialpool((*ppi).first, coMPool); + int32_t nMat2 = 0; + if (ppi2 != coPersonen2.end()) { + coMPool2.clear(); + poReg2->AddMaterialpool((*ppi2).first, coMPool2); + } + for (CRegion::Materialpool::iterator mi = coMPool.begin(); mi != coMPool.end(); mi++) { + if (ppi2 != coPersonen2.end()) { + mi2 = coMPool2.find((*mi).first); + if (mi2 != coMPool2.end()) { + nMat2 = (*mi2).second; + } + else { + nMat2 = 0; + } + } + if (mi != coMPool.begin()) { + out << ", "; + } + else { + out << ", hat: "; + } + sMat = (*mi).first; + if (!sMat.empty()) { + sMat[0] = (char)toupper(sMat[0]); + } + if (IsFlag(VF_SHOWTDIFF) && (*mi).second != nMat2) { + out << (*mi).second << "(" << std::showpos << ((*mi).second - nMat2) << std::noshowpos << ") " << sMat; + } + else { + out << (*mi).second << " " << sMat; + } + } + } + + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize, sPfx); + } + } + } + if (bRegHead && !poReg->GetEndKommandos().empty()) { + for (int32_t i = 0; i < (int32_t)poReg->GetEndKommandos().size(); i++) { + if (poReg->GetEndKommandos()[i].empty()) { + COutput::TPrintf("vorlage", "\n"); + } + else { + if (poReg->GetEndKommandos()[i].asString()[0] == ';') { + WrapOut(" ; ", poReg->GetEndKommandos()[i].asString().substr(2), (size_t)g_nLineSize); + } + } + } + } +} + +void CVorlage::ShowInvisibles(CRegion* poReg, CRegion* poReg2, CReport* poRep2) +{ + EinheitenDB* poUnits; + EinheitenDB::iterator ui; + CEinheit* poHU; + bool bHead = false; + + poUnits = &g_coREDB[poReg->GetKey()]; + + for (ui = poUnits->begin(); ui != poUnits->end(); ui++) { + if ((*ui).second->Partei() != m_nPlayer) { + poHU = g_poCurrentReport->SearchUnit((*ui).second->Nummer(), false); + if (!poHU) { + EinheitenDB::iterator edbi; + edbi = g_coREinheitenDB[0].find((*ui).second->Nummer()); + if (edbi != g_coREinheitenDB[0].end()) { + const CEinheit* pUT = (*edbi).second; + if (pUT) { + const CRegion* pRH = (*edbi).second->Region(); + if (pRH && pRH->GetKey() == poReg->GetKey()) { + if (!bHead) { + COutput::TPrintf("vorlage", "\n ; - - - - - - - - - - - -\n"); + COutput::TPrintf("vorlage", " ; Unsichtbare oder getarnte fremde Einheiten:\n"); + bHead = true; + } + FremdEinheiten((*ui).second, poRep2); + } + } + } + } + } + } +} + +void CVorlage::BauwerkAusgabe(CBauwerk* pBuilding, CRegion::VEinheiten* poVE) +{ + int nPers = 0; + int nUnits = 0; + if (poVE) { + for (size_t j = 0; j < poVE->size(); j++) { + if (pBuilding->Nummer() == poVE->operator[](j)->Aufenthaltsort()) { + nPers += poVE->operator[](j)->Anzahl(); + ++nUnits; + } + } + } + int32_t nKap = CBlockBase::GetValue(CBuildingInfo::Lookup(pBuilding->XTyp()), "kapazitaet").asLong(); + int32_t nCountUnits = CBlockBase::GetValue(CBuildingInfo::Lookup(pBuilding->XTyp()), "einheiten").asLong(); + COutput::TPrintf("vorlage", " ; In %s '%s' (%s) [%d/%d%s]:\n", pBuilding->XTyp().c_str(), pBuilding->Name().c_str(), itoan(pBuilding->Nummer(), g_poCurrentReport->BNrBase()), nCountUnits > 0 ? nUnits : nPers, nKap ? nKap : pBuilding->Groesse(), + nKap && nKap != pBuilding->Groesse() ? std::string("/" + std::to_string(pBuilding->Groesse())).c_str() : ""); + + if (IsFlag(VF_SHOWBESCHREIBUNG) && !(pBuilding->Beschreibung().empty())) { + WrapOut(" ; ", pBuilding->Beschreibung(), (size_t)g_nLineSize); + } + + if (pBuilding->m_nBelagerer) { + COutput::TPrintf("vorlage", " ; Belagert von: %d\n", pBuilding->m_nBelagerer); + } + if (!pBuilding->m_coEffects.empty() && IsFlag(VF_SHOWVERBOSEINFO)) { + for (size_t i = 0; i < pBuilding->m_coEffects.size(); i++) { + WrapOut(" ; ", pBuilding->m_coEffects[i], (size_t)g_nLineSize); + } + } + + if (!pBuilding->GetKommandos().empty()) { + for (int32_t i = 0; i < (int32_t)pBuilding->GetKommandos().size(); i++) { + if (pBuilding->GetKommandos()[i].empty()) { + COutput::TPrintf("vorlage", "\n"); + } + else { + if (pBuilding->GetKommandos()[i].asString()[0] == ';') { + WrapOut(" ; ", pBuilding->GetKommandos()[i].asString().substr(2), (size_t)g_nLineSize); + } + } + } + } +} + +void CVorlage::SchiffAusgabe(CSchiff* pSchiff) +{ + int32_t count = pSchiff->Anzahl(); + std::string anzahl = count == 1 ? "" : ", Anzahl " + std::to_string(count) + ","; + // static int dbgcount = 0; + if (pSchiff->MaxHolz() && pSchiff->MaxHolz() * count != pSchiff->Holz()) { + COutput::TPrintf("vorlage", " ; An Bord von %s '%s' (%s)%s (%d/0) im Bau (%d/%d):\n", pSchiff->Typ().c_str(), pSchiff->Name().c_str(), itoan(pSchiff->Nummer(), g_poCurrentReport->BNrBase()), anzahl.c_str(), pSchiff->Ladung(), pSchiff->Holz(), + pSchiff->MaxHolz() * count); + } + else { + COutput::TPrintf("vorlage", " ; An Bord von %s '%s' (%s)%s Kap: %dGE/%dGE(%d%%):\n", pSchiff->Typ().c_str(), pSchiff->Name().c_str(), itoan(pSchiff->Nummer(), g_poCurrentReport->BNrBase()), anzahl.c_str(), pSchiff->MaxLadung() - pSchiff->Ladung(), + pSchiff->MaxLadung(), pSchiff->Schaden()); + } + if (IsFlag(VF_SHOWBESCHREIBUNG) && !(pSchiff->Beschreibung().empty())) { + WrapOut(" ; ", pSchiff->Beschreibung(), (size_t)g_nLineSize); + } + + if (!pSchiff->m_coEffects.empty() && IsFlag(VF_SHOWVERBOSEINFO)) { + for (size_t i = 0; i < pSchiff->m_coEffects.size(); i++) { + WrapOut(" ; ", pSchiff->m_coEffects[i], (size_t)g_nLineSize); + } + } + + if (!pSchiff->GetKommandos().empty()) { + for (int32_t i = 0; i < (int32_t)pSchiff->GetKommandos().size(); i++) { + if (pSchiff->GetKommandos()[i].empty()) { + COutput::TPrintf("vorlage", "\n"); + } + else { + if (pSchiff->GetKommandos()[i].asString()[0] == ';') { + WrapOut(" ; ", pSchiff->GetKommandos()[i].asString().substr(2), (size_t)g_nLineSize); + } + } + } + } +} + +void CVorlage::Einheitenvorlage(CEinheit* poUnit, CReport* poRep2) +{ + // VKommandos coMetaErg; + CEinheit* poLU; + std::ostringstream out; + const char* pcKampf; + // bool bMetas = false; + bool bKommando = false; + size_t i; + + m_poCurrentUnit = poUnit; + g_poCurrentUnit = poUnit; + + poLU = poRep2 ? poRep2->SearchUnit(poUnit->Nummer(), false) : 0; + + if (poUnit->m_nSchiff) { + out << "," << ((m_poCurrentRegion->GetShip(poUnit->m_nSchiff) && m_poCurrentRegion->GetShip(poUnit->m_nSchiff)->Kapitaen() == poUnit->m_nNummer) ? 'S' : 's') << ":" << itoan(poUnit->m_nSchiff, g_poCurrentReport->BNrBase()); + } + if (IsEqual(poUnit->m_sTyp.c_str(), "Untote") || (0 == CRasse::Lookup(poUnit->RealType()).GetValue(std::string("Unterhalt")).asLong() && 0 != CRasse::Lookup(poUnit->RealType()).GetValue(std::string("Gewicht")).asLong())) { + out << ",I"; + } + if (poUnit->m_nBauwerk) { + CBauwerk* pBW = m_poCurrentRegion->GetBuilding(poUnit->m_nBauwerk); + int nI = pBW ? pBW->Groesse() : 0; + if (pBW) { + int32_t nKap = CBlockBase::GetValue(CBuildingInfo::Lookup(pBW->XTyp()), "kapazitaet").asLong(); + if (nKap) { + nI = nKap; + } + } + if (pBW && (pBW->m_nBesitzer == poUnit->m_nNummer || (!pBW->m_nBesitzer && poUnit->m_nPlace == 1))) { + bKommando = true; + } + if (bKommando && pBW->Unterhalt()) { + out << ",U" << pBW->Unterhalt(); + } + if (IsFlag(VF_SHOWVERBOSEINFO)) { + out << "," << (bKommando ? 'B' : 'b') << ":" << itoan(poUnit->m_nBauwerk, g_poCurrentReport->BNrBase()) << "(" << poUnit->m_nPlace << "/" << nI << ")"; + } + } + + static bool bReadKampfStati = false; + static std::map csKampfStatiMap; + if (!bReadKampfStati) { + bReadKampfStati = true; + CConfigFile oCF(GetConfigFileName()); + size_t j = 1; + while (true) { + if (!oCF.FetchLine("Kampfstatus", j++, false)) { + break; + } + csKampfStatiMap[oCF.GetLong(1)] = oCF.GetString(0); + } + } + if (!csKampfStatiMap.empty() && csKampfStatiMap.find(poUnit->m_nKampfStatus) != csKampfStatiMap.end()) { + pcKampf = csKampfStatiMap[poUnit->m_nKampfStatus].c_str(); + } + else { + if (IsFlag(VF_NEWERESSEASTATI)) { + switch (poUnit->m_nKampfStatus) { + case 0: + pcKampf = "aggressiv"; + break; + case 1: + pcKampf = "vorne"; + break; + case 2: + pcKampf = "hinten"; + break; + case 3: + pcKampf = "defensiv"; + break; + case 4: + pcKampf = "k\xE4mpft nicht"; + break; + case 5: + pcKampf = "flieht"; + break; + default: + pcKampf = "unbekannt"; + } + } + else { + switch (poUnit->m_nKampfStatus) { + case 0: + pcKampf = "vorne"; + break; + case 1: + pcKampf = "hinten"; + break; + case 2: + pcKampf = "k\xE4mpft nicht"; + break; + case 3: + pcKampf = "flieht"; + break; + default: + pcKampf = "unbekannt"; + } + } + } + + if (CMetaCommand::ProcExists("CreateUnitHeader")) { + VKommandos coOutput; + CMetaCommand::Call("CreateUnitHeader", coOutput); + if (!coOutput.empty()) { + for (int32_t j = 0; j < (int32_t)coOutput.size(); j++) { + COutput::TPrintf("vorlage", " %s\n", coOutput[j].c_str()); + } + } + } + else { + // if( IsFlag( VF_BASE36 ) ) + if (IsFlag(VF_SHOWVERBOSEINFO)) { + COutput::TPrintf("vorlage", "\n EINHEIT %s; %s [%d,%d$%s] %s%s%s%s%s%s\n", itoan(poUnit->m_nNummer, g_poCurrentReport->ENrBase()), poUnit->m_sName.c_str(), poUnit->m_nAnzahl, poUnit->m_nSilber, getAndReset(out).c_str(), + (!poUnit->WahrerTyp().empty()) ? (poUnit->Typ() + std::string(", ")).c_str() : "", poUnit->m_nParteitarnung ? "parteigetarnt, " : "", poUnit->m_nBewacht ? "bewacht, " : "", + poUnit->m_shp.empty() ? "" : std::string(poUnit->m_shp + ", ").c_str(), pcKampf, (poUnit->m_nHunger) ? ", hungert" : ""); + if (poUnit->m_nVerkleidung) { + COutput::TPrintf("vorlage", " ; Verkleidet als %s (%s)\n", poUnit->Region()->Map()->Report()->Parteiname(poUnit->m_nVerkleidung).substr(1).c_str(), itoan(poUnit->m_nVerkleidung, g_poCurrentReport->PNrBase())); + } + if (poUnit->m_nVerraeter) { + COutput::TPrintf("vorlage", " ; VERR\xC4TER!\n"); + } + // else + // COutput::TPrintf( "vorlage", "\n EINHEIT %6d; %s [%d,%d$%s] %s%s%s%s%s%s\n", poUnit->m_nNummer, poUnit->m_sName.c_str(), poUnit->m_nAnzahl, poUnit->m_nSilber, Buff, (!poUnit->m_sWahrerTyp.empty())?poUnit->m_sTyp.c_str():"", + // poUnit->m_nParteitarnung?"parteigetarnt, ":"", poUnit->m_nBewacht?"bewacht, ":"", poUnit->m_shp.empty()?"":std::string( poUnit->m_shp + ", " ).c_str(), pcKampf, (poUnit->m_nHunger)?", hungert":"" ); + } + else { + COutput::TPrintf("vorlage", " EINHEIT %s; %s [%d,%d$%s]\n", itoan(poUnit->m_nNummer, g_poCurrentReport->ENrBase()), poUnit->m_sName.c_str(), poUnit->m_nAnzahl, poUnit->m_nSilber, getAndReset(out).c_str()); + } + if (!poUnit->Gruppe().empty() && IsFlag(VF_SHOWVERBOSEINFO)) { + WrapOut(" ; In Gruppe: ", poUnit->Gruppe(), (size_t)g_nLineSize); + } + if (poUnit->CBlockBase::GetValue("hero").asLong() && IsFlag(VF_SHOWVERBOSEINFO)) { + COutput::TPrintf("vorlage", " ; Heldenstatus!\n"); + } + + if (poUnit->GetValue("unaided", "").asLong()) { + COutput::TPrintf("vorlage", " ; Bekommt im Kampf keine Hilfe!\n"); + } + + if (IsFlag(VF_SHOWBESCHREIBUNG) && !(poUnit->Beschreibung().empty())) { + std::string sDescr = poUnit->m_sBeschreibung; + if (IsFlag(VF_STRIPDUPLICATEDESCR)) { + if (sDescr.length() > 60 && g_csDupDescrFilter.insert(sDescr).second) { + sDescr = sDescr.substr(0, 57) + " ..."; + } + } + WrapOut(" ; ", sDescr, (size_t)g_nLineSize); + } + + if (IsFlag(VF_SHOWLASTEN)) { + poUnit->Kapazitaeten("vorlage"); + } + if (poUnit->m_nSchiff && m_poCurrentRegion->GetShip(poUnit->m_nSchiff) && m_poCurrentRegion->GetShip(poUnit->m_nSchiff)->Kapitaen() == poUnit->m_nNummer && IsFlag(VF_SHOWVERBOSEINFO)) { + std::string sKueste = "auf offener See"; + // NW = 0, NO = 1, O = 2, SO = 3, SW =4, W =5 + if (m_poCurrentRegion->GetTerrain()->m_bLand) { + sKueste = ", auslaufen nach "; + switch (m_poCurrentRegion->GetShip(poUnit->m_nSchiff)->Kueste()) { + case 0: + sKueste += "Nordwesten"; + break; + case 1: + sKueste += "Nordosten"; + break; + case 2: + sKueste += "Osten"; + break; + case 3: + sKueste += "Suedosten"; + break; + case 4: + sKueste += "Suedwesten"; + break; + case 5: + sKueste += "Westen"; + break; + default: + sKueste = ", Ablegerichtung beliebig"; + break; + } + } + out << "Kapitaen von " << m_poCurrentRegion->GetShip(poUnit->m_nSchiff)->Name() << " (" << itoan(m_poCurrentRegion->GetShip(poUnit->m_nSchiff)->Nummer(), g_poCurrentReport->BNrBase()) << ") " << sKueste; + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize); + } + + if (IsFlag(VF_SHOWPRIVAT) && !poUnit->m_sPrivat.empty()) { + WrapOut(" ; ", std::string("Privat: ") + poUnit->m_sPrivat, (size_t)g_nLineSize); + } + + if (!poUnit->m_coEffects.empty() && IsFlag(VF_SHOWVERBOSEINFO)) { + for (size_t j = 0; j < poUnit->m_coEffects.size(); j++) { + WrapOut(" ; ", poUnit->m_coEffects[j], (size_t)g_nLineSize); + } + } + + if (IsFlag(VF_SHOWMESSAGES)) { + for (CEinheit::Botschaften::const_iterator bi = poUnit->m_coBotschaften.begin(); bi != poUnit->m_coBotschaften.end(); bi++) { + WrapOut(" ; ", (*bi), (size_t)g_nLineSize, " ; "); + // COutput::TPrintf( "vorlage", " ; %s\n", (*bi).c_str() ); + } + for (CEinheit::Messages::const_iterator mi = poUnit->m_cpoMessages.begin(); mi != poUnit->m_cpoMessages.end(); mi++) { + WrapOut(" ; ", ((CMessage*)((*mi).get()))->Render(g_poCurrentReport), (size_t)g_nLineSize, " ; > "); + } + } + + if (IsFlag(VF_SHOWTALENTE)) { + if (poUnit->m_coSprueche.size()) { + out << "Spr\xFC" + "che: "; + for (i = 0; i < poUnit->m_coSprueche.size(); i++) { + if (i) { + out << ", "; + } + out << poUnit->m_coSprueche[i]; + } + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize); + } + if (poUnit->m_cpoKampfzauber.size()) { + out << "Kampfzauber: "; + for (i = 0; i < poUnit->m_cpoKampfzauber.size(); i++) { + if (i) { + out << ", "; + } + out << poUnit->m_cpoKampfzauber[i]->GetValue("name").asString() << " " << poUnit->m_cpoKampfzauber[i]->GetValue("level").asLong(); + } + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize); + } + + for (i = 0; i < poUnit->m_coTalente.size(); i++) { + if (i) { + out << ", "; + } + if (poUnit->m_nTarnung >= 0 && IsEqual(poUnit->m_coTalente[i].m_sTyp.c_str(), "Tarnung")) { + if (IsFlag(VF_NOSKILLPOINTS)) { + if (IsFlag(VF_SHOWTDIFF) && poLU && (poUnit->m_coTalente[i].m_nStufe - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Stufe").asLong())) { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_nTarnung << "/" << poUnit->m_coTalente[i].m_nStufe << " (" << std::showpos + << (poUnit->m_coTalente[i].m_nStufe - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Stufe").asLong()) << std::noshowpos << ")"; + } + else { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_nTarnung << "/" << poUnit->m_coTalente[i].m_nStufe; + } + } + else { + if (IsFlag(VF_SHOWTDIFF) && poLU && (poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Tage").asLong() / poLU->m_nAnzahl)) { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_nTarnung << "/" << poUnit->m_coTalente[i].m_nStufe << " [" << poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl << "(" << std::showpos + << (poLU ? poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Tage").asLong() / poLU->m_nAnzahl : 0) << std::noshowpos << ")]"; + } + else { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_nTarnung << "/" << poUnit->m_coTalente[i].m_nStufe << " [" << poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl << "]"; + } + } + } + else { + if (IsFlag(VF_NOSKILLPOINTS)) { + if (IsFlag(VF_SHOWTDIFF) && poLU && (poUnit->m_coTalente[i].m_nStufe - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Stufe").asLong())) { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_coTalente[i].m_nStufe << " (" << std::showpos << (poUnit->m_coTalente[i].m_nStufe - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Stufe").asLong()) << std::noshowpos + << ")"; + } + else { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_coTalente[i].m_nStufe; + } + } + else { + if (IsFlag(VF_SHOWTDIFF) && poLU && (poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Tage").asLong() / poLU->m_nAnzahl)) { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_coTalente[i].m_nStufe << " [" << poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl << "(" << std::showpos + << (poLU ? poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl - poLU->GetValue(poUnit->m_coTalente[i].m_sTyp, "Tage").asLong() / poLU->m_nAnzahl : 0) << std::noshowpos << ")]"; + } + else { + out << poUnit->m_coTalente[i].m_sTyp << " " << poUnit->m_coTalente[i].m_nStufe << " [" << poUnit->m_coTalente[i].m_nTage / poUnit->m_nAnzahl << "]"; + } + } + } + if (IsEqual(poUnit->m_coTalente[i].m_sTyp.c_str(), "Magie") && poUnit->m_nAuramax > 0) { + if (IsFlag(VF_SHOWTDIFF) && poLU && (poUnit->m_nAura - poLU->m_nAura)) { + out << ", Aura " << poUnit->m_nAura << "(" << std::showpos << (poUnit->m_nAura - poLU->m_nAura) << std::noshowpos << ")"; + } + else { + out << ", Aura " << poUnit->m_nAura; + } + if (IsFlag(VF_SHOWTDIFF) && poLU && (poUnit->m_nAuramax - poLU->m_nAuramax)) { + out << " [" << poUnit->m_nAuramax << "(" << std::showpos << (poUnit->m_nAuramax - poLU->m_nAuramax) << std::noshowpos << ")]"; + } + else { + out << " [" << poUnit->m_nAuramax << "]"; + } + } + } + if (i) { + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize); + } + } + if (IsFlag(VF_SHOWGEGENSTAENDE)) { + bool bSome = false; + for (i = 0; i < poUnit->m_coGegenstaende.size(); i++) { + if (!IsEqual(poUnit->m_coGegenstaende[i].first, "Silber")) { + if (bSome) { + out << ", "; + } + out << poUnit->m_coGegenstaende[i].second << " " << poUnit->m_coGegenstaende[i].first.c_str(); + bSome = true; + } + } + if (bSome) { + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize); + } + } + } + + if (!IsFlag(VF_FULLCOMMANDOUTPUT)) { + for (int32_t j = 0; j < (int32_t)poUnit->m_csKommandos.size(); j++) { + if (poUnit->m_csMetaOut.empty() || IsFlag(VF_DONTKILLCOMMANDS)) { + COutput::TPrintf("vorlage", " %s\n", poUnit->m_csKommandos[j].c_str()); + } + else { + auto p = poUnit->m_csKommandos[j].asString().find_first_not_of(" \t"); + if (poUnit->m_csKommandos[j].empty() || (p != std::string::npos && (poUnit->m_csKommandos[j].asString()[p] == '/' || poUnit->m_csKommandos[j].asString()[p] == ';'))) { + COutput::TPrintf("vorlage", " %s\n", poUnit->m_csKommandos[j].c_str()); + } + else { + poUnit->m_csKommandos[j] = Value(""); + } + } + } + } + + for (int32_t j = 0; j < (int32_t)poUnit->m_csMetaOut.size(); j++) { + if (!poUnit->m_csMetaOut[j].empty() && poUnit->m_csMetaOut[j].asString()[0] == ';') { + WrapOut(" ; ", poUnit->m_csMetaOut[j].c_str(), (size_t)g_nLineSize, " "); + } + else if (!poUnit->m_csMetaOut[j].empty() && poUnit->m_csMetaOut[j].asString()[0] == '/') { + COutput::TPrintf("vorlage", " %s\n", poUnit->m_csMetaOut[j].c_str()); + } + else { + std::string sLine = poUnit->m_csMetaOut[j].c_str(); + std::string sPref; + size_t nLSize = (size_t)g_nCommandLineSize - 4; + while (sLine.length() > nLSize) { + if (sLine[nLSize - 2] == ' ') { + COutput::TPrintf("vorlage", " %s%s\\\n", sPref.c_str(), sLine.substr(0, nLSize - 1).c_str()); + sLine.erase(0, nLSize - 1); + } + else { + COutput::TPrintf("vorlage", " %s%s\\\n", sPref.c_str(), sLine.substr(0, nLSize - 2).c_str()); + sLine.erase(0, nLSize - 2); + } + if (sPref.empty()) { + nLSize -= 4; + sPref = " "; + } + } + COutput::TPrintf("vorlage", " %s%s\n", sPref.c_str(), sLine.c_str()); + } + // COutput::TPrintf( "vorlage", " %s\n", poUnit->m_csMetaOut[i].c_str() ); + } + if (IsFlag(VF_FULLCOMMANDOUTPUT) && !poUnit->GetMetaOut().changed()) { + // Emulation des Verhaltens ohne --fullcom f�r Einheiten ohne Ergebnisse durch das Script + for (int32_t j = 0; j < (int32_t)poUnit->m_csKommandos.size(); j++) { + auto p = poUnit->m_csKommandos[j].asString().find_first_not_of(" \t"); + if (!poUnit->m_csKommandos[j].empty() && p != std::string::npos && poUnit->m_csKommandos[j].asString()[p] != '/') { + COutput::TPrintf("vorlage", " %s\n", poUnit->m_csKommandos[j].c_str()); + } + } + } +} + +void CVorlage::FremdEinheiten(CEinheit* poUnit, CReport* poRep2) +{ + CEinheit* poHU; + CEinheit* poLU; + std::ostringstream out; + std::string sPfx; + + poHU = poUnit; + poLU = poRep2 ? poRep2->SearchUnit(poUnit->Nummer(), false) : 0; + + if (!IsFlag(VF_SHOWUNITSNEW) || !poLU || (poLU && poLU->Region()->GetKey() != poHU->Region()->GetKey()) || (poLU && poHU->Partei() != poLU->Partei())) { + COutput::TPrintf("vorlage", "\n"); + + std::string sPName = g_poCurrentReport->m_coParteien[poUnit->m_nPartei]; + if (sPName.empty()) { + sPName = "#"; + } + + sPfx = " ; "; + sPfx += poUnit->m_nPartei ? sPName[0] : '-'; + + poHU = g_poCurrentReport->SearchUnit(poUnit->Nummer(), false); + if (!poHU) { + sPfx += "!"; + } + else { + if (!poUnit->GetQuality() || poUnit->m_nVerraeter || (poUnit->GetQuality() > poHU->GetQuality() && (sPName[0] != '+' || !poHU->GetQuality()))) { + sPfx += "!"; + } + else { + sPfx += " "; + } + } + + // Name und Einheitennummer + out << poUnit->m_sName << " (" << itoan(poUnit->m_nNummer, g_poCurrentReport->ENrBase()) << ")"; + + // Partei + if (poUnit->m_nPartei >= 0) { + out << ", "; + if (poUnit->m_nVerraeter) { + out << "getarnt als "; + } + out << sPName.substr(1) << " (" << itoan(poUnit->m_nPartei, g_poCurrentReport->PNrBase()) << ")"; + } + else { + out << ", parteigetarnt"; + } + if (poUnit->m_nVerraeter) { + out << ", Verr\xE4ter"; + } + if (!poUnit->Gruppe().empty()) { + out << ", Mitglied in " << poUnit->Gruppe(); + } + // Anzahl und Typ + if (!poUnit->m_sTyp.empty()) { + std::string sTyp = poUnit->m_sWahrerTyp.empty() ? poUnit->PrefixedTyp() : poUnit->PrefixedTyp(true); + std::string sITyp = poUnit->m_sWahrerTyp.empty() ? poUnit->Typ() : poUnit->WahrerTyp(); + if (poUnit->m_nAnzahl == 1 && sTyp.size()) { + if (sITyp[sITyp.size() - 1] == 'n' && sITyp[0] != 'K') { + sTyp.erase(sTyp.size() - 2); + } + else { + sTyp.erase(sTyp.size() - 1); + } + } + out << ", " << poUnit->m_nAnzahl << " " << sTyp; + } + + // Aus Richtung + if (poRep2) { + if (poLU && (poLU->Region()->GetKey() != poUnit->Region()->GetKey())) { + char b[32]; + out << ", aus " << (poLU->Region()->GetName().empty() && poLU->Region()->GetTerrain() ? poLU->Region()->GetTerrain()->m_sName : poLU->Region()->GetName()) << " (" << poLU->Region()->GetEX() << "," << poLU->Region()->GetEY() << ")"; + } + } + + static bool bReadSilverMask = true; + static std::map coSilverMasks; + if (bReadSilverMask) { + bReadSilverMask = false; + CConfigFile oCF(GetConfigFileName()); + size_t i = 1; + while (true) { + if (!oCF.FetchLine("SilverMasks", i++, false)) { + break; + } + coSilverMasks[oCF.GetLong(1)] = oCF.GetString(0); + } + } + + // Verbose-Infos + if (IsFlag(VF_SHOWUNITSVERBOSE)) { + if (poUnit->m_nSilber || !poUnit->m_coGegenstaende.empty()) { + out << ", hat: "; + } + if (poUnit->m_nSilber) { + if (poUnit->GetQuality() == 10) { + out << poUnit->m_nSilber << " Silber"; + } + else { + if (coSilverMasks.empty()) { + if (poUnit->m_nSilber >= 5000) { + out << " Silberkassette"; + } + else { + out << " Silberbeutel"; + } + } + else { + for (std::map::const_iterator i = coSilverMasks.begin(); i != coSilverMasks.end(); i++) { + if (poUnit->m_nSilber >= (*i).first) { + out << (poUnit->m_nSilber / (*i).first) << (*i).second; + break; + } + } + } + } + } + + // Gegenstaende + bool bSome = false; + for (size_t gi = 0; gi < poUnit->m_coGegenstaende.size(); gi++) { + if (!IsEqual(poUnit->m_coGegenstaende[gi].first, "Silber")) { + if (bSome || poUnit->m_nSilber) { + out << ", "; + } + out << poUnit->m_coGegenstaende[gi].second << " " << poUnit->m_coGegenstaende[gi].first; + bSome = true; + } + } + + // Talente + for (size_t ti = 0; ti < poUnit->m_coTalente.size(); ti++) { + if (!ti) { + out << ", Talente: "; + } + else { + out << ", "; + } + out << poUnit->m_coTalente[ti].m_sTyp << " " << poUnit->m_coTalente[ti].m_nStufe << "[" << (poUnit->m_coTalente[ti].m_nTage / poUnit->m_nAnzahl) << "]"; + } + + // Beschreibung + if (!poUnit->m_sBeschreibung.empty()) { + if (IsFlag(VF_STRIPDUPLICATEDESCR)) { + if (poUnit->m_sBeschreibung.length() < 60 || !g_csDupDescrFilter.insert(poUnit->m_sBeschreibung).second) { + out << "; " << poUnit->m_sBeschreibung; + } + else { + out << "; " << poUnit->m_sBeschreibung.substr(0, 57) << " ..."; + } + } + else { + out << "; " << poUnit->m_sBeschreibung; + } + } + } + + // Ausgabe + WrapOut(" ; ", getAndReset(out), (size_t)g_nLineSize, sPfx); + } +} + +static bool OpenOXFile(const std::string& sRName, std::string& sOXName, bool bForce = false) +{ + std::string sName; + + if (sOXName.empty()) { + return false; + } + + sName = sRName; + if (sName.length() > 3 && IsEqual(sName.substr(sName.length() - 3).c_str(), ".cr")) { + sName.erase(sName.length() - 2); + if (sOXName[0] == '.') { + sOXName.erase(0, 1); + } + sName += sOXName; + + if (!bForce && FileExists(sName)) { + ERRMSG(0, ("FEHLER: Datei '%s' existiert bereits!\n", sName.c_str())); + } + else { + COutput::SetTarget("vorlage", new COutput(sName)); + // g_hOut = fopen( sName.c_str(), "w" ); + if (!COutput::Target("vorlage")->IsOkay()) { + ERRMSG(0, ("FEHLER: Datei '%s' konnte nicht fuer die Ausgabe ge\xF6" + "ffnet werden!\n", + sName.c_str())); + return false; + } + return true; + } + } + return false; +} + +extern "C" { +static void ExitHandler() +{ + // CRNENode::ClearRules(); + CHierarchy::Free(); + Expression::clearAllVars(); + COutput::CloseTargets(); + CloseAllOpenFiles(); +} +} + +///////////////////////////////////////////////////////////////////// +//.block: main +///////////////////////////////////////////////////////////////////// +// -f -k -t -g -n -l -uv -sb -st -ox zv -i beispiel.vms +int main(int argc, char* argv[]) +{ + // CrashDumpHandler oCDH( std::string( VERSIONINFO ) + "(Build " + ToString( (int32_t)BUILDNUMBER ) + ")" ); + CKarte::IslandQueue cpoQueue; + std::vector coArgs; + std::vector coScripts; + std::vector coReports; + std::vector cpoReports; + std::vector coPlayers; + std::string sPlayer; + std::string sMapOutName; + std::string sConfigPath; + clock_t nStart; + clock_t nTStart; + std::string sOName; + std::string sOXName; + // std::string sOPName; + std::string sGroupName; + bool bForce = false; + bool bTime = true; + // bool bCROut = false; + bool bLocalHeader = true; + int32_t nPlayer = -1; + int nTX, nTY; + unsigned int i = 1; + size_t j; + // int p; + +#ifdef _MSC_VER + _old_new_handler = _set_new_handler(my_new_handler); +#endif + + atexit(ExitHandler); + + setvbuf(stdout, 0, _IONBF, 0); + setvbuf(stderr, 0, _IONBF, 0); + + COutput::SetTarget("stdout", new COutput(stdout)); + COutput::SetTarget("stderr", new COutput(stderr)); + COutput::SetTarget("vorlage", new COutput(stdout)); + COutput::SetTarget("debug", new COutput(stderr)); + COutput::SetTarget("trace", new COutput(stderr)); + if (IsConsole(stdout)) + COutput::SetTarget("console", new COutput(stdout)); + else if (IsConsole(stderr)) + COutput::SetTarget("console", new COutput(stderr)); + else { + COutput::SetTarget("console", new COutput(stderr)); + g_coFlags.insert(VF_NOCONSOLE); + } + + // printf("sizeof:\nCExpression::Variables: %d\nstd::vector: %d\nCMCI: %d\n", + // sizeof(Expression::Variables), sizeof(std::vector), sizeof(CMCI) ); + /* + printf("sizeof(CRegion)=%d\n", sizeof(CRegion)); + printf("sizeof(DummyRegion)=%d\n", sizeof(DummyRegion)); + printf("sizeof(CBlockBase)=%d\n", sizeof(CBlockBase)); + printf("sizeof(Value)=%d\n", sizeof(Value)); + */ + g_pfErrorFunc = VorlageErrorMsg; + + g_oScriptBase.Import("", "#func EMR_building $BNr\n{\n$BNr=itoan(antoi($BNr,10),36)\n#return building[$BNr].name+' ('+$BNr+')'\n}\n"); + g_oScriptBase.Import("", "#func EMR_eq $Val1 $Val2\n{\n#return $Val1==$Val2\n}\n"); + g_oScriptBase.Import("", "#func EMR_faction $PNr\n{\n$PNr=itoan(antoi($PNr,10),36)\n#return partei[$PNr].parteiname+' ('+$PNr+')'\n}\n"); + g_oScriptBase.Import("", "#func EMR_if $CC $Val1 $Val2\n{\n#if $CC=='0'\n{\n#return $Val2\n}\n#else\n{\n#return $Val1\n}\n}\n"); + g_oScriptBase.Import("", "#func EMR_int $Val\n{\n#return $Val\n}\n"); + g_oScriptBase.Import("", "#func EMR_isnull $Val\n{\n#return !$Val||$Val=='-1'\n}\n"); + g_oScriptBase.Import("", + "#func EMR_region $RegInfo\n{\n#if $($RegInfo).z\n{\n#return $($RegInfo).name+' ('+$($RegInfo).x+','+$($RegInfo).y+','+$($RegInfo).z+')'\n}\n#else\n{\n#return $($RegInfo).name+' ('+$($RegInfo).x+','+$($RegInfo).y+')'\n}\n}\n"); + g_oScriptBase.Import("", "#func EMR_resource $Resource $Wanted\n{\n#return $Resource\n}"); + g_oScriptBase.Import("", "#func EMR_skill $Skill\n{\n#return $Skill\n}\n"); + g_oScriptBase.Import("", "#func EMR_order $Order\n{\n#return $Order\n}\n"); + g_oScriptBase.Import("", "#func EMR_unit $ENr\n{\n$ENr=itoan(antoi($ENr,10),36)\n#return unit[$ENr].name+' ('+$ENr+')'\n}\n"); + g_oScriptBase.Import("", "#func EMR_strlen $String\n{\n#return length($String)\n}\n"); + + Value oBuild(int32_t(PBEMTOOLS_BUILD_NUMBER_EMU)); + Value oVersion{std::string(PBEMTOOLS_VERSION_STRING_SHORT)}; + Expression::setConstant("BUILDNUMBER", &oBuild); + Expression::setConstant("TOOLVERSION", &oVersion); + + if (clock() == (clock_t)-1) { + TRACEMSG(("Keine Zeitmessung m\xF6glich!\n")); + bTime = false; + } + nTStart = clock(); + + setlocale(LC_CTYPE, "German"); + SetBreakHandler(MyBreak); + + char* pOpts = getenv("VORLAGEOPTIONS"); + if (pOpts) { + split(coArgs, std::string(pOpts), std::string(" \t"), std::string("\0x22'"), '\\'); + } + + for (j = 1; j < (size_t)argc; j++) { + if (strlen(argv[j]) && argv[j][0] == '@' && strcmp(argv[j - 1], "-o")) { + std::fstream oIS; + std::string sLine; + oIS.open(&argv[j][1], ios::in); + + if (oIS.fail()) { + ERRMSG(0, ("FEHLER: Steuerdatei-Datei '%s' konnte nicht ge\xF6" + "ffnet werden!\n", + &argv[j][1])); + } + else { + while (true) { + getline(oIS, sLine); + if (oIS.fail()) { + break; + } + while (!sLine.empty() && sLine[sLine.size() - 1] < 32 && sLine[sLine.size() - 1] > 0) { + sLine.erase(sLine.size() - 1, 1); + } + split(coArgs, sLine, std::string(" \t"), std::string("\0x22'"), '\\'); + } + } + } + else if (!strcmp(argv[j], "--cfgpath")) { + j++; + if (j < (size_t)argc) { + sConfigPath = argv[j]; + } + } + else + coArgs.push_back(std::string(argv[j])); + } + + i = 0; + +// g_sConfigPathName = argv[0]; +#ifdef _WIN32 + if (sConfigPath.empty()) { + g_sConfigPathName = GetExecutablePathname(); + } + else { + g_sConfigPathName = sConfigPath; + if (sConfigPath[sConfigPath.length() - 1] != '/' && sConfigPath[sConfigPath.length() - 1] != '\\') + g_sConfigPathName += "/"; + } + auto p = g_sConfigPathName.find_last_of("\\/:"); + if (p == std::string::npos) { + g_sConfigPathName = ""; + } + else { + g_sConfigPathName.erase(p + 1); + } + g_sConfigPathName += g_sSpiel + std::string(".cfg"); +#else + if (sConfigPath.empty()) { + g_sConfigPathName = getenv("HOME"); + } + else { + g_sConfigPathName = sConfigPath; + } + if (!g_sConfigPathName.empty() || g_sConfigPathName[g_sConfigPathName.size() - 1] != '/') { + g_sConfigPathName += "/"; + } + g_sConfigPathName += std::string(".") + g_sSpiel + std::string("rc"); +#endif + + for (j = 0; j < coArgs.size(); j++) { + if (!strcmp("-o", coArgs[j].c_str()) || !strcmp("-ox", coArgs[j].c_str())) { + COutput::SetTarget("stderr", new COutput(stdout)); + } + else if (!strcmp("-q", coArgs[i].c_str())) { + bTime = false; + } + else if (!strcmp("-v", coArgs[i].c_str())) { + bLocalHeader = false; + } + } + + for (j = 0; j < coArgs.size(); j++) { + if (!strcmp("-e", coArgs[j].c_str())) { + if (++j >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer die Fehlerausgabe mit Option '-e'!\n")); + EXIT(1); + } + COutput::SetTarget("stderr", new COutput(coArgs[j], true)); + // g_hErr = fopen( coArgs[i].c_str(), "w" ); + if (!COutput::Target("stderr")->IsOkay()) { + ERRMSG(0, ("FEHLER: Datei '%s' konnte nicht fuer die Ausgabe geoeffnet werden!\n", coArgs[j].c_str())); + EXIT(1); + } + g_bError = true; + } + } + + if (bTime && bLocalHeader) { + CONMSG(("\n%s [Build %d],\n(C) Copyright 1999-2019 by S.Schuemann\n", VERSIONINFO, PBEMTOOLS_BUILD_NUMBER_EMU)); + } + + for (size_t aix = 0; aix < coArgs.size(); aix++) { + g_sCmdOptions += std::string(" ") + coArgs[aix]; + } + + g_coFlags.insert(VF_SHOWVERBOSEINFO); + bool explicitCommandLineSize = false; + while (i < coArgs.size()) { + if (!strcmp("-sb", coArgs[i].c_str())) { + g_coFlags.insert(VF_SORTBURGEN); + } + else if (!strcmp("-sp", coArgs[i].c_str())) { + g_coFlags.insert(VF_SORTPRIVAT); + } + else if (!strcmp("-st", coArgs[i].c_str())) { + g_coFlags.insert(VF_SORTTALENTE); + } + else if (!strcmp("-sk", coArgs[i].c_str())) { + g_coFlags.insert(VF_SORTKOMMANDO); + } + else if (!strcmp("-si", coArgs[i].c_str())) { + g_coFlags.insert(VF_SORTISLANDS); + } + else if (!strcmp("-cr", coArgs[i].c_str())) { + g_coFlags.insert(VF_CROUTPUT); + } + else if (!strcmp("-t", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWTALENTE); + g_coFlags.insert(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-td", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWTALENTE); + g_coFlags.insert(VF_SHOWTDIFF); + g_coFlags.insert(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-ts", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWEMULATEDDAYS); + } + else if (!strcmp("-d", coArgs[i].c_str())) { + g_coFlags.insert(VF_DEBUGMODE); + } + else if (!strcmp("-f", coArgs[i].c_str())) { + bForce = true; + } + else if (!strcmp("-fd", coArgs[i].c_str())) { + CMetaCommand::ForceDeclares(true); + g_coFlags.insert(VF_FORCEDECLARES); + } + else if (!strcmp("-fr", coArgs[i].c_str())) { + CMessage::ForceRendering(true); + } + else if (!strcmp("--fullcom", coArgs[i].c_str())) { + g_coFlags.insert(VF_FULLCOMMANDOUTPUT); + } + else if (!strcmp("--fullregs", coArgs[i].c_str())) { + g_coFlags.insert(VF_RUNALLVISIBLEREGIONS); + } + else if (!strcmp("-g", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWGEGENSTAENDE); + g_coFlags.insert(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-b", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWBESCHREIBUNG); + g_coFlags.insert(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-hb", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWHANDEL); + } + else if (!strcmp("-k", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWMINIKARTE); + g_coFlags.insert(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-kl", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWMINIKARTE); + g_coFlags.insert(VF_SHOWLUXUS); + g_coFlags.insert(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-klp", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWMINIKARTE); + g_coFlags.insert(VF_SHOWLUXUS); + g_coFlags.insert(VF_SHOWLPROD); + g_coFlags.insert(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-kk", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWKOMPKARTE); + } + else if (!strcmp("--kkall", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWWORLDKARTE); + } + else if (!strcmp("-l", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWLASTEN); + } + else if (!strcmp("-m", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWMATPOOL); + } + else if (!strcmp("-more", coArgs[i].c_str())) { + g_coFlags.insert(VF_PAGER); + } + else if (!strcmp("-mi", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Anzahl der Durchlaeufe fehlt fuer die Option '-mi'!\n")); + EXIT(1); + } + g_nMinPasses = atoi(coArgs[i].c_str()); + if (g_nMinPasses < 0 || g_nMinPasses > 10) { + ERRMSG(0, ("FEHLER: Falscher Wert fuer die Anzahl der Durchlaeufe (0-10)!\n")); + EXIT(1); + } + } + else if (!strcmp("-map", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer die Option '-map'!\n")); + EXIT(1); + } + sMapOutName = coArgs[i]; + } + else if (!strcmp("--mapr", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer die Option '--mapr'!\n")); + EXIT(1); + } + sMapOutName = coArgs[i]; + g_coFlags.insert(VF_EXPORTWITHROUND); + } + else if (!strcmp("-n", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWMESSAGES); + } + else if (!strcmp("--no-battle-messages", coArgs[i].c_str())) { + g_coFlags.insert(VF_NOBATTLEMESSAGES); + } + else if (!strcmp("-nrzv", coArgs[i].c_str())) { + g_coFlags.erase(VF_SHOWVERBOSEINFO); + } + else if (!strcmp("-nv", coArgs[i].c_str())) { + g_coFlags.insert(VF_SUPPRESSTURNOUTPUT); + } + else if (!strcmp("-q", coArgs[i].c_str())) { + bTime = false; + } + else if (!strcmp("-v", coArgs[i].c_str())) { + TRACEMSG(("\n%s,\n(C) Copyright 1999-2019 by S.Schuemann\n", VERSIONINFO)); + exit(0); + } + else if (!strcmp("-p", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Parteinummer fuer die Option '-p'!\n")); + EXIT(1); + } + sPlayer = coArgs[i]; + nPlayer = 0; + } + else if (!strcmp("-gr", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Gruppenname fuer die Option '-gr'!\n")); + EXIT(1); + } + sGroupName = coArgs[i]; + } + else if (!strcmp("-cfg", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Spielname fuer die Option '-cfg'!\n")); + EXIT(1); + } + g_sSpiel = coArgs[i]; + } + else if (!strcmp("--output-encoding", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Encoding fuer die Option '--output-encoding'!\n")); + EXIT(1); + } + if (!CharacterMapper::IsSupported(coArgs[i])) { + ERRMSG(0, ("FEHLER: Encoding '%s' wird nicht unterstuetzt!\n", coArgs[i].c_str())); + EXIT(1); + } + COutput::SetEncoding(coArgs[i]); + } + else if (!strcmp("-et", coArgs[i].c_str())) { + g_coFlags.insert(VF_TRACEONERROR); + } + else if (!strcmp("-pb", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWPRIVAT); + } + else if (!strcmp("-pi", coArgs[i].c_str())) { + g_coFlags.insert(VF_PROGRESSINFO); + } + else if (!strcmp("-pm", coArgs[i].c_str())) { + g_coFlags.insert(VF_PRIVATMETA); + } + else if (!strcmp("-u", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWUNITS); + } + else if (!strcmp("-uv", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWUNITSVERBOSE); + } + else if (!strcmp("-us", coArgs[i].c_str())) { + g_coFlags.insert(VF_SORTFOREIGN); + } + else if (!strcmp("-up", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWTRIBEOVERVIEW); + } + else if (!strcmp("-un", coArgs[i].c_str())) { + g_coFlags.insert(VF_SHOWUNITSNEW); + } + else if (!strcmp("-uq", coArgs[i].c_str())) { + g_coFlags.insert(VF_SUPPRESSUNITS); + } + else if (!strcmp("-rc", coArgs[i].c_str())) { + g_coFlags.insert(VF_DONTKILLCOMMANDS); + } + else if (!strcmp("-wait", coArgs[i].c_str())) { + g_bWait = true; + } + else if (!strcmp("-ws", coArgs[i].c_str())) { + g_coFlags.insert(VF_SUPPRESSKEYWARN); + } + else if (!strcmp("-wall", coArgs[i].c_str())) { + g_coFlags.insert(VF_NOWARNINGS); + } + else if (!strcmp("-wfirst", coArgs[i].c_str())) { + g_coFlags.insert(VF_SUPPRESSMULTIERRORS); + } + else if (!strcmp("--pedantic", coArgs[i].c_str())) { + g_coFlags.insert(VF_DIAGPEDANTIC); + } + else if (!strcmp("--version2", coArgs[i].c_str())) { + g_coFlags.insert(VF_VERSION2WARNING); + } + else if (!strcmp("--strip-duplicate-descr", coArgs[i].c_str())) { + g_coFlags.insert(VF_STRIPDUPLICATEDESCR); + } + else if (!strcmp("--restricted", coArgs[i].c_str())) { + g_coFlags.insert(VF_RESTRICTED); + } + else if (!strcmp("--fix-encodings", coArgs[i].c_str())) { + g_coFlags.insert(VF_FIXENCODINGS); + } + else if (!strcmp("-w", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Zeilenlaenge fuer die Option '-w'!\n")); + EXIT(1); + } + g_nLineSize = atoi(coArgs[i].c_str()); + if (g_nLineSize < 40) { + ERRMSG(0, ("FEHLER: Zeilenlaenge darf nicht kleiner als 40 sein!\n")); + EXIT(1); + } + } + else if (!strcmp("-wc", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Zeilenlaenge fuer die Option '-wc'!\n")); + EXIT(1); + } + g_nCommandLineSize = atoi(coArgs[i].c_str()); + explicitCommandLineSize = true; + if (g_nCommandLineSize < 40) { + ERRMSG(0, ("FEHLER: Zeilenlaenge darf nicht kleiner als 40 sein!\n")); + EXIT(1); + } + } + else if (!strcmp("-cl", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Groessenangabe fuer die Option '-cl'!\n")); + EXIT(1); + } + g_nContainerLimit = atoi(coArgs[i].c_str()); + if (g_nLineSize < 16) { + ERRMSG(0, ("FEHLER: Behaelterlimit darf nicht kleiner als 16 sein!\n")); + EXIT(1); + } + } + else if (!strcmp("--rseed", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Initialisierungswert fuer die Option '--rseed'!\n")); + EXIT(1); + } + Random((int32_t)strtol(coArgs[i].c_str(), NULL, 0)); + } + else if (!strcmp("--limit-runtime", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Zeit fuer die Option '--limit-runtime'!\n")); + EXIT(1); + } + g_nLimitRuntime = (int32_t)strtol(coArgs[i].c_str(), NULL, 0); + } + else if (!strcmp("-e", coArgs[i].c_str())) { + ++i; + /* + if( ++i>=coArgs.size() ) + { + ERRMSG( 0, ( "FEHLER: Kein Dateiname fuer die Fehlerausgabe mit Option '-e'!\n" )); + EXIT( 1 ); + } + COutput::SetTarget( "stderr", new COutput( coArgs[i], true ) ); +// g_hErr = fopen( coArgs[i].c_str(), "w" ); + if( !COutput::Target( "stderr" )->IsOkay() ) + { + ERRMSG( 0, ( "FEHLER: Datei '%s' konnte nicht fuer die Ausgabe geoeffnet werden!\n", coArgs[i].c_str() )); + EXIT( 1 ); + } + g_bError = true; + */ + } + else if (!strcmp("-do", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer die Debugausgabe mit Option '-do'!\n")); + EXIT(1); + } + COutput::SetTarget("debug", new COutput(coArgs[i], true)); + // g_hErr = fopen( coArgs[i].c_str(), "w" ); + if (!COutput::Target("debug")->IsOkay()) { + ERRMSG(0, ("FEHLER: Datei '%s' konnte nicht fuer die Ausgabe geoeffnet werden!\n", coArgs[i].c_str())); + EXIT(1); + } + } + else if (!strcmp("-to", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer die Traceausgabe mit Option '-to'!\n")); + EXIT(1); + } + COutput::SetTarget("trace", new COutput(coArgs[i], true)); + // g_hErr = fopen( coArgs[i].c_str(), "w" ); + if (!COutput::Target("trace")->IsOkay()) { + ERRMSG(0, ("FEHLER: Datei '%s' konnte nicht fuer die Ausgabe geoeffnet werden!\n", coArgs[i].c_str())); + EXIT(1); + } + } + else if (!strcmp("-pw", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Passwort fuer die Option '-pw'!\n")); + EXIT(1); + } + CReport::SetDefaultPassword(coArgs[i].c_str()); + } + else if (!strcmp("-o", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer die Ausgabe mit Option '-o'!\n")); + EXIT(1); + } + + sOName = coArgs[i]; + } + else if (!strcmp("-ox", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Dateierweiterung fuer die Ausgabe mit Option '-ox'!\n")); + EXIT(1); + } + sOXName = coArgs[i].c_str(); + } + /* + else if( !strcmp( "-op", coArgs[i].c_str() ) ) + { + if( ++i>=coArgs.size() ) + { + ERRMSG( 0, ( "FEHLER: Keine Dateierweiterung fuer die Ausgabe mit Option '-ox'!\n" )); + EXIT( 1 ); + } + + sOPName = coArgs[i].c_str(); + } + */ + else if (!strcmp("-i", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer Import!\n")); + EXIT(1); + } + coScripts.push_back(coArgs[i]); + } + else if (!strcmp("-xn", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer den Namensregeln!\n")); + EXIT(1); + } + nStart = clock(); + if (bTime) { + TRACEMSG(("lese '%s'...", coArgs[i].c_str())); + } + try { + CRNENode::ReadRules(coArgs[i]); + } + catch (CRNEException e) { + ERRMSG(0, ("FEHLER: Syntaxfehler in Namensregeln: %s\n", e.why().c_str())); + EXIT(1); + } + if (bTime) { + TRACEMSG((" (%1.2f sek.)\n", float(clock() - nStart) / CLOCKS_PER_SEC)); + } + } + else if (!strcmp("-insel", coArgs[i].c_str())) { + int ic; + //-kk -k -t -g -n -l -sb -st 491403.cr 491404.cr >test.out 568 + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Kein Dateiname fuer den Inseltest!\n")); + EXIT(1); + } + + nStart = clock(); + if (bTime) { + TRACEMSG(("lese '%s'...", coArgs[i].c_str())); + } + CReport oR(coArgs[i].c_str()); + if (bTime) { + TRACEMSG((" (%1.2f sek.)\n", float(clock() - nStart) / CLOCKS_PER_SEC)); + } + oR.Karte()->DumpFullMap("stdout", ""); + if (bTime) { + TRACEMSG(("ermittle Inseln...")); + } + nStart = clock(); + ic = oR.Karte()->Islandize(); + if (bTime) { + TRACEMSG((" %d gefunden. (%1.2f sek.)\n", ic, float(clock() - nStart) / CLOCKS_PER_SEC)); + } + EXIT(1); + } + else if (!strcmp("-mr", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Koordinatenoffsets fuer die Option '-mr'!\n")); + EXIT(1); + } + nTX = atoi(coArgs[i].c_str()); + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Nur ein Koordinatenoffset fuer die Option '-mr'!\n")); + EXIT(1); + } + nTY = atoi(coArgs[i].c_str()); + CRegion::SetMoveOffset(nTX, nTY); + if (i >= coArgs.size()) { + EXIT(1); + } + } + else if (*(coArgs[i].c_str()) == '-') { + ERRMSG(0, ("FEHLER: Unbekannte Option '%s'!\n", coArgs[i].c_str())); + EXIT(1); + } + else { + break; + } + i++; + } + + if (!explicitCommandLineSize) { + g_nCommandLineSize = g_nLineSize; + } + + if (i < coArgs.size()) { + CReport* poRep1; + CReport* poRep2; + int nActRound = 0; + int nPrevRound; + CVorlage oV; + CReport* pNewRep; + std::string sName; + std::string sSpiel; + while (i < coArgs.size()) { + while (!strcmp("-mr", coArgs[i].c_str())) { + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Keine Koordinatenoffsets fuer die Option '-mr'!\n")); + EXIT(1); + } + nTX = atoi(coArgs[i].c_str()); + if (++i >= coArgs.size()) { + ERRMSG(0, ("FEHLER: Nur ein Koordinatenoffset fuer die Option '-mr'!\n")); + EXIT(1); + } + nTY = atoi(coArgs[i].c_str()); + CRegion::SetMoveOffset(nTX, nTY); + if (++i >= coArgs.size()) { + EXIT(1); + } + } + if (bTime) { + TRACEMSG(("lese '%s'...", coArgs[i].c_str())); + } + sName = coArgs[i++].c_str(); + nStart = clock(); + pNewRep = new CReport(sName); + if (sSpiel.empty() && pNewRep->HasSpiel()) { + sSpiel = pNewRep->Spiel(); + } + if (pNewRep->HasSpiel() && sSpiel != pNewRep->Spiel()) { + ERRMSG(0, ("Warnung: Wiederspruechliche Spielangaben in den CRs!\n")); + } + if (pNewRep->IsValid()) { + for (j = 0; j < cpoReports.size(); j++) { + if (pNewRep->Partei() == cpoReports[j]->Partei()) { + break; + } + } + if (j == cpoReports.size()) { + if (pNewRep) { + coPlayers.push_back(pNewRep->Partei()); + } + if (bTime) { + TRACEMSG((" (%1.2f sek.), Partei %s.\n", float(clock() - nStart) / CLOCKS_PER_SEC, itoan(pNewRep->Partei(), pNewRep->PNrBase()))); + } + } + else { + if (bTime) { + TRACEMSG((" (%1.2f sek.), wieder Partei %s.\n", float(clock() - nStart) / CLOCKS_PER_SEC, itoan(pNewRep->Partei(), pNewRep->PNrBase()))); + } + } + cpoReports.push_back(pNewRep); + pNewRep->Karte()->FillIslandQueue(cpoQueue); + } + else { + TRACEMSG(("Fehler beim Einlesen!\n")); + delete pNewRep; + } + } + + for (i = 0; i < cpoReports.size(); i++) { + if (nActRound < cpoReports[i]->Runde()) { + nActRound = cpoReports[i]->Runde(); + } + } + + if (!nActRound || !coPlayers.size()) { + ERRMSG(0, ("FEHLER: Kein Vorlagentauglicher Basisreport gefunden!\n")); + EXIT(1); + } + + if (!coScripts.empty()) { + bool bOk; + for (std::vector::iterator si = coScripts.begin(); si != coScripts.end(); si++) { + nStart = clock(); + if (bTime) { + TRACEMSG(("importiere '%s'...", (*si).c_str())); + } + bOk = g_oScriptBase.Import((*si)); + if (bTime) { + TRACEMSG((" (%1.2f sek.)\n", float(clock() - nStart) / CLOCKS_PER_SEC)); + } + if (!bOk) { + EXIT(1); + } + } + } + + if (bTime) { + TRACEMSG(("Aktuelle Runde: %d\n", nActRound)); + } + nStart = clock(); + if (bTime) { + TRACEMSG(("Regions- und Einheitendatenbanken werden aufgebaut...")); + } + CVorlage::InitDBS(nActRound, cpoReports); + + if (bTime) { + TRACEMSG((" (%1.2f sek.)\n", float(clock() - nStart) / CLOCKS_PER_SEC)); + } + if (!cpoQueue.empty()) { + if (bTime) { + TRACEMSG(("werte %d Insel Tags aus...", cpoQueue.size())); + } + nStart = clock(); + + CVorlage::Islandize(cpoQueue, g_coRDB); + + if (bTime) { + TRACEMSG((" (%1.2f sek.)\n", float(clock() - nStart) / CLOCKS_PER_SEC)); + } + } + + nStart = clock(); + if (bTime) { + TRACEMSG(("erzeuge Zugvorlage(n)...")); + } + CRegion::SetCurrentRound(nActRound); + + bool bFoundActivePlayer = false; + + try { + for (j = 0; j < coPlayers.size(); j++) { + if (coPlayers[j] > 0) { + poRep1 = 0; + poRep2 = 0; + nPrevRound = 0; + + for (i = 0; i < cpoReports.size(); i++) { + if (!poRep1 && cpoReports[i]->Partei() == coPlayers[j] && cpoReports[i]->Runde() == nActRound) { + poRep1 = cpoReports[i]; + } + if (cpoReports[i]->Partei() == coPlayers[j] && cpoReports[i]->Runde() > nPrevRound && cpoReports[i]->Runde() < nActRound) { + nPrevRound = cpoReports[i]->Runde(); + poRep2 = cpoReports[i]; + } + } + + if (!sPlayer.empty() && poRep1) { + nPlayer = (int32_t)strtol(sPlayer.c_str(), 0, poRep1->PNrBase()); + } + if (poRep1 && (nPlayer < 0 || poRep1->Partei() == nPlayer)) { + CRegion::SetCurrentPlayer(nPlayer); + bFoundActivePlayer = true; + if (!sGroupName.empty()) { + g_nOnlyGroup = CReport::GetGroupIdByName(sGroupName); + } + else { + g_nOnlyGroup = -1; + } + if (sOName.empty()) { + if (OpenOXFile(poRep1->FileName(), sOXName, bForce)) { + oV.Vorlage(*poRep1, poRep2, bTime); + // fclose( g_hOut ); g_hOut = 0; + } + else { + if (sOXName.empty()) { + oV.Vorlage(*poRep1, poRep2, bTime); + } + } + } + else { + if (!sOName.empty()) { + std::string sOPName = sOName; + CRegExp::Replace(sOPName, "@p", ToString(poRep1->Partei())); + CRegExp::Replace(sOPName, "@P", itoan(poRep1->Partei(), poRep1->PNrBase())); + CRegExp::Replace(sOPName, "@r", ToString(poRep1->Runde())); + CRegExp::Replace(sOPName, "@j", poRep1->Jahr() < 10 ? (std::string("0") + ToString(poRep1->Jahr())) : ToString(poRep1->Jahr())); + CRegExp::Replace(sOPName, "@m", poRep1->Zeitalter() == 2 ? ToString(poRep1->Monat()) : (poRep1->Monat() < 10 ? (std::string("0") + ToString(poRep1->Monat())) : ToString(poRep1->Monat()))); + CRegExp::Replace(sOPName, "@w", ToString(poRep1->Woche())); + COutput::SetTarget("vorlage", new COutput(sOPName)); + if (!COutput::Target("vorlage")->IsOkay()) { + ERRMSG(0, ("FEHLER: Datei '%s' konnte nicht fuer die Ausgabe geoeffnet werden!\n", sOPName.c_str())); + } + else { + oV.Vorlage(*poRep1, poRep2, bTime); + } + } + } + } + } + } + } + catch (CTimeoutException& ex) { + ERRMSG(0, ("FEHLER: Abbruch durch zu lange Skriptlaufzeit nach %d Sekunden!\n", ex._runtime)); + } + + if (bTime) { + TRACEMSG(("(%1.2f sek.)\n", float(clock() - (unsigned)g_nTimeCorrection - nStart) / CLOCKS_PER_SEC)); + } + if (!bFoundActivePlayer) { + if (!sPlayer.empty()) { + ERRMSG(0, ("FEHLER: Konnte keine Befehle fuer Partei '%s' finden, habe nichts ausgefuehrt.", sPlayer.c_str())); + } + else { + ERRMSG(0, ("FEHLER: Konnte keine Befehle finden, habe nichts ausgefuehrt.")); + } + } + + if (!sMapOutName.empty()) { + nStart = clock(); + if (bTime) { + TRACEMSG(("schreibe Karten-CR...")); + } + oV.WriteMap(sMapOutName); + if (bTime) { + TRACEMSG((" (%1.2f sek.)\n", float(clock() - nStart) / CLOCKS_PER_SEC)); + } + } + + for (i = 0; i < cpoReports.size(); i++) { + delete cpoReports[i]; + } + + if (bTime) { + if (g_nTimeCorrection) { + TRACEMSG(("Zeit ueber alles (%1.2f sek. + %1.2f sek. fuer Eingaben)\n", float(clock() - (unsigned)g_nTimeCorrection - nTStart) / CLOCKS_PER_SEC, float(g_nTimeCorrection) / CLOCKS_PER_SEC)); + } + else { + TRACEMSG(("Zeit ueber alles (%1.2f sek.)\n", float(clock() - nTStart) / CLOCKS_PER_SEC)); + } + } + + if (g_nNumErrors && !g_nNumWarnings) { + TRACEMSG(("Es trat%s %d Fehler auf!\n", g_nNumErrors == 1 ? "" : "en", g_nNumErrors)); + } + else if (!g_nNumErrors && g_nNumWarnings) { + TRACEMSG(("Es traten %d Warnungen auf!\n", g_nNumWarnings)); + } + else if (g_nNumErrors && g_nNumWarnings) { + TRACEMSG(("Es traten %d Fehler und %d Warnungen auf!\n", g_nNumErrors, g_nNumWarnings)); + } + } + else { + COutput* pTarget = COutput::Target("stdout"); + if (g_bError) { + pTarget = COutput::Target("stderr"); + } + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + pTarget->Printf("\nAufruf: VORLAGE [Optionen] [CR-Datei1] { [CR-Datei2] {...} } { [> Vorlagendatei] }\n\n"); + pTarget->Write(" -b Beschreibungen der Einheiten mit in die Vorlage uebernehmen\n"); + pTarget->Write(" -cfg s Gibt den Basisnamen der Konfigurationsdatei an\n"); + pTarget->Write(" --cfgpath p Pfad in dem die Konfig-Dateien gesucht werden\n"); + pTarget->Write(" -cr Ausgabe der Metabefehlsauswertung als CR, statt in eine Vorlage\n"); + pTarget->Write(" -cl n Limitierung der Behaeltergroesse auf n, Warnung wenn groesser\n"); + pTarget->Write(" -d Debug-Mode aktivieren\n"); + pTarget->Write(" -do f Die Debugausgaben von Vorlage erfolgen in die Datei f\n"); + pTarget->Write(" -e f Die Fehlermeldungen von Vorlage erfolgen in die Datei f\n"); + pTarget->Write(" -et Bei Skript-Fehlern in den Debugger springen\n"); + pTarget->Write(" -f Forciert das Ueberschreiben bestehender Zugdateien\n"); + pTarget->Write(" -fd Fordert das Deklarieren von Variablen vor erstem Gebrauch\n"); + pTarget->Write(" -fr Erzwingt das Rendern der Messages durch Vorlage (nicht empfohlen)\n"); + pTarget->Write(" --fullcom Unit-Output enthaelt auch die persistenten Kommentare\n"); + pTarget->Write(" --fullregs Alle Regionen des aktuellen CRs oder mit 'visibility' in OnRegion\n"); + pTarget->Write(" und EndRegion beruecksichtigen\n"); + pTarget->Write(" --fix-encodings\n"); + pTarget->Write(" Versucht, UTF-8 und Latin1-Mischungen in Latin1 zu konvertieren\n"); + pTarget->Write(" -g Gegnstandsliste in Einheitenkommentar\n"); + pTarget->Write(" -gr n Zugvorlage nur fuer Gruppe n erzeugen (kein Einfluss bei -cr)\n"); + pTarget->Write(" -hb Parteihandelsbilanz zu Beginn der Vorlage\n"); + pTarget->Write(" -i f Datei f als Skriptdatei einlesen; Die Option kann mehrfach\n"); + pTarget->Write(" verwendet werden\n"); + pTarget->Write(" -k Minikarte der Nachbarregionen und Regionsinfos\n"); + pTarget->Write(" -kl Wie -k, aber mit Luxusgutpreisen\n"); + pTarget->Write(" -klp Wie -k, aber mit angebotenem Luxusgut\n"); + pTarget->Write(" -kk Komplettkarte der Ausdehnung des Basis-Reports im Kopf der Vorlage\n"); + pTarget->Write(" --kkall Komplettkarte ohne Beachtung des Basis-Reports, also alles\n"); + pTarget->Write(" -l Gewichtsuebersicht in Einheitenkommentar\n"); + pTarget->Write(" --limit-runtime t\n"); + pTarget->Write(" Skriptlaufzeit auf t Sekunden beschraenken\n"); + pTarget->Write(" -m Materialpool anzeigen\n"); + pTarget->Write(" -mi n Multipass-Interpretation n-fach mit Durchlaufnummer in $PASSNUM\n"); + pTarget->Write(" -map f Karten-CR exportieren\n"); + pTarget->Write(" --mapr f Karten-CR incl. Runden-Infos exportieren\n"); + pTarget->Write(" -more Bei Bildschirmausgaben nach jedem Bildschirm anhalten\n"); + pTarget->Write(" -mr dx dy Bei nachfolgenden CRs dx und dy zu den Koordinaten addieren\n"); + pTarget->Write(" -n Regionsspezifische Nachrichten in Regionsinfo uebernehmen\n"); + pTarget->Write(" --no-battle-messages\n"); + pTarget->Write(" Unterdrueckt detailierte Kampf-Nachrichten in den Redionen\n"); + pTarget->Write(" -nrzv Zugvorlage wie im Anhang des NR\n"); + pTarget->Write(" -nv Ausgabe der Zugvorlage unterdruecken\n"); + pTarget->Write(" -o f Die Ausgabe der Vorlage erfolgt nicht auf stdout, sondern\n"); + pTarget->Write(" in die Datei mit dem Namen f\n"); + pTarget->Write(" -ox x Wie -o, aber es wird nur eine Dateierweiterung angegeben,\n"); + pTarget->Write(" die mit dem Dateinamen des Reports den neuen Namen ergibt.\n"); + pTarget->Write(" --output-encoding e\n"); + pTarget->Write(" Ermoeglicht die Angabe eines Zeichensatzes fuer die Ausagedateien\n"); + pTarget->Write(" -p n Legt fest, fuer welchen Spieler die Vorlage erstellt wird,\n"); + pTarget->Write(" wenn CRs von verschiedenen Spielern uebergeben werden\n"); + pTarget->Write(" -pb Zeigt BESCHREIBE-PRIVAT-Inhalte in der Vorlage an\n"); + pTarget->Write(" --pedantic Mehr Fehlermeldungen zur Fehlersuche in Skripten\n"); + pTarget->Write(" -pi Fortschrittsanzeige bei der Abarbeitung der Einheiten\n"); + pTarget->Write(" -pm Metabefehle in privaten Beschreibungen statt persistenten\n"); + pTarget->Write(" Kommentaren suchen\n"); + pTarget->Write(" -pw p Passwort w fuer Zugvorlage, wenn keines in CR gefunden\n"); + pTarget->Write(" -q Unterdrueckt die Ausgabe der Zeitmessungen\n"); + pTarget->Write(" -rc Befehle aus CR nicht durch Metabefehl-Ergebnisse ueberschreiben\n"); + pTarget->Write(" -sb Einheiten-Sortierung u. Gruppierung nach Bauwerken (auch Schiffe)\n"); + pTarget->Write(" -si Sortierung der Regionen nach Inseln\n"); + pTarget->Write(" -sk Komandofuehrende Einheiten falls Sortierung immer an den Anfang\n"); + pTarget->Write(" -sp Einheiten-Sortierung nach Privat-Beschreibung\n"); + pTarget->Write(" -st Einheiten-Sortierung nach Talenten\n"); + pTarget->Write(" Diese drei Sortierungen koennen kombiniert werden;\n"); + pTarget->Write(" Es wird dann innerhalb der Bauwerkegruppen nach\n"); + pTarget->Write(" Talenten sortiert\n"); + pTarget->Write(" --restricted\n"); + pTarget->Write(" Dateifunktionen und Serveruntaugliche Befehle werden blockiert\n"); + pTarget->Write(" --rseed n Initialisiert den Zufallsgenerator mit dem angegebenen Wert\n"); + pTarget->Write(" --strip-duplicate-descr\n"); + pTarget->Write(" Doppelte Einheiten-Beschreibungen in einer Region abkuerzen\n"); + pTarget->Write(" -t Talentliste in Einheitenkommentar\n"); + pTarget->Write(" -ts Quantisierte Talentpunkte-Anzeige fuer aktuelle Eressea-Zuege\n"); + pTarget->Write(" -td Talentliste in Einheitenkommentar mit Aenderungen wenn moeglich\n"); + pTarget->Write(" -u Fremde Einheiten am Ende der Einheiten einer Region auflisten\n"); + pTarget->Write(" -uv Wie -u, aber mit Angabe von Beschreibung und Guetern\n"); + pTarget->Write(" -up Uebersicht ueber die Parteien in der Region einfuegen\n"); + pTarget->Write(" -us Fremde Einheiten nicht am Ende auflisten sondern einsortieren\n"); + pTarget->Write(" -un fremde Einheiten werden mit -u o. -uv nur angezeigt, wenn sie\n"); + pTarget->Write(" im vorreport nicht in derselben Region waren (oder die Region\n"); + pTarget->Write(" neu im Report ist)\n"); + pTarget->Write(" -uq Die Ausgabe jeglicher Einheitenbloecke wird unterdrueckt.\n"); + pTarget->Write(" -v Ausgabe der Versionsinfo von Vorlage und Ende\n"); + pTarget->Write(" --version2 Warnungen bei Features die ab V2.0 nicht unterstuetzt werden\n"); + pTarget->Write(" -w l Nachrichten und Info-Zeilen auf l Zeichen umbrechen (default: 100)\n"); + pTarget->Write(" -wc l Befehle auf l Zeichen umbrechen (default: -w l)\n"); + pTarget->Write(" -ws Warnungen ueber unbekannte Feldkennungen unterdruecken\n"); + pTarget->Write(" -wall Alle Warnungen unterdruecken\n"); + pTarget->Write(" -wait Nach der Ausfuehrung auf Druck auf die Eingabetaste warten\n"); + pTarget->Write(" -wfirst Warnungen und Fehlermeldungen nur beim ersten auftreten melden\n"); + pTarget->Write(" -xn f Das Regelfile f fuer den Namensgenerator verwenden\n"); + } + + TRACEMSG(("\n")); + + EXIT(g_returnCode); + + return 0; +} diff --git a/Vorlage/Zugvorlage.h b/Vorlage/Zugvorlage.h new file mode 100644 index 0000000..ec7fc02 --- /dev/null +++ b/Vorlage/Zugvorlage.h @@ -0,0 +1,113 @@ +/**************************************************************************** + * $Source: D:\\Development\\Repository/ETools/Vorlage/Zugvorlage.h,v $ + * $Author: ssh $ + * $Date: 2003/07/01 09:39:30 $ + * $Revision: 1.1 $ + * $State: Exp $ + * Copyright: (c) Copyright 1999 by S.Schuemann + * Project: Eressea-Tools + * Zweck: Algemeine Utility-Funktionen + ***************************************************************************** + * + * $Log: Zugvorlage.h,v $ + * Revision 1.1 2003/07/01 09:39:30 ssh + * *** empty log message *** + * + * Revision 1.1 2003/07/01 09:19:29 ssh + * Initial recvsing of Source... + * + * Revision 1.7 2000/02/24 09:56:48 S.Schuemann + * Diverse Aenderungen auf dem Pfad zur Vorlage V1.4 beta 10c + * + * Revision 1.6 1999/11/28 17:38:41 S.Schuemann + * - Mannigfaltige Änderungen für Vorlage V1.4 beta 9 + * + * Revision 1.5 1999/11/17 08:59:13 S.Schuemann + * - support für multiple CRs + * + * - vielfache Änderungen für Vorlage 1.4 beta 8 + * + * Revision 1.4 1999/10/20 02:19:40 S.Schuemann + * - Die neue Option '-hb' erlaubt es, zu Beginn der Zugvorlage + * eine Handelsübersicht, nach Parteien und Produkten einzufügen, + * um den Überblick zu behalten + * + * - Die neue Option '-si' erlaubt es, die Regionen nach + * Inselzugehörigkeit zu sortieren, statt nach Report- + * Reihenfolge, dabei liegen alle Regionen beisammen, die + * miteinander Verbunden sind + * + * - Die neue Option '-ox ext' leitet die Zugvorlage, wie die + * Option '-o filename' in eine Datei um, die aber den selben + * Basisnamen wie der Bezugsreport hat, aber die Datei- + * erweiterung ext bekommt; Der Report muß die Endung '.cr' + * haben (wie ja üblich) + * + * - Die neue Option '-pb' zeigt BESCHREIBE-PRIVAT-Inhalte in + * der Vorlage an + * + * - Bei Schiffen steht nun auch die freie und die theoretische + * Kapazität + * + * - In den Regionsinfos steht nun der von den Bauern + * erwirtschaftete Gewinn, also die Menge, die man maximal + * Abschöpfen kann, ohne die Regionsreserven zu gefährden + * + * - In der REGION-Zeile steht nun auch noch der Geländetyp + * + * Revision 1.3 1999/10/18 21:32:20 S.Schuemann + * - Diverse Aenderungen, fuer die Versionen 1.3.1, 1.3.2, 1.3.3 sowie 1.4 b 1 und 1.4 b 2 + * + * Revision 1.2 1999/09/27 10:31:20 S.Schuemann + * - Final 1.3 + * - Kampfstatus wird bei Einheiten angezeigt + * - Schiff und Ablegekueste werden beim Kapitaen angezeigt + * - Anpassungen durch neue Klasse CReport statt CWorldDB mit + * leicht veraenderten Zustaendigkeiten + * + * Revision 1.1.1.1 1999/09/20 14:55:45 Steffen + * - Initial CVS-checkin; + * - Basierend auf dem Stand von Vorlage V1.3b6 gesaeubert und aufgeteilt; + * - Fehler in Kapazitaetsberechnung behoben; + * + *****************************************************************************/ +#pragma once + +#include + +#include +#include +#include + +class CVorlage +{ +public: + CVorlage() {} + + ~CVorlage() {} + + void Vorlage(CReport& oReport, CReport* poRep2, bool bTime = false); + void RunMetacommands(CReport& oReport); + void WriteMap(const std::string& sFile); + + static void InitDBS(int32_t nRunde, std::vector& cpoReports); + static void Islandize(CKarte::IslandQueue& cpoQueue, RegionDB& coRDB); + +protected: + void Regionsvorlage(CRegion* poReg, CRegion* poReg2, CReport* poRep2); + void ShowInvisibles(CRegion* poReg, CRegion* poReg2, CReport* poRep2); + void Einheitenvorlage(CEinheit* poUnit, CReport* poRep2); + void FremdEinheiten(CEinheit* poUnit, CReport* poRep2); + void BauwerkAusgabe(CBauwerk* pBuilding, CRegion::VEinheiten* poVE = 0); + void SchiffAusgabe(CSchiff* pSchiff); + std::string MutateCRBlock(std::fstream& oIS, CBlockBase* poBlockObj, bool utf8); + + int32_t m_nPlayer; + int32_t m_nUnits; + CReport* m_poCurrentReport; + CKarte* m_poKarte; + CRegion* m_poCurrentRegion; + CEinheit* m_poCurrentUnit; + VKommandos m_coInitCmd; + VKommandos m_coExitCmd; +}; diff --git a/Vorlage/crashdumphandler.cpp b/Vorlage/crashdumphandler.cpp new file mode 100644 index 0000000..49217cb --- /dev/null +++ b/Vorlage/crashdumphandler.cpp @@ -0,0 +1,435 @@ +//--------------------------------------------------------------------------------------- +// crashdumphandler.cpp +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2005, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#define NOMINMAX + +#include "crashdumphandler.h" + +#include +#include +#include +#include +#include + +extern std::vector g_coCallStack; + +#ifdef _WIN32 +#include +#include +#include +#include + +class CrashDumpHandler::Private +{ +private: + LPTOP_LEVEL_EXCEPTION_FILTER _pfnOldHandler; + static Private* g_pCDH; + + typedef BOOL(__stdcall* SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL); + typedef BOOL(__stdcall* SYMCLEANUPPROC)(HANDLE); + typedef BOOL(__stdcall* STACKWALKPROC)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE); + typedef LPVOID(__stdcall* SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD); + typedef DWORD(__stdcall* SYMGETMODULEBASEPROC)(HANDLE, DWORD); + typedef BOOL(__stdcall* SYMGETSYMFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL); + typedef DWORD(__stdcall* SYMGETOPTIONSPROC)(); + typedef DWORD(__stdcall* SYMSETOPTIONSPROC)(DWORD); + SYMINITIALIZEPROC _SymInitialize; + SYMCLEANUPPROC _SymCleanup; + STACKWALKPROC _StackWalk; + SYMFUNCTIONTABLEACCESSPROC _SymFunctionTableAccess; + SYMGETMODULEBASEPROC _SymGetModuleBase; + SYMGETSYMFROMADDRPROC _SymGetSymFromAddr; + SYMGETOPTIONSPROC _SymGetOptions; + SYMSETOPTIONSPROC _SymSetOptions; + std::string _sAppInfo; + +public: + Private(const std::string& sAppInfo) + : _sAppInfo(sAppInfo) + { + g_pCDH = this; + _pfnOldHandler = SetUnhandledExceptionFilter(UnhandledExceptionFilter); + } + + ~Private() + { + SetUnhandledExceptionFilter(_pfnOldHandler); + _pfnOldHandler = 0; + g_pCDH = 0; + } + + static LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo) + { + PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + if (pExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT) { + std::ofstream dumpFile("crashinfo.txt"); + time_t ti; + time(&ti); + dumpFile << "Application: " << g_pCDH->_sAppInfo << " (Win32)\nCrash-Time: " << ctime(&ti) << std::endl; + dumpFile << "Skript stack:\n"; + for (size_t i = 0; i < g_coCallStack.size(); i++) { + dumpFile << i << " " << g_coCallStack[i] << std::endl; + } + dumpFile << std::endl; + + g_pCDH->HandleCrash(dumpFile, pExceptionInfo); + + dumpFile.close(); + fflush(stdout); + fprintf(stderr, "\n\nVorlage wurde wegen eines schwerwiegenden Fehlers beendet! Infos bitte der angelegten Datei 'crashinfo.txt' entnehmen und bei Bedarf an s.schuemann@pobox.com senden!\n\n"); + fflush(stderr); + abort(); + return EXCEPTION_CONTINUE_SEARCH; + } + if (g_pCDH->_pfnOldHandler) { + return g_pCDH->_pfnOldHandler(pExceptionInfo); + } + else { + return EXCEPTION_CONTINUE_SEARCH; + } + } + + void HandleCrash(std::ostream& os, PEXCEPTION_POINTERS pExceptionInfo) + { + PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + os << tfm::format("Exception code: %08X\n", pExceptionRecord->ExceptionCode); + TCHAR szFaultingModule[MAX_PATH]; + DWORD section, offset; + GetLogicalAddress(pExceptionRecord->ExceptionAddress, szFaultingModule, sizeof(szFaultingModule), section, offset); + os << tfm::format("Fault address: %08X %02X:%08X %s\n", pExceptionRecord->ExceptionAddress, section, offset, szFaultingModule); + PCONTEXT pCtx = pExceptionInfo->ContextRecord; // Show the registers +#ifdef _M_IX86 // Intel Only! + os << "\nRegisters:\n"; + os << tfm::format("EAX:%08X EBX:%08X ECX:%08X EDX:%08X\nESI:%08X EDI:%08X EBP:%08X\n", pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi, pCtx->Ebp); + os << tfm::format("CS:EIP: %04X:%08X SS:ESP: %04X:%08X\n", pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp); + os << tfm::format("DS:%04X ES:%04X FS:%04X GS:%04X Flags:%08X\n", pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs, pCtx->EFlags); +#endif + os.flush(); + if (!InitImagehlpFunctions()) { + os << "(IMAGEHLP.DLL or its exported procs not found, using fallback.)\n"; +#ifdef _M_IX86 // Intel Only! + // Walk the stack using x86 specific code + WintelStackWalk(os, pCtx); +#else + os << "Unsopported plattform, couldn't walk stack.\n"; +#endif + return; + } + ImagehlpStackWalk(os, pCtx); + _SymCleanup(GetCurrentProcess()); + os << std::endl; + } + + void WintelStackWalk(std::ostream& os, PCONTEXT pContext) + { + os << "\nCall stack:\n"; + os << "L# Address Frame Logical addr Module\n"; + DWORD pc = pContext->Eip; + PDWORD pFrame, pPrevFrame; + pFrame = (PDWORD)pContext->Ebp; + int lvl = 0; + do { + TCHAR szModule[MAX_PATH] = ""; + DWORD section = 0, offset = 0; + GetLogicalAddress((PVOID)pc, szModule, sizeof(szModule), section, offset); + os << tfm::format("%02d %08X %08X %04X:%08X %s\n", lvl++, pc, pFrame, section, offset, szModule); + pc = pFrame[1]; + pPrevFrame = pFrame; + pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack + if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a + break; // DWORD boundary. Bail if not so. + if (pFrame <= pPrevFrame) + break; + // Can two DWORDs be read from the supposed frame address? + if (IsBadWritePtr(pFrame, sizeof(PVOID) * 2)) + break; + } while (1); + } + + // Walks the stack, and writes the results to the report file + void ImagehlpStackWalk(std::ostream& os, PCONTEXT pContext) + { + os << "\nCall stack:\n"; + os << "L# Address Frame Logical addr Symbol/Module\n"; + + // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag + STACKFRAME sf; + memset(&sf, 0, sizeof(sf)); + // Initialize the STACKFRAME structure for the first call. This is only + // necessary for Intel CPUs, and isn't mentioned in the documentation. + sf.AddrPC.Offset = pContext->Eip; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrStack.Offset = pContext->Esp; + sf.AddrStack.Mode = AddrModeFlat; + sf.AddrFrame.Offset = pContext->Ebp; + sf.AddrFrame.Mode = AddrModeFlat; + int lvl = 0; + while (1) { + if (!_StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf, pContext, 0, _SymFunctionTableAccess, _SymGetModuleBase, 0)) + break; + if (0 == sf.AddrFrame.Offset) // Basic sanity check to make sure + break; // the frame is OK. Bail if not. + os << tfm::format("%02d %08X %08X ", lvl++, sf.AddrPC.Offset, sf.AddrFrame.Offset); + // IMAGEHLP is wacky, and requires you to pass in a pointer to an + // IMAGEHLP_SYMBOL structure. The problem is that this structure is + // variable length. That is, you determine how big the structure is + // at runtime. This means that you can't use sizeof(struct). + // So...make a buffer that's big enough, and make a pointer + // to the buffer. We also need to initialize not one, but TWO + // members of the structure before it can be used. + BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512]; + PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer; + pSymbol->SizeOfStruct = sizeof(symbolBuffer); + pSymbol->MaxNameLength = 512; + DWORD symDisplacement = 0; // Displacement of the input address, + // relative to the start of the symbol + TCHAR szModule[MAX_PATH] = ""; + DWORD section = 0, offset = 0; + GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset); + + if (_SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symDisplacement, pSymbol)) { + os << tfm::format("%04X:%08X %hs+%X %s\n", section, offset, pSymbol->Name, symDisplacement, szModule); + } + else { + os << tfm::format("%04X:%08X %s\n", section, offset, szModule); + } + } + } + + // by the len parameter (in characters!) + bool GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD& offset) + { + if (addr == NULL) + return false; + + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery(addr, &mbi, sizeof(mbi))) + return false; + DWORD hMod = (DWORD)mbi.AllocationBase; + if (!GetModuleFileName((HMODULE)hMod, szModule, len)) + return false; // Point to the DOS header in memory + PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; + // From the DOS header, find the NT (PE) header + PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew); + PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr); + DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address + // Iterate through the section table, looking for the one that encompasses + // the linear address. + for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++) { + DWORD sectionStart = pSection->VirtualAddress; + DWORD sectionEnd = sectionStart + std::max(pSection->SizeOfRawData, pSection->Misc.VirtualSize); + // Is the address in this section??? + if ((rva >= sectionStart) && (rva <= sectionEnd)) { + // Yes, address is in the section. Calculate section and offset, + // and store in the "section" & "offset" params, which were + // passed by reference. + section = i + 1; + offset = rva - sectionStart; + return true; + } + } + return false; // Should never get here! + } + + bool InitImagehlpFunctions() + { + HMODULE hModImagehlp = LoadLibrary("IMAGEHLP.DLL"); + if (!hModImagehlp) + return FALSE; + _SymInitialize = (SYMINITIALIZEPROC)GetProcAddress(hModImagehlp, "SymInitialize"); + if (!_SymInitialize) + return FALSE; + _SymCleanup = (SYMCLEANUPPROC)GetProcAddress(hModImagehlp, "SymCleanup"); + if (!_SymCleanup) + return FALSE; + _StackWalk = (STACKWALKPROC)GetProcAddress(hModImagehlp, "StackWalk"); + if (!_StackWalk) + return FALSE; + _SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC)GetProcAddress(hModImagehlp, "SymFunctionTableAccess"); + if (!_SymFunctionTableAccess) + return FALSE; + _SymGetModuleBase = (SYMGETMODULEBASEPROC)GetProcAddress(hModImagehlp, "SymGetModuleBase"); + if (!_SymGetModuleBase) + return FALSE; + _SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC)GetProcAddress(hModImagehlp, "SymGetSymFromAddr"); + if (!_SymGetSymFromAddr) + return FALSE; + _SymSetOptions = (SYMSETOPTIONSPROC)GetProcAddress(hModImagehlp, "SymSetOptions"); + if (!_SymSetOptions) + return FALSE; + _SymGetOptions = (SYMGETOPTIONSPROC)GetProcAddress(hModImagehlp, "SymGetOptions"); + if (!_SymGetOptions) + return FALSE; + + DWORD dwOpts = _SymGetOptions(); + + // Always defer loading to make life faster. + _SymSetOptions(dwOpts | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); + + char pcPath[_MAX_PATH]; + std::string sPath; + if (_getcwd(pcPath, _MAX_PATH) != NULL) { + sPath = pcPath; + sPath += ";"; + } + else { + sPath = ".;"; + } + sPath += getenv("PATH"); + if (!_SymInitialize(GetCurrentProcess(), (LPSTR)sPath.c_str(), TRUE)) + return FALSE; + return TRUE; + } +}; + +CrashDumpHandler::Private* CrashDumpHandler::Private::g_pCDH = 0; + +#else + +#include +#include +#include +#include + +class CrashDumpHandler::Private +{ +private: + sig_t _pfnOldSEGVHandler; + sig_t _pfnOldILLHandler; + sig_t _pfnOldFPEHandler; + sig_t _pfnOldPIPEHandler; + // sig_t _pfnOldBUSHandler; + // sig_t _pfnOldABRTHandler; + std::string _sAppInfo; + +public: + Private(const std::string& sAppInfo); + ~Private(); + void HandleCrash(const char* reason); + void DumpTrace(std::ofstream& os); +}; + +CrashDumpHandler::Private* g_pCDH = 0; +bool doneDump = false; + +static void SigSEGVHandler(int rc) +{ + g_pCDH->HandleCrash("segment violation"); +} + +static void SigILLHandler(int rc) +{ + g_pCDH->HandleCrash("illegal instruction"); +} + +static void SigFPEHandler(int rc) +{ + g_pCDH->HandleCrash("floating point exception"); +} + +static void SigPIPEHandler(int rc) +{ + g_pCDH->HandleCrash("write on single ended pipe"); +} + +/* +static void SigBUSHandler(int rc) +{ + g_pCDH->HandleCrash( "bus error" ); +} + +static void SigABRTHandler(int rc) +{ + g_pCDH->HandleCrash( "abort program" ); +} + */ + +CrashDumpHandler::Private::Private(const std::string& sAppInfo) + : _sAppInfo(sAppInfo) +{ + g_pCDH = this; + _pfnOldSEGVHandler = signal(SIGSEGV, &SigSEGVHandler); + _pfnOldILLHandler = signal(SIGILL, &SigILLHandler); + _pfnOldFPEHandler = signal(SIGFPE, &SigFPEHandler); + _pfnOldPIPEHandler = signal(SIGPIPE, &SigPIPEHandler); + // _pfnOldBUSHandler = signal(SIGBUS, &SigBUSHandler); + // _pfnOldABRTHandler = signal(SIGABRT, &SigABRTHandler); +} + +CrashDumpHandler::Private::~Private() +{ + signal(SIGSEGV, _pfnOldSEGVHandler); + signal(SIGILL, _pfnOldILLHandler); + signal(SIGFPE, _pfnOldFPEHandler); + signal(SIGPIPE, _pfnOldPIPEHandler); + // signal(SIGBUS, _pfnOldBUSHandler); + // signal(SIGABRT, _pfnOldABRTHandler); +} + +void CrashDumpHandler::Private::HandleCrash(const char* reason) +{ + std::ofstream dumpFile("crashinfo.txt"); + time_t ti; + static void* array[10]; + size_t size; + char** strings; + size_t i; + + time(&ti); + dumpFile << "Application: " << _sAppInfo << " (Linux)\nCrash-Time: " << ctime(&ti) << "\nReason: " << reason << std::endl; + + dumpFile << "Skript stack:\n"; + for (i = 0; i < g_coCallStack.size(); i++) { + dumpFile << i << " " << g_coCallStack[i] << std::endl; + } + size = static_cast(backtrace(array, 10)); + strings = backtrace_symbols(array, static_cast(size)); + dumpFile << "\nProcess trace (" << size << " frames):" << std::endl; + + for (i = 0; i < size; i++) + dumpFile << strings[i] << std::endl; + + free(strings); + dumpFile.close(); + + fflush(stdout); + fprintf(stderr, "\n\nVorlage wurde wegen eines schwerwiegenden Fehlers beendet! Infos bitte der angelegten Datei 'crashinfo.txt' entnehmen und bei Bedarf an s.schuemann@pobox.com senden!\n\n"); + fflush(stderr); + abort(); +} + +void CrashDumpHandler::Private::DumpTrace(std::ofstream& os) {} + +#endif // _WIN32 + +CrashDumpHandler::CrashDumpHandler(const std::string& sAppInfo) + : _pimpl(new Private(sAppInfo)) +{ +} + +CrashDumpHandler::~CrashDumpHandler() +{ + delete _pimpl; +} diff --git a/Vorlage/crashdumphandler.h b/Vorlage/crashdumphandler.h new file mode 100644 index 0000000..ff0cb75 --- /dev/null +++ b/Vorlage/crashdumphandler.h @@ -0,0 +1,39 @@ +//--------------------------------------------------------------------------------------- +// crashdumphandler.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2005, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- +#pragma once + +#include + +class CrashDumpHandler +{ +public: + class Private; + CrashDumpHandler(const std::string& sAppInfo); + ~CrashDumpHandler(); + +private: + Private* _pimpl; +}; diff --git a/Vorlage/history.txt b/Vorlage/history.txt new file mode 100644 index 0000000..03a46ec --- /dev/null +++ b/Vorlage/history.txt @@ -0,0 +1,3183 @@ +---[Vorlage V1.7.6]--- + +* Bugfix: Vorlage konnte bei neuen `[CRHierarchy]`-Sub-Blöcken crashen wenn + diese nicht zusätzlich definiert waren, selbst wenn es reguläre Blocke + sind. (#23) +* Bugfix: `#tag EINHEIT[] ejcOrdersconfimed 1` funktionierte nicht + mit Variablen im Ausdruck `` (#22) +* Feature: Wenn ausführliche Regions-Infos aktiv sind, werden nach dem + Materialpool nun auch die bewachenden Partieen aufgelistet. (#21) +* Bugfix: Bei im Bau befindlichen Schiffen wurde für Flotten die Zielgröße + nur für ein Schiff angezeigt, ausserdem wird während des Baus nun die + Ladung angezeigt. (#20) +* Bugfix: Message-Typ 1235024123 wurde bei Übergaben nicht berücksichtigt + und dadurch die Werte in der Handelsbilanz unterschlagen. (#19) +* Bugfix: Die als eigene Partei getarnten Einheiten in einer Region wurden + der eigenen Partei hinzugezählt (war in v1.7.6rc1 defekt). (#18) + +---[Vorlage V1.7.5]--- + +* Bugfix: Crash bei unvollständigen UTF-8-Codes behoben. +* Feature: Bessere Fehlermeldungen bei Encoding-Fehlern. + +---[Vorlage V1.7.4]--- + +* Bugfix: Die freie Kapazität bei Schiffen (sowie deren Ladung-Attibut) + konnten nicht immer korrekt berechnet werden und die Berechnung + enthielt zudem einen Fehler im Fall "schwerer" Einheiten. Der Fehler + wurde behoben und wenn verfügbar (Eressea) auf die CR-Attribute + cargo/capacity zurückgegriffen (#16) +* Bugfix: Ein Pufferüberlauf in der Formatierung der Gegenstände einer + Einheit konnte Vorlage zum Abbruch bringen, alle Formatierungen + wurden auf eine sichere Formatierung unabhängig von der Ausgabelänge + umgestellt. (#15) +* Feature: Bei Einheitennummern von Einheiten auf Schiffen oder in + Bauwerken wird der Marker vor der Einheitennummer ('s'/'S', bzw. + 'b'/'B') der durch Großbuchstaben auch das Kommando markiert, durch + einen Doppelpunkt getrennt. Die Markierung stammte aus der Zeit vor + der Base36-Einführung und war damals noch besser lesbar. (#13) +* Bugfix: Ein Fehler in der Formatierung führte zur Darstellung von + negativen Zahlen für freie Arbeitsplätze und bei Änderungen in + Einheiten als positives 2er-Komplement. (#10) +* Bugfix: Die erste v1.7.4 hatte einen Fehler bei dem die schliessenden + Klammern bei Talent-Differenzen fehlen konnten. + +---[Vorlage V1.7.3]--- + +* Beschleunigung des Scanners (b552) +* Feature: Die Nachrichten zu Kämpfen in Regionen der Zugvorlage werden + nun ebenfalls bei Verwendung von `-n` bei den Nachrichten der Region + ausgegeben, wem das zu viel ist, der kann die Details des Kampfes + mit der neuen Option `--no-battle-messages` ausblenden (#10, b552) +* Feature: Für Bauwerke kann nun in der Config festgelegt werden, oben + ihre Kapazität sich auf Individuen oder Einheiten bezieht. In Eressea + ist davon der Leuchtturm betroffen, und durch as Config-Attribut + `"einheiten"` mit einem Wert von `1` wird die Auslastung in Einheiten + statt Individuen angezeigt. + Beispiel: + [Buildings] + "Leuchtturm", 3, "Talent", 4, "Kapazitaet", 1, "Einheiten", ... + (#9/b551) +* Featur: Der Befehl '#var' erlaubt nun die Initialisierung der + deklarierten Variablen, also z.B.: + #var $i=0 $j=1 + (b550) +* Feature: Die Option `--output-encoding` unterstützt jetzt auch `utf8` + als Output-Encoding für die Zugvorlage (inkl. BOM) (#7/b548) +* Vorlage benutzt nun intern für Kapazitätsberechnungen von Einheiten + double wie sonst überall auch, vorher war es float. (#6/b548) +* Feature: Option `-wc l` erlaubt es für Kommandozeilen eine andere + Zeilenlänge zu wählen als für die Kommentare, der Default ist der + der Kommentare (#1/b546) +* Bugfix: Die erste 1.7.3 verhielt sich als ob `-fd` benutzt wurde. +* Bugfix: Fehlerhafte Anzeige von Flotten als im Bau befindlich und mit + verwirrenden Grössen (#5/b548) + +---[Vorlage V1.7.2]--- + +* Neuer Befehl `#assert []` erlaubt es Assertions zu + definieren, bei deren Fehlschlagen ein Fehler mit der optionalen + Message ausgegeben wird (b542) +* Die Skript-Ausführung wurde beschleunigt indem erkannt wird in welchen + Elementen überhaupt Inplace-Auswertungen vorkommen, und nur in dem + Fall der teurere Auswertungsteil durchlaufen wird (#4/b542) +* Bugfix: Keine warnungen mehr, beim Überschreiben von internen Funktionen + und Prozeduren (#3/b542) +* Bugfix: Mixed up data types from config structures are now fixed. + (#2/b542) + +---[Vorlage V1.7.1]--- + +* Eine Menge Cleanup, der Code musste großflächig entstaubt werden um + mit einem modernen Compiler mit kritischerem Warning-Level und + '-Werror' überhaupt compiliert zu werden. +* Wechsel auf C++14, und Entfernung der Boost-Abhängigkeit. +* Wechsel auf CMake als Build-Tool (b527) +* Bugfix: Potentieller Crash wenn der 'OutputLineFilter' benutzt wird. + (b535) + +---[Vorlage V1.6.2-1]--- + +* Der Befehl '#encoding' unterstützt nun auch UTF-8, so das man seine + Skript-Dateien nun auch in UTF-8 schreiben kann, wenn man an den + Anfang '#encoding utf-8' schreibt, oder die Datei mit BOM + markiert ist (was unter Linux oft nicht passiert). (b524) + +* Bugfix: Bei UTF-8-CR-Ausgabe wurden die COMMAND-Blöcke mit falschem + Encoding geschrieben (#99/b524) + +---[Vorlage V1.6.2]--- + +* Vorlage unterstützt nun auch REPORT[dr].REGION[idx] bzw. dazu + REPORT[dr].REGION.SIZE (#93/b523) + +* Vorlage unterstützt nun (zunächst nur rudimentär) UTF-8-CRs, wie sie + vom Eressea-Server ab dem 2.12.2007 gesendet werden. Die aktuelle + Implementation wandelt jedoch intern nur in ISO-8859-1 (das alte + Server-Encoding) um, erst V1.6.3 wird auch intern UTF-8 unterstützen. + (b521) + +* Mit der neuen Option '--restricted' kann Vorlage um einige Funktionen + reduziert werden, mit dem Ziel Vorlage als Dienst für Spieler auf + einem Server zu betreiben. + Nicht mehr benutzbare Funktionen: + open(), close(), readline(), writeline(), readvalue(), writevalue(), + status(), statustext() + Daneben gehen die Befehle #trace und #input nicht mehr und zudem + kann bei #config kein Pfad mehr angegeben werden, die Files + werden dann nur im aktuellen Verzeichnis gesucht. (#89/b518) + +* Die neue Option '--limit-runtime' ermöglicht es, die Ausführung der + Skripte auf eine gegebene Zeit in Sekunden zu begrenzen. Beim + Überschreiten der Zeit wird die Ausführung mit einer Fehlermeldung + abgebrochen. (#90/b518) + +* Mit dem neuen Befehl '#continue' kann man in #while direkt wieder an + den Anfang des #while springen. (#74/b517) + +* Wenn Vorlage keine Befehle für die angegebene Partei findet (Option + -p) wird jetzt ein Fehler gemeldet. (#78/b517) + +* Bugfix: Doku und Code wiedersprachen sich hinsichtlich des + Wertebereiches von floats, es gilt für einen float $v der Bereich, + -10^50 < $v < -10^50 und es werden 10 Stellen Genauigkeit + zugesichert (#84/#85/b516) + +* Bugfix: Das Arbeiten mit sehr grossen floats konnte zu Abstürzen bei + der Konvertierung in einen String führen (findet implizit z.B. im + Debugger statt) (#84/#85/b516) + +* Bugfix: Der Zugriff auf PARTEI-Objekte funktionierte nicht korrekt, da + je Partei nur eine Version, unabhängig der Report-Runde erreichbar ist + (im Konfliktfall der letzte geladene) (#88/b518) + +* Bugfix: OnUnit wurde für Verräter aufgerufen (#72/b518) + +* Bugfix: Die Option -fd hat nicht immer korrekte Meldungen ausgegeben + und die mitgelieferte standard.vms vertrug sich nicht mit der Option. + (#79/#80/b519) + +* Bugfix: Der Zugriff auf Regionsbotschaften funktionierte nicht. + (#83/b519) + +* Bugfix: Bei der Verwendung von Strings als Index in Arrays wurde statt + einer Fehlermeldung der Index 0 verwendet. (#92/b520) + +* Bugfix: Beim Zugriff auf Gebäude und Schiffe über Regionen (also z.B. + REGION[x,y].SHIP) konnte manchmal zwar via SIZE eine Anzahl gesehen + werden, der Zugriff auf die Elemente klappte jedoch nicht. (#82/b522) + +---[Vorlage V1.6.1]--- + +* Das Attribut wahrerTyp gibt nun bei Gebäuden keine Warnung mehr aus + (#69/b515) + +* Das Kapitel [Things] kann jetzt als vierten Wert die Plural- + Bezeichnung enthalten, die dann bei Bedarf verwendet wird, + entsprechend hat THINGS[].PLURAL nun die Plural-Bezeichnung, + wenn definiert, sonst entspricht sie der Einzahl (b515) + +* Bei Schiffen wird nun der Bau-Fortschritt angezeigt (#68/b514) + +* Die neuen Callbacks OnShip und OnBuilding werden nun zwischen + OnRegion und EndRegion für jedes Schiff/Bauwerk aufgerufen und + in ihnen sind SHIP bzw. BUILDING ohne Index gültig + (#67/b514) + +* Mit REPORT[-1].MESSAGE.SIZE bzw. REPORT[-1].MESSAGE[idx] kommt man + nun auch an die MESSAGE-Blöcke der Vorrunde (#64/b514) + +* Helden sind nun entsprechend markiert und auch ihre Attribute werden + warnungsfrei erkannt (#60/b514) + +* Bugfix: Einige Fehlermeldungen waren durch einen Index-Fehler + vertausch (#65/b514) + +* Bugfix: Die Funktionen mit regulären Ausdrücken kamen nicht mit + Umlauten im Ausdruck zurecht. + (#70/b514) + +* Bugfix: Steuerzeichen konnten das CR-Einlesen blockieren. + (#62/b514) + +* Bugfix: EMR_region war in standard.vms fehlerhaft benannt + (#59/b513) + +---[Vorlage V1.6]--- + +* Mit der neuen Option '--rseed' kann man ab jetzt den + Initialisierungwert des Zufallsgenerators setzen; Dies hilft vor + allem bei Debugging von Code der den Zufallsgenerator nutzt und nur + unter bestimmten Konstellationen Probleme erzeugt. (#57/b511) + +* Die neuen Konstanten TOOLVERSION und BUILDNUMBER ermöglichen es nun, + in Skripten auf Änderungen in Vorlage-Versionen zu reagieren; Dabei + enthält TOOLVERSION z.B. den String '1.6rc7' und BUILDNUMBER z.B. + den Integer 509, bei älteren Versionen hingegen stehen ergibt sich + jeweils der Name der Konstanten als String, also keine Fehlermeldung + (#55/b509) + +* Der #tag-Befehl kann nun auch SCHIFF- und BURG-Blöcken Attribute + für den CR-Export hinzufügen, wobei hier immer die Nummer angegeben + werden muß, also '#tag BURG[burgnummer] Attributname expr' oder + eben '#tag SCHIFF[schiffnummer] Attributname expr' + (#51/b509) + +* Vorlage unterstützt nun (ohne Warnung oder Config-Eintrag) das + Attribut 'UNIT.FOLGT' + (#50/b508) + +* Mit der neuen Option '--strip-duplicate-descr' werden Einheiten- + beschreibungen abgekürzt, wenn sie sich in einer Region wiederholen, + d.h. es wird nur das erste Auftreten in voller Länge gezeigt + (#35/b499) + +* Erste Version eines Crash-Handlers unter Windows und Linux, um + möglichen Bugs leichter auf die Spur zu kommen. + (#32/#39/#48/b486) + +* Vorlage setzt nun bei erzeugten CRs das 'Konfiguration'-Attribut auf + "Vorlage" (#46/b486) + +* Mit den neuen Funktionen 'exp(v)', 'log(v)' und 'log10(v)' hat man + nun einige Erleichterung bei Berechnungen die darauf angewiesen sind. + Insbesondere für Heldenberechnungen könnte log10 nützlich sein. + (#38/b485) + +* Es gibt nun die neuen Funktionen 'and(v1,v2)', 'or(v1,v2)', + 'xor(v1,v2)' sowie 'not(v)' um binäre Operationen auf Integern + durchführen zu können. (b480) + +* Statt der bisherigen Operatoren für logisches UND ('&') bzw. ODER + ('|'), kann nun die in den meisten Skriptsprachen übliche Version + '&&' bzw. '||' benutzt werden. Für eine zukünftige Version 2 wird + darauf komplett umgestiegen, also ist zu empfehlen, dies mit der + Zeit anzupassen. Die Option '--version2' wurde um eine Warnung + zu dieser Änderung erweitert. (b480) + +* Die Option '--kkall' bewirkt, wie die alte Option '-kk' eine grosse + Karte zu Beginn der Zugvorlage. Im Gegensatz zu dieser, die nur die + Dimensionen des Basis-Reports berücksichtigt, werden bei der neuen + Option alle Regionen berücksichtigt, die Karte kann also verdammt + groß werden, wenn man z.B. irgendwelche Welten-CRs an Vorlage mit + übergibt. (#19/b480) + +* Mittels der Option '--output-encoding ' kann man den für die + Ausgabe verwendeten Zeichensatz wählen. Unterstützt werden zur Zeit + iso-8859-1, macroman sowie cp437 und cp850, wobei iso-8859-1 der + Default ist (b479) + +* Der neue Befehl '#encoding ' ermoglicht es, in Skript- + Dateien das verwendete Encoding festzulegen. Erlaubt sind zur Zeit + iso-8859-1, macroman sowie cp437 und cp850, wobei iso-8859-1 der + Default ist. Damit das funktioniert, muß #encoding als allererstes + in der Ersten Zeile stehen. (b479) + +* Mit der neuen Option '--fullcom' kann man nun aktivieren, das Vorlage + vor der Ausführung jedwelcher Skripte schon alle persistenten + Kommentare in UNIT.OUTPUT übernimmt. Trotzdem wirken sich Änderungen + durch Referenz-Parameter auf die Zeilen aus, so das es bei + gleichzeitiger Veränderung des Arrays durch diese und durch direkte + Manipulation von UNIT.OUTPUT zu einer Warnung kommt, das Mischen ist + also zu vermeiden. + + Zudem gibt es durch die Option eine neue Vorlage-Variable namens + $CURRENTMETA die den Index des aktuell ausgeführten Inline-Befehls + enthält, befindet man sich ausserhalb eines Inline-Befehls enthält + die Variable -1. Man kann also den aktuellen Befehl löschen, indem + man einfach folgendes Konstrukt benutzt: + + #if $CURRENTMETA>=0 + { + UNIT.OUTPUT[$CURRENTMETA]='' + } + + (#17/b478) + +* Mit der Option '--cfgpath' kann man nun den Default-Path für Config- + Files überschreiben, welcher unter Windows der Pfad des EXE und unter + den anderen Systemen $HOME ist (b477) + +* Mit der neuen Option '--mapr' wird wie mit '-map' ein Karten-CR + ausgegeben, in diesem werden aber auch die Runden-Infos eingetragen + (#16/b474) + +* Neben den REPORT, REGION und UNIT verfügen nun auch SHIP und BUILDING + über ein Attribut Runde, welches aus Skripten heraus genutzt werden + kann (#15/b474) + +* Die Namen der Konfigurationsdateien haben sich geringfügig geändert: + Sie sind nun immer komplett aus Kleinbuchstaben zusammengesetzt und + alle Leerzeichen werden entfernt. Das hat im Moment nur Auswirkungen + für Vinyambar, da die Spielnamen Leerzeichen enthalten (was einige + Probleme bereitet hat) (b473) + +* Vorlage versucht nun Konfigurationsdaten zuerst aus einer User-Datei + (z.B. 'eressea-user.cfg' bzw. '.eressea-user-rc' zu laden, bevor die + normale, mitgelieferte verwendet wird. So kann man eigene + Einstellungen haben ohne das diese immer mit einem Update übertragen + werden müssen. Das ganze funktioniert nur für ganze Abschnitte, d.h. + man kann nicht einzelte Zeilen eines Abschnittes übersteuern (b473) + +* Bei Verwendung der Funktion 'match(,)' werden nun der + Treffer und optional enthaltene Unterausdrücke in speziellen lokalen + Variablen (ähnlich Perl und dem Ersetzungsmuster von 'change()') + abgelegt. Diese sind: + + $& der gesammte Treffer + $` kompletter Text vor dem Treffer + $´ kompletter Text hinter dem Treffer (in Perl $') + $n n-ter Unterausdruck (z.B. $1, $5, $13, ...) + $+ letzter Unterausdruck + + Beispiel: + + $text='Ein dicker runder Gnom' + #if match($text,'d(\\w+)r') + { + ; Hier sind jetzt folgende Werte gesetzt: + ; $&='dicker' + ; $1='icke' + ; $+='icke' + ; $`='Ein ' + ; $´=' runder Gnom' + } + + (b470) + +* Diverse kleinere Performance-Optimierungen in der Dateibehandlung + (b470) + +* Mit der neuen Funktion '$fh=system()' kann man nun externe + Programme aufrufen. Die Rückgabe ist dabei ein Datei-Handle über das + man auf die Ausgaben des Programmes so zugreifen kann, aler ob es + sich um eine geöffnete Datei handelt. Am Ende muß man so geöffnete + Handles ganz normal mit close() schliessen. An den + Rückgabewert des Programmes kommt man nach dem schliessen über + 'statustext()' welches dann den Wert als String liefert + (b468) + +* Mit den neuen Funktionen '$val=read()' bzw. + '$len=write(,)' können Werte in offene Dateien + geschrieben und wieder gelesen werden. Dies funktioniert auch für + komplexe, evtl. geschachtelte Werte wie Dictionaries oder Arrays. Die + Daten werden dabei in einer vereinfachten XML-Schreibweise geschrieben + wobei beim Lesen keinerlei Verifikation erfolgt. Änderungen an so + geschriebenen Dateien sollten also sehr vorsichtig erfolgen (b463) + +* Das Kapitel '[Ships]' in der Konfigurationsdatei enhält nun auch die + anderen Werte (Reichweite und Talente, muß für einige Spiel-Dateien + evtl. noch nachgepflegt werden) (b463) + +* Mit '#tag EINHEIT[einheitennummer] ' kann man nun + auch aus anderen Kontexten heraus Tags in Einheiten-Blöcke einfügen, + mit '#tag REGION[,,] ' geht das gleiche + auch für Region-Blöcke, in beiden Fällen gilt aber, dass es im CR + schon einen Block dafür geben muß, es werden also keine neuen Blöcke + im CR erzeugt (b463) + +* Mittels der Option '-gr ' kann man erreichen, das nur für + Einheiten einer bestimmten Gruppe eine Zugvorlage erstellt wird. + Es werden die anderen Einheiten der Partei als verbündete Einheiten + angezeigt, falls man Fremde Einheiten anzeigen lässt. Allerdings + werden alle Berechnungen die die Region betreffen weiterhin auf Basis + der Partei ausgeführt, d.h. Einnahmen, Ausgaben, Personenzahlen oder + Materialpool sind weiter die der ganzen Partei. Dieses Feature + funktioniert nicht für die CR-Ausgabe. (b462) + +* Mittels Backslash ('\') können lange Zeilen, auch innerhalb von + Ausdrücken, umgebrochen werden, wobei in diesem Fall kein Leerzeichen + vor dem Backslash kommen darf, damit Vorlage weiß das der Ausdruck noch + nicht zuende ist. Alle Leerzeichen oder Tabs am Beginn der Folgezeile + werden ignoriert, die Fortsetzung darf also eingerückt werden (b462) + +* Mit der überscheibbaren Funktion CalcUnitCapacities kann man für eine + Einheit die Kapazitäten per Skript berechnen, falls der interne + Algorithmus nicht für ein bestimmtes Spiel funktioniert. Die Funktion + muß dann ein Array zurückliefern, das folgenden Aufbau hat: + + [,,,,,] + + Hier bedeuten: + + Ein float mit der Kapazität zum reiten + Ein float mit der freien Kapazität zum reiten + Wieviele Pferde zum Reiten zuviel sind (>=0) + Ein float mit der Kapazität zum gehen + Ein float mit der freien Kapazität zum gehen + Wieviele Pferde zum Gehen zuviel sind (>=0) + + Historisch gewachsen ist fKapReiten ohne die Fahrer, obwohl sie nötig + sind. Das ist wie bei Autos, wo man die max. Zuladung auch ohne das + Gewicht des Fahrers zu berücksichtigen angibt. Bei der freinen + Kapazität zum Reiten sind die Fahrer hingegen mit abgezogen. + + Die Funktion hat also den Aufbau: + + #func CalcUnitCapacities $ENr + { + ; passende Variable anlegen + #var $fKapReiten $fFKapReiten $nRHO $fKapGehen $fFKapGehen $nGHO + + ; Hier wird nun alles mögliche berechnet + ; ... + + ; Nun noch die Ergebnisse richtig verpackt zurückgeben + #return [$fKapReiten,$fFKapReiten,$nRHO,$fKapGehen,$fFKapGehen,$nGHO] + } + + (b462) + +* Mit den neuen Befehlen #error und #warning kann man Fehlermeldungen + bzw. Warnungen aus Skripten heraus erzeugen. Diese landen im Fehler- + Kanal oder im Umlenkziel von '-e' (b461) + +* Zugriff auf beliebige Textdateien ist jetzt möglich. Dazu gibt es ein + paar neue Funktionen: + + $handle=open(,) + + Die in angegebene Datei wird geöffnet. Ist + MODE_READ, so wird die Datei zum Lesen, bei MODE_WRITE zum + Schreiben, bei MODE_APPEND zum Anhängen geöffnet. MODE_READ, + MODE_WRITE und MODE_APPEND sind Konstanten die in 'standard.vms' + deklariert sind. Ob es geklappt hat erfragt man mit 'status(...)' + da die Funktion immer ein Handle liefert, das also auch im + Fehlerfall wieder mit 'close(...)' geschlossen werden muß. + + $line=readline() + + Es wird eine Zeile aus einer zum Lesen geöffneten Datei gelesen + und der Inhalt (ohne abschliessendes LineFeed o.ä.) als String + zurückgegeben. + + $num=writeline(,) + + Es wird der Text in eine zum Schreiben geöffnete Datei geschrieben + und zudem ein abschliessendes Zeilenende (je nach OS) angefügt. + Die Zahl der geschriebenen Bytes wird zurückgegeben, oder 0, wenn + ein Fehler auftrat + + $status=status() + + Es wird der Status des Handles abgefragt, gültige Ergebnisse sind + STAT_OK bei keinem Problem, STAT_EOF bei erreichen des Dateiendes, + STAT_ERROR wenn ein Fehlerzustand vorliegt. Die verschiedenen + Konstanten sind in 'standard.vms' zu finden. + + $text=statustext() + + Wenn ein Fehlerzustand für dieses Handle vorliegt, so kann eine + dazugehörige Meldung mit dieser Funktion ermittelt werden um + eine brauchbare Fehlermeldung auszugeben. + + $status=close() + + Eine offene Datei wird geschlossen. Es müssen alle Handles, auch + die bei denen das Öffnen nicht geklappt hat, geschlossen werden. + Es wird ein Status wie bei 'status(...)' zurückgegeben. + + #table dump + + Um eine formatierte Tabelle in eine Datei zu schreiben kann man + bei dem 'DUMP' einfach ein Dateihandle anhängen. + + (b461) + +* Gibt man mehrere Basis-CRs der gleichen Runde aber verschiedener + Parteien an ohne mit -p eine auszuwählen, so wird für alle Parteien + eine Vorlage bzw. ein CR erzeugt, was nur zusammen mit '-ox ext' + Sinn macht damit nicht alles in einer Datei landet. Die in diesem + Fall noch fehlenden Passwörter kann man über die Config-Datei + angeben, wo jetzt im Kapitel '[Options]' neben der alten Syntax + 'Passwort = "MeinGeheimesPasswort"' auch die neue Schreibweise + 'Passwort = "DasPasswortDerPartei"' verwendet werden + kann. Also z.B.: + + [Options] + Passwort1L3 = "DasPasswortDerPartei1L3" + + Hierbei muß die Parteinummer in der Basis angegeben werden die auch + das Spiel verwendet, und (leider) bei Eressea exakt die Eressea- + Schreibweise der Base36-Zahlen verwendet werden, sprich mit großem + 'L' und keinen anderen Buchstaben, da es sonst nicht gefunden wird. + ACHTUNG: Bei Spielen die die Zahlenbasis für Parteinummer aus der + Konfig-Datei bekommen, muß diese Option vor den Passwörtern kommen, + sonst weiß Vorlage beim Einlesen noch nicht welche Basis für die + Parteinummer Verwendung findet. + (b461) + +* Die Funktion xname erlaubt nun auch Hochkommas sowie die verschiedenen + Buchstaben aus iso-8859-1 mit Akzenten u.ä. in Namen (b460) + +* Die Maskierung von Silbermengen fremder Einheiten in Atlantis-Ablegern + ohne Silberkasetten etc. als Gegenstand kann nun über das Kapitel + + [SilverMasks] + "Silberbeutel", + "Silberkasette", + ... + + konfiguriert werden. Will man einfach die Silbermengen sehen so löscht + man die Einträge, ansonsten werden ab der Silbermenge die + angegebenen Schlüsselwörter verwendet. Für Alt-Eressea also z.B.: + + [SilverMasks] + "Silberbeutel", 500 + "Silberkasette, 5000 + + Dadurch bekommt man ab 500 Silberstücken "Silberbeutel" angezeigt, ab + 5000 Silberkasetten, will man hingegen einfach nur die Silbermünzen + sehen, kann man folgendes Verwenden: + + [SilverMasks] + "Silber", 1 + + (b460) + +* Wenn keine verwertbaren Nachrichten für die Einnahmen/Ausgaben einer + Region gefunden werden konnten wird die Ausgabe des Null-Wertes + unterdrückt, bzw. wenn verfügbar nur Nahrungskosten ausgegeben (b459) + +* Mit der neuen Option '--fullregs' werden OnRegion und EndRegion nicht + nur für Regionen des Basis-CRs mit eigenen Einheiten aufgerufen, + sondern für alle des Basis-CRs und alle zu denen man visibility-Tags + aus irgendwelchen CRs hat (b459) + +* Die zum Errechnen von Einnahmen/Ausgaben verwendeten Routinen können + auf Regionsebene überschrieben werden; Sobald Funktionen mit den Namen + 'CalcRegionIncome' und 'CalcRegionExpenses' gefunden werden, verwendet + Vorlage diese (und errechnet dadurch auch die Summen über die + Regionen). (b459) + +* Mit dem neuen Config-Kapitel [Kampfstatus] kann man die in einigen + Spielen anders vergebenen Zuordnungen zwischen Kampfstati und CR-Wert + festlegen; Die Syntax lautet: + + [Kampfstatus] + "", + + Für Eressea ist das zum Beispiel: + + [Kampfstatus] + "aggressiv", 0 + "vorne", 1 + "hinten", 2 + "defensiv", 3 + "kämpft nicht", 4 + "flieht", 5 + + Andere Spiele brauchen da eine Anpassung (b458) + +* Fehlermeldungen werden mit der neuen Option '-wfirst' nur einmal + ausgegeben, wenn sie exakt identisch sind, sprich auch der Ort + übereinstimmt, um in Schleifen oder bei '--version2' die Zahl der + Meldungen zu reduzieren (b458) + +* Mit der neuen Option '--version2' kann man Vorlage anweisen für + Konstrukte die sich zu Version 2 ändern werden Warnungen auszugeben; + Im Moment geschieht dies bei Verwendung von Inplace-Ausdrücken (b457) + +* Auf Resourcen kann nun auch mit 'REGION.RESOURCE[''].' + zugegriffen werden; die Unterscheidung wird dabei über den Typ + getroffen, sprich bei Strings wird nach dem Namen, bei Integer nach + dem Index zugegriffen (b457) + +* Prozeduren können nun mit einem '#return' ohne Parameter aus jeder + Stelle heraus verlassen werden (b456) + +* Bei unbekannten Funktionen wird nun mit der Fehlermeldung noch der + Name der Funktion angegeben (b454) + +* Bugfix: random() lieferte in rc7 immer die gleiche Zufallskette + (#57/b511) + +* Bugfix: #tag konnte keine Attribute für Einheiten aus OnInit heraus + setzen (#56/b509) + +* Bugfix: Uralter Bug im Case/Umlaut/Space-insensitiven Vergleich hat + in sehr seltenen Fällen (in der Regel bei UNIT.OUTPUT-Aktivitäten) + zum Absturz geführt (#49/b505) + +* Bugfix: Die Funktion 'change(txt,regex,pat)' hat nicht immer einen + Treffer gefunden wenn Subpatterns im Ersatzstring referenziert + wurden; So hat change('Foobar','.*(o+).*','The M$1n') die beiden + 'o' nicht gefunden (#54/b504) + +* Bugfix: Durch ein Problem in der Pufferbehandlung des CR-Readers + wurde bei überlangen Zeilen der Puffer immer um 0 Bytes vergrössert, + was zu einer Endlosschleife führte (#53/b503) + +* Bugfix: Ein Subtiler Bug im Objekt-Handling hat unter Linux die + Erzeugung vollständiger Zugvorlagen verhindert (#45/b496) + +* Bugfix: Fehlerhafte Hierarchie-Behandlung verhinderte Kombination + von CRTags und CRHierarchy (#30/b492) + +* Bugfix: Rassenbezeichnungen konnten bei Einheiten bestehend aus + einer Person u.U. fehlerhaft abgeschnitten werden. (#26/b491) + +* Bugfix: Der Message-Renderer behandelte '$if()' ohne Else-Fall, die + Verknüpfung mittels '.'-Operator sowie ',' in Strings hinter einem + Funktionsaufruf nicht korrekt (#41/b490) + +* Bugfix: Ein in Anführungszeichen stehendes Password aus dem Config- + File wurde mit doppelten schliessenden Anführungszeichen in die + Zugvorlage übernommen (#44/b490) + +* Bugfix: Die Kapazitäts- und Gewichtsberechnung wird jetzt auf zwei + Nachkommastellen gerundet ausgegeben (#27/b490) + +* Bugfix: Fliesskomma-Literale mit einem Nachkommaanteil von 0 (z.B. + 34.0) wurden fälschlicherweise in Integer konvertiert (#47/b488) + +* Bugfix: sqrt() lieferte bei negativen Parametern eine unerwartete + Ausnahme (b485) + +* Bugfix: Bei regulären Ausdrücken mit Subpatterns die nicht zutrafen + konnte die Erzeugung der Match-Variablen einen Absturz verursachen + (Beispiel: '((a)|(z))(bc)' angewendet auf 'abc' -> $1/$2='a' + $3= $4='bc') (b485) + +* Bugfix: Bei der Kapazitätsberechnung von wagenziehenden Trollen kam + es unter Umständen zu einer Division durch null, welche eine + 'Unerwartete Ausnahme' zur Folge hatte. (b480) + +* Bugfix: Der Zugriff auf die REGION-Subobjekte REGIONSBOTSCHAFTEN, + UMGEBUNG, REGIONSKOMMENTAR und REGIONSEREIGNISSE funktionierte aus + Skripts heraus nicht (#01/b477) + +* Bugfix: Traf der Interpreter auf #proc, #func oder #include, so hat er + einen ungültigen Befehl gemeldet, statt auf fehlende Klammerung + hinzuweisen (b476) + +* Bugfix: Die Funktion 'xname()' reagierte nicht besonders gut auf + leere Regelstrings oder fehlerhafte Namensdefinitions-Dateien, sondern + warf i.d.R. unerwartete Ausnahmebehandlungen, statt Fehlermeldungen + (b475) + +* Bugfix: Die Funktion 'change()' funktionierte nicht korrekt, wenn eine + zu ersetzendes Untermuster zu Beginn des Erstzungsstrings verwendet + wurde (b474) + +* Bugfix: Magiegebiet-Angaben in mehreren Partei-Blöcken führte zu einer + Warnung (#24/b473) + +* Bugfix: Der Zugriff auf KAMPFZAUBER funktionierte nicht (#20/b473) + +* Bugfix: Die Verwendung undefinierter Variablen wurde trotz '-fd' nicht + immer in Funktionsaufrufen angemahnt (b472) + +* Bugfix: Die Behandlung von Behälterreferenzen konnte Zugriffsfehler + erzeugen die je nach Situation in "Unerwartete Ausnahmebehandlung" + oder einem Absturz endeten (b472) + +* Bugfix: Beim einlesen leerer XML-Dateien blieb Vorlage u.U. hängen + (b471) + +* Bugfix: Das Schreiben von Dictionaries in XML war fehlerhaft (b471) + +* Bugfix: Bei fehlendem schliessendem Hochkomma für String-Konstanten + konnte Vorlage nach der artigen Fehlermeldung volkommen abstürzen + (b471) + +* Bugfix: Die Option '-mr' hat wegen fehlerhaft ausgewerteter Parameter + nicht funktioniert (b469) + +* Bugfix: Alle Ausgaben auf die Konsole werden unter Windows nun + umgewandelt, um Umlaute und Sonderzeichen, soweit möglich, korrekt + auszugeben. Gleiches geschieht unter Windows mit den Eingaben von + der Konsole, bei denen auch nicht alle Zeichen korrekt behandelt + wurden (b469) + +* Bugfix: Fehlerhafte Behandlung von #config TABH und #config NESTED + (b467) + +* Bugfix: Ein weiterer Fehler in der neuen Behandlung der Konfig- + Dateien, der zu falschen Datentypen führen konnte, wurde behoben + (b466) + +* Bugfix: Abstürze beim Einlesen von CRs durch Optimierungsfehler des + Compilers durch Wechsel auf eine neue Version behoben und zudem die + Größe des Programmes ein gutes Stück reduziert. (b465) + +* Bugfix: Das Einlesen von Konfigurationsdateien hatte diverse Fehler + (b464) + +* Bugfix: Bei Fehlern in Konfigurationsdateien wurden keine Meldungen + ausgegeben (b464) + +* Bugfix: 'REGION.RESOURCE[].' funktionierte in 1.6rc1 + nicht mehr korrekt (b464) + +* Bugfix: Vorlage hat bei Schiffen für die Kapazitätsberechnung nicht + auf die Daten in der Konfigurations-Datei geachtet (b462) + +* Bugfix: #tag funktionierte nicht korrekt für schon vorhandene Tags + (b462) + +* Bugfix: Bei Resourcen aus RESOURCE-Blöcken, die in der letzten Runde + noch vorhanden, diese Runde aber abgebaut sind (Bäume), wurde die + Änderung unterschlagen und die Resource komplett unterdrückt (b458) + +* Bugfix: In Config-Dateien dürfen Strings nun auch Kommas enthalten. + (b458) + +* Bugfix: Von Vorlage errechnete Gewichte werden nun intern von den + Rundungsstörungen bereinigt (b457) + +* Bugfix: 'UNIT.ANDEREPARTEI' lieferte eine Dezimalzahl, auch wenn + in dem Spiel Parteinummern in Base36 verwendet wurden (b457) + +* Bugfix: Im Debugger funktioniert nun auch e crop($b[$i],'(\\d+) ') + es wurden Debuggerausdrücke die Hochkommas enthalten, nicht korrekt + erkannt (b456) + +* Bugfix: Bei CRs von Spielen ohne RESOURCE-Blöcke wurde die Differenz + für Steine nicht angezeigt (b456) + +* Bugfix: Die Funktion 'flatten()' lieferte nur max. 251 Zeichen zurück + (b454) + +* Bugfix: 'UNIT.TARNUNG' war immernoch falsch, da es nun das Talent + 'UNIT.TARNUNG.STUFE' überdeckt hat (b454) + + +---[Vorlage V1.5.04]--- + +* Definiert man eine Funktion mit dem Namen 'OutputLineFilter', so wird + diese bei der Ausgabe der Zugvorlage für jede Ausgabezeile aufgerufen + und kann diese verändern und zurückgeben; Will man z.B. die Einrückung + auf Tabs umstellen, so könnte dies wie folgt geschehen: + + #func OutputLineFilter $Line + { + $Line=change($Line,'^ ','\09\09'); + $Line=change($Line,'^ ','\09'); + $Line=change($Line,'^ ',''); + #return $Line + } + + Dadurch wird halt für Zeilen mit vier Leerzeichen am Zeilenanfang + (also die Einheiten-Befehle) eine Einrückung durch zwei Tabs, für die + Zeilen mit zwei Leerzeichen (Einheitenkommentare) durch ein Tab und + mit einem Leerzeichen (Regionsinfos, etc.) ohne Einrückung gewählt + + HINWEIS: Es ist zu bemerken, das ein solcher Ausgabefilter die Ausgabe + der Zugvorlage je nach Rechner zumindest merkbar verzögert, also nicht + wundern, wenn er bei Verwendung eines Filters gegen Ende eine kurze + Denkpause einlegt ;-) + (b453) + +* Werden Funktionen oder Prozeduren gleichen Namens mehrfach definiert, + so führt dies nun zu einer Warnung (b453) + +* Mittels der neuen Option '--pedantic' werden Zugriffe auf Objekte oder + Attribute, die weder intern, noch im CFG-File (unter [CRTags] oder + [CRHierarchy]) codiert sind, mit einer Fehlermeldung quittiert um z.B. + Tippfehler leichter zu finden (b453) + +* Mit dem neuen Config-Datei-Kapitel [CRHierarchy] können Vorlage nun + auch neue Blöcke "schmackhaft" gemacht werden; Dazu ist folgendes + Format zu verwenden: + + [CRHierarchy] + "BLOCKNAME", , 0, 0, , ["KEY1", ...,] ["SUBBLK1", ...] + + Dabei bedeuten: + BLOCKNAME Name des Blocks, dies können zur Zeit nicht alle Blöcke + (aber die meisten) sein + 0: Block hat keine IDs oder diese sind nicht global + eindeutig + 1: Block hat IDs und ist global eindeutig + 0, 0 Für Erweiterungen vorgesehen + 0-n: Block hat (max.) 0 bis n IDs + "KEYn" Namen unter der die IDs ansprechbar sein sollen + "SUBBLKn" Namen der in diesem Block erlaubten Subblöcke + + ACHTUNG: Das ist ein experimentelles Feature, das nicht durchgängig + von Vorlage benutzt wird; das bedeutet, man kann zwar neue Blöcke + damit in Vorlage einbinden, aber bestehende dürfen damit nicht + umdefiniert werden, da dies nicht mit der alten pre-2.0-Datenhaltung + harmoniert, ebenso hat das Flag noch keinerlei Auswirkungen + + HINWEIS: Bei Verwendung von Keys kann nur über diese auf das Subobjekt + zugegriffen werden; Ein Zugriff über Index ist zur Zeit weder für die + Attribute, noch die Objekte möglich + (b452) + +* Bugfix: Ausdrücke wie 'huhu'bu ergaben keine Syntax-Fehlermeldung + (b452) + +* Bugfix: '#ifregion' außerhalb eines gültigen Regionskontextes (z.B. + OnInit) führte zu einer unerwartetes Ausnahmebehandlung (b452) + +* Bugfix: 'PARTEI[].STATUS' lieferte beim Vorhandensein von + Gruppen den Allianz-Status der Partei zur letzten Gruppe + hinter der Partei im CR, nicht zu der Partei selbst (b452) + +* Bugfix: 'UNIT.TARNUNG' hat nicht den aktuellen Tarnungsstatus der + Einheit geliefert (b452) + +* Bugfix: '#table debug' gab vor den Zeilen noch zwei Leerzeichen zur + Einrückung aus (b452) + +* Bugfix: Die Berücksichtigung von noskillpoints funktionierte nicht + korrekt, was z.B. unter Vinyambar bewirkte, Talenttage zu schlucken + (b452) + + +---[Vorlage V1.5.03]--- + +* Als Vorbereitung auf die 2.0 kann man schon jetzt statt 'unit', + 'building' und 'ship' auch mit 'einheit', 'burg' und 'schiff' + arbeiten (b451) + +* Die Objektnamen alleine, ohne alles, lieferten eine 1, wenn man in + einem gültigen Kontext für das Objekt war (z.B. 'unit' in OnUnit); + das ist jetzt nicht mehr möglich, 'unit' liefert jetzt immer 'unit' + als Text; Für Zugriffe mit Index oder Subblöcken, funktioniert der + Test auf existierende Instanzen natürlich weiterhin (b451) + +* Für Eressea wird nun ERESSEA anstelle von PARTEI als Startbefehl in + Zugvorlage geschrieben (b450) + +* Mit 'BUILDINGS[].KAPAZITAET' kann man feststellen ob ein + Bauwerkstyp eine Kapazitätsbegrenzung hat (z.B. Leuchtturm) oder diese + von der Größe abhängt (dann ist BUILDINGS[].KAPAZITAET gleich 0) + (b450) + +* Die neue Callback-Prozedur CreateUnitHeader kann verwendet werden um + selber das Aussehen der eigenen Einheiten-Blöcke in der Zugvorlage zu + bestimmen (b450) + +* Durch 'REGION.MESSAGE.SIZE', 'REGION.MESSAGE[].' + kann man über die Nachrichten in einer Region iterieren (b450) + +* Mittels 'REGION.POOL.SIZE', 'REGION.POOL[].NAME' sowie + 'REGION.POOL[].ANZAHL' kann man über den Materialpool einer + Region iterieren (b450) + +* Die neue Option '-nv' unterdrückt die Ausgabe einer Zugvorlage für + den Fall das man nur an den Debugausgaben seines Skriptes interessiert + ist (b450) + +* Mittels 'REPORT.PARTEI.SIZE' bzw. 'REPORT.PARTEI[]' sowie + 'REPORT.GRUPPE.SIZE' bzw. 'REPORT.GRUPPE[]' kann man an + über die Parteien und Gruppen des Reports iterieren (b449) + +* Das neue Attribut 'PARTEI.ALLIANZ[]' bzw. 'GRUPPE.ALLIANZ[]' + ermöglicht die Abfrage des Status einer Partei zu der Partei oder + Gruppe (nur bei eigener Partei oder eigenen Gruppen) (b449) + +* Der Zugriff auf Regionen über 'REPORT.REGION[]' wurde + beschleunigt (b449) + +* Mit 'REPORT.OPTIONEN.SIZE' sowie 'REPORT.OPTIONEN[].NAME' und + 'REPORT.OPTIONEN[].AKTIV' kann man auf die Optionen zugreifen + (b449) + +* Der Befehl #debug rückt jetzt nichtmehr selbsständig ein, damit man + damit universeller Text-Dateien durch umleiten der Ausgaben erzeugen + kann (b449) + +* Mit dem neuen Objekt 'DB' bekommt man Zugriff auf zwei interne + Strukturen von Vorlage, in denen die Regionen und Einheiten aller + CRs qualitätssortiert abgelegt sind; Man kann aber jeweils nur auf + die nach Vorlage-Meinung besten Versionen zugreifen und diese werden + dann über 'DB.REGION.SIZE' bzw. 'DB.REGION[]' sowie über + 'DB.UNIT.SIZE' bzw. 'DB.UNIT[]' angesprochen (b449) + +* Mit den Attributen 'REGION.RUNDE' und 'UNIT.RUNDE' kann man + feststellen, aus welcher Runde die Informationen über eine Region + bzw. Einheit stammen, die in derinternen Datenbank verwendung finden + (b449) + +* Es gibt jetzt mit 'UNIT.TALENTE.[]' eine generische + Möglichkeit auf die Felder des Talent-Blockes zuzugreifen; So gibt + z.B. 'UNIT.TALENTE.HIEBWAFFEN[1]' zur Zeit die Talentstufe des + Hiebwaffentalentes zurück, sofern es existiert, anderenfalls 0 + (b449) + +* Warnungen wegen unbekannten Feldkennungen (CR-Tags) können nun auch + gezielt unterdrückt werden, indem man in das Config-File unter dem + Kapitel [CRTags] passende Einträge erzeugt (Anführungszeichen sind + hierbei zwingend): + + [CRTags] + "BLOCKNAME", "Tagname1", "Tagname2" ... (usw.) + + Das bewirkt, das die CR-Tags für den angegebenen Block gültig sind. + Man braucht also für jeden Block in dem man Tags erlauben will eine + eigene Zeile in dem Kapitel. (Neue Blöcke kann man Vorlage damit aber + nicht "unterjubeln".) + (b449) + +* Anpassungen an CR-Format V64 (b449) + +* Bei Eressea CR V64 Dateien mit gesetztem noskillpoints-Tag schaltet + Vorlage die Anzeige von Lerntagen ab und bei Verwendung von -td + wird nun statt dessen eine Stufenänderung angezeigt (b449) + +* Um alte Skripte zu unterstützen gibt es solange Bedarf besteht noch + 'UNIT..TAGE' wobei der Wert dem Entspricht der gerade die + angegebene Stufe ergibt (b449) + +* Vorlage unterdrückt nun die Warnung bei Magellan-CRs mit dem Tag + 'ejcOrdersConfirmed' im CR und stellt das Flag als das Attribut + 'UNIT.EJCORDERSCONFIRMED' zur Verfügung (b449) + +* Anpassungen an CR-Format V63 (b448) + +* Es gibt mit den CR V63 Reports das neue Attribut 'REGION.VISIBILITY' + (b448) + +* Bugfix: Im Fall das der Speicher ausging, gab es i.d.R. nur eine + "Unerwartete Ausnahmebehandlung", jetzt wird der Speichermangel + gemeldet (b451) + +* Bugfix: Wenn man in Stringliteralen Hochkommas benutzt hat ohne sie + mit dem Backslash zu escapen (also z.B. #ifregion 'Tak'Kal' statt + richtig #ifregion 'Tak\'Kal'), so geriet Vorlage in einen instabielen + Zustand oder blieb hängen (b451) + +* Bugfix: Für SCHEMEN wurde irrtümlich eine Meldung bezüglich eines + unbekannten Regionstyps "Unbekannt" erzeugt (b451) + +* Bugfix: Beim Weglassen von Referenz-Parametern konnte es zu internen + Fehlern kommen (b451) + +* Bugfix: In Mallorn-Regionen tauchten in der Zugvorlage sowohl + Schößlinge als auch Mallornschößlinge auf (b451) + +* Bugfix: 'UNIT.HP' wurde nicht unterstützt (b449) + +* Bugfix: 'REGION.BESCHR' wurde immer als 0 zurückgegeben (b449) + +* Bugfix: Das Message-Rendering hat nicht mehr funktioniert (b449) + + +---[Vorlage V1.5.02]--- + +* Anpassungen an CR-Format V62 + +* In 'REGION.DURCHSCHIFFUNG.SIZE' und 'REGION.DURCHSCHIFFUNG[]' + kann man nun auf die, in Eressea ab CRV62 von den Durchreisen + getrennten, Schiffsbewegungen zugreifen (b447) + +* Der Einheitenstatus 'bekommt in Kämpfen keine Hilfe' ist nun über + 'UNIT.UNAIDED' erreichbar, wobei ungleich 0 für eine Aktivierung des + Status steht (b446) + +* Alle Warnungen können nun mit der Option '-wall' unterdrückt werden + (b446) + +* Bugfix: Das expandieren von leeren Stringvariablen als Befehl führte + zu einem Fehler (b447) + +* Bugfix: Variablennamen konnten keine Umlaute enthalten (b446) + +* Bugfix: Konfigurationsfile enthielt noch die falschen Burgengrößen + (b446) + +* Bugfix: Regionsnachrichten konnten u.U. doppelt in der Zugvorlage + erscheinen (b446) + +* Bugfix: Vorlage nimmt nun konsequent für unbekannte Rassen ein Gewicht + von 10GE pro Person an (b446) + +* Bugfix: im Gegensatz zu den Regionsausgaben wurden die Gesamtausgaben + fehlerhaft berechnet, d.h. es wurde nicht die richtige Anzahl zu + versorgender Personen verwendet (b446) + +* Bugfix: Man konnte nicht mit '#if report[] {...}' abfragen ob es + überhaupt einen Report der betroffenen Runde gibt (b446) + + +---[Vorlage V1.5.01]--- + +* Ein Konfigurationsfile für Sitanleta ist nun Teil der Distribution, + der, bei Bedarf oder Problemen durch Änderungen in Aktueller Version + unter http://www.sitanleta.de/downloads/index.html heruntergeladen + werden kann (b445) + +* Der neue CR-Block BATTLESPEC wird zur Zeit ignoriert, aber immerhin + keine Fehlermeldung/Warnung ausgegeben (b445) + +* Bugfix: Bei der Untersuchung potentiell unsichtbarer Fremdeinheiten + konnte es zu einem Absturz kommen (b445) + + +---[Vorlage V1.5 final]--- + +* Für die Erzeugung einer Zugvorlage, die sich möglichst nah an der dem + NR beigelegten orientiert gibt es die neue Option '-nrzv' (b444) + +* Von Spielern geführte Fremdrassen die keine regulären Spielerrasse + angehören, können nun einfach als Rassen in der Konfiguration + eingetragen werden und werden dann, bezüglich Unterhalt, Kapazität + und Gewicht auch so behandelt (b444) + +* Vorlage ist bei Multi-Integer-CR-Feldern nun dagegen resistent, das + diese manchmal fäschlicherweise durch Kommas statt Leerzeichen + getrennt sind (b444) + +* Das Ausgabehandling von Vorlage wurde nochmals geringfügig geändert; + Ausgabeströme werden jetzt nach folgenden Regeln behandelt: + + int. log. Strom default Umlenkbar mit Bei '>' Bei '2>' + ------------------+--------+-----------------+-----------+----------- + Zug (auch CR) stdout -o/-ox Umlenkung - + Fehlermeldungen stderr -e - Umlenkung + (#debug/#t. debug) stderr* -do -* Umlenkung* + Trace (Timing) stderr* -to -* Umlenkung* + console (Debugger) stdout nicht umlenkbar -** -** + + *) Diese Ströme werden nach stdout geleitet, wenn Zugvorlage/CR mit + -o/-ox umgelenkt werden, in diesem Fall erfolgt eine Umlenkung + mit '>' + + **)Dieser Strom ist nicht umlenkbar. Werden sowohl sowohl stdout als + auch stderr umgelenkt und somit eine Ausgabe unmöglich, wird der + Debugger, ebenso wie der #input-Befehl inaktiv + (b443) + +* Es gibt jetzt die neuen Attribute 'UNIT.OUTPUT[]' sowie + 'UNIT.OUTPUT.SIZE' mit dem man die bereits durch Metabefehle erzeugten + Zeilen einer Einheit abrufen kann; Deses Feld wirkt wie ein echtes + #array, so das im Gegensatz zu allen anderen Attributen hier auch eine + Zuweisung funktioniert und so die Befehle einer Einheit auch aus den + globaleren Kontexten (OnInit, OnExit, ...) manipuliert werden können + (b443) + +* Mit der Syntax '[val,...]' können nun auch direkt Array-Literale + erzeugt werden, ein paar Beispiele: + + $t=[] ; $t wird ein leeres #array zugewiesen + $u=[1,'foo','bar',3.14] ; $u ist nun ein #array aus vier Elementen + $u[0]=['geht','auch',['geschachtelt']] + ; jetzt wurde dem 0ten Element von $u ein verschachteltes #array + ; zugewiesen + + (b443) + +* Die Zahl der Arbeitsplätze wird nun in der Regionsinfotabelle + angezeigt (b443) + +* Im neuen Konfigurations-Kapitel '[Resources]' wird eine Liste der + Resourcen einer Region und Ihr Platzbedarf (in Arbeitsplätzen pro + Stück) angegeben mit der die interne Arbeitsweise (Baeume=8, + Schoesslinge=4) bei Änderungen oder für andere Spiele überschrieben + werden kann (b443) + +* Bei Option '-td' werden jetzt auch für Aura die Änderungen angezeigt + (b443) + +* Im Skriptdebugger wird nur für die Ergebnisse des Befehls 'e' auch + der Typ mit angegeben (b443) + +* Mit dem neuen Skript-Debugger-Kommando 'w' kann man den Call-Stack + anzeigen lassen; Es wird dabei jede gerade ablaufende Prozedur oder + Funktion mit den Aufrufparametern angezeigt, wobei die oberste die + ist in der Vorlage gerade arbeitet, die darunter die jeweiligen + Aufrufer der darüberstehenden; (b443) + +* Mit dem neuen Skript-Debugger-Kommando 'b []' kann man bedingte + Traces anlegen; Jedes mal wenn ein Ausdruck angegeben wird, wird er + zur Liste der Trace-Bedingungen hinzugefügt, wird kein Parameter + übergeben, zeigt der Debugger eine Liste der gesetzten Bedingungen + an; Mit 'bd ' kann der n-te Eintrag der Liste gelöscht werden; + Die bedingten Traces bewirken, das vor jedem Befehl den Vorlage + ausführt geprüft wird, ob eine der Bedingungen erfüllt ist und falls + ja in den Debugger gesprungen wird; Die Bedingung die dazu führte + wird angezeigt; ACHTUNG: Viele oder komplexe Bedingungen verlangsamen + die Ausführungsgeschwindigkeit zum Teil erheblich! (b443) + +* Es gibt nun das neue Attribut 'REGION.ARBEITSPLAETZE' welches die Zahl + der Arbeitsplätze der Region insgesammt angibt, d.h. ohne eine + Berücksichtigung der Bauern; freie Arbeitsplätze sind dann also durch + 'REGION.ARBEITSPLAETZE-REGION.BAUERN' gegeben (b442) + +* Mit der optionalen neuen Callback-Prozedur 'CreateRegionHeader' kann + man nun selber das Aussehen des Regionskopfes bestimmen; Es können + so alle Infos oder Erscheinungformen von Regionsköpfen erstellt + werden, wobei für Eressea zu beachten ist, das min. die REGION-Zeile + erzeugt wird; Eine minimalistische Eressea-Version wäre also: + + #proc CreateRegionHeader + { + ; REGION-Zeile aufbauen + #var $Line + $Line='REGION '+region.x+','+region.y + #if region.z { $Line=$Line+','+region.z } + $Line=$Line+' \59 '+region.name+' ('+region.terrain+', ' + $Line=$Line+region.personen+', '+region.pool.silber+'$ Silber)' + + ; REGION-Zeile ausgeben + $Line + + ; ECheck-Kommentar + #message 'ECheck Lohn '+region.lohn + } + + (b442) + +* Die Report-Änderungen von CR-Version 59 werden nun erkannt (b442) + +* Auch die Mengen von in RESOURCE-Blöcken abgelegten Rohstoffen kann + man nun, wie sonst auch, mittels 'REGION.' abfragen + (b442) + +* Mittels 'REGION.RESOURCE.SIZE' und 'REGION.RESOURCE[].' + kann man auf alle Attribute der neuen RESOURCE-Blöcke zugreifen (b442) + +* Die neuen Zugriffe 'REPORT[].REGION' bzw. 'REPORT[].UNIT' + ermöglichen den Zugriff auf Regionen und Einheiten älterer Runden, + sofern CRs aus der Runde (AktuelleRunde+) übergeben wurden, + sollte also immer negativ sein (b442) + +* Der Wert von 'REGION.MALLORN' entspricht nun der Anzahl der Mallorn- + Bäume in der Region und ist nicht mehr nur ein Flag, es wird aber + in 'REGION.BAEUME' weiterhin die Zahl der Mallornbäume zurückgegeben + so das alte Skripts die nicht auf 'REGION.MALLORN==1' sondern auf + 'REGION.MALLORN' oder 'REGION.MALLORN>0' o.ä. abfragen weiterhin + funktionieren sollten (b442) + +* Mit der neuen Option '-to ' kann man die Ausgaben der Ablauf- + Verfolgung sowie der Zeitmessungen in eine Datei umleiten; Gibt man + hier z.B. die gleiche Datei an wie unter '-e ' oder '-do ' + dann werden die Ausgaben auch in dieser Datei gemischt (b441) + +* Es gibt nun mit 'UNIT.GRUPPE' die Möglichkeit zu erfahren ob eine + Einheit in einer Gruppe ist und mit 'UNIT.GRUPPE.' kann auf + die Attribute der Gruppe der Einheit zugegriffen werden (b441) + +* Vorlage unterstützt nun die Typprefixe und zeigt diese bei fremden + Einheiten an, sie sind auch über 'UNIT.GRUPPE.TYPPREFIX' bei Gruppen + sowie für Parteien über 'PARTEI[pnr].TYPPREFIX' erreichbar (b441) + +* Weitere, z.T. passendere Fehlermeldungen werden in div. Situationen + ausgegeben (b441) + +* Beim Antreffen von unbekannten Regionstypen erzeugt Vorlage nun + virtuelle Config-Einträge und Map-Zeichen um sie in der Karte anzeigen + zu können (b441) + +* Mit der neuen Option '-do ' kann man die Ausgaben der Befehle + '#debug' und '#table debug' in eine Datei umleiten; Gibt man hier z.B. + die gleiche Datei an wie unter '-e ' dann werden die Ausgaben + auch in dieser Datei gemischt (b441) + +* Es gibt jetzt das neue Attribut 'BUILDING.BONUS' mit dem man zu einem + Bauwerk den bauernbonus bekommt; Es ist dabei nicht gesagt, das der + Bonus in der Region zum Tragen kommt, weil es ja durchaus eine größere + Burg geben kann (b440) + +* Die neue Funktion 'sqrt()' erlaubt jetzt die Berechnung der + Quadratwurzel eines Ausdrucks (b440) + +* Messages die im CR-Block REGION eingebettet waren, bekommen das + synthetische Attribut 'region[]' mit dem man an die Koordinaten + kommt (b440) + +* Vorlage hatte intern aus Performance-Gründen noch einige Schranken + (Literallänge<=512 Zeichen, max. Länge von Bezeichnern 80 Zeichen); + Diese Grenzen wurden nun aufgehoben; Einzig eine maximale Tiefe für + Rekursionen von 64 wurde aufrechterhalten, die nicht technisch + begründet ist, sondern Endlosrekursionen auffangen soll (b440) + +* Das neue Attribut 'REPORT.SPIEL' gibt an, für welches Spiel der + Durchlauf erfolgt (also z.B. 'Eressea') (b440) + +* Vorlage warnt nun, wenn die geladenen CRs wiedersprüchliche Spiel- + Informationen enthalten (b440) + +* Die neue Option '-more' bewirkt, das Vorlage die Ausgaben nach jedem + vollen Bildschirm anhält und erst durch Druck auf die Eingabetaste + fortfährt (b440) + +* Vorlage gibt nun am Ende eine Zusammenfassung über die Zahl der + aufgetretenen Fehler bzw. Warnungen aus (b439) + +* Ein paar Performancesteigernde Maßnahmen ergaben in Tests unter Win32 + eine Verringerung der Laufzeit um etwa 20% (b438) + +* Mif dem neuen Debugger-Kommando 'l' kan man die Ausführung bis zum + Verlassen der aktuellen Funktion/Prozedur ohne Trace erzwingen (b438) + +* Mit dem neuen Debugger-Kommando 'n ' ist es analog zu 's ' jetzt + möglich, eine gewünschte Anzahl von Schritten auszuführen ohne aufrufe + von Funktionen oder Prozeduren zu verfolgen (b438) + +* Bei REGION, UNIT, BUILDING, SHIP und GRENZE gibt es jetzt die neuen + Attribute EFFECTS.SIZE und EFFECTS[] (b438) + +* Obwohl es ihn schon lange gibt, ist der Befehl '#include ' + bisher nicht in der History oder Doku aufgetaucht; Er ermöglicht es, + eine Skriptdatei aus einer anderen zu importieren, so das man nicht + immer alle in der Aufrufzeile angeben muß (b) + +* Die EMR-Funktionen aus 'standard.vms' sind nun in Vorlage eingebaut, + können aber überschrieben werden indem man selber eine erzeugt; in + 'standard.vms' sind sie immernoch, und können als Basis und Beispiel + dienen (b437) + +* Verorkte Regionen werden jetzt als solche angezeigt und das neue + Attribut 'REGION.VERORKT' ermöglicht die Abfrage aus Skripten + heraus (b436) + +* Die neue Option '-sk' bewirkt bei Einheitensortierung das Einheiten + die das Kommando haben immer nach oben kommen, um sie in den + Gebäuden leichter zu finden (empfehlenswert zusammen mit '-sb') + (b435) + +* Man kann jetzt auch über die positionellen Argumente, also über + 'ARG[]' Parameter verändern, wenn sie auch im Kopf der Prozedur + mittels '&' vor dem Parameternamen als veränderlich (also als + Referenzübergabe) gekenzeichnet wurden (b435) + +* Mittels '&...' kann man bei einer Prozedur nun festlegen, das man + alle noch folgenden Parameter auch verändern können will, diese + also ebenfalls per Referenz übergeben werden (b435) + +* Mit dem Debugger-Kommando 'v' kann man das nochmalige Ausgeben des + lokalen Kontextes veranlassen (b435) + +* Genau wie '#var' funktionieren nun auch '#array' und '#dict' sowohl + im globalen, als auch im lokalen Kontext (b435) + +* Der '#sort'-Befehl kann nun auch eingebettete Arrays sortieren (b434) + +* Der die neue Syntax des Sortierbefehls für #array-Behälter lautet nun + '#sort [ [ [...]]]' so das man ihm nun noch + weitere Parameter mitgeben kann, die dann an die Vergleichsfunktion + durchgereicht werden; Es ist zwingend notwendig, das die von der + Vergleichsfunktion erwartete Zahl von zusätzlichen Parametern auch bei + #sort angegeben wird, weil Funktionen keine variable Argumentzahl + erlauben (b434) + +* Im Kopf der Zugvorlage wird nun auch die Liste der Aufrufparameter + ausgegeben um nachträglich leichter Probleme analysieren zu können + (b434) + +* Die neue Funktion 'typeof()' ergibt den Typ des Ausdruckes; Der + zurückgegebene Wert ist ein Integer und hat folgende Werte mit ihrer + Bedeutung: + 0 - TYPE_NULL - auf diesen Typ liefert isnothing() einen Wert + ungleich null, dies ist also ein "Nichts" + 1 - TYPE_ERROR - dies ist das Ergebnis eines Fehlerhaften + Ausdruckes + 2 - TYPE_INT - eine Ganzzahl, d.h. ohne Nachkommastellen + 3 - TYPE_FLOAT - eine Fließkommazahl + 4 - TYPE_STRING - ein String bzw. Textausdruck + 6 - TYPE_ARRAY - ein Feld, also #array + 7 - TYPE_DICT - ein Assoziativer Behälter #dict + Es ist _dringend_ empfohlen, _nicht_ die Zahlen zu verwenden, da + sie sich in zukünftigen Versionen ändern können; Im der neuen + 'standard.vms' sind daher mittels des '#const'-Befehls die oben + angegebenen Konstanten definiert die man am besten einbindet und + die angepasst werden, wenn sich die Werte ändern (b433) + +* Der neue Befehl '#const ' ermöglicht es jetzt globale + Konstanten zu definieren; Für den Namen gelten die bisherigen + Regeln für Bezeichner, aber es darf kein '$' zu Beginn stehen (b433) + +* Der Zugriff auf ein Array mittels '$arrayname()' ergibt jetzt + analog zu Dictionaries den Index und nicht den Inhalt (b433) + +* Die für das Rendern der von Vorlage erzeugten Pseudo-Nachrichten aus + BATTLE-Blöcken benötigte Funktion 'EMR_region' ist nun in Vorlage + eingebaut, kann aber jederzeit durch ein Skript überschrieben werden + (b432) + +* Mit der neuen Funktion 'substr(,,)' kann man nun Teile + eines Textausdruckes nach Position und Länge extrahieren; Wird die + Position negativ angegeben, so wird vom Ende gezählt, wird die + Länge negativ angegeben, so wird die um Betrag verminderte Länge + des Textes verwendet, liegen die Werte außerhalb gültiger Bereiche so + werden sie angepasst und notfalls ein Leerstring zurückgegeben (b432) + +* Auf mit #config erstellten Objekten funktioniert jetzt auch der + Ausdruck '.size', ein Zugriff über eine Position, wie bei + Dicts, funktioniert aber nicht und wird auch in 1.5 nicht mehr kommen, + das Feature macht also im Moment nur für #config-Objekte Sinn die als + primären Schlüssel eine fortlaufende Nummer haben (b432) + +* Mit der Option '-cl ' kann man erreichen, das Vorlage eine + Warnung ausgibt, wenn ein Behälter (also #dict oder #array) mehr als + Einträge enthält (b431) + +* Unter Windows kann mit CTRL+BREAK in den Debugger gesprungen werden + (b430) + +* Die neuen Funktion 'float()' und 'int()' ermöglichen eine + Wandlung in Fließkomma bzw. Integer (im Geensatz zu floor/ceil ohne + Rundung) (b430) + +* Die neue Funktion 'time()' erlaubt den Zugriff auf die Zeitmessung, + so das man selber für Teilbereiche die Zeit messen kann; Die Funktion + gibt das Ergebnis in Millisekunden zurück (b430) + +* Das Objekt 'SHIP' hat nun die beiden Attribute 'SHIP.INSASSEN' und + 'SHIP.BESCHR' wie die Gebäude ja schon seit langem (b429) + +* Vorlage zieht nun von der verbrauchten Zeit in der Anzeige die Zeit + die auf Eingaben gewartet wurde ab und gibt Nettozeiten an; Es wird + aber die Gesammtwartezeit am Ende bei der Gesammtzeit ausgegeben + (b428) + +* Der Befehl '#message' kann nun auch ohne Argument verwendet werden um + eine Leerzeile ohne Semikolon zu erzeugen (b428) + +* Vorlage erzeugt für jeden BATTLE-Block im Report eine zusätzliche + Pseudo-MESSAGE mit dem Typ -1 und in 'rendered' den künstlichen Text + "In fand ein Kampf statt." sowie einem 'region' + Eintrag mit den Koordinaten der Region um die Ausgabe der Kämpfe mit + Skriptbefehlen in OnInit zu vereinfachen; Die Ausgabe könnte in OnInit + mit folgendem Code erfolgen: + + $i=0 : $battle=0 + #message + #while $i)' u. 'tolower()' + mit denen Texte in klein bzw. in Großbuchstaben umgewandelt werden + können (b428) + +* Es ist jetzt auch '#proc ...' erlaubt, also eine #proc + ohne einen einzigen benannten Parameter (b427) + +* Anpassungen an CRV58 und damit die neuen Attribute 'UNIT.VERRAETER' + sowie 'UNIT.VERKLEIDUNG' (b427) + +* Die geänderten Kampfstati der Einheiten in Eressea CRV57 werden nun + berücksichtigt (b425) + +* Mit dem optionalen Parameter 'progress' kann '#debug' nun auch für + die Option '-pi' Ausgaben erzeugen; Ist die Option inaktiv werden die + Ausgaben unterdrückt; Die neue Syntax ist also + + #debug [progress] + + (b425) + +* Mit der neuen Option '-et' wird erreicht, das Vorlage bei einem Error + in den Debugger springt (b425) + +* Mit der neuen Option '-pw' kann man Vorlage ein Passwort für die + Zugvorlage übergeben, welches, sofern keines im CR vorhanden ist, + verwendet; Diese Option hat Vorrang vor dem Passwort im Config-File + (b425) + +* Wird im Config-File unter dem Kapitel '[Options]' ein Eintrag der Art + + Passwort = "" + + erzeugt, so wird dieses zur Zugvorlagenerstellung verwendet (b425) + +* Die neue interne Variable $EXECINLINE bestimmt, ob in einem Durchgang + die in Einheiten eingebetteten Befehle ausgeführt werden oder nicht; + Sie wird zu Beginn mit 1 initialisiert und nach einem Durchlauf auf + 0 gesetzt, so daß weitere Durchläufe dann ohne die Auswertung der + Befehle im Report stattfindet; Durch Setzten oder Löschen der Variable + in OnInit kann bei Multi-Pass-Läufen bestimmt werden ob und wann die + Auswertung erfolgen soll (b424) + +* Der neue Befehl '#sort []' ermöglicht es Arrays zu + sortieren; Wird der Name einer Vergleichtsfunktion + angegeben, so wird diese verwendet, andernfalls der Kleiner-Operator; + Die Aufgerufene Funktion, falls angegeben, sollte die Signatur wie + z.B. + + #func LessThan $arrayname $i1 $i2 + + haben und bekommt in $arrayname den Namen des Arrays (für Inplace- + Zugriff) und in $i1 und $i2 jeweils die Indizes der beiden Einträge; + Die Funktion solle einen Wert ungleich 0 liefern, falls für die beiden + Indizes $($arrayname)[$i1]<$($arrayname)[$i2] gilt, sonst 0 + (b422) + +* Es ist nun möglich Behälter in Behälter zu legen, genau genommen kann + man jeder Variable auch einen Behälter zuweisen; Dabei ist zu beachten + das dies immer Kopieren des Behälters zur Folge hat, also nur mit + Vorsicht (Performace) benutzt werden sollte; Dies ermöglicht z.B. zu + seinen Einheiten temporäre Arbeitswerte abzulegen wie in diesem + Beispiel: + + ; Ein Dict für die Infos und eines als Kopiervorlage erzeugen + #dict $unit $dummy + + #proc OnInit + { + ; Regionsindex + $ri=0 + + ; Solange noch Regionen existieren + #while $ri]' zugegriffen werden; Neue + Elemente hängt man an ein Array an, indem man sie $a[$a.size] zuweist; + Löscht man, analog zu Dictionaries ein Element mittels $a[]= + so werden alle nachfolgenden Elemente nach unten verschoben; Das + automatische Einfügen an beliebiger Stelle (mit Verschieben der + Elemente nach hinten) ist (noch) nicht vorgesehen; Man kann zudem + Größe (Anzahl der Elemente) durch '$ArrayName.SIZE' ermitteln (b422) + +* Mit den neuen Attributen 'REGION.DURCHREISE.SIZE' und + 'REGION.DURCHREISE[]' kann man auf die Durchreise-Infos einer + Region zugreifen (b421) + +* Die neue Option '-rc' verhindert, das durch die Auswertung von + Metabefehlen die bereits im CR vorhandenen Befehle gelöscht werden; + Hierdurch können natürlich u.U. mehrfache lange Befehle auftreten + (b421) + +* Die neue Option '-mi []' sorgt daführ, das die Auswertung der + Metabefehle in mehreren (min. ) Durchgängen (Passes) erfolgt; + Alle weiteren Durchgänge wirken additiv, d.h. die bisher erzeugten + Befehle (aber schon wie gewohnt die aus dem CR) werden nicht + überschrieben; Die Nummer des aktuellen Durchlaufs wird beginnend mit + 1(!) in der globalen Variable $PASSNUM abgelegt; Erhöht man diese in + OnExit von Hand um 1, so wird ein weiterer Auswertungslauf erzwungen + auch wenn die in angegebene Anzahl schon erreicht oder + überschritten ist (falls die Routinen gemerkt haben das weitere Läufe + nötig sind); $MINPASSES enthält den mit '-mi' übergebenen Wert zur + Info der Skripte (b421) + +* Das Attribut UNIT.POSITION liefert nun auch für auf Schiffen + befindliche Einheiten die Position (beginnend mit 1) innerhalb des + Reports; ACHTUNG: es kann also nicht mit #if unit.position gefragt + werden, ob eine Einheit in einem Bauwerk ist, sondern hierfür ist + #if unit.bauwerk zu verwenden (b420) + +* Der neue Befehl '#input [ []]' ermöglicht es + nun auch während der Abarbeitung Eingaben zu erfragen; Wird die + Variable weggelassen, so wird das Ergebnis der Eingabe verworfen, + fehlt auch die Aufforderung, so wird "[Weiter mit der Eingabetaste]" + ausgegeben und darauf gewartet; Dies funktioniert natürlich nur in + Umgebungen die Interaktivität zulassen, also i.d.R. nicht aus GUI- + Clients heraus (b420) + +* Inplace-Auswertung von Variablen in Objektzugriffen, mittels $() + ermöglichen z.B. Referenzen auf Objekte: + $RegRef='REGION[1,-2]' + #message 'Hier leben '+$($RegRef).Bauern+' Bauern' + (b420) + +* Vorlage unterstützt nun auch im CR Escaped Characters, d.h. z.B. + die mit \ "entschärften" Anführungszeichen in den neuen Regeln für + das Rendern der Nachrichten (b419) + +* Mit den neuen Attributen UNIT.EINHEITSBOTSCHAFTEN.SIZE und + UNIT.EINHEITSBOTSCHAFTEN[] kann man nun auf die Botschaften + an eine Einheit zugreifen (b419) + +* Mit UNIT.COMMANDS.SIZE und UNIT.COMMANDS[] kann man nun auf + die Original-Befehle aus dem CR zugreifen; Das Ergebnis bereits + ausgeführter Metabefehle ist davon unbetroffen und der Zugriff + erfolgt nur lesend (b419) + +* Vorlage kommt nun auch mit CR V57 MESSAGE-Blöcken klar, in denen die + Regions-Koordinaten in einem Drei-Integer-Feld angegeben sind; + (b419) + +* Erkennung der neuen Blöcke MESSAGETYPE und TRANSLATION und unterstützt + auch mit der bisherigen Option '-fr' das Rendern der neuen Messages, + wobei es aber, aufgrund von möglichen Änderungen oder Erweiterungen + im CR nicht zwangsläufig die Entgültige Version darstellt; Die Daten + des Translation-Blockes werden aber noch nicht verwendet; eine echte + Internationalisierung von Vorlage scheint im Moment unwahrscheinlich + (b419) + +* Mit der neuen Option '-pi' kann man die Abarbeitung der Skripte etwas + detailierter verfolgen um festzustellen in welchem Teil z.B. eine + Endlosschleife auftritt (b418) + +* Bugfix: Einige Nachrichten wurden fälschlicherweise der Region 0,0 + zugeordnet (b444) + +* Bugfix: Bei der Ausgabenberechnung über den gesamten Report wurde + immer mit 10 Silber pro Person gerechnet, ungeachtet des Eintrages im + Konfigurationsfile (b444) + +* Bugfix: Im Rassenhandling wurde nicht immer auf die Daten aus dem + Konfigurationsfile zugegriffen (b444) + +* Bugfix: Aufgrund vom ISO-C++-Standard abweichendem Verhalten der STL + des verwendeten G++-Compilers stürzte Build 443 unter Linux ab (b444) + +* Bugfix: Das Tag 'typprefix' in EINHEIT-Blöcken wurde nicht erkannt + (b444) + +* Bugfix: Wenn es kein '[Resources]'-Kapitel in der Konfiguration gab, + stürzte Vorlage ab (b444) + +* Bugfix: In der, mit '-us' einsortierten Anzeige, fremder Einheiten + wurden evtl. welche die man selber nicht, ein anderer, dessen Report + man aber mit angegeben hat, aber schon sieht, nicht angezeigt (b443) + +* Bugfix: In der Anzeige fremder Einheiten ohne Einsortierung, also '-u' + oder '-uv', aber nicht '-us', konnten auch welche aus anderen Runden + auftauchen die in der aktuellen Runde garnicht vorhanden sind (b443) + +* Bugfix: Bei verquirrlten CRs aus anderen Clients konnte z.B. die + Meldung nach unbekannten GRENZE-Blöcken auftreten (b433) + +* Bugfix: Im Skript-Debugger wurden Ausdrücke mit Hochkommas meistens + nicht korrekt behandelt und gaben dann auch nicht das Verhalten des + Interpreters wieder (b443) + +* Bugfix: Eine Zuweisung an eine Behälter-Position mit ='' bewirkte eine + Löschung des Elementes, statt eine Leerstring-Zuweisung (b443) + +* Bugfix: Bei Überarbeitung des Speicherhandlings wurde ein Memoryleak + gefixed (b443) + +* Bugfix: Unter bestimmten Konstellationen im CR konnte Vorlage sich + interne Strukturen zerschießen, was zu unvorhergesehenen Ausnahme- + behandlungen führt (b443) + +* Bugfix: Für manche Fehler wurde zusätzlich noch ein ": als Befehls..." + gemeldet (b442) + +* Bugfix: Bei einem Zugrif auf REGION.PREISE[0] bei Regionen ohne den + PREISE-Block kam es zu einer unerwarteten Ausnahme (b442) + +* Bugfix: Nach einer '\ddd'-Sequenz in Strings wurde ein Zeichen + verschluckt (b442) + +* Bugfix: Fehlermeldungen und Warnings die beim Reportlesen auftraten + wurden nicht auf der Fehlerumleitung ausgegeben (b441) + +* Bugfix: Ein Fehler in der CR-Leseroutine konnte dazu führen, das + MESSAGETYPE und TRANSLATION nicht erkannt wurden (b441) + +* Bugfix: Die Anzeige der Punktedurchschnitsänderung war undefiniert, + wenn in der Vorrunde noch keine Punkte im Report waren (b440) + +* Bugfix: Wenn #config kein passendes Kapitel fand, gab es keine Info + bezüglich der Stelle im Skript an der das passierte (b440) + +* Bugfix: Bei einigen Zugriffen auf Subobjekte wurde irrtümlich von + einem Zugriff auf ein nichtexistierendes Objekt ausgegangen (b440) + +* Bugfix: Fehler in Teilausdrücken konnten u.U. verloren gehen (b440) + +* Bugfix: Der Debugger-Befehl 'n' hatte einen Fehler der ihm quasi die + 's'-Semantik gab (b440) + +* Bugfix: Die lokale Anwendung von '#var', '#array' und '#dict' hatte + für den Fall das mehr als ein Bezeichner gegeben war noch immer einen + Bug, der zu einer Fehlermeldung führte (b440) + +* Bugfix: Die lokale Anwendung von '#var', '#array' und '#dict' bewirkte + ein paar Fehlermeldungen auch wenn die entsprechenden Variablen und + Behälter erzeugt wurden (b439) + +* Bugfix: Beim Zugriff auf komplett nicht existierende Objekte, z.B. + 'grunzwanzling['blup'].anzahl' gab es i.d.R. keinen Fehler, sondern + eine null; Dies ist nun nur noch so, wenn es zwar 'grunzwanzling' + gibt, aber keinen Eintrag unter dem Schlüssel 'blup' existiert, gibt + es hingegen 'grunzwanzling' nicht, kommt nun eine Fehlermeldung + (b439) + +* Bugfix: ARG[] war in Bezug auf Groß-/Kleinschreibung nicht durchgängig + konsistent behandelt worden, es muß immer groß geschrieben werden, es + gibt nun aber eine Fehlermeldung, wenn man dies vergisst + (b439) + +* Bugfix: Bei einigen Attributbehältern konnte 'size' mit einem Attribut + in Konflikt geraten, weil manchmal nicht gefrüft wurde, ob kein Index + angebenen ist (b439) + +* Bugfix: Das löschen von Array-Elementen mittels '$Array[index]=' und + einem ungültigen Index führte zu einer unerwarteten Ausnahmebehandlung + (b438) + +* Bugfix: '&...' bewirkte keine Veränderungsmöglichkeit (b438) + +* Bugfix: In 'standard.vms' war ein Fehler im Namen von 'EMR_region' + (b437) + +* Bugfix: Inplace-Replacement wirkte auch in Strings (b437) + +* Bugfix: Zugriffe der Art 'object[].$var' oder 'object.$var' sind + i.d.R: mit einem Fehler belegt worden (b437) + +* Bugfix: Bei Auswertung von Ausdrücken im Debugger mittels 'e' wurde + das Tracing nicht deaktiviert, so das man sofort in verwendeten + Funktionen wieder im Debugger landete (b436) + +* Bugfix: Der Parser hat durch eine "Optimierung" Variable zweimal + aufgelöst, was in manchen Inplace-Fällen zu Problemen führen konnte + (b436) + +* Bugfix: Die Funktion 'random()' hatte besonders zu Beginn kein gutes + Enthropieverhalten, Vorlage verwendet nun einen eigenen Generator + der statt des vorher verwendeten libc-rand() bei gutem Zeitverhalten + eine sehr viel längere Periode (Faktor >10^3k) und bessere Streuung + hat; die Seed-Wahl wurde ebenfalls _deutlich_ verbessert (b435) + +* Bugfix: Einige Memory-Leaks gefixed (b435) + +* Bugfix: Der Variable als Behälter oder Objekte anzusprechen hat keinen + Fehler zur Folge gehabt (b435) + +* Bugfix: Beim Anlegen von Variablen oder Behältern mit ungültigem Namen + wurde kein Fehler gemeldet (b435) + +* Bugfix: Beim mehrfachen Anlegen von Variablen oder Behältern im + gleichen Kontext (i.d.R. Global), gab es keine Fehlermeldung (b435) + +* Bugfix: Bei Zuweisungen an Ziele denen man nichts zuweisen kann, wie + z.B. Funktionsaufrufen, hat Vorlage manchmal keinen Fehler gemeldet + (b433) + +* Bugfix: Die Inplaceauswertung hatte diverse Situationen in denen sie + zu Fehlermeldungen führte, speziell im Zusammenhang mit geschachtelten + Behältern; Durch eine Anpassung des Parsers kann sie nun außer in + Hochkommas und über Ausdruckgrenzen hinweg überall eingesetzt werden; + Letzteres bedeutet, das man nicht durch Inplace aus einem Parameter + optional zwei machen kann; Daraus resultierte leider eine spührbare + Performanceeinbuße des Metalanguage-Interpreters die wohl erst in + nach der 1.5-Phase vermieden werden kann (b432) + +* Bugfix: Die Zuweisung eines Behälters an einen Behälter klappte nicht + (b432) + +* Bugfix: Zugriff auf Array-Positionen jenseits der Größe ergaben eine + unerwartete Ausnahmebehandlung (b432) + +* Bugfix: match(), before(), after(), crop() und change erzeugten bei + einem leeren regulären Ausdruck eine Ausnahmebedingung (b431) + +* Bugfix: #sort funktioniert nur mit direkten #array-Variablen, nicht + aber eingebetten, hat dies aber nicht gemeldet sondern nichts gemacht; + Die Funktionalität ist leider nicht in 1.5 erweiterbar (b431) + +* Bugfix: Verräter die sich als eigene Einheiten ausgeben wurden in + einem EINHEIT-Block in der Zugvorlage gepackt, als ob man ihnen + Befehle geben könne (b431) + +* Bugfix: Die Option '-rc' war implementiert aber nicht aktivierbar + (b431) + +* Bugfix: Ausnahmebedingungen konnten zu unbemerkten Abbrüchen von + Funktionen und Prozeduren führen; Es werden nun Fehlermeldungen + für diese Fälle generiert um sie finden und eleminieren zu helfen + (b431) + +* Bugfix: Die Semantik der Operatoren '&&', '||'und '!', sowie des '#if' + war in Bezug auf Strings vorsichtig formuliert unschön: Die Operatoren + taten überhaupt nichts sinnvolles und das '#if' hat versucht Strings + als Zahl zu interpretieren; Dies verhalten wurde dahingehend geändert, + das String mit Inhalt, also nicht Länge Null, als logisch Wahr + angesehen werden, Leerstrings als logisch Falsch; Um einfach auf die + Anwesenheit einer Einheit auf einem Schiff oder in einem Gebäude + prüfen zu können geben 'UNIT.BAUWERK' und 'UNIT.SCHIFF' nun im + Null-Fall eine Integer-Null zurück und nicht wie vorher einen String + '0'; analoges gilt für 'UNIT.TEMP' und 'UNIT.ALIAS' (b430) + +* Bugfix: Bei Befehlen die durch Skripte erzeugt wurden durch Umbrüche + die Folgezeilen fehlerhafterweise mit Semikolon eingeleitet. + +* Bugfix: Ein Seiteneffekt im Behälterhandling führt u.U. zu falschen + Sortierergebnissen bei #sort (b429) + +* Bugfix: "Einheit A (foo) lehrt Einheit B (bar)"-Messages wurden von + Vorlage nicht in die Zugvorlage übernommen (b428) + +* Bugfix: In Objekten die mit #config erzeugt wurden konnte man nicht + wie bei eingebauten Objekten mit einem Zugriff über den Key + feststellen ob es den Eintrag gibt (b428) + +* Bugfix: Weitere Fixes an #sort für den Fall das mit einer geskripteten + Vergleichsfunktion gearbeitet wird (b428) + +* Bugfix: Verbessertes Fehlerhandling statt der früher immer paarweise + auftretenden "':' als Befehlstrenner erwartet!" und "'}' am Ende eines + Komandoblocks erwartet!" (b427) + +* Bugfix: Einige Fehlermeldungen hatten keine File/Zeilen-Angaben (b427) + +* Bugfix: 'SHIP[]' und 'BUILDING[]' haben + ausserhalb der Region nicht korrekt funktioniert (b427) + +* Bugfix: Leerzeichen hinter einem '...'-Parameter in einer #proc haben + verhindert, das die Funktion als eine mit variabler Argumentzahl + erkannt wurde (b427) + +* Bugfix: Inplace-Zugriff funktionierte bei Behältern nicht (b427) + +* Bugfix: #sort rief die Compare-Funktion mit illegalen Positionen auf + (b427) + +* Bugfix: Behälter konnten als Schlüssel für Dicts genutzt werden (b427) + +* Bugfix: Passwörter aus der Config-Datei wurden abgeschnitten (b427) + +* Bugfix: Beim Kampfzauber wird nun der korrekte Level angezeigt (b427) + +* Bugfix: Index-Zugriff auf die Werte von Multi-Integer-Attributen hat + nicht funktioniert (b427) + +* Bugfix: '#input [ []]' meldete einen Fehler + wenn es die Variable nicht gab, unabhängig von der Benutzung von -fd + (b427) + +* Bugfix: Die Verwendung von Behälterzugriffen als Index, also z.B. + $array1[$array2[3]], klappte nicht korrekt, die Verwendung von + () bei geschachtelten Behältern dadurch auch nicht (b427) + +* Bugfix: Verbessertes Fehlerhandling bei Zuweisungen (b426) + +* Bugfix: Bei geschachtelten Behältern erfolgte bei falscher Indizierung + eine falsche Fehlermeldung (b425) + +* Bugfix: Bei geschachtelten Behältern funktionierte das SIZE-Attribut + nicht (b425) + +* Bugfix: Beim Zugriff auf Behälter konnte unter bestimmten Umständen + ein Abflug erfolgen (b425) + +* Bugfix: Pfadbehandlung für Konfigurationsdateien war Fehlerhaft (b424) + +* Bugfix: 'SHIP[].' funktionierte nicht ohne einen + Regionskontext (b423) + +* Bugfix: Wenn in einem Funktionsaufruf ein Parameter mit einem + Delimiter begann, so führte dies zu einem Fehler (b423) + +* Bugfix: Ein Fehler im Parser verhinderte das man ',' oder ')' als + Parameter für Funktionen verwenden konnte (b422) + +* Bugfix: Die Behandlung von Astralregionen war fehlerhaft und sie + tauchten i.d.R. nicht in der Zugvorlage auf (b421) + +* Bugfix: Div. Fehlersituationen wurden mit korrekteren Fehlermeldungen + versehen (und viele müssten noch :-/) (b421) + +* Bugfix: Vorlage ist nun resistent gegen die Verwendung von @ (b418) + +* Bugfix: Beim erreichen der 100er-Grenze in einer Region kam Vorlage + mit Option '-kl' zum Abflug/Halt, weil im Referenzreport keine Infos + über Luxusgüter sind (b418) + + +---[Vorlage V1.5 beta 5]--- + +* Mit 'vorlage -e ' kann man die eingebaute Optionserklärung + von Vorlage auch in eine Datei ausgeben lassen (b417) + +* Anpassungen der Messageauswertung an die neuen Message-IDs ab + Eressea-Runde 227 (b416) + +* Die neue Option '-mr ' Sorgt dafür das in allen folgenden + Regionen und zu den Koordinaten addiert wird; Dies ist + die einzige Option die auch zwischen den CRs übergeben werden darf + (b416) + +* Die Kampfzauber werden ab CR V53 nun bei den Magiern angezeigt (b416) + +* Das Objekt BUILDING hat nun das fehlende Attribut 'BESCHR' für den + Zugriff auf die Beschreibungen von Gebäuden (b416) + +* Anpassung an den CR V54 ohne Passwort (b415) + +* Bugfix: Der Zugriff auf eigene CR-Erweiterungen in REGION-Blöcken + funktionierte nicht (dieses Bugfix hat die Sortierreienfolge der + Regionen geändert) (b416) + +* Bugfix: Wenn Vorlage mit einem Parteilosen Report mit größerer + Rundennummer und einem älteren Parteibehafteten aufgerufen wird, + so stürzte es ab (b416) + +* Bugfix: Im rausgeschriebenen Karten-CR bei Option '-map' wurde + 'Insel' fälschlicherweise zu 'Island' (b415) + + +---[Vorlage V1.5 beta 4]--- + +* Anpassung an CR V53, der Block 'KAMPFZAUBER' wird eingelesen und + unter 'UNIT.KAMPFZAUBER.SIZE' die Anzahl der Kampfzauber-Blocke + bzw. unter 'UNIT.KAMPFZAUBER[].' die Atribute zur + Verfügung gestellt; Der Block-Key steht dabei in dem Attribut + 'UNIT.KAMPFZAUBER[].KEY' zur Verfügung (b414) + +* In der Warentauschliste (nur Option '-hb') stehen nun vor den + Parteinummern auch die Parteinamen (b414) + +* Bugfix: Die Pseudo-Tags 'herb' und 'Insel' fanden aus Info-CRs (z.B. + Karten) nicht immer den Weg in das REGION-Objekt (b414) + +* Bugfix: 'REGION[...].GRENZE', 'REGION[...].BUILDING' sowie + 'REGION[...].SHIP' funktionierten in der Regel nur mit der aktuellen + Region (b413) + +* Bugfix: 'change(txt,regx,rep)' hat nicht, wie angekündigt, alle sondern + nur das erste Auftreten des Patterns ersetzt (b413) + +* Bugfix: '#config ... FILE' hat die Pfadangaben ignoriert (b413) + +* Bugfix: Prozeduren konnten als Funktionen aufgerufen werden und + umgekehrt (b412) + +* Bugfix: '#config TABH' unterstützte keine String-Attribute (NESTED + kann keine unterstützen, das ist Syntaxbedingt) (b412) + +* Bugfix: Ohne die Option '-d' führten #debug-Ausgaben zu Fehlern + (b412) + + +---[Vorlage V1.5 beta 3]--- + +* Mittels 'PARTEI[].' kann man nun auf die + Attribute einer Partei zugreifen; Atribute sind in diesem Fall alle + Tags die in den CR-Blöcken PARTEI oder ALLIANZ (bzw. ALLIANZEN oder + ADRESSEN in Empiria) vorkommen (b411) + + +* Mit dem neuen Befehl '#debug ' kann man analog zu #message eine + Ausgabe auf dem Error-Kanal ausgeben (b410) + +* Mit der neuen #table-Option 'DEBUG' (statt 'DUMP') kann man eine + Tabelle auf dem Error-Kanal ausgeben (b410) + +* Die neue Option '-d' schaltet die obengenannten Ausgaben ein, sonst + werden sie unterdrückt (b410) + +* Bugfix: '#config buildings NESTED' funktionierte nicht korrekt, es + wurden die Subobjekte 'baukosten' und 'unterhalt' nicht angelegt + (b411) + +* Bugfix: Im Handling von 'REGION[].GRENZE[].' trat eine Exception + auf, die dazu führte, das kein Wert erzeugt wurde (b411) + +* Bugfix: Im Parser traten Memoryleaks auf, die zum Teil zu erheblichem + Speicheranstieg führen konnten (b410) + + +---[Vorlage V1.5 beta 2]--- + +* Der #config-Befehl hat nun eine erweiterte Syntax die es erlaubt + eine abweichende Datei als Quelle anzugeben; Die neue Syntax lautet: + '#config [FILE ] []' (b409) + +* Bei aktiver Differenzanzeige '-td' und aktiver Parteiübersicht '-up' + werden nun für die Personenzahl und den MAterialpool der Parteien + auch Änderungen angezeigt (b409) + +* Unter Windows akzeptiert Vorlage auch Umlaute in DOS-Schreibweise, + was vor allem im Debugger auf der Konsole hilfreich ist (b408) + +* Die neue Methode 'flatten()' gibt die Stringrepresentation des + Wertes ohne Leerzeichen, Umlaute und Großbuchstaben zurück, d.h. + Leerzeichen werden entfernt, Umlaute und Großbuchstaben konvertiert + (b408) + +* Werden in REGION-Blöcken des CRs ';herb'-Tags gefunden, so werden + diese als bei eingeschalteten Regionsinfos als Kraut angezeigt; + Dies ist, ähnlich wie das ';Insel'-Tag kein Standard-Tag, sondern + eine Erweiterung die auch von einigen Clients unterstützt wird; Es + bietet sich an einen extra Karten-CR zu pflegen, in dem Insel- und + herb-Tag enthalten sind und diesen Vorlage mit zu übergeben + (b407) + +* Die Syntax von NESTED-Config-Objekten wurde erweitert, um auch + Attribute im Hauptobjekt haben zu können; diese werden vor dem + ersten Unterobjekt, also nach dem Key, dem ersten Wert jeder + Zeile, in der gleichen Weise wie die Attribute der Subobjekte + aufgeführt; Es ergibt sich folgende Syntax: + Schlüssel| numer. Attribute | 1. Subobjekt mit Attributen | etc. + ---------+--------------------+--------------------------------+----- + "", [, "Name", ...] ["SubObj", , "Name", [...]] [...] + (b406) + +* Aus der Änderung der NESTED-Objekte ergeben sich neue Attribute + für BUILDINGS gibt; 'BUILDINGS.TALENT' gibt das nötige Bautalent an, + 'BUILDINGS.BONUS' den Bauernbonus; dies bis jetzt nur für Eressea, + weil mir die Informationen fehlen (b406) + +* Der Eintrag in eressea.cfg BUILDINGS['Universität'] lautet nun + BUILDINGS['Akademie'] (b406) + +* Warnungen über unbekannte Feldkennungen im CR können mit der neuen + Optioen '-ws' unterdrückt werden, sie können für die meisten Blöcke + auch wenn Vorlage sie nicht kennt über die Attribute abgefragt + werden (b405) + +* Es gibt nun endlich Zugriff auf die Luxusgüter mittels den neuen + Attributen 'REGION.PREISE.' bzw. 'REGION.PREISE.SIZE', + 'REGION.PREISE[].SILBER' und 'REGION.PREISE[].NAME' + (b405) + +* Bugfix: Bei der Verwendung von 'REGION[x,y]' bzw. 'REGION[x,y,z]' + konnte nicht auf Regionen die nicht im Basis-CR existierten + zugegriffen werden (b409) + +* Bugfix: Die Patternregeln für Option '-o' kollidierten mit der + Möglichkeit Optionsfiles mittels '@filename' anzugeben (b409) + +* Bugfix: Verdanon-Reports konnten nicht mehr ohne Änderungen gelesen + werden (b408) + +* Bugfix: Die Handelsbilanzen unter Verdanon (Option '-hb') hatten + keine Funktion (b408) + +* Bugfix: Die Frei-Kapazitätsberechnung unter Verdanon haben die + Personengewichte nicht berücksichtigt, weil der Typ im CR 'Personen' + heist, im CFG-File aber 'Menschen' stand; das CFG wurde angepasst + (b408) + +* Bugfix: Die Anzeige von Warentausch (ebenfalls Option '-hb') + funktionierte schon sein der Eressea-Einführung der neuen MESSAGE- + Blöcke nicht mehr, und wurden unter Verdanon und Eressea reaktiviert + (b408) + +* Bugfix: Bei Referenzparametern mit Strings wurden die Strings ohne + Hochkommas zurückgegeben und konnten so evtl. beim nächsten Aufruf + zu Problemen führen (b408) + +* Bugfix: Bei den Statusmeldungen zum Reportlesen wurden die + Parteinummern immer dezimal ausgegeben (b407) + +* Bugfix: Die erste Trace-Ausgabe-Zeile klebte noch an der vorherigen + Statusmeldung (b407) + +* Bugfix: Im Handling von Variablen Parametern führte ein Fehler + zur Löschung des vorletzten Parameters und zum Nichtanlegen des + letzten Parameters (b407) + +* Bugfix: Manchmal wurden Fehlermeldungen am Errorhandling vorbei ohne + Angabe des Ortes ausgegeben (z.B. "Operation mit typlosen Operanden") + (b406) + +* Bugfix: Das Löschen von Elementen in einem #dict führte zu einem + Abbruch der Prozedur/Funktion ohne Löschung (b405) + +* Bugfix: Bei variabler Parameterzahl stürzte Vorlage ab, wenn ein + weggelassener Parameter ein Referenzparameter ('&') war (b405) + +* Bugfix: Beim Zugrif auf das n-te Element eines #dict mittels runder + Klammern wurde die Auswertung des Restausdruckes abgebrochen (b405) + + +---[Vorlage V1.5 beta 1]--- + +* Es gibt ab jetzt wieder eine Linux-Version (b404) + +* Mit der neuen Funktion 'length()' kann man die Länge der Zeichen- + Representation eines Wertes ermitteln (b404) + +* Im Dateinamen Für die Option '-o' können nun generisch Daten des + Reports eingefügt werden, indem man @p für die dezimale Parteinummer, + @P für die Parteinummer im derzeit gültigen Format, @r für die Runde, + @j für das Jahr, @m für den Monat (dezimal) und @w für die Woche + einfügt; so ergibt z.B. '-o @p2@j@m@w.er' einen Namen wie ich ihn + benutze (4920221.er für Partei 49 (1d), zweites Zeitalter, zweites + Jahr, zweiter Monat und erste Woche); Bei alter Zeitrechnung ist der + Monat, wie das Jahr auch, mit evtl. führender Null zweistellig (b404) + +* Das Objekt UNIT hat die "neuen" Attribute 'UNIT.TEMP' und + 'UNIT.ALIAS' für die Temp-Nummer der letzten Runde, bzw. der Nummer + vor NUMMER EINHEIT (b404) + +* Burg als Objekt in den [Buildings]-Block in Config-File eingetragen + (b402) + +* Mittels 'REPORT.MESSAGE.SIZE' und 'REPORT.MESSAGE[].' + kann jetzt auf die Nachrichten des Bezugsreports zugegriffen werden; + Bei CR-Versionen die keine MESSAGE-Blöcke enthalten, ist das einzige + Attribut 'REPORT.MESSAGE[].RENDERED' mit dem auf die alten + Text-only-Nachrichten zugegriffen werden kann (b401) + +* Der Konformität wegen gibt es nun die Attribute 'REGION.GRENZE.SIZE', + 'REGION.UNIT.SIZE', 'REGION.BUILDING.SIZE' und 'REGION.SHIP.SIZE' + (b401) + +* ACHTUNG: Der Zugriff auf ein #dict mittels '$DictName()' ergab + den Wert an der Position (beginnend mit 0); Dieses Verhalten + wurde _geändert_! Es wird nun der Key des Wertes an dieser Position + zurückgegeben, den Wert selber kann man dann über diesen Key bekommen + (b400) + +* Die neue Option '-map ' erzeugt eine Karte mit dem gesammelten + geographischen Wissen der CRs die angegeben wurden; Es werden von den + Regionen nur Terrain, Name sowie evtl. Insel ausgegeben (b400) + +* Mit dem Befehl '#config []' kann man jetzt + Tabellen aus dem Config-File in globale Objekte laden; Dabei gibt + es drei Modes die Tabellenformaten im Config-File entsprechen: + TABH: Horizontale Objekt-Anordnung, d.h. alle Attribute in einer + Zeile (z.B. 'Castles' oder 'Things' im Config-File) + Hier müssen die Attributnamen, ohne die erste Spalte, angegeben + werden, die erste Spalte ist Index und kann auch als Attribut + NAME erfragt werden; Beispiel: + #config Castles tabh groesse bonus + erzeugt mit eressea.cfg ein Object bei dem der Aufruf + CASTLES['Festung'].GROESSE + 250 ergibt + TABV: Vertikale Objekt-Anordnung, d.h. alle Attribute in einer Spalte + (z.B. 'Races' im Config-File) + Hier sind die Attributnamen in der ersten Spalte, die erste + Zeile ist Index und kann als Attribut NAME erfragt werden; + Beispiel: + #config Races tabv + erzeugt ein Object RACES, welches sich genau so verhält wie + das eingebaute, welches mittelfristig entfernt wird + NESTED: Verschachteltes Object, mit einer Ebene Unterobjekten, d.h. + das Objekt ist Komplett in einer Zeile angegeben, aber mit + einem speziellen Aufbau: Erster Eintrag ist der Index, der auch + unter NAME erreichbar ist, dann folgt eine Kette von Subobjekten + die immer durch den Subobjekt-Namen gefolgt von den Attributen + als Zahl/Namens-Paare aufgelistet werden; Dieser Aufbau + impliziert, das nur numerische Attribute möglich sind; Beispiel: + #config Buildings nested + erzeugt ein Object BUILDINGS bei welchem der Aufruf + BUILDINGS['Saegewerk'].BAUKOSTEN.STEIN + den Wert 5 ergibt + (b400) + +* Mittles einem letzten Parameter '...' kann man für eine Prozedur + die Warnungen bezüglich abweichender Parameterzahl unterdrücken, also + z.B. '#proc MachWas $arg1 $arg2 ...' (b400) + +* Es gibt jetzt das neue Attribut 'UNIT.PARTEINAME' um an den Namen + einer Partei zu kommen (b399) + +* Für die meisten Objekte kann man jetzt ohne Attribut erfragen ob es + die Instanz gibt, z.B. ist 'UNIT[]' bei nicht vorhanden sein 0, + analoges gilt für REGION, GRENZE, SHIP, BUILDING (b399) + +* Der neue Befehl '#tag ' erlaubt es bei + Option '-cr', in den erzeugten Report zusätzliche Tags einzufügen; + so könnte z.B. mit '#tag EINHEIT bestaetigt 1' ein CR-Eintrag + '1;bestaetigt' für die aktuelle Einheit erzeugt werden (Das Feature + klappt nur für REGION- und EINHEIT-Blöcke) (b397) + +* Anpassung an die neue Silber-Darstellung als Gegenstand ab dem + CR51 (b396) + +* Mittels '#dict $DictName1 $DictName2 ...' kann man nun einen + assoziativen Behälter erzeugen; Dies funktioniert zur Zeit _nur_ + global; Auf den Behälter kann mittels '$DictName[]' zugegriffen + werden; Key darf dabei einen beliebigen Typ haben, auch wenn von + Fließkommazahlen eher abzuraten ist; Man kann zudem auf das n-te + Element in dem Behälter mit '$DictName()' zugreifen und die + Größe (Anzahl der Elemente) durch '$DictName.SIZE' ermitteln + *ACHTUNG: EXPERIMENTELLE PHASE, ERFAHRUNGSBERICHTE ERWÜNSCHT* (b395) + +* Um für den potentiell kommenden Zeitpunkt gewappnet zu sein, an + dem die MESSAGE-Blöcke keine gerenderten Texte mehr enthalten ist + Vorlage jetzt in der Lage anhand der Regeln in MESSAGETYPES die + Nachrichten auch selber aufzubauen, sofern dies erforderlich ist + (b395) + +* Mit der neuen Funktion 'xname(,)' wird ein Name mittels + xNamer-Mechanismen erzeugt, wobei bei Kollisionen max. + erneute Versuche unternommen werden (b394) + +* Mittels der neuen Option '-xn ' können xNamer-Regel-Dateien + importiert werden (b394) + +* Config-File-Handling verbessert und so Skriptbearbeitung etwas + beschleunigt (b392) + +* Das in den neuen CRs nicht mehr vorhandene Unterhalt-Tag bei + Bauwerken wird nun durch das Config-File erstezt (wenn keines + im CR ist) (b391) + +* Im Config-File für Eressea steht nun auch die Taverne (b391) + +* Bugfix: Es gab mit 'UNIT.ÖL' ein Problem, weil die länderspezifische + Einstellung in Vorlage falsch war (b404) + +* Bugfix: Silber wurde bei der Gewichtsberechnung doppelt gewertet + (b403) + +* Bugfix: REGION.GRENZE.SIZE funktionierte leider nicht, es gab eine + Fehlermeldung (b403) + +* Bugfix: Bei Einheiten die nur Silber hatten wurde seit der Silber- + umstellung in Eressea ein Leerkommentar ausgegeben (b403) + +* Bugfix: Durch einen Fehler konnten globale Variable nicht freistehend + verwendet werden, da 'Unbekannte Variable' gemeldet wurde (b402) + +* Bugfix: Die Copyright-Zeile in der Zugvorlage war zu lang (b402) + +* Bugfix: Durch einen Fehler bei der Verwendung von Referenz-Parametern + in der 1.5 konnte es vorkommen, das eine Exception ausgelöst wurde + und so bei Rückkehr von einem #call auch die darüber liegende Prozedur + verlassen wurde (b401) + +* Bugfix: Im Handling zur Existenzabfrage von Objekten hatte sich + bei REGION ein Fehler eingeschlichen, der dazu führte das es + scheinbar jede Region gibt (b400) + +* Bugfix: Für Küstenregionen funktionierte die Inselsortierung leider + immernoch nicht richtig, sollte aber jetzt endlich tun (b400) + +* Bugfix: Der Skriptdebugger kam mit Leerzeichen in Strings beim + parsen der Eingabezeile nicht zurecht (b400) + +* Bugfix: Vorlage reagiert nun tolleranter gegenüber PARTEI-Block- + Umgruppierungen wie sie der JClient vornimmt (b399) + +* Bugfix: Die Option '-p ' erwartete immer Dezimalzahlen, egal + wie die Regelung im Spiel war (b399) + +* Bugfix: Die Funktion 'crop(,)' hatte genau die inverse Logik + und gab leider alles ohne den passenden Ausdruck zurück (b399) + +* Bugfix: Die Inselsortierung mit '-si' funktionierte nicht richtig + und hat auch einen zusätzlich angegebenen Karten-CR bei der + Gruppierung nicht verwendet (b398) + +* Bugfix: Das Config-File für Eressea enthielt falsche (alte) Hitpoints + für einige Rassen (b397) + +* Bugfix: Nachrichten konnten durch das geänderte Message-Handling + als Kopien in Region 0,0 auftauchen (b397) + +* Bugfix: Im Zusammenhang mit Prozeduren mit variabler Anzahl von + Parametern beim Aufruf konnte es zu Verklemmungen kommen die zu einem + Hängen von Vorlage führen konnten (b397) + +* Bugfix: Der Tokenizer für Skript-Dateien reagierte auf Semikolon- + Angaben in Strings mit Fehlverhalten (auch jetzt sind jedoch + Semikolons in Strings besser zu vermeiden, da das CR-Format das + eigentlich eher nicht erlaubt) (b394) + +* Bugfix: Es konnte durch Leerzuweisungen u.U. die Zielvariable + vernichtet werden, so das sie danach als undefiniert gilt (b394) + +* Bugfix: 'UNIT.FREI.' und 'UNIT.KAP.' lieferten + seit Config-File-Umstellung in 1.5 alpha 2 falsche Werte (b393) + +* Bugfix: 'REGION.EINHEITEN' lieferte, wenn man in der Region keine + Einheiten mehr hatte, die Runde zuvor aber schon, den Wert der + vorherigen Runde (b392) + +* Bugfix: 'REPORT.PARTEI' lieferte noch eine Base10-Angabe, unabhängig + von den Modi des Spiels oder Optionen im Config-File (b391) + +* Bugfix: Die Anpassung an die CR-Änderung Burgtypen direkt anzugeben + hatte einen Fehler der die Erkennung der Typen verhinderte, so das + Vorlage die Gewinne falsch berechnet (b391) + +* Bugfix: Der Zugriff auf 'REPORT.REGION[]' klappte nicht korrekt + (b391) + +* Bugfix: Bei einer Übersichtskarte mit großen Koordinatenintervallen + stimmte die Einrückung nicht (b391) + + +---[Vorlage V1.5 alpha 4]--- + +* Der neue Befehl '#break' erlaubt es, Schleifen an beliebiger Stelle + abzubrechen und die Schleifenbedingungen so einfacher zu gestalten + +* Für Funktionen gibt es jetzt neben der Zuweisung an $RETURN auch + den Befehl '#return ' mit dem man analog zu C/C++ eine Funktion + an beliebiger Stelle beenden und einen Wert zurückgeben kann + +* Vorlage gibt nun mit '-v' eine Versionsinfo aus und beendet sich + +* Der neue Befehl '#table' erlaubt es formatierte Tabellen ähnlich den + Regionsinfos zu erzeugen; '#table clear' löscht die laufende Tabelle, + '#table ' fügt die Textrepresentation von '' als Feld in + die Tabelle ein, '#table next' beginnt eine neue Zeile und mit + '#table dump' wird die Tabelle in die Zugvorlage übertragen + +* Es gibt die neuen Attribute 'REGION.X', 'REGION.Y', 'REGION.Z' sowie + 'REGION.CHAR' um die Koordinaten bzw. das Kartenzeichen einer Region + zu bekommen + +* Man kann jetzt mit 'REPORT.REGION[]' auf Regionen per Reihenfolge + zugreifen, die Anzahl der Regionen ist in REPORT.REGION.SIZE zu finden + +* Die neue Funktion 'itoan(,)' erlaubt es Zahlen in + Strings mit wählbarer Basis zu konvertieren + +* Die neue Funktion 'antoi(,)' erlaubt es Strings mit + wählbarer Basis in Zahlen zu konvertieren + +* Bugfix: #message-Ausgaben in den Einheitenblöcken wurden nicht + umgebrochen + +* Bugfix: 'UNIT.NUMMER' hat ungeachtet der Options u.U. Base36-Nummern + zurückgegeben + +* Bugfix: Mit fehlerhaften (z.B. alten) Config-Files konnte Vorlage + mit einem Divisions-Error abfliegen + +* Bugfix: Es wird sichergestellt, das Fehlermeldungen nun immer + auf einer neuen Zeile anfangen (nicht die erste in den Meldungen + zum lesen irgendwelcher Dateien eingeklebt wird) + +* Bugfix: Vom Objekt UNIT konnte man nicht an die Attribute 'PRIVAT', + 'PARTEITARNUNG' und 'BESCHR' kommen + +* Bugfix: Die Objektzugriffe auf 'REGION.SHIP[].' und + 'REGION.BUILDING[].' funktionierten seit Umstellung + auf Volle Base36-Unterstützung nicht mehr + +* Bugfix: Weggelassene Referenzparameter führten zu Mutationen in + Skriptparametern + + +---[Vorlage V1.5 alpha 3]--- + +* Es gibt nun die neuen Funktionen 'floor()' und 'ceil()' + mit denen Fließkommawerte zur nächstkleineren bzw. nächstgrößeren + Ganzzahl gewandelt werden können + +* Die neue Funktion 'isnothing()' ergibt für leere Variable + (typlose Werte) einen Wert ungleich 0 + +* Mit der neuen Funktion 'abs()' bekommt man den Absolutwert + oder auch Betrag eines Ausdrucks (also ohne Vorzeichen) + +* Durch die Funktion 'sign()' kann man das Vorzeichen eines + Ausdrucks ermitteln; Es wird für negative Zahlen -1, für positive + Zahlen 1 und für die 0 eine 0 zurückgegeben + +* Bugfix: Im Expressionparser hatte sich leider ein neuer Bug im + Zusammenhang mit Funktionen eingeschlichen. + + +---[Vorlage V1.5 alpha 2]--- + +* Statt der Zusammenschreibung von Mehrwort-Attributen (Kräuternamen) + kann man nun auch '~' statt des Leerzeichens verwenden um darauf + zuzugreifen also z.B. 'UNIT.BLAUER~BAUMRINGEL' + +* Das Tag 'hunger' wird nun unterstützt, ausgegeben und steht auch + als Attribut von UNIT zur Verfügung + +* Das Tag 'alias' wird nun unterstützt + +* Das neue Attribut 'UNIT.POSITION' gibt die Position einer Einheit + in einem Gebäude an (oder 0, wenn die Einheit nicht in einem Gebäude + steht) + +* Option '-kk' Erzeugt eine Karte zu Beginn der Vorlage + +* Auf die Parameter einer Prozedur kann auch mittels 'ARG[]' + ( beginnend mit 0) zugegriffen, die Anzahl der Parameter + bekommt man über 'ARG.SIZE' heraus + +* Auf alle einfachen Attribute eines CR-Objektes kann nun mit dem + CR-Namen als Attribut-Namen zugegriffen werden + +* Es gibt nun die neuen Callbacks 'OnUnit', 'EndUnit' und 'EndRegion' + +* In allen Callbacks kann man nun #message verwenden um an der + entsprechenden Stelle Kommentare in die Zugvorlage einzubauen + +* Die Rassen ([Races]) haben den neuen Config-Eintrag Unterhalt + bekommen, welcher die Unkosten pro Runde für eine Person angibt + +* Die Gegenstände ([Things]) haben den neuen optionalen Config-Eintrag + KAP bekommen, der das Fassungsvermögen angibt (nur für Pferd/Wagen) + +* Die Kapazitätsangaben aus dem Config-File werden nun endlich + verwendet, genauso wie Gewichte der Rassen, Unterhalt der Rassen und + andere Rassenwerte, sofern von Vorlage intern verwendet + +* Das neue Subobjekt 'TALENTE' von 'UNIT' ermöglicht den Zugriff auf + die Talente ohne Kenntnis ihres Namens; 'UNIT.TALENTE.SIZE' gibt + dabei die Anzahl der erlernten Talente zurück, + 'UNIT.TALENTE[].' mit 0<=idx].' mit + 0<=idx', 'BBase = ' bzw. 'PBase = ' im + übersteuert werden + +* Bugfix: Der Zugriff auf REGION.LAEN klappte nicht + +* Bugfix: unter bestimmten Bedingungen wurde für ein Bauwerk die falsche + Einheit als Besitzer gekennzeichnet + +* Bugfix: Beim Objekt 'BUILDING' fehlte das Attribut 'BELAGERER' + +* Bugfix: In der Vorlage wurden unter bestimmten Bedingungen keine + Regionsänderungen angezeigt, obwohl eigene Einheiten in beiden CRs + anwesend waren + +* Bugfix: Untote werden nicht mehr als "Mitesser" gerechnet + +* Bugfix: Beim Zugriff 'REGION.UNIT[].' kam es unter + Verdanon u.ä. zu Fehlermeldungen, weil irrtümlich intern mit Base36 + gearbeitet wurde + +* Bugfix: Ein Fehler in der internen Arithmetrik führte zum Verlust der + Nachkommastellen + +* Bugfix: Es konnte zu Inkonsistenzen zwischen den in der Vorlage + angezeigten Kapazitäten und den über Metabefehle zur Verfügung + gestellten kommen + +* Bugfix: ECheck-Zeilen werden nur noch bei Eressea/Empiria erzeugt + +* Bugfix: Die Tarnrasse wird bei Dämonen nun in der Zugvorlage von + nachfolgenden Angaben mit Komma abgetrennt + +* Bugfix: Der Zugriff auf den Pool funktionierte bei Attributen mit + mehreren (zusammengeschriebenen) Worten (z.B. Kräutern) nicht + +* Bugfix: Der Expression-Parser hat die Auswertung nach einem + Funktionsaufruf irrtümlich abgebrochen + +* Bugfix: In der Funktion 'change(val1,rexp,val2)' trat ein interner + Fehler in einem regulären Ausdruck auf + +* Bugfix: Leere Gebäude/Schiffe tauchten (bei '-sb') nicht in der + Vorlage auf + +* Bugfix: Die Zeilennummern bei Fehlermeldungen und im Skriptdebugger + konnten unter bestimmten Umständen falsch sein + + +---[Vorlage V1.5 alpha 1]--- + +* Die neue Funktion 'random()' erzeugt einen Zufallswert von 0 bis + (2^31-1) + +* Die neue Funktion 'equals(val1,val2)' gibt einen Wert ungleich 0 + wenn die beiden Values sich entsprechen (Case-/Umlaut-Ignorierend) + +* Die neue Option '-b' gibt alle Beschreibungen (Einheit/Region/ + Gebäude/Schiff) aus + +* beim Umbruch wird nun versucht Anzahlen nicht von Gegenständen zu + trennen (z.B. beim Materialpool) + +* Mit #var $VarName1 $VarName2,... können nun Variablen deklariert + werden, geschieht dies außerhalb von #proc oder #func wird eine + globale Variable erzeugt + +* Die neue Option '-fd' fordert das Deklarieren von Variablen vor + erstem Gebrauch, sonst Fehlermeldung + +* Die neue Option '-e ' lenkt die Fehlermeldungen in eine Datei + um + +* Neben 'OnInit' vor und 'OnExit' nach der Vorlageerzeugung wird nun + auch 'OnRegion' zu Beginn jeder Region aufgerufen + +* #message funktioniert auch in OnRegion und ermöglicht es, weitere + Informationen im Regions-Header anzugeben + +* Die neue Funktion 'match(val,rexp)' wendet auf die + Stringrepresentation von die den regulären Ausdruck an + und gibt einen Wert ungleich 0 zurück, wenn der Ausdruck passt + +* Die neue Funktion 'before(val,rexp)' gibt alles von bis zum + ersten Treffer von zurück + +* Die neue Funktion 'after(val,rexp)' gibt alles von nach dem + ersten Treffer von zurück + +* Die neue Funktion 'crop(val,rexp)' gibt den auf passenden Teil + der Stringrepresentation von zurück + +* Die neue Funktion 'change(val1,rexp,val2) gibt eine Kopie von + zurück, in der alle Vorkommen von durch ersetzt wurden + +* Die EFFECTS-Einträge aus dem CR V47 werden in die Vorlage übernommen + +* Innerhalb von Strings können Sonderzeichen (auch Semikolon) durch die + Schreibweise '\ddd' erzeugt werden, so ergibt '\033' ein '!' + +* Bugfix: Zugriff auf Dinge mit mehrwörterigen Namen (Grüner Spinnerich) + klappte nicht richtig + +* Bugfix: '!=' und '!' Operatoren funktionieren endlich + +* Bugfix: Der Zugriff auf UNIT.HOLZFAELLEN schlug fehl, + UNIT.HOLZFAeLLEN oder UNIT.HOLZFÄLLEN hingegen funktionierte + +* Bugfix: externe Skripte reagierten manchmal sehr unvorhersehbar bei + Verwendung von Kommentaren + +* Bugfix: Fehlerhafte Optionsauflistung in Vorlage (Aufruf ohne + Parameter) behoben + + +---[Version V1.4 beta 13]--- + +* Die neuen Verdanon-Tags bei Einheiten (Ladung und Kapazitaet) erden + nun erkannt + +* Das neue Eressea-CR-V44-Tag 'temp' wird erkannt + +* Wenn die Ausgabe mit '-o' oder '-ox' in eine Datei erfolgt werden + Fehlermeldungen auf stdout ausgegeben, um so einen Pager benutzen oder + sie in eine Datei umleiten zu können + +* Wenn die Option '-sb' aktiv ist werden nun auch Burgen und Schiffe + angezeigt in denen man keine Einheiten hat + +* Die neue Option '-us' ermöglicht es, das die fremden Einheiten die mit + '-u' oder '-uv' angezeigt werden zwischen die eigenen mit einsortiert + werden + +* Talentänderungen die keine sind (+0) werden nun nicht mehr ausgegeben. + +* Es werden nun beim Aufruf Steuerdateien unterstützt, die mit + vorangestelltem '@' übergeben werden; Darin können sich dann weitere + Kommandozeilenparameter befinden + +* Bugfix: CR-Zeilen die mit einem Umlaut endeten konnten diesen beim + einlesen verlieren + +* Bugfix: Bei Option '-cr' ging in leeren COMMANDS-Blöcken das leere + Kommando verloren + +* Bugfix: Die Pfadangaben für den Import von Skript-Dateien wurde + unabhängig vom aktiven -cfg aus vorlage.cfg gelesen + +* Bugfix: Bei Verdanon und Empiria wurden oftmals irrtümlich bei den + Magiern auch (unsinnige) Aura-Werte angezeigt + +* Bugfix: Die Benutzung von Skriptdateien über Pfadangaben in der + Konfig-Datei konnte zum Absturz führen + + +---[Version V1.4 beta 12]--- + +* Die Konfiguration für Vorlage ist nun nicht mehr in der Datei + 'vorlage.cfg' (Win) oder '.vorlagerc' (Linux) sondern in einer Datei + die vom Spiel abhängt. Für Eressea sind dies 'eressea.cfg' oder + '.eressearc', für Verdanon 'verdanon.cfg' oder '.verdanonrc' (Die alte + Datei wird aber zum gegenwärtigen Zeitpunkt noch benötigt); Die Datei + für Verdanon wurde von Andreas Beer freundlicher weise zur Verfügung + gestellt, Danke dafür! + +* Die Wahl der richtigen Konfigurationsdatei lässt sich mit der neuen + Option '-cfg spiel' setzen, geschieht dies nicht, so sucht Vorlage in + dem ersten CR nach einer gültigen Spiel-Kennung wird keine gefunden, + wird wie bisher 'vorlage.cfg' oder '.vorlagerc' verwendet + +* Das neue CR-Format Version 42 wird nun unterstützt, und damit auch die + geänderten Messages + +* Die neue Option '-wait' lässt Vorlage am Ende der Ausführung auf einen + Tastendruck warten + +* Die neue Option '-td' bewirkt, wenn durch Vor-CR möglich, das bei den + Einheiten die Talentveränderungen angezeigt werden + +* Die neue Option '-up' zeigt in jeder Region am Ende eine + Zusammenfassung der fremden Parteien in der Region an; ist die + Materialpool-Anzeige aktiv, so wird zudem auch noch der jeweilige + Materialpool der Parteien ausgegeben + +* Das Objekt BUILDING wurde aus REGION ausgelagert und ist nun + eigenständig als BUILDING[id].attribut anzusprechen. + +* Der Ausdruck REGION.BUILDING[n].attribut existiert noch, arbeitet nun + aber über einen Index von 0 bis REGION.Bauwerke-1 + +* Das Objekt BUILDING[id] hat nun die Attribute Nummer, Typ, Name, + Besitzer, Groesse, Unterhalt sowie Insassen + +* Es gibt ein neues Objekt SHIP[id] mit den Attributen Nummer, Typ, + Name, Kapitaen, Kueste, Schaden, Prozent, Ladung, MaxLadung sowie + Kapazitaet + +* Mit dem Ausdruck REGION.SHIP[n].Attribut kann man über einen Index von + 0 bis REGION.Schiffe-1 auf die Schiffe in einer Region zugreifen + +* Das Objekt UNIT hat die neuen Attribute Typ, WahrerTyp und Name + +* Die Kapazitätsberechnungen für Verdanon sollten nun (nach dem neuen + Verdanon-System) funktionieren (Fehler bitte melden, spiele selber + nicht Verdanon!) + +* Bugfix: Der Modulo-Operator (%) funktionierte nicht. + + +---[Version V1.4 beta 11]--- + +* Das neue CR-Format Version 41 wird nun unterstützt, und damit auch die + neuen Messages + +* Als Konsequenz daraus berücksichtigt Vorlage nun bei CRs ab Version 41 + auch kostenpflichtige Talente in den Ausgabe-Berechnungen + +* Die Anzeige des Datums für CRs ab dem zweiten Zeitalter Eresseas ist + auf den neuen Kalender angepasst + +* Zauberer haben nun auch eine Auflistung der Sprüche, wenn die Anzeige + der Talente aktiv ist + +* Die neuen Attribute Aura und Auramax werden hinter dem Magietalent in + der Talentliste ausgegeben und auch als neue Attribute des Objektes + UNIT unterstützt + +* Bugfix: Durch einen Seiteneffekt wurde in Version 41 CRs ein falscher + Punktestand angezeigt + +* Bugfix: Fremde Einheiten von Arenaregionen und normalen Regionen + wurden gemischt wenn die Regionen (bis auf die Ebene) die selben + Koordinaten hatten + + +---[Version V1.4 beta 10b/c]--- + +* Bugfix: Fälschlicherweise wurden als Kapazitäten unter Empiria + 5.4/10.4 verwendet, unter Eressea hingegen 5.0/10.0, das ist jetzt + behoben (und aus 10.4 wurden korrekterweise 10.8) + +* Bugfix: Der Befehl #default hatte einen Bug, der dazu führte, das + Befehle wie ARBEITE nicht in die Vorlage übernimmen wurden + +* Bugfix: Die Zeile Personen/Einheiten/Punkte/Durchschnitt wurde + gesplittet + +* Bugfix: Der Insel-Sorter greift leider noch immer nicht auf alle + Regionsinfos zurück, aber sollte nun, wenn Island-Tags Verwendung + finden zumindest richtig sortieren + +* Bugfix: Die dynamisch erzeugten Regionsinfo-Tabellen wurden mit + reichlich Leerzeichen am rechten Rand "dekoriert" + +* Bugfix: Auch bei Verdanon-Vorlagen wurden ECheck-Infos und der + REGION-Befehl generiert + +* Bugfix: Das Unterhalts-Flag im Einheitenkommentar wurde auch erzeugt, + wenn gar kein Unterhalt für das Gebäude gezahlt werden muß + +* Bugfix: Bei einzelnen Katzen wurde "1 Katz" angezeigt + + +---[Version V1.4 beta 10]--- + +* CRs mit mehreren Ebenen werden nun unterstützt + +* In den EINHEIT-ECheck-Infos wird nun bei der Kommandoeinheit eines + Gebäudes der Unterhalt angezeigt + +* Bei eigenen parteigetarnten Einheiten wird dies nun angezeigt + +* Bei Eisen und Laen wird nun angezeigt, ob nichts nie vorhanden ist + ('-') oder das Vorkommen unbekannt ist ('?') + +* Im Vorlagekopf werden nun auch Personenzahl, Einheitenzahl, + Punktezahl und Prozentualer Stand zum Schnitt angegeben + +* Bei Option '-hb' wird nun das Gesammteinkommen (Eintreiben/ + Unterhalten/Arbeiten/Luxusgutverkauf), sowie die Gesammtausgaben + (Verpflegung, Luxusguteinkauf, Gebäudeunterhalt) im Vorlagekopf + ausgegeben, sowie in jeder Region diese Werte für die Region; Die + Ausgaben berücksichtigen aber leider noch nicht ausgaben für + kostenpflichtige Talente (gibt leider scheinbar keine Nachricht + darüber) + +* Bugfix: Wenn ein Bergbauer eine Region verlässt und die Eisen/Laen- + Info dadurch wegf/auml;llt wird keine Änderung angezeigt + +* Bugfix: In Empiria wird nun ohne Silbergewicht und mit 5/10GE + Kapazität gearbeitet + + +---[Version 1.4 beta 9e]--- + +* Bei Einheiten wird in der Vorlage nun in dem Einheitenkommentar das + Gebäude und die Position darin angegeben, um so zu erkennen, ob ein + Schutz/Vorteil für die Einheit vorliegt oder nicht; Die Angabe erfolge + analog zu der von ECheck benutzen Syntax für Schiffsbesatzungen, + zuzüglich der Angabe (n/m) wobei n die Position im Gebäude und m die + Kapazität angibt + +* Der neue experimentelle Befehl #default fügt in die Einheitenbefehle + die langen Befehle aus dem CR ein, um z.B. ROUTE in Verbindung mit + Metabefehlen in einer Einheit benutzen zu können + +* Bugfix: Die Änderungen in 1.4 beta 9c betreffs der neuen CR-Struktur + führten zu einem Problem mit Insel-CRs + +* Bugfix: In Empiria wurde für Silber ein Gewicht angenommen (die + Rassenkapazitäten sind aber noch immer nicht korrekt, sorry) + + +---[Version V1.4 beta 9d]--- + +* Die neue Option -klp kann anstelle von -k oder -kl verwendet werden, + und es wird nur das Produzierte Luxusgut aufgeführt + +* Bei Option -pm wird nun der erzeugte BESCHREIBE PRIVAT auch + entsprechend der Zeilenlänge, die mit -w eingestellt wurde, + umgebrochen; Hierbei wird für aktuelle Eressea-Reports die neue + Umbruchsmethode verwendet + +* Bugfix: Unter Empiria wurden keine Befehle für die Zugvorlage erzeugt, + weil die Default-Tags nicht herangezogen wurden + + +---[Version V1.4 beta 9c]--- + +* Die neuen CR-Blöcke ALLIANZ und PARTEI (die Folge-Parteiblöcke) werden + nun unterstützt + +* Es gibt nun einen Skriptdebugger mit dem man Fehler in Skripten + schneller finden können sollte (Mit ? bekommt man eine Hilfe) + +* Mit dem neuen Befehl #trace kann man Ablaufinformationen + aktivieren. #trace 1 schaltet die Ausgabe ein, #trace 2 springt in + den Metaskript-Debugger + +* Der neue Befehl #notrace deaktiviert die Ausgabe der + Ablaufinformationen + +* Das Objekt UNIT hat die neuen Attribute Land, Kampfstatus und die + Kapazitätsangaben frei.reiten, frei.gehen, kap.reiten sowie kap.gehen + +* Das Objekt REGION hat die neuen Attribute Name, Terrain sowie + 'Building[nr].Typ' und 'Building[nr].Groesse' + +* Es wird nun ein vorhandenes Basis-Tag sowie das Koordinaten-Tag + +* Es wird nun zu den Luxusgütern auch die maximale Anzahl handelbarer + Güter angezeigt + +* Das Errorhandling wurde überarbeitet und teilweise Verbessert + +* Bugfix: Nachrichten die zweimal dieselbe Einheitennummer enthielten, + wurden auch der Einheit doppelt zugeordnet (Verteilung des "Auges des + Drachen") + +* Bugfix: Der Zugriff auf Einheiten mit falscher Einheitennummer führte + zu einem Absturz + +* Bugfix: Anzeige von fremden Talenten wenn CR vorhanden klappte nicht + +* Bugfix: Fehler in Gewinnberechnung und vorlage.cfg behoben + +* Bugfix: Fehler in LerneMache-Skript behoben + +* Bugfix: Bei der Behandlung von fehlerhaften Ausdrücken kam es zu + Folgefehlern + +* Bugfix: Der Aufruf unbekannter Funktionen führte zu einem Absturz + +* Bugfix: Der Vergleichsoperator '==' wurde falsch ausgewertet + +* Bugfix: Es wurde im für den Monat Dezember das falsche Jahr im Report + angezeigt + + +---[Vorlage V1.4 beta 9a]--- + +* Das Objekt UNIT hat die neuen Attribute X und Y, um die Position der + Einheit zu erfragen + +* Bugfix: Die Auswertung des Parameters '-p' war fehlerhaft und die + Option hatte keine Wirkung + +* Bugfix: In #if / #ifregion / #ifunit wurden Zuweisungen (und nur die) + auch ausgeführt, wenn die Bedingung nicht erfüllt war + + +---[Vorlage V1.4 beta 9]--- + +* In CRs existierende Insel-Tags werden nun von Vorlage ausgewertet und + die Inselnamen für die Regionen, wenn möglich, ermittelt; diese Namen + werden dann in der REGION-Zeile mit ausgegeben + +* Die neue Option '-un' wirkt bei aktivem '-u' oder '-uv' und sorgt + dafür, das nur noch fremde Einheiten auszugeben, die neu in der Region + sind, oder in Regionen in denen man im Vergleichsmonat nicht war + +* Wenn die Information aus dem Vergleichsreport (i.d.R. dem Vormonat) + das ermöglichen, wird bei fremden Einheiten (wenn diese angezeigt + werden) nun auch noch die Region aus der sie kommen ausgegeben + +* Die Talente von Einheiten sind nun nach Stufen sortiert, das + verbessert den Wert der Einheitensortierung nach Talenten + +* Der neue Befehl #message ermöglicht es normale Kommentare aus + Metabefehlen heraus zu erzeugen; Der Text muß eine durch Hochkommas + eingeschlossene Zeichenkette sein, wenn er Leer- oder Sonderzeichen + enthalten soll + +* Die neue Option '-m' erzeugt in den Regionsinfos die Auflistung des + Materialpools (Auch wenn dieser nicht aktiviert ist); Es ist zu + beachten, das Vorlage, da es die nächsten Befehle ja nicht vorhersehen + kann, keine Reservierungen berücksichtigt + +* Es wird jetzt beim Tarne-Talent, falls eine Abweichung vom Basiswert + vorliegt, der aktive Tarne-Status angezeigt + +* Bugfix: Gelegentliche Abstürze in der Release-Version + +* Bugfix: Die Gewinnberechnung hatte manchmal zu völlig falschen + (negativen) Ergebnissen geführt + +* Bugfix: Bei der Auswertung von Ozeanregionen konnte Vorlage mit + Zugriff auf falschen Speicher abfliegen + +* Bugfix: Bei der Auswertung von Regionen die vorher nur Nachbarregioen + oder Durchreiseregionen waren konnte Vorlage mit Zugriff auf falschen + Speicher abfliegen + +* Bugfix: Es wurde in 1.4 b 8 berall Mallorn statt Bäumen gemeldet + +* Bugfix: Wenn man mit Differenzen der Regionsinfos arbeitet (also den + letzten CR mitgegeben hat) wurden manchmal Änderungen angezeigt, die + nicht stimmten, wenn aus dem alten CR keine Daten dafür vorlagen, + jetzt unterdrückt Vorlage in so einem Fall die Angabe + + +---[Vorlage V1.4 beta 8]--- + +* Unter Linux heißt 'vorlage.cfg' jetzt etwas konformer '.vorlagerc' und + wird im $HOME-Verzeichnis gesucht + +* Es können jetzt mehrere (bel. viele, wenn Speicher und Konsole + mitmachen) CRs angegeben werden, und zwar auf von verschiedenen + Spielern; Bei Verwendung der Option '-ox' werden für alle Parteien der + aktuellen (der neusten) Runde Vorlagen erzeugt, die die Namen und den + Pfad der jeweiligen Bezugs-CRs plus Endung tragen + +* Wenn Option '-ox' verwendet wird, ud die resultierenden Dateien schon + existieren, werden sie nicht überschrieben, sondern ein Fehler + gemeldet; Mit der neuen Option '-f' kann man das verhalten abschalten + und das Überschreiben forcieren + +* Wenn es die Environment-Variable 'VORLAGEOPTIONS' gibt werden die + Daten darin vor den Komandozeilen-Parametern ausgewertet + +* Beim Zugriff auf Regionsdaten oder Einheitendaten werden, beim + Vorhandensein mehrerer Informationen aus verschiedenen aktuellen CRs, + die jeweils "besten" Daten verwendet, d.h. Wenn man selber z.B. eine + Region nur als Durchreiseregion gesehen hat, aber eine Einheit einer + Partei deren CR man Vorlage mitgegeben hat (Verbündeter) dort steht, + kann man dennoch in Metabefehlen auf alle Daten der Region zugreifen, + als ob man dort eine Einheit hätte. (Wenn in einem CR ein T7-Zwerg + steht, kann man also auch mit 'region.laen' sehen ob da Laen ist, + selbst wenn man die Info im eigenen CR nicht hatte) + +* Neue Attribute im Objekt REGION: Gewinn enthält den Gewinn-Wert aus + den Regionsinfos und das Attribut Pool ermöglicht den Zugriff auf + den Material-Pool; So ergibt z.B. 'region.pool.eisen' die Eisenmenge + der eigenen Partei in der Region + +* Die neuen Optionen '-u' und '-uv' erlauben es, alle fremden Einheiten + in den Regionen hinter den eigenen als NR-ähnlichen Kommentar einfügen + zu lassen (Auch hier gilt: Die besten vorhandenen Infos werden + benutzt); Die zweite Option ist dabei Verbose, also mit allen Daten + (Talente/Beschreibungen) sofern vorhanden; Einheiten die im eigenen + Report nicht vorhanden sind, aber in einem anderen schon, werden mit + einem 'T' markiert, parteigetarnte mit einem '!'; + +* Mit 'region[x,y]' kann man auch auf Daten von anderen Regionen + zugreifen, wobei die Koordinaten x und y denen im Report entsprechen + müssen + +* Mit 'unit.region[dx,dy]' kann man auf Regionen relativ zur Position + einer Einheit zugreifen, wobei dx und dy die Deltawerte der Position + sind (z.B. ist 'unit.region[0,1]' die Region im Nordosten); + +* Die Metasprache unter stützt nun auch Funktionen mittels des #func- + Befehls; Dabei können aber zur Zeit noch keine Referenzen verwendet + werden; Des weiteren können Funktionen keine Eressea-Befehle erzeugen; + Die Werterückgabe erfolgt durch Zuweisung an $RETURN + +* Das Objekt UNIT unterstützt nun die Attribute: Bewache mit dem man den + Bewache-Status erhält (ein Wert ungleich Null bedeutet, die Einheit + bewacht die Region), frei.gehen, frei.reiten mit denen man die freie + Kapazität in GE erhält, die eine Einheit für die jeweilige + Fortbewegung hat, kap.gehen, kap.reiten die Kapazität der Einheit, + wenn sie alles abgelegt hätte (also leer) + +* In Strings kann nun mittels des '\'-Zeichens ein Hochkomma "escaped" + werden, so das es nicht als Stringende gilt; So kann man nun z.B. Mit + "#ifregion 'Shak\'Tar' {...}" auch auf Regionen Abfragen, die ein + Hochkomma im Namen haben + +* Da das '\'-Zeichen eine neue Bedeutung hat, ist nun das '|'-Zeichen + für die erzeugung von Anführungszeichen in Metabefehls-Strings + zuständig + +* Die neue Option '-sp' sortiert die Einheiten nach den + BESCHREIBE-PRIVAT-Texten, um so eine eigene Sortierung zu erzeugen + +* Bugfix: Kommentare die keine Metabefehle enthalten wurden nicht in die + Vorlage übernommen und gingen verloren + +* Bugfix: Die Leerzeile nach Kapitänsinfos wurde entfernt + + +---[Vorlage V1.4 beta 7]--- + +* Die neue Option '-cr' erlaubt es, anstelle einer Zugvorlage einen + neuen CR mit dem Ergebnis der Metabefehlsauswertung zu erzeugen um + Anwender von anderen Tools (z.B. EHMV) in den Genuß der Metasprache + kommen zu lassen; Es gelten die selben Optionen zur Beeinflussung des + Ausgabeziels wie bei einer Zugvorlage (also –o file, -ox ext oder + eben stdout) + +* Verwendete Spieldaten sind nun größtenteils in der Datei 'vorlage.cfg' + Aufgeführt und können so geändert werden; dazu gehören u.a. die + Gewichte von Gegenständen, Schiffskapazitäten, Terraintypen (incl. der + in der Minimap zu verwendenden Zeichen); Die Datei wird im selben + Verzeichnis gesucht, in dem Vorlage liegt + +* In der Minimap werden jetzt unterschiedliche Zeichen angezeigt, je + nachdem ob in der Region eigene Einheiten sind, oder nicht (default + groß=eigene Einheiten, klein=keine eigenen Einheiten) + +* Bei Unterprogrammen muß jetzt in der #proc-Zeile vor den Referenz- + Parametern, also Parametern, die das Unterprogramm nach außen + verändern kann, ein '&'-Zeichen vorangestellt werden, um beide + Übergabearten zu ermöglichen (Achtung: #next u.ä. erwarten in + Unterprogrammen Referenz-Parameter!) + +* Es gibt nun im Objekt REGION die neuen Attribute Silberpool und + Personen + +* Strings in Metabefehlen können jetzt mit Hochkommas umschlossen + werden, um Leerzeichen und Sonderzeichen darin zu ermöglichen; + +* Das '\'-Zeichen wird innerhalb von Strings bei Übertragung in Eressea- + Befehls-Zeilen in Anführungszeichen umgewandelt, um das Manko zu + beheben, das in persistenten Kommentaren keine Anführungszeichen + erlaubt sind + +* Bugfix: Der Zugriff auf andere Einheiten mit + UNIT[Einheitennummer].Attribut hatte einen Bug, der zur Meldung + 'Fehlerhafte Klammerung' führte + + +---[Vorlage V1.4 beta 6]--- + +* Erste Version die es auch für x86-Linux gibt (experimentell) + +* Mittels des '~'-Zeichens können nun auch die Befehle wie BENENNE und + BESCHREIBE in Metabefehlen verwendet werden + + +---[Vorlage V1.4 beta 5]--- + +* In Unterprogrammen kann man nun eigene Variable benutzen (z.B. + '$MyVar=1234') + +* Der neue Metabefehl '#while' ermöglicht in Unterprogrammen Schleifen + zu verwenden, um z.B. über die Einheiten einer Region zu iterieren + +* Das Objekt REGION hat die neuen Attribute Einheiten, Gewinn und Unit[] + bekommen, wobei man mit 'region.unit[0].Attribut' die oberste Einheit + des CR/NR in der Region erreicht und diesen Ausdruck wie ein UNIT- + Objekt verwenden kann + +* Das Objekt UNIT hat die neuen Attribute Partei, Bauwerk, Schiff und + Nummer + +* Die Kapitänsinfo wird nun ebenfalls bei Bedarf umgebrochen + +* Bugfix: Attribute (Gegenstände) die Leerzeichen im Namen hatten + konnten prinzipbedingt nicht angesprochen werden. Dies kann nun + erfolgen, indem man die Leerzeichen einfach wegläßt + +* Bugfix: Die Gewinn-Berechnung in den Regionsinfos hat Jobverluste, + z.B. durch Bäume nicht berücksichtigt + +* Bugfix: Beim '#every'-Befehl wurde die Phase (2. Parameter) nach dem + Durchlauf auf einen falschen Wert gesetzt + +* Bugfix: Die Kapazität von Einheiten mit Trollen die Wagen zogen hatte + immer noch einen Fehler + + +---[Vorlage V1.4 beta 4]--- + +* Unterprogramme werden jetzt mit den neuen Befehlen '#call' und '#proc' + und der Option '-i filename' unterstützt (näheres in der Doku) + +* Die Regionsinfo-Tabelle wurde etwas schmaler gestaltet + +* Bugfix: Die Kapazitätsberechnung für Trolleinheiten hatte noch Bugs + + +---[Vorlage V1.4 beta 3]--- + +* Die neue Option '-hb' erlaubt es, zu Beginn der Zugvorlage eine + Handelsübersicht, nach Parteien und Produkten einzufügen, um den + Überblick zu behalten + +* Die neue Option '-si' erlaubt es, die Regionen nach Inselzugehörigkeit + zu sortieren, statt nach Report-Reihenfolge, dabei liegen alle + Regionen beisammen, die miteinander Verbunden sind + +* Die neue Option '-ox ext' leitet die Zugvorlage, wie die Option + '-o filename' in eine Datei um, die aber den selben Basisnamen wie der + Bezugsreport hat, aber die Dateierweiterung ext bekommt; Der Report + muß die Endung '.cr' haben (wie ja üblich) + +* Die neue Option '-pb' zeigt BESCHREIBE-PRIVAT-Inhalte in der Vorlage + an + +* Bei Schiffen steht nun auch die freie und die theoretische Kapazität + +* In den Regionsinfos steht nun der von den Bauern erwirtschaftete + Gewinn, also die Menge, die man maximal Abschöpfen kann, ohne die + Regionsreserven zu gefährden + +* In der REGION-Zeile steht nun auch noch der Geländetyp + + +---[Vorlage V1.4 beta 2]--- + +* Bugfix: Es wurden bei Angabe von zwei Reports die Nachrichten von + beiden bei der Region angezeigt und bei unit[xyz] konnte unter + Umständen auf die alten Daten der Einheit zugegriffen werden + +* Bugfix: In der Doku wurde überall als Trennzeichen zwischen Befehlen + ein Semikolon genannt, es ist aber der Doppelpunkt + + +---[Vorlage V1.4 beta 1]--- + +* Es können zwei Reports angegeben werden, wobei der ältere genutzt + wird, um die Änderungen der Regionsdaten anzuzeigen, falls die + jeweilige Region in beiden Reports vorkommt (Reihenfolge egal) + +* Mit der neuen Option '-l' kann man eine Gewichtsübersicht zu Einheiten + aktivieren; Dies ergibt bei jeder Einheit eine Zeile in der das + Gesamtgewicht, sowie die freie/theoretische Kapazität beim Reiten + (wenn möglich) und beim Gehen angegeben werden; Sind zu viele Pferde + vorhanden wird dies gemeldet + +* Statt die Standardausgabe für die Zugvorlage zu benutzen kann man nun + mit der Option '-o filename' die Ausgabe in eine Datei umlenken; Dies + geht zwar normalerweise mit '> datei' auch, aber unter Windows klappt + diese Umlenkung nicht aus "START/Ausführen..." und auch nicht bei + Verknüpfungen + +* Die Zeilenlänge ab der Nachrichten und Gegenstandslisten oder + Talentlisten umgebrochen werden kann mit der Option '-w len' auf len + Zeichen eingestellt werden + +* Die neue Option '-q' unterdrückt die Ablaufmeldungen auf der Konsole, + zu denen nun auch eine Zeitmessung gehört + +* Die Reihenfolge der Regionen in der Vorlage entspricht nun der im + Report, um das durcharbeiten ohne viel Blättern zu erreichen + +* Bugfix: Bei Einheiten auf Schiffen wurden alle zu ECheck-Kapitänen + (alle bekamen das große 'S'); + +* Bugfix: Bei Schiffsneubauten oder Hafenschiffen wurde als + Auslaufrichtung irrtümlich Nordwesten angezeigt, statt + "Auslaufrichtung beliebig"; + + +---[Vorlage V1.3.3]--- + +* Neben der Tragekapazität (welche leider noch nicht immer stimmt), + wird nun das Gesamtgewicht der Einheit angegeben + +* Bugfix: Fehler beim Zugriff auf andere Einheiten über den Index + behoben + +* Bugfix: Das Verhalten von '#after' wich von der Doku ab, d.h. + '#after 1' bedeutet nun nächste Vorlage, nicht '#after 0' wie + fälschlicherweisein den alten Versionen; Es ist also jetzt eine Runde + später; + + +---[Vorlage V1.3.2]--- + +* Lange Nachrichten und Talent-/Gegenstandslisten werden nun umgebrochen + +* Bei Einheiten auf Schiffen wird nun wie in der Standardvorlage die + Schiffsnummer in den eckigen Klammern angegeben + +* Bugfix: Der Bug in der 'else'-Behandlung wurde behoben + + +---[Vorlage V1.3.1]--- + +* Unterstüzung für EINHEITSBOTSCHAFTEN implementiert + +* Bugfix: Ein Bug in der Kapitänsbehandlung machte dieses Release + erforderlich + + +---[Vorlage V1.3]--- + +* Das neue Objekt REPORT ermöglicht Zugriff auf "Rekrutierungskosten", + "Runde", "Partei" und "Personen" + +* Zu jeder Einheit wird nun der Bewache/Kampfstatus eingefügt + +* Bei Kapitänen wird nun das Schiff und die Ablegeküste eingefügt + + +---[Vorlage V1.3 beta 6]--- + +* Die Befehle #if, #ifregion und #ifunit unterstützen jetzt einen + else-Zweig + +* In Empiria-CRs werden lange Nachrichten umgebrochen, die jetzt von + Vorlage wieder zusammengefügt werden + +* Bugfix: Bugs in der Nachrichtenauswertung konnten zum Programmabbruch + führen und nicht alle Nachrichten wurden bei ihren Einheiten, + einsortiert (betrifft Handelsnachrichten, die ja zu zwei Einheiten + gehören) + + +---[Vorlage V1.3 beta 5]--- + +* Experimenteller EMPIRIA-Support wurde implementiert + +* Neue Option -pm ermöglicht es die Befehle der Einheiten mit + BESCHREIBE PRIVAT " befehl : befehl : ..." zu übergeben + +* Die Felder "belagert", "Belagerer", "Default", "privat", "wahrerTyp" + wurden implementiert + +* Bugfix: Ein Fehler in der Auswertung des UNIT-Objektes hat zu falschen + Ausdrücken geführt + + +---[Vorlage V1.3 beta 4]--- + +* Neue Option -n um die Nachrichten in die Vorlage einzufügen + +* Neues Attribut 'Strasse' vom REGION eingefügt + +* Das angewarnte CR-Kapitel "REGIONSBOTSCHAFEN" und die Tags "Strasse", + "Unterhalt" und "hp" implementiert + +* Bugfix: Bug im Gebäudeteil des Parsers gefixt + + +---[Vorlage V1.3 beta 3]--- + +* Untote werden für ECheck mit ",I" markiert + +* Die Transportkapazität wurde aus den eckigen Klammern ausgelagert + +* Bugfix: Leere COMMAND-Einträge im CR führten zum Absturz + + +---[Vorlage V1.3 beta 2]--- + +* Bugfix: Ein Fehler bei der Umlaut-Anpassung ergab, daß Wüsten in den + Minikarten als Wald angezeigt wurden + +* Es wurde das CR-Kapitel "SPRÜCHE" und die Tags "Prozent" und "Tarnung" + implementiert + + +---[Vorlage V1.3 beta]--- + +* Anpassung auf die Base36-Einheitennummern + +* Schachtelung der Metabefehle ist nun möglich + +* UNIT- und REGION-Objekt zum Zugriff auf Einheiten und Regionsdaten + +* Die Doku wurde auf HTML umgestellt + + +---[Vorlage V1.2]--- + +* Die verschiedenen Informationen in der Zugvorlage lassen sich nun + getrennt schalten + +* Es wurden Metabefehle eingeführt, mit denen sich Vorgänge + automatisieren lassen. Eine beigefügte Textdatei versucht die + Funktionalitäten zu erläutern. ;-) + + +---[Vorlage V1.1]--- + +* In den Regionskommentaren wird nun auch zusammenfassend die Zahl der + Personen und die Silbersumme angezeigt, um die Finanzen leichter + planen zu können + + +---[Vorlage V1.0]--- + +* In den Regionskommentaren wird nach der ECHECK-Zeile eine Minikarte + mit den Nachbarregionen eingefügt + +* Bei den Einheiten steht in je einer Kommentarzeile, welche Talente sie + haben und welche Objekte sie bei sich führen diff --git a/cmake/CommonSetup.cmake b/cmake/CommonSetup.cmake new file mode 100644 index 0000000..80ac2e1 --- /dev/null +++ b/cmake/CommonSetup.cmake @@ -0,0 +1,143 @@ +#################################################### +# +# FILE: +# CommonSetup.cmake +# +# BSD 3-Clause License +# -------------------- +# +# Copyright (c) 2015 Steffen Schümann, all rights reserved. +# +# 1. Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 2. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 3. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. Neither the name of +# the copyright holder nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +########################################################################### + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + FORCE) +endif(NOT CMAKE_BUILD_TYPE) + +find_package(Git REQUIRED) + +execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${GIT_EXECUTABLE} log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${GIT_EXECUTABLE} rev-list HEAD --count + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_NUM + OUTPUT_STRIP_TRAILING_WHITESPACE +) +math(EXPR BUILD_NUMBER_EMU 469+${GIT_COMMIT_NUM}) + +string(TOLOWER ${PROJECT_NAME} PROJECT_LOWERCASE_NAME) +configure_file(${PROJECT_SOURCE_DIR}/version.h.in ${CMAKE_BINARY_DIR}/${PROJECT_LOWERCASE_NAME}/version.h) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +# make sure c++14 is used +message("Configure build files to use C++14") +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(UNIX) + if(CMAKE_CXX_INCLUDE_WHAT_YOU_USE) + set(COMMON_WARNINGS "-Wno-format") + else() + set(COMMON_WARNINGS "-Wall -Wextra -Wno-unknown-warning-option -Wshadow -Wmissing-include-dirs -Wfloat-equal -Wpointer-arith -Wunreachable-code -Wno-non-virtual-dtor -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable -Wno-format-nonliteral -Wno-format -Wno-psabi -Werror") + endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(SYSTEM_LIBS dl) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_WARNINGS}") + #set(CMAKE_CXX_FLAGS "-static ${CMAKE_CXX_FLAGS} ${COMMON_WARNINGS}") + #set(CMAKE_C_FLAGS "-static ${CMAKE_C_FLAGS}") + elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + if(BUILD_32BIT) + add_definitions(-m32) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") + set(CMAKE_SHARED_LIBRARY_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS} -m32") + set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS} -m32") + endif() + set(SYSTEM_LIBS dl pthread) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # we are using Clang under Linux + set(CMAKE_CXX_FLAGS "-static ${CMAKE_CXX_FLAGS} ${COMMON_WARNINGS}") + set(CMAKE_C_FLAGS "-static ${CMAKE_C_FLAGS}") + list(APPEND SYSTEM_LIBS c++abi) + message(STATUS "Selected clang...") + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # we are using GCC under Linux + set(CMAKE_CXX_FLAGS "-static-libgcc -static-libstdc++ ${CMAKE_CXX_FLAGS} ${COMMON_WARNINGS}") + set(CMAKE_C_FLAGS "-static-libgcc ${CMAKE_C_FLAGS}") + + execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if(GCC_VERSION VERSION_LESS 4.7) + message(FATAL_ERROR "To compile with GCC, a version of 4.7 or newer is needed!") + endif() + else() + message(FATAL_ERROR "Sorry, I couldn't recognize the compiler, so I don't know how to configure C++11!") + endif() + endif() +endif(UNIX) +if(WIN32) + set(SYSTEM_LIBS "") +endif() + +if (MSVC) + if (PBEMTOOLS_MSVC_STATIC_RUNTIME) + # set all of our submodules to static runtime + set(PCRE_MSVC_STATIC_RUNTIME ON) + + # In case we are building static libraries, link also the runtime library statically + # so that MSVCR*.DLL is not required at runtime. + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + # This is achieved by replacing msvc option /MD with /MT and /MDd with /MTd + # https://gitlab.kitware.com/cmake/community/wikis/FAQ#how-can-i-build-my-msvc-application-with-a-static-runtime + foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MD") + endforeach(flag_var) + else() + set(PCRE_USE_MSVC_STATIC_RUNTIME OFF) + endif() +endif() + +#include_directories(${PROJECT_SOURCE_DIR}/include) + diff --git a/scripts/standard.vms b/scripts/standard.vms new file mode 100644 index 0000000..72ebbc2 --- /dev/null +++ b/scripts/standard.vms @@ -0,0 +1,274 @@ + + +;------------------------------------------------------------------------- +; CONSTS: Konstanten f�r die Typen als R�ckgabewerte von typeof() +;------------------------------------------------------------------------- +#const TYPE_NULL 0 +#const TYPE_ERROR 1 +#const TYPE_INT 2 +#const TYPE_FLOAT 3 +#const TYPE_STRING 4 +#const TYPE_ARRAY 6 +#const TYPE_DICT 7 + + +;------------------------------------------------------------------------- +; CONSTS: Konstanten f�r Dateizugriffe +;------------------------------------------------------------------------- +#const MODE_READ 0 +#const MODE_WRITE 1 +#const MODE_APPEND 2 + +#const STAT_OK 0 +#const STAT_EOF -1 +#const STAT_ERROR 1 + + +;------------------------------------------------------------------------- +; FUNC: max(,) +; ARGS: , Werte von denen das Maximum gesucht wird +; DESC: Diese Funktion gibt den gr��eren der beiden Werte zur�ck +;------------------------------------------------------------------------- +#func max $arg1 $arg2 +{ + #if $arg1<$arg2 + { + #return $arg2 + } + #else + { + #return $arg1 + } +} + +;------------------------------------------------------------------------- +; FUNC: min(,) +; ARGS: , Werte von denen das Minimum gesucht wird +; DESC: Diese Funktion gibt den kleineren der beiden Werte zur�ck +;------------------------------------------------------------------------- +#func min $arg1 $arg2 +{ + #if $arg1>$arg2 + { + #return $arg2 + } + #else + { + #return $arg1 + } +} + +;------------------------------------------------------------------------- +; Hier werden die Attribute zwischengespeichert, dabei gibt es +; f�r jede eigene Einheit eine Kopie der Privat-Beschreibung +;------------------------------------------------------------------------- +#dict $Attributes + +;------------------------------------------------------------------------- +; PROC: InitAttributes +; ARGS: - +; DESC: Hier werden die Privat-Beschreibungen f�r die Einheiten der Region +; in das Dictionary �bertragen, Aufruf zu Beginn von OnInit +;------------------------------------------------------------------------- +#proc InitAttributes +{ + #var $ri $ei + ; Regionsindex + $ri=0 + + ; Solange noch Regionen existieren + #while $ri +; SetAttribute +; ARGS: Einheitennummer, wird sie weggelassen wird die aktuelle +; Einheit verwendet +; Bezeichner des Attributes, case-insensitiv +; Zahl oder String zur Speicherung des Attributes +; DESC: Diese Prozedur erm�glicht das Setzen von Attributen +;------------------------------------------------------------------------- +#proc SetAttribute $Arg1 ... +{ + #var $re + ; Wurde eine Einheitennummer �bergeben? + #if ARG.SIZE==2 + { ; Nein + + ; regul�rer Ausdruck um das Attribut mit dem Format '$Name=Value:' + ; zu finden (mittels (?i) wird erreicht das die Namen nicht + ; case-sensitiv sind + $re='(?i)\\$'+$Arg1+'='+'[^:]*:' + + ; neue Privat-Beschreibung erzeugen, als Teil vor plus Attribut + ; plus Teil nach dem alten Attribut + $Attributes[unit.nummer]=before($Attributes[unit.nummer],$re)+'$'+$Arg1+'='+ARG[1]+':'+after($Attributes[unit.nummer],$re) + } + #else + { ; Jupp + + ; s.o. nur f�r Einheiten mit �bergebener Einheitennummer + $re='(?i)\\$'+ARG[1]+'='+'[^:]*:' + $Attributes[$Arg1]=before($Attributes[$Arg1],$re)+'$'+ARG[1]+'='+ARG[2]+':'+after($Attributes[$Arg1],$re) + } +} + +;------------------------------------------------------------------------- +; PROC: RemoveAttribute +; RemoveAttribute +; ARGS: Einheitennummer, wird sie weggelassen wird die aktuelle +; Einheit verwendet +; Bezeichner des Attributes, case-insensitiv +; DESC: Diese Prozedur erm�glicht das L�schen von Attributen +;------------------------------------------------------------------------- +#proc RemoveAttribute $Arg1 ... +{ + #var $re + ; Wurde eine Einheitennummer �bergeben? + #if ARG.SIZE==1 + { ; Nein + + ; regul�rer Ausdruck um das Attribut mit dem Format '$Name=Value:' + ; zu finden (mittels (?i) wird erreicht das die Namen nicht + ; case-sensitiv sind + $re='(?i)\\$'+$Arg1+'='+'[^:]*:' + + ; neue Privat-Beschreibung erzeugen, als Teil vor plus Teil nach + ; dem alten Attribut + $Attributes[unit.nummer]=before($Attributes[unit.nummer],$re)+after($Attributes[unit.nummer],$re) + } + #else + { ; Jupp + + ; s.o. nur f�r Einheiten mit �bergebener Einheitennummer + $re='(?i)\\$'+ARG[1]+'='+'[^:]*:' + $Attributes[$Arg1]=before($Attributes[$Arg1],$re)+after($Attributes[$Arg1],$re) + } +} + +;------------------------------------------------------------------------- +; FUNC: GetAttrStr(,) +; ARGS: Einheitennummer der abzufragenden Einheit +; Bezeichner des Attributes, case-insensitiv +; DESC: Diese Funktion gibt den Wert eines Attributes als String zur�ck +;------------------------------------------------------------------------- +#func GetAttrStr $ENr $Name +{ + ; In drei Schritten erst das gesamte Attribut, dann den Wert incl. '=' u. + ; ':' und schlie�lich den Wert solo herausl�sen + #return crop(crop(crop($Attributes[$ENr],'(?i)\\$'+$Name+'='+'[^:]*:'),'='+'[^:]*:'),'[^:=]*') +} + +;------------------------------------------------------------------------- +; FUNC: GetAttrInt(,) +; ARGS: Einheitennummer der abzufragenden Einheit +; Bezeichner des Attributes, case-insensitiv +; DESC: Diese Funktion gibt den Wert eines Attributes als Integer zur�ck +;------------------------------------------------------------------------- +#func GetAttrInt $ENr $Name +{ + ; Den String holen und in eine Zahl wandeln + #return antoi(GetAttrStr($ENr,$Name),10) +} + + + +#func EMR_int $Val +{ + #return $Val +} + +#func EMR_eq $Val1 $Val2 +{ + #return $Val1==$Val2 +} + +#func EMR_if $CC $Val1 $Val2 +{ + #if $CC=='0' + { + #return $Val2 + } + #else + { + #return $Val1 + } +} + +#func EMR_faction $PNr +{ + $PNr=itoan(antoi($PNr,10),36) + #return partei[$PNr].parteiname+' ('+$PNr+')' +} + +#func EMR_unit $ENr +{ + $ENr=itoan(antoi($ENr,10),36) + #return unit[$ENr].name+' ('+$ENr+')' +} + +#func EMR_building $BNr +{ + $BNr=itoan(antoi($BNr,10),36) + #return building[$BNr].name+' ('+$BNr+')' +} + +#func EMR_region $RegInfo +{ + #if $($RegInfo).z + { + #return $($RegInfo).name+' ('+$($RegInfo).x+','+$($RegInfo).y+','+$($RegInfo).z+')' + } + #else + { + #return $($RegInfo).name+' ('+$($RegInfo).x+','+$($RegInfo).y+')' + } +} + +#func EMR_resource $Resource $Wanted +{ + #return $Resource +} + +#func EMR_skill $Skill +{ + #return $Skill +} + diff --git a/version.h.in b/version.h.in new file mode 100644 index 0000000..dcb4bd2 --- /dev/null +++ b/version.h.in @@ -0,0 +1,45 @@ +//--------------------------------------------------------------------------------------- +// version.h +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2019, Steffen Schümann +// +// 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. +// +//--------------------------------------------------------------------------------------- + +#ifndef PBEMTOOLS_VERSION_H +#define PBEMTOOLS_VERSION_H + +#define PBEMTOOLS_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ +#define PBEMTOOLS_VERSION_MINOR @PROJECT_VERSION_MINOR@ +#define PBEMTOOLS_VERSION_PATCH @PROJECT_VERSION_PATCH@ +#define PBEMTOOLS_VERSION_TWEAK @PROJECT_VERSION_TWEAK@ +#if PBEMTOOLS_VERSION_TWEAK+0>0 +#define PBEMTOOLS_VERSION_STRING_SHORT "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@" +#define PBEMTOOLS_VERSION_STRING_LONG "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@-@GIT_COMMIT_HASH@" +#else +#define PBEMTOOLS_VERSION_STRING_SHORT "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@" +#define PBEMTOOLS_VERSION_STRING_LONG "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@-@GIT_COMMIT_HASH@" +#endif +#define PBEMTOOLS_SOURCE_REVISION "@GIT_COMMIT_HASH@" +#define PBEMTOOLS_SOURCE_BRANCH "@GIT_BRANCH@" +#define PBEMTOOLS_BUILD_NUMBER_EMU @BUILD_NUMBER_EMU@ + +#endif // PBEMTOOLS_VERSION_H