Пример #1
0
void ERFFile::load() {
	Common::File erf;
	open(erf);

	readHeader(erf);

	if ((_id != kERFID) && (_id != kMODID) && (_id != kHAKID) && (_id != kSAVID))
		throw Common::Exception("Not an ERF file");

	if ((_version != kVersion1) && (_version != kVersion2))
		throw Common::Exception("Unsupported ERF file version %08X", _version);

	if ((_version == kVersion2) && !_utf16le)
		throw Common::Exception("ERF file version 2.0, but not UTF-16LE");

	try {

		ERFHeader erfHeader;

		readERFHeader  (erf, erfHeader);
		readDescription(erf, erfHeader);

		if (!_noResources)
			readResources(erf, erfHeader);

		if (erf.err())
			throw Common::Exception(Common::kReadError);

	} catch (Common::Exception &e) {
		e.add("Failed reading ERF file");
		throw e;
	}

}
Пример #2
0
void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) {
	Common::File file;
	if (!file.open(fileName)) {
		warning("Script file not found: %s", fileName.c_str());
		return;
	}

	while(!file.eos()) {
		Common::String line = file.readLine();
		if (file.err()) {
			warning("Error parsing scr file: %s", fileName.c_str());
			return;
		}

		trimCommentsAndWhiteSpace(&line);
		if (line.empty())
			continue;

		if (line.matchString("puzzle:*", true)) {
			Puzzle *puzzle = new Puzzle();
			sscanf(line.c_str(),"puzzle:%u",&(puzzle->key));

			parsePuzzle(puzzle, file);
			if (isGlobal) {
				_globalPuzzles.push_back(puzzle);
			} else {
				_activePuzzles.push_back(puzzle);
			}
		} else if (line.matchString("control:*", true)) {
			parseControl(line, file);
		}
	}
}
Пример #3
0
void BIFFile::load() {
	Common::File bif;
	open(bif);

	readHeader(bif);

	if (_id != kBIFID)
		throw Common::Exception("Not a BIF file");

	if ((_version != kVersion1) && (_version != kVersion11))
		throw Common::Exception("Unsupported BIF file version %08X", _version);

	uint32 varResCount = bif.readUint32LE();
	uint32 fixResCount = bif.readUint32LE();

	if (fixResCount != 0)
		throw Common::Exception("TODO: Fixed BIF resources");

	_iResources.resize(varResCount);

	uint32 offVarResTable = bif.readUint32LE();

	try {

		readVarResTable(bif, offVarResTable);

		if (bif.err())
			throw Common::Exception(Common::kReadError);

	} catch (Common::Exception &e) {
		e.add("Failed reading BIF file");
		throw e;
	}

}
Пример #4
0
void NDSFile::load() {
	Common::File nds;
	open(nds);

	if (!isNDS(nds))
		throw Common::Exception("Not a support NDS ROM file");

	nds.seek(0x40);

	uint32 fileNameTableOffset = nds.readUint32LE();
	uint32 fileNameTableLength = nds.readUint32LE();
	uint32 fatOffset           = nds.readUint32LE();
	//uint32 fatLength = nds.readUint32LE();

	try {

		readNames(nds, fileNameTableOffset, fileNameTableLength);
		readFAT(nds, fatOffset);

	if (nds.err())
		throw Common::Exception(Common::kReadError);

	} catch (Common::Exception &e) {
		e.add("Failed reading NDS file");
		throw;
	}

}
Пример #5
0
//Gets AGIPAL Data
void GfxMgr::setAGIPal(int p0) {
	//If 0 from savefile, do not use
	if (p0 == 0)
		return;

	char filename[15];
	sprintf(filename, "pal.%d", p0);

	Common::File agipal;
	if (!agipal.open(filename)) {
		warning("Couldn't open AGIPAL palette file '%s'. Not changing palette", filename);
		return; // Needed at least by Naturette 3 which uses AGIPAL but provides no palette files
	}

	//Chunk0 holds colors 0-7
	agipal.read(&_agipalPalette[0], 24);

	//Chunk1 is the same as the chunk0

	//Chunk2 chunk holds colors 8-15
	agipal.seek(24, SEEK_CUR);
	agipal.read(&_agipalPalette[24], 24);

	//Chunk3 is the same as the chunk2

	//Chunks4-7 are duplicates of chunks0-3

	if (agipal.eos() || agipal.err()) {
		warning("Couldn't read AGIPAL palette from '%s'. Not changing palette", filename);
		return;
	}

	// Use only the lowest 6 bits of each color component (Red, Green and Blue)
	// because VGA used only 6 bits per color component (i.e. VGA had 18-bit colors).
	// This should now be identical to the original AGIPAL-hack's behavior.
	bool validVgaPalette = true;
	for (int i = 0; i < 16 * 3; i++) {
		if (_agipalPalette[i] >= (1 << 6)) {
			_agipalPalette[i] &= 0x3F; // Leave only the lowest 6 bits of each color component
			validVgaPalette = false;
		}
	}

	if (!validVgaPalette)
		warning("Invalid AGIPAL palette (Over 6 bits per color component) in '%s'. Using only the lowest 6 bits per color component", filename);

	_agipalFileNum = p0;

	initPalette(_agipalPalette);
	gfxSetPalette();

	debug(1, "Using AGIPAL palette from '%s'", filename);
}
Пример #6
0
int AgiEngine::loadWords(const char *fname) {
	Common::File fp;

	if (!fp.open(fname)) {
		warning("loadWords: can't open %s", fname);
		return errOK; // err_BadFileOpen
	}
	debug(0, "Loading dictionary: %s", fname);

	// Loop through alphabet, as words in the dictionary file are sorted by
	// first character
	for (int i = 0; i < 26; i++) {
		fp.seek(i * 2, SEEK_SET);
		int offset = fp.readUint16BE();
		if (offset == 0)
			continue;
		fp.seek(offset, SEEK_SET);
		int k = fp.readByte();
		while (!fp.eos() && !fp.err()) {
			// Read next word
			char c, str[64];
			do {
				c = fp.readByte();
				str[k++] = (c ^ 0x7F) & 0x7F;
			} while (!(c & 0x80) && k < (int)sizeof(str) - 1);
			str[k] = 0;

			// WORKAROUND:
			// The SQ0 fan game stores words starting with numbers (like '7up')
			// in its dictionary under the 'a' entry. We skip these.
			// See bug #3615061
			if (str[0] == 'a' + i) {
				// And store it in our internal dictionary
				AgiWord *w = new AgiWord;
				w->word = myStrndup(str, k);
				w->id = fp.readUint16BE();
				_game.words[i].push_back(w);
			}

			k = fp.readByte();

			// Are there more words with an already known prefix?
			// WORKAROUND: We only break after already seeing words with the
			// right prefix, for the SQ0 words starting with digits filed under
			// 'a'. See above comment and bug #3615061.
			if (k == 0 && str[0] >= 'a' + i)
				break;
		}
	}

	return errOK;
}
Пример #7
0
void ResMan::resOpen(uint32 id) {  // load resource ID into memory
	MemHandle *memHandle = resHandle(id);
	if (!memHandle)
		return;
	if (memHandle->cond == MEM_FREED) { // memory has been freed
		uint32 size = resLength(id);
		_memMan->alloc(memHandle, size);
		Common::File *clusFile = resFile(id);
		assert(clusFile);
		clusFile->seek(resOffset(id));
		clusFile->read(memHandle->data, size);
		if (clusFile->err() || clusFile->eos()) {
			error("Can't read %d bytes from offset %d from cluster file %s\nResource ID: %d (%08X)", size, resOffset(id), _prj.clu[(id >> 24) - 1].label, id, id);
		}
Пример #8
0
/**
 * Allocates enough RAM to hold the global Glitter variables.
 */
void RegisterGlobals(int num) {
	if (pGlobals == NULL) {
		numGlobals = num;

		hMasterScript = !TinselV2 ? 0 :
			READ_LE_UINT32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT));

		// Allocate RAM for pGlobals and make sure it's allocated
		pGlobals = (int32 *)calloc(numGlobals, sizeof(int32));
		if (pGlobals == NULL) {
			error("Cannot allocate memory for global data");
		}

		// Allocate RAM for interpret contexts and make sure it's allocated
		icList = (INT_CONTEXT *)calloc(NUM_INTERPRET, sizeof(INT_CONTEXT));
		if (icList == NULL) {
			error("Cannot allocate memory for interpret contexts");
		}
		g_scheduler->setResourceCallback(FreeInterpretContextPr);
	} else {
		// Check size is still the same
		assert(numGlobals == num);

		memset(pGlobals, 0, numGlobals * sizeof(int32));
		memset(icList, 0, NUM_INTERPRET * sizeof(INT_CONTEXT));
	}

	if (TinselV2) {
		// read initial values
		CdCD(nullContext);

		Common::File f;
		if (!f.open(GLOBALS_FILENAME))
			error(CANNOT_FIND_FILE, GLOBALS_FILENAME);

		int32 length = f.readSint32LE();
		if (length != num)
			error(FILE_IS_CORRUPT, GLOBALS_FILENAME);

		for (int i = 0; i < length; ++i)
			pGlobals[i] = f.readSint32LE();

		if (f.eos() || f.err())
			error(FILE_IS_CORRUPT, GLOBALS_FILENAME);

		f.close();
	}
}
Пример #9
0
// AUDIOnnn.MAP contains 10-byte entries:
// Early format:
// w 5 bits resource type and 11 bits resource number
// dw 7 bits volume number and 25 bits offset
// dw size
// Later format:
// w nEntry
// dw offset+volume (as in resource.map)
// dw size
// ending with 10 0xFFs
int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
	Common::File file;

	if (!file.open(map->getLocationName()))
		return SCI_ERROR_RESMAP_NOT_FOUND;

	bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
	file.seek(0);

	for (;;) {
		uint16 n = file.readUint16LE();
		uint32 offset = file.readUint32LE();
		uint32 size = file.readUint32LE();

		if (file.eos() || file.err()) {
			warning("Error while reading %s", map->getLocationName().c_str());
			return SCI_ERROR_RESMAP_NOT_FOUND;
		}

		if (n == 0xffff)
			break;

		byte volume_nr;

		if (oldFormat) {
			n &= 0x07ff; // Mask out resource type
			volume_nr = offset >> 25; // most significant 7 bits
			offset &= 0x01ffffff; // least significant 25 bits
		} else {
			volume_nr = offset >> 28; // most significant 4 bits
			offset &= 0x0fffffff; // least significant 28 bits
		}

		ResourceSource *src = findVolume(map, volume_nr);

		if (src) {
			const ResourceId resId(kResourceTypeAudio, n);

			if (unload)
				removeAudioResource(resId);
			else
				addResource(resId, src, offset, size, map->getLocationName());
		} else {
			warning("Failed to find audio volume %i", volume_nr);
			return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
		}
	}
