Refactoring and adding docs

This commit is contained in:
Rodrigo Alfonso
2024-01-15 05:01:54 -03:00
parent be374aa16a
commit 7dd0c0d44c
2 changed files with 63 additions and 76 deletions

View File

@@ -3,11 +3,12 @@
A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port. Its main purpose is to provide multiplayer support to homebrew games.
- [👾](#-LinkCable) [LinkCable.hpp](lib/LinkCable.hpp): The classic 16-bit **Multi-Play mode** (up to 4 players) using a GBA Link Cable!
- [🔧👾](#-LinkRawCable) [LinkRawCable.hpp](lib/LinkRawCable.hpp): A **minimal** low-level API for the 16-bit multiplayer mode.
- [🔧👾](#-LinkRawCable) [LinkRawCable.hpp](lib/LinkRawCable.hpp): A **minimal** low-level API for the 16-bit Multi-Play mode.
- [💻](#-LinkCableMultiboot) [LinkCableMultiboot.hpp](lib/LinkCableMultiboot.hpp): ‍Send **Multiboot software** (small 256KiB ROMs) to other GBAs with no cartridge!
- [🔌](#-LinkGPIO) [LinkGPIO.hpp](lib/LinkGPIO.hpp): Use the Link Port however you want to control **any device** (like LEDs, rumble motors, and that kind of stuff)!
- [🔗](#-LinkSPI) [LinkSPI.hpp](lib/LinkSPI.hpp): Connect with a PC (like a **Raspberry Pi**) or another GBA (with a GBC Link Cable) using this mode. Transfer up to 2Mbit/s!
- [📻](#-LinkWireless) [LinkWireless.hpp](lib/LinkWireless.hpp): Connect up to 5 consoles with the **Wireless Adapter**!
- [🔧📻](#-LinkRawWireless) [LinkRawWireless.hpp](lib/LinkRawWireless.hpp): A **minimal** low-level API for the Wireless Adapter.
- [🌎](#-LinkUniversal) [LinkUniversal.hpp](lib/LinkUniversal.hpp): Add multiplayer support to your game, both with 👾 *Link Cables* and 📻 *Wireless Adapters*, using the **same API**.
*(click on the emojis for documentation)*
@@ -78,31 +79,6 @@ Name | Return type | Description
⚠️ `0xFFFF` and `0x0` are reserved values, so don't send them!
# 🔧👾 LinkRawCable
- This is a minimal hardware wrapper designed for the *multiplayer mode*.
- It doesn't include any of the features of [👾 LinkCable](#-LinkCable), so it's not well suited for games.
- Its demo (`LinkRawCable_demo`) can help emulator developers in enhancing accuracy.
## Methods
Name | Return type | Description
--- | --- | ---
`isActive()` | **bool** | Returns whether the library is active or not.
`activate(baudRate = BAUD_RATE_1)` | - | Activates the library in a specific `baudRate` (`LinkRawCable::BaudRate`).
`deactivate()` | - | Deactivates the library.
`transfer(data)` | **LinkRawCable::Response** | Exchanges `data` with the other end. Returns the received data, including the assigned player id.
`transfer(data, cancel)` | **LinkRawCable::Response** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`.
`transferAsync(data)` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`. Note that until you retrieve the async data, normal `transfer(...)`s won't do anything!
`getAsyncState()` | **LinkRawCable::AsyncState** | Returns the state of the last async transfer (one of `LinkRawCable::AsyncState::IDLE`, `LinkRawCable::AsyncState::WAITING`, or `LinkRawCable::AsyncState::READY`).
`getAsyncData()` | **LinkRawCable::Response** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`.
`isMaster()` | **bool** | Returns whether the console is connected as master or not. Returns garbage when the cable is not properly connected.
`isReady()` | **bool** | Returns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected.
`getBaudRate()` | **LinkRawCable::BaudRate** | Returns the current `baudRate`.
- don't send `0xFFFF`, it's a reserved value that means *disconnected client*
- only `transfer(...)` if `isReady()`
# 💻 LinkCableMultiboot
*(aka Multiboot through Multi-Play mode)*
@@ -267,3 +243,39 @@ Name | Return type | Description
`getProtocol()` | **LinkUniversal::Protocol** | Returns the active protocol (one of `LinkUniversal::Protocol::AUTODETECT`, `LinkUniversal::Protocol::CABLE`, `LinkUniversal::Protocol::WIRELESS_AUTO`, `LinkUniversal::Protocol::WIRELESS_SERVER`, or `LinkUniversal::Protocol::WIRELESS_CLIENT`).
`setProtocol(protocol)` | - | Sets the active `protocol`.
`getWirelessState()` | **LinkWireless::State** | Returns the wireless state (same as [📻 LinkWireless](#methods-4)'s `getState()`).
# 🔧👾 LinkRawCable
- This is a minimal hardware wrapper designed for the *Multi-Play mode*.
- It doesn't include any of the features of [👾 LinkCable](#-LinkCable), so it's not well suited for games.
- Its demo (`LinkRawCable_demo`) can help emulator developers in enhancing accuracy.
## Methods
Name | Return type | Description
--- | --- | ---
`isActive()` | **bool** | Returns whether the library is active or not.
`activate(baudRate = BAUD_RATE_1)` | - | Activates the library in a specific `baudRate` (`LinkRawCable::BaudRate`).
`deactivate()` | - | Deactivates the library.
`transfer(data)` | **LinkRawCable::Response** | Exchanges `data` with the other end. Returns the received data, including the assigned player id.
`transfer(data, cancel)` | **LinkRawCable::Response** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`.
`transferAsync(data)` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`. Note that until you retrieve the async data, normal `transfer(...)`s won't do anything!
`getAsyncState()` | **LinkRawCable::AsyncState** | Returns the state of the last async transfer (one of `LinkRawCable::AsyncState::IDLE`, `LinkRawCable::AsyncState::WAITING`, or `LinkRawCable::AsyncState::READY`).
`getAsyncData()` | **LinkRawCable::Response** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`.
`isMaster()` | **bool** | Returns whether the console is connected as master or not. Returns garbage when the cable is not properly connected.
`isReady()` | **bool** | Returns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected.
`getBaudRate()` | **LinkRawCable::BaudRate** | Returns the current `baudRate`.
- don't send `0xFFFF`, it's a reserved value that means *disconnected client*
- only `transfer(...)` if `isReady()`
# 🔧📻 LinkRawWireless
- This is a minimal hardware wrapper designed for the *Wireless Adapter*.
- It doesn't include any of the features of [📻 LinkWireless](#-LinkWireless), so it's not well suited for games.
- Its demo (`LinkRawWireless_demo`) can help emulator developers in enhancing accuracy.
## Methods
- There's one method for every supported wireless adapter command.
- Use `sendCommand(...)` to send arbitrary commands.

View File

@@ -4,7 +4,7 @@
// --------------------------------------------------------------------------
// A low level driver for the GBA Wireless Adapter.
// --------------------------------------------------------------------------
// - Advanced usage only (check out the documentation).
// - Advanced usage only!
// - If you're building a game, use `LinkWireless`.
// --------------------------------------------------------------------------
@@ -16,8 +16,6 @@
#include <string>
#include <vector>
// TODO: LOGGING BUILD OPTION
#define LINK_RAW_WIRELESS_MAX_PLAYERS 5
#define LINK_RAW_WIRELESS_MIN_PLAYERS 2
#define LINK_RAW_WIRELESS_END 0
@@ -83,19 +81,6 @@ class LinkRawWireless {
std::vector<u32> responses = std::vector<u32>{};
};
enum Error {
// TODO: REPLACE lastError with logger calls
// User errors
NONE = 0,
GAME_NAME_TOO_LONG = 1,
USER_NAME_TOO_LONG = 2,
// Communication errors
COMMAND_FAILED = 5,
CONNECTION_FAILED = 6,
ACKNOWLEDGE_FAILED = 9,
};
struct Server {
u16 id = 0;
u16 gameId;
@@ -106,10 +91,30 @@ class LinkRawWireless {
bool isFull() { return nextClientNumber == 0xff; }
};
typedef struct {
u16 deviceId = 0;
u8 clientNumber = 0;
} ConnectedClient;
typedef struct {
u8 nextClientNumber = 0;
std::vector<ConnectedClient> connectedClients = {};
} SlotStatusResponse;
typedef struct {
std::vector<ConnectedClient> connectedClients = {};
} AcceptConnectionsResponse;
enum ConnectionPhase { STILL_CONNECTING, ERROR, SUCCESS };
typedef struct {
ConnectionPhase phase = STILL_CONNECTING;
u8 assignedClientNumber = 0;
} ConnectionStatus;
bool isActive() { return isEnabled; }
bool activate() {
lastError = NONE;
isEnabled = false;
bool success = reset(true);
@@ -121,7 +126,6 @@ class LinkRawWireless {
bool deactivate() {
bool success = sendCommand(LINK_RAW_WIRELESS_COMMAND_BYE).success;
lastError = NONE;
isEnabled = false;
resetState();
stop();
@@ -143,11 +147,11 @@ class LinkRawWireless {
std::string userName = "",
u16 gameId = LINK_RAW_WIRELESS_MAX_GAME_ID) {
if (gameName.length() > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) {
lastError = GAME_NAME_TOO_LONG;
logger("! game name too long");
return false;
}
if (userName.length() > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) {
lastError = USER_NAME_TOO_LONG;
logger("! user name too long");
return false;
}
gameName.append(LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH - gameName.length(),
@@ -185,7 +189,6 @@ class LinkRawWireless {
if (!success) {
reset();
lastError = COMMAND_FAILED;
return false;
}
@@ -196,14 +199,6 @@ class LinkRawWireless {
return true;
}
typedef struct {
u16 deviceId = 0;
u8 clientNumber = 0;
} ConnectedClient;
typedef struct {
u8 nextClientNumber = 0;
std::vector<ConnectedClient> connectedClients = {};
} SlotStatusResponse;
bool getSlotStatus(SlotStatusResponse& response) {
auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_SLOT_STATUS);
@@ -225,9 +220,6 @@ class LinkRawWireless {
return true;
}
typedef struct {
std::vector<ConnectedClient> connectedClients = {};
} AcceptConnectionsResponse;
bool acceptConnections(AcceptConnectionsResponse& response) {
auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_ACCEPT_CONNECTIONS);
@@ -249,6 +241,7 @@ class LinkRawWireless {
return true;
}
bool endHost(AcceptConnectionsResponse& response) {
auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_END_HOST);
@@ -277,7 +270,6 @@ class LinkRawWireless {
if (!success) {
reset();
lastError = COMMAND_FAILED;
return false;
}
@@ -296,7 +288,6 @@ class LinkRawWireless {
if (!success) {
reset();
lastError = COMMAND_FAILED;
return false;
}
@@ -346,7 +337,6 @@ class LinkRawWireless {
if (!success) {
reset();
lastError = COMMAND_FAILED;
return false;
}
@@ -356,11 +346,6 @@ class LinkRawWireless {
return true;
}
enum ConnectionPhase { STILL_CONNECTING, ERROR, SUCCESS };
typedef struct {
ConnectionPhase phase = STILL_CONNECTING;
u8 assignedClientNumber = 0;
} ConnectionStatus;
bool keepConnecting(ConnectionStatus& response) {
auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_IS_FINISHED_CONNECT);
if (!result.success || result.responses.size() == 0) {
@@ -429,7 +414,6 @@ class LinkRawWireless {
if (!success) {
reset();
// TODO: ERRORS?
return false;
}
@@ -442,7 +426,6 @@ class LinkRawWireless {
if (!result.success) {
reset();
// TODO: ERRORS?
return false;
}
@@ -523,12 +506,6 @@ class LinkRawWireless {
bool isSessionActive() { return state == SERVING || state == CONNECTED; }
u8 playerCount() { return sessionState.playerCount; }
u8 currentPlayerId() { return sessionState.currentPlayerId; }
Error getLastError(bool clear = true) {
Error error = lastError;
if (clear)
lastError = NONE;
return error;
}
~LinkRawWireless() {
delete linkSPI;
@@ -538,7 +515,6 @@ class LinkRawWireless {
struct SessionState {
u8 playerCount = 1;
u8 currentPlayerId = 0;
u8 maxPlayers = LINK_RAW_WIRELESS_MAX_PLAYERS;
};
struct LoginMemory {
@@ -550,7 +526,6 @@ class LinkRawWireless {
LinkSPI* linkSPI = new LinkSPI();
LinkGPIO* linkGPIO = new LinkGPIO();
State state = NEEDS_RESET;
Error lastError = NONE;
volatile bool isEnabled = false;
void recoverName(std::string& name,