mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-06 06:32:56 +01:00
Merge pull request #12667 from mwnciau/json-refactor
Migrate JSON library to JSON for Modern C++
This commit is contained in:
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
name: Check code formatting
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: openrct2/openrct2-build:0.2.4-format
|
||||
image: openrct2/openrct2-build:0.3.0-format
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-code-formatting]
|
||||
container:
|
||||
image: openrct2/openrct2-build:0.2.4-mingw
|
||||
image: openrct2/openrct2-build:0.3.0-mingw
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-code-formatting]
|
||||
container:
|
||||
image: openrct2/openrct2-build:0.2.4-bionic
|
||||
image: openrct2/openrct2-build:0.3.0-bionic
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-code-formatting]
|
||||
container:
|
||||
image: openrct2/openrct2-build:0.2.4-bionic32
|
||||
image: openrct2/openrct2-build:0.3.0-bionic32
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
@@ -200,7 +200,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-code-formatting]
|
||||
container:
|
||||
image: openrct2/openrct2-build:0.2.4-bionic
|
||||
image: openrct2/openrct2-build:0.3.0-bionic
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
@@ -258,7 +258,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-code-formatting]
|
||||
container:
|
||||
image: openrct2/openrct2-build:0.2.4-bionic
|
||||
image: openrct2/openrct2-build:0.3.0-bionic
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
@@ -270,7 +270,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [check-code-formatting]
|
||||
container:
|
||||
image: openrct2/openrct2-build:0.2.4-android
|
||||
image: openrct2/openrct2-build:0.3.0-android
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
@@ -23,5 +23,3 @@ SET(CMAKE_FIND_ROOT_PATH ${TARGET_ENVIRONMENT})
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
# INCLUDE_DIRECTORIES(${ORCTLIBS_INCLUDE} ${JANSSON_INCLUDE})
|
||||
|
||||
@@ -154,9 +154,10 @@
|
||||
93F76F0520BFF77B00D4512C /* Paint.TileElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93F76EFD20BFF77A00D4512C /* Paint.TileElement.cpp */; };
|
||||
93F76F0620BFF77B00D4512C /* Paint.Entrance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93F76EFE20BFF77A00D4512C /* Paint.Entrance.cpp */; };
|
||||
93F9DA3820B46F9D00D1BE92 /* ShopItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CDCB0BC20A9902E00321367 /* ShopItem.cpp */; };
|
||||
93F9DA3920B46FB800D1BE92 /* ObjectJsonHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CE9AAAB1FDA7B14004093C6 /* ObjectJsonHelpers.cpp */; };
|
||||
93F9DA3A20B46FCA00D1BE92 /* SceneryObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A53EC205FD19F000F8EF5 /* SceneryObject.cpp */; };
|
||||
93F9DA3B20B4701100D1BE92 /* StdInOutConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C3B423720591513000C5BB7 /* StdInOutConsole.cpp */; };
|
||||
93FB271F24ED32B7008241C9 /* json.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 93FB271E24ED32B7008241C9 /* json.hpp */; };
|
||||
93FB272124ED3601008241C9 /* Cursors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93FB272024ED3601008241C9 /* Cursors.cpp */; };
|
||||
93FC08FF2418F3ED00CA3054 /* duktape.h in Headers */ = {isa = PBXBuildFile; fileRef = 93FC08FD2418F3ED00CA3054 /* duktape.h */; };
|
||||
93FC09002418F3ED00CA3054 /* duk_config.h in Headers */ = {isa = PBXBuildFile; fileRef = 93FC08FE2418F3ED00CA3054 /* duk_config.h */; };
|
||||
93FC09022418F3F500CA3054 /* libduktape.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 93FC09012418F3F500CA3054 /* libduktape.dylib */; };
|
||||
@@ -442,12 +443,10 @@
|
||||
D41B74731C2125E50080A7B9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D41B74721C2125E50080A7B9 /* Assets.xcassets */; };
|
||||
D43407E21D0E14CE00C2B3D4 /* shaders in Resources */ = {isa = PBXBuildFile; fileRef = D43407E11D0E14CE00C2B3D4 /* shaders */; };
|
||||
D45A38BC1CF3006400659A24 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B31CF3006400659A24 /* libcrypto.dylib */; };
|
||||
D45A38BE1CF3006400659A24 /* libjansson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B51CF3006400659A24 /* libjansson.dylib */; };
|
||||
D45A38C11CF3006400659A24 /* libSDL2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B81CF3006400659A24 /* libSDL2.dylib */; };
|
||||
D45A38C21CF3006400659A24 /* libspeexdsp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B91CF3006400659A24 /* libspeexdsp.dylib */; };
|
||||
D45A39591CF300AF00659A24 /* libcrypto.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B31CF3006400659A24 /* libcrypto.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
D45A395A1CF300AF00659A24 /* libfreetype.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B41CF3006400659A24 /* libfreetype.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
D45A395B1CF300AF00659A24 /* libjansson.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B51CF3006400659A24 /* libjansson.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
D45A395E1CF300AF00659A24 /* libSDL2.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B81CF3006400659A24 /* libSDL2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
D45A395F1CF300AF00659A24 /* libspeexdsp.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B91CF3006400659A24 /* libspeexdsp.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
D47304D51C4FF8250015C0EA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D47304D41C4FF8250015C0EA /* libz.tbd */; };
|
||||
@@ -558,13 +557,11 @@
|
||||
F7D774901EC66FB000BE6EBC /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D47304D41C4FF8250015C0EA /* libz.tbd */; };
|
||||
F7D774911EC66FBA00BE6EBC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B31CF3006400659A24 /* libcrypto.dylib */; };
|
||||
F7D774921EC66FBA00BE6EBC /* libfreetype.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B41CF3006400659A24 /* libfreetype.dylib */; };
|
||||
F7D774931EC66FBA00BE6EBC /* libjansson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B51CF3006400659A24 /* libjansson.dylib */; };
|
||||
F7D774941EC66FBA00BE6EBC /* libpng16.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D4A8B4B31DB41873007A2F29 /* libpng16.dylib */; };
|
||||
F7D774951EC66FBA00BE6EBC /* libspeexdsp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B91CF3006400659A24 /* libspeexdsp.dylib */; };
|
||||
F7D774961EC66FBA00BE6EBC /* libzip.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; };
|
||||
F7D774971EC6705F00BE6EBC /* libcrypto.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B31CF3006400659A24 /* libcrypto.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
F7D774981EC6705F00BE6EBC /* libfreetype.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B41CF3006400659A24 /* libfreetype.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
F7D774991EC6705F00BE6EBC /* libjansson.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B51CF3006400659A24 /* libjansson.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
F7D7749A1EC6705F00BE6EBC /* libpng16.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D4A8B4B31DB41873007A2F29 /* libpng16.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
F7D7749B1EC6705F00BE6EBC /* libspeexdsp.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B91CF3006400659A24 /* libspeexdsp.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
F7D7749C1EC6705F00BE6EBC /* libzip.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
@@ -618,7 +615,6 @@
|
||||
D45A39591CF300AF00659A24 /* libcrypto.dylib in Embed Frameworks */,
|
||||
93FC09032418F41700CA3054 /* libduktape.dylib in Embed Frameworks */,
|
||||
D45A395A1CF300AF00659A24 /* libfreetype.dylib in Embed Frameworks */,
|
||||
D45A395B1CF300AF00659A24 /* libjansson.dylib in Embed Frameworks */,
|
||||
D4A8B4B51DB4188D007A2F29 /* libpng16.dylib in Embed Frameworks */,
|
||||
D45A395E1CF300AF00659A24 /* libSDL2.dylib in Embed Frameworks */,
|
||||
933F32ED24183CBB008376CE /* libicudata.dylib in Embed Frameworks */,
|
||||
@@ -636,7 +632,6 @@
|
||||
F7D774A21EC6715C00BE6EBC /* libSDL2.dylib in Embed Frameworks */,
|
||||
F7D774971EC6705F00BE6EBC /* libcrypto.dylib in Embed Frameworks */,
|
||||
F7D774981EC6705F00BE6EBC /* libfreetype.dylib in Embed Frameworks */,
|
||||
F7D774991EC6705F00BE6EBC /* libjansson.dylib in Embed Frameworks */,
|
||||
F7D7749A1EC6705F00BE6EBC /* libpng16.dylib in Embed Frameworks */,
|
||||
F7D7749B1EC6705F00BE6EBC /* libspeexdsp.dylib in Embed Frameworks */,
|
||||
F7D7749C1EC6705F00BE6EBC /* libzip.dylib in Embed Frameworks */,
|
||||
@@ -962,8 +957,6 @@
|
||||
4CE462461FD1613D0001CD98 /* Platform.Linux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.Linux.cpp; sourceTree = "<group>"; };
|
||||
4CE462481FD1613D0001CD98 /* Platform.Posix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.Posix.cpp; sourceTree = "<group>"; };
|
||||
4CE462491FD1613D0001CD98 /* Platform.Win32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.Win32.cpp; sourceTree = "<group>"; };
|
||||
4CE9AAAB1FDA7B14004093C6 /* ObjectJsonHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectJsonHelpers.cpp; sourceTree = "<group>"; };
|
||||
4CE9AAAC1FDA7B14004093C6 /* ObjectJsonHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectJsonHelpers.h; sourceTree = "<group>"; };
|
||||
4CF67196206B7E720034ADDD /* object */ = {isa = PBXFileReference; lastKnownFileType = folder; name = object; path = data/object; sourceTree = "<group>"; };
|
||||
4CFE4E7B1F90A3F1005243C2 /* Peep.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Peep.cpp; sourceTree = "<group>"; };
|
||||
4CFE4E7C1F90A3F1005243C2 /* Peep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Peep.h; sourceTree = "<group>"; };
|
||||
@@ -1368,6 +1361,8 @@
|
||||
93F76EFC20BFF77A00D4512C /* Paint.Banner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Banner.cpp; sourceTree = "<group>"; };
|
||||
93F76EFD20BFF77A00D4512C /* Paint.TileElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.TileElement.cpp; sourceTree = "<group>"; };
|
||||
93F76EFE20BFF77A00D4512C /* Paint.Entrance.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Entrance.cpp; sourceTree = "<group>"; };
|
||||
93FB271E24ED32B7008241C9 /* json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = json.hpp; sourceTree = "<group>"; };
|
||||
93FB272024ED3601008241C9 /* Cursors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cursors.cpp; sourceTree = "<group>"; };
|
||||
93FC08FD2418F3ED00CA3054 /* duktape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = duktape.h; sourceTree = "<group>"; };
|
||||
93FC08FE2418F3ED00CA3054 /* duk_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = duk_config.h; sourceTree = "<group>"; };
|
||||
93FC09012418F3F500CA3054 /* libduktape.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libduktape.dylib; sourceTree = "<group>"; };
|
||||
@@ -1478,11 +1473,8 @@
|
||||
D43BAB921F8C2B2B00A9E362 /* OpenGLAPIProc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenGLAPIProc.h; sourceTree = "<group>"; };
|
||||
D45A38B31CF3006400659A24 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libcrypto.dylib; sourceTree = "<group>"; };
|
||||
D45A38B41CF3006400659A24 /* libfreetype.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libfreetype.dylib; sourceTree = "<group>"; };
|
||||
D45A38B51CF3006400659A24 /* libjansson.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libjansson.dylib; sourceTree = "<group>"; };
|
||||
D45A38B81CF3006400659A24 /* libSDL2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libSDL2.dylib; sourceTree = "<group>"; };
|
||||
D45A38B91CF3006400659A24 /* libspeexdsp.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libspeexdsp.dylib; sourceTree = "<group>"; };
|
||||
D45A38C41CF3007A00659A24 /* jansson_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jansson_config.h; sourceTree = "<group>"; };
|
||||
D45A38C51CF3007A00659A24 /* jansson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jansson.h; sourceTree = "<group>"; };
|
||||
D45A38C71CF3007A00659A24 /* png.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png.h; sourceTree = "<group>"; };
|
||||
D45A38C81CF3007A00659A24 /* pngconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pngconf.h; sourceTree = "<group>"; };
|
||||
D45A38C91CF3007A00659A24 /* pnglibconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pnglibconf.h; sourceTree = "<group>"; };
|
||||
@@ -1883,7 +1875,6 @@
|
||||
D45A38BC1CF3006400659A24 /* libcrypto.dylib in Frameworks */,
|
||||
93FC09022418F3F500CA3054 /* libduktape.dylib in Frameworks */,
|
||||
933F32EC24183CBB008376CE /* libicudata.dylib in Frameworks */,
|
||||
D45A38BE1CF3006400659A24 /* libjansson.dylib in Frameworks */,
|
||||
933F32EA24183CBB008376CE /* libicuuc.dylib in Frameworks */,
|
||||
D4A8B4B41DB41873007A2F29 /* libpng16.dylib in Frameworks */,
|
||||
D45A38C11CF3006400659A24 /* libSDL2.dylib in Frameworks */,
|
||||
@@ -1905,7 +1896,6 @@
|
||||
F7D7748E1EC66FA000BE6EBC /* libiconv.tbd in Frameworks */,
|
||||
F7D774911EC66FBA00BE6EBC /* libcrypto.dylib in Frameworks */,
|
||||
F7D774921EC66FBA00BE6EBC /* libfreetype.dylib in Frameworks */,
|
||||
F7D774931EC66FBA00BE6EBC /* libjansson.dylib in Frameworks */,
|
||||
F7D774941EC66FBA00BE6EBC /* libpng16.dylib in Frameworks */,
|
||||
F7D774951EC66FBA00BE6EBC /* libspeexdsp.dylib in Frameworks */,
|
||||
F7D774961EC66FBA00BE6EBC /* libzip.dylib in Frameworks */,
|
||||
@@ -2263,6 +2253,15 @@
|
||||
path = scripting;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
93FB271D24ED32B7008241C9 /* nlohmann */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
93FB271E24ED32B7008241C9 /* json.hpp */,
|
||||
);
|
||||
name = nlohmann;
|
||||
path = libxc/include/nlohmann;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
C6352B871F477032006CCEE3 /* actions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2632,14 +2631,13 @@
|
||||
children = (
|
||||
9350B4F620B46E0900897BC5 /* freetype2 */,
|
||||
D45A38C61CF3007A00659A24 /* libpng16 */,
|
||||
93FB271D24ED32B7008241C9 /* nlohmann */,
|
||||
D45A38CA1CF3007A00659A24 /* openssl */,
|
||||
D45A39161CF3007A00659A24 /* SDL2 */,
|
||||
D45A39521CF3007A00659A24 /* speex */,
|
||||
9350B44320B46E0800897BC5 /* unicode */,
|
||||
93FC08FE2418F3ED00CA3054 /* duk_config.h */,
|
||||
93FC08FD2418F3ED00CA3054 /* duktape.h */,
|
||||
D45A38C41CF3007A00659A24 /* jansson_config.h */,
|
||||
D45A38C51CF3007A00659A24 /* jansson.h */,
|
||||
C6E96E331E0408A80076A04F /* zip.h */,
|
||||
C6E96E341E0408A80076A04F /* zipconf.h */,
|
||||
);
|
||||
@@ -2654,7 +2652,6 @@
|
||||
D45A38B41CF3006400659A24 /* libfreetype.dylib */,
|
||||
933F32E924183CBB008376CE /* libicudata.dylib */,
|
||||
933F32E824183CBB008376CE /* libicuuc.dylib */,
|
||||
D45A38B51CF3006400659A24 /* libjansson.dylib */,
|
||||
D4A8B4B31DB41873007A2F29 /* libpng16.dylib */,
|
||||
D45A38B81CF3006400659A24 /* libSDL2.dylib */,
|
||||
D45A38B91CF3006400659A24 /* libspeexdsp.dylib */,
|
||||
@@ -2892,6 +2889,7 @@
|
||||
4C7B53DE200143C200A52E21 /* Chat.h */,
|
||||
4C7B53DF200143C200A52E21 /* Colour.cpp */,
|
||||
4C7B53E0200143C200A52E21 /* Colour.h */,
|
||||
93FB272024ED3601008241C9 /* Cursors.cpp */,
|
||||
4C7B53E3200143C200A52E21 /* Cursors.h */,
|
||||
4C7B53E4200143C200A52E21 /* FontFamilies.cpp */,
|
||||
4C7B53E5200143C200A52E21 /* FontFamilies.h */,
|
||||
@@ -3014,8 +3012,6 @@
|
||||
F76C841F1EC4E7CC00FA49E2 /* Object.h */,
|
||||
F76C84201EC4E7CC00FA49E2 /* ObjectFactory.cpp */,
|
||||
F76C84211EC4E7CC00FA49E2 /* ObjectFactory.h */,
|
||||
4CE9AAAB1FDA7B14004093C6 /* ObjectJsonHelpers.cpp */,
|
||||
4CE9AAAC1FDA7B14004093C6 /* ObjectJsonHelpers.h */,
|
||||
4C7B53A21FFC15ED00A52E21 /* ObjectLimits.h */,
|
||||
4C7B53A31FFC180400A52E21 /* ObjectList.cpp */,
|
||||
4C7B53A41FFC180400A52E21 /* ObjectList.h */,
|
||||
@@ -3669,6 +3665,7 @@
|
||||
939A359F20C12FDE00630B3F /* Paint.Surface.h in Headers */,
|
||||
C67B28192002D7F200109C93 /* Window_internal.h in Headers */,
|
||||
93DFD05024521C1A001FCBAF /* ScPark.hpp in Headers */,
|
||||
93FB271F24ED32B7008241C9 /* json.hpp in Headers */,
|
||||
93DFD02E24521BA0001FCBAF /* FileWatcher.h in Headers */,
|
||||
2ADE2F28224418B2002598AF /* DataSerialiserTag.h in Headers */,
|
||||
93DFD04C24521C1A001FCBAF /* ScDisposable.hpp in Headers */,
|
||||
@@ -3882,7 +3879,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "version=\"23\"\nzipname=\"openrct2-libs-v23-x64-macos-dylibs.zip\"\nliburl=\"https://github.com/OpenRCT2/Dependencies/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/libxc\" || ! -e \"${SRCROOT}/libversion\" || $(head -n 1 \"${SRCROOT}/libversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/libxc\" ]]; then rm -r \"${SRCROOT}/libxc\"; fi\nmkdir \"${SRCROOT}/libxc\"\n\ncurl -L -o \"${SRCROOT}/libxc/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/libxc\" \"${SRCROOT}/libxc/$zipname\"\nrm \"${SRCROOT}/libxc/$zipname\"\n\necho $version > \"${SRCROOT}/libversion\"\nfi\n";
|
||||
shellScript = "version=\"25\"\nzipname=\"openrct2-libs-v25-x64-macos-dylibs.zip\"\nliburl=\"https://github.com/OpenRCT2/Dependencies/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/libxc\" || ! -e \"${SRCROOT}/libversion\" || $(head -n 1 \"${SRCROOT}/libversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/libxc\" ]]; then rm -r \"${SRCROOT}/libxc\"; fi\nmkdir \"${SRCROOT}/libxc\"\n\ncurl -L -o \"${SRCROOT}/libxc/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/libxc\" \"${SRCROOT}/libxc/$zipname\"\nrm \"${SRCROOT}/libxc/$zipname\"\n\necho $version > \"${SRCROOT}/libversion\"\nfi\n";
|
||||
};
|
||||
D42C09D21C254F4E00309751 /* Build g2.dat */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -4309,6 +4306,7 @@
|
||||
C688788E20289AE70084B384 /* SSE41Drawing.cpp in Sources */,
|
||||
F76C866C1EC4E88400FA49E2 /* Object.cpp in Sources */,
|
||||
F76C866E1EC4E88400FA49E2 /* ObjectFactory.cpp in Sources */,
|
||||
93FB272124ED3601008241C9 /* Cursors.cpp in Sources */,
|
||||
C68878A220289B200084B384 /* RealNames.cpp in Sources */,
|
||||
C688787120289A780084B384 /* Ride.cpp in Sources */,
|
||||
F76C86701EC4E88400FA49E2 /* ObjectManager.cpp in Sources */,
|
||||
@@ -4333,7 +4331,6 @@
|
||||
C68878CD20289B9B0084B384 /* DefaultObjects.cpp in Sources */,
|
||||
939A359A20C12FC800630B3F /* Paint.Litter.cpp in Sources */,
|
||||
C688788220289ADE0084B384 /* Rect.cpp in Sources */,
|
||||
93F9DA3920B46FB800D1BE92 /* ObjectJsonHelpers.cpp in Sources */,
|
||||
C688787320289A780084B384 /* RideRatings.cpp in Sources */,
|
||||
C688790D20289B9B0084B384 /* Circus.cpp in Sources */,
|
||||
C688788F20289B140084B384 /* Chat.cpp in Sources */,
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -4,7 +4,7 @@ Section: misc
|
||||
Priority: optional
|
||||
Standards-Version: 3.9.2
|
||||
Multi-Arch: same
|
||||
Build-Depends: debhelper (>= 9), cmake (>= 3.8), duktape-dev, libsdl2-dev, g++ (>= 4:7), pkg-config, libjansson4 (>= 2.5), libjansson-dev (>= 2.3), libspeex-dev, libspeexdsp-dev, libcurl4-openssl-dev, libcrypto++-dev, libfontconfig1-dev, libfreetype6-dev, libpng-dev, libssl-dev, libzip-dev (>= 1.0.0), libicu-dev (>= 59.0)
|
||||
Build-Depends: debhelper (>= 9), cmake (>= 3.8), duktape-dev, libsdl2-dev, g++ (>= 4:7), pkg-config, nlohmann-json3-dev (>= 3.6.0), libspeex-dev, libspeexdsp-dev, libcurl4-openssl-dev, libcrypto++-dev, libfontconfig1-dev, libfreetype6-dev, libpng-dev, libssl-dev, libzip-dev (>= 1.0.0), libicu-dev (>= 59.0)
|
||||
|
||||
Package: openrct2
|
||||
Architecture: any
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Last updated: 2020-08-15
|
||||
Last updated: 2020-09-03
|
||||
Release version: 0.3.0
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -55,7 +55,7 @@ following information in your bug report:
|
||||
* Bug details, including instructions how to reproduce it
|
||||
* Platform (Windows, Linux, FreeBSD, ...) and compiler (including version) if
|
||||
you compiled OpenRCT2 yourself.
|
||||
* The processor architecture of your OS (32 bits Windows, 64 bits Windows,
|
||||
* The processor architecture of your OS (x86 Windows, x86-64 Windows,
|
||||
Android on an ARM, Linux on a PowerPC, ...)
|
||||
* The language and culture your operating system is using.
|
||||
* Attach a saved game *and* a screenshot if possible
|
||||
@@ -66,8 +66,8 @@ following information in your bug report:
|
||||
|
||||
3.0) Supported platforms
|
||||
---- -------------------
|
||||
OpenRCT2 is currently supported on Windows 7 and above, many distributions of
|
||||
Linux, macOS 10.9 or higher, Android, FreeBSD and OpenBSD. OpenRCT2 will only work on
|
||||
OpenRCT2 is currently supported on Windows Vista and above, many distributions of
|
||||
Linux, macOS 10.13 or higher, Android, FreeBSD and OpenBSD. OpenRCT2 will only work on
|
||||
little-endian architectures.
|
||||
Further instructions can be found on GitHub.
|
||||
|
||||
@@ -143,12 +143,12 @@ to all files in this distribution, except as noted below.
|
||||
|
||||
dukglue | MIT licence.
|
||||
duktape | MIT licence.
|
||||
Jansson | MIT licence.
|
||||
libcURL | MIT (or Modified BSD-style) licence.
|
||||
libicu | Unicode licence.
|
||||
libspeex | BSD-style licence.
|
||||
libpng | libpng licence.
|
||||
libspeex | BSD-style licence.
|
||||
libzip | BSD 3 clause licence.
|
||||
nlohmann-json | MIT licence.
|
||||
OpenSSL | OpenSSL Licence
|
||||
SDL2 | zlib licence.
|
||||
zlib | zlib licence.
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
|
||||
<AdditionalDependencies>benchmarkd.lib;libbreakpadd.lib;libbreakpad_clientd.lib;bz2d.lib;discord-rpc.lib;duktape.lib;freetyped.lib;jansson_d.lib;libpng16d.lib;libspeexdsp.lib;SDL2d.lib;zip.lib;zlibd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>benchmarkd.lib;brotlicommon-static.lib;brotlidec-static.lib;brotlienc-static.lib;libbreakpadd.lib;libbreakpad_clientd.lib;bz2d.lib;discord-rpc.lib;duktape.lib;freetyped.lib;libpng16d.lib;libspeexdsp.lib;SDL2d.lib;zip.lib;zlibd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
@@ -94,7 +94,7 @@
|
||||
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>benchmark.lib;libbreakpad.lib;libbreakpad_client.lib;bz2.lib;discord-rpc.lib;duktape.lib;freetype.lib;jansson.lib;libpng16.lib;libspeexdsp.lib;SDL2.lib;zip.lib;zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>benchmark.lib;brotlicommon-static.lib;brotlidec-static.lib;brotlienc-static.lib;libbreakpad.lib;libbreakpad_client.lib;bz2.lib;discord-rpc.lib;duktape.lib;freetype.lib;libpng16.lib;libspeexdsp.lib;SDL2.lib;zip.lib;zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
<!-- 3rd party libraries / dependencies -->
|
||||
<PropertyGroup>
|
||||
<DependenciesCheckFile>$(RootDir).dependencies</DependenciesCheckFile>
|
||||
<LibsUrl Condition="'$(Platform)'=='Win32'">https://github.com/OpenRCT2/Dependencies/releases/download/v24/openrct2-libs-v21-x86-windows-static.zip</LibsUrl>
|
||||
<LibsSha1 Condition="'$(Platform)'=='Win32'">21eef7db74fd1c886f3a1ef3f7989721e42a726b</LibsSha1>
|
||||
<LibsUrl Condition="'$(Platform)'=='x64'">https://github.com/OpenRCT2/Dependencies/releases/download/v24/openrct2-libs-v21-x64-windows-static.zip</LibsUrl>
|
||||
<LibsSha1 Condition="'$(Platform)'=='x64'">6367d76f6b95859f8b45cbf03b222b8b5200d8b5</LibsSha1>
|
||||
<LibsUrl Condition="'$(Platform)'=='Win32'">https://github.com/OpenRCT2/Dependencies/releases/download/v25/openrct2-libs-v25-x86-windows-static.zip</LibsUrl>
|
||||
<LibsSha1 Condition="'$(Platform)'=='Win32'">c8c1a3052721c7dedde8c3cab73ed09b793a9b8e</LibsSha1>
|
||||
<LibsUrl Condition="'$(Platform)'=='x64'">https://github.com/OpenRCT2/Dependencies/releases/download/v25/openrct2-libs-v25-x64-windows-static.zip</LibsUrl>
|
||||
<LibsSha1 Condition="'$(Platform)'=='x64'">5384dffd21b4e87c36bb91c6882e277023285612</LibsSha1>
|
||||
<GtestVersion>2fe3bd994b3189899d93f1d5a881e725e046fdc2</GtestVersion>
|
||||
<GtestUrl>https://github.com/google/googletest/archive/$(GtestVersion).zip</GtestUrl>
|
||||
<GtestSha1>058b9df80244c03f1633cb06e9f70471a29ebb8e</GtestSha1>
|
||||
|
||||
@@ -109,7 +109,7 @@ The program can also be built as a command line program using CMake. This type o
|
||||
- libpng (>= 1.2)
|
||||
- speexdsp (only for UI client)
|
||||
- curl (only if building with http support)
|
||||
- jansson (>= 2.5)
|
||||
- nlohmann-json (>= 3.6.0)
|
||||
- openssl (>= 1.0; only if building with multiplayer support)
|
||||
- icu (>= 59.0)
|
||||
- zlib
|
||||
@@ -148,7 +148,7 @@ The recommended way of building OpenRCT2 for macOS is with Xcode. The Xcode buil
|
||||
#### CMake:
|
||||
A command line version of OpenRCT2 can be built using CMake. This type of build requires you to provide the dependencies yourself. The supported method of doing this is with [Homebrew](http://brew.sh). Once you have Homebrew installed, you can download all the required libraries with this command:
|
||||
```
|
||||
brew install cmake duktape freetype icu4c jansson libpng libzip openssl pkg-config sdl2 speexdsp
|
||||
brew install cmake duktape freetype icu4c libpng libzip nlohmann-json openssl pkg-config sdl2 speexdsp
|
||||
```
|
||||
|
||||
Once you have the dependencies installed, you can build the project using CMake using the following commands:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
"""A wrapper script around clang-format, suitable for linting multiple files
|
||||
and to use for continuous integration.
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ ExternalProject_Add(libs
|
||||
|
||||
BUILD_BYPRODUCTS
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}freetype${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}jansson${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}png16${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}SDL2-2.0${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2main${CMAKE_STATIC_LIBRARY_SUFFIX}
|
||||
@@ -58,7 +57,6 @@ add_custom_command(TARGET libs POST_BUILD
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/*.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libcrypto.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libfreetype.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libjansson.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libpng16.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libSDL2-2.0.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libspeexdsp.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
@@ -71,12 +69,6 @@ set_target_properties(freetype PROPERTIES IMPORTED_LOCATION
|
||||
)
|
||||
add_dependencies(freetype libs)
|
||||
|
||||
add_library(jansson SHARED IMPORTED)
|
||||
set_target_properties(jansson PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}jansson${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(jansson libs)
|
||||
|
||||
add_library(png SHARED IMPORTED)
|
||||
set_target_properties(png PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}png16${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
@@ -175,7 +167,7 @@ file(GLOB_RECURSE OPENRCT2_CLI_SOURCES
|
||||
"${ORCT2_ROOT}/src/openrct2-cli/*.hpp")
|
||||
|
||||
add_library(openrct2 SHARED ${LIBOPENRCT2_SOURCES})
|
||||
target_link_libraries(openrct2 android stdc++ log dl z SDL2 png jansson icu icuuc icudata ssl crypto)
|
||||
target_link_libraries(openrct2 android stdc++ log dl z SDL2 png icu icuuc icudata ssl crypto)
|
||||
|
||||
add_library(openrct2-ui SHARED ${OPENRCT2_GUI_SOURCES})
|
||||
target_link_libraries(openrct2-ui openrct2 android stdc++ GLESv1_CM GLESv2 SDL2main speexdsp)
|
||||
@@ -187,3 +179,8 @@ target_include_directories(openrct2 SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty
|
||||
target_include_directories(openrct2-ui PRIVATE "${ORCT2_ROOT}/src")
|
||||
target_include_directories(openrct2-ui SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty")
|
||||
target_include_directories(openrct2-cli PRIVATE "${ORCT2_ROOT}/src")
|
||||
|
||||
# Header-only nlohmann library is installed in CI images at /nlohmann.
|
||||
# To be tiny bit more specific, use '/nlohmann/../' instead of just '/'
|
||||
target_include_directories(openrct2 PRIVATE "/nlohmann/../")
|
||||
target_include_directories(openrct2-ui PRIVATE "/nlohmann/../")
|
||||
|
||||
@@ -15,7 +15,6 @@ public class GameActivity extends SDLActivity {
|
||||
return new String[]{
|
||||
"c++_shared",
|
||||
"speexdsp",
|
||||
"jansson",
|
||||
"png16",
|
||||
"SDL2-2.0",
|
||||
|
||||
|
||||
@@ -9,11 +9,9 @@ option(DISABLE_OPENGL "Disable OpenGL support.")
|
||||
|
||||
# Third party libraries
|
||||
if (MSVC)
|
||||
find_package(jansson REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_library(SPEEX_LDFLAGS libspeexdsp)
|
||||
else ()
|
||||
PKG_CHECK_MODULES(JANSSON REQUIRED jansson>=2.5)
|
||||
PKG_CHECK_MODULES(SDL2 REQUIRED sdl2)
|
||||
PKG_CHECK_MODULES(SPEEX REQUIRED speexdsp)
|
||||
endif ()
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "Window.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <jansson.h>
|
||||
#include <memory>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/PlatformEnvironment.h>
|
||||
@@ -53,8 +52,11 @@ struct UIThemeWindowEntry
|
||||
rct_windowclass WindowClass;
|
||||
WindowTheme Theme;
|
||||
|
||||
json_t* ToJson() const;
|
||||
static UIThemeWindowEntry FromJson(const WindowThemeDesc* wtDesc, const json_t* json);
|
||||
json_t ToJson() const;
|
||||
/**
|
||||
* @note json is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
static UIThemeWindowEntry FromJson(const WindowThemeDesc* wtDesc, json_t& json);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -76,10 +78,13 @@ public:
|
||||
void SetEntry(const UIThemeWindowEntry* entry);
|
||||
void RemoveEntry(rct_windowclass windowClass);
|
||||
|
||||
json_t* ToJson() const;
|
||||
json_t ToJson() const;
|
||||
bool WriteToFile(const std::string& path) const;
|
||||
|
||||
static UITheme* FromJson(const json_t* json);
|
||||
/**
|
||||
* @note json is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
static UITheme* FromJson(json_t& json);
|
||||
static UITheme* FromFile(const std::string& path);
|
||||
static UITheme CreatePredefined(const std::string& name, const UIThemeWindowEntry* entries, uint8_t flags);
|
||||
};
|
||||
@@ -266,7 +271,7 @@ static void ThrowThemeLoadException()
|
||||
|
||||
#pragma region UIThemeEntry
|
||||
|
||||
json_t* UIThemeWindowEntry::ToJson() const
|
||||
json_t UIThemeWindowEntry::ToJson() const
|
||||
{
|
||||
const WindowThemeDesc* wtDesc = GetWindowThemeDescriptor(WindowClass);
|
||||
if (wtDesc == nullptr)
|
||||
@@ -274,37 +279,41 @@ json_t* UIThemeWindowEntry::ToJson() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
json_t* jsonColours = json_array();
|
||||
json_t jsonColours = json_t::array();
|
||||
for (uint8_t i = 0; i < wtDesc->NumColours; i++)
|
||||
{
|
||||
colour_t colour = Theme.Colours[i];
|
||||
json_array_append_new(jsonColours, json_integer(colour));
|
||||
jsonColours.push_back(colour);
|
||||
}
|
||||
|
||||
json_t* jsonEntry = json_object();
|
||||
json_object_set_new(jsonEntry, "colours", jsonColours);
|
||||
json_t jsonEntry = {
|
||||
{ "colours", jsonColours },
|
||||
};
|
||||
|
||||
return jsonEntry;
|
||||
}
|
||||
|
||||
UIThemeWindowEntry UIThemeWindowEntry::FromJson(const WindowThemeDesc* wtDesc, const json_t* json)
|
||||
UIThemeWindowEntry UIThemeWindowEntry::FromJson(const WindowThemeDesc* wtDesc, json_t& jsonData)
|
||||
{
|
||||
json_t* jsonColours = json_object_get(json, "colours");
|
||||
if (jsonColours == nullptr)
|
||||
Guard::Assert(jsonData.is_object(), "UIThemeWindowEntry::FromJson expects parameter jsonData to be object");
|
||||
|
||||
auto jsonColours = Json::AsArray(jsonData["colours"]);
|
||||
|
||||
if (jsonColours.empty())
|
||||
{
|
||||
ThrowThemeLoadException();
|
||||
}
|
||||
|
||||
uint8_t numColours = static_cast<uint8_t>(json_array_size(jsonColours));
|
||||
numColours = std::min(numColours, wtDesc->NumColours);
|
||||
|
||||
UIThemeWindowEntry result{};
|
||||
result.WindowClass = wtDesc->WindowClass;
|
||||
result.Theme = wtDesc->DefaultTheme;
|
||||
|
||||
for (uint8_t i = 0; i < numColours; i++)
|
||||
// result.Theme.Colours only has 6 values
|
||||
size_t colourCount = std::min(jsonColours.size(), static_cast<size_t>(wtDesc->NumColours));
|
||||
|
||||
for (size_t i = 0; i < colourCount; i++)
|
||||
{
|
||||
result.Theme.Colours[i] = static_cast<colour_t>(json_integer_value(json_array_get(jsonColours, i)));
|
||||
result.Theme.Colours[i] = Json::GetNumber<colour_t>(jsonColours[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -355,10 +364,10 @@ void UITheme::RemoveEntry(rct_windowclass windowClass)
|
||||
}
|
||||
}
|
||||
|
||||
json_t* UITheme::ToJson() const
|
||||
json_t UITheme::ToJson() const
|
||||
{
|
||||
// Create entries
|
||||
json_t* jsonEntries = json_object();
|
||||
json_t jsonEntries;
|
||||
for (const UIThemeWindowEntry& entry : Entries)
|
||||
{
|
||||
const WindowThemeDesc* wtDesc = GetWindowThemeDescriptor(entry.WindowClass);
|
||||
@@ -366,30 +375,29 @@ json_t* UITheme::ToJson() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
json_object_set_new(jsonEntries, wtDesc->WindowClassSZ, entry.ToJson());
|
||||
jsonEntries[wtDesc->WindowClassSZ] = entry.ToJson();
|
||||
}
|
||||
|
||||
// Create theme object
|
||||
json_t* jsonTheme = json_object();
|
||||
json_object_set_new(jsonTheme, "name", json_string(Name.c_str()));
|
||||
json_object_set_new(jsonTheme, "entries", jsonEntries);
|
||||
|
||||
json_object_set_new(jsonTheme, "useLightsRide", json_boolean(Flags & UITHEME_FLAG_USE_LIGHTS_RIDE));
|
||||
json_object_set_new(jsonTheme, "useLightsPark", json_boolean(Flags & UITHEME_FLAG_USE_LIGHTS_PARK));
|
||||
json_object_set_new(
|
||||
jsonTheme, "useAltScenarioSelectFont", json_boolean(Flags & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT));
|
||||
json_object_set_new(jsonTheme, "useFullBottomToolbar", json_boolean(Flags & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR));
|
||||
json_t jsonTheme = {
|
||||
{ "name", Name },
|
||||
{ "entries", jsonEntries },
|
||||
{ "useLightsRide", (Flags & UITHEME_FLAG_USE_LIGHTS_RIDE) != 0 },
|
||||
{ "useLightsPark", (Flags & UITHEME_FLAG_USE_LIGHTS_PARK) != 0 },
|
||||
{ "useAltScenarioSelectFont", (Flags & UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT) != 0 },
|
||||
{ "useFullBottomToolbar", (Flags & UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR) != 0 },
|
||||
};
|
||||
|
||||
return jsonTheme;
|
||||
}
|
||||
|
||||
bool UITheme::WriteToFile(const std::string& path) const
|
||||
{
|
||||
json_t* jsonTheme = ToJson();
|
||||
auto jsonTheme = ToJson();
|
||||
bool result;
|
||||
try
|
||||
{
|
||||
Json::WriteToFile(path.c_str(), jsonTheme, JSON_INDENT(4) | JSON_PRESERVE_ORDER);
|
||||
Json::WriteToFile(path.c_str(), jsonTheme);
|
||||
result = true;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
@@ -398,52 +406,51 @@ bool UITheme::WriteToFile(const std::string& path) const
|
||||
result = false;
|
||||
}
|
||||
|
||||
json_decref(jsonTheme);
|
||||
return result;
|
||||
}
|
||||
|
||||
UITheme* UITheme::FromJson(const json_t* json)
|
||||
UITheme* UITheme::FromJson(json_t& jsonObj)
|
||||
{
|
||||
const char* themeName = json_string_value(json_object_get(json, "name"));
|
||||
if (themeName == nullptr)
|
||||
Guard::Assert(jsonObj.is_object(), "UITheme::FromJson expects parameter jsonObj to be object");
|
||||
|
||||
const std::string themeName = Json::GetString(jsonObj["name"]);
|
||||
if (themeName.empty())
|
||||
{
|
||||
ThrowThemeLoadException();
|
||||
}
|
||||
|
||||
json_t* jsonEntries = json_object_get(json, "entries");
|
||||
json_t jsonEntries = jsonObj["entries"];
|
||||
|
||||
UITheme* result = nullptr;
|
||||
try
|
||||
{
|
||||
result = new UITheme(themeName);
|
||||
|
||||
if (json_is_true(json_object_get(json, "useLightsRide")))
|
||||
{
|
||||
result->Flags |= UITHEME_FLAG_USE_LIGHTS_RIDE;
|
||||
}
|
||||
if (json_is_true(json_object_get(json, "useLightsPark")))
|
||||
{
|
||||
result->Flags |= UITHEME_FLAG_USE_LIGHTS_PARK;
|
||||
}
|
||||
if (json_is_true(json_object_get(json, "useAltScenarioSelectFont")))
|
||||
{
|
||||
result->Flags |= UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT;
|
||||
}
|
||||
if (json_is_true(json_object_get(json, "useFullBottomToolbar")))
|
||||
{
|
||||
result->Flags |= UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR;
|
||||
}
|
||||
result->Flags = Json::GetFlags<uint8_t>(
|
||||
jsonObj,
|
||||
{
|
||||
{ "useLightsRide", UITHEME_FLAG_USE_LIGHTS_RIDE },
|
||||
{ "useLightsPark", UITHEME_FLAG_USE_LIGHTS_PARK },
|
||||
{ "useAltScenarioSelectFont", UITHEME_FLAG_USE_ALTERNATIVE_SCENARIO_SELECT_FONT },
|
||||
{ "useFullBottomToolbar", UITHEME_FLAG_USE_FULL_BOTTOM_TOOLBAR },
|
||||
});
|
||||
|
||||
const char* jkey;
|
||||
json_t* jvalue;
|
||||
json_object_foreach(jsonEntries, jkey, jvalue)
|
||||
if (jsonEntries.is_object())
|
||||
{
|
||||
const WindowThemeDesc* wtDesc = GetWindowThemeDescriptor(jkey);
|
||||
if (wtDesc == nullptr)
|
||||
continue;
|
||||
for (auto& [jsonKey, jsonValue] : jsonEntries.items())
|
||||
{
|
||||
if (jsonValue.is_object())
|
||||
{
|
||||
const WindowThemeDesc* wtDesc = GetWindowThemeDescriptor(jsonKey.data());
|
||||
if (wtDesc == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UIThemeWindowEntry entry = UIThemeWindowEntry::FromJson(wtDesc, jvalue);
|
||||
result->SetEntry(&entry);
|
||||
UIThemeWindowEntry entry = UIThemeWindowEntry::FromJson(wtDesc, jsonValue);
|
||||
result->SetEntry(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -457,8 +464,8 @@ UITheme* UITheme::FromJson(const json_t* json)
|
||||
|
||||
UITheme* UITheme::FromFile(const std::string& path)
|
||||
{
|
||||
json_t* json = nullptr;
|
||||
UITheme* result = nullptr;
|
||||
json_t json;
|
||||
try
|
||||
{
|
||||
json = Json::ReadFromFile(path.c_str());
|
||||
@@ -469,8 +476,6 @@ UITheme* UITheme::FromFile(const std::string& path)
|
||||
log_error("Unable to read theme: %s", path.c_str());
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
json_decref(json);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -211,18 +211,17 @@ private:
|
||||
if (response.status == Http::Status::OK)
|
||||
{
|
||||
auto jresponse = Json::FromString(response.body);
|
||||
if (jresponse != nullptr)
|
||||
if (jresponse.is_object())
|
||||
{
|
||||
auto objName = json_string_value(json_object_get(jresponse, "name"));
|
||||
auto source = json_string_value(json_object_get(jresponse, "source"));
|
||||
auto downloadLink = json_string_value(json_object_get(jresponse, "download"));
|
||||
if (downloadLink != nullptr)
|
||||
auto objName = Json::GetString(jresponse["name"]);
|
||||
auto source = Json::GetString(jresponse["source"]);
|
||||
auto downloadLink = Json::GetString(jresponse["download"]);
|
||||
if (!downloadLink.empty())
|
||||
{
|
||||
_lastDownloadSource = source;
|
||||
UpdateProgress({ name, source, _currentDownloadIndex, _entries.size() });
|
||||
DownloadObject(entry, objName, downloadLink);
|
||||
}
|
||||
json_decref(jresponse);
|
||||
}
|
||||
}
|
||||
else if (response.status == Http::Status::NotFound)
|
||||
|
||||
@@ -112,15 +112,12 @@ endif ()
|
||||
|
||||
# Third party libraries
|
||||
if (MSVC)
|
||||
find_package(jansson CONFIG REQUIRED)
|
||||
set(JANSSON_LIBRARIES "jansson::jansson")
|
||||
find_package(png 1.6 REQUIRED)
|
||||
find_package(zlib REQUIRED)
|
||||
|
||||
find_path(LIBZIP_INCLUDE_DIRS zip.h)
|
||||
find_library(LIBZIP_LIBRARIES zip)
|
||||
else ()
|
||||
PKG_CHECK_MODULES(JANSSON REQUIRED jansson>=2.5)
|
||||
PKG_CHECK_MODULES(LIBZIP REQUIRED libzip>=1.0)
|
||||
PKG_CHECK_MODULES(ZLIB REQUIRED zlib)
|
||||
|
||||
@@ -137,12 +134,12 @@ else ()
|
||||
endif ()
|
||||
|
||||
if (STATIC)
|
||||
target_link_libraries(${PROJECT_NAME} ${JANSSON_STATIC_LIBRARIES}
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
${PNG_STATIC_LIBRARIES}
|
||||
${ZLIB_STATIC_LIBRARIES}
|
||||
${LIBZIP_STATIC_LIBRARIES})
|
||||
else ()
|
||||
target_link_libraries(${PROJECT_NAME} ${JANSSON_LIBRARIES}
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
${PNG_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${LIBZIP_LIBRARIES})
|
||||
@@ -201,7 +198,6 @@ endif()
|
||||
|
||||
# Includes
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${LIBZIP_INCLUDE_DIRS})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${JANSSON_INCLUDE_DIRS})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${PNG_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS})
|
||||
include_directories(${PROJECT_NAME} SYSTEM ${CMAKE_CURRENT_LIST_DIR}/../thirdparty)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Context.h"
|
||||
#include "OpenRCT2.h"
|
||||
#include "core/Imaging.h"
|
||||
#include "core/Json.hpp"
|
||||
#include "drawing/Drawing.h"
|
||||
#include "drawing/ImageImporter.h"
|
||||
#include "object/ObjectLimits.h"
|
||||
@@ -26,7 +27,6 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <jansson.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include "core/String.hpp"
|
||||
@@ -675,28 +675,16 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc)
|
||||
const char* spriteDescriptionPath = argv[2];
|
||||
char* directoryPath = path_get_directory(spriteDescriptionPath);
|
||||
|
||||
json_error_t error;
|
||||
auto fp = fopen(spriteDescriptionPath, "rb");
|
||||
if (fp == nullptr)
|
||||
json_t jsonSprites = Json::ReadFromFile(spriteDescriptionPath);
|
||||
if (jsonSprites.is_null())
|
||||
{
|
||||
fprintf(stderr, "Unable to read sprite description file: %s\n", spriteDescriptionPath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_t* sprite_list = json_loadf(fp, JSON_REJECT_DUPLICATES, &error);
|
||||
fclose(fp);
|
||||
if (sprite_list == nullptr)
|
||||
{
|
||||
fprintf(
|
||||
stderr, "Error parsing sprite description file: %s at line %d column %d\n", error.text, error.line,
|
||||
error.column);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!json_is_array(sprite_list))
|
||||
if (!jsonSprites.is_array())
|
||||
{
|
||||
fprintf(stderr, "Error: expected array\n");
|
||||
json_decref(sprite_list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -708,66 +696,45 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc)
|
||||
|
||||
fprintf(stdout, "Building: %s\n", spriteFilePath);
|
||||
|
||||
size_t i;
|
||||
json_t* sprite_description;
|
||||
json_t sprite_description;
|
||||
|
||||
json_array_foreach(sprite_list, i, sprite_description)
|
||||
// Note: jsonSprite is deliberately left non-const: json_t behaviour changes when const
|
||||
for (auto& [jsonKey, jsonSprite] : jsonSprites.items())
|
||||
{
|
||||
if (!json_is_object(sprite_description))
|
||||
if (!jsonSprite.is_object())
|
||||
{
|
||||
fprintf(stderr, "Error: expected object for sprite %lu\n", static_cast<unsigned long>(i));
|
||||
json_decref(sprite_list);
|
||||
fprintf(stderr, "Error: expected object for sprite %s\n", jsonKey.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_t* path = json_object_get(sprite_description, "path");
|
||||
if (!json_is_string(path))
|
||||
json_t path = jsonSprite["path"];
|
||||
if (!path.is_string())
|
||||
{
|
||||
fprintf(stderr, "Error: no path provided for sprite %lu\n", static_cast<unsigned long>(i));
|
||||
json_decref(sprite_list);
|
||||
fprintf(stderr, "Error: no path provided for sprite %s\n", jsonKey.c_str());
|
||||
return -1;
|
||||
}
|
||||
// Get x and y offsets, if present
|
||||
json_t* x_offset = json_object_get(sprite_description, "x_offset");
|
||||
json_t* y_offset = json_object_get(sprite_description, "y_offset");
|
||||
std::string strPath = Json::GetString(path);
|
||||
|
||||
// Get palette option, if present
|
||||
bool keep_palette = false;
|
||||
json_t* palette = json_object_get(sprite_description, "palette");
|
||||
if (json_is_string(palette))
|
||||
{
|
||||
const char* option = json_string_value(palette);
|
||||
if (strncmp(option, "keep", 4) == 0)
|
||||
{
|
||||
keep_palette = true;
|
||||
}
|
||||
}
|
||||
json_t x_offset = jsonSprite["x_offset"];
|
||||
json_t y_offset = jsonSprite["y_offset"];
|
||||
|
||||
// Get forcebmp option, if present
|
||||
bool forceBmp = false;
|
||||
json_t* forceBmpObject = json_object_get(sprite_description, "forceBmp");
|
||||
if (palette && json_is_boolean(forceBmpObject))
|
||||
{
|
||||
forceBmp = json_boolean_value(forceBmpObject);
|
||||
}
|
||||
bool keep_palette = Json::GetString(jsonSprite["palette"]) == "keep";
|
||||
bool forceBmp = !jsonSprite["palette"].is_null() && Json::GetBoolean(jsonSprite["forceBmp"]);
|
||||
|
||||
// Resolve absolute sprite path
|
||||
auto imagePath = platform_get_absolute_path(json_string_value(path), directoryPath);
|
||||
auto imagePath = platform_get_absolute_path(strPath.c_str(), directoryPath);
|
||||
|
||||
auto importResult = sprite_file_import(
|
||||
imagePath.c_str(), x_offset == nullptr ? 0 : json_integer_value(x_offset),
|
||||
y_offset == nullptr ? 0 : json_integer_value(y_offset), keep_palette, forceBmp, gSpriteMode);
|
||||
imagePath.c_str(), Json::GetNumber<int16_t>(x_offset), Json::GetNumber<int16_t>(y_offset), keep_palette,
|
||||
forceBmp, gSpriteMode);
|
||||
if (importResult == std::nullopt)
|
||||
{
|
||||
fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath.c_str());
|
||||
json_decref(sprite_list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sprite_file_open(spriteFilePath))
|
||||
{
|
||||
fprintf(stderr, "Unable to open sprite file: %s\nCanceling\n", spriteFilePath);
|
||||
json_decref(sprite_list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -790,7 +757,6 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc)
|
||||
if (!sprite_file_save(spriteFilePath))
|
||||
{
|
||||
fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath.c_str());
|
||||
json_decref(sprite_list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -800,7 +766,6 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc)
|
||||
sprite_file_close();
|
||||
}
|
||||
|
||||
json_decref(sprite_list);
|
||||
free(directoryPath);
|
||||
|
||||
fprintf(stdout, "Finished\n");
|
||||
|
||||
@@ -81,24 +81,12 @@ NewVersionInfo get_latest_version()
|
||||
return {};
|
||||
}
|
||||
|
||||
json_t* root = Json::FromString(res.body);
|
||||
json_t root = Json::FromString(res.body);
|
||||
|
||||
auto get_as_string = [root](std::string name) {
|
||||
std::string value;
|
||||
json_t* json_value = json_object_get(root, name.c_str());
|
||||
if (json_is_string(json_value))
|
||||
{
|
||||
value = (json_string_value(json_value));
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
verinfo.tag = get_as_string("tag_name");
|
||||
verinfo.name = get_as_string("name");
|
||||
verinfo.changelog = get_as_string("body");
|
||||
verinfo.url = get_as_string("html_url");
|
||||
|
||||
json_decref(root);
|
||||
verinfo.tag = Json::GetString(root["tag_name"]);
|
||||
verinfo.name = Json::GetString(root["name"]);
|
||||
verinfo.changelog = Json::GetString(root["body"]);
|
||||
verinfo.url = Json::GetString(root["html_url"]);
|
||||
|
||||
gConfigGeneral.last_version_check_time = now;
|
||||
config_save_default();
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
|
||||
namespace Json
|
||||
{
|
||||
json_t* ReadFromFile(const utf8* path, size_t maxSize)
|
||||
json_t ReadFromFile(const utf8* path, size_t maxSize)
|
||||
{
|
||||
json_t* json = nullptr;
|
||||
auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN);
|
||||
|
||||
size_t fileLength = static_cast<size_t>(fs.GetLength());
|
||||
@@ -26,42 +25,101 @@ namespace Json
|
||||
throw IOException("Json file too large.");
|
||||
}
|
||||
|
||||
utf8* fileData = Memory::Allocate<utf8>(fileLength + 1);
|
||||
fs.Read(fileData, fileLength);
|
||||
fileData[fileLength] = '\0';
|
||||
auto fileData = std::string(static_cast<size_t>(fileLength) + 1, '\0');
|
||||
fs.Read(static_cast<void*>(fileData.data()), fileLength);
|
||||
|
||||
json_error_t jsonLoadError;
|
||||
json = json_loads(fileData, 0, &jsonLoadError);
|
||||
Memory::Free(fileData);
|
||||
json_t json;
|
||||
|
||||
if (json == nullptr)
|
||||
try
|
||||
{
|
||||
throw JsonException(&jsonLoadError);
|
||||
json = json_t::parse(fileData, nullptr, true, true);
|
||||
}
|
||||
catch (const json_t::exception& e)
|
||||
{
|
||||
throw JsonException(String::Format("Unable to parse JSON file (%s)\n\t%s", path, e.what()));
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
void WriteToFile(const utf8* path, const json_t* json, size_t flags)
|
||||
void WriteToFile(const utf8* path, const json_t& jsonData, int indentSize)
|
||||
{
|
||||
// Serialise JSON
|
||||
const char* jsonOutput = json_dumps(json, flags);
|
||||
std::string jsonOutput = jsonData.dump(indentSize);
|
||||
|
||||
// Write to file
|
||||
auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_WRITE);
|
||||
size_t jsonOutputSize = String::SizeOf(jsonOutput);
|
||||
fs.Write(jsonOutput, jsonOutputSize);
|
||||
fs.Write(jsonOutput.data(), jsonOutput.size());
|
||||
}
|
||||
|
||||
json_t* FromString(std::string_view raw)
|
||||
json_t FromString(std::string_view raw)
|
||||
{
|
||||
json_t* root;
|
||||
json_error_t error;
|
||||
root = json_loadb(raw.data(), raw.size(), 0, &error);
|
||||
if (root == nullptr)
|
||||
json_t json;
|
||||
|
||||
try
|
||||
{
|
||||
throw JsonException(&error);
|
||||
json = json_t::parse(raw, nullptr, true, true);
|
||||
}
|
||||
return root;
|
||||
catch (const json_t::exception& e)
|
||||
{
|
||||
log_error("Unable to parse JSON string (%s)\n\t%s", raw, e.what());
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
json_t FromVector(const std::vector<uint8_t>& vec)
|
||||
{
|
||||
json_t json;
|
||||
|
||||
try
|
||||
{
|
||||
json = json_t::parse(vec.begin(), vec.end());
|
||||
}
|
||||
catch (const json_t::exception& e)
|
||||
{
|
||||
log_error("Unable to parse JSON vector (%s)\n\t%s", vec.data(), e.what());
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
std::string GetString(const json_t& jsonObj, const std::string& defaultValue)
|
||||
{
|
||||
return jsonObj.is_string() ? jsonObj.get<std::string>() : defaultValue;
|
||||
}
|
||||
|
||||
bool GetBoolean(const json_t& jsonObj, bool defaultValue)
|
||||
{
|
||||
return jsonObj.is_boolean() ? jsonObj.get<bool>() : defaultValue;
|
||||
}
|
||||
|
||||
json_t AsObject(const json_t& jsonObj)
|
||||
{
|
||||
return jsonObj.is_object() ? jsonObj : json_t::object();
|
||||
}
|
||||
|
||||
json_t AsArray(const json_t& jsonObj)
|
||||
{
|
||||
if (jsonObj.is_array())
|
||||
{
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
json_t retVal = json_t::array();
|
||||
|
||||
if (jsonObj.is_object())
|
||||
{
|
||||
for (const auto& jItem : jsonObj)
|
||||
{
|
||||
retVal.push_back(jItem);
|
||||
}
|
||||
}
|
||||
else if (!jsonObj.is_null())
|
||||
{
|
||||
retVal.push_back(jsonObj);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
} // namespace Json
|
||||
|
||||
@@ -11,36 +11,181 @@
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#include <jansson.h>
|
||||
#include <stdexcept>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using json_t = nlohmann::json;
|
||||
|
||||
namespace Json
|
||||
{
|
||||
// Don't try to load JSON files that exceed 64 MiB
|
||||
constexpr uint64_t MAX_JSON_SIZE = 64 * 1024 * 1024;
|
||||
|
||||
json_t* ReadFromFile(const utf8* path, size_t maxSize = MAX_JSON_SIZE);
|
||||
void WriteToFile(const utf8* path, const json_t* json, size_t flags = 0);
|
||||
/**
|
||||
* Read JSON file and parse contents
|
||||
* @param path Path to the source file
|
||||
* @param maxSize Max file size in bytes allowed
|
||||
* @return A JSON representation of the file
|
||||
* @note This function will throw an exception if the JSON file cannot be parsed
|
||||
*/
|
||||
json_t ReadFromFile(const utf8* path, size_t maxSize = MAX_JSON_SIZE);
|
||||
|
||||
json_t* FromString(std::string_view raw);
|
||||
/**
|
||||
* Read JSON file and parse the contents
|
||||
* @param path Path to the destination file
|
||||
* @param jsonData A JSON object
|
||||
* @param indentSize The number of spaces in an indent, or removes whitespace on -1
|
||||
*/
|
||||
void WriteToFile(const utf8* path, const json_t& jsonData, int indentSize = 4);
|
||||
|
||||
/**
|
||||
* Parse JSON from a string
|
||||
* @param raw JSON string
|
||||
* @return A JSON representation of the string
|
||||
* @note This function will throw an exception if the JSON string cannot be parsed
|
||||
*/
|
||||
json_t FromString(std::string_view raw);
|
||||
|
||||
/**
|
||||
* Parse JSON from a vector of characters
|
||||
* @param vec Vector of characters containing JSON
|
||||
* @return A JSON representation of the vector
|
||||
* @note This function will throw an exception if the JSON vector cannot be parsed
|
||||
*/
|
||||
json_t FromVector(const std::vector<uint8_t>& vec);
|
||||
|
||||
/**
|
||||
* Explicit type conversion between a JSON object and a compatible number value
|
||||
* @param T Destination numeric type
|
||||
* @param jsonObj JSON object holding the value
|
||||
* @param defaultValue Default value to return if the JSON object is not a number type
|
||||
* @return Copy of the JSON value converted to the given type
|
||||
*/
|
||||
template<typename T> T GetNumber(const json_t& jsonObj, T defaultValue = 0)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "GetNumber template parameter must be arithmetic");
|
||||
|
||||
return jsonObj.is_number() ? jsonObj.get<T>() : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit type conversion between a JSON object and a compatible enum value
|
||||
* @param T Destination enum
|
||||
* @param jsonObj JSON object holding the value
|
||||
* @param defaultValue Default value to return if the JSON object is not an enum type
|
||||
* @return Copy of the JSON value converted to the given enum type
|
||||
*/
|
||||
template<typename T> T GetEnum(const json_t& jsonObj, T defaultValue)
|
||||
{
|
||||
static_assert(std::is_enum<T>::value, "GetEnum template parameter must be an enum");
|
||||
|
||||
return jsonObj.is_number_integer() ? jsonObj.get<T>() : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit type conversion between a JSON object and an std::string
|
||||
* @param jsonObj JSON object holding the value
|
||||
* @param defaultValue Default value to return if the JSON object is not a string
|
||||
* @return Copy of the JSON value converted to std::string
|
||||
*/
|
||||
std::string GetString(const json_t& jsonObj, const std::string& defaultValue = std::string());
|
||||
|
||||
/**
|
||||
* Explicit type conversion between a JSON object and a boolean
|
||||
* @param jsonObj JSON object holding the value
|
||||
* @param defaultValue Default value to return if the JSON object is not a boolean
|
||||
* @return Copy of the JSON value converted to bool
|
||||
*/
|
||||
bool GetBoolean(const json_t& jsonObj, bool defaultValue = false);
|
||||
|
||||
/**
|
||||
* Ensures a given JSON object is an object type
|
||||
* @param jsonObj JSON object
|
||||
* @return The JSON object if it is an object type, or an empty object otherwise
|
||||
*/
|
||||
json_t AsObject(const json_t& jsonObj);
|
||||
|
||||
/**
|
||||
* Ensures a given JSON object is an array type
|
||||
* @param jsonObj JSON object
|
||||
* @return The JSON object if it is an array type, or an array containing the JSON object otherwise
|
||||
*/
|
||||
json_t AsArray(const json_t& jsonObj);
|
||||
|
||||
/**
|
||||
* Helper function to convert a json object and an initializer list to binary flags
|
||||
* @param T Type to return
|
||||
* @param jsonObj JSON object containing boolean values
|
||||
* @param list List of pairs of keys and bits to enable if that key in the object is true
|
||||
* @return Value with relevant bits flipped
|
||||
*/
|
||||
template<typename T> T GetFlags(const json_t& jsonObj, std::initializer_list<std::pair<std::string, T>> list)
|
||||
{
|
||||
static_assert(std::is_convertible<T, int>::value, "GetFlags template parameter must be integral or a weak enum");
|
||||
|
||||
T flags{};
|
||||
for (const auto& item : list)
|
||||
{
|
||||
if (jsonObj.contains(item.first) && Json::GetBoolean(jsonObj[item.first]))
|
||||
{
|
||||
flags = static_cast<T>(flags | item.second);
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the GetFlags function to allow for inverted values
|
||||
*/
|
||||
enum class FlagType : uint8_t
|
||||
{
|
||||
// Flag is turned on if the key is true
|
||||
Normal,
|
||||
// Flag is turned on if the key is false
|
||||
Inverted
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to convert a json object and an initializer list to binary flags
|
||||
* @param T Type to return
|
||||
* @param jsonObj JSON object containing boolean values
|
||||
* @param list List of tuples of keys, bits to change and flag type
|
||||
* @return Value with relevant bits flipped
|
||||
* @note FLAG_NORMAL behaves like the other GetFlags function, but FLAG_INVERTED will turn the flag on when false
|
||||
*/
|
||||
template<typename T> T GetFlags(const json_t& jsonObj, std::initializer_list<std::tuple<std::string, T, FlagType>> list)
|
||||
{
|
||||
static_assert(std::is_convertible<T, int>::value, "GetFlags template parameter must be integral or a weak enum");
|
||||
|
||||
T flags{};
|
||||
for (const auto& item : list)
|
||||
{
|
||||
if (std::get<2>(item) == FlagType::Normal)
|
||||
{
|
||||
if (jsonObj.contains(std::get<0>(item)) && Json::GetBoolean(jsonObj[std::get<0>(item)]))
|
||||
{
|
||||
flags = static_cast<T>(flags | std::get<1>(item));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the json flag doesn't exist, assume it's false
|
||||
if (!jsonObj.contains(std::get<0>(item)) || !Json::GetBoolean(jsonObj[std::get<0>(item)]))
|
||||
{
|
||||
flags = static_cast<T>(flags | std::get<1>(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
} // namespace Json
|
||||
|
||||
class JsonException final : public std::runtime_error
|
||||
{
|
||||
private:
|
||||
json_error_t _jsonError = {};
|
||||
|
||||
public:
|
||||
explicit JsonException(const std::string& message)
|
||||
: std::runtime_error(message)
|
||||
{
|
||||
}
|
||||
|
||||
explicit JsonException(const json_error_t* jsonError)
|
||||
: JsonException(std::string(jsonError->text))
|
||||
{
|
||||
_jsonError = *jsonError;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,6 +57,49 @@ void colours_init_maps()
|
||||
}
|
||||
}
|
||||
|
||||
namespace Colour
|
||||
{
|
||||
colour_t FromString(const std::string_view& s, colour_t defaultValue)
|
||||
{
|
||||
static const std::unordered_map<std::string_view, colour_t> LookupTable{
|
||||
{ "black", COLOUR_BLACK },
|
||||
{ "grey", COLOUR_GREY },
|
||||
{ "white", COLOUR_WHITE },
|
||||
{ "dark_purple", COLOUR_DARK_PURPLE },
|
||||
{ "light_purple", COLOUR_LIGHT_PURPLE },
|
||||
{ "bright_purple", COLOUR_BRIGHT_PURPLE },
|
||||
{ "dark_blue", COLOUR_DARK_BLUE },
|
||||
{ "light_blue", COLOUR_LIGHT_BLUE },
|
||||
{ "icy_blue", COLOUR_ICY_BLUE },
|
||||
{ "teal", COLOUR_TEAL },
|
||||
{ "aquamarine", COLOUR_AQUAMARINE },
|
||||
{ "saturated_green", COLOUR_SATURATED_GREEN },
|
||||
{ "dark_green", COLOUR_DARK_GREEN },
|
||||
{ "moss_green", COLOUR_MOSS_GREEN },
|
||||
{ "bright_green", COLOUR_BRIGHT_GREEN },
|
||||
{ "olive_green", COLOUR_OLIVE_GREEN },
|
||||
{ "dark_olive_green", COLOUR_DARK_OLIVE_GREEN },
|
||||
{ "bright_yellow", COLOUR_BRIGHT_YELLOW },
|
||||
{ "yellow", COLOUR_YELLOW },
|
||||
{ "dark_yellow", COLOUR_DARK_YELLOW },
|
||||
{ "light_orange", COLOUR_LIGHT_ORANGE },
|
||||
{ "dark_orange", COLOUR_DARK_ORANGE },
|
||||
{ "light_brown", COLOUR_LIGHT_BROWN },
|
||||
{ "saturated_brown", COLOUR_SATURATED_BROWN },
|
||||
{ "dark_brown", COLOUR_DARK_BROWN },
|
||||
{ "salmon_pink", COLOUR_SALMON_PINK },
|
||||
{ "bordeaux_red", COLOUR_BORDEAUX_RED },
|
||||
{ "saturated_red", COLOUR_SATURATED_RED },
|
||||
{ "bright_red", COLOUR_BRIGHT_RED },
|
||||
{ "dark_pink", COLOUR_DARK_PINK },
|
||||
{ "bright_pink", COLOUR_BRIGHT_PINK },
|
||||
{ "light_pink", COLOUR_LIGHT_PINK },
|
||||
};
|
||||
auto result = LookupTable.find(s);
|
||||
return (result != LookupTable.end()) ? result->second : defaultValue;
|
||||
}
|
||||
} // namespace Colour
|
||||
|
||||
#ifndef NO_TTF
|
||||
static uint8_t BlendColourMap[PALETTE_COUNT][PALETTE_COUNT] = { 0 };
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* Colour IDs as used by the colour dropdown, NOT palette indices.
|
||||
*/
|
||||
@@ -207,6 +209,11 @@ extern rct_colour_map ColourMapA[COLOUR_COUNT];
|
||||
|
||||
void colours_init_maps();
|
||||
|
||||
namespace Colour
|
||||
{
|
||||
colour_t FromString(const std::string_view& s, colour_t defaultValue = COLOUR_BLACK);
|
||||
}
|
||||
|
||||
#ifndef NO_TTF
|
||||
uint8_t blendColours(const uint8_t paletteIndex1, const uint8_t paletteIndex2);
|
||||
#endif
|
||||
|
||||
52
src/openrct2/interface/Cursors.cpp
Normal file
52
src/openrct2/interface/Cursors.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "Cursors.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Cursor
|
||||
{
|
||||
uint8_t FromString(const std::string& s, uint8_t defaultValue)
|
||||
{
|
||||
static const std::unordered_map<std::string, uint8_t> LookupTable{
|
||||
{ "CURSOR_BLANK", CURSOR_BLANK },
|
||||
{ "CURSOR_UP_ARROW", CURSOR_UP_ARROW },
|
||||
{ "CURSOR_UP_DOWN_ARROW", CURSOR_UP_DOWN_ARROW },
|
||||
{ "CURSOR_HAND_POINT", CURSOR_HAND_POINT },
|
||||
{ "CURSOR_ZZZ", CURSOR_ZZZ },
|
||||
{ "CURSOR_DIAGONAL_ARROWS", CURSOR_DIAGONAL_ARROWS },
|
||||
{ "CURSOR_PICKER", CURSOR_PICKER },
|
||||
{ "CURSOR_TREE_DOWN", CURSOR_TREE_DOWN },
|
||||
{ "CURSOR_FOUNTAIN_DOWN", CURSOR_FOUNTAIN_DOWN },
|
||||
{ "CURSOR_STATUE_DOWN", CURSOR_STATUE_DOWN },
|
||||
{ "CURSOR_BENCH_DOWN", CURSOR_BENCH_DOWN },
|
||||
{ "CURSOR_CROSS_HAIR", CURSOR_CROSS_HAIR },
|
||||
{ "CURSOR_BIN_DOWN", CURSOR_BIN_DOWN },
|
||||
{ "CURSOR_LAMPPOST_DOWN", CURSOR_LAMPPOST_DOWN },
|
||||
{ "CURSOR_FENCE_DOWN", CURSOR_FENCE_DOWN },
|
||||
{ "CURSOR_FLOWER_DOWN", CURSOR_FLOWER_DOWN },
|
||||
{ "CURSOR_PATH_DOWN", CURSOR_PATH_DOWN },
|
||||
{ "CURSOR_DIG_DOWN", CURSOR_DIG_DOWN },
|
||||
{ "CURSOR_WATER_DOWN", CURSOR_WATER_DOWN },
|
||||
{ "CURSOR_HOUSE_DOWN", CURSOR_HOUSE_DOWN },
|
||||
{ "CURSOR_VOLCANO_DOWN", CURSOR_VOLCANO_DOWN },
|
||||
{ "CURSOR_WALK_DOWN", CURSOR_WALK_DOWN },
|
||||
{ "CURSOR_PAINT_DOWN", CURSOR_PAINT_DOWN },
|
||||
{ "CURSOR_ENTRANCE_DOWN", CURSOR_ENTRANCE_DOWN },
|
||||
{ "CURSOR_HAND_OPEN", CURSOR_HAND_OPEN },
|
||||
{ "CURSOR_HAND_CLOSED", CURSOR_HAND_CLOSED },
|
||||
{ "CURSOR_ARROW", CURSOR_ARROW },
|
||||
};
|
||||
|
||||
auto result = LookupTable.find(s);
|
||||
return (result != LookupTable.end()) ? result->second : defaultValue;
|
||||
}
|
||||
} // namespace Cursor
|
||||
@@ -44,6 +44,11 @@ enum CURSOR_ID
|
||||
CURSOR_COUNT,
|
||||
};
|
||||
|
||||
namespace Cursor
|
||||
{
|
||||
uint8_t FromString(const std::string& s, uint8_t defaultValue);
|
||||
}
|
||||
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
constexpr size_t CURSOR_BIT_WIDTH = 32;
|
||||
|
||||
@@ -251,7 +251,6 @@
|
||||
<ClInclude Include="object\LargeSceneryObject.h" />
|
||||
<ClInclude Include="object\Object.h" />
|
||||
<ClInclude Include="object\ObjectFactory.h" />
|
||||
<ClInclude Include="object\ObjectJsonHelpers.h" />
|
||||
<ClInclude Include="object\ObjectLimits.h" />
|
||||
<ClInclude Include="object\ObjectList.h" />
|
||||
<ClInclude Include="object\ObjectManager.h" />
|
||||
@@ -527,6 +526,7 @@
|
||||
<ClCompile Include="Input.cpp" />
|
||||
<ClCompile Include="interface\Chat.cpp" />
|
||||
<ClCompile Include="interface\Colour.cpp" />
|
||||
<ClCompile Include="interface\Cursors.cpp" />
|
||||
<ClCompile Include="interface\FontFamilies.cpp" />
|
||||
<ClCompile Include="interface\Fonts.cpp" />
|
||||
<ClCompile Include="interface\InteractiveConsole.cpp" />
|
||||
@@ -575,7 +575,6 @@
|
||||
<ClCompile Include="object\LargeSceneryObject.cpp" />
|
||||
<ClCompile Include="object\Object.cpp" />
|
||||
<ClCompile Include="object\ObjectFactory.cpp" />
|
||||
<ClCompile Include="object\ObjectJsonHelpers.cpp" />
|
||||
<ClCompile Include="object\ObjectList.cpp" />
|
||||
<ClCompile Include="object\ObjectManager.cpp" />
|
||||
<ClCompile Include="object\ObjectRepository.cpp" />
|
||||
|
||||
@@ -960,24 +960,23 @@ void NetworkBase::SaveGroups()
|
||||
platform_get_user_directory(path, nullptr, sizeof(path));
|
||||
safe_strcat_path(path, "groups.json", sizeof(path));
|
||||
|
||||
json_t* jsonGroupsCfg = json_object();
|
||||
json_t* jsonGroups = json_array();
|
||||
json_t jsonGroups = json_t::array();
|
||||
for (auto& group : group_list)
|
||||
{
|
||||
json_array_append_new(jsonGroups, group->ToJson());
|
||||
jsonGroups.push_back(group->ToJson());
|
||||
}
|
||||
json_object_set_new(jsonGroupsCfg, "default_group", json_integer(default_group));
|
||||
json_object_set_new(jsonGroupsCfg, "groups", jsonGroups);
|
||||
json_t jsonGroupsCfg = {
|
||||
{ "default_group", default_group },
|
||||
{ "groups", jsonGroups },
|
||||
};
|
||||
try
|
||||
{
|
||||
Json::WriteToFile(path, jsonGroupsCfg, JSON_INDENT(4) | JSON_PRESERVE_ORDER);
|
||||
Json::WriteToFile(path, jsonGroupsCfg);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
log_error("Unable to save %s: %s", path, ex.what());
|
||||
}
|
||||
|
||||
json_decref(jsonGroupsCfg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1023,12 +1022,12 @@ void NetworkBase::LoadGroups()
|
||||
platform_get_user_directory(path, nullptr, sizeof(path));
|
||||
safe_strcat_path(path, "groups.json", sizeof(path));
|
||||
|
||||
json_t* json = nullptr;
|
||||
json_t jsonGroupConfig;
|
||||
if (Platform::FileExists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
json = Json::ReadFromFile(path);
|
||||
jsonGroupConfig = Json::ReadFromFile(path);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -1036,28 +1035,26 @@ void NetworkBase::LoadGroups()
|
||||
}
|
||||
}
|
||||
|
||||
if (json == nullptr)
|
||||
if (!jsonGroupConfig.is_object())
|
||||
{
|
||||
SetupDefaultGroups();
|
||||
}
|
||||
else
|
||||
{
|
||||
json_t* json_groups = json_object_get(json, "groups");
|
||||
size_t groupCount = json_array_size(json_groups);
|
||||
for (size_t i = 0; i < groupCount; i++)
|
||||
json_t jsonGroups = jsonGroupConfig["groups"];
|
||||
if (jsonGroups.is_array())
|
||||
{
|
||||
json_t* jsonGroup = json_array_get(json_groups, i);
|
||||
|
||||
auto newgroup = std::make_unique<NetworkGroup>(NetworkGroup::FromJson(jsonGroup));
|
||||
group_list.push_back(std::move(newgroup));
|
||||
for (auto& jsonGroup : jsonGroups)
|
||||
{
|
||||
group_list.emplace_back(std::make_unique<NetworkGroup>(NetworkGroup::FromJson(jsonGroup)));
|
||||
}
|
||||
}
|
||||
json_t* jsonDefaultGroup = json_object_get(json, "default_group");
|
||||
default_group = static_cast<uint8_t>(json_integer_value(jsonDefaultGroup));
|
||||
|
||||
default_group = Json::GetNumber<uint8_t>(jsonGroupConfig["default_group"]);
|
||||
if (GetGroupByID(default_group) == nullptr)
|
||||
{
|
||||
default_group = 0;
|
||||
}
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
// Host group should always contain all permissions.
|
||||
@@ -1594,37 +1591,35 @@ void NetworkBase::Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, co
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
json_t* NetworkBase::GetServerInfoAsJson() const
|
||||
json_t NetworkBase::GetServerInfoAsJson() const
|
||||
{
|
||||
json_t* obj = json_object();
|
||||
json_object_set_new(obj, "name", json_string(gConfigNetwork.server_name.c_str()));
|
||||
json_object_set_new(obj, "requiresPassword", json_boolean(_password.size() > 0));
|
||||
json_object_set_new(obj, "version", json_string(network_get_version().c_str()));
|
||||
json_object_set_new(obj, "players", json_integer(player_list.size()));
|
||||
json_object_set_new(obj, "maxPlayers", json_integer(gConfigNetwork.maxplayers));
|
||||
json_object_set_new(obj, "description", json_string(gConfigNetwork.server_description.c_str()));
|
||||
json_object_set_new(obj, "greeting", json_string(gConfigNetwork.server_greeting.c_str()));
|
||||
json_object_set_new(obj, "dedicated", json_boolean(gOpenRCT2Headless));
|
||||
return obj;
|
||||
json_t jsonObj = {
|
||||
{ "name", gConfigNetwork.server_name }, { "requiresPassword", _password.size() > 0 },
|
||||
{ "version", network_get_version() }, { "players", player_list.size() },
|
||||
{ "maxPlayers", gConfigNetwork.maxplayers }, { "description", gConfigNetwork.server_description },
|
||||
{ "greeting", gConfigNetwork.server_greeting }, { "dedicated", gOpenRCT2Headless },
|
||||
};
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
void NetworkBase::Server_Send_GAMEINFO(NetworkConnection& connection)
|
||||
{
|
||||
NetworkPacket packet(NetworkCommand::GameInfo);
|
||||
# ifndef DISABLE_HTTP
|
||||
json_t* obj = GetServerInfoAsJson();
|
||||
json_t jsonObj = GetServerInfoAsJson();
|
||||
|
||||
// Provider details
|
||||
json_t* jsonProvider = json_object();
|
||||
json_object_set_new(jsonProvider, "name", json_string(gConfigNetwork.provider_name.c_str()));
|
||||
json_object_set_new(jsonProvider, "email", json_string(gConfigNetwork.provider_email.c_str()));
|
||||
json_object_set_new(jsonProvider, "website", json_string(gConfigNetwork.provider_website.c_str()));
|
||||
json_object_set_new(obj, "provider", jsonProvider);
|
||||
json_t jsonProvider = {
|
||||
{ "name", gConfigNetwork.provider_name },
|
||||
{ "email", gConfigNetwork.provider_email },
|
||||
{ "website", gConfigNetwork.provider_website },
|
||||
};
|
||||
|
||||
packet.WriteString(json_dumps(obj, 0));
|
||||
jsonObj["provider"] = jsonProvider;
|
||||
|
||||
packet.WriteString(jsonObj.dump().c_str());
|
||||
packet << _serverState.gamestateSnapshotsEnabled;
|
||||
|
||||
json_decref(obj);
|
||||
# endif
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
@@ -3178,32 +3173,27 @@ void NetworkBase::Client_Send_GAMEINFO()
|
||||
_serverConnection->QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
static std::string json_stdstring_value(const json_t* string)
|
||||
{
|
||||
const char* cstr = json_string_value(string);
|
||||
return cstr == nullptr ? std::string() : std::string(cstr);
|
||||
}
|
||||
|
||||
void NetworkBase::Client_Handle_GAMEINFO([[maybe_unused]] NetworkConnection& connection, NetworkPacket& packet)
|
||||
{
|
||||
const char* jsonString = packet.ReadString();
|
||||
packet >> _serverState.gamestateSnapshotsEnabled;
|
||||
|
||||
json_error_t error;
|
||||
json_t* root = json_loads(jsonString, 0, &error);
|
||||
json_t jsonData = Json::FromString(jsonString);
|
||||
|
||||
ServerName = json_stdstring_value(json_object_get(root, "name"));
|
||||
ServerDescription = json_stdstring_value(json_object_get(root, "description"));
|
||||
ServerGreeting = json_stdstring_value(json_object_get(root, "greeting"));
|
||||
|
||||
json_t* jsonProvider = json_object_get(root, "provider");
|
||||
if (jsonProvider != nullptr)
|
||||
if (jsonData.is_object())
|
||||
{
|
||||
ServerProviderName = json_stdstring_value(json_object_get(jsonProvider, "name"));
|
||||
ServerProviderEmail = json_stdstring_value(json_object_get(jsonProvider, "email"));
|
||||
ServerProviderWebsite = json_stdstring_value(json_object_get(jsonProvider, "website"));
|
||||
ServerName = Json::GetString(jsonData["name"]);
|
||||
ServerDescription = Json::GetString(jsonData["description"]);
|
||||
ServerGreeting = Json::GetString(jsonData["greeting"]);
|
||||
|
||||
json_t jsonProvider = jsonData["provider"];
|
||||
if (jsonProvider.is_object())
|
||||
{
|
||||
ServerProviderName = Json::GetString(jsonProvider["name"]);
|
||||
ServerProviderEmail = Json::GetString(jsonProvider["email"]);
|
||||
ServerProviderWebsite = Json::GetString(jsonProvider["website"]);
|
||||
}
|
||||
}
|
||||
json_decref(root);
|
||||
|
||||
network_chat_show_server_greeting();
|
||||
}
|
||||
@@ -3977,7 +3967,7 @@ bool network_gamestate_snapshots_enabled()
|
||||
return network_get_server_state().gamestateSnapshotsEnabled;
|
||||
}
|
||||
|
||||
json_t* network_get_server_info_as_json()
|
||||
json_t network_get_server_info_as_json()
|
||||
{
|
||||
return gNetwork.GetServerInfoAsJson();
|
||||
}
|
||||
@@ -4242,8 +4232,8 @@ NetworkServerState_t network_get_server_state()
|
||||
{
|
||||
return NetworkServerState_t{};
|
||||
}
|
||||
json_t* network_get_server_info_as_json()
|
||||
json_t network_get_server_info_as_json()
|
||||
{
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
#endif /* DISABLE_NETWORK */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../actions/GameAction.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "NetworkConnection.h"
|
||||
#include "NetworkGroup.h"
|
||||
#include "NetworkPlayer.h"
|
||||
@@ -42,7 +43,7 @@ public: // Common
|
||||
void AppendChatLog(const std::string& s);
|
||||
void CloseChatLog();
|
||||
NetworkStats_t GetStats() const;
|
||||
json_t* GetServerInfoAsJson() const;
|
||||
json_t GetServerInfoAsJson() const;
|
||||
bool ProcessConnection(NetworkConnection& connection);
|
||||
void CloseConnection();
|
||||
NetworkPlayer* AddPlayer(const std::string& name, const std::string& keyhash);
|
||||
|
||||
@@ -14,31 +14,29 @@
|
||||
# include "NetworkAction.h"
|
||||
# include "NetworkTypes.h"
|
||||
|
||||
NetworkGroup NetworkGroup::FromJson(const json_t* json)
|
||||
NetworkGroup NetworkGroup::FromJson(json_t& jsonData)
|
||||
{
|
||||
NetworkGroup group;
|
||||
json_t* jsonId = json_object_get(json, "id");
|
||||
json_t* jsonName = json_object_get(json, "name");
|
||||
json_t* jsonPermissions = json_object_get(json, "permissions");
|
||||
Guard::Assert(jsonData.is_object(), "NetworkGroup::FromJson expects parameter jsonData to be object");
|
||||
|
||||
if (jsonId == nullptr || jsonName == nullptr || jsonPermissions == nullptr)
|
||||
NetworkGroup group;
|
||||
json_t jsonId = jsonData["id"];
|
||||
json_t jsonName = jsonData["name"];
|
||||
json_t jsonPermissions = jsonData["permissions"];
|
||||
|
||||
if (jsonId.is_null() || jsonName.is_null() || jsonPermissions.is_null())
|
||||
{
|
||||
throw std::runtime_error("Missing group data");
|
||||
}
|
||||
|
||||
group.Id = static_cast<uint8_t>(json_integer_value(jsonId));
|
||||
group._name = std::string(json_string_value(jsonName));
|
||||
group.Id = Json::GetNumber<uint8_t>(jsonId);
|
||||
group._name = Json::GetString(jsonName);
|
||||
std::fill(group.ActionsAllowed.begin(), group.ActionsAllowed.end(), 0);
|
||||
|
||||
for (size_t i = 0; i < json_array_size(jsonPermissions); i++)
|
||||
for (const auto& jsonValue : jsonPermissions)
|
||||
{
|
||||
json_t* jsonPermissionValue = json_array_get(jsonPermissions, i);
|
||||
const char* perm_name = json_string_value(jsonPermissionValue);
|
||||
if (perm_name == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
NetworkPermission action_id = NetworkActions::FindCommandByPermissionName(perm_name);
|
||||
const std::string permission = Json::GetString(jsonValue);
|
||||
|
||||
NetworkPermission action_id = NetworkActions::FindCommandByPermissionName(permission);
|
||||
if (action_id != NetworkPermission::Count)
|
||||
{
|
||||
group.ToggleActionPermission(action_id);
|
||||
@@ -47,21 +45,21 @@ NetworkGroup NetworkGroup::FromJson(const json_t* json)
|
||||
return group;
|
||||
}
|
||||
|
||||
json_t* NetworkGroup::ToJson() const
|
||||
json_t NetworkGroup::ToJson() const
|
||||
{
|
||||
json_t* jsonGroup = json_object();
|
||||
json_object_set_new(jsonGroup, "id", json_integer(Id));
|
||||
json_object_set_new(jsonGroup, "name", json_string(GetName().c_str()));
|
||||
json_t* actionsArray = json_array();
|
||||
json_t jsonGroup = {
|
||||
{ "id", Id },
|
||||
{ "name", GetName() },
|
||||
};
|
||||
json_t actionsArray = json_t::array();
|
||||
for (size_t i = 0; i < NetworkActions::Actions.size(); i++)
|
||||
{
|
||||
if (CanPerformAction(static_cast<NetworkPermission>(i)))
|
||||
{
|
||||
const char* perm_name = NetworkActions::Actions[i].PermissionName.c_str();
|
||||
json_array_append_new(actionsArray, json_string(perm_name));
|
||||
actionsArray.push_back(NetworkActions::Actions[i].PermissionName);
|
||||
}
|
||||
}
|
||||
json_object_set_new(jsonGroup, "permissions", actionsArray);
|
||||
jsonGroup["permissions"] = actionsArray;
|
||||
return jsonGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "NetworkPacket.h"
|
||||
|
||||
#include <array>
|
||||
#include <jansson.h>
|
||||
#include <string>
|
||||
|
||||
enum class NetworkPermission : uint32_t;
|
||||
@@ -24,7 +24,14 @@ public:
|
||||
std::array<uint8_t, 8> ActionsAllowed{};
|
||||
uint8_t Id = 0;
|
||||
|
||||
static NetworkGroup FromJson(const json_t* json);
|
||||
/**
|
||||
* Creates a NetworkGroup object from a JSON object
|
||||
*
|
||||
* @param json JSON data source
|
||||
* @return A NetworkGroup object
|
||||
* @note json is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
static NetworkGroup FromJson(json_t& json);
|
||||
|
||||
const std::string& GetName() const;
|
||||
void SetName(std::string name);
|
||||
@@ -35,7 +42,12 @@ public:
|
||||
bool CanPerformAction(NetworkPermission index) const;
|
||||
bool CanPerformCommand(int32_t command) const;
|
||||
|
||||
json_t* ToJson() const;
|
||||
/**
|
||||
* Serialise a NetworkGroup object into a JSON object
|
||||
*
|
||||
* @return JSON representation of the NetworkGroup object
|
||||
*/
|
||||
json_t ToJson() const;
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
# include "../config/Config.h"
|
||||
# include "../core/Console.hpp"
|
||||
# include "../core/Guard.hpp"
|
||||
# include "../core/Http.h"
|
||||
# include "../core/Json.hpp"
|
||||
# include "../core/String.hpp"
|
||||
@@ -119,12 +120,10 @@ private:
|
||||
if (String::Equals(buffer, NETWORK_LAN_BROADCAST_MSG))
|
||||
{
|
||||
auto body = GetBroadcastJson();
|
||||
auto bodyDump = json_dumps(body, JSON_COMPACT);
|
||||
size_t sendLen = strlen(bodyDump) + 1;
|
||||
auto bodyDump = body.dump();
|
||||
size_t sendLen = bodyDump.size() + 1;
|
||||
log_verbose("Sending %zu bytes back to %s", sendLen, sender.c_str());
|
||||
_lanListener->SendData(*endpoint, bodyDump, sendLen);
|
||||
free(bodyDump);
|
||||
json_decref(body);
|
||||
_lanListener->SendData(*endpoint, bodyDump.c_str(), sendLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,10 +131,10 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
json_t* GetBroadcastJson()
|
||||
json_t GetBroadcastJson()
|
||||
{
|
||||
auto root = network_get_server_info_as_json();
|
||||
json_object_set(root, "port", json_integer(_port));
|
||||
json_t root = network_get_server_info_as_json();
|
||||
root["port"] = _port;
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -172,20 +171,18 @@ private:
|
||||
request.method = Http::Method::POST;
|
||||
request.forceIPv4 = forceIPv4;
|
||||
|
||||
json_t* body = json_object();
|
||||
json_object_set_new(body, "key", json_string(_key.c_str()));
|
||||
json_object_set_new(body, "port", json_integer(_port));
|
||||
json_t body = {
|
||||
{ "key", _key },
|
||||
{ "port", _port },
|
||||
};
|
||||
|
||||
if (!gConfigNetwork.advertise_address.empty())
|
||||
{
|
||||
json_object_set_new(body, "address", json_string(gConfigNetwork.advertise_address.c_str()));
|
||||
body["address"] = gConfigNetwork.advertise_address;
|
||||
}
|
||||
|
||||
char* bodyDump = json_dumps(body, JSON_COMPACT);
|
||||
request.body = bodyDump;
|
||||
request.body = body.dump();
|
||||
request.header["Content-Type"] = "application/json";
|
||||
free(bodyDump);
|
||||
json_decref(body);
|
||||
|
||||
Http::DoAsync(request, [&](Http::Response response) -> void {
|
||||
if (response.status != Http::Status::OK)
|
||||
@@ -194,9 +191,9 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
json_t* root = Json::FromString(response.body);
|
||||
json_t root = Json::FromString(response.body);
|
||||
root = Json::AsObject(root);
|
||||
this->OnRegistrationResponse(root);
|
||||
json_decref(root);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -206,12 +203,9 @@ private:
|
||||
request.url = GetMasterServerUrl();
|
||||
request.method = Http::Method::PUT;
|
||||
|
||||
json_t* body = GetHeartbeatJson();
|
||||
char* bodyDump = json_dumps(body, JSON_COMPACT);
|
||||
request.body = bodyDump;
|
||||
json_t body = GetHeartbeatJson();
|
||||
request.body = body.dump();
|
||||
request.header["Content-Type"] = "application/json";
|
||||
free(bodyDump);
|
||||
json_decref(body);
|
||||
|
||||
_lastHeartbeatTime = platform_get_ticks();
|
||||
Http::DoAsync(request, [&](Http::Response response) -> void {
|
||||
@@ -221,86 +215,90 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
json_t* root = Json::FromString(response.body);
|
||||
json_t root = Json::FromString(response.body);
|
||||
root = Json::AsObject(root);
|
||||
this->OnHeartbeatResponse(root);
|
||||
json_decref(root);
|
||||
});
|
||||
}
|
||||
|
||||
void OnRegistrationResponse(json_t* jsonRoot)
|
||||
/**
|
||||
* @param jsonRoot must be of JSON type object or null
|
||||
* @note jsonRoot is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
void OnRegistrationResponse(json_t& jsonRoot)
|
||||
{
|
||||
json_t* jsonStatus = json_object_get(jsonRoot, "status");
|
||||
if (json_is_integer(jsonStatus))
|
||||
Guard::Assert(jsonRoot.is_object(), "OnRegistrationResponse expects parameter jsonRoot to be object");
|
||||
|
||||
int32_t status = Json::GetNumber<int32_t>(jsonRoot["status"]);
|
||||
|
||||
if (status == MASTER_SERVER_STATUS_OK)
|
||||
{
|
||||
int32_t status = static_cast<int32_t>(json_integer_value(jsonStatus));
|
||||
if (status == MASTER_SERVER_STATUS_OK)
|
||||
json_t jsonToken = jsonRoot["token"];
|
||||
if (jsonToken.is_string())
|
||||
{
|
||||
json_t* jsonToken = json_object_get(jsonRoot, "token");
|
||||
if (json_is_string(jsonToken))
|
||||
{
|
||||
_token = std::string(json_string_value(jsonToken));
|
||||
_status = ADVERTISE_STATUS::REGISTERED;
|
||||
}
|
||||
_token = Json::GetString(jsonToken);
|
||||
_status = ADVERTISE_STATUS::REGISTERED;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string message = Json::GetString(jsonRoot["message"]);
|
||||
if (message.empty())
|
||||
{
|
||||
const char* message = "Invalid response from server";
|
||||
json_t* jsonMessage = json_object_get(jsonRoot, "message");
|
||||
if (json_is_string(jsonMessage))
|
||||
{
|
||||
message = json_string_value(jsonMessage);
|
||||
}
|
||||
Console::Error::WriteLine("Unable to advertise (%d): %s", status, message);
|
||||
// Hack for https://github.com/OpenRCT2/OpenRCT2/issues/6277
|
||||
// Master server may not reply correctly if using IPv6, retry forcing IPv4,
|
||||
// don't wait the full timeout.
|
||||
if (!_forceIPv4 && status == 500)
|
||||
{
|
||||
_forceIPv4 = true;
|
||||
_lastAdvertiseTime = 0;
|
||||
log_info("Retry with ipv4 only");
|
||||
}
|
||||
message = "Invalid response from server";
|
||||
}
|
||||
Console::Error::WriteLine("Unable to advertise (%d): %s", status, message.c_str());
|
||||
// Hack for https://github.com/OpenRCT2/OpenRCT2/issues/6277
|
||||
// Master server may not reply correctly if using IPv6, retry forcing IPv4,
|
||||
// don't wait the full timeout.
|
||||
if (!_forceIPv4 && status == 500)
|
||||
{
|
||||
_forceIPv4 = true;
|
||||
_lastAdvertiseTime = 0;
|
||||
log_info("Retry with ipv4 only");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnHeartbeatResponse(json_t* jsonRoot)
|
||||
/**
|
||||
* @param jsonRoot must be of JSON type object or null
|
||||
* @note jsonRoot is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
void OnHeartbeatResponse(json_t& jsonRoot)
|
||||
{
|
||||
json_t* jsonStatus = json_object_get(jsonRoot, "status");
|
||||
if (json_is_integer(jsonStatus))
|
||||
Guard::Assert(jsonRoot.is_object(), "OnHeartbeatResponse expects parameter jsonRoot to be object");
|
||||
|
||||
int32_t status = Json::GetNumber<int32_t>(jsonRoot["status"]);
|
||||
if (status == MASTER_SERVER_STATUS_OK)
|
||||
{
|
||||
int32_t status = static_cast<int32_t>(json_integer_value(jsonStatus));
|
||||
if (status == MASTER_SERVER_STATUS_OK)
|
||||
{
|
||||
// Master server has successfully updated our server status
|
||||
}
|
||||
else if (status == MASTER_SERVER_STATUS_INVALID_TOKEN)
|
||||
{
|
||||
_status = ADVERTISE_STATUS::UNREGISTERED;
|
||||
Console::WriteLine("Master server heartbeat failed: Invalid Token");
|
||||
}
|
||||
// Master server has successfully updated our server status
|
||||
}
|
||||
else if (status == MASTER_SERVER_STATUS_INVALID_TOKEN)
|
||||
{
|
||||
_status = ADVERTISE_STATUS::UNREGISTERED;
|
||||
Console::WriteLine("Master server heartbeat failed: Invalid Token");
|
||||
}
|
||||
}
|
||||
|
||||
json_t* GetHeartbeatJson()
|
||||
json_t GetHeartbeatJson()
|
||||
{
|
||||
uint32_t numPlayers = network_get_num_players();
|
||||
|
||||
json_t* root = json_object();
|
||||
json_object_set_new(root, "token", json_string(_token.c_str()));
|
||||
json_object_set_new(root, "players", json_integer(numPlayers));
|
||||
json_t root = {
|
||||
{ "token", _token },
|
||||
{ "players", numPlayers },
|
||||
};
|
||||
|
||||
json_t* gameInfo = json_object();
|
||||
json_object_set_new(gameInfo, "mapSize", json_integer(gMapSize - 2));
|
||||
json_object_set_new(gameInfo, "day", json_integer(gDateMonthTicks));
|
||||
json_object_set_new(gameInfo, "month", json_integer(gDateMonthsElapsed));
|
||||
json_object_set_new(gameInfo, "guests", json_integer(gNumGuestsInPark));
|
||||
json_object_set_new(gameInfo, "parkValue", json_integer(gParkValue));
|
||||
json_t gameInfo = {
|
||||
{ "mapSize", gMapSize - 2 }, { "day", gDateMonthTicks }, { "month", gDateMonthsElapsed },
|
||||
{ "guests", gNumGuestsInPark }, { "parkValue", gParkValue },
|
||||
};
|
||||
if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
|
||||
{
|
||||
json_object_set_new(gameInfo, "cash", json_integer(gCash));
|
||||
gameInfo["cash"] = gCash;
|
||||
}
|
||||
json_object_set_new(root, "gameInfo", gameInfo);
|
||||
|
||||
root["gameInfo"] = gameInfo;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# include "NetworkUser.h"
|
||||
|
||||
# include "../core/Console.hpp"
|
||||
# include "../core/Json.hpp"
|
||||
# include "../core/Guard.hpp"
|
||||
# include "../core/Path.hpp"
|
||||
# include "../core/String.hpp"
|
||||
# include "../platform/Platform2.h"
|
||||
@@ -21,50 +21,43 @@
|
||||
|
||||
constexpr const utf8* USER_STORE_FILENAME = "users.json";
|
||||
|
||||
NetworkUser* NetworkUser::FromJson(json_t* json)
|
||||
NetworkUser* NetworkUser::FromJson(json_t& jsonData)
|
||||
{
|
||||
const char* hash = json_string_value(json_object_get(json, "hash"));
|
||||
const char* name = json_string_value(json_object_get(json, "name"));
|
||||
const json_t* jsonGroupId = json_object_get(json, "groupId");
|
||||
Guard::Assert(jsonData.is_object(), "NetworkUser::FromJson expects parameter jsonData to be object");
|
||||
|
||||
const std::string hash = Json::GetString(jsonData["hash"]);
|
||||
const std::string name = Json::GetString(jsonData["name"]);
|
||||
json_t jsonGroupId = jsonData["groupId"];
|
||||
|
||||
NetworkUser* user = nullptr;
|
||||
if (hash != nullptr && name != nullptr)
|
||||
if (!hash.empty() && !name.empty())
|
||||
{
|
||||
user = new NetworkUser();
|
||||
user->Hash = std::string(hash);
|
||||
user->Name = std::string(name);
|
||||
if (!json_is_null(jsonGroupId))
|
||||
user->Hash = hash;
|
||||
user->Name = name;
|
||||
if (jsonGroupId.is_number_integer())
|
||||
{
|
||||
user->GroupId = static_cast<uint8_t>(json_integer_value(jsonGroupId));
|
||||
user->GroupId = Json::GetNumber<uint8_t>(jsonGroupId);
|
||||
}
|
||||
user->Remove = false;
|
||||
return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
json_t* NetworkUser::ToJson() const
|
||||
json_t NetworkUser::ToJson() const
|
||||
{
|
||||
return ToJson(json_object());
|
||||
}
|
||||
json_t jsonData;
|
||||
jsonData["hash"] = Hash;
|
||||
jsonData["name"] = Name;
|
||||
|
||||
json_t* NetworkUser::ToJson(json_t* json) const
|
||||
{
|
||||
json_object_set_new(json, "hash", json_string(Hash.c_str()));
|
||||
json_object_set_new(json, "name", json_string(Name.c_str()));
|
||||
|
||||
json_t* jsonGroupId;
|
||||
json_t jsonGroupId;
|
||||
if (GroupId.HasValue())
|
||||
{
|
||||
jsonGroupId = json_integer(GroupId.GetValue());
|
||||
jsonGroupId = GroupId.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonGroupId = json_null();
|
||||
}
|
||||
json_object_set_new(json, "groupId", jsonGroupId);
|
||||
jsonData["groupId"] = jsonGroupId;
|
||||
|
||||
return json;
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
NetworkUserManager::~NetworkUserManager()
|
||||
@@ -92,18 +85,18 @@ void NetworkUserManager::Load()
|
||||
|
||||
try
|
||||
{
|
||||
json_t* jsonUsers = Json::ReadFromFile(path);
|
||||
size_t numUsers = json_array_size(jsonUsers);
|
||||
for (size_t i = 0; i < numUsers; i++)
|
||||
json_t jsonUsers = Json::ReadFromFile(path);
|
||||
for (auto& jsonUser : jsonUsers)
|
||||
{
|
||||
json_t* jsonUser = json_array_get(jsonUsers, i);
|
||||
NetworkUser* networkUser = NetworkUser::FromJson(jsonUser);
|
||||
if (networkUser != nullptr)
|
||||
if (jsonUser.is_object())
|
||||
{
|
||||
_usersByHash[networkUser->Hash] = networkUser;
|
||||
auto networkUser = NetworkUser::FromJson(jsonUser);
|
||||
if (networkUser != nullptr)
|
||||
{
|
||||
_usersByHash[networkUser->Hash] = networkUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
json_decref(jsonUsers);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
@@ -117,7 +110,7 @@ void NetworkUserManager::Save()
|
||||
utf8 path[MAX_PATH];
|
||||
GetStorePath(path, sizeof(path));
|
||||
|
||||
json_t* jsonUsers = nullptr;
|
||||
json_t jsonUsers;
|
||||
try
|
||||
{
|
||||
if (Platform::FileExists(path))
|
||||
@@ -129,36 +122,35 @@ void NetworkUserManager::Save()
|
||||
{
|
||||
}
|
||||
|
||||
if (jsonUsers == nullptr)
|
||||
{
|
||||
jsonUsers = json_array();
|
||||
}
|
||||
|
||||
// Update existing users
|
||||
std::unordered_set<std::string> savedHashes;
|
||||
size_t numUsers = json_array_size(jsonUsers);
|
||||
for (size_t i = 0; i < numUsers; i++)
|
||||
for (auto it = jsonUsers.begin(); it != jsonUsers.end();)
|
||||
{
|
||||
json_t* jsonUser = json_array_get(jsonUsers, i);
|
||||
const char* hash = json_string_value(json_object_get(jsonUser, "hash"));
|
||||
if (hash != nullptr)
|
||||
json_t jsonUser = *it;
|
||||
if (!jsonUser.is_object())
|
||||
{
|
||||
auto hashString = std::string(hash);
|
||||
const NetworkUser* networkUser = GetUserByHash(hashString);
|
||||
if (networkUser != nullptr)
|
||||
continue;
|
||||
}
|
||||
std::string hashString = Json::GetString(jsonUser["hash"]);
|
||||
|
||||
const auto networkUser = GetUserByHash(hashString);
|
||||
if (networkUser != nullptr)
|
||||
{
|
||||
if (networkUser->Remove)
|
||||
{
|
||||
if (networkUser->Remove)
|
||||
{
|
||||
json_array_remove(jsonUsers, i);
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
networkUser->ToJson(jsonUser);
|
||||
savedHashes.insert(hashString);
|
||||
}
|
||||
it = jsonUsers.erase(it);
|
||||
// erase advances the iterator so make sure we don't do it again
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// replace the existing element in jsonUsers
|
||||
*it = networkUser->ToJson();
|
||||
savedHashes.insert(hashString);
|
||||
}
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
// Add new users
|
||||
@@ -167,13 +159,11 @@ void NetworkUserManager::Save()
|
||||
const NetworkUser* networkUser = kvp.second;
|
||||
if (!networkUser->Remove && savedHashes.find(networkUser->Hash) == savedHashes.end())
|
||||
{
|
||||
json_t* jsonUser = networkUser->ToJson();
|
||||
json_array_append_new(jsonUsers, jsonUser);
|
||||
jsonUsers.push_back(networkUser->ToJson());
|
||||
}
|
||||
}
|
||||
|
||||
Json::WriteToFile(path, jsonUsers, JSON_INDENT(4) | JSON_PRESERVE_ORDER);
|
||||
json_decref(jsonUsers);
|
||||
Json::WriteToFile(path, jsonUsers);
|
||||
}
|
||||
|
||||
void NetworkUserManager::UnsetUsersOfGroup(uint8_t groupId)
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../core/Nullable.hpp"
|
||||
|
||||
#include <jansson.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
@@ -24,10 +24,20 @@ public:
|
||||
Nullable<uint8_t> GroupId;
|
||||
bool Remove;
|
||||
|
||||
static NetworkUser* FromJson(json_t* json);
|
||||
/**
|
||||
* Creates a NetworkUser object from a JSON object
|
||||
* @param jsonData Must be a JSON node of type object
|
||||
* @return Pointer to a new NetworkUser object
|
||||
* @note jsonData is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
static NetworkUser* FromJson(json_t& jsonData);
|
||||
|
||||
json_t* ToJson() const;
|
||||
json_t* ToJson(json_t* json) const;
|
||||
/**
|
||||
* Serialise a NetworkUser object into a JSON object
|
||||
*
|
||||
* @return JSON representation of the NetworkUser object
|
||||
*/
|
||||
json_t ToJson() const;
|
||||
};
|
||||
|
||||
class NetworkUserManager final
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# include "../PlatformEnvironment.h"
|
||||
# include "../config/Config.h"
|
||||
# include "../core/FileStream.hpp"
|
||||
# include "../core/Guard.hpp"
|
||||
# include "../core/Http.h"
|
||||
# include "../core/Json.hpp"
|
||||
# include "../core/Memory.hpp"
|
||||
@@ -70,35 +71,42 @@ bool ServerListEntry::IsVersionValid() const
|
||||
return Version.empty() || Version == network_get_version();
|
||||
}
|
||||
|
||||
std::optional<ServerListEntry> ServerListEntry::FromJson(const json_t* server)
|
||||
std::optional<ServerListEntry> ServerListEntry::FromJson(json_t& server)
|
||||
{
|
||||
auto port = json_object_get(server, "port");
|
||||
auto name = json_object_get(server, "name");
|
||||
auto description = json_object_get(server, "description");
|
||||
auto requiresPassword = json_object_get(server, "requiresPassword");
|
||||
auto version = json_object_get(server, "version");
|
||||
auto players = json_object_get(server, "players");
|
||||
auto maxPlayers = json_object_get(server, "maxPlayers");
|
||||
auto ip = json_object_get(server, "ip");
|
||||
auto ip4 = json_object_get(ip, "v4");
|
||||
auto addressIp = json_array_get(ip4, 0);
|
||||
Guard::Assert(server.is_object(), "ServerListEntry::FromJson expects parameter server to be object");
|
||||
|
||||
if (name == nullptr || version == nullptr)
|
||||
const auto port = Json::GetNumber<int32_t>(server["port"]);
|
||||
const auto name = Json::GetString(server["name"]);
|
||||
const auto description = Json::GetString(server["description"]);
|
||||
const auto requiresPassword = Json::GetBoolean(server["requiresPassword"]);
|
||||
const auto version = Json::GetString(server["version"]);
|
||||
const auto players = Json::GetNumber<uint8_t>(server["players"]);
|
||||
const auto maxPlayers = Json::GetNumber<uint8_t>(server["maxPlayers"]);
|
||||
std::string ip;
|
||||
// if server["ip"] or server["ip"]["v4"] are values, this will throw an exception, so check first
|
||||
if (server["ip"].is_object() && server["ip"]["v4"].is_array())
|
||||
{
|
||||
ip = Json::GetString(server["ip"]["v4"][0]);
|
||||
}
|
||||
|
||||
if (name.empty() || version.empty())
|
||||
{
|
||||
log_verbose("Cowardly refusing to add server without name or version specified.");
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerListEntry entry;
|
||||
entry.Address = String::StdFormat(
|
||||
"%s:%d", json_string_value(addressIp), static_cast<int32_t>(json_integer_value(port)));
|
||||
entry.Name = (name == nullptr ? "" : json_string_value(name));
|
||||
entry.Description = (description == nullptr ? "" : json_string_value(description));
|
||||
entry.Version = json_string_value(version);
|
||||
entry.RequiresPassword = json_is_true(requiresPassword);
|
||||
entry.Players = static_cast<uint8_t>(json_integer_value(players));
|
||||
entry.MaxPlayers = static_cast<uint8_t>(json_integer_value(maxPlayers));
|
||||
|
||||
entry.Address = ip + ":" + std::to_string(port);
|
||||
entry.Name = name;
|
||||
entry.Description = description;
|
||||
entry.Version = version;
|
||||
entry.RequiresPassword = requiresPassword;
|
||||
entry.Players = players;
|
||||
entry.MaxPlayers = maxPlayers;
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@@ -263,20 +271,17 @@ std::future<std::vector<ServerListEntry>> ServerList::FetchLocalServerListAsync(
|
||||
log_verbose("Received %zu bytes back from %s", recievedLen, sender.c_str());
|
||||
auto jinfo = Json::FromString(std::string_view(buffer));
|
||||
|
||||
auto ip4 = json_array();
|
||||
json_array_append_new(ip4, json_string(sender.c_str()));
|
||||
auto ip = json_object();
|
||||
json_object_set_new(ip, "v4", ip4);
|
||||
json_object_set_new(jinfo, "ip", ip);
|
||||
|
||||
auto entry = ServerListEntry::FromJson(jinfo);
|
||||
if (entry.has_value())
|
||||
if (jinfo.is_object())
|
||||
{
|
||||
(*entry).Local = true;
|
||||
entries.push_back(*entry);
|
||||
}
|
||||
jinfo["ip"] = { { "v4", { sender } } };
|
||||
|
||||
json_decref(jinfo);
|
||||
auto entry = ServerListEntry::FromJson(jinfo);
|
||||
if (entry.has_value())
|
||||
{
|
||||
(*entry).Local = true;
|
||||
entries.push_back(*entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -341,7 +346,7 @@ std::future<std::vector<ServerListEntry>> ServerList::FetchOnlineServerListAsync
|
||||
request.method = Http::Method::GET;
|
||||
request.header["Accept"] = "application/json";
|
||||
Http::DoAsync(request, [p](Http::Response& response) -> void {
|
||||
json_t* root{};
|
||||
json_t root;
|
||||
try
|
||||
{
|
||||
if (response.status != Http::Status::OK)
|
||||
@@ -350,46 +355,46 @@ std::future<std::vector<ServerListEntry>> ServerList::FetchOnlineServerListAsync
|
||||
}
|
||||
|
||||
root = Json::FromString(response.body);
|
||||
auto jsonStatus = json_object_get(root, "status");
|
||||
if (!json_is_number(jsonStatus))
|
||||
if (root.is_object())
|
||||
{
|
||||
throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_NUMBER);
|
||||
}
|
||||
|
||||
auto status = static_cast<int32_t>(json_integer_value(jsonStatus));
|
||||
if (status != 200)
|
||||
{
|
||||
throw MasterServerException(STR_SERVER_LIST_MASTER_SERVER_FAILED);
|
||||
}
|
||||
|
||||
auto jServers = json_object_get(root, "servers");
|
||||
if (!json_is_array(jServers))
|
||||
{
|
||||
throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_ARRAY);
|
||||
}
|
||||
|
||||
std::vector<ServerListEntry> entries;
|
||||
auto count = json_array_size(jServers);
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
auto jServer = json_array_get(jServers, i);
|
||||
if (json_is_object(jServer))
|
||||
auto jsonStatus = root["status"];
|
||||
if (!jsonStatus.is_number_integer())
|
||||
{
|
||||
auto entry = ServerListEntry::FromJson(jServer);
|
||||
if (entry.has_value())
|
||||
throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_NUMBER);
|
||||
}
|
||||
|
||||
auto status = Json::GetNumber<int32_t>(jsonStatus);
|
||||
if (status != 200)
|
||||
{
|
||||
throw MasterServerException(STR_SERVER_LIST_MASTER_SERVER_FAILED);
|
||||
}
|
||||
|
||||
auto jServers = root["servers"];
|
||||
if (!jServers.is_array())
|
||||
{
|
||||
throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_ARRAY);
|
||||
}
|
||||
|
||||
std::vector<ServerListEntry> entries;
|
||||
for (auto& jServer : jServers)
|
||||
{
|
||||
if (jServer.is_object())
|
||||
{
|
||||
entries.push_back(*entry);
|
||||
auto entry = ServerListEntry::FromJson(jServer);
|
||||
if (entry.has_value())
|
||||
{
|
||||
entries.push_back(*entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p->set_value(entries);
|
||||
p->set_value(entries);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
p->set_exception(std::current_exception());
|
||||
}
|
||||
json_decref(root);
|
||||
});
|
||||
return f;
|
||||
# endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
|
||||
#include <future>
|
||||
#include <optional>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct json_t;
|
||||
struct INetworkEndpoint;
|
||||
|
||||
struct ServerListEntry
|
||||
@@ -35,7 +35,14 @@ struct ServerListEntry
|
||||
int32_t CompareTo(const ServerListEntry& other) const;
|
||||
bool IsVersionValid() const;
|
||||
|
||||
static std::optional<ServerListEntry> FromJson(const json_t* root);
|
||||
/**
|
||||
* Creates a ServerListEntry object from a JSON object
|
||||
*
|
||||
* @param json JSON data source - must be object type
|
||||
* @return A NetworkGroup object
|
||||
* @note json is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
static std::optional<ServerListEntry> FromJson(json_t& server);
|
||||
};
|
||||
|
||||
class ServerList
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define MAX_SERVER_DESCRIPTION_LENGTH 256
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../localisation/StringIds.h"
|
||||
#include "NetworkTypes.h"
|
||||
|
||||
@@ -22,7 +23,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct json_t;
|
||||
struct GameAction;
|
||||
struct Peep;
|
||||
struct CoordsXYZ;
|
||||
@@ -117,4 +117,4 @@ std::string network_get_version();
|
||||
|
||||
NetworkStats_t network_get_stats();
|
||||
NetworkServerState_t network_get_server_state();
|
||||
json_t* network_get_server_info_as_json();
|
||||
json_t network_get_server_info_as_json();
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "../localisation/Language.h"
|
||||
#include "../object/Object.h"
|
||||
#include "../object/ObjectRepository.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
#include "ObjectList.h"
|
||||
|
||||
void BannerObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream)
|
||||
@@ -82,20 +81,23 @@ void BannerObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t he
|
||||
gfx_draw_sprite(dpi, imageId + 1, screenCoords + ScreenCoordsXY{ -12, 8 }, 0);
|
||||
}
|
||||
|
||||
void BannerObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void BannerObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
Guard::Assert(root.is_object(), "BannerObject::ReadJson expects parameter root to be object");
|
||||
json_t properties = root["properties"];
|
||||
|
||||
_legacyType.banner.scrolling_mode = json_integer_value(json_object_get(properties, "scrollingMode"));
|
||||
_legacyType.banner.price = json_integer_value(json_object_get(properties, "price"));
|
||||
_legacyType.banner.flags = ObjectJsonHelpers::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasPrimaryColour", BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR },
|
||||
});
|
||||
if (properties.is_object())
|
||||
{
|
||||
_legacyType.banner.scrolling_mode = Json::GetNumber<uint8_t>(properties["scrollingMode"]);
|
||||
_legacyType.banner.price = Json::GetNumber<int16_t>(properties["price"]);
|
||||
_legacyType.banner.flags = Json::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasPrimaryColour", BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR },
|
||||
});
|
||||
|
||||
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
|
||||
SetPrimarySceneryGroup(Json::GetString(properties["sceneryGroup"]));
|
||||
}
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "../core/String.hpp"
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../localisation/Localisation.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
void EntranceObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream)
|
||||
{
|
||||
@@ -51,12 +50,17 @@ void EntranceObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t
|
||||
gfx_draw_sprite(dpi, imageId + 2, screenCoords + ScreenCoordsXY{ 32, 44 }, 0);
|
||||
}
|
||||
|
||||
void EntranceObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void EntranceObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
_legacyType.scrolling_mode = json_integer_value(json_object_get(properties, "scrollingMode"));
|
||||
_legacyType.text_height = json_integer_value(json_object_get(properties, "textHeight"));
|
||||
Guard::Assert(root.is_object(), "EntranceObject::ReadJson expects parameter root to be object");
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
json_t properties = root["properties"];
|
||||
|
||||
if (properties.is_object())
|
||||
{
|
||||
_legacyType.scrolling_mode = Json::GetNumber<uint8_t>(properties["scrollingMode"]);
|
||||
_legacyType.text_height = Json::GetNumber<uint8_t>(properties["textHeight"]);
|
||||
}
|
||||
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "../localisation/Localisation.h"
|
||||
#include "../object/Object.h"
|
||||
#include "../object/ObjectRepository.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
#include "ObjectList.h"
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -98,40 +97,36 @@ static uint8_t ParseDrawType(const std::string& s)
|
||||
return PATH_BIT_DRAW_TYPE_LIGHTS;
|
||||
}
|
||||
|
||||
void FootpathItemObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void FootpathItemObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
_legacyType.path_bit.draw_type = ParseDrawType(ObjectJsonHelpers::GetString(properties, "renderAs"));
|
||||
_legacyType.path_bit.tool_id = ObjectJsonHelpers::ParseCursor(
|
||||
ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_LAMPPOST_DOWN);
|
||||
_legacyType.path_bit.price = json_integer_value(json_object_get(properties, "price"));
|
||||
Guard::Assert(root.is_object(), "FootpathItemObject::ReadJson expects parameter root to be object");
|
||||
|
||||
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
|
||||
json_t properties = root["properties"];
|
||||
|
||||
// Flags
|
||||
_legacyType.path_bit.flags = ObjectJsonHelpers::GetFlags<uint16_t>(
|
||||
properties,
|
||||
{
|
||||
{ "isBin", PATH_BIT_FLAG_IS_BIN },
|
||||
{ "isBench", PATH_BIT_FLAG_IS_BENCH },
|
||||
{ "isBreakable", PATH_BIT_FLAG_BREAKABLE },
|
||||
{ "isLamp", PATH_BIT_FLAG_LAMP },
|
||||
{ "isJumpingFountainWater", PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER },
|
||||
{ "isJumpingFountainSnow", PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW },
|
||||
{ "isTelevision", PATH_BIT_FLAG_IS_QUEUE_SCREEN },
|
||||
});
|
||||
|
||||
// HACK To avoid 'negated' properties in JSON, handle these separately until
|
||||
// flags are inverted in this code base.
|
||||
if (!ObjectJsonHelpers::GetBoolean(properties, "isAllowedOnQueue", false))
|
||||
if (properties.is_object())
|
||||
{
|
||||
_legacyType.path_bit.flags |= PATH_BIT_FLAG_DONT_ALLOW_ON_QUEUE;
|
||||
}
|
||||
if (!ObjectJsonHelpers::GetBoolean(properties, "isAllowedOnSlope", false))
|
||||
{
|
||||
_legacyType.path_bit.flags |= PATH_BIT_FLAG_DONT_ALLOW_ON_SLOPE;
|
||||
_legacyType.path_bit.draw_type = ParseDrawType(Json::GetString(properties["renderAs"]));
|
||||
_legacyType.path_bit.tool_id = Cursor::FromString(Json::GetString(properties["cursor"]), CURSOR_LAMPPOST_DOWN);
|
||||
_legacyType.path_bit.price = Json::GetNumber<int16_t>(properties["price"]);
|
||||
|
||||
SetPrimarySceneryGroup(Json::GetString(properties["sceneryGroup"]));
|
||||
|
||||
// clang-format off
|
||||
_legacyType.path_bit.flags = Json::GetFlags<uint16_t>(
|
||||
properties,
|
||||
{
|
||||
{ "isBin", PATH_BIT_FLAG_IS_BIN, Json::FlagType::Normal },
|
||||
{ "isBench", PATH_BIT_FLAG_IS_BENCH, Json::FlagType::Normal },
|
||||
{ "isBreakable", PATH_BIT_FLAG_BREAKABLE, Json::FlagType::Normal },
|
||||
{ "isLamp", PATH_BIT_FLAG_LAMP, Json::FlagType::Normal },
|
||||
{ "isJumpingFountainWater", PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER, Json::FlagType::Normal },
|
||||
{ "isJumpingFountainSnow", PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW, Json::FlagType::Normal },
|
||||
{ "isAllowedOnQueue", PATH_BIT_FLAG_DONT_ALLOW_ON_QUEUE, Json::FlagType::Inverted },
|
||||
{ "isAllowedOnSlope", PATH_BIT_FLAG_DONT_ALLOW_ON_SLOPE, Json::FlagType::Inverted },
|
||||
{ "isTelevision", PATH_BIT_FLAG_IS_QUEUE_SCREEN, Json::FlagType::Normal },
|
||||
});
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
#include "FootpathObject.h"
|
||||
|
||||
#include "../core/IStream.hpp"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../world/Footpath.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
void FootpathObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream)
|
||||
{
|
||||
@@ -83,21 +83,25 @@ static RailingEntrySupportType ParseSupportType(const std::string& s)
|
||||
return RailingEntrySupportType::Box;
|
||||
}
|
||||
|
||||
void FootpathObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void FootpathObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
_legacyType.support_type = ParseSupportType(ObjectJsonHelpers::GetString(json_object_get(properties, "supportType")));
|
||||
_legacyType.scrolling_mode = json_integer_value(json_object_get(properties, "scrollingMode"));
|
||||
Guard::Assert(root.is_object(), "FootpathObject::ReadJson expects parameter root to be object");
|
||||
|
||||
// Flags
|
||||
_legacyType.flags = ObjectJsonHelpers::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasSupportImages", RAILING_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE },
|
||||
{ "hasElevatedPathImages", RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS },
|
||||
{ "editorOnly", FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR },
|
||||
});
|
||||
auto properties = root["properties"];
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
if (properties.is_object())
|
||||
{
|
||||
_legacyType.support_type = ParseSupportType(Json::GetString(properties["supportType"]));
|
||||
_legacyType.scrolling_mode = Json::GetNumber<uint8_t>(properties["scrollingMode"]);
|
||||
|
||||
_legacyType.flags = Json::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasSupportImages", RAILING_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE },
|
||||
{ "hasElevatedPathImages", RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS },
|
||||
{ "editorOnly", FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR },
|
||||
});
|
||||
}
|
||||
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -9,14 +9,285 @@
|
||||
|
||||
#include "ImageTable.h"
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../PlatformEnvironment.h"
|
||||
#include "../core/File.h"
|
||||
#include "../core/FileScanner.h"
|
||||
#include "../core/IStream.hpp"
|
||||
#include "../core/Path.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../drawing/ImageImporter.h"
|
||||
#include "../sprites.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectFactory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
||||
struct ImageTable::RequiredImage
|
||||
{
|
||||
rct_g1_element g1{};
|
||||
std::unique_ptr<RequiredImage> next_zoom;
|
||||
|
||||
bool HasData() const
|
||||
{
|
||||
return g1.offset != nullptr;
|
||||
}
|
||||
|
||||
RequiredImage() = default;
|
||||
RequiredImage(const RequiredImage&) = delete;
|
||||
|
||||
RequiredImage(const rct_g1_element& orig)
|
||||
{
|
||||
auto length = g1_calculate_data_size(&orig);
|
||||
g1 = orig;
|
||||
g1.offset = new uint8_t[length];
|
||||
std::memcpy(g1.offset, orig.offset, length);
|
||||
g1.flags &= ~G1_FLAG_HAS_ZOOM_SPRITE;
|
||||
}
|
||||
|
||||
RequiredImage(uint32_t idx, std::function<const rct_g1_element*(uint32_t)> getter)
|
||||
{
|
||||
auto orig = getter(idx);
|
||||
if (orig != nullptr)
|
||||
{
|
||||
auto length = g1_calculate_data_size(orig);
|
||||
g1 = *orig;
|
||||
g1.offset = new uint8_t[length];
|
||||
std::memcpy(g1.offset, orig->offset, length);
|
||||
if ((g1.flags & G1_FLAG_HAS_ZOOM_SPRITE) && g1.zoomed_offset != 0)
|
||||
{
|
||||
// Fetch image for next zoom level
|
||||
next_zoom = std::make_unique<RequiredImage>(static_cast<uint32_t>(idx - g1.zoomed_offset), getter);
|
||||
if (!next_zoom->HasData())
|
||||
{
|
||||
next_zoom = nullptr;
|
||||
g1.flags &= ~G1_FLAG_HAS_ZOOM_SPRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~RequiredImage()
|
||||
{
|
||||
delete[] g1.offset;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(IReadObjectContext* context, std::string s)
|
||||
{
|
||||
std::vector<std::unique_ptr<RequiredImage>> result;
|
||||
if (s.empty())
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
else if (String::StartsWith(s, "$CSG"))
|
||||
{
|
||||
if (is_csg_loaded())
|
||||
{
|
||||
auto range = ParseRange(s.substr(4));
|
||||
if (!range.empty())
|
||||
{
|
||||
for (auto i : range)
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>(
|
||||
static_cast<uint32_t>(SPR_CSG_BEGIN + i),
|
||||
[](uint32_t idx) -> const rct_g1_element* { return gfx_get_g1_element(idx); }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (String::StartsWith(s, "$G1"))
|
||||
{
|
||||
auto range = ParseRange(s.substr(3));
|
||||
if (!range.empty())
|
||||
{
|
||||
for (auto i : range)
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>(
|
||||
static_cast<uint32_t>(i), [](uint32_t idx) -> const rct_g1_element* { return gfx_get_g1_element(idx); }));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (String::StartsWith(s, "$RCT2:OBJDATA/"))
|
||||
{
|
||||
auto name = s.substr(14);
|
||||
auto rangeStart = name.find('[');
|
||||
if (rangeStart != std::string::npos)
|
||||
{
|
||||
auto rangeString = name.substr(rangeStart);
|
||||
auto range = ParseRange(name.substr(rangeStart));
|
||||
name = name.substr(0, rangeStart);
|
||||
result = LoadObjectImages(context, name, range);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
auto imageData = context->GetData(s);
|
||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
||||
|
||||
ImageImporter importer;
|
||||
auto importResult = importer.Import(image, 0, 0, ImageImporter::IMPORT_FLAGS::RLE);
|
||||
|
||||
result.push_back(std::make_unique<RequiredImage>(importResult.Element));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
auto msg = String::StdFormat("Unable to load image '%s': %s", s.c_str(), e.what());
|
||||
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, msg.c_str());
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(IReadObjectContext* context, json_t& el)
|
||||
{
|
||||
Guard::Assert(el.is_object(), "ImageTable::ParseImages expects parameter el to be object");
|
||||
|
||||
auto path = Json::GetString(el["path"]);
|
||||
auto x = Json::GetNumber<int16_t>(el["x"]);
|
||||
auto y = Json::GetNumber<int16_t>(el["y"]);
|
||||
auto raw = Json::GetString(el["format"]) == "raw";
|
||||
|
||||
std::vector<std::unique_ptr<RequiredImage>> result;
|
||||
try
|
||||
{
|
||||
auto flags = ImageImporter::IMPORT_FLAGS::NONE;
|
||||
if (!raw)
|
||||
{
|
||||
flags = static_cast<ImageImporter::IMPORT_FLAGS>(flags | ImageImporter::IMPORT_FLAGS::RLE);
|
||||
}
|
||||
auto imageData = context->GetData(path);
|
||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
||||
|
||||
ImageImporter importer;
|
||||
auto importResult = importer.Import(image, 0, 0, flags);
|
||||
auto g1Element = importResult.Element;
|
||||
g1Element.x_offset = x;
|
||||
g1Element.y_offset = y;
|
||||
result.push_back(std::make_unique<RequiredImage>(g1Element));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
auto msg = String::StdFormat("Unable to load image '%s': %s", path.c_str(), e.what());
|
||||
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, msg.c_str());
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::LoadObjectImages(
|
||||
IReadObjectContext* context, const std::string& name, const std::vector<int32_t>& range)
|
||||
{
|
||||
std::vector<std::unique_ptr<RequiredImage>> result;
|
||||
auto objectPath = FindLegacyObject(name);
|
||||
auto obj = ObjectFactory::CreateObjectFromLegacyFile(context->GetObjectRepository(), objectPath.c_str());
|
||||
if (obj != nullptr)
|
||||
{
|
||||
auto& imgTable = static_cast<const Object*>(obj)->GetImageTable();
|
||||
auto numImages = static_cast<int32_t>(imgTable.GetCount());
|
||||
auto images = imgTable.GetImages();
|
||||
size_t placeHoldersAdded = 0;
|
||||
for (auto i : range)
|
||||
{
|
||||
if (i >= 0 && i < numImages)
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>(
|
||||
static_cast<uint32_t>(i), [images](uint32_t idx) -> const rct_g1_element* { return &images[idx]; }));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
placeHoldersAdded++;
|
||||
}
|
||||
}
|
||||
delete obj;
|
||||
|
||||
// Log place holder information
|
||||
if (placeHoldersAdded > 0)
|
||||
{
|
||||
std::string msg = "Adding " + std::to_string(placeHoldersAdded) + " placeholders";
|
||||
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, msg.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string msg = "Unable to open '" + objectPath + "'";
|
||||
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, msg.c_str());
|
||||
for (size_t i = 0; i < range.size(); i++)
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int32_t> ImageTable::ParseRange(std::string s)
|
||||
{
|
||||
// Currently only supports [###] or [###..###]
|
||||
std::vector<int32_t> result = {};
|
||||
if (s.length() >= 3 && s[0] == '[' && s[s.length() - 1] == ']')
|
||||
{
|
||||
s = s.substr(1, s.length() - 2);
|
||||
auto parts = String::Split(s, "..");
|
||||
if (parts.size() == 1)
|
||||
{
|
||||
result.push_back(std::stoi(parts[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto left = std::stoi(parts[0]);
|
||||
auto right = std::stoi(parts[1]);
|
||||
if (left <= right)
|
||||
{
|
||||
for (auto i = left; i <= right; i++)
|
||||
{
|
||||
result.push_back(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto i = right; i >= left; i--)
|
||||
{
|
||||
result.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ImageTable::FindLegacyObject(const std::string& name)
|
||||
{
|
||||
const auto env = GetContext()->GetPlatformEnvironment();
|
||||
auto objectsPath = env->GetDirectoryPath(DIRBASE::RCT2, DIRID::OBJECT);
|
||||
auto objectPath = Path::Combine(objectsPath, name);
|
||||
if (!File::Exists(objectPath))
|
||||
{
|
||||
// Search recursively for any file with the target name (case insensitive)
|
||||
auto filter = Path::Combine(objectsPath, "*.dat");
|
||||
auto scanner = std::unique_ptr<IFileScanner>(Path::ScanDirectory(filter, true));
|
||||
while (scanner->Next())
|
||||
{
|
||||
auto currentName = Path::GetFileName(scanner->GetPathRelative());
|
||||
if (String::Equals(currentName, name, true))
|
||||
{
|
||||
objectPath = scanner->GetPath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return objectPath;
|
||||
}
|
||||
|
||||
ImageTable::~ImageTable()
|
||||
{
|
||||
if (_data == nullptr)
|
||||
@@ -97,6 +368,70 @@ void ImageTable::Read(IReadObjectContext* context, OpenRCT2::IStream* stream)
|
||||
}
|
||||
}
|
||||
|
||||
void ImageTable::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
Guard::Assert(root.is_object(), "ImageTable::ReadJson expects parameter root to be object");
|
||||
|
||||
if (context->ShouldLoadImages())
|
||||
{
|
||||
// First gather all the required images from inspecting the JSON
|
||||
std::vector<std::unique_ptr<RequiredImage>> allImages;
|
||||
auto jsonImages = root["images"];
|
||||
|
||||
for (auto& jsonImage : jsonImages)
|
||||
{
|
||||
if (jsonImage.is_string())
|
||||
{
|
||||
auto strImage = jsonImage.get<std::string>();
|
||||
auto images = ParseImages(context, strImage);
|
||||
allImages.insert(
|
||||
allImages.end(), std::make_move_iterator(images.begin()), std::make_move_iterator(images.end()));
|
||||
}
|
||||
else if (jsonImage.is_object())
|
||||
{
|
||||
auto images = ParseImages(context, jsonImage);
|
||||
allImages.insert(
|
||||
allImages.end(), std::make_move_iterator(images.begin()), std::make_move_iterator(images.end()));
|
||||
}
|
||||
}
|
||||
|
||||
// Now add all the images to the image table
|
||||
auto imagesStartIndex = GetCount();
|
||||
for (const auto& img : allImages)
|
||||
{
|
||||
const auto& g1 = img->g1;
|
||||
AddImage(&g1);
|
||||
}
|
||||
|
||||
// Add all the zoom images at the very end of the image table.
|
||||
// This way it should not affect the offsets used within the object logic.
|
||||
for (size_t j = 0; j < allImages.size(); j++)
|
||||
{
|
||||
const auto tableIndex = imagesStartIndex + j;
|
||||
const auto* img = allImages[j].get();
|
||||
if (img->next_zoom != nullptr)
|
||||
{
|
||||
img = img->next_zoom.get();
|
||||
|
||||
// Set old image zoom offset to zoom image which we are about to add
|
||||
auto g1a = const_cast<rct_g1_element*>(&GetImages()[tableIndex]);
|
||||
g1a->zoomed_offset = static_cast<int32_t>(tableIndex) - static_cast<int32_t>(GetCount());
|
||||
|
||||
while (img != nullptr)
|
||||
{
|
||||
auto g1b = img->g1;
|
||||
if (img->next_zoom != nullptr)
|
||||
{
|
||||
g1b.zoomed_offset = -1;
|
||||
}
|
||||
AddImage(&g1b);
|
||||
img = img->next_zoom.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageTable::AddImage(const rct_g1_element* g1)
|
||||
{
|
||||
rct_g1_element newg1 = *g1;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../drawing/Drawing.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -27,6 +28,20 @@ private:
|
||||
std::unique_ptr<uint8_t[]> _data;
|
||||
std::vector<rct_g1_element> _entries;
|
||||
|
||||
/**
|
||||
* Container for a G1 image, additional information and RAII. Used by ReadJson
|
||||
*/
|
||||
struct RequiredImage;
|
||||
static std::vector<std::unique_ptr<ImageTable::RequiredImage>> ParseImages(IReadObjectContext* context, std::string s);
|
||||
/**
|
||||
* @note root is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
static std::vector<std::unique_ptr<ImageTable::RequiredImage>> ParseImages(IReadObjectContext* context, json_t& el);
|
||||
static std::vector<std::unique_ptr<ImageTable::RequiredImage>> LoadObjectImages(
|
||||
IReadObjectContext* context, const std::string& name, const std::vector<int32_t>& range);
|
||||
static std::vector<int32_t> ParseRange(std::string s);
|
||||
static std::string FindLegacyObject(const std::string& name);
|
||||
|
||||
public:
|
||||
ImageTable() = default;
|
||||
ImageTable(const ImageTable&) = delete;
|
||||
@@ -34,6 +49,10 @@ public:
|
||||
~ImageTable();
|
||||
|
||||
void Read(IReadObjectContext* context, OpenRCT2::IStream* stream);
|
||||
/**
|
||||
* @note root is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
void ReadJson(IReadObjectContext* context, json_t& root);
|
||||
const rct_g1_element* GetImages() const
|
||||
{
|
||||
return _entries.data();
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
#include "LargeSceneryObject.h"
|
||||
|
||||
#include "../core/IStream.hpp"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../interface/Cursors.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../world/Banner.h"
|
||||
#include "../world/Location.hpp"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
@@ -121,84 +121,83 @@ std::vector<rct_large_scenery_tile> LargeSceneryObject::ReadTiles(OpenRCT2::IStr
|
||||
return tiles;
|
||||
}
|
||||
|
||||
void LargeSceneryObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void LargeSceneryObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
Guard::Assert(root.is_object(), "LargeSceneryObject::ReadJson expects parameter root to be object");
|
||||
|
||||
_legacyType.large_scenery.tool_id = ObjectJsonHelpers::ParseCursor(
|
||||
ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_STATUE_DOWN);
|
||||
_legacyType.large_scenery.price = json_integer_value(json_object_get(properties, "price"));
|
||||
_legacyType.large_scenery.removal_price = json_integer_value(json_object_get(properties, "removalPrice"));
|
||||
auto properties = root["properties"];
|
||||
|
||||
auto jScrollingMode = json_object_get(properties, "scrollingMode");
|
||||
_legacyType.large_scenery.scrolling_mode = jScrollingMode != nullptr ? json_integer_value(jScrollingMode)
|
||||
: SCROLLING_MODE_NONE;
|
||||
if (properties.is_object())
|
||||
{
|
||||
_legacyType.large_scenery.tool_id = Cursor::FromString(Json::GetString(properties["cursor"]), CURSOR_STATUE_DOWN);
|
||||
|
||||
// Flags
|
||||
_legacyType.large_scenery.flags = ObjectJsonHelpers::GetFlags<uint8_t>(
|
||||
properties,
|
||||
_legacyType.large_scenery.price = Json::GetNumber<int16_t>(properties["price"]);
|
||||
_legacyType.large_scenery.removal_price = Json::GetNumber<int16_t>(properties["removalPrice"]);
|
||||
|
||||
_legacyType.large_scenery.scrolling_mode = Json::GetNumber<uint8_t>(properties["scrollingMode"], SCROLLING_MODE_NONE);
|
||||
|
||||
_legacyType.large_scenery.flags = Json::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasPrimaryColour", LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR },
|
||||
{ "hasSecondaryColour", LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR },
|
||||
{ "isAnimated", LARGE_SCENERY_FLAG_ANIMATED },
|
||||
{ "isPhotogenic", LARGE_SCENERY_FLAG_PHOTOGENIC },
|
||||
});
|
||||
|
||||
// Tiles
|
||||
auto jTiles = properties["tiles"];
|
||||
if (jTiles.is_array())
|
||||
{
|
||||
{ "hasPrimaryColour", LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR },
|
||||
{ "hasSecondaryColour", LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR },
|
||||
{ "isAnimated", LARGE_SCENERY_FLAG_ANIMATED },
|
||||
{ "isPhotogenic", LARGE_SCENERY_FLAG_PHOTOGENIC },
|
||||
});
|
||||
_tiles = ReadJsonTiles(jTiles);
|
||||
}
|
||||
|
||||
// Tiles
|
||||
auto jTiles = json_object_get(properties, "tiles");
|
||||
if (jTiles != nullptr)
|
||||
{
|
||||
_tiles = ReadJsonTiles(jTiles);
|
||||
// Read text
|
||||
auto j3dFont = properties["3dFont"];
|
||||
if (j3dFont.is_object())
|
||||
{
|
||||
_3dFont = ReadJson3dFont(j3dFont);
|
||||
_legacyType.large_scenery.flags |= LARGE_SCENERY_FLAG_3D_TEXT;
|
||||
}
|
||||
|
||||
SetPrimarySceneryGroup(Json::GetString(properties["sceneryGroup"]));
|
||||
}
|
||||
|
||||
// Read text
|
||||
auto j3dFont = json_object_get(properties, "3dFont");
|
||||
if (j3dFont != nullptr)
|
||||
{
|
||||
_3dFont = ReadJson3dFont(j3dFont);
|
||||
_legacyType.large_scenery.flags |= LARGE_SCENERY_FLAG_3D_TEXT;
|
||||
}
|
||||
|
||||
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
std::vector<rct_large_scenery_tile> LargeSceneryObject::ReadJsonTiles(const json_t* jTiles)
|
||||
std::vector<rct_large_scenery_tile> LargeSceneryObject::ReadJsonTiles(json_t& jTiles)
|
||||
{
|
||||
std::vector<rct_large_scenery_tile> tiles;
|
||||
size_t index;
|
||||
const json_t* jTile;
|
||||
json_array_foreach(jTiles, index, jTile)
|
||||
|
||||
for (auto& jTile : jTiles)
|
||||
{
|
||||
rct_large_scenery_tile tile = {};
|
||||
tile.x_offset = json_integer_value(json_object_get(jTile, "x"));
|
||||
tile.y_offset = json_integer_value(json_object_get(jTile, "y"));
|
||||
tile.z_offset = json_integer_value(json_object_get(jTile, "z"));
|
||||
tile.z_clearance = json_integer_value(json_object_get(jTile, "clearance"));
|
||||
if (!ObjectJsonHelpers::GetBoolean(jTile, "hasSupports"))
|
||||
if (jTile.is_object())
|
||||
{
|
||||
tile.flags |= LARGE_SCENERY_TILE_FLAG_NO_SUPPORTS;
|
||||
}
|
||||
if (ObjectJsonHelpers::GetBoolean(jTile, "allowSupportsAbove"))
|
||||
{
|
||||
tile.flags |= LARGE_SCENERY_TILE_FLAG_ALLOW_SUPPORTS_ABOVE;
|
||||
}
|
||||
rct_large_scenery_tile tile = {};
|
||||
tile.x_offset = Json::GetNumber<int16_t>(jTile["x"]);
|
||||
tile.y_offset = Json::GetNumber<int16_t>(jTile["y"]);
|
||||
tile.z_offset = Json::GetNumber<int16_t>(jTile["z"]);
|
||||
tile.z_clearance = Json::GetNumber<int8_t>(jTile["clearance"]);
|
||||
|
||||
// All corners are by default occupied
|
||||
auto jCorners = json_object_get(jTile, "corners");
|
||||
auto corners = 0xF;
|
||||
if (jCorners != nullptr)
|
||||
{
|
||||
corners = json_integer_value(jCorners);
|
||||
// clang-format off
|
||||
tile.flags = Json::GetFlags<uint16_t>(
|
||||
jTile,
|
||||
{
|
||||
{"hasSupports", LARGE_SCENERY_TILE_FLAG_NO_SUPPORTS, Json::FlagType::Inverted},
|
||||
{"allowSupportsAbove", LARGE_SCENERY_TILE_FLAG_ALLOW_SUPPORTS_ABOVE, Json::FlagType::Normal}
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
// All corners are by default occupied
|
||||
uint16_t corners = Json::GetNumber<uint16_t>(jTile["corners"], 0xF);
|
||||
tile.flags |= (corners & 0xFF) << 12;
|
||||
|
||||
auto walls = Json::GetNumber<int16_t>(jTile["walls"]);
|
||||
tile.flags |= (walls & 0xFF) << 8;
|
||||
|
||||
tiles.push_back(tile);
|
||||
}
|
||||
tile.flags |= (corners & 0xFF) << 12;
|
||||
|
||||
auto walls = json_integer_value(json_object_get(jTile, "walls"));
|
||||
tile.flags |= (walls & 0xFF) << 8;
|
||||
|
||||
tiles.push_back(tile);
|
||||
}
|
||||
|
||||
// HACK Add end of tiles marker
|
||||
@@ -208,29 +207,32 @@ std::vector<rct_large_scenery_tile> LargeSceneryObject::ReadJsonTiles(const json
|
||||
return tiles;
|
||||
}
|
||||
|
||||
std::unique_ptr<rct_large_scenery_text> LargeSceneryObject::ReadJson3dFont(const json_t* j3dFont)
|
||||
std::unique_ptr<rct_large_scenery_text> LargeSceneryObject::ReadJson3dFont(json_t& j3dFont)
|
||||
{
|
||||
Guard::Assert(j3dFont.is_object(), "LargeSceneryObject::ReadJson3dFont expects parameter j3dFont to be object");
|
||||
|
||||
auto font = std::make_unique<rct_large_scenery_text>();
|
||||
|
||||
auto jOffsets = json_object_get(j3dFont, "offsets");
|
||||
if (jOffsets != nullptr)
|
||||
auto jOffsets = j3dFont["offsets"];
|
||||
if (jOffsets.is_array())
|
||||
{
|
||||
auto offsets = ReadJsonOffsets(jOffsets);
|
||||
auto numOffsets = std::min(std::size(font->offset), offsets.size());
|
||||
std::copy_n(offsets.data(), numOffsets, font->offset);
|
||||
}
|
||||
|
||||
font->max_width = json_integer_value(json_object_get(j3dFont, "maxWidth"));
|
||||
font->num_images = json_integer_value(json_object_get(j3dFont, "numImages"));
|
||||
font->flags = ObjectJsonHelpers::GetFlags<uint8_t>(
|
||||
font->max_width = Json::GetNumber<uint16_t>(j3dFont["maxWidth"]);
|
||||
font->num_images = Json::GetNumber<uint8_t>(j3dFont["numImages"]);
|
||||
|
||||
font->flags = Json::GetFlags<uint8_t>(
|
||||
j3dFont,
|
||||
{
|
||||
{ "isVertical", LARGE_SCENERY_TEXT_FLAG_VERTICAL },
|
||||
{ "isTwoLine", LARGE_SCENERY_TEXT_FLAG_TWO_LINE },
|
||||
});
|
||||
|
||||
auto jGlyphs = json_object_get(j3dFont, "glyphs");
|
||||
if (jGlyphs != nullptr)
|
||||
auto jGlyphs = j3dFont["glyphs"];
|
||||
if (jGlyphs.is_array())
|
||||
{
|
||||
auto glyphs = ReadJsonGlyphs(jGlyphs);
|
||||
auto numGlyphs = std::min(std::size(font->glyphs), glyphs.size());
|
||||
@@ -240,33 +242,35 @@ std::unique_ptr<rct_large_scenery_text> LargeSceneryObject::ReadJson3dFont(const
|
||||
return font;
|
||||
}
|
||||
|
||||
std::vector<LocationXY16> LargeSceneryObject::ReadJsonOffsets(const json_t* jOffsets)
|
||||
std::vector<LocationXY16> LargeSceneryObject::ReadJsonOffsets(json_t& jOffsets)
|
||||
{
|
||||
std::vector<LocationXY16> offsets;
|
||||
size_t index;
|
||||
const json_t* jOffset;
|
||||
json_array_foreach(jOffsets, index, jOffset)
|
||||
for (auto& jOffset : jOffsets)
|
||||
{
|
||||
LocationXY16 offset = {};
|
||||
offset.x = json_integer_value(json_object_get(jOffset, "x"));
|
||||
offset.y = json_integer_value(json_object_get(jOffset, "y"));
|
||||
offsets.push_back(offset);
|
||||
if (jOffset.is_object())
|
||||
{
|
||||
LocationXY16 offset = {};
|
||||
offset.x = Json::GetNumber<int16_t>(jOffset["x"]);
|
||||
offset.y = Json::GetNumber<int16_t>(jOffset["y"]);
|
||||
offsets.push_back(offset);
|
||||
}
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
std::vector<rct_large_scenery_text_glyph> LargeSceneryObject::ReadJsonGlyphs(const json_t* jGlpyhs)
|
||||
std::vector<rct_large_scenery_text_glyph> LargeSceneryObject::ReadJsonGlyphs(json_t& jGlyphs)
|
||||
{
|
||||
std::vector<rct_large_scenery_text_glyph> glyphs;
|
||||
size_t index;
|
||||
const json_t* jGlyph;
|
||||
json_array_foreach(jGlpyhs, index, jGlyph)
|
||||
for (auto& jGlyph : jGlyphs)
|
||||
{
|
||||
rct_large_scenery_text_glyph glyph = {};
|
||||
glyph.image_offset = json_integer_value(json_object_get(jGlyph, "image"));
|
||||
glyph.width = json_integer_value(json_object_get(jGlyph, "width"));
|
||||
glyph.height = json_integer_value(json_object_get(jGlyph, "height"));
|
||||
glyphs.push_back(glyph);
|
||||
if (jGlyph.is_object())
|
||||
{
|
||||
rct_large_scenery_text_glyph glyph = {};
|
||||
glyph.image_offset = Json::GetNumber<uint8_t>(jGlyph["image"]);
|
||||
glyph.width = Json::GetNumber<uint8_t>(jGlyph["width"]);
|
||||
glyph.height = Json::GetNumber<uint8_t>(jGlyph["height"]);
|
||||
glyphs.push_back(glyph);
|
||||
}
|
||||
}
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
|
||||
private:
|
||||
static std::vector<rct_large_scenery_tile> ReadTiles(OpenRCT2::IStream* stream);
|
||||
static std::vector<rct_large_scenery_tile> ReadJsonTiles(const json_t* jTiles);
|
||||
static std::unique_ptr<rct_large_scenery_text> ReadJson3dFont(const json_t* j3dFont);
|
||||
static std::vector<LocationXY16> ReadJsonOffsets(const json_t* jOffsets);
|
||||
static std::vector<rct_large_scenery_text_glyph> ReadJsonGlyphs(const json_t* jGlpyhs);
|
||||
static std::vector<rct_large_scenery_tile> ReadJsonTiles(json_t& jTiles);
|
||||
static std::unique_ptr<rct_large_scenery_text> ReadJson3dFont(json_t& j3dFont);
|
||||
static std::vector<LocationXY16> ReadJsonOffsets(json_t& jOffsets);
|
||||
static std::vector<rct_large_scenery_text_glyph> ReadJsonGlyphs(json_t& jGlyphs);
|
||||
};
|
||||
|
||||
@@ -37,6 +37,21 @@ void Object::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream)
|
||||
throw std::runtime_error("Not supported.");
|
||||
}
|
||||
|
||||
void Object::PopulateTablesFromJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
_stringTable.ReadJson(root);
|
||||
_imageTable.ReadJson(context, root);
|
||||
}
|
||||
|
||||
rct_object_entry Object::ParseObjectEntry(const std::string& s)
|
||||
{
|
||||
rct_object_entry entry = {};
|
||||
std::fill_n(entry.name, sizeof(entry.name), ' ');
|
||||
auto copyLen = std::min<size_t>(8, s.size());
|
||||
std::copy_n(s.c_str(), copyLen, entry.name);
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::string Object::GetOverrideString(uint8_t index) const
|
||||
{
|
||||
auto legacyIdentifier = GetLegacyIdentifier();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "ImageTable.h"
|
||||
#include "StringTable.h"
|
||||
|
||||
@@ -141,7 +142,6 @@ namespace OpenRCT2
|
||||
}
|
||||
struct ObjectRepositoryItem;
|
||||
struct rct_drawpixelinfo;
|
||||
struct json_t;
|
||||
|
||||
struct IReadObjectContext
|
||||
{
|
||||
@@ -186,6 +186,16 @@ protected:
|
||||
return _imageTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the image and string tables from a JSON object
|
||||
* @param context
|
||||
* @param root JSON node of type object containing image and string info
|
||||
* @note root is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
void PopulateTablesFromJson(IReadObjectContext* context, json_t& root);
|
||||
|
||||
static rct_object_entry ParseObjectEntry(const std::string& s);
|
||||
|
||||
std::string GetOverrideString(uint8_t index) const;
|
||||
std::string GetString(ObjectStringID index) const;
|
||||
std::string GetString(int32_t language, ObjectStringID index) const;
|
||||
@@ -224,7 +234,10 @@ public:
|
||||
}
|
||||
virtual void* GetLegacyData();
|
||||
|
||||
virtual void ReadJson(IReadObjectContext* /*context*/, const json_t* /*root*/)
|
||||
/**
|
||||
* @note root is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
virtual void ReadJson(IReadObjectContext* /*context*/, json_t& /*root*/)
|
||||
{
|
||||
}
|
||||
virtual void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream);
|
||||
|
||||
@@ -160,8 +160,12 @@ public:
|
||||
|
||||
namespace ObjectFactory
|
||||
{
|
||||
/**
|
||||
* @param jRoot Must be JSON node of type object
|
||||
* @note jRoot is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
static Object* CreateObjectFromJson(
|
||||
IObjectRepository& objectRepository, const json_t* jRoot, const IFileDataRetriever* fileRetriever);
|
||||
IObjectRepository& objectRepository, json_t& jRoot, const IFileDataRetriever* fileRetriever);
|
||||
|
||||
static uint8_t ParseSourceGame(const std::string& s)
|
||||
{
|
||||
@@ -363,16 +367,16 @@ namespace ObjectFactory
|
||||
throw std::runtime_error("Unable to open object.json.");
|
||||
}
|
||||
|
||||
json_error_t jsonLoadError;
|
||||
auto jRoot = json_loadb(reinterpret_cast<const char*>(jsonBytes.data()), jsonBytes.size(), 0, &jsonLoadError);
|
||||
if (jRoot == nullptr)
|
||||
json_t jRoot = Json::FromVector(jsonBytes);
|
||||
|
||||
Object* obj = nullptr;
|
||||
|
||||
if (jRoot.is_object())
|
||||
{
|
||||
throw JsonException(&jsonLoadError);
|
||||
auto fileDataRetriever = ZipDataRetriever(*archive);
|
||||
obj = CreateObjectFromJson(objectRepository, jRoot, &fileDataRetriever);
|
||||
}
|
||||
|
||||
auto fileDataRetriever = ZipDataRetriever(*archive);
|
||||
Object* obj = CreateObjectFromJson(objectRepository, jRoot, &fileDataRetriever);
|
||||
json_decref(jRoot);
|
||||
return obj;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -392,96 +396,78 @@ namespace ObjectFactory
|
||||
Object* result = nullptr;
|
||||
try
|
||||
{
|
||||
auto jRoot = Json::ReadFromFile(path.c_str());
|
||||
json_t jRoot = Json::ReadFromFile(path.c_str());
|
||||
auto fileDataRetriever = FileSystemDataRetriever(Path::GetDirectory(path));
|
||||
result = CreateObjectFromJson(objectRepository, jRoot, &fileDataRetriever);
|
||||
json_decref(jRoot);
|
||||
}
|
||||
catch (const std::runtime_error& err)
|
||||
{
|
||||
Console::Error::WriteLine("Unable to open or read '%s': %s", path.c_str(), err.what());
|
||||
|
||||
delete result;
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Object* CreateObjectFromJson(
|
||||
IObjectRepository& objectRepository, const json_t* jRoot, const IFileDataRetriever* fileRetriever)
|
||||
Object* CreateObjectFromJson(IObjectRepository& objectRepository, json_t& jRoot, const IFileDataRetriever* fileRetriever)
|
||||
{
|
||||
Guard::Assert(jRoot.is_object(), "ObjectFactory::CreateObjectFromJson expects parameter jRoot to be object");
|
||||
|
||||
log_verbose("CreateObjectFromJson(...)");
|
||||
|
||||
Object* result = nullptr;
|
||||
auto jObjectType = json_object_get(jRoot, "objectType");
|
||||
if (json_is_string(jObjectType))
|
||||
|
||||
auto objectType = ParseObjectType(Json::GetString(jRoot["objectType"]));
|
||||
if (objectType != 0xFF)
|
||||
{
|
||||
auto objectType = ParseObjectType(json_string_value(jObjectType));
|
||||
if (objectType != 0xFF)
|
||||
auto id = Json::GetString(jRoot["id"]);
|
||||
|
||||
rct_object_entry entry = {};
|
||||
auto originalId = Json::GetString(jRoot["originalId"]);
|
||||
auto originalName = originalId;
|
||||
if (originalId.length() == 8 + 1 + 8 + 1 + 8)
|
||||
{
|
||||
auto id = json_string_value(json_object_get(jRoot, "id"));
|
||||
entry.flags = std::stoul(originalId.substr(0, 8), nullptr, 16);
|
||||
originalName = originalId.substr(9, 8);
|
||||
entry.checksum = std::stoul(originalId.substr(18, 8), nullptr, 16);
|
||||
}
|
||||
auto minLength = std::min<size_t>(8, originalName.length());
|
||||
std::memcpy(entry.name, originalName.c_str(), minLength);
|
||||
|
||||
rct_object_entry entry = {};
|
||||
auto originalId = String::ToStd(json_string_value(json_object_get(jRoot, "originalId")));
|
||||
auto originalName = originalId;
|
||||
if (originalId.length() == 8 + 1 + 8 + 1 + 8)
|
||||
{
|
||||
entry.flags = std::stoul(originalId.substr(0, 8), nullptr, 16);
|
||||
originalName = originalId.substr(9, 8);
|
||||
entry.checksum = std::stoul(originalId.substr(18, 8), nullptr, 16);
|
||||
}
|
||||
auto minLength = std::min<size_t>(8, originalName.length());
|
||||
std::memcpy(entry.name, originalName.c_str(), minLength);
|
||||
result = CreateObject(entry);
|
||||
result->SetIdentifier(id);
|
||||
result->MarkAsJsonObject();
|
||||
auto readContext = ReadObjectContext(objectRepository, id, !gOpenRCT2NoGraphics, fileRetriever);
|
||||
result->ReadJson(&readContext, jRoot);
|
||||
if (readContext.WasError())
|
||||
{
|
||||
throw std::runtime_error("Object has errors");
|
||||
}
|
||||
|
||||
result = CreateObject(entry);
|
||||
result->SetIdentifier(id);
|
||||
result->MarkAsJsonObject();
|
||||
auto readContext = ReadObjectContext(objectRepository, id, !gOpenRCT2NoGraphics, fileRetriever);
|
||||
result->ReadJson(&readContext, jRoot);
|
||||
if (readContext.WasError())
|
||||
auto jAuthors = jRoot["authors"];
|
||||
std::vector<std::string> authorVector;
|
||||
for (const auto& jAuthor : jAuthors)
|
||||
{
|
||||
if (jAuthor.is_string())
|
||||
{
|
||||
throw std::runtime_error("Object has errors");
|
||||
authorVector.emplace_back(Json::GetString(jAuthor));
|
||||
}
|
||||
}
|
||||
result->SetAuthors(std::move(authorVector));
|
||||
|
||||
auto authors = json_object_get(jRoot, "authors");
|
||||
if (json_is_array(authors))
|
||||
auto sourceGames = jRoot["sourceGame"];
|
||||
if (sourceGames.is_array() || sourceGames.is_string())
|
||||
{
|
||||
std::vector<uint8_t> sourceGameVector;
|
||||
for (const auto& jSourceGame : sourceGames)
|
||||
{
|
||||
std::vector<std::string> authorVector;
|
||||
for (size_t j = 0; j < json_array_size(authors); j++)
|
||||
{
|
||||
json_t* tryString = json_array_get(authors, j);
|
||||
if (json_is_string(tryString))
|
||||
{
|
||||
authorVector.emplace_back(json_string_value(tryString));
|
||||
}
|
||||
}
|
||||
result->SetAuthors(std::move(authorVector));
|
||||
}
|
||||
else if (json_is_string(authors))
|
||||
{
|
||||
result->SetAuthors({ json_string_value(authors) });
|
||||
}
|
||||
|
||||
auto sourceGames = json_object_get(jRoot, "sourceGame");
|
||||
if (json_is_array(sourceGames))
|
||||
{
|
||||
std::vector<uint8_t> sourceGameVector;
|
||||
for (size_t j = 0; j < json_array_size(sourceGames); j++)
|
||||
{
|
||||
sourceGameVector.push_back(ParseSourceGame(json_string_value(json_array_get(sourceGames, j))));
|
||||
}
|
||||
result->SetSourceGames(sourceGameVector);
|
||||
}
|
||||
else if (json_is_string(sourceGames))
|
||||
{
|
||||
auto sourceGame = json_string_value(sourceGames);
|
||||
result->SetSourceGames({ ParseSourceGame(sourceGame) });
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error("Object %s has an incorrect sourceGame parameter.", id);
|
||||
result->SetSourceGames({ OBJECT_SOURCE_CUSTOM });
|
||||
sourceGameVector.push_back(ParseSourceGame(Json::GetString(jSourceGame)));
|
||||
}
|
||||
result->SetSourceGames(sourceGameVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error("Object %s has an incorrect sourceGame parameter.", id.c_str());
|
||||
result->SetSourceGames({ OBJECT_SOURCE_CUSTOM });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -1,563 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma warning(disable : 4706) // assignment within conditional expression
|
||||
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../PlatformEnvironment.h"
|
||||
#include "../core/File.h"
|
||||
#include "../core/FileScanner.h"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../core/Path.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../drawing/ImageImporter.h"
|
||||
#include "../interface/Cursors.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../sprites.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectFactory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
||||
namespace ObjectJsonHelpers
|
||||
{
|
||||
/**
|
||||
* Container for a G1 image, additional information and RAII.
|
||||
*/
|
||||
struct RequiredImage
|
||||
{
|
||||
rct_g1_element g1{};
|
||||
std::unique_ptr<RequiredImage> next_zoom;
|
||||
|
||||
bool HasData() const
|
||||
{
|
||||
return g1.offset != nullptr;
|
||||
}
|
||||
|
||||
RequiredImage() = default;
|
||||
RequiredImage(const RequiredImage&) = delete;
|
||||
|
||||
RequiredImage(const rct_g1_element& orig)
|
||||
{
|
||||
auto length = g1_calculate_data_size(&orig);
|
||||
g1 = orig;
|
||||
g1.offset = new uint8_t[length];
|
||||
std::memcpy(g1.offset, orig.offset, length);
|
||||
g1.flags &= ~G1_FLAG_HAS_ZOOM_SPRITE;
|
||||
}
|
||||
|
||||
RequiredImage(uint32_t idx, std::function<const rct_g1_element*(uint32_t)> getter)
|
||||
{
|
||||
auto orig = getter(idx);
|
||||
if (orig != nullptr)
|
||||
{
|
||||
auto length = g1_calculate_data_size(orig);
|
||||
g1 = *orig;
|
||||
g1.offset = new uint8_t[length];
|
||||
std::memcpy(g1.offset, orig->offset, length);
|
||||
if ((g1.flags & G1_FLAG_HAS_ZOOM_SPRITE) && g1.zoomed_offset != 0)
|
||||
{
|
||||
// Fetch image for next zoom level
|
||||
next_zoom = std::make_unique<RequiredImage>(static_cast<uint32_t>(idx - g1.zoomed_offset), getter);
|
||||
if (!next_zoom->HasData())
|
||||
{
|
||||
next_zoom = nullptr;
|
||||
g1.flags &= ~G1_FLAG_HAS_ZOOM_SPRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~RequiredImage()
|
||||
{
|
||||
delete[] g1.offset;
|
||||
}
|
||||
};
|
||||
|
||||
bool GetBoolean(const json_t* obj, const std::string& name, bool defaultValue)
|
||||
{
|
||||
auto value = json_object_get(obj, name.c_str());
|
||||
return json_is_boolean(value) ? json_boolean_value(value) : defaultValue;
|
||||
}
|
||||
|
||||
std::string GetString(const json_t* value)
|
||||
{
|
||||
return json_is_string(value) ? std::string(json_string_value(value)) : std::string();
|
||||
}
|
||||
|
||||
std::string GetString(const json_t* obj, const std::string& name, const std::string& defaultValue)
|
||||
{
|
||||
auto value = json_object_get(obj, name.c_str());
|
||||
return json_is_string(value) ? json_string_value(value) : defaultValue;
|
||||
}
|
||||
|
||||
int32_t GetInteger(const json_t* obj, const std::string& name, const int32_t& defaultValue)
|
||||
{
|
||||
auto value = json_object_get(obj, name.c_str());
|
||||
if (json_is_integer(value))
|
||||
{
|
||||
int64_t val = json_integer_value(value);
|
||||
if (val >= std::numeric_limits<int32_t>::min() && val <= std::numeric_limits<int32_t>::max())
|
||||
{
|
||||
return static_cast<int32_t>(val);
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
float GetFloat(const json_t* obj, const std::string& name, const float& defaultValue)
|
||||
{
|
||||
auto value = json_object_get(obj, name.c_str());
|
||||
return json_is_number(value) ? json_number_value(value) : defaultValue;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetJsonStringArray(const json_t* arr)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
if (json_is_array(arr))
|
||||
{
|
||||
auto count = json_array_size(arr);
|
||||
result.reserve(count);
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
auto element = json_string_value(json_array_get(arr, i));
|
||||
result.push_back(element);
|
||||
}
|
||||
}
|
||||
else if (json_is_string(arr))
|
||||
{
|
||||
result.push_back(json_string_value(arr));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int32_t> GetJsonIntegerArray(const json_t* arr)
|
||||
{
|
||||
std::vector<int32_t> result;
|
||||
if (json_is_array(arr))
|
||||
{
|
||||
auto count = json_array_size(arr);
|
||||
result.reserve(count);
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
auto element = json_integer_value(json_array_get(arr, i));
|
||||
result.push_back(element);
|
||||
}
|
||||
}
|
||||
else if (json_is_integer(arr))
|
||||
{
|
||||
result.push_back(json_integer_value(arr));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
colour_t ParseColour(const std::string_view& s, colour_t defaultValue)
|
||||
{
|
||||
static const std::unordered_map<std::string_view, colour_t> LookupTable{
|
||||
{ "black", COLOUR_BLACK },
|
||||
{ "grey", COLOUR_GREY },
|
||||
{ "white", COLOUR_WHITE },
|
||||
{ "dark_purple", COLOUR_DARK_PURPLE },
|
||||
{ "light_purple", COLOUR_LIGHT_PURPLE },
|
||||
{ "bright_purple", COLOUR_BRIGHT_PURPLE },
|
||||
{ "dark_blue", COLOUR_DARK_BLUE },
|
||||
{ "light_blue", COLOUR_LIGHT_BLUE },
|
||||
{ "icy_blue", COLOUR_ICY_BLUE },
|
||||
{ "teal", COLOUR_TEAL },
|
||||
{ "aquamarine", COLOUR_AQUAMARINE },
|
||||
{ "saturated_green", COLOUR_SATURATED_GREEN },
|
||||
{ "dark_green", COLOUR_DARK_GREEN },
|
||||
{ "moss_green", COLOUR_MOSS_GREEN },
|
||||
{ "bright_green", COLOUR_BRIGHT_GREEN },
|
||||
{ "olive_green", COLOUR_OLIVE_GREEN },
|
||||
{ "dark_olive_green", COLOUR_DARK_OLIVE_GREEN },
|
||||
{ "bright_yellow", COLOUR_BRIGHT_YELLOW },
|
||||
{ "yellow", COLOUR_YELLOW },
|
||||
{ "dark_yellow", COLOUR_DARK_YELLOW },
|
||||
{ "light_orange", COLOUR_LIGHT_ORANGE },
|
||||
{ "dark_orange", COLOUR_DARK_ORANGE },
|
||||
{ "light_brown", COLOUR_LIGHT_BROWN },
|
||||
{ "saturated_brown", COLOUR_SATURATED_BROWN },
|
||||
{ "dark_brown", COLOUR_DARK_BROWN },
|
||||
{ "salmon_pink", COLOUR_SALMON_PINK },
|
||||
{ "bordeaux_red", COLOUR_BORDEAUX_RED },
|
||||
{ "saturated_red", COLOUR_SATURATED_RED },
|
||||
{ "bright_red", COLOUR_BRIGHT_RED },
|
||||
{ "dark_pink", COLOUR_DARK_PINK },
|
||||
{ "bright_pink", COLOUR_BRIGHT_PINK },
|
||||
{ "light_pink", COLOUR_LIGHT_PINK },
|
||||
};
|
||||
auto result = LookupTable.find(s);
|
||||
return (result != LookupTable.end()) ? result->second : defaultValue;
|
||||
}
|
||||
|
||||
uint8_t ParseCursor(const std::string& s, uint8_t defaultValue)
|
||||
{
|
||||
static const std::unordered_map<std::string, uint8_t> LookupTable{
|
||||
{ "CURSOR_BLANK", CURSOR_BLANK },
|
||||
{ "CURSOR_UP_ARROW", CURSOR_UP_ARROW },
|
||||
{ "CURSOR_UP_DOWN_ARROW", CURSOR_UP_DOWN_ARROW },
|
||||
{ "CURSOR_HAND_POINT", CURSOR_HAND_POINT },
|
||||
{ "CURSOR_ZZZ", CURSOR_ZZZ },
|
||||
{ "CURSOR_DIAGONAL_ARROWS", CURSOR_DIAGONAL_ARROWS },
|
||||
{ "CURSOR_PICKER", CURSOR_PICKER },
|
||||
{ "CURSOR_TREE_DOWN", CURSOR_TREE_DOWN },
|
||||
{ "CURSOR_FOUNTAIN_DOWN", CURSOR_FOUNTAIN_DOWN },
|
||||
{ "CURSOR_STATUE_DOWN", CURSOR_STATUE_DOWN },
|
||||
{ "CURSOR_BENCH_DOWN", CURSOR_BENCH_DOWN },
|
||||
{ "CURSOR_CROSS_HAIR", CURSOR_CROSS_HAIR },
|
||||
{ "CURSOR_BIN_DOWN", CURSOR_BIN_DOWN },
|
||||
{ "CURSOR_LAMPPOST_DOWN", CURSOR_LAMPPOST_DOWN },
|
||||
{ "CURSOR_FENCE_DOWN", CURSOR_FENCE_DOWN },
|
||||
{ "CURSOR_FLOWER_DOWN", CURSOR_FLOWER_DOWN },
|
||||
{ "CURSOR_PATH_DOWN", CURSOR_PATH_DOWN },
|
||||
{ "CURSOR_DIG_DOWN", CURSOR_DIG_DOWN },
|
||||
{ "CURSOR_WATER_DOWN", CURSOR_WATER_DOWN },
|
||||
{ "CURSOR_HOUSE_DOWN", CURSOR_HOUSE_DOWN },
|
||||
{ "CURSOR_VOLCANO_DOWN", CURSOR_VOLCANO_DOWN },
|
||||
{ "CURSOR_WALK_DOWN", CURSOR_WALK_DOWN },
|
||||
{ "CURSOR_PAINT_DOWN", CURSOR_PAINT_DOWN },
|
||||
{ "CURSOR_ENTRANCE_DOWN", CURSOR_ENTRANCE_DOWN },
|
||||
{ "CURSOR_HAND_OPEN", CURSOR_HAND_OPEN },
|
||||
{ "CURSOR_HAND_CLOSED", CURSOR_HAND_CLOSED },
|
||||
{ "CURSOR_ARROW", CURSOR_ARROW },
|
||||
};
|
||||
|
||||
auto result = LookupTable.find(s);
|
||||
return (result != LookupTable.end()) ? result->second : defaultValue;
|
||||
}
|
||||
|
||||
rct_object_entry ParseObjectEntry(const std::string& s)
|
||||
{
|
||||
rct_object_entry entry = {};
|
||||
std::fill_n(entry.name, sizeof(entry.name), ' ');
|
||||
auto copyLen = std::min<size_t>(8, s.size());
|
||||
std::copy_n(s.c_str(), copyLen, entry.name);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static std::vector<int32_t> ParseRange(std::string s)
|
||||
{
|
||||
// Currently only supports [###] or [###..###]
|
||||
std::vector<int32_t> result = {};
|
||||
if (s.length() >= 3 && s[0] == '[' && s[s.length() - 1] == ']')
|
||||
{
|
||||
s = s.substr(1, s.length() - 2);
|
||||
auto parts = String::Split(s, "..");
|
||||
if (parts.size() == 1)
|
||||
{
|
||||
result.push_back(std::stoi(parts[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto left = std::stoi(parts[0]);
|
||||
auto right = std::stoi(parts[1]);
|
||||
if (left <= right)
|
||||
{
|
||||
for (auto i = left; i <= right; i++)
|
||||
{
|
||||
result.push_back(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto i = right; i >= left; i--)
|
||||
{
|
||||
result.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string FindLegacyObject(const std::string& name)
|
||||
{
|
||||
const auto env = GetContext()->GetPlatformEnvironment();
|
||||
auto objectsPath = env->GetDirectoryPath(DIRBASE::RCT2, DIRID::OBJECT);
|
||||
auto objectPath = Path::Combine(objectsPath, name);
|
||||
if (!File::Exists(objectPath))
|
||||
{
|
||||
// Search recursively for any file with the target name (case insensitive)
|
||||
auto filter = Path::Combine(objectsPath, "*.dat");
|
||||
auto scanner = std::unique_ptr<IFileScanner>(Path::ScanDirectory(filter, true));
|
||||
while (scanner->Next())
|
||||
{
|
||||
auto currentName = Path::GetFileName(scanner->GetPathRelative());
|
||||
if (String::Equals(currentName, name, true))
|
||||
{
|
||||
objectPath = scanner->GetPath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return objectPath;
|
||||
}
|
||||
|
||||
static std::vector<std::unique_ptr<RequiredImage>> LoadObjectImages(
|
||||
IReadObjectContext* context, const std::string& name, const std::vector<int32_t>& range)
|
||||
{
|
||||
std::vector<std::unique_ptr<RequiredImage>> result;
|
||||
auto objectPath = FindLegacyObject(name);
|
||||
auto obj = ObjectFactory::CreateObjectFromLegacyFile(context->GetObjectRepository(), objectPath.c_str());
|
||||
if (obj != nullptr)
|
||||
{
|
||||
auto& imgTable = static_cast<const Object*>(obj)->GetImageTable();
|
||||
auto numImages = static_cast<int32_t>(imgTable.GetCount());
|
||||
auto images = imgTable.GetImages();
|
||||
size_t placeHoldersAdded = 0;
|
||||
for (auto i : range)
|
||||
{
|
||||
if (i >= 0 && i < numImages)
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>(
|
||||
static_cast<uint32_t>(i), [images](uint32_t idx) -> const rct_g1_element* { return &images[idx]; }));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
placeHoldersAdded++;
|
||||
}
|
||||
}
|
||||
delete obj;
|
||||
|
||||
// Log place holder information
|
||||
if (placeHoldersAdded > 0)
|
||||
{
|
||||
std::string msg = "Adding " + std::to_string(placeHoldersAdded) + " placeholders";
|
||||
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, msg.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string msg = "Unable to open '" + objectPath + "'";
|
||||
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, msg.c_str());
|
||||
for (size_t i = 0; i < range.size(); i++)
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<std::unique_ptr<RequiredImage>> ParseImages(IReadObjectContext* context, std::string s)
|
||||
{
|
||||
std::vector<std::unique_ptr<RequiredImage>> result;
|
||||
if (s.empty())
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
else if (String::StartsWith(s, "$CSG"))
|
||||
{
|
||||
if (is_csg_loaded())
|
||||
{
|
||||
auto range = ParseRange(s.substr(4));
|
||||
if (!range.empty())
|
||||
{
|
||||
for (auto i : range)
|
||||
{
|
||||
result.push_back(std::make_unique<RequiredImage>(
|
||||
static_cast<uint32_t>(SPR_CSG_BEGIN + i),
|
||||
[](uint32_t idx) -> const rct_g1_element* { return gfx_get_g1_element(idx); }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (String::StartsWith(s, "$G1"))
|
||||
{
|
||||
auto range = ParseRange(s.substr(3));
|
||||
if (!range.empty())
|
||||
{
|
||||
for (auto i : range)
|
||||
{
|
||||
result.push_back(
|
||||
std::make_unique<RequiredImage>(static_cast<uint32_t>(i), [](uint32_t idx) -> const rct_g1_element* {
|
||||
return gfx_get_g1_element(idx);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (String::StartsWith(s, "$RCT2:OBJDATA/"))
|
||||
{
|
||||
auto name = s.substr(14);
|
||||
auto rangeStart = name.find('[');
|
||||
if (rangeStart != std::string::npos)
|
||||
{
|
||||
auto rangeString = name.substr(rangeStart);
|
||||
auto range = ParseRange(name.substr(rangeStart));
|
||||
name = name.substr(0, rangeStart);
|
||||
result = LoadObjectImages(context, name, range);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
auto imageData = context->GetData(s);
|
||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
||||
|
||||
ImageImporter importer;
|
||||
auto importResult = importer.Import(image, 0, 0, ImageImporter::IMPORT_FLAGS::RLE);
|
||||
|
||||
result.push_back(std::make_unique<RequiredImage>(importResult.Element));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
auto msg = String::StdFormat("Unable to load image '%s': %s", s.c_str(), e.what());
|
||||
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, msg.c_str());
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<std::unique_ptr<RequiredImage>> ParseImages(IReadObjectContext* context, json_t* el)
|
||||
{
|
||||
auto path = GetString(el, "path");
|
||||
auto x = GetInteger(el, "x");
|
||||
auto y = GetInteger(el, "y");
|
||||
auto raw = (GetString(el, "format") == "raw");
|
||||
|
||||
std::vector<std::unique_ptr<RequiredImage>> result;
|
||||
try
|
||||
{
|
||||
auto flags = ImageImporter::IMPORT_FLAGS::NONE;
|
||||
if (!raw)
|
||||
{
|
||||
flags = static_cast<ImageImporter::IMPORT_FLAGS>(flags | ImageImporter::IMPORT_FLAGS::RLE);
|
||||
}
|
||||
auto imageData = context->GetData(path);
|
||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
||||
|
||||
ImageImporter importer;
|
||||
auto importResult = importer.Import(image, 0, 0, flags);
|
||||
auto g1Element = importResult.Element;
|
||||
g1Element.x_offset = x;
|
||||
g1Element.y_offset = y;
|
||||
result.push_back(std::make_unique<RequiredImage>(g1Element));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
auto msg = String::StdFormat("Unable to load image '%s': %s", path.c_str(), e.what());
|
||||
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, msg.c_str());
|
||||
result.push_back(std::make_unique<RequiredImage>());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ObjectStringID ParseStringId(const std::string& s)
|
||||
{
|
||||
if (s == "name")
|
||||
return ObjectStringID::NAME;
|
||||
if (s == "description")
|
||||
return ObjectStringID::DESCRIPTION;
|
||||
if (s == "capacity")
|
||||
return ObjectStringID::CAPACITY;
|
||||
if (s == "vehicleName")
|
||||
return ObjectStringID::VEHICLE_NAME;
|
||||
return ObjectStringID::UNKNOWN;
|
||||
}
|
||||
|
||||
void LoadStrings(const json_t* root, StringTable& stringTable)
|
||||
{
|
||||
auto jsonStrings = json_object_get(root, "strings");
|
||||
const char* key;
|
||||
json_t* jlanguages;
|
||||
json_object_foreach(jsonStrings, key, jlanguages)
|
||||
{
|
||||
auto stringId = ParseStringId(key);
|
||||
if (stringId != ObjectStringID::UNKNOWN)
|
||||
{
|
||||
const char* locale;
|
||||
json_t* jstring;
|
||||
json_object_foreach(jlanguages, locale, jstring)
|
||||
{
|
||||
auto langId = language_get_id_from_locale(locale);
|
||||
if (langId != LANGUAGE_UNDEFINED)
|
||||
{
|
||||
auto string = json_string_value(jstring);
|
||||
stringTable.SetString(stringId, langId, string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stringTable.Sort();
|
||||
}
|
||||
|
||||
void LoadImages(IReadObjectContext* context, const json_t* root, ImageTable& imageTable)
|
||||
{
|
||||
if (context->ShouldLoadImages())
|
||||
{
|
||||
// First gather all the required images from inspecting the JSON
|
||||
std::vector<std::unique_ptr<RequiredImage>> allImages;
|
||||
auto jsonImages = json_object_get(root, "images");
|
||||
size_t i;
|
||||
json_t* el;
|
||||
json_array_foreach(jsonImages, i, el)
|
||||
{
|
||||
if (json_is_string(el))
|
||||
{
|
||||
auto s = json_string_value(el);
|
||||
auto images = ParseImages(context, s);
|
||||
allImages.insert(
|
||||
allImages.end(), std::make_move_iterator(images.begin()), std::make_move_iterator(images.end()));
|
||||
}
|
||||
else if (json_is_object(el))
|
||||
{
|
||||
auto images = ParseImages(context, el);
|
||||
allImages.insert(
|
||||
allImages.end(), std::make_move_iterator(images.begin()), std::make_move_iterator(images.end()));
|
||||
}
|
||||
}
|
||||
|
||||
// Now add all the images to the image table
|
||||
auto imagesStartIndex = imageTable.GetCount();
|
||||
for (const auto& img : allImages)
|
||||
{
|
||||
const auto& g1 = img->g1;
|
||||
imageTable.AddImage(&g1);
|
||||
}
|
||||
|
||||
// Add all the zoom images at the very end of the image table.
|
||||
// This way it should not affect the offsets used within the object logic.
|
||||
for (size_t j = 0; j < allImages.size(); j++)
|
||||
{
|
||||
const auto tableIndex = imagesStartIndex + j;
|
||||
const auto* img = allImages[j].get();
|
||||
if (img->next_zoom != nullptr)
|
||||
{
|
||||
img = img->next_zoom.get();
|
||||
|
||||
// Set old image zoom offset to zoom image which we are about to add
|
||||
auto g1a = const_cast<rct_g1_element*>(&imageTable.GetImages()[tableIndex]);
|
||||
g1a->zoomed_offset = static_cast<int32_t>(tableIndex) - static_cast<int32_t>(imageTable.GetCount());
|
||||
|
||||
while (img != nullptr)
|
||||
{
|
||||
auto g1b = img->g1;
|
||||
if (img->next_zoom != nullptr)
|
||||
{
|
||||
g1b.zoomed_offset = -1;
|
||||
}
|
||||
imageTable.AddImage(&g1b);
|
||||
img = img->next_zoom.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ObjectJsonHelpers
|
||||
@@ -1,52 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../interface/Colour.h"
|
||||
#include "../object/Object.h"
|
||||
#include "ImageTable.h"
|
||||
#include "StringTable.h"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ObjectJsonHelpers
|
||||
{
|
||||
bool GetBoolean(const json_t* obj, const std::string& name, bool defaultValue = false);
|
||||
std::string GetString(const json_t* value);
|
||||
std::string GetString(const json_t* obj, const std::string& name, const std::string& defaultValue = "");
|
||||
int32_t GetInteger(const json_t* obj, const std::string& name, const int32_t& defaultValue = 0);
|
||||
float GetFloat(const json_t* obj, const std::string& name, const float& defaultValue = 0);
|
||||
std::vector<std::string> GetJsonStringArray(const json_t* arr);
|
||||
std::vector<int32_t> GetJsonIntegerArray(const json_t* arr);
|
||||
colour_t ParseColour(const std::string_view& s, colour_t defaultValue = COLOUR_BLACK);
|
||||
uint8_t ParseCursor(const std::string& s, uint8_t defaultValue);
|
||||
rct_object_entry ParseObjectEntry(const std::string& s);
|
||||
void LoadStrings(const json_t* root, StringTable& stringTable);
|
||||
void LoadImages(IReadObjectContext* context, const json_t* root, ImageTable& imageTable);
|
||||
|
||||
template<typename T> static T GetFlags(const json_t* obj, std::initializer_list<std::pair<std::string, T>> list)
|
||||
{
|
||||
T flags{};
|
||||
for (const auto& item : list)
|
||||
{
|
||||
if (GetBoolean(obj, item.first))
|
||||
{
|
||||
flags = static_cast<T>(flags | item.second);
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
} // namespace ObjectJsonHelpers
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "../ride/RideData.h"
|
||||
#include "../ride/ShopItem.h"
|
||||
#include "../ride/Track.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
#include "ObjectRepository.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -523,175 +522,161 @@ uint8_t RideObject::CalculateNumHorizontalFrames(const rct_ride_entry_vehicle* v
|
||||
return numHorizontalFrames;
|
||||
}
|
||||
|
||||
void RideObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void RideObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
Guard::Assert(root.is_object(), "RideObject::ReadJson expects parameter root to be object");
|
||||
|
||||
auto rideTypes = ObjectJsonHelpers::GetJsonStringArray(json_object_get(properties, "type"));
|
||||
for (size_t i = 0; i < MAX_RIDE_TYPES_PER_RIDE_ENTRY; i++)
|
||||
json_t properties = root["properties"];
|
||||
|
||||
if (properties.is_object())
|
||||
{
|
||||
uint8_t rideType = RIDE_TYPE_NULL;
|
||||
if (i < rideTypes.size())
|
||||
// This will convert a string to an array
|
||||
json_t rideTypes = Json::AsArray(properties["type"]);
|
||||
size_t numRideTypes = rideTypes.size();
|
||||
|
||||
for (size_t i = 0; i < MAX_RIDE_TYPES_PER_RIDE_ENTRY; i++)
|
||||
{
|
||||
rideType = ParseRideType(rideTypes[i]);
|
||||
if (rideType == RIDE_TYPE_NULL)
|
||||
uint8_t rideType = RIDE_TYPE_NULL;
|
||||
|
||||
if (i < numRideTypes)
|
||||
{
|
||||
context->LogError(OBJECT_ERROR_INVALID_PROPERTY, "Unknown ride type");
|
||||
rideType = ParseRideType(Json::GetString(rideTypes[i]));
|
||||
|
||||
if (rideType == RIDE_TYPE_NULL)
|
||||
{
|
||||
context->LogError(OBJECT_ERROR_INVALID_PROPERTY, "Unknown ride type");
|
||||
}
|
||||
}
|
||||
|
||||
_legacyType.ride_type[i] = rideType;
|
||||
}
|
||||
|
||||
_legacyType.max_height = Json::GetNumber<uint8_t>(properties["maxHeight"]);
|
||||
|
||||
// This needs to be set for both shops/facilities _and_ regular rides.
|
||||
for (auto& item : _legacyType.shop_item)
|
||||
{
|
||||
item = SHOP_ITEM_NONE;
|
||||
}
|
||||
|
||||
auto carColours = Json::AsArray(properties["carColours"]);
|
||||
_presetColours = ReadJsonCarColours(carColours);
|
||||
|
||||
if (IsRideTypeShopOrFacility(_legacyType.ride_type[0]))
|
||||
{
|
||||
// Standard car info for a shop
|
||||
auto& car = _legacyType.vehicles[0];
|
||||
car.spacing = 544;
|
||||
car.sprite_flags = VEHICLE_SPRITE_FLAG_FLAT;
|
||||
car.sprite_width = 1;
|
||||
car.sprite_height_negative = 1;
|
||||
car.sprite_height_positive = 1;
|
||||
car.flags = VEHICLE_ENTRY_FLAG_SPINNING;
|
||||
car.car_visual = VEHICLE_VISUAL_FLAT_RIDE_OR_CAR_RIDE;
|
||||
car.friction_sound_id = SoundId::Null;
|
||||
car.sound_range = 0xFF;
|
||||
car.draw_order = 6;
|
||||
|
||||
// Shop item
|
||||
auto rideSells = Json::AsArray(properties["sells"]);
|
||||
auto numShopItems = std::min(static_cast<size_t>(NUM_SHOP_ITEMS_PER_RIDE), rideSells.size());
|
||||
for (size_t i = 0; i < numShopItems; i++)
|
||||
{
|
||||
auto shopItem = ParseShopItem(Json::GetString(rideSells[i]));
|
||||
if (shopItem == SHOP_ITEM_NONE)
|
||||
{
|
||||
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, "Unknown shop item");
|
||||
}
|
||||
|
||||
_legacyType.shop_item[i] = shopItem;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadJsonVehicleInfo(context, properties);
|
||||
|
||||
auto swingMode = Json::GetNumber<int32_t>(properties["swingMode"]);
|
||||
if (swingMode == 1)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1;
|
||||
}
|
||||
else if (swingMode == 2)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1;
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_2;
|
||||
}
|
||||
|
||||
auto rotationMode = Json::GetNumber<int32_t>(properties["rotationMode"]);
|
||||
if (rotationMode == 1)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_1;
|
||||
}
|
||||
else if (rotationMode == 2)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_2;
|
||||
}
|
||||
|
||||
auto ratingMultiplier = properties["ratingMultipler"];
|
||||
if (ratingMultiplier.is_object())
|
||||
{
|
||||
_legacyType.excitement_multiplier = Json::GetNumber<int8_t>(ratingMultiplier["excitement"]);
|
||||
_legacyType.intensity_multiplier = Json::GetNumber<int8_t>(ratingMultiplier["intensity"]);
|
||||
_legacyType.nausea_multiplier = Json::GetNumber<int8_t>(ratingMultiplier["nausea"]);
|
||||
}
|
||||
}
|
||||
|
||||
_legacyType.ride_type[i] = rideType;
|
||||
}
|
||||
|
||||
_legacyType.max_height = ObjectJsonHelpers::GetInteger(properties, "maxHeight");
|
||||
|
||||
// This needs to be set for both shops/facilities _and_ regular rides.
|
||||
for (auto& item : _legacyType.shop_item)
|
||||
{
|
||||
item = SHOP_ITEM_NONE;
|
||||
}
|
||||
|
||||
_presetColours = ReadJsonCarColours(json_object_get(properties, "carColours"));
|
||||
|
||||
if (IsRideTypeShopOrFacility(_legacyType.ride_type[0]))
|
||||
{
|
||||
// Standard car info for a shop
|
||||
auto& car = _legacyType.vehicles[0];
|
||||
car.spacing = 544;
|
||||
car.sprite_flags = VEHICLE_SPRITE_FLAG_FLAT;
|
||||
car.sprite_width = 1;
|
||||
car.sprite_height_negative = 1;
|
||||
car.sprite_height_positive = 1;
|
||||
car.flags = VEHICLE_ENTRY_FLAG_SPINNING;
|
||||
car.car_visual = VEHICLE_VISUAL_FLAT_RIDE_OR_CAR_RIDE;
|
||||
car.friction_sound_id = SoundId::Null;
|
||||
car.sound_range = 0xFF;
|
||||
car.draw_order = 6;
|
||||
|
||||
// Shop item
|
||||
auto rideSells = ObjectJsonHelpers::GetJsonStringArray(json_object_get(json_object_get(root, "properties"), "sells"));
|
||||
auto numShopItems = std::min(static_cast<size_t>(NUM_SHOP_ITEMS_PER_RIDE), rideSells.size());
|
||||
for (size_t i = 0; i < numShopItems; i++)
|
||||
{
|
||||
auto shopItem = ParseShopItem(rideSells[i]);
|
||||
if (shopItem == SHOP_ITEM_NONE)
|
||||
_legacyType.BuildMenuPriority = Json::GetNumber<uint8_t>(properties["buildMenuPriority"]);
|
||||
_legacyType.flags |= Json::GetFlags<uint32_t>(
|
||||
properties,
|
||||
{
|
||||
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, "Unknown shop item");
|
||||
}
|
||||
|
||||
_legacyType.shop_item[i] = ParseShopItem(rideSells[i]);
|
||||
}
|
||||
{ "noInversions", RIDE_ENTRY_FLAG_NO_INVERSIONS },
|
||||
{ "noBanking", RIDE_ENTRY_FLAG_NO_BANKED_TRACK },
|
||||
{ "playDepartSound", RIDE_ENTRY_FLAG_PLAY_DEPART_SOUND },
|
||||
// Skipping "disallowWandering", no vehicle sets this flag.
|
||||
{ "playSplashSound", RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND },
|
||||
{ "playSplashSoundSlide", RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND_SLIDE },
|
||||
{ "hasShelter", RIDE_ENTRY_FLAG_COVERED_RIDE },
|
||||
{ "limitAirTimeBonus", RIDE_ENTRY_FLAG_LIMIT_AIRTIME_BONUS },
|
||||
{ "disableBreakdown", RIDE_ENTRY_FLAG_CANNOT_BREAK_DOWN },
|
||||
// Skipping noDoorsOverTrack, moved to ride groups.
|
||||
{ "noCollisionCrashes", RIDE_ENTRY_FLAG_DISABLE_COLLISION_CRASHES },
|
||||
{ "disablePainting", RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB },
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadJsonVehicleInfo(context, properties);
|
||||
|
||||
auto swingMode = ObjectJsonHelpers::GetInteger(properties, "swingMode");
|
||||
if (swingMode == 1)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1;
|
||||
}
|
||||
else if (swingMode == 2)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1;
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_2;
|
||||
}
|
||||
|
||||
auto rotationMode = ObjectJsonHelpers::GetInteger(properties, "rotationMode");
|
||||
if (rotationMode == 1)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_1;
|
||||
}
|
||||
else if (rotationMode == 2)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_2;
|
||||
}
|
||||
|
||||
auto ratingMultiplier = json_object_get(properties, "ratingMultipler");
|
||||
if (ratingMultiplier != nullptr)
|
||||
{
|
||||
_legacyType.excitement_multiplier = ObjectJsonHelpers::GetInteger(ratingMultiplier, "excitement");
|
||||
_legacyType.intensity_multiplier = ObjectJsonHelpers::GetInteger(ratingMultiplier, "intensity");
|
||||
_legacyType.nausea_multiplier = ObjectJsonHelpers::GetInteger(ratingMultiplier, "nausea");
|
||||
}
|
||||
|
||||
auto availableTrackPieces = ObjectJsonHelpers::GetJsonStringArray(json_object_get(properties, "availableTrackPieces"));
|
||||
}
|
||||
|
||||
_legacyType.BuildMenuPriority = ObjectJsonHelpers::GetInteger(properties, "buildMenuPriority", 0);
|
||||
_legacyType.flags |= ObjectJsonHelpers::GetFlags<uint32_t>(
|
||||
properties,
|
||||
{
|
||||
{ "noInversions", RIDE_ENTRY_FLAG_NO_INVERSIONS },
|
||||
{ "noBanking", RIDE_ENTRY_FLAG_NO_BANKED_TRACK },
|
||||
{ "playDepartSound", RIDE_ENTRY_FLAG_PLAY_DEPART_SOUND },
|
||||
// Skipping "disallowWandering", no vehicle sets this flag.
|
||||
{ "playSplashSound", RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND },
|
||||
{ "playSplashSoundSlide", RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND_SLIDE },
|
||||
{ "hasShelter", RIDE_ENTRY_FLAG_COVERED_RIDE },
|
||||
{ "limitAirTimeBonus", RIDE_ENTRY_FLAG_LIMIT_AIRTIME_BONUS },
|
||||
{ "disableBreakdown", RIDE_ENTRY_FLAG_CANNOT_BREAK_DOWN },
|
||||
// Skipping noDoorsOverTrack, moved to ride groups.
|
||||
{ "noCollisionCrashes", RIDE_ENTRY_FLAG_DISABLE_COLLISION_CRASHES },
|
||||
{ "disablePainting", RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB },
|
||||
});
|
||||
|
||||
RideObjectUpdateRideType(&_legacyType);
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
void RideObject::ReadJsonVehicleInfo([[maybe_unused]] IReadObjectContext* context, const json_t* properties)
|
||||
void RideObject::ReadJsonVehicleInfo([[maybe_unused]] IReadObjectContext* context, json_t& properties)
|
||||
{
|
||||
_legacyType.min_cars_in_train = ObjectJsonHelpers::GetInteger(properties, "minCarsPerTrain", 1);
|
||||
_legacyType.max_cars_in_train = ObjectJsonHelpers::GetInteger(properties, "maxCarsPerTrain", 1);
|
||||
_legacyType.cars_per_flat_ride = ObjectJsonHelpers::GetInteger(properties, "carsPerFlatRide", 255);
|
||||
_legacyType.zero_cars = json_integer_value(json_object_get(properties, "numEmptyCars"));
|
||||
Guard::Assert(properties.is_object(), "RideObject::ReadJsonVehicleInfo expects parameter properties to be object");
|
||||
|
||||
_legacyType.min_cars_in_train = Json::GetNumber<uint8_t>(properties["minCarsPerTrain"], 1);
|
||||
_legacyType.max_cars_in_train = Json::GetNumber<uint8_t>(properties["maxCarsPerTrain"], 1);
|
||||
_legacyType.cars_per_flat_ride = Json::GetNumber<uint8_t>(properties["carsPerFlatRide"], 255);
|
||||
_legacyType.zero_cars = Json::GetNumber<uint8_t>(properties["numEmptyCars"]);
|
||||
|
||||
// Train formation from car indices
|
||||
_legacyType.default_vehicle = json_integer_value(json_object_get(properties, "defaultCar"));
|
||||
_legacyType.tab_vehicle = json_integer_value(json_object_get(properties, "tabCar"));
|
||||
auto tabScale = ObjectJsonHelpers::GetFloat(properties, "tabScale");
|
||||
if (tabScale != 0 && ObjectJsonHelpers::GetFloat(properties, "tabScale") <= 0.5f)
|
||||
_legacyType.default_vehicle = Json::GetNumber<uint8_t>(properties["defaultCar"]);
|
||||
_legacyType.tab_vehicle = Json::GetNumber<uint8_t>(properties["tabCar"]);
|
||||
|
||||
float tabScale = Json::GetNumber<float>(properties["tabScale"]);
|
||||
if (tabScale != 0 && tabScale <= 0.5f)
|
||||
{
|
||||
_legacyType.flags |= RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF;
|
||||
}
|
||||
|
||||
json_t headCars = Json::AsArray(properties["headCars"]);
|
||||
json_t tailCars = Json::AsArray(properties["tailCars"]);
|
||||
|
||||
// 0xFF means N/A.
|
||||
_legacyType.front_vehicle = 0xFF;
|
||||
_legacyType.second_vehicle = 0xFF;
|
||||
_legacyType.third_vehicle = 0xFF;
|
||||
_legacyType.rear_vehicle = 0xFF;
|
||||
_legacyType.front_vehicle = Json::GetNumber<uint8_t>(headCars[0], 0xFF);
|
||||
_legacyType.second_vehicle = Json::GetNumber<uint8_t>(headCars[1], 0xFF);
|
||||
_legacyType.third_vehicle = Json::GetNumber<uint8_t>(headCars[2], 0xFF);
|
||||
_legacyType.rear_vehicle = Json::GetNumber<uint8_t>(tailCars[0], 0xFF);
|
||||
|
||||
auto headCars = ObjectJsonHelpers::GetJsonIntegerArray(json_object_get(properties, "headCars"));
|
||||
if (headCars.size() >= 1)
|
||||
{
|
||||
_legacyType.front_vehicle = headCars[0];
|
||||
}
|
||||
if (headCars.size() >= 2)
|
||||
{
|
||||
_legacyType.second_vehicle = headCars[1];
|
||||
}
|
||||
if (headCars.size() >= 3)
|
||||
{
|
||||
_legacyType.third_vehicle = headCars[2];
|
||||
}
|
||||
if (headCars.size() >= 4)
|
||||
{
|
||||
// More than 3 head cars not supported yet!
|
||||
}
|
||||
|
||||
auto tailCars = ObjectJsonHelpers::GetJsonIntegerArray(json_object_get(properties, "tailCars"));
|
||||
if (tailCars.size() >= 1)
|
||||
{
|
||||
_legacyType.rear_vehicle = tailCars[0];
|
||||
}
|
||||
if (tailCars.size() >= 2)
|
||||
{
|
||||
// More than 1 tail car not supported yet!
|
||||
}
|
||||
|
||||
auto cars = ReadJsonCars(json_object_get(properties, "cars"));
|
||||
auto cars = ReadJsonCars(properties["cars"]);
|
||||
auto numCars = std::min(std::size(_legacyType.vehicles), cars.size());
|
||||
for (size_t i = 0; i < numCars; i++)
|
||||
{
|
||||
@@ -699,129 +684,131 @@ void RideObject::ReadJsonVehicleInfo([[maybe_unused]] IReadObjectContext* contex
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<rct_ride_entry_vehicle> RideObject::ReadJsonCars(const json_t* jCars)
|
||||
std::vector<rct_ride_entry_vehicle> RideObject::ReadJsonCars(json_t& jCars)
|
||||
{
|
||||
std::vector<rct_ride_entry_vehicle> cars;
|
||||
|
||||
if (json_is_array(jCars))
|
||||
if (jCars.is_array())
|
||||
{
|
||||
json_t* jCar;
|
||||
size_t index;
|
||||
json_array_foreach(jCars, index, jCar)
|
||||
for (auto& jCar : jCars)
|
||||
{
|
||||
auto car = ReadJsonCar(jCar);
|
||||
cars.push_back(car);
|
||||
if (jCar.is_object())
|
||||
{
|
||||
auto car = ReadJsonCar(jCar);
|
||||
cars.push_back(car);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (json_is_object(jCars))
|
||||
else if (jCars.is_object())
|
||||
{
|
||||
auto car = ReadJsonCar(jCars);
|
||||
cars.push_back(car);
|
||||
}
|
||||
|
||||
return cars;
|
||||
}
|
||||
|
||||
rct_ride_entry_vehicle RideObject::ReadJsonCar(const json_t* jCar)
|
||||
rct_ride_entry_vehicle RideObject::ReadJsonCar(json_t& jCar)
|
||||
{
|
||||
Guard::Assert(jCar.is_object(), "RideObject::ReadJsonCar expects parameter jCar to be object");
|
||||
|
||||
rct_ride_entry_vehicle car = {};
|
||||
car.rotation_frame_mask = ObjectJsonHelpers::GetInteger(jCar, "rotationFrameMask");
|
||||
car.spacing = ObjectJsonHelpers::GetInteger(jCar, "spacing");
|
||||
car.car_mass = ObjectJsonHelpers::GetInteger(jCar, "mass");
|
||||
car.tab_height = ObjectJsonHelpers::GetInteger(jCar, "tabOffset");
|
||||
car.num_seats = ObjectJsonHelpers::GetInteger(jCar, "numSeats");
|
||||
if (ObjectJsonHelpers::GetBoolean(jCar, "seatsInPairs", true) && car.num_seats > 1)
|
||||
car.rotation_frame_mask = Json::GetNumber<uint16_t>(jCar["rotationFrameMask"]);
|
||||
car.spacing = Json::GetNumber<uint32_t>(jCar["spacing"]);
|
||||
car.car_mass = Json::GetNumber<uint16_t>(jCar["mass"]);
|
||||
car.tab_height = Json::GetNumber<int8_t>(jCar["tabOffset"]);
|
||||
car.num_seats = Json::GetNumber<uint8_t>(jCar["numSeats"]);
|
||||
if (Json::GetBoolean(jCar["seatsInPairs"], true) && car.num_seats > 1)
|
||||
{
|
||||
car.num_seats |= VEHICLE_SEAT_PAIR_FLAG;
|
||||
}
|
||||
|
||||
car.sprite_width = ObjectJsonHelpers::GetInteger(jCar, "spriteWidth");
|
||||
car.sprite_height_negative = ObjectJsonHelpers::GetInteger(jCar, "spriteHeightNegative");
|
||||
car.sprite_height_positive = ObjectJsonHelpers::GetInteger(jCar, "spriteHeightPositive");
|
||||
car.animation = ObjectJsonHelpers::GetInteger(jCar, "animation");
|
||||
car.base_num_frames = ObjectJsonHelpers::GetInteger(jCar, "baseNumFrames");
|
||||
car.no_vehicle_images = ObjectJsonHelpers::GetInteger(jCar, "numImages");
|
||||
car.no_seating_rows = ObjectJsonHelpers::GetInteger(jCar, "numSeatRows");
|
||||
car.spinning_inertia = ObjectJsonHelpers::GetInteger(jCar, "spinningInertia");
|
||||
car.spinning_friction = ObjectJsonHelpers::GetInteger(jCar, "spinningFriction");
|
||||
car.friction_sound_id = static_cast<SoundId>(ObjectJsonHelpers::GetInteger(jCar, "frictionSoundId", 255));
|
||||
car.log_flume_reverser_vehicle_type = ObjectJsonHelpers::GetInteger(jCar, "logFlumeReverserVehicleType");
|
||||
car.sound_range = ObjectJsonHelpers::GetInteger(jCar, "soundRange", 255);
|
||||
car.double_sound_frequency = ObjectJsonHelpers::GetInteger(jCar, "doubleSoundFrequency");
|
||||
car.powered_acceleration = ObjectJsonHelpers::GetInteger(jCar, "poweredAcceleration");
|
||||
car.powered_max_speed = ObjectJsonHelpers::GetInteger(jCar, "poweredMaxSpeed");
|
||||
car.car_visual = ObjectJsonHelpers::GetInteger(jCar, "carVisual");
|
||||
car.effect_visual = ObjectJsonHelpers::GetInteger(jCar, "effectVisual", 1);
|
||||
car.draw_order = ObjectJsonHelpers::GetInteger(jCar, "drawOrder");
|
||||
car.num_vertical_frames_override = ObjectJsonHelpers::GetInteger(jCar, "numVerticalFramesOverride");
|
||||
car.sprite_width = Json::GetNumber<uint8_t>(jCar["spriteWidth"]);
|
||||
car.sprite_height_negative = Json::GetNumber<uint8_t>(jCar["spriteHeightNegative"]);
|
||||
car.sprite_height_positive = Json::GetNumber<uint8_t>(jCar["spriteHeightPositive"]);
|
||||
car.animation = Json::GetNumber<uint8_t>(jCar["animation"]);
|
||||
car.base_num_frames = Json::GetNumber<uint16_t>(jCar["baseNumFrames"]);
|
||||
car.no_vehicle_images = Json::GetNumber<uint32_t>(jCar["numImages"]);
|
||||
car.no_seating_rows = Json::GetNumber<uint8_t>(jCar["numSeatRows"]);
|
||||
car.spinning_inertia = Json::GetNumber<uint8_t>(jCar["spinningInertia"]);
|
||||
car.spinning_friction = Json::GetNumber<uint8_t>(jCar["spinningFriction"]);
|
||||
car.friction_sound_id = Json::GetEnum<SoundId>(jCar["frictionSoundId"], SoundId::Null);
|
||||
car.log_flume_reverser_vehicle_type = Json::GetNumber<uint8_t>(jCar["logFlumeReverserVehicleType"]);
|
||||
car.sound_range = Json::GetNumber<uint8_t>(jCar["soundRange"], 255);
|
||||
car.double_sound_frequency = Json::GetNumber<uint8_t>(jCar["doubleSoundFrequency"]);
|
||||
car.powered_acceleration = Json::GetNumber<uint8_t>(jCar["poweredAcceleration"]);
|
||||
car.powered_max_speed = Json::GetNumber<uint8_t>(jCar["poweredMaxSpeed"]);
|
||||
car.car_visual = Json::GetNumber<uint8_t>(jCar["carVisual"]);
|
||||
car.effect_visual = Json::GetNumber<uint8_t>(jCar["effectVisual"], 1);
|
||||
car.draw_order = Json::GetNumber<uint8_t>(jCar["drawOrder"]);
|
||||
car.num_vertical_frames_override = Json::GetNumber<uint8_t>(jCar["numVerticalFramesOverride"]);
|
||||
|
||||
auto& peepLoadingPositions = car.peep_loading_positions;
|
||||
auto jLoadingPositions = json_object_get(jCar, "loadingPositions");
|
||||
if (json_is_array(jLoadingPositions))
|
||||
auto jLoadingPositions = jCar["loadingPositions"];
|
||||
if (jLoadingPositions.is_array())
|
||||
{
|
||||
auto arr = ObjectJsonHelpers::GetJsonIntegerArray(jLoadingPositions);
|
||||
for (auto x : arr)
|
||||
for (auto& jPos : jLoadingPositions)
|
||||
{
|
||||
peepLoadingPositions.push_back(x);
|
||||
car.peep_loading_positions.push_back(Json::GetNumber<int8_t>(jPos));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& peepLoadingWaypoints = car.peep_loading_waypoints;
|
||||
auto jLoadingWaypoints = json_object_get(jCar, "loadingWaypoints");
|
||||
if (json_is_array(jLoadingWaypoints))
|
||||
auto jLoadingWaypoints = jCar["loadingWaypoints"];
|
||||
if (jLoadingWaypoints.is_array())
|
||||
{
|
||||
car.flags |= VEHICLE_ENTRY_FLAG_LOADING_WAYPOINTS;
|
||||
car.peep_loading_waypoint_segments = Json::GetNumber<uint8_t>(jCar["numSegments"]);
|
||||
|
||||
auto numSegments = ObjectJsonHelpers::GetInteger(jCar, "numSegments");
|
||||
car.peep_loading_waypoint_segments = numSegments;
|
||||
|
||||
size_t i;
|
||||
json_t* route;
|
||||
json_array_foreach(jLoadingWaypoints, i, route)
|
||||
for (auto& jRoute : jLoadingWaypoints)
|
||||
{
|
||||
if (json_is_array(route))
|
||||
if (jRoute.is_array())
|
||||
{
|
||||
size_t j;
|
||||
json_t* waypoint;
|
||||
std::array<CoordsXY, 3> entry;
|
||||
json_array_foreach(route, j, waypoint)
|
||||
|
||||
for (size_t j = 0; j < 3; ++j)
|
||||
{
|
||||
if (json_is_array(waypoint) && json_array_size(waypoint) >= 2)
|
||||
auto jWaypoint = jRoute[j];
|
||||
if (jWaypoint.is_array() && jWaypoint.size() >= 2)
|
||||
{
|
||||
int32_t x = json_integer_value(json_array_get(waypoint, 0));
|
||||
int32_t y = json_integer_value(json_array_get(waypoint, 1));
|
||||
int32_t x = Json::GetNumber<int32_t>(jWaypoint[0]);
|
||||
int32_t y = Json::GetNumber<int32_t>(jWaypoint[1]);
|
||||
entry[j] = { x, y };
|
||||
}
|
||||
}
|
||||
peepLoadingWaypoints.push_back(entry);
|
||||
|
||||
car.peep_loading_waypoints.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto jFrames = json_object_get(jCar, "frames");
|
||||
car.sprite_flags = ObjectJsonHelpers::GetFlags<uint16_t>(
|
||||
jFrames,
|
||||
{
|
||||
{ "flat", VEHICLE_SPRITE_FLAG_FLAT },
|
||||
{ "gentleSlopes", VEHICLE_SPRITE_FLAG_GENTLE_SLOPES },
|
||||
{ "steepSlopes", VEHICLE_SPRITE_FLAG_STEEP_SLOPES },
|
||||
{ "verticalSlopes", VEHICLE_SPRITE_FLAG_VERTICAL_SLOPES },
|
||||
{ "diagonalSlopes", VEHICLE_SPRITE_FLAG_DIAGONAL_SLOPES },
|
||||
{ "flatBanked", VEHICLE_SPRITE_FLAG_FLAT_BANKED },
|
||||
{ "inlineTwists", VEHICLE_SPRITE_FLAG_INLINE_TWISTS },
|
||||
{ "flatToGentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_BANKED_TRANSITIONS },
|
||||
{ "diagonalGentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_DIAGONAL_GENTLE_SLOPE_BANKED_TRANSITIONS },
|
||||
{ "gentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TRANSITIONS },
|
||||
{ "gentleSlopeBankedTurns", VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TURNS },
|
||||
{ "flatToGentleSlopeWhileBankedTransitions", VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_WHILE_BANKED_TRANSITIONS },
|
||||
{ "corkscrews", VEHICLE_SPRITE_FLAG_CORKSCREWS },
|
||||
{ "restraintAnimation", VEHICLE_SPRITE_FLAG_RESTRAINT_ANIMATION },
|
||||
{ "curvedLiftHill", VEHICLE_SPRITE_FLAG_CURVED_LIFT_HILL },
|
||||
{ "VEHICLE_SPRITE_FLAG_15", VEHICLE_SPRITE_FLAG_15 },
|
||||
});
|
||||
auto jFrames = jCar["frames"];
|
||||
if (jFrames.is_object())
|
||||
{
|
||||
car.sprite_flags = Json::GetFlags<uint16_t>(
|
||||
jFrames,
|
||||
{
|
||||
{ "flat", VEHICLE_SPRITE_FLAG_FLAT },
|
||||
{ "gentleSlopes", VEHICLE_SPRITE_FLAG_GENTLE_SLOPES },
|
||||
{ "steepSlopes", VEHICLE_SPRITE_FLAG_STEEP_SLOPES },
|
||||
{ "verticalSlopes", VEHICLE_SPRITE_FLAG_VERTICAL_SLOPES },
|
||||
{ "diagonalSlopes", VEHICLE_SPRITE_FLAG_DIAGONAL_SLOPES },
|
||||
{ "flatBanked", VEHICLE_SPRITE_FLAG_FLAT_BANKED },
|
||||
{ "inlineTwists", VEHICLE_SPRITE_FLAG_INLINE_TWISTS },
|
||||
{ "flatToGentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_BANKED_TRANSITIONS },
|
||||
{ "diagonalGentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_DIAGONAL_GENTLE_SLOPE_BANKED_TRANSITIONS },
|
||||
{ "gentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TRANSITIONS },
|
||||
{ "gentleSlopeBankedTurns", VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TURNS },
|
||||
{ "flatToGentleSlopeWhileBankedTransitions",
|
||||
VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_WHILE_BANKED_TRANSITIONS },
|
||||
{ "corkscrews", VEHICLE_SPRITE_FLAG_CORKSCREWS },
|
||||
{ "restraintAnimation", VEHICLE_SPRITE_FLAG_RESTRAINT_ANIMATION },
|
||||
{ "curvedLiftHill", VEHICLE_SPRITE_FLAG_CURVED_LIFT_HILL },
|
||||
{ "VEHICLE_SPRITE_FLAG_15", VEHICLE_SPRITE_FLAG_15 },
|
||||
});
|
||||
}
|
||||
|
||||
car.flags |= ObjectJsonHelpers::GetFlags<uint32_t>(
|
||||
car.flags |= Json::GetFlags<uint32_t>(
|
||||
jCar,
|
||||
{
|
||||
{ "VEHICLE_ENTRY_FLAG_POWERED_RIDE_UNRESTRICTED_GRAVITY", VEHICLE_ENTRY_FLAG_POWERED_RIDE_UNRESTRICTED_GRAVITY },
|
||||
@@ -856,18 +843,21 @@ rct_ride_entry_vehicle RideObject::ReadJsonCar(const json_t* jCar)
|
||||
{ "VEHICLE_ENTRY_FLAG_GO_KART", VEHICLE_ENTRY_FLAG_GO_KART },
|
||||
{ "VEHICLE_ENTRY_FLAG_DODGEM_CAR_PLACEMENT", VEHICLE_ENTRY_FLAG_DODGEM_CAR_PLACEMENT },
|
||||
});
|
||||
|
||||
return car;
|
||||
}
|
||||
|
||||
vehicle_colour_preset_list RideObject::ReadJsonCarColours(const json_t* jCarColours)
|
||||
vehicle_colour_preset_list RideObject::ReadJsonCarColours(json_t& jCarColours)
|
||||
{
|
||||
Guard::Assert(jCarColours.is_array(), "RideObject::ReadJsonCarColours expects parameter jCarColours to be array");
|
||||
|
||||
// The JSON supports multiple configurations of per car colours, but
|
||||
// the ride entry structure currently doesn't allow for it. Assume that
|
||||
// a single configuration with multiple colour entries is per car scheme.
|
||||
if (json_array_size(jCarColours) == 1)
|
||||
if (jCarColours.size() == 1)
|
||||
{
|
||||
auto firstElement = json_array_get(jCarColours, 0);
|
||||
auto numColours = json_array_size(firstElement);
|
||||
auto firstElement = Json::AsArray(jCarColours[0]);
|
||||
auto numColours = firstElement.size();
|
||||
if (numColours >= 2)
|
||||
{
|
||||
// Read all colours from first config
|
||||
@@ -881,11 +871,9 @@ vehicle_colour_preset_list RideObject::ReadJsonCarColours(const json_t* jCarColo
|
||||
|
||||
// Read first colour for each config
|
||||
vehicle_colour_preset_list list = {};
|
||||
size_t index;
|
||||
const json_t* jConfiguration;
|
||||
json_array_foreach(jCarColours, index, jConfiguration)
|
||||
for (size_t index = 0; index < jCarColours.size(); index++)
|
||||
{
|
||||
auto config = ReadJsonColourConfiguration(jConfiguration);
|
||||
auto config = ReadJsonColourConfiguration(jCarColours[index]);
|
||||
if (config.size() >= 1)
|
||||
{
|
||||
list.list[index] = config[0];
|
||||
@@ -901,27 +889,27 @@ vehicle_colour_preset_list RideObject::ReadJsonCarColours(const json_t* jCarColo
|
||||
return list;
|
||||
}
|
||||
|
||||
std::vector<vehicle_colour> RideObject::ReadJsonColourConfiguration(const json_t* jColourConfig)
|
||||
std::vector<vehicle_colour> RideObject::ReadJsonColourConfiguration(json_t& jColourConfig)
|
||||
{
|
||||
std::vector<vehicle_colour> config;
|
||||
size_t index;
|
||||
const json_t* jColours;
|
||||
json_array_foreach(jColourConfig, index, jColours)
|
||||
|
||||
for (auto& jColours : jColourConfig)
|
||||
{
|
||||
vehicle_colour carColour = {};
|
||||
auto colours = ObjectJsonHelpers::GetJsonStringArray(jColours);
|
||||
|
||||
auto colours = Json::AsArray(jColours);
|
||||
if (colours.size() >= 1)
|
||||
{
|
||||
carColour.main = ObjectJsonHelpers::ParseColour(colours[0]);
|
||||
carColour.main = Colour::FromString(Json::GetString(colours[0]));
|
||||
carColour.additional_1 = carColour.main;
|
||||
carColour.additional_2 = carColour.main;
|
||||
if (colours.size() >= 2)
|
||||
{
|
||||
carColour.additional_1 = ObjectJsonHelpers::ParseColour(colours[1]);
|
||||
carColour.additional_1 = Colour::FromString(Json::GetString(colours[1]));
|
||||
}
|
||||
if (colours.size() >= 3)
|
||||
{
|
||||
carColour.additional_2 = ObjectJsonHelpers::ParseColour(colours[2]);
|
||||
carColour.additional_2 = Colour::FromString(Json::GetString(colours[2]));
|
||||
}
|
||||
}
|
||||
config.push_back(carColour);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../core/IStream.hpp"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../ride/Ride.h"
|
||||
#include "Object.h"
|
||||
|
||||
@@ -34,7 +35,7 @@ public:
|
||||
return &_legacyType;
|
||||
}
|
||||
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
@@ -49,11 +50,11 @@ public:
|
||||
private:
|
||||
void ReadLegacyVehicle(IReadObjectContext* context, OpenRCT2::IStream* stream, rct_ride_entry_vehicle* vehicle);
|
||||
|
||||
void ReadJsonVehicleInfo(IReadObjectContext* context, const json_t* properties);
|
||||
std::vector<rct_ride_entry_vehicle> ReadJsonCars(const json_t* jCars);
|
||||
rct_ride_entry_vehicle ReadJsonCar(const json_t* jCar);
|
||||
vehicle_colour_preset_list ReadJsonCarColours(const json_t* jCarColours);
|
||||
std::vector<vehicle_colour> ReadJsonColourConfiguration(const json_t* jColourConfig);
|
||||
void ReadJsonVehicleInfo(IReadObjectContext* context, json_t& properties);
|
||||
std::vector<rct_ride_entry_vehicle> ReadJsonCars(json_t& jCars);
|
||||
rct_ride_entry_vehicle ReadJsonCar(json_t& jCar);
|
||||
vehicle_colour_preset_list ReadJsonCarColours(json_t& jCarColours);
|
||||
std::vector<vehicle_colour> ReadJsonColourConfiguration(json_t& jColourConfig);
|
||||
|
||||
static uint8_t CalculateNumVerticalFrames(const rct_ride_entry_vehicle* vehicleEntry);
|
||||
static uint8_t CalculateNumHorizontalFrames(const rct_ride_entry_vehicle* vehicleEntry);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../peep/Staff.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
#include "ObjectManager.h"
|
||||
#include "ObjectRepository.h"
|
||||
|
||||
@@ -110,35 +109,29 @@ std::vector<rct_object_entry> SceneryGroupObject::ReadItems(IStream* stream)
|
||||
return items;
|
||||
}
|
||||
|
||||
void SceneryGroupObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void SceneryGroupObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
_legacyType.priority = json_integer_value(json_object_get(properties, "priority"));
|
||||
Guard::Assert(root.is_object(), "SceneryGroupObject::ReadJson expects parameter root to be object");
|
||||
|
||||
// Entertainer costumes
|
||||
auto jCostumes = json_object_get(properties, "entertainerCostumes");
|
||||
if (jCostumes != nullptr)
|
||||
auto properties = root["properties"];
|
||||
|
||||
if (properties.is_object())
|
||||
{
|
||||
_legacyType.entertainer_costumes = ReadJsonEntertainerCostumes(jCostumes);
|
||||
_legacyType.priority = Json::GetNumber<uint8_t>(properties["priority"]);
|
||||
_legacyType.entertainer_costumes = ReadJsonEntertainerCostumes(properties["entertainerCostumes"]);
|
||||
|
||||
_items = ReadJsonEntries(properties["entries"]);
|
||||
}
|
||||
|
||||
auto jEntries = json_object_get(properties, "entries");
|
||||
if (jEntries != nullptr)
|
||||
{
|
||||
_items = ReadJsonEntries(jEntries);
|
||||
}
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
uint32_t SceneryGroupObject::ReadJsonEntertainerCostumes(const json_t* jCostumes)
|
||||
uint32_t SceneryGroupObject::ReadJsonEntertainerCostumes(json_t& jCostumes)
|
||||
{
|
||||
uint32_t costumes = 0;
|
||||
auto szCostumes = ObjectJsonHelpers::GetJsonStringArray(jCostumes);
|
||||
for (const auto& szCostume : szCostumes)
|
||||
for (auto& jCostume : jCostumes)
|
||||
{
|
||||
auto entertainer = ParseEntertainerCostume(szCostume);
|
||||
auto entertainer = ParseEntertainerCostume(Json::GetString(jCostume));
|
||||
auto peepSprite = EntertainerCostumeToSprite(entertainer);
|
||||
costumes |= 1 << (static_cast<uint8_t>(peepSprite));
|
||||
}
|
||||
@@ -172,19 +165,14 @@ EntertainerCostume SceneryGroupObject::ParseEntertainerCostume(const std::string
|
||||
return EntertainerCostume::Panda;
|
||||
}
|
||||
|
||||
std::vector<rct_object_entry> SceneryGroupObject::ReadJsonEntries(const json_t* jEntries)
|
||||
std::vector<rct_object_entry> SceneryGroupObject::ReadJsonEntries(json_t& jEntries)
|
||||
{
|
||||
std::vector<rct_object_entry> entries;
|
||||
size_t index;
|
||||
json_t* jEntry;
|
||||
json_array_foreach(jEntries, index, jEntry)
|
||||
|
||||
for (auto& jEntry : jEntries)
|
||||
{
|
||||
auto entryId = json_string_value(jEntry);
|
||||
if (entryId != nullptr)
|
||||
{
|
||||
auto entry = ObjectJsonHelpers::ParseObjectEntry(entryId);
|
||||
entries.push_back(entry);
|
||||
}
|
||||
auto entry = ParseObjectEntry(Json::GetString(jEntry));
|
||||
entries.push_back(entry);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
{
|
||||
return &_legacyType;
|
||||
}
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void Load() override;
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
|
||||
private:
|
||||
static std::vector<rct_object_entry> ReadItems(OpenRCT2::IStream* stream);
|
||||
static uint32_t ReadJsonEntertainerCostumes(const json_t* jCostumes);
|
||||
static uint32_t ReadJsonEntertainerCostumes(json_t& jCostumes);
|
||||
static EntertainerCostume ParseEntertainerCostume(const std::string& s);
|
||||
static std::vector<rct_object_entry> ReadJsonEntries(const json_t* jEntries);
|
||||
static std::vector<rct_object_entry> ReadJsonEntries(json_t& jEntries);
|
||||
};
|
||||
|
||||
@@ -9,13 +9,11 @@
|
||||
|
||||
#include "SceneryObject.h"
|
||||
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
void SceneryObject::SetPrimarySceneryGroup(const std::string& s)
|
||||
{
|
||||
if (!s.empty())
|
||||
{
|
||||
auto sgEntry = ObjectJsonHelpers::ParseObjectEntry(s);
|
||||
auto sgEntry = ParseObjectEntry(s);
|
||||
SetPrimarySceneryGroup(&sgEntry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "../localisation/Language.h"
|
||||
#include "../world/Scenery.h"
|
||||
#include "../world/SmallScenery.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -234,96 +233,97 @@ rct_object_entry SmallSceneryObject::GetScgAbstrHeader()
|
||||
return Object::CreateHeader("SCGABSTR", 207140231, 932253451);
|
||||
}
|
||||
|
||||
void SmallSceneryObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void SmallSceneryObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
Guard::Assert(root.is_object(), "SmallSceneryObject::ReadJson expects parameter root to be object");
|
||||
|
||||
_legacyType.small_scenery.height = json_integer_value(json_object_get(properties, "height"));
|
||||
_legacyType.small_scenery.tool_id = ObjectJsonHelpers::ParseCursor(
|
||||
ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_STATUE_DOWN);
|
||||
_legacyType.small_scenery.price = json_integer_value(json_object_get(properties, "price"));
|
||||
_legacyType.small_scenery.removal_price = json_integer_value(json_object_get(properties, "removalPrice"));
|
||||
_legacyType.small_scenery.animation_delay = json_integer_value(json_object_get(properties, "animationDelay"));
|
||||
_legacyType.small_scenery.animation_mask = json_integer_value(json_object_get(properties, "animationMask"));
|
||||
_legacyType.small_scenery.num_frames = json_integer_value(json_object_get(properties, "numFrames"));
|
||||
auto properties = root["properties"];
|
||||
|
||||
// Flags
|
||||
_legacyType.small_scenery.flags = ObjectJsonHelpers::GetFlags<uint32_t>(
|
||||
properties,
|
||||
{
|
||||
{ "SMALL_SCENERY_FLAG_VOFFSET_CENTRE", SMALL_SCENERY_FLAG_VOFFSET_CENTRE },
|
||||
{ "requiresFlatSurface", SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE },
|
||||
{ "isRotatable", SMALL_SCENERY_FLAG_ROTATABLE },
|
||||
{ "isAnimated", SMALL_SCENERY_FLAG_ANIMATED },
|
||||
{ "canWither", SMALL_SCENERY_FLAG_CAN_WITHER },
|
||||
{ "canBeWatered", SMALL_SCENERY_FLAG_CAN_BE_WATERED },
|
||||
{ "hasOverlayImage", SMALL_SCENERY_FLAG_ANIMATED_FG },
|
||||
{ "hasGlass", SMALL_SCENERY_FLAG_HAS_GLASS },
|
||||
{ "hasPrimaryColour", SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR },
|
||||
{ "SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1", SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1 },
|
||||
{ "SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4", SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4 },
|
||||
{ "isClock", SMALL_SCENERY_FLAG_IS_CLOCK },
|
||||
{ "SMALL_SCENERY_FLAG_SWAMP_GOO", SMALL_SCENERY_FLAG_SWAMP_GOO },
|
||||
{ "SMALL_SCENERY_FLAG17", SMALL_SCENERY_FLAG17 },
|
||||
{ "isStackable", SMALL_SCENERY_FLAG_STACKABLE },
|
||||
{ "prohibitWalls", SMALL_SCENERY_FLAG_NO_WALLS },
|
||||
{ "hasSecondaryColour", SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR },
|
||||
{ "hasNoSupports", SMALL_SCENERY_FLAG_NO_SUPPORTS },
|
||||
{ "SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED", SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED },
|
||||
{ "SMALL_SCENERY_FLAG_COG", SMALL_SCENERY_FLAG_COG },
|
||||
{ "allowSupportsAbove", SMALL_SCENERY_FLAG_BUILD_DIRECTLY_ONTOP },
|
||||
{ "supportsHavePrimaryColour", SMALL_SCENERY_FLAG_PAINT_SUPPORTS },
|
||||
{ "SMALL_SCENERY_FLAG27", SMALL_SCENERY_FLAG27 },
|
||||
{ "isTree", SMALL_SCENERY_FLAG_IS_TREE },
|
||||
});
|
||||
|
||||
// Determine shape flags from a shape string
|
||||
auto shape = ObjectJsonHelpers::GetString(properties, "shape");
|
||||
if (!shape.empty())
|
||||
if (properties.is_object())
|
||||
{
|
||||
auto quarters = shape.substr(0, 3);
|
||||
if (quarters == "2/4")
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE | SMALL_SCENERY_FLAG_HALF_SPACE;
|
||||
}
|
||||
else if (quarters == "3/4")
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE | SMALL_SCENERY_FLAG_THREE_QUARTERS;
|
||||
}
|
||||
else if (quarters == "4/4")
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE;
|
||||
}
|
||||
if (shape.size() >= 5)
|
||||
{
|
||||
if ((shape.substr(3) == "+D"))
|
||||
_legacyType.small_scenery.height = Json::GetNumber<uint8_t>(properties["height"]);
|
||||
_legacyType.small_scenery.tool_id = Cursor::FromString(Json::GetString(properties["cursor"]), CURSOR_STATUE_DOWN);
|
||||
_legacyType.small_scenery.price = Json::GetNumber<uint16_t>(properties["price"]);
|
||||
_legacyType.small_scenery.removal_price = Json::GetNumber<uint16_t>(properties["removalPrice"]);
|
||||
_legacyType.small_scenery.animation_delay = Json::GetNumber<uint16_t>(properties["animationDelay"]);
|
||||
_legacyType.small_scenery.animation_mask = Json::GetNumber<uint16_t>(properties["animationMask"]);
|
||||
_legacyType.small_scenery.num_frames = Json::GetNumber<uint16_t>(properties["numFrames"]);
|
||||
|
||||
_legacyType.small_scenery.flags = Json::GetFlags<uint32_t>(
|
||||
properties,
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_DIAGONAL;
|
||||
{ "SMALL_SCENERY_FLAG_VOFFSET_CENTRE", SMALL_SCENERY_FLAG_VOFFSET_CENTRE },
|
||||
{ "requiresFlatSurface", SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE },
|
||||
{ "isRotatable", SMALL_SCENERY_FLAG_ROTATABLE },
|
||||
{ "isAnimated", SMALL_SCENERY_FLAG_ANIMATED },
|
||||
{ "canWither", SMALL_SCENERY_FLAG_CAN_WITHER },
|
||||
{ "canBeWatered", SMALL_SCENERY_FLAG_CAN_BE_WATERED },
|
||||
{ "hasOverlayImage", SMALL_SCENERY_FLAG_ANIMATED_FG },
|
||||
{ "hasGlass", SMALL_SCENERY_FLAG_HAS_GLASS },
|
||||
{ "hasPrimaryColour", SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR },
|
||||
{ "SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1", SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1 },
|
||||
{ "SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4", SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4 },
|
||||
{ "isClock", SMALL_SCENERY_FLAG_IS_CLOCK },
|
||||
{ "SMALL_SCENERY_FLAG_SWAMP_GOO", SMALL_SCENERY_FLAG_SWAMP_GOO },
|
||||
{ "SMALL_SCENERY_FLAG17", SMALL_SCENERY_FLAG17 },
|
||||
{ "isStackable", SMALL_SCENERY_FLAG_STACKABLE },
|
||||
{ "prohibitWalls", SMALL_SCENERY_FLAG_NO_WALLS },
|
||||
{ "hasSecondaryColour", SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR },
|
||||
{ "hasNoSupports", SMALL_SCENERY_FLAG_NO_SUPPORTS },
|
||||
{ "SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED", SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED },
|
||||
{ "SMALL_SCENERY_FLAG_COG", SMALL_SCENERY_FLAG_COG },
|
||||
{ "allowSupportsAbove", SMALL_SCENERY_FLAG_BUILD_DIRECTLY_ONTOP },
|
||||
{ "supportsHavePrimaryColour", SMALL_SCENERY_FLAG_PAINT_SUPPORTS },
|
||||
{ "SMALL_SCENERY_FLAG27", SMALL_SCENERY_FLAG27 },
|
||||
{ "isTree", SMALL_SCENERY_FLAG_IS_TREE },
|
||||
});
|
||||
|
||||
// Determine shape flags from a shape string
|
||||
auto shape = Json::GetString(properties["shape"]);
|
||||
if (!shape.empty())
|
||||
{
|
||||
auto quarters = shape.substr(0, 3);
|
||||
if (quarters == "2/4")
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE | SMALL_SCENERY_FLAG_HALF_SPACE;
|
||||
}
|
||||
else if (quarters == "3/4")
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE | SMALL_SCENERY_FLAG_THREE_QUARTERS;
|
||||
}
|
||||
else if (quarters == "4/4")
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE;
|
||||
}
|
||||
if (shape.size() >= 5)
|
||||
{
|
||||
if ((shape.substr(3) == "+D"))
|
||||
{
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_DIAGONAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto jFrameOffsets = properties["frameOffsets"];
|
||||
if (jFrameOffsets.is_array())
|
||||
{
|
||||
_frameOffsets = ReadJsonFrameOffsets(jFrameOffsets);
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_HAS_FRAME_OFFSETS;
|
||||
}
|
||||
|
||||
SetPrimarySceneryGroup(Json::GetString(properties["sceneryGroup"]));
|
||||
}
|
||||
|
||||
auto jFrameOffsets = json_object_get(properties, "frameOffsets");
|
||||
if (jFrameOffsets != nullptr)
|
||||
{
|
||||
_frameOffsets = ReadJsonFrameOffsets(jFrameOffsets);
|
||||
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_HAS_FRAME_OFFSETS;
|
||||
}
|
||||
|
||||
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SmallSceneryObject::ReadJsonFrameOffsets(const json_t* jFrameOffsets)
|
||||
std::vector<uint8_t> SmallSceneryObject::ReadJsonFrameOffsets(json_t& jFrameOffsets)
|
||||
{
|
||||
std::vector<uint8_t> offsets;
|
||||
size_t index;
|
||||
const json_t* jOffset;
|
||||
json_array_foreach(jFrameOffsets, index, jOffset)
|
||||
|
||||
for (auto& jOffset : jFrameOffsets)
|
||||
{
|
||||
offsets.push_back(json_integer_value(jOffset));
|
||||
offsets.push_back(Json::GetNumber<uint8_t>(jOffset));
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
private:
|
||||
static std::vector<uint8_t> ReadFrameOffsets(OpenRCT2::IStream* stream);
|
||||
static std::vector<uint8_t> ReadJsonFrameOffsets(const json_t* jFrameOffsets);
|
||||
static std::vector<uint8_t> ReadJsonFrameOffsets(json_t& jFrameOffsets);
|
||||
void PerformFixes();
|
||||
rct_object_entry GetScgPiratHeader();
|
||||
rct_object_entry GetScgMineHeader();
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../localisation/Localisation.h"
|
||||
#include "../world/Banner.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
void StationObject::Load()
|
||||
{
|
||||
@@ -78,17 +77,24 @@ void StationObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t h
|
||||
}
|
||||
}
|
||||
|
||||
void StationObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void StationObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
Height = ObjectJsonHelpers::GetInteger(properties, "height", 0);
|
||||
ScrollingMode = ObjectJsonHelpers::GetInteger(properties, "scrollingMode", SCROLLING_MODE_NONE);
|
||||
Flags = ObjectJsonHelpers::GetFlags<uint32_t>(
|
||||
properties,
|
||||
{ { "hasPrimaryColour", STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR },
|
||||
{ "hasSecondaryColour", STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR },
|
||||
{ "isTransparent", STATION_OBJECT_FLAGS::IS_TRANSPARENT } });
|
||||
Guard::Assert(root.is_object(), "StationObject::ReadJson expects parameter root to be object");
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
auto properties = root["properties"];
|
||||
|
||||
if (properties.is_object())
|
||||
{
|
||||
Height = Json::GetNumber<int32_t>(properties["height"]);
|
||||
ScrollingMode = Json::GetNumber<uint8_t>(properties["scrollingMode"], SCROLLING_MODE_NONE);
|
||||
Flags = Json::GetFlags<uint32_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasPrimaryColour", STATION_OBJECT_FLAGS::HAS_PRIMARY_COLOUR },
|
||||
{ "hasSecondaryColour", STATION_OBJECT_FLAGS::HAS_SECONDARY_COLOUR },
|
||||
{ "isTransparent", STATION_OBJECT_FLAGS::IS_TRANSPARENT },
|
||||
});
|
||||
}
|
||||
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -78,6 +78,45 @@ void StringTable::Read(IReadObjectContext* context, OpenRCT2::IStream* stream, O
|
||||
Sort();
|
||||
}
|
||||
|
||||
ObjectStringID StringTable::ParseStringId(const std::string& s)
|
||||
{
|
||||
if (s == "name")
|
||||
return ObjectStringID::NAME;
|
||||
if (s == "description")
|
||||
return ObjectStringID::DESCRIPTION;
|
||||
if (s == "capacity")
|
||||
return ObjectStringID::CAPACITY;
|
||||
if (s == "vehicleName")
|
||||
return ObjectStringID::VEHICLE_NAME;
|
||||
return ObjectStringID::UNKNOWN;
|
||||
}
|
||||
|
||||
void StringTable::ReadJson(json_t& root)
|
||||
{
|
||||
Guard::Assert(root.is_object(), "StringTable::ReadJson expects parameter root to be object");
|
||||
|
||||
// We trust the JSON type of root is object
|
||||
auto jsonStrings = root["strings"];
|
||||
|
||||
for (auto& [key, jsonLanguages] : jsonStrings.items())
|
||||
{
|
||||
auto stringId = ParseStringId(key);
|
||||
if (stringId != ObjectStringID::UNKNOWN)
|
||||
{
|
||||
for (auto& [locale, jsonString] : jsonLanguages.items())
|
||||
{
|
||||
auto langId = language_get_id_from_locale(locale.c_str());
|
||||
if (langId != LANGUAGE_UNDEFINED)
|
||||
{
|
||||
auto string = Json::GetString(jsonString);
|
||||
SetString(stringId, langId, string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Sort();
|
||||
}
|
||||
|
||||
std::string StringTable::GetString(ObjectStringID id) const
|
||||
{
|
||||
for (auto& string : _strings)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../localisation/Language.h"
|
||||
|
||||
#include <string>
|
||||
@@ -44,6 +45,7 @@ class StringTable
|
||||
{
|
||||
private:
|
||||
std::vector<StringTableEntry> _strings;
|
||||
static ObjectStringID ParseStringId(const std::string& s);
|
||||
|
||||
public:
|
||||
StringTable() = default;
|
||||
@@ -51,6 +53,10 @@ public:
|
||||
StringTable& operator=(const StringTable&) = delete;
|
||||
|
||||
void Read(IReadObjectContext* context, OpenRCT2::IStream* stream, ObjectStringID id);
|
||||
/**
|
||||
* @note root is deliberately left non-const: json_t behaviour changes when const
|
||||
*/
|
||||
void ReadJson(json_t& root);
|
||||
void Sort();
|
||||
std::string GetString(ObjectStringID id) const;
|
||||
std::string GetString(uint8_t language, ObjectStringID id) const;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "../core/String.hpp"
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../localisation/Localisation.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
void TerrainEdgeObject::Load()
|
||||
{
|
||||
@@ -44,12 +43,17 @@ void TerrainEdgeObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32
|
||||
gfx_draw_sprite(dpi, imageId + 5, screenCoords + ScreenCoordsXY{ 8, 8 }, 0);
|
||||
}
|
||||
|
||||
void TerrainEdgeObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void TerrainEdgeObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
HasDoors = ObjectJsonHelpers::GetBoolean(properties, "hasDoors", false);
|
||||
Guard::Assert(root.is_object(), "TerrainEdgeObject::ReadJson expects parameter root to be object");
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
auto properties = root["properties"];
|
||||
|
||||
if (properties.is_object())
|
||||
{
|
||||
HasDoors = Json::GetBoolean(properties["hasDoors"]);
|
||||
}
|
||||
|
||||
PopulateTablesFromJson(context, root);
|
||||
NumImagesLoaded = GetImageTable().GetCount();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "../drawing/Drawing.h"
|
||||
#include "../localisation/Localisation.h"
|
||||
#include "../world/Location.hpp"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
void TerrainSurfaceObject::Load()
|
||||
{
|
||||
@@ -74,24 +73,45 @@ void TerrainSurfaceObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, in
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSurfaceObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void TerrainSurfaceObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
Colour = ObjectJsonHelpers::ParseColour(ObjectJsonHelpers::GetString(properties, "colour"), 255);
|
||||
Rotations = ObjectJsonHelpers::GetInteger(properties, "rotations", 1);
|
||||
Price = ObjectJsonHelpers::GetInteger(properties, "price", 0);
|
||||
Flags = ObjectJsonHelpers::GetFlags<TERRAIN_SURFACE_FLAGS>(
|
||||
properties,
|
||||
{ { "smoothWithSelf", TERRAIN_SURFACE_FLAGS::SMOOTH_WITH_SELF },
|
||||
{ "smoothWithOther", TERRAIN_SURFACE_FLAGS::SMOOTH_WITH_OTHER },
|
||||
{ "canGrow", TERRAIN_SURFACE_FLAGS::CAN_GROW } });
|
||||
Guard::Assert(root.is_object(), "TerrainSurfaceObject::ReadJson expects parameter root to be object");
|
||||
|
||||
auto jDefault = json_object_get(root, "default");
|
||||
if (json_is_object(jDefault))
|
||||
auto properties = root["properties"];
|
||||
|
||||
if (properties.is_object())
|
||||
{
|
||||
DefaultEntry = ObjectJsonHelpers::GetInteger(properties, "normal");
|
||||
DefaultGridEntry = ObjectJsonHelpers::GetInteger(properties, "grid");
|
||||
DefaultUndergroundEntry = ObjectJsonHelpers::GetInteger(properties, "underground");
|
||||
Colour = Colour::FromString(Json::GetString(properties["colour"]), 255);
|
||||
Rotations = Json::GetNumber<int8_t>(properties["rotations"], 1);
|
||||
Price = Json::GetNumber<money32>(properties["price"]);
|
||||
Flags = Json::GetFlags<TERRAIN_SURFACE_FLAGS>(
|
||||
properties,
|
||||
{ { "smoothWithSelf", TERRAIN_SURFACE_FLAGS::SMOOTH_WITH_SELF },
|
||||
{ "smoothWithOther", TERRAIN_SURFACE_FLAGS::SMOOTH_WITH_OTHER },
|
||||
{ "canGrow", TERRAIN_SURFACE_FLAGS::CAN_GROW } });
|
||||
|
||||
for (auto& el : properties["special"])
|
||||
{
|
||||
if (el.is_object())
|
||||
{
|
||||
SpecialEntry entry;
|
||||
entry.Index = Json::GetNumber<uint32_t>(el["index"]);
|
||||
entry.Length = Json::GetNumber<int32_t>(el["length"], -1);
|
||||
entry.Rotation = Json::GetNumber<int32_t>(el["rotation"], -1);
|
||||
entry.Variation = Json::GetNumber<int32_t>(el["variation"], -1);
|
||||
entry.Grid = Json::GetBoolean(el["grid"]);
|
||||
entry.Underground = Json::GetBoolean(el["underground"]);
|
||||
SpecialEntries.push_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto jDefault = root["default"];
|
||||
if (jDefault.is_object())
|
||||
{
|
||||
DefaultEntry = Json::GetNumber<uint32_t>(jDefault["normal"]);
|
||||
DefaultGridEntry = Json::GetNumber<uint32_t>(jDefault["grid"]);
|
||||
DefaultUndergroundEntry = Json::GetNumber<uint32_t>(jDefault["underground"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -100,26 +120,7 @@ void TerrainSurfaceObject::ReadJson(IReadObjectContext* context, const json_t* r
|
||||
DefaultUndergroundEntry = 2;
|
||||
}
|
||||
|
||||
auto jSpecialArray = json_object_get(properties, "special");
|
||||
if (json_is_array(jSpecialArray))
|
||||
{
|
||||
size_t i;
|
||||
json_t* el;
|
||||
json_array_foreach(jSpecialArray, i, el)
|
||||
{
|
||||
SpecialEntry entry;
|
||||
entry.Index = ObjectJsonHelpers::GetInteger(el, "index");
|
||||
entry.Length = ObjectJsonHelpers::GetInteger(el, "length", -1);
|
||||
entry.Rotation = ObjectJsonHelpers::GetInteger(el, "rotation", -1);
|
||||
entry.Variation = ObjectJsonHelpers::GetInteger(el, "variation", -1);
|
||||
entry.Grid = ObjectJsonHelpers::GetBoolean(el, "grid");
|
||||
entry.Underground = ObjectJsonHelpers::GetBoolean(el, "underground");
|
||||
SpecialEntries.push_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
uint32_t TerrainSurfaceObject::GetImageId(
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "../interface/Cursors.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../world/Banner.h"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
void WallObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream)
|
||||
{
|
||||
@@ -94,66 +93,63 @@ void WallObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t heig
|
||||
}
|
||||
}
|
||||
|
||||
void WallObject::ReadJson(IReadObjectContext* context, const json_t* root)
|
||||
void WallObject::ReadJson(IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
Guard::Assert(root.is_object(), "WallObject::ReadJson expects parameter root to be object");
|
||||
|
||||
_legacyType.wall.tool_id = ObjectJsonHelpers::ParseCursor(
|
||||
ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_FENCE_DOWN);
|
||||
_legacyType.wall.height = json_integer_value(json_object_get(properties, "height"));
|
||||
_legacyType.wall.price = json_integer_value(json_object_get(properties, "price"));
|
||||
auto properties = root["properties"];
|
||||
|
||||
auto jScrollingMode = json_object_get(properties, "scrollingMode");
|
||||
_legacyType.wall.scrolling_mode = jScrollingMode != nullptr ? json_integer_value(jScrollingMode) : SCROLLING_MODE_NONE;
|
||||
|
||||
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
|
||||
|
||||
// Flags
|
||||
_legacyType.wall.flags = ObjectJsonHelpers::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasPrimaryColour", WALL_SCENERY_HAS_PRIMARY_COLOUR },
|
||||
{ "hasSecondaryColour", WALL_SCENERY_HAS_SECONDARY_COLOUR },
|
||||
{ "hasTernaryColour", WALL_SCENERY_HAS_TERNARY_COLOUR },
|
||||
{ "hasGlass", WALL_SCENERY_HAS_GLASS },
|
||||
{ "isBanner", WALL_SCENERY_IS_BANNER },
|
||||
{ "isDoor", WALL_SCENERY_IS_DOOR },
|
||||
{ "isLongDoorAnimation", WALL_SCENERY_LONG_DOOR_ANIMATION },
|
||||
});
|
||||
_legacyType.wall.flags2 = ObjectJsonHelpers::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "isOpaque", WALL_SCENERY_2_IS_OPAQUE },
|
||||
{ "isAnimated", WALL_SCENERY_2_ANIMATED },
|
||||
});
|
||||
|
||||
// HACK To avoid 'negated' properties in JSON, handle this separately until
|
||||
// flag is inverted in this code base.
|
||||
if (!ObjectJsonHelpers::GetBoolean(properties, "isAllowedOnSlope", false))
|
||||
if (properties.is_object())
|
||||
{
|
||||
_legacyType.wall.flags |= WALL_SCENERY_CANT_BUILD_ON_SLOPE;
|
||||
}
|
||||
_legacyType.wall.tool_id = Cursor::FromString(Json::GetString(properties["cursor"]), CURSOR_FENCE_DOWN);
|
||||
_legacyType.wall.height = Json::GetNumber<uint8_t>(properties["height"]);
|
||||
_legacyType.wall.price = Json::GetNumber<int16_t>(properties["price"]);
|
||||
|
||||
// HACK WALL_SCENERY_HAS_PRIMARY_COLOUR actually means, has any colour but we simplify the
|
||||
// JSON and handle this on load. We should change code base in future to reflect the JSON.
|
||||
if (!(_legacyType.wall.flags & WALL_SCENERY_HAS_PRIMARY_COLOUR))
|
||||
{
|
||||
if ((_legacyType.wall.flags & WALL_SCENERY_HAS_SECONDARY_COLOUR)
|
||||
|| (_legacyType.wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR))
|
||||
_legacyType.wall.scrolling_mode = Json::GetNumber<uint8_t>(properties["scrollingMode"], SCROLLING_MODE_NONE);
|
||||
|
||||
SetPrimarySceneryGroup(Json::GetString(properties["sceneryGroup"]));
|
||||
|
||||
// clang-format off
|
||||
_legacyType.wall.flags = Json::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "hasPrimaryColour", WALL_SCENERY_HAS_PRIMARY_COLOUR, Json::FlagType::Normal },
|
||||
{ "IsAllowedOnSlope", WALL_SCENERY_CANT_BUILD_ON_SLOPE, Json::FlagType::Inverted },
|
||||
{ "hasSecondaryColour", WALL_SCENERY_HAS_SECONDARY_COLOUR, Json::FlagType::Normal },
|
||||
{ "hasTernaryColour", WALL_SCENERY_HAS_TERNARY_COLOUR, Json::FlagType::Normal },
|
||||
{ "hasGlass", WALL_SCENERY_HAS_GLASS, Json::FlagType::Normal },
|
||||
{ "isBanner", WALL_SCENERY_IS_BANNER, Json::FlagType::Normal },
|
||||
{ "isDoor", WALL_SCENERY_IS_DOOR, Json::FlagType::Normal },
|
||||
{ "isLongDoorAnimation", WALL_SCENERY_LONG_DOOR_ANIMATION, Json::FlagType::Normal },
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
_legacyType.wall.flags2 = Json::GetFlags<uint8_t>(
|
||||
properties,
|
||||
{
|
||||
{ "isOpaque", WALL_SCENERY_2_IS_OPAQUE },
|
||||
{ "isAnimated", WALL_SCENERY_2_ANIMATED },
|
||||
});
|
||||
|
||||
// HACK WALL_SCENERY_HAS_PRIMARY_COLOUR actually means, has any colour but we simplify the
|
||||
// JSON and handle this on load. We should change code base in future to reflect the JSON.
|
||||
if (!(_legacyType.wall.flags & WALL_SCENERY_HAS_PRIMARY_COLOUR))
|
||||
{
|
||||
_legacyType.wall.flags |= WALL_SCENERY_HAS_PRIMARY_COLOUR;
|
||||
_legacyType.wall.flags2 |= WALL_SCENERY_2_NO_SELECT_PRIMARY_COLOUR;
|
||||
if (_legacyType.wall.flags & (WALL_SCENERY_HAS_SECONDARY_COLOUR | WALL_SCENERY_HAS_TERNARY_COLOUR))
|
||||
{
|
||||
_legacyType.wall.flags |= WALL_SCENERY_HAS_PRIMARY_COLOUR;
|
||||
_legacyType.wall.flags2 |= WALL_SCENERY_2_NO_SELECT_PRIMARY_COLOUR;
|
||||
}
|
||||
}
|
||||
|
||||
// Door sound
|
||||
auto jDoorSound = properties["doorSound"];
|
||||
if (jDoorSound.is_number())
|
||||
{
|
||||
auto doorSound = Json::GetNumber<uint8_t>(jDoorSound);
|
||||
_legacyType.wall.flags2 |= (doorSound << WALL_SCENERY_2_DOOR_SOUND_SHIFT) & WALL_SCENERY_2_DOOR_SOUND_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
// Door sound
|
||||
auto jDoorSound = json_object_get(properties, "doorSound");
|
||||
if (jDoorSound != nullptr)
|
||||
{
|
||||
auto doorSound = json_integer_value(jDoorSound);
|
||||
_legacyType.wall.flags2 |= (doorSound << WALL_SCENERY_2_DOOR_SOUND_SHIFT) & WALL_SCENERY_2_DOOR_SOUND_MASK;
|
||||
}
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
|
||||
PopulateTablesFromJson(context, root);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
}
|
||||
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "../localisation/Language.h"
|
||||
#include "../localisation/StringIds.h"
|
||||
#include "../world/Location.hpp"
|
||||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -55,52 +54,57 @@ void WaterObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t hei
|
||||
gfx_draw_string_centred(dpi, STR_WINDOW_NO_IMAGE, screenCoords, COLOUR_BLACK, nullptr);
|
||||
}
|
||||
|
||||
void WaterObject::ReadJson([[maybe_unused]] IReadObjectContext* context, const json_t* root)
|
||||
void WaterObject::ReadJson([[maybe_unused]] IReadObjectContext* context, json_t& root)
|
||||
{
|
||||
auto properties = json_object_get(root, "properties");
|
||||
_legacyType.flags = ObjectJsonHelpers::GetFlags<uint16_t>(
|
||||
properties,
|
||||
{
|
||||
{ "allowDucks", WATER_FLAGS_ALLOW_DUCKS },
|
||||
});
|
||||
Guard::Assert(root.is_object(), "WaterObject::ReadJson expects parameter root to be object");
|
||||
|
||||
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
|
||||
auto properties = root["properties"];
|
||||
|
||||
// Images which are actually palette data
|
||||
static const char* paletteNames[] = {
|
||||
"general", "waves-0", "waves-1", "waves-2", "sparkles-0", "sparkles-1", "sparkles-2",
|
||||
};
|
||||
for (auto paletteName : paletteNames)
|
||||
PopulateTablesFromJson(context, root);
|
||||
|
||||
if (properties.is_object())
|
||||
{
|
||||
auto jPalettes = json_object_get(properties, "palettes");
|
||||
if (jPalettes != nullptr)
|
||||
{
|
||||
auto jPalette = json_object_get(jPalettes, paletteName);
|
||||
if (jPalette != nullptr)
|
||||
_legacyType.flags = Json::GetFlags<uint16_t>(
|
||||
properties,
|
||||
{
|
||||
ReadJsonPalette(jPalette);
|
||||
{ "allowDucks", WATER_FLAGS_ALLOW_DUCKS },
|
||||
});
|
||||
|
||||
auto jPalettes = properties["palettes"];
|
||||
if (jPalettes.is_object())
|
||||
{
|
||||
// Images which are actually palette data
|
||||
static const char* paletteNames[] = {
|
||||
"general", "waves-0", "waves-1", "waves-2", "sparkles-0", "sparkles-1", "sparkles-2",
|
||||
};
|
||||
for (auto paletteName : paletteNames)
|
||||
{
|
||||
auto jPalette = jPalettes[paletteName];
|
||||
if (jPalette.is_object())
|
||||
{
|
||||
ReadJsonPalette(jPalette);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaterObject::ReadJsonPalette(const json_t* jPalette)
|
||||
void WaterObject::ReadJsonPalette(json_t& jPalette)
|
||||
{
|
||||
auto paletteStartIndex = json_integer_value(json_object_get(jPalette, "index"));
|
||||
auto jColours = json_object_get(jPalette, "colours");
|
||||
auto numColours = json_array_size(jColours);
|
||||
Guard::Assert(jPalette.is_object(), "WaterObject::ReadJsonPalette expects parameter jPalette to be object");
|
||||
|
||||
auto jColours = jPalette["colours"];
|
||||
auto numColours = jColours.size();
|
||||
|
||||
// This pointer gets memcopied in ImageTable::AddImage so it's fine for the unique_ptr to go out of scope
|
||||
auto data = std::make_unique<uint8_t[]>(numColours * 3);
|
||||
size_t dataIndex = 0;
|
||||
|
||||
size_t index;
|
||||
const json_t* jColour;
|
||||
json_array_foreach(jColours, index, jColour)
|
||||
for (auto& jColour : jColours)
|
||||
{
|
||||
auto szColour = json_string_value(jColour);
|
||||
if (szColour != nullptr)
|
||||
if (jColour.is_string())
|
||||
{
|
||||
auto colour = ParseColour(szColour);
|
||||
auto colour = ParseColour(Json::GetString(jColour));
|
||||
data[dataIndex + 0] = (colour >> 16) & 0xFF;
|
||||
data[dataIndex + 1] = (colour >> 8) & 0xFF;
|
||||
data[dataIndex + 2] = colour & 0xFF;
|
||||
@@ -111,7 +115,7 @@ void WaterObject::ReadJsonPalette(const json_t* jPalette)
|
||||
rct_g1_element g1 = {};
|
||||
g1.offset = data.get();
|
||||
g1.width = static_cast<int16_t>(numColours);
|
||||
g1.x_offset = static_cast<int16_t>(paletteStartIndex);
|
||||
g1.x_offset = Json::GetNumber<int16_t>(jPalette["index"]);
|
||||
g1.flags = G1_FLAG_PALETTE;
|
||||
|
||||
auto& imageTable = GetImageTable();
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
return &_legacyType;
|
||||
}
|
||||
|
||||
void ReadJson(IReadObjectContext* context, const json_t* root) override;
|
||||
void ReadJson(IReadObjectContext* context, json_t& root) override;
|
||||
void ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* stream) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
@@ -38,6 +38,6 @@ public:
|
||||
void DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int32_t height) const override;
|
||||
|
||||
private:
|
||||
void ReadJsonPalette(const json_t* jPalette);
|
||||
void ReadJsonPalette(json_t& jPalette);
|
||||
uint32_t ParseColour(const std::string& s) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user