Пример #10
0
/**
 * Read sound (or music) file data.  Call with SILENCE to free-up
 * any allocated memory.  Also returns size of data
 */
SoundPtr FileManager::getSound(const int16 sound, uint16 *size) {
	debugC(1, kDebugFile, "getSound(%d)", sound);

	// No more to do if SILENCE (called for cleanup purposes)
	if (sound == _vm->_soundSilence)
		return 0;

	// Open sounds file
	Common::File fp;                                // Handle to SOUND_FILE
	if (!fp.open(getSoundFilename())) {
		warning("Hugo Error: File not found %s", getSoundFilename());
		return 0;
	}

	if (!_hasReadHeader) {
		for (int i = 0; i < kMaxSounds; i++) {
			_soundHdr[i]._size = fp.readUint16LE();
			_soundHdr[i]._offset = fp.readUint32LE();
		}
		if (fp.err())
			error("Wrong sound file format");
		_hasReadHeader = true;
	}

	*size = _soundHdr[sound]._size;
	if (*size == 0)
		error("Wrong sound file format or missing sound %d", sound);

	// Allocate memory for sound or music, if possible
	SoundPtr soundPtr = (byte *)malloc(_soundHdr[sound]._size); // Ptr to sound data
	assert(soundPtr);

	// Seek to data and read it
	fp.seek(_soundHdr[sound]._offset, SEEK_SET);
	if (fp.read(soundPtr, _soundHdr[sound]._size) != _soundHdr[sound]._size)
		error("Wrong sound file format");

	fp.close();

	return soundPtr;
}
Пример #11
0
void NDSFile::load() {
	Common::File nds;
	open(nds);

	if (!readHeader(nds))
		throw Common::Exception("Not a valid NDS ROM file");

	try {

		readNames(nds, _fileNameTableOffset, _fileNameTableLength);
		readFAT(nds, _fatOffset);

	if (nds.err())
		throw Common::Exception(Common::kReadError);

	} catch (Common::Exception &e) {
		e.add("Failed reading NDS file");
		throw;
	}

}
Пример #12
0
bool PCMMusicPlayer::getNextChunk() {
	MusicSegment *musicSegments;
	int32 *script, *scriptBuffer;
	int id;
	int snum;
	uint32 sampleOffset, sampleLength, sampleCLength;
	Common::File file;
	byte *buffer;
	Common::SeekableReadStream *sampleStream;

	switch (_state) {
	case S_NEW:
	case S_NEXT:
		_forcePlay = false;

		script = scriptBuffer = (int32 *)LockMem(_hScript);

		// Set parameters for this chunk of music
		id = _scriptNum;
		while (id--)
			script = scriptBuffer + READ_32(script);
		snum = FROM_32(script[_scriptIndex++]);

		if (snum == MUSIC_JUMP || snum == MUSIC_END) {
			// Let usual code sort it out!
			_scriptIndex--;    // Undo increment
			_forcePlay = true; // Force a Play
			_state = S_END1;   // 'Goto' S_END1
			break;
		}

		musicSegments = (MusicSegment *) LockMem(_hSegment);

		assert(FROM_32(musicSegments[snum].numChannels) == 1);
		assert(FROM_32(musicSegments[snum].bitsPerSample) == 16);

		sampleOffset = FROM_32(musicSegments[snum].sampleOffset);
		sampleLength = FROM_32(musicSegments[snum].sampleLength);
		sampleCLength = (((sampleLength + 63) & ~63)*33)/64;

		if (!file.open(_filename))
			error(CANNOT_FIND_FILE, _filename.c_str());

		file.seek(sampleOffset);
		if (file.eos() || file.err() || (uint32)file.pos() != sampleOffset)
			error(FILE_IS_CORRUPT, _filename.c_str());

		buffer = (byte *) malloc(sampleCLength);
		assert(buffer);

		// read all of the sample
		if (file.read(buffer, sampleCLength) != sampleCLength)
			error(FILE_IS_CORRUPT, _filename.c_str());

		debugC(DEBUG_DETAILED, kTinselDebugMusic, "Creating ADPCM music chunk with size %d, "
				"offset %d (script %d.%d)", sampleCLength, sampleOffset,
				_scriptNum, _scriptIndex - 1);

		sampleStream = new Common::MemoryReadStream(buffer, sampleCLength, DisposeAfterUse::YES);

		delete _curChunk;
		_curChunk = new Tinsel8_ADPCMStream(sampleStream, DisposeAfterUse::YES, sampleCLength,
				22050, 1, 32);

		_state = S_MID;
		return true;

	case S_END1:
		debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END1 (script %d.%d)",
				_scriptNum, _scriptIndex);

		script = scriptBuffer = (int32 *) LockMem(_hScript);

		id = _scriptNum;
		while (id--)
			script = scriptBuffer + READ_32(script);
		snum = FROM_32(script[_scriptIndex]);

		if (snum == MUSIC_END) {
			_state = S_END2;
		} else {
			if (snum == MUSIC_JUMP)
				_scriptIndex = FROM_32(script[_scriptIndex+1]);

			_state = _forcePlay ? S_NEW : S_NEXT;
			_forcePlay = false;
		}

		return true;

	case S_END2:
		debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END2 (script %d.%d)",
				_scriptNum, _scriptIndex);

		_silenceSamples = 11025; // Half a second of silence
		return true;

	case S_END3:
		debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END3 (script %d.%d)",
				_scriptNum, _scriptIndex);

		stop();
		_state = S_IDLE;
		return false;

	case S_IDLE:
		return false;

	default:
		break;
	}

	return true;
}
Пример #13
0
/**
 * Opens and inits all MIDI sequence files.
 */
