Initial revision
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@117 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
abx2raw: abx2raw.c abx2raw.h
|
||||
gcc -W -Wall -g -O0 abx2raw.c -o abx2raw
|
||||
|
||||
clean:
|
||||
rm abx2raw
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* abx to raw converter. By Serge van den Boom (svdb@stack.nl),
|
||||
* The actual conversion code is from Toys for Bob.
|
||||
* So far, it ignores sample rates, so it will work ok as long as all
|
||||
* the frames have the same frequency. This is probably
|
||||
* enough for our purposes.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "abx2raw.h"
|
||||
|
||||
void convert_abx(FILE *in, FILE *out);
|
||||
uint8_t *UnCompressAudio(struct abx_header *abx, uint8_t *source);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
FILE *in, *out;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "abx2wav <infile> <outfile>\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
in = fopen(argv[1], "r");
|
||||
if (!in) {
|
||||
perror("Could not open input file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
out = fopen(argv[2], "w");
|
||||
if (!out) {
|
||||
perror("Could not open output file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
convert_abx(in, out);
|
||||
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static signed char trans[16*16] =
|
||||
{
|
||||
-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8, // Multiplier of 1
|
||||
-16,-14,-12,-10,-8,-6,-4,-2,2,4,6,8,10,12,14,16, // Multiplier of 2
|
||||
-24,-21,-18,-15,-12,-9,-6,-3,3,6,9,12,15,18,21,24, // Multiplier of 3
|
||||
-32,-28,-24,-20,-16,-12,-8,-4,4,8,12,16,20,24,28,32, // Multiplier of 4
|
||||
-40,-35,-30,-25,-20,-15,-10,-5,5,10,15,20,25,30,35,40, // Multiplier of 5
|
||||
-48,-42,-36,-30,-24,-18,-12,-6,6,12,18,24,30,36,42,48, // Multiplier of 6
|
||||
-56,-49,-42,-35,-28,-21,-14,-7,7,14,21,28,35,42,49,56, // Multiplier of 7
|
||||
-64,-56,-48,-40,-32,-24,-16,-8,8,16,24,32,40,48,56,64, // Multiplier of 8
|
||||
-72,-63,-54,-45,-36,-27,-18,-9,9,18,27,36,45,54,63,72, // Multiplier of 9
|
||||
-80,-70,-60,-50,-40,-30,-20,-10,10,20,30,40,50,60,70,80, // Multiplier of 10
|
||||
-88,-77,-66,-55,-44,-33,-22,-11,11,22,33,44,55,66,77,88, // Multiplier of 11
|
||||
-96,-84,-72,-60,-48,-36,-24,-12,12,24,36,48,60,72,84,96, // Multiplier of 12
|
||||
-104,-91,-78,-65,-52,-39,-26,-13,13,26,39,52,65,78,91,104, // Multiplier of 13
|
||||
-112,-98,-84,-70,-56,-42,-28,-14,14,28,42,56,70,84,98,112, // Multiplier of 14
|
||||
-120,-105,-90,-75,-60,-45,-30,-15,15,30,45,60,75,90,105,120,// Multiplier of 15
|
||||
-128,-112,-96,-80,-64,-48,-32,-16,16,32,48,64,80,96,112,127,// Multiplier of 16
|
||||
};
|
||||
|
||||
void
|
||||
read_data(char *buf, size_t size, FILE *file) {
|
||||
ssize_t numread;
|
||||
|
||||
numread = fread(buf, size, 1, file);
|
||||
if (numread == 0 && ferror(file)) {
|
||||
perror("read header");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((size_t) numread != 1) {
|
||||
fprintf(stderr, "Input file too small.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
convert_abx(FILE *in, FILE *out) {
|
||||
struct abx_header abx;
|
||||
struct frame_info *frame_info;
|
||||
uint8_t **frames;
|
||||
uint8_t **uncoded;
|
||||
int i;
|
||||
|
||||
read_data((uint8_t *) &abx, sizeof (struct abx_header), in);
|
||||
fprintf(stderr, "Base sample rate: %dHz\n", abx.freq);
|
||||
frame_info = malloc(sizeof (struct frame_info) * abx.num_frames);
|
||||
read_data((uint8_t *) frame_info,
|
||||
sizeof (struct frame_info) * abx.num_frames, in);
|
||||
frames = malloc(sizeof (uint8_t *) * abx.num_frames);
|
||||
uncoded = malloc(sizeof (uint8_t *) * abx.num_frames);
|
||||
for (i = 0; i < abx.num_frames; i++) {
|
||||
frames[i] = malloc(frame_info[i].fsize);
|
||||
read_data(frames[i], frame_info[i].fsize, in);
|
||||
|
||||
#if 0
|
||||
// debug output to locate the first bad frame
|
||||
fprintf(stderr, "Now going to process frame %d.\n", i);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// skip some corrupt frames
|
||||
if (i >= 270 && i <= 271) {
|
||||
// fill the bad part with zeros
|
||||
uncoded[i] = malloc(frame_info[i].usize);
|
||||
memset(uncoded[i], '\0', frame_info[i].usize);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
uncoded[i] = UnCompressAudio(&abx, frames[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < abx.num_frames; i++) {
|
||||
fwrite(uncoded[i], frame_info[i].usize, 1, out);
|
||||
}
|
||||
|
||||
for (i = 0; i < abx.num_frames; i++) {
|
||||
free(frames[i]);
|
||||
free(uncoded[i]);
|
||||
}
|
||||
free(uncoded);
|
||||
free(frames);
|
||||
free(frame_info);
|
||||
}
|
||||
|
||||
#define MAKE_WORD(byte1, byte2) ((byte2 << 8) | (byte1))
|
||||
|
||||
// This is used to make certain this C code is compatible when compiled on
|
||||
// a 68000 based machine. (Which it has been done and tested on.)
|
||||
#define Get8086word(t) MAKE_WORD ((t)[0], (t)[1])
|
||||
|
||||
// GetFreq will report the playback frequency of a particular ACOMP data
|
||||
// file.
|
||||
uint16_t
|
||||
GetFreq(uint8_t *sound) {
|
||||
return(Get8086word(sound + 2));
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
UnCompressAudio(struct abx_header *abx,
|
||||
uint8_t *source) {
|
||||
uint16_t slen, frame, freq;
|
||||
int16_t prev;
|
||||
uint8_t *result, *dest;
|
||||
|
||||
slen = Get8086word(source);
|
||||
dest = result = malloc(slen * sizeof (uint8_t));
|
||||
freq = GetFreq(source);
|
||||
if (freq == 0) {
|
||||
freq = abx->freq;
|
||||
} else if (freq != abx->freq) {
|
||||
fprintf(stderr, "Frame frequency (%d) != global frequency (%d).\n",
|
||||
freq, abx->freq);
|
||||
fprintf(stderr, "This is not supported. Output will be corrupted.\n");
|
||||
abx->freq = freq;
|
||||
}
|
||||
source += 4; // Skip length, and then frequency word.
|
||||
frame = *source++; // Frame size.
|
||||
source += 3; // Skip sqelch value, and maximum error allowed.
|
||||
prev = *source++; // Get initial previous data point.
|
||||
*dest++ = prev ^ 0x80;
|
||||
slen--; // Decrement total sound length.
|
||||
while (slen > 0)
|
||||
{
|
||||
uint16_t bytes;
|
||||
uint8_t sample;
|
||||
|
||||
sample = *source++; // Get sample.
|
||||
if (sample & RESYNC) // Is it a resync byte?
|
||||
{
|
||||
--slen; // Decrement output sample length.
|
||||
|
||||
prev = (sample & 0x7F) << 1; // Store resync byte.
|
||||
*dest++ = prev ^ 0x80;
|
||||
}
|
||||
else if (sample & SQLCH) // Is it a squelch byte?
|
||||
{
|
||||
bytes = sample & SQUELCHCNT; // And off the number of squelch bytes
|
||||
slen -= bytes; // Decrement total samples remaining count.
|
||||
|
||||
memset(dest, prev ^ 0x80, bytes);
|
||||
dest += bytes;
|
||||
}
|
||||
else // Must be a delta modulate byte!!
|
||||
{
|
||||
int8_t *base;
|
||||
|
||||
slen -= frame; // Pulling one frame out.
|
||||
// Compute base address to multiplier table.
|
||||
base = trans + (sample & MULTIPLIER) * 16;
|
||||
switch (sample & DELTAMOD) // Delta mod resolution.
|
||||
{
|
||||
case ONEBIT:
|
||||
{
|
||||
int16_t up;
|
||||
|
||||
up = base[8]; // Go up 1 bit.
|
||||
for (bytes = frame / 8; bytes; bytes--)
|
||||
{
|
||||
uint8_t mask;
|
||||
|
||||
sample = *source++;
|
||||
for(mask = 0x80; mask; mask >>= 1)
|
||||
{
|
||||
if ( sample & mask )
|
||||
prev += up;
|
||||
else
|
||||
prev -= up;
|
||||
if ( prev < 0 ) prev = 0;
|
||||
else if ( prev > 255 ) prev = 255;
|
||||
*dest++ = prev ^ 0x80;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TWOBIT:
|
||||
base+=6; // Base address of two bit delta's.
|
||||
for (bytes = frame / 4; bytes; bytes--)
|
||||
{
|
||||
sample = *source++;
|
||||
|
||||
prev += base[sample>>6];
|
||||
if ( prev < 0 ) prev = 0;
|
||||
else if ( prev > 255 ) prev = 255;
|
||||
*dest++ = prev ^ 0x80;
|
||||
|
||||
prev += base[(sample>>4)&0x3];
|
||||
if ( prev < 0 ) prev = 0;
|
||||
else if ( prev > 255 ) prev = 255;
|
||||
*dest++ = prev ^ 0x80;
|
||||
|
||||
prev += base[(sample>>2)&0x3];
|
||||
if ( prev < 0 ) prev = 0;
|
||||
else if ( prev > 255 ) prev = 255;
|
||||
*dest++ = prev ^ 0x80;
|
||||
|
||||
prev += base[sample&0x3];
|
||||
if ( prev < 0 ) prev = 0;
|
||||
else if ( prev > 255 ) prev = 255;
|
||||
*dest++ = prev ^ 0x80;
|
||||
}
|
||||
break;
|
||||
case FOURBIT:
|
||||
for (bytes = frame / 2; bytes; bytes--)
|
||||
{
|
||||
sample = *source++;
|
||||
|
||||
prev += base[sample>>4];
|
||||
if ( prev < 0 ) prev = 0;
|
||||
else if ( prev > 255 ) prev = 255;
|
||||
*dest++ = prev ^ 0x80;
|
||||
|
||||
prev += base[sample&0x0F];
|
||||
if ( prev < 0 ) prev = 0;
|
||||
else if ( prev > 255 ) prev = 255;
|
||||
*dest++ = prev ^ 0x80;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// While still audio data to decompress....
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <stdint.h>
|
||||
|
||||
struct abx_header {
|
||||
uint16_t num_frames;
|
||||
uint32_t tot_size __attribute__ ((packed));
|
||||
uint16_t bufsize __attribute__ ((packed));
|
||||
uint16_t freq __attribute__ ((packed));
|
||||
};
|
||||
|
||||
struct frame_info {
|
||||
uint32_t addr;
|
||||
uint16_t fsize; // compressed file size
|
||||
uint16_t usize; // uncompressed file size
|
||||
};
|
||||
|
||||
#define SQLCH 0x40 // Squelch byte flag
|
||||
#define RESYNC 0x80 // Resync byte flag.
|
||||
|
||||
#define DELTAMOD 0x30 // Delta modulation bits.
|
||||
|
||||
#define ONEBIT 0x10 // One bit delta modulate
|
||||
#define TWOBIT 0x20 // Two bit delta modulate
|
||||
#define FOURBIT 0x30 // four bit delta modulate
|
||||
|
||||
#define MULTIPLIER 0x0F // Bottom nibble contains multiplier value.
|
||||
#define SQUELCHCNT 0x3F // Bits for squelching.
|
||||
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
ABX2RAW="./abx2raw"
|
||||
SOX="sox"
|
||||
|
||||
echo "Converting all abx files in the current directory to wav files."
|
||||
echo "This script looks for abx2raw in the current dir. If it's somewhere"
|
||||
echo "else, edit it to point the variable ABX2RAW to the correct location."
|
||||
echo "The same goes for sox, which is expected somewhere in the path."
|
||||
echo "It's just supposed to work once on a specific set of files, and hence"
|
||||
echo "is pretty fragile."
|
||||
echo "It is assumed that all abx files have a sample rate of 11025."
|
||||
echo "If this is not the case, files won't be converted correctly."
|
||||
echo "The sample rate is reported, so you can see if it goes wrong."
|
||||
echo "It's also possible that the sample rate changes within one .abx file."
|
||||
|
||||
echo "Press ENTER when ready."
|
||||
read
|
||||
|
||||
for FILE in *.abx; do
|
||||
echo "File $FILE"
|
||||
BASE="${FILE%%.abx}"
|
||||
"$ABX2RAW" "$FILE" "${BASE}.raw"
|
||||
"$SOX" -c 1 -r 11025 -b -s "${BASE}.raw" "${BASE}.wav"
|
||||
echo
|
||||
done
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
aif2raw: aif2raw.c aif2raw.h
|
||||
gcc -W -Wall -g -O0 aif2raw.c -o aif2raw
|
||||
|
||||
clean:
|
||||
rm aif2raw
|
||||
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* aif to raw converter. By Serge van den Boom (svdb@stack.nl),
|
||||
* 20020816
|
||||
* Doesn't convert all .aif files in general, only AIFF-C, 16 bits
|
||||
* SDX2-compressed.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "aif2raw.h"
|
||||
|
||||
void convert_aif(FILE *in, FILE *out);
|
||||
void write_data(const uint8_t *data, ssize_t size, int numChannels,
|
||||
FILE *out);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
FILE *in, *out;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "aif2wav <infile> <outfile>\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
in = fopen(argv[1], "r");
|
||||
if (!in) {
|
||||
perror("Could not open input file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
out = fopen(argv[2], "w");
|
||||
if (!out) {
|
||||
perror("Could not open output file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
convert_aif(in, out);
|
||||
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* local to file byte order */
|
||||
inline uint32_t
|
||||
ltof32(uint32_t val) {
|
||||
return (val >> 24) |
|
||||
((val & 0x00ff0000L) >> 8) |
|
||||
((val & 0x0000ff00L) << 8) |
|
||||
(val << 24);
|
||||
}
|
||||
|
||||
/* file to local byte order */
|
||||
inline uint32_t
|
||||
ftol32(uint32_t val) {
|
||||
return (val >> 24) |
|
||||
((val & 0x00ff0000L) >> 8) |
|
||||
((val & 0x0000ff00L) << 8) |
|
||||
(val << 24);
|
||||
}
|
||||
|
||||
/* local to file byte order */
|
||||
inline uint16_t
|
||||
ltof16(uint16_t val) {
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
/* file to local byte order */
|
||||
inline uint16_t
|
||||
ftol16(uint16_t val) {
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* local to file byte order */
|
||||
inline void
|
||||
ltofld(char *dest, long double val) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* file to local byte order */
|
||||
inline long double
|
||||
ftolld(const char *val) {
|
||||
long double result;
|
||||
((char *) &result)[0] = val[9];
|
||||
((char *) &result)[1] = val[8];
|
||||
((char *) &result)[2] = val[7];
|
||||
((char *) &result)[3] = val[6];
|
||||
((char *) &result)[4] = val[5];
|
||||
((char *) &result)[5] = val[4];
|
||||
((char *) &result)[6] = val[3];
|
||||
((char *) &result)[7] = val[2];
|
||||
((char *) &result)[8] = val[1];
|
||||
((char *) &result)[9] = val[0];
|
||||
((char *) &result)[10] = 0x00;
|
||||
((char *) &result)[11] = 0x00;
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
read_data(char *buf, size_t size, FILE *file) {
|
||||
ssize_t numread;
|
||||
|
||||
numread = fread(buf, size, 1, file);
|
||||
if (numread == 0 && ferror(file)) {
|
||||
perror("read header");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((size_t) numread != 1) {
|
||||
fprintf(stderr, "Input file too small.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
struct aifc_Chunk *
|
||||
read_chunk(FILE *in) {
|
||||
struct aifc_Chunk *result;
|
||||
size_t numread;
|
||||
|
||||
result = malloc(sizeof (struct aifc_ChunkHeader));
|
||||
numread = fread(result, sizeof (struct aifc_ChunkHeader), 1, in);
|
||||
if (numread == 0) {
|
||||
free(result);
|
||||
if (ferror(in))
|
||||
fprintf(stderr, "Fread failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
result = realloc(result, sizeof (struct aifc_ChunkHeader) +
|
||||
ftol32(result->ckSize));
|
||||
numread = fread(&result->ckData, ftol32(result->ckSize), 1, in);
|
||||
if (numread == 0) {
|
||||
free(result);
|
||||
if (ferror(in))
|
||||
fprintf(stderr, "Fread failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* number to increase number of sections by if all preallocated
|
||||
sections are full */
|
||||
#define CHUNK_NUM_INC 5
|
||||
|
||||
struct aifc_Chunk **
|
||||
read_chunks(FILE *in) {
|
||||
struct aifc_Chunk **chunks;
|
||||
struct aifc_ContainerChunk *form;
|
||||
int maxchunks;
|
||||
int numchunks;
|
||||
|
||||
/* The FORM chunk contains all the other chunks. */
|
||||
form = malloc(sizeof (struct aifc_ContainerChunk));
|
||||
read_data((char *) form, sizeof (struct aifc_ContainerChunk), in);
|
||||
if (form->formType != aifc_FormTypeAIFC) {
|
||||
fprintf(stderr, "File is not an AIFF-C file.\n");
|
||||
free(form);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
free(form);
|
||||
|
||||
numchunks = 0;
|
||||
maxchunks = 0;
|
||||
chunks = NULL;
|
||||
while (1) {
|
||||
if (numchunks >= maxchunks) {
|
||||
maxchunks += CHUNK_NUM_INC;
|
||||
chunks = realloc(chunks,
|
||||
(maxchunks + 1) * sizeof (struct aifc_Chunk *));
|
||||
}
|
||||
chunks[numchunks] = read_chunk(in);
|
||||
if (chunks[numchunks] == NULL)
|
||||
break;
|
||||
numchunks++;
|
||||
}
|
||||
chunks = realloc(chunks,
|
||||
(numchunks + 1) * sizeof (struct aifc_Chunk *));
|
||||
return chunks;
|
||||
}
|
||||
|
||||
void
|
||||
free_chunks(struct aifc_Chunk **chunks) {
|
||||
int i;
|
||||
|
||||
for (i = 0; chunks[i] != NULL; i++)
|
||||
free(chunks[i]);
|
||||
free(chunks);
|
||||
}
|
||||
|
||||
void
|
||||
convert_aif(FILE *in, FILE *out) {
|
||||
struct aifc_Chunk **chunks;
|
||||
struct aifc_FormatVersionChunk *fverChunk;
|
||||
struct aifc_ExtCommonChunk *commChunk;
|
||||
struct aifc_SoundDataChunk *ssndChunk;
|
||||
int i;
|
||||
|
||||
fverChunk = NULL;
|
||||
commChunk = NULL;
|
||||
ssndChunk = NULL;
|
||||
chunks = read_chunks(in);
|
||||
for (i = 0; chunks[i] != NULL; i++) {
|
||||
if (chunks[i]->ckID == aifc_FormVersionID && fverChunk == NULL) {
|
||||
fverChunk = (struct aifc_FormatVersionChunk *) chunks[i];
|
||||
} else if (chunks[i]->ckID == aifc_CommonID && commChunk == NULL) {
|
||||
commChunk = (struct aifc_ExtCommonChunk *) chunks[i];
|
||||
} else if (chunks[i]->ckID == aifc_SoundDataID && ssndChunk == NULL) {
|
||||
ssndChunk = (struct aifc_SoundDataChunk *) chunks[i];
|
||||
}
|
||||
}
|
||||
if (fverChunk == NULL) {
|
||||
fprintf(stderr, "No format version chunk found.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (commChunk == NULL) {
|
||||
fprintf(stderr, "No common chunk found.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ssndChunk == NULL) {
|
||||
fprintf(stderr, "No sound data chunk found.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "Number of channels: %d\n",
|
||||
ftol16(commChunk->numChannels));
|
||||
fprintf(stderr, "Number of sample frames: %d\n",
|
||||
ftol32(commChunk->numSampleFrames));
|
||||
fprintf(stderr, "Number of bits per sample: %d\n",
|
||||
ftol16(commChunk->sampleSize));
|
||||
fprintf(stderr, "Sample rate: %.2LfHz\n",
|
||||
ftolld((char *) &commChunk->sampleRate));
|
||||
if (ftol16(commChunk->sampleSize) != 16) {
|
||||
fprintf(stderr, "Only 16-bits files are supported.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (commChunk->compressionType != aifc_CompressionTypeSDX2) {
|
||||
fprintf(stderr, "Only SDX2 compression is supported.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
write_data(ssndChunk->data + ftol32(ssndChunk->offset),
|
||||
ftol32(ssndChunk->ckSize) - ftol32(ssndChunk->offset),
|
||||
ftol16(commChunk->numChannels), out);
|
||||
free_chunks(chunks);
|
||||
}
|
||||
|
||||
void
|
||||
write_data(const uint8_t *data, ssize_t size, int numChannels, FILE *out) {
|
||||
const uint8_t *srcptr, *endptr;
|
||||
uint16_t *buffer, *dstptr;
|
||||
int i;
|
||||
int16_t *last;
|
||||
|
||||
endptr = data + size;
|
||||
srcptr = data;
|
||||
buffer = alloca(size * sizeof (uint16_t));
|
||||
dstptr = buffer;
|
||||
last = alloca(numChannels * sizeof(int16_t));
|
||||
memset(last, '\0', numChannels * sizeof(uint16_t));
|
||||
while (srcptr < endptr) {
|
||||
for (i = 0; i < numChannels; i++) {
|
||||
if (*srcptr & 1) {
|
||||
*dstptr = last[i] +
|
||||
((*(int8_t *)srcptr * abs(*(int8_t *)srcptr)) << 1);
|
||||
} else
|
||||
*dstptr = (*(int8_t *)srcptr * abs(*(int8_t *)srcptr)) << 1;
|
||||
last[i] = *dstptr;
|
||||
dstptr++;
|
||||
srcptr++;
|
||||
}
|
||||
}
|
||||
fwrite(buffer, size * sizeof (uint16_t), 1, out);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint32_t aifc_ID;
|
||||
typedef uint8_t aifc_Extended[10];
|
||||
/* This should actually be a 80-bits integer, but
|
||||
* long double takes 96 bits, even though only 80 are used */
|
||||
|
||||
#define aifc_MAKE_ID(x1, x2, x3, x4) \
|
||||
(((x4) << 24) | ((x3) << 16) | ((x2) << 8) | (x1))
|
||||
|
||||
#define aifc_FormID aifc_MAKE_ID('F', 'O', 'R', 'M')
|
||||
#define aifc_FormVersionID aifc_MAKE_ID('F', 'V', 'E', 'R')
|
||||
#define aifc_CommonID aifc_MAKE_ID('C', 'O', 'M', 'M')
|
||||
#define aifc_SoundDataID aifc_MAKE_ID('S', 'S', 'N', 'D')
|
||||
|
||||
#define aifc_FormTypeAIFF aifc_MAKE_ID('A', 'I', 'F', 'F')
|
||||
#define aifc_FormTypeAIFC aifc_MAKE_ID('A', 'I', 'F', 'C')
|
||||
|
||||
#define aifc_CompressionTypeSDX2 aifc_MAKE_ID('S', 'D', 'X', '2')
|
||||
|
||||
struct aifc_ChunkHeader {
|
||||
aifc_ID ckID;
|
||||
uint32_t ckSize;
|
||||
};
|
||||
|
||||
struct aifc_Chunk {
|
||||
aifc_ID ckID; /* Chunk ID */
|
||||
uint32_t ckSize; /* Chunk size, excluding header */
|
||||
uint8_t ckData[1]; /* placeholder for data */
|
||||
};
|
||||
|
||||
struct aifc_ContainerChunk {
|
||||
aifc_ID ckID; /* "FORM" */
|
||||
uint32_t ckSize; /* number of bytes of data */
|
||||
aifc_ID formType; /* type of file, "AIFC" */
|
||||
};
|
||||
|
||||
struct aifc_FormatVersionChunk {
|
||||
aifc_ID ckID; /* "FVER" */
|
||||
uint32_t ckSize; /* 4 */
|
||||
uint32_t timestamp; /* date of format version, in Mac format */
|
||||
};
|
||||
|
||||
|
||||
struct aifc_ExtCommonChunk {
|
||||
aifc_ID ckID; /* "COMM" */
|
||||
uint32_t ckSize; /* size of chunk data */
|
||||
uint16_t numChannels; /* number of channels */
|
||||
uint32_t numSampleFrames __attribute__ ((packed));
|
||||
/* number of sample frames */
|
||||
uint16_t sampleSize __attribute__ ((packed));
|
||||
/* number of bits per sample */
|
||||
aifc_Extended sampleRate __attribute__ ((packed));
|
||||
/* number of frames per second */
|
||||
aifc_ID compressionType __attribute__ ((packed));
|
||||
/* compression type ID */
|
||||
char *compressionName;
|
||||
/* compression type name */
|
||||
};
|
||||
|
||||
|
||||
struct aifc_SoundDataChunk {
|
||||
aifc_ID ckID; /* "SSND" */
|
||||
uint32_t ckSize; /* size of chunk data */
|
||||
uint32_t offset; /* offset to sound data */
|
||||
uint32_t blocksize; /* size of alignment blocks */
|
||||
uint8_t data[1]; /* placeholder for data */
|
||||
};
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
AIF2RAW="./aif2raw"
|
||||
SOX="sox"
|
||||
|
||||
echo "Converting all aif files in the current directory to wav files."
|
||||
echo "This script looks for aif2raw in the current dir. If it's somewhere"
|
||||
echo "else, edit it to point the variable AIF2RAW to the correct location."
|
||||
echo "The same goes for sox, which is expected somewhere in the path."
|
||||
echo "It's just supposed to work once on a specific set of files, and hence"
|
||||
echo "is pretty fragile."
|
||||
echo "It is assumed that all aif files have a sample rate of 11025."
|
||||
echo "If this is not the case, files won't be converted correctly."
|
||||
echo "The sample rate is reported, so you can see if it goes wrong."
|
||||
|
||||
echo "Press ENTER when ready."
|
||||
read
|
||||
|
||||
for FILE in *.aif; do
|
||||
echo "File $FILE"
|
||||
BASE="${FILE%%.aif}"
|
||||
"$AIF2RAW" "$FILE" "${BASE}.raw"
|
||||
"$SOX" -c 2 -r 44100 -w -s "${BASE}.raw" "${BASE}.wav"
|
||||
echo
|
||||
done
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
unduck: unduck.c unduck.h
|
||||
gcc -W -Wall -g -O0 unduck.c -o unduck
|
||||
|
||||
clean:
|
||||
rm unduck
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* duck .duk extractor.
|
||||
* By Serge van den Boom (svdb@stack.nl), 20020924
|
||||
* GPL applies
|
||||
*
|
||||
* duk audio is a ADPCM variant. Some code comes from MPlayer
|
||||
* (http://www.mplayerhq.hu/homepage/)
|
||||
*
|
||||
* For now only the audio is extracted. It's saved as 11050Hz raw audio.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "unduck.h"
|
||||
|
||||
#define AUDIO
|
||||
#undef VIDEO
|
||||
|
||||
void convert_duk(FILE *duk, FILE *frm, FILE *snd, FILE *dkm);
|
||||
#ifdef AUDIO
|
||||
void convert_audio(FILE *duk, FILE *snd, size_t cnt);
|
||||
#endif
|
||||
#ifdef VIDEO
|
||||
void convert_video(FILE *duk, FILE *dkm, size_t cnt);
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
FILE *duk, *frm;
|
||||
#ifdef AUDIO
|
||||
FILE *snd;
|
||||
#endif
|
||||
#ifdef VIDEO
|
||||
FILE *dkm;
|
||||
#endif
|
||||
char *filename, *filename2;
|
||||
size_t filenamelen;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "unduck <infile>\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
filename = argv[1];
|
||||
filenamelen = strlen(filename);
|
||||
if (filenamelen > 4 &&
|
||||
filename[filenamelen - 4] == '.' &&
|
||||
tolower(filename[filenamelen - 3]) == 'd' &&
|
||||
tolower(filename[filenamelen - 2]) == 'u' &&
|
||||
tolower(filename[filenamelen - 1]) == 'k') {
|
||||
filename[filenamelen - 4] = '\0';
|
||||
filenamelen -= 4;
|
||||
}
|
||||
|
||||
filename2 = alloca(filenamelen + 5);
|
||||
|
||||
sprintf(filename2, "%s.duk", filename);
|
||||
duk = fopen(filename2, "rb");
|
||||
if (!duk) {
|
||||
sprintf(filename2, "%s.DUK", filename);
|
||||
duk = fopen(filename2, "rb");
|
||||
}
|
||||
if (!duk) {
|
||||
perror("Could not open .duk input file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
sprintf(filename2, "%s.frm", filename);
|
||||
frm = fopen(filename2, "rb");
|
||||
if (!frm) {
|
||||
sprintf(filename2, "%s.FRM", filename);
|
||||
frm = fopen(filename2, "rb");
|
||||
}
|
||||
if (!frm) {
|
||||
perror("Could not open .frm input file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
sprintf(filename2, "%s.raw", filename);
|
||||
snd = fopen(filename2, "wb");
|
||||
if (!snd) {
|
||||
perror("Could not open .raw output file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VIDEO
|
||||
sprintf(filename2, "%s.dkm", filename);
|
||||
dkm = fopen(filename2, "wb");
|
||||
if (!dkm) {
|
||||
perror("Could not open .dkm output file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
convert_duk(duk, frm,
|
||||
#ifdef AUDIO
|
||||
snd,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef VIDEO
|
||||
dkm
|
||||
#else
|
||||
NULL
|
||||
#endif
|
||||
);
|
||||
|
||||
#ifdef VIDEO
|
||||
fclose(dkm);
|
||||
#endif
|
||||
#ifdef AUDIO
|
||||
fclose(snd);
|
||||
#endif
|
||||
fclose(frm);
|
||||
fclose(duk);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* local to file byte order */
|
||||
inline uint32_t
|
||||
ltof32(uint32_t val) {
|
||||
return (val >> 24) |
|
||||
((val & 0x00ff0000L) >> 8) |
|
||||
((val & 0x0000ff00L) << 8) |
|
||||
(val << 24);
|
||||
}
|
||||
|
||||
/* file to local byte order */
|
||||
inline uint32_t
|
||||
ftol32(uint32_t val) {
|
||||
return (val >> 24) |
|
||||
((val & 0x00ff0000L) >> 8) |
|
||||
((val & 0x0000ff00L) << 8) |
|
||||
(val << 24);
|
||||
}
|
||||
|
||||
/* local to file byte order */
|
||||
inline uint16_t
|
||||
ltof16(uint16_t val) {
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
/* file to local byte order */
|
||||
inline uint16_t
|
||||
ftol16(uint16_t val) {
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
int
|
||||
read_data(char *buf, size_t size, FILE *file) {
|
||||
ssize_t numread;
|
||||
|
||||
numread = fread(buf, size, 1, file);
|
||||
if (numread == 0 && ferror(file)) {
|
||||
perror("read header");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((size_t) numread != 1)
|
||||
return -1; // eof
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
convert_duk(FILE *duk, FILE *frm, FILE *snd, FILE *dkm) {
|
||||
uint32_t framepos;
|
||||
frame_header header;
|
||||
|
||||
while (1) {
|
||||
if (read_data((char *) &framepos, sizeof framepos, frm) == -1) {
|
||||
// eof
|
||||
break;
|
||||
}
|
||||
framepos = ftol32(framepos);
|
||||
|
||||
if (fseek(duk, framepos, SEEK_SET) == -1) {
|
||||
fprintf(stderr, "Cannot seek in .duk file to pos %lu",
|
||||
(unsigned long) framepos);
|
||||
perror("");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (read_data((char *) &header, sizeof (header), duk) == -1) {
|
||||
fprintf(stderr, "Input file too small.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
convert_audio(duk, snd, ftol32(header.sndcnt));
|
||||
#else
|
||||
fseek(duk, ftol32(header.sndcnt), SEEK_CUR);
|
||||
#endif
|
||||
fseek(duk, 16 + 2, SEEK_CUR);
|
||||
// 16 bytes to skip AvlBsh header (whatever that is)
|
||||
// 2 bytes for ??? (maybe they should be skipped *after*
|
||||
// the audio even)
|
||||
#ifdef VIDEO
|
||||
convert_video(duk, dkm, ftol32(header.vidcnt));
|
||||
#else
|
||||
fseek(duk, ftol32(header.vidcnt), SEEK_CUR);
|
||||
#endif
|
||||
}
|
||||
#ifndef AUDIO
|
||||
(void) snd;
|
||||
#endif
|
||||
#ifndef VIDEO
|
||||
(void) dkm;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This table is from one of the files that came with the original 3do source
|
||||
// It's slightly different from the data used by MPlayer.
|
||||
static int adpcm_step[89] = {
|
||||
0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xF,
|
||||
0x10, 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1C, 0x1F,
|
||||
0x22, 0x26, 0x29, 0x2E, 0x32, 0x37, 0x3D, 0x43,
|
||||
0x4A, 0x51, 0x59, 0x62, 0x6C, 0x76, 0x82, 0x8F,
|
||||
0x9E, 0xAD, 0xBF, 0xD2, 0xE7, 0xFE, 0x117, 0x133,
|
||||
0x152, 0x174, 0x199, 0x1C2, 0x1EF, 0x220, 0x256, 0x292,
|
||||
0x2D4, 0x31D, 0x36C, 0x3C4, 0x424, 0x48E, 0x503, 0x583,
|
||||
0x610, 0x6AC, 0x756, 0x812, 0x8E1, 0x9C4, 0xABE, 0xBD1,
|
||||
0xCFF, 0xE4C, 0xFBA, 0x114D, 0x1308, 0x14EF, 0x1707, 0x1954,
|
||||
0x1BDD, 0x1EA6, 0x21B7, 0x2516,
|
||||
0x28CB, 0x2CDF, 0x315C, 0x364C,
|
||||
0x3BBA, 0x41B2, 0x4844, 0x4F7E,
|
||||
0x5771, 0x6030, 0x69CE, 0x7463,
|
||||
0x7FFF
|
||||
};
|
||||
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
// *** BEGIN part copied from MPlayer ***
|
||||
// (some little changes)
|
||||
|
||||
#if 0
|
||||
// pertinent tables for IMA ADPCM
|
||||
static int adpcm_step[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
#endif
|
||||
|
||||
static int adpcm_index[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
// clamp a number between 0 and 88
|
||||
#define CLAMP_0_TO_88(x)if (x < 0) x = 0; else if (x > 88) x = 88;
|
||||
|
||||
// clamp a number within a signed 16-bit range
|
||||
#define CLAMP_S16(x) \
|
||||
if (x < -32768) \
|
||||
x = -32768; \
|
||||
else if (x > 32767) \
|
||||
x = 32767;
|
||||
|
||||
// sign extend a 16-bit value
|
||||
#define SE_16BIT(x) \
|
||||
if (x & 0x8000) \
|
||||
x -= 0x10000;
|
||||
|
||||
static void
|
||||
decode_nibbles(uint16_t *output,
|
||||
int output_size, int channels,
|
||||
int *predictor_l, int index_l,
|
||||
int *predictor_r, int index_r) {
|
||||
int step[2];
|
||||
int predictor[2] = {0, 0};
|
||||
int index[2];
|
||||
int diff;
|
||||
int i;
|
||||
int sign;
|
||||
int delta;
|
||||
int channel_number = 0;
|
||||
|
||||
step[0] = adpcm_step[index_l];
|
||||
step[1] = adpcm_step[index_r];
|
||||
predictor[0] = *predictor_l;
|
||||
predictor[1] = *predictor_r;
|
||||
index[0] = index_l;
|
||||
index[1] = index_r;
|
||||
|
||||
for (i = 0; i < output_size; i++)
|
||||
{
|
||||
delta = output[i];
|
||||
|
||||
index[channel_number] += adpcm_index[delta];
|
||||
CLAMP_0_TO_88(index[channel_number]);
|
||||
|
||||
sign = delta & 8;
|
||||
delta = delta & 7;
|
||||
|
||||
#if 0
|
||||
// fast approximation, used in most decoders
|
||||
diff = step[channel_number] >> 3;
|
||||
if (delta & 4) diff += step[channel_number];
|
||||
if (delta & 2) diff += step[channel_number] >> 1;
|
||||
if (delta & 1) diff += step[channel_number] >> 2;
|
||||
#else
|
||||
// real thing
|
||||
// diff = ((signed)delta + 0.5) * step[channel_number] / 4;
|
||||
diff = (((delta << 1) + 1) * step[channel_number]) >> 3;
|
||||
#endif
|
||||
|
||||
if (sign)
|
||||
predictor[channel_number] -= diff;
|
||||
else
|
||||
predictor[channel_number] += diff;
|
||||
|
||||
CLAMP_S16(predictor[channel_number]);
|
||||
output[i] = predictor[channel_number];
|
||||
step[channel_number] = adpcm_step[index[channel_number]];
|
||||
|
||||
// toggle channel
|
||||
channel_number ^= channels - 1;
|
||||
}
|
||||
*predictor_l = predictor[0];
|
||||
*predictor_r = predictor[1];
|
||||
}
|
||||
// *** END part copied from MPlayer ***
|
||||
|
||||
void
|
||||
convert_audio(FILE *duk, FILE *snd, size_t cnt) {
|
||||
static int predictorl = 0,
|
||||
predictorr = 0;
|
||||
// Important that they are static. The predictor value
|
||||
// of the previous call should be reused.
|
||||
int32_t indexl, indexr;
|
||||
uint16_t numsamples;
|
||||
uint8_t *input, *inptr, *inend;
|
||||
audio_chunk *header;
|
||||
uint16_t *output, *outptr;
|
||||
size_t outputsize;
|
||||
|
||||
input = alloca(cnt);
|
||||
if (read_data((char *) input, cnt, duk) == -1) {
|
||||
fprintf(stderr, "Input file too small.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
header = (audio_chunk *) input;
|
||||
|
||||
if (header->magic != 0x7ff7) {
|
||||
fprintf(stderr, "Error: Magic value 0xf77f not present, expected at "
|
||||
"location %lu.\n", ftell(duk) - cnt +
|
||||
((char *) &header->magic - (char *) header));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
numsamples = ftol16(header->numsamples);
|
||||
if (numsamples & 3) {
|
||||
fprintf(stderr, "Error: Number of samples not a multiple of 4.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((ssize_t) cnt - (size_t) ((char *) &header->data - (char *) header) !=
|
||||
numsamples) {
|
||||
fprintf(stderr, "cnt doesn't match number of samples.\n"
|
||||
"Number of samples: %d\n"
|
||||
"cnt: %d\n", numsamples,
|
||||
cnt - (size_t) ((char *) &header->data - (char *) header));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
outputsize = numsamples * 2 * sizeof (uint16_t);
|
||||
output = alloca(outputsize);
|
||||
outptr = output;
|
||||
indexl = ftol16(header->indexl);
|
||||
indexr = ftol16(header->indexr);
|
||||
|
||||
inptr = &header->data;
|
||||
inend = input + cnt;
|
||||
while (inptr < inend) {
|
||||
*(outptr++) = *inptr >> 4;
|
||||
*(outptr++) = *inptr & 0x0f;
|
||||
inptr++;
|
||||
}
|
||||
decode_nibbles(output, numsamples * 2,
|
||||
2, &predictorl, indexl, &predictorr, indexr);
|
||||
|
||||
fwrite(output, outputsize, 1, snd);
|
||||
}
|
||||
|
||||
#endif /* defined(AUDIO) */
|
||||
|
||||
|
||||
#ifdef VIDEO
|
||||
|
||||
void
|
||||
convert_video(FILE *duk, FILE *dkm, size_t cnt) {
|
||||
fseek(duk, cnt, SEEK_CUR);
|
||||
(void) duk;
|
||||
(void) dkm;
|
||||
(void) cnt;
|
||||
}
|
||||
|
||||
#endif /* defined(VIDEO) */
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t sndcnt;
|
||||
uint32_t vidcnt;
|
||||
} frame_header;
|
||||
|
||||
typedef struct {
|
||||
uint16_t magic; // always 0xf77f
|
||||
uint16_t numsamples;
|
||||
uint16_t tag; // this is NOT the initial predictor as it was
|
||||
// marked in some file.
|
||||
uint16_t indexl; // initial index for left channel
|
||||
uint16_t indexr; // initial index for right channel
|
||||
uint8_t data; // placeholder for data
|
||||
} audio_chunk;
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
UNDUCK="./unduck"
|
||||
SOX="sox"
|
||||
|
||||
echo "Extracting the sound data from all .duk files in the current directory."
|
||||
echo "This script looks for unduck in the current dir. If it's somewhere"
|
||||
echo "else, edit it to point the variable UNDUCK to the correct location."
|
||||
echo "The same goes for sox, which is expected somewhere in the path."
|
||||
echo "It is assumed that all sound files has a sample rate of 22050Hz"
|
||||
echo "and are stereo."
|
||||
echo "N.B. This program isn't fast. Make a cup of tea or something."
|
||||
|
||||
echo "Press ENTER when ready."
|
||||
read
|
||||
|
||||
for FILE in *.{duk,DUK}; do
|
||||
if [ ! -r "$FILE" ]; then
|
||||
continue
|
||||
fi
|
||||
echo "File $FILE"
|
||||
case "$FILE" in
|
||||
*.duk)
|
||||
BASE="${FILE%.duk}"
|
||||
;;
|
||||
*.DUK)
|
||||
BASE="${FILE%.DUK}"
|
||||
;;
|
||||
esac
|
||||
"$UNDUCK" "$FILE"
|
||||
"$SOX" -c 2 -r 22050 -w -s -t raw "${BASE}.raw" -t wav "${BASE}.wav"
|
||||
rm -- "${BASE}.raw"
|
||||
echo
|
||||
done
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
unpkg: unpkg.c unpkg.h
|
||||
gcc -W -Wall -g -O0 unpkg.c -o unpkg
|
||||
|
||||
clean:
|
||||
rm unpkg
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* .pkg file extractor
|
||||
* By Serge van den Boom (svdb@stack.nl), 20021012
|
||||
* GPL applies
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unpkg.h"
|
||||
|
||||
|
||||
void readIndex(const uint8 *buf);
|
||||
char *get_file_name(const uint8 *buf, const index_header *h, int package);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
char *filename;
|
||||
int in;
|
||||
struct stat sb;
|
||||
uint8 *buf;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "unpkg <infile>\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
filename = argv[1];
|
||||
|
||||
in = open(filename, O_RDONLY);
|
||||
if (in == -1) {
|
||||
perror("Could not open input file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (fstat(in, &sb) == -1) {
|
||||
perror("stat() failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
|
||||
if (buf == MAP_FAILED) {
|
||||
perror("mmap() failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
close(in);
|
||||
|
||||
readIndex(buf);
|
||||
|
||||
munmap(buf, sb.st_size);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
readIndex(const uint8 *buf) {
|
||||
index_header h;
|
||||
const uint8 *bufptr;
|
||||
|
||||
h.res_flags = MAKE_WORD(buf[0x00], buf[0x01]) ? 1 : 0;
|
||||
fprintf(stderr, "0x00000000 File type: %s\n",
|
||||
h.res_flags ? "packaged" : "not packaged");
|
||||
|
||||
h.packmem_list_offs =
|
||||
MAKE_DWORD(buf[0x02], buf[0x03], buf[0x04], buf[0x05]);
|
||||
fprintf(stderr, "0x00000002 packmem_list_offs: 0x%08x\n",
|
||||
h.packmem_list_offs);
|
||||
|
||||
h.path_list_offs =
|
||||
MAKE_DWORD(buf[0x06], buf[0x07], buf[0x08], buf[0x09]);
|
||||
fprintf(stderr, "0x00000006 path_list_offs: 0x%08x\n",
|
||||
h.path_list_offs);
|
||||
|
||||
h.file_list_offs =
|
||||
MAKE_DWORD(buf[0x0a], buf[0x0b], buf[0x0c], buf[0x0d]);
|
||||
fprintf(stderr, "0x0000000a file_list_offs: 0x%08x\n",
|
||||
h.file_list_offs);
|
||||
|
||||
h.num_packages = MAKE_WORD(buf[0x0e], buf[0x0f]);
|
||||
fprintf(stderr, "0x0000000e Number of packages: %d\n",
|
||||
h.num_packages);
|
||||
|
||||
h.num_types = MAKE_WORD(buf[0x10], buf[0x11]);
|
||||
fprintf(stderr, "0x00000010 Number of package types: %d\n",
|
||||
h.num_types);
|
||||
|
||||
h.header_len = MAKE_DWORD(buf[0x12], buf[0x13], buf[0x14], buf[0x15]);
|
||||
fprintf(stderr, "0x00000012 Header length: 0x%08x\n",
|
||||
h.header_len);
|
||||
|
||||
bufptr = buf + 0x16;
|
||||
h.package_list = malloc(h.num_packages * sizeof (package_desc));
|
||||
fprintf(stderr, "0x00000016 package info:\n");
|
||||
{
|
||||
int i;
|
||||
uint32 temp;
|
||||
|
||||
for (i = 0; i < h.num_packages; i++) {
|
||||
temp = MAKE_DWORD(bufptr[0], bufptr[1], bufptr[2], bufptr[3]);
|
||||
h.package_list[i].num_types = GET_TYPE(temp);
|
||||
h.package_list[i].num_instances = GET_INSTANCE(temp);
|
||||
h.package_list[i].file_index = GET_PACKAGE(temp);
|
||||
fprintf(stderr, "0x%08x #%d, %d types, %d instances, "
|
||||
"file index 0x%04x, ", bufptr - buf, i,
|
||||
h.package_list[i].num_types,
|
||||
h.package_list[i].num_instances,
|
||||
h.package_list[i].file_index);
|
||||
bufptr += 4;
|
||||
|
||||
temp = MAKE_DWORD(bufptr[0], bufptr[1], bufptr[2], bufptr[3]);
|
||||
h.package_list[i].flags = temp >> 24;
|
||||
h.package_list[i].data_loc = temp & 0x00ffffff;
|
||||
|
||||
if (h.package_list[i].flags != 0xff) {
|
||||
fprintf(stderr, "BAD OFFSET!\n");
|
||||
} else {
|
||||
fprintf(stderr, "offset 0x%06x\n",
|
||||
h.package_list[i].data_loc);
|
||||
}
|
||||
bufptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Type info:
|
||||
h.type_list = malloc(h.num_types * sizeof (type_desc));
|
||||
fprintf(stderr, "0x%08x type info:\n", bufptr - buf);
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < h.num_types; i++) {
|
||||
h.type_list[i].instance_count =
|
||||
MAKE_WORD(bufptr[0], bufptr[1]);
|
||||
fprintf(stderr, "0x%08x #%d, %d instances\n",
|
||||
bufptr - buf, i + 1, h.type_list[i].instance_count);
|
||||
bufptr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if ((uint32) (bufptr - buf) != h.packmem_list_offs) {
|
||||
fprintf(stderr, "PACKMEM_LIST NOT IMMEDIATELY AFTER TYPE LIST!\n");
|
||||
bufptr = buf + h.packmem_list_offs;
|
||||
}
|
||||
fprintf(stderr, "0x%08x packmem info:\n", bufptr - buf);
|
||||
{
|
||||
int i, j;
|
||||
int num_types, num_instances;
|
||||
uint32 temp;
|
||||
|
||||
for (i = 0; i < h.num_packages; i++) {
|
||||
if (h.res_flags) {
|
||||
// packaged
|
||||
char *filename;
|
||||
|
||||
filename = get_file_name(buf, &h,
|
||||
h.package_list[i].file_index);
|
||||
if (filename == NULL)
|
||||
filename = "<UNNAMED>";
|
||||
fprintf(stderr, "0x%08x Package #%d (%s):\n",
|
||||
bufptr - buf, i, filename);
|
||||
} else {
|
||||
// not packaged
|
||||
fprintf(stderr, "0x%08x Package #%d:\n", bufptr - buf, i);
|
||||
}
|
||||
num_types = h.package_list[i].num_types;
|
||||
for (j = 0; j < num_types; j++) {
|
||||
temp = MAKE_DWORD(bufptr[0], bufptr[1], bufptr[2], bufptr[3]);
|
||||
fprintf(stderr, "0x%08x Type #%d: type %d, "
|
||||
"%d instances, rest: %d\n",
|
||||
bufptr - buf, j,
|
||||
GET_TYPE(temp),
|
||||
GET_PACKAGE(temp), /* Yes, this is correct, the
|
||||
package field contains the number of instances */
|
||||
GET_INSTANCE(temp));
|
||||
bufptr += 4;
|
||||
}
|
||||
|
||||
num_instances = h.package_list[i].num_instances;
|
||||
for (j = 0; j < num_instances; j++) {
|
||||
if (h.res_flags) {
|
||||
// packaged
|
||||
fprintf(stderr, "0x%08x Instance #%d: size %d "
|
||||
"bytes\n", bufptr - buf, j,
|
||||
4 * MAKE_WORD(bufptr[0], bufptr[1]));
|
||||
} else {
|
||||
char *filename;
|
||||
|
||||
filename = get_file_name(buf, &h,
|
||||
MAKE_WORD(bufptr[0], bufptr[1]));
|
||||
if (filename == NULL)
|
||||
filename = "<UNNAMED>";
|
||||
fprintf(stderr, "0x%08x Instance #%d: %s\n",
|
||||
bufptr - buf, j, filename);
|
||||
}
|
||||
bufptr += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
get_file_name(const uint8 *buf, const index_header *h, int file_index) {
|
||||
static char result[MAXPATHLEN];
|
||||
char *ptr;
|
||||
file_info *fi;
|
||||
uint16 path_offset;
|
||||
|
||||
ptr = result;
|
||||
fi = &((file_info *) (buf + h->file_list_offs))[file_index];
|
||||
if (fi->filename[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
path_offset = MAKE_WORD(fi->path_offset[0], fi->path_offset[1]);
|
||||
if (path_offset != 0xffff) {
|
||||
strcpy(ptr, buf + h->path_list_offs + path_offset);
|
||||
ptr += strlen(ptr);
|
||||
*ptr = '/';
|
||||
ptr++;
|
||||
}
|
||||
|
||||
ptr[8] = '\0';
|
||||
strncpy(ptr, fi->filename, 8);
|
||||
ptr += strlen(ptr);
|
||||
|
||||
*ptr = '.';
|
||||
ptr++;
|
||||
|
||||
ptr[3] = '\0';
|
||||
strncpy(ptr, fi->extension, 3);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint32;
|
||||
|
||||
#define MAKE_WORD(x1, x2) (((x2) << 8) | (x1))
|
||||
#define MAKE_DWORD(x1, x2, x3, x4) (((x4) << 24) | ((x3) << 16) | \
|
||||
((x2) << 8) | (x1))
|
||||
|
||||
#define TYPE_BITS 8
|
||||
#define INSTANCE_BITS 13
|
||||
#define PACKAGE_BITS 11
|
||||
|
||||
#define GET_TYPE(res) \
|
||||
((uint8)(res) & (uint8)((1 << TYPE_BITS) - 1))
|
||||
#define GET_INSTANCE(res) \
|
||||
((uint16)((res) >> TYPE_BITS) & ((1 << INSTANCE_BITS) - 1))
|
||||
#define GET_PACKAGE(res) \
|
||||
((uint16)((res) >> (TYPE_BITS + INSTANCE_BITS)) & \
|
||||
((1 << PACKAGE_BITS) - 1))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16 instance_count;
|
||||
#if 0
|
||||
RES_VECTORS func_vectors;
|
||||
#endif
|
||||
} type_desc;
|
||||
|
||||
typedef struct {
|
||||
uint8 path_offset[2];
|
||||
char filename[8];
|
||||
char extension[3];
|
||||
} file_info;
|
||||
|
||||
typedef struct {
|
||||
uint8 num_types;
|
||||
uint16 num_instances; // only low 13 bits used
|
||||
uint16 file_index; // only low 11 bits used
|
||||
uint8 flags;
|
||||
uint32 data_loc; // only low 24 bits used
|
||||
} package_desc;
|
||||
|
||||
typedef struct {
|
||||
uint32 packmem_list_offs;
|
||||
uint32 path_list_offs;
|
||||
uint32 file_list_offs;
|
||||
uint16 num_packages;
|
||||
uint8 num_types;
|
||||
uint8 res_flags;
|
||||
uint16 header_len;
|
||||
package_desc *package_list;
|
||||
type_desc *type_list;
|
||||
|
||||
#if 0
|
||||
#ifndef PACKAGING
|
||||
char index_file_name[36];
|
||||
DWORD data_offs;
|
||||
MEM_HANDLE hPredHeader, hSuccHeader;
|
||||
#endif /* PACKAGING */
|
||||
#endif
|
||||
} index_header;
|
||||
|
||||
Reference in New Issue
Block a user