Half done with Scene Component Parsing
This commit is contained in:
		| @@ -42,4 +42,7 @@ target_compile_definitions(${DAWN_TARGET_NAME} | |||||||
|   PUBLIC |   PUBLIC | ||||||
|     ${DAWN_SHARED_DEFINITIONS} |     ${DAWN_SHARED_DEFINITIONS} | ||||||
|     DAWN_DEBUG_BUILD=${DAWN_DEBUG_BUILD} |     DAWN_DEBUG_BUILD=${DAWN_DEBUG_BUILD} | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | # Common Prefabs | ||||||
|  | tool_prefab("prefabs/ui/debug/FPSLabel.xml") | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| // Copyright (c) 2023 Dominic Masters |  | ||||||
| //  |  | ||||||
| // This software is released under the MIT License. |  | ||||||
| // https://opensource.org/licenses/MIT |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
| #include "prefab/SceneItemPrefab.hpp" |  | ||||||
| #include "scene/components/ui/UILabel.hpp" |  | ||||||
|  |  | ||||||
| namespace Dawn { |  | ||||||
|   class FPSLabel : public SceneItemPrefab<FPSLabel>, public StateOwner { |  | ||||||
|     public: |  | ||||||
|       static std::vector<Asset*> prefabAssets(AssetManager *man) { |  | ||||||
|         return {}; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       UILabel *label; |  | ||||||
|  |  | ||||||
|       FPSLabel(Scene *s, sceneitemid_t i) : SceneItemPrefab(s, i) {} |  | ||||||
|  |  | ||||||
|       void prefabInit(AssetManager *man) override { |  | ||||||
|         label = this->addComponent<UILabel>(); |  | ||||||
|         label->text = "No Frame"; |  | ||||||
|  |  | ||||||
|         useEvent([&](float_t delta){ |  | ||||||
|           std::string strFps = std::to_string((int32_t)(1.0f / delta)); |  | ||||||
|           std::string strTick = std::to_string((int32_t)(delta * 1000.0f)); |  | ||||||
|           label->text = strFps + "FPS (" + strTick + "ms)"; |  | ||||||
|         }, scene->eventSceneUnpausedUpdate); |  | ||||||
|       } |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| @@ -4,6 +4,7 @@ | |||||||
| # https://opensource.org/licenses/MIT | # https://opensource.org/licenses/MIT | ||||||
|  |  | ||||||
| # Subdirs | # Subdirs | ||||||
|  | add_subdirectory(debug) | ||||||
| add_subdirectory(display) | add_subdirectory(display) | ||||||
| add_subdirectory(example) | add_subdirectory(example) | ||||||
| add_subdirectory(physics) | add_subdirectory(physics) | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								src/dawn/scene/components/debug/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/dawn/scene/components/debug/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | # Copyright (c) 2023 Dominic Masters | ||||||
|  | #  | ||||||
|  | # This software is released under the MIT License. | ||||||
|  | # https://opensource.org/licenses/MIT | ||||||
|  |  | ||||||
|  | # Sources | ||||||
|  | target_sources(${DAWN_TARGET_NAME} | ||||||
|  |   PRIVATE | ||||||
|  |     FPSLabelComponent.cpp | ||||||
|  | ) | ||||||
							
								
								
									
										23
									
								
								src/dawn/scene/components/debug/FPSLabelComponent.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/dawn/scene/components/debug/FPSLabelComponent.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | // Copyright (c) 2023 Dominic Masters | ||||||
|  | //  | ||||||
|  | // This software is released under the MIT License. | ||||||
|  | // https://opensource.org/licenses/MIT | ||||||
|  |  | ||||||
|  | #include "FPSLabelComponent.hpp" | ||||||
|  |  | ||||||
|  | using namespace Dawn; | ||||||
|  |  | ||||||
|  | FPSLabelComponent::FPSLabelComponent(SceneItem *item) : | ||||||
|  |   SceneItemComponent(item) | ||||||
|  | { | ||||||
|  |    | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void FPSLabelComponent::onStart() { | ||||||
|  |   useEvent([&](float_t delta){ | ||||||
|  |     if(this->label == nullptr) return; | ||||||
|  |     std::string strFps = std::to_string((int32_t)(1.0f / delta)); | ||||||
|  |     std::string strTick = std::to_string((int32_t)(delta * 1000.0f)); | ||||||
|  |     label->text = strFps + "FPS (" + strTick + "ms)"; | ||||||
|  |   }, this->item->scene->eventSceneUnpausedUpdate); | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								src/dawn/scene/components/debug/FPSLabelComponent.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/dawn/scene/components/debug/FPSLabelComponent.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // Copyright (c) 2023 Dominic Masters | ||||||
|  | //  | ||||||
|  | // This software is released under the MIT License. | ||||||
|  | // https://opensource.org/licenses/MIT | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  | #include "scene/components/ui/UILabel.hpp" | ||||||
|  |  | ||||||
|  | namespace Dawn { | ||||||
|  |   class FPSLabelComponent : public SceneItemComponent { | ||||||
|  |     public: | ||||||
|  |       UILabel *label; | ||||||
|  |       FPSLabelComponent(SceneItem *item); | ||||||
|  |       void onStart() override; | ||||||
|  |   }; | ||||||
|  | } | ||||||
| @@ -29,11 +29,17 @@ namespace Dawn { | |||||||
|       void updateMesh(); |       void updateMesh(); | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|  |       //@optional | ||||||
|       StateProperty<std::string> text; |       StateProperty<std::string> text; | ||||||
|  |       // @optional | ||||||
|       StateProperty<float_t> fontSize; |       StateProperty<float_t> fontSize; | ||||||
|  |       /* @optional */ | ||||||
|       StateProperty<Font*> font; |       StateProperty<Font*> font; | ||||||
|  |       /* @optional */ | ||||||
|       StateProperty<float_t> maxWidth; |       StateProperty<float_t> maxWidth; | ||||||
|  |       /* @optional */ | ||||||
|       struct Color textColor = COLOR_WHITE; |       struct Color textColor = COLOR_WHITE; | ||||||
|  |        | ||||||
|       struct FontMeasure measure; |       struct FontMeasure measure; | ||||||
|       int32_t startQuad = 0; |       int32_t startQuad = 0; | ||||||
|       int32_t quadCount = -1; |       int32_t quadCount = -1; | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "scene/Scene.hpp" | #include "scene/Scene.hpp" | ||||||
| #include "prefabs/SimpleSpinningCubePrefab.hpp" | #include "prefabs/SimpleSpinningCubePrefab.hpp" | ||||||
| #include "prefabs/FPSLabel.hpp" | #include "prefabs/ui/debug/FPSLabel.hpp" | ||||||
| #include "scene/components/ui/UIImage.hpp" | #include "scene/components/ui/UIImage.hpp" | ||||||
| #include "display/font/BitmapFont.hpp" | #include "display/font/BitmapFont.hpp" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,4 +17,6 @@ add_subdirectory(game) | |||||||
| add_subdirectory(save) | add_subdirectory(save) | ||||||
| add_subdirectory(scene) | add_subdirectory(scene) | ||||||
|  |  | ||||||
| tool_prefab("example-prefab.xml") | # Assets | ||||||
|  | set(ROSE_ASSETS_DIR ${DAWN_ASSETS_DIR}/games/rose) | ||||||
|  | tool_prefab(${ROSE_ASSETS_DIR}/prefabs/PlayerPrefab.xml) | ||||||
| @@ -7,15 +7,25 @@ | |||||||
| #include "scene/Scene.hpp" | #include "scene/Scene.hpp" | ||||||
| #include "scene/components/GameCamera.hpp" | #include "scene/components/GameCamera.hpp" | ||||||
| #include "prefabs/PlayerPrefab.hpp" | #include "prefabs/PlayerPrefab.hpp" | ||||||
|  | #include "prefabs/ui/debug/FPSLabel.hpp" | ||||||
|  |  | ||||||
| namespace Dawn { | namespace Dawn { | ||||||
|   class HelloWorldScene : public Scene { |   class HelloWorldScene : public Scene { | ||||||
|     protected: |     protected: | ||||||
|       Camera *camera; |       Camera *camera; | ||||||
|  |       UICanvas *canvas; | ||||||
|  |  | ||||||
|       void stage() override { |       void stage() override { | ||||||
|         auto player = PlayerPrefab::create(this); |         auto player = PlayerPrefab::create(this); | ||||||
|  |  | ||||||
|  |         canvas = UICanvas::create(this); | ||||||
|  |  | ||||||
|  |         auto labelItem = FPSLabel::create(this); | ||||||
|  |         labelItem->transform.setParent(canvas->transform); | ||||||
|  |         labelItem->label->alignX = UI_COMPONENT_ALIGN_END; | ||||||
|  |         labelItem->label->alignment = glm::vec4(0, 0, 0, 0); | ||||||
|  |         labelItem->label->fontSize = 16; | ||||||
|  |  | ||||||
|         auto wallBox = this->createSceneItem()->addComponent<BoxCollider>(); |         auto wallBox = this->createSceneItem()->addComponent<BoxCollider>(); | ||||||
|         wallBox->min = glm::vec2(-4, -3); |         wallBox->min = glm::vec2(-4, -3); | ||||||
|         wallBox->max = glm::vec2(-3, 3); |         wallBox->max = glm::vec2(-3, 3); | ||||||
|   | |||||||
| @@ -43,10 +43,15 @@ function(tool_prefab in) | |||||||
|     set(DEPS prefabtool) |     set(DEPS prefabtool) | ||||||
|   endif() |   endif() | ||||||
|  |  | ||||||
|   add_custom_target(prefab_${in} |   STRING(REGEX REPLACE "[\.|\\|\/]" "-" prefab_name ${in}) | ||||||
|     COMMAND prefabtool --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_TEMP_DIR}" |   add_custom_target(prefab_${prefab_name} | ||||||
|  |     COMMAND prefabtool --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_GENERATED_DIR}/generatedprefabs" --sources="${DAWN_SOURCES_DIR}" | ||||||
|     COMMENT "Generating prefab from ${in}" |     COMMENT "Generating prefab from ${in}" | ||||||
|     DEPENDS ${DEPS} |     DEPENDS ${DEPS} | ||||||
|   ) |   ) | ||||||
|   add_dependencies(${DAWN_TARGET_NAME} prefab_${in}) |   target_include_directories(${DAWN_TARGET_NAME} | ||||||
|  |     PUBLIC | ||||||
|  |       ${DAWN_GENERATED_DIR}/generatedprefabs | ||||||
|  |   ) | ||||||
|  |   add_dependencies(${DAWN_TARGET_NAME} prefab_${prefab_name}) | ||||||
| endfunction() | endfunction() | ||||||
| @@ -7,8 +7,67 @@ | |||||||
|  |  | ||||||
| using namespace Dawn; | using namespace Dawn; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct PrefabComponentParserRuleset PrefabRegistry::getRuleset(std::string type) { | ||||||
|  |   std::vector<std::string> pathsToScan; | ||||||
|  |   pathsToScan.push_back(this->sources); | ||||||
|  |  | ||||||
|  |   auto it = pathsToScan.begin(); | ||||||
|  |   while(it != pathsToScan.end()) { | ||||||
|  |     auto str = *it; | ||||||
|  |     pathsToScan.erase(it); | ||||||
|  |  | ||||||
|  |     Directory dir = Directory(str); | ||||||
|  |     auto children = dir.readDirectory(); | ||||||
|  |      | ||||||
|  |     auto itChildren = children.begin(); | ||||||
|  |     while(itChildren != children.end()) { | ||||||
|  |       if(itChildren->second == DIRECTORY_CHILD_TYPE_DIRECTORY) { | ||||||
|  |         pathsToScan.push_back(str + "/" + itChildren->first); | ||||||
|  |       } else { | ||||||
|  |         if(itChildren->first == type+".hpp") { | ||||||
|  |           // Load ruleset | ||||||
|  |           std::string data; | ||||||
|  |           File file(str + "/" + itChildren->first); | ||||||
|  |           if(!file.readString(&data)) { | ||||||
|  |             std::cout << "Failed to read ruleset file!" << file.filename << std::endl; | ||||||
|  |           } else { | ||||||
|  |             std::cout << "EPIC!" << file.filename << std::endl; | ||||||
|  |  | ||||||
|  |             // Begin scanning contentsr | ||||||
|  |             // std::string include = this->sources; | ||||||
|  |             auto toRemove = File::normalizeSlashes(this->sources + "/"); | ||||||
|  |             auto includePath = file.filename.substr(toRemove.size(), file.filename.size() - toRemove.size()); | ||||||
|  |  | ||||||
|  |             // Now locate the first subdir since we don't want to include the root path (e.g. dawn, dawnrose, etc) | ||||||
|  |             auto firstSlash = includePath.find("/"); | ||||||
|  |             if(firstSlash != std::string::npos) { | ||||||
|  |               includePath = includePath.substr(firstSlash + 1, includePath.size() - firstSlash - 1); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             struct PrefabComponentParserRuleset ruleset; | ||||||
|  |             ruleset.include = includePath; | ||||||
|  |             ruleset.name = type; | ||||||
|  |              | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             // return; | ||||||
|  |             assertUnreachable(); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       ++itChildren; | ||||||
|  |     } | ||||||
|  |     it = pathsToScan.begin(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return { .name = "" }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  |  | ||||||
| std::vector<std::string> PrefabTool::getRequiredFlags() { | std::vector<std::string> PrefabTool::getRequiredFlags() { | ||||||
|   return { "input", "output" }; |   return { "input", "output", "sources" }; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::map<std::string, std::string> PrefabTool::getOptionalFlags() { | std::map<std::string, std::string> PrefabTool::getOptionalFlags() { | ||||||
| @@ -23,13 +82,23 @@ int32_t PrefabTool::start() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   std::string data; |   std::string data; | ||||||
|   if(!input.readString(&data)) return 1; |   if(!input.readString(&data)) { | ||||||
|  |     std::cout << "Failed to read input file!" << std::endl; | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   struct PrefabRegistry registry; | ||||||
|  |   registry.sources = flags["sources"]; | ||||||
|  |  | ||||||
|   auto xml = Xml::load(data); |   auto xml = Xml::load(data); | ||||||
|   std::string error; |   std::string error; | ||||||
|   struct Prefab prefab; |   struct Prefab prefab; | ||||||
|  |   prefab.registry = ®istry; | ||||||
|   auto result = ((PrefabParser()).parse(&xml, &prefab, &error)); |   auto result = ((PrefabParser()).parse(&xml, &prefab, &error)); | ||||||
|   if(result != 0) return result; |   if(result != 0) { | ||||||
|  |     std::cout << "Failed to parse prefab: " << error << std::endl; | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Generate output |   // Generate output | ||||||
|   std::vector<std::string> outputData; |   std::vector<std::string> outputData; | ||||||
| @@ -81,6 +150,8 @@ int32_t PrefabParser::onParse( | |||||||
|   while(itChildren != node->children.end()) { |   while(itChildren != node->children.end()) { | ||||||
|     // Parse children as components |     // Parse children as components | ||||||
|     struct PrefabComponent component; |     struct PrefabComponent component; | ||||||
|  |     component.registry = out->registry; | ||||||
|  |  | ||||||
|     auto ret = (PrefabComponentParser()).parse(*itChildren, &component, error); |     auto ret = (PrefabComponentParser()).parse(*itChildren, &component, error); | ||||||
|     if(ret != 0) return ret; |     if(ret != 0) return ret; | ||||||
|     out->components.push_back(component); |     out->components.push_back(component); | ||||||
| @@ -111,44 +182,27 @@ int32_t PrefabComponentParser::onParse( | |||||||
|     out->ref = values["ref"]; |     out->ref = values["ref"]; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   std::function<std::string(std::string)> stringParser = [&](std::string str) { |   // Get the ruleset. | ||||||
|     return "\"" + str + "\""; |   auto ruleset = out->registry->getRuleset(node->node); | ||||||
|   }; |   if(ruleset.name.size() == 0) { | ||||||
|  |     *error =  "Unknown prefab node type: " + node->node; | ||||||
|   // Handle different node types! |  | ||||||
|   std::map<std::string, struct PrefabComponentParserRuleset> attributesForTypes = { |  | ||||||
|     { "UILabel", { |  | ||||||
|       .include = "scene/components/ui/UILabel.hpp", |  | ||||||
|       .optional = { |  | ||||||
|         { "text", stringParser } |  | ||||||
|       } |  | ||||||
|     } } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   // Check if the node type is in the map |  | ||||||
|   if(attributesForTypes.find(node->node) != attributesForTypes.end()) { |  | ||||||
|     // Get the ruleset for this node type |  | ||||||
|     auto ruleset = attributesForTypes[node->node]; |  | ||||||
|     out->include = ruleset.include; |  | ||||||
|     out->type = node->node; |  | ||||||
|  |  | ||||||
|     // Iterate all the optional attributes and store within the out if present |  | ||||||
|     auto itOptional = ruleset.optional.begin(); |  | ||||||
|     while(itOptional != ruleset.optional.end()) { |  | ||||||
|       if(node->attributes.find(itOptional->first) != node->attributes.end()) { |  | ||||||
|         auto raw = node->attributes[itOptional->first]; |  | ||||||
|         auto parsed = itOptional->second(raw); |  | ||||||
|         out->values[itOptional->first] = parsed; |  | ||||||
|       } |  | ||||||
|       ++itOptional; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // TODO: Handle required attributes  |  | ||||||
|   } else { |  | ||||||
|     *error = "Unknown node type: " + node->node; |  | ||||||
|     return 1; |     return 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   out->include = ruleset.include; | ||||||
|  |   out->type = node->node; | ||||||
|  |  | ||||||
|  |   // Iterate all the optional attributes and store within the out if present | ||||||
|  |   auto itOptional = ruleset.optional.begin(); | ||||||
|  |   while(itOptional != ruleset.optional.end()) { | ||||||
|  |     if(node->attributes.find(itOptional->first) != node->attributes.end()) { | ||||||
|  |       auto raw = node->attributes[itOptional->first]; | ||||||
|  |       auto parsed = itOptional->second(raw); | ||||||
|  |       out->values[itOptional->first] = parsed; | ||||||
|  |     } | ||||||
|  |     ++itOptional; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -162,7 +216,7 @@ void PrefabGen::generate( | |||||||
|   struct ClassGenInfo classInfo; |   struct ClassGenInfo classInfo; | ||||||
|   classInfo.clazz = info->name; |   classInfo.clazz = info->name; | ||||||
|   classInfo.extend = "SceneItemPrefab<" + info->name + ">"; |   classInfo.extend = "SceneItemPrefab<" + info->name + ">"; | ||||||
|   classInfo.constructorArgs = "Scene *scene, sceneid_t id"; |   classInfo.constructorArgs = "Scene *scene, sceneitemid_t id"; | ||||||
|   classInfo.extendArgs = "scene, id"; |   classInfo.extendArgs = "scene, id"; | ||||||
|  |  | ||||||
|   struct MethodGenInfo methodAssets; |   struct MethodGenInfo methodAssets; | ||||||
|   | |||||||
| @@ -5,17 +5,27 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
| #include "util/DawnTool.hpp" | #include "util/DawnTool.hpp" | ||||||
| #include "util/File.hpp" | #include "util/Directory.hpp" | ||||||
| #include "util/XmlParser.hpp" | #include "util/XmlParser.hpp" | ||||||
| #include "util/CodeGen.hpp" | #include "util/CodeGen.hpp" | ||||||
|  | #include <regex> | ||||||
|  |  | ||||||
| namespace Dawn { | namespace Dawn { | ||||||
|   struct PrefabComponentParserRuleset { |   struct PrefabComponentParserRuleset { | ||||||
|  |     std::string name; | ||||||
|     std::string include; |     std::string include; | ||||||
|     std::map<std::string, std::function<std::string(std::string)>> optional; |     std::map<std::string, std::function<std::string(std::string)>> optional; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   struct PrefabRegistry { | ||||||
|  |     std::string sources; | ||||||
|  |      | ||||||
|  |     struct PrefabComponentParserRuleset getRuleset(std::string type); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   struct PrefabComponent { |   struct PrefabComponent { | ||||||
|  |     struct PrefabRegistry *registry; | ||||||
|  |  | ||||||
|     std::string include; |     std::string include; | ||||||
|     std::string type; |     std::string type; | ||||||
|     std::map<std::string, std::string> values; |     std::map<std::string, std::string> values; | ||||||
| @@ -23,6 +33,8 @@ namespace Dawn { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   struct Prefab { |   struct Prefab { | ||||||
|  |     struct PrefabRegistry *registry; | ||||||
|  |  | ||||||
|     std::string name; |     std::string name; | ||||||
|     std::string type; |     std::string type; | ||||||
|     std::vector<struct PrefabComponent> components; |     std::vector<struct PrefabComponent> components; | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ set( | |||||||
|     ${D}/File.cpp |     ${D}/File.cpp | ||||||
|     ${D}/Language.cpp |     ${D}/Language.cpp | ||||||
|     ${D}/CodeGen.cpp |     ${D}/CodeGen.cpp | ||||||
|  |     ${D}/Directory.cpp | ||||||
|  |  | ||||||
|   CACHE INTERNAL |   CACHE INTERNAL | ||||||
|     ${DAWN_CACHE_TARGET} |     ${DAWN_CACHE_TARGET} | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								src/dawntools/util/Directory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/dawntools/util/Directory.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | // Copyright (c) 2023 Dominic Masters | ||||||
|  | //  | ||||||
|  | // This software is released under the MIT License. | ||||||
|  | // https://opensource.org/licenses/MIT | ||||||
|  |  | ||||||
|  | #include "Directory.hpp" | ||||||
|  |  | ||||||
|  | using namespace Dawn; | ||||||
|  |  | ||||||
|  | Directory::Directory(std::string dirname) { | ||||||
|  |   this->filename = File::normalizeSlashes(dirname); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool_t Directory::open() { | ||||||
|  |   if(this->handle) return true; | ||||||
|  |   this->handle = opendir(this->filename.c_str()); | ||||||
|  |   return this->handle != nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool_t Directory::isOpen() { | ||||||
|  |   return this->handle != nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Directory::close() { | ||||||
|  |   if(this->handle) { | ||||||
|  |     closedir(this->handle); | ||||||
|  |     this->handle = nullptr; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool_t Directory::exists() { | ||||||
|  |   return this->open(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Directory::mkdirp() { | ||||||
|  |   File::mkdirp(this->filename); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::map<std::string, enum DirectoryChildType> Directory::readDirectory() { | ||||||
|  |   if(!this->open()) return {}; | ||||||
|  |   std::map<std::string, enum DirectoryChildType> children; | ||||||
|  |  | ||||||
|  |   struct dirent *dp; | ||||||
|  |   while((dp = readdir(this->handle)) != NULL) { | ||||||
|  |     if(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     //Skip anything that isn't a file or directory (symlinks, etc.) | ||||||
|  |     if(dp->d_type != DT_DIR && dp->d_type != DT_REG) continue; | ||||||
|  |     std::string fn = std::string(dp->d_name); | ||||||
|  |     children[fn] = dp->d_type == DT_DIR ? DIRECTORY_CHILD_TYPE_DIRECTORY : DIRECTORY_CHILD_TYPE_FILE; | ||||||
|  |   } | ||||||
|  |   this->close(); | ||||||
|  |   return children; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Directory::~Directory() { | ||||||
|  |   this->close(); | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								src/dawntools/util/Directory.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/dawntools/util/Directory.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | // Copyright (c) 2023 Dominic Masters | ||||||
|  | //  | ||||||
|  | // This software is released under the MIT License. | ||||||
|  | // https://opensource.org/licenses/MIT | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  | #include "util/File.hpp" | ||||||
|  |  | ||||||
|  | namespace Dawn { | ||||||
|  |   enum DirectoryChildType { | ||||||
|  |     DIRECTORY_CHILD_TYPE_FILE, | ||||||
|  |     DIRECTORY_CHILD_TYPE_DIRECTORY | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   class Directory { | ||||||
|  |     public: | ||||||
|  |       std::string filename; | ||||||
|  |       DIR *handle = nullptr; | ||||||
|  |  | ||||||
|  |       Directory(std::string dir); | ||||||
|  |  | ||||||
|  |       /** | ||||||
|  |        * Opens a connection to the directory. | ||||||
|  |        * @return True if success, otherwise for failure. | ||||||
|  |        */ | ||||||
|  |       bool_t open(); | ||||||
|  |  | ||||||
|  |       /** | ||||||
|  |        * Returns if the directory is open. | ||||||
|  |        * @return True if open, otherwise false. | ||||||
|  |        */ | ||||||
|  |       bool_t isOpen(); | ||||||
|  |  | ||||||
|  |       /** | ||||||
|  |        * Closes the directory. | ||||||
|  |        */ | ||||||
|  |       void close(); | ||||||
|  |  | ||||||
|  |       /** | ||||||
|  |        * Returns if the directory exists. | ||||||
|  |        * @return True if exists, otherwise false. | ||||||
|  |        */ | ||||||
|  |       bool_t exists(); | ||||||
|  |  | ||||||
|  |       /** | ||||||
|  |        * Creates the directory and all parent directories. | ||||||
|  |        */ | ||||||
|  |       void mkdirp(); | ||||||
|  |  | ||||||
|  |       /** | ||||||
|  |        * Reads the directory and returns a list of files. | ||||||
|  |        *  | ||||||
|  |        * @return List of filenames/directories within this directory. | ||||||
|  |        */ | ||||||
|  |       std::map<std::string, enum DirectoryChildType> readDirectory(); | ||||||
|  |  | ||||||
|  |       ~Directory(); | ||||||
|  |   }; | ||||||
|  | } | ||||||
| @@ -1,193 +0,0 @@ | |||||||
| // Copyright (c) 2023 Dominic Masters |  | ||||||
| //  |  | ||||||
| // This software is released under the MIT License. |  | ||||||
| // https://opensource.org/licenses/MIT |  | ||||||
|  |  | ||||||
| #include "File.hpp" |  | ||||||
|  |  | ||||||
| using namespace Dawn; |  | ||||||
|  |  | ||||||
| std::string File::normalizeSlashes(std::string str) { |  | ||||||
|   size_t i = 0; |  | ||||||
|   while(i < str.size()) { |  | ||||||
|     auto c = str[i]; |  | ||||||
|     if(c == '\\' || c == '/') str[i] = FILE_PATH_SEP; |  | ||||||
|     ++i; |  | ||||||
|   } |  | ||||||
|   return str; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void File::mkdirp(std::string path) { |  | ||||||
|   std::string buffer; |  | ||||||
|   char c; |  | ||||||
|   size_t i = 0; |  | ||||||
|   bool inFile; |  | ||||||
|   bool hasMore; |  | ||||||
|  |  | ||||||
|   inFile = false; |  | ||||||
|   hasMore = false; |  | ||||||
|   while(c = path[i]) { |  | ||||||
|     if((c == '\\' || c == '/') && i > 0) { |  | ||||||
|       fileMkdir(buffer.c_str(), 0755); |  | ||||||
|       inFile = false; |  | ||||||
|       hasMore = false; |  | ||||||
|       buffer += FILE_PATH_SEP; |  | ||||||
|       i++; |  | ||||||
|       continue; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if(c == '.') inFile = true; |  | ||||||
|     hasMore = true; |  | ||||||
|     buffer += c; |  | ||||||
|     i++; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if(!inFile && hasMore) { |  | ||||||
|     fileMkdir(buffer.c_str(), 0755); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // |  | ||||||
|  |  | ||||||
| File::File(std::string filename) { |  | ||||||
|   this->filename = File::normalizeSlashes(filename); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool_t File::open(enum FileMode mode) { |  | ||||||
|   assertNull(this->file); |  | ||||||
|  |  | ||||||
|   this->mode = mode; |  | ||||||
|   this->file = fopen( |  | ||||||
|     this->filename.c_str(), |  | ||||||
|     mode == FILE_MODE_READ ? "rb" : "wb" |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   if(this->file == NULL) return false; |  | ||||||
|  |  | ||||||
|   if(mode == FILE_MODE_READ) { |  | ||||||
|     fseek(this->file, 0, SEEK_END); |  | ||||||
|     this->length = ftell(this->file); |  | ||||||
|     fseek(this->file, 0, SEEK_SET); |  | ||||||
|  |  | ||||||
|     if(this->length <= 0) { |  | ||||||
|       this->close(); |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     this->length = 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool_t File::isOpen() { |  | ||||||
|   return this->file != nullptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool_t File::exists() { |  | ||||||
|   if(this->file != nullptr) return true; |  | ||||||
|   FILE *f = fopen(this->filename.c_str(), "rb"); |  | ||||||
|   if(f == NULL || f == nullptr) return false; |  | ||||||
|   fclose(f); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void File::close() { |  | ||||||
|   assertNotNull(this->file); |  | ||||||
|   fclose(this->file); |  | ||||||
|   this->file = nullptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool_t File::mkdirp() { |  | ||||||
|   File::mkdirp(this->filename); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool_t File::readString(std::string *out) { |  | ||||||
|   assertNotNull(out); |  | ||||||
|  |  | ||||||
|   if(!this->isOpen()) { |  | ||||||
|     if(!this->open(FILE_MODE_READ)) return false; |  | ||||||
|   } |  | ||||||
|   assertTrue(this->mode == FILE_MODE_READ); |  | ||||||
|   out->clear(); |  | ||||||
|  |  | ||||||
|   size_t i = 0; |  | ||||||
|   char buffer[FILE_BUFFER_SIZE + 1];// +1 for null term |  | ||||||
|   while(i != this->length) { |  | ||||||
|     size_t amt = mathMin<size_t>(FILE_BUFFER_SIZE, (this->length - i)); |  | ||||||
|     auto amtRead = fread(buffer, sizeof(char), amt, this->file); |  | ||||||
|     if(amtRead != amt) return false; |  | ||||||
|     i += amtRead; |  | ||||||
|     buffer[amtRead] = '\0'; |  | ||||||
|     out->append(buffer); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| size_t File::readAhead(char *buffer, size_t max, char needle) { |  | ||||||
|   assertNotNull(buffer); |  | ||||||
|   assertTrue(max > 0); |  | ||||||
|  |  | ||||||
|   if(!this->isOpen()) { |  | ||||||
|     if(!this->open(FILE_MODE_READ)) return 0; |  | ||||||
|   } |  | ||||||
|   assertTrue(this->mode == FILE_MODE_READ); |  | ||||||
|  |  | ||||||
|   // Buffer |  | ||||||
|   size_t pos = ftell(this->file); |  | ||||||
|   size_t amountLeftToRead = mathMin<size_t>(max, this->length - pos); |  | ||||||
|   char temporary[FILE_BUFFER_SIZE]; |  | ||||||
|   size_t n = 0; |  | ||||||
|    |  | ||||||
|   while(amountLeftToRead > 0) { |  | ||||||
|     size_t toRead = mathMin<size_t>(amountLeftToRead, FILE_BUFFER_SIZE); |  | ||||||
|     amountLeftToRead -= toRead; |  | ||||||
|     // Read bytes |  | ||||||
|     size_t read = fread(temporary, sizeof(char), toRead, this->file); |  | ||||||
|  |  | ||||||
|     // Read error? |  | ||||||
|     if(toRead != read) return 0; |  | ||||||
|  |  | ||||||
|     // Did we read the needle? |  | ||||||
|     size_t i = 0; |  | ||||||
|     while(i < read) { |  | ||||||
|       char c = temporary[i++]; |  | ||||||
|       if(c == needle) { |  | ||||||
|         return n; |  | ||||||
|       } else { |  | ||||||
|         buffer[n++] = c; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Needle was not found. |  | ||||||
|   return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool_t File::writeString(std::string in) { |  | ||||||
|   if(!this->isOpen()) { |  | ||||||
|     if(!this->open(FILE_MODE_WRITE)) return false; |  | ||||||
|   } |  | ||||||
|   assertTrue(this->mode == FILE_MODE_WRITE); |  | ||||||
|   return this->writeRaw((char *)in.c_str(), in.size()) && this->length == in.size(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool_t File::writeRaw(char *data, size_t len) { |  | ||||||
|   if(!this->isOpen()) { |  | ||||||
|     if(!this->open(FILE_MODE_WRITE)) return false; |  | ||||||
|   } |  | ||||||
|   assertTrue(this->mode == FILE_MODE_WRITE); |  | ||||||
|   this->length = fwrite(data, sizeof(char_t), len, this->file); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void File::setPosition(size_t n) { |  | ||||||
|   fseek(this->file, 0, SEEK_SET); |  | ||||||
|   fseek(this->file, n, SEEK_CUR); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| File::~File() { |  | ||||||
|   if(this->file != nullptr) this->close(); |  | ||||||
| } |  | ||||||
| @@ -1,140 +0,0 @@ | |||||||
| // Copyright (c) 2023 Dominic Masters |  | ||||||
| //  |  | ||||||
| // This software is released under the MIT License. |  | ||||||
| // https://opensource.org/licenses/MIT |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
| #include "assert/assert.hpp" |  | ||||||
| #include "util/mathutils.hpp" |  | ||||||
|  |  | ||||||
| #if defined(_MSC_VER) |  | ||||||
|   #include <direct.h> |  | ||||||
|   #include <windows.h> |  | ||||||
|   #define getcwd _getcwd |  | ||||||
|   #define FILE_PATH_SEP '\\' |  | ||||||
|   #define fileMkdir(path, perms) _mkdir(path) |  | ||||||
| #elif defined(__GNUC__) |  | ||||||
|   #include <unistd.h> |  | ||||||
|   #include <dirent.h> |  | ||||||
|   #include <sys/stat.h> |  | ||||||
|   #define FILE_PATH_SEP '/' |  | ||||||
|   #define fileMkdir(path, perms) mkdir(path, perms) |  | ||||||
| #endif |  | ||||||
| #include <errno.h> |  | ||||||
|  |  | ||||||
| #define FILE_BUFFER_SIZE 512 |  | ||||||
|  |  | ||||||
| namespace Dawn { |  | ||||||
|   enum FileMode { |  | ||||||
|     FILE_MODE_READ, |  | ||||||
|     FILE_MODE_WRITE |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   class File { |  | ||||||
|     private: |  | ||||||
|       enum FileMode mode; |  | ||||||
|  |  | ||||||
|     public: |  | ||||||
|       static std::string normalizeSlashes(std::string str); |  | ||||||
|       static void mkdirp(std::string path); |  | ||||||
|  |  | ||||||
|       std::string filename; |  | ||||||
|       size_t length; |  | ||||||
|       FILE *file = nullptr; |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Constructs a new File interface class. |  | ||||||
|        *  |  | ||||||
|        * @param filename Filename that you want to interface with. |  | ||||||
|        */ |  | ||||||
|       File(std::string filename); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Opens a connection to the file. |  | ||||||
|        *  |  | ||||||
|        * @param mode File mode to use for this interface. |  | ||||||
|        * @return True if success, otherwise for failure. |  | ||||||
|        */ |  | ||||||
|       bool_t open(enum FileMode mode); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Returns whether or not the file connection is currently opened. |  | ||||||
|        *  |  | ||||||
|        * @return True if the connection is open, otherwise if it's not. |  | ||||||
|        */ |  | ||||||
|       bool_t isOpen(); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Returns whether or not the file exists. Will open the connection if it |  | ||||||
|        * does exist. |  | ||||||
|        *  |  | ||||||
|        * @return True if exists, otherwsie if it doesn't. |  | ||||||
|        */ |  | ||||||
|       bool_t exists(); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Closes the currently open interface to the file. Done automatically |  | ||||||
|        * when this object is disposed. |  | ||||||
|        */ |  | ||||||
|       void close(); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Makes all directories above this file's filename. |  | ||||||
|        *  |  | ||||||
|        * @return True if successful, otherwise false. |  | ||||||
|        */ |  | ||||||
|       bool_t mkdirp(); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Reads the entire contents of a file to the given output string buffer. |  | ||||||
|        * This is a bit dangerous since the length of the file isn't checked |  | ||||||
|        * against the memory to be consumed. |  | ||||||
|        *  |  | ||||||
|        * @param out Pointer to a string where the output data will be stored. |  | ||||||
|        * @return True if the read was successful, otherwise false. |  | ||||||
|        */ |  | ||||||
|       bool_t readString(std::string *out); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Reads ahead from the current position to a specific needle (character). |  | ||||||
|        *  |  | ||||||
|        * @param buffer Buffer to output read chars to. |  | ||||||
|        * @param max Max length of the buffer / amount of chars to read ahead. |  | ||||||
|        * @param needle The character (needle) to look for. |  | ||||||
|        * @return Amount of chars read, or <= 0 on error. |  | ||||||
|        */ |  | ||||||
|       size_t readAhead( |  | ||||||
|         char *buffer, |  | ||||||
|         size_t max, |  | ||||||
|         char needle |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Writes the entire contents of a string to a file. |  | ||||||
|        *  |  | ||||||
|        * @param in String to write to this file. |  | ||||||
|        * @return True if written successfully, otherwise false. |  | ||||||
|        */ |  | ||||||
|       bool_t writeString(std::string in); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Write raw bytes to the file. |  | ||||||
|        *  |  | ||||||
|        * @param data Data to write. |  | ||||||
|        * @return True if written successfully, otherwise false. |  | ||||||
|        */ |  | ||||||
|       bool_t writeRaw(char *data, size_t ); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Set the position of the cursor of the file reader. |  | ||||||
|        *  |  | ||||||
|        * @param pos Position to set. |  | ||||||
|        */ |  | ||||||
|       void setPosition(size_t pos); |  | ||||||
|  |  | ||||||
|       /** |  | ||||||
|        * Destruct the File manager. |  | ||||||
|        */ |  | ||||||
|       ~File(); |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user