Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions desktop/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,16 @@ 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;

rc = fujiusb_setup(r);
if (rc) return rc;

rc = fuji_process_raf(r, input, output, profile);

rc = fuji_convert_raf(r, input, output, profile, quality);

ptp_close_session(r);
ptp_device_close(r);
Expand Down
5 changes: 3 additions & 2 deletions desktop/desktop.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define DESKTOP_H
#include <stdio.h>
#include <libpict.h>
#include <fuji.h> // for ConversionOutputQuality

int fudge_test_all_cameras(void);

Expand All @@ -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);
Expand Down
10 changes: 5 additions & 5 deletions desktop/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,17 @@ int main(int argc, char **argv) {
printf("Error parsing profile: '%s'\n", fp_get_error());
return rc;
}
fp_dump_struct(stdout, &fp);
printf("\n");

return 0;
return fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp);
}
if (!strcmp(argv[i], "--raw")) {
if (i + 3 >= argc) {
printf("%s --raw <input> <output> <profile>\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' (PTP meaning: '%s')\n", rc, fp_get_error(), ptp_perror(rc));

return rc;
}
if (!strcmp(argv[i], "--test-wifi")) {
int rc = fudge_test_all_cameras();
Expand Down
2 changes: 1 addition & 1 deletion desktop/ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
22 changes: 18 additions & 4 deletions lib/fuji.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// Fudge implementations and app logic
#ifndef FUJIAPP_FUJI_H
#define FUJIAPP_FUJI_H
#include <libpict.h>
#include "fujiptp.h"
#include "app.h"
#include "fujiptp.h"
#include <libpict.h>

#define DEVICE_NAME "Fudge"

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -201,8 +207,16 @@ 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
int fuji_process_raf(struct PtpRuntime *r, const char *input_raf_path, const char *output_path, const char *profile_xml_path);
/// @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);

#endif
162 changes: 118 additions & 44 deletions lib/fuji_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -315,60 +315,94 @@ 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) {
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;
}
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;
}

rc = fuji_send_raf(r, input_raf_path);
if (rc) 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;

// 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\n", profile_len);
}

uint8_t buffer[1024];
struct FujiProfile fp;
fp_parse_d185(profile, profile_len, &fp);
rc = fp_parse_d185(profile, profile_len, &fp);
free(profile);

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("-----\nRAF file FP profile:\n");
rc = fp_dump_struct(stdout, FP_FORMAT_HUMAN_READABLE, &fp);
if (rc == 0) {
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;
}
} else {
ptp_error_log("Failed to parse/dump RAF file profile\n");
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));
profile_len = fp_create_d185(&fp, (struct D185_t* )buffer, sizeof(buffer));
if (profile_len < 0) {
printf("Error creating d185\n");
return -1;
Expand All @@ -377,40 +411,80 @@ 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);
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;
}
}

// 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;
}
11 changes: 11 additions & 0 deletions lib/fujiptp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/libpict
Loading