Add flexible module loading and complete file embedding

- Support any folder structure (no hard-coded folders)
 - Embed all file types recursively from any folder
 - Fix require() dot-to-slash conversion for embedded modules
 - Clean build folder for fresh builds every time
 - Generate empty headers for Lua-only projects

 Backward compatible with existing projects.
This commit is contained in:
2025-11-10 01:58:00 +05:30
parent 8c9367f368
commit d9d1a8a51e
9 changed files with 283 additions and 206 deletions

View File

@@ -97,15 +97,15 @@ if( WIN32 )
list( APPEND SOURCES ${CMAKE_SOURCE_DIR}/resources.rc ) list( APPEND SOURCES ${CMAKE_SOURCE_DIR}/resources.rc )
endif() endif()
# Embed Lua files if EMBED_MAIN is ON # Embed Lua files if EMBED_MAIN is ON (recursively from all subdirectories)
if( EMBED_MAIN ) if( EMBED_MAIN )
file( GLOB LUA_FILES "${CMAKE_CURRENT_BINARY_DIR}/*.lua" ) file( GLOB_RECURSE LUA_FILES "${CMAKE_CURRENT_BINARY_DIR}/*.lua" )
if( LUA_FILES ) if( LUA_FILES )
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/embed_lua.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h ${LUA_FILES} COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/embed_lua.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h ${LUA_FILES}
DEPENDS ${LUA_FILES} DEPENDS ${LUA_FILES}
COMMENT "Embedding Lua files into executable..." COMMENT "Embedding Lua files from all subdirectories into executable..."
) )
list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h ) list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_MAIN" ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_MAIN" )
@@ -114,21 +114,60 @@ if( EMBED_MAIN )
endif() endif()
endif() endif()
# Embed asset files if EMBED_ASSETS is ON # Embed all non-Lua data files if EMBED_ASSETS is ON (from all subdirectories except CMake dirs)
# Always create embedded_assets.h to prevent compilation errors
if( EMBED_ASSETS ) if( EMBED_ASSETS )
file( GLOB_RECURSE ASSET_FILES "${CMAKE_CURRENT_BINARY_DIR}/assets/*" ) # Find all non-Lua files recursively, excluding build system files
file( GLOB_RECURSE ALL_DATA_FILES
"${CMAKE_CURRENT_BINARY_DIR}/*"
)
# Filter out unwanted files
set( ASSET_FILES "" )
foreach( FILE_PATH ${ALL_DATA_FILES} )
# Exclude: .lua files (handled separately), build artifacts, and CMake files
if( NOT FILE_PATH MATCHES "\\.lua$"
AND NOT FILE_PATH MATCHES "CMakeFiles"
AND NOT FILE_PATH MATCHES "\\.cmake$"
AND NOT FILE_PATH MATCHES "CMakeCache"
AND NOT FILE_PATH MATCHES "Makefile$"
AND NOT FILE_PATH MATCHES "\\.a$"
AND NOT FILE_PATH MATCHES "\\.o$"
AND NOT FILE_PATH MATCHES "embedded_.*\\.h$"
AND NOT FILE_PATH MATCHES "\\.exe$"
AND NOT FILE_PATH MATCHES "ReiLua$" )
list( APPEND ASSET_FILES ${FILE_PATH} )
endif()
endforeach()
if( ASSET_FILES ) if( ASSET_FILES )
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/embed_assets.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h ${ASSET_FILES} COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/embed_assets.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h ${ASSET_FILES}
DEPENDS ${ASSET_FILES} DEPENDS ${ASSET_FILES}
COMMENT "Embedding asset files into executable..." COMMENT "Embedding data files from all subdirectories into executable..."
)
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_ASSETS" )
message( STATUS "Embedding ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h with asset files" )
else()
# Create empty embedded_assets.h to prevent compilation errors
message( STATUS "EMBED_ASSETS is ON but no data files found, creating empty embedded_assets.h" )
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/create_empty_assets.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h
COMMENT "Creating empty embedded_assets.h (no data files to embed)"
)
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_ASSETS" )
endif()
list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h )
else()
# EMBED_ASSETS is OFF - create empty header for compatibility
message( STATUS "EMBED_ASSETS is OFF, creating empty embedded_assets.h" )
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/create_empty_assets.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h
COMMENT "Creating empty embedded_assets.h (EMBED_ASSETS is OFF)"
) )
list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h ) list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_ASSETS" )
else()
message( WARNING "EMBED_ASSETS is ON but no files found in build/assets/ directory!" )
endif()
endif() endif()
add_executable( ${PROJECT_NAME} ${SOURCES} ) add_executable( ${PROJECT_NAME} ${SOURCES} )

