Last active
March 14, 2019 22:12
-
-
Save sparr/0901e4150d62921ce5b051c875da7f69 to your computer and use it in GitHub Desktop.
unxwb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/usr/bin/ld: /tmp/ccG0jyX2.o: in function `xma1_header': | |
unxwb.c:(.text+0xb5c): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xb6a): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xb78): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xb86): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xb94): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xbaa): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xbb7): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xbc4): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xbd1): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xbef): undefined reference to `xma_le16' | |
/usr/bin/ld: /tmp/ccG0jyX2.o: in function `xma2_header': | |
unxwb.c:(.text+0xdaa): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xdb8): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xdc6): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xdd3): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xde0): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xdee): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xdfc): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xe0a): undefined reference to `xma_le16' | |
/usr/bin/ld: unxwb.c:(.text+0xe1f): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xe2c): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xe39): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xe46): undefined reference to `xma_le32' | |
/usr/bin/ld: unxwb.c:(.text+0xe53): undefined reference to `xma_le32' | |
/usr/bin/ld: /tmp/ccG0jyX2.o:unxwb.c:(.text+0xe60): more undefined references to `xma_le32' follow | |
/usr/bin/ld: /tmp/ccG0jyX2.o: in function `xma2_header': | |
unxwb.c:(.text+0xe82): undefined reference to `xma_le16' | |
/usr/bin/ld: /tmp/ccG0jyX2.o: in function `xma2xact_header': | |
unxwb.c:(.text+0x104d): undefined reference to `xma_be32' | |
/usr/bin/ld: unxwb.c:(.text+0x105a): undefined reference to `xma_be32' | |
/usr/bin/ld: unxwb.c:(.text+0x1067): undefined reference to `xma_be32' | |
/usr/bin/ld: unxwb.c:(.text+0x1074): undefined reference to `xma_be32' | |
/usr/bin/ld: unxwb.c:(.text+0x1081): undefined reference to `xma_be32' | |
/usr/bin/ld: /tmp/ccG0jyX2.o:unxwb.c:(.text+0x108e): more undefined references to `xma_be32' follow | |
/usr/bin/ld: /tmp/ccG0jyX2.o: in function `xma2xact_header': | |
unxwb.c:(.text+0x10d3): undefined reference to `xma_be16' | |
/usr/bin/ld: /tmp/ccG0jyX2.o: in function `unzip': | |
unxwb.c:(.text+0x3254): undefined reference to `inflateInit_' | |
/usr/bin/ld: unxwb.c:(.text+0x3324): undefined reference to `inflate' | |
/usr/bin/ld: unxwb.c:(.text+0x33f9): undefined reference to `inflateEnd' | |
collect2: error: ld returned 1 exit status |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
MyWAV 0.1.2 | |
by Luigi Auriemma | |
e-mail: [email protected] | |
web: aluigi.org | |
Copyright 2005,2006 Luigi Auriemma | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; either version 2 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
http://www.gnu.org/licenses/gpl.txt | |
*/ | |
#include <string.h> | |
#include <stdint.h> | |
/* | |
the functions return ever 0 if success, other values (-1) if error | |
note that these functions have been written with compatibility in mind | |
so don't worry if you see useless instructions | |
*/ | |
typedef struct { | |
uint8_t id[4]; | |
uint32_t size; | |
} mywav_chunk; | |
typedef struct { | |
int16_t wFormatTag; | |
union { uint16_t wChannels; uint16_t nChannels; }; | |
union { uint32_t dwSamplesPerSec; uint32_t nSamplesPerSec; }; | |
union { uint32_t dwAvgBytesPerSec; uint32_t nAvgBytesPerSec; }; | |
union { uint16_t wBlockAlign; uint16_t nBlockAlign; }; | |
uint16_t wBitsPerSample; | |
} mywav_fmtchunk; | |
/* FILE WRITING */ | |
// 8 bit | |
int mywav_fwi08(FILE *fd, int num) { | |
if(fputc((num ) & 0xff, fd) < 0) return(-1); | |
return(0); | |
} | |
// 16 bit | |
int mywav_fwi16(FILE *fd, int num) { | |
if(fputc((num ) & 0xff, fd) < 0) return(-1); | |
if(fputc((num >> 8) & 0xff, fd) < 0) return(-1); | |
return(0); | |
} | |
// 32 bit | |
int mywav_fwi32(FILE *fd, int num) { | |
if(fputc((num ) & 0xff, fd) < 0) return(-1); | |
if(fputc((num >> 8) & 0xff, fd) < 0) return(-1); | |
if(fputc((num >> 16) & 0xff, fd) < 0) return(-1); | |
if(fputc((num >> 24) & 0xff, fd) < 0) return(-1); | |
return(0); | |
} | |
// data | |
int mywav_fwmem(FILE *fd, uint8_t *mem, int size) { | |
if(size) { | |
if(fwrite(mem, size, 1, fd) != 1) return(-1); | |
} | |
return(0); | |
} | |
// chunk | |
int mywav_fwchunk(FILE *fd, mywav_chunk *chunk) { | |
if(mywav_fwmem(fd, chunk->id, 4)) return(-1); | |
if(mywav_fwi32(fd, chunk->size)) return(-1); | |
return(0); | |
} | |
// fmtchunk | |
int mywav_fwfmtchunk(FILE *fd, mywav_fmtchunk *fmtchunk) { | |
if(mywav_fwi16(fd, fmtchunk->wFormatTag)) return(-1); | |
if(mywav_fwi16(fd, fmtchunk->wChannels)) return(-1); | |
if(mywav_fwi32(fd, fmtchunk->dwSamplesPerSec)) return(-1); | |
if(mywav_fwi32(fd, fmtchunk->dwAvgBytesPerSec)) return(-1); | |
if(mywav_fwi16(fd, fmtchunk->wBlockAlign)) return(-1); | |
if(mywav_fwi16(fd, fmtchunk->wBitsPerSample)) return(-1); | |
return(0); | |
} | |
/* FILE READING */ | |
// 8 bit | |
int mywav_fri08(FILE *fd, uint8_t *num) { | |
if(fread(num, 1, 1, fd) != 1) return(-1); | |
return(0); | |
} | |
// 16 bit | |
int mywav_fri16(FILE *fd, uint16_t *num) { | |
uint16_t ret; | |
uint8_t tmp; | |
if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret = tmp; | |
if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 8); | |
*num = ret; | |
return(0); | |
} | |
// 32 bit | |
int mywav_fri32(FILE *fd, uint32_t *num) { | |
uint32_t ret; | |
uint8_t tmp; | |
if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret = tmp; | |
if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 8); | |
if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 16); | |
if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 24); | |
*num = ret; | |
return(0); | |
} | |
// data | |
int mywav_frmem(FILE *fd, uint8_t *mem, int size) { | |
if(size) { | |
if(fread(mem, size, 1, fd) != 1) return(-1); | |
} | |
return(0); | |
} | |
// chunk | |
int mywav_frchunk(FILE *fd, mywav_chunk *chunk) { | |
if(mywav_frmem(fd, (void *)&chunk->id, 4)) return(-1); | |
if(mywav_fri32(fd, (void *)&chunk->size)) return(-1); | |
return(0); | |
} | |
// fmtchunk | |
int mywav_frfmtchunk(FILE *fd, mywav_fmtchunk *fmtchunk) { | |
if(mywav_fri16(fd, (void *)&fmtchunk->wFormatTag)) return(-1); | |
if(mywav_fri16(fd, (void *)&fmtchunk->wChannels)) return(-1); | |
if(mywav_fri32(fd, (void *)&fmtchunk->dwSamplesPerSec)) return(-1); | |
if(mywav_fri32(fd, (void *)&fmtchunk->dwAvgBytesPerSec)) return(-1); | |
if(mywav_fri16(fd, (void *)&fmtchunk->wBlockAlign)) return(-1); | |
if(mywav_fri16(fd, (void *)&fmtchunk->wBitsPerSample)) return(-1); | |
return(0); | |
} | |
/* MYWAV MAIN FUNCTIONS */ | |
int mywav_seekchunk(FILE *fd, uint8_t *find) { | |
mywav_chunk chunk; | |
if(fseek(fd, sizeof(mywav_chunk) + 4, SEEK_SET) < 0) return(-1); | |
while(!mywav_frchunk(fd, &chunk)) { | |
if(!memcmp(chunk.id, find, 4)) return(chunk.size); | |
if(fseek(fd, chunk.size, SEEK_CUR) < 0) break; | |
} | |
return(-1); | |
} | |
int mywav_data(FILE *fd, mywav_fmtchunk *fmt) { | |
mywav_chunk chunk; | |
uint8_t type[4]; | |
if(mywav_frchunk(fd, &chunk) < 0) return(-1); | |
if(mywav_frmem(fd, type, 4) < 0) return(-1); | |
if(memcmp(type, "WAVE", 4)) return(-1); | |
if(mywav_seekchunk(fd, "fmt ") < 0) return(-1); | |
if(mywav_frfmtchunk(fd, fmt) < 0) return(-1); | |
return(mywav_seekchunk(fd, "data")); | |
} | |
int mywav_writehead(FILE *fd, mywav_fmtchunk *fmt, uint32_t data_size, uint8_t *morefmt, int morefmt_size, uint8_t *extra, int extra_size) { | |
mywav_chunk chunk; | |
memcpy(chunk.id, "RIFF", 4); | |
chunk.size = | |
4 + | |
sizeof(mywav_chunk) + | |
sizeof(mywav_fmtchunk) + | |
morefmt_size + | |
extra_size + | |
sizeof(mywav_chunk) + | |
data_size; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(mywav_fwmem(fd, "WAVE", 4) < 0) return(-1); | |
memcpy(chunk.id, "fmt ", 4); | |
chunk.size = sizeof(mywav_fmtchunk) + morefmt_size; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(mywav_fwfmtchunk(fd, fmt) < 0) return(-1); | |
if(mywav_fwmem(fd, morefmt, morefmt_size) < 0) return(-1); | |
if(mywav_fwmem(fd, extra, extra_size) < 0) return(-1); | |
memcpy(chunk.id, "data", 4); | |
chunk.size = data_size; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
return(0); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// from xact3wb.h | |
#define ADPCM_MINIWAVEFORMAT_BLOCKALIGN_CONVERSION_OFFSET 22 | |
#define WAVEBANK_HEADER_SIGNATURE 'DNBW' // WaveBank RIFF chunk signature | |
#define WAVEBANK_HEADER_VERSION 43 // Current wavebank file version | |
#define WAVEBANK_BANKNAME_LENGTH 64 // Wave bank friendly name length, in characters | |
#define WAVEBANK_ENTRYNAME_LENGTH 64 // Wave bank entry friendly name length, in characters | |
#define WAVEBANK_MAX_DATA_SEGMENT_SIZE 0xFFFFFFFF // Maximum wave bank data segment size, in bytes | |
#define WAVEBANK_MAX_COMPACT_DATA_SEGMENT_SIZE 0x001FFFFF // Maximum compact wave bank data segment size, in bytes | |
typedef uint32_t WAVEBANKOFFSET; | |
// | |
// Bank flags | |
// | |
#define WAVEBANK_TYPE_BUFFER 0x00000000 // In-memory buffer | |
#define WAVEBANK_TYPE_STREAMING 0x00000001 // Streaming | |
#define WAVEBANK_TYPE_MASK 0x00000001 | |
#define WAVEBANK_FLAGS_ENTRYNAMES 0x00010000 // Bank includes entry names | |
#define WAVEBANK_FLAGS_COMPACT 0x00020000 // Bank uses compact format | |
#define WAVEBANK_FLAGS_SYNC_DISABLED 0x00040000 // Bank is disabled for audition sync | |
#define WAVEBANK_FLAGS_SEEKTABLES 0x00080000 // Bank includes seek tables. | |
#define WAVEBANK_FLAGS_MASK 0x000F0000 | |
// | |
// Entry flags | |
// | |
#define WAVEBANKENTRY_FLAGS_READAHEAD 0x00000001 // Enable stream read-ahead | |
#define WAVEBANKENTRY_FLAGS_LOOPCACHE 0x00000002 // One or more looping sounds use this wave | |
#define WAVEBANKENTRY_FLAGS_REMOVELOOPTAIL 0x00000004 // Remove data after the end of the loop region | |
#define WAVEBANKENTRY_FLAGS_IGNORELOOP 0x00000008 // Used internally when the loop region can't be used | |
#define WAVEBANKENTRY_FLAGS_MASK 0x00000008 | |
// | |
// Entry wave format identifiers | |
// | |
#define WAVEBANKMINIFORMAT_TAG_PCM 0x0 // PCM data | |
#define WAVEBANKMINIFORMAT_TAG_XMA 0x1 // XMA data | |
#define WAVEBANKMINIFORMAT_TAG_ADPCM 0x2 // ADPCM data | |
#define WAVEBANKMINIFORMAT_TAG_WMA 0x3 // WMA data | |
#define WAVEBANKMINIFORMAT_BITDEPTH_8 0x0 // 8-bit data (PCM only) | |
#define WAVEBANKMINIFORMAT_BITDEPTH_16 0x1 // 16-bit data (PCM only) | |
// | |
// Arbitrary fixed sizes | |
// | |
#define WAVEBANKENTRY_XMASTREAMS_MAX 3 // enough for 5.1 channel audio | |
#define WAVEBANKENTRY_XMACHANNELS_MAX 6 // enough for 5.1 channel audio (cf. XAUDIOCHANNEL_SOURCEMAX) | |
// | |
// DVD data sizes | |
// | |
#define WAVEBANK_DVD_SECTOR_SIZE 2048 | |
#define WAVEBANK_DVD_BLOCK_SIZE (WAVEBANK_DVD_SECTOR_SIZE * 16) | |
// | |
// Bank alignment presets | |
// | |
#define WAVEBANK_ALIGNMENT_MIN 4 // Minimum alignment | |
#define WAVEBANK_ALIGNMENT_DVD WAVEBANK_DVD_SECTOR_SIZE // DVD-optimized alignment | |
// | |
// Wave bank segment identifiers | |
// | |
typedef enum WAVEBANKSEGIDX { | |
WAVEBANK_SEGIDX_BANKDATA = 0, // Bank data | |
WAVEBANK_SEGIDX_ENTRYMETADATA, // Entry meta-data | |
WAVEBANK_SEGIDX_SEEKTABLES, // Storage for seek tables for the encoded waves. | |
WAVEBANK_SEGIDX_ENTRYNAMES, // Entry friendly names | |
WAVEBANK_SEGIDX_ENTRYWAVEDATA, // Entry wave data | |
WAVEBANK_SEGIDX_COUNT | |
} WAVEBANKSEGIDX, *LPWAVEBANKSEGIDX; | |
#pragma pack(1) | |
typedef struct { | |
uint32_t dwOffset; // Region offset, in bytes | |
uint32_t dwLength; // Region length, in bytes | |
} WAVEBANKREGION; | |
typedef struct { | |
char dwSignature[4]; // (uint32_t -> char[4]) File signature | |
uint32_t dwVersion; // Version of the tool that created the file | |
WAVEBANKREGION Segments[WAVEBANK_SEGIDX_COUNT]; // Segment lookup table | |
} WAVEBANKHEADER; | |
typedef struct { | |
uint32_t dwStartSample; // Start sample for the region. | |
uint32_t dwTotalSamples; // Region length in samples. | |
} WAVEBANKSAMPLEREGION; | |
typedef struct { | |
uint32_t dwFlagsAndDuration; // dwFlags:4 and Duration:28 | |
uint32_t Format; // Entry format | |
WAVEBANKREGION PlayRegion; // Region within the wave data segment that contains this entry | |
union { | |
WAVEBANKREGION LoopRegion; // Region within the wave data that should loop | |
// XMA loop region | |
// Note: this is not the same memory layout as the XMA loop region | |
// passed to the XMA driver--it is more compact. The named values | |
// map correctly and there are enough bits to store the entire | |
// range of values that XMA considers valid, with one exception: | |
// valid values for nSubframeSkip are 1, 2, 3, or 4. In order to | |
// store this in two bits, XACT subtracts 1 from the value, then adds | |
struct | |
{ | |
uint32_t dwStartOffset; // loop start offset (in bits) | |
uint32_t nSubframeSkip_nSubframeEnd_dwEndOffset; | |
//uint32_t nSubframeSkip : 2; // needed by XMA decoder. Valid values for XMA are 1-4; XACT converts to 0-3 for storage. Add 1 to this value before passing to driver. | |
//uint32_t nSubframeEnd : 2; // needed by XMA decoder | |
//uint32_t dwEndOffset : 28; // loop end offset (in bits) | |
} XMALoopRegion[ WAVEBANKENTRY_XMASTREAMS_MAX ]; | |
// The last element in the union is an array that aliases the | |
// entire union so we can byte-reverse the whole thing. | |
WAVEBANKREGION LoopRegionAlias[ WAVEBANKENTRY_XMASTREAMS_MAX ]; | |
}; | |
} WAVEBANKENTRY; | |
typedef struct { | |
uint32_t dwFlags; // Bank flags | |
uint32_t dwEntryCount; // Number of entries in the bank | |
char szBankName[WAVEBANK_BANKNAME_LENGTH]; // Bank friendly name | |
uint32_t dwEntryMetaDataElementSize; // Size of each entry meta-data element, in bytes | |
uint32_t dwEntryNameElementSize; // Size of each entry name element, in bytes | |
uint32_t dwAlignment; // Entry alignment, in bytes | |
uint32_t CompactFormat; // Format data for compact bank | |
uint32_t BuildTime; // Build timestamp | |
} WAVEBANKDATA; | |
#pragma pack() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Show_dump 0.1.1a | |
Copyright 2004,2005,2006 Luigi Auriemma | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; either version 2 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
http://www.gnu.org/licenses/gpl.txt | |
This function, optimized for performace, shows the hex dump of a buffer and | |
places it in a stream | |
Usage: | |
show_dump(buffer, buffer_length, stdout); | |
show_dump(buffer, buffer_length, fd); | |
*/ | |
#include <string.h> | |
void show_dump(unsigned char *data, unsigned int len, FILE *stream) { | |
const static char hex[] = "0123456789abcdef"; | |
static unsigned char buff[67]; /* HEX CHAR\n */ | |
unsigned char chr, | |
*bytes, | |
*p, | |
*limit, | |
*glimit = data + len; | |
memset(buff + 2, ' ', 48); | |
while(data < glimit) { | |
limit = data + 16; | |
if(limit > glimit) { | |
limit = glimit; | |
memset(buff, ' ', 48); | |
} | |
p = buff; | |
bytes = p + 50; | |
while(data < limit) { | |
chr = *data; | |
*p++ = hex[chr >> 4]; | |
*p++ = hex[chr & 15]; | |
p++; | |
*bytes++ = ((chr < ' ') || (chr >= 0x7f)) ? '.' : chr; | |
data++; | |
} | |
*bytes++ = '\n'; | |
fwrite(buff, bytes - buff, 1, stream); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright 2005-2016 Luigi Auriemma | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; either version 2 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
http://www.gnu.org/licenses/gpl-2.0.txt | |
*/ | |
// people who supplied xwb files to analyze: john deo, antti | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <zlib.h> | |
#include <stdint.h> | |
#include "show_dump.h" | |
#include "myxact.h" | |
#include "mywav.h" | |
#include "xma_header.h" | |
/* | |
many informations about XWB files are available in xact2wb.h and xact3wb.h from the DirectX SDK | |
*/ | |
#ifdef WIN32 | |
#include <direct.h> | |
HWND mywnd = NULL; | |
char *get_file(char *title, int xwb, int multi); | |
char *get_folder(char *title); | |
#else | |
#include <unistd.h> | |
#define stricmp strcasecmp | |
#define strnicmp strncasecmp | |
#endif | |
typedef uint8_t u8; | |
typedef uint16_t u16; | |
typedef uint32_t u32; | |
#define VER "0.3.6" | |
#define PATHSZ 1024 // 257 was enough, theoretically the system could support 32kb but it's false | |
/* SIGNATURES */ | |
#define XWBSIGNi "WBND" // intel endian | |
#define XWBSIGNb "DNBW" // network endian | |
#define WBASIGNi "HVSI" "WBA\0" // intel endian | |
#define WBASIGNb "ISVH" "\0ABW" // network endian | |
#define XSBSIGNi "SDBK" // intel endian | |
#define XSBSIGNb "KBDS" // network endian | |
/* WAVEBANKMINIWAVEFORMAT */ | |
//#define XWBwFormatTag (wavebankentry.Format & ((1 << 2) - 1)) | |
//#define XWBnChannels ((wavebankentry.Format >> (2)) & ((1 << 3) - 1)) | |
//#define XWBnSamplesPerSec ((wavebankentry.Format >> (2 + 3)) & ((1 << 18) - 1)) | |
//#define XWBwBlockAlign ((wavebankentry.Format >> (2 + 3 + 18)) & ((1 << 8) - 1)) | |
//#define XWBwBitsPerSample ((wavebankentry.Format >> (2 + 3 + 18 + 8)) & ((1 << 1) - 1)) | |
/* FILE OPERATIONS AND OTHER DEFINES */ | |
#define MYFSEEK(x) if(fseek(fd, file_offset + x, SEEK_SET)) std_err(); | |
#define MAXFILENAME 128 // 260 | |
#define MAXFILENAMEEXT (MAXFILENAME + 4) | |
#define EXECIN "#FILE" | |
#define EXECINSZ (sizeof(EXECIN) - 1) | |
#define EXECOUTSZ MAXFILENAMEEXT | |
#define SHOWFILEOFF //if(verbose) fprintf(fdinfo, " current offset 0x%08x\n", (int)ftell(fd)); | |
#define read_file(a,b,c) if(fread((void *)b, 1, c, a) != (c)) read_err(); | |
u8 *mystrrchrs(u8 *str, u8 *chrs); | |
int xsb_names(FILE *fd, char *name, int track); | |
void exec_arg(char *data); | |
void exec_run(u8 *fname); | |
void hexdump(FILE *fd, u32 off, u32 len); | |
u32 unzip(FILE *fd, FILE *fdo); | |
void getxwbfile(FILE *fdin, char *fname, u32 size, int codec, int rate, int chans, int expbits, int align); | |
int xwb_scan_sign(FILE *fd); | |
int overwrite_file(char *fname); | |
int get_num(char *data); | |
void read_err(void); | |
void write_err(void); | |
u16 (*fr16)(FILE *fd); | |
u16 fri16(FILE *fd); | |
u16 frb16(FILE *fd); | |
u32 (*fr32)(FILE *fd); | |
u32 fri32(FILE *fd); | |
u32 frb32(FILE *fd); | |
int putxx(u8 *data, u32 num, int bits); | |
void std_err(void); | |
void myexit(int ret); | |
FILE *fdinfo; | |
u32 file_offset = 0; | |
int execlen = 0, | |
verbose = 0, | |
xsboff = -1, | |
hex_names = 1; | |
u8 *tmpexec = NULL, | |
*execstring = NULL; | |
int main(int argc, char *argv[]) { | |
WAVEBANKHEADER wavebankheader; | |
WAVEBANKENTRY wavebankentry; | |
WAVEBANKDATA wavebankdata; | |
FILE *fd, | |
*fdz, | |
*fdxsb = NULL; | |
int i, | |
num, | |
current_entry, | |
unpacklen, | |
wavebank_offset, | |
waveentry_offset, | |
playregion_offset, | |
last_segment, | |
compact_format = 0, | |
len, | |
codec, | |
align, | |
rate, | |
chans, | |
bits, | |
hexseg = -1, | |
dostdout = 0, | |
list = 0, | |
rawfiles = 0, | |
segidx_entry_name = 2; | |
u8 fname[MAXFILENAMEEXT + 1], | |
flags_text[80], | |
wbasign[8], | |
*outdir = NULL, | |
*entry_name = NULL, | |
*xwbname, | |
*codecstr, | |
*xsbname = NULL, | |
*ext, | |
*p; | |
setbuf(stdin, NULL); | |
setbuf(stdout, NULL); | |
setbuf(stderr, NULL); | |
fputs("\n" | |
"XWB/ZWB files unpacker "VER"\n" | |
"by Luigi Auriemma\n" | |
"e-mail: [email protected]\n" | |
"web: aluigi.org\n" | |
"\n", stderr); | |
#ifdef WIN32 | |
mywnd = GetForegroundWindow(); | |
if(GetWindowLong(mywnd, GWL_WNDPROC)) { | |
num = 0; | |
for(i = 1; i < argc; i++) { | |
if(((argv[i][0] != '-') && (argv[i][0] != '/')) || (strlen(argv[i]) != 2)) { | |
break; | |
} | |
switch(argv[i][1]) { | |
case 'd': i++; num = 1; break; // output folder already selected | |
case 'b': i += 2; break; | |
case 'x': i++; break; | |
case 'r': i++; break; | |
case 's': i++; break; | |
default: break; | |
} | |
} | |
if(i > argc) i = argc; | |
int rem_args = num ? 1 : 3; | |
i = rem_args - (argc - i); | |
if(i > 0) { | |
printf( | |
"- GUI mode activated, remember that the tool works also from command-line\n" | |
" where are available various options\n" | |
"\n"); | |
p = calloc(argc + i + 3, sizeof(char *)); // was + 1 but this code is a bit chaotic so stay safe | |
if(!p) std_err(); | |
memcpy(p, argv, sizeof(char *) * argc); | |
argv = (void *)p; | |
argc -= (rem_args - i); | |
char **lame = NULL; // long story | |
if(num) { | |
if(i >= 1) argv[argc++] = get_file("select the input XWB archive to extract", 1, 0); | |
} else { | |
if(i < 3) { // lame backup | |
if(i >= 1) argv[argc + 1] = argv[argc]; | |
if(i >= 2) argv[argc + 2] = argv[argc]; | |
} | |
if(i >= 1) argv[argc++] = "-d"; | |
if(i >= 2) lame = &argv[argc++]; | |
if(i >= 3) argv[argc] = get_file("select the input XWB archive to extract", 1, 0); | |
argc++; | |
*lame = get_folder("select the output folder where extracting the files"); | |
} | |
} | |
} | |
#endif | |
if(argc < 2) { | |
fprintf(stderr, | |
"\n" | |
"Usage: %s [options] <file.XWB>\n" | |
"\n" | |
"Options:\n" | |
"-l lists the files without extracting them\n" | |
"-d DIR output directory where extracting the files\n" | |
"-v verbose output\n" | |
"-b N OFF N is the name of the XSB containing the names of the XWB audio\n" | |
" files and OFF is the offset where these names start\n" | |
"-x OFF offset of the input file for reading the XWB data in it\n" | |
"-r \"EXE\" runs a tool for each output file. EXE is the complete command-line,\n" | |
" use the #FILE pattern which will be substituited with the output file\n" | |
" Example for fast Xbox ADPCM decoding:\n" | |
" unxwb -r \"xbadpdec #FILE #FILE.wav\" music.xwb\n" | |
"-o don't create files, simply dumps them to stdout (probably useless)\n" | |
"-s SEG show the hex content of the segment number SEG (for debugging only)\n" | |
"-D output files in decimal notation (default is hex, 00000123.dat)\n" | |
"%s\n" | |
"\n", argv[0], | |
rawfiles ? // useful in case I want to change the default mode | |
"-a add header to the output files (by default the tool extracts them in\n" | |
" raw mode exactly as they are stored in the XWB archive" : | |
"-R raw output files (by default the tool adds headers and extensions)"); | |
myexit(1); | |
} | |
fdinfo = stdout; | |
argc--; | |
for(i = 1; i < argc; i++) { | |
switch(argv[i][1]) { | |
case 'l': list = 1; break; | |
case 'd': outdir = argv[++i]; break; | |
case 'v': verbose = 1; break; | |
case 'b': { | |
xsbname = argv[++i]; | |
xsboff = get_num(argv[++i]); | |
break; | |
} | |
case 'x': file_offset = get_num(argv[++i]); break; | |
case 'r': exec_arg(argv[++i]); break; | |
case 'R': rawfiles = 1; break; | |
case 'a': rawfiles = 0; break; // like in fsbext | |
case 'o': { | |
dostdout = 1; | |
fdinfo = stderr; | |
break; | |
} | |
case 's': hexseg = get_num(argv[++i]); break; | |
case 'D': hex_names = 0; break; | |
default: { | |
fprintf(stderr, "\nError: wrong command-line argument (%s)\n\n", argv[i]); | |
myexit(1); | |
} | |
} | |
} | |
xwbname = argv[argc]; | |
if(!strcmp(xwbname, "-")) { | |
fprintf(fdinfo, "- open file %s\n", "stdin"); | |
fd = stdin; | |
} else { | |
fprintf(fdinfo, "- open file %s\n", xwbname); | |
fd = fopen(xwbname, "rb"); | |
if(!fd) std_err(); | |
} | |
/* COMPATIBILITY INITIALIZATION */ | |
fr16 = fri16; | |
fr32 = fri32; | |
memset(&wavebankheader, 0, sizeof(wavebankheader)); | |
memset(&wavebankentry, 0, sizeof(wavebankentry)); | |
memset(&wavebankdata, 0, sizeof(wavebankdata)); | |
ext = strrchr(xwbname, '.'); | |
if(ext) ext++; | |
/* ZWB */ | |
if(ext && !stricmp(ext, "zwb")) { | |
*ext = 'x'; // zwb -> xwb | |
fprintf(fdinfo, "- create XWB file %s\n", xwbname); | |
if(!overwrite_file(xwbname)) { | |
fprintf(fdinfo, "- the file will not be overwritten, I exit\n"); | |
fclose(fd); | |
myexit(1); | |
} | |
fdz = fopen(xwbname, "w+b"); | |
if(!fdz) std_err(); | |
fprintf(fdinfo, "- unzip file: ...wait..."); | |
MYFSEEK(4); // version ??? | |
unpacklen = fr32(fd); | |
len = unzip(fd, fdz); | |
fflush(fdz); | |
fprintf(fdinfo, "\n- %u bytes unpacked\n", len); | |
if(len != unpacklen) { | |
fprintf(fdinfo, | |
" Alert: the unzipped file size is different than that specified in the ZWB file\n" | |
" (%u)\n", unpacklen); | |
} | |
fclose(fd); | |
fd = fdz; | |
rewind(fd); | |
file_offset = 0; | |
} | |
if(rawfiles) { | |
fprintf(fdinfo, "- the files will be extracted in raw mode\n"); | |
} else { | |
fprintf(fdinfo, "- the tool will try to add a header to the extracted files\n"); | |
} | |
/* CHECK FOR SXB FILES */ | |
if(ext && (!stricmp(ext, "sxb") || !stricmp(ext, "vxb"))) { | |
if(fr32(fd) >> 24) { // lame endian checker | |
fr16 = frb16; | |
fr32 = frb32; | |
} | |
file_offset += fr32(fd); | |
} | |
MYFSEEK(0); | |
/* CHECK FOR WBA SIGNATURE */ | |
read_file(fd, wbasign, sizeof(wbasign)); | |
if(!memcmp(wbasign, WBASIGNi, sizeof(WBASIGNi) - 1) || | |
!memcmp(wbasign, WBASIGNb, sizeof(WBASIGNb) - 1)) { | |
file_offset += 4096; | |
} | |
MYFSEEK(0); | |
/* XSB */ | |
/* no longer automatic | |
if(!xsbname && ext) { | |
strcpy(ext, "xsb"); | |
xsbname = xwbname; | |
} | |
*/ | |
if(xsbname) { | |
fprintf(fdinfo, "- open XSB file %s\n", xsbname); | |
fdxsb = fopen(xsbname, "rb"); | |
if(!fdxsb) fprintf(fdinfo, "- XSB file not found\n"); | |
} | |
xsb_names(fdxsb, fname, -1); | |
/* OUTPUT FOLDER */ | |
if(outdir) { | |
fprintf(fdinfo, "- change directory %s\n", outdir); | |
if(chdir(outdir) < 0) std_err(); | |
} | |
/* SIGNATURE */ | |
check_signature: | |
read_file(fd, wavebankheader.dwSignature, 4); | |
if(verbose) fprintf(fdinfo, "- signature %.4s\n", wavebankheader.dwSignature); | |
if(!memcmp(wavebankheader.dwSignature, XWBSIGNi, sizeof(XWBSIGNi) - 1)) { | |
if(verbose) fprintf(fdinfo, "- little/intel endian values\n"); | |
fr16 = fri16; | |
fr32 = fri32; | |
} else if(!memcmp(wavebankheader.dwSignature, XWBSIGNb, sizeof(XWBSIGNb) - 1)) { | |
if(verbose) fprintf(fdinfo, "- big/network endian values\n"); | |
fr16 = frb16; | |
fr32 = frb32; | |
} else { | |
fprintf(fdinfo, " alert: the sign is invalid, now I scan the file for the needed signature\n"); | |
fseek(fd, -4, SEEK_CUR); | |
len = xwb_scan_sign(fd); | |
if(len < 0) { | |
fprintf(stderr, "\nError: no signature found after scanning, this file is not a XWB file\n\n"); | |
fclose(fd); | |
myexit(1); | |
} | |
file_offset += len; | |
fprintf(fdinfo, "- found possible signature at offset 0x%08x\n", file_offset); | |
MYFSEEK(0); | |
goto check_signature; | |
} | |
/* VERSION */ | |
wavebankheader.dwVersion = fr32(fd); | |
if(verbose) fprintf(fdinfo, "- version %u\n", wavebankheader.dwVersion); | |
/* SEGMENTS */ | |
last_segment = 4; | |
if(wavebankheader.dwVersion == 1) goto WAVEBANKDATA_goto; | |
if(wavebankheader.dwVersion <= 3) last_segment = 3; | |
if(wavebankheader.dwVersion >= 42) fr32(fd); // skip dwHeaderVersion | |
SHOWFILEOFF; | |
for(i = 0; i <= last_segment; i++) { // WAVEBANKREGION | |
wavebankheader.Segments[i].dwOffset = fr32(fd); | |
wavebankheader.Segments[i].dwLength = fr32(fd); | |
if(verbose) { | |
fprintf(fdinfo, "- segment %u offset 0x%08x length %u\n", | |
i, wavebankheader.Segments[i].dwOffset, wavebankheader.Segments[i].dwLength); | |
} | |
} | |
/* WAVEBANKDATA */ | |
MYFSEEK(wavebankheader.Segments[WAVEBANK_SEGIDX_BANKDATA].dwOffset); | |
WAVEBANKDATA_goto: | |
SHOWFILEOFF; | |
wavebankdata.dwFlags = fr32(fd); | |
wavebankdata.dwEntryCount = fr32(fd); | |
if((wavebankheader.dwVersion == 2) || (wavebankheader.dwVersion == 3)) { | |
read_file(fd, wavebankdata.szBankName, 16); // version 1 and 2 want 16 bytes | |
} else { | |
read_file(fd, wavebankdata.szBankName, sizeof(wavebankdata.szBankName)); | |
} | |
if(wavebankheader.dwVersion == 1) { | |
wavebank_offset = (int)ftell(fd) - file_offset; | |
wavebankdata.dwEntryMetaDataElementSize = 20; | |
} else { | |
wavebankdata.dwEntryMetaDataElementSize = fr32(fd); | |
wavebankdata.dwEntryNameElementSize = fr32(fd); | |
wavebankdata.dwAlignment = fr32(fd); | |
wavebank_offset = wavebankheader.Segments[WAVEBANK_SEGIDX_ENTRYMETADATA].dwOffset; | |
} | |
if(wavebankdata.dwFlags & WAVEBANK_FLAGS_COMPACT) { | |
compact_format = fr32(fd); | |
} | |
flags_text[0] = 0; | |
if(wavebankdata.dwFlags & WAVEBANK_TYPE_BUFFER) strcat(flags_text, "in-memory, "); | |
if(wavebankdata.dwFlags & WAVEBANK_TYPE_STREAMING) strcat(flags_text, "streaming, "); | |
if(wavebankdata.dwFlags & WAVEBANK_FLAGS_ENTRYNAMES) strcat(flags_text, "bank+entry_names, "); | |
if(wavebankdata.dwFlags & WAVEBANK_FLAGS_COMPACT) strcat(flags_text, "compact_format, "); | |
if(wavebankdata.dwFlags & WAVEBANK_FLAGS_SYNC_DISABLED) strcat(flags_text, "disabled_bank, "); | |
if(flags_text[0]) flags_text[strlen(flags_text) - 2] = 0; // remove commas | |
if(verbose) { | |
fprintf(fdinfo, "\n" | |
"- flags %s\n" | |
"- files %u\n" | |
"- bank name %.*s\n" | |
"- entry meta size %u\n" | |
"- entry name size %u\n" | |
"- alignment %u\n", | |
flags_text, | |
wavebankdata.dwEntryCount, | |
sizeof(wavebankdata.szBankName), wavebankdata.szBankName, | |
wavebankdata.dwEntryMetaDataElementSize, | |
wavebankdata.dwEntryNameElementSize, | |
wavebankdata.dwAlignment); | |
} | |
/* COMPATIBILITY WORK-AROUNDS, DEBUGGING and ALLOCATION */ | |
playregion_offset = wavebankheader.Segments[last_segment].dwOffset; | |
if(!playregion_offset) { | |
playregion_offset = | |
wavebank_offset + | |
(wavebankdata.dwEntryCount * wavebankdata.dwEntryMetaDataElementSize); | |
} | |
if(verbose && (wavebankdata.dwEntryMetaDataElementSize < 24)) { | |
fprintf(fdinfo, "- dwEntryMetaDataElementSize is small\n"); | |
} | |
if((hexseg >= 0) && (hexseg <= last_segment)) { | |
hexdump(fd, wavebankheader.Segments[hexseg].dwOffset, wavebankheader.Segments[hexseg].dwLength); | |
fclose(fd); | |
myexit(0); | |
return(0); | |
} else if(hexseg != -1) { | |
myexit(0); | |
return(0); | |
} | |
if(wavebankheader.dwVersion >= 42) segidx_entry_name = 3; | |
waveentry_offset = wavebankheader.Segments[segidx_entry_name].dwOffset; | |
if(wavebankheader.Segments[segidx_entry_name].dwOffset && wavebankheader.Segments[segidx_entry_name].dwLength) { | |
if(wavebankdata.dwEntryNameElementSize == -1) wavebankdata.dwEntryNameElementSize = 0; | |
entry_name = malloc(wavebankdata.dwEntryNameElementSize + 1); | |
if(!entry_name) std_err(); | |
entry_name[wavebankdata.dwEntryNameElementSize] = 0; | |
} | |
/* WAVEBANKENTRY */ | |
fprintf(fdinfo, "\n" | |
" length fmt freq c b filename\n" | |
"=====================================================================\n"); | |
for(current_entry = 0; current_entry < wavebankdata.dwEntryCount; current_entry++) { | |
MYFSEEK(wavebank_offset); | |
SHOWFILEOFF; | |
memset(&wavebankentry, 0, sizeof(wavebankentry)); | |
if(wavebankdata.dwFlags & WAVEBANK_FLAGS_COMPACT) { | |
len = fr32(fd); | |
wavebankentry.Format = compact_format; | |
wavebankentry.PlayRegion.dwOffset = (len & ((1 << 21) - 1)) * wavebankdata.dwAlignment; | |
wavebankentry.PlayRegion.dwLength = (len >> 21) & ((1 << 11) - 1); | |
/* WORK-AROUND BECAUSE I DON'T KNOW HOW TO HANDLE THE DEVIATION LENGTH! */ | |
MYFSEEK(wavebank_offset + wavebankdata.dwEntryMetaDataElementSize); // seek to the next | |
if(current_entry == (wavebankdata.dwEntryCount - 1)) { // the last track | |
len = wavebankheader.Segments[last_segment].dwLength; | |
} else { | |
len = ((fr32(fd) & ((1 << 21) - 1)) * wavebankdata.dwAlignment); | |
} | |
wavebankentry.PlayRegion.dwLength = | |
len - // next offset | |
wavebankentry.PlayRegion.dwOffset; // current offset | |
goto wavebank_handle; | |
} | |
if(wavebankheader.dwVersion == 1) { | |
wavebankentry.Format = fr32(fd); | |
wavebankentry.PlayRegion.dwOffset = fr32(fd); | |
wavebankentry.PlayRegion.dwLength = fr32(fd); | |
wavebankentry.LoopRegion.dwOffset = fr32(fd); | |
wavebankentry.LoopRegion.dwLength = fr32(fd); | |
} else { | |
if(wavebankdata.dwEntryMetaDataElementSize >= 4) wavebankentry.dwFlagsAndDuration = fr32(fd); | |
if(wavebankdata.dwEntryMetaDataElementSize >= 8) wavebankentry.Format = fr32(fd); | |
if(wavebankdata.dwEntryMetaDataElementSize >= 12) wavebankentry.PlayRegion.dwOffset = fr32(fd); | |
if(wavebankdata.dwEntryMetaDataElementSize >= 16) wavebankentry.PlayRegion.dwLength = fr32(fd); | |
if(wavebankdata.dwEntryMetaDataElementSize >= 20) wavebankentry.LoopRegion.dwOffset = fr32(fd); | |
if(wavebankdata.dwEntryMetaDataElementSize >= 24) wavebankentry.LoopRegion.dwLength = fr32(fd); | |
} | |
if(wavebankdata.dwEntryMetaDataElementSize < 24) { // work-around | |
if(!wavebankentry.PlayRegion.dwLength) { | |
wavebankentry.PlayRegion.dwLength = wavebankheader.Segments[last_segment].dwLength; | |
} | |
} else if(wavebankdata.dwEntryMetaDataElementSize > sizeof(wavebankentry)) { // skip unused fields | |
MYFSEEK(wavebank_offset + wavebankdata.dwEntryMetaDataElementSize); | |
} | |
wavebank_handle: | |
wavebank_offset += wavebankdata.dwEntryMetaDataElementSize; | |
wavebankentry.PlayRegion.dwOffset += playregion_offset; | |
if(wavebankheader.dwVersion == 1) { // I'm not 100% sure if the following is correct | |
// version 1: | |
// 1 00000000 000101011000100010 0 001 0 | |
// | | | | | | | |
// | | | | | wFormatTag | |
// | | | | nChannels | |
// | | | ??? | |
// | | nSamplesPerSec | |
// | wBlockAlign | |
// wBitsPerSample | |
codec = (wavebankentry.Format ) & ((1 << 1) - 1); | |
chans = (wavebankentry.Format >> (1) ) & ((1 << 3) - 1); | |
rate = (wavebankentry.Format >> (1 + 3 + 1) ) & ((1 << 18) - 1); | |
align = (wavebankentry.Format >> (1 + 3 + 1 + 18) ) & ((1 << 8) - 1); | |
bits = (wavebankentry.Format >> (1 + 3 + 1 + 18 + 8)) & ((1 << 1) - 1); | |
/*} else if(wavebankheader.dwVersion == 23) { // I'm not 100% sure if the following is correct | |
// version 23: | |
// 1000000000 001011101110000000 001 1 | |
// | | | | | | |
// | | | | ??? | |
// | | | nChannels? | |
// | | nSamplesPerSec | |
// | ??? | |
// !!!UNKNOWN FORMAT!!! | |
//codec = -1; | |
//chans = (wavebankentry.Format >> 1) & ((1 << 3) - 1); | |
//rate = (wavebankentry.Format >> 4) & ((1 << 18) - 1); | |
//bits = (wavebankentry.Format >> 31) & ((1 << 1) - 1); | |
codec = (wavebankentry.Format ) & ((1 << 1) - 1); | |
chans = (wavebankentry.Format >> (1) ) & ((1 << 3) - 1); | |
rate = (wavebankentry.Format >> (1 + 3) ) & ((1 << 18) - 1); | |
align = (wavebankentry.Format >> (1 + 3 + 18) ) & ((1 << 9) - 1); | |
bits = (wavebankentry.Format >> (1 + 3 + 18 + 9)) & ((1 << 1) - 1); */ | |
} else { // versions 2, 3, 37, 42, 43, 44 and so on, check WAVEBANKMINIWAVEFORMAT in xact3wb.h | |
// 0 00000000 000111110100000000 010 01 | |
// | | | | | | |
// | | | | wFormatTag | |
// | | | nChannels | |
// | | nSamplesPerSec | |
// | wBlockAlign | |
// wBitsPerSample | |
codec = (wavebankentry.Format ) & ((1 << 2) - 1); | |
chans = (wavebankentry.Format >> (2) ) & ((1 << 3) - 1); | |
rate = (wavebankentry.Format >> (2 + 3) ) & ((1 << 18) - 1); | |
align = (wavebankentry.Format >> (2 + 3 + 18) ) & ((1 << 8) - 1); | |
bits = (wavebankentry.Format >> (2 + 3 + 18 + 8)) & ((1 << 1) - 1); | |
} | |
// this work-around is correct but I don't know what's the latest version that falls in this rule | |
if(wavebankheader.dwVersion <= 3) { | |
if(codec == WAVEBANKMINIFORMAT_TAG_XMA) codec = WAVEBANKMINIFORMAT_TAG_ADPCM; | |
} | |
/* TRY XSB NAME */ | |
len = xsb_names(fdxsb, fname, current_entry); | |
/* CODEC / FORMAT */ | |
if(rawfiles) codec = -1; | |
switch(codec) { | |
case WAVEBANKMINIFORMAT_TAG_PCM: { | |
strcpy(fname + len, ".wav"); | |
codecstr = "PCM"; | |
break; | |
} | |
case WAVEBANKMINIFORMAT_TAG_XMA: { | |
strcpy(fname + len, ".wav"); | |
codecstr = "XMA"; | |
break; | |
} | |
case WAVEBANKMINIFORMAT_TAG_ADPCM: { | |
strcpy(fname + len, ".wav"); | |
codecstr = "ADP"; | |
break; | |
} | |
case WAVEBANKMINIFORMAT_TAG_WMA: { | |
strcpy(fname + len, ".wma"); | |
codecstr = "WMA"; | |
break; | |
} | |
default: { | |
strcpy(fname + len, ".dat"); | |
codecstr = "???"; | |
break; | |
} | |
} | |
fprintf(fdinfo, | |
" %-10u %-3s %6u %u %-2u %s\n", | |
wavebankentry.PlayRegion.dwLength, | |
codecstr, | |
rate, | |
chans, | |
bits ? 16 : 8, | |
fname); | |
if(verbose) { | |
if(entry_name) { | |
MYFSEEK(waveentry_offset); | |
waveentry_offset += wavebankdata.dwEntryNameElementSize; | |
read_file(fd, entry_name, wavebankdata.dwEntryNameElementSize); | |
fprintf(fdinfo, " description %s\n", entry_name); | |
} | |
fprintf(fdinfo, | |
" 0x%08x format 0x%08x flags 0x%08x\n" | |
" region offset 0x%08x region length 0x%08x\n" | |
"\n", | |
file_offset + wavebankentry.PlayRegion.dwOffset, wavebankentry.Format, wavebankentry.dwFlagsAndDuration, | |
file_offset + wavebankentry.LoopRegion.dwOffset, wavebankentry.LoopRegion.dwLength); | |
} | |
/* FILE EXTRACTION */ | |
if(!list) { | |
MYFSEEK(wavebankentry.PlayRegion.dwOffset); | |
if(execstring && dostdout) exec_run(fname); // start before if stdout (not used!!!) | |
getxwbfile( | |
fd, | |
dostdout ? NULL : fname, | |
wavebankentry.PlayRegion.dwLength, | |
codec, | |
rate, | |
chans, | |
bits, | |
align); | |
if(execstring && !dostdout) exec_run(fname); // start later if not | |
} | |
} | |
if(entry_name) free(entry_name); | |
if(fdxsb) fclose(fdxsb); | |
fclose(fd); | |
fprintf(fdinfo, "\n- finished (%u files)\n\n", wavebankdata.dwEntryCount); | |
myexit(0); | |
return(0); | |
} | |
u8 *mystrrchrs(u8 *str, u8 *chrs) { | |
int i; | |
u8 *p, | |
*ret = NULL; | |
if(str) { | |
for(i = 0; chrs[i]; i++) { | |
p = strrchr(str, chrs[i]); | |
if(p) { | |
str = p; | |
ret = p; | |
} | |
} | |
} | |
return(ret); | |
} | |
#ifdef WIN32 | |
char *get_file(char *title, int xwb, int multi) { | |
OPENFILENAME ofn; | |
int maxlen; | |
char *filename; | |
if(multi) { | |
maxlen = 32768; // 32k limit ansi, no limit unicode | |
} else { | |
maxlen = PATHSZ; | |
} | |
filename = malloc(maxlen + 1); | |
if(!filename) std_err(); | |
filename[0] = 0; | |
memset(&ofn, 0, sizeof(ofn)); | |
ofn.lStructSize = sizeof(ofn); | |
if(xwb) { | |
ofn.lpstrFilter = | |
"XWB archive\0" "*.xwb;*.xen\0" | |
"(*.*)\0" "*.*\0" | |
"\0" "\0"; | |
} else { | |
ofn.lpstrFilter = | |
"(*.*)\0" "*.*\0" | |
"\0" "\0"; | |
} | |
ofn.nFilterIndex = 1; | |
ofn.lpstrFile = filename; | |
ofn.nMaxFile = maxlen; | |
ofn.lpstrTitle = title; | |
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | | |
OFN_LONGNAMES | OFN_EXPLORER | | |
OFN_HIDEREADONLY | OFN_ENABLESIZING; | |
if(multi) ofn.Flags |= OFN_ALLOWMULTISELECT; | |
printf("- %s\n", ofn.lpstrTitle); | |
if(!GetOpenFileName(&ofn)) exit(1); // terminate immediately | |
return(filename); | |
} | |
char *get_folder(char *title) { | |
OPENFILENAME ofn; | |
char *p; | |
char *filename; | |
filename = malloc(PATHSZ + 1); | |
if(!filename) std_err(); | |
strcpy(filename, "enter in the output folder and press Save"); | |
memset(&ofn, 0, sizeof(ofn)); | |
ofn.lStructSize = sizeof(ofn); | |
ofn.lpstrFilter = "(*.*)\0" "*.*\0" "\0" "\0"; | |
ofn.nFilterIndex = 1; | |
ofn.lpstrFile = filename; | |
ofn.nMaxFile = PATHSZ; | |
ofn.lpstrTitle = title; | |
ofn.Flags = OFN_PATHMUSTEXIST | /*OFN_FILEMUSTEXIST |*/ | |
OFN_LONGNAMES | OFN_EXPLORER | | |
OFN_HIDEREADONLY | OFN_ENABLESIZING; | |
printf("- %s\n", ofn.lpstrTitle); | |
if(!GetSaveFileName(&ofn)) exit(1); // terminate immediately | |
p = mystrrchrs(filename, "\\/"); | |
if(p) *p = 0; | |
return(filename); | |
} | |
#endif | |
int xsb_names(FILE *fd, char *name, int track) { | |
int i; | |
char *p; | |
if(!fd) goto errorx; | |
if(track >= 0) goto return_names; | |
if(verbose) { | |
fseek(fd, xsboff, SEEK_SET); | |
for(i = 0; ; i++) { | |
for(p = name; fread(p, 1, 1, fd); p++) { | |
if(!p || (*p < ' ')) break; | |
} | |
*p = 0; | |
if(p == name) break; | |
fprintf(fdinfo, " track %04x %s\n", i, name); | |
} | |
fputc('\n', fdinfo); | |
} | |
return(0); | |
return_names: | |
fseek(fd, xsboff, SEEK_SET); | |
for(i = 0; ; i++) { | |
for(p = name; fread(p, 1, 1, fd); p++) { | |
if(!p || (*p < ' ')) break; | |
} | |
*p = 0; | |
if(p == name) break; | |
if(i == track) return(strlen(name)); | |
} | |
errorx: | |
return(sprintf(name, hex_names ? "%08x" : "%u", track)); | |
} | |
void exec_arg(char *data) { | |
int i; | |
u8 *p; | |
execstring = data; | |
for(p = execstring, i = 0; (p = strstr(p, EXECIN)); p++, i++); | |
tmpexec = malloc(strlen(execstring) - (i * EXECINSZ) + (i * EXECOUTSZ) + 1); | |
if(!tmpexec) std_err(); | |
} | |
void exec_run(u8 *fname) { | |
int fnamelen; | |
u8 *p, | |
*l, | |
*execptr; | |
fnamelen = strlen(fname); | |
execptr = tmpexec; | |
for(p = execstring; (l = strstr(p, EXECIN)); p = l + EXECINSZ) { | |
memcpy(execptr, p, l - p); | |
execptr += l - p; | |
memcpy(execptr, fname, fnamelen); | |
execptr += fnamelen; | |
} | |
strcpy(execptr, p); | |
fprintf(fdinfo, " Execute: \"%s\"\n", tmpexec); | |
system(tmpexec); | |
} | |
void hexdump(FILE *fd, u32 off, u32 len) { | |
int t; | |
u8 buff[512]; | |
fprintf(fdinfo, "- hex dump of %u bytes at offset 0x%08x\n", len, off); | |
MYFSEEK(off); | |
for(t = sizeof(buff); len; len -= t) { | |
if(len < t) t = len; | |
if(fread(buff, 1, t, fd) != t) break; | |
show_dump(buff, t, stdout); | |
} | |
} | |
u32 unzip(FILE *fd, FILE *fdo) { | |
z_stream z; | |
u32 oldsz = 0, | |
len; | |
int err = Z_OK, | |
insz, | |
outsz; | |
u8 *in, | |
*out; | |
z.zalloc = (alloc_func)0; | |
z.zfree = (free_func)0; | |
z.opaque = (voidpf)0; | |
if(inflateInit(&z)) { | |
fprintf(stderr, "\nError: zlib initialization error\n\n"); | |
myexit(1); | |
} | |
insz = 512; | |
outsz = insz * 512; | |
in = malloc(insz); | |
out = malloc(outsz); | |
if(!in || !out) std_err(); | |
//u8 *in_write = in; | |
while((len = fread(in, 1, insz, fd))) { | |
z.next_in = in; | |
z.avail_in = len; | |
z.next_out = out; | |
z.avail_out = outsz; | |
err = inflate(&z, Z_NO_FLUSH); | |
if(fwrite(out, 1, z.total_out - oldsz, fdo) != (z.total_out - oldsz)) write_err(); | |
oldsz = z.total_out; | |
if(err != Z_OK) break; // gets also the partial data | |
} | |
free(in); | |
free(out); | |
len = z.total_out; | |
inflateEnd(&z); | |
return(len); | |
} | |
void getxwbfile(FILE *fdin, char *fname, u32 size, int codec, int rate, int chans, int expbits, int align) { | |
mywav_fmtchunk fmt; | |
FILE *fdo; | |
u32 //pos, | |
dw; | |
int i, | |
len; | |
u8 buff[8192], | |
fact_chunk[64], | |
*f, | |
*p; | |
if(codec == WAVEBANKMINIFORMAT_TAG_WMA) { | |
len = fread(buff, 1, 16, fdin); | |
if(len == 16) { | |
fseek(fdin, -len, SEEK_CUR); | |
if(memcmp(buff, "\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c", len)) { | |
codec = WAVEBANKMINIFORMAT_TAG_XMA; | |
if(fname) strcpy(strrchr(fname, '.'), ".wav"); | |
} | |
} | |
} | |
if(fname) { | |
if(!overwrite_file(fname)) return; | |
fdo = fopen(fname, "wb"); | |
if(!fdo) std_err(); | |
} else { | |
fdo = stdout; | |
} | |
if(chans <= 0) chans = 1; // useless? | |
switch(codec) { | |
case WAVEBANKMINIFORMAT_TAG_PCM: { | |
fmt.wFormatTag = 0x0001; | |
fmt.wChannels = chans; | |
fmt.dwSamplesPerSec = rate; | |
fmt.wBitsPerSample = 8 << expbits; | |
fmt.wBlockAlign = (fmt.wBitsPerSample / 8) * fmt.wChannels; | |
fmt.dwAvgBytesPerSec = fmt.dwSamplesPerSec * fmt.wBlockAlign; | |
mywav_writehead(fdo, &fmt, size, NULL, 0, NULL, 0); | |
break; | |
} | |
case WAVEBANKMINIFORMAT_TAG_WMA: { | |
// WMA is ready to play | |
break; | |
} | |
case WAVEBANKMINIFORMAT_TAG_XMA: { | |
/*fmt.wFormatTag = 0x0069; | |
fmt.wChannels = chans; | |
fmt.dwSamplesPerSec = rate; | |
fmt.wBitsPerSample = 4; | |
fmt.wBlockAlign = 36 * fmt.wChannels; | |
fmt.dwAvgBytesPerSec = (689 * fmt.wBlockAlign) + 4; // boh, not important | |
mywav_writehead(fdo, &fmt, size, "\x02\x00" "\x40\x00", 4, NULL, 0); // useless*/ | |
xma2_header(fdo, rate, chans, 16, size, NULL, 0, 0); // samples? | |
break; | |
} | |
case WAVEBANKMINIFORMAT_TAG_ADPCM: { | |
fmt.wFormatTag = 0x0002; | |
fmt.wChannels = chans; | |
fmt.dwSamplesPerSec = rate; | |
fmt.wBitsPerSample = 4; | |
fmt.wBlockAlign = (align + ADPCM_MINIWAVEFORMAT_BLOCKALIGN_CONVERSION_OFFSET) * fmt.wChannels; | |
fmt.dwAvgBytesPerSec = 21 * fmt.wBlockAlign; // should be correct, although not much important | |
// code from -=CHE@TER=- of CTPAX-X Team | |
#define ADPCM_COEFFS 7 | |
static const u32 coeff7[ADPCM_COEFFS] = { | |
0x00000100, | |
0xFF000200, | |
0x00000000, | |
0x004000C0, | |
0x000000F0, | |
0xFF3001CC, | |
0xFF180188 | |
}; | |
p = buff; | |
p += putxx(p, 32 /*sizeof(WAVEFORMATEX)*/, 16); // cbSize | |
dw = (((fmt.nBlockAlign - (7 * fmt.nChannels)) * 8) / (fmt.wBitsPerSample * fmt.nChannels)) + 2; | |
fmt.nAvgBytesPerSec = ((fmt.nSamplesPerSec / dw) * fmt.nBlockAlign); | |
p += putxx(p, dw, 16); | |
p += putxx(p, ADPCM_COEFFS, 16); | |
for(i = 0; i < ADPCM_COEFFS; i++) { | |
p += putxx(p, coeff7[i], 32); | |
} | |
f = fact_chunk; | |
*f++ = 'f'; | |
*f++ = 'a'; | |
*f++ = 'c'; | |
*f++ = 't'; | |
f += putxx(f, 4, 32); | |
/* | |
http://download.microsoft.com/download/9/8/6/9863C72A-A3AA-4DDB-B1BA-CA8D17EFD2D4/RIFFNEW.pdf | |
Fact Chunk | |
This chunk is required for all WAVE formats other than WAVE_FORMAT_PCM. | |
It stores file dependent information about the contents of the WAVE data. | |
It currently specifies the time length of the data in samples. | |
*/ | |
if (fmt.nBlockAlign && fmt.nChannels) { | |
dw = ((fmt.nBlockAlign - (7 * fmt.nChannels)) * 8) / fmt.wBitsPerSample; | |
dw = (size / fmt.nBlockAlign) * dw; | |
dw = dw / fmt.nChannels; | |
} else { | |
dw = 0; | |
} | |
f += putxx(f, dw, 32); | |
mywav_writehead(fdo, &fmt, size, buff, p - buff, fact_chunk, f - fact_chunk); | |
break; | |
} | |
default: { | |
break; | |
} | |
} | |
for(len = sizeof(buff); size; size -= len) { | |
if(len > size) len = size; | |
if(fread(buff, 1, len, fdin) != len) read_err(); | |
if(fwrite(buff, 1, len, fdo) != len) write_err(); | |
} | |
if(fname) fclose(fdo); | |
} | |
int xwb_scan_sign(FILE *fd) { | |
int len, | |
tot; | |
u8 buff[2048], | |
*p, | |
*l; | |
for(tot = 0; (len = fread(buff, 1, sizeof(buff), fd)); tot += len) { | |
for(p = buff, l = buff + len - 8; p < l; p++) { | |
/* here I check the XWB signature plus the last version's byte */ | |
if( | |
(!memcmp(p, XWBSIGNi, 4) && !p[7]) || | |
(!memcmp(p, XWBSIGNb, 4) && !p[4])) { | |
return(tot + (p - buff)); | |
} | |
} | |
} | |
return(-1); | |
} | |
int overwrite_file(char *fname) { | |
static int all_answer = 0; | |
FILE *fd; | |
int t; | |
char ans[16]; | |
fd = fopen(fname, "rb"); | |
if(!fd) return(1); | |
fclose(fd); | |
if(all_answer) return 1; | |
fprintf(fdinfo, "- do you want to overwrite the file \"%s\"? (y/N/all): ", fname); | |
fflush(stdin); | |
fgets(ans, sizeof(ans), stdin); | |
t = ans[0]; | |
if(t == 'y') return(1); | |
if(t == 'a') { | |
all_answer = 1; | |
return(1); | |
} | |
return(0); | |
} | |
int get_num(char *data) { | |
int ret; | |
if((data[0] == '0') && (tolower(data[1]) == 'x')) { | |
sscanf(data + 2, "%x", &ret); | |
} else if(data[0] == '$') { | |
sscanf(data + 1, "%x", &ret); | |
} else { | |
sscanf(data, "%d", &ret); | |
} | |
return(ret); | |
} | |
void read_err(void) { | |
fprintf(stderr, "\nError: the file contains unexpected data\n\n"); | |
myexit(1); | |
} | |
void write_err(void) { | |
fprintf(stderr, "\nError: impossible to write the output file, probably your disk space is finished\n\n"); | |
myexit(1); | |
} | |
u16 fri16(FILE *fd) { | |
int t1, | |
t2; | |
t1 = fgetc(fd); | |
t2 = fgetc(fd); | |
if((t1 < 0) || (t2 < 0)) read_err(); | |
return(t1 | (t2 << 8)); | |
} | |
u16 frb16(FILE *fd) { | |
int t1, | |
t2; | |
t1 = fgetc(fd); | |
t2 = fgetc(fd); | |
if((t1 < 0) || (t2 < 0)) read_err(); | |
return(t2 | (t1 << 8)); | |
} | |
u32 fri32(FILE *fd) { | |
int t1, | |
t2, | |
t3, | |
t4; | |
t1 = fgetc(fd); | |
t2 = fgetc(fd); | |
t3 = fgetc(fd); | |
t4 = fgetc(fd); | |
if((t1 < 0) || (t2 < 0) || (t3 < 0) || (t4 < 0)) read_err(); | |
return(t1 | (t2 << 8) | (t3 << 16) | (t4 << 24)); | |
} | |
u32 frb32(FILE *fd) { | |
int t1, | |
t2, | |
t3, | |
t4; | |
t1 = fgetc(fd); | |
t2 = fgetc(fd); | |
t3 = fgetc(fd); | |
t4 = fgetc(fd); | |
if((t1 < 0) || (t2 < 0) || (t3 < 0) || (t4 < 0)) read_err(); | |
return(t4 | (t3 << 8) | (t2 << 16) | (t1 << 24)); | |
} | |
int putxx(u8 *data, u32 num, int bits) { | |
int i, | |
bytes; | |
bytes = bits >> 3; | |
if(!bytes) bytes = bits; | |
for(i = 0; i < bytes; i++) { | |
data[i] = (num >> (i << 3)) & 0xff; | |
} | |
return(bytes); | |
} | |
void std_err(void) { | |
perror("\nError"); | |
myexit(1); | |
} | |
void myexit(int ret) { | |
#ifdef WIN32 | |
u8 ans[16]; | |
if(GetWindowLong(mywnd, GWL_WNDPROC)) { | |
printf("\nPress RETURN to quit"); | |
fgets(ans, sizeof(ans), stdin); | |
} | |
#endif | |
exit(ret); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
by Luigi Auriemma | |
*/ | |
#ifdef WIN32 | |
#include <windows.h> | |
#else | |
typedef unsigned int DWORD; | |
typedef unsigned short WORD; | |
typedef unsigned char BYTE; | |
typedef struct tWAVEFORMATEX { | |
WORD wFormatTag; | |
WORD nChannels; | |
DWORD nSamplesPerSec; | |
DWORD nAvgBytesPerSec; | |
WORD nBlockAlign; | |
WORD wBitsPerSample; | |
WORD cbSize; | |
} WAVEFORMATEX,*PWAVEFORMATEX,*LPWAVEFORMATEX; | |
#endif | |
typedef struct XMASTREAMFORMAT | |
{ | |
DWORD PsuedoBytesPerSec; // Used by encoder | |
DWORD SampleRate; // Sample rate for the stream. | |
DWORD LoopStart; // Loop start offset (in bits). | |
DWORD LoopEnd; // Loop end offset (in bits). | |
// Format for SubframeData: eeee ssss. | |
// e: Subframe number of loop end point [0,3]. | |
// s: Number of subframes to skip before decoding and outputting at the loop start point [1,4]. | |
BYTE SubframeData; // Data for decoding subframes. See above. | |
BYTE Channels; // Number of channels in the stream (1 or 2). | |
WORD ChannelMask; // Channel assignments for the channels in the stream (same as | |
// lower 16 bits of dwChannelMask in WAVEFORMATEXTENSIBLE). | |
} XMASTREAMFORMAT, *PXMASTREAMFORMAT, *NPXMASTREAMFORMAT, *LPXMASTREAMFORMAT; | |
typedef const XMASTREAMFORMAT *LPCXMASTREAMFORMAT; | |
typedef struct XMAWAVEFORMAT | |
{ | |
WORD FormatTag; // Audio format type (always WAVE_FORMAT_XMA). | |
WORD BitsPerSample; // Bit depth (currently required to be 16). | |
WORD EncodeOptions; // Options for XMA encoder/decoder. | |
WORD LargestSkip; // Largest skip used in interleaving streams. | |
WORD NumStreams; // Number of interleaved audio streams. | |
BYTE LoopCount; // Number of loop repetitions (255 == infinite). | |
BYTE Version; // Version of the encoder that generated this. | |
XMASTREAMFORMAT XmaStreams[1]; // Format info for each stream (can grow based on wNumStreams). | |
} XMAWAVEFORMAT, *PXMAWAVEFORMAT, *NPXMAWAVEFORMAT, *LPXMAWAVEFORMAT; | |
typedef XMAWAVEFORMAT *LPCXMAWAVEFORMAT; | |
typedef const WAVEFORMATEX *LPCWAVEFORMATEX; | |
typedef struct XMA2WAVEFORMATEX | |
{ | |
WAVEFORMATEX wfx; | |
// Meaning of the WAVEFORMATEX fields here: | |
// wFormatTag; // Audio format type; always WAVE_FORMAT_XMA2 | |
// nChannels; // Channel count of the decoded audio | |
// nSamplesPerSec; // Sample rate of the decoded audio | |
// nAvgBytesPerSec; // Used internally by the XMA encoder | |
// nBlockAlign; // Decoded sample size; channels * wBitsPerSample / 8 | |
// wBitsPerSample; // Bits per decoded mono sample; always 16 for XMA | |
// cbSize; // Size in bytes of the rest of this structure (34) | |
WORD NumStreams; // Number of audio streams (1 or 2 channels each) | |
DWORD ChannelMask; // Spatial positions of the channels in this file, | |
// stored as SPEAKER_xxx values (see audiodefs.h) | |
DWORD SamplesEncoded; // Total number of PCM samples the file decodes to | |
DWORD BytesPerBlock; // XMA block size (but the last one may be shorter) | |
DWORD PlayBegin; // First valid sample in the decoded audio | |
DWORD PlayLength; // Length of the valid part of the decoded audio | |
DWORD LoopBegin; // Beginning of the loop region in decoded sample terms | |
DWORD LoopLength; // Length of the loop region in decoded sample terms | |
BYTE LoopCount; // Number of loop repetitions; 255 = infinite | |
BYTE EncoderVersion; // Version of XMA encoder that generated the file | |
WORD BlockCount; // XMA blocks in file (and entries in its seek table) | |
} XMA2WAVEFORMATEX, *PXMA2WAVEFORMATEX; | |
typedef struct XMA2STREAMFORMAT | |
{ | |
BYTE Channels; // Number of channels in the stream (1 or 2) | |
BYTE RESERVED; // Reserved for future use | |
WORD ChannelMask; // Spatial positions of the channels in the stream | |
} XMA2STREAMFORMAT; | |
// Legacy XMA2 format structure (big-endian byte ordering) | |
typedef struct XMA2WAVEFORMAT | |
{ | |
BYTE Version; // XMA encoder version that generated the file. | |
// Always 3 or higher for XMA2 files. | |
BYTE NumStreams; // Number of interleaved audio streams | |
BYTE RESERVED; // Reserved for future use | |
BYTE LoopCount; // Number of loop repetitions; 255 = infinite | |
DWORD LoopBegin; // Loop begin point, in samples | |
DWORD LoopEnd; // Loop end point, in samples | |
DWORD SampleRate; // The file's decoded sample rate | |
DWORD EncodeOptions; // Options for the XMA encoder/decoder | |
DWORD PsuedoBytesPerSec; // Used internally by the XMA encoder | |
DWORD BlockSizeInBytes; // Size in bytes of this file's XMA blocks (except | |
// possibly the last one). Always a multiple of | |
// 2Kb, since XMA blocks are arrays of 2Kb packets. | |
DWORD SamplesEncoded; // Total number of PCM samples encoded in this file | |
DWORD SamplesInSource; // Actual number of PCM samples in the source | |
// material used to generate this file | |
DWORD BlockCount; // Number of XMA blocks in this file (and hence | |
// also the number of entries in its seek table) | |
XMA2STREAMFORMAT Streams[1]; // Per-stream format information; the actual | |
// array length is in the NumStreams field. | |
} XMA2WAVEFORMAT; | |
inline unsigned xma_le16(unsigned n) { | |
static const int endianess = 1; | |
if(*(char *)&endianess) return(n); | |
n = (((n & 0xff00) >> 8) | | |
((n & 0x00ff) << 8)); | |
return(n); | |
} | |
inline unsigned xma_le32(unsigned n) { | |
static const int endianess = 1; | |
if(*(char *)&endianess) return(n); | |
n = (((n & 0xff000000) >> 24) | | |
((n & 0x00ff0000) >> 8) | | |
((n & 0x0000ff00) << 8) | | |
((n & 0x000000ff) << 24)); | |
return(n); | |
} | |
inline unsigned xma_be16(unsigned n) { | |
static const int endianess = 1; | |
if(!*(char *)&endianess) return(n); | |
n = (((n & 0xff00) >> 8) | | |
((n & 0x00ff) << 8)); | |
return(n); | |
} | |
inline unsigned xma_be32(unsigned n) { | |
static const int endianess = 1; | |
if(!*(char *)&endianess) return(n); | |
n = (((n & 0xff000000) >> 24) | | |
((n & 0x00ff0000) >> 8) | | |
((n & 0x0000ff00) << 8) | | |
((n & 0x000000ff) << 24)); | |
return(n); | |
} | |
int xma_quick_mask(int chans) { // made on the fly, not important and probably wrong | |
int i, | |
mask; | |
//return(0x80000000); // SPEAKER_ALL | |
mask = 0; | |
for(i = 0; i < chans; i++) { | |
mask = 1 << 1; | |
} | |
return(mask); | |
} | |
// XMA1: | |
// fmt | |
// data | |
// seek | |
int xma1_header(FILE *fd, int freq, int chans, int bits, int rawlen, unsigned char *seek, int seeklen, int samples) { | |
XMAWAVEFORMAT fmt; | |
mywav_chunk chunk; | |
if(freq <= 0) freq = 44100; | |
if(chans <= 0) chans = 1; | |
if(bits <= 0) bits = 16; | |
//if(!seek || (seeklen <= 0)) { seek = &samples; seeklen = 4; } | |
fmt.FormatTag = xma_le16(0x0165); | |
fmt.BitsPerSample = xma_le16(bits); | |
fmt.EncodeOptions = xma_le16(0x10d6); | |
fmt.LargestSkip = xma_le16(0); | |
fmt.NumStreams = xma_le16(1); | |
fmt.LoopCount = 0; | |
fmt.Version = 2; | |
fmt.XmaStreams->PsuedoBytesPerSec = xma_le32(rawlen); // used only by the encoder | |
fmt.XmaStreams->SampleRate = xma_le32(freq); | |
fmt.XmaStreams->LoopStart = xma_le32(0); | |
fmt.XmaStreams->LoopEnd = xma_le32(0); | |
fmt.XmaStreams->SubframeData = 0; | |
fmt.XmaStreams->Channels = chans; | |
fmt.XmaStreams->ChannelMask = xma_le16(xma_quick_mask(chans)); | |
memcpy(chunk.id, "RIFF", 4); | |
chunk.size = | |
4 + sizeof(mywav_chunk) // RIFF | |
+ sizeof(fmt) // fmt | |
+ sizeof(mywav_chunk) + rawlen // data | |
+ sizeof(mywav_chunk) + seeklen; // seek | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(mywav_fwmem(fd, "WAVE", 4) < 0) return(-1); | |
memcpy(chunk.id, "fmt ", 4); | |
chunk.size = sizeof(fmt) + seeklen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
//if(mywav_fwfmtchunk(fd, &fmt) < 0) return(-1); | |
if(fwrite(&fmt, 1, sizeof(fmt), fd) != sizeof(fmt)) return(-1); | |
memcpy(chunk.id, "seek", 4); | |
chunk.size = seeklen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(fwrite(seek, 1, seeklen, fd) != seeklen) return(-1); | |
// data must be placed at the end so that the main tool can write the data after it | |
memcpy(chunk.id, "data", 4); | |
chunk.size = rawlen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
return(0); | |
} | |
// XMA2: | |
// fmt | |
// data | |
// seek | |
int xma2_header(FILE *fd, int freq, int chans, int bits, int rawlen, unsigned char *seek, int seeklen, int samples) { | |
XMA2WAVEFORMATEX fmt; | |
mywav_chunk chunk; | |
if(freq <= 0) freq = 44100; | |
if(chans <= 0) chans = 1; | |
if(bits <= 0) bits = 16; | |
//if(!seek || (seeklen <= 0)) { seek = &samples; seeklen = 4; } | |
fmt.wfx.wFormatTag = xma_le16(0x0166); | |
fmt.wfx.nChannels = xma_le16(chans); | |
fmt.wfx.nSamplesPerSec = xma_le32(freq); | |
fmt.wfx.nAvgBytesPerSec = xma_le32(rawlen); // used only by the encoder | |
fmt.wfx.nBlockAlign = xma_le16(4); | |
fmt.wfx.wBitsPerSample = xma_le16(bits); | |
fmt.wfx.cbSize = xma_le16(34); | |
fmt.NumStreams = xma_le16(1); | |
fmt.ChannelMask = xma_le32(xma_quick_mask(chans)); | |
fmt.SamplesEncoded = xma_le32(samples); | |
fmt.BytesPerBlock = xma_le32(0x10000); | |
fmt.PlayBegin = xma_le32(0); | |
fmt.PlayLength = xma_le32(samples); | |
fmt.LoopBegin = xma_le32(0); | |
fmt.LoopLength = xma_le32(0); | |
fmt.LoopCount = 0; | |
fmt.EncoderVersion = 3; // or 4 | |
fmt.BlockCount = xma_le16(1); | |
memcpy(chunk.id, "RIFF", 4); | |
chunk.size = | |
4 + sizeof(mywav_chunk) // RIFF | |
+ sizeof(fmt) // fmt | |
+ sizeof(mywav_chunk) + rawlen // data | |
+ sizeof(mywav_chunk) + seeklen; // seek | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(mywav_fwmem(fd, "WAVE", 4) < 0) return(-1); | |
memcpy(chunk.id, "fmt ", 4); | |
chunk.size = sizeof(fmt) + seeklen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
//if(mywav_fwfmtchunk(fd, &fmt) < 0) return(-1); | |
if(fwrite(&fmt, 1, sizeof(fmt), fd) != sizeof(fmt)) return(-1); | |
memcpy(chunk.id, "seek", 4); | |
chunk.size = seeklen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(fwrite(seek, 1, seeklen, fd) != seeklen) return(-1); | |
// data must be placed at the end so that the main tool can write the data after it | |
memcpy(chunk.id, "data", 4); | |
chunk.size = rawlen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
return(0); | |
} | |
// XMA2xact: | |
// data | |
// XMA2 | |
// seek | |
int xma2xact_header(FILE *fd, int freq, int chans, int bits, int rawlen, unsigned char *seek, int seeklen, int samples) { | |
XMA2WAVEFORMAT fmt; | |
mywav_chunk chunk; | |
if(freq <= 0) freq = 44100; | |
if(chans <= 0) chans = 1; | |
if(bits <= 0) bits = 16; | |
//if(!seek || (seeklen <= 0)) { seek = &samples; seeklen = 4; } | |
fmt.Version = 3; // or 4 | |
fmt.NumStreams = 1; | |
fmt.RESERVED = 0; | |
fmt.LoopCount = 255; | |
fmt.LoopBegin = xma_be32(0); | |
fmt.LoopEnd = xma_be32(samples); | |
fmt.SampleRate = xma_be32(freq); | |
fmt.EncodeOptions = xma_be32(0x10d6); | |
fmt.PsuedoBytesPerSec = xma_be32(rawlen); // used only by the encoder | |
fmt.BlockSizeInBytes = xma_be32(0x10000); | |
fmt.SamplesEncoded = xma_be32(samples); | |
fmt.SamplesInSource = xma_be32(samples); | |
fmt.BlockCount = xma_be32(1); | |
fmt.Streams->Channels = chans; | |
fmt.Streams->RESERVED = 0; | |
fmt.Streams->ChannelMask = xma_be16(xma_quick_mask(chans)); | |
memcpy(chunk.id, "RIFF", 4); | |
chunk.size = | |
4 + sizeof(mywav_chunk) // RIFF | |
+ sizeof(fmt) // fmt | |
+ sizeof(mywav_chunk) + rawlen // data | |
+ sizeof(mywav_chunk) + seeklen; // seek | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(mywav_fwmem(fd, "WAVE", 4) < 0) return(-1); | |
memcpy(chunk.id, "XMA2", 4); | |
chunk.size = sizeof(fmt) + seeklen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
//if(mywav_fwfmtchunk(fd, &fmt) < 0) return(-1); | |
if(fwrite(&fmt, 1, sizeof(fmt), fd) != sizeof(fmt)) return(-1); | |
memcpy(chunk.id, "seek", 4); | |
chunk.size = seeklen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
if(fwrite(seek, 1, seeklen, fd) != seeklen) return(-1); | |
// data must be placed at the end so that the main tool can write the data after it | |
memcpy(chunk.id, "data", 4); | |
chunk.size = rawlen; | |
if(mywav_fwchunk(fd, &chunk) < 0) return(-1); | |
return(0); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment