Creating a new Rom Definition

You can now create a new rom definition which lets BRE open any ecu file you would like. Creating a new definition consists of a few things:

For a class to properly extend RomRep.HondaEcu, you must override at least these functions (its C++):

 protected:
                //Base class uses this to create a fuel map
                virtual FuelMap* createFuelMap(int xx, int yy, int index, String* mapname,
                        RomRep::MapLocs* maploc,unsigned char map __gc[,], unsigned char mlt __gc[], bool vtec);

                //Base class uses this to create an ignition map
                virtual Map* createIgnitionMap(int xx, int yy, int index, String* mapname,
                        RomRep::MapLocs* maploc,unsigned char map __gc[,], bool vtec);

                //function used to calculate AFR from a raw value
                virtual float calculateAfr(unsigned char raw) = 0;
                //function used to calculate a raw value from an AFR value
                virtual unsigned char calculateAfr(float afr) = 0;

 public:
                //used to set a two byte rpm into the rom
                virtual void setTwoByteRpm(int loc, int rpm);
                //used to retrieve a 2 byte rpm from rom
                virtual int getTwoByteRpm(int loc); //returns rpm

                //used to make sure the rom being opened is of your type 
                static bool validate(unsigned char h __gc[]);

The best way to learn is to see it in action. Recently I made a definition to have BRE open the NG series roms. This example is very basic and will only make it open and edit the fuel and ignition tables. It is in C++ so you will need to convert it to C#.

Examples

Note: if there are some mismatched variable names or other errors it is because I took the cpp code and dumped it into the h file for these examples.

Definition Class

The example starts with the definition class:

namespace RomRep
{
        [Serializable] public __gc class Pm6Ng: public HondaEcu
        {
        protected:
                int revconst;

                virtual int getHighByte(int bytes)
                {return bytes/256;}
                virtual int getLowByte(int bytes)
                {return bytes%256;}

                virtual FuelMap* createFuelMap(int xx, int yy, int index, String* mapname,
                        RomRep::MapLocs* maploc,unsigned char map __gc[,], 
                        unsigned char mlt __gc[], bool vtec)
                {
                        bool boost = false;
                        if(index >= 1) boost = true;

                        //create our NG map. More on that later...
                        RomRep::FuelMap* fm = new NGFuelMap(xx,yy,maploc->mapLoc,map,maploc->mapLoc+xx*yy,
                                mlt,thefile,maploc->mapColScalarLoc,maploc->mapRevScalarLoc,boost);
                        
                        if(mapname != NULL) fm->setName(mapname);
                        else fm->setName(String::Concat("Fuel: ",index.ToString()));
                        return im;
                }

                virtual Map* createIgnitionMap(int xx, int yy, int index, String* mapname,
                        RomRep::MapLocs* maploc,unsigned char map __gc[,], bool vtec)
                {
                        bool boost = false;
                        if(index >= 1) boost = true;

                        //create NG ign map.
                        Map* im = new NGIgnMap(xx,yy,maploc->mapLoc,map,thefile,
                                maploc->mapColScalarLoc,maploc->mapRevScalarLoc,boost);
                        
                        if(mapname != NULL) im->setName(mapname);
                        else im->setName(String::Concat("Ignition: ",index.ToString()));
                        return im;
                }

                virtual float calculateAfr(unsigned char raw)
                {
                        return ((float)raw/25.0F)+10.0F;
                }
                virtual unsigned char calculateAfr(float afr)
                {
                        return (unsigned char)((afr-10.0F)*25.0F);
                }

        public:
                Pm6Ng(unsigned char rom __gc[], int flen): HondaEcu(rom, flen, 0,false,2,"NG60")
                {
                        x = 15; //x and y are from the base class
                        y = 17;

                        target_afr_loc[0] = 0x6100; //the base class sets up this target_afr_loc 
                        target_afr_loc[1] = 0x61ff; //array so you dont have to do much with the target afr

                        revconst = 1920000; //my rev constant
                        checksumaddy = 0x3c00; //address of checksum
                        checksumlen = 0x8000; //length of checksum

                        //here we go setup the maps...
                        MapLocs* mapinfo[] = new MapLocs*[4];
                        mapinfo[0] = new MapLocs(0x5200,0x4600,0); //fuel na
                        mapinfo[1] = new MapLocs(0x530e,0x4600,0); //fuel boost
                        mapinfo[2] = new MapLocs(0x5001,0x4600,0); //ign na
                        mapinfo[3] = new MapLocs(0x5101,0x4600,0); //ign boost

                        try
                        {
                                HondaEcu::setupMaps(mapinfo); //Each definition MUST call this
                        }catch(...){}
                }

                //ended up being a carry over from the obd0 vtec stuff. oops. 
                virtual void setTwoByteRpm(int loc, int rpm)
                {
                        int val = revconst/rpm;
                        setByte(loc, val%256);
                        setByte(loc+1,val/256);
                }
                virtual int getTwoByteRpm(int loc){ //returns rpm
                        return revconst/((int)thefile[loc] + (int)thefile[loc+1]*256);
                }

                //calculate the NG's pressure headers for stock map sensor
                //NOTE: this may be incorrect. It was a quick and dirty deal.
                static void calcPressureHeaders(int loc,String* headers[], 
                        unsigned char file __gc[], bool boost)
                {
                        //0x7fea = map sensor
                        int ptr = 0;
                        if(boost)
                                ptr = (int)file[0x7fea];

                        int col = 0;            
                        for(int i = ptr; i<256; i++)
                                if(file[loc+i]/16 == col)
                                {
                                        if(!boost)
                                                headers[col] = Engine::voltsToInHg(Engine::rawToVolts(i),
                                                        DevUtil::MAPSTOCK).ToString();
                                        else
                                                headers[col] = Engine::voltsToPsi(Engine::rawToVolts(i),
                                                        DevUtil::MAPSTOCK).ToString("0.00");
                                        col++;
                                }
                }
                //bleh
                static void calcRpmHeaders(int loc,String* headers[], 
                        unsigned char file __gc[], bool boost)
                {
                        rowheaders[0] = "500";
                        rowheaders[1] = "600";
                        rowheaders[2] = "700";
                        rowheaders[3] = "800";
                        rowheaders[4] = "900";
                        rowheaders[5] = "1100";
                        rowheaders[6] = "1300";
                        rowheaders[7] = "1600";
                        rowheaders[8] = "1900";
                        rowheaders[9] = "2200";
                        rowheaders[10] = "2600";
                        rowheaders[11] = "3100";
                        rowheaders[12] = "3700";
                        rowheaders[13] = "4400";
                        rowheaders[14] = "5300";
                        rowheaders[15] = "6300";
                        rowheaders[16] = "7400";        
                }

                static bool validate(unsigned char h __gc[])
                {
                        unsigned char head[10] __gc[] = {0x02,0x16,0xBA,0xC0,0xE0,0xC0,0x83,0xC0,0x82,0x61};

                        for(int i = 0; i<head->Length; i++)
                                if(head[i] != h[i])
                                        return false;

                        return true;
                }

                //Gets a column header. I had to override this because the base class thinks that 
                //all maps in the rom have the same column headers. Tis not so with the NG roms.
                virtual String * getColHeader(int map, int column){
                        //the fuel and ign maps of the same index have the same headers...
                        return this->fuelmaps[map]->getColumnHeader(column);
                }
                //Again, I overrode this because the base class' one for all issue.
                virtual String* getColHeaders()[,]
                {
                        String* heads[,] = new String*[nummaps,this->x];
                        for(int i = 0; i< nummaps; i++)
                                for(int j = 0; j<this->x; j++)
                                        heads[i,j] = this->fuelmaps[i]->getColumnHeader(j);

                        return heads;
                }
        };
}

