#include "stdafx.h" #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" // STB_IMAGE_IMPLEMENTATION is already defined in stb_image.cpp #include #include "Emu/Cell/lv2/sys_fs.h" #include "cellGifDec.h" LOG_CHANNEL(cellGifDec); // cellGifDec aliases (only for cellGifDec.cpp) using PPMainHandle = vm::pptr; using PMainHandle = vm::ptr; using PThreadInParam = vm::cptr; using PThreadOutParam = vm::ptr; using PExtThreadInParam = vm::cptr; using PExtThreadOutParam = vm::ptr; using PPSubHandle = vm::pptr; using PSubHandle = vm::ptr; using PSrc = vm::cptr; using POpenInfo = vm::ptr; using PInfo = vm::ptr; using PInParam = vm::cptr; using POutParam = vm::ptr; using PDataCtrlParam = vm::cptr; using PDataOutInfo = vm::ptr; s32 cellGifDecCreate(PPMainHandle mainHandle, PThreadInParam threadInParam, PThreadOutParam threadOutParam) { UNIMPLEMENTED_FUNC(cellGifDec); return CELL_OK; } s32 cellGifDecExtCreate(PPMainHandle mainHandle, PThreadInParam threadInParam, PThreadOutParam threadOutParam, PExtThreadInParam extThreadInParam, PExtThreadOutParam extThreadOutParam) { UNIMPLEMENTED_FUNC(cellGifDec); return CELL_OK; } s32 cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc src, POpenInfo openInfo) { cellGifDec.warning("cellGifDecOpen(mainHandle=*0x%x, subHandle=**0x%x, src=*0x%x, openInfo=*0x%x)", mainHandle, subHandle, src, openInfo); GifStream current_subHandle; current_subHandle.fd = 0; current_subHandle.src = *src; switch (src->srcSelect) { case CELL_GIFDEC_BUFFER: current_subHandle.fileSize = src->streamSize; break; case CELL_GIFDEC_FILE: { // Get file descriptor and size fs::file file_s(vfs::get(src->fileName.get_ptr())); if (!file_s) return CELL_GIFDEC_ERROR_OPEN_FILE; current_subHandle.fileSize = file_s.size(); current_subHandle.fd = idm::make(src->fileName.get_ptr(), std::move(file_s), 0, 0); break; } } subHandle->set(vm::alloc(sizeof(GifStream), vm::main)); **subHandle = current_subHandle; return CELL_OK; } s32 cellGifDecExtOpen() { fmt::throw_exception("Unimplemented" HERE); } s32 cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PInfo info) { cellGifDec.warning("cellGifDecReadHeader(mainHandle=*0x%x, subHandle=*0x%x, info=*0x%x)", mainHandle, subHandle, info); const u32& fd = subHandle->fd; const u64& fileSize = subHandle->fileSize; CellGifDecInfo& current_info = subHandle->info; // Write the header to buffer u8 buffer[13]; switch (subHandle->src.srcSelect) { case CELL_GIFDEC_BUFFER: std::memcpy(buffer, subHandle->src.streamPtr.get_ptr(), sizeof(buffer)); break; case CELL_GIFDEC_FILE: { auto file = idm::get(fd); file->file.seek(0); file->file.read(buffer, sizeof(buffer)); break; } } if (*(be_t*)buffer != 0x47494638 || (*(le_t*)(buffer + 4) != 0x6139 && *(le_t*)(buffer + 4) != 0x6137)) // Error: The first 6 bytes are not a valid GIF signature { return CELL_GIFDEC_ERROR_STREAM_FORMAT; // Surprisingly there is no error code related with headerss } u8 packedField = buffer[10]; current_info.SWidth = buffer[6] + buffer[7] * 0x100; current_info.SHeight = buffer[8] + buffer[9] * 0x100; current_info.SGlobalColorTableFlag = packedField >> 7; current_info.SColorResolution = ((packedField >> 4) & 7)+1; current_info.SSortFlag = (packedField >> 3) & 1; current_info.SSizeOfGlobalColorTable = (packedField & 7)+1; current_info.SBackGroundColor = buffer[11]; current_info.SPixelAspectRatio = buffer[12]; *info = current_info; return CELL_OK; } s32 cellGifDecExtReadHeader() { fmt::throw_exception("Unimplemented" HERE); } s32 cellGifDecSetParameter(PMainHandle mainHandle, PSubHandle subHandle, PInParam inParam, POutParam outParam) { cellGifDec.warning("cellGifDecSetParameter(mainHandle=*0x%x, subHandle=*0x%x, inParam=*0x%x, outParam=*0x%x)", mainHandle, subHandle, inParam, outParam); CellGifDecInfo& current_info = subHandle->info; CellGifDecOutParam& current_outParam = subHandle->outParam; current_outParam.outputWidthByte = (current_info.SWidth * current_info.SColorResolution * 3)/8; current_outParam.outputWidth = current_info.SWidth; current_outParam.outputHeight = current_info.SHeight; current_outParam.outputColorSpace = inParam->colorSpace; switch ((u32)current_outParam.outputColorSpace) { case CELL_GIFDEC_RGBA: case CELL_GIFDEC_ARGB: current_outParam.outputComponents = 4; break; default: return CELL_GIFDEC_ERROR_ARG; // Not supported color space } current_outParam.outputBitDepth = 0; // Unimplemented current_outParam.useMemorySpace = 0; // Unimplemented *outParam = current_outParam; return CELL_OK; } s32 cellGifDecExtSetParameter() { fmt::throw_exception("Unimplemented" HERE); } s32 cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm::ptr data, PDataCtrlParam dataCtrlParam, PDataOutInfo dataOutInfo) { cellGifDec.warning("cellGifDecDecodeData(mainHandle=*0x%x, subHandle=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo); dataOutInfo->status = CELL_GIFDEC_DEC_STATUS_STOP; const u32& fd = subHandle->fd; const u64& fileSize = subHandle->fileSize; const CellGifDecOutParam& current_outParam = subHandle->outParam; //Copy the GIF file to a buffer std::unique_ptr gif(new u8[fileSize]); switch (subHandle->src.srcSelect) { case CELL_GIFDEC_BUFFER: std::memcpy(gif.get(), subHandle->src.streamPtr.get_ptr(), fileSize); break; case CELL_GIFDEC_FILE: { auto file = idm::get(fd); file->file.seek(0); file->file.read(gif.get(), fileSize); break; } } //Decode GIF 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(gif.get(), (s32)fileSize, &width, &height, &actual_components, 4), &::free ); if (!image) return CELL_GIFDEC_ERROR_STREAM_FORMAT; const int bytesPerLine = (u32)dataCtrlParam->outputBytesPerLine; const char nComponents = 4; uint image_size = width * height * nComponents; switch((u32)current_outParam.outputColorSpace) { case CELL_GIFDEC_RGBA: { if (bytesPerLine > width * nComponents) // 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 * i; memcpy(&data[dstOffset], &image.get()[srcOffset], linesize); } } else { memcpy(data.get_ptr(), image.get(), image_size); } } break; case CELL_GIFDEC_ARGB: { if (bytesPerLine > width * nComponents) // 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 * 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; default: return CELL_GIFDEC_ERROR_ARG; } dataOutInfo->status = CELL_GIFDEC_DEC_STATUS_FINISH; dataOutInfo->recordType = CELL_GIFDEC_RECORD_TYPE_IMAGE_DESC; return CELL_OK; } s32 cellGifDecExtDecodeData() { fmt::throw_exception("Unimplemented" HERE); } s32 cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle) { cellGifDec.warning("cellGifDecClose(mainHandle=*0x%x, subHandle=*0x%x)", mainHandle, subHandle); idm::remove(subHandle->fd); vm::dealloc(subHandle.addr()); return CELL_OK; } s32 cellGifDecDestroy(PMainHandle mainHandle) { UNIMPLEMENTED_FUNC(cellGifDec); return CELL_OK; } DECLARE(ppu_module_manager::cellGifDec)("cellGifDec", []() { REG_FUNC(cellGifDec, cellGifDecCreate); REG_FUNC(cellGifDec, cellGifDecExtCreate); REG_FUNC(cellGifDec, cellGifDecOpen); REG_FUNC(cellGifDec, cellGifDecReadHeader); REG_FUNC(cellGifDec, cellGifDecSetParameter); REG_FUNC(cellGifDec, cellGifDecDecodeData); REG_FUNC(cellGifDec, cellGifDecClose); REG_FUNC(cellGifDec, cellGifDecDestroy); REG_FUNC(cellGifDec, cellGifDecExtOpen); REG_FUNC(cellGifDec, cellGifDecExtReadHeader); REG_FUNC(cellGifDec, cellGifDecExtSetParameter); REG_FUNC(cellGifDec, cellGifDecExtDecodeData); });