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;
            }
        }
Example #2
0
        public static void AssignPositions(IList <IResourceBlock> blocks, uint basePosition, out ResourceChunkFlags flags, uint usedPages)
        {
            flags = new ResourceChunkFlags();

            if (blocks.Count <= 0)
            {
                return;
            }

            long largestBlockSize = 0;         // find largest structure
            long startPageSize    = BASE_SIZE; // 0x2000; // find starting page size
            long totalBlockSize   = 0;

            foreach (var block in blocks)
            {
                // Get size of all blocks padded
                var blockLength = block.BlockLength;
                totalBlockSize += blockLength;
                totalBlockSize += Pad(totalBlockSize);

                // Get biggest block
                if (largestBlockSize < blockLength)
                {
                    largestBlockSize = blockLength;
                }
            }

            // Get minimum page size to contain biggest block
            while (startPageSize < largestBlockSize)
            {
                startPageSize *= 2;
            }

            var  pageSizeMult = 1;
            long currentPosition;

            var  sys = (basePosition == 0x50000000);
            bool invalidLayout;

            do
            {
                invalidLayout = false;
                var blockset  = new ResourceBuilderBlockSet(blocks, sys);
                var rootblock = blockset.RootBlock;
                currentPosition = 0L;
                var  currentPageSize  = startPageSize;
                var  currentPageStart = 0L;
                var  currentPageSpace = startPageSize;
                long currentRemainder = totalBlockSize;
                var  bucketIndex      = 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;
                    }
                }

                flags = new ResourceChunkFlags(new uint[9], baseShift);

                var baseSizeMax     = baseSize << 8;
                var baseSizeMaxTest = startPageSize;

                while (baseSizeMaxTest < baseSizeMax)
                {
                    bucketIndex++;
                    baseSizeMaxTest *= 2;
                }

                if (!flags.TryAddChunk(bucketIndex))
                {
                    break;
                }

                while (blockset.Count > 0)
                {
                    var isroot = sys && (currentPosition == 0);
                    var block  = isroot ? rootblock : blockset.GetBestBlock(currentPageSpace);

                    // If there is no block to fit in space left
                    if (block == null)
                    {
                        //allocate a new page
                        currentPageStart += currentPageSize;
                        currentPosition   = currentPageStart;

                        // Get the biggest block
                        block = blockset.GetBestBlock(long.MaxValue);
                        var blockLength = block?.BlockLength ?? 0;

                        // Get the smallest page which can contain the block
                        while (blockLength <= (currentPageSize >> 1))
                        {
                            if (currentPageSize <= minPageSize)
                            {
                                break;
                            }
                            if (bucketIndex >= 8)
                            {
                                break;
                            }
                            if ((currentPageSize <= targetPageSize) && (currentRemainder >= (currentPageSize - minPageSize)))
                            {
                                break;
                            }

                            currentPageSize = currentPageSize >> 1;
                            bucketIndex++;
                        }

                        currentPageSpace = currentPageSize;

                        // Try adding another chunk to this bucket
                        if (!flags.TryAddChunk(bucketIndex))
                        {
                            invalidLayout = true;
                            break;
                        }
                    }

                    //add this block to the current page.
                    block.BlockPosition = basePosition + currentPosition;
                    var opos = currentPosition;
                    currentPosition += block.BlockLength;
                    currentPosition += Pad(currentPosition);
                    var usedspace = currentPosition - opos;
                    currentPageSpace -= usedspace;
                    currentRemainder -= usedspace;
                }

                startPageSize *= 2;
                pageSizeMult  *= 2;
            }while ((invalidLayout) || (flags.Size < totalBlockSize) || (flags.Count + usedPages > 128));
        }