1DOLLY 72TOOLCHAIN gamedev-sdk3REQUIRES HOST runtime@04REQUIRES HOST display@05REQUIRES HOST input@06REQUIRES HOST download@07REQUIRES HOST http@08REQUIRES HOST snapshot@09REQUIRES HOST upload@01011FROM /v0.1.2/Dollyfile-system cb003619fa38f3340386739e0df6a500c95f5ff040b1efb6804461adf294b25912REQUIRES HEADER libc13REQUIRES HEADER display14REQUIRES HEADER input15REQUIRES TOOL   ar16REQUIRES TOOL   cc17REQUIRES TOOL   make18REQUIRES TOOL   mkdir19REQUIRES TOOL   rm20REQUIRES TOOL   tar2122# raylib 6.0 and Box3D 0.1.0 are unchanged upstream source archives.23SOURCE /v0.1.2/dist/static/gamedev/raylib.tar b16dd083b9205e14f8b79a1d91c934b579c9a92bcb4e3af0e374f3e71dcf82d3 /tmp/raylib.tar24SOURCE /v0.1.2/dist/static/gamedev/box3d.tar  5a502138e8f7b47c5994bdd279e9dcddeae0b8e147e3b92ab79b723bd07e4377 /tmp/box3d.tar2526FILE /usr/src/dolly/gamedev/sdk.mk27    CC := cc28    AR := ar29    RAYLIB_SOURCE := /usr/src/raylib/src30    RAYLIB_MODULES := rcore rshapes rtextures rtext rmodels31    RAYLIB_OBJECTS := $(patsubst %,/usr/src/raylib/build/%.o,$(RAYLIB_MODULES))32    BOX3D_SOURCE := /usr/src/box3d/src33    BOX3D_SOURCES := $(filter-out $(BOX3D_SOURCE)/timer.c,$(wildcard $(BOX3D_SOURCE)/*.c))34    BOX3D_OBJECTS := $(patsubst $(BOX3D_SOURCE)/%.c,/usr/src/box3d/build/%.o,$(BOX3D_SOURCES))35    MATH_COMPAT_OBJECT := /usr/src/dolly/gamedev/build/math-compat.o36    37    .PHONY: all38    39    all: /usr/lib/libm.a /usr/lib/libraylib.a /usr/lib/libbox3d.a /usr/lib/libdolly-raylib.a40    41    /usr/src/raylib/build/%.o: $(RAYLIB_SOURCE)/%.c42    	mkdir -p /usr/src/raylib/build43    	$(CC) -std=gnu99 -O2 -D_GNU_SOURCE -DPLATFORM_MEMORY -DGRAPHICS_API_OPENGL_SOFTWARE -DSUPPORT_CUSTOM_FRAME_CONTROL=1 -DSW_FRAMEBUFFER_OUTPUT_BGRA=0 -fno-strict-aliasing -fno-builtin -I $(RAYLIB_SOURCE) -c $< -o $@44    45    # rtext.c triggers the embedded LLVM backend's current high-optimization46    # memory/trap limit. Text layout is not the software 3D render hot path, so47    # keep this one upstream translation unit at the reliable bootstrap level.48    /usr/src/raylib/build/rtext.o: $(RAYLIB_SOURCE)/rtext.c49    	mkdir -p /usr/src/raylib/build50    	$(CC) -std=gnu99 -O0 -D_GNU_SOURCE -DPLATFORM_MEMORY -DGRAPHICS_API_OPENGL_SOFTWARE -DSUPPORT_CUSTOM_FRAME_CONTROL=1 -DSW_FRAMEBUFFER_OUTPUT_BGRA=0 -fno-strict-aliasing -fno-builtin -I $(RAYLIB_SOURCE) -c $< -o $@51    52    /usr/lib/libraylib.a: $(RAYLIB_OBJECTS)53    	$(AR) rcs $@ $(RAYLIB_OBJECTS)54    	cp $(RAYLIB_SOURCE)/raylib.h /usr/include/raylib.h55    	cp $(RAYLIB_SOURCE)/raymath.h /usr/include/raymath.h56    	cp $(RAYLIB_SOURCE)/rlgl.h /usr/include/rlgl.h57    58    /usr/src/box3d/build/%.o: $(BOX3D_SOURCE)/%.c59    	mkdir -p /usr/src/box3d/build60    	$(CC) -std=gnu17 -O2 -fno-builtin -U__SIZEOF_INT128__ -DBOX3D_DISABLE_SIMD -I /usr/src/box3d/include -I $(BOX3D_SOURCE) -c $< -o $@61    62    /usr/src/box3d/build/platform.o: /usr/src/dolly/gamedev/box3d-platform.c63    	mkdir -p /usr/src/box3d/build64    	$(CC) -std=gnu17 -O2 -fno-builtin -U__SIZEOF_INT128__ -DBOX3D_DISABLE_SIMD -I /usr/src/box3d/include -I $(BOX3D_SOURCE) -c $< -o $@65    66    /usr/lib/libbox3d.a: $(BOX3D_OBJECTS) /usr/src/box3d/build/platform.o67    	$(AR) rcs $@ $^68    	mkdir -p /usr/include/box3d69    	cp /usr/src/box3d/include/box3d/*.h /usr/include/box3d70    71    $(MATH_COMPAT_OBJECT): /usr/src/dolly/gamedev/math-compat.c72    	mkdir -p /usr/src/dolly/gamedev/build73    	$(CC) -std=c17 -O2 -fno-builtin -c $< -o $@74    75    /usr/lib/libm.a: $(MATH_COMPAT_OBJECT)76    	$(AR) rcs $@ $^77    78    /usr/lib/libdolly-raylib.a: /usr/src/dolly/gamedev/dolly-raylib.c /usr/src/dolly/gamedev/dolly-raylib.h /usr/lib/libraylib.a79    	mkdir -p /usr/include/dolly /usr/src/dolly/gamedev/build80    	cp /usr/src/dolly/gamedev/dolly-raylib.h /usr/include/dolly/raylib.h81    	$(CC) -std=c17 -O2 -fno-builtin -I /usr/src/dolly/gamedev -c /usr/src/dolly/gamedev/dolly-raylib.c -o /usr/src/dolly/gamedev/build/dolly-raylib.o82    	$(AR) rcs $@ /usr/src/dolly/gamedev/build/dolly-raylib.o8384FILE /usr/src/dolly/gamedev/math-compat.c85    #include <math.h>86    #include <stdlib.h>87    88    int abs(int value) { return value < 0 ? -value : value; }89    float asinf(float value) { return (float)asin((double)value); }90    float atan2f(float left, float right) {91      return (float)atan2((double)left, (double)right);92    }93    float ceilf(float value) { return (float)ceil((double)value); }94    float fabsf(float value) { return (float)fabs((double)value); }95    float floorf(float value) { return (float)floor((double)value); }96    double fmax(double left, double right) {97      if (left != left) return right;98      if (right != right) return left;99      return left > right ? left : right;100    }101    double fmin(double left, double right) {102      if (left != left) return right;103      if (right != right) return left;104      return left < right ? left : right;105    }106    float fmodf(float left, float right) {107      return (float)fmod((double)left, (double)right);108    }109    float hypotf(float left, float right) {110      return (float)hypot((double)left, (double)right);111    }112    float powf(float left, float right) {113      return (float)pow((double)left, (double)right);114    }115    float roundf(float value) { return (float)round((double)value); }116    float sqrtf(float value) { return (float)sqrt((double)value); }117FILE /usr/src/dolly/gamedev/box3d-platform.c118    #define _POSIX_C_SOURCE 200809L119    120    // Dolly's Box3D target adapter replaces upstream timer.c. Box3D's built-in121    // scheduler links pthread primitives even when the normal one-worker world122    // never creates a thread. Dolly deliberately executes tasks serially inside123    // one Wasm userspace, so synchronization objects carry no host capability and124    // worker handles are inert. The scheduler's calling thread still drains every125    // queued task in b3SchedulerFinishTask.126    127    #include "core.h"128    129    #include <stdint.h>130    #include <string.h>131    #include <time.h>132    133    uint64_t b3GetTicks(void) {134      struct timespec now = {0};135      if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) return 0;136      return (uint64_t)now.tv_sec * UINT64_C(1000000000) +137             (uint64_t)now.tv_nsec;138    }139    140    float b3GetMilliseconds(uint64_t ticks) {141      return (float)((double)(b3GetTicks() - ticks) / 1000000.0);142    }143    144    float b3GetMillisecondsAndReset(uint64_t *ticks) {145      const uint64_t now = b3GetTicks();146      const float elapsed = (float)((double)(now - *ticks) / 1000000.0);147      *ticks = now;148      return elapsed;149    }150    151    void b3Yield(void) {}152    void b3Sleep(int milliseconds) { (void)milliseconds; }153    154    struct b3Mutex { unsigned unused; };155    struct b3Semaphore { int count; };156    struct b3Thread { unsigned unused; };157    158    b3Mutex *b3CreateMutex(void) {159      return b3AllocZeroed(sizeof(b3Mutex));160    }161    162    void b3DestroyMutex(b3Mutex *mutex) {163      b3Free(mutex, sizeof(b3Mutex));164    }165    166    void b3LockMutex(b3Mutex *mutex) { (void)mutex; }167    void b3UnlockMutex(b3Mutex *mutex) { (void)mutex; }168    169    b3Semaphore *b3CreateSemaphore(int initial_count) {170      b3Semaphore *semaphore = b3Alloc(sizeof(b3Semaphore));171      semaphore->count = initial_count;172      return semaphore;173    }174    175    void b3DestroySemaphore(b3Semaphore *semaphore) {176      b3Free(semaphore, sizeof(b3Semaphore));177    }178    179    void b3WaitSemaphore(b3Semaphore *semaphore) {180      if (semaphore->count > 0) semaphore->count--;181    }182    183    void b3SignalSemaphore(b3Semaphore *semaphore) {184      semaphore->count++;185    }186    187    b3Thread *b3CreateThread(b3ThreadFunction *function, void *context,188                             const char *name) {189      (void)function;190      (void)context;191      (void)name;192      return b3AllocZeroed(sizeof(b3Thread));193    }194    195    void b3JoinThread(b3Thread *thread) {196      b3Free(thread, sizeof(b3Thread));197    }198    199    // This deterministic djb2 variant is part of upstream timer.c rather than the200    // OS-independent core. Keep the exact algorithm when replacing that complete201    // translation unit so hull and mesh hashes stay compatible with upstream.202    uint32_t b3Hash(uint32_t hash, const uint8_t *data, int count) {203      uint32_t result = hash;204      int index = 0;205      while (index + 8 <= count) {206        uint64_t word;207        memcpy(&word, data + index, sizeof(word));208    #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__209        word = ((word & UINT64_C(0x00000000000000ff)) << 56) |210               ((word & UINT64_C(0x000000000000ff00)) << 40) |211               ((word & UINT64_C(0x0000000000ff0000)) << 24) |212               ((word & UINT64_C(0x00000000ff000000)) << 8) |213               ((word & UINT64_C(0x000000ff00000000)) >> 8) |214               ((word & UINT64_C(0x0000ff0000000000)) >> 24) |215               ((word & UINT64_C(0x00ff000000000000)) >> 40) |216               ((word & UINT64_C(0xff00000000000000)) >> 56);217    #endif218        result = (result << 5) + result + (uint32_t)word;219        result = (result << 5) + result + (uint32_t)(word >> 32);220        index += 8;221      }222      while (index < count) {223        result = (result << 5) + result + data[index];224        index++;225      }226      return result;227    }228FILE /usr/src/dolly/gamedev/dolly-raylib.c229    #include "dolly-raylib.h"230    231    #include <rlgl.h>232    233    #include <errno.h>234    #include <stddef.h>235    #include <stdio.h>236    #include <string.h>237    #include <termios.h>238    239    // Upstream PLATFORM_MEMORY polls a Unix tty. Dolly supplies input through240    // dolly_input_next_event(), so satisfy those unreachable backend hooks241    // locally instead of growing the command ABI with a second input path.242    int tcgetattr(int descriptor, struct termios *attributes) {243      (void)descriptor;244      (void)attributes;245      errno = ENOTTY;246      return -1;247    }248    249    int tcsetattr(int descriptor, int action, const struct termios *attributes) {250      (void)descriptor;251      (void)action;252      (void)attributes;253      errno = ENOTTY;254      return -1;255    }256    257    int getchar(void) { return EOF; }258    259    int dolly_raylib_open_sized(dolly_raylib *context, const char *title,260                                uint32_t max_width, uint32_t max_height) {261      if (context == NULL || max_width == 0 || max_height == 0) return -EINVAL;262      memset(context, 0, sizeof(*context));263      int status = dolly_display_acquire(&context->surface);264      if (status != 0) return status;265      status = dolly_input_acquire(&context->input);266      if (status != 0) {267        dolly_display_release(context->surface.generation);268        memset(context, 0, sizeof(*context));269        return status;270      }271    272      uint32_t width = context->surface.width;273      uint32_t height = context->surface.height;274      if (width > max_width || height > max_height) {275        if ((uint64_t)width * max_height > (uint64_t)height * max_width) {276          height = (uint32_t)((uint64_t)height * max_width / width);277          width = max_width;278        } else {279          width = (uint32_t)((uint64_t)width * max_height / height);280          height = max_height;281        }282        if (width == 0) width = 1;283        if (height == 0) height = 1;284        status = dolly_display_set_size(context->surface.generation, width, height,285                                        &context->surface);286        if (status != 0) {287          dolly_input_release(context->input);288          dolly_display_release(context->surface.generation);289          memset(context, 0, sizeof(*context));290          return status;291        }292      }293    294      InitWindow((int)context->surface.width, (int)context->surface.height, title);295      if (!IsWindowReady()) {296        dolly_input_release(context->input);297        dolly_display_release(context->surface.generation);298        memset(context, 0, sizeof(*context));299        return -EIO;300      }301      SetTraceLogLevel(LOG_WARNING);302      context->open = 1;303      return 0;304    }305    306    int dolly_raylib_open(dolly_raylib *context, const char *title) {307      return dolly_raylib_open_sized(context, title, 800, 450);308    }309    310    int dolly_raylib_end_frame(dolly_raylib *context) {311      if (context == NULL || !context->open) return -EINVAL;312      // SUPPORT_CUSTOM_FRAME_CONTROL makes EndDrawing flush raylib's render batch313      // without copying into PLATFORM_MEMORY's private presentation buffer or314      // sleeping. Dolly supplies both presentation and pacing below.315      EndDrawing();316    317      dolly_display_frame frame;318      int status = dolly_display_begin_frame(context->surface.generation, &frame);319      if (status == 0 && frame.pixel_format == DOLLY_DISPLAY_PIXEL_RGBA8 &&320          frame.width == context->surface.width &&321          frame.height == context->surface.height &&322          (size_t)frame.stride * frame.height <= frame.capacity) {323        rlCopyFramebuffer(0, 0, (int)frame.width, (int)frame.height,324                          PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, frame.pixels);325        status = dolly_display_present(context->surface.generation,326                                       frame.buffer_index);327      } else if (status == 0) {328        status = -EIO;329      }330      return status;331    }332    333    int dolly_raylib_wait_frame(dolly_raylib *context,334                                double timeout_milliseconds) {335      if (context == NULL || !context->open) return -EINVAL;336      return dolly_display_wait_frame(context->surface.generation,337                                      &context->animation_frame_sequence,338                                      timeout_milliseconds);339    }340    341    int dolly_raylib_set_cursor(dolly_raylib *context, uint32_t cursor) {342      if (context == NULL || !context->open) return -EINVAL;343      return dolly_display_set_cursor(context->surface.generation, cursor);344    }345    346    int dolly_raylib_next_event(dolly_raylib *context, dolly_input_event *event,347                                double timeout_milliseconds) {348      if (context == NULL || !context->open) return -EINVAL;349      return dolly_input_next_event(context->input, event, timeout_milliseconds);350    }351    352    int dolly_raylib_close(dolly_raylib *context) {353      if (context == NULL || !context->open) return -EINVAL;354      CloseWindow();355      dolly_input_release(context->input);356      int status = dolly_display_release(context->surface.generation);357      memset(context, 0, sizeof(*context));358      return status;359    }360    361    int dolly_raylib_code_is(const dolly_input_event *event, const char *code) {362      if (event == NULL || code == NULL || event->type != DOLLY_INPUT_EVENT_KEY)363        return 0;364      size_t length = strlen(code);365      return event->code_length == length &&366             memcmp(event->data + event->key_length, code, length) == 0;367    }368FILE /usr/src/dolly/gamedev/dolly-raylib.h369    #ifndef DOLLY_RAYLIB_H370    #define DOLLY_RAYLIB_H371    372    #include <dolly/display.h>373    #include <dolly/input.h>374    #include <raylib.h>375    376    typedef struct {377      dolly_display_surface surface;378      uint64_t input;379      uint32_t animation_frame_sequence;380      int open;381    } dolly_raylib;382    383    // PLATFORM_MEMORY renders wholly inside Wasm. The adapter copies its completed384    // software frame directly into Dolly's inactive buffer without an intermediate385    // raylib Image. The default open caps software rendering at 800x450; callers386    // can choose another upper bound while retaining the browser viewport ratio.387    int dolly_raylib_open(dolly_raylib *context, const char *title);388    int dolly_raylib_open_sized(dolly_raylib *context, const char *title,389                                uint32_t max_width, uint32_t max_height);390    int dolly_raylib_end_frame(dolly_raylib *context);391    int dolly_raylib_wait_frame(dolly_raylib *context, double timeout_milliseconds);392    int dolly_raylib_set_cursor(dolly_raylib *context, uint32_t cursor);393    int dolly_raylib_next_event(dolly_raylib *context, dolly_input_event *event,394                                double timeout_milliseconds);395    int dolly_raylib_close(dolly_raylib *context);396    int dolly_raylib_code_is(const dolly_input_event *event, const char *code);397    398    #endif399SLOP tar \400  -xf /tmp/raylib.tar \401  -C /402SLOP tar \403  -xf /tmp/box3d.tar \404  -C /405SLOP CWD /usr/src/dolly/gamedev make \406  -f /usr/src/dolly/gamedev/sdk.mk407408FILE /usr/include/raymath.h409FILE /usr/include/rlgl.h410FILE /usr/share/licenses/raylib/LICENSE411FILE /usr/share/licenses/box3d/LICENSE412FOLDER /usr/src/raylib413FOLDER /usr/src/box3d414415EXPORTS LIB    raylib       /usr/lib/libraylib.a416EXPORTS LIB    box3d        /usr/lib/libbox3d.a417EXPORTS LIB    dolly-raylib /usr/lib/libdolly-raylib.a418EXPORTS LIB    m            /usr/lib/libm.a419EXPORTS HEADER raylib       /usr/include/raylib.h420EXPORTS HEADER box3d        /usr/include/box3d421EXPORTS HEADER dolly-raylib /usr/include/dolly/raylib.h422423SLOP rm \424  -rf \425  /tmp/box3d.tar \426  /usr/src/raylib/build \427  /usr/src/box3d/build \428  /usr/src/dolly/gamedev/build \429  /tmp/raylib.tar430431ENTRY /bin/foreground -i /bin/slop432