diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7754f7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# ignore compressor stuff: + +/compressor/build/ +/compressor/compile_commands.json +/compressor/compressor +/compressor/.vscode/ +/compressor/*.mp4 diff --git a/compressor b/compressor deleted file mode 160000 index 504b62b..0000000 --- a/compressor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 504b62ba93ac8be7e78390ff67bc99d4ceb67791 diff --git a/compressor/CMakeLists.txt b/compressor/CMakeLists.txt new file mode 100644 index 0000000..bc65dd1 --- /dev/null +++ b/compressor/CMakeLists.txt @@ -0,0 +1,188 @@ +cmake_minimum_required(VERSION 3.20) +project(CPP_TEMPLATE VERSION 0.1.0 LANGUAGES C CXX) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Set C standard +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +# Export compile commands for IDE support (clangd, etc.) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Create symlink to compile_commands.json in project root for LSP +if(CMAKE_EXPORT_COMPILE_COMMANDS) + add_custom_target(symlink_compile_commands ALL + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${CMAKE_BINARY_DIR}/compile_commands.json + ${CMAKE_SOURCE_DIR}/compile_commands.json + COMMENT "Creating symlink to compile_commands.json in project root" + ) +endif() + +# Set output directories +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# Build type defaults +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type" FORCE) +endif() + +# Options +option(ENABLE_SANITIZERS "Enable address and undefined behavior sanitizers" ON) +option(ENABLE_STATIC_ANALYSIS "Enable static analysis warnings" ON) +set(OUTPUT_NAME "" CACHE STRING "Name of the output executable (defaults to project name)") + +# Gather source files (avoid GLOB_RECURSE - explicitly list files is better practice) +# But keeping it for template flexibility +file(GLOB_RECURSE PROJECT_SOURCES + ${CMAKE_SOURCE_DIR}/source/*.cpp + ${CMAKE_SOURCE_DIR}/source/*.c +) + +file(GLOB_RECURSE LIBRARY_SOURCES + ${CMAKE_SOURCE_DIR}/libraries/*.cpp + ${CMAKE_SOURCE_DIR}/libraries/*.c +) + +# Don't glob headers from include/ as sources (they should only be included) +file(GLOB_RECURSE PROJECT_HEADERS + ${CMAKE_SOURCE_DIR}/include/*.hpp + ${CMAKE_SOURCE_DIR}/include/*.h + ${CMAKE_SOURCE_DIR}/libraries/*.hpp + ${CMAKE_SOURCE_DIR}/libraries/*.h + ${CMAKE_SOURCE_DIR}/source/*.hpp + ${CMAKE_SOURCE_DIR}/source/*.h +) + +# Combine all sources +set(ALL_SOURCES ${PROJECT_SOURCES} ${LIBRARY_SOURCES}) + +# Determine executable name +if(OUTPUT_NAME) + set(EXECUTABLE_NAME ${OUTPUT_NAME}) +else() + set(EXECUTABLE_NAME ${PROJECT_NAME}) +endif() + +# Create executable +add_executable(${EXECUTABLE_NAME} ${ALL_SOURCES}) + +# Set target properties +set_target_properties(${EXECUTABLE_NAME} PROPERTIES + OUTPUT_NAME ${EXECUTABLE_NAME} + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +) + +# Include directories - use target-specific commands +target_include_directories(${EXECUTABLE_NAME} PRIVATE + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/libraries +) + +# Compiler-specific flags +if(MSVC) + target_compile_options(${EXECUTABLE_NAME} PRIVATE + /W4 # Warning level 4 + /permissive- # Standards conformance + /Zc:__cplusplus # Correct __cplusplus macro + /Zc:inline # Remove unreferenced COMDAT + /WX- # Don't treat warnings as errors by default + ) + + if(ENABLE_STATIC_ANALYSIS) + target_compile_options(${EXECUTABLE_NAME} PRIVATE /analyze) + endif() + + # MSVC debug flags + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(${EXECUTABLE_NAME} PRIVATE /Zi /Od) + endif() + +else() # GCC/Clang + target_compile_options(${EXECUTABLE_NAME} PRIVATE + -Wall + -Wextra + -Wpedantic + -Wshadow + -Wconversion + -Wsign-conversion + -Wuninitialized + -Wunused + -Werror=return-type + -Wcast-align + -Wformat=2 + -Wnull-dereference + ) + + # Additional warnings for static analysis + if(ENABLE_STATIC_ANALYSIS) + target_compile_options(${EXECUTABLE_NAME} PRIVATE + -Wcast-qual + -Wdouble-promotion + -Wold-style-cast + ) + endif() + + # Sanitizers (Debug builds) + if(ENABLE_SANITIZERS AND CMAKE_BUILD_TYPE STREQUAL "Debug") + # Check if sanitizers are available + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("-fsanitize=address" HAS_ASAN) + check_cxx_compiler_flag("-fsanitize=undefined" HAS_UBSAN) + + if(HAS_ASAN AND HAS_UBSAN) + target_compile_options(${EXECUTABLE_NAME} PRIVATE + -fsanitize=address,undefined,leak + -fno-omit-frame-pointer + -g + ) + target_link_options(${EXECUTABLE_NAME} PRIVATE + -fsanitize=address,undefined,leak + ) + message(STATUS "Sanitizers enabled: address, undefined, leak") + else() + message(WARNING "Sanitizers requested but not available - skipping") + endif() + endif() + + # Optimization flags for Release + if(CMAKE_BUILD_TYPE STREQUAL "Release") + target_compile_options(${EXECUTABLE_NAME} PRIVATE + -O3 + -march=native + -DNDEBUG + ) + endif() + + # Debug flags + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(${EXECUTABLE_NAME} PRIVATE + -O0 + -g3 + -ggdb + ) + endif() +endif() + +# Print configuration summary +message(STATUS "=== Configuration Summary ===") +message(STATUS "Project: ${PROJECT_NAME} v${PROJECT_VERSION}") +message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}") +message(STATUS "C++ Standard: ${CMAKE_CXX_STANDARD}") +message(STATUS "C Standard: ${CMAKE_C_STANDARD}") +message(STATUS "Executable Name: ${EXECUTABLE_NAME}") +message(STATUS "Sanitizers: ${ENABLE_SANITIZERS}") +message(STATUS "Static Analysis: ${ENABLE_STATIC_ANALYSIS}") +message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") +message(STATUS "============================") + +# Optionally, enable testing +# enable_testing() +# add_subdirectory(tests) diff --git a/compressor/build.sh b/compressor/build.sh new file mode 100755 index 0000000..7498857 --- /dev/null +++ b/compressor/build.sh @@ -0,0 +1,250 @@ +#!/bin/bash + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' +NC='\033[0m' # No Color +BOLD='\033[1m' + +# Unicode symbols +CHECK="✓" +CROSS="✗" +ARROW="➜" +GEAR="⚙" +HAMMER="🔨" +ROCKET="🚀" + +# Default values +BUILD_TYPE="Debug" +CLEAN_BUILD=false +VERBOSE=false +JOBS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) + +# Print functions +print_header() { + echo -e "${BOLD}${BLUE}╔════════════════════════════════════════════════════════╗${NC}" + echo -e "${BOLD}${BLUE}║${NC} ${HAMMER} ${BOLD}${CYAN} C/C++ Project Builder${NC} ${BOLD}${BLUE}║${NC}" + echo -e "${BOLD}${BLUE}╚════════════════════════════════════════════════════════╝${NC}" + echo "" +} + +print_separator() { + echo -e "${BLUE}────────────────────────────────────────────────────────${NC}" +} + +print_success() { + echo -e "${GREEN}${CHECK}${NC} $1" +} + +print_error() { + echo -e "${RED}${CROSS}${NC} $1" +} + +print_info() { + echo -e "${CYAN}${ARROW}${NC} $1" +} + +print_step() { + echo -e "${YELLOW}${GEAR}${NC} ${BOLD}$1${NC}" +} + +show_progress() { + local duration=$1 + local prefix=$2 + local size=40 + already_done() { for ((done=0; done<$elapsed; done++)); do printf "▓"; done } + remaining() { for ((remain=$elapsed; remain<$size; remain++)); do printf " "; done } + percentage() { printf "| %s%%" $(( (($elapsed)*100)/($size)*100/100 )); } + + for (( elapsed=1; elapsed<=$size; elapsed++ )); do + printf "\r${CYAN}${prefix}${NC} [$(already_done)$(remaining)] $(percentage)" + sleep $(echo "scale=3; $duration/$size" | bc) + done + printf "\n" +} + +usage() { + echo -e "${BOLD}Usage:${NC} $0 [OPTIONS] " + echo "" + echo -e "${BOLD}Options:${NC}" + echo -e " -r, --release Build in Release mode (default: Debug)" + echo -e " -c, --clean Clean build directory before building" + echo -e " -v, --verbose Verbose make output" + echo -e " -j, --jobs Number of parallel jobs (default: $JOBS)" + echo -e " -h, --help Show this help message" + echo "" + echo -e "${BOLD}Examples:${NC}" + echo -e " $0 myprogram" + echo -e " $0 -r -j8 myprogram" + echo -e " $0 --clean --release myprogram" + exit 1 +} + +# Parse arguments +POSITIONAL_ARGS=() +while [[ $# -gt 0 ]]; do + case $1 in + -r|--release) + BUILD_TYPE="Release" + shift + ;; + -c|--clean) + CLEAN_BUILD=true + shift + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -j|--jobs) + JOBS="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + POSITIONAL_ARGS+=("$1") + shift + ;; + esac +done + +set -- "${POSITIONAL_ARGS[@]}" + +# Check if executable name is provided +if [ -z "$1" ]; then + print_header + print_error "No executable name provided" + echo "" + usage +fi + +EXECUTABLE_NAME=$1 + +# Start build process +print_header + +# Build configuration info +print_info "Build Configuration:" +echo -e " ${BOLD}Executable:${NC} $EXECUTABLE_NAME" +echo -e " ${BOLD}Build Type:${NC} $BUILD_TYPE" +echo -e " ${BOLD}Jobs:${NC} $JOBS" +echo -e " ${BOLD}Clean Build:${NC} $CLEAN_BUILD" +echo "" +print_separator +echo "" + +# Clean build directory if requested +if [ "$CLEAN_BUILD" = true ] && [ -d "./build" ]; then + print_step "Cleaning build directory..." + rm -rf ./build + print_success "Build directory cleaned" + echo "" +fi + +# Create build directory +if [ ! -d "./build" ]; then + print_step "Creating build directory..." + mkdir -p build + print_success "Build directory created" +else + print_info "Using existing build directory" +fi +echo "" + +# CMake configuration +print_step "Configuring CMake..." +print_separator +echo "" + +cd ./build + +CMAKE_CMD="cmake -DOUTPUT_NAME=$EXECUTABLE_NAME -DCMAKE_BUILD_TYPE=$BUILD_TYPE .." + +if [ "$VERBOSE" = true ]; then + eval $CMAKE_CMD +else + eval $CMAKE_CMD > /dev/null 2>&1 +fi + +if [ $? -ne 0 ]; then + print_error "CMake configuration failed" + exit 1 +fi + +print_success "CMake configuration complete" +echo "" + +# Compilation +print_step "Compiling project..." +print_separator +echo "" + +MAKE_CMD="make -j$JOBS" +if [ "$VERBOSE" = true ]; then + MAKE_CMD="$MAKE_CMD VERBOSE=1" +fi + +START_TIME=$(date +%s) + +if [ "$VERBOSE" = true ]; then + eval $MAKE_CMD + BUILD_RESULT=$? +else + eval $MAKE_CMD 2>&1 | tee build.log | while IFS= read -r line; do + if echo "$line" | grep -q "\[.*%\]"; then + printf "\r${CYAN}${ARROW}${NC} Compiling: %s" "$line" + elif echo "$line" | grep -qE "error:|Error|ERROR"; then + echo "" + print_error "$line" + fi + done + BUILD_RESULT=${PIPESTATUS[0]} +fi + +END_TIME=$(date +%s) +BUILD_TIME=$((END_TIME - START_TIME)) + +echo "" + +if [ $BUILD_RESULT -ne 0 ]; then + print_error "Build failed!" + echo "" + print_info "Check build/build.log for details" + exit 1 +fi + +print_success "Compilation complete (${BUILD_TIME}s)" +echo "" + +# Copy executable +print_step "Copying executable to project root..." + +if [ ! -f "./bin/$EXECUTABLE_NAME" ]; then + print_error "Executable not found: ./bin/$EXECUTABLE_NAME" + exit 1 +fi + +cp ./bin/$EXECUTABLE_NAME ../ +print_success "Executable copied" +echo "" + +# Build summary +print_separator +echo -e "${BOLD}${GREEN}${ROCKET} Build Complete!${NC}" +print_separator +echo "" +echo -e "${BOLD}Summary:${NC}" +echo -e " ${BOLD}Executable:${NC} ./$EXECUTABLE_NAME" +echo -e " ${BOLD}Build Type:${NC} $BUILD_TYPE" +echo -e " ${BOLD}Build Time:${NC} ${BUILD_TIME}s" +echo -e " ${BOLD}Binary Location:${NC} ./build/bin/$EXECUTABLE_NAME" + +echo "" +print_info "Run with: ${BOLD}./$EXECUTABLE_NAME${NC}" +echo "" diff --git a/compressor/include/.gitkeep b/compressor/include/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/compressor/include/file_types.hpp b/compressor/include/file_types.hpp new file mode 100644 index 0000000..b6781d8 --- /dev/null +++ b/compressor/include/file_types.hpp @@ -0,0 +1,68 @@ +#ifndef FILE_TYPES_HPP +#define FILE_TYPES_HPP + +#include +#include +#include + +enum fileType { mp4, avi, mkv, mov, flv, webm, unknown }; + +bool isVideoFile(std::string filename, fileType &type); + +class file { + private: + std::string filename; + fileType type; + + public: + file() : filename(""), type(unknown) {} + + file(const char *fname, fileType ftype) { + this->filename = std::string(fname); + this->type = ftype; + } + + file(const char *fname) { + filename = std::string(fname); + if (!isVideoFile(fname, this->type)) { + type = unknown; + } + } + + file(const std::string &fname, fileType ftype) { + this->filename = fname; + this->type = ftype; + } + + fileType getType() const { + return type; + } + + std::string getFilename() const { + return filename; + } + + void setType(fileType ftype) { + this->type = ftype; + } + + void setFilename(const std::string &fname) { + this->filename = fname; + if (!isVideoFile(fname.c_str(), this->type)) { + type = unknown; + } + } + + void setFilename(const char *fname) { + this->filename = std::string(fname); + if (!isVideoFile(fname, this->type)) { + type = unknown; + } + } + + bool operator==(const file &other) const { + return this->getType() == other.getType(); + } +}; + +#endif // FILE_TYPES_HPP diff --git a/compressor/include/print.hpp b/compressor/include/print.hpp new file mode 100644 index 0000000..4258807 --- /dev/null +++ b/compressor/include/print.hpp @@ -0,0 +1,17 @@ +#ifndef PRINT_HPP +#define PRINT_HPP + +#include +#include +#include + +class Logger { + public: + static void log(const char* format, ...); + static void err(const char* format, ...); + static void warn(const char* format, ...); + static void print(const char* format, ...); + static void println(const char* format, ...); +}; + +#endif // PRINT_HPP diff --git a/compressor/libraries/.gitkeep b/compressor/libraries/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/compressor/source/file_types.cpp b/compressor/source/file_types.cpp new file mode 100644 index 0000000..ff33547 --- /dev/null +++ b/compressor/source/file_types.cpp @@ -0,0 +1,28 @@ +#include "file_types.hpp" + +bool isVideoFile(std::string filename, fileType &type) { + size_t dotPos = filename.find_last_of('.'); + if (dotPos == std::string::npos) { + type = unknown; + return false; + } + + std::string ext = filename.substr(dotPos + 1); + if (ext == "mp4") { + type = mp4; + } else if (ext == "avi") { + type = avi; + } else if (ext == "mkv") { + type = mkv; + } else if (ext == "mov") { + type = mov; + } else if (ext == "flv") { + type = flv; + } else if (ext == "webm") { + type = webm; + } else { + type = unknown; + return false; + } + return true; +} diff --git a/compressor/source/main.cpp b/compressor/source/main.cpp new file mode 100644 index 0000000..47c1cc2 --- /dev/null +++ b/compressor/source/main.cpp @@ -0,0 +1,71 @@ +#include +#include + +#include "print.hpp" +#include "file_types.hpp" + +using namespace std; + +bool verbose = false; +file input_file; +file output_file; +Logger logger; + +int main(int argc, char *argv[]) { + if (argc < 2) { + logger.println("No arguments provided."); + logger.println("Usage: \t%s [args...]", argv[0]); + logger.println("\t-i \tSpecify input file"); + logger.println("\t-o \tSpecify output file"); + logger.println("\t-v \t\tEnable verbose mode"); + + return 1; + } + + for (int i = 1; i < argc; ++i) { + string arg = argv[i]; + if (arg == "-i" && i + 1 < argc) { + input_file.setFilename(argv[++i]); + if (verbose) + logger.log("Input file set to: %s\n", argv[i]); + } else if (arg == "-o" && i + 1 < argc) { + output_file.setFilename(argv[++i]); + if (verbose) + logger.log("Output file set to: %s\n", argv[i]); + } else if (arg == "-v") { + verbose = true; + logger.log("Verbose mode enabled.\n"); + } else { + logger.err("Unknown argument: %s \"%s\"\n", argv[i], (i + 1 < argc) ? argv[i + 1] : ""); + return 1; + } + } + + // INFO: Check if input files are video + if (input_file.getType() == unknown) { + logger.err("Input file type is unknown or unsupported: %s\n", input_file.getFilename().c_str()); + return 1; + } else { + if (verbose) + logger.log("Input file type recognized: %s\n", input_file.getFilename().c_str()); + } + + if (output_file.getType() == unknown) { + logger.err("Output file type is unknown or unsupported: %s\n", output_file.getFilename().c_str()); + return 1; + } else { + if (verbose) + logger.log("Output file type recognized: %s\n", output_file.getFilename().c_str()); + } + + if(!(input_file == output_file)) { + logger.err("Input and output files have to be the same: %s != %s\n", input_file.getFilename().c_str(), output_file.getFilename().c_str()); + return 1; + } + + if (verbose) { + logger.log("Processing from %s to %s\n", input_file.getFilename().c_str(), output_file.getFilename().c_str()); + } + + return 0; +} diff --git a/compressor/source/print.cpp b/compressor/source/print.cpp new file mode 100644 index 0000000..760deee --- /dev/null +++ b/compressor/source/print.cpp @@ -0,0 +1,51 @@ +#include "print.hpp" +#include + +void Logger::log(const char* format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stdout,"[LOG]: "); + vfprintf(stdout,format, args); + va_end(args); + fflush(stdout); +} + +void Logger::err(const char* format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, "[ERROR]: "); + vfprintf(stderr, format, args); + va_end(args); + fflush(stderr); +} + +void Logger::warn(const char* format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, "[WARN]: "); + vfprintf(stderr, format, args); + va_end(args); + fflush(stderr); +} + +void Logger::print(const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + fflush(stdout); +} + +void Logger::println(const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + fprintf(stdout, "\n"); + va_end(args); + fflush(stdout); +} diff --git a/compressor/test.mp4 b/compressor/test.mp4 new file mode 100644 index 0000000..3ec64eb Binary files /dev/null and b/compressor/test.mp4 differ