mirror of
https://github.com/QuasarApp/macdependency.git
synced 2025-05-02 14:59:34 +00:00
213 lines
6.4 KiB
C++
213 lines
6.4 KiB
C++
#include "macho.h"
|
|
#include "machoexception.h"
|
|
|
|
#include "/usr/include/mach-o/fat.h"
|
|
|
|
#include <QtCore/QFileInfo>
|
|
#include <QtCore/QDir>
|
|
#include <QtCore/QDateTime>
|
|
#include <QtGui/QFileIconProvider>
|
|
|
|
// static variables
|
|
Demangler* MachO::demangler = 0;
|
|
DynamicLoader* MachO::dynamicLoader = 0;
|
|
int MachO::referenceCounter = 0;
|
|
|
|
MachO::MachO(const QString& fileName, const MachO* parent) : bundle(0)
|
|
{
|
|
// check if filename is bundle
|
|
QString machOFileName;
|
|
machOFileName = getApplicationInBundle(fileName);
|
|
if (machOFileName.isNull())
|
|
machOFileName = fileName;
|
|
|
|
init(machOFileName, parent);
|
|
|
|
if (referenceCounter == 0) {
|
|
demangler = new Demangler();
|
|
dynamicLoader = new DynamicLoader();
|
|
}
|
|
referenceCounter++;
|
|
}
|
|
|
|
QString MachO::getApplicationInBundle(const QString& bundlePath) {
|
|
QFileInfo fileInformation(bundlePath);
|
|
|
|
QString appPath;
|
|
// check if it is bundle at all
|
|
if (!fileInformation.isBundle() || !fileInformation.isExecutable()) {
|
|
return QString();
|
|
}
|
|
|
|
CFURLRef bundleUrl = 0;
|
|
QByteArray utf8BundlePath = bundlePath.toUtf8();
|
|
|
|
bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)utf8BundlePath.data() , utf8BundlePath.length(), true);
|
|
if (bundleUrl != NULL) {
|
|
bundle = CFBundleCreate(NULL, bundleUrl);
|
|
CFRelease(bundleUrl);
|
|
if (bundle != NULL) {
|
|
CFURLRef executableUrl = CFBundleCopyExecutableURL(bundle);
|
|
if (executableUrl != 0) {
|
|
char executableFile[FILENAME_MAX];
|
|
CFURLGetFileSystemRepresentation(executableUrl, true, (UInt8 *)executableFile, FILENAME_MAX);
|
|
appPath = executableFile;
|
|
CFRelease(executableUrl);
|
|
this->bundlePath = bundlePath;
|
|
}
|
|
}
|
|
}
|
|
return appPath;
|
|
}
|
|
|
|
void MachO::init(const QString& fileName, const MachO* parent)
|
|
{
|
|
MachOFile* parentFile = 0;
|
|
if (parent) {
|
|
parentFile = parent->file;
|
|
}
|
|
file = new MachOFile(fileName, parentFile);
|
|
// read out magic number
|
|
uint32_t magic = file->readUint32();
|
|
|
|
// Universal magic is always BE
|
|
if (file->getUint32BE(magic) == FAT_MAGIC) {
|
|
// this is an universal file
|
|
|
|
// get number of architecture headers (BE)
|
|
uint32_t numberOfArchitectures = file->readUint32BE();
|
|
|
|
// read out all architecture headers
|
|
fat_arch fatArch[numberOfArchitectures];
|
|
file->readBytes((char*)fatArch, sizeof(fat_arch)*numberOfArchitectures);
|
|
|
|
// go through all architectures
|
|
for (unsigned int n=0; n < numberOfArchitectures; n++) {
|
|
unsigned int offset = file->getUint32BE(fatArch[n].offset);
|
|
file->seek(offset);
|
|
// read out magic number
|
|
uint32_t magic = file->readUint32();
|
|
file->seek(offset);
|
|
MachOArchitecture* architecture = new MachOArchitecture(*file, magic, file->getUint32BE(fatArch[n].size));
|
|
if (parent)
|
|
architecture->initParentArchitecture(parent->getCompatibleArchitecture(architecture));
|
|
architectures.push_back(architecture);
|
|
}
|
|
} else {
|
|
// seek back to beginning
|
|
file->seek(0);
|
|
MachOArchitecture* architecture = new MachOArchitecture(*file, magic, getSize());
|
|
if (parent)
|
|
architecture->initParentArchitecture(parent->getCompatibleArchitecture(architecture));
|
|
architectures.push_back(architecture);
|
|
}
|
|
}
|
|
|
|
|
|
MachO::~MachO() {
|
|
referenceCounter--;
|
|
if (referenceCounter == 0) {
|
|
delete demangler;
|
|
demangler = 0;
|
|
delete dynamicLoader;
|
|
dynamicLoader = 0;
|
|
}
|
|
|
|
for (std::vector<MachOArchitecture*>::iterator it = architectures.begin();
|
|
it != architectures.end();
|
|
++it)
|
|
{
|
|
delete *it;
|
|
}
|
|
delete file;
|
|
if (bundle)
|
|
CFRelease(bundle);
|
|
}
|
|
|
|
// choose an architecture which is compatible to the given architecture
|
|
MachOArchitecture* MachO::getCompatibleArchitecture(MachOArchitecture* destArchitecture) const {
|
|
MachOHeader::CpuType destCpuType = destArchitecture->getHeader()->getCpuType();
|
|
|
|
// go through all architectures
|
|
for (std::vector<MachOArchitecture*>::const_iterator it = architectures.begin();
|
|
it != architectures.end();
|
|
++it)
|
|
{
|
|
// TODO: match subtypes (only for PowerPC necessary)
|
|
if ((*it)->getHeader()->getCpuType() == destCpuType)
|
|
return *it;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
long long int MachO::getSize() const {
|
|
return file->getSize();
|
|
}
|
|
|
|
time_t MachO::getLastModificationDate() const {
|
|
return QFileInfo(file->getName()).lastModified().toTime_t();
|
|
}
|
|
|
|
// return bundle version if available, otherwise NULL string
|
|
QString MachO::getBundleVersion() const {
|
|
QString version;
|
|
if (bundle != 0) {
|
|
CFStringRef cfVersion = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
|
|
// is version available at all?
|
|
if (cfVersion) {
|
|
version = extractStringFromCFStringRef(cfVersion);
|
|
}
|
|
}
|
|
return version;
|
|
}
|
|
|
|
QString MachO::getBundleName() const {
|
|
QString bundleName;
|
|
if (bundle != 0) {
|
|
CFStringRef cfBundleName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey);
|
|
// is version available at all?
|
|
if (cfBundleName) {
|
|
bundleName = extractStringFromCFStringRef(cfBundleName);
|
|
}
|
|
}
|
|
return bundleName;
|
|
}
|
|
|
|
QString MachO::extractStringFromCFStringRef(CFStringRef cfStringRef) {
|
|
QString string;
|
|
const char* szString = CFStringGetCStringPtr(cfStringRef, kCFStringEncodingASCII);
|
|
if (szString == NULL) {
|
|
CFIndex stringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfStringRef), kCFStringEncodingASCII);
|
|
|
|
char szStringNew[stringLength + 1];
|
|
if (CFStringGetCString(cfStringRef,
|
|
szStringNew,
|
|
stringLength+1,
|
|
kCFStringEncodingASCII
|
|
))
|
|
string = szStringNew;
|
|
delete[] szString;
|
|
} else {
|
|
string = szString;
|
|
}
|
|
return string;
|
|
}
|
|
|
|
QIcon MachO::getIcon() const {
|
|
QFileIconProvider iconProvider;
|
|
QFileInfo fileInfo;
|
|
// get icon from bundle
|
|
if (!bundlePath.isNull())
|
|
fileInfo = QFileInfo(bundlePath);
|
|
else
|
|
fileInfo = QFileInfo(getFileName());
|
|
|
|
return iconProvider.icon(fileInfo);
|
|
}
|
|
|
|
QString MachO::getPath() const {
|
|
return file->getPath();
|
|
}
|
|
|
|
|