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:
meep-eep
2002-10-14 23:55:51 +00:00
parent 427f29905b
commit 5389c45ca4
15 changed files with 1478 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
abx2raw: abx2raw.c abx2raw.h
gcc -W -Wall -g -O0 abx2raw.c -o abx2raw
clean:
rm abx2raw
+270
View File
@@ -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;
}
+27
View File
@@ -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.
+26
View File
@@ -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
+7
View File
@@ -0,0 +1,7 @@
aif2raw: aif2raw.c aif2raw.h
gcc -W -Wall -g -O0 aif2raw.c -o aif2raw
clean:
rm aif2raw
+273
View File
@@ -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);
}
+69
View File
@@ -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 */
};
+25
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
unduck: unduck.c unduck.h
gcc -W -Wall -g -O0 unduck.c -o unduck
clean:
rm unduck
+408
View File
@@ -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) */
+17
View File
@@ -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;
+34
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
unpkg: unpkg.c unpkg.h
gcc -W -Wall -g -O0 unpkg.c -o unpkg
clean:
rm unpkg
+241
View File
@@ -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;
}
+64
View File
@@ -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;