├── README.md ├── .gitmodules ├── .gitignore ├── src ├── platform │ ├── platform.cpp │ ├── platform.h │ ├── sdl_platform.h │ └── sdl_platform.cpp ├── renderer │ ├── renderer.cpp │ ├── renderer.h │ ├── gl_renderer.h │ └── gl_renderer.cpp ├── system.h ├── input_manager.cpp ├── loaders │ ├── file.h │ ├── image_loader.h │ ├── file.cpp │ ├── data_loader.h │ ├── image_loader.cpp │ └── data_loader.cpp ├── utils │ ├── debug.h │ ├── memory │ │ ├── polymorphic_allocator_wrappers.cpp │ │ ├── polymorphic_allocator_wrappers.h │ │ ├── freelist_allocator.h │ │ ├── linear_allocator.h │ │ ├── block_allocator.h │ │ ├── allocator.h │ │ └── freelist_allocator.cpp │ ├── CMakeLists.txt │ ├── utils.h │ ├── debug.cpp │ └── container │ │ └── array.h ├── game │ ├── game.h │ ├── world_renderer.h │ ├── game.cpp │ ├── world.h │ ├── world_renderer.cpp │ └── world.cpp ├── main.cpp ├── input_manager.h ├── engine.h ├── CMakeLists.txt ├── engine.cpp ├── graphics_manager.h ├── graphics_manager.cpp └── assets.h ├── .clang-format ├── CMakeLists.txt ├── cmake └── Modules │ └── FindLibEpoxy.cmake └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | Castlekeep 2 | ========== 3 | Reimplementation of Stronghold 1 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/sh2ck"] 2 | path = tools/sh2ck 3 | url = https://github.com/leidav/stronghold-to-castlekeep 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.[oa] 2 | 3 | *.swp 4 | *.user 5 | *~ 6 | 7 | build/ 8 | CmakeFiles 9 | CMakeCache.txt 10 | tags 11 | data/* 12 | -------------------------------------------------------------------------------- /src/platform/platform.cpp: -------------------------------------------------------------------------------- 1 | #include "platform.h" 2 | 3 | namespace castlekeep 4 | { 5 | namespace platform 6 | { 7 | PlatformInterface::~PlatformInterface() {} 8 | } // namespace platform 9 | } // namespace castlekeep 10 | -------------------------------------------------------------------------------- /src/renderer/renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "renderer.h" 2 | 3 | namespace castlekeep 4 | { 5 | namespace render 6 | { 7 | RenderInterface::~RenderInterface() {} 8 | 9 | }; // namespace render 10 | } // namespace castlekeep 11 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AccessModifierOffset: -4 3 | BreakBeforeBraces: Linux 4 | IndentWidth: 4 5 | TabWidth: 4 6 | UseTab: ForIndentation #Always 7 | AllowShortBlocksOnASingleLine: false 8 | AllowShortFunctionsOnASingleLine: Inline 9 | -------------------------------------------------------------------------------- /src/system.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSTEM_H 2 | #define SYSTEM_H 3 | 4 | #include 5 | 6 | namespace castlekeep 7 | { 8 | namespace core 9 | { 10 | template 11 | class EngineSystem 12 | { 13 | public: 14 | static constexpr size_t minimalArenaSize() 15 | { 16 | return Derived::k_minimal_arena_size; 17 | } 18 | int startUp(); 19 | }; 20 | 21 | } // namespace core 22 | } // namespace castlekeep 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/input_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "input_manager.h" 2 | 3 | #include 4 | #include "engine.h" 5 | 6 | namespace castlekeep 7 | { 8 | namespace input 9 | { 10 | InputManager::InputManager() 11 | { 12 | std::memset(&m_state, 0, sizeof(m_state)); 13 | } 14 | 15 | bool InputManager::readInput() 16 | { 17 | return core::Engine::g_engine->platform()->processEvents(m_state); 18 | } 19 | 20 | }; // namespace input 21 | }; // namespace castlekeep 22 | -------------------------------------------------------------------------------- /src/loaders/file.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_H 2 | #define FILE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace castlekeep 8 | { 9 | namespace loader 10 | { 11 | class File 12 | { 13 | public: 14 | File(); 15 | ~File(); 16 | int open(const char* path, const char* mode); 17 | void close(); 18 | char* readLine(char* buffer, size_t size); 19 | size_t readBytes(std::byte* buffer, size_t num); 20 | int seek(size_t pos); 21 | size_t size(); 22 | 23 | private: 24 | FILE* m_file; 25 | }; 26 | 27 | } // namespace loader 28 | } // namespace castlekeep 29 | #endif 30 | -------------------------------------------------------------------------------- /src/loaders/image_loader.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE_LOADER_H 2 | #define IMAGE_LOADER_H 3 | 4 | #include "memory/linear_allocator.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace castlekeep 10 | { 11 | namespace loader 12 | { 13 | constexpr int k_image_default_max_width = 2048; 14 | constexpr int k_image_default_max_height = k_image_default_max_width * 4; 15 | 16 | int loadImage(std::byte image[], size_t size, int &width, int &height, 17 | int &channels, const char *file, const memory::Arena &arena, 18 | int max_width = k_image_default_max_width, 19 | int max_height = k_image_default_max_height); 20 | } // namespace loader 21 | } // namespace castlekeep 22 | #endif 23 | -------------------------------------------------------------------------------- /src/utils/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | #ifdef DEBUG_BUILD 5 | 6 | #define DEBUG_ERROR(message) debug_error((message), __FILE__, __LINE__) 7 | #define DEBUG_ASSERT(expr, message) \ 8 | debug_assert((expr), (message), __FILE__, __LINE__) 9 | 10 | [[noreturn]] void debug_error(const char* msg, const char* file, 11 | const int line); 12 | 13 | inline void debug_assert(bool expr, const char* message, const char* file, 14 | const int line) 15 | { 16 | if (!expr) { 17 | debug_error(message, file, line); 18 | } 19 | } 20 | 21 | #else 22 | 23 | #define DEBUG_ERROR(a) ((void)0) 24 | #define DEBUG_ASSERT(expr, message) ((void)0) 25 | 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/platform/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #include "input_manager.h" 5 | #include "memory/allocator.h" 6 | #include "memory/polymorphic_allocator_wrappers.h" 7 | #include "renderer/renderer.h" 8 | 9 | namespace castlekeep 10 | { 11 | namespace platform 12 | { 13 | enum class Platforms { linux_sdl }; 14 | 15 | struct WindowHandle { 16 | void *window; 17 | }; 18 | 19 | class PlatformInterface 20 | { 21 | public: 22 | virtual ~PlatformInterface() = 0; 23 | virtual int createWindow(int width, int height, const char *name) = 0; 24 | virtual bool processEvents(input::InputState &state) = 0; 25 | virtual WindowHandle window() = 0; 26 | }; 27 | 28 | }; // namespace platform 29 | } // namespace castlekeep 30 | #endif 31 | -------------------------------------------------------------------------------- /src/utils/memory/polymorphic_allocator_wrappers.cpp: -------------------------------------------------------------------------------- 1 | #include "polymorphic_allocator_wrappers.h" 2 | 3 | namespace memory 4 | { 5 | AbstractAllocator::~AbstractAllocator() {} 6 | LinearAllocatorAdapter::LinearAllocatorAdapter(LinearAllocator &allocator) 7 | : m_allocator(allocator) 8 | { 9 | } 10 | 11 | LinearAllocatorAdapter::~LinearAllocatorAdapter() {} 12 | 13 | void *LinearAllocatorAdapter::allocate(size_t size) 14 | { 15 | return m_allocator.allocate(size); 16 | } 17 | 18 | void *LinearAllocatorAdapter::allocate_aligned(size_t size, size_t alignment) 19 | { 20 | return m_allocator.allocate_aligned(size, alignment); 21 | } 22 | 23 | void LinearAllocatorAdapter::deallocate(void *address) 24 | { 25 | return m_allocator.deallocate(address); 26 | } 27 | 28 | }; // namespace memory 29 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.9) 2 | 3 | set (PROJECT_NAME castlekeep) 4 | project (${PROJECT_NAME}) 5 | 6 | if(NOT CMAKE_BUILD_TYPE) 7 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the build type" FORCE) 8 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 9 | "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 10 | endif() 11 | 12 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 13 | 14 | if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) 15 | add_compile_options("-Wall") 16 | endif() 17 | 18 | 19 | find_package(SDL2 REQUIRED) 20 | 21 | set(OpenGL_GL_PREFERENCE "GLVND") 22 | find_package(OpenGL REQUIRED) 23 | 24 | find_package(LibEpoxy REQUIRED) 25 | 26 | find_package(PNG REQUIRED) 27 | 28 | add_subdirectory(src) 29 | -------------------------------------------------------------------------------- /src/game/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | 4 | #include 5 | #include "input_manager.h" 6 | #include "world.h" 7 | #include "world_renderer.h" 8 | 9 | namespace castlekeep 10 | { 11 | namespace game 12 | { 13 | class Game 14 | { 15 | public: 16 | constexpr static size_t k_minimal_arena_size = 17 | 10_mib + game::World::k_minimal_arena_size + 18 | game::WorldRenderer::k_minimal_arena_size; 19 | Game(const memory::Arena& arena); 20 | ~Game(); 21 | void startUp(); 22 | void update(); 23 | void render(); 24 | void shutDown(); 25 | 26 | private: 27 | memory::LinearAllocator m_allocator; 28 | memory::UniquePtr m_world; 29 | memory::UniquePtr m_world_renderer; 30 | input::InputManager* m_input_manager; 31 | Camera m_camera; 32 | }; 33 | } // namespace game 34 | }; // namespace castlekeep 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/platform/sdl_platform.h: -------------------------------------------------------------------------------- 1 | #ifndef SDL_PLATFORM_H 2 | #define SDL_PLATFORM_H 3 | 4 | #include "memory/allocator.h" 5 | #include "platform.h" 6 | #include "system.h" 7 | 8 | #include 9 | 10 | namespace castlekeep 11 | { 12 | namespace platform 13 | { 14 | using namespace memory::literals; 15 | class SDLPlatform : public PlatformInterface, 16 | public core::EngineSystem 17 | { 18 | public: 19 | constexpr static size_t k_minimal_arena_size = 1_kib; 20 | 21 | SDLPlatform(const memory::Arena &arena); 22 | ~SDLPlatform() final; 23 | int startUp(); 24 | int createWindow(int width, int height, const char *name) final; 25 | bool processEvents(input::InputState &state) final; 26 | WindowHandle window() final; 27 | 28 | private: 29 | memory::LinearAllocator m_allocator; 30 | SDL_Window *m_window; 31 | }; 32 | 33 | }; // namespace platform 34 | } // namespace castlekeep 35 | #endif 36 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2018 David Leiter 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | 16 | #include "engine.h" 17 | 18 | #include 19 | using namespace memory::literals; 20 | int main() 21 | { 22 | auto engine = castlekeep::core::Engine::create(256_mib); 23 | engine->startUp(); 24 | engine->loop(); 25 | engine->shutDown(); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(utils INTERFACE) 2 | target_sources(utils INTERFACE 3 | "${CMAKE_CURRENT_SOURCE_DIR}/utils.h" 4 | "${CMAKE_CURRENT_SOURCE_DIR}/debug.h" 5 | "${CMAKE_CURRENT_SOURCE_DIR}/debug.cpp" 6 | "${CMAKE_CURRENT_SOURCE_DIR}/container/array.h" 7 | "${CMAKE_CURRENT_SOURCE_DIR}/memory/allocator.h" 8 | "${CMAKE_CURRENT_SOURCE_DIR}/memory/linear_allocator.h" 9 | "${CMAKE_CURRENT_SOURCE_DIR}/memory/block_allocator.h" 10 | "${CMAKE_CURRENT_SOURCE_DIR}/memory/freelist_allocator.h" 11 | "${CMAKE_CURRENT_SOURCE_DIR}/memory/freelist_allocator.cpp" 12 | "${CMAKE_CURRENT_SOURCE_DIR}/memory/polymorphic_allocator_wrappers.h" 13 | "${CMAKE_CURRENT_SOURCE_DIR}/memory/polymorphic_allocator_wrappers.cpp") 14 | 15 | target_include_directories(utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) 16 | target_compile_features(utils INTERFACE cxx_std_17) 17 | 18 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 19 | target_compile_definitions(utils INTERFACE -DDEBUG_BUILD) 20 | endif() 21 | -------------------------------------------------------------------------------- /src/loaders/file.cpp: -------------------------------------------------------------------------------- 1 | #include "file.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace castlekeep 7 | { 8 | namespace loader 9 | { 10 | File::File() : m_file(nullptr) {} 11 | int File::open(const char* path, const char* mode) 12 | { 13 | m_file = std::fopen(path, mode); 14 | if (m_file == nullptr) { 15 | return -1; 16 | } 17 | return 0; 18 | } 19 | 20 | void File::close() 21 | { 22 | if (m_file != nullptr) { 23 | std::fclose(m_file); 24 | } 25 | } 26 | 27 | char* File::readLine(char* buffer, size_t size) 28 | { 29 | return std::fgets(buffer, size, m_file); 30 | } 31 | 32 | size_t File::readBytes(std::byte* buffer, size_t num) 33 | { 34 | return std::fread(buffer, 1, num, m_file); 35 | } 36 | 37 | File::~File() 38 | { 39 | close(); 40 | } 41 | 42 | int File::seek(size_t pos) 43 | { 44 | return std::fseek(m_file, pos, SEEK_SET); 45 | } 46 | 47 | size_t File::size() 48 | { 49 | size_t pos = std::ftell(m_file); 50 | std::fseek(m_file, 0, SEEK_END); 51 | size_t size = std::ftell(m_file); 52 | seek(pos); 53 | return size; 54 | } 55 | 56 | } // namespace loader 57 | } // namespace castlekeep 58 | -------------------------------------------------------------------------------- /src/loaders/data_loader.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_LOADER_H 2 | #define DATA_LOADER_H 3 | 4 | #include "memory/linear_allocator.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include "file.h" 10 | 11 | namespace castlekeep 12 | { 13 | namespace loader 14 | { 15 | enum class Type { INTEGER, STRING }; 16 | struct Value { 17 | Type type; 18 | union { 19 | int number; 20 | const char *string; 21 | }; 22 | }; 23 | 24 | class DataLoader 25 | { 26 | public: 27 | DataLoader(const memory::Arena &arena, File &file); 28 | 29 | ~DataLoader(); 30 | 31 | int begin(const char *type); 32 | 33 | int readSectionInfo(const char **name, int &rows, int &columns); 34 | 35 | int readRow(Value row[], int columns); 36 | 37 | int skipRows(int rows); 38 | 39 | private: 40 | int readLine(); 41 | 42 | int skipWhitespace(int pos); 43 | 44 | int parseInteger(int &val, int pos); 45 | 46 | int parseName(char *val, int size, int pos); 47 | 48 | File &m_file; 49 | char *m_line_buffer; 50 | char *m_section_name; 51 | char *m_format; 52 | memory::LinearAllocator m_allocator; 53 | }; 54 | } // namespace loader 55 | } // namespace castlekeep 56 | #endif 57 | -------------------------------------------------------------------------------- /src/renderer/renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERER_H 2 | #define RENDERER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace castlekeep 8 | { 9 | namespace render 10 | { 11 | typedef uint16_t TextureHandle; 12 | 13 | constexpr TextureHandle k_texture_invalid = 0; 14 | 15 | struct DrawCommand { 16 | int16_t dst_x; 17 | int16_t dst_y; 18 | int16_t src_x; 19 | int16_t src_y; 20 | uint16_t width; 21 | uint16_t height; 22 | uint32_t depth; 23 | }; 24 | 25 | struct DrawCommandBuffer { 26 | DrawCommand *commands; 27 | int length; 28 | TextureHandle texture_id; 29 | }; 30 | 31 | enum class PixelFormat { argb8888, palettized }; 32 | 33 | class RenderInterface 34 | { 35 | public: 36 | virtual ~RenderInterface() = 0; 37 | 38 | virtual TextureHandle createTexture(int width, int height, 39 | PixelFormat format, 40 | const std::byte *data) = 0; 41 | virtual int startUp() = 0; 42 | virtual void startFrame() = 0; 43 | virtual void draw(const DrawCommandBuffer &buffer) = 0; 44 | virtual void endFrame() = 0; 45 | 46 | virtual int width() = 0; 47 | virtual int height() = 0; 48 | }; 49 | 50 | } // namespace render 51 | } // namespace castlekeep 52 | #endif 53 | -------------------------------------------------------------------------------- /src/input_manager.h: -------------------------------------------------------------------------------- 1 | #ifndef INPUT_H 2 | #define INPUT_H 3 | 4 | #include 5 | 6 | namespace castlekeep 7 | { 8 | namespace input 9 | { 10 | enum Key { 11 | k_key_0 = 0, 12 | k_key_1 = 1, 13 | k_key_2 = 2, 14 | k_key_3 = 3, 15 | k_key_4 = 4, 16 | k_key_5 = 5, 17 | k_key_6 = 6, 18 | k_key_7 = 7, 19 | k_key_8 = 8, 20 | k_key_9 = 9, 21 | k_key_q = 10, 22 | k_key_w = 11, 23 | k_key_e = 12, 24 | k_key_r = 13, 25 | k_key_t = 14, 26 | k_key_y = 15, 27 | k_key_u = 16, 28 | k_key_i = 17, 29 | k_key_o = 18, 30 | k_key_p = 19, 31 | k_key_a = 20, 32 | k_key_s = 21, 33 | k_key_d = 22, 34 | k_key_f = 23, 35 | k_key_g = 24, 36 | k_key_h = 25, 37 | k_key_j = 26, 38 | k_key_k = 27, 39 | k_key_l = 28, 40 | k_key_z = 29, 41 | k_key_x = 30, 42 | k_key_c = 31, 43 | k_key_v = 32, 44 | k_key_b = 33, 45 | k_key_n = 34, 46 | k_key_m = 35, 47 | k_key_space = 36, 48 | k_key_return = 37, 49 | k_key_backspace = 38, 50 | k_key_arrow_up = 39, 51 | k_key_arrow_down = 40, 52 | k_key_arrow_left = 41, 53 | k_key_arrow_right = 42 54 | }; 55 | 56 | struct MouseState { 57 | bool left_button_down; 58 | bool right_button_down; 59 | bool middle_button_down; 60 | int16_t x; 61 | int16_t y; 62 | int16_t dx; 63 | int16_t dy; 64 | }; 65 | 66 | struct KeyState { 67 | bool key_down; 68 | bool keys[43]; 69 | }; 70 | 71 | struct InputState { 72 | MouseState mouse; 73 | KeyState keyboard; 74 | }; 75 | 76 | class InputManager 77 | { 78 | public: 79 | InputManager(); 80 | bool readInput(); 81 | const InputState& state() const { return m_state; } 82 | 83 | private: 84 | InputState m_state; 85 | }; 86 | 87 | }; // namespace input 88 | }; // namespace castlekeep 89 | #endif 90 | -------------------------------------------------------------------------------- /src/utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace utils 9 | { 10 | template 11 | static constexpr bool isPowerOfTwo(T a) 12 | { 13 | static_assert(std::is_unsigned::value, 14 | "isPowerOfTwo requires an unsigned integer parameter"); 15 | return (a & (a - 1)) == 0; 16 | } 17 | 18 | template 19 | static constexpr T nextPowerOfTwo(T a) 20 | { 21 | static_assert(std::is_unsigned::value, 22 | "nextPowerOfTwo requires an unsigned integer parameter"); 23 | a--; 24 | a = a | (a >> 1); 25 | a = a | (a >> 2); 26 | a = a | (a >> 4); 27 | if constexpr (sizeof(T) == 1) { 28 | return a + 1; 29 | } 30 | a = a | (a >> 8); 31 | if constexpr (sizeof(T) == 2) { 32 | return a + 1; 33 | } 34 | a = a | (a >> 16); 35 | if constexpr (sizeof(T) == 4) { 36 | return a + 1; 37 | } 38 | a = a | (a >> 32); 39 | return a + 1; 40 | } 41 | 42 | static size_t alignmentOffset(void *address, size_t alignment) 43 | { 44 | std::byte *addr = reinterpret_cast(address); 45 | size_t misalignment = 46 | (reinterpret_cast(addr - 1) & (alignment - 1)); 47 | return alignment - misalignment - 1; 48 | } 49 | static constexpr size_t roundDownToMultipleOfAlignment(size_t size, 50 | size_t alignment) 51 | { 52 | return size & ~(alignment - 1); 53 | } 54 | 55 | static constexpr size_t roundUpToMultipleOfAlignment(size_t size, 56 | size_t alignment) 57 | { 58 | return ((size - 1) & ~(alignment - 1)) + alignment; 59 | } 60 | } // namespace utils 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/utils/memory/polymorphic_allocator_wrappers.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYMORPHIC_ALLOCATOR_WRAPPERS 2 | #define POLYMORPHIC_ALLOCATOR_WRAPPERS 3 | 4 | #include "allocator.h" 5 | #include "block_allocator.h" 6 | #include "linear_allocator.h" 7 | 8 | namespace memory 9 | { 10 | class AbstractAllocator 11 | { 12 | public: 13 | virtual ~AbstractAllocator() = 0; 14 | virtual void *allocate(size_t size) = 0; 15 | virtual void *allocate_aligned(size_t size, size_t alignment) = 0; 16 | 17 | virtual void deallocate(void *address) = 0; 18 | }; 19 | 20 | template 21 | using AbstractContainerAllocator = 22 | ContainerAllocatorAdapter; 23 | 24 | class LinearAllocatorAdapter : public AbstractAllocator 25 | { 26 | public: 27 | LinearAllocatorAdapter(LinearAllocator &allocator); 28 | ~LinearAllocatorAdapter() final; 29 | void *allocate(size_t size) final; 30 | void *allocate_aligned(size_t size, size_t alignment) final; 31 | void deallocate(void *address) final; 32 | 33 | private: 34 | LinearAllocator &m_allocator; 35 | }; 36 | 37 | template 38 | class BlockAllocatorAdapter : public AbstractAllocator 39 | { 40 | public: 41 | BlockAllocatorAdapter(BlockAllocator &allocator) 42 | : m_allocator(allocator) 43 | { 44 | } 45 | ~BlockAllocatorAdapter() final {} 46 | void *allocate(size_t size) final { return m_allocator->allocate(size); } 47 | void *allocate_aligned(size_t size, size_t alignment) final 48 | { 49 | return m_allocator.allocate_aligned(size, alignment); 50 | } 51 | 52 | void deallocate(void *address) final 53 | { 54 | return m_allocator.deallocate(address); 55 | } 56 | 57 | private: 58 | BlockAllocator &m_allocator; 59 | }; 60 | 61 | } // namespace memory 62 | #endif 63 | -------------------------------------------------------------------------------- /src/renderer/gl_renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef GL_RENDERER_H 2 | #define GL_RENDERER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "container/array.h" 8 | #include "memory/linear_allocator.h" 9 | #include "renderer.h" 10 | #include "system.h" 11 | 12 | namespace castlekeep 13 | { 14 | namespace render 15 | { 16 | struct DrawCall { 17 | TextureHandle texture; 18 | int start; 19 | int size; 20 | }; 21 | 22 | struct Frame { 23 | DrawCall *draw_calls; 24 | int draw_call_count; 25 | }; 26 | 27 | using namespace memory::literals; 28 | class GLRenderSystem : public RenderInterface, 29 | public core::EngineSystem 30 | { 31 | using Allocator = 32 | memory::ContainerAllocatorAdapter; 33 | 34 | public: 35 | constexpr static size_t k_minimal_arena_size = 1_mib; 36 | GLRenderSystem(const memory::Arena &arena, SDL_Window *window, 37 | size_t max_textures); 38 | 39 | ~GLRenderSystem() final; 40 | 41 | TextureHandle createTexture(int width, int height, PixelFormat format, 42 | const std::byte *data) final; 43 | int startUp() final; 44 | 45 | void startFrame() final; 46 | void draw(const DrawCommandBuffer &buffer) final; 47 | void endFrame() final; 48 | 49 | int width() final; 50 | int height() final; 51 | 52 | private: 53 | GLuint texture(TextureHandle id); 54 | constexpr static size_t k_vbo_size = 100_mib; 55 | SDL_Window *m_window; 56 | SDL_GLContext m_gl_context; 57 | memory::LinearAllocator m_allocator; 58 | size_t m_num_textures; 59 | container::Array m_textures; 60 | GLuint m_vao; 61 | Frame m_frame; 62 | int m_width; 63 | int m_height; 64 | }; 65 | 66 | } // namespace render 67 | } // namespace castlekeep 68 | #endif 69 | -------------------------------------------------------------------------------- /src/utils/debug.cpp: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | 3 | #ifdef DEBUG_BUILD 4 | 5 | #include 6 | #include 7 | 8 | void callstack(); 9 | 10 | #ifdef __linux__ 11 | #include 12 | #include 13 | 14 | #define NUM_ADDRESSES 1024 15 | static void* addresses[NUM_ADDRESSES]; 16 | 17 | const char* demangle(char* symbol, char* malloced_buffer, size_t* length) 18 | { 19 | int pos = 0; 20 | char c = 0; 21 | char* name; 22 | while (((c = symbol[pos]) != 0) && (c != '(')) { 23 | pos++; 24 | } 25 | if (c == '(') { 26 | pos++; 27 | } 28 | name = &symbol[pos]; 29 | while (((c = symbol[pos]) != 0) && (c != '+')) { 30 | pos++; 31 | } 32 | symbol[pos] = 0; 33 | 34 | const char* demangled = 35 | abi::__cxa_demangle(name, malloced_buffer, length, nullptr); 36 | if (demangled == nullptr) { 37 | demangled = name; 38 | } 39 | 40 | return demangled; 41 | } 42 | 43 | #endif 44 | 45 | [[noreturn]] void debug_error(const char* msg, const char* file, const int line) 46 | { 47 | std::fprintf(stderr, "Error: \"%s\" in file \"%s\" on line %d", msg, file, 48 | line); 49 | #ifdef __linux__ 50 | fprintf(stderr, 51 | " during execution of the following functions:\n--------\n"); 52 | char** symbols; 53 | int num = backtrace(addresses, NUM_ADDRESSES); 54 | symbols = backtrace_symbols(addresses, num); 55 | size_t buffer_length = 1024; 56 | char* buffer = reinterpret_cast(std::malloc(buffer_length)); 57 | for (int i = 1; i < num; i++) { 58 | const char* demangled = demangle(symbols[i], buffer, &buffer_length); 59 | if (demangled != nullptr) { 60 | std::fprintf(stderr, "%d: %s\n--------\n", i, demangled); 61 | } 62 | } 63 | std::free(buffer); 64 | #elif 65 | std::fprintf(stderr, "\n"); 66 | #endif 67 | std::abort(); 68 | } 69 | #endif 70 | -------------------------------------------------------------------------------- /src/utils/memory/freelist_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef FREELIST_ALLOCATOR 2 | #define FREELIST_ALLOCATOR 3 | 4 | #include "allocator.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace memory 10 | { 11 | class FreeListAllocator 12 | { 13 | public: 14 | FreeListAllocator(void *memory, size_t size); 15 | FreeListAllocator(const Arena ®ion); 16 | ~FreeListAllocator(); 17 | 18 | void *allocate(size_t size); 19 | void *allocate_aligned(size_t size, size_t alignment); 20 | 21 | void deallocate(void *address); 22 | 23 | // void *allocate_aligned_old(size_t size, size_t alignment); 24 | // void deallocate_old(void *address); 25 | 26 | private: 27 | struct ChunkHeader { 28 | ChunkHeader *next; 29 | uint32_t size; 30 | uint16_t is_free; 31 | uint16_t padding; 32 | }; 33 | 34 | struct ChunkTrailer { 35 | uint32_t size; 36 | uint32_t is_free; 37 | }; 38 | struct FreeChunkTrailer { 39 | ChunkHeader *prev; 40 | uint32_t size; 41 | uint32_t is_free; 42 | }; 43 | 44 | static ChunkHeader *header(std::byte *ptr) 45 | { 46 | return reinterpret_cast(ptr); 47 | } 48 | 49 | static ChunkTrailer *trailer(std::byte *ptr) 50 | { 51 | return reinterpret_cast(ptr); 52 | } 53 | static FreeChunkTrailer *freeTrailer(void *ptr) 54 | { 55 | return reinterpret_cast(ptr); 56 | } 57 | 58 | static std::byte *bytePtr(void *ptr) 59 | { 60 | return reinterpret_cast(ptr); 61 | } 62 | 63 | static constexpr size_t minimal_split_size = 64 | std::max(static_cast(16), sizeof(ChunkHeader *)); 65 | static constexpr size_t allocation_size_alignment = 66 | std::max(static_cast(16), alignof(ChunkHeader)); 67 | 68 | std::byte *m_memory; 69 | ChunkHeader *m_free_list; 70 | size_t m_size; 71 | }; 72 | 73 | } // namespace memory 74 | #endif 75 | -------------------------------------------------------------------------------- /src/engine.h: -------------------------------------------------------------------------------- 1 | #ifndef ENGINE_H 2 | #define ENGINE_H 3 | 4 | #include "memory/allocator.h" 5 | #include "memory/linear_allocator.h" 6 | 7 | #include "game/game.h" 8 | #include "graphics_manager.h" 9 | #include "input_manager.h" 10 | #include "loaders/image_loader.h" 11 | #include "platform/platform.h" 12 | #include "renderer/renderer.h" 13 | 14 | namespace castlekeep 15 | { 16 | namespace core 17 | { 18 | class Engine 19 | { 20 | public: 21 | static Engine *g_engine; 22 | static std::unique_ptr create(size_t memory_size) 23 | { 24 | if (instanciated == true) { 25 | return nullptr; 26 | } 27 | g_engine = new Engine(memory_size); 28 | return std::unique_ptr(g_engine); 29 | } 30 | 31 | int startUp(); 32 | int loop(); 33 | int shutDown(); 34 | 35 | platform::PlatformInterface *platform() { return m_platform.get(); } 36 | render::RenderInterface *renderer() { return m_renderer.get(); } 37 | graphics::GraphicsManager *graphicsManager() 38 | { 39 | return m_graphics_manager.get(); 40 | } 41 | input::InputManager *inputManager() { return m_input_manager.get(); } 42 | 43 | private: 44 | Engine(size_t memory_size); 45 | memory::HeapRegion m_memory; 46 | memory::LinearAllocator m_global_allocator; 47 | memory::LinearAllocator m_systems_allocator; 48 | 49 | memory::UniquePtr 50 | m_platform; 51 | memory::UniquePtr 52 | m_renderer; 53 | memory::UniquePtr 54 | m_graphics_manager; 55 | memory::UniquePtr 56 | m_input_manager; 57 | memory::UniquePtr m_game; 58 | 59 | static inline bool instanciated = false; 60 | }; 61 | 62 | } // namespace core 63 | } // namespace castlekeep 64 | #endif 65 | -------------------------------------------------------------------------------- /src/utils/memory/linear_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef LINEAR_ALLOCATOR 2 | #define LINEAR_ALLOCATOR 3 | 4 | #include "allocator.h" 5 | #include "debug.h" 6 | #include "utils.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace memory 12 | { 13 | class LinearAllocator 14 | { 15 | public: 16 | using Marker = ptrdiff_t; 17 | class MarkerGuard 18 | { 19 | friend class LinearAllocator; 20 | 21 | public: 22 | ~MarkerGuard() { m_allocator.reset(m_marker); } 23 | 24 | MarkerGuard(const MarkerGuard &other) = delete; 25 | MarkerGuard(MarkerGuard &&other) = delete; 26 | 27 | private: 28 | MarkerGuard(LinearAllocator &allocator, Marker marker) 29 | : m_allocator(allocator), m_marker(marker) 30 | { 31 | } 32 | LinearAllocator &m_allocator; 33 | Marker m_marker; 34 | }; 35 | 36 | LinearAllocator(void *memory, size_t size) 37 | : m_memory(reinterpret_cast(memory)), 38 | m_free(m_memory), 39 | m_end(m_memory + size) 40 | { 41 | } 42 | LinearAllocator(const Arena &arena) 43 | : LinearAllocator(arena.memory, arena.size) 44 | { 45 | } 46 | 47 | ~LinearAllocator() {} 48 | 49 | void *allocate(size_t size) { return allocate_aligned(size, 1); } 50 | void *allocate_aligned(size_t size, size_t alignment) 51 | { 52 | std::byte *address = nullptr; 53 | size_t offset = utils::alignmentOffset(m_free, alignment); 54 | 55 | auto next_free = m_free + size + offset; 56 | if (next_free < m_end) { 57 | address = m_free + offset; 58 | m_free = next_free; 59 | } 60 | DEBUG_ASSERT(address != nullptr, "Not enough memory!"); 61 | return address; 62 | } 63 | 64 | void deallocate(void *address) { return; } 65 | 66 | void reset(Marker marker = 0) { m_free = m_memory + marker; } 67 | 68 | Marker mark() const { return m_free - m_memory; } 69 | MarkerGuard markGuarded() { return MarkerGuard(*this, m_free - m_memory); } 70 | 71 | private: 72 | std::byte *m_memory; 73 | std::byte *m_free; 74 | std::byte *m_end; 75 | }; 76 | 77 | } // namespace memory 78 | #endif 79 | -------------------------------------------------------------------------------- /src/game/world_renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_RENDERER_H 2 | #define WORLD_RENDERER_H 3 | 4 | #include "graphics_manager.h" 5 | #include "memory/allocator.h" 6 | #include "renderer/renderer.h" 7 | #include "system.h" 8 | #include "world.h" 9 | 10 | namespace castlekeep 11 | { 12 | namespace game 13 | { 14 | class Camera 15 | { 16 | public: 17 | Camera() : m_x(0), m_y(0), m_maxx(0), m_maxy(0) {} 18 | Camera(int maxx, int maxy) : m_x(0), m_y(0), m_maxx(maxx), m_maxy(maxy) {} 19 | Camera(int x, int y, int maxx, int maxy) 20 | : m_x(x), m_y(y), m_maxx(maxx), m_maxy(maxy) 21 | { 22 | } 23 | void setMaxPos(int maxx, int maxy) 24 | { 25 | m_maxx = maxx; 26 | m_maxy = maxy; 27 | } 28 | 29 | void move(int dx, int dy) 30 | { 31 | int x = m_x + dx; 32 | int y = m_y + dy; 33 | 34 | if (x < 0) { 35 | m_x = 0; 36 | } else if (x > m_maxx) { 37 | m_x = m_maxx; 38 | } else { 39 | m_x = x; 40 | } 41 | if (y < 0) { 42 | m_y = 0; 43 | } else if (y > m_maxy) { 44 | m_y = m_maxy; 45 | } else { 46 | m_y = y; 47 | } 48 | } 49 | int x() const { return m_x; } 50 | int y() const { return m_y; } 51 | 52 | private: 53 | int m_x; 54 | int m_y; 55 | int m_maxx; 56 | int m_maxy; 57 | }; 58 | 59 | using namespace memory::literals; 60 | class WorldRenderer : public core::EngineSystem 61 | { 62 | public: 63 | constexpr static size_t k_minimal_arena_size = 10_mib; 64 | 65 | constexpr static int k_tile_width = 32; 66 | constexpr static int k_tile_height = 16; 67 | 68 | WorldRenderer(const memory::Arena &arena, World *world); 69 | ~WorldRenderer(); 70 | int startUp(); 71 | int renderWorld(const Camera &camera); 72 | int worldWidth() const; 73 | int worldHeight() const; 74 | 75 | private: 76 | int renderTerrain(); 77 | void renderTile(render::DrawCommandBuffer &draw_commads, MapCoord pos, 78 | const Camera &c, int layer, const graphics::Rect &rect); 79 | memory::LinearAllocator m_allocator; 80 | World *m_world; 81 | }; 82 | } // namespace game 83 | } // namespace castlekeep 84 | #endif 85 | -------------------------------------------------------------------------------- /src/utils/memory/block_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK_ALLOCATOR 2 | #define BLOCK_ALLOCATOR 3 | 4 | #include "allocator.h" 5 | #include "debug.h" 6 | #include "utils.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace memory 12 | { 13 | template 14 | class BlockAllocator 15 | { 16 | public: 17 | BlockAllocator(void *memory, size_t size, size_t alignment = 16) 18 | : m_num_blocks(utils::roundDownToMultipleOfAlignment(size, alignment) / 19 | block_size), 20 | m_allocated(0), 21 | m_memory(reinterpret_cast(memory) + 22 | utils::alignmentOffset(memory, alignment)) 23 | { 24 | static_assert(block_size > sizeof(BlockHeader *), 25 | "The size of a block has to be at least as larg as the " 26 | "size of a pointer"); 27 | m_free = reinterpret_cast(memory); 28 | m_free->next = nullptr; 29 | } 30 | BlockAllocator(const Arena ®ion, size_t alignment = 16) 31 | : BlockAllocator(region.memory, region.size, alignment) 32 | { 33 | } 34 | 35 | ~BlockAllocator() {} 36 | 37 | void *allocate() 38 | { 39 | void *block = nullptr; 40 | if (m_allocated <= m_num_blocks) { 41 | block = m_free; 42 | if (m_free->next == nullptr) { 43 | m_free = m_free + block_size; 44 | m_free->next = nullptr; 45 | } else { 46 | m_free = m_free->next; 47 | } 48 | m_allocated++; 49 | } 50 | DEBUG_ASSERT(block != nullptr, "Not enough memory!"); 51 | return block; 52 | } 53 | void *allocate(size_t size) { return allocate(); } 54 | void *allocate_aligned(size_t size, size_t alignment) { return allocate(); } 55 | 56 | void deallocate(void *address) 57 | { 58 | if (address != nullptr) { 59 | BlockHeader *tmp = m_free; 60 | m_free = reinterpret_cast(address); 61 | m_free->next = tmp; 62 | m_allocated--; 63 | } 64 | } 65 | 66 | private: 67 | struct BlockHeader { 68 | BlockHeader *next; 69 | }; 70 | 71 | size_t m_num_blocks; 72 | size_t m_allocated; 73 | std::byte *m_memory; 74 | BlockHeader *m_free; 75 | }; 76 | 77 | } // namespace memory 78 | #endif 79 | -------------------------------------------------------------------------------- /src/game/game.cpp: -------------------------------------------------------------------------------- 1 | #include "game.h" 2 | #include "engine.h" 3 | 4 | namespace castlekeep 5 | { 6 | namespace game 7 | { 8 | Game::Game(const memory::Arena &arena) 9 | : m_allocator(arena), 10 | m_world(nullptr, m_allocator), 11 | m_world_renderer(nullptr, m_allocator), 12 | m_camera() 13 | { 14 | m_input_manager = core::Engine::g_engine->inputManager(); 15 | } 16 | 17 | Game::~Game() {} 18 | void Game::startUp() 19 | { 20 | m_world = memory::createUniquePtrObject( 21 | m_allocator, 22 | memory::Arena(m_allocator, game::World::k_minimal_arena_size), 400); 23 | m_world_renderer = memory::createUniquePtrObject( 24 | m_allocator, 25 | memory::Arena(m_allocator, game::WorldRenderer::minimalArenaSize()), 26 | m_world.get()); 27 | int width = core::Engine::g_engine->renderer()->width(); 28 | int height = core::Engine::g_engine->renderer()->height(); 29 | 30 | m_camera.setMaxPos(m_world_renderer->worldWidth() - width, 31 | m_world_renderer->worldHeight() - height); 32 | 33 | m_world->loadAssets(); 34 | m_world->generateTerrain(); 35 | } 36 | 37 | void Game::update() 38 | { 39 | int dx = 0; 40 | int dy = 0; 41 | int width = core::Engine::g_engine->renderer()->width(); 42 | int height = core::Engine::g_engine->renderer()->height(); 43 | 44 | auto state = m_input_manager->state(); 45 | 46 | if (state.keyboard.keys[input::k_key_arrow_left]) { 47 | dx = -10; 48 | } else if (state.keyboard.keys[input::k_key_arrow_right]) { 49 | dx = 10; 50 | } 51 | if (state.keyboard.keys[input::k_key_arrow_up]) { 52 | dy = -10; 53 | } else if (state.keyboard.keys[input::k_key_arrow_down]) { 54 | dy = 10; 55 | } 56 | 57 | if (state.mouse.x == 0) { 58 | dx = -10; 59 | } else if (state.mouse.x >= width - 1) { 60 | dx = 10; 61 | } 62 | if (state.mouse.y == 0) { 63 | dy = -10; 64 | } else if (state.mouse.y >= height - 1) { 65 | dy = 10; 66 | } 67 | m_camera.move(dx, dy); 68 | } 69 | 70 | void Game::render() 71 | { 72 | m_world_renderer->renderWorld(m_camera); 73 | } 74 | 75 | void Game::shutDown() {} 76 | 77 | }; // namespace game 78 | }; // namespace castlekeep 79 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibEpoxy.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libepoxy. 2 | # Once done, this will define 3 | # 4 | # LIBEPOXY_INCLUDE_DIRS - the libtasn1 include directories 5 | # LIBEPOXY_LIBRARIES - the libtasn1 libraries. 6 | # 7 | # Copyright (C) 2017 Igalia S.L. 8 | # 9 | # Redistribution and use in source and binary forms, with or without 10 | # modification, are permitted provided that the following conditions 11 | # are met: 12 | # 1. Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # 2. Redistributions in binary form must reproduce the above copyright 15 | # notice, this list of conditions and the following disclaimer in the 16 | # documentation and/or other materials provided with the distribution. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 20 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 22 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 | # THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | find_package(PkgConfig) 31 | pkg_check_modules(PC_LIBEPOXY QUIET epoxy) 32 | 33 | if (PC_LIBEPOXY_FOUND) 34 | set(LIBEPOXY_DEFINITIONS ${PC_LIBEPOXY_CFLAGS_OTHER}) 35 | endif () 36 | 37 | find_path(LIBEPOXY_INCLUDE_DIRS 38 | NAMES epoxy/gl.h 39 | PATHS ${PC_LIBEPOXY_INCLUDEDIR} ${PC_LIBEPOXY_INCLUDE_DIRS} 40 | ) 41 | 42 | find_library(LIBEPOXY_LIBRARIES 43 | NAMES epoxy 44 | PATHS ${PC_LIBEPOXY_LIBDIR} ${PC_LIBEPOXY_LIBRARY_DIRS} 45 | ) 46 | 47 | mark_as_advanced(LIBEPOXY_INCLUDE_DIRS LIBEPOXY_LIBRARIES) 48 | 49 | include(FindPackageHandleStandardArgs) 50 | find_package_handle_standard_args(LibEpoxy REQUIRED_VARS LIBEPOXY_INCLUDE_DIRS LIBEPOXY_LIBRARIES 51 | VERSION_VAR PC_LIBEPOXY_VERSION) 52 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(utils) 2 | add_executable(${PROJECT_NAME} main.cpp) 3 | target_sources(${PROJECT_NAME} PRIVATE 4 | "${CMAKE_CURRENT_SOURCE_DIR}/engine.h" 5 | "${CMAKE_CURRENT_SOURCE_DIR}/engine.cpp" 6 | "${CMAKE_CURRENT_SOURCE_DIR}/game/game.h" 7 | "${CMAKE_CURRENT_SOURCE_DIR}/game/game.cpp" 8 | "${CMAKE_CURRENT_SOURCE_DIR}/game/world.h" 9 | "${CMAKE_CURRENT_SOURCE_DIR}/game/world.cpp" 10 | "${CMAKE_CURRENT_SOURCE_DIR}/game/world_renderer.cpp" 11 | "${CMAKE_CURRENT_SOURCE_DIR}/game/world_renderer.h" 12 | "${CMAKE_CURRENT_SOURCE_DIR}/system.h" 13 | "${CMAKE_CURRENT_SOURCE_DIR}/graphics_manager.h" 14 | "${CMAKE_CURRENT_SOURCE_DIR}/graphics_manager.cpp" 15 | "${CMAKE_CURRENT_SOURCE_DIR}/assets.h" 16 | "${CMAKE_CURRENT_SOURCE_DIR}/input_manager.h" 17 | "${CMAKE_CURRENT_SOURCE_DIR}/input_manager.cpp" 18 | "${CMAKE_CURRENT_SOURCE_DIR}/renderer/renderer.h" 19 | "${CMAKE_CURRENT_SOURCE_DIR}/renderer/renderer.cpp" 20 | "${CMAKE_CURRENT_SOURCE_DIR}/renderer/gl_renderer.h" 21 | "${CMAKE_CURRENT_SOURCE_DIR}/renderer/gl_renderer.cpp" 22 | "${CMAKE_CURRENT_SOURCE_DIR}/platform/platform.h" 23 | "${CMAKE_CURRENT_SOURCE_DIR}/platform/platform.cpp" 24 | "${CMAKE_CURRENT_SOURCE_DIR}/platform/sdl_platform.h" 25 | "${CMAKE_CURRENT_SOURCE_DIR}/platform/sdl_platform.cpp" 26 | "${CMAKE_CURRENT_SOURCE_DIR}/loaders/file.h" 27 | "${CMAKE_CURRENT_SOURCE_DIR}/loaders/file.cpp" 28 | "${CMAKE_CURRENT_SOURCE_DIR}/loaders/image_loader.h" 29 | "${CMAKE_CURRENT_SOURCE_DIR}/loaders/image_loader.cpp" 30 | "${CMAKE_CURRENT_SOURCE_DIR}/loaders/data_loader.h" 31 | "${CMAKE_CURRENT_SOURCE_DIR}/loaders/data_loader.cpp") 32 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) 33 | target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 34 | 35 | target_include_directories(${PROJECT_NAME} PRIVATE ${LIBEPOXY_INCLUDE_DIRS}) 36 | 37 | include(CheckIPOSupported) 38 | check_ipo_supported(RESULT result OUTPUT output) 39 | if(result) 40 | set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) 41 | endif() 42 | 43 | target_compile_options(${PROJECT_NAME} PUBLIC "-fno-exceptions") 44 | target_compile_options(${PROJECT_NAME} PUBLIC "-fno-rtti") 45 | 46 | target_link_libraries(${PROJECT_NAME} PRIVATE utils PNG::PNG SDL2::SDL2 OpenGL::GL ${LIBEPOXY_LIBRARIES}) 47 | 48 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 49 | set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS TRUE) 50 | endif() 51 | -------------------------------------------------------------------------------- /src/engine.cpp: -------------------------------------------------------------------------------- 1 | #include "engine.h" 2 | 3 | #include "assets.h" 4 | #include "loaders/data_loader.h" 5 | #include "platform/sdl_platform.h" 6 | #include "renderer/gl_renderer.h" 7 | #include "utils/memory/allocator.h" 8 | 9 | #include 10 | 11 | namespace castlekeep 12 | { 13 | namespace core 14 | { 15 | using namespace memory::literals; 16 | 17 | Engine* Engine::g_engine = nullptr; 18 | 19 | Engine::Engine(size_t mem_size) 20 | : m_memory(mem_size), 21 | m_global_allocator(m_memory.memory()), 22 | m_systems_allocator(memory::Arena(m_global_allocator, 10_kib)), 23 | m_platform(nullptr, m_systems_allocator), 24 | m_renderer(nullptr, m_systems_allocator), 25 | m_graphics_manager(nullptr, m_systems_allocator), 26 | m_input_manager(nullptr, m_systems_allocator), 27 | m_game(nullptr, m_systems_allocator) 28 | { 29 | } 30 | 31 | int Engine::startUp() 32 | { 33 | auto platform = memory::createObject( 34 | m_systems_allocator, 35 | memory::Arena(m_global_allocator, 36 | platform::SDLPlatform::minimalArenaSize())); 37 | platform->startUp(); 38 | m_platform = memory::makeUnique( 39 | platform, m_systems_allocator); 40 | m_platform->createWindow(1920, 1080, "Castlekeep"); 41 | auto renderer = memory::createObject( 42 | m_systems_allocator, 43 | memory::Arena(m_global_allocator, 44 | render::GLRenderSystem::minimalArenaSize()), 45 | reinterpret_cast(m_platform->window().window), 46 | assets::k_num_graphics_assets); 47 | if (renderer->startUp() != 0) { 48 | fprintf(stderr, "renderer initialization failed!\n"); 49 | exit(1); 50 | } 51 | m_renderer = memory::makeUnique( 52 | renderer, m_systems_allocator); 53 | 54 | m_graphics_manager = 55 | memory::createUniquePtrObject( 56 | m_systems_allocator, 57 | memory::Arena(m_global_allocator, 58 | graphics::GraphicsManager::minimalArenaSize()), 59 | assets::k_num_tile_assets, assets::k_num_animation_assets, 60 | assets::k_num_generic_image_assets); 61 | 62 | m_graphics_manager->startUp(); 63 | 64 | m_input_manager = 65 | memory::createUniquePtrObject(m_systems_allocator); 66 | 67 | m_game = memory::createUniquePtrObject( 68 | m_systems_allocator, 69 | memory::Arena(m_global_allocator, game::Game::k_minimal_arena_size)); 70 | m_game->startUp(); 71 | 72 | return 0; 73 | } 74 | 75 | int Engine::loop() 76 | { 77 | bool is_running = true; 78 | while (is_running) { 79 | is_running = m_input_manager->readInput(); 80 | m_game->update(); 81 | m_renderer->startFrame(); 82 | m_game->render(); 83 | m_renderer->endFrame(); 84 | } 85 | return 0; 86 | } 87 | 88 | int Engine::shutDown() 89 | { 90 | return 0; 91 | } 92 | 93 | } // namespace core 94 | } // namespace castlekeep 95 | -------------------------------------------------------------------------------- /src/graphics_manager.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPHICS_MANAGER_H 2 | #define GRAPHICS_MANAGER_H 3 | 4 | #include "container/array.h" 5 | #include "memory/linear_allocator.h" 6 | #include "renderer/renderer.h" 7 | #include "system.h" 8 | 9 | namespace castlekeep 10 | { 11 | namespace graphics 12 | { 13 | typedef uint16_t TilesetHandle; 14 | typedef uint16_t AnimationHandle; 15 | typedef uint16_t TextureAtlasHandle; 16 | 17 | struct Rect { 18 | uint16_t x; 19 | uint16_t y; 20 | uint16_t width; 21 | uint16_t height; 22 | }; 23 | 24 | struct TextureAtlas { 25 | int num; 26 | Rect *rects; 27 | render::TextureHandle texture; 28 | }; 29 | 30 | struct TileObjectPart { 31 | uint8_t x; 32 | uint8_t y; 33 | }; 34 | 35 | struct TileObject { 36 | uint16_t num_parts; 37 | uint16_t start_index; 38 | }; 39 | 40 | struct Tileset { 41 | int num_objects; 42 | int num_tiles; 43 | TileObject *objects; 44 | TileObjectPart *parts; 45 | TextureAtlasHandle atlas; 46 | }; 47 | 48 | struct Pivot { 49 | uint16_t x; 50 | uint16_t y; 51 | }; 52 | 53 | struct Animation { 54 | int num_frames; 55 | Pivot *frame_pivots; 56 | TextureAtlasHandle atlas; 57 | }; 58 | 59 | constexpr TextureAtlasHandle k_atlas_invalid = 0; 60 | constexpr TilesetHandle k_tileset_invalid = 0; 61 | constexpr AnimationHandle k_animation_invalid = 0; 62 | 63 | using namespace memory::literals; 64 | class GraphicsManager : public core::EngineSystem 65 | { 66 | using AtlasAllocator = 67 | memory::ContainerAllocatorAdapter; 69 | using TilesetAllocator = 70 | memory::ContainerAllocatorAdapter; 71 | using AnimationAllocator = 72 | memory::ContainerAllocatorAdapter; 73 | 74 | public: 75 | constexpr static size_t k_minimal_arena_size = 10_mib; 76 | 77 | GraphicsManager(const memory::Arena &arena, size_t max_tilesets, 78 | size_t max_animations, size_t max_generic_image_sets); 79 | ~GraphicsManager(); 80 | 81 | int startUp(); 82 | 83 | TilesetHandle loadTileset(const char *data_file, 84 | render::TextureHandle texture); 85 | AnimationHandle loadAnimation(const char *data_file, 86 | render::TextureHandle texture); 87 | TextureAtlasHandle loadGenericImageSet(const char *data_file, 88 | render::TextureHandle texture); 89 | TextureAtlas *atlas(TextureAtlasHandle handle); 90 | Tileset *tileset(TilesetHandle handle); 91 | Animation *animation(AnimationHandle handle); 92 | 93 | private: 94 | memory::LinearAllocator m_allocator; 95 | memory::Arena m_loader_arena; 96 | size_t m_num_atlases; 97 | size_t m_num_tilesets; 98 | size_t m_num_animations; 99 | container::Array m_atlases; 100 | container::Array m_tilesets; 101 | container::Array m_animations; 102 | }; 103 | 104 | } // namespace graphics 105 | } // namespace castlekeep 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /src/loaders/image_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "image_loader.h" 2 | 3 | #include "memory/linear_allocator.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define PNG_USER_MEM_SUPPORTED 10 | 11 | static png_voidp allocate(png_structp png_ptr, png_alloc_size_t size) 12 | { 13 | auto *allocator = 14 | reinterpret_cast(png_get_mem_ptr(png_ptr)); 15 | return allocator->allocate_aligned(size, 8); 16 | } 17 | 18 | static void deallocate(png_structp png_ptr, png_voidp ptr) 19 | { 20 | return; 21 | } 22 | 23 | namespace castlekeep 24 | { 25 | namespace loader 26 | { 27 | int loadImage(std::byte image[], size_t buffer_size, int &width, int &height, 28 | int &channels, const char *file, const memory::Arena &arena, 29 | int max_width, int max_height) 30 | { 31 | memory::LinearAllocator allocator(arena); 32 | auto marker_guard = allocator.markGuarded(); 33 | png_byte buffer[10]; 34 | std::FILE *fp = fopen(file, "rb"); 35 | if (fp == nullptr) { 36 | return -1; 37 | } 38 | size_t offset = std::fread(buffer, 1, 8, fp); 39 | if (png_sig_cmp(buffer, 0, 8) != 0) { 40 | return -1; 41 | } 42 | png_structp png_ptr = 43 | png_create_read_struct_2(PNG_LIBPNG_VER_STRING, nullptr, nullptr, 44 | nullptr, &allocator, allocate, deallocate); 45 | if (!png_ptr) { 46 | return -1; 47 | } 48 | 49 | png_infop info_ptr = png_create_info_struct(png_ptr); 50 | if (!info_ptr) { 51 | png_destroy_read_struct(&png_ptr, nullptr, nullptr); 52 | return -1; 53 | } 54 | 55 | png_infop end_info = png_create_info_struct(png_ptr); 56 | if (!end_info) { 57 | png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); 58 | return -1; 59 | } 60 | 61 | if (setjmp(png_jmpbuf(png_ptr))) { 62 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 63 | fclose(fp); 64 | return -1; 65 | } 66 | png_init_io(png_ptr, fp); 67 | 68 | png_set_sig_bytes(png_ptr, static_cast(offset)); 69 | 70 | png_read_info(png_ptr, info_ptr); 71 | 72 | width = static_cast(png_get_image_width(png_ptr, info_ptr)); 73 | height = static_cast(png_get_image_height(png_ptr, info_ptr)); 74 | // png_byte color_type = png_get_color_type(png_ptr, info_ptr); 75 | channels = static_cast(png_get_channels(png_ptr, info_ptr)); 76 | png_byte interlace_type = png_get_interlace_type(png_ptr, info_ptr); 77 | 78 | int pitch = width * channels; 79 | size_t size = static_cast(pitch * height); 80 | 81 | if (width > max_width || height > max_height || size > buffer_size || 82 | interlace_type != PNG_INTERLACE_NONE) { 83 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 84 | fclose(fp); 85 | return -1; 86 | } 87 | 88 | png_byte *row_pointer = reinterpret_cast(image); 89 | for (int i = 0; i < height; i++) { 90 | png_read_row(png_ptr, row_pointer, nullptr); 91 | row_pointer += pitch; 92 | } 93 | 94 | png_read_end(png_ptr, info_ptr); 95 | 96 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 97 | 98 | fclose(fp); 99 | 100 | return 0; 101 | } 102 | } // namespace loader 103 | } // namespace castlekeep 104 | -------------------------------------------------------------------------------- /src/utils/memory/allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef ALLOCATOR_H 2 | #define ALLOCATOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace memory 9 | { 10 | namespace literals 11 | { 12 | constexpr size_t operator"" _kib(unsigned long long int size) 13 | { 14 | return static_cast(size) * 1024; 15 | } 16 | 17 | constexpr size_t operator"" _mib(unsigned long long int size) 18 | { 19 | return static_cast(size) * 1024 * 1024; 20 | } 21 | 22 | constexpr size_t operator"" _gib(unsigned long long int size) 23 | { 24 | return static_cast(size) * 1024 * 1024 * 1024; 25 | } 26 | } // namespace literals 27 | 28 | struct Arena { 29 | template 30 | Arena(Allocator &allocator, size_t size) 31 | : size(size), memory(allocator.allocate(size)) 32 | { 33 | } 34 | Arena(size_t s, void *mem) : size(s), memory(mem) {} 35 | size_t size; 36 | void *memory; 37 | }; 38 | 39 | class HeapRegion 40 | { 41 | public: 42 | HeapRegion(size_t size) : m_size(size) { m_memory = new std::byte[size]; } 43 | HeapRegion(const HeapRegion &other) = delete; 44 | HeapRegion(HeapRegion &&other) noexcept 45 | : m_size(other.m_size), m_memory(other.m_memory) 46 | { 47 | other.m_memory = nullptr; 48 | } 49 | ~HeapRegion() { delete[] m_memory; } 50 | size_t size() const { return m_size; } 51 | 52 | void *start() { return m_memory; } 53 | void *end() { return m_memory + m_size; } 54 | 55 | Arena memory() { return Arena{m_size, m_memory}; } 56 | 57 | private: 58 | size_t m_size; 59 | std::byte *m_memory; 60 | }; 61 | 62 | template 63 | T *createObject(Allocator &allocator, Args &&... args) 64 | { 65 | return new (allocator.allocate_aligned(sizeof(T), alignof(T))) 66 | T(std::forward(args)...); 67 | } 68 | 69 | template 70 | void deleteObject(T *object, Allocator &allocator) 71 | { 72 | object->~T(); 73 | allocator.deallocate(object); 74 | } 75 | 76 | template 77 | class Deleter 78 | { 79 | public: 80 | Deleter(Allocator &allocator) : m_allocator(&allocator) {} 81 | Deleter(const Deleter &deleter) : m_allocator(deleter.m_allocator) {} 82 | void operator()(T *object) 83 | { 84 | if (object != nullptr) { 85 | object->~T(); 86 | m_allocator->deallocate(object); 87 | } 88 | } 89 | 90 | private: 91 | Allocator *m_allocator; 92 | }; 93 | 94 | template 95 | using UniquePtr = std::unique_ptr>; 96 | 97 | template 98 | auto makeUnique(T *ptr, Allocator &allocator) 99 | { 100 | return UniquePtr(ptr, Deleter(allocator)); 101 | } 102 | 103 | template 104 | auto createUniquePtrObject(Allocator &allocator, Args &&... args) 105 | { 106 | T *ptr = createObject(allocator, std::forward(args)...); 107 | 108 | return UniquePtr(ptr, Deleter(allocator)); 109 | } 110 | 111 | template 112 | class ContainerAllocatorAdapter 113 | { 114 | public: 115 | using value_type = T; 116 | ContainerAllocatorAdapter(Allocator &a) : m_allocator(a) {} 117 | T *allocate(size_t size) 118 | { 119 | return reinterpret_cast( 120 | m_allocator.allocate_aligned(sizeof(T) * size, alignof(T))); 121 | } 122 | void deallocate(T *p, std::size_t) { return m_allocator.deallocate(p); } 123 | 124 | private: 125 | Allocator &m_allocator; 126 | }; 127 | 128 | } // namespace memory 129 | #endif 130 | -------------------------------------------------------------------------------- /src/game/world.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_H 2 | #define WORLD_H 3 | 4 | #include "assets.h" 5 | #include "container/array.h" 6 | #include "graphics_manager.h" 7 | #include "memory/linear_allocator.h" 8 | #include "system.h" 9 | 10 | namespace castlekeep 11 | { 12 | namespace game 13 | { 14 | class Engine; 15 | 16 | enum class TerrainType : uint8_t { 17 | grass = 0, 18 | dirt = 1, 19 | }; 20 | 21 | enum Type : uint8_t { 22 | k_terrain_type_grass = 0, 23 | k_terrain_type_dirt = 1, 24 | }; 25 | 26 | enum MacroSize : uint8_t { 27 | k_macro_size_1x1 = 0, 28 | k_macro_size_2x2 = 1, 29 | k_macro_size_3x3 = 2, 30 | k_macro_size_4x4 = 3, 31 | }; 32 | 33 | struct TerrainMacroCell { 34 | Type type; 35 | MacroSize macro_size; 36 | uint8_t part; 37 | uint8_t macro; 38 | }; 39 | 40 | struct MapCoord { 41 | int16_t x; 42 | int16_t y; 43 | }; 44 | 45 | constexpr MapCoord north(MapCoord pos) 46 | { 47 | pos.y -= 2; 48 | return pos; 49 | } 50 | constexpr MapCoord south(MapCoord pos) 51 | { 52 | pos.y += 2; 53 | return pos; 54 | } 55 | constexpr MapCoord east(MapCoord pos) 56 | { 57 | pos.x++; 58 | return pos; 59 | } 60 | constexpr MapCoord west(MapCoord pos) 61 | { 62 | pos.x--; 63 | return pos; 64 | } 65 | 66 | constexpr MapCoord northEast(MapCoord pos) 67 | { 68 | int even = pos.y & 1; 69 | pos.y--; 70 | pos.x += even; 71 | return pos; 72 | } 73 | constexpr MapCoord northWest(MapCoord pos) 74 | { 75 | int even = 1 - (pos.y & 1); 76 | pos.y--; 77 | pos.x -= even; 78 | return pos; 79 | } 80 | constexpr MapCoord southEast(MapCoord pos) 81 | { 82 | int even = (pos.y & 1); 83 | pos.y++; 84 | pos.x += even; 85 | return pos; 86 | } 87 | 88 | constexpr MapCoord southWest(MapCoord pos) 89 | { 90 | int even = 1 - (pos.y & 1); 91 | pos.y++; 92 | pos.x -= even; 93 | return pos; 94 | } 95 | 96 | constexpr bool insideMap(MapCoord pos) 97 | { 98 | return (pos.x >= 0) && (pos.y >= 0); 99 | } 100 | 101 | using namespace memory::literals; 102 | class World 103 | { 104 | friend class WorldRenderer; 105 | 106 | template 107 | using Allocator = 108 | memory::ContainerAllocatorAdapter; 109 | 110 | public: 111 | constexpr static size_t k_minimal_arena_size = 100_mib; 112 | World(const memory::Arena& arena, int terrain_size); 113 | ~World(); 114 | int loadAssets(); 115 | void generateTerrain(); 116 | int terrainSize() const; 117 | int numHorizontalTiles() const; 118 | int numVerticalTiles() const; 119 | 120 | private: 121 | size_t terrainIndex(MapCoord coord) const 122 | { 123 | return coord.y * m_terrain_size + coord.x; 124 | } 125 | 126 | void placeTerrain(MapCoord pos, int block_size, int macro, 127 | TerrainType type); 128 | void placeBuilding(MapCoord pos, int block_size, int tile_start, 129 | uint16_t tileset); 130 | void place(MapCoord pos, int size, uint16_t tiles_start, uint16_t tileset); 131 | 132 | memory::LinearAllocator m_allocator; 133 | int m_terrain_size; 134 | container::Array> m_terrain_heights; 135 | container::Array> m_terrain_macro_parts; 136 | container::Array> m_terrain_type; 137 | container::Array> m_terrain_macros; 138 | container::Array> m_building_tilesets; 139 | container::Array> m_buildings; 140 | container::Array> m_building_tiles; 141 | container::Array> 143 | m_tilesets; 144 | container::Array> 146 | m_animations; 147 | }; 148 | } // namespace game 149 | } // namespace castlekeep 150 | #endif 151 | -------------------------------------------------------------------------------- /src/game/world_renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "world_renderer.h" 2 | #include "engine.h" 3 | #include "renderer/renderer.h" 4 | 5 | namespace castlekeep 6 | { 7 | namespace game 8 | { 9 | using namespace assets; 10 | WorldRenderer::WorldRenderer(const memory::Arena &arena, World *world) 11 | : m_allocator(arena), m_world(world) 12 | { 13 | } 14 | 15 | WorldRenderer::~WorldRenderer() {} 16 | 17 | int WorldRenderer::startUp() 18 | { 19 | return 0; 20 | } 21 | 22 | void WorldRenderer::renderTile(render::DrawCommandBuffer &draw_commands, 23 | MapCoord pos, const Camera &c, int layer, 24 | const graphics::Rect &rect) 25 | { 26 | int offset_x = k_tile_width / 2 + c.x(); 27 | int offset_y = k_tile_height / 2 + c.y(); 28 | int shift = (pos.y & 0x1) * k_tile_width / 2; 29 | draw_commands.commands[draw_commands.length].src_x = rect.x; 30 | draw_commands.commands[draw_commands.length].src_y = rect.y; 31 | draw_commands.commands[draw_commands.length].dst_x = 32 | pos.x * k_tile_width + shift - offset_x; 33 | draw_commands.commands[draw_commands.length].dst_y = 34 | pos.y * k_tile_height / 2 - (rect.height - k_tile_height) - offset_y; 35 | draw_commands.commands[draw_commands.length].width = rect.width; 36 | draw_commands.commands[draw_commands.length].height = rect.height; 37 | draw_commands.commands[draw_commands.length].depth = 38 | pos.y * m_world->m_terrain_size * 32 + layer + pos.x; 39 | draw_commands.length++; 40 | } 41 | 42 | int WorldRenderer::renderWorld(const Camera &camera) 43 | { 44 | auto marker = m_allocator.markGuarded(); 45 | int terrain_size = m_world->m_terrain_size; 46 | render::DrawCommandBuffer draw_commands; 47 | draw_commands.commands = 48 | reinterpret_cast(m_allocator.allocate_aligned( 49 | sizeof(render::DrawCommand) * terrain_size * terrain_size, 50 | alignof(render::DrawCommand))); 51 | graphics::TilesetHandle terrain_tileset_handle = 52 | m_world->m_tilesets[mapAssetToIndex(TilesetID::tile_land_macros)]; 53 | 54 | graphics::Tileset *terrain_tileset = 55 | core::Engine::g_engine->graphicsManager()->tileset( 56 | terrain_tileset_handle); 57 | 58 | graphics::TextureAtlas *terrain_atlas = 59 | core::Engine::g_engine->graphicsManager()->atlas( 60 | terrain_tileset->atlas); 61 | 62 | draw_commands.length = 0; 63 | draw_commands.texture_id = terrain_atlas->texture; 64 | 65 | graphics::TilesetHandle castle_tileset_handle = 66 | m_world->m_tilesets[mapAssetToIndex(TilesetID::tile_castle)]; 67 | graphics::Tileset *castle_tileset = 68 | core::Engine::g_engine->graphicsManager()->tileset( 69 | castle_tileset_handle); 70 | 71 | graphics::TextureAtlas *castle_atlas = 72 | core::Engine::g_engine->graphicsManager()->atlas(castle_tileset->atlas); 73 | 74 | render::DrawCommandBuffer draw_commands2; 75 | draw_commands2.commands = 76 | reinterpret_cast(m_allocator.allocate_aligned( 77 | sizeof(render::DrawCommand) * terrain_size * terrain_size, 78 | alignof(render::DrawCommand))); 79 | draw_commands2.length = 0; 80 | draw_commands2.texture_id = castle_atlas->texture; 81 | 82 | int startx = camera.x() / k_tile_width; 83 | int starty = (camera.y() / k_tile_height) * 2; 84 | int endx = 85 | startx + core::Engine::g_engine->renderer()->width() / k_tile_width + 2; 86 | int endy = 87 | starty + 88 | (core::Engine::g_engine->renderer()->height() / k_tile_height) * 2 + 4; 89 | 90 | for (int y = starty; y < endy; y++) { 91 | for (int x = startx; x < endx; x++) { 92 | MapCoord pos{static_cast(x), static_cast(y)}; 93 | size_t index = m_world->terrainIndex(pos); 94 | if (m_world->m_buildings[index] >= 0) { 95 | uint16_t tile = m_world->m_building_tiles[index]; 96 | renderTile(draw_commands2, pos, camera, 1, 97 | castle_atlas->rects[tile]); 98 | } else { 99 | uint16_t tile = 100 | m_world->m_terrain_macro_parts[index] + 101 | terrain_tileset 102 | ->objects[m_world->m_terrain_macros[index] + 140] 103 | .start_index; 104 | renderTile(draw_commands, pos, camera, 0, 105 | terrain_atlas->rects[tile]); 106 | } 107 | } 108 | } 109 | core::Engine::g_engine->renderer()->draw(draw_commands); 110 | core::Engine::g_engine->renderer()->draw(draw_commands2); 111 | return 0; 112 | } 113 | 114 | int WorldRenderer::worldWidth() const 115 | { 116 | return m_world->numHorizontalTiles() * k_tile_width; 117 | } 118 | 119 | int WorldRenderer::worldHeight() const 120 | { 121 | return m_world->numVerticalTiles() * k_tile_height / 2; 122 | } 123 | 124 | int WorldRenderer::renderTerrain() {} 125 | } // namespace game 126 | } // namespace castlekeep 127 | -------------------------------------------------------------------------------- /src/loaders/data_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "data_loader.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace castlekeep 7 | { 8 | namespace loader 9 | { 10 | DataLoader::DataLoader(const memory::Arena &arena, File &file) 11 | : m_file(file), m_allocator(arena) 12 | { 13 | m_line_buffer = reinterpret_cast(m_allocator.allocate(1024)); 14 | m_section_name = reinterpret_cast(m_allocator.allocate(256)); 15 | m_format = reinterpret_cast(m_allocator.allocate(32)); 16 | } 17 | 18 | DataLoader::~DataLoader() {} 19 | int DataLoader::begin(const char *type) 20 | { 21 | readLine(); 22 | if (m_line_buffer[0] != '!') { 23 | return -1; 24 | } 25 | if (type != nullptr) { 26 | char buf[32]; 27 | parseName(buf, 32, 1); 28 | if (std::strncmp(buf, type, 32) != 0) { 29 | return -1; 30 | } 31 | } 32 | return 0; 33 | } 34 | 35 | /*int DataLoader::open(const char *file, const char *type) 36 | { 37 | m_file = std::fopen(file, "r"); 38 | if (m_file == nullptr) { 39 | return -1; 40 | } 41 | readLine(); 42 | if (m_line_buffer[0] != '!') { 43 | return -1; 44 | } 45 | char buf[32]; 46 | parseName(buf, 32, 1); 47 | if (std::strncmp(buf, type, 32) != 0) { 48 | return -1; 49 | } 50 | return 0; 51 | }*/ 52 | 53 | /*int DataLoader::open(const char *file) 54 | { 55 | m_file = std::fopen(file, "r"); 56 | if (m_file == nullptr) { 57 | return -1; 58 | } 59 | readLine(); 60 | if (m_line_buffer[0] != '!') { 61 | return -1; 62 | } 63 | return 0; 64 | }*/ 65 | 66 | int DataLoader::readSectionInfo(const char **name, int &rows, int &columns) 67 | { 68 | if (readLine() != 0) { 69 | return -1; 70 | } 71 | int pos = 0; 72 | pos += skipWhitespace(pos); 73 | if (m_line_buffer[pos] != '[') { 74 | return -1; 75 | } 76 | pos++; 77 | pos += skipWhitespace(pos); 78 | int len = parseName(m_section_name, 256, 1); 79 | if (len == 0) { 80 | return -1; 81 | } 82 | pos += len; 83 | pos += skipWhitespace(pos); 84 | if (m_line_buffer[pos] != ',') { 85 | return -1; 86 | } 87 | pos++; 88 | pos += skipWhitespace(pos); 89 | int row_count = 0; 90 | len = parseInteger(row_count, pos); 91 | if (len == 0) { 92 | return -1; 93 | } 94 | pos += len; 95 | pos += skipWhitespace(pos); 96 | if (m_line_buffer[pos] != ',') { 97 | return -1; 98 | } 99 | pos++; 100 | pos += skipWhitespace(pos); 101 | int column_count = 0; 102 | len = parseInteger(column_count, pos); 103 | if (len == 0) { 104 | return -1; 105 | } 106 | pos += len; 107 | pos += skipWhitespace(pos); 108 | if (m_line_buffer[pos] != ',') { 109 | return -1; 110 | } 111 | pos++; 112 | pos += skipWhitespace(pos); 113 | for (int i = 0; i < column_count; i++) { 114 | char c = m_line_buffer[pos]; 115 | if (c == 0) { 116 | return -1; 117 | } 118 | if ((c != 'i') && (c != 's')) { 119 | return -1; 120 | } 121 | m_format[i] = c; 122 | pos++; 123 | pos += skipWhitespace(pos); 124 | c = m_line_buffer[pos]; 125 | if (i < (column_count - 1)) { 126 | if (c != ',') { 127 | return -1; 128 | } 129 | pos++; 130 | } 131 | pos += skipWhitespace(pos); 132 | } 133 | if (m_line_buffer[pos] != ']') { 134 | return -1; 135 | } 136 | 137 | *name = m_section_name; 138 | rows = row_count; 139 | columns = column_count; 140 | return 0; 141 | } 142 | 143 | int DataLoader::readRow(Value row[], int columns) 144 | { 145 | if (readLine() != 0) { 146 | return -1; 147 | } 148 | int pos = 0; 149 | for (int i = 0; i < columns; i++) { 150 | int len = 0; 151 | pos += skipWhitespace(pos); 152 | if (m_format[i] == 'i') { 153 | int val = 0; 154 | len = parseInteger(val, pos); 155 | if (len == 0) { 156 | return 0; 157 | } 158 | row[i].type = Type::INTEGER; 159 | row[i].number = val; 160 | } else { 161 | } 162 | pos += len; 163 | pos += skipWhitespace(pos); 164 | if (i < (columns - 1)) { 165 | if (m_line_buffer[pos] != ',') { 166 | return 0; 167 | } 168 | pos++; 169 | } 170 | } 171 | if ((m_line_buffer[pos] != '\n') && (m_line_buffer[pos] != 0)) { 172 | return 0; 173 | } 174 | return columns; 175 | } 176 | 177 | int DataLoader::skipRows(int rows) 178 | { 179 | for (int i = 0; i < rows; i++) { 180 | if (readLine() != 0) { 181 | return -1; 182 | } 183 | } 184 | return 0; 185 | } 186 | 187 | int DataLoader::readLine() 188 | { 189 | while (true) { 190 | if (m_file.readLine(m_line_buffer, 1024) == nullptr) { 191 | return -1; 192 | } 193 | if ((m_line_buffer[0] != '#') && (m_line_buffer[0] != '\n')) { 194 | break; 195 | } 196 | } 197 | return 0; 198 | } 199 | 200 | int DataLoader::skipWhitespace(int pos) 201 | { 202 | int len = 0; 203 | while ((m_line_buffer[pos] == ' ') || (m_line_buffer[pos] == '\t')) { 204 | pos++; 205 | len++; 206 | } 207 | return len; 208 | } 209 | 210 | int DataLoader::parseInteger(int &val, int pos) 211 | { 212 | char c; 213 | int len = 0; 214 | val = 0; 215 | int sign = 1; 216 | if (m_line_buffer[pos] == '-') { 217 | sign = -1; 218 | pos++; 219 | len++; 220 | } 221 | 222 | while ((c = m_line_buffer[pos]) != 0) { 223 | if (!((c >= '0') && (c <= '9'))) { 224 | break; 225 | } 226 | val *= 10; 227 | val += (c - '0'); 228 | pos++; 229 | len++; 230 | } 231 | val *= sign; 232 | return len; 233 | } 234 | 235 | int DataLoader::parseName(char *str, int size, int pos) 236 | { 237 | char c; 238 | int len = 0; 239 | while (((c = m_line_buffer[pos]) != 0) && len < size) { 240 | if (!(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')))) { 241 | break; 242 | } 243 | str[len] = c; 244 | pos++; 245 | len++; 246 | } 247 | str[len] = 0; 248 | return len; 249 | } 250 | 251 | } // namespace loader 252 | } // namespace castlekeep 253 | -------------------------------------------------------------------------------- /src/game/world.cpp: -------------------------------------------------------------------------------- 1 | #include "world.h" 2 | #include "engine.h" 3 | #include "loaders/image_loader.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace castlekeep 9 | { 10 | namespace game 11 | { 12 | template 13 | void setBlock(MapCoord pos, int block_size, Function f) 14 | { 15 | MapCoord line_start = pos; 16 | // lower half 17 | int i = 0; 18 | for (int row = 0; row < block_size; row++) { 19 | MapCoord line_pos = line_start; 20 | for (int column = 0; column < row + 1; column++) { 21 | if (insideMap(line_pos)) { 22 | f(line_pos, i); 23 | } 24 | line_pos = east(line_pos); 25 | i++; 26 | } 27 | line_start = northWest(line_start); 28 | } 29 | // upper half 30 | line_start = east(line_start); 31 | for (int row = 1; row < block_size; row++) { 32 | MapCoord line_pos = line_start; 33 | for (int column = 0; column < block_size - row; column++) { 34 | if (insideMap(line_pos)) { 35 | f(line_pos, i); 36 | } 37 | line_pos = east(line_pos); 38 | i++; 39 | } 40 | line_start = northEast(line_start); 41 | } 42 | } 43 | 44 | using namespace assets; 45 | World::World(const memory::Arena& arena, int terrain_size) 46 | : m_allocator(arena), 47 | m_terrain_size(terrain_size), 48 | m_terrain_heights(static_cast(m_terrain_size * m_terrain_size), 49 | static_cast(0), m_allocator), 50 | m_terrain_macro_parts( 51 | static_cast(m_terrain_size * m_terrain_size), 52 | static_cast(0), m_allocator), 53 | m_terrain_type(static_cast(m_terrain_size * m_terrain_size), 54 | static_cast(0), m_allocator), 55 | m_terrain_macros(static_cast(m_terrain_size * m_terrain_size), 56 | static_cast(0), m_allocator), 57 | m_building_tilesets(static_cast(m_terrain_size * m_terrain_size), 58 | static_cast(0), m_allocator), 59 | m_buildings(static_cast(m_terrain_size * m_terrain_size), 60 | static_cast(-1), m_allocator), 61 | m_building_tiles(static_cast(m_terrain_size * m_terrain_size), 62 | static_cast(0), m_allocator), 63 | m_tilesets(assets::k_num_tile_assets, m_allocator), 64 | m_animations(assets::k_num_animation_assets, m_allocator) 65 | { 66 | } 67 | World::~World() {} 68 | 69 | void World::placeTerrain(MapCoord pos, int block_size, int macro, 70 | TerrainType type) 71 | { 72 | setBlock(pos, block_size, [&](MapCoord pos, int i) { 73 | size_t index = terrainIndex(pos); 74 | m_terrain_macro_parts[index] = i; 75 | m_terrain_type[index] = type; 76 | m_terrain_macros[index] = macro; 77 | }); 78 | } 79 | 80 | void World::placeBuilding(MapCoord pos, int block_size, int tile_start, 81 | uint16_t tileset) 82 | { 83 | setBlock(pos, block_size, [&](MapCoord pos, int i) { 84 | size_t index = terrainIndex(pos); 85 | m_building_tiles[index] = tile_start + i; 86 | m_buildings[index] = 0; 87 | m_building_tilesets[index] = tileset; 88 | }); 89 | } 90 | 91 | void World::generateTerrain() 92 | { 93 | std::random_device random_device; 94 | std::mt19937 random_generator(random_device()); 95 | std::uniform_int_distribution macro_distribution(0, 3); 96 | 97 | for (int y = 0; y < m_terrain_size; y++) { 98 | for (int x = 0; x < m_terrain_size; x++) { 99 | m_terrain_macro_parts[y * m_terrain_size + x] = -1; 100 | } 101 | } 102 | int block_size = 4; 103 | int blocks = (m_terrain_size / block_size) + 1; 104 | 105 | for (int block_y = 0; block_y < blocks; block_y++) { 106 | for (int block_x = 0; block_x < blocks; block_x++) { 107 | int macro = macro_distribution(random_generator); 108 | auto shift = (block_y & 0x1) * block_size / 2; 109 | MapCoord pos; 110 | pos.y = block_y * block_size + block_size - 1; 111 | pos.x = block_x * block_size + shift - 1; 112 | placeTerrain(pos, block_size, macro, TerrainType::grass); 113 | } 114 | } 115 | graphics::TilesetHandle tileset_handle = 116 | m_tilesets[mapAssetToIndex(TilesetID::tile_castle)]; 117 | graphics::Tileset* tileset = 118 | core::Engine::g_engine->graphicsManager()->tileset(tileset_handle); 119 | uint16_t tile_start = tileset->objects[31].start_index; 120 | uint16_t size = 11; 121 | MapCoord pos{50, 100}; 122 | placeBuilding(pos, size, tile_start, tileset_handle); 123 | } 124 | 125 | int World::terrainSize() const 126 | { 127 | return m_terrain_size; 128 | } 129 | 130 | int World::numHorizontalTiles() const 131 | { 132 | return m_terrain_size; 133 | } 134 | 135 | int World::numVerticalTiles() const 136 | { 137 | return m_terrain_size; 138 | } 139 | 140 | int World::loadAssets() 141 | { 142 | auto marker = m_allocator.markGuarded(); 143 | size_t buffer_size = loader::k_image_default_max_width * 144 | loader::k_image_default_max_height * 4; 145 | std::byte* image_buffer = 146 | reinterpret_cast(m_allocator.allocate(buffer_size)); 147 | 148 | auto image_loader_arena = memory::Arena(m_allocator, 1_mib); 149 | 150 | int width = 0; 151 | int height = 0; 152 | int channels = 0; 153 | for (int i = 0; i < m_tilesets.size(); i++) { 154 | if (loader::loadImage(image_buffer, buffer_size, width, height, 155 | channels, assets::tile_assets[i].image, 156 | image_loader_arena) != 0) { 157 | return -1; 158 | } 159 | 160 | render::TextureHandle texture = 161 | core::Engine::g_engine->renderer()->createTexture( 162 | width, height, render::PixelFormat::argb8888, image_buffer); 163 | if (texture == render::k_texture_invalid) { 164 | return -1; 165 | } 166 | graphics::TilesetHandle tileset = 167 | core::Engine::g_engine->graphicsManager()->loadTileset( 168 | assets::tile_assets[i].data, texture); 169 | if (tileset == graphics::k_tileset_invalid) { 170 | return -1; 171 | } 172 | m_tilesets[i] = tileset; 173 | } 174 | 175 | /*for (int i = 0; i < m_animations.size(); i++) { 176 | if (loader::loadImage(image_buffer, buffer_size, width, height, 177 | channels, assets::animation_assets[i].image, 178 | image_loader_arena) != 0) { 179 | return -1; 180 | } 181 | 182 | render::TextureHandle texture = g_engine->renderer()->createTexture( 183 | width, height, render::PixelFormat::argb8888, image_buffer); 184 | if (texture == render::k_texture_invalid) { 185 | return -1; 186 | } 187 | graphics::AnimationHandle animation = 188 | g_engine->graphicsManager()->loadAnimation( 189 | assets::animation_assets[i].data, texture); 190 | if (animation == graphics::k_tileset_invalid) { 191 | return -1; 192 | } 193 | m_animations[i] = animation; 194 | } 195 | */ 196 | return 0; 197 | } 198 | } // namespace game 199 | } // namespace castlekeep 200 | -------------------------------------------------------------------------------- /src/utils/memory/freelist_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include "freelist_allocator.h" 2 | #include "debug.h" 3 | #include "utils.h" 4 | 5 | #include 6 | 7 | namespace memory 8 | { 9 | FreeListAllocator::FreeListAllocator(void *memory, size_t size) 10 | : m_memory(reinterpret_cast(memory)), m_size(size) 11 | { 12 | m_free_list = reinterpret_cast(m_memory); 13 | 14 | m_free_list->next = nullptr; 15 | m_free_list->size = static_cast(m_size); 16 | m_free_list->is_free = 1; 17 | } 18 | 19 | FreeListAllocator::FreeListAllocator(const Arena ®ion) 20 | : FreeListAllocator(region.memory, region.size) 21 | { 22 | } 23 | 24 | FreeListAllocator::~FreeListAllocator() {} 25 | 26 | void *FreeListAllocator::allocate(size_t size) 27 | { 28 | return allocate_aligned(size, 8); 29 | } 30 | 31 | void *FreeListAllocator::allocate_aligned(size_t size, size_t data_alignment) 32 | { 33 | std::byte *memory = nullptr; 34 | ChunkHeader **prev_next = &m_free_list; 35 | ChunkHeader *prev = nullptr; 36 | ChunkHeader *chunk = m_free_list; 37 | 38 | size_t allocation_size = utils::roundUpToMultipleOfAlignment( 39 | size + sizeof(ChunkHeader) + sizeof(ChunkTrailer) + data_alignment, 40 | allocation_size_alignment); 41 | 42 | while (chunk != nullptr) { 43 | if (allocation_size <= chunk->size) { 44 | memory = bytePtr(chunk) + sizeof(ChunkHeader); 45 | size_t offset = utils::alignmentOffset(memory, data_alignment); 46 | memory += offset; 47 | *(memory - 1) = static_cast(offset); 48 | chunk->is_free = 0; 49 | ChunkHeader *next = nullptr; 50 | 51 | if ((chunk->size - allocation_size) >= 52 | (sizeof(ChunkHeader) + sizeof(ChunkTrailer) + 53 | minimal_split_size)) { 54 | next = header(bytePtr(chunk) + allocation_size); 55 | next->size = 56 | chunk->size - static_cast(allocation_size); 57 | next->next = chunk->next; 58 | next->is_free = 1; 59 | 60 | FreeChunkTrailer *next_trailer = freeTrailer( 61 | bytePtr(next) + next->size - sizeof(FreeChunkTrailer)); 62 | next_trailer->prev = prev; 63 | next_trailer->size = next->size; 64 | next_trailer->is_free = 1; 65 | 66 | chunk->size = static_cast(allocation_size); 67 | } else { 68 | next = chunk->next; 69 | } 70 | 71 | ChunkTrailer *chunk_trailer = 72 | trailer(memory + chunk->size - sizeof(ChunkHeader) - 73 | sizeof(ChunkTrailer)); 74 | chunk_trailer->size = chunk->size; 75 | chunk_trailer->is_free = 0; 76 | 77 | *prev_next = next; 78 | break; 79 | } 80 | prev_next = &chunk->next; 81 | prev = chunk; 82 | chunk = chunk->next; 83 | } 84 | DEBUG_ASSERT(memory != nullptr, "Not enough memory!"); 85 | 86 | return memory; 87 | } 88 | 89 | void FreeListAllocator::deallocate(void *address) 90 | { 91 | if (address != nullptr) { 92 | size_t offset = static_cast(*(bytePtr(address) - 1)); 93 | ChunkHeader *chunk_header = 94 | header(bytePtr(address) - offset - sizeof(ChunkHeader)); 95 | FreeChunkTrailer *chunk_trailer = 96 | freeTrailer(bytePtr(chunk_header) + chunk_header->size - 97 | sizeof(FreeChunkTrailer)); 98 | 99 | FreeChunkTrailer *left_trailer = 100 | freeTrailer(bytePtr(chunk_header) - sizeof(FreeChunkTrailer)); 101 | // merge 102 | if ((bytePtr(left_trailer) > m_memory) && (left_trailer->is_free)) { 103 | ChunkHeader *left = 104 | header(bytePtr(chunk_header) - left_trailer->size); 105 | left->size += chunk_header->size; 106 | if (left_trailer->prev != nullptr) { 107 | left_trailer->prev->next = left->next; 108 | } else { 109 | m_free_list = left->next; 110 | } 111 | chunk_header = left; 112 | } 113 | ChunkHeader *right = header(bytePtr(chunk_header) + chunk_header->size); 114 | FreeChunkTrailer *right_trailer = 115 | freeTrailer(bytePtr(right) + right->size); 116 | // merge 117 | if ((bytePtr(right) < (m_memory + m_size)) && right->is_free) { 118 | chunk_header->size += right->size; 119 | if (right_trailer->prev != nullptr) { 120 | right_trailer->prev->next = right->next; 121 | } else { 122 | m_free_list = right->next; 123 | } 124 | chunk_trailer = right_trailer; 125 | } 126 | 127 | chunk_trailer->prev = nullptr; 128 | chunk_trailer->size = chunk_header->size; 129 | chunk_trailer->is_free = 1; 130 | chunk_header->is_free = 1; 131 | chunk_header->next = m_free_list; 132 | m_free_list = chunk_header; 133 | } 134 | } 135 | /* 136 | void *FreeListAllocator::allocate_aligned_old(size_t size, 137 | size_t data_alignment) 138 | { 139 | std::byte *memory = nullptr; 140 | ChunkHeader **prev_next = &m_free_list; 141 | ChunkHeader *chunk = m_free_list; 142 | 143 | size_t allocation_size = utils::roundUpToMultipleOfAlignment( 144 | size + sizeof(ChunkHeader) + data_alignment, allocation_size_alignment); 145 | 146 | while (chunk != nullptr) { 147 | if (allocation_size <= chunk->size) { 148 | memory = reinterpret_cast(chunk) + sizeof(ChunkHeader); 149 | size_t offset = utils::alignmentOffset(memory, data_alignment); 150 | memory += offset; 151 | *(memory - 1) = static_cast(offset); 152 | 153 | ChunkHeader *next = nullptr; 154 | if (chunk->size - allocation_size >= 155 | sizeof(ChunkHeader) + minimal_split_size) { 156 | next = reinterpret_cast( 157 | reinterpret_cast(chunk) + allocation_size); 158 | next->size = 159 | chunk->size - static_cast(allocation_size); 160 | next->next = chunk->next; 161 | chunk->size = static_cast(allocation_size); 162 | } else { 163 | next = chunk->next; 164 | } 165 | *prev_next = next; 166 | break; 167 | } 168 | prev_next = &chunk->next; 169 | chunk = chunk->next; 170 | } 171 | 172 | return memory; 173 | }*/ 174 | /* 175 | void FreeListAllocator::deallocate_old(void *address) 176 | { 177 | if (address != nullptr) { 178 | std::byte *addr = reinterpret_cast(address); 179 | size_t offset = static_cast(*(addr - 1)); 180 | ChunkHeader *header = reinterpret_cast( 181 | addr - offset - sizeof(ChunkHeader)); 182 | 183 | ChunkHeader *chunk = m_free_list; 184 | ChunkHeader *prev = nullptr; 185 | ChunkHeader **prev_next = &m_free_list; 186 | 187 | // find position 188 | while ((chunk != nullptr) && !((header > prev) && (header < chunk))) { 189 | prev = chunk; 190 | prev_next = &chunk->next; 191 | chunk = chunk->next; 192 | } 193 | // insert 194 | *prev_next = header; 195 | header->next = chunk; 196 | 197 | // merge with neighbors if possible 198 | std::byte *left = reinterpret_cast(prev); 199 | std::byte *right = reinterpret_cast(chunk); 200 | std::byte *middle = reinterpret_cast(header); 201 | size_t size = header->size; 202 | if (left != nullptr) { 203 | if (left + prev->size == middle) { 204 | prev->next = chunk; 205 | prev->size += size; 206 | header = prev; 207 | } 208 | } 209 | if (right != nullptr) { 210 | if (middle + size == right) { 211 | header->next = chunk->next; 212 | header->size += chunk->size; 213 | } 214 | } 215 | } 216 | }*/ 217 | 218 | } // namespace memory 219 | -------------------------------------------------------------------------------- /src/utils/container/array.h: -------------------------------------------------------------------------------- 1 | #ifndef ARRAY_H 2 | #define ARRAY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace container 10 | { 11 | template > 12 | class Array 13 | { 14 | using traits = std::allocator_traits; 15 | 16 | public: 17 | using value_type = T; 18 | using reference = T&; 19 | using const_reference = const T&; 20 | using difference_type = ptrdiff_t; 21 | using size_type = size_t; 22 | 23 | using iterator = T*; 24 | using const_iterator = const T*; 25 | using reverse_iterator = std::reverse_iterator; 26 | using const_reverse_iterator = std::reverse_iterator; 27 | 28 | using allocator_type = Allocator; 29 | 30 | Array(size_t size, allocator_type allocator = allocator_type()) 31 | : m_size(size), m_allocator(allocator) 32 | { 33 | // m_data = allocator.allocate(size); 34 | m_data = traits::allocate(allocator, size); 35 | if constexpr (!(std::is_trivial::value && 36 | std::is_standard_layout::value)) { 37 | for (size_t i = 0; i < size; i++) { 38 | // m_allocator.construct(std::addressof(m_data[i])); 39 | traits::construct(m_allocator, std::addressof(m_data[i])); 40 | } 41 | } 42 | } 43 | Array(size_t size, const T& value, 44 | allocator_type allocator = allocator_type()) 45 | : m_size(size), m_allocator(allocator) 46 | { 47 | // m_data = allocator.allocate(size); 48 | m_data = traits::allocate(allocator, size); 49 | for (size_t i = 0; i < size; i++) { 50 | // m_allocator.construct(std::addressof(m_data[i]), value); 51 | traits::construct(m_allocator, std::addressof(m_data[i]), value); 52 | } 53 | } 54 | Array(size_t size, const T data[], 55 | allocator_type allocator = allocator_type()) 56 | : m_size(size), m_allocator(allocator) 57 | { 58 | // m_data = allocator.allocate(size); 59 | m_data = traits::allocate(allocator, size); 60 | for (size_t i = 0; i < size; i++) { 61 | // m_allocator.construct(std::addressof(m_data[i]), data[i]); 62 | traits::construct(m_allocator, std::addressof(m_data[i]), data[i]); 63 | } 64 | } 65 | // Array(const Array& array, allocator = allocator_type()) : 66 | // m_size(array.m_size),m_allocator(allocator) 67 | Array(const Array& array) : m_size(array.m_size) 68 | { 69 | m_allocator = 70 | traits::select_on_container_copy_construction(array.m_allocator); 71 | // m_data = allocator.allocate(m_size); 72 | m_data = traits::allocate(m_allocator, m_size); 73 | if constexpr (!(std::is_trivial::value && 74 | std::is_standard_layout::value)) { 75 | for (size_t i = 0; i < m_size; i++) { 76 | // m_allocator.construct(std::addressof(m_data[i]), 77 | // array.m_data[i]); 78 | traits::construct(m_allocator, std::addressof(m_data[i]), 79 | array.m_data[i]); 80 | } 81 | } 82 | } 83 | Array(Array&& array) noexcept : m_size(array.m_size), m_data(array.m_data) 84 | { 85 | m_allocator = std::move(array.m_allocator); 86 | array.array.m_data = nullptr; 87 | } 88 | ~Array() 89 | { 90 | if constexpr (!(std::is_trivial::value && 91 | std::is_standard_layout::value)) { 92 | for (size_t i = 0; i < m_size; i++) { 93 | // m_allocator.destroy(std::addressof(m_data[i])); 94 | traits::destroy(m_allocator, std::addressof(m_data[i])); 95 | } 96 | } 97 | // m_allocator.deallocate(m_data, m_size); 98 | traits::deallocate(m_allocator, m_data, m_size); 99 | } 100 | 101 | size_t size() const { return m_size; } 102 | 103 | T* data() { return m_data; } 104 | const T* data() const { return m_data; } 105 | 106 | Array& operator=(const Array& array) 107 | { 108 | if (this != &array) { 109 | if (m_size != array.m_size) { 110 | if constexpr (!(std::is_trivial::value && 111 | std::is_standard_layout::value)) { 112 | for (size_t i = 0; i < m_size; i++) { 113 | // m_allocator.destroy(std::addressof(m_data[i])); 114 | traits::destroy(m_allocator, std::addressof(m_data[i])); 115 | } 116 | } 117 | // m_allocator.deallocate(m_data, m_size); 118 | traits::deallocate(m_allocator, m_data, m_size); 119 | 120 | m_size = array.m_size; 121 | if (traits::propagate_on_container_copy_assignment::value) { 122 | m_allocator = array.m_allocator; 123 | } 124 | // m_data = m_allocator.allocate(m_size); 125 | m_data = traits::allocate(m_allocator, m_size); 126 | for (size_t i = 0; i < m_size; i++) { 127 | // m_allocator.construct(std::addressof(m_data[i]), 128 | // array.m_data[i]); 129 | traits::construct(m_allocator, std::addressof(m_data[i]), 130 | array.m_data[i]); 131 | } 132 | } else { 133 | if (traits::propagate_on_container_copy_assignment::value) { 134 | m_allocator = array.m_allocator; 135 | } 136 | std::copy_n(array.m_data, m_size, m_data); 137 | } 138 | } 139 | return *this; 140 | } 141 | 142 | Array& operator=(Array&& array) 143 | { 144 | constexpr bool propagate = 145 | traits::propagate_on_container_move_assignment::value; 146 | if (propagate || m_allocator == array.m_allocator) { 147 | if constexpr (!(std::is_trivial::value && 148 | std::is_standard_layout::value)) { 149 | for (size_t i = 0; i < m_size; i++) { 150 | // m_allocator.destroy(std::addressof(m_data[i])); 151 | traits::destroy(m_allocator, std::addressof(m_data[i])); 152 | } 153 | } 154 | // m_allocator.deallocate(m_data, m_size); 155 | traits::deallocate(m_allocator, m_data, m_size); 156 | 157 | m_size = array.m_size; 158 | m_data = array.m_data; 159 | if (propagate) { 160 | m_allocator = std::move(array.m_allocator); 161 | } 162 | array.m_data = nullptr; 163 | } else { 164 | operator=(array); 165 | } 166 | return *this; 167 | } 168 | 169 | T& operator[](size_t index) { return m_data[index]; } 170 | 171 | const T& operator[](size_t index) const { return m_data[index]; } 172 | 173 | iterator begin() { return m_data; } 174 | iterator end() { return &m_data[m_size]; } 175 | const_iterator begin() const { return m_data; } 176 | const_iterator end() const { return &m_data[m_size]; } 177 | const_iterator cbegin() const { return m_data; } 178 | const_iterator cend() const { return &m_data[m_size]; } 179 | 180 | reverse_iterator rbegin() 181 | { 182 | return std::reverse_iterator(&m_data[m_size]); 183 | } 184 | 185 | reverse_iterator rend() { return std::reverse_iterator(m_data); } 186 | 187 | const_reverse_iterator rbegin() const 188 | { 189 | return std::reverse_iterator(&m_data[m_size]); 190 | } 191 | 192 | const_reverse_iterator rend() const 193 | { 194 | return std::reverse_iterator(m_data); 195 | } 196 | 197 | const_reverse_iterator crbegin() const 198 | { 199 | return std::reverse_iterator(&m_data[m_size]); 200 | } 201 | 202 | const_reverse_iterator crend() const 203 | { 204 | return std::reverse_iterator(m_data); 205 | } 206 | 207 | allocator_type get_allocator() const { return m_allocator; } 208 | 209 | private: 210 | size_t m_size; 211 | T* m_data; 212 | allocator_type m_allocator; 213 | }; 214 | }; // namespace container 215 | #endif 216 | -------------------------------------------------------------------------------- /src/renderer/gl_renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "gl_renderer.h" 2 | 3 | #include 4 | #include 5 | #include "utils/debug.h" 6 | 7 | namespace castlekeep 8 | { 9 | namespace render 10 | { 11 | struct Vertex { 12 | int16_t x; 13 | int16_t y; 14 | uint32_t z; 15 | uint16_t u; 16 | uint16_t v; 17 | }; 18 | 19 | static const char *vertex_shader_source = 20 | R"( 21 | #version 330 core 22 | uniform float width; 23 | uniform float height; 24 | 25 | layout (location = 0) in vec2 pos; 26 | layout (location = 1) in float depth; 27 | layout (location = 2) in vec2 tex_pos_in; 28 | out vec2 tex_pos; 29 | void main() 30 | { 31 | gl_Position = vec4((pos.x/width)*2-1, (pos.y/height)*-2+1,(depth/16777216)*2-1, 1.0); 32 | //gl_Position = vec4((pos.x/width)*2-1, (pos.y/height)*-2+1,(depth/65536)*2-1, 1.0); 33 | tex_pos=tex_pos_in; 34 | } 35 | )"; 36 | static const char *fragment_shader_source = 37 | R"( 38 | #version 330 core 39 | layout (location = 0) out vec4 output_color; 40 | in vec2 tex_pos; 41 | uniform sampler2DRect tex; 42 | void main() 43 | { 44 | 45 | vec4 texel =texture(tex,tex_pos); 46 | if(texel.a<0.5) { 47 | discard; 48 | } 49 | output_color= texel; 50 | } 51 | )"; 52 | 53 | GLRenderSystem::GLRenderSystem(const memory::Arena &arena, SDL_Window *window, 54 | size_t max_textures) 55 | : m_window(window), 56 | m_gl_context(nullptr), 57 | m_allocator(arena), 58 | m_num_textures(0), 59 | m_textures(max_textures, m_allocator), 60 | m_vao(0) 61 | { 62 | SDL_GL_GetDrawableSize(m_window, &m_width, &m_height); 63 | } 64 | 65 | GLRenderSystem::~GLRenderSystem() 66 | { 67 | SDL_GL_DeleteContext(m_gl_context); 68 | } 69 | 70 | TextureHandle GLRenderSystem::createTexture(int width, int height, 71 | PixelFormat format, 72 | const std::byte *data) 73 | { 74 | GLuint texture; 75 | glGenTextures(1, &texture); 76 | glBindTexture(GL_TEXTURE_RECTANGLE, texture); 77 | glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 78 | glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 79 | glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA8, width, height, 0, GL_RGBA, 80 | GL_UNSIGNED_BYTE, data); 81 | m_textures[m_num_textures] = texture; 82 | return static_cast(++m_num_textures); 83 | } 84 | 85 | int GLRenderSystem::startUp() 86 | { 87 | char compiler_log[1024]; 88 | 89 | m_gl_context = SDL_GL_CreateContext(m_window); 90 | if (!m_gl_context) { 91 | DEBUG_ERROR("could not create opengl context"); 92 | return -1; 93 | } 94 | GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); 95 | glShaderSource(vertex_shader, 1, &vertex_shader_source, nullptr); 96 | glCompileShader(vertex_shader); 97 | GLint status; 98 | glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &status); 99 | if (status != GL_TRUE) { 100 | GLsizei log_length; 101 | glGetShaderInfoLog(vertex_shader, sizeof(compiler_log), &log_length, 102 | compiler_log); 103 | fprintf(stderr, "%s", compiler_log); 104 | DEBUG_ERROR("could not compile vertex shader"); 105 | return -1; 106 | } 107 | GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); 108 | glShaderSource(fragment_shader, 1, &fragment_shader_source, nullptr); 109 | glCompileShader(fragment_shader); 110 | glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &status); 111 | if (status != GL_TRUE) { 112 | GLsizei log_length; 113 | glGetShaderInfoLog(fragment_shader, sizeof(compiler_log), &log_length, 114 | compiler_log); 115 | fprintf(stderr, "%s", compiler_log); 116 | DEBUG_ERROR("could not compile fragment shader"); 117 | return -1; 118 | } 119 | GLuint program = glCreateProgram(); 120 | glAttachShader(program, vertex_shader); 121 | glAttachShader(program, fragment_shader); 122 | glLinkProgram(program); 123 | glUseProgram(program); 124 | 125 | GLint width_location = glGetUniformLocation(program, "width"); 126 | if (width_location == -1) { 127 | DEBUG_ERROR("could not get uniform location"); 128 | return -1; 129 | } 130 | GLint height_location = glGetUniformLocation(program, "height"); 131 | if (height_location == -1) { 132 | DEBUG_ERROR("could not get uniform location"); 133 | return -1; 134 | } 135 | glUniform1f(width_location, m_width); 136 | glUniform1f(height_location, m_height); 137 | 138 | glGenVertexArrays(1, &m_vao); 139 | glBindVertexArray(m_vao); 140 | 141 | GLuint vbo; 142 | glGenBuffers(1, &vbo); 143 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 144 | glBufferData(GL_ARRAY_BUFFER, k_vbo_size, nullptr, GL_STREAM_DRAW); 145 | glVertexAttribPointer(0, 3, GL_SHORT, GL_FALSE, sizeof(Vertex), 146 | reinterpret_cast(0)); 147 | glEnableVertexAttribArray(0); 148 | glVertexAttribPointer(1, 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(Vertex), 149 | reinterpret_cast(4)); 150 | glEnableVertexAttribArray(1); 151 | glVertexAttribPointer(2, 2, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(Vertex), 152 | reinterpret_cast(8)); 153 | glEnableVertexAttribArray(2); 154 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 155 | 156 | glDepthFunc(GL_GEQUAL); 157 | glEnable(GL_DEPTH_TEST); 158 | glClearDepthf(0.0); 159 | 160 | return 0; 161 | } 162 | 163 | void GLRenderSystem::startFrame() 164 | { 165 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 166 | } 167 | 168 | static void drawCommand(Vertex *buf, int i, const DrawCommand &cmd) 169 | { 170 | // first triangle 171 | buf[i * 6 + 0].x = cmd.dst_x; 172 | buf[i * 6 + 0].y = cmd.dst_y; 173 | buf[i * 6 + 0].z = cmd.depth; 174 | buf[i * 6 + 0].u = cmd.src_x; 175 | buf[i * 6 + 0].v = cmd.src_y; 176 | 177 | buf[i * 6 + 1].x = cmd.dst_x + cmd.width; 178 | buf[i * 6 + 1].y = cmd.dst_y; 179 | buf[i * 6 + 1].z = cmd.depth; 180 | buf[i * 6 + 1].u = cmd.src_x + cmd.width; 181 | buf[i * 6 + 1].v = cmd.src_y; 182 | 183 | buf[i * 6 + 2].x = cmd.dst_x; 184 | buf[i * 6 + 2].y = cmd.dst_y + cmd.height; 185 | buf[i * 6 + 2].z = cmd.depth; 186 | buf[i * 6 + 2].u = cmd.src_x; 187 | buf[i * 6 + 2].v = cmd.src_y + cmd.height; 188 | 189 | // second triangle 190 | buf[i * 6 + 3].x = cmd.dst_x; 191 | buf[i * 6 + 3].y = cmd.dst_y + cmd.height; 192 | buf[i * 6 + 3].z = cmd.depth; 193 | buf[i * 6 + 3].u = cmd.src_x; 194 | buf[i * 6 + 3].v = cmd.src_y + cmd.height; 195 | 196 | buf[i * 6 + 4].x = cmd.dst_x + cmd.width; 197 | buf[i * 6 + 4].y = cmd.dst_y; 198 | buf[i * 6 + 4].z = cmd.depth; 199 | buf[i * 6 + 4].u = cmd.src_x + cmd.width; 200 | buf[i * 6 + 4].v = cmd.src_y; 201 | 202 | buf[i * 6 + 5].x = cmd.dst_x + cmd.width; 203 | buf[i * 6 + 5].y = cmd.dst_y + cmd.height; 204 | buf[i * 6 + 5].z = cmd.depth; 205 | buf[i * 6 + 5].u = cmd.src_x + cmd.width; 206 | buf[i * 6 + 5].v = cmd.src_y + cmd.height; 207 | } 208 | 209 | void GLRenderSystem::draw(const DrawCommandBuffer &buffer) 210 | { 211 | Vertex *buf = reinterpret_cast( 212 | glMapBufferRange(GL_ARRAY_BUFFER, 0, k_vbo_size, 213 | GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)); 214 | DEBUG_ASSERT(buffer.length * 6 * sizeof(Vertex) <= k_vbo_size, 215 | "vbo buffer to small"); 216 | for (int i = 0; i < buffer.length; i++) { 217 | auto &cmd = buffer.commands[i]; 218 | drawCommand(buf, i, cmd); 219 | } 220 | glUnmapBuffer(GL_ARRAY_BUFFER); 221 | glBindTexture(GL_TEXTURE_RECTANGLE, texture(buffer.texture_id)); 222 | glDrawArrays(GL_TRIANGLES, 0, buffer.length * 6); 223 | } 224 | 225 | void GLRenderSystem::endFrame() 226 | { 227 | SDL_GL_SwapWindow(m_window); 228 | } 229 | 230 | int GLRenderSystem::width() 231 | { 232 | return m_width; 233 | } 234 | 235 | int GLRenderSystem::height() 236 | { 237 | return m_height; 238 | } 239 | 240 | GLuint GLRenderSystem::texture(TextureHandle id) 241 | { 242 | return m_textures[id - 1]; 243 | } 244 | 245 | }; // namespace render 246 | } // namespace castlekeep 247 | -------------------------------------------------------------------------------- /src/graphics_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics_manager.h" 2 | #include "debug.h" 3 | #include "loaders/data_loader.h" 4 | #include "loaders/file.h" 5 | 6 | #include 7 | 8 | namespace castlekeep 9 | { 10 | namespace graphics 11 | { 12 | using namespace memory::literals; 13 | 14 | GraphicsManager::GraphicsManager(const memory::Arena &arena, 15 | size_t max_tilesets, size_t max_animations, 16 | size_t max_generic_image_sets) 17 | : m_allocator(arena), 18 | m_loader_arena(10_kib, m_allocator.allocate(10_kib)), 19 | m_num_atlases(0), 20 | m_num_tilesets(0), 21 | m_num_animations(0), 22 | m_atlases(max_tilesets + max_animations + max_generic_image_sets, 23 | m_allocator), 24 | m_tilesets(max_tilesets, m_allocator), 25 | m_animations(max_animations, m_allocator) 26 | { 27 | DEBUG_ASSERT(arena.size >= k_minimal_arena_size, "Arena too small!"); 28 | } 29 | 30 | GraphicsManager::~GraphicsManager() {} 31 | 32 | int GraphicsManager::startUp() 33 | { 34 | return 0; 35 | } 36 | 37 | TilesetHandle GraphicsManager::loadTileset(const char *data_file, 38 | render::TextureHandle texture) 39 | { 40 | DEBUG_ASSERT(m_num_tilesets < m_tilesets.size(), "Too much tilesets!"); 41 | DEBUG_ASSERT(m_num_atlases < m_atlases.size(), "Too much atlases!"); 42 | auto allocation_mark = m_allocator.mark(); 43 | loader::File file; 44 | if (file.open(data_file, "r") != 0) { 45 | DEBUG_ERROR("Can not open file!"); 46 | return k_tileset_invalid; 47 | } 48 | loader::DataLoader loader(m_loader_arena, file); 49 | if (loader.begin("tile") != 0) { 50 | DEBUG_ERROR("Not a tileset file!"); 51 | return k_tileset_invalid; 52 | } 53 | loader::Value values[10]; 54 | for (int section = 0; section < 3; section++) { 55 | const char *name; 56 | int rows; 57 | int columns; 58 | if (loader.readSectionInfo(&name, rows, columns) != 0) { 59 | DEBUG_ERROR("Could not read section info!"); 60 | return k_tileset_invalid; 61 | } 62 | if (std::strcmp(name, "images") == 0) { 63 | if (loader.skipRows(rows) != 0) { 64 | m_allocator.reset(allocation_mark); 65 | return k_tileset_invalid; 66 | } 67 | } else if (std::strcmp(name, "objects") == 0) { 68 | m_tilesets[m_num_tilesets].num_objects = rows; 69 | m_tilesets[m_num_tilesets].objects = 70 | reinterpret_cast(m_allocator.allocate_aligned( 71 | sizeof(TileObject) * rows, alignof(TileObject))); 72 | for (int row = 0; row < rows; row++) { 73 | if (loader.readRow(values, columns) != 2) { 74 | m_allocator.reset(allocation_mark); 75 | DEBUG_ERROR("Could not read row!"); 76 | return k_tileset_invalid; 77 | } 78 | m_tilesets[m_num_tilesets].objects[row].start_index = 79 | static_cast(values[0].number); 80 | m_tilesets[m_num_tilesets].objects[row].num_parts = 81 | static_cast(values[1].number); 82 | } 83 | } else if (std::strcmp(name, "tiles") == 0) { 84 | m_tilesets[m_num_tilesets].num_tiles = rows; 85 | m_atlases[m_num_atlases].num = rows; 86 | m_tilesets[m_num_tilesets].parts = 87 | reinterpret_cast(m_allocator.allocate_aligned( 88 | sizeof(TileObjectPart) * rows, alignof(TileObjectPart))); 89 | m_atlases[m_num_atlases].rects = 90 | reinterpret_cast(m_allocator.allocate_aligned( 91 | sizeof(Rect) * rows, alignof(Rect))); 92 | 93 | for (int row = 0; row < rows; row++) { 94 | if (loader.readRow(values, columns) != 6) { 95 | m_allocator.reset(allocation_mark); 96 | DEBUG_ERROR("Could not read row!"); 97 | return k_tileset_invalid; 98 | } 99 | m_tilesets[m_num_tilesets].parts[row].x = 100 | static_cast(values[0].number); 101 | m_tilesets[m_num_tilesets].parts[row].y = 102 | static_cast(values[1].number); 103 | 104 | m_atlases[m_num_atlases].rects[row].x = 105 | static_cast(values[2].number); 106 | m_atlases[m_num_atlases].rects[row].y = 107 | static_cast(values[3].number); 108 | m_atlases[m_num_atlases].rects[row].width = 109 | static_cast(values[4].number); 110 | m_atlases[m_num_atlases].rects[row].height = 111 | static_cast(values[5].number); 112 | } 113 | } else { 114 | m_allocator.reset(allocation_mark); 115 | DEBUG_ERROR("Unknown section name!"); 116 | return k_tileset_invalid; 117 | } 118 | } 119 | m_atlases[m_num_atlases].texture = texture; 120 | m_num_atlases++; 121 | 122 | m_tilesets[m_num_tilesets].atlas = 123 | static_cast(m_num_atlases); 124 | m_num_tilesets++; 125 | 126 | return static_cast(m_num_tilesets); 127 | } 128 | 129 | AnimationHandle GraphicsManager::loadAnimation(const char *data_file, 130 | render::TextureHandle texture) 131 | { 132 | DEBUG_ASSERT(m_num_animations < m_animations.size(), 133 | "Too much animations!"); 134 | DEBUG_ASSERT(m_num_atlases < m_atlases.size(), "Too much atlases!"); 135 | auto allocation_mark = m_allocator.mark(); 136 | loader::File file; 137 | if (file.open(data_file, "r") != 0) { 138 | DEBUG_ERROR("Can not open file!"); 139 | return k_animation_invalid; 140 | } 141 | loader::DataLoader loader(m_loader_arena, file); 142 | if (loader.begin("anim") != 0) { 143 | DEBUG_ERROR("Not a animation file!"); 144 | return k_animation_invalid; 145 | } 146 | loader::Value values[10]; 147 | for (int section = 0; section < 2; section++) { 148 | const char *name; 149 | int rows; 150 | int columns; 151 | if (loader.readSectionInfo(&name, rows, columns) != 0) { 152 | m_allocator.reset(allocation_mark); 153 | DEBUG_ERROR("Could not read section info!"); 154 | return k_animation_invalid; 155 | } 156 | if (std::strcmp(name, "images") == 0) { 157 | m_atlases[m_num_atlases].num = rows; 158 | m_atlases[m_num_atlases].rects = 159 | reinterpret_cast(m_allocator.allocate_aligned( 160 | sizeof(Rect) * rows, alignof(Rect))); 161 | for (int row = 0; row < rows; row++) { 162 | if (loader.readRow(values, columns) != 4) { 163 | m_allocator.reset(allocation_mark); 164 | return k_animation_invalid; 165 | } 166 | m_atlases[m_num_atlases].rects[row].x = 167 | static_cast(values[0].number); 168 | m_atlases[m_num_atlases].rects[row].y = 169 | static_cast(values[1].number); 170 | m_atlases[m_num_atlases].rects[row].width = 171 | static_cast(values[2].number); 172 | m_atlases[m_num_atlases].rects[row].height = 173 | static_cast(values[3].number); 174 | } 175 | } else if (std::strcmp(name, "animation") == 0) { 176 | m_animations[m_num_animations].num_frames = rows; 177 | m_animations[m_num_animations].frame_pivots = 178 | reinterpret_cast(m_allocator.allocate_aligned( 179 | sizeof(Pivot) * rows, alignof(Pivot))); 180 | for (int row = 0; row < rows; row++) { 181 | if (loader.readRow(values, columns) != 2) { 182 | DEBUG_ERROR("Could not read row!"); 183 | m_allocator.reset(allocation_mark); 184 | return k_animation_invalid; 185 | } 186 | m_animations[m_num_animations].frame_pivots[row].x = 187 | static_cast(values[0].number); 188 | m_animations[m_num_animations].frame_pivots[row].y = 189 | static_cast(values[1].number); 190 | } 191 | } else { 192 | m_allocator.reset(allocation_mark); 193 | DEBUG_ERROR("Unknown section name!"); 194 | return k_animation_invalid; 195 | } 196 | } 197 | m_atlases[m_num_atlases].texture = texture; 198 | m_num_atlases++; 199 | 200 | m_animations[m_num_animations].atlas = 201 | static_cast(m_num_atlases); 202 | m_num_animations++; 203 | 204 | return static_cast(m_num_animations); 205 | } 206 | TextureAtlasHandle GraphicsManager::loadGenericImageSet( 207 | const char *data_file, render::TextureHandle texture) 208 | { 209 | DEBUG_ASSERT(m_num_atlases < m_atlases.size(), "Too much atlases!"); 210 | auto allocation_mark = m_allocator.mark(); 211 | loader::File file; 212 | if (file.open(data_file, "r") != 0) { 213 | DEBUG_ERROR("Can not open file!"); 214 | return k_atlas_invalid; 215 | } 216 | loader::DataLoader loader(m_loader_arena, file); 217 | if (loader.begin(nullptr) != 0) { 218 | DEBUG_ERROR("Could not open file!"); 219 | return k_atlas_invalid; 220 | } 221 | loader::Value values[10]; 222 | const char *name; 223 | int rows; 224 | int columns; 225 | if (loader.readSectionInfo(&name, rows, columns) != 0) { 226 | m_allocator.reset(allocation_mark); 227 | DEBUG_ERROR("Could not read section info!"); 228 | return k_atlas_invalid; 229 | } 230 | if (std::strcmp(name, "images") == 0) { 231 | m_atlases[m_num_atlases].num = rows; 232 | m_atlases[m_num_atlases].rects = reinterpret_cast( 233 | m_allocator.allocate_aligned(sizeof(Rect) * rows, alignof(Rect))); 234 | for (int row = 0; row < rows; row++) { 235 | if (loader.readRow(values, columns) != 4) { 236 | m_allocator.reset(allocation_mark); 237 | return k_atlas_invalid; 238 | } 239 | m_atlases[m_num_atlases].rects[row].x = 240 | static_cast(values[0].number); 241 | m_atlases[m_num_atlases].rects[row].y = 242 | static_cast(values[1].number); 243 | m_atlases[m_num_atlases].rects[row].width = 244 | static_cast(values[2].number); 245 | m_atlases[m_num_atlases].rects[row].height = 246 | static_cast(values[3].number); 247 | } 248 | } else { 249 | m_allocator.reset(allocation_mark); 250 | DEBUG_ERROR("Unknown section name!"); 251 | return k_atlas_invalid; 252 | } 253 | m_atlases[m_num_atlases].texture = texture; 254 | m_num_atlases++; 255 | return static_cast(m_num_atlases); 256 | } 257 | 258 | TextureAtlas *GraphicsManager::atlas(TextureAtlasHandle handle) 259 | { 260 | return &m_atlases[static_cast(handle) - 1]; 261 | } 262 | 263 | Tileset *GraphicsManager::tileset(TilesetHandle handle) 264 | { 265 | return &m_tilesets[static_cast(handle) - 1]; 266 | } 267 | 268 | Animation *GraphicsManager::animation(AnimationHandle handle) 269 | { 270 | return &m_animations[static_cast(handle) - 1]; 271 | } 272 | } // namespace graphics 273 | } // namespace castlekeep 274 | -------------------------------------------------------------------------------- /src/platform/sdl_platform.cpp: -------------------------------------------------------------------------------- 1 | #include "sdl_platform.h" 2 | #include "memory/allocator.h" 3 | 4 | #include 5 | 6 | namespace castlekeep 7 | { 8 | namespace platform 9 | { 10 | SDLPlatform::SDLPlatform(const memory::Arena &arena) 11 | : m_allocator(arena), m_window(nullptr) 12 | { 13 | } 14 | 15 | SDLPlatform::~SDLPlatform() 16 | { 17 | SDL_DestroyWindow(m_window); 18 | SDL_Quit(); 19 | } 20 | 21 | int SDLPlatform::startUp() 22 | { 23 | if (SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 0) { 24 | fprintf(stderr, "initialization failed!\n"); 25 | fprintf(stderr, "description: %s", SDL_GetError()); 26 | SDL_Quit(); 27 | } 28 | return 0; 29 | } 30 | 31 | int SDLPlatform::createWindow(int width, int height, const char *name) 32 | { 33 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 34 | SDL_GL_CONTEXT_PROFILE_CORE); 35 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 36 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 37 | SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 38 | SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 39 | m_window = SDL_CreateWindow( 40 | name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 41 | SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP); 42 | 43 | if (m_window == nullptr) { 44 | fprintf(stderr, "window creation failed!\n"); 45 | SDL_Quit(); 46 | } 47 | return 0; 48 | } 49 | bool SDLPlatform::processEvents(input::InputState &state) 50 | { 51 | SDL_Event event; 52 | bool is_running = true; 53 | while (SDL_PollEvent(&event)) { 54 | switch (event.type) { 55 | case SDL_QUIT: 56 | is_running = false; 57 | break; 58 | case SDL_MOUSEBUTTONDOWN: 59 | if (event.button.button == SDL_BUTTON_LEFT) { 60 | state.mouse.left_button_down = true; 61 | } else if (event.button.button == SDL_BUTTON_RIGHT) { 62 | state.mouse.right_button_down = true; 63 | } else if (event.button.button == SDL_BUTTON_MIDDLE) { 64 | state.mouse.middle_button_down = true; 65 | } 66 | break; 67 | case SDL_MOUSEBUTTONUP: 68 | if (event.button.button == SDL_BUTTON_LEFT) { 69 | state.mouse.left_button_down = false; 70 | } else if (event.button.button == SDL_BUTTON_RIGHT) { 71 | state.mouse.right_button_down = false; 72 | } else if (event.button.button == SDL_BUTTON_MIDDLE) { 73 | state.mouse.middle_button_down = false; 74 | } 75 | break; 76 | case SDL_MOUSEWHEEL: 77 | break; 78 | case SDL_MOUSEMOTION: 79 | state.mouse.x = event.motion.x; 80 | state.mouse.y = event.motion.y; 81 | state.mouse.dx = event.motion.xrel; 82 | state.mouse.dy = event.motion.yrel; 83 | break; 84 | case SDL_KEYDOWN: 85 | state.keyboard.key_down = true; 86 | switch (event.key.keysym.sym) { 87 | case SDLK_0: 88 | state.keyboard.keys[input::k_key_0] = true; 89 | break; 90 | case SDLK_1: 91 | state.keyboard.keys[input::k_key_1] = true; 92 | break; 93 | case SDLK_2: 94 | state.keyboard.keys[input::k_key_2] = true; 95 | break; 96 | case SDLK_3: 97 | state.keyboard.keys[input::k_key_3] = true; 98 | break; 99 | case SDLK_4: 100 | state.keyboard.keys[input::k_key_4] = true; 101 | break; 102 | case SDLK_5: 103 | state.keyboard.keys[input::k_key_5] = true; 104 | break; 105 | case SDLK_6: 106 | state.keyboard.keys[input::k_key_6] = true; 107 | break; 108 | case SDLK_7: 109 | state.keyboard.keys[input::k_key_7] = true; 110 | break; 111 | case SDLK_8: 112 | state.keyboard.keys[input::k_key_8] = true; 113 | break; 114 | case SDLK_9: 115 | state.keyboard.keys[input::k_key_9] = true; 116 | break; 117 | case SDLK_q: 118 | state.keyboard.keys[input::k_key_q] = true; 119 | break; 120 | case SDLK_w: 121 | state.keyboard.keys[input::k_key_w] = true; 122 | break; 123 | case SDLK_e: 124 | state.keyboard.keys[input::k_key_e] = true; 125 | break; 126 | case SDLK_r: 127 | state.keyboard.keys[input::k_key_r] = true; 128 | break; 129 | case SDLK_t: 130 | state.keyboard.keys[input::k_key_t] = true; 131 | break; 132 | case SDLK_y: 133 | state.keyboard.keys[input::k_key_y] = true; 134 | break; 135 | case SDLK_u: 136 | state.keyboard.keys[input::k_key_u] = true; 137 | break; 138 | case SDLK_i: 139 | state.keyboard.keys[input::k_key_i] = true; 140 | break; 141 | case SDLK_o: 142 | state.keyboard.keys[input::k_key_o] = true; 143 | break; 144 | case SDLK_p: 145 | state.keyboard.keys[input::k_key_p] = true; 146 | break; 147 | case SDLK_a: 148 | state.keyboard.keys[input::k_key_a] = true; 149 | break; 150 | case SDLK_s: 151 | state.keyboard.keys[input::k_key_s] = true; 152 | break; 153 | case SDLK_d: 154 | state.keyboard.keys[input::k_key_d] = true; 155 | break; 156 | case SDLK_f: 157 | state.keyboard.keys[input::k_key_f] = true; 158 | break; 159 | case SDLK_g: 160 | state.keyboard.keys[input::k_key_g] = true; 161 | break; 162 | case SDLK_h: 163 | state.keyboard.keys[input::k_key_h] = true; 164 | break; 165 | case SDLK_j: 166 | state.keyboard.keys[input::k_key_j] = true; 167 | break; 168 | case SDLK_k: 169 | state.keyboard.keys[input::k_key_k] = true; 170 | break; 171 | case SDLK_l: 172 | state.keyboard.keys[input::k_key_l] = true; 173 | break; 174 | case SDLK_z: 175 | state.keyboard.keys[input::k_key_z] = true; 176 | break; 177 | case SDLK_x: 178 | state.keyboard.keys[input::k_key_x] = true; 179 | break; 180 | case SDLK_c: 181 | state.keyboard.keys[input::k_key_c] = true; 182 | break; 183 | case SDLK_v: 184 | state.keyboard.keys[input::k_key_v] = true; 185 | break; 186 | case SDLK_b: 187 | state.keyboard.keys[input::k_key_b] = true; 188 | break; 189 | case SDLK_n: 190 | state.keyboard.keys[input::k_key_n] = true; 191 | break; 192 | case SDLK_m: 193 | state.keyboard.keys[input::k_key_m] = true; 194 | break; 195 | case SDLK_SPACE: 196 | state.keyboard.keys[input::k_key_space] = true; 197 | break; 198 | case SDLK_RETURN: 199 | state.keyboard.keys[input::k_key_return] = true; 200 | break; 201 | case SDLK_BACKSPACE: 202 | state.keyboard.keys[input::k_key_backspace] = true; 203 | break; 204 | case SDLK_UP: 205 | state.keyboard.keys[input::k_key_arrow_up] = true; 206 | break; 207 | case SDLK_DOWN: 208 | state.keyboard.keys[input::k_key_arrow_down] = true; 209 | break; 210 | case SDLK_LEFT: 211 | state.keyboard.keys[input::k_key_arrow_left] = true; 212 | break; 213 | case SDLK_RIGHT: 214 | state.keyboard.keys[input::k_key_arrow_right] = true; 215 | break; 216 | default: 217 | state.keyboard.key_down = false; 218 | break; 219 | } 220 | break; 221 | case SDL_KEYUP: 222 | state.keyboard.key_down = false; 223 | switch (event.key.keysym.sym) { 224 | case SDLK_0: 225 | state.keyboard.keys[input::k_key_0] = false; 226 | break; 227 | case SDLK_1: 228 | state.keyboard.keys[input::k_key_1] = false; 229 | break; 230 | case SDLK_2: 231 | state.keyboard.keys[input::k_key_2] = false; 232 | break; 233 | case SDLK_3: 234 | state.keyboard.keys[input::k_key_3] = false; 235 | break; 236 | case SDLK_4: 237 | state.keyboard.keys[input::k_key_4] = false; 238 | break; 239 | case SDLK_5: 240 | state.keyboard.keys[input::k_key_5] = false; 241 | break; 242 | case SDLK_6: 243 | state.keyboard.keys[input::k_key_6] = false; 244 | break; 245 | case SDLK_7: 246 | state.keyboard.keys[input::k_key_7] = false; 247 | break; 248 | case SDLK_8: 249 | state.keyboard.keys[input::k_key_8] = false; 250 | break; 251 | case SDLK_9: 252 | state.keyboard.keys[input::k_key_9] = false; 253 | break; 254 | case SDLK_q: 255 | state.keyboard.keys[input::k_key_q] = false; 256 | break; 257 | case SDLK_w: 258 | state.keyboard.keys[input::k_key_w] = false; 259 | break; 260 | case SDLK_e: 261 | state.keyboard.keys[input::k_key_e] = false; 262 | break; 263 | case SDLK_r: 264 | state.keyboard.keys[input::k_key_r] = false; 265 | break; 266 | case SDLK_t: 267 | state.keyboard.keys[input::k_key_t] = false; 268 | break; 269 | case SDLK_y: 270 | state.keyboard.keys[input::k_key_y] = false; 271 | break; 272 | case SDLK_u: 273 | state.keyboard.keys[input::k_key_u] = false; 274 | break; 275 | case SDLK_i: 276 | state.keyboard.keys[input::k_key_i] = false; 277 | break; 278 | case SDLK_o: 279 | state.keyboard.keys[input::k_key_o] = false; 280 | break; 281 | case SDLK_p: 282 | state.keyboard.keys[input::k_key_p] = false; 283 | break; 284 | case SDLK_a: 285 | state.keyboard.keys[input::k_key_a] = false; 286 | break; 287 | case SDLK_s: 288 | state.keyboard.keys[input::k_key_s] = false; 289 | break; 290 | case SDLK_d: 291 | state.keyboard.keys[input::k_key_d] = false; 292 | break; 293 | case SDLK_f: 294 | state.keyboard.keys[input::k_key_f] = false; 295 | break; 296 | case SDLK_g: 297 | state.keyboard.keys[input::k_key_g] = false; 298 | break; 299 | case SDLK_h: 300 | state.keyboard.keys[input::k_key_h] = false; 301 | break; 302 | case SDLK_j: 303 | state.keyboard.keys[input::k_key_j] = false; 304 | break; 305 | case SDLK_k: 306 | state.keyboard.keys[input::k_key_k] = false; 307 | break; 308 | case SDLK_l: 309 | state.keyboard.keys[input::k_key_l] = false; 310 | break; 311 | case SDLK_z: 312 | state.keyboard.keys[input::k_key_z] = false; 313 | break; 314 | case SDLK_x: 315 | state.keyboard.keys[input::k_key_x] = false; 316 | break; 317 | case SDLK_c: 318 | state.keyboard.keys[input::k_key_c] = false; 319 | break; 320 | case SDLK_v: 321 | state.keyboard.keys[input::k_key_v] = false; 322 | break; 323 | case SDLK_b: 324 | state.keyboard.keys[input::k_key_b] = false; 325 | break; 326 | case SDLK_n: 327 | state.keyboard.keys[input::k_key_n] = false; 328 | break; 329 | case SDLK_m: 330 | state.keyboard.keys[input::k_key_m] = false; 331 | break; 332 | case SDLK_SPACE: 333 | state.keyboard.keys[input::k_key_space] = false; 334 | break; 335 | case SDLK_RETURN: 336 | state.keyboard.keys[input::k_key_return] = false; 337 | break; 338 | case SDLK_BACKSPACE: 339 | state.keyboard.keys[input::k_key_backspace] = false; 340 | break; 341 | case SDLK_UP: 342 | state.keyboard.keys[input::k_key_arrow_up] = false; 343 | break; 344 | case SDLK_DOWN: 345 | state.keyboard.keys[input::k_key_arrow_down] = false; 346 | break; 347 | case SDLK_LEFT: 348 | state.keyboard.keys[input::k_key_arrow_left] = false; 349 | break; 350 | case SDLK_RIGHT: 351 | state.keyboard.keys[input::k_key_arrow_right] = false; 352 | break; 353 | default: 354 | break; 355 | } 356 | break; 357 | default: 358 | break; 359 | } 360 | } 361 | return is_running; 362 | } 363 | 364 | WindowHandle SDLPlatform::window() 365 | { 366 | return {m_window}; 367 | } 368 | 369 | } // namespace platform 370 | } // namespace castlekeep 371 | -------------------------------------------------------------------------------- /src/assets.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSETS_H 2 | #define ASSETS_H 3 | 4 | #include "debug.h" 5 | #include "graphics_manager.h" 6 | 7 | #include 8 | 9 | namespace castlekeep 10 | { 11 | namespace assets 12 | { 13 | struct AssetName { 14 | const char* data; 15 | const char* image; 16 | }; 17 | 18 | constexpr static inline AssetName animation_assets[] = { 19 | {"data/graphics/anim_armourer.data", "data/graphics/anim_armourer.png"}, 20 | {"data/graphics/anim_baker.data", "data/graphics/anim_baker.png"}, 21 | {"data/graphics/anim_blacksmith.data", "data/graphics/anim_blacksmith.png"}, 22 | {"data/graphics/anim_boiled_oil.data", "data/graphics/anim_boiled_oil.png"}, 23 | {"data/graphics/anim_brewer.data", "data/graphics/anim_brewer.png"}, 24 | {"data/graphics/anim_chopping_block.data", 25 | "data/graphics/anim_chopping_block.png"}, 26 | {"data/graphics/anim_dungeon.data", "data/graphics/anim_dungeon.png"}, 27 | {"data/graphics/anim_farmer.data", "data/graphics/anim_farmer.png"}, 28 | {"data/graphics/anim_flags.data", "data/graphics/anim_flags.png"}, 29 | {"data/graphics/anim_flag_small.data", "data/graphics/anim_flag_small.png"}, 30 | {"data/graphics/anim_fletcher.data", "data/graphics/anim_fletcher.png"}, 31 | {"data/graphics/anim_healer.data", "data/graphics/anim_healer.png"}, 32 | {"data/graphics/anim_hunter.data", "data/graphics/anim_hunter.png"}, 33 | {"data/graphics/anim_inn.data", "data/graphics/anim_inn.png"}, 34 | {"data/graphics/anim_iron_miner.data", "data/graphics/anim_iron_miner.png"}, 35 | {"data/graphics/anim_market.data", "data/graphics/anim_market.png"}, 36 | {"data/graphics/anim_maypole.data", "data/graphics/anim_maypole.png"}, 37 | {"data/graphics/anim_pitch_dugout.data", 38 | "data/graphics/anim_pitch_dugout.png"}, 39 | {"data/graphics/anim_poleturner.data", "data/graphics/anim_poleturner.png"}, 40 | {"data/graphics/anim_quarry.data", "data/graphics/anim_quarry.png"}, 41 | {"data/graphics/anim_shields.data", "data/graphics/anim_shields.png"}, 42 | {"data/graphics/anim_stables.data", "data/graphics/anim_stables.png"}, 43 | {"data/graphics/anim_stake.data", "data/graphics/anim_stake.png"}, 44 | {"data/graphics/anim_tanner.data", "data/graphics/anim_tanner.png"}, 45 | {"data/graphics/anim_tunnels.data", "data/graphics/anim_tunnels.png"}, 46 | {"data/graphics/anim_windmill.data", "data/graphics/anim_windmill.png"}, 47 | {"data/graphics/anim_woodcutter.data", "data/graphics/anim_woodcutter.png"}, 48 | {"data/graphics/body_animal_burning_big.data", 49 | "data/graphics/body_animal_burning_big.png"}, 50 | {"data/graphics/body_animal_burning_small.data", 51 | "data/graphics/body_animal_burning_small.png"}, 52 | {"data/graphics/body_archer.data", "data/graphics/body_archer.png"}, 53 | {"data/graphics/body_armourer.data", "data/graphics/body_armourer.png"}, 54 | {"data/graphics/body_baker.data", "data/graphics/body_baker.png"}, 55 | {"data/graphics/body_ballista.data", "data/graphics/body_ballista.png"}, 56 | {"data/graphics/body_battering_ram.data", 57 | "data/graphics/body_battering_ram.png"}, 58 | {"data/graphics/body_bear.data", "data/graphics/body_bear.png"}, 59 | {"data/graphics/body_blacksmith.data", "data/graphics/body_blacksmith.png"}, 60 | {"data/graphics/body_boy.data", "data/graphics/body_boy.png"}, 61 | {"data/graphics/body_brewer.data", "data/graphics/body_brewer.png"}, 62 | {"data/graphics/body_catapult.data", "data/graphics/body_catapult.png"}, 63 | {"data/graphics/body_chicken_brown.data", 64 | "data/graphics/body_chicken_brown.png"}, 65 | {"data/graphics/body_chicken.data", "data/graphics/body_chicken.png"}, 66 | {"data/graphics/body_cow.data", "data/graphics/body_cow.png"}, 67 | {"data/graphics/body_crossbowman.data", 68 | "data/graphics/body_crossbowman.png"}, 69 | {"data/graphics/body_crow.data", "data/graphics/body_crow.png"}, 70 | {"data/graphics/body_deer.data", "data/graphics/body_deer.png"}, 71 | {"data/graphics/body_dog.data", "data/graphics/body_dog.png"}, 72 | {"data/graphics/body_drunkard.data", "data/graphics/body_drunkard.png"}, 73 | {"data/graphics/body_farmer.data", "data/graphics/body_farmer.png"}, 74 | {"data/graphics/body_fighting_monk.data", 75 | "data/graphics/body_fighting_monk.png"}, 76 | {"data/graphics/body_fire_eater.data", "data/graphics/body_fire_eater.png"}, 77 | {"data/graphics/body_fireman.data", "data/graphics/body_fireman.png"}, 78 | {"data/graphics/body_fletcher.data", "data/graphics/body_fletcher.png"}, 79 | {"data/graphics/body_ghost.data", "data/graphics/body_ghost.png"}, 80 | {"data/graphics/body_girl.data", "data/graphics/body_girl.png"}, 81 | {"data/graphics/body_healer.data", "data/graphics/body_healer.png"}, 82 | {"data/graphics/body_horse_trader.data", 83 | "data/graphics/body_horse_trader.png"}, 84 | {"data/graphics/body_hunter.data", "data/graphics/body_hunter.png"}, 85 | {"data/graphics/body_innkeeper.data", "data/graphics/body_innkeeper.png"}, 86 | {"data/graphics/body_iron_miner.data", "data/graphics/body_iron_miner.png"}, 87 | {"data/graphics/body_jester.data", "data/graphics/body_jester.png"}, 88 | {"data/graphics/body_juggler.data", "data/graphics/body_juggler.png"}, 89 | {"data/graphics/body_knight.data", "data/graphics/body_knight.png"}, 90 | {"data/graphics/body_knight_top.data", "data/graphics/body_knight_top.png"}, 91 | {"data/graphics/body_ladder_bearer.data", 92 | "data/graphics/body_ladder_bearer.png"}, 93 | {"data/graphics/body_lady.data", "data/graphics/body_lady.png"}, 94 | {"data/graphics/body_lord.data", "data/graphics/body_lord.png"}, 95 | {"data/graphics/body_maceman.data", "data/graphics/body_maceman.png"}, 96 | {"data/graphics/body_man_burning.data", 97 | "data/graphics/body_man_burning.png"}, 98 | {"data/graphics/body_mangonel.data", "data/graphics/body_mangonel.png"}, 99 | {"data/graphics/body_miller.data", "data/graphics/body_miller.png"}, 100 | {"data/graphics/body_missile_2.data", "data/graphics/body_missile_2.png"}, 101 | {"data/graphics/body_missile_cow.data", 102 | "data/graphics/body_missile_cow.png"}, 103 | {"data/graphics/body_missile.data", "data/graphics/body_missile.png"}, 104 | {"data/graphics/body_missile_fire.data", 105 | "data/graphics/body_missile_fire.png"}, 106 | {"data/graphics/body_mother.data", "data/graphics/body_mother.png"}, 107 | {"data/graphics/body_ox.data", "data/graphics/body_ox.png"}, 108 | {"data/graphics/body_peasant.data", "data/graphics/body_peasant.png"}, 109 | {"data/graphics/body_pikeman.data", "data/graphics/body_pikeman.png"}, 110 | {"data/graphics/body_pitch_worker.data", 111 | "data/graphics/body_pitch_worker.png"}, 112 | {"data/graphics/body_poleturner.data", "data/graphics/body_poleturner.png"}, 113 | {"data/graphics/body_priest.data", "data/graphics/body_priest.png"}, 114 | {"data/graphics/body_rabbit.data", "data/graphics/body_rabbit.png"}, 115 | {"data/graphics/body_seagull.data", "data/graphics/body_seagull.png"}, 116 | {"data/graphics/body_shield.data", "data/graphics/body_shield.png"}, 117 | {"data/graphics/body_siege_engineer.data", 118 | "data/graphics/body_siege_engineer.png"}, 119 | {"data/graphics/body_siege_tower.data", 120 | "data/graphics/body_siege_tower.png"}, 121 | {"data/graphics/body_spearman.data", "data/graphics/body_spearman.png"}, 122 | {"data/graphics/body_splash.data", "data/graphics/body_splash.png"}, 123 | {"data/graphics/body_stonemason.data", "data/graphics/body_stonemason.png"}, 124 | {"data/graphics/body_swordsman.data", "data/graphics/body_swordsman.png"}, 125 | {"data/graphics/body_tanner.data", "data/graphics/body_tanner.png"}, 126 | {"data/graphics/body_tent.data", "data/graphics/body_tent.png"}, 127 | {"data/graphics/body_trader.data", "data/graphics/body_trader.png"}, 128 | {"data/graphics/body_trebutchet.data", "data/graphics/body_trebutchet.png"}, 129 | {"data/graphics/body_tunnelor.data", "data/graphics/body_tunnelor.png"}, 130 | {"data/graphics/body_wolf.data", "data/graphics/body_wolf.png"}, 131 | {"data/graphics/body_woodcutter.data", "data/graphics/body_woodcutter.png"}, 132 | {"data/graphics/tree_apple.data", "data/graphics/tree_apple.png"}, 133 | {"data/graphics/tree_birch.data", "data/graphics/tree_birch.png"}, 134 | {"data/graphics/Tree_Chestnut.data", "data/graphics/Tree_Chestnut.png"}, 135 | {"data/graphics/tree_oak.data", "data/graphics/tree_oak.png"}, 136 | {"data/graphics/tree_pine.data", "data/graphics/tree_pine.png"}, 137 | {"data/graphics/tree_shrub1.data", "data/graphics/tree_shrub1.png"}, 138 | {"data/graphics/tree_shrub2.data", "data/graphics/tree_shrub2.png"}, 139 | 140 | }; 141 | 142 | constexpr static inline AssetName tile_assets[] = { 143 | {"data/graphics/killing_pits.data", "data/graphics/killing_pits.png"}, 144 | {"data/graphics/pitch_ditches.data", "data/graphics/pitch_ditches.png"}, 145 | {"data/graphics/tile_buildings1.data", "data/graphics/tile_buildings1.png"}, 146 | {"data/graphics/tile_buildings2.data", "data/graphics/tile_buildings2.png"}, 147 | {"data/graphics/tile_burnt.data", "data/graphics/tile_burnt.png"}, 148 | {"data/graphics/tile_castle.data", "data/graphics/tile_castle.png"}, 149 | {"data/graphics/tile_churches.data", "data/graphics/tile_churches.png"}, 150 | {"data/graphics/tile_data.data", "data/graphics/tile_data.png"}, 151 | {"data/graphics/tile_farmland.data", "data/graphics/tile_farmland.png"}, 152 | {"data/graphics/tile_flatties.data", "data/graphics/tile_flatties.png"}, 153 | {"data/graphics/tile_goods.data", "data/graphics/tile_goods.png"}, 154 | {"data/graphics/tile_land3.data", "data/graphics/tile_land3.png"}, 155 | {"data/graphics/tile_land8.data", "data/graphics/tile_land8.png"}, 156 | {"data/graphics/tile_land_and_stones.data", 157 | "data/graphics/tile_land_and_stones.png"}, 158 | {"data/graphics/tile_land_macros.data", 159 | "data/graphics/tile_land_macros.png"}, 160 | {"data/graphics/tile_rocks8.data", "data/graphics/tile_rocks8.png"}, 161 | {"data/graphics/tile_ruins.data", "data/graphics/tile_ruins.png"}, 162 | {"data/graphics/tile_sea8.data", "data/graphics/tile_sea8.png"}, 163 | {"data/graphics/tile_sea_new_01.data", "data/graphics/tile_sea_new_01.png"}, 164 | {"data/graphics/tile_workshops.data", "data/graphics/tile_workshops.png"}, 165 | }; 166 | 167 | constexpr static inline AssetName generic_image_assets[] = { 168 | {"data/graphics/anim_buildings2.data", "data/graphics/anim_buildings2.png"}, 169 | {"data/graphics/anim_campaign_map_flags.data", 170 | "data/graphics/anim_campaign_map_flags.png"}, 171 | {"data/graphics/anim_castle.data", "data/graphics/anim_castle.png"}, 172 | {"data/graphics/anim_dancing_bear.data", 173 | "data/graphics/anim_dancing_bear.png"}, 174 | {"data/graphics/anim_dog_cage.data", "data/graphics/anim_dog_cage.png"}, 175 | {"data/graphics/anim_drawbridge.data", "data/graphics/anim_drawbridge.png"}, 176 | {"data/graphics/anim_ducking_stool.data", 177 | "data/graphics/anim_ducking_stool.png"}, 178 | {"data/graphics/anim_gallows.data", "data/graphics/anim_gallows.png"}, 179 | {"data/graphics/anim_gibbet.data", "data/graphics/anim_gibbet.png"}, 180 | {"data/graphics/anim_goods.data", "data/graphics/anim_goods.png"}, 181 | {"data/graphics/anim_heads.data", "data/graphics/anim_heads.png"}, 182 | {"data/graphics/anim_killing_pits.data", 183 | "data/graphics/anim_killing_pits.png"}, 184 | {"data/graphics/anim_rack.data", "data/graphics/anim_rack.png"}, 185 | {"data/graphics/anim_stocks.data", "data/graphics/anim_stocks.png"}, 186 | {"data/graphics/anim_tunnelors_guild.data", 187 | "data/graphics/anim_tunnelors_guild.png"}, 188 | {"data/graphics/anim_whitecaps.data", "data/graphics/anim_whitecaps.png"}, 189 | {"data/graphics/army_units.data", "data/graphics/army_units.png"}, 190 | {"data/graphics/blast3.data", "data/graphics/blast3.png"}, 191 | {"data/graphics/body_brazier.data", "data/graphics/body_brazier.png"}, 192 | {"data/graphics/body_disease.data", "data/graphics/body_disease.png"}, 193 | {"data/graphics/body_fire2.data", "data/graphics/body_fire2.png"}, 194 | {"data/graphics/body_fire.data", "data/graphics/body_fire.png"}, 195 | {"data/graphics/body_gate.data", "data/graphics/body_gate.png"}, 196 | {"data/graphics/body_steam.data", "data/graphics/body_steam.png"}, 197 | {"data/graphics/cracks.data", "data/graphics/cracks.png"}, 198 | {"data/graphics/cursors.data", "data/graphics/cursors.png"}, 199 | {"data/graphics/enemy_faces.data", "data/graphics/enemy_faces.png"}, 200 | {"data/graphics/float_pop_circ.data", "data/graphics/float_pop_circ.png"}, 201 | {"data/graphics/floats.data", "data/graphics/floats.png"}, 202 | {"data/graphics/floats_new.data", "data/graphics/floats_new.png"}, 203 | {"data/graphics/font_slanted.data", "data/graphics/font_slanted.png"}, 204 | {"data/graphics/font_stronghold_aa.data", 205 | "data/graphics/font_stronghold_aa.png"}, 206 | {"data/graphics/font_stronghold.data", "data/graphics/font_stronghold.png"}, 207 | {"data/graphics/icons_front_end_builder.data", 208 | "data/graphics/icons_front_end_builder.png"}, 209 | {"data/graphics/icons_front_end_combat.data", 210 | "data/graphics/icons_front_end_combat.png"}, 211 | {"data/graphics/icons_front_end.data", "data/graphics/icons_front_end.png"}, 212 | {"data/graphics/icons_front_end_economics.data", 213 | "data/graphics/icons_front_end_economics.png"}, 214 | {"data/graphics/icons_placeholders.data", 215 | "data/graphics/icons_placeholders.png"}, 216 | {"data/graphics/interface_army.data", "data/graphics/interface_army.png"}, 217 | {"data/graphics/interface_buttons.data", 218 | "data/graphics/interface_buttons.png"}, 219 | {"data/graphics/interface_icons2.data", 220 | "data/graphics/interface_icons2.png"}, 221 | {"data/graphics/interface_icons3.data", 222 | "data/graphics/interface_icons3.png"}, 223 | {"data/graphics/interface_ruins.data", "data/graphics/interface_ruins.png"}, 224 | {"data/graphics/interface_slider_bar.data", 225 | "data/graphics/interface_slider_bar.png"}, 226 | {"data/graphics/mapedit_buttons.data", "data/graphics/mapedit_buttons.png"}, 227 | {"data/graphics/mini_cursors.data", "data/graphics/mini_cursors.png"}, 228 | {"data/graphics/oil_dropped.data", "data/graphics/oil_dropped.png"}, 229 | {"data/graphics/rock_chips.data", "data/graphics/rock_chips.png"}, 230 | {"data/graphics/scribe.data", "data/graphics/scribe.png"}, 231 | {"data/graphics/smoke-30x30.data", "data/graphics/smoke-30x30.png"}, 232 | {"data/graphics/tile_chevrons.data", "data/graphics/tile_chevrons.png"}, 233 | {"data/graphics/tile_cliffs.data", "data/graphics/tile_cliffs.png"}, 234 | {"data/graphics/tile_rocks_chevrons.data", 235 | "data/graphics/tile_rocks_chevrons.png"}, 236 | {"data/graphics/tile_walls.data", "data/graphics/tile_walls.png"}, 237 | }; 238 | 239 | enum class AnimationID { 240 | anim_armourer = 0, 241 | anim_baker = 1, 242 | anim_blacksmith = 2, 243 | anim_boiled_oil = 3, 244 | anim_brewer = 4, 245 | anim_chopping_block = 5, 246 | anim_dungeon = 6, 247 | anim_farmer = 7, 248 | anim_flags = 8, 249 | anim_flag_small = 9, 250 | anim_fletcher = 10, 251 | anim_healer = 11, 252 | anim_hunter = 12, 253 | anim_inn = 13, 254 | anim_iron_miner = 14, 255 | anim_market = 15, 256 | anim_maypole = 16, 257 | anim_pitch_dugout = 17, 258 | anim_poleturner = 18, 259 | anim_quarry = 19, 260 | anim_shields = 20, 261 | anim_stables = 21, 262 | anim_stake = 22, 263 | anim_tanner = 23, 264 | anim_tunnels = 24, 265 | anim_windmill = 25, 266 | anim_woodcutter = 26, 267 | body_animal_burning_big = 27, 268 | body_animal_burning_small = 28, 269 | body_archer = 29, 270 | body_armourer = 30, 271 | body_baker = 31, 272 | body_ballista = 32, 273 | body_battering_ram = 33, 274 | body_bear = 34, 275 | body_blacksmith = 35, 276 | body_boy = 36, 277 | body_brewer = 37, 278 | body_catapult = 38, 279 | body_chicken_brown = 39, 280 | body_chicken = 40, 281 | body_cow = 41, 282 | body_crossbowman = 42, 283 | body_crow = 43, 284 | body_deer = 44, 285 | body_dog = 45, 286 | body_drunkard = 46, 287 | body_farmer = 47, 288 | body_fighting_monk = 48, 289 | body_fire_eater = 49, 290 | body_fireman = 50, 291 | body_fletcher = 51, 292 | body_ghost = 52, 293 | body_girl = 53, 294 | body_healer = 54, 295 | body_horse_trader = 55, 296 | body_hunter = 56, 297 | body_innkeeper = 57, 298 | body_iron_miner = 58, 299 | body_jester = 59, 300 | body_juggler = 60, 301 | body_knight = 61, 302 | body_knight_top = 62, 303 | body_ladder_bearer = 63, 304 | body_lady = 64, 305 | body_lord = 65, 306 | body_maceman = 66, 307 | body_man_burning = 67, 308 | body_mangonel = 68, 309 | body_miller = 69, 310 | body_missile_2 = 70, 311 | body_missile_cow = 71, 312 | body_missile = 72, 313 | body_missile_fire = 73, 314 | body_mother = 74, 315 | body_ox = 75, 316 | body_peasant = 76, 317 | body_pikeman = 77, 318 | body_pitch_worker = 78, 319 | body_poleturner = 79, 320 | body_priest = 80, 321 | body_rabbit = 81, 322 | body_seagull = 82, 323 | body_shield = 83, 324 | body_siege_engineer = 84, 325 | body_siege_tower = 85, 326 | body_spearman = 86, 327 | body_splash = 87, 328 | body_stonemason = 88, 329 | body_swordsman = 89, 330 | body_tanner = 90, 331 | body_tent = 91, 332 | body_trader = 92, 333 | body_trebutchet = 93, 334 | body_tunnelor = 94, 335 | body_wolf = 95, 336 | body_woodcutter = 96, 337 | tree_apple = 97, 338 | tree_birch = 98, 339 | tree_chestnut = 99, 340 | tree_oak = 100, 341 | tree_pine = 101, 342 | tree_shrub1 = 102, 343 | tree_shrub2 = 103 344 | }; 345 | 346 | enum class TilesetID { 347 | killing_pits = 0, 348 | pitch_ditches = 1, 349 | tile_buildings1 = 2, 350 | tile_buildings2 = 3, 351 | tile_burnt = 4, 352 | tile_castle = 5, 353 | tile_churches = 6, 354 | tile_data = 7, 355 | tile_farmland = 8, 356 | tile_flatties = 9, 357 | tile_goods = 10, 358 | tile_land3 = 11, 359 | tile_land8 = 12, 360 | tile_land_and_stones = 13, 361 | tile_land_macros = 14, 362 | tile_rocks8 = 15, 363 | tile_ruins = 16, 364 | tile_sea8 = 17, 365 | tile_sea_new_01 = 18, 366 | tile_workshops = 19 367 | }; 368 | enum class GenericImageID { 369 | anim_buildings2 = 0, 370 | anim_campaign_map_flags = 1, 371 | anim_castle = 2, 372 | anim_dancing_bear = 3, 373 | anim_dog_cage = 4, 374 | anim_drawbridge = 5, 375 | anim_ducking_stool = 6, 376 | anim_gallows = 7, 377 | anim_gibbet = 8, 378 | anim_goods = 9, 379 | anim_heads = 10, 380 | anim_killing_pits = 11, 381 | anim_rack = 12, 382 | anim_stocks = 13, 383 | anim_tunnelors_guild = 14, 384 | anim_whitecaps = 15, 385 | army_units = 16, 386 | blast3 = 17, 387 | body_brazier = 18, 388 | body_disease = 19, 389 | body_fire2 = 20, 390 | body_fire = 21, 391 | body_gate = 22, 392 | body_steam = 23, 393 | cracks = 24, 394 | cursors = 25, 395 | enemy_faces = 26, 396 | float_pop_circ = 27, 397 | floats = 28, 398 | floats_new = 29, 399 | font_slanted = 30, 400 | font_stronghold_aa = 31, 401 | font_stronghold = 32, 402 | icons_front_end_builder = 33, 403 | icons_front_end_combat = 34, 404 | icons_front_end = 35, 405 | icons_front_end_economics = 36, 406 | icons_placeholders = 37, 407 | interface_army = 38, 408 | interface_buttons = 39, 409 | interface_icons2 = 40, 410 | interface_icons3 = 41, 411 | interface_ruins = 42, 412 | interface_slider_bar = 43, 413 | mapedit_buttons = 44, 414 | mini_cursors = 45, 415 | oil_dropped = 46, 416 | rock_chips = 47, 417 | scribe = 48, 418 | smoke_30x30 = 49, 419 | tile_chevrons = 50, 420 | tile_cliffs = 51, 421 | tile_rocks_chevrons = 52, 422 | tile_walls = 53 423 | }; 424 | 425 | constexpr size_t k_num_animation_assets = 426 | sizeof(animation_assets) / sizeof(AssetName); 427 | constexpr size_t k_num_tile_assets = sizeof(tile_assets) / sizeof(AssetName); 428 | constexpr size_t k_num_generic_image_assets = 0; 429 | constexpr size_t k_num_graphics_assets = 430 | k_num_animation_assets + k_num_tile_assets + k_num_generic_image_assets; 431 | 432 | constexpr size_t mapAssetToIndex(AnimationID id) 433 | { 434 | return static_cast(id); 435 | } 436 | constexpr size_t mapAssetToIndex(TilesetID id) 437 | { 438 | return static_cast(id); 439 | } 440 | constexpr size_t mapAssetToIndex(GenericImageID id) 441 | { 442 | return static_cast(id); 443 | } 444 | 445 | } // namespace assets 446 | 447 | } // namespace castlekeep 448 | #endif 449 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------