void OpenMidiFiles() {
	Common::File midiStream;

	if (TinselV0) {
		// The early demo version of DW1 doesn't have MIDI
	} else if (TinselV2) {
		// DW2 uses a different music mechanism
	} else if (TinselV1Mac) {
		// open MIDI sequence file in binary mode
		if (!midiStream.open(MIDI_FILE))
			error(CANNOT_FIND_FILE, MIDI_FILE);

		uint32 curTrack = 1;
		uint32 songLength = 0;
		int32 fileSize = midiStream.size();

		// Init
		for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++)
			g_midiOffsets[i] = 0;

		midiStream.skip(4);	// skip file header

		while (!midiStream.eos() && !midiStream.err() && midiStream.pos() != fileSize) {
			assert(curTrack < ARRAYSIZE(g_midiOffsets));
			g_midiOffsets[curTrack] = midiStream.pos();
			//debug("%d: %d", curTrack, g_midiOffsets[curTrack]);

			songLength = midiStream.readUint32BE();
			midiStream.skip(songLength);

			curTrack++;
		}

		midiStream.close();
	} else {
		if (g_midiBuffer.pDat)
			// already allocated
			return;

		// open MIDI sequence file in binary mode
		if (!midiStream.open(MIDI_FILE))
			error(CANNOT_FIND_FILE, MIDI_FILE);

		// get length of the largest sequence
		g_midiBuffer.size = midiStream.readUint32LE();
		if (midiStream.eos() || midiStream.err())
			error(FILE_IS_CORRUPT, MIDI_FILE);

		if (g_midiBuffer.size) {
			// allocate a buffer big enough for the largest MIDI sequence
			if ((g_midiBuffer.pDat = (uint8 *)malloc(g_midiBuffer.size)) != NULL) {
				// clear out the buffer
				memset(g_midiBuffer.pDat, 0, g_midiBuffer.size);
			}
		}

		// Now scan through the contents of the MIDI file to find the offset
		// of each individual track, in order to create a mapping from MIDI
		// offset to track number, for the enhanced MIDI soundtrack.
		// The first song is always at position 4. The subsequent ones are
		// calculated dynamically.
		uint32 curOffset = 4;
		uint32 curTrack = 0;
		uint32 songLength = 0;

		// Init
		for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++)
			g_midiOffsets[i] = 0;

		while (!midiStream.eos() && !midiStream.err()) {
			if (curOffset + (4 * curTrack) >= (uint32)midiStream.size())
				break;

			assert(curTrack < ARRAYSIZE(g_midiOffsets));
			g_midiOffsets[curTrack] = curOffset + (4 * curTrack);
			//debug("%d: %d", curTrack, midiOffsets[curTrack]);

			songLength = midiStream.readUint32LE();
			curOffset += songLength;
			midiStream.skip(songLength);

			curTrack++;
		}

		midiStream.close();
	}
}
Пример #14
0
/**
 * Plays an animated cutscene.
 * @param id the id of the file
 */
