bool Resource::readTableFile(const GameVersion *gameVersion) { Common::File tableFile; tableFile.open(_tableFilename); if (tableFile.isOpen() && tableFile.readUint32BE() == 'QTBL') { if (tableFile.readUint32BE() != CURRENT_TBL_VERSION) warning("Incorrect version of queen.tbl, please update it"); tableFile.seek(gameVersion->tableOffset); readTableEntries(&tableFile); return true; } return false; }
void ExtractCine::unpackFile(Common::File &file) { char fileName[15]; unsigned int entryCount = file.readUint16BE(); // How many entries? unsigned int entrySize = file.readUint16BE(); // How many bytes per entry? assert(entrySize == 0x1e); while (entryCount--) { file.read_throwsOnError(fileName, 14); fileName[14] = '\0'; Common::Filename outPath(_outputPath); outPath.setFullName(fileName); uint32 offset = file.readUint32BE(); unsigned int packedSize = file.readUint32BE(); unsigned int unpackedSize = file.readUint32BE(); // Skip one file.readUint32BE(); unsigned int savedPos = file.pos(); print("unpacking '%s' ... ", outPath.getFullName().c_str()); Common::File fpOut(outPath, "wb"); file.seek(offset, SEEK_SET); assert(unpackedSize >= packedSize); uint8 *data = (uint8 *)calloc(unpackedSize, 1); uint8 *packedData = (uint8 *)calloc(packedSize, 1); assert(data); assert(packedData); file.read_throwsOnError(packedData, packedSize); bool status = true; if (packedSize != unpackedSize) { CineUnpacker cineUnpacker; status = cineUnpacker.unpack(packedData, packedSize, data, unpackedSize); } else { memcpy(data, packedData, packedSize); } free(packedData); fpOut.write(data, unpackedSize); free(data); if (!status) { print("CRC ERROR"); } else { print("ok"); } print(", packedSize %u unpackedSize %u", packedSize, unpackedSize); file.seek(savedPos, SEEK_SET); } }
void SoundHE::setupHEMusicFile() { int i, total_size; Common::File musicFile; Common::String buf(_vm->generateFilename(-4)); if (musicFile.open(buf) == true) { musicFile.seek(4, SEEK_SET); total_size = musicFile.readUint32BE(); musicFile.seek(16, SEEK_SET); _heMusicTracks = musicFile.readUint32LE(); debug(5, "Total music tracks %d", _heMusicTracks); int musicStart = (_vm->_game.heversion >= 80) ? 56 : 20; musicFile.seek(musicStart, SEEK_SET); _heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic)); for (i = 0; i < _heMusicTracks; i++) { _heMusic[i].id = musicFile.readUint32LE(); _heMusic[i].offset = musicFile.readUint32LE(); _heMusic[i].size = musicFile.readUint32LE(); if (_vm->_game.heversion >= 80) { musicFile.seek(+9, SEEK_CUR); } else { musicFile.seek(+13, SEEK_CUR); } } musicFile.close(); } }
/** * Opens a file and loads a sound effect. * * @param fileName Sfx filename * * @returns True is everything is OK, False otherwise */ bool FPSfx::loadFile(const char *fileName) { if (!_soundSupported) return true; SoundCodecs codec = FPCODEC_UNKNOWN; Common::File file; if (file.open(fileName)) codec = FPCODEC_ADPCM; else if (file.open(setExtension(fileName, ".MP3"))) codec = FPCODEC_MP3; else if (file.open(setExtension(fileName, ".OGG"))) codec = FPCODEC_OGG; else if (file.open(setExtension(fileName, ".FLA"))) codec = FPCODEC_FLAC; else { warning("FPSfx::LoadFile(): Cannot open sfx file!"); return false; } Common::SeekableReadStream *buffer; switch (codec) { case FPCODEC_ADPCM: { if (file.readUint32BE() != MKTAG('A', 'D', 'P', 0x10)) { warning("FPSfx::LoadFile(): Invalid ADP header!"); return false; } uint32 rate = file.readUint32LE(); uint32 channels = file.readUint32LE(); buffer = file.readStream(file.size() - file.pos()); _rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels); } break; case FPCODEC_MP3: #ifdef USE_MAD buffer = file.readStream(file.size()); _rewindableStream = Audio::makeMP3Stream(buffer, DisposeAfterUse::YES); #endif break; case FPCODEC_OGG: #ifdef USE_VORBIS buffer = file.readStream(file.size()); _rewindableStream = Audio::makeVorbisStream(buffer, DisposeAfterUse::YES); #endif break; case FPCODEC_FLAC: buffer = file.readStream(file.size()); #ifdef USE_FLAC _rewindableStream = Audio::makeFLACStream(buffer, DisposeAfterUse::YES); #endif break; default: return false; } _fileLoaded = true; return true; }
ZorkCursor::ZorkCursor(const Common::String &fileName) : _width(0), _height(0), _hotspotX(0), _hotspotY(0) { Common::File file; if (!file.open(fileName)) return; uint32 magic = file.readUint32BE(); if (magic != MKTAG('Z', 'C', 'R', '1')) { warning("%s is not a Zork Cursor file", fileName.c_str()); return; } _hotspotX = file.readUint16LE(); _hotspotY = file.readUint16LE(); _width = file.readUint16LE(); _height = file.readUint16LE(); uint dataSize = _width * _height * sizeof(uint16); _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); uint32 bytesRead = file.read(_surface.getPixels(), dataSize); assert(bytesRead == dataSize); // Convert to RGB 565 _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); }
void Sound::loadVoiceFile(const GameSpecificSettings *gss) { // Game versions which use separate voice files if (_vm->getGameType() == GType_FF || _vm->getGameId() == GID_SIMON1CD32) return; char filename[16]; Common::File *file = new Common::File(); if (!_hasVoiceFile) { _voice = makeCompressedSound(_mixer, file, gss->speech_filename); _hasVoiceFile = (_voice != 0); } if (!_hasVoiceFile && _vm->getGameType() == GType_SIMON2) { // for simon2 mac/amiga, only read index file file->open("voices.idx"); if (file->isOpen() == true) { int end = file->size(); _filenums = (uint16 *)malloc((end / 6 + 1) * 2); _offsets = (uint32 *)malloc((end / 6 + 1) * 4); for (int i = 1; i <= end / 6; i++) { _filenums[i] = file->readUint16BE(); _offsets[i] = file->readUint32BE(); } _hasVoiceFile = true; } } if (!_hasVoiceFile) { sprintf(filename, "%s.wav", gss->speech_filename); file->open(filename); if (file->isOpen()) { _hasVoiceFile = true; _voice = new WavSound(_mixer, file); } } const bool dataIsUnsigned = true; if (!_hasVoiceFile) { sprintf(filename, "%s.voc", gss->speech_filename); file->open(filename); if (file->isOpen()) { _hasVoiceFile = true; _voice = new VocSound(_mixer, file, dataIsUnsigned); } } if (!_hasVoiceFile) { sprintf(filename, "%s", gss->speech_filename); file->open(filename); if (file->isOpen()) { _hasVoiceFile = true; if (_vm->getGameType() == GType_PP) _voice = new WavSound(_mixer, file); else _voice = new VocSound(_mixer, file, dataIsUnsigned); } } }
bool ScummDebugger::Cmd_ImportRes(int argc, const char** argv) { Common::File file; uint32 size; int resnum; if (argc != 4) { DebugPrintf("Syntax: importres <restype> <filename> <resnum>\n"); return true; } resnum = atoi(argv[3]); // FIXME add bounds check if (!strncmp(argv[1], "scr", 3)) { file.open(argv[2], Common::File::kFileReadMode); if (file.isOpen() == false) { DebugPrintf("Could not open file %s\n", argv[2]); return true; } if (_vm->_game.features & GF_SMALL_HEADER) { size = file.readUint16LE(); file.seek(-2, SEEK_CUR); } else if (_vm->_game.features & GF_SMALL_HEADER) { if (_vm->_game.version == 4) file.seek(8, SEEK_CUR); size = file.readUint32LE(); file.readUint16LE(); file.seek(-6, SEEK_CUR); } else { file.readUint32BE(); size = file.readUint32BE(); file.seek(-8, SEEK_CUR); } file.read(_vm->res.createResource(rtScript, resnum, size), size); } else DebugPrintf("Unknown importres type '%s'\n", argv[1]); return true; }
Audio::AudioStream *RawSound::makeAudioStream(uint sound) { if (_offsets == NULL) return NULL; Common::File *file = new Common::File(); if (!file->open(_filename)) { warning("RawSound::makeAudioStream: Could not open file \"%s\"", _filename.c_str()); return NULL; } file->seek(_offsets[sound], SEEK_SET); uint size = file->readUint32BE(); return Audio::makeRawStream(new Common::SeekableSubReadStream(file, _offsets[sound] + 4, _offsets[sound] + 4 + size, DisposeAfterUse::YES), 22050, _flags, DisposeAfterUse::YES); }
BaseSound::BaseSound(Audio::Mixer *mixer, const Common::String &filename, uint32 base, bool bigEndian) : _mixer(mixer), _filename(filename), _offsets(NULL) { uint res = 0; uint32 size; Common::File file; if (!file.open(_filename)) error("BaseSound: Could not open file \"%s\"", filename.c_str()); file.seek(base + sizeof(uint32), SEEK_SET); if (bigEndian) size = file.readUint32BE(); else size = file.readUint32LE(); // The Feeble Files uses set amount of voice offsets if (size == 0) size = 40000; res = size / sizeof(uint32); _offsets = (uint32 *)malloc(size + sizeof(uint32)); _freeOffsets = true; file.seek(base, SEEK_SET); for (uint i = 0; i < res; i++) { if (bigEndian) _offsets[i] = base + file.readUint32BE(); else _offsets[i] = base + file.readUint32LE(); } _offsets[res] = file.size(); }
Common::File *Resource::openDataFile(const Common::String fileName, uint32 fileHeader) { Common::File *dataFile = new Common::File(); dataFile->open(translateFileName(fileName)); if (!dataFile->isOpen()) error("openDataFile: Couldn't open %s (%s)", translateFileName(fileName).c_str(), fileName.c_str()); if (fileHeader > 0) { uint32 headerTag = dataFile->readUint32BE(); if (headerTag != fileHeader) { dataFile->close(); error("openDataFile: Unexpected header in %s (%s) - expected: %d, got: %d", translateFileName(fileName).c_str(), fileName.c_str(), fileHeader, headerTag); } } return dataFile; }
void Sound::loadVoiceFile(const GameSpecificSettings *gss) { // Game versions which use separate voice files if (_hasVoiceFile || _vm->getGameType() == GType_FF || _vm->getGameId() == GID_SIMON1CD32) return; _voice = makeSound(_mixer, gss->speech_filename); _hasVoiceFile = (_voice != 0); if (_hasVoiceFile) return; if (_vm->getGameType() == GType_SIMON2) { // for simon2 mac/amiga, only read index file Common::File file; if (file.open("voices.idx")) { int end = file.size(); _filenums = (uint16 *)malloc((end / 6 + 1) * 2); _offsets = (uint32 *)malloc((end / 6 + 1 + 1) * 4); for (int i = 1; i <= end / 6; i++) { _filenums[i] = file.readUint16BE(); _offsets[i] = file.readUint32BE(); } // We need to add a terminator entry otherwise we get an out of // bounds read when the offset table is accessed in // BaseSound::getSoundStream. _offsets[end / 6 + 1] = 0; _hasVoiceFile = true; return; } } const bool dataIsUnsigned = true; if (Common::File::exists(gss->speech_filename)) { _hasVoiceFile = true; if (_vm->getGameType() == GType_PP) _voice = new WavSound(_mixer, gss->speech_filename); else _voice = new VocSound(_mixer, gss->speech_filename, dataIsUnsigned); } }
Font::Font(Common::String filename) { const uint32 headerFont = MKTAG('T', 'F', 'F', '\0'); Common::File stream; stream.open(filename); uint32 header = stream.readUint32BE(); if (header != headerFont) error("Invalid resource - %s", filename.c_str()); stream.skip(4); // total memory _count = stream.readUint16LE(); _first = stream.readUint16LE(); _last = stream.readUint16LE(); _width = stream.readUint16LE(); _height = stream.readUint16LE(); _fontSurface.create(_width * _count, _height, ::Graphics::PixelFormat::createFormatCLUT8()); byte cur; int bitIndex = 7; byte *p; cur = stream.readByte(); for (uint n = 0; n < _count; n++) { for (uint y = 0; y < _height; y++) { p = (byte *)_fontSurface.getBasePtr(n * _width, y); for (uint x = n * _width; x < n * _width + _width; x++) { *p++ = (cur & (1 << bitIndex)) ? 0 : 0xFF; bitIndex--; if (bitIndex < 0) { bitIndex = 7; cur = stream.readByte(); } } } } }
bool PatchedFile::load(Common::SeekableReadStream *file, const Common::String &patchName) { uint8 md5_p[16], md5_f[16]; uint32 zctrllen, zdatalen, zextralen; Common::File patch; _patchName = patchName; // Open the patch if (!patch.open(_patchName)) { error("Unable to open patchfile %s", _patchName.c_str()); return false; } // Check for appropriate signature if (patch.readUint32BE() != MKTAG('P','A','T','R')) { error("%s patchfile is corrupted", _patchName.c_str()); return false; } // Check the version number if (patch.readUint16LE() != _kVersionMajor || patch.readUint16LE() > _kVersionMinor) { error("%s has a wrong version number (must be major = %d, minor <= %d)", _patchName.c_str(), _kVersionMajor, _kVersionMinor); return false; } _flags = patch.readUint32LE(); // Check if the file to patch match Common::computeStreamMD5(*file, md5_f, _kMd5size); file->seek(0, SEEK_SET); patch.read(md5_p, 16); if (memcmp(md5_p, md5_f, 16) != 0 || (uint32)file->size() != patch.readUint32LE()) { Debug::debug(Debug::Patchr,"%s targets a different file", _patchName.c_str()); return false; } // Read lengths from header _newSize = patch.readUint32LE(); zctrllen = patch.readUint32LE(); zdatalen = patch.readUint32LE(); zextralen = patch.readUint32LE(); patch.close(); // Opens ctrl, diff and extra substreams Common::File *tmp; tmp = new Common::File; tmp->open(_patchName); _ctrl = new Common::SeekableSubReadStream(tmp, _kHeaderSize, _kHeaderSize + zctrllen, DisposeAfterUse::YES); if (_flags & FLAG_COMPRESS_CTRL) _ctrl = Common::wrapCompressedReadStream(_ctrl); //ctrl stream sanity checks if (_ctrl->size() % (3 * sizeof(uint32)) != 0) { error("%s patchfile is corrupted", _patchName.c_str()); return false; } instrLeft = _ctrl->size() / (3 * sizeof(uint32)); tmp = new Common::File; tmp->open(_patchName); _diff = new Common::SeekableSubReadStream(tmp, _kHeaderSize + zctrllen, _kHeaderSize + zctrllen + zdatalen, DisposeAfterUse::YES); _diff = Common::wrapCompressedReadStream(_diff); if (_flags & FLAG_MIX_DIFF_EXTRA) _extra = _diff; else { tmp = new Common::File; tmp->open(_patchName); _extra = new Common::SeekableSubReadStream(tmp, _kHeaderSize + zctrllen + zdatalen, _kHeaderSize + zctrllen + zdatalen + zextralen, DisposeAfterUse::YES); _extra = Common::wrapCompressedReadStream(_extra); } _file = file; readNextInst(); return true; }
Common::ErrorCode Blorb::load() { // First, chew through the file and index the chunks Common::File f; if (!f.open(_filename) || f.size() < 12) return Common::kReadingFailed; if (f.readUint32BE() != ID_FORM) return Common::kReadingFailed; f.readUint32BE(); if (f.readUint32BE() != ID_IFRS) return Common::kReadingFailed; if (f.readUint32BE() != ID_RIdx) return Common::kReadingFailed; f.readUint32BE(); uint count = f.readUint32BE(); // First read in the resource index for (uint idx = 0; idx < count; ++idx) { ChunkEntry ce; ce._type = f.readUint32BE(); ce._number = f.readUint32BE(); ce._offset = f.readUint32BE(); _chunks.push_back(ce); } // Further iterate through the resources for (uint idx = 0; idx < _chunks.size(); ++idx) { ChunkEntry &ce = _chunks[idx]; f.seek(ce._offset); ce._offset += 8; ce._id = f.readUint32BE(); ce._size = f.readUint32BE(); if (ce._type == ID_Pict) { ce._filename = Common::String::format("pic%u", ce._number); if (ce._id == ID_JPEG) ce._filename += ".jpg"; else if (ce._id == ID_PNG) ce._filename += ".png"; else if (ce._id == ID_Rect) ce._filename += ".rect"; } else if (ce._type == ID_Snd) { ce._filename = Common::String::format("sound%u", ce._number); if (ce._id == ID_MIDI) ce._filename += ".midi"; else if (ce._id == ID_MP3) ce._filename += ".mp3"; else if (ce._id == ID_WAVE) ce._filename += ".wav"; else if (ce._id == ID_AIFF || ce._id == ID_FORM) ce._filename += ".aiff"; else if (ce._id == ID_OGG) ce._filename += ".ogg"; else if (ce._id == ID_MOD) ce._filename += ".mod"; } else if (ce._type == ID_Data) { ce._filename = Common::String::format("data%u", ce._number); } else if (ce._type == ID_Exec) { char buffer[5]; WRITE_BE_UINT32(buffer, ce._id); buffer[4] = '\0'; Common::String type(buffer); if ( (_interpType == INTERPRETER_FROTZ && type == "ZCOD") || (_interpType == INTERPRETER_GLULXE && type == "GLUL") || (_interpType == INTERPRETER_TADS2 && type == "TAD2") || (_interpType == INTERPRETER_TADS3 && type == "TAD3") || (_interpType == INTERPRETER_HUGO && type == "HUGO") ) { // Game executable ce._filename = "game"; } else { ce._filename = type; } } } return Common::kNoError; }
bool AGOSEngine::loadVGASoundFile(uint16 id, uint8 type) { Common::File in; char filename[15]; byte *dst; uint32 srcSize, dstSize; if (getPlatform() == Common::kPlatformAmiga || getPlatform() == Common::kPlatformAtariST) { if (getGameType() == GType_ELVIRA1 && (getFeatures() & GF_DEMO) && getPlatform() == Common::kPlatformAmiga) { sprintf(filename, "%c%d.out", 48 + id, type); } else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) { sprintf(filename, "%.2d%d.out", id, type); } else if (getGameType() == GType_PN) { sprintf(filename, "%c%d.in", id + 48, type); } else { sprintf(filename, "%.3d%d.out", id, type); } } else { if (getGameType() == GType_ELVIRA1) { if (elvira1_soundTable[id] == 0) return false; sprintf(filename, "%.2d.SND", elvira1_soundTable[id]); } else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { sprintf(filename, "%.2d%d.VGA", id, type); } else if (getGameType() == GType_PN) { sprintf(filename, "%c%d.out", id + 48, type); } else { sprintf(filename, "%.3d%d.VGA", id, type); } } in.open(filename); if (in.isOpen() == false || in.size() == 0) { return false; } dstSize = srcSize = in.size(); if (getGameType() == GType_PN && (getFeatures() & GF_CRUNCHED)) { Common::Stack<uint32> data; byte *dataOut = 0; int dataOutSize = 0; for (uint i = 0; i < srcSize / 4; ++i) data.push(in.readUint32BE()); decompressPN(data, dataOut, dataOutSize); dst = allocBlock (dataOutSize); memcpy(dst, dataOut, dataOutSize); delete[] dataOut; } else if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) { byte *srcBuffer = (byte *)malloc(srcSize); if (in.read(srcBuffer, srcSize) != srcSize) error("loadVGASoundFile: Read failed"); dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4); dst = allocBlock (dstSize); decrunchFile(srcBuffer, dst, srcSize); free(srcBuffer); } else { dst = allocBlock(dstSize); if (in.read(dst, dstSize) != dstSize) error("loadVGASoundFile: Read failed"); } in.close(); return true; }
void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) { Common::File file; if (!file.open(fileName)) { warning("Could not open file %s", fileName.c_str()); return; } // Read the magic number // Some files are true TGA, while others are TGZ uint32 fileType = file.readUint32BE(); uint32 imageWidth; uint32 imageHeight; Graphics::TGADecoder tga; uint16 *buffer; bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA; // All ZVision images are in RGB 555 Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); destination.format = pixelFormat555; bool isTGZ; // Check for TGZ files if (fileType == MKTAG('T', 'G', 'Z', '\0')) { isTGZ = true; // TGZ files have a header and then Bitmap data that is compressed with LZSS uint32 decompressedSize = file.readSint32LE(); imageWidth = file.readSint32LE(); imageHeight = file.readSint32LE(); LzssReadStream lzssStream(&file); buffer = (uint16 *)(new uint16[decompressedSize]); lzssStream.read(buffer, decompressedSize); } else { isTGZ = false; // Reset the cursor file.seek(0); // Decode if (!tga.loadStream(file)) { warning("Error while reading TGA image"); return; } Graphics::Surface tgaSurface = *(tga.getSurface()); imageWidth = tgaSurface.w; imageHeight = tgaSurface.h; buffer = (uint16 *)tgaSurface.getPixels(); } // Flip the width and height if transposed if (isTransposed) { uint16 temp = imageHeight; imageHeight = imageWidth; imageWidth = temp; } // If the destination internal buffer is the same size as what we're copying into it, // there is no need to free() and re-create if (imageWidth != destination.w || imageHeight != destination.h) { destination.create(imageWidth, imageHeight, pixelFormat555); } // If transposed, 'un-transpose' the data while copying it to the destination // Otherwise, just do a simple copy if (isTransposed) { uint16 *dest = (uint16 *)destination.getPixels(); for (uint32 y = 0; y < imageHeight; ++y) { uint32 columnIndex = y * imageWidth; for (uint32 x = 0; x < imageWidth; ++x) { dest[columnIndex + x] = buffer[x * imageHeight + y]; } } } else { memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); } // Cleanup if (isTGZ) { delete[] buffer; } else { tga.destroy(); } // Convert in place to RGB 565 from RGB 555 destination.convertToInPlace(_pixelFormat); }
/** * Plays the specified MIDI sequence through the sound driver. * @param dwFileOffset File offset of MIDI sequence data * @param bLoop Whether to loop the sequence */ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { g_currentMidi = dwFileOffset; g_currentLoop = bLoop; bool mute = false; if (ConfMan.hasKey("mute")) mute = ConfMan.getBool("mute"); SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); // the index and length of the last tune loaded uint32 dwSeqLen = 0; // length of the sequence // Support for external music from the music enhancement project if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) { int trackNumber = GetTrackNumber(dwFileOffset); // Track 8 has been removed in the German CD re-release "Neon Edition" if ((_vm->getFeatures() & GF_ALT_MIDI) && trackNumber >= 8) trackNumber++; int track = 0; if (trackNumber >= 0) { if (_vm->getFeatures() & GF_SCNFILES) track = enhancedAudioSCNVersion[trackNumber]; else track = enhancedAudioGRAVersion[trackNumber]; if (track > 0) { StopMidi(); // StopMidi resets these fields, so set them again g_currentMidi = dwFileOffset; g_currentLoop = bLoop; // try to play track, but don't fall back to a true CD g_system->getAudioCDManager()->play(track, bLoop ? -1 : 1, 0, 0, true); // Check if an enhanced audio track is being played. // If it is, stop here and don't load a MIDI track if (g_system->getAudioCDManager()->isPlaying()) { return true; } } } else { warning("Unknown MIDI offset %d", dwFileOffset); } } if (dwFileOffset == 0) return true; Common::File midiStream; // open MIDI sequence file in binary mode if (!midiStream.open(MIDI_FILE)) error(CANNOT_FIND_FILE, MIDI_FILE); // move to correct position in the file midiStream.seek(dwFileOffset, SEEK_SET); if (TinselV1Mac) { // The Macintosh version of DW1 uses raw PCM for music dwSeqLen = midiStream.readUint32BE(); _vm->_sound->playDW1MacMusic(midiStream, dwSeqLen); } else { dwSeqLen = midiStream.readUint32LE(); // make sure buffer is large enough for this sequence assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size); // stop any currently playing tune _vm->_midiMusic->stop(); // read the sequence. This needs to be read again before playSEQ() is // called even if the music is restarting, as playSEQ() reads the file // name off the buffer itself. However, that function adds SMF headers // to the buffer, thus if it's read again, the SMF headers will be read // and the filename will always be 'MThd'. if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen) error(FILE_IS_CORRUPT, MIDI_FILE); // WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii", // which actually affects all ports, since it's specific to the GRA version. // // The GRA version does not seem to set the channel volume at all for the first // intro track, thus we need to do that here. We only initialize the channels // used in that sequence. And we are using 127 as default channel volume. // // Only in the GRA version dwFileOffset can be "38888", just to be sure, we // check for the SCN files feature flag not being set though. if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) { _vm->_midiMusic->send(0x7F07B0 | 3); _vm->_midiMusic->send(0x7F07B0 | 5); _vm->_midiMusic->send(0x7F07B0 | 8); _vm->_midiMusic->send(0x7F07B0 | 10); _vm->_midiMusic->send(0x7F07B0 | 13); } _vm->_midiMusic->playMIDI(dwSeqLen, bLoop); } midiStream.close(); return true; }
/** * 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(); } }
void MidiMusicPlayer::playSEQ(uint32 size, bool loop) { // MIDI.DAT holds the file names in DW1 PSX Common::String baseName((char *)g_midiBuffer.pDat, size); Common::String seqName = baseName + ".SEQ"; // TODO: Load the instrument bank (<baseName>.VB and <baseName>.VH) Common::File seqFile; if (!seqFile.open(seqName)) error("Failed to open SEQ file '%s'", seqName.c_str()); if (seqFile.readUint32LE() != MKTAG('S', 'E', 'Q', 'p')) error("Failed to find SEQp tag"); // Make sure we don't have a SEP file (with multiple SEQ's inside) if (seqFile.readUint32BE() != 1) error("Can only play SEQ files, not SEP"); uint16 ppqn = seqFile.readUint16BE(); uint32 tempo = seqFile.readUint16BE() << 8; tempo |= seqFile.readByte(); /* uint16 beat = */ seqFile.readUint16BE(); // SEQ is directly based on SMF and we'll use that to our advantage here // and convert to SMF and then use the SMF MidiParser. // Calculate the SMF size we'll need uint32 dataSize = seqFile.size() - 15; uint32 actualSize = dataSize + 7 + 22; // Resize the buffer if necessary if (g_midiBuffer.size < actualSize) { g_midiBuffer.pDat = (byte *)realloc(g_midiBuffer.pDat, actualSize); assert(g_midiBuffer.pDat); } // Now construct the header WRITE_BE_UINT32(g_midiBuffer.pDat, MKTAG('M', 'T', 'h', 'd')); WRITE_BE_UINT32(g_midiBuffer.pDat + 4, 6); // header size WRITE_BE_UINT16(g_midiBuffer.pDat + 8, 0); // type 0 WRITE_BE_UINT16(g_midiBuffer.pDat + 10, 1); // one track WRITE_BE_UINT16(g_midiBuffer.pDat + 12, ppqn); WRITE_BE_UINT32(g_midiBuffer.pDat + 14, MKTAG('M', 'T', 'r', 'k')); WRITE_BE_UINT32(g_midiBuffer.pDat + 18, dataSize + 7); // SEQ data size + tempo change event size // Add in a fake tempo change event WRITE_BE_UINT32(g_midiBuffer.pDat + 22, 0x00FF5103); // no delta, meta event, tempo change, param size = 3 WRITE_BE_UINT16(g_midiBuffer.pDat + 26, tempo >> 8); g_midiBuffer.pDat[28] = tempo & 0xFF; // Now copy in the rest of the events seqFile.read(g_midiBuffer.pDat + 29, dataSize); seqFile.close(); MidiParser *parser = MidiParser::createParser_SMF(); if (parser->loadMusic(g_midiBuffer.pDat, actualSize)) { parser->setTrack(0); parser->setMidiDriver(this); parser->setTimerRate(getBaseTempo()); parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1); _parser = parser; _isLooping = loop; _isPlaying = true; } else { delete parser; } }
void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed) { Common::File file; if (!_engine->getSearchManager()->openFile(file, fileName)) { warning("Could not open file %s", fileName.c_str()); return; } // Read the magic number // Some files are true TGA, while others are TGZ uint32 fileType = file.readUint32BE(); uint32 imageWidth; uint32 imageHeight; Image::TGADecoder tga; uint16 *buffer; // All ZVision images are in RGB 555 destination.format = _engine->_resourcePixelFormat; bool isTGZ; // Check for TGZ files if (fileType == MKTAG('T', 'G', 'Z', '\0')) { isTGZ = true; // TGZ files have a header and then Bitmap data that is compressed with LZSS uint32 decompressedSize = file.readSint32LE() / 2; imageWidth = file.readSint32LE(); imageHeight = file.readSint32LE(); LzssReadStream lzssStream(&file); buffer = (uint16 *)(new uint16[decompressedSize]); lzssStream.read(buffer, 2 * decompressedSize); #ifndef SCUMM_LITTLE_ENDIAN for (uint32 i = 0; i < decompressedSize; ++i) buffer[i] = FROM_LE_16(buffer[i]); #endif } else { isTGZ = false; // Reset the cursor file.seek(0); // Decode if (!tga.loadStream(file)) { warning("Error while reading TGA image"); return; } Graphics::Surface tgaSurface = *(tga.getSurface()); imageWidth = tgaSurface.w; imageHeight = tgaSurface.h; buffer = (uint16 *)tgaSurface.getPixels(); } // Flip the width and height if transposed if (transposed) { uint16 temp = imageHeight; imageHeight = imageWidth; imageWidth = temp; } // If the destination internal buffer is the same size as what we're copying into it, // there is no need to free() and re-create if (imageWidth != destination.w || imageHeight != destination.h) { destination.create(imageWidth, imageHeight, _engine->_resourcePixelFormat); } // If transposed, 'un-transpose' the data while copying it to the destination // Otherwise, just do a simple copy if (transposed) { uint16 *dest = (uint16 *)destination.getPixels(); for (uint32 y = 0; y < imageHeight; ++y) { uint32 columnIndex = y * imageWidth; for (uint32 x = 0; x < imageWidth; ++x) { dest[columnIndex + x] = buffer[x * imageHeight + y]; } } } else { memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * destination.format.bytesPerPixel); } // Cleanup if (isTGZ) { delete[] buffer; } else { tga.destroy(); } }
void readElement(Common::File &file, Saga::ResourceData &element) { element.id = file.readUint32BE(); element.offset = file.readUint32LE(); element.size = file.readUint32LE(); debug(3, "Entry: id %u, offset %u, size %u", element.id, (uint)element.offset, (uint)element.size); }