View File

@@ -18,60 +18,25 @@ if errorlevel 1 (
exit /b 1 exit /b 1
) )
REM Clean old embedded files (important for dev builds!) REM ALWAYS clean build folder for fresh build
echo Cleaning old embedded files... echo Cleaning build directory for fresh build...
del /Q embedded_main.h embedded_assets.h 2>nul del /Q /S * >nul 2>&1
for /d %%p in (*) do rmdir "%%p" /s /q >nul 2>&1
echo * Build directory cleaned
echo.
REM Warn about Lua files in build directory REM Configure
dir /b *.lua >nul 2>&1
if not errorlevel 1 (
echo.
echo WARNING: Found Lua files in build directory!
echo Development builds should load from file system, not embed.
echo.
dir /b *.lua
echo.
set /p REMOVE="Remove these files from build directory? (Y/n): "
if /i not "%REMOVE%"=="n" (
del /Q *.lua
echo Lua files removed.
)
echo.
)
REM Warn about assets folder in build directory
if exist "assets" (
echo.
echo WARNING: Found assets folder in build directory!
echo Development builds should load from file system, not embed.
echo.
set /p REMOVE="Remove assets folder from build directory? (Y/n): "
if /i not "%REMOVE%"=="n" (
rmdir /S /Q assets
echo Assets folder removed.
)
echo.
)
REM Clean old configuration if requested
if "%1"=="clean" (
echo Cleaning build directory...
del /Q CMakeCache.txt *.o *.a 2>nul
rmdir /S /Q CMakeFiles 2>nul
echo Clean complete!
echo.
)
REM Configure with MinGW
echo Configuring CMake for development... echo Configuring CMake for development...
cmake -G "MinGW Makefiles" .. cmake -G "MinGW Makefiles" ..
if errorlevel 1 ( if errorlevel 1 (
echo. echo.
echo ERROR: CMake configuration failed! echo ERROR: CMake configuration failed!
pause
exit /b 1 exit /b 1
) )
REM Build
echo. echo.
echo Building ReiLua... echo Building ReiLua...
mingw32-make mingw32-make
@@ -79,6 +44,7 @@ mingw32-make
if errorlevel 1 ( if errorlevel 1 (
echo. echo.
echo ERROR: Build failed! echo ERROR: Build failed!
pause
exit /b 1 exit /b 1
) )

View File

