mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 16:32:39 +00:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
d01826cdc9
1
.gitignore
vendored
1
.gitignore
vendored
@ -417,3 +417,4 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
# JetBrains
|
# JetBrains
|
||||||
.idea
|
.idea
|
||||||
|
cmake-build-*
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -102,3 +102,6 @@
|
|||||||
[submodule "externals/LibAtrac9"]
|
[submodule "externals/LibAtrac9"]
|
||||||
path = externals/LibAtrac9
|
path = externals/LibAtrac9
|
||||||
url = https://github.com/shadps4-emu/ext-LibAtrac9.git
|
url = https://github.com/shadps4-emu/ext-LibAtrac9.git
|
||||||
|
[submodule "externals/libpng"]
|
||||||
|
path = externals/libpng
|
||||||
|
url = https://github.com/pnggroup/libpng
|
||||||
|
@ -830,7 +830,7 @@ endif()
|
|||||||
|
|
||||||
create_target_directory_groups(shadps4)
|
create_target_directory_groups(shadps4)
|
||||||
|
|
||||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half)
|
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half zlib-ng::zlib png_static)
|
||||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
|
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
|
||||||
|
|
||||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||||
@ -860,9 +860,9 @@ if (NOT ENABLE_QT_GUI)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
|
||||||
target_link_libraries(shadps4 PRIVATE cryptoppwin zlib-ng::zlib)
|
target_link_libraries(shadps4 PRIVATE cryptoppwin)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp zlib-ng::zlib)
|
target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT_GUI)
|
if (ENABLE_QT_GUI)
|
||||||
|
13
externals/CMakeLists.txt
vendored
13
externals/CMakeLists.txt
vendored
@ -58,8 +58,10 @@ if (NOT TARGET zlib-ng::zlib)
|
|||||||
set(WITH_GTEST OFF)
|
set(WITH_GTEST OFF)
|
||||||
set(WITH_NEW_STRATEGIES ON)
|
set(WITH_NEW_STRATEGIES ON)
|
||||||
set(WITH_NATIVE_INSTRUCTIONS ON)
|
set(WITH_NATIVE_INSTRUCTIONS ON)
|
||||||
|
set(ZLIB_COMPAT ON CACHE BOOL "" FORCE)
|
||||||
add_subdirectory(zlib-ng)
|
add_subdirectory(zlib-ng)
|
||||||
add_library(zlib-ng::zlib ALIAS zlib)
|
add_library(zlib-ng::zlib ALIAS zlib)
|
||||||
|
add_library(ZLIB::ZLIB ALIAS zlib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SDL3
|
# SDL3
|
||||||
@ -153,6 +155,17 @@ if (NOT TARGET half::half)
|
|||||||
add_library(half::half ALIAS half)
|
add_library(half::half ALIAS half)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# libpng
|
||||||
|
set(PNG_SHARED OFF CACHE BOOL "" FORCE)
|
||||||
|
set(PNG_STATIC ON CACHE BOOL "" FORCE)
|
||||||
|
set(PNG_TESTS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(PNG_TOOLS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(SKIP_INSTALL_ALL OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib-ng" CACHE STRING "" FORCE)
|
||||||
|
set(ZLIB_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/zlib-ng/zlibstatic-ngd" CACHE STRING "" FORCE)
|
||||||
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libpng/zlib.h" "#include \"../zlib-ng/zlib.h\"")
|
||||||
|
add_subdirectory(libpng)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
# date
|
# date
|
||||||
if (NOT TARGET date::date-tz)
|
if (NOT TARGET date::date-tz)
|
||||||
|
1
externals/libpng
vendored
Submodule
1
externals/libpng
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c1cc0f3f4c3d4abd11ca68c59446a29ff6f95003
|
@ -1,31 +1,30 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <zlib-ng.h>
|
#include <zlib.h>
|
||||||
#include "common/io_file.h"
|
#include "common/io_file.h"
|
||||||
#include "common/logging/formatter.h"
|
#include "common/logging/formatter.h"
|
||||||
#include "core/file_format/pkg.h"
|
#include "core/file_format/pkg.h"
|
||||||
#include "core/file_format/pkg_type.h"
|
#include "core/file_format/pkg_type.h"
|
||||||
|
|
||||||
static void DecompressPFSC(std::span<const char> compressed_data,
|
static void DecompressPFSC(std::span<char> compressed_data, std::span<char> decompressed_data) {
|
||||||
std::span<char> decompressed_data) {
|
z_stream decompressStream;
|
||||||
zng_stream decompressStream;
|
|
||||||
decompressStream.zalloc = Z_NULL;
|
decompressStream.zalloc = Z_NULL;
|
||||||
decompressStream.zfree = Z_NULL;
|
decompressStream.zfree = Z_NULL;
|
||||||
decompressStream.opaque = Z_NULL;
|
decompressStream.opaque = Z_NULL;
|
||||||
|
|
||||||
if (zng_inflateInit(&decompressStream) != Z_OK) {
|
if (inflateInit(&decompressStream) != Z_OK) {
|
||||||
// std::cerr << "Error initializing zlib for deflation." << std::endl;
|
// std::cerr << "Error initializing zlib for deflation." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
decompressStream.avail_in = compressed_data.size();
|
decompressStream.avail_in = compressed_data.size();
|
||||||
decompressStream.next_in = reinterpret_cast<const Bytef*>(compressed_data.data());
|
decompressStream.next_in = reinterpret_cast<unsigned char*>(compressed_data.data());
|
||||||
decompressStream.avail_out = decompressed_data.size();
|
decompressStream.avail_out = decompressed_data.size();
|
||||||
decompressStream.next_out = reinterpret_cast<Bytef*>(decompressed_data.data());
|
decompressStream.next_out = reinterpret_cast<unsigned char*>(decompressed_data.data());
|
||||||
|
|
||||||
if (zng_inflate(&decompressStream, Z_FINISH)) {
|
if (inflate(&decompressStream, Z_FINISH)) {
|
||||||
}
|
}
|
||||||
if (zng_inflateEnd(&decompressStream) != Z_OK) {
|
if (inflateEnd(&decompressStream) != Z_OK) {
|
||||||
// std::cerr << "Error ending zlib inflate" << std::endl;
|
// std::cerr << "Error ending zlib inflate" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,7 +593,6 @@ constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303;
|
|||||||
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304;
|
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304;
|
||||||
|
|
||||||
// Videodec library
|
// Videodec library
|
||||||
|
|
||||||
constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000;
|
constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000;
|
||||||
constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001;
|
constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001;
|
||||||
constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002;
|
constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002;
|
||||||
@ -616,3 +615,14 @@ constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012;
|
|||||||
constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013;
|
constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013;
|
||||||
constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014;
|
constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014;
|
||||||
constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015;
|
constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015;
|
||||||
|
|
||||||
|
// PngDec library
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012;
|
||||||
|
constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020;
|
@ -1,55 +1,47 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <png.h>
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
#include "externals/stb_image.h"
|
|
||||||
#include "pngdec.h"
|
#include "pngdec.h"
|
||||||
|
|
||||||
namespace Libraries::PngDec {
|
namespace Libraries::PngDec {
|
||||||
|
|
||||||
void setImageInfoParams(OrbisPngDecImageInfo* imageInfo, int width, int height, int channels,
|
struct PngHandler {
|
||||||
bool isInterlaced, bool isTransparent) {
|
png_structp png_ptr;
|
||||||
if (imageInfo != nullptr) {
|
png_infop info_ptr;
|
||||||
imageInfo->imageWidth = width;
|
};
|
||||||
imageInfo->imageHeight = height;
|
|
||||||
imageInfo->bitDepth = 8; // always 8?
|
static inline OrbisPngDecColorSpace MapPngColor(int color) {
|
||||||
switch (channels) { // clut missing
|
switch (color) {
|
||||||
case 1:
|
case PNG_COLOR_TYPE_GRAY:
|
||||||
imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE;
|
return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE;
|
||||||
break;
|
|
||||||
case 2:
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||||||
imageInfo->colorSpace =
|
return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA;
|
||||||
OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA;
|
|
||||||
break;
|
case PNG_COLOR_TYPE_PALETTE:
|
||||||
case 3:
|
return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_CLUT;
|
||||||
imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB;
|
|
||||||
break;
|
case PNG_COLOR_TYPE_RGB:
|
||||||
case 4:
|
return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB;
|
||||||
imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGBA;
|
|
||||||
break;
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||||
default:
|
return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGBA;
|
||||||
imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
imageInfo->imageFlag = 0;
|
|
||||||
if (isInterlaced) {
|
|
||||||
imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE;
|
|
||||||
}
|
|
||||||
if (isTransparent) {
|
|
||||||
imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UNREACHABLE_MSG("unknown png color type");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checktRNS(const u8* png_raw, int size) {
|
void PngDecError(png_structp png_ptr, png_const_charp error_message) {
|
||||||
for (int i = 30; i < size - 4; i += 4) {
|
LOG_ERROR(Lib_Png, "PNG error {}", error_message);
|
||||||
if (std::memcmp(png_raw + i, "tRNS", 4) == 0) {
|
}
|
||||||
return true;
|
|
||||||
}
|
void PngDecWarning(png_structp png_ptr, png_const_charp error_message) {
|
||||||
}
|
LOG_ERROR(Lib_Png, "PNG warning {}", error_message);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress,
|
s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress,
|
||||||
@ -66,11 +58,21 @@ s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memo
|
|||||||
LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth);
|
LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth);
|
||||||
return ORBIS_PNG_DEC_ERROR_INVALID_SIZE;
|
return ORBIS_PNG_DEC_ERROR_INVALID_SIZE;
|
||||||
}
|
}
|
||||||
const OrbisPngDecCreateParam* nextParam = param + 1;
|
auto pngh = (PngHandler*)memoryAddress;
|
||||||
int ret = (8 << (reinterpret_cast<uintptr_t>(nextParam) & 0x1f)) *
|
|
||||||
(param->maxImageWidth + 0x47U & 0xfffffff8) +
|
pngh->png_ptr =
|
||||||
0xd000;
|
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PngDecError, PngDecWarning);
|
||||||
*handle = reinterpret_cast<OrbisPngDecHandle>(ret);
|
|
||||||
|
if (pngh->png_ptr == nullptr)
|
||||||
|
return ORBIS_PNG_DEC_ERROR_FATAL;
|
||||||
|
|
||||||
|
pngh->info_ptr = png_create_info_struct(pngh->png_ptr);
|
||||||
|
if (pngh->info_ptr == nullptr) {
|
||||||
|
png_destroy_read_struct(&pngh->png_ptr, nullptr, nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*handle = pngh;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,22 +90,91 @@ s32 PS4_SYSV_ABI scePngDecDecode(OrbisPngDecHandle handle, const OrbisPngDecDeco
|
|||||||
LOG_ERROR(Lib_Png, "invalid image address!");
|
LOG_ERROR(Lib_Png, "invalid image address!");
|
||||||
return ORBIS_PNG_DEC_ERROR_INVALID_ADDR;
|
return ORBIS_PNG_DEC_ERROR_INVALID_ADDR;
|
||||||
}
|
}
|
||||||
|
LOG_TRACE(Lib_Png,
|
||||||
|
"pngMemSize = {} , imageMemSize = {} , pixelFormat = {} , alphaValue = {} , "
|
||||||
|
"imagePitch = {}",
|
||||||
|
param->pngMemSize, param->imageMemSize, param->pixelFormat, param->alphaValue,
|
||||||
|
param->imagePitch);
|
||||||
|
|
||||||
int width, height, channels;
|
auto pngh = (PngHandler*)handle;
|
||||||
const u8* png_raw = (const u8*)param->pngMemAddr;
|
|
||||||
u8* img = stbi_load_from_memory(png_raw, param->pngMemSize, &width, &height, &channels,
|
struct pngstruct {
|
||||||
STBI_rgb_alpha); // STBI_rgb_alpha?
|
const u8* data;
|
||||||
if (!img) {
|
size_t size;
|
||||||
LOG_ERROR(Lib_Png, "Decoding failed!");
|
u64 offset;
|
||||||
return ORBIS_PNG_DEC_ERROR_DECODE_ERROR;
|
} pngdata = {
|
||||||
|
.data = (const u8*)param->pngMemAddr,
|
||||||
|
.size = param->pngMemSize,
|
||||||
|
.offset = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read png from memory
|
||||||
|
png_set_read_fn(pngh->png_ptr, (void*)&pngdata,
|
||||||
|
[](png_structp ps, png_bytep data, png_size_t len) {
|
||||||
|
if (len == 0)
|
||||||
|
return;
|
||||||
|
auto pngdata = (pngstruct*)png_get_io_ptr(ps);
|
||||||
|
::memcpy(data, pngdata->data + pngdata->offset, len);
|
||||||
|
pngdata->offset += len;
|
||||||
|
});
|
||||||
|
|
||||||
|
u32 width, height;
|
||||||
|
int color_type, bit_depth;
|
||||||
|
png_read_info(pngh->png_ptr, pngh->info_ptr);
|
||||||
|
|
||||||
|
width = png_get_image_width(pngh->png_ptr, pngh->info_ptr);
|
||||||
|
height = png_get_image_height(pngh->png_ptr, pngh->info_ptr);
|
||||||
|
color_type = MapPngColor(png_get_color_type(pngh->png_ptr, pngh->info_ptr));
|
||||||
|
bit_depth = png_get_bit_depth(pngh->png_ptr, pngh->info_ptr);
|
||||||
|
|
||||||
|
if (imageInfo != nullptr) {
|
||||||
|
imageInfo->bitDepth = bit_depth;
|
||||||
|
imageInfo->imageWidth = width;
|
||||||
|
imageInfo->imageHeight = height;
|
||||||
|
imageInfo->colorSpace = color_type;
|
||||||
|
imageInfo->imageFlag = 0;
|
||||||
|
if (png_get_interlace_type(pngh->png_ptr, pngh->info_ptr) == 1) {
|
||||||
|
imageInfo->imageFlag |= OrbisPngDecImageFlag::ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE;
|
||||||
|
}
|
||||||
|
if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) {
|
||||||
|
|
||||||
|
imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool isInterlaced = (png_raw[28] == 1);
|
|
||||||
bool isTransparent = checktRNS(png_raw, param->pngMemSize);
|
if (bit_depth == 16)
|
||||||
setImageInfoParams(imageInfo, width, height, channels, isInterlaced, isTransparent);
|
png_set_strip_16(pngh->png_ptr);
|
||||||
u8* imageBuffer = (u8*)(param->imageMemAddr);
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||||
memcpy(imageBuffer, img, width * height * 4); // copy/pass decoded data
|
png_set_palette_to_rgb(pngh->png_ptr);
|
||||||
stbi_image_free(img);
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||||
return 0;
|
png_set_expand_gray_1_2_4_to_8(pngh->png_ptr);
|
||||||
|
if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS))
|
||||||
|
png_set_tRNS_to_alpha(pngh->png_ptr);
|
||||||
|
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
|
png_set_gray_to_rgb(pngh->png_ptr);
|
||||||
|
if (param->pixelFormat == OrbisPngDecPixelFormat::ORBIS_PNG_DEC_PIXEL_FORMAT_B8G8R8A8)
|
||||||
|
png_set_bgr(pngh->png_ptr);
|
||||||
|
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
|
||||||
|
color_type == PNG_COLOR_TYPE_PALETTE)
|
||||||
|
png_set_add_alpha(pngh->png_ptr, param->alphaValue, PNG_FILLER_AFTER);
|
||||||
|
|
||||||
|
int pass = png_set_interlace_handling(pngh->png_ptr);
|
||||||
|
png_read_update_info(pngh->png_ptr, pngh->info_ptr);
|
||||||
|
|
||||||
|
auto const numChannels = png_get_channels(pngh->png_ptr, pngh->info_ptr);
|
||||||
|
auto horizontal_bytes = numChannels * width;
|
||||||
|
|
||||||
|
int stride = param->imagePitch > 0 ? param->imagePitch : horizontal_bytes;
|
||||||
|
|
||||||
|
for (int j = 0; j < pass; j++) { // interlaced
|
||||||
|
auto ptr = (png_bytep)param->imageMemAddr;
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
png_read_row(pngh->png_ptr, ptr, nullptr);
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (width > 32767 || height > 32767) ? 0 : (width << 16) | height;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI scePngDecDecodeWithInputControl() {
|
s32 PS4_SYSV_ABI scePngDecDecodeWithInputControl() {
|
||||||
@ -112,8 +183,8 @@ s32 PS4_SYSV_ABI scePngDecDecodeWithInputControl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI scePngDecDelete(OrbisPngDecHandle handle) {
|
s32 PS4_SYSV_ABI scePngDecDelete(OrbisPngDecHandle handle) {
|
||||||
handle = nullptr; // ?
|
auto pngh = *(PngHandler**)handle;
|
||||||
LOG_ERROR(Lib_Png, "(STUBBED)called");
|
png_destroy_read_struct(&pngh->png_ptr, &pngh->info_ptr, nullptr);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,16 +194,60 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param,
|
|||||||
LOG_ERROR(Lib_Png, "Invalid param!");
|
LOG_ERROR(Lib_Png, "Invalid param!");
|
||||||
return ORBIS_PNG_DEC_ERROR_INVALID_PARAM;
|
return ORBIS_PNG_DEC_ERROR_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
int width, height, channels;
|
|
||||||
const u8* png_raw = (const u8*)(param->pngMemAddr);
|
u8 header[8];
|
||||||
int img = stbi_info_from_memory(png_raw, param->pngMemSize, &width, &height, &channels);
|
memcpy(header, param->pngMemAddr, 8);
|
||||||
if (img == 0) {
|
// Check if the header indicates a valid PNG file
|
||||||
LOG_ERROR(Lib_Png, "Decoding failed!");
|
if (png_sig_cmp(header, 0, 8)) {
|
||||||
return ORBIS_PNG_DEC_ERROR_DECODE_ERROR;
|
LOG_ERROR(Lib_Png, "Memory doesn't contain a valid png file");
|
||||||
|
return ORBIS_PNG_DEC_ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
bool isInterlaced = (png_raw[28] == 1);
|
// Create a libpng structure, also pass our custom error/warning functions
|
||||||
bool isTransparent = checktRNS(png_raw, param->pngMemSize);
|
auto png_ptr =
|
||||||
setImageInfoParams(imageInfo, width, height, channels, isInterlaced, isTransparent);
|
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PngDecError, PngDecWarning);
|
||||||
|
|
||||||
|
// Create a libpng info structure
|
||||||
|
auto info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
|
||||||
|
struct pngstruct {
|
||||||
|
const u8* data;
|
||||||
|
size_t size;
|
||||||
|
u64 offset;
|
||||||
|
} pngdata = {
|
||||||
|
.data = (const u8*)param->pngMemAddr,
|
||||||
|
.size = param->pngMemSize,
|
||||||
|
.offset = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
png_set_read_fn(png_ptr, (void*)&pngdata, [](png_structp ps, png_bytep data, png_size_t len) {
|
||||||
|
auto pngdata = (pngstruct*)png_get_io_ptr(ps);
|
||||||
|
::memcpy(data, pngdata->data + pngdata->offset, len);
|
||||||
|
pngdata->offset += len;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now call png_read_info with our pngPtr as image handle, and infoPtr to receive the file
|
||||||
|
// info.
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
imageInfo->imageWidth = png_get_image_width(png_ptr, info_ptr);
|
||||||
|
imageInfo->imageHeight = png_get_image_height(png_ptr, info_ptr);
|
||||||
|
imageInfo->colorSpace = MapPngColor(png_get_color_type(png_ptr, info_ptr));
|
||||||
|
imageInfo->bitDepth = png_get_bit_depth(png_ptr, info_ptr);
|
||||||
|
imageInfo->imageFlag = 0;
|
||||||
|
if (png_get_interlace_type(png_ptr, info_ptr) == 1) {
|
||||||
|
imageInfo->imageFlag |= OrbisPngDecImageFlag::ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE;
|
||||||
|
}
|
||||||
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||||
|
|
||||||
|
imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||||
|
LOG_TRACE(
|
||||||
|
Lib_Png,
|
||||||
|
"imageWidth = {} , imageHeight = {} , colorSpace = {} , bitDepth = {} , imageFlag = {}",
|
||||||
|
imageInfo->imageWidth, imageInfo->imageHeight, imageInfo->colorSpace, imageInfo->bitDepth,
|
||||||
|
imageInfo->imageFlag);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,9 +264,7 @@ s32 PS4_SYSV_ABI scePngDecQueryMemorySize(const OrbisPngDecCreateParam* param) {
|
|||||||
LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth);
|
LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth);
|
||||||
return ORBIS_PNG_DEC_ERROR_INVALID_SIZE;
|
return ORBIS_PNG_DEC_ERROR_INVALID_SIZE;
|
||||||
}
|
}
|
||||||
int ret =
|
return sizeof(PngHandler);
|
||||||
(8 << ((u8)param->attribute & 0x1f)) * (param->maxImageWidth + 0x47U & 0xfffffff8) + 0xd090;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym) {
|
void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym) {
|
||||||
|
@ -8,54 +8,55 @@
|
|||||||
namespace Core::Loader {
|
namespace Core::Loader {
|
||||||
class SymbolsResolver;
|
class SymbolsResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Libraries::PngDec {
|
namespace Libraries::PngDec {
|
||||||
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001;
|
enum OrbisPngDecColorSpace {
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002;
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003;
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004;
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005;
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010;
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011;
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012;
|
|
||||||
constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020;
|
|
||||||
|
|
||||||
typedef struct OrbisPngDecParseParam {
|
|
||||||
const void* pngMemAddr;
|
|
||||||
u32 pngMemSize;
|
|
||||||
u32 reserved;
|
|
||||||
} OrbisPngDecParseParam;
|
|
||||||
|
|
||||||
typedef struct OrbisPngDecImageInfo {
|
|
||||||
u32 imageWidth;
|
|
||||||
u32 imageHeight;
|
|
||||||
u16 colorSpace;
|
|
||||||
u16 bitDepth;
|
|
||||||
u32 imageFlag;
|
|
||||||
} OrbisPngDecImageInfo;
|
|
||||||
|
|
||||||
typedef enum OrbisPngDecColorSpace {
|
|
||||||
ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE = 2,
|
ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE = 2,
|
||||||
ORBIS_PNG_DEC_COLOR_SPACE_RGB,
|
ORBIS_PNG_DEC_COLOR_SPACE_RGB,
|
||||||
ORBIS_PNG_DEC_COLOR_SPACE_CLUT,
|
ORBIS_PNG_DEC_COLOR_SPACE_CLUT,
|
||||||
ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA = 18,
|
ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA = 18,
|
||||||
ORBIS_PNG_DEC_COLOR_SPACE_RGBA
|
ORBIS_PNG_DEC_COLOR_SPACE_RGBA
|
||||||
} ScePngDecColorSpace;
|
};
|
||||||
|
|
||||||
typedef enum OrbisPngDecImageFlag {
|
enum OrbisPngDecImageFlag {
|
||||||
ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE = 1,
|
ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE = 1,
|
||||||
ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST = 2
|
ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST = 2
|
||||||
} OrbisPngDecImageFlag;
|
};
|
||||||
|
|
||||||
typedef struct OrbisPngDecCreateParam {
|
enum OrbisPngDecPixelFormat {
|
||||||
|
ORBIS_PNG_DEC_PIXEL_FORMAT_R8G8B8A8 = 0,
|
||||||
|
ORBIS_PNG_DEC_PIXEL_FORMAT_B8G8R8A8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OrbisPngDecAttribute {
|
||||||
|
ORBIS_PNG_DEC_ATTRIBUTE_NONE = 0,
|
||||||
|
ORBIS_PNG_DEC_ATTRIBUTE_BIT_DEPTH_16
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisPngDecParseParam {
|
||||||
|
const void* pngMemAddr;
|
||||||
|
u32 pngMemSize;
|
||||||
|
u32 reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisPngDecImageInfo {
|
||||||
|
u32 imageWidth;
|
||||||
|
u32 imageHeight;
|
||||||
|
u16 colorSpace;
|
||||||
|
u16 bitDepth;
|
||||||
|
u32 imageFlag;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisPngDecCreateParam {
|
||||||
u32 thisSize;
|
u32 thisSize;
|
||||||
u32 attribute;
|
u32 attribute;
|
||||||
u32 maxImageWidth;
|
u32 maxImageWidth;
|
||||||
} OrbisPngDecCreateParam;
|
};
|
||||||
|
|
||||||
typedef void* OrbisPngDecHandle;
|
typedef void* OrbisPngDecHandle;
|
||||||
|
|
||||||
typedef struct OrbisPngDecDecodeParam {
|
struct OrbisPngDecDecodeParam {
|
||||||
const void* pngMemAddr;
|
const void* pngMemAddr;
|
||||||
void* imageMemAddr;
|
void* imageMemAddr;
|
||||||
u32 pngMemSize;
|
u32 pngMemSize;
|
||||||
@ -63,7 +64,7 @@ typedef struct OrbisPngDecDecodeParam {
|
|||||||
u16 pixelFormat;
|
u16 pixelFormat;
|
||||||
u16 alphaValue;
|
u16 alphaValue;
|
||||||
u32 imagePitch;
|
u32 imagePitch;
|
||||||
} OrbisPngDecDecodeParam;
|
};
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress,
|
s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress,
|
||||||
u32 memorySize, OrbisPngDecHandle* handle);
|
u32 memorySize, OrbisPngDecHandle* handle);
|
||||||
|
@ -13,33 +13,6 @@
|
|||||||
#include "imgui/imgui_std.h"
|
#include "imgui/imgui_std.h"
|
||||||
#include "savedatadialog_ui.h"
|
#include "savedatadialog_ui.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <date/tz.h>
|
|
||||||
|
|
||||||
// Need to make a copy of the formatter for std::chrono::local_time for use with date::local_time
|
|
||||||
template <typename Duration, typename Char>
|
|
||||||
struct fmt::formatter<date::local_time<Duration>, Char> : formatter<std::tm, Char> {
|
|
||||||
FMT_CONSTEXPR formatter() {
|
|
||||||
this->format_str_ = fmt::detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(date::local_time<Duration> val, FormatContext& ctx) const -> decltype(ctx.out()) {
|
|
||||||
using period = typename Duration::period;
|
|
||||||
if (period::num == 1 && period::den == 1 &&
|
|
||||||
!std::is_floating_point<typename Duration::rep>::value) {
|
|
||||||
return formatter<std::tm, Char>::format(
|
|
||||||
localtime(fmt::detail::to_time_t(date::current_zone()->to_sys(val))), ctx);
|
|
||||||
}
|
|
||||||
auto epoch = val.time_since_epoch();
|
|
||||||
auto subsecs = fmt::detail::duration_cast<Duration>(
|
|
||||||
epoch - fmt::detail::duration_cast<std::chrono::seconds>(epoch));
|
|
||||||
return formatter<std::tm, Char>::do_format(
|
|
||||||
localtime(fmt::detail::to_time_t(date::current_zone()->to_sys(val))), ctx, &subsecs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace ImGui;
|
using namespace ImGui;
|
||||||
using namespace Libraries::CommonDialog;
|
using namespace Libraries::CommonDialog;
|
||||||
using Common::ElfInfo;
|
using Common::ElfInfo;
|
||||||
@ -125,12 +98,9 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) {
|
|||||||
param_sfo.Open(param_sfo_path);
|
param_sfo.Open(param_sfo_path);
|
||||||
|
|
||||||
auto last_write = param_sfo.GetLastWrite();
|
auto last_write = param_sfo.GetLastWrite();
|
||||||
#ifdef __APPLE__
|
std::string date_str =
|
||||||
auto t = date::zoned_time{date::current_zone(), last_write};
|
fmt::format("{:%d %b, %Y %R}",
|
||||||
#else
|
fmt::localtime(std::chrono::system_clock::to_time_t(last_write)));
|
||||||
auto t = std::chrono::zoned_time{std::chrono::current_zone(), last_write};
|
|
||||||
#endif
|
|
||||||
std::string date_str = fmt::format("{:%d %b, %Y %R}", t.get_local_time());
|
|
||||||
|
|
||||||
size_t size = Common::FS::GetDirectorySize(dir_path);
|
size_t size = Common::FS::GetDirectorySize(dir_path);
|
||||||
std::string size_str = SpaceSizeToString(size);
|
std::string size_str = SpaceSizeToString(size);
|
||||||
|
@ -162,8 +162,7 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||||||
if (!playgo->Open(filepath)) {
|
if (!playgo->Open(filepath)) {
|
||||||
LOG_ERROR(Loader, "PlayGo: unable to open file");
|
LOG_ERROR(Loader, "PlayGo: unable to open file");
|
||||||
}
|
}
|
||||||
} else if (entry.path().filename() == "pic0.png" ||
|
} else if (entry.path().filename() == "pic1.png") {
|
||||||
entry.path().filename() == "pic1.png") {
|
|
||||||
auto* splash = Common::Singleton<Splash>::Instance();
|
auto* splash = Common::Singleton<Splash>::Instance();
|
||||||
if (splash->IsLoaded()) {
|
if (splash->IsLoaded()) {
|
||||||
continue;
|
continue;
|
||||||
|
109
src/main.cpp
109
src/main.cpp
@ -1,7 +1,13 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "functional"
|
||||||
|
#include "iostream"
|
||||||
|
#include "string"
|
||||||
|
#include "unordered_map"
|
||||||
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include "common/config.h"
|
||||||
#include "common/memory_patcher.h"
|
#include "common/memory_patcher.h"
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
|
|
||||||
@ -14,26 +20,105 @@ int main(int argc, char* argv[]) {
|
|||||||
SetConsoleOutputCP(CP_UTF8);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool has_game_argument = false;
|
||||||
|
std::string game_path;
|
||||||
|
|
||||||
|
// Map of argument strings to lambda functions
|
||||||
|
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
|
||||||
|
{"-h",
|
||||||
|
[&](int&) {
|
||||||
|
std::cout << "Usage: shadps4 [options] <elf or eboot.bin path>\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -g, --game <path|ID> Specify game path to launch\n"
|
||||||
|
" -p, --patch <patch_file> Apply specified patch file\n"
|
||||||
|
" -f, --fullscreen <true|false> Specify window initial fullscreen "
|
||||||
|
"state. Does not overwrite the config file."
|
||||||
|
" -h, --help Display this help message\n";
|
||||||
|
exit(0);
|
||||||
|
}},
|
||||||
|
{"--help", [&](int& i) { arg_map["-h"](i); }},
|
||||||
|
|
||||||
|
{"-g",
|
||||||
|
[&](int& i) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
game_path = argv[++i];
|
||||||
|
has_game_argument = true;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Error: Missing argument for -g/--game\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"--game", [&](int& i) { arg_map["-g"](i); }},
|
||||||
|
|
||||||
|
{"-p",
|
||||||
|
[&](int& i) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
MemoryPatcher::patchFile = argv[++i];
|
||||||
|
} else {
|
||||||
|
std::cerr << "Error: Missing argument for -p/--patch\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"--patch", [&](int& i) { arg_map["-p"](i); }},
|
||||||
|
{"-f",
|
||||||
|
[&](int& i) {
|
||||||
|
if (++i >= argc) {
|
||||||
|
std::cerr << "Error: Missing argument for -f/--fullscreen\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
std::string f_param(argv[i]);
|
||||||
|
bool is_fullscreen;
|
||||||
|
if (f_param == "true") {
|
||||||
|
is_fullscreen = true;
|
||||||
|
} else if (f_param == "false") {
|
||||||
|
is_fullscreen = false;
|
||||||
|
} else {
|
||||||
|
std::cerr
|
||||||
|
<< "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Set fullscreen mode without saving it to config file
|
||||||
|
Config::setFullscreenMode(is_fullscreen);
|
||||||
|
}},
|
||||||
|
{"--fullscreen", [&](int& i) { arg_map["-f"](i); }},
|
||||||
|
};
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
fmt::print("Usage: {} <elf or eboot.bin path>\n", argv[0]);
|
int dummy = 0; // one does not simply pass 0 directly
|
||||||
return -1;
|
arg_map.at("-h")(dummy);
|
||||||
}
|
|
||||||
// check if eboot file exists
|
|
||||||
if (!std::filesystem::exists(argv[1])) {
|
|
||||||
fmt::print("Eboot.bin file not found\n");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < argc; i++) {
|
// Parse command-line arguments using the map
|
||||||
std::string curArg = argv[i];
|
for (int i = 1; i < argc; ++i) {
|
||||||
if (curArg == "-p") {
|
std::string cur_arg = argv[i];
|
||||||
std::string patchFile = argv[i + 1];
|
auto it = arg_map.find(cur_arg);
|
||||||
MemoryPatcher::patchFile = patchFile;
|
if (it != arg_map.end()) {
|
||||||
|
it->second(i); // Call the associated lambda function
|
||||||
|
} else if (i == argc - 1 && !has_game_argument) {
|
||||||
|
// Assume the last argument is the game file if not specified via -g/--game
|
||||||
|
game_path = argv[i];
|
||||||
|
has_game_argument = true;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n";
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!has_game_argument) {
|
||||||
|
std::cerr << "Error: Please provide a game path or ID.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the game path or ID exists
|
||||||
|
if (!std::filesystem::exists(game_path)) {
|
||||||
|
std::cerr << "Error: Game file not found\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the emulator with the specified game
|
||||||
Core::Emulator emulator;
|
Core::Emulator emulator;
|
||||||
emulator.Run(argv[1]);
|
emulator.Run(game_path);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "iostream"
|
||||||
|
#include "unordered_map"
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/memory_patcher.h"
|
#include "common/memory_patcher.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
@ -26,10 +29,90 @@ int main(int argc, char* argv[]) {
|
|||||||
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||||
Config::load(user_dir / "config.toml");
|
Config::load(user_dir / "config.toml");
|
||||||
|
|
||||||
// Check if elf or eboot.bin path was passed as a command line argument
|
|
||||||
bool has_command_line_argument = argc > 1;
|
bool has_command_line_argument = argc > 1;
|
||||||
|
bool show_gui = false, has_game_argument = false;
|
||||||
|
std::string gamePath;
|
||||||
|
|
||||||
// Check if the game install directory is set
|
// Map of argument strings to lambda functions
|
||||||
|
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
|
||||||
|
{"-h",
|
||||||
|
[&](int&) {
|
||||||
|
std::cout << "Usage: shadps4 [options]\n"
|
||||||
|
"Options:\n"
|
||||||
|
" No arguments: Opens the GUI.\n"
|
||||||
|
" -g, --game <path|ID> Specify <eboot.bin or elf path> or "
|
||||||
|
"<game ID (CUSAXXXXX)> to launch\n"
|
||||||
|
" -p, --patch <patch_file> Apply specified patch file\n"
|
||||||
|
" -s, --show-gui Show the GUI\n"
|
||||||
|
" -f, --fullscreen <true|false> Specify window initial fullscreen "
|
||||||
|
"state. Does not overwrite the config file."
|
||||||
|
" -h, --help Display this help message\n";
|
||||||
|
exit(0);
|
||||||
|
}},
|
||||||
|
{"--help", [&](int& i) { arg_map["-h"](i); }}, // Redirect --help to -h
|
||||||
|
|
||||||
|
{"-s", [&](int&) { show_gui = true; }},
|
||||||
|
{"--show-gui", [&](int& i) { arg_map["-s"](i); }},
|
||||||
|
|
||||||
|
{"-g",
|
||||||
|
[&](int& i) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
gamePath = argv[++i];
|
||||||
|
has_game_argument = true;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Error: Missing argument for -g/--game\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"--game", [&](int& i) { arg_map["-g"](i); }},
|
||||||
|
|
||||||
|
{"-p",
|
||||||
|
[&](int& i) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
MemoryPatcher::patchFile = argv[++i];
|
||||||
|
} else {
|
||||||
|
std::cerr << "Error: Missing argument for -p\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"--patch", [&](int& i) { arg_map["-p"](i); }},
|
||||||
|
{"-f",
|
||||||
|
[&](int& i) {
|
||||||
|
if (++i >= argc) {
|
||||||
|
std::cerr
|
||||||
|
<< "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
std::string f_param(argv[i]);
|
||||||
|
bool is_fullscreen;
|
||||||
|
if (f_param == "true") {
|
||||||
|
is_fullscreen = true;
|
||||||
|
} else if (f_param == "false") {
|
||||||
|
is_fullscreen = false;
|
||||||
|
} else {
|
||||||
|
std::cerr
|
||||||
|
<< "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
// Set fullscreen mode without saving it to config file
|
||||||
|
Config::setFullscreenMode(is_fullscreen);
|
||||||
|
}},
|
||||||
|
{"--fullscreen", [&](int& i) { arg_map["-f"](i); }},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse command-line arguments using the map
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
std::string cur_arg = argv[i];
|
||||||
|
auto it = arg_map.find(cur_arg);
|
||||||
|
if (it != arg_map.end()) {
|
||||||
|
it->second(i); // Call the associated lambda function
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no game directory is set and no command line argument, prompt for it
|
||||||
if (Config::getGameInstallDirs().empty() && !has_command_line_argument) {
|
if (Config::getGameInstallDirs().empty() && !has_command_line_argument) {
|
||||||
GameInstallDialog dlg;
|
GameInstallDialog dlg;
|
||||||
dlg.exec();
|
dlg.exec();
|
||||||
@ -40,21 +123,46 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
// Initialize the main window
|
// Initialize the main window
|
||||||
MainWindow* m_main_window = new MainWindow(nullptr);
|
MainWindow* m_main_window = new MainWindow(nullptr);
|
||||||
m_main_window->Init();
|
if ((has_command_line_argument && show_gui) || !has_command_line_argument) {
|
||||||
|
m_main_window->Init();
|
||||||
// Check for command line arguments
|
|
||||||
if (has_command_line_argument) {
|
|
||||||
Core::Emulator emulator;
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
std::string curArg = argv[i];
|
|
||||||
if (curArg == "-p") {
|
|
||||||
std::string patchFile = argv[i + 1];
|
|
||||||
MemoryPatcher::patchFile = patchFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
emulator.Run(argv[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the Qt application
|
if (has_command_line_argument && !has_game_argument) {
|
||||||
|
std::cerr << "Error: Please provide a game path or ID.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process game path or ID if provided
|
||||||
|
if (has_game_argument) {
|
||||||
|
std::filesystem::path game_file_path(gamePath);
|
||||||
|
|
||||||
|
// Check if the provided path is a valid file
|
||||||
|
if (!std::filesystem::exists(game_file_path)) {
|
||||||
|
// If not a file, treat it as a game ID and search in install directories
|
||||||
|
bool game_found = false;
|
||||||
|
for (const auto& install_dir : Config::getGameInstallDirs()) {
|
||||||
|
auto potential_game_path = install_dir / gamePath / "eboot.bin";
|
||||||
|
if (std::filesystem::exists(potential_game_path)) {
|
||||||
|
game_file_path = potential_game_path;
|
||||||
|
game_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!game_found) {
|
||||||
|
std::cerr << "Error: Game ID or file path not found: " << gamePath << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the emulator with the resolved game path
|
||||||
|
Core::Emulator emulator;
|
||||||
|
emulator.Run(game_file_path.string());
|
||||||
|
if (!show_gui) {
|
||||||
|
return 0; // Exit after running the emulator without showing the GUI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the main window and run the Qt application
|
||||||
|
m_main_window->show();
|
||||||
return a.exec();
|
return a.exec();
|
||||||
}
|
}
|
@ -362,7 +362,7 @@ bool BufferCache::IsRegionRegistered(VAddr addr, size_t size) {
|
|||||||
if (buf_start_addr < end_addr && addr < buf_end_addr) {
|
if (buf_start_addr < end_addr && addr < buf_end_addr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
page = Common::DivCeil(end_addr, CACHING_PAGESIZE);
|
page = Common::DivCeil(buf_end_addr, CACHING_PAGESIZE);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user