feat: Property (node) API impl with mxml

To provide a unified backend for all games, no
matter if they have AVS 2 available or now, use the
property (node) abstraction for any means of
configuration. This is somewhat annoying and a pain
because the property API isn't amazing. However,
the past has shown that having different means of
providing and handling (structured) configuration
data isn't great either.

Thus, have a non AVS implementation that allows
AVS independent applications/old games to use
the same core bemanitools API.
Summary:

Test Plan:
Summary:

Test Plan:
This commit is contained in:
icex2 2024-08-15 11:34:31 +02:00
parent 7a6c3af9c8
commit e3c97801ed
20 changed files with 1404 additions and 3 deletions

View File

@ -11,6 +11,7 @@ libs_config := \
geninput \
util \
iface-core \
mxml \
ldflags_config := \
-lcomctl32 \

View File

@ -18,8 +18,8 @@ src_core := \
log-sink-std.c \
property-ext.c \
property-mxml.c \
property-mxml.c \
property-node-ext.c \
property-node-mxml.c \
property-node-trace.c \
property-node.c \
property-trace.c \

View File

@ -0,0 +1,33 @@
#ifndef CORE_PROPERTY_MXML_INTERNAL_H
#define CORE_PROPERTY_MXML_INTERNAL_H
#include <mxml/mxml.h>
#include "core/property.h"
#include "core/property-node.h"
// As there is no need to have a fixed size allocated for properties with
// everything dynamically growing on the heap when necessary, just
// use this as a dummy value to be compatible with the property interface
#define CORE_PROPERTY_MXML_INTERNAL_FIXED_SIZE_DUMMY 573
typedef struct core_property_mxml_internal_property {
mxml_node_t *document;
} core_property_mxml_internal_property_t;
_Static_assert(
sizeof(core_property_mxml_internal_property_t) <= sizeof(core_property_t),
"Not enough space for stack allocations");
typedef struct core_property_mxml_internal_property_node {
core_property_mxml_internal_property_t *property;
mxml_node_t *node;
// Used when node is being iterated to remember the root of the iteration
mxml_node_t *node_root_iter;
} core_property_mxml_internal_property_node_t;
_Static_assert(
sizeof(core_property_mxml_internal_property_node_t) <= sizeof(core_property_node_t),
"Not enough space for stack allocations");
#endif

View File