bool MoviePlayer::load(uint32 id) {
	Common::File f;
	Common::String filename;

	if (_decoderType == kVideoDecoderDXA)
		_bgSoundStream = Audio::SeekableAudioStream::openStreamFile(sequenceList[id]);
	else
		_bgSoundStream = NULL;

	if (SwordEngine::_systemVars.showText) {
		filename = Common::String::format("%s.txt", sequenceList[id]);
		if (f.open(filename)) {
			Common::String line;
			int lineNo = 0;
			int lastEnd = -1;

			_movieTexts.clear();
			while (!f.eos() && !f.err()) {
				line = f.readLine();
				lineNo++;
				if (line.empty() || line[0] == '#') {
					continue;
				}

				const char *ptr = line.c_str();

				// TODO: Better error handling
				int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
				int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);

				while (*ptr && Common::isSpace(*ptr))
					ptr++;

				if (startFrame > endFrame) {
					warning("%s:%d: startFrame (%d) > endFrame (%d)", filename.c_str(), lineNo, startFrame, endFrame);
					continue;
				}

				if (startFrame <= lastEnd) {
					warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd);
					continue;
				}
				
				int color = 0;
				if (*ptr == '@') {
					++ptr;
					color = strtoul(ptr, const_cast<char **>(&ptr), 10);
					while (*ptr && Common::isSpace(*ptr))
						ptr++;
				} 

				_movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color));
				lastEnd = endFrame;
			}
			f.close();
		}
	}

	switch (_decoderType) {
	case kVideoDecoderDXA:
		filename = Common::String::format("%s.dxa", sequenceList[id]);
		break;
	case kVideoDecoderSMK:
		filename = Common::String::format("%s.smk", sequenceList[id]);
		break;
	case kVideoDecoderPSX:
		filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]);

		// Need to switch to true color
		initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0);

		// Need to load here in case it fails in which case we'd need
		// to go back to paletted mode
		if (_decoder->loadFile(filename)) {
			return true;
		} else {
			initGraphics(g_system->getWidth(), g_system->getHeight(), true);
			return false;
		}
		break;
	}

	return _decoder->loadFile(filename.c_str());
}
Пример #15
0
/**
 * Opens and inits all MIDI sequence files.
 */
