// I think I used this for debugging.
// I don't use it for anything now / ground.

#include <IOKit/IOKitLib.h>
#include <stdio.h>
#include <string.h>

typedef struct { char major, minor, build, reserved[1]; UInt16 release; } SMCVersion;
typedef struct { UInt16 version, length; UInt32 cpuPLimit, gpuPLimit, memPLimit; } SMCPLimitData;
typedef struct { UInt32 dataSize, dataType; UInt8 dataAttributes; } SMCKeyInfoData;

typedef struct {
    UInt32 key;
    SMCVersion vers;
    SMCPLimitData pLimitData;
    SMCKeyInfoData keyInfo;
    UInt8 result, status, data8;
    UInt32 data32;
    UInt8 bytes[32];
} SMCParamStruct;

enum { kSMCHandleYPCEvent = 2, kSMCReadKey = 5, kSMCGetKeyInfo = 9 };

static UInt32 s2u(const char *s){UInt32 v=0;for(int i=0;i<4;i++)v=(v<<8)|(unsigned char)s[i];return v;}

static int readKey(io_connect_t c, const char *key, UInt8 *out, UInt32 *sz, char *typ){
    SMCParamStruct p={0}; size_t z=sizeof(p);
    p.data8=kSMCGetKeyInfo; p.key=s2u(key);
    if (IOConnectCallStructMethod(c,kSMCHandleYPCEvent,&p,sizeof(p),&p,&z)) return -1;
    if (p.result) return -2;
    *sz = p.keyInfo.dataSize;
    UInt32 t = p.keyInfo.dataType;
    typ[0]=(t>>24)&0xff; typ[1]=(t>>16)&0xff; typ[2]=(t>>8)&0xff; typ[3]=t&0xff; typ[4]=0;
    
    memset(&p,0,sizeof(p)); z=sizeof(p);
    p.data8=kSMCReadKey; p.key=s2u(key); p.keyInfo.dataSize=*sz;
    if (IOConnectCallStructMethod(c,kSMCHandleYPCEvent,&p,sizeof(p),&p,&z)) return -3;
    if (p.result) return -4;
    memcpy(out, p.bytes, *sz);
    return 0;
}

// Decode common SMC numeric types
static double decode(UInt8 *b, UInt32 sz, const char *t){
    if (!strcmp(t,"flt ") && sz==4){ float f; memcpy(&f,b,4); return f; }
    if (!strcmp(t,"ui8 ") && sz==1) return b[0];
    if (!strcmp(t,"ui16") && sz==2) return (b[0]<<8)|b[1];
    if (!strcmp(t,"ui32") && sz==4) return ((UInt32)b[0]<<24)|((UInt32)b[1]<<16)|((UInt32)b[2]<<8)|b[3];
    if (!strcmp(t,"fpe2") && sz==2) return (((b[0]<<8)|b[1]) >> 2);
    if (!strcmp(t,"fp2e") && sz==2) return (((b[0]<<8)|b[1]) / 16384.0);
    if (!strcmp(t,"si8 ") && sz==1) return (signed char)b[0];
    if (!strcmp(t,"si16") && sz==2) return (SInt16)((b[0]<<8)|b[1]);
    return -1;
}

int main(void){
    io_service_t s = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("AppleSMC"));
    if (!s){ fprintf(stderr,"no AppleSMC\n"); return 1; }
    io_connect_t c;
    if (IOServiceOpen(s, mach_task_self(), 0, &c)){ fprintf(stderr,"open failed\n"); return 1; }
    IOObjectRelease(s);

    // every fan/cooling key documented across Mac generations + speculative M-series ones
    const char *keys[] = {
        "FNum","F0Ac","F1Ac","F0Mn","F0Mx","F0Tg","F0St",
        "F0RV","F1RV","FANC","FRC0","FRC1","FRCM","FRCm",
        "TF0a","TF0b","TF0c","TF0d","TF0e","TF0f","TF0g",
        "FZ0a","FZ0b","FZ0c","FZ0d","FZ0e","FZ0f",
        // M-series speculative
        "F0Pr","F0In","F0Sp","F0Cf",
        "rPM0","rPM1","fan0","Fan0","FAN0","FANR",
        "B0FC","B0FT","B0FN","TC0F","TG0F",
        // Apple SPU keyspace candidates
        "Fnum","fnum","fanA","FanA","FANS","FANT",
        NULL
    };

    printf("Probing SMC keys on this Mac:\n\n");
    for (int i=0; keys[i]; i++){
        UInt8 buf[32]={0}; UInt32 sz=0; char typ[5]={0};
        int r = readKey(c, keys[i], buf, &sz, typ);
        if (r==0){
            double v = decode(buf, sz, typ);
            printf("  %-4s  type=%-4s size=%u  raw=", keys[i], typ, sz);
            for (UInt32 j=0;j<sz;j++) printf("%02x ", buf[j]);
            if (v >= 0) printf(" -> %.3f", v);
            printf("\n");
        }
    }

    IOServiceClose(c);
    return 0;
}

