diff --git a/CMakeLists.txt b/CMakeLists.txt index 9810ff92..017b8850 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16.3) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) if (NOT CMAKE_BUILD_TYPE) @@ -127,11 +127,21 @@ set(HOST_SOURCES src/Emulator/Host/controller.cpp src/Emulator/Host/controller.h ) +set(COMMON src/common/types.h + src/common/endian.h + src/common/concepts.h + src/common/io_file.cpp + src/common/io_file.h +) + +set(FILE_FORMAT src/core/file_format/psf.cpp + src/core/file_format/psf.h +) + add_executable(shadps4 src/common/assert.cpp src/common/assert.h src/common/bounded_threadsafe_queue.h - src/common/concepts.h src/common/debug.h src/common/disassembler.cpp src/common/disassembler.h @@ -139,8 +149,6 @@ add_executable(shadps4 src/common/discord.h src/common/error.cpp src/common/error.h - src/common/io_file.cpp - src/common/io_file.h src/common/path_util.cpp src/common/path_util.h src/common/logging/backend.cpp @@ -162,7 +170,6 @@ add_executable(shadps4 src/common/string_util.h src/common/thread.cpp src/common/thread.h - src/common/types.h src/common/uint128.h src/common/version.h ${LIBC_SOURCES} @@ -227,6 +234,8 @@ add_executable(shadps4 src/core/hle/libraries/libkernel/time_management.h src/core/tls.cpp src/core/tls.h + ${COMMON} + ${FILE_FORMAT} ) create_target_directory_groups(shadps4) diff --git a/src/common/endian.h b/src/common/endian.h new file mode 100644 index 00000000..4b0b70cd --- /dev/null +++ b/src/common/endian.h @@ -0,0 +1,242 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +/** + * (c) 2014-2016 Alexandro Sanchez Bach. All rights reserved. + * Released under GPL v2 license. Read LICENSE for more details. + * Some modifications for using with shadps4 by georgemoralis + */ + +#pragma once + +#include +#include +#include "common/types.h" + +namespace Common { + +/** + * Native endianness + */ +template +using NativeEndian = T; + +template +class SwappedEndian { +public: + const T& Raw() const { + return data; + } + + T Swap() const { + return std::byteswap(data); + } + + void FromRaw(const T& value) { + data = value; + } + + void FromSwap(const T& value) { + data = std::byteswap(value); + } + + operator const T() const { + return Swap(); + } + + template + explicit operator const SwappedEndian() const { + SwappedEndian res; + if (sizeof(T1) < sizeof(T)) { + res.FromRaw(Raw() >> ((sizeof(T) - sizeof(T1)) * 8)); + } else if (sizeof(T1) > sizeof(T)) { + res.FromSwap(Swap()); + } else { + res.FromRaw(Raw()); + } + return res; + } + + SwappedEndian& operator=(const T& right) { + FromSwap(right); + return *this; + } + SwappedEndian& operator=(const SwappedEndian& right) = default; + + template + SwappedEndian& operator+=(T1 right) { + return *this = T(*this) + right; + } + template + SwappedEndian& operator-=(T1 right) { + return *this = T(*this) - right; + } + template + SwappedEndian& operator*=(T1 right) { + return *this = T(*this) * right; + } + template + SwappedEndian& operator/=(T1 right) { + return *this = T(*this) / right; + } + template + SwappedEndian& operator%=(T1 right) { + return *this = T(*this) % right; + } + template + SwappedEndian& operator&=(T1 right) { + return *this = T(*this) & right; + } + template + SwappedEndian& operator|=(T1 right) { + return *this = T(*this) | right; + } + template + SwappedEndian& operator^=(T1 right) { + return *this = T(*this) ^ right; + } + template + SwappedEndian& operator<<=(T1 right) { + return *this = T(*this) << right; + } + template + SwappedEndian& operator>>=(T1 right) { + return *this = T(*this) >> right; + } + + template + SwappedEndian& operator+=(const SwappedEndian& right) { + return *this = Swap() + right.Swap(); + } + template + SwappedEndian& operator-=(const SwappedEndian& right) { + return *this = Swap() - right.Swap(); + } + template + SwappedEndian& operator*=(const SwappedEndian& right) { + return *this = Swap() * right.Swap(); + } + template + SwappedEndian& operator/=(const SwappedEndian& right) { + return *this = Swap() / right.Swap(); + } + template + SwappedEndian& operator%=(const SwappedEndian& right) { + return *this = Swap() % right.Swap(); + } + template + SwappedEndian& operator&=(const SwappedEndian& right) { + return *this = Raw() & right.Raw(); + } + template + SwappedEndian& operator|=(const SwappedEndian& right) { + return *this = Raw() | right.Raw(); + } + template + SwappedEndian& operator^=(const SwappedEndian& right) { + return *this = Raw() ^ right.Raw(); + } + + template + SwappedEndian operator&(const SwappedEndian& right) const { + return SwappedEndian{Raw() & right.Raw()}; + } + template + SwappedEndian operator|(const SwappedEndian& right) const { + return SwappedEndian{Raw() | right.Raw()}; + } + template + SwappedEndian operator^(const SwappedEndian& right) const { + return SwappedEndian{Raw() ^ right.Raw()}; + } + + template + bool operator==(T1 right) const { + return (T1)Swap() == right; + } + template + bool operator!=(T1 right) const { + return !(*this == right); + } + template + bool operator>(T1 right) const { + return (T1)Swap() > right; + } + template + bool operator<(T1 right) const { + return (T1)Swap() < right; + } + template + bool operator>=(T1 right) const { + return (T1)Swap() >= right; + } + template + bool operator<=(T1 right) const { + return (T1)Swap() <= right; + } + + template + bool operator==(const SwappedEndian& right) const { + return Raw() == right.Raw(); + } + template + bool operator!=(const SwappedEndian& right) const { + return !(*this == right); + } + template + bool operator>(const SwappedEndian& right) const { + return (T1)Swap() > right.Swap(); + } + template + bool operator<(const SwappedEndian& right) const { + return (T1)Swap() < right.Swap(); + } + template + bool operator>=(const SwappedEndian& right) const { + return (T1)Swap() >= right.Swap(); + } + template + bool operator<=(const SwappedEndian& right) const { + return (T1)Swap() <= right.Swap(); + } + + SwappedEndian operator++(int) { + SwappedEndian res = *this; + *this += 1; + return res; + } + SwappedEndian operator--(int) { + SwappedEndian res = *this; + *this -= 1; + return res; + } + SwappedEndian& operator++() { + *this += 1; + return *this; + } + SwappedEndian& operator--() { + *this -= 1; + return *this; + } + +private: + T data; +}; + +template +using LittleEndian = std::conditional_t, + SwappedEndian>; + +template +using BigEndian = + std::conditional_t, SwappedEndian>; + +} // namespace Common + +using u16_be = Common::BigEndian; +using u32_be = Common::BigEndian; +using u64_be = Common::BigEndian; + +using u16_le = Common::LittleEndian; +using u32_le = Common::LittleEndian; +using u64_le = Common::LittleEndian; diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp new file mode 100644 index 00000000..9501550c --- /dev/null +++ b/src/core/file_format/psf.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include "common/io_file.h" +#include "psf.h" + +PSF::PSF() = default; + +PSF::~PSF() = default; + +bool PSF::open(const std::string& filepath) { + Common::FS::IOFile file(filepath,Common::FS::FileAccessMode::Read); + if (!file.IsOpen()) { + return false; + } + + const u64 psfSize = file.GetSize(); + psf.resize(psfSize); + file.Seek(0); + file.ReadRaw(&psf[0], psfSize); + + // Parse file contents + PSFHeader header; + std::memcpy(&header, psf.data(), sizeof(header)); + for (u32 i = 0; i < header.index_table_entries; i++) { + PSFEntry entry; + std::memcpy(&entry, &psf[sizeof(PSFHeader) + i * sizeof(PSFEntry)], sizeof(entry)); + + const std::string key = (char*)&psf[header.key_table_offset + entry.key_offset]; + if (entry.param_fmt == PSFEntry::Fmt::TextRaw || + entry.param_fmt == PSFEntry::Fmt::TextNormal) { + map_strings[key] = (char*)&psf[header.data_table_offset + entry.data_offset]; + } + if (entry.param_fmt == PSFEntry::Fmt::Integer) { + u32 value; + std::memcpy(&value, &psf[header.data_table_offset + entry.data_offset], sizeof(value)); + map_integers[key] = value; + } + } + return true; +} + +std::string PSF::GetString(const std::string& key) { + if (map_strings.find(key) != map_strings.end()) { + return map_strings.at(key); + } + return ""; +} + +u32 PSF::GetInteger(const std::string& key) { + if (map_integers.find(key) != map_integers.end()) { + return map_integers.at(key); // TODO std::invalid_argument exception if it fails? + } + return 0; +} diff --git a/src/core/file_format/psf.h b/src/core/file_format/psf.h new file mode 100644 index 00000000..31978630 --- /dev/null +++ b/src/core/file_format/psf.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "common/endian.h" + +struct PSFHeader { + u32_be magic; + u32_le version; + u32_le key_table_offset; + u32_le data_table_offset; + u32_le index_table_entries; +}; + +struct PSFEntry { + enum Fmt : u16 { + TextRaw = 0x0400, // String in UTF-8 format and not NULL terminated + TextNormal = 0x0402, // String in UTF-8 format and NULL terminated + Integer = 0x0404, // Unsigned 32-bit integer + }; + + u16_le key_offset; + u16_be param_fmt; + u32_le param_len; + u32_le param_max_len; + u32_le data_offset; +}; + +class PSF { +public: + PSF(); + ~PSF(); + + bool open(const std::string& filepath); + + std::string GetString(const std::string& key); + u32 GetInteger(const std::string& key); + + std::unordered_map map_strings; + std::unordered_map map_integers; + +private: + std::vector psf; +};