mirror of
https://github.com/afska/gba-link-connection.git
synced 2026-10-02 13:56:42 -05:00
Refactoring and adding docs
This commit is contained in:
64
README.md
64
README.md
@@ -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.
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user