BRE API

2.2

Introduction

BRE now supports scripting/plugins. The scripts are written in regular C# with a couple of small differences to a 'real' C# app which are shown/discussed later.

The scripting code is based on a freely available command line scripting utility written by Oleg Shilo available on the code project called CSScript. I really only used the parser and assembly resolver, however, so that we could dynamically load script dependencies. The rest of the code is mine and if you want to use it you must figure out its interface (easy if you know what you are doing). The site to get it from is here: http://www.codeproject.com/csharp/cs-script_for_CP.asp and its a good tool for everyday use if you like C#.

Why C#? Why not VB.NET?

My preference. I dislike VB code greatly. It is ugly and has no curly braces, uses english too much and in many cases produces slower code. If you don't like it, I don't care all too much. I suppose if I was repeatedly bugged I would add VB support. But not until then. Would it be hard? No. Would I have to read VB.NET scripts? Yes. Yuck.

How does it work?

.NET comes with mechanisms to compile and load .NET code dynamically. So each of these scripts are moderately parsed for dynamic resolving by BRE, compiled by the framework down to an assembly (.csc/.dll file) and then loaded into BRE's script manager. Each script is instantiated only once for each child window (the ROM window with the maps and stuff) that the script applies to. This means that each ROM file you open has an object for each script associated with it which allows you to store instance info in the script. Yay. I figured this would offer more versatility than static BS.

Originally I wrote this to allow the scripts to be unloaded. Let me make this clear: THE SCRIPTS LOADED INTO BRE CANNOT BE CLEANLY UNLOADED OR RELOADED (I.E. RECOMPILED) until BRE is restarted. See, .NET doesnt let you unload loaded assemblies. I dont know why they did it this way but it sucks a big fat you-know-what. In order to get them to be unloaded correctly you need to put each assembly in its own AppDomain. This complicates every communication between BRE and the script by a factor of about 19023809 (ok, maybe not that high) and severly limits functionality. Also, it makes everything SLOW. What does this mean? Well, if you want to reload a script (recompile it) into BRE you can do it, but the old version of the script will still be loaded (and will take up lots-o memory) until BRE is restarted.

Because each script is loaded into the main AppDomain, it is essentially getting added directly to BRE. So you have access to anything and everything. You even have the power to take down BRE pretty easily. I suppose thats sort of a bad thing, but hey, its not like BRE will be running high privileges with sockets open on some secure network. To keep unintentional take-downs to a minimum there are mechanisms in the bre20.child class that will create gui stuff (maps, checkboxes, textboxes, etc) for you and monitor the event activity.

With these scripts you have a LOT of power. You can go as deep as you want. You can use the mechanisms provided by the bre20.child class to add GUI components with handlers and change the ROM through the child's interface, you can create your own dialog boxes or GUI components that behave in any way you wish and then modify the ROM's byte array directly if you feel so inclined. Be careful and play nice.

I apologize for the possible steep learning curve. But, hey, if you have never programmed before this should be a good way to learn the basics. Also, if the above few paragraphs were cryptic don't worry about it. There are some scripts included which should be fairly good examples. Look in the plugins directory. Also, I've written some small 'guide' type documents. You can view them by clicking on the "Related Pages" tab at the top of this document.

Considerations

Much of this documentation was written without a spell checker or anything of that nature. I tend to leave out letters, so go easy on me. Much of the comments in the api are sparse because extensive documentation takes a HUGE amount of time. Frankly, I have a feeling that this guide and related code will be rarely used by anyone so I cant really justify spending the time on really good docs. If you want more documentation or clarification on something, let me know. Hey, if you want to write some stuff, I'd be glad to add it in here.

This guide was generated from C++ .NET code. The scripting is in C# so there are some differences. For instance, all of the ::'s in C++ are '.'s in C#, all ->'s are '.'s, all pointer's (*'s) are absent in C#, etc. You will probably see some __gc[] and __gc[,] but dont worry, the __gc is just C++ .NETs way of having the garbage collection on primitive type arrays. These types in C++ have C# counterparts (I will add more as I find them):

C++ C#

String* string

Object* object

unsigned char byte

type name __gc[] type[]

type name __gc[,] type[,]

Also, Doxygen doent like the __gc[]'s after the function headers. In much of the documentation it screws up and either doesnt print the __gc[] or trims off the end like __gc[ . All of the multi-dimensional ( __gc[,] ) arrays are trimmed off at the comma in the link list at the top of each class documentation page. An example of this is the get2DArray() function in RomRep.HondaEcu.

EXAMPLE: This shows the necessities of most scripts

//put whatever .NET assemblies you need here
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

//namespaces from bre.exe 
//not absolutely necessary unless you don't want 
//to type the namespace for everything
//
using bre20;
using RomRep;

//References to the main assemblies go here.
//You can use any objects that are in any of the DLLs 
//provided with BRE or any other .net DLLs you create.
//These directives MUST MUST have the //s in front of them.
//Also, these references must go after the using directives
//or the parser wont look for them.
//
//css_reference ..\bre20.exe;
//css_reference ..\SimpleGrid.dll;

//You can reference other scripts/C# files as well.
//Treat this like a C++ #include directive.
//The directive MUST have the //s in front of css_import 
//as well or the .NET compiler will give an error.
//
//css_import externalScript.cs; 

