libdgf/LibDgf/Ps2/Gs/GsMemoryUtils.cs
2021-03-17 03:18:15 -06:00

207 lines
8.2 KiB
C#

using LibDgf.Txm;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace LibDgf.Ps2.Gs
{
public static class GsMemoryUtils
{
public const int BYTES_PER_COLUMN = 64;
public const int COLUMNS_PER_BLOCK = 4;
public const int BYTES_PER_BLOCK = BYTES_PER_COLUMN * COLUMNS_PER_BLOCK;
public const int BLOCKS_PER_PAGE = 32;
public const int BYTES_PER_PAGE = BYTES_PER_BLOCK * BLOCKS_PER_PAGE;
public const int TOTAL_PAGES = 512;
public const int TOTAL_BYTES = BYTES_PER_PAGE * TOTAL_PAGES;
public class ColumnParams
{
public int Width { get; }
public int Height { get; }
public int BitsPerPixel { get; }
public int ColsPerPage { get; }
public int RowsPerPage { get; }
internal ColumnParams(int width, int height, int bitsPerPixel, int colsPerPage, int rowsPerPage)
{
Width = width;
Height = height;
BitsPerPixel = bitsPerPixel;
ColsPerPage = colsPerPage;
RowsPerPage = rowsPerPage;
}
}
static readonly ColumnParams PSMCT32_COLUMN_PARAMS = new ColumnParams(8, 2, 32, 8, 4);
static readonly ColumnParams PSMT8_COLUMN_PARAMS = new ColumnParams(16, 4, 8, 8, 4);
static readonly ColumnParams PSMT4_COLUMN_PARAMS = new ColumnParams(32, 4, 4, 4, 8);
public static ColumnParams GetColumnParams(TxmPixelFormat format)
{
switch (format)
{
case TxmPixelFormat.PSMCT32:
case TxmPixelFormat.PSMCT24:
return PSMCT32_COLUMN_PARAMS;
case TxmPixelFormat.PSMT8:
return PSMT8_COLUMN_PARAMS;
case TxmPixelFormat.PSMT4:
return PSMT4_COLUMN_PARAMS;
default:
throw new NotSupportedException($"{format} not supported.");
}
}
public static byte[] ReadColumn(BinaryReader br, TxmPixelFormat format, int bytesPerLine)
{
ColumnParams colParams = GetColumnParams(format);
byte[] data = new byte[colParams.Width * colParams.Height * colParams.BitsPerPixel / 8];
int bytesPerDestLine = colParams.Width * colParams.BitsPerPixel / 8;
int skipLength = bytesPerLine - bytesPerDestLine;
for (int i = 0; i < colParams.Height; ++i)
{
byte[] lineData = br.ReadBytes(bytesPerDestLine);
br.BaseStream.Seek(skipLength, SeekOrigin.Current);
Buffer.BlockCopy(lineData, 0, data, i * bytesPerDestLine, lineData.Length);
}
return data;
}
public static void WriteColumn(BinaryWriter bw, TxmPixelFormat format, int bytesPerLine, byte[] data)
{
ColumnParams colParams = GetColumnParams(format);
int bytesPerDestLine = colParams.Width * colParams.BitsPerPixel / 8;
int skipLength = bytesPerLine - bytesPerDestLine;
for (int i = 0; i < colParams.Height; ++i)
{
bw.Write(data, i * bytesPerDestLine, bytesPerDestLine);
bw.Seek(skipLength, SeekOrigin.Current);
}
}
// --------------------------------------------------------------------
// Block address to linear offset lookup for PSMCT32
static readonly int[,] PSMCT32_BLOCK_LOOKUP = new[,]
{
{ 0, 1, 4, 5, 16, 17, 20, 21 },
{ 2, 3, 6, 7, 18, 19, 22, 23 },
{ 8, 9, 12, 13, 24, 25, 28, 29 },
{ 10, 11, 14, 15, 26, 27, 30, 31 }
};
static readonly int[,] PSMT8_BLOCK_LOOKUP = new[,]
{
{ 0, 1, 4, 5, 16, 17, 20, 21 },
{ 2, 3, 6, 7, 18, 19, 22, 23 },
{ 8, 9, 12, 13, 24, 25, 28, 29 },
{ 10, 11, 14, 15, 26, 27, 30, 31 }
};
static readonly int[,] PSMT4_BLOCK_LOOKUP = new[,]
{
{ 0, 2, 8, 10 },
{ 1, 3, 9, 11 },
{ 4, 6, 12, 14 },
{ 5, 7, 13, 15 },
{ 16, 18, 24, 26 },
{ 17, 19, 25, 27 },
{ 20, 22, 28, 30 },
{ 21, 23, 29, 31 }
};
static readonly int[] PSMCT32_BLOCK_REVERSE_LOOKUP;
static readonly int[] PSMT8_BLOCK_REVERSE_LOOKUP;
static readonly int[] PSMT4_BLOCK_REVERSE_LOOKUP;
static GsMemoryUtils()
{
PSMCT32_BLOCK_REVERSE_LOOKUP = MakeReverseLookup(PSMCT32_BLOCK_LOOKUP);
PSMT8_BLOCK_REVERSE_LOOKUP = MakeReverseLookup(PSMT8_BLOCK_LOOKUP);
PSMT4_BLOCK_REVERSE_LOOKUP = MakeReverseLookup(PSMT4_BLOCK_LOOKUP);
}
static int[] MakeReverseLookup(int[,] lut)
{
int[] reverse = new int[lut.Length];
int i = 0;
foreach (var n in lut)
{
reverse[n] = i++;
}
return reverse;
}
public static int CalcBlockNumber(TxmPixelFormat format, int blockX, int blockY, int texBufWidth)
{
ColumnParams colParams = GetColumnParams(format);
switch (format)
{
case TxmPixelFormat.PSMCT32:
return CalcBlockNumber(colParams, PSMCT32_BLOCK_LOOKUP,blockX, blockY, texBufWidth);
case TxmPixelFormat.PSMT8:
return CalcBlockNumber(colParams, PSMT8_BLOCK_LOOKUP, blockX, blockY, texBufWidth);
case TxmPixelFormat.PSMT4:
return CalcBlockNumber(colParams, PSMT4_BLOCK_LOOKUP, blockX, blockY, texBufWidth);
default:
throw new NotSupportedException($"{format} not supported");
}
}
public static int CalcBlockMemoryOffset(TxmPixelFormat format, int index)
{
ColumnParams colParams = GetColumnParams(format);
switch (format)
{
case TxmPixelFormat.PSMCT32:
return CalcBlockMemoryOffset(colParams, PSMCT32_BLOCK_REVERSE_LOOKUP, index);
case TxmPixelFormat.PSMT8:
return CalcBlockMemoryOffset(colParams, PSMT8_BLOCK_REVERSE_LOOKUP, index);
case TxmPixelFormat.PSMT4:
return CalcBlockMemoryOffset(colParams, PSMT4_BLOCK_REVERSE_LOOKUP, index);
default:
throw new NotSupportedException($"{format} not supported");
}
}
static int CalcBlockNumber(ColumnParams colParams, int[,] lut, int blockX, int blockY, int texBufWidth)
{
int pageX = blockX / colParams.ColsPerPage;
int pageY = blockY / colParams.RowsPerPage;
int blockXInPage = blockX % colParams.ColsPerPage;
int blockYInPage = blockY % colParams.RowsPerPage;
return (pageY * texBufWidth + pageX) * BLOCKS_PER_PAGE + lut[blockYInPage, blockXInPage];
}
static int CalcBlockMemoryOffset(ColumnParams colParams, int[] lut, int index)
{
int pageIndex = index / BLOCKS_PER_PAGE;
int rem = index % BLOCKS_PER_PAGE;
int pixelsPerBlock = colParams.Width * colParams.Height * COLUMNS_PER_BLOCK;
int memBlockNum = lut[rem];
int fullBlockRows = memBlockNum / colParams.ColsPerPage;
int partialBlocks = memBlockNum % colParams.ColsPerPage;
return (
pageIndex * BLOCKS_PER_PAGE * pixelsPerBlock + // Full pages
fullBlockRows * pixelsPerBlock * colParams.ColsPerPage + // Full row of blocks
partialBlocks * colParams.Width // Partial row of blocks
) * colParams.BitsPerPixel / 8;
}
public static int CalcTxmImageOffset(ColumnParams colParams, int blockIndex, int imageWidth)
{
int blocksPerRow = imageWidth / colParams.Width;
if (blocksPerRow == 0) blocksPerRow = 1;
int fullRows = blockIndex / blocksPerRow;
int rowBlockIndex = blockIndex % blocksPerRow;
return (
fullRows * imageWidth * colParams.Height * 4 +
rowBlockIndex * colParams.Width
) * colParams.BitsPerPixel / 8;
}
}
}