subrepo:
  subdir:   "lib/libpl2"
  merged:   "58e4fa38"
upstream:
  origin:   "https://gitlab.com/parallel-launcher/libpl2"
  branch:   "master"
  commit:   "58e4fa38"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "4f60dd7"
This commit is contained in:
a
2025-06-27 11:57:17 -04:00
parent 221c63e659
commit f183158d03
22 changed files with 3904 additions and 0 deletions

2
lib/libpl2/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/public
/.vscode

11
lib/libpl2/.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,11 @@
image: ubuntu:rolling
pages:
script:
- apt update && apt install doxygen -y
- doxygen
artifacts:
paths:
- public
only:
- master

12
lib/libpl2/.gitrepo Normal file
View File

@@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme
;
[subrepo]
remote = https://gitlab.com/parallel-launcher/libpl2
branch = master
commit = 58e4fa38badf31b732f853ab15689a55db65f3d2
parent = 221c63e659988b7f37df82bd4897dc08fa70e82a
method = merge
cmdver = 0.4.9

2512
lib/libpl2/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

21
lib/libpl2/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Matt Pharoah
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

6
lib/libpl2/README.md Normal file
View File

@@ -0,0 +1,6 @@
# libpl2
libpl2 is a helper library to enable decomp roms to integrate with ParallelN64 and Parallel Launcher, allowing for features such as savestate detection, graphics plugin detection, and in-game romhacking.com integration.
You can find the documentation here:
https://parallel-launcher.gitlab.io/libpl2/

70
lib/libpl2/libpl2-emu.c Normal file
View File

@@ -0,0 +1,70 @@
#include "libpl2-emu.h"
#include "libpl2-internal.h"
typedef struct {
unsigned short pluginId;
unsigned short capabilities;
char name[];
} plugin_internal_t;
lpl2_bool lpl2_get_core_version( lpl2_version *version, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_NOTNULL( version, err, FALSE )
volatile const __lpl2_response *response = __lpl2_query_basic( 0x0001 );
volatile const lpl2_version *payload = (volatile const lpl2_version*)response->payload;
if( response->protocolStatus == 0 ) {
version->major = payload->major;
version->minor = payload->minor;
version->patch = payload->patch;
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
} else {
version->major = version->minor = version->patch = 0;
if( err ) *err = LPL2_UNEXPECTED_ERROR( response->status );
return FALSE;
}
}
lpl2_cheat_flags lpl2_get_cheat_flags( lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, LPL2_NO_CHEAT_FLAGS )
LPL2_VERIFY_ABI( 2, err, LPL2_NO_CHEAT_FLAGS )
volatile const __lpl2_response *response = __lpl2_query_basic( 0x0005 );
if( response->protocolStatus != 0 ) {
if( err ) *err = LPL2_UNEXPECTED_ERROR( response->status );
return LPL2_NO_CHEAT_FLAGS;
}
if( err ) *err = LPL2_ERR_OKAY;
return (lpl2_cheat_flags)response->commandStatus;
}
lpl2_cheat_flags lpl2_clear_cheat_flags( lpl2_cheat_flags flags, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, LPL2_NO_CHEAT_FLAGS )
LPL2_VERIFY_ABI( 2, err, LPL2_NO_CHEAT_FLAGS )
volatile const __lpl2_response *response = __lpl2_query_byte( 0x0006, (unsigned char)flags );
if( response->protocolStatus != 0 ) {
if( err ) *err = LPL2_UNEXPECTED_ERROR( response->status );
return LPL2_NO_CHEAT_FLAGS;
}
if( err ) *err = LPL2_ERR_OKAY;
return (lpl2_cheat_flags)response->commandStatus;
}
lpl2_bool lpl2_get_graphics_plugin( lpl2_plugin_info *plugin, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_NOTNULL( plugin, err, FALSE )
volatile const __lpl2_response *response = __lpl2_query_basic( 0x0004 );
volatile const plugin_internal_t *payload = (volatile const plugin_internal_t*)response->payload;
if( response->status == 0 ) {
plugin->pluginId = (lpl2_gfx_plugin)payload->pluginId;
plugin->capabilities = (lpl2_gfx_capabilities)payload->capabilities;
plugin->name[__lpl2_strcpy( plugin->name, payload->name, 9 )] = '\0';
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
} else {
if( err ) *err = LPL2_UNEXPECTED_ERROR( response->status );
return FALSE;
}
}

141
lib/libpl2/libpl2-emu.h Normal file
View File

@@ -0,0 +1,141 @@
#ifndef LIBPL2_EMU_
#define LIBPL2_EMU_
#include "libpl2-error.h"
#include "libpl2-version.h"
#include "libpl2-stddef.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! @defgroup page_emu Emulator Commands
*
* The functions on this page are handled directly by the emulator core. They
* will function even if the emulator core is extracted from Parallel Launcher
* and put into a normal RetroArch installation.
*
* @{
*/
/*! Graphics plugins */
typedef enum {
LPL2_PLUGIN_PARALLEL = 1, /*!< ParaLLEl */
LPL2_PLUGIN_GLIDEN64 = 2, /*!< GlideN64 */
LPL2_PLUGIN_OGRE = 3, /*!< OGRE */
LPL2_PLUGIN_GLIDE64 = 4, /*!< Glide64 */
LPL2_PLUGIN_ANGRYLION = 5, /*!< Angrylion */
LPL2_PLUGIN_RICE = 6, /*!< Rice */
LPL2_GLN64 = 7, /*!< gln64 */
} lpl2_gfx_plugin;
/*! Graphics plugin capabilities */
typedef enum {
/*! Indicates that the plugin is rendering at higher resolutions than an actual N64.
* @note The ParaLLEl plugin will have this bit set to 0 if its upscaling factor is set to 1
*/
LPL2_GFX_UPSCALING = 0x0001,
/*! Indicates that framebuffer emulation is currently enabled on this plugin */
LPL2_GFX_FRAMEBUFFER_EMULATION = 0x0002,
/*! Indicates that the plugin is currently handling depth comparisons accurately (ie. decals will work properly) */
LPL2_GFX_ACCURATE_DEPTH_COMPARE = 0x0004,
/*! Indicates that accurate LLE RSP emulation is being used
* @since LIBPL_ABI_VERSION_5
*/
LPL2_GFX_RSP_EMULATION = 0x0008,
/*! Indicates that the widescreen viewport hack is being used (GLideN64/OGRE)
* @since LIBPL_ABI_VERSION_6
*/
LPL2_WIDESCREEN_VIEWPORT = 0x0010,
} lpl2_gfx_capabilities;
/*! Cheat status flags */
typedef enum {
LPL2_USED_CHEATS = 0x01, /*!< Gameshark codes have been used **/
LPL2_USED_SAVESTATES = 0x02, /*!< A savestate has been loaded **/
LPL2_USED_SLOWDOWN = 0x04, /*!< Emulator slowdown has been used **/
LPL2_USED_FRAME_ADVANCE = 0x08, /*!< The emulation has been advanced in frame by frame mode **/
LPL2_USED_SPEEDUP = 0x10, /*!< The emulation has been sped up @since LIBPL_ABI_VERSION_4 **/
// convenience values for clearing flags
LPL2_ALL_CHEAT_FLAGS = 0xff, /*!< A mask that covers all flags */
LPL2_ALL_TAS_FLAGS = 0xfe, /*!< A mask that covers all flags except for @ref LPL2_USED_CHEATS */
LPL2_NO_CHEAT_FLAGS = 0x00 /*!< An empty mask with no flags set */
} lpl2_cheat_flags;
/*! A structure containing information about the current graphics plugin and its settings */
typedef struct {
lpl2_gfx_plugin pluginId;
lpl2_gfx_capabilities capabilities;
char name[10];
} lpl2_plugin_info;
/*! Gets the emulator core version
*
* @param[out] version A pointer to an @ref lpl2_version struct where the ParallelN64 emulator core version will be stored
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE on success, FALSE on an error.
*
* @exception LPL2_ERR_INVALID_ARGUMENTS version is NULL
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
*
* @see ::lpl2_get_launcher_version
*/
lpl2_bool lpl2_get_core_version( lpl2_version *version, lpl2_err *err ) __attribute__((access(write_only, 1), access(write_only, 2)));
/*! Check if any cheats or TAS tools have been used.
*
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return The current state of the cheat flags.
*
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
*
* @since LPL_ABI_VERSION_2
*/
lpl2_cheat_flags lpl2_get_cheat_flags( lpl2_err *err ) __attribute__((access(write_only, 1), warn_unused_result));
/*! Clears the specified cheat flags, meaning that the next call to @ref lpl2_get_graphics_plugin will no longer report this
* cheat as having been used. If the user is actively using cheats at the time this is called, the flags for the active cheats
* will not be cleared.
*
* @param[in] flags A bitmask of the cheat flags to clear
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return The new state of the cheat flags.
*
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
*
* @since LPL_ABI_VERSION_2
*/
lpl2_cheat_flags lpl2_clear_cheat_flags( lpl2_cheat_flags flags, lpl2_err *err ) __attribute__((access(write_only, 2)));
/*! Gets information about the current graphics plugin
* @param[out] plugin A pointer to a @ref lpl2_plugin_info struct where the plugin info will be stored
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE on success, FALSE on an error.
*
* @exception LPL2_ERR_INVALID_ARGUMENTS plugin is NULL
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
*/
lpl2_bool lpl2_get_graphics_plugin( lpl2_plugin_info *plugin, lpl2_err *err ) __attribute__((access(write_only, 1), access(write_only, 2)));
/*! @} */
#ifdef __cplusplus
}
#endif
#endif

