Hogwarts Wand Docs: ../server/ac_hexfile.c

Wand Sourcecode: ../server/ac_hexfile.c

//
// ac_hexfile.c - handle intel hex files
//
// Copyright (C) 2006  Nathan (Acorn) Pooley
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License (gpl.txt) for more details. 
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// You can contact the author, Nathan (Acorn) Pooley, by writing
// to Nathan (Acorn) Pooley, 949 Buckeye Drive, Sunnyvale, CA  94086, USA
// or through the form at http://www.rawbw.com/~acorn/wand/feedback.html
//

//#@DOC@ handle intel hex files

//###########################################################################
//############################### INCLUDES ##################################
//###########################################################################

#include "ac_hexfile.h"

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

//###########################################################################
//############################### TYPEDEFS ##################################
//###########################################################################


//===========================================================================
// AcHexRegion
//===========================================================================
typedef struct AcHexRegionRec {
    void    *data;
    AcHexAddress     addr;
    AcHexOffset  len;
    AcHexOffset  alloc;
    struct AcHexRegionRec   *next;
    struct AcHexRegionRec   *prev;
} AcHexRegion;

//===========================================================================
// AcHexFile
//===========================================================================
struct AcHexFileRec {
    char *filename;
    AcHexRegion head;       // this region always contains addr 0
                            // It may have a length of 0
};

//###########################################################################
//############################### PROTOTYPES ################################
//###########################################################################

static void AcHexRegionDelete(AcHexFile *f, AcHexRegion *r);

//###########################################################################
//############################### GLOBALS ###################################
//###########################################################################

static int debug = 0;

//###########################################################################
//############################### CODE ######################################
//###########################################################################

//===========================================================================
// AcHexGetFilename()
//===========================================================================
const char *AcHexGetFilename(AcHexFile *f)
{
    return f->filename ? f->filename : "";
}

//===========================================================================
// AcHexGetline()
//===========================================================================
static int AcHexGetline(FILE *fp, unsigned char *buf, int maxlen)
{
    unsigned char *s = buf;
    unsigned char *e = buf + maxlen;
    int c;

    while(1) {
        c = getc(fp);
        if (c == EOF || c == '\n' || c == '\r') {
            break;
        }
        if (s==e) {
            return -2;  // error
        }
        *(s++) = c;
    }
    if (s==buf && c==EOF) return -1;
    return s-buf;
}

//===========================================================================
// AcHexDelete() - delete hex structure
//===========================================================================
void AcHexDelete(AcHexFile *f)
{
    if (f->filename) {
        free(f->filename);
    }   

    while(f->head.next != &f->head) {
        AcHexRegionDelete(f, f->head.next);
    }
    AcHexRegionDelete(f, &f->head);

    free(f);
}

//===========================================================================
// AcHexCreate() - Create hex structure
//===========================================================================
AcHexFile *AcHexCreate(void)
{
    AcHexFile *f;

    f = calloc(1,sizeof(AcHexFile));
    if (f) {
        f->head.next = &f->head;
        f->head.prev = &f->head;
    }

    return f;
}

//===========================================================================
// AcHexToBin() - convert ascii buffer to binary
//===========================================================================
static int AcHexToBin(unsigned char *s,int cnt)
{
    unsigned int tmp = 0;
    int htmp = 0;
    unsigned char *e = s+cnt;
    unsigned char *d = s;
    unsigned char *buf = s;
    int sum = 0;

    for(;s != e; s++) {
        int c = *s;
        if (isspace(c)) continue;
        if (!isxdigit(c)) {
            return -1;
        }
        if (isdigit(c)) {
            c -= '0';
        } else {
            c = toupper(c) - 'A' + 10;
        }
        if (htmp) {
            unsigned int num = tmp | c;
            sum += num;
            *(d++) = num;
            htmp = 0;
        } else {
            tmp = c << 4;
            htmp = 1;
        }
    }
    if (htmp || (sum & 0xff)) return -1;

    return d-buf;
}

