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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3983 - (hide annotations) (download)
Tue Aug 3 16:05:32 2021 UTC (2 years, 8 months ago) by schoenebeck
File size: 56626 byte(s)
Drop "svn:executable" property from all source files.

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

  ViewVC Help
Powered by ViewVC