@@ -62,49 +62,15 @@ fi
mkdir -p build mkdir -p build
cd build || exit 1 cd build || exit 1
# Clean old embedded files (important for dev builds!) # ALWAYS clean build folder for fresh build
echo "Cleaning old embedded files..." echo "Cleaning build directory for fresh build..."
rm -f embedded_main.h embedded_assets.h rm -rf ./* 2>/dev/null
echo "✓ Build directory cleaned"
# Warn about Lua files in build directory
LUA_COUNT=$(ls *.lua 2>/dev/null | wc -l)
if [ "$LUA_COUNT" -gt 0 ]; then
echo "" echo ""
echo "WARNING: Found Lua files in build directory!"
echo "Development builds should load from file system, not embed."
echo ""
ls -1 *.lua
echo ""
read -p "Remove these files from build directory? (Y/n): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
rm -f *.lua
echo "Lua files removed."
fi
echo ""
fi
# Warn about assets folder in build directory
if [ -d "assets" ]; then
echo ""
echo "WARNING: Found assets folder in build directory!"
echo "Development builds should load from file system, not embed."
echo ""
read -p "Remove assets folder from build directory? (Y/n): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
rm -rf assets
echo "Assets folder removed."
fi
echo ""
fi
# Clean old configuration if requested # Clean old configuration if requested
if [ "$1" == "clean" ]; then if [ "$1" == "clean" ]; then
echo "Cleaning build directory..." echo "Extra clean flag detected (already cleaned)"
rm -rf CMakeCache.txt CMakeFiles/ *.o *.a
echo "Clean complete!"
echo ""
fi fi
# Detect platform and set appropriate generator # Detect platform and set appropriate generator

View File

@@ -24,21 +24,48 @@ if errorlevel 1 (
exit /b 1 exit /b 1
) )
REM ALWAYS clean build folder for fresh build
echo Cleaning build directory for fresh build...
del /Q /S * >nul 2>&1
for /d %%p in (*) do rmdir "%%p" /s /q >nul 2>&1
echo * Build directory cleaned
echo.
REM Clean old embedded files REM Clean old embedded files
echo Cleaning old embedded files... echo Ready for fresh build...
del /Q embedded_main.h embedded_assets.h 2>nul del /Q embedded_main.h embedded_assets.h 2>nul
REM Check for Lua files REM Auto-copy from game folder if it exists
echo. echo.
if exist "..\game" (
echo Found game/ folder - auto-copying ALL contents to build...
REM Copy all files from game folder recursively, excluding LSP files
xcopy /E /I /Y /EXCLUDE:..\game\ReiLua_API.lua+..\game\.luarc.json "..\game\*" . >nul 2>&1
if exist "..\game\ReiLua_API.lua" del /Q "ReiLua_API.lua" 2>nul
if exist "..\game\.luarc.json" del /Q ".luarc.json" 2>nul
echo * Copied ALL game files and folders
echo * All folder structures preserved ^(user-created folders included^)
echo.
)
REM Check for Lua files
echo Checking for Lua files... echo Checking for Lua files...
dir /b *.lua >nul 2>&1 dir /b *.lua >nul 2>&1
if errorlevel 1 ( if errorlevel 1 (
echo. echo.
echo WARNING: No Lua files found in build directory! echo WARNING: No Lua files found in build directory!
echo. echo.
echo Please copy your Lua files: if exist "..\game" (
echo No Lua files found in game/ folder.
echo Add your main.lua to game/ folder and try again.
) else (
echo Tip: Create a game/ folder in project root and add main.lua there.
echo Or manually copy files:
echo cd build echo cd build
echo copy ..\your_game\*.lua . echo copy ..\your_game\*.lua .
)
echo. echo.
set /p CONTINUE="Do you want to continue anyway? (y/N): " set /p CONTINUE="Do you want to continue anyway? (y/N): "
if /i not "%CONTINUE%"=="y" exit /b 1 if /i not "%CONTINUE%"=="y" exit /b 1
@@ -47,24 +74,22 @@ if errorlevel 1 (
dir /b *.lua dir /b *.lua
) )
REM Check for assets folder REM Check for non-Lua data files (any folder, any file type)
echo. echo.
echo Checking for assets... echo Checking for data files to embed...
if not exist "assets" ( set DATA_COUNT=0
echo. for /r %%f in (*) do (
echo WARNING: No assets folder found! echo %%~nxf | findstr /i /v ".lua .exe .o .a CMake Makefile" >nul
echo. if not errorlevel 1 set /a DATA_COUNT+=1
echo To embed assets, create the folder and copy files: )
echo cd build
echo mkdir assets if %DATA_COUNT% GTR 0 (
echo copy ..\your_game\assets\* assets\ echo Found data files to embed
echo. echo ^(includes: images, sounds, config, data, and any other files^)
set /p CONTINUE="Do you want to continue without assets? (y/N): "
if /i not "%CONTINUE%"=="y" exit /b 1
set EMBED_ASSETS=OFF
) else (
echo Found assets folder
set EMBED_ASSETS=ON set EMBED_ASSETS=ON
) else (
echo No non-Lua files found ^(only Lua code will be embedded^)
set EMBED_ASSETS=OFF
) )
echo. echo.
@@ -72,7 +97,7 @@ echo ================================
echo Build Configuration echo Build Configuration
echo ================================ echo ================================
echo Lua Embedding: ON echo Lua Embedding: ON
echo Asset Embedding: %EMBED_ASSETS% echo Data Embedding: %EMBED_ASSETS%
echo Build Type: Release echo Build Type: Release
echo ================================ echo ================================
echo. echo.

View File

@@ -68,38 +68,50 @@ fi
mkdir -p build mkdir -p build
cd build || exit 1 cd build || exit 1
# ALWAYS clean build folder for fresh build
echo "Cleaning build directory for fresh build..."
rm -rf ./* 2>/dev/null
echo "✓ Build directory cleaned"
echo ""
# Clean old embedded files # Clean old embedded files
echo "Cleaning old embedded files..." echo "Ready for fresh build..."
rm -f embedded_main.h embedded_assets.h rm -f embedded_main.h embedded_assets.h 2>/dev/null
# Auto-copy from game folder if it exists # Auto-copy from game folder if it exists
echo "" echo ""
if [ -d "../game" ]; then if [ -d "../game" ]; then
echo "Found game/ folder - auto-copying contents to build..." echo "Found game/ folder - auto-copying ALL contents to build..."
# Copy all Lua files from game folder EXCEPT ReiLua_API.lua # Copy everything from game folder recursively, excluding:
if ls ../game/*.lua 1> /dev/null 2>&1; then # - ReiLua_API.lua (LSP only)
for lua_file in ../game/*.lua; do # - .luarc.json (LSP config)
filename=$(basename "$lua_file") # - .DS_Store (macOS)
if [ "$filename" != "ReiLua_API.lua" ]; then # - Hidden files starting with . (except .gitkeep if present)
cp "$lua_file" .
fi # Use rsync if available for better copying, otherwise use cp
done if command -v rsync &> /dev/null; then
LUA_COUNT=$(ls *.lua 2>/dev/null | wc -l) rsync -av --exclude='ReiLua_API.lua' --exclude='.luarc.json' --exclude='.DS_Store' --exclude='.*' --include='.gitkeep' ../game/ . 2>/dev/null
echo " ✓ Copied $LUA_COUNT Lua file(s)"
else else
echo " ⚠ No Lua files found in game/" # Fallback to find + cp - Copy ALL files and directories
(cd ../game && find . -type f \
! -name 'ReiLua_API.lua' \
! -name '.luarc.json' \
! -name '.DS_Store' \
! -path '*/\.*' -o -name '.gitkeep' \
-exec sh -c 'mkdir -p "../build/$(dirname "{}")" && cp -p "{}" "../build/{}"' \; 2>/dev/null)
fi fi
# Copy assets folder if it exists # Count what was copied
if [ -d "../game/assets" ]; then LUA_COUNT=$(find . -maxdepth 10 -name "*.lua" -type f 2>/dev/null | wc -l)
rm -rf assets ASSET_COUNT=$(find assets -type f 2>/dev/null | wc -l || echo "0")
cp -r ../game/assets . TOTAL_FILES=$(find . -type f ! -path './CMakeFiles/*' ! -path './.cmake/*' ! -name 'CMake*' ! -name '*.a' ! -name '*.o' 2>/dev/null | wc -l)
ASSET_COUNT=$(find assets -type f 2>/dev/null | wc -l)
echo " ✓ Copied assets/ ($ASSET_COUNT files)" echo " ✓ Copied ALL game files and folders:"
else echo " - $LUA_COUNT Lua file(s) (including all subdirectories)"
echo " No assets folder in game/" echo " - $ASSET_COUNT Asset file(s) (if assets folder exists)"
fi echo " - $TOTAL_FILES total file(s)"
echo " - All folder structures preserved (user-created folders included)"
echo "" echo ""
fi fi
@@ -131,34 +143,18 @@ else
ls -1 *.lua ls -1 *.lua
fi fi
# Check for assets folder # Check for non-Lua data files (any folder, any file type)
echo "" echo ""
echo "Checking for assets..." echo "Checking for data files to embed..."
if [ ! -d "assets" ]; then NON_LUA_FILES=$(find . -type f ! -name "*.lua" ! -path "./CMakeFiles/*" ! -path "./.cmake/*" ! -name "CMake*" ! -name "Makefile*" ! -name "*.o" ! -name "*.a" 2>/dev/null | wc -l)
echo ""
echo "WARNING: No assets folder found!" if [ "$NON_LUA_FILES" -gt 0 ]; then
echo "" echo "Found $NON_LUA_FILES non-Lua file(s) to embed"
if [ -d "../game" ]; then echo " (includes: images, sounds, config, data, and any other files)"
echo "No assets found in game/assets/ folder."
echo "Add assets to game/assets/ if you need them embedded."
else
echo "Tip: Create game/assets/ in project root for auto-copy."
echo "Or manually:"
echo " cd build"
echo " mkdir assets"
echo " cp ../your_game/assets/* assets/"
fi
echo ""
read -p "Do you want to continue without assets? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
EMBED_ASSETS="OFF"
else
ASSET_FILES=$(find assets -type f 2>/dev/null | wc -l)
echo "Found $ASSET_FILES asset file(s) in assets folder"
EMBED_ASSETS="ON" EMBED_ASSETS="ON"
else
echo "No non-Lua files found (only Lua code will be embedded)"
EMBED_ASSETS="OFF"
fi fi
echo "" echo ""
@@ -166,7 +162,7 @@ echo "================================"
echo "Build Configuration" echo "Build Configuration"
echo "================================" echo "================================"
echo "Lua Embedding: ON" echo "Lua Embedding: ON"
echo "Asset Embedding: $EMBED_ASSETS" echo "Data Embedding: $EMBED_ASSETS"
echo "Build Type: Release" echo "Build Type: Release"
echo "================================" echo "================================"
echo "" echo ""

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env python3
"""
Create an empty embedded_assets.h file for compatibility.
Usage: python create_empty_assets.py <output.h>
"""
import sys
if __name__ == '__main__':
if len(sys.argv) < 2:
print('Usage: python create_empty_assets.py <output.h>')
sys.exit(1)
output_file = sys.argv[1]
with open(output_file, 'w') as f:
f.write('#ifndef EMBEDDED_ASSETS_H\n')
f.write('#define EMBEDDED_ASSETS_H\n')
f.write('/* No assets to embed */\n')
f.write('typedef struct { const char* name; const unsigned char* data; unsigned int size; } EmbeddedAsset;\n')
f.write('static const EmbeddedAsset embedded_assets[] = {};\n')
f.write('static const int embedded_asset_count = 0;\n')
f.write('#endif\n')
print(f'Created empty {output_file}')

