/*
 * Dumps InfoIC.dll version 100 (6.60)
 *
 * Compiles with MinGW-w64, GCC 6.3
 *
 * i686-w64-mingw32-gcc -o dump-infoic-dll.exe -m32 -Wall -W dump-infoic-dll.c
 * wine dump-infoic-dll.exe > windb.json
 *
 * Reference:
 * http://www.nullsecurity.org/article/minipro_reverse_engineering_the_infoic_dll
 */

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>

#define FATAL(fmt, args...) do { fprintf(stderr, "ERROR: " fmt "\n", ##args); exit(1); } while(0)

typedef int(*GetDllInfoProc)(int*, int*);

typedef struct
{
    DWORD   protocol_id;         // 0x00
    DWORD   u04_dw;              // 0x04 - ??? always 0
    DWORD   type;                // 0x08 - Chip type: 0=""; 1="EEPROM"; 2="MCU/MPU"; 3="PLD/CPLD"; 4="SRAM"; 5="LOGIC"
    char    name[40];            // 0x0c
    DWORD   variant;             // 0x34
    DWORD   code_memory_size;    // 0x38
    DWORD   data_memory_size;    // 0x3c
    DWORD   data_memory2_size;   // 0x40
    WORD    u44_w;               // 0x44 - ???
    WORD    read_buffer_size;    // 0x46
    WORD    write_buffer_size;   // 0x48
    WORD    opts1;               // 0x4a
    DWORD   opts2;               // 0x4c
    DWORD   opts3;               // 0x50
    BYTE    chip_id[8];          // 0x54
    DWORD   chip_id_bytes_count; // 0x5c
    DWORD   u60_dw;              // 0x60 - ???
    DWORD   package_details;     // 0x64
    DWORD   opts4;               // 0x68
} Ic_100;

typedef struct
{
    DWORD id;
    DWORD logo_id;
    char short_name[20];
    char long_name[40];
    Ic_100* ics;
    DWORD num_ics;
} Mfc_100;

typedef void(*GetMfcStruProc_100)(DWORD, Mfc_100*);

static void hexcat(char* dest, const BYTE b)
{
    char buffer[3];
    snprintf(buffer, sizeof(buffer), "%02x", b);
    strcat(dest, buffer);
}

static void json_dump_ic_fields(
    FILE* fp,
    const Ic_100* ic,
    const char* indent)
{
    char name[100] = {0};
    strncat(name, ic->name, sizeof(ic->name));

    char chip_id[100] = {0};
    for(int i = 0; i < (int) ic->chip_id_bytes_count; ++i) {
        hexcat(chip_id, ic->chip_id[i]);
    }

    #define PRINT(fmt, args...) fprintf(fp, "%s" fmt "\n", indent, ##args)
    PRINT("\"protocol_id\": %lu,", ic->protocol_id);
    PRINT("\"type\": %lu,", ic->type);
    PRINT("\"name\": \"%s\",", name);
    PRINT("\"variant\": %lu,", ic->variant);
    PRINT("\"code_memory_size\": %lu,", ic->code_memory_size);
    PRINT("\"data_memory_size\": %lu,", ic->data_memory_size);
    PRINT("\"data_memory2_size\": %lu,", ic->data_memory2_size);
    PRINT("\"read_buffer_size\": %u,", ic->read_buffer_size);
    PRINT("\"write_buffer_size\": %u,", ic->write_buffer_size);
    PRINT("\"opts1\": %u,", ic->opts1);
    PRINT("\"opts2\": %lu,", ic->opts2);
    PRINT("\"opts3\": %lu,", ic->opts3);
    PRINT("\"chip_id\": \"%s\",", chip_id);
    PRINT("\"chip_id_bytes_count\": %lu,", ic->chip_id_bytes_count);
    PRINT("\"package_details\": %lu,", ic->package_details);
    PRINT("\"opts4\": %lu,", ic->opts4);

    PRINT("\"u04_dw\": %lu,", ic->u04_dw);
    PRINT("\"u44_w\": %u,", ic->u44_w);
    PRINT("\"u60_dw\": %lu", ic->u60_dw);
    #undef PRINT
}

static void json_dump_mfc_fields(FILE* fp, const Mfc_100* mfc, const char* indent)
{
    char ic_indent[100] = {0};
    strcat(ic_indent, indent);
    strcat(ic_indent, "    ");

    char short_name[100] = {0};
    char long_name[100] = {0};
    strncat(short_name, mfc->short_name, sizeof(mfc->short_name));
    strncat(long_name, mfc->long_name, sizeof(mfc->long_name));

    #define PRINT(fmt, args...) fprintf (fp, "%s" fmt "\n", indent, ##args)
    PRINT("\"id\": %lu,", mfc->id);
    PRINT("\"short_name\": \"%s\",", short_name);
    PRINT("\"long_name\": \"%s\",", long_name);
    PRINT("\"ics\": [");
    for(int i = 0; i < (int) mfc->num_ics; ++i) {
	int last = i + 1 >= (int) mfc->num_ics;
	PRINT("  {");
	json_dump_ic_fields(fp, &mfc->ics[i], ic_indent);
	PRINT("  }%s", last ? "" : ",");
    }
    PRINT("]");
    #undef PRINT
}

int main()
{
    HMODULE handle;
    GetDllInfoProc GetDllInfo;
    int num_ics, dll_version, num_mfcs;
    GetMfcStruProc_100 GetMfcStru;

    assert(sizeof(Ic_100) == 0x6c);
    assert(sizeof(Mfc_100) == 0x4c);

    handle = LoadLibrary("InfoIC.dll");
    if(!handle) {
        FATAL("could not open InfoIC.dll");
    }

    GetDllInfo = (GetDllInfoProc) GetProcAddress(handle, "GetDllInfo");
    if(!GetDllInfo) {
        FATAL("function not found: GetDllInfo");
    }

    num_ics = GetDllInfo(&dll_version, &num_mfcs);
    fprintf(
        stderr,
        "Version %d\n"
        "%d manufacturers\n"
        "%d ics\n",
        dll_version,
        num_mfcs,
        num_ics);

    if(dll_version != 100) {
        FATAL("unsupported dll version %d", dll_version);
    }

    GetMfcStru = (GetMfcStruProc_100) GetProcAddress(handle, "GetMfcStru");
    if(!GetMfcStru) {
        FATAL("function not found: GetMfcStru");
    }

    fprintf(stdout, "[\n");
    for(int i = 0; i < num_mfcs; ++i) {
        int last = (i + 1) >= num_mfcs;
        Mfc_100 mfc;
        GetMfcStru(i, &mfc);
        fprintf(stdout, "  {\n");
        json_dump_mfc_fields(stdout, &mfc, "    ");
        fprintf(stdout, "  }%s\n", last ? "" : ",");
    }
    fprintf(stdout, "]\n");

    FreeLibrary(handle);
    return 0;
}
