From 346f8781f25255a35e708b22aea7e582853422bb Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Sun, 27 Feb 2022 17:04:11 +0100 Subject: baseline --- .gitignore | 1 + CMakeLists.txt | 18 +++++++ LICENSE | 22 ++++++++ README.md | 11 ++++ cmake/Findlibpng.cmake | 68 ++++++++++++++++++++++++ src/CMakeLists.txt | 27 ++++++++++ src/image.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ src/image.h | 13 +++++ src/main.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 434 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmake/Findlibpng.cmake create mode 100644 src/CMakeLists.txt create mode 100644 src/image.c create mode 100644 src/image.h create mode 100644 src/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3ba38ad --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.18) + +if(CMAKE_POLICY_DEFAULT_CMP0017 OR CMAKE_POLICY_DEFAULT_CMP0020) + # touch these to remove warnings +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif() + +project(splash VERSION 0.0.0 LANGUAGES C) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +add_compile_definitions(VERSION=${CMAKE_PROJECT_VERSION}) + +add_subdirectory(src) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1b5a336 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022 Jan Drögehoff + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc4eab7 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# 3ds-splash + +C program intended to convert splash screen binaries to image files and back +developed as a reference for any future implementation dealing with this + +### Dependencies +- libpng + +### [License](LICENSE) + +3ds-splash is licensed under MIT \ No newline at end of file diff --git a/cmake/Findlibpng.cmake b/cmake/Findlibpng.cmake new file mode 100644 index 0000000..62c4d87 --- /dev/null +++ b/cmake/Findlibpng.cmake @@ -0,0 +1,68 @@ +# +# LIBPNG_INCLUDE_DIRS +# LIBPNG_LIBRARIES +# LIBPNG_CFLAGS +# LIBPNG_FOUND + + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_check_modules(_LIBPNG libpng) + + if (BUILD_STATIC AND NOT _LIBPNG_FOUND) + message(FATAL_ERROR "Cannot find static build information") + endif() +endif() +set(LIBPNG_FOUND ${_LIBPNG_FOUND}) + +if (_LIBPNG_FOUND) # we can rely on pkg-config + + if (NOT BUILD_STATIC) + set(LIBPNG_LIBRARIES ${_LIBPNG_LIBRARIES}) + set(LIBPNG_INCLUDE_DIRS ${_LIBPNG_INCLUDE_DIRS}) + set(LIBPNG_CFLAGS ${_LIBPNG_CFLAGS_OTHER}) + else() + set(LIBPNG_LIBRARIES ${_LIBPNG_STATIC_LIBRARIES}) + set(LIBPNG_INCLUDE_DIRS ${_LIBPNG_STATIC_INCLUDE_DIRS}) + set(LIBPNG_CFLAGS ${_LIBPNG_STATIC_CFLAGS_OTHER}) + endif() +else() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) + else() + set(_lib_suffix 32) + endif() + + find_path(LIBPNG_INC + NAMES png.h + HINTS + ENV LIBPNGPath${_lib_suffix} + ENV LIBPNGPath + ${_LIBPNG_INCLUDE_DIRS} + PATHS + /usr/include/libpng16 /usr/local/include/libpng16 + /usr/include/libpng /usr/local/include/libpng) + + find_library(LIBPNG_LIB + NAMES ${_LIBPNG_LIBRARIES} libpng.so libpng16.so + HINTS + ENV LIBPNGPath${_lib_suffix} + ENV LIBPNGPath + ${_LIBPNG_LIBRARY_DIRS} + ${_LIBPNG_STATIC_LIBRARY_DIRS} + PATHS + /usr/lib${_lib_suffix} /usr/local/lib${_lib_suffix} + /usr/lib /usr/local/lib) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LIBPNG DEFAULT_MSG LIBPNG_LIB LIBPNG_INC) + mark_as_advanced(LIBPNG_INC LIBPNG_LIB) + + if(LIBPNG_FOUND) + set(LIBPNG_INCLUDE_DIRS ${LIBPNG_INC}) + set(LIBPNG_LIBRARIES ${LIBPNG_LIB}) + if (BUILD_STATIC) + set(LIBPNG_LIBRARIES ${LIBPNG_LIBRARIES} ${_LIBPNG_STATIC_LIBRARIES}) + endif() + endif() +endif() \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..63e18a6 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,27 @@ + +find_package(libpng REQUIRED) + +list(APPEND + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/image.c + ${CMAKE_CURRENT_SOURCE_DIR}/image.h + ${CMAKE_CURRENT_SOURCE_DIR}/main.c +) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +set(CFLAGS + -Wall -Wextra -pedantic + -Wconversion -Wshadow -Wstrict-aliasing + -Winit-self -Wcast-align -Wpointer-arith + -Wmissing-declarations -Wmissing-include-dirs + -Wno-unused-parameter -Wuninitialized +) + +add_executable(splash ${SOURCES}) +target_compile_options(splash PUBLIC ${CFLAGS}) + +target_include_directories(splash PUBLIC ${LIBPNG_INCLUDE_DIRS}) + +target_link_libraries(splash LINK_PUBLIC ${LIBPNG_LIBRARIES}) +target_link_libraries(splash LINK_PUBLIC m) diff --git a/src/image.c b/src/image.c new file mode 100644 index 0000000..618a854 --- /dev/null +++ b/src/image.c @@ -0,0 +1,141 @@ +#include +#include + +#include "image.h" + +size_t bin_to_rgba(uint8_t** bufp, size_t size) +{ + size_t out_size = (size / 3) * 4; + uint8_t* buf = malloc(out_size); + + for (size_t i = 0, j = 0; i < size; i+=3, j+=4) + { + (buf+j)[0] = (*bufp+i)[2]; + (buf+j)[1] = (*bufp+i)[1]; + (buf+j)[2] = (*bufp+i)[0]; + (buf+j)[3] = 0xFF; + } + free(*bufp); + *bufp = buf; + return out_size; +} + + +size_t rgba_to_bin(uint8_t** bufp, size_t size) +{ + size_t out_size = (size / 4) * 3; + uint8_t* buf = malloc(out_size); + + for (size_t i = 0, j = 0; i < size; i+=4, j+=3) + { + (buf+j)[0] = (*bufp+i)[2]; + (buf+j)[1] = (*bufp+i)[1]; + (buf+j)[2] = (*bufp+i)[0]; + } + free(*bufp); + *bufp = buf; + return out_size; +} + +size_t png_to_rgba(uint8_t** bufp, size_t size) +{ + size_t out_size = 0; + + uint32_t* buf = (uint32_t*)*bufp; + FILE* fp = fmemopen(buf, size, "rb");; + png_bytep* row_pointers = NULL; + + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info = png_create_info_struct(png); + + png_init_io(png, fp); + png_read_info(png, info); + + if(png_get_bit_depth(png, info) == 16) png_set_strip_16(png); + + uint32_t w = png_get_image_width(png, info); + uint32_t h = png_get_image_height(png, info); + + printf("%i, %i\n", w, h); + + png_byte color_type = png_get_color_type(png, info); + + if(color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); + + if(png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png); + + row_pointers = malloc(sizeof(png_bytep) * h); + out_size = sizeof(uint32_t) * (w * h); + uint32_t* out = malloc(out_size); + for(uint32_t y = 0; y < h; y++) + { + row_pointers[y] = (png_byte*)(out+(w*y)); + } + + png_read_image(png, row_pointers); + + png_destroy_read_struct(&png, &info, NULL); + + if (fp) fclose(fp); + if (row_pointers) free(row_pointers); + + free(*bufp); + *bufp = (uint8_t*)out; + + return out_size; +} + +#define DEFAULT_WIDTH 240 + +size_t rgba_to_png(uint8_t** bufp, size_t size) +{ + uint32_t* buf = (uint32_t*)*bufp; + + size_t out_size = size; + uint32_t* out = malloc(out_size); + memset(out, 0, out_size); + //FILE *fp = fopen("out.png", "wb"); + FILE* fp = fmemopen(out, size, "wb"); + png_bytep* row_pointers = NULL; + + uint32_t w = 240; + uint32_t h = (uint32_t)((size/4)/w); + + printf("%i, %i\n", w, h); + + printf("%li\n", size); + + /* initialize stuff */ + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info_ptr = png_create_info_struct(png); + + png_init_io(png, fp); + + png_set_IHDR(png, info_ptr, w, h, + 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png, info_ptr); + + row_pointers = malloc(sizeof(png_bytep) * h); + for(uint32_t y = 0; y < h; y++) + { + row_pointers[y] = (png_byte*)(buf+(w*y)); + } + + png_write_image(png, row_pointers); + + png_write_end(png, NULL); + + free(row_pointers); + fclose(fp); + + while (!out[out_size/4]) --out_size; + + free(buf); + *bufp = out; + + return out_size; +} \ No newline at end of file diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..5c14fdf --- /dev/null +++ b/src/image.h @@ -0,0 +1,13 @@ +#ifndef IMAGE_H +#define IMAGE_H + +#include +#include + +size_t bin_to_rgba(uint8_t**, size_t); +size_t rgba_to_bin(uint8_t**, size_t); + +size_t png_to_rgba(uint8_t**, size_t); +size_t rgba_to_png(uint8_t**, size_t); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..b4fea17 --- /dev/null +++ b/src/main.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include + +#include "image.h" + +#define ARRAY_LEN(arr) sizeof(arr) / sizeof(arr[0]) + +#define FORMAT_ERROR "%s is not in a supported format\n" + +typedef size_t (*splash_func)(uint8_t**, size_t); +typedef struct { + char* extension; + splash_func func; +} splash_types; + +const splash_types in_conversions[] = { + { .extension = "bin", .func = bin_to_rgba }, + { .extension = "png", .func = png_to_rgba }, +}; + +const splash_types out_conversions[] = { + { .extension = "bin", .func = rgba_to_bin }, + { .extension = "png", .func = rgba_to_png }, +}; + +int main(int argc, char** argv) +{ + FILE* in_fd = NULL; + FILE* out_fd = NULL; + + int retval = EXIT_SUCCESS; + if (argc < 3) + { + fprintf(stderr, "%s [input] [output]\n", argv[0]); + retval = EXIT_FAILURE; + goto exit; + } + + char* in_path = argv[1]; + size_t in_path_len = strlen(in_path); + splash_func in_func = NULL; + + char* out_path = argv[2]; + size_t out_path_len = strlen(out_path); + splash_func out_func = NULL; + + for (size_t i = 0; i <= ARRAY_LEN(in_conversions); ++i) + { + if (i == ARRAY_LEN(in_conversions)) + { + fprintf(stderr, FORMAT_ERROR, in_path); + retval = EXIT_FAILURE; + goto exit; + } + + char* extension = in_conversions[i].extension; + if (!strcmp(in_path+(in_path_len-strlen(extension)), extension)) + { + in_func = in_conversions[i].func; + break; + } + } + + for (size_t i = 0; i <= ARRAY_LEN(out_conversions); ++i) + { + if (i == ARRAY_LEN(out_conversions)) + { + fprintf(stderr, FORMAT_ERROR, out_path); + retval = EXIT_FAILURE; + goto exit; + } + + char* extension = out_conversions[i].extension; + if (!strcmp(out_path+(out_path_len-strlen(extension)), extension)) + { + out_func = out_conversions[i].func; + break; + } + } + + + in_fd = fopen(in_path, "rb"); + if (!in_fd) + { + fprintf(stderr, "cannot open %s\n", in_path); + retval = EXIT_FAILURE; + goto exit; + } + + fseek(in_fd, 0L, SEEK_END); + size_t in_size = (size_t)ftell(in_fd); + rewind(in_fd); + + if (!in_size) + { + fprintf(stderr, "%s is empty\n", in_path); + retval = EXIT_FAILURE; + goto exit; + } + + out_fd = fopen(out_path, "wb"); + if (!out_fd) + { + fprintf(stderr, "cannot open %s\n", out_path); + retval = EXIT_FAILURE; + goto exit; + } + + uint8_t* buf = malloc(in_size+1); + fread(buf, 1, in_size, in_fd); + buf[in_size] = '\0'; + + if (in_func) + { + in_size = in_func(&buf, in_size); + } + + if (out_func) + { + in_size = out_func(&buf, in_size); + } + + fwrite(buf, 1, in_size, out_fd); + + exit: + + if (in_fd) fclose(in_fd); + if (out_fd) fclose(out_fd); + + return retval; +} \ No newline at end of file -- cgit v1.2.3