View File

@@ -40,17 +40,18 @@ def embed_files(output_file, input_files):
data = inf.read() data = inf.read()
var_name = sanitize_name(input_file) var_name = sanitize_name(input_file)
# Extract relative path from 'assets/' onwards if present # Extract relative path from build directory
if 'assets' in input_file.replace('\\', '/'): relative_name = input_file
parts = input_file.replace('\\', '/').split('assets/') for prefix in ['build/', 'build\\']:
if prefix in input_file:
parts = input_file.split(prefix, 1)
if len(parts) > 1: if len(parts) > 1:
relative_name = 'assets/' + parts[-1] relative_name = parts[1]
else: break
relative_name = os.path.basename(input_file) # Normalize path separators
else: relative_name = relative_name.replace('\\', '/')
relative_name = os.path.basename(input_file)
f.write(f'/* Embedded asset: {input_file} ({len(data)} bytes) */\n') f.write(f'/* Embedded file: {relative_name} ({len(data)} bytes) */\n')
f.write(f'static const unsigned char embedded_asset_{idx}_{var_name}[] = {{\n') f.write(f'static const unsigned char embedded_asset_{idx}_{var_name}[] = {{\n')
for i, byte in enumerate(data): for i, byte in enumerate(data):
@@ -78,15 +79,16 @@ def embed_files(output_file, input_files):
f.write('static const EmbeddedAsset embedded_assets[] = {\n') f.write('static const EmbeddedAsset embedded_assets[] = {\n')
for idx, input_file in enumerate(input_files): for idx, input_file in enumerate(input_files):
var_name = sanitize_name(input_file) var_name = sanitize_name(input_file)
# Extract relative path from 'assets/' onwards if present # Extract relative path from build directory
if 'assets' in input_file.replace('\\', '/'): relative_name = input_file
parts = input_file.replace('\\', '/').split('assets/') for prefix in ['build/', 'build\\']:
if prefix in input_file:
parts = input_file.split(prefix, 1)
if len(parts) > 1: if len(parts) > 1:
relative_name = 'assets/' + parts[-1] relative_name = parts[1]
else: break
relative_name = os.path.basename(input_file) # Normalize path separators
else: relative_name = relative_name.replace('\\', '/')
relative_name = os.path.basename(input_file)
f.write(f' {{ "{relative_name}", embedded_asset_{idx}_{var_name}, embedded_asset_{idx}_{var_name}_len }},\n') f.write(f' {{ "{relative_name}", embedded_asset_{idx}_{var_name}, embedded_asset_{idx}_{var_name}_len }},\n')
f.write('};\n\n') f.write('};\n\n')

