<commit_before>//------------------------------------------------------------------------------
//  spine-simple-sapp.c
//  An annotated 'simplest possible' sokol_spine.h sample.
//------------------------------------------------------------------------------
#define SOKOL_SPINE_IMPL
#include "sokol_gfx.h"
#include "sokol_app.h"
#include "sokol_fetch.h"
#include "spine/spine.h"
#include "sokol_spine.h"
#include "sokol_glue.h"
#include "stb/stb_image.h"
#include "util/fileutil.h"
#include "dbgui/dbgui.h"

typedef struct {
    bool loaded;
    sspine_range data;
} load_status_t;

static struct {
    sspine_atlas atlas;
    sspine_skeleton skeleton;
    sspine_instance instance;
    sspine_layer_transform layer_transform;
    sg_pass_action pass_action;
    struct {
        load_status_t atlas;
        load_status_t skeleton;
        bool failed;
    } load_status;
    struct {
        uint8_t atlas[4 * 1024];
        uint8_t skeleton[128 * 1024];
        uint8_t image[512 * 1024];
    } buffers;
} state;

static void atlas_data_loaded(const sfetch_response_t* response);
static void skeleton_data_loaded(const sfetch_response_t* response);
static void image_data_loaded(const sfetch_response_t* response);
static void create_spine_objects(void);

static void init(void) {
    // sokol-gfx must be setup before sokol-spine
    sg_setup(&(sg_desc){ .context = sapp_sgcontext() });
    // optional debugging UI, only active in the spine-simple-sapp-ui sample
    __dbgui_setup(sapp_sample_count());

    // Setup sokol_spine.h, if desired, memory usage can be tuned by 
    // setting the max number of vertices, draw commands and pool sizes
    sspine_setup(&(sspine_desc){
        .max_vertices = 6 * 1024,
        .max_commands = 16,
        .atlas_pool_size = 1,
        .skeleton_pool_size = 1,
        .skinset_pool_size = 1,
        .instance_pool_size = 1, 
    });

    // We'll use sokol_fetch.h for data loading because this gives us
    // asynchronous file loading which also works on the web.
    // The only downside is that spine initialization is spread
    // over a couple of callbacks and frames.
    // Configure sokol-fetch so that atlas and skeleton file
    // data are loaded in parallel across 2 channels.
    sfetch_setup(&(sfetch_desc_t){
        .max_requests = 3,
        .num_channels = 2,
        .num_lanes = 1,
    });

    // Setup a sokol-gfx pass action to clear the default framebuffer to black
    // (used in sg_begin_default_pass() down in the frame callback)
    state.pass_action = (sg_pass_action){
        .colors[0] = { .action = SG_ACTION_CLEAR, .value = { 0.0f, 0.0f, 0.0f, 1.0f } }
    };

    // Start loading the spine atlas and skeleton file. This happens asynchronously
    // and in undefined finish-order. The 'fetch callbacks' will be called when the data 
    // has finished loading (or an error occurs).
    // sokol_spine.h itself doesn't care about how the data is loaded, it expects
    // all data in memory chunks.
    char path_buf[512];
    sfetch_send(&(sfetch_request_t){
        .path = fileutil_get_path("raptor-pma.atlas", path_buf, sizeof(path_buf)),
        .channel = 0,
        .buffer_ptr = state.buffers.atlas,
        .buffer_size = sizeof(state.buffers.atlas),
        .callback = atlas_data_loaded,
    });
    sfetch_send(&(sfetch_request_t){
        .path = fileutil_get_path("raptor-pro.skel", path_buf, sizeof(path_buf)),
        .channel = 1,
        .buffer_ptr = state.buffers.skeleton,
        .buffer_size = sizeof(state.buffers.skeleton),
        .callback = skeleton_data_loaded,
    });
}

