├── .gitignore ├── .gitmodules ├── ApplicationTools.cmake ├── CMakeLists.txt ├── DataSpecRegistry.hpp.in ├── Doxyfile ├── LICENSE ├── README.md ├── bintoc ├── CMakeLists.txt ├── bintoc.c ├── hecl-bintocConfig.cmake.in └── hecl-bintocConfigVersion.cmake.in ├── blender ├── CMakeLists.txt ├── hecl │ ├── Nodegrid.py │ ├── Patching.py │ ├── __init__.py │ ├── armature.py │ ├── frme.py │ ├── hmdl │ │ ├── HMDLMesh.py │ │ ├── HMDLShader.py │ │ └── __init__.py │ ├── mapa.py │ ├── mapu.py │ ├── path.py │ ├── sact │ │ ├── ANIM.py │ │ ├── SACTAction.py │ │ ├── SACTSubtype.py │ │ └── __init__.py │ ├── srea │ │ └── __init__.py │ └── swld │ │ └── __init__.py ├── hecl_blendershell.py └── zip_package.py ├── bootstrap.sh ├── driver ├── CMakeLists.txt ├── ToolBase.hpp ├── ToolCook.hpp ├── ToolExtract.hpp ├── ToolHelp.hpp ├── ToolImage.hpp ├── ToolInit.hpp ├── ToolInstallAddon.hpp ├── ToolPackage.hpp ├── ToolSpec.hpp └── main.cpp ├── extern ├── CMakeLists.txt └── libpng │ ├── ANNOUNCE │ ├── CHANGES │ ├── CMakeLists.txt │ ├── README │ ├── TODO │ ├── arm │ ├── arm_init.c │ ├── filter_neon.S │ ├── filter_neon_intrinsics.c │ └── palette_neon_intrinsics.c │ ├── intel │ ├── filter_sse2_intrinsics.c │ └── intel_init.c │ ├── png.c │ ├── png.h │ ├── pngconf.h │ ├── pngdebug.h │ ├── pngerror.c │ ├── pngget.c │ ├── pnginfo.h │ ├── pnglibconf.h │ ├── pngmem.c │ ├── pngpread.c │ ├── pngpriv.h │ ├── pngread.c │ ├── pngrio.c │ ├── pngrtran.c │ ├── pngrutil.c │ ├── pngset.c │ ├── pngstruct.h │ ├── pngtest.c │ ├── pngtrans.c │ ├── pngwio.c │ ├── pngwrite.c │ ├── pngwtran.c │ └── pngwutil.c ├── extra └── hecl_autocomplete.sh ├── include └── hecl │ ├── ApplicationReps.hpp.in │ ├── Backend.hpp │ ├── BitVector.hpp │ ├── Blender │ ├── Connection.hpp │ ├── SDNARead.hpp │ └── Token.hpp │ ├── CVar.hpp │ ├── CVarCommons.hpp │ ├── CVarManager.hpp │ ├── ClientProcess.hpp │ ├── Compilers.hpp │ ├── Console.hpp │ ├── Database.hpp │ ├── FourCC.hpp │ ├── HMDLMeta.hpp │ ├── MathExtras.hpp │ ├── MultiProgressPrinter.hpp │ ├── Pipeline.hpp │ ├── PipelineBase.hpp │ ├── Runtime.hpp │ ├── SteamFinder.hpp │ ├── SystemChar.hpp │ ├── TypedVariant.hpp │ ├── UniformBufferPool.hpp │ ├── VertexBufferPool.hpp │ ├── hecl.hpp │ └── winsupport.hpp ├── lib ├── Blender │ ├── CMakeLists.txt │ ├── Connection.cpp │ ├── HMDL.cpp │ ├── MeshOptimizer.cpp │ ├── MeshOptimizer.hpp │ └── SDNARead.cpp ├── CMakeLists.txt ├── CVar.cpp ├── CVarCommons.cpp ├── CVarManager.cpp ├── ClientProcess.cpp ├── Compilers.cpp ├── Console.cpp ├── HumanizeNumber.cpp ├── MultiProgressPrinter.cpp ├── Pipeline.cpp ├── Project.cpp ├── ProjectPath.cpp ├── Runtime │ ├── CMakeLists.txt │ ├── FileStoreManager.cpp │ └── HMDL_RT.cpp ├── SteamFinder.cpp ├── WideStringConvert.cpp ├── closefrom.c └── hecl.cpp └── shaderc ├── CMakeLists.txt ├── main.cpp ├── shaderc.cpp └── shaderc.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | DataSpecRegistry.hpp 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/libSquish"] 2 | path = extern/libSquish 3 | url = ../libSquish.git 4 | branch = master 5 | [submodule "extern/athena"] 6 | path = extern/athena 7 | url = ../../libAthena/athena.git 8 | branch = master 9 | [submodule "extern/boo"] 10 | path = extern/boo 11 | url = ../boo.git 12 | branch = master 13 | [submodule "extern/libjpeg-turbo"] 14 | path = extern/libjpeg-turbo 15 | url = ../libjpeg-turbo.git 16 | branch = thp 17 | -------------------------------------------------------------------------------- /ApplicationTools.cmake: -------------------------------------------------------------------------------- 1 | include_guard(GLOBAL) 2 | 3 | file(RELATIVE_PATH REL_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR}) 4 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/${REL_PATH}) 5 | 6 | unset(HECL_APPLICATION_REPS_TARGETS_LIST CACHE) 7 | unset(HECL_APPLICATION_REPS_INCLUDES_LIST CACHE) 8 | unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL CACHE) 9 | unset(HECL_APPLICATION_PIPELINE_REPS CACHE) 10 | unset(HECL_APPLICATION_STAGE_REPS CACHE) 11 | 12 | # add_pipeline_rep(my::fully::qualified::class my_class_header.hpp [UNIVERSAL]) 13 | function(add_pipeline_rep name header) 14 | if(IS_ABSOLUTE ${header}) 15 | set(theHeader ${header}) 16 | else() 17 | set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header}) 18 | endif() 19 | if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST) 20 | set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "") 21 | endif() 22 | if ("${ARGV2}" STREQUAL "UNIVERSAL") 23 | set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL};${name}" CACHE INTERNAL "") 24 | else() 25 | set(HECL_APPLICATION_PIPELINE_REPS "${HECL_APPLICATION_PIPELINE_REPS};${name}" CACHE INTERNAL "") 26 | endif() 27 | endfunction() 28 | 29 | # add_stage_rep(my::fully::qualified::class my_class_header.hpp) 30 | function(add_stage_rep name header) 31 | if(IS_ABSOLUTE ${header}) 32 | set(theHeader ${header}) 33 | else() 34 | set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header}) 35 | endif() 36 | if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST) 37 | set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "") 38 | endif() 39 | set(HECL_APPLICATION_STAGE_REPS "${HECL_APPLICATION_STAGE_REPS};${name}" CACHE INTERNAL "") 40 | endfunction() 41 | 42 | function(add_shader_target target) 43 | if (NOT ${target} IN_LIST HECL_APPLICATION_REPS_TARGETS_LIST) 44 | set(HECL_APPLICATION_REPS_TARGETS_LIST "${HECL_APPLICATION_REPS_TARGETS_LIST};${target}" CACHE INTERNAL "") 45 | endif() 46 | endfunction() 47 | 48 | function(add_shader file) 49 | get_filename_component(name ${file} NAME) 50 | get_filename_component(dir ${file} DIRECTORY) 51 | shaderc(${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name} ${file}.shader) 52 | add_stage_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp) 53 | add_pipeline_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp UNIVERSAL) 54 | add_library(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.cpp) 55 | add_shader_target(shader_${name}) 56 | endfunction() 57 | 58 | function(add_special_shader name) 59 | add_stage_rep(${name} ${name}.hpp) 60 | add_pipeline_rep(${name} ${name}.hpp UNIVERSAL) 61 | add_library(${name} ${name}.hpp ${ARGN}) 62 | add_shader_target(${name}) 63 | endfunction() 64 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 2 | cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17 3 | project(hecl) 4 | 5 | if(MSVC) 6 | add_compile_options( 7 | # Disable exceptions 8 | $<$:/EHsc> 9 | /wd4267 /wd4244 10 | ) 11 | add_compile_definitions(UNICODE=1 _UNICODE=1 _CRT_SECURE_NO_WARNINGS=1) 12 | else() 13 | set(CMAKE_CXX_STANDARD 20) 14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 15 | add_compile_options( 16 | # Disable exceptions 17 | $<$:-fno-exceptions> 18 | -Wno-multichar 19 | ) 20 | endif() 21 | endif() 22 | 23 | include(ApplicationTools.cmake) 24 | 25 | configure_file(DataSpecRegistry.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/DataSpecRegistry.hpp @ONLY) 26 | 27 | unset(HECL_APPLICATION_REPS_INCLUDES_LOCAL) 28 | foreach(theHeader ${HECL_APPLICATION_REPS_INCLUDES_LIST}) 29 | set(HECL_APPLICATION_REPS_INCLUDES_LOCAL "${HECL_APPLICATION_REPS_INCLUDES_LOCAL}#include \"${theHeader}\"\n") 30 | endforeach() 31 | unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL) 32 | foreach(name ${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL}) 33 | set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL}UNIVERSAL_PIPELINES_${name} \\\n") 34 | endforeach() 35 | unset(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL) 36 | unset(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL) 37 | unset(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL) 38 | unset(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL) 39 | unset(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL) 40 | foreach(name ${HECL_APPLICATION_PIPELINE_REPS}) 41 | set(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL}OPENGL_PIPELINES_${name} \\\n") 42 | set(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL}VULKAN_PIPELINES_${name} \\\n") 43 | set(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL}D3D11_PIPELINES_${name} \\\n") 44 | set(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL}METAL_PIPELINES_${name} \\\n") 45 | set(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL}NX_PIPELINES_${name} \\\n") 46 | endforeach() 47 | 48 | unset(HECL_APPLICATION_STAGE_REPS_LOCAL) 49 | foreach(name ${HECL_APPLICATION_STAGE_REPS}) 50 | set(HECL_APPLICATION_STAGE_REPS_LOCAL "${HECL_APPLICATION_STAGE_REPS_LOCAL}STAGES_${name}(P, S) \\\n") 51 | endforeach() 52 | 53 | configure_file(include/hecl/ApplicationReps.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/ApplicationReps.hpp @ONLY) 54 | 55 | add_subdirectory(extern) 56 | add_subdirectory(bintoc) 57 | 58 | if(NOT TARGET bintoc) 59 | # Use native if cross compiling 60 | find_package(hecl-bintoc REQUIRED) 61 | endif() 62 | 63 | if(NOT TARGET atdna) 64 | # Import native atdna if cross-compiling 65 | find_package(atdna REQUIRED) 66 | if(NOT TARGET atdna) 67 | message(FATAL_ERROR "atdna required for building hecl; please verify LLVM installation") 68 | endif() 69 | endif() 70 | 71 | add_subdirectory(lib) 72 | add_subdirectory(blender) 73 | add_subdirectory(driver) 74 | install(DIRECTORY include/hecl DESTINATION include/hecl) 75 | -------------------------------------------------------------------------------- /DataSpecRegistry.hpp.in: -------------------------------------------------------------------------------- 1 | /* Include this file once in the main translation unit of any executable file 2 | * using HECL's database functionality (see driver/main.cpp) 3 | */ 4 | #ifdef DATA_SPEC_REGISTRY_HPP 5 | #error DataSpecRegistry.hpp may only be included once 6 | #endif 7 | #define DATA_SPEC_REGISTRY_HPP 8 | 9 | #include "hecl/Database.hpp" 10 | 11 | namespace hecl::Database { 12 | /* Centralized registry for DataSpec lookup */ 13 | std::vector DATA_SPEC_REGISTRY; 14 | } 15 | 16 | @HECL_DATASPEC_DECLS@ 17 | 18 | /* Please Call Me! */ 19 | void HECLRegisterDataSpecs() { 20 | @HECL_DATASPEC_PUSHES@ 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015-2018 HECL Contributors 4 | Original Authors: Jack Andersen and Phillip "Antidote" Stephens 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### HECL (high-level, extensible combiner language) 2 | 3 | **HECL** is a toolkit for building custom asset pipelines, assisting the development of conversion tools and runtime loaders. 4 | 5 | The most significant feature is the intermediate HECL language, using an expressive command syntax to represent cross-platform shaders. This includes a common source representation and intermediate binary representation. Complete vertex and fragment shader programs are generated for supported platforms and may be built on-demand as part of a 3D application runtime. 6 | 7 | ```py 8 | # Combiner 1: Opaque *Diffuse* and *Emissive* 9 | HECLOpaque(Texture(0, UV(0)) * Lighting() + Texture(1, UV(0))) 10 | 11 | # Combiner 2: Alpha-blended single-texture 12 | # (both texture-accesses folded to a single sample operation) 13 | HECLAlpha(Texture(0, UV(0)), Texture(0, UV(0)).a) 14 | 15 | # Combiner 3: Additive-blended single-texture 16 | # (modern graphics APIs require blending configuration along with all shader configs) 17 | HECLAdditive(Texture(0, UV(0)), Texture(0, UV(0)).a) 18 | ``` 19 | 20 | Beyond shaders, HECL also defines a rigged mesh format called HMDL. Meshes using this encoding interact with HECL, with pose transforms applied via the vertex shader. 21 | 22 | For asset pipelines, HECL provides a project system with dependency-resolution much like an IDE or `make`. Assets in their editable representation are *cooked* in-bulk and whenever the source file is updated. Currently, blender is the only-supported input format for rigged meshes with node-materials. 23 | 24 | #### Supported Backends 25 | 26 | * GLSL 330 *(with optional SPIR-V conversion)* 27 | * HLSL (Shader Model 4) 28 | * Metal 1.1 29 | * GX *(complete TexCoordGen and TEV configs in intermediate structures)* 30 | -------------------------------------------------------------------------------- /bintoc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT CMAKE_CROSSCOMPILING) 2 | add_executable(bintoc bintoc.c) 3 | target_include_directories(bintoc PRIVATE ${ZLIB_INCLUDE_DIR}) 4 | target_link_libraries(bintoc ${ZLIB_LIBRARIES}) 5 | function(bintoc out in sym) 6 | if(IS_ABSOLUTE ${out}) 7 | set(theOut ${out}) 8 | else() 9 | set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out}) 10 | endif() 11 | if(IS_ABSOLUTE ${in}) 12 | set(theIn ${in}) 13 | else() 14 | set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in}) 15 | endif() 16 | get_filename_component(outDir ${theOut} DIRECTORY) 17 | file(MAKE_DIRECTORY ${outDir}) 18 | add_custom_command(OUTPUT ${theOut} 19 | COMMAND $ ARGS ${theIn} ${theOut} ${sym} 20 | DEPENDS ${theIn} bintoc) 21 | endfunction() 22 | function(bintoc_compress out in sym) 23 | if(IS_ABSOLUTE ${out}) 24 | set(theOut ${out}) 25 | else() 26 | set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out}) 27 | endif() 28 | if(IS_ABSOLUTE ${in}) 29 | set(theIn ${in}) 30 | else() 31 | set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in}) 32 | endif() 33 | get_filename_component(outDir ${theOut} DIRECTORY) 34 | file(MAKE_DIRECTORY ${outDir}) 35 | add_custom_command(OUTPUT ${theOut} 36 | COMMAND $ ARGS --compress ${theIn} ${theOut} ${sym} 37 | DEPENDS ${theIn} bintoc) 38 | endfunction() 39 | 40 | ################## 41 | # Package Export # 42 | ################## 43 | 44 | # Add all targets to the build-tree export set 45 | export(TARGETS bintoc FILE "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocTargets.cmake") 46 | 47 | # Export the package for use from the build-tree 48 | # (this registers the build-tree with a global CMake-registry) 49 | export(PACKAGE hecl-bintoc) 50 | 51 | # Create the atdnaConfig.cmake 52 | # ... for the build tree 53 | configure_file(hecl-bintocConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocConfig.cmake" @ONLY) 54 | # ... for the install tree 55 | configure_file(hecl-bintocConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hecl-bintocConfig.cmake" @ONLY) 56 | # ... for both 57 | configure_file(hecl-bintocConfigVersion.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocConfigVersion.cmake" @ONLY) 58 | 59 | else() 60 | # Use native if cross compiling 61 | find_package(hecl-bintoc REQUIRED) 62 | endif() 63 | -------------------------------------------------------------------------------- /bintoc/bintoc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define CHUNK 16384 8 | #define LINE_BREAK 32 9 | static uint8_t buf[CHUNK]; 10 | static uint8_t zbuf[CHUNK]; 11 | 12 | void print_usage() { fprintf(stderr, "Usage: bintoc [--compress] \n"); } 13 | 14 | int main(int argc, char** argv) { 15 | if (argc < 4) { 16 | print_usage(); 17 | return 1; 18 | } 19 | char* input = argv[1]; 20 | char* output = argv[2]; 21 | char* symbol = argv[3]; 22 | bool compress = false; 23 | if (strcmp(input, "--compress") == 0) { 24 | if (argc < 5) { 25 | print_usage(); 26 | return 1; 27 | } 28 | input = argv[2]; 29 | output = argv[3]; 30 | symbol = argv[4]; 31 | compress = true; 32 | } 33 | FILE* fin = fopen(input, "rb"); 34 | if (!fin) { 35 | fprintf(stderr, "Unable to open %s for reading\n", input); 36 | return 1; 37 | } 38 | FILE* fout = fopen(output, "wb"); 39 | if (!fout) { 40 | fprintf(stderr, "Unable to open %s for writing\n", output); 41 | return 1; 42 | } 43 | fprintf(fout, "#include \n#include \n"); 44 | fprintf(fout, "extern \"C\" const uint8_t %s[] =\n{\n ", symbol); 45 | size_t totalSz = 0; 46 | size_t readSz; 47 | if (compress) { 48 | size_t compressedSz = 0; 49 | z_stream strm = {.zalloc = Z_NULL, .zfree = Z_NULL, .opaque = Z_NULL}; 50 | int ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS | 16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); 51 | if (ret != Z_OK) { 52 | fprintf(stderr, "zlib initialization failed %d\n", ret); 53 | return 1; 54 | } 55 | while ((strm.avail_in = fread(buf, 1, sizeof(buf), fin))) { 56 | totalSz += strm.avail_in; 57 | strm.next_in = buf; 58 | int eof = feof(fin); 59 | do { 60 | strm.next_out = zbuf; 61 | strm.avail_out = sizeof(zbuf); 62 | ret = deflate(&strm, eof ? Z_FINISH : Z_NO_FLUSH); 63 | if (ret == Z_STREAM_ERROR) { 64 | fprintf(stderr, "zlib compression failed %d\n", ret); 65 | return 1; 66 | } 67 | size_t sz = sizeof(zbuf) - strm.avail_out; 68 | if (sz > 0) { 69 | for (int b = 0; b < sz; ++b) { 70 | fprintf(fout, "0x%02X, ", zbuf[b]); 71 | if ((compressedSz + b + 1) % LINE_BREAK == 0) 72 | fprintf(fout, "\n "); 73 | } 74 | compressedSz += sz; 75 | } 76 | } while (strm.avail_out == 0 || (eof && (ret == Z_OK || ret == Z_BUF_ERROR))); 77 | } 78 | deflateEnd(&strm); 79 | fprintf(fout, "0x00};\nextern \"C\" const size_t %s_SZ = %zu;\n", symbol, compressedSz); 80 | fprintf(fout, "extern \"C\" const size_t %s_DECOMPRESSED_SZ = %zu;\n", symbol, totalSz); 81 | } else { 82 | while ((readSz = fread(buf, 1, sizeof(buf), fin))) { 83 | for (int b = 0; b < readSz; ++b) { 84 | fprintf(fout, "0x%02X, ", buf[b]); 85 | if ((totalSz + b + 1) % LINE_BREAK == 0) 86 | fprintf(fout, "\n "); 87 | } 88 | totalSz += readSz; 89 | } 90 | fprintf(fout, "0x0};\nextern \"C\" const size_t %s_SZ = %zu;\n", symbol, totalSz); 91 | } 92 | fclose(fin); 93 | fclose(fout); 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /bintoc/hecl-bintocConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # - Config file for the bintoc package 2 | 3 | # Compute paths 4 | get_filename_component(BINTOC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 5 | 6 | # Our library dependencies (contains definitions for IMPORTED targets) 7 | if(NOT TARGET bintoc AND NOT bintoc_BINARY_DIR) 8 | include("${BINTOC_CMAKE_DIR}/hecl-bintocTargets.cmake") 9 | endif() 10 | 11 | function(bintoc out in sym) 12 | if(IS_ABSOLUTE ${out}) 13 | set(theOut ${out}) 14 | else() 15 | set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out}) 16 | endif() 17 | if(IS_ABSOLUTE ${in}) 18 | set(theIn ${in}) 19 | else() 20 | set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in}) 21 | endif() 22 | get_filename_component(outDir ${theOut} DIRECTORY) 23 | file(MAKE_DIRECTORY ${outDir}) 24 | add_custom_command(OUTPUT ${theOut} 25 | COMMAND $ ARGS ${theIn} ${theOut} ${sym} 26 | DEPENDS ${theIn}) 27 | endfunction() 28 | 29 | function(bintoc_compress out in sym) 30 | if(IS_ABSOLUTE ${out}) 31 | set(theOut ${out}) 32 | else() 33 | set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out}) 34 | endif() 35 | if(IS_ABSOLUTE ${in}) 36 | set(theIn ${in}) 37 | else() 38 | set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in}) 39 | endif() 40 | get_filename_component(outDir ${theOut} DIRECTORY) 41 | file(MAKE_DIRECTORY ${outDir}) 42 | add_custom_command(OUTPUT ${theOut} 43 | COMMAND $ ARGS --compress ${theIn} ${theOut} ${sym} 44 | DEPENDS ${theIn}) 45 | endfunction() 46 | -------------------------------------------------------------------------------- /bintoc/hecl-bintocConfigVersion.cmake.in: -------------------------------------------------------------------------------- 1 | set(PACKAGE_VERSION "@BINTOC_VERSION@") 2 | 3 | # Check whether the requested PACKAGE_FIND_VERSION is compatible 4 | if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") 5 | set(PACKAGE_VERSION_COMPATIBLE FALSE) 6 | else() 7 | set(PACKAGE_VERSION_COMPATIBLE TRUE) 8 | if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") 9 | set(PACKAGE_VERSION_EXACT TRUE) 10 | endif() 11 | endif() 12 | 13 | -------------------------------------------------------------------------------- /blender/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND PY_SOURCES 2 | hecl/__init__.py 3 | hecl/Nodegrid.py 4 | hecl/Patching.py 5 | hecl/hmdl/__init__.py 6 | hecl/hmdl/HMDLMesh.py 7 | hecl/hmdl/HMDLShader.py 8 | hecl/sact/__init__.py 9 | hecl/sact/SACTAction.py 10 | hecl/sact/SACTSubtype.py 11 | hecl/srea/__init__.py 12 | hecl/swld/__init__.py 13 | hecl/armature.py 14 | hecl/mapa.py 15 | hecl/mapu.py 16 | hecl/frme.py 17 | hecl/path.py) 18 | 19 | bintoc(hecl_blendershell.cpp hecl_blendershell.py HECL_BLENDERSHELL) 20 | 21 | find_package(Python COMPONENTS Interpreter REQUIRED) 22 | 23 | add_custom_command(OUTPUT hecl.zip DEPENDS ${PY_SOURCES} 24 | COMMAND ${Python_EXECUTABLE} ARGS zip_package.py ${CMAKE_CURRENT_BINARY_DIR}/hecl.zip 25 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 26 | COMMENT "Generating addon package") 27 | bintoc(hecl_addon.cpp "${CMAKE_CURRENT_BINARY_DIR}/hecl.zip" HECL_ADDON) 28 | 29 | add_library(hecl-blender-addon 30 | hecl_blendershell.py 31 | hecl_blendershell.cpp 32 | zip_package.py 33 | hecl.zip 34 | hecl_addon.cpp 35 | ${PY_SOURCES}) 36 | -------------------------------------------------------------------------------- /blender/hecl/Nodegrid.py: -------------------------------------------------------------------------------- 1 | # Node Grid Arranger Class 2 | NODE_PADDING = 80 3 | FRAME_NAMES = ['Textures','Output','Blend'] 4 | FRAME_WIDTHS = [400, 180, 180] 5 | TOTAL_WIDTH = 0.0 6 | for width in FRAME_WIDTHS: 7 | TOTAL_WIDTH += width + NODE_PADDING 8 | FRAME_COLORS = [(0.6,0.48,0.44),(0.53,0.6,0.47),(0.56,0.46,0.90)] 9 | class Nodegrid: 10 | 11 | def __init__(self, nodetree, cycles=False): 12 | self.ncol = len(FRAME_NAMES) 13 | self.heights = [] 14 | self.frames = [] 15 | self.col_roffs = [[0.0,0.0]] * self.ncol 16 | for i in range(self.ncol): 17 | if cycles and i<1: 18 | self.heights.append(-1600.0) 19 | self.frames.append(None) 20 | continue 21 | elif cycles: 22 | self.heights.append(-1600.0) 23 | else: 24 | self.heights.append(0.0) 25 | frame_node = nodetree.nodes.new('NodeFrame') 26 | frame_node.label = FRAME_NAMES[i] 27 | frame_node.use_custom_color = True 28 | frame_node.color = FRAME_COLORS[i] 29 | self.frames.append(frame_node) 30 | 31 | def place_node(self, node, col, update_height = True): 32 | if col < 0 or col >= self.ncol: 33 | return False 34 | 35 | x_pos = NODE_PADDING 36 | for i in range(col): 37 | x_pos += FRAME_WIDTHS[i] + NODE_PADDING*2 38 | node.location[0] = x_pos - TOTAL_WIDTH/2 39 | node.location[1] = self.heights[col] 40 | if update_height: 41 | self.heights[col] -= node.height + NODE_PADDING 42 | self.frames[col].height += node.height + NODE_PADDING 43 | node.parent = self.frames[col] 44 | 45 | return True 46 | 47 | def place_node_right(self, node, col, srow): 48 | heights_backup = self.heights[col] 49 | if self.place_node(node, col): 50 | node.location[0] += self.col_roffs[col][srow] 51 | if srow == 1: 52 | node.location[1] -= 175 53 | self.col_roffs[col][srow] += 200 54 | self.heights[col] = heights_backup 55 | 56 | def row_break(self, col): 57 | self.heights[col] -= 350 58 | self.col_roffs[col][0] = 0.0 59 | self.col_roffs[col][1] = 0.0 60 | -------------------------------------------------------------------------------- /blender/hecl/Patching.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | import os.path 4 | from pathlib import Path 5 | 6 | def _mkdir(path): 7 | try: 8 | os.mkdir(path) 9 | except: 10 | pass 11 | 12 | def path_components(path): 13 | retval = [] 14 | base, end = os.path.split(path) 15 | while end != '': 16 | retval.insert(0, end) 17 | base, end = os.path.split(base) 18 | return retval 19 | 20 | def find_project_root(): 21 | if bpy.data.filepath == '': 22 | return None 23 | path = os.path.split(bpy.data.filepath) 24 | test_path = os.path.join(path[0], '.hecl') 25 | while not os.path.exists(test_path): 26 | path = os.path.split(path[0]) 27 | test_path = os.path.join(path[0], '.hecl') 28 | if os.path.exists(test_path): 29 | return path[0] 30 | return None 31 | 32 | def get_patching_dirs(make_dirs=False): 33 | proj_root = find_project_root() 34 | if not proj_root: 35 | return None, None 36 | rel_to_blend = os.path.relpath(bpy.data.filepath, start=proj_root) 37 | rel_to_blend_comps = path_components(rel_to_blend) 38 | trace_dir = os.path.join(proj_root, '.hecl', 'patches') 39 | global_out = trace_dir 40 | if not make_dirs and not os.path.exists(trace_dir): 41 | return None, global_out 42 | _mkdir(trace_dir) 43 | for comp in rel_to_blend_comps: 44 | ext_pair = os.path.splitext(comp) 45 | if ext_pair[1] == '.blend': 46 | trace_dir = os.path.join(trace_dir, ext_pair[0]) 47 | if not make_dirs and not os.path.exists(trace_dir): 48 | return None, global_out 49 | _mkdir(trace_dir) 50 | return trace_dir, global_out 51 | trace_dir = os.path.join(trace_dir, comp) 52 | if not make_dirs and not os.path.exists(trace_dir): 53 | return None, global_out 54 | _mkdir(trace_dir) 55 | 56 | class FILE_OT_hecl_patching_save(bpy.types.Operator): 57 | '''Save text datablocks to hecl patching directory''' 58 | bl_idname = "file.hecl_patching_save" 59 | bl_label = "Save HECL Patches" 60 | bl_options = {'REGISTER'} 61 | 62 | def execute(self, context): 63 | patching_dir, global_dir = get_patching_dirs(make_dirs=True) 64 | if not patching_dir: 65 | self.report({'WARNING'}, 'Unable to save patches for ' + bpy.data.filepath) 66 | return {'CANCELLED'} 67 | count = 0 68 | for text in bpy.data.texts: 69 | if not text.name.endswith('.py') or text.name.startswith('g_'): 70 | continue 71 | text_abspath = os.path.join(patching_dir, text.name) 72 | text_file = open(text_abspath, 'w') 73 | text_file.write(text.as_string()) 74 | text_file.close() 75 | count += 1 76 | if count == 1: 77 | self.report({'INFO'}, 'saved 1 patch') 78 | else: 79 | self.report({'INFO'}, 'saved %d patches' % count) 80 | return {'FINISHED'} 81 | 82 | class FILE_OT_hecl_patching_load(bpy.types.Operator): 83 | '''Load text datablocks from hecl patching directory''' 84 | bl_idname = "file.hecl_patching_load" 85 | bl_label = "Load HECL Patches" 86 | bl_options = {'REGISTER'} 87 | 88 | def execute(self, context): 89 | patching_dir, global_dir = get_patching_dirs() 90 | count = 0 91 | 92 | # Locals 93 | if patching_dir: 94 | p = Path(patching_dir) 95 | for path in p.glob('*.py'): 96 | path = path.name 97 | text_abspath = os.path.join(patching_dir, path) 98 | text_file = open(text_abspath, 'r') 99 | if path in bpy.data.texts: 100 | text = bpy.data.texts[path] 101 | else: 102 | text = bpy.data.texts.new(path) 103 | text.from_string(text_file.read()) 104 | text_file.close() 105 | count += 1 106 | 107 | # Globals 108 | if global_dir: 109 | p = Path(global_dir) 110 | for path in p.glob('g_*.py'): 111 | path = path.name 112 | text_abspath = os.path.join(global_dir, path) 113 | text_file = open(text_abspath, 'r') 114 | if path in bpy.data.texts: 115 | text = bpy.data.texts[path] 116 | else: 117 | text = bpy.data.texts.new(path) 118 | text.from_string(text_file.read()) 119 | text_file.close() 120 | count += 1 121 | 122 | if count == 1: 123 | self.report({'INFO'}, 'loaded 1 patch') 124 | else: 125 | self.report({'INFO'}, 'loaded %d patches' % count) 126 | return {'FINISHED'} 127 | 128 | def save_func(self, context): 129 | self.layout.operator("file.hecl_patching_save", text="Save HECL Patches") 130 | 131 | def load_func(self, context): 132 | self.layout.operator("file.hecl_patching_load", text="Load HECL Patches") 133 | 134 | def register(): 135 | bpy.utils.register_class(FILE_OT_hecl_patching_save) 136 | bpy.utils.register_class(FILE_OT_hecl_patching_load) 137 | bpy.types.TOPBAR_MT_file_external_data.append(load_func) 138 | bpy.types.TOPBAR_MT_file_external_data.append(save_func) 139 | 140 | def unregister(): 141 | bpy.utils.unregister_class(FILE_OT_hecl_patching_save) 142 | bpy.utils.unregister_class(FILE_OT_hecl_patching_load) 143 | bpy.types.TOPBAR_MT_file_external_data.remove(load_func) 144 | bpy.types.TOPBAR_MT_file_external_data.remove(save_func) 145 | -------------------------------------------------------------------------------- /blender/hecl/armature.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | def cook(writebuf, arm): 4 | writebuf(struct.pack('I', len(arm.bones))) 5 | for bone in arm.bones: 6 | writebuf(struct.pack('I', len(bone.name))) 7 | writebuf(bone.name.encode()) 8 | 9 | writebuf(struct.pack('fff', bone.head_local[0], bone.head_local[1], bone.head_local[2])) 10 | 11 | if bone.parent: 12 | writebuf(struct.pack('i', arm.bones.find(bone.parent.name))) 13 | else: 14 | writebuf(struct.pack('i', -1)) 15 | 16 | writebuf(struct.pack('I', len(bone.children))) 17 | for child in bone.children: 18 | writebuf(struct.pack('i', arm.bones.find(child.name))) 19 | 20 | def draw(layout, context): 21 | layout.prop_search(context.scene, 'hecl_arm_obj', context.scene, 'objects') 22 | if not len(context.scene.hecl_arm_obj): 23 | layout.label(text="Armature not specified", icon='ERROR') 24 | elif context.scene.hecl_arm_obj not in context.scene.objects: 25 | layout.label(text="'"+context.scene.hecl_arm_obj+"' not in scene", icon='ERROR') 26 | else: 27 | obj = context.scene.objects[context.scene.hecl_arm_obj] 28 | if obj.type != 'ARMATURE': 29 | layout.label(text="'"+context.scene.hecl_arm_obj+"' not an 'ARMATURE'", icon='ERROR') 30 | 31 | import bpy 32 | def register(): 33 | bpy.types.Scene.hecl_arm_obj = bpy.props.StringProperty( 34 | name='HECL Armature Object', 35 | description='Blender Armature Object to export during HECL\'s cook process') 36 | -------------------------------------------------------------------------------- /blender/hecl/hmdl/HMDLMesh.py: -------------------------------------------------------------------------------- 1 | import bpy, bmesh, operator, struct 2 | 3 | # Function to quantize normals to 15-bit precision 4 | def quant_norm(n): 5 | nf = n.copy() 6 | for i in range(3): 7 | nf[i] = int(nf[i] * 16384) / 16384.0 8 | return nf.freeze() 9 | 10 | # Function to quantize lightmap UVs to 15-bit precision 11 | def quant_luv(n): 12 | uf = n.copy() 13 | for i in range(2): 14 | uf[i] = int(uf[i] * 32768) / 32768.0 15 | return uf.freeze() 16 | 17 | # Function to output all mesh attribute values 18 | def write_mesh_attrs(writebuf, bm, rna_loops, use_luv, material_slots): 19 | dlay = None 20 | if len(bm.verts.layers.deform): 21 | dlay = bm.verts.layers.deform[0] 22 | 23 | clays = [] 24 | for cl in range(len(bm.loops.layers.color)): 25 | clays.append(bm.loops.layers.color[cl]) 26 | writebuf(struct.pack('I', len(clays))) 27 | 28 | luvlay = None 29 | if use_luv: 30 | luvlay = bm.loops.layers.uv[0] 31 | ulays = [] 32 | for ul in range(len(bm.loops.layers.uv)): 33 | ulays.append(bm.loops.layers.uv[ul]) 34 | writebuf(struct.pack('I', len(ulays))) 35 | 36 | # Verts 37 | writebuf(struct.pack('I', len(bm.verts))) 38 | for v in bm.verts: 39 | writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2])) 40 | if dlay: 41 | sf = tuple(sorted(v[dlay].items())) 42 | writebuf(struct.pack('I', len(sf))) 43 | total_len = 0.0 44 | for ent in sf: 45 | total_len += ent[1] 46 | for ent in sf: 47 | writebuf(struct.pack('If', ent[0], ent[1] / total_len)) 48 | else: 49 | writebuf(struct.pack('I', 0)) 50 | 51 | # Loops 52 | loop_count = 0 53 | for f in bm.faces: 54 | for l in f.loops: 55 | loop_count += 1 56 | writebuf(struct.pack('I', loop_count)) 57 | for f in bm.faces: 58 | for l in f.loops: 59 | if rna_loops: 60 | nf = quant_norm(rna_loops[l.index].normal) 61 | else: 62 | nf = quant_norm(l.vert.normal) 63 | writebuf(struct.pack('fff', nf[0], nf[1], nf[2])) 64 | for cl in range(len(clays)): 65 | col = l[clays[cl]] 66 | writebuf(struct.pack('fff', col[0], col[1], col[2])) 67 | for cl in range(len(ulays)): 68 | if luvlay and cl == 0 and material_slots[l.face.material_index].material['retro_lightmapped']: 69 | uv = quant_luv(l[luvlay].uv) 70 | else: 71 | uv = l[ulays[cl]].uv 72 | writebuf(struct.pack('ff', uv[0], uv[1])) 73 | writebuf(struct.pack('IIIII', l.vert.index, l.edge.index, l.face.index, 74 | l.link_loop_next.index, l.link_loop_prev.index)) 75 | if l.edge.is_contiguous: 76 | writebuf(struct.pack('II', l.link_loop_radial_next.index, l.link_loop_radial_prev.index)) 77 | else: 78 | writebuf(struct.pack('II', 0xffffffff, 0xffffffff)) 79 | 80 | # Edges 81 | writebuf(struct.pack('I', len(bm.edges))) 82 | for e in bm.edges: 83 | for v in e.verts: 84 | writebuf(struct.pack('I', v.index)) 85 | writebuf(struct.pack('I', len(e.link_faces))) 86 | for f in e.link_faces: 87 | writebuf(struct.pack('I', f.index)) 88 | writebuf(struct.pack('b', e.is_contiguous)) 89 | 90 | # Faces 91 | writebuf(struct.pack('I', len(bm.faces))) 92 | for f in bm.faces: 93 | norm = f.normal 94 | writebuf(struct.pack('fff', norm[0], norm[1], norm[2])) 95 | centroid = f.calc_center_bounds() 96 | writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2])) 97 | writebuf(struct.pack('I', f.material_index)) 98 | for l in f.loops: 99 | writebuf(struct.pack('I', l.index)) 100 | 101 | -------------------------------------------------------------------------------- /blender/hecl/mapu.py: -------------------------------------------------------------------------------- 1 | import bpy, os, struct 2 | 3 | def cook(writebuf): 4 | found_lib = False 5 | for obj in bpy.context.scene.objects: 6 | if obj.data and obj.data.library: 7 | path = os.path.normpath(bpy.path.abspath(obj.data.library.filepath)) 8 | writebuf(struct.pack('I', len(path))) 9 | writebuf(path.encode()) 10 | found_lib = True 11 | break 12 | if not found_lib: 13 | raise RuntimeError('No hexagon segments present') 14 | 15 | world_count = 0 16 | for obj in bpy.context.scene.objects: 17 | if not obj.parent and obj.type == 'EMPTY': 18 | world_count += 1 19 | writebuf(struct.pack('I', world_count)) 20 | 21 | for obj in bpy.context.scene.objects: 22 | if not obj.parent and obj.type == 'EMPTY': 23 | writebuf(struct.pack('I', len(obj.name))) 24 | writebuf(obj.name.encode()) 25 | writebuf(struct.pack('ffffffffffffffff', 26 | obj.matrix_local[0][0], obj.matrix_local[0][1], obj.matrix_local[0][2], obj.matrix_local[0][3], 27 | obj.matrix_local[1][0], obj.matrix_local[1][1], obj.matrix_local[1][2], obj.matrix_local[1][3], 28 | obj.matrix_local[2][0], obj.matrix_local[2][1], obj.matrix_local[2][2], obj.matrix_local[2][3], 29 | obj.matrix_local[3][0], obj.matrix_local[3][1], obj.matrix_local[3][2], obj.matrix_local[3][3])) 30 | writebuf(struct.pack('I', len(obj.children))) 31 | for child in obj.children: 32 | writebuf(struct.pack('ffffffffffffffff', 33 | child.matrix_local[0][0], child.matrix_local[0][1], child.matrix_local[0][2], child.matrix_local[0][3], 34 | child.matrix_local[1][0], child.matrix_local[1][1], child.matrix_local[1][2], child.matrix_local[1][3], 35 | child.matrix_local[2][0], child.matrix_local[2][1], child.matrix_local[2][2], child.matrix_local[2][3], 36 | child.matrix_local[3][0], child.matrix_local[3][1], child.matrix_local[3][2], child.matrix_local[3][3])) 37 | writebuf(struct.pack('ffff', obj.retro_mapworld_color[0], obj.retro_mapworld_color[1], 38 | obj.retro_mapworld_color[2], obj.retro_mapworld_color[3])) 39 | writebuf(struct.pack('I', len(obj.retro_mapworld_path))) 40 | writebuf(obj.retro_mapworld_path.encode()) 41 | 42 | def draw(layout, context): 43 | obj = context.active_object 44 | if not obj: 45 | return 46 | while obj.parent: 47 | obj = obj.parent 48 | layout.prop(obj, 'retro_mapworld_color', text='Color') 49 | layout.prop(obj, 'retro_mapworld_path', text='Path') 50 | 51 | # Registration 52 | def register(): 53 | bpy.types.Object.retro_mapworld_color = bpy.props.FloatVectorProperty(name='Retro: MapWorld Color',\ 54 | description='Sets map world color', subtype='COLOR', size=4, min=0.0, max=1.0) 55 | bpy.types.Object.retro_mapworld_path = bpy.props.StringProperty(name='Retro: MapWorld Path', description='Sets path to World root') 56 | -------------------------------------------------------------------------------- /blender/hecl/swld/__init__.py: -------------------------------------------------------------------------------- 1 | import bpy, struct 2 | from mathutils import Vector 3 | 4 | def build_dock_connections(scene): 5 | areas = [] 6 | docks = [] 7 | 8 | for obj in sorted(scene.objects, key=lambda x: x.name): 9 | if obj.type == 'MESH' and obj.parent is None: 10 | dock_list = [] 11 | for ch in obj.children: 12 | if ch.type == 'MESH': 13 | docks.append((len(areas), len(dock_list), ch)) 14 | dock_list.append(ch) 15 | areas.append((obj, dock_list)) 16 | 17 | dock_dict = dict() 18 | 19 | for dockA in docks: 20 | mtxA = dockA[2].matrix_world 21 | locA = Vector((mtxA[0][3], mtxA[1][3], mtxA[2][3])) 22 | match = False 23 | for dockB in docks: 24 | if dockA == dockB: 25 | continue 26 | mtxB = dockB[2].matrix_world 27 | locB = Vector((mtxB[0][3], mtxB[1][3], mtxB[2][3])) 28 | if (locA - locB).magnitude < 0.1: 29 | dock_dict[dockA[2].name] = dockB 30 | match = True 31 | break 32 | #if not match: 33 | # raise RuntimeError('No dock match for %s' % dockA[2].name) 34 | 35 | return (areas, dock_dict) 36 | 37 | # Cook 38 | def cook(writebuf): 39 | areas, dock_conns = build_dock_connections(bpy.context.scene) 40 | writebuf(struct.pack('I', len(areas))) 41 | for area in areas: 42 | obj = area[0] 43 | dock_list = area[1] 44 | writebuf(struct.pack('I', len(obj.name))) 45 | writebuf(obj.name.encode()) 46 | 47 | pt = Vector(obj.bound_box[0]) 48 | writebuf(struct.pack('fff', pt[0], pt[1], pt[2])) 49 | pt = Vector(obj.bound_box[6]) 50 | writebuf(struct.pack('fff', pt[0], pt[1], pt[2])) 51 | 52 | wmtx = obj.matrix_world 53 | writebuf(struct.pack('ffffffffffffffff', 54 | wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3], 55 | wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3], 56 | wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3], 57 | wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3])) 58 | 59 | wmtx_inv = wmtx.inverted() 60 | 61 | writebuf(struct.pack('I', len(dock_list))) 62 | for ch in dock_list: 63 | if len(ch.data.vertices) < 4: 64 | raise RuntimeError('Not enough vertices in dock %s' % ch.name) 65 | wmtx = wmtx_inv @ ch.matrix_world 66 | for vi in range(4): 67 | v = wmtx @ ch.data.vertices[vi].co 68 | writebuf(struct.pack('fff', v[0], v[1], v[2])) 69 | if ch.name in dock_conns: 70 | conn_dock = dock_conns[ch.name] 71 | writebuf(struct.pack('I', conn_dock[0])) 72 | writebuf(struct.pack('I', conn_dock[1])) 73 | else: 74 | writebuf(struct.pack('I', 0xffffffff)) 75 | writebuf(struct.pack('I', 0xffffffff)) 76 | 77 | # Panel draw 78 | def draw(layout, context): 79 | pass 80 | -------------------------------------------------------------------------------- /blender/zip_package.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | import zipfile 3 | 4 | def zipdir(path, ziph): 5 | # ziph is zipfile handle 6 | for root, dirs, files in os.walk(path): 7 | for file in files: 8 | ziph.write(os.path.join(root, file)) 9 | 10 | package_path = 'hecl' 11 | target_zip = sys.argv[1] 12 | 13 | zf = zipfile.ZipFile(target_zip, mode='w', compression=zipfile.ZIP_DEFLATED) 14 | #print('GENERATING', target_zip) 15 | if os.path.isdir(package_path): 16 | zipdir(package_path, zf) 17 | 18 | zf.close() 19 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git submodule update --init --recursive 3 | 4 | -------------------------------------------------------------------------------- /driver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT WINDOWS_STORE) 2 | 3 | add_executable(hecl main.cpp 4 | ToolBase.hpp 5 | ToolPackage.hpp 6 | ToolExtract.hpp 7 | ToolInit.hpp 8 | ToolHelp.hpp 9 | ToolCook.hpp 10 | ToolImage.hpp 11 | ToolSpec.hpp 12 | ../DataSpecRegistry.hpp.in) 13 | if(COMMAND add_sanitizers) 14 | add_sanitizers(hecl) 15 | endif() 16 | 17 | if(NOT WIN32) 18 | list(APPEND PLAT_LIBS pthread) 19 | endif() 20 | 21 | if(APPLE) 22 | find_library(CF_LIBRARY CoreFoundation) 23 | list(APPEND PLAT_LIBS ${CF_LIBRARY}) 24 | endif() 25 | 26 | target_link_libraries(hecl PUBLIC ${DATA_SPEC_LIBS} hecl-full) 27 | 28 | if(TARGET nod) 29 | target_link_libraries(hecl PUBLIC nod) 30 | target_compile_definitions(hecl PUBLIC HECL_HAS_NOD=1) 31 | endif() 32 | 33 | endif() 34 | -------------------------------------------------------------------------------- /driver/ToolBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifndef _WIN32 10 | #include 11 | #include 12 | #else 13 | #include 14 | #endif 15 | 16 | #include "hecl/Database.hpp" 17 | #include "logvisor/logvisor.hpp" 18 | 19 | extern logvisor::Module LogModule; 20 | 21 | struct ToolPassInfo { 22 | hecl::SystemString pname; 23 | hecl::SystemString cwd; 24 | std::vector args; 25 | std::vector flags; 26 | hecl::SystemString output; 27 | hecl::Database::Project* project = nullptr; 28 | unsigned verbosityLevel = 0; 29 | bool force = false; 30 | bool yes = false; 31 | bool gui = false; 32 | }; 33 | 34 | #define RED "\033[0;31m" 35 | #define GREEN "\033[0;32m" 36 | #define YELLOW "\033[0;33m" 37 | #define BLUE "\033[0;34m" 38 | #define MAGENTA "\033[0;35m" 39 | #define CYAN "\033[0;36m" 40 | #define BOLD "\033[1m" 41 | #define NORMAL "\033[0m" 42 | 43 | #define HIDE_CURSOR "\033[?25l" 44 | #define SHOW_CURSOR "\033[?25h" 45 | 46 | #define WRAP_INDENT 4 47 | 48 | extern bool XTERM_COLOR; 49 | 50 | class ToolBase { 51 | protected: 52 | const ToolPassInfo& m_info; 53 | bool m_good = false; 54 | 55 | bool continuePrompt() { 56 | if (!m_info.yes) { 57 | if (XTERM_COLOR) 58 | fmt::print(FMT_STRING(_SYS_STR("\n" BLUE BOLD "Continue?" NORMAL " (Y/n) "))); 59 | else 60 | fmt::print(FMT_STRING(_SYS_STR("\nContinue? (Y/n) "))); 61 | fflush(stdout); 62 | 63 | int ch; 64 | #ifndef _WIN32 65 | struct termios tioOld, tioNew; 66 | tcgetattr(0, &tioOld); 67 | tioNew = tioOld; 68 | tioNew.c_lflag &= ~ICANON; 69 | tcsetattr(0, TCSANOW, &tioNew); 70 | while ((ch = getchar())) 71 | #else 72 | while ((ch = getch())) 73 | #endif 74 | { 75 | if (ch == 'n' || ch == 'N') { 76 | fmt::print(FMT_STRING(_SYS_STR("\n"))); 77 | return false; 78 | } 79 | if (ch == 'y' || ch == 'Y' || ch == '\r' || ch == '\n') 80 | break; 81 | } 82 | #ifndef _WIN32 83 | tcsetattr(0, TCSANOW, &tioOld); 84 | #endif 85 | } 86 | fmt::print(FMT_STRING(_SYS_STR("\n"))); 87 | return true; 88 | } 89 | 90 | public: 91 | explicit ToolBase(const ToolPassInfo& info) : m_info(info) { 92 | hecl::VerbosityLevel = info.verbosityLevel; 93 | hecl::GuiMode = info.gui; 94 | } 95 | virtual ~ToolBase() = default; 96 | virtual hecl::SystemStringView toolName() const = 0; 97 | virtual int run() = 0; 98 | virtual void cancel() {} 99 | explicit operator bool() const { return m_good; } 100 | }; 101 | 102 | class HelpOutput { 103 | public: 104 | using HelpFunc = void (*)(HelpOutput&); 105 | 106 | private: 107 | FILE* m_sout = nullptr; 108 | HelpFunc m_helpFunc; 109 | int m_lineWidth; 110 | hecl::SystemString m_wrapBuffer; 111 | 112 | void _wrapBuf(hecl::SystemString& string) { 113 | int counter; 114 | hecl::SystemString::iterator it = string.begin(); 115 | 116 | while (it != string.end()) { 117 | std::ptrdiff_t v = it - string.begin(); 118 | 119 | /* copy string until the end of the line is reached */ 120 | for (counter = WRAP_INDENT; counter < m_lineWidth; ++counter) { 121 | if (it >= string.end()) 122 | return; 123 | if (*it == _SYS_STR('\n')) { 124 | counter = WRAP_INDENT; 125 | ++it; 126 | } 127 | if (counter == WRAP_INDENT) { 128 | for (int i = 0; i < WRAP_INDENT; ++i) 129 | it = string.insert(it, _SYS_STR(' ')) + 1; 130 | } 131 | if (it >= string.end()) 132 | return; 133 | if (*it != _SYS_STR('\n')) 134 | ++it; 135 | } 136 | /* check for whitespace */ 137 | if (isspace(*it)) { 138 | *it = _SYS_STR('\n'); 139 | counter = WRAP_INDENT; 140 | ++it; 141 | } else { 142 | /* check for nearest whitespace back in string */ 143 | for (hecl::SystemString::iterator k = it; k != string.begin(); --k) { 144 | if (isspace(*k)) { 145 | counter = WRAP_INDENT; 146 | if (k - string.begin() < v) 147 | k = string.insert(it, _SYS_STR('\n')); 148 | else 149 | *k = _SYS_STR('\n'); 150 | it = k + 1; 151 | break; 152 | } 153 | } 154 | } 155 | } 156 | } 157 | 158 | public: 159 | explicit HelpOutput(HelpFunc helpFunc) 160 | : m_helpFunc(helpFunc), m_lineWidth(hecl::GuiMode ? 120 : hecl::ConsoleWidth()) {} 161 | 162 | void go() { 163 | #if _WIN32 164 | m_sout = stdout; 165 | m_helpFunc(*this); 166 | #else 167 | m_sout = popen("less -R", "w"); 168 | if (m_sout) { 169 | m_helpFunc(*this); 170 | pclose(m_sout); 171 | } else { 172 | m_sout = stdout; 173 | m_helpFunc(*this); 174 | } 175 | #endif 176 | } 177 | 178 | void print(const hecl::SystemChar* str) { fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), str); } 179 | 180 | void printBold(const hecl::SystemChar* str) { 181 | if (XTERM_COLOR) 182 | fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "")), str); 183 | else 184 | fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), str); 185 | } 186 | 187 | void secHead(const hecl::SystemChar* headName) { 188 | if (XTERM_COLOR) 189 | fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "\n")), headName); 190 | else 191 | fmt::print(m_sout, FMT_STRING(_SYS_STR("{}\n")), headName); 192 | } 193 | 194 | void optionHead(const hecl::SystemChar* flag, const hecl::SystemChar* synopsis) { 195 | if (XTERM_COLOR) 196 | fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL " ({})\n")), flag, synopsis); 197 | else 198 | fmt::print(m_sout, FMT_STRING(_SYS_STR("{} ({})\n")), flag, synopsis); 199 | } 200 | 201 | void beginWrap() { m_wrapBuffer.clear(); } 202 | 203 | void wrap(const hecl::SystemChar* str) { m_wrapBuffer += str; } 204 | 205 | void wrapBold(const hecl::SystemChar* str) { 206 | if (XTERM_COLOR) 207 | m_wrapBuffer += _SYS_STR("" BOLD ""); 208 | m_wrapBuffer += str; 209 | if (XTERM_COLOR) 210 | m_wrapBuffer += _SYS_STR("" NORMAL ""); 211 | } 212 | 213 | void endWrap() { 214 | _wrapBuf(m_wrapBuffer); 215 | m_wrapBuffer += _SYS_STR('\n'); 216 | fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), m_wrapBuffer); 217 | m_wrapBuffer.clear(); 218 | } 219 | }; 220 | 221 | static hecl::SystemString MakePathArgAbsolute(const hecl::SystemString& arg, const hecl::SystemString& cwd) { 222 | #if _WIN32 223 | if (arg.size() >= 2 && iswalpha(arg[0]) && arg[1] == _SYS_STR(':')) 224 | return arg; 225 | if (arg[0] == _SYS_STR('\\') || arg[0] == _SYS_STR('/')) 226 | return arg; 227 | return cwd + _SYS_STR('\\') + arg; 228 | #else 229 | if (arg[0] == _SYS_STR('/') || arg[0] == _SYS_STR('\\')) 230 | return arg; 231 | if (cwd.back() == _SYS_STR('/') || cwd.back() == _SYS_STR('\\')) 232 | return cwd + arg; 233 | return cwd + _SYS_STR('/') + arg; 234 | #endif 235 | } 236 | -------------------------------------------------------------------------------- /driver/ToolExtract.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolBase.hpp" 4 | #include 5 | 6 | #if _WIN32 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | #include "hecl/MultiProgressPrinter.hpp" 13 | 14 | class ToolExtract final : public ToolBase { 15 | hecl::Database::IDataSpec::ExtractPassInfo m_einfo; 16 | struct SpecExtractPass { 17 | const hecl::Database::DataSpecEntry* m_entry; 18 | std::unique_ptr m_instance; 19 | SpecExtractPass(const hecl::Database::DataSpecEntry* entry, std::unique_ptr&& instance) 20 | : m_entry(entry), m_instance(std::move(instance)) {} 21 | SpecExtractPass(const SpecExtractPass& other) = delete; 22 | SpecExtractPass(SpecExtractPass&& other) = default; 23 | }; 24 | std::vector m_specPasses; 25 | std::vector m_reps; 26 | std::unique_ptr m_fallbackProj; 27 | hecl::Database::Project* m_useProj = nullptr; 28 | 29 | public: 30 | explicit ToolExtract(const ToolPassInfo& info) : ToolBase(info) { 31 | if (!m_info.args.size()) 32 | LogModule.report(logvisor::Fatal, FMT_STRING("hecl extract needs a source path as its first argument")); 33 | 34 | if (!info.project) { 35 | hecl::SystemString rootDir; 36 | 37 | if (info.output.empty()) { 38 | /* Get name from input file and init project there */ 39 | hecl::SystemString baseFile = info.args.front(); 40 | size_t slashPos = baseFile.rfind(_SYS_STR('/')); 41 | if (slashPos == hecl::SystemString::npos) 42 | slashPos = baseFile.rfind(_SYS_STR('\\')); 43 | if (slashPos != hecl::SystemString::npos) 44 | baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end()); 45 | size_t dotPos = baseFile.rfind(_SYS_STR('.')); 46 | if (dotPos != hecl::SystemString::npos) 47 | baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos); 48 | 49 | if (baseFile.empty()) 50 | LogModule.report(logvisor::Fatal, FMT_STRING("hecl extract must be ran within a project directory")); 51 | 52 | rootDir = info.cwd + baseFile; 53 | } else { 54 | if (hecl::PathRelative(info.output.c_str())) 55 | rootDir = info.cwd + info.output; 56 | else 57 | rootDir = info.output; 58 | } 59 | 60 | size_t ErrorRef = logvisor::ErrorCount; 61 | hecl::ProjectRootPath newProjRoot(rootDir); 62 | newProjRoot.makeDir(); 63 | m_fallbackProj.reset(new hecl::Database::Project(newProjRoot)); 64 | if (logvisor::ErrorCount > ErrorRef) 65 | LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to init project at '{}'")), rootDir); 66 | LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("initialized project at '{}/.hecl'")), rootDir); 67 | m_useProj = m_fallbackProj.get(); 68 | } else 69 | m_useProj = info.project; 70 | 71 | m_einfo.srcpath = m_info.args.front(); 72 | m_einfo.force = info.force; 73 | m_einfo.extractArgs.reserve(info.args.size()); 74 | auto it = info.args.cbegin(); 75 | ++it; 76 | for (; it != info.args.cend(); ++it) 77 | m_einfo.extractArgs.push_back(*it); 78 | 79 | m_specPasses.reserve(hecl::Database::DATA_SPEC_REGISTRY.size()); 80 | for (const hecl::Database::DataSpecEntry* entry : hecl::Database::DATA_SPEC_REGISTRY) { 81 | if (entry->m_factory) { 82 | auto ds = entry->m_factory(*m_useProj, hecl::Database::DataSpecTool::Extract); 83 | if (ds && ds->canExtract(m_einfo, m_reps)) 84 | m_specPasses.emplace_back(entry, std::move(ds)); 85 | } 86 | } 87 | } 88 | 89 | static void Help(HelpOutput& help) { 90 | help.secHead(_SYS_STR("NAME")); 91 | help.beginWrap(); 92 | help.wrap(_SYS_STR("hecl-extract - Extract objects from supported package/image formats\n")); 93 | help.endWrap(); 94 | 95 | help.secHead(_SYS_STR("SYNOPSIS")); 96 | help.beginWrap(); 97 | help.wrap(_SYS_STR("hecl extract [...]\n")); 98 | help.endWrap(); 99 | 100 | help.secHead(_SYS_STR("DESCRIPTION")); 101 | help.beginWrap(); 102 | help.wrap(_SYS_STR("This command recursively extracts all or part of a dataspec-supported ") 103 | _SYS_STR("package format. Each object is decoded to a working format and added to the project.\n\n")); 104 | help.endWrap(); 105 | 106 | help.secHead(_SYS_STR("OPTIONS")); 107 | help.optionHead(_SYS_STR("[/...]"), _SYS_STR("input file")); 108 | help.beginWrap(); 109 | help.wrap(_SYS_STR("Specifies the package file or disc image to source data from. ") 110 | _SYS_STR("An optional subnode specifies a named hierarchical-node specific ") 111 | _SYS_STR("to the game architecture (levels/areas).")); 112 | help.endWrap(); 113 | } 114 | 115 | hecl::SystemStringView toolName() const override { return _SYS_STR("extract"sv); } 116 | 117 | static void _recursivePrint(int level, hecl::Database::IDataSpec::ExtractReport& rep) { 118 | for (int l = 0; l < level; ++l) 119 | fmt::print(FMT_STRING(_SYS_STR(" "))); 120 | if (XTERM_COLOR) 121 | fmt::print(FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "")), rep.name); 122 | else 123 | fmt::print(FMT_STRING(_SYS_STR("{}")), rep.name); 124 | 125 | if (rep.desc.size()) 126 | fmt::print(FMT_STRING(_SYS_STR(" [{}]")), rep.desc); 127 | fmt::print(FMT_STRING(_SYS_STR("\n"))); 128 | for (hecl::Database::IDataSpec::ExtractReport& child : rep.childOpts) 129 | _recursivePrint(level + 1, child); 130 | } 131 | 132 | int run() override { 133 | if (m_specPasses.empty()) { 134 | if (XTERM_COLOR) 135 | fmt::print(FMT_STRING(_SYS_STR("" RED BOLD "NOTHING TO EXTRACT" NORMAL "\n"))); 136 | else 137 | fmt::print(FMT_STRING(_SYS_STR("NOTHING TO EXTRACT\n"))); 138 | return 1; 139 | } 140 | 141 | if (XTERM_COLOR) 142 | fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO EXTRACT:" NORMAL "\n"))); 143 | else 144 | fmt::print(FMT_STRING(_SYS_STR("ABOUT TO EXTRACT:\n"))); 145 | 146 | for (hecl::Database::IDataSpec::ExtractReport& rep : m_reps) { 147 | _recursivePrint(0, rep); 148 | fmt::print(FMT_STRING(_SYS_STR("\n"))); 149 | } 150 | fflush(stdout); 151 | 152 | if (continuePrompt()) { 153 | for (SpecExtractPass& ds : m_specPasses) { 154 | if (XTERM_COLOR) 155 | fmt::print(FMT_STRING(_SYS_STR("" MAGENTA BOLD "Using DataSpec {}:" NORMAL "\n")), ds.m_entry->m_name); 156 | else 157 | fmt::print(FMT_STRING(_SYS_STR("Using DataSpec {}:\n")), ds.m_entry->m_name); 158 | 159 | ds.m_instance->doExtract(m_einfo, {true}); 160 | fmt::print(FMT_STRING(_SYS_STR("\n\n"))); 161 | } 162 | } 163 | 164 | return 0; 165 | } 166 | }; 167 | -------------------------------------------------------------------------------- /driver/ToolHelp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolBase.hpp" 4 | #include 5 | #include 6 | 7 | class ToolHelp final : public ToolBase { 8 | 9 | public: 10 | explicit ToolHelp(const ToolPassInfo& info) : ToolBase(info) { 11 | if (m_info.args.empty()) { 12 | LogModule.report(logvisor::Error, FMT_STRING("help requires a tool name argument")); 13 | return; 14 | } 15 | m_good = true; 16 | } 17 | 18 | ~ToolHelp() override = default; 19 | 20 | static void Help(HelpOutput& help) { 21 | /* clang-format off */ 22 | help.printBold( 23 | _SYS_STR(" ___________ \n") 24 | _SYS_STR(" ,.-'\"...........``~., \n") 25 | _SYS_STR(" ,.-\".......................\"-., \n") 26 | _SYS_STR(" ,/..................................\":, \n") 27 | _SYS_STR(" .,?........................................, \n") 28 | _SYS_STR(" /...........................................,}\n") 29 | _SYS_STR(" ./........................................,:`^`..}\n") 30 | _SYS_STR(" ./.......................................,:\"...../\n") 31 | _SYS_STR(" ?.....__..................................:`...../\n") 32 | _SYS_STR(" /__.(...\"~-,_...........................,:`....../\n") 33 | _SYS_STR(" /(_....\"~,_....\"~,_.....................,:`...._/ \n") 34 | _SYS_STR(" {.._$;_....\"=,_.....\"-,_......,.-~-,},.~\";/....} \n") 35 | _SYS_STR(" ((...*~_......\"=-._...\";,,./`........../\"..../ \n") 36 | _SYS_STR(" ,,,___.`~,......\"~.,....................`......}....../ \n") 37 | _SYS_STR("............(....`=-,,...`.........................(...;_,,-\" \n") 38 | _SYS_STR("............/.`~,......`-.................................../ \n") 39 | _SYS_STR(".............`~.*-,.....................................|,./...,__ \n") 40 | _SYS_STR(",,_..........}.>-._...................................|.......`=~-, \n") 41 | _SYS_STR(".....`=~-,__......`,................................. \n") 42 | _SYS_STR("...................`=~-,,.,........................... \n") 43 | _SYS_STR(".........................`:,,..........................`\n") 44 | _SYS_STR("...........................`=-,...............,%%`>--==`` \n") 45 | _SYS_STR(".................................._.........._,-%%...` \n") 46 | _SYS_STR("...................................,\n")); 47 | /* clang-format on */ 48 | } 49 | 50 | static void ShowHelp(const hecl::SystemString& toolName) { 51 | /* Select tool's help-text streamer */ 52 | HelpOutput::HelpFunc helpFunc = nullptr; 53 | if (toolName == _SYS_STR("init")) 54 | helpFunc = ToolInit::Help; 55 | else if (toolName == _SYS_STR("spec")) 56 | helpFunc = ToolSpec::Help; 57 | else if (toolName == _SYS_STR("extract")) 58 | helpFunc = ToolExtract::Help; 59 | else if (toolName == _SYS_STR("cook")) 60 | helpFunc = ToolCook::Help; 61 | else if (toolName == _SYS_STR("package") || toolName == _SYS_STR("pack")) 62 | helpFunc = ToolPackage::Help; 63 | else if (toolName == _SYS_STR("help")) 64 | helpFunc = ToolHelp::Help; 65 | else { 66 | LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unrecognized tool '{}' - can't help")), toolName); 67 | return; 68 | } 69 | 70 | HelpOutput ho(helpFunc); 71 | ho.go(); 72 | } 73 | 74 | hecl::SystemStringView toolName() const override { return _SYS_STR("help"sv); } 75 | 76 | int run() override { 77 | ShowHelp(m_info.args.front()); 78 | return 0; 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /driver/ToolImage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if HECL_HAS_NOD 4 | 5 | #include 6 | #include 7 | #include "ToolBase.hpp" 8 | #include 9 | #include "nod/DiscGCN.hpp" 10 | #include "nod/DiscWii.hpp" 11 | #include "athena/FileReader.hpp" 12 | 13 | class ToolImage final : public ToolBase { 14 | std::unique_ptr m_fallbackProj; 15 | hecl::Database::Project* m_useProj; 16 | 17 | public: 18 | explicit ToolImage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) { 19 | if (!info.project) 20 | LogModule.report(logvisor::Fatal, FMT_STRING("hecl image must be ran within a project directory")); 21 | 22 | /* Scan args */ 23 | if (info.args.size()) { 24 | /* See if project path is supplied via args and use that over the getcwd one */ 25 | for (const hecl::SystemString& arg : info.args) { 26 | if (arg.empty()) 27 | continue; 28 | 29 | hecl::SystemString subPath; 30 | hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath); 31 | 32 | if (root) { 33 | if (!m_fallbackProj) { 34 | m_fallbackProj.reset(new hecl::Database::Project(root)); 35 | m_useProj = m_fallbackProj.get(); 36 | break; 37 | } 38 | } 39 | } 40 | } 41 | if (!m_useProj) 42 | LogModule.report(logvisor::Fatal, 43 | FMT_STRING("hecl image must be ran within a project directory or " 44 | "provided a path within a project")); 45 | } 46 | 47 | ~ToolImage() override = default; 48 | 49 | static void Help(HelpOutput& help) { 50 | help.secHead(_SYS_STR("NAME")); 51 | help.beginWrap(); 52 | help.wrap(_SYS_STR("hecl-image - Generate GameCube/Wii disc image from packaged files\n")); 53 | help.endWrap(); 54 | 55 | help.secHead(_SYS_STR("SYNOPSIS")); 56 | help.beginWrap(); 57 | help.wrap(_SYS_STR("hecl image []\n")); 58 | help.endWrap(); 59 | 60 | help.secHead(_SYS_STR("DESCRIPTION")); 61 | help.beginWrap(); 62 | help.wrap(_SYS_STR("This command uses the current contents of `out` to generate a GameCube or ") 63 | _SYS_STR("Wii disc image. `hecl package` must have been run previously to be effective.\n")); 64 | help.endWrap(); 65 | 66 | help.secHead(_SYS_STR("OPTIONS")); 67 | help.optionHead(_SYS_STR(""), _SYS_STR("input directory")); 68 | help.beginWrap(); 69 | help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting image from. ") 70 | _SYS_STR("Project must contain an out/sys and out/files directory to succeed.\n")); 71 | help.endWrap(); 72 | } 73 | 74 | hecl::SystemStringView toolName() const override { return _SYS_STR("image"sv); } 75 | 76 | int run() override { 77 | if (XTERM_COLOR) 78 | fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO IMAGE:" NORMAL "\n"))); 79 | else 80 | fmt::print(FMT_STRING(_SYS_STR("ABOUT TO IMAGE:\n"))); 81 | 82 | fmt::print(FMT_STRING(_SYS_STR(" {}\n")), m_useProj->getProjectRootPath().getAbsolutePath()); 83 | fflush(stdout); 84 | 85 | if (continuePrompt()) { 86 | hecl::ProjectPath outPath(m_useProj->getProjectWorkingPath(), _SYS_STR("out")); 87 | if (!outPath.isDirectory()) { 88 | LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("{} is not a directory")), outPath.getAbsolutePath()); 89 | return 1; 90 | } 91 | 92 | hecl::ProjectPath bootBinPath(outPath, _SYS_STR("sys/boot.bin")); 93 | if (!bootBinPath.isFile()) { 94 | LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("{} is not a file")), bootBinPath.getAbsolutePath()); 95 | return 1; 96 | } 97 | 98 | athena::io::FileReader r(bootBinPath.getAbsolutePath()); 99 | if (r.hasError()) { 100 | LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open {}")), bootBinPath.getAbsolutePath()); 101 | return 1; 102 | } 103 | std::string id = r.readString(6); 104 | r.close(); 105 | 106 | hecl::SystemStringConv idView(id); 107 | hecl::SystemString fileOut = hecl::SystemString(outPath.getAbsolutePath()) + _SYS_STR('/') + idView.c_str(); 108 | hecl::MultiProgressPrinter printer(true); 109 | auto progFunc = [&printer](float totalProg, nod::SystemStringView fileName, size_t fileBytesXfered) { 110 | printer.print(fileName.data(), nullptr, totalProg); 111 | }; 112 | if (id[0] == 'G') { 113 | fileOut += _SYS_STR(".gcm"); 114 | if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(outPath.getAbsolutePath()) == UINT64_MAX) 115 | return 1; 116 | LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Generating {} as GameCube image")), fileOut); 117 | nod::DiscBuilderGCN db(fileOut, progFunc); 118 | if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success) 119 | return 1; 120 | } else { 121 | fileOut += _SYS_STR(".iso"); 122 | bool dualLayer; 123 | if (nod::DiscBuilderWii::CalculateTotalSizeRequired(outPath.getAbsolutePath(), dualLayer) == UINT64_MAX) 124 | return 1; 125 | LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Generating {} as {}-layer Wii image")), fileOut, 126 | dualLayer ? _SYS_STR("dual") : _SYS_STR("single")); 127 | nod::DiscBuilderWii db(fileOut, dualLayer, progFunc); 128 | if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success) 129 | return 1; 130 | } 131 | } 132 | 133 | return 0; 134 | } 135 | }; 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /driver/ToolInit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolBase.hpp" 4 | #include 5 | 6 | class ToolInit final : public ToolBase { 7 | const hecl::SystemString* m_dir = nullptr; 8 | 9 | public: 10 | explicit ToolInit(const ToolPassInfo& info) : ToolBase(info) { 11 | hecl::Sstat theStat; 12 | const hecl::SystemString* dir; 13 | if (info.args.size()) 14 | dir = &info.args.front(); 15 | else 16 | dir = &info.cwd; 17 | 18 | if (hecl::Stat(dir->c_str(), &theStat)) { 19 | hecl::MakeDir(dir->c_str()); 20 | if (hecl::Stat(dir->c_str(), &theStat)) { 21 | LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to stat '{}'")), *dir); 22 | return; 23 | } 24 | } 25 | if (!S_ISDIR(theStat.st_mode)) { 26 | LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' is not a directory")), *dir); 27 | return; 28 | } 29 | 30 | hecl::SystemString testPath = *dir + _SYS_STR("/.hecl/beacon"); 31 | if (!hecl::Stat(testPath.c_str(), &theStat)) { 32 | LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("project already exists at '{}'")), *dir); 33 | return; 34 | } 35 | 36 | m_dir = dir; 37 | } 38 | 39 | int run() override { 40 | if (!m_dir) 41 | return 1; 42 | size_t ErrorRef = logvisor::ErrorCount; 43 | hecl::Database::Project proj((hecl::ProjectRootPath(*m_dir))); 44 | if (logvisor::ErrorCount > ErrorRef) 45 | return 1; 46 | LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("initialized project at '{}/.hecl'")), *m_dir); 47 | return 0; 48 | } 49 | 50 | static void Help(HelpOutput& help) { 51 | help.secHead(_SYS_STR("NAME")); 52 | help.beginWrap(); 53 | help.wrap(_SYS_STR("hecl-init - Initialize a brand-new project database\n")); 54 | help.endWrap(); 55 | 56 | help.secHead(_SYS_STR("SYNOPSIS")); 57 | help.beginWrap(); 58 | help.wrap(_SYS_STR("hecl init []\n")); 59 | help.endWrap(); 60 | 61 | help.secHead(_SYS_STR("DESCRIPTION")); 62 | help.beginWrap(); 63 | help.wrap(_SYS_STR("Creates a ")); 64 | help.wrapBold(_SYS_STR(".hecl")); 65 | help.wrap(_SYS_STR(" directory within the selected directory with an initialized database index. ") 66 | _SYS_STR("This constitutes an empty HECL project, ready for making stuff!!\n")); 67 | help.endWrap(); 68 | 69 | help.secHead(_SYS_STR("OPTIONS")); 70 | help.optionHead(_SYS_STR(""), _SYS_STR("group directory path")); 71 | help.beginWrap(); 72 | help.wrap(_SYS_STR("Directory to create new project database in. If not specified, current directory is used.\n")); 73 | help.endWrap(); 74 | } 75 | 76 | hecl::SystemStringView toolName() const override { return _SYS_STR("init"sv); } 77 | }; 78 | -------------------------------------------------------------------------------- /driver/ToolInstallAddon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolBase.hpp" 4 | #include 5 | 6 | class ToolInstallAddon final : public ToolBase { 7 | public: 8 | explicit ToolInstallAddon(const ToolPassInfo& info) : ToolBase(info) {} 9 | 10 | int run() override { 11 | hecl::blender::SharedBlenderToken.getBlenderConnection(); 12 | return 0; 13 | } 14 | 15 | static void Help(HelpOutput& help) { 16 | help.secHead(_SYS_STR("NAME")); 17 | help.beginWrap(); 18 | help.wrap(_SYS_STR("hecl-installaddon - Installs embedded Blender addon into local Blender\n")); 19 | help.endWrap(); 20 | 21 | help.secHead(_SYS_STR("SYNOPSIS")); 22 | help.beginWrap(); 23 | help.wrap(_SYS_STR("hecl installaddon\n")); 24 | help.endWrap(); 25 | 26 | help.secHead(_SYS_STR("DESCRIPTION")); 27 | help.beginWrap(); 28 | help.wrap(_SYS_STR("Installs the hecl Blender addon into Blender. The path to the blender executable ") 29 | _SYS_STR("can be overridden by setting the BLENDER_BIN environment variable.")); 30 | help.endWrap(); 31 | } 32 | 33 | hecl::SystemStringView toolName() const override { return _SYS_STR("installaddon"sv); } 34 | }; 35 | -------------------------------------------------------------------------------- /driver/ToolSpec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolBase.hpp" 4 | #include 5 | #include 6 | 7 | class ToolSpec final : public ToolBase { 8 | enum Mode { MLIST = 0, MENABLE, MDISABLE } mode = MLIST; 9 | 10 | public: 11 | explicit ToolSpec(const ToolPassInfo& info) : ToolBase(info) { 12 | if (info.args.empty()) 13 | return; 14 | 15 | if (!info.project) 16 | LogModule.report(logvisor::Fatal, FMT_STRING("hecl spec must be ran within a project directory")); 17 | 18 | const auto& specs = info.project->getDataSpecs(); 19 | hecl::SystemString firstArg = info.args.front(); 20 | hecl::ToLower(firstArg); 21 | 22 | if (firstArg == _SYS_STR("enable")) 23 | mode = MENABLE; 24 | else if (firstArg == _SYS_STR("disable")) 25 | mode = MDISABLE; 26 | else 27 | return; 28 | 29 | if (info.args.size() < 2) 30 | LogModule.report(logvisor::Fatal, FMT_STRING("Speclist argument required")); 31 | 32 | auto it = info.args.begin(); 33 | ++it; 34 | for (; it != info.args.end(); ++it) { 35 | 36 | bool found = false; 37 | for (auto& spec : specs) { 38 | if (!it->compare(spec.spec.m_name)) { 39 | found = true; 40 | break; 41 | } 42 | } 43 | if (!found) 44 | LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' is not found in the dataspec registry")), *it); 45 | } 46 | } 47 | 48 | static void Help(HelpOutput& help) { 49 | help.secHead(_SYS_STR("NAME")); 50 | help.beginWrap(); 51 | help.wrap(_SYS_STR("hecl-spec - Configure target data options\n")); 52 | help.endWrap(); 53 | 54 | help.secHead(_SYS_STR("SYNOPSIS")); 55 | help.beginWrap(); 56 | help.wrap(_SYS_STR("hecl spec [enable|disable] [...]\n")); 57 | help.endWrap(); 58 | 59 | help.secHead(_SYS_STR("DESCRIPTION")); 60 | help.beginWrap(); 61 | help.wrap( 62 | _SYS_STR("This command configures the HECL project with the user's preferred target DataSpecs.\n\n") 63 | _SYS_STR("Providing enable/disable argument will bulk-set the enable status of the provided spec(s)") 64 | _SYS_STR("list. If enable/disable is not provided, a list of supported DataSpecs is printed.\n\n")); 65 | help.endWrap(); 66 | 67 | help.secHead(_SYS_STR("OPTIONS")); 68 | help.optionHead(_SYS_STR("..."), _SYS_STR("DataSpec name(s)")); 69 | help.beginWrap(); 70 | help.wrap(_SYS_STR("Specifies platform-names to enable/disable")); 71 | help.endWrap(); 72 | } 73 | 74 | hecl::SystemStringView toolName() const override { return _SYS_STR("spec"sv); } 75 | 76 | int run() override { 77 | if (!m_info.project) { 78 | for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) { 79 | if (XTERM_COLOR) 80 | fmt::print(FMT_STRING(_SYS_STR("" BOLD CYAN "{}" NORMAL "\n")), spec->m_name); 81 | else 82 | fmt::print(FMT_STRING(_SYS_STR("{}\n")), spec->m_name); 83 | fmt::print(FMT_STRING(_SYS_STR(" {}\n")), spec->m_desc); 84 | } 85 | return 0; 86 | } 87 | 88 | const auto& specs = m_info.project->getDataSpecs(); 89 | if (mode == MLIST) { 90 | for (auto& spec : specs) { 91 | if (XTERM_COLOR) 92 | fmt::print(FMT_STRING(_SYS_STR("" BOLD CYAN "{}" NORMAL "")), spec.spec.m_name); 93 | else 94 | fmt::print(FMT_STRING(_SYS_STR("{}")), spec.spec.m_name); 95 | if (spec.active) { 96 | if (XTERM_COLOR) 97 | fmt::print(FMT_STRING(_SYS_STR(" " BOLD GREEN "[ENABLED]" NORMAL ""))); 98 | else 99 | fmt::print(FMT_STRING(_SYS_STR(" [ENABLED]"))); 100 | } 101 | fmt::print(FMT_STRING(_SYS_STR("\n {}\n")), spec.spec.m_desc); 102 | } 103 | return 0; 104 | } 105 | 106 | std::vector opSpecs; 107 | auto it = m_info.args.begin(); 108 | ++it; 109 | for (; it != m_info.args.end(); ++it) { 110 | hecl::SystemString itName = *it; 111 | hecl::ToLower(itName); 112 | for (auto& spec : specs) { 113 | hecl::SystemString compName(spec.spec.m_name); 114 | hecl::ToLower(compName); 115 | if (itName == compName) { 116 | opSpecs.emplace_back(spec.spec.m_name); 117 | break; 118 | } 119 | } 120 | } 121 | 122 | if (opSpecs.size()) { 123 | if (mode == MENABLE) 124 | m_info.project->enableDataSpecs(opSpecs); 125 | else if (mode == MDISABLE) 126 | m_info.project->disableDataSpecs(opSpecs); 127 | } 128 | 129 | return 0; 130 | } 131 | }; 132 | -------------------------------------------------------------------------------- /extern/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(boo) 2 | add_subdirectory(libSquish) 3 | add_subdirectory(athena) 4 | add_subdirectory(libpng) 5 | add_subdirectory(libjpeg-turbo) 6 | -------------------------------------------------------------------------------- /extern/libpng/ANNOUNCE: -------------------------------------------------------------------------------- 1 | Libpng 1.6.19 - November 12, 2015 2 | 3 | This is a public release of libpng, intended for use in production codes. 4 | 5 | Files available for download: 6 | 7 | Source files with LF line endings (for Unix/Linux) and with a 8 | "configure" script 9 | 10 | libpng-1.6.19.tar.xz (LZMA-compressed, recommended) 11 | libpng-1.6.19.tar.gz 12 | 13 | Source files with CRLF line endings (for Windows), without the 14 | "configure" script 15 | 16 | lpng1619.7z (LZMA-compressed, recommended) 17 | lpng1619.zip 18 | 19 | Other information: 20 | 21 | libpng-1.6.19-README.txt 22 | libpng-1.6.19-LICENSE.txt 23 | libpng-1.6.19-*.asc (armored detached GPG signatures) 24 | 25 | Changes since the last public release (1.6.18): 26 | 27 | Updated obsolete information about the simplified API macros in the 28 | manual pages (Bug report by Arc Riley). 29 | Avoid potentially dereferencing NULL info_ptr in png_info_init_3(). 30 | Rearranged png.h to put the major sections in the same order as 31 | in libpng17. 32 | Eliminated unused PNG_COST_SHIFT, PNG_WEIGHT_SHIFT, PNG_COST_FACTOR, and 33 | PNG_WEIGHT_FACTOR macros. 34 | Suppressed some warnings from the Borland C++ 5.5.1/5.82 compiler 35 | (Bug report by Viktor Szakats). Several warnings remain and are 36 | unavoidable, where we test for overflow. 37 | Fixed potential leak of png_pixels in contrib/pngminus/pnm2png.c 38 | Fixed uninitialized variable in contrib/gregbook/rpng2-x.c 39 | Moved config.h.in~ from the "libpng_autotools_files" list to the 40 | "libpng_autotools_extra" list in autogen.sh because it was causing a 41 | false positive for missing files (bug report by Robert C. Seacord). 42 | Removed unreachable "break" statements in png.c, pngread.c, and pngrtran.c 43 | to suppress clang warnings (Bug report by Viktor Szakats). 44 | Fixed some bad links in the man page. 45 | Changed "n bit" to "n-bit" in comments. 46 | Added signed/unsigned 16-bit safety net. This removes the dubious 47 | 0x8000 flag definitions on 16-bit systems. They aren't supported 48 | yet the defs *probably* work, however it seems much safer to do this 49 | and be advised if anyone, contrary to advice, is building libpng 1.6 50 | on a 16-bit system. It also adds back various switch default clauses 51 | for GCC; GCC errors out if they are not present (with an appropriately 52 | high level of warnings). 53 | Safely convert num_bytes to a png_byte in png_set_sig_bytes() (Robert 54 | Seacord). 55 | Fixed the recently reported 1's complement security issue by replacing 56 | the value that is illegal in the PNG spec, in both signed and unsigned 57 | values, with 0. Illegal unsigned values (anything greater than or equal 58 | to 0x80000000) can still pass through, but since these are not illegal 59 | in ANSI-C (unlike 0x80000000 in the signed case) the checking that 60 | occurs later can catch them (John Bowler). 61 | Fixed png_save_int_32 when int is not 2's complement (John Bowler). 62 | Updated libpng16 with all the recent test changes from libpng17, 63 | including changes to pngvalid.c to ensure that the original, 64 | distributed, version of contrib/visupng/cexcept.h can be used 65 | (John Bowler). 66 | pngvalid contains the correction to the use of SAVE/STORE_ 67 | UNKNOWN_CHUNKS; a bug revealed by changes in libpng 1.7. More 68 | tests contain the --strict option to detect warnings and the 69 | pngvalid-standard test has been corrected so that it does not 70 | turn on progressive-read. There is a separate test which does 71 | that. (John Bowler) 72 | Also made some signed/unsigned fixes. 73 | Make pngstest error limits version specific. Splitting the machine 74 | generated error structs out to a file allows the values to be updated 75 | without changing pngstest.c itself. Since libpng 1.6 and 1.7 have 76 | slightly different error limits this simplifies maintenance. The 77 | makepngs.sh script has also been updated to more accurately reflect 78 | current problems in libpng 1.7 (John Bowler). 79 | Incorporated new test PNG files into make check. tests/pngstest-* 80 | are changed so that the new test files are divided into 8 groups by 81 | gamma and alpha channel. These tests have considerably better code 82 | and pixel-value coverage than contrib/pngsuite; however,coverage is 83 | still incomplete (John Bowler). 84 | Removed the '--strict' in 1.6 because of the double-gamma-correction 85 | warning, updated pngstest-errors.h for the errors detected with the 86 | new contrib/testspngs PNG test files (John Bowler). 87 | Worked around rgb-to-gray issues in libpng 1.6. The previous 88 | attempts to ignore the errors in the code aren't quite enough to 89 | deal with the 'channel selection' encoding added to libpng 1.7; abort. 90 | Fixed 'pow' macros in pngvalid.c. It is legal for 'pow' to be a 91 | macro, therefore the argument list cannot contain preprocessing 92 | directives. Make sure pow is a function where this happens. This is 93 | a minimal safe fix, the issue only arises in non-performance-critical 94 | code (bug report by Curtis Leach, fix by John Bowler). 95 | Added sPLT support to pngtest.c 96 | Prevent setting or writing over-length PLTE chunk (Cosmin Truta). 97 | Silently truncate over-length PLTE chunk while reading. 98 | Libpng incorrectly calculated the output rowbytes when the application 99 | decreased either the number of channels or the bit depth (or both) in 100 | a user transform. This was safe; libpng overallocated buffer space 101 | (potentially by quite a lot; up to 4 times the amount required) but, 102 | from 1.5.4 on, resulted in a png_error (John Bowler). 103 | Fixed some inconsequential cut-and-paste typos in png_set_cHRM_XYZ_fixed(). 104 | Clarified COPYRIGHT information to state explicitly that versions 105 | are derived from previous versions. 106 | Removed much of the long list of previous versions from png.h and 107 | libpng.3. 108 | 109 | Send comments/corrections/commendations to png-mng-implement at lists.sf.net 110 | (subscription required; visit 111 | https://lists.sourceforge.net/lists/listinfo/png-mng-implement 112 | to subscribe) 113 | or to glennrp at users.sourceforge.net 114 | 115 | Glenn R-P 116 | -------------------------------------------------------------------------------- /extern/libpng/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT WIN32 AND NOT APPLE AND NOT NX) # remove WIN32 when specter/freetype is gone 2 | find_library(PNG_LIB NAMES png libpng) 3 | endif() 4 | if(NOT PNG_LIB) 5 | message(STATUS "Using HECL's built-in libpng") 6 | if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm(64)?") 7 | set(INTRINSICS 8 | arm/arm_init.c 9 | arm/filter_neon.S 10 | arm/filter_neon_intrinsics.c 11 | arm/palette_neon_intrinsics.c) 12 | elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL x86_64) 13 | set(INTRINSICS 14 | intel/filter_sse2_intrinsics.c 15 | intel/intel_init.c) 16 | endif() 17 | add_library(png 18 | png.h 19 | pngconf.h 20 | pngdebug.h 21 | pnginfo.h 22 | pngpriv.h 23 | pngstruct.h 24 | pnglibconf.h 25 | 26 | png.c 27 | pngerror.c 28 | pngget.c 29 | pngmem.c 30 | pngpread.c 31 | pngread.c 32 | pngrio.c 33 | pngrtran.c 34 | pngrutil.c 35 | pngset.c 36 | pngtrans.c 37 | pngwio.c 38 | pngwrite.c 39 | pngwtran.c 40 | pngwutil.c 41 | ${INTRINSICS}) 42 | if(APPLE) 43 | target_compile_options(png PRIVATE -Wno-implicit-fallthrough) 44 | endif() 45 | target_link_libraries(png PUBLIC ${ZLIB_LIBRARIES}) 46 | target_include_directories(png PUBLIC ${ZLIB_INCLUDE_DIR}) 47 | set(PNG_LIBRARIES png CACHE PATH "PNG libraries" FORCE) 48 | set(PNG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "PNG include path" FORCE) 49 | else() 50 | set(PNG_LIBRARIES ${PNG_LIB} CACHE PATH "PNG libraries" FORCE) 51 | find_path(PNG_INCLUDE_DIR png.h) 52 | set(PNG_INCLUDE_DIR ${PNG_INCLUDE_DIR} CACHE PATH "PNG include path" FORCE) 53 | endif() 54 | -------------------------------------------------------------------------------- /extern/libpng/TODO: -------------------------------------------------------------------------------- 1 | /* 2 | TODO - list of things to do for libpng: 3 | 4 | Final bug fixes. 5 | Better C++ wrapper/full C++ implementation? 6 | Fix problem with C++ and EXTERN "C". 7 | cHRM transformation. 8 | Remove setjmp/longjmp usage in favor of returning error codes. 9 | Palette creation. 10 | Add "grayscale->palette" transformation and "palette->grayscale" detection. 11 | Improved dithering. 12 | Multi-lingual error and warning message support. 13 | Complete sRGB transformation (presently it simply uses gamma=0.45455). 14 | Make profile checking optional via a png_set_something() call. 15 | Man pages for function calls. 16 | Better documentation. 17 | Better filter selection 18 | (counting huffman bits/precompression? filter inertia? filter costs?). 19 | Histogram creation. 20 | Text conversion between different code pages (Latin-1 -> Mac and DOS). 21 | Avoid building gamma tables whenever possible. 22 | Use greater precision when changing to linear gamma for compositing against 23 | background and doing rgb-to-gray transformation. 24 | Investigate pre-incremented loop counters and other loop constructions. 25 | Add interpolated method of handling interlacing. 26 | Switch to the simpler zlib (zlib/libpng) license if legally possible. 27 | Extend pngvalid.c to validate more of the libpng transformations. 28 | 29 | */ 30 | -------------------------------------------------------------------------------- /extern/libpng/arm/arm_init.c: -------------------------------------------------------------------------------- 1 | 2 | /* arm_init.c - NEON optimised filter functions 3 | * 4 | * Copyright (c) 2018 Cosmin Truta 5 | * Copyright (c) 2014,2016 Glenn Randers-Pehrson 6 | * Written by Mans Rullgard, 2011. 7 | * 8 | * This code is released under the libpng license. 9 | * For conditions of distribution and use, see the disclaimer 10 | * and license in png.h 11 | */ 12 | 13 | /* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are 14 | * called. 15 | */ 16 | #define _POSIX_SOURCE 1 17 | 18 | #include "../pngpriv.h" 19 | 20 | #ifdef PNG_READ_SUPPORTED 21 | 22 | #if PNG_ARM_NEON_OPT > 0 23 | #ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */ 24 | /* WARNING: it is strongly recommended that you do not build libpng with 25 | * run-time checks for CPU features if at all possible. In the case of the ARM 26 | * NEON instructions there is no processor-specific way of detecting the 27 | * presence of the required support, therefore run-time detection is extremely 28 | * OS specific. 29 | * 30 | * You may set the macro PNG_ARM_NEON_FILE to the file name of file containing 31 | * a fragment of C source code which defines the png_have_neon function. There 32 | * are a number of implementations in contrib/arm-neon, but the only one that 33 | * has partial support is contrib/arm-neon/linux.c - a generic Linux 34 | * implementation which reads /proc/cpufino. 35 | */ 36 | #ifndef PNG_ARM_NEON_FILE 37 | # ifdef __linux__ 38 | # define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c" 39 | # endif 40 | #endif 41 | 42 | #ifdef PNG_ARM_NEON_FILE 43 | 44 | #include /* for sig_atomic_t */ 45 | static int png_have_neon(png_structp png_ptr); 46 | #include PNG_ARM_NEON_FILE 47 | 48 | #else /* PNG_ARM_NEON_FILE */ 49 | # error "PNG_ARM_NEON_FILE undefined: no support for run-time ARM NEON checks" 50 | #endif /* PNG_ARM_NEON_FILE */ 51 | #endif /* PNG_ARM_NEON_CHECK_SUPPORTED */ 52 | 53 | #ifndef PNG_ALIGNED_MEMORY_SUPPORTED 54 | # error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED" 55 | #endif 56 | 57 | void 58 | png_init_filter_functions_neon(png_structp pp, unsigned int bpp) 59 | { 60 | /* The switch statement is compiled in for ARM_NEON_API, the call to 61 | * png_have_neon is compiled in for ARM_NEON_CHECK. If both are defined 62 | * the check is only performed if the API has not set the NEON option on 63 | * or off explicitly. In this case the check controls what happens. 64 | * 65 | * If the CHECK is not compiled in and the option is UNSET the behavior prior 66 | * to 1.6.7 was to use the NEON code - this was a bug caused by having the 67 | * wrong order of the 'ON' and 'default' cases. UNSET now defaults to OFF, 68 | * as documented in png.h 69 | */ 70 | png_debug(1, "in png_init_filter_functions_neon"); 71 | #ifdef PNG_ARM_NEON_API_SUPPORTED 72 | switch ((pp->options >> PNG_ARM_NEON) & 3) 73 | { 74 | case PNG_OPTION_UNSET: 75 | /* Allow the run-time check to execute if it has been enabled - 76 | * thus both API and CHECK can be turned on. If it isn't supported 77 | * this case will fall through to the 'default' below, which just 78 | * returns. 79 | */ 80 | #endif /* PNG_ARM_NEON_API_SUPPORTED */ 81 | #ifdef PNG_ARM_NEON_CHECK_SUPPORTED 82 | { 83 | static volatile sig_atomic_t no_neon = -1; /* not checked */ 84 | 85 | if (no_neon < 0) 86 | no_neon = !png_have_neon(pp); 87 | 88 | if (no_neon) 89 | return; 90 | } 91 | #ifdef PNG_ARM_NEON_API_SUPPORTED 92 | break; 93 | #endif 94 | #endif /* PNG_ARM_NEON_CHECK_SUPPORTED */ 95 | 96 | #ifdef PNG_ARM_NEON_API_SUPPORTED 97 | default: /* OFF or INVALID */ 98 | return; 99 | 100 | case PNG_OPTION_ON: 101 | /* Option turned on */ 102 | break; 103 | } 104 | #endif 105 | 106 | /* IMPORTANT: any new external functions used here must be declared using 107 | * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the 108 | * 'prefix' option to configure works: 109 | * 110 | * ./configure --with-libpng-prefix=foobar_ 111 | * 112 | * Verify you have got this right by running the above command, doing a build 113 | * and examining pngprefix.h; it must contain a #define for every external 114 | * function you add. (Notice that this happens automatically for the 115 | * initialization function.) 116 | */ 117 | pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon; 118 | 119 | if (bpp == 3) 120 | { 121 | pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon; 122 | pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon; 123 | pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = 124 | png_read_filter_row_paeth3_neon; 125 | } 126 | 127 | else if (bpp == 4) 128 | { 129 | pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon; 130 | pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon; 131 | pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = 132 | png_read_filter_row_paeth4_neon; 133 | } 134 | } 135 | #endif /* PNG_ARM_NEON_OPT > 0 */ 136 | #endif /* READ */ 137 | -------------------------------------------------------------------------------- /extern/libpng/arm/palette_neon_intrinsics.c: -------------------------------------------------------------------------------- 1 | 2 | /* palette_neon_intrinsics.c - NEON optimised palette expansion functions 3 | * 4 | * Copyright (c) 2018-2019 Cosmin Truta 5 | * Copyright (c) 2017-2018 Arm Holdings. All rights reserved. 6 | * Written by Richard Townsend , February 2017. 7 | * 8 | * This code is released under the libpng license. 9 | * For conditions of distribution and use, see the disclaimer 10 | * and license in png.h 11 | */ 12 | 13 | #include "../pngpriv.h" 14 | 15 | #if PNG_ARM_NEON_IMPLEMENTATION == 1 16 | 17 | #if defined(_MSC_VER) && defined(_M_ARM64) 18 | # include 19 | #else 20 | # include 21 | #endif 22 | 23 | /* Build an RGBA8 palette from the separate RGB and alpha palettes. */ 24 | void 25 | png_riffle_palette_neon(png_structrp png_ptr) 26 | { 27 | png_const_colorp palette = png_ptr->palette; 28 | png_bytep riffled_palette = png_ptr->riffled_palette; 29 | png_const_bytep trans_alpha = png_ptr->trans_alpha; 30 | int num_trans = png_ptr->num_trans; 31 | int i; 32 | 33 | png_debug(1, "in png_riffle_palette_neon"); 34 | 35 | /* Initially black, opaque. */ 36 | uint8x16x4_t w = {{ 37 | vdupq_n_u8(0x00), 38 | vdupq_n_u8(0x00), 39 | vdupq_n_u8(0x00), 40 | vdupq_n_u8(0xff), 41 | }}; 42 | 43 | /* First, riffle the RGB colours into an RGBA8 palette. 44 | * The alpha component is set to opaque for now. 45 | */ 46 | for (i = 0; i < 256; i += 16) 47 | { 48 | uint8x16x3_t v = vld3q_u8((png_const_bytep)(palette + i)); 49 | w.val[0] = v.val[0]; 50 | w.val[1] = v.val[1]; 51 | w.val[2] = v.val[2]; 52 | vst4q_u8(riffled_palette + (i << 2), w); 53 | } 54 | 55 | /* Fix up the missing transparency values. */ 56 | for (i = 0; i < num_trans; i++) 57 | riffled_palette[(i << 2) + 3] = trans_alpha[i]; 58 | } 59 | 60 | /* Expands a palettized row into RGBA8. */ 61 | int 62 | png_do_expand_palette_rgba8_neon(png_structrp png_ptr, png_row_infop row_info, 63 | png_const_bytep row, png_bytepp ssp, png_bytepp ddp) 64 | { 65 | png_uint_32 row_width = row_info->width; 66 | const png_uint_32 *riffled_palette = 67 | (const png_uint_32 *)png_ptr->riffled_palette; 68 | const png_int_32 pixels_per_chunk = 4; 69 | int i; 70 | 71 | png_debug(1, "in png_do_expand_palette_rgba8_neon"); 72 | 73 | if (row_width < pixels_per_chunk) 74 | return 0; 75 | 76 | /* This function originally gets the last byte of the output row. 77 | * The NEON part writes forward from a given position, so we have 78 | * to seek this back by 4 pixels x 4 bytes. 79 | */ 80 | *ddp = *ddp - ((pixels_per_chunk * sizeof(png_uint_32)) - 1); 81 | 82 | for (i = 0; i < row_width; i += pixels_per_chunk) 83 | { 84 | uint32x4_t cur; 85 | png_bytep sp = *ssp - i, dp = *ddp - (i << 2); 86 | cur = vld1q_dup_u32 (riffled_palette + *(sp - 3)); 87 | cur = vld1q_lane_u32(riffled_palette + *(sp - 2), cur, 1); 88 | cur = vld1q_lane_u32(riffled_palette + *(sp - 1), cur, 2); 89 | cur = vld1q_lane_u32(riffled_palette + *(sp - 0), cur, 3); 90 | vst1q_u32((void *)dp, cur); 91 | } 92 | if (i != row_width) 93 | { 94 | /* Remove the amount that wasn't processed. */ 95 | i -= pixels_per_chunk; 96 | } 97 | 98 | /* Decrement output pointers. */ 99 | *ssp = *ssp - i; 100 | *ddp = *ddp - (i << 2); 101 | return i; 102 | } 103 | 104 | /* Expands a palettized row into RGB8. */ 105 | int 106 | png_do_expand_palette_rgb8_neon(png_structrp png_ptr, png_row_infop row_info, 107 | png_const_bytep row, png_bytepp ssp, png_bytepp ddp) 108 | { 109 | png_uint_32 row_width = row_info->width; 110 | png_const_bytep palette = (png_const_bytep)png_ptr->palette; 111 | const png_uint_32 pixels_per_chunk = 8; 112 | int i; 113 | 114 | png_debug(1, "in png_do_expand_palette_rgb8_neon"); 115 | 116 | if (row_width <= pixels_per_chunk) 117 | return 0; 118 | 119 | /* Seeking this back by 8 pixels x 3 bytes. */ 120 | *ddp = *ddp - ((pixels_per_chunk * sizeof(png_color)) - 1); 121 | 122 | for (i = 0; i < row_width; i += pixels_per_chunk) 123 | { 124 | uint8x8x3_t cur; 125 | png_bytep sp = *ssp - i, dp = *ddp - ((i << 1) + i); 126 | cur = vld3_dup_u8(palette + sizeof(png_color) * (*(sp - 7))); 127 | cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 6)), cur, 1); 128 | cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 5)), cur, 2); 129 | cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 4)), cur, 3); 130 | cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 3)), cur, 4); 131 | cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 2)), cur, 5); 132 | cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 1)), cur, 6); 133 | cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 0)), cur, 7); 134 | vst3_u8((void *)dp, cur); 135 | } 136 | 137 | if (i != row_width) 138 | { 139 | /* Remove the amount that wasn't processed. */ 140 | i -= pixels_per_chunk; 141 | } 142 | 143 | /* Decrement output pointers. */ 144 | *ssp = *ssp - i; 145 | *ddp = *ddp - ((i << 1) + i); 146 | return i; 147 | } 148 | 149 | #endif /* PNG_ARM_NEON_IMPLEMENTATION */ 150 | -------------------------------------------------------------------------------- /extern/libpng/intel/intel_init.c: -------------------------------------------------------------------------------- 1 | 2 | /* intel_init.c - SSE2 optimized filter functions 3 | * 4 | * Copyright (c) 2018 Cosmin Truta 5 | * Copyright (c) 2016-2017 Glenn Randers-Pehrson 6 | * Written by Mike Klein and Matt Sarett, Google, Inc. 7 | * Derived from arm/arm_init.c 8 | * 9 | * This code is released under the libpng license. 10 | * For conditions of distribution and use, see the disclaimer 11 | * and license in png.h 12 | */ 13 | 14 | #include "../pngpriv.h" 15 | 16 | #ifdef PNG_READ_SUPPORTED 17 | #if PNG_INTEL_SSE_IMPLEMENTATION > 0 18 | 19 | void 20 | png_init_filter_functions_sse2(png_structp pp, unsigned int bpp) 21 | { 22 | /* The techniques used to implement each of these filters in SSE operate on 23 | * one pixel at a time. 24 | * So they generally speed up 3bpp images about 3x, 4bpp images about 4x. 25 | * They can scale up to 6 and 8 bpp images and down to 2 bpp images, 26 | * but they'd not likely have any benefit for 1bpp images. 27 | * Most of these can be implemented using only MMX and 64-bit registers, 28 | * but they end up a bit slower than using the equally-ubiquitous SSE2. 29 | */ 30 | png_debug(1, "in png_init_filter_functions_sse2"); 31 | if (bpp == 3) 32 | { 33 | pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2; 34 | pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2; 35 | pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = 36 | png_read_filter_row_paeth3_sse2; 37 | } 38 | else if (bpp == 4) 39 | { 40 | pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2; 41 | pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2; 42 | pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = 43 | png_read_filter_row_paeth4_sse2; 44 | } 45 | 46 | /* No need optimize PNG_FILTER_VALUE_UP. The compiler should 47 | * autovectorize. 48 | */ 49 | } 50 | 51 | #endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */ 52 | #endif /* PNG_READ_SUPPORTED */ 53 | -------------------------------------------------------------------------------- /extern/libpng/pngdebug.h: -------------------------------------------------------------------------------- 1 | 2 | /* pngdebug.h - Debugging macros for libpng, also used in pngtest.c 3 | * 4 | * Copyright (c) 2018 Cosmin Truta 5 | * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson 6 | * Copyright (c) 1996-1997 Andreas Dilger 7 | * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. 8 | * 9 | * This code is released under the libpng license. 10 | * For conditions of distribution and use, see the disclaimer 11 | * and license in png.h 12 | */ 13 | 14 | /* Define PNG_DEBUG at compile time for debugging information. Higher 15 | * numbers for PNG_DEBUG mean more debugging information. This has 16 | * only been added since version 0.95 so it is not implemented throughout 17 | * libpng yet, but more support will be added as needed. 18 | * 19 | * png_debug[1-2]?(level, message ,arg{0-2}) 20 | * Expands to a statement (either a simple expression or a compound 21 | * do..while(0) statement) that outputs a message with parameter 22 | * substitution if PNG_DEBUG is defined to 2 or more. If PNG_DEBUG 23 | * is undefined, 0 or 1 every png_debug expands to a simple expression 24 | * (actually ((void)0)). 25 | * 26 | * level: level of detail of message, starting at 0. A level 'n' 27 | * message is preceded by 'n' 3-space indentations (not implemented 28 | * on Microsoft compilers unless PNG_DEBUG_FILE is also 29 | * defined, to allow debug DLL compilation with no standard IO). 30 | * message: a printf(3) style text string. A trailing '\n' is added 31 | * to the message. 32 | * arg: 0 to 2 arguments for printf(3) style substitution in message. 33 | */ 34 | #ifndef PNGDEBUG_H 35 | #define PNGDEBUG_H 36 | /* These settings control the formatting of messages in png.c and pngerror.c */ 37 | /* Moved to pngdebug.h at 1.5.0 */ 38 | # ifndef PNG_LITERAL_SHARP 39 | # define PNG_LITERAL_SHARP 0x23 40 | # endif 41 | # ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET 42 | # define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b 43 | # endif 44 | # ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET 45 | # define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d 46 | # endif 47 | # ifndef PNG_STRING_NEWLINE 48 | # define PNG_STRING_NEWLINE "\n" 49 | # endif 50 | 51 | #ifdef PNG_DEBUG 52 | # if (PNG_DEBUG > 0) 53 | # if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) 54 | # include 55 | # if (PNG_DEBUG > 1) 56 | # ifndef _DEBUG 57 | # define _DEBUG 58 | # endif 59 | # ifndef png_debug 60 | # define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) 61 | # endif 62 | # ifndef png_debug1 63 | # define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) 64 | # endif 65 | # ifndef png_debug2 66 | # define png_debug2(l,m,p1,p2) \ 67 | _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) 68 | # endif 69 | # endif 70 | # else /* PNG_DEBUG_FILE || !_MSC_VER */ 71 | # ifndef PNG_STDIO_SUPPORTED 72 | # include /* not included yet */ 73 | # endif 74 | # ifndef PNG_DEBUG_FILE 75 | # define PNG_DEBUG_FILE stderr 76 | # endif /* PNG_DEBUG_FILE */ 77 | 78 | # if (PNG_DEBUG > 1) 79 | # ifdef __STDC__ 80 | # ifndef png_debug 81 | # define png_debug(l,m) \ 82 | do { \ 83 | int num_tabs=l; \ 84 | fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \ 85 | (num_tabs==2 ? " " : (num_tabs>2 ? " " : "")))); \ 86 | } while (0) 87 | # endif 88 | # ifndef png_debug1 89 | # define png_debug1(l,m,p1) \ 90 | do { \ 91 | int num_tabs=l; \ 92 | fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \ 93 | (num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1); \ 94 | } while (0) 95 | # endif 96 | # ifndef png_debug2 97 | # define png_debug2(l,m,p1,p2) \ 98 | do { \ 99 | int num_tabs=l; \ 100 | fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \ 101 | (num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1,p2);\ 102 | } while (0) 103 | # endif 104 | # else /* __STDC __ */ 105 | # ifndef png_debug 106 | # define png_debug(l,m) \ 107 | do { \ 108 | int num_tabs=l; \ 109 | char format[256]; \ 110 | snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ 111 | (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ 112 | m,PNG_STRING_NEWLINE); \ 113 | fprintf(PNG_DEBUG_FILE,format); \ 114 | } while (0) 115 | # endif 116 | # ifndef png_debug1 117 | # define png_debug1(l,m,p1) \ 118 | do { \ 119 | int num_tabs=l; \ 120 | char format[256]; \ 121 | snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ 122 | (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ 123 | m,PNG_STRING_NEWLINE); \ 124 | fprintf(PNG_DEBUG_FILE,format,p1); \ 125 | } while (0) 126 | # endif 127 | # ifndef png_debug2 128 | # define png_debug2(l,m,p1,p2) \ 129 | do { \ 130 | int num_tabs=l; \ 131 | char format[256]; \ 132 | snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ 133 | (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ 134 | m,PNG_STRING_NEWLINE); \ 135 | fprintf(PNG_DEBUG_FILE,format,p1,p2); \ 136 | } while (0) 137 | # endif 138 | # endif /* __STDC __ */ 139 | # endif /* (PNG_DEBUG > 1) */ 140 | 141 | # endif /* _MSC_VER */ 142 | # endif /* (PNG_DEBUG > 0) */ 143 | #endif /* PNG_DEBUG */ 144 | #ifndef png_debug 145 | # define png_debug(l, m) ((void)0) 146 | #endif 147 | #ifndef png_debug1 148 | # define png_debug1(l, m, p1) ((void)0) 149 | #endif 150 | #ifndef png_debug2 151 | # define png_debug2(l, m, p1, p2) ((void)0) 152 | #endif 153 | #endif /* PNGDEBUG_H */ 154 | -------------------------------------------------------------------------------- /extern/libpng/pngrio.c: -------------------------------------------------------------------------------- 1 | 2 | /* pngrio.c - functions for data input 3 | * 4 | * Copyright (c) 2018 Cosmin Truta 5 | * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson 6 | * Copyright (c) 1996-1997 Andreas Dilger 7 | * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. 8 | * 9 | * This code is released under the libpng license. 10 | * For conditions of distribution and use, see the disclaimer 11 | * and license in png.h 12 | * 13 | * This file provides a location for all input. Users who need 14 | * special handling are expected to write a function that has the same 15 | * arguments as this and performs a similar function, but that possibly 16 | * has a different input method. Note that you shouldn't change this 17 | * function, but rather write a replacement function and then make 18 | * libpng use it at run time with png_set_read_fn(...). 19 | */ 20 | 21 | #include "pngpriv.h" 22 | 23 | #ifdef PNG_READ_SUPPORTED 24 | 25 | /* Read the data from whatever input you are using. The default routine 26 | * reads from a file pointer. Note that this routine sometimes gets called 27 | * with very small lengths, so you should implement some kind of simple 28 | * buffering if you are using unbuffered reads. This should never be asked 29 | * to read more than 64K on a 16-bit machine. 30 | */ 31 | void /* PRIVATE */ 32 | png_read_data(png_structrp png_ptr, png_bytep data, size_t length) 33 | { 34 | png_debug1(4, "reading %d bytes", (int)length); 35 | 36 | if (png_ptr->read_data_fn != NULL) 37 | (*(png_ptr->read_data_fn))(png_ptr, data, length); 38 | 39 | else 40 | png_error(png_ptr, "Call to NULL read function"); 41 | } 42 | 43 | #ifdef PNG_STDIO_SUPPORTED 44 | /* This is the function that does the actual reading of data. If you are 45 | * not reading from a standard C stream, you should create a replacement 46 | * read_data function and use it at run time with png_set_read_fn(), rather 47 | * than changing the library. 48 | */ 49 | void PNGCBAPI 50 | png_default_read_data(png_structp png_ptr, png_bytep data, size_t length) 51 | { 52 | size_t check; 53 | 54 | if (png_ptr == NULL) 55 | return; 56 | 57 | /* fread() returns 0 on error, so it is OK to store this in a size_t 58 | * instead of an int, which is what fread() actually returns. 59 | */ 60 | check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); 61 | 62 | if (check != length) 63 | png_error(png_ptr, "Read Error"); 64 | } 65 | #endif 66 | 67 | /* This function allows the application to supply a new input function 68 | * for libpng if standard C streams aren't being used. 69 | * 70 | * This function takes as its arguments: 71 | * 72 | * png_ptr - pointer to a png input data structure 73 | * 74 | * io_ptr - pointer to user supplied structure containing info about 75 | * the input functions. May be NULL. 76 | * 77 | * read_data_fn - pointer to a new input function that takes as its 78 | * arguments a pointer to a png_struct, a pointer to 79 | * a location where input data can be stored, and a 32-bit 80 | * unsigned int that is the number of bytes to be read. 81 | * To exit and output any fatal error messages the new write 82 | * function should call png_error(png_ptr, "Error msg"). 83 | * May be NULL, in which case libpng's default function will 84 | * be used. 85 | */ 86 | void PNGAPI 87 | png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, 88 | png_rw_ptr read_data_fn) 89 | { 90 | if (png_ptr == NULL) 91 | return; 92 | 93 | png_ptr->io_ptr = io_ptr; 94 | 95 | #ifdef PNG_STDIO_SUPPORTED 96 | if (read_data_fn != NULL) 97 | png_ptr->read_data_fn = read_data_fn; 98 | 99 | else 100 | png_ptr->read_data_fn = png_default_read_data; 101 | #else 102 | png_ptr->read_data_fn = read_data_fn; 103 | #endif 104 | 105 | #ifdef PNG_WRITE_SUPPORTED 106 | /* It is an error to write to a read device */ 107 | if (png_ptr->write_data_fn != NULL) 108 | { 109 | png_ptr->write_data_fn = NULL; 110 | png_warning(png_ptr, 111 | "Can't set both read_data_fn and write_data_fn in the" 112 | " same structure"); 113 | } 114 | #endif 115 | 116 | #ifdef PNG_WRITE_FLUSH_SUPPORTED 117 | png_ptr->output_flush_fn = NULL; 118 | #endif 119 | } 120 | #endif /* READ */ 121 | -------------------------------------------------------------------------------- /extern/libpng/pngwio.c: -------------------------------------------------------------------------------- 1 | 2 | /* pngwio.c - functions for data output 3 | * 4 | * Copyright (c) 2018 Cosmin Truta 5 | * Copyright (c) 1998-2002,2004,2006-2014,2016,2018 Glenn Randers-Pehrson 6 | * Copyright (c) 1996-1997 Andreas Dilger 7 | * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. 8 | * 9 | * This code is released under the libpng license. 10 | * For conditions of distribution and use, see the disclaimer 11 | * and license in png.h 12 | * 13 | * This file provides a location for all output. Users who need 14 | * special handling are expected to write functions that have the same 15 | * arguments as these and perform similar functions, but that possibly 16 | * use different output methods. Note that you shouldn't change these 17 | * functions, but rather write replacement functions and then change 18 | * them at run time with png_set_write_fn(...). 19 | */ 20 | 21 | #include "pngpriv.h" 22 | 23 | #ifdef PNG_WRITE_SUPPORTED 24 | 25 | /* Write the data to whatever output you are using. The default routine 26 | * writes to a file pointer. Note that this routine sometimes gets called 27 | * with very small lengths, so you should implement some kind of simple 28 | * buffering if you are using unbuffered writes. This should never be asked 29 | * to write more than 64K on a 16-bit machine. 30 | */ 31 | 32 | void /* PRIVATE */ 33 | png_write_data(png_structrp png_ptr, png_const_bytep data, size_t length) 34 | { 35 | /* NOTE: write_data_fn must not change the buffer! */ 36 | if (png_ptr->write_data_fn != NULL ) 37 | (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data), 38 | length); 39 | 40 | else 41 | png_error(png_ptr, "Call to NULL write function"); 42 | } 43 | 44 | #ifdef PNG_STDIO_SUPPORTED 45 | /* This is the function that does the actual writing of data. If you are 46 | * not writing to a standard C stream, you should create a replacement 47 | * write_data function and use it at run time with png_set_write_fn(), rather 48 | * than changing the library. 49 | */ 50 | void PNGCBAPI 51 | png_default_write_data(png_structp png_ptr, png_bytep data, size_t length) 52 | { 53 | size_t check; 54 | 55 | if (png_ptr == NULL) 56 | return; 57 | 58 | check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); 59 | 60 | if (check != length) 61 | png_error(png_ptr, "Write Error"); 62 | } 63 | #endif 64 | 65 | /* This function is called to output any data pending writing (normally 66 | * to disk). After png_flush is called, there should be no data pending 67 | * writing in any buffers. 68 | */ 69 | #ifdef PNG_WRITE_FLUSH_SUPPORTED 70 | void /* PRIVATE */ 71 | png_flush(png_structrp png_ptr) 72 | { 73 | if (png_ptr->output_flush_fn != NULL) 74 | (*(png_ptr->output_flush_fn))(png_ptr); 75 | } 76 | 77 | # ifdef PNG_STDIO_SUPPORTED 78 | void PNGCBAPI 79 | png_default_flush(png_structp png_ptr) 80 | { 81 | png_FILE_p io_ptr; 82 | 83 | if (png_ptr == NULL) 84 | return; 85 | 86 | io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); 87 | fflush(io_ptr); 88 | } 89 | # endif 90 | #endif 91 | 92 | /* This function allows the application to supply new output functions for 93 | * libpng if standard C streams aren't being used. 94 | * 95 | * This function takes as its arguments: 96 | * png_ptr - pointer to a png output data structure 97 | * io_ptr - pointer to user supplied structure containing info about 98 | * the output functions. May be NULL. 99 | * write_data_fn - pointer to a new output function that takes as its 100 | * arguments a pointer to a png_struct, a pointer to 101 | * data to be written, and a 32-bit unsigned int that is 102 | * the number of bytes to be written. The new write 103 | * function should call png_error(png_ptr, "Error msg") 104 | * to exit and output any fatal error messages. May be 105 | * NULL, in which case libpng's default function will 106 | * be used. 107 | * flush_data_fn - pointer to a new flush function that takes as its 108 | * arguments a pointer to a png_struct. After a call to 109 | * the flush function, there should be no data in any buffers 110 | * or pending transmission. If the output method doesn't do 111 | * any buffering of output, a function prototype must still be 112 | * supplied although it doesn't have to do anything. If 113 | * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile 114 | * time, output_flush_fn will be ignored, although it must be 115 | * supplied for compatibility. May be NULL, in which case 116 | * libpng's default function will be used, if 117 | * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not 118 | * a good idea if io_ptr does not point to a standard 119 | * *FILE structure. 120 | */ 121 | void PNGAPI 122 | png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, 123 | png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) 124 | { 125 | if (png_ptr == NULL) 126 | return; 127 | 128 | png_ptr->io_ptr = io_ptr; 129 | 130 | #ifdef PNG_STDIO_SUPPORTED 131 | if (write_data_fn != NULL) 132 | png_ptr->write_data_fn = write_data_fn; 133 | 134 | else 135 | png_ptr->write_data_fn = png_default_write_data; 136 | #else 137 | png_ptr->write_data_fn = write_data_fn; 138 | #endif 139 | 140 | #ifdef PNG_WRITE_FLUSH_SUPPORTED 141 | # ifdef PNG_STDIO_SUPPORTED 142 | 143 | if (output_flush_fn != NULL) 144 | png_ptr->output_flush_fn = output_flush_fn; 145 | 146 | else 147 | png_ptr->output_flush_fn = png_default_flush; 148 | 149 | # else 150 | png_ptr->output_flush_fn = output_flush_fn; 151 | # endif 152 | #else 153 | PNG_UNUSED(output_flush_fn) 154 | #endif /* WRITE_FLUSH */ 155 | 156 | #ifdef PNG_READ_SUPPORTED 157 | /* It is an error to read while writing a png file */ 158 | if (png_ptr->read_data_fn != NULL) 159 | { 160 | png_ptr->read_data_fn = NULL; 161 | 162 | png_warning(png_ptr, 163 | "Can't set both read_data_fn and write_data_fn in the" 164 | " same structure"); 165 | } 166 | #endif 167 | } 168 | #endif /* WRITE */ 169 | -------------------------------------------------------------------------------- /extra/hecl_autocomplete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | _hecl () 4 | { 5 | local word=${COMP_WORDS[COMP_CWORD]} 6 | local filecmds=(init spec extract add remove group cook clean package) 7 | 8 | if [ $COMP_CWORD == 1 ] 9 | then 10 | COMPREPLY=($(compgen -W "${filecmds[*]} help" "${word}")) 11 | return 0 12 | elif [ $COMP_CWORD == 2 ] 13 | then 14 | case ${COMP_WORDS[1]} in 15 | init|extract|add|remove|group|cook|clean|package) 16 | COMPREPLY=($(compgen -f -- "${word}")) 17 | ;; 18 | spec) 19 | COMPREPLY=($(compgen -W "enable disable" "${word}")) 20 | ;; 21 | help) 22 | COMPREPLY=($(compgen -W "${filecmds[*]}" "${word}")) 23 | ;; 24 | esac 25 | fi 26 | } 27 | 28 | complete -F _hecl hecl 29 | 30 | -------------------------------------------------------------------------------- /include/hecl/ApplicationReps.hpp.in: -------------------------------------------------------------------------------- 1 | /* CMake-curated application reps header */ 2 | 3 | #define STAGE_SPECIALIZATIONS(T, P) \ 4 | T, \ 5 | T, \ 6 | T, \ 7 | T, \ 8 | T, 9 | 10 | @HECL_APPLICATION_REPS_INCLUDES_LOCAL@ 11 | 12 | #define HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL \ 13 | @HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL@ 14 | 15 | #define HECL_APPLICATION_STAGE_REPS(P, S) \ 16 | @HECL_APPLICATION_STAGE_REPS_LOCAL@ 17 | -------------------------------------------------------------------------------- /include/hecl/Blender/SDNARead.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "hecl/FourCC.hpp" 7 | #include "hecl/SystemChar.hpp" 8 | 9 | #include 10 | 11 | namespace athena::io { 12 | class MemoryReader; 13 | } 14 | 15 | namespace hecl::blender { 16 | enum class BlendType; 17 | 18 | struct SDNABlock : public athena::io::DNA { 19 | AT_DECL_DNA 20 | DNAFourCC magic; 21 | DNAFourCC nameMagic; 22 | Value numNames; 23 | Vector, AT_DNA_COUNT(numNames)> names; 24 | Align<4> align1; 25 | DNAFourCC typeMagic; 26 | Value numTypes; 27 | Vector, AT_DNA_COUNT(numTypes)> types; 28 | Align<4> align2; 29 | DNAFourCC tlenMagic; 30 | Vector, AT_DNA_COUNT(numTypes)> tlens; 31 | Align<4> align3; 32 | DNAFourCC strcMagic; 33 | Value numStrcs; 34 | struct SDNAStruct : public athena::io::DNA { 35 | AT_DECL_DNA 36 | Value type; 37 | Value numFields; 38 | struct SDNAField : public athena::io::DNA { 39 | AT_DECL_DNA 40 | Value type; 41 | Value name; 42 | atUint32 offset; 43 | }; 44 | Vector fields; 45 | 46 | void computeOffsets(const SDNABlock& block); 47 | const SDNAField* lookupField(const SDNABlock& block, const char* n) const; 48 | }; 49 | Vector strcs; 50 | 51 | const SDNAStruct* lookupStruct(const char* n, atUint32& idx) const; 52 | }; 53 | 54 | struct FileBlock : public athena::io::DNA { 55 | AT_DECL_DNA 56 | DNAFourCC type; 57 | Value size; 58 | Value ptr; 59 | Value sdnaIdx; 60 | Value count; 61 | }; 62 | 63 | class SDNARead { 64 | std::vector m_data; 65 | SDNABlock m_sdnaBlock; 66 | 67 | public: 68 | explicit SDNARead(SystemStringView path); 69 | explicit operator bool() const { return !m_data.empty(); } 70 | const SDNABlock& sdnaBlock() const { return m_sdnaBlock; } 71 | void enumerate(const std::function& func) const; 72 | }; 73 | 74 | BlendType GetBlendType(SystemStringView path); 75 | 76 | } // namespace hecl::blender 77 | -------------------------------------------------------------------------------- /include/hecl/Blender/Token.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace hecl::blender { 6 | class Connection; 7 | 8 | class Token { 9 | std::unique_ptr m_conn; 10 | 11 | public: 12 | Connection& getBlenderConnection(); 13 | void shutdown(); 14 | 15 | Token() = default; 16 | ~Token(); 17 | Token(const Token&) = delete; 18 | Token& operator=(const Token&) = delete; 19 | Token(Token&&) = default; 20 | Token& operator=(Token&&) = default; 21 | }; 22 | 23 | } // namespace hecl::blender 24 | -------------------------------------------------------------------------------- /include/hecl/CVarCommons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "hecl/CVarManager.hpp" 8 | 9 | #undef min 10 | #undef max 11 | 12 | namespace hecl { 13 | 14 | using namespace std::literals; 15 | 16 | #if defined(__APPLE__) 17 | #define DEFAULT_GRAPHICS_API "Metal"sv 18 | #else 19 | #define DEFAULT_GRAPHICS_API "Vulkan"sv 20 | #endif 21 | 22 | struct CVarCommons { 23 | CVarManager& m_mgr; 24 | CVar* m_fullscreen = nullptr; 25 | CVar* m_graphicsApi = nullptr; 26 | CVar* m_drawSamples = nullptr; 27 | CVar* m_texAnisotropy = nullptr; 28 | CVar* m_deepColor = nullptr; 29 | CVar* m_variableDt = nullptr; 30 | 31 | CVar* m_debugOverlayPlayerInfo = nullptr; 32 | CVar* m_debugOverlayWorldInfo = nullptr; 33 | CVar* m_debugOverlayAreaInfo = nullptr; 34 | CVar* m_debugOverlayShowFrameCounter = nullptr; 35 | CVar* m_debugOverlayShowFramerate = nullptr; 36 | CVar* m_debugOverlayShowInGameTime = nullptr; 37 | CVar* m_debugOverlayShowResourceStats = nullptr; 38 | CVar* m_debugOverlayShowRandomStats = nullptr; 39 | CVar* m_debugOverlayShowRoomTimer = nullptr; 40 | CVar* m_debugToolDrawAiPath = nullptr; 41 | CVar* m_debugToolDrawLighting = nullptr; 42 | CVar* m_debugToolDrawCollisionActors = nullptr; 43 | CVar* m_debugToolDrawMazePath = nullptr; 44 | CVar* m_debugToolDrawPlatformCollision = nullptr; 45 | CVar* m_logFile = nullptr; 46 | 47 | CVarCommons(CVarManager& manager); 48 | 49 | bool getFullscreen() const { return m_fullscreen->toBoolean(); } 50 | 51 | void setFullscreen(bool b) { m_fullscreen->fromBoolean(b); } 52 | 53 | std::string getGraphicsApi() const { return m_graphicsApi->toLiteral(); } 54 | 55 | void setGraphicsApi(std::string_view api) { m_graphicsApi->fromLiteral(api); } 56 | 57 | uint32_t getSamples() const { return std::max(1u, m_drawSamples->toUnsigned()); } 58 | 59 | void setSamples(uint32_t v) { m_drawSamples->fromInteger(std::max(uint32_t(1), v)); } 60 | 61 | uint32_t getAnisotropy() const { return std::max(1u, uint32_t(m_texAnisotropy->toUnsigned())); } 62 | 63 | void setAnisotropy(uint32_t v) { m_texAnisotropy->fromInteger(std::max(1u, v)); } 64 | 65 | bool getDeepColor() const { return m_deepColor->toBoolean(); } 66 | 67 | void setDeepColor(bool b) { m_deepColor->fromBoolean(b); } 68 | 69 | bool getVariableFrameTime() const { return m_variableDt->toBoolean(); } 70 | 71 | void setVariableFrameTime(bool b) { m_variableDt->fromBoolean(b); } 72 | 73 | std::string getLogFile() const { return m_logFile->toLiteral(); }; 74 | 75 | void setLogFile(std::string_view log) { m_logFile->fromLiteral(log); } 76 | 77 | void serialize() { m_mgr.serialize(); } 78 | 79 | static CVarCommons* instance(); 80 | }; 81 | 82 | } // namespace hecl 83 | -------------------------------------------------------------------------------- /include/hecl/CVarManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "hecl/CVar.hpp" 9 | #include "hecl/SystemChar.hpp" 10 | 11 | namespace hecl { 12 | namespace Runtime { 13 | class FileStoreManager; 14 | } 15 | extern CVar* com_developer; 16 | extern CVar* com_configfile; 17 | extern CVar* com_enableCheats; 18 | extern CVar* com_cubemaps; 19 | class CVarManager final { 20 | using CVarContainer = DNACVAR::CVarContainer; 21 | template 22 | CVar* _newCVar(std::string_view name, std::string_view help, const T& value, CVar::EFlags flags) { 23 | if (CVar* ret = registerCVar(std::make_unique(name, value, help, flags))) { 24 | deserialize(ret); 25 | return ret; 26 | } 27 | return nullptr; 28 | } 29 | 30 | hecl::Runtime::FileStoreManager& m_store; 31 | bool m_useBinary; 32 | static CVarManager* m_instance; 33 | 34 | public: 35 | CVarManager() = delete; 36 | CVarManager(const CVarManager&) = delete; 37 | CVarManager& operator=(const CVarManager&) = delete; 38 | CVarManager& operator=(const CVarManager&&) = delete; 39 | CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary = false); 40 | ~CVarManager(); 41 | 42 | CVar* newCVar(std::string_view name, std::string_view help, const atVec2f& value, CVar::EFlags flags) { 43 | return _newCVar(name, help, value, flags); 44 | } 45 | CVar* newCVar(std::string_view name, std::string_view help, const atVec2d& value, CVar::EFlags flags) { 46 | return _newCVar(name, help, value, flags); 47 | } 48 | CVar* newCVar(std::string_view name, std::string_view help, const atVec3f& value, CVar::EFlags flags) { 49 | return _newCVar(name, help, value, flags); 50 | } 51 | CVar* newCVar(std::string_view name, std::string_view help, const atVec3d& value, CVar::EFlags flags) { 52 | return _newCVar(name, help, value, flags); 53 | } 54 | CVar* newCVar(std::string_view name, std::string_view help, const atVec4f& value, CVar::EFlags flags) { 55 | return _newCVar(name, help, value, flags); 56 | } 57 | CVar* newCVar(std::string_view name, std::string_view help, const atVec4d& value, CVar::EFlags flags) { 58 | return _newCVar(name, help, value, flags); 59 | } 60 | CVar* newCVar(std::string_view name, std::string_view help, std::string_view value, CVar::EFlags flags) { 61 | return _newCVar(name, help, value, flags); 62 | } 63 | CVar* newCVar(std::string_view name, std::string_view help, bool value, CVar::EFlags flags) { 64 | return _newCVar(name, help, value, flags); 65 | } 66 | // Float and double are internally identical, all floating point values are stored as `double` 67 | CVar* newCVar(std::string_view name, std::string_view help, float value, CVar::EFlags flags) { 68 | return _newCVar(name, help, static_cast(value), flags); 69 | } 70 | CVar* newCVar(std::string_view name, std::string_view help, double value, CVar::EFlags flags) { 71 | return _newCVar(name, help, value, flags); 72 | } 73 | // Integer CVars can be seamlessly converted between either type, the distinction is to make usage absolutely clear 74 | CVar* newCVar(std::string_view name, std::string_view help, int32_t value, CVar::EFlags flags) { 75 | return _newCVar(name, help, value, flags); 76 | } 77 | 78 | CVar* newCVar(std::string_view name, std::string_view help, uint32_t value, CVar::EFlags flags) { 79 | return _newCVar(name, help, value, flags); 80 | } 81 | 82 | CVar* registerCVar(std::unique_ptr&& cvar); 83 | 84 | CVar* findCVar(std::string_view name); 85 | template 86 | CVar* findOrMakeCVar(std::string_view name, _Args&&... args) { 87 | if (CVar* cv = findCVar(name)) 88 | return cv; 89 | return newCVar(name, std::forward<_Args>(args)...); 90 | } 91 | 92 | std::vector archivedCVars() const; 93 | std::vector cvars(CVar::EFlags filter = CVar::EFlags::Any) const; 94 | 95 | void deserialize(CVar* cvar); 96 | void serialize(); 97 | 98 | static CVarManager* instance(); 99 | 100 | void proc(); 101 | 102 | void list(class Console* con, const std::vector& args); 103 | void setCVar(class Console* con, const std::vector& args); 104 | void getCVar(class Console* con, const std::vector& args); 105 | 106 | void setDeveloperMode(bool v, bool setDeserialized = false); 107 | void setCheatsEnabled(bool v, bool setDeserialized = false); 108 | bool restartRequired() const; 109 | 110 | void parseCommandLine(const std::vector& args); 111 | 112 | private: 113 | bool suppressDeveloper(); 114 | void restoreDeveloper(bool oldDeveloper); 115 | 116 | std::unordered_map> m_cvars; 117 | std::unordered_map m_deferedCVars; 118 | }; 119 | 120 | } // namespace hecl 121 | -------------------------------------------------------------------------------- /include/hecl/ClientProcess.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "hecl/Blender/Token.hpp" 12 | #include "hecl/hecl.hpp" 13 | #include "hecl/SystemChar.hpp" 14 | 15 | #include 16 | 17 | namespace hecl::Database { 18 | class IDataSpec; 19 | } 20 | 21 | namespace hecl { 22 | class MultiProgressPrinter; 23 | 24 | extern int CpuCountOverride; 25 | void SetCpuCountOverride(int argc, const SystemChar** argv); 26 | 27 | class ClientProcess { 28 | std::mutex m_mutex; 29 | std::condition_variable m_cv; 30 | std::condition_variable m_initCv; 31 | std::condition_variable m_waitCv; 32 | const MultiProgressPrinter* m_progPrinter; 33 | int m_completedCooks = 0; 34 | int m_addedCooks = 0; 35 | 36 | public: 37 | struct Transaction { 38 | ClientProcess& m_parent; 39 | enum class Type { Buffer, Cook, Lambda } m_type; 40 | bool m_complete = false; 41 | virtual void run(blender::Token& btok) = 0; 42 | Transaction(ClientProcess& parent, Type tp) : m_parent(parent), m_type(tp) {} 43 | }; 44 | struct BufferTransaction final : Transaction { 45 | ProjectPath m_path; 46 | void* m_targetBuf; 47 | size_t m_maxLen; 48 | size_t m_offset; 49 | void run(blender::Token& btok) override; 50 | BufferTransaction(ClientProcess& parent, const ProjectPath& path, void* target, size_t maxLen, size_t offset) 51 | : Transaction(parent, Type::Buffer), m_path(path), m_targetBuf(target), m_maxLen(maxLen), m_offset(offset) {} 52 | }; 53 | struct CookTransaction final : Transaction { 54 | ProjectPath m_path; 55 | Database::IDataSpec* m_dataSpec; 56 | bool m_returnResult = false; 57 | bool m_force; 58 | bool m_fast; 59 | void run(blender::Token& btok) override; 60 | CookTransaction(ClientProcess& parent, const ProjectPath& path, bool force, bool fast, Database::IDataSpec* spec) 61 | : Transaction(parent, Type::Cook), m_path(path), m_dataSpec(spec), m_force(force), m_fast(fast) {} 62 | }; 63 | struct LambdaTransaction final : Transaction { 64 | std::function m_func; 65 | void run(blender::Token& btok) override; 66 | LambdaTransaction(ClientProcess& parent, std::function&& func) 67 | : Transaction(parent, Type::Lambda), m_func(std::move(func)) {} 68 | }; 69 | 70 | private: 71 | std::list> m_pendingQueue; 72 | std::list> m_completedQueue; 73 | int m_inProgress = 0; 74 | bool m_running = true; 75 | 76 | struct Worker { 77 | ClientProcess& m_proc; 78 | int m_idx; 79 | std::thread m_thr; 80 | blender::Token m_blendTok; 81 | bool m_didInit = false; 82 | Worker(ClientProcess& proc, int idx); 83 | void proc(); 84 | }; 85 | std::vector m_workers; 86 | static ThreadLocalPtr ThreadWorker; 87 | 88 | public: 89 | ClientProcess(const MultiProgressPrinter* progPrinter = nullptr); 90 | ~ClientProcess() { shutdown(); } 91 | std::shared_ptr addBufferTransaction(const hecl::ProjectPath& path, void* target, 92 | size_t maxLen, size_t offset); 93 | std::shared_ptr addCookTransaction(const hecl::ProjectPath& path, bool force, bool fast, 94 | Database::IDataSpec* spec); 95 | std::shared_ptr addLambdaTransaction(std::function&& func); 96 | bool syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force, bool fast); 97 | void swapCompletedQueue(std::list>& queue); 98 | void waitUntilComplete(); 99 | void shutdown(); 100 | bool isBusy() const { return m_pendingQueue.size() || m_inProgress; } 101 | 102 | static int GetThreadWorkerIdx() { 103 | Worker* w = ThreadWorker.get(); 104 | if (w) 105 | return w->m_idx; 106 | return -1; 107 | } 108 | }; 109 | 110 | } // namespace hecl 111 | -------------------------------------------------------------------------------- /include/hecl/Compilers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace hecl { 14 | 15 | namespace PlatformType { 16 | using PlatformEnum = boo::IGraphicsDataFactory::Platform; 17 | struct Null {}; 18 | struct OpenGL { 19 | static constexpr PlatformEnum Enum = PlatformEnum::OpenGL; 20 | static constexpr char Name[] = "OpenGL"; 21 | #if BOO_HAS_GL 22 | using Context = boo::GLDataFactory::Context; 23 | #endif 24 | }; 25 | struct D3D11 { 26 | static constexpr PlatformEnum Enum = PlatformEnum::D3D11; 27 | static constexpr char Name[] = "D3D11"; 28 | #if _WIN32 29 | using Context = boo::D3D11DataFactory::Context; 30 | #endif 31 | }; 32 | struct Metal { 33 | static constexpr PlatformEnum Enum = PlatformEnum::Metal; 34 | static constexpr char Name[] = "Metal"; 35 | #if BOO_HAS_METAL 36 | using Context = boo::MetalDataFactory::Context; 37 | #endif 38 | }; 39 | struct Vulkan { 40 | static constexpr PlatformEnum Enum = PlatformEnum::Vulkan; 41 | static constexpr char Name[] = "Vulkan"; 42 | #if BOO_HAS_VULKAN 43 | using Context = boo::VulkanDataFactory::Context; 44 | #endif 45 | }; 46 | struct NX { 47 | static constexpr PlatformEnum Enum = PlatformEnum::NX; 48 | static constexpr char Name[] = "NX"; 49 | #if BOO_HAS_NX 50 | using Context = boo::NXDataFactory::Context; 51 | #endif 52 | }; 53 | } // namespace PlatformType 54 | 55 | namespace PipelineStage { 56 | using StageEnum = boo::PipelineStage; 57 | struct Null { 58 | static constexpr StageEnum Enum = StageEnum::Null; 59 | static constexpr char Name[] = "Null"; 60 | }; 61 | struct Vertex { 62 | static constexpr StageEnum Enum = StageEnum::Vertex; 63 | static constexpr char Name[] = "Vertex"; 64 | }; 65 | struct Fragment { 66 | static constexpr StageEnum Enum = StageEnum::Fragment; 67 | static constexpr char Name[] = "Fragment"; 68 | }; 69 | struct Geometry { 70 | static constexpr StageEnum Enum = StageEnum::Geometry; 71 | static constexpr char Name[] = "Geometry"; 72 | }; 73 | struct Control { 74 | static constexpr StageEnum Enum = StageEnum::Control; 75 | static constexpr char Name[] = "Control"; 76 | }; 77 | struct Evaluation { 78 | static constexpr StageEnum Enum = StageEnum::Evaluation; 79 | static constexpr char Name[] = "Evaluation"; 80 | }; 81 | } // namespace PipelineStage 82 | 83 | #ifdef _LIBCPP_VERSION 84 | using StageBinaryData = std::shared_ptr; 85 | inline StageBinaryData MakeStageBinaryData(size_t sz) { 86 | return StageBinaryData(new uint8_t[sz], std::default_delete{}); 87 | } 88 | #else 89 | using StageBinaryData = std::shared_ptr; 90 | inline StageBinaryData MakeStageBinaryData(size_t sz) { return StageBinaryData(new uint8_t[sz]); } 91 | #endif 92 | 93 | template 94 | std::pair CompileShader(std::string_view text); 95 | 96 | } // namespace hecl 97 | -------------------------------------------------------------------------------- /include/hecl/Console.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace boo { 12 | class IWindow; 13 | 14 | enum class EModifierKey; 15 | enum class ESpecialKey; 16 | 17 | struct IGraphicsCommandQueue; 18 | } // namespace boo 19 | 20 | namespace hecl { 21 | class CVarManager; 22 | class CVar; 23 | struct SConsoleCommand { 24 | enum class ECommandFlags { Normal = 0, Cheat = (1 << 0), Developer = (1 << 1) }; 25 | std::string m_displayName; 26 | std::string m_helpString; 27 | std::string m_usage; 28 | std::function&)> m_func; 29 | ECommandFlags m_flags; 30 | }; 31 | ENABLE_BITWISE_ENUM(SConsoleCommand::ECommandFlags) 32 | 33 | class Console { 34 | friend class LogVisorAdapter; 35 | struct LogVisorAdapter : logvisor::ILogger { 36 | Console* m_con; 37 | LogVisorAdapter(Console* con) : logvisor::ILogger(log_typeid(LogVisorAdapter)), m_con(con) {} 38 | 39 | ~LogVisorAdapter() override = default; 40 | void report(const char* modName, logvisor::Level severity, fmt::string_view format, fmt::format_args args) override; 41 | void report(const char* modName, logvisor::Level severity, fmt::wstring_view format, 42 | fmt::wformat_args args) override; 43 | void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum, 44 | fmt::string_view format, fmt::format_args args) override; 45 | void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum, 46 | fmt::wstring_view format, fmt::wformat_args args) override; 47 | }; 48 | 49 | public: 50 | static Console* m_instance; 51 | enum class Level { 52 | Info, /**< Non-error informative message */ 53 | Warning, /**< Non-error warning message */ 54 | Error, /**< Recoverable error message */ 55 | Fatal /**< Non-recoverable error message (Kept for compatibility with logvisor) */ 56 | }; 57 | 58 | enum class State { Closed, Closing, Opened, Opening }; 59 | 60 | private: 61 | CVarManager* m_cvarMgr = nullptr; 62 | boo::IWindow* m_window = nullptr; 63 | std::unordered_map m_commands; 64 | std::vector> m_log; 65 | int m_logOffset = 0; 66 | std::string m_commandString; 67 | std::vector m_commandHistory; 68 | int m_cursorPosition = -1; 69 | int m_currentCommand = -1; 70 | size_t m_maxLines = 0; 71 | bool m_overwrite : 1; 72 | bool m_cursorAtEnd : 1; 73 | State m_state = State::Closed; 74 | CVar* m_conSpeed; 75 | CVar* m_conHeight; 76 | float m_cachedConSpeed; 77 | float m_cachedConHeight; 78 | bool m_showCursor = true; 79 | float m_cursorTime = 0.f; 80 | 81 | public: 82 | Console(CVarManager*); 83 | void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, 84 | std::function&)>&& func, 85 | SConsoleCommand::ECommandFlags cmdFlags = SConsoleCommand::ECommandFlags::Normal); 86 | void unregisterCommand(std::string_view name); 87 | 88 | void executeString(const std::string& strToExec); 89 | 90 | void help(Console* con, const std::vector& args); 91 | void listCommands(Console* con, const std::vector& args); 92 | bool commandExists(std::string_view cmd) const; 93 | 94 | void vreport(Level level, fmt::string_view format, fmt::format_args args); 95 | template > 96 | void report(Level level, const S& format, Args&&... args) { 97 | vreport(level, fmt::to_string_view(format), 98 | fmt::basic_format_args>( 99 | fmt::make_args_checked(format, args...))); 100 | } 101 | 102 | void init(boo::IWindow* ctx); 103 | void proc(); 104 | void draw(boo::IGraphicsCommandQueue* gfxQ); 105 | void handleCharCode(unsigned long chr, boo::EModifierKey mod, bool repeat); 106 | void handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool repeat); 107 | void handleSpecialKeyUp(boo::ESpecialKey sp, boo::EModifierKey mod); 108 | void dumpLog(); 109 | static Console* instance(); 110 | static void RegisterLogger(Console* con); 111 | bool isOpen() const { return m_state == State::Opened; } 112 | }; 113 | } // namespace hecl 114 | -------------------------------------------------------------------------------- /include/hecl/FourCC.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace hecl { 11 | 12 | /** 13 | * @brief FourCC representation used within HECL's database 14 | * 15 | * FourCCs are efficient, mnemonic four-char-sequences used to represent types 16 | * while fitting comfortably in a 32-bit word. HECL uses a four-char array 17 | * to remain endian-independent. 18 | */ 19 | class FourCC { 20 | protected: 21 | union { 22 | char fcc[4]; 23 | uint32_t num = 0; 24 | }; 25 | 26 | public: 27 | // Sentinel FourCC 28 | constexpr FourCC() noexcept = default; 29 | constexpr FourCC(const FourCC& other) noexcept = default; 30 | constexpr FourCC(FourCC&& other) noexcept = default; 31 | constexpr FourCC(const char* name) noexcept : fcc{name[0], name[1], name[2], name[3]} {} 32 | constexpr FourCC(uint32_t n) noexcept : num(n) {} 33 | 34 | constexpr FourCC& operator=(const FourCC&) noexcept = default; 35 | constexpr FourCC& operator=(FourCC&&) noexcept = default; 36 | 37 | constexpr bool operator==(const FourCC& other) const noexcept { return num == other.num; } 38 | constexpr bool operator!=(const FourCC& other) const noexcept { return !operator==(other); } 39 | constexpr bool operator==(const char* other) const noexcept { 40 | return other[0] == fcc[0] && other[1] == fcc[1] && other[2] == fcc[2] && other[3] == fcc[3]; 41 | } 42 | constexpr bool operator!=(const char* other) const noexcept { return !operator==(other); } 43 | constexpr bool operator==(int32_t other) const noexcept { return num == uint32_t(other); } 44 | constexpr bool operator!=(int32_t other) const noexcept { return !operator==(other); } 45 | constexpr bool operator==(uint32_t other) const noexcept { return num == other; } 46 | constexpr bool operator!=(uint32_t other) const noexcept { return !operator==(other); } 47 | 48 | std::string toString() const { return std::string(std::begin(fcc), std::end(fcc)); } 49 | constexpr std::string_view toStringView() const { return std::string_view(fcc, std::size(fcc)); } 50 | constexpr uint32_t toUint32() const noexcept { return num; } 51 | constexpr const char* getChars() const noexcept { return fcc; } 52 | constexpr char* getChars() noexcept { return fcc; } 53 | constexpr bool IsValid() const noexcept { return num != 0; } 54 | }; 55 | #define FOURCC(chars) FourCC(SBIG(chars)) 56 | 57 | using BigDNA = athena::io::DNA; 58 | 59 | /** FourCC with DNA read/write */ 60 | class DNAFourCC final : public BigDNA, public FourCC { 61 | public: 62 | constexpr DNAFourCC() noexcept : FourCC() {} 63 | constexpr DNAFourCC(const FourCC& other) noexcept : FourCC(other) {} 64 | constexpr DNAFourCC(const char* name) noexcept : FourCC(name) {} 65 | constexpr DNAFourCC(uint32_t n) noexcept : FourCC(n) {} 66 | DNAFourCC(athena::io::IStreamReader& r) { read(r); } 67 | AT_DECL_EXPLICIT_DNA_YAML 68 | }; 69 | template <> 70 | inline void DNAFourCC::Enumerate(Read::StreamT& r) { 71 | r.readUBytesToBuf(fcc, std::size(fcc)); 72 | } 73 | template <> 74 | inline void DNAFourCC::Enumerate(Write::StreamT& w) { 75 | w.writeBytes(fcc, std::size(fcc)); 76 | } 77 | template <> 78 | inline void DNAFourCC::Enumerate(ReadYaml::StreamT& r) { 79 | const std::string rs = r.readString(); 80 | rs.copy(fcc, std::size(fcc)); 81 | } 82 | template <> 83 | inline void DNAFourCC::Enumerate(WriteYaml::StreamT& w) { 84 | w.writeString(std::string_view{fcc, std::size(fcc)}); 85 | } 86 | template <> 87 | inline void DNAFourCC::Enumerate(BinarySize::StreamT& s) { 88 | s += std::size(fcc); 89 | } 90 | 91 | } // namespace hecl 92 | 93 | namespace std { 94 | template <> 95 | struct hash { 96 | size_t operator()(const hecl::FourCC& val) const noexcept { return val.toUint32(); } 97 | }; 98 | } // namespace std 99 | 100 | FMT_CUSTOM_FORMATTER(hecl::FourCC, "{:c}{:c}{:c}{:c}", obj.getChars()[0], obj.getChars()[1], obj.getChars()[2], 101 | obj.getChars()[3]) 102 | FMT_CUSTOM_FORMATTER(hecl::DNAFourCC, "{:c}{:c}{:c}{:c}", obj.getChars()[0], obj.getChars()[1], obj.getChars()[2], 103 | obj.getChars()[3]) 104 | -------------------------------------------------------------------------------- /include/hecl/HMDLMeta.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace hecl { 6 | 7 | enum class HMDLTopology : atUint32 { 8 | Triangles, 9 | TriStrips, 10 | }; 11 | 12 | #define HECL_HMDL_META_SZ 32 13 | 14 | struct HMDLMeta : athena::io::DNA { 15 | AT_DECL_DNA 16 | Value magic = 'TACO'; 17 | Value topology; 18 | Value vertStride; 19 | Value vertCount; 20 | Value indexCount; 21 | Value colorCount; 22 | Value uvCount; 23 | Value weightCount; 24 | Value bankCount; 25 | }; 26 | 27 | } // namespace hecl 28 | -------------------------------------------------------------------------------- /include/hecl/MultiProgressPrinter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "hecl/SystemChar.hpp" 9 | 10 | #if _WIN32 11 | #ifndef WIN32_LEAN_AND_MEAN 12 | #define WIN32_LEAN_AND_MEAN 13 | #endif 14 | #include 15 | #endif 16 | 17 | namespace hecl { 18 | 19 | class MultiProgressPrinter { 20 | std::thread m_logThread; 21 | mutable std::mutex m_logLock; 22 | bool m_newLineAfter; 23 | 24 | struct TermInfo { 25 | #if _WIN32 26 | HANDLE console; 27 | #endif 28 | int width; 29 | bool xtermColor = false; 30 | bool truncate = false; 31 | } m_termInfo; 32 | 33 | struct ThreadStat { 34 | hecl::SystemString m_message, m_submessage; 35 | float m_factor = 0.f; 36 | bool m_active = false; 37 | void print(const TermInfo& tinfo) const; 38 | }; 39 | mutable std::vector m_threadStats; 40 | 41 | mutable float m_mainFactor = -1.f; 42 | mutable int m_indeterminateCounter = 0; 43 | mutable int m_curThreadLines = 0; 44 | mutable int m_curProgLines = 0; 45 | mutable int m_latestThread = -1; 46 | mutable bool m_running = false; 47 | mutable bool m_dirty = false; 48 | mutable bool m_mainIndeterminate = false; 49 | uint64_t m_lastLogCounter = 0; 50 | void LogProc(); 51 | void DoPrint(); 52 | void DrawIndeterminateBar(); 53 | void MoveCursorUp(int n); 54 | 55 | public: 56 | MultiProgressPrinter(bool activate = false); 57 | ~MultiProgressPrinter(); 58 | void print(const hecl::SystemChar* message, const hecl::SystemChar* submessage, float factor = -1.f, 59 | int threadIdx = 0) const; 60 | void setMainFactor(float factor) const; 61 | void setMainIndeterminate(bool indeterminate) const; 62 | void startNewLine() const; 63 | void flush() const; 64 | }; 65 | 66 | } // namespace hecl 67 | -------------------------------------------------------------------------------- /include/hecl/Runtime.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "hecl/SystemChar.hpp" 7 | 8 | #include 9 | #include 10 | 11 | namespace hecl { 12 | struct HMDLMeta; 13 | 14 | namespace Runtime { 15 | 16 | /** 17 | * @brief Per-platform file store resolution 18 | */ 19 | class FileStoreManager { 20 | SystemString m_domain; 21 | SystemString m_storeRoot; 22 | 23 | public: 24 | FileStoreManager(SystemStringView domain); 25 | SystemStringView getDomain() const { return m_domain; } 26 | /** 27 | * @brief Returns the full path to the file store, including domain 28 | * @return Full path to store e.g /home/foo/.hecl/bar 29 | */ 30 | SystemStringView getStoreRoot() const { return m_storeRoot; } 31 | }; 32 | 33 | /** 34 | * @brief Integrated reader/constructor/container for HMDL data 35 | */ 36 | struct HMDLData { 37 | boo::ObjToken m_vbo; 38 | boo::ObjToken m_ibo; 39 | std::unique_ptr m_vtxFmtData; 40 | boo::VertexFormatInfo m_vtxFmt; 41 | 42 | HMDLData(boo::IGraphicsDataFactory::Context& ctx, const void* metaData, const void* vbo, const void* ibo); 43 | 44 | boo::ObjToken newShaderDataBindng(boo::IGraphicsDataFactory::Context& ctx, 45 | const boo::ObjToken& shader, 46 | size_t ubufCount, 47 | const boo::ObjToken* ubufs, 48 | const boo::PipelineStage* ubufStages, size_t texCount, 49 | const boo::ObjToken* texs) { 50 | return ctx.newShaderDataBinding(shader, m_vbo.get(), nullptr, m_ibo.get(), ubufCount, ubufs, ubufStages, nullptr, 51 | nullptr, texCount, texs, nullptr, nullptr); 52 | } 53 | }; 54 | 55 | } // namespace Runtime 56 | } // namespace hecl 57 | -------------------------------------------------------------------------------- /include/hecl/SteamFinder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "hecl/SystemChar.hpp" 4 | 5 | namespace hecl { 6 | 7 | hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /include/hecl/SystemChar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef _WIN32 4 | #include 5 | #include 6 | #include 7 | #else 8 | #ifndef WIN32_LEAN_AND_MEAN 9 | #define WIN32_LEAN_AND_MEAN 1 10 | #endif 11 | #ifndef NOMINMAX 12 | #define NOMINMAX 13 | #endif 14 | #include 15 | #endif 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace hecl { 22 | 23 | #if _WIN32 && UNICODE 24 | #define HECL_UCS2 1 25 | #endif 26 | 27 | #if HECL_UCS2 28 | typedef wchar_t SystemChar; 29 | typedef std::wstring SystemString; 30 | typedef std::wstring_view SystemStringView; 31 | static inline void ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towlower); } 32 | static inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towupper); } 33 | #ifndef _SYS_STR 34 | #define _SYS_STR(val) L##val 35 | #endif 36 | typedef struct _stat Sstat; 37 | #else 38 | typedef char SystemChar; 39 | typedef std::string SystemString; 40 | typedef std::string_view SystemStringView; 41 | static inline void ToLower(SystemString& str) { 42 | std::transform(str.begin(), str.end(), str.begin(), 43 | [](SystemChar c) { return std::tolower(static_cast(c)); }); 44 | } 45 | static inline void ToUpper(SystemString& str) { 46 | std::transform(str.begin(), str.end(), str.begin(), 47 | [](SystemChar c) { return std::toupper(static_cast(c)); }); 48 | } 49 | #ifndef _SYS_STR 50 | #define _SYS_STR(val) val 51 | #endif 52 | typedef struct stat Sstat; 53 | #endif 54 | 55 | constexpr size_t StrLen(const SystemChar* str) { return std::char_traits::length(str); } 56 | 57 | } // namespace hecl 58 | -------------------------------------------------------------------------------- /include/hecl/UniformBufferPool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "hecl/BitVector.hpp" 11 | 12 | #include 13 | #include 14 | 15 | namespace hecl { 16 | 17 | #define HECL_UBUFPOOL_ALLOCATION_BLOCK 262144 18 | 19 | /** This class provides a uniform structure for packing instanced uniform-buffer 20 | * data with consistent stride into a vector of 256K 'Buckets'. 21 | * 22 | * This results in a space-efficient way of managing GPU data of things like UI 23 | * widgets. These can potentially have numerous binding instances, so this avoids 24 | * allocating a full GPU buffer object for each. */ 25 | template 26 | class UniformBufferPool { 27 | public: 28 | /* Resolve div_t type using ssize_t as basis */ 29 | #if _WIN32 30 | using IndexTp = SSIZE_T; 31 | #else 32 | using IndexTp = ssize_t; 33 | #endif 34 | private: 35 | struct InvalidTp {}; 36 | using DivTp = std::conditional_t< 37 | std::is_same::value, std::lldiv_t, 38 | std::conditional_t::value, std::ldiv_t, 39 | std::conditional_t::value, std::div_t, InvalidTp>>>; 40 | static_assert(!std::is_same::value, "unsupported IndexTp for DivTp resolution"); 41 | 42 | /** Size of single element, rounded up to 256-multiple */ 43 | static constexpr IndexTp m_stride = ROUND_UP_256(sizeof(UniformStruct)); 44 | static_assert(m_stride <= HECL_UBUFPOOL_ALLOCATION_BLOCK, "Stride too large for uniform pool"); 45 | 46 | /** Number of rounded elements per 256K bucket */ 47 | static constexpr IndexTp m_countPerBucket = HECL_UBUFPOOL_ALLOCATION_BLOCK / m_stride; 48 | 49 | /** Buffer size per bucket (ideally 256K) */ 50 | static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket; 51 | 52 | /** BitVector indicating free allocation blocks */ 53 | hecl::llvm::BitVector m_freeBlocks; 54 | 55 | /** Efficient way to get bucket and block simultaneously */ 56 | DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); } 57 | 58 | /** Factory pointer for building additional buffers */ 59 | boo::IGraphicsDataFactory* m_factory = nullptr; 60 | 61 | /** Private bucket info */ 62 | struct Bucket { 63 | boo::ObjToken buffer; 64 | uint8_t* cpuBuffer = nullptr; 65 | std::atomic_size_t useCount = {}; 66 | bool dirty = false; 67 | Bucket() = default; 68 | Bucket(const Bucket& other) = delete; 69 | Bucket& operator=(const Bucket& other) = delete; 70 | Bucket(Bucket&& other) = default; 71 | Bucket& operator=(Bucket&& other) = default; 72 | 73 | void updateBuffer() { 74 | if (useCount == 0) { 75 | destroy(); 76 | return; 77 | } 78 | if (dirty && cpuBuffer) { 79 | buffer->unmap(); 80 | cpuBuffer = nullptr; 81 | } 82 | dirty = false; 83 | } 84 | 85 | void increment(UniformBufferPool& pool) { 86 | if (useCount.fetch_add(1) == 0 && !buffer) 87 | buffer = pool.m_factory->newPoolBuffer(boo::BufferUse::Uniform, pool.m_stride, pool.m_countPerBucket BooTrace); 88 | } 89 | 90 | void decrement(UniformBufferPool& pool) { 91 | --useCount; 92 | } 93 | 94 | void destroy() { 95 | if (cpuBuffer) { 96 | buffer->unmap(); 97 | cpuBuffer = nullptr; 98 | } 99 | buffer.reset(); 100 | } 101 | }; 102 | std::vector> m_buckets; 103 | 104 | public: 105 | /** User block-owning token */ 106 | class Token { 107 | friend class UniformBufferPool; 108 | UniformBufferPool* m_pool = nullptr; 109 | IndexTp m_index = -1; 110 | DivTp m_div; 111 | Token(UniformBufferPool* pool) : m_pool(pool) { 112 | auto& freeSpaces = pool->m_freeBlocks; 113 | int idx = freeSpaces.find_first(); 114 | if (idx == -1) { 115 | pool->m_buckets.push_back(std::make_unique()); 116 | m_index = freeSpaces.size(); 117 | freeSpaces.resize(freeSpaces.size() + pool->m_countPerBucket, true); 118 | } else { 119 | m_index = idx; 120 | } 121 | freeSpaces.reset(m_index); 122 | m_div = pool->getBucketDiv(m_index); 123 | 124 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 125 | bucket.increment(*m_pool); 126 | } 127 | 128 | public: 129 | Token() = default; 130 | Token(const Token& other) = delete; 131 | Token& operator=(const Token& other) = delete; 132 | Token& operator=(Token&& other) noexcept { 133 | m_pool = other.m_pool; 134 | m_index = other.m_index; 135 | m_div = other.m_div; 136 | other.m_index = -1; 137 | return *this; 138 | } 139 | Token(Token&& other) noexcept : m_pool(other.m_pool), m_index(other.m_index), m_div(other.m_div) { other.m_index = -1; } 140 | 141 | ~Token() { 142 | if (m_index != -1) { 143 | m_pool->m_freeBlocks.set(m_index); 144 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 145 | bucket.decrement(*m_pool); 146 | } 147 | } 148 | 149 | UniformStruct& access() { 150 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 151 | if (!bucket.cpuBuffer) 152 | bucket.cpuBuffer = reinterpret_cast(bucket.buffer->map(m_sizePerBucket)); 153 | bucket.dirty = true; 154 | return reinterpret_cast(bucket.cpuBuffer[m_div.rem * m_pool->m_stride]); 155 | } 156 | 157 | std::pair, IndexTp> getBufferInfo() const { 158 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 159 | return {bucket.buffer, m_div.rem * m_pool->m_stride}; 160 | } 161 | 162 | explicit operator bool() const { return m_pool != nullptr && m_index != -1; } 163 | }; 164 | 165 | UniformBufferPool() = default; 166 | UniformBufferPool(const UniformBufferPool& other) = delete; 167 | UniformBufferPool& operator=(const UniformBufferPool& other) = delete; 168 | 169 | /** Load dirty buffer data into GPU */ 170 | void updateBuffers() { 171 | for (auto& bucket : m_buckets) 172 | bucket->updateBuffer(); 173 | } 174 | 175 | /** Allocate free block into client-owned Token */ 176 | Token allocateBlock(boo::IGraphicsDataFactory* factory) { 177 | m_factory = factory; 178 | return Token(this); 179 | } 180 | 181 | void doDestroy() { 182 | for (auto& bucket : m_buckets) 183 | bucket->buffer.reset(); 184 | } 185 | }; 186 | 187 | } // namespace hecl 188 | -------------------------------------------------------------------------------- /include/hecl/VertexBufferPool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "hecl/BitVector.hpp" 12 | 13 | #include 14 | #include 15 | 16 | namespace hecl { 17 | 18 | #define HECL_VBUFPOOL_ALLOCATION_BLOCK 524288 19 | 20 | /** This class provides a uniform structure for packing instanced vertex-buffer 21 | * data with consistent stride into a vector of 512K 'Buckets'. 22 | * 23 | * This results in a space-efficient way of managing GPU data of things like UI 24 | * widgets. These can potentially have numerous binding instances, so this avoids 25 | * allocating a full GPU buffer object for each. */ 26 | template 27 | class VertexBufferPool { 28 | public: 29 | /* Resolve div_t type using ssize_t as basis */ 30 | #if _WIN32 31 | using IndexTp = SSIZE_T; 32 | #else 33 | using IndexTp = ssize_t; 34 | #endif 35 | private: 36 | struct InvalidTp {}; 37 | using DivTp = std::conditional_t< 38 | std::is_same::value, std::lldiv_t, 39 | std::conditional_t::value, std::ldiv_t, 40 | std::conditional_t::value, std::div_t, InvalidTp>>>; 41 | static_assert(!std::is_same::value, "unsupported IndexTp for DivTp resolution"); 42 | 43 | /** Size of single element */ 44 | static constexpr IndexTp m_stride = sizeof(VertStruct); 45 | static_assert(m_stride <= HECL_VBUFPOOL_ALLOCATION_BLOCK, "Stride too large for vertex pool"); 46 | 47 | /** Number of elements per 256K bucket */ 48 | static constexpr IndexTp m_countPerBucket = HECL_VBUFPOOL_ALLOCATION_BLOCK / m_stride; 49 | 50 | /** Buffer size per bucket (ideally 256K) */ 51 | static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket; 52 | 53 | /** BitVector indicating free allocation elements */ 54 | hecl::llvm::BitVector m_freeElements; 55 | 56 | /** Efficient way to get bucket and element simultaneously */ 57 | DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); } 58 | 59 | /** Factory pointer for building additional buffers */ 60 | boo::IGraphicsDataFactory* m_factory = nullptr; 61 | 62 | /** Private bucket info */ 63 | struct Bucket { 64 | boo::ObjToken buffer; 65 | uint8_t* cpuBuffer = nullptr; 66 | std::atomic_size_t useCount = {}; 67 | bool dirty = false; 68 | Bucket() = default; 69 | Bucket(const Bucket& other) = delete; 70 | Bucket& operator=(const Bucket& other) = delete; 71 | Bucket(Bucket&& other) = delete; 72 | Bucket& operator=(Bucket&& other) = delete; 73 | 74 | void updateBuffer() { 75 | if (useCount == 0) { 76 | destroy(); 77 | return; 78 | } 79 | if (dirty && cpuBuffer) { 80 | buffer->unmap(); 81 | cpuBuffer = nullptr; 82 | } 83 | dirty = false; 84 | } 85 | 86 | void increment(VertexBufferPool& pool) { 87 | if (useCount.fetch_add(1) == 0 && !buffer) 88 | buffer = pool.m_factory->newPoolBuffer(boo::BufferUse::Vertex, pool.m_stride, pool.m_countPerBucket BooTrace); 89 | } 90 | 91 | void decrement(VertexBufferPool& pool) { 92 | --useCount; 93 | } 94 | 95 | void destroy() { 96 | if (cpuBuffer) { 97 | buffer->unmap(); 98 | cpuBuffer = nullptr; 99 | } 100 | buffer.reset(); 101 | } 102 | }; 103 | std::vector> m_buckets; 104 | 105 | public: 106 | /** User element-owning token */ 107 | class Token { 108 | friend class VertexBufferPool; 109 | VertexBufferPool* m_pool = nullptr; 110 | IndexTp m_index = -1; 111 | IndexTp m_count = 0; 112 | DivTp m_div; 113 | Token(VertexBufferPool* pool, IndexTp count) : m_pool(pool), m_count(count) { 114 | assert(count <= pool->m_countPerBucket && "unable to fit in bucket"); 115 | auto& freeSpaces = pool->m_freeElements; 116 | int idx = freeSpaces.find_first_contiguous(count, pool->m_countPerBucket); 117 | if (idx == -1) { 118 | pool->m_buckets.push_back(std::make_unique()); 119 | m_index = freeSpaces.size(); 120 | freeSpaces.resize(freeSpaces.size() + pool->m_countPerBucket, true); 121 | } else { 122 | m_index = idx; 123 | } 124 | freeSpaces.reset(m_index, m_index + count); 125 | m_div = pool->getBucketDiv(m_index); 126 | 127 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 128 | bucket.increment(*pool); 129 | } 130 | 131 | public: 132 | Token() = default; 133 | Token(const Token& other) = delete; 134 | Token& operator=(const Token& other) = delete; 135 | Token& operator=(Token&& other) noexcept { 136 | m_pool = other.m_pool; 137 | m_index = other.m_index; 138 | m_count = other.m_count; 139 | m_div = other.m_div; 140 | other.m_index = -1; 141 | return *this; 142 | } 143 | Token(Token&& other) noexcept : m_pool(other.m_pool), m_index(other.m_index), m_count(other.m_count), m_div(other.m_div) { 144 | other.m_index = -1; 145 | } 146 | 147 | ~Token() { 148 | if (m_index != -1) { 149 | m_pool->m_freeElements.set(m_index, m_index + m_count); 150 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 151 | bucket.decrement(*m_pool); 152 | } 153 | } 154 | 155 | VertStruct* access() { 156 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 157 | if (!bucket.cpuBuffer) 158 | bucket.cpuBuffer = reinterpret_cast(bucket.buffer->map(m_sizePerBucket)); 159 | bucket.dirty = true; 160 | return reinterpret_cast(&bucket.cpuBuffer[m_div.rem * m_pool->m_stride]); 161 | } 162 | 163 | std::pair, IndexTp> getBufferInfo() const { 164 | Bucket& bucket = *m_pool->m_buckets[m_div.quot]; 165 | return {bucket.buffer, m_div.rem}; 166 | } 167 | 168 | explicit operator bool() const { return m_pool != nullptr && m_index != -1; } 169 | }; 170 | 171 | VertexBufferPool() = default; 172 | VertexBufferPool(const VertexBufferPool& other) = delete; 173 | VertexBufferPool& operator=(const VertexBufferPool& other) = delete; 174 | 175 | /** Load dirty buffer data into GPU */ 176 | void updateBuffers() { 177 | for (auto& bucket : m_buckets) 178 | bucket->updateBuffer(); 179 | } 180 | 181 | /** Allocate free block into client-owned Token */ 182 | Token allocateBlock(boo::IGraphicsDataFactory* factory, IndexTp count) { 183 | m_factory = factory; 184 | return Token(this, count); 185 | } 186 | 187 | void doDestroy() { 188 | for (auto& bucket : m_buckets) 189 | bucket->buffer.reset(); 190 | } 191 | 192 | static constexpr IndexTp bucketCapacity() { return m_countPerBucket; } 193 | }; 194 | 195 | } // namespace hecl 196 | -------------------------------------------------------------------------------- /include/hecl/winsupport.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef WIN32_LEAN_AND_MEAN 4 | #define WIN32_LEAN_AND_MEAN 1 5 | #endif 6 | #ifndef NOMINMAX 7 | #define NOMINMAX 1 8 | #endif 9 | #include "windows.h" 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef DEF_INLINE_MEMMEM 15 | #define DEF_INLINE_MEMMEM 16 | inline void* memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen) { 17 | int needle_first; 18 | const uint8_t* p = static_cast(haystack); 19 | size_t plen = hlen; 20 | 21 | if (!nlen) 22 | return NULL; 23 | 24 | needle_first = *(unsigned char*)needle; 25 | 26 | while (plen >= nlen && (p = static_cast(memchr(p, needle_first, plen - nlen + 1)))) { 27 | if (!memcmp(p, needle, nlen)) 28 | return (void*)p; 29 | 30 | p++; 31 | plen = hlen - (p - static_cast(haystack)); 32 | } 33 | 34 | return NULL; 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /lib/Blender/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(BLENDER_SOURCES 2 | Connection.cpp 3 | MeshOptimizer.hpp 4 | MeshOptimizer.cpp 5 | SDNARead.cpp 6 | HMDL.cpp) 7 | 8 | hecl_add_list(Blender BLENDER_SOURCES) 9 | -------------------------------------------------------------------------------- /lib/Blender/HMDL.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/Blender/Connection.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #undef min 11 | #undef max 12 | 13 | namespace hecl::blender { 14 | 15 | atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec) { 16 | atVec3f res; 17 | athena::simd_floats resf; 18 | athena::simd_floats mtxf[3]; 19 | for (int i = 0; i < 3; ++i) 20 | mtx[i].simd.copy_to(mtxf[i]); 21 | athena::simd_floats vecf(vec.val.simd); 22 | resf[0] = mtxf[0][0] * vecf[0] + mtxf[0][1] * vecf[1] + mtxf[0][2] * vecf[2] + mtxf[0][3]; 23 | resf[1] = mtxf[1][0] * vecf[0] + mtxf[1][1] * vecf[1] + mtxf[1][2] * vecf[2] + mtxf[1][3]; 24 | resf[2] = mtxf[2][0] * vecf[0] + mtxf[2][1] * vecf[1] + mtxf[2][2] * vecf[2] + mtxf[2][3]; 25 | res.simd.copy_from(resf); 26 | return res; 27 | } 28 | 29 | atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec) { 30 | atVec3f res; 31 | athena::simd_floats resf; 32 | athena::simd_floats mtxf[3]; 33 | for (int i = 0; i < 3; ++i) 34 | mtx[i].simd.copy_to(mtxf[i]); 35 | athena::simd_floats vecf(vec.val.simd); 36 | resf[0] = mtxf[0][0] * vecf[0] + mtxf[0][1] * vecf[1] + mtxf[0][2] * vecf[2]; 37 | resf[1] = mtxf[1][0] * vecf[0] + mtxf[1][1] * vecf[1] + mtxf[1][2] * vecf[2]; 38 | resf[2] = mtxf[2][0] * vecf[0] + mtxf[2][1] * vecf[1] + mtxf[2][2] * vecf[2]; 39 | res.simd.copy_from(resf); 40 | return res; 41 | } 42 | 43 | HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinIndex) const { 44 | /* If skinned, compute max weight vec count */ 45 | size_t weightCount = 0; 46 | for (const SkinBanks::Bank& bank : skinBanks.banks) 47 | weightCount = std::max(weightCount, bank.m_boneIdxs.size()); 48 | size_t weightVecCount = weightCount / 4; 49 | if (weightCount % 4) 50 | ++weightVecCount; 51 | 52 | /* Prepare HMDL meta */ 53 | HMDLMeta metaOut; 54 | metaOut.topology = topology; 55 | metaOut.vertStride = (3 + 3 + colorLayerCount + uvLayerCount * 2 + weightVecCount * 4) * 4; 56 | metaOut.colorCount = colorLayerCount; 57 | metaOut.uvCount = uvLayerCount; 58 | metaOut.weightCount = weightVecCount; 59 | metaOut.bankCount = skinBanks.banks.size(); 60 | 61 | /* Total all verts from all surfaces (for ibo length) */ 62 | size_t boundVerts = 0; 63 | for (const Surface& surf : surfaces) 64 | boundVerts += surf.verts.size(); 65 | 66 | /* Maintain unique vert pool for VBO */ 67 | std::vector> vertPool; 68 | vertPool.reserve(boundVerts); 69 | 70 | /* Target surfaces representation */ 71 | std::vector outSurfaces; 72 | outSurfaces.reserve(surfaces.size()); 73 | 74 | /* Index buffer */ 75 | std::vector iboData; 76 | iboData.reserve(boundVerts); 77 | 78 | for (const Surface& surf : surfaces) { 79 | size_t iboStart = iboData.size(); 80 | for (const Surface::Vert& v : surf.verts) { 81 | if (v.iPos == 0xffffffff) { 82 | iboData.push_back(0xffffffff); 83 | continue; 84 | } 85 | 86 | size_t ti = 0; 87 | bool found = false; 88 | for (const std::pair& tv : vertPool) { 89 | if (v == *tv.second && surf.skinBankIdx == tv.first->skinBankIdx) { 90 | iboData.push_back(ti); 91 | found = true; 92 | break; 93 | } 94 | ++ti; 95 | } 96 | if (!found) { 97 | iboData.push_back(vertPool.size()); 98 | vertPool.emplace_back(&surf, &v); 99 | } 100 | } 101 | outSurfaces.emplace_back(surf, iboStart, iboData.size() - iboStart); 102 | } 103 | 104 | metaOut.vertCount = vertPool.size(); 105 | metaOut.indexCount = iboData.size(); 106 | 107 | size_t vboSz = metaOut.vertCount * metaOut.vertStride; 108 | poolSkinIndex.allocate(vertPool.size()); 109 | HMDLBuffers ret(std::move(metaOut), vboSz, iboData, std::move(outSurfaces), skinBanks); 110 | athena::io::MemoryWriter vboW(ret.m_vboData.get(), vboSz); 111 | uint32_t curPoolIdx = 0; 112 | for (const std::pair& sv : vertPool) { 113 | const Surface& s = *sv.first; 114 | const Surface::Vert& v = *sv.second; 115 | 116 | if (absoluteCoords) { 117 | atVec3f preXfPos = MtxVecMul4RM(sceneXf, pos[v.iPos]); 118 | vboW.writeVec3fLittle(preXfPos); 119 | 120 | atVec3f preXfNorm = MtxVecMul3RM(sceneXf, norm[v.iNorm]); 121 | athena::simd_floats f(preXfNorm.simd * preXfNorm.simd); 122 | float mag = f[0] + f[1] + f[2]; 123 | if (mag > FLT_EPSILON) 124 | mag = 1.f / std::sqrt(mag); 125 | preXfNorm.simd *= mag; 126 | vboW.writeVec3fLittle(preXfNorm); 127 | } else { 128 | vboW.writeVec3fLittle(pos[v.iPos]); 129 | vboW.writeVec3fLittle(norm[v.iNorm]); 130 | } 131 | 132 | for (size_t i = 0; i < colorLayerCount; ++i) { 133 | const Vector3f& c = color[v.iColor[i]]; 134 | athena::simd_floats f(c.val.simd); 135 | vboW.writeUByte(std::max(0, std::min(255, int(f[0] * 255)))); 136 | vboW.writeUByte(std::max(0, std::min(255, int(f[1] * 255)))); 137 | vboW.writeUByte(std::max(0, std::min(255, int(f[2] * 255)))); 138 | vboW.writeUByte(255); 139 | } 140 | 141 | for (size_t i = 0; i < uvLayerCount; ++i) 142 | vboW.writeVec2fLittle(uv[v.iUv[i]]); 143 | 144 | if (weightVecCount) { 145 | const SkinBanks::Bank& bank = skinBanks.banks[s.skinBankIdx]; 146 | const auto& binds = skins[v.iSkin]; 147 | 148 | auto it = bank.m_boneIdxs.cbegin(); 149 | for (size_t i = 0; i < weightVecCount; ++i) { 150 | atVec4f vec = {}; 151 | for (size_t j = 0; j < 4; ++j) { 152 | if (it == bank.m_boneIdxs.cend()) 153 | break; 154 | for (const SkinBind& bind : binds) { 155 | if (!bind.valid()) 156 | break; 157 | if (bind.vg_idx == *it) { 158 | vec.simd[j] = bind.weight; 159 | break; 160 | } 161 | } 162 | ++it; 163 | } 164 | vboW.writeVec4fLittle(vec); 165 | } 166 | } 167 | 168 | /* mapping pool verts to skin indices */ 169 | poolSkinIndex.m_poolToSkinIndex[curPoolIdx] = sv.second->iSkin; 170 | ++curPoolIdx; 171 | } 172 | 173 | return ret; 174 | } 175 | 176 | } // namespace hecl::blender 177 | -------------------------------------------------------------------------------- /lib/Blender/MeshOptimizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "hecl/Blender/Connection.hpp" 12 | 13 | namespace hecl::blender { 14 | 15 | template 16 | class IndexArray { 17 | std::array arr; 18 | public: 19 | IndexArray() { std::fill(arr.begin(), arr.end(), UINT32_MAX); } 20 | class const_iterator { 21 | typename std::array::const_iterator it; 22 | public: 23 | explicit const_iterator(typename std::array::const_iterator i) : it(i) {} 24 | bool operator==(const_iterator other) const { return it == other.it; } 25 | bool operator!=(const_iterator other) const { return it != other.it; } 26 | const_iterator& operator++() { ++it; return *this; } 27 | uint32_t operator*() const { return *it; } 28 | }; 29 | const_iterator begin() const { return const_iterator(arr.cbegin()); } 30 | const_iterator end() const { 31 | typename std::array::const_iterator I, E; 32 | for (I = arr.cbegin(), E = arr.cend(); I != E && *I != UINT32_MAX; ++I) {} 33 | return const_iterator(I); 34 | } 35 | uint32_t& operator[](size_t idx) { return arr[idx]; } 36 | const uint32_t& operator[](size_t idx) const { return arr[idx]; } 37 | static constexpr size_t capacity() { return S; } 38 | size_t size() const { return end() - begin(); } 39 | }; 40 | 41 | class MeshOptimizer { 42 | static constexpr size_t MaxColorLayers = Mesh::MaxColorLayers; 43 | static constexpr size_t MaxUVLayers = Mesh::MaxUVLayers; 44 | static constexpr size_t MaxSkinEntries = Mesh::MaxSkinEntries; 45 | 46 | const std::vector& materials; 47 | bool use_luvs; 48 | 49 | uint32_t color_count; 50 | uint32_t uv_count; 51 | 52 | struct Vertex { 53 | Vector3f co = {}; 54 | std::array skin_ents = {}; 55 | explicit Vertex(Connection& conn); 56 | }; 57 | std::vector verts; 58 | 59 | struct Loop { 60 | Vector3f normal = {}; 61 | std::array colors = {}; 62 | std::array uvs = {}; 63 | uint32_t vert = UINT32_MAX; 64 | uint32_t edge = UINT32_MAX; 65 | uint32_t face = UINT32_MAX; 66 | uint32_t link_loop_next = UINT32_MAX; 67 | uint32_t link_loop_prev = UINT32_MAX; 68 | uint32_t link_loop_radial_next = UINT32_MAX; 69 | uint32_t link_loop_radial_prev = UINT32_MAX; 70 | explicit Loop(Connection& conn, uint32_t color_count, uint32_t uv_count); 71 | }; 72 | std::vector loops; 73 | 74 | struct Edge { 75 | static constexpr size_t MaxLinkFaces = 8; 76 | IndexArray<2> verts; 77 | IndexArray link_faces; 78 | bool is_contiguous = false; 79 | bool tag = false; 80 | explicit Edge(Connection& conn); 81 | }; 82 | std::vector edges; 83 | 84 | struct Face { 85 | Vector3f normal = {}; 86 | Vector3f centroid = {}; 87 | uint32_t material_index = UINT32_MAX; 88 | IndexArray<3> loops; 89 | explicit Face(Connection& conn); 90 | }; 91 | std::vector faces; 92 | 93 | std::unordered_map b_pos; 94 | std::unordered_map b_norm; 95 | std::unordered_map, uint32_t> b_skin; 96 | std::unordered_map b_color; 97 | std::unordered_map b_uv; 98 | std::unordered_map b_luv; 99 | 100 | uint32_t get_pos_idx(const Vertex& v) const; 101 | uint32_t get_norm_idx(const Loop& l) const; 102 | uint32_t get_skin_idx(const Vertex& v) const; 103 | uint32_t get_color_idx(const Loop& l, uint32_t cidx) const; 104 | uint32_t get_uv_idx(const Loop& l, uint32_t uidx) const; 105 | void sort_faces_by_skin_group(std::vector& faces) const; 106 | std::pair strip_next_loop(uint32_t prev_loop, uint32_t out_count) const; 107 | 108 | bool loops_contiguous(const Loop& la, const Loop& lb) const; 109 | bool splitable_edge(const Edge& e) const; 110 | Mesh::Surface generate_surface(std::vector& island_faces, uint32_t mat_idx) const; 111 | 112 | public: 113 | explicit MeshOptimizer(Connection& conn, const std::vector& materials, bool use_luvs); 114 | void optimize(Mesh& mesh, int max_skin_banks) const; 115 | }; 116 | 117 | } -------------------------------------------------------------------------------- /lib/Blender/SDNARead.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/Blender/SDNARead.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "hecl/hecl.hpp" 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | namespace hecl::blender { 14 | 15 | void SDNABlock::SDNAStruct::computeOffsets(const SDNABlock& block) { 16 | atUint32 offset = 0; 17 | for (SDNAField& f : fields) { 18 | const auto& name = block.names[f.name]; 19 | f.offset = offset; 20 | if (name.front() == '*') { 21 | offset += 8; 22 | } else { 23 | atUint32 length = block.tlens[f.type]; 24 | auto bracket = name.find('['); 25 | if (bracket != std::string::npos) 26 | length *= strtoul(name.data() + bracket + 1, nullptr, 10); 27 | offset += length; 28 | } 29 | } 30 | } 31 | 32 | const SDNABlock::SDNAStruct::SDNAField* SDNABlock::SDNAStruct::lookupField(const SDNABlock& block, 33 | const char* n) const { 34 | for (const SDNAField& field : fields) { 35 | const auto& name = block.names[field.name]; 36 | auto bracket = name.find('['); 37 | if (bracket != std::string::npos) { 38 | if (!name.compare(0, bracket, n)) 39 | return &field; 40 | } else if (name == n) 41 | return &field; 42 | } 43 | return nullptr; 44 | } 45 | 46 | const SDNABlock::SDNAStruct* SDNABlock::lookupStruct(const char* n, atUint32& idx) const { 47 | idx = 0; 48 | for (const SDNAStruct& strc : strcs) { 49 | const auto& name = types[strc.type]; 50 | if (name == n) 51 | return &strc; 52 | ++idx; 53 | } 54 | return nullptr; 55 | } 56 | 57 | void SDNARead::enumerate(const std::function& func) const { 58 | athena::io::MemoryReader r(m_data.data(), m_data.size()); 59 | r.seek(12); 60 | while (r.position() < r.length()) { 61 | FileBlock block; 62 | block.read(r); 63 | if (block.type == FOURCC('ENDB')) 64 | break; 65 | athena::io::MemoryReader r2(m_data.data() + r.position(), block.size); 66 | if (!func(block, r2)) 67 | break; 68 | r.seek(block.size); 69 | } 70 | } 71 | 72 | SDNARead::SDNARead(SystemStringView path) { 73 | athena::io::FileReader r(path); 74 | if (r.hasError()) 75 | return; 76 | 77 | atUint64 length = r.length(); 78 | char magicBuf[7]; 79 | r.readUBytesToBuf(magicBuf, 7); 80 | r.seek(0, athena::SeekOrigin::Begin); 81 | if (strncmp(magicBuf, "BLENDER", 7)) { 82 | /* Try gzip decompression */ 83 | std::unique_ptr compBuf(new uint8_t[4096]); 84 | m_data.resize((length * 2 + 4095) & ~4095); 85 | z_stream zstrm = {}; 86 | inflateInit2(&zstrm, 16 + MAX_WBITS); 87 | zstrm.next_out = (Bytef*)m_data.data(); 88 | zstrm.avail_out = m_data.size(); 89 | zstrm.total_out = 0; 90 | 91 | atUint64 rs; 92 | while ((rs = r.readUBytesToBuf(compBuf.get(), 4096))) { 93 | int inflateRet; 94 | zstrm.next_in = compBuf.get(); 95 | zstrm.avail_in = rs; 96 | while (zstrm.avail_in) { 97 | if (!zstrm.avail_out) { 98 | zstrm.avail_out = m_data.size(); 99 | m_data.resize(zstrm.avail_out * 2); 100 | zstrm.next_out = (Bytef*)m_data.data() + zstrm.avail_out; 101 | } 102 | inflateRet = inflate(&zstrm, Z_NO_FLUSH); 103 | if (inflateRet == Z_STREAM_END) 104 | break; 105 | if (inflateRet != Z_OK) { 106 | inflateEnd(&zstrm); 107 | m_data = std::vector(); 108 | return; 109 | } 110 | } 111 | if (inflateRet == Z_STREAM_END) 112 | break; 113 | } 114 | 115 | inflateEnd(&zstrm); 116 | 117 | if (strncmp((char*)m_data.data(), "BLENDER", 7)) { 118 | m_data = std::vector(); 119 | return; 120 | } 121 | } else { 122 | m_data.resize(length); 123 | r.readUBytesToBuf(m_data.data(), length); 124 | } 125 | 126 | enumerate([this](const FileBlock& block, athena::io::MemoryReader& r) { 127 | if (block.type == FOURCC('DNA1')) { 128 | m_sdnaBlock.read(r); 129 | for (SDNABlock::SDNAStruct& s : m_sdnaBlock.strcs) 130 | s.computeOffsets(m_sdnaBlock); 131 | return false; 132 | } 133 | return true; 134 | }); 135 | } 136 | 137 | BlendType GetBlendType(SystemStringView path) { 138 | SDNARead r(path); 139 | if (!r) 140 | return BlendType::None; 141 | 142 | atUint32 idPropIdx; 143 | const auto* idPropStruct = r.sdnaBlock().lookupStruct("IDProperty", idPropIdx); 144 | if (!idPropStruct) 145 | return BlendType::None; 146 | const auto* typeField = idPropStruct->lookupField(r.sdnaBlock(), "type"); 147 | if (!typeField) 148 | return BlendType::None; 149 | atUint32 typeOffset = typeField->offset; 150 | const auto* nameField = idPropStruct->lookupField(r.sdnaBlock(), "name"); 151 | if (!nameField) 152 | return BlendType::None; 153 | atUint32 nameOffset = nameField->offset; 154 | const auto* dataField = idPropStruct->lookupField(r.sdnaBlock(), "data"); 155 | if (!dataField) 156 | return BlendType::None; 157 | atUint32 dataOffset = dataField->offset; 158 | 159 | atUint32 idPropDataIdx; 160 | const auto* idPropDataStruct = r.sdnaBlock().lookupStruct("IDPropertyData", idPropDataIdx); 161 | if (!idPropDataStruct) 162 | return BlendType::None; 163 | const auto* valField = idPropDataStruct->lookupField(r.sdnaBlock(), "val"); 164 | if (!valField) 165 | return BlendType::None; 166 | atUint32 valOffset = dataOffset + valField->offset; 167 | 168 | BlendType ret = BlendType::None; 169 | r.enumerate( 170 | [idPropIdx, typeOffset, nameOffset, valOffset, &ret](const FileBlock& block, athena::io::MemoryReader& r) { 171 | if (block.type == FOURCC('DATA') && block.sdnaIdx == idPropIdx) { 172 | r.seek(typeOffset, athena::SeekOrigin::Begin); 173 | if (r.readUByte() != 1) 174 | return true; 175 | 176 | r.seek(nameOffset, athena::SeekOrigin::Begin); 177 | if (r.readString() != "hecl_type") 178 | return true; 179 | 180 | r.seek(valOffset, athena::SeekOrigin::Begin); 181 | ret = BlendType(r.readUint32Little()); 182 | return false; 183 | } 184 | return true; 185 | }); 186 | 187 | return ret; 188 | } 189 | 190 | } // namespace hecl::blender 191 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | macro(hecl_add_list rel_path a_list) 2 | unset(tmp_list) 3 | foreach(path IN LISTS ${a_list}) 4 | list(APPEND tmp_list "${rel_path}/${path}") 5 | endforeach(path) 6 | set(${a_list} "${tmp_list}" PARENT_SCOPE) 7 | endmacro(hecl_add_list) 8 | 9 | add_subdirectory(Blender) 10 | add_subdirectory(Runtime) 11 | 12 | if(WIN32) 13 | list(APPEND PLAT_SRCS ../include/hecl/winsupport.hpp) 14 | endif() 15 | 16 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") 17 | add_definitions(-DHECL_MULTIPROCESSOR) 18 | endif() 19 | 20 | set(HECL_HEADERS 21 | ../include/hecl/CVar.hpp 22 | ../include/hecl/CVarManager.hpp 23 | ../include/hecl/Console.hpp 24 | ../include/hecl/CVarCommons.hpp 25 | ../include/hecl/hecl.hpp 26 | ../include/hecl/MultiProgressPrinter.hpp 27 | ../include/hecl/FourCC.hpp 28 | ../include/hecl/TypedVariant.hpp 29 | ../include/hecl/HMDLMeta.hpp 30 | ../include/hecl/Backend.hpp 31 | ../include/hecl/Blender/Connection.hpp 32 | ../include/hecl/Blender/SDNARead.hpp 33 | ../include/hecl/Blender/Token.hpp 34 | ../include/hecl/SteamFinder.hpp 35 | ../include/hecl/Database.hpp 36 | ../include/hecl/Runtime.hpp 37 | ../include/hecl/ClientProcess.hpp 38 | ../include/hecl/SystemChar.hpp 39 | ../include/hecl/BitVector.hpp 40 | ../include/hecl/MathExtras.hpp 41 | ../include/hecl/UniformBufferPool.hpp 42 | ../include/hecl/VertexBufferPool.hpp 43 | ../include/hecl/PipelineBase.hpp 44 | ../include/hecl/Pipeline.hpp 45 | ../include/hecl/Compilers.hpp) 46 | set(COMMON_SOURCES 47 | hecl.cpp 48 | MultiProgressPrinter.cpp 49 | Project.cpp 50 | ProjectPath.cpp 51 | HumanizeNumber.cpp 52 | CVar.cpp 53 | CVarCommons.cpp 54 | CVarManager.cpp 55 | Console.cpp 56 | ClientProcess.cpp 57 | SteamFinder.cpp 58 | WideStringConvert.cpp 59 | Compilers.cpp 60 | Pipeline.cpp) 61 | 62 | if(UNIX) 63 | list(APPEND PLAT_SRCS closefrom.c) 64 | endif() 65 | 66 | add_library(hecl-full 67 | ${FRONTEND_SOURCES} 68 | ${RUNTIME_SOURCES} 69 | ${BLENDER_SOURCES} 70 | ${COMMON_SOURCES} 71 | ${HECL_HEADERS} 72 | ${PLAT_SRCS}) 73 | target_include_directories(hecl-full PUBLIC ../include) 74 | target_link_libraries(hecl-full PUBLIC ${HECL_APPLICATION_REPS_TARGETS_LIST} 75 | hecl-blender-addon boo athena-core logvisor) 76 | target_atdna(hecl-full atdna_HMDLMeta_full.cpp ../include/hecl/HMDLMeta.hpp) 77 | target_atdna(hecl-full atdna_CVar_full.cpp ../include/hecl/CVar.hpp) 78 | target_atdna(hecl-full atdna_SDNARead_full.cpp ../include/hecl/Blender/SDNARead.hpp) 79 | 80 | add_library(hecl-light 81 | ${RUNTIME_SOURCES} 82 | ${COMMON_SOURCES} 83 | ${HECL_HEADERS} 84 | ${PLAT_SRCS}) 85 | target_include_directories(hecl-light PUBLIC ../include) 86 | target_link_libraries(hecl-light PUBLIC ${HECL_APPLICATION_REPS_TARGETS_LIST} boo athena-core logvisor) 87 | target_atdna(hecl-light atdna_HMDLMeta_light.cpp ../include/hecl/HMDLMeta.hpp) 88 | target_atdna(hecl-light atdna_CVar_light.cpp ../include/hecl/CVar.hpp) 89 | 90 | add_library(hecl-compilers Compilers.cpp WideStringConvert.cpp) 91 | get_target_property(BOO_INCLUDES boo INTERFACE_INCLUDE_DIRECTORIES) 92 | target_include_directories(hecl-compilers PUBLIC ../include ${BOO_INCLUDES}) 93 | target_link_libraries(hecl-compilers PUBLIC athena-core logvisor xxhash 94 | glslang OSDependent OGLCompiler SPIRV glslang-default-resource-limits) 95 | 96 | if(COMMAND add_sanitizers) 97 | add_sanitizers(hecl-full) 98 | add_sanitizers(hecl-light) 99 | add_sanitizers(hecl-compilers) 100 | endif() 101 | 102 | if(WINDOWS_STORE) 103 | set_property(TARGET hecl-full PROPERTY VS_WINRT_COMPONENT TRUE) 104 | endif() 105 | -------------------------------------------------------------------------------- /lib/CVarCommons.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/CVarCommons.hpp" 2 | 3 | namespace hecl { 4 | 5 | namespace { 6 | CVarCommons* m_instance = nullptr; 7 | } 8 | 9 | CVarCommons::CVarCommons(CVarManager& manager) : m_mgr(manager) { 10 | m_fullscreen = m_mgr.findOrMakeCVar("fullscreen"sv, "Start in fullscreen"sv, false, 11 | hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive); 12 | m_graphicsApi = m_mgr.findOrMakeCVar("graphicsApi"sv, "API to use for rendering graphics"sv, DEFAULT_GRAPHICS_API, 13 | hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | 14 | hecl::CVar::EFlags::ModifyRestart); 15 | m_drawSamples = m_mgr.findOrMakeCVar("drawSamples"sv, "Number of MSAA samples to use for render targets"sv, 1, 16 | hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | 17 | hecl::CVar::EFlags::ModifyRestart); 18 | m_texAnisotropy = m_mgr.findOrMakeCVar( 19 | "texAnisotropy"sv, "Number of anisotropic samples to use for sampling textures"sv, 1, 20 | hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); 21 | m_deepColor = m_mgr.findOrMakeCVar("deepColor"sv, "Allow framebuffer with color depth greater-then 24-bits"sv, false, 22 | hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | 23 | hecl::CVar::EFlags::ModifyRestart); 24 | m_variableDt = m_mgr.findOrMakeCVar( 25 | "variableDt", "Enable variable delta time (experimental)", false, 26 | (hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart)); 27 | 28 | m_debugOverlayPlayerInfo = m_mgr.findOrMakeCVar( 29 | "debugOverlay.playerInfo"sv, "Displays information about the player, such as location and orientation"sv, false, 30 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 31 | m_debugOverlayWorldInfo = m_mgr.findOrMakeCVar( 32 | "debugOverlay.worldInfo"sv, "Displays information about the current world, such as world asset ID, and areaId"sv, 33 | false, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 34 | m_debugOverlayAreaInfo = m_mgr.findOrMakeCVar( 35 | "debugOverlay.areaInfo"sv, 36 | "Displays information about the current area, such as asset ID, object/layer counts, and active layer bits"sv, 37 | false, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 38 | m_debugOverlayShowFrameCounter = 39 | m_mgr.findOrMakeCVar("debugOverlay.showFrameCounter"sv, "Displays the current frame index"sv, false, 40 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 41 | m_debugOverlayShowFramerate = 42 | m_mgr.findOrMakeCVar("debugOverlay.showFramerate"sv, "Displays the current framerate"sv, false, 43 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 44 | m_debugOverlayShowInGameTime = 45 | m_mgr.findOrMakeCVar("debugOverlay.showInGameTime"sv, "Displays the current in game time"sv, false, 46 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 47 | m_debugOverlayShowRoomTimer = m_mgr.findOrMakeCVar( 48 | "debugOverlay.showRoomTimer", "Displays the current/last room timers in seconds and frames"sv, false, 49 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 50 | m_debugOverlayShowResourceStats = m_mgr.findOrMakeCVar( 51 | "debugOverlay.showResourceStats"sv, "Displays the current live resource object and token counts"sv, false, 52 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 53 | m_debugOverlayShowRandomStats = m_mgr.findOrMakeCVar( 54 | "debugOverlay.showRandomStats", "Displays the current number of random calls per frame"sv, false, 55 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 56 | m_debugToolDrawAiPath = 57 | m_mgr.findOrMakeCVar("debugTool.drawAiPath", "Draws the selected paths of any AI in the room"sv, false, 58 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 59 | m_debugToolDrawLighting = m_mgr.findOrMakeCVar("debugTool.drawLighting", "Draws the lighting setup in a room"sv, 60 | false, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::ReadOnly); 61 | m_debugToolDrawCollisionActors = 62 | m_mgr.findOrMakeCVar("debugTool.drawCollisionActors", "Draws the collision actors for enemies and objects"sv, 63 | false, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::ReadOnly); 64 | m_debugToolDrawMazePath = 65 | m_mgr.findOrMakeCVar("debugTool.drawMazePath", "Draws the maze path in Dynamo"sv, false, 66 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 67 | m_debugToolDrawPlatformCollision = 68 | m_mgr.findOrMakeCVar("debugTool.drawPlatformCollision", "Draws the bounding boxes of platforms"sv, false, 69 | hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); 70 | m_logFile = m_mgr.findOrMakeCVar("logFile"sv, "Any log prints will be stored to this file upon exit"sv, "app.log"sv, 71 | hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | 72 | hecl::CVar::EFlags::ModifyRestart); 73 | 74 | m_instance = this; 75 | } 76 | 77 | CVarCommons* CVarCommons::instance() { return m_instance; } 78 | } // namespace hecl -------------------------------------------------------------------------------- /lib/HumanizeNumber.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/hecl.hpp" 2 | #include "logvisor/logvisor.hpp" 3 | 4 | /* 5 | * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. 6 | * Copyright 2013 John-Mark Gurney 7 | * All rights reserved. 8 | * 9 | * This code is derived from software contributed to The NetBSD Foundation 10 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 11 | * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above copyright 19 | * notice, this list of conditions and the following disclaimer in the 20 | * documentation and/or other materials provided with the distribution. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | namespace hecl { 36 | static logvisor::Module Log("hecl::HumanizeNumber"); 37 | 38 | static const int maxscale = 7; 39 | 40 | std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags) { 41 | const char *prefixes, *sep; 42 | int i, remainder, s1, s2, sign; 43 | int divisordeccut; 44 | int64_t divisor, max; 45 | size_t baselen; 46 | 47 | /* validate args */ 48 | if (suffix == nullptr) 49 | suffix = ""; 50 | if ((flags & HNFlags::Divisor1000) != HNFlags::None && (flags & HNFlags::IECPrefixes) != HNFlags::None) 51 | Log.report(logvisor::Fatal, FMT_STRING("invalid flags combo")); 52 | 53 | /* setup parameters */ 54 | remainder = 0; 55 | 56 | if ((flags & HNFlags::IECPrefixes) != HNFlags::None) { 57 | baselen = 2; 58 | /* 59 | * Use the prefixes for power of two recommended by 60 | * the International Electrotechnical Commission 61 | * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...). 62 | * 63 | * HN_IEC_PREFIXES implies a divisor of 1024 here 64 | * (use of HN_DIVISOR_1000 would have triggered 65 | * an assertion earlier). 66 | */ 67 | divisor = 1024; 68 | divisordeccut = 973; /* ceil(.95 * 1024) */ 69 | if ((flags & HNFlags::B) != HNFlags::None) 70 | prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei"; 71 | else 72 | prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei"; 73 | } else { 74 | baselen = 1; 75 | if ((flags & HNFlags::Divisor1000) != HNFlags::None) { 76 | divisor = 1000; 77 | divisordeccut = 950; 78 | if ((flags & HNFlags::B) != HNFlags::None) 79 | prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E"; 80 | else 81 | prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E"; 82 | } else { 83 | divisor = 1024; 84 | divisordeccut = 973; /* ceil(.95 * 1024) */ 85 | if ((flags & HNFlags::B) != HNFlags::None) 86 | prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E"; 87 | else 88 | prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E"; 89 | } 90 | } 91 | 92 | #define SCALE2PREFIX(scale) (&prefixes[(scale)*3]) 93 | 94 | if (quotient < 0) { 95 | sign = -1; 96 | quotient = -quotient; 97 | baselen += 2; /* sign, digit */ 98 | } else { 99 | sign = 1; 100 | baselen += 1; /* digit */ 101 | } 102 | if ((flags & HNFlags::NoSpace) != HNFlags::None) 103 | sep = ""; 104 | else { 105 | sep = " "; 106 | baselen++; 107 | } 108 | baselen += strlen(suffix); 109 | 110 | /* Check if enough room for `x y' + suffix */ 111 | if (len < baselen) 112 | Log.report(logvisor::Fatal, FMT_STRING("buffer size {} insufficient for minimum size {}"), len, baselen); 113 | len += 1; 114 | 115 | if ((scale & int(HNScale::AutoScale)) != 0) { 116 | /* See if there is additional columns can be used. */ 117 | for (max = 1, i = len - baselen; i-- > 0;) 118 | max *= 10; 119 | 120 | /* 121 | * Divide the number until it fits the given column. 122 | * If there will be an overflow by the rounding below, 123 | * divide once more. 124 | */ 125 | for (i = 0; (quotient >= max || (quotient == max - 1 && remainder >= divisordeccut)) && i < maxscale; i++) { 126 | remainder = quotient % divisor; 127 | quotient /= divisor; 128 | } 129 | } else { 130 | for (i = 0; i < scale && i < maxscale; i++) { 131 | remainder = quotient % divisor; 132 | quotient /= divisor; 133 | } 134 | } 135 | 136 | /* If a value <= 9.9 after rounding and ... */ 137 | /* 138 | * XXX - should we make sure there is enough space for the decimal 139 | * place and if not, don't do HN_DECIMAL? 140 | */ 141 | if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) && i > 0 && 142 | (flags & HNFlags::Decimal) != HNFlags::None) { 143 | s1 = (int)quotient + ((remainder * 10 + divisor / 2) / divisor / 10); 144 | s2 = ((remainder * 10 + divisor / 2) / divisor) % 10; 145 | return fmt::format(FMT_STRING("{}{}{}{}{}{}"), sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i), suffix); 146 | } else 147 | return fmt::format(FMT_STRING("{}{}{}{}"), sign * (quotient + (remainder + divisor / 2) / divisor), sep, 148 | SCALE2PREFIX(i), suffix); 149 | } 150 | 151 | } // namespace hecl 152 | -------------------------------------------------------------------------------- /lib/Pipeline.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/Pipeline.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace hecl { 7 | 8 | #if HECL_RUNTIME 9 | 10 | PipelineConverterBase* conv = nullptr; 11 | 12 | class ShaderCacheZipStream : public athena::io::IStreamReader { 13 | std::unique_ptr m_compBuf; 14 | athena::io::FileReader m_reader; 15 | z_stream m_zstrm = {}; 16 | 17 | public: 18 | explicit ShaderCacheZipStream(const hecl::SystemChar* path) : m_reader(path) { 19 | if (m_reader.hasError()) 20 | return; 21 | if (m_reader.readUint32Big() != SBIG('SHAD')) 22 | return; 23 | m_compBuf.reset(new uint8_t[4096]); 24 | m_zstrm.next_in = m_compBuf.get(); 25 | m_zstrm.avail_in = 0; 26 | inflateInit(&m_zstrm); 27 | } 28 | ~ShaderCacheZipStream() override { inflateEnd(&m_zstrm); } 29 | explicit operator bool() const { return m_compBuf.operator bool(); } 30 | atUint64 readUBytesToBuf(void* buf, atUint64 len) override { 31 | m_zstrm.next_out = (Bytef*)buf; 32 | m_zstrm.avail_out = len; 33 | m_zstrm.total_out = 0; 34 | while (m_zstrm.avail_out != 0) { 35 | if (m_zstrm.avail_in == 0) { 36 | atUint64 readSz = m_reader.readUBytesToBuf(m_compBuf.get(), 4096); 37 | m_zstrm.avail_in = readSz; 38 | m_zstrm.next_in = m_compBuf.get(); 39 | } 40 | int inflateRet = inflate(&m_zstrm, Z_NO_FLUSH); 41 | if (inflateRet != Z_OK) 42 | break; 43 | } 44 | return m_zstrm.total_out; 45 | } 46 | void seek(atInt64, athena::SeekOrigin) override {} 47 | atUint64 position() const override { return 0; } 48 | atUint64 length() const override { return 0; } 49 | }; 50 | 51 | template 52 | void StageConverter::loadFromStream(FactoryCtx& ctx, ShaderCacheZipStream& r) { 53 | uint32_t count = r.readUint32Big(); 54 | for (uint32_t i = 0; i < count; ++i) { 55 | uint64_t hash = r.readUint64Big(); 56 | uint32_t size = r.readUint32Big(); 57 | StageBinaryData data = MakeStageBinaryData(size); 58 | r.readUBytesToBuf(data.get(), size); 59 | m_stageCache.insert(std::make_pair(hash, Do(ctx, StageBinary(data, size)))); 60 | } 61 | } 62 | 63 | static boo::AdditionalPipelineInfo ReadAdditionalInfo(ShaderCacheZipStream& r) { 64 | boo::AdditionalPipelineInfo additionalInfo; 65 | additionalInfo.srcFac = boo::BlendFactor(r.readUint32Big()); 66 | additionalInfo.dstFac = boo::BlendFactor(r.readUint32Big()); 67 | additionalInfo.prim = boo::Primitive(r.readUint32Big()); 68 | additionalInfo.depthTest = boo::ZTest(r.readUint32Big()); 69 | additionalInfo.depthWrite = r.readBool(); 70 | additionalInfo.colorWrite = r.readBool(); 71 | additionalInfo.alphaWrite = r.readBool(); 72 | additionalInfo.culling = boo::CullMode(r.readUint32Big()); 73 | additionalInfo.patchSize = r.readUint32Big(); 74 | additionalInfo.overwriteAlpha = r.readBool(); 75 | additionalInfo.depthAttachment = r.readBool(); 76 | return additionalInfo; 77 | } 78 | 79 | static std::vector ReadVertexFormat(ShaderCacheZipStream& r) { 80 | std::vector ret; 81 | uint32_t count = r.readUint32Big(); 82 | ret.reserve(count); 83 | 84 | for (uint32_t i = 0; i < count; ++i) { 85 | ret.emplace_back(); 86 | ret.back().semantic = boo::VertexSemantic(r.readUint32Big()); 87 | ret.back().semanticIdx = int(r.readUint32Big()); 88 | } 89 | 90 | return ret; 91 | } 92 | 93 | template 94 | bool PipelineConverter

