Cet outil décode les entêtes des fichiers Nanoréseau et permet d'en extraire les sections binaires.

Exemple d'utilisation :

$ nrfile -e NRDOS.MO5

NRFile, version 1.0

File: NRDOS.MO5 (7552 bytes)

Header:
   Station identifier: "        " (default)
   File type: binary
   File mode: binary
   File status: read/write
   Nanoreseau version: 3.1
   Creation date: 27/05/85
   Last modification date: 27/05/85
   Created on a: TO7
   Created with: unreferenced application
   File attributes: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Binary contents:
   Format: extended
   Target computer: MO5
   Target application: unreferenced application
   Loading flags: broadcast, load application first
   Application: "NRDOS"

   Code section 1:
      Size: 7373 bytes
      Load address: 00-5000 (do not switch RAM page)

      Section has been dumped to NRDOS.MO5-00-5000.bin.

   Execution address: 00-5000 (do not switch RAM page)

Code source :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Undefine this for Unix-like systems
#define PATH_WINDOWS

int gExtract = 0;

FILE *gSource = NULL;
unsigned char *gBuffer = NULL;
char *gFilename = NULL;
long gSize = 0;

char gIdentifier[9] = {0};
unsigned char gFileType = 0;

void TargetApp(unsigned char xByte)
{
    switch (xByte)
    {
    case 0:
        printf("unreferenced application");
        break;
    case 1:
        printf("BASIC 1.0");
        break;
    case 2:
        printf("LOGO");
        break;
    case 3:
        printf("LSE");
        break;
    default:
        printf("unknown application");
    }

}

void TargetComputer(unsigned char xByte)
{
     switch (xByte)
    {
    case 0:
        printf("TO7");
        break;
    case 1:
        printf("MO5");
        break;
    case 2:
        printf("TO7/70");
        break;
    default:
        printf("unknown computer");
    }
}

void PageSwitch(unsigned char xPage)
{
  switch (xPage)
        {
        case 0:
            printf("do not switch RAM page");
            break;

        case 0x40:
        case 0x41:
        case 0x42:
        case 0x43:
        case 0x44:
        case 0x45:
            printf("switch to RAM page %d",xPage-0x40);
            break;

        case 0x80:
        case 0x81:
        case 0x82:
        case 0x83:
            printf("switch to 64K RAM expansion page %d",xPage-0x80);
            break;

        case 0xff:
            printf("switch to internal ROM [BASIC]");
            break;

        default:
            printf("unknown RAM page");
        }
        printf(")\n");
}

void AnalyseBinExt(unsigned char *xpBuffer)
{
    long lOffset = 38;
    unsigned int lSection = 1, lSize = 0, lAddress = 0;
    char lBuffer[33] = {0};
    char *lFilename = NULL;
    unsigned char lPage = 0;
    FILE *lDump = NULL;

    printf("   Target computer: ");
    TargetComputer(xpBuffer[1]);
    printf("\n");

    printf("   Target application: ");
    TargetApp(xpBuffer[2]);
    printf("\n");

    printf("   Loading flags: ");
    if (xpBuffer[3]&0x80)
    {
        printf("broadcast");
    }
    if (xpBuffer[3]&0x40)
    {
        if (xpBuffer[3]&0x80)
        {
            printf(", ");
        }
        printf("load application first");
    }
    printf("\n");

    strncpy(lBuffer,(char*)xpBuffer+6,32);
    printf("   Application: \"%s\"\n\n",lBuffer);

    while (xpBuffer[lOffset]!=3)
    {
        printf("   Code section %d:\n",lSection);
        lSize = xpBuffer[lOffset+1]*256+xpBuffer[lOffset+2];
        lPage = xpBuffer[lOffset+5];
        printf("      Size: %d bytes\n",lSize);
        lAddress = xpBuffer[lOffset+3]*256+xpBuffer[lOffset+4];
        printf("      Load address: %02X-%04X (",lPage,lAddress);

        PageSwitch(lPage);
        printf("\n");

        if (gExtract)
        {
            lFilename = (char*)malloc(strlen(gFilename+11));
            sprintf(lFilename,"%s-%02x-%04x.bin",gFilename,lPage,lAddress);
            lDump = fopen(lFilename,"wb");
            fwrite(xpBuffer+lOffset+6,lSize,1,lDump);
            fclose(lDump);
            printf("      Section has been dumped to %s.\n\n",lFilename);
            free(lFilename);
        }

        lOffset += 6+lSize;

        lSection++;
    }

    lPage = xpBuffer[lOffset+5];
    printf("   Execution address: %02X-%04X (",lPage,xpBuffer[lOffset+3]*256+xpBuffer[lOffset+4]);
    PageSwitch(lPage);
}

void AnalyseBinSimple(unsigned char *xpBuffer)
{
    long lOffset = 0;
    unsigned int lSection = 1, lSize = 0, lAddress = 0;
    char *lFilename = NULL;
    FILE *lDump = NULL;

    while (xpBuffer[lOffset]==0)
    {
        printf("   Code section %d:\n",lSection);
        lSize = xpBuffer[lOffset+1]*256+xpBuffer[lOffset+2];
        printf("      Size: %d bytes\n",lSize);
        lAddress = xpBuffer[lOffset+3]*256+xpBuffer[lOffset+4];
        printf("      Load address: %04X\n",lAddress);
        printf("\n");

        if (gExtract)
        {
            lFilename = (char*)malloc(strlen(gFilename+11));
            sprintf(lFilename,"%s-%04x.bin",gFilename,lAddress);
            lDump = fopen(lFilename,"wb");
            fwrite(xpBuffer+lOffset+6,lSize,1,lDump);
            fclose(lDump);
            printf("      Section has been dumped to %s.\n\n",lFilename);
            free(lFilename);
        }

        lOffset += 5+lSize;
        lSection++;
    }

    printf("   Execution address: %04X\n",xpBuffer[lOffset+3]*256+xpBuffer[lOffset+4]);
}

