Compare commits

..

6 commits

Author SHA1 Message Date
Syping
5c43a39d13 libragephoto 0.8.2 release
- CMakeLists.txt: update version to 0.8.2
- RagePhoto.cpp: fix memory issue on Windows convertPath function for C+
+11 support
- RagePhoto.cs: code style changes for Boolean
- RagePhoto.h: update copyright header
- RagePhoto.hpp: update copyright header
- RagePhotoLibrary.h: update copyright header
- src/dotnet: add package tags and update version to 0.8.2
2025-11-30 08:13:17 +01:00
Syping
728a3dff6c libragephoto 0.8.1 release
- CMakeLists.txt: update version to 0.8.1
- doc: depend on CMake >= 3.16
- ragephoto-gtkviewer: depend on CMake >= 3.16 and ragephoto >= 0.8
- ragephoto-qtviewer: depend on CMake >= 3.16 and ragephoto >= 0.8
- src/dotnet: update version to 0.8.1
2025-11-11 19:22:47 +01:00
Syping
b87fd1d8fd libragephoto: fix memory issues with Jpeg saving
- RagePhoto.c: return PHOTOREADERROR when no Jpeg is being set when
ragephotodata_savef is being called with Jpeg format
- RagePhoto.cpp: return PhotoReadError when no Jpeg is being set when
RagePhoto::save is being called with Jpeg format
2025-11-10 20:16:25 +01:00
Syping
b150959727 libragephoto: fix jpegSize not set to 0 when being cleared 2025-11-10 20:03:45 +01:00
Syping
c8cd8112f0 libragephoto 0.8.0 release
- CMakeLists.txt: update version to 0.8.0
- src/dotnet: add .NET 8.0 target
- src/dotnet: add README.md
- src/dotnet: fix .targets file for .NET Framework 4.7
- src/dotnet: simplify exception string handling
- src/dotnet: update version to 0.8.0
2025-11-10 17:58:06 +01:00
Syping
3ea95bab44 GitHub Actions: simplify .NET workflow artifact management 2025-11-09 22:20:48 +01:00
15 changed files with 153 additions and 133 deletions

View file