//===========================================================================
// AcHexLoadFile() - load entire file into a hexfile sturcture
//===========================================================================
// Curently format is ignored - use 0
AcHexFile *AcHexLoadFile(const char *filename, int format)
{
    unsigned char buf[100];
    AcHexFile *f;
    int err = 0;
    int done = 0;
    int type;
    AcHexAddress addr;
    AcHexAddress addrhi = 0;
    FILE *fp;

    if (format) return 0;

    f = AcHexCreate();
    if (!f) return 0;

    f->filename = strdup(filename);

    fp = fopen(filename,"r");

    if (!fp) {
        AcHexDelete(f);
        fprintf(stderr,"Hex file loading error (could not open file)\n");
        return 0;
    }

    while(!done) {
        int cnt = AcHexGetline(fp, buf, sizeof(buf)-1);
        unsigned char *s;

        if (cnt < 11) {
            if (cnt == -2) {
                fprintf(stderr,"Hex file loading error (a)\n");
                err = 1;
                break;
            }
            if (cnt > 0 && buf[0] == ':') {
                fprintf(stderr,"Hex file loading error (b)\n");
                err = 1;
                break;
            }
            if (cnt < 0) break;
            continue;
        }
        if (buf[0] != ':') continue;

        cnt = AcHexToBin(buf+1,cnt-1);
        if (cnt < 5) {
            fprintf(stderr,"Hex file loading error (c)\n");
            err=1;
            break;
        }
        cnt -= 5;
        s = buf+1;
        if (*s != cnt) {
            fprintf(stderr,"Hex file loading error (d)\n");
            err=1;
            break;
        }
        if (debug) {
            printf("cnt=%d ",cnt);
        }
        s++;
        addr = addrhi + (s[0] << 8) + s[1];
        if (debug) {
            printf("addr=%08lx  addrhi=%08lx ",addr,addrhi);
        }
        s += 2;
        type = *s;
        if (debug) {
            printf("TYPE=%02x\n",type);
        }
        s++;
        switch(type) {
            case 0x00:  // data record
                if (AcHexSetBytes(f,addr,s,cnt)) {
                    err = 1;
                    done = 1;
                    fprintf(stderr,"Hex file loading error (e)\n");
                }
                break;
            case 0x02:  // extended segment addr record
                if (cnt != 2 || ((addr-addrhi) & 0xffff)) {
                    fprintf(stderr,"Hex file loading error (f)\n");
                    err = 1;
                    done = 1;
                    break;
                }
                addrhi = (s[0] << 16) + (s[1] << 8);
                break;
            case 0x04:  // extended addr record
                if (cnt != 2 || ((addr-addrhi) & 0xffff)) {
                    fprintf(stderr,"Hex file loading error (g)\n");
                    err = 1;
                    done = 1;
                    break;
                }
                addrhi = (s[0] << 24) + (s[1] << 16);
                break;
            case 0x01:  // EOF record
                if (cnt != 0 || ((addr-addrhi) & 0xffff)) {
                    fprintf(stderr,"Hex file loading error (h)\n");
                    err = 1;
                    break;
                }
                done = 1;
                break;
            default:
                fprintf(stderr,"Hex file loading error (bad record type)\n");
                err=1;
                done = 1;
                break;
        }
    }

    fclose(fp);

    if (err || !done) {
        AcHexDelete(f);
        fprintf(stderr,"Hex file loading error (i)\n");
        return 0;
    }
    return f;
}

//===========================================================================
// AcHexRegionSizeCalc()
//===========================================================================
static AcHexOffset AcHexRegionSizeCalc(AcHexOffset req)
{
    AcHexOffset size = 2048;
    req += 1024;
    while(size < req) size *= 2;
    return size;
}

//===========================================================================
// AcHexRegionDelete()
//===========================================================================
static void AcHexRegionDelete(AcHexFile *f, AcHexRegion *r)
{
    if (r->data) {
        free(r->data);
    }

    //
    // Never free the head node
    //
    if (r == &f->head) {
        r->data = 0;
        r->alloc = 0;
        r->len = 0;
        return;
    }
    r->next->prev = r->prev;
    r->prev->next = r->next;
    free(r);
}

//===========================================================================
// AcHexRegionCreate()
//===========================================================================
static AcHexRegion *AcHexRegionCreate(
                            AcHexFile *f, 
                            AcHexAddress addr, 
                            AcHexOffset size)
{
    AcHexRegion *r = calloc(1,sizeof(AcHexRegion));
    if (!r) return 0;
    r->addr = addr;
    r->data = calloc(size,1);
    if (!r->data) {
        free(r);
        return 0;
    }
    r->len   = 0;
    r->alloc = size;
    r->data  = calloc(size/4, 4);

    return r;
}

//===========================================================================
// AcHexRegionSplit() - split a region into 2 regions
//===========================================================================
//
// 0 = success
// -1 = failure
//
static int AcHexRegionSplit(
                AcHexFile *f, 
                AcHexRegion *r,
                AcHexAddress addr)
{
    AcHexOffset len0, len1;
    AcHexRegion *r2;

    if (addr <= r->addr || addr >= r->addr + r->len) return -1;

    len0 = addr - r->addr;
    len1 = r->len - len0;

    r2 = AcHexRegionCreate(f,addr,len1);
    if (!r2) return -1;

    memcpy(r2->data, (char*)r->data + len0, len1);
    
    r->len = len0;

    r2->prev = r;
    r2->next = r->next;
    r2->next->prev = r2;
    r->next = r2;

    return 0;
}

