├── .appveyor.yml ├── .github └── FUNDING.yml ├── .gitignore ├── .gitmodules ├── .reapack-index.conf ├── ABOUT.md ├── CMakeLists.txt ├── COPYING ├── COPYING.LESSER ├── Extensions └── ReaPack.ext ├── README.md ├── cmake ├── FindMiniZip.cmake ├── FindPHP.cmake ├── FindSWELL.cmake ├── FindWDL.cmake ├── brew-llvm.cmake ├── linux-cross.cmake └── vcpkg-triplets │ ├── arch-env.cmake │ └── arm64ec-windows-static.cmake ├── index.xml ├── prepare.rb ├── src ├── CMakeLists.txt ├── about.cpp ├── about.hpp ├── action.cpp ├── action.hpp ├── api.cpp ├── api.hpp ├── api_helper.hpp ├── api_misc.cpp ├── api_package.cpp ├── api_repo.cpp ├── archive.cpp ├── archive.hpp ├── archive_tasks.cpp ├── browser.cpp ├── browser.hpp ├── browser_entry.cpp ├── browser_entry.hpp ├── buildinfo.hpp.in ├── buildinfo.rc ├── config.cpp ├── config.hpp ├── control.cpp ├── control.hpp ├── database.cpp ├── database.hpp ├── dialog.cpp ├── dialog.hpp ├── dialog.mm ├── dllimport.hpp ├── download.cpp ├── download.hpp ├── errors.hpp ├── event.cpp ├── event.hpp ├── filedialog.cpp ├── filedialog.hpp ├── filesystem.cpp ├── filesystem.hpp ├── filter.cpp ├── filter.hpp ├── hash.cpp ├── hash.hpp ├── iconlist.cpp ├── iconlist.hpp ├── import.cpp ├── import.hpp ├── index.cpp ├── index.hpp ├── index_v1.cpp ├── install.cpp ├── listview.cpp ├── listview.hpp ├── main.cpp ├── manager.cpp ├── manager.hpp ├── menu.cpp ├── menu.hpp ├── metadata.cpp ├── metadata.hpp ├── obsquery.cpp ├── obsquery.hpp ├── package.cpp ├── package.hpp ├── path.cpp ├── path.hpp ├── platform.cpp ├── platform.hpp ├── progress.cpp ├── progress.hpp ├── reapack.cpp ├── reapack.hpp ├── receipt.cpp ├── receipt.hpp ├── registry.cpp ├── registry.hpp ├── remote.cpp ├── remote.hpp ├── report.cpp ├── report.hpp ├── resource.hpp ├── resource.rc ├── richedit-generic.cpp ├── richedit-win32.cpp ├── richedit.hpp ├── richedit.mm ├── serializer.cpp ├── serializer.hpp ├── source.cpp ├── source.hpp ├── string.cpp ├── string.hpp ├── synchronize.cpp ├── tabbar.cpp ├── tabbar.hpp ├── task.cpp ├── task.hpp ├── thread.cpp ├── thread.hpp ├── time.cpp ├── time.hpp ├── transaction.cpp ├── transaction.hpp ├── version.cpp ├── version.hpp ├── win32.cpp ├── win32.hpp ├── xml.hpp ├── xml_libxml2.cpp └── xml_tinyxml2.cpp ├── test ├── CMakeLists.txt ├── action.cpp ├── api.cpp ├── database.cpp ├── event.cpp ├── filesystem.cpp ├── filter.cpp ├── hash.cpp ├── helper.cpp ├── helper.hpp ├── index.cpp ├── index_v1.cpp ├── indexes │ ├── ReaPack │ │ └── cache │ │ │ ├── broken.xml │ │ │ ├── future_version.xml │ │ │ ├── invalid_version.xml │ │ │ ├── wrong_root.xml │ │ │ └── Новая папка.xml │ └── v1 │ │ └── ReaPack │ │ └── cache │ │ ├── author.xml │ │ ├── changelog.xml │ │ ├── explicit_sections.xml │ │ ├── metadata.xml │ │ ├── missing_platform.xml │ │ ├── missing_source_file.xml │ │ ├── missing_source_url.xml │ │ ├── missing_type.xml │ │ ├── missing_version.xml │ │ ├── pkg_desc.xml │ │ ├── pkg_metadata.xml │ │ ├── src_platform.xml │ │ ├── src_type.xml │ │ ├── time.xml │ │ ├── unnamed_category.xml │ │ ├── unnamed_package.xml │ │ ├── unsupported_type.xml │ │ ├── valid_index.xml │ │ ├── wrong_category_tag.xml │ │ ├── wrong_package_tag.xml │ │ └── wrong_version_tag.xml ├── metadata.cpp ├── package.cpp ├── path.cpp ├── platform.cpp ├── receipt.cpp ├── registry.cpp ├── remote.cpp ├── serializer.cpp ├── source.cpp ├── string.cpp ├── time.cpp ├── version.cpp ├── win32.cpp └── xml.cpp ├── vcpkg.json └── vendor └── reaper_plugin_secrets.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: 'cfillion' 2 | custom: 3 | - "https://reapack.com/donate" 4 | - "https://paypal.me/cfillion" 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /vendor 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/WDL"] 2 | path = vendor/WDL 3 | url = https://github.com/justinfrankel/WDL.git 4 | [submodule "vendor/reaper-sdk"] 5 | path = vendor/reaper-sdk 6 | url = https://github.com/justinfrankel/reaper-sdk.git 7 | -------------------------------------------------------------------------------- /.reapack-index.conf: -------------------------------------------------------------------------------- 1 | --about ABOUT.md 2 | --ignore src 3 | --ignore test 4 | --ignore vendor 5 | --strict 6 | -------------------------------------------------------------------------------- /ABOUT.md: -------------------------------------------------------------------------------- 1 | # ReaPack: Package Manager for REAPER 2 | 3 | This is **ReaPack v[[REAPACK_VERSION]]** [[REAPACK_REVISION]] built on [[REAPACK_BUILDTIME]] 4 | 5 | ``` 6 | Copyright (C) 2015-2025 Christian Fillion 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | Visit to get a copy of the 19 | GNU Lesser General Public License. 20 | ``` 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | option(RUNTIME_OPENSSL 4 | "Load OpenSSL at runtime instead of linking against a specific version" OFF) 5 | 6 | if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) 7 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" 8 | CACHE STRING "") 9 | endif() 10 | 11 | if(DEFINED ENV{VCPKG_DEFAULT_TRIPLET} AND NOT DEFINED VCPKG_TARGET_TRIPLET) 12 | set(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}" CACHE STRING "") 13 | endif() 14 | 15 | project(reapack LANGUAGES C CXX) 16 | 17 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 18 | 19 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 20 | if(WIN32) 21 | set(USER_CONFIG_DIR "$ENV{APPDATA}") 22 | elseif(APPLE) 23 | set(USER_CONFIG_DIR "~/Library/Application Support") 24 | else() 25 | set(USER_CONFIG_DIR "~/.config") 26 | endif() 27 | 28 | set(CMAKE_INSTALL_PREFIX "${USER_CONFIG_DIR}/REAPER" CACHE PATH 29 | "REAPER resource path where to install ReaPack" FORCE) 30 | endif() 31 | 32 | if(CMAKE_OSX_ARCHITECTURES) 33 | list(JOIN CMAKE_OSX_ARCHITECTURES "-" ARCH_NAME) 34 | elseif(MSVC_CXX_ARCHITECTURE_ID) 35 | set(ARCH_NAME ${MSVC_CXX_ARCHITECTURE_ID}) 36 | else() 37 | set(ARCH_NAME ${CMAKE_SYSTEM_PROCESSOR}) 38 | endif() 39 | 40 | STRING(TOLOWER "${ARCH_NAME}" ARCH_NAME) 41 | 42 | set(CMAKE_CXX_EXTENSIONS OFF) 43 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 44 | set(CMAKE_CXX_VISIBILITY_PRESET "hidden") 45 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 46 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 47 | 48 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON) 49 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) 50 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON) 51 | 52 | if(VCPKG_TOOLCHAIN) 53 | set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL Release) 54 | set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release) 55 | endif() 56 | 57 | if(WIN32) 58 | foreach(arg 59 | CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG 60 | CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE 61 | CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO 62 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_MINSIZEREL 63 | ) 64 | # Embed debug information in the object files 65 | string(REGEX REPLACE "/Z[iI]" "/Z7" ${arg} "${${arg}}") 66 | endforeach() 67 | 68 | add_compile_options( 69 | # Exception handling model 70 | /EHsc 71 | 72 | # Enhance optimized debugging 73 | $<$>:/Zo> 74 | 75 | # Eliminate duplicate strings 76 | $<$>:/GF> 77 | 78 | # Enable function-level linking 79 | $<$>:/Gy> 80 | 81 | # Remove unreferenced COMDAT 82 | $<$>:/Zc:inline> 83 | ) 84 | 85 | add_link_options( 86 | # Remove unreferenced code 87 | $<$>:/OPT:REF> 88 | 89 | # Remove duplicate sections 90 | $<$>:/OPT:ICF> 91 | 92 | # Use relative path to the PDB file to avoid exposing the full path 93 | /PDBALTPATH:%_PDB% 94 | ) 95 | else() 96 | add_compile_options(-fsigned-char -fstack-protector-strong -fdiagnostics-color) 97 | endif() 98 | 99 | find_package(Ruby) 100 | if(RUBY_FOUND) 101 | add_custom_target(prepare 102 | COMMAND ruby prepare.rb src/*.{{h,c}pp,mm} 103 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 104 | ) 105 | endif() 106 | 107 | add_library(reaper_reapack SHARED) 108 | set_target_properties(reaper_reapack PROPERTIES 109 | PREFIX "" # disable the "lib" prefix 110 | OUTPUT_NAME "reaper_reapack-${ARCH_NAME}" 111 | ) 112 | add_subdirectory(src) 113 | target_link_libraries(reaper_reapack reapack) 114 | 115 | add_subdirectory(test) 116 | add_custom_target(test 117 | COMMAND $ 118 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 119 | USES_TERMINAL 120 | ) 121 | add_dependencies(test tests) 122 | 123 | set(REAPER_USER_PLUGINS "UserPlugins") 124 | install(TARGETS reaper_reapack 125 | COMPONENT ReaPack 126 | RUNTIME DESTINATION "${REAPER_USER_PLUGINS}" # Windows .dll 127 | LIBRARY DESTINATION "${REAPER_USER_PLUGINS}" # Linux .so/macOS .dylib 128 | ) 129 | -------------------------------------------------------------------------------- /Extensions/ReaPack.ext: -------------------------------------------------------------------------------- 1 | @description ReaPack: Package manager for REAPER 2 | @version 1.2.5.1 3 | @author cfillion 4 | @donation https://reapack.com/donate 5 | @provides 6 | [darwin32 ] reaper_reapack-i386.dylib https://github.com/cfillion/reapack/releases/download/v$version/$path 7 | [darwin64 ] reaper_reapack-x86_64.dylib https://github.com/cfillion/reapack/releases/download/v$version/$path 8 | [darwin-arm64 ] reaper_reapack-arm64.dylib https://github.com/cfillion/reapack/releases/download/v$version/$path 9 | [linux32 ] reaper_reapack-i686.so https://github.com/cfillion/reapack/releases/download/v$version/$path 10 | [linux64 ] reaper_reapack-x86_64.so https://github.com/cfillion/reapack/releases/download/v$version/$path 11 | [linux-armv7l ] reaper_reapack-armv7l.so https://github.com/cfillion/reapack/releases/download/v$version/$path 12 | [linux-aarch64 ] reaper_reapack-aarch64.so https://github.com/cfillion/reapack/releases/download/v$version/$path 13 | [win32 ] reaper_reapack-x86.dll https://github.com/cfillion/reapack/releases/download/v$version/$path 14 | [win64 ] reaper_reapack-x64.dll https://github.com/cfillion/reapack/releases/download/v$version/$path 15 | [windows-arm64ec] reaper_reapack-arm64ec.dll https://github.com/cfillion/reapack/releases/download/v$version/$path 16 | @changelog 17 | • Check SSL certificate revocation servers on a best-efforts basis instead of strictly failing downloads on Windows [#90] 18 | • Lift 4096 character limit from user input when importing repositories 19 | • Propose to automatically download files from reapack.com for users impacted by GitHub's updated rate limits (error 429) 20 | -------------------------------------------------------------------------------- /cmake/FindMiniZip.cmake: -------------------------------------------------------------------------------- 1 | if(MiniZip_FOUND) 2 | return() 3 | endif() 4 | 5 | find_package(WDL REQUIRED) 6 | find_path(MiniZip_INCLUDE_DIR 7 | NAMES unzip.h 8 | PATHS ${WDL_DIR} 9 | PATH_SUFFIXES zlib 10 | NO_DEFAULT_PATH 11 | ) 12 | mark_as_advanced(MiniZip_INCLUDE_DIR) 13 | 14 | include(FindPackageHandleStandardArgs) 15 | find_package_handle_standard_args(MiniZip REQUIRED_VARS MiniZip_INCLUDE_DIR) 16 | 17 | add_library(minizip 18 | ${MiniZip_INCLUDE_DIR}/ioapi.c 19 | ${MiniZip_INCLUDE_DIR}/unzip.c 20 | ${MiniZip_INCLUDE_DIR}/zip.c 21 | ) 22 | 23 | target_include_directories(minizip INTERFACE ${MiniZip_INCLUDE_DIR}) 24 | 25 | find_package(ZLIB REQUIRED) 26 | target_link_libraries(minizip ZLIB::ZLIB) 27 | 28 | add_library(MiniZip::MiniZip ALIAS minizip) 29 | -------------------------------------------------------------------------------- /cmake/FindPHP.cmake: -------------------------------------------------------------------------------- 1 | find_program(PHP_EXECUTABLE php) 2 | mark_as_advanced(PHP_EXECUTABLE) 3 | 4 | execute_process( 5 | COMMAND ${PHP_EXECUTABLE} -v 6 | OUTPUT_VARIABLE PHP_VERSION_OUTPUT 7 | ERROR_QUIET 8 | OUTPUT_STRIP_TRAILING_WHITESPACE 9 | ) 10 | 11 | if(PHP_VERSION_OUTPUT MATCHES "PHP ([^ ]+) ") 12 | set(PHP_VERSION "${CMAKE_MATCH_1}") 13 | endif() 14 | 15 | include(FindPackageHandleStandardArgs) 16 | find_package_handle_standard_args(PHP 17 | REQUIRED_VARS PHP_EXECUTABLE 18 | VERSION_VAR PHP_VERSION 19 | ) 20 | -------------------------------------------------------------------------------- /cmake/FindSWELL.cmake: -------------------------------------------------------------------------------- 1 | if(SWELL_FOUND) 2 | return() 3 | endif() 4 | 5 | find_package(WDL REQUIRED) 6 | 7 | find_path(SWELL_INCLUDE_DIR 8 | NAMES swell/swell.h 9 | PATHS ${WDL_DIR} 10 | NO_DEFAULT_PATH 11 | ) 12 | mark_as_advanced(SWELL_INCLUDE_DIR) 13 | 14 | set(SWELL_DIR "${SWELL_INCLUDE_DIR}/swell") 15 | set(SWELL_RESGEN "${SWELL_DIR}/swell_resgen.php") 16 | 17 | include(FindPackageHandleStandardArgs) 18 | find_package_handle_standard_args(SWELL REQUIRED_VARS SWELL_DIR) 19 | 20 | add_library(swell ${SWELL_DIR}/swell-modstub$,.mm,-generic.cpp>) 21 | 22 | if(APPLE) 23 | find_library(APPKIT AppKit) 24 | mark_as_advanced(APPKIT) 25 | target_link_libraries(swell PUBLIC ${APPKIT}) 26 | endif() 27 | 28 | target_compile_definitions(swell PUBLIC SWELL_PROVIDED_BY_APP) 29 | target_include_directories(swell INTERFACE ${SWELL_INCLUDE_DIR}) 30 | 31 | add_library(SWELL::swell ALIAS swell) 32 | -------------------------------------------------------------------------------- /cmake/FindWDL.cmake: -------------------------------------------------------------------------------- 1 | if(WDL_FOUND) 2 | return() 3 | endif() 4 | 5 | find_path(WDL_INCLUDE_DIR 6 | NAMES WDL/wdltypes.h 7 | PATHS ${CMAKE_SOURCE_DIR}/vendor/WDL 8 | NO_DEFAULT_PATH 9 | ) 10 | mark_as_advanced(WDL_INCLUDE_DIR) 11 | 12 | set(WDL_DIR "${WDL_INCLUDE_DIR}/WDL") 13 | 14 | include(FindPackageHandleStandardArgs) 15 | find_package_handle_standard_args(WDL REQUIRED_VARS WDL_DIR WDL_INCLUDE_DIR) 16 | 17 | add_library(wdl ${WDL_DIR}/wingui/wndsize.cpp) 18 | 19 | target_compile_definitions(wdl INTERFACE WDL_NO_DEFINE_MINMAX) 20 | target_include_directories(wdl INTERFACE ${WDL_INCLUDE_DIR}) 21 | 22 | if(NOT WIN32) 23 | find_package(SWELL REQUIRED) 24 | target_link_libraries(wdl SWELL::swell) 25 | endif() 26 | 27 | add_library(WDL::WDL ALIAS wdl) 28 | -------------------------------------------------------------------------------- /cmake/brew-llvm.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_C_COMPILER /usr/local/opt/llvm/bin/clang) 2 | set(CMAKE_CXX_COMPILER /usr/local/opt/llvm/bin/clang++) 3 | set(CMAKE_PREFIX_PATH /usr/local/opt/llvm) 4 | -------------------------------------------------------------------------------- /cmake/linux-cross.cmake: -------------------------------------------------------------------------------- 1 | if(NOT DEFINED ENV{ARCH}) 2 | message(FATAL_ERROR "The ARCH environment variable is not set.") 3 | endif() 4 | 5 | set(CMAKE_SYSTEM_NAME Linux) 6 | set(CMAKE_SYSTEM_PROCESSOR $ENV{ARCH}) 7 | 8 | if($ENV{ARCH} STREQUAL "i686") 9 | set(CMAKE_C_FLAGS -m32) 10 | set(CMAKE_CXX_FLAGS -m32) 11 | elseif(NOT DEFINED ENV{TOOLCHAIN_PREFIX}) 12 | message(FATAL_ERROR "The TOOLCHAIN_PREFIX environment variable is not set.") 13 | else() 14 | set(CMAKE_C_COMPILER $ENV{TOOLCHAIN_PREFIX}-gcc) 15 | set(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN_PREFIX}-g++) 16 | endif() 17 | 18 | if(DEFINED ENV{TOOLCHAIN_PREFIX}) 19 | set(CMAKE_FIND_ROOT_PATH /usr/$ENV{TOOLCHAIN_PREFIX}) 20 | set(CMAKE_LIBRARY_PATH /usr/lib/$ENV{TOOLCHAIN_PREFIX}) 21 | set(CMAKE_INCLUDE_PATH /usr/include/$ENV{TOOLCHAIN_PREFIX}) 22 | endif() 23 | -------------------------------------------------------------------------------- /cmake/vcpkg-triplets/arch-env.cmake: -------------------------------------------------------------------------------- 1 | if(NOT DEFINED ENV{ARCH}) 2 | message(FATAL_ERROR "The ARCH environment variable is not set.") 3 | endif() 4 | 5 | if($ENV{ARCH} STREQUAL "x86_64") 6 | set(VCPKG_TARGET_ARCHITECTURE x64) 7 | elseif($ENV{ARCH} MATCHES "^i[36]86$") 8 | set(VCPKG_TARGET_ARCHITECTURE x86) 9 | elseif($ENV{ARCH} STREQUAL "armv7l") 10 | set(VCPKG_TARGET_ARCHITECTURE arm) 11 | elseif($ENV{ARCH} STREQUAL "aarch64") 12 | set(VCPKG_TARGET_ARCHITECTURE arm64) 13 | else() 14 | set(VCPKG_TARGET_ARCHITECTURE $ENV{ARCH}) 15 | endif() 16 | set(VCPKG_BUILD_TYPE release) 17 | set(VCPKG_CRT_LINKAGE dynamic) 18 | set(VCPKG_LIBRARY_LINKAGE static) 19 | 20 | set(VCPKG_CMAKE_SYSTEM_NAME ${CMAKE_HOST_SYSTEM_NAME}) 21 | set(VCPKG_OSX_ARCHITECTURES $ENV{ARCH}) 22 | set(VCPKG_OSX_DEPLOYMENT_TARGET $ENV{DEPLOY_TARGET}) 23 | -------------------------------------------------------------------------------- /cmake/vcpkg-triplets/arm64ec-windows-static.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE arm64ec) 2 | set(VCPKG_CRT_LINKAGE static) 3 | set(VCPKG_LIBRARY_LINKAGE static) 4 | -------------------------------------------------------------------------------- /prepare.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | Signal.trap('INT') { abort } 4 | 5 | REGEX = /\A\/\*.+?\*\/\n/m.freeze 6 | PREAMBLE = DATA.read.freeze 7 | 8 | def process(input) 9 | if input.start_with? '/' 10 | before = input.hash 11 | input.sub! REGEX, PREAMBLE 12 | input if input.hash != before 13 | else 14 | input.prepend "#{PREAMBLE}\n" 15 | end 16 | end 17 | 18 | ARGV.each do |path| 19 | File.open path, 'r+t' do |file| 20 | output = process file.read 21 | next unless output 22 | 23 | file.rewind 24 | file.write output 25 | file.truncate file.pos 26 | end 27 | end 28 | 29 | __END__ 30 | /* ReaPack: Package manager for REAPER 31 | * Copyright (C) 2015-2025 Christian Fillion 32 | * 33 | * This program is free software: you can redistribute it and/or modify 34 | * it under the terms of the GNU Lesser General Public License as published by 35 | * the Free Software Foundation, either version 3 of the License, or 36 | * (at your option) any later version. 37 | * 38 | * This program is distributed in the hope that it will be useful, 39 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 40 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 41 | * GNU Lesser General Public License for more details. 42 | * 43 | * You should have received a copy of the GNU Lesser General Public License 44 | * along with this program. If not, see . 45 | */ 46 | -------------------------------------------------------------------------------- /src/action.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "action.hpp" 19 | 20 | #include 21 | 22 | Action::Action(const char *name, const char *desc, const Callback &callback) 23 | : m_name(name), m_reg{}, m_callback(callback) 24 | { 25 | m_reg.accel.cmd = plugin_register("command_id", const_cast(m_name)); 26 | m_reg.desc = desc; 27 | 28 | plugin_register("gaccel", &m_reg); 29 | } 30 | 31 | Action::~Action() 32 | { 33 | plugin_register("-gaccel", &m_reg); 34 | plugin_register("-command_id", const_cast(m_name)); 35 | } 36 | 37 | Action *ActionList::find(const Action::CommandID id) const 38 | { 39 | const auto it = m_list.find(id); 40 | return it != m_list.end() ? it->second.get() : nullptr; 41 | } 42 | 43 | bool ActionList::run(const Action::CommandID id) const 44 | { 45 | if(Action *action = find(id)) { 46 | action->run(); 47 | return true; 48 | } 49 | 50 | return false; 51 | } 52 | -------------------------------------------------------------------------------- /src/action.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_ACTION_HPP 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | class Action { 27 | public: 28 | typedef unsigned short CommandID; 29 | typedef std::function Callback; 30 | 31 | Action(const char *name, const char *desc, const Callback &); 32 | Action(const Action &) = delete; 33 | ~Action(); 34 | 35 | CommandID id() const { return m_reg.accel.cmd; } 36 | void run() const { m_callback(); } 37 | 38 | private: 39 | const char *m_name; 40 | gaccel_register_t m_reg; 41 | Callback m_callback; 42 | }; 43 | 44 | class ActionList { 45 | public: 46 | template void add(Args&&... args) 47 | { 48 | auto action = std::make_unique(args...); 49 | m_list.emplace(action->id(), std::move(action)); 50 | } 51 | 52 | bool run(Action::CommandID id) const; 53 | 54 | private: 55 | Action *find(Action::CommandID id) const; 56 | 57 | std::map> m_list; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/api.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "api.hpp" 19 | 20 | #include 21 | 22 | #include 23 | 24 | using namespace std::string_literals; 25 | 26 | #define KEY(prefix) (prefix "_ReaPack_"s + m_func->name) 27 | 28 | APIReg::APIReg(const APIFunc *func) 29 | : m_func(func), 30 | m_impl(KEY("API")), m_vararg(KEY("APIvararg")), m_help(KEY("APIdef")) 31 | { 32 | plugin_register(m_impl.c_str(), m_func->cImpl); 33 | plugin_register(m_vararg.c_str(), m_func->reascriptImpl); 34 | plugin_register(m_help.c_str(), m_func->definition); 35 | } 36 | 37 | APIReg::~APIReg() 38 | { 39 | plugin_register(("-" + m_impl).c_str(), m_func->cImpl); 40 | plugin_register(("-" + m_vararg).c_str(), m_func->reascriptImpl); 41 | plugin_register(("-" + m_help).c_str(), m_func->definition); 42 | } 43 | -------------------------------------------------------------------------------- /src/api.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_API_HPP 19 | #define REAPACK_API_HPP 20 | 21 | #include 22 | 23 | struct APIFunc { 24 | const char *name; 25 | void *cImpl; 26 | void *reascriptImpl; 27 | void *definition; 28 | }; 29 | 30 | class APIReg { 31 | public: 32 | APIReg(const APIFunc *); 33 | APIReg(const APIReg &) = delete; 34 | ~APIReg(); 35 | 36 | private: 37 | const APIFunc *m_func; 38 | 39 | // REAPER < 6.67 requires these strings to remain valid after registering 40 | std::string m_impl; 41 | std::string m_vararg; 42 | std::string m_help; 43 | }; 44 | 45 | namespace API { 46 | // api_misc.cpp 47 | extern APIFunc BrowsePackages; 48 | extern APIFunc CompareVersions; 49 | extern APIFunc ProcessQueue; 50 | 51 | // api_package.cpp 52 | extern APIFunc AboutInstalledPackage; 53 | extern APIFunc EnumOwnedFiles; 54 | extern APIFunc FreeEntry; 55 | extern APIFunc GetEntryInfo; 56 | extern APIFunc GetOwner; 57 | 58 | // api_repo.cpp 59 | extern APIFunc AboutRepository; 60 | extern APIFunc AddSetRepository; 61 | extern APIFunc GetRepositoryInfo; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/api_helper.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_API_HELPER_HPP 19 | #define REAPACK_API_HELPER_HPP 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | template 27 | struct ReaScriptAPI; 28 | 29 | template 30 | struct ReaScriptAPI 31 | { 32 | static void *applyVarArg(R(*fn)(Args...), void **argv, int argc) 33 | { 34 | if(static_cast(argc) < sizeof...(Args)) 35 | return nullptr; 36 | 37 | const auto &args { makeTuple(argv, std::index_sequence_for{}) }; 38 | 39 | if constexpr (std::is_void_v) { 40 | std::apply(fn, args); 41 | return nullptr; 42 | } 43 | else if constexpr (std::is_floating_point_v) { 44 | const auto value { std::apply(fn, args) }; 45 | void *storage { argv[argc - 1] }; 46 | *static_cast(storage) = value; 47 | return storage; 48 | } 49 | else { 50 | // cast numbers to have the same size as a pointer to avoid warnings 51 | using IntPtrR = std::conditional_t, R, intptr_t>; 52 | const auto value { static_cast(std::apply(fn, args)) }; 53 | return reinterpret_cast(value); 54 | } 55 | } 56 | 57 | private: 58 | template 59 | using NthType = typename std::tuple_element>::type; 60 | 61 | template 62 | static auto makeTuple(void **argv, std::index_sequence) 63 | { 64 | // C++17 is amazing 65 | return std::make_tuple( 66 | std::is_floating_point_v> ? 67 | *reinterpret_cast*>(argv[I]) : 68 | (NthType)reinterpret_cast(argv[I]) 69 | ... 70 | ); 71 | } 72 | }; 73 | 74 | template 75 | void *InvokeReaScriptAPI(void **argv, int argc) 76 | { 77 | return ReaScriptAPI::applyVarArg(fn, argv, argc); 78 | } 79 | 80 | #define ARG_TYPE(arg) BOOST_PP_TUPLE_ELEM(2, 0, arg) 81 | #define ARG_NAME(arg) BOOST_PP_TUPLE_ELEM(2, 1, arg) 82 | 83 | #define DEFARGS(r, data, i, arg) BOOST_PP_COMMA_IF(i) ARG_TYPE(arg) ARG_NAME(arg) 84 | #define DOCARGS(r, macro, i, arg) \ 85 | BOOST_PP_EXPR_IF(i, ",") BOOST_PP_STRINGIZE(macro(arg)) 86 | 87 | #define DEFINE_API(type, name, args, help, ...) \ 88 | static type API_##name(BOOST_PP_SEQ_FOR_EACH_I(DEFARGS, _, args)) __VA_ARGS__ \ 89 | \ 90 | APIFunc API::name { #name, \ 91 | reinterpret_cast(&API_##name), \ 92 | reinterpret_cast(&InvokeReaScriptAPI<&API_##name>), \ 93 | reinterpret_cast(const_cast( \ 94 | #type "\0" \ 95 | BOOST_PP_SEQ_FOR_EACH_I(DOCARGS, ARG_TYPE, args) "\0" \ 96 | BOOST_PP_SEQ_FOR_EACH_I(DOCARGS, ARG_NAME, args) "\0" \ 97 | help \ 98 | )) \ 99 | } 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/api_misc.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "api.hpp" 19 | #include "api_helper.hpp" 20 | 21 | #include "browser.hpp" 22 | #include "reapack.hpp" 23 | 24 | DEFINE_API(void, BrowsePackages, ((const char*, filter)), 25 | R"(Opens the package browser with the given filter string.)", 26 | { 27 | if(Browser *browser = g_reapack->browsePackages()) 28 | browser->setFilter(filter); 29 | }); 30 | 31 | DEFINE_API(int, CompareVersions, ((const char*, ver1))((const char*, ver2)) 32 | ((char*, errorOut))((int, errorOut_sz)), 33 | R"(Returns 0 if both versions are equal, a positive value if ver1 is higher than ver2 and a negative value otherwise.)", 34 | { 35 | VersionName a, b; 36 | std::string error; 37 | 38 | b.tryParse(ver2, &error); 39 | a.tryParse(ver1, &error); 40 | 41 | if(errorOut) 42 | snprintf(errorOut, errorOut_sz, "%s", error.c_str()); 43 | 44 | return a.compare(b); 45 | }); 46 | 47 | DEFINE_API(void, ProcessQueue, ((bool, refreshUI)), 48 | R"(Run pending operations and save the configuration file. If refreshUI is true the browser and manager windows are guaranteed to be refreshed (otherwise it depends on which operations are in the queue).)", 49 | { 50 | g_reapack->commitConfig(refreshUI); 51 | }); 52 | -------------------------------------------------------------------------------- /src/api_repo.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "api.hpp" 19 | #include "api_helper.hpp" 20 | 21 | #include "errors.hpp" 22 | #include "reapack.hpp" 23 | #include "remote.hpp" 24 | 25 | #include 26 | #include // required to get correct tribool casts 27 | 28 | DEFINE_API(bool, AboutRepository, ((const char*, repoName)), 29 | R"(Show the about dialog of the given repository. Returns true if the repository exists in the user configuration. 30 | The repository index is downloaded asynchronously if the cached copy doesn't exist or is older than one week.)", 31 | { 32 | if(const Remote &repo = g_reapack->remote(repoName)) { 33 | g_reapack->about(repo); 34 | return true; 35 | } 36 | 37 | return false; 38 | }); 39 | 40 | DEFINE_API(bool, AddSetRepository, ((const char*, name))((const char*, url)) 41 | ((bool, enable))((int, autoInstall))((char*, errorOut))((int, errorOut_sz)), 42 | R"(Add or modify a repository. Set url to nullptr (or empty string in Lua) to keep the existing URL. Call ReaPack_ProcessQueue(true) when done to process the new list and update the GUI. 43 | 44 | autoInstall: usually set to 2 (obey user setting).)", 45 | { 46 | try { 47 | Remote remote = g_reapack->remote(name); 48 | remote.setName(name); 49 | remote.setUrl(url && strlen(url) > 0 ? url : remote.url()); 50 | remote.setEnabled(enable); 51 | remote.setAutoInstall(boost::lexical_cast(autoInstall)); 52 | g_reapack->addSetRemote(remote); 53 | } 54 | catch(const reapack_error &e) { 55 | if(errorOut) 56 | snprintf(errorOut, errorOut_sz, "%s", e.what()); 57 | return false; 58 | } 59 | catch(const boost::bad_lexical_cast &) { 60 | if(errorOut) 61 | snprintf(errorOut, errorOut_sz, "invalid value for autoInstall"); 62 | return false; 63 | } 64 | 65 | return true; 66 | }); 67 | 68 | DEFINE_API(bool, GetRepositoryInfo, ((const char*, name)) 69 | ((char*, urlOut))((int, urlOut_sz)) 70 | ((bool*, enabledOut))((int*, autoInstallOut)), 71 | R"(Get the infos of the given repository. 72 | 73 | autoInstall: 0=manual, 1=when sychronizing, 2=obey user setting)", 74 | { 75 | const Remote &remote = g_reapack->remote(name); 76 | 77 | if(!remote) 78 | return false; 79 | 80 | if(urlOut) 81 | snprintf(urlOut, urlOut_sz, "%s", remote.url().c_str()); 82 | if(enabledOut) 83 | *enabledOut = remote.isEnabled(); 84 | if(autoInstallOut) 85 | *autoInstallOut = boost::lexical_cast(remote.autoInstall()); 86 | 87 | return true; 88 | }); 89 | -------------------------------------------------------------------------------- /src/archive.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_ARCHIVE_HPP 19 | #define REAPACK_ARCHIVE_HPP 20 | 21 | #include "path.hpp" 22 | #include "thread.hpp" 23 | 24 | class ThreadPool; 25 | 26 | typedef void *zipFile; 27 | 28 | namespace Archive { 29 | void import(const std::string &path); 30 | }; 31 | 32 | class ArchiveReader { 33 | public: 34 | ArchiveReader(const Path &path); 35 | ~ArchiveReader(); 36 | int extractFile(const Path &); 37 | int extractFile(const Path &, std::ostream &) noexcept; 38 | 39 | private: 40 | zipFile m_zip; 41 | }; 42 | 43 | typedef std::shared_ptr ArchiveReaderPtr; 44 | 45 | class ArchiveWriter { 46 | public: 47 | ArchiveWriter(const Path &path); 48 | ~ArchiveWriter(); 49 | int addFile(const Path &fn); 50 | int addFile(const Path &fn, std::istream &) noexcept; 51 | 52 | private: 53 | zipFile m_zip; 54 | }; 55 | 56 | typedef std::shared_ptr ArchiveWriterPtr; 57 | 58 | class FileExtractor : public ThreadTask { 59 | public: 60 | FileExtractor(const Path &target, const ArchiveReaderPtr &); 61 | const TempPath &path() const { return m_path; } 62 | 63 | bool concurrent() const override { return false; } 64 | bool run() override; 65 | 66 | private: 67 | TempPath m_path; 68 | ArchiveReaderPtr m_reader; 69 | }; 70 | 71 | class FileCompressor : public ThreadTask { 72 | public: 73 | FileCompressor(const Path &target, const ArchiveWriterPtr &); 74 | const Path &path() const { return m_path; } 75 | 76 | bool concurrent() const override { return false; } 77 | bool run() override; 78 | 79 | private: 80 | Path m_path; 81 | ArchiveWriterPtr m_writer; 82 | }; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/archive_tasks.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "task.hpp" 19 | 20 | #include "archive.hpp" 21 | #include "config.hpp" 22 | #include "filesystem.hpp" 23 | #include "index.hpp" 24 | #include "reapack.hpp" 25 | #include "remote.hpp" 26 | #include "string.hpp" 27 | #include "transaction.hpp" 28 | 29 | #include 30 | #include 31 | 32 | static const Path ARCHIVE_TOC("toc"); 33 | 34 | ExportTask::ExportTask(const std::string &path, Transaction *tx) 35 | : Task(tx), m_path(path) 36 | { 37 | } 38 | 39 | bool ExportTask::start() 40 | { 41 | std::stringstream toc; 42 | ArchiveWriterPtr writer; 43 | 44 | try { 45 | writer = std::make_shared(m_path.temp()); 46 | } 47 | catch(const reapack_error &e) { 48 | tx()->receipt()->addError({ 49 | String::format("Could not open archive for writing: %s", e.what()), 50 | m_path.temp().join() 51 | }); 52 | return false; 53 | } 54 | 55 | std::vector jobs; 56 | 57 | for(const Remote &remote : g_reapack->config()->remotes.getEnabled()) { 58 | bool addedRemote = false; 59 | 60 | for(const Registry::Entry &entry : tx()->registry()->getEntries(remote.name())) { 61 | if(!addedRemote) { 62 | toc << "REPO " << remote.toString() << '\n'; 63 | jobs.push_back(new FileCompressor(Index::pathFor(remote.name()), writer)); 64 | addedRemote = true; 65 | } 66 | 67 | toc << "PACK " 68 | << quoted(entry.category) << '\x20' 69 | << quoted(entry.package) << '\x20' 70 | << quoted(entry.version.toString()) << '\x20' 71 | << entry.flags << '\n' 72 | ; 73 | 74 | for(const Registry::File &file : tx()->registry()->getFiles(entry)) 75 | jobs.push_back(new FileCompressor(file.path, writer)); 76 | } 77 | } 78 | 79 | writer->addFile(ARCHIVE_TOC, toc); 80 | 81 | // Start after we've written the table of contents in the main thread 82 | // because we cannot safely write into the zip from more than one 83 | // thread at the same time. 84 | for(FileCompressor *job : jobs) { 85 | job->onFinishAsync >> [=] { 86 | if(job->state() == ThreadTask::Success) 87 | tx()->receipt()->addExport(job->path()); 88 | }; 89 | 90 | tx()->threadPool()->push(job); 91 | } 92 | 93 | return true; 94 | } 95 | 96 | void ExportTask::commit() 97 | { 98 | if(!FS::rename(m_path)) { 99 | tx()->receipt()->addError({ 100 | String::format("Could not move to permanent location: %s", FS::lastError()), 101 | m_path.target().prependRoot().join() 102 | }); 103 | } 104 | } 105 | 106 | void ExportTask::rollback() 107 | { 108 | FS::remove(m_path.temp()); 109 | } 110 | -------------------------------------------------------------------------------- /src/browser_entry.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_BROWSER_ENTRY_HPP 19 | #define REAPACK_BROWSER_ENTRY_HPP 20 | 21 | #include "browser.hpp" 22 | #include "listview.hpp" 23 | #include "registry.hpp" 24 | 25 | #include 26 | #include 27 | 28 | class Index; 29 | class Menu; 30 | class Remote; 31 | 32 | typedef std::shared_ptr IndexPtr; 33 | 34 | class Browser::Entry { 35 | public: 36 | enum Flag { 37 | UninstalledFlag = 1<<0, 38 | InstalledFlag = 1<<1, 39 | OutOfDateFlag = 1<<2, 40 | ObsoleteFlag = 1<<3, 41 | ProtectedFlag = 1<<4, 42 | }; 43 | 44 | enum PossibleAction { 45 | CanInstallLatest = 1<<0, 46 | CanReinstall = 1<<1, 47 | CanUninstall = 1<<2, 48 | CanClearQueued = 1<<3, 49 | 50 | CanToggleFlags = 1<<10, 51 | }; 52 | 53 | Entry(const Package *, const Registry::Entry &, const IndexPtr &); 54 | Entry(const Registry::Entry &, const IndexPtr &); 55 | 56 | std::optional target; 57 | std::optional flags; 58 | 59 | std::string displayState() const; 60 | const std::string &indexName() const; 61 | const std::string &categoryName() const; 62 | const std::string &packageName() const; 63 | std::string displayName() const; 64 | Package::Type type() const; 65 | std::string displayType() const; 66 | std::string displayVersion() const; 67 | const VersionName *sortVersion() const; 68 | std::string displayAuthor() const; 69 | const Time *lastUpdate() const; 70 | 71 | void updateRow(ListView::Row *) const; 72 | void fillMenu(Menu &) const; 73 | 74 | int possibleActions(bool allowToggle) const; 75 | bool test(Flag f) const { return (m_flags & f) == f; } 76 | bool test(PossibleAction f, bool allowToggle = true) const { 77 | return (possibleActions(allowToggle) & f) == f; } 78 | bool operator==(const Entry &o) const; 79 | 80 | private: 81 | int m_flags; 82 | 83 | public: 84 | Registry::Entry regEntry; 85 | const Package *package; 86 | IndexPtr index; 87 | const Version *current; 88 | const Version *latest; 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/buildinfo.hpp.in: -------------------------------------------------------------------------------- 1 | #define REAPACK_FILENAME "@REAPACK_FILENAME@" 2 | #define REAPACK_BASENAME "@REAPACK_BASENAME@" 3 | #define REAPACK_VERSION "@REAPACK_VERSION@" 4 | #define REAPACK_REVISION "@REAPACK_REVISION@" 5 | #define REAPACK_BUILDTIME "@REAPACK_BUILDTIME@" 6 | #define REAPACK_COPYRIGHT "@REAPACK_COPYRIGHT@" 7 | 8 | #define REAPACK_VERSION_MAJOR @REAPACK_VERSION_MAJOR@ 9 | #define REAPACK_VERSION_MINOR @REAPACK_VERSION_MINOR@ 10 | #define REAPACK_VERSION_PATCH @REAPACK_VERSION_PATCH@ 11 | #define REAPACK_VERSION_TWEAK @REAPACK_VERSION_TWEAK@ 12 | -------------------------------------------------------------------------------- /src/buildinfo.rc: -------------------------------------------------------------------------------- 1 | #include "buildinfo.hpp" 2 | 3 | #include 4 | 5 | #define VERSION_ARRAY \ 6 | REAPACK_VERSION_MAJOR, REAPACK_VERSION_MINOR, REAPACK_VERSION_PATCH, \ 7 | REAPACK_VERSION_TWEAK 8 | 9 | // https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource 10 | 11 | VS_VERSION_INFO VERSIONINFO 12 | FILEVERSION VERSION_ARRAY 13 | PRODUCTVERSION VERSION_ARRAY 14 | BEGIN 15 | BLOCK "StringFileInfo" 16 | BEGIN 17 | BLOCK "040904B0" 18 | BEGIN 19 | VALUE "ProductName", "ReaPack" 20 | VALUE "CompanyName", "https://reapack.com" 21 | VALUE "FileDescription", "REAPER plug-in extension" 22 | VALUE "FileVersion", REAPACK_VERSION "-" REAPACK_REVISION 23 | VALUE "ProductVersion", REAPACK_VERSION 24 | VALUE "OriginalFilename", REAPACK_FILENAME 25 | VALUE "InternalName", REAPACK_BASENAME 26 | VALUE "LegalCopyright", REAPACK_COPYRIGHT 27 | END 28 | END 29 | 30 | BLOCK "VarFileInfo" 31 | BEGIN 32 | // Links to the block in the StringFileInfo structure above 33 | VALUE "Translation", 0x0409, 0x04B0 34 | END 35 | END 36 | -------------------------------------------------------------------------------- /src/config.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_CONFIG_HPP 19 | #define REAPACK_CONFIG_HPP 20 | 21 | #include "remote.hpp" 22 | 23 | #include 24 | 25 | class Path; 26 | 27 | struct WindowState { 28 | std::string about; 29 | std::string browser; 30 | std::string manager; 31 | }; 32 | 33 | struct InstallOpts { 34 | bool autoInstall; 35 | bool bleedingEdge; 36 | bool promptObsolete; 37 | }; 38 | 39 | struct NetworkOpts { 40 | enum StaleThreshold { 41 | NoThreshold = 0, 42 | OneWeekThreshold = 7 * 24 * 3600, 43 | }; 44 | 45 | std::string proxy; 46 | bool verifyPeer; 47 | time_t staleThreshold; 48 | boost::logic::tribool fallbackProxy; 49 | }; 50 | 51 | struct FilterOpts { 52 | bool expandSynonyms; 53 | }; 54 | 55 | class Config { 56 | public: 57 | Config(const Path &); 58 | Config(const Config &) = delete; 59 | ~Config(); 60 | 61 | void read(); 62 | void write(); 63 | 64 | void resetOptions(); 65 | void restoreDefaultRemotes(); 66 | 67 | bool isFirstRun() const { return m_isFirstRun; } 68 | 69 | InstallOpts install; 70 | NetworkOpts network; 71 | FilterOpts filter; 72 | WindowState windowState; 73 | 74 | RemoteList remotes; 75 | 76 | private: 77 | std::string getString(const char *g, const char *k, const std::string &fallback = {}) const; 78 | void setString(const char *g, const char *k, const std::string &v) const; 79 | 80 | bool getBool(const char *g, const char *k, bool fallback = false) const; 81 | unsigned int getUInt(const char *g, const char *k, unsigned int fallback = 0) const; 82 | void setUInt(const char *g, const char *k, unsigned int v) const; 83 | 84 | void deleteKey(const char *g, const char *k) const; 85 | void cleanupArray(const char *g, const char *k, unsigned int begin, unsigned int end) const; 86 | 87 | void migrate(); 88 | 89 | std::string m_path; 90 | bool m_isFirstRun; 91 | unsigned int m_version; 92 | 93 | void readRemotes(); 94 | void restoreSelfRemote(); 95 | void writeRemotes(); 96 | unsigned int m_remotesIniSize; 97 | }; 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/control.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "control.hpp" 19 | 20 | #include 21 | 22 | void InhibitControl::setRedraw(const bool inhibit) 23 | { 24 | static std::map s_lock; 25 | 26 | auto owner = s_lock.find(m_handle); 27 | 28 | if(owner != s_lock.end()) { 29 | if(inhibit || owner->second != this) 30 | return; 31 | } 32 | else if(!inhibit) 33 | return; 34 | 35 | SendMessage(m_handle, WM_SETREDRAW, !inhibit, 0); 36 | 37 | if(inhibit) 38 | s_lock.insert({m_handle, this}); 39 | else { 40 | s_lock.erase(owner); 41 | #ifdef _WIN32 42 | RedrawWindow(m_handle, nullptr, nullptr, 43 | RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); 44 | #endif 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/control.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_CONTROL_HPP 19 | #define REAPACK_CONTROL_HPP 20 | 21 | #ifdef _WIN32 22 | # include 23 | #else 24 | # include 25 | #endif 26 | 27 | class Dialog; 28 | 29 | class Control { 30 | public: 31 | Control(HWND handle) : m_handle(handle) {} 32 | virtual ~Control() = default; 33 | 34 | HWND handle() const { return m_handle; } 35 | 36 | protected: 37 | friend Dialog; 38 | 39 | virtual void onNotify(LPNMHDR, LPARAM) {} 40 | virtual bool onContextMenu(HWND, int, int) { return false; } 41 | 42 | private: 43 | HWND m_handle; 44 | }; 45 | 46 | class InhibitControl { 47 | public: 48 | InhibitControl(Control *ctrl) : InhibitControl(ctrl->handle()) {} 49 | InhibitControl(HWND handle) : m_handle(handle) { setRedraw(true); } 50 | ~InhibitControl() { setRedraw(false); } 51 | 52 | private: 53 | void setRedraw(const bool inhibit); 54 | 55 | HWND m_handle; 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/database.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_DATABASE_HPP 19 | #define REAPACK_DATABASE_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | class reapack_error; 27 | 28 | struct sqlite3; 29 | struct sqlite3_stmt; 30 | 31 | class Statement; 32 | 33 | class Database { 34 | public: 35 | struct Version { 36 | int16_t major; 37 | int16_t minor; 38 | 39 | operator bool() const { return major || minor; } 40 | 41 | bool operator<(const Version &o) const 42 | { 43 | return major == o.major ? minor < o.minor : major < o.major; 44 | } 45 | }; 46 | 47 | Database(const std::string &filename = {}); 48 | ~Database(); 49 | 50 | Statement *prepare(const char *sql); 51 | void exec(const char *sql); 52 | int64_t lastInsertId() const; 53 | Version version() const; 54 | void setVersion(const Version &); 55 | int errorCode() const; 56 | 57 | void begin(); 58 | void commit(); 59 | void savepoint(); 60 | void restore(); 61 | void release(); 62 | 63 | private: 64 | friend Statement; 65 | 66 | reapack_error lastError() const; 67 | 68 | sqlite3 *m_db; 69 | std::vector m_statements; 70 | size_t m_savePoint; 71 | }; 72 | 73 | class Statement { 74 | public: 75 | typedef std::function ExecCallback; 76 | 77 | Statement(const char *sql, const Database *db); 78 | ~Statement(); 79 | 80 | void bind(int index, const std::string &text); 81 | void bind(int index, int64_t integer); 82 | void exec(); 83 | void exec(const ExecCallback &); 84 | 85 | int64_t intColumn(int index) const; 86 | bool boolColumn(int index) const { return intColumn(index) != 0; } 87 | std::string stringColumn(int index) const; 88 | 89 | private: 90 | friend Database; 91 | 92 | const Database *m_db; 93 | sqlite3_stmt *m_stmt; 94 | }; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/dialog.mm: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "dialog.hpp" 19 | 20 | #include 21 | #include 22 | 23 | bool Dialog::isTextEditUnderMouse() const 24 | { 25 | POINT p; 26 | GetCursorPos(&p); 27 | const HWND ctrl = WindowFromPoint(p); 28 | 29 | return ctrl && [static_cast(ctrl) isKindOfClass:[NSTextView class]]; 30 | } 31 | -------------------------------------------------------------------------------- /src/dllimport.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_DLLIMPORT_HPP 19 | #define REAPACK_DLLIMPORT_HPP 20 | 21 | #ifndef _WIN32 22 | # error This file should not be included on non-Windows systems. 23 | #endif 24 | 25 | #include 26 | 27 | template>> 28 | class DllImport { 29 | public: 30 | DllImport(const wchar_t *dll, const char *func) 31 | { 32 | if (m_lib = LoadLibrary(dll)) 33 | m_proc = reinterpret_cast(GetProcAddress(m_lib, func)); 34 | else 35 | m_proc = nullptr; 36 | } 37 | 38 | ~DllImport() 39 | { 40 | if (m_lib) 41 | FreeLibrary(m_lib); 42 | } 43 | 44 | operator bool() const { return m_proc != nullptr; } 45 | 46 | template 47 | auto operator()(Args&&... args) const { return m_proc(std::forward(args)...); } 48 | 49 | private: 50 | HINSTANCE m_lib; 51 | Proc *m_proc; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/download.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_DOWNLOAD_HPP 19 | #define REAPACK_DOWNLOAD_HPP 20 | 21 | #include "config.hpp" 22 | #include "path.hpp" 23 | #include "thread.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | class Hash; 31 | 32 | class DownloadContext { 33 | public: 34 | static void GlobalInit(); 35 | static void GlobalCleanup(); 36 | 37 | DownloadContext(); 38 | ~DownloadContext(); 39 | 40 | operator CURL*() { return m_curl; } 41 | 42 | private: 43 | CURL *m_curl; 44 | }; 45 | 46 | class Download : public ThreadTask { 47 | public: 48 | enum Flag { 49 | NoCacheFlag = 1<<0, 50 | }; 51 | 52 | Download(const std::string &url, const NetworkOpts &, int flags = 0); 53 | 54 | void setName(const std::string &); 55 | void setExpectedChecksum(const std::string &checksum) { 56 | m_expectedChecksum = checksum; 57 | } 58 | const std::string &url() const { return m_url; } 59 | 60 | bool concurrent() const override { return true; } 61 | bool run() override { return run(false); } 62 | bool run(bool proxy); 63 | 64 | AsyncEvent onRequestProxyAsync; 65 | 66 | protected: 67 | virtual std::ostream *openStream() = 0; 68 | virtual void closeStream() {} 69 | 70 | private: 71 | struct WriteContext { 72 | std::ostream *stream; 73 | std::unique_ptr hash; 74 | 75 | void write(const char *data, size_t len); 76 | bool checkChecksum(const std::string &expected) const; 77 | }; 78 | 79 | bool has(Flag f) const { return (m_flags & f) != 0; } 80 | static size_t WriteData(char *, size_t, size_t, void *); 81 | static int UpdateProgress(void *, double, double, double, double); 82 | 83 | std::string m_url; 84 | std::string m_expectedChecksum; 85 | NetworkOpts m_opts; 86 | int m_flags; 87 | }; 88 | 89 | class MemoryDownload : public Download { 90 | public: 91 | MemoryDownload(const std::string &url, const NetworkOpts &, int flags = 0); 92 | 93 | std::string contents() const { return m_stream.str(); } 94 | 95 | protected: 96 | std::ostream *openStream() override { return &m_stream; } 97 | 98 | private: 99 | std::stringstream m_stream; 100 | }; 101 | 102 | class FileDownload : public Download { 103 | public: 104 | FileDownload(const Path &target, const std::string &url, 105 | const NetworkOpts &, int flags = 0); 106 | 107 | const TempPath &path() const { return m_path; } 108 | bool save(); 109 | 110 | protected: 111 | std::ostream *openStream() override; 112 | void closeStream() override; 113 | 114 | private: 115 | TempPath m_path; 116 | std::ofstream m_stream; 117 | }; 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /src/errors.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_ERRORS_HPP 19 | #define REAPACK_ERRORS_HPP 20 | 21 | #include 22 | #include 23 | 24 | #include "string.hpp" 25 | 26 | class reapack_error : public std::runtime_error { 27 | public: 28 | using runtime_error::runtime_error; 29 | }; 30 | 31 | struct ErrorInfo { 32 | std::string message; 33 | std::string context; 34 | }; 35 | 36 | inline std::ostream &operator<<(std::ostream &os, const ErrorInfo &err) 37 | { 38 | os << err.context << ":\r\n" << String::indent(err.message) << "\r\n"; 39 | return os; 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/event.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "event.hpp" 19 | 20 | #include 21 | 22 | static std::weak_ptr s_loop; 23 | 24 | AsyncEventImpl::Loop::Loop() 25 | { 26 | plugin_register("timer", reinterpret_cast(&mainThreadTimer)); 27 | } 28 | 29 | AsyncEventImpl::Loop::~Loop() 30 | { 31 | plugin_register("-timer", reinterpret_cast(&mainThreadTimer)); 32 | } 33 | 34 | void AsyncEventImpl::Loop::mainThreadTimer() 35 | { 36 | s_loop.lock()->processQueue(); 37 | } 38 | 39 | void AsyncEventImpl::Loop::push(const MainThreadFunc &event, const void *source) 40 | { 41 | std::lock_guard guard(m_mutex); 42 | m_queue.insert({source, event}); 43 | } 44 | 45 | void AsyncEventImpl::Loop::forget(const void *source) 46 | { 47 | std::lock_guard guard(m_mutex); 48 | m_queue.erase(source); 49 | } 50 | 51 | void AsyncEventImpl::Loop::processQueue() 52 | { 53 | decltype(m_queue) events; 54 | 55 | { 56 | std::lock_guard guard(m_mutex); 57 | std::swap(events, m_queue); 58 | } 59 | 60 | for(const auto &[emitter, func] : events) 61 | func(), (void)emitter; 62 | } 63 | 64 | AsyncEventImpl::Emitter::Emitter() 65 | { 66 | if(s_loop.expired()) 67 | s_loop = m_loop = std::make_shared(); 68 | else 69 | m_loop = s_loop.lock(); 70 | } 71 | 72 | AsyncEventImpl::Emitter::~Emitter() 73 | { 74 | if(s_loop.use_count() > 1) 75 | m_loop->forget(this); 76 | } 77 | 78 | void AsyncEventImpl::Emitter::runInMainThread(const MainThreadFunc &event) const 79 | { 80 | m_loop->push(event, this); 81 | } 82 | -------------------------------------------------------------------------------- /src/event.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_EVENT_HPP 19 | #define REAPACK_EVENT_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | template 30 | class Event; 31 | 32 | template 33 | class Event { 34 | public: 35 | using Handler = std::function; 36 | using ReturnType = std::conditional_t< 37 | std::is_void_v, void, std::optional 38 | >; 39 | 40 | Event() = default; 41 | Event(const Event &) = delete; 42 | 43 | operator bool() const { return !m_handlers.empty(); } 44 | void reset() { m_handlers.clear(); } 45 | 46 | Event &operator>>(const Handler &func) 47 | { 48 | m_handlers.push_back(func); 49 | return *this; 50 | } 51 | 52 | ReturnType operator()(Args... args) const 53 | { 54 | if constexpr (std::is_void_v) { 55 | for(const auto &func : m_handlers) 56 | func(std::forward(args)...); 57 | } 58 | else { 59 | ReturnType ret; 60 | for(const auto &func : m_handlers) 61 | ret = func(std::forward(args)...); 62 | return ret; 63 | } 64 | } 65 | 66 | private: 67 | std::vector m_handlers; 68 | }; 69 | 70 | namespace AsyncEventImpl { 71 | using MainThreadFunc = std::function; 72 | 73 | class Loop { 74 | public: 75 | Loop(); 76 | ~Loop(); 77 | 78 | void push(const MainThreadFunc &, const void *source = nullptr); 79 | void forget(const void *source); 80 | 81 | private: 82 | static void mainThreadTimer(); 83 | void processQueue(); 84 | 85 | std::mutex m_mutex; 86 | std::multimap m_queue; 87 | }; 88 | 89 | class Emitter { 90 | public: 91 | Emitter(); 92 | ~Emitter(); 93 | 94 | void runInMainThread(const MainThreadFunc &) const; 95 | 96 | private: 97 | std::shared_ptr m_loop; 98 | }; 99 | }; 100 | 101 | template 102 | class AsyncEvent; 103 | 104 | template 105 | class AsyncEvent : public Event { 106 | public: 107 | using typename Event::ReturnType; 108 | 109 | std::future operator()(Args... args) const 110 | { 111 | auto promise = std::make_shared>(); 112 | 113 | // don't wait until the next timer tick to return nothing if there are no 114 | // handlers currently subscribed to the event 115 | if(!*this) { 116 | if constexpr (std::is_void_v) 117 | promise->set_value(); 118 | else 119 | promise->set_value(std::nullopt); 120 | 121 | return promise->get_future(); 122 | } 123 | 124 | m_emitter.runInMainThread([=] { 125 | if constexpr (std::is_void_v) { 126 | Event::operator()(args...); 127 | promise->set_value(); 128 | } 129 | else 130 | promise->set_value(Event::operator()(args...)); 131 | }); 132 | 133 | return promise->get_future(); 134 | } 135 | 136 | private: 137 | AsyncEventImpl::Emitter m_emitter; 138 | }; 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /src/filedialog.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "filedialog.hpp" 19 | 20 | #include "path.hpp" 21 | #include "win32.hpp" 22 | 23 | #ifndef _WIN32 24 | # include 25 | #endif 26 | 27 | #ifdef _WIN32 28 | static std::string getFileName(BOOL(__stdcall *func)(LPOPENFILENAME), 29 | HWND parent, HINSTANCE instance, const char *title, const Path &initialDir, 30 | const Win32::char_type *filters, const Win32::char_type *defaultExt) 31 | { 32 | const auto &&wideTitle = Win32::widen(title); 33 | const auto &&wideInitialDir = Win32::widen(initialDir.join()); 34 | 35 | wchar_t pathBuffer[4096] = {}; 36 | 37 | OPENFILENAME of{sizeof(of), parent, instance}; 38 | of.lpstrFilter = filters; 39 | of.lpstrFile = pathBuffer; 40 | of.nMaxFile = static_cast(std::size(pathBuffer)); 41 | of.lpstrInitialDir = wideInitialDir.c_str(); 42 | of.lpstrTitle = wideTitle.c_str(); 43 | of.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT; 44 | of.lpstrDefExt = defaultExt; 45 | 46 | return func(&of) ? Win32::narrow(pathBuffer) : std::string{}; 47 | } 48 | #endif 49 | 50 | std::string FileDialog::getOpenFileName(HWND parent, HINSTANCE instance, 51 | const char *title, const Path &initialDir, 52 | const Win32::char_type *filters, const Win32::char_type *defaultExt) 53 | { 54 | #ifdef _WIN32 55 | return getFileName(&GetOpenFileName, parent, instance, 56 | title, initialDir, filters, defaultExt); 57 | #else 58 | const char *path = BrowseForFiles(title, initialDir.join().c_str(), 59 | nullptr, false, filters); 60 | return path ? path : std::string{}; 61 | #endif 62 | } 63 | 64 | std::string FileDialog::getSaveFileName(HWND parent, HINSTANCE instance, 65 | const char *title, const Path &initialDir, 66 | const Win32::char_type *filters, const Win32::char_type *defaultExt) 67 | { 68 | #ifdef _WIN32 69 | return getFileName(&GetSaveFileName, parent, instance, 70 | title, initialDir, filters, defaultExt); 71 | #else 72 | char path[4096]{}; 73 | 74 | if(BrowseForSaveFile(title, initialDir.join().c_str(), 75 | nullptr, filters, path, sizeof(path))) 76 | return path; 77 | else 78 | return {}; 79 | #endif 80 | } 81 | -------------------------------------------------------------------------------- /src/filedialog.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_FILEBROWSE_HPP 19 | #define REAPACK_FILEBROWSE_HPP 20 | 21 | #include "win32.hpp" 22 | 23 | class Path; 24 | 25 | namespace FileDialog { 26 | std::string getOpenFileName(HWND, HINSTANCE, const char *title, 27 | const Path &directory, const Win32::char_type *filters, const Win32::char_type *defext); 28 | std::string getSaveFileName(HWND, HINSTANCE, const char *title, 29 | const Path &directory, const Win32::char_type *filters, const Win32::char_type *defext); 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/filesystem.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_FILESYSTEM_HPP 19 | #define REAPACK_FILESYSTEM_HPP 20 | 21 | #include 22 | #include 23 | 24 | class Path; 25 | class TempPath; 26 | 27 | namespace FS { 28 | FILE *open(const Path &); 29 | bool open(std::ifstream &, const Path &); 30 | bool open(std::ofstream &, const Path &); 31 | bool write(const Path &, const std::string &); 32 | bool rename(const TempPath &); 33 | bool rename(const Path &, const Path &); 34 | bool remove(const Path &); 35 | bool removeRecursive(const Path &); 36 | bool mtime(const Path &, time_t *); 37 | bool exists(const Path &, bool dir = false); 38 | bool mkdir(const Path &); 39 | Path canonical(const Path &); 40 | 41 | const char *lastError(); 42 | 43 | template::value>> 45 | bool allExists(const T &container, const bool dir = false) 46 | { 47 | return std::all_of(container.begin(), container.end(), 48 | [&dir](const Path &path) { return exists(path, dir); }); 49 | } 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/filter.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_FILTER_HPP 19 | #define REAPACK_FILTER_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | class Filter { 27 | public: 28 | Filter(const std::string & = {}); 29 | void set(const std::string &); 30 | Filter &operator=(const std::string &f) { set(f); return *this; } 31 | 32 | bool match(std::vector rows) const; 33 | 34 | private: 35 | class Node { 36 | public: 37 | enum Flag { 38 | StartAnchorFlag = 1<<0, 39 | EndAnchorFlag = 1<<1, 40 | LiteralFlag = 1<<2, 41 | NotFlag = 1<<3, 42 | FullWordFlag = 1<<4, 43 | }; 44 | 45 | Node(int flags) : m_flags(flags) {} 46 | virtual ~Node() = default; 47 | 48 | virtual bool match(const std::vector &) const = 0; 49 | bool test(Flag f) const { return (m_flags & f) != 0; } 50 | 51 | private: 52 | int m_flags; 53 | }; 54 | 55 | class Group : public Node { 56 | public: 57 | enum Type { 58 | MatchAll, 59 | MatchAny, 60 | }; 61 | 62 | Group(Type type, int flags = 0, Group *parent = nullptr); 63 | void clear() { m_nodes.clear(); } 64 | Group *push(const std::string_view &, int *flags); 65 | 66 | bool match(const std::vector &) const override; 67 | 68 | private: 69 | Group *addSubGroup(Type, int flags); 70 | bool pushSynonyms(const std::string_view &, int *flags); 71 | 72 | Group *m_parent; 73 | Type m_type; 74 | std::vector> m_nodes; 75 | }; 76 | 77 | class Token : public Node { 78 | public: 79 | Token(const std::string_view &buf, int flags); 80 | bool match(const std::vector &) const override; 81 | bool matchRow(const std::string &) const; 82 | 83 | private: 84 | std::string_view m_buf; 85 | }; 86 | 87 | std::string m_input; 88 | Group m_root; 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/hash.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_HASH_HPP 19 | #define REAPACK_HASH_HPP 20 | 21 | #include 22 | #include 23 | 24 | class Hash { 25 | public: 26 | enum Algorithm { 27 | SHA256 = 0x12, 28 | }; 29 | 30 | static bool getAlgorithm(const std::string &hash, Algorithm *out); 31 | 32 | Hash(Algorithm); 33 | Hash(const Hash &) = delete; 34 | 35 | void addData(const char *data, size_t len); 36 | const std::string &digest(); 37 | 38 | private: 39 | class Context { 40 | public: 41 | virtual ~Context() = default; 42 | virtual size_t hashSize() const = 0; 43 | virtual void addData(const char *data, size_t len) = 0; 44 | virtual void getHash(unsigned char *out) = 0; 45 | }; 46 | 47 | class CNGContext; 48 | class SHA256Context; 49 | class EVPContext; 50 | 51 | Algorithm m_algo; 52 | std::string m_value; 53 | std::unique_ptr m_context; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/iconlist.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "iconlist.hpp" 19 | 20 | #ifdef _WIN32 21 | # define DEFINE_ICON(icon, rc, _) IconList::Icon IconList::icon = MAKEINTRESOURCE(rc) 22 | #else 23 | # define DEFINE_ICON(icon, _, name) IconList::Icon IconList::icon = name 24 | #endif 25 | 26 | DEFINE_ICON(CheckedIcon, 141, "checked"); 27 | DEFINE_ICON(UncheckedIcon, 142, "unchecke"); 28 | 29 | IconList::IconList(const std::initializer_list &icons) 30 | { 31 | m_list = ImageList_Create(16, 16, 1, 32 | static_cast(icons.size()), static_cast(icons.size())); 33 | 34 | for(const auto *icon : icons) 35 | loadIcon(icon); 36 | } 37 | 38 | void IconList::loadIcon(const Win32::char_type *name) 39 | { 40 | #ifdef _WIN32 41 | HINSTANCE reaper = GetModuleHandle(nullptr); 42 | HICON icon = LoadIcon(reaper, name); 43 | ImageList_AddIcon(m_list, icon); 44 | #else 45 | HICON icon = LoadNamedImage(name, true); 46 | ImageList_Add(m_list, icon, 0); // v5.20 47 | #endif 48 | DestroyIcon(icon); 49 | } 50 | 51 | IconList::~IconList() 52 | { 53 | ImageList_Destroy(m_list); 54 | } 55 | -------------------------------------------------------------------------------- /src/iconlist.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_ICONLIST_HPP 19 | #define REAPACK_ICONLIST_HPP 20 | 21 | #include "win32.hpp" 22 | 23 | #include 24 | 25 | #ifdef _WIN32 26 | # include 27 | # include 28 | #else 29 | # include 30 | #endif 31 | 32 | class IconList { 33 | public: 34 | typedef const Win32::char_type *Icon; 35 | 36 | static Icon CheckedIcon; 37 | static Icon UncheckedIcon; 38 | 39 | IconList(const std::initializer_list &icons); 40 | ~IconList(); 41 | 42 | void loadIcon(Icon icon); 43 | HIMAGELIST handle() const { return m_list; } 44 | 45 | private: 46 | HIMAGELIST m_list; 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/import.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_IMPORT_HPP 19 | #define REAPACK_IMPORT_HPP 20 | 21 | #include "dialog.hpp" 22 | 23 | #include "remote.hpp" 24 | 25 | #include 26 | #include 27 | 28 | class MemoryDownload; 29 | class ThreadPool; 30 | 31 | class Import : public Dialog 32 | { 33 | public: 34 | Import(); 35 | 36 | protected: 37 | void onInit() override; 38 | void onCommand(int, int) override; 39 | void onTimer(int) override; 40 | 41 | private: 42 | enum State { 43 | OK, 44 | Aborted, 45 | Close, 46 | }; 47 | 48 | struct ImportData { 49 | size_t index; 50 | Remote remote; 51 | std::string contents; 52 | 53 | bool operator<(const ImportData &o) const { return index < o.index; } 54 | }; 55 | 56 | ThreadPool *setupPool(); 57 | void fetch(); 58 | bool read(MemoryDownload *, size_t index); 59 | void processQueue(); 60 | bool import(const ImportData &); 61 | void setWaiting(bool); 62 | 63 | std::unique_ptr m_pool; 64 | State m_state; 65 | std::deque m_queue; 66 | short m_fakePos; 67 | 68 | HWND m_url; 69 | HWND m_progress; 70 | HWND m_discover; 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/index.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_INDEX_HPP 19 | #define REAPACK_INDEX_HPP 20 | 21 | #include "metadata.hpp" 22 | #include "package.hpp" 23 | #include "source.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | class Index; 32 | class Path; 33 | class Remote; 34 | class XmlNode; 35 | struct NetworkOpts; 36 | 37 | typedef std::shared_ptr IndexPtr; 38 | 39 | class Index : public std::enable_shared_from_this { 40 | public: 41 | static Path pathFor(const std::string &name); 42 | static IndexPtr load(const std::string &name, const char *data = nullptr); 43 | 44 | Index(const std::string &name); 45 | ~Index(); 46 | 47 | void setName(const std::string &); 48 | const std::string &name() const { return m_name; } 49 | 50 | Metadata *metadata() { return &m_metadata; } 51 | const Metadata *metadata() const { return &m_metadata; } 52 | 53 | bool addCategory(const Category *cat); 54 | const auto &categories() const { return m_categories; } 55 | const Category *category(size_t i) const { return m_categories[i]; } 56 | const Category *category(const std::string &name) const; 57 | const Package *find(const std::string &cat, const std::string &pkg) const; 58 | 59 | const std::vector &packages() const { return m_packages; } 60 | 61 | private: 62 | static void loadV1(XmlNode, Index *); 63 | 64 | std::string m_name; 65 | Metadata m_metadata; 66 | std::vector m_categories; 67 | std::vector m_packages; 68 | 69 | std::unordered_map m_catMap; 70 | }; 71 | 72 | class Category { 73 | public: 74 | Category(const std::string &name, const Index *); 75 | ~Category(); 76 | 77 | const Index *index() const { return m_index; } 78 | const std::string &name() const { return m_name; } 79 | std::string fullName() const; 80 | 81 | bool addPackage(const Package *pack); 82 | const auto &packages() const { return m_packages; } 83 | const Package *package(size_t i) const { return m_packages[i]; } 84 | const Package *package(const std::string &name) const; 85 | 86 | private: 87 | const Index *m_index; 88 | 89 | std::string m_name; 90 | std::vector m_packages; 91 | std::unordered_map m_pkgMap; 92 | }; 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/manager.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_MANAGER_HPP 19 | #define REAPACK_MANAGER_HPP 20 | 21 | #include "dialog.hpp" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | class ListView; 28 | class Menu; 29 | class Remote; 30 | struct NetworkOpts; 31 | 32 | typedef boost::logic::tribool tribool; 33 | 34 | class Manager : public Dialog { 35 | public: 36 | Manager(); 37 | 38 | void refresh(); 39 | bool importRepo(); 40 | 41 | protected: 42 | void onInit() override; 43 | void onCommand(int, int) override; 44 | bool onKeyDown(int, int) override; 45 | void onTimer(int) override; 46 | void onClose() override; 47 | 48 | private: 49 | struct RemoteMods { 50 | std::optional enable; 51 | std::optional autoInstall; 52 | operator bool() const { return enable || autoInstall; } 53 | }; 54 | 55 | typedef std::function ModsCallback; 56 | 57 | Remote getRemote(int index) const; 58 | bool fillContextMenu(Menu &, int index) const; 59 | void setMods(const ModsCallback &); 60 | void toggleEnabled(); 61 | bool isRemoteEnabled(const Remote &) const; 62 | void setRemoteAutoInstall(const tribool &); 63 | tribool remoteAutoInstall(const Remote &) const; 64 | void uninstall(); 65 | void toggle(std::optional &, bool current); 66 | void refreshIndex(); 67 | void copyUrl(); 68 | void launchBrowser(); 69 | void importExport(); 70 | void options(); 71 | void setupNetwork(); 72 | void importArchive(); 73 | void exportArchive(); 74 | void aboutRepo(bool focus = true); 75 | 76 | void setChange(int); 77 | bool confirm() const; 78 | bool apply(); 79 | void reset(); 80 | 81 | HWND m_apply; 82 | ListView *m_list; 83 | 84 | size_t m_changes; 85 | bool m_importing; 86 | 87 | std::map m_mods; 88 | std::set m_uninstall; 89 | std::optional m_autoInstall, m_bleedingEdge, 90 | m_promptObsolete, m_expandSynonyms; 91 | 92 | Serializer m_serializer; 93 | }; 94 | 95 | class NetworkConfig : public Dialog { 96 | public: 97 | NetworkConfig(NetworkOpts *); 98 | 99 | protected: 100 | void onInit() override; 101 | void onCommand(int, int) override; 102 | 103 | private: 104 | void apply(); 105 | 106 | NetworkOpts *m_opts; 107 | HWND m_proxy; 108 | HWND m_staleThreshold; 109 | HWND m_verifyPeer; 110 | HWND m_fallbackProxy; 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/menu.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_MENU_HPP 19 | #define REAPACK_MENU_HPP 20 | 21 | #include 22 | 23 | #ifdef _WIN32 24 | # include 25 | #else 26 | # include 27 | #endif 28 | 29 | class Menu { 30 | public: 31 | Menu(HMENU handle = nullptr); 32 | ~Menu(); 33 | 34 | UINT size() { return m_size; } 35 | bool empty() const { return m_size == 0; } 36 | 37 | UINT addAction(const std::string &label, int commandId); 38 | UINT addAction(const std::string &label, const char *namedCommand); 39 | void addSeparator(); 40 | Menu addMenu(const std::string &label); 41 | 42 | int show(int x, int y, HWND parent) const; 43 | int show(HWND control, HWND parent) const; 44 | 45 | void enable(UINT); 46 | void disable(UINT); 47 | void setEnabled(bool, UINT); 48 | 49 | void check(UINT); 50 | void checkRadio(UINT); 51 | 52 | private: 53 | void append(MENUITEMINFO &); 54 | 55 | HMENU m_handle; 56 | bool m_ownership; 57 | UINT m_size; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/metadata.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "metadata.hpp" 19 | 20 | #include 21 | 22 | auto Metadata::getLinkType(const char *rel) -> LinkType 23 | { 24 | if(!strcmp(rel, "donation")) 25 | return DonationLink; 26 | else if(!strcmp(rel, "screenshot")) 27 | return ScreenshotLink; 28 | 29 | return WebsiteLink; 30 | } 31 | 32 | void Metadata::addLink(const LinkType type, const Link &link) 33 | { 34 | if(boost::algorithm::starts_with(link.url, "http")) 35 | m_links.insert({type, link}); 36 | } 37 | -------------------------------------------------------------------------------- /src/metadata.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_METADATA_HPP 19 | #define REAPACK_METADATA_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | struct Link { std::string name; std::string url; }; 26 | 27 | class Metadata { 28 | public: 29 | enum LinkType { 30 | WebsiteLink, 31 | ScreenshotLink, 32 | DonationLink, 33 | }; 34 | 35 | static LinkType getLinkType(const char *rel); 36 | 37 | void setAbout(const std::string &rtf) { m_about = rtf; } 38 | const std::string &about() const { return m_about; } 39 | void addLink(const LinkType, const Link &); 40 | const auto &links() const { return m_links; } 41 | 42 | private: 43 | std::string m_about; 44 | std::multimap m_links; 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/obsquery.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "obsquery.hpp" 19 | 20 | #include "listview.hpp" 21 | #include "menu.hpp" 22 | #include "package.hpp" 23 | #include "resource.hpp" 24 | 25 | #include 26 | 27 | enum { ACTION_SELECT_ALL = 300, ACTION_UNSELECT_ALL }; 28 | 29 | ObsoleteQuery::ObsoleteQuery(std::vector *entries, bool *enable) 30 | : Dialog(IDD_OBSQUERY_DIALOG), m_entries(entries), m_enable(enable) 31 | { 32 | } 33 | 34 | void ObsoleteQuery::onInit() 35 | { 36 | Dialog::onInit(); 37 | 38 | m_enableCtrl = getControl(IDC_ENABLE); 39 | m_okBtn = getControl(IDOK); 40 | 41 | m_list = createControl(IDC_LIST, ListView::Columns{ 42 | {"Package", 550} 43 | }); 44 | 45 | m_list->onSelect >> [=] { setEnabled(m_list->hasSelection(), m_okBtn); }; 46 | m_list->onFillContextMenu >> [=] (Menu &menu, int) { 47 | menu.addAction("Select &all", ACTION_SELECT_ALL); 48 | menu.addAction("&Unselect all", ACTION_UNSELECT_ALL); 49 | return true; 50 | }; 51 | 52 | m_list->reserveRows(m_entries->size()); 53 | 54 | for(const Registry::Entry &entry : *m_entries) { 55 | std::ostringstream stream; 56 | stream << entry.remote << '/' << entry.category << '/' 57 | << Package::displayName(entry.package, entry.description); 58 | m_list->createRow()->setCell(0, stream.str()); 59 | } 60 | 61 | m_list->autoSizeHeader(); 62 | 63 | setChecked(true, m_enableCtrl); 64 | 65 | disable(m_okBtn); 66 | } 67 | 68 | void ObsoleteQuery::onCommand(const int id, int event) 69 | { 70 | switch(id) { 71 | case IDOK: 72 | prepare(); 73 | break; 74 | case IDC_ENABLE: 75 | *m_enable = isChecked(m_enableCtrl); 76 | break; 77 | case ACTION_SELECT_ALL: 78 | m_list->selectAll(); 79 | break; 80 | case ACTION_UNSELECT_ALL: 81 | m_list->unselectAll(); 82 | break; 83 | } 84 | 85 | Dialog::onCommand(id, event); 86 | } 87 | 88 | void ObsoleteQuery::prepare() 89 | { 90 | std::vector selected; 91 | 92 | for(int index : m_list->selection()) 93 | selected.emplace_back(m_entries->at(index)); 94 | 95 | m_entries->swap(selected); 96 | } 97 | -------------------------------------------------------------------------------- /src/obsquery.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_OBSOLETE_QUERY_HPP 19 | #define REAPACK_OBSOLETE_QUERY_HPP 20 | 21 | #include "dialog.hpp" 22 | 23 | #include "registry.hpp" 24 | 25 | #include 26 | 27 | class ListView; 28 | 29 | class ObsoleteQuery : public Dialog { 30 | public: 31 | ObsoleteQuery(std::vector *, bool *enable); 32 | 33 | protected: 34 | void onInit() override; 35 | void onCommand(int, int) override; 36 | 37 | private: 38 | void prepare(); 39 | 40 | std::vector *m_entries; 41 | bool *m_enable; 42 | 43 | HWND m_enableCtrl; 44 | HWND m_okBtn; 45 | ListView *m_list; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/package.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_PACKAGE_HPP 19 | #define REAPACK_PACKAGE_HPP 20 | 21 | #include "metadata.hpp" 22 | #include "version.hpp" 23 | 24 | class Category; 25 | 26 | class Package { 27 | public: 28 | enum Type { 29 | UnknownType, 30 | ScriptType, 31 | ExtensionType, 32 | EffectType, 33 | DataType, 34 | ThemeType, 35 | LangPackType, 36 | WebInterfaceType, 37 | ProjectTemplateType, 38 | TrackTemplateType, 39 | MIDINoteNamesType, 40 | AutomationItemType, 41 | }; 42 | 43 | static Type getType(const char *); 44 | static const char *displayType(Type); 45 | static const std::string &displayName(const std::string &name, const std::string &desc); 46 | 47 | Package(const Type, const std::string &name, const Category *); 48 | ~Package(); 49 | 50 | const Category *category() const { return m_category; } 51 | Type type() const { return m_type; } 52 | std::string displayType() const { return displayType(m_type); } 53 | const std::string &name() const { return m_name; } 54 | std::string fullName() const; 55 | void setDescription(const std::string &d) { m_desc = d; } 56 | const std::string &description() const { return m_desc; } 57 | const std::string &displayName() const 58 | { return displayName(m_name, m_desc); } 59 | 60 | Metadata *metadata() { return &m_metadata; } 61 | const Metadata *metadata() const { return &m_metadata; } 62 | 63 | bool addVersion(const Version *ver); 64 | const auto &versions() const { return m_versions; } 65 | const Version *version(size_t index) const; 66 | const Version *lastVersion(bool pres = true, const VersionName &from = {}) const; 67 | const Version *findVersion(const VersionName &) const; 68 | 69 | private: 70 | class CompareVersion { 71 | public: 72 | bool operator()(const Version *l, const Version *r) const 73 | { 74 | return l->name() < r->name(); 75 | } 76 | }; 77 | 78 | const Category *m_category; 79 | 80 | Type m_type; 81 | std::string m_name; 82 | std::string m_desc; 83 | Metadata m_metadata; 84 | std::set m_versions; 85 | 86 | }; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/path.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_PATH_HPP 19 | #define REAPACK_PATH_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | class UseRootPath; 26 | 27 | class Path { 28 | public: 29 | enum Attribute { 30 | Absolute = 1<<0, 31 | UNC = 1<<1, 32 | }; 33 | 34 | static const Path DATA; 35 | static const Path CACHE; 36 | static const Path CONFIG; 37 | static const Path REGISTRY; 38 | 39 | static const Path &root() { return s_root; } 40 | 41 | Path(const std::string &path = {}); 42 | 43 | void append(const std::string &parts, bool traversal = true); 44 | void append(const Path &other); 45 | void remove(size_t pos, size_t count); 46 | void removeLast(); 47 | void clear(); 48 | 49 | bool empty() const { return m_parts.empty(); } 50 | size_t size() const { return m_parts.size(); } 51 | int attributes() const { return m_attributes; } 52 | bool test(Attribute f) const { return (m_attributes & f) != 0; } 53 | 54 | Path dirname() const; 55 | std::string front() const; 56 | std::string basename() const; 57 | std::string join(bool nativeSeparator = true) const; 58 | bool startsWith(const Path &) const; 59 | 60 | Path prependRoot() const; 61 | Path removeRoot() const; 62 | 63 | std::list::const_iterator begin() const { return m_parts.begin(); } 64 | std::list::const_iterator end() const { return m_parts.end(); } 65 | 66 | bool operator==(const Path &) const; 67 | bool operator!=(const Path &) const; 68 | bool operator<(const Path &) const; 69 | Path operator+(const std::string &) const; 70 | Path operator+(const Path &) const; 71 | const Path &operator+=(const std::string &); 72 | const Path &operator+=(const Path &); 73 | std::string &operator[](size_t); 74 | const std::string &operator[](size_t) const; 75 | 76 | private: 77 | static Path s_root; 78 | friend UseRootPath; 79 | 80 | const std::string &at(size_t) const; 81 | 82 | std::list m_parts; 83 | int m_attributes; 84 | }; 85 | 86 | inline std::ostream &operator<<(std::ostream &os, const Path &p) 87 | { 88 | return os << p.join(); 89 | }; 90 | 91 | class UseRootPath { 92 | public: 93 | UseRootPath(const Path &); 94 | ~UseRootPath(); 95 | 96 | private: 97 | Path m_backup; 98 | }; 99 | 100 | class TempPath { 101 | public: 102 | TempPath(const Path &target); 103 | 104 | const Path &target() const { return m_target; } 105 | const Path &temp() const { return m_temp; } 106 | 107 | private: 108 | Path m_target; 109 | Path m_temp; 110 | }; 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /src/platform.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "platform.hpp" 19 | 20 | #include 21 | #include 22 | 23 | const Platform::Enum Platform::Current = Platform:: 24 | #ifdef __APPLE__ 25 | # ifdef __x86_64__ 26 | Darwin_x86_64 27 | # elif __i386__ 28 | Darwin_i386 29 | # elif __arm64__ 30 | Darwin_arm64 31 | # else 32 | Unknown 33 | # endif 34 | 35 | #elif __linux__ 36 | # ifdef __x86_64__ 37 | Linux_x86_64 38 | # elif __i686__ 39 | Linux_i686 40 | # elif __ARM_ARCH_7A__ 41 | Linux_armv7l 42 | # elif __aarch64__ 43 | Linux_aarch64 44 | # else 45 | Unknown 46 | # endif 47 | 48 | #elif _WIN32 49 | # ifdef _M_ARM64EC 50 | Windows_arm64ec 51 | # elif _M_X64 52 | Windows_x64 53 | # else 54 | Windows_x86 55 | # endif 56 | 57 | #else 58 | Unknown 59 | #endif 60 | ; 61 | 62 | static_assert(Platform::Current != Platform::Unknown, 63 | "The current operating system or architecture is not supported."); 64 | 65 | auto Platform::parse(const char *platform, const bool hasArm64Ec) -> Enum 66 | { 67 | constexpr std::pair map[] { 68 | { "all", Generic }, 69 | 70 | { "darwin", Darwin_Any }, 71 | { "darwin32", Darwin_i386 }, 72 | { "darwin64", Darwin_x86_64 }, 73 | { "darwin-arm64", Darwin_arm64 }, 74 | 75 | { "linux", Linux_Any }, 76 | { "linux32", Linux_i686 }, 77 | { "linux64", Linux_x86_64 }, 78 | { "linux-armv7l", Linux_armv7l }, 79 | { "linux-aarch64", Linux_aarch64 }, 80 | 81 | { "windows", Windows_Any }, 82 | { "win32", Windows_x86 }, 83 | { "win64", Windows_x64 }, 84 | { "windows-arm64ec", Windows_arm64ec }, 85 | }; 86 | 87 | for(auto &[key, value] : map) { 88 | if(!strcmp(platform, key)) { 89 | if(!hasArm64Ec && value == Windows_x64) 90 | return Windows_x64_arm64ec; 91 | return value; 92 | } 93 | } 94 | 95 | return Unknown; 96 | } 97 | 98 | bool Platform::test() const 99 | { 100 | return (m_value & Current) != Unknown; 101 | } 102 | -------------------------------------------------------------------------------- /src/platform.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_PLATFORM_HPP 19 | #define REAPACK_PLATFORM_HPP 20 | 21 | class Platform { 22 | public: 23 | enum Enum { 24 | Unknown, 25 | 26 | Darwin_i386 = 1<<0, 27 | Darwin_x86_64 = 1<<1, 28 | Darwin_arm64 = 1<<2, 29 | Darwin_Any = Darwin_i386 | Darwin_x86_64 | Darwin_arm64, 30 | 31 | Linux_i686 = 1<<3, 32 | Linux_x86_64 = 1<<4, 33 | Linux_armv7l = 1<<5, 34 | Linux_aarch64 = 1<<6, 35 | Linux_Any = Linux_i686 | Linux_x86_64 | Linux_armv7l | Linux_aarch64, 36 | 37 | Windows_x86 = 1<<7, 38 | Windows_x64 = 1<<8, 39 | Windows_arm64ec = 1<<9, 40 | Windows_Any = Windows_x86 | Windows_x64 | Windows_arm64ec, 41 | Windows_x64_arm64ec = Windows_x64 | Windows_arm64ec, 42 | 43 | Generic = Darwin_Any | Linux_Any | Windows_Any, 44 | }; 45 | 46 | static const Enum Current; 47 | 48 | Platform() : m_value(Generic) {} 49 | Platform(Enum val) : m_value(val) {} 50 | Platform(const char *str, bool hasArm64Ec = true) : m_value(parse(str, hasArm64Ec)) {} 51 | 52 | Enum value() const { return m_value; } 53 | bool test() const; 54 | 55 | operator Enum() const { return m_value; } 56 | Platform &operator=(Enum n) { m_value = n; return *this; } 57 | 58 | private: 59 | static Enum parse(const char *, bool hasArm64Ec); 60 | 61 | Enum m_value; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/progress.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "progress.hpp" 19 | 20 | #include "resource.hpp" 21 | #include "win32.hpp" 22 | 23 | #include 24 | 25 | Progress::Progress(ThreadPool *pool) 26 | : Dialog(IDD_PROGRESS_DIALOG), 27 | m_pool(pool), m_current(), m_label(nullptr), m_progress(nullptr), 28 | m_done(0), m_total(0) 29 | { 30 | m_pool->onPush >> std::bind(&Progress::addTask, this, std::placeholders::_1); 31 | } 32 | 33 | void Progress::onInit() 34 | { 35 | Dialog::onInit(); 36 | 37 | m_label = getControl(IDC_LABEL); 38 | m_progress = getControl(IDC_PROGRESS); 39 | 40 | Win32::setWindowText(m_label, "Initializing..."); 41 | } 42 | 43 | void Progress::onCommand(const int id, int) 44 | { 45 | switch(id) { 46 | case IDCANCEL: 47 | m_pool->abort(); 48 | 49 | // don't wait until the current downloads are finished 50 | // before getting out of the user way 51 | hide(); 52 | break; 53 | } 54 | } 55 | 56 | void Progress::onTimer(const int id) 57 | { 58 | #ifdef _WIN32 59 | if(!IsWindowEnabled(handle())) 60 | return; 61 | #endif 62 | 63 | show(); 64 | stopTimer(id); 65 | } 66 | 67 | void Progress::addTask(ThreadTask *task) 68 | { 69 | m_total++; 70 | if(m_current.step) 71 | updateProgress(); 72 | 73 | if(!isVisible()) 74 | startTimer(100); 75 | 76 | task->onStartAsync >> [=] { 77 | m_current = task->summary(); 78 | updateProgress(); 79 | }; 80 | 81 | task->onFinishAsync >> [=] { 82 | m_done++; 83 | updateProgress(); 84 | }; 85 | } 86 | 87 | void Progress::updateProgress() 88 | { 89 | Win32::setWindowText(m_label, String::format("%s %s of %s: %s", 90 | m_current.step, 91 | String::number(std::min(m_done + 1, m_total)).c_str(), 92 | String::number(m_total).c_str(), 93 | m_current.item.c_str() 94 | ).c_str()); 95 | 96 | const double pos = static_cast( 97 | std::min(m_done + 1, m_total)) / std::max(2, m_total); 98 | const int percent = static_cast(pos * 100); 99 | 100 | SendMessage(m_progress, PBM_SETPOS, percent, 0); 101 | Win32::setWindowText(handle(), String::format( 102 | "ReaPack: Operation in progress (%d%%)", percent 103 | ).c_str()); 104 | } 105 | -------------------------------------------------------------------------------- /src/progress.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_PROGRESS_HPP 19 | #define REAPACK_PROGRESS_HPP 20 | 21 | #include "dialog.hpp" 22 | 23 | #include "thread.hpp" 24 | 25 | class Progress : public Dialog { 26 | public: 27 | Progress(ThreadPool *); 28 | 29 | protected: 30 | void onInit() override; 31 | void onCommand(int, int) override; 32 | void onTimer(int) override; 33 | 34 | private: 35 | void addTask(ThreadTask *); 36 | void updateProgress(); 37 | 38 | ThreadPool *m_pool; 39 | ThreadSummary m_current; 40 | 41 | HWND m_label; 42 | HWND m_progress; 43 | 44 | int m_done; 45 | int m_total; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/reapack.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_REAPACK_HPP 19 | #define REAPACK_REAPACK_HPP 20 | 21 | #include "action.hpp" 22 | #include "api.hpp" 23 | #include "browser.hpp" 24 | #include "browser_entry.hpp" 25 | #include "config.hpp" 26 | #include "path.hpp" 27 | 28 | #include 29 | 30 | #include 31 | 32 | class About; 33 | class Browser; 34 | class Manager; 35 | class Progress; 36 | class Remote; 37 | class Transaction; 38 | 39 | #define g_reapack (ReaPack::instance()) 40 | 41 | class ReaPack { 42 | public: 43 | static ReaPack *instance() { return s_instance; } 44 | static Path resourcePath(); 45 | 46 | ReaPack(REAPER_PLUGIN_HINSTANCE, HWND mainWindow); 47 | ~ReaPack(); 48 | 49 | ActionList *actions() { return &m_actions; } 50 | 51 | void synchronizeAll(); 52 | void uninstall(const Remote &); 53 | 54 | void uploadPackage(); 55 | void importRemote(); 56 | void manageRemotes(); 57 | void aboutSelf(); 58 | void about(const Remote &, bool focus = true); 59 | About *about(bool instantiate = true); 60 | Browser *browsePackages(); 61 | void refreshManager(); 62 | void refreshBrowser(); 63 | bool requestProxy(); 64 | 65 | void addSetRemote(const Remote &); 66 | Remote remote(const std::string &name) const; 67 | 68 | Transaction *setupTransaction(); 69 | void commitConfig(bool refresh = true); 70 | Config *config() { return &m_config; } 71 | 72 | private: 73 | static ReaPack *s_instance; 74 | 75 | void createDirectories(); 76 | void registerSelf(); 77 | void setupActions(); 78 | void setupAPI(); 79 | void teardownTransaction(); 80 | 81 | REAPER_PLUGIN_HINSTANCE m_instance; 82 | HWND m_mainWindow; 83 | 84 | UseRootPath m_useRootPath; 85 | Config m_config; 86 | ActionList m_actions; 87 | std::list m_api; 88 | 89 | Transaction *m_tx; 90 | std::unique_ptr m_about; 91 | std::unique_ptr m_browser; 92 | std::unique_ptr m_manager; 93 | std::unique_ptr m_progress; 94 | }; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/receipt.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_RECEIPT_HPP 19 | #define REAPACK_RECEIPT_HPP 20 | 21 | #include "registry.hpp" 22 | #include "errors.hpp" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | class Index; 30 | class InstallTicket; 31 | class Path; 32 | class ReceiptPage; 33 | class Version; 34 | 35 | typedef std::shared_ptr IndexPtr; 36 | 37 | class Receipt { 38 | public: 39 | enum Flag { 40 | NoFlag = 0, 41 | ErrorFlag = 1<<0, 42 | RestartNeededFlag = 1<<1, 43 | IndexChangedFlag = 1<<2, 44 | PackageChangedFlag = 1<<3, 45 | InstalledFlag = 1<<4, 46 | RemovedFlag = 1<<5, 47 | ExportedFlag = 1<<6, 48 | 49 | InstalledOrRemoved = InstalledFlag | RemovedFlag, 50 | RefreshBrowser = IndexChangedFlag | PackageChangedFlag | InstalledOrRemoved, 51 | }; 52 | 53 | Receipt(); 54 | 55 | int flags() const { return m_flags; } 56 | bool test(Flag f) const { return (m_flags & f) != 0; } 57 | bool empty() const; 58 | 59 | void setIndexChanged() { m_flags |= IndexChangedFlag; } 60 | void setPackageChanged() { m_flags |= PackageChangedFlag; } 61 | void addInstall(const Version *, const Registry::Entry &); 62 | void addRemoval(const Path &p); 63 | void addExport(const Path &p); 64 | void addError(const ErrorInfo &); 65 | 66 | ReceiptPage installedPage() const; 67 | ReceiptPage removedPage() const; 68 | ReceiptPage exportedPage() const; 69 | ReceiptPage errorPage() const; 70 | 71 | private: 72 | int m_flags; 73 | 74 | std::multiset m_installs; 75 | std::set m_removals; 76 | std::set m_exports; 77 | std::vector m_errors; 78 | }; 79 | 80 | class ReceiptPage { 81 | public: 82 | template 83 | ReceiptPage(const T &list, const char *singular, const char *plural = nullptr) 84 | : m_size(list.size()) 85 | { 86 | setTitle(m_size == 1 || !plural ? singular : plural); 87 | 88 | std::ostringstream stream; 89 | 90 | for(const auto &item : list) { 91 | if(stream.tellp() > 0) 92 | stream << "\r\n"; 93 | 94 | stream << item; 95 | } 96 | 97 | m_contents = stream.str(); 98 | } 99 | 100 | const std::string &title() const { return m_title; } 101 | const std::string &contents() const { return m_contents; } 102 | bool empty() const { return m_size == 0; } 103 | 104 | private: 105 | void setTitle(const char *title); 106 | size_t m_size; 107 | std::string m_title; 108 | std::string m_contents; 109 | }; 110 | 111 | class InstallTicket { 112 | public: 113 | enum Type { 114 | Install, 115 | Reinstall, 116 | Update, 117 | Downgrade, 118 | }; 119 | 120 | InstallTicket(const Version *ver, const Registry::Entry &previousEntry); 121 | 122 | bool operator<(const InstallTicket &) const; 123 | 124 | private: 125 | friend std::ostream &operator<<(std::ostream &, const InstallTicket &); 126 | Type deduceType(const Registry::Entry &) const; 127 | 128 | const Version *m_version; 129 | VersionName m_previous; 130 | Type m_type; 131 | 132 | IndexPtr m_index; // to keep it alive long enough 133 | }; 134 | 135 | std::ostream &operator<<(std::ostream &, const InstallTicket &); 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /src/registry.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_REGISTRY_HPP 19 | #define REAPACK_REGISTRY_HPP 20 | 21 | #include "database.hpp" 22 | #include "package.hpp" 23 | #include "path.hpp" 24 | #include "version.hpp" 25 | 26 | #include 27 | #include 28 | 29 | class Registry { 30 | public: 31 | struct Entry { 32 | enum Flag { 33 | PinnedFlag = 1<<0, 34 | BleedingEdgeFlag = 1<<1, 35 | }; 36 | 37 | typedef int64_t id_t; 38 | 39 | id_t id; 40 | std::string remote; 41 | std::string category; 42 | std::string package; 43 | std::string description; 44 | Package::Type type; 45 | VersionName version; 46 | std::string author; 47 | int flags; 48 | 49 | operator bool() const { return id > 0; } 50 | bool operator==(const Entry &o) const { return id == o.id; } 51 | bool test(Flag f) const { return (flags & f) == f; } 52 | }; 53 | 54 | struct File { 55 | Path path; 56 | int sections; 57 | Package::Type type; 58 | 59 | bool operator<(const File &o) const { return path < o.path; } 60 | }; 61 | 62 | Registry(const Path &path = {}); 63 | 64 | Entry getEntry(const Package *) const; 65 | Entry getOwner(const Path &) const; 66 | std::vector getEntries(const std::string &) const; 67 | std::vector getFiles(const Entry &) const; 68 | std::vector getMainFiles(const Entry &) const; 69 | Entry push(const Version *, int flags = 0, std::vector *conflicts = nullptr); 70 | void setFlags(const Entry &, int flags); 71 | void forget(const Entry &); 72 | 73 | void savepoint() { m_db.savepoint(); } 74 | void restore() { m_db.restore(); } 75 | void commit() { m_db.commit(); } 76 | 77 | private: 78 | void migrate(); 79 | void convertImplicitSections(); 80 | void fillEntry(const Statement *, Entry *) const; 81 | 82 | Database m_db; 83 | Statement *m_insertEntry; 84 | Statement *m_updateEntry; 85 | Statement *m_setFlags; 86 | Statement *m_findEntry; 87 | Statement *m_allEntries; 88 | Statement *m_forgetEntry; 89 | Statement *m_getOwner; 90 | 91 | Statement *m_getFiles; 92 | Statement *m_insertFile; 93 | Statement *m_clearFiles; 94 | Statement *m_forgetFiles; 95 | }; 96 | 97 | namespace std { 98 | template<> struct hash { 99 | std::size_t operator()(const Registry::Entry &e) const 100 | { 101 | return std::hash()(e.id); 102 | } 103 | }; 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /src/remote.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_REMOTE_HPP 19 | #define REAPACK_REMOTE_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | typedef boost::logic::tribool tribool; 27 | 28 | class Remote { 29 | public: 30 | static Remote fromString(const std::string &data); 31 | 32 | Remote(); 33 | Remote(const std::string &name, const std::string &url, bool enabled = true, 34 | const tribool &autoInstall = boost::logic::indeterminate); 35 | 36 | std::string toString() const; 37 | 38 | void setName(const std::string &name); 39 | const std::string &name() const { return m_name; } 40 | 41 | void setUrl(const std::string &url); 42 | const std::string &url() const { return m_url; } 43 | 44 | bool isNull() const { return m_name.empty() || m_url.empty(); } 45 | 46 | void enable() { setEnabled(true); } 47 | void disable() { setEnabled(false); } 48 | void setEnabled(const bool enabled) { m_enabled = enabled; } 49 | bool isEnabled() const { return m_enabled; } 50 | 51 | void setAutoInstall(const tribool &autoInstall) { m_autoInstall = autoInstall; } 52 | tribool autoInstall() const { return m_autoInstall; } 53 | bool autoInstall(bool fallback) const; 54 | 55 | void protect() { m_protected = true; } 56 | bool isProtected() const { return m_protected; } 57 | 58 | bool operator<(const Remote &o) const { return m_name < o.name(); } 59 | operator bool() const { return !isNull(); } 60 | 61 | private: 62 | std::string m_name; 63 | std::string m_url; 64 | bool m_enabled; 65 | bool m_protected; 66 | tribool m_autoInstall; 67 | }; 68 | 69 | class RemoteList { 70 | public: 71 | RemoteList() {} 72 | RemoteList(const RemoteList &) = delete; 73 | 74 | void add(const Remote &); 75 | void remove(const Remote &remote) { remove(remote.name()); } 76 | void remove(const std::string &name); 77 | Remote get(const std::string &name) const; 78 | std::vector getEnabled() const; 79 | 80 | bool empty() const { return m_remotes.empty(); } 81 | size_t size() const { return m_remotes.size(); } 82 | bool hasName(const std::string &name) const { return m_map.count(name) > 0; } 83 | 84 | std::vector::const_iterator begin() const { return m_remotes.begin(); } 85 | std::vector::const_iterator end() const { return m_remotes.end(); } 86 | 87 | private: 88 | std::vector m_remotes; 89 | std::map m_map; 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /src/report.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "report.hpp" 19 | 20 | #include "receipt.hpp" 21 | #include "resource.hpp" 22 | #include "tabbar.hpp" 23 | #include "win32.hpp" 24 | 25 | Report::Report(const Receipt *receipt) 26 | : Dialog(IDD_REPORT_DIALOG), m_receipt(receipt), m_empty(true) 27 | { 28 | } 29 | 30 | void Report::onInit() 31 | { 32 | Dialog::onInit(); 33 | 34 | m_tabbar = createControl(IDC_TABS, this); 35 | m_tabbar->onTabChange >> [=] (const int i) { 36 | Win32::setWindowText(getControl(IDC_REPORT), m_pages[i].c_str()); 37 | }; 38 | 39 | const ReceiptPage pages[] { 40 | m_receipt->installedPage(), 41 | m_receipt->removedPage(), 42 | m_receipt->exportedPage(), 43 | m_receipt->errorPage(), 44 | }; 45 | 46 | for(const auto &page : pages) 47 | addPage(page); 48 | 49 | updateLabel(); 50 | 51 | SetFocus(getControl(IDOK)); 52 | 53 | if(m_receipt->test(Receipt::RestartNeededFlag)) 54 | startTimer(1); 55 | } 56 | 57 | void Report::onTimer(int timer) 58 | { 59 | stopTimer(timer); 60 | 61 | Win32::messageBox(handle(), 62 | "One or more native REAPER extensions were installed.\n" 63 | "These newly installed files won't be loaded until REAPER is restarted.", 64 | "ReaPack Notice", MB_OK); 65 | } 66 | 67 | void Report::updateLabel() 68 | { 69 | const char *label; 70 | 71 | if(m_receipt->flags() == Receipt::ErrorFlag) 72 | label = "Operation failed. The following error(s) occured:"; 73 | else if(m_receipt->test(Receipt::ErrorFlag)) 74 | label = "The operation was partially completed (one or more errors occured):"; 75 | else if(m_receipt->test(Receipt::InstalledOrRemoved)) 76 | label = "All done! Description of the changes:"; 77 | else 78 | label = "Operation completed successfully!"; 79 | 80 | Win32::setWindowText(getControl(IDC_LABEL), label); 81 | } 82 | 83 | void Report::addPage(const ReceiptPage &page) 84 | { 85 | m_pages.emplace_back(page.contents()); 86 | m_tabbar->addTab({page.title().c_str()}); 87 | 88 | if(m_empty && !page.empty()) { 89 | m_tabbar->setCurrentIndex(m_tabbar->count() - 1); 90 | m_empty = false; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/report.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_REPORT_HPP 19 | #define REAPACK_REPORT_HPP 20 | 21 | #include "dialog.hpp" 22 | 23 | class Receipt; 24 | class ReceiptPage; 25 | class TabBar; 26 | 27 | class Report : public Dialog { 28 | public: 29 | Report(const Receipt *); 30 | 31 | protected: 32 | void onInit() override; 33 | void onTimer(int) override; 34 | 35 | private: 36 | void updateLabel(); 37 | void addPage(const ReceiptPage &); 38 | 39 | const Receipt *m_receipt; 40 | bool m_empty; 41 | TabBar *m_tabbar; 42 | std::vector m_pages; 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/resource.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_RESOURCE_HPP 19 | #define REAPACK_RESOURCE_HPP 20 | 21 | #ifdef _WIN32 22 | # include 23 | # include 24 | #else 25 | # define PROGRESS_CLASS "msctls_progress32" 26 | # define WC_LISTVIEW "SysListView32" 27 | # define WC_TABCONTROL "SysTabControl32" 28 | # define PBS_MARQUEE 0 29 | #endif 30 | 31 | #define DIALOG_STYLE DS_MODALFRAME | WS_POPUP | WS_SYSMENU | WS_CAPTION 32 | #define DIALOG_FONT 8, "MS Shell Dlg" 33 | 34 | #define IDAPPLY 0x3021 35 | 36 | #define IDD_PROGRESS_DIALOG 100 37 | #define IDD_REPORT_DIALOG 101 38 | #define IDD_CONFIG_DIALOG 102 39 | #define IDD_ABOUT_DIALOG 103 40 | #define IDD_IMPORT_DIALOG 104 41 | #define IDD_BROWSER_DIALOG 105 42 | #define IDD_NETCONF_DIALOG 106 43 | #define IDD_OBSQUERY_DIALOG 107 44 | 45 | #define IDC_LABEL 200 46 | #define IDC_LABEL2 201 47 | #define IDC_LABEL3 202 48 | #define IDC_PROGRESS 210 49 | #define IDC_REPORT 211 50 | #define IDC_LIST 212 51 | #define IDC_IMPORT 213 52 | #define IDC_TABS 214 53 | #define IDC_ACTION 215 54 | #define IDC_WEBSITE 216 55 | #define IDC_DONATE 217 56 | #define IDC_ABOUT 218 57 | #define IDC_MENU 219 58 | #define IDC_GROUPBOX 220 59 | #define IDC_URL 221 60 | #define IDC_FILTER 222 61 | #define IDC_CLEAR 223 62 | #define IDC_DISPLAY 224 63 | #define IDC_SELECT 225 64 | #define IDC_UNSELECT 226 65 | #define IDC_OPTIONS 227 66 | #define IDC_BROWSE 228 67 | #define IDC_PROXY 229 68 | #define IDC_VERIFYPEER 230 69 | #define IDC_SCREENSHOT 231 70 | #define IDC_ENABLE 232 71 | #define IDC_CHANGELOG 233 72 | #define IDC_DISCOVER 234 73 | #define IDC_STALETHRSH 235 74 | #define IDC_FALLBCKPXY 236 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/richedit-generic.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "richedit.hpp" 19 | 20 | #include "string.hpp" 21 | 22 | // This is the Linux implementation of RichEdit 23 | // See also richedit.mm and richedit-win32.cpp 24 | 25 | void RichEdit::Init() 26 | { 27 | } 28 | 29 | RichEdit::RichEdit(HWND handle) 30 | : Control(handle) 31 | { 32 | } 33 | 34 | RichEdit::~RichEdit() = default; 35 | 36 | void RichEdit::onNotify(LPNMHDR, LPARAM) 37 | { 38 | } 39 | 40 | void RichEdit::setPlainText(const std::string &text) 41 | { 42 | SetWindowText(handle(), text.c_str()); 43 | } 44 | 45 | bool RichEdit::setRichText(const std::string &rtf) 46 | { 47 | setPlainText(String::stripRtf(rtf)); 48 | return true; 49 | } 50 | -------------------------------------------------------------------------------- /src/richedit-win32.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "richedit.hpp" 19 | 20 | // This is the Win32 implementation of RichEdit 21 | // The macOS implementation is in richedit.mm, Linux is in richedit-generic.cpp 22 | 23 | #include "dllimport.hpp" 24 | #include "win32.hpp" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | static void HandleLink(ENLINK *info, HWND handle) 31 | { 32 | const CHARRANGE &range = info->chrg; 33 | 34 | std::wstring url(range.cpMax - range.cpMin, 0); 35 | 36 | TEXTRANGE tr{range, url.data()}; 37 | SendMessage(handle, EM_GETTEXTRANGE, 0, (LPARAM)&tr); 38 | 39 | if(info->msg == WM_LBUTTONUP) 40 | ShellExecute(nullptr, L"open", url.c_str(), nullptr, nullptr, SW_SHOW); 41 | } 42 | 43 | void RichEdit::Init() 44 | { 45 | LoadLibrary(L"Msftedit.dll"); 46 | } 47 | 48 | RichEdit::RichEdit(HWND handle) 49 | : Control(handle) 50 | { 51 | SendMessage(handle, EM_AUTOURLDETECT, true, 0); 52 | SendMessage(handle, EM_SETEVENTMASK, 0, ENM_LINK); 53 | SendMessage(handle, EM_SETEDITSTYLE, 54 | SES_HYPERLINKTOOLTIPS, SES_HYPERLINKTOOLTIPS); 55 | 56 | // available since Windows 10 version 1703 57 | static DllImport 58 | _SetDialogControlDpiChangeBehavior 59 | {L"user32.dll", "SetDialogControlDpiChangeBehavior"}; 60 | 61 | if(_SetDialogControlDpiChangeBehavior) { 62 | _SetDialogControlDpiChangeBehavior(handle, 63 | DCDC_DISABLE_FONT_UPDATE, DCDC_DISABLE_FONT_UPDATE); 64 | } 65 | } 66 | 67 | RichEdit::~RichEdit() = default; 68 | 69 | void RichEdit::onNotify(LPNMHDR info, LPARAM lParam) 70 | { 71 | switch(info->code) { 72 | case EN_LINK: 73 | HandleLink((ENLINK *)lParam, handle()); 74 | break; 75 | }; 76 | } 77 | 78 | void RichEdit::setPlainText(const std::string &text) 79 | { 80 | Win32::setWindowText(handle(), text.c_str()); 81 | } 82 | 83 | bool RichEdit::setRichText(const std::string &rtf) 84 | { 85 | std::stringstream stream(rtf); 86 | 87 | EDITSTREAM es{}; 88 | es.dwCookie = (DWORD_PTR)&stream; 89 | es.pfnCallback = [](DWORD_PTR cookie, LPBYTE buf, LONG size, LONG *pcb) -> DWORD { 90 | std::stringstream *stream = reinterpret_cast(cookie); 91 | *pcb = (LONG)stream->readsome((char *)buf, size); 92 | return 0; 93 | }; 94 | 95 | SendMessage(handle(), EM_STREAMIN, SF_RTF, (LPARAM)&es); 96 | 97 | if(es.dwError) 98 | return false; 99 | 100 | GETTEXTLENGTHEX tl{}; 101 | LONG length = (LONG)SendMessage(handle(), EM_GETTEXTLENGTHEX, (WPARAM)&tl, 0); 102 | 103 | if(!length) 104 | return false; 105 | 106 | // scale down a little bit, by default everything is way to big 107 | SendMessage(handle(), EM_SETZOOM, 3, 4); 108 | 109 | return true; 110 | } 111 | -------------------------------------------------------------------------------- /src/richedit.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_RICHEDIT_HPP 19 | #define REAPACK_RICHEDIT_HPP 20 | 21 | #include "control.hpp" 22 | 23 | #include 24 | 25 | class RichEdit : public Control { 26 | public: 27 | static void Init(); 28 | 29 | RichEdit(HWND); 30 | ~RichEdit(); 31 | 32 | void setPlainText(const std::string &); 33 | bool setRichText(const std::string &); 34 | 35 | protected: 36 | void onNotify(LPNMHDR, LPARAM) override; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/richedit.mm: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "richedit.hpp" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | void RichEdit::Init() 25 | { 26 | } 27 | 28 | RichEdit::RichEdit(HWND handle) 29 | : Control(handle) 30 | { 31 | } 32 | 33 | RichEdit::~RichEdit() = default; 34 | 35 | void RichEdit::onNotify(LPNMHDR, LPARAM) 36 | { 37 | } 38 | 39 | void RichEdit::setPlainText(const std::string &text) 40 | { 41 | NSString *str = [NSString 42 | stringWithCString: text.c_str() 43 | encoding: NSUTF8StringEncoding 44 | ]; 45 | 46 | NSDictionary *darkMode = [NSDictionary 47 | dictionaryWithObject: NSColor.textColor 48 | forKey: NSForegroundColorAttributeName]; 49 | 50 | NSAttributedString *attrStr = [[NSAttributedString alloc] 51 | initWithString: str attributes: darkMode]; 52 | 53 | auto textView = static_cast(handle()); 54 | [[textView textStorage] setAttributedString: attrStr]; 55 | [attrStr release]; 56 | } 57 | 58 | bool RichEdit::setRichText(const std::string &rtf) 59 | { 60 | NSString *str = [NSString 61 | stringWithCString: rtf.c_str() 62 | encoding: NSUTF8StringEncoding 63 | ]; 64 | 65 | NSAttributedString *attrStr = [[NSAttributedString alloc] 66 | initWithRTF: [str dataUsingEncoding: NSUTF8StringEncoding] 67 | documentAttributes: nullptr]; 68 | 69 | if(!attrStr) 70 | return false; 71 | 72 | auto textView = static_cast(handle()); 73 | [[textView textStorage] setAttributedString: attrStr]; 74 | [textView setTextColor: NSColor.textColor]; // dark mode support 75 | [attrStr release]; 76 | 77 | // auto-detect links, equivalent to Windows' EM_AUTOURLDETECT message 78 | const BOOL isEditable = textView.isEditable; 79 | [textView setEditable: YES]; 80 | [textView setEnabledTextCheckingTypes: NSTextCheckingTypeLink]; 81 | [textView checkTextInDocument: nil]; 82 | [textView setEditable: isEditable]; 83 | 84 | return [[textView string] length]; 85 | } 86 | -------------------------------------------------------------------------------- /src/serializer.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "serializer.hpp" 19 | 20 | #include 21 | #include 22 | 23 | static const unsigned short VERSION = 1; 24 | static const char FIELD_END = '\x20'; 25 | static const char RECORD_END = ','; 26 | 27 | auto Serializer::read(const std::string &input, const int userVersion) -> Data 28 | { 29 | m_userVersion = userVersion; 30 | 31 | bool first = true; 32 | std::istringstream stream(input); 33 | 34 | Data out; 35 | 36 | while(!stream.eof()) { 37 | std::string line; 38 | std::getline(stream, line, RECORD_END); 39 | 40 | std::istringstream lineStream(line); 41 | 42 | Record rec; 43 | for(size_t i = 0; i < rec.size(); i++) { 44 | if(lineStream.eof()) 45 | return out; 46 | 47 | std::string field; 48 | std::getline(lineStream, field, FIELD_END); 49 | 50 | int value; 51 | 52 | try { 53 | value = boost::lexical_cast(field); 54 | } 55 | catch(const boost::bad_lexical_cast &) { 56 | return out; // data is invalid! aborting. 57 | } 58 | 59 | rec[i] = value; 60 | } 61 | 62 | if(first) { 63 | if(rec[0] != m_userVersion || rec[1] != VERSION) 64 | return {}; 65 | 66 | first = false; 67 | } 68 | else 69 | out.push_back(rec); 70 | } 71 | 72 | return out; 73 | } 74 | 75 | std::string Serializer::write(const Data &data) const 76 | { 77 | if(!m_userVersion) 78 | return {}; 79 | 80 | std::ostringstream stream; 81 | 82 | stream 83 | << m_userVersion << FIELD_END 84 | << VERSION << RECORD_END; 85 | 86 | auto it = data.begin(); 87 | 88 | while(true) { 89 | const Record &rec = *it; 90 | 91 | size_t j = 0; 92 | while(true) { 93 | stream << rec[j]; 94 | 95 | if(++j < rec.size()) 96 | stream << FIELD_END; 97 | else 98 | break; 99 | } 100 | 101 | it++; 102 | 103 | if(it != data.end()) 104 | stream << RECORD_END; 105 | else 106 | break; 107 | } 108 | 109 | return stream.str(); 110 | } 111 | -------------------------------------------------------------------------------- /src/serializer.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_SERIALIZER_HPP 19 | #define REAPACK_SERIALIZER_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | class Serializer { 26 | public: 27 | typedef std::array Record; 28 | typedef std::list Data; 29 | 30 | Serializer() : m_userVersion(0) {} 31 | int userVersion() const { return m_userVersion; } 32 | operator bool() const { return m_userVersion > 0; } 33 | 34 | Data read(const std::string &, int userVersion); 35 | std::string write(const Data &in) const; 36 | 37 | private: 38 | int m_userVersion; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/source.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_SOURCE_HPP 19 | #define REAPACK_SOURCE_HPP 20 | 21 | #include "package.hpp" 22 | #include "path.hpp" 23 | #include "platform.hpp" 24 | 25 | class Package; 26 | class Version; 27 | 28 | class Source { 29 | public: 30 | enum Section { 31 | UnknownSection = 0, 32 | MainSection = 1<<0, 33 | MIDIEditorSection = 1<<1, 34 | MIDIInlineEditorSection = 1<<2, 35 | MIDIEventListEditorSection = 1<<3, 36 | MediaExplorerSection = 1<<4, 37 | 38 | ImplicitSection = -1, // for compatibility with v1.0 39 | }; 40 | 41 | static Section getSection(const char *); 42 | static Section detectSection(const Path &category); 43 | 44 | Source(const std::string &file, const std::string &url, const Version *); 45 | 46 | const Version *version() const { return m_version; } 47 | Package::Type type() const; 48 | const std::string &file() const; 49 | const std::string &url() const { return m_url; } 50 | Path targetPath() const; 51 | 52 | void setChecksum(const std::string &checksum) { m_checksum = checksum; } 53 | const std::string &checksum() const { return m_checksum; } 54 | 55 | void setPlatform(Platform p) { m_platform = p; } 56 | Platform platform() const { return m_platform; } 57 | 58 | void setTypeOverride(Package::Type t) { m_type = t; } 59 | Package::Type typeOverride() const { return m_type; } 60 | 61 | void setSections(int); 62 | int sections() const { return m_sections; } 63 | 64 | private: 65 | Platform m_platform; 66 | Package::Type m_type; 67 | std::string m_file; 68 | std::string m_url; 69 | std::string m_checksum; 70 | int m_sections; 71 | Path m_targetPath; 72 | const Version *m_version; 73 | }; 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/string.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "string.hpp" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | std::string String::format(const char *fmt, ...) 27 | { 28 | va_list args; 29 | 30 | va_start(args, fmt); 31 | const int size = vsnprintf(nullptr, 0, fmt, args); 32 | va_end(args); 33 | 34 | std::string buf(size, 0); 35 | 36 | va_start(args, fmt); 37 | vsnprintf(&buf[0], size + 1, fmt, args); 38 | va_end(args); 39 | 40 | return buf; 41 | } 42 | 43 | std::string String::indent(const std::string &text) 44 | { 45 | std::string output; 46 | std::istringstream input(text); 47 | std::string line; 48 | bool first = true; 49 | 50 | while(getline(input, line, '\n')) { 51 | if(first) 52 | first = false; 53 | else 54 | output += "\r\n"; 55 | 56 | boost::algorithm::trim(line); 57 | 58 | if(line.empty()) 59 | continue; 60 | 61 | output += "\x20\x20"; 62 | output += line; 63 | } 64 | 65 | return output; 66 | } 67 | 68 | std::string String::stripRtf(const std::string &rtf) 69 | { 70 | static const std::regex rtfRules( 71 | // passthrough for later replacement to \n 72 | R"((\\line |\\par\}))" "|" 73 | R"(\\line(\n)\s*)" "|" 74 | 75 | // preserve literal braces 76 | R"(\\([\{\}]))" "|" 77 | 78 | // hidden groups (strip contents) 79 | R"(\{\\(?:f\d+|fonttbl|colortbl)[^\{\}]+\})" "|" 80 | 81 | // formatting tags and groups (keep contents) 82 | R"(\\\w+\s?|\{|\})" "|" 83 | 84 | // newlines and indentation 85 | R"(\s*\n\s*)" 86 | ); 87 | 88 | std::string text = std::regex_replace(rtf, rtfRules, "$1$2$3"); 89 | boost::algorithm::replace_all(text, "\\line ", "\n"); 90 | boost::algorithm::replace_all(text, "\\par}", "\n\n"); 91 | boost::algorithm::trim(text); 92 | 93 | return text; 94 | } 95 | 96 | void String::ImplDetail::imbueStream(std::ostream &stream) 97 | { 98 | class NumPunct : public std::numpunct 99 | { 100 | protected: 101 | char do_thousands_sep() const override { return ','; } 102 | std::string do_grouping() const override { return "\3"; } 103 | }; 104 | 105 | stream.imbue(std::locale(std::locale::classic(), new NumPunct)); 106 | } 107 | -------------------------------------------------------------------------------- /src/string.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_STRING_HPP 19 | #define REAPACK_STRING_HPP 20 | 21 | #include 22 | #include 23 | 24 | namespace String { 25 | namespace ImplDetail { 26 | void imbueStream(std::ostream &); 27 | }; 28 | 29 | #ifdef __GNUC__ 30 | __attribute__((format(printf, 1, 2))) 31 | #endif 32 | std::string format(const char *fmt, ...); 33 | 34 | std::string indent(const std::string &); 35 | std::string stripRtf(const std::string &); 36 | 37 | template>> 38 | std::string number(const T v) 39 | { 40 | std::ostringstream stream; 41 | ImplDetail::imbueStream(stream); 42 | stream << v; 43 | return stream.str(); 44 | }; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/synchronize.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "task.hpp" 19 | 20 | #include "config.hpp" 21 | #include "download.hpp" 22 | #include "filesystem.hpp" 23 | #include "index.hpp" 24 | #include "reapack.hpp" 25 | #include "transaction.hpp" 26 | 27 | SynchronizeTask::SynchronizeTask(const Remote &remote, const bool stale, 28 | const bool fullSync, const InstallOpts &opts, Transaction *tx) 29 | : Task(tx), m_remote(remote), m_indexPath(Index::pathFor(m_remote.name())), 30 | m_opts(opts), m_stale(stale), m_fullSync(fullSync) 31 | { 32 | } 33 | 34 | bool SynchronizeTask::start() 35 | { 36 | const auto &netConfig = g_reapack->config()->network; 37 | 38 | time_t mtime = 0, now = time(nullptr); 39 | FS::mtime(m_indexPath, &mtime); 40 | 41 | const time_t threshold = netConfig.staleThreshold; 42 | if(!m_stale && mtime && (!threshold || mtime > now - threshold)) 43 | return true; 44 | 45 | auto dl = new FileDownload(m_indexPath, m_remote.url(), 46 | netConfig, Download::NoCacheFlag); 47 | dl->setName(m_remote.name()); 48 | 49 | dl->onFinishAsync >> [=] { 50 | if(dl->save()) 51 | tx()->receipt()->setIndexChanged(); 52 | }; 53 | 54 | tx()->threadPool()->push(dl); 55 | return true; 56 | } 57 | 58 | void SynchronizeTask::commit() 59 | { 60 | if(!FS::exists(m_indexPath)) 61 | return; 62 | 63 | const IndexPtr &index = tx()->loadIndex(m_remote); // TODO: reuse m_indexPath 64 | if(!index || !m_fullSync) 65 | return; 66 | 67 | for(const Package *pkg : index->packages()) 68 | synchronize(pkg); 69 | 70 | if(m_opts.promptObsolete && !m_remote.isProtected()) { 71 | for(const auto &entry : tx()->registry()->getEntries(m_remote.name())) { 72 | if(!entry.test(Registry::Entry::PinnedFlag) && 73 | !index->find(entry.category, entry.package)) 74 | tx()->addObsolete(entry); 75 | } 76 | } 77 | } 78 | 79 | void SynchronizeTask::synchronize(const Package *pkg) 80 | { 81 | const auto &entry = tx()->registry()->getEntry(pkg); 82 | 83 | if(!entry && !m_opts.autoInstall) 84 | return; 85 | 86 | const bool pres = m_opts.bleedingEdge || entry.test(Registry::Entry::BleedingEdgeFlag); 87 | const Version *latest = pkg->lastVersion(pres, entry.version); 88 | 89 | if(!latest) 90 | return; 91 | 92 | if(entry.version == latest->name()) { 93 | if(FS::allExists(latest->files())) 94 | return; // latest version is really installed, nothing to do here! 95 | } 96 | else if(entry.test(Registry::Entry::PinnedFlag) || latest->name() < entry.version) 97 | return; 98 | 99 | tx()->install(latest, entry, entry.flags); 100 | } 101 | -------------------------------------------------------------------------------- /src/tabbar.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "tabbar.hpp" 19 | 20 | #include "dialog.hpp" 21 | #include "win32.hpp" 22 | 23 | #ifdef _WIN32 24 | # include 25 | #endif 26 | 27 | TabBar::TabBar(HWND handle, Dialog *parent, const Tabs &tabs) 28 | : Control(handle), m_parent(parent), m_lastPage(-1) 29 | { 30 | for(const Tab &tab : tabs) 31 | addTab(tab); 32 | } 33 | 34 | int TabBar::addTab(const Tab &tab) 35 | { 36 | const int index = count(); 37 | 38 | m_pages.push_back(tab.page); 39 | 40 | TCITEM item{}; 41 | item.mask |= TCIF_TEXT; 42 | const auto &&wideText = Win32::widen(tab.text); 43 | item.pszText = const_cast(wideText.c_str()); 44 | 45 | TabCtrl_InsertItem(handle(), index, &item); 46 | 47 | if(index == 0) 48 | switchPage(); 49 | 50 | return index; 51 | } 52 | 53 | int TabBar::currentIndex() const 54 | { 55 | return TabCtrl_GetCurSel(handle()); 56 | } 57 | 58 | void TabBar::setCurrentIndex(const int index) 59 | { 60 | if(index < 0 && index >= count()) 61 | return; 62 | 63 | TabCtrl_SetCurSel(handle(), index); 64 | switchPage(); 65 | } 66 | 67 | void TabBar::setFocus() 68 | { 69 | const int index = currentIndex(); 70 | 71 | if(index > -1 && m_parent->hasFocus()) 72 | SetFocus(m_pages[index].front()); 73 | } 74 | 75 | int TabBar::count() const 76 | { 77 | return TabCtrl_GetItemCount(handle()); 78 | } 79 | 80 | void TabBar::clear() 81 | { 82 | m_pages.clear(); 83 | m_lastPage = -1; 84 | 85 | #ifdef TabCtrl_DeleteAllItems 86 | TabCtrl_DeleteAllItems(handle()); 87 | #else 88 | for(int i = count(); i > 0; i--) 89 | TabCtrl_DeleteItem(handle(), i - 1); 90 | #endif 91 | } 92 | 93 | void TabBar::onNotify(LPNMHDR info, LPARAM) 94 | { 95 | switch(info->code) { 96 | case TCN_SELCHANGE: 97 | switchPage(); 98 | break; 99 | }; 100 | } 101 | 102 | void TabBar::switchPage() 103 | { 104 | InhibitControl block(m_parent->handle()); 105 | 106 | if(m_lastPage > -1) { 107 | for(HWND control : m_pages[m_lastPage]) 108 | ShowWindow(control, SW_HIDE); 109 | } 110 | 111 | const int index = currentIndex(); 112 | onTabChange(index); 113 | 114 | if(index < 0 || static_cast(index) >= m_pages.size()) { 115 | m_lastPage = -1; 116 | return; 117 | } 118 | 119 | const Page &page = m_pages[index]; 120 | m_lastPage = index; 121 | 122 | if(page.empty()) 123 | return; 124 | 125 | for(HWND control : page) 126 | ShowWindow(control, SW_SHOW); 127 | 128 | setFocus(); 129 | } 130 | -------------------------------------------------------------------------------- /src/tabbar.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_TABBAR_HPP 19 | #define REAPACK_TABBAR_HPP 20 | 21 | #include "control.hpp" 22 | 23 | #include "event.hpp" 24 | 25 | #include 26 | 27 | class Dialog; 28 | 29 | class TabBar : public Control { 30 | public: 31 | typedef std::vector Page; 32 | struct Tab { const char *text; Page page; }; 33 | typedef std::vector Tabs; 34 | 35 | TabBar(HWND handle, Dialog *parent, const Tabs &tabs = {}); 36 | int addTab(const Tab &); 37 | int currentIndex() const; 38 | void setCurrentIndex(int); 39 | void setFocus(); 40 | int count() const; 41 | void clear(); 42 | 43 | Event onTabChange; 44 | 45 | protected: 46 | void onNotify(LPNMHDR, LPARAM) override; 47 | 48 | private: 49 | void switchPage(); 50 | 51 | Dialog *m_parent; 52 | int m_lastPage; 53 | std::vector m_pages; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/task.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "task.hpp" 19 | 20 | #include "archive.hpp" 21 | #include "config.hpp" 22 | #include "download.hpp" 23 | #include "errors.hpp" 24 | #include "filesystem.hpp" 25 | #include "index.hpp" 26 | #include "reapack.hpp" 27 | #include "transaction.hpp" 28 | 29 | UninstallTask::UninstallTask(const Registry::Entry &re, Transaction *tx) 30 | : Task(tx), m_entry(std::move(re)) 31 | { 32 | } 33 | 34 | bool UninstallTask::start() 35 | { 36 | tx()->registry()->getFiles(m_entry).swap(m_files); 37 | 38 | // allow conflicting packages to be installed 39 | tx()->registry()->forget(m_entry); 40 | 41 | return true; 42 | } 43 | 44 | void UninstallTask::commit() 45 | { 46 | for(const auto &file : m_files) { 47 | if(!FS::exists(file.path) || FS::removeRecursive(file.path)) 48 | tx()->receipt()->addRemoval(file.path); 49 | else 50 | tx()->receipt()->addError({FS::lastError(), file.path.join()}); 51 | 52 | tx()->registerFile({false, m_entry, file}); 53 | } 54 | 55 | tx()->registry()->forget(m_entry); 56 | } 57 | 58 | FlagsTask::FlagsTask(const Registry::Entry &re, const int flags, Transaction *tx) 59 | : Task(tx), m_entry(std::move(re)), m_flags(flags) 60 | { 61 | } 62 | 63 | void FlagsTask::commit() 64 | { 65 | tx()->registry()->setFlags(m_entry, m_flags); 66 | tx()->receipt()->setPackageChanged(); 67 | } 68 | -------------------------------------------------------------------------------- /src/task.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_TASK_HPP 19 | #define REAPACK_TASK_HPP 20 | 21 | #include "config.hpp" 22 | #include "path.hpp" 23 | #include "registry.hpp" 24 | #include "remote.hpp" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | class ArchiveReader; 32 | class Index; 33 | class Source; 34 | class ThreadTask; 35 | class Transaction; 36 | class Version; 37 | struct InstallOpts; 38 | 39 | typedef std::shared_ptr ArchiveReaderPtr; 40 | typedef std::shared_ptr IndexPtr; 41 | 42 | class Task { 43 | public: 44 | Task(Transaction *parent) : m_tx(parent) {} 45 | virtual ~Task() = default; 46 | 47 | virtual bool start() { return true; } 48 | virtual void commit() = 0; 49 | virtual void rollback() {} 50 | 51 | bool operator<(const Task &o) { return priority() < o.priority(); } 52 | 53 | protected: 54 | virtual int priority() const { return 0; } 55 | Transaction *tx() const { return m_tx; } 56 | 57 | private: 58 | Transaction *m_tx; 59 | }; 60 | 61 | class SynchronizeTask : public Task { 62 | public: 63 | // TODO: remove InstallOpts argument 64 | SynchronizeTask(const Remote &remote, bool stale, bool fullSync, 65 | const InstallOpts &, Transaction *); 66 | 67 | protected: 68 | bool start() override; 69 | void commit() override; 70 | 71 | private: 72 | void synchronize(const Package *); 73 | 74 | Remote m_remote; 75 | Path m_indexPath; 76 | InstallOpts m_opts; 77 | bool m_stale; 78 | bool m_fullSync; 79 | }; 80 | 81 | class InstallTask : public Task { 82 | public: 83 | InstallTask(const Version *ver, int flags, const Registry::Entry &, 84 | const ArchiveReaderPtr &, Transaction *); 85 | 86 | bool start() override; 87 | void commit() override; 88 | void rollback() override; 89 | 90 | private: 91 | void push(ThreadTask *, const TempPath &); 92 | 93 | const Version *m_version; 94 | int m_flags; 95 | Registry::Entry m_oldEntry; 96 | ArchiveReaderPtr m_reader; 97 | 98 | bool m_fail; 99 | IndexPtr m_index; // keep in memory 100 | std::vector m_oldFiles; 101 | std::vector m_newFiles; 102 | std::unordered_set m_waiting; 103 | }; 104 | 105 | class UninstallTask : public Task { 106 | public: 107 | UninstallTask(const Registry::Entry &, Transaction *); 108 | 109 | protected: 110 | int priority() const override { return 1; } 111 | bool start() override; 112 | void commit() override; 113 | 114 | private: 115 | Registry::Entry m_entry; 116 | std::vector m_files; 117 | std::set m_removedFiles; 118 | }; 119 | 120 | class FlagsTask : public Task { 121 | public: 122 | FlagsTask(const Registry::Entry &, int flags, Transaction *); 123 | 124 | protected: 125 | void commit() override; 126 | 127 | private: 128 | Registry::Entry m_entry; 129 | int m_flags; 130 | }; 131 | 132 | class ExportTask : public Task { 133 | public: 134 | ExportTask(const std::string &path, Transaction *); 135 | 136 | protected: 137 | bool start() override; 138 | void commit() override; 139 | void rollback() override; 140 | 141 | private: 142 | TempPath m_path; 143 | }; 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /src/thread.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_THREAD_HPP 19 | #define REAPACK_THREAD_HPP 20 | 21 | #include "errors.hpp" 22 | #include "event.hpp" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | struct ThreadSummary { 32 | const char *step; 33 | std::string item; 34 | }; 35 | 36 | class ThreadTask { 37 | public: 38 | enum State { 39 | Idle, 40 | Queued, 41 | Running, 42 | Success, 43 | Failure, 44 | Aborted, 45 | }; 46 | 47 | ThreadTask(); 48 | virtual ~ThreadTask(); 49 | 50 | virtual bool concurrent() const = 0; 51 | 52 | void exec(); // runs in the current thread 53 | const ThreadSummary &summary() const { return m_summary; } 54 | State state() const { return m_state; } 55 | void setError(const ErrorInfo &err) { m_error = err; } 56 | const ErrorInfo &error() { return m_error; } 57 | 58 | bool aborted() const { return m_abort; } 59 | void abort() { m_abort = true; } 60 | 61 | AsyncEvent onStartAsync; 62 | AsyncEvent onFinishAsync; 63 | 64 | protected: 65 | virtual bool run() = 0; 66 | 67 | void setSummary(const ThreadSummary &s) { m_summary = s; } 68 | 69 | private: 70 | ThreadSummary m_summary; 71 | State m_state; 72 | ErrorInfo m_error; 73 | std::atomic_bool m_abort; 74 | }; 75 | 76 | class WorkerThread { 77 | public: 78 | WorkerThread(); 79 | ~WorkerThread(); 80 | 81 | void push(ThreadTask *); 82 | void clear(); 83 | 84 | private: 85 | void run(); 86 | ThreadTask *nextTask(); 87 | 88 | bool m_stop; 89 | std::mutex m_mutex; 90 | std::condition_variable m_wake; 91 | std::queue m_queue; 92 | 93 | std::thread m_thread; 94 | }; 95 | 96 | class ThreadPool { 97 | public: 98 | ThreadPool() {} 99 | ThreadPool(const ThreadPool &) = delete; 100 | ~ThreadPool(); 101 | 102 | void push(ThreadTask *); 103 | void abort(); 104 | 105 | bool idle() const { return m_running.empty(); } 106 | 107 | Event onPush; 108 | Event onAbort; 109 | Event onDone; 110 | 111 | private: 112 | std::array, 6> m_pool; 113 | std::unordered_set m_running; 114 | }; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /src/time.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "time.hpp" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | Time::Time(const char *iso8601) : m_tm() 25 | { 26 | tm time = {}; 27 | 28 | std::istringstream stream(iso8601); 29 | stream >> std::get_time(&time, "%Y-%m-%dT%H:%M:%S"); 30 | 31 | if(stream.good()) 32 | std::swap(m_tm, time); 33 | } 34 | 35 | Time::Time(int year, int month, int day, int hour, int minute, int second) 36 | : m_tm() 37 | { 38 | m_tm.tm_year = year - 1900; 39 | m_tm.tm_mon = month - 1; 40 | m_tm.tm_mday = day; 41 | 42 | m_tm.tm_hour = hour; 43 | m_tm.tm_min = minute; 44 | m_tm.tm_sec = second; 45 | } 46 | 47 | std::string Time::toString() const 48 | { 49 | if(!isValid()) 50 | return {}; 51 | 52 | char buf[32] = {}; 53 | strftime(buf, sizeof(buf), "%B %d %Y", &m_tm); 54 | return buf; 55 | } 56 | 57 | int Time::compare(const Time &o) const 58 | { 59 | const std::array l{year(), month(), day(), hour(), minute(), second()}; 60 | const std::array r{o.year(), o.month(), o.day(), o.hour(), o.minute(), o.second()}; 61 | 62 | if(l < r) 63 | return -1; 64 | else if(l > r) 65 | return 1; 66 | 67 | return 0; 68 | } 69 | 70 | std::ostream &operator<<(std::ostream &os, const Time &time) 71 | { 72 | os << time.toString(); 73 | return os; 74 | } 75 | -------------------------------------------------------------------------------- /src/time.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_TIME_HPP 19 | #define REAPACK_TIME_HPP 20 | 21 | #include 22 | #include 23 | 24 | class Time { 25 | public: 26 | Time(const char *iso8601); 27 | Time(int year, int month, int day, int hour = 0, int minute = 0, int second = 0); 28 | Time(const std::tm &tm = {}) : m_tm(tm) {} 29 | 30 | bool isValid() const { return m_tm.tm_year > 0; } 31 | operator bool() const { return isValid(); } 32 | 33 | int year() const { return m_tm.tm_year + 1900; } 34 | int month() const { return m_tm.tm_mon + 1; } 35 | int day() const { return m_tm.tm_mday; } 36 | int hour() const { return m_tm.tm_hour; } 37 | int minute() const { return m_tm.tm_min; } 38 | int second() const { return m_tm.tm_sec; } 39 | 40 | std::string toString() const; 41 | 42 | int compare(const Time &) const; 43 | bool operator<(const Time &o) const { return compare(o) < 0; } 44 | bool operator<=(const Time &o) const { return compare(o) <= 0; } 45 | bool operator>(const Time &o) const { return compare(o) > 0; } 46 | bool operator>=(const Time &o) const { return compare(o) >= 0; } 47 | bool operator==(const Time &o) const { return compare(o) == 0; } 48 | bool operator!=(const Time &o) const { return compare(o) != 0; } 49 | 50 | private: 51 | std::tm m_tm; 52 | }; 53 | 54 | std::ostream &operator<<(std::ostream &os, const Time &time); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/version.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_VERSION_HPP 19 | #define REAPACK_VERSION_HPP 20 | 21 | #include "time.hpp" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | class Package; 30 | class Path; 31 | class Source; 32 | 33 | class VersionName { 34 | public: 35 | VersionName(); 36 | VersionName(const std::string &); 37 | VersionName(const VersionName &); 38 | 39 | VersionName &operator=(const VersionName &) = default; 40 | 41 | void parse(const std::string &); 42 | bool tryParse(const std::string &, std::string *errorOut = nullptr); 43 | 44 | size_t size() const { return m_segments.size(); } 45 | bool isStable() const { return m_stable; } 46 | const std::string &toString() const { return m_string; } 47 | 48 | int compare(const VersionName &) const; 49 | 50 | #define COMPOP(op) \ 51 | bool operator op (const VersionName &o) const { return compare(o) op 0; } 52 | 53 | COMPOP(<) COMPOP(<=) COMPOP(>) COMPOP(>=) COMPOP(==) COMPOP(!=) 54 | 55 | #undef COMPOP 56 | 57 | private: 58 | typedef uint16_t Numeric; 59 | typedef std::variant Segment; 60 | 61 | Segment segment(size_t i) const; 62 | 63 | std::string m_string; 64 | std::vector m_segments; 65 | bool m_stable; 66 | }; 67 | 68 | class Version { 69 | public: 70 | static std::string displayAuthor(const std::string &name); 71 | 72 | Version(const std::string &, const Package *); 73 | ~Version(); 74 | 75 | const VersionName &name() const { return m_name; } 76 | const Package *package() const { return m_package; } 77 | std::string fullName() const; 78 | 79 | void setAuthor(const std::string &author) { m_author = author; } 80 | const std::string &author() const { return m_author; } 81 | std::string displayAuthor() const { return displayAuthor(m_author); } 82 | 83 | void setTime(const Time &time) { if(time) m_time = time; } 84 | const Time &time() const { return m_time; } 85 | 86 | void setChangelog(const std::string &cl) { m_changelog = cl; } 87 | const std::string &changelog() const { return m_changelog; } 88 | 89 | bool addSource(const Source *source); 90 | const auto &sources() const { return m_sources; } 91 | const Source *source(size_t i) const { return m_sources[i]; } 92 | 93 | const std::set &files() const { return m_files; } 94 | 95 | private: 96 | VersionName m_name; 97 | std::string m_author; 98 | std::string m_changelog; 99 | Time m_time; 100 | const Package *m_package; 101 | std::vector m_sources; 102 | std::set m_files; 103 | }; 104 | 105 | std::ostream &operator<<(std::ostream &, const Version &); 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /src/win32.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "win32.hpp" 19 | 20 | #ifdef _WIN32 21 | # define widen_cstr(cstr) widen(cstr).c_str() 22 | #else 23 | # include 24 | # define widen_cstr(cstr) cstr 25 | #endif 26 | 27 | #include 28 | 29 | #ifdef _WIN32 30 | std::wstring Win32::widen(const char *input, const UINT codepage) 31 | { 32 | const int size = MultiByteToWideChar(codepage, 0, input, -1, nullptr, 0) - 1; 33 | 34 | std::wstring output(size, 0); 35 | MultiByteToWideChar(codepage, 0, input, -1, &output[0], size); 36 | 37 | return output; 38 | } 39 | 40 | std::string Win32::narrow(const wchar_t *input) 41 | { 42 | const int size = WideCharToMultiByte(CP_UTF8, 0, 43 | input, -1, nullptr, 0, nullptr, nullptr) - 1; 44 | 45 | std::string output(size, 0); 46 | WideCharToMultiByte(CP_UTF8, 0, input, -1, &output[0], size, nullptr, nullptr); 47 | 48 | return output; 49 | } 50 | #endif 51 | 52 | int Win32::messageBox(const HWND handle, const char *text, 53 | const char *title, const unsigned int buttons) 54 | { 55 | return MessageBox(handle, widen_cstr(text), widen_cstr(title), buttons); 56 | } 57 | 58 | std::string Win32::getWindowText(const HWND handle) 59 | { 60 | int buffer_len = GetWindowTextLength(handle); 61 | if(buffer_len) 62 | ++buffer_len; // null terminator 63 | else // REAPER <6.09 on non-Windows, before SWELL a34caf91 64 | buffer_len = 8192; 65 | 66 | std::vector buffer(buffer_len, 0); 67 | GetWindowText(handle, buffer.data(), buffer_len); 68 | 69 | return narrow(buffer.data()); 70 | } 71 | 72 | void Win32::setWindowText(const HWND handle, const char *text) 73 | { 74 | SetWindowText(handle, widen_cstr(text)); 75 | } 76 | 77 | void Win32::shellExecute(const char *what, const char *arg) 78 | { 79 | ShellExecute(nullptr, L("open"), widen_cstr(what), 80 | arg ? widen_cstr(arg) : nullptr, nullptr, SW_SHOW); 81 | } 82 | 83 | HANDLE Win32::globalCopy(const std::string &text) 84 | { 85 | // calculate the size in bytes including the null terminator 86 | const size_t size = (text.size() + 1) * sizeof(char_type); 87 | 88 | HANDLE mem = GlobalAlloc(GMEM_MOVEABLE, size); 89 | memcpy(GlobalLock(mem), widen_cstr(text.c_str()), size); 90 | GlobalUnlock(mem); 91 | 92 | return mem; 93 | } 94 | 95 | bool Win32::writePrivateProfileString(const char *group, const char *key, 96 | const char *value, const char *path) 97 | { 98 | // value can be null for deleting a key 99 | return WritePrivateProfileString(widen_cstr(group), widen_cstr(key), 100 | value ? widen_cstr(value) : nullptr, widen_cstr(path)); 101 | } 102 | 103 | std::string Win32::getPrivateProfileString(const char *group, const char *key, 104 | const char *fallback, const char *path) 105 | { 106 | char_type buffer[4096]; 107 | GetPrivateProfileString(widen_cstr(group), widen_cstr(key), widen_cstr(fallback), 108 | buffer, static_cast(std::size(buffer)), widen_cstr(path)); 109 | 110 | return narrow(buffer); 111 | } 112 | 113 | unsigned int Win32::getPrivateProfileInt(const char *group, const char *key, 114 | const unsigned int fallback, const char *path) 115 | { 116 | return GetPrivateProfileInt(widen_cstr(group), widen_cstr(key), 117 | fallback, widen_cstr(path)); 118 | } 119 | -------------------------------------------------------------------------------- /src/win32.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_WIN32_HPP 19 | #define REAPACK_WIN32_HPP 20 | 21 | // Utility wrappers around the Windows Wide API 22 | 23 | #include 24 | 25 | #ifdef _WIN32 26 | # define L(str) L##str 27 | # include 28 | #else 29 | # define L(str) str 30 | # include 31 | #endif 32 | 33 | namespace Win32 { 34 | #ifdef _WIN32 35 | typedef wchar_t char_type; 36 | 37 | std::wstring widen(const char *, UINT codepage = CP_UTF8); 38 | inline std::wstring widen(const std::string &str, UINT codepage = CP_UTF8) 39 | { return widen(str.c_str(), codepage); } 40 | 41 | std::string narrow(const wchar_t *); 42 | inline std::string narrow(const std::wstring &str) { return narrow(str.c_str()); } 43 | 44 | inline std::string ansi2utf8(const char *str) { return narrow(widen(str, CP_ACP)); } 45 | inline std::string ansi2utf8(const std::string &str) { return ansi2utf8(str.c_str()); } 46 | #else 47 | typedef char char_type; 48 | 49 | inline std::string widen(const char *s, UINT = 0) { return s; } 50 | inline std::string widen(const std::string &s) { return s; } 51 | 52 | inline std::string narrow(const char *s) { return s; } 53 | inline std::string narrow(const std::string &s) { return s; } 54 | #endif 55 | 56 | int messageBox(HWND, const char *text, const char *title, unsigned int buttons); 57 | void setWindowText(HWND handle, const char *text); 58 | std::string getWindowText(HWND handle); 59 | void shellExecute(const char *what, const char *arg = nullptr); 60 | HANDLE globalCopy(const std::string &); 61 | 62 | bool writePrivateProfileString(const char *g, const char *k, const char *v, const char *p); 63 | std::string getPrivateProfileString(const char *g, const char *k, const char *v, const char *p); 64 | unsigned int getPrivateProfileInt(const char *g, const char *k, unsigned int f, const char *p); 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/xml.hpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef REAPACK_XML_HPP 19 | #define REAPACK_XML_HPP 20 | 21 | #include 22 | #include 23 | 24 | class XmlNode; 25 | class XmlString; 26 | 27 | class XmlDocument { 28 | struct Impl; 29 | using ImplPtr = std::unique_ptr; 30 | 31 | public: 32 | XmlDocument(std::istream &); 33 | XmlDocument(const XmlDocument &) = delete; 34 | XmlDocument(XmlDocument &&); 35 | ~XmlDocument(); 36 | 37 | operator bool() const; 38 | const char *error() const; 39 | 40 | XmlNode root() const; 41 | 42 | private: 43 | ImplPtr m_impl; 44 | }; 45 | 46 | class XmlNode { 47 | friend XmlDocument; 48 | struct Impl; 49 | using ImplPtr = std::unique_ptr; 50 | 51 | public: 52 | XmlNode(void *); 53 | XmlNode(const XmlNode &); 54 | ~XmlNode(); 55 | 56 | XmlNode &operator=(const XmlNode &); 57 | operator bool() const; 58 | 59 | const char *name() const; 60 | XmlString attribute(const char *name) const; 61 | bool attribute(const char *name, int *value) const; 62 | XmlString text() const; 63 | 64 | XmlNode firstChild(const char *element = nullptr) const; 65 | XmlNode nextSibling(const char *element = nullptr) const; 66 | 67 | private: 68 | ImplPtr m_impl; 69 | }; 70 | 71 | class XmlString { 72 | public: 73 | XmlString(const void *); 74 | XmlString(const XmlString &) = delete; 75 | XmlString(XmlString &&) = default; 76 | ~XmlString(); 77 | 78 | operator bool() const { return m_str != nullptr; } 79 | const char *operator *() const { return reinterpret_cast(m_str); } 80 | const char *value_or(const char *fallback) const { return m_str ? **this : fallback; } 81 | 82 | private: 83 | const void *m_str; 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/xml_tinyxml2.cpp: -------------------------------------------------------------------------------- 1 | /* ReaPack: Package manager for REAPER 2 | * Copyright (C) 2015-2025 Christian Fillion 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "xml.hpp" 19 | 20 | #include 21 | 22 | #include 23 | 24 | struct XmlDocument::Impl { tinyxml2::XMLDocument doc; }; 25 | struct XmlNode::Impl { tinyxml2::XMLElement *node; }; 26 | 27 | XmlDocument::XmlDocument(std::istream &stream) 28 | : m_impl{std::make_unique()} 29 | { 30 | std::string everything(std::istreambuf_iterator(stream), {}); 31 | m_impl->doc.Parse(everything.c_str(), everything.size()); 32 | } 33 | 34 | XmlDocument::XmlDocument(XmlDocument &&) = default; 35 | XmlDocument::~XmlDocument() = default; 36 | 37 | XmlDocument::operator bool() const 38 | { 39 | return m_impl->doc.ErrorID() == tinyxml2::XML_SUCCESS; 40 | } 41 | 42 | const char *XmlDocument::error() const 43 | { 44 | return !*this ? m_impl->doc.ErrorStr() : nullptr; 45 | } 46 | 47 | XmlNode XmlDocument::root() const 48 | { 49 | return m_impl->doc.RootElement(); 50 | } 51 | 52 | 53 | XmlNode::XmlNode(void *node) 54 | : m_impl(new Impl{static_cast(node)}) {} 55 | XmlNode::XmlNode(const XmlNode ©) { *this = copy; } 56 | XmlNode::~XmlNode() = default; 57 | 58 | XmlNode &XmlNode::operator=(const XmlNode &other) 59 | { 60 | m_impl.reset(new Impl(*other.m_impl)); 61 | return *this; 62 | } 63 | 64 | XmlNode::operator bool() const 65 | { 66 | return m_impl->node != nullptr; 67 | } 68 | 69 | const char *XmlNode::name() const 70 | { 71 | return m_impl->node->Value(); 72 | } 73 | 74 | XmlString XmlNode::attribute(const char *name) const 75 | { 76 | return m_impl->node->Attribute(name); 77 | } 78 | 79 | bool XmlNode::attribute(const char *name, int *output) const 80 | { 81 | return m_impl->node->QueryIntAttribute(name, output) == tinyxml2::XML_SUCCESS; 82 | } 83 | 84 | XmlString XmlNode::text() const 85 | { 86 | return m_impl->node->GetText(); 87 | } 88 | 89 | XmlNode XmlNode::firstChild(const char *element) const 90 | { 91 | return m_impl->node->FirstChildElement(element); 92 | } 93 | 94 | XmlNode XmlNode::nextSibling(const char *element) const 95 | { 96 | return m_impl->node->NextSiblingElement(element); 97 | } 98 | 99 | XmlString::XmlString(const void *str) : m_str(str) {} 100 | XmlString::~XmlString() = default; 101 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Catch2 REQUIRED) 2 | mark_as_advanced(Catch2_DIR) 3 | 4 | add_executable(tests EXCLUDE_FROM_ALL 5 | action.cpp 6 | api.cpp 7 | database.cpp 8 | event.cpp 9 | filesystem.cpp 10 | filter.cpp 11 | hash.cpp 12 | helper.cpp 13 | helper.hpp 14 | index.cpp 15 | index_v1.cpp 16 | metadata.cpp 17 | package.cpp 18 | path.cpp 19 | platform.cpp 20 | receipt.cpp 21 | registry.cpp 22 | remote.cpp 23 | serializer.cpp 24 | source.cpp 25 | string.cpp 26 | time.cpp 27 | version.cpp 28 | win32.cpp 29 | xml.cpp 30 | ) 31 | 32 | # std::uncaught_exceptions is unavailable prior to macOS 10.12 33 | target_compile_definitions(tests PRIVATE CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) 34 | target_include_directories(tests PRIVATE 35 | ${CMAKE_SOURCE_DIR}/src 36 | ${CMAKE_SOURCE_DIR}/vendor ${CMAKE_SOURCE_DIR}/vendor/reaper-sdk/sdk 37 | ) 38 | target_link_libraries(tests Catch2::Catch2WithMain reapack) 39 | -------------------------------------------------------------------------------- /test/action.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | #include 5 | 6 | static const char *M = "[action]"; 7 | 8 | using namespace std::string_literals; 9 | 10 | TEST_CASE("registering command ID", M) { 11 | static const char *commandName = nullptr; 12 | 13 | plugin_register = [](const char *what, void *data) { 14 | if(!strcmp(what, "command_id")) { 15 | commandName = static_cast(data); 16 | return 1234; 17 | } 18 | 19 | return 0; 20 | }; 21 | 22 | const Action action("HELLO", "Hello World", {}); 23 | REQUIRE(action.id() == 1234); 24 | REQUIRE(commandName == "HELLO"s); 25 | } 26 | 27 | TEST_CASE("registering action in REAPER's Action List", M) { 28 | static gaccel_register_t *reg = nullptr; 29 | 30 | plugin_register = [](const char *what, void *data) { 31 | if(!strcmp(what, "command_id")) 32 | return 4321; 33 | else if(!strcmp(what, "gaccel")) 34 | reg = static_cast(data); 35 | 36 | return 0; 37 | }; 38 | 39 | const Action action("HELLO", "Hello World", {}); 40 | CHECK(reg != nullptr); 41 | REQUIRE(reg->accel.cmd == 4321); 42 | REQUIRE(reg->desc == "Hello World"s); 43 | } 44 | 45 | TEST_CASE("commands & actions are unregistered on destruction", M) { 46 | static std::vector reglog; 47 | static std::vector datalog; 48 | 49 | plugin_register = [](const char *what, void *data) { 50 | reglog.push_back(what); 51 | datalog.push_back(data); 52 | return 0; 53 | }; 54 | 55 | { 56 | const Action action("HELLO", "Hello World", {}); 57 | REQUIRE(reglog == std::vector{"command_id", "gaccel"}); 58 | } 59 | 60 | REQUIRE(reglog == std::vector{ 61 | "command_id", "gaccel", "-gaccel", "-command_id"}); 62 | REQUIRE(datalog[0] == datalog[3]); 63 | REQUIRE(datalog[1] == datalog[2]); 64 | } 65 | 66 | TEST_CASE("run action", M) { 67 | static int commandId = 0; 68 | 69 | plugin_register = [](const char *what, void *data) { 70 | if(!strcmp(what, "command_id")) 71 | return ++commandId; 72 | return 0; 73 | }; 74 | 75 | int a = 0, b = 0; 76 | ActionList list; 77 | list.add("Action1", "First action", [&a] { ++a; }); 78 | list.add("Action2", "Second action", [&b] { ++b; }); 79 | 80 | REQUIRE(!list.run(0)); 81 | 82 | CHECK(a == 0); 83 | REQUIRE(list.run(1)); 84 | REQUIRE(a == 1); 85 | 86 | CHECK(b == 0); 87 | REQUIRE(list.run(2)); 88 | REQUIRE(b == 1); 89 | 90 | REQUIRE(!list.run(3)); 91 | } 92 | -------------------------------------------------------------------------------- /test/api.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | static const char *M = "[api]"; 8 | 9 | TEST_CASE("CompareVersions", M) { 10 | const auto CompareVersions = (int (*)(const char *, const char *, 11 | char *, int))API::CompareVersions.cImpl; 12 | 13 | char error[255] = {}; 14 | 15 | SECTION("equal") { 16 | REQUIRE(CompareVersions("1.0", "1.0", error, sizeof(error)) == 0); 17 | REQUIRE(strcmp(error, "") == 0); 18 | } 19 | 20 | SECTION("lower") { 21 | REQUIRE(CompareVersions("1.0", "2.0", error, sizeof(error)) < 0); 22 | REQUIRE(strcmp(error, "") == 0); 23 | } 24 | 25 | SECTION("higher") { 26 | REQUIRE(CompareVersions("2.0", "1.0", error, sizeof(error)) > 0); 27 | REQUIRE(strcmp(error, "") == 0); 28 | } 29 | 30 | SECTION("invalid") { 31 | REQUIRE(CompareVersions("abc", "def", error, sizeof(error)) == 0); 32 | REQUIRE(strcmp(error, "invalid version name 'abc'") == 0); 33 | } 34 | 35 | SECTION("invalid no error buffer") 36 | CompareVersions("abc", "def", nullptr, 0); // no crash 37 | } 38 | -------------------------------------------------------------------------------- /test/filesystem.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | #include 5 | 6 | static const char *M = "[filesystem]"; 7 | static const Path RIPATH("test/indexes"); 8 | 9 | TEST_CASE("open unicode file", M) { 10 | UseRootPath root(RIPATH); 11 | const Path &path = Index::pathFor("Новая папка"); 12 | 13 | FILE *file = FS::open(path); 14 | REQUIRE(file); 15 | fclose(file); 16 | } 17 | 18 | TEST_CASE("file modification time", M) { 19 | UseRootPath root(RIPATH); 20 | const Path &path = Index::pathFor("Новая папка"); 21 | 22 | time_t time = 0; 23 | REQUIRE(FS::mtime(path, &time)); 24 | REQUIRE(time > 0); 25 | } 26 | 27 | TEST_CASE("file exists", M) { 28 | UseRootPath root(RIPATH); 29 | 30 | SECTION("file") { 31 | const Path &file = Index::pathFor("Новая папка"); 32 | REQUIRE(FS::exists(file)); 33 | REQUIRE_FALSE(FS::exists(file, true)); 34 | } 35 | 36 | SECTION("directory") { 37 | REQUIRE_FALSE(FS::exists(Path("ReaPack"))); 38 | REQUIRE(FS::exists(Path("ReaPack"), true)); 39 | } 40 | } 41 | 42 | TEST_CASE("all files exists", M) { 43 | UseRootPath root(RIPATH); 44 | 45 | REQUIRE(FS::allExists(std::vector{})); 46 | 47 | REQUIRE(FS::allExists(std::set{ 48 | Index::pathFor("future_version"), 49 | Index::pathFor("broken"), 50 | })); 51 | 52 | REQUIRE_FALSE(FS::allExists(std::list{ 53 | Index::pathFor("future_version"), 54 | Index::pathFor("not_found"), 55 | })); 56 | 57 | REQUIRE_FALSE(FS::allExists(std::vector{"ReaPack"})); // directory 58 | REQUIRE(FS::allExists(std::vector{"ReaPack"}, true)); 59 | } 60 | -------------------------------------------------------------------------------- /test/hash.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | 5 | static const char *M = "[hash]"; 6 | 7 | TEST_CASE("sha256 hashes", M) { 8 | Hash hash(Hash::SHA256); 9 | 10 | SECTION("empty") 11 | REQUIRE(hash.digest() == 12 | "1220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); 13 | 14 | SECTION("digest twice") 15 | REQUIRE(hash.digest() == hash.digest()); 16 | 17 | SECTION("single chunk") { 18 | hash.addData("hello world", 11); 19 | 20 | REQUIRE(hash.digest() == 21 | "1220b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"); 22 | } 23 | 24 | SECTION("split chunks") { 25 | hash.addData("foo bar", 7); 26 | hash.addData(" bazqux", 4); 27 | 28 | REQUIRE(hash.digest() == 29 | "1220dbd318c1c462aee872f41109a4dfd3048871a03dedd0fe0e757ced57dad6f2d7"); 30 | } 31 | } 32 | 33 | TEST_CASE("invalid algorithm", M) { 34 | Hash hash(static_cast(0)); 35 | hash.addData("foo bar", 7); 36 | REQUIRE(hash.digest() == ""); 37 | } 38 | 39 | TEST_CASE("get hash algorithm", M) { 40 | Hash::Algorithm algo; 41 | 42 | SECTION("empty string") 43 | REQUIRE_FALSE(Hash::getAlgorithm("", &algo)); 44 | 45 | SECTION("only sha-256 ID") 46 | REQUIRE_FALSE(Hash::getAlgorithm("12", &algo)); 47 | 48 | SECTION("unexpected size") 49 | REQUIRE_FALSE(Hash::getAlgorithm("1202ab", &algo)); 50 | 51 | SECTION("seemingly good (but not actually) sha-256") { 52 | REQUIRE(Hash::getAlgorithm("1202abcd", &algo)); 53 | REQUIRE(algo == Hash::SHA256); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/helper.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | std::ostream &operator<<(std::ostream &os, const std::set &list) 8 | { 9 | os << '{'; 10 | 11 | for(const Path &path : list) 12 | os << path << ", "; 13 | 14 | os << '}'; 15 | 16 | return os; 17 | } 18 | -------------------------------------------------------------------------------- /test/helper.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class Path; 5 | class Time; 6 | 7 | std::ostream &operator<<(std::ostream &, const std::set &); 8 | 9 | // include Catch only after having declared our ostream overloads 10 | #include 11 | #include 12 | #include 13 | -------------------------------------------------------------------------------- /test/indexes/ReaPack/cache/broken.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/indexes/ReaPack/cache/future_version.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/indexes/ReaPack/cache/invalid_version.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/indexes/ReaPack/cache/wrong_root.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/indexes/ReaPack/cache/Новая папка.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/author.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://google.com/ 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/changelog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | http://google.com 6 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/explicit_sections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://google.com/ 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | http://twitter.com/cfi30 6 | http://google.com 7 | 8 | 10 | 11 | Donate 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/missing_platform.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | hello 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/missing_source_file.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://encrypted.google.com/ 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/missing_source_url.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/missing_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/missing_version.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/pkg_desc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://google.com/ 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/pkg_metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | http://google.com 6 | 7 | 8 | 9 | 11 | 12 | Donate 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/src_platform.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://google.com/ 6 | https://google.com/ 7 | https://google.com/ 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/src_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://google.com/ 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/time.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://google.com/ 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/unnamed_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/unnamed_package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/unsupported_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | http://google.com 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/valid_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://google.com/ 6 | http://cfillion.tk/ 7 | Fixed a division by zero error. 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/wrong_category_tag.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/wrong_package_tag.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/indexes/v1/ReaPack/cache/wrong_version_tag.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/metadata.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | 5 | static const char *M = "[metadata]"; 6 | 7 | TEST_CASE("repository links", M) { 8 | Metadata md; 9 | CHECK(md.links().empty()); 10 | 11 | SECTION("website links") { 12 | md.addLink(Metadata::WebsiteLink, {"First", "http://example.com"}); 13 | REQUIRE(md.links().count(Metadata::WebsiteLink) == 1); 14 | md.addLink(Metadata::WebsiteLink, {"Second", "http://example.com"}); 15 | 16 | auto link = md.links().begin(); 17 | REQUIRE(link->first == Metadata::WebsiteLink); 18 | REQUIRE(link->second.name == "First"); 19 | REQUIRE((++link)->second.name == "Second"); 20 | 21 | REQUIRE(md.links().count(Metadata::DonationLink) == 0); 22 | } 23 | 24 | SECTION("donation links") { 25 | md.addLink(Metadata::DonationLink, {"First", "http://example.com"}); 26 | REQUIRE(md.links().count(Metadata::DonationLink) == 1); 27 | REQUIRE(md.links().begin()->first == Metadata::DonationLink); 28 | } 29 | 30 | SECTION("drop invalid links") { 31 | md.addLink(Metadata::WebsiteLink, {"name", "not http(s)"}); 32 | REQUIRE(md.links().count(Metadata::WebsiteLink) == 0); 33 | } 34 | } 35 | 36 | TEST_CASE("link type from stmdng", M) { 37 | REQUIRE(Metadata::getLinkType("website") == Metadata::WebsiteLink); 38 | REQUIRE(Metadata::getLinkType("donation") == Metadata::DonationLink); 39 | REQUIRE(Metadata::getLinkType("screenshot") == Metadata::ScreenshotLink); 40 | REQUIRE(Metadata::getLinkType("bacon") == Metadata::WebsiteLink); 41 | } 42 | 43 | TEST_CASE("about text", M) { 44 | Metadata md; 45 | CHECK(md.about().empty()); 46 | 47 | md.setAbout("Hello World"); 48 | REQUIRE(md.about() == "Hello World"); 49 | } 50 | -------------------------------------------------------------------------------- /test/serializer.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | 5 | static const char *M = "[serializer]"; 6 | 7 | TEST_CASE("read from serialized data", M) { 8 | Serializer s; 9 | REQUIRE(s.userVersion() == 0); 10 | REQUIRE_FALSE(s); 11 | 12 | SECTION("valid") { 13 | const auto &out = s.read("1 1,1 2,3 4,5 6", 1); 14 | REQUIRE(out.size() == 3); 15 | auto it = out.begin(); 16 | REQUIRE(*it++ == (Serializer::Record{1,2})); 17 | REQUIRE(*it++ == (Serializer::Record{3,4})); 18 | REQUIRE(*it++ == (Serializer::Record{5,6})); 19 | 20 | REQUIRE(s.userVersion() == 1); 21 | REQUIRE(s); 22 | } 23 | 24 | SECTION("wrong user version") { 25 | const auto &out = s.read("1 1,1 2,3 4,5 6", 2); 26 | REQUIRE(out.empty()); 27 | REQUIRE(s.userVersion() == 2); 28 | REQUIRE(s); 29 | } 30 | 31 | SECTION("wrong data version") { 32 | const auto &out = s.read("1 42,1 2,3 4,5 6", 1); 33 | REQUIRE(out.empty()); 34 | REQUIRE(s.userVersion() == 1); 35 | REQUIRE(s); 36 | } 37 | 38 | SECTION("not an integer") { 39 | const auto &out = s.read("1 1,1 2,hello world,3 4", 1); 40 | REQUIRE(out.size() == 1); 41 | REQUIRE(out.front() == (Serializer::Record{1,2})); 42 | } 43 | 44 | SECTION("single field") { 45 | const auto &out = s.read("1 1,1 2,3,4 5", 1); 46 | REQUIRE(out.size() == 1); 47 | REQUIRE(out.front() == (Serializer::Record{1,2})); 48 | } 49 | 50 | SECTION("empty string") { 51 | const auto &out = s.read({}, 1); 52 | REQUIRE(out.empty()); 53 | } 54 | } 55 | 56 | TEST_CASE("write to string", M) { 57 | Serializer s; 58 | REQUIRE(s.write({{1, 2}}).empty()); // no user version set 59 | s.read({}, 42); 60 | 61 | REQUIRE(s.write({{1, 2}, {3, 4}}) == "42 1,1 2,3 4"); 62 | } 63 | -------------------------------------------------------------------------------- /test/string.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | 5 | static const char *M = "[string]"; 6 | 7 | TEST_CASE("string format", M) { 8 | const std::string &formatted = String::format("%d%% Hello %s!", 100, "World"); 9 | CHECK(formatted.size() == 17); 10 | REQUIRE(formatted == "100% Hello World!"); 11 | } 12 | 13 | TEST_CASE("indent string", M) { 14 | std::string actual; 15 | 16 | SECTION("simple") 17 | actual = String::indent("line1\nline2"); 18 | 19 | SECTION("already indented") 20 | actual = String::indent(" line1\n line2"); 21 | 22 | REQUIRE(actual == " line1\r\n line2"); 23 | } 24 | 25 | TEST_CASE("pretty-print numbers", M) { 26 | REQUIRE(String::number(42'000'000) == "42,000,000"); 27 | } 28 | 29 | TEST_CASE("strip RTF header", M) { 30 | REQUIRE("Hello World" == String::stripRtf(R"( 31 | {\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard 32 | Hello World 33 | } 34 | )")); 35 | } 36 | 37 | TEST_CASE("strip RTF color header", M) { 38 | REQUIRE("Hello World" == String::stripRtf(R"( 39 | {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 Courier;}} 40 | {\colortbl;\red255\green0\blue0;\red0\green0\blue255;} 41 | \widowctrl\hyphauto 42 | Hello World 43 | } 44 | )")); 45 | } 46 | 47 | TEST_CASE("strip RTF paragraphs", M) { 48 | REQUIRE("foo\n\nbar\n\nbaz" == String::stripRtf(R"( 49 | {\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 foo\par} 50 | {\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 bar\par} 51 | {\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 baz\par} 52 | )")); 53 | } 54 | 55 | TEST_CASE("strip RTF line breaks", M) { 56 | REQUIRE("foo\nbar\nbaz" == String::stripRtf(R"(foo\line bar\line 57 | baz\line)")); 58 | } 59 | 60 | TEST_CASE("strip RTF literal braces", M) { 61 | REQUIRE("{ }" == String::stripRtf(R"(\{ \})")); 62 | } 63 | -------------------------------------------------------------------------------- /test/time.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | 5 | static const char *M = "[time]"; 6 | 7 | TEST_CASE("valid time", M) { 8 | const Time time("2016-02-12T01:16:40Z"); 9 | REQUIRE(time.year() == 2016); 10 | REQUIRE(time.month() == 2); 11 | REQUIRE(time.day() == 12); 12 | REQUIRE(time.hour() == 1); 13 | REQUIRE(time.minute() == 16); 14 | REQUIRE(time.second() == 40); 15 | REQUIRE(time == Time(2016, 2, 12, 1, 16, 40)); 16 | REQUIRE(time.isValid()); 17 | REQUIRE(time); 18 | } 19 | 20 | TEST_CASE("garbage time string", M) { 21 | const Time time("hello world"); 22 | REQUIRE_FALSE(time.isValid()); 23 | REQUIRE_FALSE(time); 24 | REQUIRE(time == Time()); 25 | REQUIRE(time.toString() == ""); 26 | } 27 | 28 | TEST_CASE("out of range time string", M) { 29 | const Time time("2016-99-99T99:99:99Z"); 30 | REQUIRE_FALSE(time.isValid()); 31 | REQUIRE_FALSE(time); 32 | REQUIRE(time == Time()); 33 | REQUIRE(time.toString() == ""); 34 | } 35 | 36 | TEST_CASE("compare times", M) { 37 | SECTION("equality") { 38 | REQUIRE(Time(2016,1,2,3,4,5).compare(Time(2016,1,2,3,4,5)) == 0); 39 | 40 | REQUIRE(Time(2016,1,2,3,4,5) == Time(2016,1,2,3,4,5)); 41 | REQUIRE_FALSE(Time(2016,1,2,3,4,5) == Time(2016,1,2,3,4)); 42 | } 43 | 44 | SECTION("inequality") { 45 | REQUIRE_FALSE(Time(2016,1,2,3,4,5) != Time(2016,1,2,3,4,5)); 46 | REQUIRE(Time(2016,1,2,3,4,5) != Time(2017,5,4,3,2,1)); 47 | } 48 | 49 | SECTION("less than") { 50 | REQUIRE(Time(2015, 2, 3).compare(Time(2016, 2, 3)) == -1); 51 | 52 | REQUIRE(Time(2015, 2, 3) < Time(2016, 2, 3)); 53 | REQUIRE_FALSE(Time(2016, 2, 3) < Time(2016, 2, 3)); 54 | REQUIRE_FALSE(Time(2016, 2, 1) < Time(2015, 2, 2)); 55 | } 56 | 57 | SECTION("less than or equal") { 58 | REQUIRE(Time(2015, 2, 3) <= Time(2016, 2, 3)); 59 | REQUIRE(Time(2016, 2, 3) <= Time(2016, 2, 3)); 60 | REQUIRE_FALSE(Time(2016, 2, 1) <= Time(2015, 2, 2)); 61 | } 62 | 63 | SECTION("greater than") { 64 | REQUIRE(Time(2016, 2, 3).compare(Time(2015, 2, 3)) == 1); 65 | 66 | REQUIRE_FALSE(Time(2015, 2, 30) > Time(2016, 2, 3)); 67 | REQUIRE_FALSE(Time(2016, 2, 3) > Time(2016, 2, 3)); 68 | REQUIRE(Time(2016, 2, 3) > Time(2015, 2, 3)); 69 | } 70 | 71 | SECTION("greater than or equal") { 72 | REQUIRE_FALSE(Time(2015, 2, 30) >= Time(2016, 2, 3)); 73 | REQUIRE(Time(2016, 2, 3) >= Time(2016, 2, 3)); 74 | REQUIRE(Time(2016, 2, 3) >= Time(2015, 2, 3)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/win32.cpp: -------------------------------------------------------------------------------- 1 | #include "helper.hpp" 2 | 3 | #include 4 | 5 | static const char *M = "[win32]"; 6 | 7 | TEST_CASE("widen string", M) { 8 | SECTION("ascii") { 9 | const auto &wide = Win32::widen("hello world"); 10 | REQUIRE(wide == L("hello world")); 11 | REQUIRE(wide.size() == 11); 12 | } 13 | 14 | SECTION("cyrillic") { 15 | const auto &wide = Win32::widen("世界"); 16 | REQUIRE(wide == L("\u4e16\u754c")); 17 | #ifdef _WIN32 18 | REQUIRE(wide.size() == 2); 19 | #else 20 | REQUIRE(wide.size() == 6); 21 | #endif 22 | } 23 | } 24 | 25 | TEST_CASE("narrow string", M) { 26 | SECTION("ascii") { 27 | const auto &narrow = Win32::narrow(L("hello world")); 28 | REQUIRE(narrow == "hello world"); 29 | } 30 | 31 | SECTION("cyrillic") { 32 | const auto &narrow = Win32::narrow(L("\u4e16\u754c")); 33 | REQUIRE(narrow == "世界"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reapack", 3 | "version-string": "current", 4 | "dependencies": [ 5 | { "name": "boost-algorithm", "platform": "!linux" }, 6 | { "name": "boost-lexical-cast", "platform": "!linux" }, 7 | { "name": "boost-logic", "platform": "!linux" }, 8 | { "name": "boost-math", "platform": "!linux" }, 9 | { "name": "boost-preprocessor", "platform": "!linux" }, 10 | { "name": "boost-range", "platform": "!linux" }, 11 | "catch2", 12 | { "name": "curl", "platform": "windows", "features": [ "non-http" ] }, 13 | "sqlite3", 14 | { "name": "tinyxml2", "platform": "windows" } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /vendor/reaper_plugin_secrets.h: -------------------------------------------------------------------------------- 1 | #include "reaper_plugin_functions.h" 2 | 3 | // The following API functions are undocumented and not included in 4 | // reaper_plugin_functions.h. 5 | 6 | REAPERAPI_DEF bool (*ListView_HeaderHitTest)(HWND, POINT); 7 | --------------------------------------------------------------------------------