@ -8,42 +8,40 @@ jobs:
Linux: Linux:
runs-on: ${{matrix.runner}} runs-on: ${{matrix.runner}}
container: container:
image: almalinux:8 image: ${{matrix.image}}
strategy: strategy:
matrix: matrix:
arch: [arm64, x64] arch: [arm64, x64]
libc: [glibc]
include: include:
- arch: arm64 - arch: arm64
runner: ubuntu-24.04-arm runner: ubuntu-24.04-arm
- arch: x64 - arch: x64
runner: ubuntu-24.04 runner: ubuntu-24.04
- libc: glibc
image: almalinux:8
install: dnf install -y cmake gcc gcc-c++
libdir: lib64
steps: steps:
- name: Cloning - name: Cloning
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Install packages - name: Install packages
run: dnf install -y cmake gcc gcc-c++ run: ${{matrix.install}}
- name: Configure CMake - name: Configure CMake
run: | run: cmake -B "build/shared" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
cmake -B "build/shared" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
cmake -B "build/static" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DRAGEPHOTO_STATIC=ON -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build - name: Build
run: | run: cmake --build "build/shared" --config ${{env.BUILD_TYPE}}
cmake --build "build/shared" --config ${{env.BUILD_TYPE}}
cmake --build "build/static" --config ${{env.BUILD_TYPE}}
- name: Install - name: Install
run: | run: cmake --install "build/shared" --config ${{env.BUILD_TYPE}} --prefix "install/shared/"
cmake --install "build/shared" --config ${{env.BUILD_TYPE}} --prefix "install/shared/"
cmake --install "build/static" --config ${{env.BUILD_TYPE}} --prefix "install/static/"
- name: Preparing for Upload - name: Preparing for Upload
run: | run: |
mkdir "artifacts" mkdir "artifacts"
cp "install/shared/lib64/libragephoto.so" \ cp "install/shared/${{matrix.libdir}}/libragephoto.so" \
"install/static/lib64/libragephoto.a" \
"artifacts/" "artifacts/"
- name: Upload - name: Upload
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v5
with: with:
name: Linux ${{matrix.arch}} name: Linux ${{matrix.arch}} ${{matrix.libc}}
path: ${{github.workspace}}/artifacts/ path: ${{github.workspace}}/artifacts/
macOS: macOS:
runs-on: ${{matrix.runner}} runs-on: ${{matrix.runner}}
@ -61,22 +59,15 @@ jobs:
- name: Cloning - name: Cloning
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Configure CMake - name: Configure CMake
run: | run: cmake -B "${{github.workspace}}/build/shared" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_DEPLOYMENT_TARGET=${{env.OSX_DEPLOYMENT_TARGET}}
cmake -B "${{github.workspace}}/build/shared" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_DEPLOYMENT_TARGET=${{env.OSX_DEPLOYMENT_TARGET}}
cmake -B "${{github.workspace}}/build/static" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DRAGEPHOTO_STATIC=ON -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_DEPLOYMENT_TARGET=${{env.OSX_DEPLOYMENT_TARGET}}
- name: Build - name: Build
run: | run: cmake --build "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}}
cmake --build "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}}
cmake --build "${{github.workspace}}/build/static" --config ${{env.BUILD_TYPE}}
- name: Install - name: Install
run: | run: cmake --install "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}} --prefix "${{github.workspace}}/install/shared/"
cmake --install "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}} --prefix "${{github.workspace}}/install/shared/"
cmake --install "${{github.workspace}}/build/static" --config ${{env.BUILD_TYPE}} --prefix "${{github.workspace}}/install/static/"
- name: Preparing for Upload - name: Preparing for Upload
run: | run: |
mkdir "artifacts" mkdir "artifacts"
cp "install/shared/lib/libragephoto.dylib" \ cp "install/shared/lib/libragephoto.dylib" \
"install/static/lib/libragephoto.a" \
"artifacts/" "artifacts/"
- name: Upload - name: Upload
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v5
@ -103,26 +94,19 @@ jobs:
with: with:
arch: ${{matrix.msvc}} arch: ${{matrix.msvc}}
- name: Configure CMake - name: Configure CMake
run: | run: cmake -B "${{github.workspace}}/build/shared" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DRAGEPHOTO_UNICODE=wincvt -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -GNinja
cmake -B "${{github.workspace}}/build/shared" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DRAGEPHOTO_UNICODE=wincvt -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
cmake -B "${{github.workspace}}/build/static" -DRAGEPHOTO_C_LIBRARY=ON -DRAGEPHOTO_EXTRACT=OFF -DRAGEPHOTO_STATIC=ON -DRAGEPHOTO_UNICODE=wincvt -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build - name: Build
run: | run: cmake --build "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}}
cmake --build "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}}
cmake --build "${{github.workspace}}/build/static" --config ${{env.BUILD_TYPE}}
- name: Install - name: Install
run: | run: cmake --install "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}} --prefix "${{github.workspace}}/install/shared/"
cmake --install "${{github.workspace}}/build/shared" --config ${{env.BUILD_TYPE}} --prefix "${{github.workspace}}/install/shared/"
cmake --install "${{github.workspace}}/build/static" --config ${{env.BUILD_TYPE}} --prefix "${{github.workspace}}/install/static/"
- name: Preparing for Upload - name: Preparing for Upload
run: | run: |
mkdir "artifacts" mkdir "artifacts"
Copy-Item "install\shared\bin\libragephoto.dll" "artifacts\" Copy-Item "install\shared\bin\libragephoto.dll" "artifacts\"
Copy-Item "install\static\lib\ragephoto.lib" "artifacts\"
- name: Upload - name: Upload
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v5
with: with:
name: Windows MSVC ${{matrix.arch}} name: Windows ${{matrix.arch}}
path: ${{github.workspace}}/artifacts/ path: ${{github.workspace}}/artifacts/
Release: Release:
needs: [Linux, macOS, Windows] needs: [Linux, macOS, Windows]
@ -130,15 +114,15 @@ jobs:
steps: steps:
- name: Cloning - name: Cloning
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Download Linux arm64 Artifacts - name: Download Linux arm64 glibc Artifacts
uses: actions/download-artifact@v6 uses: actions/download-artifact@v6
with: with:
name: Linux arm64 name: Linux arm64 glibc
path: ${{github.workspace}}/src/dotnet/runtimes/linux-arm64/native path: ${{github.workspace}}/src/dotnet/runtimes/linux-arm64/native
- name: Download Linux x64 Artifacts - name: Download Linux x64 glibc Artifacts
uses: actions/download-artifact@v6 uses: actions/download-artifact@v6
with: with:
name: Linux x64 name: Linux x64 glibc
path: ${{github.workspace}}/src/dotnet/runtimes/linux-x64/native path: ${{github.workspace}}/src/dotnet/runtimes/linux-x64/native
- name: Download macOS arm64 Artifacts - name: Download macOS arm64 Artifacts
uses: actions/download-artifact@v6 uses: actions/download-artifact@v6
@ -153,17 +137,17 @@ jobs:
- name: Download Windows arm64 Artifacts - name: Download Windows arm64 Artifacts
uses: actions/download-artifact@v6 uses: actions/download-artifact@v6
with: with:
name: Windows MSVC arm64 name: Windows arm64
path: ${{github.workspace}}/src/dotnet/runtimes/win-arm64/native path: ${{github.workspace}}/src/dotnet/runtimes/win-arm64/native
- name: Download Windows x64 Artifacts - name: Download Windows x64 Artifacts
uses: actions/download-artifact@v6 uses: actions/download-artifact@v6
with: with:
name: Windows MSVC x64 name: Windows x64
path: ${{github.workspace}}/src/dotnet/runtimes/win-x64/native path: ${{github.workspace}}/src/dotnet/runtimes/win-x64/native
- name: Download Windows x86 Artifacts - name: Download Windows x86 Artifacts
uses: actions/download-artifact@v6 uses: actions/download-artifact@v6
with: with:
name: Windows MSVC x86 name: Windows x86
path: ${{github.workspace}}/src/dotnet/runtimes/win-x86/native path: ${{github.workspace}}/src/dotnet/runtimes/win-x86/native
- name: Setup MSBuild - name: Setup MSBuild
uses: microsoft/setup-msbuild@v2 uses: microsoft/setup-msbuild@v2

