//========================================================================== // // RenameNerve // // Renames map headers and map name pictures in nerve.wad so as to load it // alongside Doom II and offer both episodes without causing conflicts. // MD5 checksum for NERVE.WAD: 967d5ae23daf45196212ae1b605da3b0 // //========================================================================== void FWadCollection::RenameNerve () { if (gameinfo.gametype != GAME_Doom) return; bool found = false; BYTE cksum[16]; static const BYTE nerve[16] = { 0x96, 0x7d, 0x5a, 0xe2, 0x3d, 0xaf, 0x45, 0x19, 0x62, 0x12, 0xae, 0x1b, 0x60, 0x5d, 0xa3, 0xb0 }; size_t nervesize = 3819855; // NERVE.WAD's file size int w = IWAD_FILENUM; while (++w < GetNumWads()) { FileReader *fr = GetFileReader(w); if (fr == NULL) { continue; } if (fr->GetLength() != (long)nervesize) { // Skip MD5 computation when there is a // cheaper way to know this is not the file continue; } fr->Seek(0, SEEK_SET); MD5Context md5; md5.Update(fr, fr->GetLength()); md5.Final(cksum); if (memcmp(nerve, cksum, 16) == 0) { found = true; break; } } if (!found) return; for (int i = GetFirstLump(w); i <= GetLastLump(w); i++) { // Only rename the maps from NERVE.WAD assert(LumpInfo[i].wadnum == w); if (LumpInfo[i].lump->dwName == MAKE_ID('C', 'W', 'I', 'L')) { LumpInfo[i].lump->Name[0] = 'N'; } else if (LumpInfo[i].lump->dwName == MAKE_ID('M', 'A', 'P', '0')) { LumpInfo[i].lump->Name[6] = LumpInfo[i].lump->Name[4]; LumpInfo[i].lump->Name[5] = '0'; LumpInfo[i].lump->Name[4] = 'L'; LumpInfo[i].lump->dwName = MAKE_ID('L', 'E', 'V', 'E'); } } }
HMISong::HMISong (FileReader &reader, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), Tracks(0) { #ifdef _WIN32 if (ExitEvent == NULL) { return; } #endif int len = reader.GetLength(); if (len < 0x100) { // Way too small to be HMI. return; } MusHeader = new BYTE[len]; SongLen = len; NumTracks = 0; if (reader.Read(MusHeader, len) != len) return; // Do some validation of the MIDI file if (memcmp(MusHeader, HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0) { SetupForHMI(len); } else if (((DWORD *)MusHeader)[0] == MAKE_ID('H','M','I','M') && ((DWORD *)MusHeader)[1] == MAKE_ID('I','D','I','P')) { SetupForHMP(len); } }
XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args) : MIDIStreamer(type, args), MusHeader(0), Songs(0) { SongLen = reader.GetLength(); MusHeader = new uint8_t[SongLen]; if (reader.Read(MusHeader, SongLen) != SongLen) return; // Find all the songs in this file. NumSongs = FindXMIDforms(MusHeader, SongLen, NULL); if (NumSongs == 0) { return; } // XMIDI files are played with a constant 120 Hz clock rate. While the // song may contain tempo events, these are vestigial remnants from the // original MIDI file that were not removed by the converter and should // be ignored. // // We can use any combination of Division and Tempo values that work out // to be 120 Hz. Division = 60; InitialTempo = 500000; Songs = new TrackInfo[NumSongs]; memset(Songs, 0, sizeof(*Songs) * NumSongs); FindXMIDforms(MusHeader, SongLen, Songs); CurrSong = Songs; DPrintf(DMSG_SPAMMY, "XMI song count: %d\n", NumSongs); }
bool FPatchTexture::Check(FileReader & file) { if (file.GetLength() < 13) return false; // minimum length of a valid Doom patch BYTE *data = new BYTE[file.GetLength()]; file.Seek(0, SEEK_SET); file.Read(data, file.GetLength()); const patch_t * foo = (const patch_t *)data; int height = LittleShort(foo->height); int width = LittleShort(foo->width); bool gapAtStart=true; if (height > 0 && height < 2048 && width > 0 && width <= 2048 && width < file.GetLength()/4) { // The dimensions seem like they might be valid for a patch, so // check the column directory for extra security. At least one // column must begin exactly at the end of the column directory, // and none of them must point past the end of the patch. bool gapAtStart = true; int x; for (x = 0; x < width; ++x) { DWORD ofs = LittleLong(foo->columnofs[x]); if (ofs == (DWORD)width * 4 + 8) { gapAtStart = false; } else if (ofs >= (DWORD)(file.GetLength())) // Need one byte for an empty column (but there's patches that don't know that!) { delete [] data; return false; } } delete [] data; return !gapAtStart; } delete [] data; return false; }
errno_t EasyDecryptInMemory(const string& filename, const string& key, MemoryWriter *writer) { LOGPOS(); FilePath inFilePath(filename); FileReader reader; if (reader.Open(inFilePath.value()) != CPT_OK) { LOGW("Open file %s failed!", inFilePath.value().c_str()); return CPT_ERROR; } uint64_t u64 = 0; reader.GetLength(&u64); if (writer->Reserve(u64) != CPT_OK) { LOGW("MemoryWriter SetLength failed!"); return CPT_ERROR; } Decrypt decrypt; if (decrypt.SetReader(&reader) != CPT_OK) { LOGW("Decrypt set reader failed!"); return CPT_ERROR; } if (decrypt.LoadHeader() != CPT_OK) { LOGW("Decrypt LoadHeader error"); return CPT_ERROR; } if (decrypt.DecryptHeader(key.c_str(), key.length()) == NULL) { LOGW("LoadHeader error"); return CPT_ERROR; } if (decrypt.SetWriter(writer) != CPT_OK) { LOGW("Decrypt set writer failed!"); return CPT_ERROR; } int err = decrypt.PreDecrypt(); ASSERT(err == CPT_OK); if (decrypt.DoDecrypt(key.c_str(), key.length())) { LOGW("Decrypt decrypt file failed!"); return CPT_ERROR; } err = decrypt.PostDecrypt(); ASSERT(err == CPT_OK); return CPT_OK; }
// Note: Only works for mono samples. bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file, uint16 blockAlign) //------------------------------------------------------------------------------------------- { static const int32 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; static const int32 IMAUnpackTable[90] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767, 0 }; if((sampleLen < 4) || (!target) || (blockAlign < 5) || (blockAlign > file.GetLength())) return false; SmpLength nPos = 0; while((nPos < sampleLen) && file.CanRead(5)) { int32 value = file.ReadIntLE<int16>(); int32 nIndex = file.ReadIntLE<uint8>(); nIndex = Clamp(nIndex, 0, 89); file.Skip(1); target[nPos++] = (int16)value; for(uint32 i = 0; (i < (blockAlign - 4u) * 2u) && (nPos < sampleLen) && file.AreBytesLeft(); i++) { uint8 delta; if(i & 1) { delta = (file.ReadIntLE<uint8>() >> 4) & 0x0F; } else { delta = file.ReadIntLE<uint8>() & 0x0F; file.SkipBack(1); } int32 v = IMAUnpackTable[nIndex] >> 3; if (delta & 1) v += IMAUnpackTable[nIndex] >> 2; if (delta & 2) v += IMAUnpackTable[nIndex] >> 1; if (delta & 4) v += IMAUnpackTable[nIndex]; if (delta & 8) value -= v; else value += v; nIndex += IMAIndexTab[delta & 7]; nIndex = Clamp(nIndex, 0, 88); target[nPos++] = static_cast<int16>(Clamp(value, -32768, 32767)); }
bool FScanner::OpenFile (const char *name) { Close (); FileReader fr; if (!fr.OpenFile(name)) return false; auto filesize = fr.GetLength(); auto filebuff = fr.Read(); if (filebuff.Size() == 0 && filesize > 0) return false; ScriptBuffer = FString((const char *)filebuff.Data(), filesize); ScriptName = name; // This is used for error messages so the full file name is preferable LumpNum = -1; PrepareScript (); return true; }
off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence) { FileReader *reader = reinterpret_cast<MPG123Decoder*>(handle)->Reader; if(whence == SEEK_CUR) { if(offset < 0 && reader->Tell()+offset < 0) return -1; } else if(whence == SEEK_END) { if(offset < 0 && reader->GetLength()+offset < 0) return -1; } if(reader->Seek(offset, whence) != 0) return -1; return reader->Tell(); }
CZipArchive::CZipArchive(FileReader &file) : ArchiveBase(file) //------------------------------------------------------------ { zipFile = new mz_zip_archive(); mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile); MemsetZero(*zip); if(!mz_zip_reader_init_mem(zip, file.GetRawData(), file.GetLength(), 0)) { delete zip; zip = nullptr; zipFile = nullptr; } if(!zip) { return; } for(mz_uint i = 0; i < mz_zip_reader_get_num_files(zip); ++i) { ArchiveFileInfo info; info.type = ArchiveFileInvalid; mz_zip_archive_file_stat stat; MemsetZero(stat); if(mz_zip_reader_file_stat(zip, i, &stat)) { info.type = ArchiveFileNormal; info.name = mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetCP437, stat.m_filename)); info.size = stat.m_uncomp_size; } if(mz_zip_reader_is_file_a_directory(zip, i)) { info.type = ArchiveFileSpecial; } else if(mz_zip_reader_is_file_encrypted(zip, i)) { info.type = ArchiveFileSpecial; } contents.push_back(info); } }
off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence) { MPG123Decoder *self = reinterpret_cast<MPG123Decoder*>(handle); FileReader *reader = self->Reader; if(whence == SEEK_SET) offset += self->StartOffset; else if(whence == SEEK_CUR) { if(offset < 0 && reader->Tell()+offset < self->StartOffset) return -1; } else if(whence == SEEK_END) { if(offset < 0 && reader->GetLength()+offset < self->StartOffset) return -1; } if(reader->Seek(offset, whence) != 0) return -1; return reader->Tell() - self->StartOffset; }
HMISong::HMISong (FileReader &reader) { int len = (int)reader.GetLength(); if (len < 0x100) { // Way too small to be HMI. return; } MusHeader = new uint8_t[len]; SongLen = len; NumTracks = 0; if (reader.Read(MusHeader, len) != len) return; // Do some validation of the MIDI file if (memcmp(MusHeader, HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0) { SetupForHMI(len); } else if (((uint32_t *)MusHeader)[0] == MAKE_ID('H','M','I','M') && ((uint32_t *)MusHeader)[1] == MAKE_ID('I','D','I','P')) { SetupForHMP(len); } }
void FWadCollection::AddFile (const char *filename, FileReader *wadinfo) { int startlump; bool isdir = false; if (wadinfo == NULL) { // Does this exist? If so, is it a directory? if (!DirEntryExists(filename, &isdir)) { Printf(TEXTCOLOR_RED "%s: File or Directory not found\n", filename); PrintLastError(); return; } if (!isdir) { try { wadinfo = new FileReader(filename); } catch (CRecoverableError &err) { // Didn't find file Printf (TEXTCOLOR_RED "%s\n", err.GetMessage()); PrintLastError (); return; } } } if (!batchrun) Printf (" adding %s", filename); startlump = NumLumps; FResourceFile *resfile; if (!isdir) resfile = FResourceFile::OpenResourceFile(filename, wadinfo); else resfile = FResourceFile::OpenDirectory(filename); if (resfile != NULL) { uint32_t lumpstart = LumpInfo.Size(); resfile->SetFirstLump(lumpstart); for (uint32_t i=0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); FWadCollection::LumpRecord *lump_p = &LumpInfo[LumpInfo.Reserve(1)]; lump_p->lump = lump; lump_p->wadnum = Files.Size(); } if (static_cast<int>(Files.Size()) == GetIwadNum() && gameinfo.gametype == GAME_Strife && gameinfo.flags & GI_SHAREWARE) { resfile->FindStrifeTeaserVoices(); } Files.Push(resfile); for (uint32_t i=0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); if (lump->Flags & LUMPF_EMBEDDED) { FString path; path.Format("%s:%s", filename, lump->FullName.GetChars()); FileReader *embedded = lump->NewReader(); AddFile(path, embedded); } } if (hashfile) { uint8_t cksum[16]; char cksumout[33]; memset(cksumout, 0, sizeof(cksumout)); FileReader *reader = wadinfo; if (reader != NULL) { MD5Context md5; reader->Seek(0, SEEK_SET); md5.Update(reader, reader->GetLength()); md5.Final(cksum); for (size_t j = 0; j < sizeof(cksum); ++j) { sprintf(cksumout + (j * 2), "%02X", cksum[j]); } fprintf(hashfile, "file: %s, hash: %s, size: %ld\n", filename, cksumout, reader->GetLength()); } else fprintf(hashfile, "file: %s, Directory structure\n", filename); for (uint32_t i = 0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); if (!(lump->Flags & LUMPF_EMBEDDED)) { reader = lump->NewReader(); MD5Context md5; md5.Update(reader, lump->LumpSize); md5.Final(cksum); for (size_t j = 0; j < sizeof(cksum); ++j) { sprintf(cksumout + (j * 2), "%02X", cksum[j]); } fprintf(hashfile, "file: %s, lump: %s, hash: %s, size: %d\n", filename, lump->FullName.IsNotEmpty() ? lump->FullName.GetChars() : lump->Name, cksumout, lump->LumpSize); delete reader; } } } return; } }
MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args) : MIDIStreamer(type, args), MusHeader(0), Tracks(0) { int p; int i; #ifdef _WIN32 if (ExitEvent == NULL) { return; } #endif SongLen = reader.GetLength(); MusHeader = new BYTE[SongLen]; if (reader.Read(MusHeader, SongLen) != SongLen) return; // Do some validation of the MIDI file if (MusHeader[4] != 0 || MusHeader[5] != 0 || MusHeader[6] != 0 || MusHeader[7] != 6) return; if (MusHeader[8] != 0 || MusHeader[9] > 2) return; Format = MusHeader[9]; if (Format == 0) { NumTracks = 1; } else { NumTracks = MusHeader[10] * 256 + MusHeader[11]; } // The division is the number of pulses per quarter note (PPQN). Division = MusHeader[12] * 256 + MusHeader[13]; if (Division == 0) { // PPQN is zero? Then the song cannot play because it never pulses. return; } Tracks = new TrackInfo[NumTracks]; // Gather information about each track for (i = 0, p = 14; i < NumTracks && p < SongLen + 8; ++i) { DWORD chunkLen = (MusHeader[p+4]<<24) | (MusHeader[p+5]<<16) | (MusHeader[p+6]<<8) | (MusHeader[p+7]); if (chunkLen + p + 8 > (DWORD)SongLen) { // Track too long, so truncate it chunkLen = SongLen - p - 8; } if (MusHeader[p+0] == 'M' && MusHeader[p+1] == 'T' && MusHeader[p+2] == 'r' && MusHeader[p+3] == 'k') { Tracks[i].TrackBegin = MusHeader + p + 8; Tracks[i].TrackP = 0; Tracks[i].MaxTrackP = chunkLen; } p += chunkLen + 8; } // In case there were fewer actual chunks in the file than the // header specified, update NumTracks with the current value of i NumTracks = i; if (NumTracks == 0) { // No tracks, so nothing to play return; } }
OPENMPT_NAMESPACE_BEGIN PNG::Bitmap *PNG::ReadPNG(FileReader &file) //----------------------------------------- { file.Rewind(); if(!file.ReadMagic("\211PNG\r\n\032\n")) { return nullptr; } uint32_t width = 0; uint32_t height = 0; uint8_t bitDepth; uint8_t colorType; uint8_t compressionMethod; uint8_t filterMethod; uint8_t interlaceMethod; std::vector<uint8_t> dataIn; std::vector<Pixel> palette; while(file.AreBytesLeft()) { uint32_t chunkLength = file.ReadUint32BE(); char magic[4]; file.ReadArray(magic); FileReader chunk = file.ReadChunk(chunkLength); file.Skip(4); // CRC32 if(!memcmp(magic, "IHDR", 4)) { // Image header width = chunk.ReadUint32BE(); height = chunk.ReadUint32BE(); bitDepth = chunk.ReadUint8(); colorType = chunk.ReadUint8(); compressionMethod = chunk.ReadUint8(); filterMethod = chunk.ReadUint8(); interlaceMethod = chunk.ReadUint8(); ASSERT(!filterMethod && !interlaceMethod); } else if(!memcmp(magic, "IDAT", 4)) { // Data block(s) z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = static_cast<uInt>(chunk.GetLength()); strm.next_in = (Bytef *)(chunk.GetRawData()); if(inflateInit2(&strm, 15) != Z_OK) { break; } int retVal; do { dataIn.resize(dataIn.size() + 4096); strm.avail_out = 4096; strm.next_out = (Bytef *)&dataIn[dataIn.size() - 4096]; retVal = inflate(&strm, Z_NO_FLUSH); } while(retVal == Z_OK); inflateEnd(&strm); } else if(!memcmp(magic, "PLTE", 4)) { // Palette for <= 8-bit images palette.resize(256); size_t numEntries = std::min<size_t>(256u, chunk.GetLength() / 3u); for(size_t i = 0; i < numEntries; i++) { uint8_t p[3]; chunk.ReadArray(p); palette[i] = Pixel(p[0], p[1], p[2], 255); } } } // LUT for translating the color type into a number of color samples const uint32_t sampleTable[] = { 1, // 0: Grayscale 0, 3, // 2: RGB 1, // 3: Palette bitmap 2, // 4: Grayscale + Alpha 0, 4 // 6: RGBA }; const uint32_t bitsPerPixel = colorType < CountOf(sampleTable) ? sampleTable[colorType] * bitDepth : 0; if(!width || !height || !bitsPerPixel || (colorType != 2 && colorType != 3 && colorType != 6) || bitDepth != 8 // Only RGB(A) and 8-bit palette PNGs for now. || compressionMethod || interlaceMethod || (colorType == 3 && palette.empty()) || dataIn.size() < (bitsPerPixel * width * height) / 8 + height) // Enough data present? { return nullptr; } Bitmap *bitmap = new (std::nothrow) Bitmap(width, height); Pixel *pixelOut = bitmap->GetPixels(); uint32_t x = 0, y = 0; size_t offset = 0; while(y < height) { if(x == 0) { filterMethod = dataIn[offset++]; ASSERT(!filterMethod); } if(colorType == 6) { // RGBA pixelOut->r = dataIn[offset++]; pixelOut->g = dataIn[offset++]; pixelOut->b = dataIn[offset++]; pixelOut->a = dataIn[offset++]; } else if(colorType == 2) { // RGB pixelOut->r = dataIn[offset++]; pixelOut->g = dataIn[offset++]; pixelOut->b = dataIn[offset++]; pixelOut->a = 255; } else if(colorType == 3) { // Palette *pixelOut = palette[dataIn[offset++]]; } pixelOut++; x++; if(x == width) { y++; x = 0; } } return bitmap; }
OPENMPT_NAMESPACE_BEGIN bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------- { file.Rewind(); // No valid MO3 file (magic bytes: "MO3") if(!file.CanRead(8) || !file.ReadMagic("MO3")) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } #ifdef NO_MO3 // As of November 2013, the format revision is 5; Versions > 31 are unlikely to exist in the next few years, // so we will just ignore those if there's no UNMO3 library to tell us if the file is valid or not // (avoid log entry with .MOD files that have a song name starting with "MO3". if(file.ReadUint8() > 31) { return false; } AddToLog(GetStrI18N("The file appears to be a MO3 file, but this OpenMPT build does not support loading MO3 files.")); return false; #else bool result = false; // Result of trying to load the module, false == fail. // Try to load unmo3 dynamically. mpt::Library unmo3 = mpt::Library(mpt::LibraryPath::App(MPT_PATHSTRING("unmo3"))); if(!unmo3.IsValid()) { // Didn't succeed. AddToLog(GetStrI18N("Loading MO3 file failed because unmo3.dll could not be loaded.")); } else { // Library loaded successfully. #if MPT_OS_WINDOWS #define UNMO3_API __stdcall #else #define UNMO3_API #endif typedef uint32 (UNMO3_API * UNMO3_GETVERSION)(); // Decode a MO3 file (returns the same "exit codes" as UNMO3.EXE, eg. 0=success) // IN: data/len = MO3 data/len // OUT: data/len = decoded data/len (if successful) // flags & 1: Don't load samples typedef int32 (UNMO3_API * UNMO3_DECODE_OLD)(const void **data, uint32 *len); typedef int32 (UNMO3_API * UNMO3_DECODE)(const void **data, uint32 *len, uint32 flags); // Free the data returned by UNMO3_Decode typedef void (UNMO3_API * UNMO3_FREE)(const void *data); #undef UNMO3_API UNMO3_GETVERSION UNMO3_GetVersion = nullptr; UNMO3_DECODE_OLD UNMO3_Decode_Old = nullptr; UNMO3_DECODE UNMO3_Decode = nullptr; UNMO3_FREE UNMO3_Free = nullptr; unmo3.Bind(UNMO3_GetVersion, "UNMO3_GetVersion"); if(UNMO3_GetVersion == nullptr) { // Old API version: No "flags" parameter. unmo3.Bind(UNMO3_Decode_Old, "UNMO3_Decode"); } else { unmo3.Bind(UNMO3_Decode, "UNMO3_Decode"); } unmo3.Bind(UNMO3_Free, "UNMO3_Free"); if((UNMO3_Decode != nullptr || UNMO3_Decode_Old != nullptr) && UNMO3_Free != nullptr) { file.Rewind(); const void *stream = file.GetRawData(); uint32 length = mpt::saturate_cast<uint32>(file.GetLength()); int32 unmo3result; if(UNMO3_Decode != nullptr) { unmo3result = UNMO3_Decode(&stream, &length, (loadFlags & loadSampleData) ? 0 : 1); } else { // Old API version: No "flags" parameter. unmo3result = UNMO3_Decode_Old(&stream, &length); } if(unmo3result == 0) { // If decoding was successful, stream and length will keep the new pointers now. FileReader unpackedFile(stream, length); result = ReadXM(unpackedFile, loadFlags) || ReadIT(unpackedFile, loadFlags) || ReadS3M(unpackedFile, loadFlags) || ReadMTM(unpackedFile, loadFlags) || ReadMod(unpackedFile, loadFlags) || ReadM15(unpackedFile, loadFlags); if(result) { m_ContainerType = MOD_CONTAINERTYPE_MO3; } UNMO3_Free(stream); } } } return result; #endif // NO_MO3 }
bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) { file.Rewind(); #if defined(MPT_WITH_OPUSFILE) int rate = 0; int channels = 0; std::vector<int16> raw_sample_data; FileReader initial = file.GetChunk(65536); // 512 is recommended by libopusfile if(op_test(NULL, initial.GetRawData<unsigned char>(), initial.GetLength()) != 0) { return false; } OggOpusFile *of = op_open_memory(file.GetRawData<unsigned char>(), file.GetLength(), NULL); if(!of) { return false; } rate = 48000; channels = op_channel_count(of, -1); if(rate <= 0 || channels <= 0) { op_free(of); of = NULL; return false; } if(channels > 2 || op_link_count(of) != 1) { // We downmix multichannel to stereo as recommended by Opus specification in // case we are not able to handle > 2 channels. // We also decode chained files as stereo even if they start with a mono // stream, which simplifies handling of link boundaries for us. channels = 2; } std::vector<int16> decodeBuf(120 * 48000 / 1000); // 120ms (max Opus packet), 48kHz bool eof = false; while(!eof) { int framesRead = 0; if(channels == 2) { framesRead = op_read_stereo(of, &(decodeBuf[0]), static_cast<int>(decodeBuf.size())); } else if(channels == 1) { framesRead = op_read(of, &(decodeBuf[0]), static_cast<int>(decodeBuf.size()), NULL); } if(framesRead > 0) { raw_sample_data.insert(raw_sample_data.end(), decodeBuf.begin(), decodeBuf.begin() + (framesRead * channels)); } else if(framesRead == 0) { eof = true; } else if(framesRead == OP_HOLE) { // continue } else { // other errors are fatal, stop decoding eof = true; } } op_free(of); of = NULL; if(raw_sample_data.empty()) { return false; } DestroySampleThreadsafe(sample); strcpy(m_szNames[sample], ""); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = raw_sample_data.size() / channels; Samples[sample].uFlags.set(CHN_16BIT); Samples[sample].uFlags.set(CHN_STEREO, channels == 2); Samples[sample].AllocateSample(); std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].pSample16); Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); return Samples[sample].pSample != nullptr; #else // !MPT_WITH_OPUSFILE MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); return false; #endif // MPT_WITH_OPUSFILE }
unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size, bool ismain) { FileReader *fp; int lumpnum; if (ismain) { wmPathExpander.openmode = PathExpander::OM_FILEORLUMP; wmPathExpander.clearPathlist(); #ifdef _WIN32 wmPathExpander.addToPathlist("C:\\TIMIDITY"); wmPathExpander.addToPathlist("\\TIMIDITY"); wmPathExpander.addToPathlist(progdir); #else wmPathExpander.addToPathlist("/usr/local/lib/timidity"); wmPathExpander.addToPathlist("/etc/timidity"); wmPathExpander.addToPathlist("/etc"); #endif } if (!(fp = wmPathExpander.openFileReader(filename, &lumpnum))) return NULL; if (ismain) { if (lumpnum > 0) { wmPathExpander.openmode = PathExpander::OM_LUMP; wmPathExpander.clearPathlist(); // when reading from a PK3 we don't want to use any external path } else { wmPathExpander.openmode = PathExpander::OM_FILE; } } if (fp == NULL) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); return NULL; } long fsize = fp->GetLength(); if (fsize > WM_MAXFILESIZE) { /* don't bother loading suspiciously long files */ _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LONGFIL, filename, 0); return NULL; } unsigned char *data = (unsigned char*)malloc(fsize+1); if (data == NULL) { _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); return NULL; } fp->Read(data, fsize); delete fp; data[fsize] = 0; *size = fsize; return data; }
sf_count_t SndFileDecoder::file_get_filelen(void *user_data) { FileReader *reader = reinterpret_cast<SndFileDecoder*>(user_data)->Reader; return reader->GetLength(); }
VSTPresets::ErrorCode VSTPresets::LoadFile(FileReader &file, CVstPlugin &plugin) //------------------------------------------------------------------------------ { const bool firstChunk = file.GetPosition() == 0; ChunkHeader header; if(!file.ReadConvertEndianness(header) || header.chunkMagic != cMagic) { return invalidFile; } if(header.fxID != plugin.GetUID()) { return wrongPlugin; } if(header.fxMagic == fMagic || header.fxMagic == chunkPresetMagic) { // Program PlugParamIndex numParams = file.ReadUint32BE(); VstPatchChunkInfo info; info.version = 1; info.pluginUniqueID = header.fxID; info.pluginVersion = header.fxVersion; info.numElements = numParams; MemsetZero(info.future); plugin.Dispatch(effBeginLoadProgram, 0, 0, &info, 0.0f); plugin.Dispatch(effBeginSetProgram, 0, 0, nullptr, 0.0f); char prgName[28]; file.ReadString<mpt::String::maybeNullTerminated>(prgName, 28); plugin.Dispatch(effSetProgramName, 0, 0, prgName, 0.0f); if(header.fxMagic == fMagic) { if(plugin.GetNumParameters() != numParams) { return wrongParameters; } for(PlugParamIndex p = 0; p < numParams; p++) { plugin.SetParameter(p, file.ReadFloatBE()); } } else { FileReader chunk = file.ReadChunk(file.ReadUint32BE()); plugin.Dispatch(effSetChunk, 1, chunk.GetLength(), const_cast<char *>(chunk.GetRawData()), 0); } plugin.Dispatch(effEndSetProgram, 0, 0, nullptr, 0.0f); } else if((header.fxMagic == bankMagic || header.fxMagic == chunkBankMagic) && firstChunk) { // Bank - only read if it's the first chunk in the file, not if it's a sub chunk. uint32 numProgs = file.ReadUint32BE(); uint32 currentProgram = file.ReadUint32BE(); file.Skip(124); VstPatchChunkInfo info; info.version = 1; info.pluginUniqueID = header.fxID; info.pluginVersion = header.fxVersion; info.numElements = numProgs; MemsetZero(info.future); plugin.Dispatch(effBeginLoadBank, 0, 0, &info, 0.0f); if(header.fxMagic == bankMagic) { VstInt32 oldCurrentProgram = plugin.GetCurrentProgram(); for(uint32 p = 0; p < numProgs; p++) { plugin.Dispatch(effBeginSetProgram, 0, 0, nullptr, 0.0f); plugin.Dispatch(effSetProgram, 0, 0, nullptr, 0.0f); ErrorCode retVal = LoadFile(file, plugin); if(retVal != noError) { return retVal; } plugin.Dispatch(effEndSetProgram, 0, 0, nullptr, 0.0f); } plugin.SetCurrentProgram(oldCurrentProgram); } else { FileReader chunk = file.ReadChunk(file.ReadUint32BE()); plugin.Dispatch(effSetChunk, 0, chunk.GetLength(), const_cast<char *>(chunk.GetRawData()), 0); } if(header.version >= 2) { plugin.SetCurrentProgram(currentProgram); } } return noError; }
bool CSoundFile::ReadITQ(FileReader &file, ModLoadingFlags loadFlags) //------------------------------------------------------------------ { file.Rewind(); ITFileHeader fileHeader; if(!file.ReadConvertEndianness(fileHeader) || (memcmp(fileHeader.id, "ITQM", 4)) || fileHeader.insnum > 0xFF || fileHeader.smpnum >= MAX_SAMPLES || !file.CanRead(fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4)) { return false; } else if(loadFlags == onlyVerifyHeader) { return true; } InitializeGlobals(); bool interpretModPlugMade = false; // OpenMPT crap at the end of file file.Seek(file.GetLength() - 4); size_t mptStartPos = file.ReadUint32LE(); if(mptStartPos >= file.GetLength() || mptStartPos < 0x100) { mptStartPos = file.GetLength(); } if(!memcmp(fileHeader.id, "tpm.", 4)) { // Legacy MPTM files (old 1.17.02.xx releases) ChangeModTypeTo(MOD_TYPE_MPT); } else { if(mptStartPos <= file.GetLength() - 3 && fileHeader.cwtv > 0x888 && fileHeader.cwtv <= 0xFFF) { file.Seek(mptStartPos); ChangeModTypeTo(file.ReadMagic("228") ? MOD_TYPE_MPT : MOD_TYPE_IT); } else { ChangeModTypeTo(MOD_TYPE_IT); } if(GetType() == MOD_TYPE_IT) { // Which tracker was used to made this? if((fileHeader.cwtv & 0xF000) == 0x5000) { // OpenMPT Version number (Major.Minor) // This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used. m_dwLastSavedWithVersion = (fileHeader.cwtv & 0x0FFF) << 16; if(!memcmp(fileHeader.reserved, "OMPT", 4)) interpretModPlugMade = true; } else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888) { // OpenMPT 1.17 and 1.18 (raped IT format) // Exact version number will be determined later. interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0217 && fileHeader.cmwt == 0x0200 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { if(memchr(fileHeader.chnpan, 0xFF, sizeof(fileHeader.chnpan)) != NULL) { // ModPlug Tracker 1.16 (semi-raped IT format) m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); madeWithTracker = "ModPlug tracker 1.09 - 1.16"; } else { // OpenMPT 1.17 disguised as this in compatible mode, // but never writes 0xFF in the pan map for unused channels (which is an invalid value). m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); madeWithTracker = "OpenMPT 1.17 (compatibility export)"; } interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { // ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); madeWithTracker = "ModPlug tracker b3.3 - 1.09"; interpretModPlugMade = true; } } else // case: type == MOD_TYPE_MPT { if (fileHeader.cwtv >= verMptFileVerLoadLimit) { AddToLog(str_LoadingIncompatibleVersion); return false; } else if (fileHeader.cwtv > verMptFileVer) { AddToLog(str_LoadingMoreRecentVersion); } } } if(GetType() == MOD_TYPE_IT) mptStartPos = file.GetLength(); // Read row highlights if((fileHeader.special & ITFileHeader::embedPatternHighlights)) { // MPT 1.09, 1.07 and most likely other old MPT versions leave this blank (0/0), but have the "special" flag set. // Newer versions of MPT and OpenMPT 1.17 *always* write 4/16 here. // Thus, we will just ignore those old versions. if(m_dwLastSavedWithVersion == 0 || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02)) { m_nDefaultRowsPerBeat = fileHeader.highlight_minor; m_nDefaultRowsPerMeasure = fileHeader.highlight_major; } #ifdef _DEBUG if((fileHeader.highlight_minor | fileHeader.highlight_major) == 0) { Log("IT Header: Row highlight is 0"); } #endif } m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & ITFileHeader::linearSlides) != 0); m_SongFlags.set(SONG_ITOLDEFFECTS, (fileHeader.flags & ITFileHeader::itOldEffects) != 0); m_SongFlags.set(SONG_ITCOMPATGXX, (fileHeader.flags & ITFileHeader::itCompatGxx) != 0); m_SongFlags.set(SONG_EMBEDMIDICFG, (fileHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) || (fileHeader.special & ITFileHeader::embedMIDIConfiguration)); m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & ITFileHeader::extendedFilterRange) != 0); mpt::String::Read<mpt::String::spacePadded>(songName, fileHeader.songname); // Global Volume m_nDefaultGlobalVolume = fileHeader.globalvol << 1; if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; m_nDefaultTempo = std::max(uint8(32), fileHeader.tempo); // Tempo 31 is possible. due to conflicts with the rest of the engine, let's just clamp it to 32. m_nSamplePreAmp = std::min(fileHeader.mv, uint8(128)); // Reading Channels Pan Positions for(CHANNELINDEX i = 0; i < 64; i++) if(fileHeader.chnpan[i] != 0xFF) { ChnSettings[i].Reset(); ChnSettings[i].nVolume = Clamp(fileHeader.chnvol[i], uint8(0), uint8(64)); if(fileHeader.chnpan[i] & 0x80) ChnSettings[i].dwFlags.set(CHN_MUTE); uint8 n = fileHeader.chnpan[i] & 0x7F; if(n <= 64) ChnSettings[i].nPan = n * 4; if(n == 100) ChnSettings[i].dwFlags.set(CHN_SURROUND); } // Reading orders file.Seek(sizeof(ITFileHeader)); if(GetType() == MOD_TYPE_IT) { Order.ReadAsByte(file, fileHeader.ordnum); } else { if(fileHeader.cwtv > 0x88A && fileHeader.cwtv <= 0x88D) { Order.Deserialize(file); } else { Order.ReadAsByte(file, fileHeader.ordnum); // Replacing 0xFF and 0xFE with new corresponding indexes Order.Replace(0xFE, Order.GetIgnoreIndex()); Order.Replace(0xFF, Order.GetInvalidPatIndex()); } } // Reading instrument, sample and pattern offsets std::vector<uint32> insPos, smpPos, patPos; file.ReadVectorLE(insPos, fileHeader.insnum); file.ReadVectorLE(smpPos, fileHeader.smpnum); file.ReadVectorLE(patPos, fileHeader.patnum); // Find the first parapointer. // This is used for finding out whether the edit history is actually stored in the file or not, // as some early versions of Schism Tracker set the history flag, but didn't save anything. // We will consider the history invalid if it ends after the first parapointer. uint32 minPtr = Util::MaxValueOfType(minPtr); for(uint16 n = 0; n < fileHeader.insnum; n++) { if(insPos[n] > 0) { minPtr = std::min(minPtr, insPos[n]); } } for(uint16 n = 0; n < fileHeader.smpnum; n++) { if(smpPos[n] > 0) { minPtr = std::min(minPtr, smpPos[n]); } } for(uint16 n = 0; n < fileHeader.patnum; n++) { if(patPos[n] > 0) { minPtr = std::min(minPtr, patPos[n]); } } if(fileHeader.special & ITFileHeader::embedSongMessage) { minPtr = std::min(minPtr, fileHeader.msgoffset); } // Reading IT Edit History Info // This is only supposed to be present if bit 1 of the special flags is set. // However, old versions of Schism and probably other trackers always set this bit // even if they don't write the edit history count. So we have to filter this out... // This is done by looking at the parapointers. If the history data end after // the first parapointer, we assume that it's actually no history data. if(fileHeader.special & ITFileHeader::embedEditHistory) { const uint16 nflt = file.ReadUint16LE(); if(file.CanRead(nflt * sizeof(ITHistoryStruct)) && file.GetPosition() + nflt * sizeof(ITHistoryStruct) <= minPtr) { m_FileHistory.reserve(nflt); for(size_t n = 0; n < nflt; n++) { FileHistory mptHistory; ITHistoryStruct itHistory; file.ReadConvertEndianness(itHistory); itHistory.ConvertToMPT(mptHistory); m_FileHistory.push_back(mptHistory); } } else { // Oops, we were not supposed to read this. file.SkipBack(2); } } else if(fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.cmwt == 0x0214 && fileHeader.cwtv == 0x0214 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4) && (fileHeader.special & (ITFileHeader::embedEditHistory | ITFileHeader::embedPatternHighlights)) == 0) { // Another non-conforming application is unmo3 < v2.4.0.1, which doesn't set the special bit // at all, but still writes the two edit history length bytes (zeroes)... if(file.ReadUint16LE() != 0) { // These were not zero bytes -> We're in the wrong place! file.SkipBack(2); madeWithTracker = "UNMO3"; } } // Reading MIDI Output & Macros if(m_SongFlags[SONG_EMBEDMIDICFG] && file.Read(m_MidiCfg)) { m_MidiCfg.Sanitize(); } // Ignore MIDI data. Fixes some files like denonde.it that were made with old versions of Impulse Tracker (which didn't support Zxx filters) and have Zxx effects in the patterns. if(fileHeader.cwtv < 0x0214) { MemsetZero(m_MidiCfg.szMidiSFXExt); MemsetZero(m_MidiCfg.szMidiZXXExt); m_SongFlags.set(SONG_EMBEDMIDICFG); } if(file.ReadMagic("MODU")) { madeWithTracker = "BeRoTracker"; } // Read pattern names: "PNAM" FileReader patNames; if(file.ReadMagic("PNAM")) { patNames = file.GetChunk(file.ReadUint32LE()); } m_nChannels = GetModSpecifications().channelsMin; // Read channel names: "CNAM" if(file.ReadMagic("CNAM")) { FileReader chnNames = file.GetChunk(file.ReadUint32LE()); const CHANNELINDEX readChns = std::min(MAX_BASECHANNELS, static_cast<CHANNELINDEX>(chnNames.GetLength() / MAX_CHANNELNAME)); m_nChannels = readChns; for(CHANNELINDEX i = 0; i < readChns; i++) { chnNames.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[i].szName, MAX_CHANNELNAME); } } // Read mix plugins information if(file.CanRead(9)) { LoadMixPlugins(file); } // Read Song Message if(fileHeader.special & ITFileHeader::embedSongMessage) { if(fileHeader.msglength > 0 && file.Seek(fileHeader.msgoffset)) { // Generally, IT files should use CR for line endings. However, ChibiTracker uses LF. One could do... // if(itHeader.cwtv == 0x0214 && itHeader.cmwt == 0x0214 && itHeader.reserved == ITFileHeader::chibiMagic) --> Chibi detected. // But we'll just use autodetection here: songMessage.Read(file, fileHeader.msglength, SongMessage::leAutodetect); } } // Reading Instruments m_nInstruments = 0; if(fileHeader.flags & ITFileHeader::instrumentMode) { m_nInstruments = std::min(fileHeader.insnum, INSTRUMENTINDEX(MAX_INSTRUMENTS - 1)); } for(INSTRUMENTINDEX i = 0; i < GetNumInstruments(); i++) { if(insPos[i] > 0 && file.Seek(insPos[i]) && file.CanRead(fileHeader.cmwt < 0x200 ? sizeof(ITOldInstrument) : sizeof(ITInstrument))) { ModInstrument *instrument = AllocateInstrument(i + 1); if(instrument != nullptr) { ITInstrToMPT(file, *instrument, fileHeader.cmwt); // MIDI Pitch Wheel Depth is a global setting in IT. Apply it to all instruments. instrument->midiPWD = fileHeader.pwd; } } } // In order to properly compute the position, in file, of eventual extended settings // such as "attack" we need to keep the "real" size of the last sample as those extra // setting will follow this sample in the file FileReader::off_t lastSampleOffset = 0; if(fileHeader.smpnum > 0) { lastSampleOffset = smpPos[fileHeader.smpnum - 1] + sizeof(ITSample); } //// #ITQ // Reading Samples m_nSamples = std::min(fileHeader.smpnum, SAMPLEINDEX(MAX_SAMPLES - 1)); size_t nbytes = 0; // size of sample data in file for(SAMPLEINDEX i = 0; i < GetNumSamples(); i++) { ITQSample sampleHeader; if(smpPos[i] > 0 && file.Seek(smpPos[i]) && file.ReadConvertEndianness(sampleHeader)) { if(!memcmp(sampleHeader.id, "ITQS", 4)) { size_t sampleOffset = sampleHeader.ConvertToMPT(Samples[i + 1]); mpt::String::Read<mpt::String::spacePadded>(m_szNames[i + 1], sampleHeader.name); if((loadFlags & loadSampleData) && file.Seek(sampleOffset)) { Samples[i+1].originalSize = sampleHeader.nbytes; sampleHeader.GetSampleFormatITQ(fileHeader.cwtv).ReadSample(Samples[i + 1], file); lastSampleOffset = std::max(lastSampleOffset, file.GetPosition()); } } } } m_nSamples = std::max(SAMPLEINDEX(1), GetNumSamples()); m_nMinPeriod = 8; m_nMaxPeriod = 0xF000; PATTERNINDEX numPats = std::min(static_cast<PATTERNINDEX>(patPos.size()), GetModSpecifications().patternsMax); if(numPats != patPos.size()) { // Hack: Notify user here if file contains more patterns than what can be read. AddToLog(mpt::String::Print(str_PatternSetTruncationNote, patPos.size(), numPats)); } if(!(loadFlags & loadPatternData)) { numPats = 0; } // Checking for number of used channels, which is not explicitely specified in the file. for(PATTERNINDEX pat = 0; pat < numPats; pat++) { if(patPos[pat] == 0 || !file.Seek(patPos[pat])) continue; uint16 len = file.ReadUint16LE(); ROWINDEX numRows = file.ReadUint16LE(); if(numRows < GetModSpecifications().patternRowsMin || numRows > GetModSpecifications().patternRowsMax || !file.Skip(4)) continue; FileReader patternData = file.GetChunk(len); ROWINDEX row = 0; std::vector<uint8> chnMask(GetNumChannels()); while(row < numRows && patternData.AreBytesLeft()) { uint8 b = patternData.ReadUint8(); if(!b) { row++; continue; } CHANNELINDEX ch = (b & IT_bitmask_patternChanField_c); // 0x7f We have some data grab a byte keeping only 7 bits if(ch) { ch = (ch - 1);// & IT_bitmask_patternChanMask_c; // 0x3f mask of the byte again, keeping only 6 bits } if(ch >= chnMask.size()) { chnMask.resize(ch + 1, 0); } if(b & IT_bitmask_patternChanEnabled_c) // 0x80 check if the upper bit is enabled. { chnMask[ch] = patternData.ReadUint8(); // set the channel mask for this channel. } // Channel used if(chnMask[ch] & 0x0F) // if this channel is used set m_nChannels { if(ch >= GetNumChannels() && ch < MAX_BASECHANNELS) { m_nChannels = ch + 1; } } // Now we actually update the pattern-row entry the note,instrument etc. // Note if(chnMask[ch] & 1) patternData.Skip(1); // Instrument if(chnMask[ch] & 2) patternData.Skip(1); // Volume if(chnMask[ch] & 4) patternData.Skip(1); // Effect if(chnMask[ch] & 8) patternData.Skip(2); } } // Compute extra instruments settings position if(lastSampleOffset > 0) { file.Seek(lastSampleOffset); } // Load instrument and song extensions. LoadExtendedInstrumentProperties(file, &interpretModPlugMade); if(interpretModPlugMade) { m_nMixLevels = mixLevels_original; } // We need to do this here, because if there no samples (so lastSampleOffset = 0), we need to look after the last pattern (sample data normally follows pattern data). // And we need to do this before reading the patterns because m_nChannels might be modified by LoadExtendedSongProperties. *sigh* LoadExtendedSongProperties(GetType(), file, &interpretModPlugMade); m_nTempoMode = tempo_mode_modern; // Reading Patterns Patterns.ResizeArray(std::max(MAX_PATTERNS, numPats)); for(PATTERNINDEX pat = 0; pat < numPats; pat++) { if(patPos[pat] == 0 || !file.Seek(patPos[pat])) { // Empty 64-row pattern if(Patterns.Insert(pat, 64)) { AddToLog(mpt::String::Print("Allocating patterns failed starting from pattern %1", pat)); break; } // Now (after the Insert() call), we can read the pattern name. CopyPatternName(Patterns[pat], patNames); continue; } uint16 len = file.ReadUint16LE(); ROWINDEX numRows = file.ReadUint16LE(); if(numRows < GetModSpecifications().patternRowsMin || numRows > GetModSpecifications().patternRowsMax || !file.Skip(4) || Patterns.Insert(pat, numRows)) continue; FileReader patternData = file.GetChunk(len); // Now (after the Insert() call), we can read the pattern name. CopyPatternName(Patterns[pat], patNames); std::vector<uint8> chnMask(GetNumChannels()); std::vector<ModCommand> lastValue(GetNumChannels(), ModCommand::Empty()); ModCommand *m = Patterns[pat]; ROWINDEX row = 0; while(row < numRows && patternData.AreBytesLeft()) { uint8 b = patternData.ReadUint8(); if(!b) { row++; m += GetNumChannels(); continue; } CHANNELINDEX ch = b & IT_bitmask_patternChanField_c; // 0x7f if(ch) { ch = (ch - 1); //& IT_bitmask_patternChanMask_c; // 0x3f } if(ch >= chnMask.size()) { chnMask.resize(ch + 1, 0); lastValue.resize(ch + 1, ModCommand::Empty()); ASSERT(chnMask.size() <= GetNumChannels()); } if(b & IT_bitmask_patternChanEnabled_c) // 0x80 { chnMask[ch] = patternData.ReadUint8(); } // Now we grab the data for this particular row/channel. if((chnMask[ch] & 0x10) && (ch < m_nChannels)) { m[ch].note = lastValue[ch].note; } if((chnMask[ch] & 0x20) && (ch < m_nChannels)) { m[ch].instr = lastValue[ch].instr; } if((chnMask[ch] & 0x40) && (ch < m_nChannels)) { m[ch].volcmd = lastValue[ch].volcmd; m[ch].vol = lastValue[ch].vol; } if((chnMask[ch] & 0x80) && (ch < m_nChannels)) { m[ch].command = lastValue[ch].command; m[ch].param = lastValue[ch].param; } if(chnMask[ch] & 1) // Note { uint8 note = patternData.ReadUint8(); if(ch < m_nChannels) { if(note < 0x80) note++; if(!(GetType() & MOD_TYPE_MPT)) { if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE; else if(note == 0xFD) note = NOTE_NONE; } m[ch].note = note; lastValue[ch].note = note; } } if(chnMask[ch] & 2) { uint8 instr = patternData.ReadUint8(); if(ch < m_nChannels) { m[ch].instr = instr; lastValue[ch].instr = instr; } } if(chnMask[ch] & 4) { uint8 vol = patternData.ReadUint8(); if(ch < m_nChannels) { // 0-64: Set Volume if(vol <= 64) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = vol; } else // 128-192: Set Panning if(vol >= 128 && vol <= 192) { m[ch].volcmd = VOLCMD_PANNING; m[ch].vol = vol - 128; } else // 65-74: Fine Volume Up if(vol < 75) { m[ch].volcmd = VOLCMD_FINEVOLUP; m[ch].vol = vol - 65; } else // 75-84: Fine Volume Down if(vol < 85) { m[ch].volcmd = VOLCMD_FINEVOLDOWN; m[ch].vol = vol - 75; } else // 85-94: Volume Slide Up if(vol < 95) { m[ch].volcmd = VOLCMD_VOLSLIDEUP; m[ch].vol = vol - 85; } else // 95-104: Volume Slide Down if(vol < 105) { m[ch].volcmd = VOLCMD_VOLSLIDEDOWN; m[ch].vol = vol - 95; } else // 105-114: Pitch Slide Up if(vol < 115) { m[ch].volcmd = VOLCMD_PORTADOWN; m[ch].vol = vol - 105; } else // 115-124: Pitch Slide Down if(vol < 125) { m[ch].volcmd = VOLCMD_PORTAUP; m[ch].vol = vol - 115; } else // 193-202: Portamento To if(vol >= 193 && vol <= 202) { m[ch].volcmd = VOLCMD_TONEPORTAMENTO; m[ch].vol = vol - 193; } else // 203-212: Vibrato depth if(vol >= 203 && vol <= 212) { m[ch].volcmd = VOLCMD_VIBRATODEPTH; m[ch].vol = vol - 203; // Old versions of ModPlug saved this as vibrato speed instead, so let's fix that. if(m[ch].vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 17, 02, 54)) m[ch].volcmd = VOLCMD_VIBRATOSPEED; } else // 213-222: Unused (was velocity) // 223-232: Offset if(vol >= 223 && vol <= 232) { m[ch].volcmd = VOLCMD_OFFSET; m[ch].vol = vol - 223; } lastValue[ch].volcmd = m[ch].volcmd; lastValue[ch].vol = m[ch].vol; } } // Reading command/param if(chnMask[ch] & 8) { uint8 cmd = patternData.ReadUint8(); uint8 param = patternData.ReadUint8(); if(ch < m_nChannels) { if(cmd) { m[ch].command = cmd; m[ch].param = param; S3MConvert(m[ch], true); lastValue[ch].command = m[ch].command; lastValue[ch].param = m[ch].param; } } } } } UpgradeModFlags(); if(!m_dwLastSavedWithVersion && fileHeader.cwtv == 0x0888) { // There are some files with OpenMPT extensions, but the "last saved with" field contains 0. // Was there an OpenMPT version that wrote 0 there, or are they hacked? m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); } if(m_dwLastSavedWithVersion && madeWithTracker.empty()) { madeWithTracker = "OpenMPT " + MptVersion::ToStr(m_dwLastSavedWithVersion); if(memcmp(fileHeader.reserved, "OMPT", 4) && (fileHeader.cwtv & 0xF000) == 0x5000) { madeWithTracker += " (compatibility export)"; } else if(MptVersion::IsTestBuild(m_dwLastSavedWithVersion)) { madeWithTracker += " (test build)"; } } else { switch(fileHeader.cwtv >> 12) { case 0: if(!madeWithTracker.empty()) { // BeRoTracker has been detected above. } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.flags == 9 && fileHeader.special == 0 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.insnum == 0 && fileHeader.patnum + 1 == fileHeader.ordnum && fileHeader.globalvol == 128 && fileHeader.mv == 100 && fileHeader.speed == 1 && fileHeader.sep == 128 && fileHeader.pwd == 0 && fileHeader.msglength == 0 && fileHeader.msgoffset == 0 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { madeWithTracker = "OpenSPC conversion"; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && !memcmp(fileHeader.reserved, "\0\0\0\0", 4)) { // ModPlug Tracker 1.00a5, instruments 560 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); madeWithTracker = "ModPlug tracker 1.00a5"; interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(fileHeader.reserved, "CHBI", 4)) { madeWithTracker = "ChibiTracker"; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !(fileHeader.special & 3) && !memcmp(fileHeader.reserved, "\0\0\0\0", 4) && !strcmp(Samples[1].filename, "XXXXXXXX.YYY")) { madeWithTracker = "CheeseTracker"; } else { if(fileHeader.cmwt > 0x0214) { madeWithTracker = "Impulse Tracker 2.15"; } else if(fileHeader.cwtv > 0x0214) { // Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3) // p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change // anything as far as file saving is concerned. madeWithTracker = mpt::String::Print("Impulse Tracker 2.14p%1", fileHeader.cwtv - 0x0214); } else { madeWithTracker = mpt::String::Print("Impulse Tracker %1.%2", (fileHeader.cwtv & 0x0F00) >> 8, mpt::fmt::hex0<2>((fileHeader.cwtv & 0xFF))); } } break; case 1: madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv); break; case 6: madeWithTracker = "BeRoTracker"; break; case 7: madeWithTracker = mpt::String::Print("ITMCK %1.%2.%3", (fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F); break; } } if(GetType() == MOD_TYPE_IT) { // Set appropriate mod flags if the file was not made with MPT. if(!interpretModPlugMade) { SetModFlag(MSF_MIDICC_BUGEMULATION, false); SetModFlag(MSF_OLDVOLSWING, false); SetModFlag(MSF_COMPATIBLE_PLAY, true); } } else { //START - mpt specific: //Using member cwtv on pifh as the version number. const uint16 version = fileHeader.cwtv; if(version > 0x889 && file.Seek(mptStartPos)) { std::istringstream iStrm(std::string(file.GetRawData(), file.BytesLeft())); if(version >= 0x88D) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead("mptm", MptVersion::num); ssb.ReadItem(GetTuneSpecificTunings(), "0", 1, &ReadTuningCollection); ssb.ReadItem(*this, "1", 1, &ReadTuningMap); ssb.ReadItem(Order, "2", 1, &ReadModSequenceOld); ssb.ReadItem(Patterns, FileIdPatterns, strlen(FileIdPatterns), &ReadModPatterns); ssb.ReadItem(Order, FileIdSequences, strlen(FileIdSequences), &ReadModSequences); if(ssb.m_Status & srlztn::SNT_FAILURE) { AddToLog("Unknown error occured while deserializing file."); } } else //Loading for older files. { if(GetTuneSpecificTunings().Deserialize(iStrm)) { AddToLog("Error occured - loading failed while trying to load tune specific tunings."); } else { ReadTuningMap(iStrm, *this); } } } //version condition(MPT) } return true; }