@ -0,0 +1,240 @@
#define LOG_MODULE "core-property-mxml"
#include <windows.h>
#include <errno.h>
#include <stdio.h>
#include <mxml/mxml.h>
#include "core/property-mxml-internal.h"
#include "core/property-node-mxml.h"
#include "core/property-mxml.h"
#include "iface-core/log.h"
#include "util/fs.h"
#include "util/mem.h"
static core_property_result_t
_core_property_mxml_create(size_t size, core_property_t **property_)
{
mxml_node_t *node;
core_property_mxml_internal_property_t *property;
// There is no need to use the size parameter here as everything's allocated
// dynamically on the heap and no initial fixed size is needed
node = mxmlNewXML(NULL);
if (!node) {
return CORE_PROPERTY_RESULT_ERROR_ALLOC;
}
property = xmalloc(sizeof(core_property_mxml_internal_property_t));
property->document = node;
*property_ = (core_property_t *) property;
return CORE_PROPERTY_RESULT_SUCCESS;
}
static core_property_result_t
_core_property_mxml_file_load(const char *path, core_property_t **property_)
{
mxml_node_t *node;
core_property_mxml_internal_property_t *property;
// The return value NULL of the loading function
// doesn't distinguish file not found and loading error =|
if (!path_exists(path)) {
return CORE_PROPERTY_RESULT_NOT_FOUND;
}
node = mxmlLoadFilename(NULL, NULL, path);
if (!node) {
return CORE_PROPERTY_RESULT_ERROR_READ;
}
property = xmalloc(sizeof(core_property_mxml_internal_property_t));
property->document = node;
*property_ = (core_property_t *) property;
return CORE_PROPERTY_RESULT_SUCCESS;
}
static core_property_result_t
_core_property_mxml_str_load(const char *str, core_property_t **property_)
{
mxml_node_t *node;
core_property_mxml_internal_property_t *property;
node = mxmlLoadString(NULL, NULL, str);
if (!node) {
return CORE_PROPERTY_RESULT_ERROR_READ;
}
property = xmalloc(sizeof(core_property_mxml_internal_property_t));
property->document = node;
*property_ = (core_property_t *) property;
return CORE_PROPERTY_RESULT_SUCCESS;
}
static core_property_result_t
_core_property_mxml_size(const core_property_t *property_, size_t *size)
{
*size = CORE_PROPERTY_MXML_INTERNAL_FIXED_SIZE_DUMMY;
return CORE_PROPERTY_RESULT_SUCCESS;
}
static core_property_result_t _core_property_mxml_clone(
const core_property_t *property_, core_property_t **property_cloned_)
{
const core_property_mxml_internal_property_t *property;
core_property_mxml_internal_property_t **property_cloned;
char *str_copy;
mxml_node_t *node_cloned;
property = (const core_property_mxml_internal_property_t*) property_;
property_cloned = (core_property_mxml_internal_property_t**) property_cloned_;
// Ensure actual cloning by storing and loading this
// Just "adding it" to the other tree with mxml creates a reference only
str_copy = mxmlSaveAllocString(property->document, NULL);
if (str_copy == NULL) {
return CORE_PROPERTY_RESULT_ERROR_READ;
}
node_cloned = mxmlLoadString(NULL, NULL, str_copy);
if (node_cloned == NULL) {
return CORE_PROPERTY_RESULT_ERROR_WRITE;
}
*property_cloned = xmalloc(sizeof(core_property_mxml_internal_property_t));
(*property_cloned)->document = node_cloned;
return CORE_PROPERTY_RESULT_SUCCESS;
}
static core_property_result_t _core_property_mxml_root_node_get(
const core_property_t *property_, core_property_node_t *node_)
{
core_property_mxml_internal_property_t *property;
core_property_mxml_internal_property_node_t *node;
mxml_node_t *mxml_node;
mxml_type_t type;
property = (core_property_mxml_internal_property_t *) property_;
node = (core_property_mxml_internal_property_node_t *) node_;
memset(node, 0, sizeof(core_property_mxml_internal_property_node_t));
// Walk the nodes as the loaded xml node is the document root
// which can be the xml directive, or if missing, the first root
// element
for (mxml_node = property->document;
mxml_node != NULL;
mxml_node = mxmlWalkNext(mxml_node, property->document, MXML_DESCEND_FIRST))
{
type = mxmlGetType(mxml_node);
// Consider the first element to be found the root element of the
// document. Having multiple roots isn't a valid XML document
// anyway
if (type == MXML_TYPE_ELEMENT) {
break;
}
}
if (mxml_node == NULL) {
return CORE_PROPERTY_RESULT_NOT_FOUND;
}
node->property = property;
// No difference for property vs. property_node
node->node = mxml_node;
return CORE_PROPERTY_RESULT_SUCCESS;
}
static core_property_result_t _core_property_mxml_other_node_insert(
core_property_t *property_, const core_property_node_t *node_)
{
core_property_mxml_internal_property_t *property;
core_property_mxml_internal_property_node_t *node;
char *str_copy;
mxml_node_t *node_cloned;
property = (core_property_mxml_internal_property_t *) property_;
node = (core_property_mxml_internal_property_node_t *) node_;
// Ensure actual cloning by storing and loading this
// Just "adding it" to the other tree with mxml creates a reference only
str_copy = mxmlSaveAllocString(node->node, NULL);
if (str_copy == NULL) {
return CORE_PROPERTY_RESULT_ERROR_READ;
}
node_cloned = mxmlLoadString(NULL, NULL, str_copy);
if (node_cloned == NULL) {
return CORE_PROPERTY_RESULT_ERROR_WRITE;
}
// Hook the new node into the existing tree
mxmlAdd(property->document, MXML_ADD_AFTER, NULL, node_cloned);
return CORE_PROPERTY_NODE_RESULT_SUCCESS;
}
static void _core_property_mxml_free(core_property_t **property_)
{
core_property_mxml_internal_property_t *property;
property = (core_property_mxml_internal_property_t *) (*property_);
mxmlDelete(property->document);
free(*property_);
*property_ = NULL;
}
static void _core_property_mxml_core_api_get(core_property_api_t *api)
{
log_assert(api);
api->version = 1;
api->v1.create = _core_property_mxml_create;
api->v1.file_load = _core_property_mxml_file_load;
api->v1.str_load = _core_property_mxml_str_load;
api->v1.size = _core_property_mxml_size;
api->v1.clone = _core_property_mxml_clone;
api->v1.root_node_get = _core_property_mxml_root_node_get;
api->v1.other_node_insert = _core_property_mxml_other_node_insert;
api->v1.free = _core_property_mxml_free;
}
void core_property_mxml_core_api_set()
{
core_property_api_t api;
_core_property_mxml_core_api_get(&api);
core_property_api_set(&api);
}

View File

@ -0,0 +1,8 @@
#ifndef CORE_PROPERTY_MXML_H
#define CORE_PROPERTY_MXML_H
#include "main/core/property.h"
void core_property_mxml_core_api_set();
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
#ifndef CORE_PROPERTY_NODE_MXML_H
#define CORE_PROPERTY_NODE_MXML_H
#include "main/core/property-node.h"
void core_property_node_mxml_core_api_get(core_property_node_api_t *api);
void core_property_node_mxml_core_api_set();
#endif