Map Classes

There was one issue that necessitated new map classes: the HondaEcu base class assumes all maps share a common column header array. The NG roms are an exception in that the boost maps have different column headers than the NA maps. All that I had to do was setup the column headers in each map (code is in the Definition Class). To do this I had to create a fuel and an ignition class.

Fuel

namespace RomRep
{
        [Serializable] public __gc class NGFuelMap : public RomRep::Obd0FuelMap
        {
        private:
                bool boost;
        public:
                NGFuelMap(int x1, int y1,unsigned int loc, unsigned char themap __gc[,], 
                        unsigned int multloc, unsigned char mltarray __gc[],unsigned char fil __gc[],
                        int colScalarLoc, int rowScalaorLoc, bool boost):Obd0FuelMap(x1,y1,loc,
                        themap,multloc1,mltarray,fil)
                {
                        this->vtec = false;
                        this->boost = boost;
                        revscalarloc = rscalarloc;
                        pressscalarloc = cscalarloc;

                        //these are in the definition. Pass in the already 
                        //created arrays for the headers: vac and rpm.
                        RomRep::Pm6Ng::calcPressureHeaders(cscalarloc,vac,fil,boost);
                        RomRep::Pm6Ng::calcRpmHeaders(rscalarloc,rpm,fil,boost);
                }
        };
}

Ignition is largely the same

 namespace RomRep
{
        [Serializable] public __gc class NGIgnMap : public RomRep::Obd0IgnMap
        {
        private:
                bool boost;

        public:
                NGIgnMap(int x1, int y1, unsigned int loc, unsigned char themap __gc[,], 
                        unsigned char fil __gc[], int cscalarloc, int rscalarloc, 
                        bool boost):Obd0IgnMap(x1,y1,loc,themap,fil)
                {
                        this->vtec = false;
                        this->boost = boost;
                        revscalarloc = rscalarloc;
                        pressscalarloc = cscalarloc;

                        RomRep::Pm6Ng::calcPressureHeaders(cscalarloc,vac,fil,boost);
                        RomRep::Pm6Ng::calcRpmHeaders(rscalarloc,rpm,fil,boost);
                }
        };
}

Insertion Script

Moving onto the actual script portion, all you have to do is make a script (read the main page) with the OnOpenRom(ref OnOpenRomArgs ra) function in it.

                public static object OnOpenRom(ref OnOpenRomArgs ra)
                {
                        //Did BRE already find a suitable ROM type?
                        //if so, return 
                        if(ra.getRom() != null)
                                return null;

                        //otherwise see if we are trying to open an NG rom
                        byte[] file = Engine.getFile(ra.getFilename(),10)
                        if(Pm6Ng.validate(file))
                        {
                                file = Engine.getFile(ra.getFilename(),0x8000)
                                ra.setOpenable(true);
                                ra.setRom(new Pm6Ng(file,0x8000));
                        }
                        return null;
                }



BRE Documentation Home