diff --git a/projects/BMFontExtender/Makefile b/projects/BMFontExtender/Makefile
new file mode 100644
index 000000000..67038eb8e
--- /dev/null
+++ b/projects/BMFontExtender/Makefile
@@ -0,0 +1,405 @@
+#**************************************************************************************************
+#
+# raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5
+#
+# Copyright (c) 2013-2019 Ramon Santamaria (@raysan5)
+#
+# This software is provided "as-is", without any express or implied warranty. In no event
+# will the authors be held liable for any damages arising from the use of this software.
+#
+# Permission is granted to anyone to use this software for any purpose, including commercial
+# applications, and to alter it and redistribute it freely, subject to the following restrictions:
+#
+# 1. The origin of this software must not be misrepresented; you must not claim that you
+# wrote the original software. If you use this software in a product, an acknowledgment
+# in the product documentation would be appreciated but is not required.
+#
+# 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+# as being the original software.
+#
+# 3. This notice may not be removed or altered from any source distribution.
+#
+#**************************************************************************************************
+
+.PHONY: all clean
+
+# Define required raylib variables
+PROJECT_NAME ?= game
+RAYLIB_VERSION ?= 4.2.0
+RAYLIB_PATH ?= C:/Users/hope8/OneDrive/GameDev/RayLibCPP/raylib/raylib
+
+# Define compiler path on Windows
+COMPILER_PATH ?= C:/raylib/w64devkit/bin
+
+# Define default options
+# One of PLATFORM_DESKTOP, PLATFORM_ANDROID, PLATFORM_WEB
+PLATFORM ?= PLATFORM_DESKTOP
+
+# Locations of your newly installed library and associated headers. See ../src/Makefile
+# On Linux, if you have installed raylib but cannot compile the examples, check that
+# the *_INSTALL_PATH values here are the same as those in src/Makefile or point to known locations.
+# To enable system-wide compile-time and runtime linking to libraylib.so, run ../src/$ sudo make install RAYLIB_LIBTYPE_SHARED.
+# To enable compile-time linking to a special version of libraylib.so, change these variables here.
+# To enable runtime linking to a special version of libraylib.so, see EXAMPLE_RUNTIME_PATH below.
+# If there is a libraylib in both EXAMPLE_RUNTIME_PATH and RAYLIB_INSTALL_PATH, at runtime,
+# the library at EXAMPLE_RUNTIME_PATH, if present, will take precedence over the one at RAYLIB_INSTALL_PATH.
+# RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths.
+DESTDIR ?= C:/Users/hope8/OneDrive/GameDev/RayLibCPP/raylib/raylib
+RAYLIB_INSTALL_PATH ?= $(DESTDIR)
+# RAYLIB_H_INSTALL_PATH locates the installed raylib header and associated source files.
+RAYLIB_H_INSTALL_PATH ?= $(DESTDIR)
+
+# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll)
+RAYLIB_LIBTYPE ?= STATIC
+
+# Build mode for project: DEBUG or RELEASE
+BUILD_MODE ?= RELEASE
+
+# Use external GLFW library instead of rglfw module
+# TODO: Review usage on Linux. Target version of choice. Switch on -lglfw or -lglfw3
+USE_EXTERNAL_GLFW ?= FALSE
+
+# Use Wayland display server protocol on Linux desktop
+# by default it uses X11 windowing system
+USE_WAYLAND_DISPLAY ?= FALSE
+
+# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ # No uname.exe on MinGW!, but OS=Windows_NT on Windows!
+ # ifeq ($(UNAME),Msys) -> Windows
+ ifeq ($(OS),Windows_NT)
+ PLATFORM_OS=WINDOWS
+ export PATH := $(COMPILER_PATH):$(PATH)
+ else
+ UNAMEOS=$(shell uname)
+ ifeq ($(UNAMEOS),Linux)
+ PLATFORM_OS=LINUX
+ endif
+ ifeq ($(UNAMEOS),FreeBSD)
+ PLATFORM_OS=BSD
+ endif
+ ifeq ($(UNAMEOS),OpenBSD)
+ PLATFORM_OS=BSD
+ endif
+ ifeq ($(UNAMEOS),NetBSD)
+ PLATFORM_OS=BSD
+ endif
+ ifeq ($(UNAMEOS),DragonFly)
+ PLATFORM_OS=BSD
+ endif
+ ifeq ($(UNAMEOS),Darwin)
+ PLATFORM_OS=OSX
+ endif
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ UNAMEOS=$(shell uname)
+ ifeq ($(UNAMEOS),Linux)
+ PLATFORM_OS=LINUX
+ endif
+endif
+
+# RAYLIB_PATH adjustment for different platforms.
+# If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
+# Required for ldconfig or other tools that do not perform path expansion.
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),LINUX)
+ RAYLIB_PREFIX ?= ..
+ RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX))
+ endif
+endif
+# Default path for raylib on Raspberry Pi, if installed in different path, update it!
+# This is not currently used by src/Makefile. Not sure of its origin or usage. Refer to wiki.
+# TODO: update install: target in src/Makefile for RPI, consider relation to LINUX.
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ RAYLIB_PATH ?= /home/pi/raylib
+endif
+
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ # Emscripten required variables
+ EMSDK_PATH ?= C:/emsdk
+ EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten
+ CLANG_PATH = $(EMSDK_PATH)/upstream/bin
+ PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit
+ NODE_PATH = $(EMSDK_PATH)/node/14.18.2_64bit/bin
+ export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH)
+endif
+
+# Define raylib release directory for compiled library.
+# RAYLIB_RELEASE_PATH points to provided binaries or your freshly built version
+RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src
+
+# EXAMPLE_RUNTIME_PATH embeds a custom runtime location of libraylib.so or other desired libraries
+# into each example binary compiled with RAYLIB_LIBTYPE=SHARED. It defaults to RAYLIB_RELEASE_PATH
+# so that these examples link at runtime with your version of libraylib.so in ../release/libs/linux
+# without formal installation from ../src/Makefile. It aids portability and is useful if you have
+# multiple versions of raylib, have raylib installed to a non-standard location, or want to
+# bundle libraylib.so with your game. Change it to your liking.
+# NOTE: If, at runtime, there is a libraylib.so at both EXAMPLE_RUNTIME_PATH and RAYLIB_INSTALL_PATH,
+# The library at EXAMPLE_RUNTIME_PATH, if present, will take precedence over RAYLIB_INSTALL_PATH,
+# Implemented for LINUX below with CFLAGS += -Wl,-rpath,$(EXAMPLE_RUNTIME_PATH)
+# To see the result, run readelf -d core/core_basic_window; looking at the RPATH or RUNPATH attribute.
+# To see which libraries a built example is linking to, ldd core/core_basic_window;
+# Look for libraylib.so.1 => $(RAYLIB_INSTALL_PATH)/libraylib.so.1 or similar listing.
+EXAMPLE_RUNTIME_PATH ?= $(RAYLIB_RELEASE_PATH)
+
+# Define default C compiler: gcc
+# NOTE: define g++ compiler if using C++
+CC = gcc
+
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),OSX)
+ # OSX default compiler
+ CC = clang
+ endif
+ ifeq ($(PLATFORM_OS),BSD)
+ # FreeBSD, OpenBSD, NetBSD, DragonFly default compiler
+ CC = clang
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ ifeq ($(USE_RPI_CROSS_COMPILER),TRUE)
+ # Define RPI cross-compiler
+ #CC = armv6j-hardfloat-linux-gnueabi-gcc
+ CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ # HTML5 emscripten compiler
+ # WARNING: To compile to HTML5, code must be redesigned
+ # to use emscripten.h and emscripten_set_main_loop()
+ CC = emcc
+endif
+
+# Define default make program: Mingw32-make
+MAKE = mingw32-make
+
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),LINUX)
+ MAKE = make
+ endif
+ ifeq ($(PLATFORM_OS),OSX)
+ MAKE = make
+ endif
+endif
+
+# Define compiler flags:
+# -O0 defines optimization level (no optimization, better for debugging)
+# -O1 defines optimization level
+# -g include debug information on compilation
+# -s strip unnecessary data from build -> do not use in debug builds
+# -Wall turns on most, but not all, compiler warnings
+# -std=c99 defines C language mode (standard C from 1999 revision)
+# -std=gnu99 defines C language mode (GNU C from 1999 revision)
+# -Wno-missing-braces ignore invalid warning (GCC bug 53119)
+# -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec
+CFLAGS += -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces
+
+ifeq ($(BUILD_MODE),DEBUG)
+ CFLAGS += -g -O0
+else
+ CFLAGS += -s -O1
+endif
+
+# Additional flags for compiler (if desired)
+#CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),WINDOWS)
+ # resource file contains windows executable icon and properties
+ # -Wl,--subsystem,windows hides the console window
+ CFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data -Wl,--subsystem,windows
+ endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ ifeq ($(RAYLIB_LIBTYPE),STATIC)
+ CFLAGS += -D_DEFAULT_SOURCE
+ endif
+ ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ # Explicitly enable runtime link to libraylib.so
+ CFLAGS += -Wl,-rpath,$(EXAMPLE_RUNTIME_PATH)
+ endif
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ CFLAGS += -std=gnu99
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ # -Os # size optimization
+ # -O2 # optimization level 2, if used, also set --memory-init-file 0
+ # -s USE_GLFW=3 # Use glfw3 library (context/input management)
+ # -s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing -> WARNING: Audio buffers could FAIL!
+ # -s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB)
+ # -s USE_PTHREADS=1 # multithreading support
+ # -s WASM=0 # disable Web Assembly, emitted by default
+ # -s EMTERPRETIFY=1 # enable emscripten code interpreter (very slow)
+ # -s EMTERPRETIFY_ASYNC=1 # support synchronous loops by emterpreter
+ # -s FORCE_FILESYSTEM=1 # force filesystem to load/save files data
+ # -s ASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off)
+ # --profiling # include information for code profiling
+ # --memory-init-file 0 # to avoid an external memory initialization code file (.mem)
+ # --preload-file resources # specify a resources folder for data compilation
+ CFLAGS += -Os -s USE_GLFW=3 -s TOTAL_MEMORY=16777216 --preload-file resources
+ ifeq ($(BUILD_MODE), DEBUG)
+ CFLAGS += -s ASSERTIONS=1 --profiling
+ endif
+
+ # Define a custom shell .html and output extension
+ CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
+ EXT = .html
+endif
+
+# Define include paths for required headers
+# NOTE: Several external required libraries (stb and others)
+INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external
+
+# Define additional directories containing required header files
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ # RPI required libraries
+ INCLUDE_PATHS += -I/opt/vc/include
+ INCLUDE_PATHS += -I/opt/vc/include/interface/vmcs_host/linux
+ INCLUDE_PATHS += -I/opt/vc/include/interface/vcos/pthreads
+endif
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),BSD)
+ # Consider -L$(RAYLIB_H_INSTALL_PATH)
+ INCLUDE_PATHS += -I/usr/local/include
+ endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ # Reset everything.
+ # Precedence: immediately local, installed version, raysan5 provided libs -I$(RAYLIB_H_INSTALL_PATH) -I$(RAYLIB_PATH)/release/include
+ INCLUDE_PATHS = -I$(RAYLIB_H_INSTALL_PATH) -isystem. -isystem$(RAYLIB_PATH)/src -isystem$(RAYLIB_PATH)/release/include -isystem$(RAYLIB_PATH)/src/external
+ endif
+endif
+
+# Define library paths containing required libs.
+LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src
+
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),BSD)
+ # Consider -L$(RAYLIB_INSTALL_PATH)
+ LDFLAGS += -L. -Lsrc -L/usr/local/lib
+ endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ # Reset everything.
+ # Precedence: immediately local, installed version, raysan5 provided libs
+ LDFLAGS = -L. -L$(RAYLIB_INSTALL_PATH) -L$(RAYLIB_RELEASE_PATH)
+ endif
+endif
+
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ LDFLAGS += -L/opt/vc/lib
+endif
+
+# Define any libraries required on linking
+# if you want to link libraries (libname.so or libname.a), use the -lname
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),WINDOWS)
+ # Libraries for Windows desktop compilation
+ # NOTE: WinMM library required to set high-res timer resolution
+ LDLIBS = -lraylib -lopengl32 -lgdi32 -lwinmm
+ endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ # Libraries for Debian GNU/Linux desktop compiling
+ # NOTE: Required packages: libegl1-mesa-dev
+ LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt
+
+ # On X11 requires also below libraries
+ LDLIBS += -lX11
+ # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them
+ #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor
+
+ # On Wayland windowing system, additional libraries requires
+ ifeq ($(USE_WAYLAND_DISPLAY),TRUE)
+ LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon
+ endif
+ # Explicit link to libc
+ ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ LDLIBS += -lc
+ endif
+ endif
+ ifeq ($(PLATFORM_OS),OSX)
+ # Libraries for OSX 10.9 desktop compiling
+ # NOTE: Required packages: libopenal-dev libegl1-mesa-dev
+ LDLIBS = -lraylib -framework OpenGL -framework OpenAL -framework Cocoa
+ endif
+ ifeq ($(PLATFORM_OS),BSD)
+ # Libraries for FreeBSD, OpenBSD, NetBSD, DragonFly desktop compiling
+ # NOTE: Required packages: mesa-libs
+ LDLIBS = -lraylib -lGL -lpthread -lm
+
+ # On XWindow requires also below libraries
+ LDLIBS += -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor
+ endif
+ ifeq ($(USE_EXTERNAL_GLFW),TRUE)
+ # NOTE: It could require additional packages installed: libglfw3-dev
+ LDLIBS += -lglfw
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ # Libraries for Raspberry Pi compiling
+ # NOTE: Required packages: libasound2-dev (ALSA)
+ LDLIBS = -lraylib -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ # Libraries for web (HTML5) compiling
+ LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a
+endif
+
+# Define a recursive wildcard function
+rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
+
+# Define all source files required
+SRC_DIR = src
+OBJ_DIR = obj
+
+# Define all object files from source files
+SRC = $(call rwildcard, *.c, *.h)
+#OBJS = $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
+OBJS ?= main.c
+
+# For Android platform we call a custom Makefile.Android
+ifeq ($(PLATFORM),PLATFORM_ANDROID)
+ MAKEFILE_PARAMS = -f Makefile.Android
+ export PROJECT_NAME
+ export SRC_DIR
+else
+ MAKEFILE_PARAMS = $(PROJECT_NAME)
+endif
+
+# Default target entry
+# NOTE: We call this Makefile target or Makefile.Android target
+all:
+ $(MAKE) $(MAKEFILE_PARAMS)
+
+# Project target defined by PROJECT_NAME
+$(PROJECT_NAME): $(OBJS)
+ $(CC) -o $(PROJECT_NAME)$(EXT) $(OBJS) $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
+# Compile source files
+# NOTE: This pattern will compile every module defined on $(OBJS)
+#%.o: %.c
+$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
+ $(CC) -c $< -o $@ $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
+
+# Clean everything
+clean:
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),WINDOWS)
+ del *.o *.exe /s
+ endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ find -type f -executable | xargs file -i | grep -E 'x-object|x-archive|x-sharedlib|x-executable' | rev | cut -d ':' -f 2- | rev | xargs rm -fv
+ endif
+ ifeq ($(PLATFORM_OS),OSX)
+ find . -type f -perm +ugo+x -delete
+ rm -f *.o
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ find . -type f -executable -delete
+ rm -fv *.o
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ del *.o *.html *.js
+endif
+ @echo Cleaning done
+
diff --git a/projects/BMFontExtender/Makefile.Android b/projects/BMFontExtender/Makefile.Android
new file mode 100644
index 000000000..7e41ea52f
--- /dev/null
+++ b/projects/BMFontExtender/Makefile.Android
@@ -0,0 +1,300 @@
+#**************************************************************************************************
+#
+# raylib makefile for Android project (APK building)
+#
+# Copyright (c) 2017 Ramon Santamaria (@raysan5)
+#
+# This software is provided "as-is", without any express or implied warranty. In no event
+# will the authors be held liable for any damages arising from the use of this software.
+#
+# Permission is granted to anyone to use this software for any purpose, including commercial
+# applications, and to alter it and redistribute it freely, subject to the following restrictions:
+#
+# 1. The origin of this software must not be misrepresented; you must not claim that you
+# wrote the original software. If you use this software in a product, an acknowledgment
+# in the product documentation would be appreciated but is not required.
+#
+# 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+# as being the original software.
+#
+# 3. This notice may not be removed or altered from any source distribution.
+#
+#**************************************************************************************************
+
+# Define required raylib variables
+PLATFORM ?= PLATFORM_ANDROID
+RAYLIB_PATH ?= ..\..
+
+# Define Android architecture (armeabi-v7a, arm64-v8a, x86, x86-64) and API version
+ANDROID_ARCH ?= ARM
+ANDROID_API_VERSION = 21
+ifeq ($(ANDROID_ARCH),ARM)
+ ANDROID_ARCH_NAME = armeabi-v7a
+endif
+ifeq ($(ANDROID_ARCH),ARM64)
+ ANDROID_ARCH_NAME = arm64-v8a
+endif
+
+# Required path variables
+# NOTE: JAVA_HOME must be set to JDK
+JAVA_HOME ?= C:/JavaJDK
+ANDROID_HOME = C:/android-sdk
+ANDROID_TOOLCHAIN = C:/android_toolchain_$(ANDROID_ARCH)_API$(ANDROID_API_VERSION)
+ANDROID_BUILD_TOOLS = $(ANDROID_HOME)/build-tools/28.0.1
+ANDROID_PLATFORM_TOOLS = $(ANDROID_HOME)/platform-tools
+
+# Android project configuration variables
+PROJECT_NAME ?= raylib_game
+PROJECT_LIBRARY_NAME ?= main
+PROJECT_BUILD_PATH ?= android.$(PROJECT_NAME)
+PROJECT_RESOURCES_PATH ?= resources
+PROJECT_SOURCE_FILES ?= raylib_game.c
+
+# Some source files are placed in directories, when compiling to some
+# output directory other than source, that directory must pre-exist.
+# Here we get a list of required folders that need to be created on
+# code output folder $(PROJECT_BUILD_PATH)\obj to avoid GCC errors.
+PROJECT_SOURCE_DIRS = $(sort $(dir $(PROJECT_SOURCE_FILES)))
+
+# Android app configuration variables
+APP_LABEL_NAME ?= rGame
+APP_COMPANY_NAME ?= raylib
+APP_PRODUCT_NAME ?= rgame
+APP_VERSION_CODE ?= 1
+APP_VERSION_NAME ?= 1.0
+APP_ICON_LDPI ?= $(RAYLIB_PATH)\logo\raylib_36x36.png
+APP_ICON_MDPI ?= $(RAYLIB_PATH)\logo\raylib_48x48.png
+APP_ICON_HDPI ?= $(RAYLIB_PATH)\logo\raylib_72x72.png
+APP_SCREEN_ORIENTATION ?= landscape
+APP_KEYSTORE_PASS ?= raylib
+
+# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll)
+RAYLIB_LIBTYPE ?= STATIC
+
+# Library path for libraylib.a/libraylib.so
+RAYLIB_LIB_PATH = $(RAYLIB_PATH)\src
+
+# Shared libs must be added to APK if required
+# NOTE: Generated NativeLoader.java automatically load those libraries
+ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ PROJECT_SHARED_LIBS = lib/$(ANDROID_ARCH_NAME)/libraylib.so
+endif
+
+# Compiler and archiver
+# NOTE: GCC is being deprecated in Android NDK r16
+ifeq ($(ANDROID_ARCH),ARM)
+ CC = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-clang
+ AR = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-ar
+endif
+ifeq ($(ANDROID_ARCH),ARM64)
+ CC = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-clang
+ AR = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-ar
+endif
+
+# Compiler flags for arquitecture
+ifeq ($(ANDROID_ARCH),ARM)
+ CFLAGS = -std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
+endif
+ifeq ($(ANDROID_ARCH),ARM64)
+ CFLAGS = -std=c99 -target aarch64 -mfix-cortex-a53-835769
+endif
+# Compilation functions attributes options
+CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC
+# Compiler options for the linker
+CFLAGS += -Wall -Wa,--noexecstack -Wformat -Werror=format-security -no-canonical-prefixes
+# Preprocessor macro definitions
+CFLAGS += -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=$(ANDROID_API_VERSION)
+
+# Paths containing required header files
+INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external/android/native_app_glue
+
+# Linker options
+LDFLAGS = -Wl,-soname,lib$(PROJECT_LIBRARY_NAME).so -Wl,--exclude-libs,libatomic.a
+LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings
+# Force linking of library module to define symbol
+LDFLAGS += -u ANativeActivity_onCreate
+# Library paths containing required libs
+LDFLAGS += -L. -L$(PROJECT_BUILD_PATH)/obj -L$(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME) -L$(ANDROID_TOOLCHAIN)\sysroot\usr\lib
+
+# Define any libraries to link into executable
+# if you want to link libraries (libname.so or libname.a), use the -lname
+LDLIBS = -lm -lc -lraylib -llog -landroid -lEGL -lGLESv2 -lOpenSLES -ldl
+
+# Generate target objects list from PROJECT_SOURCE_FILES
+OBJS = $(patsubst %.c, $(PROJECT_BUILD_PATH)/obj/%.o, $(PROJECT_SOURCE_FILES))
+
+# Android APK building process... some steps required...
+# NOTE: typing 'make' will invoke the default target entry called 'all',
+all: create_temp_project_dirs \
+ copy_project_required_libs \
+ copy_project_resources \
+ generate_loader_script \
+ generate_android_manifest \
+ generate_apk_keystore \
+ config_project_package \
+ compile_project_code \
+ compile_project_class \
+ compile_project_class_dex \
+ create_project_apk_package \
+ sign_project_apk_package \
+ zipalign_project_apk_package
+
+# Create required temp directories for APK building
+create_temp_project_dirs:
+ if not exist $(PROJECT_BUILD_PATH) mkdir $(PROJECT_BUILD_PATH)
+ if not exist $(PROJECT_BUILD_PATH)\obj mkdir $(PROJECT_BUILD_PATH)\obj
+ if not exist $(PROJECT_BUILD_PATH)\src mkdir $(PROJECT_BUILD_PATH)\src
+ if not exist $(PROJECT_BUILD_PATH)\src\com mkdir $(PROJECT_BUILD_PATH)\src\com
+ if not exist $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME) mkdir $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME)
+ if not exist $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME)\$(APP_PRODUCT_NAME) mkdir $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME)\$(APP_PRODUCT_NAME)
+ if not exist $(PROJECT_BUILD_PATH)\lib mkdir $(PROJECT_BUILD_PATH)\lib
+ if not exist $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME) mkdir $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)
+ if not exist $(PROJECT_BUILD_PATH)\bin mkdir $(PROJECT_BUILD_PATH)\bin
+ if not exist $(PROJECT_BUILD_PATH)\res mkdir $(PROJECT_BUILD_PATH)\res
+ if not exist $(PROJECT_BUILD_PATH)\res\drawable-ldpi mkdir $(PROJECT_BUILD_PATH)\res\drawable-ldpi
+ if not exist $(PROJECT_BUILD_PATH)\res\drawable-mdpi mkdir $(PROJECT_BUILD_PATH)\res\drawable-mdpi
+ if not exist $(PROJECT_BUILD_PATH)\res\drawable-hdpi mkdir $(PROJECT_BUILD_PATH)\res\drawable-hdpi
+ if not exist $(PROJECT_BUILD_PATH)\res\values mkdir $(PROJECT_BUILD_PATH)\res\values
+ if not exist $(PROJECT_BUILD_PATH)\assets mkdir $(PROJECT_BUILD_PATH)\assets
+ if not exist $(PROJECT_BUILD_PATH)\assets\$(PROJECT_RESOURCES_PATH) mkdir $(PROJECT_BUILD_PATH)\assets\$(PROJECT_RESOURCES_PATH)
+ if not exist $(PROJECT_BUILD_PATH)\obj\screens mkdir $(PROJECT_BUILD_PATH)\obj\screens
+ $(foreach dir, $(PROJECT_SOURCE_DIRS), $(call create_dir, $(dir)))
+
+define create_dir
+ if not exist $(PROJECT_BUILD_PATH)\obj\$(1) mkdir $(PROJECT_BUILD_PATH)\obj\$(1)
+endef
+
+# Copy required shared libs for integration into APK
+# NOTE: If using shared libs they are loaded by generated NativeLoader.java
+copy_project_required_libs:
+ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ copy /Y $(RAYLIB_LIB_PATH)\libraylib.so $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.so
+endif
+ifeq ($(RAYLIB_LIBTYPE),STATIC)
+ copy /Y $(RAYLIB_LIB_PATH)\libraylib.a $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.a
+endif
+
+# Copy project required resources: strings.xml, icon.png, assets
+# NOTE: Required strings.xml is generated and game resources are copied to assets folder
+# TODO: Review xcopy usage, it can not be found in some systems!
+copy_project_resources:
+ copy $(APP_ICON_LDPI) $(PROJECT_BUILD_PATH)\res\drawable-ldpi\icon.png /Y
+ copy $(APP_ICON_MDPI) $(PROJECT_BUILD_PATH)\res\drawable-mdpi\icon.png /Y
+ copy $(APP_ICON_HDPI) $(PROJECT_BUILD_PATH)\res\drawable-hdpi\icon.png /Y
+ @echo ^ > $(PROJECT_BUILD_PATH)/res/values/strings.xml
+ @echo ^^$(APP_LABEL_NAME)^^ >> $(PROJECT_BUILD_PATH)/res/values/strings.xml
+ if exist $(PROJECT_RESOURCES_PATH) C:\Windows\System32\xcopy $(PROJECT_RESOURCES_PATH) $(PROJECT_BUILD_PATH)\assets\$(PROJECT_RESOURCES_PATH) /Y /E /F
+
+# Generate NativeLoader.java to load required shared libraries
+# NOTE: Probably not the bet way to generate this file... but it works.
+generate_loader_script:
+ @echo package com.$(APP_COMPANY_NAME).$(APP_PRODUCT_NAME); > $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo. >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo public class NativeLoader extends android.app.NativeActivity { >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo static { >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ @echo System.loadLibrary("raylib"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+endif
+ @echo System.loadLibrary("$(PROJECT_LIBRARY_NAME)"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+
+# Generate AndroidManifest.xml with all the required options
+# NOTE: Probably not the bet way to generate this file... but it works.
+generate_android_manifest:
+ @echo ^ > $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo package="com.$(APP_COMPANY_NAME).$(APP_PRODUCT_NAME)" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:versionCode="$(APP_VERSION_CODE)" android:versionName="$(APP_VERSION_NAME)" ^> >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:configChanges="orientation|keyboardHidden|screenSize" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:screenOrientation="$(APP_SCREEN_ORIENTATION)" android:launchMode="singleTask" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:clearTaskOnLaunch="true"^> >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+
+# Generate storekey for APK signing: $(PROJECT_NAME).keystore
+# NOTE: Configure here your Distinguished Names (-dname) if required!
+generate_apk_keystore:
+ if not exist $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore $(JAVA_HOME)/bin/keytool -genkeypair -validity 1000 -dname "CN=$(APP_COMPANY_NAME),O=Android,C=ES" -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -alias $(PROJECT_NAME)Key -keyalg RSA
+
+# Config project package and resource using AndroidManifest.xml and res/values/strings.xml
+# NOTE: Generates resources file: src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/R.java
+config_project_package:
+ $(ANDROID_BUILD_TOOLS)/aapt package -f -m -S $(PROJECT_BUILD_PATH)/res -J $(PROJECT_BUILD_PATH)/src -M $(PROJECT_BUILD_PATH)/AndroidManifest.xml -I $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar
+
+# Compile native_app_glue code as static library: obj/libnative_app_glue.a
+compile_native_app_glue:
+ $(CC) -c $(RAYLIB_PATH)/src/external/android/native_app_glue/android_native_app_glue.c -o $(PROJECT_BUILD_PATH)/obj/native_app_glue.o $(CFLAGS)
+ $(AR) rcs $(PROJECT_BUILD_PATH)/obj/libnative_app_glue.a $(PROJECT_BUILD_PATH)/obj/native_app_glue.o
+
+# Compile project code into a shared library: lib/lib$(PROJECT_LIBRARY_NAME).so
+compile_project_code: $(OBJS)
+ $(CC) -o $(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(OBJS) -shared $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS)
+
+# Compile all .c files required into object (.o) files
+# NOTE: Those files will be linked into a shared library
+$(PROJECT_BUILD_PATH)/obj/%.o:%.c
+ $(CC) -c $^ -o $@ $(INCLUDE_PATHS) $(CFLAGS) --sysroot=$(ANDROID_TOOLCHAIN)/sysroot
+
+# Compile project .java code into .class (Java bytecode)
+compile_project_class:
+ $(JAVA_HOME)/bin/javac -verbose -source 1.7 -target 1.7 -d $(PROJECT_BUILD_PATH)/obj -bootclasspath $(JAVA_HOME)/jre/lib/rt.jar -classpath $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar;$(PROJECT_BUILD_PATH)/obj -sourcepath $(PROJECT_BUILD_PATH)/src $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/R.java $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+
+# Compile .class files into Dalvik executable bytecode (.dex)
+# NOTE: Since Android 5.0, Dalvik interpreter (JIT) has been replaced by ART (AOT)
+compile_project_class_dex:
+ $(ANDROID_BUILD_TOOLS)/dx --verbose --dex --output=$(PROJECT_BUILD_PATH)/bin/classes.dex $(PROJECT_BUILD_PATH)/obj
+
+# Create Android APK package: bin/$(PROJECT_NAME).unsigned.apk
+# NOTE: Requires compiled classes.dex and lib$(PROJECT_LIBRARY_NAME).so
+# NOTE: Use -A resources to define additional directory in which to find raw asset files
+create_project_apk_package:
+ $(ANDROID_BUILD_TOOLS)/aapt package -f -M $(PROJECT_BUILD_PATH)/AndroidManifest.xml -S $(PROJECT_BUILD_PATH)/res -A $(PROJECT_BUILD_PATH)/assets -I $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar -F $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_BUILD_PATH)/bin
+ cd $(PROJECT_BUILD_PATH) && $(ANDROID_BUILD_TOOLS)/aapt add bin/$(PROJECT_NAME).unsigned.apk lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(PROJECT_SHARED_LIBS)
+
+# Create signed APK package using generated Key: bin/$(PROJECT_NAME).signed.apk
+sign_project_apk_package:
+ $(JAVA_HOME)/bin/jarsigner -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -signedjar $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_NAME)Key
+
+# Create zip-aligned APK package: $(PROJECT_NAME).apk
+zipalign_project_apk_package:
+ $(ANDROID_BUILD_TOOLS)/zipalign -f 4 $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_NAME).apk
+
+# Install $(PROJECT_NAME).apk to default emulator/device
+# NOTE: Use -e (emulator) or -d (device) parameters if required
+install:
+ $(ANDROID_PLATFORM_TOOLS)/adb install --abi $(ANDROID_ARCH_NAME) -rds $(PROJECT_NAME).apk
+
+# Check supported ABI for the device (armeabi-v7a, arm64-v8a, x86, x86_64)
+check_device_abi:
+ $(ANDROID_PLATFORM_TOOLS)/adb shell getprop ro.product.cpu.abi
+
+# Monitorize output log coming from device, only raylib tag
+logcat:
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat -c
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat raylib:V *:S
+
+# Install and monitorize $(PROJECT_NAME).apk to default emulator/device
+deploy:
+ $(ANDROID_PLATFORM_TOOLS)/adb install -r $(PROJECT_NAME).apk
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat -c
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat raylib:V *:S
+
+#$(ANDROID_PLATFORM_TOOLS)/adb logcat *:W
+
+# Clean everything
+clean:
+ del $(PROJECT_BUILD_PATH)\* /f /s /q
+ rmdir $(PROJECT_BUILD_PATH) /s /q
+ @echo Cleaning done
diff --git a/projects/BMFontExtender/main.c b/projects/BMFontExtender/main.c
new file mode 100644
index 000000000..e9e674908
--- /dev/null
+++ b/projects/BMFontExtender/main.c
@@ -0,0 +1,94 @@
+/*******************************************************************************************
+*
+* raylib [core] example - Basic 3d example
+*
+* Welcome to raylib!
+*
+* To compile example, just press F5.
+* Note that compiled executable is placed in the same folder as .c file
+*
+* You can find all basic examples on C:\raylib\raylib\examples folder or
+* raylib official webpage: www.raylib.com
+*
+* Enjoy using raylib. :)
+*
+* This example has been created using raylib 1.0 (www.raylib.com)
+* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
+*
+* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_WEB)
+ #include
+#endif
+
+//----------------------------------------------------------------------------------
+// Main entry point
+//----------------------------------------------------------------------------------
+int main()
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 1200;
+ const int screenHeight = 900;
+
+ InitWindow(screenWidth, screenHeight, "raylib");
+
+ // Font
+ Font fontnice = LoadFont("Assets/font/elandnice.fnt");
+ Font fontstrawberry = LoadFont("Assets/font/gostrawberry.fnt");
+ Font fontgukdae = LoadFont("Assets/font/gukdae.fnt");
+
+ const char* beautifulhangul = "한글은 아름다워요. 예뻐요. 그림 같아요";
+ const char* hun[7];
+ hun[0] = "나랏말싸미 듕귁에 달아 문자와로 서로 사맛디 아니할새";
+ hun[1] = "이런 젼차로 어린백성이 니르고자 할배 있어도";
+ hun[2] = "마참내 그 뜻을 시려펴디 못할 놈이 하노라";
+ hun[3] = "내 이를 위하여 어여삐 여겨";
+ hun[4] = "새로 스물여덟자를 맹그나니";
+ hun[5] = "사람마다 해여 수비녀겨 날로 쓰매";
+ hun[6] = "편한케 하고져 할 따르미니라";
+ const char* author = "대한민국 변호사, 이동근이 만들었어요. 2023. 11. 2.";
+
+ //--------------------------------------------------------------------------------------
+
+#if defined(PLATFORM_WEB)
+ emscripten_set_main_loop(UpdateDrawFrame, 60, 1);
+#else
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ BeginDrawing();
+ ClearBackground(BLACK);
+ DrawTextEx(fontnice, beautifulhangul , (Vector2) { 100, 110 }, 48, 5, DARKGREEN);
+
+ for (int i =0; i < 7; i++)
+ {
+ Vector2 length = MeasureTextEx(fontstrawberry, hun[i], 36, 5); // font height is 36
+ Vector2 pos = { screenWidth / 2 - length.x / 2, (float)260 + (float)i * (float)40 }; // middle alignment
+ DrawTextEx(fontstrawberry, hun[i], pos, 36, 5, RAYWHITE );
+ }
+
+
+ DrawTextEx(fontgukdae, author, (Vector2) { 360, 800 }, 32, 5, VIOLET );
+ EndDrawing();
+ }
+#endif
+
+ UnloadFont(fontnice);
+ UnloadFont(fontstrawberry);
+ UnloadFont(fontgukdae);
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
diff --git a/projects/BMFontExtender/main.code-workspace b/projects/BMFontExtender/main.code-workspace
new file mode 100644
index 000000000..2c9896f4d
--- /dev/null
+++ b/projects/BMFontExtender/main.code-workspace
@@ -0,0 +1,19 @@
+{
+ "folders": [
+ {
+ "path": "."
+ },
+ {
+ "path": "../../../../LoadBMFontExtender/Assets"
+ }
+ ],
+ "settings": {
+ "files.associations": {
+ "raylib.h": "c",
+ "math.h": "c",
+ "blocks.h": "c",
+ "stdio.h": "c",
+ "*.m": "c"
+ }
+ }
+}
\ No newline at end of file
diff --git a/projects/BMFontExtender/resources/LICENSE b/projects/BMFontExtender/resources/LICENSE
new file mode 100644
index 000000000..ebde2c573
--- /dev/null
+++ b/projects/BMFontExtender/resources/LICENSE
@@ -0,0 +1 @@
+Assets license.
diff --git a/src/rtext.c b/src/rtext.c
index dd7c83f39..4aa8e14ac 100644
--- a/src/rtext.c
+++ b/src/rtext.c
@@ -126,6 +126,7 @@ static Font defaultFont = { 0 };
//----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_FNT)
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
+static Font LoadBMFontEx(const char *fileName); // Load a BMFont file (AngelCode font file)
#endif
static int textLineSpacing = 15; // Text vertical line spacing in pixels
@@ -332,7 +333,7 @@ Font LoadFont(const char *fileName)
else
#endif
#if defined(SUPPORT_FILEFORMAT_FNT)
- if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName);
+ if (IsFileExtension(fileName, ".fnt")) font = LoadBMFontEx(fileName);
else
#endif
{
@@ -2158,6 +2159,187 @@ static Font LoadBMFont(const char *fileName)
return font;
}
+
+// Extender of LoadBMFont. Load a BMFont file (AngelCode font file)
+// REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
+static Font LoadBMFontEX(const char *fileName)
+{
+ #define MAX_BUFFER_SIZE 256
+
+ Font font = { 0 };
+
+ char buffer[MAX_BUFFER_SIZE] = { 0 };
+ char *searchPoint = NULL;
+
+ int fontSize = 0;
+ int glyphCount = 0;
+
+ int imWidth = 0;
+ int imHeight = 0;
+ int totalPage = 1; // page variable
+ int curPage = 0;
+ char imFileName[10][129] = { 0 }; // up to ten png file.
+
+ int base = 0; // Useless data
+ int readBytes = 0; // Data bytes read
+ int readVars = 0; // Variables filled by sscanf()
+
+ char *fileText = LoadFileText(fileName);
+
+ if (fileText == NULL) return font;
+
+ char *fileTextPtr = fileText;
+
+ // NOTE: We skip first line, it contains no useful information
+ readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+ fileTextPtr += (readBytes + 1);
+
+ // Read line data
+ readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+ searchPoint = strstr(buffer, "lineHeight");
+ readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i pages=%i", &fontSize, &base, &imWidth, &imHeight, &totalPage);
+ fileTextPtr += (readBytes + 1);
+
+ if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed
+
+ for (int i = 0; i < totalPage; i++)
+ {
+ readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+ searchPoint = strstr(buffer, "file");
+ readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName[i]);
+ fileTextPtr += (readBytes + 1);
+
+ if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read
+ }
+
+ readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+ searchPoint = strstr(buffer, "count");
+ readVars = sscanf(searchPoint, "count=%i", &glyphCount);
+ fileTextPtr += (readBytes + 1);
+
+ if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read
+
+ // Compose correct path using route of .fnt file (fileName) and imFileName
+ char **imPath;
+ char *lastSlash = NULL;
+ imPath = malloc(sizeof(char) * 100); // imPath Initialization
+
+ for (int i = 0; i< totalPage; i++)
+ {
+ lastSlash = strrchr(fileName, '/');
+ if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
+
+ if (lastSlash != NULL)
+ {
+ // NOTE: We need some extra space to avoid memory corruption on next allocations!
+ imPath[i] = (char *)RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName[i]) + 4, 1);
+ memcpy(imPath[i], fileName, TextLength(fileName) - TextLength(lastSlash) + 1);
+ memcpy(imPath[i] + TextLength(fileName) - TextLength(lastSlash) + 1, imFileName[i], TextLength(imFileName[i]));
+ }
+ else imPath[i] = imFileName[i];
+
+ TRACELOGD(" > Image loading path: %s", imPath[i]);
+ }
+
+ Image imFont[totalPage];
+
+ for (int i = 0; i < totalPage; i++)
+ {
+ imFont[i] = LoadImage(imPath[i]);
+
+ if (imFont[i].format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
+ {
+ // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
+ Image imFontAlpha = {
+ .data = RL_CALLOC(imFont[i].width*imFont[i].height, 2),
+ .width = imFont[i].width,
+ .height = imFont[i].height,
+ .mipmaps = 1,
+ .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA
+ };
+
+ for (int p = 0, pi = 0; p < (imFont[i].width*imFont[i].height*2); p += 2, pi++)
+ {
+ ((unsigned char *)(imFontAlpha.data))[p] = 0xff;
+ ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont[i].data)[pi];
+ }
+
+ UnloadImage(imFont[i]);
+ imFont[i] = imFontAlpha;
+ }
+
+ if (lastSlash != NULL) RL_FREE(imPath[i]);
+ }
+
+ // Resize and ReDraw Font Image
+ Image fullFont;
+ fullFont = imFont[0];
+
+ // If multiple atlas, then merge atlas
+ if (totalPage > 1)
+ {
+ ImageResizeCanvas(&fullFont, imWidth, imHeight * totalPage, 0, 0, BLACK);
+
+ for (int index = 1; index <= totalPage; index++)
+ {
+ Rectangle srcRec = { 0.0f, 0.0f, (float)imWidth, (float)imHeight};
+ Rectangle destRec = { 0.0f, (float)imHeight * (float)index, (float)imWidth, (float)imHeight};
+ ImageDraw(&fullFont, imFont[index], srcRec, destRec, WHITE);
+ }
+ }
+
+ font.texture = LoadTextureFromImage(fullFont);
+
+
+
+ // Fill font characters info data
+ font.baseSize = fontSize;
+ font.glyphCount = glyphCount;
+ font.glyphPadding = 0;
+ font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
+ font.recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
+
+ int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX, pageID;
+
+ for (int i = 0; i < glyphCount; i++)
+ {
+ readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+ readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i page=%i",
+ &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX, &pageID);
+ fileTextPtr += (readBytes + 1);
+
+ if (readVars == 9) // Make sure all char data has been properly read
+ {
+ // Get character rectangle in the font atlas texture
+ font.recs[i] = (Rectangle){ (float)charX, (float)charY + (float)imHeight * pageID, (float)charWidth, (float)charHeight };
+
+ // Save data properly in sprite font
+ font.glyphs[i].value = charId;
+ font.glyphs[i].offsetX = charOffsetX;
+ font.glyphs[i].offsetY = charOffsetY;
+ font.glyphs[i].advanceX = charAdvanceX;
+
+ // Fill character image data from imFont data
+ font.glyphs[i].image = ImageFromImage(fullFont, font.recs[i]);
+ }
+ else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName);
+ }
+
+ UnloadImage(fullFont);
+ UnloadFileText(fileText);
+
+ if (font.texture.id == 0)
+ {
+ UnloadFont(font);
+ font = GetFontDefault();
+ TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load texture, reverted to default font", fileName);
+ }
+ else TRACELOG(LOG_INFO, "FONT: [%s] Font loaded successfully (%i glyphs)", fileName, font.glyphCount);
+
+ free(imPath);
+ return font;
+}
+
#endif
#endif // SUPPORT_MODULE_RTEXT