diff --git a/.gitmodules b/.gitmodules index 442ca0ff..fb40d0ce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "lib/AudioFile"] path = lib/AudioFile url = https://github.com/adamstark/AudioFile.git +[submodule "lib/freetype"] + path = lib/freetype + url = https://gitlab.freedesktop.org/freetype/freetype.git diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6ce1a1b4..4c1f567d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,6 +22,9 @@ add_subdirectory(glm) add_library(stb INTERFACE) target_include_directories(stb INTERFACE stb) +# FreeType +add_subdirectory(freetype) + # OpenAL if(DAWN_TARGET_OPENAL) set(LIBTYPE "STATIC") diff --git a/lib/freetype b/lib/freetype new file mode 160000 index 00000000..562f3481 --- /dev/null +++ b/lib/freetype @@ -0,0 +1 @@ +Subproject commit 562f34819229080abb05b4e7ae7e9e47fc84c6eb diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt index ba848087..ca965749 100644 --- a/src/dawn/CMakeLists.txt +++ b/src/dawn/CMakeLists.txt @@ -8,6 +8,7 @@ target_link_libraries(${DAWN_TARGET_NAME} PUBLIC glm stb + freetype ) # Includes diff --git a/src/dawn/asset/assets/TextureAsset.cpp b/src/dawn/asset/assets/TextureAsset.cpp index cf882c2a..190acb93 100644 --- a/src/dawn/asset/assets/TextureAsset.cpp +++ b/src/dawn/asset/assets/TextureAsset.cpp @@ -20,8 +20,16 @@ void TextureAsset::updateSync() { ) return; this->state = 0x04; - this->texture.setSize(this->width, this->height); + + + this->texture.setSize(this->width, this->height, this->format); this->texture.buffer(this->colors); + + this->texture.wrapModeX = this->wrapModeX; + this->texture.wrapModeY = this->wrapModeY; + this->texture.filterModeMin = this->filterModeMin; + this->texture.filterModeMag = this->filterModeMag; + this->state = 0x05; this->loaded = true; } @@ -33,24 +41,83 @@ void TextureAsset::updateAsync() { this->state = 0x02; // Parse header data. - char integer[32]; + char integer[256]; size_t j = 0, i = 0; + enum TextureAssetHeaderParseState parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_VERSION; while(true) { + if(parseState == TEXTURE_ASSET_HEADER_PARSE_STATE_END) break; + auto c = this->buffer[i++]; - if(c == '|') { - integer[j] = '\0'; - if(this->width == -1) { + if(c != '|') { + integer[j++] = c; + continue; + } + + integer[j] = '\0'; + + switch(parseState) { + case TEXTURE_ASSET_HEADER_PARSE_STATE_VERSION: { + auto compared = strcmp(integer, "DT_1.00"); + assertTrue(compared == 0); + j = 0; + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_WIDTH; + break; + } + + case TEXTURE_ASSET_HEADER_PARSE_STATE_WIDTH: { this->width = atoi(integer); assertTrue(this->width > 0); j = 0; - continue; - } else { - this->height = atoi(integer); - assertTrue(this->height > 0); + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_HEIGHT; break; } + + case TEXTURE_ASSET_HEADER_PARSE_STATE_HEIGHT: { + this->height = atoi(integer); + assertTrue(this->height > 0); + j = 0; + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_FORMAT; + break; + } + + case TEXTURE_ASSET_HEADER_PARSE_STATE_FORMAT: { + this->format = (enum TextureFormat)atoi(integer); + j = 0; + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_X; + break; + } + + case TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_X: { + this->wrapModeX = (enum TextureWrapMode)atoi(integer); + j = 0; + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_Y; + break; + } + + case TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_Y: { + this->wrapModeY = (enum TextureWrapMode)atoi(integer); + j = 0; + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MIN; + break; + } + + case TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MIN: { + this->filterModeMin = (enum TextureFilterMode)atoi(integer); + j = 0; + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MAG; + break; + } + + case TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MAG: { + this->filterModeMag = (enum TextureFilterMode)atoi(integer); + j = 0; + parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_END; + break; + } + + default: + assertUnreachable(); } - integer[j++] = c; } this->colors = (struct Color *)((void *)(this->buffer + i)); diff --git a/src/dawn/asset/assets/TextureAsset.hpp b/src/dawn/asset/assets/TextureAsset.hpp index d21c9e35..4c327879 100644 --- a/src/dawn/asset/assets/TextureAsset.hpp +++ b/src/dawn/asset/assets/TextureAsset.hpp @@ -9,12 +9,29 @@ #include "display/Texture.hpp" namespace Dawn { + enum TextureAssetHeaderParseState { + TEXTURE_ASSET_HEADER_PARSE_STATE_VERSION, + TEXTURE_ASSET_HEADER_PARSE_STATE_WIDTH, + TEXTURE_ASSET_HEADER_PARSE_STATE_HEIGHT, + TEXTURE_ASSET_HEADER_PARSE_STATE_FORMAT, + TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_X, + TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_Y, + TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MIN, + TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MAG, + TEXTURE_ASSET_HEADER_PARSE_STATE_END + }; + class TextureAsset : public Asset { protected: AssetLoader loader; uint8_t *buffer = nullptr; int32_t width = -1, height = -1; struct Color *colors; + enum TextureFormat format; + enum TextureWrapMode wrapModeX; + enum TextureWrapMode wrapModeY; + enum TextureFilterMode filterModeMin; + enum TextureFilterMode filterModeMag; public: Texture texture; diff --git a/src/dawn/asset/assets/TrueTypeAsset.cpp b/src/dawn/asset/assets/TrueTypeAsset.cpp index 73ccd8c9..a1ba7801 100644 --- a/src/dawn/asset/assets/TrueTypeAsset.cpp +++ b/src/dawn/asset/assets/TrueTypeAsset.cpp @@ -15,7 +15,7 @@ TrueTypeAsset::TrueTypeAsset(AssetManager *assMan, std::string name) : void TrueTypeAsset::updateSync() { if(this->state != 0x04) return; - this->font.texture.setSize(this->width, this->height); + this->font.texture.setSize(this->width, this->height, TEXTURE_FORMAT_RGBA); this->font.texture.buffer(this->pixels); auto i = this->pixels; memoryCopy( diff --git a/src/dawn/display/_Texture.hpp b/src/dawn/display/_Texture.hpp index b66d3959..f54626cf 100644 --- a/src/dawn/display/_Texture.hpp +++ b/src/dawn/display/_Texture.hpp @@ -5,10 +5,46 @@ #pragma once #include "display/Color.hpp" +#include "state/StateOwner.hpp" namespace Dawn { - class ITexture { + enum TextureFormat { + TEXTURE_FORMAT_R = 1, + TEXTURE_FORMAT_RG = 2, + TEXTURE_FORMAT_RGB = 3, + TEXTURE_FORMAT_RGBA = 4 + }; + + enum TextureWrapMode { + TEXTURE_WRAP_MODE_REPEAT = 0, + TEXTURE_WRAP_MODE_MIRRORED_REPEAT = 1, + TEXTURE_WRAP_MODE_CLAMP_TO_EDGE = 2, + TEXTURE_WRAP_MODE_CLAMP_TO_BORDER = 3 + }; + + enum TextureFilterMode { + TEXTURE_FILTER_MODE_NEAREST = 0, + TEXTURE_FILTER_MODE_LINEAR = 1 + }; + + class ITexture : public StateOwner { + protected: + bool_t texturePropertiesNeedUpdating = true; + public: + StateProperty wrapModeX; + StateProperty wrapModeY; + StateProperty filterModeMin; + StateProperty filterModeMag; + + ITexture() : + wrapModeX(TEXTURE_WRAP_MODE_CLAMP_TO_EDGE), + wrapModeY(TEXTURE_WRAP_MODE_CLAMP_TO_EDGE), + filterModeMin(TEXTURE_FILTER_MODE_LINEAR), + filterModeMag(TEXTURE_FILTER_MODE_LINEAR) + { + } + /** * Returns the width of the texture. * @@ -28,8 +64,13 @@ namespace Dawn { * * @param width Width of the texture (in pixels). * @param height Height of the texture (in pixels). + * @param format Data format of the texture to use. */ - virtual void setSize(int32_t width, int32_t height) = 0; + virtual void setSize( + int32_t width, + int32_t height, + enum TextureFormat format + ) = 0; /** * Fill a texture with a single color. This is stupidly costly. diff --git a/src/dawn/display/font/ExampleFont.cpp b/src/dawn/display/font/ExampleFont.cpp index 48a07a31..41bc271c 100644 --- a/src/dawn/display/font/ExampleFont.cpp +++ b/src/dawn/display/font/ExampleFont.cpp @@ -99,6 +99,6 @@ void ExampleFont::init() { pixels[j++] = this->getColor(n & 0x01); } - this->realTexture.setSize(128, 64); + this->realTexture.setSize(128, 64, TEXTURE_FORMAT_RGBA); this->realTexture.buffer(pixels); } diff --git a/src/dawnliminal/CMakeLists.txt b/src/dawnliminal/CMakeLists.txt index c0224a20..261a3063 100644 --- a/src/dawnliminal/CMakeLists.txt +++ b/src/dawnliminal/CMakeLists.txt @@ -20,8 +20,8 @@ add_subdirectory(save) set(LIMINAL_ASSETS_DIR ${DAWN_ASSETS_DIR}/games/liminal) tool_truetype(font_main ${LIMINAL_ASSETS_DIR}/fonts/Ysabeau-Medium.ttf) -tool_texture(texture_eth ${LIMINAL_ASSETS_DIR}/textures/eth.png) -tool_texture(texture_border ${LIMINAL_ASSETS_DIR}/textures/texture_test.png) +tool_texture(texture_eth FILE=${LIMINAL_ASSETS_DIR}/textures/eth.png) +tool_texture(texture_border FILE=${LIMINAL_ASSETS_DIR}/textures/texture_test.png) tool_scene(${LIMINAL_ASSETS_DIR}/scenes/SceneBase.xml) tool_vnscene(${LIMINAL_ASSETS_DIR}/scenes/Scene1Prologue0.xml) diff --git a/src/dawnopengl/display/Texture.cpp b/src/dawnopengl/display/Texture.cpp index 60809e86..3a865dbe 100644 --- a/src/dawnopengl/display/Texture.cpp +++ b/src/dawnopengl/display/Texture.cpp @@ -7,10 +7,18 @@ using namespace Dawn; +Texture::Texture() : ITexture() { +} + void Texture::bind(textureslot_t slot) { assertTrue(this->id != -1); glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(GL_TEXTURE_2D, this->id); + + if(this->texturePropertiesNeedUpdating) { + this->updateTextureProperties(); + this->texturePropertiesNeedUpdating = false; + } } int32_t Texture::getWidth() { @@ -21,56 +29,140 @@ int32_t Texture::getHeight() { return this->height; } -void Texture::setSize(int32_t width, int32_t height) { +void Texture::setSize(int32_t width, int32_t height, enum TextureFormat format) { if(this->id != -1) glDeleteTextures(1, &this->id); this->width = width; this->height = height; + this->format = format; glGenTextures(1, &this->id); if(this->id <= 0) throw "Texture generation failed!"; - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, this->id); - - // Setup our preferred texture params, later this will be configurable. - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // Initialize the texture to blank - glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA, - width, height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL - ); + glActiveTexture(GL_TEXTURE0); + this->bufferRaw(NULL); } void Texture::fill(struct Color color) { struct Color *pixels = (struct Color *)memoryAllocate( sizeof(struct Color) * this->width * this->height ); - this->buffer(pixels); - memoryFree(pixels); } bool_t Texture::isReady() { return this->id != -1; } - -void Texture::buffer(struct Color pixels[]) { + +void Texture::updateTextureProperties() { + switch(((enum TextureWrapMode)this->wrapModeX)) { + case TEXTURE_WRAP_MODE_REPEAT: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + break; + + case TEXTURE_WRAP_MODE_MIRRORED_REPEAT: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + break; + + case TEXTURE_WRAP_MODE_CLAMP_TO_EDGE: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + break; + + case TEXTURE_WRAP_MODE_CLAMP_TO_BORDER: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + break; + + default: + assertUnreachable(); + } + + switch(((enum TextureWrapMode)this->wrapModeY)) { + case TEXTURE_WRAP_MODE_REPEAT: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T ,GL_REPEAT); + break; + + case TEXTURE_WRAP_MODE_MIRRORED_REPEAT: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + break; + + case TEXTURE_WRAP_MODE_CLAMP_TO_EDGE: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + break; + + case TEXTURE_WRAP_MODE_CLAMP_TO_BORDER: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + break; + + default: + assertUnreachable(); + } + + switch(((enum TextureFilterMode)this->filterModeMin)) { + case TEXTURE_FILTER_MODE_NEAREST: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + break; + + case TEXTURE_FILTER_MODE_LINEAR: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + break; + + default: + assertUnreachable(); + } + + switch(((enum TextureFilterMode)this->filterModeMag)) { + case TEXTURE_FILTER_MODE_NEAREST: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); + break; + + case TEXTURE_FILTER_MODE_LINEAR: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + break; + + default: + assertUnreachable(); + } +} + +void Texture::bufferRaw(void *data) { + assertTrue(this->isReady()); glBindTexture(GL_TEXTURE_2D, this->id); + GLenum format; + switch(this->format) { + case TEXTURE_FORMAT_R: + format = GL_RED; + break; + + case TEXTURE_FORMAT_RG: + format = GL_RG; + break; + + case TEXTURE_FORMAT_RGB: + format = GL_RGB; + break; + + case TEXTURE_FORMAT_RGBA: + format = GL_RGBA; + break; + + default: + assertUnreachable(); + } + glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA, + GL_TEXTURE_2D, 0, format, this->width, this->height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixels + 0, format, GL_UNSIGNED_BYTE, data ); + glGenerateMipmap(GL_TEXTURE_2D); + this->texturePropertiesNeedUpdating = true; +} + +void Texture::buffer(struct Color pixels[]) { + this->bufferRaw((void*)pixels); } Texture::~Texture() { diff --git a/src/dawnopengl/display/Texture.hpp b/src/dawnopengl/display/Texture.hpp index b292c864..56d3785b 100644 --- a/src/dawnopengl/display/Texture.hpp +++ b/src/dawnopengl/display/Texture.hpp @@ -19,11 +19,16 @@ namespace Dawn { int32_t width = -1; int32_t height = -1; GLuint id = -1; + enum TextureFormat format; + + void updateTextureProperties(); + void bufferRaw(void *data); public: + Texture(); int32_t getWidth() override; int32_t getHeight() override; - void setSize(int32_t width, int32_t height) override; + void setSize(int32_t width, int32_t height, enum TextureFormat format) override; void fill(struct Color) override; bool_t isReady() override; void buffer(struct Color pixels[]) override; diff --git a/src/dawnopengl/display/TextureRenderTarget.cpp b/src/dawnopengl/display/TextureRenderTarget.cpp index 94f709ac..d04ee6e0 100644 --- a/src/dawnopengl/display/TextureRenderTarget.cpp +++ b/src/dawnopengl/display/TextureRenderTarget.cpp @@ -26,7 +26,7 @@ void TextureRenderTarget::setSize(float_t width, float_t height) { if(this->fboId != -1) glDeleteFramebuffers(1, &this->fboId); // Resize texture - this->texture.setSize((int32_t)width, (int32_t) height); + this->texture.setSize((int32_t)width, (int32_t)height, TEXTURE_FORMAT_RGBA); this->eventRenderTargetResized.invoke(this, width, height); // Create Frame Buffer diff --git a/src/dawntools/texturetool/CMakeLists.txt b/src/dawntools/texturetool/CMakeLists.txt index f343a691..31744395 100644 --- a/src/dawntools/texturetool/CMakeLists.txt +++ b/src/dawntools/texturetool/CMakeLists.txt @@ -38,15 +38,42 @@ target_link_libraries(texturetool ) # Tool Function -function(tool_texture target in) +function(tool_texture target) + # Defaults + set(FILE "" ) + set(FILTER_MIN "") + set(FILTER_MAG "") + set(WRAP_X "") + set(WRAP_Y "") + + # Parse Args + foreach(_PAIR IN LISTS ARGN) + if (_PAIR MATCHES "^([^:]+)=(.*)$") + set(${CMAKE_MATCH_1} ${CMAKE_MATCH_2}) + else() + message(FATAL_ERROR "Invalid pair: ${_PAIR}") + endif() + endforeach() + + # Check for missing args + if(NOT DEFINED FILE) + message(FATAL_ERROR "Missing FILE input") + endif() + set(DEPS "") if(DAWN_BUILD_TOOLS) set(DEPS texturetool) endif() add_custom_target(${target} - COMMAND texturetool --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_ASSETS_BUILD_DIR}/${target}" - COMMENT "Generating texture ${target} from ${in}" + COMMAND texturetool + --input="${DAWN_ASSETS_SOURCE_DIR}/${FILE}" + --output="${DAWN_ASSETS_BUILD_DIR}/${target}" + --wrapX="${WRAP_X}" + --wrapY="${WRAP_Y}" + --filterMin="${FILTER_MIN}" + --filterMag="${FILTER_MIN}" + COMMENT "Generating texture ${target} from ${FILE}" DEPENDS ${DEPS} ) add_dependencies(${DAWN_TARGET_NAME} ${target}) diff --git a/src/dawntools/texturetool/TextureTool.cpp b/src/dawntools/texturetool/TextureTool.cpp index 9b66163c..c930107c 100644 --- a/src/dawntools/texturetool/TextureTool.cpp +++ b/src/dawntools/texturetool/TextureTool.cpp @@ -11,6 +11,15 @@ std::vector TextureTool::getRequiredFlags() { return std::vector{ "input", "output" }; } +std::map TextureTool::getOptionalFlags() { + return { + { "wrapX", "clamp" }, + { "wrapY", "clamp" }, + { "filterMin", "linear" }, + { "filterMax", "linear" } + }; +} + int32_t TextureTool::start() { // Finished with XML data, now we can write data out. File fileOut(flags["output"] + ".texture"); @@ -40,6 +49,38 @@ int32_t TextureTool::start() { } stbi_image_free(imageRaw); + std::function wrapFromString = [&](std::string wr) { + if(wr == "repeat") return 0; + if(wr == "mirror") return 1; + if(wr == "clamp") return 2; + if(wr == "border") return 3; + return -1; + }; + + int32_t wrapX = wrapFromString(flags["wrapX"]); + if(wrapX == -1) { + std::cout << "Invalid wrapX value " << flags["wrapX"] << std::endl; + return 1; + } + + int32_t wrapY = wrapFromString(flags["wrapY"]); + if(wrapY == -1) { + std::cout << "Invalid wrapY value " << flags["wrapY"] << std::endl; + return 1; + } + + // Write info + char headerBuffer[256]; + size_t headerBufferLength = sprintf((char *)headerBuffer, "DT_1.00|%i|%i|%i|%i|%i|%i|%i|", + w, + h, + 4, // RGBA, + wrapX, // WRAPX + wrapY, // WRAPY + flags["filterMin"] == "nearest" ? 0 : 1, + flags["filterMag"] == "nearest" ? 0 : 1 + ); + // Open and create output File out(flags["output"] + ".texture"); if(!out.mkdirp()) { @@ -50,10 +91,6 @@ int32_t TextureTool::start() { std::cout << "Failed to open texture file for writing " << out.filename << std::endl; return 1; } - - // Write info - char headerBuffer[64]; - size_t headerBufferLength = sprintf((char *)headerBuffer, "%i|%i|", w, h); if(!out.writeRaw(headerBuffer, headerBufferLength)) { std::cout << "Failed to write texture header for " << out.filename << std::endl; return 1; diff --git a/src/dawntools/texturetool/TextureTool.hpp b/src/dawntools/texturetool/TextureTool.hpp index 911f8f39..1e7114e7 100644 --- a/src/dawntools/texturetool/TextureTool.hpp +++ b/src/dawntools/texturetool/TextureTool.hpp @@ -12,6 +12,7 @@ namespace Dawn { class TextureTool : public DawnTool { protected: std::vector getRequiredFlags() override; + std::map getOptionalFlags() override; public: int32_t start(); diff --git a/src/dawntools/util/DawnTool.cpp b/src/dawntools/util/DawnTool.cpp index 661273d9..02af162b 100644 --- a/src/dawntools/util/DawnTool.cpp +++ b/src/dawntools/util/DawnTool.cpp @@ -51,6 +51,7 @@ int32_t DawnTool::exec(const int32_t argc, const char *argv[]) { // Get prefix and val and store auto prefix = flag.substr(0, equalPos); auto val = flag.substr(equalPos + 1); + if(val.size() == 0) continue; flags[prefix] = val; }