aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scumm/module.mk1
-rw-r--r--scumm/resource.cpp1191
-rw-r--r--scumm/scumm.cpp85
-rw-r--r--scumm/scumm.h39
-rw-r--r--scumm/sound.cpp1046
-rw-r--r--scumm/util.cpp272
-rw-r--r--scumm/util.h71
7 files changed, 1391 insertions, 1314 deletions
diff --git a/scumm/module.mk b/scumm/module.mk
index 4e23c3aa93..38a7a9b662 100644
--- a/scumm/module.mk
+++ b/scumm/module.mk
@@ -49,6 +49,7 @@ MODULE_OBJS := \
scumm/sound.o \
scumm/string.o \
scumm/usage_bits.o \
+ scumm/util.o \
scumm/vars.o \
scumm/verbs.o \
scumm/wiz_he.o \
diff --git a/scumm/resource.cpp b/scumm/resource.cpp
index 67fdf23327..6276ca822e 100644
--- a/scumm/resource.cpp
+++ b/scumm/resource.cpp
@@ -38,165 +38,6 @@ static uint16 newTag2Old(uint32 oldTag);
static const char *resTypeFromId(int id);
-
-ScummFile::ScummFile() : _encbyte(0), _subFileStart(0), _subFileLen(0) {
-}
-
-void ScummFile::setEnc(byte value) {
- _encbyte = value;
-}
-
-void ScummFile::setSubfileRange(uint32 start, uint32 len) {
- // TODO: Add sanity checks
- const uint32 fileSize = File::size();
- assert(start <= fileSize);
- assert(start + len <= fileSize);
- _subFileStart = start;
- _subFileLen = len;
- seek(0, SEEK_SET);
-}
-
-void ScummFile::resetSubfile() {
- _subFileStart = 0;
- _subFileLen = 0;
- seek(0, SEEK_SET);
-}
-
-bool ScummFile::open(const char *filename, AccessMode mode) {
- if (File::open(filename, mode)) {
- resetSubfile();
- return true;
- } else {
- return false;
- }
-}
-
-bool ScummFile::openSubFile(const char *filename) {
- assert(isOpen());
-
- // Disable the XOR encryption and reset any current subfile range
- setEnc(0);
- resetSubfile();
-
- // Read in the filename table and look for the specified file
-
- unsigned long file_off, file_len;
- char file_name[0x20+1];
- unsigned long i;
-
- // Get the length of the data file to use for consistency checks
- const uint32 data_file_len = size();
-
- // Read offset and length to the file records */
- const uint32 file_record_off = readUint32BE();
- const uint32 file_record_len = readUint32BE();
-
- // Do a quick check to make sure the offset and length are good
- if (file_record_off + file_record_len > data_file_len) {
- return false;
- }
-
- // Do a little consistancy check on file_record_length
- if (file_record_len % 0x28) {
- return false;
- }
-
- // Scan through the files
- for (i = 0; i < file_record_len; i += 0x28) {
- // read a file record
- seek(file_record_off + i, SEEK_SET);
- file_off = readUint32BE();
- file_len = readUint32BE();
- read(file_name, 0x20);
- file_name[0x20] = 0;
-
- assert(file_name[0]);
- //debug(7, " extracting \'%s\'", file_name);
-
- // Consistency check. make sure the file data is in the file
- if (file_off + file_len > data_file_len) {
- return false;
- }
-
- if (scumm_stricmp(file_name, filename) == 0) {
- // We got a match!
- setSubfileRange(file_off, file_len);
- return true;
- }
- }
-
- return false;
-}
-
-
-bool ScummFile::eof() {
- return _subFileLen ? (pos() >= _subFileLen) : File::eof();
-}
-
-uint32 ScummFile::pos() {
- return File::pos() - _subFileStart;
-}
-
-uint32 ScummFile::size() {
- return _subFileLen ? _subFileLen : File::size();
-}
-
-void ScummFile::seek(int32 offs, int whence) {
- if (_subFileLen) {
- // Constrain the seek to the subfile
- switch (whence) {
- case SEEK_END:
- offs = _subFileStart + _subFileLen - offs;
- break;
- case SEEK_SET:
- offs += _subFileStart;
- break;
- case SEEK_CUR:
- offs += File::pos();
- break;
- }
- assert((int32)_subFileStart <= offs && offs <= (int32)(_subFileStart + _subFileLen));
- whence = SEEK_SET;
- }
- File::seek(offs, whence);
-}
-
-uint32 ScummFile::read(void *ptr, uint32 len) {
- uint32 realLen;
-
- if (_subFileLen) {
- // Limit the amount we read by the subfile boundaries.
- const uint32 curPos = pos();
- assert(_subFileLen >= curPos);
- uint32 newPos = curPos + len;
- if (newPos > _subFileLen) {
- len = _subFileLen - curPos;
- _ioFailed = true;
- }
- }
-
- realLen = File::read(ptr, len);
-
-
- // If an encryption byte was specified, XOR the data we just read by it.
- // This simple kind of "encryption" was used by some of the older SCUMM
- // games.
- if (_encbyte) {
- byte *p = (byte *)ptr;
- byte *end = p + realLen;
- while (p < end)
- *p++ ^= _encbyte;
- }
-
- return realLen;
-}
-
-uint32 ScummFile::write(const void *, uint32) {
- error("ScummFile does not support writing!");
-}
-
-
-
/* Open a room */
void ScummEngine::openRoom(int room) {
int room_offs, roomlimit;
@@ -885,1038 +726,6 @@ int ScummEngine::loadResource(int type, int idx) {
error("Cannot read resource");
}
-int ScummEngine::readSoundResource(int type, int idx) {
- uint32 pos, total_size, size, tag, basetag, max_total_size;
- int pri, best_pri;
- uint32 best_size = 0, best_offs = 0;
-
- debugC(DEBUG_RESOURCE, "readSoundResource(%s,%d)", resTypeFromId(type), idx);
-
- pos = 0;
-
- _fileHandle.readUint32LE();
- max_total_size = _fileHandle.readUint32BE() - 8;
- basetag = fileReadDword();
- total_size = _fileHandle.readUint32BE();
-
- debugC(DEBUG_RESOURCE, " basetag: %s, total_size=%d", tag2str(TO_BE_32(basetag)), total_size);
-
- if (basetag == MKID('MIDI') || basetag == MKID('iMUS')) {
- if (_midiDriver != MD_PCSPK && _midiDriver != MD_PCJR) {
- _fileHandle.seek(-8, SEEK_CUR);
- _fileHandle.read(createResource(type, idx, total_size + 8), total_size + 8);
- return 1;
- }
- } else if (basetag == MKID('SOU ')) {
- best_pri = -1;
- while (pos < total_size) {
- tag = fileReadDword();
- size = _fileHandle.readUint32BE() + 8;
- pos += size;
-
- pri = -1;
-
- switch (tag) {
- case MKID('TOWS'):
- pri = 16;
- break;
- case MKID('SBL '):
- pri = 15;
- break;
- case MKID('ADL '):
- pri = 1;
- if (_midiDriver == MD_ADLIB)
- pri = 10;
- break;
- case MKID('AMI '):
- pri = 3;
- break;
- case MKID('ROL '):
- pri = 3;
- if (_native_mt32)
- pri = 5;
- break;
- case MKID('GMD '):
- pri = 4;
- break;
- case MKID('MAC '):
- pri = 2;
- break;
- case MKID('SPK '):
- pri = -1;
-// if (_midiDriver == MD_PCSPK)
-// pri = 11;
- break;
- }
-
- if ((_midiDriver == MD_PCSPK || _midiDriver == MD_PCJR) && pri != 11)
- pri = -1;
-
- debugC(DEBUG_RESOURCE, " tag: %s, total_size=%d, pri=%d", tag2str(TO_BE_32(tag)), size, pri);
-
-
- if (pri > best_pri) {
- best_pri = pri;
- best_size = size;
- best_offs = _fileHandle.pos();
- }
-
- _fileHandle.seek(size - 8, SEEK_CUR);
- }
-
- if (best_pri != -1) {
- _fileHandle.seek(best_offs - 8, SEEK_SET);
- _fileHandle.read(createResource(type, idx, best_size), best_size);
- return 1;
- }
- } else if (basetag == MKID('Mac0')) {
- _fileHandle.seek(-12, SEEK_CUR);
- total_size = _fileHandle.readUint32BE() - 8;
- byte *ptr = (byte *)calloc(total_size, 1);
- _fileHandle.read(ptr, total_size);
-// dumpResource("sound-", idx, ptr);
- convertMac0Resource(type, idx, ptr, total_size);
- free(ptr);
- return 1;
- } else if (basetag == MKID('Mac1')) {
- _fileHandle.seek(-12, SEEK_CUR);
- total_size = _fileHandle.readUint32BE();
- _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
- return 1;
- } else if (basetag == MKID('HSHD')) {
- _fileHandle.seek(-12, SEEK_CUR);
- total_size = _fileHandle.readUint32BE();
- _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
- return 1;
- } else if (basetag == MKID('TALK')) {
- _fileHandle.seek(-12, SEEK_CUR);
- total_size = _fileHandle.readUint32BE();
- _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
- return 1;
- } else if (basetag == MKID('DIGI')) {
- _fileHandle.seek(-12, SEEK_CUR);
- total_size = _fileHandle.readUint32BE();
- _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
- return 1;
- } else if (basetag == MKID('FMUS')) {
- // Used in 3DO version of puttputt joins the parade and probably others
- // Specifies a separate file to be used for music from what I gather.
- int tmpsize;
- File dmuFile;
- char buffer[128];
- debugC(DEBUG_SOUND, "Found base tag FMUS in sound %d, size %d", idx, total_size);
- debugC(DEBUG_SOUND, "It was at position %d", _fileHandle.pos());
-
- _fileHandle.seek(4, SEEK_CUR);
- // HSHD size
- tmpsize = _fileHandle.readUint32BE();
- // skip to size part of the SDAT block
- _fileHandle.seek(tmpsize - 4, SEEK_CUR);
- // SDAT size
- tmpsize = _fileHandle.readUint32BE();
-
- // SDAT contains name of file we want
- _fileHandle.read(buffer, tmpsize - 8);
- // files seem to be 11 chars (8.3) unused space is replaced by spaces
- *(strstr(buffer, " ")) = '\0';
-
- debugC(DEBUG_SOUND, "FMUS file %s", buffer);
- if (dmuFile.open(buffer) == false) {
- warning("Can't open music file %s*", buffer);
- res.roomoffs[type][idx] = 0xFFFFFFFF;
- return 0;
- }
- dmuFile.seek(4, SEEK_SET);
- total_size = dmuFile.readUint32BE();
- debugC(DEBUG_SOUND, "dmu file size %d", total_size);
- dmuFile.seek(-8, SEEK_CUR);
- dmuFile.read(createResource(type, idx, total_size), total_size);
- dmuFile.close();
- return 1;
- } else if (basetag == MKID('Crea')) {
- _fileHandle.seek(-12, SEEK_CUR);
- total_size = _fileHandle.readUint32BE();
- _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
- return 1;
- } else if (FROM_LE_32(basetag) == max_total_size) {
- _fileHandle.seek(-12, SEEK_CUR);
- total_size = _fileHandle.readUint32BE();
- _fileHandle.seek(-8, SEEK_CUR);
- _fileHandle.read(createResource(type, idx, total_size), total_size);
- return 1;
- } else {
- warning("Unrecognized base tag 0x%08x in sound %d", basetag, idx);
- }
- res.roomoffs[type][idx] = 0xFFFFFFFF;
- return 0;
-}
-
-// Adlib MIDI-SYSEX to set MIDI instruments for small header games.
-static byte ADLIB_INSTR_MIDI_HACK[95] = {
- 0x00, 0xf0, 0x14, 0x7d, 0x00, // sysex 00: part on/off
- 0x00, 0x00, 0x03, // part/channel (offset 5)
- 0x00, 0x00, 0x07, 0x0f, 0x00, 0x00, 0x08, 0x00,
- 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xf7,
- 0x00, 0xf0, 0x41, 0x7d, 0x10, // sysex 16: set instrument
- 0x00, 0x01, // part/channel (offset 28)
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xf7,
- 0x00, 0xb0, 0x07, 0x64 // Controller 7 = 100 (offset 92)
-};
-
-static const byte map_param[7] = {
- 0, 2, 3, 4, 8, 9, 0,
-};
-
-static const byte freq2note[128] = {
- /*128*/ 6, 6, 6, 6,
- /*132*/ 7, 7, 7, 7, 7, 7, 7,
- /*139*/ 8, 8, 8, 8, 8, 8, 8, 8, 8,
- /*148*/ 9, 9, 9, 9, 9, 9, 9, 9, 9,
- /*157*/ 10, 10, 10, 10, 10, 10, 10, 10, 10,
- /*166*/ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- /*176*/ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
- /*186*/ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- /*197*/ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- /*209*/ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- /*222*/ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- /*235*/ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
- /*249*/ 18, 18, 18, 18, 18, 18, 18
-};
-
-static const uint16 num_steps_table[] = {
- 1, 2, 4, 5,
- 6, 7, 8, 9,
- 10, 12, 14, 16,
- 18, 21, 24, 30,
- 36, 50, 64, 82,
- 100, 136, 160, 192,
- 240, 276, 340, 460,
- 600, 860, 1200, 1600
-};
-
-int ScummEngine::convert_extraflags(byte * ptr, byte * src_ptr) {
- int flags = src_ptr[0];
-
- int t1, t2, t3, t4, time;
- int v1, v2, v3;
-
- if (!(flags & 0x80))
- return -1;
-
- t1 = (src_ptr[1] & 0xf0) >> 3;
- t2 = (src_ptr[2] & 0xf0) >> 3;
- t3 = (src_ptr[3] & 0xf0) >> 3 | (flags & 0x40 ? 0x80 : 0);
- t4 = (src_ptr[3] & 0x0f) << 1;
- v1 = (src_ptr[1] & 0x0f);
- v2 = (src_ptr[2] & 0x0f);
- v3 = 31;
- if ((flags & 0x7) == 0) {
- v1 = v1 + 31 + 8;
- v2 = v2 + 31 + 8;
- } else {
- v1 = v1 * 2 + 31;
- v2 = v2 * 2 + 31;
- }
-
- /* flags a */
- if ((flags & 0x7) == 6)
- ptr[0] = 0;
- else {
- ptr[0] = (flags >> 4) & 0xb;
- ptr[1] = map_param[flags & 0x7];
- }
-
- /* extra a */
- ptr[2] = 0;
- ptr[3] = 0;
- ptr[4] = t1 >> 4;
- ptr[5] = t1 & 0xf;
- ptr[6] = v1 >> 4;
- ptr[7] = v1 & 0xf;
- ptr[8] = t2 >> 4;
- ptr[9] = t2 & 0xf;
- ptr[10] = v2 >> 4;
- ptr[11] = v2 & 0xf;
- ptr[12] = t3 >> 4;
- ptr[13] = t3 & 0xf;
- ptr[14] = t4 >> 4;
- ptr[15] = t4 & 0xf;
- ptr[16] = v3 >> 4;
- ptr[17] = v3 & 0xf;
-
- time = num_steps_table[t1] + num_steps_table[t2]
- + num_steps_table[t3 & 0x7f] + num_steps_table[t4];
- if (flags & 0x20) {
- int playtime = ((src_ptr[4] >> 4) & 0xf) * 118 +
- (src_ptr[4] & 0xf) * 8;
- if (playtime > time)
- time = playtime;
- }
- /*
- time = ((src_ptr[4] >> 4) & 0xf) * 118 +
- (src_ptr[4] & 0xf) * 8;
- */
- return time;
-}
-
-#define kMIDIHeaderSize 46
-static inline byte *writeMIDIHeader(byte *ptr, const char *type, int ppqn, int total_size) {
- uint32 dw = TO_BE_32(total_size);
-
- memcpy(ptr, type, 4); ptr += 4;
- memcpy(ptr, &dw, 4); ptr += 4;
- memcpy(ptr, "MDhd", 4); ptr += 4;
- ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 8;
- ptr += 4;
- memset(ptr, 0, 8), ptr += 8;
- memcpy(ptr, "MThd", 4); ptr += 4;
- ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 6;
- ptr += 4;
- ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 1; // MIDI format 0 with 1 track
- ptr += 4;
-
- *ptr++ = ppqn >> 8;
- *ptr++ = ppqn & 0xFF;
-
- memcpy(ptr, "MTrk", 4); ptr += 4;
- memcpy(ptr, &dw, 4); ptr += 4;
-
- return ptr;
-}
-
-static inline byte *writeVLQ(byte *ptr, int value) {
- if (value > 0x7f) {
- if (value > 0x3fff) {
- *ptr++ = (value >> 14) | 0x80;
- value &= 0x3fff;
- }
- *ptr++ = (value >> 7) | 0x80;
- value &= 0x7f;
- }
- *ptr++ = value;
- return ptr;
-}
-
-static inline byte Mac0ToGMInstrument(uint32 type, int &transpose) {
- transpose = 0;
- switch (type) {
- case MKID('MARI'): return 12;
- case MKID('PLUC'): return 45;
- case MKID('HARM'): return 22;
- case MKID('PIPE'): return 19;
- case MKID('TROM'): transpose = -12; return 57;
- case MKID('STRI'): return 48;
- case MKID('HORN'): return 60;
- case MKID('VIBE'): return 11;
- case MKID('SHAK'): return 77;
- case MKID('PANP'): return 75;
- case MKID('WHIS'): return 76;
- case MKID('ORGA'): return 17;
- case MKID('BONG'): return 115;
- case MKID('BASS'): transpose = -24; return 35;
- default:
- error("Unknown Mac0 instrument %s found", tag2str(type));
- }
-}
-
-void ScummEngine::convertMac0Resource(int type, int idx, byte *src_ptr, int size) {
- /*
- From Markus Magnuson (superqult) we got this information:
- Mac0
- ---
- 4 bytes - 'SOUN'
- BE 4 bytes - block length
-
- 4 bytes - 'Mac0'
- BE 4 bytes - (blockLength - 27)
- 28 bytes - ???
-
- do this three times (once for each channel):
- 4 bytes - 'Chan'
- BE 4 bytes - channel length
- 4 bytes - instrument name (e.g. 'MARI')
-
- do this for ((chanLength-24)/4) times:
- 2 bytes - note duration
- 1 byte - note value
- 1 byte - note velocity
-
- 4 bytes - ???
- 4 bytes - 'Loop'/'Done'
- 4 bytes - ???
-
- 1 byte - 0x09
- ---
-
- Instruments (General Midi):
- "MARI" - Marimba (12)
- "PLUC" - Pizzicato Strings (45)
- "HARM" - Harmonica (22)
- "PIPE" - Church Organ? (19) or Flute? (73) or Bag Pipe (109)
- "TROM" - Trombone (57)
- "STRI" - String Ensemble (48 or 49)
- "HORN" - French Horn? (60) or English Horn? (69)
- "VIBE" - Vibraphone (11)
- "SHAK" - Shakuhachi? (77)
- "PANP" - Pan Flute (75)
- "WHIS" - Whistle (78) / Bottle (76)
- "ORGA" - Drawbar Organ (16; but could also be 17-20)
- "BONG" - Woodblock? (115)
- "BASS" - Bass (32-39)
-
-
- Now the task could be to convert this into MIDI, to be fed into iMuse.
- Or we do something similiar to what is done in Player_V3, assuming
- we can identify SFX in the MI datafiles for each of the instruments
- listed above.
- */
-
-#if 0
- byte *ptr = createResource(type, idx, size);
- memcpy(ptr, src_ptr, size);
-#else
- const int ppqn = 480;
- byte *ptr, *start_ptr;
-
- int total_size = 0;
- total_size += kMIDIHeaderSize; // Header
- total_size += 7; // Tempo META
- total_size += 3 * 3; // Three program change mesages
- total_size += 22; // Possible jump SysEx
- total_size += 5; // EOT META
-
- int i, len;
- byte track_instr[3];
- byte *track_data[3];
- int track_len[3];
- int track_transpose[3];
- bool looped = false;
-
- src_ptr += 8;
- // TODO: Decipher the unknown bytes in the header. For now, skip 'em
- src_ptr += 28;
-
- // Parse the three channels
- for (i = 0; i < 3; i++) {
- assert(*((uint32*)src_ptr) == MKID('Chan'));
- len = READ_BE_UINT32(src_ptr + 4);
- track_len[i] = len - 24;
- track_instr[i] = Mac0ToGMInstrument(*(uint32*)(src_ptr + 8), track_transpose[i]);
- track_data[i] = src_ptr + 12;
- src_ptr += len;
- looped = (*((uint32*)(src_ptr - 8)) == MKID('Loop'));
-
- // For each note event, we need up to 6 bytes for the
- // Note On (3 VLQ, 3 event), and 6 bytes for the Note
- // Off (3 VLQ, 3 event). So 12 bytes total.
- total_size += 12 * track_len[i];
- }
- assert(*src_ptr == 0x09);
-
- // Create sound resource
- start_ptr = createResource(type, idx, total_size);
-
- // Insert MIDI header
- ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
-
- // Write a tempo change Meta event
- // 473 / 4 Hz, convert to micro seconds.
- uint32 dw = 1000000 * 437 / 4 / ppqn; // 1000000 * ppqn * 4 / 473;
- memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
- *ptr++ = (byte)((dw >> 16) & 0xFF);
- *ptr++ = (byte)((dw >> 8) & 0xFF);
- *ptr++ = (byte)(dw & 0xFF);
-
- // Insert program change messages
- *ptr++ = 0; // VLQ
- *ptr++ = 0xC0;
- *ptr++ = track_instr[0];
- *ptr++ = 0; // VLQ
- *ptr++ = 0xC1;
- *ptr++ = track_instr[1];
- *ptr++ = 0; // VLQ
- *ptr++ = 0xC2;
- *ptr++ = track_instr[2];
-
- // And now, the actual composition. Please turn all cell phones
- // and pagers off during the performance. Thank you.
- uint16 nextTime[3] = { 1, 1, 1 };
- int stage[3] = { 0, 0, 0 };
-
- while (track_len[0] | track_len[1] | track_len[2]) {
- int best = -1;
- uint16 bestTime = 0xFFFF;
- for (i = 0; i < 3; ++i) {
- if (track_len[i] && nextTime[i] < bestTime) {
- bestTime = nextTime[i];
- best = i;
- }
- }
- assert (best != -1);
-
- if (!stage[best]) {
- // We are STARTING this event.
- if (track_data[best][2] > 1) {
- // Note On
- ptr = writeVLQ(ptr, nextTime[best]);
- *ptr++ = 0x90 | best;
- *ptr++ = track_data[best][2] + track_transpose[best];
- *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
- for (i = 0; i < 3; ++i)
- nextTime[i] -= bestTime;
- }
- nextTime[best] += READ_BE_UINT16 (track_data[best]);
- stage[best] = 1;
- } else {
- // We are ENDING this event.
- if (track_data[best][2] > 1) {
- // There was a Note On, so do a Note Off
- ptr = writeVLQ(ptr, nextTime[best]);
- *ptr++ = 0x80 | best;
- *ptr++ = track_data[best][2] + track_transpose[best];
- *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
- for (i = 0; i < 3; ++i)
- nextTime[i] -= bestTime;
- }
- track_data[best] += 4;
- track_len[best] -= 4;
- stage[best] = 0;
- }
- }
-
- // Is this a looped song? If so, effect a loop by
- // using the S&M maybe_jump SysEx command.
- // FIXME: Jamieson630: The jump seems to be happening
- // too quickly! There should maybe be a pause after
- // the last Note Off? But I couldn't find one in the
- // MI1 Lookout music, where I was hearing problems.
- if (looped) {
- memcpy(ptr, "\x00\xf0\x13\x7d\x30\00", 6); ptr += 6; // maybe_jump
- memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump
- memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> 0 (only track)
- memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> 1 (first beat)
- memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // tick -> 1
- memcpy(ptr, "\x00\xf7", 2); ptr += 2; // SysEx end marker
- }
-
- // Insert end of song META
- memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
-
- assert(ptr <= start_ptr + total_size);
-
- // Rewrite MIDI header, this time with true size
- total_size = ptr - start_ptr;
- ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
-#endif
-}
-
-void ScummEngine::convertADResource(int type, int idx, byte *src_ptr, int size) {
-
- // We will ignore the PPQN in the original resource, because
- // it's invalid anyway. We use a constant PPQN of 480.
- const int ppqn = 480;
- uint32 dw;
- int i, ch;
- byte *ptr;
- int total_size = kMIDIHeaderSize + 7 + 8 * sizeof(ADLIB_INSTR_MIDI_HACK) + size;
- total_size += 24; // Up to 24 additional bytes are needed for the jump sysex
-
- ptr = createResource(type, idx, total_size);
-
- src_ptr += 2;
- size -= 2;
-
- // 0x80 marks a music resource. Otherwise it's a SFX
- if (*src_ptr == 0x80) {
- byte ticks, play_once;
- byte num_instr;
- byte *channel, *instr, *track;
-
- ptr = writeMIDIHeader(ptr, "ADL ", ppqn, total_size);
-
- // The "speed" of the song
- ticks = *(src_ptr + 1);
-
- // Flag that tells us whether we should loop the song (0) or play it only once (1)
- play_once = *(src_ptr + 2);
-
- // Number of instruments used
- num_instr = *(src_ptr + 8); // Normally 8
-
- // copy the pointer to instrument data
- channel = src_ptr + 9;
- instr = src_ptr + 0x11;
-
- // skip over the rest of the header and copy the MIDI data into a buffer
- src_ptr += 0x11 + 8 * 16;
- size -= 0x11 + 8 * 16;
-
- CHECK_HEAP
-
- track = src_ptr;
-
- // Convert the ticks into a MIDI tempo.
- // Unfortunate LOOM and INDY3 have different interpretation
- // of the ticks value.
- if (_gameId == GID_INDY3) {
- // Note: since we fix ppqn at 480, ppqn/473 is almost 1
- dw = 500000 * 256 / 473 * ppqn / ticks;
- } else if (_gameId == GID_LOOM) {
- dw = 500000 * ppqn / 4 / ticks;
- } else {
- dw = 500000 * 256 / ticks;
- }
- debugC(DEBUG_SOUND, " ticks = %d, speed = %ld", ticks, dw);
-
- // Write a tempo change Meta event
- memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
- *ptr++ = (byte)((dw >> 16) & 0xFF);
- *ptr++ = (byte)((dw >> 8) & 0xFF);
- *ptr++ = (byte)(dw & 0xFF);
-
- // Copy our hardcoded instrument table into it
- // Then, convert the instrument table as given in this song resource
- // And write it *over* the hardcoded table.
- // Note: we deliberately.
-
- /* now fill in the instruments */
- for (i = 0; i < num_instr; i++) {
- ch = channel[i] - 1;
- if (ch < 0 || ch > 15)
- continue;
-
- if (instr[i*16 + 13])
- warning("Sound %d instrument %d uses percussion", idx, i);
-
- debugC(DEBUG_SOUND, "Sound %d: instrument %d on channel %d.", idx, i, ch);
-
- memcpy(ptr, ADLIB_INSTR_MIDI_HACK, sizeof(ADLIB_INSTR_MIDI_HACK));
-
- ptr[5] += ch;
- ptr[28] += ch;
- ptr[92] += ch;
-
- /* flags_1 */
- ptr[30 + 0] = (instr[i * 16 + 3] >> 4) & 0xf;
- ptr[30 + 1] = instr[i * 16 + 3] & 0xf;
-
- /* oplvl_1 */
- ptr[30 + 2] = (instr[i * 16 + 4] >> 4) & 0xf;
- ptr[30 + 3] = instr[i * 16 + 4] & 0xf;
-
- /* atdec_1 */
- ptr[30 + 4] = ((~instr[i * 16 + 5]) >> 4) & 0xf;
- ptr[30 + 5] = (~instr[i * 16 + 5]) & 0xf;
-
- /* sustrel_1 */
- ptr[30 + 6] = ((~instr[i * 16 + 6]) >> 4) & 0xf;
- ptr[30 + 7] = (~instr[i * 16 + 6]) & 0xf;
-
- /* waveform_1 */
- ptr[30 + 8] = (instr[i * 16 + 7] >> 4) & 0xf;
- ptr[30 + 9] = instr[i * 16 + 7] & 0xf;
-
- /* flags_2 */
- ptr[30 + 10] = (instr[i * 16 + 8] >> 4) & 0xf;
- ptr[30 + 11] = instr[i * 16 + 8] & 0xf;
-
- /* oplvl_2 */
- ptr[30 + 12] = (instr[i * 16 + 9] >> 4) & 0xf;
- ptr[30 + 13] = instr[i * 16 + 9] & 0xf;
-
- /* atdec_2 */
- ptr[30 + 14] = ((~instr[i * 16 + 10]) >> 4) & 0xf;
- ptr[30 + 15] = (~instr[i * 16 + 10]) & 0xf;
-
- /* sustrel_2 */
- ptr[30 + 16] = ((~instr[i * 16 + 11]) >> 4) & 0xf;
- ptr[30 + 17] = (~instr[i * 16 + 11]) & 0xf;
-
- /* waveform_2 */
- ptr[30 + 18] = (instr[i * 16 + 12] >> 4) & 0xf;
- ptr[30 + 19] = instr[i * 16 + 12] & 0xf;
-
- /* feedback */
- ptr[30 + 20] = (instr[i * 16 + 2] >> 4) & 0xf;
- ptr[30 + 21] = instr[i * 16 + 2] & 0xf;
- ptr += sizeof(ADLIB_INSTR_MIDI_HACK);
- }
-
- // There is a constant delay of ppqn/3 before the music starts.
- if (ppqn / 3 >= 128)
- *ptr++ = (ppqn / 3 >> 7) | 0x80;
- *ptr++ = ppqn / 3 & 0x7f;
-
- // Now copy the actual music data
- memcpy(ptr, track, size);
- ptr += size;
-
- if (!play_once) {
- // The song is meant to be looped. We achieve this by inserting just
- // before the song end a jump to the song start. More precisely we abuse
- // a S&M sysex, "maybe_jump" to achieve this effect. We could also
- // use a set_loop sysex, but it's a bit longer, a little more complicated,
- // and has no advantage either.
-
- // First, find the track end
- byte *end = ptr;
- ptr -= size;
- for (; ptr < end; ptr++) {
- if (*ptr == 0xff && *(ptr + 1) == 0x2f)
- break;
- }
- assert(ptr < end);
-
- // Now insert the jump. The jump offset is measured in ticks.
- // We have ppqn/3 ticks before the first note.
-
- const int jump_offset = ppqn / 3;
- memcpy(ptr, "\xf0\x13\x7d\x30\00", 5); ptr += 5; // maybe_jump
- memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump
- memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> there is only one track, 0
- memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> for now, 1 (first beat)
- // Ticks
- *ptr++ = (byte)((jump_offset >> 12) & 0x0F);
- *ptr++ = (byte)((jump_offset >> 8) & 0x0F);
- *ptr++ = (byte)((jump_offset >> 4) & 0x0F);
- *ptr++ = (byte)(jump_offset & 0x0F);
- memcpy(ptr, "\x00\xf7", 2); ptr += 2; // sysex end marker
- }
- } else {
-
- /* This is a sfx resource. First parse it quickly to find the parallel
- * tracks.
- */
- ptr = writeMIDIHeader(ptr, "ASFX", ppqn, total_size);
-
- byte current_instr[3][14];
- int current_note[3];
- int track_time[3];
- byte *track_data[3];
-
- int track_ctr = 0;
- byte chunk_type = 0;
- int delay, delay2, olddelay;
-
- // Write a tempo change Meta event
- // 473 / 4 Hz, convert to micro seconds.
- dw = 1000000 * ppqn * 4 / 473;
- memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
- *ptr++ = (byte)((dw >> 16) & 0xFF);
- *ptr++ = (byte)((dw >> 8) & 0xFF);
- *ptr++ = (byte)(dw & 0xFF);
-
- for (i = 0; i < 3; i++) {
- track_time[i] = -1;
- current_note[i] = -1;
- }
- while (size > 0) {
- assert(track_ctr < 3);
- track_data[track_ctr] = src_ptr;
- track_time[track_ctr] = 0;
- track_ctr++;
- while (size > 0) {
- chunk_type = *(src_ptr);
- if (chunk_type == 1) {
- src_ptr += 15;
- size -= 15;
- } else if (chunk_type == 2) {
- src_ptr += 11;
- size -= 11;
- } else if (chunk_type == 0x80) {
- src_ptr ++;
- size --;
- } else {
- break;
- }
- }
- if (chunk_type == 0xff)
- break;
- src_ptr++;
- }
-
- int curtime = 0;
- for (;;) {
- int mintime = -1;
- ch = -1;
- for (i = 0; i < 3; i++) {
- if (track_time[i] >= 0 &&
- (mintime == -1 || mintime > track_time[i])) {
- mintime = track_time[i];
- ch = i;
- }
- }
- if (mintime < 0)
- break;
-
- src_ptr = track_data[ch];
- chunk_type = *src_ptr;
-
- if (current_note[ch] >= 0) {
- delay = mintime - curtime;
- curtime = mintime;
- ptr = writeVLQ(ptr, delay);
- *ptr++ = 0x80 + ch; // key off channel;
- *ptr++ = current_note[ch];
- *ptr++ = 0;
- current_note[ch] = -1;
- }
-
- switch (chunk_type) {
- case 1:
- /* Instrument definition */
- memcpy(current_instr[ch], src_ptr + 1, 14);
- src_ptr += 15;
- break;
-
- case 2:
- /* tone/parammodulation */
- memcpy(ptr, ADLIB_INSTR_MIDI_HACK,
- sizeof(ADLIB_INSTR_MIDI_HACK));
-
- ptr[5] += ch;
- ptr[28] += ch;
- ptr[92] += ch;
-
- /* flags_1 */
- ptr[30 + 0] = (current_instr[ch][3] >> 4) & 0xf;
- ptr[30 + 1] = current_instr[ch][3] & 0xf;
-
- /* oplvl_1 */
- ptr[30 + 2] = (current_instr[ch][4] >> 4) & 0xf;
- ptr[30 + 3] = current_instr[ch][4] & 0xf;
-
- /* atdec_1 */
- ptr[30 + 4] = ((~current_instr[ch][5]) >> 4) & 0xf;
- ptr[30 + 5] = (~current_instr[ch][5]) & 0xf;
-
- /* sustrel_1 */
- ptr[30 + 6] = ((~current_instr[ch][6]) >> 4) & 0xf;
- ptr[30 + 7] = (~current_instr[ch][6]) & 0xf;
-
- /* waveform_1 */
- ptr[30 + 8] = (current_instr[ch][7] >> 4) & 0xf;
- ptr[30 + 9] = current_instr[ch][7] & 0xf;
-
- /* flags_2 */
- ptr[30 + 10] = (current_instr[ch][8] >> 4) & 0xf;
- ptr[30 + 11] = current_instr[ch][8] & 0xf;
-
- /* oplvl_2 */
- ptr[30 + 12] = ((current_instr[ch][9]) >> 4) & 0xf;
- ptr[30 + 13] = (current_instr[ch][9]) & 0xf;
-
- /* atdec_2 */
- ptr[30 + 14] = ((~current_instr[ch][10]) >> 4) & 0xf;
- ptr[30 + 15] = (~current_instr[ch][10]) & 0xf;
-
- /* sustrel_2 */
- ptr[30 + 16] = ((~current_instr[ch][11]) >> 4) & 0xf;
- ptr[30 + 17] = (~current_instr[ch][11]) & 0xf;
-
- /* waveform_2 */
- ptr[30 + 18] = (current_instr[ch][12] >> 4) & 0xf;
- ptr[30 + 19] = current_instr[ch][12] & 0xf;
-
- /* feedback */
- ptr[30 + 20] = (current_instr[ch][2] >> 4) & 0xf;
- ptr[30 + 21] = current_instr[ch][2] & 0xf;
-
- delay = mintime - curtime;
- curtime = mintime;
-
- {
- delay = convert_extraflags(ptr + 30 + 22, src_ptr + 1);
- delay2 = convert_extraflags(ptr + 30 + 40, src_ptr + 6);
- debugC(DEBUG_SOUND, "delays: %d / %d", delay, delay2);
- if (delay2 >= 0 && delay2 < delay)
- delay = delay2;
- if (delay == -1)
- delay = 0;
- }
-
- /* duration */
- ptr[30 + 58] = 0; // ((delay * 17 / 63) >> 4) & 0xf;
- ptr[30 + 59] = 0; // (delay * 17 / 63) & 0xf;
-
- ptr += sizeof(ADLIB_INSTR_MIDI_HACK);
-
- olddelay = mintime - curtime;
- curtime = mintime;
- ptr = writeVLQ(ptr, olddelay);
-
- {
- int freq = ((current_instr[ch][1] & 3) << 8)
- | current_instr[ch][0];
- if (!freq)
- freq = 0x80;
- freq <<= ((current_instr[ch][1] >> 2) & 7) + 1;
- int note = -11;
- while (freq >= 0x100) {
- note += 12;
- freq >>= 1;
- }
- debugC(DEBUG_SOUND, "Freq: %d (%x) Note: %d", freq, freq, note);
- if (freq < 0x80)
- note = 0;
- else
- note += freq2note[freq - 0x80];
-
- debugC(DEBUG_SOUND, "Note: %d", note);
- if (note <= 0)
- note = 1;
- else if (note > 127)
- note = 127;
-
- // Insert a note on event
- *ptr++ = 0x90 + ch; // key on channel
- *ptr++ = note;
- *ptr++ = 63;
- current_note[ch] = note;
- track_time[ch] = curtime + delay;
- }
- src_ptr += 11;
- break;
-
- case 0x80:
- track_time[ch] = -1;
- src_ptr ++;
- break;
-
- default:
- track_time[ch] = -1;
- }
- track_data[ch] = src_ptr;
- }
- }
-
- // Insert end of song sysex
- memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
-}
-
-
-int ScummEngine::readSoundResourceSmallHeader(int type, int idx) {
- uint32 pos, total_size, size, tag;
- uint32 ad_size = 0, ad_offs = 0;
- uint32 ro_size = 0, ro_offs = 0;
- uint32 wa_size = 0, wa_offs = 0;
-
- debug(4, "readSoundResourceSmallHeader(%s,%d)", resTypeFromId(type), idx);
-
- if ((_gameId == GID_LOOM) && (_features & GF_PC) && VAR(VAR_SOUNDCARD) == 4) {
- // Roland resources in Loom are tagless
- // So we add an RO tag to allow imuse to detect format
- byte *ptr, *src_ptr;
- ro_offs = _fileHandle.pos();
- ro_size = _fileHandle.readUint16LE();
-
- src_ptr = (byte *) calloc(ro_size - 4, 1);
- _fileHandle.seek(ro_offs + 4, SEEK_SET);
- _fileHandle.read(src_ptr, ro_size -4);
-
- ptr = createResource(type, idx, ro_size + 2);
- memcpy(ptr, "RO", 2); ptr += 2;
- memcpy(ptr, src_ptr, ro_size - 4); ptr += ro_size - 4;
- return 1;
- } else if (_features & GF_OLD_BUNDLE) {
- wa_offs = _fileHandle.pos();
- wa_size = _fileHandle.readUint16LE();
- _fileHandle.seek(wa_size - 2, SEEK_CUR);
-
- if (!(_features & GF_ATARI_ST || _features & GF_MACINTOSH)) {
- ad_offs = _fileHandle.pos();
- ad_size = _fileHandle.readUint16LE();
- }
- _fileHandle.seek(4, SEEK_CUR);
- total_size = wa_size + ad_size;
- } else {
- total_size = size = _fileHandle.readUint32LE();
- tag = _fileHandle.readUint16LE();
- debug(4, " tag='%c%c', size=%d", (char) (tag & 0xff),
- (char) ((tag >> 8) & 0xff), size);
-
- if (tag == 0x4F52) { // RO
- ro_offs = _fileHandle.pos();
- ro_size = size;
- } else {
- pos = 6;
- while (pos < total_size) {
- size = _fileHandle.readUint32LE();
- tag = _fileHandle.readUint16LE();
- debug(4, " tag='%c%c', size=%d", (char) (tag & 0xff),
- (char) ((tag >> 8) & 0xff), size);
- pos += size;
-
- // MI1 and Indy3 uses one or more nested SO resources, which contains AD and WA
- // resources.
- if ((tag == 0x4441) && !(ad_offs)) { // AD
- ad_size = size;
- ad_offs = _fileHandle.pos();
- } else if ((tag == 0x4157) && !(wa_offs)) { // WA
- wa_size = size;
- wa_offs = _fileHandle.pos();
- } else { // other AD, WA and nested SO resources
- if (tag == 0x4F53) { // SO
- pos -= size;
- size = 6;
- pos += 6;
- }
- }
- _fileHandle.seek(size - 6, SEEK_CUR);
- }
- }
- }
-
- if ((_midiDriver == MD_ADLIB) && ad_offs != 0) {
- // AD resources have a header, instrument definitions and one MIDI track.
- // We build an 'ADL ' resource from that:
- // 8 bytes resource header
- // 16 bytes MDhd header
- // 14 bytes MThd header
- // 8 bytes MTrk header
- // 7 bytes MIDI tempo sysex
- // + some default instruments
- byte *ptr;
- if (_features & GF_OLD_BUNDLE) {
- ptr = (byte *) calloc(ad_size - 4, 1);
- _fileHandle.seek(ad_offs + 4, SEEK_SET);
- _fileHandle.read(ptr, ad_size - 4);
- convertADResource(type, idx, ptr, ad_size - 4);
- free(ptr);
- return 1;
- } else {
- ptr = (byte *) calloc(ad_size - 6, 1);
- _fileHandle.seek(ad_offs, SEEK_SET);
- _fileHandle.read(ptr, ad_size - 6);
- convertADResource(type, idx, ptr, ad_size - 6);
- free(ptr);
- return 1;
- }
- } else if (((_midiDriver == MD_PCJR) || (_midiDriver == MD_PCSPK)) && wa_offs != 0) {
- if (_features & GF_OLD_BUNDLE) {
- _fileHandle.seek(wa_offs, SEEK_SET);
- _fileHandle.read(createResource(type, idx, wa_size), wa_size);
- } else {
- _fileHandle.seek(wa_offs - 6, SEEK_SET);
- _fileHandle.read(createResource(type, idx, wa_size + 6), wa_size + 6);
- }
- return 1;
- } else if (ro_offs != 0) {
- _fileHandle.seek(ro_offs - 2, SEEK_SET);
- _fileHandle.read(createResource(type, idx, ro_size - 4), ro_size - 4);
- return 1;
- }
- res.roomoffs[type][idx] = 0xFFFFFFFF;
- return 0;
-}
-
int ScummEngine::getResourceRoomNr(int type, int idx) {
if (type == rtRoom && _heversion < 70)
return idx;
diff --git a/scumm/scumm.cpp b/scumm/scumm.cpp
index 18f746f73c..d3a5ad94d7 100644
--- a/scumm/scumm.cpp
+++ b/scumm/scumm.cpp
@@ -2545,91 +2545,6 @@ void ScummEngine::errorString(const char *buf1, char *buf2) {
}
}
-#pragma mark -
-#pragma mark --- Utilities ---
-#pragma mark -
-
-void checkRange(int max, int min, int no, const char *str) {
- if (no < min || no > max) {
- char buf[256];
- snprintf(buf, sizeof(buf), str, no);
- error("Value %d is out of bounds (%d,%d) (%s)", no, min, max, buf);
- }
-}
-
-/**
- * Convert an old style direction to a new style one (angle),
- */
-int newDirToOldDir(int dir) {
- if (dir >= 71 && dir <= 109)
- return 1;
- if (dir >= 109 && dir <= 251)
- return 2;
- if (dir >= 251 && dir <= 289)
- return 0;
- return 3;
-}
-
-/**
- * Convert an new style (angle) direction to an old style one.
- */
-int oldDirToNewDir(int dir) {
- assert(0 <= dir && dir <= 3);
- const int new_dir_table[4] = { 270, 90, 180, 0 };
- return new_dir_table[dir];
-}
-
-/**
- * Convert an angle to a simple direction.
- */
-int toSimpleDir(int dirType, int dir) {
- if (dirType) {
- const int16 directions[] = { 22, 72, 107, 157, 202, 252, 287, 337 };
- for (int i = 0; i < 7; i++)
- if (dir >= directions[i] && dir <= directions[i+1])
- return i+1;
- } else {
- const int16 directions[] = { 71, 109, 251, 289 };
- for (int i = 0; i < 3; i++)
- if (dir >= directions[i] && dir <= directions[i+1])
- return i+1;
- }
-
- return 0;
-}
-
-/**
- * Convert a simple direction to an angle.
- */
-int fromSimpleDir(int dirType, int dir) {
- if (dirType)
- return dir * 45;
- else
- return dir * 90;
-}
-
-/**
- * Normalize the given angle - that means, ensure it is positive, and
- * change it to the closest multiple of 45 degree by abusing toSimpleDir.
- */
-int normalizeAngle(int angle) {
- int temp;
-
- temp = (angle + 360) % 360;
-
- return toSimpleDir(1, temp) * 45;
-}
-
-const char *tag2str(uint32 tag) {
- static char str[5];
- str[0] = (char)(tag >> 24);
- str[1] = (char)(tag >> 16);
- str[2] = (char)(tag >> 8);
- str[3] = (char)tag;
- str[4] = '\0';
- return str;
-}
-
} // End of namespace Scumm
using namespace Scumm;
diff --git a/scumm/scumm.h b/scumm/scumm.h
index 79ece5d928..df1fddfa7b 100644
--- a/scumm/scumm.h
+++ b/scumm/scumm.h
@@ -31,6 +31,7 @@
#include "scumm/gfx.h"
#include "scumm/script.h"
+#include "scumm/util.h"
#include "scumm/wiz_he.h"
namespace GUI {
@@ -61,29 +62,6 @@ struct ScummGameSettings;
typedef Common::Map<Common::String, int> ObjectIDMap;
-class ScummFile : public File {
-private:
- byte _encbyte;
- uint32 _subFileStart;
- uint32 _subFileLen;
-public:
- ScummFile();
- void setEnc(byte value);
-
- void setSubfileRange(uint32 start, uint32 len);
- void resetSubfile();
-
- bool open(const char *filename, AccessMode mode = kFileReadMode);
- bool openSubFile(const char *filename);
-
- bool eof();
- uint32 pos();
- uint32 size();
- void seek(int32 offs, int whence = SEEK_SET);
- uint32 read(void *ptr, uint32 size);
- uint32 write(const void *ptr, uint32 size);
-};
-
// Use g_scumm from error() ONLY
extern ScummEngine *g_scumm;
@@ -1333,21 +1311,6 @@ public:
byte VAR_WIZ_TCOLOR;
};
-// This is a constant lookup table of reverse bit masks
-extern const byte revBitMask[8];
-
-/* Direction conversion functions (between old dir and new dir format) */
-int newDirToOldDir(int dir);
-int oldDirToNewDir(int dir);
-
-int normalizeAngle(int angle);
-int fromSimpleDir(int dirtype, int dir);
-int toSimpleDir(int dirtype, int dir);
-
-void checkRange(int max, int min, int no, const char *str);
-
-const char *tag2str(uint32 tag);
-
} // End of namespace Scumm
#endif
diff --git a/scumm/sound.cpp b/scumm/sound.cpp
index 720909480b..a54f45d957 100644
--- a/scumm/sound.cpp
+++ b/scumm/sound.cpp
@@ -1195,4 +1195,1050 @@ const SaveLoadEntry *Sound::getSaveLoadEntries() {
return soundEntries;
}
+
+#pragma mark -
+#pragma mark --- Sound resource handling ---
+#pragma mark -
+
+/*
+ * TODO: The way we handle sound/music resources really is one huge hack.
+ * We probably should reconsider how we do this, and maybe come up with a
+ * better/cleaner solution. Even if we keep the existing code, it really
+ * could stand a thorough cleanup!
+ */
+
+
+int ScummEngine::readSoundResource(int type, int idx) {
+ uint32 pos, total_size, size, tag, basetag, max_total_size;
+ int pri, best_pri;
+ uint32 best_size = 0, best_offs = 0;
+
+ debugC(DEBUG_RESOURCE, "readSoundResource(%d)", idx);
+
+ pos = 0;
+
+ _fileHandle.readUint32LE();
+ max_total_size = _fileHandle.readUint32BE() - 8;
+ basetag = fileReadDword();
+ total_size = _fileHandle.readUint32BE();
+
+ debugC(DEBUG_RESOURCE, " basetag: %s, total_size=%d", tag2str(TO_BE_32(basetag)), total_size);
+
+ if (basetag == MKID('MIDI') || basetag == MKID('iMUS')) {
+ if (_midiDriver != MD_PCSPK && _midiDriver != MD_PCJR) {
+ _fileHandle.seek(-8, SEEK_CUR);
+ _fileHandle.read(createResource(type, idx, total_size + 8), total_size + 8);
+ return 1;
+ }
+ } else if (basetag == MKID('SOU ')) {
+ best_pri = -1;
+ while (pos < total_size) {
+ tag = fileReadDword();
+ size = _fileHandle.readUint32BE() + 8;
+ pos += size;
+
+ pri = -1;
+
+ switch (tag) {
+ case MKID('TOWS'):
+ pri = 16;
+ break;
+ case MKID('SBL '):
+ pri = 15;
+ break;
+ case MKID('ADL '):
+ pri = 1;
+ if (_midiDriver == MD_ADLIB)
+ pri = 10;
+ break;
+ case MKID('AMI '):
+ pri = 3;
+ break;
+ case MKID('ROL '):
+ pri = 3;
+ if (_native_mt32)
+ pri = 5;
+ break;
+ case MKID('GMD '):
+ pri = 4;
+ break;
+ case MKID('MAC '):
+ pri = 2;
+ break;
+ case MKID('SPK '):
+ pri = -1;
+// if (_midiDriver == MD_PCSPK)
+// pri = 11;
+ break;
+ }
+
+ if ((_midiDriver == MD_PCSPK || _midiDriver == MD_PCJR) && pri != 11)
+ pri = -1;
+
+ debugC(DEBUG_RESOURCE, " tag: %s, total_size=%d, pri=%d", tag2str(TO_BE_32(tag)), size, pri);
+
+
+ if (pri > best_pri) {
+ best_pri = pri;
+ best_size = size;
+ best_offs = _fileHandle.pos();
+ }
+
+ _fileHandle.seek(size - 8, SEEK_CUR);
+ }
+
+ if (best_pri != -1) {
+ _fileHandle.seek(best_offs - 8, SEEK_SET);
+ _fileHandle.read(createResource(type, idx, best_size), best_size);
+ return 1;
+ }
+ } else if (basetag == MKID('Mac0')) {
+ _fileHandle.seek(-12, SEEK_CUR);
+ total_size = _fileHandle.readUint32BE() - 8;
+ byte *ptr = (byte *)calloc(total_size, 1);
+ _fileHandle.read(ptr, total_size);
+// dumpResource("sound-", idx, ptr);
+ convertMac0Resource(type, idx, ptr, total_size);
+ free(ptr);
+ return 1;
+ } else if (basetag == MKID('Mac1')) {
+ _fileHandle.seek(-12, SEEK_CUR);
+ total_size = _fileHandle.readUint32BE();
+ _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
+ return 1;
+ } else if (basetag == MKID('HSHD')) {
+ _fileHandle.seek(-12, SEEK_CUR);
+ total_size = _fileHandle.readUint32BE();
+ _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
+ return 1;
+ } else if (basetag == MKID('TALK')) {
+ _fileHandle.seek(-12, SEEK_CUR);
+ total_size = _fileHandle.readUint32BE();
+ _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
+ return 1;
+ } else if (basetag == MKID('DIGI')) {
+ _fileHandle.seek(-12, SEEK_CUR);
+ total_size = _fileHandle.readUint32BE();
+ _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
+ return 1;
+ } else if (basetag == MKID('FMUS')) {
+ // Used in 3DO version of puttputt joins the parade and probably others
+ // Specifies a separate file to be used for music from what I gather.
+ int tmpsize;
+ File dmuFile;
+ char buffer[128];
+ debugC(DEBUG_SOUND, "Found base tag FMUS in sound %d, size %d", idx, total_size);
+ debugC(DEBUG_SOUND, "It was at position %d", _fileHandle.pos());
+
+ _fileHandle.seek(4, SEEK_CUR);
+ // HSHD size
+ tmpsize = _fileHandle.readUint32BE();
+ // skip to size part of the SDAT block
+ _fileHandle.seek(tmpsize - 4, SEEK_CUR);
+ // SDAT size
+ tmpsize = _fileHandle.readUint32BE();
+
+ // SDAT contains name of file we want
+ _fileHandle.read(buffer, tmpsize - 8);
+ // files seem to be 11 chars (8.3) unused space is replaced by spaces
+ *(strstr(buffer, " ")) = '\0';
+
+ debugC(DEBUG_SOUND, "FMUS file %s", buffer);
+ if (dmuFile.open(buffer) == false) {
+ warning("Can't open music file %s*", buffer);
+ res.roomoffs[type][idx] = 0xFFFFFFFF;
+ return 0;
+ }
+ dmuFile.seek(4, SEEK_SET);
+ total_size = dmuFile.readUint32BE();
+ debugC(DEBUG_SOUND, "dmu file size %d", total_size);
+ dmuFile.seek(-8, SEEK_CUR);
+ dmuFile.read(createResource(type, idx, total_size), total_size);
+ dmuFile.close();
+ return 1;
+ } else if (basetag == MKID('Crea')) {
+ _fileHandle.seek(-12, SEEK_CUR);
+ total_size = _fileHandle.readUint32BE();
+ _fileHandle.read(createResource(type, idx, total_size), total_size - 8);
+ return 1;
+ } else if (FROM_LE_32(basetag) == max_total_size) {
+ _fileHandle.seek(-12, SEEK_CUR);
+ total_size = _fileHandle.readUint32BE();
+ _fileHandle.seek(-8, SEEK_CUR);
+ _fileHandle.read(createResource(type, idx, total_size), total_size);
+ return 1;
+ } else {
+ warning("Unrecognized base tag 0x%08x in sound %d", basetag, idx);
+ }
+ res.roomoffs[type][idx] = 0xFFFFFFFF;
+ return 0;
+}
+
+// Adlib MIDI-SYSEX to set MIDI instruments for small header games.
+static byte ADLIB_INSTR_MIDI_HACK[95] = {
+ 0x00, 0xf0, 0x14, 0x7d, 0x00, // sysex 00: part on/off
+ 0x00, 0x00, 0x03, // part/channel (offset 5)
+ 0x00, 0x00, 0x07, 0x0f, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xf7,
+ 0x00, 0xf0, 0x41, 0x7d, 0x10, // sysex 16: set instrument
+ 0x00, 0x01, // part/channel (offset 28)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf7,
+ 0x00, 0xb0, 0x07, 0x64 // Controller 7 = 100 (offset 92)
+};
+
+static const byte map_param[7] = {
+ 0, 2, 3, 4, 8, 9, 0,
+};
+
+static const byte freq2note[128] = {
+ /*128*/ 6, 6, 6, 6,
+ /*132*/ 7, 7, 7, 7, 7, 7, 7,
+ /*139*/ 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ /*148*/ 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ /*157*/ 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ /*166*/ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ /*176*/ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ /*186*/ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ /*197*/ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ /*209*/ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ /*222*/ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ /*235*/ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ /*249*/ 18, 18, 18, 18, 18, 18, 18
+};
+
+static const uint16 num_steps_table[] = {
+ 1, 2, 4, 5,
+ 6, 7, 8, 9,
+ 10, 12, 14, 16,
+ 18, 21, 24, 30,
+ 36, 50, 64, 82,
+ 100, 136, 160, 192,
+ 240, 276, 340, 460,
+ 600, 860, 1200, 1600
+};
+
+int ScummEngine::convert_extraflags(byte * ptr, byte * src_ptr) {
+ int flags = src_ptr[0];
+
+ int t1, t2, t3, t4, time;
+ int v1, v2, v3;
+
+ if (!(flags & 0x80))
+ return -1;
+
+ t1 = (src_ptr[1] & 0xf0) >> 3;
+ t2 = (src_ptr[2] & 0xf0) >> 3;
+ t3 = (src_ptr[3] & 0xf0) >> 3 | (flags & 0x40 ? 0x80 : 0);
+ t4 = (src_ptr[3] & 0x0f) << 1;
+ v1 = (src_ptr[1] & 0x0f);
+ v2 = (src_ptr[2] & 0x0f);
+ v3 = 31;
+ if ((flags & 0x7) == 0) {
+ v1 = v1 + 31 + 8;
+ v2 = v2 + 31 + 8;
+ } else {
+ v1 = v1 * 2 + 31;
+ v2 = v2 * 2 + 31;
+ }
+
+ /* flags a */
+ if ((flags & 0x7) == 6)
+ ptr[0] = 0;
+ else {
+ ptr[0] = (flags >> 4) & 0xb;
+ ptr[1] = map_param[flags & 0x7];
+ }
+
+ /* extra a */
+ ptr[2] = 0;
+ ptr[3] = 0;
+ ptr[4] = t1 >> 4;
+ ptr[5] = t1 & 0xf;
+ ptr[6] = v1 >> 4;
+ ptr[7] = v1 & 0xf;
+ ptr[8] = t2 >> 4;
+ ptr[9] = t2 & 0xf;
+ ptr[10] = v2 >> 4;
+ ptr[11] = v2 & 0xf;
+ ptr[12] = t3 >> 4;
+ ptr[13] = t3 & 0xf;
+ ptr[14] = t4 >> 4;
+ ptr[15] = t4 & 0xf;
+ ptr[16] = v3 >> 4;
+ ptr[17] = v3 & 0xf;
+
+ time = num_steps_table[t1] + num_steps_table[t2]
+ + num_steps_table[t3 & 0x7f] + num_steps_table[t4];
+ if (flags & 0x20) {
+ int playtime = ((src_ptr[4] >> 4) & 0xf) * 118 +
+ (src_ptr[4] & 0xf) * 8;
+ if (playtime > time)
+ time = playtime;
+ }
+ /*
+ time = ((src_ptr[4] >> 4) & 0xf) * 118 +
+ (src_ptr[4] & 0xf) * 8;
+ */
+ return time;
+}
+
+#define kMIDIHeaderSize 46
+static inline byte *writeMIDIHeader(byte *ptr, const char *type, int ppqn, int total_size) {
+ uint32 dw = TO_BE_32(total_size);
+
+ memcpy(ptr, type, 4); ptr += 4;
+ memcpy(ptr, &dw, 4); ptr += 4;
+ memcpy(ptr, "MDhd", 4); ptr += 4;
+ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 8;
+ ptr += 4;
+ memset(ptr, 0, 8), ptr += 8;
+ memcpy(ptr, "MThd", 4); ptr += 4;
+ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 6;
+ ptr += 4;
+ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 1; // MIDI format 0 with 1 track
+ ptr += 4;
+
+ *ptr++ = ppqn >> 8;
+ *ptr++ = ppqn & 0xFF;
+
+ memcpy(ptr, "MTrk", 4); ptr += 4;
+ memcpy(ptr, &dw, 4); ptr += 4;
+
+ return ptr;
+}
+
+static inline byte *writeVLQ(byte *ptr, int value) {
+ if (value > 0x7f) {
+ if (value > 0x3fff) {
+ *ptr++ = (value >> 14) | 0x80;
+ value &= 0x3fff;
+ }
+ *ptr++ = (value >> 7) | 0x80;
+ value &= 0x7f;
+ }
+ *ptr++ = value;
+ return ptr;
+}
+
+static inline byte Mac0ToGMInstrument(uint32 type, int &transpose) {
+ transpose = 0;
+ switch (type) {
+ case MKID('MARI'): return 12;
+ case MKID('PLUC'): return 45;
+ case MKID('HARM'): return 22;
+ case MKID('PIPE'): return 19;
+ case MKID('TROM'): transpose = -12; return 57;
+ case MKID('STRI'): return 48;
+ case MKID('HORN'): return 60;
+ case MKID('VIBE'): return 11;
+ case MKID('SHAK'): return 77;
+ case MKID('PANP'): return 75;
+ case MKID('WHIS'): return 76;
+ case MKID('ORGA'): return 17;
+ case MKID('BONG'): return 115;
+ case MKID('BASS'): transpose = -24; return 35;
+ default:
+ error("Unknown Mac0 instrument %s found", tag2str(type));
+ }
+}
+
+void ScummEngine::convertMac0Resource(int type, int idx, byte *src_ptr, int size) {
+ /*
+ From Markus Magnuson (superqult) we got this information:
+ Mac0
+ ---
+ 4 bytes - 'SOUN'
+ BE 4 bytes - block length
+
+ 4 bytes - 'Mac0'
+ BE 4 bytes - (blockLength - 27)
+ 28 bytes - ???
+
+ do this three times (once for each channel):
+ 4 bytes - 'Chan'
+ BE 4 bytes - channel length
+ 4 bytes - instrument name (e.g. 'MARI')
+
+ do this for ((chanLength-24)/4) times:
+ 2 bytes - note duration
+ 1 byte - note value
+ 1 byte - note velocity
+
+ 4 bytes - ???
+ 4 bytes - 'Loop'/'Done'
+ 4 bytes - ???
+
+ 1 byte - 0x09
+ ---
+
+ Instruments (General Midi):
+ "MARI" - Marimba (12)
+ "PLUC" - Pizzicato Strings (45)
+ "HARM" - Harmonica (22)
+ "PIPE" - Church Organ? (19) or Flute? (73) or Bag Pipe (109)
+ "TROM" - Trombone (57)
+ "STRI" - String Ensemble (48 or 49)
+ "HORN" - French Horn? (60) or English Horn? (69)
+ "VIBE" - Vibraphone (11)
+ "SHAK" - Shakuhachi? (77)
+ "PANP" - Pan Flute (75)
+ "WHIS" - Whistle (78) / Bottle (76)
+ "ORGA" - Drawbar Organ (16; but could also be 17-20)
+ "BONG" - Woodblock? (115)
+ "BASS" - Bass (32-39)
+
+
+ Now the task could be to convert this into MIDI, to be fed into iMuse.
+ Or we do something similiar to what is done in Player_V3, assuming
+ we can identify SFX in the MI datafiles for each of the instruments
+ listed above.
+ */
+
+#if 0
+ byte *ptr = createResource(type, idx, size);
+ memcpy(ptr, src_ptr, size);
+#else
+ const int ppqn = 480;
+ byte *ptr, *start_ptr;
+
+ int total_size = 0;
+ total_size += kMIDIHeaderSize; // Header
+ total_size += 7; // Tempo META
+ total_size += 3 * 3; // Three program change mesages
+ total_size += 22; // Possible jump SysEx
+ total_size += 5; // EOT META
+
+ int i, len;
+ byte track_instr[3];
+ byte *track_data[3];
+ int track_len[3];
+ int track_transpose[3];
+ bool looped = false;
+
+ src_ptr += 8;
+ // TODO: Decipher the unknown bytes in the header. For now, skip 'em
+ src_ptr += 28;
+
+ // Parse the three channels
+ for (i = 0; i < 3; i++) {
+ assert(*((uint32*)src_ptr) == MKID('Chan'));
+ len = READ_BE_UINT32(src_ptr + 4);
+ track_len[i] = len - 24;
+ track_instr[i] = Mac0ToGMInstrument(*(uint32*)(src_ptr + 8), track_transpose[i]);
+ track_data[i] = src_ptr + 12;
+ src_ptr += len;
+ looped = (*((uint32*)(src_ptr - 8)) == MKID('Loop'));
+
+ // For each note event, we need up to 6 bytes for the
+ // Note On (3 VLQ, 3 event), and 6 bytes for the Note
+ // Off (3 VLQ, 3 event). So 12 bytes total.
+ total_size += 12 * track_len[i];
+ }
+ assert(*src_ptr == 0x09);
+
+ // Create sound resource
+ start_ptr = createResource(type, idx, total_size);
+
+ // Insert MIDI header
+ ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
+
+ // Write a tempo change Meta event
+ // 473 / 4 Hz, convert to micro seconds.
+ uint32 dw = 1000000 * 437 / 4 / ppqn; // 1000000 * ppqn * 4 / 473;
+ memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
+ *ptr++ = (byte)((dw >> 16) & 0xFF);
+ *ptr++ = (byte)((dw >> 8) & 0xFF);
+ *ptr++ = (byte)(dw & 0xFF);
+
+ // Insert program change messages
+ *ptr++ = 0; // VLQ
+ *ptr++ = 0xC0;
+ *ptr++ = track_instr[0];
+ *ptr++ = 0; // VLQ
+ *ptr++ = 0xC1;
+ *ptr++ = track_instr[1];
+ *ptr++ = 0; // VLQ
+ *ptr++ = 0xC2;
+ *ptr++ = track_instr[2];
+
+ // And now, the actual composition. Please turn all cell phones
+ // and pagers off during the performance. Thank you.
+ uint16 nextTime[3] = { 1, 1, 1 };
+ int stage[3] = { 0, 0, 0 };
+
+ while (track_len[0] | track_len[1] | track_len[2]) {
+ int best = -1;
+ uint16 bestTime = 0xFFFF;
+ for (i = 0; i < 3; ++i) {
+ if (track_len[i] && nextTime[i] < bestTime) {
+ bestTime = nextTime[i];
+ best = i;
+ }
+ }
+ assert (best != -1);
+
+ if (!stage[best]) {
+ // We are STARTING this event.
+ if (track_data[best][2] > 1) {
+ // Note On
+ ptr = writeVLQ(ptr, nextTime[best]);
+ *ptr++ = 0x90 | best;
+ *ptr++ = track_data[best][2] + track_transpose[best];
+ *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
+ for (i = 0; i < 3; ++i)
+ nextTime[i] -= bestTime;
+ }
+ nextTime[best] += READ_BE_UINT16 (track_data[best]);
+ stage[best] = 1;
+ } else {
+ // We are ENDING this event.
+ if (track_data[best][2] > 1) {
+ // There was a Note On, so do a Note Off
+ ptr = writeVLQ(ptr, nextTime[best]);
+ *ptr++ = 0x80 | best;
+ *ptr++ = track_data[best][2] + track_transpose[best];
+ *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
+ for (i = 0; i < 3; ++i)
+ nextTime[i] -= bestTime;
+ }
+ track_data[best] += 4;
+ track_len[best] -= 4;
+ stage[best] = 0;
+ }
+ }
+
+ // Is this a looped song? If so, effect a loop by
+ // using the S&M maybe_jump SysEx command.
+ // FIXME: Jamieson630: The jump seems to be happening
+ // too quickly! There should maybe be a pause after
+ // the last Note Off? But I couldn't find one in the
+ // MI1 Lookout music, where I was hearing problems.
+ if (looped) {
+ memcpy(ptr, "\x00\xf0\x13\x7d\x30\00", 6); ptr += 6; // maybe_jump
+ memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump
+ memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> 0 (only track)
+ memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> 1 (first beat)
+ memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // tick -> 1
+ memcpy(ptr, "\x00\xf7", 2); ptr += 2; // SysEx end marker
+ }
+
+ // Insert end of song META
+ memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
+
+ assert(ptr <= start_ptr + total_size);
+
+ // Rewrite MIDI header, this time with true size
+ total_size = ptr - start_ptr;
+ ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
+#endif
+}
+
+void ScummEngine::convertADResource(int type, int idx, byte *src_ptr, int size) {
+
+ // We will ignore the PPQN in the original resource, because
+ // it's invalid anyway. We use a constant PPQN of 480.
+ const int ppqn = 480;
+ uint32 dw;
+ int i, ch;
+ byte *ptr;
+ int total_size = kMIDIHeaderSize + 7 + 8 * sizeof(ADLIB_INSTR_MIDI_HACK) + size;
+ total_size += 24; // Up to 24 additional bytes are needed for the jump sysex
+
+ ptr = createResource(type, idx, total_size);
+
+ src_ptr += 2;
+ size -= 2;
+
+ // 0x80 marks a music resource. Otherwise it's a SFX
+ if (*src_ptr == 0x80) {
+ byte ticks, play_once;
+ byte num_instr;
+ byte *channel, *instr, *track;
+
+ ptr = writeMIDIHeader(ptr, "ADL ", ppqn, total_size);
+
+ // The "speed" of the song
+ ticks = *(src_ptr + 1);
+
+ // Flag that tells us whether we should loop the song (0) or play it only once (1)
+ play_once = *(src_ptr + 2);
+
+ // Number of instruments used
+ num_instr = *(src_ptr + 8); // Normally 8
+
+ // copy the pointer to instrument data
+ channel = src_ptr + 9;
+ instr = src_ptr + 0x11;
+
+ // skip over the rest of the header and copy the MIDI data into a buffer
+ src_ptr += 0x11 + 8 * 16;
+ size -= 0x11 + 8 * 16;
+
+ CHECK_HEAP
+
+ track = src_ptr;
+
+ // Convert the ticks into a MIDI tempo.
+ // Unfortunate LOOM and INDY3 have different interpretation
+ // of the ticks value.
+ if (_gameId == GID_INDY3) {
+ // Note: since we fix ppqn at 480, ppqn/473 is almost 1
+ dw = 500000 * 256 / 473 * ppqn / ticks;
+ } else if (_gameId == GID_LOOM) {
+ dw = 500000 * ppqn / 4 / ticks;
+ } else {
+ dw = 500000 * 256 / ticks;
+ }
+ debugC(DEBUG_SOUND, " ticks = %d, speed = %ld", ticks, dw);
+
+ // Write a tempo change Meta event
+ memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
+ *ptr++ = (byte)((dw >> 16) & 0xFF);
+ *ptr++ = (byte)((dw >> 8) & 0xFF);
+ *ptr++ = (byte)(dw & 0xFF);
+
+ // Copy our hardcoded instrument table into it
+ // Then, convert the instrument table as given in this song resource
+ // And write it *over* the hardcoded table.
+ // Note: we deliberately.
+
+ /* now fill in the instruments */
+ for (i = 0; i < num_instr; i++) {
+ ch = channel[i] - 1;
+ if (ch < 0 || ch > 15)
+ continue;
+
+ if (instr[i*16 + 13])
+ warning("Sound %d instrument %d uses percussion", idx, i);
+
+ debugC(DEBUG_SOUND, "Sound %d: instrument %d on channel %d.", idx, i, ch);
+
+ memcpy(ptr, ADLIB_INSTR_MIDI_HACK, sizeof(ADLIB_INSTR_MIDI_HACK));
+
+ ptr[5] += ch;
+ ptr[28] += ch;
+ ptr[92] += ch;
+
+ /* flags_1 */
+ ptr[30 + 0] = (instr[i * 16 + 3] >> 4) & 0xf;
+ ptr[30 + 1] = instr[i * 16 + 3] & 0xf;
+
+ /* oplvl_1 */
+ ptr[30 + 2] = (instr[i * 16 + 4] >> 4) & 0xf;
+ ptr[30 + 3] = instr[i * 16 + 4] & 0xf;
+
+ /* atdec_1 */
+ ptr[30 + 4] = ((~instr[i * 16 + 5]) >> 4) & 0xf;
+ ptr[30 + 5] = (~instr[i * 16 + 5]) & 0xf;
+
+ /* sustrel_1 */
+ ptr[30 + 6] = ((~instr[i * 16 + 6]) >> 4) & 0xf;
+ ptr[30 + 7] = (~instr[i * 16 + 6]) & 0xf;
+
+ /* waveform_1 */
+ ptr[30 + 8] = (instr[i * 16 + 7] >> 4) & 0xf;
+ ptr[30 + 9] = instr[i * 16 + 7] & 0xf;
+
+ /* flags_2 */
+ ptr[30 + 10] = (instr[i * 16 + 8] >> 4) & 0xf;
+ ptr[30 + 11] = instr[i * 16 + 8] & 0xf;
+
+ /* oplvl_2 */
+ ptr[30 + 12] = (instr[i * 16 + 9] >> 4) & 0xf;
+ ptr[30 + 13] = instr[i * 16 + 9] & 0xf;
+
+ /* atdec_2 */
+ ptr[30 + 14] = ((~instr[i * 16 + 10]) >> 4) & 0xf;
+ ptr[30 + 15] = (~instr[i * 16 + 10]) & 0xf;
+
+ /* sustrel_2 */
+ ptr[30 + 16] = ((~instr[i * 16 + 11]) >> 4) & 0xf;
+ ptr[30 + 17] = (~instr[i * 16 + 11]) & 0xf;
+
+ /* waveform_2 */
+ ptr[30 + 18] = (instr[i * 16 + 12] >> 4) & 0xf;
+ ptr[30 + 19] = instr[i * 16 + 12] & 0xf;
+
+ /* feedback */
+ ptr[30 + 20] = (instr[i * 16 + 2] >> 4) & 0xf;
+ ptr[30 + 21] = instr[i * 16 + 2] & 0xf;
+ ptr += sizeof(ADLIB_INSTR_MIDI_HACK);
+ }
+
+ // There is a constant delay of ppqn/3 before the music starts.
+ if (ppqn / 3 >= 128)
+ *ptr++ = (ppqn / 3 >> 7) | 0x80;
+ *ptr++ = ppqn / 3 & 0x7f;
+
+ // Now copy the actual music data
+ memcpy(ptr, track, size);
+ ptr += size;
+
+ if (!play_once) {
+ // The song is meant to be looped. We achieve this by inserting just
+ // before the song end a jump to the song start. More precisely we abuse
+ // a S&M sysex, "maybe_jump" to achieve this effect. We could also
+ // use a set_loop sysex, but it's a bit longer, a little more complicated,
+ // and has no advantage either.
+
+ // First, find the track end
+ byte *end = ptr;
+ ptr -= size;
+ for (; ptr < end; ptr++) {
+ if (*ptr == 0xff && *(ptr + 1) == 0x2f)
+ break;
+ }
+ assert(ptr < end);
+
+ // Now insert the jump. The jump offset is measured in ticks.
+ // We have ppqn/3 ticks before the first note.
+
+ const int jump_offset = ppqn / 3;
+ memcpy(ptr, "\xf0\x13\x7d\x30\00", 5); ptr += 5; // maybe_jump
+ memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump
+ memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> there is only one track, 0
+ memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> for now, 1 (first beat)
+ // Ticks
+ *ptr++ = (byte)((jump_offset >> 12) & 0x0F);
+ *ptr++ = (byte)((jump_offset >> 8) & 0x0F);
+ *ptr++ = (byte)((jump_offset >> 4) & 0x0F);
+ *ptr++ = (byte)(jump_offset & 0x0F);
+ memcpy(ptr, "\x00\xf7", 2); ptr += 2; // sysex end marker
+ }
+ } else {
+
+ /* This is a sfx resource. First parse it quickly to find the parallel
+ * tracks.
+ */
+ ptr = writeMIDIHeader(ptr, "ASFX", ppqn, total_size);
+
+ byte current_instr[3][14];
+ int current_note[3];
+ int track_time[3];
+ byte *track_data[3];
+
+ int track_ctr = 0;
+ byte chunk_type = 0;
+ int delay, delay2, olddelay;
+
+ // Write a tempo change Meta event
+ // 473 / 4 Hz, convert to micro seconds.
+ dw = 1000000 * ppqn * 4 / 473;
+ memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
+ *ptr++ = (byte)((dw >> 16) & 0xFF);
+ *ptr++ = (byte)((dw >> 8) & 0xFF);
+ *ptr++ = (byte)(dw & 0xFF);
+
+ for (i = 0; i < 3; i++) {
+ track_time[i] = -1;
+ current_note[i] = -1;
+ }
+ while (size > 0) {
+ assert(track_ctr < 3);
+ track_data[track_ctr] = src_ptr;
+ track_time[track_ctr] = 0;
+ track_ctr++;
+ while (size > 0) {
+ chunk_type = *(src_ptr);
+ if (chunk_type == 1) {
+ src_ptr += 15;
+ size -= 15;
+ } else if (chunk_type == 2) {
+ src_ptr += 11;
+ size -= 11;
+ } else if (chunk_type == 0x80) {
+ src_ptr ++;
+ size --;
+ } else {
+ break;
+ }
+ }
+ if (chunk_type == 0xff)
+ break;
+ src_ptr++;
+ }
+
+ int curtime = 0;
+ for (;;) {
+ int mintime = -1;
+ ch = -1;
+ for (i = 0; i < 3; i++) {
+ if (track_time[i] >= 0 &&
+ (mintime == -1 || mintime > track_time[i])) {
+ mintime = track_time[i];
+ ch = i;
+ }
+ }
+ if (mintime < 0)
+ break;
+
+ src_ptr = track_data[ch];
+ chunk_type = *src_ptr;
+
+ if (current_note[ch] >= 0) {
+ delay = mintime - curtime;
+ curtime = mintime;
+ ptr = writeVLQ(ptr, delay);
+ *ptr++ = 0x80 + ch; // key off channel;
+ *ptr++ = current_note[ch];
+ *ptr++ = 0;
+ current_note[ch] = -1;
+ }
+
+ switch (chunk_type) {
+ case 1:
+ /* Instrument definition */
+ memcpy(current_instr[ch], src_ptr + 1, 14);
+ src_ptr += 15;
+ break;
+
+ case 2:
+ /* tone/parammodulation */
+ memcpy(ptr, ADLIB_INSTR_MIDI_HACK,
+ sizeof(ADLIB_INSTR_MIDI_HACK));
+
+ ptr[5] += ch;
+ ptr[28] += ch;
+ ptr[92] += ch;
+
+ /* flags_1 */
+ ptr[30 + 0] = (current_instr[ch][3] >> 4) & 0xf;
+ ptr[30 + 1] = current_instr[ch][3] & 0xf;
+
+ /* oplvl_1 */
+ ptr[30 + 2] = (current_instr[ch][4] >> 4) & 0xf;
+ ptr[30 + 3] = current_instr[ch][4] & 0xf;
+
+ /* atdec_1 */
+ ptr[30 + 4] = ((~current_instr[ch][5]) >> 4) & 0xf;
+ ptr[30 + 5] = (~current_instr[ch][5]) & 0xf;
+
+ /* sustrel_1 */
+ ptr[30 + 6] = ((~current_instr[ch][6]) >> 4) & 0xf;
+ ptr[30 + 7] = (~current_instr[ch][6]) & 0xf;
+
+ /* waveform_1 */
+ ptr[30 + 8] = (current_instr[ch][7] >> 4) & 0xf;
+ ptr[30 + 9] = current_instr[ch][7] & 0xf;
+
+ /* flags_2 */
+ ptr[30 + 10] = (current_instr[ch][8] >> 4) & 0xf;
+ ptr[30 + 11] = current_instr[ch][8] & 0xf;
+
+ /* oplvl_2 */
+ ptr[30 + 12] = ((current_instr[ch][9]) >> 4) & 0xf;
+ ptr[30 + 13] = (current_instr[ch][9]) & 0xf;
+
+ /* atdec_2 */
+ ptr[30 + 14] = ((~current_instr[ch][10]) >> 4) & 0xf;
+ ptr[30 + 15] = (~current_instr[ch][10]) & 0xf;
+
+ /* sustrel_2 */
+ ptr[30 + 16] = ((~current_instr[ch][11]) >> 4) & 0xf;
+ ptr[30 + 17] = (~current_instr[ch][11]) & 0xf;
+
+ /* waveform_2 */
+ ptr[30 + 18] = (current_instr[ch][12] >> 4) & 0xf;
+ ptr[30 + 19] = current_instr[ch][12] & 0xf;
+
+ /* feedback */
+ ptr[30 + 20] = (current_instr[ch][2] >> 4) & 0xf;
+ ptr[30 + 21] = current_instr[ch][2] & 0xf;
+
+ delay = mintime - curtime;
+ curtime = mintime;
+
+ {
+ delay = convert_extraflags(ptr + 30 + 22, src_ptr + 1);
+ delay2 = convert_extraflags(ptr + 30 + 40, src_ptr + 6);
+ debugC(DEBUG_SOUND, "delays: %d / %d", delay, delay2);
+ if (delay2 >= 0 && delay2 < delay)
+ delay = delay2;
+ if (delay == -1)
+ delay = 0;
+ }
+
+ /* duration */
+ ptr[30 + 58] = 0; // ((delay * 17 / 63) >> 4) & 0xf;
+ ptr[30 + 59] = 0; // (delay * 17 / 63) & 0xf;
+
+ ptr += sizeof(ADLIB_INSTR_MIDI_HACK);
+
+ olddelay = mintime - curtime;
+ curtime = mintime;
+ ptr = writeVLQ(ptr, olddelay);
+
+ {
+ int freq = ((current_instr[ch][1] & 3) << 8)
+ | current_instr[ch][0];
+ if (!freq)
+ freq = 0x80;
+ freq <<= ((current_instr[ch][1] >> 2) & 7) + 1;
+ int note = -11;
+ while (freq >= 0x100) {
+ note += 12;
+ freq >>= 1;
+ }
+ debugC(DEBUG_SOUND, "Freq: %d (%x) Note: %d", freq, freq, note);
+ if (freq < 0x80)
+ note = 0;
+ else
+ note += freq2note[freq - 0x80];
+
+ debugC(DEBUG_SOUND, "Note: %d", note);
+ if (note <= 0)
+ note = 1;
+ else if (note > 127)
+ note = 127;
+
+ // Insert a note on event
+ *ptr++ = 0x90 + ch; // key on channel
+ *ptr++ = note;
+ *ptr++ = 63;
+ current_note[ch] = note;
+ track_time[ch] = curtime + delay;
+ }
+ src_ptr += 11;
+ break;
+
+ case 0x80:
+ track_time[ch] = -1;
+ src_ptr ++;
+ break;
+
+ default:
+ track_time[ch] = -1;
+ }
+ track_data[ch] = src_ptr;
+ }
+ }
+
+ // Insert end of song sysex
+ memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
+}
+
+
+int ScummEngine::readSoundResourceSmallHeader(int type, int idx) {
+ uint32 pos, total_size, size, tag;
+ uint32 ad_size = 0, ad_offs = 0;
+ uint32 ro_size = 0, ro_offs = 0;
+ uint32 wa_size = 0, wa_offs = 0;
+
+ debug(4, "readSoundResourceSmallHeader(%d)", idx);
+
+ if ((_gameId == GID_LOOM) && (_features & GF_PC) && VAR(VAR_SOUNDCARD) == 4) {
+ // Roland resources in Loom are tagless
+ // So we add an RO tag to allow imuse to detect format
+ byte *ptr, *src_ptr;
+ ro_offs = _fileHandle.pos();
+ ro_size = _fileHandle.readUint16LE();
+
+ src_ptr = (byte *) calloc(ro_size - 4, 1);
+ _fileHandle.seek(ro_offs + 4, SEEK_SET);
+ _fileHandle.read(src_ptr, ro_size -4);
+
+ ptr = createResource(type, idx, ro_size + 2);
+ memcpy(ptr, "RO", 2); ptr += 2;
+ memcpy(ptr, src_ptr, ro_size - 4); ptr += ro_size - 4;
+ return 1;
+ } else if (_features & GF_OLD_BUNDLE) {
+ wa_offs = _fileHandle.pos();
+ wa_size = _fileHandle.readUint16LE();
+ _fileHandle.seek(wa_size - 2, SEEK_CUR);
+
+ if (!(_features & GF_ATARI_ST || _features & GF_MACINTOSH)) {
+ ad_offs = _fileHandle.pos();
+ ad_size = _fileHandle.readUint16LE();
+ }
+ _fileHandle.seek(4, SEEK_CUR);
+ total_size = wa_size + ad_size;
+ } else {
+ total_size = size = _fileHandle.readUint32LE();
+ tag = _fileHandle.readUint16LE();
+ debug(4, " tag='%c%c', size=%d", (char) (tag & 0xff),
+ (char) ((tag >> 8) & 0xff), size);
+
+ if (tag == 0x4F52) { // RO
+ ro_offs = _fileHandle.pos();
+ ro_size = size;
+ } else {
+ pos = 6;
+ while (pos < total_size) {
+ size = _fileHandle.readUint32LE();
+ tag = _fileHandle.readUint16LE();
+ debug(4, " tag='%c%c', size=%d", (char) (tag & 0xff),
+ (char) ((tag >> 8) & 0xff), size);
+ pos += size;
+
+ // MI1 and Indy3 uses one or more nested SO resources, which contains AD and WA
+ // resources.
+ if ((tag == 0x4441) && !(ad_offs)) { // AD
+ ad_size = size;
+ ad_offs = _fileHandle.pos();
+ } else if ((tag == 0x4157) && !(wa_offs)) { // WA
+ wa_size = size;
+ wa_offs = _fileHandle.pos();
+ } else { // other AD, WA and nested SO resources
+ if (tag == 0x4F53) { // SO
+ pos -= size;
+ size = 6;
+ pos += 6;
+ }
+ }
+ _fileHandle.seek(size - 6, SEEK_CUR);
+ }
+ }
+ }
+
+ if ((_midiDriver == MD_ADLIB) && ad_offs != 0) {
+ // AD resources have a header, instrument definitions and one MIDI track.
+ // We build an 'ADL ' resource from that:
+ // 8 bytes resource header
+ // 16 bytes MDhd header
+ // 14 bytes MThd header
+ // 8 bytes MTrk header
+ // 7 bytes MIDI tempo sysex
+ // + some default instruments
+ byte *ptr;
+ if (_features & GF_OLD_BUNDLE) {
+ ptr = (byte *) calloc(ad_size - 4, 1);
+ _fileHandle.seek(ad_offs + 4, SEEK_SET);
+ _fileHandle.read(ptr, ad_size - 4);
+ convertADResource(type, idx, ptr, ad_size - 4);
+ free(ptr);
+ return 1;
+ } else {
+ ptr = (byte *) calloc(ad_size - 6, 1);
+ _fileHandle.seek(ad_offs, SEEK_SET);
+ _fileHandle.read(ptr, ad_size - 6);
+ convertADResource(type, idx, ptr, ad_size - 6);
+ free(ptr);
+ return 1;
+ }
+ } else if (((_midiDriver == MD_PCJR) || (_midiDriver == MD_PCSPK)) && wa_offs != 0) {
+ if (_features & GF_OLD_BUNDLE) {
+ _fileHandle.seek(wa_offs, SEEK_SET);
+ _fileHandle.read(createResource(type, idx, wa_size), wa_size);
+ } else {
+ _fileHandle.seek(wa_offs - 6, SEEK_SET);
+ _fileHandle.read(createResource(type, idx, wa_size + 6), wa_size + 6);
+ }
+ return 1;
+ } else if (ro_offs != 0) {
+ _fileHandle.seek(ro_offs - 2, SEEK_SET);
+ _fileHandle.read(createResource(type, idx, ro_size - 4), ro_size - 4);
+ return 1;
+ }
+ res.roomoffs[type][idx] = 0xFFFFFFFF;
+ return 0;
+}
+
+
} // End of namespace Scumm
diff --git a/scumm/util.cpp b/scumm/util.cpp
new file mode 100644
index 0000000000..e1710d24c8
--- /dev/null
+++ b/scumm/util.cpp
@@ -0,0 +1,272 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2004 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#pragma mark -
+#pragma mark --- ScummFile ---
+#pragma mark -
+
+
+ScummFile::ScummFile() : _encbyte(0), _subFileStart(0), _subFileLen(0) {
+}
+
+void ScummFile::setEnc(byte value) {
+ _encbyte = value;
+}
+
+void ScummFile::setSubfileRange(uint32 start, uint32 len) {
+ // TODO: Add sanity checks
+ const uint32 fileSize = File::size();
+ assert(start <= fileSize);
+ assert(start + len <= fileSize);
+ _subFileStart = start;
+ _subFileLen = len;
+ seek(0, SEEK_SET);
+}
+
+void ScummFile::resetSubfile() {
+ _subFileStart = 0;
+ _subFileLen = 0;
+ seek(0, SEEK_SET);
+}
+
+bool ScummFile::open(const char *filename, AccessMode mode) {
+ if (File::open(filename, mode)) {
+ resetSubfile();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ScummFile::openSubFile(const char *filename) {
+ assert(isOpen());
+
+ // Disable the XOR encryption and reset any current subfile range
+ setEnc(0);
+ resetSubfile();
+
+ // Read in the filename table and look for the specified file
+
+ unsigned long file_off, file_len;
+ char file_name[0x20+1];
+ unsigned long i;
+
+ // Get the length of the data file to use for consistency checks
+ const uint32 data_file_len = size();
+
+ // Read offset and length to the file records */
+ const uint32 file_record_off = readUint32BE();
+ const uint32 file_record_len = readUint32BE();
+
+ // Do a quick check to make sure the offset and length are good
+ if (file_record_off + file_record_len > data_file_len) {
+ return false;
+ }
+
+ // Do a little consistancy check on file_record_length
+ if (file_record_len % 0x28) {
+ return false;
+ }
+
+ // Scan through the files
+ for (i = 0; i < file_record_len; i += 0x28) {
+ // read a file record
+ seek(file_record_off + i, SEEK_SET);
+ file_off = readUint32BE();
+ file_len = readUint32BE();
+ read(file_name, 0x20);
+ file_name[0x20] = 0;
+
+ assert(file_name[0]);
+ //debug(7, " extracting \'%s\'", file_name);
+
+ // Consistency check. make sure the file data is in the file
+ if (file_off + file_len > data_file_len) {
+ return false;
+ }
+
+ if (scumm_stricmp(file_name, filename) == 0) {
+ // We got a match!
+ setSubfileRange(file_off, file_len);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ScummFile::eof() {
+ return _subFileLen ? (pos() >= _subFileLen) : File::eof();
+}
+
+uint32 ScummFile::pos() {
+ return File::pos() - _subFileStart;
+}
+
+uint32 ScummFile::size() {
+ return _subFileLen ? _subFileLen : File::size();
+}
+
+void ScummFile::seek(int32 offs, int whence) {
+ if (_subFileLen) {
+ // Constrain the seek to the subfile
+ switch (whence) {
+ case SEEK_END:
+ offs = _subFileStart + _subFileLen - offs;
+ break;
+ case SEEK_SET:
+ offs += _subFileStart;
+ break;
+ case SEEK_CUR:
+ offs += File::pos();
+ break;
+ }
+ assert((int32)_subFileStart <= offs && offs <= (int32)(_subFileStart + _subFileLen));
+ whence = SEEK_SET;
+ }
+ File::seek(offs, whence);
+}
+
+uint32 ScummFile::read(void *ptr, uint32 len) {
+ uint32 realLen;
+
+ if (_subFileLen) {
+ // Limit the amount we read by the subfile boundaries.
+ const uint32 curPos = pos();
+ assert(_subFileLen >= curPos);
+ uint32 newPos = curPos + len;
+ if (newPos > _subFileLen) {
+ len = _subFileLen - curPos;
+ _ioFailed = true;
+ }
+ }
+
+ realLen = File::read(ptr, len);
+
+
+ // If an encryption byte was specified, XOR the data we just read by it.
+ // This simple kind of "encryption" was used by some of the older SCUMM
+ // games.
+ if (_encbyte) {
+ byte *p = (byte *)ptr;
+ byte *end = p + realLen;
+ while (p < end)
+ *p++ ^= _encbyte;
+ }
+
+ return realLen;
+}
+
+uint32 ScummFile::write(const void *, uint32) {
+ error("ScummFile does not support writing!");
+}
+
+#pragma mark -
+#pragma mark --- Utilities ---
+#pragma mark -
+
+void checkRange(int max, int min, int no, const char *str) {
+ if (no < min || no > max) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), str, no);
+ error("Value %d is out of bounds (%d,%d) (%s)", no, min, max, buf);
+ }
+}
+
+/**
+ * Convert an old style direction to a new style one (angle),
+ */
+int newDirToOldDir(int dir) {
+ if (dir >= 71 && dir <= 109)
+ return 1;
+ if (dir >= 109 && dir <= 251)
+ return 2;
+ if (dir >= 251 && dir <= 289)
+ return 0;
+ return 3;
+}
+
+/**
+ * Convert an new style (angle) direction to an old style one.
+ */
+int oldDirToNewDir(int dir) {
+ assert(0 <= dir && dir <= 3);
+ const int new_dir_table[4] = { 270, 90, 180, 0 };
+ return new_dir_table[dir];
+}
+
+/**
+ * Convert an angle to a simple direction.
+ */
+int toSimpleDir(int dirType, int dir) {
+ if (dirType) {
+ const int16 directions[] = { 22, 72, 107, 157, 202, 252, 287, 337 };
+ for (int i = 0; i < 7; i++)
+ if (dir >= directions[i] && dir <= directions[i+1])
+ return i+1;
+ } else {
+ const int16 directions[] = { 71, 109, 251, 289 };
+ for (int i = 0; i < 3; i++)
+ if (dir >= directions[i] && dir <= directions[i+1])
+ return i+1;
+ }
+
+ return 0;
+}
+
+/**
+ * Convert a simple direction to an angle.
+ */
+int fromSimpleDir(int dirType, int dir) {
+ if (dirType)
+ return dir * 45;
+ else
+ return dir * 90;
+}
+
+/**
+ * Normalize the given angle - that means, ensure it is positive, and
+ * change it to the closest multiple of 45 degree by abusing toSimpleDir.
+ */
+int normalizeAngle(int angle) {
+ int temp;
+
+ temp = (angle + 360) % 360;
+
+ return toSimpleDir(1, temp) * 45;
+}
+
+const char *tag2str(uint32 tag) {
+ static char str[5];
+ str[0] = (char)(tag >> 24);
+ str[1] = (char)(tag >> 16);
+ str[2] = (char)(tag >> 8);
+ str[3] = (char)tag;
+ str[4] = '\0';
+ return str;
+}
+
+} // End of namespace Scumm
diff --git a/scumm/util.h b/scumm/util.h
new file mode 100644
index 0000000000..4b6c0bd974
--- /dev/null
+++ b/scumm/util.h
@@ -0,0 +1,71 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2004 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+#ifndef SCUMM_UTIL_H
+#define SCUMM_UTIL_H
+
+#include "common/file.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+class ScummFile : public File {
+private:
+ byte _encbyte;
+ uint32 _subFileStart;
+ uint32 _subFileLen;
+public:
+ ScummFile();
+ void setEnc(byte value);
+
+ void setSubfileRange(uint32 start, uint32 len);
+ void resetSubfile();
+
+ bool open(const char *filename, AccessMode mode = kFileReadMode);
+ bool openSubFile(const char *filename);
+
+ bool eof();
+ uint32 pos();
+ uint32 size();
+ void seek(int32 offs, int whence = SEEK_SET);
+ uint32 read(void *ptr, uint32 size);
+ uint32 write(const void *ptr, uint32 size);
+};
+
+
+// This is a constant lookup table of reverse bit masks
+extern const byte revBitMask[8];
+
+/* Direction conversion functions (between old dir and new dir format) */
+int newDirToOldDir(int dir);
+int oldDirToNewDir(int dir);
+
+int normalizeAngle(int angle);
+int fromSimpleDir(int dirtype, int dir);
+int toSimpleDir(int dirtype, int dir);
+
+void checkRange(int max, int min, int no, const char *str);
+
+const char *tag2str(uint32 tag);
+
+} // End of namespace Scumm
+
+#endif