├── INSTALL ├── icons ├── FCA.png ├── FCB.png ├── FCC.png ├── FCD.png ├── FCS.png ├── F__.xcf ├── TBA.png ├── TBB.png ├── TBC.png ├── TBD.png ├── TBS.png ├── TBT.png └── TB_.xcf ├── 41-odvr.rules ├── sandec ├── Makefile ├── README.md └── sandec.c ├── convert_raw_files.sh ├── odvr_icons.c ├── util ├── splogfilter └── decodesetup ├── odvr_date.h ├── Makefile ├── odvr_date.c ├── odvr_gui.h ├── odvr_cfg.h ├── olympusdvr.h ├── README.md ├── odvr_cfg.c ├── cli.c ├── odvr_gui.c ├── olympusdvr.c ├── COPYING └── gui.c /INSTALL: -------------------------------------------------------------------------------- 1 | see README 2 | -------------------------------------------------------------------------------- /icons/FCA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/FCA.png -------------------------------------------------------------------------------- /icons/FCB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/FCB.png -------------------------------------------------------------------------------- /icons/FCC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/FCC.png -------------------------------------------------------------------------------- /icons/FCD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/FCD.png -------------------------------------------------------------------------------- /icons/FCS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/FCS.png -------------------------------------------------------------------------------- /icons/F__.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/F__.xcf -------------------------------------------------------------------------------- /icons/TBA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/TBA.png -------------------------------------------------------------------------------- /icons/TBB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/TBB.png -------------------------------------------------------------------------------- /icons/TBC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/TBC.png -------------------------------------------------------------------------------- /icons/TBD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/TBD.png -------------------------------------------------------------------------------- /icons/TBS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/TBS.png -------------------------------------------------------------------------------- /icons/TBT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/TBT.png -------------------------------------------------------------------------------- /icons/TB_.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilly/odvr/HEAD/icons/TB_.xcf -------------------------------------------------------------------------------- /41-odvr.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="usb", ATTR{idVendor}=="07b4", ATTR{idProduct}=="020d", ACTION=="add", GROUP="audio", MODE="0664" 2 | -------------------------------------------------------------------------------- /sandec/Makefile: -------------------------------------------------------------------------------- 1 | all: nasced 2 | 3 | sandec: 4 | winegcc -mconsole -mno-cygwin -o sandec sandec.c 5 | 6 | nasced: nasced.c 7 | gcc nasced.c -lm -o sandec 8 | 9 | clean: 10 | -rm -f sandec sandec.exe.so *~ \#*\# 11 | 12 | -------------------------------------------------------------------------------- /convert_raw_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Convert all files with .raw extension located at the root of the current folder into .wav files. 3 | # Usage: ./convert_raw_files.sh 4 | 5 | total=$(find . -maxdepth 1 -name "*.raw" | wc -l) 6 | if [ ! $total -eq 0 ]; then 7 | count=0 8 | for rawfile in *.raw 9 | do 10 | echo "Processing file $((count=count+1)) of $total: $rawfile" 11 | ./sandec/sandec "$rawfile" 12 | done 13 | echo "All conversions are complete." 14 | else 15 | echo "No .raw files found" 16 | fi 17 | -------------------------------------------------------------------------------- /sandec/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Converts Olympus Digital Voice Recorder files to WAV files. 5 | 6 | 7 | Building 8 | ======== 9 | 10 | There are two implementations: 11 | - `sandec.c` which requires Wine and a DLL (`san_dec.dll`) to be found in the official Olympus Windows installer (this was the historically first implementation used with odvr and it remains here as an archive) 12 | - `nasced.c` which is a pure Linux implementation (Makefile is configured to use that one by default) 13 | 14 | To build: 15 | - If you already built odvr, probably sandec is already built using `nasced.c` 16 | - You can also build it running `make` in sandec folder. 17 | 18 | Usage 19 | ===== 20 | 21 | `sandec [filename].raw` 22 | 23 | Copyright 24 | ========= 25 | Copyright (C) 2008 Robert Mazur (rm@nateo.pl) 26 | License GPLv3+: GNU GPL version 3 or later 27 | -------------------------------------------------------------------------------- /odvr_icons.c: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "odvr_icons.h" 23 | -------------------------------------------------------------------------------- /util/splogfilter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use XML::Simple; 6 | 7 | my $spl = XMLin($ARGV[0] or 'SnoopyProExport.log', 8 | ForceArrary => [ qw(urb) ]); 9 | 10 | use Data::Dumper; 11 | #print Dumper($spl); 12 | 13 | foreach my $urb (sort { $a->{timestamp} <=> $b->{timestamp} } @{$spl->{urb}}){ 14 | print "Config: $urb->{function}\n" if $urb->{endpoint} == -1; 15 | 16 | if(ref $urb->{payload}{payloadbytes} eq 'HASH'){ 17 | #print "!!!\n"; 18 | next; 19 | } 20 | 21 | my %name = ( 0x02, => 'Out', 0x81 => 'In', 0x83 => 'Status' ); 22 | if(defined $name{$urb->{endpoint}}){ 23 | print "$name{$urb->{endpoint}}:" . 24 | ' ' x (7 - length($name{$urb->{endpoint}})); 25 | hexdump($urb->{payload}{payloadbytes}); 26 | } 27 | print "\n"; 28 | } 29 | 30 | exit 0; 31 | 32 | sub hexdump { 33 | my @hexstr = split(//, shift); 34 | my $len = $#hexstr + 1; 35 | my $i = 0; 36 | 37 | while(my @set = splice(@hexstr, 0, 4)){ 38 | my $pset = join('', @set); 39 | print "\n" . ' ' x 8 if not $i % 8 and $i; 40 | print "$pset "; 41 | $i++; 42 | } 43 | print "\n" if not $len % 8; 44 | } 45 | -------------------------------------------------------------------------------- /odvr_date.h: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef ODVR_DATE_H 19 | #define ODVR_DATE_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | typedef struct odvrDate { 26 | GDate date; 27 | uint8_t hour; 28 | uint8_t min; 29 | uint8_t sec; 30 | } odvrDate_t; 31 | 32 | 33 | void set_date_format(char *format); 34 | /* Return a string representation of the date */ 35 | gchar *dateString(odvrDate_t *date); 36 | /* Compare dates */ 37 | gint datecmp(odvrDate_t *date1, odvrDate_t *date2); 38 | /* Compare date and times */ 39 | gint timecmp(odvrDate_t *date1, odvrDate_t *date2); 40 | void datecpy(odvrDate_t *date1, odvrDate_t *date2); 41 | 42 | #endif /* ODVR_DATE_H */ 43 | -------------------------------------------------------------------------------- /util/decodesetup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | while(<>){ 7 | print; 8 | 9 | if(/^(\S+)\s+(\S+)\s+S\s+(\S+)\s+s\s+(([0-9a-zA-Z]{2,4}\s*?){5})/){ 10 | my $raw = $4; 11 | my @setup = map { hex($_) } split(/\s+/, $raw); 12 | my %fields = decode_setup(@setup); 13 | $fields{raw} = $raw; 14 | 15 | #use Data::Dumper; 16 | #print Dumper(\%fields); 17 | 18 | print " = $fields{request}: $fields{wValue}, $fields{wLength}, $fields{wLength}\n"; 19 | } 20 | } 21 | 22 | exit 0; 23 | 24 | sub decode_setup { 25 | my @names = qw(bmRequestType bRequest wValue wIndex wLength); 26 | my @requests = qw(GET_STATUS CLEAR_FEATURE reserved 27 | SET_FEATURE reserved SET_ADDRESS 28 | GET_DESCRIPTOR SET_DESCRIPTOR 29 | GET_CONFIGURATION SET_CONFIGURATION 30 | GET_INTERFACE SET_INTERFACE 31 | SYNCH_FRAME); 32 | my @rttype = qw(Standard Class Vendor reserved); 33 | my %fields = ('packet' => join(':', @_)); 34 | my $i = 0; 35 | map { $fields{$names[$i++]} = $_ } @_; 36 | 37 | # Translate: name for request 38 | $fields{request} = $requests[$fields{bRequest}]; 39 | 40 | # Translate: request type bitfield 41 | my $rt = 0; 42 | vec($rt, 0, 8) = $fields{bmRequestType}; 43 | $fields{RequestTypeDirection} = vec($rt, 7, 1) ? 'Device-to-host' : 'Host-to-device'; 44 | $fields{RequestTypeType} = $rttype[vec($rt, 6, 1) * 2 + vec($rt, 5, 1)]; 45 | 46 | return %fields; 47 | } 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-g -O2 -Wall 3 | LDFLAGS= 4 | LIBS=-lusb -lsndfile -lm 5 | BINS=odvr odvr.x86 odvr-gui 6 | TICONS=TBA TBB TBC TBD TBS TBT 7 | FICONS=FCA FCB FCC FCD FCS 8 | ICONS=$(TICONS) $(FICONS) 9 | X86LIBS=-L$(HOME)/build/lib32 10 | PREFIX=/usr/local 11 | SYSCONFDIR=/etc 12 | VERSION=0.1.5-cml 13 | GUICFLAGS = `pkg-config gtk+-2.0 --cflags` 14 | LDADD = `pkg-config gtk+-2.0 --libs` 15 | CFLAGS+=$(GUICFLAGS) 16 | 17 | all: odvr odvr-gui buildsandec 18 | 19 | install: odvr odvr-gui 20 | install -o root -g root -m 755 odvr $(PREFIX)/bin 21 | install -o root -g root -m 755 odvr-gui $(PREFIX)/bin 22 | -lsb_release -a 2>/dev/null | grep -q Ubuntu && install -o root -g root -m 644 \ 23 | 41-odvr.rules $(SYSCONFDIR)/udev/rules.d/ && /etc/init.d/udev reload 24 | 25 | release: $(BINS) 26 | -rm -rf debpkg/usr debpkg/etc 27 | mkdir -p debpkg/usr/bin debpkg/etc/udev/rules.d 28 | cp odvr debpkg/usr/bin 29 | cp 41-odvr.rules debpkg/etc/udev/rules.d 30 | dpkg -b debpkg odvr-$(VERSION).deb 31 | 32 | odvr: cli.o olympusdvr.o 33 | $(CC) $(CFLAGS) $(LDFLAGS)-o $@ $^ $(LIBS) 34 | 35 | odvr.x86: cli.c olympusdvr.c 36 | gcc -static $(X86LIBS) -m32 -O2 -Wall -o $@ $^ -lusb -lsndfile -lm 37 | strip $@ 38 | 39 | odvr_icons.h: 40 | @ echo Making $@ 41 | @ gdk-pixbuf-csource --struct --extern --build-list `echo $(ICONS) | \ 42 | awk '{ for (i = 1; i <= NF; i++) printf("%s icons/%s.png ", $$i, $$i) }'` | \ 43 | sed 's/\/\* pixel_data: \*\// \/\* pixel_data \*\/ (guint8 *)/' > $@ 44 | 45 | odvr_icons.c: odvr_icons.h 46 | 47 | 48 | odvr-gui: odvr_icons.o gui.o odvr_gui.o odvr_date.o odvr_cfg.o olympusdvr.o 49 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(LDADD) 50 | 51 | buildsandec: sandec 52 | cd sandec && $(MAKE) 53 | 54 | clean: 55 | -rm -f *.o *~ \#*\# $(BINS) *.deb odvr_icons.h 56 | -------------------------------------------------------------------------------- /odvr_date.c: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | 21 | #include "odvr_date.h" 22 | 23 | static gchar *date_format = NULL; 24 | 25 | void set_date_format(char *format) 26 | { 27 | if (date_format) 28 | g_free(date_format); 29 | date_format = strdup(format); 30 | } 31 | 32 | /* Return a string representation of the date */ 33 | gchar *dateString(odvrDate_t *fileDate) 34 | { 35 | #define BUFSIZE 32 36 | gchar *buf; 37 | gchar *dateString; 38 | 39 | buf = g_malloc(BUFSIZE); 40 | dateString = g_malloc(BUFSIZE); 41 | g_date_strftime(dateString, BUFSIZE, date_format, &fileDate->date); 42 | if (buf && dateString) 43 | g_snprintf(buf, BUFSIZE, "%s-%02d:%02d:%02d", 44 | dateString, 45 | fileDate->hour, 46 | fileDate->min, 47 | fileDate->sec); 48 | g_free(dateString); 49 | return buf; 50 | } 51 | 52 | gint datecmp(odvrDate_t *date1, odvrDate_t *date2) 53 | { 54 | return g_date_compare(&date1->date, &date2->date); 55 | } 56 | 57 | gint timecmp(odvrDate_t *date1, odvrDate_t *date2) 58 | { 59 | if (datecmp(date1, date2) != 0) 60 | return datecmp(date1, date2); 61 | if (date1->hour != date2->hour) 62 | return date1->hour - date2->hour; 63 | if (date1->min != date2->min) 64 | return date1->min - date2->min; 65 | return date1->sec - date2->sec; 66 | } 67 | 68 | void datecpy(odvrDate_t *date1, odvrDate_t *date2) 69 | { 70 | date1->date = date2->date; 71 | date1->hour = date2->hour; 72 | date1->min = date2->min; 73 | date1->sec = date2->sec; 74 | } 75 | -------------------------------------------------------------------------------- /odvr_gui.h: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef ODVR_GUI_H 19 | #define ODVR_GUI_H 20 | 21 | #include 22 | #include 23 | #include "olympusdvr.h" 24 | #include "odvr_date.h" 25 | #include "odvr_cfg.h" 26 | 27 | typedef struct odvrData { 28 | odvr dev; 29 | GtkWidget *window; 30 | GdkPixbuf *folderIcon[ODVR_NUM_FOLDERS]; 31 | GtkWidget *menu_bar ; 32 | GtkWidget *buttonBox; 33 | GtkWidget *destinationDirBox; 34 | GtkWidget *ddDialog; 35 | GtkWidget *ddButton; 36 | GtkWidget *overallProgressBar; 37 | GtkWidget *fileProgressBar; 38 | GtkTreeSelection *selection; 39 | GtkWidget *view; 40 | /* 41 | * I keep a pointer to the D and S buttons and menus so that I hide or reveal them 42 | * depending on the model of the device 43 | */ 44 | GtkWidget *buttonD, *buttonS; 45 | GtkWidget *transferMenuD, *transferMenuS; 46 | GtkWidget *clearMenuD, *clearMenuS; 47 | GtkTreeStore *file_store; 48 | gint quality_used; /* Bitmap */ 49 | odvr_config_t *cfg; 50 | } odvrData_t; 51 | 52 | enum { 53 | COL_FOLDER = 0, 54 | COL_FOLDER_ID, 55 | COL_SLOT, 56 | COL_ID, 57 | COL_SIZE, 58 | COL_LENGTH, 59 | COL_DATE, 60 | COL_QUALITY, 61 | NUM_COLS 62 | }; 63 | 64 | #define ODVR_RESPONSE_YES_ALL 1 65 | #define ODVR_RESPONSE_NO_ALL 2 66 | 67 | void gui_err(const char *errorString, const char *errorDetail); 68 | 69 | gint yes_no_acknowledge(gchar *question); 70 | 71 | gint acknowledge_ok_cancel(gchar *question); 72 | 73 | gint acknowledge(gchar *question); 74 | 75 | /* Exit the program without confirmation or saving configuration */ 76 | gboolean delete_event( GtkWidget *widget); 77 | gboolean quit_event( GtkWidget *widget, gpointer data); 78 | 79 | GtkWidget *create_view (odvrData_t *odvrData); 80 | 81 | #endif /* ODVR_GUI_H */ 82 | -------------------------------------------------------------------------------- /odvr_cfg.h: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef ODVR_CFG_H 19 | #define ODVR_CFG_H 20 | 21 | #include 22 | 23 | #define CONFIGFILE ".odvr-gui.cfg" 24 | #define DEFAULT_WINDOW_WIDTH 500 25 | #define DEFAULT_WINDOW_HEIGHT 600 26 | #define DEFAULT_PROGRESS_WINDOW_WIDTH 500 27 | #define DEFAULT_PROGRESS_WINDOW_HEIGHT 100 28 | #define DEFAULT_DATE_FORMAT "%Y/%m/%d" 29 | 30 | typedef struct odvr_config { 31 | gboolean dirty; 32 | char *version; 33 | char *destination_dir; 34 | gboolean show_wav_size; 35 | gboolean keep_original_timestamp; 36 | gint window_height, window_width; 37 | gint progress_window_height, progress_window_width; 38 | char *date_format; 39 | gboolean report_total_filesize; 40 | } odvr_config_t; 41 | 42 | odvr_config_t *odvr_cfg_load_config(void); 43 | void odvr_cfg_save_config(odvr_config_t *cfg); 44 | gboolean odvr_cfg_get_dirty(odvr_config_t *cfg); 45 | void odvr_cfg_set_version(odvr_config_t *cfg, char *version); 46 | char *odvr_cfg_get_version(odvr_config_t *cfg); 47 | void odvr_cfg_set_destination_dir(odvr_config_t *cfg, char *destination_dir); 48 | char *odvr_cfg_get_destination_dir(odvr_config_t *cfg); 49 | void odvr_cfg_set_show_wav_size(odvr_config_t *cfg, gboolean show_wav_size); 50 | gboolean odvr_cfg_get_show_wav_size(odvr_config_t *cfg); 51 | void odvr_cfg_set_keep_original_timestamp(odvr_config_t *cfg, gboolean keep_original_timestamp); 52 | gboolean odvr_cfg_get_keep_original_timestamp(odvr_config_t *cfg); 53 | void odvr_cfg_set_window_size(odvr_config_t *cfg, gint width, gint height); 54 | void odvr_cfg_set_progress_window_size(odvr_config_t *cfg, gint width, gint height); 55 | void odvr_cfg_get_window_size(odvr_config_t *cfg, gint *width, gint *height); 56 | void odvr_cfg_get_progress_window_size(odvr_config_t *cfg, gint *width, gint *height); 57 | void odvr_cfg_set_date_format(odvr_config_t *cfg, char *date_format); 58 | char *odvr_cfg_get_date_format(odvr_config_t *cfg); 59 | void odvr_cfg_set_report_total_filesize(odvr_config_t *cfg, gboolean report_total_filesize); 60 | gboolean odvr_cfg_get_report_total_filesize(odvr_config_t *cfg); 61 | 62 | #endif /* ODVR_CFG_H */ 63 | -------------------------------------------------------------------------------- /olympusdvr.h: -------------------------------------------------------------------------------- 1 | /* olympusdvr.h: Olympus DVR library 2 | * 3 | * Copyright (C) 2007-2008 Tristan Willy 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #ifndef OLYMPUSDVR_H 19 | #define OLYMPUSDVR_H 20 | 21 | #include 22 | 23 | /* odvr_open flags */ 24 | #define ODVR_TRACE 1 25 | 26 | /* misc defined */ 27 | #define ODVR_FOLDER_A 1 28 | #define ODVR_FOLDER_B 2 29 | #define ODVR_FOLDER_C 3 30 | #define ODVR_FOLDER_D 4 31 | #define ODVR_FOLDER_S 4 32 | #define ODVR_NUM_FOLDERS 4 33 | 34 | #define ODVR_QUALITY_HQ 2 35 | #define ODVR_QUALITY_SP 0 36 | #define ODVR_QUALITY_LP 1 37 | #define ODVR_QUALITY_NEW_SP 5 38 | #define ODVR_QUALITY_NEW_LP 6 39 | #define ODVR_QUALITY_NEW_HQ 7 40 | #define ODVR_QUALITY_XHQ 8 41 | #define ODVR_QUALITY_NUM 9 /* Number of quality settings */ 42 | 43 | typedef struct odvr * odvr; 44 | 45 | typedef struct filestat { 46 | uint8_t folder; 47 | uint8_t slot; 48 | uint16_t id; 49 | uint16_t size; 50 | uint8_t month; 51 | uint8_t day; 52 | uint8_t year; /* year = 2000 + filestat.year */ 53 | uint8_t hour; 54 | uint8_t min; 55 | uint8_t sec; 56 | uint8_t quality; 57 | } filestat_t; 58 | 59 | /* open and close interfaces */ 60 | odvr odvr_open(int); 61 | void odvr_close(odvr); 62 | 63 | /* resets the DVR and reconnects to it */ 64 | int odvr_reset(odvr, int); 65 | 66 | /* return a string describing an error */ 67 | const char *odvr_error(odvr); 68 | 69 | /* Turn on/off tracing */ 70 | int odvr_enable_trace(odvr); 71 | int odvr_disable_trace(odvr); 72 | 73 | /* return specific model (multiple models per product ID) */ 74 | const char *odvr_model(odvr); 75 | /* returns the number of used slots in a folder */ 76 | int odvr_filecount(odvr, uint8_t folder); 77 | /* returns the number of folders in the device */ 78 | int odvr_foldercount(odvr); 79 | /* returns folder code for ASCII character name */ 80 | uint8_t odvr_foldercode(odvr, const char); 81 | /* returns folder ASCII character name for a given folder code */ 82 | char odvr_foldername(odvr, const uint8_t); 83 | /* get folder/slot metadata, like quality and recording date */ 84 | int odvr_filestat(odvr, uint8_t folder, uint8_t slot, filestat_t *); 85 | /* file size to length convert */ 86 | float odvr_length(filestat_t *); 87 | /* file size to wav file size convert */ 88 | uint32_t odvr_wavfilesize(filestat_t *stat); 89 | /* returns a description of a quality level */ 90 | const char *odvr_quality_name(uint8_t quality); 91 | /* open a folder/slot for reading */ 92 | int odvr_open_file(odvr, uint8_t folder, uint8_t slot); 93 | /* returns minimum block size */ 94 | int odvr_block_size(odvr); 95 | /* read a block of signed PCM 16 */ 96 | int odvr_read_block(odvr, void *block, int maxbytes, uint8_t quality); 97 | /* open, read, and save a WAV file */ 98 | int odvr_save_wav(odvr, uint8_t folder, uint8_t slot, int fd); 99 | /* open, read, and save a raw data file */ 100 | int odvr_save_raw(odvr, uint8_t folder, uint8_t slot, int fd); 101 | /* delete a whole folder */ 102 | int odvr_clear_folder(odvr, uint8_t folder); 103 | 104 | #endif /* OLYMPUSDVR_H */ 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | odvr is a user-space driver for Olympus digital voice recorders that do not 5 | support USB Mass Storage. Not all formats are directly supported 6 | (sandec/PULCOD), and functionality is limited, but basic download and listing 7 | capabilities are implemented. 8 | 9 | 10 | Building 11 | ======== 12 | 13 | Build dependencies: 14 | 15 | ``` 16 | sudo apt-get update && sudo apt-get install libusb-dev libgtk2.0-dev libsndfile1-dev -y 17 | ``` 18 | 19 | There is no configure script at this time. Libusb and libsndfile, 20 | and their associated development headers, are required. To build, run: 21 | 22 | ``` 23 | $ make odvr 24 | ``` 25 | 26 | A static x86 linux binary is included as `odvr.x86`. 27 | 28 | 29 | Installing 30 | ========== 31 | 32 | 1. Build odvr. 33 | 2. Run "make install" as root. 34 | 3. If you are not running Ubuntu, modify `41-odvr.rules` to fit your distro 35 | and install it. Don't forget to reload the udev rules. The other option is 36 | to run odvr as root, but this is not recommended. 37 | 38 | 39 | Running 40 | ======= 41 | 42 | odvr *requires* access to the user-space USB interface. It is recommended 43 | to place `41-odvr.rules` into `/etc/udev/rules.d` or setup your own udev rules 44 | rather than running odvr as root. After changing udev rules, don't forget to 45 | run "udevcontrol reload_rules" and to replugin your DVR. 46 | 47 | Again, root privileges are required unless udev is properly setup. 48 | 49 | For the impatient, run the following to test odvr: 50 | 51 | ``` 52 | $ sudo odvr -l 53 | ``` 54 | 55 | Usage 56 | ===== 57 | 58 | Usage: odvr [options] 59 | 60 | -= Options =- 61 | 62 | ``` 63 | -h : This help. 64 | -v : Print version. 65 | -d : Download all files in . 66 | -e : Download everything. 67 | -l : List all files. 68 | -x : Delete all recordings in . 69 | -c : Delete all recordings. 70 | -y : "yes" to all yes/no questions. 71 | -r : Reset the DVR. This may fix some sync issues. 72 | -D : Enable debug tracing. 73 | -E : Download everything in RAW format. 74 | -t : Format downloaded wav files names as F-YYYYmmdd-HHMMss. 75 | ``` 76 | 77 | Examples 78 | ======== 79 | 80 | List recording information: 81 | 82 | ``` 83 | $ odvr -l 84 | ``` 85 | 86 | Download all recordings in folder B: 87 | 88 | ``` 89 | $ odvr -d b 90 | ``` 91 | 92 | Download all recordings in the scheduled folder: 93 | 94 | ``` 95 | $ odvr -d s 96 | ``` 97 | 98 | List and download every recording: 99 | 100 | ``` 101 | $ odvr -l -e 102 | ``` 103 | 104 | 105 | Notes 106 | ===== 107 | 108 | Downloaded files are signed 16 bit PCM WAV files at the recorded sample rate. 109 | Olympus DVRs internally use a 3-bit differential PCM format with 14 bit 110 | resolution. Unfortunately, the Windows software converts from this format into 111 | lossy 4bit IMA ADPCM *and* it resamples. Files downloaded by odvr should be of 112 | higher quality than with the Windows software, but it does result in larger 113 | WAV files. It is recommended to recompress the WAV files with MP3, Vorbis, or 114 | Speex if file size is an issue. 115 | 116 | Some programs may have difficulty playing or reading the odd bitrate WAV 117 | files. If they do, I recommend using "sox" to resample them into something more 118 | common, such as 44100hz. 119 | 120 | Mac/PPC support is currently non-existent. There are several places in the 121 | odvr code that are endian sensitive, and the code hasn't been tested on 122 | big-endian machines. PPC Mac/Linux/BSD developers are more than welcome to 123 | submit patches! 124 | 125 | odvr may get out-of-sync with the attached DVR. Use `-r` to force a DVR reset 126 | when odvr runs. For example, `odvr -r -l` will reset and then list recordings. 127 | 128 | Some Olympus DVRs have a high-quality encoding option (PULCOD). This 129 | encoding type is not directly supported and odvr will complain about it. Use a 130 | different quality level for your recordings or you can use the unsupported 131 | `sandec` program that that is included with this source. Use odvr to 132 | download the raw files, then run `sandec` with the filename of the raw file. 133 | You can also convert all raw files downloaded to odvr folder automatically 134 | running the bash script also included in this source `./convert_raw_files.sh`. 135 | -------------------------------------------------------------------------------- /sandec/sandec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef int (__cdecl *SAN_DEC)(uint8_t *in, uint8_t *out, int mode, int bit_size); 7 | typedef int (__cdecl *SAN_DEC_PULCOD2_INIT)(int a, int b); 8 | 9 | #define wav_add_long(a) out[i++] = a & 0xff;\ 10 | out[i++] = (a >> 8) & 0xff;\ 11 | out[i++] = (a >> 16) & 0xff;\ 12 | out[i++] = (a >> 24) & 0xff;\ 13 | 14 | int wave_header(uint8_t out[], int data_size, int freq) 15 | { 16 | int file_size = data_size + 44 - 8; 17 | int i = 0; 18 | 19 | out[i++] = 'R'; 20 | out[i++] = 'I'; 21 | out[i++] = 'F'; 22 | out[i++] = 'F'; 23 | 24 | wav_add_long(file_size); 25 | 26 | out[i++] = 'W'; 27 | out[i++] = 'A'; 28 | out[i++] = 'V'; 29 | out[i++] = 'E'; 30 | 31 | out[i++] = 'f'; 32 | out[i++] = 'm'; 33 | out[i++] = 't'; 34 | out[i++] = ' '; 35 | 36 | out[i++] = 16; 37 | out[i++] = 0; 38 | out[i++] = 0; 39 | out[i++] = 0; 40 | 41 | out[i++] = 1; // uncompressed 42 | out[i++] = 0; 43 | 44 | out[i++] = 1; // mono 45 | out[i++] = 0; 46 | 47 | wav_add_long(freq); // sample rate 48 | 49 | int byte_rate = freq*16*1/8; 50 | wav_add_long(byte_rate); // bps 51 | 52 | out[i++] = 2; // block aligment 53 | out[i++] = 0; 54 | 55 | out[i++] = 16; // bits per sample 56 | out[i++] = 0; 57 | 58 | out[i++] = 'd'; 59 | out[i++] = 'a'; 60 | out[i++] = 't'; 61 | out[i++] = 'a'; 62 | 63 | wav_add_long(data_size); 64 | 65 | return i; 66 | } 67 | 68 | int main(int argc, const char *argv[]) 69 | { 70 | 71 | if(argc < 2) 72 | { 73 | printf("sandec 1.1 \n\ 74 | Copyright (C) 2008 Robert Mazur (rm@nateo.pl)\n\ 75 | Converts Olympus Digital Voice Recorder files to WAV files.\n\ 76 | License GPLv3+: GNU GPL version 3 or later \n\ 77 | Ussage: sandec [filename].raw\n"); 78 | return -1; 79 | } 80 | 81 | HINSTANCE hLibrary = LoadLibrary("san_dec.dll"); 82 | if (hLibrary == NULL) 83 | { 84 | printf("Unable to load san_dec.dll!\n"); 85 | return -1; 86 | } 87 | 88 | SAN_DEC san_dec; 89 | san_dec = (SAN_DEC)GetProcAddress(hLibrary, "san_dec"); 90 | 91 | SAN_DEC_PULCOD2_INIT san_dec_pulcod2_init; 92 | san_dec_pulcod2_init = (SAN_DEC_PULCOD2_INIT)GetProcAddress(hLibrary, "san_dec_pulcod2_init"); 93 | 94 | if(san_dec == NULL) 95 | { 96 | printf("Unable to find 'san_dec' proc!\n"); 97 | return -1; 98 | } 99 | 100 | if(san_dec_pulcod2_init == NULL) 101 | { 102 | printf("Unable to find 'san_dec_pulcod2_init' proc!\n"); 103 | return -1; 104 | } 105 | 106 | int ret, i; 107 | 108 | int fd, fd2; 109 | uint8_t in[8*1024], out[32*1024]; 110 | 111 | int len = 0, out_len = 0, data_size=0, quality = 0, freq=16000, pulcod_size=0, max_size=0; 112 | int bit_size = 16; 113 | int mode = 1; 114 | 115 | fd = open(argv[1],O_RDONLY|O_BINARY); 116 | if(fd<0) 117 | { 118 | printf("Unable to open '%s'!\n",argv[1]); 119 | return -1; 120 | } 121 | 122 | // 5 odvr\01 123 | // 13 fileinfo 124 | 125 | lseek(fd, 5+12, SEEK_SET); 126 | 127 | ret = read(fd, &quality, 1); 128 | if(ret < 0) return -1; 129 | 130 | 131 | switch(quality) 132 | { 133 | case 0: freq = 10600; mode = 0; break; 134 | case 1: freq = 5750; mode = 0; break; 135 | case 2: freq = 16000; mode = 0; break; 136 | case 3: mode = 0; break; 137 | case 4: break; 138 | case 5: freq = 12000; pulcod_size = 36; mode = 2; max_size = 4032; break; 139 | case 6: freq = 8000; pulcod_size = 64; mode = 2; max_size = 7168; break; 140 | case 7: freq = 16000; pulcod_size = 24; mode = 2; max_size = 2688; break; 141 | case 8: freq = 16000; mode = 1; break; 142 | case 9: mode = 1; break; 143 | case 10: mode = 1; break; 144 | } 145 | 146 | if(pulcod_size) 147 | { 148 | ret = san_dec_pulcod2_init(freq, pulcod_size); 149 | if(ret < 0) 150 | { 151 | printf("Pulcod2_init (%d,%d) failed!\n", freq, pulcod_size); 152 | return -1; 153 | } 154 | } 155 | 156 | char filename[512]; 157 | 158 | strncpy(filename, argv[1], 512); 159 | filename[511]=0; 160 | 161 | int str_len = strlen(filename); 162 | if(str_len < 4) str_len = 4; 163 | 164 | filename[str_len-0] = 0; 165 | filename[str_len-1] = 'v'; 166 | filename[str_len-2] = 'a'; 167 | filename[str_len-3] = 'w'; 168 | filename[str_len-4] = '.'; 169 | 170 | fd2 = open(filename, O_CREAT|O_WRONLY|O_BINARY|O_EXCL); 171 | if(fd2<0) 172 | { 173 | printf("Unable to open '%s'!\n", filename); 174 | return -1; 175 | } 176 | 177 | ret = wave_header(out, 0, freq); 178 | 179 | ret = write(fd2, out, ret); 180 | 181 | if(ret<0) 182 | { 183 | printf("Wave write error!\n"); 184 | return -1; 185 | } 186 | 187 | 188 | do{ 189 | 190 | len = 0; 191 | 192 | ret = read(fd, &len, 2); 193 | 194 | if(ret < 0) break; 195 | 196 | if(read(fd, in, len) <= 0) break; 197 | 198 | for(i=0; i<32*1024; i++) out[i] = 0; 199 | 200 | if(pulcod_size) 201 | { 202 | int i; 203 | 204 | for(i = 2; i < 512; i += 256) 205 | { 206 | int k = 0; 207 | int count = 0; 208 | do 209 | { 210 | 211 | if(in[i+k] == 0x80) 212 | { 213 | int zero = 1, j; 214 | for(j = 1; j < 9; j++) 215 | if(in[i+k+j] != 0) zero = 0; 216 | if(zero) //silence to end 217 | break; 218 | } 219 | 220 | ret = san_dec(in+i+k, out+out_len, mode, bit_size); 221 | 222 | if(ret >= 0) 223 | { 224 | out_len += ret*2; 225 | k += 9; 226 | } 227 | 228 | }while(++count < 28); 229 | } 230 | if(out_len < max_size) 231 | out_len = max_size; 232 | 233 | } 234 | else // ! pulcod2 235 | { 236 | ret = san_dec(in+2, out+out_len, mode, bit_size); 237 | if(ret <= 0) 238 | break; 239 | out_len += ret*2; 240 | } 241 | 242 | ret = write(fd2, out, out_len); 243 | 244 | if(ret<0) 245 | { 246 | printf("Wave write error!\n"); 247 | return -1; 248 | } 249 | 250 | data_size += out_len; 251 | out_len = 0; 252 | 253 | }while(len > 0); 254 | 255 | close(fd); 256 | 257 | ret = lseek(fd2, 0, SEEK_SET); 258 | 259 | if(ret<0) 260 | { 261 | printf("Wave seek error!\n"); 262 | return -1; 263 | } 264 | 265 | ret = wave_header(out, data_size, freq); 266 | 267 | ret = write(fd2, out, ret); 268 | 269 | if(ret<0) 270 | { 271 | printf("Wave write error!\n"); 272 | return -1; 273 | } 274 | 275 | ret = close(fd2); 276 | 277 | if(ret<0) 278 | { 279 | printf("Wave close error!\n"); 280 | } 281 | 282 | FreeLibrary(hLibrary); 283 | 284 | return 0; 285 | } 286 | -------------------------------------------------------------------------------- /odvr_cfg.c: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "odvr_cfg.h" 23 | 24 | static int strcmp0(char *s1, char *s2) 25 | { 26 | if (s1 && s2) 27 | return strcmp(s1, s2); 28 | if ((s1 == NULL) && (s2 == NULL)) 29 | return 0; 30 | if (s1) 31 | return 1; 32 | return -1; 33 | } 34 | 35 | /* Load the configuration parameters */ 36 | odvr_config_t *odvr_cfg_load_config(void) 37 | { 38 | char *cfgfilename; 39 | FILE *f_config; 40 | odvr_config_t *cfg; 41 | 42 | cfg = g_new0(odvr_config_t, 1); 43 | if (!cfg) 44 | return NULL; 45 | 46 | /* Set defaults */ 47 | cfg->dirty = FALSE; 48 | cfg->version = NULL; 49 | cfg->destination_dir = g_strconcat (g_get_home_dir(), NULL); 50 | cfg->show_wav_size = FALSE; 51 | cfg->keep_original_timestamp = FALSE; 52 | cfg->window_height = DEFAULT_WINDOW_HEIGHT; 53 | cfg->window_width = DEFAULT_WINDOW_WIDTH; 54 | cfg->progress_window_height = DEFAULT_PROGRESS_WINDOW_HEIGHT; 55 | cfg->progress_window_width = DEFAULT_PROGRESS_WINDOW_WIDTH; 56 | cfg->date_format = DEFAULT_DATE_FORMAT; 57 | cfg->report_total_filesize = TRUE; 58 | 59 | cfgfilename = g_build_filename (g_get_home_dir(), CONFIGFILE, NULL); 60 | 61 | f_config = fopen (cfgfilename, "r"); 62 | 63 | if (f_config) { 64 | /* g_print ("reading from file '%s'\n", cfgfilename); */ 65 | 66 | char line[1024]; 67 | char *value; 68 | 69 | while (fgets (line, 1023, f_config)) { 70 | line[strlen(line)-1]= (char) 0; /* eliminate lf */ 71 | 72 | if (line[0] == '#') 73 | continue; 74 | 75 | if ((value = strchr (line, ':'))) { 76 | 77 | *value = (char) 0; 78 | value++; 79 | 80 | if (!strcmp0(line,"version")) { 81 | if (strlen(value)) 82 | cfg->version = strdup(value); 83 | else 84 | cfg->version = NULL; 85 | } 86 | else if (!strcmp0(line,"destination_dir")) { 87 | if (strlen(value)) 88 | cfg->destination_dir = strdup(value); 89 | else 90 | cfg->destination_dir = g_strconcat (g_get_home_dir(), NULL); 91 | } 92 | else if (!strcmp0(line,"show_wav_file_size")) { 93 | if (strlen(value)) 94 | cfg->show_wav_size = (atoi(value) != 0); 95 | else 96 | cfg->show_wav_size = FALSE; 97 | } 98 | else if (!strcmp0(line,"keep_original_timestamp")) { 99 | if (strlen(value)) 100 | cfg->keep_original_timestamp = (atoi(value) != 0); 101 | else 102 | cfg->keep_original_timestamp = FALSE; 103 | } 104 | else if (!strcmp0(line,"window_height")) { 105 | if (strlen(value)) 106 | cfg->window_height = atoi(value); 107 | else 108 | cfg->window_height = DEFAULT_WINDOW_HEIGHT; 109 | } 110 | else if (!strcmp0(line,"window_width")) { 111 | if (strlen(value)) 112 | cfg->window_width = atoi(value); 113 | else 114 | cfg->window_width = DEFAULT_WINDOW_WIDTH; 115 | } 116 | else if (!strcmp0(line,"progress_window_height")) { 117 | if (strlen(value)) 118 | cfg->progress_window_height = atoi(value); 119 | else 120 | cfg->progress_window_height = DEFAULT_WINDOW_HEIGHT; 121 | } 122 | else if (!strcmp0(line,"progress_window_width")) { 123 | if (strlen(value)) 124 | cfg->progress_window_width = atoi(value); 125 | else 126 | cfg->progress_window_width = DEFAULT_WINDOW_WIDTH; 127 | } 128 | else if (!strcmp0(line,"date_format")) { 129 | if (strlen(value)) 130 | cfg->date_format = strdup(value); 131 | else 132 | cfg->date_format = DEFAULT_DATE_FORMAT; 133 | } 134 | else if (!strcmp0(line,"report_total_filesize")) { 135 | if (strlen(value)) 136 | cfg->report_total_filesize = atoi(value); 137 | else 138 | cfg->report_total_filesize = TRUE; 139 | } 140 | else 141 | g_print("Unknown config key %s\n", line); 142 | } 143 | } 144 | cfg->dirty = FALSE; 145 | 146 | fclose (f_config); 147 | } 148 | 149 | return cfg; 150 | } 151 | 152 | void odvr_cfg_save_config(odvr_config_t *cfg) 153 | { 154 | char *cfgfilename; 155 | FILE *f_config; 156 | 157 | if (!cfg) 158 | return; 159 | 160 | cfgfilename = g_build_filename (g_get_home_dir(), CONFIGFILE, NULL); 161 | 162 | f_config = fopen (cfgfilename, "w"); 163 | 164 | if (f_config) { 165 | fprintf(f_config, "# Configuration file for odvr-gui\n"); 166 | fprintf(f_config, "version:%s\n", cfg->version); 167 | fprintf(f_config, "destination_dir:%s\n", cfg->destination_dir); 168 | fprintf(f_config, "show_wav_file_size:%d\n", cfg->show_wav_size?1:0); 169 | fprintf(f_config, "keep_original_timestamp:%d\n", cfg->keep_original_timestamp?1:0); 170 | fprintf(f_config, "window_width:%d\n", cfg->window_width); 171 | fprintf(f_config, "window_height:%d\n", cfg->window_height); 172 | fprintf(f_config, "progress_window_width:%d\n", cfg->progress_window_width); 173 | fprintf(f_config, "progress_window_height:%d\n", cfg->progress_window_height); 174 | fprintf(f_config, "date_format:%s\n", cfg->date_format); 175 | fprintf(f_config, "report_total_filesize:%d\n", cfg->report_total_filesize); 176 | 177 | fclose(f_config); 178 | } 179 | else { 180 | g_print("Couldn't open config file %s\n",cfgfilename ); 181 | } 182 | } 183 | 184 | #define CFG_GET(TYPE, NAME, DEFAULT) \ 185 | TYPE odvr_cfg_get_##NAME(odvr_config_t *cfg) { \ 186 | if (!cfg) \ 187 | return DEFAULT; \ 188 | return cfg->NAME; \ 189 | } 190 | 191 | #define CFG_GET_STRING(NAME, DEFAULT) \ 192 | char *odvr_cfg_get_##NAME(odvr_config_t *cfg) { \ 193 | if (!cfg) \ 194 | return DEFAULT; \ 195 | return cfg->NAME; \ 196 | } 197 | 198 | #define CFG_SET_STRING(NAME) \ 199 | void odvr_cfg_set_##NAME(odvr_config_t *cfg, char *NAME) \ 200 | { \ 201 | if (!cfg) \ 202 | return; \ 203 | if (strcmp0(cfg->NAME, NAME)) { \ 204 | g_free(cfg->NAME); \ 205 | cfg->dirty = TRUE; \ 206 | cfg->NAME = strdup(NAME); \ 207 | } \ 208 | } 209 | 210 | #define CFG_SET_ORDINAL(TYPE, NAME) \ 211 | void odvr_cfg_set_##NAME(odvr_config_t *cfg, TYPE NAME) \ 212 | { \ 213 | if (!cfg) \ 214 | return; \ 215 | if (cfg->NAME != NAME) { \ 216 | cfg->dirty = TRUE; \ 217 | cfg->NAME = NAME; \ 218 | } \ 219 | } 220 | 221 | void odvr_cfg_get_window_size(odvr_config_t *cfg, gint *width, gint *height) 222 | { 223 | if (!cfg) 224 | return; 225 | *height = cfg->window_height; 226 | *width = cfg->window_width; 227 | 228 | } 229 | 230 | void odvr_cfg_get_progress_window_size(odvr_config_t *cfg, gint *width, gint *height) 231 | { 232 | if (!cfg) 233 | return; 234 | *height = cfg->progress_window_height; 235 | *width = cfg->progress_window_width; 236 | 237 | } 238 | 239 | void odvr_cfg_set_version(odvr_config_t *cfg, char *version) 240 | { 241 | if (!cfg) 242 | return; 243 | if (strcmp0(cfg->version, version)) { 244 | g_free(cfg->version); 245 | cfg->version = strdup(version); 246 | } 247 | } 248 | 249 | void odvr_cfg_set_window_size(odvr_config_t *cfg, gint width, gint height) 250 | { 251 | if (!cfg) 252 | return; 253 | if (cfg->window_width != width) { 254 | cfg->dirty = TRUE; 255 | cfg->window_width = width; 256 | } 257 | if (cfg->window_height != height) { 258 | cfg->dirty = TRUE; 259 | cfg->window_height = height; 260 | } 261 | } 262 | 263 | void odvr_cfg_set_progress_window_size(odvr_config_t *cfg, gint width, gint height) 264 | { 265 | if (!cfg) 266 | return; 267 | if (cfg->progress_window_width != width) { 268 | cfg->dirty = TRUE; 269 | cfg->progress_window_width = width; 270 | } 271 | if (cfg->progress_window_height != height) { 272 | cfg->dirty = TRUE; 273 | cfg->progress_window_height = height; 274 | } 275 | } 276 | 277 | CFG_GET(gboolean, dirty, FALSE); 278 | CFG_GET_STRING(destination_dir, NULL); 279 | CFG_GET(gboolean, show_wav_size, FALSE); 280 | CFG_GET(gboolean, keep_original_timestamp, FALSE); 281 | CFG_GET_STRING(date_format, NULL); 282 | CFG_GET(gboolean, report_total_filesize, TRUE); 283 | 284 | CFG_SET_STRING(destination_dir); 285 | CFG_SET_ORDINAL(gboolean, show_wav_size); 286 | CFG_SET_ORDINAL(gboolean, keep_original_timestamp); 287 | CFG_SET_STRING(date_format); 288 | CFG_SET_ORDINAL(gboolean, report_total_filesize); 289 | -------------------------------------------------------------------------------- /cli.c: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR CLI 2 | * 3 | * Copyright (C) 2007-2008 Tristan Willy 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "olympusdvr.h" 29 | 30 | #define VERSION "0.1.5" 31 | 32 | void download_folder(odvr dev, uint8_t folder, int opt_time); 33 | void download_folder_raw(odvr dev, uint8_t folder, int opt_time); 34 | 35 | void print_listing(odvr dev); 36 | int user_confirmed(const char *); 37 | void print_usage(void); 38 | 39 | int main(int argc, char *argv[]){ 40 | int opt, opt_download, opt_ls, opt_reset, opt_debug, 41 | opt_clear, opt_delete, opt_yesall, opt_raw, opt_time; 42 | int i, open_flags; 43 | const char *model; 44 | odvr dev; 45 | 46 | opt_ls = opt_download = opt_reset = opt_debug = opt_clear = 47 | opt_delete = opt_yesall = opt_raw = opt_time = 0; 48 | while((opt = getopt(argc, argv, "hvleErDd:cx:yt")) != -1){ 49 | switch(opt){ 50 | case 'h': 51 | print_usage(); 52 | exit(1); 53 | case 'v': 54 | fprintf(stderr, "odvr version %s\n", VERSION); 55 | exit(1); 56 | case 'e': 57 | opt_download = 1; 58 | break; 59 | case 'E': 60 | opt_raw = 1; 61 | break; 62 | case 'r': 63 | opt_reset = 1; 64 | break; 65 | case 'd': 66 | if(optarg[1] == '\0') 67 | opt_download = toupper(optarg[0]); 68 | if(!((opt_download >= 'A' && opt_download <= 'D') || 69 | opt_download == 'S')){ 70 | fprintf(stderr, "Bad download option.\n"); 71 | print_usage(); 72 | exit(1); 73 | } 74 | break; 75 | case 'D': 76 | opt_debug = 1; 77 | break; 78 | case 'l': 79 | opt_ls = 1; 80 | break; 81 | case 'c': 82 | opt_clear = 1; 83 | break; 84 | case 't': 85 | opt_time = 1; 86 | break; 87 | case 'x': 88 | if(optarg[1] == '\0') 89 | opt_delete = toupper(optarg[0]); 90 | if(!((opt_delete >= 'A' && opt_delete <= 'D') || 91 | opt_delete == 'S')){ 92 | fprintf(stderr, "Bad delete option.\n"); 93 | print_usage(); 94 | exit(1); 95 | } 96 | break; 97 | case 'y': 98 | opt_yesall = 1; 99 | break; 100 | case '?': 101 | case ':': 102 | default: 103 | print_usage(); 104 | exit(1); 105 | } 106 | } 107 | 108 | open_flags = 0; 109 | if(opt_debug) 110 | open_flags = ODVR_TRACE; 111 | 112 | if((dev = odvr_open(open_flags)) == NULL){ 113 | fprintf(stderr, "Failed to open Olympus device: %s\n", odvr_error(NULL)); 114 | exit(1); 115 | } 116 | 117 | /* fixme: odvr_close() should leave the device in a stable state */ 118 | if(opt_reset){ 119 | printf("Resetting...\n"); 120 | if(odvr_reset(dev, open_flags)){ 121 | fprintf(stderr, "Failed to reset and reaquire device: %s\n", odvr_error(dev)); 122 | exit(1); 123 | } 124 | } 125 | 126 | if((model = odvr_model(dev)) == NULL){ 127 | fprintf(stderr, "Couldn't query model name: %s\n", odvr_error(dev)); 128 | exit(1); 129 | } 130 | printf("Model: %s\n", model); 131 | 132 | if(opt_ls){ 133 | print_listing(dev); 134 | } 135 | 136 | if(opt_download == 1){ 137 | for(i = ODVR_FOLDER_A; i <= odvr_foldercount(dev); i++) 138 | download_folder(dev, i, opt_time); 139 | } else if(opt_download > 1 && 140 | odvr_foldercode(dev, opt_download) <= odvr_foldercount(dev)){ 141 | download_folder(dev, odvr_foldercode(dev, opt_download), opt_time); 142 | } 143 | 144 | if(opt_raw != 0) 145 | for(i = ODVR_FOLDER_A; i <= odvr_foldercount(dev); i++) 146 | download_folder_raw(dev, i, opt_time); 147 | 148 | if(opt_clear){ 149 | if(opt_yesall || user_confirmed("delete all recordings")){ 150 | for(i = ODVR_FOLDER_A; i <= odvr_foldercount(dev); i++){ 151 | if(odvr_clear_folder(dev, i)) 152 | fprintf(stderr, "Couldn't clear folder %c: %s\n", odvr_foldername(dev, i), 153 | odvr_error(dev)); 154 | } 155 | } 156 | } 157 | if(opt_delete){ 158 | if(!opt_yesall) 159 | printf("Going to delete folder %c.\n", opt_delete); 160 | if(opt_yesall || user_confirmed(NULL)){ 161 | if(odvr_clear_folder(dev, odvr_foldercode(dev, opt_delete))) 162 | fprintf(stderr, "Couldn't clear folder %c: %s\n", opt_delete, 163 | odvr_error(dev)); 164 | } 165 | } 166 | 167 | odvr_close(dev); 168 | exit(0); 169 | } 170 | 171 | void print_listing(odvr dev){ 172 | filestat_t stat; 173 | int folder, i, numfiles, sec, min, hour; 174 | 175 | for(folder = ODVR_FOLDER_A; folder <= odvr_foldercount(dev); folder++){ 176 | if((numfiles = odvr_filecount(dev, folder)) < 0){ 177 | fprintf(stderr, "Couldn't get file count on Folder %c: %s\n", 178 | odvr_foldername(dev,folder), odvr_error(dev)); 179 | continue; 180 | } 181 | 182 | printf("Folder %c (%d files):\n", odvr_foldername(dev, folder), numfiles); 183 | printf("Slot File Length Date Quality\n"); 184 | for(i = 1; i <= numfiles; i++){ 185 | if(odvr_filestat(dev, folder, i, &stat) < 0){ 186 | fprintf(stderr, "Error getting file stat: %s\n", odvr_error(dev)); 187 | return; 188 | } 189 | sec = odvr_length(&stat) + 0.5; 190 | hour = sec / 3600; 191 | sec = sec % 3600; 192 | min = sec / 60; 193 | sec = sec % 60; 194 | printf("%-4d %-6d %02d:%02d:%02d %2d/%02d/%4d %2d:%02d:%02d %s\n", 195 | stat.slot, stat.id, hour, min, sec, stat.month, 196 | stat.day, stat.year + 2000, 197 | stat.hour, stat.min, stat.sec, 198 | odvr_quality_name(stat.quality)); 199 | } 200 | } 201 | } 202 | 203 | struct tm odvr_time(filestat_t stat) { 204 | struct tm result; 205 | 206 | result.tm_year = stat.year + 2000 - 1900; 207 | result.tm_mon = stat.month - 1; 208 | result.tm_mday = stat.day; 209 | result.tm_hour = stat.hour; 210 | result.tm_min = stat.min; 211 | result.tm_sec = stat.sec; 212 | result.tm_isdst = -1; 213 | 214 | return result; 215 | } 216 | 217 | void set_file_times(char fn[128], filestat_t stat) { 218 | struct utimbuf new_times; 219 | struct tm timeinfo; 220 | 221 | timeinfo = odvr_time(stat); 222 | 223 | new_times.actime = mktime(&timeinfo); 224 | new_times.modtime = mktime(&timeinfo); 225 | utime(fn, &new_times); 226 | } 227 | 228 | void odvr_filetime(char *result, filestat_t stat) { 229 | struct tm timeinfo; 230 | 231 | timeinfo = odvr_time(stat); 232 | 233 | sprintf(result, "%04d%02d%02d-%02d%02d%02d", timeinfo.tm_year + 1900, 234 | timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, 235 | timeinfo.tm_min, timeinfo.tm_sec); 236 | } 237 | 238 | void download_folder(odvr dev, uint8_t folder, int opt_time){ 239 | filestat_t instat; 240 | struct stat outstat; 241 | FILE *out; 242 | uint8_t slot; 243 | char fn[128]; 244 | char ftime[32]; 245 | 246 | for(slot = 1; slot <= odvr_filecount(dev, folder); slot++){ 247 | if(odvr_filestat(dev, folder, slot, &instat) < 0){ 248 | fprintf(stderr, "Error getting file instat: %s\n", odvr_error(dev)); 249 | continue; 250 | } 251 | 252 | if (opt_time > 0) { 253 | odvr_filetime(ftime, instat); 254 | sprintf(fn, "%c-%s.wav", odvr_foldername(dev, folder), ftime); 255 | } else { 256 | sprintf(fn, "D%c_%04d_%02d.wav", odvr_foldername(dev, folder), instat.id, 257 | slot); 258 | } 259 | 260 | if(stat(fn, &outstat) == 0){ 261 | fprintf(stderr, "\"%s\" already exists. Skipping this file.\n", fn); 262 | continue; 263 | } 264 | 265 | if((out = fopen(fn, "w+")) == NULL){ 266 | fprintf(stderr, "Error opening \"%s\" for writing: %s\n", 267 | fn, strerror(errno)); 268 | continue; 269 | } 270 | 271 | printf("Downloading \"%s\"...\n", fn); 272 | if(odvr_save_wav(dev, folder, slot, fileno(out))) 273 | fprintf(stderr, "Error downloading \"%s\": %s\n", fn, odvr_error(dev)); 274 | 275 | fclose(out); 276 | set_file_times(fn, instat); 277 | } 278 | } 279 | 280 | void download_folder_raw(odvr dev, uint8_t folder, int opt_time){ 281 | filestat_t instat; 282 | struct stat outstat; 283 | FILE *out; 284 | uint8_t slot; 285 | char fn[128]; 286 | char ftime[32]; 287 | 288 | for(slot = 1; slot <= odvr_filecount(dev, folder); slot++){ 289 | if(odvr_filestat(dev, folder, slot, &instat) < 0){ 290 | fprintf(stderr, "Error getting file instat: %s\n", odvr_error(dev)); 291 | continue; 292 | } 293 | 294 | if (opt_time > 0) { 295 | odvr_filetime(ftime, instat); 296 | sprintf(fn, "%c-%s.raw", odvr_foldername(dev, folder), ftime); 297 | } else { 298 | sprintf(fn, "D%c_%04d.raw", odvr_foldername(dev, folder), instat.id); 299 | } 300 | 301 | if(stat(fn, &outstat) == 0){ 302 | fprintf(stderr, "\"%s\" already exists. Skipping this file.\n", fn); 303 | continue; 304 | } 305 | 306 | if((out = fopen(fn, "w+")) == NULL){ 307 | fprintf(stderr, "Error opening \"%s\" for writing: %s\n", 308 | fn, strerror(errno)); 309 | continue; 310 | } 311 | 312 | printf("Downloading \"%s\"...\n", fn); 313 | if(odvr_save_raw(dev, folder, slot, fileno(out))) 314 | fprintf(stderr, "Error downloading \"%s\": %s\n", fn, odvr_error(dev)); 315 | 316 | fclose(out); 317 | set_file_times(fn, instat); 318 | } 319 | } 320 | 321 | 322 | int user_confirmed(const char *action){ 323 | char answer[64]; 324 | int i; 325 | 326 | if(action) 327 | printf("Going to %s.\n", action); 328 | printf("Are you sure (yes/no)? "); 329 | if(!fgets(answer, 64, stdin)){ 330 | return 0; /* no input */ 331 | } 332 | 333 | /* lowercase and check for "yes" or "y" */ 334 | for(i = 0; i < 64 && answer[i]; i++) 335 | answer[i] = tolower(answer[i]); 336 | if(!strncmp("yes\n", answer, 64) || 337 | !strncmp("y\n", answer, 64)){ 338 | return 1; 339 | } 340 | 341 | return 0; 342 | } 343 | 344 | void print_usage(void){ 345 | fprintf(stderr, 346 | "Usage: odvr [options]\n" 347 | "-= Options =-\n" 348 | " -h : This help.\n" 349 | " -v : Print version.\n" 350 | " -d : Download all files in .\n" 351 | " -e : Download everything.\n" 352 | " -l : List all files.\n" 353 | " -x : Delete all recordings in .\n" 354 | " -c : Delete all recordings.\n" 355 | " -y : \"yes\" to all yes/no questions.\n" 356 | " -r : Reset the DVR. This may fix some sync issues.\n" 357 | " -D : Enable debug tracing.\n" 358 | " -E : Download everything in RAW format.\n" 359 | " -t : Format downloaded wav files names as F-YYYYmmdd-HHMMss.\n" 360 | ); 361 | } 362 | -------------------------------------------------------------------------------- /odvr_gui.c: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include "odvr_date.h" 22 | #include "odvr_cfg.h" 23 | 24 | #include "odvr_gui.h" 25 | 26 | void gui_err(const char *errorString, const char *errorDetail) 27 | { 28 | GtkWidget *dialog; 29 | 30 | dialog = gtk_message_dialog_new(NULL, 31 | GTK_DIALOG_DESTROY_WITH_PARENT, 32 | GTK_MESSAGE_ERROR, 33 | GTK_BUTTONS_OK, 34 | "%s\n%s", errorString, errorDetail); 35 | 36 | gtk_dialog_run (GTK_DIALOG (dialog)); 37 | gtk_widget_destroy (dialog); 38 | 39 | } 40 | 41 | gint yes_no_acknowledge(gchar *question) 42 | { 43 | GtkWidget *messageDialog; 44 | gint response; 45 | 46 | messageDialog = gtk_message_dialog_new( 47 | NULL, 48 | GTK_DIALOG_DESTROY_WITH_PARENT, 49 | GTK_MESSAGE_QUESTION, 50 | GTK_BUTTONS_NONE, 51 | question); 52 | gtk_dialog_add_buttons(GTK_DIALOG(messageDialog), 53 | GTK_STOCK_YES, GTK_RESPONSE_YES, 54 | NULL); 55 | gtk_dialog_add_buttons(GTK_DIALOG(messageDialog), 56 | GTK_STOCK_NO, GTK_RESPONSE_NO, 57 | NULL); 58 | gtk_dialog_add_buttons(GTK_DIALOG(messageDialog), 59 | "Yes to all", ODVR_RESPONSE_YES_ALL, 60 | NULL); 61 | gtk_dialog_add_buttons(GTK_DIALOG(messageDialog), 62 | "No to all", ODVR_RESPONSE_NO_ALL, 63 | NULL); 64 | gtk_dialog_add_buttons(GTK_DIALOG(messageDialog), 65 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 66 | NULL); 67 | 68 | response = gtk_dialog_run (GTK_DIALOG (messageDialog)); 69 | gtk_widget_destroy (messageDialog); 70 | 71 | return response; 72 | } 73 | 74 | gint acknowledge_ok_cancel(gchar *question) 75 | { 76 | GtkWidget *messageDialog; 77 | gint response; 78 | 79 | messageDialog = gtk_message_dialog_new( 80 | NULL, 81 | GTK_DIALOG_DESTROY_WITH_PARENT, 82 | GTK_MESSAGE_QUESTION, 83 | GTK_BUTTONS_NONE, 84 | question); 85 | gtk_dialog_add_buttons(GTK_DIALOG(messageDialog), 86 | GTK_STOCK_OK, GTK_RESPONSE_OK, 87 | NULL); 88 | gtk_dialog_add_buttons(GTK_DIALOG(messageDialog), 89 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 90 | NULL); 91 | 92 | response = gtk_dialog_run (GTK_DIALOG (messageDialog)); 93 | gtk_widget_destroy (messageDialog); 94 | 95 | return response; 96 | } 97 | 98 | gint acknowledge(gchar *question) 99 | { 100 | GtkWidget *messageDialog; 101 | gint response; 102 | 103 | messageDialog = gtk_message_dialog_new( 104 | NULL, 105 | GTK_DIALOG_DESTROY_WITH_PARENT, 106 | GTK_MESSAGE_QUESTION, 107 | GTK_BUTTONS_OK, 108 | question); 109 | 110 | response = gtk_dialog_run (GTK_DIALOG (messageDialog)); 111 | gtk_widget_destroy (messageDialog); 112 | 113 | return response; 114 | } 115 | 116 | /* Exit the program without confirmation or saving configuration */ 117 | gboolean delete_event( GtkWidget *widget) 118 | { 119 | gtk_main_quit (); 120 | 121 | return FALSE; 122 | } 123 | 124 | /* Exit the program without after saving configuration */ 125 | gboolean quit_event( GtkWidget *widget, gpointer data ) 126 | { 127 | odvrData_t *odvrData = (odvrData_t *)(data); 128 | odvr_config_t *cfg = odvrData->cfg; 129 | char *destinationDir; 130 | gint width, height; 131 | 132 | destinationDir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(odvrData->ddButton)); 133 | if (strcmp(odvr_cfg_get_destination_dir(cfg), destinationDir)) 134 | odvr_cfg_set_destination_dir(cfg, destinationDir); 135 | 136 | gtk_window_get_size(GTK_WINDOW(odvrData->window), &width, &height); 137 | odvr_cfg_set_window_size(odvrData->cfg, width, height); 138 | 139 | 140 | if (odvr_cfg_get_dirty(cfg)) 141 | odvr_cfg_save_config(cfg); 142 | 143 | gtk_main_quit (); 144 | 145 | return FALSE; 146 | } 147 | 148 | static void length_func (GtkTreeViewColumn *col, 149 | GtkCellRenderer *renderer, 150 | GtkTreeModel *model, 151 | GtkTreeIter *iter, 152 | gpointer user_data) 153 | { 154 | float length; 155 | gchar buf[64]; 156 | int sec, min, hour; 157 | 158 | GtkTreeIter parent; 159 | 160 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) { 161 | g_object_set(renderer, "text", NULL, NULL); 162 | return; 163 | } 164 | 165 | gtk_tree_model_get(model, iter, COL_LENGTH, &length, -1); 166 | sec = length + 0.5; 167 | hour = sec / 3600; 168 | sec = sec % 3600; 169 | min = sec / 60; 170 | sec = sec % 60; 171 | g_snprintf(buf, sizeof(buf), "%02d:%02d:%02d", hour, min, sec); 172 | g_object_set(renderer, "text", buf, NULL); 173 | } 174 | 175 | static void folder_func (GtkTreeViewColumn *col, 176 | GtkCellRenderer *renderer, 177 | GtkTreeModel *model, 178 | GtkTreeIter *iter, 179 | gpointer user_data) 180 | { 181 | GdkPixbuf *folderIcon; 182 | GtkTreePath *path; 183 | GtkTreeIter parent; 184 | 185 | path = gtk_tree_model_get_path(model, iter); 186 | 187 | gtk_tree_model_get(model, iter, COL_FOLDER, &folderIcon, -1); 188 | 189 | 190 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) { 191 | g_object_set(renderer, "pixbuf", folderIcon, NULL); 192 | } 193 | else 194 | g_object_set(renderer, "pixbuf", NULL, NULL); 195 | 196 | 197 | gtk_tree_path_free(path); 198 | 199 | return; 200 | } 201 | 202 | static void slot_func (GtkTreeViewColumn *col, 203 | GtkCellRenderer *renderer, 204 | GtkTreeModel *model, 205 | GtkTreeIter *iter, 206 | gpointer user_data) 207 | { 208 | int slot; 209 | gchar buf[64]; 210 | GtkTreeIter parent; 211 | 212 | gtk_tree_model_get(model, iter, COL_SLOT, &slot, -1); 213 | 214 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) { 215 | g_object_set(renderer, "text", NULL, NULL); 216 | return; 217 | } 218 | 219 | g_snprintf(buf, sizeof(buf), "%d", slot); 220 | g_object_set(renderer, "text", buf, NULL); 221 | } 222 | 223 | static void file_func (GtkTreeViewColumn *col, 224 | GtkCellRenderer *renderer, 225 | GtkTreeModel *model, 226 | GtkTreeIter *iter, 227 | gpointer user_data) 228 | { 229 | int id; 230 | gchar buf[64]; 231 | GtkTreeIter parent; 232 | 233 | gtk_tree_model_get(model, iter, COL_ID, &id, -1); 234 | 235 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) { 236 | g_snprintf(buf, sizeof(buf),"(%d)", 237 | gtk_tree_model_iter_n_children(model, iter)); 238 | g_object_set(renderer, "text", buf, NULL); 239 | return; 240 | } 241 | 242 | g_snprintf(buf, sizeof(buf), "%d", id); 243 | g_object_set(renderer, "text", buf, NULL); 244 | } 245 | 246 | static void date_func (GtkTreeViewColumn *col, 247 | GtkCellRenderer *renderer, 248 | GtkTreeModel *model, 249 | GtkTreeIter *iter, 250 | gpointer user_data) 251 | { 252 | odvrDate_t *fileDate; 253 | gchar *buf; 254 | GtkTreeIter parent; 255 | 256 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) { 257 | g_object_set(renderer, "text", NULL, NULL); 258 | return; 259 | } 260 | 261 | gtk_tree_model_get(model, iter, COL_DATE, &fileDate, -1); 262 | 263 | buf = dateString(fileDate); 264 | g_object_set(renderer, "text",buf, NULL); 265 | } 266 | 267 | static void quality_func (GtkTreeViewColumn *col, 268 | GtkCellRenderer *renderer, 269 | GtkTreeModel *model, 270 | GtkTreeIter *iter, 271 | gpointer user_data) 272 | { 273 | int quality; 274 | GtkTreeIter parent; 275 | 276 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) { 277 | g_object_set(renderer, "text", NULL, NULL); 278 | return; 279 | } 280 | gtk_tree_model_get(model, iter, COL_QUALITY, &quality, -1); 281 | g_object_set(renderer, "text", odvr_quality_name(quality), NULL); 282 | } 283 | 284 | 285 | GtkWidget *create_view (odvrData_t *odvrData) 286 | { 287 | GtkTreeViewColumn *col; 288 | GtkCellRenderer *renderer; 289 | GtkWidget *view; 290 | 291 | view = gtk_tree_view_new(); 292 | 293 | /* Folder icon */ 294 | col = gtk_tree_view_column_new(); 295 | gtk_tree_view_column_set_resizable(col, TRUE); 296 | gtk_tree_view_column_set_reorderable(col, FALSE); 297 | gtk_tree_view_column_set_title(col, "Folder/Slot"); 298 | gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); 299 | renderer = gtk_cell_renderer_pixbuf_new(); 300 | gtk_tree_view_column_pack_start(col, renderer, TRUE); 301 | gtk_tree_view_column_set_cell_data_func(col, renderer, folder_func, 302 | NULL, NULL); 303 | renderer = gtk_cell_renderer_text_new(); 304 | gtk_tree_view_column_pack_start(col, renderer, TRUE); 305 | gtk_tree_view_column_set_cell_data_func(col, renderer, slot_func, 306 | NULL, NULL); 307 | 308 | col = gtk_tree_view_column_new(); 309 | gtk_tree_view_column_set_resizable(col, TRUE); 310 | gtk_tree_view_column_set_reorderable(col, FALSE); 311 | gtk_tree_view_column_set_title(col, "File"); 312 | gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); 313 | renderer = gtk_cell_renderer_text_new(); 314 | gtk_tree_view_column_pack_start(col, renderer, TRUE); 315 | gtk_tree_view_column_set_cell_data_func(col, renderer, file_func, 316 | NULL, NULL); 317 | 318 | col = gtk_tree_view_column_new(); 319 | gtk_tree_view_column_set_resizable (col, TRUE); 320 | gtk_tree_view_column_set_reorderable(col, TRUE); 321 | gtk_tree_view_column_set_title(col, "Length"); 322 | gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); 323 | renderer = gtk_cell_renderer_text_new(); 324 | gtk_tree_view_column_pack_start(col, renderer, TRUE); 325 | gtk_tree_view_column_set_cell_data_func(col, renderer, length_func, 326 | NULL, NULL); 327 | 328 | if (odvr_cfg_get_show_wav_size(odvrData->cfg)) { 329 | /* Display wav file size */ 330 | col = gtk_tree_view_column_new(); 331 | gtk_tree_view_column_set_resizable (col, TRUE); 332 | gtk_tree_view_column_set_reorderable(col, TRUE); 333 | gtk_tree_view_column_set_title(col, "Size"); 334 | gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); 335 | renderer = gtk_cell_renderer_text_new(); 336 | gtk_tree_view_column_pack_start(col, renderer, TRUE); 337 | gtk_tree_view_column_add_attribute(col, renderer, "text", COL_SIZE); 338 | } 339 | 340 | col = gtk_tree_view_column_new(); 341 | gtk_tree_view_column_set_resizable (col, TRUE); 342 | gtk_tree_view_column_set_reorderable(col, TRUE); 343 | gtk_tree_view_column_set_title(col, "Date"); 344 | gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); 345 | renderer = gtk_cell_renderer_text_new(); 346 | gtk_tree_view_column_pack_start(col, renderer, TRUE); 347 | gtk_tree_view_column_set_cell_data_func(col, renderer, date_func, 348 | NULL, NULL); 349 | 350 | col = gtk_tree_view_column_new(); 351 | gtk_tree_view_column_set_resizable (col, TRUE); 352 | gtk_tree_view_column_set_reorderable(col, TRUE); 353 | gtk_tree_view_column_set_title(col, "Quality"); 354 | gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); 355 | renderer = gtk_cell_renderer_text_new(); 356 | gtk_tree_view_column_pack_start(col, renderer, TRUE); 357 | gtk_tree_view_column_set_cell_data_func(col, renderer, quality_func, 358 | NULL, NULL); 359 | 360 | gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(view), TRUE); 361 | 362 | return view; 363 | } 364 | -------------------------------------------------------------------------------- /olympusdvr.c: -------------------------------------------------------------------------------- 1 | /* olympusdvr.c: Olympus DVR library 2 | * 3 | * Copyright (C) 2007-2008 Tristan Willy 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "olympusdvr.h" 27 | 28 | #define FOLDER_IDX(x) ((x) - 1) 29 | #define SLOT_IDX(x) ((x) - 1) 30 | #define IDX_FOLDER(x) ((x) + 1) 31 | #define IDX_SLOT(x) ((x) + 1) 32 | #define MAX_MODEL_LENGTH 32 33 | #define MAX_ERROR_LENGTH 256 34 | #define QUIRK_RESET_ON_CLOSE 1 35 | #define QUIRK_HAS_S_FOLDER 2 36 | 37 | /* IS_MODEL(odvr handle, model string): true if h->model eq model string */ 38 | #define IS_MODEL(h, m) \ 39 | (!strncmp(h->model, (m), strlen((m)) + 1)) 40 | 41 | struct odvr { 42 | struct usb_device *usb_dev; 43 | struct usb_dev_handle *usb_handle; 44 | char model[MAX_MODEL_LENGTH]; 45 | int filecount[ODVR_NUM_FOLDERS]; 46 | filestat_t *metadata[ODVR_NUM_FOLDERS][100]; 47 | char error[MAX_ERROR_LENGTH]; 48 | int quirks; 49 | int trace; 50 | int num_folders; 51 | }; 52 | 53 | char global_error[MAX_ERROR_LENGTH]; 54 | 55 | static int set_error(odvr h, char *format, ...){ 56 | va_list args; 57 | char *dst; 58 | int retc; 59 | 60 | if(h == NULL){ 61 | dst = global_error; 62 | } else { 63 | dst = h->error; 64 | } 65 | 66 | va_start(args, format); 67 | retc = vsnprintf(dst, MAX_ERROR_LENGTH, format, args); 68 | va_end(args); 69 | 70 | return retc; 71 | } 72 | 73 | static void free_metadata(odvr h){ 74 | int f, i; 75 | 76 | for(f = 0; f < ODVR_NUM_FOLDERS; f++){ 77 | for(i = 0; i < 99 && h->metadata[f][i]; i++){ 78 | free(h->metadata[f][i]); 79 | h->metadata[f][i] = NULL; 80 | } 81 | } 82 | } 83 | 84 | /* return usb device for ovdr devices */ 85 | static struct usb_device *find_odvr(void){ 86 | struct usb_bus *b; 87 | struct usb_device *d; 88 | 89 | for(b = usb_get_busses(); b; b = b->next){ 90 | for(d = b->devices; d; d = d->next){ 91 | if(d->descriptor.idVendor == 0x07b4 && 92 | d->descriptor.idProduct == 0x020d) 93 | return d; 94 | } 95 | } 96 | 97 | return NULL; 98 | } 99 | 100 | void trace_hexdump(const char *label, void *data, int len){ 101 | int i; 102 | unsigned char *d_idx = (unsigned char *)data; 103 | 104 | fprintf(stderr, "Trace: %s (%d bytes):", label, len); 105 | for(i = 0; i < len; i++){ 106 | if(!(i % 16)) 107 | fprintf(stderr, "\n"); 108 | fprintf(stderr, "%02X", *d_idx++); 109 | if(!((i+1) % 2)) 110 | fprintf(stderr, " "); 111 | } 112 | fprintf(stderr, "\n"); 113 | } 114 | 115 | static int try_usb_bulk_write(odvr h, int endpoint, void *packet, int len){ 116 | int retc; 117 | 118 | if(h->trace){ 119 | trace_hexdump("Bulk Write", packet, len); 120 | } 121 | 122 | retc = usb_bulk_write(h->usb_handle, endpoint, packet, len, 1000); 123 | if(retc == -ETIMEDOUT){ 124 | set_error(h, "timed out writing to EP%d (device out of sync?)", endpoint); 125 | return -5; 126 | } 127 | if(retc < 0){ 128 | set_error(h, "bulk write of %d bytes to EP%d failed with code %d: %s", 129 | len, endpoint, retc, usb_strerror()); 130 | return -10; 131 | } 132 | if(retc != len){ 133 | set_error(h, "only wrote %d bytes of %d requested to bulk EP%d", 134 | retc, len, endpoint); 135 | return -20; 136 | } 137 | 138 | return retc; 139 | } 140 | 141 | static int try_usb_bulk_read(odvr h, int endpoint, void *packet, int len){ 142 | int retc; 143 | 144 | retc = usb_bulk_read(h->usb_handle, endpoint, packet, len, 1000); 145 | if(retc == -ETIMEDOUT){ 146 | set_error(h, "timed out reading from EP%d (device out of sync?)", 147 | endpoint); 148 | return -5; 149 | } 150 | if(retc < 0){ 151 | set_error(h, "bulk read of %d bytes to EP%d failed with code %d: %s", 152 | len, endpoint, retc, usb_strerror()); 153 | return -10; 154 | } 155 | if(retc != len){ 156 | set_error(h, "only read %d bytes of %d requested to bulk EP%d", 157 | retc, len, endpoint); 158 | return -20; 159 | } 160 | 161 | if(h->trace){ 162 | trace_hexdump("Bulk Read", packet, len); 163 | } 164 | 165 | return retc; 166 | } 167 | 168 | /* Change state and set flags depending on device. 169 | * Requires model name to already be known! */ 170 | static void detect_quirks(odvr h){ 171 | h->quirks = 0; 172 | 173 | /* These models have a fourth 'S' folder */ 174 | if(IS_MODEL(h, "VN-240PC") || 175 | IS_MODEL(h, "VN-480PC") || 176 | IS_MODEL(h, "VN-960PC")){ 177 | h->num_folders = 4; 178 | h->quirks |= QUIRK_HAS_S_FOLDER; 179 | } 180 | /* These models have a fourth 'D' folder */ 181 | if(IS_MODEL(h, "2100PC") || 182 | IS_MODEL(h, "3100PC") || 183 | IS_MODEL(h, "4100PC")){ 184 | h->num_folders = 4; 185 | } 186 | 187 | /* Somewhere in the Linux kernel, and not libusb, a SET_INTERFACE, 188 | * with correct parameters, is sent out to the device. This is *BAD* in our 189 | * situation since the VN-120PC, and possibly up to the VN-480PC has buggy 190 | * firmware. They will fail if you try to set the alternative interface. 191 | * 192 | * Our solution here is to reset the device on close. This will leave buggy 193 | * devices in a valid state. 194 | */ 195 | if(IS_MODEL(h, "VN-120PC") || 196 | IS_MODEL(h, "VN-240PC") || 197 | IS_MODEL(h, "VN-480PC")){ 198 | h->quirks |= QUIRK_RESET_ON_CLOSE; 199 | } 200 | } 201 | 202 | /* open odvr 203 | * fixme: string errors */ 204 | odvr odvr_open(int flags){ 205 | struct odvr *h; 206 | int i, errflg; 207 | char *errors[] = 208 | { NULL, 209 | "couldn't locate USB busses", 210 | "couldn't locate USB devices", 211 | "couldn't locate device (unknown product ID?)", 212 | "couldn't open device", 213 | "couldn't set configuration", 214 | "couldn't claim interface", 215 | "couldn't set alternate interface" }; 216 | 217 | if((h = malloc(sizeof(struct odvr))) == NULL) 218 | return NULL; 219 | 220 | errflg = 0; 221 | usb_init(); 222 | if(!errflg && usb_find_busses() < 0) 223 | errflg = 1; 224 | if(!errflg && usb_find_devices() < 0) 225 | errflg = 2; 226 | if(!errflg && (h->usb_dev = find_odvr()) == NULL) 227 | errflg = 3; 228 | if(!errflg && (h->usb_handle = usb_open(h->usb_dev)) == NULL) 229 | errflg = 4; 230 | if(errflg){ 231 | free(h); 232 | set_error(NULL, errors[errflg]); 233 | return NULL; 234 | } 235 | 236 | if(!errflg && usb_claim_interface(h->usb_handle, 0) < 0) 237 | errflg = 6; 238 | if(errflg){ 239 | usb_close(h->usb_handle); 240 | free(h); 241 | set_error(NULL, errors[errflg]); 242 | return NULL; 243 | } 244 | 245 | /* clear metadata and file counts */ 246 | for(i = 0; i < ODVR_NUM_FOLDERS; i++){ 247 | h->metadata[i][0] = NULL; 248 | h->filecount[i] = -1; 249 | } 250 | 251 | if(flags & ODVR_TRACE){ 252 | odvr_enable_trace(h); 253 | } else { 254 | odvr_disable_trace(h); 255 | } 256 | 257 | /* Set the number of folders. Quirks may change this. */ 258 | h->num_folders = 3; 259 | 260 | /* get the model */ 261 | h->model[0] = 0; 262 | /* HACK HACK HACK: Issue #15 263 | * Querying the model on a VN-4100PC (and other new models?) can leave the 264 | * device in a state where the model name isn't returned on even # runs. 265 | * Workaround: try to get the model name twice. */ 266 | if(odvr_model(h) == NULL) 267 | odvr_model(h); 268 | 269 | /* determine quirks */ 270 | detect_quirks(h); 271 | 272 | return h; 273 | } 274 | 275 | void odvr_close(odvr h){ 276 | if(h->quirks & QUIRK_RESET_ON_CLOSE) 277 | usb_reset(h->usb_handle); 278 | 279 | usb_close(h->usb_handle); 280 | free_metadata(h); 281 | free(h); 282 | } 283 | 284 | const char *odvr_error(odvr h){ 285 | if(h) 286 | return h->error; 287 | return global_error; 288 | } 289 | 290 | int odvr_enable_trace(odvr h){ 291 | h->trace = 1; 292 | return 0; 293 | } 294 | 295 | int odvr_disable_trace(odvr h){ 296 | h->trace = 0; 297 | return 0; 298 | } 299 | 300 | /* reset the USB connection and reconnect 301 | * fixme: this shouldn't even be needed */ 302 | int odvr_reset(odvr h, int flags){ 303 | int i; 304 | odvr new; 305 | 306 | usb_reset(h->usb_handle); 307 | 308 | for(i = 0; i < 20 && (new = odvr_open(flags)) == NULL; i++){ 309 | usleep(500000); 310 | } 311 | 312 | if(new == NULL) 313 | return -1; 314 | 315 | /* copy back new handle 316 | * note: this can be dangerous if odvr changes! */ 317 | memcpy(h, new, sizeof(struct odvr)); 318 | free(new); 319 | 320 | return 0; 321 | } 322 | 323 | static int valid_status(odvr h, const uint8_t *packet){ 324 | if(! (packet[0] == 0x06 || 325 | packet[0] == 0xff || 326 | packet[0] == 0x00)){ 327 | set_error(h, "unknown DVR status code"); 328 | return 0; 329 | } 330 | 331 | return 1; 332 | } 333 | 334 | static int cmd_check(odvr h){ 335 | uint8_t packet[8]; 336 | int retc; 337 | 338 | /* fixme: proper interrupt status handling. ex: a thread to handle this */ 339 | packet[0] = 0xff; 340 | while(packet[0] != 0x00 && 341 | (retc = 342 | usb_interrupt_read(h->usb_handle, 3, (void *)packet, 8, 75)) > 0){ 343 | if(h->trace){ 344 | trace_hexdump("Interrupt Read", packet, 8); 345 | } 346 | 347 | if(retc != 8){ 348 | set_error(h, "strange status code length"); 349 | return -1; 350 | } 351 | if(!valid_status(h, packet)) 352 | return -1; 353 | } 354 | 355 | if(packet[0] == 0x00 || 356 | retc == -ETIMEDOUT){ 357 | /* no status left to pickup */ 358 | return 0; 359 | } 360 | 361 | if(retc < 0){ 362 | set_error(h, "failed to read DVR status code (%d): %s", 363 | retc, usb_strerror()); 364 | return -10; 365 | } 366 | 367 | return -100; 368 | } 369 | 370 | static int cmd_check_blocking(odvr h){ 371 | uint8_t packet[8]; 372 | int retc; 373 | 374 | if((retc = 375 | usb_interrupt_read(h->usb_handle, 3, (void *)packet, 8, 0)) < 0){ 376 | set_error(h, "failed to read status code"); 377 | return -10; 378 | } 379 | if(h->trace) 380 | trace_hexdump("Interrupt Read", packet, 8); 381 | 382 | if(!valid_status(h, packet)) 383 | return -20; 384 | 385 | return 0; 386 | } 387 | 388 | static int prepare(odvr h){ 389 | uint8_t packet[64]; 390 | 391 | memset(packet, 0, 64); 392 | packet[0] = 0xf0; 393 | 394 | if(try_usb_bulk_write(h, 2, packet, 64) < 0) 395 | return -10; 396 | 397 | return 0; 398 | } 399 | 400 | /* note: we cannot rely on h->quirks here */ 401 | const char *odvr_model(odvr h){ 402 | uint8_t packet[64]; 403 | char *loc; 404 | 405 | if(h->model[0]){ 406 | /* model already queried */ 407 | return h->model; 408 | } 409 | 410 | prepare(h); 411 | 412 | memset(packet, 0, 64); 413 | packet[0] = 0xb2; 414 | 415 | if(try_usb_bulk_write(h, 2, packet, 64) < 0) 416 | return NULL; 417 | 418 | if(try_usb_bulk_read(h, 1, packet, 64) < 0) 419 | return NULL; 420 | 421 | /* null-terminate and copy name */ 422 | packet[63] = 0; 423 | if((loc = strstr((char *)packet, "PC")) != NULL){ 424 | loc[2] = 0; 425 | strncpy(h->model, (char *)packet, MAX_MODEL_LENGTH); 426 | } else { 427 | snprintf(h->model, MAX_MODEL_LENGTH, "unknown"); 428 | } 429 | 430 | if(cmd_check(h)) 431 | return NULL; 432 | 433 | return h->model; 434 | } 435 | 436 | int odvr_filecount(odvr h, uint8_t folder){ 437 | uint8_t packet[64]; 438 | int num, fidx; 439 | 440 | if(folder < 1 || folder > odvr_foldercount(h)) 441 | return -5; 442 | fidx = FOLDER_IDX(folder); 443 | 444 | /* cache hit? */ 445 | if(h->filecount[fidx] >= 0) 446 | return h->filecount[fidx]; 447 | 448 | prepare(h); 449 | 450 | memset(packet, 0, 64); 451 | packet[0] = 0x47; 452 | packet[1] = folder; 453 | 454 | if(try_usb_bulk_write(h, 2, packet, 64) < 0) 455 | return -10; 456 | 457 | if(try_usb_bulk_read(h, 1, packet, 64) < 0) 458 | return -20; 459 | 460 | num = packet[0]; 461 | 462 | if(cmd_check(h)) 463 | return -30; 464 | 465 | h->filecount[fidx] = num; 466 | return num; 467 | } 468 | 469 | int odvr_foldercount(odvr h){ 470 | return h->num_folders; 471 | } 472 | 473 | uint8_t odvr_foldercode(odvr h, const char name){ 474 | if(toupper(name) == 'S') 475 | return odvr_foldercount(h); 476 | 477 | return toupper(name) - 'A' + 1; 478 | } 479 | 480 | char odvr_foldername(odvr h, const uint8_t folder){ 481 | if(folder == odvr_foldercount(h) && 482 | h->quirks & QUIRK_HAS_S_FOLDER) 483 | return 'S'; 484 | 485 | return folder - 1 + 'A'; 486 | } 487 | 488 | static int get_folder_metadata(odvr h, uint8_t folder){ 489 | uint8_t packet[64]; 490 | int i, num_files, fidx, sidx; 491 | 492 | num_files = odvr_filecount(h, folder); 493 | if(num_files < 0) 494 | return -5; 495 | if(num_files == 0) 496 | return 0; 497 | 498 | /* pre-allocate metadata */ 499 | fidx = FOLDER_IDX(folder); 500 | for(sidx = 0; sidx < num_files; sidx++){ 501 | if((h->metadata[fidx][sidx] = malloc(sizeof(filestat_t))) == NULL) 502 | break; 503 | } 504 | if(sidx < num_files){ 505 | /* allocation failed */ 506 | while(--sidx >= 0){ 507 | free(h->metadata[fidx][sidx]); 508 | h->metadata[fidx][sidx] = NULL; 509 | } 510 | return -20; 511 | } 512 | h->metadata[fidx][sidx] = NULL; /* mark end */ 513 | 514 | for(sidx = 0; sidx < num_files; sidx++){ 515 | memset(packet, 0, 64); 516 | packet[0] = sidx == 0 ? 0x40 : 0x41; 517 | packet[1] = folder; 518 | 519 | if(try_usb_bulk_write(h, 2, packet, 64) < 0) 520 | return -30; 521 | 522 | /* read h->metadata */ 523 | if(try_usb_bulk_read(h, 1, packet, 64) < 0) 524 | return -37; 525 | 526 | /* stuff we know already */ 527 | h->metadata[fidx][sidx]->folder = IDX_FOLDER(fidx); 528 | h->metadata[fidx][sidx]->slot = IDX_SLOT(sidx); 529 | 530 | /* file ID */ 531 | h->metadata[fidx][sidx]->id = *((uint16_t *)(packet + 10)); 532 | 533 | /* timestamp */ 534 | h->metadata[fidx][sidx]->month = packet[1]; 535 | h->metadata[fidx][sidx]->day = packet[2]; 536 | h->metadata[fidx][sidx]->year = packet[0]; 537 | h->metadata[fidx][sidx]->hour = packet[3]; 538 | h->metadata[fidx][sidx]->min = packet[4]; 539 | h->metadata[fidx][sidx]->sec = packet[5]; 540 | 541 | /* Quality */ 542 | h->metadata[fidx][sidx]->quality = packet[8]; 543 | 544 | /* dump extra stuff on the floor */ 545 | for(i = 0; i < 5; i++){ 546 | if(try_usb_bulk_read(h, 1, packet, 64) < 0) 547 | return -40; 548 | } 549 | 550 | if(cmd_check(h)) 551 | return -45; 552 | 553 | /* last packet had our size */ 554 | h->metadata[fidx][sidx]->size = *((uint16_t *)(packet + 40)); 555 | } 556 | 557 | return 0; 558 | } 559 | 560 | /* I don't know what the units are to the size paramter are yet, so we fudge 561 | * this a little. */ 562 | float odvr_length(filestat_t *stat){ 563 | float divisor[] = { 15.74, 8.56, 23.73, 0, 0, 6.03, 2.03, 12.03, 24.16 }; 564 | 565 | if(stat->quality >= sizeof(divisor)/sizeof(divisor[0])) 566 | return 0; 567 | if(divisor[stat->quality] == 0) 568 | return 0; 569 | 570 | return stat->size / divisor[stat->quality]; 571 | } 572 | 573 | uint32_t odvr_wavfilesize(filestat_t *stat){ 574 | float multiplier[] = { 2038, 3748, 1351, 0, 0, 5320, 15805, 2667, 1328 }; 575 | 576 | if(stat->quality >= sizeof(multiplier)/sizeof(multiplier[0])) 577 | return 0; 578 | 579 | return stat->size * multiplier[stat->quality]; 580 | } 581 | 582 | int odvr_filestat(odvr h, uint8_t folder, uint8_t slot, filestat_t *stat){ 583 | int fidx, sidx, nfiles; 584 | 585 | if(folder < 1 || folder > odvr_foldercount(h)) 586 | return -10; 587 | 588 | fidx = FOLDER_IDX(folder); 589 | nfiles = odvr_filecount(h, folder); 590 | 591 | sidx = SLOT_IDX(slot); 592 | if(sidx < 0 || sidx > 99 || slot > nfiles) 593 | return -20; 594 | 595 | /* there are files, but we haven't got the folder metadata */ 596 | if(h->metadata[fidx][0] == NULL){ 597 | if(get_folder_metadata(h, folder)) 598 | return -30; 599 | } 600 | 601 | *stat = *h->metadata[fidx][sidx]; 602 | return 0; 603 | } 604 | 605 | const char *odvr_quality_name(uint8_t quality){ 606 | const char *quality_names[9] = 607 | { "SP", "LP", "HQ", NULL, NULL, "SP", "LP", "HQ", 608 | "XHQ" }; 609 | 610 | if(quality >= sizeof(quality_names)/sizeof(quality_names[0])){ 611 | snprintf(global_error, MAX_ERROR_LENGTH, "Unknown (%d)", quality); 612 | return global_error; 613 | } 614 | 615 | if(quality_names[quality] == NULL){ 616 | snprintf(global_error, MAX_ERROR_LENGTH, "Unknown (%d)", quality); 617 | return global_error; 618 | } 619 | 620 | return quality_names[quality]; 621 | } 622 | 623 | static void read_header(uint16_t *header, uint16_t *state, uint16_t *cur, uint16_t *last){ 624 | *state = header[0] & 0x03fff; 625 | 626 | *cur = ((header[1] & 0x0fff) << 2) | 627 | ((header[0] >> 14) & 0x03); 628 | 629 | *last = ((header[2] & 0x03ff) << 4) | 630 | ((header[1] >> 12) & 0x0f); 631 | } 632 | 633 | static uint8_t three_shift(uint16_t *ptr){ 634 | uint8_t out; 635 | uint16_t *a = ptr, *b = ptr+1, *c= ptr+2; 636 | 637 | out = *a & 0x07; 638 | *a >>= 3; 639 | *a |= (*b & 0x07) << 13; 640 | *b >>= 3; 641 | *b |= (*c & 0x07) << 13; 642 | *c >>= 3; 643 | 644 | return out; 645 | } 646 | 647 | static int16_t get_sample(uint8_t nib, uint16_t *state, uint16_t *cur, uint16_t *last){ 648 | int32_t pred; 649 | int32_t diff; 650 | 651 | /* calculate diff */ 652 | if(nib & 4){ 653 | diff = (*state & ~0x03) - ((nib - 3) << 1) * (*state & ~0x01); 654 | diff >>= 2; 655 | diff -= ((nib >> 1) & 1) & (*state & 1); 656 | } else { 657 | diff = (*state & ~0x03) + (nib << 1) * *state; 658 | diff >>= 2; 659 | } 660 | 661 | /* apply diff and predict */ 662 | pred = (*cur << 1) - *last + diff; 663 | if(pred > 16383) { 664 | pred = 16383; 665 | } else { 666 | if(pred < 0) { 667 | pred = 0; 668 | } 669 | } 670 | 671 | /* shift sample history */ 672 | *last = *cur; 673 | *cur = pred; 674 | 675 | /* update state */ 676 | if((nib & 0x03) <= 1) 677 | *state = *state + (*state >> 3) - (*state >> 2); 678 | else 679 | *state = *state + (*state >> 2) + (nib & 1) * (*state >> 1); 680 | if(*state < 2) 681 | *state = 2; 682 | if(*state > 16383) 683 | *state = 16383; 684 | 685 | /* return signed 16bit PCM sample from computed unsigned 14bit PCM sample */ 686 | return (*cur - 8192) * 4; 687 | } 688 | 689 | static int convert_pcm16(uint8_t *data, int len, uint16_t *out, 690 | uint8_t quality){ 691 | uint16_t state, cur, last, *stream; 692 | int i; 693 | 694 | read_header((void *) data, &state, &cur, &last); 695 | stream = (uint16_t *)data; /* note: adjusted in first itteration */ 696 | for(i = 0; i < (((len - 6) * 8) / 3); i++){ 697 | /* move to next 3-word chunk */ 698 | if(!(i % 16)) 699 | stream += 3; 700 | /* 3-bit shift and convert */ 701 | *out++ = get_sample(three_shift(stream), &state, &cur, &last); 702 | } 703 | 704 | return i; 705 | } 706 | 707 | /* returns maximum number of bytes in a block */ 708 | int odvr_block_size(odvr h){ 709 | return 2698; 710 | } 711 | 712 | /* returns 16bit signed PCM */ 713 | int odvr_read_block(odvr h, void *block, int maxbytes, uint8_t quality){ 714 | uint8_t chunk[576 + 6]; /* +6 for shift overrun */ 715 | int len, nsamples; 716 | 717 | if(maxbytes < odvr_block_size(h)){ 718 | set_error(h, "odvr_read_block() requires a larger block size"); 719 | return -5; 720 | } 721 | 722 | if(try_usb_bulk_read(h, 1, chunk, 576) < 0) 723 | return -10; 724 | 725 | len = chunk[0]; 726 | len <<= 8; 727 | len |= chunk[1]; 728 | len *= 2; 729 | 730 | if(len == 0){ 731 | /* ack the download */ 732 | if(cmd_check(h)) 733 | return -15; 734 | } 735 | 736 | /* VN-xx00PC XHQ seems to trim 8 bytes off the end */ 737 | if(quality == ODVR_QUALITY_XHQ) 738 | len -= 8; 739 | 740 | if(len > 0){ 741 | nsamples = convert_pcm16(chunk + 2, len, block, quality); 742 | if(nsamples < 0) 743 | return -20; 744 | } else { 745 | nsamples = 0; 746 | } 747 | 748 | return nsamples; 749 | } 750 | 751 | /* returns raw data */ 752 | int odvr_read_raw_block(odvr h, void *block, int maxbytes, uint8_t quality){ 753 | uint8_t chunk[576 + 6]; /* +6 for shift overrun */ 754 | int len, i, size; 755 | 756 | if(maxbytes < odvr_block_size(h)){ 757 | set_error(h, "odvr_read_block() requires a larger block size"); 758 | return -5; 759 | } 760 | 761 | if((size = try_usb_bulk_read(h, 1, chunk, 576)) < 0) 762 | return -10; 763 | 764 | len = chunk[0]; 765 | len <<= 8; 766 | len |= chunk[1]; 767 | len *= 2; 768 | 769 | if(len == 0){ 770 | /* ack the download */ 771 | if(cmd_check(h)) 772 | return -15; 773 | } 774 | 775 | for(i=0; i 0){ 872 | if(sf_write_short(out, block, ns) != ns){ 873 | set_error(h, "sndfile failed to write complete PCM block"); 874 | sf_close(out); 875 | return -40; 876 | } 877 | } 878 | 879 | /* error in odvr_read_block */ 880 | if(ns < 0) 881 | return ns; 882 | 883 | sf_close(out); 884 | return 0; 885 | } 886 | 887 | int odvr_save_raw(odvr h, uint8_t folder, uint8_t slot, int fd){ 888 | int16_t block[4096]; 889 | filestat_t stat; 890 | int ns; 891 | int8_t version; 892 | 893 | if(odvr_filestat(h, folder, slot, &stat)) 894 | return -10; 895 | 896 | version = 'o'; 897 | if(write(fd, &version, 1) != 1)return -40; 898 | version = 'd'; 899 | if(write(fd, &version, 1) != 1)return -40; 900 | version = 'v'; 901 | if(write(fd, &version, 1) != 1)return -40; 902 | version = 'r'; 903 | if(write(fd, &version, 1) != 1)return -40; 904 | version = 1; 905 | if(write(fd, &version, 1) != 1)return -40; 906 | 907 | /* filestat */ 908 | 909 | if(write(fd, &stat.folder, 1) != 1)return -40; 910 | if(write(fd, &stat.slot, 1) != 1)return -40; 911 | if(write(fd, &stat.id, 2) != 2)return -40; 912 | if(write(fd, &stat.size, 2) != 2)return -40; 913 | if(write(fd, &stat.month, 1) != 1)return -40; 914 | if(write(fd, &stat.day, 1) != 1)return -40; 915 | if(write(fd, &stat.year, 1) != 1)return -40; 916 | if(write(fd, &stat.hour, 1) != 1)return -40; 917 | if(write(fd, &stat.min, 1) != 1)return -40; 918 | if(write(fd, &stat.sec, 1) != 1)return -40; 919 | if(write(fd, &stat.quality, 1)!= 1)return -40; 920 | 921 | /* download */ 922 | if(odvr_open_file(h, folder, slot) < 0) 923 | return -35; 924 | while((ns = odvr_read_raw_block(h, block, 925 | 4096 * sizeof(uint16_t), stat.quality)) > 0){ 926 | if(write(fd, &ns, 2) != 2)return -40; 927 | if(write(fd, block, ns) != ns){ 928 | set_error(h, "failed to write data"); 929 | return -40; 930 | } 931 | } 932 | 933 | /* error in odvr_read_block */ 934 | if(ns < 0) 935 | return ns; 936 | 937 | return 0; 938 | } 939 | 940 | 941 | int odvr_clear_folder(odvr h, uint8_t folder){ 942 | uint8_t packet[64]; 943 | 944 | memset(packet, 0, 64); 945 | packet[0] = 0xa0; 946 | packet[1] = folder; 947 | packet[2] = 0xff; 948 | packet[3] = 0xff; 949 | 950 | if(prepare(h)) 951 | return -10; 952 | 953 | if(try_usb_bulk_write(h, 2, packet, 64) < 0) 954 | return -20; 955 | 956 | if(cmd_check_blocking(h)) 957 | return -30; 958 | 959 | return 0; 960 | } 961 | 962 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | -------------------------------------------------------------------------------- /gui.c: -------------------------------------------------------------------------------- 1 | /* olympus: Olympus DVR GUI 2 | * 3 | * Copyright (C) 2008 Conor McLoughlin 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "olympusdvr.h" 35 | #include "odvr_date.h" 36 | #include "odvr_gui.h" 37 | 38 | #define NAME "Olympus Digital Voice Recorder" 39 | #define VERSION "0.1.4.1-cml" 40 | 41 | enum { 42 | ACTION_SELECT_ALL = 0, 43 | ACTION_SELECT_NONE, 44 | ACTION_SELECT_BY_QUALITY, 45 | ACTION_SELECT_BY_DATE, 46 | ACTION_TRANSFER_FOLDER, 47 | ACTION_TRANSFER_SELECTED, 48 | ACTION_TRANSFER_ALL, 49 | ACTION_CLEAR, 50 | }; 51 | 52 | typedef struct actionData { 53 | char action; 54 | char folderId; 55 | gint response; 56 | gint quality_selected; /* Bitmap */ 57 | odvrDate_t start_date_selected, end_date_selected; 58 | gint total_bytes_to_transfer, total_bytes_transferred; 59 | gint bytes_to_transfer, bytes_transferred; 60 | gint files_to_transfer, files_transferred; 61 | odvrData_t *odvrData; 62 | gboolean abortTransfer; 63 | } actionData_t; 64 | 65 | char folderNames[] = {'A','B','C','D','S'}; 66 | extern GdkPixdata TBA, TBB, TBC, TBD, TBS, TBT; 67 | extern GdkPixdata FCA, FCB, FCC, FCD, FCS; 68 | GdkPixdata *transferFolderIcons[5]; 69 | GdkPixdata *folderIcons[5] ; 70 | 71 | 72 | void scan_device(odvrData_t *odvrData) 73 | { 74 | GtkTreeStore *file_store = odvrData->file_store; 75 | filestat_t stat; 76 | int file; 77 | GtkTreeIter iter, folder_iter; 78 | odvrDate_t *creationDate; 79 | int folder; 80 | odvr dev = odvrData->dev; 81 | odvrData->quality_used = 0; 82 | 83 | for(folder = ODVR_FOLDER_A; folder <= odvr_foldercount(dev); folder++) 84 | { 85 | 86 | if (odvr_foldername(dev, folder) == 'D') { 87 | gtk_widget_show (odvrData->buttonD); 88 | gtk_widget_show (odvrData->transferMenuD); 89 | gtk_widget_show (odvrData->clearMenuD); 90 | } 91 | if (odvr_foldername(dev, folder) == 'S') { 92 | gtk_widget_show (odvrData->buttonS); 93 | gtk_widget_show (odvrData->transferMenuS); 94 | gtk_widget_show (odvrData->clearMenuS); 95 | } 96 | 97 | creationDate = g_new0(odvrDate_t, 1); 98 | gtk_tree_store_append(file_store, &folder_iter, NULL); 99 | if ( (folder == odvr_foldercount(dev)) && 100 | (odvr_foldername(dev, folder) == 'S')) 101 | gtk_tree_store_set(file_store, &folder_iter, 102 | COL_FOLDER, odvrData->folderIcon[odvr_foldercount(dev)], 103 | -1); 104 | else 105 | gtk_tree_store_set(file_store, &folder_iter, 106 | COL_FOLDER, odvrData->folderIcon[folder-ODVR_FOLDER_A], 107 | -1); 108 | 109 | for (file = 1; file <= odvr_filecount(dev, folder); file++) 110 | { 111 | if(odvr_filestat(dev, folder, file, &stat) < 0) 112 | { 113 | gui_err("Error getting file stat", odvr_error(dev)); 114 | exit(1); 115 | } 116 | 117 | creationDate = g_new(odvrDate_t, 1); 118 | g_date_set_dmy(&creationDate->date, stat.day, stat.month, stat.year+2000); 119 | creationDate->hour = stat.hour; 120 | creationDate->min = stat.min; 121 | creationDate->sec = stat.sec; 122 | 123 | if (stat.quality < 8*sizeof(odvrData->quality_used)) 124 | odvrData->quality_used |= (1<files_transferred, actionData->files_to_transfer); 147 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(actionData->odvrData->overallProgressBar), 148 | progress); 149 | if (odvr_cfg_get_report_total_filesize(actionData->odvrData->cfg)) 150 | progressFraction = (gdouble)(actionData->total_bytes_transferred)/ 151 | actionData->total_bytes_to_transfer; 152 | else 153 | progressFraction = (gdouble)(actionData->files_transferred)/ 154 | actionData->files_to_transfer; 155 | 156 | if (progressFraction > 1.0) 157 | progressFraction = 1.0; 158 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(actionData->odvrData->overallProgressBar), 159 | progressFraction); 160 | 161 | 162 | progressFraction = (gdouble)(actionData->bytes_transferred)/actionData->bytes_to_transfer; 163 | sprintf(progress, "%d%%", (int) (progressFraction*100)); 164 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(actionData->odvrData->fileProgressBar), 165 | progress); 166 | if (progressFraction > 1.0) 167 | progressFraction = 1.0; 168 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(actionData->odvrData->fileProgressBar), 169 | progressFraction); 170 | 171 | sprintf(progress, "%d%%", (int) (progressFraction*100)); 172 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(actionData->odvrData->fileProgressBar), 173 | progress); 174 | 175 | while (g_main_context_iteration(NULL, FALSE)); 176 | } 177 | 178 | #define ERR_STRING_SIZE 4096 179 | 180 | static gint transfer_file( actionData_t *actionData, uint8_t folder, uint8_t slot, gint previousResponse) 181 | { 182 | odvr dev = actionData->odvrData->dev; 183 | filestat_t instat; 184 | char fn[128]; 185 | struct stat outstat; 186 | FILE *out; 187 | gint response = previousResponse; 188 | pid_t pID ; 189 | char message[256]; 190 | int status; 191 | char *errorString; 192 | int shmFd; 193 | 194 | if(odvr_filestat(dev, folder, slot, &instat) < 0){ 195 | sprintf(message, "Error getting file instat: %s\n", odvr_error(dev)); 196 | acknowledge(message); 197 | 198 | return GTK_RESPONSE_CANCEL; 199 | } 200 | 201 | sprintf(fn, "%s/D%c_%04d.wav", 202 | gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (actionData->odvrData->ddButton)), 203 | odvr_foldername(dev, folder), instat.id); 204 | 205 | if(stat(fn, &outstat) == 0) { 206 | /* File already exists */ 207 | if (previousResponse == ODVR_RESPONSE_NO_ALL) { 208 | actionData->files_transferred++; 209 | update_progress(actionData); 210 | return ODVR_RESPONSE_NO_ALL; 211 | } 212 | 213 | if (previousResponse != ODVR_RESPONSE_YES_ALL) { 214 | sprintf(message, "File %s exists. Overwrite?", fn); 215 | response = yes_no_acknowledge(message); 216 | if ((response != GTK_RESPONSE_YES) && (response != ODVR_RESPONSE_YES_ALL)) { 217 | actionData->files_transferred++; 218 | update_progress(actionData); 219 | return response; 220 | } 221 | } 222 | } 223 | 224 | if((out = fopen(fn, "w+")) == NULL){ 225 | sprintf(message, "Error opening \"%s\" for writing: %s\n", 226 | fn, strerror(errno)); 227 | acknowledge(message); 228 | actionData->files_transferred++; 229 | update_progress(actionData); 230 | return response; 231 | } 232 | 233 | actionData->bytes_transferred = 0; 234 | actionData->bytes_to_transfer = odvr_wavfilesize(&instat); 235 | 236 | /* Fork a process to read the wav file */ 237 | /* Create a shared memory area for any error string returned by the child */ 238 | shmFd = open("/dev/zero", O_RDWR); 239 | if (shmFd < 0) { 240 | sprintf(message, 241 | "Fatal error - Failed to open file descriptor for shared memory"); 242 | acknowledge(message); 243 | exit(1); 244 | } 245 | 246 | errorString = mmap(0, ERR_STRING_SIZE, 247 | PROT_READ | PROT_WRITE, MAP_SHARED, 248 | shmFd, 0); 249 | close(shmFd); 250 | 251 | pID = fork(); 252 | if (pID < 0) { /* failed to fork */ 253 | sprintf(message, "Fatal error - Failed to fork"); 254 | acknowledge(message); 255 | exit(1); 256 | } 257 | else if (pID == 0) { /* child */ 258 | /* Setup sbort signal handler */ 259 | if(odvr_save_wav(dev, folder, slot, fileno(out))) { 260 | sprintf(errorString, "Error downloading \"%s\": %s\n", 261 | fn, odvr_error(dev)); 262 | _exit(1); 263 | } 264 | _exit(0); 265 | } 266 | else { /* parent */ 267 | struct timespec sleepTime = {0,10}; 268 | gint previous_bytes_transferred = 0; 269 | while (waitpid(pID , &status, WNOHANG) == 0) { 270 | stat(fn, &outstat); 271 | actionData->bytes_transferred = outstat.st_size; 272 | actionData->total_bytes_transferred += 273 | actionData->bytes_transferred - previous_bytes_transferred; 274 | update_progress(actionData); 275 | if (actionData->abortTransfer) { 276 | response = GTK_RESPONSE_CANCEL; 277 | kill(pID, SIGTERM); 278 | } 279 | nanosleep(&sleepTime, NULL); 280 | previous_bytes_transferred = actionData->bytes_transferred; 281 | } 282 | 283 | if (actionData->abortTransfer) { 284 | /* Aborting a transfer may leave the device in a bad state */ 285 | odvr_reset(dev, 0); 286 | } 287 | 288 | if (status && !actionData->abortTransfer) { 289 | acknowledge(errorString); 290 | } 291 | 292 | munmap(errorString, ERR_STRING_SIZE); 293 | } 294 | 295 | fclose(out); 296 | 297 | 298 | if (odvr_cfg_get_keep_original_timestamp(actionData->odvrData->cfg)) { 299 | /* Set the timestamp of the transferred file back to its original date/time */ 300 | struct tm cal_time; 301 | time_t ttime; 302 | struct utimbuf fileTime; 303 | 304 | cal_time.tm_year = instat.year+2000-1900; 305 | cal_time.tm_mon = instat.month-1; 306 | cal_time.tm_mday = instat.day; 307 | cal_time.tm_hour = instat.hour; 308 | cal_time.tm_min = instat.min; 309 | cal_time.tm_sec = instat.sec; 310 | cal_time.tm_wday = 0; 311 | cal_time.tm_yday = 0; 312 | cal_time.tm_isdst = 0; 313 | ttime = mktime(&cal_time); 314 | 315 | 316 | if (ttime != -1) { 317 | fileTime.actime = ttime; 318 | fileTime.modtime = ttime; 319 | utime(fn, &fileTime); 320 | } 321 | } 322 | 323 | actionData->files_transferred++; 324 | update_progress(actionData); 325 | 326 | return response; 327 | } 328 | 329 | static void transfer_selected(GtkTreeModel *model, 330 | GtkTreePath *path, 331 | GtkTreeIter *iter, 332 | gpointer data) 333 | { 334 | gint slot, folder, id; 335 | actionData_t *actionData = data; 336 | GtkTreeIter parent; 337 | 338 | if (actionData->response == GTK_RESPONSE_CANCEL) 339 | return; 340 | if (actionData->abortTransfer) 341 | return; 342 | 343 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) 344 | return; /* Transfer files rather than folders. */ 345 | 346 | gtk_tree_model_get(model, iter, COL_SLOT, &slot, -1); 347 | gtk_tree_model_get(model, iter, COL_ID, &id, -1); 348 | gtk_tree_model_get(model, iter, COL_FOLDER_ID, &folder, -1); 349 | 350 | actionData->response = transfer_file(actionData, folder, slot, actionData->response); 351 | 352 | gtk_tree_selection_unselect_iter(actionData->odvrData->selection, 353 | iter); 354 | while (g_main_context_iteration(NULL, FALSE)); 355 | 356 | } 357 | 358 | static void bytecount_selected(GtkTreeModel *model, 359 | GtkTreePath *path, 360 | GtkTreeIter *iter, 361 | gpointer data) 362 | { 363 | gint wavSize; 364 | actionData_t *actionData = data; 365 | GtkTreeIter parent; 366 | 367 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) 368 | return; /* Transfer files rather than folders. */ 369 | 370 | gtk_tree_model_get(model, iter, COL_SIZE, &wavSize, -1); 371 | 372 | actionData->total_bytes_to_transfer += wavSize; 373 | 374 | return; 375 | } 376 | 377 | /* Set the requested folder selected before transfer */ 378 | static gboolean select_folder(GtkTreeModel *model, 379 | GtkTreePath *path, 380 | GtkTreeIter *iter, 381 | gpointer data) 382 | { 383 | gint folderId; 384 | actionData_t *actionData = data; 385 | GtkTreeIter parent; 386 | 387 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) { 388 | return FALSE; /* Transfer files rather than folders. */ 389 | } 390 | 391 | gtk_tree_model_get(model, iter, COL_FOLDER_ID, &folderId, -1); 392 | if (folderId-1 != actionData->folderId) { 393 | gtk_tree_selection_unselect_iter(actionData->odvrData->selection, iter); 394 | return FALSE; 395 | } 396 | 397 | gtk_tree_selection_select_iter(actionData->odvrData->selection, iter); 398 | 399 | return FALSE; 400 | } 401 | 402 | static void progress_callback(actionData_t *actionData) 403 | { 404 | actionData->abortTransfer = TRUE; 405 | } 406 | 407 | static GtkWidget *createProgressDialog(actionData_t *actionData) 408 | { 409 | GtkWidget *progressDialog; 410 | GtkWidget *progressBar; 411 | gint width, height; 412 | 413 | progressDialog = gtk_dialog_new_with_buttons("File transfer", 414 | NULL, 415 | GTK_DIALOG_DESTROY_WITH_PARENT, 416 | NULL); 417 | 418 | odvr_cfg_get_progress_window_size(actionData->odvrData->cfg, &width, &height); 419 | gtk_window_set_default_size(GTK_WINDOW(progressDialog), width, height); 420 | 421 | gtk_dialog_add_button(GTK_DIALOG (progressDialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); 422 | 423 | progressBar = gtk_progress_bar_new (); 424 | actionData->odvrData->overallProgressBar = progressBar; 425 | gtk_box_pack_start (GTK_BOX (GTK_DIALOG(progressDialog)->vbox), progressBar, FALSE, TRUE, 5); 426 | gtk_widget_show (progressBar); 427 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressBar),0.0); 428 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar),NULL); 429 | 430 | progressBar = gtk_progress_bar_new (); 431 | actionData->odvrData->fileProgressBar = progressBar; 432 | gtk_box_pack_start (GTK_BOX (GTK_DIALOG(progressDialog)->vbox), progressBar, FALSE, TRUE, 5); 433 | gtk_widget_show (progressBar); 434 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressBar),0.0); 435 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressBar),NULL); 436 | 437 | g_signal_connect_swapped (progressDialog, 438 | "response", 439 | G_CALLBACK (progress_callback), 440 | actionData); 441 | 442 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(actionData->odvrData->overallProgressBar),0.0); 443 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(actionData->odvrData->fileProgressBar),0.0); 444 | 445 | gtk_window_set_modal(GTK_WINDOW(progressDialog), TRUE); 446 | gtk_widget_show (progressDialog); 447 | 448 | return progressDialog; 449 | } 450 | 451 | static void destroyProgressDialog(GtkWidget *progressDialog, actionData_t *actionData) 452 | { 453 | gint width, height; 454 | 455 | gtk_window_get_size(GTK_WINDOW(progressDialog), &width, &height); 456 | odvr_cfg_set_progress_window_size(actionData->odvrData->cfg, width, height); 457 | 458 | actionData->odvrData->overallProgressBar = NULL; 459 | actionData->odvrData->fileProgressBar = NULL; 460 | gtk_widget_hide (progressDialog); 461 | gtk_object_destroy(GTK_OBJECT(progressDialog)); 462 | } 463 | 464 | static void transfer(actionData_t *actionData) 465 | { 466 | GtkWidget *progressDialog; 467 | 468 | actionData->response = GTK_RESPONSE_NONE; 469 | actionData->abortTransfer = FALSE; 470 | 471 | progressDialog = createProgressDialog(actionData); 472 | 473 | if (actionData->action == ACTION_TRANSFER_FOLDER) { 474 | /* Mark the selection */ 475 | gtk_tree_model_foreach (gtk_tree_view_get_model(GTK_TREE_VIEW(actionData->odvrData->view)), 476 | select_folder, 477 | (gpointer) actionData); 478 | } 479 | else if (actionData->action == ACTION_TRANSFER_SELECTED) { 480 | /* Nothing special to do here */ 481 | 482 | } 483 | else if (actionData->action == ACTION_TRANSFER_ALL) { 484 | /* Mark everything selected */ 485 | gtk_tree_selection_select_all (GTK_TREE_SELECTION(actionData->odvrData->selection)); 486 | } 487 | else { 488 | g_message ("Unknown transfer action"); 489 | } 490 | 491 | 492 | actionData->response = GTK_RESPONSE_NONE; 493 | actionData->files_transferred = 0; 494 | actionData->files_to_transfer = 495 | gtk_tree_selection_count_selected_rows(GTK_TREE_SELECTION(actionData->odvrData->selection)); 496 | actionData->total_bytes_transferred = 0; 497 | actionData->total_bytes_to_transfer = 0; 498 | 499 | /* Find the total bytes to transfer. 500 | We only need to do this if we use it in the progress bar */ 501 | if (odvr_cfg_get_report_total_filesize(actionData->odvrData->cfg)) 502 | gtk_tree_selection_selected_foreach (GTK_TREE_SELECTION(actionData->odvrData->selection), 503 | bytecount_selected, 504 | (gpointer) actionData); 505 | 506 | /* Transfer the selected files */ 507 | gtk_tree_selection_selected_foreach (GTK_TREE_SELECTION(actionData->odvrData->selection), 508 | transfer_selected, 509 | (gpointer) actionData); 510 | 511 | destroyProgressDialog(progressDialog, actionData); 512 | 513 | } 514 | 515 | static gboolean quality_selected(GtkTreeModel *model, 516 | GtkTreePath *path, 517 | GtkTreeIter *iter, 518 | gpointer data) 519 | { 520 | gint quality; 521 | actionData_t *actionData = data; 522 | 523 | GtkTreeIter parent; 524 | 525 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) 526 | return FALSE; /* Don't select folders, continue */ 527 | 528 | gtk_tree_model_get(model, iter, COL_QUALITY, &quality, -1); 529 | 530 | if (quality >= 8*sizeof(actionData->quality_selected)) { 531 | gtk_tree_selection_unselect_iter(actionData->odvrData->selection, 532 | iter); 533 | return FALSE; /* Invalid quality setting */ 534 | } 535 | 536 | if (actionData->quality_selected & (1<odvrData->selection, 538 | iter); 539 | else 540 | gtk_tree_selection_unselect_iter(actionData->odvrData->selection, 541 | iter); 542 | 543 | return FALSE; 544 | } 545 | 546 | static void select_by_quality(actionData_t *actionData ) 547 | { 548 | gint quality; 549 | GtkWidget *qualityDialog; 550 | GtkWidget *qualityCheckButton[ODVR_QUALITY_NUM]; 551 | 552 | qualityDialog = gtk_dialog_new_with_buttons("Select quality settings", 553 | NULL, 554 | GTK_DIALOG_DESTROY_WITH_PARENT, 555 | NULL); 556 | 557 | /* Create toggle buttons for each of the qualities available. 558 | Set any currently selected to active. */ 559 | for (quality = 0; quality < ODVR_QUALITY_NUM; quality++) { 560 | if (actionData->odvrData->quality_used & (1<quality_selected & (1<vbox), 566 | GTK_WIDGET(qualityCheckButton[quality]), FALSE, FALSE, 0); 567 | gtk_widget_show(qualityCheckButton[quality]); 568 | } 569 | else 570 | qualityCheckButton[quality] = NULL; 571 | } 572 | 573 | gtk_dialog_add_button(GTK_DIALOG (qualityDialog), GTK_STOCK_OK, GTK_RESPONSE_OK); 574 | 575 | /* Pop up the box to select the quality settings to select */ 576 | gtk_dialog_run (GTK_DIALOG (qualityDialog)); 577 | /* Extract the selected quality settings */ 578 | actionData->quality_selected = 0; 579 | for (quality = 0; quality < ODVR_QUALITY_NUM; quality++) { 580 | if (qualityCheckButton[quality]) 581 | if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(qualityCheckButton[quality]))) 582 | actionData->quality_selected |= (1<odvrData->view)), 587 | quality_selected, 588 | (gpointer) actionData); 589 | 590 | } 591 | 592 | static gboolean date_select(GtkTreeModel *model, 593 | GtkTreePath *path, 594 | GtkTreeIter *iter, 595 | gpointer data) 596 | { 597 | actionData_t *actionData = data; 598 | 599 | GtkTreeIter parent; 600 | 601 | if (!gtk_tree_model_iter_parent(model, &parent, iter)) 602 | return FALSE; /* Don't select folders. */ 603 | 604 | /* If date is in range, select, else unselect */ 605 | gtk_tree_selection_unselect_iter(actionData->odvrData->selection, 606 | iter); 607 | 608 | return FALSE; 609 | } 610 | 611 | 612 | static void select_by_date(actionData_t *actionData ) 613 | { 614 | 615 | /* Ask for date range */ 616 | 617 | gtk_tree_model_foreach (gtk_tree_view_get_model(GTK_TREE_VIEW(actionData->odvrData->view)), 618 | date_select, 619 | (gpointer) actionData); 620 | } 621 | 622 | static void select_files( actionData_t *actionData ) 623 | { 624 | switch (actionData->action) { 625 | case ACTION_SELECT_ALL: 626 | gtk_tree_selection_select_all (GTK_TREE_SELECTION(actionData->odvrData->selection)); 627 | break; 628 | case ACTION_SELECT_NONE: 629 | gtk_tree_selection_unselect_all (GTK_TREE_SELECTION(actionData->odvrData->selection)); 630 | break; 631 | case ACTION_SELECT_BY_QUALITY: 632 | select_by_quality(actionData); 633 | break; 634 | case ACTION_SELECT_BY_DATE: 635 | select_by_date(actionData); 636 | g_message ("Select by date"); 637 | break; 638 | default: 639 | g_message ("Unknown select"); 640 | } 641 | } 642 | 643 | static void clear( actionData_t *actionData ) 644 | { 645 | char buf[64]; 646 | GtkTreePath *path; 647 | GtkTreeStore *file_store; 648 | GtkTreeIter iter; 649 | 650 | sprintf(buf, "This will remove all files from folder %c\n Are you sure?", 651 | odvr_foldername(actionData->odvrData->dev, actionData->folderId+1)); 652 | if (acknowledge_ok_cancel(buf) != GTK_RESPONSE_OK) 653 | return; 654 | 655 | odvr_clear_folder(actionData->odvrData->dev, actionData->folderId+1); 656 | 657 | /* Remove the items from the store */ 658 | file_store= actionData->odvrData->file_store; 659 | path = gtk_tree_path_new_from_indices(actionData->folderId, 0, -1); 660 | if (gtk_tree_model_get_iter(GTK_TREE_MODEL(file_store), &iter, path)) { 661 | #if 0 662 | /* Disconnect from the viewer while deleting, otherwise it is 663 | wasting time refreshing the display as each row is deleted. */ 664 | g_object_ref(GTK_TREE_MODEL(file_store)); 665 | gtk_tree_view_set_model(GTK_TREE_VIEW(actionData->odvrData->view), 666 | NULL); 667 | #endif 668 | while (gtk_tree_store_remove(file_store, &iter)) { 669 | } 670 | 671 | #if 0 672 | /* Re-attach model to view */ 673 | gtk_tree_view_set_model(GTK_TREE_VIEW(actionData->odvrData->view), 674 | GTK_TREE_MODEL(file_store)); 675 | g_object_unref(GTK_TREE_MODEL(file_store)); 676 | #endif 677 | } 678 | 679 | gtk_tree_path_free(path); 680 | } 681 | 682 | static void clear_all( actionData_t *actionData ) 683 | { 684 | char folderId; 685 | char buf[64]; 686 | GtkTreePath *path; 687 | GtkTreeStore *file_store; 688 | GtkTreeIter iter; 689 | 690 | sprintf(buf, "This will remove all files from the device\n Are you sure?"); 691 | if (acknowledge_ok_cancel(buf) != GTK_RESPONSE_OK) 692 | return; 693 | 694 | file_store= actionData->odvrData->file_store; 695 | 696 | #if 1 697 | /* Disconnect from the viewer while deleting, otherwise it is 698 | wasting time refreshing the display as each row is deleted. */ 699 | g_object_ref(GTK_TREE_MODEL(file_store)); 700 | gtk_tree_view_set_model(GTK_TREE_VIEW(actionData->odvrData->view), 701 | NULL); 702 | #endif 703 | 704 | for (folderId=0; 705 | folderIdodvrData->dev); 706 | folderId++) { 707 | odvr_clear_folder(actionData->odvrData->dev, folderId+1); 708 | 709 | /* Remove the items from the store */ 710 | path = gtk_tree_path_new_from_indices(folderId, 0, -1); 711 | if (gtk_tree_model_get_iter(GTK_TREE_MODEL(file_store), &iter, path)) { 712 | while (gtk_tree_store_remove(file_store, &iter)) { 713 | } 714 | 715 | } 716 | 717 | gtk_tree_path_free(path); 718 | } 719 | 720 | #if 1 721 | /* Re-attach model to view */ 722 | gtk_tree_view_set_model(GTK_TREE_VIEW(actionData->odvrData->view), 723 | GTK_TREE_MODEL(file_store)); 724 | g_object_unref(GTK_TREE_MODEL(file_store)); 725 | #endif 726 | 727 | } 728 | 729 | /* transfer button pressed */ 730 | static gboolean transfer_event( GtkWidget *widget, 731 | actionData_t *actionData ) 732 | { 733 | transfer(actionData); 734 | 735 | return FALSE; 736 | 737 | } 738 | 739 | static void about( gpointer callback_data, 740 | guint callback_action, 741 | GtkWidget *menu_item ) 742 | { 743 | gchar *authors[] = {"Conor McLoughin", "Tristan Willy", NULL}; 744 | 745 | gtk_show_about_dialog(NULL, 746 | "version", VERSION, 747 | "authors", authors, 748 | "copyright", "2007-2008 Conor McLoughin, Tristan Willy", 749 | "license", "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.", 750 | "wrap-license", TRUE, 751 | "website", "http://code.google.com/p/odvr/", 752 | NULL); 753 | 754 | 755 | } 756 | 757 | 758 | 759 | 760 | gboolean view_selection_funct (GtkTreeSelection *selection, 761 | GtkTreeModel *model, 762 | GtkTreePath *path, 763 | gboolean path_currently_selected, 764 | gpointer userdata) 765 | { 766 | GtkTreeIter iter, parent; 767 | 768 | if (gtk_tree_model_get_iter(model, &iter, path)) { 769 | if (!gtk_tree_model_iter_parent(model, &parent, &iter)) { 770 | return FALSE; /* Folder cannot be selected */ 771 | } 772 | else 773 | return TRUE; /* Not a folder. Can be selected */ 774 | } 775 | return TRUE; 776 | } 777 | 778 | 779 | void add_menus(odvrData_t *odvrData, int numFolders) 780 | { 781 | GtkWidget *file_menu, *transfer_menu, *clear_menu, *help_menu, *select_menu; 782 | GtkWidget *file_item, *transfer_item, *clear_item, *help_item, *select_item; 783 | GtkWidget *menu_item; 784 | GtkWidget *quit_item; 785 | actionData_t actionData; 786 | int folder; 787 | 788 | odvrData->menu_bar = gtk_menu_bar_new(); 789 | gtk_widget_show (odvrData->menu_bar); 790 | 791 | /* File menu */ 792 | file_menu = gtk_menu_new(); 793 | gtk_widget_show (file_menu); 794 | 795 | quit_item = gtk_menu_item_new_with_label ("Quit"); 796 | gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), quit_item); 797 | g_signal_connect(G_OBJECT (quit_item), "activate", 798 | G_CALLBACK (quit_event), 799 | (gpointer) odvrData); 800 | gtk_widget_show (quit_item); 801 | 802 | file_item = gtk_menu_item_new_with_label ("File"); 803 | gtk_widget_show (file_item); 804 | 805 | gtk_menu_item_set_submenu (GTK_MENU_ITEM (file_item), file_menu); 806 | gtk_menu_bar_append (GTK_MENU_BAR (odvrData->menu_bar), file_item); 807 | 808 | 809 | /* Transfer menu */ 810 | transfer_menu = gtk_menu_new(); 811 | gtk_widget_show (transfer_menu); 812 | 813 | for(folder = 0; folder < sizeof(folderNames); folder++) { 814 | char buf[] = "Folder ?"; 815 | buf[7] = folderNames[folder]; 816 | menu_item = gtk_menu_item_new_with_label (buf); 817 | gtk_menu_shell_append (GTK_MENU_SHELL (transfer_menu), menu_item); 818 | actionData.action = ACTION_TRANSFER_FOLDER; 819 | actionData.folderId = folder; 820 | actionData.odvrData = odvrData; 821 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 822 | G_CALLBACK (transfer), 823 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 824 | 825 | gtk_widget_show (menu_item); 826 | if (folderNames[folder] == 'D') { 827 | odvrData->transferMenuD = menu_item; 828 | gtk_widget_hide (menu_item); 829 | } 830 | if (folderNames[folder] == 'S') { 831 | odvrData->transferMenuS = menu_item; 832 | gtk_widget_hide (menu_item); 833 | } 834 | } 835 | 836 | menu_item = gtk_menu_item_new_with_label ("Selected files"); 837 | gtk_widget_show (menu_item); 838 | gtk_menu_shell_append (GTK_MENU_SHELL (transfer_menu), menu_item); 839 | actionData.action = ACTION_TRANSFER_SELECTED; 840 | actionData.odvrData = odvrData; 841 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 842 | G_CALLBACK (transfer), 843 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 844 | 845 | menu_item = gtk_menu_item_new_with_label ("All files"); 846 | gtk_widget_show (menu_item); 847 | gtk_menu_shell_append (GTK_MENU_SHELL (transfer_menu), menu_item); 848 | actionData.action = ACTION_TRANSFER_ALL; 849 | actionData.odvrData = odvrData; 850 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 851 | G_CALLBACK (transfer), 852 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 853 | 854 | 855 | transfer_item = gtk_menu_item_new_with_label ("Transfer"); 856 | gtk_widget_show (transfer_item); 857 | 858 | gtk_menu_item_set_submenu (GTK_MENU_ITEM (transfer_item), transfer_menu); 859 | gtk_menu_bar_append (GTK_MENU_BAR (odvrData->menu_bar), transfer_item); 860 | 861 | 862 | /* Select menu */ 863 | select_menu = gtk_menu_new(); 864 | gtk_widget_show (select_menu); 865 | 866 | menu_item = gtk_menu_item_new_with_label ("All"); 867 | gtk_menu_shell_append (GTK_MENU_SHELL (select_menu), menu_item); 868 | actionData.action = ACTION_SELECT_ALL; 869 | actionData.odvrData = odvrData; 870 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 871 | G_CALLBACK (select_files), 872 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 873 | gtk_widget_show (menu_item); 874 | 875 | 876 | menu_item = gtk_menu_item_new_with_label ("None"); 877 | gtk_menu_shell_append (GTK_MENU_SHELL (select_menu), menu_item); 878 | actionData.action = ACTION_SELECT_NONE; 879 | actionData.odvrData = odvrData; 880 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 881 | G_CALLBACK (select_files), 882 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 883 | gtk_widget_show (menu_item); 884 | 885 | menu_item = gtk_menu_item_new_with_label ("By quality"); 886 | gtk_menu_shell_append (GTK_MENU_SHELL (select_menu), menu_item); 887 | actionData.action = ACTION_SELECT_BY_QUALITY; 888 | actionData.odvrData = odvrData; 889 | actionData.quality_selected = 0; 890 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 891 | G_CALLBACK (select_files), 892 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 893 | gtk_widget_show (menu_item); 894 | 895 | #if 0 896 | menu_item = gtk_menu_item_new_with_label ("By date"); 897 | gtk_menu_shell_append (GTK_MENU_SHELL (select_menu), menu_item); 898 | actionData.action = ACTION_SELECT_BY_DATE; 899 | actionData.odvrData = odvrData; 900 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 901 | G_CALLBACK (select_files), 902 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 903 | gtk_widget_show (menu_item); 904 | #endif 905 | 906 | select_item = gtk_menu_item_new_with_label ("Select"); 907 | gtk_widget_show (select_item); 908 | 909 | gtk_menu_item_set_submenu (GTK_MENU_ITEM (select_item), select_menu); 910 | gtk_menu_bar_append (GTK_MENU_BAR (odvrData->menu_bar), select_item); 911 | 912 | 913 | 914 | /* Clear menu */ 915 | clear_menu = gtk_menu_new(); 916 | gtk_widget_show (clear_menu); 917 | 918 | for(folder = 0; folder < sizeof(folderNames); folder++) { 919 | char buf[] = "Folder ?"; 920 | buf[7] = folderNames[folder]; 921 | menu_item = gtk_menu_item_new_with_label (buf); 922 | gtk_menu_shell_append (GTK_MENU_SHELL (clear_menu), menu_item); 923 | actionData.action = ACTION_CLEAR; 924 | actionData.folderId = folder; 925 | actionData.odvrData = odvrData; 926 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 927 | G_CALLBACK (clear), 928 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 929 | 930 | gtk_widget_show (menu_item); 931 | if (folderNames[folder] == 'D') { 932 | odvrData->clearMenuD = menu_item; 933 | gtk_widget_hide (menu_item); 934 | } 935 | if (folderNames[folder] == 'S') { 936 | odvrData->clearMenuS = menu_item; 937 | gtk_widget_hide (menu_item); 938 | } 939 | } 940 | 941 | menu_item = gtk_menu_item_new_with_label ("All"); 942 | gtk_menu_shell_append (GTK_MENU_SHELL (clear_menu), menu_item); 943 | actionData.action = ACTION_CLEAR; 944 | actionData.folderId = -1; 945 | actionData.odvrData = odvrData; 946 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 947 | G_CALLBACK (clear_all), 948 | (gpointer) g_memdup(&actionData, sizeof(actionData)) ); 949 | 950 | gtk_widget_show (menu_item); 951 | 952 | 953 | clear_item = gtk_menu_item_new_with_label ("Clear"); 954 | gtk_widget_show (clear_item); 955 | 956 | gtk_menu_item_set_submenu (GTK_MENU_ITEM (clear_item), clear_menu); 957 | gtk_menu_bar_append (GTK_MENU_BAR (odvrData->menu_bar), clear_item); 958 | 959 | 960 | /* Help menu */ 961 | help_menu = gtk_menu_new(); 962 | gtk_widget_show (help_menu); 963 | 964 | menu_item = gtk_menu_item_new_with_label ("About"); 965 | gtk_menu_shell_append (GTK_MENU_SHELL (help_menu), menu_item); 966 | g_signal_connect_swapped (G_OBJECT (menu_item), "activate", 967 | G_CALLBACK (about), (gpointer) 0); 968 | gtk_widget_show (menu_item); 969 | 970 | help_item = gtk_menu_item_new_with_label ("Help"); 971 | gtk_menu_item_set_right_justified(GTK_MENU_ITEM(help_item), TRUE); 972 | gtk_widget_show (help_item); 973 | 974 | gtk_menu_item_set_submenu (GTK_MENU_ITEM (help_item), help_menu); 975 | gtk_menu_bar_append (GTK_MENU_BAR (odvrData->menu_bar), help_item); 976 | 977 | } 978 | 979 | gint add_buttons(odvrData_t *odvrData, int numFolders) 980 | { 981 | GtkWidget *button = NULL; 982 | GtkWidget *image; 983 | int folder; 984 | actionData_t *actionData; 985 | GError *error=NULL; 986 | GdkPixbuf *pixBuf; 987 | 988 | /* Add botton box */ 989 | if (odvrData->buttonBox == NULL) { 990 | odvrData->buttonBox = gtk_hbox_new (FALSE, 5); 991 | gtk_widget_show (odvrData->buttonBox); 992 | } 993 | 994 | for(folder = 0; folder < sizeof(folderNames); folder++) { 995 | char toolTip[] = "Transfer folder A"; 996 | 997 | /* Create "Transfer folder" button */ 998 | button = gtk_button_new(); 999 | toolTip[16]= folderNames[folder]; 1000 | gtk_widget_set_tooltip_text(button, toolTip); 1001 | 1002 | pixBuf = gdk_pixbuf_from_pixdata(transferFolderIcons[folder], FALSE, &error); 1003 | if (error) { 1004 | gui_err("gdk_pixbuf_from_pixdata failed.", error->message); 1005 | } 1006 | image = gtk_image_new_from_pixbuf(pixBuf); 1007 | 1008 | odvrData->folderIcon[folder] = gdk_pixbuf_from_pixdata(folderIcons[folder], 1009 | FALSE, &error); 1010 | if (error) { 1011 | gui_err("Could not load icon.", error->message); 1012 | g_free(error); 1013 | error=NULL; 1014 | return -1; 1015 | } 1016 | 1017 | gtk_button_set_image(GTK_BUTTON(button), image); 1018 | 1019 | actionData = g_new(actionData_t, 1); 1020 | actionData->action = ACTION_TRANSFER_FOLDER; 1021 | actionData->folderId = folder; 1022 | actionData->odvrData = odvrData; 1023 | 1024 | g_signal_connect (G_OBJECT (button), "clicked", 1025 | G_CALLBACK (transfer_event), actionData); 1026 | 1027 | /* Put the button in the box */ 1028 | gtk_box_pack_start (GTK_BOX (odvrData->buttonBox), button, FALSE, FALSE, 0); 1029 | 1030 | if ( (folderNames[folder] == 'D') || (folderNames[folder] == 'S') ) 1031 | { 1032 | gtk_widget_hide (button); 1033 | if (folderNames[folder] == 'D') 1034 | odvrData->buttonD = button; 1035 | if (folderNames[folder] == 'S') 1036 | odvrData->buttonS = button; 1037 | } 1038 | else 1039 | { 1040 | gtk_widget_show (button); 1041 | } 1042 | 1043 | } 1044 | 1045 | pixBuf = gdk_pixbuf_from_pixdata(&TBT, FALSE, &error); 1046 | image = gtk_image_new_from_pixbuf(pixBuf); 1047 | button = gtk_button_new(); 1048 | gtk_widget_set_tooltip_text(button, "Transfer selected files"); 1049 | gtk_button_set_image(GTK_BUTTON(button), image); 1050 | 1051 | actionData = g_new(actionData_t, 1); 1052 | actionData->action = ACTION_TRANSFER_SELECTED; 1053 | actionData->folderId = -1; 1054 | actionData->odvrData = odvrData; 1055 | g_signal_connect (G_OBJECT (button), "clicked", 1056 | G_CALLBACK (transfer_event), actionData); 1057 | 1058 | gtk_box_pack_start (GTK_BOX (odvrData->buttonBox), button, FALSE, FALSE, 0); 1059 | gtk_widget_show (button); 1060 | 1061 | return 0; 1062 | } 1063 | 1064 | void add_destinationDir(odvrData_t *odvrData) 1065 | { 1066 | char *destinationDir; 1067 | 1068 | GtkWidget *label, *ddDialog; 1069 | 1070 | /* Add box for destination directory */ 1071 | if (odvrData->destinationDirBox == NULL) { 1072 | odvrData->destinationDirBox = gtk_vbox_new (FALSE, 1); 1073 | gtk_widget_show (odvrData->destinationDirBox); 1074 | } 1075 | 1076 | label = gtk_label_new(NULL); 1077 | gtk_label_set_markup (GTK_LABEL (label), 1078 | "Destination directory"); 1079 | 1080 | gtk_box_pack_start (GTK_BOX (odvrData->destinationDirBox), label, FALSE, FALSE, 0); 1081 | gtk_widget_show (label); 1082 | 1083 | /* Create button to select destination file/directory */ 1084 | ddDialog = gtk_file_chooser_dialog_new("Destination directory", 1085 | NULL, 1086 | GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, 1087 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 1088 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 1089 | NULL); 1090 | odvrData->ddButton = gtk_file_chooser_button_new_with_dialog(ddDialog); 1091 | 1092 | gtk_box_pack_start (GTK_BOX (odvrData->destinationDirBox), odvrData->ddButton, FALSE, TRUE, 0); 1093 | gtk_widget_show (odvrData->ddButton); 1094 | 1095 | /* Set the default or configured destination directory */ 1096 | destinationDir = odvr_cfg_get_destination_dir(odvrData->cfg); 1097 | if (destinationDir) { 1098 | gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(odvrData->ddButton), 1099 | destinationDir); 1100 | } 1101 | 1102 | 1103 | } 1104 | 1105 | 1106 | int main(int argc, char *argv[]) 1107 | { 1108 | odvrData_t odvrData; 1109 | odvr dev; 1110 | int open_flags; 1111 | GtkWidget *mainVbox; 1112 | GtkWidget *fileWindow = NULL; 1113 | GtkWidget *view; 1114 | GtkWidget *separator; 1115 | GtkTreeStore *file_store; 1116 | GtkTreeSelection *selection; 1117 | const char *model = NULL; 1118 | int numFolders = ODVR_NUM_FOLDERS; 1119 | gint width, height; 1120 | 1121 | gtk_init (&argc, &argv); 1122 | 1123 | memset(&odvrData, 0, sizeof(odvrData)); 1124 | 1125 | odvrData.cfg = odvr_cfg_load_config(); 1126 | odvr_cfg_set_version(odvrData.cfg, VERSION); 1127 | 1128 | transferFolderIcons[0] = &TBA; 1129 | transferFolderIcons[1] = &TBB; 1130 | transferFolderIcons[2] = &TBC; 1131 | transferFolderIcons[3] = &TBD; 1132 | transferFolderIcons[4] = &TBS; 1133 | folderIcons[0] = &FCA; 1134 | folderIcons[1] = &FCB; 1135 | folderIcons[2] = &FCC; 1136 | folderIcons[3] = &FCD; 1137 | folderIcons[4] = &FCS; 1138 | 1139 | open_flags = 0; 1140 | 1141 | while((dev = odvr_open(open_flags)) == NULL) { 1142 | if (acknowledge_ok_cancel("Failed to open Olympus device.\nConnect and try again") == 1143 | GTK_RESPONSE_CANCEL) 1144 | return -1; 1145 | 1146 | } 1147 | odvrData.dev = dev; 1148 | 1149 | if (dev) 1150 | { 1151 | if((model = odvr_model(dev)) == NULL) 1152 | { 1153 | g_print("Couldn't query model name. %s", 1154 | odvr_error(dev)); 1155 | } 1156 | 1157 | numFolders = odvr_foldercount(dev); 1158 | } 1159 | 1160 | /* Create a new window */ 1161 | odvrData.window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 1162 | gtk_window_set_title (GTK_WINDOW (odvrData.window), 1163 | NAME); 1164 | 1165 | odvr_cfg_get_window_size(odvrData.cfg, &width, &height); 1166 | gtk_window_set_default_size(GTK_WINDOW(odvrData.window), width, height); 1167 | 1168 | set_date_format(odvr_cfg_get_date_format(odvrData.cfg)); 1169 | 1170 | /* Set a handler for delete_event that immediately 1171 | * exits GTK. */ 1172 | g_signal_connect (G_OBJECT (odvrData.window), "delete_event", 1173 | G_CALLBACK (delete_event), NULL); 1174 | 1175 | /* Sets the border width of the window. */ 1176 | gtk_container_set_border_width (GTK_CONTAINER (odvrData.window), 5); 1177 | 1178 | mainVbox = gtk_vbox_new (FALSE, 5); 1179 | gtk_widget_show (mainVbox); 1180 | gtk_container_add (GTK_CONTAINER (odvrData.window), mainVbox); 1181 | 1182 | add_menus(&odvrData, numFolders); 1183 | gtk_box_pack_start(GTK_BOX(mainVbox), odvrData.menu_bar, FALSE, FALSE, 1); 1184 | 1185 | 1186 | /* Create a list store to store the files on the folder. */ 1187 | file_store = gtk_tree_store_new(8, 1188 | GDK_TYPE_PIXBUF, /* Folder */ 1189 | G_TYPE_UINT, /* Folder */ 1190 | G_TYPE_UINT, /* Slot */ 1191 | G_TYPE_UINT, /* File */ 1192 | G_TYPE_UINT, /* Size */ 1193 | G_TYPE_FLOAT, /* Length */ 1194 | G_TYPE_POINTER, /* Date */ 1195 | G_TYPE_UINT); /* Quality */ 1196 | 1197 | odvrData.file_store = file_store; 1198 | 1199 | if (add_buttons(&odvrData, numFolders) < 0) 1200 | return -1; 1201 | 1202 | gtk_box_pack_start(GTK_BOX(mainVbox), odvrData.buttonBox, FALSE, FALSE, 1); 1203 | 1204 | separator = gtk_hseparator_new (); 1205 | gtk_box_pack_start (GTK_BOX (mainVbox), separator, FALSE, TRUE, 1); 1206 | gtk_widget_show (separator); 1207 | 1208 | 1209 | add_destinationDir(&odvrData); 1210 | gtk_box_pack_start(GTK_BOX(mainVbox), odvrData.destinationDirBox, FALSE, FALSE, 1); 1211 | 1212 | separator = gtk_hseparator_new (); 1213 | gtk_box_pack_start (GTK_BOX (mainVbox), separator, FALSE, TRUE, 1); 1214 | gtk_widget_show (separator); 1215 | 1216 | fileWindow = gtk_scrolled_window_new (NULL, NULL); 1217 | gtk_widget_show (fileWindow); 1218 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fileWindow), 1219 | GTK_POLICY_AUTOMATIC, 1220 | GTK_POLICY_AUTOMATIC); 1221 | 1222 | 1223 | 1224 | if (dev) 1225 | { 1226 | gtk_window_set_title (GTK_WINDOW (odvrData.window), 1227 | model); 1228 | 1229 | scan_device(&odvrData); 1230 | } 1231 | 1232 | view = create_view(&odvrData); 1233 | odvrData.view = view; 1234 | 1235 | gtk_widget_show_all (view); 1236 | 1237 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(file_store)); 1238 | g_object_unref(file_store); /* destroy model automatically with view */ 1239 | 1240 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); 1241 | odvrData.selection = selection; 1242 | gtk_tree_selection_set_select_function(selection, 1243 | view_selection_funct, 1244 | NULL, NULL); 1245 | gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); 1246 | 1247 | gtk_container_add(GTK_CONTAINER(fileWindow), view); 1248 | gtk_container_add(GTK_CONTAINER(mainVbox), fileWindow); 1249 | 1250 | gtk_widget_show (odvrData.window); 1251 | 1252 | 1253 | gtk_main (); 1254 | 1255 | 1256 | exit(0); 1257 | } 1258 | --------------------------------------------------------------------------------