// sokol-fetch callback functions for loading the atlas and skeleton data.
// These are called in undefined order, but the spine atlas must be created 
// before the skeleton (because the skeleton creation functions needs an
// atlas handle), this ordering problem is solved by both functions checking
// whether the other function has already finished, and if yes a common
// function 'create_spine_objects()' is called
static void atlas_data_loaded(const sfetch_response_t* response) {
    if (response->fetched) {
        // atlas data was successfully loaded
        state.load_status.atlas = (load_status_t){
            .loaded = true,
            .data = (sspine_range){ .ptr = response->buffer_ptr, .size = response->fetched_size }
        };
        // when both atlas and skeleton files have finished loading, create spine objects
        if (state.load_status.atlas.loaded && state.load_status.skeleton.loaded) {
            create_spine_objects();
        }
    }
    else if (response->failed) {
        // loading the atlas data failed
        state.load_status.failed = true;
    }
}

static void skeleton_data_loaded(const sfetch_response_t* response) {
    if (response->fetched) {
        state.load_status.skeleton = (load_status_t) {
            .loaded = true,
            .data = (sspine_range){ .ptr = response->buffer_ptr, .size = response->fetched_size }
        };
        if (state.load_status.atlas.loaded && state.load_status.skeleton.loaded) {
            create_spine_objects();
        }
    }
    else if (response->failed) {
        state.load_status.failed = true;
    }
}