View file

@ -18,7 +18,7 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.16...3.28) cmake_policy(VERSION 3.16...3.28)
project(ragephoto VERSION 0.7.1 LANGUAGES C CXX) project(ragephoto VERSION 0.8.2 LANGUAGES C CXX)
include(GNUInstallDirs) include(GNUInstallDirs)
# RagePhoto CMake includes # RagePhoto CMake includes

View file

@ -1,6 +1,6 @@
#[[************************************************************************** #[[**************************************************************************
* libragephoto RAGE Photo Parser * libragephoto RAGE Photo Parser
* Copyright (C) 2021-2024 Syping * Copyright (C) 2021-2025 Syping
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:
@ -16,7 +16,8 @@
* responsible for anything with use of the software, you are self responsible. * responsible for anything with use of the software, you are self responsible.
****************************************************************************]] ****************************************************************************]]
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.16...3.28)
find_package(Doxygen REQUIRED) find_package(Doxygen REQUIRED)
include(GNUInstallDirs) include(GNUInstallDirs)

View file

@ -1,6 +1,6 @@
#[[************************************************************************** #[[**************************************************************************
* libragephoto RAGE Photo Parser * libragephoto RAGE Photo Parser
* Copyright (C) 2021-2022 Syping * Copyright (C) 2021-2025 Syping
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:
@ -16,7 +16,8 @@
* responsible for anything with use of the software, you are self responsible. * responsible for anything with use of the software, you are self responsible.
****************************************************************************]] ****************************************************************************]]
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.16...3.28)
project(ragephoto-gtkviewer LANGUAGES CXX) project(ragephoto-gtkviewer LANGUAGES CXX)
include(GNUInstallDirs) include(GNUInstallDirs)
@ -35,7 +36,7 @@ if (TARGET ragephoto)
set(RAGEPHOTO_LIBRARIES ragephoto) set(RAGEPHOTO_LIBRARIES ragephoto)
set(RAGEPHOTO_LIBRARY_DIRS ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}) set(RAGEPHOTO_LIBRARY_DIRS ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
else() else()
pkg_check_modules(RAGEPHOTO REQUIRED ragephoto>=0.6) pkg_check_modules(RAGEPHOTO REQUIRED ragephoto>=0.8)
endif() endif()
add_executable(ragephoto-gtkviewer WIN32 ${GTKVIEWER_SOURCES}) add_executable(ragephoto-gtkviewer WIN32 ${GTKVIEWER_SOURCES})
@ -44,8 +45,6 @@ set_target_properties(ragephoto-gtkviewer PROPERTIES
) )
target_compile_options(ragephoto-gtkviewer PRIVATE ${GTKMM_CFLAGS} ${RAGEPHOTO_CFLAGS}) target_compile_options(ragephoto-gtkviewer PRIVATE ${GTKMM_CFLAGS} ${RAGEPHOTO_CFLAGS})
target_link_libraries(ragephoto-gtkviewer PRIVATE ${GTKMM_LIBRARIES} ${RAGEPHOTO_LIBRARIES}) target_link_libraries(ragephoto-gtkviewer PRIVATE ${GTKMM_LIBRARIES} ${RAGEPHOTO_LIBRARIES})
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.13.0") target_link_directories(ragephoto-gtkviewer PRIVATE ${GTKMM_LIBRARY_DIRS} ${RAGEPHOTO_LIBRARY_DIRS})
target_link_directories(ragephoto-gtkviewer PRIVATE ${GTKMM_LIBRARY_DIRS} ${RAGEPHOTO_LIBRARY_DIRS})
endif()
target_include_directories(ragephoto-gtkviewer PRIVATE ${GTKMM_INCLUDE_DIRS} ${RAGEPHOTO_INCLUDE_DIRS}) target_include_directories(ragephoto-gtkviewer PRIVATE ${GTKMM_INCLUDE_DIRS} ${RAGEPHOTO_INCLUDE_DIRS})
install(TARGETS ragephoto-gtkviewer DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS ragephoto-gtkviewer DESTINATION ${CMAKE_INSTALL_BINDIR})

View file