void OpenMidiFiles() {
	Common::File midiStream;

	// Demo version has no midi file
	// Also, Discworld PSX uses still unsupported psx SEQ format for music...
	if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2) || TinselV1PSX)
		return;

	if (midiBuffer.pDat)
		// already allocated
		return;

	// open MIDI sequence file in binary mode
	if (!midiStream.open(MIDI_FILE))
		error(CANNOT_FIND_FILE, MIDI_FILE);

	// gen length of the largest sequence
	midiBuffer.size = midiStream.readUint32LE();
	if (midiStream.eos() || midiStream.err())
		error(FILE_IS_CORRUPT, MIDI_FILE);

	if (midiBuffer.size) {
		// allocate a buffer big enough for the largest MIDI sequence
		if ((midiBuffer.pDat = (uint8 *)malloc(midiBuffer.size)) != NULL) {
			// clear out the buffer
			memset(midiBuffer.pDat, 0, midiBuffer.size);
//			VMM_lock(midiBuffer.pDat, midiBuffer.size);
		} else {
			//mSeqHandle = NULL;
		}
	}

	// Now scan through the contents of the MIDI file to find the offset
	// of each individual track, in order to create a mapping from MIDI
	// offset to track number, for the enhanced MIDI soundtrack.
	// The first song is always at position 4. The subsequent ones are
	// calculated dynamically.
	uint32 curOffset = 4;
	uint32 curTrack = 0;
	uint32 songLength = 0;

	// Init
	for (int i = 0; i < ARRAYSIZE(midiOffsets); i++)
		midiOffsets[i] = 0;

	while (!midiStream.eos() && !midiStream.err()) {
		if (curOffset + (4 * curTrack) >= (uint32)midiStream.size())
			break;

		assert(curTrack < ARRAYSIZE(midiOffsets));
		midiOffsets[curTrack] = curOffset + (4 * curTrack);
		//debug("%d: %d", curTrack, midiOffsets[curTrack]);

		songLength = midiStream.readUint32LE();
		curOffset += songLength;
		midiStream.skip(songLength);

		curTrack++;
	}

	midiStream.close();
}
Пример #16
0
bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
	if (_cowHeader == NULL) {
		warning("Sound::startSpeech: COW file isn't open");
		return false;
	}

	uint32 locIndex = 0xFFFFFFFF;
	uint32 sampleSize = 0;
	uint32 index = 0;

	if (_cowMode == CowPSX) {
		Common::File file;
		uint16 i;

		if (!file.open("speech.lis")) {
			warning ("Could not open speech.lis");
			return false;
		}

		for (i = 0; !file.eos() && !file.err(); i++)
			if (file.readUint16LE() == roomNo) {
				locIndex = i;
				break;
			}
		file.close();

		if (locIndex == 0xFFFFFFFF) {
			warning ("Could not find room %d in speech.lis", roomNo);
			return false;
		}

		if (!file.open("speech.inf")) {
			warning ("Could not open speech.inf");
			return false;
		}

		uint16 numRooms = file.readUint16LE(); // Read number of rooms referenced in this file

		file.seek(locIndex * 4 + 2); // 4 bytes per room, skip first 2 bytes

		uint16 numLines = file.readUint16LE();
		uint16 roomOffset = file.readUint16LE();

		file.seek(2 + numRooms * 4 + roomOffset * 2); // The offset is in terms of uint16's, so multiply by 2. Skip the room indexes too.

		locIndex = 0xFFFFFFFF;

		for (i = 0; i < numLines; i++)
			if (file.readUint16LE() == localNo) {
				locIndex = i;
				break;
			}

		if (locIndex == 0xFFFFFFFF) {
			warning ("Could not find local number %d in room %d in speech.inf", roomNo, localNo);
			return false;
		}

		file.close();

		index = _cowHeader[(roomOffset + locIndex) * 2];
		sampleSize = _cowHeader[(roomOffset + locIndex) * 2 + 1];
	} else {
		locIndex = _cowHeader[roomNo] >> 2;
		sampleSize = _cowHeader[locIndex + (localNo * 2)];
		index = _cowHeader[locIndex + (localNo * 2) - 1];
	}

	debug(6, "startSpeech(%d, %d): locIndex %d, sampleSize %d, index %d", roomNo, localNo, locIndex, sampleSize, index);

	Audio::AudioStream *stream = 0;

	if (sampleSize) {
		uint8 speechVol = (_speechVolR + _speechVolL) / 2;
		int8 speechPan = (_speechVolR - _speechVolL) / 2;
		if ((_cowMode == CowWave) || (_cowMode == CowDemo)) {
			uint32 size;
			int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size);
			if (data) {
				stream = Audio::makeRawStream((byte *)data, size, 11025, SPEECH_FLAGS);
				_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
			}
		} else if (_cowMode == CowPSX && sampleSize != 0xffffffff) {
			_cowFile.seek(index * 2048);
			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
			assert(tmp);
			stream = Audio::makeVagStream(tmp);
			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
			// with compressed audio, we can't calculate the wave volume.
			// so default to talking.
			for (int cnt = 0; cnt < 480; cnt++)
				_waveVolume[cnt] = true;
			_waveVolPos = 0;
		}
