Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 38 additions & 51 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,6 @@ endfunction()
ensure_stb_header(stb_image.h)
ensure_stb_header(stb_image_write.h)

add_library(spratcore STATIC
src/core/cli_parse.cpp
src/core/layout_parser.cpp
src/core/output_pattern.cpp
src/core/i18n.cpp
src/core/stb_impl.cpp
src/commands/spratlayout_command.cpp
src/commands/spratpack_command.cpp
src/commands/spratconvert_command.cpp
src/commands/spratframes_command.cpp
src/commands/spratunpack_command.cpp
)
target_include_directories(spratcore PUBLIC src)
target_include_directories(spratcore SYSTEM PRIVATE ${STB_DIR})
target_include_directories(spratcore PRIVATE ${LIBARCHIVE_INCLUDE_DIRS})

option(SPRAT_ENABLE_NLS "Enable gettext-based translations when available" ON)
option(SPRAT_REQUIRE_GETTEXT "Fail configure if gettext support is requested but unavailable" OFF)

Expand All @@ -143,12 +127,11 @@ if(SPRAT_ENABLE_NLS)
find_package(Intl QUIET)
if(Intl_FOUND)
set(SPRAT_GETTEXT_AVAILABLE TRUE)
target_compile_definitions(spratcore PUBLIC SPRAT_ENABLE_GETTEXT)
if(TARGET Intl::Intl)
target_link_libraries(spratcore PUBLIC Intl::Intl)
set(SPRAT_INTL_LIBRARIES Intl::Intl)
else()
target_link_libraries(spratcore PUBLIC ${Intl_LIBRARIES})
target_include_directories(spratcore PUBLIC ${Intl_INCLUDE_DIRS})
set(SPRAT_INTL_LIBRARIES ${Intl_LIBRARIES})
set(SPRAT_INTL_INCLUDE_DIRS ${Intl_INCLUDE_DIRS})
endif()
message(STATUS "gettext support enabled (via Intl)")
else()
Expand All @@ -158,13 +141,11 @@ if(SPRAT_ENABLE_NLS)
# Some platforms provide libintl through gettext
if(TARGET Gettext::Gettext)
set(SPRAT_GETTEXT_AVAILABLE TRUE)
target_compile_definitions(spratcore PUBLIC SPRAT_ENABLE_GETTEXT)
target_link_libraries(spratcore PUBLIC Gettext::Gettext)
set(SPRAT_INTL_LIBRARIES Gettext::Gettext)
message(STATUS "gettext support enabled (via Gettext target)")
elseif(GETTEXT_LIBRARIES)
set(SPRAT_GETTEXT_AVAILABLE TRUE)
target_compile_definitions(spratcore PUBLIC SPRAT_ENABLE_GETTEXT)
target_link_libraries(spratcore PUBLIC ${GETTEXT_LIBRARIES})
set(SPRAT_INTL_LIBRARIES ${GETTEXT_LIBRARIES})
message(STATUS "gettext support enabled (via GETTEXT_LIBRARIES)")
endif()
endif()
Expand All @@ -176,11 +157,8 @@ if(SPRAT_ENABLE_NLS)
message(STATUS "gettext not found; translations will fallback to source strings")
endif()
endif()
target_compile_definitions(spratcore PUBLIC SPRAT_LOCALE_DIR="${CMAKE_INSTALL_FULL_LOCALEDIR}")

