diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 75f4878a..35c25709 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -4,7 +4,7 @@ set(TARGET_NAME "SWA") option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF) function(BIN2C) - cmake_parse_arguments(BIN2C_ARGS "" "TARGET_OBJ;SOURCE_FILE;DEST_FILE;ARRAY_TYPE;ARRAY_NAME" "" ${ARGN}) + cmake_parse_arguments(BIN2C_ARGS "" "TARGET_OBJ;SOURCE_FILE;DEST_FILE;ARRAY_NAME;COMPRESSION_TYPE" "" ${ARGN}) if(NOT BIN2C_ARGS_TARGET_OBJ) message(FATAL_ERROR "TARGET_OBJ not specified.") @@ -18,8 +18,12 @@ function(BIN2C) set(BIN2C_ARGS_DEST_FILE "${BIN2C_ARGS_SOURCE_FILE}") endif() + if(NOT BIN2C_ARGS_COMPRESSION_TYPE) + set(BIN2C_ARGS_COMPRESSION_TYPE "none") + endif() + add_custom_command(OUTPUT "${BIN2C_ARGS_DEST_FILE}.c" - COMMAND file_to_c "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_ARRAY_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h" + COMMAND file_to_c "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_COMPRESSION_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h" DEPENDS "${BIN2C_ARGS_SOURCE_FILE}" file_to_c BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h" COMMENT "Generating binary header for ${BIN2C_ARGS_SOURCE_FILE}..." @@ -313,9 +317,9 @@ generate_aggregate_header( set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources") set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/achievements_menu/trophy.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/achievements_menu/trophy.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_trophy") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_general_window") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fade") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fill") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon_night") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/achievements_menu/trophy.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/achievements_menu/trophy.dds" ARRAY_NAME "g_trophy" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_NAME "g_general_window" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_NAME "g_select_fade" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_NAME "g_select_fill" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night") diff --git a/UnleashedRecomp/decompressor.h b/UnleashedRecomp/decompressor.h new file mode 100644 index 00000000..4809f7e6 --- /dev/null +++ b/UnleashedRecomp/decompressor.h @@ -0,0 +1,9 @@ +#pragma once + +template +static std::unique_ptr decompressZstd(const uint8_t(&data)[N], size_t decompressedSize) +{ + auto decompressedData = std::make_unique(decompressedSize); + ZSTD_decompress(decompressedData.get(), decompressedSize, data, N); + return decompressedData; +} diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index eb00a97c..b842ff5a 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -624,9 +625,9 @@ void AchievementMenu::Init() g_fntNewRodinDB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); g_fntNewRodinUB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE); - g_upTrophyIcon = LoadTexture(g_trophy, sizeof(g_trophy)); - g_upSelectionCursor = LoadTexture(g_select_fill, sizeof(g_select_fill)); - g_upWindow = LoadTexture(g_general_window, sizeof(g_general_window)); + g_upTrophyIcon = LoadTexture(decompressZstd(g_trophy, g_trophy_uncompressed_size).get(), g_trophy_uncompressed_size); + g_upSelectionCursor = LoadTexture(decompressZstd(g_select_fill, g_select_fill_uncompressed_size).get(), g_select_fill_uncompressed_size); + g_upWindow = LoadTexture(decompressZstd(g_general_window, g_general_window_uncompressed_size).get(), g_general_window_uncompressed_size); } void AchievementMenu::Draw() diff --git a/UnleashedRecomp/ui/achievement_overlay.cpp b/UnleashedRecomp/ui/achievement_overlay.cpp index 209a3f3e..fe88c962 100644 --- a/UnleashedRecomp/ui/achievement_overlay.cpp +++ b/UnleashedRecomp/ui/achievement_overlay.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; @@ -78,7 +79,7 @@ void AchievementOverlay::Init() g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE); - g_upWindow = LoadTexture(g_general_window, sizeof(g_general_window)); + g_upWindow = LoadTexture(decompressZstd(g_general_window, g_general_window_uncompressed_size).get(), g_general_window_uncompressed_size); } void AchievementOverlay::Draw() diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp index 9e3639b5..8142c8f8 100644 --- a/UnleashedRecomp/ui/message_window.cpp +++ b/UnleashedRecomp/ui/message_window.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; @@ -176,7 +177,7 @@ void MessageWindow::Init() g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 28.0f * FONT_SCALE); - g_upSelectionCursor = LoadTexture(g_select_fade, sizeof(g_select_fade)); + g_upSelectionCursor = LoadTexture(decompressZstd(g_select_fade, g_select_fade_uncompressed_size).get(), g_select_fade_uncompressed_size); } void MessageWindow::Draw() diff --git a/tools/file_to_c/CMakeLists.txt b/tools/file_to_c/CMakeLists.txt index 08975574..96bfb044 100644 --- a/tools/file_to_c/CMakeLists.txt +++ b/tools/file_to_c/CMakeLists.txt @@ -6,3 +6,6 @@ project("file_to_c") set(CMAKE_CXX_STANDARD 17) add_executable(file_to_c "file_to_c.cpp") + +find_package(zstd CONFIG REQUIRED) +target_link_libraries(file_to_c PRIVATE $,zstd::libzstd_static,zstd::libzstd>) diff --git a/tools/file_to_c/file_to_c.cpp b/tools/file_to_c/file_to_c.cpp index 4d69e8c9..4a9eb490 100644 --- a/tools/file_to_c/file_to_c.cpp +++ b/tools/file_to_c/file_to_c.cpp @@ -26,6 +26,7 @@ #include #include #include +#include std::vector read_file(const char* path) { std::ifstream input_file{path, std::ios::binary}; @@ -55,13 +56,13 @@ void create_parent_if_needed(const char* path) { int main(int argc, const char** argv) { if (argc != 6) { - printf("Usage: %s [input file] [array name] [array type] [output C file] [output C header]\n", argv[0]); + printf("Usage: %s [input file] [array name] [compression type] [output C file] [output C header]\n", argv[0]); return EXIT_SUCCESS; } const char* input_path = argv[1]; const char* array_name = argv[2]; - const char* array_type = argv[3]; + std::string compression_type = argv[3]; const char* output_c_path = argv[4]; const char* output_h_path = argv[5]; @@ -73,21 +74,44 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } + // Compress if requested. + std::vector compressed_contents; + std::transform(compression_type.begin(), compression_type.end(), compression_type.begin(), tolower); + + if (compression_type == "zstd") { + size_t bound_size = ZSTD_compressBound(contents.size()); + compressed_contents.resize(bound_size); + + size_t compressed_size = ZSTD_compress(compressed_contents.data(), bound_size, contents.data(), contents.size(), ZSTD_maxCLevel()); + compressed_contents.resize(compressed_size); + } + else if (compression_type != "none") { + fprintf(stderr, "Unknown compression type %s!", compression_type.c_str()); + return EXIT_FAILURE; + } + // Create the output directories if they don't exist create_parent_if_needed(output_c_path); create_parent_if_needed(output_h_path); // Write the C file with the array + std::vector& contents_to_write = !compressed_contents.empty() ? compressed_contents : contents; { std::ofstream output_c_file{output_c_path}; - output_c_file << "extern " << array_type << " " << array_name << "[" << contents.size() << "];\n"; - output_c_file << array_type << " " << array_name << "[" << contents.size() << "] = {"; + output_c_file << "extern unsigned char " << array_name << "[" << contents_to_write.size() << "];\n"; + output_c_file << "unsigned char " << array_name << "[" << contents_to_write.size() << "] = {"; - for (char x : contents) { - output_c_file << (int)x << ", "; + for (char x : contents_to_write) { + output_c_file << (int)(unsigned char)x << ", "; } output_c_file << "};\n"; + + // Write decompressed size. + if (!compressed_contents.empty()) { + output_c_file << "extern size_t " << array_name << "_uncompressed_size;\n"; + output_c_file << "size_t " << array_name << "_uncompressed_size = " << contents.size() << ";\n"; + } } // Write the header file with the extern array @@ -97,7 +121,14 @@ int main(int argc, const char** argv) { "#ifdef __cplusplus\n" " extern \"C\" {\n" "#endif\n" - "extern " << array_type << " " << array_name << "[" << contents.size() << "];\n" + "extern unsigned char " << array_name << "[" << contents_to_write.size() << "];\n"; + + // Write decompressed size. + if (!compressed_contents.empty()) { + output_h_file << "extern size_t " << array_name << "_uncompressed_size;\n"; + } + + output_h_file << "#ifdef __cplusplus\n" " }\n" "#endif\n";