void CopyTexelsClut(BinaryReader br, BinaryWriter bw, TxmHeader pakTxm, TxmHeader textureTxm) { if (pakTxm.ClutPixelFormat != TxmPixelFormat.None) { throw new ArgumentException("Cannot operate on source TXM with CLUT.", nameof(pakTxm)); } if (textureTxm.ClutPixelFormat == TxmPixelFormat.None) { return; } var destColumnParams = GsMemoryUtils.GetColumnParams(textureTxm.ClutPixelFormat); int copyLength = textureTxm.GetClutByteSize(); int baseBlockNumber = textureTxm.ClutBufferBase - pakTxm.ImageBufferBase; int srcBase = 0x10 + pakTxm.GetClutByteSize(); var destBase = 0x10; int bytesPerSrcLine = pakTxm.GetImageByteSize() / pakTxm.ImageHeight; int bytesPerDestLine = textureTxm.GetClutByteSize() / textureTxm.ClutHeight; bw.Write(new byte[copyLength]); int numXBlocks = textureTxm.ClutWidth / destColumnParams.Width; if (numXBlocks == 0) { numXBlocks = 1; } int numYBlocks = textureTxm.ClutHeight / (destColumnParams.Height * GsMemoryUtils.COLUMNS_PER_BLOCK); if (numYBlocks == 0) { numYBlocks = 1; } int destBlock = 0; for (int blockY = 0; blockY < numYBlocks; ++blockY) { for (int blockX = 0; blockX < numXBlocks; ++blockX) { int blockNumber = baseBlockNumber + GsMemoryUtils.CalcBlockNumber(textureTxm.ClutPixelFormat, blockX, blockY, 1); br.BaseStream.Seek(srcBase + GsMemoryUtils.CalcBlockMemoryOffset(pakTxm.ImageSourcePixelFormat, blockNumber), SeekOrigin.Begin); bw.BaseStream.Seek(destBase + GsMemoryUtils.CalcTxmImageOffset(destColumnParams, destBlock, textureTxm.ClutWidth), SeekOrigin.Begin); for (int i = 0; i < GsMemoryUtils.COLUMNS_PER_BLOCK; ++i) { byte[] col = GsMemoryUtils.ReadColumn(br, pakTxm.ImageSourcePixelFormat, bytesPerSrcLine); GsMemoryUtils.WriteColumn(bw, textureTxm.ClutPixelFormat, bytesPerDestLine, col); } ++destBlock; } } // Dump palette //bw.BaseStream.Seek(destBase, SeekOrigin.Begin); //BinaryReader palBr = new BinaryReader(bw.BaseStream); //using (var palette = TxmConversion.ConvertTxmRgba32(palBr, textureTxm.ClutWidth, textureTxm.ClutHeight)) //{ // palette.SaveAsPng($"palette_{numWrittenTextures}.png"); //} }
void CopyTexels(BinaryReader br, BinaryWriter bw, TxmHeader pakTxm, TxmHeader textureTxm) { if (pakTxm.ClutPixelFormat != TxmPixelFormat.None) { throw new ArgumentException("Cannot operate on source TXM with CLUT.", nameof(pakTxm)); } var destColumnParams = GsMemoryUtils.GetColumnParams(textureTxm.ImageSourcePixelFormat); int copyLength = textureTxm.GetImageByteSize(); int srcBase = 0x10 + pakTxm.GetClutByteSize(); int baseBlockNumber = textureTxm.ImageBufferBase - pakTxm.ImageBufferBase; int destBase = 0x10 + textureTxm.GetClutByteSize(); int bytesPerSrcLine = pakTxm.GetImageByteSize() / pakTxm.ImageHeight; int bytesPerDestLine = copyLength / textureTxm.ImageHeight; bw.Write(new byte[copyLength]); int numXBlocks = textureTxm.ImageWidth / destColumnParams.Width; if (numXBlocks == 0) { numXBlocks = 1; } int numYBlocks = textureTxm.ImageHeight / (destColumnParams.Height * GsMemoryUtils.COLUMNS_PER_BLOCK); if (numYBlocks == 0) { numYBlocks = 1; } int destBlock = 0; for (int blockY = 0; blockY < numYBlocks; ++blockY) { for (int blockX = 0; blockX < numXBlocks; ++blockX) { int blockNumber = baseBlockNumber + GsMemoryUtils.CalcBlockNumber(textureTxm.ImageSourcePixelFormat, blockX, blockY, textureTxm.Misc); br.BaseStream.Seek(srcBase + GsMemoryUtils.CalcBlockMemoryOffset(pakTxm.ImageSourcePixelFormat, blockNumber), SeekOrigin.Begin); bw.BaseStream.Seek(destBase + GsMemoryUtils.CalcTxmImageOffset(destColumnParams, destBlock, textureTxm.ImageWidth), SeekOrigin.Begin); for (int i = 0; i < GsMemoryUtils.COLUMNS_PER_BLOCK; ++i) { byte[] col = GsMemoryUtils.ReadColumn(br, pakTxm.ImageSourcePixelFormat, bytesPerSrcLine); if (pakTxm.ImageSourcePixelFormat != textureTxm.ImageSourcePixelFormat) { col = PsmtMixer.MixColumn(col, pakTxm.ImageSourcePixelFormat, textureTxm.ImageSourcePixelFormat, i % 2 != 0); } GsMemoryUtils.WriteColumn(bw, textureTxm.ImageSourcePixelFormat, bytesPerDestLine, col); } ++destBlock; } } }