View File

@@ -27,7 +27,17 @@ def embed_files(output_file, input_files):
data = inf.read() data = inf.read()
var_name = sanitize_name(input_file) var_name = sanitize_name(input_file)
f.write(f'/* Embedded file: {input_file} */\n') # Get relative path for better module loading
# Try to extract path relative to common directories
relative_name = input_file
for prefix in ['build/', 'build\\']:
if prefix in input_file:
parts = input_file.split(prefix, 1)
if len(parts) > 1:
relative_name = parts[1]
break
f.write(f'/* Embedded file: {relative_name} */\n')
f.write(f'static const unsigned char embedded_lua_{idx}_{var_name}[] = {{\n') f.write(f'static const unsigned char embedded_lua_{idx}_{var_name}[] = {{\n')
for i, byte in enumerate(data): for i, byte in enumerate(data):
@@ -55,18 +65,32 @@ def embed_files(output_file, input_files):
f.write('static const EmbeddedLuaFile embedded_lua_files[] = {\n') f.write('static const EmbeddedLuaFile embedded_lua_files[] = {\n')
for idx, input_file in enumerate(input_files): for idx, input_file in enumerate(input_files):
var_name = sanitize_name(input_file) var_name = sanitize_name(input_file)
# Store both original filename and basename for require compatibility # Store relative path for proper require() support
basename = os.path.basename(input_file) relative_name = input_file
f.write(f' {{ "{basename}", embedded_lua_{idx}_{var_name}, embedded_lua_{idx}_{var_name}_len }},\n') for prefix in ['build/', 'build\\']:
if prefix in input_file:
parts = input_file.split(prefix, 1)
if len(parts) > 1:
relative_name = parts[1]
break
# Normalize path separators
relative_name = relative_name.replace('\\', '/')
f.write(f' {{ "{relative_name}", embedded_lua_{idx}_{var_name}, embedded_lua_{idx}_{var_name}_len }},\n')
f.write('};\n\n') f.write('};\n\n')
f.write(f'static const int embedded_lua_file_count = {len(input_files)};\n\n') f.write(f'static const int embedded_lua_file_count = {len(input_files)};\n\n')
# Main entry point (first file) # Main entry point (first file with 'main.lua' in name, or first file)
var_name = sanitize_name(input_files[0]) main_idx = 0
for idx, input_file in enumerate(input_files):
if 'main.lua' in input_file.lower():
main_idx = idx
break
var_name = sanitize_name(input_files[main_idx])
f.write('/* Main entry point */\n') f.write('/* Main entry point */\n')
f.write(f'#define embedded_main_lua embedded_lua_0_{var_name}\n') f.write(f'#define embedded_main_lua embedded_lua_{main_idx}_{var_name}\n')
f.write(f'#define embedded_main_lua_len embedded_lua_0_{var_name}_len\n\n') f.write(f'#define embedded_main_lua_len embedded_lua_{main_idx}_{var_name}_len\n\n')
f.write('#endif /* EMBEDDED_MAIN_H */\n') f.write('#endif /* EMBEDDED_MAIN_H */\n')