//===========================================================================
// AcHexRegionPrint() - print region info
//===========================================================================
static void AcHexRegionPrint(AcHexFile *f, AcHexRegion *r)
{
    printf("  Region %08lx - %08lx  size=%08lx alloc=%08lx ptr=%08lx\n",
        (long)(r->addr),
        (long)(r->addr + r->len - 1),
        (long)(r->len),
        (long)(r->alloc),
        (long)(r->data));
}

//===========================================================================
// AcBytePrint() - print hex bytes from binary buffer
//===========================================================================
void AcBytePrint(void *buf,
                unsigned long len,
                unsigned long addr,
                int rpt)
{
    unsigned long o = 0;
    unsigned char *p = buf;
    for (; o<len; o++, addr++, p++) {
        if (!o || !(addr&0xf)) {
            if (o) {
                printf("\n");
            }
            while (rpt) {
                unsigned char *s = p+1;
                unsigned long o2 = o+1;
                unsigned long o3 = o+2;
                while(o2<len && *s == *p) {
                    o2++;
                    s++;
                }
                o2 = o2 - o - 1;
                s = p+2;
                while(o3<len-1 && s[0]==p[0] && s[1]==p[1]) {
                    o3 += 2;
                    s  += 2;
                }
                o3 = o3 - o - 1;
                if (o2 >= o3 && o2 >= 16) {
                    printf("    %08lx - %08lx = %02x\n",
                        (long)addr,
                        (long)(addr + o2),
                        *p);
                    addr += o2 + 1;
                    o    += o2 + 1;
                    p    += o2 + 1;
                } else if (o3 >= 16) {
                    printf("    %08lx - %08lx = %02x %02x\n",
                        (long)addr,
                        (long)(addr + o3),
                        p[0],p[1]);
                    addr += o3 + 1;
                    o    += o3 + 1;
                    p    += o3 + 1;
                } else {
                    if (o>=len) return;
                    break;
                }
            }
            printf("    %08lx: ",addr);
        }
        printf("%02x ",*p);
    }
    printf("\n");
}

//===========================================================================
// AcHexRegionPrintData() - print region data
//===========================================================================
static void AcHexRegionPrintData(AcHexFile *f, AcHexRegion *r)
{
#if 1
    AcBytePrint(r->data, r->len, r->addr, 1);
#else
    AcHexOffset o = 0;
    AcHexAddress a = r->addr;
    unsigned char *p = r->data;
    for (; o<r->len; o++, a++, p++) {
        if (!o || !(a&0xf)) {
            if (o) {
                printf("\n");
            }
            printf("    %08lx: ",a);
        }
        printf("%02x ",*p);
    }
    printf("\n");
#endif
}

//===========================================================================
// AcHexPrint() - print region data
//===========================================================================
void AcHexPrint(AcHexFile *f, int showdata)
{
    AcHexRegion *r = &f->head;
    printf("Hex file %s\n", f->filename?f->filename:"");
    do {
        AcHexRegionPrint(f,r);
        if (showdata) {
            AcHexRegionPrintData(f,r);
        }
        r = r->next;
    } while (r != &f->head);
}

//===========================================================================
// AcHexRegionFind() - find region containing or preceding addr
//===========================================================================
static AcHexRegion *AcHexRegionFind(AcHexFile *f, AcHexAddress addr)
{
    AcHexRegion *r = &f->head;

    do {
        if (addr < r->addr) {
            return r->prev;
        }
        r = r->next;
    } while(r!=&f->head);

    return r->prev;
}

//===========================================================================
// AcHexGetRegion() - get region ingfo
//===========================================================================
//
// Given addr as input, return start, end, and len of region containing addr.
// If no region contains addr then return info for following region. 
// If addr is after last region then return 0 and do not change start, end.
//
// f     - hex file
// addr  - address to look for
// start - returns start addr of reqion (may be NULL)
// end   - returns end addr of reqion (may be NULL)
//
// Returns: len of region, or 0 if addr is greter than end of last region
//
AcHexOffset AcHexGetRegion(
                AcHexFile *f, 
                AcHexAddress addr, 
                AcHexAddress *start, 
                AcHexAddress *end)
{
    AcHexRegion *r = AcHexRegionFind(f,addr);
    if (addr >= r->addr + r->len) {
        r = r->next;
        if (r == &f->head) return 0;
    }

    if (start) *start = r->addr;
    if (end)   *end   = r->addr + r->len - 1;
    return r->len;
}

