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