Use TopfieldUSBEcode for received values too.
[MacTF.git] / TFUSBController.m
blob7ab5b7e6d5bdff3ddefb04af64f35c0402f704be
1 //
2 //  TFUSBController.m
3 //  MacTF
4 //
5 //  Created by Nathan Oates on Sun Aug 01 2004.
6 //  Copyright (c) 2004-7 Nathan Oates nathan@noates.com All rights reserved.
7 //
9 /* This file was modified by Kalle Olavi Niemitalo on 2007-10-18.  */
11 // includes code based on uproar, license noted below:
12 //  uproar 0.1
13 //  Copyright (c) 2001 Kasima Tharnpipitchai <me@kasima.org>
15 /* 
16  *
17  * This source code is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Public License as published
19  * by the Free Software Foundation; either version 2 of the License,
20  * or (at your option) any later version.
21  *
22  * This source code is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25  * Please refer to the GNU Public License for more details.
26  *
27  * You should have received a copy of the GNU Public License along with
28  * this source code; if not, write to:
29  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30  */
32 /* Much of the code that initializes and closes the device is from Apple
33  * and is released under the license below.
34  */
37  * © Copyright 2001 Apple Computer, Inc. All rights reserved.
38  *
39  * IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in 
40  * consideration of your agreement to the following terms, and your use, installation, 
41  * modification or redistribution of this Apple software constitutes acceptance of these
42  * terms.  If you do not agree with these terms, please do not use, install, modify or 
43  * redistribute this Apple software.
44  *
45  * In consideration of your agreement to abide by the following terms, and subject to these 
46  * terms, Apple grants you a personal, non exclusive license, under Apple’s copyrights in this 
47  * original Apple software (the “Apple Software”), to use, reproduce, modify and redistribute 
48  * the Apple Software, with or without modifications, in source and/or binary forms; provided 
49  * that if you redistribute the Apple Software in its entirety and without modifications, you 
50  * must retain this notice and the following text and disclaimers in all such redistributions 
51  * of the Apple Software.  Neither the name, trademarks, service marks or logos of Apple 
52  * Computer, Inc. may be used to endorse or promote products derived from the Apple Software 
53  * without specific prior written permission from Apple. Except as expressly stated in this 
54  * notice, no other rights or licenses, express or implied, are granted by Apple herein, 
55  * including but not limited to any patent rights that may be infringed by your derivative 
56  * works or by other works in which the Apple Software may be incorporated.
57  * 
58  * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES, 
59  * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
60  * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE 
61  * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
62  *
63  * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL 
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
65  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, 
66  * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND 
67  * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR 
68  * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
69  */
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <unistd.h>
76 #include <mach/mach.h>
77 #include <IOKit/IOCFPlugIn.h>
78 #include <CoreFoundation/CFNumber.h>
80 #import "DataHandler.h"
81 #import "TFUSBController.h"
82 #import "TFDataFormat.h"
83 #import "UIElements.h"
85 static void hexDump(UInt8 *buf, int len);
86 static int doSend(IOUSBDeviceInterface197 **dev,
87         IOUSBInterfaceInterface197 **intf, UInt8 *outBuf, UInt32 len, int type);
88 static int doRecv(IOUSBDeviceInterface197 **dev,
89         IOUSBInterfaceInterface197 **intf, UInt8 *inBuf, UInt32 dataLen, int type);
90 static int dealWithInterface(io_service_t usbInterfaceRef,
91         USBDeviceContext *device);
92 static int dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device);
93 static int initDevice(USBDeviceContext *device);
95 static int debug;
96 static int rate;
97 static int kBlockSize;
98 static int connectedSpeed;
100 @interface TFUSBController (PrivateMethods)
102 // Low-level USB
103 - (NSData*) sendCommand:(NSData*)fullyPackagedCommand
104         toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
105         careForReturn:(BOOL)careFactor;
107 // Protocol message sequences
108 - (id) getFileListForPath:(NSString*)path;
109 - (void) turnTurboOn:(BOOL)turnOn;
110 - (void) renameFile:(NSString*)oldName withName:(NSString*)newName atPath:(NSString*)currentPath;
111 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath;
112 - (void) checkUSB:(USBDeviceContext*)device;
114 // User interface
115 - (UIElements*) uiElements;
116 - (void) updateProgress:(NSDictionary*)inDict;
117 - (NSString*) elapsedTime:(NSTimeInterval)totalSeconds;
119 // Queue management
120 - (BOOL) hasPriorityTransfer;
121 - (void) getNextTransfer:(NSDictionary**)transfer queue:(NSMutableArray**)queue;
122 - (void) removeTransfer:(NSDictionary*)transfer fromQueue:(NSMutableArray*)queue;
124 @end
126 #define         TopfieldVendorID        4571
127    //1317 for HC
128    //0x138c for humax
129    //4571 for toppy
130 #define         TF5kProdID      4096
131 //42514 for HC
132 //0x02ad for humax
133 //4096 for toppy
135 // ---------------------------------------------------------------------------
136 #pragma mark Functions outside TFUSBController
137 // ---------------------------------------------------------------------------
139 static void
140 hexDump(UInt8 *buf, int len)
142         int row, col, maxrows;
144         maxrows = len/16;
145         if (len % 16) maxrows++;
146         for (row=0; row< maxrows; row++) {
147                 for (col=0; col<16; col++) {
148                         if (!(col%2)) printf(" ");
149                         printf("%02x", buf[row*16 + col] & 0xff);
150                 }
151                 printf("\t");   
152                 for (col=0; col<16; col++) {
153                         if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
154                                 printf("%c", buf[row*16 + col]);
155                         }
156                         else { printf("."); }
157                 }
158                 printf("\n");
159         }
162 static int
163 doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
164         UInt8 *outBuf, UInt32 len, int type)
166         IOReturn    err;
167         UInt32      sendLen;
169         if (debug == 1){
170                 printf(("sending:\n"));
171                 hexDump(outBuf, len);
172         }
173         sendLen = ((len/kBlockSize))*kBlockSize;
174         if (len % kBlockSize)
175                 sendLen += kBlockSize;
176         if ((sendLen % 0x200) == 0)  
177                 sendLen += kBlockSize;
179         err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
180         if (err) {
181                 printf("write err: %08x\n", err);
182                 return err;
183         }
184         return err;
187 static int
188 doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
189         UInt8 *inBuf, UInt32 dataLen, int type)
191         IOReturn    err;
192         UInt32      len;
194         if (dataLen > kMaxXferSize) return 1;
196         len = (dataLen/kBlockSize) * kBlockSize;
197         if (dataLen % kBlockSize)
198                 len += kBlockSize;
200         err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len,  1000, 20000);
201         if (err) {
202                 printf("read err 2: %08x\n", err);
203                 printf("resetting\n");
204                 (*intf)->ClearPipeStallBothEnds(intf, 2); // ignore return value
205                 return err;
206         }
208         if (debug == 1) {
209                 printf(("receiving: \n")); 
210                 hexDump(inBuf, len);
211         }
212         return err;
215 static int
216 dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
218         IOReturn                     err;
219         IOCFPlugInInterface        **iodev;             // requires <IOKit/IOCFPlugIn.h>
220         IOUSBInterfaceInterface197 **intf;
221         IOUSBDeviceInterface197    **dev;
222         SInt32                       score;
223         UInt8                        numPipes, confNum, dSpeed;
226         err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
227         if (err || !iodev)
228         {
229                 printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
230                 return kUproarDeviceErr;
231         }
232         err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
233         (*iodev)->Release(iodev);                               // done with this
234         intf = device->intf;
235         if (err || !intf)
236         {
237                 printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
238                 return kUproarDeviceErr;
239         }
241         dev = device->dev;
242         err = (*intf)->USBInterfaceOpen(intf);
243         if (err)
244         {
245                 printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
246                 return kUproarDeviceErr;
247         }
248         err = (*intf)->GetNumEndpoints(intf, &numPipes);
249         if (err)
250         {
251                 printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
252                 return kUproarDeviceErr;
253         }
255         printf("dealWithInterface: found %d pipes\n", numPipes);
257         err = (*intf)->GetConfigurationValue(intf, &confNum);
258         err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
259         if (dSpeed == 2) {
260                 kBlockSize = 0x40;
261         } else {
262                 kBlockSize = 0x4;
263         }
264         printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
265         connectedSpeed = dSpeed;
266         return kUproarSuccess;
270 static int
271 dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
273         IOReturn                        err;
274         IOCFPlugInInterface           **iodev;          // requires <IOKit/IOCFPlugIn.h>
275         IOUSBDeviceInterface197       **dev;
276         SInt32                          score;
277         UInt8                           numConf;
278         IOUSBConfigurationDescriptorPtr confDesc;
279         IOUSBFindInterfaceRequest       interfaceRequest;
280         io_iterator_t                   iterator;
281         io_service_t                    usbInterfaceRef;
282         int                             found=0;
285         err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
286         if (err || !iodev)
287         {
288                 printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
289                 return kUproarDeviceErr;
290         }
291         err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
292         (*iodev)->Release(iodev);                               
294         dev = device->dev;
295         if (err || !dev)
296         {
297                 printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
298                 return kUproarDeviceErr;
299         }
300         err = (*dev)->USBDeviceOpen(dev);
301         if (err)
302         {
303                 printf("dealWithDevice: unable to open device. ret = %08x\n", err);
304                 return kUproarDeviceErr;
305         }
306         err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
307         if (err || !numConf)
308         {
309                 printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
310                 return kUproarDeviceErr;
311         }
312         printf("dealWithDevice: found %d configurations\n", numConf);
313         err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc); // get the first config desc (index 0)
314         if (err)
315         {
316                 printf("dealWithDevice:unable to get config descriptor for index 0\n");
317                 return kUproarDeviceErr;
318         }
319         err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
320         if (err)
321         {
322                 printf("dealWithDevice: unable to set the configuration\n");
323                 return kUproarDeviceErr;
324         }
326         interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare;    // requested class
327         interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; // requested subclass
328         interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; // requested protocol
329         interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;  // requested alt setting
331         err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
332         if (err)
333         {
334                 printf("dealWithDevice: unable to create interface iterator\n");
335                 return kUproarDeviceErr;
336         }
338         while (usbInterfaceRef = IOIteratorNext(iterator))
339         {
340                 printf("found interface: %#jx\n", (uintmax_t)usbInterfaceRef);
341                 err = dealWithInterface(usbInterfaceRef, device);
342                 IOObjectRelease(usbInterfaceRef);       // no longer need this reference
343                 found = 1;
344         }
346         IOObjectRelease(iterator);
347         iterator = 0;
348         if ((!found) || (err))
349                 return kUproarDeviceErr;
350         else
351                 return kUproarSuccess;
354 static int
355 initDevice(USBDeviceContext *device)
357         mach_port_t            masterPort = 0;
358         kern_return_t          err;
359         CFMutableDictionaryRef matchingDictionary = 0;          // requires <IOKit/IOKitLib.h>
360         short                  idVendor = TopfieldVendorID;
361         short                  idProduct = TF5kProdID;
362         CFNumberRef            numberRef;
363         io_iterator_t          iterator = 0;
364         io_service_t           usbDeviceRef;
365         int                    found =0;
367         err = IOMasterPort(bootstrap_port, &masterPort);                        
369         if (err)
370         {
371                 printf("Anchortest: could not create master port, err = %08x\n", err);
372                 return err;
373         }
374         matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName);  // requires <IOKit/usb/IOUSBLib.h>
375         if (!matchingDictionary)
376         {
377                 printf("Anchortest: could not create matching dictionary\n");
378                 return -1;
379         }
380         numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
381         if (!numberRef)
382         {
383                 printf("Anchortest: could not create CFNumberRef for vendor\n");
384                 return -1;
385         }
386         CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
387         CFRelease(numberRef);
388         numberRef = 0;
389         numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
390         if (!numberRef)
391         {
392                 printf("Anchortest: could not create CFNumberRef for product\n");
393                 return -1;
394         }
395         CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
396         CFRelease(numberRef);
397         numberRef = 0;
399         err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
400         matchingDictionary = 0;                 // this was consumed by the above call
402         while (usbDeviceRef = IOIteratorNext(iterator))
403         {
404                 printf("Found device %#jx\n", (uintmax_t)usbDeviceRef);
405                 err = dealWithDevice(usbDeviceRef, device);
406                 IOObjectRelease(usbDeviceRef);                  // no longer need this reference
407                 found = 1;
408         }
411         IOObjectRelease(iterator);
412         iterator = 0;
413         mach_port_deallocate(mach_task_self(), masterPort);
414         if ((!found) || (err))
415                 return kUproarDeviceErr;
416         else
417                 return kUproarSuccess;
420 @implementation TFUSBController (PrivateMethods)
422 // ---------------------------------------------------------------------------
423 #pragma mark Low-level USB (PrivateMethods)
424 // ---------------------------------------------------------------------------
426 - (NSData*)sendCommand:(NSData*)fullyPackagedCommand
427         toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
428         careForReturn:(BOOL) careFactor
430         IOReturn        err;
431         int cmdLength = [fullyPackagedCommand length];
432         unsigned char outBuffer[cmdLength];
433         memset(outBuffer, 0, cmdLength);
434 //      NSLog(@"send: %@", [fullyPackagedCommand description]);
435         [fullyPackagedCommand getBytes:outBuffer];
436         
437         err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
438         if (err)
439         {
440                 NSLog(@"sendError: %08x\n");
441                 return nil;
442         }
443         
444         if (! getResponse) return nil;
445         
446         int inLen = 0xFFFF; // i think this is biggest needed?
447         unsigned char inBuf[inLen];
448         memset(inBuf, 0, inLen);
449         err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
450         if (err)
451         {
452                 NSLog(@"inError: %08x\n", err);
453                 return nil;
454         }
456         if (! careFactor) return nil;
457         NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
458         data = [dataFormat swap:data];
459         inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
460         return [data subdataWithRange:(NSRange) {0,inLen}];
463 // ---------------------------------------------------------------------------
464 #pragma mark Protocol message sequences (PrivateMethods)
465 // ---------------------------------------------------------------------------
467 - (id) getFileListForPath:(NSString*)path {     
468         if (myContext == nil)
469                 return nil;
471         NSData* hddListCmd = [dataFormat prepareCmdHddDirWithPath:path];
472         if (hddListCmd == nil)
473                 return nil;
475         [self checkUSB:myContext]; // sends cancel and waits for response
476         int contiguousErrors = 0;
477         [[dh fileList] removeAllObjects];
478         NSData* response = [self sendCommand:hddListCmd toDevice:myContext
479                 expectResponse:YES careForReturn:YES];
480         while (response != nil && contiguousErrors++ < 5) {
481                 TopfieldUSBEcode ecode = USB_OK;
482                 if (![dataFormat isCommunicationBlockValid:response error:NULL ecode:&ecode]) {
483                         response = [self sendCommand:[dataFormat prepareFailWithECode:ecode]
484                                 toDevice:myContext expectResponse:YES careForReturn:YES];
485                 } else switch ([dataFormat cmdFromCommunicationBlock:response]) {
486                 case USB_Fail:
487 //                      [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
488                         [[dh fileList] removeAllObjects];
489                         response = [self sendCommand:hddListCmd toDevice:myContext
490                                 expectResponse:YES careForReturn:YES];
491                         break;
492                 case USB_DataHddDir:
493                         contiguousErrors = 0;
494                         size_t i;
495                         // Swapping sometimes adds a byte of padding.  The following uses
496                         // only complete 144-byte structures and so ignores such padding.
497                         for (i=0; 8+(i+1)*114 <= [response length]; i++) {
498                                 NSData* typeFile = [response subdataWithRange:(NSRange) {8+i*114,114}];
499                                 NSMutableDictionary* tfFile = [dh newTFFileFromSwappedHexData:typeFile];
500                                 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
501                                         [dh convertRawDataToUseful:tfFile];
502                                         [[dh fileList] addObject:tfFile];
503                                 }
504                                 [tfFile release];
505                         }
506                         response = [self sendCommand:[dataFormat prepareSuccess]
507                                 toDevice:myContext expectResponse:YES careForReturn:YES];
508                         break;
509                 case USB_DataHddDirEnd:
510                         contiguousErrors = 0;
511                         response = nil;
512                         break;
513                 default:
514                         [self checkUSB:myContext]; // cancel whatever is going on
515                         [[dh fileList] removeAllObjects];
516                         response = [self sendCommand:hddListCmd toDevice:myContext
517                                 expectResponse:YES careForReturn:YES];
518                         break;
519                 }
520         }
522         [tableView reloadData];
523         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
524         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
525         return nil;
528 - (void) turnTurboOn:(BOOL)turnOn {
529         if (![[[self uiElements] isConnected] intValue]) return;
530         [self checkUSB:myContext];
531         NSData* turboCommand = [dataFormat prepareCmdTurboWithMode:(turnOn ? 1 : 0)];
532         [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
535 - (void) renameFile:(NSString*)oldName withName:(NSString*)newName
536         atPath:(NSString*)currentPath
538         NSLog(@"%@,%@,%@", oldName, newName, currentPath);
539         [self checkUSB:myContext];
540         NSString* oldFname = [dataFormat fnameForFile:oldName atPath:currentPath];
541         NSString* newFname = [dataFormat fnameForFile:newName atPath:currentPath];
542         NSData* fileRenCmd = [dataFormat prepareCmdHddRenameFromFname:oldFname
543                 toFname:newFname];
544         [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
547 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
548         [self checkUSB:myContext];
549         NSString* fname = [dataFormat fnameForFile:newName atPath:currentPath];
550         NSData* newFoldCmd = [dataFormat prepareCmdHddCreateDirWithFname:fname];
551         NSData* usbCancel = [dataFormat prepareCancel];
552         [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
553         [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
556 - (void) checkUSB:(USBDeviceContext*)device {
557         NSData* usbCancel = [dataFormat prepareCancel];
558         int retry;
559         for (retry = 0; retry < 8; ++retry) {
560                 NSData* block = [self sendCommand:usbCancel toDevice:device
561                         expectResponse:YES careForReturn:YES];
562                 NSError* error = nil;
563                 if (![dataFormat isCommunicationBlockValid:block
564                                 error:&error ecode:NULL]) {
565                         NSLog(@"bad response to cancel: %@", [error localizedDescription]);
566                         continue;
567                 }
568                 TopfieldUSBCmd cmd = [dataFormat cmdFromCommunicationBlock:block];
569                 if (cmd != USB_Success) {
570                         NSLog(@"response to cancel was %#x rather than success", (unsigned) cmd);
571                         continue;
572                 }
573                 return;
574         }
575         // tell someone that no longer connected here??
578 // ---------------------------------------------------------------------------
579 #pragma mark User interface (PrivateMethods)
580 // ---------------------------------------------------------------------------
582 - (UIElements*) uiElements {
583         return [NSApp delegate];
586 - (void) updateProgress:(NSDictionary*)inDict {
587         NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
588         double offset = [[inDict objectForKey:@"offset"] doubleValue];
589         double size = [[inDict objectForKey:@"size"] doubleValue];
590         NSDate* startTime = [inDict objectForKey:@"startTime"];         
591         [progressBar setDoubleValue:((double)offset/size*100)];
592         [progressBar displayIfNeeded];
593         [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
594         [progressTime displayIfNeeded];
595         [pool release];
598 - (NSString*) elapsedTime:(NSTimeInterval)totalSeconds {
599         char hp = 20;
600         char mp = 20;
601         char sp = 20; //padding
602         int hours = (totalSeconds / 3600);  // returns number of whole hours fitted in totalSecs
603         if (hours < 10)
604                 hp = 48;
605         int minutes = ((totalSeconds / 60) - hours*60);  // Whole minutes
606         if (minutes < 10)
607                 mp = 48;
608         int seconds = ((long) totalSeconds % 60);       // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
609         if (seconds < 10)
610                 sp = 48;
611         return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
614 // ---------------------------------------------------------------------------
615 #pragma mark Queue management (PrivateMethods)
616 // ---------------------------------------------------------------------------
618 - (BOOL) hasPriorityTransfer {
619         BOOL ret;
620         @synchronized (self) {
621                 ret = ([priorityTransferQueue count] > 0);
622         }
623         return ret;
626 - (void) getNextTransfer:(NSDictionary**)transferOut
627         queue:(NSMutableArray**)queueOut
629         NSDictionary* transfer = nil;
630         NSMutableArray* queue = nil;
632         @synchronized (self) {
633                 if ([priorityTransferQueue count] > 0) {
634                         queue = priorityTransferQueue;
635                         transfer = [queue objectAtIndex:0];
636                 } else if ([transferQueue count] > 0) {
637                         queue = transferQueue;
638                         transfer = [queue objectAtIndex:0];
639                 }
641                 // The queue exists as long as the TFUSBController does
642                 // so it need not be retained and autoreleased here.
643                 if (transfer != nil)
644                         [[transfer retain] autorelease];
645         }
647         if (transferOut != NULL)
648                 *transferOut = transfer;
649         if (queueOut != NULL)
650                 *queueOut = queue;
653 - (void) removeTransfer:(NSDictionary*)transfer
654         fromQueue:(NSMutableArray*)queue
656         @synchronized (self) {
657                 [queue removeObjectIdenticalTo:transfer];
658         }
661 @end
663 @implementation TFUSBController
665 // ---------------------------------------------------------------------------
666 #pragma mark Low-level USB
667 // ---------------------------------------------------------------------------
669 - (void) closeDevice:(USBDeviceContext *)device
671         IOUSBInterfaceInterface197 **intf;
672         IOUSBDeviceInterface197    **dev;
673         IOReturn                     err;
675         intf = device->intf;
676         dev = device->dev;
678         err = (*intf)->USBInterfaceClose(intf);
679         if (err)
680         {
681                 printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
682         }
683         err = (*intf)->Release(intf);
684         if (err)
685         {
686                 printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
687         }
689         err = (*dev)->USBDeviceClose(dev);
690         if (err)
691         {
692                 printf("dealWithDevice: error closing device - %08x\n", err);
693                 (*dev)->Release(dev);
694         }
695         err = (*dev)->Release(dev);
696         if (err)
697         {
698                 printf("dealWithDevice: error releasing device - %08x\n", err);
699         }
702 - (USBDeviceContext*) initializeUSB {
703         USBDeviceContext *device;
704         int               err;
705         kBlockSize = 0x08;
706         device = malloc(sizeof(USBDeviceContext));
707         err = initDevice(device);
708         if (err) {
709                 printf("Could not connect to Topfield\n");
710                 return nil;
711         }
712         printf("\n\n");
713         printf("Connected to Topfield\n\n");
714         myContext = device;
715         return device;
718 // ---------------------------------------------------------------------------
719 #pragma mark Protocol message sequences
720 // ---------------------------------------------------------------------------
722 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath
723                 toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset
724                 withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {   
725 //      [progressBar setDoubleValue:0];
726 //      [progressTime setDoubleValue:0];
727         NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
728         NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
730         [[[self uiElements] currentlyField] setStringValue:
731                 [NSLocalizedString(@"DOWNLOADING", @"Downloading: ")
732                         stringByAppendingString:nameOnToppy]];
733         [[[self uiElements] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
734         [[[self uiElements] currentlyField] displayIfNeeded];
736         //prepackage commands to send
737         NSData* fileSendCmd = [dataFormat
738                 prepareCmdHddFileSendWithDirection:USB_FileToHost
739                 fname:[dataFormat fnameForFile:nameOnToppy atPath:currentPath]
740                 offset:offset];
741         NSData* usbSuccess = [dataFormat prepareSuccess];
742         NSData* usbCancel = [dataFormat prepareCancel];
743         
744         // send commands
745         if ([turboCB state]) 
746                 [self turnTurboOn:YES];
747         else
748                 [self checkUSB:myContext]; //turbo has a check itself
750         NSData* receivedBlock = [self sendCommand:fileSendCmd toDevice:myContext
751                 expectResponse:YES careForReturn:YES];
752         NSError* error = nil;
753         if (![dataFormat isCommunicationBlockValid:receivedBlock
754                         error:&error ecode:NULL]) {
755                 NSLog(@"Malformed response from Toppy after %@: %@",
756                         [dataFormat nameOfCmd:USB_CmdHddFileSend],
757                         [error localizedDescription]);
758                 [self turnTurboOn:NO];
759                 return 1;
760         }
761         TopfieldUSBCmd receivedCmd = [dataFormat
762                 cmdFromCommunicationBlock:receivedBlock];
763         if (receivedCmd == USB_Fail) {
764                 TopfieldUSBEcode ecode = [dataFormat ecodeFromFail:receivedBlock];
765                 NSLog(@"Download fails.  Toppy reports error %@",
766                         [dataFormat nameOfEcode:ecode]);
767                 // TODO: retry if USB_Err_CRC?
768                 [self turnTurboOn:NO];
769         } else if (receivedCmd != USB_DataHddFileStart) {
770                 NSLog(@"Unexpected response from Toppy during download.  "
771                        "Expected %@ but received %@",
772                         [dataFormat nameOfCmd:USB_DataHddFileStart],
773                         [dataFormat nameOfCmd:receivedCmd]);
774                 [self turnTurboOn:NO];
775                 return 1;
776         }
778         // start timer
779         NSDate* startTime = [NSDate date];
780         startTime = [startTime addTimeInterval:(0-existingTime)];
781         // acknowledge the USB_DataHddFileStart
782         receivedBlock = [self sendCommand:usbSuccess toDevice:myContext
783                 expectResponse:YES careForReturn:YES];
784         if (![dataFormat isCommunicationBlockValid:receivedBlock
785                         error:&error ecode:NULL]) {
786                 NSLog(@"Malformed block from Toppy after %@: %@",
787                         [dataFormat nameOfCmd:USB_Success],
788                         [error localizedDescription]);
789                 [self turnTurboOn:NO];
790                 return 1;
791         }
792         receivedCmd = [dataFormat cmdFromCommunicationBlock:receivedBlock];
793         if (receivedCmd != USB_DataHddFileData) {
794                 NSLog(@"Unexpected response from Toppy during download.  "
795                        "Expected %@ but received %@",
796                         [dataFormat nameOfCmd:USB_DataHddFileData],
797                         [dataFormat nameOfCmd:receivedCmd]);
798                 [self turnTurboOn:NO];
799                 return 1;
800         }
801         if ([receivedBlock length] < 16) {
802                 NSLog(@"Incorrect Data length in %@",
803                         [dataFormat nameOfCmd:receivedCmd]);
804                 [self turnTurboOn:NO];
805                 return 1;
806         }
807         SInt64 receivedOffset = [dataFormat offsetFromDataHddFileData:receivedBlock];
808         if (receivedOffset != offset) {
809                 NSLog(@"File offset skipped from %lld to %lld!",
810                         offset, receivedOffset);
811                 [self turnTurboOn:NO];
812                 return 1;
813         }
815         // clean up data and prepare path to save it
816         NSData* finalData = [receivedBlock subdataWithRange:(NSRange) {16,[receivedBlock length]-16}];
817         
818         // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
819         if (offset == 0)
820                 [[NSData dataWithBytes:"\0" length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
821         NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
822         [outFile seekToFileOffset:offset];
823         [outFile writeData:finalData];
824         BOOL returnSuccess = YES;
825         if (looping) {  // loop for multiple data sends (ie files > 64k)
826                 int updateRate = 8; 
827                 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
828                         updateRate = 24;
829                         NSLog(@"large file detected - low GUI update rate");
830                 }
832                 // Because this loop usually runs for a long time, use a separate
833                 // NSAutoreleasePool for each iteration.  Keep the current pool
834                 // in a variable outside the loop so that it can be released when
835                 // the loop is left with a break statement.  (@finally is another
836                 // alternative, but it would require extra code in order to avoid
837                 // releasing exception objects too early.)
838                 NSAutoreleasePool* pool = nil;
839                 int dataBlocksReceived = 0;
840                 TopfieldUSBEcode sendEcode = USB_OK;
842                 for (;;) {
843                         if (pool != nil)
844                                 [pool release];
845                         pool = [[NSAutoreleasePool alloc] init];
847                         if (![[[self uiElements] isConnected] boolValue]) {
848                                 NSLog(@"Aborting the transfer because the Toppy has been disconnected.");
849                                 returnSuccess = NO;
850                                 break;
851                         }
852                         if ([self hasPriorityTransfer]) {
853                                 NSLog(@"Suspending the transfer to make way for a priority transfer.");
854                                 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
855                                                 fileInfo,@"filename", currentPath,@"path", savePath,@"savePath",
856                                                 [NSNumber numberWithUnsignedLongLong:[outFile offsetInFile]],@"offset",
857                                                 @"download",@"transferType", [NSNumber numberWithBool:YES],@"looping",
858                                                 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
859                                                 nil]
860                                         atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
861                                 [self sendCommand:usbCancel toDevice:myContext
862                                         expectResponse:YES careForReturn:NO];
863                                 returnSuccess = NO;
864                                 break;
865                         }
867                         NSData* sendBlock;
868                         if (sendEcode == USB_OK)
869                                 sendBlock = usbSuccess;
870                         else
871                                 sendBlock = [dataFormat prepareFailWithECode:sendEcode];
872                         receivedBlock = [self sendCommand:sendBlock toDevice:myContext
873                                 expectResponse:YES careForReturn:YES];
874                         sendEcode = USB_OK;
875                         if (![dataFormat isCommunicationBlockValid:receivedBlock
876                                         error:&error ecode:&sendEcode]) {
877                                 NSLog(@"Malformed block from Toppy after %@: %@",
878                                         [dataFormat nameOfCmd:USB_Success],
879                                         [error localizedDescription]);
880                                 continue;
881                         }
882                         receivedCmd = [dataFormat cmdFromCommunicationBlock:receivedBlock];
883                         if (receivedCmd == USB_DataHddFileData) {
884                                 if ([receivedBlock length] < 16) {
885                                         NSLog(@"Received %@ is too short, only %d bytes",
886                                                 [dataFormat nameOfCmd:receivedCmd],
887                                                 (int) [receivedBlock length]);
888                                         sendEcode = USB_Err_Size;
889                                         continue;
890                                 }
891                                 SInt64 receivedOffset = [dataFormat
892                                         offsetFromDataHddFileData:receivedBlock];
893                                 if (receivedOffset != [outFile offsetInFile]) {
894                                         NSLog(@"File offset skipped from %lld to %lld!",
895                                                 [outFile offsetInFile], receivedOffset);
896                                         returnSuccess = NO;
897                                         break;
898                                 }
899                                 NSData* fileData = [receivedBlock
900                                         subdataWithRange:(NSRange) {16, [receivedBlock length] - 16}];
901                                 [outFile writeData:fileData];
903                                 dataBlocksReceived++;
904                                 if (dataBlocksReceived < 8 || dataBlocksReceived % updateRate == 0) {
905                                         [self performSelectorOnMainThread:@selector(updateProgress:)
906                                                 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
907                                                         [NSNumber numberWithDouble:(double)[outFile offsetInFile]], @"offset",
908                                                         fileSize, @"size", startTime, @"startTime", nil]
909                                                 waitUntilDone:NO
910                                                 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
911                                 }
912                                 continue;
913                         } else if (receivedCmd == USB_DataHddFileEnd) {
914                                 [self sendCommand:usbSuccess toDevice:myContext
915                                         expectResponse:NO careForReturn:NO];
916                                 returnSuccess = YES;
917                                 break;
918                         } else {
919                                 NSLog(@"Unexpected response from Toppy during download.  "
920                                            "Expected %@ but received %@",
921                                         [dataFormat nameOfCmd:USB_DataHddFileData],
922                                         [dataFormat nameOfCmd:receivedCmd]);
923                                 [self sendCommand:usbCancel toDevice:myContext
924                                         expectResponse:YES careForReturn:NO];
925                                 returnSuccess = NO;
926                                 break;
927                         }
928                         // not reached
929                 } // for ever
931                 if (pool != nil)
932                         [pool release];
933         } // if looping
935         [outFile synchronizeFile];
936         [outFile closeFile];
937         //now add the right modification date to the file
938         [[NSFileManager defaultManager]
939                 changeFileAttributes:[NSDictionary
940                         dictionaryWithObject:[fileInfo objectForKey:@"date"]
941                         forKey:@"NSFileModificationDate"]
942                 atPath:savePath];
943         [self turnTurboOn:NO];
944         if (looping) [[self uiElements] finishTransfer];
945         return returnSuccess ? 1 : 0;
948 - (void) uploadFile:(NSString*)fileToUpload ofSize:(long long)size
949         fromPath:(NSString*)curPath withAttributes:(NSData*)typeFile
950         atOffset:(unsigned long long)offset
951         existingTime:(NSTimeInterval)existingTime
953         NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
954         USBDeviceContext* dev = myContext;
955         // prepare Send command
956         NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
957         NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
958         char dir = USB_FileToDevice;
959         NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
960         short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
961         const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
962         [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
963         NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
964         [build appendData:d];
965         dir = 0x5c; // 0x5c = "/"
966         [build appendData:[NSData dataWithBytes:&dir length:1]];
967         [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
968         if ([fname length] < 95)
969                 [build increaseLengthBy:95-[fname length]];
970         dir = 0x00;
971         [build appendData:[NSData dataWithBytes:&dir length:1]];
972         UInt64 offset_bigendian = EndianU64_NtoB(offset);
973         [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
974         
975         // prepackage commands to send
976         NSData* fileSendCmd = [dataFormat prepareCommand:USB_CmdHddFileSend
977                 withData:build];
978         NSData* fileStartCmd = [dataFormat prepareCommand:USB_DataHddFileStart
979                 withData:typeFile];
980         NSData* fileEndCmd = [dataFormat prepareDataHddFileEnd];
982         //start timer
983         NSDate* startTime = [NSDate date];
984         startTime = [startTime addTimeInterval:(0-existingTime)];
985         //send commands
986         if ([turboCB state]) 
987                 [self turnTurboOn:YES];
988         else
989                 [self checkUSB:dev];
990         // now the proper commands
991         NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
992         data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
993         const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
994         NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
995         NSData* responseToCheck = nil;
997         NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
998         [fileHandle seekToFileOffset:offset];
999         do {
1000                 if ([self hasPriorityTransfer]) {
1001                         //break out and create a new transfer to continue it
1002                         NSLog(@"pausing upload");
1003                         [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
1004                                         fileToUpload,@"filename",
1005                                         [NSNumber numberWithUnsignedLongLong:size],@"fileSize",
1006                                         curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",
1007                                         [NSNumber numberWithUnsignedLongLong:offset],@"offset",
1008                                         [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
1009                                         nil]
1010                                 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
1011                         [self turnTurboOn:NO];
1012                         break;
1013                 } else {
1014                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1015                 offset_bigendian = EndianU64_NtoB(offset);
1016                 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
1017                 [fileData appendData:[fileHandle readDataOfLength:rate]];
1018                 offset += rate;
1019                 NSData* fileDataCmd = [dataFormat prepareCommand:USB_DataHddFileData
1020                         withData:fileData];
1021                 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
1022                 if ([data length] >= 8) 
1023                         responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
1024                 else responseToCheck = nil;
1025                 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck]) 
1026                         break;
1027                 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
1028                         withObject:[NSDictionary dictionaryWithObjectsAndKeys:
1029                                 [NSNumber numberWithDouble:(double)offset], @"offset",
1030                                 [NSNumber numberWithDouble:(double)size], @"size",
1031                                 startTime, @"startTime", nil]];
1032                 [pool release];
1033                 }
1034         } while (offset < size && [[[self uiElements] isConnected] intValue]);
1035         if ([[[self uiElements] isConnected] intValue])
1036                 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
1037         [fileHandle closeFile]; 
1038         [[self uiElements] goToPath:[[self uiElements]currentPath]];
1039         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
1040         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
1041         [self turnTurboOn:NO];
1042         [[self uiElements] finishTransfer];
1045 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
1046         [self checkUSB:myContext];
1047         NSString* fname = [dataFormat fnameForFile:[fileInfo objectForKey:@"name"]
1048                 atPath:currentPath];
1049         NSData* fileDelCmd = [dataFormat prepareCmdHddDelWithFname:fname];
1050         [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
1053 // ---------------------------------------------------------------------------
1054 #pragma mark Queue management
1055 // ---------------------------------------------------------------------------
1057 - (void) addPriorityTransfer:(id)newTransfer {
1058         @synchronized (self) {
1059                 [priorityTransferQueue addObject:newTransfer];
1060                 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
1061         }
1064 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
1065         @synchronized (self) {
1066                 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
1067                 else [transferQueue addObject:newTransfer];
1068                 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1069         }
1072 - (void) clearQueues {
1073         @synchronized (self) {
1074                 [priorityTransferQueue removeAllObjects];
1075                 [transferQueue removeAllObjects];
1076                 [pausedQueue removeAllObjects];
1077         }
1080 - (BOOL) hasCurrentTransfer {
1081         BOOL ret;
1082         @synchronized (self) {
1083                 ret = ([transferQueue count] > 0);
1084         }
1085         return ret;
1088 - (id) currentTransferInfo {
1089         id transfer = nil;
1090         @synchronized (self) {
1091                 if ([transferQueue count] > 0) {
1092                         transfer = [transferQueue objectAtIndex:0];
1093                         // Make a copy of the object so that it can be safely manipulated
1094                         // in a different thread from the original.
1095                         transfer = [[transfer copy] autorelease];
1096                 }
1097         }
1098         return transfer;
1101 - (id) firstPausedTransferInfo {
1102         id transfer = nil;
1103         @synchronized (self) {
1104                 if ([pausedQueue count] > 0) {
1105                         transfer = [pausedQueue objectAtIndex:0];
1106                         // Make a copy of the object so that it can be safely manipulated
1107                         // in a different thread from the original.
1108                         transfer = [[transfer copy] autorelease];
1109                 }
1110         }
1111         return transfer;
1114 - (void) transfer:(id)sender {
1115         while ([[[self uiElements] isConnected] intValue]) {
1116                 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
1117                 NSDictionary* currentTransfer = nil;
1118                 NSMutableArray* queue = nil;
1119                 [self getNextTransfer:&currentTransfer queue:&queue];
1120                 if (currentTransfer != nil) {
1121                         NSString* transferType = [currentTransfer objectForKey:@"transferType"];
1122                         if ([transferType isEqualToString:@"fileList"]) {
1123                                 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
1124                         }
1125                         else if ([transferType isEqualToString:@"turbo"]) {
1126                 //              [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
1127                         }
1128                         else if ([transferType isEqualToString:@"rename"]) {
1129                                 [self renameFile:[currentTransfer objectForKey:@"oldName"]
1130                                         withName:[currentTransfer objectForKey:@"newName"]
1131                                         atPath:[currentTransfer objectForKey:@"path"]];
1132                         }
1133                         else if ([transferType isEqualToString:@"newFolder"]) {
1134                                 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
1135                         }
1136                         else if ([transferType isEqualToString:@"delete"]) {
1137                                 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
1138                         }
1139                         else if ([transferType isEqualToString:@"pause"]) {
1140                                 @synchronized (self) {
1141                                         if ([transferQueue count] != 0) {
1142                                                 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:
1143                                                         [NSPredicate predicateWithFormat:@"(filename = %@)",
1144                                                                 [currentTransfer objectForKey:@"filename"]]];
1145                                                 if ([toPause count] > 1) NSLog(@"multiple pauses?");
1146                                                 else {
1147                                                         [pausedQueue addObjectsFromArray:toPause];
1148                                                         [transferQueue removeObjectsInArray:toPause];
1149                                                 }
1150                                         }
1151                                 }
1152                         }
1153                         else if ([transferType isEqualToString:@"resume"]) {
1154                                 @synchronized (self) {
1155                                         if ([pausedQueue count] != 0) {
1156                                                 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:
1157                                                         [NSPredicate predicateWithFormat:@"(filename = %@)",
1158                                                                 [currentTransfer objectForKey:@"filename"]]];
1159                                                 if ([toResume count] > 1) NSLog(@"multiple resumes?");
1160                                                 else {
1161                                                         [transferQueue addObjectsFromArray:toResume];
1162                                                         [pausedQueue removeObjectsInArray:toResume];
1163                                                 }
1164                                         }
1165                                 }
1166                         }
1167                         else if ([transferType isEqualToString:@"download"]) {
1168                                 [self getFile:[currentTransfer objectForKey:@"filename"]
1169                                         forPath:[currentTransfer objectForKey:@"path"]
1170                                         toSaveTo:[currentTransfer objectForKey:@"savePath"]
1171                                         beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1172                                         withLooping:[[currentTransfer objectForKey:@"looping"] boolValue]
1173                                         existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1174                         }
1175                         else if ([transferType isEqualToString:@"upload"]) {
1176                                 [self uploadFile:[currentTransfer objectForKey:@"filename"]
1177                                         ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue]
1178                                         fromPath:[currentTransfer objectForKey:@"path"]
1179                                         withAttributes:[currentTransfer objectForKey:@"attributes"]
1180                                         atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1181                                         existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1182                         }
1183                         else {
1184                                 NSLog(@"Unrecognized transfer type: %@", transferType);
1185                         }
1186                         [self removeTransfer:currentTransfer fromQueue:queue];
1187                 }
1188                 else usleep(100);
1189                 [pool release];
1190         }
1193 // ---------------------------------------------------------------------------
1194 #pragma mark User interface
1195 // ---------------------------------------------------------------------------
1197 - (void) setProgressBar:(NSProgressIndicator*)bar
1198         time:(NSTextField*)timeField
1199         turbo:(NSButton*)turbo
1201         progressBar = bar;
1202         progressTime = timeField;
1203         turboCB = turbo;
1206 - (void) setDH:(id)newDH tableView:(id)tv {
1207         dh = newDH;
1208         tableView = tv;
1211 // ---------------------------------------------------------------------------
1212 #pragma mark Miscellaneous
1213 // ---------------------------------------------------------------------------
1215 - (id) init {
1216         if (self = [super init]) {
1217                 debug = 0;
1218                 rate = 0xfe00;
1219                 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1220                 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1221                 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1222                 dataFormat = [TFDataFormat new];
1223         }
1224         return self;
1227 - (void) dealloc {
1228         [dataFormat release];
1229         [super dealloc];
1232 - (int) getSpeed {
1233         return connectedSpeed;
1236 - (void) setDebug:(int)mode {
1237         debug = mode;
1238         NSLog(@"Debug level: %i", debug);
1241 - (void) setRate:(int)newRate {
1242         rate = newRate;
1243         NSLog(@"New rate set: %i", rate);
1246 @end