::loadFromFile(FactoryCtx& ctx, const hecl::SystemChar* path) { 95 | ShaderCacheZipStream r(path); 96 | if (!r) 97 | return false; 98 | 99 | m_vertexConverter.loadFromStream(ctx, r); 100 | m_fragmentConverter.loadFromStream(ctx, r); 101 | m_geometryConverter.loadFromStream(ctx, r); 102 | m_controlConverter.loadFromStream(ctx, r); 103 | m_evaluationConverter.loadFromStream(ctx, r); 104 | 105 | uint32_t count = r.readUint32Big(); 106 | for (uint32_t i = 0; i < count; ++i) { 107 | uint64_t hash = r.readUint64Big(); 108 | StageRuntimeObject vertex; 109 | StageRuntimeObject fragment; 110 | StageRuntimeObject geometry; 111 | StageRuntimeObject control; 112 | StageRuntimeObject evaluation; 113 | if (uint64_t vhash = r.readUint64Big()) 114 | vertex = m_vertexConverter.m_stageCache.find(vhash)->second; 115 | if (uint64_t fhash = r.readUint64Big()) 116 | fragment = m_fragmentConverter.m_stageCache.find(fhash)->second; 117 | if (uint64_t ghash = r.readUint64Big()) 118 | geometry = m_geometryConverter.m_stageCache.find(ghash)->second; 119 | if (uint64_t chash = r.readUint64Big()) 120 | control = m_controlConverter.m_stageCache.find(chash)->second; 121 | if (uint64_t ehash = r.readUint64Big()) 122 | evaluation = m_evaluationConverter.m_stageCache.find(ehash)->second; 123 | 124 | boo::AdditionalPipelineInfo additionalInfo = ReadAdditionalInfo(r); 125 | std::vector vtxFmt = ReadVertexFormat(r); 126 | 127 | m_pipelineCache.insert( 128 | std::make_pair(hash, FinalPipeline

(*this, ctx, 129 | StageCollection>( 130 | vertex, fragment, geometry, control, evaluation, additionalInfo, 131 | boo::VertexFormatInfo(vtxFmt.size(), vtxFmt.data()))))); 132 | } 133 | 134 | return true; 135 | } 136 | 137 | #define SPECIALIZE_STAGE_CONVERTER(P) \ 138 | template class StageConverter; \ 139 | template class StageConverter; \ 140 | template class StageConverter; \ 141 | template class StageConverter; \ 142 | template class StageConverter; 143 | 144 | #if BOO_HAS_GL 145 | template class PipelineConverter; 146 | SPECIALIZE_STAGE_CONVERTER(PlatformType::OpenGL) 147 | #endif 148 | #if BOO_HAS_VULKAN 149 | template class PipelineConverter; 150 | SPECIALIZE_STAGE_CONVERTER(PlatformType::Vulkan) 151 | #endif 152 | #if _WIN32 153 | template class PipelineConverter; 154 | SPECIALIZE_STAGE_CONVERTER(PlatformType::D3D11) 155 | #endif 156 | #if BOO_HAS_METAL 157 | template class PipelineConverter; 158 | SPECIALIZE_STAGE_CONVERTER(PlatformType::Metal) 159 | #endif 160 | #if BOO_HAS_NX 161 | template class PipelineConverter; 162 | SPECIALIZE_STAGE_CONVERTER(PlatformType::NX) 163 | #endif 164 | 165 | #endif 166 | 167 | } // namespace hecl -------------------------------------------------------------------------------- /lib/Runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(RUNTIME_SOURCES 2 | FileStoreManager.cpp 3 | HMDL_RT.cpp) 4 | 5 | hecl_add_list(Runtime RUNTIME_SOURCES) 6 | -------------------------------------------------------------------------------- /lib/Runtime/FileStoreManager.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/Runtime.hpp" 2 | 3 | #include "hecl/hecl.hpp" 4 | 5 | #include 6 | 7 | #if _WIN32 8 | #include 9 | #endif 10 | 11 | #if WINDOWS_STORE 12 | using namespace Windows::Storage; 13 | #endif 14 | 15 | namespace hecl::Runtime { 16 | static logvisor::Module Log("FileStoreManager"); 17 | 18 | FileStoreManager::FileStoreManager(SystemStringView domain) : m_domain(domain) { 19 | #if _WIN32 20 | #if !WINDOWS_STORE 21 | WCHAR home[MAX_PATH]; 22 | if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, home))) 23 | Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to locate profile for file store"))); 24 | 25 | SystemString path(home); 26 | #else 27 | StorageFolder ^ cacheFolder = ApplicationData::Current->LocalCacheFolder; 28 | SystemString path(cacheFolder->Path->Data()); 29 | #endif 30 | path += _SYS_STR("/.heclrun"); 31 | 32 | hecl::MakeDir(path.c_str()); 33 | path += _SYS_STR('/'); 34 | path += domain.data(); 35 | 36 | hecl::MakeDir(path.c_str()); 37 | m_storeRoot = path; 38 | #else 39 | const char* xdg_data_home = getenv("XDG_DATA_HOME"); 40 | std::string path; 41 | if (xdg_data_home) { 42 | if (xdg_data_home[0] != '/') 43 | Log.report(logvisor::Fatal, FMT_STRING("invalid $XDG_DATA_HOME for file store (must be absolute)")); 44 | path = xdg_data_home; 45 | } else { 46 | const char* home = getenv("HOME"); 47 | if (!home) 48 | Log.report(logvisor::Fatal, FMT_STRING("unable to locate $HOME for file store")); 49 | path = home; 50 | path += "/.local/share"; 51 | } 52 | path += "/hecl/"; 53 | path += domain.data(); 54 | if (RecursiveMakeDir(path.c_str()) != 0) 55 | Log.report(logvisor::Fatal, FMT_STRING("unable to mkdir at {}"), path); 56 | m_storeRoot = path; 57 | #endif 58 | } 59 | 60 | } // namespace hecl::Runtime 61 | -------------------------------------------------------------------------------- /lib/Runtime/HMDL_RT.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/HMDLMeta.hpp" 2 | 3 | #include "hecl/Runtime.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace hecl::Runtime { 9 | static logvisor::Module HMDL_Log("HMDL"); 10 | 11 | HMDLData::HMDLData(boo::IGraphicsDataFactory::Context& ctx, const void* metaData, const void* vbo, const void* ibo) { 12 | HMDLMeta meta; 13 | { 14 | athena::io::MemoryReader r(metaData, HECL_HMDL_META_SZ); 15 | meta.read(r); 16 | } 17 | if (meta.magic != 'TACO') 18 | HMDL_Log.report(logvisor::Fatal, FMT_STRING("invalid HMDL magic")); 19 | 20 | m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vbo, meta.vertStride, meta.vertCount); 21 | m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, ibo, 4, meta.indexCount); 22 | 23 | const size_t elemCount = 2 + meta.colorCount + meta.uvCount + meta.weightCount; 24 | m_vtxFmtData = std::make_unique(elemCount); 25 | 26 | m_vtxFmtData[0].semantic = boo::VertexSemantic::Position3; 27 | m_vtxFmtData[1].semantic = boo::VertexSemantic::Normal3; 28 | size_t e = 2; 29 | 30 | for (size_t i = 0; i < meta.colorCount; ++i, ++e) { 31 | m_vtxFmtData[e].semantic = boo::VertexSemantic::ColorUNorm; 32 | m_vtxFmtData[e].semanticIdx = i; 33 | } 34 | 35 | for (size_t i = 0; i < meta.uvCount; ++i, ++e) { 36 | m_vtxFmtData[e].semantic = boo::VertexSemantic::UV2; 37 | m_vtxFmtData[e].semanticIdx = i; 38 | } 39 | 40 | for (size_t i = 0; i < meta.weightCount; ++i, ++e) { 41 | m_vtxFmtData[e].semantic = boo::VertexSemantic::Weight; 42 | m_vtxFmtData[e].semanticIdx = i; 43 | } 44 | 45 | m_vtxFmt = boo::VertexFormatInfo(elemCount, m_vtxFmtData.get()); 46 | } 47 | 48 | } // namespace hecl::Runtime 49 | -------------------------------------------------------------------------------- /lib/SteamFinder.cpp: -------------------------------------------------------------------------------- 1 | #include "hecl/SteamFinder.hpp" 2 | #include "hecl/hecl.hpp" 3 | #ifdef WIN32 4 | #include 5 | #define PATH_SEP L'\\' 6 | #else 7 | #define PATH_SEP '/' 8 | #endif 9 | 10 | namespace hecl { 11 | 12 | /* Used to extract alternate steam install directories from libraryfolders.vdf */ 13 | static const std::regex regSteamPath(R"(^\s+\"[0-9]+\"\s+\"(.*)\")", std::regex::ECMAScript | std::regex::optimize); 14 | 15 | hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name) { 16 | hecl::SystemString steamInstallDir; 17 | hecl::Sstat theStat; 18 | 19 | #ifdef WIN32 20 | #if !WINDOWS_STORE 21 | HKEY hkey; 22 | hecl::SystemChar _steamInstallDir[MAX_PATH] = {0}; 23 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), 0, KEY_QUERY_VALUE, &hkey) != 24 | ERROR_SUCCESS) { 25 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, 26 | &hkey) != ERROR_SUCCESS) 27 | return {}; 28 | } 29 | 30 | DWORD size = MAX_PATH; 31 | if (RegQueryValueEx(hkey, _SYS_STR("InstallPath"), nullptr, nullptr, (LPBYTE)_steamInstallDir, &size) == 32 | ERROR_SUCCESS) 33 | steamInstallDir = _steamInstallDir; 34 | RegCloseKey(hkey); 35 | 36 | if (steamInstallDir.empty()) 37 | return {}; 38 | #else 39 | return {}; 40 | #endif 41 | 42 | #elif defined(__APPLE__) 43 | steamInstallDir = getenv("HOME"); 44 | steamInstallDir += "/Library/Application Support/Steam"; 45 | if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) 46 | return {}; 47 | 48 | #else 49 | steamInstallDir = getenv("HOME"); 50 | steamInstallDir += "/.local/share/Steam"; 51 | if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) { 52 | steamInstallDir = getenv("HOME"); 53 | steamInstallDir += "/.steam/steam"; 54 | if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) 55 | return {}; 56 | } 57 | 58 | #endif 59 | 60 | const hecl::SystemString appPath = hecl::SystemString(_SYS_STR("common")) + PATH_SEP + name; 61 | 62 | /* Try main steam install directory first */ 63 | const hecl::SystemString steamAppsMain = steamInstallDir + PATH_SEP + _SYS_STR("steamapps"); 64 | const hecl::SystemString mainAppPath = steamAppsMain + PATH_SEP + appPath; 65 | if (!hecl::Stat(mainAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) { 66 | return mainAppPath; 67 | } 68 | 69 | /* Iterate alternate steam install dirs */ 70 | const hecl::SystemString libraryFoldersVdfPath = steamAppsMain + PATH_SEP + _SYS_STR("libraryfolders.vdf"); 71 | auto fp = hecl::FopenUnique(libraryFoldersVdfPath.c_str(), _SYS_STR("r")); 72 | if (fp == nullptr) { 73 | return {}; 74 | } 75 | hecl::FSeek(fp.get(), 0, SEEK_END); 76 | const int64_t fileLen = hecl::FTell(fp.get()); 77 | if (fileLen <= 0) { 78 | return {}; 79 | } 80 | hecl::FSeek(fp.get(), 0, SEEK_SET); 81 | std::string fileBuf(fileLen, '\0'); 82 | if (std::fread(fileBuf.data(), 1, fileLen, fp.get()) != fileLen) { 83 | return {}; 84 | } 85 | 86 | std::smatch dirMatch; 87 | auto begin = fileBuf.cbegin(); 88 | const auto end = fileBuf.cend(); 89 | while (std::regex_search(begin, end, dirMatch, regSteamPath)) { 90 | const std::string match = dirMatch[1].str(); 91 | const hecl::SystemStringConv otherInstallDir(match); 92 | const auto otherAppPath = 93 | hecl::SystemString(otherInstallDir.sys_str()) + PATH_SEP + _SYS_STR("steamapps") + PATH_SEP + appPath; 94 | 95 | if (!hecl::Stat(otherAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) { 96 | return otherAppPath; 97 | } 98 | 99 | begin = dirMatch.suffix().first; 100 | } 101 | 102 | return {}; 103 | } 104 | 105 | } // namespace hecl 106 | -------------------------------------------------------------------------------- /lib/WideStringConvert.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace hecl { 5 | static logvisor::Module Log("hecl-wsconv"); 6 | 7 | std::string WideToUTF8(std::wstring_view src) { 8 | std::string retval; 9 | retval.reserve(src.length()); 10 | for (wchar_t ch : src) { 11 | utf8proc_uint8_t mb[4]; 12 | utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb); 13 | if (c < 0) { 14 | Log.report(logvisor::Warning, FMT_STRING("invalid UTF-8 character while encoding")); 15 | return retval; 16 | } 17 | retval.append(reinterpret_cast(mb), c); 18 | } 19 | return retval; 20 | } 21 | 22 | std::string Char16ToUTF8(std::u16string_view src) { 23 | std::string retval; 24 | retval.reserve(src.length()); 25 | for (char16_t ch : src) { 26 | utf8proc_uint8_t mb[4]; 27 | utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb); 28 | if (c < 0) { 29 | Log.report(logvisor::Warning, FMT_STRING("invalid UTF-8 character while encoding")); 30 | return retval; 31 | } 32 | retval.append(reinterpret_cast(mb), c); 33 | } 34 | return retval; 35 | } 36 | 37 | std::wstring UTF8ToWide(std::string_view src) { 38 | std::wstring retval; 39 | retval.reserve(src.length()); 40 | const utf8proc_uint8_t* buf = reinterpret_cast(src.data()); 41 | while (*buf) { 42 | utf8proc_int32_t wc; 43 | utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc); 44 | if (len < 0) { 45 | Log.report(logvisor::Warning, FMT_STRING("invalid UTF-8 character while decoding")); 46 | return retval; 47 | } 48 | buf += len; 49 | retval += wchar_t(wc); 50 | } 51 | return retval; 52 | } 53 | 54 | std::u16string UTF8ToChar16(std::string_view src) { 55 | std::u16string retval; 56 | retval.reserve(src.length()); 57 | const utf8proc_uint8_t* buf = reinterpret_cast(src.data()); 58 | while (*buf) { 59 | utf8proc_int32_t wc; 60 | utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc); 61 | if (len < 0) { 62 | Log.report(logvisor::Warning, FMT_STRING("invalid UTF-8 character while decoding")); 63 | return retval; 64 | } 65 | buf += len; 66 | retval += char16_t(wc); 67 | } 68 | return retval; 69 | } 70 | 71 | } // namespace hecl 72 | -------------------------------------------------------------------------------- /lib/closefrom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Unix SMB/CIFS implementation. 3 | * Samba utility functions 4 | * Copyright (C) Volker Lendecke 2016 5 | * 6 | * ** NOTE! The following LGPL license applies to the replace 7 | * ** library. This does NOT imply that all of Samba is released 8 | * ** under the LGPL 9 | * 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU Lesser General Public 12 | * License as published by the Free Software Foundation; either 13 | * version 3 of the License, or (at your option) any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * Library General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Lesser General Public 21 | * License along with this library; if not, see . 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | static int closefrom_sysconf(int lower) 31 | { 32 | long max_files, fd; 33 | 34 | max_files = sysconf(_SC_OPEN_MAX); 35 | if (max_files == -1) { 36 | max_files = 65536; 37 | } 38 | 39 | for (fd=lower; fdd_name, &endptr, 10); 75 | if ((fd == 0) && (errno == EINVAL)) { 76 | continue; 77 | } 78 | if ((fd == ULLONG_MAX) && (errno == ERANGE)) { 79 | continue; 80 | } 81 | if (*endptr != '\0') { 82 | continue; 83 | } 84 | if (fd == dir_fd) { 85 | continue; 86 | } 87 | if (fd > INT_MAX) { 88 | continue; 89 | } 90 | if (fd < lower) { 91 | continue; 92 | } 93 | 94 | if (num_fds >= (fd_array_size / sizeof(int))) { 95 | void *tmp; 96 | 97 | if (fd_array_size == 0) { 98 | fd_array_size = 16 * sizeof(int); 99 | } else { 100 | if (fd_array_size + fd_array_size < 101 | fd_array_size) { 102 | /* overflow */ 103 | goto fail; 104 | } 105 | fd_array_size = fd_array_size + fd_array_size; 106 | } 107 | 108 | tmp = realloc(fds, fd_array_size); 109 | if (tmp == NULL) { 110 | goto fail; 111 | } 112 | fds = tmp; 113 | } 114 | 115 | fds[num_fds++] = fd; 116 | } 117 | 118 | for (i=0; i ARGS -o ${theOut} ${theInsList} 37 | DEPENDS ${theInsList} shaderc COMMENT "Compiling shader ${outRel}.shader") 38 | endfunction() 39 | -------------------------------------------------------------------------------- /shaderc/main.cpp: -------------------------------------------------------------------------------- 1 | #include "shaderc.hpp" 2 | #include "logvisor/logvisor.hpp" 3 | #include "athena/FileWriter.hpp" 4 | #include "glslang/Public/ShaderLang.h" 5 | #include "hecl/hecl.hpp" 6 | #include 7 | 8 | static logvisor::Module Log("shaderc"); 9 | 10 | #if _WIN32 11 | #include 12 | extern pD3DCompile D3DCompilePROC; 13 | pD3DCompile D3DCompilePROC = nullptr; 14 | 15 | static bool FindBestD3DCompile() { 16 | HMODULE d3dCompilelib = LoadLibraryW(L"D3DCompiler_47.dll"); 17 | if (!d3dCompilelib) { 18 | d3dCompilelib = LoadLibraryW(L"D3DCompiler_46.dll"); 19 | if (!d3dCompilelib) { 20 | d3dCompilelib = LoadLibraryW(L"D3DCompiler_45.dll"); 21 | if (!d3dCompilelib) { 22 | d3dCompilelib = LoadLibraryW(L"D3DCompiler_44.dll"); 23 | if (!d3dCompilelib) { 24 | d3dCompilelib = LoadLibraryW(L"D3DCompiler_43.dll"); 25 | } 26 | } 27 | } 28 | } 29 | if (d3dCompilelib) { 30 | D3DCompilePROC = (pD3DCompile)GetProcAddress(d3dCompilelib, "D3DCompile"); 31 | return D3DCompilePROC != nullptr; 32 | } 33 | return false; 34 | } 35 | 36 | int wmain(int argc, const hecl::SystemChar** argv) 37 | #else 38 | int main(int argc, const hecl::SystemChar** argv) 39 | #endif 40 | { 41 | logvisor::RegisterConsoleLogger(); 42 | logvisor::RegisterStandardExceptions(); 43 | 44 | #if _WIN32 45 | if (!FindBestD3DCompile()) { 46 | Log.report(logvisor::Info, FMT_STRING("Unable to find D3DCompiler dll")); 47 | return 1; 48 | } 49 | #endif 50 | 51 | if (argc == 1) { 52 | Log.report(logvisor::Info, FMT_STRING("Usage: shaderc -o [-D definevar=defineval]... ...")); 53 | return 0; 54 | } 55 | 56 | hecl::SystemString outPath; 57 | hecl::shaderc::Compiler c; 58 | for (int i = 1; i < argc; ++i) { 59 | if (argv[i][0] == '-') { 60 | if (argv[i][1] == 'o') { 61 | if (argv[i][2]) { 62 | outPath = &argv[i][2]; 63 | } else if (i + 1 < argc) { 64 | ++i; 65 | outPath = argv[i]; 66 | } else { 67 | Log.report(logvisor::Error, FMT_STRING("Invalid -o argument")); 68 | return 1; 69 | } 70 | } else if (argv[i][1] == 'D') { 71 | const hecl::SystemChar* define; 72 | if (argv[i][2]) { 73 | define = &argv[i][2]; 74 | } else if (i + 1 < argc) { 75 | ++i; 76 | define = argv[i]; 77 | } else { 78 | Log.report(logvisor::Error, FMT_STRING("Invalid -D argument")); 79 | return 1; 80 | } 81 | hecl::SystemUTF8Conv conv(define); 82 | const char* defineU8 = conv.c_str(); 83 | if (const char* equals = strchr(defineU8, '=')) 84 | c.addDefine(std::string(defineU8, equals - defineU8), equals + 1); 85 | else 86 | c.addDefine(defineU8, ""); 87 | } else { 88 | Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unrecognized flag option '{:c}'")), argv[i][1]); 89 | return 1; 90 | } 91 | } else { 92 | c.addInputFile(argv[i]); 93 | } 94 | } 95 | 96 | if (outPath.empty()) { 97 | Log.report(logvisor::Error, FMT_STRING("-o option is required")); 98 | return 1; 99 | } 100 | 101 | hecl::SystemStringView baseName; 102 | auto slashPos = outPath.find_last_of(_SYS_STR("/\\")); 103 | if (slashPos != hecl::SystemString::npos) 104 | baseName = outPath.data() + slashPos + 1; 105 | else 106 | baseName = outPath; 107 | 108 | if (!glslang::InitializeProcess()) { 109 | Log.report(logvisor::Error, FMT_STRING("Unable to initialize glslang")); 110 | return 1; 111 | } 112 | 113 | hecl::SystemUTF8Conv conv(baseName); 114 | std::pair ret; 115 | if (!c.compile(conv.str(), ret)) 116 | return 1; 117 | 118 | { 119 | hecl::SystemString headerPath = outPath + _SYS_STR(".hpp"); 120 | athena::io::FileWriter w(headerPath); 121 | if (w.hasError()) { 122 | Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Error opening '{}' for writing")), headerPath); 123 | return 1; 124 | } 125 | std::string header = ret.first.str(); 126 | w.writeBytes(header.data(), header.size()); 127 | } 128 | 129 | { 130 | hecl::SystemString impPath = outPath + _SYS_STR(".cpp"); 131 | athena::io::FileWriter w(impPath); 132 | if (w.hasError()) { 133 | Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Error opening '{}' for writing")), impPath); 134 | return 1; 135 | } 136 | std::string source = ret.second.str(); 137 | w.writeBytes(source.data(), source.size()); 138 | } 139 | 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /shaderc/shaderc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "hecl/SystemChar.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | namespace hecl::shaderc { 8 | 9 | class Compiler { 10 | enum class StageType { Vertex, Fragment, Geometry, Control, Evaluation }; 11 | 12 | std::vector m_inputFiles; 13 | std::unordered_map m_fileContents; 14 | const std::string* getFileContents(SystemStringView path); 15 | std::unordered_map m_defines; 16 | template 17 | static bool StageAction(StageType type, const std::string& name, const std::string& basename, 18 | const std::string& stage, std::stringstream& implOut); 19 | template 20 | static bool StageAction(const std::string& platforms, StageType type, const std::string& name, 21 | const std::string& basename, const std::string& stage, std::stringstream& implOut); 22 | bool includeFile(SystemStringView file, std::string& out, int depth = 0); 23 | bool compileFile(SystemStringView file, std::string_view baseName, 24 | std::pair& out); 25 | 26 | public: 27 | void addInputFile(SystemStringView file); 28 | void addDefine(std::string_view var, std::string_view val); 29 | bool compile(std::string_view baseName, std::pair& out); 30 | }; 31 | 32 | } // namespace hecl::shaderc 33 | --------------------------------------------------------------------------------