59
lib/libpl2/libpl2-error.c Normal file
View File

@@ -0,0 +1,59 @@
#include "libpl2-error.h"
const char *lpl2_get_error_enum_string( lpl2_err error ) {
switch( error ) {
case LPL2_ERR_OKAY: return "LPL2_ERR_OKAY";
case LPL2_ERR_WAIT: return "LPL2_ERR_WAIT";
case LPL2_ERR_INVALID_ARGUMENTS: return "LPL2_ERR_INVALID_ARGUMENTS";
case LPL2_ERR_MISALIGNED_POINTER_ARG: return "LPL2_ERR_MISALIGNED_POINTER_ARG";
case LPL2_ERR_BROKEN_PIPE: return "LPL2_ERR_BROKEN_PIPE";
case LPL2_ERR_LIBPL_NOT_INITIALIZED: return "LPL2_ERR_LIBPL_NOT_INITIALIZED";
case LPL2_ERR_LIBPL_NOT_SUPPORTED: return "LPL2_ERR_LIBPL_NOT_SUPPORTED";
case LPL2_ERR_LIBPL_OLD_ABI: return "LPL2_ERR_LIBPL_OLD_ABI";
case LPL2_ERR_SD_CARD_ALREADY_LOADED: return "LPL2_ERR_SD_CARD_ALREADY_LOADED";
case LPL2_ERR_SD_CARD_ALREADY_EXISTS: return "LPL2_ERR_SD_CARD_ALREADY_EXISTS";
case LPL2_ERR_SD_CARD_CORRUPTED: return "LPL2_ERR_SD_CARD_CORRUPTED";
case LPL2_ERR_SD_CARD_CREATE_ERROR: return "LPL2_ERR_SD_CARD_CREATE_ERROR";
case LPL2_ERR_SD_CARD_NOT_FOUND: return "LPL2_ERR_SD_CARD_NOT_FOUND";
case LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED: return "LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED";
case LPL2_ERR_RHDC_NETWORK_ERROR: return "LPL2_ERR_RHDC_NETWORK_ERROR";
case LPL2_ERR_RHDC_RATE_LIMIT: return "LPL2_ERR_RHDC_RATE_LIMIT";
case LPL2_ERR_RHDC_AVATAR_NOT_FOUND: return "LPL2_ERR_RHDC_AVATAR_NOT_FOUND";
case LPL2_ERR_RHDC_AVATAR_INVALID: return "LPL2_ERR_RHDC_AVATAR_INVALID";
default: return "LPL2_ERR_UNKNOWN";
}
}
const char *lpl2_get_error_description( lpl2_err error ) {
switch( error ) {
case LPL2_ERR_OKAY: return "No error";
case LPL2_ERR_WAIT: return "Waiting on async result (not an error)";
case LPL2_ERR_INVALID_ARGUMENTS: return "One or more arguments passed to the function are invalid";
case LPL2_ERR_MISALIGNED_POINTER_ARG: return "One or more pointer arguments passed to the function has incorrect alignment";
case LPL2_ERR_BROKEN_PIPE: return "The connection to Parallel Launcher has been lost";
case LPL2_ERR_LIBPL_NOT_INITIALIZED: return "lpl2_init has not been called yet";
case LPL2_ERR_LIBPL_NOT_SUPPORTED: return "The emulator does not support libpl";
case LPL2_ERR_LIBPL_OLD_ABI: return "The function you are trying to use is from a newer ABI than the one you provied in your lpl2_init call";
case LPL2_ERR_SD_CARD_ALREADY_LOADED: return "An SD card has already been loaded";
case LPL2_ERR_SD_CARD_ALREADY_EXISTS: return "An SD card with the given uid already exists";
case LPL2_ERR_SD_CARD_CORRUPTED: return "The emulator failed to load the SD card image";
case LPL2_ERR_SD_CARD_CREATE_ERROR: return "An unknown error occurred creating the SD card image";
case LPL2_ERR_SD_CARD_NOT_FOUND: return "An SD card with the given uid does not exist";
case LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED: return "The user has not enabled RHDC integration";
case LPL2_ERR_RHDC_NETWORK_ERROR: return "A network error occurred while calling RHDC";
case LPL2_ERR_RHDC_RATE_LIMIT: return "The emulator refused to run this command because you are making too many requests to RHDC too quickly";
case LPL2_ERR_RHDC_AVATAR_NOT_FOUND: return "The user either does not exist or does not have an avatar";
case LPL2_ERR_RHDC_AVATAR_INVALID: return "An image processing error occurred";
default: return "UNKNOWN: Error code does not match a known libpl2 error";
}
}

