//=============================================================================// // // Purpose: pak page allocation and alignment // //=============================================================================// #include "rtech/ipakfile.h" #include "pakstate.h" #include "pakalloc.h" //----------------------------------------------------------------------------- // aligns the segment headers for each asset type //----------------------------------------------------------------------------- void Pak_AlignSegmentHeaders(PakFile_t* const pak, PakSegmentDescriptor_t* const desc) { uint64_t headersSize = 0; uint8_t headerSegmentAlignment = static_cast(desc->segmentAlignmentForType[SF_HEAD]); for (uint8_t i = 0; i < PAK_MAX_TYPES; ++i) { const PakAssetBinding_t& binding = g_pPakGlobals->m_assetBindings[i]; if (desc->assetTypeCount[i]) { // asset header alignment really shouldn't be above 255 // if this needs raising, headerSegmentAlignment should be made wider assert(binding.headerAlignment <= UINT8_MAX); const size_t alignedSize = ALIGN_VALUE(headersSize, static_cast(binding.headerAlignment)); pak->memoryData.unkAssetTypeBindingSizes[i] = alignedSize; headersSize = alignedSize + (desc->assetTypeCount[i] * binding.nativeClassSize); desc->segmentSizeForType[SF_HEAD] = headersSize; headerSegmentAlignment = static_cast(max(headerSegmentAlignment, binding.headerAlignment)); desc->segmentAlignmentForType[SF_HEAD] = headerSegmentAlignment; } } } //----------------------------------------------------------------------------- // aligns each individual non-header segment //----------------------------------------------------------------------------- void Pak_AlignSegments(PakFile_t* const pak, PakSegmentDescriptor_t* const desc) { for (uint16_t i = 0; i < pak->GetSegmentCount(); ++i) { const PakSegmentHeader_t* const segHeader = pak->GetSegmentHeader(i); const uint8_t segmentType = segHeader->typeFlags & (SF_TEMP | SF_CPU); if (segmentType != SF_HEAD) // if not a header segment { // should this be a hard error on release? // segment alignment must not be 0 and must be a power of two assert(segHeader->dataAlignment > 0 && IsPowerOfTwo(segHeader->dataAlignment)); const size_t alignedSegmentSize = ALIGN_VALUE(desc->segmentSizeForType[segmentType], static_cast(segHeader->dataAlignment)); //const size_t sizeAligned = ~(m_align - 1) & (m_align - 1 + segmentSizeForType[segmentType]); desc->segmentSizes[i] = alignedSegmentSize; desc->segmentSizeForType[segmentType] = alignedSegmentSize + segHeader->dataSize; // check if this segment's alignment is higher than the previous highest for this type // if so, increase the alignment to accommodate this segment desc->segmentAlignmentForType[segmentType] = max(desc->segmentAlignmentForType[segmentType], segHeader->dataAlignment); } } } //----------------------------------------------------------------------------- // copy's pages into pre-allocated and aligned segments //----------------------------------------------------------------------------- void Pak_CopyPagesToSegments(PakFile_t* const pak, PakLoadedInfo_t* const loadedInfo, PakSegmentDescriptor_t* const desc) { for (uint32_t i = 0; i < pak->GetPageCount(); ++i) { const PakPageHeader_t* const pageHeader = pak->GetPageHeader(i); const uint32_t segmentIndex = pageHeader->segmentIdx; const PakSegmentHeader_t* const segHeader = pak->GetSegmentHeader(segmentIndex); const int typeFlags = segHeader->typeFlags; // check if header page if ((typeFlags & (SF_TEMP | SF_CPU)) != 0) { // align the segment's current size to the alignment of the new page to get copied in // this ensures that the location holding the page is aligned as required // // since the segment will always have alignment equal to or greater than the page, and that alignment will always be a power of 2 // the page does not have to be aligned to the same alignment as the segment, as aligning it to its own alignment is sufficient as long as // every subsequent page does the same thing const size_t alignedSegmentSize = ALIGN_VALUE(desc->segmentSizes[segmentIndex], static_cast(pageHeader->pageAlignment)); // get a pointer to the newly aligned location within the segment for this page pak->memoryData.memPageBuffers[i] = reinterpret_cast(loadedInfo->segmentBuffers[typeFlags & (SF_TEMP | SF_CPU)]) + alignedSegmentSize; // update the segment size to reflect the new alignment and page size desc->segmentSizes[segmentIndex] = alignedSegmentSize + pak->memoryData.pageHeaders[i].dataSize; } else { // all headers go into one segment and are dealt with separately in Pak_ProcessPakFile // since headers must be copied individually into a buffer that is big enough for the "native class" version of the header // instead of just the file version pak->memoryData.memPageBuffers[i] = reinterpret_cast(loadedInfo->segmentBuffers[SF_HEAD]); } } }