mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-27 00:50:05 +00:00
Loader: Guess filetype from the magic, or fallback to the extension.
This commit is contained in:
parent
04622a859c
commit
82ec17db7d
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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...
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user