[Scummvm-cvs-logs] SF.net SVN: scummvm: [23136] tools/trunk
h00ligan at users.sourceforge.net
h00ligan at users.sourceforge.net
Thu Jun 15 16:37:56 CEST 2006
Revision: 23136
Author: h00ligan
Date: 2006-06-15 07:34:46 -0700 (Thu, 15 Jun 2006)
ViewCVS: http://svn.sourceforge.net/scummvm/?rev=23136&view=rev
Log Message:
-----------
compress SAGA WIP #2
Modified Paths:
--------------
tools/trunk/compress.c
tools/trunk/compress_saga.cpp
tools/trunk/util.c
tools/trunk/util.h
Added Paths:
-----------
tools/trunk/utils/
tools/trunk/utils/adpcm.cpp
tools/trunk/utils/adpcm.h
tools/trunk/utils/audiostream.cpp
tools/trunk/utils/audiostream.h
tools/trunk/utils/file.cpp
tools/trunk/utils/file.h
tools/trunk/utils/md5.cpp
tools/trunk/utils/md5.h
tools/trunk/utils/stream.h
tools/trunk/utils/util.h
tools/trunk/utils/voc.cpp
tools/trunk/utils/voc.h
tools/trunk/utils/wave.cpp
tools/trunk/utils/wave.h
Modified: tools/trunk/compress.c
===================================================================
--- tools/trunk/compress.c 2006-06-15 14:28:19 UTC (rev 23135)
+++ tools/trunk/compress.c 2006-06-15 14:34:46 UTC (rev 23136)
@@ -225,8 +225,6 @@
}
fclose(f);
- /* TODO: setRawAudioType(false, false, 8); */
-
/* Convert the WAV temp file to OGG/MP3 */
encodeAudio(outName, false, -1, tempEncoded, compMode);
}
Modified: tools/trunk/compress_saga.cpp
===================================================================
--- tools/trunk/compress_saga.cpp 2006-06-15 14:28:19 UTC (rev 23135)
+++ tools/trunk/compress_saga.cpp 2006-06-15 14:34:46 UTC (rev 23136)
@@ -24,312 +24,15 @@
#include <stdio.h>
#include "compress.h"
+#include "utils/md5.h"
+#include "utils/util.h"
+#include "utils/AudioStream.h"
+#include "utils/File.h"
+#include "utils/voc.h"
+#include "utils/wave.h"
+#include "utils/adpcm.h"
-namespace Common {
-// <!-- taken from common\util.h
-
-/**
- * List of game language.
- */
-enum Language {
- EN_ANY, // Generic English (when only one game version exist)
- EN_USA,
- EN_GRB,
-
- DE_DEU,
- FR_FRA,
- IT_ITA,
- PT_BRA,
- ES_ESP,
- JA_JPN,
- ZH_TWN,
- KO_KOR,
- SE_SWE,
- HB_ISR,
- RU_RUS,
- CZ_CZE,
- NL_NLD,
- NB_NOR,
- PL_POL,
-
- UNK_LANG = -1 // Use default language (i.e. none specified)
-};
-
-/**
- * List of game platforms. Specifying a platform for a target can be used to
- * give the game engines a hint for which platform the game data file are.
- * This may be optional or required, depending on the game engine and the
- * game in question.
- */
-enum Platform {
- kPlatformPC,
- kPlatformAmiga,
- kPlatformAtariST,
- kPlatformMacintosh,
- kPlatformFMTowns,
- kPlatformWindows,
- kPlatformNES,
- kPlatformC64,
- kPlatformLinux,
- kPlatformAcorn,
- kPlatformSegaCD,
- kPlatform3DO,
-// kPlatformPCEngine,
-
- kPlatformUnknown = -1
-};
-// taken from common\util.h -->
-
-// <!-- taken from common/md5.c
-
-typedef struct {
- uint32 total[2];
- uint32 state[4];
- uint8 buffer[64];
-} md5_context;
-
-#define GET_UINT32(n, b, i) (n) = READ_LE_UINT32(b + i)
-#define PUT_UINT32(n, b, i) WRITE_LE_UINT32(b + i, n)
-
-void md5_starts(md5_context *ctx) {
- ctx->total[0] = 0;
- ctx->total[1] = 0;
-
- ctx->state[0] = 0x67452301;
- ctx->state[1] = 0xEFCDAB89;
- ctx->state[2] = 0x98BADCFE;
- ctx->state[3] = 0x10325476;
-}
-
-static void md5_process(md5_context *ctx, const uint8 data[64]) {
- uint32 X[16], A, B, C, D;
-
- GET_UINT32(X[0], data, 0);
- GET_UINT32(X[1], data, 4);
- GET_UINT32(X[2], data, 8);
- GET_UINT32(X[3], data, 12);
- GET_UINT32(X[4], data, 16);
- GET_UINT32(X[5], data, 20);
- GET_UINT32(X[6], data, 24);
- GET_UINT32(X[7], data, 28);
- GET_UINT32(X[8], data, 32);
- GET_UINT32(X[9], data, 36);
- GET_UINT32(X[10], data, 40);
- GET_UINT32(X[11], data, 44);
- GET_UINT32(X[12], data, 48);
- GET_UINT32(X[13], data, 52);
- GET_UINT32(X[14], data, 56);
- GET_UINT32(X[15], data, 60);
-
-#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
-
-#define P(a, b, c, d, k, s, t) \
-{ \
- a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
-}
-
- A = ctx->state[0];
- B = ctx->state[1];
- C = ctx->state[2];
- D = ctx->state[3];
-
-#define F(x, y, z) (z ^ (x & (y ^ z)))
-
- P(A, B, C, D, 0, 7, 0xD76AA478);
- P(D, A, B, C, 1, 12, 0xE8C7B756);
- P(C, D, A, B, 2, 17, 0x242070DB);
- P(B, C, D, A, 3, 22, 0xC1BDCEEE);
- P(A, B, C, D, 4, 7, 0xF57C0FAF);
- P(D, A, B, C, 5, 12, 0x4787C62A);
- P(C, D, A, B, 6, 17, 0xA8304613);
- P(B, C, D, A, 7, 22, 0xFD469501);
- P(A, B, C, D, 8, 7, 0x698098D8);
- P(D, A, B, C, 9, 12, 0x8B44F7AF);
- P(C, D, A, B, 10, 17, 0xFFFF5BB1);
- P(B, C, D, A, 11, 22, 0x895CD7BE);
- P(A, B, C, D, 12, 7, 0x6B901122);
- P(D, A, B, C, 13, 12, 0xFD987193);
- P(C, D, A, B, 14, 17, 0xA679438E);
- P(B, C, D, A, 15, 22, 0x49B40821);
-
-#undef F
-
-#define F(x, y, z) (y ^ (z & (x ^ y)))
-
- P(A, B, C, D, 1, 5, 0xF61E2562);
- P(D, A, B, C, 6, 9, 0xC040B340);
- P(C, D, A, B, 11, 14, 0x265E5A51);
- P(B, C, D, A, 0, 20, 0xE9B6C7AA);
- P(A, B, C, D, 5, 5, 0xD62F105D);
- P(D, A, B, C, 10, 9, 0x02441453);
- P(C, D, A, B, 15, 14, 0xD8A1E681);
- P(B, C, D, A, 4, 20, 0xE7D3FBC8);
- P(A, B, C, D, 9, 5, 0x21E1CDE6);
- P(D, A, B, C, 14, 9, 0xC33707D6);
- P(C, D, A, B, 3, 14, 0xF4D50D87);
- P(B, C, D, A, 8, 20, 0x455A14ED);
- P(A, B, C, D, 13, 5, 0xA9E3E905);
- P(D, A, B, C, 2, 9, 0xFCEFA3F8);
- P(C, D, A, B, 7, 14, 0x676F02D9);
- P(B, C, D, A, 12, 20, 0x8D2A4C8A);
-
-#undef F
-
-#define F(x, y, z) (x ^ y ^ z)
-
- P(A, B, C, D, 5, 4, 0xFFFA3942);
- P(D, A, B, C, 8, 11, 0x8771F681);
- P(C, D, A, B, 11, 16, 0x6D9D6122);
- P(B, C, D, A, 14, 23, 0xFDE5380C);
- P(A, B, C, D, 1, 4, 0xA4BEEA44);
- P(D, A, B, C, 4, 11, 0x4BDECFA9);
- P(C, D, A, B, 7, 16, 0xF6BB4B60);
- P(B, C, D, A, 10, 23, 0xBEBFBC70);
- P(A, B, C, D, 13, 4, 0x289B7EC6);
- P(D, A, B, C, 0, 11, 0xEAA127FA);
- P(C, D, A, B, 3, 16, 0xD4EF3085);
- P(B, C, D, A, 6, 23, 0x04881D05);
- P(A, B, C, D, 9, 4, 0xD9D4D039);
- P(D, A, B, C, 12, 11, 0xE6DB99E5);
- P(C, D, A, B, 15, 16, 0x1FA27CF8);
- P(B, C, D, A, 2, 23, 0xC4AC5665);
-
-#undef F
-
-#define F(x, y, z) (y ^ (x | ~z))
-
- P(A, B, C, D, 0, 6, 0xF4292244);
- P(D, A, B, C, 7, 10, 0x432AFF97);
- P(C, D, A, B, 14, 15, 0xAB9423A7);
- P(B, C, D, A, 5, 21, 0xFC93A039);
- P(A, B, C, D, 12, 6, 0x655B59C3);
- P(D, A, B, C, 3, 10, 0x8F0CCC92);
- P(C, D, A, B, 10, 15, 0xFFEFF47D);
- P(B, C, D, A, 1, 21, 0x85845DD1);
- P(A, B, C, D, 8, 6, 0x6FA87E4F);
- P(D, A, B, C, 15, 10, 0xFE2CE6E0);
- P(C, D, A, B, 6, 15, 0xA3014314);
- P(B, C, D, A, 13, 21, 0x4E0811A1);
- P(A, B, C, D, 4, 6, 0xF7537E82);
- P(D, A, B, C, 11, 10, 0xBD3AF235);
- P(C, D, A, B, 2, 15, 0x2AD7D2BB);
- P(B, C, D, A, 9, 21, 0xEB86D391);
-
-#undef F
-
- ctx->state[0] += A;
- ctx->state[1] += B;
- ctx->state[2] += C;
- ctx->state[3] += D;
-}
-
-void md5_update(md5_context *ctx, const uint8 *input, uint32 length) {
- uint32 left, fill;
-
- if (!length)
- return;
-
- left = ctx->total[0] & 0x3F;
- fill = 64 - left;
-
- ctx->total[0] += length;
- ctx->total[0] &= 0xFFFFFFFF;
-
- if (ctx->total[0] < length)
- ctx->total[1]++;
-
- if (left && length >= fill) {
- memcpy((void *)(ctx->buffer + left), (const void *)input, fill);
- md5_process(ctx, ctx->buffer);
- length -= fill;
- input += fill;
- left = 0;
- }
-
- while (length >= 64) {
- md5_process(ctx, input);
- length -= 64;
- input += 64;
- }
-
- if (length) {
- memcpy((void *)(ctx->buffer + left), (const void *)input, length);
- }
-}
-
-static const uint8 md5_padding[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-void md5_finish(md5_context *ctx, uint8 digest[16]) {
- uint32 last, padn;
- uint32 high, low;
- uint8 msglen[8];
-
- high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
- low = (ctx->total[0] << 3);
-
- PUT_UINT32(low, msglen, 0);
- PUT_UINT32(high, msglen, 4);
-
- last = ctx->total[0] & 0x3F;
- padn = (last < 56) ? (56 - last) : (120 - last);
-
- md5_update(ctx, md5_padding, padn);
- md5_update(ctx, msglen, 8);
-
- PUT_UINT32(ctx->state[0], digest, 0);
- PUT_UINT32(ctx->state[1], digest, 4);
- PUT_UINT32(ctx->state[2], digest, 8);
- PUT_UINT32(ctx->state[3], digest, 12);
-}
-
-bool md5_file(const char *name, uint8 digest[16], uint32 length) {
- FILE *f;
-
- f = fopen(name, "rb");
- if (f == NULL) {
- printf("md5_file couldn't open '%s'", name);
- return false;
- }
-
- md5_context ctx;
- uint32 i;
- unsigned char buf[1000];
- bool restricted = (length != 0);
- int readlen;
-
- if (!restricted || sizeof(buf) <= length)
- readlen = sizeof(buf);
- else
- readlen = length;
-
- md5_starts(&ctx);
-
-
- while ((i = (uint32)fread(buf, 1, readlen, f)) > 0) {
- md5_update(&ctx, buf, i);
-
- length -= i;
- if (restricted && length == 0)
- break;
-
- if (restricted && sizeof(buf) > length)
- readlen = length;
- }
-
- md5_finish(&ctx, digest);
- fclose(f);
- return true;
-}
-
-}
-// taken from common/md5.c -->
-
#include "sagagame.h"
#include "sagaresnames.h"
#include "sagagame.cpp"
@@ -343,6 +46,7 @@
GameDescription *currentGameDescription = NULL;
int currentFileIndex = -1;
+bool isBigEndian = false;
bool detectFile(const char *inFileName) {
int gamesCount = ARRAYSIZE(gameDescriptions);
@@ -410,23 +114,120 @@
fclose(tempf);
}
+void writeBufferToFile(uint8* data, uint32 inputSize, const char* toFileName) {
+ FILE * tempf;
+
+ tempf = fopen(toFileName, "wb");
+ if (tempf == NULL)
+ error("unable to open %s\n", toFileName);
+ fwrite(data, 1, inputSize, tempf);
+ fclose(tempf);
+}
+
+#define HEADER_SIZE 9
+
+uint16 sampleRate;
+uint32 sampleSize;
+uint8 sampleBits;
+uint8 sampleStereo;
+
+void writeHeader(FILE* outputFile) {
+ writeByte(outputFile, gCompMode);
+ writeUint16LE(outputFile, sampleRate);
+ writeUint32LE(outputFile, sampleSize);
+ writeByte(outputFile, sampleBits);
+ writeByte(outputFile, sampleStereo);
+}
+/*
+case kSoundVOX:
+ buffer.frequency = soundInfo->frequency;
+ buffer.isSigned = soundInfo->isSigned;
+ buffer.sampleBits = soundInfo->sampleBits;
+ buffer.stereo = soundInfo->stereo;
+ buffer.size = soundResourceLength * 4;
+ if (onlyHeader) {
+ buffer.buffer = NULL;
+ free(soundResource);
+ } else {
+ voxStream = Audio::makeADPCMStream(&readS, soundResourceLength, Audio::kADPCMOki);
+ buffer.buffer = (byte *)malloc(buffer.size);
+ voxSize = voxStream->readBuffer((int16*)buffer.buffer, soundResourceLength * 2);
+ if (voxSize != soundResourceLength * 2) {
+ error("SndRes::load() wrong VOX output size");
+ }
+ delete voxStream;
+ }
+ result = true;
+ break;
+*/
uint32 encodeEntry(GameSoundInfo *soundInfo, FILE* inputFile, uint32 inputSize, FILE* outputFile) {
+ Audio::AudioStream *inStream;
+ uint8 *inputData;
+ Common::File inputFileStream(inputFile);
+ int rate, size;
+ byte flags;
+
+ if (soundInfo->resourceType == kSoundVOX) {
+ sampleSize = inputSize * 4;
+ sampleRate = (uint16)soundInfo->frequency;
+ sampleBits = soundInfo->sampleBits;
+ sampleStereo = soundInfo->stereo;
+ writeHeader(outputFile);
+ inStream = Audio::makeADPCMStream(&inputFileStream, inputSize, Audio::kADPCMOki);
+ inputData = (byte *)malloc(sampleSize);
+ inStream->readBuffer((int16*)inputData, inputSize * 2);
+ delete inStream;
+ writeBufferToFile(inputData, sampleSize, TEMP_RAW);
+ free(inputData);
+
+ setRawAudioType( true, sampleStereo != 0, sampleBits);
+ encodeAudio(TEMP_RAW, true, sampleRate, tempEncoded, gCompMode);
+ return copyFile(tempEncoded, outputFile) + HEADER_SIZE;
+ }
if (soundInfo->resourceType == kSoundVOC) {
- fseek(inputFile, 26, SEEK_CUR); //skip header, assume it's OK
- extractAndEncodeVOC(TEMP_RAW, inputFile, gCompMode);
- return copyFile(tempEncoded, outputFile);
+ inputData = Audio::loadVOCFromStream(inputFileStream, size, rate);
+ sampleSize = size;
+ sampleRate = rate;
+ sampleBits = 8;
+ sampleStereo = 0;
+ writeBufferToFile(inputData, sampleSize, TEMP_RAW);
+ free(inputData);
+ writeHeader(outputFile);
+
+ setRawAudioType( true, false, 8);
+ encodeAudio(TEMP_RAW, true, sampleRate, tempEncoded, gCompMode);
+ return copyFile(tempEncoded, outputFile) + HEADER_SIZE;
}
if (soundInfo->resourceType == kSoundPCM) {
copyFile(inputFile, inputSize, TEMP_RAW);
+ sampleSize = inputSize;
+ sampleRate = (uint16)soundInfo->frequency;
+ sampleBits = soundInfo->sampleBits;
+ sampleStereo = soundInfo->stereo;
+ writeHeader(outputFile);
- //BUG-BUG: bool(c++) -> bool(c) cause bug in al->eax conversion
setRawAudioType( !soundInfo->isBigEndian, soundInfo->stereo, soundInfo->sampleBits);
encodeAudio(TEMP_RAW, true, soundInfo->frequency, tempEncoded, gCompMode);
- return copyFile(tempEncoded, outputFile);
+ return copyFile(tempEncoded, outputFile) + HEADER_SIZE;
}
+ if (soundInfo->resourceType == kSoundWAV) {
+ if (!Audio::loadWAVFromStream(inputFileStream, size, rate, flags))
+ error("Unable to read WAV");
+ sampleSize = size;
+ sampleRate = rate;
+ sampleBits = ((flags & Audio::Mixer::FLAG_16BITS) != 0) ? 16 : 8;
+ sampleStereo = ((flags & Audio::Mixer::FLAG_STEREO) != 0);
+ writeHeader(outputFile);
+ copyFile(inputFile, size, TEMP_RAW);
+
+ setRawAudioType( true, sampleStereo != 0, sampleBits);
+ encodeAudio(TEMP_RAW, true, sampleRate, tempEncoded, gCompMode);
+ return copyFile(tempEncoded, outputFile) + HEADER_SIZE;
+ }
+
error("sorry - unsupported resourceType %ul\n", soundInfo->resourceType);
}
@@ -446,6 +247,14 @@
GameFileDescription *currentFileDescription;
GameSoundInfo *soundInfo;
+ currentFileDescription = ¤tGameDescription->filesDescriptions[currentFileIndex];
+
+ isBigEndian = ((currentGameDescription->features & GF_BIG_ENDIAN_DATA) != 0);
+
+ if (currentFileDescription->fileType & GAME_SWAPENDIAN)
+ isBigEndian = !isBigEndian;
+ ///isBigEndian = false;
+
inputFile = fopen(inputFileName, "rb");
inputFileSize = fileSize(inputFile);
printf("filesize: %ul\n", inputFileSize);
@@ -456,8 +265,13 @@
*/
fseek(inputFile, inputFileSize - RSC_TABLEINFO_SIZE, SEEK_SET);
- resTableOffset = readUint32LE(inputFile);
- resTableCount = readUint32LE(inputFile);
+ if (!isBigEndian) {
+ resTableOffset = readUint32LE(inputFile);
+ resTableCount = readUint32LE(inputFile);
+ } else {
+ resTableOffset = readUint32BE(inputFile);
+ resTableCount = readUint32BE(inputFile);
+ }
printf("table offset: %ul\nnumber of records: %ul\n", resTableOffset, resTableCount);
if (resTableOffset != inputFileSize - RSC_TABLEINFO_SIZE - RSC_TABLEENTRY_SIZE * resTableCount) {
@@ -472,8 +286,13 @@
// Put offsets of all the records in a table
for (i = 0; i < resTableCount; i++) {
+ if (!isBigEndian) {
inputTable[i].offset = readUint32LE(inputFile);
inputTable[i].size = readUint32LE(inputFile);
+ } else {
+ inputTable[i].offset = readUint32BE(inputFile);
+ inputTable[i].size = readUint32BE(inputFile);
+ }
printf("record: %ul, offset: %ul, size: %ul\n", i, inputTable[i].offset, inputTable[i].size);
@@ -483,7 +302,6 @@
}
}
- currentFileDescription = ¤tGameDescription->filesDescriptions[currentFileIndex];
outputTable = (Record*)malloc(resTableCount * sizeof(Record));
outputFile = fopen("out.res", "wb");
Modified: tools/trunk/util.c
===================================================================
--- tools/trunk/util.c 2006-06-15 14:28:19 UTC (rev 23135)
+++ tools/trunk/util.c 2006-06-15 14:34:46 UTC (rev 23136)
@@ -51,6 +51,17 @@
fprintf(stderr, "WARNING: %s!\n", buf);
}
+void debug(int level, const char *s, ...) {
+ char buf[1024];
+ va_list va;
+
+ va_start(va, s);
+ vsnprintf(buf, 1024, s, va);
+ va_end(va);
+
+ fprintf(stderr, "DEBUG: %s!\n", buf);
+}
+
uint8 readByte(FILE *fp) {
return fgetc(fp);
}
Modified: tools/trunk/util.h
===================================================================
--- tools/trunk/util.h 2006-06-15 14:28:19 UTC (rev 23135)
+++ tools/trunk/util.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -53,7 +53,9 @@
typedef signed int int32;
#if !defined(__cplusplus)
-typedef enum { false = 0, true = 1 } bool;
+typedef uint8 bool;
+#define false 0
+#define true 1
/* If your C compiler doesn't support 'inline', please add a check for it. */
#if defined(_MSC_VER)
@@ -67,6 +69,82 @@
* Various utility macros
*/
+#if defined(_MSC_VER)
+
+ #define scumm_stricmp stricmp
+ #define scumm_strnicmp _strnicmp
+ #define snprintf _snprintf
+
+ #define SCUMM_LITTLE_ENDIAN
+
+ #define START_PACK_STRUCTS pack(push, 1)
+ #define END_PACK_STRUCTS pack(pop)
+
+
+#elif defined(__MINGW32__)
+
+ #define scumm_stricmp stricmp
+ #define scumm_strnicmp strnicmp
+
+ #define SCUMM_LITTLE_ENDIAN
+
+ #define START_PACK_STRUCTS pack(push, 1)
+ #define END_PACK_STRUCTS pack(pop)
+
+
+ #ifndef _HEAPOK
+ #define _HEAPOK (-2)
+ #endif
+
+#elif defined(UNIX)
+
+ #define scumm_stricmp strcasecmp
+ #define scumm_strnicmp strncasecmp
+
+ #if defined(__DECCXX) // Assume alpha architecture
+ #define INVERSE_MKID
+ #define SCUMM_NEED_ALIGNMENT
+ #endif
+
+ #if !defined(__GNUC__)
+ #define START_PACK_STRUCTS pack (1)
+ #define END_PACK_STRUCTS pack ()
+ #endif
+
+#else
+
+ #error No system type defined
+
+#endif
+
+
+//
+// GCC specific stuff
+//
+#if defined(__GNUC__)
+ #define GCC_PACK __attribute__((packed))
+ #define NORETURN __attribute__((__noreturn__))
+ #define GCC_PRINTF(x,y) __attribute__((format(printf, x, y)))
+#else
+ #define GCC_PACK
+ #define GCC_PRINTF(x,y)
+#endif
+
+#define READ_UINT16(a) READ_LE_UINT16(a)
+#define READ_UINT32(a) READ_LE_UINT32(a)
+
+#define WRITE_UINT16(a, v) WRITE_LE_UINT16(a, v)
+#define WRITE_UINT32(a, v) WRITE_LE_UINT32(a, v)
+
+#define FROM_LE_32(a) ((uint32)(a))
+#define FROM_LE_16(a) ((uint16)(a))
+
+#define TO_LE_32(a) ((uint32)(a))
+#define TO_LE_16(a) ((uint16)(a))
+
+#define TO_BE_32(a) SWAP_BYTES_32(a)
+#define TO_BE_16(a) SWAP_BYTES_16(a)
+
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
static inline uint32 SWAP_32(uint32 a) {
@@ -154,6 +232,7 @@
/* Misc stuff */
void NORETURN_PRE error(const char *s, ...) NORETURN_POST;
void warning(const char *s, ...);
+void debug(int level, const char *s, ...);
#if defined(__cplusplus)
}
Added: tools/trunk/utils/adpcm.cpp
===================================================================
--- tools/trunk/utils/adpcm.cpp (rev 0)
+++ tools/trunk/utils/adpcm.cpp 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,332 @@
+#include "adpcm.h"
+#include "util.h"
+
+namespace Audio {
+
+// TODO: Switch from a SeekableReadStream to a plain ReadStream. This requires
+// some internal refactoring but is definitely possible and will increase the
+// flexibility of this code.
+class ADPCMInputStream : public AudioStream {
+private:
+ Common::SeekableReadStream *_stream;
+ uint32 _endpos;
+ int _channels;
+ typesADPCM _type;
+ uint32 _blockAlign;
+ uint32 _blockPos;
+ int _blockLen;
+ int _rate;
+
+ struct ADPCMChannelStatus {
+ byte predictor;
+ int16 delta;
+ int16 coeff1;
+ int16 coeff2;
+ int16 sample1;
+ int16 sample2;
+ };
+
+ struct adpcmStatus {
+ // IMA
+ int32 last;
+ int32 stepIndex;
+
+ // MS ADPCM
+ ADPCMChannelStatus ch[2];
+ } _status;
+
+ int16 stepAdjust(byte);
+ int16 decodeOKI(byte);
+ int16 decodeMSIMA(byte);
+ int16 decodeMS(ADPCMChannelStatus *c, byte);
+
+public:
+ ADPCMInputStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate, int channels = 2, uint32 blockAlign = 0);
+ ~ADPCMInputStream() {};
+
+ int readBuffer(int16 *buffer, const int numSamples);
+ int readBufferOKI(int16 *buffer, const int numSamples);
+ int readBufferMSIMA1(int16 *buffer, const int numSamples);
+ int readBufferMSIMA2(int16 *buffer, const int numSamples);
+ int readBufferMS(int channels, int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); }
+ bool isStereo() const { return false; }
+ int getRate() const { return _rate; }
+};
+
+// Routines to convert 12 bit linear samples to the
+// Dialogic or Oki ADPCM coding format aka VOX.
+// See also <http://www.comptek.ru/telephony/tnotes/tt1-13.html>
+//
+// In addition, also MS IMA ADPCM is supported. See
+// <http://wiki.multimedia.cx/index.php?title=Microsoft_IMA_ADPCM>.
+
+ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign)
+ : _stream(stream), _channels(channels), _type(type), _blockAlign(blockAlign), _rate(rate) {
+
+ _status.last = 0;
+ _status.stepIndex = 0;
+ memset(_status.ch, 0, sizeof(_status.ch));
+ _endpos = stream->pos() + size;
+ _blockPos = _blockLen = 0;
+
+ if (type == kADPCMMSIma && blockAlign == 0)
+ error("ADPCMInputStream(): blockAlign isn't specifiled for MS IMA ADPCM");
+ if (type == kADPCMMS && blockAlign == 0)
+ error("ADPCMInputStream(): blockAlign isn't specifiled for MS ADPCM");
+}
+
+int ADPCMInputStream::readBuffer(int16 *buffer, const int numSamples) {
+ switch (_type) {
+ case kADPCMOki:
+ return readBufferOKI(buffer, numSamples);
+ break;
+ case kADPCMMSIma:
+ if (_channels == 1)
+ return readBufferMSIMA1(buffer, numSamples);
+ else
+ return readBufferMSIMA2(buffer, numSamples);
+ break;
+ case kADPCMMS:
+ return readBufferMS(_channels, buffer, numSamples);
+ break;
+ default:
+ error("Unsupported ADPCM encoding");
+ break;
+ }
+ return 0;
+}
+
+int ADPCMInputStream::readBufferOKI(int16 *buffer, const int numSamples) {
+ int samples;
+ byte data;
+
+ assert(numSamples % 2 == 0);
+
+ for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+ data = _stream->readByte();
+ buffer[samples] = TO_LE_16(decodeOKI((data >> 4) & 0x0f));
+ buffer[samples + 1] = TO_LE_16(decodeOKI(data & 0x0f));
+ }
+ return samples;
+}
+
+
+int ADPCMInputStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
+ int samples;
+ byte data;
+
+ assert(numSamples % 2 == 0);
+
+ samples = 0;
+
+ while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+ if (_blockPos == _blockAlign) {
+ // read block header
+ _status.last = _stream->readSint16LE();
+ _status.stepIndex = _stream->readSint16LE();
+ _blockPos = 4;
+ }
+
+ for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+ data = _stream->readByte();
+ _blockPos++;
+ buffer[samples] = TO_LE_16(decodeMSIMA(data & 0x0f));
+ buffer[samples + 1] = TO_LE_16(decodeMSIMA((data >> 4) & 0x0f));
+ }
+ }
+ return samples;
+}
+
+
+// Microsoft as usual tries to implement it differently. This method
+// is used for stereo data.
+int ADPCMInputStream::readBufferMSIMA2(int16 *buffer, const int numSamples) {
+ int samples;
+ uint32 data;
+ int nibble;
+
+ for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos;) {
+ for (int channel = 0; channel < 2; channel++) {
+ data = _stream->readUint32LE();
+
+ for (nibble = 0; nibble < 8; nibble++) {
+ byte k = ((data & 0xf0000000) >> 28);
+ buffer[samples + channel + nibble * 2] = TO_LE_16(decodeMSIMA(k));
+ data <<= 4;
+ }
+ }
+ samples += 16;
+ }
+ return samples;
+}
+
+static const int MSADPCMAdaptCoeff1[] = {
+ 256, 512, 0, 192, 240, 460, 392
+};
+
+static const int MSADPCMAdaptCoeff2[] = {
+ 0, -256, 0, 64, 0, -208, -232
+};
+
+int ADPCMInputStream::readBufferMS(int channels, int16 *buffer, const int numSamples) {
+ int samples;
+ byte data;
+ int stereo = channels - 1; // We use it in index
+
+ samples = 0;
+
+ while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+ if (_blockPos == _blockAlign) {
+ // read block header
+ _status.ch[0].predictor = Common::CLIP(_stream->readByte(), (byte)0, (byte)6);
+ _status.ch[0].coeff1 = MSADPCMAdaptCoeff1[_status.ch[0].predictor];
+ _status.ch[0].coeff2 = MSADPCMAdaptCoeff2[_status.ch[0].predictor];
+ if (stereo) {
+ _status.ch[1].predictor = Common::CLIP(_stream->readByte(), (byte)0, (byte)6);
+ _status.ch[1].coeff1 = MSADPCMAdaptCoeff1[_status.ch[1].predictor];
+ _status.ch[1].coeff2 = MSADPCMAdaptCoeff2[_status.ch[1].predictor];
+ }
+
+ _status.ch[0].delta = _stream->readSint16LE();
+ if (stereo)
+ _status.ch[1].delta = _stream->readSint16LE();
+
+ buffer[samples++] = _status.ch[0].sample1 = _stream->readSint16LE();
+ if (stereo)
+ buffer[samples++] = _status.ch[1].sample1 = _stream->readSint16LE();
+
+ buffer[samples++] = _status.ch[0].sample2 = _stream->readSint16LE();
+ if (stereo)
+ buffer[samples++] = _status.ch[1].sample2 = _stream->readSint16LE();
+
+ _blockPos = channels * 7;
+ }
+
+
+ for (; samples < numSamples && _blockPos < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+ data = _stream->readByte();
+ _blockPos++;
+ buffer[samples] = TO_LE_16(decodeMS(&_status.ch[0], (data >> 4) & 0x0f));
+ buffer[samples + 1] = TO_LE_16(decodeMS(&_status.ch[stereo], data & 0x0f));
+ }
+ }
+
+ return samples;
+}
+
+
+// adjust the step for use on the next sample.
+int16 ADPCMInputStream::stepAdjust(byte code) {
+ static const int16 adjusts[] = {-1, -1, -1, -1, 2, 4, 6, 8};
+
+ return adjusts[code & 0x07];
+}
+
+static const int16 okiStepSize[49] = {
+ 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
+};
+
+// Decode Linear to ADPCM
+int16 ADPCMInputStream::decodeOKI(byte code) {
+ int16 diff, E, samp;
+
+ E = (2 * (code & 0x7) + 1) * okiStepSize[_status.stepIndex] / 8;
+ diff = (code & 0x08) ? -E : E;
+ samp = _status.last + diff;
+
+ // Clip the values to +/- 2^11 (supposed to be 12 bits)
+ if (samp > 2048)
+ samp = 2048;
+ if (samp < -2048)
+ samp = -2048;
+
+ _status.last = samp;
+ _status.stepIndex += stepAdjust(code);
+ if (_status.stepIndex < 0)
+ _status.stepIndex = 0;
+ if (_status.stepIndex > ARRAYSIZE(okiStepSize) - 1)
+ _status.stepIndex = ARRAYSIZE(okiStepSize) - 1;
+
+ // * 16 effectively converts 12-bit input to 16-bit output
+ return samp * 16;
+}
+
+
+static const uint16 imaStepTable[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
+};
+
+int16 ADPCMInputStream::decodeMSIMA(byte code) {
+ int32 diff, E, samp;
+
+ E = (2 * (code & 0x7) + 1) * imaStepTable[_status.stepIndex] / 8;
+ diff = (code & 0x08) ? -E : E;
+ samp = _status.last + diff;
+
+ if (samp < -0x8000)
+ samp = -0x8000;
+ else if (samp > 0x7fff)
+ samp = 0x7fff;
+
+ _status.last = samp;
+
+ _status.stepIndex += stepAdjust(code);
+ if (_status.stepIndex < 0)
+ _status.stepIndex = 0;
+ if (_status.stepIndex > ARRAYSIZE(imaStepTable) - 1)
+ _status.stepIndex = ARRAYSIZE(imaStepTable) - 1;
+
+ return samp;
+}
+
+static const int MSADPCMAdaptationTable[] = {
+ 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230
+};
+
+
+int16 ADPCMInputStream::decodeMS(ADPCMChannelStatus *c, byte code) {
+ int32 predictor;
+
+ predictor = (((c->sample1) * (c->coeff1)) + ((c->sample2) * (c->coeff2))) / 256;
+ predictor += (signed)((code & 0x08) ? (code - 0x10) : (code)) * c->delta;
+
+ if (predictor < -0x8000)
+ predictor = -0x8000;
+ else if (predictor > 0x7fff)
+ predictor = 0x7fff;
+
+ c->sample2 = c->sample1;
+ c->sample1 = predictor;
+ c->delta = (MSADPCMAdaptationTable[(int)code] * c->delta) >> 8;
+
+ if (c->delta < 16)
+ c->delta = 16;
+
+ return (int16)predictor;
+}
+
+AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign) {
+ return new ADPCMInputStream(stream, size, type, rate, channels, blockAlign);
+}
+
+} // End of namespace Audio
Property changes on: tools/trunk/utils/adpcm.cpp
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/adpcm.h
===================================================================
--- tools/trunk/utils/adpcm.h (rev 0)
+++ tools/trunk/utils/adpcm.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,21 @@
+#ifndef SOUND_ADPCM_H
+#define SOUND_ADPCM_H
+
+#include "audiostream.h"
+#include "stream.h"
+
+namespace Audio {
+
+class AudioStream;
+
+enum typesADPCM {
+ kADPCMOki,
+ kADPCMMSIma,
+ kADPCMMS
+};
+
+AudioStream *makeADPCMStream(Common::SeekableReadStream *stream, uint32 size, typesADPCM type, int rate = 22050, int channels = 2, uint32 blockAlign = 0);
+
+} // End of namespace Audio
+
+#endif
Property changes on: tools/trunk/utils/adpcm.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/audiostream.cpp
===================================================================
--- tools/trunk/utils/audiostream.cpp (rev 0)
+++ tools/trunk/utils/audiostream.cpp 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,140 @@
+#include "audiostream.h"
+
+
+namespace Audio {
+
+struct StreamFileFormat {
+ /** Decodername */
+ const char* decoderName;
+ const char* fileExtension;
+ /**
+ * Pointer to a function which tries to open a file of type StreamFormat.
+ * Return NULL in case of an error (invalid/nonexisting file).
+ */
+ AudioStream* (*openStreamFile)(FILE *file, uint32 size);
+};
+
+
+#pragma mark -
+#pragma mark --- LinearMemoryStream ---
+#pragma mark -
+
+
+/**
+ * A simple raw audio stream, purely memory based. It operates on a single
+ * block of data, which is passed to it upon creation.
+ * Optionally supports looping the sound.
+ *
+ * Design note: This code tries to be as optimized as possible (without
+ * resorting to assembly, that is). To this end, it is written as a template
+ * class. This way the compiler can actually create optimized code for each
+ * special code. This results in a total of 12 versions of the code being
+ * generated.
+ */
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+class LinearMemoryStream : public AudioStream {
+protected:
+ const byte *_ptr;
+ const byte *_end;
+ const byte *_loopPtr;
+ const byte *_loopEnd;
+ const int _rate;
+ const byte *_origPtr;
+
+ inline bool eosIntern() const { return _ptr >= _end; };
+public:
+ LinearMemoryStream(int rate, const byte *ptr, uint32 len, uint32 loopOffset, uint32 loopLen, bool autoFreeMemory)
+ : _ptr(ptr), _end(ptr+len), _loopPtr(0), _loopEnd(0), _rate(rate) {
+
+ // Verify the buffer sizes are sane
+ if (is16Bit && stereo)
+ assert((len & 3) == 0 && (loopLen & 3) == 0);
+ else if (is16Bit || stereo)
+ assert((len & 1) == 0 && (loopLen & 1) == 0);
+
+ if (loopLen) {
+ _loopPtr = _ptr + loopOffset;
+ _loopEnd = _loopPtr + loopLen;
+ }
+ if (stereo) // Stereo requires even sized data
+ assert(len % 2 == 0);
+
+ _origPtr = autoFreeMemory ? ptr : 0;
+ }
+ ~LinearMemoryStream() {
+ free(const_cast<byte *>(_origPtr));
+ }
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const { return stereo; }
+ bool endOfData() const { return eosIntern(); }
+
+ int getRate() const { return _rate; }
+};
+
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+int LinearMemoryStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+ while (samples < numSamples && !eosIntern()) {
+ const int len = Common::MIN(numSamples, samples + (int)(_end - _ptr) / (is16Bit ? 2 : 1));
+ while (samples < len) {
+ *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE);
+ _ptr += (is16Bit ? 2 : 1);
+ samples++;
+ }
+ // Loop, if looping was specified
+ if (_loopPtr && eosIntern()) {
+ _ptr = _loopPtr;
+ _end = _loopEnd;
+ }
+ }
+ return samples;
+}
+
+
+#pragma mark -
+#pragma mark --- Input stream factory ---
+#pragma mark -
+
+/* In the following, we use preprocessor / macro tricks to simplify the code
+ * which instantiates the input streams. We used to use template functions for
+ * this, but MSVC6 / EVC 3-4 (used for WinCE builds) are extremely buggy when it
+ * comes to this feature of C++... so as a compromise we use macros to cut down
+ * on the (source) code duplication a bit.
+ * So while normally macro tricks are said to make maintenance harder, in this
+ * particular case it should actually help it :-)
+ */
+
+#define MAKE_LINEAR(STEREO, UNSIGNED) \
+ if (is16Bit) { \
+ if (isLE) \
+ return new LinearMemoryStream<STEREO, true, UNSIGNED, true>(rate, ptr, len, loopOffset, loopLen, autoFree); \
+ else \
+ return new LinearMemoryStream<STEREO, true, UNSIGNED, false>(rate, ptr, len, loopOffset, loopLen, autoFree); \
+ } else \
+ return new LinearMemoryStream<STEREO, false, UNSIGNED, false>(rate, ptr, len, loopOffset, loopLen, autoFree)
+
+AudioStream *makeLinearInputStream(int rate, byte flags, const byte *ptr, uint32 len, uint32 loopOffset, uint32 loopLen) {
+ const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0;
+ const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0;
+ const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0;
+ const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0;
+ const bool autoFree = (flags & Audio::Mixer::FLAG_AUTOFREE) != 0;
+
+ if (isStereo) {
+ if (isUnsigned) {
+ MAKE_LINEAR(true, true);
+ } else {
+ MAKE_LINEAR(true, false);
+ }
+ } else {
+ if (isUnsigned) {
+ MAKE_LINEAR(false, true);
+ } else {
+ MAKE_LINEAR(false, false);
+ }
+ }
+}
+
+
+} // End of namespace Audio
Property changes on: tools/trunk/utils/audiostream.cpp
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/audiostream.h
===================================================================
--- tools/trunk/utils/audiostream.h (rev 0)
+++ tools/trunk/utils/audiostream.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,131 @@
+#ifndef SOUND_AUDIOSTREAM_H
+#define SOUND_AUDIOSTREAM_H
+
+#include "../util.h"
+#include "util.h"
+
+namespace Audio {
+
+class Mixer {
+public:
+ enum {
+ /** unsigned samples (default: signed) */
+ FLAG_UNSIGNED = 1 << 0,
+
+ /** sound is 16 bits wide (default: 8bit) */
+ FLAG_16BITS = 1 << 1,
+
+ /** sample is little endian (default: big endian) */
+ FLAG_LITTLE_ENDIAN = 1 << 2,
+
+ /** sound is in stereo (default: mono) */
+ FLAG_STEREO = 1 << 3,
+
+ /** reverse the left and right stereo channel */
+ FLAG_REVERSE_STEREO = 1 << 4,
+
+ /** sound buffer is freed automagically at the end of playing */
+ FLAG_AUTOFREE = 1 << 5,
+
+ /** loop the audio */
+ FLAG_LOOP = 1 << 6
+ };
+};
+
+/**
+ * Generic input stream for the resampling code.
+ */
+class AudioStream {
+public:
+ virtual ~AudioStream() {}
+
+ /**
+ * Fill the given buffer with up to numSamples samples.
+ * Returns the actual number of samples read, or -1 if
+ * a critical error occured (note: you *must* check if
+ * this value is less than what you requested, this can
+ * happen when the stream is fully used up).
+ *
+ * Data has to be in native endianess, 16 bit per sample, signed.
+ * For stereo stream, buffer will be filled with interleaved
+ * left and right channel samples, starting with a left sample.
+ * Furthermore, the samples in the left and right are summed up.
+ * So if you request 4 samples from a stereo stream, you will get
+ * a total of two left channel and two right channel samples.
+ */
+ virtual int readBuffer(int16 *buffer, const int numSamples) = 0;
+
+ /** Is this a stereo stream? */
+ virtual bool isStereo() const = 0;
+
+ /**
+ * End of data reached? If this returns true, it means that at this
+ * time there is no data available in the stream. However there may be
+ * more data in the future.
+ * This is used by e.g. a rate converter to decide whether to keep on
+ * converting data or stop.
+ */
+ virtual bool endOfData() const = 0;
+
+ /**
+ * End of stream reached? If this returns true, it means that all data
+ * in this stream is used up and no additional data will appear in it
+ * in the future.
+ * This is used by the mixer to decide whether a given stream shall be
+ * removed from the list of active streams (and thus be destroyed).
+ * By default this maps to endOfData()
+ */
+ virtual bool endOfStream() const { return endOfData(); }
+
+ /** Sample rate of the stream. */
+ virtual int getRate() const = 0;
+};
+
+/**
+ * A simple AudioStream which represents a 'silent' stream,
+ * containing the specified number of zero samples.
+ */
+class ZeroInputStream : public AudioStream {
+private:
+ int _len;
+public:
+ ZeroInputStream(uint32 len) : _len(len) { }
+ int readBuffer(int16 *buffer, const int numSamples) {
+ int samples = Common::MIN(_len, numSamples);
+ memset(buffer, 0, samples * 2);
+ _len -= samples;
+ return samples;
+ }
+ bool isStereo() const { return false; }
+ bool eos() const { return _len <= 0; }
+
+ int getRate() const { return -1; }
+};
+
+AudioStream *makeLinearInputStream(int rate, byte flags, const byte *ptr, uint32 len, uint32 loopOffset, uint32 loopLen);
+
+/**
+ * An audio stream to which additional data can be appended on-the-fly.
+ * Used by SMUSH, iMuseDigital, and the Kyrandia 3 VQA player.
+ */
+class AppendableAudioStream : public Audio::AudioStream {
+public:
+ virtual void append(const byte *data, uint32 len) = 0;
+ virtual void finish() = 0;
+};
+
+AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len);
+
+
+// This used to be an inline template function, but
+// buggy template function handling in MSVC6 forced
+// us to go with the macro approach. So far this is
+// the only template function that MSVC6 seemed to
+// compile incorrectly. Knock on wood.
+#define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \
+ ((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
+
+
+} // End of namespace Audio
+
+#endif
Property changes on: tools/trunk/utils/audiostream.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/file.cpp
===================================================================
--- tools/trunk/utils/file.cpp (rev 0)
+++ tools/trunk/utils/file.cpp 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,94 @@
+#include "file.h"
+
+
+namespace Common {
+
+
+File::File(FILE*file)
+ : _handle(file), _ioFailed(false) {
+}
+
+
+File::~File() {
+ close();
+}
+
+void File::close() {
+ _handle = NULL;
+}
+
+bool File::isOpen() const {
+ return _handle != NULL;
+}
+
+bool File::ioFailed() const {
+ return _ioFailed != 0;
+}
+
+void File::clearIOFailed() {
+ _ioFailed = false;
+}
+
+bool File::eof() const {
+ if (_handle == NULL) {
+ error("File::eof: File is not open!");
+ return false;
+ }
+
+ return feof(_handle) != 0;
+}
+
+uint32 File::pos() const {
+ if (_handle == NULL) {
+ error("File::pos: File is not open!");
+ return 0;
+ }
+
+ return ftell(_handle);
+}
+
+uint32 File::size() const {
+ if (_handle == NULL) {
+ error("File::size: File is not open!");
+ return 0;
+ }
+
+ uint32 oldPos = ftell(_handle);
+ fseek(_handle, 0, SEEK_END);
+ uint32 length = ftell(_handle);
+ fseek(_handle, oldPos, SEEK_SET);
+
+ return length;
+}
+
+void File::seek(int32 offs, int whence) {
+ if (_handle == NULL) {
+ error("File::seek: File is not open!");
+ return;
+ }
+
+ if (fseek(_handle, offs, whence) != 0)
+ clearerr(_handle);
+}
+
+uint32 File::read(void *ptr, uint32 len) {
+ byte *ptr2 = (byte *)ptr;
+ size_t real_len;
+
+ if (_handle == NULL) {
+ error("File::read: File is not open!");
+ return 0;
+ }
+
+ if (len == 0)
+ return 0;
+
+ real_len = fread(ptr2, 1, len, _handle);
+ if (real_len < len) {
+ _ioFailed = true;
+ }
+
+ return (uint32)real_len;
+}
+
+} // End of namespace Common
Property changes on: tools/trunk/utils/file.cpp
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/file.h
===================================================================
--- tools/trunk/utils/file.h (rev 0)
+++ tools/trunk/utils/file.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,43 @@
+#ifndef COMMON_FILE_H
+#define COMMON_FILE_H
+
+#include "stream.h"
+
+namespace Common {
+
+class File : public SeekableReadStream {
+protected:
+ /** POSIX file handle to the actual file; 0 if no file is open. */
+ FILE *_handle;
+
+ /** Status flag which tells about recent I/O failures. */
+ bool _ioFailed;
+
+private:
+ // Disallow copying File objects. There is not strict reason for this,
+ // except that so far we never had real need for such a feature, and
+ // code that accidentally copied File objects tended to break in strange
+ // ways.
+ File(const File &f);
+ File &operator =(const File &f);
+
+public:
+
+ File(FILE*file);
+ virtual ~File();
+
+ virtual void close();
+ bool isOpen() const;
+ bool ioFailed() const;
+ void clearIOFailed();
+ bool eos() const { return eof(); }
+ bool eof() const;
+ uint32 pos() const;
+ uint32 size() const;
+ void seek(int32 offs, int whence = SEEK_SET);
+ uint32 read(void *dataPtr, uint32 dataSize);
+};
+
+} // End of namespace Common
+
+#endif
Property changes on: tools/trunk/utils/file.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/md5.cpp
===================================================================
--- tools/trunk/utils/md5.cpp (rev 0)
+++ tools/trunk/utils/md5.cpp 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,243 @@
+#include "md5.h"
+
+namespace Common {
+
+#define GET_UINT32(n, b, i) (n) = READ_LE_UINT32(b + i)
+#define PUT_UINT32(n, b, i) WRITE_LE_UINT32(b + i, n)
+
+void md5_starts(md5_context *ctx) {
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+}
+
+static void md5_process(md5_context *ctx, const uint8 data[64]) {
+ uint32 X[16], A, B, C, D;
+
+ GET_UINT32(X[0], data, 0);
+ GET_UINT32(X[1], data, 4);
+ GET_UINT32(X[2], data, 8);
+ GET_UINT32(X[3], data, 12);
+ GET_UINT32(X[4], data, 16);
+ GET_UINT32(X[5], data, 20);
+ GET_UINT32(X[6], data, 24);
+ GET_UINT32(X[7], data, 28);
+ GET_UINT32(X[8], data, 32);
+ GET_UINT32(X[9], data, 36);
+ GET_UINT32(X[10], data, 40);
+ GET_UINT32(X[11], data, 44);
+ GET_UINT32(X[12], data, 48);
+ GET_UINT32(X[13], data, 52);
+ GET_UINT32(X[14], data, 56);
+ GET_UINT32(X[15], data, 60);
+
+#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a, b, c, d, k, s, t) \
+{ \
+ a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
+}
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+
+#define F(x, y, z) (z ^ (x & (y ^ z)))
+
+ P(A, B, C, D, 0, 7, 0xD76AA478);
+ P(D, A, B, C, 1, 12, 0xE8C7B756);
+ P(C, D, A, B, 2, 17, 0x242070DB);
+ P(B, C, D, A, 3, 22, 0xC1BDCEEE);
+ P(A, B, C, D, 4, 7, 0xF57C0FAF);
+ P(D, A, B, C, 5, 12, 0x4787C62A);
+ P(C, D, A, B, 6, 17, 0xA8304613);
+ P(B, C, D, A, 7, 22, 0xFD469501);
+ P(A, B, C, D, 8, 7, 0x698098D8);
+ P(D, A, B, C, 9, 12, 0x8B44F7AF);
+ P(C, D, A, B, 10, 17, 0xFFFF5BB1);
+ P(B, C, D, A, 11, 22, 0x895CD7BE);
+ P(A, B, C, D, 12, 7, 0x6B901122);
+ P(D, A, B, C, 13, 12, 0xFD987193);
+ P(C, D, A, B, 14, 17, 0xA679438E);
+ P(B, C, D, A, 15, 22, 0x49B40821);
+
+#undef F
+
+#define F(x, y, z) (y ^ (z & (x ^ y)))
+
+ P(A, B, C, D, 1, 5, 0xF61E2562);
+ P(D, A, B, C, 6, 9, 0xC040B340);
+ P(C, D, A, B, 11, 14, 0x265E5A51);
+ P(B, C, D, A, 0, 20, 0xE9B6C7AA);
+ P(A, B, C, D, 5, 5, 0xD62F105D);
+ P(D, A, B, C, 10, 9, 0x02441453);
+ P(C, D, A, B, 15, 14, 0xD8A1E681);
+ P(B, C, D, A, 4, 20, 0xE7D3FBC8);
+ P(A, B, C, D, 9, 5, 0x21E1CDE6);
+ P(D, A, B, C, 14, 9, 0xC33707D6);
+ P(C, D, A, B, 3, 14, 0xF4D50D87);
+ P(B, C, D, A, 8, 20, 0x455A14ED);
+ P(A, B, C, D, 13, 5, 0xA9E3E905);
+ P(D, A, B, C, 2, 9, 0xFCEFA3F8);
+ P(C, D, A, B, 7, 14, 0x676F02D9);
+ P(B, C, D, A, 12, 20, 0x8D2A4C8A);
+
+#undef F
+
+#define F(x, y, z) (x ^ y ^ z)
+
+ P(A, B, C, D, 5, 4, 0xFFFA3942);
+ P(D, A, B, C, 8, 11, 0x8771F681);
+ P(C, D, A, B, 11, 16, 0x6D9D6122);
+ P(B, C, D, A, 14, 23, 0xFDE5380C);
+ P(A, B, C, D, 1, 4, 0xA4BEEA44);
+ P(D, A, B, C, 4, 11, 0x4BDECFA9);
+ P(C, D, A, B, 7, 16, 0xF6BB4B60);
+ P(B, C, D, A, 10, 23, 0xBEBFBC70);
+ P(A, B, C, D, 13, 4, 0x289B7EC6);
+ P(D, A, B, C, 0, 11, 0xEAA127FA);
+ P(C, D, A, B, 3, 16, 0xD4EF3085);
+ P(B, C, D, A, 6, 23, 0x04881D05);
+ P(A, B, C, D, 9, 4, 0xD9D4D039);
+ P(D, A, B, C, 12, 11, 0xE6DB99E5);
+ P(C, D, A, B, 15, 16, 0x1FA27CF8);
+ P(B, C, D, A, 2, 23, 0xC4AC5665);
+
+#undef F
+
+#define F(x, y, z) (y ^ (x | ~z))
+
+ P(A, B, C, D, 0, 6, 0xF4292244);
+ P(D, A, B, C, 7, 10, 0x432AFF97);
+ P(C, D, A, B, 14, 15, 0xAB9423A7);
+ P(B, C, D, A, 5, 21, 0xFC93A039);
+ P(A, B, C, D, 12, 6, 0x655B59C3);
+ P(D, A, B, C, 3, 10, 0x8F0CCC92);
+ P(C, D, A, B, 10, 15, 0xFFEFF47D);
+ P(B, C, D, A, 1, 21, 0x85845DD1);
+ P(A, B, C, D, 8, 6, 0x6FA87E4F);
+ P(D, A, B, C, 15, 10, 0xFE2CE6E0);
+ P(C, D, A, B, 6, 15, 0xA3014314);
+ P(B, C, D, A, 13, 21, 0x4E0811A1);
+ P(A, B, C, D, 4, 6, 0xF7537E82);
+ P(D, A, B, C, 11, 10, 0xBD3AF235);
+ P(C, D, A, B, 2, 15, 0x2AD7D2BB);
+ P(B, C, D, A, 9, 21, 0xEB86D391);
+
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+}
+
+void md5_update(md5_context *ctx, const uint8 *input, uint32 length) {
+ uint32 left, fill;
+
+ if (!length)
+ return;
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += length;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if (ctx->total[0] < length)
+ ctx->total[1]++;
+
+ if (left && length >= fill) {
+ memcpy((void *)(ctx->buffer + left), (const void *)input, fill);
+ md5_process(ctx, ctx->buffer);
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+ while (length >= 64) {
+ md5_process(ctx, input);
+ length -= 64;
+ input += 64;
+ }
+
+ if (length) {
+ memcpy((void *)(ctx->buffer + left), (const void *)input, length);
+ }
+}
+
+static const uint8 md5_padding[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void md5_finish(md5_context *ctx, uint8 digest[16]) {
+ uint32 last, padn;
+ uint32 high, low;
+ uint8 msglen[8];
+
+ high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
+ low = (ctx->total[0] << 3);
+
+ PUT_UINT32(low, msglen, 0);
+ PUT_UINT32(high, msglen, 4);
+
+ last = ctx->total[0] & 0x3F;
+ padn = (last < 56) ? (56 - last) : (120 - last);
+
+ md5_update(ctx, md5_padding, padn);
+ md5_update(ctx, msglen, 8);
+
+ PUT_UINT32(ctx->state[0], digest, 0);
+ PUT_UINT32(ctx->state[1], digest, 4);
+ PUT_UINT32(ctx->state[2], digest, 8);
+ PUT_UINT32(ctx->state[3], digest, 12);
+}
+
+bool md5_file(const char *name, uint8 digest[16], uint32 length) {
+ FILE *f;
+
+ f = fopen(name, "rb");
+ if (f == NULL) {
+ printf("md5_file couldn't open '%s'", name);
+ return false;
+ }
+
+ md5_context ctx;
+ uint32 i;
+ unsigned char buf[1000];
+ bool restricted = (length != 0);
+ int readlen;
+
+ if (!restricted || sizeof(buf) <= length)
+ readlen = sizeof(buf);
+ else
+ readlen = length;
+
+ md5_starts(&ctx);
+
+
+ while ((i = (uint32)fread(buf, 1, readlen, f)) > 0) {
+ md5_update(&ctx, buf, i);
+
+ length -= i;
+ if (restricted && length == 0)
+ break;
+
+ if (restricted && sizeof(buf) > length)
+ readlen = length;
+ }
+
+ md5_finish(&ctx, digest);
+ fclose(f);
+ return true;
+}
+
+}
Property changes on: tools/trunk/utils/md5.cpp
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/md5.h
===================================================================
--- tools/trunk/utils/md5.h (rev 0)
+++ tools/trunk/utils/md5.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,22 @@
+#ifndef COMMON_MD5_H
+#define COMMON_MD5_H
+
+#include "../util.h"
+
+namespace Common {
+
+typedef struct {
+ uint32 total[2];
+ uint32 state[4];
+ uint8 buffer[64];
+} md5_context;
+
+void md5_starts(md5_context *ctx);
+void md5_update(md5_context *ctx, const uint8 *input, uint32 length);
+void md5_finish(md5_context *ctx, uint8 digest[16]);
+
+bool md5_file(const char *name, uint8 digest[16], uint32 length = 0);
+
+} // End of namespace Common
+
+#endif
Property changes on: tools/trunk/utils/md5.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/stream.h
===================================================================
--- tools/trunk/utils/stream.h (rev 0)
+++ tools/trunk/utils/stream.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,130 @@
+#ifndef COMMON_STREAM_H
+#define COMMON_STREAM_H
+
+#include "../util.h"
+
+namespace Common {
+
+/**
+ * Virtual base class for both ReadStream and WriteStream.
+ */
+class Stream {
+public:
+ virtual ~Stream() {}
+
+ /**
+ * Returns true if any I/O failure occured.
+ * This flag is never cleared automatically. In order to clear it,
+ * client code has to call clearIOFailed() explicitly.
+ *
+ * @todo Instead of returning a plain bool, maybe we should define
+ * a list of error codes which can be returned here.
+ */
+ virtual bool ioFailed() const { return false; }
+
+ /**
+ * Reset the I/O error status.
+ */
+ virtual void clearIOFailed() {}
+};
+
+
+/**
+ * Generic interface for a readable data stream.
+ */
+class ReadStream : virtual public Stream {
+public:
+ /**
+ * Returns true if the end of the stream has been reached.
+ */
+ virtual bool eos() const = 0;
+
+ /**
+ * Read data from the stream. Subclasses must implement this
+ * method; all other read methods are implemented using it.
+ *
+ * @param dataPtr pointer to a buffer into which the data is read
+ * @param dataSize number of bytes to be read
+ * @return the number of bytes which were actually read.
+ */
+ virtual uint32 read(void *dataPtr, uint32 dataSize) = 0;
+
+
+ // The remaining methods all have default implementations; subclasses
+ // need not (and should not) overload them.
+
+ byte readByte() {
+ byte b = 0;
+ read(&b, 1);
+ return b;
+ }
+
+ int8 readSByte() {
+ int8 b = 0;
+ read(&b, 1);
+ return b;
+ }
+
+ uint16 readUint16LE() {
+ uint16 a = readByte();
+ uint16 b = readByte();
+ return a | (b << 8);
+ }
+
+ uint32 readUint32LE() {
+ uint32 a = readUint16LE();
+ uint32 b = readUint16LE();
+ return (b << 16) | a;
+ }
+
+ uint16 readUint16BE() {
+ uint16 b = readByte();
+ uint16 a = readByte();
+ return a | (b << 8);
+ }
+
+ uint32 readUint32BE() {
+ uint32 b = readUint16BE();
+ uint32 a = readUint16BE();
+ return (b << 16) | a;
+ }
+
+ int16 readSint16LE() {
+ return (int16)readUint16LE();
+ }
+
+ int32 readSint32LE() {
+ return (int32)readUint32LE();
+ }
+
+ int16 readSint16BE() {
+ return (int16)readUint16BE();
+ }
+
+ int32 readSint32BE() {
+ return (int32)readUint32BE();
+ }
+};
+
+
+/**
+ * Interface for a seekable & readable data stream.
+ *
+ * @todo We really need better error handling here!
+ * Like seek should somehow indicate whether it failed.
+ */
+class SeekableReadStream : public ReadStream {
+public:
+
+ virtual uint32 pos() const = 0;
+ virtual uint32 size() const = 0;
+
+ virtual void seek(int32 offset, int whence = SEEK_SET) = 0;
+
+ void skip(uint32 offset) { seek(offset, SEEK_CUR); }
+};
+
+
+} // End of namespace Common
+
+#endif
Property changes on: tools/trunk/utils/stream.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/util.h
===================================================================
--- tools/trunk/utils/util.h (rev 0)
+++ tools/trunk/utils/util.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,73 @@
+#ifndef COMMON_UTIL_H
+#define COMMON_UTIL_H
+
+namespace Common {
+
+
+#ifdef MIN
+#undef MIN
+#endif
+
+#ifdef MAX
+#undef MAX
+#endif
+
+template<typename T> inline T ABS (T x) { return (x>=0) ? x : -x; }
+template<typename T> inline T MIN (T a, T b) { return (a<b) ? a : b; }
+template<typename T> inline T MAX (T a, T b) { return (a>b) ? a : b; }
+template<typename T> inline T CLIP (T v, T amin, T amax)
+ { if (v < amin) return amin; else if (v > amax) return amax; else return v; }
+
+/**
+ * List of game language.
+ */
+enum Language {
+ EN_ANY, // Generic English (when only one game version exist)
+ EN_USA,
+ EN_GRB,
+
+ DE_DEU,
+ FR_FRA,
+ IT_ITA,
+ PT_BRA,
+ ES_ESP,
+ JA_JPN,
+ ZH_TWN,
+ KO_KOR,
+ SE_SWE,
+ HB_ISR,
+ RU_RUS,
+ CZ_CZE,
+ NL_NLD,
+ NB_NOR,
+ PL_POL,
+
+ UNK_LANG = -1 // Use default language (i.e. none specified)
+};
+
+/**
+ * List of game platforms. Specifying a platform for a target can be used to
+ * give the game engines a hint for which platform the game data file are.
+ * This may be optional or required, depending on the game engine and the
+ * game in question.
+ */
+enum Platform {
+ kPlatformPC,
+ kPlatformAmiga,
+ kPlatformAtariST,
+ kPlatformMacintosh,
+ kPlatformFMTowns,
+ kPlatformWindows,
+ kPlatformNES,
+ kPlatformC64,
+ kPlatformLinux,
+ kPlatformAcorn,
+ kPlatformSegaCD,
+ kPlatform3DO,
+// kPlatformPCEngine,
+
+ kPlatformUnknown = -1
+};
+}
+
+#endif
Property changes on: tools/trunk/utils/util.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/voc.cpp
===================================================================
--- tools/trunk/utils/voc.cpp (rev 0)
+++ tools/trunk/utils/voc.cpp 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,118 @@
+#include "voc.h"
+#include "stream.h"
+#include "audiostream.h"
+
+
+namespace Audio {
+
+int getSampleRateFromVOCRate(int vocSR) {
+ if (vocSR == 0xa5 || vocSR == 0xa6) {
+ return 11025;
+ } else if (vocSR == 0xd2 || vocSR == 0xd3) {
+ return 22050;
+ } else {
+ int sr = 1000000L / (256L - vocSR);
+ // inexact sampling rates occur e.g. in the kitchen in Monkey Island,
+ // very easy to reach right from the start of the game.
+ //warning("inexact sample rate used: %i (0x%x)", sr, vocSR);
+ return sr;
+ }
+}
+
+byte *loadVOCFromStream(Common::ReadStream &stream, int &size, int &rate, int &loops, int &begin_loop, int &end_loop) {
+ VocFileHeader fileHeader;
+
+ if (stream.read(&fileHeader, 8) != 8)
+ goto invalid;
+
+ if (!memcmp(&fileHeader, "VTLK", 4)) {
+ if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader))
+ goto invalid;
+ } else if (!memcmp(&fileHeader, "Creative", 8)) {
+ if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8)
+ goto invalid;
+ } else {
+ invalid:;
+ warning("loadVOCFromStream: Invalid header");
+ return NULL;
+ }
+
+ if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0)
+ error("loadVOCFromStream: Invalid header");
+ if (fileHeader.desc[19] != 0x1A)
+ debug(3, "loadVOCFromStream: Partially invalid header");
+
+ int32 offset = FROM_LE_16(fileHeader.datablock_offset);
+ int16 version = FROM_LE_16(fileHeader.version);
+ int16 code = FROM_LE_16(fileHeader.id);
+ assert(offset == sizeof(VocFileHeader));
+ // 0x100 is an invalid VOC version used by German version of DOTT (Disk) and
+ // French version of Simon the Sorcerer 2 (CD)
+ assert(version == 0x010A || version == 0x0114 || version == 0x0100);
+ assert(code == ~version + 0x1234);
+
+ int len;
+ byte *ret_sound = 0;
+ size = 0;
+ begin_loop = 0;
+ end_loop = 0;
+
+ while ((code = stream.readByte())) {
+ len = stream.readByte();
+ len |= stream.readByte() << 8;
+ len |= stream.readByte() << 16;
+
+ switch(code) {
+ case 1: {
+ int time_constant = stream.readByte();
+ int packing = stream.readByte();
+ len -= 2;
+ rate = getSampleRateFromVOCRate(time_constant);
+ debug(9, "VOC Data Block: %d, %d, %d", rate, packing, len);
+ if (packing == 0) {
+ if (size) {
+ ret_sound = (byte *)realloc(ret_sound, size + len);
+ } else {
+ ret_sound = (byte *)malloc(len);
+ }
+ stream.read(ret_sound + size, len);
+ size += len;
+ begin_loop = size;
+ end_loop = size;
+ } else {
+ warning("VOC file packing %d unsupported", packing);
+ }
+ } break;
+ case 6: // begin of loop
+ assert(len == 2);
+ loops = stream.readUint16LE();
+ break;
+ case 7: // end of loop
+ assert(len == 0);
+ break;
+ default:
+ warning("Invalid code in VOC file : %d", code);
+ return ret_sound;
+ }
+ }
+ debug(4, "VOC Data Size : %d", size);
+ return ret_sound;
+}
+
+byte *loadVOCFromStream(Common::ReadStream &stream, int &size, int &rate) {
+ int loops, begin_loop, end_loop;
+ return loadVOCFromStream(stream, size, rate, loops, begin_loop, end_loop);
+}
+
+AudioStream *makeVOCStream(Common::ReadStream &stream) {
+ int size, rate;
+ byte *data = loadVOCFromStream(stream, size, rate);
+
+ if (!data)
+ return 0;
+
+ return makeLinearInputStream(rate, Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED, data, size, 0, 0);
+}
+
+
+} // End of namespace Audio
Property changes on: tools/trunk/utils/voc.cpp
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/voc.h
===================================================================
--- tools/trunk/utils/voc.h (rev 0)
+++ tools/trunk/utils/voc.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,66 @@
+#ifndef SOUND_VOC_H
+#define SOUND_VOC_H
+
+#include "../util.h"
+
+namespace Common { class ReadStream; }
+
+namespace Audio {
+
+class AudioStream;
+
+
+#if !defined(__GNUC__)
+#pragma START_PACK_STRUCTS
+#endif
+
+struct VocFileHeader {
+ uint8 desc[20];
+ uint16 datablock_offset;
+ uint16 version;
+ uint16 id;
+} GCC_PACK;
+
+struct VocBlockHeader {
+ uint8 blocktype;
+ uint8 size[3];
+ uint8 sr;
+ uint8 pack;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+#pragma END_PACK_STRUCTS
+#endif
+
+/**
+ * Take a sample rate parameter as it occurs in a VOC sound header, and
+ * return the corresponding sample frequency.
+ *
+ * This method has special cases for the standard rates of 11025 and 22050 kHz,
+ * which due to limitations of the format, cannot be encoded exactly in a VOC
+ * file. As a consequence, many game files have sound data sampled with those
+ * rates, but the VOC marks them incorrectly as 11111 or 22222 kHz. This code
+ * works around that and "unrounds" the sampling rates.
+ */
+extern int getSampleRateFromVOCRate(int vocSR);
+
+/**
+ * Try to load a VOC from the given seekable stream. Returns a pointer to memory
+ * containing the PCM sample data (allocated with malloc). It is the callers
+ * responsibility to dellocate that data again later on! Currently this
+ * function only supports uncompressed raw PCM data.
+ */
+extern byte *loadVOCFromStream(Common::ReadStream &stream, int &size, int &rate);
+
+/**
+ * Try to load a VOC from the given seekable stream and create an AudioStream
+ * from that data. Currently this function only supports uncompressed raw PCM
+ * data. Looping is not supported.
+ *
+ * This function uses loadVOCFromStream() internally.
+ */
+AudioStream *makeVOCStream(Common::ReadStream &stream);
+
+} // End of namespace Audio
+
+#endif
Property changes on: tools/trunk/utils/voc.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/wave.cpp
===================================================================
--- tools/trunk/utils/wave.cpp (rev 0)
+++ tools/trunk/utils/wave.cpp 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,160 @@
+#include "stream.h"
+#include "AudioStream.h"
+#include "adpcm.h"
+
+namespace Audio {
+
+bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType, int *blockAlign_) {
+ const uint32 initialPos = stream.pos();
+ byte buf[4+1];
+
+ buf[4] = 0;
+
+ stream.read(buf, 4);
+ if (memcmp(buf, "RIFF", 4) != 0) {
+ warning("getWavInfo: No 'RIFF' header");
+ return false;
+ }
+
+ uint32 wavLength = stream.readUint32LE();
+
+ stream.read(buf, 4);
+ if (memcmp(buf, "WAVE", 4) != 0) {
+ warning("getWavInfo: No 'WAVE' header");
+ return false;
+ }
+
+ stream.read(buf, 4);
+ if (memcmp(buf, "fmt ", 4) != 0) {
+ warning("getWavInfo: No 'fmt' header");
+ return false;
+ }
+
+ uint32 fmtLength = stream.readUint32LE();
+ if (fmtLength < 16) {
+ // A valid fmt chunk always contains at least 16 bytes
+ warning("getWavInfo: 'fmt' header is too short");
+ return false;
+ }
+
+ // Next comes the "type" field of the fmt header. Some typical
+ // values for it:
+ // 1 -> uncompressed PCM
+ // 17 -> IMA ADPCM compressed WAVE
+ // See <http://www.saettler.com/RIFFNEW/RIFFNEW.htm> for a more complete
+ // list of common WAVE compression formats...
+ uint16 type = stream.readUint16LE(); // == 1 for PCM data
+ uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo
+ uint32 samplesPerSec = stream.readUint32LE(); // in Hz
+ uint32 avgBytesPerSec = stream.readUint32LE(); // == SampleRate * NumChannels * BitsPerSample/8
+
+ uint16 blockAlign = stream.readUint16LE(); // == NumChannels * BitsPerSample/8
+ uint16 bitsPerSample = stream.readUint16LE(); // 8, 16 ...
+ // 8 bit data is unsigned, 16 bit data signed
+
+
+ if (wavType != 0)
+ *wavType = type;
+
+ if (blockAlign_ != 0)
+ *blockAlign_ = blockAlign;
+#if 0
+ printf("WAVE information:\n");
+ printf(" total size: %d\n", wavLength);
+ printf(" fmt size: %d\n", fmtLength);
+ printf(" type: %d\n", type);
+ printf(" numChannels: %d\n", numChannels);
+ printf(" samplesPerSec: %d\n", samplesPerSec);
+ printf(" avgBytesPerSec: %d\n", avgBytesPerSec);
+ printf(" blockAlign: %d\n", blockAlign);
+ printf(" bitsPerSample: %d\n", bitsPerSample);
+#endif
+
+ if (type != 1 && type != 2 && type != 17) {
+ warning("getWavInfo: only PCM, MS ADPCM or IMA ADPCM data is supported (type %d)", type);
+ return false;
+ }
+
+ if (blockAlign != numChannels * bitsPerSample / 8 && type != 2) {
+ debug(0, "getWavInfo: blockAlign is invalid");
+ }
+
+ if (avgBytesPerSec != samplesPerSec * blockAlign && type != 2) {
+ debug(0, "getWavInfo: avgBytesPerSec is invalid");
+ }
+
+ // Prepare the return values.
+ rate = samplesPerSec;
+
+ flags = 0;
+ if (bitsPerSample == 8) // 8 bit data is unsigned
+ flags |= Audio::Mixer::FLAG_UNSIGNED;
+ else if (bitsPerSample == 16) // 16 bit data is signed little endian
+ flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
+ else if (bitsPerSample == 4 && type == 17) // MS IMA ADPCM compressed. We decompress it
+ flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
+ else if (bitsPerSample == 4 && type == 2) // MS ADPCM compressed. We decompress it
+ flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
+ else {
+ warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample);
+ return false;
+ }
+
+ if (numChannels == 2)
+ flags |= Audio::Mixer::FLAG_STEREO;
+ else if (numChannels != 1) {
+ warning("getWavInfo: unsupported number of channels %d", numChannels);
+ return false;
+ }
+
+ // It's almost certainly a WAV file, but we still need to find its
+ // 'data' chunk.
+
+ // Skip over the rest of the fmt chunk.
+ int offset = fmtLength - 16;
+
+ do {
+ stream.seek(offset, SEEK_CUR);
+ if (stream.pos() >= initialPos + wavLength + 8) {
+ warning("getWavInfo: Can't find 'data' chunk");
+ return false;
+ }
+ stream.read(buf, 4);
+ offset = stream.readUint32LE();
+
+#if 0
+ printf(" found a '%s' tag of size %d\n", buf, offset);
+#endif
+ } while (memcmp(buf, "data", 4) != 0);
+
+ // Stream now points at 'offset' bytes of sample data...
+ size = offset;
+
+ return true;
+}
+
+AudioStream *makeWAVStream(Common::SeekableReadStream &stream) {
+ int size, rate;
+ byte flags;
+ uint16 type;
+ int blockAlign;
+
+ if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign))
+ return 0;
+
+ if (type == 17) // MS IMA ADPCM
+ return makeADPCMStream(&stream, size, kADPCMMSIma, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1);
+ if (type == 2) // MS ADPCM
+ return makeADPCMStream(&stream, size, kADPCMMS, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
+
+ byte *data = (byte *)malloc(size);
+ assert(data);
+ stream.read(data, size);
+
+ // Since we allocated our own buffer for the data, we must set the autofree flag.
+ flags |= Audio::Mixer::FLAG_AUTOFREE;
+
+ return makeLinearInputStream(rate, flags, data, size, 0, 0);
+}
+
+} // End of namespace Audio
Property changes on: tools/trunk/utils/wave.cpp
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
Added: tools/trunk/utils/wave.h
===================================================================
--- tools/trunk/utils/wave.h (rev 0)
+++ tools/trunk/utils/wave.h 2006-06-15 14:34:46 UTC (rev 23136)
@@ -0,0 +1,31 @@
+#ifndef SOUND_WAVE_H
+#define SOUND_WAVE_H
+
+
+namespace Common { class SeekableReadStream; }
+
+namespace Audio {
+
+class AudioStream;
+
+/**
+ * Try to load a WAVE from the given seekable stream. Returns true if
+ * successful. In that case, the stream's seek position will be set to the
+ * start of the audio data, and size, rate and flags contain information
+ * necessary for playback. Currently this function only supports uncompressed
+ * raw PCM data as well as IMA ADPCM.
+ */
+extern bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType = 0, int *blockAlign = 0);
+
+/**
+ * Try to load a WAVE from the given seekable stream and create an AudioStream
+ * from that data. Currently this function only supports uncompressed raw PCM
+ * data as well as IMA ADPCM.
+ *
+ * This function uses loadWAVFromStream() internally.
+ */
+AudioStream *makeWAVStream(Common::SeekableReadStream &stream);
+
+} // End of namespace Audio
+
+#endif
Property changes on: tools/trunk/utils/wave.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Rev Author URL Id
Name: svn:eol-style
+ native
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the Scummvm-git-logs
mailing list