Пример #1
0
bool SavePartInfo::write(Common::WriteStream &stream) const {
	if (!_header.write(stream))
		return false;

	stream.writeUint32LE(_gameID);
	stream.writeUint32LE(_gameVersion);
	stream.writeByte(_endian);
	stream.writeUint32LE(_varCount);
	stream.writeUint32LE(_descMaxLength);

	if (stream.write(_desc, _descMaxLength) != _descMaxLength)
		return false;

	return flushStream(stream);
}
Пример #2
0
bool LureEngine::saveGame(uint8 slotNumber, Common::String &caption) {
	Common::WriteStream *f = this->_saveFileMan->openForSaving(
		generateSaveName(slotNumber));
	if (f == NULL)
		return false;

	f->write("lure", 5);
	f->writeByte(getLanguage());
	f->writeByte(LURE_SAVEGAME_MINOR);
	f->writeString(caption);
	f->writeByte(0); // End of string terminator

	Resources::getReference().saveToStream(f);
	Game::getReference().saveToStream(f);
	Sound.saveToStream(f);
	Fights.saveToStream(f);
	Room::getReference().saveToStream(f);

	delete f;
	return true;
}
Пример #3
0
void Decompiler::writeBlock(Common::WriteStream &out, const Block *block, size_t indent) {
	for (const auto instruction : block->instructions) {
		writeInstruction(out, instruction, indent);
	}

	for (const auto &childType : block->childrenTypes) {
		if (isSubRoutineCall(childType)) {
			writeIndent(out, indent);
			const Instruction *instruction = block->instructions.back();


			out.writeString(formatJumpLabelName(*instruction->branches[0]));
			out.writeString("(");

			for (size_t i = 0; i < instruction->variables.size(); ++i) {
				out.writeString(formatVariableName(instruction->variables[i]));
				if (i < instruction->variables.size() - 1)
					out.writeString(", ");
			}

			out.writeString(");\n");

			writeBlock(out, block->children[1], indent);
		}
	}

	for (const auto &control : block->controls) {
		if (control.type == kControlTypeReturn) {
			writeIndent(out, indent);
			out.writeString("return;\n");
		} else if (control.type == kControlTypeIfCond) {
			writeIfControl(out, control, indent);
		}

		// TODO: while
		// TODO: break
		// TODO: continue
	}
}
Пример #4
0
reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
	Common::String name = s->_segMan->getString(argv[0]);

#ifdef ENABLE_SCI32
	// Cache the file existence result for the Phantasmagoria
	// save index file, as the game scripts keep checking for
	// its existence.
	if (name == PHANTASMAGORIA_SAVEGAME_INDEX && s->_virtualIndexFile)
		return TRUE_REG;
