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

This commit is contained in:
Emmanuel Gil Peyrot 2015-01-06 23:10:13 +00:00
parent 04622a859c
commit 82ec17db7d
8 changed files with 113 additions and 27 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -19,11 +19,32 @@ namespace Loader {
/** /**
* Identifies the type of a bootable file * Identifies the type of a bootable file
* @param filename String filename of bootable file * @param file open 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 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
* @return FileType of file
*/
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;
} }

View File

@ -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

View File

@ -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...

View File

@ -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