// this function is called when both the spine atlas and skeleton file has been loaded,
// first an atlas object is created from the loaded atlas data, and then a skeleton
// object (which requires an atlas object as dependency), then a spine instance object.
// Finally any images requires by the atlas object are loaded
static void create_spine_objects(void) {
    // Create spine atlas object from loaded atlas data.
    state.atlas = sspine_make_atlas(&(sspine_atlas_desc){
        .data = state.load_status.atlas.data,
    });

    // Next create a spine skeleton object, skeleton data files can be either
    // text (JSON) or binary (in our case, 'raptor-pro.skel' is a binary skeleton file).
    // In case of JSON data, make sure that the data is 0-terminated!
    state.skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
        .atlas = state.atlas,
        .binary_data = state.load_status.skeleton.data,
        // we can pre-scale the skeleton...<commit_msg>spine-simple-sapp.c: fix typo<commit_after>//------------------------------------------------------------------------------
//  spine-simple-sapp.c
//  An annotated 'simplest possible' sokol_spine.h sample.
//------------------------------------------------------------------------------
#define SOKOL_SPINE_IMPL
#include "sokol_gfx.h"
#include "sokol_app.h"
#include "sokol_fetch.h"
#include "spine/spine.h"
#include "sokol_spine.h"
#include "sokol_glue.h"
#include "stb/stb_image.h"
#include "util/fileutil.h"
#include "dbgui/dbgui.h"

typedef struct {
    bool loaded;
    sspine_range data;
} load_status_t;

static struct {
    sspine_atlas atlas;
    sspine_skeleton skeleton;
    sspine_instance instance;
    sspine_layer_transform layer_transform;
    sg_pass_action pass_action;
    struct {
        load_status_t atlas;
        load_status_t skeleton;
        bool failed;
    } load_status;
    struct {
        uint8_t atlas[4 * 1024];
        uint8_t skeleton[128 * 1024];
        uint8_t image[512 * 1024];
    } buffers;
} state;

static void atlas_data_loaded(const sfetch_response_t* response);
static void skeleton_data_loaded(const sfetch_response_t* response);
static void image_data_loaded(const sfetch_response_t* response);
static void create_spine_objects(void);

static void init(void) {
    // sokol-gfx must be setup before sokol-spine
    sg_setup(&(sg_desc){ .context = sapp_sgcontext() });
    // optional debugging UI, only active in the spine-simple-sapp-ui sample
    __dbgui_setup(sapp_sample_count());

    // Setup sokol_spine.h, if desired, memory usage can be tuned by 
    // setting the max number of vertices, draw commands and pool sizes
    sspine_setup(&(sspine_desc){
        .max_vertices = 6 * 1024,
        .max_commands = 16,
        .atlas_pool_size = 1,
        .skeleton_pool_size = 1,
        .skinset_pool_size = 1,
        .instance_pool_size = 1, 
    });

    // We'll use sokol_fetch.h for data loading because this gives us
    // asynchronous file loading which also works on the web.
    // The only downside is that spine initialization is spread
    // over a couple of callbacks and frames.
    // Configure sokol-fetch so that atlas and skeleton file
    // data are loaded in parallel across 2 channels.
    sfetch_setup(&(sfetch_desc_t){
        .max_requests = 3,
        .num_channels = 2,
        .num_lanes = 1,
    });

    // Setup a sokol-gfx pass action to clear the default framebuffer to black
    // (used in sg_begin_default_pass() down in the frame callback)
    state.pass_action = (sg_pass_action){
        .colors[0] = { .action = SG_ACTION_CLEAR, .value = { 0.0f, 0.0f, 0.0f, 1.0f } }
    };

    // Start loading the spine atlas and skeleton file. This happens asynchronously
    // and in undefined finish-order. The 'fetch callbacks' will be called when the data 
    // has finished loading (or an error occurs).
    // sokol_spine.h itself doesn't care about how the data is loaded, it expects
    // all data in memory chunks.
    char path_buf[512];
    sfetch_send(&(sfetch_request_t){
        .path = fileutil_get_path("raptor-pma.atlas", path_buf, sizeof(path_buf)),
        .channel = 0,
        .buffer_ptr = state.buffers.atlas,
        .buffer_size = sizeof(state.buffers.atlas),
        .callback = atlas_data_loaded,
    });
    sfetch_send(&(sfetch_request_t){
        .path = fileutil_get_path("raptor-pro.skel", path_buf, sizeof(path_buf)),
        .channel = 1,
        .buffer_ptr = state.buffers.skeleton,
        .buffer_size = sizeof(state.buffers.skeleton),
        .callback = skeleton_data_loaded,
    });
}

// sokol-fetch callback functions for loading the atlas and skeleton data.
// These are called in undefined order, but the spine atlas must be created 
// before the skeleton (because the skeleton creation functions needs an
// atlas handle), this ordering problem is solved by both functions checking
// whether the other function has already finished, and if yes a common
// function 'create_spine_objects()' is called
static void atlas_data_loaded(const sfetch_response_t* response) {
    if (response->fetched) {
        // atlas data was successfully loaded
        state.load_status.atlas = (load_status_t){
            .loaded = true,
            .data = (sspine_range){ .ptr = response->buffer_ptr, .size = response->fetched_size }
        };
        // when both atlas and skeleton files have finished loading, create spine objects
        if (state.load_status.atlas.loaded && state.load_status.skeleton.loaded) {
            create_spine_objects();
        }
    }
    else if (response->failed) {
        // loading the atlas data failed
        state.load_status.failed = true;
    }
}

static void skeleton_data_loaded(const sfetch_response_t* response) {
    if (response->fetched) {
        state.load_status.skeleton = (load_status_t) {
            .loaded = true,
            .data = (sspine_range){ .ptr = response->buffer_ptr, .size = response->fetched_size }
        };
        if (state.load_status.atlas.loaded && state.load_status.skeleton.loaded) {
            create_spine_objects();
        }
    }
    else if (response->failed) {
        state.load_status.failed = true;
    }
}

// this function is called when both the spine atlas and skeleton file has been loaded,
// first an atlas object is created from the loaded atlas data, and then a skeleton
// object (which requires an atlas object as dependency), then a spine instance object.
// Finally any images required by the atlas object are loaded
static void create_spine_objects(void) {
    // Create spine atlas object from loaded atlas data.
    state.atlas = sspine_make_atlas(&(sspine_atlas_desc){
        .data = state.load_status.atlas.data,
    });

    // Next create a spine skeleton object, skeleton data files can be either
    // text (JSON) or binary (in our case, 'raptor-pro.skel' is a binary skeleton file).
    // In case of JSON data, make sure that the data is 0-terminated!
    state.skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
        .atlas = state.atlas,
        .binary_data = state.load_status.skeleton.data,
        // we can pre-scale the skeleton...
{'fraction_non_alphanumeric': 0.11942469295410472, 'fraction_numerical': 0.008403361344537815, 'mean_word_length': 2.6105600933488913, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '323456', 'n_tokens_mistral': 3917, 'n_tokens_neox': 3734, 'n_words': 1113}