Simplify prepareCommand:withData:.
[MacTF.git] / TFUSBController.m
blob0942e6847d02508a4dc0abdf4150eafad1bdc134
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>
14 #import "TFUSBController.h"
15 #import "UIElements.h"
17 @interface TFUSBController (PrivateMethods)
18 - (UInt32) checkCommunicationBlock:(NSData*)block;
19 - (UInt32) cmdFromCommunicationBlock:(NSData*)block;
20 - (NSData*) prepareFailWithECode:(UInt32)eCode;
21 - (NSData*) prepareCmdHddDirWithPath:(NSString*)path;
22 - (UIElements*) uiElements;
23 @end
25 @implementation TFUSBController
28 /* 
29  *
30  * This source code is free software; you can redistribute it and/or
31  * modify it under the terms of the GNU Public License as published
32  * by the Free Software Foundation; either version 2 of the License,
33  * or (at your option) any later version.
34  *
35  * This source code is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
38  * Please refer to the GNU Public License for more details.
39  *
40  * You should have received a copy of the GNU Public License along with
41  * this source code; if not, write to:
42  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
43  */
47 /* Much of the code that initializes and closes the device is from Apple
48  * and is released under the license below.
49  */
52  * © Copyright 2001 Apple Computer, Inc. All rights reserved.
53  *
54  * IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in 
55  * consideration of your agreement to the following terms, and your use, installation, 
56  * modification or redistribution of this Apple software constitutes acceptance of these
57  * terms.  If you do not agree with these terms, please do not use, install, modify or 
58  * redistribute this Apple software.
59  *
60  * In consideration of your agreement to abide by the following terms, and subject to these 
61  * terms, Apple grants you a personal, non exclusive license, under Apple’s copyrights in this 
62  * original Apple software (the “Apple Software”), to use, reproduce, modify and redistribute 
63  * the Apple Software, with or without modifications, in source and/or binary forms; provided 
64  * that if you redistribute the Apple Software in its entirety and without modifications, you 
65  * must retain this notice and the following text and disclaimers in all such redistributions 
66  * of the Apple Software.  Neither the name, trademarks, service marks or logos of Apple 
67  * Computer, Inc. may be used to endorse or promote products derived from the Apple Software 
68  * without specific prior written permission from Apple. Except as expressly stated in this 
69  * notice, no other rights or licenses, express or implied, are granted by Apple herein, 
70  * including but not limited to any patent rights that may be infringed by your derivative 
71  * works or by other works in which the Apple Software may be incorporated.
72  * 
73  * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES, 
74  * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
75  * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE 
76  * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
77  *
78  * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL 
79  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
80  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, 
81  * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND 
82  * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR 
83  * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
84  */                     
87 #define         TopfieldVendorID        4571
88    //1317 for HC
89    //0x138c for humax
90    //4571 for toppy
91 #define         TF5kProdID      4096
92 //42514 for HC
93 //0x02ad for humax
94 //4096 for toppy
96 // ---------------------------------------------------------------------------
97 // Low-level USB
98 // ---------------------------------------------------------------------------
100 int doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
101                 UInt8 *outBuf, UInt32 len, int type) 
103         IOReturn        err;
104         UInt32          sendLen;        
105         
106                 if (debug == 1){
107                         printf(("sending:\n"));
108             hexDump(outBuf, len);
109                 }
110                 sendLen = ((len/kBlockSize))*kBlockSize;
111         if (len % kBlockSize)
112             sendLen += kBlockSize;
113         if ((sendLen % 0x200) == 0)  
114                 sendLen += kBlockSize;
116         err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
117         if (err) {
118                 printf("write err: %08x\n", err);
119                 return err;
120         }
121         return err;
124 int doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
125             UInt8 *inBuf, UInt32 dataLen, int type) {
126         IOReturn        err;
127         UInt32          len;
128         
129         if (dataLen > kMaxXferSize) return 1;
130         
131         len = (dataLen/kBlockSize) * kBlockSize;
132         if (dataLen % kBlockSize)
133                 len += kBlockSize;
135                 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len,  1000, 20000);
136                 if (err) {
137                 printf("read err 2: %08x\n", err);
138                                 printf("resetting\n");
139                                 err = (*intf)->ClearPipeStallBothEnds(intf, 2); 
140                         return err;
141         }
142                 
143         if (debug == 1) {
144           printf(("receiving: \n")); 
145                         hexDump(inBuf, len);
146                 }
147         return err;
150 int dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
152     IOReturn                            err;
153     IOCFPlugInInterface                 **iodev;                // requires <IOKit/IOCFPlugIn.h>
154     IOUSBInterfaceInterface197          **intf;
155     IOUSBDeviceInterface197             **dev;
156     SInt32                              score;
157         UInt8                           numPipes, confNum, dSpeed;
160     err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
161     if (err || !iodev)
162     {
163         printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
164         return kUproarDeviceErr;
165     }
166     err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
167     (*iodev)->Release(iodev);                           // done with this
168     intf = device->intf;
169     if (err || !intf)
170     {
171         printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
172         return kUproarDeviceErr;
173     }
174     
175     dev = device->dev;
176     err = (*intf)->USBInterfaceOpen(intf);
177     if (err)
178     {
179         printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
180         return kUproarDeviceErr;
181     }
182     err = (*intf)->GetNumEndpoints(intf, &numPipes);
183     if (err)
184     {
185         printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
186         return kUproarDeviceErr;
187     }
188     
189     printf("dealWithInterface: found %d pipes\n", numPipes);
191     err = (*intf)->GetConfigurationValue(intf, &confNum);
192     err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
193         if (dSpeed == 2) {
194                 kBlockSize = 0x40;
195         } else {
196                 kBlockSize = 0x4;
197         }
198     printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
199     connectedSpeed = dSpeed;
200     return kUproarSuccess;
204 int dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
206     IOReturn                            err;
207     IOCFPlugInInterface                 **iodev;                // requires <IOKit/IOCFPlugIn.h>
208     IOUSBDeviceInterface197             **dev;
209     SInt32                              score;
210     UInt8                               numConf;
211     IOUSBConfigurationDescriptorPtr     confDesc;
212     IOUSBFindInterfaceRequest           interfaceRequest;
213     io_iterator_t                       iterator;
214     io_service_t                        usbInterfaceRef;
215     int                                 found=0;
216     
217     
218     err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
219     if (err || !iodev)
220     {
221         printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
222         return kUproarDeviceErr;
223     }
224     err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
225     (*iodev)->Release(iodev);                           
226     
227     dev = device->dev;
228     if (err || !dev)
229     {
230         printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
231         return kUproarDeviceErr;
232     }
233     err = (*dev)->USBDeviceOpen(dev);
234     if (err)
235     {
236         printf("dealWithDevice: unable to open device. ret = %08x\n", err);
237         return kUproarDeviceErr;
238     }
239     err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
240     if (err || !numConf)
241     {
242         printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
243         return kUproarDeviceErr;
244     }
245     printf("dealWithDevice: found %d configurations\n", numConf);
246     err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc);     // get the first config desc (index 0)
247     if (err)
248     {
249         printf("dealWithDevice:unable to get config descriptor for index 0\n");
250         return kUproarDeviceErr;
251     }
252     err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
253     if (err)
254     {
255         printf("dealWithDevice: unable to set the configuration\n");
256         return kUproarDeviceErr;
257     }
259     interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare;             // requested class
260     interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;          // requested subclass
261     interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;          // requested protocol
262     interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;           // requested alt setting
263     
264     err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
265     if (err)
266     {
267         printf("dealWithDevice: unable to create interface iterator\n");
268         return kUproarDeviceErr;
269     }
270     
271     while (usbInterfaceRef = IOIteratorNext(iterator))
272     {
273         printf("found interface: %p\n", (void*)usbInterfaceRef);
274         err = dealWithInterface(usbInterfaceRef, device);
275         IOObjectRelease(usbInterfaceRef);       // no longer need this reference
276         found = 1;
277     }
278     
279     IOObjectRelease(iterator);
280     iterator = 0;
281     if ((!found) || (err))
282         return kUproarDeviceErr;
283     else
284         return kUproarSuccess;
288 int initDevice(USBDeviceContext *device){
289     mach_port_t         masterPort = 0;
290     kern_return_t               err;
291     CFMutableDictionaryRef      matchingDictionary = 0;         // requires <IOKit/IOKitLib.h>
292     short                       idVendor = TopfieldVendorID;
293     short                       idProduct = TF5kProdID;
294     CFNumberRef                 numberRef;
295     io_iterator_t               iterator = 0;
296     io_service_t                usbDeviceRef;
297     int                         found =0;
298     
299     err = IOMasterPort(bootstrap_port, &masterPort);                    
300     
301     if (err)
302     {
303         printf("Anchortest: could not create master port, err = %08x\n", err);
304         return err;
305     }
306     matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName);      // requires <IOKit/usb/IOUSBLib.h>
307     if (!matchingDictionary)
308     {
309         printf("Anchortest: could not create matching dictionary\n");
310         return -1;
311     }
312     numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
313     if (!numberRef)
314     {
315         printf("Anchortest: could not create CFNumberRef for vendor\n");
316         return -1;
317     }
318     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
319     CFRelease(numberRef);
320     numberRef = 0;
321     numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
322     if (!numberRef)
323     {
324         printf("Anchortest: could not create CFNumberRef for product\n");
325         return -1;
326     }
327     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
328     CFRelease(numberRef);
329     numberRef = 0;
330     
331     err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
332     matchingDictionary = 0;                     // this was consumed by the above call
333     
334     while (usbDeviceRef = IOIteratorNext(iterator))
335     {
336         printf("Found device %p\n", (void*)usbDeviceRef);
337         err = dealWithDevice(usbDeviceRef, device);
338         IOObjectRelease(usbDeviceRef);                  // no longer need this reference
339         found = 1;
340     }
341     
342     
343     IOObjectRelease(iterator);
344     iterator = 0;
345     mach_port_deallocate(mach_task_self(), masterPort);
346     if ((!found) || (err))
347         return kUproarDeviceErr;
348     else
349         return kUproarSuccess;
353 - (void) closeDevice:(USBDeviceContext *) device
355     IOUSBInterfaceInterface197  **intf;
356     IOUSBDeviceInterface197     **dev;
357     IOReturn                    err;
358     
359     intf = device->intf;
360     dev = device->dev;
361     
362     err = (*intf)->USBInterfaceClose(intf);
363     if (err)
364     {
365         printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
366     }
367     err = (*intf)->Release(intf);
368     if (err)
369     {
370         printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
371     }
372     
373     err = (*dev)->USBDeviceClose(dev);
374     if (err)
375     {
376         printf("dealWithDevice: error closing device - %08x\n", err);
377         (*dev)->Release(dev);
378     }
379     err = (*dev)->Release(dev);
380     if (err)
381     {
382         printf("dealWithDevice: error releasing device - %08x\n", err);
383     }
386 - (USBDeviceContext*) initializeUSB {
387         USBDeviceContext        *device;
388         int                     err;
389         kBlockSize = 0x08;
390         device = malloc(sizeof(USBDeviceContext));
391         err = initDevice(device);
392         if (err) {
393                 printf("Could not connect to Topfield\n");
394                 return nil;
395         }
396         printf("\n\n");
397         printf("Connected to Topfield\n\n");
398         myContext = device;
399         return device;
402 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand
403                 toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
404                 careForReturn:(BOOL) careFactor{
405         IOReturn        err;
406         int cmdLength = [fullyPackagedCommand length];
407         unsigned char outBuffer[cmdLength];
408         memset(outBuffer, 0, cmdLength);
409 //      NSLog(@"send: %@", [fullyPackagedCommand description]);
410         [fullyPackagedCommand getBytes:outBuffer];
411         
412         err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
413         if (err)
414                 NSLog(@"sendError: %08x\n");
415         
416         if (! getResponse) return nil;
417         
418         int inLen = 0xFFFF; // i think this is biggest needed?
419         unsigned char inBuf[inLen];
420         memset(inBuf, 0, inLen);
421         err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
422         if (err)
423                 NSLog(@"inError: %08x\n", err);
424         
425         if (! careFactor) return nil;
426         NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
427         data = [self swap:data];
428         inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
429         return [data subdataWithRange:(NSRange) {0,inLen}];
432 // ---------------------------------------------------------------------------
433 // Framing of communication blocks
434 // ---------------------------------------------------------------------------
436 -(UInt16) findCRC:(const UInt8*)p length:(size_t) n
438         UInt16 m_crc16 = 0x0000;
439         static const UInt16 crc16Tbl[256] = {
440                 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
441                 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
442                 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
443                 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
444                 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
445                 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
446                 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
447                 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
448                 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
449                 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
450                 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
451                 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
452                 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
453                 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
454                 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
455                 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
456                 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
457                 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
458                 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
459                 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
460                 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
461                 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
462                 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
463                 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
464                 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
465                 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
466                 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
467                 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
468                 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
469                 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
470                 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
471                 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
472         };
473         size_t i;       
474         for(i = 0; i < n; i++)
475                         m_crc16 = (m_crc16 >> 8) ^ crc16Tbl[*p++ ^ (m_crc16 & 0xFF)];
476         return m_crc16;
479 -(NSMutableData*) swap: (NSData*) inData {
480         int len = [inData length];
481         int uneven = 0;
482         if (len % 2 != 0) {
483                 uneven = 1; // prepare space for padding
484         }
485         char *array;
486         char *outarray;
487         if ((array = calloc((len + uneven), sizeof(char))) == NULL) {
488                 NSLog(@"ERROR: Malloc failed");
489                 return [NSMutableData dataWithData:inData];
490         }
491         if ((outarray = calloc((len + uneven), sizeof(char))) == NULL) {
492                 NSLog(@"ERROR: Malloc failed");
493                 return [NSMutableData dataWithData:inData];
494         }
495         [inData getBytes:array length:len];
496         swab(array, outarray, len+uneven);
497         NSMutableData* ret = [NSMutableData dataWithCapacity:len+uneven];
498         [ret setData:[NSData dataWithBytes:outarray length:len+uneven]];
499         free(array);
500         free(outarray);
501         return ret; 
504 - (NSData*) prepareCommand:(UInt32)cmd withData:(NSData*) inData {
505         NSMutableData* block = [NSMutableData dataWithLength:8];
506         if (inData != nil)
507                 [block appendData:inData];
508         size_t blockLength = [block length];
509         if (blockLength > 0xFFFF)
510                 return nil; // wouldn't fit in UInt16
512         UInt32 cmd_bigendian = EndianU32_NtoB(cmd);
513         [block replaceBytesInRange:(NSRange){4,4} withBytes:&cmd_bigendian];
515         UInt16 crc = [self findCRC:(const UInt8*)[block bytes]+4 length:blockLength-4];
516         UInt16 crc_bigendian = EndianU16_NtoB(crc);
517         [block replaceBytesInRange:(NSRange){2,2} withBytes:&crc_bigendian];
519         UInt16 blockLength_bigendian = EndianU16_NtoB(blockLength);
520         [block replaceBytesInRange:(NSRange){0,2} withBytes:&blockLength_bigendian];
522         return [self swap:block];
525 // Check the size and CRC of a received communication block.
526 // Return an ECODE to be sent back, or USB_OK.
527 - (UInt32) checkCommunicationBlock:(NSData*)block {
528         if (block == nil) return USB_Err_RunFail;
529         size_t length = [block length];
530         if (length < 8) return USB_Err_Size;
531         UInt16 crc16_bigendian;
532         [block getBytes:&crc16_bigendian range:(NSRange){2,2}];
533         UInt16 crc = EndianU16_BtoN(crc16_bigendian);
534         UInt16 calcCrc = [self findCRC:(const UInt8*)[block bytes]+4 length:length-4];
535         if (crc != calcCrc) return USB_Err_Crc;
536         return USB_OK;
539 - (UInt32) cmdFromCommunicationBlock:(NSData*)block {
540         UInt32 cmd_bigendian;
541         [block getBytes:&cmd_bigendian range:(NSRange){4,4}];
542         return EndianU32_BtoN(cmd_bigendian);
545 // ---------------------------------------------------------------------------
546 // Preparing communication blocks for various commands
547 // ---------------------------------------------------------------------------
549 - (NSData*) prepareFailWithECode:(UInt32)eCode {
550         const UInt32 eCode_bigendian = EndianU32_NtoB(eCode);
551         NSData* data = [NSData dataWithBytes:&eCode_bigendian length:4];
552         if (data == nil) // out of memory presumably
553                 return nil;
554         return [self prepareCommand:USB_Fail withData:data];
557 // Prepare a communication block that asks the Toppy to list the files
558 // in a directory.  Return the communication block, or nil on error.
559 - (NSData*) prepareCmdHddDirWithPath:(NSString*)path {
560         NSData *pathData = [path dataUsingEncoding:NSISOLatin1StringEncoding];
561         if (pathData == nil) // path contains characters unsupported by encoding
562                 return nil;
563         NSMutableData *cmdData = [NSMutableData dataWithData:pathData];
564         [cmdData increaseLengthBy:1]; // not sure if need to pad here?
565         return [self prepareCommand:USB_CmdHddDir withData:cmdData];
568 // ---------------------------------------------------------------------------
569 // Protocol message sequences
570 // ---------------------------------------------------------------------------
572 - (id) getFileListForPath:(NSString*) path {    
573         if (myContext == nil)
574                 return nil;
576         NSData* hddListCmd = [self prepareCmdHddDirWithPath:path];
577         if (hddListCmd == nil)
578                 return nil;
580         [self checkUSB:myContext]; // sends cancel and waits for response
581         int contiguousErrors = 0;
582         [[dh fileList] removeAllObjects];
583         NSData* response = [self sendCommand:hddListCmd toDevice:myContext
584                 expectResponse:YES careForReturn:YES];
585         while (response != nil && contiguousErrors++ < 5) {
586                 const UInt32 eCode = [self checkCommunicationBlock:response];
587                 if (eCode != USB_OK) {
588                         response = [self sendCommand:[self prepareFailWithECode:eCode]
589                                 toDevice:myContext expectResponse:YES careForReturn:YES];
590                 } else switch ([self cmdFromCommunicationBlock:response]) {
591                 case USB_Fail:
592 //                      [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
593                         [[dh fileList] removeAllObjects];
594                         response = [self sendCommand:hddListCmd toDevice:myContext
595                                 expectResponse:YES careForReturn:YES];
596                         break;
597                 case USB_DataHddDir:
598                         contiguousErrors = 0;
599                         size_t i;
600                         // Swapping sometimes adds a byte of padding.  The following uses
601                         // only complete 144-byte structures and so ignores such padding.
602                         for (i=0; 8+(i+1)*114 <= [response length]; i++) {
603                                 NSData* typeFile = [response subdataWithRange:(NSRange) {8+i*114,114}];
604                                 NSMutableDictionary* tfFile = [dh getTFFileFromSwappedHexData:typeFile];
605                                 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
606                                         [dh convertRawDataToUseful:tfFile];
607                                         [[dh fileList] addObject:tfFile];
608                                 }
609                         }
610                         response = [self sendCommand:[self prepareCommand:USB_Success withData:nil]
611                                 toDevice:myContext expectResponse:YES careForReturn:YES];
612                         break;
613                 case USB_DataHddDirEnd:
614                         contiguousErrors = 0;
615                         response = nil;
616                         break;
617                 default:
618                         [self checkUSB:myContext]; // cancel whatever is going on
619                         [[dh fileList] removeAllObjects];
620                         response = [self sendCommand:hddListCmd toDevice:myContext
621                                 expectResponse:YES careForReturn:YES];
622                         break;
623                 }
624         }
626         [tableView reloadData];
627         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
628         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
629         return nil;
632 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath
633                 toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset
634                 withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {   
635 //      [progressBar setDoubleValue:0];
636 //      [progressTime setDoubleValue:0];
637         NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
638         [[[self uiElements] currentlyField] setStringValue:
639                 [NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
640         [[[self uiElements] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
641         [[[self uiElements] currentlyField] displayIfNeeded];
642         //construct file send request
643         NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
644         NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
645         char dir = USB_FileToHost;
646         NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
647         short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
648         const SInt16 nsize_bigendian = EndianS16_NtoB(nsize);
649         [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
650         NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
651         [build appendData:d];   
652         dir = 0x5c; // 0x5c = "/"
653         [build appendData:[NSData dataWithBytes:&dir length:1]];
654         [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
655         dir = 0x00;
656         [build appendData:[NSData dataWithBytes:&dir length:1]];
657         //add 8 byte offset here
658         const UInt64 offset_bigendian = EndianU64_NtoB(offset);
659         [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
660         
661         //prepackage commands to send
662         NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
663         NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
664         NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
665         
666         // send commands
667         
668         if ([turboCB state]) 
669                 [self turnTurboOn:YES];
670         else
671                 [self checkUSB:myContext]; //turbo has a check itself
672         [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
673         //start timer
674         NSDate* startTime = [NSDate date];
675         startTime = [startTime addTimeInterval:(0-existingTime)];
676         // send start request and get response
677         NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
678         const UInt32 rW_bigendian = EndianU32_NtoB(USB_DataHddFileData);
679         NSData *responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
680         if ([data length] < 16) {
681                 NSLog(@"Incorrect Data length");
682                 return 1;
683         }
684         NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
685         if (![responseWanted isEqualToData:responseToCheck]) {
686                 NSLog(@"Unexpected response from Toppy during download");
687                 return 1;
688         }
689         
690         // clean up data and prepare path to save it
691         NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
692         NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
693         
694         // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
695         if (offset == 0)
696                 [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
697         NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
698         [outFile seekToFileOffset:offset];
699         [outFile writeData:finalData];
700         if (looping) {  // loop for multiple data sends (ie files > 64k)
701                 int updateRate = 8; 
702                 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
703                         updateRate = 24;
704                         NSLog(@"large file detected - low GUI update rate");
705                 }
706                 int timesAround = 0;
707                 const UInt32 test_bigendian = EndianU32_NtoB(USB_DataHddFileData);
708                 double amountReceived = 0xfe00 + offset;
709                 NSData* testData = [NSData dataWithBytes:&test_bigendian length:4];
710                 while ([header isEqualToData:testData] && [[[self uiElements] isConnected] intValue]) {
711                         if ([priorityTransferQueue count] != 0) {
712                                 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
713                                                 fileInfo,@"filename", currentPath,@"path", savePath,@"savePath",
714                                                 [NSNumber numberWithUnsignedLongLong:amountReceived],@"offset",
715                                                 @"download",@"transferType", [NSNumber numberWithBool:YES],@"looping",
716                                                 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
717                                                 nil]
718                                         atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
719                                 // send reset again     just to make sure!
720                                 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
721                                 [outFile closeFile];
722                                 //now add the right modification date to the file
723                                 [[NSFileManager defaultManager]
724                                         changeFileAttributes:[NSDictionary
725                                                 dictionaryWithObject:[fileInfo objectForKey:@"date"]
726                                                 forKey:@"NSFileModificationDate"]
727                                         atPath:savePath];
728                                 [self turnTurboOn:NO];
729                                 return 0;
730                         }
731                         timesAround ++;
732                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];                             
733                         data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
734                         amountReceived += 0xfe00;
735                         if (timesAround < 8 || timesAround % updateRate == 0) {
736                                 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
737                                         withObject:[NSDictionary dictionaryWithObjectsAndKeys:
738                                                 [NSNumber numberWithDouble:(double)amountReceived], @"offset",
739                                                 fileSize, @"size", startTime, @"startTime", nil]];
740                         }
741                         if ([data length] > 16){ //there is something to read 
742                                 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
743                                 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
744                                 [outFile writeData:finalData];
745                                 [outFile synchronizeFile];
746                         } 
747                         else break;
748                         [pool release];
749                 }
750         }
751         [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again        just to make sure!
752         [outFile closeFile];
753     //now add the right modification date to the file
754         [[NSFileManager defaultManager]
755                 changeFileAttributes:[NSDictionary
756                         dictionaryWithObject:[fileInfo objectForKey:@"date"]
757                         forKey:@"NSFileModificationDate"]
758                 atPath:savePath];
759         [self turnTurboOn:NO];
760         if (looping) [[self uiElements] finishTransfer];
761         return 0;
764 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size
765                 fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile
766                 atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
767         NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
768         USBDeviceContext* dev = myContext;
769         // prepare Send command
770         NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
771         NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
772         char dir = USB_FileToDevice;
773         NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
774         short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
775         const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
776         [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
777         NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
778         [build appendData:d];
779         dir = 0x5c; // 0x5c = "/"
780         [build appendData:[NSData dataWithBytes:&dir length:1]];
781         [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
782         if ([fname length] < 95)
783                 [build increaseLengthBy:95-[fname length]];
784         dir = 0x00;
785         [build appendData:[NSData dataWithBytes:&dir length:1]];
786         UInt64 offset_bigendian = EndianU64_NtoB(offset);
787         [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
788         
789         // prepackage commands to send
790         NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
791         NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
792         NSData* fileEndCmd = [self prepareCommand:USB_DataHddFileEnd withData:nil];
794         //start timer
795         NSDate* startTime = [NSDate date];
796         startTime = [startTime addTimeInterval:(0-existingTime)];
797         //send commands
798         if ([turboCB state]) 
799                 [self turnTurboOn:YES];
800         else
801                 [self checkUSB:dev];
802         // now the proper commands
803         NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
804         data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
805         const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
806         NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
807         NSData* responseToCheck = nil;
809         NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
810         [fileHandle seekToFileOffset:offset];
811         do {
812                 if ([priorityTransferQueue count] != 0) {
813                         //break out and create a new transfer to continue it
814                         NSLog(@"pausing upload");
815                         [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
816                                         fileToUpload,@"filename",
817                                         [NSNumber numberWithUnsignedLongLong:size],@"fileSize",
818                                         curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",
819                                         [NSNumber numberWithUnsignedLongLong:offset],@"offset",
820                                         [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
821                                         nil]
822                                 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
823                         [self turnTurboOn:NO];
824                         break;
825                 } else {
826                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
827                 offset_bigendian = EndianU64_NtoB(offset);
828                 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
829                 [fileData appendData:[fileHandle readDataOfLength:rate]];
830                 offset += rate;
831                 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
832                 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
833                 if ([data length] >= 8) 
834                         responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
835                 else responseToCheck = nil;
836                 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck]) 
837                         break;
838                 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
839                         withObject:[NSDictionary dictionaryWithObjectsAndKeys:
840                                 [NSNumber numberWithDouble:(double)offset], @"offset",
841                                 [NSNumber numberWithDouble:(double)size], @"size",
842                                 startTime, @"startTime", nil]];
843                 [pool release];
844                 }
845         } while (offset < size && [[[self uiElements] isConnected] intValue]);
846         if ([[[self uiElements] isConnected] intValue])
847                 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
848         [fileHandle closeFile]; 
849         [[self uiElements] goToPath:[[self uiElements]currentPath]];
850         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
851         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
852         [self turnTurboOn:NO];
853         [[self uiElements] finishTransfer];
856 - (void) turnTurboOn:(BOOL) turnOn {
857         if (![[[self uiElements] isConnected] intValue]) return;
858         [self checkUSB:myContext];
859         int mode = 0;
860         if (turnOn) 
861                 mode = 1;
862         const SInt32 mode_bigendian = EndianS32_NtoB(mode);
863         NSData* modeData = [NSData dataWithBytes:&mode_bigendian length:4];
864         NSLog([modeData description]); //debugging
865         NSData* turboCommand  = [self prepareCommand:USB_CmdTurbo withData:modeData];
866         [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
869 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
870         [self checkUSB:myContext];
871         NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
872         NSMutableData* build = [NSMutableData dataWithData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
873         char dir = 0x5c; // 0x5c = "\"
874         [build appendData:[NSData dataWithBytes:&dir length:1]];
875         [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
876         dir = 0x00;
877         [build appendData:[NSData dataWithBytes:&dir length:1]];
878         NSData* fileDelCmd = [self prepareCommand:USB_CmdHddDel withData:build];
879         [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
882 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
883         NSLog(@"%@,%@,%@", oldName, newName, currentPath);
884         [self checkUSB:myContext];
885         unsigned short int i = [oldName length]+[currentPath length]+2;
886         UInt16 i_bigendian = EndianU16_NtoB(i);
887         NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
888         [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
889         char dir = 0x5c;
890         [build appendData:[NSData dataWithBytes:&dir length:1]];
891         [build appendData:[oldName dataUsingEncoding:NSISOLatin1StringEncoding]];
892         dir = 0x00;
893         [build appendData:[NSData dataWithBytes:&dir length:1]];
894         i = [newName length]+[currentPath length]+2;
895         i_bigendian = EndianU16_NtoB(i);
896         [build appendData:[NSData dataWithBytes:&i_bigendian length:2]];
897         [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
898         dir = 0x5c;
899         [build appendData:[NSData dataWithBytes:&dir length:1]];
900         [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
901         dir = 0x00;
902         [build appendData:[NSData dataWithBytes:&dir length:1]];
903         NSData* fileRenCmd = [self prepareCommand:USB_CmdHddRename withData:build];
904         [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
907 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
908         [self checkUSB:myContext];
909         unsigned short int i = [newName length]+[currentPath length]+2;
910         const UInt16 i_bigendian = EndianU16_NtoB(i);
911         NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
912         [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
913         char dir = 0x5c;
914         [build appendData:[NSData dataWithBytes:&dir length:1]];
915         [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
916         dir = 0x00;
917         [build appendData:[NSData dataWithBytes:&dir length:1]];
918         NSData* newFoldCmd = [self prepareCommand:USB_CmdHddCreateDir withData:build];
919         NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
920         [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
921         [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
924 - (void) checkUSB:(USBDeviceContext*)device {
925         NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
926         NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
927         const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
928         int i = 0;
929         NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
930         while ((data == nil || [data length] < 8) && i < 8) {
931                 NSLog (@"incorrect response");
932                 i++;
933                 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
934         }
935         if (!i<=8) {
936                 // tell someone that no longer connected here??
937                 return;
938                 }
939         NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
940         if (![responseWanted isEqualToData:responseToCheck]) {
941                 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again   
942         }
945 // ---------------------------------------------------------------------------
946 // Queue management
947 // ---------------------------------------------------------------------------
949 - (void) addPriorityTransfer:(id)newTransfer {
950         [priorityTransferQueue addObject:newTransfer];
951         NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
954 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
955         if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
956         else [transferQueue addObject:newTransfer];
957         NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
960 - (void) clearQueues {
961         [priorityTransferQueue removeAllObjects];
962         [transferQueue removeAllObjects];
963         [pausedQueue removeAllObjects];
966 - (id) transferQueue {
967         return transferQueue;
970 - (id) pausedQueue {
971         return pausedQueue;
974 - (void) transfer:(id)sender {
975         while ([[[self uiElements] isConnected] intValue]) {
976                 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
977                 if ([priorityTransferQueue count] != 0 ) {
978                         id currentTransfer = [priorityTransferQueue objectAtIndex:0];
979                         if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"fileList"]) {
980                                 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
981                                 [priorityTransferQueue removeObjectAtIndex:0];
982 //                              NSLog(@"fL fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
983                         }
984                         else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
985                 //              [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
986                                 [priorityTransferQueue removeObjectAtIndex:0];
987                         }
988                         else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"rename"]) {
989                                 [self renameFile:[currentTransfer objectForKey:@"oldName"]
990                                         withName:[currentTransfer objectForKey:@"newName"]
991                                         atPath:[currentTransfer objectForKey:@"path"]];
992                                 [priorityTransferQueue removeObjectAtIndex:0];
993                         }
994                         else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
995                                 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
996                                 [priorityTransferQueue removeObjectAtIndex:0];
997                         }
998                         if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
999                                 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
1000                                 [priorityTransferQueue removeObjectAtIndex:0];
1001                         }
1002                         if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"pause"]) {
1003                                 if ([transferQueue count] != 0) {
1004                                         NSArray* toPause = [transferQueue filteredArrayUsingPredicate:
1005                                                 [NSPredicate predicateWithFormat:@"(filename = %@)",
1006                                                         [currentTransfer objectForKey:@"filename"]]];
1007                                         if ([toPause count] > 1) NSLog(@"multiple pauses?");
1008                                         else {
1009                                                 [pausedQueue addObjectsFromArray:toPause];
1010                                                 [transferQueue removeObjectsInArray:toPause];
1011                                         }
1012                                 }
1013                                 [priorityTransferQueue removeObjectAtIndex:0];
1014                         }
1015                         if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"resume"]) {
1016                                 if ([pausedQueue count] != 0) {
1017                                         NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:
1018                                                 [NSPredicate predicateWithFormat:@"(filename = %@)",
1019                                                         [currentTransfer objectForKey:@"filename"]]];
1020                                         if ([toResume count] > 1) NSLog(@"multiple resumes?");
1021                                         else {
1022                                                 [transferQueue addObjectsFromArray:toResume];
1023                                                 [pausedQueue removeObjectsInArray:toResume];
1024                                         }
1025                                 }
1026                                 [priorityTransferQueue removeObjectAtIndex:0];
1027                         }
1028                 }
1029                 else if ([transferQueue count] != 0) {
1030                         id currentTransfer = [transferQueue objectAtIndex:0];
1031                         if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"download"]) {
1032                                 [self getFile:[currentTransfer objectForKey:@"filename"]
1033                                         forPath:[currentTransfer objectForKey:@"path"]
1034                                         toSaveTo:[currentTransfer objectForKey:@"savePath"]
1035                                         beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1036                                         withLooping:[[currentTransfer objectForKey:@"looping"] boolValue]
1037                                         existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1038                                 [transferQueue removeObjectAtIndex:0];
1039 //                              NSLog(@"dl fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1040                         }
1041                         else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"upload"]) {
1042 //                              NSLog(@"ul start %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1043                                 [self uploadFile:[currentTransfer objectForKey:@"filename"]
1044                                         ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue]
1045                                         fromPath:[currentTransfer objectForKey:@"path"]
1046                                         withAttributes:[currentTransfer objectForKey:@"attributes"]
1047                                         atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1048                                         existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1049                                 [transferQueue removeObjectAtIndex:0];
1050 //                              NSLog(@"ul fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1051                         }
1052                 }
1053                 else usleep(100);
1054                 [pool release];
1055         }
1058 // ---------------------------------------------------------------------------
1059 // User interface
1060 // ---------------------------------------------------------------------------
1062 - (UIElements*) uiElements {
1063         return [NSApp delegate];
1066 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
1067         progressBar = bar;
1068         progressTime = timeField;
1069         turboCB = turbo;
1072 -(void) updateProgress:(NSDictionary*) inDict {
1073         NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
1074         double offset = [[inDict objectForKey:@"offset"] doubleValue];
1075         double size = [[inDict objectForKey:@"size"] doubleValue];
1076         NSDate* startTime = [inDict objectForKey:@"startTime"];         
1077         [progressBar setDoubleValue:((double)offset/size*100)];
1078         [progressBar displayIfNeeded];
1079         [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
1080         [progressTime displayIfNeeded];
1081         [pool release];
1084 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
1085         char hp = 20;
1086         char mp = 20;
1087         char sp = 20; //padding
1088         int hours = (totalSeconds / 3600);  // returns number of whole hours fitted in totalSecs
1089         if (hours < 10)
1090                 hp = 48;
1091         int minutes = ((totalSeconds / 60) - hours*60);  // Whole minutes
1092         if (minutes < 10)
1093                 mp = 48;
1094         int seconds = ((long) totalSeconds % 60);       // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
1095         if (seconds < 10)
1096                 sp = 48;
1097         return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
1100 - (void) setDH:(id)newDH tableView:(id)tv {
1101         dh = newDH;
1102         tableView = tv;
1105 // ---------------------------------------------------------------------------
1106 // Miscellaneous
1107 // ---------------------------------------------------------------------------
1109 -(id) init {
1110         if (self = [super init]) {
1111                 debug = 0;
1112                 rate = 0xfe00;
1113                 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1114                 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1115                 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1116         }
1117         return self;
1120 void hexDump(UInt8 *buf, int len) {
1121         int row, col, maxrows;
1122         
1123         maxrows = len/16;
1124         if (len % 16) maxrows++;
1125         for (row=0; row< maxrows; row++) {
1126                 for (col=0; col<16; col++) {
1127                         if (!(col%2)) printf(" ");
1128                         printf("%02x", buf[row*16 + col] & 0xff);
1129                 }
1130                 printf("\t");   
1131                 for (col=0; col<16; col++) {
1132                         if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
1133                                 printf("%c", buf[row*16 + col]);
1134                         }
1135                         else { printf("."); }
1136                 }
1137                 printf("\n");
1138         }
1141 - (int) getSpeed {
1142         return connectedSpeed;
1145 -(void) setDebug:(int)mode {
1146         debug = mode;
1147         NSLog(@"Debug level: %i", debug);
1150 -(void) setRate:(int) newRate {
1151         rate = newRate;
1152         NSLog(@"New rate set: %i", rate);
1155 @end