#endif

	bool exists = false;

	// Check for regular file
	exists = Common::File::exists(name);

	// Check for a savegame with the name
	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
	if (!exists)
		exists = !saveFileMan->listSavefiles(name).empty();

	// Try searching for the file prepending "target-"
	const Common::String wrappedName = g_sci->wrapFilename(name);
	if (!exists) {
		exists = !saveFileMan->listSavefiles(wrappedName).empty();
	}

	// SCI2+ debug mode
	if (DebugMan.isDebugChannelEnabled(kDebugLevelDebugMode)) {
		if (!exists && name == "1.scr")		// PQ4
			exists = true;
		if (!exists && name == "18.scr")	// QFG4
			exists = true;
		if (!exists && name == "99.scr")	// GK1, KQ7
			exists = true;
		if (!exists && name == "classes")	// GK2, SQ6, LSL7
			exists = true;
	}

	// Special case for non-English versions of LSL5: The English version of
	// LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
	// memory.drv exists (which is where the game's password is stored). If
	// it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
	// writing and creates a new file. Non-English versions call kFileIO(),
	// case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
	// found. We create a default memory.drv file with no password, so that
	// the game can continue.
	if (!exists && name == "memory.drv") {
		// Create a new file, and write the bytes for the empty password
		// string inside
		byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
		Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
		for (int i = 0; i < 10; i++)
			outFile->writeByte(defaultContent[i]);
		outFile->finalize();
		exists = !outFile->err();	// check whether we managed to create the file.
		delete outFile;
	}

	// Special case for KQ6 Mac: The game checks for two video files to see
	// if they exist before it plays them. Since we support multiple naming
	// schemes for resource fork files, we also need to support that here in
	// case someone has a "HalfDome.bin" file, etc. 
	if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
			(name == "HalfDome" || name == "Kq6Movie"))
		exists = Common::MacResManager::exists(name);

	debugC(kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
	return make_reg(0, exists);
}
Пример #5
0
static void saveOrLoadPtr(Common::WriteStream &stream, int16 *&p, int16 *base) {
	int32 offset = (int32)(p - base);
	stream.writeSint32LE(offset);
}
Пример #6
0
static void saveOrLoad(Common::WriteStream &stream, int16 &i) {
	stream.writeSint16LE(i);
}
Пример #7
0
void TwoDAFile::writeCSV(Common::WriteStream &out) const {
	// Write column headers

	for (size_t i = 0; i < _headers.size(); i++) {
		const bool needQuote = _headers[i].contains(',');
		if (needQuote)
			out.writeByte('"');

		out.writeString(_headers[i]);

		if (needQuote)
			out.writeByte('"');

		if (i < (_headers.size() - 1))
			out.writeByte(',');
	}

	out.writeByte('\n');

	// Write array

	for (size_t i = 0; i < _rows.size(); i++) {
		for (size_t j = 0; j < _rows[i]->_data.size(); j++) {
			const bool needQuote = _rows[i]->_data[j].contains(',');

			if (needQuote)
				out.writeByte('"');

			if (_rows[i]->_data[j] != "****")
				out.writeString(_rows[i]->_data[j]);

			if (needQuote)
				out.writeByte('"');

			if (j < (_rows[i]->_data.size() - 1))
				out.writeByte(',');
		}

		out.writeByte('\n');
	}

	out.flush();
}
Пример #8
0
void pngWriteToStream(png_structp pngPtr, png_bytep data, png_size_t length) {
	void *writeIOptr = png_get_io_ptr(pngPtr);
	Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
	stream->write(data, length);
}
Пример #9
0
	bool err() const {
		// CHECKME: does Z_STREAM_END make sense here?
		return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
	}
