1 /***************************************************************************************** 2 Monkey's Audio MACLib.h (include for using MACLib.lib in your projects) 3 Copyright (C) 2000-2003 by Matthew T. Ashland All Rights Reserved. 4 5 Overview: 6 7 There are two main interfaces... create one (using CreateIAPExxx) and go to town: 8 9 IAPECompress - for creating APE files 10 IAPEDecompress - for decompressing and analyzing APE files 11 12 Note(s): 13 14 Unless otherwise specified, functions return ERROR_SUCCESS (0) on success and an 15 error code on failure. 16 17 The terminology "Sample" refers to a single sample value, and "Block" refers 18 to a collection of "Channel" samples. For simplicity, MAC typically uses blocks 19 everywhere so that channel mis-alignment cannot happen. (i.e. on a CD, a sample is 20 2 bytes and a block is 4 bytes ([2 bytes per sample] * [2 channels] = 4 bytes)) 21 22 Questions / Suggestions: 23 24 Please direct questions or comments to the Monkey's Audio developers board: 25 http://www.monkeysaudio.com/cgi-bin/YaBB/YaBB.cgi -> Developers 26 or, if necessary, matt @ monkeysaudio.com 27 *****************************************************************************************/ 28 29 #ifndef APE_MACLIB_H 30 #define APE_MACLIB_H 31 32 /************************************************************************************************* 33 APE File Format Overview: (pieces in order -- only valid for the latest version APE files) 34 35 JUNK - any amount of "junk" before the APE_DESCRIPTOR (so people that put ID3v2 tags on the files aren't hosed) 36 APE_DESCRIPTOR - defines the sizes (and offsets) of all the pieces, as well as the MD5 checksum 37 APE_HEADER - describes all of the necessary information about the APE file 38 SEEK TABLE - the table that represents seek offsets [optional] 39 HEADER DATA - the pre-audio data from the original file [optional] 40 APE FRAMES - the actual compressed audio (broken into frames for seekability) 41 TERMINATING DATA - the post-audio data from the original file [optional] 42 TAG - describes all the properties of the file [optional] 43 44 Notes: 45 46 Junk: 47 48 This block may not be supported in the future, so don't write any software that adds meta data 49 before the APE_DESCRIPTOR. Please use the APE Tag for any meta data. 50 51 Seek Table: 52 53 A 32-bit unsigned integer array of offsets from the header to the frame data. May become "delta" 54 values someday to better suit huge files. 55 56 MD5 Hash: 57 58 Since the header is the last part written to an APE file, you must calculate the MD5 checksum out of order. 59 So, you first calculate from the tail of the seek table to the end of the terminating data. 60 Then, go back and do from the end of the descriptor to the tail of the seek table. 61 You may wish to just cache the header data when starting and run it last, so you don't 62 need to seek back in the I/O. 63 *************************************************************************************************/ 64 65 #include "NoWindows.h" 66 #include "All.h" 67 68 /***************************************************************************************** 69 Defines 70 *****************************************************************************************/ 71 #define COMPRESSION_LEVEL_FAST 1000 72 #define COMPRESSION_LEVEL_NORMAL 2000 73 #define COMPRESSION_LEVEL_HIGH 3000 74 #define COMPRESSION_LEVEL_EXTRA_HIGH 4000 75 #define COMPRESSION_LEVEL_INSANE 5000 76 77 #define MAC_FORMAT_FLAG_8_BIT 1 // is 8-bit [OBSOLETE] 78 #define MAC_FORMAT_FLAG_CRC 2 // uses the new CRC32 error detection [OBSOLETE] 79 #define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // uint32 nPeakLevel after the header [OBSOLETE] 80 #define MAC_FORMAT_FLAG_24_BIT 8 // is 24-bit [OBSOLETE] 81 #define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // has the number of seek elements after the peak level 82 #define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // create the wave header on decompression (not stored) 83 84 #define CREATE_WAV_HEADER_ON_DECOMPRESSION -1 85 #define MAX_AUDIO_BYTES_UNKNOWN -1 86 87 typedef void (__stdcall * APE_PROGRESS_CALLBACK) (int); 88 89 /***************************************************************************************** 90 WAV header structure 91 *****************************************************************************************/ 92 struct WAVE_HEADER 93 { 94 // RIFF header 95 char cRIFFHeader[4]; 96 unsigned int nRIFFBytes; 97 98 // data type 99 char cDataTypeID[4]; 100 101 // wave format 102 char cFormatHeader[4]; 103 unsigned int nFormatBytes; 104 105 unsigned short nFormatTag; 106 unsigned short nChannels; 107 unsigned int nSamplesPerSec; 108 unsigned int nAvgBytesPerSec; 109 unsigned short nBlockAlign; 110 unsigned short nBitsPerSample; 111 112 // data chunk header 113 char cDataHeader[4]; 114 unsigned int nDataBytes; 115 }; 116 117 /***************************************************************************************** 118 APE_DESCRIPTOR structure (file header that describes lengths, offsets, etc.) 119 *****************************************************************************************/ 120 struct APE_DESCRIPTOR 121 { 122 char cID[4]; // should equal 'MAC ' 123 uint16 nVersion; // version number * 1000 (3.81 = 3810) 124 125 uint32 nDescriptorBytes; // the number of descriptor bytes (allows later expansion of this header) 126 uint32 nHeaderBytes; // the number of header APE_HEADER bytes 127 uint32 nSeekTableBytes; // the number of bytes of the seek table 128 uint32 nHeaderDataBytes; // the number of header data bytes (from original file) 129 uint32 nAPEFrameDataBytes; // the number of bytes of APE frame data 130 uint32 nAPEFrameDataBytesHigh; // the high order number of APE frame data bytes 131 uint32 nTerminatingDataBytes; // the terminating data of the file (not including tag data) 132 133 uint8 cFileMD5[16]; // the MD5 hash of the file (see notes for usage... it's a littly tricky) 134 }; 135 136 /***************************************************************************************** 137 APE_HEADER structure (describes the format, duration, etc. of the APE file) 138 *****************************************************************************************/ 139 struct APE_HEADER 140 { 141 uint16 nCompressionLevel; // the compression level (see defines I.E. COMPRESSION_LEVEL_FAST) 142 uint16 nFormatFlags; // any format flags (for future use) 143 144 uint32 nBlocksPerFrame; // the number of audio blocks in one frame 145 uint32 nFinalFrameBlocks; // the number of audio blocks in the final frame 146 uint32 nTotalFrames; // the total number of frames 147 148 uint16 nBitsPerSample; // the bits per sample (typically 16) 149 uint16 nChannels; // the number of channels (1 or 2) 150 uint32 nSampleRate; // the sample rate (typically 44100) 151 }; 152 153 /************************************************************************************************* 154 Classes (fully defined elsewhere) 155 *************************************************************************************************/ 156 class CIO; 157 class CInputSource; 158 class CAPEInfo; 159 160 /************************************************************************************************* 161 IAPEDecompress fields - used when querying for information 162 163 Note(s): 164 -the distinction between APE_INFO_XXXX and APE_DECOMPRESS_XXXX is that the first is querying the APE 165 information engine, and the other is querying the decompressor, and since the decompressor can be 166 a range of an APE file (for APL), differences will arise. Typically, use the APE_DECOMPRESS_XXXX 167 fields when querying for info about the length, etc. so APL will work properly. 168 (i.e. (APE_INFO_TOTAL_BLOCKS != APE_DECOMPRESS_TOTAL_BLOCKS) for APL files) 169 *************************************************************************************************/ 170 enum APE_DECOMPRESS_FIELDS 171 { 172 APE_INFO_FILE_VERSION = 1000, // version of the APE file * 1000 (3.93 = 3930) [ignored, ignored] 173 APE_INFO_COMPRESSION_LEVEL = 1001, // compression level of the APE file [ignored, ignored] 174 APE_INFO_FORMAT_FLAGS = 1002, // format flags of the APE file [ignored, ignored] 175 APE_INFO_SAMPLE_RATE = 1003, // sample rate (Hz) [ignored, ignored] 176 APE_INFO_BITS_PER_SAMPLE = 1004, // bits per sample [ignored, ignored] 177 APE_INFO_BYTES_PER_SAMPLE = 1005, // number of bytes per sample [ignored, ignored] 178 APE_INFO_CHANNELS = 1006, // channels [ignored, ignored] 179 APE_INFO_BLOCK_ALIGN = 1007, // block alignment [ignored, ignored] 180 APE_INFO_BLOCKS_PER_FRAME = 1008, // number of blocks in a frame (frames are used internally) [ignored, ignored] 181 APE_INFO_FINAL_FRAME_BLOCKS = 1009, // blocks in the final frame (frames are used internally) [ignored, ignored] 182 APE_INFO_TOTAL_FRAMES = 1010, // total number frames (frames are used internally) [ignored, ignored] 183 APE_INFO_WAV_HEADER_BYTES = 1011, // header bytes of the decompressed WAV [ignored, ignored] 184 APE_INFO_WAV_TERMINATING_BYTES = 1012, // terminating bytes of the decompressed WAV [ignored, ignored] 185 APE_INFO_WAV_DATA_BYTES = 1013, // data bytes of the decompressed WAV [ignored, ignored] 186 APE_INFO_WAV_TOTAL_BYTES = 1014, // total bytes of the decompressed WAV [ignored, ignored] 187 APE_INFO_APE_TOTAL_BYTES = 1015, // total bytes of the APE file [ignored, ignored] 188 APE_INFO_TOTAL_BLOCKS = 1016, // total blocks of audio data [ignored, ignored] 189 APE_INFO_LENGTH_MS = 1017, // length in ms (1 sec = 1000 ms) [ignored, ignored] 190 APE_INFO_AVERAGE_BITRATE = 1018, // average bitrate of the APE [ignored, ignored] 191 APE_INFO_FRAME_BITRATE = 1019, // bitrate of specified APE frame [frame index, ignored] 192 APE_INFO_DECOMPRESSED_BITRATE = 1020, // bitrate of the decompressed WAV [ignored, ignored] 193 APE_INFO_PEAK_LEVEL = 1021, // peak audio level (obsolete) (-1 is unknown) [ignored, ignored] 194 APE_INFO_SEEK_BIT = 1022, // bit offset [frame index, ignored] 195 APE_INFO_SEEK_BYTE = 1023, // byte offset [frame index, ignored] 196 APE_INFO_WAV_HEADER_DATA = 1024, // error code [buffer *, max bytes] 197 APE_INFO_WAV_TERMINATING_DATA = 1025, // error code [buffer *, max bytes] 198 APE_INFO_WAVEFORMATEX = 1026, // error code [waveformatex *, ignored] 199 APE_INFO_IO_SOURCE = 1027, // I/O source (CIO *) [ignored, ignored] 200 APE_INFO_FRAME_BYTES = 1028, // bytes (compressed) of the frame [frame index, ignored] 201 APE_INFO_FRAME_BLOCKS = 1029, // blocks in a given frame [frame index, ignored] 202 APE_INFO_TAG = 1030, // point to tag (CAPETag *) [ignored, ignored] 203 204 APE_DECOMPRESS_CURRENT_BLOCK = 2000, // current block location [ignored, ignored] 205 APE_DECOMPRESS_CURRENT_MS = 2001, // current millisecond location [ignored, ignored] 206 APE_DECOMPRESS_TOTAL_BLOCKS = 2002, // total blocks in the decompressors range [ignored, ignored] 207 APE_DECOMPRESS_LENGTH_MS = 2003, // total blocks in the decompressors range [ignored, ignored] 208 APE_DECOMPRESS_CURRENT_BITRATE = 2004, // current bitrate [ignored, ignored] 209 APE_DECOMPRESS_AVERAGE_BITRATE = 2005, // average bitrate (works with ranges) [ignored, ignored] 210 211 APE_INTERNAL_INFO = 3000, // for internal use -- don't use (returns APE_FILE_INFO *) [ignored, ignored] 212 }; 213 214 /************************************************************************************************* 215 IAPEDecompress - interface for working with existing APE files (decoding, seeking, analyzing, etc.) 216 *************************************************************************************************/ 217 class IAPEDecompress 218 { 219 public: 220 221 // destructor (needed so implementation's destructor will be called) ~IAPEDecompress()222 virtual ~IAPEDecompress() {} 223 224 /********************************************************************************************* 225 * Decompress / Seek 226 *********************************************************************************************/ 227 228 ////////////////////////////////////////////////////////////////////////////////////////////// 229 // GetData(...) - gets raw decompressed audio 230 // 231 // Parameters: 232 // char * pBuffer 233 // a pointer to a buffer to put the data into 234 // int nBlocks 235 // the number of audio blocks desired (see note at intro about blocks vs. samples) 236 // int * pBlocksRetrieved 237 // the number of blocks actually retrieved (could be less at end of file or on critical failure) 238 ////////////////////////////////////////////////////////////////////////////////////////////// 239 virtual int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved) = 0; 240 241 ////////////////////////////////////////////////////////////////////////////////////////////// 242 // Seek(...) - seeks 243 // 244 // Parameters: 245 // int nBlockOffset 246 // the block to seek to (see note at intro about blocks vs. samples) 247 ////////////////////////////////////////////////////////////////////////////////////////////// 248 virtual int Seek(int nBlockOffset) = 0; 249 250 /********************************************************************************************* 251 * Get Information 252 *********************************************************************************************/ 253 254 ////////////////////////////////////////////////////////////////////////////////////////////// 255 // GetInfo(...) - get information about the APE file or the state of the decompressor 256 // 257 // Parameters: 258 // APE_DECOMPRESS_FIELDS Field 259 // the field we're querying (see APE_DECOMPRESS_FIELDS above for more info) 260 // int nParam1 261 // generic parameter... usage is listed in APE_DECOMPRESS_FIELDS 262 // int nParam2 263 // generic parameter... usage is listed in APE_DECOMPRESS_FIELDS 264 ////////////////////////////////////////////////////////////////////////////////////////////// 265 virtual int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0) = 0; 266 }; 267 268 /************************************************************************************************* 269 IAPECompress - interface for creating APE files 270 271 Usage: 272 273 To create an APE file, you Start(...), then add data (in a variety of ways), then Finish(...) 274 *************************************************************************************************/ 275 class IAPECompress 276 { 277 public: 278 279 // destructor (needed so implementation's destructor will be called) ~IAPECompress()280 virtual ~IAPECompress() {} 281 282 /********************************************************************************************* 283 * Start 284 *********************************************************************************************/ 285 286 ////////////////////////////////////////////////////////////////////////////////////////////// 287 // Start(...) / StartEx(...) - starts encoding 288 // 289 // Parameters: 290 // CIO * pioOutput / const str_utf16 * pFilename 291 // the output... either a filename or an I/O source 292 // WAVEFORMATEX * pwfeInput 293 // format of the audio to encode (use FillWaveFormatEx() if necessary) 294 // int nMaxAudioBytes 295 // the absolute maximum audio bytes that will be encoded... encoding fails with a 296 // ERROR_APE_COMPRESS_TOO_MUCH_DATA if you attempt to encode more than specified here 297 // (if unknown, use MAX_AUDIO_BYTES_UNKNOWN to allocate as much storage in the seek table as 298 // possible... limit is then 2 GB of data (~4 hours of CD music)... this wastes around 299 // 30kb, so only do it if completely necessary) 300 // int nCompressionLevel 301 // the compression level for the APE file (fast - extra high) 302 // (note: extra-high is much slower for little gain) 303 // const void * pHeaderData 304 // a pointer to a buffer containing the WAV header (data before the data block in the WAV) 305 // (note: use NULL for on-the-fly encoding... see next parameter) 306 // int nHeaderBytes 307 // number of bytes in the header data buffer (use CREATE_WAV_HEADER_ON_DECOMPRESSION and 308 // NULL for the pHeaderData and MAC will automatically create the appropriate WAV header 309 // on decompression) 310 ////////////////////////////////////////////////////////////////////////////////////////////// 311 312 virtual int Start(const str_utf16 * pOutputFilename, const WAVEFORMATEX * pwfeInput, 313 int nMaxAudioBytes = MAX_AUDIO_BYTES_UNKNOWN, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, 314 const void * pHeaderData = NULL, int nHeaderBytes = CREATE_WAV_HEADER_ON_DECOMPRESSION) = 0; 315 316 virtual int StartEx(CIO * pioOutput, const WAVEFORMATEX * pwfeInput, 317 int nMaxAudioBytes = MAX_AUDIO_BYTES_UNKNOWN, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, 318 const void * pHeaderData = NULL, int nHeaderBytes = CREATE_WAV_HEADER_ON_DECOMPRESSION) = 0; 319 320 /********************************************************************************************* 321 * Add / Compress Data 322 * - there are 3 ways to add data: 323 * 1) simple call AddData(...) 324 * 2) lock MAC's buffer, copy into it, and unlock (LockBuffer(...) / UnlockBuffer(...)) 325 * 3) from an I/O source (AddDataFromInputSource(...)) 326 *********************************************************************************************/ 327 328 ////////////////////////////////////////////////////////////////////////////////////////////// 329 // AddData(...) - adds data to the encoder 330 // 331 // Parameters: 332 // unsigned char * pData 333 // a pointer to a buffer containing the raw audio data 334 // int nBytes 335 // the number of bytes in the buffer 336 ////////////////////////////////////////////////////////////////////////////////////////////// 337 virtual int AddData(unsigned char * pData, int nBytes) = 0; 338 339 ////////////////////////////////////////////////////////////////////////////////////////////// 340 // GetBufferBytesAvailable(...) - returns the number of bytes available in the buffer 341 // (helpful when locking) 342 ////////////////////////////////////////////////////////////////////////////////////////////// 343 virtual int GetBufferBytesAvailable() = 0; 344 345 ////////////////////////////////////////////////////////////////////////////////////////////// 346 // LockBuffer(...) - locks MAC's buffer so we can copy into it 347 // 348 // Parameters: 349 // int * pBytesAvailable 350 // returns the number of bytes available in the buffer (DO NOT COPY MORE THAN THIS IN) 351 // 352 // Return: 353 // pointer to the buffer (add at that location) 354 ////////////////////////////////////////////////////////////////////////////////////////////// 355 virtual unsigned char * LockBuffer(int * pBytesAvailable) = 0; 356 357 ////////////////////////////////////////////////////////////////////////////////////////////// 358 // UnlockBuffer(...) - releases the buffer 359 // 360 // Parameters: 361 // int nBytesAdded 362 // the number of bytes copied into the buffer 363 // BOOL bProcess 364 // whether MAC should process as much as possible of the buffer 365 ////////////////////////////////////////////////////////////////////////////////////////////// 366 virtual int UnlockBuffer(int nBytesAdded, BOOL bProcess = TRUE) = 0; 367 368 369 ////////////////////////////////////////////////////////////////////////////////////////////// 370 // AddDataFromInputSource(...) - use a CInputSource (input source) to add data 371 // 372 // Parameters: 373 // CInputSource * pInputSource 374 // a pointer to the input source 375 // int nMaxBytes 376 // the maximum number of bytes to let MAC add (-1 if MAC can add any amount) 377 // int * pBytesAdded 378 // returns the number of bytes added from the I/O source 379 ////////////////////////////////////////////////////////////////////////////////////////////// 380 virtual int AddDataFromInputSource(CInputSource * pInputSource, int nMaxBytes = -1, int * pBytesAdded = NULL) = 0; 381 382 /********************************************************************************************* 383 * Finish / Kill 384 *********************************************************************************************/ 385 386 ////////////////////////////////////////////////////////////////////////////////////////////// 387 // Finish(...) - ends encoding and finalizes the file 388 // 389 // Parameters: 390 // unsigned char * pTerminatingData 391 // a pointer to a buffer containing the information to place at the end of the APE file 392 // (comprised of the WAV terminating data (data after the data block in the WAV) followed 393 // by any tag information) 394 // int nTerminatingBytes 395 // number of bytes in the terminating data buffer 396 // int nWAVTerminatingBytes 397 // the number of bytes of the terminating data buffer that should be appended to a decoded 398 // WAV file (it's basically nTerminatingBytes - the bytes that make up the tag) 399 ////////////////////////////////////////////////////////////////////////////////////////////// 400 virtual int Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) = 0; 401 402 ////////////////////////////////////////////////////////////////////////////////////////////// 403 // Kill(...) - stops encoding and deletes the output file 404 // --- NOT CURRENTLY IMPLEMENTED --- 405 ////////////////////////////////////////////////////////////////////////////////////////////// 406 virtual int Kill() = 0; 407 }; 408 409 /************************************************************************************************* 410 Functions to create the interfaces 411 412 Usage: 413 Interface creation returns a NULL pointer on failure (and fills error code if it was passed in) 414 415 Usage example: 416 int nErrorCode; 417 IAPEDecompress * pAPEDecompress = CreateIAPEDecompress("c:\\1.ape", &nErrorCode); 418 if (pAPEDecompress == NULL) 419 { 420 // failure... nErrorCode will have specific code 421 } 422 423 *************************************************************************************************/ 424 extern "C" 425 { 426 IAPEDecompress * __stdcall CreateIAPEDecompress(const str_utf16 * pFilename, int * pErrorCode = NULL); 427 IAPEDecompress * __stdcall CreateIAPEDecompressEx(CIO * pIO, int * pErrorCode = NULL); 428 IAPEDecompress * __stdcall CreateIAPEDecompressEx2(CAPEInfo * pAPEInfo, int nStartBlock = -1, int nFinishBlock = -1, int * pErrorCode = NULL); 429 IAPECompress * __stdcall CreateIAPECompress(int * pErrorCode = NULL); 430 } 431 432 /************************************************************************************************* 433 Simple functions - see the SDK sample projects for usage examples 434 *************************************************************************************************/ 435 extern "C" 436 { 437 // process whole files 438 DLLEXPORT int __stdcall CompressFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, int * pPercentageDone = NULL, APE_PROGRESS_CALLBACK ProgressCallback = 0, int * pKillFlag = NULL); 439 DLLEXPORT int __stdcall DecompressFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); 440 DLLEXPORT int __stdcall ConvertFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); 441 DLLEXPORT int __stdcall VerifyFile(const str_ansi * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible); 442 443 DLLEXPORT int __stdcall CompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, int * pPercentageDone = NULL, APE_PROGRESS_CALLBACK ProgressCallback = 0, int * pKillFlag = NULL); 444 DLLEXPORT int __stdcall DecompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); 445 DLLEXPORT int __stdcall ConvertFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); 446 DLLEXPORT int __stdcall VerifyFileW(const str_utf16 * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible = FALSE); 447 448 // helper functions 449 DLLEXPORT int __stdcall FillWaveFormatEx(WAVEFORMATEX * pWaveFormatEx, int nSampleRate = 44100, int nBitsPerSample = 16, int nChannels = 2); 450 DLLEXPORT int __stdcall FillWaveHeader(WAVE_HEADER * pWAVHeader, int nAudioBytes, WAVEFORMATEX * pWaveFormatEx, int nTerminatingBytes = 0); 451 } 452 453 #endif // #ifndef APE_MACLIB_H 454