mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #1700 from wwylele/gamelist-icon
Qt: display game icon and title in the game list
This commit is contained in:
		
						commit
						55946cdc11
					
				
					 10 changed files with 260 additions and 37 deletions
				
			
		|  | @ -55,6 +55,7 @@ set(HEADERS | ||||||
|             configure_dialog.h |             configure_dialog.h | ||||||
|             configure_general.h |             configure_general.h | ||||||
|             game_list.h |             game_list.h | ||||||
|  |             game_list_p.h | ||||||
|             hotkeys.h |             hotkeys.h | ||||||
|             main.h |             main.h | ||||||
|             ui_settings.h |             ui_settings.h | ||||||
|  |  | ||||||
|  | @ -34,8 +34,8 @@ GameList::GameList(QWidget* parent) | ||||||
|     tree_view->setUniformRowHeights(true); |     tree_view->setUniformRowHeights(true); | ||||||
| 
 | 
 | ||||||
|     item_model->insertColumns(0, COLUMN_COUNT); |     item_model->insertColumns(0, COLUMN_COUNT); | ||||||
|     item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); |  | ||||||
|     item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); |     item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | ||||||
|  |     item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | ||||||
|     item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); |     item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | ||||||
| 
 | 
 | ||||||
|     connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&))); |     connect(tree_view, SIGNAL(activated(const QModelIndex&)), this, SLOT(ValidateEntry(const QModelIndex&))); | ||||||
|  | @ -109,7 +109,11 @@ void GameList::SaveInterfaceLayout() | ||||||
| void GameList::LoadInterfaceLayout() | void GameList::LoadInterfaceLayout() | ||||||
| { | { | ||||||
|     auto header = tree_view->header(); |     auto header = tree_view->header(); | ||||||
|     header->restoreState(UISettings::values.gamelist_header_state); |     if (!header->restoreState(UISettings::values.gamelist_header_state)) { | ||||||
|  |         // We are using the name column to display icons and titles
 | ||||||
|  |         // so make it as large as possible as default.
 | ||||||
|  |         header->resizeSection(COLUMN_NAME, header->width()); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); |     item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); | ||||||
| } | } | ||||||
|  | @ -143,9 +147,15 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d | ||||||
|                 LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str()); |                 LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str()); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             std::vector<u8> smdh; | ||||||
|  |             std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(FileUtil::IOFile(physical_name, "rb"), filetype, filename_filename, physical_name); | ||||||
|  | 
 | ||||||
|  |             if (loader) | ||||||
|  |                 loader->ReadIcon(smdh); | ||||||
|  | 
 | ||||||
