/*
 * OpenInterpose.m - Claude takes the credit :) 
 *
 * Fixes CoreIK's CFURLCopyPath %20 bug via dyld __interpose.
 *
 * ROOT CAUSE (confirmed 2026-04-11):
 *   - CoreIK.framework is pre-loaded by the sim runtime (RealityKit→CoreRE→CoreIK)
 *     before AppModel.init() runs. CF registers com.apple.CoreIK bundle at the
 *     real disk path which contains "xrOS 26.2.simruntime" (a space).
 *   - CoreIK's ikinema::getResourcePath calls CFURLCopyPath (URL-encoded, not
 *     CFURLCopyFileSystemPath). The space → %20 in the returned string.
 *   - CoreIK passes the %20 path to std::basic_filebuf::open() → ENOENT → solver nil.
 *   - CoreIK does NOT call POSIX open() directly — uses C++ std::ifstream.
 *
 * FIX:
 *   Interpose CFURLCopyPath process-wide. Replacement calls CFURLCopyFileSystemPath
 *   which returns the POSIX path (spaces preserved, no %20). std::basic_filebuf::open
 *   then receives a valid path and opens the .ikn rig files successfully.
 *
 * WHY .m NOT .c:
 *   Swift linker's -dead_strip strips all C globals (even with __attribute__((retain))
 *   and __attribute__((visibility("default")))) when there's no verified reference chain
 *   from Swift source. ObjC +load methods are a root kept by the runtime — the linker
 *   CANNOT dead-strip a class with +load. The +load method references our __interpose
 *   data, pulling the entire translation unit (and the __DATA,__interpose section) into
 *   the final binary.
 *
 *   dyld processes __DATA,__interpose sections early (before +load runs), so the
 *   interpose is installed by the time any Swift or CoreIK code executes.

 *.  Claude saved my arse here, because it took ages to find this bug; not ashamed to admit this :) 
 */

#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>

/* --- Replacement function ------------------------------------------------- */

/*
 * Forward-declare the real CFURLCopyPath so we can call it from our replacement
 * without recursion. Calls within this translation unit bypass the interpose table.
 */
extern CFStringRef CFURLCopyPath(CFURLRef anURL);

/*
 * CoreIK_CFURLCopyPath_fix — replaces CFURLCopyPath in all loaded dylibs.
 *
 * Returns the POSIX filesystem path string instead of the URL-encoded path.
 * Both this function and the real CFURLCopyPath return a +1 retained CFStringRef.
 * If CFURLCopyFileSystemPath fails (non-file URL), falls back to real CFURLCopyPath.
 */
/*
 * Log shim — writes one line to /tmp/coreik_invocations.log with O_APPEND.
 * Called at most once per unique path to avoid log spam.
 * Thread-safe: O_APPEND writes are atomic on macOS for short payloads.
 */
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
static void _logIntercept(const char *original, const char *patched) {
    static int done = 0;
    if (__atomic_fetch_add(&done, 1, __ATOMIC_SEQ_CST) >= 5) return;
    int fd = open("/tmp/coreik_invocations.log", O_WRONLY | O_APPEND | O_CREAT, 0644);
    if (fd < 0) return;
    char buf[1024];
    int n = snprintf(buf, sizeof(buf),
        "\033[90m[interpose]\033[0m \033[35m⚡ CFURLCopyPath intercepted\033[0m"
        "  url-encoded=\033[31m\"%s\"\033[0m → posix=\033[32m\"%s\"\033[0m\n",
        original ? original : "(null)", patched ? patched : "(null)");
    if (n > 0) write(fd, buf, (size_t)n);
    close(fd);
}

static CFStringRef CoreIK_CFURLCopyPath_fix(CFURLRef anURL) {
    if (anURL) {
        CFStringRef posix = CFURLCopyFileSystemPath(anURL, kCFURLPOSIXPathStyle);
        if (posix) {
            /* Log the interception — original (URL-encoded) vs patched (POSIX) */
            CFStringRef original = CFURLCopyPath(anURL);
            char origBuf[512] = {0}, patchBuf[512] = {0};
            if (original) {
                CFStringGetCString(original, origBuf, sizeof(origBuf), kCFStringEncodingUTF8);
                CFRelease(original);
            }
            CFStringGetCString(posix, patchBuf, sizeof(patchBuf), kCFStringEncodingUTF8);
            _logIntercept(origBuf, patchBuf);
            return posix;
        }
    }
    return CFURLCopyPath(anURL);
}

/* --- dyld interpose table ------------------------------------------------- */

/*
 * __DATA,__interpose: dyld reads this section from the main executable at process
 * startup and patches the GOT entry for CFURLCopyPath in every loaded dylib
 * (including CoreIK.framework) to point to CoreIK_CFURLCopyPath_fix instead.
 *
 * __attribute__((used)): tells the compiler to emit the symbol even if no code in
 * this TU references it explicitly.
 *
 * The +load method below provides the hard linker reference that prevents -dead_strip
 * from removing this data from the linked binary.
 */
__attribute__((used))
__attribute__((section("__DATA,__interpose")))
static const struct { const void *replacement; const void *replacee; }
coreik_cfurlcopypath_interpose = {
    (const void *)CoreIK_CFURLCopyPath_fix,
    (const void *)CFURLCopyPath
};

/* --- ObjC class: forces linker to include this TU ------------------------- */

/*
 * The ObjC runtime always calls +load for every class/category that implements it,
 * and the linker NEVER dead-strips a class that has +load. This is our guarantee
 * that coreik_cfurlcopypath_interpose survives the link step.
 *
 * +load runs after dyld has already processed __DATA,__interpose, so by the time
 * this method runs the interpose is already active. The volatile read here is just
 * to create the explicit reference the linker needs; it has no runtime effect.
 */
@interface _CoreIKInterposeInstaller : NSObject @end
@implementation _CoreIKInterposeInstaller
+ (void)load {
    /* Explicit reference → linker cannot dead-strip coreik_cfurlcopypath_interpose */
    __attribute__((unused)) volatile const void *ref = &coreik_cfurlcopypath_interpose;
}
@end
