#pragma once #include "util/types.hpp" #include #include #include namespace fs { class file; } namespace psf { enum sound_format_flag : s32 { lpcm_2 = 1 << 0, // Linear PCM 2 Ch. lpcm_5_1 = 1 << 2, // Linear PCM 5.1 Ch. lpcm_7_1 = 1 << 4, // Linear PCM 7.1 Ch. ac3 = 1 << 8, // Dolby Digital 5.1 Ch. dts = 1 << 9, // DTS 5.1 Ch. }; enum resolution_flag : s32 { _480p = 1 << 0, _576p = 1 << 1, _720p = 1 << 2, _1080p = 1 << 3, _480p_16_9 = 1 << 4, _576p_16_9 = 1 << 5, }; enum class format : u16 { array = 0x0004, // claimed to be a non-NTS string (char array) string = 0x0204, integer = 0x0404, }; enum class error { ok, stream, not_psf, corrupt, }; class entry final { format m_type{}; u32 m_max_size{}; // Entry max size (supplementary info, stored in PSF format) u32 m_value_integer{}; // TODO: is it really unsigned? std::string m_value_string{}; public: // Construct string entry, assign the value entry(format type, u32 max_size, std::string_view value, bool allow_truncate = false) noexcept; // Construct integer entry, assign the value entry(u32 value) noexcept; ~entry() = default; const std::string& as_string() const; u32 as_integer() const; entry& operator =(std::string_view value); entry& operator =(u32 value); format type() const { return m_type; } u32 max(bool with_nts) const { return m_max_size - (!with_nts && m_type == format::string ? 1 : 0); } u32 size() const; bool is_valid() const; }; // Define PSF registry as a sorted map of entries: using registry = std::map>; struct load_result_t { registry sfo; error errc; explicit operator bool() const { return !sfo.empty(); } }; // Load PSF registry from SFO binary format load_result_t load(const fs::file&, std::string_view filename); load_result_t load(const std::string& filename); inline registry load_object(const fs::file& f, std::string_view filename) { return load(f, filename).sfo; } inline registry load_object(const std::string& filename) { return load(filename).sfo; } // Convert PSF registry to SFO binary format std::vector save_object(const registry&, std::vector&& init = std::vector{}); // Get string value or default value std::string_view get_string(const registry& psf, std::string_view key, std::string_view def = ""sv); // Get integer value or default value u32 get_integer(const registry& psf, std::string_view key, u32 def = 0); bool check_registry(const registry& psf, std::function validate = {}, u32 line = __builtin_LINE(), u32 col = __builtin_COLUMN(), const char* file = __builtin_FILE(), const char* func = __builtin_FUNCTION()); // Assign new entry inline void assign(registry& psf, std::string_view key, entry&& _entry) { const auto found = psf.find(key); if (found == psf.end()) { psf.emplace(key, std::move(_entry)); return; } found->second = std::move(_entry); return; } // Make string entry inline entry string(u32 max_size, std::string_view value, bool allow_truncate = false) { return {format::string, max_size, value, allow_truncate}; } // Make string entry (from char[N]) template inline entry string(u32 max_size, char (&value_array)[CharN], bool allow_truncate = false) { std::string_view value{value_array, CharN}; value = value.substr(0, std::min(value.find_first_of('\0'), value.size())); return string(max_size, value, allow_truncate); } // Make array entry inline entry array(u32 max_size, std::string_view value) { return {format::array, max_size, value}; } // Checks if of HDD catgeory (assumes a valid category is being passed) constexpr bool is_cat_hdd(std::string_view cat) { return cat.size() == 2u && cat[1] != 'D' && cat != "DG"sv && cat != "MS"sv; } }