62
lib/libpl2/libpl2-error.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef LIBPL2_ERROR_
#define LIBPL2_ERROR_
#ifdef __cplusplus
extern "C" {
#endif
/*! @defgroup page_error_codes Error Codes
*
* @{
*/
/*! libpl2 error codes
* Almost all libpl2 functions take in an optional err pointer. When a non-NULL pointer is passed in, its value will be set to one
* of the documented error codes.
*/
typedef enum {
LPL2_ERR_OKAY = 0x0000, /*!< No error */
LPL2_ERR_WAIT = 0x0001, /*!< Waiting on async result (not an error) */
LPL2_ERR_INVALID_ARGUMENTS = 0x0101, /*!< One or more arguments passed to the function are invalid */
LPL2_ERR_MISALIGNED_POINTER_ARG = 0x0102, /*!< One or more pointer arguments passed to the function has incorrect alignment */
LPL2_ERR_BROKEN_PIPE = 0x0103, /*!< The connection to Parallel Launcher has been lost */
LPL2_ERR_LIBPL_NOT_INITIALIZED = 0x0301, /*!< @ref lpl2_init has not been called yet */
LPL2_ERR_LIBPL_NOT_SUPPORTED = 0x0302, /*!< The emulator does not support libpl */
LPL2_ERR_LIBPL_OLD_ABI = 0x0303, /*!< The function you are trying to use is from a newer ABI than the one you provied in your @ref lpl2_init call */
LPL2_ERR_SD_CARD_ALREADY_LOADED = 0x0401, /*!< An SD card has already been loaded */
LPL2_ERR_SD_CARD_ALREADY_EXISTS = 0x0402, /*!< An SD card with the given uid already exists */
LPL2_ERR_SD_CARD_CORRUPTED = 0x0403, /*!< The emulator failed to load the SD card image */
LPL2_ERR_SD_CARD_NOT_FOUND = 0x0404, /*!< An SD card with the given uid does not exist */
LPL2_ERR_SD_CARD_CREATE_ERROR = 0x0405, /*!< An unknown error occurred creating the SD card image */
LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED = 0x0501, /*!< The user has not enabled RHDC integration */
LPL2_ERR_RHDC_NETWORK_ERROR = 0x0502, /*!< A network error occurred while calling RHDC */
LPL2_ERR_RHDC_RATE_LIMIT = 0x0503, /*!< The emulator refused to run this command because you are making too many requests to RHDC too quickly */
LPL2_ERR_RHDC_AVATAR_NOT_FOUND = 0x0504, /*!< The user either does not exist or does not have an avatar */
LPL2_ERR_RHDC_AVATAR_INVALID = 0x0505, /*!< An image processing error occurred */
} lpl2_err;
/*! Returns a string containing the name of the enum value for the given error code. Useful for printing debug messages.
*
* @param error An error code set by libpl2 function
* @return A pointer to a string in static memory containing the enum name (or LPL2_ERR_UNKNOWN for an unknown error code)
*/
const char *lpl2_get_error_enum_string( lpl2_err error ) __attribute__((pure, warn_unused_result, returns_nonnull));
/*! Returns a string containing a description of what the given error code means. Useful for printing debug messages.
*
* @param error An error code set by libpl2 function
* @return A pointer to a string in static memory describing what the error code means
*/
const char *lpl2_get_error_description( lpl2_err error ) __attribute__((pure, warn_unused_result, returns_nonnull));
/*! @} */
#ifdef __cplusplus
}
#endif
#endif

82
lib/libpl2/libpl2-init.c Normal file
View File

@@ -0,0 +1,82 @@
#include "libpl2-init.h"
#ifdef LIBDRAGON
#include <libdragon.h>
#else
#include <ultra64.h>
#endif
#include "libpl2-internal.h"
#include "libpl2-launcher.h"
#include "libpl2-emu.h"
struct lpl_abi_requirements {
lpl2_version coreVersion;
lpl2_version launcherVersion;
};
static const struct lpl_abi_requirements sAbiReqs[] = {
{ { 2, 13, 0 }, { 0, 0, 0 } }, // LIBPL_ABI_VERSION_2
{ { 2, 13, 0 }, { 6, 20, 0 } }, // LIBPL_ABI_VERSION_3
{ { 2, 14, 3 }, { 6, 22, 0 } }, // LIBPL_ABI_VERSION_4
{ { 2, 15, 0 }, { 6, 22, 0 } }, // LIBPL_ABI_VERSION_5
{ { 2, 16, 0 }, { 6, 22, 0 } }, // LIBPL_ABI_VERSION_6
{ { 2, 16, 0 }, { 7, 10, 1 } }, // LIBPL_ABI_VERSION_7
};
lpl2_bool lpl2_init( libpl_abi_version minimumAbiVersion, lpl2_err *err ) {
if( __libpl2_status == __LPL2_INITIALIZED && minimumAbiVersion <= __libpl2_abi ) {
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
}
__libpl2_status = __LPL2_NOT_SUPPORTED;
if( minimumAbiVersion < LIBPL_ABI_VERSION_1 || minimumAbiVersion > LIBPL_ABI_VERSION_CURRENT ) {
if( err ) *err = LPL2_ERR_INVALID_ARGUMENTS;
return FALSE;
}
__libpl2_abi = (int)minimumAbiVersion;
if( minimumAbiVersion == LIBPL_ABI_VERSION_1 ) {
__libpl2_status = __LPL2_INITIALIZED;
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
}
#ifdef LIBDRAGON
io_write( 0x1ffb0000u, 0u );
const unsigned int response = io_read( 0x1ffb0000u );
#else
unsigned int response = 0;
osPiWriteIo( 0x1ffb0000u, 0u );
osPiReadIo( 0x1ffb0000u, &response );
#endif
if( response != 0x00500000u ) {
if( err ) *err = LPL2_ERR_LIBPL_NOT_SUPPORTED;
return FALSE;
}
lpl2_version version;
__libpl2_status = __LPL2_INITIALIZED;
if( !lpl2_get_core_version( &version, NULL ) || lpl2_compare_versions( &version, &sAbiReqs[(int)minimumAbiVersion - 2].coreVersion ) < 0 ) {
__libpl2_status = __LPL2_NOT_SUPPORTED;
if( err ) *err = LPL2_ERR_LIBPL_OLD_ABI;
return FALSE;
}
if( minimumAbiVersion == LIBPL_ABI_VERSION_2 ) {
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
}
lpl2_err plErr;
if( !lpl2_get_launcher_version( &version, &plErr ) || lpl2_compare_versions( &version, &sAbiReqs[(int)minimumAbiVersion - 2].launcherVersion ) < 0 ) {
__libpl2_status = __LPL2_NOT_SUPPORTED;
if( err ) *err = (plErr == LPL2_ERR_BROKEN_PIPE) ? LPL2_ERR_BROKEN_PIPE : LPL2_ERR_LIBPL_OLD_ABI;
return FALSE;
}
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
}

