From 7101247fd511bd6501ae4b0dd74bb46192e859e7 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Oct 2022 17:44:17 +0300 Subject: [PATCH] added psf file format and correct load to gamelist viewer --- shadPS4/Types.h | 32 +++++++++++++ shadPS4/emulator/fileFormat/PSF.cpp | 70 +++++++++++++++++++++++++++++ shadPS4/emulator/fileFormat/PSF.h | 44 ++++++++++++++++++ shadPS4/gui/GameListViewer.cpp | 17 +++---- shadPS4/shadPS4.vcxproj | 2 + shadPS4/shadPS4.vcxproj.filters | 12 +++++ 6 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 shadPS4/emulator/fileFormat/PSF.cpp create mode 100644 shadPS4/emulator/fileFormat/PSF.h diff --git a/shadPS4/Types.h b/shadPS4/Types.h index 0d8de2da..aeb19eb7 100644 --- a/shadPS4/Types.h +++ b/shadPS4/Types.h @@ -1,4 +1,5 @@ #pragma once +#include using S08 = char; using S16 = short; @@ -12,3 +13,34 @@ using U64 = unsigned long long; using F32 = float; using F64 = double; + + +template< typename T > T inline LoadBE(T* src) { return *src; }; +template< typename T > inline void StoreBE(T* dst, T src) { *dst = src; }; + +inline S16 LoadBE(S16* src) { return _loadbe_i16(src); }; +inline S32 LoadBE(S32* src) { return _loadbe_i32(src); }; +inline S64 LoadBE(S64* src) { return _loadbe_i64(src); }; + +inline U16 LoadBE(U16* src) { return _load_be_u16(src); }; +inline U32 LoadBE(U32* src) { return _load_be_u32(src); }; +inline U64 LoadBE(U64* src) { return _load_be_u64(src); }; + +inline void StoreBE(S16* dst, S16 const src) { _storebe_i16(dst, src); }; +inline void StoreBE(S32* dst, S32 const src) { _storebe_i32(dst, src); }; +inline void StoreBE(S64* dst, S64 const src) { _storebe_i64(dst, src); }; + +inline void StoreBE(U16* dst, U16 const src) { _store_be_u16(dst, src); }; +inline void StoreBE(U32* dst, U32 const src) { _store_be_u32(dst, src); }; +inline void StoreBE(U64* dst, U64 const src) { _store_be_u64(dst, src); }; + + +template< typename T > inline void ReadBE(T& val) +{ + val = LoadBE(&val); // swap inplace +} + +template< typename T > inline void WriteBE(T& val) +{ + StoreBE(&val, val); // swap inplace +} \ No newline at end of file diff --git a/shadPS4/emulator/fileFormat/PSF.cpp b/shadPS4/emulator/fileFormat/PSF.cpp new file mode 100644 index 00000000..893d0683 --- /dev/null +++ b/shadPS4/emulator/fileFormat/PSF.cpp @@ -0,0 +1,70 @@ +#include "PSF.h" +#include "../../Core/FsFile.h" +#include +#include + +PSF::PSF() +{ +} + + +PSF::~PSF() +{ +} +bool PSF::open(const std::string& filepath) { + FsFile file; + if (!file.Open(filepath, fsRead)) + { + return false; + } + + const U64 psfSize = file.getFileSize(); + psf.resize(psfSize); + file.Seek(0, fsSeekSet); + file.Read(&psf[0], psfSize); + + // Parse file contents + const auto& header = (PSFHeader&)psf[0]; + for (U32 i = 0; i < header.indexTableEntries; i++) { + const U32 offset = sizeof(PSFHeader) + i * sizeof(PSFEntry); + auto& entry = (PSFEntry&)psf[offset]; + + std::string key = (char*)&psf[header.keyTableOffset + entry.keyOffset]; + ReadBE(entry.param_fmt);//param_fmt is big endian convert it (this convert the original entry maybe we should store it elsewhere?) + if (entry.param_fmt == PSFEntry::Fmt::TEXT_RAW || + entry.param_fmt == PSFEntry::Fmt::TEXT_NORMAL) { + map_strings[key] = (char*)&psf[header.dataTableOffset + entry.dataOffset]; + } + if (entry.param_fmt == PSFEntry::Fmt::INTEGER) { + map_integers[key] = (U32&)psf[header.dataTableOffset + entry.dataOffset]; + } + } + //debug code print all keys + std::ofstream out; + out.open("psf.txt", std::fstream::out | std::fstream::app); + out << "---------------------------------------------" << "\n"; + for (auto stringkey : map_strings) + { + out << " " << stringkey.first << " : " << stringkey.second << "\n"; + } + for (auto integerkey : map_integers) + { + out << " " << integerkey.first << " : " << integerkey.second << "\n"; + } + out << "---------------------------------------------" << "\n"; + + return true; +} +std::string PSF::get_string(const std::string& key) { + if (map_strings.find(key) != map_strings.end()) { + return map_strings.at(key); + } + return ""; +} +U32 PSF::get_integer(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; +} \ No newline at end of file diff --git a/shadPS4/emulator/fileFormat/PSF.h b/shadPS4/emulator/fileFormat/PSF.h new file mode 100644 index 00000000..820bfa33 --- /dev/null +++ b/shadPS4/emulator/fileFormat/PSF.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "../../types.h" + +struct PSFHeader { + U32 magic; //big endian + U32 version; + U32 keyTableOffset; + U32 dataTableOffset; + U32 indexTableEntries; +}; + +struct PSFEntry { + enum Fmt : U16 { + TEXT_RAW = 0x0400, // String in UTF-8 format and not NULL terminated + TEXT_NORMAL = 0x0402, // String in UTF-8 format and NULL terminated + INTEGER = 0x0404, // Unsigned 32-bit integer + }; + + U16 keyOffset; + U16 param_fmt;//big endian + U32 paramLen; + U32 paramMaxLen; + U32 dataOffset; +}; + +class PSF +{ + std::vector psf; + std::unordered_map map_strings; + std::unordered_map map_integers; + +public: + PSF(); + ~PSF(); + bool open(const std::string& filepath); + + // Access data + std::string get_string(const std::string& key); + U32 get_integer(const std::string& key); +}; + diff --git a/shadPS4/gui/GameListViewer.cpp b/shadPS4/gui/GameListViewer.cpp index ed34a116..074ecfc9 100644 --- a/shadPS4/gui/GameListViewer.cpp +++ b/shadPS4/gui/GameListViewer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "../emulator/fileFormat/PSF.h" GameListViewer::GameListViewer(QWidget* parent) : QWidget(parent) @@ -147,20 +148,20 @@ void GameListWorker::AddEntriesToGameList(const std::string& dir_path) { QFileInfoList fList = parent_folder.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::DirsFirst); foreach(QFileInfo item, fList) { - //TODO PSF psf; - //TODO if (!psf.open(item.absoluteFilePath().toStdString() + "/PARAM.SFO")) - //TODO continue;//if we can't open param.sfo go to the next entry + PSF psf; + if (!psf.open(item.absoluteFilePath().toStdString() + "/PARAM.SFO")) + continue;//if we can't open param.sfo go to the next entry //TODO std::string test = psf.get_string("TITLE_ID"); QString iconpath(item.absoluteFilePath() + "/ICON0.PNG"); emit EntryReady({ new GameIconItem(iconpath), - new GameListItem("TODO"/*QString::fromStdString(psf.get_string("TITLE"))*/), - new GameListItem("TODO"/*QString::fromStdString(psf.get_string("TITLE_ID"))*/), - new GameListItem("TODO"/*QString::fromStdString(psf.get_string("SYSTEM_VER"))*/), - new GameListItem("TODO"/*QString::fromStdString(psf.get_string("APP_VER"))*/), - new GameListItem("TODO"/*QString::fromStdString(psf.get_string("CATEGORY"))*/), + new GameListItem(QString::fromStdString(psf.get_string("TITLE"))), + new GameListItem(QString::fromStdString(psf.get_string("TITLE_ID"))), + new GameListItem(QString("%1").arg(psf.get_integer("SYSTEM_VER"), 8, 16, QLatin1Char('0'))), + new GameListItem(QString::fromStdString(psf.get_string("APP_VER"))), + new GameListItem(QString::fromStdString(psf.get_string("CATEGORY"))), new GameListItem(item.fileName()) }); diff --git a/shadPS4/shadPS4.vcxproj b/shadPS4/shadPS4.vcxproj index 56ef7d75..698a9717 100644 --- a/shadPS4/shadPS4.vcxproj +++ b/shadPS4/shadPS4.vcxproj @@ -12,6 +12,7 @@ + @@ -27,6 +28,7 @@ + diff --git a/shadPS4/shadPS4.vcxproj.filters b/shadPS4/shadPS4.vcxproj.filters index 5b6e92a3..bfe0d7f8 100644 --- a/shadPS4/shadPS4.vcxproj.filters +++ b/shadPS4/shadPS4.vcxproj.filters @@ -27,6 +27,12 @@ {73d07238-8864-48b5-9987-e455fa73c82f} + + {66864aec-390b-47a8-bddb-b52032630d7a} + + + {ed31734c-f010-4590-9f01-18e0b2497ffb} + @@ -41,6 +47,9 @@ Core + + emulator\fileFormat + @@ -62,5 +71,8 @@ Core + + emulator\fileFormat + \ No newline at end of file