Пример #10
0
void MainWindow::exportWAVImpl(Sound::AudioStream *sound, Common::WriteStream &wav) {
	assert(sound);

	const uint16 channels = sound->getChannels();
	const uint32 rate     = sound->getRate();

	std::deque<SoundBuffer> buffers;

	uint64 length = getSoundLength(sound);
	if (length != Sound::RewindableAudioStream::kInvalidLength)
		buffers.resize((length / (SoundBuffer::kBufferSize / channels)) + 1);

	uint32 samples = 0;
	std::deque<SoundBuffer>::iterator buffer = buffers.begin();
	while (!sound->endOfStream()) {
		if (buffer == buffers.end()) {
			buffers.push_back(SoundBuffer());
			buffer = --buffers.end();
		}

		buffer->samples = sound->readBuffer(buffer->buffer, SoundBuffer::kBufferSize);

		if (buffer->samples > 0)
			samples += buffer->samples;

		++buffer;
	}

	samples /= channels;

	const uint32 dataSize   = samples * channels * 2;
	const uint32 byteRate   = rate * channels * 2;
	const uint16 blockAlign = channels * 2;

	wav.writeUint32BE(MKTAG('R', 'I', 'F', 'F'));
	wav.writeUint32LE(36 + dataSize);
	wav.writeUint32BE(MKTAG('W', 'A', 'V', 'E'));

	wav.writeUint32BE(MKTAG('f', 'm', 't', ' '));
	wav.writeUint32LE(16);
	wav.writeUint16LE(1);
	wav.writeUint16LE(channels);
	wav.writeUint32LE(rate);
	wav.writeUint32LE(byteRate);
	wav.writeUint16LE(blockAlign);
	wav.writeUint16LE(16);

	wav.writeUint32BE(MKTAG('d', 'a', 't', 'a'));
	wav.writeUint32LE(dataSize);

	for (std::deque<SoundBuffer>::const_iterator b = buffers.begin(); b != buffers.end(); ++b)
		for (int i = 0; i < b->samples; i++)
			wav.writeUint16LE(b->buffer[i]);
}
Пример #11
0
static void decompress00(Common::SeekableReadStream &small, Common::WriteStream &out, uint32 size) {
	out.writeStream(small, size);
}
Пример #12
0
void Decompiler::writeIndent(Common::WriteStream &out, size_t indent) {
	for (size_t i = 0; i < indent; ++i)
		out.writeString("\t");
}
Пример #13
0
void Decompiler::writeInstruction(Common::WriteStream &out, const Instruction* instruction, size_t indent) {
	switch (instruction->opcode) {
		case kOpcodeCONST: {
			const Variable *v = instruction->variables[0];
			writeIndent(out, indent);
			out.writeString(getVariableTypeName(v->type) + " " + formatVariableName(v) + " = " + formatInstructionData(*instruction) + ";\n");

			break;
		}

		case kOpcodeACTION: {
			unsigned int paramCount = instruction->args[1];

			writeIndent(out, indent);

			if (instruction->variables.size() > paramCount) {
				const Variable *ret = instruction->variables.back();
				out.writeString(getVariableTypeName(ret->type, _ncs->getGame()) + " " + formatVariableName(ret) + " = ");
			}

			out.writeString(getFunctionName(_ncs->getGame(), instruction->args[0]));
			out.writeString("(");
			for (unsigned int i = 0; i < paramCount; ++i) {
				out.writeString(formatVariableName(instruction->variables[i]));
				if (i < paramCount - 1)
					out.writeString(", ");
			}
			out.writeString(");\n");

			break;
		}

		case kOpcodeCPDOWNBP:
		case kOpcodeCPDOWNSP:
		case kOpcodeCPTOPBP:
		case kOpcodeCPTOPSP: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];

			writeIndent(out, indent);
			out.writeString(getVariableTypeName(v2->type, _ncs->getGame()) + " " + formatVariableName(v2) + " = " + formatVariableName(v1) + ";\n");

			break;
		}

		case kOpcodeLOGAND: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " && " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeLOGOR: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " || " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeEQ: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " == " + formatVariableName(v2) + ";\n"
			);


			break;
		}

		case kOpcodeLEQ: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " <= " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeLT: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " < " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeGEQ: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " >= " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeGT: {
			const Variable *v1 = instruction->variables[0];
			const Variable *v2 = instruction->variables[1];
			const Variable *result = instruction->variables[2];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					formatVariableName(v1) + " > " + formatVariableName(v2) + ";\n"
			);

			break;
		}

		case kOpcodeNOT: {
			const Variable *v = instruction->variables[0];
			const Variable *result = instruction->variables[1];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(result->type, _ncs->getGame()) + " " +
					formatVariableName(result) + " = " +
					"!" + formatVariableName(v) + ";\n"
			);

			break;
		}

		case kOpcodeRSADD: {
			const Variable *v = instruction->variables[0];

			writeIndent(out, indent);
			out.writeString(
					getVariableTypeName(v->type, _ncs->getGame()) + " " +
					formatVariableName(v) + " = "
			);

			switch (v->type) {
				case kTypeString:
					out.writeString("\"\"");
					break;
				case kTypeInt:
					out.writeString("0");
					break;
				case kTypeFloat:
					out.writeString("0.0");
					break;

				default:
					// TODO: No idea how empty objects or engine types are intialized.
					out.writeString("0");
					break;
			}

			out.writeString(";\n");

			break;
		}

		// TODO: Not all necessary instruction are implemented here

		default:
			break;
	}
}
Пример #14
0
void disNCS(const Common::UString &inFile, const Common::UString &outFile,
            Aurora::GameID &game, Command &command, bool printStack, bool printControlTypes) {

	Common::SeekableReadStream *ncs = new Common::ReadFile(inFile);

	Common::WriteStream *out = 0;
	try {
		if (!outFile.empty())
			out = new Common::WriteFile(outFile);
		else
			out = new Common::StdOutStream;

		status("Disassembling script...");
		NWScript::Disassembler disassembler(*ncs, game);

		if (game != Aurora::kGameIDUnknown) {
			try {
				status("Analyzing script stack...");
				disassembler.analyzeStack();
			} catch (...) {
				Common::exceptionDispatcherWarnAndIgnore("Script analysis failed");
			}

			try {
				status("Analyzing control flow...");
				disassembler.analyzeControlFlow();
			} catch (...) {
				Common::exceptionDispatcherWarnAndIgnore("Control flow analysis failed");
			}
		}

		switch (command) {
			case kCommandListing:
				disassembler.createListing(*out, printStack);
				break;

			case kCommandAssembly:
				disassembler.createAssembly(*out, printStack);
				break;

			case kCommandDot:
				disassembler.createDot(*out, printControlTypes);
				break;

			default:
				throw Common::Exception("Invalid command %u", (uint)command);
		}

	} catch (...) {
		delete ncs;
		delete out;
		throw;
	}

	out->flush();

	if (!outFile.empty())
		status("Disassembled \"%s\" into \"%s\"", inFile.c_str(), outFile.c_str());

	delete ncs;
	delete out;
}
Пример #15
0
void GameState::writeState(uint32 sceneId, uint32 threadId) {
	Common::WriteStream *writeStream = newWriteStream();
	writeStream->writeUint32LE(sceneId);
	writeStream->writeUint32LE(threadId);
	writeStateInternal(writeStream);
}
Пример #16
0
	void clearErr() {
		// Note: we don't reset the _zlibErr here, as it is not
		// clear in general how
		_wrapped->clearErr();
	}
