├── .gitignore ├── include ├── decima │ ├── serializable │ │ ├── serializable.hpp │ │ ├── object │ │ │ ├── resource │ │ │ │ ├── resource.hpp │ │ │ │ ├── primitive_resource.hpp │ │ │ │ ├── index_array_resource.hpp │ │ │ │ └── vertex_array_resource.hpp │ │ │ ├── object_dummy.hpp │ │ │ ├── collection.hpp │ │ │ ├── prefetch.hpp │ │ │ ├── common │ │ │ │ ├── bbox.hpp │ │ │ │ └── vec.hpp │ │ │ ├── object.hpp │ │ │ ├── translation.hpp │ │ │ ├── texture.hpp │ │ │ └── texture_set.hpp │ │ ├── handlers.hpp │ │ ├── stream.hpp │ │ ├── string.hpp │ │ ├── guid.hpp │ │ ├── reference.hpp │ │ └── array.hpp │ ├── archive │ │ ├── archive_file.hpp │ │ ├── archive_manager.hpp │ │ ├── archive_tree.hpp │ │ └── archive.hpp │ └── shared.hpp ├── utils.hpp ├── app.hpp ├── projectds_app.hpp └── util │ ├── compressor.hpp │ └── buffer.hpp ├── .gitmodules ├── src ├── decima │ ├── serializable │ │ ├── object │ │ │ ├── collection_draw.cpp │ │ │ ├── collection.cpp │ │ │ ├── object.cpp │ │ │ ├── object_dummy.cpp │ │ │ ├── prefetch.cpp │ │ │ ├── resource │ │ │ │ ├── index_array_resource.cpp │ │ │ │ ├── primitive_resource.cpp │ │ │ │ ├── vertex_array_resource.cpp │ │ │ │ ├── index_array_resource_draw.cpp │ │ │ │ ├── primitive_resource_draw.cpp │ │ │ │ └── vertex_array_resource_draw.cpp │ │ │ ├── translation.cpp │ │ │ ├── prefetch_draw.cpp │ │ │ ├── object_draw.cpp │ │ │ ├── translation_draw.cpp │ │ │ ├── texture_set.cpp │ │ │ ├── texture.cpp │ │ │ ├── texture_set_draw.cpp │ │ │ └── texture_draw.cpp │ │ ├── guid.cpp │ │ ├── stream.cpp │ │ ├── string.cpp │ │ ├── reference.cpp │ │ └── handlers.cpp │ └── archive │ │ ├── archive.cpp │ │ ├── archive_manager.cpp │ │ ├── archive_file.cpp │ │ └── archive_tree.cpp ├── main.cpp ├── utils.cpp ├── projectds_app.cpp ├── app.cpp └── projectds_app_draw.cpp ├── cmake_subprojects ├── glad │ └── CMakeLists.txt ├── hash │ └── CMakeLists.txt └── imgui │ └── CMakeLists.txt ├── CMakeLists.txt ├── LICENSE ├── .github └── workflows │ └── cmake.yml ├── libs ├── hash │ ├── MurmurHash3.h │ ├── md5.h │ ├── MurmurHash3.cpp │ └── md5.c └── glad │ └── include │ └── KHR │ └── khrplatform.h ├── README.md ├── ProjectDS.cmake └── .clang-format /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-* 2 | .idea 3 | TEST_DATA -------------------------------------------------------------------------------- /include/decima/serializable/serializable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Decima { 4 | class CoreSerializable { 5 | }; 6 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/glfw"] 2 | path = libs/glfw 3 | url = https://github.com/glfw/glfw 4 | [submodule "libs/imgui"] 5 | path = libs/imgui 6 | url = https://github.com/ocornut/imgui 7 | -------------------------------------------------------------------------------- /include/decima/serializable/object/resource/resource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/object.hpp" 4 | 5 | namespace Decima { 6 | class Resource : public CoreObject { 7 | }; 8 | } -------------------------------------------------------------------------------- /src/decima/serializable/object/collection_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/collection.hpp" 2 | 3 | void Decima::Collection::draw() { 4 | for (auto& ref : refs.data()) { 5 | ref.draw(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/decima/serializable/object/collection.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/collection.hpp" 2 | 3 | void Decima::Collection::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 4 | CoreObject::parse(manager, buffer, file); 5 | refs.parse(buffer, file); 6 | } 7 | -------------------------------------------------------------------------------- /src/decima/serializable/object/object.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/object.hpp" 2 | 3 | namespace Decima { 4 | void CoreObject::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 5 | header = buffer.get(); 6 | guid.parse(buffer, file); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/decima/serializable/object/object_dummy.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/object_dummy.hpp" 2 | 3 | void Decima::Dummy::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 4 | CoreObject::parse(manager, buffer, file); 5 | buffer = buffer.skip(header.file_size - sizeof(Decima::GUID)); 6 | } 7 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by i.getsman on 06.08.2020. 3 | // 4 | 5 | #include "projectds_app.hpp" 6 | 7 | int main() { 8 | Decima::ArchiveManager manager; 9 | 10 | ProjectDS app(std::move(manager), { 1700, 720 }, "Project Decima [" DECIMA_VERSION "]"); 11 | app.init(); 12 | app.run(); 13 | } 14 | -------------------------------------------------------------------------------- /include/decima/serializable/object/object_dummy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/object.hpp" 4 | 5 | namespace Decima { 6 | class Dummy : public Decima::CoreObject { 7 | public: 8 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/decima/serializable/guid.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/guid.hpp" 2 | 3 | #include 4 | 5 | void Decima::GUID::parse(ash::buffer& buffer, CoreFile& file) { 6 | buffer.get(m_data_1.data(), sizeof(m_data_1)); 7 | } 8 | 9 | void Decima::GUID::draw() { 10 | ImGui::Text("%s", Decima::to_string(*this).c_str()); 11 | } 12 | -------------------------------------------------------------------------------- /cmake_subprojects/glad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(glad) 3 | 4 | set(GLAD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/glad/src) 5 | set(GLAD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/glad/include) 6 | 7 | add_library(glad STATIC 8 | ${GLAD_SOURCE_DIR}/glad.c) 9 | 10 | target_include_directories(glad PUBLIC ${GLAD_INCLUDE_DIR}) 11 | -------------------------------------------------------------------------------- /cmake_subprojects/hash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(hash) 3 | 4 | set(HASH_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/hash) 5 | set(HASH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/hash) 6 | 7 | add_library(hash STATIC 8 | ${HASH_SOURCE_DIR}/md5.c 9 | ${HASH_SOURCE_DIR}/MurmurHash3.cpp) 10 | 11 | target_include_directories(hash PUBLIC ${HASH_INCLUDE_DIR}) 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(ProjectDS) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8) 8 | message(FATAL_ERROR "64-bit platform is required") 9 | endif () 10 | 11 | add_subdirectory(cmake_subprojects/hash) 12 | add_subdirectory(cmake_subprojects/glad) 13 | add_subdirectory(cmake_subprojects/imgui) 14 | add_subdirectory(libs/glfw) 15 | 16 | include(ProjectDS.cmake) -------------------------------------------------------------------------------- /include/decima/serializable/handlers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Decima { 8 | class CoreObject; 9 | 10 | template 11 | using Handler = std::function(Args&&...)>; 12 | 13 | std::invoke_result_t> get_type_handler(std::uint64_t hash); 14 | 15 | std::string get_type_name(std::uint64_t hash); 16 | } -------------------------------------------------------------------------------- /include/decima/serializable/object/collection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/object.hpp" 4 | #include "decima/serializable/array.hpp" 5 | #include "decima/serializable/reference.hpp" 6 | 7 | namespace Decima { 8 | class Collection : public CoreObject { 9 | public: 10 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 11 | void draw() override; 12 | 13 | public: 14 | Array refs; 15 | }; 16 | } -------------------------------------------------------------------------------- /src/decima/serializable/object/prefetch.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/prefetch.hpp" 2 | 3 | #include 4 | 5 | void Decima::Prefetch::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 6 | CoreObject::parse(manager, buffer, file); 7 | paths.parse(buffer, file); 8 | sizes.parse(buffer, file); 9 | links_total = buffer.get(); 10 | std::generate_n(std::back_inserter(links), paths.data().size(), [&] { 11 | Array link; 12 | link.parse(buffer, file); 13 | return std::move(link); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /src/decima/serializable/object/resource/index_array_resource.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/resource/index_array_resource.hpp" 2 | 3 | void Decima::IndexArrayResource::parse(Decima::ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 4 | CoreObject::parse(manager, buffer, file); 5 | indices_count = buffer.get(); 6 | if (indices_count > 0) { 7 | flags = buffer.get(); 8 | type = buffer.get(); 9 | is_streaming = buffer.get(); 10 | resource_uuid.parse(buffer, file); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /include/decima/serializable/object/prefetch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "object.hpp" 7 | #include "decima/serializable/array.hpp" 8 | #include "decima/serializable/string.hpp" 9 | 10 | namespace Decima { 11 | class Prefetch : public CoreObject { 12 | public: 13 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 14 | void draw() override; 15 | 16 | public: 17 | Array paths; 18 | Array sizes; 19 | std::uint32_t links_total; 20 | std::vector> links; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/decima/serializable/object/resource/primitive_resource.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/resource/primitive_resource.hpp" 2 | 3 | void Decima::PrimitiveResource::parse(Decima::ArchiveManager& manager, ash::buffer& buffer, Decima::CoreFile& file) { 4 | CoreObject::parse(manager, buffer, file); 5 | flags = buffer.get(); 6 | vertex_array.parse(buffer, file); 7 | index_array.parse(buffer, file); 8 | bounding_box = buffer.get(); 9 | skd_tree.parse(buffer, file); 10 | start_index = buffer.get(); 11 | end_index = buffer.get(); 12 | hash = buffer.get(); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /include/decima/serializable/object/common/bbox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/common/vec.hpp" 4 | 5 | namespace Decima { 6 | class BBox2 { 7 | public: 8 | Vec2 min; 9 | Vec2 max; 10 | }; 11 | 12 | class BBox3 { 13 | public: 14 | Vec3 min; 15 | Vec3 max; 16 | }; 17 | } 18 | 19 | template <> 20 | inline std::string Decima::to_string(const BBox2& value) { 21 | return Decima::to_string(value.min) + " - " + Decima::to_string(value.max); 22 | } 23 | 24 | template <> 25 | inline std::string Decima::to_string(const BBox3& value) { 26 | return Decima::to_string(value.min) + " - " + Decima::to_string(value.max); 27 | } 28 | -------------------------------------------------------------------------------- /cmake_subprojects/imgui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(imgui) 3 | 4 | set(IMGUI_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/imgui) 5 | set(IMGUI_INCLUDE_DIR 6 | ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/imgui 7 | ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/imgui/examples 8 | ${CMAKE_CURRENT_SOURCE_DIR}/) 9 | 10 | add_library(imgui STATIC 11 | ${IMGUI_SOURCE_DIR}/imgui.cpp 12 | ${IMGUI_SOURCE_DIR}/imgui_draw.cpp 13 | ${IMGUI_SOURCE_DIR}/imgui_widgets.cpp 14 | ${IMGUI_SOURCE_DIR}/examples/imgui_impl_opengl3.cpp 15 | ${IMGUI_SOURCE_DIR}/examples/imgui_impl_glfw.cpp) 16 | 17 | target_include_directories(imgui PUBLIC ${IMGUI_INCLUDE_DIR}) 18 | target_link_libraries(imgui PRIVATE opengl32 glfw glad) -------------------------------------------------------------------------------- /include/decima/serializable/object/resource/primitive_resource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/resource/resource.hpp" 4 | #include "decima/serializable/object/common/bbox.hpp" 5 | #include "decima/serializable/reference.hpp" 6 | 7 | namespace Decima { 8 | class PrimitiveResource : public Resource { 9 | public: 10 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 11 | void draw() override; 12 | 13 | public: 14 | std::uint32_t flags; 15 | Ref vertex_array; 16 | Ref index_array; 17 | BBox3 bounding_box; 18 | Ref skd_tree; 19 | std::uint32_t start_index; 20 | std::uint32_t end_index; 21 | std::uint32_t hash; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /include/decima/serializable/stream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/archive/archive_manager.hpp" 4 | #include "decima/serializable/string.hpp" 5 | 6 | namespace Decima { 7 | class Stream { 8 | public: 9 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file); 10 | void draw(); 11 | 12 | inline const String& name() const noexcept { return m_name; } 13 | inline std::uint32_t offset() const noexcept { return m_offs; } 14 | inline std::uint32_t size() const noexcept { return m_size; } 15 | inline const std::vector& data() const noexcept { return m_data; } 16 | 17 | private: 18 | String m_name; 19 | std::uint32_t m_offs; 20 | std::uint32_t m_size; 21 | std::vector m_data; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/decima/serializable/object/translation.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/translation.hpp" 2 | 3 | static std::string read_string(ash::buffer& buffer, const std::string& default_value = "") { 4 | const auto length = buffer.get(); 5 | 6 | if (length > 0) { 7 | std::string string(length, '\0'); 8 | buffer.get(string); 9 | return string; 10 | } 11 | 12 | return default_value; 13 | } 14 | 15 | void Decima::Translation::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 16 | CoreObject::parse(manager, buffer, file); 17 | 18 | for (uint32_t i = 0; i < std::size(languages); i++) { 19 | translations[i] = read_string(buffer); 20 | comments[i] = read_string(buffer); 21 | flags[i] = buffer.get(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /include/decima/serializable/object/common/vec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "decima/shared.hpp" 6 | 7 | namespace Decima { 8 | class Vec2 { 9 | public: 10 | float x; 11 | float y; 12 | }; 13 | 14 | class Vec3 { 15 | public: 16 | float x; 17 | float y; 18 | float z; 19 | }; 20 | } 21 | 22 | template <> 23 | inline std::string Decima::to_string(const Vec2& value) { 24 | std::stringstream buffer; 25 | buffer << '(' << std::scientific << value.x << ", " << value.y << ')'; 26 | return buffer.str(); 27 | } 28 | 29 | template <> 30 | inline std::string Decima::to_string(const Vec3& value) { 31 | std::stringstream buffer; 32 | buffer << '(' << std::scientific << value.x << ", " << value.y << ", " << value.z << ')'; 33 | return buffer.str(); 34 | } 35 | -------------------------------------------------------------------------------- /include/decima/serializable/object/object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "decima/serializable/guid.hpp" 7 | #include "decima/serializable/serializable.hpp" 8 | #include "decima/shared.hpp" 9 | 10 | class ProjectDS; 11 | 12 | namespace Decima { 13 | class ArchiveManager; 14 | class CoreFile; 15 | 16 | DECIMA_PACK(struct CoreHeader { 17 | std::uint64_t file_type; 18 | std::uint32_t file_size; 19 | }); 20 | 21 | class CoreObject : public CoreSerializable { 22 | public: 23 | virtual void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file); 24 | virtual void draw(); 25 | 26 | inline static CoreHeader peek_header(ash::buffer buffer) { 27 | return buffer.get(); 28 | } 29 | 30 | public: 31 | CoreHeader header; 32 | GUID guid; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /include/decima/archive/archive_file.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Decima { 7 | class Archive; 8 | class ArchiveManager; 9 | class ArchiveFileEntry; 10 | class CoreObject; 11 | class Ref; 12 | 13 | class CoreFile { 14 | public: 15 | CoreFile(Archive& archive, ArchiveManager& manager, ArchiveFileEntry& entry, std::ifstream& source); 16 | 17 | void parse(); 18 | 19 | void queue_reference(Ref*); 20 | void resolve_reference(const std::shared_ptr&); 21 | void resolve_reference(const CoreFile&); 22 | 23 | private: 24 | Archive& archive; 25 | ArchiveManager& manager; 26 | ArchiveFileEntry& entry; 27 | 28 | public: 29 | std::vector contents; 30 | std::vector, std::size_t>> objects; 31 | std::vector references; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /src/decima/serializable/object/prefetch_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/prefetch.hpp" 2 | 3 | #include 4 | 5 | void Decima::Prefetch::draw() { 6 | ImGui::Columns(2); 7 | 8 | ImGui::Text("Path"); 9 | ImGui::NextColumn(); 10 | 11 | ImGui::Text("Size"); 12 | ImGui::NextColumn(); 13 | 14 | ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() - 200); 15 | ImGui::SetColumnWidth(1, 200); 16 | 17 | ImGui::Separator(); 18 | 19 | ImGuiListClipper clipper(paths.data().size()); 20 | 21 | while (clipper.Step()) { 22 | for (auto index = clipper.DisplayStart; index < clipper.DisplayEnd; index++) { 23 | ImGui::TextUnformatted(paths.data().at(index).data().data()); 24 | ImGui::NextColumn(); 25 | 26 | ImGui::Text("%d bytes", sizes.data().at(index)); 27 | ImGui::NextColumn(); 28 | 29 | ImGui::Separator(); 30 | } 31 | } 32 | 33 | ImGui::Columns(1); 34 | } 35 | -------------------------------------------------------------------------------- /src/decima/serializable/object/object_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/object.hpp" 2 | 3 | #include 4 | 5 | #include "utils.hpp" 6 | 7 | void Decima::CoreObject::draw() { 8 | ImGui::TextDisabled("Default handler"); 9 | ImGui::Columns(2); 10 | ImGui::SetColumnWidth(-1, 200); 11 | ImGui::Text("Name"); 12 | ImGui::NextColumn(); 13 | ImGui::Text("Value"); 14 | ImGui::NextColumn(); 15 | 16 | ImGui::Separator(); 17 | 18 | ImGui::Text("Magic"); 19 | ImGui::NextColumn(); 20 | ImGui::Text("%s", uint64_to_hex(header.file_type).c_str()); 21 | if (ImGui::BeginPopupContextItem("File magic")) { 22 | if (ImGui::Selectable("Copy file magic")) 23 | ImGui::SetClipboardText(uint64_to_hex(header.file_type).c_str()); 24 | 25 | ImGui::EndPopup(); 26 | } 27 | ImGui::NextColumn(); 28 | 29 | ImGui::Separator(); 30 | 31 | ImGui::Text("Size"); 32 | ImGui::NextColumn(); 33 | ImGui::Text("%u", header.file_size + 12); 34 | 35 | ImGui::Columns(1); 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 REDxEYE & ShadelessFox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/decima/serializable/object/resource/index_array_resource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/resource/resource.hpp" 4 | #include "decima/serializable/array.hpp" 5 | #include "decima/shared.hpp" 6 | 7 | namespace Decima { 8 | enum class IndexArrayType : std::uint32_t { 9 | Index16 = 0, 10 | Index32 = 1 11 | }; 12 | 13 | class IndexArrayResource : public Resource { 14 | public: 15 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 16 | void draw() override; 17 | 18 | public: 19 | std::uint32_t indices_count; 20 | std::uint32_t flags; 21 | IndexArrayType type; 22 | std::uint32_t is_streaming; 23 | GUID resource_uuid; 24 | }; 25 | } 26 | 27 | template <> 28 | inline std::string Decima::to_string(const IndexArrayType& value) { 29 | switch (value) { 30 | case IndexArrayType::Index16: 31 | return "Index16"; 32 | case IndexArrayType::Index32: 33 | return "Index32"; 34 | default: 35 | return "Unknown: " + std::to_string(static_cast(value)); 36 | } 37 | } -------------------------------------------------------------------------------- /src/decima/serializable/object/translation_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/translation.hpp" 2 | 3 | #include 4 | 5 | void Decima::Translation::draw() { 6 | ImGui::Columns(4); 7 | ImGui::SetColumnWidth(-1, 200); 8 | ImGui::Text("Language"); 9 | ImGui::NextColumn(); 10 | ImGui::Text("Value"); 11 | ImGui::NextColumn(); 12 | ImGui::Text("Comment"); 13 | ImGui::NextColumn(); 14 | ImGui::Text("Flag"); 15 | ImGui::NextColumn(); 16 | 17 | for (std::size_t index = 0; index < std::size(Decima::Translation::languages); index++) { 18 | ImGui::Separator(); 19 | ImGui::Text("%s", Decima::Translation::languages[index]); 20 | ImGui::NextColumn(); 21 | ImGui::TextWrapped("%s", translations[index].c_str()); 22 | ImGui::NextColumn(); 23 | ImGui::TextWrapped("%s", comments[index].c_str()); 24 | ImGui::NextColumn(); 25 | if(flags[index]) { 26 | ImGui::Text("%d", flags[index]); 27 | } else { 28 | ImGui::TextDisabled("%d", flags[index]); 29 | } 30 | ImGui::NextColumn(); 31 | } 32 | 33 | ImGui::Columns(1); 34 | } 35 | -------------------------------------------------------------------------------- /include/decima/serializable/string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "decima/shared.hpp" 6 | #include "decima/archive/archive_file.hpp" 7 | #include "decima/serializable/serializable.hpp" 8 | #include "util/buffer.hpp" 9 | 10 | namespace Decima { 11 | using StringMutator = std::function; 12 | 13 | class String : public CoreSerializable { 14 | public: 15 | void parse(ash::buffer& buffer, CoreFile& file); 16 | void draw(); 17 | void draw(StringMutator mutator); 18 | 19 | inline const std::string& data() const noexcept { return m_data; } 20 | 21 | protected: 22 | std::string m_data; 23 | }; 24 | 25 | class StringHashed : public CoreSerializable { 26 | public: 27 | void parse(ash::buffer& buffer, CoreFile& file); 28 | void draw(); 29 | void draw(StringMutator mutator); 30 | 31 | inline std::uint32_t hash() const noexcept { return m_hash; } 32 | inline const std::string& data() const noexcept { return m_data; } 33 | 34 | private: 35 | std::uint32_t m_hash; 36 | std::string m_data; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake Build Windows 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | jobs: 10 | build: 11 | # The CMake configure and build commands are platform agnostic and should work equally 12 | # well on Windows or Mac. You can convert this to a matrix build if you need 13 | # cross-platform coverage. 14 | # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow#configuring-a-build-matrix 15 | runs-on: windows-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | submodules: 'recursive' 21 | 22 | - name: Create Build Environment 23 | shell: cmd 24 | run: cmake -E make_directory ${{runner.workspace}}/build 25 | 26 | - name: Configure CMake 27 | shell: cmd 28 | working-directory: ${{runner.workspace}}/build 29 | run: cmake %GITHUB_WORKSPACE% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% 30 | 31 | - name: Build 32 | working-directory: ${{runner.workspace}}/build 33 | shell: cmd 34 | run: cmake --build . --config %BUILD_TYPE% 35 | -------------------------------------------------------------------------------- /libs/hash/MurmurHash3.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MurmurHash3 was written by Austin Appleby, and is placed in the public 3 | // domain. The author hereby disclaims copyright to this source code. 4 | 5 | #ifndef _MURMURHASH3_H_ 6 | #define _MURMURHASH3_H_ 7 | 8 | //----------------------------------------------------------------------------- 9 | // Platform-specific functions and macros 10 | 11 | // Microsoft Visual Studio 12 | 13 | #if defined(_MSC_VER) && (_MSC_VER < 1600) 14 | 15 | typedef unsigned char uint8_t; 16 | typedef unsigned int uint32_t; 17 | typedef unsigned __int64 uint64_t; 18 | 19 | // Other compilers 20 | 21 | #else // defined(_MSC_VER) 22 | 23 | #include 24 | 25 | #endif // !defined(_MSC_VER) 26 | 27 | //----------------------------------------------------------------------------- 28 | 29 | void MurmurHash3_x86_32(const void * key, int len, uint32_t seed, void * out); 30 | 31 | void MurmurHash3_x86_128(const void * key, int len, uint32_t seed, void * out); 32 | 33 | void MurmurHash3_x64_128(const void * key, int len, uint32_t seed, void * out); 34 | 35 | //----------------------------------------------------------------------------- 36 | 37 | #endif // _MURMURHASH3_H_ -------------------------------------------------------------------------------- /include/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | std::string uint64_to_hex(uint64_t value); 10 | 11 | uint64_t hash_string(const std::string& filename, uint8_t seed); 12 | 13 | std::string sanitize_name(std::string filename); 14 | 15 | void split(const std::string& str, std::vector& cont, char delim); 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | inline std::string format_size(std::size_t size) noexcept { 22 | using FormatInfo = std::pair; 23 | 24 | static const std::vector formats { 25 | FormatInfo { "B", 0 }, 26 | FormatInfo { "KB", 2 }, 27 | FormatInfo { "MB", 1 }, 28 | FormatInfo { "GB", 1 }, 29 | FormatInfo { "TB", 1 } 30 | }; 31 | 32 | const auto format_order = std::min(formats.size(), std::log(size) / 6.931471805599453); 33 | const auto [format_name, format_precision] = formats[format_order]; 34 | 35 | std::stringstream buffer; 36 | buffer << std::fixed << std::setprecision(format_precision) 37 | << (size / std::pow(1024, format_order)) 38 | << ' ' << format_name; 39 | 40 | return buffer.str(); 41 | } 42 | -------------------------------------------------------------------------------- /include/app.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define WIN32_LEAN_AND_MEAN 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | struct WindowInfo { 14 | uint32_t m_win_width; 15 | uint32_t m_win_height; 16 | std::string m_title; 17 | }; 18 | 19 | class App { 20 | public: 21 | App(std::pair window_size, std::string title); 22 | 23 | void init(); 24 | 25 | void run(); 26 | 27 | private: 28 | double m_prev_time = glfwGetTime(); 29 | 30 | void init_glfw(); 31 | 32 | void init_opengl(); 33 | 34 | void update(double ts); 35 | 36 | void begin_frame(); 37 | 38 | void end_frame(); 39 | 40 | void glfw_error_handler(int error, const char* message); 41 | 42 | protected: 43 | virtual void init_user(); 44 | 45 | virtual void update_user(double ts); 46 | 47 | virtual void input_user(); 48 | 49 | virtual void begin_frame_user(); 50 | 51 | virtual void end_frame_user(); 52 | 53 | inline void clear_window(float r = 0, float g = 0, float b = 0, float a = 1, 54 | uint32_t flags = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 55 | 56 | WindowInfo m_win_info; 57 | GLFWwindow* m_window; 58 | }; 59 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by MED45 on 26.07.2020. 3 | // 4 | #include 5 | #include 6 | 7 | #include "utils.hpp" 8 | #include 9 | 10 | std::string uint64_to_hex(uint64_t value) { 11 | char hash[32]; 12 | sprintf(hash, "%llx", value); 13 | return std::string(hash); 14 | } 15 | 16 | uint64_t hash_string(const std::string& filename, uint8_t seed) { 17 | std::uint64_t hash[2]; 18 | MurmurHash3_x64_128(filename.c_str(), (int32_t)filename.size() + 1, seed, &hash); 19 | return hash[0]; 20 | } 21 | 22 | std::string sanitize_name(std::string filename) { 23 | std::replace(filename.begin(), filename.end(), '\\', '/'); 24 | 25 | const auto extension = filename.substr(filename.rfind('.') + 1); 26 | 27 | if (extension != "stream" && extension != "core") 28 | return filename + ".core"; 29 | 30 | return filename; 31 | } 32 | 33 | void split(const std::string& str, std::vector& cont, char delim) { 34 | std::size_t offset = 0; 35 | 36 | for (std::size_t index = 0; index < str.size(); index++) { 37 | if (str[index] == delim) { 38 | cont.emplace_back(str.substr(offset, index - offset)); 39 | offset = index + 1; 40 | } 41 | } 42 | 43 | cont.emplace_back(str.substr(offset)); 44 | } 45 | -------------------------------------------------------------------------------- /include/decima/serializable/object/translation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/object.hpp" 4 | 5 | namespace Decima { 6 | class Translation : public CoreObject { 7 | public: 8 | static constexpr const char* languages[] { 9 | "English", 10 | "French", 11 | "Spanish", 12 | "German", 13 | "Italian", 14 | "Dutch", 15 | "Portuguese", 16 | "Chinese (Traditional)", 17 | "Korean", 18 | "Russian", 19 | "Polish", 20 | "Danish", 21 | "Finnish", 22 | "Norwegian", 23 | "Swedish", 24 | "Japanese", 25 | "Spanish (Mexico)", 26 | "Portuguese (Brazil)", 27 | "Turkish", 28 | "Arabic", 29 | "Chinese (Simplified)", 30 | "English (UK)", 31 | "Greek", 32 | "Czech", 33 | "Hungarian" 34 | }; 35 | 36 | std::string translations[std::size(languages)]; 37 | std::string comments[std::size(languages)]; 38 | std::uint8_t flags[std::size(languages)]; 39 | 40 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 41 | void draw() override; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /include/decima/archive/archive_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "decima/archive/archive.hpp" 7 | #include "decima/serializable/object/prefetch.hpp" 8 | #include "decima/shared.hpp" 9 | #include "util/compressor.hpp" 10 | 11 | namespace Decima { 12 | class CoreFile; 13 | 14 | class ArchiveManager { 15 | public: 16 | void load_archive(const std::string& path); 17 | void load_prefetch(); 18 | 19 | [[nodiscard]] Decima::OptionalRef query_file(std::uint64_t hash); 20 | [[nodiscard]] Decima::OptionalRef query_file(const std::string& name); 21 | 22 | [[nodiscard]] Decima::OptionalRef get_file_entry(std::uint64_t hash); 23 | [[nodiscard]] Decima::OptionalRef get_file_entry(const std::string& name); 24 | 25 | std::unordered_map hash_to_archive_index; 26 | 27 | // TODO: GUI-related, must be removed 28 | std::unordered_map hash_to_name; 29 | std::unordered_map hash_to_index; 30 | 31 | std::vector archives; 32 | std::unique_ptr compressor; 33 | std::unique_ptr prefetch; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/decima/serializable/object/resource/vertex_array_resource.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/resource/vertex_array_resource.hpp" 2 | 3 | #include 4 | 5 | void Decima::VertexStreamData::parse(ash::buffer& buffer, CoreFile& file) { 6 | offset = buffer.get(); 7 | storage_type = buffer.get(); 8 | slots_used = buffer.get(); 9 | element_type = buffer.get(); 10 | } 11 | 12 | void Decima::VertexStreamInfo::parse(ash::buffer& buffer, CoreFile& file) { 13 | flags = buffer.get(); 14 | stride = buffer.get(); 15 | descriptors.parse(buffer, file); 16 | resource_uuid.parse(buffer, file); 17 | } 18 | 19 | void Decima::VertexArrayResource::parse(Decima::ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 20 | CoreObject::parse(manager, buffer, file); 21 | vertex_count = buffer.get(); 22 | vertex_stream_count = buffer.get(); 23 | is_streaming = buffer.get(); 24 | vertex_stream_info.resize(vertex_stream_count); 25 | std::generate(vertex_stream_info.begin(), vertex_stream_info.end(), [&] { 26 | VertexStreamInfo info; 27 | info.parse(buffer, file); 28 | return info; 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /src/decima/serializable/stream.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/stream.hpp" 2 | 3 | #include 4 | 5 | void Decima::Stream::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 6 | m_name.parse(buffer, file); 7 | buffer = buffer.skip(20); 8 | m_offs = buffer.get(); 9 | m_size = buffer.get(); 10 | 11 | auto& stream_file = manager.query_file(m_name.data() + ".core.stream").value().get(); 12 | m_data = stream_file.contents; 13 | } 14 | 15 | void Decima::Stream::draw() { 16 | ImGui::Columns(2); 17 | { 18 | ImGui::SetColumnWidth(-1, 80); 19 | ImGui::Text("Field"); 20 | ImGui::NextColumn(); 21 | ImGui::Text("Value"); 22 | ImGui::NextColumn(); 23 | 24 | ImGui::Separator(); 25 | 26 | ImGui::Text("Name"); 27 | ImGui::NextColumn(); 28 | m_name.draw([](const auto& name) { return name + ".core.stream"; }); 29 | ImGui::NextColumn(); 30 | 31 | ImGui::Separator(); 32 | 33 | ImGui::Text("Offset"); 34 | ImGui::NextColumn(); 35 | ImGui::Text("%u", m_offs); 36 | ImGui::NextColumn(); 37 | 38 | ImGui::Separator(); 39 | 40 | ImGui::Text("Length"); 41 | ImGui::NextColumn(); 42 | ImGui::Text("%u", m_size); 43 | ImGui::NextColumn(); 44 | } 45 | ImGui::Columns(1); 46 | } 47 | -------------------------------------------------------------------------------- /include/decima/serializable/guid.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "decima/shared.hpp" 7 | #include "util/buffer.hpp" 8 | 9 | namespace Decima { 10 | class CoreFile; 11 | 12 | class GUID { 13 | public: 14 | void parse(ash::buffer& buffer, CoreFile& file); 15 | void draw(); 16 | 17 | inline std::array data() const noexcept { return m_data_1; } 18 | 19 | inline std::size_t hash() const noexcept { 20 | constexpr std::hash hash {}; 21 | return hash(m_data_8[0]) ^ hash(m_data_8[1]); 22 | } 23 | 24 | private: 25 | friend std::string Decima::to_string(const Decima::GUID& value); 26 | 27 | union { 28 | std::array m_data_1; 29 | std::array m_data_8; 30 | }; 31 | }; 32 | 33 | static_assert(sizeof(GUID) == 16); 34 | } 35 | 36 | template <> 37 | inline std::string Decima::to_string(const Decima::GUID& value) { 38 | std::string buffer(36, ' '); 39 | std::sprintf(buffer.data(), "%08x-%04x-%04x-%04x-%012llx", 40 | std::uint32_t(value.m_data_8[0] >> 32 & 0xffffffff), 41 | std::uint16_t(value.m_data_8[0] >> 16 & 0xffff), 42 | std::uint16_t(value.m_data_8[0] >> 0 & 0xffff), 43 | std::uint16_t(value.m_data_8[1] >> 48 & 0xffff), 44 | std::uint64_t(value.m_data_8[1] >> 0 & 0xffffffffffff)); 45 | return buffer; 46 | } -------------------------------------------------------------------------------- /src/projectds_app.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by i.getsman on 06.08.2020. 3 | // 4 | 5 | #include "projectds_app.hpp" 6 | 7 | #include "imgui.h" 8 | #include "imgui_impl_glfw.h" 9 | #include "imgui_impl_opengl3.h" 10 | 11 | ProjectDS::ProjectDS(Decima::ArchiveManager&& manager, const std::pair& windowSize, const std::string& title) 12 | : App(windowSize, title) 13 | , archive_manager(std::move(manager)) {} 14 | 15 | void ProjectDS::begin_frame_user() { 16 | App::begin_frame_user(); 17 | ImGui_ImplGlfw_NewFrame(); 18 | ImGui::NewFrame(); 19 | 20 | } 21 | 22 | void ProjectDS::end_frame_user() { 23 | App::end_frame_user(); 24 | ImGui::Render(); 25 | ImGuiIO& io = ImGui::GetIO(); 26 | 27 | if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { 28 | GLFWwindow* backup_current_context = glfwGetCurrentContext(); 29 | ImGui::UpdatePlatformWindows(); 30 | ImGui::RenderPlatformWindowsDefault(); 31 | glfwMakeContextCurrent(backup_current_context); 32 | } 33 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 34 | 35 | } 36 | 37 | void ProjectDS::init_imgui() { 38 | IMGUI_CHECKVERSION(); 39 | ImGui::CreateContext(); 40 | 41 | ImGuiIO& io = ImGui::GetIO(); 42 | if (m_multi_viewport) { 43 | io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; 44 | } 45 | 46 | ImGui::StyleColorsDark(); 47 | 48 | ImGui_ImplGlfw_InitForOpenGL(m_window, true); 49 | ImGui_ImplOpenGL3_Init("#version 150"); 50 | ImGui_ImplOpenGL3_NewFrame(); 51 | } 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/decima/serializable/string.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/string.hpp" 2 | 3 | #include 4 | 5 | static void string_draw(const std::string& data, Decima::StringMutator mutator) { 6 | ImGui::TextWrapped("%s", data.c_str()); 7 | 8 | if (ImGui::BeginPopupContextItem(data.c_str())) { 9 | if (ImGui::Selectable("Copy to clipboard")) 10 | ImGui::SetClipboardText(mutator(data).c_str()); 11 | ImGui::EndPopup(); 12 | } 13 | } 14 | 15 | static void string_draw(const std::string& data) { 16 | static const Decima::StringMutator default_mutator = [](const auto& str) { return str; }; 17 | string_draw(data, default_mutator); 18 | } 19 | 20 | void Decima::String::parse(ash::buffer& buffer, CoreFile& file) { 21 | const auto size = buffer.get(); 22 | 23 | if (size > 0) { 24 | m_data.resize(size); 25 | buffer.get(m_data); 26 | } 27 | } 28 | 29 | void Decima::String::draw() { 30 | string_draw(m_data); 31 | } 32 | 33 | void Decima::String::draw(Decima::StringMutator mutator) { 34 | string_draw(m_data, mutator); 35 | } 36 | 37 | void Decima::StringHashed::parse(ash::buffer& buffer, CoreFile& file) { 38 | const auto size = buffer.get(); 39 | 40 | if (size > 0) { 41 | m_hash = buffer.get(); 42 | m_data.resize(size); 43 | buffer.get(m_data); 44 | } 45 | } 46 | 47 | void Decima::StringHashed::draw() { 48 | string_draw(m_data); 49 | } 50 | 51 | void Decima::StringHashed::draw(Decima::StringMutator mutator) { 52 | string_draw(m_data, mutator); 53 | } 54 | -------------------------------------------------------------------------------- /include/projectds_app.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "app.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #include "decima/archive/archive_manager.hpp" 9 | #include "decima/archive/archive_tree.hpp" 10 | 11 | class ProjectDS : public App { 12 | public: 13 | enum class Popup { 14 | None, 15 | About, 16 | AppendExportByName, 17 | AppendExportByHash, 18 | Shortcuts 19 | }; 20 | 21 | struct ShortcutInfo { 22 | std::string_view name; 23 | std::string_view description; 24 | std::size_t key; 25 | ImGuiKeyModFlags mods; 26 | std::function callback; 27 | }; 28 | 29 | public: 30 | ProjectDS(Decima::ArchiveManager&&, const std::pair& windowSize, const std::string& title); 31 | 32 | public: 33 | bool m_multi_viewport; 34 | Popup current_popup = Popup::None; 35 | std::vector shortcuts; 36 | 37 | Decima::ArchiveManager archive_manager; 38 | std::vector file_names; 39 | FileTree root_tree; 40 | SelectionInfo selection_info; 41 | ImGuiTextFilter filter; 42 | MemoryEditor file_viewer; 43 | bool root_tree_constructing { false }; 44 | 45 | void init_user() override; 46 | 47 | void init_imgui(); 48 | 49 | void init_filetype_handlers(); 50 | 51 | protected: 52 | void update_user(double ts) override; 53 | void input_user() override; 54 | 55 | void draw_dockspace(); 56 | void draw_filepreview(); 57 | void draw_tree(); 58 | void draw_export(); 59 | 60 | void begin_frame_user() override; 61 | 62 | void end_frame_user() override; 63 | }; 64 | -------------------------------------------------------------------------------- /src/decima/serializable/object/resource/index_array_resource_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/resource/index_array_resource.hpp" 2 | 3 | #include 4 | 5 | void Decima::IndexArrayResource::draw() { 6 | ImGui::Columns(2); 7 | 8 | { 9 | ImGui::Text("Property"); 10 | ImGui::NextColumn(); 11 | 12 | ImGui::Text("Value"); 13 | ImGui::NextColumn(); 14 | 15 | ImGui::Separator(); 16 | } 17 | 18 | ImGui::SetColumnWidth(0, 200); 19 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 20 | 21 | { 22 | ImGui::Text("Indices count"); 23 | ImGui::NextColumn(); 24 | 25 | ImGui::Text("%u", indices_count); 26 | ImGui::NextColumn(); 27 | 28 | ImGui::Separator(); 29 | } 30 | 31 | { 32 | ImGui::Text("Flags"); 33 | ImGui::NextColumn(); 34 | 35 | ImGui::Text("%u", flags); 36 | ImGui::NextColumn(); 37 | 38 | ImGui::Separator(); 39 | } 40 | 41 | { 42 | ImGui::Text("Index type"); 43 | ImGui::NextColumn(); 44 | 45 | ImGui::Text("%s", Decima::to_string(type).c_str()); 46 | ImGui::NextColumn(); 47 | 48 | ImGui::Separator(); 49 | } 50 | 51 | { 52 | ImGui::Text("Is streaming"); 53 | ImGui::NextColumn(); 54 | 55 | ImGui::Text("%u", is_streaming); 56 | ImGui::NextColumn(); 57 | 58 | ImGui::Separator(); 59 | } 60 | 61 | { 62 | ImGui::Text("Resource UUID"); 63 | ImGui::NextColumn(); 64 | 65 | ImGui::Text("%s", Decima::to_string(resource_uuid).c_str()); 66 | ImGui::NextColumn(); 67 | } 68 | 69 | ImGui::Columns(1); 70 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### This project is no longer under development. See https://github.com/ShadelessFox/decima 2 | 3 | This project is aimed to provide graphical user interface for previewing, exporting and modifying game resources in games based on Decima Engine in research purposes. 4 | 5 | ![The program interface showing texture of Sam's face](https://i.imgur.com/0yv9GsU.png) 6 | 7 | ## People who made this project possible 8 | * [Wunkolo](https://github.com/Wunkolo) who reverse-engineered the format of game archives and extracted keys required for decryption 9 | * [Yretenai](https://github.com/yretenai) who at early steps gave basic understanding how game works 10 | * [Nukem9](https://github.com/Nukem9) who from nowhere kicked out the door and provided priceless intelligence about RTTI data that game uses to load its resources 11 | 12 | ## Building 13 | ### Requirements: 14 | * Windows 10 64-bit _(note: this is the only supported platform)_ 15 | * CMake 3.10 or greater 16 | * Any of listed compilers: 17 | * Visual Studio 2019 18 | * Clang 19 | * MinGW x64 20 | 21 | ### Steps: 22 | Open console and run following commands: 23 | 1. ```git clone --recursive https://github.com/REDxEYE/ProjectDecima.git``` 24 | 1. ```cd ProjectDecima``` 25 | 1. ```git checkout development``` to get all latest features *(Optional)* 26 | 1. ```cmake CMakeLists.txt -G "Visual Studio 16 2019" -B build``` 27 | 1. ```cmake --build build --config Release``` 28 | 29 | ## Copyright 30 | * [Library 'imgui'](https://github.com/ocornut/imgui) by [ocornut](https://github.com/ocornut) 31 | * [Library 'mio'](https://github.com/mandreyel/mio) by [mandreyel](https://github.com/mandreyel) 32 | * All rights to the Decima Engine, its source code and all games based on this engine belong to their developers. 33 | -------------------------------------------------------------------------------- /include/decima/archive/archive_tree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "decima/archive/archive_file.hpp" 9 | #include "decima/serializable/object/object.hpp" 10 | 11 | struct FileInfo { 12 | std::string path; 13 | std::string name; 14 | std::uint64_t hash; 15 | 16 | inline operator bool() const { 17 | return hash != 0; 18 | } 19 | }; 20 | 21 | struct SelectionInfo { 22 | SelectionInfo() = default; 23 | std::uint64_t preview_file { 0 }; 24 | std::uint64_t preview_file_size { 0 }; 25 | std::uint64_t preview_file_offset { 0 }; 26 | std::uint64_t selected_file { 0 }; 27 | FileInfo highlighted_file; 28 | std::set selected_files; 29 | Decima::CoreFile* file; 30 | }; 31 | 32 | template 33 | using FileTreeToggleable = std::pair; 34 | 35 | class FileTree { 36 | public: 37 | enum class ExpandMode { 38 | None, 39 | Hide, 40 | Show 41 | }; 42 | 43 | static constexpr std::size_t ExpandThreshold = 15; 44 | 45 | std::map>> folders; 46 | std::map> files; 47 | 48 | FileTree* add_folder(const std::string& name); 49 | void add_file(const std::string& filename, uint64_t hash, Decima::CoreHeader header); 50 | void add_file(const std::string& path, const std::string& filename, uint64_t hash, Decima::CoreHeader header); 51 | 52 | ExpandMode apply_filter(const ImGuiTextFilter& filter); 53 | void reset_filter(bool visibility); 54 | 55 | void draw(SelectionInfo& selection, Decima::ArchiveManager& manager, bool header, ExpandMode expand); 56 | 57 | std::size_t size() const; 58 | }; 59 | -------------------------------------------------------------------------------- /include/decima/shared.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Decima { 8 | constexpr static uint8_t cipher_seed = 0x2A; 9 | constexpr static uint32_t plain_cipher_key[4] = { 0x0FA3A9443, 0x0F41CAB62, 0x0F376811C, 0x0D2A89E3E }; 10 | constexpr static uint32_t chunk_cipher_key[4] = { 0x06C084A37, 0x07E159D95, 0x03D5AF7E8, 0x018AA7D3F }; 11 | 12 | template 13 | using OptionalRef = std::optional>; 14 | 15 | template 16 | std::string to_string(const T&); 17 | } 18 | 19 | #ifdef _MSC_VER 20 | #define DECIMA_PACK(_Def) __pragma(pack(push, 1)) _Def __pragma(pack(pop)) 21 | #else 22 | #define DECIMA_PACK(_Def) _Def __attribute__((packed)) 23 | #endif 24 | 25 | #define __DECIMA_STR2(s) #s 26 | #define __DECIMA_STR1(s) __DECIMA_STR2(s) 27 | 28 | #define DECIMA_VERSION_MAJOR 0 29 | #define DECIMA_VERSION_MINOR 1 30 | #define DECIMA_VERSION_MICRO 8 31 | 32 | // clang-format off 33 | #define DECIMA_VERSION \ 34 | __DECIMA_STR1(DECIMA_VERSION_MAJOR) "." \ 35 | __DECIMA_STR1(DECIMA_VERSION_MINOR) "." \ 36 | __DECIMA_STR1(DECIMA_VERSION_MICRO) 37 | // clang-format on 38 | 39 | #define DECIMA_LOG(...) (_decima_log(_decima_filename(__FILE__), ':', __LINE__, ' ', __VA_ARGS__)) 40 | 41 | template 42 | inline constexpr void _decima_log(Args&&... args) { 43 | (std::cout << ... << args) << '\n'; 44 | } 45 | 46 | template 47 | inline constexpr const char* _decima_filename(const char (&path)[Size]) { 48 | std::size_t separator_offset = 0; 49 | 50 | for (std::size_t index = 0; index < Size; index++) { 51 | if (path[index] == '/' || path[index] == '\\') { 52 | separator_offset = index + 1; 53 | } 54 | } 55 | 56 | return path + separator_offset; 57 | } -------------------------------------------------------------------------------- /include/decima/serializable/reference.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/guid.hpp" 4 | #include "decima/serializable/serializable.hpp" 5 | #include "decima/serializable/string.hpp" 6 | 7 | namespace Decima { 8 | class ArchiveManager; 9 | class CoreFile; 10 | class CoreObject; 11 | 12 | enum class RefLoadMode : std::uint8_t { 13 | NotPresent = 0, 14 | Embedded = 1, 15 | ImmediateCoreFile = 2, 16 | CoreFile = 3, 17 | WorkOnly = 5 18 | }; 19 | 20 | class Ref : public CoreSerializable { 21 | public: 22 | void parse(ash::buffer& buffer, CoreFile& file); 23 | void draw(); 24 | 25 | inline RefLoadMode mode() const { return m_mode; } 26 | inline GUID guid() const { return m_guid; } 27 | inline StringHashed file() const { return m_file; } 28 | inline const std::shared_ptr& target() const { return m_object; } 29 | inline const std::shared_ptr& owner() const { return m_owner; } 30 | 31 | private: 32 | friend class CoreFile; 33 | 34 | RefLoadMode m_mode; 35 | GUID m_guid; 36 | StringHashed m_file; 37 | std::shared_ptr m_object; 38 | std::shared_ptr m_owner; 39 | bool m_show_object { false }; 40 | }; 41 | } 42 | 43 | template <> 44 | inline std::string Decima::to_string(const Decima::RefLoadMode& value) { 45 | switch (value) { 46 | case RefLoadMode::NotPresent: 47 | return "Not present"; 48 | case RefLoadMode::Embedded: 49 | return "Embedded"; 50 | case RefLoadMode::ImmediateCoreFile: 51 | case RefLoadMode::CoreFile: 52 | return "CoreFile"; 53 | case RefLoadMode::WorkOnly: 54 | return "Work only"; 55 | default: 56 | return "Unknown: " + std::to_string(static_cast(value)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/decima/serializable/object/texture_set.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/texture_set.hpp" 2 | 3 | void Decima::TextureDefaultColor::parse(ash::buffer& buffer, CoreFile& file) { 4 | buffer.get(rgba, sizeof(rgba)); 5 | } 6 | 7 | void Decima::DecimaTextureSetEntry::parse(ash::buffer& buffer, CoreFile& file) { 8 | compression_method = buffer.get(); 9 | create_mip_maps = buffer.get(); 10 | color_space = buffer.get(); 11 | packing_info = buffer.get(); 12 | texture_type = buffer.get(); 13 | texture.parse(buffer, file); 14 | } 15 | 16 | void Decima::DecimaTextureSetTextureDescriptor::parse(ash::buffer& buffer, CoreFile& file) { 17 | texture_type = buffer.get(); 18 | path.parse(buffer, file); 19 | active = buffer.get(); 20 | gamma_space = buffer.get(); 21 | storage_type = buffer.get(); 22 | quality_type = buffer.get(); 23 | compression_method = buffer.get(); 24 | if (active > 0) { 25 | width = buffer.get(); 26 | height = buffer.get(); 27 | } else { 28 | unk_0 = buffer.get(); 29 | } 30 | default_color.parse(buffer, file); 31 | } 32 | 33 | void Decima::TextureSet::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 34 | CoreObject::parse(manager, buffer, file); 35 | 36 | entries.resize(buffer.get()); 37 | for (auto& entry : entries) 38 | entry.parse(buffer, file); 39 | 40 | mip_map_mode = buffer.get(); 41 | 42 | descriptors.resize(buffer.get()); 43 | for (auto& entry : descriptors) 44 | entry.parse(buffer, file); 45 | 46 | preset.parse(buffer, file); 47 | } 48 | -------------------------------------------------------------------------------- /ProjectDS.cmake: -------------------------------------------------------------------------------- 1 | add_executable(ProjectDS 2 | src/main.cpp 3 | src/decima/archive/archive.cpp 4 | src/decima/archive/archive_manager.cpp 5 | src/decima/archive/archive_tree.cpp 6 | src/decima/archive/archive_file.cpp 7 | src/utils.cpp 8 | src/app.cpp 9 | src/projectds_app.cpp 10 | src/projectds_app_draw.cpp 11 | src/decima/serializable/object/object.cpp 12 | src/decima/serializable/object/object_draw.cpp 13 | src/decima/serializable/object/object_dummy.cpp 14 | src/decima/serializable/object/collection.cpp 15 | src/decima/serializable/object/collection_draw.cpp 16 | src/decima/serializable/object/prefetch.cpp 17 | src/decima/serializable/object/prefetch_draw.cpp 18 | src/decima/serializable/object/translation.cpp 19 | src/decima/serializable/object/translation_draw.cpp 20 | src/decima/serializable/object/texture.cpp 21 | src/decima/serializable/object/texture_draw.cpp 22 | src/decima/serializable/object/texture_set.cpp 23 | src/decima/serializable/object/texture_set_draw.cpp 24 | src/decima/serializable/reference.cpp 25 | src/decima/serializable/string.cpp 26 | src/decima/serializable/stream.cpp 27 | src/decima/serializable/guid.cpp 28 | src/decima/serializable/handlers.cpp 29 | src/decima/serializable/object/resource/vertex_array_resource.cpp 30 | src/decima/serializable/object/resource/vertex_array_resource_draw.cpp 31 | src/decima/serializable/object/resource/index_array_resource.cpp 32 | src/decima/serializable/object/resource/index_array_resource_draw.cpp 33 | src/decima/serializable/object/resource/primitive_resource.cpp 34 | src/decima/serializable/object/resource/primitive_resource_draw.cpp) 35 | 36 | target_link_libraries(ProjectDS PRIVATE hash imgui glfw glad) 37 | target_include_directories(ProjectDS PRIVATE include) 38 | 39 | if (MSVC) 40 | target_compile_definitions(ProjectDS PUBLIC _CRT_SECURE_NO_WARNINGS _ITERATOR_DEBUG_LEVEL=0) 41 | target_compile_options(ProjectDS PUBLIC /EHs) 42 | endif () -------------------------------------------------------------------------------- /include/decima/serializable/array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "decima/serializable/object/object.hpp" 7 | #include "decima/serializable/serializable.hpp" 8 | 9 | namespace Decima { 10 | class ArchiveManager; 11 | 12 | template 13 | class Array : public CoreSerializable { 14 | public: 15 | static_assert(std::is_trivial_v, "Array type must be either trivial or inherit Decima::CoreSerializable"); 16 | 17 | inline void parse(ash::buffer& buffer, CoreFile& file) { 18 | std::uint32_t length = buffer.get(); 19 | m_data.resize(length); 20 | buffer.get(m_data); 21 | } 22 | 23 | inline const std::vector& data() const { return m_data; } 24 | inline std::vector& data() { return m_data; } 25 | 26 | private: 27 | std::vector m_data; 28 | }; 29 | 30 | template 31 | class Array>> : public CoreSerializable { 32 | public: 33 | inline void parse(ash::buffer& buffer, CoreFile& file) { 34 | std::uint32_t length = buffer.get(); 35 | m_data.resize(length); 36 | for (auto& item : m_data) 37 | item.parse(buffer, file); 38 | } 39 | 40 | inline const std::vector& data() const { return m_data; } 41 | inline std::vector& data() { return m_data; } 42 | 43 | private: 44 | std::vector m_data; 45 | }; 46 | 47 | template 48 | class Array>> : public CoreSerializable { 49 | public: 50 | inline void parse(ArchiveManager& manager, ash::buffer& buffer) { 51 | std::uint32_t length = buffer.get(); 52 | m_data.resize(length); 53 | for (auto& item : m_data) 54 | item.parse(manager, buffer); 55 | } 56 | 57 | inline const std::vector& data() const { return m_data; } 58 | inline std::vector& data() { return m_data; } 59 | 60 | private: 61 | std::vector m_data; 62 | }; 63 | } -------------------------------------------------------------------------------- /src/decima/serializable/object/resource/primitive_resource_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/resource/primitive_resource.hpp" 2 | 3 | #include 4 | 5 | void Decima::PrimitiveResource::draw() { 6 | ImGui::Columns(2); 7 | 8 | { 9 | ImGui::Text("Property"); 10 | ImGui::NextColumn(); 11 | 12 | ImGui::Text("Value"); 13 | ImGui::NextColumn(); 14 | 15 | ImGui::Separator(); 16 | } 17 | 18 | ImGui::SetColumnWidth(0, 200); 19 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 20 | 21 | { 22 | ImGui::Text("Flags"); 23 | ImGui::NextColumn(); 24 | 25 | ImGui::Text("%u", flags); 26 | ImGui::NextColumn(); 27 | 28 | ImGui::Separator(); 29 | } 30 | 31 | { 32 | ImGui::Text("Bounding Box"); 33 | ImGui::NextColumn(); 34 | 35 | ImGui::Text("%s", Decima::to_string(bounding_box).c_str()); 36 | ImGui::NextColumn(); 37 | 38 | ImGui::Separator(); 39 | } 40 | 41 | { 42 | ImGui::Text("Start index"); 43 | ImGui::NextColumn(); 44 | 45 | ImGui::Text("%u", start_index); 46 | ImGui::NextColumn(); 47 | 48 | ImGui::Separator(); 49 | } 50 | 51 | { 52 | ImGui::Text("End index"); 53 | ImGui::NextColumn(); 54 | 55 | ImGui::Text("%u", end_index); 56 | ImGui::NextColumn(); 57 | 58 | ImGui::Separator(); 59 | } 60 | 61 | { 62 | ImGui::Text("Hash"); 63 | ImGui::NextColumn(); 64 | 65 | ImGui::Text("%#08x", hash); 66 | ImGui::NextColumn(); 67 | 68 | ImGui::Separator(); 69 | } 70 | 71 | { 72 | ImGui::Text("Vertex array"); 73 | ImGui::NextColumn(); 74 | 75 | vertex_array.draw(); 76 | ImGui::NextColumn(); 77 | 78 | ImGui::Separator(); 79 | } 80 | 81 | { 82 | ImGui::Text("Index array"); 83 | ImGui::NextColumn(); 84 | 85 | index_array.draw(); 86 | ImGui::NextColumn(); 87 | 88 | ImGui::Separator(); 89 | } 90 | 91 | { 92 | ImGui::Text("SKD tree"); 93 | ImGui::NextColumn(); 94 | 95 | skd_tree.draw(); 96 | ImGui::NextColumn(); 97 | } 98 | 99 | ImGui::Columns(1); 100 | } 101 | -------------------------------------------------------------------------------- /src/decima/archive/archive.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/archive/archive.hpp" 2 | #include "decima/shared.hpp" 3 | 4 | #include 5 | #include 6 | 7 | static void decrypt(uint32_t key_1, uint32_t key_2, uint32_t* data) { 8 | const std::uint32_t key[8] = { 9 | key_1, Decima::plain_cipher_key[1], Decima::plain_cipher_key[2], Decima::plain_cipher_key[3], 10 | key_2, Decima::plain_cipher_key[1], Decima::plain_cipher_key[2], Decima::plain_cipher_key[3] 11 | }; 12 | 13 | std::uint32_t iv[8]; 14 | MurmurHash3_x64_128(key, 16, Decima::cipher_seed, iv); 15 | MurmurHash3_x64_128(key + 4, 16, Decima::cipher_seed, iv + 4); 16 | 17 | data[0] ^= iv[0]; 18 | data[1] ^= iv[1]; 19 | data[2] ^= iv[2]; 20 | data[3] ^= iv[3]; 21 | data[4] ^= iv[4]; 22 | data[5] ^= iv[5]; 23 | data[6] ^= iv[6]; 24 | data[7] ^= iv[7]; 25 | } 26 | 27 | Decima::Archive::Archive(const std::string& path) 28 | : m_file(std::make_unique(path, std::ios::binary)) 29 | , path(path) { open(); } 30 | 31 | bool Decima::Archive::open() { 32 | m_file->seekg(0, std::ios::beg); 33 | m_file->read((char*)&header, sizeof(ArchiveHeader)); 34 | 35 | if (header.type != ArchiveType::Regular && header.type != ArchiveType::Encrypted) 36 | return false; 37 | 38 | if (header.type == ArchiveType::Encrypted) 39 | decrypt(header.key, header.key + 1, (uint32_t*)&header.file_size); 40 | 41 | file_entries.resize(header.file_entries_count); 42 | m_file->read((char*)file_entries.data(), sizeof(ArchiveFileEntry) * header.file_entries_count); 43 | 44 | chunk_entries.resize(header.chunk_entries_count); 45 | m_file->read((char*)chunk_entries.data(), sizeof(ArchiveChunkEntry) * header.chunk_entries_count); 46 | 47 | if (header.type == ArchiveType::Encrypted) { 48 | for (auto& entry : file_entries) { 49 | uint32_t key_1 = entry.key; 50 | uint32_t key_2 = entry.span.key; 51 | 52 | decrypt(entry.key, entry.span.key, (uint32_t*)&entry); 53 | 54 | entry.key = key_1; 55 | entry.span.key = key_2; 56 | } 57 | 58 | for (auto& entry : chunk_entries) { 59 | uint32_t key_1 = entry.decompressed_span.key; 60 | uint32_t key_2 = entry.compressed_span.key; 61 | 62 | decrypt(entry.decompressed_span.key, entry.compressed_span.key, (uint32_t*)&entry); 63 | 64 | entry.decompressed_span.key = key_1; 65 | entry.compressed_span.key = key_2; 66 | } 67 | } 68 | 69 | for (std::size_t index = 0; index < file_entries.size(); index++) { 70 | m_hash_to_index.emplace(file_entries.at(index).hash, index); 71 | } 72 | 73 | return true; 74 | } 75 | -------------------------------------------------------------------------------- /include/decima/archive/archive.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "decima/archive/archive_file.hpp" 9 | 10 | namespace Decima { 11 | enum class ArchiveType : uint32_t { 12 | /** Regular type of the archive, not encrypted */ 13 | Regular = 0x20304050, 14 | /** Regular type of the archive, encrypted with [key] */ 15 | Encrypted = 0x21304050, 16 | }; 17 | 18 | class ArchiveHeader { 19 | public: 20 | /** Type of this archive file, either regular or encrypted */ 21 | ArchiveType type; 22 | /** Key used to decrypt contents of this archive */ 23 | uint32_t key; 24 | /** Total size of this archive file, in bytes */ 25 | uint64_t file_size; 26 | /** Total size of uncompressed files, in bytes */ 27 | uint64_t data_size; 28 | /** Count of file entries */ 29 | uint64_t file_entries_count; 30 | /** Count of chunk entries */ 31 | uint32_t chunk_entries_count; 32 | /** Maximum size of chunk, in bytes */ 33 | uint32_t chunk_maximum_size; 34 | }; 35 | 36 | class ArchiveSpan { 37 | public: 38 | /** Starting offset of this span, in bytes */ 39 | uint64_t offset; 40 | /** Size of this span, in bytes */ 41 | uint32_t size; 42 | /** Key used to decrypt contents of this span */ 43 | uint32_t key; 44 | }; 45 | 46 | class ArchiveFileEntry { 47 | public: 48 | /** Unique identifier of this file entry */ 49 | uint32_t index; 50 | /** Key used to decrypt contents of this file entry */ 51 | uint32_t key; 52 | /** Hash of the name of this file entry, obtained using MurMurHash3 algorithm */ 53 | uint64_t hash; 54 | /** Span of the decompressed data of this file entry */ 55 | ArchiveSpan span; 56 | }; 57 | 58 | class ArchiveChunkEntry { 59 | public: 60 | /** Span of the decompressed data of this chunk entry */ 61 | ArchiveSpan decompressed_span; 62 | /** Span of the compressed data of this chunk entry */ 63 | ArchiveSpan compressed_span; 64 | }; 65 | 66 | class Archive { 67 | public: 68 | explicit Archive(const std::string& path); 69 | 70 | Decima::ArchiveHeader header {}; 71 | std::vector file_entries; 72 | std::vector chunk_entries; 73 | std::string path; 74 | 75 | private: 76 | friend class ArchiveManager; 77 | friend class CoreFile; 78 | 79 | bool open(); 80 | 81 | std::unordered_map m_cache; 82 | std::unordered_map m_hash_to_index; 83 | std::unique_ptr m_file; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /src/decima/serializable/reference.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/reference.hpp" 2 | #include "decima/serializable/object/object.hpp" 3 | 4 | #include 5 | 6 | void Decima::Ref::parse(ash::buffer& buffer, Decima::CoreFile& file) { 7 | m_owner = file.objects.back().first; 8 | m_mode = buffer.get(); 9 | if (m_mode != RefLoadMode::NotPresent) 10 | m_guid.parse(buffer, file); 11 | if (m_mode >= RefLoadMode::ImmediateCoreFile) 12 | m_file.parse(buffer, file); 13 | file.queue_reference(this); 14 | } 15 | 16 | void Decima::Ref::draw() { 17 | m_guid.draw(); 18 | ImGui::SameLine(); 19 | ImGui::TextDisabled("Reference (%s)", Decima::to_string(m_mode).c_str()); 20 | ImGui::SameLine(); 21 | 22 | if (ImGui::SmallButton(("Show##" + Decima::to_string(m_guid)).c_str())) { 23 | m_show_object = m_object != nullptr; 24 | } 25 | 26 | if (ImGui::IsItemHovered()) { 27 | ImGui::BeginTooltip(); 28 | ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); 29 | 30 | if (m_object == nullptr) { 31 | ImGui::Text("Not resolved"); 32 | } else { 33 | ImGui::Text("Click to show"); 34 | } 35 | 36 | ImGui::PopTextWrapPos(); 37 | ImGui::EndTooltip(); 38 | } 39 | 40 | if (m_show_object) { 41 | ImGui::SetNextWindowSize({ 600, 400 }, ImGuiCond_Appearing); 42 | ImGui::SetNextWindowPos(ImGui::GetMousePos(), ImGuiCond_Appearing, { 0.5, 0.5 }); 43 | 44 | if (ImGui::Begin(("Reference to " + Decima::to_string(m_guid)).c_str(), &m_show_object, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking)) { 45 | ImGui::Columns(2); 46 | ImGui::SetColumnWidth(0, 100); 47 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 100); 48 | 49 | { 50 | ImGui::Text("Reference"); 51 | ImGui::NextColumn(); 52 | 53 | ImGui::Text("%s", Decima::to_string(m_mode).c_str()); 54 | ImGui::NextColumn(); 55 | 56 | ImGui::Separator(); 57 | } 58 | 59 | if (!m_file.data().empty()) { 60 | ImGui::Text("File"); 61 | ImGui::NextColumn(); 62 | 63 | m_file.draw(); 64 | ImGui::NextColumn(); 65 | 66 | ImGui::Separator(); 67 | } 68 | 69 | { 70 | ImGui::Text("UUID"); 71 | ImGui::NextColumn(); 72 | 73 | m_guid.draw(); 74 | ImGui::NextColumn(); 75 | } 76 | 77 | ImGui::Columns(1); 78 | 79 | ImGui::Separator(); 80 | 81 | ImGui::BeginChild(("ReferenceChild" + Decima::to_string(m_guid)).c_str()); 82 | m_object->draw(); 83 | ImGui::EndChild(); 84 | } 85 | 86 | ImGui::End(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/decima/archive/archive_manager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "utils.hpp" 4 | #include "decima/archive/archive_manager.hpp" 5 | #include "decima/archive/archive.hpp" 6 | #include "decima/serializable/object/prefetch.hpp" 7 | 8 | void Decima::ArchiveManager::load_archive(const std::string& path) { 9 | auto& archive = archives.emplace_back(path); 10 | archive.open(); 11 | 12 | for (const auto& entry : archive.file_entries) { 13 | hash_to_archive_index.emplace(entry.hash, static_cast(archives.size() - 1)); 14 | } 15 | } 16 | 17 | void Decima::ArchiveManager::load_prefetch() { 18 | auto& prefetch_file = query_file("prefetch/fullgame.prefetch").value().get(); 19 | prefetch_file.parse(); 20 | 21 | prefetch = std::make_unique(static_cast(*prefetch_file.objects.at(0).first)); 22 | 23 | for (std::uint64_t index = 0; index < prefetch->paths.data().size(); index++) { 24 | auto path = prefetch->paths.data()[index].data(); 25 | auto hash = hash_string(sanitize_name(path), cipher_seed); 26 | hash_to_name.emplace(hash, path); 27 | hash_to_index.emplace(hash, index); 28 | } 29 | } 30 | 31 | Decima::OptionalRef Decima::ArchiveManager::get_file_entry(std::uint64_t hash) { 32 | if (auto archive_id = hash_to_archive_index.find(hash); archive_id != hash_to_archive_index.end()) { 33 | auto& archive = archives.at(archive_id->second); 34 | auto file_id = archive.m_hash_to_index.find(hash)->second; 35 | 36 | return std::make_optional(std::ref(archive.file_entries.at(file_id))); 37 | } 38 | 39 | return {}; 40 | } 41 | 42 | Decima::OptionalRef Decima::ArchiveManager::get_file_entry(const std::string& name) { 43 | return get_file_entry(hash_string(sanitize_name(name), cipher_seed)); 44 | } 45 | 46 | Decima::OptionalRef Decima::ArchiveManager::query_file(std::uint64_t hash) { 47 | if (auto archive_index = hash_to_archive_index.find(hash); archive_index != hash_to_archive_index.end()) { 48 | auto& archive = archives.at(archive_index->second); 49 | 50 | if (auto index = archive.m_hash_to_index.find(hash); index != archive.m_hash_to_index.end()) { 51 | auto cache = archive.m_cache.find(index->second); 52 | 53 | if (cache == archive.m_cache.end()) { 54 | Decima::CoreFile file(archive, *this, archive.file_entries.at(index->second), *archive.m_file); 55 | cache = archive.m_cache.emplace(index->second, std::move(file)).first; 56 | } 57 | 58 | return std::make_optional(std::ref(cache->second)); 59 | } 60 | } 61 | 62 | return {}; 63 | } 64 | 65 | Decima::OptionalRef Decima::ArchiveManager::query_file(const std::string& name) { 66 | return query_file(hash_string(sanitize_name(name), cipher_seed)); 67 | } 68 | -------------------------------------------------------------------------------- /src/app.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by i.getsman on 06.08.2020. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include "app.hpp" 9 | 10 | #include "utils.hpp" 11 | #include "decima/shared.hpp" 12 | 13 | App::App(std::pair window_size, std::string title) { 14 | m_win_info.m_win_width = window_size.first; 15 | m_win_info.m_win_height = window_size.second; 16 | m_win_info.m_title = std::move(title); 17 | } 18 | 19 | void App::init() { 20 | DECIMA_LOG("Initialization"); 21 | init_glfw(); 22 | init_opengl(); 23 | init_user(); 24 | DECIMA_LOG("Done"); 25 | } 26 | 27 | void App::run() { 28 | while (!glfwWindowShouldClose(m_window)) { 29 | auto ts = glfwGetTime() - m_prev_time; 30 | update(ts); 31 | m_prev_time = glfwGetTime(); 32 | } 33 | } 34 | 35 | void App::init_glfw() { 36 | DECIMA_LOG(" GLFW Initialization"); 37 | glfwInit(); 38 | m_window = glfwCreateWindow(m_win_info.m_win_width, m_win_info.m_win_height, m_win_info.m_title.c_str(), 39 | nullptr, nullptr); 40 | auto binded_error_callback = std::bind(&App::glfw_error_handler, this, 41 | std::placeholders::_1, 42 | std::placeholders::_2); 43 | glfwSetErrorCallback(reinterpret_cast(&binded_error_callback)); 44 | glfwMakeContextCurrent(m_window); 45 | glfwSetInputMode(m_window, GLFW_STICKY_KEYS, GLFW_FALSE); 46 | glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 47 | glfwSetInputMode(m_window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); 48 | glfwSwapInterval(1); 49 | 50 | glfwSetWindowSizeCallback(m_window, [](GLFWwindow* hwnd, int width, int height) { 51 | glViewport(0, 0, width, height); 52 | }); 53 | glfwSetWindowMaximizeCallback(m_window, [](GLFWwindow* hwnd, int maximized) { 54 | int width, height; 55 | glfwGetWindowSize(hwnd, &width, &height); 56 | glViewport(0, 0, width, height); 57 | }); 58 | DECIMA_LOG(" GLFW Done"); 59 | } 60 | 61 | void App::glfw_error_handler(int error, const char* message) { 62 | throw std::runtime_error("GLFW error: " + std::to_string(error) + ": " + message); 63 | } 64 | 65 | void App::init_opengl() { 66 | DECIMA_LOG(" GLAD Initialization"); 67 | gladLoadGL(); 68 | glEnable(GL_DEBUG_OUTPUT); 69 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 70 | 71 | glEnable(GL_DEPTH_TEST); 72 | glDepthFunc(GL_LESS); 73 | 74 | glEnable(GL_CULL_FACE); 75 | glCullFace(GL_BACK); 76 | DECIMA_LOG(" GLAD Done"); 77 | } 78 | 79 | void App::update(double ts) { 80 | input_user(); 81 | begin_frame(); 82 | update_user(ts); 83 | end_frame(); 84 | } 85 | 86 | void App::clear_window(float r, float g, float b, float a, uint32_t flags) { 87 | glClear(flags); 88 | glClearColor(r, g, b, a); 89 | } 90 | 91 | void App::begin_frame() { 92 | clear_window(); 93 | begin_frame_user(); 94 | } 95 | 96 | void App::end_frame() { 97 | end_frame_user(); 98 | glfwSwapBuffers(m_window); 99 | glfwPollEvents(); 100 | } 101 | 102 | void App::init_user() { 103 | } 104 | 105 | void App::update_user(double ts) { 106 | } 107 | 108 | void App::input_user() { 109 | } 110 | 111 | void App::begin_frame_user() { 112 | } 113 | 114 | void App::end_frame_user() { 115 | } 116 | -------------------------------------------------------------------------------- /include/util/compressor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | #include 7 | 8 | namespace Decima { 9 | enum class CompressionLevel { 10 | None, 11 | SuperFast, 12 | VeryFast, 13 | Fast, 14 | Normal, 15 | Optimal1, 16 | Optimal2, 17 | Optimal3, 18 | Optimal4, 19 | Optimal5 20 | }; 21 | 22 | class Compressor { 23 | public: 24 | typedef int (*CompressFn)(int format, const std::uint8_t* src, std::size_t src_len, std::uint8_t* dst, int level, void*, std::size_t, std::size_t, void*, std::size_t); 25 | typedef int (*DecompressFn)(const std::uint8_t* src, std::size_t src_len, std::uint8_t* dst, std::size_t dst_len, int fuzz, int crc, int verbose, std::uint8_t*, std::size_t, void*, void*, void*, std::size_t, int); 26 | typedef std::uint64_t (*GetConfigValuesFn)(std::uint8_t* buffer); 27 | 28 | public: 29 | explicit inline Compressor(HMODULE module) 30 | : m_module(module) 31 | , m_compress(reinterpret_cast(GetProcAddress(m_module, "OodleLZ_Compress"))) 32 | , m_decompress(reinterpret_cast(GetProcAddress(m_module, "OodleLZ_Decompress"))) 33 | , m_get_config_values(reinterpret_cast(GetProcAddress(m_module, "Oodle_GetConfigValues"))) { } 34 | 35 | explicit inline Compressor(const std::string& path) 36 | : Compressor(LoadLibraryA(path.c_str())) { } 37 | 38 | explicit inline Compressor(const std::wstring& path) 39 | : Compressor(LoadLibraryW(path.c_str())) { } 40 | 41 | inline ~Compressor() { 42 | FreeLibrary(m_module); 43 | } 44 | 45 | template 46 | inline std::uint32_t compress(const Input& input, Output& output, CompressionLevel level = CompressionLevel::Normal) const noexcept { 47 | output.resize(calculate_compression_bound(input.size())); 48 | return m_compress(8, (std::uint8_t*)input.data(), input.size(), (std::uint8_t*)output.data(), static_cast(level), nullptr, 0, 0, nullptr, 0); 49 | } 50 | 51 | template 52 | inline std::uint32_t decompress(const Input& input, Output& output, int crc = 0) const noexcept { 53 | return m_decompress((std::uint8_t*)input.data(), input.size(), (std::uint8_t*)output.data(), output.size(), 0, crc, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, 0); 54 | } 55 | 56 | inline std::uint64_t get_version() const noexcept { 57 | std::array buffer {}; 58 | m_get_config_values((std::uint8_t*)buffer.data()); 59 | return buffer[6]; 60 | } 61 | 62 | inline std::string get_version_string() const noexcept { 63 | auto version = get_version(); 64 | return std::to_string((version & 0xff) - (version >> 24)) + "." + std::to_string(version >> 16 & 0xff) + "." + std::to_string(version >> 8 & 0xff); 65 | } 66 | 67 | private: 68 | inline static std::size_t calculate_compression_bound(std::size_t size) noexcept { 69 | return size + 274 * ((size + 0x3FFFF) / 0x40000); 70 | } 71 | 72 | HMODULE m_module; 73 | CompressFn m_compress; 74 | DecompressFn m_decompress; 75 | GetConfigValuesFn m_get_config_values; 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /include/util/buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ash { 7 | namespace detail { 8 | template 9 | struct is_container : std::false_type { }; 10 | 11 | template 12 | struct is_container().data()), 15 | decltype(std::declval().size()) 16 | >> : std::true_type { }; 17 | 18 | template 19 | inline constexpr bool is_container_v = is_container::value; 20 | } 21 | 22 | template > 23 | class basic_buffer { 24 | public: 25 | using pointer = const typename Traits::char_type*; 26 | 27 | inline basic_buffer(pointer begin, pointer end) noexcept 28 | : m_beg(begin) 29 | , m_end(end) { } 30 | 31 | inline basic_buffer(pointer begin, std::size_t size) noexcept 32 | : m_beg(begin) 33 | , m_end(begin + size) { } 34 | 35 | inline basic_buffer(basic_buffer&& other) noexcept = default; 36 | inline basic_buffer(const basic_buffer& other) noexcept = default; 37 | 38 | basic_buffer& operator=(const basic_buffer& other) noexcept = default; 39 | basic_buffer& operator=(basic_buffer&& other) noexcept = default; 40 | 41 | inline pointer data() const noexcept { return m_beg; } 42 | 43 | inline pointer begin() const noexcept { return m_beg; } 44 | inline pointer cbegin() const noexcept { return m_beg; } 45 | 46 | inline pointer end() const noexcept { return m_end; } 47 | inline pointer cend() const noexcept { return m_end; } 48 | 49 | inline std::size_t size() const noexcept { return std::distance(m_beg, m_end); } 50 | 51 | inline basic_buffer slice(std::size_t offset, std::size_t count) const { 52 | if (offset + count > size()) 53 | throw std::range_error("Cannot take slice that is larger than buffer"); 54 | return { m_beg + offset, m_beg + offset + count }; 55 | } 56 | 57 | inline basic_buffer take(std::size_t count) const { 58 | return slice(0, count); 59 | } 60 | 61 | inline basic_buffer last(std::size_t count) const { 62 | return slice(size() - count, count); 63 | } 64 | 65 | inline basic_buffer skip(std::size_t count) const { 66 | return slice(count, size() - count); 67 | } 68 | 69 | inline basic_buffer& get(void* dst, std::size_t size) { 70 | const auto slice = take(size); 71 | memcpy(dst, slice.data(), slice.size()); 72 | return *this = skip(size); 73 | } 74 | 75 | template >> 76 | inline void get(Container& container) { 77 | using Type = typename Container::value_type; 78 | get(container.data(), container.size() * sizeof(Type)); 79 | } 80 | 81 | template >> 82 | inline Type get() { 83 | Type result {}; 84 | get(&result, sizeof(Type)); 85 | return result; 86 | } 87 | 88 | private: 89 | pointer m_beg; 90 | pointer m_end; 91 | }; 92 | 93 | typedef basic_buffer buffer; 94 | } 95 | -------------------------------------------------------------------------------- /libs/hash/md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | Permission is granted to anyone to use this software for any purpose, 7 | including commercial applications, and to alter it and redistribute it 8 | freely, subject to the following restrictions: 9 | 1. The origin of this software must not be misrepresented; you must not 10 | claim that you wrote the original software. If you use this software 11 | in a product, an acknowledgment in the product documentation would be 12 | appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and must not be 14 | misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source distribution. 16 | L. Peter Deutsch 17 | ghost@aladdin.com 18 | */ 19 | /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ 20 | /* 21 | Independent implementation of MD5 (RFC 1321). 22 | This code implements the MD5 Algorithm defined in RFC 1321, whose 23 | text is available at 24 | http://www.ietf.org/rfc/rfc1321.txt 25 | The code is derived from the text of the RFC, including the test suite 26 | (section A.5) but excluding the rest of Appendix A. It does not include 27 | any code or documentation that is identified in the RFC as being 28 | copyrighted. 29 | The original and principal author of md5.h is L. Peter Deutsch 30 | . Other authors are noted in the change history 31 | that follows (in reverse chronological order): 32 | 2002-04-13 lpd Removed support for non-ANSI compilers; removed 33 | references to Ghostscript; clarified derivation from RFC 1321; 34 | now handles byte order either statically or dynamically. 35 | 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 36 | 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); 37 | added conditionalization for C++ compilation from Martin 38 | Purschke . 39 | 1999-05-03 lpd Original version. 40 | */ 41 | 42 | #ifndef md5_INCLUDED 43 | # define md5_INCLUDED 44 | 45 | /* 46 | * This package supports both compile-time and run-time determination of CPU 47 | * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be 48 | * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is 49 | * defined as non-zero, the code will be compiled to run only on big-endian 50 | * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to 51 | * run on either big- or little-endian CPUs, but will run slightly less 52 | * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. 53 | */ 54 | 55 | typedef unsigned char md5_byte_t; /* 8-bit byte */ 56 | typedef unsigned int md5_word_t; /* 32-bit word */ 57 | 58 | /* Define the state of the MD5 Algorithm. */ 59 | typedef struct md5_state_s { 60 | md5_word_t count[2]; /* message length in bits, lsw first */ 61 | md5_word_t abcd[4]; /* digest buffer */ 62 | md5_byte_t buf[64]; /* accumulate block */ 63 | } md5_state_t; 64 | 65 | #ifdef __cplusplus 66 | extern "C" 67 | { 68 | #endif 69 | 70 | /* Initialize the algorithm. */ 71 | void md5_init(md5_state_t* pms); 72 | 73 | /* Append a string to the message. */ 74 | void md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes); 75 | 76 | /* Finish the message and return the digest. */ 77 | void md5_finish(md5_state_t* pms, md5_byte_t digest[16]); 78 | 79 | /* Use Algorithm*/ 80 | void md5Hash(md5_byte_t* in, int size, md5_byte_t* digest_out); 81 | 82 | #ifdef __cplusplus 83 | } /* end extern "C" */ 84 | #endif 85 | 86 | #endif /* md5_INCLUDED */ -------------------------------------------------------------------------------- /include/decima/serializable/object/texture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef NOMINMAX 4 | #define NOMINMAX 5 | #endif 6 | 7 | #include "decima/serializable/object/object.hpp" 8 | #include "decima/serializable/stream.hpp" 9 | 10 | #include 11 | 12 | namespace Decima { 13 | enum class TexturePixelFormat : std::uint8_t { 14 | RGBA8 = 0xC, 15 | RGBA16F = 0x13, 16 | A8 = 0x1F, 17 | BC1 = 0x42, 18 | BC2 = 0x43, 19 | BC3 = 0x44, 20 | BC4 = 0x45, 21 | BC5 = 0x47, 22 | BC6 = 0x49, 23 | BC7 = 0x4B 24 | }; 25 | 26 | enum class TextureType : std::uint16_t { 27 | Tex2D = 0, 28 | Tex3D = 1, 29 | TexCubeMap = 2, 30 | Tex2DArray = 3 31 | }; 32 | 33 | struct TexturePixelFormatInfo { 34 | /* This format's block size (in pixels) */ 35 | int block_size; 36 | /* How many bits occupies one pixel */ 37 | int block_density; 38 | /* Corresponding OpenGL internal format */ 39 | int internal_format; 40 | /* Texture format */ 41 | int data_format; 42 | /* Is format whether compressed or not */ 43 | bool compressed; 44 | 45 | unsigned int calculate_size(int width, int height) const; 46 | unsigned int create_texture(int width, int height, const void* data, std::size_t size) const; 47 | }; 48 | 49 | extern const std::unordered_map texture_format_info; 50 | 51 | class Texture : public CoreObject { 52 | public: 53 | ~Texture(); 54 | 55 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 56 | void draw() override; 57 | 58 | private: 59 | void draw_preview(float preview_width, float preview_height, float zoom_region, float zoom_scale); 60 | 61 | TextureType type; 62 | std::uint16_t width; 63 | std::uint16_t height; 64 | std::uint16_t layers; 65 | std::uint8_t total_mips; 66 | TexturePixelFormat pixel_format; 67 | std::uint16_t unk_0; 68 | std::uint32_t unk_1; 69 | GUID unk_2; 70 | std::uint32_t buffer_size; 71 | std::uint32_t total_size; 72 | std::uint32_t stream_size; 73 | std::uint32_t stream_mips; 74 | std::uint32_t unk_3; 75 | std::uint32_t unk_4; 76 | Decima::Stream external_data; 77 | std::vector embedded_data; 78 | std::vector mip_textures; 79 | int mip_index; 80 | }; 81 | } 82 | 83 | template <> 84 | inline std::string Decima::to_string(const TexturePixelFormat& value) { 85 | switch (value) { 86 | case Decima::TexturePixelFormat::RGBA8: 87 | return "RGBA8"; 88 | case Decima::TexturePixelFormat::RGBA16F: 89 | return "RGBA16F"; 90 | case Decima::TexturePixelFormat::A8: 91 | return "A8"; 92 | case Decima::TexturePixelFormat::BC1: 93 | return "BC1"; 94 | case Decima::TexturePixelFormat::BC2: 95 | return "BC2"; 96 | case Decima::TexturePixelFormat::BC3: 97 | return "BC3"; 98 | case Decima::TexturePixelFormat::BC4: 99 | return "BC4"; 100 | case Decima::TexturePixelFormat::BC5: 101 | return "BC5"; 102 | case Decima::TexturePixelFormat::BC6: 103 | return "BC6"; 104 | case Decima::TexturePixelFormat::BC7: 105 | return "BC7"; 106 | default: 107 | return "Unknown: " + std::to_string(static_cast(value)); 108 | } 109 | } 110 | 111 | template <> 112 | inline std::string Decima::to_string(const TextureType& value) { 113 | switch (value) { 114 | case TextureType::Tex2D: 115 | return "2D"; 116 | case TextureType::Tex3D: 117 | return "3D"; 118 | case TextureType::TexCubeMap: 119 | return "CubeMap"; 120 | case TextureType::Tex2DArray: 121 | return "2D Array"; 122 | default: 123 | return "Unknown: " + std::to_string(static_cast(value)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/decima/serializable/handlers.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/handlers.hpp" 2 | 3 | #include "decima/serializable/object/object.hpp" 4 | #include "decima/serializable/object/object_dummy.hpp" 5 | #include "decima/serializable/object/collection.hpp" 6 | #include "decima/serializable/object/translation.hpp" 7 | #include "decima/serializable/object/prefetch.hpp" 8 | #include "decima/serializable/object/texture.hpp" 9 | #include "decima/serializable/object/texture_set.hpp" 10 | #include "decima/serializable/object/resource/vertex_array_resource.hpp" 11 | #include "decima/serializable/object/resource/index_array_resource.hpp" 12 | #include "decima/serializable/object/resource/primitive_resource.hpp" 13 | 14 | #include "utils.hpp" 15 | 16 | class FileMagics { 17 | }; 18 | 19 | class DeathStranding_FileMagics : public FileMagics { 20 | public: 21 | // clang-format off 22 | static constexpr uint64_t Armature = 0x11e1d1a40b933e66; 23 | static constexpr uint64_t Texture = 0xa664164d69fd2b38; 24 | static constexpr uint64_t TextureSet = 0xa321e8c307328d2e; 25 | static constexpr uint64_t Translation = 0x31be502435317445; 26 | static constexpr uint64_t Shader = 0x16bb69a9e5aa0d9e; 27 | static constexpr uint64_t Collection = 0xf3586131b4f18516; 28 | static constexpr uint64_t Prefetch = 0xd05789eae3acbf02; 29 | static constexpr uint64_t VertexArrayResource = 0x3ac29a123faabab4; 30 | static constexpr uint64_t IndexArrayResource = 0x5fe633b37cedbf84; 31 | static constexpr uint64_t PrimitiveResource = 0xee49d93da4c1f4b8; 32 | // clang-format on 33 | }; 34 | 35 | class ZeroDawn_FileMagics : public FileMagics { 36 | public: 37 | static constexpr uint64_t Texture = 0xf2e1afb7052b3866; 38 | }; 39 | 40 | template 41 | inline static std::shared_ptr construct(Args&&... args) { 42 | return std::make_shared(std::forward(args)...); 43 | } 44 | 45 | static const std::unordered_map> handlers { 46 | // clang-format off 47 | { DeathStranding_FileMagics::Translation, construct }, 48 | { DeathStranding_FileMagics::Texture, construct }, 49 | { DeathStranding_FileMagics::TextureSet, construct }, 50 | { DeathStranding_FileMagics::Collection, construct }, 51 | { DeathStranding_FileMagics::Prefetch, construct }, 52 | { DeathStranding_FileMagics::VertexArrayResource, construct }, 53 | { DeathStranding_FileMagics::IndexArrayResource, construct }, 54 | { DeathStranding_FileMagics::PrimitiveResource, construct }, 55 | // clang-format on 56 | }; 57 | 58 | static const std::unordered_map names = { 59 | // clang-format off 60 | { DeathStranding_FileMagics::Armature, "Armature" }, 61 | { DeathStranding_FileMagics::Texture, "Texture" }, 62 | { DeathStranding_FileMagics::TextureSet, "TextureSet" }, 63 | { DeathStranding_FileMagics::Translation, "Translation" }, 64 | { DeathStranding_FileMagics::Shader, "Shader" }, 65 | { DeathStranding_FileMagics::Collection, "Collection" }, 66 | { DeathStranding_FileMagics::Prefetch, "Prefetch" }, 67 | { DeathStranding_FileMagics::VertexArrayResource, "VertexArrayResource" }, 68 | { DeathStranding_FileMagics::IndexArrayResource, "IndexArrayResource" }, 69 | { DeathStranding_FileMagics::PrimitiveResource, "PrimitiveResource" }, 70 | // clang-format on 71 | }; 72 | 73 | std::invoke_result_t> Decima::get_type_handler(std::uint64_t hash) { 74 | const auto handler = handlers.find(hash); 75 | return handler != handlers.end() ? handler->second() : construct(); 76 | } 77 | 78 | std::string Decima::get_type_name(uint64_t hash) { 79 | const auto name = names.find(hash); 80 | return name != names.end() ? name->second : "Unknown '" + uint64_to_hex(hash) + "'"; 81 | } -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: DontAlign 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Right 10 | AlignOperands: false 11 | AlignTrailingComments: false 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Empty 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: All 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: MultiLine 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: false 30 | AfterControlStatement: false 31 | AfterEnum: false 32 | AfterFunction: false 33 | AfterNamespace: false 34 | AfterObjCDeclaration: false 35 | AfterStruct: false 36 | AfterUnion: false 37 | AfterExternBlock: false 38 | BeforeCatch: false 39 | BeforeElse: false 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: All 45 | BreakBeforeBraces: 'Custom' 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeComma 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 0 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: false 60 | DeriveLineEnding: true 61 | DerivePointerAlignment: false 62 | DisableFormat: false 63 | ExperimentalAutoDetectBinPacking: false 64 | FixNamespaceComments: false 65 | ForEachMacros: 66 | - foreach 67 | - Q_FOREACH 68 | - BOOST_FOREACH 69 | IncludeBlocks: Preserve 70 | IncludeCategories: 71 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 72 | Priority: 2 73 | SortPriority: 0 74 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 75 | Priority: 3 76 | SortPriority: 0 77 | - Regex: '.*' 78 | Priority: 1 79 | SortPriority: 0 80 | IncludeIsMainRegex: '(Test)?$' 81 | IncludeIsMainSourceRegex: '' 82 | IndentCaseLabels: false 83 | IndentGotoLabels: true 84 | IndentPPDirectives: BeforeHash 85 | IndentWidth: 4 86 | IndentWrappedFunctionNames: false 87 | JavaScriptQuotes: Leave 88 | JavaScriptWrapImports: true 89 | KeepEmptyLinesAtTheStartOfBlocks: true 90 | MacroBlockBegin: '' 91 | MacroBlockEnd: '' 92 | MaxEmptyLinesToKeep: 1 93 | NamespaceIndentation: All 94 | ObjCBinPackProtocolList: Auto 95 | ObjCBlockIndentWidth: 4 96 | ObjCSpaceAfterProperty: true 97 | ObjCSpaceBeforeProtocolList: true 98 | PenaltyBreakAssignment: 2 99 | PenaltyBreakBeforeFirstCallParameter: 19 100 | PenaltyBreakComment: 300 101 | PenaltyBreakFirstLessLess: 120 102 | PenaltyBreakString: 1000 103 | PenaltyBreakTemplateDeclaration: 10 104 | PenaltyExcessCharacter: 1000000 105 | PenaltyReturnTypeOnItsOwnLine: 60 106 | PointerAlignment: Left 107 | ReflowComments: true 108 | SortIncludes: false 109 | SortUsingDeclarations: false 110 | SpaceAfterCStyleCast: false 111 | SpaceAfterLogicalNot: false 112 | SpaceAfterTemplateKeyword: true 113 | SpaceBeforeAssignmentOperators: true 114 | SpaceBeforeCpp11BracedList: true 115 | SpaceBeforeCtorInitializerColon: true 116 | SpaceBeforeInheritanceColon: true 117 | SpaceBeforeParens: ControlStatements 118 | SpaceBeforeRangeBasedForLoopColon: true 119 | SpaceInEmptyBlock: true 120 | SpaceInEmptyParentheses: false 121 | SpacesBeforeTrailingComments: 1 122 | SpacesInAngles: false 123 | SpacesInConditionalStatement: false 124 | SpacesInContainerLiterals: true 125 | SpacesInCStyleCastParentheses: false 126 | SpacesInParentheses: false 127 | SpacesInSquareBrackets: false 128 | SpaceBeforeSquareBrackets: false 129 | Standard: Latest 130 | StatementMacros: 131 | - Q_UNUSED 132 | - QT_REQUIRE_VERSION 133 | TabWidth: 8 134 | UseCRLF: false 135 | UseTab: Never 136 | ... 137 | 138 | -------------------------------------------------------------------------------- /src/decima/serializable/object/resource/vertex_array_resource_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/resource/vertex_array_resource.hpp" 2 | 3 | #include 4 | 5 | void Decima::VertexStreamData::draw() { 6 | ImGui::Columns(2); 7 | 8 | { 9 | ImGui::Text("Property"); 10 | ImGui::NextColumn(); 11 | 12 | ImGui::Text("Value"); 13 | ImGui::NextColumn(); 14 | 15 | ImGui::Separator(); 16 | } 17 | 18 | ImGui::SetColumnWidth(0, 200); 19 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 20 | 21 | { 22 | ImGui::Text("Storage type"); 23 | ImGui::NextColumn(); 24 | 25 | ImGui::Text("%s", Decima::to_string(storage_type).c_str()); 26 | ImGui::NextColumn(); 27 | 28 | ImGui::Separator(); 29 | } 30 | 31 | { 32 | ImGui::Text("Element type"); 33 | ImGui::NextColumn(); 34 | 35 | ImGui::Text("%s", Decima::to_string(element_type).c_str()); 36 | ImGui::NextColumn(); 37 | 38 | ImGui::Separator(); 39 | } 40 | 41 | { 42 | ImGui::Text("Slots used"); 43 | ImGui::NextColumn(); 44 | 45 | ImGui::Text("%u", slots_used); 46 | ImGui::NextColumn(); 47 | 48 | ImGui::Separator(); 49 | } 50 | 51 | { 52 | ImGui::Text("Offset"); 53 | ImGui::NextColumn(); 54 | 55 | ImGui::Text("%u", offset); 56 | ImGui::NextColumn(); 57 | 58 | ImGui::Separator(); 59 | } 60 | 61 | ImGui::Columns(1); 62 | } 63 | 64 | void Decima::VertexStreamInfo::draw() { 65 | ImGui::Columns(2); 66 | 67 | { 68 | ImGui::Text("Property"); 69 | ImGui::NextColumn(); 70 | 71 | ImGui::Text("Value"); 72 | ImGui::NextColumn(); 73 | 74 | ImGui::Separator(); 75 | } 76 | 77 | ImGui::SetColumnWidth(0, 200); 78 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 79 | 80 | { 81 | ImGui::Text("Flags"); 82 | ImGui::NextColumn(); 83 | 84 | ImGui::Text("%u", flags); 85 | ImGui::NextColumn(); 86 | 87 | ImGui::Separator(); 88 | } 89 | 90 | { 91 | ImGui::Text("Stride"); 92 | ImGui::NextColumn(); 93 | 94 | ImGui::Text("%u", stride); 95 | ImGui::NextColumn(); 96 | 97 | ImGui::Separator(); 98 | } 99 | 100 | { 101 | ImGui::Text("Resource UUID"); 102 | ImGui::NextColumn(); 103 | 104 | ImGui::Text("%s", Decima::to_string(resource_uuid).c_str()); 105 | ImGui::NextColumn(); 106 | 107 | ImGui::Separator(); 108 | } 109 | 110 | ImGui::Columns(1); 111 | 112 | if (ImGui::TreeNodeEx("Descriptors", ImGuiTreeNodeFlags_SpanFullWidth)) { 113 | for (std::size_t entry_index = 0; entry_index < descriptors.data().size(); entry_index++) { 114 | const auto entry_name = "Descriptor #" + std::to_string(entry_index); 115 | 116 | if (ImGui::TreeNodeEx(entry_name.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) { 117 | descriptors.data().at(entry_index).draw(); 118 | ImGui::TreePop(); 119 | } 120 | } 121 | 122 | ImGui::TreePop(); 123 | } 124 | } 125 | 126 | void Decima::VertexArrayResource::draw() { 127 | ImGui::Columns(2); 128 | 129 | { 130 | ImGui::Text("Property"); 131 | ImGui::NextColumn(); 132 | 133 | ImGui::Text("Value"); 134 | ImGui::NextColumn(); 135 | 136 | ImGui::Separator(); 137 | } 138 | 139 | ImGui::SetColumnWidth(0, 200); 140 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 141 | 142 | { 143 | ImGui::Text("Vertex count"); 144 | ImGui::NextColumn(); 145 | 146 | ImGui::Text("%u", vertex_count); 147 | ImGui::NextColumn(); 148 | 149 | ImGui::Separator(); 150 | } 151 | 152 | { 153 | ImGui::Text("Is streaming"); 154 | ImGui::NextColumn(); 155 | 156 | ImGui::Text("%u", is_streaming); 157 | ImGui::NextColumn(); 158 | 159 | ImGui::Separator(); 160 | } 161 | 162 | ImGui::Columns(1); 163 | 164 | if (ImGui::TreeNodeEx("Streams", ImGuiTreeNodeFlags_SpanFullWidth)) { 165 | for (std::size_t entry_index = 0; entry_index < vertex_stream_info.size(); entry_index++) { 166 | const auto entry_name = "Stream #" + std::to_string(entry_index); 167 | 168 | if (ImGui::TreeNodeEx(entry_name.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) { 169 | vertex_stream_info.at(entry_index).draw(); 170 | ImGui::TreePop(); 171 | } 172 | } 173 | 174 | ImGui::TreePop(); 175 | } 176 | } -------------------------------------------------------------------------------- /src/decima/serializable/object/texture.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/texture.hpp" 2 | 3 | const std::unordered_map Decima::texture_format_info { 4 | // clang-format off 5 | { Decima::TexturePixelFormat::BC1, { 4, 4, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, true } }, 6 | { Decima::TexturePixelFormat::BC3, { 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, true } }, 7 | { Decima::TexturePixelFormat::BC4, { 4, 4, GL_COMPRESSED_RED_RGTC1, 0, true } }, 8 | { Decima::TexturePixelFormat::BC5, { 4, 8, GL_COMPRESSED_RG_RGTC2, 0, true } }, 9 | { Decima::TexturePixelFormat::BC6, { 4, 8, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, 0, true } }, 10 | { Decima::TexturePixelFormat::BC7, { 4, 8, GL_COMPRESSED_RGBA_BPTC_UNORM, 0, true } }, 11 | { Decima::TexturePixelFormat::A8, { 1, 8, GL_R8, GL_RED, false } }, 12 | { Decima::TexturePixelFormat::RGBA8, { 1, 32, GL_RGBA8, GL_RGBA, false } }, 13 | { Decima::TexturePixelFormat::RGBA16F, { 1, 64, GL_RGBA16F, GL_RGBA, false } }, 14 | // clang-format on 15 | }; 16 | 17 | unsigned int Decima::TexturePixelFormatInfo::calculate_size(int width, int height) const { 18 | return width * height * block_density; 19 | } 20 | 21 | unsigned int Decima::TexturePixelFormatInfo::create_texture(int width, int height, const void* data, std::size_t size) const { 22 | GLuint texture_id; 23 | glGenTextures(1, &texture_id); 24 | glBindTexture(GL_TEXTURE_2D, texture_id); 25 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 26 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 27 | 28 | if (compressed) { 29 | glCompressedTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, size, data); 30 | } else { 31 | /* 32 | * This is really only a specific case for RGBA8, 33 | * but as far as we know there's more other unknown 34 | * formats for us at this moment, so let this be a 35 | * feature for the future (badum-tss). 36 | */ 37 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, data_format, GL_UNSIGNED_BYTE, data); 38 | } 39 | 40 | glBindTexture(GL_TEXTURE_2D, 0); 41 | 42 | return texture_id; 43 | } 44 | 45 | void Decima::Texture::parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) { 46 | CoreObject::parse(manager, buffer, file); 47 | type = buffer.get(); 48 | width = buffer.get(); 49 | height = buffer.get(); 50 | layers = buffer.get(); 51 | total_mips = buffer.get(); 52 | pixel_format = buffer.get(); 53 | unk_0 = buffer.get(); 54 | unk_1 = buffer.get(); 55 | unk_2.parse(buffer, file); 56 | buffer_size = buffer.get(); 57 | total_size = buffer.get(); 58 | stream_size = buffer.get(); 59 | stream_mips = buffer.get(); 60 | unk_3 = buffer.get(); 61 | unk_4 = buffer.get(); 62 | 63 | if (stream_size > 0) 64 | external_data.parse(manager, buffer, file); 65 | 66 | embedded_data.resize(total_size); 67 | 68 | /* TODO: 69 | * By some reason, some textures contain 70 | * total_size greater than actual embedded 71 | * buffer. Add this workaround until we'll 72 | * figure out what is happening. 73 | */ 74 | 75 | buffer.get(embedded_data.data(), std::min(embedded_data.size(), buffer.size())); 76 | 77 | if (const auto format = texture_format_info.find(pixel_format); format != texture_format_info.end()) { 78 | const auto [format_block_size, format_block_density, format_type_internal, format_type_data, format_compressed] = format->second; 79 | 80 | const char* stream_data = nullptr; 81 | 82 | for (auto mip = 0; mip < total_mips; mip++) { 83 | /* 84 | * Mips that are smaller than minimum block size 85 | * actually occupy size of one block and must be 86 | * cropped to the size of the mip, but we don't 87 | * do that just for the sake of simplicity. 88 | */ 89 | const auto mip_width = std::max(width >> mip, format_block_size); 90 | const auto mip_height = std::max(height >> mip, format_block_size); 91 | const auto mip_buffer_size = mip_width * mip_height * format_block_density / 8; 92 | 93 | if (mip == 0) 94 | stream_data = external_data.data().data(); 95 | 96 | if (mip >= stream_mips) 97 | stream_data = embedded_data.data(); 98 | 99 | mip_textures.push_back(format->second.create_texture(mip_width, mip_height, stream_data, mip_buffer_size)); 100 | stream_data += mip_buffer_size; 101 | } 102 | } 103 | } 104 | 105 | Decima::Texture::~Texture() { 106 | for (const auto texture_id : mip_textures) { 107 | glDeleteTextures(1, &texture_id); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/decima/archive/archive_file.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/archive/archive_file.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "decima/archive/archive.hpp" 10 | #include "decima/archive/archive_manager.hpp" 11 | #include "decima/serializable/object/object.hpp" 12 | #include "decima/serializable/handlers.hpp" 13 | #include "decima/serializable/reference.hpp" 14 | 15 | static void decrypt_chunk(uint8_t* data, const Decima::ArchiveChunkEntry& chunk_entry) { 16 | uint32_t iv[4]; 17 | MurmurHash3_x64_128(&chunk_entry, 0x10, Decima::cipher_seed, iv); 18 | 19 | iv[0] ^= Decima::chunk_cipher_key[0]; 20 | iv[1] ^= Decima::chunk_cipher_key[1]; 21 | iv[2] ^= Decima::chunk_cipher_key[2]; 22 | iv[3] ^= Decima::chunk_cipher_key[3]; 23 | 24 | uint8_t digest[16]; 25 | md5Hash((md5_byte_t*)iv, 16, digest); 26 | for (uint32_t i = 0; i < chunk_entry.compressed_span.size; i++) { 27 | data[i] ^= digest[i % 16]; 28 | } 29 | } 30 | 31 | std::vector unpack(Decima::Compressor& compressor, const Decima::Archive& archive, const Decima::ArchiveFileEntry& entry, std::ifstream& source) { 32 | auto aligned_offset = [](std::uint64_t offset, std::uint64_t alignment) { 33 | return offset & ~(alignment - 1); 34 | }; 35 | 36 | auto chunk_from_offset = [&](std::uint64_t offset) { 37 | return std::find_if(archive.chunk_entries.begin(), archive.chunk_entries.end(), [&](const auto& item) { 38 | return item.decompressed_span.offset == offset; 39 | }); 40 | }; 41 | 42 | auto [chunk_entry_begin, chunk_entry_end] = [&] { 43 | auto first_chunk_offset = aligned_offset(entry.span.offset, archive.header.chunk_maximum_size); 44 | auto last_chunk_offset = aligned_offset(entry.span.offset + entry.span.size, archive.header.chunk_maximum_size); 45 | 46 | return std::make_pair(chunk_from_offset(first_chunk_offset), chunk_from_offset(last_chunk_offset) + 1); 47 | }(); 48 | 49 | source.seekg(chunk_entry_begin->compressed_span.offset, std::ios::beg); 50 | 51 | std::size_t chunk_buffer_offset = 0; 52 | std::vector chunk_buffer; 53 | 54 | std::size_t result_buffer_size = 0; 55 | std::size_t result_buffer_offset = entry.span.offset & (archive.header.chunk_maximum_size - 1); 56 | std::vector result_buffer; 57 | 58 | std::for_each(chunk_entry_begin, chunk_entry_end, [&] (const Decima::ArchiveChunkEntry& chunk) { 59 | chunk_buffer.resize(chunk_buffer.size() + chunk.compressed_span.size); 60 | source.read((char*)chunk_buffer.data() + chunk_buffer_offset, chunk.compressed_span.size); 61 | if (archive.header.type == Decima::ArchiveType::Encrypted) 62 | decrypt_chunk((uint8_t*)chunk_buffer.data() + chunk_buffer_offset, chunk); 63 | chunk_buffer_offset += chunk.compressed_span.size; 64 | result_buffer_size += chunk.decompressed_span.size; 65 | }); 66 | 67 | result_buffer.resize(result_buffer_size); 68 | compressor.decompress(chunk_buffer, result_buffer); 69 | 70 | result_buffer.erase(result_buffer.begin(), result_buffer.begin() + result_buffer_offset); 71 | result_buffer.erase(result_buffer.begin() + entry.span.size, result_buffer.end()); 72 | 73 | return result_buffer; 74 | } 75 | 76 | Decima::CoreFile::CoreFile(Archive& archive, ArchiveManager& manager, ArchiveFileEntry& entry, std::ifstream& source) 77 | : archive(archive) 78 | , manager(manager) 79 | , entry(entry) 80 | , contents(unpack(*manager.compressor, archive, entry, source)) { } 81 | 82 | void Decima::CoreFile::resolve_reference(const std::shared_ptr& object) { 83 | auto index = std::remove_if(references.begin(), references.end(), [&](Ref* ref) { 84 | if (ref->m_guid.hash() == object->guid.hash()) { 85 | ref->m_object = object; 86 | return true; 87 | } 88 | 89 | return false; 90 | }); 91 | 92 | references.erase(index, references.end()); 93 | } 94 | 95 | void Decima::CoreFile::resolve_reference(const Decima::CoreFile& file) { 96 | for (auto& [object, object_offset] : objects) { 97 | resolve_reference(object); 98 | } 99 | } 100 | 101 | void Decima::CoreFile::queue_reference(Decima::Ref* ref) { 102 | if (ref->mode() == RefLoadMode::NotPresent || ref->mode() == RefLoadMode::WorkOnly) 103 | return; 104 | 105 | if (ref->mode() == RefLoadMode::ImmediateCoreFile || ref->mode() == RefLoadMode::CoreFile) { 106 | auto& file = manager.query_file(ref->file().data()).value().get(); 107 | file.references.push_back(ref); 108 | file.parse(); 109 | } else { 110 | references.push_back(ref); 111 | } 112 | } 113 | 114 | void Decima::CoreFile::parse() { 115 | if (objects.empty()) { 116 | // TODO: This is shitty API I wrote, please replace this 117 | ash::buffer buffer(contents.data(), contents.size()); 118 | 119 | while (buffer.size() > 0) { 120 | const auto entry_header = Decima::CoreObject::peek_header(buffer); 121 | const auto entry_offset = buffer.data() - contents.data(); 122 | 123 | /* 124 | * Tricky hack to allow objects during parsing. 125 | * This is useful for getting owner of the reference 126 | */ 127 | auto handler = Decima::get_type_handler(entry_header.file_type); 128 | objects.emplace_back(handler, entry_offset); 129 | handler->parse(manager, buffer, *this); 130 | } 131 | } 132 | 133 | resolve_reference(*this); 134 | } 135 | -------------------------------------------------------------------------------- /include/decima/serializable/object/resource/vertex_array_resource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decima/serializable/object/resource/resource.hpp" 4 | #include "decima/serializable/array.hpp" 5 | #include "decima/serializable/guid.hpp" 6 | #include "decima/shared.hpp" 7 | 8 | namespace Decima { 9 | enum class VertexElementType : std::uint8_t { 10 | Pos = 0, 11 | TangentBFlip = 1, 12 | Tangent = 2, 13 | Binormal = 3, 14 | Normal = 4, 15 | Color = 5, 16 | UV0 = 6, 17 | UV1 = 7, 18 | UV2 = 8, 19 | UV3 = 9, 20 | UV4 = 10, 21 | UV5 = 11, 22 | UV6 = 12, 23 | MotionVec = 13, 24 | Vec4Byte0 = 14, 25 | Vec4Byte1 = 15, 26 | BlendWeights = 16, 27 | BlendIndices = 17, 28 | BlendWeights2 = 18, 29 | BlendIndices2 = 19, 30 | BlendWeights3 = 20, 31 | BlendIndices3 = 21, 32 | PivotPoint = 22, 33 | AltPos = 23, 34 | AltTangent = 24, 35 | AltBinormal = 25, 36 | AltNormal = 26, 37 | AltColor = 27, 38 | AltUV0 = 28, 39 | Invalid = 29 40 | }; 41 | 42 | enum class VertexElementStorageType : std::uint8_t { 43 | Undefined = 0, 44 | SignedShortNormalized = 1, 45 | Float = 2, 46 | HalfFloat = 3, 47 | UnsignedByteNormalized = 4, 48 | SignedShort = 5, 49 | XYZ10W2Normalized = 6, 50 | UnsignedByte = 7, 51 | UnsignedShort = 8, 52 | UnsignedShortNormalized = 9, 53 | UnsignedNormalized8sRGB = 10, 54 | XYZ10W2UnsignedNormalized = 11 55 | }; 56 | 57 | class VertexStreamData { 58 | public: 59 | void parse(ash::buffer& buffer, CoreFile& file); 60 | void draw(); 61 | 62 | public: 63 | std::uint8_t offset; 64 | VertexElementStorageType storage_type; 65 | std::uint8_t slots_used; 66 | VertexElementType element_type; 67 | }; 68 | 69 | class VertexStreamInfo { 70 | public: 71 | void parse(ash::buffer& buffer, CoreFile& file); 72 | void draw(); 73 | 74 | public: 75 | std::uint32_t flags; 76 | std::uint32_t stride; 77 | Array descriptors; 78 | GUID resource_uuid; 79 | }; 80 | 81 | class VertexArrayResource : public Resource { 82 | public: 83 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 84 | void draw() override; 85 | 86 | public: 87 | std::uint32_t vertex_count; 88 | std::uint32_t vertex_stream_count; 89 | std::uint8_t is_streaming; 90 | std::vector vertex_stream_info; 91 | }; 92 | } 93 | 94 | template <> 95 | inline std::string Decima::to_string(const VertexElementType& value) { 96 | switch (value) { 97 | case VertexElementType::Invalid: 98 | return "Invalid"; 99 | case VertexElementType::Pos: 100 | return "Position"; 101 | case VertexElementType::TangentBFlip: 102 | return "Tangent binormal flip"; 103 | case VertexElementType::Tangent: 104 | return "Tangent"; 105 | case VertexElementType::Binormal: 106 | return "Binormal"; 107 | case VertexElementType::Normal: 108 | return "Normal"; 109 | case VertexElementType::Color: 110 | return "Color"; 111 | case VertexElementType::UV0: 112 | case VertexElementType::UV1: 113 | case VertexElementType::UV2: 114 | case VertexElementType::UV3: 115 | case VertexElementType::UV4: 116 | case VertexElementType::UV5: 117 | case VertexElementType::UV6: 118 | return "UV"; 119 | case VertexElementType::MotionVec: 120 | return "Motion vector"; 121 | case VertexElementType::Vec4Byte0: 122 | case VertexElementType::Vec4Byte1: 123 | return "Vec4"; 124 | case VertexElementType::BlendWeights: 125 | case VertexElementType::BlendWeights2: 126 | case VertexElementType::BlendWeights3: 127 | return "Blend weights"; 128 | case VertexElementType::BlendIndices: 129 | case VertexElementType::BlendIndices3: 130 | case VertexElementType::BlendIndices2: 131 | return "Blend indices"; 132 | case VertexElementType::PivotPoint: 133 | return "Pivot point"; 134 | case VertexElementType::AltPos: 135 | return "Alternate position"; 136 | case VertexElementType::AltTangent: 137 | return "Alternate tangent"; 138 | case VertexElementType::AltBinormal: 139 | return "Alternate binormal"; 140 | case VertexElementType::AltNormal: 141 | return "Alternate normal"; 142 | case VertexElementType::AltColor: 143 | return "Alternate color"; 144 | case VertexElementType::AltUV0: 145 | return "Alternate UV"; 146 | default: 147 | return "Unknown: " + std::to_string(static_cast(value)); 148 | } 149 | } 150 | 151 | template <> 152 | inline std::string Decima::to_string(const VertexElementStorageType& value) { 153 | switch (value) { 154 | case VertexElementStorageType::Undefined: 155 | return "Undefined"; 156 | case VertexElementStorageType::UnsignedByteNormalized: 157 | return "Unsigned Int8 (normalized)"; 158 | case VertexElementStorageType::UnsignedByte: 159 | return "Unsigned Int8"; 160 | case VertexElementStorageType::SignedShortNormalized: 161 | return "Signed Int16 (normalized)"; 162 | case VertexElementStorageType::SignedShort: 163 | return "Signed Int16"; 164 | case VertexElementStorageType::UnsignedShort: 165 | return "Unsigned Int16"; 166 | case VertexElementStorageType::UnsignedShortNormalized: 167 | return "Unsigned Int16 (normalized)"; 168 | case VertexElementStorageType::Float: 169 | return "Float32"; 170 | case VertexElementStorageType::HalfFloat: 171 | return "Float16"; 172 | case VertexElementStorageType::XYZ10W2Normalized: 173 | return "Signed XYZ10W2 (normalized)"; 174 | case VertexElementStorageType::XYZ10W2UnsignedNormalized: 175 | return "Unsigned XYZ10W2 (normalized)"; 176 | case VertexElementStorageType::UnsignedNormalized8sRGB: 177 | return "Unsigned Int8 sRGB (normalized)"; 178 | default: 179 | return "Unknown: " + std::to_string(static_cast(value)); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/decima/serializable/object/texture_set_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/texture_set.hpp" 2 | 3 | #include 4 | 5 | void Decima::TextureDefaultColor::draw() { 6 | ImGui::SetNextItemWidth(-1.0f); 7 | ImGui::InputFloat4("##", rgba, "%.3f", ImGuiInputTextFlags_ReadOnly); 8 | } 9 | 10 | void Decima::DecimaTextureSetEntry::draw() { 11 | ImGui::Columns(2); 12 | 13 | { 14 | ImGui::Text("Property"); 15 | ImGui::NextColumn(); 16 | 17 | ImGui::Text("Value"); 18 | ImGui::NextColumn(); 19 | 20 | ImGui::Separator(); 21 | } 22 | 23 | ImGui::SetColumnWidth(0, 200); 24 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 25 | 26 | { 27 | ImGui::Text("Compression method"); 28 | ImGui::NextColumn(); 29 | 30 | ImGui::Text("%s", Decima::to_string(compression_method).c_str()); 31 | ImGui::NextColumn(); 32 | 33 | ImGui::Separator(); 34 | } 35 | 36 | { 37 | ImGui::Text("Create mip-maps"); 38 | ImGui::NextColumn(); 39 | 40 | ImGui::Text("%u", create_mip_maps); 41 | ImGui::NextColumn(); 42 | 43 | ImGui::Separator(); 44 | } 45 | 46 | { 47 | ImGui::Text("Color space"); 48 | ImGui::NextColumn(); 49 | 50 | ImGui::Text("%s", Decima::to_string(color_space).c_str()); 51 | ImGui::NextColumn(); 52 | 53 | ImGui::Separator(); 54 | } 55 | 56 | { 57 | ImGui::Text("Packing info"); 58 | ImGui::NextColumn(); 59 | 60 | ImGui::Text("%u", packing_info); 61 | ImGui::NextColumn(); 62 | 63 | ImGui::Separator(); 64 | } 65 | 66 | { 67 | ImGui::Text("Texture type"); 68 | ImGui::NextColumn(); 69 | 70 | ImGui::Text("%s", Decima::to_string(texture_type).c_str()); 71 | ImGui::NextColumn(); 72 | 73 | ImGui::Separator(); 74 | } 75 | 76 | { 77 | ImGui::Text("Texture"); 78 | ImGui::NextColumn(); 79 | 80 | texture.draw(); 81 | ImGui::NextColumn(); 82 | 83 | ImGui::Separator(); 84 | } 85 | 86 | ImGui::Columns(1); 87 | } 88 | 89 | void Decima::DecimaTextureSetTextureDescriptor::draw() { 90 | ImGui::Columns(2); 91 | 92 | { 93 | ImGui::Text("Property"); 94 | ImGui::NextColumn(); 95 | 96 | ImGui::Text("Value"); 97 | ImGui::NextColumn(); 98 | 99 | ImGui::Separator(); 100 | } 101 | 102 | ImGui::SetColumnWidth(0, 200); 103 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 104 | 105 | { 106 | ImGui::Text("Texture type"); 107 | ImGui::NextColumn(); 108 | 109 | ImGui::Text("%s", Decima::to_string(texture_type).c_str()); 110 | ImGui::NextColumn(); 111 | 112 | ImGui::Separator(); 113 | } 114 | 115 | { 116 | ImGui::Text("Path"); 117 | ImGui::NextColumn(); 118 | 119 | path.draw(); 120 | ImGui::NextColumn(); 121 | 122 | ImGui::Separator(); 123 | } 124 | 125 | { 126 | ImGui::Text("Active"); 127 | ImGui::NextColumn(); 128 | 129 | ImGui::Text("%u", active); 130 | ImGui::NextColumn(); 131 | 132 | ImGui::Separator(); 133 | } 134 | 135 | { 136 | ImGui::Text("Gamma space"); 137 | ImGui::NextColumn(); 138 | 139 | ImGui::Text("%u", gamma_space); 140 | ImGui::NextColumn(); 141 | 142 | ImGui::Separator(); 143 | } 144 | 145 | { 146 | ImGui::Text("Storage type"); 147 | ImGui::NextColumn(); 148 | 149 | ImGui::Text("%s", Decima::to_string(storage_type).c_str()); 150 | ImGui::NextColumn(); 151 | 152 | ImGui::Separator(); 153 | } 154 | 155 | { 156 | ImGui::Text("Quality type"); 157 | ImGui::NextColumn(); 158 | 159 | ImGui::Text("%s", Decima::to_string(quality_type).c_str()); 160 | ImGui::NextColumn(); 161 | 162 | ImGui::Separator(); 163 | } 164 | 165 | { 166 | ImGui::Text("Compression method"); 167 | ImGui::NextColumn(); 168 | 169 | ImGui::Text("%s", Decima::to_string(compression_method).c_str()); 170 | ImGui::NextColumn(); 171 | 172 | ImGui::Separator(); 173 | } 174 | 175 | if(active > 0) { 176 | { 177 | ImGui::Text("Width"); 178 | ImGui::NextColumn(); 179 | 180 | ImGui::Text("%u", width); 181 | ImGui::NextColumn(); 182 | 183 | ImGui::Separator(); 184 | } 185 | 186 | { 187 | ImGui::Text("Height"); 188 | ImGui::NextColumn(); 189 | 190 | ImGui::Text("%u", height); 191 | ImGui::NextColumn(); 192 | 193 | ImGui::Separator(); 194 | } 195 | } else { 196 | { 197 | ImGui::Text("Unknown #0"); 198 | ImGui::NextColumn(); 199 | 200 | ImGui::Text("%u", unk_0); 201 | ImGui::NextColumn(); 202 | 203 | ImGui::Separator(); 204 | } 205 | } 206 | 207 | { 208 | ImGui::Text("Default color"); 209 | ImGui::NextColumn(); 210 | 211 | default_color.draw(); 212 | ImGui::NextColumn(); 213 | 214 | ImGui::Separator(); 215 | } 216 | 217 | ImGui::Columns(1); 218 | } 219 | 220 | void Decima::TextureSet::draw() { 221 | ImGui::Columns(2); 222 | 223 | { 224 | ImGui::Text("Property"); 225 | ImGui::NextColumn(); 226 | 227 | ImGui::Text("Value"); 228 | ImGui::NextColumn(); 229 | 230 | ImGui::Separator(); 231 | } 232 | 233 | ImGui::SetColumnWidth(0, 200); 234 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 235 | 236 | { 237 | ImGui::Text("Mip-map mode"); 238 | ImGui::NextColumn(); 239 | 240 | ImGui::Text("%s", Decima::to_string(mip_map_mode).c_str()); 241 | ImGui::NextColumn(); 242 | 243 | ImGui::Separator(); 244 | } 245 | 246 | { 247 | ImGui::Text("Preset"); 248 | ImGui::NextColumn(); 249 | 250 | preset.draw(); 251 | ImGui::NextColumn(); 252 | 253 | ImGui::Separator(); 254 | } 255 | 256 | ImGui::Columns(1); 257 | 258 | if (ImGui::TreeNodeEx("Entries", ImGuiTreeNodeFlags_SpanFullWidth)) { 259 | for (std::size_t entry_index = 0; entry_index < entries.size(); entry_index++) { 260 | const auto entry_name = "Entry #" + std::to_string(entry_index); 261 | 262 | if (ImGui::TreeNodeEx(entry_name.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) { 263 | entries.at(entry_index).draw(); 264 | ImGui::TreePop(); 265 | } 266 | } 267 | 268 | ImGui::TreePop(); 269 | } 270 | 271 | if (ImGui::TreeNodeEx("Descriptors", ImGuiTreeNodeFlags_SpanFullWidth)) { 272 | for (std::size_t descriptor_index = 0; descriptor_index < descriptors.size(); descriptor_index++) { 273 | const auto descriptor_name = "Descriptor #" + std::to_string(descriptor_index); 274 | 275 | if (ImGui::TreeNodeEx(descriptor_name.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) { 276 | descriptors.at(descriptor_index).draw(); 277 | ImGui::TreePop(); 278 | } 279 | } 280 | 281 | ImGui::TreePop(); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /include/decima/serializable/object/texture_set.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "decima/serializable/object/object.hpp" 6 | #include "decima/serializable/string.hpp" 7 | #include "decima/serializable/guid.hpp" 8 | #include "decima/serializable/reference.hpp" 9 | 10 | namespace Decima { 11 | enum class TextureColorSpace : std::uint32_t { 12 | Linear = 0, 13 | SRGB = 1 14 | }; 15 | 16 | enum class TextureSetType : std::uint32_t { 17 | Invalid = 0, 18 | Color = 1, 19 | Alpha = 2, 20 | Normal = 3, 21 | Reflectance = 4, 22 | AO = 5, 23 | Roughness = 6, 24 | Height = 7, 25 | Mask = 8, 26 | MaskAlpha = 9, 27 | Incandescence = 10, 28 | TranslucencyDiffusion = 11, 29 | TranslucencyAmount = 12, 30 | Misc_01 = 13, 31 | Curvature = 14, 32 | Luminance = 15 33 | }; 34 | 35 | enum class TextureSetStorageType : std::uint32_t { 36 | RGB = 0, 37 | R = 1, 38 | G = 2, 39 | B = 3, 40 | A = 4 41 | }; 42 | 43 | enum class TextureSetQualityType : std::uint32_t { 44 | Default = 0, 45 | CompressedHigh = 1, 46 | CompressedLow = 2, 47 | Uncompressed = 3, 48 | NormalBC6 = 4, 49 | NormalHigh = 5, 50 | NormalLow = 6, 51 | BC4 = 8, 52 | Clean = 7, 53 | NormalRoughnessBC7 = 9, 54 | AlphaToCoverageBC4 = 10 55 | }; 56 | 57 | enum class TextureCompressionMethod : std::uint32_t { 58 | PerceptualData = 0, 59 | NormalData = 1, 60 | VariableData = 2, 61 | DefaultData = 3 62 | }; 63 | 64 | enum class DecimaTextureMipMapMode : std::uint32_t { 65 | Wrap = 0, 66 | Clamp = 1, 67 | Mirror = 2, 68 | ClampToBorder = 3 69 | }; 70 | 71 | class TextureDefaultColor { 72 | public: 73 | void parse(ash::buffer& buffer, CoreFile& file); 74 | void draw(); 75 | 76 | private: 77 | float rgba[4]; 78 | }; 79 | 80 | class DecimaTextureSetEntry { 81 | public: 82 | void parse(ash::buffer& buffer, CoreFile& file); 83 | void draw(); 84 | 85 | private: 86 | TextureCompressionMethod compression_method; 87 | std::uint8_t create_mip_maps; 88 | TextureColorSpace color_space; 89 | std::uint32_t packing_info; 90 | TextureSetType texture_type; 91 | Ref texture; 92 | }; 93 | 94 | struct DecimaTextureSetTextureDescriptor { 95 | public: 96 | void parse(ash::buffer& buffer, CoreFile& file); 97 | void draw(); 98 | 99 | private: 100 | TextureSetType texture_type; 101 | StringHashed path; 102 | std::uint8_t active; 103 | std::uint8_t gamma_space; 104 | TextureSetStorageType storage_type; 105 | TextureSetQualityType quality_type; 106 | TextureCompressionMethod compression_method; 107 | std::uint32_t width {}; 108 | std::uint32_t height {}; 109 | std::uint32_t unk_0 {}; 110 | TextureDefaultColor default_color; 111 | }; 112 | 113 | class TextureSet : public CoreObject { 114 | public: 115 | void parse(ArchiveManager& manager, ash::buffer& buffer, CoreFile& file) override; 116 | void draw() override; 117 | 118 | private: 119 | std::vector entries; 120 | DecimaTextureMipMapMode mip_map_mode; 121 | std::vector descriptors; 122 | Ref preset; 123 | }; 124 | } 125 | 126 | template <> 127 | inline std::string Decima::to_string(const Decima::TextureColorSpace& value) { 128 | switch (value) { 129 | case TextureColorSpace::Linear: 130 | return "Linear"; 131 | case TextureColorSpace::SRGB: 132 | return "sRGB"; 133 | default: 134 | return "Unknown: " + std::to_string(static_cast(value)); 135 | } 136 | } 137 | 138 | template <> 139 | inline std::string Decima::to_string(const Decima::TextureSetType& value) { 140 | switch (value) { 141 | case TextureSetType::Invalid: 142 | return "Invalid"; 143 | case TextureSetType::Color: 144 | return "Color"; 145 | case TextureSetType::Alpha: 146 | return "Alpha"; 147 | case TextureSetType::Normal: 148 | return "Normal"; 149 | case TextureSetType::Reflectance: 150 | return "Reflectance"; 151 | case TextureSetType::AO: 152 | return "AO"; 153 | case TextureSetType::Roughness: 154 | return "Roughness"; 155 | case TextureSetType::Height: 156 | return "Height"; 157 | case TextureSetType::Mask: 158 | return "Mask"; 159 | case TextureSetType::MaskAlpha: 160 | return "Mask alpha"; 161 | case TextureSetType::Incandescence: 162 | return "Incandescence"; 163 | case TextureSetType::TranslucencyDiffusion: 164 | return "Translucency diffusion"; 165 | case TextureSetType::TranslucencyAmount: 166 | return "Translucency amount"; 167 | case TextureSetType::Misc_01: 168 | return "Misc_01"; 169 | case TextureSetType::Curvature: 170 | return "Curvature"; 171 | case TextureSetType::Luminance: 172 | return "Luminance"; 173 | default: 174 | return "Unknown: " + std::to_string(static_cast(value)); 175 | } 176 | } 177 | 178 | template <> 179 | inline std::string Decima::to_string(const Decima::TextureSetStorageType& value) { 180 | switch (value) { 181 | case TextureSetStorageType::RGB: 182 | return "RGB"; 183 | case TextureSetStorageType::R: 184 | return "R"; 185 | case TextureSetStorageType::G: 186 | return "G"; 187 | case TextureSetStorageType::B: 188 | return "B"; 189 | case TextureSetStorageType::A: 190 | return "A"; 191 | default: 192 | return "Unknown: " + std::to_string(static_cast(value)); 193 | } 194 | } 195 | 196 | template <> 197 | inline std::string Decima::to_string(const Decima::TextureSetQualityType& value) { 198 | switch (value) { 199 | case TextureSetQualityType::Default: 200 | return "Default"; 201 | case TextureSetQualityType::CompressedHigh: 202 | return "Compressed (High)"; 203 | case TextureSetQualityType::CompressedLow: 204 | return "Compressed (Low)"; 205 | case TextureSetQualityType::Uncompressed: 206 | return "Uncompressed"; 207 | case TextureSetQualityType::NormalBC6: 208 | return "Normal (BC6)"; 209 | case TextureSetQualityType::NormalHigh: 210 | return "Normal (High)"; 211 | case TextureSetQualityType::NormalLow: 212 | return "Normal (Low)"; 213 | case TextureSetQualityType::BC4: 214 | return "BC4"; 215 | case TextureSetQualityType::Clean: 216 | return "Clean"; 217 | case TextureSetQualityType::NormalRoughnessBC7: 218 | return "Normal roughness (BC7)"; 219 | case TextureSetQualityType::AlphaToCoverageBC4: 220 | return "Alpha to coverage (BC4)"; 221 | default: 222 | return "Unknown: " + std::to_string(static_cast(value)); 223 | } 224 | } 225 | 226 | template <> 227 | inline std::string Decima::to_string(const Decima::TextureCompressionMethod& value) { 228 | switch (value) { 229 | case TextureCompressionMethod::PerceptualData: 230 | return "Perceptual data"; 231 | case TextureCompressionMethod::NormalData: 232 | return "Normal data"; 233 | case TextureCompressionMethod::VariableData: 234 | return "Variable data"; 235 | case TextureCompressionMethod::DefaultData: 236 | return "Default data"; 237 | default: 238 | return "Unknown: " + std::to_string(static_cast(value)); 239 | } 240 | } 241 | 242 | template <> 243 | inline std::string Decima::to_string(const Decima::DecimaTextureMipMapMode& value) { 244 | switch (value) { 245 | case DecimaTextureMipMapMode::Wrap: 246 | return "Wrap"; 247 | case DecimaTextureMipMapMode::Clamp: 248 | return "Clamp"; 249 | case DecimaTextureMipMapMode::Mirror: 250 | return "Mirror"; 251 | case DecimaTextureMipMapMode::ClampToBorder: 252 | return "Clamp to border"; 253 | default: 254 | return "Unknown: " + std::to_string(static_cast(value)); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /libs/hash/MurmurHash3.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MurmurHash3 was written by Austin Appleby, and is placed in the public 3 | // domain. The author hereby disclaims copyright to this source code. 4 | 5 | // Note - The x86 and x64 versions do _not_ produce the same results, as the 6 | // algorithms are optimized for their respective platforms. You can still 7 | // compile and run any of them on any platform, but your performance with the 8 | // non-native version will be less than optimal. 9 | 10 | #include "MurmurHash3.h" 11 | 12 | //----------------------------------------------------------------------------- 13 | // Platform-specific functions and macros 14 | 15 | // Microsoft Visual Studio 16 | 17 | #if defined(_MSC_VER) 18 | 19 | #define FORCE_INLINE __forceinline 20 | 21 | #include 22 | 23 | #define ROTL32(x,y) _rotl(x,y) 24 | #define ROTL64(x,y) _rotl64(x,y) 25 | 26 | #define BIG_CONSTANT(x) (x) 27 | 28 | // Other compilers 29 | 30 | #else // defined(_MSC_VER) 31 | 32 | #define FORCE_INLINE inline __attribute__((always_inline)) 33 | 34 | inline uint32_t rotl32(uint32_t x, int8_t r) 35 | { 36 | return (x << r) | (x >> (32 - r)); 37 | } 38 | 39 | inline uint64_t rotl64(uint64_t x, int8_t r) 40 | { 41 | return (x << r) | (x >> (64 - r)); 42 | } 43 | 44 | #define ROTL32(x,y) rotl32(x,y) 45 | #define ROTL64(x,y) rotl64(x,y) 46 | 47 | #define BIG_CONSTANT(x) (x##LLU) 48 | 49 | #endif // !defined(_MSC_VER) 50 | 51 | //----------------------------------------------------------------------------- 52 | // Block read - if your platform needs to do endian-swapping or can only 53 | // handle aligned reads, do the conversion here 54 | 55 | FORCE_INLINE uint32_t getblock32(const uint32_t * p, int i) 56 | { 57 | return p[i]; 58 | } 59 | 60 | FORCE_INLINE uint64_t getblock64(const uint64_t * p, int i) 61 | { 62 | return p[i]; 63 | } 64 | 65 | //----------------------------------------------------------------------------- 66 | // Finalization mix - force all bits of a hash block to avalanche 67 | 68 | FORCE_INLINE uint32_t fmix32(uint32_t h) 69 | { 70 | h ^= h >> 16; 71 | h *= 0x85ebca6b; 72 | h ^= h >> 13; 73 | h *= 0xc2b2ae35; 74 | h ^= h >> 16; 75 | 76 | return h; 77 | } 78 | 79 | //---------- 80 | 81 | FORCE_INLINE uint64_t fmix64(uint64_t k) 82 | { 83 | k ^= k >> 33; 84 | k *= BIG_CONSTANT(0xff51afd7ed558ccd); 85 | k ^= k >> 33; 86 | k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); 87 | k ^= k >> 33; 88 | 89 | return k; 90 | } 91 | 92 | //----------------------------------------------------------------------------- 93 | 94 | void MurmurHash3_x86_32(const void * key, int len, 95 | uint32_t seed, void * out) 96 | { 97 | const uint8_t * data = (const uint8_t*)key; 98 | const int nblocks = len / 4; 99 | 100 | uint32_t h1 = seed; 101 | 102 | const uint32_t c1 = 0xcc9e2d51; 103 | const uint32_t c2 = 0x1b873593; 104 | 105 | //---------- 106 | // body 107 | 108 | const uint32_t * blocks = (const uint32_t *)(data + nblocks * 4); 109 | 110 | for (int i = -nblocks; i; i++) 111 | { 112 | uint32_t k1 = getblock32(blocks, i); 113 | 114 | k1 *= c1; 115 | k1 = ROTL32(k1, 15); 116 | k1 *= c2; 117 | 118 | h1 ^= k1; 119 | h1 = ROTL32(h1, 13); 120 | h1 = h1 * 5 + 0xe6546b64; 121 | } 122 | 123 | //---------- 124 | // tail 125 | 126 | const uint8_t * tail = (const uint8_t*)(data + nblocks * 4); 127 | 128 | uint32_t k1 = 0; 129 | 130 | switch (len & 3) 131 | { 132 | case 3: k1 ^= tail[2] << 16; 133 | case 2: k1 ^= tail[1] << 8; 134 | case 1: k1 ^= tail[0]; 135 | k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; 136 | }; 137 | 138 | //---------- 139 | // finalization 140 | 141 | h1 ^= len; 142 | 143 | h1 = fmix32(h1); 144 | 145 | *(uint32_t*)out = h1; 146 | } 147 | 148 | //----------------------------------------------------------------------------- 149 | 150 | void MurmurHash3_x86_128(const void * key, const int len, 151 | uint32_t seed, void * out) 152 | { 153 | const uint8_t * data = (const uint8_t*)key; 154 | const int nblocks = len / 16; 155 | 156 | uint32_t h1 = seed; 157 | uint32_t h2 = seed; 158 | uint32_t h3 = seed; 159 | uint32_t h4 = seed; 160 | 161 | const uint32_t c1 = 0x239b961b; 162 | const uint32_t c2 = 0xab0e9789; 163 | const uint32_t c3 = 0x38b34ae5; 164 | const uint32_t c4 = 0xa1e38b93; 165 | 166 | //---------- 167 | // body 168 | 169 | const uint32_t * blocks = (const uint32_t *)(data + nblocks * 16); 170 | 171 | for (int i = -nblocks; i; i++) 172 | { 173 | uint32_t k1 = getblock32(blocks, i * 4 + 0); 174 | uint32_t k2 = getblock32(blocks, i * 4 + 1); 175 | uint32_t k3 = getblock32(blocks, i * 4 + 2); 176 | uint32_t k4 = getblock32(blocks, i * 4 + 3); 177 | 178 | k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; 179 | 180 | h1 = ROTL32(h1, 19); h1 += h2; h1 = h1 * 5 + 0x561ccd1b; 181 | 182 | k2 *= c2; k2 = ROTL32(k2, 16); k2 *= c3; h2 ^= k2; 183 | 184 | h2 = ROTL32(h2, 17); h2 += h3; h2 = h2 * 5 + 0x0bcaa747; 185 | 186 | k3 *= c3; k3 = ROTL32(k3, 17); k3 *= c4; h3 ^= k3; 187 | 188 | h3 = ROTL32(h3, 15); h3 += h4; h3 = h3 * 5 + 0x96cd1c35; 189 | 190 | k4 *= c4; k4 = ROTL32(k4, 18); k4 *= c1; h4 ^= k4; 191 | 192 | h4 = ROTL32(h4, 13); h4 += h1; h4 = h4 * 5 + 0x32ac3b17; 193 | } 194 | 195 | //---------- 196 | // tail 197 | 198 | const uint8_t * tail = (const uint8_t*)(data + nblocks * 16); 199 | 200 | uint32_t k1 = 0; 201 | uint32_t k2 = 0; 202 | uint32_t k3 = 0; 203 | uint32_t k4 = 0; 204 | 205 | switch (len & 15) 206 | { 207 | case 15: k4 ^= tail[14] << 16; 208 | case 14: k4 ^= tail[13] << 8; 209 | case 13: k4 ^= tail[12] << 0; 210 | k4 *= c4; k4 = ROTL32(k4, 18); k4 *= c1; h4 ^= k4; 211 | 212 | case 12: k3 ^= tail[11] << 24; 213 | case 11: k3 ^= tail[10] << 16; 214 | case 10: k3 ^= tail[9] << 8; 215 | case 9: k3 ^= tail[8] << 0; 216 | k3 *= c3; k3 = ROTL32(k3, 17); k3 *= c4; h3 ^= k3; 217 | 218 | case 8: k2 ^= tail[7] << 24; 219 | case 7: k2 ^= tail[6] << 16; 220 | case 6: k2 ^= tail[5] << 8; 221 | case 5: k2 ^= tail[4] << 0; 222 | k2 *= c2; k2 = ROTL32(k2, 16); k2 *= c3; h2 ^= k2; 223 | 224 | case 4: k1 ^= tail[3] << 24; 225 | case 3: k1 ^= tail[2] << 16; 226 | case 2: k1 ^= tail[1] << 8; 227 | case 1: k1 ^= tail[0] << 0; 228 | k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; 229 | }; 230 | 231 | //---------- 232 | // finalization 233 | 234 | h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; 235 | 236 | h1 += h2; h1 += h3; h1 += h4; 237 | h2 += h1; h3 += h1; h4 += h1; 238 | 239 | h1 = fmix32(h1); 240 | h2 = fmix32(h2); 241 | h3 = fmix32(h3); 242 | h4 = fmix32(h4); 243 | 244 | h1 += h2; h1 += h3; h1 += h4; 245 | h2 += h1; h3 += h1; h4 += h1; 246 | 247 | ((uint32_t*)out)[0] = h1; 248 | ((uint32_t*)out)[1] = h2; 249 | ((uint32_t*)out)[2] = h3; 250 | ((uint32_t*)out)[3] = h4; 251 | } 252 | 253 | //----------------------------------------------------------------------------- 254 | 255 | void MurmurHash3_x64_128(const void * key, const int len, 256 | const uint32_t seed, void * out) 257 | { 258 | const uint8_t * data = (const uint8_t*)key; 259 | const int nblocks = len / 16; 260 | 261 | uint64_t h1 = seed; 262 | uint64_t h2 = seed; 263 | 264 | const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); 265 | const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); 266 | 267 | //---------- 268 | // body 269 | 270 | const uint64_t * blocks = (const uint64_t *)(data); 271 | 272 | for (int i = 0; i < nblocks; i++) 273 | { 274 | uint64_t k1 = getblock64(blocks, i * 2 + 0); 275 | uint64_t k2 = getblock64(blocks, i * 2 + 1); 276 | 277 | k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h1 ^= k1; 278 | 279 | h1 = ROTL64(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; 280 | 281 | k2 *= c2; k2 = ROTL64(k2, 33); k2 *= c1; h2 ^= k2; 282 | 283 | h2 = ROTL64(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; 284 | } 285 | 286 | //---------- 287 | // tail 288 | 289 | const uint8_t * tail = (const uint8_t*)(data + nblocks * 16); 290 | 291 | uint64_t k1 = 0; 292 | uint64_t k2 = 0; 293 | 294 | switch (len & 15) 295 | { 296 | case 15: k2 ^= ((uint64_t)tail[14]) << 48; 297 | case 14: k2 ^= ((uint64_t)tail[13]) << 40; 298 | case 13: k2 ^= ((uint64_t)tail[12]) << 32; 299 | case 12: k2 ^= ((uint64_t)tail[11]) << 24; 300 | case 11: k2 ^= ((uint64_t)tail[10]) << 16; 301 | case 10: k2 ^= ((uint64_t)tail[9]) << 8; 302 | case 9: k2 ^= ((uint64_t)tail[8]) << 0; 303 | k2 *= c2; k2 = ROTL64(k2, 33); k2 *= c1; h2 ^= k2; 304 | 305 | case 8: k1 ^= ((uint64_t)tail[7]) << 56; 306 | case 7: k1 ^= ((uint64_t)tail[6]) << 48; 307 | case 6: k1 ^= ((uint64_t)tail[5]) << 40; 308 | case 5: k1 ^= ((uint64_t)tail[4]) << 32; 309 | case 4: k1 ^= ((uint64_t)tail[3]) << 24; 310 | case 3: k1 ^= ((uint64_t)tail[2]) << 16; 311 | case 2: k1 ^= ((uint64_t)tail[1]) << 8; 312 | case 1: k1 ^= ((uint64_t)tail[0]) << 0; 313 | k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h1 ^= k1; 314 | }; 315 | 316 | //---------- 317 | // finalization 318 | 319 | h1 ^= len; h2 ^= len; 320 | 321 | h1 += h2; 322 | h2 += h1; 323 | 324 | h1 = fmix64(h1); 325 | h2 = fmix64(h2); 326 | 327 | h1 += h2; 328 | h2 += h1; 329 | 330 | ((uint64_t*)out)[0] = h1; 331 | ((uint64_t*)out)[1] = h2; 332 | } 333 | 334 | //----------------------------------------------------------------------------- 335 | -------------------------------------------------------------------------------- /src/decima/serializable/object/texture_draw.cpp: -------------------------------------------------------------------------------- 1 | #include "decima/serializable/object/texture.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "utils.hpp" 7 | #include "projectds_app.hpp" 8 | 9 | void Decima::Texture::draw() { 10 | ImGui::Columns(2); 11 | 12 | { 13 | ImGui::Text("Property"); 14 | ImGui::NextColumn(); 15 | 16 | ImGui::Text("Value"); 17 | ImGui::NextColumn(); 18 | 19 | ImGui::Separator(); 20 | } 21 | 22 | ImGui::SetColumnWidth(0, 200); 23 | ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() - 200); 24 | 25 | { 26 | ImGui::Text("Type"); 27 | ImGui::NextColumn(); 28 | 29 | ImGui::Text("%s", Decima::to_string(type).c_str()); 30 | ImGui::NextColumn(); 31 | 32 | ImGui::Separator(); 33 | } 34 | 35 | { 36 | ImGui::Text("Width"); 37 | ImGui::NextColumn(); 38 | 39 | ImGui::Text("%u", width); 40 | ImGui::NextColumn(); 41 | 42 | ImGui::Separator(); 43 | } 44 | 45 | { 46 | ImGui::Text("Height"); 47 | ImGui::NextColumn(); 48 | 49 | ImGui::Text("%u", height); 50 | ImGui::NextColumn(); 51 | 52 | ImGui::Separator(); 53 | } 54 | 55 | { 56 | ImGui::Text("Layers"); 57 | ImGui::NextColumn(); 58 | 59 | ImGui::Text("%u", layers); 60 | ImGui::NextColumn(); 61 | 62 | ImGui::Separator(); 63 | } 64 | 65 | { 66 | ImGui::Text("Total mip-maps"); 67 | ImGui::NextColumn(); 68 | 69 | ImGui::Text("%u", total_mips); 70 | ImGui::NextColumn(); 71 | 72 | ImGui::Separator(); 73 | } 74 | 75 | { 76 | ImGui::Text("Format"); 77 | ImGui::NextColumn(); 78 | 79 | ImGui::Text("%s", Decima::to_string(pixel_format).c_str()); 80 | ImGui::NextColumn(); 81 | 82 | ImGui::Separator(); 83 | } 84 | 85 | { 86 | ImGui::Text("Unknown #0"); 87 | ImGui::NextColumn(); 88 | 89 | ImGui::Text("%u", unk_0); 90 | ImGui::NextColumn(); 91 | 92 | ImGui::Separator(); 93 | } 94 | 95 | { 96 | ImGui::Text("Unknown #1"); 97 | ImGui::NextColumn(); 98 | 99 | ImGui::Text("%u", unk_1); 100 | ImGui::NextColumn(); 101 | 102 | ImGui::Separator(); 103 | } 104 | 105 | { 106 | ImGui::Text("Unknown #2"); 107 | ImGui::NextColumn(); 108 | 109 | guid.draw(); 110 | ImGui::NextColumn(); 111 | 112 | ImGui::Separator(); 113 | } 114 | 115 | { 116 | ImGui::Text("Buffer size"); 117 | ImGui::NextColumn(); 118 | 119 | ImGui::Text("%u", buffer_size); 120 | ImGui::NextColumn(); 121 | 122 | ImGui::Separator(); 123 | } 124 | 125 | { 126 | ImGui::Text("Total size"); 127 | ImGui::NextColumn(); 128 | 129 | ImGui::Text("%u", total_size); 130 | ImGui::NextColumn(); 131 | 132 | ImGui::Separator(); 133 | } 134 | 135 | { 136 | ImGui::Text("Stream size"); 137 | ImGui::NextColumn(); 138 | 139 | ImGui::Text("%u", stream_size); 140 | ImGui::NextColumn(); 141 | 142 | ImGui::Separator(); 143 | } 144 | 145 | { 146 | ImGui::Text("Stream mip-maps"); 147 | ImGui::NextColumn(); 148 | 149 | ImGui::Text("%u", stream_mips); 150 | ImGui::NextColumn(); 151 | 152 | ImGui::Separator(); 153 | } 154 | 155 | { 156 | ImGui::Text("Unknown #3"); 157 | ImGui::NextColumn(); 158 | 159 | ImGui::Text("%u", unk_3); 160 | ImGui::NextColumn(); 161 | 162 | ImGui::Separator(); 163 | } 164 | 165 | { 166 | ImGui::Text("Unknown #4"); 167 | ImGui::NextColumn(); 168 | 169 | ImGui::Text("%u", unk_4); 170 | ImGui::NextColumn(); 171 | 172 | ImGui::Separator(); 173 | } 174 | 175 | { 176 | ImGui::Text("Stream"); 177 | ImGui::NextColumn(); 178 | 179 | if (stream_size > 0) { 180 | ImGui::BeginChild(unk_2.hash(), { 0, 150 }, true); 181 | external_data.draw(); 182 | ImGui::EndChild(); 183 | } else { 184 | ImGui::Text("No external stream"); 185 | } 186 | 187 | draw_preview(256, 256, 128, 4); 188 | 189 | ImGui::NextColumn(); 190 | } 191 | 192 | ImGui::Columns(1); 193 | } 194 | 195 | void Decima::Texture::draw_preview(float preview_width, float preview_height, float zoom_region, float zoom_scale) { 196 | if (mip_textures.empty()) { 197 | ImGui::TextDisabled("No preview available"); 198 | return; 199 | } 200 | 201 | if (mip_textures.size() > 1) { 202 | if (ImGui::ArrowButton("Up", ImGuiDir_Left)) 203 | mip_index = std::max(0, mip_index - 1); 204 | ImGui::SameLine(); 205 | if (ImGui::ArrowButton("Down", ImGuiDir_Right)) 206 | mip_index = std::min(int(mip_textures.size()) - 1, mip_index + 1); 207 | ImGui::SameLine(); 208 | ImGui::PushItemWidth(150); 209 | ImGui::DragInt("##", &mip_index, 0.05f, 0, mip_textures.size() - 1, "Mip #%d"); 210 | } 211 | 212 | ImGui::Text("Mip #%d (%s, %dx%d)", mip_index, mip_index >= stream_mips ? "Internal" : "External", width >> mip_index, height >> mip_index); 213 | 214 | glEnable(GL_BLEND); 215 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 216 | 217 | const ImVec2 pos = ImGui::GetCursorScreenPos(); 218 | const ImVec4 tint = { 1, 1, 1, 1 }; 219 | const ImVec4 border = { 1, 1, 1, 1 }; 220 | ImGui::Image(reinterpret_cast(static_cast(mip_textures[mip_index])), { preview_width, preview_height }, { 0, 0 }, { 1, 1 }, tint, border); 221 | 222 | if (ImGui::BeginPopupContextItem("Export Image")) { 223 | if (ImGui::Selectable("Export image")) { 224 | auto full_path = pfd::save_file("Choose destination file", "", { "Truevision TGA (TARGA)", "*.tga" }).result(); 225 | 226 | if (!full_path.empty()) { 227 | full_path += ".tga"; 228 | 229 | DECIMA_PACK(struct TGAHeader { 230 | std::uint8_t id_length; 231 | std::uint8_t color_map_type; 232 | std::uint8_t image_type; 233 | std::uint16_t color_map_origin; 234 | std::uint16_t color_map_length; 235 | std::uint8_t color_map_depth; 236 | std::uint16_t x_origin; 237 | std::uint16_t y_origin; 238 | std::uint16_t width; 239 | std::uint16_t height; 240 | std::uint8_t pixel_depth; 241 | std::uint8_t image_descriptor; 242 | }); 243 | 244 | TGAHeader tga_header { 0 }; 245 | tga_header.image_type = 2; 246 | tga_header.width = width; 247 | tga_header.height = height; 248 | tga_header.pixel_depth = 32; 249 | tga_header.image_descriptor = 8; 250 | 251 | DECIMA_PACK(struct TGAFooter { 252 | std::uint32_t extension_offset; 253 | std::uint32_t dev_area_offset; 254 | std::int8_t signature[18]; 255 | }); 256 | 257 | TGAFooter tga_footer { 0 }; 258 | std::memcpy(tga_footer.signature, "TRUEVISION-XFILE.", 18); 259 | 260 | std::vector buffer; 261 | buffer.resize(width * height); 262 | 263 | glGetTextureImage(mip_textures[0], 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.size() * 4, buffer.data()); 264 | 265 | for (std::size_t y = 0; y < height / 2; y++) { 266 | for (std::size_t x = 0; x < width; x++) { 267 | auto& a = buffer[y * width + x]; 268 | auto& b = buffer[(height - y - 1) * width + x]; 269 | 270 | constexpr auto rgb2bgr = [](std::uint32_t color) { 271 | return (color & 0xff000000) | ((color & 0xff0000) >> 16) | (color & 0x00ff00) | ((color & 0x0000ff) << 16); 272 | }; 273 | 274 | a = rgb2bgr(a); 275 | b = rgb2bgr(b); 276 | 277 | std::swap(a, b); 278 | } 279 | } 280 | 281 | std::ofstream writer { full_path, std::ios::binary | std::ios::trunc }; 282 | writer.write(reinterpret_cast(&tga_header), sizeof(tga_header)); 283 | writer.write(reinterpret_cast(buffer.data()), buffer.size() * 4); 284 | writer.write(reinterpret_cast(&tga_footer), sizeof(tga_footer)); 285 | 286 | DECIMA_LOG("File was saved to: ", full_path); 287 | } 288 | } 289 | 290 | ImGui::EndPopup(); 291 | } 292 | 293 | if (ImGui::IsItemHovered()) { 294 | ImGui::BeginTooltip(); 295 | 296 | auto& io = ImGui::GetIO(); 297 | auto region_x = io.MousePos.x - pos.x - zoom_region * 0.5f; 298 | auto region_y = io.MousePos.y - pos.y - zoom_region * 0.5f; 299 | 300 | if (region_x < 0.0f) { 301 | region_x = 0.0f; 302 | } else if (region_x > preview_width - zoom_region) { 303 | region_x = preview_width - zoom_region; 304 | } 305 | 306 | if (region_y < 0.0f) { 307 | region_y = 0.0f; 308 | } else if (region_y > preview_height - zoom_region) { 309 | region_y = preview_height - zoom_region; 310 | } 311 | 312 | ImVec2 uv0 = { region_x / preview_width, region_y / preview_height }; 313 | ImVec2 uv1 = { (region_x + zoom_region) / preview_width, (region_y + zoom_region) / preview_height }; 314 | ImGui::Image(reinterpret_cast(static_cast(mip_textures[mip_index])), ImVec2(zoom_region * zoom_scale, zoom_region * zoom_scale), uv0, uv1, tint, border); 315 | 316 | ImGui::EndTooltip(); 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/decima/archive/archive_tree.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by MED45 on 27.07.2020. 3 | // 4 | 5 | #include 6 | 7 | #include "decima/archive/archive_tree.hpp" 8 | #include "decima/archive/archive_manager.hpp" 9 | #include "decima/archive/archive.hpp" 10 | 11 | #include 12 | #include "utils.hpp" 13 | 14 | FileTree* FileTree::add_folder(const std::string& name) { 15 | if (folders.find(name) == folders.end()) 16 | folders.emplace(name, std::make_pair(std::make_unique(), true)); 17 | return folders.at(name).first.get(); 18 | } 19 | 20 | void FileTree::add_file(const std::string& filename, uint64_t hash, Decima::CoreHeader header) { 21 | add_file(filename, filename, hash, header); 22 | } 23 | 24 | void FileTree::add_file(const std::string& path, const std::string& filename, uint64_t hash, Decima::CoreHeader header) { 25 | FileInfo info {}; 26 | info.path = path; 27 | info.name = filename; 28 | info.hash = hash; 29 | files.emplace(filename, std::make_pair(std::move(info), true)); 30 | } 31 | 32 | bool is_filter_matches(FileTree& root, const ImGuiTextFilter& filter) { 33 | bool result = false; 34 | 35 | for (auto& [name, data] : root.files) { 36 | if (filter.PassFilter(name.c_str())) { 37 | data.second = true; 38 | result = true; 39 | } 40 | } 41 | 42 | for (auto& [name, data] : root.folders) { 43 | if (is_filter_matches(*data.first, filter)) { 44 | data.second = true; 45 | result = true; 46 | } 47 | } 48 | 49 | return result; 50 | } 51 | 52 | FileTree::ExpandMode FileTree::apply_filter(const ImGuiTextFilter& filter) { 53 | if (filter.IsActive()) { 54 | reset_filter(false); 55 | is_filter_matches(*this, filter); 56 | } else { 57 | reset_filter(true); 58 | } 59 | 60 | return size() < ExpandThreshold ? ExpandMode::Show : ExpandMode::Hide; 61 | } 62 | 63 | void FileTree::reset_filter(bool visibility) { 64 | for (auto& [_, data] : folders) { 65 | data.first->reset_filter(visibility); 66 | data.second = visibility; 67 | } 68 | 69 | for (auto& [_, data] : files) { 70 | data.second = visibility; 71 | } 72 | } 73 | 74 | void FileTree::draw(SelectionInfo& selection, Decima::ArchiveManager& manager, bool header, ExpandMode expand) { 75 | if (header) { 76 | ImGui::BeginChild(ImGui::GetID("TreeView")); 77 | 78 | ImGui::Separator(); 79 | 80 | ImGui::Columns(3); 81 | ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() - 200); 82 | ImGui::SetColumnWidth(1, 100); 83 | ImGui::SetColumnWidth(2, 100); 84 | 85 | ImGui::Text("Name"); 86 | ImGui::NextColumn(); 87 | ImGui::Text("Type"); 88 | ImGui::NextColumn(); 89 | ImGui::Text("Size"); 90 | ImGui::NextColumn(); 91 | 92 | ImGui::Separator(); 93 | 94 | ImGui::Columns(1); 95 | 96 | ImGui::BeginChild(ImGui::GetID("TreeViewContents")); 97 | 98 | ImGui::Columns(3); 99 | ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() - 200); 100 | ImGui::SetColumnWidth(1, 100); 101 | ImGui::SetColumnWidth(2, 100); 102 | } 103 | 104 | for (auto& [name, data] : folders) { 105 | if (!data.second) 106 | continue; 107 | 108 | if (expand != ExpandMode::None) 109 | ImGui::SetNextItemOpen(expand == ExpandMode::Show, ImGuiCond_Always); 110 | 111 | const auto tree_name = name + "##" + std::to_string(folders.size()); 112 | const auto show = ImGui::TreeNodeEx(tree_name.c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick); 113 | const auto items_count = data.first->files.size() + data.first->folders.size(); 114 | 115 | if (ImGui::IsItemClicked(ImGuiMouseButton_Left) && !ImGui::IsItemToggledOpen() && ImGui::GetIO().KeyCtrl) { 116 | /* 117 | * Check whether all files in this folders are selected or not, 118 | * if selected, then deselect everything, if at least one is not selected, 119 | * then select everything 120 | */ 121 | 122 | std::set folder_files; 123 | 124 | for (const auto& file : data.first->files) { 125 | folder_files.insert(file.second.first.hash); 126 | } 127 | 128 | bool contains_all = true; 129 | 130 | for (const auto& item : folder_files) { 131 | if (selection.selected_files.find(item) == selection.selected_files.end()) { 132 | contains_all = false; 133 | break; 134 | } 135 | } 136 | 137 | if (contains_all) { 138 | for (const auto& item : folder_files) 139 | selection.selected_files.erase(item); 140 | } else { 141 | for (const auto& item : folder_files) 142 | selection.selected_files.insert(item); 143 | } 144 | } 145 | 146 | ImGui::NextColumn(); 147 | ImGui::Text("Folder"); 148 | ImGui::NextColumn(); 149 | ImGui::Text("%llu item%c", items_count, items_count == 1 ? ' ' : 's'); 150 | ImGui::NextColumn(); 151 | 152 | if (show) { 153 | if (items_count > 0) { 154 | data.first->draw(selection, manager, false, expand); 155 | } else { 156 | ImGui::TextDisabled("Empty"); 157 | ImGui::NextColumn(); 158 | ImGui::NextColumn(); 159 | ImGui::NextColumn(); 160 | } 161 | ImGui::TreePop(); 162 | } 163 | } 164 | 165 | for (auto& [name, data] : files) { 166 | if (!data.second) 167 | continue; 168 | 169 | if (expand != ExpandMode::None) 170 | ImGui::SetNextItemOpen(expand == ExpandMode::Show, ImGuiCond_Always); 171 | 172 | const auto is_selected = selection.selected_files.find(data.first.hash) != selection.selected_files.end(); 173 | 174 | ImGui::TreeNodeEx(name.c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf | static_cast(is_selected)); 175 | 176 | if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()) { 177 | selection.selected_file = data.first.hash; 178 | 179 | if (ImGui::GetIO().KeyCtrl) { 180 | if (is_selected) { 181 | selection.selected_files.erase(data.first.hash); 182 | } else { 183 | selection.selected_files.insert(data.first.hash); 184 | } 185 | } 186 | } else if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsItemHovered()) { 187 | selection.highlighted_file = data.first; 188 | ImGui::OpenPopup("TreeContextMenu"); 189 | } 190 | 191 | ImGui::SetNextWindowSize({ 320, 0 }, ImGuiCond_Always); 192 | 193 | if (ImGui::BeginPopupContextWindow("TreeContextMenu")) { 194 | ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, { 0, 0 }); 195 | ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); 196 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 1, 1, 1, 0.5 }); 197 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 1, 1, 1, 0.4 }); 198 | 199 | if (ImGui::Button("Copy name", { -1, 0 })) { 200 | ImGui::SetClipboardText(selection.highlighted_file.path.c_str()); 201 | ImGui::CloseCurrentPopup(); 202 | } 203 | 204 | if (ImGui::Button("Copy hash", { -1, 0 })) { 205 | ImGui::SetClipboardText(uint64_to_hex(selection.highlighted_file.hash).c_str()); 206 | ImGui::CloseCurrentPopup(); 207 | } 208 | 209 | ImGui::Separator(); 210 | 211 | if (ImGui::Button("Find out what references this file", { -1, 0 })) { 212 | DECIMA_LOG("What references file '", selection.highlighted_file.path, "':"); 213 | 214 | auto index = manager.hash_to_index.at(selection.highlighted_file.hash); 215 | auto links = manager.prefetch->links.at(index).data(); 216 | auto paths = manager.prefetch->paths.data(); 217 | 218 | for (auto& link : links) { 219 | DECIMA_LOG(" - '", paths.at(link).data(), "'"); 220 | } 221 | 222 | if (links.empty()) { 223 | DECIMA_LOG(" - "); 224 | } 225 | 226 | ImGui::CloseCurrentPopup(); 227 | } 228 | 229 | if (ImGui::Button("Find out what is referenced by this file", { -1, 0 })) { 230 | DECIMA_LOG("What is referenced by file '", selection.highlighted_file.path, "':"); 231 | 232 | auto index = manager.hash_to_index.at(selection.highlighted_file.hash); 233 | auto paths = manager.prefetch->paths.data(); 234 | auto found = false; 235 | 236 | for (std::uint64_t link_index = 0; link_index < paths.size(); link_index++) { 237 | for (auto& link : manager.prefetch->links.at(link_index).data()) { 238 | if (link == index) { 239 | DECIMA_LOG(" - '", paths.at(link_index).data(), "'"); 240 | found = true; 241 | } 242 | } 243 | } 244 | 245 | if (!found) { 246 | DECIMA_LOG(" - "); 247 | } 248 | 249 | ImGui::CloseCurrentPopup(); 250 | } 251 | 252 | ImGui::PopStyleColor(3); 253 | ImGui::PopStyleVar(); 254 | ImGui::EndPopup(); 255 | } 256 | 257 | ImGui::TreePop(); 258 | 259 | ImGui::NextColumn(); 260 | ImGui::Text("File"); 261 | ImGui::NextColumn(); 262 | 263 | const auto file_entry = manager.get_file_entry(data.first.hash); 264 | 265 | if (file_entry.has_value()) { 266 | ImGui::Text("%s", format_size(file_entry.value().get().span.size).c_str()); 267 | } else { 268 | ImGui::Text("Unknown"); 269 | } 270 | 271 | ImGui::NextColumn(); 272 | } 273 | 274 | if (header) { 275 | ImGui::Columns(1); 276 | ImGui::EndChild(); 277 | ImGui::EndChild(); 278 | } 279 | } 280 | 281 | std::size_t FileTree::size() const { 282 | std::size_t size = 0; 283 | 284 | for (auto& [name, data] : files) { 285 | if (data.second) 286 | size += 1; 287 | } 288 | 289 | for (auto& [name, data] : folders) { 290 | size += data.first.get()->size(); 291 | } 292 | 293 | return size; 294 | } 295 | -------------------------------------------------------------------------------- /libs/glad/include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | 157 | #elif defined(__VMS ) || defined(__sgi) 158 | 159 | /* 160 | * Using 161 | */ 162 | #include 163 | typedef int32_t khronos_int32_t; 164 | typedef uint32_t khronos_uint32_t; 165 | typedef int64_t khronos_int64_t; 166 | typedef uint64_t khronos_uint64_t; 167 | #define KHRONOS_SUPPORT_INT64 1 168 | #define KHRONOS_SUPPORT_FLOAT 1 169 | 170 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 171 | 172 | /* 173 | * Win32 174 | */ 175 | typedef __int32 khronos_int32_t; 176 | typedef unsigned __int32 khronos_uint32_t; 177 | typedef __int64 khronos_int64_t; 178 | typedef unsigned __int64 khronos_uint64_t; 179 | #define KHRONOS_SUPPORT_INT64 1 180 | #define KHRONOS_SUPPORT_FLOAT 1 181 | 182 | #elif defined(__sun__) || defined(__digital__) 183 | 184 | /* 185 | * Sun or Digital 186 | */ 187 | typedef int khronos_int32_t; 188 | typedef unsigned int khronos_uint32_t; 189 | #if defined(__arch64__) || defined(_LP64) 190 | typedef long int khronos_int64_t; 191 | typedef unsigned long int khronos_uint64_t; 192 | #else 193 | typedef long long int khronos_int64_t; 194 | typedef unsigned long long int khronos_uint64_t; 195 | #endif /* __arch64__ */ 196 | #define KHRONOS_SUPPORT_INT64 1 197 | #define KHRONOS_SUPPORT_FLOAT 1 198 | 199 | #elif 0 200 | 201 | /* 202 | * Hypothetical platform with no float or int64 support 203 | */ 204 | typedef int khronos_int32_t; 205 | typedef unsigned int khronos_uint32_t; 206 | #define KHRONOS_SUPPORT_INT64 0 207 | #define KHRONOS_SUPPORT_FLOAT 0 208 | 209 | #else 210 | 211 | /* 212 | * Generic fallback 213 | */ 214 | #include 215 | typedef int32_t khronos_int32_t; 216 | typedef uint32_t khronos_uint32_t; 217 | typedef int64_t khronos_int64_t; 218 | typedef uint64_t khronos_uint64_t; 219 | #define KHRONOS_SUPPORT_INT64 1 220 | #define KHRONOS_SUPPORT_FLOAT 1 221 | 222 | #endif 223 | 224 | 225 | /* 226 | * Types that are (so far) the same on all platforms 227 | */ 228 | typedef signed char khronos_int8_t; 229 | typedef unsigned char khronos_uint8_t; 230 | typedef signed short int khronos_int16_t; 231 | typedef unsigned short int khronos_uint16_t; 232 | 233 | /* 234 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 235 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 236 | * to be the only LLP64 architecture in current use. 237 | */ 238 | #ifdef _WIN64 239 | typedef signed long long int khronos_intptr_t; 240 | typedef unsigned long long int khronos_uintptr_t; 241 | typedef signed long long int khronos_ssize_t; 242 | typedef unsigned long long int khronos_usize_t; 243 | #else 244 | typedef signed long int khronos_intptr_t; 245 | typedef unsigned long int khronos_uintptr_t; 246 | typedef signed long int khronos_ssize_t; 247 | typedef unsigned long int khronos_usize_t; 248 | #endif 249 | 250 | #if KHRONOS_SUPPORT_FLOAT 251 | /* 252 | * Float type 253 | */ 254 | typedef float khronos_float_t; 255 | #endif 256 | 257 | #if KHRONOS_SUPPORT_INT64 258 | /* Time types 259 | * 260 | * These types can be used to represent a time interval in nanoseconds or 261 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 262 | * of nanoseconds since some arbitrary system event (e.g. since the last 263 | * time the system booted). The Unadjusted System Time is an unsigned 264 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 265 | * may be either signed or unsigned. 266 | */ 267 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 268 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 269 | #endif 270 | 271 | /* 272 | * Dummy value used to pad enum types to 32 bits. 273 | */ 274 | #ifndef KHRONOS_MAX_ENUM 275 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 276 | #endif 277 | 278 | /* 279 | * Enumerated boolean type 280 | * 281 | * Values other than zero should be considered to be true. Therefore 282 | * comparisons should not be made against KHRONOS_TRUE. 283 | */ 284 | typedef enum { 285 | KHRONOS_FALSE = 0, 286 | KHRONOS_TRUE = 1, 287 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 288 | } khronos_boolean_enum_t; 289 | 290 | #endif /* __khrplatform_h_ */ 291 | -------------------------------------------------------------------------------- /libs/hash/md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | Permission is granted to anyone to use this software for any purpose, 7 | including commercial applications, and to alter it and redistribute it 8 | freely, subject to the following restrictions: 9 | 1. The origin of this software must not be misrepresented; you must not 10 | claim that you wrote the original software. If you use this software 11 | in a product, an acknowledgment in the product documentation would be 12 | appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and must not be 14 | misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source distribution. 16 | L. Peter Deutsch 17 | ghost@aladdin.com 18 | */ 19 | /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ 20 | /* 21 | Independent implementation of MD5 (RFC 1321). 22 | This code implements the MD5 Algorithm defined in RFC 1321, whose 23 | text is available at 24 | http://www.ietf.org/rfc/rfc1321.txt 25 | The code is derived from the text of the RFC, including the test suite 26 | (section A.5) but excluding the rest of Appendix A. It does not include 27 | any code or documentation that is identified in the RFC as being 28 | copyrighted. 29 | The original and principal author of md5.c is L. Peter Deutsch 30 | . Other authors are noted in the change history 31 | that follows (in reverse chronological order): 32 | 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order 33 | either statically or dynamically; added missing #include 34 | in library. 35 | 2002-03-11 lpd Corrected argument list for main(), and added int return 36 | type, in test program and T value program. 37 | 2002-02-21 lpd Added missing #include in test program. 38 | 2000-07-03 lpd Patched to eliminate warnings about "constant is 39 | unsigned in ANSI C, signed in traditional"; made test program 40 | self-checking. 41 | 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 42 | 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 43 | 1999-05-03 lpd Original version. 44 | */ 45 | 46 | #include "md5.h" 47 | #include 48 | 49 | #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ 50 | #ifdef ARCH_IS_BIG_ENDIAN 51 | # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) 52 | #else 53 | # define BYTE_ORDER 0 54 | #endif 55 | 56 | #define T_MASK ((md5_word_t)~0) 57 | #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) 58 | #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) 59 | #define T3 0x242070db 60 | #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) 61 | #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) 62 | #define T6 0x4787c62a 63 | #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) 64 | #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) 65 | #define T9 0x698098d8 66 | #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) 67 | #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) 68 | #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) 69 | #define T13 0x6b901122 70 | #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) 71 | #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) 72 | #define T16 0x49b40821 73 | #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) 74 | #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) 75 | #define T19 0x265e5a51 76 | #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) 77 | #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) 78 | #define T22 0x02441453 79 | #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) 80 | #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) 81 | #define T25 0x21e1cde6 82 | #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) 83 | #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) 84 | #define T28 0x455a14ed 85 | #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) 86 | #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) 87 | #define T31 0x676f02d9 88 | #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) 89 | #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) 90 | #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) 91 | #define T35 0x6d9d6122 92 | #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) 93 | #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) 94 | #define T38 0x4bdecfa9 95 | #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) 96 | #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) 97 | #define T41 0x289b7ec6 98 | #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) 99 | #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) 100 | #define T44 0x04881d05 101 | #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) 102 | #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) 103 | #define T47 0x1fa27cf8 104 | #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) 105 | #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) 106 | #define T50 0x432aff97 107 | #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) 108 | #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) 109 | #define T53 0x655b59c3 110 | #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) 111 | #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) 112 | #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) 113 | #define T57 0x6fa87e4f 114 | #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) 115 | #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) 116 | #define T60 0x4e0811a1 117 | #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) 118 | #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) 119 | #define T63 0x2ad7d2bb 120 | #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) 121 | 122 | 123 | static void 124 | md5_process(md5_state_t* pms, const md5_byte_t* data /*[64]*/) { 125 | md5_word_t 126 | a = pms->abcd[0], b = pms->abcd[1], 127 | c = pms->abcd[2], d = pms->abcd[3]; 128 | md5_word_t t; 129 | #if BYTE_ORDER > 0 130 | /* Define storage only for big-endian CPUs. */ 131 | md5_word_t X[16]; 132 | #else 133 | /* Define storage for little-endian or both types of CPUs. */ 134 | md5_word_t xbuf[16]; 135 | const md5_word_t* X; 136 | #endif 137 | 138 | { 139 | #if BYTE_ORDER == 0 140 | /* 141 | * Determine dynamically whether this is a big-endian or 142 | * little-endian machine, since we can use a more efficient 143 | * algorithm on the latter. 144 | */ 145 | static const int w = 1; 146 | 147 | if (*((const md5_byte_t*) &w)) /* dynamic little-endian */ 148 | #endif 149 | #if BYTE_ORDER <= 0 /* little-endian */ 150 | { 151 | /* 152 | * On little-endian machines, we can process properly aligned 153 | * data without copying it. 154 | */ 155 | if (!((data - (const md5_byte_t*) 0) & 3)) { 156 | /* data are properly aligned */ 157 | X = (const md5_word_t*) data; 158 | } else { 159 | /* not aligned */ 160 | memcpy(xbuf, data, 64); 161 | X = xbuf; 162 | } 163 | } 164 | #endif 165 | #if BYTE_ORDER == 0 166 | else /* dynamic big-endian */ 167 | #endif 168 | #if BYTE_ORDER >= 0 /* big-endian */ 169 | { 170 | /* 171 | * On big-endian machines, we must arrange the bytes in the 172 | * right order. 173 | */ 174 | const md5_byte_t* xp = data; 175 | int i; 176 | 177 | # if BYTE_ORDER == 0 178 | X = xbuf; /* (dynamic only) */ 179 | # else 180 | # define xbuf X /* (static only) */ 181 | # endif 182 | for (i = 0; i < 16; ++i, xp += 4) 183 | xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); 184 | } 185 | #endif 186 | } 187 | 188 | #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) 189 | 190 | /* Round 1. */ 191 | /* Let [abcd k s i] denote the operation 192 | a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ 193 | #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) 194 | #define SET(a, b, c, d, k, s, Ti)\ 195 | t = a + F(b,c,d) + X[k] + Ti;\ 196 | a = ROTATE_LEFT(t, s) + b 197 | /* Do the following 16 operations. */ 198 | SET(a, b, c, d, 0, 7, T1); 199 | SET(d, a, b, c, 1, 12, T2); 200 | SET(c, d, a, b, 2, 17, T3); 201 | SET(b, c, d, a, 3, 22, T4); 202 | SET(a, b, c, d, 4, 7, T5); 203 | SET(d, a, b, c, 5, 12, T6); 204 | SET(c, d, a, b, 6, 17, T7); 205 | SET(b, c, d, a, 7, 22, T8); 206 | SET(a, b, c, d, 8, 7, T9); 207 | SET(d, a, b, c, 9, 12, T10); 208 | SET(c, d, a, b, 10, 17, T11); 209 | SET(b, c, d, a, 11, 22, T12); 210 | SET(a, b, c, d, 12, 7, T13); 211 | SET(d, a, b, c, 13, 12, T14); 212 | SET(c, d, a, b, 14, 17, T15); 213 | SET(b, c, d, a, 15, 22, T16); 214 | #undef SET 215 | 216 | /* Round 2. */ 217 | /* Let [abcd k s i] denote the operation 218 | a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ 219 | #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) 220 | #define SET(a, b, c, d, k, s, Ti)\ 221 | t = a + G(b,c,d) + X[k] + Ti;\ 222 | a = ROTATE_LEFT(t, s) + b 223 | /* Do the following 16 operations. */ 224 | SET(a, b, c, d, 1, 5, T17); 225 | SET(d, a, b, c, 6, 9, T18); 226 | SET(c, d, a, b, 11, 14, T19); 227 | SET(b, c, d, a, 0, 20, T20); 228 | SET(a, b, c, d, 5, 5, T21); 229 | SET(d, a, b, c, 10, 9, T22); 230 | SET(c, d, a, b, 15, 14, T23); 231 | SET(b, c, d, a, 4, 20, T24); 232 | SET(a, b, c, d, 9, 5, T25); 233 | SET(d, a, b, c, 14, 9, T26); 234 | SET(c, d, a, b, 3, 14, T27); 235 | SET(b, c, d, a, 8, 20, T28); 236 | SET(a, b, c, d, 13, 5, T29); 237 | SET(d, a, b, c, 2, 9, T30); 238 | SET(c, d, a, b, 7, 14, T31); 239 | SET(b, c, d, a, 12, 20, T32); 240 | #undef SET 241 | 242 | /* Round 3. */ 243 | /* Let [abcd k s t] denote the operation 244 | a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ 245 | #define H(x, y, z) ((x) ^ (y) ^ (z)) 246 | #define SET(a, b, c, d, k, s, Ti)\ 247 | t = a + H(b,c,d) + X[k] + Ti;\ 248 | a = ROTATE_LEFT(t, s) + b 249 | /* Do the following 16 operations. */ 250 | SET(a, b, c, d, 5, 4, T33); 251 | SET(d, a, b, c, 8, 11, T34); 252 | SET(c, d, a, b, 11, 16, T35); 253 | SET(b, c, d, a, 14, 23, T36); 254 | SET(a, b, c, d, 1, 4, T37); 255 | SET(d, a, b, c, 4, 11, T38); 256 | SET(c, d, a, b, 7, 16, T39); 257 | SET(b, c, d, a, 10, 23, T40); 258 | SET(a, b, c, d, 13, 4, T41); 259 | SET(d, a, b, c, 0, 11, T42); 260 | SET(c, d, a, b, 3, 16, T43); 261 | SET(b, c, d, a, 6, 23, T44); 262 | SET(a, b, c, d, 9, 4, T45); 263 | SET(d, a, b, c, 12, 11, T46); 264 | SET(c, d, a, b, 15, 16, T47); 265 | SET(b, c, d, a, 2, 23, T48); 266 | #undef SET 267 | 268 | /* Round 4. */ 269 | /* Let [abcd k s t] denote the operation 270 | a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ 271 | #define I(x, y, z) ((y) ^ ((x) | ~(z))) 272 | #define SET(a, b, c, d, k, s, Ti)\ 273 | t = a + I(b,c,d) + X[k] + Ti;\ 274 | a = ROTATE_LEFT(t, s) + b 275 | /* Do the following 16 operations. */ 276 | SET(a, b, c, d, 0, 6, T49); 277 | SET(d, a, b, c, 7, 10, T50); 278 | SET(c, d, a, b, 14, 15, T51); 279 | SET(b, c, d, a, 5, 21, T52); 280 | SET(a, b, c, d, 12, 6, T53); 281 | SET(d, a, b, c, 3, 10, T54); 282 | SET(c, d, a, b, 10, 15, T55); 283 | SET(b, c, d, a, 1, 21, T56); 284 | SET(a, b, c, d, 8, 6, T57); 285 | SET(d, a, b, c, 15, 10, T58); 286 | SET(c, d, a, b, 6, 15, T59); 287 | SET(b, c, d, a, 13, 21, T60); 288 | SET(a, b, c, d, 4, 6, T61); 289 | SET(d, a, b, c, 11, 10, T62); 290 | SET(c, d, a, b, 2, 15, T63); 291 | SET(b, c, d, a, 9, 21, T64); 292 | #undef SET 293 | 294 | /* Then perform the following additions. (That is increment each 295 | of the four registers by the value it had before this block 296 | was started.) */ 297 | pms->abcd[0] += a; 298 | pms->abcd[1] += b; 299 | pms->abcd[2] += c; 300 | pms->abcd[3] += d; 301 | } 302 | 303 | void 304 | md5_init(md5_state_t* pms) { 305 | pms->count[0] = pms->count[1] = 0; 306 | pms->abcd[0] = 0x67452301; 307 | pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; 308 | pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; 309 | pms->abcd[3] = 0x10325476; 310 | } 311 | 312 | void 313 | md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes) { 314 | const md5_byte_t* p = data; 315 | int left = nbytes; 316 | int offset = (pms->count[0] >> 3) & 63; 317 | md5_word_t nbits = (md5_word_t) (nbytes << 3); 318 | 319 | if (nbytes <= 0) 320 | return; 321 | 322 | /* Update the message length. */ 323 | pms->count[1] += nbytes >> 29; 324 | pms->count[0] += nbits; 325 | if (pms->count[0] < nbits) 326 | pms->count[1]++; 327 | 328 | /* Process an initial partial block. */ 329 | if (offset) { 330 | int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); 331 | 332 | memcpy(pms->buf + offset, p, copy); 333 | if (offset + copy < 64) 334 | return; 335 | p += copy; 336 | left -= copy; 337 | md5_process(pms, pms->buf); 338 | } 339 | 340 | /* Process full blocks. */ 341 | for (; left >= 64; p += 64, left -= 64) 342 | md5_process(pms, p); 343 | 344 | /* Process a final partial block. */ 345 | if (left) 346 | memcpy(pms->buf, p, left); 347 | } 348 | 349 | void 350 | md5_finish(md5_state_t* pms, md5_byte_t digest[16]) { 351 | static const md5_byte_t pad[64] = { 352 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 353 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 354 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 355 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 356 | }; 357 | md5_byte_t data[8]; 358 | int i; 359 | 360 | /* Save the length before padding. */ 361 | for (i = 0; i < 8; ++i) 362 | data[i] = (md5_byte_t) (pms->count[i >> 2] >> ((i & 3) << 3)); 363 | /* Pad to 56 bytes mod 64. */ 364 | md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); 365 | /* Append the length. */ 366 | md5_append(pms, data, 8); 367 | for (i = 0; i < 16; ++i) 368 | digest[i] = (md5_byte_t) (pms->abcd[i >> 2] >> ((i & 3) << 3)); 369 | } 370 | 371 | void 372 | md5Hash(md5_byte_t* in, int size, md5_byte_t* digest_out) { 373 | md5_state_t state; 374 | 375 | md5_init(&state); 376 | md5_append(&state, in, size); 377 | md5_finish(&state, digest_out); 378 | 379 | } -------------------------------------------------------------------------------- /src/projectds_app_draw.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by i.getsman on 06.08.2020. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include "projectds_app.hpp" 9 | 10 | #include "decima/serializable/handlers.hpp" 11 | #include "util/pfd.h" 12 | #include "utils.hpp" 13 | 14 | static void show_data_selection_dialog(ProjectDS& self) { 15 | std::string folder = pfd::select_folder("Select game folder").result(); 16 | std::string compressor_file; 17 | 18 | if (!folder.empty()) { 19 | for (auto file : std::filesystem::recursive_directory_iterator(folder)) { 20 | auto filename = file.path().filename(); 21 | 22 | if (filename.extension() == ".dll" && filename.string().find("oo2core") == 0 && compressor_file.empty()) { 23 | compressor_file = file.path().string(); 24 | } 25 | 26 | if (filename.extension() == ".bin") { 27 | DECIMA_LOG("Loading archive '", file.path().stem().string(), "'"); 28 | self.archive_manager.load_archive(file.path().string()); 29 | } 30 | } 31 | 32 | if (compressor_file.empty()) { 33 | DECIMA_LOG("Could not find compressor library"); 34 | 35 | while (true) { 36 | auto result = pfd::open_file("Select oo2core_X_win64.dll", "", { "oo2core_X_win64.dll", "oo2core_*_win64.dll" }).result(); 37 | 38 | if (!result.empty()) { 39 | compressor_file = result[0]; 40 | break; 41 | } 42 | } 43 | } 44 | 45 | self.archive_manager.compressor = std::make_unique(compressor_file); 46 | 47 | DECIMA_LOG("Using compressor '", std::filesystem::path(compressor_file).filename().string(), "' (version ", self.archive_manager.compressor->get_version_string(), ")"); 48 | 49 | if (self.archive_manager.compressor->get_version() < 0x2E070030) { 50 | pfd::message("Unsupported compressor", "Compressor library version must be at least 2.7.0 (oo2core_7_win64) or greater", pfd::choice::ok, pfd::icon::error); 51 | std::exit(EXIT_FAILURE); 52 | } 53 | 54 | self.archive_manager.load_prefetch(); 55 | 56 | self.file_names.clear(); 57 | self.file_names.reserve(self.archive_manager.hash_to_name.size()); 58 | 59 | std::thread([](ProjectDS& self) { 60 | self.root_tree_constructing = true; 61 | 62 | for (const auto& [hash, path] : self.archive_manager.hash_to_name) { 63 | self.file_names.push_back(path.c_str()); 64 | 65 | std::vector split_path; 66 | split(path, split_path, '/'); 67 | 68 | auto* current_root = &self.root_tree; 69 | 70 | for (auto it = split_path.begin(); it != split_path.end() - 1; it++) 71 | current_root = current_root->add_folder(*it); 72 | 73 | if (self.archive_manager.hash_to_archive_index.find(hash) != self.archive_manager.hash_to_archive_index.end()) { 74 | current_root->add_file(path, split_path.back(), hash, { 0 }); 75 | } 76 | } 77 | 78 | self.root_tree_constructing = false; 79 | }, std::ref(self)).detach(); 80 | } 81 | } 82 | 83 | static void show_export_selection_dialog(ProjectDS& self) { 84 | const auto write_to_file = [](Decima::CoreFile& file, const std::filesystem::path& path) { 85 | std::filesystem::create_directories(path.parent_path()); 86 | 87 | std::ofstream output_file{ path, std::ios::binary }; 88 | output_file.write(reinterpret_cast(file.contents.data()), file.contents.size()); 89 | 90 | std::cout << "File was exported to: " << path << "\n"; 91 | }; 92 | 93 | if (self.selection_info.selected_files.empty()) 94 | return; 95 | 96 | if (const auto root = pfd::select_folder("Choose destination folder").result(); !root.empty()) { 97 | for (const auto selected_file : self.selection_info.selected_files) { 98 | if (auto entry = self.archive_manager.hash_to_name.find(selected_file); entry != self.archive_manager.hash_to_name.end()) { 99 | write_to_file(self.archive_manager.query_file(entry->second).value(), std::filesystem::path(root) / entry->second); 100 | } else { 101 | write_to_file(self.archive_manager.query_file(selected_file).value(), std::filesystem::path(root) / uint64_to_hex(selected_file)); 102 | } 103 | } 104 | } 105 | } 106 | 107 | void ProjectDS::init_user() { 108 | App::init_user(); 109 | init_imgui(); 110 | init_filetype_handlers(); 111 | ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; 112 | 113 | file_viewer.WriteFn = [](auto, auto, auto) { 114 | /* Dummy write function because 115 | * ReadOnly forbids any selection. */ 116 | }; 117 | 118 | shortcuts.push_back(ShortcutInfo { 119 | "Escape", 120 | "Reset highlighted selection in raw view", 121 | GLFW_KEY_ESCAPE, 122 | ImGuiKeyModFlags_None, 123 | [&] { 124 | if (selection_info.preview_file != 0) { 125 | selection_info.preview_file_size = selection_info.file->contents.size(); 126 | selection_info.preview_file_offset = 0; 127 | } 128 | }, 129 | }); 130 | 131 | shortcuts.push_back(ShortcutInfo { 132 | "Ctrl+O", 133 | "Open file dialog to select game folder", 134 | GLFW_KEY_O, 135 | ImGuiKeyModFlags_Ctrl, 136 | [&] { show_data_selection_dialog(*this); }, 137 | }); 138 | 139 | shortcuts.push_back(ShortcutInfo { 140 | "Ctrl+E", 141 | "Export currently selected files", 142 | GLFW_KEY_E, 143 | ImGuiKeyModFlags_Ctrl, 144 | [&] { show_export_selection_dialog(*this); }, 145 | }); 146 | 147 | shortcuts.push_back(ShortcutInfo { 148 | "Ctrl+A", 149 | "Add file to selection by its name", 150 | GLFW_KEY_A, 151 | ImGuiKeyModFlags_Ctrl, 152 | [&] { current_popup = Popup::AppendExportByName; }, 153 | }); 154 | 155 | shortcuts.push_back(ShortcutInfo { 156 | "Ctrl+Shift+A", 157 | "Add file to selection by its hash", 158 | GLFW_KEY_A, 159 | ImGuiKeyModFlags_Ctrl | ImGuiKeyModFlags_Shift, 160 | [&] { current_popup = Popup::AppendExportByHash; }, 161 | }); 162 | } 163 | 164 | void ProjectDS::input_user() { 165 | ImGuiIO& io = ImGui::GetIO(); 166 | 167 | if (io.WantCaptureKeyboard) 168 | return; 169 | 170 | for (const auto& shortcut : shortcuts) { 171 | if ((io.KeyMods & shortcut.mods) == shortcut.mods && io.KeysDown[shortcut.key]) 172 | shortcut.callback(); 173 | } 174 | } 175 | 176 | void ProjectDS::update_user(double ts) { 177 | App::update_user(ts); 178 | draw_dockspace(); 179 | draw_filepreview(); 180 | draw_export(); 181 | draw_tree(); 182 | } 183 | 184 | void ProjectDS::init_filetype_handlers() { 185 | } 186 | 187 | void ProjectDS::draw_dockspace() { 188 | ImGuiViewport* viewport = ImGui::GetMainViewport(); 189 | ImGui::SetNextWindowPos(viewport->GetWorkPos()); 190 | ImGui::SetNextWindowSize(viewport->GetWorkSize()); 191 | ImGui::SetNextWindowViewport(viewport->ID); 192 | ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); 193 | ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); 194 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0.0f, 0.0f }); 195 | const auto dock_flags = ImGuiWindowFlags_MenuBar 196 | | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar 197 | | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove 198 | | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus 199 | | ImGuiWindowFlags_NoBackground; 200 | ImGui::Begin("DockSpace", nullptr, dock_flags); 201 | { 202 | ImGui::PopStyleVar(3); 203 | ImGui::DockSpace(ImGui::GetID("Dock")); 204 | 205 | if (ImGui::BeginMenuBar()) { 206 | if (ImGui::BeginMenu("File")) { 207 | if (ImGui::MenuItem("Open archive", "Ctrl+O")) { 208 | show_data_selection_dialog(*this); 209 | } 210 | 211 | ImGui::EndMenu(); 212 | } 213 | 214 | if (ImGui::BeginMenu("Help")) { 215 | if (ImGui::MenuItem("About")) 216 | current_popup = Popup::About; 217 | 218 | if (ImGui::MenuItem("Shortcuts")) 219 | current_popup = Popup::Shortcuts; 220 | 221 | ImGui::EndMenu(); 222 | } 223 | 224 | ImGui::EndMenuBar(); 225 | } 226 | } 227 | ImGui::End(); 228 | 229 | if (current_popup == Popup::Shortcuts) { 230 | ImGui::OpenPopup("Shortcuts"); 231 | current_popup = Popup::None; 232 | } 233 | 234 | if (current_popup == Popup::About) { 235 | ImGui::OpenPopup("About"); 236 | current_popup = Popup::None; 237 | } 238 | 239 | ImGui::SetNextWindowSize({ 500, 0 }, ImGuiCond_Always); 240 | 241 | if (ImGui::BeginPopupModal("Shortcuts", nullptr, ImGuiWindowFlags_NoMove)) { 242 | ImGui::Columns(2); 243 | { 244 | ImGui::Text("Name"); 245 | ImGui::SetColumnWidth(-1, 150); 246 | ImGui::NextColumn(); 247 | 248 | ImGui::Text("Description"); 249 | ImGui::NextColumn(); 250 | 251 | ImGui::Separator(); 252 | 253 | for (const auto& shortcut : shortcuts) { 254 | ImGui::TextUnformatted(shortcut.name.data()); 255 | ImGui::NextColumn(); 256 | 257 | ImGui::TextWrapped("%s", shortcut.description.data()); 258 | ImGui::NextColumn(); 259 | 260 | ImGui::Separator(); 261 | } 262 | } 263 | ImGui::Columns(1); 264 | 265 | if (ImGui::Button("Got it", { -1, 0 })) 266 | ImGui::CloseCurrentPopup(); 267 | 268 | ImGui::EndPopup(); 269 | } 270 | 271 | ImGui::SetNextWindowSize({ 600, 0 }, ImGuiCond_Always); 272 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, { 0.5, 0.5 }); 273 | 274 | if (ImGui::BeginPopupModal("About", nullptr, ImGuiWindowFlags_NoMove)) { 275 | ImGui::Text("ProjectDecima " DECIMA_VERSION " (" __DATE__ ")"); 276 | ImGui::Separator(); 277 | 278 | ImGui::TextWrapped("This project is aimed to provide GUI for export/preview files inside Decima engine manager."); 279 | 280 | if (ImGui::TreeNode("Show license")) { 281 | ImGui::Text(R"(MIT License 282 | 283 | Copyright (c) 2020 REDxEYE & ShadelessFox 284 | 285 | Permission is hereby granted, free of charge, to any person obtaining a copy 286 | of this software and associated documentation files (the "Software"), to deal 287 | in the Software without restriction, including without limitation the rights 288 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 289 | copies of the Software, and to permit persons to whom the Software is 290 | furnished to do so, subject to the following conditions: 291 | 292 | The above copyright notice and this permission notice shall be included in all 293 | copies or substantial portions of the Software. 294 | 295 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 296 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 297 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 298 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 299 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 300 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 301 | SOFTWARE.)"); 302 | ImGui::TreePop(); 303 | } 304 | 305 | ImGui::Separator(); 306 | 307 | if (ImGui::Button("Source code", { -1, 0 })) 308 | std::system("start https://github.com/REDxEYE/ProjectDecima"); 309 | 310 | if (ImGui::Button("Report problem", { -1, 0 })) 311 | std::system("start https://github.com/REDxEYE/ProjectDecima/issues"); 312 | 313 | ImGui::Separator(); 314 | 315 | if (ImGui::Button("Close", { -1, 0 })) 316 | ImGui::CloseCurrentPopup(); 317 | 318 | ImGui::EndPopup(); 319 | } 320 | } 321 | 322 | void ProjectDS::draw_filepreview() { 323 | ImGui::Begin("File preview"); 324 | { 325 | if (selection_info.selected_file > 0) { 326 | const auto file_entry_opt = archive_manager.get_file_entry(selection_info.selected_file); 327 | 328 | if (file_entry_opt.has_value()) { 329 | const auto& file_entry = file_entry_opt.value().get(); 330 | 331 | std::string filename; 332 | if (archive_manager.hash_to_name.find(selection_info.selected_file) != archive_manager.hash_to_name.end()) { 333 | filename = sanitize_name(archive_manager.hash_to_name.at(selection_info.selected_file)); 334 | } else { 335 | filename = uint64_to_hex(selection_info.selected_file); 336 | } 337 | ImGui::TextWrapped("%s", filename.c_str()); 338 | 339 | if (ImGui::BeginPopupContextItem("File preview name")) { 340 | if (ImGui::Selectable("Copy path")) 341 | ImGui::SetClipboardText(filename.c_str()); 342 | ImGui::EndPopup(); 343 | } 344 | 345 | ImGui::Separator(); 346 | ImGui::Columns(2); 347 | { 348 | ImGui::Text("Archive ID"); 349 | ImGui::NextColumn(); 350 | 351 | ImGui::Text("%s", archive_manager.archives.at(archive_manager.hash_to_archive_index.at(selection_info.selected_file)).path.c_str()); 352 | ImGui::NextColumn(); 353 | 354 | ImGui::Separator(); 355 | 356 | ImGui::Text("Size"); 357 | ImGui::NextColumn(); 358 | 359 | ImGui::Text("%u bytes", file_entry.span.size); 360 | ImGui::NextColumn(); 361 | 362 | ImGui::Separator(); 363 | 364 | ImGui::Text("Hash"); 365 | ImGui::NextColumn(); 366 | 367 | ImGui::Text("%llX", file_entry.hash); 368 | ImGui::NextColumn(); 369 | 370 | ImGui::Separator(); 371 | 372 | ImGui::Text("Entry ID"); 373 | ImGui::NextColumn(); 374 | 375 | ImGui::Text("%u", file_entry.index); 376 | ImGui::NextColumn(); 377 | 378 | ImGui::Separator(); 379 | 380 | ImGui::Text("Offset"); 381 | ImGui::NextColumn(); 382 | 383 | ImGui::Text("%llu", file_entry.span.offset); 384 | ImGui::Separator(); 385 | } 386 | ImGui::Columns(1); 387 | 388 | const bool selected_file_changed = selection_info.preview_file != selection_info.selected_file; 389 | 390 | if (selected_file_changed) { 391 | selection_info.file = &archive_manager.query_file(selection_info.selected_file).value().get(); 392 | selection_info.file->parse(); 393 | selection_info.preview_file = selection_info.selected_file; 394 | selection_info.preview_file_size = selection_info.file->contents.size(); 395 | selection_info.preview_file_offset = 0; 396 | } 397 | } else { 398 | ImGui::Text("Error getting file info!"); 399 | } 400 | } else { 401 | ImGui::Text("No file selected"); 402 | } 403 | 404 | ImGui::DockSpace(ImGui::GetID("Dock2")); 405 | } 406 | ImGui::End(); 407 | 408 | ImGui::Begin("Normal View", nullptr, ImGuiWindowFlags_AlwaysAutoResize); 409 | { 410 | if (selection_info.selected_file > 0) { 411 | for (const auto& [file, file_offset] : selection_info.file->objects) { 412 | std::stringstream buffer; 413 | buffer << '[' << Decima::to_string(file->guid) << "] " << Decima::get_type_name(file->header.file_type); 414 | 415 | const bool opened = ImGui::TreeNode(buffer.str().c_str()); 416 | 417 | if (ImGui::BeginPopupContextItem(buffer.str().c_str())) { 418 | if (ImGui::Selectable("Highlight")) { 419 | selection_info.preview_file_offset = file_offset; 420 | selection_info.preview_file_size = file->header.file_size + sizeof(Decima::CoreHeader); 421 | } 422 | 423 | ImGui::EndPopup(); 424 | } 425 | 426 | if (opened) { 427 | file->draw(); 428 | ImGui::TreePop(); 429 | } 430 | 431 | ImGui::Separator(); 432 | } 433 | } 434 | } 435 | ImGui::End(); 436 | 437 | ImGui::Begin("Raw View", nullptr, ImGuiWindowFlags_AlwaysAutoResize); 438 | { 439 | if (selection_info.selected_file > 0) { 440 | file_viewer.DrawContents( 441 | selection_info.file->contents.data() + selection_info.preview_file_offset, 442 | selection_info.preview_file_size, 443 | 0); 444 | } 445 | } 446 | ImGui::End(); 447 | } 448 | 449 | void ProjectDS::draw_tree() { 450 | ImGui::Begin("Trees"); 451 | { 452 | FileTree::ExpandMode expand_mode = FileTree::ExpandMode::None; 453 | 454 | if (filter.Draw()) { 455 | expand_mode = root_tree.apply_filter(filter); 456 | 457 | file_names.clear(); 458 | 459 | for (auto& [_, path] : archive_manager.hash_to_name) { 460 | if (filter.PassFilter(path.c_str())) { 461 | file_names.push_back(path.c_str()); 462 | } 463 | } 464 | } 465 | 466 | if (root_tree_constructing) { 467 | ImGui::PushStyleColor(ImGuiCol_Text, 0xff99ffff); 468 | ImGui::TextWrapped("File tree is still constructing, some files may be missing"); 469 | ImGui::PopStyleColor(); 470 | } 471 | 472 | ImGui::BeginChild("FileTree"); 473 | root_tree.draw(selection_info, archive_manager, true, expand_mode); 474 | ImGui::EndChild(); 475 | } 476 | ImGui::End(); 477 | } 478 | 479 | void ProjectDS::draw_export() { 480 | ImGui::Begin("Export"); 481 | { 482 | if (ImGui::Button("Add file by name", { -1, 0 })) 483 | current_popup = Popup::AppendExportByName; 484 | 485 | if (ImGui::Button("Add file by hash", { -1, 0 })) 486 | current_popup = Popup::AppendExportByHash; 487 | 488 | if (ImGui::Button("Export selected items", { -1, 0 })) 489 | show_export_selection_dialog(*this); 490 | 491 | if (ImGui::PushItemWidth(-1), ImGui::ListBoxHeader("##", { 0, -1 })) { 492 | for (const auto selected_file : selection_info.selected_files) { 493 | if (archive_manager.hash_to_name.find(selected_file) != archive_manager.hash_to_name.end()) { 494 | if (ImGui::Selectable(archive_manager.hash_to_name[selected_file].c_str())) 495 | selection_info.selected_file = selected_file; 496 | } else { 497 | std::string new_name = "Hash: " + uint64_to_hex(selected_file); 498 | if (ImGui::Selectable(new_name.c_str())) 499 | selection_info.selected_file = selected_file; 500 | } 501 | } 502 | 503 | ImGui::ListBoxFooter(); 504 | } 505 | 506 | if (current_popup == Popup::AppendExportByName) { 507 | ImGui::OpenPopup("AppendExportByName"); 508 | current_popup = Popup::None; 509 | } 510 | 511 | if (current_popup == Popup::AppendExportByName) { 512 | ImGui::OpenPopup("AppendExportByName"); 513 | current_popup = Popup::None; 514 | } 515 | 516 | if (ImGui::BeginPopup("AppendExportByName")) { 517 | static char path[512]; 518 | 519 | ImGui::InputText("File name", path, IM_ARRAYSIZE(path) - 1); 520 | 521 | if (ImGui::Button("Add to selection!")) { 522 | uint64_t file_hash = hash_string(sanitize_name(path), Decima::cipher_seed); 523 | if (archive_manager.get_file_entry(file_hash).has_value()) { 524 | selection_info.selected_files.insert(file_hash); 525 | } 526 | } 527 | 528 | ImGui::EndPopup(); 529 | } 530 | 531 | if (current_popup == Popup::AppendExportByHash) { 532 | ImGui::OpenPopup("AppendExportByHash"); 533 | current_popup = Popup::None; 534 | } 535 | 536 | if (ImGui::BeginPopup("AppendExportByHash")) { 537 | static std::uint64_t file_hash = 0; 538 | 539 | ImGui::InputScalar("File hash", ImGuiDataType_U64, &file_hash); 540 | 541 | if (ImGui::Button("Add to selection!")) { 542 | if (archive_manager.get_file_entry(file_hash).has_value()) { 543 | selection_info.selected_files.insert(file_hash); 544 | } 545 | } 546 | 547 | ImGui::EndPopup(); 548 | } 549 | } 550 | ImGui::End(); 551 | } 552 | --------------------------------------------------------------------------------