Repository: udoprog/c10t Branch: master Commit: 4b9cb2e173f1 Files: 140 Total size: 441.8 KB Directory structure: gitextract_o4qlv98t/ ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── ChangeLog ├── LICENSE.txt ├── README.md ├── dist/ │ ├── Makefile.osx │ ├── README.md │ ├── all.sh │ ├── config.mk │ ├── make.sh │ └── targets/ │ ├── x86-linux │ ├── x86-windows │ ├── x86_64-linux │ └── x86_64-windows ├── docs/ │ ├── NBT.txt │ ├── bigtest.nbt │ └── test.nbt ├── gui/ │ └── c10t-tk/ │ ├── README │ ├── c10t-tk.py │ ├── mainwindow.py │ └── tooltip.py ├── palette.json ├── res/ │ ├── example.html │ └── libc10t.js ├── scripts/ │ ├── area-set.sh │ ├── area-slice.sh │ ├── generate-tests.sh │ └── google-api/ │ ├── README.txt │ ├── google-api.php │ ├── google-api.ps1 │ ├── google-api.sh │ ├── index.html │ └── libc10t.google.js ├── src/ │ ├── 2d/ │ │ └── cube.hpp │ ├── algorithm.cpp │ ├── algorithm.hpp │ ├── altitude_graph.cpp │ ├── altitude_graph.hpp │ ├── cache.hpp │ ├── dirlist.cpp │ ├── dirlist.hpp │ ├── dlopen.cpp │ ├── dlopen.hpp │ ├── engine/ │ │ ├── CMakeLists.txt │ │ ├── block_rotation.cpp │ │ ├── block_rotation.hpp │ │ ├── engine_base.hpp │ │ ├── engine_core.hpp │ │ ├── engine_settings.hpp │ │ ├── fatiso_engine.cpp │ │ ├── fatiso_engine.hpp │ │ ├── flat_base.cpp │ │ ├── flat_base.hpp │ │ ├── functions.cpp │ │ ├── functions.hpp │ │ ├── isometric_base.cpp │ │ ├── isometric_base.hpp │ │ ├── isometric_engine.cpp │ │ ├── isometric_engine.hpp │ │ ├── oblique_engine.cpp │ │ ├── oblique_engine.hpp │ │ ├── obliqueangle_engine.cpp │ │ ├── obliqueangle_engine.hpp │ │ ├── topdown_engine.cpp │ │ └── topdown_engine.hpp │ ├── fileutils.cpp │ ├── fileutils.hpp │ ├── generate_map.cpp │ ├── generate_map.hpp │ ├── generate_statistics.cpp │ ├── generate_statistics.hpp │ ├── image/ │ │ ├── CMakeLists.txt │ │ ├── algorithms.cpp │ │ ├── algorithms.hpp │ │ ├── cached_image.cpp │ │ ├── cached_image.hpp │ │ ├── color.cpp │ │ ├── color.hpp │ │ ├── format/ │ │ │ ├── base.hpp │ │ │ └── png.hpp │ │ ├── image_base.cpp │ │ ├── image_base.hpp │ │ ├── image_operations.cpp │ │ ├── image_operations.hpp │ │ ├── memory_image.cpp │ │ ├── memory_image.hpp │ │ └── virtual_image.hpp │ ├── json.cpp │ ├── json.hpp │ ├── main.cpp │ ├── main_utils.cpp │ ├── main_utils.hpp │ ├── marker.cpp │ ├── marker.hpp │ ├── mc/ │ │ ├── CMakeLists.txt │ │ ├── blocks.cpp │ │ ├── blocks.hpp │ │ ├── dynamic_buffer.cpp │ │ ├── dynamic_buffer.hpp │ │ ├── level.cpp │ │ ├── level.hpp │ │ ├── level_info.cpp │ │ ├── level_info.hpp │ │ ├── marker.cpp │ │ ├── marker.hpp │ │ ├── region.cpp │ │ ├── region.hpp │ │ ├── region_inspect.cpp │ │ ├── region_iterator.cpp │ │ ├── region_iterator.hpp │ │ ├── rotated_level_info.cpp │ │ ├── rotated_level_info.hpp │ │ ├── utils.cpp │ │ ├── utils.hpp │ │ ├── world.cpp │ │ └── world.hpp │ ├── nbt/ │ │ ├── CMakeLists.txt │ │ ├── nbt.cpp │ │ ├── nbt.hpp │ │ ├── nbt_inspect.cpp │ │ └── types.hpp │ ├── nullstream.cpp │ ├── nullstream.hpp │ ├── players.cpp │ ├── players.hpp │ ├── settings_t.cpp │ ├── settings_t.hpp │ ├── text.cpp │ ├── text.hpp │ ├── threads/ │ │ ├── renderer.hpp │ │ ├── renderer_settings.hpp │ │ ├── threadworker.hpp │ │ ├── threadworker_fake.hpp │ │ └── threadworker_impl.hpp │ ├── warps.cpp │ ├── warps.hpp │ └── win32/ │ └── tss_cleanup_implemented.cpp └── test/ ├── CMakeLists.txt └── test.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *~ *.pyc build *.cmake *.a Makefile CMakeFiles *.orig *.rej src/config.hpp ================================================ FILE: .gitmodules ================================================ [submodule "libs/unc"] path = libs/unc url = git://github.com/udoprog/unc.git branch = stable ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 2.6) project(c10t) set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}") # attempt to find out which revision we are building from execute_process(COMMAND git rev-list HEAD -n1 --abbrev-commit OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE C10T_REVISION) OPTION(BUILD_DEV_LIBS "Attempt to build libraries under ./libs" ON) set(C10T_VERSION "git ${C10T_REVISION}") set(C10T_SITE "http://github.com/udoprog/c10t") set(C10T_CONTACT "Udoprog et. al (see README)") set(C10T_COMMENT "Created using c10t (http://github.com/udoprog/c10t)") configure_file(${CMAKE_SOURCE_DIR}/src/config.hpp.cmake ${CMAKE_BINARY_DIR}/src/config.hpp) include_directories(${CMAKE_BINARY_DIR}/src) # output the binary to the build directory instead of in src/ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) OPTION(Boost_USE_STATIC_LIBS "Use static boost libs" TRUE) find_package(ZLIB REQUIRED) find_package(PNG 1.2 REQUIRED) find_package(Freetype 2 REQUIRED) find_package(Threads REQUIRED) find_package(Boost 1.46 COMPONENTS thread filesystem system REQUIRED) include_directories(${CMAKE_SOURCE_DIR}/src) include_directories(${ZLIB_INCLUDE_DIR}) include_directories(${PNG_INCLUDE_DIR}) include_directories(${FREETYPE_INCLUDE_DIR_freetype2}) include_directories(${FREETYPE_INCLUDE_DIR_ft2build}) include_directories(${Boost_INCLUDE_DIR}) set(c10t_LIBRARIES dl # internal libraries that could/should be externalized c10t-image c10t-nbt c10t-mc c10t-engine # external libraries ${ZLIB_LIBRARIES} ${PNG_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${FREETYPE_LIBRARY} ) if (BUILD_DEV_LIBS) include_directories("${CMAKE_SOURCE_DIR}/libs/unc/src") add_subdirectory(libs/unc) set(c10t_LIBRARIES ${c10t_LIBRARIES} unc uncdata ) else (BUILD_DEV_LIBS) find_package(PkgConfig) pkg_check_modules(UNC unc) set(c10t_LIBRARIES ${c10t_LIBRARIES} ${UNC_LIBRARIES}) endif (BUILD_DEV_LIBS) add_subdirectory(src/image) add_subdirectory(src/engine) add_subdirectory(src/nbt) add_subdirectory(src/mc) add_subdirectory(test) set(c10t_SOURCES src/main.cpp src/dlopen.cpp src/main_utils.cpp src/algorithm.cpp src/players.cpp src/fileutils.cpp src/dirlist.cpp src/altitude_graph.cpp src/warps.cpp src/text.cpp src/json.cpp src/generate_map.cpp src/generate_statistics.cpp src/marker.cpp src/nullstream.cpp src/settings_t.cpp ) add_executable(c10t ${c10t_SOURCES}) add_executable(c10t-debug EXCLUDE_FROM_ALL ${c10t_SOURCES}) add_executable(nbt-inspect EXCLUDE_FROM_ALL src/nbt/nbt_inspect.cpp src/nbt/nbt.cpp) add_executable(region-inspect EXCLUDE_FROM_ALL src/mc/region_inspect.cpp) set_target_properties(c10t PROPERTIES COMPILE_FLAGS "-O3 -Wall -pedantic") set_target_properties(c10t-debug PROPERTIES COMPILE_FLAGS "-Wall -pedantic -g") set_target_properties(nbt-inspect PROPERTIES COMPILE_FLAGS "-O3 -Wall -pedantic") set_target_properties(region-inspect PROPERTIES COMPILE_FLAGS "-O3 -Wall -pedantic") target_link_libraries(c10t ${c10t_LIBRARIES}) target_link_libraries(c10t-debug ${c10t_LIBRARIES}) target_link_libraries(nbt-inspect ${ZLIB_LIBRARIES}) target_link_libraries(region-inspect ${c10t_LIBRARIES}) ================================================ FILE: ChangeLog ================================================ Wed Nov 17 21:23:38 CET 2010 - By popular demand, including the google-api scripts by the following individuals from minecraftforum: Rendrik - php Kochu - powershell Wed Nov 3 15:16:21 CET 2010 - Show warp positions, by phlip at minecraftforum ================================================ FILE: LICENSE.txt ================================================ // BSD license. Copyright (c) 2010, John-John Tedro et al. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the minecraft community nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ c10t - a cartography tool for Minecraft ======================================= I wrote c10t entirely because i liked the tool Cartograph by ZomBuster, but I wasn't too fond of a couple of aspects about how rendering was performed. Requirements ------------ * libz (?) * libpng (>= 1.2) * libfreetype (>= 2) * libboost (thread, filesystem, system and test) (>= 1.46) Features -------- * Pipelined rendering process (using image compositioning) which allows for multithreaded rendering. * Very memory friendly, you can specify a memory limit and it will switch caching to file. * Uses proper command line options. * Clean code, for easing further development. * Multiplatform - yes, it does compile properly on windows and mac. * A gui wrapper, see: http://github.com/udoprog/c10t-swt (native gui with java bindings) I would not have done this were it not for the excellent inspiration by ZomBuster and Firemark (the linux port of cartograph). Thank you for the inspiration. Building from Source (using cmake) ---------------------------------- ### Ubuntu ### 1. Install dependencies: $ sudo apt-get install build-essential cmake libpng-dev zlib1g-dev libboost-all-dev libfreetype6-dev If libboost >= 1.46 is not available by using your package manager, you need to install it from source. To do so download and untar it from www.boost.org and perform the following operations as root: $ ./bootstrap --prefix=/usr $ ./b2 install If you have troubles installing boost, consult the Getting Started Guide on the website of the boost library. 2. Clone repository $ git clone git://github.com/udoprog/c10t.git $ cd c10t You need to clone the repository in order to successfully execute the commands in step three. You can get support for git on the following web page: http://schacon.github.com/git/gittutorial.html Just scroll to the section "Using git for collaboration". 3. Update submodules which contains some required dependencies (./libs): $ git submodule init $ git submodule update 4. Run: $ mkdir build $ cd build $ cmake .. $ make c10t * CMake should generate a file called src/config.h from the input file src/config.h.cmake * There are several targets you can make. c10t, c10t-lib, c10t-debug, and c10t-test. * If you wish to build all of these simply run `make` which defaults to target `all`. * If you wish to build any individual one run `make ` e.g. `make c10t-test`. 4. The executable `c10t` should be in the current directory. There are a couple of available targets * ___c10t-debug___ - debug build with symbols, nice for debugging * ___c10t-lib___ - library that contains all c10t functions not in main or nbt_inspect * ___nbt-inspect___ - dumps the content of an nbt data file (basically anything in the world directory). Useful for writing tools. * ___region-inspect___ - dumps the content of a mcr region file. Issues ------ * Issues should be posted on http://github.com/udoprog/c10t/issues * Run the program in debug mode (c10t --debug), this will print useful information, but be much slower. * ALWAYS include `c10t --version` information when posting issues, that way it can be determined weither the issue already has been resolved or not, if this information is missing, the issue probably cannot be resolved. * ALWAYS include the following information: Platform (e.g. Windows, Linux, Mac) and Architecture (x86, x86_64) * IF POSSIBLE include information which can help us reproduce the problem. Contributors ------------ UniversE - for epic center calculation, and saving the api! Contributions ------------- ZomBuster and Firemark - for their original work Guardian9979 - for his continual nagging for improvements acleone - for his work with cmake [85e980a] j005u - for explaining how cocoa works jnnnnn - for limit options which are helpful when debugging (and quite cool) mudaltsov - for mapping out the Mac OS X building process and creating a splendid package! frozencow - for fixing boost_thread configuration for cmake reportingjsr - for pointing out build issues Dim-Dul - for providing a nice map to render vostok4 - for implementing google API support rmmh - for figuring out png compression and encouraging people to build properly. Athemis - for fixing beta 1.2 compatibility. sn4kebite - for implementing Wool colors. ekryyn - for implementing the altitude graph! ================================================ FILE: dist/Makefile.osx ================================================ ARCH=intel OS=osx VERSION=SNAPSHOT USR=/opt/local CXXFLAGS=-arch i386 -arch x86_64 -Isrc -I/opt/local/include LDFLAGS=-pthread LDFLAGS+="/opt/local/lib/libpng.a" LDFLAGS+="/opt/local/lib/libz.a" LDFLAGS+="/opt/local/lib/libboost_thread-mt.a" LDFLAGS+="/opt/local/lib/libboost_system-mt.a" LDFLAGS+="/opt/local/lib/libboost_filesystem-mt.a" LDFLAGS+="/opt/local/lib/libfreetype.a" DIST=c10t BIN=c10t TARGET_DEBUG=c10t-debug include config.mk local-package: cp LICENSE.txt ${PACKAGE}/LICENSE.txt cp README.md ${PACKAGE}/README cp -R scripts/google-api/ ${PACKAGE}/ cp -R res/libc10t.js ${PACKAGE}/ cp -R res/example.html ${PACKAGE}/ zip -r ${PACKAGE}-${OS}-${ARCH}.zip ${PACKAGE} mv ${PACKAGE}-${OS}-${ARCH}.zip ${BUILD}/${PACKAGE}-${OS}-${ARCH}.zip sha1sum ${BUILD}/${PACKAGE}-${OS}-${ARCH}.zip > ${BUILD}/${PACKAGE}-${OS}-${ARCH}.zip.sha1 ================================================ FILE: dist/README.md ================================================ This little setup came out of me wanting to setup my server to build SNAPSHOTS about once very day. These scripts generate static binaries, and they require you to have all required libraries pre-installed. This is not meant to be used by a broad set of users, therefore it is very specialized to my system setup, but still might be useful if you want to setup something similar yourself. This is what I do for building static binary for windows: i686-mingw32-g++ -DBOOST_THREAD_USE_LIB -I./contrib/include \ -static-libgcc $(find src -name "*.cpp") \ ./contrib/boost_thread/src/win32/thread.cpp \ ./contrib/boost_thread/src/win32/tss_pe.cpp \ ./contrib/tss_dummy.cpp \ -static -lpng14 -lz -lpthreadGC2 \ -o c10t.exe Now, the only thing I havent gotten down yet is mac. I have no experience and no means to test it, so if someone would like to inform me on how it's done, I'm all ears. If you want to run these manually, you have to be in the root directory, and issue the following command: make -f dist/Makefile. clean all Please not that the 'clean' is basically compulsary, since it will probably otherwise break when doing multiple targets due to output format. This also assumes that you've already generated the src/config.h, you do this by typing: cmake . My cross development setup is base of gentoo crossdev, this allows me to have multiple comilers and library setups without conflicts: http://en.gentoo-wiki.com/wiki/Crossdev Good luck, this is me sharing my experiences -- Udoprog ================================================ FILE: dist/all.sh ================================================ #!/bin/sh for dist in $(ls -1 dist/targets); do if ! dist/make.sh $dist "$@"; then echo "TARGET FAILED: $dist" exit 1 fi done ================================================ FILE: dist/config.mk ================================================ USR?=/usr/${TARGET}/usr LIB?=${USR}/lib SOURCES+=src/algorithm.cpp SOURCES+=src/altitude_graph.cpp SOURCES+=src/dirlist.cpp SOURCES+=src/dlopen.cpp SOURCES+=src/engine/block_rotation.cpp SOURCES+=src/engine/fatiso_engine.cpp SOURCES+=src/engine/flat_base.cpp SOURCES+=src/engine/functions.cpp SOURCES+=src/engine/isometric_base.cpp SOURCES+=src/engine/isometric_engine.cpp SOURCES+=src/engine/obliqueangle_engine.cpp SOURCES+=src/engine/oblique_engine.cpp SOURCES+=src/engine/topdown_engine.cpp SOURCES+=src/fileutils.cpp SOURCES+=src/generate_map.cpp SOURCES+=src/generate_statistics.cpp SOURCES+=src/image/algorithms.cpp SOURCES+=src/image/cached_image.cpp SOURCES+=src/image/color.cpp SOURCES+=src/image/image_base.cpp SOURCES+=src/image/image_operations.cpp SOURCES+=src/image/memory_image.cpp SOURCES+=src/json.cpp SOURCES+=src/main.cpp SOURCES+=src/main_utils.cpp SOURCES+=src/marker.cpp SOURCES+=src/mc/blocks.cpp SOURCES+=src/mc/dynamic_buffer.cpp SOURCES+=src/mc/level.cpp SOURCES+=src/mc/level_info.cpp SOURCES+=src/mc/marker.cpp SOURCES+=src/mc/region.cpp SOURCES+=src/mc/region_inspect.cpp SOURCES+=src/mc/region_iterator.cpp SOURCES+=src/mc/rotated_level_info.cpp SOURCES+=src/mc/utils.cpp SOURCES+=src/mc/world.cpp SOURCES+=src/nbt/nbt.cpp SOURCES+=src/nbt/nbt_inspect.cpp SOURCES+=src/nullstream.cpp SOURCES+=src/players.cpp SOURCES+=src/settings_t.cpp SOURCES+=src/text.cpp SOURCES+=src/warps.cpp SOURCES+=src/win32/tss_cleanup_implemented.cpp LDFLAGS+=${LIB}/libpng.a LDFLAGS+=${LIB}/libboost_thread.a LDFLAGS+=${LIB}/libboost_system.a LDFLAGS+=${LIB}/libboost_filesystem.a LDFLAGS+=${LIB}/libfreetype.a LDFLAGS+=${LIB}/libz.a LDFLAGS+=${LIB}/libbz2.a LDFLAGS+=${LIB}/libdl.a LDFLAGS+=${LIB}/libunc.a LDFLAGS+=${LIB}/libuncdata.a OBJECTS=${SOURCES:.cpp=.o} CXXFLAGS+=-Isrc -I${USR}/include/freetype2 -Wall -fomit-frame-pointer -O2 CXX=${TARGET}-g++ STRIP=${TARGET}-strip VERSION?=SNAPSHOT PACKAGE=c10t-${VERSION}-${OS}-${ARCH} BUILD=./build all: package .SUFFIXES: .cpp .o .cpp.o: ${CXX} ${CXXFLAGS} -c $< -o $@ ${BIN}: ${OBJECTS} ${CXX} ${CXXFLAGS} ${OBJECTS} ${LDFLAGS} -o ${BIN} ${STRIP} ${BIN} clean: ${RM} ${OBJECTS} ${RM} -rf ${PACKAGE} pre-package: ${BIN} echo "pre-package: ${PACKAGE}" mkdir -p ${PACKAGE} cp ${BIN} ${PACKAGE}/${BIN} rm -rf ${BUILD} mkdir -p ${BUILD} post-package: echo "post-package: ${PACKAGE}" ${RM} -rf ${PACKAGE} package: pre-package local-package post-package %.sha1: sha1sum $* > $*.sha1 cp $@ ${BUILD}/$@ %.tar.gz: tar -cvf $*.tar $* gzip -f $*.tar cp $@ ${BUILD}/$@ %.zip: zip -r $*.zip $* cp $@ ${BUILD}/$@ ================================================ FILE: dist/make.sh ================================================ #!/bin/bash target=$1 shift dist_target=dist/targets/$target build_target=build/$target dist_config=dist/config.mk dist_src=src dist_scripts=scripts dist_res=res exit_usage() { echo "Usage: dist/dist.sh [make-opts]" echo " is one of:" ls -1 dist/targets exit 1 } do_cp() { if test $1 -nt $2; then printf "cp %-37s -> %s\n" "$1" "$2" cp $1 $2 fi return 0 } do_sync() { src=$1 dir=$2 shift 2 echo "Syncing $src to $dir/$src \"$@\"" while read file; do target="$dir/$file" mkdir -p $(dirname "$target") if test $file -nt $target; then printf "%-40s -> %s\n" "$file" "$target" cp -p "$file" "$target" fi done < <(find $src -type f "$@") } [[ ! -d dist/targets ]] && echo "Not in build directory" && exit 1 [[ -z $target ]] && exit_usage [[ ! -f $dist_target ]] && exit_usage if ! cmake .; then echo "cmake: failed" exit 1 fi mkdir -p $build_target do_cp $dist_target $build_target/Makefile do_cp $dist_config $build_target/config.mk do_cp LICENSE.txt $build_target/LICENSE.txt do_cp README.md $build_target/README.md do_sync $dist_src $build_target -name "*.cpp" do_sync $dist_src $build_target -name "*.hpp" do_sync $dist_scripts $build_target do_sync $dist_res $build_target echo "cd $build_target && make $@" cd $build_target && make "$@" exit $? ================================================ FILE: dist/targets/x86-linux ================================================ ARCH=x86 OS=linux TARGET=i686-linux-gnu LDFLAGS=-pthread -static -static-libgcc BIN=c10t include config.mk local-package: cp LICENSE.txt ${PACKAGE}/LICENSE.txt cp README.md ${PACKAGE}/README cp -R scripts/google-api ${PACKAGE}/ cp -R res/libc10t.js ${PACKAGE}/ cp -R res/example.html ${PACKAGE}/ make ${PACKAGE}.tar.gz make ${PACKAGE}.tar.gz.sha1 ================================================ FILE: dist/targets/x86-windows ================================================ ARCH=x86 OS=windows TARGET=i686-mingw32 BIN=c10t.exe CXXFLAGS=-DBOOST_THREAD_USE_LIB=1 LDFLAGS=-static -static-libgcc include config.mk local-package: cp LICENSE.txt ${PACKAGE}/LICENSE.txt cp README.md ${PACKAGE}/README.txt cp -R scripts/google-api ${PACKAGE}/ cp -R res/libc10t.js ${PACKAGE}/ cp -R res/example.html ${PACKAGE}/ make ${PACKAGE}.zip make ${PACKAGE}.zip.sha1 ================================================ FILE: dist/targets/x86_64-linux ================================================ ARCH=x86_64 OS=linux LDFLAGS=-pthread -static -static-libgcc BIN=c10t include config.mk TARGET=x86_64-linux-gnu USR=/usr local-package: cp LICENSE.txt ${PACKAGE}/LICENSE.txt cp README.md ${PACKAGE}/README cp -R scripts/google-api ${PACKAGE}/ cp -R res/libc10t.js ${PACKAGE}/ cp -R res/example.html ${PACKAGE}/ make ${PACKAGE}.tar.gz make ${PACKAGE}.tar.gz.sha1 ================================================ FILE: dist/targets/x86_64-windows ================================================ ARCH=x86_64 OS=windows TARGET=x86_64-w64-mingw32 BIN=c10t.exe CXXFLAGS=-m64 -O3 -DBOOST_THREAD_USE_LIB=1 -DBOOST_USE_WINDOWS_H LDFLAGS=-static -static-libgcc SOURCES=src/win32/tss_cleanup_implemented.cpp include config.mk local-package: cp LICENSE.txt ${PACKAGE}/LICENSE.txt cp README.md ${PACKAGE}/README.txt cp -R scripts/google-api ${PACKAGE}/ cp -R res/libc10t.js ${PACKAGE}/ cp -R res/example.html ${PACKAGE}/ make ${PACKAGE}.zip make ${PACKAGE}.zip.sha1 ================================================ FILE: docs/NBT.txt ================================================ Named Binary Tag specification NBT (Named Binary Tag) is a tag based binary format designed to carry large amounts of binary data with smaller amounts of additional data. An NBT file consists of a single GZIPped Named Tag of type TAG_Compound. A Named Tag has the following format: byte tagType TAG_String name [payload] The tagType is a single byte defining the contents of the payload of the tag. The name is a descriptive name, and can be anything (eg "cat", "banana", "Hello World!"). It has nothing to do with the tagType. The purpose for this name is to name tags so parsing is easier and can be made to only look for certain recognized tag names. Exception: If tagType is TAG_End, the name is skipped and assumed to be "". The [payload] varies by tagType. Note that ONLY Named Tags carry the name and tagType data. Explicitly identified Tags (such as TAG_String above) only contains the payload. The tag types and respective payloads are: TYPE: 0 NAME: TAG_End Payload: None. Note: This tag is used to mark the end of a list. Cannot be named! If type 0 appears where a Named Tag is expected, the name is assumed to be "". (In other words, this Tag is always just a single 0 byte when named, and nothing in all other cases) TYPE: 1 NAME: TAG_Byte Payload: A single signed byte (8 bits) TYPE: 2 NAME: TAG_Short Payload: A signed short (16 bits, big endian) TYPE: 3 NAME: TAG_Int Payload: A signed short (32 bits, big endian) TYPE: 4 NAME: TAG_Long Payload: A signed long (64 bits, big endian) TYPE: 5 NAME: TAG_Float Payload: A floating point value (32 bits, big endian, IEEE 754-2008, binary32) TYPE: 6 NAME: TAG_Double Payload: A floating point value (64 bits, big endian, IEEE 754-2008, binary64) TYPE: 7 NAME: TAG_Byte_Array Payload: TAG_Int length An array of bytes of unspecified format. The length of this array is bytes TYPE: 8 NAME: TAG_String Payload: TAG_Short length An array of bytes defining a string in UTF-8 format. The length of this array is bytes TYPE: 9 NAME: TAG_List Payload: TAG_Byte tagId TAG_Int length A sequential list of Tags (not Named Tags), of type . The length of this array is Tags Notes: All tags share the same type. TYPE: 10 NAME: TAG_Compound Payload: A sequential list of Named Tags. This array keeps going until a TAG_End is found. TAG_End end Notes: If there's a nested TAG_Compound within this tag, that one will also have a TAG_End, so simply reading until the next TAG_End will not work. The names of the named tags have to be unique within each TAG_Compound The order of the tags is not guaranteed. Decoding example: (Use http://www.minecraft.net/docs/test.nbt to test your implementation) First we start by reading a Named Tag. After unzipping the stream, the first byte is a 10. That means the tag is a TAG_Compound (as expected by the specification). The next two bytes are 0 and 11, meaning the name string consists of 11 UTF-8 characters. In this case, they happen to be "hello world". That means our root tag is named "hello world". We can now move on to the payload. From the specification, we see that TAG_Compound consists of a series of Named Tags, so we read another byte to find the tagType. It happens to be an 8. The name is 4 letters long, and happens to be "name". Type 8 is TAG_String, meaning we read another two bytes to get the length, then read that many bytes to get the contents. In this case, it's "Bananrama". So now we know the TAG_Compound contains a TAG_String named "name" with the content "Bananrama" We move on to reading the next Named Tag, and get a 0. This is TAG_End, which always has an implied name of "". That means that the list of entries in the TAG_Compound is over, and indeed all of the NBT file. So we ended up with this: TAG_Compound("hello world"): 1 entries { TAG_String("name"): Bananrama } For a slightly longer test, download http://www.minecraft.net/docs/bigtest.nbt You should end up with this: TAG_Compound("Level"): 11 entries { TAG_Short("shortTest"): 32767 TAG_Long("longTest"): 9223372036854775807 TAG_Float("floatTest"): 0.49823147 TAG_String("stringTest"): HELLO WORLD THIS IS A TEST STRING ! TAG_Int("intTest"): 2147483647 TAG_Compound("nested compound test"): 2 entries { TAG_Compound("ham"): 2 entries { TAG_String("name"): Hampus TAG_Float("value"): 0.75 } TAG_Compound("egg"): 2 entries { TAG_String("name"): Eggbert TAG_Float("value"): 0.5 } } TAG_List("listTest (long)"): 5 entries of type TAG_Long { TAG_Long: 11 TAG_Long: 12 TAG_Long: 13 TAG_Long: 14 TAG_Long: 15 } TAG_Byte("byteTest"): 127 TAG_List("listTest (compound)"): 2 entries of type TAG_Compound { TAG_Compound: 2 entries { TAG_String("name"): Compound tag #0 TAG_Long("created-on"): 1264099775885 } TAG_Compound: 2 entries { TAG_String("name"): Compound tag #1 TAG_Long("created-on"): 1264099775885 } } TAG_Byte_Array("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"): [1000 bytes] TAG_Double("doubleTest"): 0.4931287132182315 } ================================================ FILE: gui/c10t-tk/README ================================================ This is a simple GUI using Python and Tkinter. I know Tkinter is ugly and it sucks... But it is available on the standard Python library, so this GUI works out-of-the-box on Linux and Windows and Mac (but I don't have a Mac to test). The image previewing feature requires PIL (Python Imaging Library). If it is not available, the GUI will still work, the image will be saved to disk, but it won't be displayed. How to run: * cd to the project root dir (the one which will contain the c10t executable) * run ./gui/c10t-tk/c10t-tk.py Notice: in fact, you can run the GUI from whatever directory you want, but the suggestion above will make everything work by default. This is what it does: * Graphical interface to select the most common c10t options * Runs c10t with such options * Shows the final command-line, so it's easy to copy-paste that onto scripts * Previews the rendered image, with zoom What it still doesn't do: * It don't show any output from c10t * There is no error checking when c10t runs * There is no progress bar (the program just hangs while c10t is running) * The program also hangs briefly while zooming in/out the image Possible improvements: * Some kind of interface to select blocks * Some kind of interface to select files/directories * Auto-detect the correct minecraft save path on other systems * Use a checkbutton for threads (when off, use the built-in auto-detection) instead of the quick-and-dirty hack of selecting zero threads * Use radiobuttons (intead of checkbuttons) for selecting the rendering mode (group by (normal, oblique, obliqueangle, isometric) (normal, night, heightmap) ) * Add support for --no-alpha and --striped-terrain * Improve the packaging? ================================================ FILE: gui/c10t-tk/c10t-tk.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4 sw=4 et import re import os.path import subprocess from mainwindow import MainWindow def default_c10t_executable(): return "./c10t" def default_minecraft_world(): return "~/.minecraft/saves/World1" def default_output_image(): return os.path.abspath("out.png") def quote_arg_if_needed(arg): """Add quotes if the argument has 'weird' characters. This function is extremely simple, and it is not fool-proof. Improvements are quite welcome! WARNING: single-quotes inside the argument will break this!""" if re.search(r'''[^-a-zA-Z0-9_.,/+=]''', arg): return "'%s'" % (arg,) else: return arg def args_to_string(args): """Converts a list of arguments to one string that can be copy-pasted into a terminal and will work (hopefully).""" return " ".join(quote_arg_if_needed(arg) for arg in args) class Program(object): def __init__(self): self.win = MainWindow() self.args = [] # Files self.win.ui.exepath = default_c10t_executable() self.win.ui.world = default_minecraft_world() self.win.ui.output = default_output_image() self.update_ui_commandline() self.win.update_button_callback = self.update_ui_commandline self.win.run_button_callback = self.run_command self.win.load_button_callback = self.load_image def run_command(self): self.update_ui_commandline() proc = subprocess.Popen(self.args, shell=False) # TODO: Add a progress window/progress bar # Meanwhile... let's just block this program until c10t finishes... # Ugly, but better than nothing. proc.communicate() # TODO: Check process returncode self.load_image() def load_image(self): self.win.load_image_from_file(os.path.expanduser(self.win.ui.output)) def update_ui_commandline(self): self.build_commandline() self.win.ui.command = args_to_string(self.args) def build_commandline(self): ui = self.win.ui args = [os.path.expanduser(ui.exepath)] # Filtering if ui.topcheck : args.extend(["--top" , str(ui.top )]) if ui.bottomcheck: args.extend(["--bottom", str(ui.bottom)]) if ui.limitscheck: args.extend([ "--limits", ",".join(str(x) for x in ( ui.limitsnorth, ui.limitssouth, ui.limitseast, ui.limitswest, )) ]) if ui.cavemodecheck: args.append("--cave-mode") if ui.excludecheck: for block in re.split("[ \t,;/]+", ui.exclude): args.extend(["-e", str(block)]) if ui.includecheck: args.append("--hide-all") for block in re.split("[ \t,;/]+", ui.include): args.extend(["-i", str(block)]) # Rendering if ui.obliquecheck : args.append("--oblique") if ui.obliqueanglecheck: args.append("--oblique-angle") if ui.isometriccheck : args.append("--isometric") if ui.nightcheck : args.append("--night") if ui.heightmapcheck : args.append("--heightmap") if ui.rotate : args.extend(["-r", str(ui.rotate)]) if int(ui.threads) != 0: args.extend(["--threads", str(ui.threads)]) # Text and fonts args.extend(["--ttf-size" , str(ui.ttfsize)]) args.extend(["--ttf-color", str(ui.ttfcolor)]) if ui.showplayerscheck: args.append("--show-players") if ui.showsignscheck : args.append("--show-signs") if ui.showcoordscheck : args.append("--show-coordinates") if ui.playercolorcheck: args.extend(["--player-color", str(ui.playercolor)]) if ui.signcolorcheck : args.extend(["--sign-color", str(ui.signcolor)]) if ui.coordcolorcheck : args.extend(["--coordinate-color", str(ui.coordcolor)]) # Adding the "Files" section to the end for readability reasons args.extend([ "-w", os.path.expanduser(ui.world), "-o", os.path.expanduser(ui.output), ]) self.args = args def main(self): self.win.mainloop() if __name__ == "__main__": p = Program() p.main() ================================================ FILE: gui/c10t-tk/mainwindow.py ================================================ # -*- coding: utf-8 -*- # vi:ts=4 sw=4 et # Let me warn you... Tkinter sucks! # It's so... crude... and ugly... # But the *only* reason I'm using it is that # it is available on the standard Python library. # # Also, be aware that most of this code is ugly because all it does is # setting up the widgets at the window. from Tkinter import * from tooltip import ToolTip try: import PIL.Image import PIL.ImageTk PIL_NOT_AVAILABLE = False except ImportError as e: PIL_NOT_AVAILABLE = True PIL_NOT_AVAILABLE_MESSAGE = repr(e) def add_tooltip(text, widgets): """Adds a tooltip to multiple elements at once. Useful for setting the same tooltip for the label/checkbox and the associated entry/spinbox. The tooltip widget is also stored at the "tooltip" attribute.""" for w in widgets: w.tooltip = ToolTip(w, text=text) def cross_platform_mouse_wheel(event): """Abstracts all Windows/Linux/Mac differences when handling the mouse wheel. Receives an 'event' object, and returns: negative value ==> scrolling down positive value ==> scrolling up zero ==> something went wrong... This function should be called from within "", "" and "" event handlers. """ # http://infohost.nmt.edu/tcc/help/pubs/tkinter/events.html#event-handlers # Linux maps scrolling to mouse buttons 4 and 5 if event.num == 4: # scroll up return 1 elif event.num == 5: # scroll down return -1 # Windows and MacOs have a MouseWheel event elif event.delta: # In Windows, delta is a multiple of 120 if abs(event.delta) >= 120: return event.delta // 120 # In MacOS, delta is a multiples of 1 else: return event.delta return 0 class XCheckbutton(Checkbutton): """Tkinter requires a Tk variable for Checkbutton. This class automatically creates such variable (as IntVar), stores it at the widget's "var" attribute, and adds get/set methods to access that var. """ def __init__(self, *args, **kwargs): self.var = IntVar() Checkbutton.__init__(self, variable=self.var, *args, **kwargs) def get(self): return self.var.get() def set(self, value): self.var.set(value) class XEntry(Entry): """For some reason, Entry supplies .get() but doesn't have a .set() method. This class adds such simple and useful method.""" def set(self, value): # When the state is disabled or readonly, then .delete() and # .insert() methods do nothing. So, I change the state to NORMAL # before calling them, and restore the state afterwards. state = self["state"] self["state"] = NORMAL self.delete(0, END) self.insert(0, value) self["state"] = state class XSpinbox(Spinbox): """As the Entry widget, Tkinter doesn't provide a .set() method to Spinbox. This class adds that method. In addition, it adds support for mouse-wheel.""" def __init__(self, *args, **kwargs): Spinbox.__init__(self, *args, **kwargs) self.bind("", self.mouse_wheel_handler) self.bind("", self.mouse_wheel_handler) self.bind("", self.mouse_wheel_handler) def set(self, value): # Adding a simple ".set()" method to spinboxes... # http://stackoverflow.com/questions/3019800/tkinter-spinbox-widget/3024931#3024931 # So simple solution... WHY doesn't Tkinter have it already?! self.delete(0, END) self.insert(0, value) def mouse_wheel_handler(self, event): # Mouse wheel in spinboxes... # http://www.daniweb.com/forums/post1158775.html#post1158775 dir = cross_platform_mouse_wheel(event) while dir > 0: self.invoke("buttonup") dir -= 1 while dir < 0: self.invoke("buttondown") dir += 1 class FilesFrame(LabelFrame): def __init__(self, master=None): LabelFrame.__init__(self, master, text = u"Files", padx = 5, pady = 5 ) self.exepath_label = Label(self, text=u"c10t path", anchor=W, justify=LEFT) self.exepath_label.grid(column=0, row=0, columnspan=2, sticky=EW) self.exepath_entry = XEntry(self, name="exepath") self.exepath_entry.grid(column=2, row=0, sticky=EW) add_tooltip(u"Path to the c10t executable", ( self.exepath_label, self.exepath_entry, )) self.world_label = Label(self, text=u"Input world", anchor=W, justify=LEFT) self.world_label.grid(column=0, row=1, columnspan=2, sticky=EW) self.world_entry = XEntry(self, name="world") self.world_entry.grid(column=2, row=1, sticky=EW) add_tooltip(u"Path to the World directory (the one that contains level.dat)", ( self.world_label, self.world_entry, )) self.output_label = Label(self, text=u"Output image", anchor=W, justify=LEFT) self.output_label.grid(column=0, row=2, sticky=EW) self.load_button = Button(self, padx=0, pady=0, text=u"Load") self.load_button.grid(column=1, row=2) self.output_entry = XEntry(self, name="output") self.output_entry.grid(column=2, row=2, sticky=EW) add_tooltip(u"Destination file for the generated PNG", ( self.output_label, self.output_entry, )) add_tooltip(u"(re)Loads this image.", ( self.load_button, )) # Setting columns and rows to auto-expand for i in (0,2): self.columnconfigure(i, weight=1) for i in xrange(3): self.rowconfigure(i, weight=1) class FilteringFrame(LabelFrame): def __init__(self, master=None): LabelFrame.__init__(self, master, text = u"Filtering", padx = 5, pady = 5 ) self.top_checkbutton = XCheckbutton(self, name="topcheck", text=u"Top", anchor=W, justify=LEFT) self.top_checkbutton.grid(column=0, row=0, sticky=EW) self.top_spinbox = XSpinbox(self, name="top", from_=0, to=127, width=3) self.top_spinbox.set(127) self.top_spinbox.grid(column=1, row=0, sticky=EW) add_tooltip(u"Splice from the top, must be <= 127", ( self.top_checkbutton, self.top_spinbox, )) self.bottom_checkbutton = XCheckbutton(self, name="bottomcheck", text=u"Bottom", anchor=W, justify=LEFT) self.bottom_checkbutton.grid(column=0, row=1, sticky=EW) self.bottom_spinbox = XSpinbox(self, name="bottom", from_=0, to=127, width=3) self.bottom_spinbox.set(0) self.bottom_spinbox.grid(column=1, row=1, sticky=EW) add_tooltip(u"Splice from the bottom, must be >= 0", ( self.bottom_checkbutton, self.bottom_spinbox, )) # Begin "Limits" row... self.limits_checkbutton = XCheckbutton(self, name="limitscheck", text=u"Limits", anchor=W, justify=LEFT) self.limits_checkbutton.grid(column=0, row=2, sticky=EW) add_tooltip(u"Limit render to certain area (North, South, East, West)", ( self.limits_checkbutton, )) self.limits_frame = Frame(self) self.limits_frame.grid(column=1, row=2, sticky=EW) # This loop sets the following vars: # self.limitsnorth_spinbox # self.limitssouth_spinbox # self.limitseast_spinbox # self.limitswest_spinbox for index, dir in enumerate(("north", "south", "east", "west")): name="limits"+dir widget = XSpinbox(self.limits_frame, name=name, from_=-999, to=999, width=4) widget.set(0) widget.grid(column=index, row=0, sticky=EW) self.limits_frame.columnconfigure(index, weight=1) setattr(self, name+"_spinbox", widget) signal = u"negative" if index % 2 == 0 else u"positive" tooltip = u"%s limit (%s)" % (dir.capitalize(), signal) add_tooltip(tooltip, (widget,)) # End "Limits" row... self.cavemode_checkbutton = XCheckbutton(self, name="cavemodecheck", text=u"Cave mode", anchor=W, justify=LEFT) self.cavemode_checkbutton.grid(column=0, row=3, sticky=EW) add_tooltip(u"Cave mode - top down until solid block found, then render bottom outlines only", ( self.cavemode_checkbutton, )) self.exclude_checkbutton = XCheckbutton(self, name="excludecheck", text=u"Exclude", anchor=W, justify=LEFT) self.exclude_checkbutton.grid(column=0, row=4, sticky=EW) self.exclude_entry = XEntry(self, name="exclude") self.exclude_entry.grid(column=1, row=4, sticky=EW) add_tooltip(u"Exclude block-ids from render", ( self.exclude_checkbutton, self.exclude_entry, )) self.include_checkbutton = XCheckbutton(self, name="includecheck", text=u"Include", anchor=W, justify=LEFT) self.include_checkbutton.grid(column=0, row=5, sticky=EW) self.include_entry = XEntry(self, name="include") self.include_entry.grid(column=1, row=5, sticky=EW) add_tooltip(u"Include block-ids in render (and automatically exclude all non-listed blocks)", ( self.include_checkbutton, self.include_entry, )) # Setting columns to auto-expand for i in xrange(2): self.columnconfigure(i, weight=1) for i in xrange(6): self.rowconfigure(i, weight=1) class RenderingFrame(LabelFrame): def __init__(self, master=None): LabelFrame.__init__(self, master, text = u"Rendering", padx = 5, pady = 5 ) self.oblique_checkbutton = XCheckbutton(self, name="obliquecheck", text=u"Oblique", anchor=W, justify=LEFT) self.oblique_checkbutton.grid(column=0, row=0, sticky=EW) add_tooltip(u"Oblique rendering", ( self.oblique_checkbutton, )) self.obliqueangle_checkbutton = XCheckbutton(self, name="obliqueanglecheck", text=u"Oblique angle", anchor=W, justify=LEFT) self.obliqueangle_checkbutton.grid(column=0, row=1, sticky=EW) add_tooltip(u"Oblique angle rendering", ( self.obliqueangle_checkbutton, )) self.isometric_checkbutton = XCheckbutton(self, name="isometriccheck", text=u"Isometric", anchor=W, justify=LEFT) self.isometric_checkbutton.grid(column=0, row=2, sticky=EW) add_tooltip(u"Isometric rendering", ( self.isometric_checkbutton, )) self.night_checkbutton = XCheckbutton(self, name="nightcheck", text=u"Night-time", anchor=W, justify=LEFT) self.night_checkbutton.grid(column=0, row=3, sticky=EW) add_tooltip(u"Night-time rendering", ( self.night_checkbutton, )) self.heightmap_checkbutton = XCheckbutton(self, name="heightmapcheck", text=u"Heightmap", anchor=W, justify=LEFT) self.heightmap_checkbutton.grid(column=0, row=4, sticky=EW) add_tooltip(u"Heightmap rendering", ( self.heightmap_checkbutton, )) self.rotate_label = Label(self, text=u"Rotate", anchor=W, justify=LEFT) self.rotate_label.grid(column=0, row=5, sticky=EW) add_tooltip(u"Rotate the rendering clockwise", ( self.rotate_label, )) self.rotate_frame = Frame(self) self.rotate_frame.grid(column=1, row=5, sticky=EW) self.rotate_var = IntVar(name="rotate") # This loop sets the following vars: # self.rotate0_radiobutton # self.rotate90_radiobutton # self.rotate180_radiobutton # self.rotate270_radiobutton for index, angle in enumerate(("0", "90", "180", "270")): name = "rotate"+angle widget = Radiobutton(self.rotate_frame, text=angle, variable=self.rotate_var, value=int(angle)) widget.var = self.rotate_var widget.grid(column=index, row=0, sticky=EW) self.rotate_frame.columnconfigure(index, weight=1) setattr(self, name+"_radiobutton", widget) self.rotate_var.set(0) self.threads_label = Label(self, text=u"Threads", anchor=W, justify=LEFT) self.threads_label.grid(column=0, row=6, sticky=EW) self.threads_spinbox = XSpinbox(self, name="threads", from_=0, to=999, width=3) self.threads_spinbox.set(0) self.threads_spinbox.grid(column=1, row=6, sticky=EW) add_tooltip(u"Specify the amount of threads to use, for maximum efficency, this should match the amount of cores on your machine. Use zero to auto-detect the number of cores.", ( self.threads_label, self.threads_spinbox, )) # Setting columns to auto-expand for i in xrange(2): self.columnconfigure(i, weight=1) for i in xrange(7): self.rowconfigure(i, weight=1) class FontFrame(LabelFrame): def __init__(self, master=None): LabelFrame.__init__(self, master, text = u"Text and fonts", padx = 5, pady = 5 ) self.ttfsize_label = Label(self, text=u"Font size", anchor=W, justify=LEFT) self.ttfsize_label.grid(column=0, row=0, columnspan=2, sticky=EW) self.ttfsize_spinbox = XSpinbox(self, name="ttfsize", from_=1, to=999, width=3) self.ttfsize_spinbox.set(12) self.ttfsize_spinbox.grid(column=2, row=0, sticky=EW) add_tooltip(u"Use the specified font size when drawing text", ( self.ttfsize_label, self.ttfsize_spinbox, )) self.ttfcolor_label = Label(self, text=u"Default color", anchor=W, justify=LEFT) self.ttfcolor_label.grid(column=0, row=1, columnspan=2, sticky=EW) add_tooltip(u"Use the specified color when drawing text", ( self.ttfcolor_label, )) self.ttfcolor_entry = XEntry(self, name="ttfcolor", width=15) self.ttfcolor_entry.set(u"0,0,0,255") self.ttfcolor_entry.grid(column=2, row=1, sticky=EW) self.showplayers_checkbutton = XCheckbutton(self, name="showplayerscheck", text=u"Show players", anchor=W, justify=LEFT) self.showplayers_checkbutton.grid(column=0, row=2, sticky=EW) add_tooltip(u"Will draw out player position and names from the players database in /players", ( self.showplayers_checkbutton, )) self.playercolor_checkbutton = XCheckbutton(self, name="playercolorcheck", text=u"Color:", anchor=W, justify=LEFT) self.playercolor_checkbutton.grid(column=1, row=2, sticky=EW) add_tooltip(u"Use a custom color for players", ( self.playercolor_checkbutton, )) self.playercolor_entry = XEntry(self, name="playercolor", width=15) self.playercolor_entry.set(u"0,0,0,255") self.playercolor_entry.grid(column=2, row=2, sticky=EW) self.showsigns_checkbutton = XCheckbutton(self, name="showsignscheck", text=u"Show signs", anchor=W, justify=LEFT) self.showsigns_checkbutton.grid(column=0, row=3, sticky=EW) add_tooltip(u"Will draw out signs from all chunks", ( self.showsigns_checkbutton, )) self.signcolor_checkbutton = XCheckbutton(self, name="signcolorcheck", text=u"Color:", anchor=W, justify=LEFT) self.signcolor_checkbutton.grid(column=1, row=3, sticky=EW) add_tooltip(u"Use a custom color for signs", ( self.signcolor_checkbutton, )) self.signcolor_entry = XEntry(self, name="signcolor", width=15) self.signcolor_entry.set(u"0,0,0,255") self.signcolor_entry.grid(column=2, row=3, sticky=EW) self.showcoords_checkbutton = XCheckbutton(self, name="showcoordscheck", text=u"Show coordinates", anchor=W, justify=LEFT) self.showcoords_checkbutton.grid(column=0, row=4, sticky=EW) add_tooltip(u"Will draw out each chunks expected coordinates", ( self.showcoords_checkbutton, )) self.coordcolor_checkbutton = XCheckbutton(self, name="coordcolorcheck", text=u"Color:", anchor=W, justify=LEFT) self.coordcolor_checkbutton.grid(column=1, row=4, sticky=EW) add_tooltip(u"Use a custom color for coordinates", ( self.coordcolor_checkbutton, )) self.coordcolor_entry = XEntry(self, name="coordcolor", width=15) self.coordcolor_entry.set(u"0,0,0,255") self.coordcolor_entry.grid(column=2, row=4, sticky=EW) add_tooltip(u"Red,Green,Blue,Alpha\n0,0,0,255 means black", ( self.playercolor_entry, self.signcolor_entry, self.coordcolor_entry, self.ttfcolor_entry, )) # Setting columns to auto-expand for i in xrange(3): self.columnconfigure(i, weight=1) for i in xrange(5): self.rowconfigure(i, weight=1) class ConfigurationFrame(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.files_frame = FilesFrame(self) self.files_frame.pack(fill=BOTH, expand=1) self.filtering_frame = FilteringFrame(self) self.filtering_frame.pack(fill=BOTH, expand=1) self.rendering_frame = RenderingFrame(self) self.rendering_frame.pack(fill=BOTH, expand=1) self.font_frame = FontFrame(self) self.font_frame.pack(fill=BOTH, expand=1) class RunFrame(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.run_button = Button(self, text=u"Run!") self.run_button.pack(side=LEFT) self.update_button = Button(self, text=u"Update") self.update_button.pack(side=LEFT) add_tooltip(u"Update the command-line, without running the command. Easy for copy-and-paste into scripts.", ( self.update_button, )) self.command_entry = XEntry(self, name="command", state="readonly") self.command_entry.pack(side=LEFT, fill=X, expand=1) add_tooltip(u"The full command-line that will be invoked", ( self.command_entry, )) class ImageFrame(Frame): """This is just a Frame containing one Canvas, two scrollbars and some logic for scrolling the image.""" # This class is inspired by: # http://effbot.org/tkinterbook/photoimage.htm # http://www.swharden.com/blog/2010-03-03-viewing-large-images-with-scrollbars-using-python-tk-and-pil/ def __init__(self, master=None): Frame.__init__(self, master) # Adding the widgets self.vertical_scrollbar = Scrollbar(self, orient=VERTICAL) self.vertical_scrollbar.pack(side=RIGHT, fill=Y) self.horizontal_scrollbar = Scrollbar(self, orient=HORIZONTAL) self.horizontal_scrollbar.pack(side=BOTTOM, fill=X) self.canvas = Canvas(self) self.canvas.pack(side=LEFT, expand=YES, fill=BOTH) # Add an error message if needed if PIL_NOT_AVAILABLE: self.canvas.create_text(10, 10, anchor=NW, text=u"Python Image Library (PIL) could not be loaded.\n\nNo image will be displayed here, but\nthey will still be rendered to the disk.\n\n%s" % (PIL_NOT_AVAILABLE_MESSAGE,)) # Connecting canvas and scrollbars self.horizontal_scrollbar["command"] = self.canvas.xview self.vertical_scrollbar["command"] = self.canvas.yview self.canvas["xscrollcommand"] = self.horizontal_scrollbar.set self.canvas["yscrollcommand"] = self.vertical_scrollbar.set # Adding the event handlers to the canvas self.canvas.bind("", self.button_press_1_handler) self.canvas.bind("", self.button_motion_1_handler) self.canvas.bind("", self.button_release_1_handler) self.canvas.bind("", self.mouse_wheel_handler) self.canvas.bind("", self.mouse_wheel_handler) self.canvas.bind("", self.mouse_wheel_handler) # Adding mouse wheel support to scrollbars self.horizontal_scrollbar.bind("", self.hscroll_mouse_wheel_handler) self.horizontal_scrollbar.bind("", self.hscroll_mouse_wheel_handler) self.horizontal_scrollbar.bind("", self.hscroll_mouse_wheel_handler) self.vertical_scrollbar.bind("", self.vscroll_mouse_wheel_handler) self.vertical_scrollbar.bind("", self.vscroll_mouse_wheel_handler) self.vertical_scrollbar.bind("", self.vscroll_mouse_wheel_handler) # Internal vars for handling the image self.pil_image = None self.tk_resized_images = {} self.canvas_image = None self.zoom = 0 def hscroll_mouse_wheel_handler(self, event): dir = cross_platform_mouse_wheel(event) self.canvas.xview_scroll(-dir, UNITS) def vscroll_mouse_wheel_handler(self, event): dir = cross_platform_mouse_wheel(event) self.canvas.yview_scroll(-dir, UNITS) def button_press_1_handler(self, event): self.canvas.scan_mark(event.x, event.y) self.canvas["cursor"] = "fleur" def button_motion_1_handler(self, event): self.canvas.scan_dragto(event.x, event.y, gain=1) def button_release_1_handler(self, event): self.canvas["cursor"] = "" def mouse_wheel_handler(self, event): dir = cross_platform_mouse_wheel(event) if dir == 0: return # Code for zooming the image (like Google Maps) self.resize_image_to_zoom(delta=dir, center=(event.x, event.y)) def resize_image_to_zoom(self, delta=None, zoom=None, center=None, forcereload=False): """Parameters: delta : How much to increase/decrease the current zoom? zoom : Set zoom to this absolute value. center : Uses these coordinates (x,y) as the zoom center, scrolling the canvas as needed. These are "window coordinates", relative to the widget's top-left corner. forcereload: Clears the zoom cache, forces reloading the canvas from self.pil_image. """ # Sanity check if PIL_NOT_AVAILABLE: return if self.pil_image is None: return # Calculating the new zoom value prevzoom = self.zoom if zoom is not None: self.zoom = zoom if delta is not None: self.zoom += delta # Clamping maximum zoom if self.zoom > 2: self.zoom = 2 # Clamping minimum zoom if self.zoom < -4: self.zoom = -4 if forcereload: # Clearing the image cache self.tk_resized_images = {} # Zoom size is not cached... if not self.tk_resized_images.has_key(self.zoom): mult = 2 ** self.zoom ow, oh = self.pil_image.size w, h = int(mult * ow), int(mult * oh) # Let's apply a nice filter when scaling down, # but keep those lovely pixels when scaling up! filter = PIL.Image.NEAREST if mult >=1 else PIL.Image.BICUBIC # Resizing pil_resized_image = self.pil_image.resize((w,h), filter) # Converting to Tk tk_photoimage = PIL.ImageTk.PhotoImage(pil_resized_image) # Saving the Tk image to cache self.tk_resized_images[self.zoom] = tk_photoimage # Keeping the position centered if center: # Window coordinates, relative to widget's top-left corner wx, wy = center # Old canvas coordinates ocx = self.canvas.canvasx(wx) ocy = self.canvas.canvasy(wy) tx = self.canvas.canvasx(0) ty = self.canvas.canvasy(0) # Old scroll position osx = ocx - wx osy = ocy - wy # Multiplication... deltazoom = self.zoom - prevzoom deltamult = 2 ** deltazoom # New canvas coordinates ncx = ocx * deltamult ncy = ocy * deltamult # New scroll position nsx = ncx - wx nsy = ncy - wy # Debug #print ( # "w=({wx},{wy})\n" # "old canvas=({ocx},{ocy})\n" # "old scroll=({osx},{osy})\n" # "old scroll=({tx},{ty})\n" # "new canvas=({ncx},{ncy})\n" # "new scroll=({nsx},{nsy})\n" # "deltazoom={deltazoom}; deltamult={deltamult}" # .format(**locals()) #) # Getting the image from cache tk_img = self.tk_resized_images[self.zoom] # Setting it to canvas self.canvas.itemconfigure(self.canvas_image, image=tk_img) # Setting the size new_width = tk_img.width() new_height = tk_img.height() self.canvas["scrollregion"] = (0, 0, new_width, new_height) # Finally, scrolling in order to keep the position centered if center: # It was difficult to find the right formula to make # tkinter flawlessly... # http://stackoverflow.com/questions/3950773/how-to-scroll-a-tkinter-canvas-to-an-absolute-position offset_x = +1 if nsx >= 0 else 0 offset_y = +1 if nsy >= 0 else 0 self.canvas.xview_moveto(float(nsx + offset_x)/new_width) self.canvas.yview_moveto(float(nsy + offset_y)/new_height) def load_image_from_file(self, imagepath): if PIL_NOT_AVAILABLE: return # Loading the image from disk self.pil_image = PIL.Image.open(imagepath) # Creating the image inside the canvas if self.canvas_image is None: self.canvas_image = self.canvas.create_image(0, 0, anchor=NW) # Updating the canvas image self.resize_image_to_zoom(forcereload=True) class ApplicationFrame(Frame): """The main application frame is divided like this: +--------------+--------------------+ |Configuration | Run Frame here | |Frame here, +--------------------+ |which contains| ^| |sub-frames for| Image Frame here | |each section. | | | | | | |< >v| +--------------+--------------------+ """ def __init__(self, master=None): Frame.__init__(self, master, padx=5, pady=5 ) self.configuration_frame = ConfigurationFrame(self) self.configuration_frame.grid(column=0, row=0, rowspan=2, sticky=NSEW) self.run_frame = RunFrame(self) self.run_frame.grid(column=1, row=0, sticky=NSEW) self.image_frame = ImageFrame(self) self.image_frame.grid(column=1, row=1, sticky=NSEW) self.columnconfigure(1, weight=1) self.rowconfigure(1, weight=1) def find_named_widgets(widget): """Starting at the 'widget', scans down recursively and returns a dict with {'name': widget_instance}, but only if the name has been set by the user. There is a special treatment for Radiobutton: it returns the associated variable instead of the widget, but only if the user has set a name to the variable and stored it at the widget's "var" attribute.""" d = {} for name, w in widget.children.iteritems(): # If the user has set a name for this widget, and it is a valid name. # Unnamed widgets get numbers in their name. if name[0].isalpha() or name[0] == "_": d[name] = w # Special case for Radiobutton if ( isinstance(w, Radiobutton) and hasattr(w, "var") and not w.var._name.startswith("PY_VAR") ): d[w.var._name] = w.var # Recursive call d.update(find_named_widgets(w)) return d class UiShortcuts(object): """This object is a quick and handy way of accessing widget values. See the following small example to get an idea of how it works: ui = UiShortcuts(root_window) # This will call .set("bar") on a widget named "foo" ui.foo = "bar" # This will call .get() on a widget named "foo" print ui.foo """ def __init__(self, root): object.__setattr__(self, "widgets", find_named_widgets(root)) def __dir__(self): # Simple method to support tab-completion return self.__dict__.keys() + self.widgets.keys() def __getattr__(self, name): return self.widgets[name].get() def __setattr__(self, name, value): self.widgets[name].set(value) class MainWindow(Tk): """Root window. Contains one ApplicationFrame at self.app, and has shortcuts to all important widgets in self.ui. If you want to interact with the interface, using self.ui should be enough, and it hides all the messy toolkit-related code.""" def __init__(self): Tk.__init__(self) self.title("c10t GUI") self.app = ApplicationFrame(self) self.app.pack(fill=BOTH, expand=1) self.ui = UiShortcuts(self) # Global quit handlers self.bind_all("", self.quit_handler) self.protocol("WM_DELETE_WINDOW", self.quit_handler) #self.on_change_callback = None #self.app.configuration_frame.bind("") # Adding an "update" button, just because I don't know how to bind # to "on change"-like events in Tkinter. # http://stackoverflow.com/questions/3876229/how-to-run-a-code-whenever-a-tkinter-widget-value-changes self.update_button_callback = None self.app.run_frame.update_button["command"] = self.update_button_handler self.run_button_callback = None self.app.run_frame.run_button["command"] = self.run_button_handler self.load_button_callback = None self.app.configuration_frame.files_frame.load_button["command"] = self.load_button_handler def quit_handler(self, event=None): self.destroy() #def on_change_handler(self, event=None): # if self.on_change_callback: # self.on_change_callback() def update_button_handler(self, event=None): if self.update_button_callback: self.update_button_callback() def run_button_handler(self, event=None): if self.run_button_callback: self.run_button_callback() def load_button_handler(self): if self.load_button_callback: self.load_button_callback() def load_image_from_file(self, imagepath): self.app.image_frame.load_image_from_file(imagepath) ================================================ FILE: gui/c10t-tk/tooltip.py ================================================ # -*- coding: utf-8 -*- # vi:ts=4 sw=4 et # Downloaded from: # http://tkinter.unpythonic.net/wiki/ToolTip '''Michael Lange The ToolTip class provides a flexible tooltip widget for Tkinter; it is based on IDLE's ToolTip module which unfortunately seems to be broken (at least the version I saw). INITIALIZATION OPTIONS: anchor : where the text should be positioned inside the widget, must be on of "n", "s", "e", "w", "nw" and so on; default is "center" bd : borderwidth of the widget; default is 1 (NOTE: don't use "borderwidth" here) bg : background color to use for the widget; default is "lightyellow" (NOTE: don't use "background") delay : time in ms that it takes for the widget to appear on the screen when the mouse pointer has entered the parent widget; default is 1500 (XXX: changed to 800) fg : foreground (i.e. text) color to use; default is "black" (NOTE: don't use "foreground") follow_mouse : if set to 1 the tooltip will follow the mouse pointer instead of being displayed outside of the parent widget; this may be useful if you want to use tooltips for large widgets like listboxes or canvases; default is 0 font : font to use for the widget; default is system specific justify : how multiple lines of text will be aligned, must be "left", "right" or "center"; default is "left" padx : extra space added to the left and right within the widget; default is 4 pady : extra space above and below the text; default is 2 relief : one of "flat", "ridge", "groove", "raised", "sunken" or "solid"; default is "solid" state : must be "normal" or "disabled"; if set to "disabled" the tooltip will not appear; default is "normal" text : the text that is displayed inside the widget textvariable : if set to an instance of Tkinter.StringVar() the variable's value will be used as text for the widget width : width of the widget; the default is 0, which means that "wraplength" will be used to limit the widgets width wraplength : limits the number of characters in each line; default is 150 (XXX: changed to 200) WIDGET METHODS: configure(**opts) : change one or more of the widget's options as described above; the changes will take effect the next time the tooltip shows up; NOTE: follow_mouse cannot be changed after widget initialization Other widget methods that might be useful if you want to subclass ToolTip: enter() : callback when the mouse pointer enters the parent widget leave() : called when the mouse pointer leaves the parent widget motion() : is called when the mouse pointer moves inside the parent widget if follow_mouse is set to 1 and the tooltip has shown up to continually update the coordinates of the tooltip window coords() : calculates the screen coordinates of the tooltip window create_contents() : creates the contents of the tooltip window (by default a Tkinter.Label) ''' # Ideas gleaned from PySol import Tkinter class ToolTip: # XXX Changed the default delay from 1500 to 800 # XXX Changed the default wraplength from 150 to 200 def __init__(self, master, text='Your text here', delay=800, **opts): self.master = master self._opts = {'anchor':'center', 'bd':1, 'bg':'lightyellow', 'delay':delay, 'fg':'black',\ 'follow_mouse':0, 'font':None, 'justify':'left', 'padx':4, 'pady':2,\ 'relief':'solid', 'state':'normal', 'text':text, 'textvariable':None,\ 'width':0, 'wraplength':200} self.configure(**opts) self._tipwindow = None self._id = None self._id1 = self.master.bind("", self.enter, '+') self._id2 = self.master.bind("", self.leave, '+') self._id3 = self.master.bind("", self.leave, '+') self._follow_mouse = 0 if self._opts['follow_mouse']: self._id4 = self.master.bind("", self.motion, '+') self._follow_mouse = 1 def configure(self, **opts): for key in opts: if self._opts.has_key(key): self._opts[key] = opts[key] else: KeyError = 'KeyError: Unknown option: "%s"' %key raise KeyError ##----these methods handle the callbacks on "", "" and ""---------------## ##----events on the parent widget; override them if you want to change the widget's behavior--## def enter(self, event=None): self._schedule() def leave(self, event=None): self._unschedule() self._hide() def motion(self, event=None): if self._tipwindow and self._follow_mouse: x, y = self.coords() self._tipwindow.wm_geometry("+%d+%d" % (x, y)) ##------the methods that do the work:---------------------------------------------------------## def _schedule(self): self._unschedule() if self._opts['state'] == 'disabled': return self._id = self.master.after(self._opts['delay'], self._show) def _unschedule(self): id = self._id self._id = None if id: self.master.after_cancel(id) def _show(self): if self._opts['state'] == 'disabled': self._unschedule() return if not self._tipwindow: self._tipwindow = tw = Tkinter.Toplevel(self.master) # hide the window until we know the geometry tw.withdraw() tw.wm_overrideredirect(1) if tw.tk.call("tk", "windowingsystem") == 'aqua': tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, "help", "none") self.create_contents() tw.update_idletasks() x, y = self.coords() tw.wm_geometry("+%d+%d" % (x, y)) tw.deiconify() def _hide(self): tw = self._tipwindow self._tipwindow = None if tw: tw.destroy() ##----these methods might be overridden in derived classes:----------------------------------## def coords(self): # The tip window must be completely outside the master widget; # otherwise when the mouse enters the tip window we get # a leave event and it disappears, and then we get an enter # event and it reappears, and so on forever :-( # or we take care that the mouse pointer is always outside the tipwindow :-) tw = self._tipwindow twx, twy = tw.winfo_reqwidth(), tw.winfo_reqheight() w, h = tw.winfo_screenwidth(), tw.winfo_screenheight() # calculate the y coordinate: if self._follow_mouse: y = tw.winfo_pointery() + 20 # make sure the tipwindow is never outside the screen: if y + twy > h: y = y - twy - 30 else: y = self.master.winfo_rooty() + self.master.winfo_height() + 3 if y + twy > h: y = self.master.winfo_rooty() - twy - 3 # we can use the same x coord in both cases: x = tw.winfo_pointerx() - twx / 2 if x < 0: x = 0 elif x + twx > w: x = w - twx return x, y def create_contents(self): opts = self._opts.copy() for opt in ('delay', 'follow_mouse', 'state'): del opts[opt] label = Tkinter.Label(self._tipwindow, **opts) label.pack() ##---------demo code-----------------------------------## def demo(): root = Tkinter.Tk(className='ToolTip-demo') l = Tkinter.Listbox(root) l.insert('end', "I'm a listbox") l.pack(side='top') t1 = ToolTip(l, follow_mouse=1, text="I'm a tooltip with follow_mouse set to 1, so I won't be placed outside my parent") b = Tkinter.Button(root, text='Quit', command=root.quit) b.pack(side='bottom') t2 = ToolTip(b, text='Enough of this') root.mainloop() if __name__ == '__main__': demo() ================================================ FILE: palette.json ================================================ [ { "namespace": "minecraft", "material": "air", "mode": "block", "top_color": [255, 255, 255, 0], "darken": false, "legacy_id": 0 }, { "namespace": "minecraft", "material": "stone", "mode": "block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": 1, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "granite", "mode": "block", "top_color": [153, 113, 98, 255], "darken": true, "legacy_id": 1, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "polished_granite", "mode": "block", "top_color": [133, 88, 75, 255], "darken": true, "legacy_id": 1, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "diorite", "mode": "block", "top_color": [162, 161, 169, 255], "darken": true, "legacy_id": 1, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "polished_diorite", "mode": "block", "top_color": [158, 158, 167, 255], "darken": true, "legacy_id": 1, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "andesite", "mode": "block", "top_color": [114, 114, 119, 255], "darken": true, "legacy_id": 1, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "polished_andesite", "mode": "block", "top_color": [119, 119, 124, 255], "darken": true, "legacy_id": 1, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "grass_block", "mode": "block", "top_color": [120, 172, 70, 255], "side_color": [134, 96, 67, 255], "darken": true, "legacy_id": 2 }, { "namespace": "minecraft", "material": "dirt", "mode": "block", "top_color": [134, 96, 67, 255], "darken": true, "legacy_id": 3, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "coarse_dirt", "mode": "block", "top_color": [134, 96, 67, 255], "darken": true, "legacy_id": 3, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "podzol", "mode": "block", "top_color": [108, 67, 29, 255], "darken": true, "legacy_id": 3, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "cobblestone", "mode": "block", "top_color": [100, 100, 100, 255], "darken": true, "legacy_id": 4 }, { "namespace": "minecraft", "material": "oak_planks", "mode": "block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": 5, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "spruce_planks", "mode": "block", "top_color": [102, 77, 46, 255], "darken": true, "legacy_id": 5, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "birch_planks", "mode": "block", "top_color": [193, 177, 122, 255], "darken": true, "legacy_id": 5, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "jungle_planks", "mode": "block", "top_color": [152, 109, 76, 255], "darken": true, "legacy_id": 5, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "acacia_planks", "mode": "block", "top_color": [168, 91, 50, 255], "darken": true, "legacy_id": 5, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "dark_oak_planks", "mode": "block", "top_color": [60, 39, 18, 255], "darken": true, "legacy_id": 5, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "oak_sapling", "mode": "block", "top_color": [74, 131, 66, 128], "darken": true, "legacy_id": 6, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "spruce_sapling", "mode": "block", "top_color": [50, 89, 45, 128], "darken": true, "legacy_id": 6, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "birch_sapling", "mode": "block", "top_color": [94, 167, 84, 128], "darken": true, "legacy_id": 6, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "jungle_sapling", "mode": "block", "top_color": [58, 147, 19, 128], "darken": true, "legacy_id": 6, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "acacia_sapling", "mode": "block", "top_color": [58, 145, 19, 128], "darken": true, "legacy_id": 6, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "dark_oak_sapling", "mode": "block", "top_color": [52, 131, 18, 128], "darken": true, "legacy_id": 6, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "bedrock", "mode": "block", "top_color": [84, 84, 84, 255], "darken": true, "legacy_id": 7 }, { "namespace": "minecraft", "material": "flowing_water", "mode": "block", "top_color": [56, 68, 127, 64], "darken": false, "legacy_id": 8 }, { "namespace": "minecraft", "material": "water", "mode": "block", "top_color": [56, 68, 127, 64], "darken": false, "legacy_id": 9 }, { "namespace": "minecraft", "material": "flowing_lava", "mode": "block", "top_color": [255, 90, 0, 255], "darken": false, "legacy_id": 10 }, { "namespace": "minecraft", "material": "lava", "mode": "block", "top_color": [255, 90, 0, 255], "darken": false, "legacy_id": 11 }, { "namespace": "minecraft", "material": "sand", "mode": "block", "top_color": [218, 210, 158, 255], "darken": true, "legacy_id": 12, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "red_sand", "mode": "block", "top_color": [186, 102, 44, 255], "darken": true, "legacy_id": 12, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "gravel", "mode": "block", "top_color": [136, 126, 126, 255], "darken": true, "legacy_id": 13 }, { "namespace": "minecraft", "material": "gold_ore", "mode": "block", "top_color": [143, 140, 125, 255], "darken": true, "legacy_id": 14 }, { "namespace": "minecraft", "material": "iron_ore", "mode": "block", "top_color": [136, 130, 127, 255], "darken": true, "legacy_id": 15 }, { "namespace": "minecraft", "material": "coal_ore", "mode": "block", "top_color": [115, 115, 115, 255], "darken": true, "legacy_id": 16 }, { "namespace": "minecraft", "material": "oak_log", "mode": "log_block", "top_color": [184, 148, 95, 255], "side_color": [101, 80, 49, 255], "darken": false, "legacy_id": 17, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "spruce_log", "mode": "log_block", "top_color": [106, 82, 48, 255], "side_color": [45, 28, 12, 255], "darken": false, "legacy_id": 17, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "birch_log", "mode": "log_block", "top_color": [183, 164, 118, 255], "side_color": [201, 201, 196, 255], "darken": false, "legacy_id": 17, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "jungle_log", "mode": "log_block", "top_color": [155, 120, 75, 255], "side_color": [86, 67, 27, 255], "darken": false, "legacy_id": 17, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "oak_leaves", "mode": "legacy_leaf_block", "top_color": [74, 131, 66, 128], "darken": true, "legacy_id": 18, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "spruce_leaves", "mode": "legacy_leaf_block", "top_color": [50, 89, 45, 128], "darken": true, "legacy_id": 18, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "birch_leaves", "mode": "legacy_leaf_block", "top_color": [94, 167, 84, 128], "darken": true, "legacy_id": 18, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "jungle_leaves", "mode": "legacy_leaf_block", "top_color": [58, 147, 19, 128], "darken": true, "legacy_id": 18, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "sponge", "mode": "block", "top_color": [195, 195, 50, 255], "darken": true, "legacy_id": 19 }, { "namespace": "minecraft", "material": "glass", "mode": "block", "top_color": [255, 255, 255, 48], "darken": true, "legacy_id": 20 }, { "namespace": "minecraft", "material": "lapis_ore", "mode": "block", "top_color": [102, 112, 134, 255], "darken": true, "legacy_id": 21 }, { "namespace": "minecraft", "material": "lapis_block", "mode": "block", "top_color": [29, 71, 165, 255], "darken": true, "legacy_id": 22 }, { "namespace": "minecraft", "material": "dispenser", "mode": "block", "top_color": [107, 107, 107, 255], "darken": true, "legacy_id": 23 }, { "namespace": "minecraft", "material": "sandstone", "mode": "block", "top_color": [218, 210, 158, 255], "darken": true, "legacy_id": 24 }, { "namespace": "minecraft", "material": "note_block", "mode": "block", "top_color": [100, 67, 50, 255], "darken": true, "legacy_id": 25 }, { "namespace": "minecraft", "material": "white_bed", "mode": "block", "top_color": [223, 223, 223, 255], "darken": true, "legacy_id": 26, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_bed", "mode": "block", "top_color": [234, 128, 55, 255], "darken": true, "legacy_id": 26, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_bed", "mode": "block", "top_color": [191, 76, 201, 255], "darken": true, "legacy_id": 26, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_bed", "mode": "block", "top_color": [105, 139, 212, 255], "darken": true, "legacy_id": 26, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_bed", "mode": "block", "top_color": [195, 181, 28, 255], "darken": true, "legacy_id": 26, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_bed", "mode": "block", "top_color": [59, 189, 48, 255], "darken": true, "legacy_id": 26, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_bed", "mode": "block", "top_color": [218, 132, 155, 255], "darken": true, "legacy_id": 26, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_bed", "mode": "block", "top_color": [67, 67, 67, 255], "darken": true, "legacy_id": 26, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_gray_bed", "mode": "block", "top_color": [159, 166, 166, 255], "darken": true, "legacy_id": 26, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_bed", "mode": "block", "top_color": [39, 117, 150, 255], "darken": true, "legacy_id": 26, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_bed", "mode": "block", "top_color": [130, 54, 196, 255], "darken": true, "legacy_id": 26, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_bed", "mode": "block", "top_color": [39, 51, 154, 255], "darken": true, "legacy_id": 26, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_bed", "mode": "block", "top_color": [86, 51, 28, 255], "darken": true, "legacy_id": 26, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_bed", "mode": "block", "top_color": [56, 77, 24, 255], "darken": true, "legacy_id": 26, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_bed", "mode": "block", "top_color": [164, 45, 41, 255], "darken": true, "legacy_id": 26, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_bed", "mode": "block", "top_color": [27, 23, 23, 255], "darken": true, "legacy_id": 26, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "powered_rail", "mode": "block", "top_color": [120, 120, 120, 128], "side_color": [255, 220, 0, 128], "darken": false, "legacy_id": 27 }, { "namespace": "minecraft", "material": "detector_rail", "mode": "block", "top_color": [120, 120, 120, 128], "side_color": [230, 0, 0, 128], "darken": false, "legacy_id": 28 }, { "namespace": "minecraft", "material": "sticky_piston", "mode": "block", "top_color": [157, 192, 79, 255], "darken": true, "legacy_id": 29 }, { "namespace": "minecraft", "material": "cobweb", "mode": "block", "top_color": [237, 237, 237, 128], "darken": true, "legacy_id": 30 }, { "namespace": "minecraft", "material": "grass", "mode": "block", "top_color": [144, 188, 39, 255], "side_color": [144, 188, 39, 255], "darken": false, "legacy_id": 31 }, { "namespace": "minecraft", "material": "dead_bush", "mode": "block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": 32 }, { "namespace": "minecraft", "material": "piston", "mode": "block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": 33 }, { "namespace": "minecraft", "material": "piston_head", "mode": "block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": 34 }, { "namespace": "minecraft", "material": "white_wool", "mode": "block", "top_color": [223, 223, 223, 255], "darken": true, "legacy_id": 35, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_wool", "mode": "block", "top_color": [234, 128, 55, 255], "darken": true, "legacy_id": 35, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_wool", "mode": "block", "top_color": [191, 76, 201, 255], "darken": true, "legacy_id": 35, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_wool", "mode": "block", "top_color": [105, 139, 212, 255], "darken": true, "legacy_id": 35, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_wool", "mode": "block", "top_color": [195, 181, 28, 255], "darken": true, "legacy_id": 35, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_wool", "mode": "block", "top_color": [59, 189, 48, 255], "darken": true, "legacy_id": 35, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_wool", "mode": "block", "top_color": [218, 132, 155, 255], "darken": true, "legacy_id": 35, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_wool", "mode": "block", "top_color": [67, 67, 67, 255], "darken": true, "legacy_id": 35, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_gray_wool", "mode": "block", "top_color": [159, 166, 166, 255], "darken": true, "legacy_id": 35, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_wool", "mode": "block", "top_color": [39, 117, 150, 255], "darken": true, "legacy_id": 35, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_wool", "mode": "block", "top_color": [130, 54, 196, 255], "darken": true, "legacy_id": 35, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_wool", "mode": "block", "top_color": [39, 51, 154, 255], "darken": true, "legacy_id": 35, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_wool", "mode": "block", "top_color": [86, 51, 28, 255], "darken": true, "legacy_id": 35, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_wool", "mode": "block", "top_color": [56, 77, 24, 255], "darken": true, "legacy_id": 35, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_wool", "mode": "block", "top_color": [164, 45, 41, 255], "darken": true, "legacy_id": 35, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_wool", "mode": "block", "top_color": [27, 23, 23, 255], "darken": true, "legacy_id": 35, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "moving_piston", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 36 }, { "namespace": "minecraft", "material": "dandelion", "mode": "block", "top_color": [255, 255, 0, 255], "darken": true, "legacy_id": 37 }, { "namespace": "minecraft", "material": "poppy", "mode": "block", "top_color": [209, 6, 9, 255], "darken": true, "legacy_id": 38, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "blue_orchid", "mode": "block", "top_color": [28, 146, 214, 255], "darken": true, "legacy_id": 38, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "allium", "mode": "block", "top_color": [191, 117, 251, 255], "darken": true, "legacy_id": 38, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "azure_bluet", "mode": "block", "top_color": [216, 222, 230, 255], "darken": true, "legacy_id": 38, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "red_tulip", "mode": "block", "top_color": [208, 54, 18, 255], "darken": true, "legacy_id": 38, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "orange_tulip", "mode": "block", "top_color": [222, 115, 31, 255], "darken": true, "legacy_id": 38, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "white_tulip", "mode": "block", "top_color": [231, 231, 231, 255], "darken": true, "legacy_id": 38, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "pink_tulip", "mode": "block", "top_color": [234, 190, 234, 255], "darken": true, "legacy_id": 38, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "oxeye_daisy", "mode": "block", "top_color": [210, 199, 30, 255], "darken": true, "legacy_id": 38, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "brown_mushroom", "mode": "block", "top_color": [200, 200, 0, 255], "darken": true, "legacy_id": 39 }, { "namespace": "minecraft", "material": "red_mushroom", "mode": "block", "top_color": [255, 0, 0, 255], "darken": true, "legacy_id": 40 }, { "namespace": "minecraft", "material": "gold_block", "mode": "block", "top_color": [255, 237, 140, 255], "darken": true, "legacy_id": 41 }, { "namespace": "minecraft", "material": "iron_block", "mode": "block", "top_color": [217, 217, 217, 255], "darken": true, "legacy_id": 42 }, { "namespace": "minecraft", "material": "stone_slab", "mode": "legacy_slab_block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 0 }, { "namespace": "minecraft", "material": "sandstone_slab", "mode": "legacy_slab_block", "top_color": [218, 210, 158, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 1 }, { "namespace": "minecraft", "material": "petrified_oak_slab", "mode": "legacy_slab_block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 2 }, { "namespace": "minecraft", "material": "cobblestone_slab", "mode": "legacy_slab_block", "top_color": [100, 100, 100, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 3 }, { "namespace": "minecraft", "material": "brick_slab", "mode": "legacy_slab_block", "top_color": [86, 35, 23, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 4 }, { "namespace": "minecraft", "material": "stone_brick_slab", "mode": "legacy_slab_block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 5 }, { "namespace": "minecraft", "material": "nether_brick_slab", "mode": "legacy_slab_block", "top_color": [66, 32, 38, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 6 }, { "namespace": "minecraft", "material": "quartz_slab", "mode": "legacy_slab_block", "top_color": [217, 213, 206, 255], "darken": true, "legacy_id": [43, 44], "legacy_meta": 7 }, { "namespace": "minecraft", "material": "bricks", "mode": "block", "top_color": [86, 35, 23, 255], "darken": true, "legacy_id": 45 }, { "namespace": "minecraft", "material": "tnt", "mode": "block", "top_color": [255, 0, 0, 255], "darken": true, "legacy_id": 46 }, { "namespace": "minecraft", "material": "bookshelf", "mode": "block", "top_color": [191, 169, 116, 255], "darken": true, "legacy_id": 47 }, { "namespace": "minecraft", "material": "mossy_cobblestone", "mode": "block", "top_color": [127, 174, 125, 255], "darken": true, "legacy_id": 48 }, { "namespace": "minecraft", "material": "obsidian", "mode": "block", "top_color": [17, 13, 26, 255], "darken": true, "legacy_id": 49 }, { "namespace": "minecraft", "material": "torch", "mode": "torch_block", "top_color": [255, 225, 96, 208], "darken": false, "legacy_id": 50 }, { "namespace": "minecraft", "material": "wall_torch", "mode": "torch_block", "top_color": [255, 225, 96, 208], "darken": false }, { "namespace": "minecraft", "material": "fire", "mode": "block", "top_color": [224, 174, 21, 255], "darken": false, "legacy_id": 51 }, { "namespace": "minecraft", "material": "spawner", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 52 }, { "namespace": "minecraft", "material": "oak_stairs", "mode": "block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": 53 }, { "namespace": "minecraft", "material": "chest", "mode": "block", "top_color": [191, 135, 2, 255], "darken": true, "legacy_id": 54 }, { "namespace": "minecraft", "material": "redstone_wire", "mode": "block", "top_color": [111, 1, 1, 255], "darken": true, "legacy_id": 55 }, { "namespace": "minecraft", "material": "diamond_ore", "mode": "block", "top_color": [129, 140, 143, 255], "darken": true, "legacy_id": 56 }, { "namespace": "minecraft", "material": "diamond_block", "mode": "block", "top_color": [45, 166, 152, 255], "darken": true, "legacy_id": 57 }, { "namespace": "minecraft", "material": "crafting_table", "mode": "block", "top_color": [169, 107, 0, 255], "darken": true, "legacy_id": 58 }, { "namespace": "minecraft", "material": "wheat", "mode": "block", "top_color": [144, 188, 39, 255], "darken": true, "legacy_id": 59 }, { "namespace": "minecraft", "material": "farmland", "mode": "block", "top_color": [134, 96, 67, 255], "darken": true, "legacy_id": 60 }, { "namespace": "minecraft", "material": "furnace", "mode": "block", "top_color": [188, 188, 188, 255], "darken": true, "legacy_id": [61, 62] }, { "namespace": "minecraft", "material": "sign", "mode": "block", "darken": false, "legacy_id": [63, 68] }, { "namespace": "minecraft", "material": "oak_door", "mode": "block", "darken": false, "legacy_id": 64 }, { "namespace": "minecraft", "material": "ladder", "mode": "block", "top_color": [255, 200, 140, 0], "darken": false, "legacy_id": 65 }, { "namespace": "minecraft", "material": "rail", "mode": "block", "top_color": [120, 120, 120, 128], "darken": true, "legacy_id": 66 }, { "namespace": "minecraft", "material": "cobblestone_stairs", "mode": "block", "top_color": [120, 120, 120, 128], "darken": true, "legacy_id": 67 }, { "namespace": "minecraft", "material": "lever", "mode": "block", "top_color": [255, 255, 255, 0], "darken": false, "legacy_id": 69 }, { "namespace": "minecraft", "material": "stone_pressure_plate", "mode": "half_block", "top_color": [120, 120, 120, 255], "darken": true, "legacy_id": 70 }, { "namespace": "minecraft", "material": "iron_door", "mode": "block", "darken": false, "legacy_id": 71 }, { "namespace": "minecraft", "material": "oak_pressure_plate", "mode": "half_block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": 72 }, { "namespace": "minecraft", "material": "redstone_ore", "mode": "block", "top_color": [163, 145, 145, 255], "darken": true, "legacy_id": [73, 74] }, { "namespace": "minecraft", "material": "redstone_torch", "mode": "torch_block", "top_color": [181, 140, 64, 32], "darken": false, "legacy_id": [75, 76] }, { "namespace": "minecraft", "material": "redstone_wall_torch", "mode": "torch_block", "top_color": [181, 140, 64, 32], "darken": false }, { "namespace": "minecraft", "material": "stone_button", "mode": "block", "top_color": [255, 255, 255, 0], "darken": false, "legacy_id": 77 }, { "namespace": "minecraft", "material": "snow", "mode": "half_block", "top_color": [255, 255, 255, 255], "darken": true, "legacy_id": 78 }, { "namespace": "minecraft", "material": "ice", "mode": "block", "top_color": [120, 120, 255, 120], "darken": true, "legacy_id": 79 }, { "namespace": "minecraft", "material": "snow_block", "mode": "block", "top_color": [255, 255, 255, 255], "darken": true, "legacy_id": 80 }, { "namespace": "minecraft", "material": "cactus", "mode": "block", "top_color": [85, 107, 47, 255], "darken": true, "legacy_id": 81 }, { "namespace": "minecraft", "material": "clay", "mode": "block", "top_color": [144, 152, 168, 255], "darken": true, "legacy_id": 82 }, { "namespace": "minecraft", "material": "sugar_cane", "mode": "block", "top_color": [193, 234, 150, 200], "darken": true, "legacy_id": 83 }, { "namespace": "minecraft", "material": "jukebox", "mode": "block", "top_color": [125, 66, 44, 255], "darken": true, "legacy_id": 84 }, { "namespace": "minecraft", "material": "oak_fence", "mode": "block", "top_color": [157, 128, 79, 200], "darken": true, "legacy_id": 85 }, { "namespace": "minecraft", "material": "pumpkin", "mode": "block", "top_color": [227, 144, 29, 255], "darken": true, "legacy_id": 86 }, { "namespace": "minecraft", "material": "netherrack", "mode": "block", "top_color": [194, 115, 115, 255], "darken": true, "legacy_id": 87 }, { "namespace": "minecraft", "material": "soul_sand", "mode": "block", "top_color": [121, 97, 82, 255], "darken": true, "legacy_id": 88 }, { "namespace": "minecraft", "material": "glowstone", "mode": "block", "top_color": [255, 188, 94, 255], "darken": true, "legacy_id": 89 }, { "namespace": "minecraft", "material": "nether_portal", "mode": "block", "top_color": [60, 13, 106, 127], "darken": true, "legacy_id": 90 }, { "namespace": "minecraft", "material": "jack_o_lantern", "mode": "block", "top_color": [227, 144, 29, 255], "darken": true, "legacy_id": 91 }, { "namespace": "minecraft", "material": "cake", "mode": "half_block", "top_color": [228, 205, 206, 255], "darken": true, "legacy_id": 92 }, { "namespace": "minecraft", "material": "repeater", "mode": "half_block", "top_color": [111, 1, 1, 255], "darken": true, "legacy_id": [93, 94] }, { "namespace": "minecraft", "material": "white_stained_glass", "mode": "block", "top_color": [223, 223, 223, 127], "darken": true, "legacy_id": 95, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_stained_glass", "mode": "block", "top_color": [234, 128, 55, 127], "darken": true, "legacy_id": 95, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_stained_glass", "mode": "block", "top_color": [191, 76, 201, 127], "darken": true, "legacy_id": 95, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_stained_glass", "mode": "block", "top_color": [105, 139, 212, 127], "darken": true, "legacy_id": 95, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_stained_glass", "mode": "block", "top_color": [195, 181, 28, 127], "darken": true, "legacy_id": 95, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_stained_glass", "mode": "block", "top_color": [59, 189, 48, 127], "darken": true, "legacy_id": 95, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_stained_glass", "mode": "block", "top_color": [218, 132, 155, 127], "darken": true, "legacy_id": 95, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_stained_glass", "mode": "block", "top_color": [67, 67, 67, 127], "darken": true, "legacy_id": 95, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_stained_glass", "mode": "block", "top_color": [159, 166, 166, 127], "darken": true, "legacy_id": 95, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_stained_glass", "mode": "block", "top_color": [39, 117, 150, 127], "darken": true, "legacy_id": 95, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_stained_glass", "mode": "block", "top_color": [130, 54, 196, 127], "darken": true, "legacy_id": 95, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_stained_glass", "mode": "block", "top_color": [39, 51, 154, 127], "darken": true, "legacy_id": 95, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_stained_glass", "mode": "block", "top_color": [86, 51, 28, 127], "darken": true, "legacy_id": 95, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_stained_glass", "mode": "block", "top_color": [56, 77, 24, 127], "darken": true, "legacy_id": 95, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_stained_glass", "mode": "block", "top_color": [164, 45, 41, 127], "darken": true, "legacy_id": 95, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_stained_glass", "mode": "block", "top_color": [27, 23, 23, 127], "darken": true, "legacy_id": 95, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "oak_trapdoor", "mode": "half_block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": 96 }, { "namespace": "minecraft", "material": "infested_stone", "mode": "block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": 97, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "infested_cobblestone", "mode": "block", "top_color": [100, 100, 100, 255], "darken": true, "legacy_id": 97, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "infested_stone_bricks", "mode": "block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": 97, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "infested_mossy_stone_bricks", "mode": "block", "top_color": [127, 174, 125, 255], "darken": true, "legacy_id": 97, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "infested_cracked_stone_bricks", "mode": "block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": 97, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "infested_chiseled_stone_bricks", "mode": "block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": 97, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "stone_bricks", "mode": "block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": 98 }, { "namespace": "minecraft", "material": "brown_mushroom_block", "mode": "block", "top_color": [206, 174, 123, 255], "darken": true, "legacy_id": 99 }, { "namespace": "minecraft", "material": "red_mushroom_block", "mode": "block", "top_color": [183, 31, 29, 255], "darken": true, "legacy_id": 100 }, { "namespace": "minecraft", "material": "iron_bars", "mode": "block", "top_color": [217, 217, 217, 200], "darken": true, "legacy_id": 101 }, { "namespace": "minecraft", "material": "glass_pane", "mode": "block", "top_color": [255, 255, 255, 48], "darken": true, "legacy_id": 102 }, { "namespace": "minecraft", "material": "melon", "mode": "block", "top_color": [50, 200, 45, 255], "darken": true, "legacy_id": 103 }, { "namespace": "minecraft", "material": "pumpkin_stem", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 104 }, { "namespace": "minecraft", "material": "melon_stem", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 105 }, { "namespace": "minecraft", "material": "vine", "mode": "block", "top_color": [50, 89, 45, 128], "darken": false, "legacy_id": 106 }, { "namespace": "minecraft", "material": "oak_fence_gate", "mode": "block", "top_color": [157, 128, 79, 200], "darken": true, "legacy_id": 107 }, { "namespace": "minecraft", "material": "brick_stairs", "mode": "block", "top_color": [86, 35, 23, 255], "darken": true, "legacy_id": 108 }, { "namespace": "minecraft", "material": "stone_brick_stairs", "mode": "block", "top_color": [128, 128, 128, 255], "darken": true, "legacy_id": 109 }, { "namespace": "minecraft", "material": "mycelium", "mode": "block", "top_color": [110, 93, 133, 255], "darken": true, "legacy_id": 110 }, { "namespace": "minecraft", "material": "lily_pad", "mode": "half_block", "top_color": [50, 89, 45, 230], "darken": false, "legacy_id": 111 }, { "namespace": "minecraft", "material": "nether_bricks", "mode": "block", "top_color": [66, 32, 38, 255], "darken": true, "legacy_id": 112 }, { "namespace": "minecraft", "material": "nether_brick_fence", "mode": "block", "top_color": [66, 32, 38, 200], "darken": true, "legacy_id": 113 }, { "namespace": "minecraft", "material": "nether_brick_stairs", "mode": "block", "top_color": [66, 32, 38, 255], "darken": true, "legacy_id": 114 }, { "namespace": "minecraft", "material": "nether_wart", "mode": "block", "top_color": [149, 21, 8, 230], "darken": true, "legacy_id": 115 }, { "namespace": "minecraft", "material": "enchanting_table", "mode": "block", "top_color": [130, 5, 5, 255], "darken": true, "legacy_id": 116 }, { "namespace": "minecraft", "material": "brewing_stand", "mode": "block", "top_color": [124, 118, 51, 230], "darken": true, "legacy_id": 117 }, { "namespace": "minecraft", "material": "cauldron", "mode": "block", "top_color": [49, 49, 49, 255], "darken": true, "legacy_id": 118 }, { "namespace": "minecraft", "material": "end_portal", "mode": "block", "top_color": [35, 60, 99, 128], "darken": true, "legacy_id": 119 }, { "namespace": "minecraft", "material": "end_portal_frame", "mode": "block", "top_color": [62, 115, 105, 255], "darken": true, "legacy_id": 120 }, { "namespace": "minecraft", "material": "end_stone", "mode": "block", "top_color": [203, 206, 148, 255], "darken": true, "legacy_id": 121 }, { "namespace": "minecraft", "material": "dragon_egg", "mode": "block", "top_color": [72, 4, 82, 255], "darken": true, "legacy_id": 122 }, { "namespace": "minecraft", "material": "redstone_lamp", "mode": "block", "top_color": [145, 90, 57, 255], "darken": true, "legacy_id": 123 }, { "namespace": "minecraft", "material": "oak_slab", "mode": "legacy_slab_block", "top_color": [157, 128, 79, 255], "darken": true, "legacy_id": [125, 126], "legacy_meta": 0 }, { "namespace": "minecraft", "material": "spruce_slab", "mode": "legacy_slab_block", "top_color": [102, 77, 46, 255], "darken": true, "legacy_id": [125, 126], "legacy_meta": 1 }, { "namespace": "minecraft", "material": "birch_slab", "mode": "legacy_slab_block", "top_color": [193, 177, 122, 255], "darken": true, "legacy_id": [125, 126], "legacy_meta": 2 }, { "namespace": "minecraft", "material": "jungle_slab", "mode": "legacy_slab_block", "top_color": [152, 109, 76, 255], "darken": true, "legacy_id": [125, 126], "legacy_meta": 3 }, { "namespace": "minecraft", "material": "acacia_slab", "mode": "legacy_slab_block", "top_color": [168, 91, 50, 255], "darken": true, "legacy_id": [125, 126], "legacy_meta": 4 }, { "namespace": "minecraft", "material": "dark_oak_slab", "mode": "legacy_slab_block", "top_color": [60, 39, 18, 255], "darken": true, "legacy_id": [125, 126], "legacy_meta": 5 }, { "namespace": "minecraft", "material": "cocoa", "mode": "block", "top_color": [190, 116, 45, 230], "darken": true, "legacy_id": 127 }, { "namespace": "minecraft", "material": "sandstone_stairs", "mode": "block", "top_color": [218, 210, 158, 255], "darken": true, "legacy_id": 128 }, { "namespace": "minecraft", "material": "emerald_ore", "mode": "block", "top_color": [94, 124, 105, 255], "darken": true, "legacy_id": 129 }, { "namespace": "minecraft", "material": "ender_chest", "mode": "block", "top_color": [42, 58, 60, 255], "side_color": [35, 49, 33, 255], "darken": true, "legacy_id": 130 }, { "namespace": "minecraft", "material": "tripwire_hook", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 131 }, { "namespace": "minecraft", "material": "tripwire", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 132 }, { "namespace": "minecraft", "material": "emerald_block", "mode": "block", "top_color": [84, 218, 123, 255], "darken": true, "legacy_id": 133 }, { "namespace": "minecraft", "material": "spruce_stairs", "mode": "block", "top_color": [102, 77, 46, 255], "darken": true, "legacy_id": 134 }, { "namespace": "minecraft", "material": "birch_stairs", "mode": "block", "top_color": [193, 177, 122, 255], "darken": true, "legacy_id": 135 }, { "namespace": "minecraft", "material": "jungle_stairs", "mode": "block", "top_color": [152, 109, 76, 255], "darken": true, "legacy_id": 136 }, { "namespace": "minecraft", "material": "command_block", "mode": "block", "top_color": [186, 118, 74, 255], "darken": true, "legacy_id": 137 }, { "namespace": "minecraft", "material": "beacon", "mode": "block", "top_color": [196, 255, 254, 230], "darken": true, "legacy_id": 138 }, { "namespace": "minecraft", "material": "cobblestone_wall", "mode": "block", "top_color": [100, 100, 100, 255], "darken": true, "legacy_id": 139 }, { "namespace": "minecraft", "material": "flower_pot", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 140 }, { "namespace": "minecraft", "material": "carrots", "mode": "block", "top_color": [2, 161, 0, 255], "darken": true, "legacy_id": 141 }, { "namespace": "minecraft", "material": "potatoes", "mode": "block", "top_color": [0, 174, 25, 255], "darken": true, "legacy_id": 142 }, { "namespace": "minecraft", "material": "oak_button", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 143 }, { "namespace": "minecraft", "material": "skeleton_skull", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "skeleton_wall_skull", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "wither_skeleton_skull", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "wither_skeleton_wall_skull", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "zombie_head", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "zombie_wall_head", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "player_head", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "player_wall_head", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "creeper_head", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "creeper_wall_head", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "dragon_head", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "dragon_wall_head", "mode": "block", "top_color": [255, 255,255, 0], "darken": true, "legacy_id": 144, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "anvil", "mode": "block", "top_color": [66, 62, 62, 255], "darken": true, "legacy_id": 145, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "chipped_anvil", "mode": "block", "top_color": [66, 62, 62, 255], "darken": true, "legacy_id": 145, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "damaged_anvil", "mode": "block", "top_color": [66, 62, 62, 255], "darken": true, "legacy_id": 145, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "trapped_chest", "mode": "block", "top_color": [191, 135, 2, 255], "darken": true, "legacy_id": 146 }, { "namespace": "minecraft", "material": "light_weighted_pressure_plate", "mode": "half_block", "top_color": [255, 237, 140, 255], "darken": true, "legacy_id": 147 }, { "namespace": "minecraft", "material": "heavy_weighted_pressure_plate", "mode": "half_block", "top_color": [217, 217, 217, 255], "darken": true, "legacy_id": 148 }, { "namespace": "minecraft", "material": "comparator", "mode": "half_block", "top_color": [111, 1, 1, 255], "darken": true, "legacy_id": 149 }, { "namespace": "minecraft", "material": "daylight_detector", "mode": "half_block", "top_color": [194, 179, 158, 255], "side_color": [29, 23, 14, 255], "darken": true, "legacy_id": 151 }, { "namespace": "minecraft", "material": "redstone_block", "mode": "block", "top_color": [150, 24, 7, 255], "darken": true, "legacy_id": 152 }, { "namespace": "minecraft", "material": "nether_quartz_ore", "mode": "block", "top_color": [190, 143, 136, 255], "darken": true, "legacy_id": 153 }, { "namespace": "minecraft", "material": "hopper", "mode": "block", "top_color": [55, 55, 55, 255], "darken": true, "legacy_id": 154 }, { "namespace": "minecraft", "material": "quartz_block", "mode": "block", "top_color": [217, 213, 206, 255], "darken": true, "legacy_id": 155 }, { "namespace": "minecraft", "material": "quartz_stairs", "mode": "block", "top_color": [217, 213, 206, 255], "darken": true, "legacy_id": 156 }, { "namespace": "minecraft", "material": "activator_rail", "mode": "block", "top_color": [120, 120, 120, 128], "side_color": [255, 220, 0, 128], "darken": false, "legacy_id": 157 }, { "namespace": "minecraft", "material": "dropper", "mode": "block", "top_color": [107, 107, 107, 255], "darken": true, "legacy_id": 158 }, { "namespace": "minecraft", "material": "white_terracotta", "mode": "block", "top_color": [210, 177, 161, 255], "darken": true, "legacy_id": 159, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_terracotta", "mode": "block", "top_color": [162, 82, 36, 255], "darken": true, "legacy_id": 159, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_terracotta", "mode": "block", "top_color": [149, 89, 110, 255], "darken": true, "legacy_id": 159, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_terracotta", "mode": "block", "top_color": [117, 111, 140, 255], "darken": true, "legacy_id": 159, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_terracotta", "mode": "block", "top_color": [186, 134, 36, 255], "darken": true, "legacy_id": 159, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_terracotta", "mode": "block", "top_color": [102, 115, 48, 255], "darken": true, "legacy_id": 159, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_terracotta", "mode": "block", "top_color": [163, 80, 80, 255], "darken": true, "legacy_id": 159, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_terracotta", "mode": "block", "top_color": [57, 42, 35, 255], "darken": true, "legacy_id": 159, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_gray_terracotta", "mode": "block", "top_color": [135, 107, 98, 255], "darken": true, "legacy_id": 159, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_terracotta", "mode": "block", "top_color": [91, 95, 94, 255], "darken": true, "legacy_id": 159, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_terracotta", "mode": "block", "top_color": [119, 72, 87, 255], "darken": true, "legacy_id": 159, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_terracotta", "mode": "block", "top_color": [76, 62, 92, 255], "darken": true, "legacy_id": 159, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_terracotta", "mode": "block", "top_color": [77, 51, 36, 255], "darken": true, "legacy_id": 159, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_terracotta", "mode": "block", "top_color": [76, 84, 43, 255], "darken": true, "legacy_id": 159, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_terracotta", "mode": "block", "top_color": [142, 61, 47, 255], "darken": true, "legacy_id": 159, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_terracotta", "mode": "block", "top_color": [39, 24, 17, 255], "darken": true, "legacy_id": 159, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "white_stained_glass_pane", "mode": "block", "top_color": [223, 223, 223, 127], "darken": true, "legacy_id": 160, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_stained_glass_pane", "mode": "block", "top_color": [234, 128, 55, 127], "darken": true, "legacy_id": 160, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_stained_glass_pane", "mode": "block", "top_color": [191, 76, 201, 127], "darken": true, "legacy_id": 160, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_stained_glass_pane", "mode": "block", "top_color": [105, 139, 212, 127], "darken": true, "legacy_id": 160, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_stained_glass_pane", "mode": "block", "top_color": [195, 181, 28, 127], "darken": true, "legacy_id": 160, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_stained_glass_pane", "mode": "block", "top_color": [59, 189, 48, 127], "darken": true, "legacy_id": 160, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_stained_glass_pane", "mode": "block", "top_color": [218, 132, 155, 127], "darken": true, "legacy_id": 160, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_stained_glass_pane", "mode": "block", "top_color": [67, 67, 67, 127], "darken": true, "legacy_id": 160, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_stained_glass_pane", "mode": "block", "top_color": [159, 166, 166, 127], "darken": true, "legacy_id": 160, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_stained_glass_pane", "mode": "block", "top_color": [39, 117, 150, 127], "darken": true, "legacy_id": 160, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_stained_glass_pane", "mode": "block", "top_color": [130, 54, 196, 127], "darken": true, "legacy_id": 160, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_stained_glass_pane", "mode": "block", "top_color": [39, 51, 154, 127], "darken": true, "legacy_id": 160, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_stained_glass_pane", "mode": "block", "top_color": [86, 51, 28, 127], "darken": true, "legacy_id": 160, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_stained_glass_pane", "mode": "block", "top_color": [56, 77, 24, 127], "darken": true, "legacy_id": 160, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_stained_glass_pane", "mode": "block", "top_color": [164, 45, 41, 127], "darken": true, "legacy_id": 160, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_stained_glass_pane", "mode": "block", "top_color": [27, 23, 23, 127], "darken": true, "legacy_id": 160, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "acacia_leaves", "mode": "legacy_leaf_block", "top_color": [58, 145, 19, 128], "darken": true, "legacy_id": 161, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "dark_oak_leaves", "mode": "legacy_leaf_block", "top_color": [52, 131, 18, 128], "darken": true, "legacy_id": 161, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "acacia_log", "mode": "log_block", "top_color": [155, 90, 62, 255], "side_color": [104, 98, 88, 255], "darken": false, "legacy_id": 162, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "dark_oak_log", "mode": "log_block", "top_color": [79, 63, 41, 255], "side_color": [51, 40, 23, 255], "darken": false, "legacy_id": 162, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "acacia_stairs", "mode": "block", "top_color": [168, 91, 50, 255], "darken": true, "legacy_id": 163 }, { "namespace": "minecraft", "material": "dark_oak_stairs", "mode": "block", "top_color": [60, 39, 18, 255], "darken": true, "legacy_id": 164 }, { "namespace": "minecraft", "material": "slime_block", "mode": "block", "top_color": [114, 188, 105, 230], "darken": true, "legacy_id": 165 }, { "namespace": "minecraft", "material": "barrier", "mode": "block", "top_color": [255, 255, 255, 0], "darken": true, "legacy_id": 166 }, { "namespace": "minecraft", "material": "iron_trapdoor", "mode": "half_block", "top_color": [217, 217, 217, 255], "darken": true, "legacy_id": 167 }, { "namespace": "minecraft", "material": "prismarine", "mode": "block", "top_color": [69, 122, 123, 255], "darken": true, "legacy_id": 168, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "prismarine_bricks", "mode": "block", "top_color": [69, 123, 108, 255], "darken": true, "legacy_id": 168, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "dark_prismarine", "mode": "block", "top_color": [59, 87, 75, 255], "darken": true, "legacy_id": 168, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "sea_lantern", "mode": "block", "top_color": [68, 121, 104, 255], "darken": true, "legacy_id": 169 }, { "namespace": "minecraft", "material": "hay_block", "mode": "block", "top_color": [192, 160, 14, 255], "darken": true, "legacy_id": 170 }, { "namespace": "minecraft", "material": "white_carpet", "mode": "half_block", "top_color": [223, 223, 223, 255], "darken": true, "legacy_id": 171, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_carpet", "mode": "half_block", "top_color": [234, 128, 55, 255], "darken": true, "legacy_id": 171, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_carpet", "mode": "half_block", "top_color": [191, 76, 201, 255], "darken": true, "legacy_id": 171, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_carpet", "mode": "half_block", "top_color": [105, 139, 212, 255], "darken": true, "legacy_id": 171, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_carpet", "mode": "half_block", "top_color": [195, 181, 28, 255], "darken": true, "legacy_id": 171, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_carpet", "mode": "half_block", "top_color": [59, 189, 48, 255], "darken": true, "legacy_id": 171, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_carpet", "mode": "half_block", "top_color": [218, 132, 155, 255], "darken": true, "legacy_id": 171, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_carpet", "mode": "half_block", "top_color": [67, 67, 67, 255], "darken": true, "legacy_id": 171, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_gray_carpet", "mode": "half_block", "top_color": [159, 166, 166, 255], "darken": true, "legacy_id": 171, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_carpet", "mode": "half_block", "top_color": [39, 117, 150, 255], "darken": true, "legacy_id": 171, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_carpet", "mode": "half_block", "top_color": [130, 54, 196, 255], "darken": true, "legacy_id": 171, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_carpet", "mode": "half_block", "top_color": [39, 51, 154, 255], "darken": true, "legacy_id": 171, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_carpet", "mode": "half_block", "top_color": [86, 51, 28, 255], "darken": true, "legacy_id": 171, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_carpet", "mode": "half_block", "top_color": [56, 77, 24, 255], "darken": true, "legacy_id": 171, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_carpet", "mode": "half_block", "top_color": [164, 45, 41, 255], "darken": true, "legacy_id": 171, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_carpet", "mode": "half_block", "top_color": [27, 23, 23, 255], "darken": true, "legacy_id": 171, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "terracotta", "mode": "block", "top_color": [140, 86, 63, 255], "darken": true, "legacy_id": 172 }, { "namespace": "minecraft", "material": "coal_block", "mode": "block", "top_color": [20, 20, 20, 255], "darken": true, "legacy_id": 173 }, { "namespace": "minecraft", "material": "packed_ice", "mode": "block", "top_color": [120, 120, 255, 180], "darken": true, "legacy_id": 174 }, { "namespace": "minecraft", "material": "sunflower", "mode": "large_plant_block", "top_color": [230, 187, 33, 255], "side_color": [66, 108, 43, 255], "darken": false, "legacy_id": 175, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "lilac", "mode": "large_plant_block", "top_color": [159, 120, 164, 255], "darken": false, "legacy_id": 175, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "tall_grass", "mode": "large_plant_block", "top_color": [144, 188, 39, 255], "darken": false, "legacy_id": 175, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "large_fern", "mode": "large_plant_block", "top_color": [144, 188, 39, 255], "darken": false, "legacy_id": 175, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "rose_bush", "mode": "large_plant_block", "top_color": [190, 26, 18, 255], "darken": false, "legacy_id": 175, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "peony", "mode": "large_plant_block", "top_color": [222, 165, 247, 255], "side_color": [69, 96, 73, 255], "darken": false, "legacy_id": 175, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "red_sandstone", "mode": "block", "top_color": [166, 85, 30, 255], "darken": true, "legacy_id": 179 }, { "namespace": "minecraft", "material": "red_sandstone_stairs", "mode": "block", "top_color": [166, 85, 30, 255], "darken": true, "legacy_id": 180 }, { "namespace": "minecraft", "material": "red_sandstone_slab", "mode": "legacy_slab_block", "top_color": [166, 85, 30, 255], "darken": true, "legacy_id": [ 181, 182 ] }, { "namespace": "minecraft", "material": "spruce_fence_gate", "mode": "block", "top_color": [102, 77, 46, 200], "darken": true, "legacy_id": 183 }, { "namespace": "minecraft", "material": "birch_fence_gate", "mode": "block", "top_color": [193, 177, 122, 200], "darken": true, "legacy_id": 184 }, { "namespace": "minecraft", "material": "jungle_fence_gate", "mode": "block", "top_color": [152, 109, 76, 200], "darken": true, "legacy_id": 185 }, { "namespace": "minecraft", "material": "dark_oak_fence_gate", "mode": "block", "top_color": [60, 39, 18, 200], "darken": true, "legacy_id": 186 }, { "namespace": "minecraft", "material": "acacia_fence_gate", "mode": "block", "top_color": [168, 91, 50, 200], "darken": true, "legacy_id": 187 }, { "namespace": "minecraft", "material": "spruce_fence", "mode": "block", "top_color": [102, 77, 46, 200], "darken": true, "legacy_id": 188 }, { "namespace": "minecraft", "material": "birch_fence", "mode": "block", "top_color": [193, 177, 122, 200], "darken": true, "legacy_id": 189 }, { "namespace": "minecraft", "material": "jungle_fence", "mode": "block", "top_color": [152, 109, 76, 200], "darken": true, "legacy_id": 190 }, { "namespace": "minecraft", "material": "dark_oak_fence", "mode": "block", "top_color": [60, 39, 18, 200], "darken": true, "legacy_id": 191 }, { "namespace": "minecraft", "material": "acacia_fence", "mode": "block", "top_color": [168, 91, 50, 200], "darken": true, "legacy_id": 192 }, { "namespace": "minecraft", "material": "spruce_door", "mode": "block", "darken": false, "legacy_id": 193 }, { "namespace": "minecraft", "material": "birch_door", "mode": "block", "darken": false, "legacy_id": 194 }, { "namespace": "minecraft", "material": "jungle_door", "mode": "block", "darken": false, "legacy_id": 195 }, { "namespace": "minecraft", "material": "acacia_door", "mode": "block", "darken": false, "legacy_id": 196 }, { "namespace": "minecraft", "material": "dark_oak_door", "mode": "block", "darken": false, "legacy_id": 197 }, { "namespace": "minecraft", "material": "end_rod", "mode": "block", "top_color": [94, 73, 131, 140], "darken": false, "legacy_id": 198 }, { "namespace": "minecraft", "material": "chorus_plant", "mode": "block", "top_color": [116, 70, 124, 255], "darken": true, "legacy_id": 199 }, { "namespace": "minecraft", "material": "chorus_flower", "mode": "block", "top_color": [108, 84, 108, 255], "darken": true, "legacy_id": 200 }, { "namespace": "minecraft", "material": "purpur_block", "mode": "block", "top_color": [104, 58, 103, 255], "darken": true, "legacy_id": 201 }, { "namespace": "minecraft", "material": "purpur_pillar", "mode": "block", "top_color": [166, 124, 166, 255], "darken": true, "legacy_id": 202 }, { "namespace": "minecraft", "material": "purpur_stairs", "mode": "block", "top_color": [140, 105, 142, 255], "darken": true, "legacy_id": 203 }, { "namespace": "minecraft", "material": "purpur_slab", "mode": "legacy_slab_block", "top_color": [104, 58, 103, 255], "darken": true, "legacy_id": [204, 205] }, { "namespace": "minecraft", "material": "end_stone_bricks", "mode": "block", "top_color": [223, 229, 170, 255], "darken": true, "legacy_id": 206 }, { "namespace": "minecraft", "material": "beetroots", "mode": "block", "top_color": [76, 108, 49, 255], "darken": true, "legacy_id": 207 }, { "namespace": "minecraft", "material": "grass_path", "mode": "block", "top_color": [149, 125, 71, 255], "darken": true, "legacy_id": 208 }, { "namespace": "minecraft", "material": "end_gateway", "mode": "block", "top_color": [15, 15, 25, 255], "darken": true, "legacy_id": 209 }, { "namespace": "minecraft", "material": "repeating_command_block", "mode": "block", "top_color": [135, 122, 152, 255], "darken": true, "legacy_id": 210 }, { "namespace": "minecraft", "material": "chain_command_block", "mode": "block", "top_color": [114, 135, 131, 255], "darken": true, "legacy_id": 211 }, { "namespace": "minecraft", "material": "frosted_ice", "mode": "block", "top_color": [120, 120, 255, 120], "darken": true, "legacy_id": 212 }, { "namespace": "minecraft", "material": "magma_block", "mode": "block", "top_color": [132, 64, 26, 255], "darken": true, "legacy_id": 213 }, { "namespace": "minecraft", "material": "nether_wart_block", "mode": "block", "top_color": [107, 60, 60, 255], "darken": true, "legacy_id": 214 }, { "namespace": "minecraft", "material": "red_nether_bricks", "mode": "block", "top_color": [106, 59, 62, 255], "darken": true, "legacy_id": 215 }, { "namespace": "minecraft", "material": "bone_block", "mode": "block", "top_color": [218, 214, 193, 255], "darken": true, "legacy_id": 216 }, { "namespace": "minecraft", "material": "structure_void", "mode": "block", "top_color": [255, 255, 255, 0], "darken": false, "legacy_id": 217 }, { "namespace": "minecraft", "material": "observer", "mode": "block", "top_color": [107, 107, 107, 255], "darken": true, "legacy_id": 218 }, { "namespace": "minecraft", "material": "white_shulker_box", "mode": "block", "top_color": [205, 210, 210, 255], "darken": true, "legacy_id": 219 }, { "namespace": "minecraft", "material": "orange_shulker_box", "mode": "block", "top_color": [228, 104, 9, 255], "darken": true, "legacy_id": 220 }, { "namespace": "minecraft", "material": "magenta_shulker_box", "mode": "block", "top_color": [168, 52, 158, 255], "darken": true, "legacy_id": 221 }, { "namespace": "minecraft", "material": "light_blue_shulker_box", "mode": "block", "top_color": [46, 156, 206, 255], "darken": true, "legacy_id": 222 }, { "namespace": "minecraft", "material": "yellow_shulker_box", "mode": "block", "top_color": [241, 182, 27, 255], "darken": true, "legacy_id": 223 }, { "namespace": "minecraft", "material": "lime_shulker_box", "mode": "block", "top_color": [94, 163, 22, 255], "darken": true, "legacy_id": 224 }, { "namespace": "minecraft", "material": "pink_shulker_box", "mode": "block", "top_color": [221, 114, 150, 255], "darken": true, "legacy_id": 225 }, { "namespace": "minecraft", "material": "gray_shulker_box", "mode": "block", "top_color": [56, 59, 63, 255], "darken": true, "legacy_id": 226 }, { "namespace": "minecraft", "material": "light_gray_shulker_box", "mode": "block", "top_color": [117, 117, 108, 255], "darken": true, "legacy_id": 227 }, { "namespace": "minecraft", "material": "cyan_shulker_box", "mode": "block", "top_color": [20, 116, 130, 255], "darken": true, "legacy_id": 228 }, { "namespace": "minecraft", "material": "purple_shulker_box", "mode": "block", "top_color": [148, 103, 148, 255], "darken": true, "legacy_id": 229 }, { "namespace": "minecraft", "material": "blue_shulker_box", "mode": "block", "top_color": [42, 44, 135, 255], "darken": true, "legacy_id": 230 }, { "namespace": "minecraft", "material": "brown_shulker_box", "mode": "block", "top_color": [103, 64, 35, 255], "darken": true, "legacy_id": 231 }, { "namespace": "minecraft", "material": "green_shulker_box", "mode": "block", "top_color": [76, 96, 32, 255], "darken": true, "legacy_id": 232 }, { "namespace": "minecraft", "material": "red_shulker_box", "mode": "block", "top_color": [138, 30, 29, 255], "darken": true, "legacy_id": 233 }, { "namespace": "minecraft", "material": "black_shulker_box", "mode": "block", "top_color": [23, 24, 28, 255], "darken": true, "legacy_id": 234 }, { "namespace": "minecraft", "material": "white_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 235 }, { "namespace": "minecraft", "material": "orange_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 236 }, { "namespace": "minecraft", "material": "magenta_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 237 }, { "namespace": "minecraft", "material": "light_blue_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 238 }, { "namespace": "minecraft", "material": "yellow_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 239 }, { "namespace": "minecraft", "material": "lime_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 240 }, { "namespace": "minecraft", "material": "pink_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 241 }, { "namespace": "minecraft", "material": "gray_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 242 }, { "namespace": "minecraft", "material": "silver_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 243 }, { "namespace": "minecraft", "material": "cyan_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 244 }, { "namespace": "minecraft", "material": "purple_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 245 }, { "namespace": "minecraft", "material": "blue_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 246 }, { "namespace": "minecraft", "material": "brown_glazed_terracotta ", "mode": "block", "darken": true, "legacy_id": 247 }, { "namespace": "minecraft", "material": "green_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 248 }, { "namespace": "minecraft", "material": "red_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 249 }, { "namespace": "minecraft", "material": "black_glazed_terracotta", "mode": "block", "darken": true, "legacy_id": 250 }, { "namespace": "minecraft", "material": "white_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_gray_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_concrete", "mode": "block", "darken": true, "legacy_id": 251, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "white_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 0 }, { "namespace": "minecraft", "material": "orange_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 1 }, { "namespace": "minecraft", "material": "magenta_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 2 }, { "namespace": "minecraft", "material": "light_blue_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 3 }, { "namespace": "minecraft", "material": "yellow_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 4 }, { "namespace": "minecraft", "material": "lime_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 5 }, { "namespace": "minecraft", "material": "pink_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 6 }, { "namespace": "minecraft", "material": "gray_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 7 }, { "namespace": "minecraft", "material": "light_gray_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 8 }, { "namespace": "minecraft", "material": "cyan_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 9 }, { "namespace": "minecraft", "material": "purple_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 10 }, { "namespace": "minecraft", "material": "blue_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 11 }, { "namespace": "minecraft", "material": "brown_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 12 }, { "namespace": "minecraft", "material": "green_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 13 }, { "namespace": "minecraft", "material": "red_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 14 }, { "namespace": "minecraft", "material": "black_concrete_powder", "mode": "block", "darken": true, "legacy_id": 252, "legacy_meta": 15 }, { "namespace": "minecraft", "material": "structure_block", "mode": "block", "top_color": [98, 74, 98, 255], "darken": true, "legacy_id": 255 } ] ================================================ FILE: res/example.html ================================================ ================================================ FILE: res/libc10t.js ================================================ function c10t(json) { this.MapX = json.st.MapX; this.MapY = json.st.MapY; this.MapZ = json.st.MapZ; this.mx_x = json.world.mx_x; this.mn_x = json.world.mn_x; this.mx_z = json.world.mx_z; this.mn_z = json.world.mn_z; this.mx_y = this.MapY; this.mn_y = 0; this.cx = json.world.cx; this.cy = json.world.cy; this.dx = json.world.dx; this.dz = json.world.dz; this.dy = this.MapY; this.mode = this.modes[json.world.mode]; this.projection = this.projections[this.mode]; this.project = function (x, y, z) { cropCompensation = this.projection(0, 0, 0); coordinates = this.projection(x, y, z); coordinates[0] -= cropCompensation[0] - this.cx; coordinates[1] -= cropCompensation[1] - this.cy; return coordinates; } } c10t.prototype.modes = { 0x0: "top", 0x1: "oblique", 0x2: "obliqueangle", 0x3: "isometric" }; c10t.prototype.projections = { 'top': function(x, y, z) { x -= this.mn_x; z -= this.mn_z; return [(this.dz - z - 1), x]; }, 'oblique': function(x, y, z) { x -= this.mn_x; z -= this.mn_z; return [(this.dz - z - 1) + x, x]; }, 'obliqueangle': function(x, y, z) { x -= this.mn_x; z -= this.mn_z; return [(this.dz - z - 1) + x, (this.dy - y - 1) + z + x]; }, 'isometric': function(x, y, z) { x -= this.mn_x; z -= this.mn_z; return [2 * ((this.dz - z - 1) + x), 2 * (this.dy - y - 1) + z + x]; } }; ================================================ FILE: scripts/area-set.sh ================================================ #!/bin/bash basics=" -w /home/j/.minecraft/saves/World1" prefix='./images/area' threads=2 for i in $(seq -150 -20) do imgfile="$prefix$i.png" echo "Rendering $i..." ./c10t $basics -o $imgfile -L -150,$i,-100,150 -q -s & # Wait for threads to finish... let mod=$i%$threads if [ $mod = 0 ]; then wait fi done echo Trimming... # Make all images the same size (assuming last image largest) mogrify -bordercolor white -border 1x1 -trim $prefix*.png newsize=$(identify -format '%wx%h' $imgfile) mogrify -extent $newsize -background white +repage $prefix*.png #Animate into a gif... echo Animating... convert -delay 20 -loop -1 $prefix*.png $prefix-animated.gif ================================================ FILE: scripts/area-slice.sh ================================================ #!/bin/bash basics=" -w /home/j/.minecraft/saves/World1" prefix='./images/areaslice' threads=2 for i in $(seq -150 -20) do imgfile="$prefix$i.png" let j=i-2 echo "Rendering $i..." ./c10t $basics -o $imgfile -L $j,$i,-100,150 -q -s & # Wait for threads to finish... let mod=$i%$threads if [ $mod = 0 ]; then wait fi done echo Trimming... # Make all images the same size (assuming last image largest) mogrify -bordercolor white -border 1x1 -trim $prefix*.png newsize=$(identify -format '%wx130' $imgfile) mogrify -gravity south -extent $newsize -background white +repage $prefix*.png #Animate into a gif... echo Animating... sleep 1 convert -delay 20 -loop -1 $prefix*.png $prefix-animated.gif ================================================ FILE: scripts/generate-tests.sh ================================================ #!/bin/bash set -e world=$1 dir=$2 C10T=./c10t C10T_ARGS="-w $world -L -70,-60,5,20" if [[ ! -d $world ]]; then echo "Not a directory: $world" exit 1; fi if [[ ! -d $dir ]]; then echo "Not a directory: $dir" exit 1; fi echo_palette() { cat << END Wood 0,255,0 Stone 0,0,0,20 Leaves 0,0,255,20 END } echo_html() { cat << END c10t - showroom

c10t Showroom

Normal map

Oblique map

Oblique angled map

Isometric

END } echo_html > $dir/index.html motion() { $C10T $C10T_ARGS $1 -o $dir/$2.png $C10T $C10T_ARGS $1 --night -o $dir/$2-n.png $C10T $C10T_ARGS $1 --heightmap -o $dir/$2-H.png $C10T $C10T_ARGS $1 --cave-mode -o $dir/$2-C.png $C10T $C10T_ARGS $1 --no-alpha -o $dir/$2-A.png $C10T $C10T_ARGS $1 -P <(echo_palette;) -o $dir/$2-P.png } motion "" "n" motion "-q" "q" motion "-y" "y" motion "-z" "z" ================================================ FILE: scripts/google-api/README.txt ================================================ There has been a couple of variants of this script popping out recently, I took the liberty of consolidating them into the master branch. -- Udoprog - Nov 17 2010 === google-api.ps1 === Originally authored by Kochu """ Usage is the same as with the .sh: google-api.ps1 "world" "target" You may need to change the path to the c10t executable in the script, add its location to the PATH variable, or install it into system32. To run it, you will need to enable script execution by powershell - it is off by default. To do this, you will need to run powershell as an admin and type "set-executionpolicy unrestricted", then "y" to confirm You'll have to do it only once. Hope it works. """ -- Kochu @ http://www.minecraftforum.net/viewtopic.php?f=25&t=33803&start=570 === google-api.php === Originally authored by Rendrik """ I've ported the google-api script to PHP, mainly for use in a windows environment. I haven't tested this in Linux. It uses GD to resize the images. The script must be placed in the same directory as c10t.exe Usage is: php google-api.php [world path]* [image output directory]* [c10t args in quotes] For example: php google-api.php world\ gmap_images\ "--isometric -r 270" or php google-api.php world\ tiles\ "--oblique-angle --show-signs" Make sure you have the trailing slash for directorys. This will create the following files: map.html options.js (image dir)\(lots of images).png Just open map.html to test it out. If there is any issue, you can open the PHP file and change the VERBOSE constant to true. This will output the c10t commands and responses. """ -- Rendrik @ http://www.minecraftforum.net/viewtopic.php?f=25&t=33803&start=570 ================================================ FILE: scripts/google-api/google-api.php ================================================ .0625, # 6.25% 2048 => .125, # 12.5% 1024 => .25, # 25% 512 => .5, # 50% 256 => 1, # 100% 128 => 2 ); # 200% $zoom = array( 4096 => 0, 2048 => 1, 1024 => 2, 512 => 3, 256 => 4, 128 => 5); $tileSizes = array(0 => 4096, 1 => 2048, 2=> 1024, 3=> 512, 4 => 256, 5 => 128); # Verify world directory is valid if( !is_dir($inPath) || !file_exists($inPath."level.dat") ) { echo("Invalid world directory: ".$inPath."\r\n"); exit(); } else echo("World directory...OK\r\n"); # Verify the executable exec("c10t", $output); if( empty($output) ) { echo("Cannot find c10t.exe!\r\n"); exit(); } else echo("c10t.exe...OK\r\n"); if (extension_loaded('gd') && function_exists('gd_info')) { echo("PHP GD extension...OK\r\n"); } else { echo("GD extension is not installed or loaded!\r\nSee php.ini and be sure it contains extension=php_gd2.dll\r\n"); exit(); } # check / create output dir for images if( !is_dir($outDir) ) { mkdir($outDir); echo("Creating folder ".$outDir."\r\n"); } else echo("Folder ".$outDir." already exists.\r\n"); /** * function resizeImage * * This will use GD to resize an image. */ function resizeImage($file, $scale) { list($width, $height) = getimagesize($file); $newheight = $height * $scale; $newwidth = $newheight; if(VERBOSE) echo("Resizing " . $file . "\told(".$height.")". "\tnew(".$newheight.")" . "\tscale(" . $scale . ")\r\n"); echo("."); $src = imagecreatefrompng($file); $dst = imagecreatetruecolor($newwidth, $newheight); $img = imagecopyresampled($dst, $src, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); imagealphablending( $dst, false ); imagesavealpha( $dst, true ); imagepng( $dst, $file, 9 ); } /** * function generate * * This will execute c10t with the proper parameters. */ function generate($arg1, $name, $tile, $zoom, $scale) { $start = time(); global $inPath; global $outDir; global $c10tArgs; echo("Generating: ".$name."\t Tile Size:".$tile."\t Zoom:".$zoom."..."); # generate a set of split files $run = "c10t ".$c10tArgs." ".$arg1." --split ".$tile." -w ".$inPath." -o ".$outDir.$name.".%d.%d.".$zoom.".png --write-json ".$outDir.$name.".json"; # Uncomment for details if(VERBOSE) echo("\r\n".$run."\r\n"); exec($run, $output); if(VERBOSE) var_dump($output); $end = time(); $elapsed = $end - $start; echo("Done in ".getTimeStr($elapsed)."\r\n"); } /** * function read * * This will open a file and return it's contents. */ function read($file) { if(file_exists($file)) { $in = fopen ($file, "r"); if (!$in) return false; $raw = ""; if(filesize($file) <= 0) return false; else $raw = fread($in, filesize($file)); fclose($in); return $raw; } return false; } /** * function write * * This will write contents to a file. */ function write($file, $content) { if( empty($file) || empty($content) ) return false; $fl = fopen($file, "w+"); fputs($fl, $content); fclose($fl); return true; } /** * function getTimeStr * * Return text for how many seconds is passed in. */ function getTimeStr($durationInSeconds) { $week = floor($durationInSeconds / 86400 / 7); $day = $durationInSeconds / 86400 % 7; $hour = $durationInSeconds / 3600 % 24; $min = $durationInSeconds / 60 % 60; $sec = $durationInSeconds % 60; if($week != 0) { $time = $week . isPlural($week, " week"); if($day != 0) $time .= ", " . $day . isPlural($day, " day"); } else if($day != 0) { $time = $day . isPlural($day, " day"); if($hour != 0) $time .= ", " . $hour . isPlural($hour, " hour"); } else if($hour != 0) { $time = $hour . isPlural($hour, " hour"); if($min != 0) $time .= ", " . $min . isPlural($min, " minute"); } else if($min != 0) { $time = $min . isPlural($min, " minute"); if($sec != 0) $time .= ", " . $sec . isPlural($sec, " second"); } else if($sec != 0) { $time = $sec . isPlural($sec, " second"); } return $time; } /** * function isPlural * * Return pluralized text. */ function isPlural($num, $word){ if($num > 1) return $word."s"; else return $word; } $_start = time(); $fl = fopen($outHTML, "w+"); fputs($fl, $output); fclose($fl); # Loop and call generate for day/night and each zoom level foreach($tileSizes as $t) { $z = $zoom[$t]; $s = $scale[$t]; generate("", "day", $t, $z, $s); generate("-n", "ngt", $t, $z, $s); } # convert the files to the appropriate sizes using GD module $validExtensions = array("png"); $files = array(); if(is_dir($outDir)) { if($handle = opendir($outDir)) { while(($file = readdir($handle)) !== false) { ## List $extension = end(explode(".", $file)); if( in_array($extension, $validExtensions) ) array_push($files, $file); } closedir($handle); } echo("Resizing images"); foreach($files as $file) { $zoomLevel = substr($file, -5, 1); resizeImage($outDir.$file, $scale[$tileSizes[$zoomLevel]]); } echo(" Done.\r\n"); } else { echo($outDir." is not a valid directory... Somehow. Quitting."); exit(); } $output = '
'; echo("Writing ".$outHTML."..."); if( !write($outHTML, $output) ) { echo("Error writing ".$outHTML); exit(); } echo("Done.\r\n"); # Googlemap options JS if( !$jsonDay = read($outDir."day.json") ) { echo("Error opening day.json"); exit(); } if( !$jsonNgt = read($outDir."ngt.json") ) { echo("Error opening ngt.json"); exit(); } # '.$outDir.'\ This extra slash below is intended for escaping $js = 'var options = { host: "'.$outDir.'\", scaleControl: false, navigationControl: true, streetViewControl: false, noClear: false, backgroundColor: "#000000", isPng: true, } var modes = { \'day\': { name: "Day", alt: "Day Mode", data: '.$jsonDay.'}, \'ngt\': { name: "Night", alt: "Night Mode", data: '.$jsonNgt.'}, }'; echo("Writing options.js..."); if( !write("options.js", $js) ) { echo("Error writing options.js"); exit(); } echo("Done.\r\n"); $_end = time(); echo("Complete in ".getTimeStr($_end - $_start) ); ?> ================================================ FILE: scripts/google-api/google-api.ps1 ================================================ $CONVERT="convert" $C10T="c10t.exe" $C10T_OPTS="" $C10T_OUT="c10t.out.txt" $TILE_SIZES="4096", "2048", "1024", "512", "256", "128" $SCALE = @{"4096"="6.25%"; "2048"="12.5%"; "1024"="25%"; "512"="50%"; "256"="100%"; "128"="200%"} $ZOOM = @{"4096"="0"; "2048"="1"; "1024"="2"; "512"="3"; "256"="4"; "128"="5"} $FACTOR=16 $current= [IO.Directory]::GetCurrentDirectory() $world=$args[0] $target=$args[1] $tiles="tiles" $hostn="" $C10T_OPTS="$C10T_OPTS -w $world" if ( !($world) -or !(test-path $world -pathtype container) ) { write-host "Directory does not exist: $world"; return 1 } if ((!($C10T -match ".exe$")) -or (!(test-path $C10T -pathtype leaf)) ) { write-host "Not an executable: $C10T" return 1 } if(!(test-path $target -pathtype container)){new-item -type directory -path $target} if(!(test-path "$target\$tiles" -pathtype container)){new-item -type directory -path "$target\$tiles"} set-content -Path $target/index.html -Value @"
"@ write-host "NOTE: if something goes wrong, check out $C10T_OUT" write-host "" > $C10T_OUT function generate { Param ($x_opts, $name, $pixelsplit, $zoom, $scale) # generate a set of split files $src="$target\$tiles\$name.`%d.`%d.$zoom.png" write-host "$C10T $C10T_OPTS $x_opts --pixelsplit=$pixelsplit -o $src --write-json=$target\$name.json" "$C10T $C10T_OPTS $x_opts --pixelsplit=$pixelsplit -o $src --write-json=`"$target\$name.json`" " invoke-expression "$C10T $C10T_OPTS $x_opts --pixelsplit=$pixelsplit -o $src --write-json=`"$target\$name.json`" " # convert the files to the appropriate sizes foreach ($file in Get-ChildItem "$target\$tiles\$name.*.*.$zoom.png") { $tg=$file.FullName write-host "$CONVERT $file -scale $scale $tg" invoke-expression "$CONVERT $file -scale $scale $file" } write-host "done!" Get-ChildItem "$target\$tiles\$name.*.*.$zoom.png" } echo 1 foreach ($t in $TILE_SIZES) { $z=$ZOOM.Get_Item("$t") $s=$SCALE.Get_Item("$t") generate -x_opts " " -name "day" -pixelsplit $t -zoom $z -scale $s generate -x_opts "-n" -name "night" -pixelsplit $t -zoom $z -scale $s } set-content $target\options.js @" var options = { host: "$hostn$tiles/", scaleControl: false, navigationControl: true, streetViewControl: false, noClear: false, backgroundColor: "#000000", isPng: true, } var modes = { 'day': { name: "Day", alt: "Day Mode", data: $(cat $target/day.json)}, 'night': { name: "Night", alt: "Night Mode", data: $(cat $target/night.json)}, } "@ ================================================ FILE: scripts/google-api/google-api.sh ================================================ #!/bin/bash C10T=c10t [[ -x ./$C10T ]] && C10T=./$C10T scriptdir=$(dirname $(readlink -f $0)) world="" output="" host="" zoom=5 factor=4 base=256 res=$scriptdir opts="" exit_usage() { echo "Usage: google-api -w -o [options]" echo "-H - Host prefix to use for all scripts" echo "-z - Number of zoom levels to generate (default: 5)" echo "-b - Resolution for the base tile (default: 256px)" echo "-r - Directory to look for reasources (defaults to script directory)" echo "-O - Extra options to pass directly to c10t" exit 1 } while getopts "w:o:H:z:b:O:h" opt; do case $opt in w) world=$OPTARG ;; o) output=$OPTARG ;; H) host=$OPTARG ;; O) opts=$OPTARG ;; h) exit_usage ;; z) zoom=$OPTARG factor=$[$zoom - 1] ;; b) base=$OPTARG ;; \?) exit 1 ;; esac done shift $OPTIND [ -z $world ] && exit_usage [ -z $output ] && exit_usage [ ! -d $world ] && { echo "$0: -w: directory does not exist: $world"; exit_usage } [ ! -d $output ] && { echo "$0: -o: directory does not exist: $output"; exit_usage } [ $zoom -lt 2 ] && { echo "$0: -z: must be a numberic value greater than 2, but was '$zoom'"; exit_usage } [ $base -lt 32 ] && { echo "$0: -b: must be a numberic value greater than 32, but was '$base'"; exit_usage } if ! $C10T -h &> /dev/null; then echo "Could not find executable: $C10T" exit_usage fi google_js=$res/libc10t.google.js index_html=$res/index.html [ ! -f $google_js ] && { echo "could not find: $google_js" exit 1; } [ ! -f $index_html ] && { echo "could not find: $index_html" exit 1; } host_google_js="${host}$(basename $google_js)" host_options_js="${host}options.js" create_tile_sizes() { b=$base i=$[$zoom - 1] echo -n $[$b / 2] while [[ $i -gt 0 ]]; do echo -n " $b" b=$[$b*2] i=$[$i - 1] done } splits=$(create_tile_sizes) # this is what each tile will be resized to tiles=$output/tiles c10t_opts="$opts -w $world" if [[ -z $world ]] || [[ ! -d $world ]]; then echo "Directory does not exist: $world"; exit 1; fi [ ! -d $target ] && mkdir -p $target [ ! -d $tiles ] && mkdir -p $tiles generate() { x_opts=$1 name=$2 pixelsplit="$3" # generate a set of split files tile=$tiles/$name.%d.%d.%d.png echo "$C10T $c10t_opts $x_opts --split=\"$pixelsplit\" --split-base=$base -o $tile --write-json=$output/$name.json" if ! $C10T $c10t_opts $x_opts --split="$pixelsplit" --split-base=$base -o $tile --write-json="$output/$name.json"; then exit 1 fi echo "done!" } echo "Copying $index_html" cat $index_html | \ sed -r "s/\bC10T_GOOGLE_JS\b/$host_google_js/" | \ sed "s/\bC10T_OPTIONS_JS\b/$host_options_js/" > $output/index.html echo "Copying $google_js" cp $google_js $output generate "" "day" "$splits" generate "-n" "night" "$splits" cat > $output/options.js << ENDL var options = { factor: $factor, host: "${host}tiles/", scaleControl: false, navigationControl: true, streetViewControl: false, noClear: false, backgroundColor: "#000000", isPng: true, } var modes = { 'day': { name: "Day", alt: "Day Mode", data: $(cat $output/day.json)}, 'night': { name: "Night", alt: "Night Mode", data: $(cat $output/night.json)}, } ENDL ================================================ FILE: scripts/google-api/index.html ================================================
================================================ FILE: scripts/google-api/libc10t.google.js ================================================ function extend(t , o) { for (k in o) { if (o[k] != null) { t[k] = o[k]; } } return t; } function keys(o) { var a = []; for (m in modes) { a[a.length] = m; }; return a; } // Direct Link Feature var _globalDefaults = { lat : 0, lng : 0, zoom : 1, type : "day", } function get_parameters() { var lat = _globalDefaults.lat; var lng = _globalDefaults.lng; var zoom = _globalDefaults.zoom; var type = _globalDefaults.type; var query = location.search.substring(1); var pairs = query.split("&"); for (var i=0; i -1 ? window.location.href.substring(0,pos) : window.location.href; var ret = base + "?lat=" + lat.toFixed(6) + "&lng=" + lng.toFixed(6); if (map.getZoom() != _globalDefaults.zoom) ret += "&zoom=" + map.getZoom(); if (map.getMapTypeId() != _globalDefaults.type) ret += "&type=" + map.getMapTypeId(); return ret; } function refreshLink(map, control) { var link = retrieveLink(map); if (!(control.innerHTML.replace(/&/gi,"&") == link)) { control.innerHTML = link; } } function createLinkControl(map) { var controlDiv = document.createElement('DIV'); controlDiv.style.padding = '5px'; var controlUI = document.createElement('DIV'); controlUI.style.backgroundColor = 'white'; controlUI.style.borderStyle = 'solid'; controlUI.style.borderWidth = '1px'; controlUI.style.textAlign = 'center'; controlUI.title = 'The link to the current position'; controlDiv.appendChild(controlUI); var controlText = document.createElement('DIV'); controlText.name = "linkControl"; controlText.style.fontFamily = 'Arial,sans-serif'; controlText.style.fontSize = '12px'; controlText.style.paddingLeft = '4px'; controlText.style.paddingRight = '4px'; controlUI.appendChild(controlText); if (controlDiv.attachEvent) { controlDiv.attachEvent("onclick", function() {window.location.href = retrieveLink(map)}); } else { controlDiv.addEventListener("click", function() {window.location.href = retrieveLink(map)} , false); } map.controls[google.maps.ControlPosition.TOP_RIGHT].push(controlDiv); refreshLink(map, controlText); return controlText; } // The maximum width/height of the grid in regions (must be a power of two) var GRID_WIDTH_IN_REGIONS = 4096; // Map from a GRID_WIDTH_IN_REGIONS x GRID_WIDTH_IN_REGIONS square to Lat/Long (0, 0),(-90, 90) var SCALE_FACTOR = 90.0 / GRID_WIDTH_IN_REGIONS; // Override the default Mercator projection with Euclidean projection // (insert oblig. Flatland reference here) function EuclideanProjection() {}; EuclideanProjection.prototype.fromLatLngToPoint = function(latLng, opt_point) { var point = opt_point || new google.maps.Point(0, 0); point.x = latLng.lng() / SCALE_FACTOR; point.y = latLng.lat() / SCALE_FACTOR; return point; }; EuclideanProjection.prototype.fromPointToLatLng = function(point) { var lng = point.x * SCALE_FACTOR; var lat = point.y * SCALE_FACTOR; return new google.maps.LatLng(lat, lng, true); }; function new_map_type(m, o, ob) { var world = ob.data.world; return extend( { getTileUrl: function(c, z) { var img = o.host + m + "." + (world.split - z) + "." + c.x + "." + c.y + ".png"; return img }, isPng: true, name : "none", alt : "none", minZoom: 1, maxZoom: world.split, tileSize: new google.maps.Size(world.split_base, world.split_base) }, ob ); } function initialize(id, opt, modes) { var element = document.getElementById(id); opt = extend(opt, { mapTypeControlOptions: { mapTypeIds: keys(modes), style: google.maps.MapTypeControlStyle.DROPDOWN_MENU}}); var map = new google.maps.Map(element, opt); var firstMode = null; for (m in modes) { var imt = new google.maps.ImageMapType(new_map_type(m, opt, modes[m])); imt.projection = new EuclideanProjection(); // Now attach the grid map type to the map's registry map.mapTypes.set(m, imt); if (firstMode == null) firstMode = m; } map.setMapTypeId(firstMode); var globaldata = modes[firstMode].data; var world = globaldata.world; var factor = Math.pow(2, opt.factor); { var parameters = get_parameters(); var latlng; _globalCenter = new google.maps.Point(world.cx / factor, world.cy / factor); latlng = EuclideanProjection.prototype.fromPointToLatLng(_globalCenter) parameters.lat += latlng.lat(); parameters.lng += latlng.lng(); latlng = new google.maps.LatLng(parameters.lat, parameters.lng, true); map.setZoom(parameters.zoom); map.setMapTypeId(parameters.type); map.setCenter(latlng); } for (var i = 0; i < globaldata.markers.length; i++) { var m = globaldata.markers[i]; var point = new google.maps.Point(m.x / factor, m.y / factor); var latlng = EuclideanProjection.prototype.fromPointToLatLng(point) new google.maps.Marker({ position: latlng, map: map, title: m.text }); } var linkControl = createLinkControl(map); if (window.attachEvent) { window.attachEvent("onresize", function() {this.map.onResize()} ); window.attachEvent("onmousemove", function() {refreshLink(map, linkControl)} ); } else { window.addEventListener("resize", function() {this.map.onResize()} , false); window.addEventListener("mousemove", function() {refreshLink(map, linkControl)} , false); } } ================================================ FILE: src/2d/cube.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _CUBE_H_ #define _CUBE_H_ #include #include struct point { typedef uint64_t pos_t; pos_t x; pos_t y; pos_t z; inline point(pos_t x, pos_t y, pos_t z) : x(x), y(y), z(z) {} }; class point2 { public: typedef uint64_t pos_t; const pos_t x; const pos_t y; point2(const pos_t x, const pos_t y) : x(x), y(y) {} bool operator<(const point2& oth) const { if (y < oth.y) { return true; } if (y == oth.y && x < oth.x) { return true; } return false; } }; class top_cube { public: typedef uint64_t pos_t; top_cube(const pos_t x, const pos_t y, const pos_t z) : x(x), y(y), z(z) {} inline void project(point &p, pos_t &rx, pos_t &ry) const { rx = z - p.z - 1; ry = p.x; } inline void limits(pos_t &rx, pos_t &ry) const { rx = z; ry = x; } private: const pos_t x; const pos_t y; const pos_t z; }; class oblique_cube { public: typedef uint64_t pos_t; oblique_cube(const pos_t x, const pos_t y, const pos_t z) : x(x), y(y), z(z) {} inline void project(point &p, pos_t &rx, pos_t &ry) const { rx = z - p.z - 1; ry = p.x + (y - p.y - 1); } inline void limits(pos_t &rx, pos_t &ry) const { rx = z; ry = x + y; } private: const pos_t x; const pos_t y; const pos_t z; }; template class angle_cube { public: typedef uint64_t pos_t; angle_cube(const pos_t x, const pos_t y, const pos_t z) : x(x), y(y), z(z) {} inline void project(point &p, pos_t &rx, pos_t &ry) const { rx = F1 * ((z - p.z - 1) + p.x); ry = F2 * (y - p.y - 1) + F3 * p.z + F3 * p.x; } inline void limits(pos_t &rx, pos_t &ry) const { rx = F1 * (z + x); ry = F2 * y + F3 * z + F3 * x; } private: const pos_t x; const pos_t y; const pos_t z; }; #endif /* _CUBE_H_ */ ================================================ FILE: src/algorithm.cpp ================================================ #include #include #include namespace nonstd { // hackish split } ================================================ FILE: src/algorithm.hpp ================================================ #ifndef NONSTD_ALGORITHM #define NONSTD_ALGORITHM #include #include namespace nonstd { template class reporting { public: virtual void add(T) = 0; virtual void done(T) = 0; }; template class continious : public reporting { public: const static uintmax_t LINE_WIDTH = 30; typedef void (*progress_func)(O&, T); typedef void (*endline_func)(O&, T); continious(O& out, T progress_threshold, progress_func progress_f, endline_func endline_f) : out(out), chunks(0), total(0), progress_threshold(progress_threshold), line(0), progress_f(progress_f), endline_f(endline_f) { } virtual void add(T parts) { chunks += parts; while (chunks > progress_threshold) { chunks -= progress_threshold; total += progress_threshold; progress_f(out, total); if (!(line++ < LINE_WIDTH)) { endline_f(out, total); line = 0; } } } void done(T last_part) { total += chunks + last_part; endline_f(out, total); } private: O& out; T chunks; T total; T progress_threshold; unsigned int line; progress_func progress_f; endline_func endline_f; }; template class limited : public reporting { public: const static uintmax_t LINE_WIDTH = 30; typedef void (*progress_func)(O&, T); typedef void (*endline_func)(O&, T, T); limited(O& out, T progress_threshold, T total_limit, progress_func progress_f, endline_func endline_f) : out(out), chunks(0), total(0), progress_threshold(progress_threshold), line(0), progress_f(progress_f), endline_f(endline_f), total_limit(total_limit) { } virtual void add(T parts) { chunks += parts; while (chunks > progress_threshold) { chunks -= progress_threshold; total += progress_threshold; progress_f(out, total); if (line++ >= LINE_WIDTH) { endline_f(out, total, total_limit); line = 0; } } } void done(T last) { total += last; endline_f(out, total_limit, total_limit); } private: O& out; T chunks; T total; T progress_threshold; unsigned int line; progress_func progress_f; endline_func endline_f; T total_limit; }; } #endif /* NONSTD_ALGORITHM */ ================================================ FILE: src/altitude_graph.cpp ================================================ #include "altitude_graph.hpp" #include "text.hpp" using namespace std; namespace fs = boost::filesystem; #define BORDER_X 50 #define BORDER_Y 50 AltitudeGraph::AltitudeGraph(settings_t& _s) : s(_s), width(800), height(600) { altitudeRegistry.reset(new long[mc::MapY]); for(int i = 0; i < mc::MapY; i++) { altitudeRegistry[i] = 0; } } void AltitudeGraph::createGraph() { image_ptr graphImg; memory_image* image = new memory_image(width, height); graphImg.reset(image); color bgcolor(255, 255, 255, 255); color fgcolor(150,0,0,255); color axiscolor(0,0,0,255); color seacolor(0,0,255,255); text::font_face ffsea(s.ttf_path, 8, seacolor); ffsea.init(); text::font_face ff12(s.ttf_path, 12, axiscolor); ff12.init(); // fill background graphImg->fill(bgcolor); int _w = width - BORDER_X; int _h = height - BORDER_Y; long maxVal = this->getMax(); int x_step = _w / mc::MapY; std::stringstream maxss; maxss << "MAX = " << maxVal; ff12.draw(graphImg, "NB of blocks", BORDER_X - 30 , BORDER_Y - 25); ff12.draw(graphImg, maxss.str(), BORDER_X - 20 , BORDER_Y - 10); ff12.draw(graphImg, "Altitude", _w - 2*BORDER_X , _h + 17); int x=0, y=0, x0=BORDER_X, y0=_h; for(int i = 0; i < mc::MapY; i++) { x = BORDER_X + x_step*i; if (maxVal == 0) { y = _h; } else { y = _h - (int)( ( (float)altitudeRegistry[i] / (float)maxVal ) * (_h-BORDER_Y) ); } graphImg->draw_line(x, y, x0, y0, fgcolor); x0 = x; y0 = y; } // draw axis graphImg->draw_line(BORDER_X, BORDER_Y, BORDER_X, _h, axiscolor); graphImg->draw_line(BORDER_X, _h, _w, _h, axiscolor); // draw axis labels for(int i=0; i < mc::MapY; i++) { color _axiscolor = axiscolor; x = BORDER_X + x_step*i; int size = 2; if(i == 63) { _axiscolor = seacolor; ffsea.draw(graphImg, "Sea", x+4, _h+25); size = 25; } if(i%10 == 0) { size = 5; } graphImg->draw_line(x, _h, x, _h+size, _axiscolor); } png_format::opt_type opts; graphImg->save(s.statistics_path.string() + "_graph.png", opts); } void AltitudeGraph::registerBloc(mc::MaterialT *material, int altitude) { altitudeRegistry[altitude] += 1; } long AltitudeGraph::getMax() { long max = 0; for(int i = 0; i < mc::MapY; i++) { if(max < altitudeRegistry[i]) max = altitudeRegistry[i]; } return max; } ================================================ FILE: src/altitude_graph.hpp ================================================ #ifndef STATISTICS_HPP #define STATISTICS_HPP // Include this first, to evade setjmp header bug #include "image/format/png.hpp" #include #include #include #include #include #include "settings_t.hpp" #include "players.hpp" #include "image/image_base.hpp" #include "image/memory_image.hpp" #include "image/cached_image.hpp" #include "image/algorithms.hpp" #include "image/format/png.hpp" #include "mc/world.hpp" #include "mc/blocks.hpp" #include "mc/utils.hpp" #include "nbt/nbt.hpp" class AltitudeGraph { public: AltitudeGraph(settings_t& _s); void createGraph(); /* call this to register block information */ void registerBloc(mc::MaterialT *material, int altitude); long getMax(); private: settings_t s; int width; int height; boost::scoped_array altitudeRegistry; }; #endif // STATISTICS_HPP ================================================ FILE: src/cache.hpp ================================================ #ifndef _CACHE_H_ #define _CACHE_H_ #include #include #include #include #include "image/image_operations.hpp" namespace fs = boost::filesystem; #define CACHE_MAGIC "CMap" struct cache_hdr { bool compressed; std::time_t mod; size_t max_x, max_y; size_t size; }; class cache_file { private: const fs::path cache_dir; const uint32_t source_write_time; const bool cache_compress; const fs::path cache_path; typedef std::vector::size_type v_size_type; public: cache_file(const fs::path cache_dir, std::string basename, const uint32_t source_write_time, bool cache_compress) : cache_dir(cache_dir), source_write_time(source_write_time), cache_compress(cache_compress), cache_path(cache_dir / basename) { } bool create_directories() { return fs::create_directories(cache_dir); } bool exists() { return fs::is_regular_file(cache_path) && fs::last_write_time(cache_path) >= source_write_time; } void clear() { fs::remove(cache_path); } bool gzreadall(gzFile gzf, char* buf, unsigned int len) { unsigned int read = 0; while (read < len) { int have = gzread(gzf, buf + read, len - read); if (have == 0) { int errnum; const char* errorstr = gzerror(gzf, &errnum); if (errnum != 0) { std::cerr << errorstr << std::endl; gzclose(gzf); return false; } if (gzeof(gzf)) { gzclose(gzf); return false; } } if (have < 0) { gzclose(gzf); return false; } read += have; } return true; } bool gzwriteall(gzFile gzf, const char* buf, unsigned int len) { unsigned int written = 0; while (written < len) { int have = gzwrite(gzf, buf + written, len - written); if (have <= 0) { int errnum; const char* errorstr = gzerror(gzf, &errnum); if (errnum != 0) { std::cerr << errorstr << std::endl; gzclose(gzf); return false; } gzclose(gzf); return false; } written += have; } return true; } bool read(boost::shared_ptr oper) { gzFile gzf = gzopen(cache_path.string().c_str(), "r"); if (gzf == Z_NULL) { return false; } //std::ifstream fs(cache_path.string().c_str()); cache_hdr hdr; { char m[4]; //fs.read(m, 4); //if (fs.fail()) return false; if (!gzreadall(gzf, m, 4)) { return false; } if ( m[0] != CACHE_MAGIC[0] || m[1] != CACHE_MAGIC[1] || m[2] != CACHE_MAGIC[2] || m[3] != CACHE_MAGIC[3] ) return false; //fs.read(reinterpret_cast(&hdr), sizeof(cache_hdr)); //if (fs.fail()) return false; if (!gzreadall(gzf, reinterpret_cast(&hdr), sizeof(cache_hdr))) { return false; } } if (hdr.compressed != cache_compress) return false; if (hdr.mod != source_write_time) return false; oper->max_x = hdr.max_x; oper->max_y = hdr.max_y; oper->operations.resize(hdr.size); //fs.read(reinterpret_cast(&(oper->operations.front())), sizeof(image_operation) * hdr.size); //if (fs.fail()) return false; if (!gzreadall(gzf, reinterpret_cast(&(oper->operations.front())), sizeof(image_operation) * hdr.size)) { return false; } gzclose(gzf); return true; } bool write(boost::shared_ptr oper) { //std::ofstream fs(cache_path.string().c_str()); gzFile gzf = gzopen(cache_path.string().c_str(), "w"); if (gzf == Z_NULL) { return false; } cache_hdr hdr; { //fs.write(CACHE_MAGIC, 4); //if (fs.fail()) return false; if (!gzwriteall(gzf, CACHE_MAGIC, 4)) { return false; } hdr.compressed = cache_compress; hdr.max_x = oper->max_x; hdr.max_y = oper->max_y; hdr.mod = source_write_time; hdr.size = oper->operations.size(); //fs.write(reinterpret_cast(&hdr), sizeof(cache_hdr)); //if (fs.fail()) return false; if (!gzwriteall(gzf, reinterpret_cast(&hdr), sizeof(cache_hdr))) { return false; } } //fs.write(reinterpret_cast(&(oper->operations.front())), sizeof(image_operation) * hdr.size); //if (fs.fail()) return false; if (!gzwriteall(gzf, reinterpret_cast(&(oper->operations.front())), sizeof(image_operation) * hdr.size)) { return false; } gzclose(gzf); return true; } }; #endif /* _CACHE_H_ */ ================================================ FILE: src/dirlist.cpp ================================================ #include "dirlist.hpp" #include "fileutils.hpp" dirlist::dirlist(const fs::path path) { if (fs::is_directory(path)) directories.push(path); } bool dirlist::has_next(dir_filter_func dir_filter, file_filter_func file_filter) { if (!files.empty()) { return true; } if (directories.empty()) { return false; } // work until you find any files while (!directories.empty()) { fs::path dir_path = directories.front(); directories.pop(); if (!fs::is_directory(dir_path)) { continue; } fs::directory_iterator end_itr; for ( fs::directory_iterator itr(dir_path); itr != end_itr; ++itr ) { if (fs::is_directory(itr->status())) { if (!dir_filter(itr->path().stem().string())) { continue; } directories.push(itr->path()); } else if (fs::is_regular_file(itr->status())) { if (!file_filter(path_string(itr->path().filename()))) { continue; } files.push(itr->path()); } } } return !files.empty(); } fs::path dirlist::next() { fs::path next = files.front(); files.pop(); return next; } ================================================ FILE: src/dirlist.hpp ================================================ #ifndef _DIRLIST_HPP #define _DIRLIST_HPP #include #include #include namespace fs = boost::filesystem; class dirlist { public: typedef bool (dir_filter_func)(const std::string&); typedef bool (file_filter_func)(const std::string&); dirlist(const fs::path path); bool has_next(dir_filter_func, file_filter_func); fs::path next(); private: std::queue directories; std::queue files; }; #endif /* _DIRLIST_HPP */ ================================================ FILE: src/dlopen.cpp ================================================ #include "dlopen.hpp" /** * Concept shamelessly stolen from; * http://www.codeproject.com/KB/architecture/plat_ind_coding.aspx */ #include #if defined(_MSC_VER) # include #elif defined(__GNUC__) # include #else #error Unknown compiler #endif struct dl_t { #if defined(_MSC_VER) HINSTANCE impl; #elif defined(__GNUC__) void* impl; #endif }; # if defined(_MSC_VER) #define EXT ".dll" #define EXTL 4 # elif defined(__GNUC__) #define EXT ".so" #define EXTL 3 # endif dl_t* dl_open(const char *name) { std::string fullname = std::string(name); void* impl; if (fullname.substr(fullname.size() - EXTL).compare(EXT) != 0) { fullname += EXT; } # if defined(_MSC_VER) impl = LoadLibrary(fullname); # elif defined(__GNUC__) impl = dlopen(fullname.c_str(), RTLD_LAZY); # endif if (impl == NULL) { return NULL; } dl_t* dl = new dl_t; dl->impl = impl; return dl; } void* dl_sym(dl_t* dl, const char *sym) { #if defined(_MSC_VER) return (void*)GetProcAddress(dl->impl, sym); #elif defined(__GNUC__) return dlsym(dl->impl, sym); #endif } bool dl_close(dl_t* dl) { bool ok; #if defined(_MSC_VER) ok = FreeLibrary(dl->impl); #elif defined(__GNUC__) ok = dlclose(dl->impl); #endif delete dl; return ok; } ================================================ FILE: src/dlopen.hpp ================================================ #ifndef _DLOPEN_HPP #define _DLOPEN_HPP struct dl_t; dl_t* dl_open(const char*); void* dl_sym(dl_t*, const char*); bool dl_close(dl_t*); #endif /* _DLOPEN_HPP */ ================================================ FILE: src/engine/CMakeLists.txt ================================================ add_library( c10t-engine isometric_base.cpp flat_base.cpp topdown_engine.cpp oblique_engine.cpp obliqueangle_engine.cpp isometric_engine.cpp fatiso_engine.cpp functions.cpp block_rotation.cpp ) set_target_properties(c10t-engine PROPERTIES COMPILE_FLAGS "-O3 -Wall -pedantic -g") ================================================ FILE: src/engine/block_rotation.cpp ================================================ #include "engine/block_rotation.hpp" block_rotation::block_rotation( int rotation, boost::shared_ptr array) : x(0), z(0), rotation(rotation), array(array) { } void block_rotation::set_xz(int x, int z) { transform_xz(x, z); this->x = x; this->z = z; } void block_rotation::transform_xz(int& x, int& z) { int t = x; switch (rotation) { case 270: x = 15 - z; z = t; break; case 180: z = 15 - z; x = 15 - x; break; case 90: x = z; z = 15 - t; break; }; } /** */ int block_rotation::get8(int y, int d) { int p = ((y * 16 + z) * 16 + x); if (!(p >= 0 && p < array->length)) return d; return array->values[p] & 0xff; } /** * Data values are packed two by two and the position LSB decides which * half-byte contains the requested block data value. */ int block_rotation::get4(int y, int d) { int tmp = (y * 16 + z) * 16 + x; int p = tmp >> 1; int b = tmp & 0x1; if (!(p >= 0 && p < array->length)) return d; return (array->values[p] >> (b * 4)) & 0xf; } ================================================ FILE: src/engine/block_rotation.hpp ================================================ #ifndef _ENGINE_BLOCK_ROTATION_HPP #define _ENGINE_BLOCK_ROTATION_HPP #include #include #include "nbt/types.hpp" #include "mc/blocks.hpp" class block_rotation { public: block_rotation(int rotation, boost::shared_ptr array); void set_xz(int x, int z); void transform_xz(int& x, int& z); int get8(int y, int d=-1); int get4(int y, int d=-1); private: int x, z; int rotation; boost::shared_ptr array; }; #endif /* _ENGINE_BLOCK_ROTATION_HPP */ ================================================ FILE: src/engine/engine_base.hpp ================================================ #ifndef _ENGINE_ENGINE_BASE_HPP #define _ENGINE_ENGINE_BASE_HPP #include "engine/engine_settings.hpp" #include "engine/engine_core.hpp" #include "2d/cube.hpp" #include "mc/level.hpp" #include "mc/world.hpp" #include "mc/blocks.hpp" #include "image/image_operations.hpp" template class engine_base : public engine_core { public: engine_base(engine_settings engine_s, mc::world& world) : part_c(mc::MapX + 1, mc::MapY + 1, mc::MapZ + 1), pos_c(world.diff_x * mc::MapX, mc::MapY, world.diff_z * mc::MapZ), mpos_c((world.diff_x + 1) * mc::MapX, mc::MapY, (world.diff_z + 1) * mc::MapZ), engine_s(engine_s), world(world) { } void project_limits(pos_t& image_width, pos_t& image_height) { part_c.limits(image_width, image_height); } void project_position(point& p, pos_t& image_x, pos_t& image_y) { part_c.project(p, image_x, image_y); } void get_boundaries(pos_t& width, pos_t& height) { mpos_c.limits(width, height); } void get_level_boundaries(pos_t& width, pos_t& height) { part_c.limits(width, height); } void w2pt(int xPos, int zPos, pos_t& x, pos_t& y) { pos_t posx = xPos - get_world().min_x; pos_t posz = zPos - get_world().min_z; point pos(posx * mc::MapX, mc::MapY, posz * mc::MapZ); pos_c.project(pos, x, y); } void wp2pt(int xPos, int yPos, int zPos, pos_t& x, pos_t& y) { point pos(xPos - get_world().min_xp, yPos, zPos - get_world().min_zp); mpos_c.project(pos, x, y); x -= im_min_x; y -= im_min_y; } void reset_image_limits() { im_min_x = std::numeric_limits::max(); im_min_y = std::numeric_limits::max(); im_max_x = std::numeric_limits::min(); im_max_y = std::numeric_limits::min(); } void update_image_limits(pos_t x, pos_t y, pos_t max_x, pos_t max_y) { im_min_x = std::min(im_min_x, x); im_min_y = std::min(im_min_y, y); im_max_x = std::max(im_max_x, max_x); im_max_y = std::max(im_max_y, max_y); }; const engine_settings& get_settings() { return engine_s; } const mc::world& get_world() { return world; } pos_t get_min_x() { return im_min_x; } pos_t get_max_x() { return im_max_x; } pos_t get_min_y() { return im_min_y; } pos_t get_max_y() { return im_max_y; } private: const C part_c; const C pos_c; const C mpos_c; const engine_settings engine_s; const mc::world& world; pos_t im_min_x; pos_t im_min_y; pos_t im_max_x; pos_t im_max_y; }; #endif /* _ENGINE_ENGINE_BASE_HPP */ ================================================ FILE: src/engine/engine_core.hpp ================================================ #ifndef _ENGINE_ENGINE_CORE_HPP #define _ENGINE_ENGINE_CORE_HPP #include #include #include "mc/level.hpp" #include "image/image_operations.hpp" class engine_core; typedef boost::shared_ptr engine_core_ptr; class engine_core { public: typedef uint64_t pos_t; typedef boost::shared_ptr level_ptr; typedef boost::shared_ptr image_operations_ptr; virtual void get_boundaries(pos_t& width, pos_t& height) = 0; virtual void get_level_boundaries(pos_t& width, pos_t& height) = 0; virtual void w2pt(int xPos, int zPos, pos_t& x, pos_t& y) = 0; virtual void wp2pt(int xPos, int yPos, int zPos, pos_t& x, pos_t& y) = 0; virtual void reset_image_limits() = 0; virtual void update_image_limits(pos_t x, pos_t y, pos_t max_x, pos_t max_y) = 0; virtual void render(level_ptr, image_operations_ptr) = 0; virtual pos_t get_min_x() = 0; virtual pos_t get_max_x() = 0; virtual pos_t get_min_y() = 0; virtual pos_t get_max_y() = 0; }; #endif /* _ENGINE_ENGINE_CORE_HPP */ ================================================ FILE: src/engine/engine_settings.hpp ================================================ #ifndef _ENGINE_ENGINE_SETTINGS_HPP #define _ENGINE_ENGINE_SETTINGS_HPP #include struct engine_settings { int rotation; bool night; bool heightmap; bool striped_terrain; bool hellmode; bool cavemode; int top; int bottom; }; #endif /* _ENGINE_ENGINE_SETTINGS_HPP */ ================================================ FILE: src/engine/fatiso_engine.cpp ================================================ #include "engine/fatiso_engine.hpp" fatiso_engine::fatiso_engine( engine_settings& s, mc::world& world ) : isometric_base(s, world) { } void fatiso_engine::render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { color topdark(top); color toplight(top); color sidelight(side); if (bt == mc::LegacyBlocks::Grass) { topdark.darken(0x20); toplight.darken(0x10); sidelight.lighten(0x20); } else { toplight = color(side); topdark = color(side); } o->add_pixel(px + 0, py + 0, side); o->add_pixel(px + 0, py + 1, side); o->add_pixel(px + 0, py + 2, side); o->add_pixel(px + 1, py - 1, side); o->add_pixel(px + 1, py + 0, side); o->add_pixel(px + 1, py + 1, side); o->add_pixel(px + 2, py - 1, side); o->add_pixel(px + 2, py + 0, side); o->add_pixel(px + 2, py + 1, side); o->add_pixel(px + 3, py - 2, side); o->add_pixel(px + 3, py - 1, side); o->add_pixel(px + 3, py + 0, side); o->add_pixel(px - 1, py + 0, sidelight); o->add_pixel(px - 1, py + 1, sidelight); o->add_pixel(px - 1, py + 2, sidelight); o->add_pixel(px - 2, py - 1, sidelight); o->add_pixel(px - 2, py + 0, sidelight); o->add_pixel(px - 2, py + 1, sidelight); o->add_pixel(px - 3, py - 1, sidelight); o->add_pixel(px - 3, py + 0, sidelight); o->add_pixel(px - 3, py + 1, sidelight); o->add_pixel(px - 4, py - 2, sidelight); o->add_pixel(px - 4, py - 1, sidelight); o->add_pixel(px - 4, py + 0, sidelight); o->add_pixel(px + 0, py - 2, topdark); o->add_pixel(px + 0, py - 1, topdark); o->add_pixel(px + 1, py - 3, topdark); o->add_pixel(px + 1, py - 2, topdark); o->add_pixel(px + 2, py - 3, topdark); o->add_pixel(px + 2, py - 2, topdark); o->add_pixel(px + 3, py - 4, topdark); o->add_pixel(px + 3, py - 3, topdark); o->add_pixel(px - 1, py - 2, toplight); o->add_pixel(px - 1, py - 1, toplight); o->add_pixel(px - 2, py - 3, toplight); o->add_pixel(px - 2, py - 2, toplight); o->add_pixel(px - 3, py - 3, toplight); o->add_pixel(px - 3, py - 2, toplight); o->add_pixel(px - 4, py - 4, toplight); o->add_pixel(px - 4, py - 3, toplight); o->add_pixel(px - 3, py - 5, top); o->add_pixel(px - 3, py - 4, top); o->add_pixel(px - 2, py - 5, top); o->add_pixel(px - 2, py - 4, top); o->add_pixel(px - 1, py - 6, top); o->add_pixel(px - 1, py - 5, top); o->add_pixel(px - 1, py - 4, top); o->add_pixel(px - 1, py - 3, top); o->add_pixel(px + 0, py - 6, top); o->add_pixel(px + 0, py - 5, top); o->add_pixel(px + 0, py - 4, top); o->add_pixel(px + 0, py - 3, top); o->add_pixel(px + 1, py - 5, top); o->add_pixel(px + 1, py - 4, top); o->add_pixel(px + 2, py - 5, top); o->add_pixel(px + 2, py - 4, top); } void fatiso_engine::render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { render_block(o, bt, px, py, top, side); } void fatiso_engine::render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { render_block(o, bt, px, py, top, side); } ================================================ FILE: src/engine/fatiso_engine.hpp ================================================ #ifndef FATISO_ENGINE #define FATISO_ENGINE #include "engine/isometric_base.hpp" typedef angle_cube<4,5,2> fatiso_cube; class fatiso_engine : public isometric_base { public: fatiso_engine(engine_settings& s, mc::world& world); void project_limits( pos_t& image_width, pos_t& image_height ); void project_position( point& p, pos_t& image_x, pos_t& image_y ); void project_world_limits( pos_t& image_width, pos_t& image_height ); void project_world_position( point& p, pos_t& image_x, pos_t& image_y ); void render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); }; #endif /* FATISO_ENGINE */ ================================================ FILE: src/engine/flat_base.cpp ================================================ #include "engine/flat_base.hpp" ================================================ FILE: src/engine/flat_base.hpp ================================================ #ifndef _ENGINE_FLAT_BASE_HPP #define _ENGINE_FLAT_BASE_HPP #include "engine/engine_base.hpp" #include "engine/block_rotation.hpp" #include "engine/functions.hpp" #include template class flat_base : public engine_base { public: typedef uint64_t pos_t; typedef boost::shared_ptr level_ptr; typedef boost::shared_ptr image_operations_ptr; flat_base(engine_settings& s, mc::world& world) : engine_base(s, world) { } void render(level_ptr level, image_operations_ptr oper) { const engine_settings& s = flat_base::get_settings(); pos_t iw, ih; flat_base::get_level_boundaries(iw, ih); oper->set_limits(iw, ih); boost::shared_ptr L = level->get_level(); bool* blocked = new bool[iw*ih]; for (unsigned int i = 0; i < iw*ih; i++) { blocked[i] = false; } // block type BOOST_REVERSE_FOREACH(mc::Section_Compound Section, L->Sections) { block_rotation br_blocks(s.rotation, Section.Blocks); block_rotation br_data(s.rotation, Section.Data); //block_rotation br_block_light(s.rotation, Section.BlockLight); //block_rotation br_sky_light(s.rotation, Section.SkyLight); for (int y = 15; y >= 0; y--) { int abs_y = (Section.Y * 16) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = 0; x < mc::MapX; x++) { unsigned int blocked_position = x * iw + z; if (blocked[blocked_position]) { continue; } br_blocks.set_xz(x, z); br_data.set_xz(x, z); //br_block_light.set_xz(x, z); //br_sky_light.set_xz(x, z); // do incremental color fill until color is opaque int block_type = br_blocks.get8(y); int block_data = br_data.get4(y); mc::MaterialMode mode; color top; color side; boost::optional material = mc::get_material_legacy(block_type, block_data); if (material) { mc::MaterialT *m = material.get(); if (!m->enabled) { continue; } mode = m->mode; top = m->top; side = m->side; } else { mode = mc::MaterialMode::Block; top = mc::SharedDefaultColor; side = mc::SharedDefaultColor; } blocked[blocked_position] = top.is_opaque(); /*int block_light = br_block_light.get4(y + 1); int sky_light = br_sky_light.get4(y + 1, 15);*/ //apply_shading(s, block_light, sky_light, 0, abs_y, top); point p(x, abs_y, z); pos_t px; pos_t py; flat_base::project_position(p, px, py); int log_rotation; switch(mode) { case mc::MaterialMode::Block: render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::HalfBlock: render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::TorchBlock: render_torchblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LargeFlowerBlock: // Check if the requested block is the top block if(block_data & 0x08) { // Small sanity check if(y > 0 && br_blocks.get8(y-1) == block_type) { // Minecraft currently doesn't set the lower bits to the // corresponding type so we have to do this here. block_data = br_data.get4(y-1) & 0x07; top = mc::get_color_legacy(block_type, block_data); side = mc::get_side_color_legacy(block_type, block_data); } else { // Top block not placed on a correct bottom block. // The expected LargeFlower multi block structure is invalid, skip it. continue; } } else { // Force the top of the lower block to also be the side color. top = side; } render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LogBlock: // Log blocks are just a regular block that may differ in orientation. Top // color is considered the inner material. Because some bits of metadata are // used both for variant and rotation state, block type needs to be fetched again. log_rotation = (block_data & 0x0C) >> 2; switch(log_rotation) { case 0: // Up/down top = mc::get_color_legacy(block_type, block_data & 0x3); side = mc::get_side_color_legacy(block_type, block_data & 0x3); break; case 1: // East/west case 2: // North/south // TODO: Actually implement render rotation, for now simply swap top and side. side = mc::get_color_legacy(block_type, block_data & 0x3); top = mc::get_side_color_legacy(block_type, block_data & 0x3); break; case 3: // Only sides, thus no top color. side = mc::get_side_color_legacy(block_type, block_data & 0x3); top = side; break; } render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LegacySlab: // Legacy slab is just a half block; but is sometimes a full block. // The first legacy id is the full block version. if (block_type == material.get()->legacy_ids[0]) { render_block(oper, block_type, px, py, top, side); } render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LegacyLeaves: // Legacy leaves is just a regular block; however some bits of the metadata // are used for other block states, only the two first bits are used for block // type therefore we need to re-fetch the block type now. top = mc::get_color_legacy(block_type, block_data & 0x3); side = mc::get_side_color_legacy(block_type, block_data & 0x3); render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; } } } } } delete [] blocked; oper->reverse(); } virtual void render_block(image_operations_ptr, int, pos_t, pos_t, color, color) = 0; virtual void render_halfblock(image_operations_ptr, int, pos_t, pos_t, color, color) = 0; virtual void render_torchblock(image_operations_ptr, int, pos_t, pos_t, color, color) = 0; }; #endif /* _ENGINE_FLAT_BASE_HPP */ ================================================ FILE: src/engine/functions.cpp ================================================ #include "engine/functions.hpp" void apply_shading( const engine_settings& s, int block_light, int sky_light, int height_map, int y, color &c) { if(s.night) { c.darken(0x6 * (16 - block_light)); } else if (sky_light != -1 && y != s.top) { c.darken(0x6 * (16 - std::max(sky_light, block_light))); } //c.darken((mc::MapY - y)); // in heightmap mode, brightness = height if (s.heightmap) { c.b = y*2; c.g = y*2; c.r = y*2; c.a = 0xff; } else if (s.striped_terrain && y % 2 == 0) { c.darken(0xf); } } ================================================ FILE: src/engine/functions.hpp ================================================ #ifndef _ENGINE_FUNCTIONS_HPP #define _ENGINE_FUNCTIONS_HPP #include "engine/block_rotation.hpp" #include "engine/engine_settings.hpp" #include "image/color.hpp" #include "mc/blocks.hpp" /** * Shared functions between engines. */ /** * Apply shading to specified block and color. **/ void apply_shading( const engine_settings& engine_s, int bl, int sl, int hm, int y, color &c); inline bool is_open(mc::MaterialT*& m) { if (!m) { return false; } return m->top.is_transparent(); } inline bool is_open(int bt) { if (bt == -1) { return false; } switch(bt) { case mc::LegacyBlocks::Air: return true; case mc::LegacyBlocks::Leaves: return true; default: return false; } } // this functions is currently unused inline bool cave_ignore_block(int y, int bt, block_rotation& b_r, bool &cave_initial) { if (cave_initial) { if (!is_open(bt)) { cave_initial = false; return true; } return true; } if (!is_open(bt) && is_open(b_r.get8(y + 1))) { return false; } return true; } // this functions is currently unused inline bool hell_ignore_block(int y, int bt, block_rotation& b_r, bool &hell_initial) { if (hell_initial) { if (is_open(bt)) { hell_initial = false; return false; } return true; } return false; } #endif /* _ENGINE_FUNCTIONS_HPP */ ================================================ FILE: src/engine/isometric_base.cpp ================================================ #include "engine/isometric_base.hpp" ================================================ FILE: src/engine/isometric_base.hpp ================================================ #ifndef _ENGINE_ISOMETRIC_BASE_HPP #define _ENGINE_ISOMETRIC_BASE_HPP #include "engine/engine_base.hpp" #include "engine/block_rotation.hpp" #include "engine/functions.hpp" #include template class isometric_base : public engine_base { public: typedef uint64_t pos_t; typedef boost::shared_ptr level_ptr; typedef boost::shared_ptr image_operations_ptr; isometric_base(engine_settings& s, mc::world& world) : engine_base(s, world) { } void render(level_ptr level, image_operations_ptr oper) { const engine_settings& s = engine_base::get_settings(); boost::shared_ptr L = level->get_level(); pos_t image_width = 0; pos_t image_height = 0; engine_base::project_limits(image_width, image_height); oper->set_limits(image_width + 1, image_height); BOOST_FOREACH(mc::Section_Compound Section, L->Sections) { block_rotation br_blocks(s.rotation, Section.Blocks); block_rotation br_data(s.rotation, Section.Data); //block_rotation br_block_light(s.rotation, Section.BlockLight); //block_rotation br_sky_light(s.rotation, Section.SkyLight); for (int y = 0; y < 16; y++) { int abs_y = (16 * Section.Y) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = mc::MapX - 1; x >= 0; x--) { br_blocks.set_xz(x, z); br_data.set_xz(x, z); //br_block_light.set_xz(x, z); //br_sky_light.set_xz(x, z); int block_type = br_blocks.get8(y); point p(x, abs_y, z); pos_t px = 0; pos_t py = 0; engine_base::project_position(p, px, py); int block_data = br_data.get4(y); mc::MaterialMode mode; color top; color side; boost::optional material = mc::get_material_legacy(block_type, block_data); if (material) { mc::MaterialT *m = material.get(); if (!m->enabled) { continue; } mode = m->mode; top = m->top; side = m->side; } else { mode = mc::MaterialMode::Block; top = mc::SharedDefaultColor; side = mc::SharedDefaultColor; } int log_rotation; //int block_light = br_block_light.get4(y + 1); //int sky_light = br_sky_light.get4(y + 1); //apply_shading(s, block_light, sky_light, 0, y, top); //apply_shading(s, 0, 0, 0, y, side); switch(mode) { case mc::MaterialMode::Block: render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::HalfBlock: render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::TorchBlock: render_torchblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LargeFlowerBlock: // Check if the requested block is the top block if(block_data & 0x08) { // Small sanity check if(y > 0 && br_blocks.get8(y-1) == block_type) { // Minecraft currently doesn't set the lower bits to the // corresponding type so we have to do this here. block_data = br_data.get4(y-1) & 0x07; top = mc::get_color_legacy(block_type, block_data); side = mc::get_side_color_legacy(block_type, block_data); } else { // Top block not placed on a correct bottom block. // The expected LargeFlower multi block structure is invalid, skip it. continue; } } else { // Force the top of the lower block to also be the side color. top = side; } render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LogBlock: // Log blocks are just a regular block that may differ in orientation. Top // color is considered the inner material. Because some bits of metadata are // used both for variant and rotation state, block type needs to be fetched again. log_rotation = (block_data & 0x0C) >> 2; switch(log_rotation) { case 0: // Up/down top = mc::get_color_legacy(block_type, block_data & 0x3); side = mc::get_side_color_legacy(block_type, block_data & 0x3); break; case 1: // East/west case 2: // North/south // TODO: Actually implement render rotation, for now simply swap top and side. side = mc::get_color_legacy(block_type, block_data & 0x3); top = mc::get_side_color_legacy(block_type, block_data & 0x3); break; case 3: // Only sides, thus no top color. side = mc::get_side_color_legacy(block_type, block_data & 0x3); top = side; break; } render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LegacySlab: // Legacy slab is just a half block; but is sometimes a full block. // The first legacy id is the full block version. if (block_type == material.get()->legacy_ids[0]) { render_block(oper, block_type, px, py, top, side); } render_halfblock(oper, block_type, px, py, top, side); break; case mc::MaterialMode::LegacyLeaves: // Legacy leaves is just a regular block; however some bits of the metadata // are used for other block states, only the two first bits are used for block // type therefore we need to re-fetch the block type now. top = mc::get_color_legacy(block_type, block_data & 0x3); side = mc::get_side_color_legacy(block_type, block_data & 0x3); render_block(oper, block_type, px, py, top, side); render_halfblock(oper, block_type, px, py, top, side); break; } } } } } } virtual void render_block(image_operations_ptr, int, pos_t, pos_t, color, color) = 0; virtual void render_halfblock(image_operations_ptr, int, pos_t, pos_t, color, color) = 0; virtual void render_torchblock(image_operations_ptr, int, pos_t, pos_t, color, color) = 0; }; #endif /* _ENGINE_ISOMETRIC_BASE_HPP */ ================================================ FILE: src/engine/isometric_engine.cpp ================================================ #include "engine/isometric_engine.hpp" isometric_engine::isometric_engine( engine_settings& s, mc::world& world ) : isometric_base(s, world) { } void isometric_engine::render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { o->add_pixel(px, py, top); o->add_pixel(px + 1, py, top); o->add_pixel(px - 2, py, top); o->add_pixel(px - 1, py, top); o->add_pixel(px - 2, py + 1, side); o->add_pixel(px - 1, py + 1, side); o->add_pixel(px - 1, py + 2, side); o->add_pixel(px - 2, py + 2, side); side.lighten(0x20); o->add_pixel(px, py + 1, side); o->add_pixel(px, py + 2, side); o->add_pixel(px + 1, py + 1, side); o->add_pixel(px + 1, py + 2, side); } void isometric_engine::render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { o->add_pixel(px, py + 1, top); o->add_pixel(px + 1, py + 1, top); o->add_pixel(px - 2, py + 1, top); o->add_pixel(px - 1, py + 1, top); o->add_pixel(px - 2, py + 2, side); o->add_pixel(px - 1, py + 2, side); side.lighten(0x20); o->add_pixel(px, py + 2, side); o->add_pixel(px + 1, py + 2, side); } void isometric_engine::render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { o->add_pixel(px, py, top); o->add_pixel(px - 1, py, top); top.lighten(0x20); top.a -= 0xb0; o->add_pixel(px, py + 1, top); o->add_pixel(px - 1, py + 1, top); o->add_pixel(px - 1, py + 1, side); o->add_pixel(px - 1, py + 2, side); side.lighten(0x20); o->add_pixel(px, py + 1, side); o->add_pixel(px, py + 2, side); o->add_pixel(px - 2, py, top); o->add_pixel(px + 1, py, top); o->add_pixel(px, py - 1, top); o->add_pixel(px - 1, py - 1, top); } ================================================ FILE: src/engine/isometric_engine.hpp ================================================ #ifndef ISOMETRIC_ENGINE #define ISOMETRIC_ENGINE #include "engine/isometric_base.hpp" typedef angle_cube<2,2,1> isometric_cube; class isometric_engine : public isometric_base { public: isometric_engine(engine_settings& s, mc::world& world); void render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); }; #endif /* ISOMETRIC_ENGINE */ ================================================ FILE: src/engine/oblique_engine.cpp ================================================ #include "engine/oblique_engine.hpp" #include "engine/block_rotation.hpp" #include "engine/functions.hpp" #include void oblique_engine::render(level_ptr level, boost::shared_ptr oper) { pos_t iw, ih; project_limits(iw, ih); //const engine_settings& s = get_settings(); } ================================================ FILE: src/engine/oblique_engine.hpp ================================================ #ifndef OBLIQUE_ENGINE #define OBLIQUE_ENGINE #include "engine/engine_base.hpp" class oblique_engine : public engine_base { public: oblique_engine(engine_settings& s, mc::world& world) : engine_base(s, world) {} void render(level_ptr level, boost::shared_ptr operations); }; #endif /* OBLIQUE_ENGINE */ ================================================ FILE: src/engine/obliqueangle_engine.cpp ================================================ #include "engine/obliqueangle_engine.hpp" obliqueangle_engine::obliqueangle_engine( engine_settings& s, mc::world& world ) : isometric_base(s, world) { } void obliqueangle_engine::render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { o->add_pixel(px, py, top); o->add_pixel(px + 1, py, top); o->add_pixel(px, py + 1, side); side.lighten(0x20); o->add_pixel(px + 1, py + 1, side); } void obliqueangle_engine::render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { o->add_pixel(px, py + 1, top); o->add_pixel(px + 1, py + 1, top); } void obliqueangle_engine::render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { o->add_pixel(px, py, top); top.lighten(0x20); top.a -= 0xb0; o->add_pixel(px - 1, py, top); o->add_pixel(px + 2, py, top); o->add_pixel(px, py - 1, top); o->add_pixel(px, py + 1, top); o->add_pixel(px, py + 1, side); } ================================================ FILE: src/engine/obliqueangle_engine.hpp ================================================ #ifndef OBLIQUEANGLE_ENGINE #define OBLIQUEANGLE_ENGINE #include "engine/isometric_base.hpp" typedef angle_cube<1,1,1> obliqueangle_cube; class obliqueangle_engine : public isometric_base { public: obliqueangle_engine(engine_settings& s, mc::world& world); void render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); }; #endif /* OBLIQUE_ENGINE */ ================================================ FILE: src/engine/topdown_engine.cpp ================================================ #include "engine/topdown_engine.hpp" topdown_engine::topdown_engine( engine_settings& s, mc::world& world ) : flat_base(s, world) { } void topdown_engine::render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { o->add_pixel(px, py, top); } void topdown_engine::render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { render_block(o, bt, px, py, top, side); } void topdown_engine::render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ) { render_block(o, bt, px, py, top, side); } ================================================ FILE: src/engine/topdown_engine.hpp ================================================ #ifndef TOPDOWN_ENGINE #define TOPDOWN_ENGINE #include "engine/flat_base.hpp" class topdown_engine : public flat_base { public: topdown_engine(engine_settings& s, mc::world& world); void render_block( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_halfblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); void render_torchblock( image_operations_ptr o, int bt, pos_t px, pos_t py, color top, color side ); }; #endif /* TOPDOWN_ENGINE */ ================================================ FILE: src/fileutils.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "fileutils.hpp" ================================================ FILE: src/fileutils.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _FILEUTILS_HPP #define _FILEUTILS_HPP #include #include #include #define BOOST_FSv2 ((BOOST_VERSION / 100) <= 1045) namespace fs = boost::filesystem; inline std::string path_string(fs::path path) { #if BOOST_FSv2 return path; #else return path.string(); #endif } #endif /* _FILEUTILS_HPP */ ================================================ FILE: src/generate_map.cpp ================================================ #include "config.hpp" #include "image/format/png.hpp" #include "generate_map.hpp" #include "text.hpp" #include "players.hpp" #include "warps.hpp" #include "marker.hpp" #include "algorithm.hpp" #include "engine/engine_core.hpp" #include "engine/topdown_engine.hpp" #include "engine/oblique_engine.hpp" #include "engine/obliqueangle_engine.hpp" #include "engine/isometric_engine.hpp" #include "engine/fatiso_engine.hpp" #include "mc/blocks.hpp" #include "mc/region_iterator.hpp" #include "dlopen.hpp" #include "image/algorithms.hpp" #include "image/image_base.hpp" #include "image/cached_image.hpp" #include "image/memory_image.hpp" #include "cache.hpp" #include "json.hpp" #include #include #include #include #include #include using namespace std; namespace fs = boost::filesystem; typedef std::map levels_map; template void dot(std::ostream& out, T total) { if (total == (T) 0x00) { out << " done!"; } else { out << "." << std::flush; } } void parts_endl(std::ostream& out, unsigned int total) { out << " " << setw(8) << total << " parts" << endl; } void parts_perc_endl(std::ostream& out, unsigned int progress, unsigned int total) { out << " " << setw(8) << progress << " parts " << (progress * 100) / total << "%" << endl; } void mb_endl(std::ostream& out, streampos progress, streampos total) { out << " " << setw(8) << fixed << float(progress) / 1000000 << " MB " << (progress * 100) / total << "%" << endl; } /** * Load all warps from a database and push them to a container. */ template inline void load_warps(ostream& out, fs::path warps_path, T& warps) { out << "warps: " << warps_path << ": " << flush; warps_db wdb(warps_path); try { wdb.read(warps); out << warps.size() << " warp(s) OK" << endl; } catch(warps_db_exception& e) { out << e.what() << endl; } } /** * Load all players from a database and push them to a container. */ template inline void load_players(ostream& out, fs::path show_players_path, T& players, S& player_set) { out << "players: " << show_players_path << ": " << flush; players_db pdb(show_players_path, player_set); std::vector all_players; try { pdb.read(all_players); } catch(players_db_exception& e) { out << " " << e.what() << endl; return; } out << all_players.size() << " player(s) found" << endl; BOOST_FOREACH(player p, all_players) { if (p.error) { out << " " << p.path << ":" << p.error_where << ": " << p.error_why << endl; continue; } players.push_back(p); } out << " " << players.size() << " player(s) OK" << endl; } /** * Push all players to a standard type of marker. */ template void push_player_markers(settings_t& s, text::font_face base_font, P& players, T& markers) { text::font_face player_font = base_font; if (s.has_player_color) { player_font.set_color(s.player_color); } BOOST_FOREACH(player p, players) { if (p.zPos / mc::MapZ < s.min_z) continue; if (p.zPos / mc::MapZ > s.max_z) continue; if (p.xPos / mc::MapX < s.min_x) continue; if (p.xPos / mc::MapX > s.max_x) continue; markers.push_back(new marker(p.name, "player", player_font, p.xPos, p.yPos, p.zPos)); } } /** * Push all signs to a standard type of marker. */ template void push_sign_markers(settings_t& s, text::font_face base_font, S& signs, T& markers) { text::font_face sign_font = base_font; if (s.has_sign_color) { sign_font.set_color(s.sign_color); } BOOST_FOREACH(mc::marker lm, signs) { if (!s.show_signs_filter.empty() && lm.get_text().find(s.show_signs_filter) == std::string::npos) { continue; } if (!s.strip_sign_prefix) { markers.push_back(new marker(lm.get_text(), "sign", sign_font, lm.get_x(), lm.get_y(), lm.get_z())); } else { std::string text = lm.get_text().substr(s.show_signs_filter.size()); markers.push_back(new marker(text, "sign", sign_font, lm.get_x(), lm.get_y(), lm.get_z())); } } } typedef std::map levels_map; /** * Push all coordinates to a standard type of marker. */ template void push_coordinate_markers( std::ostream& out, settings_t& s, text::font_face base_font, mc::world& world, L& levels, T& markers) { text::font_face coordinate_font = base_font; if (s.has_coordinate_color) { coordinate_font.set_color(s.coordinate_color); } BOOST_FOREACH(levels_map::value_type value, levels) { mc::utils::level_coord c = value.second.get_coord(); mc::level_info::level_info_ptr level_info = value.second.get_level(); if (c.get_z() - 4 < world.min_z) continue; if (c.get_z() + 4 > world.max_z) continue; if (c.get_x() - 4 < world.min_x) continue; if (c.get_x() + 4 > world.max_x) continue; if (c.get_z() % 10 != 0) continue; if (c.get_x() % 10 != 0) continue; std::stringstream result; result << "(" << level_info->get_x() * mc::MapX << ", " << level_info->get_z() * mc::MapZ << ")"; if (s.debug) { out << "Pushing coordinate info " << result.str() << std::endl; } markers.push_back(new marker(result.str(), "coord", coordinate_font, c.get_x() * mc::MapX, 0, c.get_z() * mc::MapZ)); } } /** * Push all warps to a standard type of marker. */ template inline void push_warp_markers( settings_t& s, text::font_face base_font, W& warps, T& markers) { text::font_face warp_font = base_font; if (s.has_warp_color) { warp_font.set_color(s.warp_color); } /* initial code for projecting warps */ BOOST_FOREACH(warp w, warps) { if (w.zPos / mc::MapZ < s.min_z) continue; if (w.zPos / mc::MapZ > s.max_z) continue; if (w.xPos / mc::MapX < s.min_x) continue; if (w.xPos / mc::MapX > s.max_x) continue; marker *m = new marker(w.name, "warp", warp_font, w.xPos, w.yPos, w.zPos); markers.push_back(m); } } /* * Store part of a level rendered as a small image. * * This will allow us to composite the entire image later and calculate sizes then. */ void populate_markers( settings_t& s, json::array* array, boost::shared_ptr engine, boost::ptr_vector& markers) { boost::ptr_vector::iterator it; for (it = markers.begin(); it != markers.end(); it++) { marker m = *it; mc::utils::level_coord original_coord(m.get_x(), m.get_z()); mc::utils::level_coord coord = original_coord.rotate(s.rotation); pos_t x, y; engine->wp2pt(coord.get_x(), m.get_y(), coord.get_z(), x, y); json::object* o = new json::object; o->put("text", new json::string(m.get_text())); o->put("type", new json::string(m.get_type())); // the projected coordinates o->put("x", new json::number(x)); o->put("y", new json::number(y)); // the real coordinates o->put("X", new json::number(m.get_x())); o->put("Y", new json::number(m.get_y())); o->put("Z", new json::number(m.get_z())); array->push(o); } // don't bother to check for errors right now, but could be done using the "fail" accessor. } inline void overlay_markers( settings_t& s, image_ptr work_in_progress, boost::shared_ptr engine, boost::ptr_vector& markers ) { memory_image positionmark(5, 5); positionmark.fill(s.ttf_color); boost::ptr_vector::iterator it; for (it = markers.begin(); it != markers.end(); it++) { marker m = *it; text::font_face font = m.get_font(); if (!font.is_initialized()) { continue; } mc::utils::level_coord original_coord(m.get_x(), m.get_z()); mc::utils::level_coord coord = original_coord.rotate(s.rotation); pos_t x; pos_t y; engine->wp2pt(coord.get_x(), m.get_y(), coord.get_z(), x, y); font.draw(work_in_progress, m.get_text(), x + 5, y); //all->safe_composite(x - 3, y - 3, positionmark); } } /** * Helper blocks */ typedef std::map levels_map; void write_json_file( ostream& out, settings_t& s, boost::shared_ptr engine, mc::world& world, boost::ptr_vector markers) { // calculate world center engine_core::pos_t center_x, center_y; mc::utils::level_coord coord = mc::utils::level_coord(s.center_x*16, s.center_z*16).rotate(s.rotation); engine->wp2pt(coord.get_x(), 0, coord.get_z(), center_x, center_y); json::object file; json::object* json_static = new json::object; json_static->put("MapX", new json::number(mc::MapX)); json_static->put("MapY", new json::number(mc::MapY)); json_static->put("MapZ", new json::number(mc::MapZ)); file.put("st", json_static); json::object* json_world = new json::object; json_world->put("cx", new json::number(center_x)); json_world->put("cy", new json::number(center_y)); json_world->put("dx", new json::number((world.diff_x + 1) * mc::MapX)); json_world->put("dz", new json::number((world.diff_z + 1) * mc::MapZ)); json_world->put("dy", new json::number(mc::MapY)); json_world->put("mn_x", new json::number(world.min_x * 16)); json_world->put("mn_z", new json::number(world.min_z * 16)); json_world->put("mx_x", new json::number(world.max_x * 16)); json_world->put("mx_z", new json::number(world.max_z * 16)); json_world->put("rot", new json::number(s.rotation)); json_world->put("mode", new json::number(s.mode)); json_world->put("split_base", new json::number(s.split_base)); json_world->put("split", new json::number(s.split.size())); file.put("world", json_world); json::array* markers_array = new json::array; populate_markers(s, markers_array, engine, markers); file.put("markers", markers_array); if (s.write_json) { out << "Writing json information: " << path_string(s.write_json_path) << endl; std::ofstream of(path_string(s.write_json_path).c_str()); of << file; of.close(); } if (s.write_js) { out << "Writing js (javascript `var c10t_json') information: " << path_string(s.write_js_path) << endl; std::ofstream of(path_string(s.write_js_path).c_str()); of << "var c10t_json = " << file << ";"; of.close(); } } /** * Generate a map * * This is one of the main methods, it does the following steps. * * - Look for specificed databases. * - Scan the world for regions containing levels. * - Depending on settings, choose which rendering engine to use. * - Setup work-in-progress image to required type depending on predicted memory * use. * - Perform rendering phase where engine takes level information, and produces * image_operations, composite all operations to the work-in-progress image. * Try to distribute work evenly among threads. * */ bool generate_map( ostream& out, ostream& out_log, ostream& error, settings_t &s, std::vector& hints, fs::path& world_path, fs::path& output_path) { out << endl << "Generating PNG Map" << endl << endl; // all marker source information. std::vector players; std::vector warps; std::vector signs; // this is where the actual markers will be populated later. boost::ptr_vector markers; // symbolic definition of a world. mc::world world(world_path); // where to store level info levels_map levels; // this is the rendering engine that will be used. boost::shared_ptr engine; // image to work against, could be backed by hard drive, or purely in memory. image_ptr work_in_progress; /** * Any of these options will trigger the database blocks to run. */ bool use_any_database = s.show_players || s.show_signs || s.show_coordinates || s.show_warps; bool output_json = s.write_json || s.write_js; /* * Look for specificed databases. */ if (use_any_database) { out << " --- LOOKING FOR DATABASES --- " << endl; if (s.show_warps) { load_warps(out, s.show_warps_path, warps); } if (s.show_players) { load_players(out, world_path / "players", players, s.show_players_set); } if (s.show_signs) { out << "will look for signs in levels" << endl; } if (s.show_coordinates) { out << "will store chunk coordinates" << endl; } } { out << " --- SCANNING WORLD DIRECTORY --- " << endl; out << "world: " << path_string(world_path) << endl; } /* * Scan the world for regions containing levels. */ { nonstd::continious reporter(out, 100, dot, parts_endl); mc::region_iterator iterator = world.get_iterator(); int failed_regions = 0; int filtered_levels = 0; while (iterator.has_next()) { mc::region_ptr region = iterator.next(); try { region->read_header(); } catch(mc::bad_region& e) { ++failed_regions; out_log << path_string(region->get_path()) << ": could not read header" << std::endl; continue; } std::list coords; region->read_coords(coords); BOOST_FOREACH(mc::utils::level_coord c, coords) { mc::level_info::level_info_ptr level(new mc::level_info(region, c)); mc::utils::level_coord coord = level->get_coord(); if (s.coord_out_of_range(coord)) { ++filtered_levels; out_log << level->get_path() << ": (z,x) position" << " (" << coord.get_z() << "," << coord.get_x() << ")" << " out of limit" << std::endl; continue; } mc::rotated_level_info rlevel = mc::rotated_level_info(level, coord.rotate(s.rotation)); levels.insert(levels_map::value_type(rlevel.get_coord(), rlevel)); world.update(rlevel.get_coord()); reporter.add(1); } } reporter.done(0); if (failed_regions > 0) { out << "SEE LOG: " << failed_regions << " region(s) failed!" << endl; } if (filtered_levels > 0) { out << "SEE LOG: " << filtered_levels << " level(s) filtered!" << endl; } } if (levels.size() <= 0) { out << "No chunks to render" << endl; return 0; } if (s.debug) { out << " --- DEBUG WORLD INFO --- " << endl; out << "mc::world" << endl; out << " min_x: " << world.min_x << endl; out << " max_x: " << world.max_x << endl; out << " min_z: " << world.min_z << endl; out << " max_z: " << world.max_z << endl; out << " levels: " << levels.size() << endl; out << " radius: " << s.max_radius << endl; out << " chunk pos: " << world.chunk_x << "x" << world.chunk_y << endl; } engine_settings engine_s; engine_s.rotation = s.rotation; engine_s.night = s.night; engine_s.heightmap = s.heightmap; engine_s.striped_terrain = s.striped_terrain; engine_s.hellmode = s.hellmode; engine_s.cavemode = s.cavemode; engine_s.top = s.top; engine_s.bottom = s.bottom; if (s.engine_use) { dl_t* dl = dl_open(path_string(s.engine_path).c_str()); if (dl == NULL) { error << "Failed to open library: " << path_string(s.engine_path) << endl; return false; } return true; } else { /** * Depending on settings, choose which rendering engine to use. */ switch (s.mode) { case Top: engine.reset(new topdown_engine(engine_s, world)); break; case Oblique: engine.reset(new oblique_engine(engine_s, world)); break; case ObliqueAngle: engine.reset(new obliqueangle_engine(engine_s, world)); break; case Isometric: engine.reset(new isometric_engine(engine_s, world)); break; case FatIso: engine.reset(new fatiso_engine(engine_s, world)); break; } } /** * Setup work-in-progress image to required type depending on predicted memory * use. */ { pos_t image_width, image_height; pos_t level_width, level_height; engine->get_boundaries(image_width, image_height); engine->get_level_boundaries(level_width, level_height); pos_t memory_usage = (image_width * image_height * sizeof(color)) / 0x100000; if (memory_usage >= s.memory_limit) { { out << " --- BUILDING SWAP --- " << endl; out << "NOTE: A swap file is being built to accommodate high memory usage" << endl; out << "swap file: " << s.swap_file << endl; out << "swap size: " << memory_usage << " MB" << endl; out << "memory limit: " << s.memory_limit << " MB" << endl; } cached_image* image; try { image = new cached_image(s.swap_file, image_width, image_height, level_width, level_height); } catch(std::ios::failure& e) { if (errno != 0) { error << s.swap_file << ": " << strerror(errno); } else { error << s.swap_file << ": " << e.what() << ": could not open file"; } return false; } work_in_progress.reset(image); nonstd::limited c(out, 1024 * 1024, image->get_size(), dot, mb_endl); try { image->build(c); } catch(std::ios::failure& e) { if (errno != 0) { error << s.swap_file << ": could not build cache: " << strerror(errno); } else { error << s.swap_file << ": could not build cache: " << e.what(); } return false; } } else { { out << " --- ALLOCATING MEMORY --- " << endl; out << "memory usage: " << memory_usage << " MB" << endl; out << "memory limit: " << s.memory_limit << " MB" << endl; } work_in_progress.reset(new memory_image(image_width, image_height)); } } /* reset image limits for cropping */ engine->reset_image_limits(); /** * Perform rendering phase where engine takes level information, and produces * image_operations, composite all operations to the work-in-progress image. * Try to distribute work evenly among threads. */ { out << " --- RENDERING --- " << endl; unsigned int world_size = levels.size(); int effective_threads = s.threads - 1; if (effective_threads <= 1) { effective_threads = 1; } int cache_hits = 0; int failed_levels = 0; /** * Define a dynamically growing buffer to read regions in. * Is grown on demand, but never shrunk. */ mc::dynamic_buffer region_buffer(mc::region::CHUNK_MAX); nonstd::limited reporter(out, 50, world_size, dot, parts_perc_endl); uint32_t id = 1; BOOST_FOREACH(levels_map::value_type value, levels) { reporter.add(1); mc::rotated_level_info rotated_level_info = value.second; mc::level_info_ptr level_info = rotated_level_info.get_level(); mc::level_ptr level(new mc::level(level_info)); fs::path path = level_info->get_path(); try { level->read(region_buffer); } catch(mc::invalid_file& e) { out_log << path << ": " << e.what() << endl; continue; } mc::utils::level_coord coord = rotated_level_info.get_coord(); image_operations_ptr operations(new image_operations(id++)); bool cache_hit = false; time_t mod = level->modification_time(); std::stringstream ss; ss << boost::format("%d.%d.cmap") % coord.get_x() % coord.get_z(); std::string basename = ss.str(); fs::path level_dir = mc::utils::level_dir(s.cache_dir, coord.get_x(), coord.get_z()); cache_file cache(level_dir, basename, mod, s.cache_compress); if (s.cache_use) { if (cache.exists()) { if (cache.read(operations)) { cache_hit = true; } cache.clear(); } } if (!cache_hit) { engine->render(level, operations); } //operations->optimize(); if (s.cache_use) { // create the necessary directories required when caching cache.create_directories(); // ignore failure while writing the operations to cache if (!cache.write(operations)) { // on failure, remove the cache file - this will prompt c10t to regenerate it next time cache.clear(); } } if (s.debug) { out << path_string(path) << ": dequeued OK" << endl; } if (cache_hit) { ++cache_hits; } //if (p.signs.size() > 0) { //if (s.debug) { out << "Found " << p.signs.size() << " signs"; }; //signs.insert(signs.end(), p.signs.begin(), p.signs.end()); //} try { pos_t x, y; engine->w2pt(coord.get_x(), coord.get_z(), x, y); // update image limits engine->update_image_limits( x + 1, y, x + operations->max_x, y + operations->max_y - 1); work_in_progress->composite(x, y, operations); } catch(std::ios::failure& e) { out << path_string(s.swap_file) << ": " << strerror(errno); return false; } } reporter.done(0); if (failed_levels > 0) { out << "SEE LOG: " << failed_levels << " level(s) failed!" << endl; } if (s.cache_use) { out << "cache_hits: " << cache_hits << "/" << world_size << endl; } out << "image limits: " << engine->get_min_x() << "x" << engine->get_min_y() << " to " << engine->get_max_x() << "x" << engine->get_max_y() << " will be the cropped image (" << (engine->get_max_x() - engine->get_min_x()) << "x" << (engine->get_max_y() - engine->get_min_y()) << ")" << endl; image_ptr cropped = image::crop(work_in_progress, engine->get_min_x(), engine->get_max_x(), engine->get_min_y(), engine->get_max_y()); work_in_progress = cropped; } if (use_any_database) { text::font_face font(s.ttf_path, s.ttf_size, s.ttf_color); /* * If we are only going to output json information, do not initialize font. * This will prevent any fonts from actually rendering anything later on. */ if (!output_json) { try { font.init(); } catch(text::text_error& e) { error << "failed to initialize font: " << e.what() << std::endl; } } if (s.show_players) { push_player_markers(s, font, players, markers); } if (s.show_signs && signs.size() > 0) { push_sign_markers(s, font, signs, markers); } if (s.show_coordinates) { push_coordinate_markers(out, s, font, world, levels, markers); } if (s.show_warps) { push_warp_markers(s, font, warps, markers); } } if (output_json) { if (!use_any_database) { hints.push_back("Use `--write-json' in combination with `--show-*'" " in order to write different types of markers to file"); } write_json_file(out, s, engine, world, markers); } else { overlay_markers(s, work_in_progress, engine, markers); } engine_core::pos_t center_x, center_y; engine->wp2pt(0, 0, 0, center_x, center_y); if (s.use_split) { out << " --- SAVING MULTIPLE IMAGES --- " << endl; int i = 0; image_ptr target; BOOST_FOREACH(unsigned int split_i, s.split) { if (!target && s.split_base > 0) { target.reset(new memory_image(s.split_base, s.split_base)); } std::map parts; image::split(work_in_progress, split_i, parts); out << "Level " << i << ": splitting into " << parts.size() << " image on " << split_i << "px" << endl; for (std::map::iterator it = parts.begin(); it != parts.end(); it++) { const point2 p = it->first; image_ptr img(it->second); stringstream ss; ss << boost::format(path_string(output_path)) % i % p.x % p.y; fs::path path(ss.str()); if (!fs::is_directory(path.parent_path())) { fs::create_directories(path.parent_path()); } png_format::opt_type opts; opts.center_x = center_x; opts.center_y = center_y; opts.comment = C10T_COMMENT; std::string path_str(path_string(path)); if (s.split_base > 0) { target->clear(); img->resize(target); } else { target = img; } try { target->save(path_str, opts); } catch (format_exception& e) { out << path_string(path) << ": " << e.what() << endl; continue; } out << path_string(path) << ": OK" << endl; } ++i; } } else { { out << " --- SAVING IMAGE --- " << endl; out << "path: " << path_string(output_path) << endl; } png_format::opt_type opts; opts.center_x = center_x; opts.center_y = center_y; opts.comment = C10T_COMMENT; try { work_in_progress->save(path_string(output_path), opts); } catch (format_exception& e) { out << path_string(output_path) << ": " << e.what() << endl; return false; } out << path_string(output_path) << ": OK" << endl; } return true; } ================================================ FILE: src/generate_map.hpp ================================================ #ifndef __GENERATE_MAP_HPP__ #define __GENERATE_MAP_HPP__ #include #include #include #include #include "settings_t.hpp" bool generate_map( std::ostream& out, std::ostream& out_log, std::ostream& error, settings_t &s, std::vector& hints, boost::filesystem::path& world_path, boost::filesystem::path& output_path); #endif /*__GENERATE_MAP_HPP__*/ ================================================ FILE: src/generate_statistics.cpp ================================================ #include "altitude_graph.hpp" #include "generate_statistics.hpp" #include "mc/blocks.hpp" #include "mc/region_iterator.hpp" #include "mc/level_info.hpp" #include "mc/level.hpp" #include "players.hpp" #include "main_utils.hpp" #include "engine/block_rotation.hpp" #include #include #include using namespace std; template void dot(std::ostream& out, T total) { if (total == 0x00) { out << " done!"; } else { out << "." << std::flush; } } void uint_endl(std::ostream& out, unsigned int total) { out << " " << setw(8) << total << " parts" << endl; } bool generate_statistics( std::ostream& out, std::ostream& out_log, std::ostream& error, settings_t &s, std::vector& hints, fs::path& world_path, fs::path& output_path) { out << endl << "Generating Statistics File" << endl << endl; std::vector players; mc::world world(world_path); AltitudeGraph *_stat = new AltitudeGraph(s); mc::MaterialT *materials = mc::MaterialTable.data(); long statistics[mc::MaterialTable.size()]; for (int i = 0; i < mc::MaterialTable.size(); i++) { statistics[i] = 0; } boost::optional graph_block; { mc::MaterialT *material; if(get_blocktype(s.graph_block, material)) { graph_block = boost::optional(material); } else { graph_block = boost::optional(); } } bool any_db = s.show_players || s.show_signs || s.show_coordinates || s.show_warps; if (any_db) { out << " --- LOOKING FOR DATABASES --- " << endl; if (s.show_players) { error << "loading of players in altitiude graph has been disabled" << endl; } //if (s.show_players) { //load_players(out, world_path / "players", players, s.show_players_set); //} } int failed_regions = 0; int filtered_levels = 0; int failed_levels = 0; int levels = 0; { nonstd::continious reporter(out, 100, dot, uint_endl); mc::region_iterator iterator = world.get_iterator(); mc::dynamic_buffer region_buffer(mc::region::CHUNK_MAX); while (iterator.has_next()) { mc::region_ptr region = iterator.next(); try { region->read_header(); } catch(mc::bad_region& e) { ++failed_regions; out_log << region->get_path() << ": could not read header" << std::endl; continue; } std::list coords; region->read_coords(coords); BOOST_FOREACH(mc::utils::level_coord c, coords) { mc::level_info::level_info_ptr level(new mc::level_info(region, c)); mc::utils::level_coord coord = level->get_coord(); ++levels; if (s.coord_out_of_range(coord)) { ++filtered_levels; out_log << level->get_path() << ": (z,x) position" << " (" << coord.get_z() << "," << coord.get_x() << ")" << " out of limit" << std::endl; continue; } mc::level level_data(level); world.update(level->get_coord()); try { level_data.read(region_buffer); } catch(mc::invalid_file& e) { ++failed_levels; out_log << level->get_path() << ": " << e.what(); continue; } boost::shared_ptr L = level_data.get_level(); BOOST_FOREACH(mc::Section_Compound Section, L->Sections) { block_rotation br_blocks(0, Section.Blocks); block_rotation br_data(0, Section.Data); for (int y = 15; y >= 0; y--) { int abs_y = (Section.Y * 16) + y; for (int z = 0; z < mc::MapZ; z++) { for (int x = 0; x < mc::MapX; x++) { br_blocks.set_xz(x, z); br_data.set_xz(x, z); int block_type = br_blocks.get8(y); int block_data = br_data.get4(y); boost::optional material = mc::get_material_legacy(block_type, block_data); if (material) { if (material.get()->enabled) { size_t index = material.get() - materials; statistics[index] += 1; } if(graph_block && material.get() == graph_block.get()) { _stat->registerBloc(material.get(), abs_y); } } } } } } reporter.add(1); } } reporter.done(0); if (failed_regions > 0) { out << "SEE LOG: " << failed_regions << " region(s) failed!" << endl; } if (filtered_levels > 0) { out << "SEE LOG: " << filtered_levels << " level(s) filtered!" << endl; } if (failed_levels > 0) { out << "SEE LOG: " << failed_levels << " level(s) failed!" << endl; } } ofstream stats(path_string(output_path).c_str()); stats << "[WORLD]" << endl; stats << "min_x " << world.min_x << endl; stats << "max_x " << world.max_x << endl; stats << "min_z " << world.min_z << endl; stats << "max_z " << world.max_z << endl; stats << "chunks " << (levels-filtered_levels-failed_levels) << " of " << levels << endl; if (s.show_players) { stats << "[PLAYERS]" << endl; std::vector::iterator plit = players.begin(); for (; plit != players.end(); plit++) { player p = *plit; stats << p.name << " " << p.xPos << " " << p.yPos << " " << p.zPos << endl; } } stats << "[BLOCKS]" << endl; for (int i = 0; i < mc::MaterialTable.size(); i++) { if (statistics[i] > 0) { std::string full_name = mc::MaterialTable[i].mc_namespace + ":" + mc::MaterialTable[i].name; stats << std::left << setw(32) << full_name << " " << statistics[i] << endl; } } stats.close(); if (stats.fail()) { error << "failed to write statistics to " << output_path; return false; } out << "statistics written to " << output_path; if(graph_block) _stat->createGraph(); return true; } ================================================ FILE: src/generate_statistics.hpp ================================================ #ifndef __GENERATE_STATISTICS_HPP__ #define __GENERATE_STATISTICS_HPP__ #include #include #include #include #include "settings_t.hpp" bool generate_statistics( std::ostream& out, std::ostream& out_log, std::ostream& error, settings_t &s, std::vector& hints, boost::filesystem::path& world_path, boost::filesystem::path& output_path); #endif /*__GENERATE_STATISTICS_HPP__*/ ================================================ FILE: src/image/CMakeLists.txt ================================================ add_library(c10t-image image_operations.cpp image_base.cpp memory_image.cpp cached_image.cpp color.cpp algorithms.cpp ${ZLIB_LIBRARIES} ${PNG_LIBRARIES}) set_target_properties(c10t-image PROPERTIES COMPILE_FLAGS "-O3 -Wall -pedantic -g") ================================================ FILE: src/image/algorithms.cpp ================================================ #include "image/algorithms.hpp" namespace image { image_ptr crop(image_ptr base, pos_t min_x, pos_t max_x, pos_t min_y, pos_t max_y) { image_ptr ptr(new virtual_image(max_x - min_x, max_y - min_y, base, min_x, min_y)); return ptr; } } ================================================ FILE: src/image/algorithms.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef __IMAGE_ALGORITHMS__ #define __IMAGE_ALGORITHMS__ #include "image/image_base.hpp" #include "image/virtual_image.hpp" #include "2d/cube.hpp" #include namespace image { /** * Split an image into multiple smaller images based on a specific pixel size. */ template void split(image_ptr base, int pixels, M& map) { for (pos_t w = 0, px = 0; w < base->get_width(); w += pixels, px++) { for (pos_t h = 0, py = 0; h < base->get_height(); h += pixels, py++) { map[point2(px, py)] = new virtual_image(pixels, pixels, base, w, h); } } } image_ptr crop(image_ptr base, pos_t min_x, pos_t max_x, pos_t min_y, pos_t max_y); } #endif /*__IMAGE_ALGORITHMS__*/ ================================================ FILE: src/image/cached_image.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "image/cached_image.hpp" #include "algorithm.hpp" cached_image::cached_image(const fs::path path, pos_t w, pos_t h, pos_t l_w, pos_t l_h) : image_base(w, h), path(path), buffer_size((l_w + 1) * l_h), buffer_set(false), buffer(new color[buffer_size]), buffer_w(0), buffer_h(0), buffer_x(0), buffer_y(0) { using namespace ::std; fs.exceptions(ios::failbit | ios::badbit); fs.open(path.string().c_str(), ios::in | ios::out | ios::trunc); this->size = boost::numeric_cast(get_width()) * boost::numeric_cast(get_height()) * sizeof(color); } cached_image::~cached_image() { flush_buffer(); fs.close(); } std::streampos cached_image::get_size() { return size; } // cached_image void cached_image::set_pixel(pos_t x, pos_t y, color& c) { if (!(x < get_width())) { return; } if (!(y < get_height())) { return; } fs.seekp(sizeof(color) * get_offset(x, y)); fs.write(reinterpret_cast(&c), sizeof(color)); } void cached_image::get_pixel(pos_t x, pos_t y, color& c) { if (!(x < get_width())) { return; } if (!(y < get_height())) { return; } fs.seekg(sizeof(color) * get_offset(x, y)); fs.read(reinterpret_cast(&c), sizeof(color)); } void cached_image::get_line(pos_t y, pos_t x, pos_t width, color* c) { if (!(y < get_height())) { return; } if (!(x < get_width())) { return; } if (!(width + x < get_width())) { width = get_width() - x; } fs.seekg(sizeof(color) * get_offset(x, y)); fs.read(reinterpret_cast(c), sizeof(color) * width); } void cached_image::set_line(pos_t y, pos_t x, pos_t width, color* c) { if (!(y < get_height())) { return; } if (!(x < get_width())) { return; } if (!(width + x < get_width())) { width = get_width() - x; } fs.seekp(sizeof(color) * get_offset(x, y)); fs.write(reinterpret_cast(c), sizeof(color) * width); } void cached_image::flush_buffer() { if (buffer_set) { for (pos_t y = 0; y < buffer_h; y++) { set_line(buffer_y + y, buffer_x, buffer_w, &buffer[y * buffer_w]); } } } void cached_image::read_buffer() { for (pos_t y = 0; y < buffer_h; y++) { get_line(buffer_y + y, buffer_x, buffer_w, &buffer[y * buffer_w]); } } void cached_image::blend_pixel(pos_t x, pos_t y, color &c) { // do nothing if color is invisible if (c.is_invisible()) { return; } pos_t bx = x - buffer_x; pos_t by = y - buffer_y; pos_t bp = bx + by * buffer_w; if (x >= buffer_x && y >= buffer_y && bp < buffer_size) { buffer[bp].blend(c); } else { // Blend pixels that are "out of (the buffer's) scope" color o; get_pixel(x, y, o); o.blend(c); set_pixel(x, y, o); } } void cached_image::align(pos_t x, pos_t y, pos_t w, pos_t h) { flush_buffer(); // reallocate buffer when the current buffer is too small if (buffer_size < w * h) { buffer.reset(new color[w * h]); buffer_size = w * h; } buffer_x = x, buffer_y = y; buffer_w = w, buffer_h = h; read_buffer(); buffer_set = true; } ================================================ FILE: src/image/cached_image.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef CACHED_IMAGE #define CACHED_IMAGE #include #include #include #include #include #include "image/image_base.hpp" #include "image/color.hpp" #include "algorithm.hpp" namespace fs = boost::filesystem; class cached_image : public image_base { public: static const pos_t WRITE_SIZE = 4096 * 8; cached_image(const fs::path path, pos_t w, pos_t h, pos_t l_w, pos_t l_h); ~cached_image(); /** * Will build the cache from scratch, filling it with null, which coneniently fits * with black transparent colors. * * @reporter Any type which supports 'set_limit', 'add' and 'done' functions. */ template void build(R& reporter) { using namespace ::std; streampos written = 0; streampos write_size = WRITE_SIZE; boost::scoped_array nil(new char[write_size]); ::memset(nil.get(), 0x0, write_size); while (written < size) { streampos write = min(size, write_size); fs.write(nil.get(), write); written += write; reporter.add(write); } reporter.done(0); } std::streampos get_size(); void set_pixel(pos_t x, pos_t y, color&); void get_pixel(pos_t x, pos_t y, color&); /* * This is where you may use caching or whatever mechanism. * * It is guaranteed that no other points will be used * except for those that has previously been declared * with 'align'. * * @see #align **/ void blend_pixel(pos_t x, pos_t y, color &c); /* * Read a list of colors. * The pointer color must contain all necessary colors. * * The rationale for this method is that it usually is much more efficient * to have a continious set of colors read/written since it usually is more * efficient. While letting the single operations be made against memory. * * This method must assert that the result reflects the same as the backend * store, usually means that you actually have to read from it. **/ void get_line(pos_t y, pos_t x, pos_t width, color*); /* * Set a list of colors. * @see #get_line **/ void set_line(pos_t y, pos_t x, pos_t width, color*); /* * Align whatever caching mechanism might be used to only expect blend requests for these areas. **/ void align(pos_t x, pos_t y, pos_t w, pos_t h); private: const fs::path path; std::fstream fs; pos_t buffer_size; bool buffer_set; boost::scoped_array buffer; std::streampos size; pos_t buffer_w; pos_t buffer_h; pos_t buffer_x; pos_t buffer_y; void read_buffer(); void flush_buffer(); }; #endif /* CACHED_IMAGE */ ================================================ FILE: src/image/color.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "image/color.hpp" #include #include float color_i_to_f[] = { 0.0000f, 0.0039f, 0.0078f, 0.0118f, 0.0157f, 0.0196f, 0.0235f, 0.0275f, 0.0314f, 0.0353f, 0.0392f, 0.0431f, 0.0471f, 0.0510f, 0.0549f, 0.0588f, 0.0627f, 0.0667f, 0.0706f, 0.0745f, 0.0784f, 0.0824f, 0.0863f, 0.0902f, 0.0941f, 0.0980f, 0.1020f, 0.1059f, 0.1098f, 0.1137f, 0.1176f, 0.1216f, 0.1255f, 0.1294f, 0.1333f, 0.1373f, 0.1412f, 0.1451f, 0.1490f, 0.1529f, 0.1569f, 0.1608f, 0.1647f, 0.1686f, 0.1725f, 0.1765f, 0.1804f, 0.1843f, 0.1882f, 0.1922f, 0.1961f, 0.2000f, 0.2039f, 0.2078f, 0.2118f, 0.2157f, 0.2196f, 0.2235f, 0.2275f, 0.2314f, 0.2353f, 0.2392f, 0.2431f, 0.2471f, 0.2510f, 0.2549f, 0.2588f, 0.2627f, 0.2667f, 0.2706f, 0.2745f, 0.2784f, 0.2824f, 0.2863f, 0.2902f, 0.2941f, 0.2980f, 0.3020f, 0.3059f, 0.3098f, 0.3137f, 0.3176f, 0.3216f, 0.3255f, 0.3294f, 0.3333f, 0.3373f, 0.3412f, 0.3451f, 0.3490f, 0.3529f, 0.3569f, 0.3608f, 0.3647f, 0.3686f, 0.3725f, 0.3765f, 0.3804f, 0.3843f, 0.3882f, 0.3922f, 0.3961f, 0.4000f, 0.4039f, 0.4078f, 0.4118f, 0.4157f, 0.4196f, 0.4235f, 0.4275f, 0.4314f, 0.4353f, 0.4392f, 0.4431f, 0.4471f, 0.4510f, 0.4549f, 0.4588f, 0.4627f, 0.4667f, 0.4706f, 0.4745f, 0.4784f, 0.4824f, 0.4863f, 0.4902f, 0.4941f, 0.4980f, 0.5020f, 0.5059f, 0.5098f, 0.5137f, 0.5176f, 0.5216f, 0.5255f, 0.5294f, 0.5333f, 0.5373f, 0.5412f, 0.5451f, 0.5490f, 0.5529f, 0.5569f, 0.5608f, 0.5647f, 0.5686f, 0.5725f, 0.5765f, 0.5804f, 0.5843f, 0.5882f, 0.5922f, 0.5961f, 0.6000f, 0.6039f, 0.6078f, 0.6118f, 0.6157f, 0.6196f, 0.6235f, 0.6275f, 0.6314f, 0.6353f, 0.6392f, 0.6431f, 0.6471f, 0.6510f, 0.6549f, 0.6588f, 0.6627f, 0.6667f, 0.6706f, 0.6745f, 0.6784f, 0.6824f, 0.6863f, 0.6902f, 0.6941f, 0.6980f, 0.7020f, 0.7059f, 0.7098f, 0.7137f, 0.7176f, 0.7216f, 0.7255f, 0.7294f, 0.7333f, 0.7373f, 0.7412f, 0.7451f, 0.7490f, 0.7529f, 0.7569f, 0.7608f, 0.7647f, 0.7686f, 0.7725f, 0.7765f, 0.7804f, 0.7843f, 0.7882f, 0.7922f, 0.7961f, 0.8000f, 0.8039f, 0.8078f, 0.8118f, 0.8157f, 0.8196f, 0.8235f, 0.8275f, 0.8314f, 0.8353f, 0.8392f, 0.8431f, 0.8471f, 0.8510f, 0.8549f, 0.8588f, 0.8627f, 0.8667f, 0.8706f, 0.8745f, 0.8784f, 0.8824f, 0.8863f, 0.8902f, 0.8941f, 0.8980f, 0.9020f, 0.9059f, 0.9098f, 0.9137f, 0.9176f, 0.9216f, 0.9255f, 0.9294f, 0.9333f, 0.9373f, 0.9412f, 0.9451f, 0.9490f, 0.9529f, 0.9569f, 0.9608f, 0.9647f, 0.9686f, 0.9725f, 0.9765f, 0.9804f, 0.9843f, 0.9882f, 0.9922f, 0.9961f, 1.0000f }; float alpha_over_c(float u, float o, float ua, float oa); /** * Takes two color values and does an alpha over blending without using floats. */ inline float alpha_over_c(float ac, float aa, float bc, float ba) { return (ac * aa) + (bc * ba - aa * bc * ba); } void color::blend(const color &other) { if (other.is_invisible()) return; if (other.is_opaque() || is_invisible()) { r = other.r; g = other.g; b = other.b; a = other.a; return; } r = alpha_over_c(other.r, other.a, r, a); g = alpha_over_c(other.g, other.a, g, a); b = alpha_over_c(other.b, other.a, b, a); a = a + other.a * (1.0f - a); r = ((r * 1.0f) / a); g = ((g * 1.0f) / a); b = ((b * 1.0f) / a); } // pull color down towards black void color::darken(float f) { r = std::max(r - (r * f), 0.0f); g = std::max(g - (g * f), 0.0f); b = std::max(b - (b * f), 0.0f); } void color::darken(int f) { darken(color_i_to_f[uint8_t(f)]); } void color::lighten(float f) { r = std::min(r + (r * f), 1.0f); g = std::min(g + (g * f), 1.0f); b = std::min(b + (b * f), 1.0f); } void color::lighten(int f) { lighten(color_i_to_f[uint8_t(f)]); } ================================================ FILE: src/image/color.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef COLOR_HPP #define COLOR_HPP #include #include #include #include #include #include #include extern float color_i_to_f[0x100]; struct color{ float r; float g; float b; float a; int32_t z; color(color *c) : r(c->r), g(c->g), b(c->b), a(c->a), z(0) { } color() : r(1.0f), g(1.0f), b(1.0f), a(0.0f), z(0) { } color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(color_i_to_f[r]), g(color_i_to_f[g]), b(color_i_to_f[b]), a(color_i_to_f[a]), z(0) { } color(int r, int g, int b, int a) : r(color_i_to_f[uint8_t(r)]), g(color_i_to_f[uint8_t(g)]), b(color_i_to_f[uint8_t(b)]), a(color_i_to_f[uint8_t(a)]), z(0) { } color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a), z(0) { } bool is_opaque() const { return a == 1.0f; } bool is_transparent() const { return a != 1.0f; } bool is_invisible() const { return a == 0.0f; } ~color(){ } void darken(float c); void darken(int c); void lighten(float c); void lighten(int c); void blend(const color &other); inline void read(color *buf) { r = buf->r; g = buf->g; b = buf->b; a = buf->a; } inline void write(color *buf) { buf->r = r; buf->g = g; buf->b = b; buf->a = a; } friend std::ostream& operator<<(std::ostream& out, const color& c) // output { std::stringstream ss; ss << (int)roundf(c.r*255.0f) << "," << (int)roundf(c.g*255.0f) << "," << (int)roundf(c.b*255.0f) << "," << (int)roundf(c.a*255.0f); out << ss.str(); return out; } bool operator!=(const color &c) const { return !(r == c.r && g == c.g && b == c.b && a == c.a); } bool operator==(const color &c) const { return r == c.r && g == c.g && b == c.b && a == c.a; } }; #endif /* COLOR_HPP */ ================================================ FILE: src/image/format/base.hpp ================================================ #ifndef _IMAGE_FORMAT_BASE_HPP_ #define _IMAGE_FORMAT_BASE_HPP_ #include class format_exception : public std::exception { private: const char* why; public: format_exception(const char* why) : why(why) { } const char* what() throw() { return this->why; } }; #endif /* _IMAGE_FORMAT_BASE_HPP_ */ ================================================ FILE: src/image/format/png.hpp ================================================ #ifndef FILEFORMAT_PNG #define FILEFORMAT_PNG #include #include #include "image/format/base.hpp" #include "image/image_base.hpp" #include struct png_config { std::string comment; size_t center_x, center_y; }; class png_format { public: typedef png_config opt_type; static void save(image_base* image, const std::string& path, opt_type& opts) { FILE *fp; png_structp write_struct = NULL; png_infop info_struct = NULL; png_bytep bytes_row = NULL; color* color_row = NULL; fp = fopen(path.c_str(), "wb"); if (fp == NULL) { throw format_exception(strerror(errno)); } write_struct = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (write_struct == NULL) { fclose(fp); throw format_exception("failed to acquire write_struct"); } /* optipng tends to select these options for c10t generated images */ png_set_compression_level(write_struct, 9); png_set_compression_mem_level(write_struct, 8); png_set_compression_strategy(write_struct, Z_DEFAULT_STRATEGY); png_set_filter(write_struct, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); info_struct = png_create_info_struct(write_struct); if (info_struct == NULL) { fclose(fp); png_destroy_write_struct(&write_struct, NULL); throw format_exception("failed to acquire info_struct"); } /* this is where we end up on errors */ if (setjmp(png_jmpbuf(write_struct))) { fclose(fp); png_destroy_write_struct(&write_struct, &info_struct); delete [] bytes_row; delete [] color_row; throw format_exception("unknown libpng error"); } png_init_io(write_struct, fp); png_set_IHDR(write_struct, info_struct, image->get_width(), image->get_height(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (!opts.comment.empty()) { png_text title_text; title_text.compression = PNG_TEXT_COMPRESSION_NONE; title_text.key = (char *)"Title"; title_text.text = (char *)opts.comment.c_str(); png_set_text(write_struct, info_struct, &title_text, 1); } { png_text title_text; title_text.compression = PNG_TEXT_COMPRESSION_NONE; title_text.key = (char *)"center-x"; std::string text = boost::lexical_cast(opts.center_x); title_text.text = const_cast(text.c_str()); png_set_text(write_struct, info_struct, &title_text, 1); } { png_text title_text; title_text.compression = PNG_TEXT_COMPRESSION_NONE; title_text.key = (char *)"center-y"; std::string text = boost::lexical_cast(opts.center_y); title_text.text = const_cast(text.c_str()); png_set_text(write_struct, info_struct, &title_text, 1); } png_write_info(write_struct, info_struct); bytes_row = new png_byte[4 * image->get_width()]; color_row = new color[image->get_width()]; for (size_t y = 0; y < image->get_height(); y++) { image->get_line(y, color_row); for (unsigned int i = 0; i < image->get_width(); i++) { bytes_row[i*4 + 0] = png_byte(color_row[i].r * 255.0f); bytes_row[i*4 + 1] = png_byte(color_row[i].g * 255.0f); bytes_row[i*4 + 2] = png_byte(color_row[i].b * 255.0f); bytes_row[i*4 + 3] = png_byte(color_row[i].a * 255.0f); } png_write_row(write_struct, bytes_row); } png_write_end(write_struct, NULL); fclose(fp); png_free_data(write_struct, info_struct, PNG_FREE_ALL, -1); png_destroy_write_struct(&write_struct, &info_struct); delete [] bytes_row; delete [] color_row; } }; #endif /* FILEFORMAT_PNG */ ================================================ FILE: src/image/image_base.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "image_base.hpp" #include #include // image_base void image_base::fill(color &q) { for (pos_t x = 0; x < get_width(); x++) { for (pos_t y = 0; y < get_height(); y++) { set_pixel(x, y, q); } } } void image_base::clear() { boost::shared_array line(new color[get_width()]); ::memset(line.get(), 0x0, get_width() * sizeof(color)); for (pos_t y = 0; y < get_height(); y++) set_line(y, 0, get_width(), line.get()); } void image_base::composite(int x, int y, image_operations_ptr opers) { std::vector::size_type i = 0; align(x, y, opers->max_x, opers->max_y); while (i++ < opers->operations.size()) { image_operation op = opers->operations[i]; blend_pixel(x + op.x, y + op.y, op.c); } } void image_base::draw_line(pos_t x1, pos_t y1, pos_t x2, pos_t y2, color &c) { int sx, sy; int dx = (x1set_pixel(x1, y1, c); int e2 = 2*err; if(e2 > -dy) { err -= dy; x1 += sx; } if(e2 < dx) { err += dx; y1 += sy; } } while(x1 != x2 || y1 != y2); } void image_base::safe_blend_pixel(pos_t x, pos_t y, color &c) { if (x >= width) return; if (y >= height) return; blend_pixel(x, y, c); } void image_base::resize(image_ptr target) { if (target->get_width() > get_width()) { // scale up unsigned int factor = target->get_width() / get_width(); boost::shared_array line(new color[get_width()]); unsigned int scanline = 0; bool first = false; for (pos_t y = 0; y < target->get_height(); y++) { pos_t y_p = (y / factor); if (y_p != scanline || !first) { get_line(y_p, pos_t(0), get_width(), line.get()); first = true; scanline = y_p; } for (pos_t x = 0; x < target->get_width(); x++) { pos_t x_p = (x / factor); target->set_pixel(x, y, line[x_p]); } } } else { // scale down unsigned int factor = get_width() / target->get_width(); boost::shared_array line(new color[get_width()]); for (pos_t y = 0; y < target->get_height(); y++) { pos_t y_p = ((y * factor) + factor / 2); get_line(y_p, pos_t(0), get_width(), line.get()); for (pos_t x = 0; x < target->get_width(); x++) { pos_t x_p = ((x * factor) + factor / 2); target->set_pixel(x, y, line[x_p]); } } } } ================================================ FILE: src/image/image_base.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef IMAGE_BASE #define IMAGE_BASE #include #include #include #include #include "image/image_operations.hpp" #include "image/color.hpp" class image_base; typedef boost::shared_ptr image_ptr; typedef uint64_t pos_t; class image_base { protected: pos_t width, height; public: typedef void (*progress_c)(int , int); image_base(pos_t width, pos_t height) : width(width), height(height) { } virtual ~image_base() { } void fill(color& c); void clear(); inline pos_t get_width() { return width; }; inline pos_t get_height() { return height; }; void composite(int xoffset, int yoffset, image_operations_ptr oper); inline std::streampos get_offset(std::streampos x, std::streampos y) { std::streampos width = get_width(); return x + y * width; } void safe_blend_pixel(pos_t x, pos_t y, color &c); void get_line(pos_t y, color *c) { get_line(y, 0, get_width(), c); } template void save(const std::string str, typename T::opt_type opts) { T::save(this, str, opts); } void draw_line(pos_t x1, pos_t y1, pos_t x2, pos_t y2, color &c); void resize(image_ptr target); virtual void blend_pixel(pos_t x, pos_t y, color &c) = 0; virtual void set_pixel(pos_t x, pos_t y, color& c) = 0; virtual void get_pixel(pos_t x, pos_t y, color& c) = 0; virtual void get_line(pos_t y, pos_t offset, pos_t width, color*) = 0; virtual void set_line(pos_t y, pos_t offset, pos_t width, color*) {}; virtual void align(pos_t x, pos_t y, pos_t width, pos_t height) {}; }; #endif /* IMAGE_BASE */ ================================================ FILE: src/image/image_operations.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "image/image_operations.hpp" #include #include #include image_operations::image_operations(uint32_t z) : z(z), min_x(0), min_y(0), max_x(0), max_y(0) { } image_operations::~image_operations() { } void image_operations::add_pixel(pos_t x, pos_t y, color &c) { if (c.is_invisible()) { return; } if (!(x >= min_x)) { return; } if (!(y >= min_y)) { return; } if (!(x < max_x)) { return; } if (!(y < max_y)) { return; } color n = color(c); // tag with depth. n.z = z; image_operation oper; oper.x = (uint16_t)x; oper.y = (uint16_t)y; oper.c = n; operations.push_back(oper); } void image_operations::optimize() { bool* blocked = new bool[max_x*max_y]; for (unsigned int i = 0; i < max_x*max_y; i++) { blocked[i] = false; } operations_vector new_operations; BOOST_REVERSE_FOREACH(image_operation operation, operations) { int offset = operation.x * max_x + operation.y; if (blocked[offset]) { continue; } blocked[offset] = operation.c.is_opaque(); new_operations.push_back(operation); } std::reverse(new_operations.begin(), new_operations.end()); operations = new_operations; delete [] blocked; } void image_operations::reverse() { std::reverse(operations.begin(), operations.end()); } void image_operations::set_limits(pos_t x, pos_t y) { min_x = 0; min_y = 0; max_x = x; max_y = y; operations.reserve(max_x * max_y * 2); } ================================================ FILE: src/image/image_operations.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef IMAGE_OPERATIONS #define IMAGE_OPERATIONS #include #include #include #include #include "image/color.hpp" typedef uint64_t pos_t; class image_operations; typedef boost::shared_ptr image_operations_ptr; struct image_operation { color c; uint16_t x, y; }; class image_operations { public: /* * The depth of the image operations. */ uint32_t z; pos_t min_x, min_y; pos_t max_x, max_y; typedef std::vector operations_vector; image_operations(uint32_t z); ~image_operations(); operations_vector operations; void add_pixel(pos_t x, pos_t y, color &c); void set_limits(pos_t x, pos_t y); void optimize(); void reverse(); }; #endif /* IMAGE_OPERATIONS */ ================================================ FILE: src/image/memory_image.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "image/memory_image.hpp" #include // memory_image memory_image::memory_image(int w, int h) : image_base(w, h) { colors = new color[w * h]; memset(colors, 0x255, w * h); } memory_image::~memory_image() { delete [] colors; } void memory_image::set_pixel(pos_t x, pos_t y, color &c) { if (!(x < get_width())) { return; } if (!(y < get_height())) { return; } c.write(this->colors + get_offset(x, y)); } void memory_image::get_pixel(pos_t x, pos_t y, color &c) { if (!(x < get_width())) { return; } if (!(y < get_height())) { return; } c.read(this->colors + get_offset(x, y)); } void memory_image::get_line(pos_t y, pos_t offset, pos_t width, color* c) { if (!(y < get_height())) { return; } if (!(offset < get_width())) { return; } if (!(width + offset < get_width())) { width = get_width() - offset; } memcpy(c, this->colors + get_offset(offset, y), width * sizeof(color)); } void memory_image::blend_pixel(pos_t x, pos_t y, color &c) { color o; get_pixel(x, y, o); if (c.z < o.z) { c.blend(o); set_pixel(x, y, c); } else { o.blend(c); set_pixel(x, y, o); } } ================================================ FILE: src/image/memory_image.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef MEMORY_IMAGE #define MEMORY_IMAGE #include "image/image_base.hpp" #include "image/color.hpp" class memory_image : public image_base { private: color *colors; public: memory_image(int w, int h); ~memory_image(); void blend_pixel(pos_t x, pos_t y, color &c); void set_pixel(pos_t x, pos_t y, color&); void get_pixel(pos_t x, pos_t y, color&); void get_line(pos_t y, pos_t offset, pos_t width, color*); }; #endif /* MEMORY_IMAGE */ ================================================ FILE: src/image/virtual_image.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef VIRTUAL_IMAGE #define VIRTUAL_IMAGE #include #include "image/image_base.hpp" class virtual_image : public image_base { private: image_ptr base; pos_t x, y; public: virtual_image(pos_t w, pos_t h, image_ptr base, pos_t x, pos_t y) : image_base(w, h), base(base), x(x), y(y) { } void blend_pixel(pos_t x, pos_t y, color &c) { base->blend_pixel(this->x + x, this->y + y, c); } void set_pixel(pos_t x, pos_t y, color& c) { base->set_pixel(this->x + x, this->y + y, c); } void get_pixel(pos_t x, pos_t y, color& c) { base->get_pixel(this->x + x, this->y + y, c); } void get_line(pos_t y, pos_t x, pos_t width, color* c) { pos_t o_x = this->x + x; pos_t o_y = this->y + y; pos_t p_width = 0; if (o_x > base->get_width()) { goto exit_zero; } if (o_y > base->get_height()) { goto exit_zero; } p_width = std::min(base->get_width() - o_x, width); if (p_width == width) { base->get_line(o_y, o_x, width, c); } else { base->get_line(o_y, o_x, p_width, c); // fill up the rest with zeroes memset(c + p_width, 0x0, sizeof(color) * (width - p_width)); } return; exit_zero: memset(c, 0x0, sizeof(color) * width); } }; #endif /* VIRTUAL_IMAGE */ ================================================ FILE: src/json.cpp ================================================ #include "json.hpp" #include namespace json { void encode_string(std::ostream& os, std::string s) { unc::ustring str = unc::decode(s); unc::ustring::iterator it; os << "\""; for (it = str.begin(); it != str.end(); it++) { unc::codepoint_t c = *it; switch(c) { case '"': os << "\\\""; break; case '\\': os << "\\\\"; break; case '/': os << "\\/"; break; case '\b': os << "\\b"; break; case '\f': os << "\\f"; break; case '\n': os << "\\n"; break; case '\r': os << "\\r"; break; case '\t': os << "\\t"; break; default: std::string s; unc::encode_codepoint(c, s); os << s; break; } } os << "\""; } } ================================================ FILE: src/json.hpp ================================================ #ifndef _JSON_H_ #define _JSON_H_ #include #include #include #include namespace json { enum json_type { String, Number, Object, Array, None }; class string; class number; class object; class array; template struct Trait { const static json_type type = None; }; template<> struct Trait { const static enum json_type type = String; }; template<> struct Trait { const static enum json_type type = Number; }; template<> struct Trait { const static enum json_type type = Object; }; template<> struct Trait { const static enum json_type type = Array; }; void encode_string(std::ostream& os, std::string s); class basic_json { public: virtual ~basic_json() {}; virtual void write(std::ostream& os) = 0; virtual json_type get_type() = 0; }; class string : public basic_json { private: std::string s; public: const static json_type type = Trait::type; json_type get_type() { return Trait::type; } string(std::string s) : s(s) {} void write(std::ostream& os) { encode_string(os, s); } }; class number : public basic_json { private: int v; public: const static json_type type = Trait::type; json_type get_type() { return Trait::type; } number(int v) : v(v) {} void write(std::ostream& os) { os << v; } }; class object : public basic_json { private: std::map a; public: const static json_type type = Trait::type; json_type get_type() { return Trait::type; } void put(std::string k, basic_json* o) { a[k] = o; } void write(std::ostream& os) { os << "{"; unsigned int i = 0; std::map::iterator it; for (it = a.begin(); it != a.end(); it++) { encode_string(os, (*it).first); os << ":"; (*it).second->write(os); if (++i < a.size()) os << ","; } os << "}"; } friend std::ostream& operator<<(std::ostream& os, object& w) { w.write(os); return os; } ~object() { std::map::iterator it; for (it = a.begin(); it != a.end(); it++) { delete (*it).second; } } }; class array : public basic_json { private: std::vector a; public: const static json_type type = Trait::type; json_type get_type() { return Trait::type; } void push(basic_json* o) { a.push_back(o); } void push(basic_json& o) { a.push_back(&o); } void write(std::ostream& os) { os << "["; unsigned int i = 0; std::vector::iterator it; for (it = a.begin(); it != a.end(); it++) { (*it)->write(os); if (++i < a.size()) os << ","; } os << "]"; } friend std::ostream& operator<<(std::ostream& os, array& a) { a.write(os); return os; } ~array() { std::vector::iterator it; for (it = a.begin(); it != a.end(); it++) { delete *it; } } }; template T* cast(basic_json *b) { return reinterpret_cast(b); } } #endif /* _JSON_H_ */ ================================================ FILE: src/main.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include #include #include #include #include #include #include #include #include #include "config.hpp" #include "nullstream.hpp" #include "json.hpp" #include "marker.hpp" #include "image/memory_image.hpp" #include "image/image_base.hpp" #include "engine/engine_core.hpp" #include "generate_map.hpp" #include "generate_statistics.hpp" #include #include #include "main_utils.hpp" using namespace std; namespace fs = boost::filesystem; typedef uint64_t pos_t; const uint8_t ERROR_BYTE = 0x01; const uint8_t RENDER_BYTE = 0x10; const uint8_t COMP_BYTE = 0x20; const uint8_t IMAGE_BYTE = 0x30; const uint8_t PARSE_BYTE = 0x40; const uint8_t END_BYTE = 0xF0; inline void cout_error(const string& message) { cout << hex << std::setw(2) << setfill('0') << static_cast(ERROR_BYTE) << hex << message << flush; } inline void cout_end() { cout << hex << std::setw(2) << setfill('0') << static_cast(END_BYTE) << flush; } int do_help(ostream& out) { out << "This program was made possible because of the work and inspiration by ZomBuster and Firemark" << endl; out << "" << endl; out << "Written by Udoprog et al." << endl; out << "" << endl; out << "The following libraries are in use for this program:" << endl << " zlib (compression)" << endl << " http://www.zlib.net" << endl << " boost (thread, filesystem)" << endl << " http://www.boost.org" << endl << " libpng (portable network graphics)" << endl << " http://www.libpng.org" << endl << " libfreetype (font loading)" << endl << " http://www.freetype.org" << endl << "" << endl; # if defined(C10T_DISABLE_THREADS) out << endl; out << "C10T_DISABLE_THREADS: Threads has been disabled for this build" << endl; # endif out << endl; out << "Usage: c10t [options]" << endl; out << "Options:" << endl /*******************************************************************************/ << " -w, --world - use this world directory as input " << endl << " -o, --output - use this file as output file for generated png " << endl << " -S, --statistics - create a statistics file of the entire world " << endl << " --graph-block " << endl << " - make graph for block repartition by altitude " << endl << " with filename _graph.png " << endl << endl << " --log [file] - Specify another location for logging warnings, " << endl << " defaults to `c10t.log' " << endl << " --no-log - Suppress logging of warnings " << endl << endl << " -s, --silent - execute silently, printing nothing except errors " << endl << " -h, --help - display this help text " << endl << " -v, --version - display version information " << endl << " -D, --debug - display debug information while executing " << endl << " -l, --list-colors - list all available colors and block types " << endl << endl << "Rendering options:" << endl << " -q, --oblique - Oblique rendering " << endl << " -y, --oblique-angle - Oblique angle rendering " << endl << " -z, --isometric - Isometric rendering " << endl << " -Z, --fatiso - A fat isometric rendering (very slow) " << endl << " -r - rotate the rendering 90, 180 or 270 degrees CW " << endl << endl << " -n, --night - Night-time rendering mode " << endl << " -H, --heightmap - Heightmap rendering mode (black to white) " << endl << endl << "Filtering options:" << endl << " -e, --exclude - Exclude block-id from render (multiple occurences" << endl << " is possible) " << endl << " -i, --include - Include only this block-id in render (multiple " << endl << " occurences is possible) " << endl << " -a, --hide-all - Show no blocks except those specified with '-i' " << endl << " -c, --cave-mode - Cave mode - top down until solid block found, " << endl << " then render bottom outlines only " << endl << " --hell-mode - Hell mode - top down until non-solid block found," << endl << " then render normally (a.k.a. nether) " << endl << endl << " -t, --top - Splice from the top, must be less than 128 " << endl << " -b, --bottom - Splice from the bottom, must be greater than or " << endl << " equal to zero. " << endl << " -L, --limits - Limit render to certain area. int-list form: " << endl << " North,South,East,West, e.g. " << endl << " -L 0,100,-10,20 limiting between 0 and 100 in the" << endl << " north-south direction and between -10 and 20 in " << endl << " the east-west direction. " << endl << " Note: South and West are the positive directions." << endl << " -R, --radius - Limit render to a specific radius, useful when " << endl << " your map is absurdly large and you want a 'fast' " << endl << " limiting option. " << endl << " The default maximum radius is 1000 chunks. " << endl << " --center , - Offset the map centering on limits by chunks " << endl << " and . " << endl << endl << " -N, --no-check - Ignore missing /level.dat " << endl /*******************************************************************************/ << endl << "Performance options:" << endl << " -M, --memory-limit - Will limit the memory usage caching operations to" << endl << " file when necessary " << endl << " -C, --swap-file - Swap file to use when memory limit `-M' is " << endl << " reached, defaults to `swap.bin' " << endl << " -m, --threads - Specify the amount of threads to use, for maximum" << endl << " efficency, this should match the amount of cores " << endl << " on your machine " << endl << " --prebuffer - Specify how many jobs to prebuffer for each " << endl << " individual thread " << endl << " " << endl << " -B - Specify the base color for a specific block name " << endl << " or legacy id; has the format " << endl << " `='; <8 digit hex> specifies the " << endl << " RGBA values as `,,[,]'. " << endl << " example: `-B minecraft:grass=0,255,0,120' " << endl << " example: `-B 2=0,255,0,120' " << endl << " DEPRECATED: Use json palette files instead " << endl << " --side - Specify the side color for a specific block name " << endl << " or legacy id; has the format " << endl << " `='; <8 digit hex> specifies the " << endl << " RGBA values as `,,[,]'. " << endl << " example: `--side minecraft:grass=0,255,0,120' " << endl << " example: `--side 2=0,255,0,120' " << endl << " DEPRECATED: Use json palette files instead " << endl << " -p, --split 'px1 px2 ..' - Split the render into parts which must be pxX " << endl << " pixels squared. `output' name must contain three " << endl << " format specifiers `%d' for `level' x and y " << endl << " position. Supports multiple splits which will be " << endl << " placed on specific `level's. " << endl << " --split-base dim - Resize each tile generated by --split to `dim' " << endl << " pixels squared (default: tiles are pxX pixels " << endl << " squared) " << endl /*******************************************************************************/ << endl << "Other Options:" << endl << " -x, --binary - Will output progress information in binary form, " << endl << " good for integration with third party tools " << endl << " --require-all - Will force c10t to require all chunks or fail " << endl << " not ignoring bad chunks " << endl << " --show-players[=NICKLIST] - Will draw out player position and names from the " << endl << " players database in /players " << endl << " it is possible to define which nicks to show by " << endl << " specifying a comma separated list of nicks " << endl << " --show-signs[=PREFIX] - Will draw out signs from all chunks, if PREFIX " << endl << " is specified, only signs matching the prefix will" << endl << " be drawn " << endl << " --strip-sign-prefix - When drawing sign text, removes the match prefix " << endl << " --show-warps= - Will draw out warp positions from the specified " << endl << " warps.txt file, as used by hey0's mod " << endl << " --show-coordinates - Will draw out each chunks expected coordinates " << endl << " -P - Use as custom palette, in JSON format " << endl << " -W - Dump default palette to , in JSON format " << endl << " The default palette is no longer built-it and is " << endl << " available as `palette.json' " << endl << " --pedantic-broad-phase - Will enforce that all level chunks are parsable " << endl << " during broad phase by getting x/y/z positions " << endl << " from a quick parsing " << endl << " --no-alpha - Set non-invisible blocks color alpha channel to " << endl << " fully opaque (solid) " << endl << " --striped-terrain - Darken every other block on a vertical basis " << endl << " which helps to distinguish heights " << endl << " --write-json - Write markers to in JSON format instead of" << endl << " printing them on map " << endl << " --write-js - Same as `write-json' with the exception that the " << endl << " result will be a valid javascript file containing" << endl << " a declaration for `var c10t_json' " << endl /*******************************************************************************/ << endl << "Font Options:" << endl << " --ttf-path - Use the following ttf file when drawing text. " << endl << " defaults to `font.ttf' " << endl << " --ttf-size - Use the specified font size when drawing text. " << endl << " defaults to `12' " << endl << " --ttf-color - Use the specified color when drawing text. " << endl << " defaults to `0,0,0,255' (black) " << endl << " --sign-color - Use the specified color when drawing signs. " << endl << " defaults to " << endl << " --player-color - Use the specified color when showing players. " << endl << " defaults to " << endl << " --warp-color - Use the specified color when showing warps. " << endl << " defaults to " << endl << " --coordinate-color " << endl << " - Use the specified color when drawing coordinates." << endl << " defaults to " << endl << " --cache-key - Indicates that c10t should cache operations using" << endl << " the unique cache key , this should represent" << endl << " an unique combination of options. The cache files" << endl << " will be put in " << endl << " //c..cmap " << endl << " --cache-dir - Use the following directory as cache directory " << endl << " defaults to 'cache' if not specified " << endl << " --cache-compress - Compress the cache files using zlib compression " << endl /*******************************************************************************/ << endl; out << endl; out << "Typical usage:" << endl; out << " c10t -w /path/to/world -o /path/to/png.png" << endl; out << endl; out << " Utilize render cache and apply a 256 MB memory restriction (rest will be written to image.dat):" << endl; out << " c10t -w /path/to/world -o /path/to/png.png --cache-key='compressed' --cache-compress -M 256 -C image.dat" << endl; out << endl; out << " Print out player positions using the font `example.ttf'" << endl; out << " c10t -w /path/to/world -o /path/to/png.png --show-players --ttf-font example.ttf" << endl; out << endl; out << " Split the result into multiple files, using 10 chunks across in each file, the two number formatters will be replaced with the x/z positions of the chunks" << endl; out << " c10t -w /path/to/world -o /path/to/%d.%d.%d.png --split 10" << endl; out << endl; return 0; } int do_version(ostream& out) { out << "c10t - a cartography tool for minecraft" << endl; # if defined(C10T_DISABLE_THREADS) out << endl; out << "C10T_DISABLE_THREADS: Threads have been disabled for this build" << endl; out << endl; # endif out << "version: " << C10T_VERSION << ", built on " << __DATE__ << endl; out << "by: " << C10T_CONTACT << endl; out << "site: " << C10T_SITE << endl; return 0; } int main(int argc, char *argv[]){ nullstream nil; ostream out(cout.rdbuf()); ofstream out_log; vector hints; out.precision(2); out.setf(ios_base::fixed); mc::initialize_constants(); fs::path executable_dir = fs::system_complete(fs::path(argv[0]).remove_filename()); settings_t s(executable_dir); if (!read_opts(s, argc, argv)) { goto exit_error; } if (!do_read_palette(s, s.palette_read_path)) { goto exit_error; } out << "Successfully read palette from " << s.palette_read_path << endl; BOOST_FOREACH(std::string exclude, s.excluded) { mc::MaterialT *material; if (!get_blocktype(exclude, material)) { error << "Unkown material for exclusion " << exclude << endl; goto exit_error; } material->enabled = false; } BOOST_FOREACH(std::string include, s.included) { mc::MaterialT *material; if (!get_blocktype(include, material)) { error << "Unkown material for inclusion " << include << endl; goto exit_error; } material->enabled = true; } BOOST_FOREACH(std::string top_override, s.top_color_overrides) { if (!do_base_color_set(top_override.c_str())) { error << "Invalid top color override " << top_override << endl; goto exit_error; } } BOOST_FOREACH(std::string side_override, s.side_color_overrides) { if (!do_side_color_set(side_override.c_str())) { error << "Invalid side color override " << side_override << endl; goto exit_error; } } if(s.disable_alpha) { for (size_t i = 0; i < mc::MaterialTable.size(); i++) { if (!mc::MaterialTable[i].top.is_invisible()) { mc::MaterialTable[i].top.a = color_i_to_f[255]; } if (!mc::MaterialTable[i].side.is_invisible()) { mc::MaterialTable[i].side.a = color_i_to_f[255]; } } } switch(s.action) { case Version: return do_version(out); case Help: return do_help(out); case ListColors: return do_colors(out); case WritePalette: if (!do_write_palette(s, s.palette_write_path)) { goto exit_error; } out << "Successfully wrote palette to " << s.palette_write_path << endl; return 0; case None: error << "No action specified, please type `c10t -h' for help"; goto exit_error; default: break; } if (s.binary) { out.rdbuf(out_log.rdbuf()); } if (s.silent) { out.rdbuf(nil.rdbuf()); } if (!s.no_log) { out_log.open(path_string(s.output_log).c_str()); out_log << "START LOG" << endl; } if (s.memory_limit_default) { hints.push_back("To use less memory, specify a memory limit with `-M ', if it is reached c10t will swap to disk instead"); } if (s.cache_use) { if (!fs::is_directory(s.cache_dir)) { error << "Directory required for caching: " << path_string(s.cache_dir); goto exit_error; } // then create the subdirectory using cache_key s.cache_dir = s.cache_dir / s.cache_key; if (!fs::is_directory(s.cache_dir)) { out << "Creating directory for caching: " << path_string(s.cache_dir) << endl; fs::create_directories(s.cache_dir); } { out << "Caching to directory: " << s.cache_dir << std::endl; out << "Cache compression: " << (s.cache_compress ? "ON" : "OFF") << std::endl; } } if (s.world_path.empty()) { error << "You must specify a world to render using `-w '"; goto exit_error; } if (!s.nocheck) { fs::path level_dat = s.world_path / "level.dat"; if (!fs::exists(level_dat)) { error << "Does not exist: " << path_string(level_dat); goto exit_error; } } /* hell mode requires entering the subdirectory DIM-1 */ if (s.hellmode) { s.world_path = s.world_path / "DIM-1"; } if (!fs::is_directory(s.world_path)) { if (!fs::is_directory(s.world_path)) { error << "Does not exist: " << path_string(s.world_path); goto exit_error; } } switch(s.action) { case GenerateWorld: /* do some nice sanity checking prior to generating since this might * catch a couple of errors */ if (s.output_path.empty()) { error << "You must specify output file using `-o '"; goto exit_error; } if (!fs::is_directory(s.output_path.parent_path())) { error << "Output directory does not exist: " << s.output_path; goto exit_error; } if (s.use_split) { try { boost::format(s.output_path.stem().string()) % 0 % 0; } catch (boost::io::too_many_args& e) { error << "The `-o' parameter must contain two number format specifiers `%d' (x and y coordinates) - example: -o out/base.%d.%d.png"; goto exit_error; } } if (!generate_map(out, out_log, error, s, hints, s.world_path, s.output_path)) { goto exit_error; } break; case GenerateStatistics: if (!generate_statistics(out, out_log, error, s, hints, s.world_path, s.statistics_path)) { goto exit_error; } break; default: error << "No action specified"; goto exit_error; } if (hints.size() > 0 || warnings.size() > 0) { int i = 1; for (vector::iterator it = warnings.begin(); it != warnings.end(); it++) { out << "WARNING " << i++ << ": " << *it << endl; } i = 1; for (vector::iterator it = hints.begin(); it != hints.end(); it++) { out << "Hint " << i++ << ": " << *it << endl; } out << endl; } if (s.binary) { cout_end(); } else { out << argv[0] << ": all done!" << endl; } mc::deinitialize_constants(); if (!s.no_log) { out << "Log written to " << path_string(s.output_log) << endl; out_log << "END LOG" << endl; out_log.close(); } return 0; exit_error: if (s.binary) { cout_error(error.str()); } else { out << argv[0] << ": " << error.str() << endl; } mc::deinitialize_constants(); if (!s.no_log) { out << "Log written to " << path_string(s.output_log) << endl; out_log << "END LOG" << endl; out_log.close(); } return 1; } ================================================ FILE: src/main_utils.cpp ================================================ #include "main_utils.hpp" #include #include #include #include #include #include #include #include #include #include "mc/blocks.hpp" bool parse_color(const std::string value, color& c); bool parse_set(const char* set_str, int& blockid, color& c); // Convert a string such as "-30,40,50,30" to the corresponding N,S,E,W integers, // and fill in the min/max settings. bool parse_limits(const std::string& limits_str, settings_t& s); bool read_set(std::set& set, const std::string s); bool do_write_palette(settings_t& s, const boost::filesystem::path& path); bool do_read_palette(settings_t& s, const boost::filesystem::path& path); namespace fs = boost::filesystem; using namespace std; using namespace boost; vector hints; vector warnings; stringstream error; template void boost_split(C& collection, const string& s) { char_separator sep(" \t\n\r,:"); typedef tokenizer > tokenizer; tokenizer tokens(s, sep); for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != tokens.end(); ++tok_iter) { collection.insert(collection.end(), *tok_iter); } } bool get_blocktype(const std::string& block_string, mc::MaterialT*& block_type) { // First attempt modern name lookup std::map::iterator it = mc::MaterialMap.find(block_string); if (it == mc::MaterialMap.end()) { // Perhaps it is an old identifier then // TODO: also accept id:meta format try { int id = lexical_cast(block_string); int meta = 0; boost::optional material = mc::get_material_legacy(id, meta); if (material) { block_type = material.get(); return true; } } catch(const bad_lexical_cast& e) { // ignore string to integer translation errors } error << "Block is nither a known block name nor a known numeric block identifier; " << block_string; return false; } else { block_type = it->second; return true; } } bool parse_color(const string value, color& c) { std::vector parts; boost_split(parts, value); int cr, cg, cb, ca=0xff; if (parts.size() < 3) { error << "Color sets must be of the form ,,[,] but was: " << value; return false; } try { cr = lexical_cast(parts[0]); cg = lexical_cast(parts[1]); cb = lexical_cast(parts[2]); if (parts.size() == 4) { ca = lexical_cast(parts[3]); } } catch(const bad_lexical_cast& e) { error << "Cannot be converted to a color: " << value; return false; } if (!( cr >= 0 && cr <= 0xff && cg >= 0 && cg <= 0xff && cb >= 0 && cb <= 0xff && ca >= 0 && ca <= 0xff)) { error << "Color values must be between 0-255: " << value; return false; } c.r = color_i_to_f[cr]; c.g = color_i_to_f[cg]; c.b = color_i_to_f[cb]; c.a = color_i_to_f[ca]; return true; } bool parse_set(const char* set_str, mc::MaterialT*& block_type, color& c) { istringstream iss(set_str); string block_name, color_string; assert(getline(iss, block_name, '=')); assert(getline(iss, color_string)); if (!get_blocktype(block_name, block_type)) { return false; } if (!parse_color(color_string, c)) { return false; } return true; } bool do_base_color_set(const char *set_str) { mc::MaterialT *material; color color_top; if (parse_set(set_str, material, color_top)) { material->top = color_top; return true; } return false; } bool do_side_color_set(const char *set_str) { mc::MaterialT *material; color color_side; if (parse_set(set_str, material, color_side)) { material->side = color_side; return true; } return false; } // Convert a string such as "-30,40,50,30" to the corresponding N,S,E,W integers, // and fill in the min/max settings. bool parse_limits(const string& limits_str, settings_t& s) { std::vector limits; boost_split(limits, limits_str); if (limits.size() != 4) { error << "Limit argument must of format: ,,,"; return false; } try { s.min_x = lexical_cast(limits[0]); s.max_x = lexical_cast(limits[1]); s.min_z = lexical_cast(limits[2]); s.max_z = lexical_cast(limits[3]); } catch(const bad_lexical_cast& e) { error << "Cannot be converted to set of numbers: " << limits_str; return false; } return true; } bool parse_tuple(const string& str, settings_t& s, int& a, int& b) { std::vector parts; boost_split(parts, str); if (parts.size() != 2) { error << "Tuple must be of format: ,"; return false; } try { a = lexical_cast(parts[0]); b = lexical_cast(parts[1]); } catch(const bad_lexical_cast& e) { error << "Cannot be converted to set of numbers: " << str; return false; } return true; } bool read_set(std::set& set, const string s) { boost_split(set, s); if (set.size() == 0) { error << "List must specify items separated by comma `,'"; return false; } return true; } bool do_write_palette(settings_t& s, const fs::path& path) { std::ofstream pal(path.string().c_str()); mc::MaterialMode_tr_to_string materialmode_tr; bool first_material = true; pal << "[" << std::endl; BOOST_FOREACH(mc::MaterialT &material, mc::MaterialTable) { if (!material.enabled) { continue; } if (first_material) { first_material = false; } else { pal << "," << std::endl; } pal << " {" << std::endl; pal << " \"namespace\": \"" << material.mc_namespace << "\"," << std::endl; pal << " \"material\": \"" << material.name << "\"," << std::endl; pal << " \"mode\": \"" << materialmode_tr.get_value(material.mode).get() << "\"," << std::endl; pal << " \"top_color\": [" << material.top << "]," << std::endl; pal << " \"side_color\": [" << material.side << "]," << std::endl; pal << " \"darken\": false," << std::endl; if (material.legacy_ids.size() == 1) { pal << " \"legacy_id\": " << material.legacy_ids[0] << "," << std::endl; } else if (material.legacy_ids.size() > 1) { pal << " \"legacy_id\": ["; bool first_id = true; BOOST_FOREACH(int i, material.legacy_ids) { if (first_id) { first_id = false; } else { pal << ", "; } pal << i; } pal << "]" << std::endl; } if (material.legacy_ids.size() > 0) { pal << " \"legacy_meta\": " << material.legacy_meta << std::endl; } pal << " }"; } pal << "]" << std::endl; if (pal.fail()) { error << "Failed to write palette to " << path; return false; } return true; } int do_colors(std::ostream& out) { out << "List of material Colors (total: " << mc::MaterialTable.size() << ")" << endl; BOOST_FOREACH(mc::MaterialT &material, mc::MaterialTable) { bool print_separator = false; BOOST_FOREACH(int i, material.legacy_ids) { if (print_separator) { out << ", "; } out << i << ":" << material.legacy_meta; print_separator = true; } if (print_separator) { out << "; "; } out << material.mc_namespace << ":" << material.name << " = top(" << material.top << "), side(" << material.side << ")" << endl; } return 0; } std::vector int_vector_from_json(boost::property_tree::ptree parsed_json) { std::vector result = std::vector(); boost::optional single = parsed_json.get_value_optional(); if (single) { result = { single.get() }; } else { result.reserve(4); BOOST_FOREACH(boost::property_tree::ptree::value_type &entry, parsed_json) { single = entry.second.get_value_optional(); if (entry.first.empty() && single) { result.push_back(single.get()); } else { error << "Error while parsing integer array" << std::endl; } } result.shrink_to_fit(); } return result; } color color_from_json(const boost::property_tree::ptree color_json) { boost::property_tree::ptree::const_iterator it = color_json.begin(); if (it == color_json.end()) { throw "not a color"; } int r = it->second.get_value(); it++; if (it == color_json.end()) { throw "not a color"; } int g = it->second.get_value(); it++; if (it == color_json.end()) { throw "not a color"; } int b = it->second.get_value(); it++; if (it == color_json.end()) { throw "not a color"; } int a = it->second.get_value(); it++; if (it != color_json.end()) { throw "not a color"; } return color(r, g, b, a); } struct color_translator { typedef boost::property_tree::ptree internal_type; typedef color external_type; boost::optional get_value(const boost::property_tree::ptree &pt) { try { return boost::optional(color_from_json(pt)); } catch(...) { } return boost::optional(); } }; bool do_read_palette(settings_t& s, const fs::path& path) { boost::property_tree::ptree parsed_json; try { std::ifstream json_src(path.string().c_str()); boost::property_tree::read_json(json_src, parsed_json); json_src.close(); } catch(boost::property_tree::json_parser::json_parser_error& e) { return false; } BOOST_FOREACH(boost::property_tree::ptree::value_type &mc_block_entry, parsed_json) { // flat JSON list expected, of which entries have no name (.first). boost::property_tree::ptree block_entry = mc_block_entry.second; boost::optional mc_namespace = block_entry.get_optional("namespace"); boost::optional material = block_entry.get_optional("material"); mc::MaterialMode_tr_from_string materialmode_tr; boost::optional mode = block_entry.get_optional("mode", materialmode_tr); color_translator color_tr; boost::optional top_color = boost::optional(); if (boost::optional node = block_entry.get_child_optional("top_color")) { top_color = color_tr.get_value(node.get()); } boost::optional side_color = boost::optional(); if (boost::optional node = block_entry.get_child_optional("side_color")) { side_color = color_tr.get_value(node.get()); } boost::optional darken_side = block_entry.get_optional("darken"); std::vector legacy_id; if (boost::optional node = block_entry.get_child_optional("legacy_id")) { legacy_id = int_vector_from_json(node.get()); } boost::optional legacy_meta = block_entry.get_optional("legacy_meta"); if (mc_namespace && material) { if (legacy_id.size() == 0 && legacy_meta) { error << "Invalid entry; " << mc_namespace.get() << ":" << material.get() << " has invalid legacy attributes." << std::endl; } else if(!mode) { error << "Invalid entry; " << mc_namespace.get() << ":" << material.get() << " is missing the material mode attribute." << std::endl; } else if (legacy_meta && (legacy_meta.get() < 0 || legacy_meta.get() >= 16)) { error << "Invalid entry; " << mc_namespace.get() << ":" << material.get() << " has an invalid legacy meta value, it must be >= 0 and < 16." << std::endl; } else if (mode.get() == mc::MaterialMode::LegacySlab && legacy_id.size() != 2) { error << "Invalid entry; " << mc_namespace.get() << ":" << material.get() << " has invalid legacy mode; exactly two legacy ids are required." << std::endl; } else { if (!top_color) { error << "Warning; block " << mc_namespace.get() << ":" << material.get() << " has no top color set" << std::endl; } color top = top_color.value_or(mc::SharedDefaultColor); color side = side_color.value_or(top); if (!darken_side || darken_side.get()) { side.darken(0x20); } mc::MaterialTable.push_back(mc::MaterialT { mc_namespace.get(), material.get(), mode.get(), top, side, legacy_id, legacy_meta.value_or(0), s.enable_all_blocks }); } } else { error << "Invalid entry; entry is missing namespace or material attribute." << std::endl; } } // Whenever the MaterialTable is changed the palette lookup // tables needs to be regenerated. mc::reload_palette(); return true; } int flag; struct option long_options[] = { {"world", required_argument, 0, 'w'}, {"output", required_argument, 0, 'o'}, {"top", required_argument, 0, 't'}, {"bottom", required_argument, 0, 'b'}, {"limits", required_argument, 0, 'L'}, {"radius", required_argument, 0, 'R'}, {"memory-limit", required_argument, 0, 'M'}, {"cache-file", required_argument, 0, 'C'}, {"swap-file", required_argument, 0, 'C'}, {"exclude", required_argument, 0, 'e'}, {"include", required_argument, 0, 'i'}, {"rotate", required_argument, 0, 'r'}, {"threads", required_argument, 0, 'm'}, {"help", no_argument, 0, 'h'}, {"silent", no_argument, 0, 's'}, {"version", no_argument, 0, 'v'}, {"debug", no_argument, 0, 'D'}, {"list-colors", no_argument, 0, 'l'}, {"hide-all", no_argument, 0, 'a'}, {"no-check", no_argument, 0, 'N'}, {"oblique", no_argument, 0, 'q'}, {"oblique-angle", no_argument, 0, 'y'}, {"isometric", no_argument, 0, 'z'}, {"fatiso", no_argument, 0, 'Z'}, {"cave-mode", no_argument, 0, 'c'}, {"night", no_argument, 0, 'n'}, {"heightmap", no_argument, 0, 'H'}, {"binary", no_argument, 0, 'x'}, {"require-all", no_argument, &flag, 0}, {"show-players", optional_argument, &flag, 1}, {"ttf-path", required_argument, &flag, 2}, {"ttf-size", required_argument, &flag, 3}, {"ttf-color", required_argument, &flag, 4}, {"show-coordinates", no_argument, &flag, 5}, {"pedantic-broad-phase", no_argument, &flag, 6}, {"show-signs", optional_argument, &flag, 7}, {"sign-color", required_argument, &flag, 8}, {"player-color", required_argument, &flag, 9}, {"coordinate-color", required_argument, &flag, 10}, {"cache-key", required_argument, &flag, 11}, {"cache-dir", required_argument, &flag, 12}, {"cache-compress", no_argument, &flag, 13}, {"no-alpha", no_argument, &flag, 14}, {"striped-terrain", no_argument, &flag, 15}, {"write-json", required_argument, &flag, 16}, {"write-js", required_argument, &flag, 26}, {"write-markers", required_argument, &flag, 21}, {"split", required_argument, 0, 'p'}, {"split-base", required_argument, &flag, 27}, {"show-warps", required_argument, &flag, 18}, {"warp-color", required_argument, &flag, 19}, {"prebuffer", required_argument, &flag, 20}, {"hell-mode", no_argument, &flag, 22}, {"statistics", optional_argument, 0, 'S'}, {"log", required_argument, &flag, 24}, {"no-log", no_argument, &flag, 25}, {"center", required_argument, &flag, 30}, {"graph-block", required_argument, &flag, 31}, {"strip-sign-prefix", no_argument, &flag, 32}, {"engine", required_argument, &flag, 64}, {"side", required_argument, &flag, 65}, {0, 0, 0, 0} }; bool read_opts(settings_t& s, int argc, char* argv[]) { int c; int option_index; while ((c = getopt_long(argc, argv, "DNvxcnHqzZyalshM:C:L:R:w:o:e:t:b:i:m:r:W:P:B:S:p:", long_options, &option_index)) != -1) { if (c == 0) { switch (flag) { case 0: s.require_all = true; break; case 1: s.show_players = true; if (optarg != NULL) { if (!read_set(s.show_players_set, optarg)) { return false; } } break; case 2: s.ttf_path = optarg; if (access(optarg, R_OK) == -1) { error << "ttf path cannot be accessed"; return false; } break; case 3: s.ttf_size = atoi(optarg); if (s.ttf_size <= 0) { error << "ttf-size must be greater than 0"; return false; } break; case 4: if (!parse_color(optarg, s.ttf_color)) { return false; } break; case 5: s.show_coordinates = true; break; case 6: s.pedantic_broad_phase = true; break; case 7: s.show_signs = true; if (optarg) { s.show_signs_filter = optarg; if (s.show_signs_filter.empty()) { error << "Sign filter must not be empty string"; return false; } } break; case 8: if (!parse_color(optarg, s.sign_color)) { return false; } s.has_sign_color = true; break; case 9: if (!parse_color(optarg, s.player_color)) { return false; } s.has_player_color = true; break; case 10: if (!parse_color(optarg, s.coordinate_color)) { return false; } s.has_coordinate_color = true; break; case 11: s.cache_use = true; s.cache_key = optarg; break; case 12: s.cache_dir = optarg; break; case 13: s.cache_compress = true; break; case 14: s.disable_alpha = true; break; case 15: s.striped_terrain = true; break; case 21: hints.push_back("`--write-markers' has been deprecated in favour of `--write-json' - use that instead and note the new json structure"); case 16: s.write_json = true; s.write_json_path = fs::system_complete(fs::path(optarg)); { fs::path parent = s.write_json_path.parent_path(); if (!fs::is_directory(parent)) { error << "Not a directory: " << parent.string(); return false; } } break; case 26: s.write_js = true; s.write_js_path = fs::system_complete(fs::path(optarg)); { fs::path parent = s.write_js_path.parent_path(); if (!fs::is_directory(parent)) { error << "Not a directory: " << parent.string(); return false; } } break; case 27: try { s.split_base = boost::lexical_cast(optarg); } catch(boost::bad_lexical_cast& e) { error << "Cannot be converted to number: " << optarg; return false; } if (!(s.split_base >= 1)) { error << "split argument must be greater or equal to one"; return false; } break; case 18: s.show_warps = true; s.show_warps_path = fs::system_complete(fs::path(optarg)); break; case 19: if (!parse_color(optarg, s.warp_color)) { return false; } s.has_warp_color = true; break; case 20: s.prebuffer = atoi(optarg); if (s.prebuffer <= 0) { error << "Number of prebuffered jobs must be more than 0"; return false; } break; case 22: s.hellmode = true; break; case 24: s.output_log = fs::system_complete(fs::path(optarg)); break; case 25: s.no_log = true; break; case 30: if (!parse_tuple(optarg, s, s.center_x, s.center_z)) { return false; } break; case 31: s.graph_block = optarg; break; case 32: s.strip_sign_prefix = true; break; case 64: s.engine_path = fs::system_complete(fs::path(optarg)); if (!fs::is_regular_file(s.engine_path)) { error << optarg << ": not a file"; return false; } s.engine_use = true; break; case 65: s.side_color_overrides.push_back(optarg); } continue; } switch (c) { case 'v': s.action = Version; break; case 'h': s.action = Help; break; case 'm': s.threads = atoi(optarg); if (s.threads <= 0) { error << "Number of worker threads must be more than 0"; return false; } break; case 'q': s.mode = Oblique; break; case 'z': s.mode = Isometric; break; case 'Z': s.mode = FatIso; break; case 'D': s.debug = true; break; case 'y': s.mode = ObliqueAngle; break; case 'a': s.enable_all_blocks = false; break; case 'i': s.included.push_back(optarg); break; case 'e': s.excluded.push_back(optarg); break; case 'w': s.world_path = fs::system_complete(fs::path(optarg)); break; case 'o': s.action = GenerateWorld; s.output_path = fs::system_complete(fs::path(optarg)); break; case 's': s.silent = true; break; case 'x': s.binary = true; break; case 'r': s.rotation = atoi(optarg) % 360; if (s.rotation < 0) { s.rotation += 360; } if (s.rotation % 90 != 0) { error << "Rotation must be a multiple of 90 degrees"; return false; } break; case 'N': s.nocheck = true; break; case 'n': s.night = true; break; case 'H': s.heightmap = true; break; case 'c': s.cavemode = true; break; case 't': s.top = atoi(optarg); if (!(s.top > s.bottom && s.top < mc::MapY)) { error << "Top limit must be between ` - " << mc::MapY << "', not " << s.top; return false; } break; case 'L': if (!parse_limits(optarg, s)) { return false; } break; case 'R': s.max_radius = boost::lexical_cast(optarg); if (s.max_radius < 1) { error << "Radius must be greater than zero"; return false; } if (s.max_radius > 0xffffffff) { error << "Radius too big"; return false; } break; case 'b': s.bottom = atoi(optarg); if (!(s.bottom < s.top && s.bottom >= 0)) { error << "Bottom limit must be between `0 - ', not " << s.bottom; return false; } break; case 'l': s.action = ListColors; break; case 'M': { s.memory_limit = boost::lexical_cast(optarg); if (s.memory_limit <= 0) { error << "Memory limit must be non-negative value, not " << s.memory_limit; return false; } s.memory_limit_default = false; } break; case 'C': s.swap_file = fs::system_complete(fs::path(optarg)); break; case 'W': s.palette_write_path = optarg; break; case 'P': s.palette_read_path = optarg; break; case 'B': s.top_color_overrides.push_back(optarg); case 'S': s.action = GenerateStatistics; if (optarg != NULL) { s.statistics_path = fs::system_complete(fs::path(optarg)); } break; case 'p': { std::list result; std::string split_string(optarg); boost_split(result, split_string); BOOST_FOREACH(std::string str, result) { unsigned int split_int = 0; try { split_int = boost::lexical_cast(str); } catch(boost::bad_lexical_cast& e) { error << "Cannot be converted to number: " << str; return false; } if (!(split_int >= 1)) { error << "split argument must be greater or equal to one"; return false; } s.split.push_back(split_int); } s.use_split = true; } break; case '?': if (optopt == 'c') error << "Option -" << optopt << " requires an argument"; else if (isprint (optopt)) error << "Unknown option `-" << optopt << "'"; else error << "Unknown option character `\\x" << std::hex << static_cast(optopt) << "'."; return false; default: error << "Unknown getopt error : ) - congrats, you broke it"; return false; } } if (!s.palette_write_path.empty()) { s.action = WritePalette; } return true; } ================================================ FILE: src/main_utils.hpp ================================================ #ifndef __MAIN_UTILS_HPP__ #define __MAIN_UTILS_HPP__ #include #include #include #include #include #include "image/color.hpp" #include "settings_t.hpp" #include "mc/blocks.hpp" #include class application_error : std::exception { private: const char* message; public: application_error(const char* message) : message(message) {} const char* what() const throw() { return message; } }; bool do_write_palette(settings_t& s, const boost::filesystem::path& path); bool do_read_palette(settings_t& s, const boost::filesystem::path& path); bool get_blocktype(const std::string& block_string, mc::MaterialT*& block_type); bool read_opts(settings_t& s, int argc, char* argv[]); int do_colors(std::ostream& out); bool do_base_color_set(const char *set_str); bool do_side_color_set(const char *set_str); extern std::vector hints; extern std::vector warnings; extern std::stringstream error; #endif /* __MAIN_UTILS_HPP__ */ ================================================ FILE: src/marker.cpp ================================================ #include "marker.hpp" marker::marker(std::string text, std::string type, text::font_face font, int x, int y, int z) : text(text), type(type), font(font), x(x), y(y), z(z) { } std::string marker::get_text() { return text; } std::string marker::get_type() { return type; } text::font_face marker::get_font() { return font; } int marker::get_x() { return x; } int marker::get_y() { return y; } int marker::get_z() { return z; } ================================================ FILE: src/marker.hpp ================================================ #ifndef __MARKER_HPP__ #define __MARKER_HPP__ #include #include #include #include "settings_t.hpp" #include "text.hpp" #include "players.hpp" #include "mc/blocks.hpp" #include "mc/marker.hpp" #include "mc/level_info.hpp" #include "mc/rotated_level_info.hpp" #include "mc/world.hpp" struct marker { public: marker(std::string text, std::string type, text::font_face font, int x, int y, int z); std::string get_text(); std::string get_type(); text::font_face get_font(); int get_x(); int get_y(); int get_z(); private: std::string text; std::string type; text::font_face font; int x; int y; int z; }; #endif /*__MARKER_HPP__*/ ================================================ FILE: src/mc/CMakeLists.txt ================================================ add_library( c10t-mc blocks.cpp level.cpp utils.cpp world.cpp region.cpp level_info.cpp region_iterator.cpp rotated_level_info.cpp dynamic_buffer.cpp marker.cpp ) set_target_properties(c10t-mc PROPERTIES COMPILE_FLAGS "-O3 -Wall -pedantic -g") ================================================ FILE: src/mc/blocks.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include #include #include #include #include "blocks.hpp" namespace mc { const color SharedInvisColor = color(0, 0, 0, 0); const color SharedDefaultColor = color(0, 0, 0, 0xff); const int MapX = 0x10; const int MapZ = 0x10; const int MapY = 0x100; std::vector MaterialTable; std::map MaterialMap; std::vector> MaterialPaletteLegacy; // Regenerate the internal lookup tables for materials (by name and by legacy id+meta). // The internal lookup tables use direct pointer into the primary material list; // therfore any changes to the size of that list must be directly followed by a call // to this method or undefined behaviour can be expected. void reload_palette() { MaterialMap.clear(); MaterialPaletteLegacy.clear(); MaterialTable.shrink_to_fit(); MaterialPaletteLegacy.resize(MaterialTable.size()); MaterialT *materials = MaterialTable.data(); for (size_t i = 0; i < MaterialTable.size(); i++) { MaterialT *material = &materials[i]; std::string fq_material = material->mc_namespace + ":" + material->name; std::pair::iterator, bool> const& result = MaterialMap.insert(std::map::value_type(fq_material, material)); if (!result.second) { std::cerr << "Palette loader: Duplicated name " << fq_material << std::endl; // For now the last parsed material shall take precedence, this is similar // to the legacy loader below. result.first->second = material; } BOOST_FOREACH(int j, material->legacy_ids) { if (material->legacy_meta >= 0 && material->legacy_meta < 16) { if (MaterialPaletteLegacy[j].size() == 0 && material->legacy_meta == 0) { MaterialPaletteLegacy[j].resize(1); } else if (MaterialPaletteLegacy[j].size() <= static_cast(material->legacy_meta)) { MaterialPaletteLegacy[j].resize(16); } if (MaterialPaletteLegacy[j][material->legacy_meta]) { MaterialT *previous = MaterialPaletteLegacy[j][material->legacy_meta]; std::cerr << "Legacy Palette loader: " << fq_material << " is overwriting " << previous->mc_namespace << ":" << previous->name << std::endl; } MaterialPaletteLegacy[j][material->legacy_meta] = material; } else { // This code should not be reached; unless a programming error is present or memory corruption occurred. // JSON loader code shall refuse to load blocks that would result in this state. std::cerr << "Legacy Palette loader: Invalid internal state for " << fq_material << ", legacy lookup cannot be initailized." << std::endl; } } } } void initialize_constants() { } void deinitialize_constants() { MaterialMap.clear(); MaterialPaletteLegacy.clear(); } } ================================================ FILE: src/mc/blocks.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _BLOCKS_H_ #define _BLOCKS_H_ #include #include #include #include "image/color.hpp" namespace mc { enum MaterialMode { Block, HalfBlock, TorchBlock, LargeFlowerBlock, LogBlock, LegacySlab, LegacyLeaves }; enum LegacyBlocks { Air = 0x00, Grass = 0x02, Leaves = 0x12 }; const char MATERIAL_MODE_BLOCK[] = "block"; const char MATERIAL_MODE_HALF_BLOCK[] = "half_block"; const char MATERIAL_MODE_TORCH_BLOCK[] = "torch_block"; const char MATERIAL_MODE_LARGE_FLOWER_BLOCK[] = "large_plant_block"; const char MATERIAL_MODE_LOG_BLOCK[] = "log_block"; const char MATERIAL_MODE_LEGACY_SLAB[] = "legacy_slab_block"; const char MATERIAL_MODE_LEGACY_LEAVES[] = "legacy_leaf_block"; struct MaterialMode_tr_to_string { typedef MaterialMode internal_type; typedef std::string external_type; boost::optional get_value(const MaterialMode &m) { switch(m) { case MaterialMode::Block: return boost::make_optional(MATERIAL_MODE_BLOCK); break; case MaterialMode::HalfBlock: return boost::make_optional(MATERIAL_MODE_HALF_BLOCK); break; case MaterialMode::TorchBlock: return boost::make_optional(MATERIAL_MODE_TORCH_BLOCK); break; case MaterialMode::LargeFlowerBlock: return boost::make_optional(MATERIAL_MODE_LARGE_FLOWER_BLOCK); break; case MaterialMode::LogBlock: return boost::make_optional(MATERIAL_MODE_LOG_BLOCK); break; case MaterialMode::LegacySlab: return boost::make_optional(MATERIAL_MODE_LEGACY_SLAB); break; case MaterialMode::LegacyLeaves: return boost::make_optional(MATERIAL_MODE_LEGACY_LEAVES); break; default: return boost::optional(); } } }; struct MaterialMode_tr_from_string { typedef std::string internal_type; typedef MaterialMode external_type; boost::optional get_value(const std::string &s) { if (MATERIAL_MODE_BLOCK == s) { return boost::make_optional(MaterialMode::Block); } else if (MATERIAL_MODE_HALF_BLOCK == s) { return boost::make_optional(MaterialMode::HalfBlock); } else if (MATERIAL_MODE_TORCH_BLOCK == s) { return boost::make_optional(MaterialMode::TorchBlock); } else if (MATERIAL_MODE_LARGE_FLOWER_BLOCK == s) { return boost::make_optional(MaterialMode::LargeFlowerBlock); } else if (MATERIAL_MODE_LOG_BLOCK == s) { return boost::make_optional(MaterialMode::LogBlock); } else if (MATERIAL_MODE_LEGACY_SLAB == s) { return boost::make_optional(MaterialMode::LegacySlab); } else if (MATERIAL_MODE_LEGACY_LEAVES == s) { return boost::make_optional(MaterialMode::LegacyLeaves); } return boost::optional(); } }; void reload_palette(); void initialize_constants(); void deinitialize_constants(); extern const color SharedInvisColor; extern const color SharedDefaultColor; extern const int MapY; extern const int MapX; extern const int MapZ; typedef struct { std::string mc_namespace; std::string name; MaterialMode mode; color top; color side; std::vector legacy_ids; int legacy_meta; bool enabled; } MaterialT; extern std::vector MaterialTable; extern std::map MaterialMap; extern std::vector> MaterialPaletteLegacy; inline boost::optional get_material_legacy(int material, int data) { // Legacy lookup needs to aggressivly fallback to the first metadata // variant when possible, this is because some blocks have either only // one variant with multiple states or even multiple // variants with multiple states. if (material >= 0 && static_cast(material) < MaterialPaletteLegacy.size()) { if (data < 0) { data = 0; } unsigned int udata = static_cast(data); std::vector subpallet = MaterialPaletteLegacy[material]; if (subpallet.size() == 1) { udata = 0; } if (udata < subpallet.size() && subpallet[udata]) { return boost::optional(subpallet[udata]); } else if(subpallet.size() > 0 && subpallet[0]) { return subpallet[0]; } } std::cout << "Unkown legacy material (" << std::hex << material << ":" << std::hex << data << ")" << std::endl; return boost::optional(); } inline color get_color_legacy(int material, int data) { boost::optional blocktype = get_material_legacy(material, data); if (blocktype) { return blocktype.get()->top; } else { return SharedDefaultColor; } } inline color get_side_color_legacy(int material, int data) { boost::optional blocktype = get_material_legacy(material, data); if (blocktype) { return blocktype.get()->side; } else { return SharedDefaultColor; } } inline color get_color_legacy(int material) { return get_color_legacy(material, 0); } inline color get_side_color_legacy(int material) { return get_side_color_legacy(material, 0); } } #endif /* _BLOCKS_H_ */ ================================================ FILE: src/mc/dynamic_buffer.cpp ================================================ #include "mc/dynamic_buffer.hpp" #include namespace mc { dynamic_buffer::dynamic_buffer(size_t size) : factor(1), factor_max(DEFAULT_FACTOR_MAX), size(size), buffer_size(size), buffer(new char[size]) { } dynamic_buffer::dynamic_buffer(size_t size, int factor_max) : factor(1), factor_max(factor_max), size(size), buffer_size(size), buffer(new char[size]) { } dynamic_buffer::~dynamic_buffer() { delete [] buffer; } size_t dynamic_buffer::get_size() { return buffer_size; } char* dynamic_buffer::get() { return buffer; } /** * Expand the buffer and return the amount it has been expanded with. */ size_t dynamic_buffer::expand() { if (factor >= factor_max) { return 0; } factor += 1; size_t new_size = factor * size; char* new_buffer = new char[new_size]; memcpy(new_buffer, buffer, buffer_size); delete[] buffer; size_t expanded_size = new_size - buffer_size; buffer = new_buffer; buffer_size = new_size; return expanded_size; } } ================================================ FILE: src/mc/dynamic_buffer.hpp ================================================ #ifndef _MC_DYNAMIC_BUFFER_HPP #define _MC_DYNAMIC_BUFFER_HPP #include namespace mc { /* * A dynamic buffer can be used when you need a fixed sized buffer that can * be expanded. * * This is useful if you have a shared buffer of optimal size which might * expand, but won't in the normal case. */ class dynamic_buffer { public: enum { DEFAULT_FACTOR_MAX = 16 }; dynamic_buffer(size_t size); dynamic_buffer(size_t size, int factor_max); ~dynamic_buffer(); size_t get_size(); char* get(); /** * Expand the buffer and return the amount it has been expanded with. */ size_t expand(); private: int factor; int factor_max; size_t size; size_t buffer_size; char* buffer; }; } #endif /* _MC_DYNAMIC_BUFFER_HPP */ ================================================ FILE: src/mc/level.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "mc/level.hpp" #include "nbt/nbt.hpp" #include "mc/region.hpp" #include "mc/level_info.hpp" #include #include /* Compound() { Compound(Level) { List(Entities, TAG_Byte, 0): [ ] ByteArray(Biomes): (256 bytes) Long(LastUpdate): 7446079 Int(xPos): -1 Int(zPos): -1 List(TileEntities, TAG_Compound, 0): [ ] Byte(TerrainPopulated): 0x1 IntArray(HeightMap): (256 ints) List(Sections, TAG_Compound, 1): [ Compound() { ByteArray(SkyLight): (2048 bytes) ByteArray(BlockLight): (2048 bytes) Byte(Y): 0x0 ByteArray(BlockStates): (4096 bytes) } ] } } */ namespace mc { enum section_name { Level, Sections, None }; struct level_context { boost::shared_ptr Level; bool error; size_t error_where; const char* error_why; Section_Compound* tmp_Section; section_name p[64]; int pos; level_context() : error(false), error_where(0), error_why("") { this->Level.reset(new Level_Compound); this->pos = 0; } }; inline bool in_level_section(level_context* C) { return C->pos == 4 && C->p[0] == None && C->p[1] == Level && C->p[2] == Sections && C->p[3] == None; } void begin_compound(level_context* C, nbt::String name) { if (name.compare("Level") == 0) { C->p[C->pos++] = Level; return; } C->p[C->pos++] = None; if (in_level_section(C)) { C->tmp_Section = new Section_Compound; C->tmp_Section->Y = 0; } } void end_compound(level_context* C, nbt::String name) { if (in_level_section(C)) { if (!C->tmp_Section->Data) { std::cout << "missing Data" << std::endl; } if (!C->tmp_Section->SkyLight) { std::cout << "missing SkyLight" << std::endl; } if (!C->tmp_Section->BlockLight) { std::cout << "missing BlockLight" << std::endl; } if (!C->tmp_Section->Blocks) { std::cout << "missing Blocks" << std::endl; } C->Level->Sections.push_back(C->tmp_Section); C->tmp_Section = NULL; } --C->pos; } void begin_list(level_context* C, nbt::String name, nbt::Byte type, nbt::Int count) { if (name.compare("Sections") == 0) { C->p[C->pos++] = Sections; return; } C->p[C->pos++] = None; } void end_list(level_context* C, nbt::String name) { --C->pos; } void register_string(level_context* C, nbt::String name, nbt::String value) { } void register_byte(level_context* C, nbt::String name, nbt::Byte value) { if (in_level_section(C)) { if (name.compare("Y") == 0) { C->tmp_Section->Y = value; return; } } } void register_int(level_context* C, nbt::String name, nbt::Int i) { } void register_int_array(level_context* C, nbt::String name, nbt::IntArray* int_array) { } void register_byte_array(level_context* C, nbt::String name, nbt::ByteArray* byte_array) { if (in_level_section(C)) { if (name.compare("Data") == 0) { C->tmp_Section->Data.reset(byte_array); return; } if (name.compare("SkyLight") == 0) { C->tmp_Section->SkyLight.reset(byte_array); return; } if (name.compare("BlockLight") == 0) { C->tmp_Section->BlockLight.reset(byte_array); return; } if (name.compare("Blocks") == 0) { C->tmp_Section->Blocks.reset(byte_array); return; } } delete byte_array; } void register_long_array(level_context* C, nbt::String name, nbt::LongArray* long_array) { delete long_array; } void error_handler(level_context* C, size_t where, const char *why) { C->error = true; C->error_where = where; C->error_why = why; } level::~level(){ } level::level(level_info_ptr _level_info) : _level_info(_level_info) {} std::string level::get_path() { return _level_info->get_path(); } bool level::operator<(const level& other) const { return _level_info->get_coord() < other._level_info->get_coord(); } time_t level::modification_time() { return _level_info->modification_time(); } boost::shared_ptr level::get_level() { return Level; } /** * might throw invalid_file if the file is not gramatically correct */ void level::read(dynamic_buffer& buffer) { level_context context; nbt::Parser parser(&context); parser.register_byte_array = register_byte_array; parser.register_int_array = register_int_array; parser.register_long_array = register_long_array; parser.register_byte = register_byte; parser.register_string = register_string; parser.register_int = register_int; parser.begin_compound = begin_compound; parser.begin_list = begin_list; parser.end_list = end_list; parser.end_compound = end_compound; parser.error_handler = error_handler; std::stringstream oss; uint32_t len; try { len = _level_info->get_region()->read_data(_level_info->get_x(), _level_info->get_z(), buffer); } catch(mc::bad_region& e) { throw invalid_file(e.what()); } std::string chunk_data = oss.str(); parser.parse_buffer(buffer.get(), len); if (context.error) { throw invalid_file(context.error_why); } if (!context.Level) { throw invalid_file("not a level data file"); } Level = context.Level; } } ================================================ FILE: src/mc/level.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef __MC_LEVEL_HPP__ #define __MC_LEVEL_HPP__ #include #include #include #include "mc/dynamic_buffer.hpp" #include "mc/utils.hpp" #include "mc/marker.hpp" #include "nbt/types.hpp" namespace mc { namespace fs = boost::filesystem; class level_info; class level; class region; typedef boost::shared_ptr level_info_ptr; typedef boost::shared_ptr level_ptr; typedef boost::shared_ptr region_ptr; class invalid_file : std::exception { private: const char* message; public: invalid_file(const char* message) : message(message) {} const char* what() const throw() { return message; } }; struct Section_Compound { boost::shared_ptr Blocks; boost::shared_ptr Data; boost::shared_ptr SkyLight; boost::shared_ptr BlockLight; nbt::Byte Y; }; struct Level_Compound { nbt::Int xPos; nbt::Int zPos; boost::shared_ptr HeightMap; boost::ptr_vector Sections; }; class level { public: level(level_info_ptr _level_info); ~level(); std::string get_path(); time_t modification_time(); /* * might throw invalid_file if the file is not gramatically correct */ void read(dynamic_buffer& buffer); boost::shared_ptr get_level(); bool operator<(const level& other) const; private: level_info_ptr _level_info; // these must be public for the parser to be able to reach them. std::vector signs; boost::shared_ptr Level; }; } #endif /* __MC_LEVEL_HPP__ */ ================================================ FILE: src/mc/level_info.cpp ================================================ #include "mc/level_info.hpp" #include "mc/region.hpp" namespace mc { level_info::level_info() : coord() { } level_info::level_info(region_ptr _region, int x, int z) : _region(_region), coord(x, z) { } level_info::level_info(region_ptr _region, utils::level_coord coord) : _region(_region) { utils::level_coord rc = utils::path_to_region_coord(_region->get_path()); this->coord = utils::level_coord(rc.get_x() + coord.get_x(), rc.get_z() + coord.get_z()); } std::string level_info::get_path() { std::stringstream ss; ss << _region->get_path().string() << "(" << coord.get_x() << "," << coord.get_z() << ")"; return ss.str(); } time_t level_info::modification_time() { return _region->read_modification_time(coord.get_x(), coord.get_z()); } region_ptr level_info::get_region() { return _region; } bool level_info::operator<(const level_info& other) const { return coord < other.coord; } level_info level_info::rotate(int degrees) { return level_info(_region, coord.rotate(degrees)); } int level_info::get_x() { return coord.get_x(); } int level_info::get_z() { return coord.get_z(); } const utils::level_coord level_info::get_coord() { return coord; } } ================================================ FILE: src/mc/level_info.hpp ================================================ #ifndef _MC_LEVEL_INFO_HPP #define _MC_LEVEL_INFO_HPP #include #include "mc/utils.hpp" #include namespace mc { class level_info; class region; class level_info { public: typedef boost::shared_ptr region_ptr; typedef boost::shared_ptr level_info_ptr; level_info(); level_info(region_ptr _region, int x, int z); level_info(region_ptr _region, utils::level_coord coord); std::string get_path(); region_ptr get_region(); time_t modification_time(); bool operator<(const level_info& other) const; level_info rotate(int degrees); int get_x(); int get_z(); const utils::level_coord get_coord(); private: region_ptr _region; utils::level_coord coord; fs::path path; }; } #endif /* _MC_LEVEL_INFO_HPP */ ================================================ FILE: src/mc/marker.cpp ================================================ #include "mc/marker.hpp" namespace mc { marker::marker(std::string text, int x, int y, int z) : text(text), x(x), y(y), z(z) { } std::string marker::get_text() { return text; } int marker::get_x() { return x; } int marker::get_y() { return y; } int marker::get_z() { return z; } } ================================================ FILE: src/mc/marker.hpp ================================================ #ifndef _MC_MARKER_HPP #define _MC_MARKER_HPP #include namespace mc { /* * designates a text related to a position * Possible usages: * Signs * Player Positions * Custom Markers **/ class marker { public: marker(std::string text, int x, int y, int z); std::string get_text(); int get_x(); int get_y(); int get_z(); private: std::string text; int x; int y; int z; }; } #endif /* _MC_MARKER_HPP */ ================================================ FILE: src/mc/region.cpp ================================================ #include "mc/region.hpp" #include #include #include namespace mc { class zerror : public std::exception { private: const char* message; public: zerror(const char* message) : message(message) { } ~zerror() throw() { } const char* what() const throw() { return message; } }; class zstream { public: zstream() : strm(new z_stream) { strm->zalloc = (alloc_func)NULL; strm->zfree = (free_func)NULL; strm->opaque = NULL; inflateInit(strm.get()); } ~zstream() { inflateEnd(strm.get()); } uint32_t get_avail_out() { return strm->avail_out; } uint32_t get_avail_in() { return strm->avail_in; } void set_in(Bytef* b, uint32_t len) { strm->next_in = b; strm->avail_in = len; } void set_out(Bytef* b, uint32_t len) { strm->next_out = b; strm->avail_out = len; } bool in_empty() { return strm->avail_in <= 0; } bool out_empty() { return strm->avail_out <= 0; } void inflate() { int status = ::inflate(strm.get(), Z_NO_FLUSH); switch (status) { case Z_ERRNO: throw zerror("failed to inflate data (Z_ERRNO)"); case Z_STREAM_ERROR: throw zerror("failed to inflate data (Z_STREAM_ERROR)"); case Z_DATA_ERROR: throw zerror("failed to inflate data (Z_DATA_ERROR)"); case Z_MEM_ERROR: throw zerror("failed to inflate data (Z_MEM_ERROR)"); case Z_BUF_ERROR: throw zerror("failed to inflate data (Z_BUF_ERROR)"); case Z_VERSION_ERROR: throw zerror("failed to inflate data (Z_VERSION_ERROR)"); default: break; } } private: boost::scoped_ptr strm; }; region::region(fs::path path) : path(path), in_buffer(CHUNK_MAX) { } void region::read_header() { header.reset(new char[HEADER_SIZE]); std::fstream fp(path.string().c_str(), std::ios::in | std::ios::binary); if (fp.fail()) { throw bad_region(path, "failed to open region"); } fp.read(header.get(), HEADER_SIZE); if (fp.fail()) { throw bad_region(path, "failed to read header area"); } } inline unsigned int region::get_offset(unsigned int x, unsigned int z) const { return 4 * ((x&31) + (z&31) * REGION_SIZE); } chunk_offset region::read_chunk_offset(unsigned int x, unsigned int z) const { if (!header) { throw bad_region(path, "header has not been loaded"); } int o = get_offset(x, z); uint8_t buf[HEADER_RECORD_SIZE]; ::memcpy(reinterpret_cast(buf), &header[o], HEADER_RECORD_SIZE); chunk_offset co; co.sector_number = ((buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8)) >> 8; co.sector_count = uint8_t(buf[3]); return co; } time_t region::read_modification_time(unsigned int x, unsigned int z) const { if (!header) { throw bad_region(path, "header has not been loaded"); } int o = get_offset(x, z) + RECORD_SIZE; uint8_t buf[HEADER_RECORD_SIZE]; ::memcpy(reinterpret_cast(buf), &header[o], HEADER_RECORD_SIZE); uint32_t mtime = ((buf[0] << 24) || (buf[1] << 16) || (buf[2] << 8)) || buf[3]; return mtime; } uint32_t region::read_data(int x, int z, dynamic_buffer& buffer) { chunk_offset co = read_chunk_offset(x, z); uint8_t buf[5]; std::fstream fp(path.string().c_str(), std::ios::in | std::ios::binary); if (fp.fail()) { throw bad_region(path, "failed to open file"); } fp.seekg(RECORD_SIZE * co.sector_number); if (fp.fail()) { throw bad_region(path, "could not seek"); } fp.read(reinterpret_cast(buf), 5); if (fp.fail()) { throw bad_region(path, "could not read chunk header"); } uint32_t len = (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + (buf[3]); uint8_t version = buf[4]; if (version != 2) { throw bad_region(path, "bad chunk version"); } while (len > in_buffer.get_size()) { if (in_buffer.expand() == 0) { throw bad_region(path, "region input buffer requires too much memory"); } } fp.read(in_buffer.get(), len); if (fp.fail()) { throw bad_region(path, "could not read chunk"); } fp.close(); zstream strm; size_t pos = 0; strm.set_in(reinterpret_cast(in_buffer.get()), len - 1); strm.set_out(reinterpret_cast(buffer.get() + pos), buffer.get_size()); do { try { strm.inflate(); } catch(const zerror& e) { throw bad_region(path, e.what()); } if (!strm.in_empty()) { uint32_t expanded = buffer.expand(); if (expanded == 0) { throw bad_region(path, "region output buffer requires too much memory"); } pos += expanded; strm.set_out(reinterpret_cast(buffer.get() + pos), expanded); } } while(!strm.in_empty()); return buffer.get_size() - strm.get_avail_out(); } fs::path region::get_path() { return path; } } ================================================ FILE: src/mc/region.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _MC_REGION_HPP_ #define _MC_REGION_HPP_ #include #include #include #include "mc/utils.hpp" #include "mc/dynamic_buffer.hpp" #include #include #include #include namespace fs = boost::filesystem; namespace mc { class bad_region : public std::exception { private: const fs::path path; const char* message; public: bad_region(const fs::path path, const char* message) : path(path), message(message) { } ~bad_region() throw() { } const char* what() const throw() { return message; } const fs::path where() { return path; } }; typedef struct chunk_offset { uint32_t sector_number; uint8_t sector_count; } chunk_offset; class region; typedef boost::shared_ptr region_ptr; class region { public: enum { HEADER_SIZE = 8192, REGION_SIZE = 32, HEADER_RECORD_SIZE = 4, RECORD_SIZE = 4096, CHUNK_MAX = 1024 * 128 } constants; private: fs::path path; boost::shared_array header; dynamic_buffer in_buffer; public: region(fs::path path); void read_header(); inline unsigned int get_offset(unsigned int x, unsigned int z) const; chunk_offset read_chunk_offset(unsigned int x, unsigned int z) const; time_t read_modification_time(unsigned int x, unsigned int z) const; template void read_coords(T& coll) const { using mc::utils::level_coord; for (int z = 0; z < REGION_SIZE; z++) { for (int x = 0; x < REGION_SIZE; x++) { chunk_offset co = read_chunk_offset(x, z); if (co.sector_number != 0) { level_coord c(x, z); coll.push_back(c); } } } } uint32_t read_data(int x, int z, dynamic_buffer& buffer); fs::path get_path(); }; } #endif /*_MC_REGION_HPP_*/ ================================================ FILE: src/mc/region_inspect.cpp ================================================ #include "mc/region.hpp" #include "mc/utils.hpp" #include "nbt/nbt.hpp" #include #include #include #include using namespace std; struct inspect_context { int width; }; void begin_compound(inspect_context* inspect, nbt::String name) { cout << setw(inspect->width) << "" << "Compound(" << name << ") {" << endl; inspect->width += 2; } void end_compound(inspect_context* inspect, nbt::String name) { inspect->width -= 2; cout << setw(inspect->width) << "" << "}" << endl; } void begin_list(inspect_context* inspect, nbt::String name, nbt::Byte type, nbt::Int length) { cout << setw(inspect->width) << "" << "List(" << name << ", " << nbt::tag_string_map[type] << ", " << length << "): [" << endl; inspect->width += 2; } void end_list(inspect_context* inspect, nbt::String name) { inspect->width -= 2; cout << setw(inspect->width) << "" << "]" << endl; } void register_long(inspect_context* inspect, nbt::String name, nbt::Long value) { cout << setw(inspect->width) << "" << "Long(" << name << "): " << dec << value << endl; } void register_short(inspect_context* inspect, nbt::String name, nbt::Short value) { cout << setw(inspect->width) << "" << "Short(" << name << "): " << value << endl; } void register_string(inspect_context* inspect, nbt::String name, nbt::String value) { cout << setw(inspect->width) << "" << "String(" << name << "): " << value << endl; } void register_float(inspect_context* inspect, nbt::String name, nbt::Float value) { cout << setw(inspect->width) << "" << "Float(" << name << "): " << value << endl; } void register_double(inspect_context* inspect, nbt::String name, nbt::Double value) { cout << setw(inspect->width) << "" << "Double(" << name << "): " << value << endl; } void register_int(inspect_context* inspect, nbt::String name, nbt::Int value) { cout << setw(inspect->width) << "" << "Int(" << name << "): " << dec << value << endl; } void register_byte(inspect_context* inspect, nbt::String name, nbt::Byte value) { cout << setw(inspect->width) << "" << "Byte(" << name << "): 0x" << hex << (int)value << endl; } void register_byte_array(inspect_context* inspect, nbt::String name, nbt::ByteArray* value) { cout << setw(inspect->width) << "" << "ByteArray(" << name << "): " << "(" << dec << int(value->length) << " bytes)" << endl; delete value; } void register_int_array(inspect_context* inspect, nbt::String name, nbt::IntArray* value) { cout << setw(inspect->width) << "" << "IntArray(" << name << "): " << "(" << dec << int(value->length) << " ints)" << endl; delete value; } void register_long_array(inspect_context* inspect, nbt::String name, nbt::LongArray* value) { cout << setw(inspect->width) << "" << "LongArray(" << name << "): " << "(" << dec << int(value->length) << " longs)" << endl; delete value; } void error_handler(inspect_context* ctx, size_t where, const char* why) { std::cout << where << ":" << why << std::endl; } int main(int argc, char* argv[]) { using mc::utils::level_coord; if (argc < 2) { return 1; } inspect_context ctx; nbt::Parser parser(&ctx); parser.error_handler = error_handler; parser.begin_compound = begin_compound; parser.end_compound = end_compound; parser.begin_list = begin_list; parser.end_list = end_list; parser.register_long = register_long; parser.register_short = register_short; parser.register_string = register_string; parser.register_float = register_float; parser.register_double = register_double; parser.register_int = register_int; parser.register_byte = register_byte; parser.register_byte_array = register_byte_array; parser.register_int_array = register_int_array; parser.register_long_array = register_long_array; mc::region region(argv[1]); list coords; std::cout << "Reading Header" << std::endl; try { region.read_header(); } catch(mc::bad_region& e) { std::cout << region.get_path() << ": " << e.what() << std::endl; return 1; } region.read_coords(coords); mc::dynamic_buffer buffer(mc::region::CHUNK_MAX); BOOST_FOREACH(level_coord c, coords) { std::cout << "BUFFER SIZE = " << std::dec << buffer.get_size() << std::endl; try { int len = region.read_data(c.get_x(), c.get_z(), buffer); std::cout << "CHUNK SIZE: " << std::dec << int(len) << std::endl; ctx.width = 0; parser.parse_buffer(buffer.get(), len); } catch(mc::bad_region& e) { std::cout << region.get_path() << ": " << e.what() << std::endl; } } } ================================================ FILE: src/mc/region_iterator.cpp ================================================ #include "mc/region_iterator.hpp" #include "mc/region.hpp" #include "dirlist.hpp" bool dir_filter(const std::string& name) { if (name.compare(0, 3, "DIM") == 0) return false; if (name.compare("players") == 0) return false; return true; } bool file_filter(const std::string& name) { if (name.length() < 8) return false; if (name.compare(name.length() - 4, 4, ".mca") != 0) return false; if (name.compare(0, 2, "r.") != 0) return false; return true; } namespace mc { region_iterator::region_iterator(const fs::path path) : root(path), lister(new dirlist(path)) { } bool region_iterator::has_next() { if (!lister->has_next(dir_filter, file_filter)) { return false; } fs::path next = lister->next(); current_region.reset(new region(next)); return true; } region_ptr region_iterator::next() { return current_region; } } ================================================ FILE: src/mc/region_iterator.hpp ================================================ #ifndef _MC_REGION_ITERATOR_HPP #define _MC_REGION_ITERATOR_HPP #include #include #include class dirlist; namespace fs = boost::filesystem; namespace mc { class level_info; class region; /** * Dynamically iterates over, and instantiates regions. */ class region_iterator { public: typedef boost::shared_ptr region_ptr; typedef boost::shared_ptr level_info_ptr; region_iterator(const fs::path path); bool has_next(); boost::shared_ptr next(); private: fs::path root; boost::shared_ptr lister; std::list current_levels; region_ptr current_region; }; } #endif /* _MC_REGION_ITERATOR_HPP */ ================================================ FILE: src/mc/rotated_level_info.cpp ================================================ #include "mc/rotated_level_info.hpp" #include "mc/level_info.hpp" namespace mc { rotated_level_info::rotated_level_info( level_info_ptr level, utils::level_coord coord ) : level(level), coord(coord) { } bool rotated_level_info::operator<(const rotated_level_info& other) const { return coord < other.coord; } rotated_level_info::level_info_ptr rotated_level_info::get_level() { return level; } utils::level_coord rotated_level_info::get_coord() { return coord; } } ================================================ FILE: src/mc/rotated_level_info.hpp ================================================ #ifndef _MC_ROTATED_LEVEL_INFO_HPP #define _MC_ROTATED_LEVEL_INFO_HPP #include #include "mc/utils.hpp" namespace mc { class level_info; class rotated_level_info { public: typedef boost::shared_ptr level_info_ptr; rotated_level_info(level_info_ptr level, utils::level_coord coord); bool operator<(const rotated_level_info& other) const; level_info_ptr get_level(); utils::level_coord get_coord(); private: level_info_ptr level; mc::utils::level_coord coord; }; } #endif /* _MC_ROTATED_LEVEL_INFO_HPP */ ================================================ FILE: src/mc/utils.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "mc/utils.hpp" #include #include #include #include namespace mc { namespace utils { using std::string; void split(std::vector& v, const std::string& str, char delim) { std::stringstream ss(str); std::string item; while(std::getline(ss, item, delim)) { v.push_back(item); } } const char *b36alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"; string b36encode(int number) { if (number == 0) { return string("0"); } std::stringstream ss; if (number < 0) { ss << "-"; number = -number; } std::vector v; while (number != 0) { div_t d = div(number, 36); v.push_back(b36alphabet[d.rem]); number = d.quot; } for (std::vector::reverse_iterator it = v.rbegin(); it!=v.rend(); ++it) { ss << *it; } return ss.str(); } int b36decode(const string num) { if (num.empty()) throw bad_cast(); long int res = strtol(num.c_str(), NULL, 36); if (errno == ERANGE) throw bad_cast(); return res; } int b10decode(const string num) { if (num.empty()) throw bad_cast(); long int res = strtol(num.c_str(), NULL, 10); if (errno == ERANGE) throw bad_cast(); return res; } fs::path level_dir(const fs::path base, int x, int z) { int modx = x % 64; if (modx < 0) modx += 64; int modz = z % 64; if (modz < 0) modz += 64; return base / b36encode(modx) / b36encode(modz); } fs::path level_path(const fs::path base, int x, int z, const string prefix, const string suffix) { return level_dir(base, x, z) / ((prefix + ".") + b36encode(x) + "." + b36encode(z) + ("." + suffix)); } level_coord path_to_level_coord(const fs::path path) { if (!fs::is_regular_file(path)) { throw invalid_argument("not a regular file"); } string extension = path.extension().string(); std::vector parts; split(parts, path.stem().string(), '.'); if (parts.size() != 3 || extension.compare(".dat") != 0) { throw invalid_argument("level data file name does not match ..dat"); } try { return level_coord(b36decode(parts.at(1)), b36decode(parts.at(2))); } catch(const bad_cast& e) { throw invalid_argument("could not decode coordinates from file name"); } } level_coord path_to_region_coord(const fs::path path) { if (!fs::is_regular_file(path)) { throw invalid_argument("not a regular file"); } string extension = path.extension().string(); std::vector parts; split(parts, path.stem().string(), '.'); if (parts.size() != 3 || extension.compare(".mca") != 0) { throw invalid_argument("level data file name does not match ..mca"); } try { return level_coord(b10decode(parts.at(1)) * 32, b10decode(parts.at(2)) * 32); } catch(const bad_cast& e) { throw invalid_argument("could not decode coordinates from file name"); } } } } ================================================ FILE: src/mc/utils.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef __MC_UTILS_HPP__ #define __MC_UTILS_HPP__ #include #include namespace fs = boost::filesystem; namespace mc { namespace utils { /** * The fastest portable split I could find, only * limitation is that it splits on only one character, but that's O.K. * And yes - this actually has an effective impact on performance since * it is heavily used during broad phase scanning. **/ void split(std::vector& v, const std::string& str, char delim); class invalid_argument : public std::exception { private: const char* message; public: invalid_argument(const char* message) : message(message) {} const char* what() const throw() { return message; } }; class bad_cast : public std::exception {}; std::string b36encode(int number); int b36decode(const std::string number); fs::path level_dir(const fs::path base, int x, int z); fs::path level_path(const fs::path base, int x, int z, const std::string prefix, const std::string suffix); /* * Used to indicate a level coordinate. */ struct level_coord { private: int x; int z; public: level_coord() : x(0), z(0) {} level_coord(int x, int z) : x(x), z(z) {} int get_x() const { return x; }; int get_z() const { return z; }; level_coord add_x(int x) { return level_coord(this->x + x, this->z); }; level_coord add_z(int z) { return level_coord(this->x, this->z + z); }; level_coord rotate(int rotation) const { int x = this->x; int z = this->z; int t = x; switch (rotation % 360) { case 270: x = z; z = -t; break; case 180: x = -x; z = -z; break; case 90: x = -z; z = t; break; } return level_coord(x, z); } bool operator<(const level_coord& other) const { if (z < other.z) { return true; } if (z > other.z) { return false; } return x < other.x; } }; /* * Take a path to a level chunk file, and return the coordinates. * Indicate that no coordinates are viable by throwing invalid_argument **/ level_coord path_to_level_coord(const fs::path path); level_coord path_to_region_coord(const fs::path path); } } #endif /* __MC_UTILS_HPP__ */ ================================================ FILE: src/mc/world.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "world.hpp" #include "dirlist.hpp" #include "mc/utils.hpp" #include "mc/blocks.hpp" #include "mc/region_iterator.hpp" #include "mc/level_info.hpp" namespace mc { world::world(fs::path world_path) : world_path(world_path), min_x(INT_MAX), min_z(INT_MAX), max_x(INT_MIN), max_z(INT_MIN), chunk_x(0), chunk_y(0) { } region_iterator world::get_iterator() { return region_iterator(world_path); } void world::update(utils::level_coord coord) { min_x = std::min(min_x, coord.get_x()); max_x = std::max(max_x, coord.get_x()); min_z = std::min(min_z, coord.get_z()); max_z = std::max(max_z, coord.get_z()); diff_x = max_x - min_x; diff_z = max_z - min_z; min_xp = min_x * mc::MapX; min_zp = min_z * mc::MapZ; } } ================================================ FILE: src/mc/world.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef __MC_WORLD_HPP__ #define __MC_WORLD_HPP__ #include #include #include #include #include #include "fileutils.hpp" #include #include #include #include namespace mc { namespace fs = boost::filesystem; class level_info; class region_iterator; class iterator_error : public std::exception { private: const char* message; public: iterator_error(const char* message) : message(message) { } ~iterator_error() throw() { } const char* what() const throw() { return message; } }; class world { public: fs::path world_path; int min_x, min_z, max_x, max_z; // the difference between min_* and max_* int diff_x, diff_z; // min_* as a point int min_xp, min_zp; int chunk_x, chunk_y; world(fs::path path); region_iterator get_iterator(); void update(utils::level_coord coord); }; } #endif /* __MC_WORLD_HPP__ */ ================================================ FILE: src/nbt/CMakeLists.txt ================================================ add_library(c10t-nbt nbt.cpp ${ZLIB_LIBRARIES}) set_target_properties(c10t-nbt PROPERTIES COMPILE_FLAGS "-O3 -Wall -pedantic -g") ================================================ FILE: src/nbt/nbt.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "nbt.hpp" bool nbt::is_big_endian() { int32_t i = 1; return ((int8_t*)(&i))[0] == 0; } ================================================ FILE: src/nbt/nbt.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _NBT_H_ #define _NBT_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nbt/types.hpp" namespace nbt { class bad_grammar : std::exception {}; #define NBT_STACK_SIZE 100 #define nbt_assert_error(exc_env, file, cond, why) \ do { \ if (!(cond)) { \ size_t where = file->tell(); \ error_handler(context, where, why); \ longjmp(exc_env, 1); \ } \ } while(0) #define assert_error_c(exc_env, file, cond, why, cleanup) \ do { \ if (!(cond)) { \ size_t where = file->tell(); \ error_handler(context, where, why); \ cleanup; \ longjmp(exc_env, 1); \ } \ } while(0) class input_buffer { public: typedef int pos_t; virtual pos_t read(void* target, pos_t len) = 0; virtual pos_t tell() = 0; virtual pos_t flush(pos_t len) = 0; virtual bool empty() = 0; virtual bool ok() = 0; virtual ~input_buffer() { } }; typedef boost::shared_ptr input_buffer_ptr; class memory_buffer : public input_buffer { private: const char* buffer; pos_t offs; pos_t size; public: memory_buffer(const char* buffer, pos_t size) : buffer(buffer), offs(0), size(size) { } virtual pos_t read(void* target, pos_t len) { if (!(offs + len <= size)) { return -1; } ::memcpy(target, &buffer[offs], len); offs += len; return len; } virtual pos_t tell() { return offs; } virtual pos_t flush(pos_t len) { if (!(offs + len <= size)) { return -1; } offs += len; return len; } virtual bool empty() { return offs == size; } virtual bool ok() { return true; } }; class gzfile_buffer : public input_buffer { private: const char* path; const pos_t flush_buffer_size; char* flush_buffer; gzFile file; enum { FILE_READY, // Special case for resolving gzeof behaviour in empty test. FLUSH_BUFFER_SINGLE_BYTE, END_OF_FILE } file_state; public: gzfile_buffer(const char* path) : path(path), flush_buffer_size(1024), flush_buffer(new char[flush_buffer_size]), file_state(FILE_READY) { file = gzopen(path, "rb"); } virtual ~gzfile_buffer() { if (file != NULL) { gzclose(file); } delete [] flush_buffer; } virtual pos_t read(void* target, pos_t len) { char* c_target = reinterpret_cast(target); pos_t read = 0; if (file_state == FLUSH_BUFFER_SINGLE_BYTE && len > 0) { c_target[0] = flush_buffer[0]; file_state = FILE_READY; read++; } while (read < len) { pos_t in = gzread(file, c_target + read, len - read); if (in == 0) { return -1; } read += in; } return len; } virtual pos_t tell() { off_t offset = 0; if (file_state == FLUSH_BUFFER_SINGLE_BYTE) { offset = -1; } return gztell(file) + offset; } virtual pos_t flush(pos_t len) { pos_t flushed = 0; while (flushed < len) { pos_t in = read(flush_buffer, std::min(len - flushed, flush_buffer_size)); if (in == -1) { return -1; } flushed += in; } return len; } virtual bool empty() { if (file_state == FILE_READY) { // gzeof will not return EOF until EOF has previously been detected // during a read. pos_t in = read(flush_buffer, 1); if (in == -1) { file_state = END_OF_FILE; } else { file_state = FLUSH_BUFFER_SINGLE_BYTE; } } return gzeof(file) == 1; } virtual bool ok() { return file != NULL; } }; const Byte TAG_End = 0x0; const Byte TAG_Byte = 0x1; const Byte TAG_Short = 0x2; const Byte TAG_Int = 0x3; const Byte TAG_Long = 0x4; const Byte TAG_Float = 0x5; const Byte TAG_Double = 0x6; const Byte TAG_Byte_Array = 0x7; const Byte TAG_String = 0x8; const Byte TAG_List = 0x9; const Byte TAG_Compound = 0xa; const Byte TAG_Int_Array = 0xb; const Byte TAG_Long_Array = 0xc; const std::string TAG_End_str("TAG_End"); const std::string TAG_Byte_str("TAG_Byte"); const std::string TAG_Short_str("TAG_Short"); const std::string TAG_Int_str("TAG_Int"); const std::string TAG_Long_str("TAG_Long"); const std::string TAG_Float_str("TAG_Float"); const std::string TAG_Double_str("TAG_Double"); const std::string TAG_Byte_Array_str("TAG_Byte_Array"); const std::string TAG_String_str("TAG_String"); const std::string TAG_List_str("TAG_List"); const std::string TAG_Compound_str("TAG_Compound"); const std::string TAG_Int_Array_str("TAG_Int_Array"); const std::string TAG_Long_Array_str("TAG_Long_Array"); const std::string tag_string_map[] = { TAG_End_str, TAG_Byte_str, TAG_Short_str, TAG_Int_str, TAG_Long_str, TAG_Float_str, TAG_Double_str, TAG_Byte_Array_str, TAG_String_str, TAG_List_str, TAG_Compound_str, TAG_Int_Array_str, TAG_Long_Array_str }; bool is_big_endian(); template void default_begin_compound(C* context, nbt::String name) { } template void default_end_compound(C* context, String name) { } template void default_begin_list(C* context, nbt::String name, nbt::Byte type, nbt::Int length) { } template void default_end_list(C* context, nbt::String name) { } template void default_error_handler(C* context, size_t where, const char *why) { std::cerr << "Unhandled nbt parser error at byte " << where << ": " << why << std::endl; exit(1); } template class Parser { private: jmp_buf exc_env; bool running; C *context; inline Byte read_byte(input_buffer_ptr file) { Byte b; nbt_assert_error(exc_env, file, file->read(&b, sizeof(Byte)) == sizeof(Byte), "Buffer too short to read Byte"); return b; } inline Short read_short(input_buffer_ptr file) { uint8_t b[2]; nbt_assert_error(exc_env, file, file->read(b, sizeof(b)) == sizeof(b), "Buffer to short to read Short"); Short s = (b[0] << 8) + b[1]; return s; } inline Int read_int(input_buffer_ptr file) { Int i; #ifdef BOOST_BIG_ENDIAN nbt_assert_error(exc_env, file, file->read(&i, sizeof(b)) == sizeof(b), "Buffer to short to read Int"); #else Byte b[sizeof(i)]; nbt_assert_error(exc_env, file, file->read(b, sizeof(b)) == sizeof(b), "Buffer to short to read Int"); Int *ip = &i; *((Byte*)ip) = b[3]; *((Byte*)ip + 1) = b[2]; *((Byte*)ip + 2) = b[1]; *((Byte*)ip + 3) = b[0]; #endif return i; } inline String read_string(input_buffer_ptr file) { Short s = read_short(file); nbt_assert_error(exc_env, file, s >= 0, "String specified with invalid length < 0"); uint8_t *str = new uint8_t[s + 1]; assert_error_c(exc_env, file, file->read(str, s) == s, "Buffer to short to read String", delete str); String so((const char*)str, s); delete [] str; return so; } inline void flush_string(input_buffer_ptr file) { Short s = read_short(file); nbt_assert_error(exc_env, file, file->flush(s) != -1, "Buffer to short to flush String"); } inline Float read_float(input_buffer_ptr file) { Float f; #ifdef BOOST_BIG_ENDIAN nbt_assert_error(exc_env, file, file->read(&f, sizeof(f)) == sizeof(f), "Buffer to short to read Float"); #else Byte b[sizeof(f)]; nbt_assert_error(exc_env, file, file->read(b, sizeof(f)) == sizeof(f), "Buffer to short to read Float"); Float *fp = &f; *((Byte*)fp) = b[3]; *((Byte*)fp + 1) = b[2]; *((Byte*)fp + 2) = b[1]; *((Byte*)fp + 3) = b[0]; #endif return f; } inline Long read_long(input_buffer_ptr file) { Long l; #ifdef BOOST_BIG_ENDIAN nbt_assert_error(exc_env, file, file->read(&l, sizeof(b)) == sizeof(b), "Buffer to short to read Long"); #else Byte b[sizeof(l)]; nbt_assert_error(exc_env, file, file->read(b, sizeof(b)) == sizeof(b), "Buffer to short to read Long"); Long *lp = &l; *((Byte*)lp) = b[7]; *((Byte*)lp + 1) = b[6]; *((Byte*)lp + 2) = b[5]; *((Byte*)lp + 3) = b[4]; *((Byte*)lp + 4) = b[3]; *((Byte*)lp + 5) = b[2]; *((Byte*)lp + 6) = b[1]; *((Byte*)lp + 7) = b[0]; #endif return l; } inline Double read_double(input_buffer_ptr file) { Double d; #ifdef BOOST_BIG_ENDIAN nbt_assert_error(exc_env, file, file->read(&d, sizeof(d)) == sizeof(d), "Buffer to short to read Double"); #else Byte b[sizeof(d)]; nbt_assert_error(exc_env, file, file->read(b, sizeof(d)) == sizeof(d), "Buffer to short to read Double"); Double *dp = &d; *((Byte*)dp) = b[7]; *((Byte*)dp + 1) = b[6]; *((Byte*)dp + 2) = b[5]; *((Byte*)dp + 3) = b[4]; *((Byte*)dp + 4) = b[3]; *((Byte*)dp + 5) = b[2]; *((Byte*)dp + 6) = b[1]; *((Byte*)dp + 7) = b[0]; #endif return d; } inline Byte read_tagType(input_buffer_ptr file) { Byte type = read_byte(file); nbt_assert_error(exc_env, file, type >= 0 && type <= TAG_Long_Array, "Not a valid tag type"); return type; } inline void flush_byte_array(input_buffer_ptr file) { Int length = read_int(file); nbt_assert_error(exc_env, file, file->flush(length) != -1, "Buffer to short to flush ByteArray"); } inline void handle_byte_array(String name, input_buffer_ptr file) { Int length = read_int(file); Byte *values = new Byte[length]; nbt_assert_error(exc_env, file, file->read(values, length) == length, "Buffer to short to read ByteArray"); ByteArray *array = new ByteArray(); array->values = values; array->length = length; register_byte_array(context, name, array); } inline void flush_int_array(input_buffer_ptr file) { Int length = read_int(file); nbt_assert_error(exc_env, file, file->flush(length * sizeof(Int)) != -1, "Buffer to short to flush IntArray"); } inline void handle_int_array(String name, input_buffer_ptr file) { Int length = read_int(file); Int *values = new Int[length]; for (Int i = 0; i < length; i++) { values[i] = read_int(file); } IntArray *array = new IntArray(); array->values = values; array->length = length; register_int_array(context, name, array); } inline void flush_long_array(input_buffer_ptr file) { Int length = read_int(file); nbt_assert_error(exc_env, file, file->flush(length * sizeof(Long)) != -1, "Buffer to short to flush LongArray"); } inline void handle_long_array(String name, input_buffer_ptr file) { Int length = read_int(file); Long *values = new Long[length]; for (Int i = 0; i < length; i++) { values[i] = read_long(file); } LongArray *array = new LongArray(); array->values = values; array->length = length; register_long_array(context, name, array); } public: typedef void (*begin_compound_t)(C*, String name); typedef void (*end_compound_t)(C*, String name); typedef void (*begin_list_t)(C*, String name, Byte type, Int length); typedef void (*end_list_t)(C*, String name); typedef void (*register_long_t)(C*, String name, Long l); typedef void (*register_short_t)(C*, String name, Short l); typedef void (*register_string_t)(C*, String name, String l); typedef void (*register_float_t)(C*, String name, Float l); typedef void (*register_double_t)(C*, String name, Double l); typedef void (*register_int_t)(C*, String name, Int l); typedef void (*register_byte_t)(C*, String name, Byte b); typedef void (*register_byte_array_t)(C*, String name, ByteArray* array); typedef void (*register_int_array_t)(C*, String name, IntArray* array); typedef void (*register_long_array_t)(C*, String name, LongArray* array); typedef void (*error_handler_t)(C*, size_t where, const char *why); register_long_t register_long; register_short_t register_short; register_string_t register_string; register_float_t register_float; register_double_t register_double; register_int_t register_int; register_byte_t register_byte; register_byte_array_t register_byte_array; register_int_array_t register_int_array; register_long_array_t register_long_array; begin_compound_t begin_compound; end_compound_t end_compound; begin_list_t begin_list; end_list_t end_list; error_handler_t error_handler; Parser() : context(NULL), register_long(NULL), register_short(NULL), register_string(NULL), register_float(NULL), register_double(NULL), register_int(NULL), register_byte(NULL), register_byte_array(NULL), register_int_array(NULL), register_long_array(NULL), begin_compound(&default_begin_compound), end_compound(&default_end_compound), begin_list(&default_begin_list), end_list(&default_end_list), error_handler(&default_error_handler) { } Parser(C *context) : context(context), register_long(NULL), register_short(NULL), register_string(NULL), register_float(NULL), register_double(NULL), register_int(NULL), register_byte(NULL), register_byte_array(NULL), register_int_array(NULL), register_long_array(NULL), begin_compound(&default_begin_compound), end_compound(&default_end_compound), begin_list(&default_begin_list), end_list(&default_end_list), error_handler(&default_error_handler) { this->context = context; } void stop() { running = false; } void parse(input_buffer_ptr file) { running = true; stack_entry *stack = new stack_entry[NBT_STACK_SIZE]; int stack_p = 0; stack_entry *root = stack + 0; if (setjmp(exc_env) == 1) { delete [] stack; return; } nbt_assert_error(exc_env, file, file->ok(), "Underlying input problem"); root->type = read_tagType(file); nbt_assert_error(exc_env, file, root->type == TAG_Compound, "Expected TAG_Compound at root"); root->name = read_string(file); begin_compound(context, root->name); while(running && stack_p >= 0) { nbt_assert_error(exc_env, file, stack_p < NBT_STACK_SIZE, "Stack cannot be larger than NBT_STACK_SIZE"); stack_entry* top = stack + stack_p; Byte type; String name(""); // if top is of Compound type, we must read the item name first if (top->type == TAG_Compound) { type = read_tagType(file); if (type == TAG_End) { end_compound(context, top->name); --stack_p; continue; } name = read_string(file); } // if top of stack is of type list, the type must be inferred, name is assumed to be "" (empty string) else if (top->type == TAG_List) { if (top->list_read >= top->list_count) { end_list(context, top->name); --stack_p; continue; } type = top->list_type; top->list_read++; } else { nbt_assert_error(exc_env, file, 1, "Unknown stack type"); continue; } switch(type) { case TAG_Long: if (register_long == NULL) { nbt_assert_error(exc_env, file, file->flush(sizeof(nbt::Long)) != -1, "Buffer too short to flush long"); } else { register_long(context, name, read_long(file)); } break; case TAG_Short: if (register_short == NULL) { nbt_assert_error(exc_env, file, file->flush(sizeof(nbt::Short)) != -1, "Buffer too short to flush short"); } else { register_short(context, name, read_short(file)); } break; case TAG_String: if (register_string == NULL) { flush_string(file); } else { register_string(context, name, read_string(file)); } break; case TAG_Float: if (register_float == NULL) { nbt_assert_error(exc_env, file, file->flush(sizeof(nbt::Float)) != -1, "Buffer too short to flush float"); } else { register_float(context, name, read_float(file)); } break; case TAG_Double: if (register_double == NULL) { nbt_assert_error(exc_env, file, file->flush(sizeof(nbt::Double)) != -1, "Buffer too short to flush double"); } else { register_double(context, name, read_double(file)); } break; case TAG_Int: if (register_int == NULL) { nbt_assert_error(exc_env, file, file->flush(sizeof(nbt::Int)) != -1, "Buffer too short to flush int"); } else { register_int(context, name, read_int(file)); } break; case TAG_Byte: if (register_byte == NULL) { nbt_assert_error(exc_env, file, file->flush(sizeof(nbt::Byte)) != -1, "Buffer too short to flush byte"); } else { register_byte(context, name, read_byte(file)); } break; case TAG_List: { stack_entry* c = stack + ++stack_p; c->list_read = 0; c->list_type = read_tagType(file); c->list_count = read_int(file); c->name = name; c->type = TAG_List; begin_list(context, name, c->list_type, c->list_count); } break; case TAG_Compound: { begin_compound(context, name); stack_entry* c = stack + ++stack_p; c->list_read = 0; c->list_count = 0; c->list_type = -1; c->name = name; c->type = TAG_Compound; } break; case TAG_Byte_Array: if (register_byte_array == NULL) { flush_byte_array(file); } else { handle_byte_array(name, file); } break; case TAG_Int_Array: if (register_int_array == NULL) { flush_int_array(file); } else { handle_int_array(name, file); } break; case TAG_Long_Array: if (register_long_array == NULL) { flush_long_array(file); } else { handle_long_array(name, file); } break; default: nbt_assert_error(exc_env, file, 0, "Encountered unknown type"); break; } } nbt_assert_error(exc_env, file, file->empty(), "input buffer is not empty"); delete [] stack; } void parse_buffer(const char* buffer, unsigned int size) { input_buffer_ptr file(new memory_buffer(buffer, size)); parse(file); } void parse_file(const char *path) { input_buffer_ptr file(new gzfile_buffer(path)); parse(file); } }; } #endif /* _NBT_H_ */ ================================================ FILE: src/nbt/nbt_inspect.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "nbt/nbt.hpp" #include #include using namespace std; struct inspect_context { int width; }; void begin_compound(inspect_context* inspect, nbt::String name) { cout << setw(inspect->width) << "" << "BEGIN Compound(" << name << ")" << endl; inspect->width += 2; } void end_compound(inspect_context* inspect, nbt::String name) { inspect->width -= 2; cout << setw(inspect->width) << "" << "END Compound(" << name << ")" << endl; } void begin_list(inspect_context* inspect, nbt::String name, nbt::Byte type, nbt::Int length) { cout << setw(inspect->width) << "" << "BEGIN List(" << name << ", " << nbt::tag_string_map[type] << ", " << length << ")" << endl; inspect->width += 2; } void end_list(inspect_context* inspect, nbt::String name) { inspect->width -= 2; cout << setw(inspect->width) << "" << "END List(" << name << ")" << endl; } void register_long(inspect_context* inspect, nbt::String name, nbt::Long value) { cout << setw(inspect->width) << "" << "Long(" << name << "): " << dec << value << endl; } void register_short(inspect_context* inspect, nbt::String name, nbt::Short value) { cout << setw(inspect->width) << "" << "Short(" << name << "): " << value << endl; } void register_string(inspect_context* inspect, nbt::String name, nbt::String value) { cout << setw(inspect->width) << "" << "String(" << name << "): " << value << endl; } void register_float(inspect_context* inspect, nbt::String name, nbt::Float value) { cout << setw(inspect->width) << "" << "Float(" << name << "): " << value << endl; } void register_double(inspect_context* inspect, nbt::String name, nbt::Double value) { cout << setw(inspect->width) << "" << "Double(" << name << "): " << value << endl; } void register_int(inspect_context* inspect, nbt::String name, nbt::Int value) { cout << setw(inspect->width) << "" << "Int(" << name << "): " << dec << value << endl; } void register_byte(inspect_context* inspect, nbt::String name, nbt::Byte value) { cout << setw(inspect->width) << "" << "Byte(" << name << "): 0x" << hex << (int)value << endl; } void register_byte_array(inspect_context* inspect, nbt::String name, nbt::ByteArray* value) { cout << setw(inspect->width) << "" << "ByteArray(" << name << "): " << "(binary blob)" << endl; delete value; } void register_long_array(inspect_context* inspect, nbt::String name, nbt::LongArray* value) { cout << setw(inspect->width) << "" << "LongArray(" << name << "): " << "(binary blob)" << endl; delete value; } int main(int argc, char* argv[]) { if (argc < 2) { return 1; } inspect_context ctx; ctx.width = 0; nbt::Parser parser(&ctx); parser.begin_compound = begin_compound; parser.end_compound = end_compound; parser.begin_list = begin_list; parser.end_list = end_list; parser.register_long = register_long; parser.register_short = register_short; parser.register_string = register_string; parser.register_float = register_float; parser.register_double = register_double; parser.register_int = register_int; parser.register_byte = register_byte; parser.register_byte_array = register_byte_array; parser.register_long_array = register_long_array; parser.parse_file(argv[1]); return 0; } ================================================ FILE: src/nbt/types.hpp ================================================ #ifndef _NBT_TYPES_HPP #define _NBT_TYPES_HPP #include #include namespace nbt { typedef int8_t Byte; typedef int16_t Short; typedef int32_t Int; typedef int64_t Long; typedef std::string String; typedef float Float; typedef double Double; struct ByteArray { Int length; Byte *values; ~ByteArray() { delete [] values; } }; struct IntArray { Int length; Int *values; ~IntArray() { delete [] values; } }; struct LongArray { Int length; Long *values; ~LongArray() { delete [] values; } }; struct stack_entry { Byte type; String name; Int list_count, list_read; Byte list_type; }; } #endif /* _NBT_TYPES_HPP */ ================================================ FILE: src/nullstream.cpp ================================================ #include "nullstream.hpp" nullstream::nullstream() : std::ios(0), std::ostream(0) { } ================================================ FILE: src/nullstream.hpp ================================================ #ifndef __NULLSTREAM_HPP__ #define __NULLSTREAM_HPP__ #include #include struct nullstream : std::ostream { nullstream(); }; #endif /* __NULLSTREAM_HPP__ */ ================================================ FILE: src/players.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "players.hpp" namespace fs = boost::filesystem; void error_handler(player *p, size_t where, const char* why) { p->error_where = where; p->error_why = why; p->error = true; } void begin_list(player *p, std::string name, nbt::Byte, nbt::Int) { if (name.compare("Pos") == 0) { p->in_pos = true; } } void end_list(player *p, std::string name) { if (name.compare("Pos") == 0) { p->in_pos = false; } } void register_double(player *p, std::string name, nbt::Double value) { if (p->in_pos) { switch (p->pos_c) { case 0: p->xPos = value; break; case 1: p->yPos = value; break; case 2: p->zPos = value; break; } p->pos_c++; } } player::player(const fs::path path) : path(path), name(path.stem().string()), error(false), error_where(0), error_why(""), in_pos(false), pos_c(0), xPos(0), yPos(0), zPos(0) { nbt::Parser parser(this); parser.error_handler = error_handler; parser.begin_list = begin_list; parser.end_list = end_list; parser.register_double = register_double; parser.parse_file(path.string().c_str()); } players_db::players_db(fs::path path, std::set set) : path(path), filter_set(set) { } void players_db::read(std::vector& players) const { fs::path full_path = fs::system_complete( path ); if (!fs::is_directory(full_path)) { throw players_db_exception("database does not exist"); } fs::directory_iterator end_iter; for ( fs::directory_iterator dir_itr( full_path ); dir_itr != end_iter; ++dir_itr ) { player p(dir_itr->path()); if (filter_set.size() > 0) { if (filter_set.find(p.name) == filter_set.end()) { continue; } } players.push_back(p); } } ================================================ FILE: src/players.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _PLAYERS_H_ #define _PLAYERS_H_ #include #include #include #include #include #include #include #include "settings_t.hpp" #include "nbt/nbt.hpp" class players_db_exception : public std::exception { private: const char* message; public: players_db_exception(const char* message) : message(message) { } const char* what() throw() { return message; } }; class player { public: boost::filesystem::path path; std::string name; bool error; size_t error_where; std::string error_why; bool in_pos; int pos_c; nbt::Int xPos, yPos, zPos; player(const boost::filesystem::path path); }; class players_db { private: const boost::filesystem::path path; const std::set filter_set; public: players_db(const boost::filesystem::path path, std::set set); void read(std::vector&) const; }; #endif /* _PLAYERS_H_ */ ================================================ FILE: src/settings_t.cpp ================================================ #include "settings_t.hpp" #include "mc/blocks.hpp" settings_t::settings_t(fs::path& install_path) { this->excluded.push_back("minecraft:air"); this->threads = 1; this->prebuffer = 4; this->split_base = 0; this->use_split = false; this->cavemode = false; this->hellmode = false; this->top = 127; this->bottom = 0; this->mode = Top; this->nocheck = false; this->silent = false; this->show_players = false; this->show_coordinates = false; this->show_signs = false; this->strip_sign_prefix = false; this->show_warps = false; this->require_all = false; this->striped_terrain = false; this->rotation = 0; this->binary = false; this->night = false; this->heightmap = false; this->debug = false; this->swap_file = "swap.bin"; this->memory_limit = 1000; this->memory_limit_default = true; this->min_x = -10000; this->max_x = 10000; this->min_z = -10000; this->max_z = 10000; this->max_radius = 1000; this->ttf_path = fs::path("font.ttf"); this->ttf_size = 12; this->ttf_color = color(0, 0, 0, 0xff); this->sign_color = color(0, 0, 0, 0xff); this->player_color = color(0, 0, 0, 0xff); this->has_player_color = false; this->has_sign_color = false; this->has_coordinate_color = false; this->has_warp_color = false; this->coordinate_color = color(0, 0, 0, 0xff); this->warp_color = color(0, 0, 0, 0xff); this->pedantic_broad_phase = false; this->cache_use = false; this->cache_key = ""; this->cache_dir = "cache"; this->cache_compress = false; this->write_json = false; this->write_js = false; this->no_log = false; this->disable_alpha = false; this->enable_all_blocks = true; this->install_path = install_path; this->output_log = fs::system_complete(fs::path("c10t.log")); this->output_path = fs::system_complete(fs::path("out.png")); this->statistics_path = fs::system_complete(fs::path("statistics.txt")); this->palette_read_path = install_path /= fs::path("palette.json"); this->action = None; this->center_x = 0; this->center_z = 0; this->engine_use = false; } bool settings_t::coord_out_of_range(mc::utils::level_coord& coord) { int x = coord.get_x() - center_x; int z = coord.get_z() - center_z; uint64_t x2 = uint64_t(x) * uint64_t(x); uint64_t z2 = uint64_t(z) * uint64_t(z); uint64_t r2 = max_radius * max_radius; return x < min_x || x > max_x || z < min_z || z > max_z || (x2 + z2) > r2+1; } ================================================ FILE: src/settings_t.hpp ================================================ #ifndef __SETTINGS_T_HPP__ #define __SETTINGS_T_HPP__ #include #include #include #include "image/color.hpp" #include "mc/utils.hpp" enum mode { Top = 0x0, Oblique = 0x1, ObliqueAngle = 0x2, Isometric = 0x3, FatIso = 0x4 }; enum action { None, Version, Help, GenerateWorld, GenerateStatistics, ListColors, WritePalette }; struct settings_t { settings_t(fs::path& install_path); bool binary; bool cache_compress; bool cache_use; bool cavemode; bool debug; bool has_coordinate_color; bool has_player_color; bool has_sign_color; bool has_warp_color; bool heightmap; bool hellmode; bool memory_limit_default; bool night; bool nocheck; bool no_log; bool pedantic_broad_phase; bool require_all; bool show_coordinates; bool show_players; bool show_signs; bool show_warps; bool silent; bool strip_sign_prefix; bool striped_terrain; bool use_split; bool write_js; bool write_json; bool disable_alpha; bool enable_all_blocks; std::list included; std::list excluded; std::list top_color_overrides; std::list side_color_overrides; color coordinate_color; color player_color; color sign_color; color ttf_color; color warp_color; enum mode mode; boost::filesystem::path install_path; boost::filesystem::path cache_dir; boost::filesystem::path output_log; boost::filesystem::path output_path; boost::filesystem::path palette_read_path; boost::filesystem::path palette_write_path; boost::filesystem::path show_warps_path; boost::filesystem::path statistics_path; boost::filesystem::path swap_file; boost::filesystem::path ttf_path; boost::filesystem::path world_path; boost::filesystem::path write_json_path; boost::filesystem::path write_js_path; boost::filesystem::path engine_path; bool engine_use; int bottom; uint64_t max_radius; int64_t min_x; int64_t max_x; int64_t min_z; int64_t max_z; int top; int ttf_size; size_t memory_limit; std::list split; std::set show_players_set; std::string cache_key; std::string show_signs_filter; // top/bottom used for slicing unsigned int prebuffer; unsigned int rotation; unsigned int split_base; unsigned int threads; std::string graph_block; int center_x; int center_z; enum action action; bool coord_out_of_range(mc::utils::level_coord& coord); }; #endif /*__SETTINGS_T_HPP__*/ ================================================ FILE: src/text.cpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #include "text.hpp" #include namespace text { font_face::font_face(const fs::path font_path, int size, color base) : font_path(font_path), size(size), base(base), initialized(false) { } void font_face::init() { int error; error = FT_Init_FreeType(&library); if (error) { throw text_error("Failed to initialize the freetype2 library"); } error = FT_New_Face(library, font_path.string().c_str(), 0, &face); if (error == FT_Err_Unknown_File_Format) { throw text_error("Could not open file - unknown file format: " + font_path.string()); } else if (error) { throw text_error("Could not open file: " + font_path.string()); } set_size(size); initialized = true; } void font_face::set_size(int size) { if (FT_Set_Pixel_Sizes(face, 0, size)) { throw text_error("Failed to set font resolution"); } this->size = size; } void font_face::draw_bitmap(image_ptr image, FT_Bitmap* bitmap, pos_t pen_x, pos_t pen_y) const { assert(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY); uint8_t* buffer = bitmap->buffer; pos_t s_bitmap_rows = boost::numeric_cast(bitmap->rows); pos_t s_bitmap_width = boost::numeric_cast(bitmap->width); for (pos_t y = 0; y < s_bitmap_rows && y < image->get_height() + pen_y; y++) { for (pos_t x = 0; x < s_bitmap_width && x < image->get_width() + pen_x; x++) { color c(base); c.a = color_i_to_f[buffer[x + y * bitmap->width]]; image->safe_blend_pixel(pen_x + x, pen_y + y, c); } } } void font_face::draw(image_ptr image, const std::string rawtext, int x, int y) const { FT_GlyphSlot slot = face->glyph; int error; int pen_x = x, pen_y = y; unc::ustring text = unc::decode(rawtext); for (unc::ustring::iterator it = text.begin(); it != text.end(); it++ ) { uint32_t cc = *it; if (cc == '\n') { pen_x = x; pen_y += size + 2; continue; } error = FT_Load_Char( face, cc, FT_LOAD_RENDER ); if ( error ) continue; draw_bitmap(image, &slot->bitmap, pen_x + slot->bitmap_left, pen_y - slot->bitmap_top); /* increment pen position */ pen_x += slot->bitmap.width + 2; } } void font_face::set_color(color& c) { base = c; } bool font_face::is_initialized() { return initialized; } } ================================================ FILE: src/text.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _TEXT_H_ #define _TEXT_H_ #include #include #include #include #include #include FT_FREETYPE_H namespace fs = boost::filesystem; #include "image/image_base.hpp" #include namespace text { class text_error : public std::exception { private: const std::string error; public: text_error(const std::string error) : error(error) {}; virtual ~text_error() throw() {}; virtual const char* what() const throw() { return error.c_str(); } }; class font_face { private: const fs::path font_path; int size; color base; bool initialized; FT_Library library; FT_Face face; public: font_face(const fs::path font_path, int size, color base); void init(); void set_size(int size); void draw_bitmap(image_ptr image, FT_Bitmap* bitmap, pos_t pen_x, pos_t pen_y) const; void draw(image_ptr image, const std::string rawtext, int x, int y) const; void set_color(color& c); bool is_initialized(); }; } #endif ================================================ FILE: src/threads/renderer.hpp ================================================ #ifndef __THREADS__RENDERER_HPP__ #define __THREADS__RENDERER_HPP__ #include "mc/utils.hpp" #include "mc/world.hpp" #include "mc/level.hpp" #include #include #include #include #include "cache.hpp" #include "image/image_operations.hpp" #include "engine/engine_core.hpp" #include "threads/threadworker.hpp" #include "threads/renderer_settings.hpp" namespace fs = boost::filesystem; struct render_result { boost::shared_ptr level; image_base_ptr image; bool fatal; std::string fatal_why; std::vector signs; bool cache_hit; mc::utils::level_coord coord; fs::path path; render_result() : fatal(false), fatal_why("(no error)") {} bool operator<(const render_result& other) const { return coord < other.coord; } }; struct render_job { int32_t order; boost::shared_ptr level; boost::shared_ptr engine; mc::utils::level_coord coord; fs::path path; }; class renderer : public threadworker { public: renderer_settings& r; renderer(renderer_settings r, int n, int total) : threadworker(n, total), r(r) { } render_result work(render_job job) { render_result p; p.coord = job.coord; p.operations.reset(new image_operations(job.order)); p.level = job.level; p.cache_hit = false; p.path = job.path; time_t mod = p.level->modification_time(); std::stringstream ss; ss << boost::format("%d.%d.cmap") % job.coord.get_x() % job.coord.get_z(); std::string basename = ss.str(); cache_file cache(mc::utils::level_dir(r.cache_dir, job.coord.get_x(), job.coord.get_z()), basename, mod, r.cache_compress); if (r.cache_use) { if (cache.exists()) { if (cache.read(p.operations)) { p.cache_hit = true; return p; } cache.clear(); } } //p.signs = job.level->get_signs(); job.engine->render(job.level, p.operations); //p.operations->optimize(); if (r.cache_use) { // create the necessary directories required when caching cache.create_directories(); // ignore failure while writing the operations to cache if (!cache.write(p.operations)) { // on failure, remove the cache file - this will prompt c10t to regenerate it next time cache.clear(); } } return p; } }; #endif /* __THREADS__RENDERER_HPP__ */ ================================================ FILE: src/threads/renderer_settings.hpp ================================================ #ifndef _THREADS_RENDERER_SETTINGS_HPP #define _THREADS_RENDERER_SETTINGS_HPP #include namespace fs = boost::filesystem; struct renderer_settings { bool cache_use; fs::path cache_dir; bool cache_compress; }; #endif /* _THREADS_RENDERER_SETTINGS_HPP */ ================================================ FILE: src/threads/threadworker.hpp ================================================ // Distributed under the BSD License, see accompanying LICENSE.txt // (C) Copyright 2010 John-John Tedro et al. #ifndef _THREADWORKER_H_ #define _THREADWORKER_H_ #include #include #include // I admit, this is pretty drastic, but I was desperate // now I just keep it for the sake of having it available to new // platforms. #if !defined(C10T_DISABLE_THREADS) #include "threads/threadworker_impl.hpp" #else #include "threads/threadworker_fake.hpp" #endif #endif /* _THREADWORKER_H_ */ ================================================ FILE: src/threads/threadworker_fake.hpp ================================================ #ifndef __THREADWORKER_SUB_HPP__ #define __THREADWORKER_SUB_HPP__ template class threadworker { private: std::queue in; const int thread_count; public: threadworker(int c) : thread_count(c) { } virtual ~threadworker() { } void give(I t) { in.push(t); } void start() { } void run(int id) { } virtual O work(I) = 0; O get() { I i = in.front(); in.pop(); return work(i); } void join() { } }; #endif /*__THREADWORKER_SUB_HPP__*/ ================================================ FILE: src/threads/threadworker_impl.hpp ================================================ #ifndef __THREADWORKER_SUB_HPP__ #define __THREADWORKER_SUB_HPP__ #include #include #include #include #include class interrupted_exception : public std::exception {}; template class sync_queue { private: std::queue q; boost::mutex mutex; boost::condition empty_cond; volatile int count; volatile bool interrupted; public: sync_queue() : count(0), interrupted(false) {} void add(T o) { boost::mutex::scoped_lock lock(mutex); q.push(o); empty_cond.notify_one(); } T take(int& pos) throw(interrupted_exception) { boost::mutex::scoped_lock lock(mutex); while (!interrupted && q.empty()) { empty_cond.wait(lock); } if (interrupted) { throw interrupted_exception(); } T o = q.front(); q.pop(); pos = count++; return o; } bool empty() { boost::mutex::scoped_lock lock(mutex); return q.empty(); } void interrupt() { boost::mutex::scoped_lock lock(mutex); interrupted = true; empty_cond.notify_all(); } }; template class threadworker { private: const int total; sync_queue in; sync_queue out; boost::condition start_cond; boost::condition input_cond; boost::mutex start_mutex; const int thread_count; boost::detail::atomic_count input; boost::detail::atomic_count output; volatile bool started; volatile bool interrupted; boost::scoped_array threads; public: threadworker(int c, int total) : total(total), thread_count(c), input(0), output(0), started(false), interrupted(false), threads(new boost::thread[thread_count]) { for (int i = 0; i < thread_count; i++) { threads[i] = boost::thread(boost::bind(&threadworker::run, this, i)); } } virtual ~threadworker() { in.interrupt(); out.interrupt(); interrupted = true; { boost::mutex::scoped_lock lock(start_mutex); start_cond.notify_all(); } for (int i = 0; i < thread_count; i++) { threads[i].join(); } } void give(I t) { in.add(t); } void start() { boost::mutex::scoped_lock lock(start_mutex); started = true; start_cond.notify_all(); } void internal_work() throw(interrupted_exception) { int qp; I i = in.take(qp); O o = work(i); if (interrupted) { throw interrupted_exception(); } out.add(o); ++output; } void run(int id) { { boost::mutex::scoped_lock lock(start_mutex); while (!interrupted && !started) { start_cond.wait(lock); } } if (interrupted) { return; } while (++input <= total) { try { internal_work(); } catch(interrupted_exception& e) { break; } } } virtual O work(I) = 0; O get() { int t; return out.take(t); } void join() { } }; #endif /*__THREADWORKER_SUB_HPP__*/ ================================================ FILE: src/warps.cpp ================================================ #include "warps.hpp" #include #include #include using namespace std; void warps_db::read(std::vector& warps) { using ::boost::lexical_cast; using ::boost::split; if (!fs::is_regular_file(path)) { throw warps_db_exception("database does not exist"); } ifstream fp(path.string().c_str()); if (!fp.good()) { throw warps_db_exception("could not open file"); } while (!fp.eof()) { string line; getline(fp, line); if (line.empty()) continue; vector parts; split(parts, line, boost::is_any_of(":")); if (parts.size() < 4) continue; warp w; w.name = parts[0]; try { w.xPos = int(lexical_cast(parts[1])); w.yPos = int(lexical_cast(parts[2])); w.zPos = int(lexical_cast(parts[3])); } catch(...) { // silently ignore continue; } warps.push_back(w); } fp.close(); } ================================================ FILE: src/warps.hpp ================================================ #ifndef _WARPS_H_ #define _WARPS_H_ #include #include #include #include #include #include #include "nbt/nbt.hpp" namespace fs = boost::filesystem; class warps_db_exception : public std::exception { private: const char* why; public: warps_db_exception(const char* why) : why(why) { } const char* what() throw() { return this->why; } }; class warp { public: std::string name; nbt::Int xPos, yPos, zPos; }; class warps_db { public: const fs::path path; warps_db(const fs::path path) : path(path) {} void read(std::vector&); }; #endif /* _WARPS_H_ */ ================================================ FILE: src/win32/tss_cleanup_implemented.cpp ================================================ namespace boost { void tss_cleanup_implemented() {} }; ================================================ FILE: test/CMakeLists.txt ================================================ add_executable(c10t-test EXCLUDE_FROM_ALL test.cpp) target_link_libraries(c10t-test c10t-lib) target_link_libraries(c10t-test ${c10t_LIBRARIES}) target_link_libraries(c10t-test boost_unit_test_framework) ================================================ FILE: test/test.cpp ================================================ #include "color.hpp" #include "image.hpp" #include "2d/cube.hpp" #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE c10t_tests #include #include BOOST_AUTO_TEST_CASE( test_cube_projection_1 ) { // x, y, z Cube c(10, 10, 20); point p1(0, 0, 0), p2(0, 0, 20), p3(10, 0, 20), p4(10, 0, 0); int x, y; { c.project_top(p1, x, y); BOOST_REQUIRE(x == 0 && y == 0); c.project_top(p2, x, y); BOOST_REQUIRE(x == 0 && y == 20); c.project_top(p3, x, y); BOOST_REQUIRE(x == 10 && y == 20); c.project_top(p4, x, y); BOOST_REQUIRE(x == 10 && y == 0); } { c.project_oblique(p1, x, y); BOOST_REQUIRE(x == 0 && y == 10); c.project_oblique(p2, x, y); BOOST_REQUIRE(x == 0 && y == 30); c.project_oblique(p3, x, y); BOOST_REQUIRE(x == 10 && y == 30); c.project_oblique(p4, x, y); BOOST_REQUIRE(x == 10 && y == 10); } { c.project_obliqueangle(p1, x, y); BOOST_REQUIRE(x == 20 && y == 10); c.project_obliqueangle(p2, x, y); BOOST_REQUIRE(x == 0 && y == 30); c.project_obliqueangle(p3, x, y); BOOST_REQUIRE(x == 10 && y == 40); c.project_obliqueangle(p4, x, y); BOOST_REQUIRE(x == 30 && y == 20); } } BOOST_AUTO_TEST_CASE( test_cube_projection_2 ) { // x, y, z Cube c(20, 10, 10); point p1(0, 0, 0), p2(0, 0, 10), p3(20, 0, 10), p4(20, 0, 0); int x, y; { c.project_top(p1, x, y); BOOST_REQUIRE(x == 0 && y == 0); c.project_top(p2, x, y); BOOST_REQUIRE(x == 0 && y == 10); c.project_top(p3, x, y); BOOST_REQUIRE(x == 20 && y == 10); c.project_top(p4, x, y); BOOST_REQUIRE(x == 20 && y == 0); } { c.project_oblique(p1, x, y); BOOST_REQUIRE(x == 0 && y == 10); c.project_oblique(p2, x, y); BOOST_REQUIRE(x == 0 && y == 20); c.project_oblique(p3, x, y); BOOST_REQUIRE(x == 20 && y == 20); c.project_oblique(p4, x, y); BOOST_REQUIRE(x == 20 && y == 10); } { c.project_obliqueangle(p1, x, y); BOOST_REQUIRE(x == 10 && y == 10); c.project_obliqueangle(p2, x, y); BOOST_REQUIRE(x == 0 && y == 20); c.project_obliqueangle(p3, x, y); BOOST_REQUIRE(x == 20 && y == 40); c.project_obliqueangle(p4, x, y); BOOST_REQUIRE(x == 30 && y == 30); } }