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#.
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; } }; }
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); } }; }
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; }