Does anyone know if it is possible to load the config.xml and qcar-resources.dat (The files that contain the marker info) from outside the app bundle?
According to the official forums and documentation, it is not. However, there is an app out there called Blippar which seems to be doing it. I extracted the app Documents folder from an iPhone backup and they seem to have a config.xml in there that has more tracking markers than the one that is in the app bundle.
How could they have done this if the QCAR SDK doesn't allow you to specify a location of the tracking files?
I tried being hacky and creating a category on NSBundle to override
to return a path from the documents folder instead, but that didn't seem to work.
Ok, I've solved it.
First I created a category on NSBundle, and created new implementations of pretty much every method related to loading files from the bundle, until I found the one Qualcomm are using, which is:
- (NSString *)pathForResource:(NSString *)name ofType:(NSString *)ext inDirectory:(NSString *)subpath;
Once I had that, I changed my category method to be:
#define XFILPATH4DOCUMENT(_value) [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:_value]
- (NSString *)pathForResourceOverride:(NSString *)name ofType:(NSString *)ext inDirectory:(NSString *)subpath
{
if (([name isEqualToString:@"config"] && [ext isEqualToString:@"xml"]) || ([name isEqualToString:@"qcar-resources"] && [ext isEqualToString:@"dat"]))
{
// OMG
NSString *fileName = [NSString stringWithFormat:@"%@.%@", name, ext];
NSString *path = XFILPATH4DOCUMENT(fileName);
NSLog(@"%@", path);
return path;
}
else
{
return [self pathForResourceOverride:name ofType:ext inDirectory:subpath];
}
}
Then, through the magic of method swizzling:
Swizzle([NSBundle class], @selector(pathForResource:ofType:inDirectory:), @selector(pathForResourceOverride:ofType:inDirectory:));
Using this method:
#import <objc/runtime.h>
#import <objc/message.h>
void Swizzle(Class c, SEL orig, SEL replacement)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, replacement);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, replacement, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
Which I got from the answer here: Method Swizzle on iPhone device
Scary? Yes.
But it works.