diff --git a/data/languages/en.json b/data/languages/en.json new file mode 100644 index 0000000..6287dfb --- /dev/null +++ b/data/languages/en.json @@ -0,0 +1,8 @@ +{ + "meta": { + "language": { + "name": "English", + "code": "en" + } + } +} \ No newline at end of file diff --git a/src/duskraylib/display/draw/drawui.c b/src/duskraylib/display/draw/drawui.c index 9c4f06f..6d84ee7 100644 --- a/src/duskraylib/display/draw/drawui.c +++ b/src/duskraylib/display/draw/drawui.c @@ -88,9 +88,11 @@ void drawUIText( assertNotNull(text, "Text to draw cannot be NULL"); if(length == 0) return; + BeginBlendMode(BLEND_ALPHA); for(uint16_t i = 0; i < length; i++) { drawUIChar(text[i], x + (i * FONT_TILE_WIDTH), y, color); } + EndBlendMode(); } void drawUIChar( diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 063df48..8544a21 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -6,4 +6,5 @@ # Tools add_subdirectory(mapcompile) add_subdirectory(tilecompile) -add_subdirectory(fontcompile) \ No newline at end of file +add_subdirectory(fontcompile) +add_subdirectory(languagecompile) \ No newline at end of file diff --git a/tools/languagecompile/CMakeLists.txt b/tools/languagecompile/CMakeLists.txt new file mode 100644 index 0000000..c10b92c --- /dev/null +++ b/tools/languagecompile/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +# Custom command to generate all header files +add_custom_target(DUSK_LANGUAGES + COMMAND + ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/languagecompile.py + --output ${DUSK_GENERATED_HEADERS_DIR}/locale/language/ + --input ${DUSK_DATA_DIR}/languages/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/languagecompile.py + COMMENT "Generating language header files" + VERBATIM +) + +# Ensure headers are generated before compiling main +add_dependencies(${DUSK_TARGET_NAME} DUSK_LANGUAGES) \ No newline at end of file diff --git a/tools/languagecompile/languagecompile.py b/tools/languagecompile/languagecompile.py new file mode 100644 index 0000000..61e26e4 --- /dev/null +++ b/tools/languagecompile/languagecompile.py @@ -0,0 +1,136 @@ +import sys, os +import argparse +from datetime import datetime + +# Check if the script is run with the correct arguments +parser = argparse.ArgumentParser(description="Generate chunk header files") +parser.add_argument('--output', required=True, help='Dir to write headers') +parser.add_argument('--input', required=True, help='Input directory containing language files') +args = parser.parse_args() + +# Ensure outdir exists +outputFile = args.output +outputDir = args.output +os.makedirs(outputDir, exist_ok=True) + +inputDir = args.input +# Scan for .json files in the input directory +if not os.path.exists(inputDir): + print(f"Error: Input directory '{inputDir}' does not exist.") + sys.exit(1) + +jsonFiles = [f for f in os.listdir(inputDir) if f.endswith('.json')] +if not jsonFiles or len(jsonFiles) == 0: + print(f"Error: No JSON files found in '{inputDir}'.") + sys.exit(1) + +# take JSON from form { "a": { "b": { "c": "d" } } } to "a.b.c": "d" +def flattenJson(y): + keyValues = {} + for key, value in y.items(): + if isinstance(value, dict): + # If the value is a dictionary, recurse into it + subKeyValues = flattenJson(value) + for subKey, subValue in subKeyValues.items(): + keyValues[f"{key}.{subKey}"] = subValue + else: + # If the value is not a dictionary, add it to the keyValues + keyValues[key] = value + return keyValues + +def escapeString(s): + # Escape double quotes and backslashes in the string + return s.replace('\\', '\\\\').replace('"', '\\"') + +# For each language file... +now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") +isFirstLanguage = True + +# Because I code in english, I am going to reorder the langs so it is first. +jsonFiles.sort(key=lambda x: x.lower() if x.lower() == 'en.json' else "zz_" + x.lower()) + +keysExpected = [] + +languages = [] +for jsonFile in jsonFiles: + inputFile = os.path.join(inputDir, jsonFile) + languageName = os.path.splitext(jsonFile)[0] + langUpper = languageName.upper() + outputFile = os.path.join(outputDir, f"{languageName}.h") + + # Read the JSON file + with open(inputFile, 'r', encoding='utf-8') as f: + content = f.read() + + # Write the header file + with open(outputFile, 'w', encoding='utf-8') as f: + f.write(f"// Generated from {jsonFile} on {now}\n") + f.write("#pragma once\n") + f.write("#include \"dusk.h\"\n\n") + f.write(f"// Language: {languageName} from {jsonFile}\n") + + keyValues = flattenJson(eval(content)) + + if 'meta.language.name' not in keyValues: + print(f"Error: 'meta.language.name' not found in {jsonFile}.") + sys.exit(1) + + f.write(f"#define LANGUAGE_{langUpper}_CODE \"{languageName}\"\n") + f.write(f"#define LANGUAGE_{langUpper}_NAME \"{keyValues['meta.language.name']}\"\n") + f.write(f"#define LANGUAGE_{langUpper}_COUNT_KEYS {len(keyValues)}\n\n") + + # Write keys + f.write(f"static const char_t *LANGUAGE_{langUpper}_KEYS[] = {{\n") + for key in keyValues.keys(): + f.write(f' "{escapeString(key)}",\n') + f.write("};\n\n") + + # Write values + f.write(f"static const char_t *LANGUAGE_{langUpper}_VALUES[] = {{\n") + for value in keyValues.values(): + f.write(f' "{escapeString(value)}",\n') + f.write("};\n\n") + + languages.append(langUpper) + + if isFirstLanguage: + # For the first language, we also write the keysExpected + keysExpected = list(keyValues.keys()) + else: + for key in keysExpected: + if key in keyValues: + continue + print(f"Error, expected language translation key: '{key}' was not found in {jsonFile}.") + sys.exit(1) + +# Now write the main header file +mainOutputFile = os.path.join(outputDir, "languages.h") +with open(mainOutputFile, 'w', encoding='utf-8') as f: + f.write("// Generated from languagecompile.py\n") + f.write("#pragma once\n") + f.write("#include \"dusk.h\"\n") + for lang in languages: + f.write(f'#include "locale/language/{lang.lower()}.h"\n') + f.write("\n") + + f.write(f"#define LANGUAGES_COUNT {len(languages)}\n\n") + + f.write("static const char_t *LANGUAGE_CODES[] = {\n") + for lang in languages: + f.write(f' LANGUAGE_{lang}_CODE,\n') + f.write("};\n\n") + + f.write("static const char_t *LANGUAGE_NAMES[] = {\n") + for lang in languages: + f.write(f' LANGUAGE_{lang}_NAME,\n') + f.write("};\n\n") + + f.write("static const char_t *LANGUAGE_KEYS[] = {\n") + for lang in languages: + f.write(f' LANGUAGE_{lang}_KEYS,\n') + f.write("};\n\n") + + f.write("static const char_t *LANGUAGE_VALUES[] = {\n") + for lang in languages: + f.write(f' LANGUAGE_{lang}_VALUES,\n') + f.write("};\n\n") \ No newline at end of file