#ifdef USE_FLAC
		else if (_cowMode == CowFLAC) {
			_cowFile.seek(index);
			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
			assert(tmp);
			stream = Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
			// with compressed audio, we can't calculate the wave volume.
			// so default to talking.
			for (int cnt = 0; cnt < 480; cnt++)
				_waveVolume[cnt] = true;
			_waveVolPos = 0;
		}
#endif
#ifdef USE_VORBIS
		else if (_cowMode == CowVorbis) {
			_cowFile.seek(index);
			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
			assert(tmp);
			stream = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
			// with compressed audio, we can't calculate the wave volume.
			// so default to talking.
			for (int cnt = 0; cnt < 480; cnt++)
				_waveVolume[cnt] = true;
			_waveVolPos = 0;
		}
#endif
#ifdef USE_MAD
		else if (_cowMode == CowMP3) {
			_cowFile.seek(index);
			Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
			assert(tmp);
			stream = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
			_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
			// with compressed audio, we can't calculate the wave volume.
			// so default to talking.
			for (int cnt = 0; cnt < 480; cnt++)
				_waveVolume[cnt] = true;
			_waveVolPos = 0;
		}
#endif
		return true;
	} else
		return false;
}
Пример #17
0
Common::Error GagEngine::StateScript()
{
	Common::Error status(Common::kNoError);

	Common::File file;
	if(file.open(_script, *_archive))
	{
		// simple script parsing: skim through, find section and execute commands
		ParserState parse_state(PS_SECTION_SEARCH);
		Common::String buffer;
		Common::String command_name;

		bool done(false);
		while(!file.eos())
		{
			// add space for multiline command support
			Common::String line = file.readLine() + ' ';
			if(file.err())
			{
				status = Common::Error(Common::kReadingFailed, _script + ", readLine()");
				break;
			}

			bool skip_line(false);
			for(Common::String::const_iterator it = line.begin(); it != line.end(); ++it)
			{
				switch(parse_state)
				{
				case PS_SECTION_SEARCH:
					// section
					if(*it == '[')
					{
						buffer.clear();

						parse_state = PS_SECTION_NAME;
					}
					break;

				case PS_SECTION_NAME:
					// section end
					if(*it == ']')
					{
#ifdef DEBUG_SKIM_SCRIPT
						parse_state = PS_SECTION_BODY;
#else
						if(buffer == _section)
							parse_state = PS_SECTION_BODY;
						else if(buffer == _END_SECTION)
						{
							status = Common::Error(Common::kUnknownError, "[" + _section + "] script section not found");
							skip_line = true;
							done = true;
						}
						else
							parse_state = PS_SECTION_SEARCH;
#endif
					}
					else
						buffer += *it;
					break;

				case PS_SECTION_BODY:
					// section
					if(*it == '[')
					{
#ifdef DEBUG_SKIM_SCRIPT
						buffer.clear();
						parse_state = PS_SECTION_NAME;
#else
						skip_line = true;
						done = true;
#endif
					}
					// comment
					else if(*it == '*')
					{
						skip_line = true;
					}
					//NOTE: invalid syntax
					else if(*it == '-' || *it == '/' || (*it >= 'A' && *it <= 'Z'))
					{
#ifndef DEBUG_SKIM_SCRIPT
						warning("invalid script syntax [file: %s, section: %s, line: \"%s\"], skipped", _script.c_str(), _section.c_str(), line.c_str());
#endif
						skip_line = true;
					}
					// command name
					else if((*it >= 'a' && *it <= 'z'))
					{
						buffer.clear();
						buffer += *it;

						parse_state = PS_COMMAND_NAME;
					}
					break;

				case PS_COMMAND_NAME:
					// command value
					if(*it == '=')
					{
						command_name = buffer;
						buffer.clear();

						parse_state = PS_COMMAND_VALUE;
					}
					else
						buffer += *it;
					break;

				case PS_COMMAND_VALUE:
					// command value end
					if(*it == ';')
					{
						command_name.trim();
						buffer.trim();

#ifndef DEBUG_SKIM_SCRIPT
						debug("%s=%s;", command_name.c_str(), buffer.c_str());
#endif

						Common::HashMap<Common::String, Common::Error (GagEngine::*)(const Common::String &)>::const_iterator f = _commandCallbacks.find(command_name);
						if(f != _commandCallbacks.end())
						{
							status = (this->*f->_value)(buffer);
							if(status.getCode() != Common::kNoError)
							{
								skip_line = true;
								done = true;
							}
						}

						parse_state = PS_SECTION_BODY;
					}
					else
						buffer += *it;
					break;
				}

				if(skip_line)
					break;
			}

			if(done)
				break;
		}
	}
	else
	{
		status = Common::Error(Common::kReadingFailed, _script + ", open()");
	}

	return status;
}
Пример #18
0
/**
 * Plays an animated cutscene.
 * @param id the id of the file
 */