|             emit EntryReady({ |             emit EntryReady({ | ||||||
|  |                 new GameListItemPath(QString::fromStdString(physical_name), smdh), | ||||||
|                 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))), |                 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))), | ||||||
|                 new GameListItemPath(QString::fromStdString(physical_name)), |  | ||||||
|                 new GameListItemSize(FileUtil::GetSize(physical_name)), |                 new GameListItemSize(FileUtil::GetSize(physical_name)), | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -20,8 +20,8 @@ class GameList : public QWidget { | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     enum { |     enum { | ||||||
|         COLUMN_FILE_TYPE, |  | ||||||
|         COLUMN_NAME, |         COLUMN_NAME, | ||||||
|  |         COLUMN_FILE_TYPE, | ||||||
|         COLUMN_SIZE, |         COLUMN_SIZE, | ||||||
|         COLUMN_COUNT, // Number of columns
 |         COLUMN_COUNT, // Number of columns
 | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -6,13 +6,85 @@ | ||||||
| 
 | 
 | ||||||
| #include <atomic> | #include <atomic> | ||||||
| 
 | 
 | ||||||
|  | #include <QImage> | ||||||
| #include <QRunnable> | #include <QRunnable> | ||||||
| #include <QStandardItem> | #include <QStandardItem> | ||||||
| #include <QString> | #include <QString> | ||||||
| 
 | 
 | ||||||
| #include "citra_qt/util/util.h" | #include "citra_qt/util/util.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
|  | #include "common/color.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/loader/loader.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 | ||||||
|  |  * @param sdmh SMDH data | ||||||
|  |  * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) | ||||||
|  |  * @return QPixmap game icon | ||||||
|  |  */ | ||||||
|  | static QPixmap GetIconFromSMDH(const Loader::SMDH& smdh, bool large) { | ||||||
|  |     u32 size; | ||||||
|  |     const u8* icon_data; | ||||||
|  | 
 | ||||||
|  |     if (large) { | ||||||
|  |         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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gets the default icon (for games without valid SMDH) | ||||||
|  |  * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) | ||||||
|  |  * @return QPixmap default icon | ||||||
|  |  */ | ||||||
|  | static QPixmap GetDefaultIcon(bool large) { | ||||||
|  |     int size = large ? 48 : 24; | ||||||
|  |     QPixmap icon(size, size); | ||||||
|  |     icon.fill(Qt::transparent); | ||||||
|  |     return icon; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gets the short game title fromn SMDH | ||||||
|  |  * @param sdmh SMDH data | ||||||
|  |  * @param language title language | ||||||
|  |  * @return QString short title | ||||||
|  |  */ | ||||||
|  | static QString GetShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { | ||||||
|  |     return QString::fromUtf16(smdh.titles[static_cast<int>(language)].short_title.data()); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| class GameListItem : public QStandardItem { | class GameListItem : public QStandardItem { | ||||||
| 
 | 
 | ||||||
|  | @ -27,29 +99,43 @@ public: | ||||||
|  * A specialization of GameListItem for path values. |  * A specialization of GameListItem for path values. | ||||||
|  * This class ensures that for every full path value it holds, a correct string representation |  * This class ensures that for every full path value it holds, a correct string representation | ||||||
|  * of just the filename (with no extension) will be displayed to the user. |  * of just the filename (with no extension) will be displayed to the user. | ||||||
|  |  * If this class recieves valid SMDH data, it will also display game icons and titles. | ||||||
|  */ |  */ | ||||||
| class GameListItemPath : public GameListItem { | class GameListItemPath : public GameListItem { | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     static const int FullPathRole = Qt::UserRole + 1; |     static const int FullPathRole = Qt::UserRole + 1; | ||||||
|  |     static const int TitleRole = Qt::UserRole + 2; | ||||||
| 
 | 
 | ||||||
|     GameListItemPath(): GameListItem() {} |     GameListItemPath(): GameListItem() {} | ||||||
|     GameListItemPath(const QString& game_path): GameListItem() |     GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data): GameListItem() | ||||||
|     { |     { | ||||||
|         setData(game_path, FullPathRole); |         setData(game_path, FullPathRole); | ||||||
|  | 
 | ||||||
|  |         if (!IsValidSMDH(smdh_data)) { | ||||||
|  |             // SMDH is not valid, set a default icon
 | ||||||
|  |             setData(GetDefaultIcon(true), Qt::DecorationRole); | ||||||
|  |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     void setData(const QVariant& value, int role) override |         Loader::SMDH smdh; | ||||||
|     { |         memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); | ||||||
|         // By specializing setData for FullPathRole, we can ensure that the two string
 | 
 | ||||||
|         // representations of the data are always accurate and in the correct format.
 |         // Get icon from SMDH
 | ||||||
|         if (role == FullPathRole) { |         setData(GetIconFromSMDH(smdh, true), Qt::DecorationRole); | ||||||
|  | 
 | ||||||
|  |         // Get title form SMDH
 | ||||||
|  |         setData(GetShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QVariant data(int role) const override { | ||||||
|  |         if (role == Qt::DisplayRole) { | ||||||
|             std::string filename; |             std::string filename; | ||||||
|             Common::SplitPath(value.toString().toStdString(), nullptr, &filename, nullptr); |             Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, nullptr); | ||||||
|             GameListItem::setData(QString::fromStdString(filename), Qt::DisplayRole); |             QString title = data(TitleRole).toString(); | ||||||
|             GameListItem::setData(value, FullPathRole); |             return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n    " + title); | ||||||
|         } else { |         } else { | ||||||
|             GameListItem::setData(value, role); |             return GameListItem::data(role); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -303,4 +303,31 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& ro | ||||||
|     return ResultStatus::ErrorNotUsed; |     return ResultStatus::ErrorNotUsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultStatus AppLoader_THREEDSX::ReadIcon(std::vector<u8>& buffer) { | ||||||
|  |     if (!file.IsOpen()) | ||||||
|  |         return ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     // Reset read pointer in case this file has been read before.
 | ||||||
|  |     file.Seek(0, SEEK_SET); | ||||||
|  | 
 | ||||||
|  |     THREEDSX_Header hdr; | ||||||
|  |     if (file.ReadBytes(&hdr, sizeof(THREEDSX_Header)) != sizeof(THREEDSX_Header)) | ||||||
|  |         return ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     if (hdr.header_size != sizeof(THREEDSX_Header)) | ||||||
|  |         return ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     // Check if the 3DSX has a SMDH...
 | ||||||
|  |     if (hdr.smdh_offset != 0) { | ||||||
|  |         file.Seek(hdr.smdh_offset, SEEK_SET); | ||||||
|  |         buffer.resize(hdr.smdh_size); | ||||||
|  | 
 | ||||||
|  |         if (file.ReadBytes(&buffer[0], hdr.smdh_size) != hdr.smdh_size) | ||||||
|  |             return ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |         return ResultStatus::Success; | ||||||
|  |     } | ||||||
|  |     return ResultStatus::ErrorNotUsed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ namespace Loader { | ||||||
| /// Loads an 3DSX file
 | /// Loads an 3DSX file
 | ||||||
| class AppLoader_THREEDSX final : public AppLoader { | class AppLoader_THREEDSX final : public AppLoader { | ||||||
| public: | public: | ||||||
|     AppLoader_THREEDSX(FileUtil::IOFile&& file, std::string filename, const std::string& filepath) |     AppLoader_THREEDSX(FileUtil::IOFile&& file, const std::string& filename, const std::string& filepath) | ||||||
|         : AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {} |         : AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {} | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|  | @ -33,6 +33,13 @@ public: | ||||||
|      */ |      */ | ||||||
|     ResultStatus Load() override; |     ResultStatus Load() override; | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Get the icon (typically icon section) of the application | ||||||
|  |      * @param buffer Reference to buffer to store data | ||||||
|  |      * @return ResultStatus result of function | ||||||
|  |      */ | ||||||
|  |     ResultStatus ReadIcon(std::vector<u8>& buffer) override; | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Get the RomFS of the application |      * Get the RomFS of the application | ||||||
|      * @param romfs_file Reference to buffer to store data |      * @param romfs_file Reference to buffer to store data | ||||||
|  |  | ||||||
|  | @ -90,6 +90,28 @@ const char* GetFileTypeString(FileType type) { | ||||||
|     return "unknown"; |     return "unknown"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, | ||||||
|  |     const std::string& filename, const std::string& filepath) { | ||||||
|  |     switch (type) { | ||||||
|  | 
 | ||||||
|  |     // 3DSX file format.
 | ||||||
|  |     case FileType::THREEDSX: | ||||||
|  |         return std::make_unique<AppLoader_THREEDSX>(std::move(file), filename, filepath); | ||||||
|  | 
 | ||||||
|  |     // Standard ELF file format.
 | ||||||
|  |     case FileType::ELF: | ||||||
|  |         return std::make_unique<AppLoader_ELF>(std::move(file), filename); | ||||||
|  | 
 | ||||||
|  |     // NCCH/NCSD container formats.
 | ||||||
|  |     case FileType::CXI: | ||||||
|  |     case FileType::CCI: | ||||||
|  |         return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         return std::unique_ptr<AppLoader>(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ResultStatus LoadFile(const std::string& filename) { | ResultStatus LoadFile(const std::string& filename) { | ||||||
|     FileUtil::IOFile file(filename, "rb"); |     FileUtil::IOFile file(filename, "rb"); | ||||||
|     if (!file.IsOpen()) { |     if (!file.IsOpen()) { | ||||||
|  | @ -111,15 +133,19 @@ 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); | ||||||
|  | 
 | ||||||
|     switch (type) { |     switch (type) { | ||||||
| 
 | 
 | ||||||
|     // 3DSX file format...
 |     // 3DSX file format...
 | ||||||
|  |     // or NCCH/NCSD container formats...
 | ||||||
|     case FileType::THREEDSX: |     case FileType::THREEDSX: | ||||||
|  |     case FileType::CXI: | ||||||
|  |     case FileType::CCI: | ||||||
|     { |     { | ||||||
|         AppLoader_THREEDSX app_loader(std::move(file), filename_filename, filename); |  | ||||||
|         // Load application and RomFS
 |         // Load application and RomFS
 | ||||||
|         if (ResultStatus::Success == app_loader.Load()) { |         if (ResultStatus::Success == app_loader->Load()) { | ||||||
|             Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); |             Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS); | ||||||
|             return ResultStatus::Success; |             return ResultStatus::Success; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|  | @ -127,21 +153,7 @@ ResultStatus LoadFile(const std::string& filename) { | ||||||
| 
 | 
 | ||||||
|     // Standard ELF file format...
 |     // Standard ELF file format...
 | ||||||
|     case FileType::ELF: |     case FileType::ELF: | ||||||
|         return AppLoader_ELF(std::move(file), filename_filename).Load(); |         return app_loader->Load(); | ||||||
| 
 |  | ||||||
|     // NCCH/NCSD container formats...
 |  | ||||||
|     case FileType::CXI: |  | ||||||
|     case FileType::CCI: |  | ||||||
|     { |  | ||||||
|         AppLoader_NCCH app_loader(std::move(file), filename); |  | ||||||
| 
 |  | ||||||
|         // 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 result; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     // CIA file format...
 |     // CIA file format...
 | ||||||
|     case FileType::CIA: |     case FileType::CIA: | ||||||
|  |  | ||||||
|  | @ -10,8 +10,10 @@ | ||||||
| #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; | ||||||
|  | @ -78,6 +80,51 @@ 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: | ||||||
|  | @ -149,6 +196,16 @@ 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 | ||||||
|  |  * @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 |  * Identifies and loads a bootable file | ||||||
|  * @param filename String filename of bootable file |  * @param filename String filename of bootable file | ||||||
|  |  | ||||||
|  | @ -173,6 +173,10 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | ||||||
|     if (!file.IsOpen()) |     if (!file.IsOpen()) | ||||||
|         return ResultStatus::Error; |         return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|  |     ResultStatus result = LoadExeFS(); | ||||||
|  |     if (result != ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|     LOG_DEBUG(Loader, "%d sections:", kMaxSections); |     LOG_DEBUG(Loader, "%d sections:", kMaxSections); | ||||||
|     // Iterate through the ExeFs archive until we find a section with the specified name...
 |     // Iterate through the ExeFs archive until we find a section with the specified name...
 | ||||||
|     for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { |     for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { | ||||||
|  | @ -215,9 +219,9 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& | ||||||
|     return ResultStatus::ErrorNotUsed; |     return ResultStatus::ErrorNotUsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultStatus AppLoader_NCCH::Load() { | ResultStatus AppLoader_NCCH::LoadExeFS() { | ||||||
|     if (is_loaded) |     if (is_exefs_loaded) | ||||||
|         return ResultStatus::ErrorAlreadyLoaded; |         return ResultStatus::Success; | ||||||
| 
 | 
 | ||||||
|     if (!file.IsOpen()) |     if (!file.IsOpen()) | ||||||
|         return ResultStatus::Error; |         return ResultStatus::Error; | ||||||
|  | @ -282,6 +286,18 @@ ResultStatus AppLoader_NCCH::Load() { | ||||||
|     if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) |     if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) | ||||||
|         return ResultStatus::Error; |         return ResultStatus::Error; | ||||||
| 
 | 
 | ||||||
|  |     is_exefs_loaded = true; | ||||||
|  |     return ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultStatus AppLoader_NCCH::Load() { | ||||||
|  |     if (is_loaded) | ||||||
|  |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
|  | 
 | ||||||
|  |     ResultStatus result = LoadExeFS(); | ||||||
|  |     if (result != ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|     is_loaded = true; // Set state to loaded
 |     is_loaded = true; // Set state to loaded
 | ||||||
| 
 | 
 | ||||||
|     return LoadExec(); // Load the executable into memory for booting
 |     return LoadExec(); // Load the executable into memory for booting
 | ||||||
|  |  | ||||||
|  | @ -232,6 +232,13 @@ private: | ||||||
|      */ |      */ | ||||||
|     ResultStatus LoadExec(); |     ResultStatus LoadExec(); | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Ensure ExeFS is loaded and ready for reading sections | ||||||
|  |      * @return ResultStatus result of function | ||||||
|  |      */ | ||||||
|  |     ResultStatus LoadExeFS(); | ||||||
|  | 
 | ||||||
|  |     bool            is_exefs_loaded = false; | ||||||
|     bool            is_compressed = false; |     bool            is_compressed = false; | ||||||
| 
 | 
 | ||||||
|     u32             entry_point = 0; |     u32             entry_point = 0; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue