Cog/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp
Christopher Snowhill da1973bcd9 Build libOpenMPT from source once again
Bundle libOpenMPT as a dynamic framework, which should be safe once
again, now that there is only one version to bundle. Also, now it is
using the versions of libvorbisfile and libmpg123 that are bundled with
the player, instead of compiling minimp3 and stbvorbis.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-30 22:56:52 -07:00

721 lines
19 KiB
C++

/*
* serialization_utils.cpp
* -----------------------
* Purpose: Serializing data to and from MPTM files.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "serialization_utils.h"
#include "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#include <array>
#include <istream>
#include <ostream>
#include <sstream>
#include "misc_util.h"
OPENMPT_NAMESPACE_BEGIN
namespace srlztn
{
#ifdef MPT_ALL_LOGGING
#define SSB_LOGGING
#endif
#ifdef SSB_LOGGING
#define SSB_LOG(x) MPT_LOG_GLOBAL(LogDebug, "serialization", x)
#else
#define SSB_LOG(x) do { } while(0)
#endif
static const uint8 HeaderId_FlagByte = 0;
// Indexing starts from 0.
static inline bool Testbit(uint8 val, uint8 bitindex) {return ((val & (1 << bitindex)) != 0);}
static inline void Setbit(uint8& val, uint8 bitindex, bool newval)
{
if(newval) val |= (1 << bitindex);
else val &= ~(1 << bitindex);
}
bool ID::IsPrintable() const
{
for(std::size_t i = 0; i < m_ID.length(); ++i)
{
if(m_ID[i] <= 0 || isprint(static_cast<unsigned char>(m_ID[i])) == 0)
{
return false;
}
}
return true;
}
//Format: First bit tells whether the size indicator is 1 or 2 bytes.
static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str)
{
uint16 s = static_cast<uint16>(str.size());
LimitMax(s, uint16(std::numeric_limits<uint16>::max() / 2));
mpt::IO::WriteAdaptiveInt16LE(oStrm, s);
oStrm.write(str.c_str(), s);
}
void WriteItemString(std::ostream& oStrm, const std::string &str)
{
uint32 id = static_cast<uint32>(std::min(str.size(), static_cast<std::size_t>((std::numeric_limits<uint32>::max() >> 4)))) << 4;
id |= 12; // 12 == 1100b
Binarywrite<uint32>(oStrm, id);
id >>= 4;
if(id > 0)
oStrm.write(str.data(), id);
}
void ReadItemString(std::istream& iStrm, std::string& str, const DataSize)
{
// bits 0,1: Bytes per char type: 1,2,3,4.
// bits 2,3: Bytes in size indicator, 1,2,3,4
uint32 id = 0;
Binaryread(iStrm, id, 1);
const uint8 nSizeBytes = (id & 12) >> 2; // 12 == 1100b
if (nSizeBytes > 0)
{
uint8 bytes = std::min(uint8(3), nSizeBytes);
uint8 v2 = 0;
uint8 v3 = 0;
uint8 v4 = 0;
if(bytes >= 1) Binaryread(iStrm, v2);
if(bytes >= 2) Binaryread(iStrm, v3);
if(bytes >= 3) Binaryread(iStrm, v4);
id &= 0xff;
id |= (v2 << 8) | (v3 << 16) | (v4 << 24);
}
// Limit to 1 MB.
str.resize(std::min(id >> 4, uint32(1000000)));
for(size_t i = 0; i < str.size(); i++)
iStrm.read(&str[i], 1);
id = (id >> 4) - static_cast<uint32>(str.size());
if(id > 0)
iStrm.ignore(id);
}
mpt::ustring ID::AsString() const
{
if(IsPrintable())
{
return mpt::ToUnicode(mpt::Charset::ISO8859_1, m_ID);
}
if(m_ID.length() > 8)
{
return mpt::ustring();
}
uint64le val;
val.set(0);
std::memcpy(&val, m_ID.data(), m_ID.length());
return mpt::ufmt::val(val);
}
const char Ssb::s_EntryID[3] = {'2','2','8'};
Ssb::Ssb()
: m_Status(SNT_NONE)
, m_nFixedEntrySize(0)
, m_posStart(0)
, m_nIdbytes(IdSizeVariable)
, m_nCounter(0)
, m_Flags((1 << RwfWMapStartPosEntry) + (1 << RwfWMapSizeEntry) + (1 << RwfWVersionNum))
{
return;
}
SsbWrite::SsbWrite(std::ostream& os)
: oStrm(os)
, m_posEntrycount(0)
, m_posMapPosField(0)
{
return;
}
SsbRead::SsbRead(std::istream& is)
: iStrm(is)
, m_nReadVersion(0)
, m_rposMapBegin(0)
, m_posMapEnd(0)
, m_posDataBegin(0)
, m_rposEndofHdrData(0)
, m_nReadEntrycount(0)
, m_nNextReadHint(0)
{
return;
}
void SsbWrite::AddWriteNote(const SsbStatus s)
{
m_Status |= s;
SSB_LOG(MPT_UFORMAT("{}: 0x{}")(U_("Write note: "), mpt::ufmt::hex(s)));
}
void SsbRead::AddReadNote(const SsbStatus s)
{
m_Status |= s;
SSB_LOG(MPT_UFORMAT("{}: 0x{}")(U_("Read note: "), mpt::ufmt::hex(s)));
}
void SsbRead::AddReadNote(const ReadEntry* const pRe, const NumType nNum)
{
m_Status |= SNT_PROGRESS;
SSB_LOG(MPT_UFORMAT("Read entry: {{num, id, rpos, size, desc}} = {{{}, {}, {}, {}, {}}}")(
nNum,
(pRe && pRe->nIdLength < 30 && m_Idarray.size() > 0) ? ID(&m_Idarray[pRe->nIdpos], pRe->nIdLength).AsString() : U_(""),
(pRe) ? pRe->rposStart : 0,
(pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : U_(""),
U_("")));
#ifndef SSB_LOGGING
MPT_UNREFERENCED_PARAMETER(pRe);
MPT_UNREFERENCED_PARAMETER(nNum);
#endif
}
// Called after writing an entry.
void SsbWrite::AddWriteNote(const ID &id, const NumType nEntryNum, const DataSize nBytecount, const RposType rposStart)
{
m_Status |= SNT_PROGRESS;
SSB_LOG(MPT_UFORMAT("Wrote entry: {{num, id, rpos, size}} = {{{}, {}, {}, {}}}")(nEntryNum, id.AsString(), rposStart, nBytecount));
#ifndef SSB_LOGGING
MPT_UNREFERENCED_PARAMETER(id);
MPT_UNREFERENCED_PARAMETER(nEntryNum);
MPT_UNREFERENCED_PARAMETER(nBytecount);
MPT_UNREFERENCED_PARAMETER(rposStart);
#endif
}
void SsbRead::ResetReadstatus()
{
m_Status = SNT_NONE;
m_Idarray.reserve(32);
m_Idarray.push_back(0);
}
void SsbWrite::WriteMapItem(const ID &id,
const RposType& rposDataStart,
const DataSize& nDatasize,
const char* pszDesc)
{
SSB_LOG(MPT_UFORMAT("Writing map entry: id={}, rpos={}, size={}")(
(id.GetSize() > 0) ? id.AsString() : U_(""),
rposDataStart,
nDatasize));
std::ostringstream mapStream;
if(m_nIdbytes > 0)
{
if (m_nIdbytes != IdSizeVariable && id.GetSize() != m_nIdbytes)
{ AddWriteNote(SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING); return; }
if (m_nIdbytes == IdSizeVariable) //Variablesize ID?
mpt::IO::WriteAdaptiveInt16LE(mapStream, static_cast<uint16>(id.GetSize()));
if(id.GetSize() > 0)
mapStream.write(id.GetBytes(), id.GetSize());
}
if (GetFlag(RwfWMapStartPosEntry)) //Startpos
mpt::IO::WriteAdaptiveInt64LE(mapStream, rposDataStart);
if (GetFlag(RwfWMapSizeEntry)) //Entrysize
mpt::IO::WriteAdaptiveInt64LE(mapStream, nDatasize);
if (GetFlag(RwfWMapDescEntry)) //Entry descriptions
WriteAdaptive12String(mapStream, std::string(pszDesc));
m_MapStreamString.append(mapStream.str());
}
void SsbWrite::IncrementWriteCounter()
{
m_nCounter++;
if(m_nCounter >= static_cast<uint16>(std::numeric_limits<uint16>::max() >> 2))
{
FinishWrite();
AddWriteNote(SNW_MAX_WRITE_COUNT_REACHED);
}
}
void SsbWrite::BeginWrite(const ID &id, const uint64& nVersion)
{
SSB_LOG(MPT_UFORMAT("Write header with ID = {}")(id.AsString()));
ResetWritestatus();
if(!oStrm.good())
{ AddWriteNote(SNRW_BADGIVEN_STREAM); return; }
// Start bytes.
oStrm.write(s_EntryID, sizeof(s_EntryID));
m_posStart = oStrm.tellp() - Offtype(sizeof(s_EntryID));
// Object ID.
{
uint8 idsize = static_cast<uint8>(id.GetSize());
Binarywrite<uint8>(oStrm, idsize);
if(idsize > 0) oStrm.write(id.GetBytes(), id.GetSize());
}
// Form header.
uint8 header = 0;
SetFlag(RwfWMapStartPosEntry, GetFlag(RwfWMapStartPosEntry) && m_nFixedEntrySize == 0);
SetFlag(RwfWMapSizeEntry, GetFlag(RwfWMapSizeEntry) && m_nFixedEntrySize == 0);
header = (m_nIdbytes != 4) ? (m_nIdbytes & 3) : 3; //0,1 : Bytes per IDtype, 0,1,2,4
Setbit(header, 2, GetFlag(RwfWMapStartPosEntry)); //2 : Startpos in map?
Setbit(header, 3, GetFlag(RwfWMapSizeEntry)); //3 : Datasize in map?
Setbit(header, 4, GetFlag(RwfWVersionNum)); //4 : Version numeric field?
Setbit(header, 7, GetFlag(RwfWMapDescEntry)); //7 : Entrydescriptions in map?
// Write header
Binarywrite<uint8>(oStrm, header);
// Additional options.
uint8 tempU8 = 0;
Setbit(tempU8, 0, (m_nIdbytes == IdSizeVariable) || (m_nIdbytes == 3) || (m_nIdbytes > 4));
Setbit(tempU8, 1, m_nFixedEntrySize != 0);
const uint8 flags = tempU8;
if(flags != s_DefaultFlagbyte)
{
mpt::IO::WriteAdaptiveInt32LE(oStrm, 2); //Headersize - now it is 2.
Binarywrite<uint8>(oStrm, HeaderId_FlagByte);
Binarywrite<uint8>(oStrm, flags);
}
else
mpt::IO::WriteAdaptiveInt32LE(oStrm, 0);
if(Testbit(header, 4)) // Version(numeric)?
mpt::IO::WriteAdaptiveInt64LE(oStrm, nVersion);
if(Testbit(flags, 0)) // Custom IDbytecount?
{
uint8 n = (m_nIdbytes == IdSizeVariable) ? 1 : static_cast<uint8>((m_nIdbytes << 1));
Binarywrite<uint8>(oStrm, n);
}
if(Testbit(flags, 1)) // Fixedsize entries?
mpt::IO::WriteAdaptiveInt32LE(oStrm, m_nFixedEntrySize);
//Entrycount. Reserve two bytes(max uint16_max / 4 entries), actual value is written after writing data.
m_posEntrycount = oStrm.tellp();
Binarywrite<uint16>(oStrm, 0);
SetFlag(RwfRwHasMap, (m_nIdbytes != 0 || GetFlag(RwfWMapStartPosEntry) || GetFlag(RwfWMapSizeEntry) || GetFlag(RwfWMapDescEntry)));
m_posMapPosField = oStrm.tellp();
if (GetFlag(RwfRwHasMap)) //Mapping begin pos(reserve space - actual value is written after writing data)
Binarywrite<uint64>(oStrm, 0);
}
SsbRead::ReadRv SsbRead::OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin)
{
if (pE != nullptr)
AddReadNote(pE, m_nCounter);
else if (GetFlag(RwfRMapHasId) == false) // Not ID's in map.
{
ReadEntry e;
e.rposStart = static_cast<RposType>(posReadBegin - m_posStart);
e.nSize = static_cast<DataSize>(iStrm.tellg() - posReadBegin);
AddReadNote(&e, m_nCounter);
}
else // Entry not found.
{
SSB_LOG(MPT_UFORMAT("No entry with id {} found.")(id.AsString()));
#ifndef SSB_LOGGING
MPT_UNREFERENCED_PARAMETER(id);
#endif
return EntryNotFound;
}
m_nCounter++;
return EntryRead;
}
void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite)
{
const Offtype nRawEntrySize = oStrm.tellp() - posBeforeWrite;
MPT_MAYBE_CONSTANT_IF(nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > std::numeric_limits<DataSize>::max())
{
AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE);
return;
}
if(GetFlag(RwfRMapHasSize) && (nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > (std::numeric_limits<DataSize>::max() >> 2)))
{ AddWriteNote(SNW_DATASIZETYPE_OVERFLOW); return; }
DataSize nEntrySize = static_cast<DataSize>(nRawEntrySize);
// Handle fixed size entries:
if (m_nFixedEntrySize > 0)
{
if(nEntrySize <= m_nFixedEntrySize)
{
for(uint32 i = 0; i<m_nFixedEntrySize-nEntrySize; i++)
oStrm.put(0);
nEntrySize = m_nFixedEntrySize;
}
else
{ AddWriteNote(SNW_INSUFFICIENT_FIXEDSIZE); return; }
}
if (GetFlag(RwfRwHasMap))
WriteMapItem(id, static_cast<RposType>(posBeforeWrite - m_posStart), nEntrySize, "");
AddWriteNote(id, m_nCounter, nEntrySize, static_cast<RposType>(posBeforeWrite - m_posStart));
IncrementWriteCounter();
}
void SsbRead::BeginRead(const ID &id, const uint64& nVersion)
{
SSB_LOG(MPT_UFORMAT("Read header with expected ID = {}")(id.AsString()));
ResetReadstatus();
if (!iStrm.good())
{ AddReadNote(SNRW_BADGIVEN_STREAM); return; }
m_posStart = iStrm.tellg();
// Start bytes.
{
char temp[sizeof(s_EntryID)];
ArrayReader<char>(sizeof(s_EntryID))(iStrm, temp, sizeof(s_EntryID));
if(std::memcmp(temp, s_EntryID, sizeof(s_EntryID)))
{
AddReadNote(SNR_STARTBYTE_MISMATCH);
return;
}
}
// Compare IDs.
uint8 storedIdLen = 0;
Binaryread<uint8>(iStrm, storedIdLen);
std::array<char, 256> storedIdBuf;
storedIdBuf = {};
if(storedIdLen > 0)
{
iStrm.read(storedIdBuf.data(), storedIdLen);
}
if(!(id == ID(storedIdBuf.data(), storedIdLen)))
{
AddReadNote(SNR_OBJECTCLASS_IDMISMATCH);
}
if ((m_Status & SNT_FAILURE) != 0)
{
SSB_LOG(U_("ID mismatch, terminating read."));
return;
}
SSB_LOG(U_("ID match, continuing reading."));
// Header
uint8 tempU8;
Binaryread<uint8>(iStrm, tempU8);
const uint8 header = tempU8;
m_nIdbytes = ((header & 3) == 3) ? 4 : (header & 3);
if (Testbit(header, 6))
SetFlag(RwfRTwoBytesDescChar, true);
// Read headerdata size
uint32 tempU32 = 0;
mpt::IO::ReadAdaptiveInt32LE(iStrm, tempU32);
const uint32 headerdatasize = tempU32;
// If headerdatasize != 0, read known headerdata and ignore rest.
uint8 flagbyte = s_DefaultFlagbyte;
if(headerdatasize >= 2)
{
Binaryread<uint8>(iStrm, tempU8);
if(tempU8 == HeaderId_FlagByte)
Binaryread<uint8>(iStrm, flagbyte);
iStrm.ignore( (tempU8 == HeaderId_FlagByte) ? headerdatasize - 2 : headerdatasize - 1);
}
uint64 tempU64 = 0;
// Read version numeric if available.
if (Testbit(header, 4))
{
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
m_nReadVersion = tempU64;
if(tempU64 > nVersion)
AddReadNote(SNR_LOADING_OBJECT_WITH_LARGER_VERSION);
}
if (Testbit(header, 5))
{
Binaryread<uint8>(iStrm, tempU8);
iStrm.ignore(tempU8);
}
if(Testbit(flagbyte, 0)) // Custom ID?
{
Binaryread<uint8>(iStrm, tempU8);
if ((tempU8 & 1) != 0)
m_nIdbytes = IdSizeVariable;
else
m_nIdbytes = (tempU8 >> 1);
if(m_nIdbytes == 0)
AddReadNote(SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED);
}
m_nFixedEntrySize = 0;
if(Testbit(flagbyte, 1)) // Fixedsize entries?
mpt::IO::ReadAdaptiveInt32LE(iStrm, m_nFixedEntrySize);
SetFlag(RwfRMapHasStartpos, Testbit(header, 2));
SetFlag(RwfRMapHasSize, Testbit(header, 3));
SetFlag(RwfRMapHasId, (m_nIdbytes > 0));
SetFlag(RwfRMapHasDesc, Testbit(header, 7));
SetFlag(RwfRwHasMap, GetFlag(RwfRMapHasId) || GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || GetFlag(RwfRMapHasDesc));
if (GetFlag(RwfRwHasMap) == false)
{
SSB_LOG(U_("No map in the file."));
}
if (Testbit(flagbyte, 2)) // Object description?
{
uint16 size = 0;
mpt::IO::ReadAdaptiveInt16LE(iStrm, size);
iStrm.ignore(size * (GetFlag(RwfRTwoBytesDescChar) ? 2 : 1));
}
if(Testbit(flagbyte, 3))
iStrm.ignore(5);
// Read entrycount
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > 16000)
// The current code can only write 16383 entries because it uses a Adaptive64LE with a fixed size=2
// Additionally, 16000 is an arbitrary limit to avoid an out-of-memory DoS when caching the map.
{ AddReadNote(SNR_TOO_MANY_ENTRIES_TO_READ); return; }
m_nReadEntrycount = static_cast<NumType>(tempU64);
if(m_nReadEntrycount == 0)
AddReadNote(SNR_ZEROENTRYCOUNT);
// Read map rpos if map exists.
if (GetFlag(RwfRwHasMap))
{
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > static_cast<uint64>(std::numeric_limits<Offtype>::max()))
{ AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; }
}
const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart;
MPT_MAYBE_CONSTANT_IF(rawEndOfHdrData < 0 || static_cast<uint64>(rawEndOfHdrData) > std::numeric_limits<RposType>::max())
{
AddReadNote(SNR_INSUFFICIENT_RPOSTYPE);
return;
}
m_rposEndofHdrData = static_cast<RposType>(rawEndOfHdrData);
m_rposMapBegin = (GetFlag(RwfRwHasMap)) ? static_cast<RposType>(tempU64) : m_rposEndofHdrData;
if (GetFlag(RwfRwHasMap) == false)
m_posMapEnd = m_posStart + m_rposEndofHdrData;
SetFlag(RwfRHeaderIsRead, true);
}
void SsbRead::CacheMap()
{
if(GetFlag(RwfRwHasMap) || m_nFixedEntrySize > 0)
{
iStrm.seekg(m_posStart + m_rposMapBegin);
if(iStrm.fail())
{ AddReadNote(SNR_BADSTREAM_AFTER_MAPHEADERSEEK); return; }
SSB_LOG(MPT_UFORMAT("Reading map from rpos: {}")(m_rposMapBegin));
mapData.resize(m_nReadEntrycount);
m_Idarray.reserve(m_nReadEntrycount * 4);
//Read map
for(NumType i = 0; i<m_nReadEntrycount; i++)
{
if(iStrm.fail())
{ AddReadNote(SNR_BADSTREAM_AT_MAP_READ); return; }
// Read ID.
uint16 nIdsize = m_nIdbytes;
if(nIdsize == IdSizeVariable) //Variablesize ID
mpt::IO::ReadAdaptiveInt16LE(iStrm, nIdsize);
const size_t nOldEnd = m_Idarray.size();
if (nIdsize > 0 && (Util::MaxValueOfType(nOldEnd) - nOldEnd >= nIdsize))
{
m_Idarray.resize(nOldEnd + nIdsize);
iStrm.read(&m_Idarray[nOldEnd], nIdsize);
}
mapData[i].nIdLength = nIdsize;
mapData[i].nIdpos = nOldEnd;
// Read position.
if(GetFlag(RwfRMapHasStartpos))
{
uint64 tempU64;
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > static_cast<uint64>(std::numeric_limits<Offtype>::max()))
{ AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; }
mapData[i].rposStart = static_cast<RposType>(tempU64);
}
// Read entry size.
if (m_nFixedEntrySize > 0)
mapData[i].nSize = m_nFixedEntrySize;
else if(GetFlag(RwfRMapHasSize)) // Map has datasize field.
{
uint64 tempU64;
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > static_cast<uint64>(std::numeric_limits<Offtype>::max()))
{ AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; }
mapData[i].nSize = static_cast<DataSize>(tempU64);
}
// If there's no entry startpos in map, count start pos from datasizes.
// Here readentry.rposStart is set to relative position from databegin.
if (mapData[i].nSize != invalidDatasize && GetFlag(RwfRMapHasStartpos) == false)
mapData[i].rposStart = (i > 0) ? mapData[i-1].rposStart + mapData[i-1].nSize : 0;
if(GetFlag(RwfRMapHasDesc)) //Map has entrydescriptions?
{
uint16 size = 0;
mpt::IO::ReadAdaptiveInt16LE(iStrm, size);
if(GetFlag(RwfRTwoBytesDescChar))
iStrm.ignore(size * 2);
else
iStrm.ignore(size);
}
}
m_posMapEnd = iStrm.tellg();
SSB_LOG(MPT_UFORMAT("End of map(rpos): {}")(m_posMapEnd - m_posStart));
}
SetFlag(RwfRMapCached, true);
m_posDataBegin = (m_rposMapBegin == m_rposEndofHdrData) ? m_posMapEnd : m_posStart + Postype(m_rposEndofHdrData);
iStrm.seekg(m_posDataBegin);
// If there are no positions in the map but there are entry sizes, rposStart will
// be relative to data start. Now that posDataBegin is known, make them relative to
// startpos.
if (GetFlag(RwfRMapHasStartpos) == false && (GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0))
{
const RposType offset = static_cast<RposType>(m_posDataBegin - m_posStart);
for(size_t i = 0; i < m_nReadEntrycount; i++)
mapData[i].rposStart += offset;
}
}
const ReadEntry* SsbRead::Find(const ID &id)
{
iStrm.clear();
if (GetFlag(RwfRMapCached) == false)
CacheMap();
if (m_nFixedEntrySize > 0 && GetFlag(RwfRMapHasStartpos) == false && GetFlag(RwfRMapHasSize) == false)
iStrm.seekg(m_posDataBegin + Postype(m_nFixedEntrySize * m_nCounter));
if (GetFlag(RwfRMapHasId) == true)
{
const size_t nEntries = mapData.size();
for(size_t i0 = 0; i0 < nEntries; i0++)
{
const size_t i = (i0 + m_nNextReadHint) % nEntries;
if(mapData[i].nIdpos < m_Idarray.size() && id == ID(&m_Idarray[mapData[i].nIdpos], mapData[i].nIdLength))
{
m_nNextReadHint = (i + 1) % nEntries;
if (mapData[i].rposStart != 0)
iStrm.seekg(m_posStart + Postype(mapData[i].rposStart));
return &mapData[i];
}
}
}
return nullptr;
}
void SsbWrite::FinishWrite()
{
const Postype posDataEnd = oStrm.tellp();
Postype posMapStart = oStrm.tellp();
SSB_LOG(MPT_UFORMAT("Writing map to rpos: {}")(posMapStart - m_posStart));
if (GetFlag(RwfRwHasMap)) //Write map
{
oStrm.write(m_MapStreamString.c_str(), m_MapStreamString.length());
}
const Postype posMapEnd = oStrm.tellp();
// Write entry count.
oStrm.seekp(m_posEntrycount);
// Write a fixed size=2 Adaptive64LE because space for this value has already been reserved berforehand.
mpt::IO::WriteAdaptiveInt64LE(oStrm, m_nCounter, 2);
if (GetFlag(RwfRwHasMap))
{ // Write map start position.
oStrm.seekp(m_posMapPosField);
const uint64 rposMap = posMapStart - m_posStart;
// Write a fixed size=8 Adaptive64LE because space for this value has already been reserved berforehand.
mpt::IO::WriteAdaptiveInt64LE(oStrm, rposMap, 8);
}
// Seek to end.
oStrm.seekp(std::max(posMapEnd, posDataEnd));
SSB_LOG(MPT_UFORMAT("End of stream(rpos): {}")(oStrm.tellp() - m_posStart));
}
} // namespace srlztn
OPENMPT_NAMESPACE_END