From a5dcc51f2e9b831d88a366727e8fcc91dac86a5d Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Fri, 3 Oct 2025 17:28:19 +0200 Subject: [PATCH 1/8] use new fp lib + more verbose when parsing fp --- desktop/main.c | 2 +- lib/fp | 2 +- lib/fuji.h | 1 + lib/fuji_usb.c | 27 +++++++++++++++++++-------- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/desktop/main.c b/desktop/main.c index f52024b..23609d0 100644 --- a/desktop/main.c +++ b/desktop/main.c @@ -148,7 +148,7 @@ int main(int argc, char **argv) { printf("Error parsing profile: '%s'\n", fp_get_error()); return rc; } - fp_dump_struct(stdout, &fp); + fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); printf("\n"); return 0; diff --git a/lib/fp b/lib/fp index 0d0ba3d..adf7bac 160000 --- a/lib/fp +++ b/lib/fp @@ -1 +1 @@ -Subproject commit 0d0ba3dba2807f3b8339914b3be4e8ba5ccb795f +Subproject commit adf7bac269d6b23bb1a017398af88ee539740a70 diff --git a/lib/fuji.h b/lib/fuji.h index 65cec4e..8b571b4 100644 --- a/lib/fuji.h +++ b/lib/fuji.h @@ -203,6 +203,7 @@ int fuji_send_object_ex(struct PtpRuntime *r, const void *data, size_t length); /// @param input_raf_path Path for RAF file /// @param profile_xml_path String data for XML profile to be parsed by fp +/// @param output_path Path for the resulting JPG file int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path); #endif diff --git a/lib/fuji_usb.c b/lib/fuji_usb.c index 8aaddc4..3ea4f5b 100644 --- a/lib/fuji_usb.c +++ b/lib/fuji_usb.c @@ -353,21 +353,32 @@ int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const cha uint8_t buffer[1024]; struct FujiProfile fp; - fp_parse_d185(profile, profile_len, &fp); - - struct FujiProfile user_fp; - rc = fp_parse_fp1(profile_xml_path, &user_fp); + rc = fp_parse_d185(profile, profile_len, &fp); if (rc == 0) { - rc = fp_apply_profile(&user_fp, &fp); - if (rc) { - ptp_error_log("Failed to merge profile\n"); + printf("RAF file FP profile:\n"); + fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); + struct FujiProfile user_fp; + rc = fp_parse_fp1(profile_xml_path, &user_fp); + + if (rc == 0) { + rc = fp_apply_profile(&user_fp, &fp); + if (rc) { + ptp_error_log("Failed to merge profile\n"); + return PTP_RUNTIME_ERR; + } + printf("Merged FP profiles:\n"); + fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); + } else { + ptp_error_log("Failed to parse '%s', check console output\n", profile_xml_path); return PTP_RUNTIME_ERR; } } else { - ptp_error_log("Failed to parse '%s', check console output\n", profile_xml_path); + ptp_error_log("Failed to parse RAF file profile\n"); return PTP_RUNTIME_ERR; } + + profile_len = fp_create_d185(&fp, buffer, sizeof(buffer)); if (profile_len < 0) { printf("Error creating d185\n"); From 2a5fc46b20f6a48022936aa9e4825542feeadb2b Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Mon, 6 Oct 2025 19:13:04 +0200 Subject: [PATCH 2/8] make the conversion exit on error, not roll through --- desktop/main.c | 5 +---- lib/fuji_usb.c | 45 +++++++++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/desktop/main.c b/desktop/main.c index 23609d0..9f5ae73 100644 --- a/desktop/main.c +++ b/desktop/main.c @@ -148,10 +148,7 @@ int main(int argc, char **argv) { printf("Error parsing profile: '%s'\n", fp_get_error()); return rc; } - fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); - printf("\n"); - - return 0; + return fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); } if (!strcmp(argv[i], "--raw")) { if (i + 3 >= argc) { diff --git a/lib/fuji_usb.c b/lib/fuji_usb.c index 3ea4f5b..d7eb7a8 100644 --- a/lib/fuji_usb.c +++ b/lib/fuji_usb.c @@ -349,36 +349,45 @@ int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const cha memcpy(profile, ptp_get_payload(r), profile_len); ptp_mutex_unlock(r); - ptp_verbose_log("Got %d bytes of profile\n", profile_len); + ptp_verbose_log("Got %d bytes of profile embedded in the RAW file\n", profile_len); uint8_t buffer[1024]; struct FujiProfile fp; rc = fp_parse_d185(profile, profile_len, &fp); - if (rc == 0) { - printf("RAF file FP profile:\n"); - fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); - struct FujiProfile user_fp; - rc = fp_parse_fp1(profile_xml_path, &user_fp); + free(profile); + if (rc == 0) { + printf("-----\nRAF file FP profile:\n"); + rc = fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); if (rc == 0) { - rc = fp_apply_profile(&user_fp, &fp); - if (rc) { - ptp_error_log("Failed to merge profile\n"); + struct FujiProfile user_fp; + rc = fp_parse_fp1(profile_xml_path, &user_fp); + + if (rc == 0) { + rc = fp_merge_profile(&user_fp, &fp); + if (rc) { + ptp_error_log("Failed to merge profile\n"); + return PTP_RUNTIME_ERR; + } + printf("-----\nMerged FP profiles:\n"); + rc = fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); + if (rc) { + ptp_error_log("Failed to print/dump merged profile\n"); + return PTP_RUNTIME_ERR; + } + } else { + ptp_error_log("Failed to parse '%s', check console output\n", profile_xml_path); return PTP_RUNTIME_ERR; } - printf("Merged FP profiles:\n"); - fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); } else { - ptp_error_log("Failed to parse '%s', check console output\n", profile_xml_path); - return PTP_RUNTIME_ERR; + ptp_error_log("Failed to parse/dump RAF file profile\n"); + return PTP_RUNTIME_ERR; } } else { ptp_error_log("Failed to parse RAF file profile\n"); return PTP_RUNTIME_ERR; } - - profile_len = fp_create_d185(&fp, buffer, sizeof(buffer)); if (profile_len < 0) { printf("Error creating d185\n"); @@ -413,11 +422,11 @@ int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const cha } fwrite(ptp_get_payload(r), 1, ptp_get_payload_length(r), f); fclose(f); - + + // file delete is required, otherwise the camera hangs-up (LED blinks green-orange) rc = ptp_delete_object(r, (int)list->data[0]); - if (rc) return rc; - free(list); + if (rc) return rc; break; } From 08170118fd188402121e4029913a57e5eefb201a Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Mon, 6 Oct 2025 19:17:53 +0200 Subject: [PATCH 3/8] make RAW conv full quality & describe other quality settings (0xD183) --- desktop/backend.c | 4 ++-- desktop/desktop.h | 5 +++-- desktop/main.c | 4 +++- desktop/ui.c | 2 +- lib/fuji.h | 12 ++++++++--- lib/fuji_usb.c | 54 +++++++++++++++++++++++++++++++++++++++++------ lib/fujiptp.h | 11 ++++++++++ 7 files changed, 76 insertions(+), 16 deletions(-) diff --git a/desktop/backend.c b/desktop/backend.c index 8ad5ded..d5472e3 100644 --- a/desktop/backend.c +++ b/desktop/backend.c @@ -221,7 +221,7 @@ int fudge_dump_usb(int devnum) { return rc; } -int fudge_process_raf(int devnum, const char *input, const char *output, const char *profile) { +int fudge_convert_raf(int devnum, const char *input, const char *output, const char *profile, const enum ConversionOutputQuality quality) { struct PtpRuntime *r = ptp_new(PTP_USB); int rc = fudge_usb_connect(r, devnum); if (rc) return rc; @@ -229,7 +229,7 @@ int fudge_process_raf(int devnum, const char *input, const char *output, const c rc = fujiusb_setup(r); if (rc) return rc; - rc = fuji_process_raf(r, input, output, profile); + rc = fuji_process_raf(r, input, output, profile, quality); ptp_close_session(r); ptp_device_close(r); diff --git a/desktop/desktop.h b/desktop/desktop.h index 7e99100..15dba91 100644 --- a/desktop/desktop.h +++ b/desktop/desktop.h @@ -3,6 +3,7 @@ #define DESKTOP_H #include #include +#include // for ConversionOutputQuality int fudge_test_all_cameras(void); @@ -27,8 +28,8 @@ int fuji_test_discovery(struct PtpRuntime *r); //int fuji_test_filesystem(struct PtpRuntime *r); //int fuji_test_setup(struct PtpRuntime *r); -/// @brief CLI function to do quick conversion -int fudge_process_raf(int devnum, const char *input, const char *output, const char *profile); +/// @brief CLI function to do a RAW file conversion into image file +int fudge_convert_raf(int devnum, const char *input, const char *output, const char *profile, const enum ConversionOutputQuality); int fudge_download_backup(int devnum, const char *output); int fudge_restore_backup(int devnum, const char *output); diff --git a/desktop/main.c b/desktop/main.c index 9f5ae73..9a08e66 100644 --- a/desktop/main.c +++ b/desktop/main.c @@ -155,7 +155,9 @@ int main(int argc, char **argv) { printf("%s --raw \n", argv[0]); return -1; } - return fudge_process_raf(devnum, argv[i + 1], argv[i + 2], argv[i + 3]); + int rc = fudge_convert_raf(devnum, argv[i + 1], argv[i + 2], argv[i + 3], CONVERSION_OUTPUT_QUALITY_FULL); + plat_dbg("Result: %d '%s'\n", rc, fp_get_error()); + return rc; } if (!strcmp(argv[i], "--test-wifi")) { int rc = fudge_test_all_cameras(); diff --git a/desktop/ui.c b/desktop/ui.c index 7157d8d..b22b03b 100644 --- a/desktop/ui.c +++ b/desktop/ui.c @@ -137,7 +137,7 @@ static void option(const char *name, struct FujiLookup *tbl, int *option, uint32 static void *thread_raw_conversion(void *arg) { struct State *state = (struct State *)arg; - int rc = fudge_process_raf(0, state->raf_path, state->output_jpg_path, state->fp_xml_path); + int rc = fudge_convert_raf(0, state->raf_path, state->output_jpg_path, state->fp_xml_path, CONVERSION_OUTPUT_QUALITY_FULL); if (rc) { app_print("Failed to convert RAW"); } diff --git a/lib/fuji.h b/lib/fuji.h index 8b571b4..7c04ab9 100644 --- a/lib/fuji.h +++ b/lib/fuji.h @@ -2,9 +2,9 @@ // Fudge implementations and app logic #ifndef FUJIAPP_FUJI_H #define FUJIAPP_FUJI_H -#include -#include "fujiptp.h" #include "app.h" +#include "fujiptp.h" +#include #define DEVICE_NAME "Fudge" @@ -41,6 +41,12 @@ enum DiscoverRet { FUJI_D_INVALID_NETWORK = 6, }; +enum ConversionOutputQuality { + CONVERSION_OUTPUT_QUALITY_THUMBNAIL, + CONVERSION_OUTPUT_QUALITY_PREVIEW, + CONVERSION_OUTPUT_QUALITY_FULL, +}; + /// @brief Holds all information about a camera that has been detected (through any means) struct DiscoverInfo { enum FujiTransport transport; @@ -204,6 +210,6 @@ int fuji_send_object_ex(struct PtpRuntime *r, const void *data, size_t length); /// @param input_raf_path Path for RAF file /// @param profile_xml_path String data for XML profile to be parsed by fp /// @param output_path Path for the resulting JPG file -int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path); +int fuji_process_raf(struct PtpRuntime* r, const char* input_raf_path, const char* output_path, const char* profile_xml_path, const enum ConversionOutputQuality quality); #endif diff --git a/lib/fuji_usb.c b/lib/fuji_usb.c index d7eb7a8..c7013a0 100644 --- a/lib/fuji_usb.c +++ b/lib/fuji_usb.c @@ -325,7 +325,7 @@ int fuji_send_raf(struct PtpRuntime *r, const char *path) { return rc; } -int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path) { +int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path, const enum ConversionOutputQuality quality) { int rc; struct FujiDeviceKnowledge *fuji = fuji_get(r); @@ -397,27 +397,47 @@ int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const cha rc = ptp_set_prop_value_data(r, PTP_DPC_FUJI_RawConvProfile, buffer, profile_len); if (rc) return rc; - free(profile); - - rc = ptp_set_prop_value16(r, PTP_DPC_FUJI_StartRawConversion, 0); + switch (quality) { + case CONVERSION_OUTPUT_QUALITY_THUMBNAIL: + case CONVERSION_OUTPUT_QUALITY_PREVIEW: + rc = ptp_set_prop_value16(r, PTP_DPC_FUJI_StartRawConversion, FUJI_START_RAW_CONVERSION_PREVIEW_CONV); + break; + case CONVERSION_OUTPUT_QUALITY_FULL: + rc = ptp_set_prop_value16(r, PTP_DPC_FUJI_StartRawConversion, FUJI_START_RAW_CONVERSION_FULL_CONV); + break; + } + if (rc) return rc; - for (int i = 0; i < 20; i++) { + for (int i = 0; i < 50; i++) { struct PtpArray *list; rc = ptp_get_object_handles(r, -1, 0, 0, &list); if (rc) return rc; if (list->length == 0) { + // wait for the converted file to be ready printf("Waiting..\n"); - usleep(1000 * 1000); + usleep(1000 * 100); free(list); + continue; } else { - rc = ptp_get_object(r, (int) list->data[0]); + // X-RAW Studio here does a call to ptp_get_object_info but it's not necessary + switch (quality) { + case CONVERSION_OUTPUT_QUALITY_THUMBNAIL: + rc = ptp_get_thumbnail(r, (int)list->data[0]); + break; + case CONVERSION_OUTPUT_QUALITY_PREVIEW: + case CONVERSION_OUTPUT_QUALITY_FULL: + rc = ptp_get_object(r, (int)list->data[0]); + break; + } + if (rc) return rc; FILE *f = fopen(output_path, "wb"); if (f == NULL) { ptp_error_log("Failed to write to output"); + free(list); return PTP_RUNTIME_ERR; } fwrite(ptp_get_payload(r), 1, ptp_get_payload_length(r), f); @@ -432,5 +452,25 @@ int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const cha } } + // Download final the profile, used for the output file + ptp_mutex_lock(r); + rc = ptp_get_prop_value(r, PTP_DPC_FUJI_RawConvProfile); + if (rc) { + ptp_mutex_unlock(r); + return rc; + } + profile_len = ptp_get_payload_length(r); + profile = malloc(profile_len); + memcpy(profile, ptp_get_payload(r), profile_len); + ptp_mutex_unlock(r); + + ptp_verbose_log("Got %d bytes of applied profile, used for conversion\n", profile_len); + + rc = fp_parse_d185(profile, profile_len, &fp); + free(profile); + printf("-----\nApplied FP profile:\n"); + rc = fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp); + if (rc) return rc; + return 0; } diff --git a/lib/fujiptp.h b/lib/fujiptp.h index 731c4ad..621db54 100644 --- a/lib/fujiptp.h +++ b/lib/fujiptp.h @@ -154,6 +154,17 @@ enum FujiUSBModes { FUJI_USB_MODE_WEBCAM = 8, }; +/// @brief Possible value for PTP_DPC_FUJI_StartRawConversion +enum FujiStartRawConversion { + // ignores ImageSize, Image Quality from RawConvProfile operation. Produces images smaller than S, Normal. + // Used by X-RAW Studio for "Preview: Conversion Speed Priority" + FUJI_START_RAW_CONVERSION_PREVIEW_CONV = 0, + // respects ImageSize, Image Quality from RawConvProfile. + // Used by X-RAW Studio for "Preview: Image Quality Priority" as well as the conversion itself + // ("Preview: Image Quality Priority" uses your ImageSize settings, but sets ImageQuality to "Normal") + FUJI_START_RAW_CONVERSION_FULL_CONV = 1, +}; + // ECs and PCs stuff from libgphoto2 ptp.h - most appear to be inaccurate or misplaced #define PTP_EC_FUJI_PreviewAvailable 0xC001 #define PTP_EC_FUJI_ObjectAdded 0xC004 From 6976525f5803cc7d2ba56bf2e0758500ce702b74 Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Mon, 6 Oct 2025 23:53:03 +0200 Subject: [PATCH 4/8] increase PTP bulk USB packet size for RAW conversion (as seen by Fuji) --- desktop/backend.c | 5 ++++- desktop/main.c | 3 ++- lib/fuji.h | 2 +- lib/fuji_usb.c | 8 ++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/desktop/backend.c b/desktop/backend.c index d5472e3..44447cf 100644 --- a/desktop/backend.c +++ b/desktop/backend.c @@ -229,7 +229,10 @@ int fudge_convert_raf(int devnum, const char *input, const char *output, const c rc = fujiusb_setup(r); if (rc) return rc; - rc = fuji_process_raf(r, input, output, profile, quality); + // observed that Fuji uses this packat size during RAW conversion + r->max_packet_size = FUJI_MAX_PARTIAL_OBJECT; + + rc = fuji_convert_raf(r, input, output, profile, quality); ptp_close_session(r); ptp_device_close(r); diff --git a/desktop/main.c b/desktop/main.c index 9a08e66..c0105f2 100644 --- a/desktop/main.c +++ b/desktop/main.c @@ -156,7 +156,8 @@ int main(int argc, char **argv) { return -1; } int rc = fudge_convert_raf(devnum, argv[i + 1], argv[i + 2], argv[i + 3], CONVERSION_OUTPUT_QUALITY_FULL); - plat_dbg("Result: %d '%s'\n", rc, fp_get_error()); + plat_dbg("Result: %d '%s' (PTP meaning: '%s')\n", rc, fp_get_error(), ptp_perror(rc)); + return rc; } if (!strcmp(argv[i], "--test-wifi")) { diff --git a/lib/fuji.h b/lib/fuji.h index 7c04ab9..aabfed5 100644 --- a/lib/fuji.h +++ b/lib/fuji.h @@ -210,6 +210,6 @@ int fuji_send_object_ex(struct PtpRuntime *r, const void *data, size_t length); /// @param input_raf_path Path for RAF file /// @param profile_xml_path String data for XML profile to be parsed by fp /// @param output_path Path for the resulting JPG file -int fuji_process_raf(struct PtpRuntime* r, const char* input_raf_path, const char* output_path, const char* profile_xml_path, const enum ConversionOutputQuality quality); +int fuji_convert_raf(struct PtpRuntime* r, const char* input_raf_path, const char* output_path, const char* profile_xml_path, const enum ConversionOutputQuality quality); #endif diff --git a/lib/fuji_usb.c b/lib/fuji_usb.c index c7013a0..96dfe8b 100644 --- a/lib/fuji_usb.c +++ b/lib/fuji_usb.c @@ -47,7 +47,7 @@ int fuji_send_object_ex(struct PtpRuntime *r, const void *data, size_t length) { struct PtpCommand cmd; cmd.code = PTP_OC_FUJI_SendObject2; cmd.param_length = 0; - return ptp_send_data(r, &cmd, data, (int)length); + return ptp_send_data(r, &cmd, data, length); } int fujiusb_dump_info(struct PtpRuntime *r) { @@ -315,17 +315,13 @@ int fuji_send_raf(struct PtpRuntime *r, const char *path) { return rc; } - r->max_packet_size = 261632; - rc = fuji_send_object_ex(r, buffer, file_size); - r->max_packet_size = 512; - free(buffer); return rc; } -int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path, const enum ConversionOutputQuality quality) { +int fuji_convert_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path, const enum ConversionOutputQuality quality) { int rc; struct FujiDeviceKnowledge *fuji = fuji_get(r); From 24872b7149ba09681c643be07c55fb9af962254c Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Thu, 23 Oct 2025 15:43:25 +0200 Subject: [PATCH 5/8] extract upload of RAW into separate function, set the PTP out buffer for the whole transaction --- desktop/backend.c | 23 ++++++++++++++++-- desktop/desktop.h | 3 +++ lib/fuji.h | 9 ++++++- lib/fuji_usb.c | 60 ++++++++++++++++++++++++++++++----------------- 4 files changed, 71 insertions(+), 24 deletions(-) diff --git a/desktop/backend.c b/desktop/backend.c index 44447cf..bc7b19d 100644 --- a/desktop/backend.c +++ b/desktop/backend.c @@ -221,6 +221,27 @@ int fudge_dump_usb(int devnum) { return rc; } +int fudge_get_profile(int devnum, const char *input, struct FujiProfile* fp) +{ + struct PtpRuntime* r = ptp_new(PTP_USB); + int rc = fudge_usb_connect(r, devnum); + if (rc) return rc; + + rc = fujiusb_setup(r); + if (rc) return rc; + + int profile_len; + void* profile; + + rc = fuji_upload_raf_get_profile(r, input, &profile, &profile_len); + if (rc) return rc; + + rc = fp_parse_d185(profile, profile_len, fp); + free(profile); + + return rc; +} + int fudge_convert_raf(int devnum, const char *input, const char *output, const char *profile, const enum ConversionOutputQuality quality) { struct PtpRuntime *r = ptp_new(PTP_USB); int rc = fudge_usb_connect(r, devnum); @@ -229,8 +250,6 @@ int fudge_convert_raf(int devnum, const char *input, const char *output, const c rc = fujiusb_setup(r); if (rc) return rc; - // observed that Fuji uses this packat size during RAW conversion - r->max_packet_size = FUJI_MAX_PARTIAL_OBJECT; rc = fuji_convert_raf(r, input, output, profile, quality); diff --git a/desktop/desktop.h b/desktop/desktop.h index 15dba91..df4aa58 100644 --- a/desktop/desktop.h +++ b/desktop/desktop.h @@ -28,6 +28,9 @@ int fuji_test_discovery(struct PtpRuntime *r); //int fuji_test_filesystem(struct PtpRuntime *r); //int fuji_test_setup(struct PtpRuntime *r); +/// @brief +int fudge_get_profile(int devnum, const char *input, struct FujiProfile* fp); + /// @brief CLI function to do a RAW file conversion into image file int fudge_convert_raf(int devnum, const char *input, const char *output, const char *profile, const enum ConversionOutputQuality); diff --git a/lib/fuji.h b/lib/fuji.h index aabfed5..aa71174 100644 --- a/lib/fuji.h +++ b/lib/fuji.h @@ -207,7 +207,14 @@ int fuji_send_object_info_ex(struct PtpRuntime *r, int storage_id, int handle, s /// @brief Function for 0x900d int fuji_send_object_ex(struct PtpRuntime *r, const void *data, size_t length); -/// @param input_raf_path Path for RAF file +/// @brief Upload the RAW file to the camera and get it's conversion profile +/// @param input_raf_path Path for RAW file +/// @param [out] profile in d185 format +/// @param [out] profile_len length of the d185 profile +int fuji_upload_raf_get_profile(struct PtpRuntime* r, const char* input_raf_path, void** profile, int * const profile_len); + +/// @brief Do a conversion of a RAW file according to the profile file using connected camera +/// @param input_raf_path Path for RAW file /// @param profile_xml_path String data for XML profile to be parsed by fp /// @param output_path Path for the resulting JPG file int fuji_convert_raf(struct PtpRuntime* r, const char* input_raf_path, const char* output_path, const char* profile_xml_path, const enum ConversionOutputQuality quality); diff --git a/lib/fuji_usb.c b/lib/fuji_usb.c index 96dfe8b..50bf653 100644 --- a/lib/fuji_usb.c +++ b/lib/fuji_usb.c @@ -321,31 +321,49 @@ int fuji_send_raf(struct PtpRuntime *r, const char *path) { return rc; } +int fuji_upload_raf_get_profile(struct PtpRuntime* r, const char* input_raf_path, void** profile, int * const profile_len) +{ + // observed that Fuji uses this packet size during RAW conversion + r->max_packet_size = FUJI_MAX_PARTIAL_OBJECT; + + int rc; + struct FujiDeviceKnowledge *fuji = fuji_get(r); + if (fuji->transport != FUJI_FEATURE_RAW_CONV) { + ptp_error_log("Not in raw transfer mode\n"); + return PTP_RUNTIME_ERR; + } + + rc = fuji_send_raf(r, input_raf_path); + if (rc) + return rc; + + // Download the profile + ptp_mutex_lock(r); + rc = ptp_get_prop_value(r, PTP_DPC_FUJI_RawConvProfile); + if (rc) { + ptp_mutex_unlock(r); + return rc; + } + *profile_len = ptp_get_payload_length(r); + *profile = malloc(*profile_len); + memcpy(*profile, ptp_get_payload(r), *profile_len); + ptp_mutex_unlock(r); + + ptp_verbose_log("Got %d bytes of profile embedded in the RAW file\n", profile_len); + + return rc; +} + int fuji_convert_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path, const enum ConversionOutputQuality quality) { int rc; - struct FujiDeviceKnowledge *fuji = fuji_get(r); - if (fuji->transport != FUJI_FEATURE_RAW_CONV) { - ptp_error_log("Not in raw transfer mode\n"); - return PTP_RUNTIME_ERR; - } - - rc = fuji_send_raf(r, input_raf_path); - if (rc) return rc; - - // Download the profile - ptp_mutex_lock(r); - rc = ptp_get_prop_value(r, PTP_DPC_FUJI_RawConvProfile); + int profile_len; + void *profile; + + rc = fuji_upload_raf_get_profile(r, input_raf_path, &profile, &profile_len); if (rc) { - ptp_mutex_unlock(r); - return rc; - } - int profile_len = ptp_get_payload_length(r); - void *profile = malloc(profile_len); - memcpy(profile, ptp_get_payload(r), profile_len); - ptp_mutex_unlock(r); - ptp_verbose_log("Got %d bytes of profile embedded in the RAW file\n", profile_len); + } uint8_t buffer[1024]; struct FujiProfile fp; @@ -384,7 +402,7 @@ int fuji_convert_raf(struct PtpRuntime *r, const char *input_raf_path, const cha return PTP_RUNTIME_ERR; } - profile_len = fp_create_d185(&fp, buffer, sizeof(buffer)); + profile_len = fp_create_d185(&fp, (struct D185_t* )buffer, sizeof(buffer)); if (profile_len < 0) { printf("Error creating d185\n"); return -1; From 5ecaa65bfc4aecb9505b529c3729fa3765ecbbbb Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Mon, 17 Nov 2025 20:02:15 +0100 Subject: [PATCH 6/8] update subrepos --- lib/fp | 2 +- lib/libpict | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fp b/lib/fp index adf7bac..fc9599a 160000 --- a/lib/fp +++ b/lib/fp @@ -1 +1 @@ -Subproject commit adf7bac269d6b23bb1a017398af88ee539740a70 +Subproject commit fc9599ab826aa7af9458c0021c1a321e6713f03c diff --git a/lib/libpict b/lib/libpict index 8a10673..06377df 160000 --- a/lib/libpict +++ b/lib/libpict @@ -1 +1 @@ -Subproject commit 8a106733f49e286c4ef4d251bbc0ad41a9902569 +Subproject commit 06377df80e68936f90b6fe649bd85e44692ec6c4 From 4a7a8502e9370070a0f076772584c2a4c97865f9 Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Mon, 17 Nov 2025 20:07:39 +0100 Subject: [PATCH 7/8] update readme (roadmap/dreams) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3f72067..bf5d4e9 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ This app isn't finished yet, so don't set high expectations. Fuji's implementati - Liveview & Remote capture - Implement Bluetooth pairing +- get D185 prop PTP comms from more cameras to reverse-engineer the protocol better +- implement generating FP2 profile file from the RAW file, so you can start with edits +- Replacement of X-RAW Studio for desktop + ## Compiling ``` git clone https://github.com/petabyt/fudge.git --depth 1 --recurse-submodules From b9adbe0c7ff4f37068789603a16033aba068f41f Mon Sep 17 00:00:00 2001 From: Mateusz Grzywacz Date: Mon, 17 Nov 2025 23:18:10 +0100 Subject: [PATCH 8/8] revert not impl function from the future --- desktop/backend.c | 21 --------------------- desktop/desktop.h | 3 --- 2 files changed, 24 deletions(-) diff --git a/desktop/backend.c b/desktop/backend.c index bc7b19d..1250a4d 100644 --- a/desktop/backend.c +++ b/desktop/backend.c @@ -221,27 +221,6 @@ int fudge_dump_usb(int devnum) { return rc; } -int fudge_get_profile(int devnum, const char *input, struct FujiProfile* fp) -{ - struct PtpRuntime* r = ptp_new(PTP_USB); - int rc = fudge_usb_connect(r, devnum); - if (rc) return rc; - - rc = fujiusb_setup(r); - if (rc) return rc; - - int profile_len; - void* profile; - - rc = fuji_upload_raf_get_profile(r, input, &profile, &profile_len); - if (rc) return rc; - - rc = fp_parse_d185(profile, profile_len, fp); - free(profile); - - return rc; -} - int fudge_convert_raf(int devnum, const char *input, const char *output, const char *profile, const enum ConversionOutputQuality quality) { struct PtpRuntime *r = ptp_new(PTP_USB); int rc = fudge_usb_connect(r, devnum); diff --git a/desktop/desktop.h b/desktop/desktop.h index df4aa58..15dba91 100644 --- a/desktop/desktop.h +++ b/desktop/desktop.h @@ -28,9 +28,6 @@ int fuji_test_discovery(struct PtpRuntime *r); //int fuji_test_filesystem(struct PtpRuntime *r); //int fuji_test_setup(struct PtpRuntime *r); -/// @brief -int fudge_get_profile(int devnum, const char *input, struct FujiProfile* fp); - /// @brief CLI function to do a RAW file conversion into image file int fudge_convert_raf(int devnum, const char *input, const char *output, const char *profile, const enum ConversionOutputQuality);