Implement ZSTD compression in file_to_c.

This commit is contained in:
Skyth 2024-12-04 16:36:42 +03:00
parent 1a794ef9ee
commit 0a588949a7
7 changed files with 70 additions and 20 deletions

View file

@ -4,7 +4,7 @@ set(TARGET_NAME "SWA")
option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF) option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF)
function(BIN2C) 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) if(NOT BIN2C_ARGS_TARGET_OBJ)
message(FATAL_ERROR "TARGET_OBJ not specified.") message(FATAL_ERROR "TARGET_OBJ not specified.")
@ -18,8 +18,12 @@ function(BIN2C)
set(BIN2C_ARGS_DEST_FILE "${BIN2C_ARGS_SOURCE_FILE}") set(BIN2C_ARGS_DEST_FILE "${BIN2C_ARGS_SOURCE_FILE}")
endif() endif()
if(NOT BIN2C_ARGS_COMPRESSION_TYPE)
set(BIN2C_ARGS_COMPRESSION_TYPE "none")
endif()
add_custom_command(OUTPUT "${BIN2C_ARGS_DEST_FILE}.c" 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 DEPENDS "${BIN2C_ARGS_SOURCE_FILE}" file_to_c
BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h" BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h"
COMMENT "Generating binary header for ${BIN2C_ARGS_SOURCE_FILE}..." 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_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources")
set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") 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/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_TYPE "unsigned char" ARRAY_NAME "g_general_window") 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_TYPE "unsigned char" ARRAY_NAME "g_select_fade") 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_TYPE "unsigned char" ARRAY_NAME "g_select_fill") 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_TYPE "unsigned char" ARRAY_NAME "g_game_icon") 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_TYPE "unsigned char" ARRAY_NAME "g_game_icon_night") 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")

View file

@ -0,0 +1,9 @@
#pragma once
template<size_t N>
static std::unique_ptr<uint8_t[]> decompressZstd(const uint8_t(&data)[N], size_t decompressedSize)
{
auto decompressedData = std::make_unique<uint8_t[]>(decompressedSize);
ZSTD_decompress(decompressedData.get(), decompressedSize, data, N);
return decompressedData;
}

View file

@ -8,6 +8,7 @@
#include <user/config.h> #include <user/config.h>
#include <app.h> #include <app.h>
#include <exports.h> #include <exports.h>
#include <decompressor.h>
#include <res/images/achievements_menu/trophy.dds.h> #include <res/images/achievements_menu/trophy.dds.h>
#include <res/images/common/general_window.dds.h> #include <res/images/common/general_window.dds.h>
#include <res/images/common/select_fill.dds.h> #include <res/images/common/select_fill.dds.h>
@ -624,9 +625,9 @@ void AchievementMenu::Init()
g_fntNewRodinDB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); 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_fntNewRodinUB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE);
g_upTrophyIcon = LoadTexture(g_trophy, sizeof(g_trophy)); g_upTrophyIcon = LoadTexture(decompressZstd(g_trophy, g_trophy_uncompressed_size).get(), g_trophy_uncompressed_size);
g_upSelectionCursor = LoadTexture(g_select_fill, sizeof(g_select_fill)); g_upSelectionCursor = LoadTexture(decompressZstd(g_select_fill, g_select_fill_uncompressed_size).get(), g_select_fill_uncompressed_size);
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 AchievementMenu::Draw() void AchievementMenu::Draw()

View file

@ -8,6 +8,7 @@
#include <user/achievement_data.h> #include <user/achievement_data.h>
#include <app.h> #include <app.h>
#include <exports.h> #include <exports.h>
#include <decompressor.h>
#include <res/images/common/general_window.dds.h> #include <res/images/common/general_window.dds.h>
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; 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_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() void AchievementOverlay::Draw()

View file

@ -3,6 +3,7 @@
#include <api/SWA.h> #include <api/SWA.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <exports.h> #include <exports.h>
#include <decompressor.h>
#include <res/images/common/select_fade.dds.h> #include <res/images/common/select_fade.dds.h>
constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; 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_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() void MessageWindow::Draw()

View file

@ -6,3 +6,6 @@ project("file_to_c")
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
add_executable(file_to_c "file_to_c.cpp") add_executable(file_to_c "file_to_c.cpp")
find_package(zstd CONFIG REQUIRED)
target_link_libraries(file_to_c PRIVATE $<IF:$<TARGET_EXISTS:zstd::libzstd_static>,zstd::libzstd_static,zstd::libzstd>)

View file

@ -26,6 +26,7 @@
#include <fstream> #include <fstream>
#include <cstdio> #include <cstdio>
#include <vector> #include <vector>
#include <zstd.h>
std::vector<char> read_file(const char* path) { std::vector<char> read_file(const char* path) {
std::ifstream input_file{path, std::ios::binary}; 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) { int main(int argc, const char** argv) {
if (argc != 6) { 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; return EXIT_SUCCESS;
} }
const char* input_path = argv[1]; const char* input_path = argv[1];
const char* array_name = argv[2]; 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_c_path = argv[4];
const char* output_h_path = argv[5]; const char* output_h_path = argv[5];
@ -73,21 +74,44 @@ int main(int argc, const char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Compress if requested.
std::vector<char> 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 the output directories if they don't exist
create_parent_if_needed(output_c_path); create_parent_if_needed(output_c_path);
create_parent_if_needed(output_h_path); create_parent_if_needed(output_h_path);
// Write the C file with the array // Write the C file with the array
std::vector<char>& contents_to_write = !compressed_contents.empty() ? compressed_contents : contents;
{ {
std::ofstream output_c_file{output_c_path}; std::ofstream output_c_file{output_c_path};
output_c_file << "extern " << array_type << " " << array_name << "[" << contents.size() << "];\n"; output_c_file << "extern unsigned char " << array_name << "[" << contents_to_write.size() << "];\n";
output_c_file << array_type << " " << array_name << "[" << contents.size() << "] = {"; output_c_file << "unsigned char " << array_name << "[" << contents_to_write.size() << "] = {";
for (char x : contents) { for (char x : contents_to_write) {
output_c_file << (int)x << ", "; output_c_file << (int)(unsigned char)x << ", ";
} }
output_c_file << "};\n"; 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 // Write the header file with the extern array
@ -97,7 +121,14 @@ int main(int argc, const char** argv) {
"#ifdef __cplusplus\n" "#ifdef __cplusplus\n"
" extern \"C\" {\n" " extern \"C\" {\n"
"#endif\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" "#ifdef __cplusplus\n"
" }\n" " }\n"
"#endif\n"; "#endif\n";