View File

@ -117,6 +117,12 @@ const char *core_property_node_result_to_str(core_property_node_result_t result)
return "Internal";
case CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND:
return "Node not found";
case CORE_PROPERTY_NODE_RESULT_INVALID_NODE_TYPE:
return "Invalid node type";
case CORE_PROPERTY_NODE_RESULT_INVALID_NODE_STRUCTURE:
return "Invalid node structure";
case CORE_PROPERTY_NODE_RESULT_INVALID_NODE_DATA:
return "Invalid node data";
default:
return "Undefined error";
}

View File

@ -10,7 +10,7 @@
#include "main/core/property.h"
#define CORE_PROPERTY_NODE_RESULT_IS_ERROR(x) \
(x > CORE_PROPERTY_NODE_RESULT_SUCCESS)
(x != CORE_PROPERTY_NODE_RESULT_SUCCESS)
// Based on actual AVS impl max size
#define CORE_PROPERTY_NODE_NAME_SIZE_MAX 256
// Guestimate, should be enough, I hope?
@ -33,6 +33,9 @@ typedef enum core_property_node_result {
CORE_PROPERTY_NODE_RESULT_SUCCESS = 0,
CORE_PROPERTY_NODE_RESULT_ERROR_INTERNAL = 1,
CORE_PROPERTY_NODE_RESULT_NODE_NOT_FOUND = 2,
CORE_PROPERTY_NODE_RESULT_INVALID_NODE_TYPE = 3,
CORE_PROPERTY_NODE_RESULT_INVALID_NODE_STRUCTURE = 4,
CORE_PROPERTY_NODE_RESULT_INVALID_NODE_DATA = 5,
} core_property_node_result_t;
typedef core_property_node_result_t (*core_property_node_name_get_t)(

View File

@ -88,6 +88,8 @@ const char *core_property_result_to_str(core_property_result_t result)
return "Permissions";
case CORE_PROPERTY_RESULT_ERROR_READ:
return "Read error";
case CORE_PROPERTY_RESULT_ERROR_WRITE:
return "Write error";
default:
return "Undefined error";
}

View File

@ -5,7 +5,8 @@
#include <stdint.h>
#include <stdlib.h>
#include "api/core/log.h"
#define CORE_PROPERTY_RESULT_IS_ERROR(x) \
(x != CORE_PROPERTY_RESULT_SUCCESS)
// Macro to allow inlining of the caller function and line numbers
// to make debugging easier
@ -25,6 +26,7 @@ typedef struct core_property_node {
// Have size known, but not contents, to allow for stack allocations
void *v1;
void *v2;
void *v3;
} core_property_node_t;
typedef enum core_property_result {
@ -34,6 +36,7 @@ typedef enum core_property_result {
CORE_PROPERTY_RESULT_NOT_FOUND = 3,
CORE_PROPERTY_RESULT_ERROR_PERMISSIONS = 4,
CORE_PROPERTY_RESULT_ERROR_READ = 5,
CORE_PROPERTY_RESULT_ERROR_WRITE = 6,
} core_property_result_t;
typedef core_property_result_t (*core_property_create_t)(

View File

@ -6,6 +6,7 @@ libs_extiotest := \
core \
util \
iface-core \
mxml \
src_extiotest := \
main.c \

View File

@ -22,6 +22,7 @@ libs_iidxhook1 := \
iface-io \
iface-core \
util \
mxml \
src_iidxhook1 := \
config-iidxhook1.c \

View File

@ -22,6 +22,7 @@ libs_iidxhook2 := \
iface-core \
module \
util \
mxml \
src_iidxhook2 := \
config-iidxhook2.c \

View File

@ -26,6 +26,7 @@ libs_iidxhook3 := \
iface-core \
module \
util \
mxml \
src_iidxhook3 := \
avs-boot.c \

View File

@ -28,6 +28,7 @@ libs_iidxhook4-cn := \
iface-core \
module \
util \
mxml \
src_iidxhook4-cn := \
avs-boot.c \

View File

@ -28,6 +28,7 @@ libs_iidxhook4 := \
iface-core \
module \
util \
mxml \
src_iidxhook4 := \
dllmain.c \

View File

@ -27,6 +27,7 @@ libs_iidxhook5-cn := \
iface-core \
module \
util \
mxml \
src_iidxhook5-cn := \
avs-boot.c \

View File

@ -28,6 +28,7 @@ libs_iidxhook5 := \
iface-core \
module \
util \
mxml \
src_iidxhook5 := \
dllmain.c \

View File

@ -11,6 +11,7 @@ libs_inject := \
util \
dwarfstack \
iface-core \
mxml \
src_inject := \
main.c \