From 8d06606f280b0f81a80449c17c4e506bb15fd2b7 Mon Sep 17 00:00:00 2001 From: Fatbag Date: Wed, 14 Dec 2011 01:10:04 -0600 Subject: [PATCH] Compliant XA decompression code added to the FileHandler interface --- Libraries/FileHandler/xa/Makefile | 30 ++++++ Libraries/FileHandler/xa/read_xa.c | 155 ++++++++++++++++++++++++++ Libraries/FileHandler/xa/read_xa.h | 43 ++++++++ Libraries/FileHandler/xa/xadecode.c | 162 ++++++++++++++++++++++++++++ 4 files changed, 390 insertions(+) create mode 100644 Libraries/FileHandler/xa/read_xa.c create mode 100644 Libraries/FileHandler/xa/read_xa.h create mode 100644 Libraries/FileHandler/xa/xadecode.c diff --git a/Libraries/FileHandler/xa/Makefile b/Libraries/FileHandler/xa/Makefile index e69de29..d2a565f 100644 --- a/Libraries/FileHandler/xa/Makefile +++ b/Libraries/FileHandler/xa/Makefile @@ -0,0 +1,30 @@ +# macros -------------------------------------------------------------------- +CC = gcc +LD = gcc +CFLAGS = -m32 -Wall -Wextra -Wabi -Os -march=i686 -fomit-frame-pointer -ffast-math -funsafe-loop-optimizations -fmerge-all-constants -g0 -fno-exceptions +LDFLAGS = -m32 -s -fwhole-program + +AR = ar rcs +WINDRES = windres -F pe-i386 + +OBJECTS = obj/read_xa.o obj/xadecode.o + +# These will rebuild the entire library upon edit. +DEPS = Makefile \ + read_xa.h + +# dependencies -------------------------------------------------------------- +all: xadecode.exe + +$(OBJECTS): $(DEPS) + +xadecode.exe: $(OBJECTS) + $(CC) $(LDFLAGS) -o $@ $(OBJECTS) + +# make rules ---------------------------------------------------------------- +obj/%.o: %.c + $(CC) -c -ansi -pedantic $(CFLAGS) -o $@ $< + +# maintenance --------------------------------------------------------------- +clean: + del /Q /S obj xadecode.exe \ No newline at end of file diff --git a/Libraries/FileHandler/xa/read_xa.c b/Libraries/FileHandler/xa/read_xa.c new file mode 100644 index 0000000..629a186 --- /dev/null +++ b/Libraries/FileHandler/xa/read_xa.c @@ -0,0 +1,155 @@ +/* + read_xa.c - Copyright (c) 2011 Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include "read_xa.h" + +#define HINIBBLE(byte) ((byte) >> 4) +#define LONIBBLE(byte) ((byte) & 0x0F) + +#ifndef read_int32 + #define read_uint32(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1)) | ((x)[2]<<(8*2)) | ((x)[3]<<(8*3))) + #define read_uint16(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1))) +#endif + +#ifndef __inline +#define __inline +#endif +#ifndef __restrict +#define __restrict +#endif + +unsigned xa_compressed_size(unsigned Frames, unsigned Channels) +{ + /* The required input size is: + ** Channels * (ceil(n/2) + ceil(n/28)) + ** | a | | b | + ** + ** a = The space required for all 2-sample bytes in the XA data for a single channel (Period: 2 frames) + ** b = The space required for all control bytes in the XA data for a single channel (Period: 28 frames) + ** (a+b) is multiplied by Channels to produce the final size + ** + ** This source package assumes a partial block at the end of the XA data is legal but a partial frame is not. + */ + + unsigned SingleChannelData = (((Frames+1)>>1) + (Frames+27)/28); + + if(Frames > 0xFFFFFFFFu-27) return 0; + if(0xFFFFFFFFu/SingleChannelData < Channels) return 0; + + return Channels*SingleChannelData; +} + +int xa_read_header(xaheader_t * XAHeader, const uint8_t * Buffer, unsigned FileSize) +{ + if(FileSize < 24) return 0; + memcpy(&XAHeader->szID, Buffer, 4); + XAHeader->dwOutSize = read_uint32(Buffer+4); + XAHeader->wFormatTag = read_uint16(Buffer+8); + XAHeader->nChannels = read_uint16(Buffer+10); + XAHeader->nSamplesPerSec = read_uint32(Buffer+12); + XAHeader->nAvgBytesPerSec = read_uint32(Buffer+16); + XAHeader->nBlockAlign = read_uint16(Buffer+20); + XAHeader->wBitsPerSample = read_uint16(Buffer+22); + + if(XAHeader->szID[0] != 'X' || XAHeader->szID[1] != 'A' || XAHeader->szID[3] != '\0' || + XAHeader->wFormatTag != 1 || + XAHeader->nChannels == 0 || XAHeader->nChannels > 8 || + XAHeader->nSamplesPerSec < 8000 || XAHeader->nSamplesPerSec > 192000 || + !(XAHeader->nSamplesPerSec%8000==0 || XAHeader->nSamplesPerSec%11025==0) || + XAHeader->wBitsPerSample != 16 || + XAHeader->nBlockAlign != XAHeader->nChannels*(XAHeader->wBitsPerSample>>3) || + XAHeader->nAvgBytesPerSec != XAHeader->nSamplesPerSec*XAHeader->nBlockAlign || + XAHeader->dwOutSize%XAHeader->nBlockAlign != 0 + ) return 0; + + XAHeader->Frames = XAHeader->dwOutSize/XAHeader->nBlockAlign; + XAHeader->XADataSize = xa_compressed_size(XAHeader->Frames, XAHeader->nChannels); + if(FileSize-24 < XAHeader->XADataSize) + return 0; + + return 1; +} + +__inline int16_t Clip16(int sample) +{ + if(sample>=32767) return 32767; + else if(sample<=-32768) return -32768; + else return (int16_t) sample; +} + +typedef struct { + int PrevSample, CurSample; + int divisor; /* residual right-shift value */ + int c1, c2; /* predictor coefficients */ +} channel_t; + +const int XATable[] = +{ + 0, 240, 460, 392, + 0, 0, -208, -220, + 0, 1, 3, 4, + 7, 8, 10, 11, + 0, -1, -3, -4 +}; + +int xa_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, unsigned Frames, unsigned Channels) +{ + channel_t * Channel = malloc(Channels * sizeof(channel_t)); + if(!Channel) return 0; + memset(Channel, 0x00, Channels * sizeof(channel_t)); + + while(Frames){ + unsigned i; + + for(i=0; i> Channel[j].divisor; + NewValue = (NewValue + Channel[j].CurSample*Channel[j].c1 + Channel[j].PrevSample*Channel[j].c2 + 128) >> 8; + Channel[j].PrevSample = Channel[j].CurSample; + Channel[j].CurSample = Clip16(NewValue); + } + *(OutBuffer++) = (Channel[j].PrevSample&0x00FFu)>>(8*0); + *(OutBuffer++) = (Channel[j].PrevSample&0xFF00u)>>(8*1); + } + if(!--Frames) break; + + for(j=0; j>(8*0); + *(OutBuffer++) = (Channel[j].CurSample&0xFF00u)>>(8*1); + } + if(!--Frames) break; + } + } + + free(Channel); + return 1; +} \ No newline at end of file diff --git a/Libraries/FileHandler/xa/read_xa.h b/Libraries/FileHandler/xa/read_xa.h new file mode 100644 index 0000000..bafa2f1 --- /dev/null +++ b/Libraries/FileHandler/xa/read_xa.h @@ -0,0 +1,43 @@ +/* + read_xa.h - Copyright (c) 2011 Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +typedef struct +{ + char szID[4]; + DWORD dwOutSize; + /* WAVEFORMATEX */ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + + unsigned Frames; + unsigned XADataSize; +} xaheader_t; + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned xa_compressed_size(unsigned Frames, unsigned Channels); +int xa_read_header(xaheader_t * XAHeader, const uint8_t * Buffer, unsigned FileSize); +int xa_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, unsigned Frames, unsigned Channels); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Libraries/FileHandler/xa/xadecode.c b/Libraries/FileHandler/xa/xadecode.c new file mode 100644 index 0000000..31ebf10 --- /dev/null +++ b/Libraries/FileHandler/xa/xadecode.c @@ -0,0 +1,162 @@ +/* + xadecode.c - Copyright (c) 2011 Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include "read_xa.h" + +#ifndef write_int32 + #define write_uint32(dest, src) \ + (dest)[0] = ((src)&0x000000FF)>>(8*0); \ + (dest)[1] = ((src)&0x0000FF00)>>(8*1); \ + (dest)[2] = ((src)&0x00FF0000)>>(8*2); \ + (dest)[3] = ((src)&0xFF000000)>>(8*3) + #define write_uint16(dest, src) \ + (dest)[0] = ((src)&0x00FF)>>(8*0); \ + (dest)[1] = ((src)&0xFF00)>>(8*1) +#endif + +int main(int argc, char *argv[]){ + int overwrite = 0; + char *InFile, *OutFile; + HANDLE hFile; + HANDLE ProcessHeap = GetProcessHeap(); + unsigned FileSize; + DWORD bytestransferred = 0; + uint8_t * XAData; + xaheader_t XAHeader; + + if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + printf("Usage: xadecode [-f] infile outfile\n" + " -or- xadecode infile\n" + "Transcode or play a Maxis XA sound.\n" + "Use -f to force overwriting without confirmation.\n" + "\n" + "Report bugs to .\n" + "xadecode is maintained by the Niotso project.\n" + "Home page: "); + return 0; + } + + if(argc >= 4 && !strcmp(argv[1], "-f")){ + overwrite++; + InFile = argv[2]; + OutFile = argv[3]; + }else{ + InFile = argv[1]; + OutFile = argv[2]; + } + + /**** + ** Open the file and read in entire contents to memory + */ + + hFile = CreateFile(InFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if(hFile == INVALID_HANDLE_VALUE){ + if(GetLastError() == ERROR_FILE_NOT_FOUND){ + printf("%sThe specified input file does not exist.", "xadecode: error: "); + return -1; + } + printf("%sThe input file could not be opened for reading.", "xadecode: error: "); + return -1; + } + FileSize = GetFileSize(hFile, NULL); + if(FileSize < 24){ + printf("%sNot a valid XA file.", "xadecode: error: "); + return -1; + } + XAData = HeapAlloc(ProcessHeap, HEAP_NO_SERIALIZE, FileSize); + if(XAData == NULL){ + printf("%sMemory for this file could not be allocated.", "xadecode: error: "); + return -1; + } + if(!ReadFile(hFile, XAData, FileSize, &bytestransferred, NULL) || bytestransferred != FileSize){ + printf("%sThe input file could not be read.", "xadecode: error: "); + return -1; + } + CloseHandle(hFile); + + /**** + ** Transcode the data from XA to LPCM + */ + + if(!xa_read_header(&XAHeader, XAData, FileSize)){ + printf("%sNot a valid XA file.", "xadecode: error: "); + return -1; + } + + if(argc >= 3){ /* Transcode */ + uint8_t * WaveData = HeapAlloc(ProcessHeap, HEAP_NO_SERIALIZE, 44+XAHeader.dwOutSize); + if(WaveData == NULL){ + printf("%sMemory for this file could not be allocated.", "xadecode: error: "); + return -1; + } + + if(!xa_decode(XAData+24, WaveData+44, XAHeader.Frames, XAHeader.nChannels)){ + printf("%sMemory for this file could not be allocated.", "xadecode: error: "); + return -1; + } + + HeapFree(ProcessHeap, HEAP_NO_SERIALIZE, XAData); + + /**** + ** Write the Microsoft WAV header + */ + + write_uint32(WaveData, 0x46464952); /* "RIFF" */ + write_uint32(WaveData+4, 36+XAHeader.dwOutSize); + write_uint32(WaveData+8, 0x45564157); /* "WAVE" */ + write_uint32(WaveData+12, 0x20746d66); /* "fmt " */ + write_uint32(WaveData+16, 16); + write_uint16(WaveData+20, 1); + write_uint16(WaveData+22, XAHeader.nChannels); + write_uint32(WaveData+24, XAHeader.nSamplesPerSec); + write_uint32(WaveData+28, XAHeader.nAvgBytesPerSec); + write_uint16(WaveData+32, XAHeader.nBlockAlign); + write_uint16(WaveData+34, XAHeader.wBitsPerSample); + write_uint32(WaveData+36, 0x61746164); /* "data" */ + write_uint32(WaveData+40, XAHeader.dwOutSize); + + /**** + ** Write the contents to the output file + */ + + hFile = CreateFile(OutFile, GENERIC_WRITE, 0, NULL, CREATE_NEW+overwrite, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if(hFile == INVALID_HANDLE_VALUE){ + if(!overwrite && GetLastError() == ERROR_FILE_EXISTS){ + char c; + printf("File \"%s\" exists.\nContinue anyway? (y/n) ", OutFile); + c = getchar(); + if(c != 'y' && c != 'Y'){ + printf("\nAborted."); + return -1; + } + hFile = CreateFile(OutFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + } + if(hFile == INVALID_HANDLE_VALUE){ + printf("%sThe output file could not be opened for writing.", "xadecode: error: "); + return -1; + } + } + WriteFile(hFile, WaveData, 44+XAHeader.dwOutSize, &bytestransferred, NULL); + CloseHandle(hFile); + } + + return 0; +} \ No newline at end of file