//C# needs namespaces...
namespace Scripting
{
        //class can be named anything.
        public class Script
        {
                //instance variables!
                //
                //BRE main window is of type bre20.Form1. I never thought anyone would see that :)
                Form1 mainWind;
                //
                //each child window is of type 'bre20.child'
                child romWind;

                //not necessary but you can do some init stuff in here
                //you get no parameters.
                public Script()
                {
                }
                
                //This method is NOT necessary.
                //OnOpenRom gets called when the user opens a ROM before ANY child 
                //window is created. Its here so you can do any processing on the rom 
                //before the window made. This also enables the programmer to add
                //support for new ROM types. All you need to do is create your own 
                //ROM class that extends RomRep.HondaEcu, and your own class that 
                //extends ParamPage (or a modified ParamPage object) then give them 
                //to the passed in OnOpenRomArgs object. Since all this happens before 
                //any creation, we can also create new scripts which correspond to this
                //new type by using the AppliesTo() method.
                //
                //At invocation, the isOpenable function WILL BE FALSE. Also, the 
                //current rom will be passed in and is available through the getRom() 
                //function if BRE finds a match. The Parameter page object will be 
                //available through the getParamPage() function. It, as well as the
                //getRom() function, will be null if no openable rom was found by BRE.
                //To create a new ROM type you must open the rom yourself and do all 
                //processing. There is a 'NewRom.cs' script in the plugins dir which 
                //demonstrates this functionality.
                //
                //The param is:
                //ra = OnOpenRomArgs object
                //
                //ra.getRom();                  returns the current rom. will be null if
                //                              BRE cant open the file natively.
                //
                //ra.setRom(RomRep.HondaEcu);   set your new type of rom for BRE to open
                //                              The bool below MUST be set as well.
                //
                //ra.setOpenable(bool);         set this if you want to open the file
                //                              rom MUST be set with your new type as well.
                //
                //ra.getFilename();             returns the full path and filename.
                //
                //ra.setParamPage(ParamPage);   sets the param page
                //
                //ra.getParamPage();            returns the param page object associated
                //                              with the HondaEcu object
                public static object OnOpenRom(ref OnOpenRomArgs ra)
                {
                        //Did BRE already find a suitable ROM type?
                        //if so, return NULL
                        if(ra.getRom() != null)
                                return null;

                        //otherwise open all not openable stuff as a JDM PW0
                        ra.setOpenable(true);
                        ra.setRom(new JdmPw0(Engine.getFile(ra.getFilename(),0x8000),0x8000));
                        return ra;
                }
                
                //this is ABSOLUTELY necessary. It tells BRE what ROM types this script applies to
                //The string returned is split from the ':' and then BRE tries to match each string
                //with the ROM type. The matching is case insensitive and if the type just contains
                //one of these strings then the script is assumed to apply. 
                //
                //i.e. returning "pr3:pw0" would, among others, work with "pr3 dev" ROMs.
                //can pass in the string 'all' to apply to all ROMs.
                public static object AppliesTo()
                {
                        return "pr3:pw0";
                }
                
                //again, ABSOLUTELY necessary
                //this is the string that shows up in the manager window
                public static object ScriptTitle()
                {
                        return "Sample BRE Script by Ben Ogle";
                }

                //ScriptMain is the main entry point into your script. When the script is 
                //instantiated for the first time, this is called. 
                //
                //Passed in through args are the objects you should need:
                //args[0] = main window of type bre20.Form1
                //args[1] = child window of type bre20.child
                //
                public object ScriptMain(params object[] args)
                {
                        mainWind = (Form1)args[0];
                        romWind = (child)args[1];
                        
                        // With each object that BRE creates for you, you must add a handler
                        // function. Parameter 4 in this function call sets up that handler. 
                        // The string must be the name of the function WITH the 
                        // Namespace.className.FunctionName
                        // 
                        childWind.CreateCheckBox("testing", 1, 55, "Scripting.Script.chk_click");


                        //this method must return something
                        return null;
                }

                //Here is a handler function definition 
                //the params are:
                //args[0] = main window
                //args[1] = rom window (the child object)
                //args[2] = object that sent the event 
                //args[3] = event object (will be base class of EventArgs)
                //
                public object chk_click(params object[] args)
                {
                        CheckBox c = (CheckBox)args[2];

                        MessageBox.Show("checked: " + c.Checked.ToString());

                        //must return something
                        return null;
                }
        }
}

EXAMPLE: This shows the necessities of ALL scripts

using System;

//assuming this is in a child directory of the exe:
//css_reference ..\bre20.exe;

namespace Scripting
{
        public class Script
        {
                
                public static object AppliesTo()
                {
                        return "all";
                }
                
                public static object ScriptTitle()
                {
                        return "Basic BRE Script by Ben Ogle";
                }

                public object ScriptMain(params object[] args)
                {

                        return null;
                }
        }//end class
}//end namespace

Known Issues

  1. If you change something in a script file that is referenced by the script file that has the ScriptMain entry point, but do not change that main script file, the changes will NOT be recompiled. The parser only looks at whether the main script file has been modified. I suppose I should change that.
  2. The reload menu item in the script manager just unloads the script. Yeah.



BRE Documentation Home