#include #include #include #include #include #include #include "voc.h" static FILE *VocOpenFile( const char *filename, const char *mode, off_t *size_rtn ); int VocIsFileVoc(const char *filename); unsigned int VocGetEnvSampleRate(voc_data_struct *vd); void VocDestroyData(voc_data_struct *vd); int VocAddDataBlock(voc_data_struct *vd, int type); int VocReadHeader(const char *filename, FILE *fp, voc_data_struct *vd); int VocReadPartialData( voc_data_struct *vd, off_t offset, off_t max_chunk_size, int read_opt ); #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define ABSOLUTE(x) (((x) < 0) ? ((x) * -1) : (x)) /* * Procedure to open a voc file and return its entire file size. */ static FILE *VocOpenFile( const char *filename, const char *mode, off_t *size_rtn ) { FILE *fp; struct stat stat_buf; if(filename == NULL) { fprintf(stderr, "Cannot open file with no name.\n"); *size_rtn = 0; return(NULL); } if(mode == NULL) { fprintf(stderr, "%s: Open mode not givin.\n", filename); *size_rtn = 0; return(NULL); } if(stat(filename, &stat_buf)) { fprintf(stderr, "%s: No such file.\n", filename); *size_rtn = 0; return(NULL); } *size_rtn = stat_buf.st_size; fp = fopen(filename, mode); if(fp == NULL) { fprintf(stderr, "%s: Cannot open.\n", filename); *size_rtn = 0; return(NULL); } return(fp); } /* * Checks if the specified file is a VOC file. */ int VocIsFileVoc(const char *filename) { int i; char magicname[0x14]; FILE *fp; off_t filesize; /* Open file. */ fp = VocOpenFile(filename, "rb", &filesize); if(fp == NULL) return(VocErrorNoAccess); if(filesize == 0) return(VocErrorNoAccess); /* Bytes 0x00 to 0x13: Get magic name. */ for(i = 0; i < 0x13; i++) magicname[i] = (char)fgetc(fp); /* Close file. */ fclose(fp); /* Check magic name. */ magicname[0x13] = '\0'; if(strcmp(magicname, VocMagicName)) return(VocErrorNotVoc); else return(VocSuccess); } /* * Returns the sample rate of that of the first data block * of type VocBlockTypeSoundData that is found on the blocks * in the vd structure. * * If vd is invalid or no blocks of type VocBlockTypeSoundData exist * then the value VocDefaultSampleRate will be returned. */ unsigned int VocGetEnvSampleRate(voc_data_struct *vd) { int i; unsigned int sample_rate = VocDefaultSampleRate; voc_block_struct **ptr, *block_ptr; if(vd == NULL) return(sample_rate); /* Go through each data block. */ for(i = 0, ptr = vd->datablock; i < vd->total_datablocks; i++, ptr++) { block_ptr = *ptr; if(block_ptr == NULL) continue; /* Check if this type is a VocBlockTypeSoundData. */ if(block_ptr->type == VocBlockTypeSoundData) { sample_rate = block_ptr->sample_rate; break; } } /* Sanitize sample rate. */ if(sample_rate == 0) sample_rate = VocDefaultSampleRate; return(sample_rate); } /* * Deallocates all resources and closes any opened files specified * in the given vd structure and resets its values. */ void VocDestroyData(voc_data_struct *vd) { int i; voc_block_struct **ptr, *block_ptr; if(vd == NULL) return; /* Data blocks. */ for(i = 0, ptr = vd->datablock; i < vd->total_datablocks; i++, ptr++ ) { block_ptr = *ptr; if(block_ptr == NULL) continue; free(block_ptr->str); free(block_ptr); } free(vd->datablock); vd->datablock = NULL; vd->total_datablocks = 0; /* Reset values. */ free(vd->filename); vd->filename = NULL; vd->filesize = 0; if(vd->fp != NULL) { fclose(vd->fp); vd->fp = NULL; } free(vd->data); vd->data = NULL; vd->data_len = 0; vd->total_data_len = 0; vd->major_version = 0; vd->minor_version = 0; vd->first_data_block = 0; return; } /* * Allocates a new data block on the vd structure by the specified * type. */ int VocAddDataBlock(voc_data_struct *vd, int type) { int n; if(vd == NULL) return(-1); if(vd->total_datablocks < 0) vd->total_datablocks = 0; n = vd->total_datablocks; vd->total_datablocks++; vd->datablock = (voc_block_struct **)realloc( vd->datablock, vd->total_datablocks * sizeof(voc_block_struct *) ); if(vd->datablock == NULL) { vd->total_datablocks = 0; return(-1); } vd->datablock[n] = (voc_block_struct *)calloc( 1, sizeof(voc_block_struct) ); if(vd->datablock[n] == NULL) { vd->total_datablocks = n; return(-1); } /* Set type. */ vd->datablock[n]->type = type; return(n); } /* * Reads the header from the stream fp and initializes the given * vd structure if fp is valid and a voc file. * * The given filename is used only for referance purposes, it can be * NULL. * * If VocSuccess is returned then the given fp will be transfered * to the vd structure and should not be referanced again. For all * other return values, the calling function is responsible for * closing the given fp. */ int VocReadHeader(const char *filename, FILE *fp, voc_data_struct *vd) { int x, y, z; unsigned char type; off_t nextseek; /* To identify that this is a VOC file. */ char magicname[0x14]; /* For calculating that pesky 3 byte unsigned int block offset. */ unsigned int size_x, size_y, size_z; int bytes_read = 0; struct stat stat_buf; off_t filesize; /* Error checks. */ if(vd == NULL) return(VocErrorNoBuffers); if(fp == NULL) return(VocErrorBadValue); /* Rewind to beginning of voc file. */ rewind(fp); /* Reset values. */ memset(vd, 0x00, sizeof(voc_data_struct)); /* Get size of file. */ if(fstat(fileno(fp), &stat_buf)) return(VocErrorNoAccess); filesize = stat_buf.st_size; if(filesize == 0) return(VocErrorNoAccess); /* Is this is a voc file? Check 0x00 to 0x12 for magic number. */ for(x = 0; x < 0x13; x++) { magicname[x] = (char)fgetc(fp); bytes_read++; } magicname[0x13] = '\0'; /* Magic name does not match? If so then this is not a voc * file. */ if(strcmp(magicname, VocMagicName)) return(VocErrorNotVoc); /* Begin reading voc file header. */ /* Record file name. */ if(filename != NULL) vd->filename = strdup(filename); /* Record file size. */ vd->filesize = filesize; /* 0x13: Read but skip abort print byte. */ fgetc(fp); bytes_read++; /* 0x14 to 0x15: Get offset to first data block. */ x = fgetc(fp); y = fgetc(fp); bytes_read += 2; /* Calculate, LSBF. */ vd->first_data_block = x + (y * 256); /* 0x16 to 0x17: Minor and major version numbers. */ vd->minor_version = (unsigned char)fgetc(fp); vd->major_version = (unsigned char)fgetc(fp); bytes_read += 2; /* 0x18 to 0x19: Extended stuff. */ x = fgetc(fp); y = fgetc(fp); bytes_read += 2; /* Move fp to start of first block. */ fseek(fp, vd->first_data_block, SEEK_SET); nextseek = ftell(fp); while(nextseek < vd->filesize) { fseek(fp, nextseek, SEEK_SET); /* Read type. */ type = (unsigned char)fgetc(fp); bytes_read++; switch(type) { case VocBlockTypeTerminator: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* *** Terminator type has no size! *** */ /* Get size. */ vd->datablock[y]->size = 1; /* Set nextseek. */ nextseek += 1; break; case VocBlockTypeSoundData: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Calculate and set data_start_pos. */ vd->datablock[y]->data_start_pos = nextseek + 6; /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* Get data_len. */ if((long)vd->datablock[y]->size >= 2) vd->datablock[y]->data_len = vd->datablock[y]->size - 2; else vd->datablock[y]->data_len = 2; /* Get sample rate. */ z = fgetc(fp); bytes_read++; if(z >= 256) z = 255; vd->datablock[y]->sample_rate = (unsigned int)( (double)(-1000000) / (double)(z - 256) ); /* Get compresion type. */ vd->datablock[y]->compression_type = (unsigned char)fgetc(fp); bytes_read++; /* Calculate bits. */ switch(vd->datablock[y]->compression_type) { case 0x00: vd->datablock[y]->bits = 8; break; case 0x01: vd->datablock[y]->bits = 4; break; case 0x02: vd->datablock[y]->bits = 2.6; break; case 0x03: vd->datablock[y]->bits = 2; break; case 0x04: /* Multi DAC, not standard */ vd->datablock[y]->bits = 8; break; default: vd->datablock[y]->bits = 8; break; } break; case VocBlockTypeSoundDataContinue: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Calculate and set data_start_pos. */ vd->datablock[y]->data_start_pos = nextseek + 4; /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* Get data_len. */ vd->datablock[y]->data_len = vd->datablock[y]->size; break; case VocBlockTypeSilence: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* Get silence length. */ fread(&(vd->datablock[y]->silence_length), 1, 2, fp); bytes_read += 2; /* Get sample rate. */ z = fgetc(fp); bytes_read++; if(z >= 256) z = 255; vd->datablock[y]->sample_rate = (unsigned int)( (double)(-1000000) / (double)(z - 256) ); break; case VocBlockTypeMarker: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* Get marker number. */ fread(&(vd->datablock[y]->marker_num), 1, 2, fp); bytes_read += 2; break; case VocBlockTypeASCII: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* Get string. */ vd->datablock[y]->str_len = vd->datablock[y]->size; vd->datablock[y]->str = (char *)calloc(1, (vd->datablock[y]->str_len + 1) * sizeof(char) ); for(z = 0; z < vd->datablock[y]->size; z++) { vd->datablock[y]->str[z] = (char)fgetc(fp); bytes_read++; } vd->datablock[y]->str[z] = '\0'; break; case VocBlockTypeRepeat: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* Get repeat count. */ fread(&(vd->datablock[y]->repeat_count), 1, 2, fp); bytes_read += 2; break; case VocBlockTypeEndRepeat: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* There is no info for this field. */ break; case VocBlockTypeExtended: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); /* Get time constant. */ fread(&(vd->datablock[y]->time_constant), 1, 2, fp); bytes_read += 2; /* Get pack. */ vd->datablock[y]->pack = (unsigned char)fgetc(fp); bytes_read++; /* Get mode (0 = mono, 1 = stereo). */ vd->datablock[y]->mode = (unsigned char)fgetc(fp); bytes_read++; break; /* Unknown, assume field is just 1 byte line. */ default: /* Allocate data block. */ y = VocAddDataBlock((voc_data_struct *)vd, type); if(y < 0) return(VocErrorNoBuffers); /* Get size. */ size_x = (unsigned int)fgetc(fp); size_y = (unsigned int)fgetc(fp); size_z = (unsigned int)fgetc(fp); bytes_read += 3; vd->datablock[y]->size = size_x + (size_y << 8) + (size_z << 16); /* Set nextseek. */ nextseek += (vd->datablock[y]->size + 4); break; } } /* Add audio lengths from all data blocks containing audio data. */ vd->total_data_len = 0; for(x = 0; x < vd->total_datablocks; x++) { if(vd->datablock[x] == NULL) continue; if( (vd->datablock[x]->type != VocBlockTypeSoundData) && (vd->datablock[x]->type != VocBlockTypeSoundDataContinue) ) continue; vd->total_data_len += vd->datablock[x]->data_len; } /* Transfer given fp to the vd structure, the calling * function should not referance it again since we are returning * VocSuccess. */ vd->fp = fp; return(VocSuccess); } /* * Reads a segment of data from the VOC file reffered to * by the filename member on the given wd structure (which * should have been initialized wuth a prior call to * VocReadHeader(). * * File will be opened as needed if the fp member is NULL. * * The member data and data_len will be reallocated as needed * if the requested max_chunk_size is different than data_len. */ int VocReadPartialData( voc_data_struct *vd, off_t offset, off_t max_chunk_size, int read_opt /* Reading format. */ ) { int i, datablock_num; int total_datablocks; voc_block_struct *block_ptr; off_t src_buf_pos; int tar_buf_pos; off_t offset_count; if(vd == NULL) return(VocErrorBadValue); if(offset < 0) return(VocErrorBadValue); /* Read nothing? */ if(max_chunk_size <= 0) { free(vd->data); vd->data = NULL; vd->data_len = 0; return(VocSuccess); } /* Get total_datablocks. */ total_datablocks = vd->total_datablocks; /* Free and allocate data buffer as needed. */ if(vd->data_len != max_chunk_size) { vd->data_len = max_chunk_size; vd->data = (char *)realloc( vd->data, vd->data_len * sizeof(char) ); } if(vd->data == NULL) { vd->data_len = 0; return(VocErrorNoBuffers); } /* Open file as needed. */ if(vd->fp == NULL) { off_t filesize; if(vd->filename == NULL) return(VocErrorBadValue); vd->fp = VocOpenFile(vd->filename, "rb", &filesize); if(vd->fp == NULL) return(VocErrorNoAccess); if(filesize == 0) return(VocErrorNoAccess); } /* Go through each data block. */ for(datablock_num = 0, tar_buf_pos = 0, offset_count = 0; datablock_num < total_datablocks; datablock_num++ ) { if(tar_buf_pos >= max_chunk_size) break; block_ptr = vd->datablock[datablock_num]; if(block_ptr == NULL) continue; /* Skip data blocks not of type audio or audio continue. */ if((block_ptr->type != VocBlockTypeSoundData) && (block_ptr->type != VocBlockTypeSoundDataContinue) ) continue; /* Skip if offset would not put us in this block. */ if((offset_count + block_ptr->data_len) < offset) { offset_count += block_ptr->data_len; continue; } /* Seek beginning of audio data block in file. */ src_buf_pos = (off_t)((int)offset - (int)offset_count); if(src_buf_pos < 0) src_buf_pos = 0; fseek( vd->fp, block_ptr->data_start_pos + src_buf_pos, SEEK_SET ); offset_count += block_ptr->data_len; offset = offset_count; while((tar_buf_pos < max_chunk_size) && (src_buf_pos < block_ptr->data_len) ) { vd->data[tar_buf_pos] = (char)fgetc(vd->fp); tar_buf_pos++; src_buf_pos++; } } /* Sanitize data length. */ if(tar_buf_pos < vd->data_len) vd->data_len = tar_buf_pos; /* Shift the DSP data that was read, voc file format stores the * data as unsigned char. */ switch(read_opt) { case VocReadSigned8: for(i = 0; i < vd->data_len; i++) vd->data[i] = (char)((int)vd->data[i] - (int)128); break; default: break; } return(VocSuccess); }