# Binaries
# For Windows cross-compilation, PkgConfig often leaks host paths.
# We prefer find_package or explicit paths.
# LibArchive detection
if(TARGET LibArchive::LibArchive)
set(LIBARCHIVE_LIBRARIES LibArchive::LibArchive)
set(LIBARCHIVE_FOUND TRUE)
Expand All @@ -193,6 +171,7 @@ elseif(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Windows" OR EMSCRIPTEN)
set(LIBARCHIVE_LIBRARIES LibArchive::LibArchive)
if(NOT LIBARCHIVE_INCLUDE_DIRS)
find_path(LIBARCHIVE_INCLUDE_DIR NAMES archive.h)
set(LIBARCHIVE_INCLUDE_DIRS ${LIBARCHIVE_INCLUDE_DIR})
else()
set(LIBARCHIVE_INCLUDE_DIR ${LIBARCHIVE_INCLUDE_DIRS})
endif()
Expand Down Expand Up @@ -220,6 +199,7 @@ else()
pkg_check_modules(LIBARCHIVE REQUIRED libarchive)
endif()

# Zopfli detection
if(PkgConfig_FOUND)
pkg_check_modules(ZOPFLIPNG zopflipng)
endif()
Expand All @@ -242,19 +222,38 @@ if(NOT ZOPFLIPNG_FOUND)
endif()
endif()

add_executable(spratlayout
src/spratlayout.cpp
# Target definitions
add_library(spratcore STATIC
src/core/cli_parse.cpp
src/core/layout_parser.cpp
src/core/output_pattern.cpp
src/core/i18n.cpp
src/core/stb_impl.cpp
src/commands/spratlayout_command.cpp
src/commands/spratpack_command.cpp
src/commands/spratconvert_command.cpp
src/commands/spratframes_command.cpp
src/commands/spratunpack_command.cpp
)

target_include_directories(spratcore PUBLIC src)
target_include_directories(spratcore SYSTEM PRIVATE ${STB_DIR})
target_include_directories(spratcore PRIVATE ${LIBARCHIVE_INCLUDE_DIRS})
if(SPRAT_GETTEXT_AVAILABLE)
target_compile_definitions(spratcore PUBLIC SPRAT_ENABLE_GETTEXT)
target_link_libraries(spratcore PUBLIC ${SPRAT_INTL_LIBRARIES})
if(SPRAT_INTL_INCLUDE_DIRS)
target_include_directories(spratcore PUBLIC ${SPRAT_INTL_INCLUDE_DIRS})
endif()
endif()
target_compile_definitions(spratcore PUBLIC SPRAT_LOCALE_DIR="${CMAKE_INSTALL_FULL_LOCALEDIR}")

add_executable(spratlayout src/spratlayout.cpp)
target_link_libraries(spratlayout PRIVATE spratcore ${LIBARCHIVE_LIBRARIES})
target_include_directories(spratlayout PRIVATE ${LIBARCHIVE_INCLUDE_DIRS})
target_include_directories(spratlayout SYSTEM PRIVATE ${STB_DIR})
target_include_directories(spratlayout PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

add_executable(spratpack
src/spratpack.cpp
src/commands/spratpack_command.cpp
)
add_executable(spratpack src/spratpack.cpp)
target_link_libraries(spratpack PRIVATE spratcore ${LIBARCHIVE_LIBRARIES})
if(ZOPFLIPNG_FOUND)
target_link_libraries(spratpack PRIVATE ${ZOPFLIPNG_LIBRARIES})
Expand All @@ -263,32 +262,20 @@ if(ZOPFLIPNG_FOUND)
endif()
target_include_directories(spratpack PRIVATE ${LIBARCHIVE_INCLUDE_DIRS})
target_include_directories(spratpack SYSTEM PRIVATE ${STB_DIR})
target_include_directories(spratpack PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

add_executable(spratconvert
src/spratconvert.cpp
src/commands/spratconvert_command.cpp
)
add_executable(spratconvert src/spratconvert.cpp)
target_link_libraries(spratconvert PRIVATE spratcore)
target_include_directories(spratconvert SYSTEM PRIVATE ${STB_DIR})
target_include_directories(spratconvert PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

add_executable(spratframes
src/spratframes.cpp
src/commands/spratframes_command.cpp
)
add_executable(spratframes src/spratframes.cpp)
target_link_libraries(spratframes PRIVATE spratcore)
target_include_directories(spratframes SYSTEM PRIVATE ${STB_DIR})
target_include_directories(spratframes PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

add_executable(spratunpack
src/spratunpack.cpp
src/commands/spratunpack_command.cpp
)
add_executable(spratunpack src/spratunpack.cpp)
target_link_libraries(spratunpack PRIVATE spratcore ${LIBARCHIVE_LIBRARIES})
target_include_directories(spratunpack PRIVATE ${LIBARCHIVE_INCLUDE_DIRS})
target_include_directories(spratunpack SYSTEM PRIVATE ${STB_DIR})
target_include_directories(spratunpack PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

target_compile_definitions(spratconvert PRIVATE SPRAT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
target_compile_definitions(spratlayout PRIVATE
SPRAT_GLOBAL_PROFILE_CONFIG="${CMAKE_INSTALL_FULL_DATADIR}/sprat/spratprofiles.cfg")
Expand Down
17 changes: 10 additions & 7 deletions src/commands/spratunpack_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,13 +621,16 @@ class SpriteUnpacker {
}

// Encode as PNG in memory
int png_size = 0;
unsigned char* png_buffer_raw = stbi_write_png_to_mem(sprite_data.data(), out_w * NUM_CHANNELS,
out_w, out_h, NUM_CHANNELS, &png_size);
if (png_buffer_raw == nullptr) {
std::vector<unsigned char> png_buffer;
auto write_to_vec = [](void* context, void* data, int size) {
auto* vec = static_cast<std::vector<unsigned char>*>(context);
const auto* bytes = static_cast<const unsigned char*>(data);
vec->insert(vec->end(), bytes, bytes + size);
};
Comment on lines +625 to +629
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The stbi_write_png_to_func callback receives size as an int. While it's unlikely to be negative in a successful scenario, adding a check to handle non-positive size values would make the callback more robust and prevent potential undefined behavior from std::vector::insert if an invalid size is ever passed.

        auto write_to_vec = [](void* context, void* data, int size) {
            if (size <= 0) {
                return;
            }
            auto* vec = static_cast<std::vector<unsigned char>*>(context);
            const auto* bytes = static_cast<const unsigned char*>(data);
            vec->insert(vec->end(), bytes, bytes + size);
        };


if (!stbi_write_png_to_func(write_to_vec, &png_buffer, out_w, out_h, NUM_CHANNELS, sprite_data.data(), out_w * NUM_CHANNELS)) {
return false;
}
std::unique_ptr<unsigned char, void(*)(void*)> png_buffer(png_buffer_raw, std::free);

std::string filename = frame.name;
if (filename.find('.') == std::string::npos) {
Expand All @@ -640,7 +643,7 @@ class SpriteUnpacker {
}

archive_entry_set_pathname(entry, filename.c_str());
archive_entry_set_size(entry, png_size);
archive_entry_set_size(entry, static_cast<la_int64_t>(png_buffer.size()));
archive_entry_set_filetype(entry, AE_IFREG);
constexpr int DEFAULT_FILE_PERMISSIONS = 0644;
archive_entry_set_perm(entry, DEFAULT_FILE_PERMISSIONS);
Expand All @@ -652,7 +655,7 @@ class SpriteUnpacker {
return false;
}

if (archive_write_data(a, png_buffer.get(), png_size) != static_cast<ssize_t>(png_size)) {
if (archive_write_data(a, png_buffer.data(), png_buffer.size()) != static_cast<ssize_t>(png_buffer.size())) {
std::cerr << tr("Error: Failed to write archive data: ") << archive_error_string(a) << '\n';
archive_entry_free(entry);
return false;
Expand Down
Loading