#include "stdafx.h" #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/SysCalls/Modules.h" extern "C" { #include "stblib/stb_image.h" } #include "Emu/FS/VFS.h" #include "Emu/FS/vfsFileBase.h" #include "Emu/SysCalls/lv2/sys_fs.h" #include "cellPngDec.h" extern Module cellPngDec; s32 pngDecCreate( vm::ptr mainHandle, vm::cptr param, vm::cptr ext = vm::null) { // alloc memory (should probably use param->cbCtrlMallocFunc) auto dec = CellPngDecMainHandle::make(Memory.Alloc(sizeof(PngDecoder), 128)); if (!dec) { return CELL_PNGDEC_ERROR_FATAL; } // initialize decoder dec->malloc = param->cbCtrlMallocFunc; dec->malloc_arg = param->cbCtrlMallocArg; dec->free = param->cbCtrlFreeFunc; dec->free_arg = param->cbCtrlFreeArg; if (ext) { } // use virtual memory address as a handle *mainHandle = dec; return CELL_OK; } s32 pngDecDestroy(CellPngDecMainHandle dec) { if (!Memory.Free(dec.addr())) { return CELL_PNGDEC_ERROR_FATAL; } return CELL_OK; } s32 pngDecOpen( CellPngDecMainHandle dec, vm::ptr subHandle, vm::cptr src, vm::ptr openInfo, vm::cptr cb = vm::null, vm::cptr param = vm::null) { // alloc memory (should probably use dec->malloc) auto stream = CellPngDecSubHandle::make(Memory.Alloc(sizeof(PngStream), 128)); if (!stream) { return CELL_PNGDEC_ERROR_FATAL; } // initialize stream stream->fd = 0; stream->src = *src; switch (src->srcSelect.data()) { case se32(CELL_PNGDEC_BUFFER): stream->fileSize = src->streamSize; break; case se32(CELL_PNGDEC_FILE): { // Get file descriptor and size std::shared_ptr file_s(Emu.GetVFS().OpenFile(src->fileName.get_ptr(), vfsRead)); if (!file_s) return CELL_PNGDEC_ERROR_OPEN_FILE; stream->fd = Emu.GetIdManager().make(file_s, 0, 0); stream->fileSize = file_s->GetSize(); break; } } if (cb) { // TODO: callback } if (param) { // TODO: param->selectChunk } // use virtual memory address as a handle *subHandle = stream; // set memory info openInfo->initSpaceAllocated = 4096; return CELL_OK; } s32 pngDecClose(CellPngDecSubHandle stream) { Emu.GetIdManager().remove(stream->fd); if (!Memory.Free(stream.addr())) { return CELL_PNGDEC_ERROR_FATAL; } return CELL_OK; } s32 pngReadHeader( CellPngDecSubHandle stream, vm::ptr info, vm::ptr extInfo = vm::null) { CellPngDecInfo& current_info = stream->info; if (stream->fileSize < 29) { return CELL_PNGDEC_ERROR_HEADER; // The file is smaller than the length of a PNG header } //Write the header to buffer vm::var buffer; // Alloc buffer for PNG header auto buffer_32 = buffer.To>(); switch (stream->src.srcSelect.data()) { case se32(CELL_PNGDEC_BUFFER): memmove(buffer.begin(), stream->src.streamPtr.get_ptr(), buffer.size()); break; case se32(CELL_PNGDEC_FILE): { auto file = Emu.GetIdManager().get(stream->fd); file->file->Seek(0); file->file->Read(buffer.begin(), buffer.size()); break; } } if (buffer_32[0].data() != se32(0x89504E47) || buffer_32[1].data() != se32(0x0D0A1A0A) || // Error: The first 8 bytes are not a valid PNG signature buffer_32[3].data() != se32(0x49484452)) // Error: The PNG file does not start with an IHDR chunk { return CELL_PNGDEC_ERROR_HEADER; } switch (buffer[25]) { case 0: current_info.colorSpace = CELL_PNGDEC_GRAYSCALE; current_info.numComponents = 1; break; case 2: current_info.colorSpace = CELL_PNGDEC_RGB; current_info.numComponents = 3; break; case 3: current_info.colorSpace = CELL_PNGDEC_PALETTE; current_info.numComponents = 1; break; case 4: current_info.colorSpace = CELL_PNGDEC_GRAYSCALE_ALPHA; current_info.numComponents = 2; break; case 6: current_info.colorSpace = CELL_PNGDEC_RGBA; current_info.numComponents = 4; break; default: cellPngDec.Error("cellPngDecDecodeData: Unsupported color space (%d)", (u32)buffer[25]); return CELL_PNGDEC_ERROR_HEADER; } current_info.imageWidth = buffer_32[4]; current_info.imageHeight = buffer_32[5]; current_info.bitDepth = buffer[24]; current_info.interlaceMethod = (CellPngDecInterlaceMode)buffer[28]; current_info.chunkInformation = 0; // Unimplemented *info = current_info; if (extInfo) { extInfo->reserved = 0; } return CELL_OK; } s32 pngDecSetParameter( CellPngDecSubHandle stream, vm::cptr inParam, vm::ptr outParam, vm::cptr extInParam = vm::null, vm::ptr extOutParam = vm::null) { CellPngDecInfo& current_info = stream->info; CellPngDecOutParam& current_outParam = stream->outParam; current_outParam.outputWidthByte = (current_info.imageWidth * current_info.numComponents * current_info.bitDepth) / 8; current_outParam.outputWidth = current_info.imageWidth; current_outParam.outputHeight = current_info.imageHeight; current_outParam.outputColorSpace = inParam->outputColorSpace; switch (current_outParam.outputColorSpace.data()) { case se32(CELL_PNGDEC_PALETTE): case se32(CELL_PNGDEC_GRAYSCALE): current_outParam.outputComponents = 1; break; case se32(CELL_PNGDEC_GRAYSCALE_ALPHA): current_outParam.outputComponents = 2; break; case se32(CELL_PNGDEC_RGB): current_outParam.outputComponents = 3; break; case se32(CELL_PNGDEC_RGBA): case se32(CELL_PNGDEC_ARGB): current_outParam.outputComponents = 4; break; default: cellPngDec.Error("pngDecSetParameter: Unsupported color space (%d)", current_outParam.outputColorSpace); return CELL_PNGDEC_ERROR_ARG; } current_outParam.outputBitDepth = inParam->outputBitDepth; current_outParam.outputMode = inParam->outputMode; current_outParam.useMemorySpace = 0; // Unimplemented *outParam = current_outParam; return CELL_OK; } s32 pngDecodeData( CellPngDecSubHandle stream, vm::ptr data, vm::cptr dataCtrlParam, vm::ptr dataOutInfo, vm::cptr cbCtrlDisp = vm::null, vm::ptr dispParam = vm::null) { dataOutInfo->status = CELL_PNGDEC_DEC_STATUS_STOP; const u32& fd = stream->fd; const u64& fileSize = stream->fileSize; const CellPngDecOutParam& current_outParam = stream->outParam; //Copy the PNG file to a buffer vm::var png((u32)fileSize); switch (stream->src.srcSelect.data()) { case se32(CELL_PNGDEC_BUFFER): memmove(png.begin(), stream->src.streamPtr.get_ptr(), png.size()); break; case se32(CELL_PNGDEC_FILE): { auto file = Emu.GetIdManager().get(stream->fd); file->file->Seek(0); file->file->Read(png.ptr(), png.size()); break; } } //Decode PNG file. (TODO: Is there any faster alternative? Can we do it without external libraries?) int width, height, actual_components; auto image = std::unique_ptr ( stbi_load_from_memory(png.ptr(), (s32)fileSize, &width, &height, &actual_components, 4), &::free ); if (!image) { cellPngDec.Error("pngDecodeData: stbi_load_from_memory failed"); return CELL_PNGDEC_ERROR_STREAM_FORMAT; } const bool flip = current_outParam.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP; const int bytesPerLine = (u32)dataCtrlParam->outputBytesPerLine; uint image_size = width * height; switch (current_outParam.outputColorSpace.data()) { case se32(CELL_PNGDEC_RGB): case se32(CELL_PNGDEC_RGBA): { const char nComponents = current_outParam.outputColorSpace == CELL_PNGDEC_RGBA ? 4 : 3; image_size *= nComponents; if (bytesPerLine > width * nComponents || flip) //check if we need padding { const int linesize = std::min(bytesPerLine, width * nComponents); for (int i = 0; i < height; i++) { const int dstOffset = i * bytesPerLine; const int srcOffset = width * nComponents * (flip ? height - i - 1 : i); memcpy(&data[dstOffset], &image.get()[srcOffset], linesize); } } else { memcpy(data.get_ptr(), image.get(), image_size); } break; } case se32(CELL_PNGDEC_ARGB): { const int nComponents = 4; image_size *= nComponents; if (bytesPerLine > width * nComponents || flip) //check if we need padding { //TODO: find out if we can't do padding without an extra copy const int linesize = std::min(bytesPerLine, width * nComponents); char *output = (char *)malloc(linesize); for (int i = 0; i < height; i++) { const int dstOffset = i * bytesPerLine; const int srcOffset = width * nComponents * (flip ? height - i - 1 : i); for (int j = 0; j < linesize; j += nComponents) { output[j + 0] = image.get()[srcOffset + j + 3]; output[j + 1] = image.get()[srcOffset + j + 0]; output[j + 2] = image.get()[srcOffset + j + 1]; output[j + 3] = image.get()[srcOffset + j + 2]; } memcpy(&data[dstOffset], output, linesize); } free(output); } else { uint* img = (uint*)new char[image_size]; uint* source_current = (uint*)&(image.get()[0]); uint* dest_current = img; for (uint i = 0; i < image_size / nComponents; i++) { uint val = *source_current; *dest_current = (val >> 24) | (val << 8); // set alpha (A8) as leftmost byte source_current++; dest_current++; } memcpy(data.get_ptr(), img, image_size); delete[] img; } break; } case se32(CELL_PNGDEC_GRAYSCALE): case se32(CELL_PNGDEC_PALETTE): case se32(CELL_PNGDEC_GRAYSCALE_ALPHA): cellPngDec.Error("pngDecodeData: Unsupported color space (%d)", current_outParam.outputColorSpace); break; default: cellPngDec.Error("pngDecodeData: Unsupported color space (%d)", current_outParam.outputColorSpace); return CELL_PNGDEC_ERROR_ARG; } dataOutInfo->status = CELL_PNGDEC_DEC_STATUS_FINISH; return CELL_OK; } s32 cellPngDecCreate( vm::ptr mainHandle, vm::cptr threadInParam, vm::ptr threadOutParam) { cellPngDec.Warning("cellPngDecCreate(mainHandle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x)", mainHandle, threadInParam, threadOutParam); // create decoder if (auto res = pngDecCreate(mainHandle, threadInParam)) return res; // set codec version threadOutParam->pngCodecVersion = PNGDEC_CODEC_VERSION; return CELL_OK; } s32 cellPngDecExtCreate( vm::ptr mainHandle, vm::cptr threadInParam, vm::ptr threadOutParam, vm::cptr extThreadInParam, vm::ptr extThreadOutParam) { cellPngDec.Warning("cellPngDecCreate(mainHandle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", mainHandle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam); // create decoder if (auto res = pngDecCreate(mainHandle, threadInParam, extThreadInParam)) return res; // set codec version threadOutParam->pngCodecVersion = PNGDEC_CODEC_VERSION; extThreadOutParam->reserved = 0; return CELL_OK; } s32 cellPngDecDestroy(CellPngDecMainHandle mainHandle) { cellPngDec.Warning("cellPngDecDestroy(mainHandle=*0x%x)", mainHandle); // destroy decoder return pngDecDestroy(mainHandle); } s32 cellPngDecOpen( CellPngDecMainHandle mainHandle, vm::ptr subHandle, vm::cptr src, vm::ptr openInfo) { cellPngDec.Warning("cellPngDecOpen(mainHandle=*0x%x, subHandle=**0x%x, src=*0x%x, openInfo=*0x%x)", mainHandle, subHandle, src, openInfo); // create stream handle return pngDecOpen(mainHandle, subHandle, src, openInfo); } s32 cellPngDecExtOpen( CellPngDecMainHandle mainHandle, vm::ptr subHandle, vm::cptr src, vm::ptr openInfo, vm::cptr cbCtrlStrm, vm::cptr opnParam) { cellPngDec.Warning("cellPngDecExtOpen(mainHandle=*0x%x, subHandle=**0x%x, src=*0x%x, openInfo=*0x%x, cbCtrlStrm=*0x%x, opnParam=*0x%x)", mainHandle, subHandle, src, openInfo, cbCtrlStrm, opnParam); // create stream handle return pngDecOpen(mainHandle, subHandle, src, openInfo, cbCtrlStrm, opnParam); } s32 cellPngDecClose(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle) { cellPngDec.Warning("cellPngDecClose(mainHandle=*0x%x, subHandle=*0x%x)", mainHandle, subHandle); return pngDecClose(subHandle); } s32 cellPngDecReadHeader(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr info) { cellPngDec.Warning("cellPngDecReadHeader(mainHandle=*0x%x, subHandle=*0x%x, info=*0x%x)", mainHandle, subHandle, info); return pngReadHeader(subHandle, info); } s32 cellPngDecExtReadHeader( CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr info, vm::ptr extInfo) { cellPngDec.Warning("cellPngDecExtReadHeader(mainHandle=*0x%x, subHandle=*0x%x, info=*0x%x, extInfo=*0x%x)", mainHandle, subHandle, info, extInfo); return pngReadHeader(subHandle, info, extInfo); } s32 cellPngDecSetParameter( CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::cptr inParam, vm::ptr outParam) { cellPngDec.Warning("cellPngDecSetParameter(mainHandle=*0x%x, subHandle=*0x%x, inParam=*0x%x, outParam=*0x%x)", mainHandle, subHandle, inParam, outParam); return pngDecSetParameter(subHandle, inParam, outParam); } s32 cellPngDecExtSetParameter( CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::cptr inParam, vm::ptr outParam, vm::cptr extInParam, vm::ptr extOutParam) { cellPngDec.Warning("cellPngDecExtSetParameter(mainHandle=*0x%x, subHandle=*0x%x, inParam=*0x%x, outParam=*0x%x, extInParam=*0x%x, extOutParam=*0x%x", mainHandle, subHandle, inParam, outParam, extInParam, extOutParam); return pngDecSetParameter(subHandle, inParam, outParam, extInParam, extOutParam); } s32 cellPngDecDecodeData( CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr data, vm::cptr dataCtrlParam, vm::ptr dataOutInfo) { cellPngDec.Warning("cellPngDecDecodeData(mainHandle=*0x%x, subHandle=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo); return pngDecodeData(subHandle, data, dataCtrlParam, dataOutInfo); } s32 cellPngDecExtDecodeData( CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr data, vm::cptr dataCtrlParam, vm::ptr dataOutInfo, vm::cptr cbCtrlDisp, vm::ptr dispParam) { cellPngDec.Warning("cellPngDecExtDecodeData(mainHandle=*0x%x, subHandle=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x, cbCtrlDisp=*0x%x, dispParam=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo, cbCtrlDisp, dispParam); return pngDecodeData(subHandle, data, dataCtrlParam, dataOutInfo, cbCtrlDisp, dispParam); } s32 cellPngDecGetUnknownChunks( CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::pptr unknownChunk, vm::ptr unknownChunkNumber) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetpCAL(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr pcal) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetcHRM(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr chrm) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetsCAL(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr scal) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetpHYs(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr phys) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetoFFs(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr offs) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetsPLT(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr splt) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetbKGD(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr bkgd) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGettIME(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr time) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGethIST(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr hist) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGettRNS(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr trns) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetsBIT(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr sbit) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetiCCP(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr iccp) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetsRGB(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr srgb) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetgAMA(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr gama) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetPLTE(CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr plte) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } s32 cellPngDecGetTextChunk( CellPngDecMainHandle mainHandle, CellPngDecSubHandle subHandle, vm::ptr textInfoNum, vm::pptr textInfo) { UNIMPLEMENTED_FUNC(cellPngDec); return CELL_OK; } Module cellPngDec("cellPngDec", []() { REG_FUNC(cellPngDec, cellPngDecGetUnknownChunks); REG_FUNC(cellPngDec, cellPngDecClose); REG_FUNC(cellPngDec, cellPngDecGetpCAL); REG_FUNC(cellPngDec, cellPngDecGetcHRM); REG_FUNC(cellPngDec, cellPngDecGetsCAL); REG_FUNC(cellPngDec, cellPngDecGetpHYs); REG_FUNC(cellPngDec, cellPngDecGetoFFs); REG_FUNC(cellPngDec, cellPngDecGetsPLT); REG_FUNC(cellPngDec, cellPngDecGetbKGD); REG_FUNC(cellPngDec, cellPngDecGettIME); REG_FUNC(cellPngDec, cellPngDecGethIST); REG_FUNC(cellPngDec, cellPngDecGettRNS); REG_FUNC(cellPngDec, cellPngDecGetsBIT); REG_FUNC(cellPngDec, cellPngDecGetiCCP); REG_FUNC(cellPngDec, cellPngDecGetsRGB); REG_FUNC(cellPngDec, cellPngDecGetgAMA); REG_FUNC(cellPngDec, cellPngDecGetPLTE); REG_FUNC(cellPngDec, cellPngDecGetTextChunk); REG_FUNC(cellPngDec, cellPngDecDestroy); REG_FUNC(cellPngDec, cellPngDecCreate); REG_FUNC(cellPngDec, cellPngDecExtCreate); REG_FUNC(cellPngDec, cellPngDecExtSetParameter); REG_FUNC(cellPngDec, cellPngDecSetParameter); REG_FUNC(cellPngDec, cellPngDecExtReadHeader); REG_FUNC(cellPngDec, cellPngDecReadHeader); REG_FUNC(cellPngDec, cellPngDecExtOpen); REG_FUNC(cellPngDec, cellPngDecOpen); REG_FUNC(cellPngDec, cellPngDecExtDecodeData); REG_FUNC(cellPngDec, cellPngDecDecodeData); });