Skip to content

Instantly share code, notes, and snippets.

@sparr
Last active March 14, 2019 22:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sparr/0901e4150d62921ce5b051c875da7f69 to your computer and use it in GitHub Desktop.
Save sparr/0901e4150d62921ce5b051c875da7f69 to your computer and use it in GitHub Desktop.
unxwb
/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
/*
MyWAV 0.1.2
by Luigi Auriemma
e-mail: aluigi@autistici.org
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);
}
// 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()
/*
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);
}
}
/*
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: aluigi@autistici.org\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);
}
/*
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