bool MoviePlayer::load(uint32 id) {
	Common::String filename;

	if (SwordEngine::_systemVars.showText) {
		Common::File f;
		filename = Common::String::format("%s.txt", sequenceList[id]);

		if (f.open(filename)) {
			Common::String line;
			int lineNo = 0;
			int lastEnd = -1;

			_movieTexts.clear();
			while (!f.eos() && !f.err()) {
				line = f.readLine();
				lineNo++;
				if (line.empty() || line[0] == '#') {
					continue;
				}

				const char *ptr = line.c_str();

				// TODO: Better error handling
				int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
				int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);

				while (*ptr && Common::isSpace(*ptr))
					ptr++;

				if (startFrame > endFrame) {
					warning("%s:%d: startFrame (%d) > endFrame (%d)", filename.c_str(), lineNo, startFrame, endFrame);
					continue;
				}

				if (startFrame <= lastEnd) {
					warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd);
					continue;
				}

				int color = 0;
				if (*ptr == '@') {
					++ptr;
					color = strtoul(ptr, const_cast<char **>(&ptr), 10);
					while (*ptr && Common::isSpace(*ptr))
						ptr++;
				}

				_movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color));
				lastEnd = endFrame;
			}
		}
	}

	switch (_decoderType) {
	case kVideoDecoderDXA:
		filename = Common::String::format("%s.dxa", sequenceList[id]);
		break;
	case kVideoDecoderSMK:
		filename = Common::String::format("%s.smk", sequenceList[id]);
		break;
	case kVideoDecoderPSX:
		filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]);
		break;
	case kVideoDecoderMP2:
		filename = Common::String::format("%s.mp2", sequenceList[id]);
		break;
	}

	// Need to switch to true color for PSX/MP2 videos
	if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2)
		initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0);

	if (!_decoder->loadFile(filename)) {
		// Go back to 8bpp color
		if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2)
			initGraphics(g_system->getWidth(), g_system->getHeight(), true);

		return false;
	}

	// For DXA/MP2, also add the external sound file
	if (_decoderType == kVideoDecoderDXA || _decoderType == kVideoDecoderMP2)
		_decoder->addStreamFileTrack(sequenceList[id]);

	_decoder->start();
	return true;
}
Пример #19
0
bool Sword2Engine::initStartMenu() {
	// Print out a list of all the start points available.
	// There should be a linc produced file called startup.txt.
	// This file should contain ascii numbers of all the resource game
	// objects that are screen managers.
	// We query each in turn and setup an array of start structures.
	// If the file doesn't exist then we say so and return a 0.

	Common::File fp;

	// ok, load in the master screen manager file

	_totalStartups = 0;
	_totalScreenManagers = 0;

	if (!fp.open("startup.inf")) {
		warning("Cannot open startup.inf - the debugger won't have a start menu");
		return false;
	}

	// The startup.inf file which contains a list of all the files. Now
	// extract the filenames

	int start_ids[MAX_starts];
	int lineno = 0;

	while (!fp.eos() && !fp.err()) {
		Common::String line = fp.readLine();

		// Skip empty lines or, more likely, the end of the stream.
		if (line.size() == 0)
			continue;

		char *errptr;
		int id;

		lineno++;
		id = strtol(line.c_str(), &errptr, 10);

		if (*errptr) {
			warning("startup.inf:%d: Invalid string '%s'", lineno, line.c_str());
			continue;
		}

		if (!_resman->checkValid(id)) {
			warning("startup.inf:%d: Invalid resource %d", lineno, id);
			continue;
		}

		if (_resman->fetchType(id) != SCREEN_MANAGER) {
			warning("startup.inf:%d: '%s' (%d) is not a screen manager", lineno, _resman->fetchName(id), id);
			continue;
		}

		start_ids[_totalScreenManagers] = id;

		if (++_totalScreenManagers >= MAX_starts) {
			warning("Too many entries in startup.inf");
			break;
		}
	}

	// An I/O error before EOS? That's bad, but this is not a vital file.
	if (fp.err() && !fp.eos())
		warning("I/O error while reading startup.inf");

	fp.close();

	// Using this method the Gode generated resource.inf must have #0d0a
	// on the last entry

	debug(1, "%d screen manager objects", _totalScreenManagers);

	// Open each object and make a query call. The object must fill in a
	// startup structure. It may fill in several if it wishes - for
	// instance a startup could be set for later in the game where
	// specific vars are set

	for (uint i = 0; i < _totalScreenManagers; i++) {
		_startRes = start_ids[i];

		debug(2, "Querying screen manager %d", _startRes);

		// Open each one and run through the interpreter. Script 0 is
		// the query request script. We have already made reasonably
		// sure the resource is ok.

		_logic->runResScript(_startRes, 0);
	}

	return 1;
}
Пример #20
0
bool ResourceManager::init() {
    uint32 i, j;

    // Until proven differently, assume we're on CD 1. This is so the start
    // dialog will be able to play any music at all.

    setCD(1);

    // We read in the resource info which tells us the names of the
    // resource cluster files ultimately, although there might be groups
    // within the clusters at this point it makes no difference. We only
    // wish to know what resource files there are and what is in each

    Common::File file;

    if (!file.open("resource.inf")) {
        GUIErrorMessage("Broken Sword II: Cannot open resource.inf");
        return false;
    }

    // The resource.inf file is a simple text file containing the names of
    // all the resource files.

    while (1) {
        char *buf = _resFiles[_totalClusters].fileName;
        uint len = sizeof(_resFiles[_totalClusters].fileName);

        if (!file.readLine(buf, len))
            break;

        int pos = strlen(buf);
        if (buf[pos - 1] == 0x0A)
            buf[pos - 1] = 0;

        _resFiles[_totalClusters].numEntries = -1;
        _resFiles[_totalClusters].entryTab = NULL;
        if (++_totalClusters >= MAX_res_files) {
            GUIErrorMessage("Broken Sword II: Too many entries in resource.inf");
            return false;
        }
    }

    file.close();

    // Now load in the binary id to res conversion table
    if (!file.open("resource.tab")) {
        GUIErrorMessage("Broken Sword II: Cannot open resource.tab");
        return false;
    }

    // Find how many resources
    uint32 size = file.size();

    _totalResFiles = size / 4;

    // Table seems ok so malloc some space
    _resConvTable = (uint16 *)malloc(size);

    for (i = 0; i < size / 2; i++)
        _resConvTable[i] = file.readUint16LE();

    if (file.eos() || file.err()) {
        file.close();
        GUIErrorMessage("Broken Sword II: Cannot read resource.tab");
        return false;
    }

    file.close();

    // Check that we have cd.inf file, unless we are running PSX
    // version, which has all files on one disc.

    if (!file.open("cd.inf") && !Sword2Engine::isPsx()) {
        GUIErrorMessage("Broken Sword II: Cannot open cd.inf");
        return false;
    }

    CdInf *cdInf = new CdInf[_totalClusters];

    for (i = 0; i < _totalClusters; i++) {

        if (Sword2Engine::isPsx()) { // We are running PSX version, artificially fill CdInf structure
            cdInf[i].cd = CD1;
        } else { // We are running PC version, read cd.inf file
            file.read(cdInf[i].clusterName, sizeof(cdInf[i].clusterName));

            cdInf[i].cd = file.readByte();

            if (file.eos() || file.err()) {
                delete[] cdInf;
                file.close();
                GUIErrorMessage("Broken Sword II: Cannot read cd.inf");
                return false;
            }

        }

        // It has been reported that there are two different versions
        // of the cd.inf file: One where all clusters on CD also have
        // the LOCAL_CACHE bit set. This bit is no longer used. To
        // avoid future problems, let's normalize the flag once and for
        // all here.

        if (cdInf[i].cd & LOCAL_PERM)
            cdInf[i].cd = 0;
        else if (cdInf[i].cd & CD1)
            cdInf[i].cd = 1;
        else if (cdInf[i].cd & CD2)
            cdInf[i].cd = 2;
        else
            cdInf[i].cd = 0;

        // Any file on "CD 0" may be needed at all times. Verify that
        // it exists. Any other missing cluster will be requested with
        // an "insert CD" message. Of course, the file may still vanish
        // during game-play (oh, that wascally wabbit!) in which case
        // the resource manager will print a fatal error.

        if (cdInf[i].cd == 0 && !Common::File::exists((char *)cdInf[i].clusterName)) {
            GUIErrorMessage("Broken Sword II: Cannot find " + Common::String((char *)cdInf[i].clusterName));
            delete[] cdInf;
            return false;
        }
    }

    file.close();

    // We check the presence of resource files in cd.inf
    // This is ok in PC version, but in PSX version we don't
    // have cd.inf so we'll have to skip this.
    if (!Sword2Engine::isPsx()) {
        for (i = 0; i < _totalClusters; i++) {
            for (j = 0; j < _totalClusters; j++) {
                if (scumm_stricmp((char *)cdInf[j].clusterName, _resFiles[i].fileName) == 0)
                    break;
            }

            if (j == _totalClusters) {
                delete[] cdInf;
                GUIErrorMessage(Common::String(_resFiles[i].fileName) + " is not in cd.inf");
                return false;
            }

            _resFiles[i].cd = cdInf[j].cd;
        }
    }

    delete[] cdInf;

    debug(1, "%d resources in %d cluster files", _totalResFiles, _totalClusters);
    for (i = 0; i < _totalClusters; i++)
        debug(2, "filename of cluster %d: -%s (%d)", i, _resFiles[i].fileName, _resFiles[i].cd);

    _resList = (Resource *)malloc(_totalResFiles * sizeof(Resource));

    for (i = 0; i < _totalResFiles; i++) {
        _resList[i].ptr = NULL;
        _resList[i].size = 0;
        _resList[i].refCount = 0;
        _resList[i].prev = _resList[i].next = NULL;
    }

    return true;
}