49
lib/libpl2/libpl2-init.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef LIBPL2_INIT_
#define LIBPL2_INIT_
#include "libpl2-stddef.h"
#include "libpl2-error.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! @defgroup page_init Initialization
*
* @{
*/
/*! libpl ABI versions */
typedef enum {
LIBPL_ABI_VERSION_1 = 1, /*!< initial libpl prototype (2023-07-17) */
LIBPL_ABI_VERSION_2 = 2, /*!< revised prototype (2023-08-1) */
LIBPL_ABI_VERSION_3 = 3, /*!< first advertised public release (2023-08-1) */
LIBPL_ABI_VERSION_4 = 4, /*!< SD card update (2023-10-03) */
LIBPL_ABI_VERSION_5 = 5, /*!< LLE RSP check (2024-02-16) */
LIBPL_ABI_VERSION_6 = 6, /*!< Widescreen viewport check (2024-03-05) */
LIBPL_ABI_VERSION_7 = 7, /*!< Variable size RHDC avatar fetching (2025-01-04) */
LIBPL_ABI_VERSION_CURRENT = LIBPL_ABI_VERSION_7 /*!< the latest version */
} libpl_abi_version;
/*! Initializes libpl and checks if it is supported by the emulator.
* This function should be called before any other libpl function.
*
* @param[in] minimumAbiVersion The required libpl ABI version
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE if the emulator supports the requested version of libpl or later
*
* @exception LPL2_ERR_INVALID_ARGUMENTS minimumAbiVersion is invalid
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI the emulator does not support the requested ABI version. You may try calling lpl2_init again with a lower ABI version.
* @exception LPL2_ERR_BROKEN_PIPE the emulator core appears to support libpl, however, it failed to establish a connection with Parallel Launcher
*/
lpl2_bool lpl2_init( libpl_abi_version minimumAbiVersion, lpl2_err *err ) __attribute__((access(write_only, 2)));
/*! @} */
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,52 @@
#include "libpl2-internal.h"
enum __libpl2_status_t __libpl2_status = __LPL2_INITIALIZED;
unsigned int __libpl2_abi = 0;
typedef struct {
unsigned short commandId;
unsigned short payloadSize;
char payload[];
} __lpl2_request;
static volatile __lpl2_request *g_request = (volatile __lpl2_request*)0xbffb0000u;
unsigned short __lpl2_strcpy( volatile char *dest, volatile const char *src, unsigned short maxChars ) {
unsigned short len = 0;
while( *src != '\0' && len < maxChars ) {
dest[len++] = *(src++);
}
return len;
}
volatile const __lpl2_response *__lpl2_query_basic( unsigned short commandId ) {
g_request->payloadSize = 0;
g_request->commandId = commandId;
return (volatile __lpl2_response*)g_request;
}
volatile const __lpl2_response *__lpl2_query_string( unsigned short commandId, const char *arg ) {
g_request->payloadSize = __lpl2_strcpy( (char*)g_request->payload, arg, 0xFFFC );
g_request->commandId = commandId;
return (volatile __lpl2_response*)g_request;
}
volatile const __lpl2_response *__lpl2_query_byte( unsigned short commandId, unsigned char arg ) {
g_request->payloadSize = 1;
g_request->payload[0] = (char)arg;
g_request->commandId = commandId;
return (volatile __lpl2_response*)g_request;
}
volatile const __lpl2_response *__lpl2_query_uint( unsigned short commandId, unsigned int arg ) {
g_request->payloadSize = 4;
*((volatile unsigned int*)g_request->payload) = arg;
g_request->commandId = commandId;
return (volatile __lpl2_response*)g_request;
}
volatile const __lpl2_response *__lpl2_payload_send( unsigned short commandId, unsigned short payloadSize ) {
g_request->payloadSize = payloadSize;
g_request->commandId = commandId;
return (volatile __lpl2_response*)g_request;
}

View File

@@ -0,0 +1,66 @@
#ifndef LIBPL2_INTERNAL_
#define LIBPL2_INTERNAL_
#include "libpl2-error.h"
#include "libpl2-stddef.h"
#ifdef __cplusplus
extern "C" {
#endif
extern enum __libpl2_status_t {
__LPL2_NOT_INITIALIZED = 2,
__LPL2_NOT_SUPPORTED = 1,
__LPL2_INITIALIZED = 0
} __libpl2_status;
extern unsigned int __libpl2_abi;
#define LPL2_VERIFY_INITIALIZED( errPtr, returnValue ) \
if( __libpl2_status != __LPL2_INITIALIZED ) { \
if( errPtr ) *errPtr = (__libpl2_status == __LPL2_NOT_INITIALIZED) ? LPL2_ERR_LIBPL_NOT_INITIALIZED : LPL2_ERR_LIBPL_NOT_SUPPORTED; \
return returnValue; \
}
#define LPL2_VERIFY_ABI( minAbiVersion, errPtr, returnValue ) \
if( __libpl2_abi < minAbiVersion ) { \
if( errPtr ) *errPtr = LPL2_ERR_LIBPL_OLD_ABI; \
return returnValue; \
}
#define LPL2_VERIFY_NOTNULL( arg, errPtr, returnValue ) \
if( arg == NULL ) { \
if( errPtr ) *errPtr = LPL2_ERR_INVALID_ARGUMENTS; \
return returnValue; \
}
#define LPL2_UNEXPECTED_ERROR( status ) ((lpl2_err)((unsigned short)0x8000 | (unsigned short)status))
typedef struct {
union {
struct {
unsigned char protocolStatus;
unsigned char commandStatus;
};
unsigned short status;
};
unsigned short payloadSize;
char payload[];
} __attribute__((aligned(8))) __lpl2_response;
// Copies a string up to but NOT INCLUDING the null terminator or until maxChars are copied
unsigned short __lpl2_strcpy( volatile char *dest, volatile const char *src, unsigned short maxChars ) __attribute__((nonnull(1,2)));
volatile const __lpl2_response *__lpl2_query_basic( unsigned short commandId ) __attribute__((returns_nonnull));
volatile const __lpl2_response *__lpl2_query_string( unsigned short commandId, const char *arg ) __attribute__((returns_nonnull, nonnull(2)));
volatile const __lpl2_response *__lpl2_query_byte( unsigned short commandId, unsigned char arg ) __attribute__((returns_nonnull));
volatile const __lpl2_response *__lpl2_query_uint( unsigned short commandId, unsigned int arg ) __attribute__((returns_nonnull));
inline __attribute__((always_inline, assume_aligned(4), const)) volatile char *__lpl2_payload_start() { return (volatile char*)0xbffb0004u; }
volatile const __lpl2_response *__lpl2_payload_send( unsigned short commandId, unsigned short payloadSize ) __attribute__((returns_nonnull));
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,152 @@
#include "libpl2-launcher.h"
#include "libpl2-internal.h"
typedef struct {
unsigned int size;
unsigned char format;
char name[];
} sdcard_internal_t;
lpl2_bool lpl2_get_launcher_version( lpl2_version *version, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_ABI( 3, err, FALSE )
LPL2_VERIFY_NOTNULL( version, err, FALSE )
volatile const __lpl2_response *response = __lpl2_query_basic( 0x0100 );
volatile const lpl2_version *payload = (volatile const lpl2_version*)response->payload;
if( response->status == 0 ) {
version->major = payload->major;
version->minor = payload->minor;
version->patch = payload->patch;
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
} else {
version->major = version->minor = version->patch = 0;
if( err ) {
switch( response->protocolStatus ) {
case 3: case 4: *err = LPL2_ERR_BROKEN_PIPE; break;
default: *err = LPL2_UNEXPECTED_ERROR( response->status ); break;
}
}
return FALSE;
}
}
lpl2_bool lpl2_get_sd_card_info( const char *uid, lpl2_sd_card_info *info, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_ABI( 4, err, FALSE )
LPL2_VERIFY_NOTNULL( uid, err, FALSE )
LPL2_VERIFY_NOTNULL( info, err, FALSE )
volatile const __lpl2_response *response = __lpl2_query_string( 0x0101, uid );
volatile const sdcard_internal_t *payload = (volatile const sdcard_internal_t*)response->payload;
if( response->status == 0 ) {
info->size = payload->size;
info->format = (lpl2_sd_format)payload->format;
info->name[__lpl2_strcpy( info->name, payload->name, response->payloadSize - 5 )] = '\0';
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
} else if( err ) {
switch( response->status ) {
case 1: *err = LPL2_ERR_SD_CARD_CORRUPTED; break;
case 4: *err = LPL2_ERR_SD_CARD_NOT_FOUND; break;
case 0x200: case 0x600: *err = LPL2_ERR_INVALID_ARGUMENTS; break;
case 0x300: case 0x400: *err = LPL2_ERR_BROKEN_PIPE; break;
default: *err = LPL2_UNEXPECTED_ERROR( response->status ); break;
}
}
return FALSE;
}
lpl2_bool lpl2_create_sd_card( const char *uid, const char *name, lpl2_sd_format format, unsigned char sizeMiB, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_ABI( 4, err, FALSE )
LPL2_VERIFY_NOTNULL( uid, err, FALSE )
LPL2_VERIFY_NOTNULL( name, err, FALSE )
volatile char *requestPayload = __lpl2_payload_start();
requestPayload[0] = (char)sizeMiB;
requestPayload[1] = (char)format;
const unsigned short nameLen = __lpl2_strcpy( &requestPayload[2], name, 12 );
if( nameLen == 0 || nameLen > 11 ) {
if( err ) *err = LPL2_ERR_INVALID_ARGUMENTS;
return FALSE;
}
requestPayload[nameLen + 2] = '\0';
const unsigned short uidLen = __lpl2_strcpy( &requestPayload[nameLen + 3], uid, 37 );
if( uidLen == 0 || uidLen > 36 ) {
if( err ) *err = LPL2_ERR_INVALID_ARGUMENTS;
return FALSE;
}
requestPayload[nameLen + uidLen + 3] = '\0';
volatile const __lpl2_response *response = __lpl2_payload_send( 0x0102, nameLen + uidLen + 4 );
if( response->status == 0 ) {
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
} else if( err ) {
switch( response->status ) {
case 1: *err = LPL2_ERR_SD_CARD_ALREADY_LOADED; break;
case 2: *err = LPL2_ERR_SD_CARD_CORRUPTED; break;
case 3: *err = LPL2_ERR_SD_CARD_CREATE_ERROR; break;
case 5: case 6: *err = LPL2_ERR_SD_CARD_ALREADY_EXISTS; break;
case 0x200: case 0x600: *err = LPL2_ERR_INVALID_ARGUMENTS; break;
case 0x300: case 0x400: *err = LPL2_ERR_BROKEN_PIPE; break;
default: *err = LPL2_UNEXPECTED_ERROR( response->status ); break;
}
}
return FALSE;
}
lpl2_bool lpl2_create_auto_sd_card( lpl2_sd_format format, unsigned char sizeMiB, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_ABI( 4, err, FALSE )
volatile char *requestPayload = __lpl2_payload_start();
requestPayload[0] = (char)sizeMiB;
requestPayload[1] = (char)format;
volatile const __lpl2_response *response = __lpl2_payload_send( 0x0103, 2 );
if( response->status == 0 ) {
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
} else if( err ) {
switch( response->status ) {
case 1: *err = LPL2_ERR_SD_CARD_ALREADY_LOADED; break;
case 2: *err = LPL2_ERR_SD_CARD_CORRUPTED; break;
case 3: *err = LPL2_ERR_SD_CARD_CREATE_ERROR; break;
case 0x600: *err = LPL2_ERR_INVALID_ARGUMENTS; break;
case 0x300: case 0x400: *err = LPL2_ERR_BROKEN_PIPE; break;
default: *err = LPL2_UNEXPECTED_ERROR( response->status ); break;
}
}
return FALSE;
}
lpl2_bool lpl2_load_sd_card( const char *uid, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_ABI( 4, err, FALSE )
LPL2_VERIFY_NOTNULL( uid, err, FALSE )
volatile const __lpl2_response *response = __lpl2_query_string( 0x0104, uid );
if( response->status == 0 ) {
if( err ) *err = LPL2_ERR_OKAY;
return TRUE;
} else if( err ) {
switch( response->status ) {
case 1: *err = LPL2_ERR_SD_CARD_ALREADY_LOADED; break;
case 2: *err = LPL2_ERR_SD_CARD_CORRUPTED; break;
case 4: *err = LPL2_ERR_SD_CARD_NOT_FOUND; break;
case 0x200: case 0x600: *err = LPL2_ERR_INVALID_ARGUMENTS; break;
case 0x300: case 0x400: *err = LPL2_ERR_BROKEN_PIPE; break;
default: *err = LPL2_UNEXPECTED_ERROR( response->status ); break;
}
}
return FALSE;
}

View File

@@ -0,0 +1,153 @@
#ifndef LIBPL2_LAUNCHER_
#define LIBPL2_LAUNCHER_
#include "libpl2-error.h"
#include "libpl2-version.h"
#include "libpl2-stddef.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! @defgroup page_launcher Parallel Launcher Commands
* @{
*/
/*! SD card formats */
typedef enum __attribute__((packed)) {
LPL2_SD_UNFORMATTED = 0, /*!< Unformatted */
LPL2_SD_FAT12 = 12, /*!< FAT-12 */
LPL2_SD_FAT16 = 16, /*!< FAT-16 */
LPL2_SD_FAT32 = 32, /*!< FAT-32 */
} lpl2_sd_format;
/*! SD card info */
typedef struct {
unsigned int size; /*!< The size of the SD card in bytes */
lpl2_sd_format format; /*!< The filesystem format of the SD card */
char name[12]; /*< The name of the SD card */
} lpl2_sd_card_info;
/*! Gets the emulator core version
*
* @param[out] version A pointer to an @ref lpl2_version struct where the Parallel Launcher version will be stored
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE on success, FALSE on an error.
*
* @exception LPL2_ERR_INVALID_ARGUMENTS version is NULL
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
*
* @see ::lpl2_get_core_version
*/
lpl2_bool lpl2_get_launcher_version( lpl2_version *version, lpl2_err *err ) __attribute__((access(write_only, 1), access(write_only, 2)));
/*! Fetches info about an SD card created with @ref lpl2_create_sd_card. This can be used to check if a shared SD card exists and
* has the expected size and format before loading it.
*
* @param[in] uid A unique string identifying the shared SD card (max 36 characters)
* @param[out] info A pointer to a struct where the SD card information will be stored
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE on success, FALSE on an error.
*
* @exception LPL2_ERR_INVALID_ARGUMENTS info or uid is NULL or uid is empty or too long
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
* @exception LPL2_ERR_SD_CARD_NOT_FOUND no SD card with the given uid was found
* @exception LPL2_ERR_SD_CARD_CORRUPTED an SD card with the uid exists, but the file could not be accessed
*
* @since LPL_ABI_VERSION_4
*/
lpl2_bool lpl2_get_sd_card_info( const char *uid, lpl2_sd_card_info *info, lpl2_err *err ) __attribute__((access(write_only, 2), access(write_only, 3)));
/*! Creates a new SD card and loads it. This function can only be used to create an SD card when the user has not already selected
* one in Parallel Launcher. If the user has already loaded an SD card, this function will do nothing and return FALSE.
*
* Unlike @ref lpl2_create_auto_sd_card, this function requires you to provide a unique identifier for the SD card. Any other
* ROM can request this card by passing the same uid to @ref lpl2_load_sd_card. This allows collaborating romhacks to share a
* common SD card.
*
* @param[in] uid A unique string that the shared SD card can be identified with (max 36 characters)
* @param[in] name The filename of the SD card and the volume name for FAT formatted cards (max 11 characters)
* @param[in] format The format of the SD card
* @param[in] sizeMiB The desired size of the SD card in MiB (2^20 bytes). \n
* For FAT12, sizeMiB must be between 1 and 32 \n
* For FAT16 and FAT32, sizeMiB must be between 32 and 255
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE if an SD card was created and loaded; FALSE otherwise
*
* @exception LPL2_ERR_INVALID_ARGUMENTS info or uid is NULL, empty, or too long, format is invalid, or sizeMiB is out of range
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
* @exception LPL2_ERR_SD_CARD_ALREADY_LOADED an SD card has already been loaded
* @exception LPL2_ERR_SD_CARD_ALREADY_EXISTS an SD card with this uid or name already exists
* @exception LPL2_ERR_SD_CARD_CREATE_ERROR an unknown error occurred when attempting to create the SD card image
* @exception LPL2_ERR_SD_CARD_CORRUPTED the SD card was created, but then somehow could not be accessed
*
* @since LPL_ABI_VERSION_4
*/
lpl2_bool lpl2_create_sd_card( const char *uid, const char *name, lpl2_sd_format format, unsigned char sizeMiB, lpl2_err *err ) __attribute__((access(write_only, 5)));
/*! Creates a new SD card and loads it. This function can only be used to create an SD card when the user has not already selected
* one in Parallel Launcher. If the user has already loaded an SD card, this function will do nothing and return FALSE.
*
* @param[in] format The format of the SD card
* @param[in] sizeMiB The desired size of the SD card in MiB (2^20 bytes) \n
* For FAT12, sizeMiB must be between 1 and 32 \n
* For FAT16 and FAT32, sizeMiB must be between 32 and 255
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE if an SD card was created and loaded; FALSE otherwise
*
* @exception LPL2_ERR_INVALID_ARGUMENTS format is invalid or sizeMiB is out of range
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
* @exception LPL2_ERR_SD_CARD_ALREADY_LOADED an SD card has already been loaded
* @exception LPL2_ERR_SD_CARD_CREATE_ERROR an unknown error occurred when attempting to create the SD card image
* @exception LPL2_ERR_SD_CARD_CORRUPTED the SD card was created, but then somehow could not be accessed
*
* @since LPL_ABI_VERSION_4
*/
lpl2_bool lpl2_create_auto_sd_card( lpl2_sd_format format, unsigned char sizeMiB, lpl2_err *err ) __attribute__((access(write_only, 3)));
/*! Loads an SD card that was created with @ref lpl2_create_sd_card. This function can only be used to load an SD card when the
* the user has not already selected one in Parallel Launcher. If the user has already loaded an SD card, this function will do
* nothing and return FALSE.
*
* @param[in] uid A unique string identifying the shared SD card (max 36 characters)
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE if an SD card was loaded; FALSE otherwise
*
* @exception LPL2_ERR_INVALID_ARGUMENTS uid is NULL, empty, or too long
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
* @exception LPL2_ERR_SD_CARD_NOT_FOUND no SD card with the given uid was found
* @exception LPL2_ERR_SD_CARD_ALREADY_LOADED an SD card has already been loaded
* @exception LPL2_ERR_SD_CARD_CORRUPTED the SD card exists, but could not be read
*
* @note It would be wise to call @ref lpl2_get_sd_card_info first, to verify that the SD card has the expected size and format
*
* @since LPL_ABI_VERSION_4
*/
lpl2_bool lpl2_load_sd_card( const char *uid, lpl2_err *err ) __attribute__((access(write_only, 2)));
/*! @} */
#ifdef __cplusplus
}
#endif
#endif

70
lib/libpl2/libpl2-rhdc.c Normal file
View File

@@ -0,0 +1,70 @@
#include "libpl2-rhdc.h"
#include "libpl2-internal.h"
unsigned short lpl2_get_my_rhdc_username( char *username, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, 0 )
LPL2_VERIFY_ABI( 3, err, 0 )
LPL2_VERIFY_NOTNULL( username, err, 0 )
volatile const __lpl2_response *response = __lpl2_query_basic( 0x0200 );
if( response->status == 0 && response->payloadSize <= 30 ) {
if( err ) *err = LPL2_ERR_OKAY;
const unsigned short len = __lpl2_strcpy( username, response->payload, 30 );
username[len] = '\0';
return len;
} else if( err ) {
switch( response->status ) {
case 1: *err = LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED; break;
case 0x300: case 0x400: *err = LPL2_ERR_BROKEN_PIPE; break;
default: *err = LPL2_UNEXPECTED_ERROR( response->status ); break;
}
}
return 0;
}
lpl2_bool lpl2_get_rhdc_avatar_async( const char *username, lpl2_avatar_options options, void *avatar, lpl2_err *err ) {
LPL2_VERIFY_INITIALIZED( err, FALSE )
LPL2_VERIFY_ABI( 7, err, FALSE )
LPL2_VERIFY_NOTNULL( username, err, FALSE )
LPL2_VERIFY_NOTNULL( avatar, err, FALSE )
if( ((unsigned int)avatar & 7) != 0 ) {
if( err ) *err = LPL2_ERR_MISALIGNED_POINTER_ARG;
return FALSE;
}
avatar = __builtin_assume_aligned( avatar, 8 );
volatile char *requestPayload = __lpl2_payload_start();
*((volatile unsigned int*)requestPayload) = (unsigned int)options;
const unsigned short usernameLen = __lpl2_strcpy( &requestPayload[4], username, 31 );
if( usernameLen == 0 || usernameLen > 30 ) {
if( err ) *err = LPL2_ERR_INVALID_ARGUMENTS;
return FALSE;
}
volatile const __lpl2_response *response = __lpl2_payload_send( 0x0203, usernameLen + 4 );
volatile const int *tempAvatar = (volatile const int*)__builtin_assume_aligned( (const void*)response->payload, 4 );
if( response->status == 0 ) {
if( err ) *err = LPL2_ERR_OKAY;
for( unsigned short i = 0; i < (response->payloadSize >> 2); i++ ) {
((unsigned int*)avatar)[i] = tempAvatar[i];
}
return TRUE;
} else if( err ) {
switch( response->status ) {
case 0x500: *err = LPL2_ERR_WAIT; break;
case 1: *err = LPL2_ERR_RHDC_AVATAR_NOT_FOUND; break;
case 2: *err = LPL2_ERR_RHDC_NETWORK_ERROR; break;
case 3: *err = LPL2_ERR_RHDC_AVATAR_INVALID; break;
case 4: *err = LPL2_ERR_RHDC_RATE_LIMIT; break;
case 0x200: case 0x201: case 0x600: *err = LPL2_ERR_INVALID_ARGUMENTS; break;
case 0x300: case 0x400: *err = LPL2_ERR_BROKEN_PIPE; break;
default: *err = LPL2_UNEXPECTED_ERROR( response->status ); break;
}
}
return FALSE;
}

179
lib/libpl2/libpl2-rhdc.h Normal file
View File

@@ -0,0 +1,179 @@
#ifndef LIBPL2_RHDC_
#define LIBPL2_RHDC_
#include "libpl2-stddef.h"
#include "libpl2-error.h"
#include "libpl2-texture.h"
/*! @defgroup page_rhdc Romhacking.com Commands
* @{
*/
/*! Convenience struct for storing a 16x16 RHDC avatar in RGBA16 format. Alias of @ref lpl2_texture_16x16_rgba16 */
typedef lpl2_texture_16x16_rgba16 lpl2_avatar_16x16_rgba16;
/*! Convenience struct for storing a 32x32 RHDC avatar in RGBA16 format. Alias of @ref lpl2_texture_32x32_rgba16 */
typedef lpl2_texture_32x32_rgba16 lpl2_avatar_32x32_rgba16;
/*! Convenience struct for storing a 64x64 RHDC avatar in RGBA16 format. */
typedef struct { lpl2_texture_64x32_rgba16 textures[2]; } lpl2_avatar_64x64_rgba16 __attribute__((aligned(8)));
/*! Convenience struct for storing a 16x16 RHDC avatar in RGBA32 format. Alias of @ref lpl2_texture_16x16_rgba32 */
typedef lpl2_texture_16x16_rgba32 lpl2_avatar_16x16_rgba32;
/*! Convenience struct for storing a 32x32 RHDC avatar in RGBA32 format. Alias of @ref lpl2_texture_32x32_rgba32 */
typedef lpl2_texture_32x32_rgba32 lpl2_avatar_32x32_rgba32;
/*! Convenience struct for storing a 64x64 RHDC avatar in RGBA32 format. */
typedef struct { lpl2_texture_64x16_rgba32 textures[4]; } lpl2_avatar_64x64_rgba32 __attribute__((aligned(8)));
#ifdef __cplusplus
extern "C" {
#endif
/*! Image processing options for the avatar fetching calls */
typedef enum {
LPL2_AVATAR_16x16 = 0x10000000, /*!< Size Flag. Return a 16x16 image */
LPL2_AVATAR_32x32 = 0x20000000, /*!< Size Flag. Return a 32x32 image */
LPL2_AVATAR_64x64 = 0x40000000, /*!< Size Flag. Return a 64x64 image */
LPL2_AVATAR_RGBA16 = 0x100000, /*!< Format Flag. Return an image in RGBA16 (RGBA5551) format */
LPL2_AVATAR_RGBA32 = 0x200000, /*!< Format Flag. Return an image in RGBA32 (RGBA8888) format */
/*! Optional Flag.
* If this flag is provided, Floyd-Steinberg dithering will be used when converting the avatar to the RGBA16 format.
* This flag has no effect if the LPL2_AVATAR_RGBA32 flag is also provided.
*/
LPL2_AVATAR_DITHER = 0x0001,
/*! Optional Flag.
* If this flag is provided, the avatar will be resized to the requested size without using any filtering.
* Otherwise, if this flag is not provided, the avatar will be scaled using bilinear filtering.
*/
LPL2_AVATAR_NO_FILTERING = 0x0002,
/*! Optional Flag.
* If this flag is provided, any alpha transparency in the image will be removed and replaced with black.
*/
LPL2_AVATAR_NO_ALPHA = 0x0004,
/*! Optional Flag.
* If this flag is provided, an avatar that is not square will be zoomed in and the sides cropped to fit in a square box.
* Otherwise, if this flag is not provided, the avatar will be given a border of transparent (or black if the
* LPL2_AVATAR_NO_ALPHA flag is provided) pixels on the sides.
*/
LPL2_AVATAR_ZOOM = 0x0008,
} lpl2_avatar_options;
/*! Gets the username of the current RHDC user
* @note RHDC usernames are at most 30 characters long as may contain alphanumeric ASCII characters, underscores, and hyphens
*
* @param[out] username A buffer at least 31 bytes large where the username will be stored
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return Returns the length of the username in bytes (not including the null terminator), or 0 on an error
*
* @exception LPL2_ERR_INVALID_ARGUMENTS username is NULL or not a valid RHDC username
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
* @exception LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED the user has not enabled RHDC integration
*
* @since LPL_ABI_VERSION_3
*/
unsigned short lpl2_get_my_rhdc_username( char *username, lpl2_err *err ) __attribute__((access(write_only, 1), access(write_only, 2)));
/*! Asynchronously fetches the avatar for the given username in RHDC as a square image and stores it at the provided memory
* address. If the operation failed or the user is not found, the provided memory buffer is not modified.
*
* @param[in] username A null-terminated string containing the username or userId
* @param[in] options Flags for how the avatar should be processed. You must include a Size Flag and a Format Flag.
* @param[out] avatar A buffer that the avatar will be written to. It is strongly recommended that you pass in a pointer to the
* appropriate lpl2_avatar_* type, though you may also pass in an 8-byte-aligned pointer to an arbitrary buffer of the appropriate
* size. The expected types and sizes are listed below:
* - @ref lpl2_avatar_16x16_rgba16 (512 bytes) for LPL2_AVATAR_16x16 | LPL2_AVATAR_RGBA16
* - @ref lpl2_avatar_16x16_rgba32 (1024 bytes) for LPL2_AVATAR_16x16 | LPL2_AVATAR_RGBA32
* - @ref lpl2_avatar_32x32_rgba16 (2048 bytes) for LPL2_AVATAR_32x32 | LPL2_AVATAR_RGBA16
* - @ref lpl2_avatar_32x32_rgba32 (4096 bytes) for LPL2_AVATAR_32x32 | LPL2_AVATAR_RGBA32
* - @ref lpl2_avatar_64x64_rgba16 (8192 bytes) for LPL2_AVATAR_64x64 | LPL2_AVATAR_RGBA16 (contains 2 textures)
* - @ref lpl2_avatar_64x64_rgba32 (16384 bytes) for LPL2_AVATAR_64x64 | LPL2_AVATAR_RGBA32 (contains 4 textures)
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL;
* however, you likely want to at least check for an error code of LPL2_ERR_WAIT.
* @return TRUE if the request has completed and the avatar has been copied. FALSE on an error or if the avatar has not been fully
* downloaded yet.
*
* @exception LPL2_ERR_WAIT the web request to RHDC has not completed yet
* @exception LPL2_ERR_INVALID_ARGUMENTS username is NULL or not a valid RHDC username, avatar is NULL, or the options are invalid
* @exception LPL2_ERR_MISALIGNED_POINTER_ARG avatar is not aligned to an 8-byte boundary
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
* @exception LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED the user has not enabled RHDC integration
* @exception LPL2_ERR_RHDC_NETWORK_ERROR could not connect to RHDC (can retry)
* @exception LPL2_ERR_RHDC_RATE_LIMIT you are being rate limited (can retry later)
* @exception LPL2_ERR_RHDC_AVATAR_NOT_FOUND the user does not exist or does not have an avatar
* @exception LPL2_ERR_RHDC_AVATAR_INVALID an unexpected error occurred while processing the avatar image
*
* @note This is an asynchronous request. When called for the first time, or if the result of a prior request with the same
* arguments is not yet ready, the function will return FALSE and @p err will be set to LPL2_ERR_WAIT. Once the asynchronous
* request completes successfully, all future requests with the same arguments will return TRUE and copy the avatar. It is safe to
* make multiple concurrent asynchronous requests-- you do NOT need to wait for it to complete before making another libpl2 call.
*
* @since LPL_ABI_VERSION_7
*/
lpl2_bool lpl2_get_rhdc_avatar_async( const char *username, lpl2_avatar_options options, void *avatar, lpl2_err *err ) __attribute__((access(write_only, 3), access(write_only, 4)));
/*! Fetches the avatar for the given username in RHDC as a square image and stores it at the provided memory address. If the
* operation failed or the user is not found, the provided memory buffer is not modified. This operation blocks until the avatar
* is fully downloaded or an error occurs.
*
* @param[in] username A null-terminated string containing the username or userId
* @param[in] options Flags for how the avatar should be processed. You must include a Size Flag and a Format Flag.
* @param[out] avatar A buffer that the avatar will be written to. It is strongly recommended that you pass in a pointer to the
* appropriate lpl2_avatar_* type, though you may also pass in an 8-byte-aligned pointer to an arbitrary buffer of the appropriate
* size. The expected types and sizes are listed below:
* - @ref lpl2_avatar_16x16_rgba16 (512 bytes) for LPL2_AVATAR_16x16 | LPL2_AVATAR_RGBA16
* - @ref lpl2_avatar_16x16_rgba32 (1024 bytes) for LPL2_AVATAR_16x16 | LPL2_AVATAR_RGBA32
* - @ref lpl2_avatar_32x32_rgba16 (2048 bytes) for LPL2_AVATAR_32x32 | LPL2_AVATAR_RGBA16
* - @ref lpl2_avatar_32x32_rgba32 (4096 bytes) for LPL2_AVATAR_32x32 | LPL2_AVATAR_RGBA32
* - @ref lpl2_avatar_64x64_rgba16 (8192 bytes) for LPL2_AVATAR_64x64 | LPL2_AVATAR_RGBA16 (contains 2 textures)
* - @ref lpl2_avatar_64x64_rgba32 (16384 bytes) for LPL2_AVATAR_64x64 | LPL2_AVATAR_RGBA32 (contains 4 textures)
* @param[out] err On an error, this value (if non-NULL) is set to an error code (see listed "exceptions" below for possible error
* codes). On success, the value is set to LPL2_ERR_OKAY. If you do not care about the specific error, you may pass in NULL.
* @return TRUE if the user's avatar was successfully downloaded and copied. FALSE on an error or if the user has no avatar.
*
* @exception LPL2_ERR_INVALID_ARGUMENTS username is NULL or not a valid RHDC username, avatar is NULL, or the options are invalid
* @exception LPL2_ERR_MISALIGNED_POINTER_ARG avatar is not aligned to an 8-byte boundary
* @exception LPL2_ERR_LIBPL_NOT_INITIALIZED @ref lpl2_init has not been called
* @exception LPL2_ERR_LIBPL_NOT_SUPPORTED the emulator does not support libpl
* @exception LPL2_ERR_LIBPL_OLD_ABI @ref lpl2_init with an older ABI that does not support this function
* @exception LPL2_ERR_BROKEN_PIPE the connection to Parallel Launcher has been lost
* @exception LPL2_ERR_RHDC_INTEGRATION_NOT_ENABLED the user has not enabled RHDC integration
* @exception LPL2_ERR_RHDC_NETWORK_ERROR could not connect to RHDC (can retry)
* @exception LPL2_ERR_RHDC_RATE_LIMIT you are being rate limited (can retry later)
* @exception LPL2_ERR_RHDC_AVATAR_NOT_FOUND the user does not exist or does not have an avatar
* @exception LPL2_ERR_RHDC_AVATAR_INVALID an unexpected error occurred while processing the avatar image
*
* @attention This function will loop until the avatar is downloaded from RHDC, which may cause the emulator to stutter or freeze
* during this time. For a better user expierence, use the @ref lpl2_get_rhdc_avatar_async function instead and handle the
* LPL_ERR_WAIT error code.
*
* @since LPL_ABI_VERSION_7
*/
inline lpl2_bool __attribute__((warning("Use of blocking libpl call will likely cause the emulator to stutter. Use the async version of the libpl call for a better user experience."), access(write_only, 3), access(write_only, 4))) lpl2_get_rhdc_avatar_blocking( const char *username, lpl2_avatar_options options, void *avatar, lpl2_err *err ) {
lpl2_err e = LPL2_ERR_WAIT;
while( e == LPL2_ERR_WAIT ) lpl2_get_rhdc_avatar_async( username, options, avatar, &e );
if( err ) *err = e;
return e == LPL2_ERR_OKAY;
}
/*! @} */
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,46 @@
#ifndef LIBPL2_STDDEF_
#define LIBPL2_STDDEF_
#if __GNUC__ >= 14
typedef int __attribute__((hardbool)) lpl2_bool;
#else
typedef int lpl2_bool;
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#ifdef __cplusplus
#if __cplusplus >= 199711L
#define NULL nullptr
#else
#define NULL 0
#endif
#else
#if __STDC_VERSION__ >= 202311L
#define NULL nullptr
#else
#define NULL ((void*)0)
#endif
#endif
#endif
#ifdef __cplusplus
#define lpl2_static_assert( expr ) static_assert( expr )
#else
#if __STDC_VERSION__ >= 202311L
#define lpl2_static_assert( expr ) static_assert( expr )
#elif __STDC_VERSION__ >= 201112L
#define lpl2_static_assert( expr ) _Static_assert( expr )
#else
#define lpl2_static_assert( expr )
#endif
#endif
#endif

View File

@@ -0,0 +1,84 @@
#ifndef LIBPL2_TEXTURE_
#define LIBPL2_TEXTURE_
#ifdef __cplusplus
extern "C" {
#endif
#include "libpl2-stddef.h"
/*! @defgroup page_texture Texture Structs
*
* The structs on this page are provided for convenience and can be used for buffers that store textures.
* They can be used for things outside of libpl2.
*
* @{
*/
/*! An optional convenience struct for storing a single RGBA16 texel */
typedef struct {
unsigned short r: 5; /*!< The red component of the colour */
unsigned short g: 5; /*!< The green component of the colour */
unsigned short b: 5; /*!< The blue component of the colour */
unsigned short a: 1; /*!< The alpha bit of the colour */
} lpl2_texel_rgba16;
lpl2_static_assert( sizeof( lpl2_texel_rgba16 ) == 2 );
/*! A convenience struct for storing a single RGBA32 texel */
typedef struct {
unsigned char r; /*!< The red component of the colour */
unsigned char g; /*!< The green component of the colour */
unsigned char b; /*!< The blue component of the colour */
unsigned char a; /*!< The alpha component of the colour */
} lpl2_texel_rgba32 __attribute__((aligned(4)));
lpl2_static_assert( sizeof( lpl2_texel_rgba32 ) == 4 );
/*! A convenience struct for storing a 16x16 RGBA16 texture. */
typedef union {
lpl2_texel_rgba16 texels[256];
lpl2_texel_rgba16 yx[16][16];
} lpl2_texture_16x16_rgba16 __attribute__((aligned(8)));
lpl2_static_assert( sizeof( lpl2_texture_16x16_rgba16 ) == 512 );
/*! A convenience struct for storing a 32x32 RGBA16 texture. */
typedef union {
lpl2_texel_rgba16 texels[1024];
lpl2_texel_rgba16 yx[32][32];
} lpl2_texture_32x32_rgba16 __attribute__((aligned(8)));
lpl2_static_assert( sizeof( lpl2_texture_32x32_rgba16 ) == 2048 );
/*! A convenience struct for storing a 64x32 RGBA16 texture. */
typedef union {
lpl2_texel_rgba16 texels[2048];
lpl2_texel_rgba16 yx[32][64];
} lpl2_texture_64x32_rgba16 __attribute__((aligned(8)));
lpl2_static_assert( sizeof( lpl2_texture_64x32_rgba16 ) == 4096 );
/*! A convenience struct for storing a 16x16 RGBA32 texture. */
typedef union {
lpl2_texel_rgba32 texels[256];
lpl2_texel_rgba32 yx[16][16];
} lpl2_texture_16x16_rgba32 __attribute__((aligned(8)));
lpl2_static_assert( sizeof( lpl2_texture_16x16_rgba32 ) == 1024 );
/*! A convenience struct for storing a 32x32 RGBA32 texture. */
typedef union {
lpl2_texel_rgba32 texels[1024];
lpl2_texel_rgba32 yx[32][32];
} lpl2_texture_32x32_rgba32 __attribute__((aligned(8)));
lpl2_static_assert( sizeof( lpl2_texture_32x32_rgba32 ) == 4096 );
/*! A convenience struct for storing a 64x16 RGBA32 texture. */
typedef union {
lpl2_texel_rgba32 texels[1024];
lpl2_texel_rgba32 yx[16][64];
} lpl2_texture_64x16_rgba32 __attribute__((aligned(8)));
lpl2_static_assert( sizeof( lpl2_texture_64x16_rgba32 ) == 4096 );
/*! @} */
#ifdef __cplusplus
}
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More