# Coding style guidelines

## Structure

The code is divided into the following directories:
 * src/         - utility functions, tools
 *   game/      - game logic
 *   ui/        - UI implementations
 *     classic/ - the 1993 UI
 *     cmdline/ - experimental command line UI
 *     nop/     - dummy UI for tools
 *   hw/        - hardware abstraction
 *     sdl/     - libSDL
 *       1/     - libSDL 1.2.x
 *       2/     - libSDL 2.x
 *     alleg/   - Allegro
 *       4/     - Allegro 4.x
 *     nop/     - dummy HW for tools
 *   os/        - Operating System; file paths
 *     unix/    - yay
 *     win32/   - boo
 *     msdos/   - wtf

Some facts:
 * main is in hw/ (due to SDL)
 * os/ knows nothing about game/, ui/ or hw/
 * hw/ knows nothing about game/ or ui/
 * game/ knows nothing about hw/
 * ui/ is known via src/ui.h
 * hw/ is known via src/hw.h
 * os/ is known via src/os.h
 * src/ knows game via the very narrow src/gameapi.h
   (the exception is saveconv.c)
 * ui/ knows all about game/

Different hw/ and ui/ implementations can be built with a single configure.
The resulting binaries follow a "1oom_$UI_$HW" naming convention.

## Style

- no tabs, 4 spaces for indentation

- always add the { } braces to if, do, while, for statements

- one space after a ',', if, do, while, for and return statements,
  no space after function name:
    if (func(4, 5) < 0) {
        return (foo * 4) + 2;
    }

- add spaces around +, -, *, /, =, == etc:
    a = (22 + b * 4) / 7;

- have the opening brace on the same line as the statement,
  unless the condition spills to multiple lines:
    if (foo) {
        ...
    }
    if (1
      && really_long_name
      && (function_call(with_parameters) < and_a_comparison)
    ) {
        ...
    }

- have the opening brace of a function in the next line:
    int func(void)
    {
        ...
    }

- use C-style comments /* like here */

- function and variable names are lowercase with '_' for space

- defines and enums are UPPERCASE with '_' for space

- avoid global variables

- declare functions / data which are not used outside the module as static

- group static functions / data above others, separate with a /* --.. */ line

- avoid memory allocations after game startup

Do not use unsafe string functions like strcpy, strcat and sprintf. 1oom pbx
files may redefine most in-game strings, and besides, it's good practice.
There are replacement functions in src/lib.c which take an additional buffer
size argument and crash instead of overflowing the buffer.

Avoid declaring variables after the start of scope:
    if (foo) {
        int this_is_ok;
        v = 123;
        int this_is_frowned_upon;
        ...
    }

Note that declaring the loop variable inside for is ok:
    for (int i = 0; i < FOO; ++i) {
        ...
    }

Otherwise, try to keep close to C89.

Avoid #ifdefs! No OS-specific #ifdefs are allowed in game/, ui/ or src/.
Acceptable ones include HAVE_SAMPLERATE etc and FEATURE_MODEBUG.

## On brevity

Comment only the unobvious or the big picture. Do not add inane comments like:
    /* do stuff */
    do_stuff();

Keep the commit message as one line of 70 characters or less. Do not end it
with a '.'. Save any explanatory paragraphs for the merge request.

## Portability

1oom is designed to be cross-platform and work on different Operating
Systems, processors and libraries. Bear this in mind when writing code.

Each hardware abstraction library will have its own directory in hw/.
Any and all of these must be buildable with one configure and produce
differently named binaries.

Do not use the `long` type (its size differs across platforms; use
`int` or `int64_t` depending on which you want).

Be careful with platform dependencies: do not use Windows API
functions outside os/win32/, for example. Do not use SDL outside of
hw/sdl/.

Tests for OS in configure.ac and use for example `IS_WINDOWS` instead of
the preprocessor `_WIN32`. The #defines can be used to identify the OS
if necessary. Try to avoid this if possible. Only consider them in hw/ or os/.

Hey, you there, OS X porter! Do not turn os/unix/ into an #ifdef shithole,
just copy it to os/osx/ and hack away.

Be careful of endianness!  The the macros in bits.h to do endianness
conversion.  Never assume that integer types have a particular byte
ordering.  Similarly, never assume that fields inside a structure
are aligned in a particular way.  This is most relevant when reading
or writing data to a file or a network pipe.

For signed integers, you shouldn’t assume that `(i >> n)` is the same as
`(i / (1 << n))`.  However, most processors handle bitshifts of signed
integers properly, so it’s not a huge problem.

## GNU GPL and licensing

All code submitted to the project must be licensed under the GNU GPLv2 or a
compatible license.

## No thanks

An incomplete list of reasons for rejecting patches:

- new build systems: CMake, Visual Studio, XCode, ...
  * This project uses automake etc. Maintaining multiple build systems
    is bound to fail, especially with binary or otherwise messy project file
    format. While automake etc are far from perfect, they are not an obstacle
    for portability.

- new build dependencies
  * There is no reason to require Turing-complete dependencies, for example
    Python or Perl, to build this project.
    (No, Debian libsdl1.2-dev depending on python is not an excuse.)
    (Neither is src/version.sh.)
    There is very little reason for tools (convert, ...) either.
    Limiting build deps benefits portability.

- new buildtime data files
  * This helps with the above item.

- new runtime data files
  * This projects aims to have 0 extra files required besides the executable
    and the LBX files. The lbxview font is an unfortunate compromise.

- new library dependencies
  * Keeping the library dependencies at minimum helps portability.
    These are considered on a case-by-case basis.
    (Note that new hw/ are encouraged!)

- new optional libraries
  * Keeping the amount of libraries reasonable helps to avoid #ifdef hell.
    These are considered on a case-by-case basis.
    Libs with unstable/numerous APIs are discouraged. (libpng, ffmpeg, ...)

- new picture formats
  * There are plenty of tools for PCX file conversion.
    Optional PNG support would likely require more lines of code than the
    NIH PCX implementation. Don't even start with lossy formats.

- movie recording
  * No.

- cosmetic OpenGL shaders
  * No.

- Scale2x, HQ3x, ...
  * Nearest/linear via OpenGL should be enough.

- software 2x, 3x, ... scaling
  * Show a platform without OpenGL and with screen resolution exceeding 400x300
    and port 1oom to it (configure.ac, os/FOO/, also hw/FOO/ if no SDL) then OK.
    Otherwise -uiscale and nearest/linear via OpenGL should be enough.

If you *must* have some of these then have fun with your fork.
