/[svn]/libgig/trunk/src/Akai.cpp
ViewVC logotype

Annotation of /libgig/trunk/src/Akai.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3369 - (hide annotations) (download)
Fri Nov 24 22:48:43 2017 UTC (6 years, 5 months ago) by schoenebeck
File size: 56600 byte(s)
* src/Akai.cpp: Fixed compilation error with recent, more
  strict compilers.
* Bumped version (4.0.0.svn34).

1 schoenebeck 2572 /*
2     libakai - C++ cross-platform akai sample disk reader
3     Copyright (C) 2002-2003 Sébastien Métrot
4    
5 schoenebeck 3369 Linux port by Christian Schoenebeck <cuse@users.sourceforge.net> 2003-2017
6 schoenebeck 2572
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11    
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     Lesser General Public License for more details.
16    
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21     // Akai.cpp
22    
23     #include "Akai.h"
24    
25     #include <stdio.h>
26     #include <string.h>
27     #include <unistd.h>
28     #include <fcntl.h>
29 schoenebeck 2574 #if defined(_CARBON_) || defined(__APPLE__)
30     # if defined (__GNUC__) && (__GNUC__ >= 4)
31     # include <sys/disk.h>
32     # else
33     # include <dev/disk.h>
34     # endif
35 schoenebeck 2573 #endif
36 schoenebeck 2572 #include <sys/types.h>
37     #include <sys/stat.h>
38     #include <errno.h>
39 schoenebeck 2574 #if defined(_CARBON_) || defined(__APPLE__)
40 schoenebeck 2572 #include <paths.h>
41 schoenebeck 2574 #include <sys/ioctl.h>
42 schoenebeck 2572 #include <sys/param.h>
43     #include <IOKit/IOKitLib.h>
44     #include <IOKit/IOBSD.h>
45     #include <IOKit/storage/IOMediaBSDClient.h>
46     #include <IOKit/storage/IOMedia.h>
47     #include <IOKit/storage/IOCDMedia.h>
48     #include <IOKit/storage/IOCDTypes.h>
49     #include <CoreFoundation/CoreFoundation.h>
50     #endif
51    
52 schoenebeck 2573 #if defined(_CARBON_) || defined(__APPLE__)
53    
54     // These definitions were taken from mount_cd9660.c
55     // There are some similar definitions in IOCDTypes.h
56     // however there seems to be some dissagreement in
57     // the definition of CDTOC.length
58     struct _CDMSF {
59     u_char minute;
60     u_char second;
61     u_char frame;
62     };
63    
64     #define MSF_TO_LBA(msf) \
65     (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
66    
67     struct _CDTOC_Desc {
68     u_char session;
69     u_char ctrl_adr; /* typed to be machine and compiler independent */
70     u_char tno;
71     u_char point;
72     struct _CDMSF address;
73     u_char zero;
74     struct _CDMSF p;
75     };
76    
77     struct _CDTOC {
78     u_short length; /* in native cpu endian */
79     u_char first_session;
80     u_char last_session;
81     struct _CDTOC_Desc trackdesc[1];
82     };
83    
84     // Most of the following Mac CDROM IO functions were taken from Apple's IOKit
85     // examples (BSD style license). ReadTOC() function was taken from the Bochs x86
86     // Emulator (LGPL). Most probably they have taken it however also from some
87     // other BSD style licensed example code as well ...
88    
89     // Returns an iterator across all CD media (class IOCDMedia). Caller is responsible for releasing
90     // the iterator when iteration is complete.
91     static kern_return_t FindEjectableCDMedia(io_iterator_t *mediaIterator) {
92     kern_return_t kernResult;
93     CFMutableDictionaryRef classesToMatch;
94    
95     // CD media are instances of class kIOCDMediaClass
96     classesToMatch = IOServiceMatching(kIOCDMediaClass);
97     if (classesToMatch == NULL) {
98     printf("IOServiceMatching returned a NULL dictionary.\n");
99     } else {
100     CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
101     // Each IOMedia object has a property with key kIOMediaEjectableKey which is true if the
102     // media is indeed ejectable. So add this property to the CFDictionary we're matching on.
103     }
104    
105     kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, mediaIterator);
106    
107     return kernResult;
108     }
109    
110     // Given an iterator across a set of CD media, return the BSD path to the
111     // next one. If no CD media was found the path name is set to an empty string.
112     static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize) {
113     io_object_t nextMedia;
114     kern_return_t kernResult = KERN_FAILURE;
115    
116     *bsdPath = '\0';
117    
118     nextMedia = IOIteratorNext(mediaIterator);
119     if (nextMedia) {
120     CFTypeRef bsdPathAsCFString;
121    
122     bsdPathAsCFString = IORegistryEntryCreateCFProperty(nextMedia,
123     CFSTR(kIOBSDNameKey),
124     kCFAllocatorDefault,
125     0);
126     if (bsdPathAsCFString) {
127     strlcpy(bsdPath, _PATH_DEV, maxPathSize);
128    
129     // Add "r" before the BSD node name from the I/O Registry to specify the raw disk
130     // node. The raw disk nodes receive I/O requests directly and do not go through
131     // the buffer cache.
132    
133     strlcat(bsdPath, "r", maxPathSize);
134    
135     size_t devPathLength = strlen(bsdPath);
136    
137     if (CFStringGetCString((CFStringRef)bsdPathAsCFString,
138     bsdPath + devPathLength,
139     maxPathSize - devPathLength,
140     kCFStringEncodingUTF8)) {
141     printf("BSD path: %s\n", bsdPath);
142     kernResult = KERN_SUCCESS;
143     }
144    
145     CFRelease(bsdPathAsCFString);
146     }
147    
148     IOObjectRelease(nextMedia);
149     }
150    
151     return kernResult;
152     }
153    
154     // Given the path to a CD drive, open the drive.
155     // Return the file descriptor associated with the device.
156 schoenebeck 3053 /*static int OpenDrive(const char *bsdPath) {
157 schoenebeck 2573 int fileDescriptor;
158    
159     // This open() call will fail with a permissions error if the sample has been changed to
160     // look for non-removable media. This is because device nodes for fixed-media devices are
161     // owned by root instead of the current console user.
162    
163     fileDescriptor = open(bsdPath, O_RDONLY);
164    
165     if (fileDescriptor == -1) {
166     printf("Error opening device %s: ", bsdPath);
167     perror(NULL);
168     }
169    
170     return fileDescriptor;
171 schoenebeck 3053 }*/
172 schoenebeck 2573
173     // Given the file descriptor for a whole-media CD device, read a sector from the drive.
174     // Return true if successful, otherwise false.
175 schoenebeck 3053 /*static Boolean ReadSector(int fileDescriptor) {
176 schoenebeck 2573 char *buffer;
177     ssize_t numBytes;
178     u_int32_t blockSize;
179    
180     // This ioctl call retrieves the preferred block size for the media. It is functionally
181     // equivalent to getting the value of the whole media object's "Preferred Block Size"
182     // property from the IORegistry.
183     if (ioctl(fileDescriptor, DKIOCGETBLOCKSIZE, &blockSize)) {
184     perror("Error getting preferred block size");
185    
186     // Set a reasonable default if we can't get the actual preferred block size. A real
187     // app would probably want to bail at this point.
188     blockSize = kCDSectorSizeCDDA;
189     }
190    
191     printf("Media has block size of %d bytes.\n", blockSize);
192    
193     // Allocate a buffer of the preferred block size. In a real application, performance
194     // can be improved by reading as many blocks at once as you can.
195     buffer = (char*) malloc(blockSize);
196    
197     // Do the read. Note that we use read() here, not fread(), since this is a raw device
198     // node.
199     numBytes = read(fileDescriptor, buffer, blockSize);
200    
201     // Free our buffer. Of course, a real app would do something useful with the data first.
202     free(buffer);
203    
204     return numBytes == blockSize ? true : false;
205 schoenebeck 3053 }*/
206 schoenebeck 2573
207     // Given the file descriptor for a device, close that device.
208 schoenebeck 3053 /*static void CloseDrive(int fileDescriptor) {
209 schoenebeck 2573 close(fileDescriptor);
210 schoenebeck 3053 }*/
211 schoenebeck 2573
212     // path is the BSD path to a raw device such as /dev/rdisk1
213     static struct _CDTOC * ReadTOC(const char *devpath) {
214     struct _CDTOC * toc_p = NULL;
215     io_iterator_t iterator = 0;
216     io_registry_entry_t service = 0;
217     CFDictionaryRef properties = 0;
218     CFDataRef data = 0;
219     mach_port_t port = 0;
220 schoenebeck 3369 const char *devname;
221 schoenebeck 2573
222     if ((devname = strrchr(devpath, '/')) != NULL) {
223     ++devname;
224     } else {
225     devname = (char *) devpath;
226     }
227    
228     if (IOMasterPort(bootstrap_port, &port) != KERN_SUCCESS) {
229     fprintf(stderr, "IOMasterPort failed\n");
230     goto Exit;
231     }
232    
233     if (IOServiceGetMatchingServices(port, IOBSDNameMatching(port, 0, devname),
234     &iterator) != KERN_SUCCESS) {
235     fprintf(stderr, "IOServiceGetMatchingServices failed\n");
236     goto Exit;
237     }
238    
239     service = IOIteratorNext(iterator);
240    
241     IOObjectRelease(iterator);
242    
243     iterator = 0;
244    
245     while (service && !IOObjectConformsTo(service, "IOCDMedia")) {
246     if (IORegistryEntryGetParentIterator(service, kIOServicePlane,
247     &iterator) != KERN_SUCCESS)
248     {
249     fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
250     goto Exit;
251     }
252    
253     IOObjectRelease(service);
254     service = IOIteratorNext(iterator);
255     IOObjectRelease(iterator);
256     }
257    
258     if (!service) {
259     fprintf(stderr, "CD media not found\n");
260     goto Exit;
261     }
262    
263     if (IORegistryEntryCreateCFProperties(service, (__CFDictionary **) &properties,
264     kCFAllocatorDefault,
265     kNilOptions) != KERN_SUCCESS)
266     {
267     fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
268     goto Exit;
269     }
270    
271     data = (CFDataRef) CFDictionaryGetValue(properties, CFSTR(kIOCDMediaTOCKey));
272     if (data == NULL) {
273     fprintf(stderr, "CFDictionaryGetValue failed\n");
274     goto Exit;
275     } else {
276     CFRange range;
277     CFIndex buflen;
278    
279     buflen = CFDataGetLength(data) + 1;
280     range = CFRangeMake(0, buflen);
281     toc_p = (struct _CDTOC *) malloc(buflen);
282     if (toc_p == NULL) {
283     fprintf(stderr, "Out of memory\n");
284     goto Exit;
285     } else {
286     CFDataGetBytes(data, range, (unsigned char *) toc_p);
287     }
288    
289     /*
290     fprintf(stderr, "Table of contents\n length %d first %d last %d\n",
291     toc_p->length, toc_p->first_session, toc_p->last_session);
292     */
293    
294     CFRelease(properties);
295     }
296    
297     Exit:
298    
299     if (service) {
300     IOObjectRelease(service);
301     }
302    
303     return toc_p;
304     }
305    
306     #endif // defined(_CARBON_) || defined(__APPLE__)
307    
308 schoenebeck 2572 //////////////////////////////////
309     // AkaiSample:
310     AkaiSample::AkaiSample(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
311     : AkaiDiskElement(pDisk->GetPos())
312     {
313     mpParent = pParent;
314     mpDisk = pDisk;
315     mDirEntry = DirEntry;
316     mpSamples = NULL;
317     mHeaderOK = false;
318     mPos = 0;
319    
320     //
321     LoadHeader();
322     }
323    
324     AkaiSample::~AkaiSample()
325     {
326     if (mpSamples)
327     free(mpSamples);
328     }
329    
330     AkaiDirEntry AkaiSample::GetDirEntry()
331     {
332     return mDirEntry;
333     }
334    
335     bool AkaiSample::LoadSampleData()
336     {
337     if (!LoadHeader())
338     return false;
339     if (mpSamples)
340     return true;
341    
342     mpDisk->SetPos(mImageOffset);
343     mpSamples = (int16_t*) malloc(mNumberOfSamples * sizeof(int16_t));
344     if (!mpSamples)
345     return false;
346    
347     mpDisk->ReadInt16((uint16_t*)mpSamples, mNumberOfSamples);
348     return true;
349     }
350    
351     void AkaiSample::ReleaseSampleData()
352     {
353     if (!mpSamples)
354     return;
355     free(mpSamples);
356     mpSamples = NULL;
357     }
358    
359     int AkaiSample::SetPos(int Where, akai_stream_whence_t Whence)
360     {
361     if (!mHeaderOK) return -1;
362     int w = Where;
363     switch (Whence)
364     {
365     case akai_stream_start:
366     mPos = w;
367     break;
368     /*case eStreamRewind:
369     w = -w;*/
370     case akai_stream_curpos:
371     mPos += w;
372     break;
373     case akai_stream_end:
374     mPos = mNumberOfSamples - w;
375     break;
376     }
377     if (mPos > mNumberOfSamples) mPos = mNumberOfSamples;
378     if (mPos < 0) mPos = 0;
379     return mPos;
380     }
381    
382     int AkaiSample::Read(void* pBuffer, uint SampleCount)
383     {
384     if (!mHeaderOK) return 0;
385    
386     if (mPos + SampleCount > mNumberOfSamples) SampleCount = mNumberOfSamples - mPos;
387    
388     mpDisk->SetPos(mImageOffset + mPos * 2); // FIXME: assumes 16 bit sample depth
389     mpDisk->ReadInt16((uint16_t*)pBuffer, SampleCount);
390     return SampleCount;
391     }
392    
393     bool AkaiSample::LoadHeader()
394     {
395     if (mHeaderOK)
396     return true;
397    
398     mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
399    
400     // Length Format Description
401     // --------------------------------------------------------------
402     // 1 3
403     if (mpDisk->ReadInt8() != AKAI_SAMPLE_ID)
404     return false;
405     // 1 Not important: 0 for 22050Hz, 1 for 44100Hz
406     mpDisk->ReadInt8();
407     // 1 unsigned MIDI root note (C3=60)
408     mMidiRootNote = mpDisk->ReadInt8();
409     // 12 AKAII Filename
410     char buffer[13];
411     mpDisk->Read(buffer, 12, 1);
412     AkaiToAscii(buffer, 12);
413     mName = buffer;
414    
415     // 1 128
416     mpDisk->ReadInt8();
417     // 1 unsigned Number of active loops
418     mActiveLoops = mpDisk->ReadInt8();
419     // 1 unsigned char First active loop (0 for none)
420     mFirstActiveLoop = mpDisk->ReadInt8();
421     // 1 0
422     mpDisk->ReadInt8();
423     // 1 unsigned Loop mode: 0=in release 1=until release
424     // 2=none 3=play to end
425     mLoopMode = mpDisk->ReadInt8();
426     // 1 signed Cents tune -50...+50
427     mTuneCents = mpDisk->ReadInt8();
428     // 1 signed Semi tune -50...+50
429     mTuneSemitones = mpDisk->ReadInt8();
430     // 4 0,8,2,0
431     mpDisk->ReadInt8();
432     mpDisk->ReadInt8();
433     mpDisk->ReadInt8();
434     mpDisk->ReadInt8();
435     //
436     // 4 unsigned Number of sample words
437     mNumberOfSamples = mpDisk->ReadInt32();
438     // 4 unsigned Start marker
439     mStartMarker = mpDisk->ReadInt32();
440     // 4 unsigned End marker
441     mEndMarker = mpDisk->ReadInt32();
442     //
443     // 4 unsigned Loop 1 marker
444     // 2 unsigned Loop 1 fine length (65536ths)
445     // 4 unsigned Loop 1 coarse length (words)
446     // 2 unsigned Loop 1 time (msec. or 9999=infinite)
447     //
448     // 84 [as above] Loops 2 to 8
449     //
450     int i;
451     for (i=0; i<8; i++)
452     mLoops[i].Load(mpDisk);
453     // 4 0,0,255,255
454     mpDisk->ReadInt32();
455     // 2 unsigned Sampling frequency
456     mSamplingFrequency = mpDisk->ReadInt16();
457     // 1 signed char Loop tune offset -50...+50
458     mLoopTuneOffset = mpDisk->ReadInt8();
459    
460     mImageOffset = mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150; // 150 is the size of the header
461    
462     //FIXME: no header validation yet implemented
463     return (mHeaderOK = true);
464     }
465    
466     bool AkaiSampleLoop::Load(DiskImage* pDisk)
467     {
468     // 4 unsigned Loop 1 marker
469     mMarker = pDisk->ReadInt32();
470     // 2 unsigned Loop 1 fine length (65536ths)
471     mFineLength = pDisk->ReadInt16();
472     // 4 unsigned Loop 1 coarse length (words)
473     mCoarseLength = pDisk->ReadInt32();
474     // 2 unsigned Loop 1 time (msec. or 9999=infinite)
475     mTime = pDisk->ReadInt16();
476     return true;
477     }
478    
479     //////////////////////////////////
480     // AkaiProgram:
481     AkaiProgram::AkaiProgram(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
482     : AkaiDiskElement(pDisk->GetPos())
483     {
484     mpParent = pParent;
485     mpDisk = pDisk;
486     mDirEntry = DirEntry;
487     mpKeygroups = NULL;
488     Load();
489     }
490    
491     AkaiProgram::~AkaiProgram()
492     {
493     if (mpKeygroups)
494     delete[] mpKeygroups;
495     }
496    
497     AkaiDirEntry AkaiProgram::GetDirEntry()
498     {
499     return mDirEntry;
500     }
501    
502     bool AkaiProgram::Load()
503     {
504     // Read each of the program parameters
505     uint temppos = mpDisk->GetPos();
506     mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
507     // byte description default range/comments
508     // ---------------------------------------------------------------------------
509     // 1 program ID 1
510     uint8_t ProgID = mpDisk->ReadInt8();
511     if (ProgID != AKAI_PROGRAM_ID)
512     {
513     mpDisk->SetPos(temppos);
514     return false;
515     }
516     // 2-3 first keygroup address 150,0
517 schoenebeck 3053 /*uint16_t KeygroupAddress =*/ mpDisk->ReadInt16();
518 schoenebeck 2572 // 4-15 program name 10,10,10... AKAII character set
519     char buffer[13];
520     mpDisk->Read(buffer, 12, 1);
521     AkaiToAscii(buffer, 12);
522     mName = buffer;
523     // 16 MIDI program number 0 0..127
524     mMidiProgramNumber = mpDisk->ReadInt8();
525     // 17 MIDI channel 0 0..15, 255=OMNI
526     mMidiChannel = mpDisk->ReadInt8();
527     // 18 polyphony 15 1..16
528     mPolyphony = mpDisk->ReadInt8();
529     // 19 priority 1 0=LOW 1=NORM 2=HIGH 3=HOLD
530     mPriority = mpDisk->ReadInt8();
531     // 20 low key 24 24..127
532     mLowKey = mpDisk->ReadInt8();
533     // 21 high key 127 24..127
534     mHighKey = mpDisk->ReadInt8();
535     // 22 octave shift 0 -2..2
536     mOctaveShift = mpDisk->ReadInt8();
537     // 23 aux output select 255 0..7, 255=OFF
538     mAuxOutputSelect = mpDisk->ReadInt8();
539     // 24 mix output level 99 0..99
540     mMixOutputSelect = mpDisk->ReadInt8();
541     // 25 mix output pan 0 -50..50
542     mMixPan = mpDisk->ReadInt8();
543     // 26 volume 80 0..99
544     mVolume = mpDisk->ReadInt8();
545     // 27 vel>volume 20 -50..50
546     mVelocityToVolume = mpDisk->ReadInt8();
547     // 28 key>volume 0 -50..50
548     mKeyToVolume = mpDisk->ReadInt8();
549     // 29 pres>volume 0 -50..50
550     mPressureToVolume = mpDisk->ReadInt8();
551     // 30 pan lfo rate 50 0..99
552     mPanLFORate = mpDisk->ReadInt8();
553     // 31 pan lfo depth 0 0..99
554     mPanLFODepth = mpDisk->ReadInt8();
555     // 32 pan lfo delay 0 0..99
556     mPanLFODelay = mpDisk->ReadInt8();
557     // 33 key>pan 0 -50..50
558     mKeyToPan = mpDisk->ReadInt8();
559     // 34 lfo rate 50 0..99
560     mLFORate = mpDisk->ReadInt8();
561     // 35 lfo depth 0 0..99
562     mLFODepth = mpDisk->ReadInt8();
563     // 36 lfo delay 0 0..99
564     mLFODelay = mpDisk->ReadInt8();
565     // 37 mod>lfo depth 30 0..99
566     mModulationToLFODepth = mpDisk->ReadInt8();
567     // 38 pres>lfo depth 0 0..99
568     mPressureToLFODepth = mpDisk->ReadInt8();
569     // 39 vel>lfo depth 0 0..99
570     mVelocityToLFODepth = mpDisk->ReadInt8();
571     // 40 bend>pitch 2 0..12 semitones
572     mBendToPitch = mpDisk->ReadInt8();
573     // 41 pres>pitch 0 -12..12 semitones
574     mPressureToPitch = mpDisk->ReadInt8();
575     // 42 keygroup crossfade 0 0=OFF 1=ON
576     mKeygroupCrossfade = mpDisk->ReadInt8()?true:false;
577     // 43 number of keygroups 1 1..99
578     mNumberOfKeygroups = mpDisk->ReadInt8();
579     // 44 (internal use) 0 program number
580     mpDisk->ReadInt8();
581     // 45-56 key temperament C,C#,D... 0 -25..25 cents
582     uint i;
583     for (i = 0; i<11; i++)
584     mKeyTemperament[i] = mpDisk->ReadInt8();
585     // 57 fx output 0 0=OFF 1=ON
586     mFXOutput = mpDisk->ReadInt8()?true:false;
587     // 58 mod>pan 0 -50..50
588     mModulationToPan = mpDisk->ReadInt8();
589     // 59 stereo coherence 0 0=OFF 1=ON
590     mStereoCoherence = mpDisk->ReadInt8()?true:false;
591     // 60 lfo desync 1 0=OFF 1=ON
592     mLFODesync = mpDisk->ReadInt8()?true:false;
593     // 61 pitch law 0 0=LINEAR
594     mPitchLaw = mpDisk->ReadInt8();
595     // 62 voice re-assign 0 0=OLDEST 1=QUIETEST
596     mVoiceReassign = mpDisk->ReadInt8();
597     // 63 softped>volume 10 0..99
598     mSoftpedToVolume = mpDisk->ReadInt8();
599     // 64 softped>attack 10 0..99
600     mSoftpedToAttack = mpDisk->ReadInt8();
601     // 65 softped>filt 10 0..99
602     mSoftpedToFilter = mpDisk->ReadInt8();
603     // 66 tune cents 0 -128..127 (-50..50 cents)
604     mSoftpedToTuneCents = mpDisk->ReadInt8();
605     // 67 tune semitones 0 -50..50
606     mSoftpedToTuneSemitones = mpDisk->ReadInt8();
607     // 68 key>lfo rate 0 -50..50
608     mKeyToLFORate = mpDisk->ReadInt8();
609     // 69 key>lfo depth 0 -50..50
610     mKeyToLFODepth = mpDisk->ReadInt8();
611     // 70 key>lfo delay 0 -50..50
612     mKeyToLFODelay = mpDisk->ReadInt8();
613     // 71 voice output scale 1 0=-6dB 1=0dB 2=+12dB
614     mVoiceOutputScale = mpDisk->ReadInt8();
615     // 72 stereo output scale 0 0=0dB 1=+6dB
616     mStereoOutputScale = mpDisk->ReadInt8();
617     // 73-150 (not used)
618    
619     // Read the key groups:
620     if (mpKeygroups)
621     delete[] mpKeygroups;
622     mpKeygroups = new AkaiKeygroup[mNumberOfKeygroups];
623     for (i = 0; i < mNumberOfKeygroups; i++)
624     {
625     // Go where the key group is on the disk:
626     mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150 * (i+1));
627     if (!mpKeygroups[i].Load(mpDisk))
628     {
629     mpDisk->SetPos(temppos);
630     return false;
631     }
632     }
633    
634     mpDisk->SetPos(temppos);
635     return true;
636     }
637    
638     uint AkaiProgram::ListSamples(std::list<String>& rSamples)
639     {
640     return 0;
641     }
642    
643     AkaiSample* AkaiProgram::GetSample(uint Index)
644     {
645     return NULL;
646     }
647    
648     AkaiSample* AkaiProgram::GetSample(const String& rName)
649     {
650     return NULL;
651     }
652    
653     // AkaiKeygroup:
654     bool AkaiKeygroup::Load(DiskImage* pDisk)
655     {
656     uint i;
657     // byte description default range/comments
658     // ---------------------------------------------------------------------------
659     // 1 keygroup ID 2
660     if (pDisk->ReadInt8() != AKAI_KEYGROUP_ID)
661     return false;
662     // 2-3 next keygroup address 44,1 300,450,600,750.. (16-bit)
663 schoenebeck 3053 /*uint16_t NextKeygroupAddress =*/ pDisk->ReadInt16();
664 schoenebeck 2572 // 4 low key 24 24..127
665     mLowKey = pDisk->ReadInt8();
666     // 5 high key 127 24..127
667     mHighKey = pDisk->ReadInt8();
668     // 6 tune cents 0 -128..127 (-50..50 cents)
669     mTuneCents = pDisk->ReadInt8();
670     // 7 tune semitones 0 -50..50
671     mTuneSemitones = pDisk->ReadInt8();
672     // 8 filter 99 0..99
673     mFilter = pDisk->ReadInt8();
674     // 9 key>filter 12 0..24 semitone/oct
675     mKeyToFilter = pDisk->ReadInt8();
676     // 10 vel>filt 0 -50..50
677     mVelocityToFilter = pDisk->ReadInt8();
678     // 11 pres>filt 0 -50..50
679     mPressureToFilter = pDisk->ReadInt8();
680     // 12 env2>filt 0 -50..50
681     mEnveloppe2ToFilter = pDisk->ReadInt8();
682    
683     // 13 env1 attack 0 0..99
684     // 14 env1 decay 30 0..99
685     // 15 env1 sustain 99 0..99
686     // 16 env1 release 45 0..99
687     // 17 env1 vel>attack 0 -50..50
688     // 18 env1 vel>release 0 -50..50
689     // 19 env1 offvel>release 0 -50..50
690     // 20 env1 key>dec&rel 0 -50..50
691     // 21 env2 attack 0 0..99
692     // 22 env2 decay 50 0..99
693     // 23 env2 sustain 99 0..99
694     // 24 env2 release 45 0..99
695     // 25 env2 vel>attack 0 -50..50
696     // 26 env2 vel>release 0 -50..50
697     // 27 env2 offvel>release 0 -50..50
698     // 28 env2 key>dec&rel 0 -50..50
699     for (i=0; i<2; i++)
700     mEnveloppes[i].Load(pDisk);
701    
702     // 29 vel>env2>filter 0 -50..50
703     mVelocityToEnveloppe2ToFilter = pDisk->ReadInt8();
704     // 30 env2>pitch 0 -50..50
705     mEnveloppe2ToPitch = pDisk->ReadInt8();
706     // 31 vel zone crossfade 1 0=OFF 1=ON
707     mVelocityZoneCrossfade = pDisk->ReadInt8()?true:false;
708     // 32 vel zones used 4
709     mVelocityZoneUsed = pDisk->ReadInt8();
710     // 33 (internal use) 255
711     pDisk->ReadInt8();
712     // 34 (internal use) 255
713     pDisk->ReadInt8();
714     //
715    
716     // 35-46 sample 1 name 10,10,10... AKAII character set
717     // 47 low vel 0 0..127
718     // 48 high vel 127 0..127
719     // 49 tune cents 0 -128..127 (-50..50 cents)
720     // 50 tune semitones 0 -50..50
721     // 51 loudness 0 -50..+50
722     // 52 filter 0 -50..+50
723     // 53 pan 0 -50..+50
724     // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
725     // 2=LOOP_UNTIL_REL 3=NO_LOOP
726     // 4=PLAY_TO_END
727     // 55 (internal use) 255
728     // 56 (internal use) 255
729     // 57-58 (internal use) 44,1
730     //
731     // 59-82 [repeat 35-58 for sample 2]
732     //
733     // 83-106 [repeat 35-58 for sample 3]
734     //
735     // 107-130 [repeat 35-58 for sample 4]
736     //
737     for (i=0; i<4; i++)
738     mSamples[i].Load(pDisk);
739    
740     // 131 beat detune 0 -50..50
741     mBeatDetune = pDisk->ReadInt8();
742     // 132 hold attack until loop 0 0=OFF 1=ON
743     mHoldAttackUntilLoop = pDisk->ReadInt8()?true:false;
744     // 133-136 sample 1-4 key tracking 0 0=TRACK 1=FIXED
745     for (i=0; i<4; i++)
746     mSampleKeyTracking[i] = pDisk->ReadInt8()?true:false;
747     // 137-140 sample 1-4 aux out offset 0 0..7
748     for (i=0; i<4; i++)
749     mSampleAuxOutOffset[i] = pDisk->ReadInt8();
750     // 141-148 vel>sample start 0 -9999..9999 (16-bit signed)
751     for (i=0; i<4; i++)
752     mVelocityToSampleStart[i] = pDisk->ReadInt8();
753     // 149 vel>volume offset 0 -50..50
754     for (i=0; i<4; i++)
755     mVelocityToVolumeOffset[i] = pDisk->ReadInt8();
756     // 150 (not used)
757    
758     return true;
759     }
760    
761     bool AkaiEnveloppe::Load(DiskImage* pDisk)
762     {
763     // 13 env1 attack 0 0..99
764     mAttack = pDisk->ReadInt8();
765     // 14 env1 decay 30 0..99
766     mDecay = pDisk->ReadInt8();
767     // 15 env1 sustain 99 0..99
768     mSustain = pDisk->ReadInt8();
769     // 16 env1 release 45 0..99
770     mRelease = pDisk->ReadInt8();
771     // 17 env1 vel>attack 0 -50..50
772     mVelocityToAttack = pDisk->ReadInt8();
773     // 18 env1 vel>release 0 -50..50
774     mVelocityToRelease = pDisk->ReadInt8();
775     // 19 env1 offvel>release 0 -50..50
776     mOffVelocityToRelease = pDisk->ReadInt8();
777     // 20 env1 key>dec&rel 0 -50..50
778     mKeyToDecayAndRelease = pDisk->ReadInt8();
779     return true;
780     }
781    
782     bool AkaiKeygroupSample::Load(DiskImage* pDisk)
783     {
784     // 35-46 sample 1 name 10,10,10... AKAII character set
785     char buffer[13];
786     pDisk->Read(buffer, 12, 1);
787     AkaiToAscii(buffer, 12);
788     mName = buffer;
789    
790     // 47 low vel 0 0..127
791     mLowLevel = pDisk->ReadInt8();
792     // 48 high vel 127 0..127
793 schoenebeck 3053 /*uint8_t mHighLevel =*/ pDisk->ReadInt8();
794 schoenebeck 2572 // 49 tune cents 0 -128..127 (-50..50 cents)
795 schoenebeck 3053 /*int8_t mTuneCents =*/ pDisk->ReadInt8();
796 schoenebeck 2572 // 50 tune semitones 0 -50..50
797 schoenebeck 3053 /*int8_t mTuneSemitones =*/ pDisk->ReadInt8();
798 schoenebeck 2572 // 51 loudness 0 -50..+50
799 schoenebeck 3053 /*int8_t mLoudness =*/ pDisk->ReadInt8();
800 schoenebeck 2572 // 52 filter 0 -50..+50
801 schoenebeck 3053 /*int8_t mFilter =*/ pDisk->ReadInt8();
802 schoenebeck 2572 // 53 pan 0 -50..+50
803 schoenebeck 3053 /*int8_t mPan =*/ pDisk->ReadInt8();
804 schoenebeck 2572 // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
805     // 2=LOOP_UNTIL_REL 3=NO_LOOP
806     // 4=PLAY_TO_END
807 schoenebeck 3053 /*uint8_t mLoopMode =*/ pDisk->ReadInt8();
808 schoenebeck 2572 // 55 (internal use) 255
809     pDisk->ReadInt8();
810     // 56 (internal use) 255
811     pDisk->ReadInt8();
812     // 57-58 (internal use) 44,1
813     pDisk->ReadInt16();
814     //
815    
816     return true;
817     }
818    
819     //////////////////////////////////
820     // AkaiVolume:
821     AkaiVolume::AkaiVolume(DiskImage* pDisk, AkaiPartition* pParent, const AkaiDirEntry& DirEntry)
822     : AkaiDiskElement()
823     {
824     mpDisk = pDisk;
825     mpParent = pParent;
826     mDirEntry = DirEntry;
827    
828     if (mDirEntry.mType != AKAI_TYPE_DIR_S1000 && mDirEntry.mType != AKAI_TYPE_DIR_S3000)
829     {
830     printf("Creating Unknown Volume type! %d\n",mDirEntry.mType);
831 schoenebeck 2573 #ifdef WIN32
832 schoenebeck 2572 OutputDebugString("Creating Unknown Volume type!\n");
833     #endif
834     }
835     }
836    
837     AkaiVolume::~AkaiVolume()
838     {
839     {
840     std::list<AkaiProgram*>::iterator it;
841     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
842     for (it = mpPrograms.begin(); it != end; it++)
843     if (*it)
844     (*it)->Release();
845     }
846    
847     {
848     std::list<AkaiSample*>::iterator it;
849     std::list<AkaiSample*>::iterator end = mpSamples.end();
850     for (it = mpSamples.begin(); it != end; it++)
851     if (*it)
852     (*it)->Release();
853     }
854     }
855    
856     uint AkaiVolume::ReadDir()
857     {
858     uint i;
859     if (mpPrograms.empty())
860     {
861     uint maxfiles = ReadFAT(mpDisk, mpParent,mDirEntry.mStart) ? AKAI_MAX_FILE_ENTRIES_S1000 : AKAI_MAX_FILE_ENTRIES_S3000;
862     for (i = 0; i < maxfiles; i++)
863     {
864     AkaiDirEntry DirEntry;
865     ReadDirEntry(mpDisk, mpParent, DirEntry, mDirEntry.mStart, i);
866     DirEntry.mIndex = i;
867     if (DirEntry.mType == 'p')
868     {
869     AkaiProgram* pProgram = new AkaiProgram(mpDisk, this, DirEntry);
870     pProgram->Acquire();
871     mpPrograms.push_back(pProgram);
872     }
873     else if (DirEntry.mType == 's')
874     {
875     AkaiSample* pSample = new AkaiSample(mpDisk, this, DirEntry);
876     pSample->Acquire();
877     mpSamples.push_back(pSample);
878     }
879     }
880     }
881     return (uint)(mpPrograms.size() + mpSamples.size());
882     }
883    
884     uint AkaiVolume::ListPrograms(std::list<AkaiDirEntry>& rPrograms)
885     {
886     rPrograms.clear();
887     ReadDir();
888    
889     std::list<AkaiProgram*>::iterator it;
890     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
891     for (it = mpPrograms.begin(); it != end; it++)
892     if (*it)
893     rPrograms.push_back((*it)->GetDirEntry());
894     return (uint)rPrograms.size();
895     }
896    
897     AkaiProgram* AkaiVolume::GetProgram(uint Index)
898     {
899     uint i = 0;
900    
901     if (mpPrograms.empty())
902     {
903     std::list<AkaiDirEntry> dummy;
904     ListPrograms(dummy);
905     }
906    
907     std::list<AkaiProgram*>::iterator it;
908     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
909     for (it = mpPrograms.begin(); it != end; it++)
910     {
911     if (*it && i == Index)
912     {
913     (*it)->Acquire();
914     return *it;
915     }
916     i++;
917     }
918     return NULL;
919     }
920    
921     AkaiProgram* AkaiVolume::GetProgram(const String& rName)
922     {
923     if (mpPrograms.empty())
924     {
925     std::list<AkaiDirEntry> dummy;
926     ListPrograms(dummy);
927     }
928    
929     std::list<AkaiProgram*>::iterator it;
930     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
931     for (it = mpPrograms.begin(); it != end; it++)
932     {
933     if (*it && rName == (*it)->GetDirEntry().mName)
934     {
935     (*it)->Acquire();
936     return *it;
937     }
938     }
939     return NULL;
940     }
941    
942     uint AkaiVolume::ListSamples(std::list<AkaiDirEntry>& rSamples)
943     {
944     rSamples.clear();
945     ReadDir();
946    
947     std::list<AkaiSample*>::iterator it;
948     std::list<AkaiSample*>::iterator end = mpSamples.end();
949     for (it = mpSamples.begin(); it != end; it++)
950     if (*it)
951     rSamples.push_back((*it)->GetDirEntry());
952     return (uint)rSamples.size();
953     }
954    
955     AkaiSample* AkaiVolume::GetSample(uint Index)
956     {
957     uint i = 0;
958    
959     if (mpSamples.empty())
960     {
961     std::list<AkaiDirEntry> dummy;
962     ListSamples(dummy);
963     }
964    
965     std::list<AkaiSample*>::iterator it;
966     std::list<AkaiSample*>::iterator end = mpSamples.end();
967     for (it = mpSamples.begin(); it != end; it++)
968     {
969     if (*it && i == Index)
970     {
971     (*it)->Acquire();
972     return *it;
973     }
974     i++;
975     }
976     return NULL;
977     }
978    
979     AkaiSample* AkaiVolume::GetSample(const String& rName)
980     {
981     if (mpSamples.empty())
982     {
983     std::list<AkaiDirEntry> dummy;
984     ListSamples(dummy);
985     }
986    
987     std::list<AkaiSample*>::iterator it;
988     std::list<AkaiSample*>::iterator end = mpSamples.end();
989     for (it = mpSamples.begin(); it != end; it++)
990     {
991     if (*it && rName == (*it)->GetDirEntry().mName)
992     {
993     (*it)->Acquire();
994     return *it;
995     }
996     }
997     return NULL;
998     }
999    
1000     AkaiDirEntry AkaiVolume::GetDirEntry()
1001     {
1002     return mDirEntry;
1003     }
1004    
1005     bool AkaiVolume::IsEmpty()
1006     {
1007     return ReadDir() == 0;
1008     }
1009    
1010    
1011     //////////////////////////////////
1012     // AkaiPartition:
1013     AkaiPartition::AkaiPartition(DiskImage* pDisk, AkaiDisk* pParent)
1014     {
1015     mpDisk = pDisk;
1016     mpParent = pParent;
1017     }
1018    
1019     AkaiPartition::~AkaiPartition()
1020     {
1021     std::list<AkaiVolume*>::iterator it;
1022     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1023     for (it = mpVolumes.begin(); it != end; it++)
1024     if (*it)
1025     (*it)->Release();
1026     }
1027    
1028     uint AkaiPartition::ListVolumes(std::list<AkaiDirEntry>& rVolumes)
1029     {
1030     rVolumes.clear();
1031     uint i;
1032     if (mpVolumes.empty())
1033     {
1034     for (i = 0; i < AKAI_MAX_DIR_ENTRIES; i++)
1035     {
1036     AkaiDirEntry DirEntry;
1037     ReadDirEntry(mpDisk, this, DirEntry, AKAI_ROOT_ENTRY_OFFSET, i);
1038     DirEntry.mIndex = i;
1039     if (DirEntry.mType == AKAI_TYPE_DIR_S1000 || DirEntry.mType == AKAI_TYPE_DIR_S3000)
1040     {
1041     AkaiVolume* pVolume = new AkaiVolume(mpDisk, this, DirEntry);
1042     pVolume->Acquire();
1043     if (!pVolume->IsEmpty())
1044     {
1045     mpVolumes.push_back(pVolume);
1046     rVolumes.push_back(DirEntry);
1047     }
1048     else
1049     pVolume->Release();
1050     }
1051     }
1052     }
1053     else
1054     {
1055     std::list<AkaiVolume*>::iterator it;
1056     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1057     for (it = mpVolumes.begin(); it != end; it++)
1058     if (*it)
1059     rVolumes.push_back((*it)->GetDirEntry());
1060     }
1061     return (uint)rVolumes.size();
1062     }
1063    
1064     AkaiVolume* AkaiPartition::GetVolume(uint Index)
1065     {
1066     uint i = 0;
1067    
1068     if (mpVolumes.empty())
1069     {
1070     std::list<AkaiDirEntry> dummy;
1071     ListVolumes(dummy);
1072     }
1073    
1074     std::list<AkaiVolume*>::iterator it;
1075     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1076     for (it = mpVolumes.begin(); it != end; it++)
1077     {
1078     if (*it && i == Index)
1079     {
1080     (*it)->Acquire();
1081     return *it;
1082     }
1083     i++;
1084     }
1085     return NULL;
1086     }
1087    
1088     AkaiVolume* AkaiPartition::GetVolume(const String& rName)
1089     {
1090     if (mpVolumes.empty())
1091     {
1092     std::list<AkaiDirEntry> dummy;
1093     ListVolumes(dummy);
1094     }
1095    
1096     std::list<AkaiVolume*>::iterator it;
1097     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1098     for (it = mpVolumes.begin(); it != end; it++)
1099     {
1100     if (*it && rName == (*it)->GetDirEntry().mName)
1101     {
1102     (*it)->Acquire();
1103     return *it;
1104     }
1105     }
1106     return NULL;
1107     }
1108    
1109     bool AkaiPartition::IsEmpty()
1110     {
1111     std::list<AkaiDirEntry> Volumes;
1112     return ListVolumes(Volumes) == 0;
1113     }
1114    
1115    
1116     //////////////////////////////////
1117     // AkaiDisk:
1118     AkaiDisk::AkaiDisk(DiskImage* pDisk)
1119     {
1120     mpDisk = pDisk;
1121     }
1122    
1123     AkaiDisk::~AkaiDisk()
1124     {
1125     std::list<AkaiPartition*>::iterator it;
1126     std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1127     for (it = mpPartitions.begin(); it != end ; it++)
1128     if (*it)
1129     (*it)->Release();
1130     }
1131    
1132     uint AkaiDisk::GetPartitionCount()
1133     {
1134     if (!mpPartitions.empty())
1135     return (uint)mpPartitions.size();
1136    
1137     uint offset = 0;
1138     uint16_t size = 0;
1139     while (size != AKAI_PARTITION_END_MARK && size != 0x0fff && size != 0xffff
1140     && size<30720 && mpPartitions.size()<9)
1141     {
1142     // printf("size: %x\t",size);
1143     AkaiPartition* pPartition = new AkaiPartition(mpDisk,this);
1144     pPartition->Acquire();
1145     pPartition->SetOffset(offset);
1146    
1147     if (!pPartition->IsEmpty())
1148     {
1149     mpPartitions.push_back(pPartition); // Add this partitions' offset to the list.
1150     }
1151    
1152     mpDisk->SetPos(offset);
1153     if (!mpDisk->ReadInt16(&size))
1154     return (uint)mpPartitions.size();
1155     uint t = size;
1156     offset += AKAI_BLOCK_SIZE * t;
1157     // printf("new offset %d / size %d\n",offset,size);
1158     }
1159    
1160     return (uint)mpPartitions.size();
1161     }
1162    
1163     AkaiPartition* AkaiDisk::GetPartition(uint count)
1164     {
1165     std::list<AkaiPartition*>::iterator it;
1166     std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1167     uint i = 0;
1168     for (i = 0, it = mpPartitions.begin(); it != end && i != count; i++) it++;
1169    
1170     if (i != count || it == end)
1171     return NULL;
1172    
1173     (*it)->Acquire();
1174     return *it;
1175     }
1176    
1177     /////////////////////////
1178     // AkaiDiskElement
1179    
1180     int AkaiDiskElement::ReadFAT(DiskImage* pDisk, AkaiPartition* pPartition, int block)
1181     {
1182     int16_t value = 0;
1183     pDisk->SetPos(pPartition->GetOffset()+AKAI_FAT_OFFSET + block*2);
1184     pDisk->Read(&value, 2,1);
1185     return value;
1186     }
1187    
1188    
1189     bool AkaiDiskElement::ReadDirEntry(DiskImage* pDisk, AkaiPartition* pPartition, AkaiDirEntry& rEntry, int block, int pos)
1190     {
1191     char buffer[13];
1192    
1193     if (block == AKAI_ROOT_ENTRY_OFFSET)
1194     {
1195     pDisk->SetPos(pPartition->GetOffset()+AKAI_DIR_ENTRY_OFFSET + pos * AKAI_DIR_ENTRY_SIZE);
1196     pDisk->Read(buffer, 12, 1);
1197     AkaiToAscii(buffer, 12);
1198     rEntry.mName = buffer;
1199    
1200     pDisk->ReadInt16(&rEntry.mType);
1201     pDisk->ReadInt16(&rEntry.mStart);
1202     rEntry.mSize = 0;
1203     return true;
1204     }
1205     else
1206     {
1207     if (pos < 341)
1208     {
1209     pDisk->SetPos(block * AKAI_BLOCK_SIZE + pos * AKAI_FILE_ENTRY_SIZE + pPartition->GetOffset());
1210     }
1211     else
1212     {
1213     int temp;
1214     temp = ReadFAT(pDisk, pPartition, block);
1215     pDisk->SetPos(pPartition->GetOffset()+temp * AKAI_BLOCK_SIZE + (pos - 341) * AKAI_FILE_ENTRY_SIZE);
1216     }
1217    
1218     pDisk->Read(buffer, 12, 1);
1219     AkaiToAscii(buffer, 12);
1220     rEntry.mName = buffer;
1221    
1222     uint8_t t1,t2,t3;
1223     pDisk->SetPos(4,akai_stream_curpos);
1224     pDisk->Read(&t1, 1,1);
1225     rEntry.mType = t1;
1226    
1227     pDisk->Read(&t1, 1,1);
1228     pDisk->Read(&t2, 1,1);
1229     pDisk->Read(&t3, 1,1);
1230     rEntry.mSize = (unsigned char)t1 | ((unsigned char)t2 <<8) | ((unsigned char)t3<<16);
1231    
1232     pDisk->ReadInt16(&rEntry.mStart,1);
1233     return true;
1234     } // not root block
1235     }
1236    
1237     void AkaiDiskElement::AkaiToAscii(char * buffer, int length)
1238     {
1239     int i;
1240    
1241     for (i = 0; i < length; i++)
1242     {
1243     if (buffer[i]>=0 && buffer[i]<=9)
1244     buffer[i] +=48;
1245     else if (buffer[i]==10)
1246     buffer[i] = 32;
1247     else if (buffer[i]>=11 && buffer[i]<=36)
1248     buffer[i] = 64+(buffer[i]-10);
1249     else
1250     buffer[i] = 32;
1251     }
1252     buffer[length] = '\0';
1253     while (length-- > 0 && buffer[length] == 32)
1254     {
1255     // This block intentionaly left blank :)
1256     }
1257     buffer[length+1] = '\0';
1258     }
1259    
1260    
1261     //////////////////////////////////
1262     // DiskImage:
1263    
1264     DiskImage::DiskImage(const char* path)
1265     {
1266     Init();
1267     OpenStream(path);
1268     }
1269    
1270     #ifdef _CARBON_
1271     kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
1272     {
1273     kern_return_t kernResult;
1274     mach_port_t masterPort;
1275     CFMutableDictionaryRef classesToMatch;
1276    
1277     kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
1278     if ( KERN_SUCCESS != kernResult )
1279     {
1280     printf( "IOMasterPort returned %d\n", kernResult );
1281     }
1282    
1283     classesToMatch = IOServiceMatching( kIOCDMediaClass );
1284     if ( classesToMatch == NULL )
1285     {
1286     printf( "IOServiceMatching returned a NULL dictionary.\n" );
1287     }
1288     else
1289     {
1290     CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
1291     }
1292    
1293     kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
1294     if ( KERN_SUCCESS != kernResult )
1295     {
1296     printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
1297     }
1298    
1299     return kernResult;
1300     }
1301    
1302     kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
1303     {
1304     io_object_t nextMedia;
1305     kern_return_t kernResult = KERN_FAILURE;
1306    
1307     *bsdPath = '\0';
1308    
1309     nextMedia = IOIteratorNext( mediaIterator );
1310     if ( nextMedia )
1311     {
1312     CFTypeRef bsdPathAsCFString;
1313    
1314     bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia,
1315     CFSTR( kIOBSDNameKey ),
1316     kCFAllocatorDefault,
1317     0 );
1318     if ( bsdPathAsCFString )
1319     {
1320     size_t devPathLength;
1321    
1322     strcpy( bsdPath, _PATH_DEV );
1323     strcat( bsdPath, "r" );
1324    
1325     devPathLength = strlen( bsdPath );
1326    
1327     if ( CFStringGetCString( (__CFString*)bsdPathAsCFString,
1328     bsdPath + devPathLength,
1329     maxPathSize - devPathLength,
1330     kCFStringEncodingASCII ) )
1331     {
1332     printf( "BSD path: %s\n", bsdPath );
1333     kernResult = KERN_SUCCESS;
1334     }
1335    
1336     CFRelease( bsdPathAsCFString );
1337     }
1338     IOObjectRelease( nextMedia );
1339     }
1340    
1341     return kernResult;
1342     }
1343    
1344     struct _CDMSF
1345     {
1346     u_char minute;
1347     u_char second;
1348     u_char frame;
1349     };
1350    
1351     /* converting from minute-second to logical block entity */
1352     #define MSF_TO_LBA(msf) (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
1353    
1354     struct _CDTOC_Desc
1355     {
1356     u_char session;
1357     u_char ctrl_adr; /* typed to be machine and compiler independent */
1358     u_char tno;
1359     u_char point;
1360     struct _CDMSF address;
1361     u_char zero;
1362     struct _CDMSF p;
1363     };
1364    
1365     struct _CDTOC
1366     {
1367     u_short length; /* in native cpu endian */
1368     u_char first_session;
1369     u_char last_session;
1370     struct _CDTOC_Desc trackdesc[1];
1371     };
1372    
1373     static struct _CDTOC * ReadTOC (const char * devpath )
1374     {
1375     struct _CDTOC * toc_p = NULL;
1376     io_iterator_t iterator = 0;
1377     io_registry_entry_t service = 0;
1378     CFDictionaryRef properties = 0;
1379     CFDataRef data = 0;
1380     mach_port_t port = 0;
1381     char * devname;
1382     if (( devname = strrchr( devpath, '/' )) != NULL )
1383     {
1384     ++devname;
1385     }
1386     else
1387     {
1388     devname = ( char *) devpath;
1389     }
1390    
1391     if ( IOMasterPort(bootstrap_port, &port ) != KERN_SUCCESS )
1392     {
1393     printf( "IOMasterPort failed\n" ); goto Exit ;
1394     }
1395     if ( IOServiceGetMatchingServices( port, IOBSDNameMatching( port, 0, devname ),
1396     &iterator ) != KERN_SUCCESS )
1397     {
1398     printf( "IOServiceGetMatchingServices failed\n" ); goto Exit ;
1399     }
1400    
1401     char buffer[1024];
1402     IOObjectGetClass(iterator,buffer);
1403     printf("Service: %s\n",buffer);
1404    
1405    
1406     IOIteratorReset (iterator);
1407     service = IOIteratorNext( iterator );
1408    
1409     IOObjectRelease( iterator );
1410    
1411     iterator = 0;
1412     while ( service && !IOObjectConformsTo( service, "IOCDMedia" ))
1413     {
1414     char buffer[1024];
1415     IOObjectGetClass(service,buffer);
1416     printf("Service: %s\n",buffer);
1417    
1418     if ( IORegistryEntryGetParentIterator( service, kIOServicePlane, &iterator ) != KERN_SUCCESS )
1419     {
1420     printf( "IORegistryEntryGetParentIterator failed\n" );
1421     goto Exit ;
1422     }
1423    
1424     IOObjectRelease( service );
1425     service = IOIteratorNext( iterator );
1426     IOObjectRelease( iterator );
1427    
1428     }
1429     if ( service == NULL )
1430     {
1431     printf( "CD media not found\n" ); goto Exit ;
1432     }
1433     if ( IORegistryEntryCreateCFProperties( service, (__CFDictionary **) &properties,
1434     kCFAllocatorDefault,
1435     kNilOptions ) != KERN_SUCCESS )
1436     {
1437     printf( "IORegistryEntryGetParentIterator failed\n" ); goto Exit ;
1438     }
1439    
1440     data = (CFDataRef) CFDictionaryGetValue( properties, CFSTR( "TOC" ) );
1441     if ( data == NULL )
1442     {
1443     printf( "CFDictionaryGetValue failed\n" ); goto Exit ;
1444     }
1445     else
1446     {
1447    
1448     CFRange range;
1449     CFIndex buflen;
1450    
1451     buflen = CFDataGetLength( data ) + 1;
1452     range = CFRangeMake( 0, buflen );
1453     toc_p = ( struct _CDTOC *) malloc( buflen );
1454     if ( toc_p == NULL )
1455     {
1456     printf( "Out of memory\n" ); goto Exit ;
1457     }
1458     else
1459     {
1460     CFDataGetBytes( data, range, ( unsigned char *) toc_p );
1461     }
1462    
1463     /*
1464     fprintf( stderr, "Table of contents\n length %d first %d last %d\n",
1465     toc_p->length, toc_p->first_session, toc_p->last_session );
1466     */
1467    
1468     CFRelease( properties );
1469    
1470     }
1471     Exit :
1472     if ( service )
1473     {
1474     IOObjectRelease( service );
1475     }
1476    
1477     return toc_p;
1478     }
1479     #endif // _CARBON_
1480    
1481     DiskImage::DiskImage(int disk)
1482     {
1483     Init();
1484     #ifdef _WIN32_
1485     char buffer[1024];
1486     sprintf(buffer,"%c:\\",'A'+disk);
1487     mSize = GetFileSize(buffer,NULL);
1488    
1489     sprintf(buffer,"\\\\.\\%c:",'a'+disk);
1490     mFile = CreateFile(buffer, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1491    
1492     DWORD junk;
1493     DISK_GEOMETRY dg;
1494     DISK_GEOMETRY* pdg = &dg;
1495     BOOL res = DeviceIoControl(mFile,
1496     IOCTL_DISK_GET_DRIVE_GEOMETRY,
1497     NULL, 0,
1498     pdg, sizeof(*pdg),
1499     &junk,
1500     NULL);
1501    
1502     if (res)
1503     {
1504     mSize = dg.BytesPerSector * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.Cylinders.LowPart;
1505     mClusterSize = dg.BytesPerSector;
1506     }
1507 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__)
1508 schoenebeck 2572 kern_return_t kernResult;
1509     io_iterator_t mediaIterator;
1510     char bsdPath[ MAXPATHLEN ];
1511     kernResult = FindEjectableCDMedia( &mediaIterator );
1512     kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
1513     if ( bsdPath[ 0 ] != '\0' )
1514     {
1515     strcpy(bsdPath,"/dev/rdisk1s0");
1516    
1517     struct _CDTOC * toc = ReadTOC( bsdPath );
1518     if ( toc )
1519     {
1520     size_t toc_entries = ( toc->length - 2 ) / sizeof (struct _CDTOC_Desc );
1521    
1522     int start_sector = -1;
1523     int data_track = -1;
1524     // Iterate through the list backward. Pick the first data track and
1525     // get the address of the immediately previous (or following depending
1526     // on how you look at it). The difference in the sector numbers
1527     // is returned as the sized of the data track.
1528 schoenebeck 3053 for (ssize_t i=toc_entries - 1; i>=0; i-- )
1529 schoenebeck 2572 {
1530     if ( start_sector != -1 )
1531     {
1532     start_sector = MSF_TO_LBA(toc->trackdesc[i].p) - start_sector;
1533     break ;
1534    
1535     }
1536     if (( toc->trackdesc[i].ctrl_adr >> 4) != 1 )
1537     continue ;
1538     if ( toc->trackdesc[i].ctrl_adr & 0x04 )
1539     {
1540     data_track = toc->trackdesc[i].point;
1541     start_sector = MSF_TO_LBA(toc->trackdesc[i].p);
1542     }
1543     }
1544    
1545     free( toc );
1546     if ( start_sector == -1 )
1547     {
1548     start_sector = 0;
1549     }
1550     }
1551     // mSize = start_sector;
1552     // printf("size %d\n",mSize);
1553    
1554    
1555     mFile = open(bsdPath,O_RDONLY);
1556     if (!mFile)
1557     printf("Error while opening file: %s\n",bsdPath);
1558     else
1559     {
1560     printf("opened file: %s\n",bsdPath);
1561    
1562 schoenebeck 3053 mSize = (int) lseek(mFile,1000000000,SEEK_SET);
1563 schoenebeck 2572 printf("size %d\n",mSize);
1564     lseek(mFile,0,SEEK_SET);
1565    
1566     mSize = 700 * 1024 * 1024;
1567    
1568     }
1569     }
1570     if ( mediaIterator )
1571     {
1572     IOObjectRelease( mediaIterator );
1573     }
1574     #elif LINUX
1575     OpenStream("/dev/cdrom");
1576     #endif
1577     }
1578    
1579     void DiskImage::Init()
1580     {
1581     mFile = 0;
1582     mPos = 0;
1583     mCluster = (uint)-1;
1584     mStartFrame = -1;
1585     mEndFrame = -1;
1586 schoenebeck 2573 #ifdef WIN32
1587 schoenebeck 2572 mpCache = (char*) VirtualAlloc(NULL,mClusterSize,MEM_COMMIT,PAGE_READWRITE);
1588     #else
1589     mpCache = NULL; // we allocate the cache later when we know what type of media we access
1590     #endif
1591     }
1592    
1593     DiskImage::~DiskImage()
1594     {
1595 schoenebeck 2573 #ifdef WIN32
1596 schoenebeck 2572 if (mFile != INVALID_HANDLE_VALUE)
1597     {
1598     CloseHandle(mFile);
1599     }
1600 schoenebeck 2573 #elif defined _CARBON_ || defined(__APPLE__) || LINUX
1601 schoenebeck 2572 if (mFile)
1602     {
1603     close(mFile);
1604     }
1605     #endif
1606     if (mpCache)
1607     {
1608 schoenebeck 2573 #ifdef WIN32
1609 schoenebeck 2572 VirtualFree(mpCache, 0, MEM_RELEASE);
1610 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1611 schoenebeck 2572 free(mpCache);
1612     #endif
1613     }
1614     }
1615    
1616     akai_stream_state_t DiskImage::GetState() const
1617     {
1618     if (!mFile) return akai_stream_closed;
1619     if (mPos > mSize) return akai_stream_end_reached;
1620     return akai_stream_ready;
1621     }
1622    
1623     int DiskImage::GetPos() const
1624     {
1625     return mPos;
1626     }
1627    
1628     int DiskImage::SetPos(int Where, akai_stream_whence_t Whence)
1629     {
1630     // printf("setpos %d\n",Where);
1631     int w = Where;
1632     switch (Whence)
1633     {
1634     case akai_stream_start:
1635     mPos = w;
1636     break;
1637     /*case eStreamRewind:
1638     w = -w;*/
1639     case akai_stream_curpos:
1640     mPos += w;
1641     break;
1642     case akai_stream_end:
1643     mPos = mSize - w;
1644     break;
1645     }
1646     // if (mPos > mSize)
1647     // mPos = mSize;
1648     if (mPos < 0)
1649     mPos = 0;
1650     return mPos;
1651     }
1652    
1653     int DiskImage::Available (uint WordSize)
1654     {
1655     return (mSize-mPos)/WordSize;
1656     }
1657    
1658     int DiskImage::Read(void* pData, uint WordCount, uint WordSize)
1659     {
1660     int readbytes = 0;
1661     int sizetoread = WordCount * WordSize;
1662    
1663     while (sizetoread > 0) {
1664     if (mSize <= mPos) return readbytes / WordSize;
1665     int requestedCluster = (mRegularFile) ? mPos / mClusterSize
1666     : mPos / mClusterSize + mStartFrame;
1667     if (mCluster != requestedCluster) { // read the requested cluster into cache
1668     mCluster = requestedCluster;
1669 schoenebeck 2573 #ifdef WIN32
1670 schoenebeck 2572 if (mCluster * mClusterSize != SetFilePointer(mFile, mCluster * mClusterSize, NULL, FILE_BEGIN)) {
1671     printf("ERROR: couldn't seek device!\n");
1672 schoenebeck 2574 #if 0 // FIXME: endian correction is missing correct detection
1673 schoenebeck 2572 if ((readbytes > 0) && (mEndian != eEndianNative)) {
1674     switch (WordSize) {
1675     case 2: bswap_16_s ((uint16*)pData, readbytes); break;
1676     case 4: bswap_32_s ((uint32*)pData, readbytes); break;
1677     case 8: bswap_64_s ((uint64*)pData, readbytes); break;
1678     }
1679     }
1680 schoenebeck 2574 #endif
1681 schoenebeck 2572 return readbytes / WordSize;
1682     }
1683     DWORD size;
1684     ReadFile(mFile, mpCache, mClusterSize, &size, NULL);
1685 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1686 schoenebeck 2572 if (mCluster * mClusterSize != lseek(mFile, mCluster * mClusterSize, SEEK_SET))
1687     return readbytes / WordSize;
1688     // printf("trying to read %d bytes from device!\n",mClusterSize);
1689 schoenebeck 3053 /*int size =*/ read(mFile, mpCache, mClusterSize);
1690 schoenebeck 2572 // printf("read %d bytes from device!\n",size);
1691     #endif
1692     }
1693     // printf("read %d bytes at pos %d\n",WordCount*WordSize,mPos);
1694    
1695     int currentReadSize = sizetoread;
1696     int posInCluster = mPos % mClusterSize;
1697     if (currentReadSize > mClusterSize - posInCluster) // restrict to this current cached cluster.
1698     currentReadSize = mClusterSize - posInCluster;
1699    
1700     memcpy((uint8_t*)pData + readbytes, mpCache + posInCluster, currentReadSize);
1701    
1702     mPos += currentReadSize;
1703     readbytes += currentReadSize;
1704     sizetoread -= currentReadSize;
1705     // printf("new pos %d\n",mPos);
1706     }
1707    
1708 schoenebeck 2574 #if 0 // FIXME: endian correction is missing correct detection
1709 schoenebeck 2572 if ((readbytes > 0) && (mEndian != eEndianNative))
1710     switch (WordSize)
1711     {
1712     case 2: bswap_16_s ((uint16_t*)pData, readbytes); break;
1713     case 4: bswap_32_s ((uint32_t*)pData, readbytes); break;
1714     case 8: bswap_64_s ((uint64_t*)pData, readbytes); break;
1715     }
1716     #endif
1717    
1718     return readbytes / WordSize;
1719     }
1720    
1721     void DiskImage::ReadInt8(uint8_t* pData, uint WordCount) {
1722     Read(pData, WordCount, 1);
1723     }
1724    
1725     void DiskImage::ReadInt16(uint16_t* pData, uint WordCount) {
1726     int i;
1727     for (i = 0; i < WordCount; i++) {
1728     *(pData + i) = ReadInt16();
1729     }
1730     }
1731    
1732     void DiskImage::ReadInt32(uint32_t* pData, uint WordCount) {
1733     int i;
1734     for (i = 0; i < WordCount; i++) {
1735     *(pData + i) = ReadInt32();
1736     }
1737     }
1738    
1739     int DiskImage::ReadInt8(uint8_t* pData) {
1740     return Read(pData, 1, 1);
1741     }
1742    
1743     int DiskImage::ReadInt16(uint16_t* pData) {
1744     int result = Read(pData, 1, 2);
1745     #if WORDS_BIGENDIAN
1746     swapBytes_16(pData);
1747     #endif
1748     return result;
1749     }
1750    
1751     int DiskImage::ReadInt32(uint32_t* pData) {
1752     int result = Read(pData, 1, 4);
1753     #if WORDS_BIGENDIAN
1754     swapBytes_32(pData);
1755     #endif
1756     return result;
1757     }
1758    
1759     uint8_t DiskImage::ReadInt8()
1760     {
1761     uint8_t word;
1762     Read(&word,1,1);
1763     return word;
1764     }
1765    
1766     uint16_t DiskImage::ReadInt16()
1767     {
1768     uint16_t word;
1769     Read(&word,1,2);
1770     #if WORDS_BIGENDIAN
1771     swapBytes_16(&word);
1772     #endif
1773     return word;
1774     }
1775    
1776     uint32_t DiskImage::ReadInt32()
1777     {
1778     uint32_t word;
1779     Read(&word,1,4);
1780     #if WORDS_BIGENDIAN
1781     swapBytes_32(&word);
1782     #endif
1783     return word;
1784     }
1785    
1786     void DiskImage::OpenStream(const char* path) {
1787 schoenebeck 2573 #ifdef WIN32
1788 schoenebeck 2572 mFile = CreateFile(path, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1789     BY_HANDLE_FILE_INFORMATION FileInfo;
1790     GetFileInformationByHandle(mFile,&FileInfo);
1791     mSize = FileInfo.nFileSizeLow;
1792 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1793 schoenebeck 2572 struct stat filestat;
1794     stat(path,&filestat);
1795     mFile = open(path, O_RDONLY | O_NONBLOCK);
1796     if (mFile <= 0) {
1797     printf("Can't open %s\n", path);
1798     mFile = 0;
1799     return;
1800     }
1801     // TODO: we should also check here if 'path' is a link or something
1802     if (S_ISREG(filestat.st_mode)) { // regular file
1803     printf("Using regular Akai image file.\n");
1804     mRegularFile = true;
1805 schoenebeck 3053 mSize = (int) filestat.st_size;
1806 schoenebeck 2572 mClusterSize = DISK_CLUSTER_SIZE;
1807     mpCache = (char*) malloc(mClusterSize);
1808 schoenebeck 2573 } else { // CDROM
1809     #if defined(_CARBON_) || defined(__APPLE__)
1810     printf("Can't open %s: not a regular file\n", path);
1811     #else // Linux ...
1812 schoenebeck 2572 mRegularFile = false;
1813     mClusterSize = CD_FRAMESIZE;
1814     mpCache = (char*) malloc(mClusterSize);
1815    
1816     struct cdrom_tochdr tochdr;
1817     struct cdrom_tocentry tocentry;
1818     int prev_addr = 0;
1819     if (ioctl(mFile, CDROMREADTOCHDR, (unsigned long)&tochdr) < 0) {
1820     printf("Trying to read TOC of %s failed\n", path);
1821     close(mFile);
1822     mFile = 0;
1823     return;
1824     }
1825     printf("Total tracks: %d\n", tochdr.cdth_trk1);
1826    
1827     /* we access the CD with logical blocks as entity */
1828     tocentry.cdte_format = CDROM_LBA;
1829    
1830     int firstDataTrack = -1;
1831     int start, end, length;
1832    
1833     /* we pick up the lowest data track by iterating backwards, starting with Lead Out */
1834     for (int t = tochdr.cdth_trk1; t >= 0; t--) {
1835     tocentry.cdte_track = (t == tochdr.cdth_trk1) ? CDROM_LEADOUT : t + 1;
1836     if (ioctl(mFile, CDROMREADTOCENTRY, (unsigned long)&tocentry) < 0){
1837     printf("Failed to read TOC entry for track %d\n", tocentry.cdte_track);
1838     close(mFile);
1839     mFile = 0;
1840     return;
1841     }
1842     if (tocentry.cdte_track == CDROM_LEADOUT) {
1843     printf("Lead Out: Start(LBA)=%d\n", tocentry.cdte_addr.lba);
1844     }
1845     else {
1846     printf("Track %d: Start(LBA)=%d End(LBA)=%d Length(Blocks)=%d ",
1847     tocentry.cdte_track, tocentry.cdte_addr.lba, prev_addr - 1, prev_addr - tocentry.cdte_addr.lba);
1848     if (tocentry.cdte_ctrl & CDROM_DATA_TRACK) {
1849     printf("Type: Data\n");
1850     firstDataTrack = tocentry.cdte_track;
1851     start = tocentry.cdte_addr.lba;
1852     end = prev_addr - 1;
1853     length = prev_addr - tocentry.cdte_addr.lba;
1854     }
1855     else printf("Type: Audio\n");
1856     }
1857     prev_addr = tocentry.cdte_addr.lba;
1858     }
1859    
1860     if (firstDataTrack == -1) {
1861     printf("Sorry, no data track found on %s\n", path);
1862     close(mFile);
1863     mFile = 0;
1864     return;
1865     }
1866    
1867     printf("Ok, I'll pick track %d\n", firstDataTrack);
1868     mStartFrame = start;
1869     mEndFrame = end;
1870     mSize = length * CD_FRAMESIZE;
1871 schoenebeck 2573 #endif
1872 schoenebeck 2572 } // CDROM
1873     #endif
1874     }
1875    
1876     bool DiskImage::WriteImage(const char* path)
1877     {
1878 schoenebeck 2573 #if defined(_CARBON_) || defined(__APPLE__) || LINUX
1879 schoenebeck 2572 const uint bufferSize = 524288; // 512 kB
1880 schoenebeck 2587 int fOut = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC,
1881     S_IRUSR | S_IWUSR | S_IRGRP);
1882 schoenebeck 2572 if (mFile <= 0) {
1883     printf("Can't open output file %s\n", path);
1884     return false;
1885     }
1886     uint8_t* pBuffer = new uint8_t[bufferSize];
1887     SetPos(0);
1888     while (Available() > 0) {
1889     int readBytes = Read(pBuffer,bufferSize,1);
1890     if (readBytes > 0) write(fOut,pBuffer,readBytes);
1891     }
1892     delete[] pBuffer;
1893     close(fOut);
1894     return true;
1895     #endif // _CARBON_ || LINUX
1896 schoenebeck 2574 return false;
1897 schoenebeck 2572 }
1898    
1899     inline void DiskImage::swapBytes_16(void* Word) {
1900     uint8_t byteCache;
1901     byteCache = *((uint8_t*) Word);
1902     *((uint8_t*) Word) = *((uint8_t*) Word + 1);
1903     *((uint8_t*) Word + 1) = byteCache;
1904     }
1905    
1906     inline void DiskImage::swapBytes_32(void* Word) {
1907     uint8_t byteCache;
1908     byteCache = *((uint8_t*) Word);
1909     *((uint8_t*) Word) = *((uint8_t*) Word + 3);
1910     *((uint8_t*) Word + 3) = byteCache;
1911     byteCache = *((uint8_t*) Word + 1);
1912     *((uint8_t*) Word + 1) = *((uint8_t*) Word + 2);
1913     *((uint8_t*) Word + 2) = byteCache;
1914     }

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC