Quellcode durchsuchen

Loader: Guess filetype from the magic, or fallback to the extension.

Emmanuel Gil Peyrot vor 11 Jahren
Ursprung
Commit
82ec17db7d

+ 12 - 1
src/core/loader/3dsx.cpp

@@ -44,7 +44,6 @@ enum THREEDSX_Error {
 static const u32 RELOCBUFSIZE = 512;
 static const u32 RELOCBUFSIZE = 512;
 
 
 // File header
 // File header
-static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
 #pragma pack(1)
 #pragma pack(1)
 struct THREEDSX_Header
 struct THREEDSX_Header
 {
 {
@@ -202,6 +201,18 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr)
     return ERROR_NONE;
     return ERROR_NONE;
 }
 }
 
 
+FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) {
+    u32 magic;
+    file.Seek(0, SEEK_SET);
+    if (1 != file.ReadArray<u32>(&magic, 1))
+        return FileType::Error;
+
+    if (MakeMagic('3', 'D', 'S', 'X') == magic)
+        return FileType::THREEDSX;
+
+    return FileType::Error;
+}
+
 ResultStatus AppLoader_THREEDSX::Load() {
 ResultStatus AppLoader_THREEDSX::Load() {
     if (is_loaded)
     if (is_loaded)
         return ResultStatus::ErrorAlreadyLoaded;
         return ResultStatus::ErrorAlreadyLoaded;

+ 7 - 0
src/core/loader/3dsx.h

@@ -17,6 +17,13 @@ class AppLoader_THREEDSX final : public AppLoader {
 public:
 public:
     AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
     AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
 
 
+    /**
+     * Returns the type of the file
+     * @param file FileUtil::IOFile open file
+     * @return FileType found, or FileType::Error if this loader doesn't know it
+     */
+    static FileType IdentifyType(FileUtil::IOFile& file);
+
     /**
     /**
      * Load the bootable file
      * Load the bootable file
      * @return ResultStatus result of function
      * @return ResultStatus result of function

+ 12 - 0
src/core/loader/elf.cpp

@@ -330,6 +330,18 @@ bool ElfReader::LoadSymbols() {
 
 
 namespace Loader {
 namespace Loader {
 
 
+FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) {
+    u32 magic;
+    file.Seek(0, SEEK_SET);
+    if (1 != file.ReadArray<u32>(&magic, 1))
+        return FileType::Error;
+
+    if (MakeMagic('\x7f', 'E', 'L', 'F') == magic)
+        return FileType::ELF;
+
+    return FileType::Error;
+}
+
 ResultStatus AppLoader_ELF::Load() {
 ResultStatus AppLoader_ELF::Load() {
     if (is_loaded)
     if (is_loaded)
         return ResultStatus::ErrorAlreadyLoaded;
         return ResultStatus::ErrorAlreadyLoaded;

+ 7 - 0
src/core/loader/elf.h

@@ -17,6 +17,13 @@ class AppLoader_ELF final : public AppLoader {
 public:
 public:
     AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
     AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
 
 
+    /**
+     * Returns the type of the file
+     * @param file FileUtil::IOFile open file
+     * @return FileType found, or FileType::Error if this loader doesn't know it
+     */
+    static FileType IdentifyType(FileUtil::IOFile& file);
+
     /**
     /**
      * Load the bootable file
      * Load the bootable file
      * @return ResultStatus result of function
      * @return ResultStatus result of function

+ 45 - 15
src/core/loader/loader.cpp

@@ -19,11 +19,32 @@ namespace Loader {
 
 
 /**
 /**
  * Identifies the type of a bootable file
  * Identifies the type of a bootable file
+ * @param file open file
+ * @return FileType of file
+ */
+static FileType IdentifyFile(FileUtil::IOFile& file) {
+    FileType type;
+
+#define CHECK_TYPE(loader) \
+    type = AppLoader_##loader::IdentifyType(file); \
+    if (FileType::Error != type) \
+        return type;
+
+    CHECK_TYPE(THREEDSX)
+    CHECK_TYPE(ELF)
+    CHECK_TYPE(NCCH)
+
+#undef CHECK_TYPE
+
+    return FileType::Unknown;
+}
+
+/**
+ * Guess the type of a bootable file from its extension
  * @param filename String filename of bootable file
  * @param filename String filename of bootable file
- * @todo (ShizZy) this function sucks... make it actually check file contents etc.
  * @return FileType of file
  * @return FileType of file
  */
  */
-FileType IdentifyFile(const std::string &filename) {
+static FileType GuessFromFilename(const std::string& filename) {
     if (filename.size() == 0) {
     if (filename.size() == 0) {
         LOG_ERROR(Loader, "invalid filename %s", filename.c_str());
         LOG_ERROR(Loader, "invalid filename %s", filename.c_str());
         return FileType::Error;
         return FileType::Error;
@@ -34,22 +55,20 @@ FileType IdentifyFile(const std::string &filename) {
         return FileType::Unknown;
         return FileType::Unknown;
     std::string extension = Common::ToLower(filename.substr(extension_loc));
     std::string extension = Common::ToLower(filename.substr(extension_loc));
 
 
-    // TODO(bunnei): Do actual filetype checking instead of naively checking the extension
-    if (extension == ".elf") {
+    if (extension == ".elf")
         return FileType::ELF;
         return FileType::ELF;
-    } else if (extension == ".axf") {
+    else if (extension == ".axf")
         return FileType::ELF;
         return FileType::ELF;
-    } else if (extension == ".cxi") {
+    else if (extension == ".cxi")
         return FileType::CXI;
         return FileType::CXI;
-    } else if (extension == ".cci") {
+    else if (extension == ".cci")
         return FileType::CCI;
         return FileType::CCI;
-    } else if (extension == ".bin") {
+    else if (extension == ".bin")
         return FileType::BIN;
         return FileType::BIN;
-    } else if (extension == ".3ds") {
+    else if (extension == ".3ds")
         return FileType::CCI;
         return FileType::CCI;
-    } else if (extension == ".3dsx") {
+    else if (extension == ".3dsx")
         return FileType::THREEDSX;
         return FileType::THREEDSX;
-    }
     return FileType::Unknown;
     return FileType::Unknown;
 }
 }
 
 
@@ -60,7 +79,16 @@ ResultStatus LoadFile(const std::string& filename) {
     if (!file->IsOpen())
     if (!file->IsOpen())
         return ResultStatus::Error;
         return ResultStatus::Error;
 
 
-    switch (IdentifyFile(filename)) {
+    FileType type = IdentifyFile(*file);
+    FileType filename_type = GuessFromFilename(filename);
+
+    if (type != filename_type) {
+        LOG_WARNING(Loader, "File %s has a different type than its extension.", filename.c_str());
+        if (FileType::Unknown == type)
+            type = filename_type;
+    }
+
+    switch (type) {
 
 
     //3DSX file format...
     //3DSX file format...
     case FileType::THREEDSX:
     case FileType::THREEDSX:
@@ -72,7 +100,8 @@ ResultStatus LoadFile(const std::string& filename) {
 
 
     // NCCH/NCSD container formats...
     // NCCH/NCSD container formats...
     case FileType::CXI:
     case FileType::CXI:
-    case FileType::CCI: {
+    case FileType::CCI:
+    {
         AppLoader_NCCH app_loader(std::move(file));
         AppLoader_NCCH app_loader(std::move(file));
 
 
         // Load application and RomFS
         // Load application and RomFS
@@ -100,10 +129,11 @@ ResultStatus LoadFile(const std::string& filename) {
 
 
     // IdentifyFile could know identify file type...
     // IdentifyFile could know identify file type...
     case FileType::Unknown:
     case FileType::Unknown:
-
-    default:
+    {
+        LOG_CRITICAL(Loader, "File %s is of unknown type.");
         return ResultStatus::ErrorInvalidFormat;
         return ResultStatus::ErrorInvalidFormat;
     }
     }
+    }
     return ResultStatus::Error;
     return ResultStatus::Error;
 }
 }
 
 

+ 4 - 7
src/core/loader/loader.h

@@ -38,6 +38,10 @@ enum class ResultStatus {
     ErrorMemoryAllocationFailed,
     ErrorMemoryAllocationFailed,
 };
 };
 
 
+static u32 MakeMagic(char a, char b, char c, char d) {
+    return a | b << 8 | c << 16 | d << 24;
+}
+
 /// Interface for loading an application
 /// Interface for loading an application
 class AppLoader : NonCopyable {
 class AppLoader : NonCopyable {
 public:
 public:
@@ -100,13 +104,6 @@ protected:
     bool                              is_loaded = false;
     bool                              is_loaded = false;
 };
 };
 
 
-/**
- * Identifies the type of a bootable file
- * @param filename String filename of bootable file
- * @return FileType of file
- */
-FileType IdentifyFile(const std::string &filename);
-
 /**
 /**
  * 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

+ 17 - 2
src/core/loader/ncch.cpp

@@ -97,6 +97,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // AppLoader_NCCH class
 // AppLoader_NCCH class
 
 
+FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
+    u32 magic;
+    file.Seek(0x100, SEEK_SET);
+    if (1 != file.ReadArray<u32>(&magic, 1))
+        return FileType::Error;
+
+    if (MakeMagic('N', 'C', 'S', 'D') == magic)
+        return FileType::CCI;
+
+    if (MakeMagic('N', 'C', 'C', 'H') == magic)
+        return FileType::CXI;
+
+    return FileType::Error;
+}
+
 ResultStatus AppLoader_NCCH::LoadExec() const {
 ResultStatus AppLoader_NCCH::LoadExec() const {
     if (!is_loaded)
     if (!is_loaded)
         return ResultStatus::ErrorNotLoaded;
         return ResultStatus::ErrorNotLoaded;
@@ -171,7 +186,7 @@ ResultStatus AppLoader_NCCH::Load() {
     file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
     file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
 
 
     // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
     // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
-    if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
+    if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
         LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
         LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
         ncch_offset = 0x4000;
         ncch_offset = 0x4000;
         file->Seek(ncch_offset, SEEK_SET);
         file->Seek(ncch_offset, SEEK_SET);
@@ -179,7 +194,7 @@ ResultStatus AppLoader_NCCH::Load() {
     }
     }
 
 
     // Verify we are loading the correct file type...
     // Verify we are loading the correct file type...
-    if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
+    if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
         return ResultStatus::ErrorInvalidFormat;
         return ResultStatus::ErrorInvalidFormat;
 
 
     // Read ExHeader...
     // Read ExHeader...

+ 8 - 1
src/core/loader/ncch.h

@@ -13,7 +13,7 @@
 
 
 struct NCCH_Header {
 struct NCCH_Header {
     u8 signature[0x100];
     u8 signature[0x100];
-    char magic[4];
+    u32 magic;
     u32 content_size;
     u32 content_size;
     u8 partition_id[8];
     u8 partition_id[8];
     u16 maker_code;
     u16 maker_code;
@@ -148,6 +148,13 @@ class AppLoader_NCCH final : public AppLoader {
 public:
 public:
     AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
     AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
 
 
+    /**
+     * Returns the type of the file
+     * @param file FileUtil::IOFile open file
+     * @return FileType found, or FileType::Error if this loader doesn't know it
+     */
+    static FileType IdentifyType(FileUtil::IOFile& file);
+
     /**
     /**
      * Load the application
      * Load the application
      * @return ResultStatus result of function
      * @return ResultStatus result of function