1
0
mirror of https://github.com/djhackersdev/bemanitools.git synced 2024-11-30 17:24:30 +01:00
bemanitools/doc/development.md

422 lines
14 KiB
Markdown

# Development
This document is intended for developers interested in contributing to Bemanitools. Please read this document before
you start developing.
## Goals
We want you to understand what this project is about and its goals. The following list serves as a guidance for all
developers to identify valuable contributions for this project. As the project evolves, these gaols might do as well.
* Allow running Konami arcade rhythm games, i.e. games of the Bemani series, on arbitrary hardware.
* Emulate required software and hardware features.
* Provide means to cope with incompatibility issues resulting from using a different software platform (e.g. version
of Windows).
* Provide an API for custom interfaces and configuring fundamental application features.
## Development environment
The following tooling is required in order to build this project.
### Tooling
#### Linux / MacOSX
* git
* make
* mingw-w64
* clang-format
* wine (optional, for running tests or some quick testing without requiring a VM)
On MacOSX, you can use homebrew or macports to install these packages.
#### Windows
TODO
### IDE
Ultimately, you are free to use whatever you feel comfortable with for development. The following is our preferred
development environment which we run on a Linux distribution of our choice:
* Visual Studio Code with the following extensions
* C/C++
* C++ Intellisense
* Clang-Format
### Further tools for testing and debugging
* Debugger: Can be part of your reverse engineering IDE of your choice or stand-along like
[OllyDbg](http://www.ollydbg.de/).
* [apitrace](https://apitrace.github.io/): Trace render calls to graphics APIs like D3D and OpenGL.
This tool allows you to record and re-play render calls of an application with frame-by-frame
debugging. Very useful to analyze the render pipeline or debug graphicial glitches.
## Building
Simply run make in the root folder:
```
make
```
All output is located in the *build* folder including the final *bemanitools.zip* package.
### Release building
A release build is a clean build including code formatting and testing. This can be executed by running the following
command:
```
make release
```
## Building with docker
You can also build bemanitools using docker which avoids having to setup a full development
environment if you are just interested in building binaries for the latest changes. Naturally, this
requires you to have the docker daemon installed. Then, run the following command from the root
folder of the project:
```bash
make build-docker
```
Once completed successfully, the build output is located in the `build/docker` sub-folder.
## Creating releases
For developers to create official releases with major and minor versioning:
1. Ensure that all everything you want to have for this release is merged into master.
1. Create a [changelog](CHANGELOG.md) based on aggregating and summerizing commit messages of
whatever got added starting from the tag of the current version to the current head of master.
Commit that changelog and push to master.
1. Create a tag with the next version number, e.g. for 5.28:
```bash
git tag v5.28
```
1. Push the tag upstream:
```bash
git push origin v5.28
```
1. Wait for the CI pipeline to finish building the release and check if everything's ok.
1. On GitLab, go to "Tags" in the repository, click on the commit ID below the tag in the list you
just created.
1. Click on the "Pipelines" tab and click on the download button on the right and on "Download
release artifacts".
1. Rename the downloaded zip to "bemanitools-v5.28.zip" and the replace v5.28 with the version
you want to release.
1. Upload the release.
1. For publishing the release, create a post with the following contents:
```
v5.28
<insert link to uploaded zip here>
<additional comments or things to point out for this release>
Changelog copy-paste:
<insert copy-pasted changelog you created previously here>
```
## Code formatting
To apply our code style using clang-format, simply run the following command:
```
make code-format
```
Please also refer to the [section about](###Additional-code-style-guidelines) which cannot be covered using
clang-format.
## Testing
This still needs to be improved/implemented properly to run the unit-tests easily. Currently, you have to be on either
a Linux/MacOSX system and run
```
make run-tests
```
This executes all currently available unit-tests and reports to the terminal any errors. This requires wine to be
installed.
## Project structure
Now that your setup is ready to go, here is brief big picture of what you find in this project.
* build: This folder will be generated once you have run the build process.
* dist: Distribution related files such as (default) configuration files, shell scripts, etc.
* doc: Documentation for the tools and hooks as well as some development related docs.
* src: The source code
* imports: Provides headers and import definitions for AVS libs and a few other dependencies.
* main: The main source code with game specific hook libraries, hardware emulation and application fixes.
* test: Unit tests for modules that are not required for hooking or the presence of a piece of hardware.
* .clang-format: Code style for clang-format.
* GNUmakefile: Our makefile to build this project.
* Module.mk: Defines the various libraries and exe files to build for the distribution packages.
## Code style and guidelines
Please follow these guidelines to keep a consistent style for the code base. Furthermore, we provide some best practices
that have shown to be helpful. Please read them and reach out to us if you have any concerns or valuable
additions/changes.
### Clang-format
The style we agreed on is provided as a clang-format file. Therefore, we use clang-format for autoformatting our code.
You can use clang-format from your terminal but when using Visual Studio Code, just install the extension. Apply
formatting manually at the end or enable the "reformat on save" feature.
However, clang-format cannot provide guidance to cover all our style rules. Therefore, we ask you to stick to the
"additional" guidelines in the following sections.
### Additional code style guidelines
#### No trailing comments
```
// NOPE
int var = 1; // this is a variable
// OK
// this is a variable
int var = 1;
```
#### Comment style
* Use either // or /* ... */ for single line.
* Use /* ... */ for multiline comments.
* Use /* ... */ for documentation.
Examples:
```
// single line comment
int var = 1;
/* another single line comment */
int var2 = 2;
/* multi
line
comment */
/**
* This is a function.
*/
int func(int a, int b);
```
#### Include guards
Provide include guards for every header file. The naming follows the namespacing of the module and the module name.
Example for bsthook/acio.h file:
```
#ifndef BSTHOOK_ACIO_H
#define BSTHOOK_ACIO_H
// ...
#endif
```
#### Empty line before and after control blocks
Control blocks include: if, if-else, for, while, do-while, switch
Makes the code more readible with control blocks being easily visible.
Example
```
int var = 1;
if (var == 2) {
// ...
}
printf("%d\n", var);
```
#### Empty line at the start of blocks
There are situations when you want to have an empty line at the start of the block to enhance readability, for example:
```
if (a &&
b &&
c) {
d = 5;
}
```
Here the assignment aligns with the conditions of the if-black making it hard to read. Better:
```
if (a &&
b &&
c) {
d = 5;
}
```
#### Includes
* Always keep all includes at the top of a header and source file. Exception: Documentation before include guards on
header file.
* Use *< >* for system-based includes, e.g. <stdio.h>.
* Use *" "* for project-based includes, e.g. "util/log.h".
* For project-based includes, always use the full path relative to the root folder (src/main, src/test), e.g.
"util/log.h" and not "log.h" when being in another module in the "util" namespace.
* Sorting
* System-based includes before project-based includes
* Block group them by different namespaces
* Lex sort block groups
* Because windows header files are a mess, the sorting on system-based includes is not always applicable. Please add
a comment when applicable and apply the necessary order.
Example for sorting
```
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "iidxhook-util/acio.h"
#include "iidxhook-util/d3d9.h"
#include "util/log.h"
#include "util/mem.h"
```
### Documentation
In general, add comments where required to explain a certain piece of code. If is not self-explanatory about:
* Why is it implemented like this
* Very important details to understand how and why it is working that only you know
* A complex algorithm/logic
Make sure to add some comments or even an extended document. Avoid comments that explain trivial and obvious things
like "enable feature X" before an if-block with a feature switch.
Especially if it comes to reverse-engineering efforts, comments or even a separate document is crucial to allow others
to understand the depths you dived into.
Any extended notes about documentation some hardware, protocol, reverse-engineering a feature etc. can be stored in
the *doc/dev* folder and stay with the repository. Make good use of that!
#### Header files
Document any enum, struct or function exposed by a header file. Documentation of static functions or variables in
source modules is not required. Also provide documentation for the module.
Example for my-namespace/my-module.h
```
/**
* Some example module to show you where documentation is expected.
*/
#ifndef MY_NAMESPACE_MY_MODULE_H
#define MY_NAMESPACE_MY_MODULE_H
/**
* Very useful enum for things.
*/
enum my_enum {
MY_NAMESPACE_MY_MODULE_MY_ENUM_VAL_1 = 1,
MY_NAMESPACE_MY_MODULE_MY_ENUM_VAL_2 = 2,
}
/**
* Some cool data structure.
*/
struct my_struct {
int a;
float b;
};
/**
* This is my awesome function doing great things.
*
* Here are some details about it:
* - Detail 1
* - Detail 2
*
* @param a If > 0, something happens.
* @param b Only positive values valid, makes sure fancy things happen.
* @return Result of the computation X which is always > 0. -1 on error.
*/
int my_namespace_my_module_func(int a, int b);
#endif
```
### Naming conventions
In general, try to keep names short but don't overdo it by using abbrevations of things you created. Sometimes this is
not possible and we accept exceptions if there are no proper alternatives.
#### Namespacing
The folder names use lower-case names with dashes *-* as seperators, e.g. *my-namespace*.
#### Modules
Header and source files of modules use lower-case names with dashes *-* as seperators, e.g. *my-module.c*, *my-module.h.
The include guards contain the name of the namespace and module, see [here](#### Include guards).
Variables, functions, structs, enums and macros are namespace accordingly.
#### Variables
Snake-case with proper namespacing to namespace and module. Namespacing applies to static and global variables. Local
variables are not namespaced.
```
// For namespace "ezusb", module "device", static variable in module
static HANDLE ezusb_device_handle;
// Local variable in some function
int buffer_size = 256;
```
#### Functions
Snake-case with proper namespacing to namespace and module for all functions that are not hook functions.
```
// For namespace "ezusb", module "device", static variable in module, init function
void ezusb_device_init(...);
// CreateFileA hook function inside module
HANDLE my_CreateFileA(...)
{
// ...
}
```
#### Structs
Snake-case with proper namespacing to namespace and module.
```
// For namespace "ezusb", module "device" ctx struct
struct ezusb_device_ctx {
// ...
}
```
#### Enums
Snake-case with proper namespacing to namespace and module. Upper-case for enum entries
```
// For namespace "ezusb", module "device" state enum
struct EZUSB_DEVICE_STATE {
EZUSB_DEVICE_STATE_INIT = 0,
EZUSB_DEVICE_STATE_RUNNING = 1,
// ...
}
```
#### Macros
Upper-case with underscore as spacing, proper namespacing to namespace and module.
```
// For namespace "ezusb", module "device" vid
#define EZUSB_DEVICE_VID 0xFFFF
```
#### Multiple variable declarations
Only one declaration per line which also avoids various errors, for example:
```
// Nope
char a, b, b;
```
```
// Ok
char a;
char b;
char c;
```
### Testing
We advice you to write unit tests for all modules that allow proper unit testing. This applies to modules that are not
part of the actual hooking process and do not rely on external devices to be available. Add these tests to the
*src/test* sub-folder.
This does not only speed up your own development but hardens the code base and avoids having to test these things by
running the real applications, hence saving a lot of time and trouble.
### Further best practices
* Avoid external dependencies like additional libraries. Bemanitools is extremely self-contained which enables high
portability and control which is important for implementing various "hacks" to just make things work.
* If you see some module/function lacking documentation that you have to use/understand, add documentation once you
figured out what the function/module is doing. This does not only help your future you but others reading the code.
* Keep documentation and readme files up-to-date. When introducing changes/adding new features, review existing
documentation and apply necessary changes.
## Misc
The core API interception code was ripped out, cleaned up a tiny bit and released on GitHub. BT5 will eventually be
ported to use this external library in order to avoid maintaining the same code in two places at once.
https://github.com/decafcode/capnhook
This too is a little rudimentary; it doesn't come with any examples or even a README yet.