[Scummvm-cvs-logs] SF.net SVN: scummvm:[48882] tools/trunk/engines
m_kiewitz at users.sourceforge.net
m_kiewitz at users.sourceforge.net
Sat May 1 21:28:16 CEST 2010
Revision: 48882
http://scummvm.svn.sourceforge.net/scummvm/?rev=48882&view=rev
Author: m_kiewitz
Date: 2010-05-01 19:28:16 +0000 (Sat, 01 May 2010)
Log Message:
-----------
SCI: new files for sci audio compression, not yet added to official tool - not for use on games that use sci2.1+
Added Paths:
-----------
tools/trunk/engines/sci/
tools/trunk/engines/sci/compress_sci.cpp
tools/trunk/engines/sci/compress_sci.h
Copied: tools/trunk/engines/sci/compress_sci.cpp (from rev 48111, tools/trunk/engines/tinsel/compress_tinsel.cpp)
===================================================================
--- tools/trunk/engines/sci/compress_sci.cpp (rev 0)
+++ tools/trunk/engines/sci/compress_sci.cpp 2010-05-01 19:28:16 UTC (rev 48882)
@@ -0,0 +1,403 @@
+/* compress_sci - resource.aud/resource.sfx compressor
+ * Copyright (C) 2009 The ScummVM Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// by m_kiewitz
+
+#include <stdlib.h>
+
+#include "compress.h"
+#include "common/endian.h"
+
+#include "sound/audiostream.h"
+#include "sound/wave.h"
+
+#include "compress_sci.h"
+
+// data-format of resource.aud/resource.sfx:
+// just multiple SOL-Audio/WAVE files appended to each other
+// SCI1.1 also have resource id and headersize PER resource entry (but in WAVE resources)
+// we can NOT just read in resource.aud, because at least in kq6 there is garbage data inside as well. And because
+// of
+
+// those are accessed directly by offset. We need to add an additional offset to offset mapping table right at the
+// start of the file. We also include the compression type as first 4 bytes
+
+// This code CURRENTLY DOES NOT SUPPORT SCI2.1+ GAMES! In those games the current code will actually destroy the content of
+// the samples, because SCI32 used a different scheme for decoding. I don't know yet how to detect SCI32 games easily
+// without having resourcemanager.
+
+#define TEMP_RAW "tempfile.raw"
+#define TEMP_ENC "tempfile.enc"
+
+CompressSci::CompressSci(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) {
+ _supportsProgressBar = true;
+
+ ToolInput input1;
+ input1.format = "resource.*";
+ _inputPaths.push_back(input1);
+
+ _outputToDirectory = false;
+
+ _shorthelp = "Used to compress sierra resource.aud/resource.sfx files. (NOT SCI32 compatible!)";
+ _helptext = "\nUsage: " + getName() + " [mode-params] [-o outputname] <inputname>\n";
+}
+
+// header is first 6 bytes read from file
+SciResourceDataType CompressSci::detectData(byte *header, bool compressMode) {
+ byte buffer[20];
+ uint32 dataSize;
+ memcpy(&buffer, header, 6);
+ if (memcmp(buffer, "RIFF", 4) == 0) {
+ // WAVE files begin with
+ // "RIFF" [size of following data:DWORD]
+ _input.read_throwsOnError(&buffer[6], 2);
+ dataSize = READ_LE_UINT32(buffer + 4);
+ _inputEndOffset = _inputOffset + 8 + dataSize;
+ return kSciResourceDataTypeWAVE;
+ }
+ if (memcmp(buffer, "\x8d\x0bSOL\x00", 6) == 0) {
+ // SOL files begin with
+ // 0x8D 0x0B/0x0C "SOL" 0x00 [samplerate:WORD] [flags:BYTE] [size of following data after header:DWORD]
+ // 0x0C variant have an additional byte inbetween data and [size of]
+ _input.read_throwsOnError(&buffer[6], 7);
+ dataSize = READ_LE_UINT32(buffer + 9);
+ _inputEndOffset = _inputOffset + 13 + dataSize;
+ return kSciResourceDataTypeSOL;
+ }
+ if (memcmp(buffer, "\x8d\x0cSOL\x00", 6) == 0) {
+ // see above
+ _input.read_throwsOnError(&buffer[6], 8);
+ dataSize = READ_LE_UINT32(buffer + 9);
+ _inputEndOffset = _inputOffset + 14 + dataSize;
+ return kSciResourceDataTypeSOL;
+ }
+ // No audio data found, if we are at offset 0 of the file -> exit w/ error message
+ if (!_inputOffset)
+ error("Input file doesn't seem to be a valid sci audio resource file");
+
+ int searchForward = 0;
+ bool noSignature = false;
+ if (memcmp(buffer, "\x8e\x00", 2) == 0) {
+ // sync resource, we expect SOL audio with a resourceid afterwards
+ searchForward = 10000;
+ }
+ // We didn't find anything useful, though this may be a valid file after all - kq6 resource.aud contains garbage data
+ if (!searchForward) {
+ searchForward = 2048;
+ if (!compressMode)
+ warning("possibly garbage found at offset %lx\n", _input.pos());
+ noSignature = true;
+ }
+
+ int originalOffset = _input.pos();
+ byte syncBuffer[10000];
+ uint32 syncPos = 0;
+ _input.read_throwsOnError(&syncBuffer, searchForward);
+ while (true) {
+ if (syncPos == (searchForward - 5)) {
+ if (noSignature)
+ error("no SOL audio after garbage data at %lx", originalOffset);
+ else
+ error("no SOL audio after sync resource at %lx", originalOffset);
+ }
+ if (syncBuffer[syncPos] == 0x8D) {
+ if (memcmp(&syncBuffer[syncPos + 1], "\x0cSOL\x00", 5) == 0)
+ break;
+ if (memcmp(&syncBuffer[syncPos + 1], "\x0bSOL\x00", 5) == 0)
+ break;
+ }
+ syncPos++;
+ }
+ if (!noSignature) {
+ _inputEndOffset = _inputOffset + 6 + syncPos;
+ return kSciResourceTypeTypeSync;
+ }
+ // We skipped garbage, so skip that and call us again so that SOL data gets actually seen
+ _input.seek(-searchForward + syncPos, SEEK_CUR);
+ _inputOffset = _input.pos();
+ _input.read_throwsOnError(header, 6);
+ return detectData(header, compressMode);
+}
+
+static const uint16 tableDPCM16[128] = {
+ 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080,
+ 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120,
+ 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0,
+ 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230,
+ 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280,
+ 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0,
+ 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320,
+ 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370,
+ 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0,
+ 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480,
+ 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700,
+ 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00,
+ 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000
+};
+
+static const byte tableDPCM8[8] = {0, 1, 2, 3, 6, 10, 15, 21};
+
+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;
+}
+
+static void deDPCM16(byte *soundBuf, byte *inputBuf, uint32 n) {
+ int16 *out = (int16 *) soundBuf;
+
+ int32 s = 0;
+ for (uint32 i = 0; i < n; i++) {
+ byte b = *inputBuf++;
+ if (b & 0x80)
+ s -= tableDPCM16[b & 0x7f];
+ else
+ s += tableDPCM16[b];
+
+ s = CLIP<int32>(s, -32768, 32767);
+ *out++ = (uint16)s;
+ }
+}
+
+static void deDPCM8Nibble(byte *soundBuf, int32 &s, byte b) {
+ if (b & 8) {
+// TODO: Can't include it currently because i have yet no idea how to identifiy sci2.1+ easily
+// #ifdef ENABLE_SCI32
+// // SCI2.1 reverses the order of the table values here
+// if (getSciVersion() >= SCI_VERSION_2_1)
+// s -= tableDPCM8[b & 7];
+// else
+// #endif
+ s -= tableDPCM8[7 - (b & 7)];
+ } else
+ s += tableDPCM8[b & 7];
+ s = CLIP<int32>(s, 0, 255);
+ *soundBuf = s;
+}
+
+static void deDPCM8(byte *soundBuf, byte *inputBuf, uint32 n) {
+ int32 s = 0x80;
+
+ for (uint i = 0; i < n; i++) {
+ byte b = *inputBuf++;
+
+ deDPCM8Nibble(soundBuf++, s, b >> 4);
+ deDPCM8Nibble(soundBuf++, s, b & 0xf);
+ }
+}
+
+// Will compress dataType at current offset in inputfile to outputfile using requested codec
+void CompressSci::compressData(SciResourceDataType dataType) {
+ int orgDataSize = _inputEndOffset - _inputOffset;
+ int newDataSize = 0;
+ byte *orgData = new byte[orgDataSize];
+ byte *newData = 0;
+
+ int sampleRate = 0;
+ byte *sampleData = 0;
+ int sampleDataSize = 0;
+ bool sampleIsStereo = false;
+ uint8 sampleBits = 8;
+ byte sampleFlags = 0;
+
+ if (!orgData)
+ error("malloc error");
+
+ switch (dataType) {
+ case kSciResourceDataTypeWAVE:
+ print("WAVE found\n");
+ if (!Audio::loadWAVFromStream(_input, sampleDataSize, sampleRate, sampleFlags))
+ error("Unable to read WAV at offset %lx", _inputOffset);
+
+ sampleData = new byte[sampleDataSize];
+ if (!sampleData)
+ error("malloc error");
+ _input.read_throwsOnError(sampleData, sampleDataSize);
+ if (sampleFlags & Audio::Mixer::FLAG_16BITS)
+ sampleBits = 16;
+ if (sampleFlags & Audio::Mixer::FLAG_STEREO)
+ sampleIsStereo = true;
+ break;
+ case kSciResourceDataTypeSOL: {
+ _input.readByte();
+ byte headerSize = _input.readByte();
+ _input.readUint32LE(); // Skip over "SOL" 0x00
+ sampleRate = _input.readUint16LE();
+ sampleFlags = _input.readByte();
+ sampleDataSize = _input.readUint32LE();
+ if (headerSize == 0x0C)
+ _input.readByte();
+ // Now we read SOL datastream
+ sampleData = new byte[sampleDataSize];
+ if (!sampleData)
+ error("malloc error");
+ _input.read_throwsOnError(sampleData, sampleDataSize);
+
+ bool dataUnsigned = false;
+ if (sampleFlags & 0x04)
+ sampleBits = 16;
+ if (sampleFlags & 0x08)
+ dataUnsigned = true;
+ if (sampleFlags & 0x01) {
+ // SOL datastream is compressed, we need to uncompress it
+ byte *uncompressedData = new byte[sampleDataSize * 2];
+ if (sampleBits == 16)
+ deDPCM16(uncompressedData, sampleData, sampleDataSize);
+ else
+ deDPCM8(uncompressedData, sampleData, sampleDataSize);
+ delete[] sampleData;
+ sampleData = uncompressedData;
+ sampleDataSize *= 2;
+ }
+ break;
+ }
+ case kSciResourceTypeTypeSync:
+ print("SYNC found at %lx\n", _inputOffset);
+ // Simply copy original data over
+ _input.read_throwsOnError(orgData, orgDataSize);
+ newData = orgData;
+ newDataSize = orgDataSize;
+ break;
+ default:
+ error("Unsupported datatype");
+ }
+
+ if (sampleData) {
+ // Write sample data to temporary raw file
+ Common::File tempfileRaw(TEMP_RAW, "wb");
+ tempfileRaw.write(sampleData, sampleDataSize);
+ tempfileRaw.close();
+ delete[] sampleData;
+ // Now compress temporary raw file
+ setRawAudioType(true, sampleIsStereo, sampleBits);
+ encodeAudio(TEMP_RAW, true, sampleRate, TEMP_ENC, _format);
+ // And copy encoded file into output-file
+ Common::File tempfileEnc(TEMP_ENC, "rb");
+ newDataSize = tempfileEnc.size();
+ newData = new byte[newDataSize];
+ tempfileEnc.read_throwsOnError(newData, newDataSize);
+ tempfileEnc.close();
+ }
+
+ _output.write(newData, newDataSize);
+
+ if ((newData) && (newData != orgData))
+ delete[] newData;
+ delete[] orgData;
+}
+
+void CompressSci::execute() {
+ Common::Filename infile = _inputPaths[0].path;
+ Common::Filename outfile = _outputPath;
+ SciResourceDataType recognizedDataType;
+
+ _input.open(infile, "rb");
+ _inputSize = _input.size();
+
+ // First find out how many samples are in this file
+ // We only support SOL audio and WAVE
+ // We can't support games that use "raw" audio (would have to walk through audio map for those, which would
+ // complicate this code - all talkie games that use this (kq5) don't use much space anyway, so not supporting
+ // those isn't a big issue
+
+ _input.seek(0, SEEK_SET);
+ byte header[6];
+
+ _inputOffset = 0;
+ _input.read_throwsOnError(&header, 6);
+ if (memcmp(header, "MP3 ", 4) == 0)
+ error("This resource file is already MP3-compressed, aborting...\n");
+ if (memcmp(header, "OGG ", 4) == 0)
+ error("This resource file is already OGG-compressed, aborting...\n");
+ if (memcmp(header, "FLAC", 4) == 0)
+ error("This resource file is already FLAC-compressed, aborting...\n");
+
+ int resourceCount = 0;
+ do {
+ recognizedDataType = detectData(header, false);
+ if (!recognizedDataType)
+ error("Unsupported data at offset %lx", _inputOffset);
+ _input.seek(_inputEndOffset, SEEK_SET);
+ _inputOffset = _inputEndOffset;
+ if (_inputOffset == _inputSize)
+ break;
+
+ resourceCount++;
+ _input.read_throwsOnError(&header, 6);
+ } while (true);
+
+ print("Valid sci audio resource file. Found %d resources", resourceCount);
+
+ // Now write basic output file header
+ _output.open(outfile, "wb");
+ // Compression ID
+ switch (_format) {
+ case AUDIO_MP3: _output.writeUint32BE(MKID_BE('MP3 ')); break;
+ case AUDIO_VORBIS: _output.writeUint32BE(MKID_BE('OGG ')); break;
+ case AUDIO_FLAC: _output.writeUint32BE(MKID_BE('FLAC')); break;
+ default: throw ToolException("Unknown audio format!");
+ }
+ // Resource count
+ _output.writeUint32LE(resourceCount);
+ // Offset mapping table
+ for (int resourceNo = 0; resourceNo < resourceCount; resourceNo++) {
+ _output.writeUint32LE(0); // Original offset
+ _output.writeUint32LE(0); // New offset
+ }
+ // Now actually compress the file
+ _input.seek(0, SEEK_SET);
+ for (int resourceNo = 0; resourceNo < resourceCount; resourceNo++) {
+ _inputOffset = _input.pos();
+ _outputOffset = _output.pos();
+ _input.read_throwsOnError(&header, 6);
+ recognizedDataType = detectData(header, true);
+
+ assert(recognizedDataType);
+ _input.seek(_inputOffset, SEEK_SET);
+ compressData(recognizedDataType);
+
+ // Seek inputfile to the end of the data
+ _input.seek(_inputEndOffset, SEEK_SET);
+ // Seek outputfile to mapping table
+ _output.seek(8 + resourceNo * 8, SEEK_SET);
+ // And write offset translations
+ _output.writeUint32LE(_inputOffset);
+ _output.writeUint32LE(_outputOffset);
+ // Seek to end of file
+ _output.seek(0, SEEK_END);
+ }
+
+ /* And some clean-up :-) */
+ unlink(TEMP_RAW);
+ unlink(TEMP_ENC);
+}
+
+
+#ifdef STANDALONE_MAIN
+int main(int argc, char *argv[]) {
+ CompressSci sci(argv[0]);
+ return sci.run(argc, argv);
+}
+#endif
Copied: tools/trunk/engines/sci/compress_sci.h (from rev 48111, tools/trunk/engines/tinsel/compress_tinsel.h)
===================================================================
--- tools/trunk/engines/sci/compress_sci.h (rev 0)
+++ tools/trunk/engines/sci/compress_sci.h 2010-05-01 19:28:16 UTC (rev 48882)
@@ -0,0 +1,52 @@
+/* compress_sci - resource.aud/resource.sfx compressor
+ * Copyright (C) 2009 The ScummVM Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef COMPRESS_SCI_H
+#define COMPRESS_SCI_H
+
+#include "compress.h"
+
+enum SciResourceDataType {
+ kSciResourceDataTypeUnknown = 0,
+ kSciResourceDataTypeWAVE = 1,
+ kSciResourceDataTypeSOL = 2,
+ kSciResourceTypeTypeSync = 3
+};
+
+class CompressSci : public CompressionTool {
+public:
+ CompressSci(const std::string &name = "compress_sci");
+
+ virtual void execute();
+
+protected:
+ SciResourceDataType detectData(byte *header, bool compressMode);
+ void compressData(SciResourceDataType dataType);
+
+ Common::File _input, _output;
+ int _inputOffset;
+ int _inputEndOffset;
+ int _inputSize;
+ int _outputOffset;
+};
+
+#endif
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