@ -1,6 +1,6 @@
#[[************************************************************************** #[[**************************************************************************
* libragephoto RAGE Photo Parser * libragephoto RAGE Photo Parser
* Copyright (C) 2021-2022 Syping * Copyright (C) 2021-2025 Syping
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:
@ -16,7 +16,8 @@
* responsible for anything with use of the software, you are self responsible. * responsible for anything with use of the software, you are self responsible.
****************************************************************************]] ****************************************************************************]]
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.16)
cmake_policy(VERSION 3.16...3.28)
project(ragephoto-qtviewer LANGUAGES CXX) project(ragephoto-qtviewer LANGUAGES CXX)
include(GNUInstallDirs) include(GNUInstallDirs)
@ -41,7 +42,7 @@ if (TARGET ragephoto)
set(RAGEPHOTO_LIBRARY_DIRS ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}) set(RAGEPHOTO_LIBRARY_DIRS ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
else() else()
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(RAGEPHOTO REQUIRED ragephoto>=0.6) pkg_check_modules(RAGEPHOTO REQUIRED ragephoto>=0.8)
endif() endif()
add_executable(ragephoto-qtviewer WIN32 ${QTVIEWER_SOURCES}) add_executable(ragephoto-qtviewer WIN32 ${QTVIEWER_SOURCES})
@ -50,8 +51,6 @@ set_target_properties(ragephoto-qtviewer PROPERTIES
) )
target_compile_options(ragephoto-qtviewer PRIVATE ${RAGEPHOTO_CFLAGS}) target_compile_options(ragephoto-qtviewer PRIVATE ${RAGEPHOTO_CFLAGS})
target_link_libraries(ragephoto-qtviewer PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${RAGEPHOTO_LIBRARIES}) target_link_libraries(ragephoto-qtviewer PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${RAGEPHOTO_LIBRARIES})
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.13.0") target_link_directories(ragephoto-qtviewer PRIVATE ${RAGEPHOTO_LIBRARY_DIRS})
target_link_directories(ragephoto-qtviewer PRIVATE ${RAGEPHOTO_LIBRARY_DIRS})
endif()
target_include_directories(ragephoto-qtviewer PRIVATE ${RAGEPHOTO_INCLUDE_DIRS}) target_include_directories(ragephoto-qtviewer PRIVATE ${RAGEPHOTO_INCLUDE_DIRS})
install(TARGETS ragephoto-qtviewer DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS ragephoto-qtviewer DESTINATION ${CMAKE_INSTALL_BINDIR})

View file

