mirror of
https://github.com/simtactics/niotso.git
synced 2025-03-15 08:11:22 +00:00
Compliant XA decompression code added to the FileHandler interface
This commit is contained in:
parent
1a279588f1
commit
8d06606f28
4 changed files with 390 additions and 0 deletions
|
@ -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
|
155
Libraries/FileHandler/xa/read_xa.c
Normal file
155
Libraries/FileHandler/xa/read_xa.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
read_xa.c - Copyright (c) 2011 Fatbag <X-Fi6@phppoll.org>
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
#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<Channels; i++){
|
||||
unsigned byte = *(InBuffer++);
|
||||
Channel[i].divisor = LONIBBLE(byte)+8;
|
||||
Channel[i].c1 = XATable[HINIBBLE(byte)];
|
||||
Channel[i].c2 = XATable[HINIBBLE(byte)+4];
|
||||
}
|
||||
|
||||
for(i=0; i<14; i++){
|
||||
unsigned j;
|
||||
for(j=0; j<Channels; j++){
|
||||
unsigned byte = *(InBuffer++);
|
||||
int n;
|
||||
for(n=0; n<2; n++){
|
||||
int NewValue = (n == 0) ? HINIBBLE(byte) : LONIBBLE(byte);
|
||||
NewValue = (NewValue << 28) >> 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<Channels; j++){
|
||||
*(OutBuffer++) = (Channel[j].CurSample&0x00FFu)>>(8*0);
|
||||
*(OutBuffer++) = (Channel[j].CurSample&0xFF00u)>>(8*1);
|
||||
}
|
||||
if(!--Frames) break;
|
||||
}
|
||||
}
|
||||
|
||||
free(Channel);
|
||||
return 1;
|
||||
}
|
43
Libraries/FileHandler/xa/read_xa.h
Normal file
43
Libraries/FileHandler/xa/read_xa.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
read_xa.h - Copyright (c) 2011 Fatbag <X-Fi6@phppoll.org>
|
||||
|
||||
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
|
162
Libraries/FileHandler/xa/xadecode.c
Normal file
162
Libraries/FileHandler/xa/xadecode.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
xadecode.c - Copyright (c) 2011 Fatbag <X-Fi6@phppoll.org>
|
||||
|
||||
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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#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 <X-Fi6@phppoll.org>.\n"
|
||||
"xadecode is maintained by the Niotso project.\n"
|
||||
"Home page: <http://www.niotso.org/>");
|
||||
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;
|
||||
}
|
Loading…
Add table
Reference in a new issue