void AnalyseBin(unsigned char *xpBuffer)
{
    printf("Binary contents:\n   Format: ");
    if (xpBuffer[0] == 1)
    {
       printf("extended\n");
       AnalyseBinExt(xpBuffer);
    } else
    {
       printf("simple\n\n");
       AnalyseBinSimple(xpBuffer);
    }
}

void ExtractFilename(char *xSrcStr, char **xStrDst)
{
    char *lPtr = strrchr(xSrcStr,
#ifdef PATH_WINDOWS
    '\\'
#else
    '/'
#endif // PATH_WINDOWS
    );

    if (lPtr==NULL)
    {
        *xStrDst = (char*) malloc(strlen(xSrcStr));
        strcpy(*xStrDst,xSrcStr);
    }
    else
    {
        *xStrDst = (char*) malloc(strlen(lPtr));
        strcpy(*xStrDst,lPtr+1);
    }
}

void usage()
{
    printf("Usage: nrfile [-e] filename\n");
}

int main(int argc, char *argv[])
{
    long i;

    printf("NRFile, version 1.0\n");
    if ((argc!=2)&&(argc!=3))
    {
        usage();
        return -1;
    }

    if (argv[1][0]=='-')
    {
        if (argc!=3)
        {
           usage();
           return -1;
        }
        if (argv[1][1] == 'e')
        {
            gExtract = 1;
        }
        else
        {
           usage();
           return -1;
        }
    } else if (argc!=2)
    {
           usage();
           return -1;
    }



    if (gExtract)
    {
        ExtractFilename(argv[2],&gFilename);
        gSource = fopen(argv[2],"rb");
    } else
    {
        ExtractFilename(argv[1],&gFilename);
        gSource = fopen(argv[1],"rb");
    }

    if (gSource == NULL)
    {
        printf("Error: cannot open file.\n");
        free(gFilename);
        return -2;
    }

    fseek(gSource,0,SEEK_END);
    gSize = ftell(gSource);
    fseek(gSource,0,SEEK_SET);

    gBuffer = malloc(gSize);
    if (gBuffer == NULL)
    {
        printf("Error: cannot allocate memory.\n");
        free(gFilename);
        fclose(gSource);
        return -3;
    }
    memset(gBuffer,0,gSize);

    fread(gBuffer,1,gSize,gSource);
    fclose(gSource);

    if (strnicmp((char*)gBuffer,"*NRUSTL*",8))
    {
        printf("Error: this file is not Nanoreseau compatible.\n");
        free(gBuffer);
        free(gFilename);
        return -4;
    }

    printf("\nFile: %s (%lu bytes)\n\n",gFilename,gSize);
    printf("Header:\n");

    strncpy(gIdentifier,(char *)gBuffer+8,8);
    printf("   Station identifier: \"%s\"",gIdentifier);
    if (strncmp(gIdentifier,"        ",8) == 0)
    {
        printf(" (default)");
    }
    printf("\n");

    gFileType = gBuffer[17];
    printf("   File type: ");
    switch (gFileType)
    {
    case 0:
        printf("BASIC program");
        break;
    case 1:
        printf("BASIC data");
        break;
    case 2:
        printf("binary");
        break;
    case 3:
        printf("source code");
        break;
    case 5:
        printf("indexed data");
        break;
    default:
        printf("unknown");
    }
    printf("\n");

    printf("   File mode: ");
    switch (gBuffer[18])
    {
    case 0:
        printf("binary");
        break;
    case 0xff:
        printf("ASCII");
        break;
    default:
        printf("unknown");
    }
    printf("\n");

    printf("   File status: ");
    switch (gBuffer[23])
    {
    case 0:
        printf("read/write");
        break;
    case 0xff:
        printf("write protected");
        break;
    default:
        printf("unknown");
    }
    printf("\n");

    printf("   Nanoreseau version: %d.%d\n",gBuffer[24],gBuffer[25]);

    printf("   Creation date: %02d/%02d/%02d\n",gBuffer[28],gBuffer[27],gBuffer[26]);
    printf("   Last modification date: ");
    if ((gBuffer[29] == 0)&&(gBuffer[30] == 0)&&(gBuffer[31] == 0))
    {
        printf("never");
    }
    else
    {
        printf("%02d/%02d/%02d",gBuffer[31],gBuffer[30],gBuffer[29]);
    }
    printf("\n");

    printf("   Created on a: ");
    TargetComputer(gBuffer[32]);
    printf("\n");

    printf("   Created with: ");
    TargetApp(gBuffer[33]);
    printf("\n");

    printf("   File attributes: ");
    for (i=80;i<96;i++)
    {
        printf("%02X ",gBuffer[i]);
    }
    for (i=96;i<128;i++)
    {
        if (i%16==0)
        {
            printf("\n                    ");
        }
        printf("%02X ",gBuffer[i]);

    }
    printf("\n\n");

    if (gFileType == 2)
    {
        AnalyseBin(gBuffer+128);
    }

    free(gBuffer);
    free(gFilename);
    return 0;
}