mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-31 07:59:02 -05:00 
			
		
		
		
	Merge pull request #1817 from linkmauve/smdh-stuff
Improve SMDH support in loaders and frontends
This commit is contained in:
		| @@ -114,7 +114,13 @@ int main(int argc, char **argv) { | |||||||
|     System::Init(emu_window.get()); |     System::Init(emu_window.get()); | ||||||
|     SCOPE_EXIT({ System::Shutdown(); }); |     SCOPE_EXIT({ System::Shutdown(); }); | ||||||
|  |  | ||||||
|     Loader::ResultStatus load_result = Loader::LoadFile(boot_filename); |     std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(boot_filename); | ||||||
|  |     if (!loader) { | ||||||
|  |         LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", boot_filename.c_str()); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Loader::ResultStatus load_result = loader->Load(); | ||||||
|     if (Loader::ResultStatus::Success != load_result) { |     if (Loader::ResultStatus::Success != load_result) { | ||||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result); |         LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result); | ||||||
|         return -1; |         return -1; | ||||||
|   | |||||||
| @@ -132,30 +132,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d | |||||||
|         if (deep_scan && FileUtil::IsDirectory(physical_name)) { |         if (deep_scan && FileUtil::IsDirectory(physical_name)) { | ||||||
|             AddFstEntriesToGameList(physical_name, true); |             AddFstEntriesToGameList(physical_name, true); | ||||||
|         } else { |         } else { | ||||||
|             std::string filename_filename, filename_extension; |             std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); | ||||||
|             Common::SplitPath(physical_name, nullptr, &filename_filename, &filename_extension); |             if (!loader) | ||||||
|  |  | ||||||
|             Loader::FileType guessed_filetype = Loader::GuessFromExtension(filename_extension); |  | ||||||
|             if (guessed_filetype == Loader::FileType::Unknown) |  | ||||||
|                 return true; |                 return true; | ||||||
|             Loader::FileType filetype = Loader::IdentifyFile(physical_name); |  | ||||||
|             if (filetype == Loader::FileType::Unknown) { |  | ||||||
|                 LOG_WARNING(Frontend, "File %s is of indeterminate type and is possibly corrupted.", physical_name.c_str()); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             if (guessed_filetype != filetype) { |  | ||||||
|                 LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str()); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             std::vector<u8> smdh; |             std::vector<u8> smdh; | ||||||
|             std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(FileUtil::IOFile(physical_name, "rb"), filetype, filename_filename, physical_name); |             loader->ReadIcon(smdh); | ||||||
|  |  | ||||||
|             if (loader) |  | ||||||
|                 loader->ReadIcon(smdh); |  | ||||||
|  |  | ||||||
|             emit EntryReady({ |             emit EntryReady({ | ||||||
|                 new GameListItemPath(QString::fromStdString(physical_name), smdh), |                 new GameListItemPath(QString::fromStdString(physical_name), smdh), | ||||||
|                 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))), |                 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||||||
|                 new GameListItemSize(FileUtil::GetSize(physical_name)), |                 new GameListItemSize(FileUtil::GetSize(physical_name)), | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -15,52 +15,21 @@ | |||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "common/color.h" | #include "common/color.h" | ||||||
|  |  | ||||||
| #include "core/loader/loader.h" | #include "core/loader/smdh.h" | ||||||
|  |  | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Tests if data is a valid SMDH by its length and magic number. |  | ||||||
|  * @param smdh_data data buffer to test |  | ||||||
|  * @return bool test result |  | ||||||
|  */ |  | ||||||
| static bool IsValidSMDH(const std::vector<u8>& smdh_data) { |  | ||||||
|     if (smdh_data.size() < sizeof(Loader::SMDH)) |  | ||||||
|         return false; |  | ||||||
|  |  | ||||||
|     u32 magic; |  | ||||||
|     memcpy(&magic, smdh_data.data(), 4); |  | ||||||
|  |  | ||||||
|     return Loader::MakeMagic('S', 'M', 'D', 'H') == magic; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Gets game icon from SMDH |  * Gets game icon from SMDH | ||||||
|  * @param sdmh SMDH data |  * @param sdmh SMDH data | ||||||
|  * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) |  * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) | ||||||
|  * @return QPixmap game icon |  * @return QPixmap game icon | ||||||
|  */ |  */ | ||||||
| static QPixmap GetIconFromSMDH(const Loader::SMDH& smdh, bool large) { | static QPixmap GetQPixmapFromSMDH(const Loader::SMDH& smdh, bool large) { | ||||||
|     u32 size; |     std::vector<u16> icon_data = smdh.GetIcon(large); | ||||||
|     const u8* icon_data; |     const uchar* data = reinterpret_cast<const uchar*>(icon_data.data()); | ||||||
|  |     int size = large ? 48 : 24; | ||||||
|     if (large) { |     QImage icon(data, size, size, QImage::Format::Format_RGB16); | ||||||
|         size = 48; |  | ||||||
|         icon_data = smdh.large_icon.data(); |  | ||||||
|     } else { |  | ||||||
|         size = 24; |  | ||||||
|         icon_data = smdh.small_icon.data(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QImage icon(size, size, QImage::Format::Format_RGB888); |  | ||||||
|     for (u32 x = 0; x < size; ++x) { |  | ||||||
|         for (u32 y = 0; y < size; ++y) { |  | ||||||
|             u32 coarse_y = y & ~7; |  | ||||||
|             auto v = Color::DecodeRGB565( |  | ||||||
|                 icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2); |  | ||||||
|             icon.setPixel(x, y, qRgb(v.r(), v.g(), v.b())); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return QPixmap::fromImage(icon); |     return QPixmap::fromImage(icon); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -82,8 +51,8 @@ static QPixmap GetDefaultIcon(bool large) { | |||||||
|  * @param language title language |  * @param language title language | ||||||
|  * @return QString short title |  * @return QString short title | ||||||
|  */ |  */ | ||||||
| static QString GetShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { | static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { | ||||||
|     return QString::fromUtf16(smdh.titles[static_cast<int>(language)].short_title.data()); |     return QString::fromUtf16(smdh.GetShortTitle(language).data()); | ||||||
| } | } | ||||||
|  |  | ||||||
| class GameListItem : public QStandardItem { | class GameListItem : public QStandardItem { | ||||||
| @@ -112,7 +81,7 @@ public: | |||||||
|     { |     { | ||||||
|         setData(game_path, FullPathRole); |         setData(game_path, FullPathRole); | ||||||
|  |  | ||||||
|         if (!IsValidSMDH(smdh_data)) { |         if (!Loader::IsValidSMDH(smdh_data)) { | ||||||
|             // SMDH is not valid, set a default icon |             // SMDH is not valid, set a default icon | ||||||
|             setData(GetDefaultIcon(true), Qt::DecorationRole); |             setData(GetDefaultIcon(true), Qt::DecorationRole); | ||||||
|             return; |             return; | ||||||
| @@ -122,10 +91,10 @@ public: | |||||||
|         memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); |         memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); | ||||||
|  |  | ||||||
|         // Get icon from SMDH |         // Get icon from SMDH | ||||||
|         setData(GetIconFromSMDH(smdh, true), Qt::DecorationRole); |         setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole); | ||||||
|  |  | ||||||
|         // Get title form SMDH |         // Get title form SMDH | ||||||
|         setData(GetShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); |         setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QVariant data(int role) const override { |     QVariant data(int role) const override { | ||||||
|   | |||||||
| @@ -272,7 +272,15 @@ bool GMainWindow::InitializeSystem() { | |||||||
| } | } | ||||||
|  |  | ||||||
| bool GMainWindow::LoadROM(const std::string& filename) { | bool GMainWindow::LoadROM(const std::string& filename) { | ||||||
|     Loader::ResultStatus result = Loader::LoadFile(filename); |     std::unique_ptr<Loader::AppLoader> app_loader = Loader::GetLoader(filename); | ||||||
|  |     if (!app_loader) { | ||||||
|  |         LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str()); | ||||||
|  |         QMessageBox::critical(this, tr("Error while loading ROM!"), | ||||||
|  |                               tr("The ROM format is not supported.")); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Loader::ResultStatus result = app_loader->Load(); | ||||||
|     if (Loader::ResultStatus::Success != result) { |     if (Loader::ResultStatus::Success != result) { | ||||||
|         LOG_CRITICAL(Frontend, "Failed to load ROM!"); |         LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||||||
|         System::Shutdown(); |         System::Shutdown(); | ||||||
|   | |||||||
| @@ -121,6 +121,7 @@ set(SRCS | |||||||
|             loader/elf.cpp |             loader/elf.cpp | ||||||
|             loader/loader.cpp |             loader/loader.cpp | ||||||
|             loader/ncch.cpp |             loader/ncch.cpp | ||||||
|  |             loader/smdh.cpp | ||||||
|             tracer/recorder.cpp |             tracer/recorder.cpp | ||||||
|             memory.cpp |             memory.cpp | ||||||
|             settings.cpp |             settings.cpp | ||||||
| @@ -256,6 +257,7 @@ set(HEADERS | |||||||
|             loader/elf.h |             loader/elf.h | ||||||
|             loader/loader.h |             loader/loader.h | ||||||
|             loader/ncch.h |             loader/ncch.h | ||||||
|  |             loader/smdh.h | ||||||
|             tracer/recorder.h |             tracer/recorder.h | ||||||
|             tracer/citrace.h |             tracer/citrace.h | ||||||
|             memory.h |             memory.h | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
| #include "core/file_sys/archive_romfs.h" | #include "core/file_sys/archive_romfs.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
|  | #include "core/hle/service/fs/archive.h" | ||||||
| #include "core/loader/3dsx.h" | #include "core/loader/3dsx.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
|  |  | ||||||
| @@ -263,6 +264,8 @@ ResultStatus AppLoader_THREEDSX::Load() { | |||||||
|  |  | ||||||
|     Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); |     Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); | ||||||
|  |  | ||||||
|  |     Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS); | ||||||
|  |  | ||||||
|     is_loaded = true; |     is_loaded = true; | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,6 +27,14 @@ public: | |||||||
|      */ |      */ | ||||||
|     static FileType IdentifyType(FileUtil::IOFile& file); |     static FileType IdentifyType(FileUtil::IOFile& file); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the type of this file | ||||||
|  |      * @return FileType corresponding to the loaded file | ||||||
|  |      */ | ||||||
|  |     FileType GetFileType() override { | ||||||
|  |         return IdentifyType(file); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Load the bootable file |      * Load the bootable file | ||||||
|      * @return ResultStatus result of function |      * @return ResultStatus result of function | ||||||
|   | |||||||
| @@ -27,6 +27,14 @@ public: | |||||||
|      */ |      */ | ||||||
|     static FileType IdentifyType(FileUtil::IOFile& file); |     static FileType IdentifyType(FileUtil::IOFile& file); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the type of this file | ||||||
|  |      * @return FileType corresponding to the loaded file | ||||||
|  |      */ | ||||||
|  |     FileType GetFileType() override { | ||||||
|  |         return IdentifyType(file); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Load the bootable file |      * Load the bootable file | ||||||
|      * @return ResultStatus result of function |      * @return ResultStatus result of function | ||||||
|   | |||||||
| @@ -8,9 +8,7 @@ | |||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
|  |  | ||||||
| #include "core/file_sys/archive_romfs.h" |  | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/service/fs/archive.h" |  | ||||||
| #include "core/loader/3dsx.h" | #include "core/loader/3dsx.h" | ||||||
| #include "core/loader/elf.h" | #include "core/loader/elf.h" | ||||||
| #include "core/loader/ncch.h" | #include "core/loader/ncch.h" | ||||||
| @@ -67,6 +65,9 @@ FileType GuessFromExtension(const std::string& extension_) { | |||||||
|     if (extension == ".3dsx") |     if (extension == ".3dsx") | ||||||
|         return FileType::THREEDSX; |         return FileType::THREEDSX; | ||||||
|  |  | ||||||
|  |     if (extension == ".cia") | ||||||
|  |         return FileType::CIA; | ||||||
|  |  | ||||||
|     return FileType::Unknown; |     return FileType::Unknown; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -90,7 +91,15 @@ const char* GetFileTypeString(FileType type) { | |||||||
|     return "unknown"; |     return "unknown"; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, | /** | ||||||
|  |  * Get a loader for a file with a specific type | ||||||
|  |  * @param file The file to load | ||||||
|  |  * @param type The type of the file | ||||||
|  |  * @param filename the file name (without path) | ||||||
|  |  * @param filepath the file full path (with name) | ||||||
|  |  * @return std::unique_ptr<AppLoader> a pointer to a loader object;  nullptr for unsupported type | ||||||
|  |  */ | ||||||
|  | static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type, | ||||||
|     const std::string& filename, const std::string& filepath) { |     const std::string& filename, const std::string& filepath) { | ||||||
|     switch (type) { |     switch (type) { | ||||||
|  |  | ||||||
| @@ -108,15 +117,15 @@ std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, | |||||||
|         return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); |         return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         return std::unique_ptr<AppLoader>(); |         return nullptr; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultStatus LoadFile(const std::string& filename) { | std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { | ||||||
|     FileUtil::IOFile file(filename, "rb"); |     FileUtil::IOFile file(filename, "rb"); | ||||||
|     if (!file.IsOpen()) { |     if (!file.IsOpen()) { | ||||||
|         LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); |         LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); | ||||||
|         return ResultStatus::Error; |         return nullptr; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::string filename_filename, filename_extension; |     std::string filename_filename, filename_extension; | ||||||
| @@ -133,44 +142,7 @@ ResultStatus LoadFile(const std::string& filename) { | |||||||
|  |  | ||||||
|     LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type)); |     LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type)); | ||||||
|  |  | ||||||
|     std::unique_ptr<AppLoader> app_loader = GetLoader(std::move(file), type, filename_filename, filename); |     return GetFileLoader(std::move(file), type, filename_filename, filename); | ||||||
|  |  | ||||||
|     switch (type) { |  | ||||||
|  |  | ||||||
|     // 3DSX file format... |  | ||||||
|     // or NCCH/NCSD container formats... |  | ||||||
|     case FileType::THREEDSX: |  | ||||||
|     case FileType::CXI: |  | ||||||
|     case FileType::CCI: |  | ||||||
|     { |  | ||||||
|         // Load application and RomFS |  | ||||||
|         ResultStatus result = app_loader->Load(); |  | ||||||
|         if (ResultStatus::Success == result) { |  | ||||||
|             Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS); |  | ||||||
|             return ResultStatus::Success; |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Standard ELF file format... |  | ||||||
|     case FileType::ELF: |  | ||||||
|         return app_loader->Load(); |  | ||||||
|  |  | ||||||
|     // CIA file format... |  | ||||||
|     case FileType::CIA: |  | ||||||
|         return ResultStatus::ErrorNotImplemented; |  | ||||||
|  |  | ||||||
|     // Error occurred durring IdentifyFile... |  | ||||||
|     case FileType::Error: |  | ||||||
|  |  | ||||||
|     // IdentifyFile could know identify file type... |  | ||||||
|     case FileType::Unknown: |  | ||||||
|     { |  | ||||||
|         LOG_CRITICAL(Loader, "File %s is of unknown type.", filename.c_str()); |  | ||||||
|         return ResultStatus::ErrorInvalidFormat; |  | ||||||
|     } |  | ||||||
|     } |  | ||||||
|     return ResultStatus::Error; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Loader | } // namespace Loader | ||||||
|   | |||||||
| @@ -10,10 +10,8 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #include "common/common_funcs.h" |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/swap.h" |  | ||||||
|  |  | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| struct AddressMapping; | struct AddressMapping; | ||||||
| @@ -80,57 +78,18 @@ constexpr u32 MakeMagic(char a, char b, char c, char d) { | |||||||
|     return a | b << 8 | c << 16 | d << 24; |     return a | b << 8 | c << 16 | d << 24; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH |  | ||||||
| struct SMDH { |  | ||||||
|     u32_le magic; |  | ||||||
|     u16_le version; |  | ||||||
|     INSERT_PADDING_BYTES(2); |  | ||||||
|  |  | ||||||
|     struct Title { |  | ||||||
|         std::array<u16, 0x40> short_title; |  | ||||||
|         std::array<u16, 0x80> long_title; |  | ||||||
|         std::array<u16, 0x40> publisher; |  | ||||||
|     }; |  | ||||||
|     std::array<Title, 16> titles; |  | ||||||
|  |  | ||||||
|     std::array<u8, 16> ratings; |  | ||||||
|     u32_le region_lockout; |  | ||||||
|     u32_le match_maker_id; |  | ||||||
|     u64_le match_maker_bit_id; |  | ||||||
|     u32_le flags; |  | ||||||
|     u16_le eula_version; |  | ||||||
|     INSERT_PADDING_BYTES(2); |  | ||||||
|     float_le banner_animation_frame; |  | ||||||
|     u32_le cec_id; |  | ||||||
|     INSERT_PADDING_BYTES(8); |  | ||||||
|  |  | ||||||
|     std::array<u8, 0x480> small_icon; |  | ||||||
|     std::array<u8, 0x1200> large_icon; |  | ||||||
|  |  | ||||||
|     /// indicates the language used for each title entry |  | ||||||
|     enum class TitleLanguage { |  | ||||||
|         Japanese = 0, |  | ||||||
|         English = 1, |  | ||||||
|         French = 2, |  | ||||||
|         German = 3, |  | ||||||
|         Italian = 4, |  | ||||||
|         Spanish = 5, |  | ||||||
|         SimplifiedChinese = 6, |  | ||||||
|         Korean= 7, |  | ||||||
|         Dutch = 8, |  | ||||||
|         Portuguese = 9, |  | ||||||
|         Russian = 10, |  | ||||||
|         TraditionalChinese = 11 |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong"); |  | ||||||
|  |  | ||||||
| /// Interface for loading an application | /// Interface for loading an application | ||||||
| class AppLoader : NonCopyable { | class AppLoader : NonCopyable { | ||||||
| public: | public: | ||||||
|     AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) { } |     AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) { } | ||||||
|     virtual ~AppLoader() { } |     virtual ~AppLoader() { } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the type of this file | ||||||
|  |      * @return FileType corresponding to the loaded file | ||||||
|  |      */ | ||||||
|  |     virtual FileType GetFileType() = 0; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Load the application |      * Load the application | ||||||
|      * @return ResultStatus result of function |      * @return ResultStatus result of function | ||||||
| @@ -197,20 +156,10 @@ protected: | |||||||
| extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings; | extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Get a loader for a file with a specific type |  * Identifies a bootable file and return a suitable loader | ||||||
|  * @param file The file to load |  | ||||||
|  * @param type The type of the file |  | ||||||
|  * @param filename the file name (without path) |  | ||||||
|  * @param filepath the file full path (with name) |  | ||||||
|  * @return std::unique_ptr<AppLoader> a pointer to a loader object;  nullptr for unsupported type |  | ||||||
|  */ |  | ||||||
| std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, const std::string& filename, const std::string& filepath); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Identifies and loads a bootable file |  | ||||||
|  * @param filename String filename of bootable file |  * @param filename String filename of bootable file | ||||||
|  * @return ResultStatus result of function |  * @return best loader for this file | ||||||
|  */ |  */ | ||||||
| ResultStatus LoadFile(const std::string& filename); | std::unique_ptr<AppLoader> GetLoader(const std::string& filename); | ||||||
|  |  | ||||||
| } // namespace | } // namespace | ||||||
|   | |||||||
| @@ -10,8 +10,10 @@ | |||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
|  |  | ||||||
|  | #include "core/file_sys/archive_romfs.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
|  | #include "core/hle/service/fs/archive.h" | ||||||
| #include "core/loader/ncch.h" | #include "core/loader/ncch.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
|  |  | ||||||
| @@ -303,7 +305,12 @@ ResultStatus AppLoader_NCCH::Load() { | |||||||
|  |  | ||||||
|     is_loaded = true; // Set state to loaded |     is_loaded = true; // Set state to loaded | ||||||
|  |  | ||||||
|     return LoadExec(); // Load the executable into memory for booting |     result = LoadExec(); // Load the executable into memory for booting | ||||||
|  |     if (ResultStatus::Success != result) | ||||||
|  |         return result; | ||||||
|  |  | ||||||
|  |     Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS); | ||||||
|  |     return ResultStatus::Success; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { | ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { | ||||||
|   | |||||||
| @@ -173,6 +173,14 @@ public: | |||||||
|      */ |      */ | ||||||
|     static FileType IdentifyType(FileUtil::IOFile& file); |     static FileType IdentifyType(FileUtil::IOFile& file); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the type of this file | ||||||
|  |      * @return FileType corresponding to the loaded file | ||||||
|  |      */ | ||||||
|  |     FileType GetFileType() override { | ||||||
|  |         return IdentifyType(file); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Load the application |      * Load the application | ||||||
|      * @return ResultStatus result of function |      * @return ResultStatus result of function | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								src/core/loader/smdh.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/core/loader/smdh.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | // Copyright 2016 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <cstring> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | #include "core/loader/loader.h" | ||||||
|  | #include "core/loader/smdh.h" | ||||||
|  |  | ||||||
|  | #include "video_core/utils.h" | ||||||
|  |  | ||||||
|  | namespace Loader { | ||||||
|  |  | ||||||
|  | bool IsValidSMDH(const std::vector<u8>& smdh_data) { | ||||||
|  |     if (smdh_data.size() < sizeof(Loader::SMDH)) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     u32 magic; | ||||||
|  |     memcpy(&magic, smdh_data.data(), sizeof(u32)); | ||||||
|  |  | ||||||
|  |     return Loader::MakeMagic('S', 'M', 'D', 'H') == magic; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::vector<u16> SMDH::GetIcon(bool large) const { | ||||||
|  |     u32 size; | ||||||
|  |     const u8* icon_data; | ||||||
|  |  | ||||||
|  |     if (large) { | ||||||
|  |         size = 48; | ||||||
|  |         icon_data = large_icon.data(); | ||||||
|  |     } else { | ||||||
|  |         size = 24; | ||||||
|  |         icon_data = small_icon.data(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::vector<u16> icon(size * size); | ||||||
|  |     for (u32 x = 0; x < size; ++x) { | ||||||
|  |         for (u32 y = 0; y < size; ++y) { | ||||||
|  |             u32 coarse_y = y & ~7; | ||||||
|  |             const u8* pixel = icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2; | ||||||
|  |             icon[x + size * y] = (pixel[1] << 8) + pixel[0]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return icon; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::array<u16, 0x40> SMDH::GetShortTitle(Loader::SMDH::TitleLanguage language) const { | ||||||
|  |     return titles[static_cast<int>(language)].short_title; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace | ||||||
							
								
								
									
										82
									
								
								src/core/loader/smdh.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/core/loader/smdh.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | // Copyright 2016 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/common_funcs.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/swap.h" | ||||||
|  |  | ||||||
|  | namespace Loader { | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Tests if data is a valid SMDH by its length and magic number. | ||||||
|  |  * @param smdh_data data buffer to test | ||||||
|  |  * @return bool test result | ||||||
|  |  */ | ||||||
|  | bool IsValidSMDH(const std::vector<u8>& smdh_data); | ||||||
|  |  | ||||||
|  | /// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH | ||||||
|  | struct SMDH { | ||||||
|  |     u32_le magic; | ||||||
|  |     u16_le version; | ||||||
|  |     INSERT_PADDING_BYTES(2); | ||||||
|  |  | ||||||
|  |     struct Title { | ||||||
|  |         std::array<u16, 0x40> short_title; | ||||||
|  |         std::array<u16, 0x80> long_title; | ||||||
|  |         std::array<u16, 0x40> publisher; | ||||||
|  |     }; | ||||||
|  |     std::array<Title, 16> titles; | ||||||
|  |  | ||||||
|  |     std::array<u8, 16> ratings; | ||||||
|  |     u32_le region_lockout; | ||||||
|  |     u32_le match_maker_id; | ||||||
|  |     u64_le match_maker_bit_id; | ||||||
|  |     u32_le flags; | ||||||
|  |     u16_le eula_version; | ||||||
|  |     INSERT_PADDING_BYTES(2); | ||||||
|  |     float_le banner_animation_frame; | ||||||
|  |     u32_le cec_id; | ||||||
|  |     INSERT_PADDING_BYTES(8); | ||||||
|  |  | ||||||
|  |     std::array<u8, 0x480> small_icon; | ||||||
|  |     std::array<u8, 0x1200> large_icon; | ||||||
|  |  | ||||||
|  |     /// indicates the language used for each title entry | ||||||
|  |     enum class TitleLanguage { | ||||||
|  |         Japanese = 0, | ||||||
|  |         English = 1, | ||||||
|  |         French = 2, | ||||||
|  |         German = 3, | ||||||
|  |         Italian = 4, | ||||||
|  |         Spanish = 5, | ||||||
|  |         SimplifiedChinese = 6, | ||||||
|  |         Korean= 7, | ||||||
|  |         Dutch = 8, | ||||||
|  |         Portuguese = 9, | ||||||
|  |         Russian = 10, | ||||||
|  |         TraditionalChinese = 11 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Gets game icon from SMDH | ||||||
|  |      * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) | ||||||
|  |      * @return vector of RGB565 data | ||||||
|  |      */ | ||||||
|  |     std::vector<u16> GetIcon(bool large) const; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Gets the short game title from SMDH | ||||||
|  |      * @param language title language | ||||||
|  |      * @return UTF-16 array of the short title | ||||||
|  |      */ | ||||||
|  |     std::array<u16, 0x40> GetShortTitle(Loader::SMDH::TitleLanguage language) const; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong"); | ||||||
|  |  | ||||||
|  | } // namespace | ||||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei