public static void AssignPositions(IList <IResourceBlock> blocks, uint basePosition, out RpfResourcePageFlags pageFlags) { var sys = (basePosition == 0x50000000); long pad(long p) { return((ALIGN_SIZE - (p % ALIGN_SIZE)) % ALIGN_SIZE); } long largestBlockSize = 0; // find largest structure long startPageSize = BASE_SIZE; // 0x2000; // find starting page size long totalBlockSize = 0; foreach (var block in blocks) { var blockLength = block.BlockLength; totalBlockSize += blockLength; totalBlockSize += pad(totalBlockSize); if (largestBlockSize < blockLength) { largestBlockSize = blockLength; } } while (startPageSize < largestBlockSize) { startPageSize *= 2; } pageFlags = new RpfResourcePageFlags(); var pageSizeMult = 1; while (true) { if (blocks.Count == 0) { break; } var blockset = new ResourceBuilderBlockSet(blocks, sys); var rootblock = blockset.RootBlock; var currentPosition = 0L; var currentPageSize = startPageSize; var currentPageStart = 0L; var currentPageSpace = startPageSize; var currentRemainder = totalBlockSize; var pageCount = 1; var pageCounts = new uint[9]; var pageCountIndex = 0; var targetPageSize = Math.Max(65536 * pageSizeMult, startPageSize >> (sys ? 5 : 2)); var minPageSize = Math.Max(512 * pageSizeMult, Math.Min(targetPageSize, startPageSize) >> 4); var baseShift = 0u; var baseSize = 512; while (baseSize < minPageSize) { baseShift++; baseSize *= 2; if (baseShift >= 0xF) { break; } } var baseSizeMax = baseSize << 8; var baseSizeMaxTest = startPageSize; while (baseSizeMaxTest < baseSizeMax) { pageCountIndex++; baseSizeMaxTest *= 2; } pageCounts[pageCountIndex] = 1; while (true) { var isroot = sys && (currentPosition == 0); var block = isroot ? rootblock : blockset.TakeBestBlock(currentPageSpace); var blockLength = block?.Length ?? 0; if (block != null) { //add this block to the current page. block.Block.FilePosition = basePosition + currentPosition; var opos = currentPosition; currentPosition += blockLength; currentPosition += pad(currentPosition); var usedspace = currentPosition - opos; currentPageSpace -= usedspace; currentRemainder -= usedspace;//blockLength;// } else if (blockset.Count > 0) { //allocate a new page currentPageStart += currentPageSize; currentPosition = currentPageStart; block = blockset.FindBestBlock(long.MaxValue); //just find the biggest block blockLength = block?.Length ?? 0; while (blockLength <= (currentPageSize >> 1)) //determine best new page size { if (currentPageSize <= minPageSize) { break; } if (pageCountIndex >= 8) { break; } if ((currentPageSize <= targetPageSize) && (currentRemainder >= (currentPageSize - minPageSize))) { break; } currentPageSize = currentPageSize >> 1; pageCountIndex++; } currentPageSpace = currentPageSize; pageCounts[pageCountIndex]++; pageCount++; } else { break; } } pageFlags = new RpfResourcePageFlags(pageCounts, baseShift); if ((pageCount == pageFlags.Count) && (pageFlags.Size >= currentPosition)) //make sure page counts fit in the flags value { break; } startPageSize *= 2; pageSizeMult *= 2; } }
public static void AssignPositionsForMeta(IList <IResourceBlock> blocks, uint basePosition, out RpfResourcePageFlags pageFlags) { // find largest structure long largestBlockSize = 0; foreach (var block in blocks) { if (largestBlockSize < block.BlockLength) { largestBlockSize = block.BlockLength; } } // find minimum page size long currentPageSize = 0x2000; while (currentPageSize < largestBlockSize) { currentPageSize *= 2; } long currentPageCount; long currentPosition; while (true) { currentPageCount = 0; currentPosition = 0; // reset all positions foreach (var block in blocks) { block.FilePosition = -1; } foreach (var block in blocks) { if (block.FilePosition != -1) { throw new Exception("Block was already assigned a position!"); } // check if new page is necessary... // if yes, add a new page and align to it long maxSpace = currentPageCount * currentPageSize - currentPosition; if (maxSpace < (block.BlockLength + SKIP_SIZE)) { currentPageCount++; currentPosition = currentPageSize * (currentPageCount - 1); } // set position block.FilePosition = basePosition + currentPosition; currentPosition += block.BlockLength; // + SKIP_SIZE; //is padding everywhere really necessary?? // align... if ((currentPosition % ALIGN_SIZE) != 0) { currentPosition += (ALIGN_SIZE - (currentPosition % ALIGN_SIZE)); } } // break if everything fits... if (currentPageCount < 128) { break; } currentPageSize *= 2; } pageFlags = new RpfResourcePageFlags(RpfResourceFileEntry.GetFlagsFromBlocks((uint)currentPageCount, (uint)currentPageSize, 0)); }