View File

@@ -124,16 +124,31 @@ static int embedded_lua_loader( lua_State* L ) {
const char* name = lua_tostring( L, 1 ); const char* name = lua_tostring( L, 1 );
if ( name == NULL ) return 0; if ( name == NULL ) return 0;
/* Convert module name dots to slashes (e.g., "lib.gamestate" -> "lib/gamestate") */
char converted_name[512];
strncpy( converted_name, name, sizeof(converted_name) - 1 );
converted_name[sizeof(converted_name) - 1] = '\0';
for ( char* p = converted_name; *p; p++ ) {
if ( *p == '.' ) *p = '/';
}
/* Try both converted name and original name */
const char* names_to_try[] = { converted_name, name };
for ( int n = 0; n < 2; n++ ) {
const char* try_name = names_to_try[n];
for ( int i = 0; i < embedded_lua_file_count; i++ ) { for ( int i = 0; i < embedded_lua_file_count; i++ ) {
const EmbeddedLuaFile* file = &embedded_lua_files[i]; const EmbeddedLuaFile* file = &embedded_lua_files[i];
const char* basename = file->name; const char* basename = file->name;
size_t name_len = strlen( name ); size_t name_len = strlen( try_name );
size_t base_len = strlen( basename ); size_t base_len = strlen( basename );
if ( strcmp( basename, name ) == 0 || /* Match exactly, or match without .lua extension */
if ( strcmp( basename, try_name ) == 0 ||
( base_len > 4 && strcmp( basename + base_len - 4, ".lua" ) == 0 && ( base_len > 4 && strcmp( basename + base_len - 4, ".lua" ) == 0 &&
strncmp( basename, name, base_len - 4 ) == 0 && name_len == base_len - 4 ) ) { strncmp( basename, try_name, base_len - 4 ) == 0 && name_len == base_len - 4 ) ) {
if ( luaL_loadbuffer( L, (const char*)file->data, file->size, file->name ) == 0 ) { if ( luaL_loadbuffer( L, (const char*)file->data, file->size, file->name ) == 0 ) {
return 1; return 1;
@@ -144,6 +159,7 @@ static int embedded_lua_loader( lua_State* L ) {
} }
} }
} }
}
lua_pushfstring( L, "\n\tno embedded file '%s'", name ); lua_pushfstring( L, "\n\tno embedded file '%s'", name );
return 1; return 1;
@@ -1530,6 +1546,25 @@ bool luaInit( int argn, const char** argc ) {
TraceLog( LOG_WARNING, "%s", "Failed to init Lua" ); TraceLog( LOG_WARNING, "%s", "Failed to init Lua" );
return false; return false;
} }
/* Configure package.path to include basePath and all subdirectories */
if ( state->basePath != NULL && strlen( state->basePath ) > 0 ) {
lua_getglobal( L, "package" );
lua_getfield( L, -1, "path" );
const char* currentPath = lua_tostring( L, -1 );
lua_pop( L, 1 );
/* Add basePath and recursive subdirectory patterns for flexible module loading */
char newPath[STRING_LEN * 4];
snprintf( newPath, sizeof(newPath),
"%s?.lua;%s?/init.lua;%s?/?.lua;%s?/?/init.lua;%s",
state->basePath, state->basePath, state->basePath, state->basePath, currentPath );
lua_pushstring( L, newPath );
lua_setfield( L, -2, "path" );
lua_pop( L, 1 );
}
/* Define object types. */ /* Define object types. */
defineBuffer(); defineBuffer();
defineImage(); defineImage();