diff options
| -rw-r--r-- | scumm/module.mk | 1 | ||||
| -rw-r--r-- | scumm/resource.cpp | 1191 | ||||
| -rw-r--r-- | scumm/scumm.cpp | 85 | ||||
| -rw-r--r-- | scumm/scumm.h | 39 | ||||
| -rw-r--r-- | scumm/sound.cpp | 1046 | ||||
| -rw-r--r-- | scumm/util.cpp | 272 | ||||
| -rw-r--r-- | scumm/util.h | 71 | 
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  | 