Пример #17
0
int ObjectV2::save(Common::WriteStream &dest) {
	dest.write(_objData, _objSize);
	return 0;
}
Пример #18
0
void TwoDAFile::writeBinary(Common::WriteStream &out) const {
	const size_t columnCount = _headers.size();
	const size_t rowCount    = _rows.size();
	const size_t cellCount   = columnCount * rowCount;

	out.writeString("2DA V2.b\n");

	// Write the column headers

	for (std::vector<Common::UString>::const_iterator h = _headers.begin(); h != _headers.end(); ++h) {
		out.writeString(*h);
		out.writeByte('\t');
	}
	out.writeByte('\0');

	// Write the row indices

	out.writeUint32LE((uint32) rowCount);
	for (size_t i = 0; i < rowCount; i++) {
		out.writeString(Common::composeString(i));
		out.writeByte('\t');
	}

	/* Deduplicate cell data strings. Binary 2DA files don't store the
	 * data for each cell directly: instead, each cell contains an offset
	 * into a data array. This way, cells with the same data only need to
	 * to store this data once.
	 *
	 * The original binary 2DA files in KotOR/KotOR2 make extensive use
	 * of that, and we should do this as well.
	 *
	 * Basically, this involves going through each cell, and looking up
	 * if we already saved this particular piece of data. If not, save
	 * it, otherwise only remember the offset. There's no need to be
	 * particularly smart about it, so we're just doing it the naive
	 * O(n^2) way.
	 */

	std::vector<Common::UString> data;
	std::vector<size_t> offsets;

	data.reserve(cellCount);
	offsets.reserve(cellCount);

	size_t dataSize = 0;

	std::vector<size_t> cells;
	cells.reserve(cellCount);

	for (size_t i = 0; i < rowCount; i++) {
		assert(_rows[i]);

		for (size_t j = 0; j < columnCount; j++) {
			const Common::UString cell = _rows[i]->getString(j);

			// Do we already know about this cell data string?
			size_t foundCell = SIZE_MAX;
			for (size_t k = 0; k < data.size(); k++) {
				if (data[k] == cell) {
					foundCell = k;
					break;
				}
			}

			// If not, add it to the cell data array
			if (foundCell == SIZE_MAX) {
				foundCell = data.size();

				data.push_back(cell);
				offsets.push_back(dataSize);

				dataSize += data.back().size() + 1;

				if (dataSize > 65535)
					throw Common::Exception("TwoDAFile::writeBinary(): Cell data size overflow");
			}

			// Remember the offset to the cell data array
			cells.push_back(offsets[foundCell]);
		}
	}

	// Write cell data offsets
	for (std::vector<size_t>::const_iterator c = cells.begin(); c != cells.end(); ++c)
		out.writeUint16LE((uint16) *c);

	// Size of the all cell data strings
	out.writeUint16LE((uint16) dataSize);

	// Write cell data strings
	for (std::vector<Common::UString>::const_iterator d = data.begin(); d != data.end(); ++d) {
		out.writeString(*d);
		out.writeByte('\0');
	}
}
Пример #19
0
void pngFlushStream(png_structp pngPtr) {
	void *writeIOptr = png_get_io_ptr(pngPtr);
	Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
	stream->flush();
}
Пример #20
0
// This function leaks. For now I don't care
bool PspUnitTests::testFileSystem() {
	// create memory
	const uint32 BufSize = 32 * 1024;
	char* buffer = new char[BufSize];
	int i;
	Common::WriteStream *wrStream;
	Common::SeekableReadStream *rdStream;

	PSP_INFO_PRINT("testing fileSystem...\n");

	// fill buffer
	for (i=0; i<(int)BufSize; i += 4) {
		buffer[i] = 'A';
		buffer[i + 1] = 'B';
		buffer[i + 2] = 'C';
		buffer[i + 3] = 'D';
	}

	// create a file
	const char *path = "./file.test";
	Common::FSNode file(path);

	PSP_INFO_PRINT("creating write stream...\n");

	wrStream = file.createWriteStream();
	if (!wrStream) {
		PSP_ERROR("%s couldn't be created.\n", path);
		return false;
	}

	// write contents
	char* index = buffer;
	int32 totalLength = BufSize;
	int32 curLength = 50;

	PSP_INFO_PRINT("writing...\n");

	while(totalLength - curLength > 0) {
		if ((int)wrStream->write(index, curLength) != curLength) {
			PSP_ERROR("couldn't write %d bytes\n", curLength);
			return false;
		}
		totalLength -= curLength;
		index += curLength;
		//curLength *= 2;
		//PSP_INFO_PRINT("write\n");
	}

	// write the rest
	if ((int)wrStream->write(index, totalLength) != totalLength) {
		PSP_ERROR("couldn't write %d bytes\n", curLength);
		return false;
	}

	delete wrStream;

	PSP_INFO_PRINT("reading...\n");

	rdStream = file.createReadStream();
	if (!rdStream) {
		PSP_ERROR("%s couldn't be created.\n", path);
		return false;
	}

	// seek to beginning
	if (!rdStream->seek(0, SEEK_SET)) {
		PSP_ERROR("couldn't seek to the beginning after writing the file\n");
		return false;
	}

	// read the contents
	char *readBuffer = new char[BufSize + 4];
	memset(readBuffer, 0, (BufSize + 4));
	index = readBuffer;
	while (rdStream->read(index, 100) == 100) {
		index += 100;
	}

	if (!rdStream->eos()) {
		PSP_ERROR("didn't find EOS at end of stream\n");
		return false;
	}

	// compare
	for (i=0; i<(int)BufSize; i++)
		if (buffer[i] != readBuffer[i]) {
			PSP_ERROR("reading/writing mistake at %x. Got %x instead of %x\n", i, readBuffer[i], buffer[i]);
			return false;
		}

	// Check for exceeding limit
	for (i=0; i<4; i++) {
		if (readBuffer[BufSize + i]) {
			PSP_ERROR("read exceeded limits. %d = %x\n", BufSize + i, readBuffer[BufSize + i]);
		}
	}

	delete rdStream;

	PSP_INFO_PRINT("writing...\n");

	wrStream = file.createWriteStream();
	if (!wrStream) {
		PSP_ERROR("%s couldn't be created.\n", path);
		return false;
	}

	const char *phrase = "Jello is really fabulous";
	uint32 phraseLen = strlen(phrase);

	int ret;
	if ((ret = wrStream->write(phrase, phraseLen)) != (int)phraseLen) {
		PSP_ERROR("couldn't write phrase. Got %d instead of %d\n", ret, phraseLen);
		return false;
	}

	PSP_INFO_PRINT("reading...\n");

	delete wrStream;
	rdStream = file.createReadStream();
	if (!rdStream) {
		PSP_ERROR("%s couldn't be created.\n", path);
		return false;
	}

	char *readPhrase = new char[phraseLen + 2];
	memset(readPhrase, 0, phraseLen + 2);

	if ((ret = rdStream->read(readPhrase, phraseLen) != phraseLen)) {
		PSP_ERROR("read error on phrase. Got %d instead of %d\n", ret, phraseLen);
		return false;
	}

	for (i=0; i<(int)phraseLen; i++) {
		if (readPhrase[i] != phrase[i]) {
			PSP_ERROR("bad read/write in phrase. At %d, %x != %x\n", i, readPhrase[i], phrase[i]);
			return false;
		}
	}

	// check for exceeding
	if (readPhrase[i] != 0) {
		PSP_ERROR("found excessive copy in phrase. %c at %d\n", readPhrase[i], i);
		return false;
	}

	PSP_INFO_PRINT("trying to read end...\n");

	// seek to end
	if (!rdStream->seek(0, SEEK_END)) {
		PSP_ERROR("couldn't seek to end for append\n");
		return false;
	};

	// try to read
	if (rdStream->read(readPhrase, 2) || !rdStream->eos()) {
		PSP_ERROR("was able to read at end of file\n");
		return false;
	}

	PSP_INFO_PRINT("ok\n");
	return true;
}