//===========================================================================
// AcHexGetBytes() - get bytes from AcHexFile (unkown bytes not touched)
//===========================================================================
//
// returns # of known bytes retrieved
//
AcHexOffset AcHexGetBytes(
                AcHexFile *f, 
                AcHexAddress addr, 
                void *data, 
                AcHexOffset bytes)
{
    char *dst = data;
    AcHexRegion *r = AcHexRegionFind(f,addr);
    AcHexOffset sum = 0;

    while(bytes) {
        char *src = r->data;
        AcHexOffset len = r->len;
        if (addr < r->addr) {
            AcHexOffset dif = r->addr - addr;
            if (dif >= bytes) break;
            dst   += dif;
            addr  += dif;
            bytes -= dif;
        }
        if (addr > r->addr) {
            AcHexOffset dif = addr - r->addr;
            dif = dif < len ? dif : len;
            len -= dif;
            src += dif;
        }
        if (len) {
            len = len < bytes ? len : bytes;
            memcpy(dst, src, len);
            sum   += len;
            addr  += len;
            dst   += len;
            bytes -= len;
        }
        r = r->next;
        if (r == &f->head) break;
    }
    return sum;
}

//===========================================================================
// AcHexSetBytes() - write bytes into AcHexFile
//===========================================================================
//
// 0 = success
// -1 = failure
//
int AcHexSetBytes(
                AcHexFile *f, 
                AcHexAddress addr, 
                const void *data, 
                AcHexOffset bytes)
{
    AcHexRegion *s = AcHexRegionFind(f,addr);
    AcHexRegion *e = s;
    AcHexAddress ea1 = addr + bytes;
    AcHexAddress ea = ea1;

    //
    // find last region overlapping new bytes (e)
    //
    do {
        e = e->next;
    } while (e != &f->head && e->addr <= ea);
    e = e->prev;
    
    if (ea < e->addr + e->len) {
        ea = e->addr + e->len;
    }

    //
    // do we overlap or connect with preceding region?
    //
    if (addr <= s->addr + s->len) {

        //
        // are we talking about addr==0 and len==0?
        //
        if (s->len == 0) {
            AcHexOffset alloc = AcHexRegionSizeCalc(bytes);
            s->data = malloc(alloc);
            if (!s->data) return -1;
            s->alloc = alloc;

        //
        // do we not have enough room allocated already?
        //
        } else if (ea > s->addr + s->alloc) {
            AcHexOffset alloc = AcHexRegionSizeCalc(ea - s->addr);
            void *ptr = realloc(s->data, alloc);
            if (!ptr) return -1;
            s->data = ptr;
            s->alloc = alloc;
        }

    //
    // no overlap - create new region
    //
    } else {
        AcHexOffset alloc = bytes + 8192;
        AcHexRegion *r;

        r = AcHexRegionCreate(f,addr,alloc);
        if (!r) return -1;

        r->prev = s;
        r->next = s->next;
        s->next->prev = r;
        s->next = r;

        e = e==s ? r : e;
        s = r;
    }

    memcpy((char*)s->data + addr - s->addr, data, bytes);
    s->len = ea - s->addr;

    //
    // success, and no additional overlapped regions
    //
    if (e == s) {
        if (debug) {
            AcHexPrint(f,1);
        }
        return 0;
    }

    //
    // handle overlapped regions
    //
    s = s->next;

    while(s != e) {
        AcHexRegion *r = s->next;
        AcHexRegionDelete(f, s);
        s = r;
    }

    //
    // need to copy end of overlapped region?
    //
    if (ea1 < ea) {
        memcpy( s->data + ea1 - s->addr, 
                e->data + ea1 - e->addr,
                ea - ea1);
    }
    AcHexRegionDelete(f,e);
    if (debug) {
        AcHexPrint(f,1);
    }
    return 0;
}

//===========================================================================
// AcHexDeleteBytes() - delete bytes from AcHexFile
//===========================================================================
//
// 0 = success
// -1 = failure
//
int AcHexDeleteBytes(
                AcHexFile *f, 
                AcHexAddress addr, 
                AcHexOffset bytes)
{
    if (bytes == 0) return 0;

    while(1) {
        AcHexRegion *r = AcHexRegionFind(f,addr);

        //
        // if r does not contain addr then we are done
        //
        if (r->addr + r->len <= addr) {
            return 0;   // done
        }

        //
        // split at the end?
        //
        if (addr + bytes < r->addr + r->len) {
            if (AcHexRegionSplit(f,r,addr + bytes)) {
                return -1;
            }
        }

        //
        // split at the start?
        //
        if (addr > r->addr) {
            if (AcHexRegionSplit(f,r,addr)) {
                return -1;
            }
            r = r->next;
        }

        AcHexRegionDelete(f,r);
    }
}


This file Copyright (C) 2006 by Nathan (Acorn) Pooley
Go to TOP Wand page
Go to Acorn's personal webpage
Go to Hogwarts website: www.gotohogwarts.com
Snout: www.snout.org/game
Gadgets of Justice Unlimited
Snout GC (Wiki)
Snout Wiki
File created by do_doc at Wed May 30 03:24:55 PDT 2007