Developer Notes

Jump to: navigation, search

Notes for OHRRPGCE developers. This page is actually directed at the core developers as much as anyone else, as there are some pretty easy to forget gotchas when working on the engine. Category:OHRRPGCE Development links to other articles relevant to developers.

FreeBASIC stuff[edit]

Non-standard use of FreeBASIC[edit]

This section lists a things which might surprise FreeBASIC programmers:

  • Source files ending in .rbas are written in RELOADBasic, which is preprocessed to FreeBASIC.
  • integer is always 32 bit (see Before #include'ing a .bi you need to "call" the use_native_integer() macro, and then "call" the use_32bit_integer() macro afterwards.
  • GOSUB and RETRACE are used instead of GOSUB and RETURN, see below
  • bool is an alias to integer (not boolean!) and is strictly informative. bool isn't yet used in most of the source. The defines YES and NO are used as truth values.
  • A few dark corners use calls to undocumented functions in the FB runtime library, such as fb_KeyHit, fb_FileGetIOB, and more frighteningly internal functions like fb_hStrDelTemp and hooks into file objects.
  • In some places vectors are used instead of FB's builtin arrays. vector is #defined as ptr.
  • There's a fair amount of C, C++, Objective C code for the most low level stuff (CRT and Unix .bi translations are not portable)
  • Linking is done by default using g++ instead of fbc, originally to make linking C++ work, however more recently fbc also works if you want, using scons linkgcc=0
  • Wrappers around various FB functions like DIR and KILL are used, see below
  • A fork of FB is currently used to target Android and Mac
  • Since the QB days the OHR used a whole library of assembly routines called allmodex, and none of the QB graphics or audio commands. We still only use fbgfx commands in gfx_fb.bas, and they must not be used elsewhere.

Hmm, looking over that list, every single item except allmodex and a couple wrappers are my (TMC's) fault. I just can't resist looking under the hood and playing with FB internals.


WITH is not just shorthand, it stores a pointer and uses that to access .members. Inside a 'WITH foo' block, be careful not to make the reference to 'foo' invalid, eg.:

WITH myarray(i)
 REDIM myarray(i + 10)
 'Accessing myarray(i) members with .dot syntax will now likely segfault

Converting doubles to integers[edit]

  • INT is actually the floor function. It return a double. Both unfortunate.
  • CINT returns the nearest integer to a double (rounding to even to break ties). Returns an integer
  • FIX truncates (rounds to 0). Returns a double.
  • Assigning an double to an integer variable uses CINT

INT is usually the wrong function to use.


There are several bugs in FB that affect us. A very incomplete list:

  • Implicitly declared strings (undeclared variables with $ suffix) are not freed! This leads to lots of memory leaks, which you're just going to have to ignore if using valgrind.
  • fbc just loves thowing branch crossing variable definition warnings randomly, especially the linux builds. Real branch crossing warning are bugs that need to be fixed, but unfortunately most of these warnings are completely imaginary (and their random nature suggests they are due to initialised memory in the compiler (as might be caused by a branch crossing a variable definition, actually)).
  • There's quite a few things (such as placement NEW) documented as being accessible from -lang fb only which we actually use from -lang deprecated.
  • SHELL on Linux (used in findfiles and isdir) (and maybe other unices) leaks file descriptors! valgrind reports these. Luckily the limit very high.
  • OPEN on Linux can't open files which contain a \ character, it appears that the rtlib is doing something it shouldn't.



I've added warnings to all the types that are manually allocated/freed, which looks like:

'WARNING: don't add strings to this
TYPE foo

Don't add members of other types which need to constructed or destructed either (which is just other types containing strings). Please slap such a warning on a Type if you manually allocate/free memory for it anywhere. If you do want to add a string to such a type, you either need to replace Allocate/Deallocate with New/Delete, or to manually call .Destructor() before deleting it (strings don't actually need fancy initialisation, aside from zeroing out the string descriptor memory). I have no clue why I didn't use New/Delete everywhere in the first place; this should be cleaned up!


We don't use FreeBASIC's GOSUB, instead we use our own implementation. Use RETRACE instead of RETURN, this way we can use the normal meaning of RETURN. GOSUB must be the last statement on the line.

This uses scary macros containing assembly which push the current instruction pointer to the stack (see; related details at FB stack internals). While I know of no way for these pointers to end up corrupted or interfere with other code, returning from a GOSUB block can causes problems. Jumping out of scopes is allowed, but you should not be allow to jump into a scope. It's disallowed in C, for example, and FB will throw a branch crossing warning normally, however it doesn't know that a GOSUB will later cause a RETRACE, so you'll get no warning or error.

GOSUB is not allowed inside a WITH, SCOPE (after variable definitions), FOR with a local counter or non-constant end or step, (and possibly other constructs I can't think of right now?).

WITH *blarg
 GOSUB foo
 .bar = 3   'BAD: WITH pointer may be corrupted

FOR i as integer = 0 TO 4
 GOSUB foo  'BAD: i may be corrupted

FOR i = 1 TO UBOUND(cmdline_args)
 GOSUB foo  'BAD: loop number may be corrupted

FOR i = 0 TO 2
 GOSUB foo  'Allowable, but avoidance suggested

On the other hand, FOR, WITH, SCOPE and DIM are allowed inside a GOSUB block.

Also, every GOSUB must be balanced by a RETRACE. You can not GOSUB twice, RETRACE once, and then EXIT FUNCTION.

Luckily illegal GOSUBs can be detected using the misc/gosub.el script (which TMC runs every year or two) which . There is also misc/with.el to place warnings after GOSUBs within WITH blocks. I can't remember whether this was necessary because misc/gosub.el won't detect these cases or not.

File functions[edit]

Use our alternatives to various FB file related functions. These add error checking and logging and work around FB bugs

  • openfile should be used instead of OPEN when opening a file that might be an unlumped .rpg or .rpgdir lump file; it's always safe to use openfile instead of open.
  • findfiles or isfile or isdir instead of DIR (Note: isfile/isdir/findfiles could be improved by using OS-specific functions, eg. stat() on Unix. Browsing could be greatly sped up.)
  • killdir instead of RMDIR
  • makedir instead of MKDIR
  • safekill instead of KILL (better to use safekill even if you're sure the file exists)
  • And more file/system functions (which don't necessarily always replace the FB equivalent)
  • If you want to know the number of bytes successfully read from the file (like C's read()), use fb_FileGetIOB (See lumpfile.bas for example)

A note on files[edit]

FB's file functions are mostly wrappers around the C runtime, but add a lot of additional stuff. Unlike in C, reading off the end of a file succeeds, reading zeroed out bytes (even if using fb_FileGetIOB; which returns the number of bytes actually read, not the amount written to memory). However, writing off the end of a file will still write garbage between the end of the existing file and the start of the new data. You can use the extendfile Sub to guard against this before writing.


Note: __FB_UNIX__ has been fixed since FB 0.24 and this can be removed. Yay! __FB_UNIX__ only exists in FB 0.21 or later (which is our requirement now anyway), however it's always defined (taking value 0 or -1), unlike __FB_LINUX__ and so on. I recommend always using our own __UNIX__ define instead, as it's much less confusing. Don't use __FB_LINUX__ except for truely Linux-specific stuff. Example (in

#ifdef __UNIX__
# define SLASH "/"
# define LINE_END !"\n"
# define SLASH "\"
# define LINE_END !"\r\n"


FB arrays are very limited. See vectors for documentation of our custom arrays library.



PageUp+PageDown+Esc/Alt+F4/Cmd+Q/etc. work by setting keyval(scQUIT) (scQUIT is -1), which causes setkeys to send a stream of ESC keypresses until clearkey(scQUIT) is called. All menus in Custom should exit on a Esc keypress, except for those with unsaved state (eg. a modified help file and the Save/Save+Quit/Discard+Quit game menu). You need to call clearkey(scQUIT) before querying the user whether to save if an Esc keypress will cancel the attempted exit.

See Also[edit]