예제 #1
0
        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;
            }
        }