/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.HLE.modules; import static jpcsp.HLE.modules.sceMpeg.getIntBuffer; import static jpcsp.HLE.modules.sceMpeg.releaseIntBuffer; import static jpcsp.graphics.GeCommands.TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888; import org.apache.log4j.Logger; import jpcsp.Memory; import jpcsp.MemoryMap; import jpcsp.Allegrex.compiler.RuntimeContext; import jpcsp.HLE.BufferInfo; import jpcsp.HLE.BufferInfo.LengthInfo; import jpcsp.HLE.BufferInfo.Usage; import jpcsp.HLE.HLEFunction; import jpcsp.HLE.HLEModule; import jpcsp.HLE.HLEUnimplemented; import jpcsp.HLE.Modules; import jpcsp.HLE.TPointer; import jpcsp.HLE.TPointer32; import jpcsp.HLE.kernel.types.SceKernelErrors; import jpcsp.HLE.kernel.types.SceMp4AvcCscStruct; import jpcsp.HLE.kernel.types.SceMpegYCrCbBuffer; import jpcsp.HLE.kernel.types.SceMpegYCrCbBufferSrc; import jpcsp.graphics.VideoEngine; import jpcsp.media.codec.h264.H264Utils; import jpcsp.memory.IMemoryReader; import jpcsp.memory.IMemoryWriter; import jpcsp.memory.MemoryReader; import jpcsp.memory.MemoryWriter; import jpcsp.util.Debug; import jpcsp.util.Utilities; public class sceMpegbase extends HLEModule { public static Logger log = Modules.getLogger("sceMpegbase"); private static void read(int addr, int length, int[] buffer, int offset) { addr |= MemoryMap.START_RAM; if (log.isTraceEnabled()) { log.trace(String.format("read addr=0x%08X, length=0x%X", addr, length)); } // Optimize the most common case if (RuntimeContext.hasMemoryInt()) { int length4 = length >> 2; int addrOffset = addr >> 2; int[] memoryInt = RuntimeContext.getMemoryInt(); for (int i = 0, j = offset; i < length4; i++) { int value = memoryInt[addrOffset++]; buffer[j++] = (value ) & 0xFF; buffer[j++] = (value >> 8) & 0xFF; buffer[j++] = (value >> 16) & 0xFF; buffer[j++] = (value >> 24) & 0xFF; } } else { IMemoryReader memoryReader = MemoryReader.getMemoryReader(addr, length, 1); for (int i = 0, j = offset; i < length; i++) { buffer[j++] = memoryReader.readNext(); } } } private static void copy(Memory mem, int dst, int src, int length) { if (log.isTraceEnabled()) { log.trace(String.format("copy dst=0x%08X, src=0x%08X, length=0x%X", dst, src, length)); } mem.memcpy(dst | MemoryMap.START_RAM, src | MemoryMap.START_RAM, length); } private static void copyBlocks(Memory mem, int dst, int src, int blocks) { copy(mem, dst, src, blocks << 4); } private int hleMpegBaseCscAvcRange(TPointer bufferRGB, int unknown, int bufferWidth, SceMp4AvcCscStruct mp4AvcCscStruct, int rangeX, int rangeY, int rangeWidth, int rangeHeight) { int width = mp4AvcCscStruct.width << 4; int height = mp4AvcCscStruct.height << 4; // It seems that the pixel output format is always ABGR8888. int videoPixelMode = TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888; int bytesPerPixel = sceDisplay.getPixelFormatBytes(videoPixelMode); int destAddr = bufferRGB.getAddress(); int width2 = width >> 1; int height2 = height >> 1; int length = width * height; int length2 = width2 * height2; // Read the YCbCr image. // See the description of the format used by the PSP in sceVideocodecDecode(). int[] luma = getIntBuffer(length); int[] cb = getIntBuffer(length2); int[] cr = getIntBuffer(length2); int sizeY1 = ((width + 16) >> 5) * (height >> 1) * 16; int sizeY2 = (width >> 5) * (height >> 1) * 16; int sizeCrCb1 = sizeY1 >> 1; int sizeCrCb2 = sizeY1 >> 1; int[] bufferY1 = getIntBuffer(sizeY1); int[] bufferY2 = getIntBuffer(sizeY2); int[] bufferCrCb1 = getIntBuffer(sizeCrCb1); int[] bufferCrCb2 = getIntBuffer(sizeCrCb2); read(mp4AvcCscStruct.buffer0, sizeY1, bufferY1, 0); read(mp4AvcCscStruct.buffer1, sizeY2, bufferY2, 0); read(mp4AvcCscStruct.buffer4, sizeCrCb1, bufferCrCb1, 0); read(mp4AvcCscStruct.buffer5, sizeCrCb2, bufferCrCb2, 0); for (int x = 0, j = 0; x < width; x += 32) { for (int y = 0, i = x; y < height; y += 2, i += 2 * width, j += 16) { System.arraycopy(bufferY1, j, luma, i, 16); } } for (int x = 16, j = 0; x < width; x += 32) { for (int y = 0, i = x; y < height; y += 2, i += 2 * width, j += 16) { System.arraycopy(bufferY2, j, luma, i, 16); } } for (int x = 0, j = 0; x < width2; x += 16) { for (int y = 0; y < height2; y += 2) { for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) { cb[i] = bufferCrCb1[j++]; cr[i] = bufferCrCb1[j++]; } } } for (int x = 0, j = 0; x < width2; x += 16) { for (int y = 1; y < height2; y += 2) { for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) { cb[i] = bufferCrCb2[j++]; cr[i] = bufferCrCb2[j++]; } } } read(mp4AvcCscStruct.buffer2, sizeY1, bufferY1, 0); read(mp4AvcCscStruct.buffer3, sizeY2, bufferY2, 0); read(mp4AvcCscStruct.buffer6, sizeCrCb1, bufferCrCb1, 0); read(mp4AvcCscStruct.buffer7, sizeCrCb2, bufferCrCb2, 0); for (int x = 0, j = 0; x < width; x += 32) { for (int y = 1, i = x + width; y < height; y += 2, i += 2 * width, j += 16) { System.arraycopy(bufferY1, j, luma, i, 16); } } for (int x = 16, j = 0; x < width; x += 32) { for (int y = 1, i = x + width; y < height; y += 2, i += 2 * width, j += 16) { System.arraycopy(bufferY2, j, luma, i, 16); } } for (int x = 8, j = 0; x < width2; x += 16) { for (int y = 0; y < height2; y += 2) { for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) { cb[i] = bufferCrCb1[j++]; cr[i] = bufferCrCb1[j++]; } } } for (int x = 8, j = 0; x < width2; x += 16) { for (int y = 1; y < height2; y += 2) { for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) { cb[i] = bufferCrCb2[j++]; cr[i] = bufferCrCb2[j++]; } } } releaseIntBuffer(bufferY1); releaseIntBuffer(bufferY2); releaseIntBuffer(bufferCrCb1); releaseIntBuffer(bufferCrCb2); // Convert YCbCr to ABGR int[] abgr = getIntBuffer(length); H264Utils.YUV2ABGR(width, height, luma, cb, cr, abgr); releaseIntBuffer(luma); releaseIntBuffer(cb); releaseIntBuffer(cr); // Do not cache the video image as a texture in the VideoEngine to allow fluid rendering VideoEngine.getInstance().addVideoTexture(destAddr, destAddr + (rangeY + rangeHeight) * bufferWidth * bytesPerPixel); // Write the ABGR image if (videoPixelMode == TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888 && RuntimeContext.hasMemoryInt()) { // Optimize the most common case int pixelIndex = rangeY * width + rangeX; int addr = destAddr; for (int i = 0; i < rangeHeight; i++) { System.arraycopy(abgr, pixelIndex, RuntimeContext.getMemoryInt(), addr >> 2, rangeWidth); pixelIndex += width; addr += bufferWidth * bytesPerPixel; } } else { int addr = destAddr; for (int i = 0; i < rangeHeight; i++) { IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(addr, rangeWidth * bytesPerPixel, bytesPerPixel); int pixelIndex = (i + rangeY) * width + rangeX; for (int j = 0; j < rangeWidth; j++, pixelIndex++) { int abgr8888 = abgr[pixelIndex]; int pixelColor = Debug.getPixelColor(abgr8888, videoPixelMode); memoryWriter.writeNext(pixelColor); } memoryWriter.flush(); addr += bufferWidth * bytesPerPixel; } } releaseIntBuffer(abgr); return 0; } @HLEFunction(nid = 0xBEA18F91, version = 150) public int sceMpegBasePESpacketCopy(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=16, usage=Usage.in) TPointer32 packetInfo) { Memory mem = Memory.getInstance(); int infoAddr = packetInfo.getAddress(); while (infoAddr != 0) { int bufferAddr = mem.read32(infoAddr + 0); int destinationAddr = mem.read32(infoAddr + 4) | MemoryMap.START_RAM; int nextInfoAddr = mem.read32(infoAddr + 8); int bufferLength = mem.read32(infoAddr + 12) & 0x00000FFF; if (log.isDebugEnabled()) { log.debug(String.format("sceMpegBasePESpacketCopy packet at 0x%08X: bufferAddr=0x%08X, destinationAddr=0x%08X, nextInfoAddr=0x%08X, bufferLength=0x%X: %s", infoAddr, bufferAddr, destinationAddr, nextInfoAddr, bufferLength, Utilities.getMemoryDump(bufferAddr, bufferLength))); } if (bufferLength == 0) { return SceKernelErrors.ERROR_INVALID_SIZE; } mem.memcpy(destinationAddr, bufferAddr, bufferLength); infoAddr = nextInfoAddr; } Modules.sceMpegModule.hleMpegNotifyRingbufferRead(); Modules.sceMpegModule.hleMpegNotifyVideoDecoderThread(); return 0; } @HLEFunction(nid = 0xBE45C284, version = 150) public int sceMpegBaseYCrCbCopyVme(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.in) TPointer destBufferYCrCb, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=40, usage=Usage.in) TPointer32 srcBufferYCrCb, int type) { SceMpegYCrCbBuffer destMpegYCrCbBuffer = new SceMpegYCrCbBuffer(); destMpegYCrCbBuffer.read(destBufferYCrCb); SceMpegYCrCbBufferSrc srcMpegYCrCbBuffer = new SceMpegYCrCbBufferSrc(); srcMpegYCrCbBuffer.read(srcBufferYCrCb); if (log.isDebugEnabled()) { log.debug(String.format("sceMpegBaseYCrCbCopyVme destMpegYCrCbBuffer: %s", destMpegYCrCbBuffer)); log.debug(String.format("sceMpegBaseYCrCbCopyVme srcMpegYCrCbBuffer: %s", srcMpegYCrCbBuffer)); } Memory mem = destBufferYCrCb.getMemory(); int sizeY = srcMpegYCrCbBuffer.frameWidth * srcMpegYCrCbBuffer.frameHeight; int sizeCrCb = sizeY >> 2; copy(mem, destMpegYCrCbBuffer.bufferY, srcMpegYCrCbBuffer.bufferY, sizeY); copy(mem, destMpegYCrCbBuffer.bufferY2, srcMpegYCrCbBuffer.bufferY2, sizeY); copy(mem, destMpegYCrCbBuffer.bufferCr, srcMpegYCrCbBuffer.bufferCr, sizeCrCb); copy(mem, destMpegYCrCbBuffer.bufferCb, srcMpegYCrCbBuffer.bufferCb, sizeCrCb); copy(mem, destMpegYCrCbBuffer.bufferCr2, srcMpegYCrCbBuffer.bufferCr2, sizeCrCb); copy(mem, destMpegYCrCbBuffer.bufferCb2, srcMpegYCrCbBuffer.bufferCb2, sizeCrCb); return 0; } @HLEFunction(nid = 0x7AC0321A, version = 150) public int sceMpegBaseYCrCbCopy(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=48, usage=Usage.in) TPointer dst, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=48, usage=Usage.in) TPointer src, int flags) { SceMp4AvcCscStruct dstStruct = new SceMp4AvcCscStruct(); dstStruct.read(dst); SceMp4AvcCscStruct srcStruct = new SceMp4AvcCscStruct(); srcStruct.read(src); int size1 = ((srcStruct.width + 16) >> 5) * (srcStruct.height >> 1); int size2 = (srcStruct.width >> 5) * (srcStruct.height >> 1); if (log.isDebugEnabled()) { log.debug(String.format("sceMpegBaseYCrCbCopy dstStruct: %s", dstStruct)); log.debug(String.format("sceMpegBaseYCrCbCopy srcStruct: %s", srcStruct)); log.debug(String.format("sceMpegBaseYCrCbCopy size1=0x%X, size2=0x%X", size1, size2)); } Memory mem = Memory.getInstance(); if ((flags & 1) != 0) { copyBlocks(mem, dstStruct.buffer0, srcStruct.buffer0, size1); copyBlocks(mem, dstStruct.buffer1, srcStruct.buffer1, size2); copyBlocks(mem, dstStruct.buffer4, srcStruct.buffer4, size1 >> 1); copyBlocks(mem, dstStruct.buffer5, srcStruct.buffer5, size2 >> 1); } if ((flags & 2) != 0) { copyBlocks(mem, dstStruct.buffer2, srcStruct.buffer2, size1); copyBlocks(mem, dstStruct.buffer3, srcStruct.buffer3, size2); copyBlocks(mem, dstStruct.buffer6, srcStruct.buffer6, size1 >> 1); copyBlocks(mem, dstStruct.buffer7, srcStruct.buffer7, size2 >> 1); } return 0; } @HLEFunction(nid = 0xCE8EB837, version = 150) public int sceMpegBaseCscVme(TPointer bufferRGB, TPointer bufferRGB2, int bufferWidth, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.in) TPointer32 bufferYCrCb) { SceMpegYCrCbBuffer sceMpegYCrCbBuffer = new SceMpegYCrCbBuffer(); sceMpegYCrCbBuffer.read(bufferYCrCb); int width = sceMpegYCrCbBuffer.frameBufferWidth16 << 4; int height = sceMpegYCrCbBuffer.frameBufferHeight16 << 4; int videoPixelMode = TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888; int bytesPerPixel = sceDisplay.getPixelFormatBytes(videoPixelMode); int rangeX = 0; int rangeY = 0; int rangeWidth = width; int rangeHeight = height; int destAddr = bufferRGB.getAddress(); if (log.isDebugEnabled()) { log.debug(String.format("sceMpegBaseCscVme sceMpegYCrCbBuffer: %s", sceMpegYCrCbBuffer)); } int width2 = width >> 1; int height2 = height >> 1; int length = width * height; int length2 = width2 * height2; // Read the YCbCr image int[] luma = getIntBuffer(length); int[] cb = getIntBuffer(length2); int[] cr = getIntBuffer(length2); read(sceMpegYCrCbBuffer.bufferY, length, luma, 0); read(sceMpegYCrCbBuffer.bufferCb, length2, cb, 0); read(sceMpegYCrCbBuffer.bufferCr, length2, cr, 0); // Convert YCbCr to ABGR int[] abgr = getIntBuffer(length); H264Utils.YUV2ABGR(width, height, luma, cb, cr, abgr); releaseIntBuffer(luma); releaseIntBuffer(cb); releaseIntBuffer(cr); // Do not cache the video image as a texture in the VideoEngine to allow fluid rendering VideoEngine.getInstance().addVideoTexture(destAddr, destAddr + (rangeY + rangeHeight) * bufferWidth * bytesPerPixel); // Write the ABGR image if (videoPixelMode == TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888 && RuntimeContext.hasMemoryInt()) { // Optimize the most common case int pixelIndex = rangeY * width + rangeX; for (int i = 0; i < rangeHeight; i++) { int addr = destAddr + (i * bufferWidth) * bytesPerPixel; System.arraycopy(abgr, pixelIndex, RuntimeContext.getMemoryInt(), addr >> 2, rangeWidth); pixelIndex += width; } } else { int addr = destAddr; for (int i = 0; i < rangeHeight; i++) { IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(addr, rangeWidth * bytesPerPixel, bytesPerPixel); int pixelIndex = (i + rangeY) * width + rangeX; for (int j = 0; j < rangeWidth; j++, pixelIndex++) { int abgr8888 = abgr[pixelIndex]; int pixelColor = Debug.getPixelColor(abgr8888, videoPixelMode); memoryWriter.writeNext(pixelColor); } memoryWriter.flush(); addr += bufferWidth * bytesPerPixel; } } releaseIntBuffer(abgr); return 0; } @HLEUnimplemented @HLEFunction(nid = 0x492B5E4B, version = 150) public int sceMpegBaseCscInit(int unknown) { // sceMpegBaseCscAvc is decoding with no alpha H264Utils.setAlpha(0x00); return 0; } @HLEFunction(nid = 0x91929A21, version = 150) public int sceMpegBaseCscAvc(TPointer bufferRGB, int unknown, int bufferWidth, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=48, usage=Usage.in) TPointer mp4AvcCscStructAddr) { SceMp4AvcCscStruct mp4AvcCscStruct = new SceMp4AvcCscStruct(); mp4AvcCscStruct.read(mp4AvcCscStructAddr); int rangeX = 0; int rangeY = 0; int rangeWidth = mp4AvcCscStruct.width << 4; int rangeHeight = mp4AvcCscStruct.height << 4; if (log.isDebugEnabled()) { log.debug(String.format("sceMpegBaseCscAvc %s", mp4AvcCscStruct)); } return hleMpegBaseCscAvcRange(bufferRGB, unknown, bufferWidth, mp4AvcCscStruct, rangeX, rangeY, rangeWidth, rangeHeight); } @HLEFunction(nid = 0x304882E1, version = 150) public int sceMpegBaseCscAvcRange(TPointer bufferRGB, int unknown, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=16, usage=Usage.in) TPointer32 rangeAddr, int bufferWidth, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=48, usage=Usage.in) TPointer mp4AvcCscStructAddr) { SceMp4AvcCscStruct mp4AvcCscStruct = new SceMp4AvcCscStruct(); mp4AvcCscStruct.read(mp4AvcCscStructAddr); int rangeX = rangeAddr.getValue(0) << 4; int rangeY = rangeAddr.getValue(4) << 4; int rangeWidth = rangeAddr.getValue(8) << 4; int rangeHeight = rangeAddr.getValue(12) << 4; if (log.isDebugEnabled()) { log.debug(String.format("sceMpegBaseCscAvcRange range x=%d, y=%d, width=%d, height=%d, %s", rangeX, rangeY, rangeWidth, rangeHeight, mp4AvcCscStruct)); } return hleMpegBaseCscAvcRange(bufferRGB, unknown, bufferWidth, mp4AvcCscStruct, rangeX, rangeY, rangeWidth, rangeHeight); } @HLEUnimplemented @HLEFunction(nid = 0x0530BE4E, version = 150) public int sceMpegbase_0530BE4E(int unknown) { return 0; } @HLEUnimplemented @HLEFunction(nid = 0xAC9E717E, version = 150) public int sceMpegbase_AC9E717E(int unknown1, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=22, usage=Usage.in) TPointer unknown2) { return 0; } }