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