Пример #1
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);
		}
	}
}
Пример #2
0
bool ThemeEngine::themeConfigUsable(const Common::FSNode &node, Common::String &themeName) {
	Common::File stream;
	bool foundHeader = false;

	if (node.getName().matchString("*.zip", true) && !node.isDirectory()) {
		Common::Archive *zipArchive = Common::makeZipArchive(node);
		if (zipArchive && zipArchive->hasFile("THEMERC")) {
			// Open THEMERC from the ZIP file.
			stream.open("THEMERC", *zipArchive);
		}
		// Delete the ZIP archive again. Note: This only works because
		// stream.open() only uses ZipArchive::createReadStreamForMember,
		// and that in turn happens to read all the data for a given
		// archive member into a memory block. So there will be no dangling
		// reference to zipArchive anywhere. This could change if we
		// ever modify ZipArchive::createReadStreamForMember.
		delete zipArchive;
	} else if (node.isDirectory()) {
		Common::FSNode headerfile = node.getChild("THEMERC");
		if (!headerfile.exists() || !headerfile.isReadable() || headerfile.isDirectory())
			return false;
		stream.open(headerfile);
	}

	if (stream.isOpen()) {
		Common::String stxHeader = stream.readLine();
		foundHeader = themeConfigParseHeader(stxHeader, themeName);
	}

	return foundHeader;
}
Пример #3
0
void Resource::readStaticText() {
	Common::File *labTextFile = openDataFile("Lab:Rooms/LabText");

	for (int i = 0; i < 48; i++)
		_staticText[i] = labTextFile->readLine();

	delete labTextFile;
}
Пример #4
0
bool ThemeEngine::loadThemeXML(const Common::String &themeId) {
	assert(_parser);
	assert(_themeArchive);

	_themeName.clear();


	//
	// Now that we have a Common::Archive, verify that it contains a valid THEMERC File
	//
	Common::File themercFile;
	themercFile.open("THEMERC", *_themeArchive);
	if (!themercFile.isOpen()) {
		warning("Theme '%s' contains no 'THEMERC' file.", themeId.c_str());
		return false;
	}

	Common::String stxHeader = themercFile.readLine();
	if (!themeConfigParseHeader(stxHeader, _themeName) || _themeName.empty()) {
		warning("Corrupted 'THEMERC' file in theme '%s'", themeId.c_str());
		return false;
	}

	Common::ArchiveMemberList members;
	if (0 == _themeArchive->listMatchingMembers(members, "*.stx")) {
		warning("Found no STX files for theme '%s'.", themeId.c_str());
		return false;
	}

	//
	// Loop over all STX files, load and parse them
	//
	for (Common::ArchiveMemberList::iterator i = members.begin(); i != members.end(); ++i) {
		assert((*i)->getName().hasSuffix(".stx"));

		if (_parser->loadStream((*i)->createReadStream()) == false) {
			warning("Failed to load STX file '%s'", (*i)->getDisplayName().c_str());
			_parser->close();
			return false;
		}

		if (_parser->parse() == false) {
			warning("Failed to parse STX file '%s'", (*i)->getDisplayName().c_str());
			_parser->close();
			return false;
		}

		_parser->close();
	}

	assert(!_themeName.empty());
	return true;
}
Пример #5
0
TLib::TLib(MemoryManager &memManager, const Common::String &filename) :
		_memoryManager(memManager) {

	// If the resource strings list isn't yet loaded, load them
	if (_resStrings.size() == 0) {
		Common::File f;
		if (f.open("tsage.cfg")) {
			while (!f.eos()) {
				_resStrings.push_back(f.readLine());
			}
			f.close();
		}
	}

	if (!_file.open(filename))
		error("Missing file %s", filename.c_str());

	loadIndex();
}
Пример #6
0
bool ThemeEngine::themeConfigUsable(const Common::ArchiveMember &member, Common::String &themeName) {
	Common::File stream;
	bool foundHeader = false;

	if (member.getName().matchString("*.zip", true)) {
		Common::Archive *zipArchive = Common::makeZipArchive(member.createReadStream());

		if (zipArchive && zipArchive->hasFile("THEMERC")) {
			stream.open("THEMERC", *zipArchive);
		}

		delete zipArchive;
	}

	if (stream.isOpen()) {
		Common::String stxHeader = stream.readLine();
		foundHeader = themeConfigParseHeader(stxHeader, themeName);
	}

	return foundHeader;
}
Пример #7
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;
}
Пример #8
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());
}
Пример #9
0
void Screen::rollCredits() {
	uint32 loopingMusicId = _vm->_sound->getLoopingMusicId();

	// Prepare for the credits by fading down, stoping the music, etc.

	_vm->_mouse->setMouse(0);

	_vm->_sound->muteFx(true);
	_vm->_sound->muteSpeech(true);

	waitForFade();
	fadeDown();
	waitForFade();

	_vm->_mouse->closeMenuImmediately();

	// There are three files which I believe are involved in showing the
	// credits:
	//
	// credits.bmp  - The "Smacker" logo, stored as follows:
	//
	//     width     2 bytes, little endian
	//     height    2 bytes, little endian
	//     palette   3 * 256 bytes
	//     data      width * height bytes
	//
	//     Note that the maximum colour component in the palette is 0x3F.
	//     This is the same resolution as the _paletteMatch table. I doubt
	//     that this is a coincidence, but let's use the image palette
	//     directly anyway, just to be safe.
	//
	// credits.clu  - The credits text (credits.txt in PSX version)
	//
	//     This is simply a text file with CRLF line endings.
	//     '^' is not shown, but used to mark the center of the line.
	//     '@' is used as a placeholder for the "Smacker" logo. At least
	//     when it appears alone.
	//     Remaining lines are centered.
	//     The German version also contains character code 9 for no
	//     apparent reason. We ignore them.
	//
	// fonts.clu    - The credits font?
	//
	//     FIXME: At this time I don't know how to interpret fonts.clu. For
	//     now, let's just the standard speech font instead.

	SpriteInfo spriteInfo;
	Common::File f;
	int i;

	spriteInfo.isText = false;

	// Read the "Smacker" logo

	uint16 logoWidth = 0;
	uint16 logoHeight = 0;
	byte *logoData = NULL;
	byte palette[256 * 3];

	if (f.open("credits.bmp")) {
		logoWidth = f.readUint16LE();
		logoHeight = f.readUint16LE();

		for (i = 0; i < 256; i++) {
			palette[i * 3 + 0] = f.readByte() << 2;
			palette[i * 3 + 1] = f.readByte() << 2;
			palette[i * 3 + 2] = f.readByte() << 2;
		}

		logoData = (byte *)malloc(logoWidth * logoHeight);

		f.read(logoData, logoWidth * logoHeight);
		f.close();
	} else {
		warning("Can't find credits.bmp");
		memset(palette, 0, sizeof(palette));
		palette[14 * 3 + 0] = 252;
		palette[14 * 3 + 1] = 252;
		palette[14 * 3 + 2] = 252;
	}

	setPalette(0, 256, palette, RDPAL_INSTANT);

	// Read the credits text

	Common::Array<CreditsLine *> creditsLines;

	int lineCount = 0;
	int lineTop = 400;
	int paragraphStart = 0;
	bool hasCenterMark = false;

	if (Sword2Engine::isPsx()) {
		if (!f.open("credits.txt")) {
			warning("Can't find credits.txt");

			free(logoData);
			return;
		}
	} else {
		if (!f.open("credits.clu")) {
			warning("Can't find credits.clu");

			free(logoData);
			return;
		}
	}

	while (1) {
		char buffer[80];
		char *line = f.readLine(buffer, sizeof(buffer));

		if (line) {
			// Replace invalid character codes prevent the 'dud'
			// symbol from showing up in the credits.

			for (byte *ptr = (byte *)line; *ptr; ptr++) {
				switch (*ptr) {
				case 9:
					// The German credits contain these.
					// Convert them to spaces.
					*ptr = 32;
					break;
				case 10:
					// LF is treated as end of line.
					*ptr = 0;
					break;
				case 170:
					// The Spanish credits contain these.
					// Convert them to periods.
					*ptr = '.';
				default:
					break;
				}
			}
		}

		if (!line || *line == 0) {
			if (!hasCenterMark) {
				for (i = paragraphStart; i < lineCount; i++)
					creditsLines[i]->type = LINE_CENTER;
			}
			paragraphStart = lineCount;
			hasCenterMark = false;
			if (paragraphStart == lineCount)
				lineTop += CREDITS_LINE_SPACING;

			if (!line)
				break;

			continue;
		}

		char *center_mark = strchr(line, '^');

		if (center_mark) {
			// The current paragraph has at least one center mark.
			hasCenterMark = true;

			if (center_mark != line) {
				creditsLines.push_back(new CreditsLine);

				// The center mark is somewhere inside the
				// line. Split it into left and right side.
				*center_mark = 0;

				creditsLines[lineCount]->top = lineTop;
				creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
				creditsLines[lineCount]->type = LINE_LEFT;
				creditsLines[lineCount]->str = strdup(line);

				lineCount++;
				*center_mark = '^';
			}

			line = center_mark;
		}

		creditsLines.push_back(new CreditsLine);

		creditsLines[lineCount]->top = lineTop;

		if (*line == '^') {
			creditsLines[lineCount]->type = LINE_RIGHT;
			line++;
		} else
			creditsLines[lineCount]->type = LINE_LEFT;

		if (strcmp(line, "@") == 0) {
			creditsLines[lineCount]->height = logoHeight;
			lineTop += logoHeight;
		} else {
			creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
			lineTop += CREDITS_LINE_SPACING;
		}

		creditsLines[lineCount]->str = strdup(line);
		lineCount++;
	}

	f.close();

	// We could easily add some ScummVM stuff to the credits, if we wanted
	// to. On the other hand, anyone with the attention span to actually
	// read all the credits probably already knows. :-)

	// Start the music and roll the credits

	// The credits music (which can also be heard briefly in the "carib"
	// cutscene) is played once.

	_vm->_sound->streamCompMusic(309, false);

	clearScene();
	fadeUp(0);

	spriteInfo.scale = 0;
	spriteInfo.scaledWidth = 0;
	spriteInfo.scaledHeight = 0;
	spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
	spriteInfo.blend = 0;

	int startLine = 0;
	int scrollPos = 0;

	bool abortCredits = false;

	int scrollSteps = lineTop + CREDITS_FONT_HEIGHT;
	uint32 musicStart = getTick();

	// Ideally the music should last just a tiny bit longer than the
	// credits. Note that musicTimeRemaining() will return 0 if the music
	// is muted, so we need a sensible fallback for that case.

	uint32 musicLength = MAX((int32)(1000 * (_vm->_sound->musicTimeRemaining() - 3)), 25 * (int32)scrollSteps);

	while (scrollPos < scrollSteps && !_vm->shouldQuit()) {
		clearScene();

		for (i = startLine; i < lineCount; i++) {
			if (!creditsLines[i])
				continue;

			// Free any sprites that have scrolled off the screen

			if (creditsLines[i]->top + creditsLines[i]->height < scrollPos) {
				debug(2, "Freeing line %d: '%s'", i, creditsLines[i]->str);

				delete creditsLines[i];
				creditsLines[i] = NULL;

				startLine = i + 1;
			} else if (creditsLines[i]->top < scrollPos + 400) {
				if (!creditsLines[i]->sprite) {
					debug(2, "Creating line %d: '%s'", i, creditsLines[i]->str);
					creditsLines[i]->sprite = _vm->_fontRenderer->makeTextSprite((byte *)creditsLines[i]->str, 600, 14, _vm->_speechFontId, 0);
				}

				FrameHeader frame;

				frame.read(creditsLines[i]->sprite);

				spriteInfo.y = creditsLines[i]->top - scrollPos;
				spriteInfo.w = frame.width;
				spriteInfo.h = frame.height;
				spriteInfo.data = creditsLines[i]->sprite + FrameHeader::size();
				spriteInfo.isText = true;

				switch (creditsLines[i]->type) {
				case LINE_LEFT:
					spriteInfo.x = RENDERWIDE / 2 - 5 - frame.width;
					break;
				case LINE_RIGHT:
					spriteInfo.x = RENDERWIDE / 2 + 5;
					break;
				case LINE_CENTER:
					if (strcmp(creditsLines[i]->str, "@") == 0) {
						spriteInfo.data = logoData;
						spriteInfo.x = (RENDERWIDE - logoWidth) / 2;
						spriteInfo.w = logoWidth;
						spriteInfo.h = logoHeight;
					} else
						spriteInfo.x = (RENDERWIDE - frame.width) / 2;
					break;
				}

				if (spriteInfo.data)
					drawSprite(&spriteInfo);
			} else
				break;
		}

		updateDisplay();

		KeyboardEvent *ke = _vm->keyboardEvent();

		if (ke && ke->kbd.keycode == Common::KEYCODE_ESCAPE) {
			if (!abortCredits) {
				abortCredits = true;
				fadeDown();
			}
		}

		if (abortCredits && getFadeStatus() == RDFADE_BLACK)
			break;

		_vm->sleepUntil(musicStart + (musicLength * scrollPos) / scrollSteps + _pauseTicks);
		scrollPos++;
	}

	// We're done. Clean up and try to put everything back where it was
	// before the credits.

	for (i = 0; i < lineCount; i++) {
		delete creditsLines[i];
	}

	free(logoData);

	if (!abortCredits) {
		fadeDown();

		// The music should either have stopped or be about to stop, so
		// wait for it to really happen.

		while (_vm->_sound->musicTimeRemaining() && !_vm->shouldQuit()) {
			updateDisplay(false);
			_vm->_system->delayMillis(100);
		}
	}

	if (_vm->shouldQuit())
		return;

	waitForFade();

	_vm->_sound->muteFx(false);
	_vm->_sound->muteSpeech(false);

	if (loopingMusicId)
		_vm->_sound->streamCompMusic(loopingMusicId, true);
	else
		_vm->_sound->stopMusic(false);

	if (!_vm->_mouse->getMouseStatus() || _vm->_mouse->isChoosing())
		_vm->_mouse->setMouse(NORMAL_MOUSE_ID);

	if (_vm->_logic->readVar(DEAD))
		_vm->_mouse->buildSystemMenu();
}
Пример #10
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;
}
Пример #11
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;
}
Пример #12
0
Common::Error GroovieEngine::run() {
	_script = new Script(this, _gameDescription->version);

	// Initialize the graphics
	switch (_gameDescription->version) {
	case kGroovieV2:
		// Request the mode with the highest precision available
		initGraphics(640, 480, true, NULL);

		// Save the enabled mode as it can be both an RGB mode or CLUT8
		_pixelFormat = _system->getScreenFormat();
		_mode8bit = (_pixelFormat == Graphics::PixelFormat::createFormatCLUT8());
		break;
	case kGroovieT7G:
		initGraphics(640, 480, true);
		_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
		break;
	}

	// Create debugger. It requires GFX to be initialized
	_debugger = new Debugger(this);
	_script->setDebugger(_debugger);

	// Create the graphics manager
	_graphicsMan = new GraphicsMan(this);

	// Create the resource and cursor managers and the video player
	// Prepare the font too
	switch (_gameDescription->version) {
	case kGroovieT7G:
		if (getPlatform() == Common::kPlatformMacintosh) {
			_macResFork = new Common::MacResManager();
			if (!_macResFork->open(_gameDescription->desc.filesDescriptions[0].fileName))
				error("Could not open %s as a resource fork", _gameDescription->desc.filesDescriptions[0].fileName);
			// The Macintosh release used system fonts. We use GUI fonts.
			_font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
		} else {
			Common::File fontfile;
			if (!fontfile.open("sphinx.fnt")) {
				error("Couldn't open sphinx.fnt");
				return Common::kNoGameDataFoundError;
			} else if (!_sphinxFont.load(fontfile)) {
				error("Error loading sphinx.fnt");
				return Common::kUnknownError;
			}
			fontfile.close();
			_font = &_sphinxFont;
		}

		_resMan = new ResMan_t7g(_macResFork);
		_grvCursorMan = new GrvCursorMan_t7g(_system, _macResFork);
		_videoPlayer = new VDXPlayer(this);
		break;
	case kGroovieV2:
		_resMan = new ResMan_v2();
		_grvCursorMan = new GrvCursorMan_v2(_system);
		_videoPlayer = new ROQPlayer(this);
		break;
	}

	// Create the music player
	if (getPlatform() == Common::kPlatformMacintosh)
		_musicPlayer = new MusicPlayerMac(this);
	else
		_musicPlayer = new MusicPlayerXMI(this, _gameDescription->version == kGroovieT7G ? "fat" : "sample");

	// Load volume levels
	syncSoundSettings();

	// Get the name of the main script
	Common::String filename = _gameDescription->desc.filesDescriptions[0].fileName;
	if (_gameDescription->version == kGroovieT7G) {
		// Run The 7th Guest's demo if requested
		if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode"))
			filename = "demo.grv";
		else if (getPlatform() == Common::kPlatformMacintosh)
			filename = "script.grv"; // Stored inside the executable's resource fork
	} else if (_gameDescription->version == kGroovieV2) {
		// Open the disk index
		Common::File disk;
		if (!disk.open(filename)) {
			error("Couldn't open %s", filename.c_str());
			return Common::kNoGameDataFoundError;
		}

		// Search the entry
		bool found = false;
		int index = 0;
		while (!found && !disk.eos()) {
			Common::String line = disk.readLine();
			if (line.hasPrefix("title: ")) {
				// A new entry
				index++;
			} else if (line.hasPrefix("boot: ") && index == _gameDescription->indexEntry) {
				// It's the boot of the entry we're looking for,
				// get the script filename
				filename = line.c_str() + 6;
				found = true;
			}
		}

		// Couldn't find the entry
		if (!found) {
			error("Couldn't find entry %d in %s", _gameDescription->indexEntry, filename.c_str());
			return Common::kUnknownError;
		}
	}

	// Check the script file extension
	if (!filename.hasSuffix(".grv")) {
		error("%s isn't a valid script filename", filename.c_str());
		return Common::kUnknownError;
	}

	// Load the script
	if (!_script->loadScript(filename)) {
		error("Couldn't load the script file %s", filename.c_str());
		return Common::kUnknownError;
	}

	// Should I load a saved game?
	if (ConfMan.hasKey("save_slot")) {
		// Get the requested slot
		int slot = ConfMan.getInt("save_slot");
		_script->directGameLoad(slot);
	}

	// Check that the game files and the audio tracks aren't together run from
	// the same cd
	checkCD();

	// Game timer counter
	uint16 tmr = 0;

	// Initialize the CD
	int cd_num = ConfMan.getInt("cdrom");
	if (cd_num >= 0)
		_system->getAudioCDManager()->openCD(cd_num);

	while (!shouldQuit()) {
		// Give the debugger a chance to act
		_debugger->onFrame();

		// Handle input
		Common::Event ev;
		while (_eventMan->pollEvent(ev)) {
			switch (ev.type) {
			case Common::EVENT_KEYDOWN:
				// CTRL-D: Attach the debugger
				if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
					_debugger->attach();

				// Send the event to the scripts
				_script->setKbdChar(ev.kbd.ascii);

				// Continue the script execution to handle the key
				_waitingForInput = false;
				break;

			case Common::EVENT_MAINMENU:
				// Closing the GMM
			case Common::EVENT_MOUSEMOVE:
				// Continue the script execution, the mouse pointer
				// may fall inside a different hotspot now
				_waitingForInput = false;
				break;

			case Common::EVENT_LBUTTONDOWN:
				// Send the event to the scripts
				_script->setMouseClick(1);

				// Continue the script execution to handle
				// the click
				_waitingForInput = false;
				break;

			case Common::EVENT_RBUTTONDOWN:
				// Send the event to the scripts (to skip the video)
				_script->setMouseClick(2);
				break;

			case Common::EVENT_QUIT:
				quitGame();
				break;

			default:
				break;
			}
		}

		// The event loop may have triggered the quit status. In this case,
		// stop the execution.
		if (shouldQuit()) {
			continue;
		}

		if (_waitingForInput) {
			// Still waiting for input, just update the mouse, game timer and then wait a bit more
			_grvCursorMan->animate();
			_system->updateScreen();
			tmr++;
			// Wait a little bit between increments.  While mouse is moving, this triggers
			// only negligably slower.
			if (tmr > 4) {
				_script->timerTick();
				tmr = 0;
			}

			_system->delayMillis(50);
		} else if (_graphicsMan->isFading()) {
			// We're waiting for a fading to end, let the CPU rest
			// for a while and continue
			_system->delayMillis(30);
		} else {
			// Everything's fine, execute another script step
			_script->step();
		}

		// Update the screen if required
		_graphicsMan->update();
	}

	return Common::kNoError;
}
Пример #13
0
void LeverControl::parseLevFile(const Common::String &fileName) {
	Common::File file;
	if (!file.open(fileName)) {
		warning("LEV file %s could could be opened", fileName.c_str());
		return;
	}

	Common::String line = file.readLine();

	while (!file.eos()) {
		if (line.matchString("*animation_id*", true)) {
			// Not used
		} else if (line.matchString("*filename*", true)) {
			char fileNameBuffer[25];
			sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer);

			Common::String animationFileName(fileNameBuffer);

			if (animationFileName.hasSuffix(".avi")) {
				_animation.avi = new ZorkAVIDecoder();
				_animation.avi->loadFile(animationFileName);
				_fileType = AVI;
			} else if (animationFileName.hasSuffix(".rlf")) {
				_animation.rlf = new RlfAnimation(animationFileName, false);
				_fileType = RLF;
			}
		} else if (line.matchString("*skipcolor*", true)) {
			// Not used
		} else if (line.matchString("*anim_coords*", true)) {
			int left, top, right, bottom;
			sscanf(line.c_str(), "%*[^:]:%d %d %d %d~", &left, &top, &right, &bottom);

			_animationCoords.left = left;
			_animationCoords.top = top;
			_animationCoords.right = right;
			_animationCoords.bottom = bottom;
		} else if (line.matchString("*mirrored*", true)) {
			uint mirrored;
			sscanf(line.c_str(), "%*[^:]:%u~", &mirrored);

			_mirrored = mirrored == 0 ? false : true;
		} else if (line.matchString("*frames*", true)) {
			sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount);

			_frameInfo = new FrameInfo[_frameCount];
		} else if (line.matchString("*elsewhere*", true)) {
			// Not used
		} else if (line.matchString("*out_of_control*", true)) {
			// Not used
		} else if (line.matchString("*start_pos*", true)) {
			sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame);
			_currentFrame = _startFrame;
		} else if (line.matchString("*hotspot_deltas*", true)) {
			uint x;
			uint y;
			sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y);

			_hotspotDelta.x = x;
			_hotspotDelta.y = y;
		} else {
			uint frameNumber;
			uint x, y;

			if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) {
				_frameInfo[frameNumber].hotspot.left = x;
				_frameInfo[frameNumber].hotspot.top = y;
				_frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x;
				_frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y;
			}

			Common::StringTokenizer tokenizer(line, " ^=()");
			tokenizer.nextToken();
			tokenizer.nextToken();

			Common::String token = tokenizer.nextToken();
			while (!tokenizer.empty()) {
				if (token == "D") {
					token = tokenizer.nextToken();

					uint angle;
					uint toFrame;
					sscanf(token.c_str(), "%u,%u", &toFrame, &angle);

					_frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame));
				} else if (token.hasPrefix("P")) {
					// Format: P(<from> to <to>)
					tokenizer.nextToken();
					tokenizer.nextToken();
					token = tokenizer.nextToken();
					uint to = atoi(token.c_str());

					_frameInfo[frameNumber].returnRoute.push_back(to);
				}

				token = tokenizer.nextToken();
			}
		}

		line = file.readLine();
	}
}
Пример #14
0
void LeverControl::parseLevFile(const Common::String &fileName) {
	Common::File file;
	if (!_engine->getSearchManager()->openFile(file, fileName)) {
		warning("LEV file %s could could be opened", fileName.c_str());
		return;
	}

	Common::String line;
	Common::String param;
	Common::String values;

	while (!file.eos()) {
		line = file.readLine();
		getLevParams(line, param, values);

		if (param.matchString("animation_id", true)) {
			// Not used
		} else if (param.matchString("filename", true)) {
			_animation = _engine->loadAnimation(values);
		} else if (param.matchString("skipcolor", true)) {
			// Not used
		} else if (param.matchString("anim_coords", true)) {
			int left, top, right, bottom;
			sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom);

			_animationCoords.left = left;
			_animationCoords.top = top;
			_animationCoords.right = right;
			_animationCoords.bottom = bottom;
		} else if (param.matchString("mirrored", true)) {
			uint mirrored;
			sscanf(values.c_str(), "%u", &mirrored);

			_mirrored = mirrored == 0 ? false : true;
		} else if (param.matchString("frames", true)) {
			sscanf(values.c_str(), "%u", &_frameCount);

			_frameInfo = new FrameInfo[_frameCount];
		} else if (param.matchString("elsewhere", true)) {
			// Not used
		} else if (param.matchString("out_of_control", true)) {
			// Not used
		} else if (param.matchString("start_pos", true)) {
			sscanf(values.c_str(), "%u", &_startFrame);
			_currentFrame = _startFrame;
		} else if (param.matchString("hotspot_deltas", true)) {
			uint x;
			uint y;
			sscanf(values.c_str(), "%u %u", &x, &y);

			_hotspotDelta.x = x;
			_hotspotDelta.y = y;
		} else if (param.matchString("venus_id", true)) {
			_venusId = atoi(values.c_str());
		} else {
			uint frameNumber;
			uint x, y;

			line.toLowercase();

			if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) {
				_frameInfo[frameNumber].hotspot.left = x;
				_frameInfo[frameNumber].hotspot.top = y;
				_frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x;
				_frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y;
			}

			Common::StringTokenizer tokenizer(line, " ^=()~");
			tokenizer.nextToken();
			tokenizer.nextToken();

			Common::String token = tokenizer.nextToken();
			while (!tokenizer.empty()) {
				if (token == "d") {
					token = tokenizer.nextToken();

					uint angle;
					uint toFrame;
					sscanf(token.c_str(), "%u,%u", &toFrame, &angle);

					_frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame));
				} else if (token.hasPrefix("p")) {
					// Format: P(<from> to <to>)
					tokenizer.nextToken();
					tokenizer.nextToken();
					token = tokenizer.nextToken();
					uint to = atoi(token.c_str());

					_frameInfo[frameNumber].returnRoute.push_back(to);
				}

				token = tokenizer.nextToken();
			}
		}

		// Don't read lines in this place because last will not be parsed.
	}
}
Пример #15
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;
}