@ -972,10 +972,14 @@ bool ragephotodata_savef(RagePhotoData *rp_data, RagePhotoFormatParser *rp_parse
#endif #endif
} }
else if (photoFormat == RAGEPHOTO_FORMAT_JPEG) { else if (photoFormat == RAGEPHOTO_FORMAT_JPEG) {
const size_t length = ragephotodata_getsavesizef(rp_data, NULL, photoFormat); if (rp_data->jpeg) {
size_t pos = 0; size_t pos = 0;
writeBuffer(rp_data->jpeg, data, &pos, rp_data->jpegSize, rp_data->jpegSize);
writeBuffer(rp_data->jpeg, data, &pos, length, rp_data->jpegSize); }
else {
rp_data->error = RAGEPHOTO_ERROR_PHOTOREADERROR; // 17
return false;
}
rp_data->error = RAGEPHOTO_ERROR_NOERROR; // 255 rp_data->error = RAGEPHOTO_ERROR_NOERROR; // 255
return true; return true;
@ -1235,6 +1239,7 @@ bool ragephoto_setphotojpeg(ragephoto_t instance, const char *data, uint32_t siz
else if (instance->data->jpeg) { else if (instance->data->jpeg) {
free(instance->data->jpeg); free(instance->data->jpeg);
instance->data->jpeg = NULL; instance->data->jpeg = NULL;
instance->data->jpegSize = 0;
} }
if (bufferSize != 0) { if (bufferSize != 0) {

View file

@ -66,13 +66,16 @@ const char* nullchar = "";
/* BEGIN OF STATIC LIBRARY FUNCTIONS */ /* BEGIN OF STATIC LIBRARY FUNCTIONS */
#if defined(_WIN32) && ((RAGEPHOTO_CXX_STD < 17) || (__cplusplus < 201703L)) #if defined(_WIN32) && ((RAGEPHOTO_CXX_STD < 17) || (__cplusplus < 201703L))
inline std::unique_ptr<wchar_t> convertPath(const char *path) inline std::wstring convertPath(const char *path)
{ {
int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, path, -1, nullptr, 0); int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, path, -1, nullptr, 0);
if (wideCharSize <= 0) if (wideCharSize <= 0)
return std::unique_ptr<wchar_t>(new wchar_t[1]()); return {};
std::unique_ptr<wchar_t> wideCharPath(new wchar_t[wideCharSize]); std::wstring wideCharPath;
MultiByteToWideChar(CP_UTF8, 0, path, -1, wideCharPath.get(), wideCharSize); wideCharPath.resize(wideCharSize);
if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, &wideCharPath[0], wideCharSize))
return {};
wideCharPath.resize(wideCharSize - 1);
return wideCharPath; return wideCharPath;
} }
#endif #endif
@ -292,7 +295,7 @@ bool RagePhoto::load(const char *data, size_t length, RagePhotoData *rp_data, Ra
rp_data->error = Error::HeaderMallocError; // 4 rp_data->error = Error::HeaderMallocError; // 4
return false; return false;
} }
memcpy(rp_data->header, photoHeader_string.c_str(), photoHeader_size); memcpy(rp_data->header, photoHeader_string.data(), photoHeader_size);
#elif defined UNICODE_ICONV #elif defined UNICODE_ICONV
iconv_t iconv_in = iconv_open("UTF-8", "UTF-16LE"); iconv_t iconv_in = iconv_open("UTF-8", "UTF-16LE");
if (iconv_in == (iconv_t)-1) { if (iconv_in == (iconv_t)-1) {
@ -610,7 +613,7 @@ bool RagePhoto::loadFile(const char *filename)
#if defined(_WIN32) && (RAGEPHOTO_CXX_STD >= 17) && (__cplusplus >= 201703L) #if defined(_WIN32) && (RAGEPHOTO_CXX_STD >= 17) && (__cplusplus >= 201703L)
std::ifstream ifs(std::filesystem::u8path(filename), std::ios::in | std::ios::binary); std::ifstream ifs(std::filesystem::u8path(filename), std::ios::in | std::ios::binary);
#elif defined(_WIN32) #elif defined(_WIN32)
std::ifstream ifs(convertPath(filename).get(), std::ios::in | std::ios::binary); std::ifstream ifs(convertPath(filename).data(), std::ios::in | std::ios::binary);
#else #else
std::ifstream ifs(filename, std::ios::in | std::ios::binary); std::ifstream ifs(filename, std::ios::in | std::ios::binary);
#endif #endif
@ -943,10 +946,14 @@ bool RagePhoto::save(char *data, uint32_t photoFormat, RagePhotoData *rp_data, R
#endif #endif
} }
else if (photoFormat == PhotoFormat::JPEG) { else if (photoFormat == PhotoFormat::JPEG) {
const size_t length = saveSize(photoFormat, rp_data, nullptr); if (rp_data->jpeg) {
size_t pos = 0; size_t pos = 0;
writeBuffer(rp_data->jpeg, data, &pos, rp_data->jpegSize, rp_data->jpegSize);
writeBuffer(rp_data->jpeg, data, &pos, length, rp_data->jpegSize); }
else {
rp_data->error = Error::PhotoReadError; // 17
return false;
}
rp_data->error = Error::NoError; // 255 rp_data->error = Error::NoError; // 255
return true; return true;
@ -1008,7 +1015,7 @@ bool RagePhoto::saveFile(const char *filename, uint32_t photoFormat)
#if defined(_WIN32) && (RAGEPHOTO_CXX_STD >= 17) && (__cplusplus >= 201703L) #if defined(_WIN32) && (RAGEPHOTO_CXX_STD >= 17) && (__cplusplus >= 201703L)
std::ofstream ofs(std::filesystem::u8path(filename), std::ios::out | std::ios::binary | std::ios::trunc); std::ofstream ofs(std::filesystem::u8path(filename), std::ios::out | std::ios::binary | std::ios::trunc);
#elif defined(_WIN32) #elif defined(_WIN32)
std::ofstream ofs(convertPath(filename).get(), std::ios::out | std::ios::binary | std::ios::trunc); std::ofstream ofs(convertPath(filename).data(), std::ios::out | std::ios::binary | std::ios::trunc);
#else #else
std::ofstream ofs(filename, std::ios::out | std::ios::binary | std::ios::trunc); std::ofstream ofs(filename, std::ios::out | std::ios::binary | std::ios::trunc);
#endif #endif
@ -1231,6 +1238,7 @@ bool RagePhoto::setJpeg(const char *data, uint32_t size, uint32_t bufferSize)
else if (m_data->jpeg) { else if (m_data->jpeg) {
free(m_data->jpeg); free(m_data->jpeg);
m_data->jpeg = nullptr; m_data->jpeg = nullptr;
m_data->jpegSize = 0;
} }
if (bufferSize != 0) { if (bufferSize != 0) {

View file

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
* libragephoto RAGE Photo Parser * libragephoto RAGE Photo Parser
* Copyright (C) 2021-2024 Syping * Copyright (C) 2021-2025 Syping
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:

View file

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
* libragephoto RAGE Photo Parser * libragephoto RAGE Photo Parser
* Copyright (C) 2023-2024 Syping * Copyright (C) 2023-2025 Syping
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:

View file

@ -1,6 +1,6 @@
/***************************************************************************** /*****************************************************************************
* libragephoto RAGE Photo Parser * libragephoto RAGE Photo Parser
* Copyright (C) 2023 Syping * Copyright (C) 2023-2025 Syping
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:

21
src/dotnet/README.md Normal file
View file

@ -0,0 +1,21 @@
## RagePhoto.Core
Open Source RAGE Photo Parser for GTA V and RDR 2
- Read/Write RAGE Photos error free and correct
- Support for metadata stored in RAGE Photos
- Simple .NET API
- Based on libragephoto
#### How to Use RagePhoto.Core
```c#
using RagePhoto;
/* Get Image from Photo */
static Image GetImageFromPhoto(String inputFile) {
using Photo photo = new();
photo.LoadFile(inputFile);
using MemoryStream jpegStream = new(photo.Jpeg);
return Image.FromStream(jpegStream);
}
```

View file

@ -4,13 +4,15 @@
<TargetFrameworks>netstandard2.1;net47</TargetFrameworks> <TargetFrameworks>netstandard2.1;net47</TargetFrameworks>
<AssemblyName>RagePhoto.Core</AssemblyName> <AssemblyName>RagePhoto.Core</AssemblyName>
<RootNamespace>RagePhoto</RootNamespace> <RootNamespace>RagePhoto</RootNamespace>
<Version>0.7.1</Version> <Version>0.8.2</Version>
<AssemblyVersion>0.7.1</AssemblyVersion> <AssemblyVersion>0.8.2</AssemblyVersion>
<FileVersion>0.7.1</FileVersion> <FileVersion>0.8.2</FileVersion>
<Authors>Syping</Authors> <Authors>Syping</Authors>
<Copyright>Copyright © 2025 Syping</Copyright> <Copyright>Copyright © 2025 Syping</Copyright>
<Description>Open Source RAGE Photo Parser for GTA V and RDR 2</Description> <Description>Open Source RAGE Photo Parser for GTA V and RDR 2</Description>
<PackageLicenseExpression>BSD-2-Clause</PackageLicenseExpression> <PackageLicenseExpression>BSD-2-Clause</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageTags>libragephoto;gta5;gtav;snapmatic;rdr2;photo</PackageTags>
<RepositoryUrl>https://github.com/Syping/libragephoto</RepositoryUrl> <RepositoryUrl>https://github.com/Syping/libragephoto</RepositoryUrl>
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
</PropertyGroup> </PropertyGroup>
@ -20,16 +22,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<NativeDlls Include="runtimes\**\*ragephoto.*" /> <SharedLibraries Include="runtimes\**\libragephoto.*" />
<None Include="@(NativeDlls)" Pack="true" PackagePath="runtimes\%(RecursiveDir)%(Filename)%(Extension)"> <None Include="@(SharedLibraries)" Pack="true" PackagePath="runtimes\%(RecursiveDir)%(Filename)%(Extension)" />
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="net47\RagePhoto.Core.targets" Pack="true" PackagePath="build\net47\RagePhoto.Core.targets" />
</None> <None Include="net47\RagePhoto.Core.targets" Pack="true" PackagePath="buildTransitive\net47\RagePhoto.Core.targets" />
<None Include="RagePhoto.Core.targets" Pack="true" PackagePath="build\net47\RagePhoto.Core.targets"> </ItemGroup>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> <ItemGroup>
<None Include="RagePhoto.Core.targets" Pack="true" PackagePath="buildTransitive\net47\RagePhoto.Core.targets"> <None Include="README.md" Pack="true" PackagePath="README.md" />
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -6,7 +6,7 @@ namespace RagePhoto {
public class Photo : IDisposable { public class Photo : IDisposable {
private bool _disposed; private Boolean _disposed;
private readonly IntPtr _instance; private readonly IntPtr _instance;
private const String _library = "libragephoto"; private const String _library = "libragephoto";
@ -18,10 +18,10 @@ namespace RagePhoto {
private static extern void ragephoto_close(IntPtr instance); private static extern void ragephoto_close(IntPtr instance);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_load(IntPtr instance, Byte[] data, UIntPtr size); private static extern Boolean ragephoto_load(IntPtr instance, Byte[] data, UIntPtr size);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_loadfile(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String filename); private static extern Boolean ragephoto_loadfile(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String filename);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
private static extern Int32 ragephoto_error(IntPtr instance); private static extern Int32 ragephoto_error(IntPtr instance);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
@ -48,30 +48,30 @@ namespace RagePhoto {
private static extern UIntPtr ragephoto_getsavesizef(IntPtr instance, UInt32 photoFormat); private static extern UIntPtr ragephoto_getsavesizef(IntPtr instance, UInt32 photoFormat);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_save(IntPtr instance, [Out] Byte[] data); private static extern Boolean ragephoto_save(IntPtr instance, [Out] Byte[] data);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_savef(IntPtr instance, [Out] Byte[] data, UInt32 photoFormat); private static extern Boolean ragephoto_savef(IntPtr instance, [Out] Byte[] data, UInt32 photoFormat);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_savefile(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String filename); private static extern Boolean ragephoto_savefile(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String filename);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_savefilef(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String filename, UInt32 photoFormat); private static extern Boolean ragephoto_savefilef(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String filename, UInt32 photoFormat);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
private static extern void ragephoto_setbufferdefault(IntPtr instance); private static extern void ragephoto_setbufferdefault(IntPtr instance);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
private static extern void ragephoto_setbufferoffsets(IntPtr instance); private static extern void ragephoto_setbufferoffsets(IntPtr instance);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_setphotodatac(IntPtr instance, IntPtr data); private static extern Boolean ragephoto_setphotodatac(IntPtr instance, IntPtr data);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
private static extern void ragephoto_setphotodesc(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String description, UInt32 bufferSize); private static extern void ragephoto_setphotodesc(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String description, UInt32 bufferSize);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
private static extern void ragephoto_setphotoformat(IntPtr instance, UInt32 photoFormat); private static extern void ragephoto_setphotoformat(IntPtr instance, UInt32 photoFormat);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
private static extern bool ragephoto_setphotojpeg(IntPtr instance, Byte[] jpeg, UInt32 size, UInt32 bufferSize); private static extern Boolean ragephoto_setphotojpeg(IntPtr instance, Byte[] jpeg, UInt32 size, UInt32 bufferSize);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
private static extern void ragephoto_setphotojson(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String json, UInt32 bufferSize); private static extern void ragephoto_setphotojson(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] String json, UInt32 bufferSize);
[DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(_library, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
@ -98,7 +98,7 @@ namespace RagePhoto {
if (_instance == IntPtr.Zero) if (_instance == IntPtr.Zero)
throw new RagePhotoException("Failed to initialize libragephoto"); throw new RagePhotoException("Failed to initialize libragephoto");
if (!ragephoto_setphotodatac(_instance, ragephoto_getphotodata(photo._instance))) if (!ragephoto_setphotodatac(_instance, ragephoto_getphotodata(photo._instance)))
throw new RagePhotoException(this, String.Format("Failed to copy Photo: {0}", Error), Error); throw new RagePhotoException(this, "Failed to copy Photo", Error);
} }
catch (Exception exception) { catch (Exception exception) {
throw new RagePhotoException("Failed to initialize libragephoto", exception); throw new RagePhotoException("Failed to initialize libragephoto", exception);
@ -114,7 +114,7 @@ namespace RagePhoto {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
protected virtual void Dispose(bool disposing) { protected virtual void Dispose(Boolean disposing) {
if (_disposed) if (_disposed)
return; return;
ragephoto_close(_instance); ragephoto_close(_instance);
@ -127,12 +127,12 @@ namespace RagePhoto {
public void Load(Byte[] data) { public void Load(Byte[] data) {
if (!ragephoto_load(_instance, data, (UIntPtr)data.LongLength)) if (!ragephoto_load(_instance, data, (UIntPtr)data.LongLength))
throw new RagePhotoException(this, String.Format("Failed to load Photo: {0}", Error), Error); throw new RagePhotoException(this, "Failed to load Photo", Error);
} }
public void LoadFile(String path) { public void LoadFile(String path) {
if (!ragephoto_loadfile(_instance, path)) if (!ragephoto_loadfile(_instance, path))
throw new RagePhotoException(this, String.Format("Failed to load Photo: {0}", Error), Error); throw new RagePhotoException(this, "Failed to load Photo", Error);
} }
public String Description { public String Description {
@ -159,7 +159,16 @@ namespace RagePhoto {
Marshal.Copy(ptr, buffer, 0, (Int32)size); Marshal.Copy(ptr, buffer, 0, (Int32)size);
return buffer; return buffer;
} }
set => SetJpeg(value); set {
UInt32 size = (UInt32)value.Length;
UInt32 bufferSize = Format switch {
PhotoFormat.GTA5 => size > (UInt32)DefaultSize.DEFAULT_GTA5_PHOTOBUFFER ? size : (UInt32)DefaultSize.DEFAULT_GTA5_PHOTOBUFFER,
PhotoFormat.RDR2 => size > (UInt32)DefaultSize.DEFAULT_RDR2_PHOTOBUFFER ? size : (UInt32)DefaultSize.DEFAULT_RDR2_PHOTOBUFFER,
_ => size
};
if (!ragephoto_setphotojpeg(_instance, value, size, bufferSize))
throw new RagePhotoException(this, "Failed to set Jpeg", Error);
}
} }
public UInt32 JpegSize { public UInt32 JpegSize {
@ -199,25 +208,25 @@ namespace RagePhoto {
public Byte[] Save() { public Byte[] Save() {
Byte[] photo = new Byte[Environment.Is64BitProcess ? (UInt64)GetSaveSize() : (UInt32)GetSaveSize()]; Byte[] photo = new Byte[Environment.Is64BitProcess ? (UInt64)GetSaveSize() : (UInt32)GetSaveSize()];
if (!ragephoto_save(_instance, photo)) if (!ragephoto_save(_instance, photo))
throw new RagePhotoException(this, string.Format("Failed to save Photo: {0}", Error), Error); throw new RagePhotoException(this, "Failed to save Photo", Error);
return photo; return photo;
} }
public Byte[] Save(PhotoFormat photoFormat) { public Byte[] Save(PhotoFormat photoFormat) {
Byte[] photo = new Byte[Environment.Is64BitProcess ? (UInt64)GetSaveSize(photoFormat) : (UInt32)GetSaveSize(photoFormat)]; Byte[] photo = new Byte[Environment.Is64BitProcess ? (UInt64)GetSaveSize(photoFormat) : (UInt32)GetSaveSize(photoFormat)];
if (!ragephoto_savef(_instance, photo, (UInt32)photoFormat)) if (!ragephoto_savef(_instance, photo, (UInt32)photoFormat))
throw new RagePhotoException(this, string.Format("Failed to save Photo: {0}", Error), Error); throw new RagePhotoException(this, "Failed to save Photo", Error);
return photo; return photo;
} }
public void SaveFile(String path) { public void SaveFile(String path) {
if (!ragephoto_savefile(_instance, path)) if (!ragephoto_savefile(_instance, path))
throw new RagePhotoException(this, string.Format("Failed to save Photo: {0}", Error), Error); throw new RagePhotoException(this, "Failed to save Photo", Error);
} }
public void SaveFile(String path, PhotoFormat photoFormat) { public void SaveFile(String path, PhotoFormat photoFormat) {
if (!ragephoto_savefilef(_instance, path, (UInt32)photoFormat)) if (!ragephoto_savefilef(_instance, path, (UInt32)photoFormat))
throw new RagePhotoException(this, string.Format("Failed to save Photo: {0}", Error), Error); throw new RagePhotoException(this, "Failed to save Photo", Error);
} }
public void SetBufferDefault() { public void SetBufferDefault() {
@ -241,32 +250,19 @@ namespace RagePhoto {
} }
public void SetJpeg(Byte[] jpeg) { public void SetJpeg(Byte[] jpeg) {
UInt32 bufferSize;
UInt32 size = (UInt32)jpeg.Length; UInt32 size = (UInt32)jpeg.Length;
switch (Format) { UInt32 bufferSize = Format switch {
case PhotoFormat.GTA5: PhotoFormat.GTA5 => size > (UInt32)DefaultSize.DEFAULT_GTA5_PHOTOBUFFER ? size : (UInt32)DefaultSize.DEFAULT_GTA5_PHOTOBUFFER,
if (size > (UInt32)DefaultSize.DEFAULT_GTA5_PHOTOBUFFER) PhotoFormat.RDR2 => size > (UInt32)DefaultSize.DEFAULT_RDR2_PHOTOBUFFER ? size : (UInt32)DefaultSize.DEFAULT_RDR2_PHOTOBUFFER,
bufferSize = size; _ => size
else };
bufferSize = (UInt32)DefaultSize.DEFAULT_GTA5_PHOTOBUFFER;
break;
case PhotoFormat.RDR2:
if (size > (UInt32)DefaultSize.DEFAULT_RDR2_PHOTOBUFFER)
bufferSize = size;
else
bufferSize = (UInt32)DefaultSize.DEFAULT_RDR2_PHOTOBUFFER;
break;
default:
bufferSize = size;
break;
}
if (!ragephoto_setphotojpeg(_instance, jpeg, size, bufferSize)) if (!ragephoto_setphotojpeg(_instance, jpeg, size, bufferSize))
throw new RagePhotoException(this, String.Format("Failed to set Jpeg: {0}", Error), Error); throw new RagePhotoException(this, "Failed to set Jpeg", Error);
} }
public void SetJpeg(Byte[] jpeg, UInt32 bufferSize) { public void SetJpeg(Byte[] jpeg, UInt32 bufferSize) {
if (!ragephoto_setphotojpeg(_instance, jpeg, (UInt32)jpeg.Length, bufferSize)) if (!ragephoto_setphotojpeg(_instance, jpeg, (UInt32)jpeg.Length, bufferSize))
throw new RagePhotoException(this, String.Format("Failed to set Jpeg: {0}", Error), Error); throw new RagePhotoException(this, "Failed to set Jpeg", Error);
} }
public void SetJson(String json) { public void SetJson(String json) {

View file

@ -15,7 +15,7 @@ namespace RagePhoto {
_error = PhotoError.Uninitialised; _error = PhotoError.Uninitialised;
} }
public RagePhotoException(Photo photo, String message, PhotoError error) : base(message) { public RagePhotoException(Photo photo, String message, PhotoError error) : base(String.Format("{0}: {1}", message, error)) {
_error = error; _error = error;
_photo = photo; _photo = photo;
} }

View file

@ -1,21 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Condition="'$(OS)' == 'Windows_NT' AND '$(Platform)' == 'arm64'">
<!-- Windows arm64 -->
<ItemGroup Condition="'$(Platform)' == 'arm64'">
<Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-arm64\native\libragephoto.dll"> <Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-arm64\native\libragephoto.dll">
<TargetPath>libragephoto.dll</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>false</Pack>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT' AND '$(Platform)' == 'x64'">
<!-- Windows x64 -->
<ItemGroup Condition="'$(Platform)' == 'x64'">
<Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x64\native\libragephoto.dll"> <Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x64\native\libragephoto.dll">
<TargetPath>libragephoto.dll</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>false</Pack>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT' AND '$(Platform)' == 'x86'">
<!-- Windows x86 -->
<ItemGroup Condition="'$(Platform)' == 'x86'">
<Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x86\native\libragephoto.dll"> <Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x86\native\libragephoto.dll">
<TargetPath>libragephoto.dll</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>false</Pack>
</Content> </Content>
</ItemGroup> </ItemGroup>
</Project> </Project>